Test non concluant :

* On ne peut utiliser simplement le formatter de la meme maniere que le
formatter basique, sans voir de difference.
 * Je n'ai pas d'ideede syntaxe pratique
 * Il y a d'autre chose plus importantes a faire

darcs-hash:20070124085321-9e428-24676c1a7f7e8119597896c69af7e71835e2f7cc.gz
This commit is contained in:
bobot 2007-01-24 09:53:21 +01:00
parent 52e9091bae
commit f191dbe231

View file

@ -1,731 +0,0 @@
# -*- coding: iso-8859-1 -*-
"""
MoinMoin - "text/html+css" Formatter
@copyright: 2000 - 2004 by Jürgen Hermann <jh@web.de>
@license: GNU GPL, see COPYING for details.
"""
from MoinMoin.formatter.base import FormatterBase
from MoinMoin import wikiutil, i18n, config
from MoinMoin.Page import Page
class Formatter(FormatterBase):
"""
Send HTML data.
"""
hardspace = '&nbsp;'
def __init__(self, request, **kw):
apply(FormatterBase.__init__, (self, request), kw)
# inline tags stack. When an inline tag is called, it goes into
# the stack. When a block element starts, all inline tags in
# the stack are closed.
self._inlineStack = []
self._in_li = 0
self._in_code = 0
self._in_code_area = 0
self._in_code_line = 0
self._code_area_num = 0
self._code_area_js = 0
self._code_area_state = ['', 0, -1, -1, 0]
self._show_section_numbers = None
self._content_ids = []
self.pagelink_preclosed = False
self._is_included = kw.get('is_included',False)
self.request = request
self.cfg = request.cfg
if not hasattr(request, '_fmt_hd_counters'):
request._fmt_hd_counters = []
# Primitive formatter functions #####################################
# all other methods should use these to format tags. This keeps the
# code clean and handle pathological cases like unclosed p and
# inline tags.
def langAttr(self, lang=None):
""" Return lang and dir attribute
Must be used on all block elements - div, p, table, etc.
@param lang: if defined, will return attributes for lang. if not
defined, will return attributes only if the current lang is
different from the content lang.
@rtype: dict
@retrun: language attributes
"""
if not lang:
lang = self.request.current_lang
# Actions that generate content in user language should change
# the content lang from the default defined in cfg.
if lang == self.request.content_lang:
# lang is inherited from content div
return {}
attr = {'lang': lang, 'dir': i18n.getDirection(lang),}
return attr
def formatAttributes(self, attr=None):
""" Return formatted attributes string
@param attr: dict containing keys and values
@rtype: string ?
@return: formated attributes or empty string
"""
if attr:
attr = [' %s="%s"' % (k, v) for k, v in attr.items()]
return ''.join(attr)
return ''
# TODO: use set when we require Python 2.3
# TODO: The list is not complete, add missing from dtd
_blocks = 'p div pre table tr td ol ul dl li dt dd h1 h2 h3 h4 h5 h6 hr form'
_blocks = dict(zip(_blocks.split(), [1] * len(_blocks)))
def open(self, tag, newline=False, attr=None):
""" Open a tag with optional attributes
@param tag: html tag, string
@param newline: render tag on a separate line
@parm attr: dict with tag attributes
@rtype: string ?
@return: open tag with attributes
"""
if tag in self._blocks:
# Block elements
result = []
# Add language attributes, but let caller overide the default
attributes = self.langAttr()
if attr:
attributes.update(attr)
# Format
attributes = self.formatAttributes(attributes)
result.append('<%s%s>' % (tag, attributes))
if newline:
result.append('\n')
return ''.join(result)
else:
# Inline elements
# Add to inlineStack
self._inlineStack.append(tag)
# Format
return '<%s%s>' % (tag, self.formatAttributes(attr))
def close(self, tag, newline=False):
""" Close tag
@param tag: html tag, string
@rtype: string ?
@return: closing tag
"""
if tag in self._blocks:
# Block elements
# Close all tags in inline stack
# Work on a copy, because close(inline) manipulate the stack
result = []
stack = self._inlineStack[:]
stack.reverse()
for inline in stack:
result.append(self.close(inline))
# Format with newline
if newline:
result.append('\n')
result.append('</%s>\n' % (tag))
return ''.join(result)
else:
# Inline elements
# Pull from stack, ignore order, that is not our problem.
# The code that calls us should keep correct calling order.
if tag in self._inlineStack:
self._inlineStack.remove(tag)
return '</%s>' % tag
# Public methods ###################################################
def startContent(self, content_id='content', **kwargs):
""" Start page content div """
# Setup id
if content_id!='content':
aid = 'top_%s' % (content_id,)
else:
aid = 'top'
self._content_ids.append(content_id)
result = []
# Use the content language
attr = self.langAttr(self.request.content_lang)
attr['id'] = content_id
result.append(self.open('div', newline=1, attr=attr))
result.append(self.anchordef(aid))
return ''.join(result)
def endContent(self):
""" Close page content div """
# Setup id
try:
cid = self._content_ids.pop()
except:
cid = 'content'
if cid!='content':
aid = 'bottom_%s' % (cid,)
else:
aid = 'bottom'
result = []
result.append(self.anchordef(aid))
result.append(self.close('div', newline=1))
return ''.join(result)
def lang(self, on, lang_name):
""" Insert text with specific lang and direction.
Enclose within span tag if lang_name is different from
the current lang
"""
tag = 'span'
if lang_name != self.request.current_lang:
# Enclose text in span using lang attributes
if on:
attr = self.langAttr(lang=lang_name)
return self.open(tag, attr=attr)
return self.close(tag)
# Direction did not change, no need for span
return ''
def sysmsg(self, on, **kw):
tag = 'div'
if on:
return self.open(tag, attr={'class': 'message'})
return self.close(tag)
# Links ##############################################################
def pagelink(self, on, pagename='', page=None, **kw):
""" Link to a page.
formatter.text_python will use an optimized call with a page!=None
parameter. DO NOT USE THIS YOURSELF OR IT WILL BREAK.
See wikiutil.link_tag() for possible keyword parameters.
"""
apply(FormatterBase.pagelink, (self, on, pagename, page), kw)
if page is None:
page = Page(self.request, pagename, formatter=self);
if self.request.user.show_nonexist_qm and on and not page.exists():
self.pagelink_preclosed = True
return (page.link_to(self.request, on=1, **kw) +
self.text("?") +
page.link_to(self.request, on=0, **kw))
elif not on and self.pagelink_preclosed:
self.pagelink_preclosed = False
return ""
else:
return page.link_to(self.request, on=on, **kw)
def interwikilink(self, on, interwiki='', pagename='', **kw):
if not on: return '</a>'
wikitag, wikiurl, wikitail, wikitag_bad = wikiutil.resolve_wiki(self.request, '%s:%s' % (interwiki, pagename))
wikiurl = wikiutil.mapURL(self.request, wikiurl)
if wikitag == 'Self': # for own wiki, do simple links
href = wikiutil.join_wiki(wikiurl, wikiutil.AbsPageName(self.request, self.page.page_name, wikitail))
return (self.url(1, href, unescaped=0, pretty_url=kw.get('pretty_url', 0)))
else: # return InterWiki hyperlink
href = wikiutil.join_wiki(wikiurl, wikitail)
if wikitag_bad:
html_class = 'badinterwiki'
else:
html_class = 'interwiki'
icon = ''
if self.request.user.show_fancy_links:
icon = self.request.theme.make_icon('interwiki', {'wikitag': wikitag})
return (self.url(1, href, title=wikitag, unescaped=0,
pretty_url=kw.get('pretty_url', 0), css = html_class) +
icon)
# unescaped=1 was changed to 0 to make interwiki links with pages with umlauts (or other non-ascii) work
def url(self, on, url=None, css=None, **kw):
"""
Keyword params:
title - title attribute
... some more (!!! TODO)
"""
if url is not None:
url = wikiutil.mapURL(self.request, url)
pretty = kw.get('pretty_url', 0)
title = kw.get('title', None)
#if not pretty and wikiutil.isPicture(url):
# # XXX
# return '<img src="%s" alt="%s">' % (url,url)
# create link
if not on:
return '</a>'
str = '<a'
if css:
str = '%s class="%s"' % (str, css)
if title:
str = '%s title="%s"' % (str, title)
str = '%s href="%s">' % (str, wikiutil.escape(url, 1))
type = kw.get('type', '')
if type=='www':
str = '%s%s ' % (str, self.icon("www"))
elif type=='mailto':
str = '%s%s ' % (str, self.icon('mailto'))
return str
def anchordef(self, id):
return '<a id="%s"></a>\n' % (id, )
def anchorlink(self, on, name='', id = None):
extra = ''
if id:
extra = ' id="%s"' % id
return ['<a href="#%s"%s>' % (name, extra), '</a>'][not on]
# Text ##############################################################
def _text(self, text):
if self._in_code:
return wikiutil.escape(text).replace(' ', self.hardspace)
return wikiutil.escape(text)
# Inline ###########################################################
def strong(self, on):
tag = 'strong'
if on:
return self.open(tag)
return self.close(tag)
def emphasis(self, on):
tag = 'em'
if on:
return self.open(tag)
return self.close(tag)
def underline(self, on):
tag = 'span'
if on:
return self.open(tag, attr={'class': 'u'})
return self.close(tag)
def highlight(self, on):
tag = 'strong'
if on:
return self.open(tag, attr={'class': 'highlight'})
return self.close(tag)
def sup(self, on):
tag = 'sup'
if on:
return self.open(tag)
return self.close(tag)
def sub(self, on):
tag = 'sub'
if on:
return self.open(tag)
return self.close(tag)
def code(self, on):
tag = 'tt'
# Maybe we don't need this, because we have tt will be in inlineStack.
self._in_code = on
if on:
return self.open(tag)
return self.close(tag)
def small(self, on):
tag = 'small'
if on:
return self.open(tag)
return self.close(tag)
def big(self, on):
tag = 'big'
if on:
return self.open(tag)
return self.close(tag)
# Block elements ####################################################
def preformatted(self, on):
FormatterBase.preformatted(self, on)
tag = 'pre'
if on:
return self.open(tag, newline=1)
return self.close(tag)
# Use by code area
_toggleLineNumbersScript = """
<script type="text/JavaScript">
function isnumbered(obj) {
return obj.childNodes.length && obj.firstChild.childNodes.length && obj.firstChild.firstChild.className == 'LineNumber';
}
function nformat(num,chrs,add) {
var nlen = Math.max(0,chrs-(''+num).length), res = '';
while (nlen>0) { res += ' '; nlen-- }
return res+num+add;
}
function addnumber(did, nstart, nstep) {
var c = document.getElementById(did), l = c.firstChild, n = 1;
if (!isnumbered(c))
if (typeof nstart == 'undefined') nstart = 1;
if (typeof nstep == 'undefined') nstep = 1;
n = nstart;
while (l != null) {
if (l.tagName == 'SPAN') {
var s = document.createElement('SPAN');
s.className = 'LineNumber'
s.appendChild(document.createTextNode(nformat(n,4,' ')));
n += nstep;
if (l.childNodes.length)
l.insertBefore(s, l.firstChild)
else
l.appendChild(s)
}
l = l.nextSibling;
}
return false;
}
function remnumber(did) {
var c = document.getElementById(did), l = c.firstChild;
if (isnumbered(c))
while (l != null) {
if (l.tagName == 'SPAN' && l.firstChild.className == 'LineNumber') l.removeChild(l.firstChild);
l = l.nextSibling;
}
return false;
}
function togglenumber(did, nstart, nstep) {
var c = document.getElementById(did);
if (isnumbered(c)) {
remnumber(did);
} else {
addnumber(did,nstart,nstep);
}
return false;
}
</script>
"""
def code_area(self, on, code_id, code_type='code', show=0, start=-1, step=-1):
res = []
ci = self.request.makeUniqueID('CA-%s_%03d' % (code_id, self._code_area_num))
if on:
# Open a code area
self._in_code_area = 1
self._in_code_line = 0
self._code_area_state = [ci, show, start, step, start]
# Open the code div - using left to right always!
attr = {'class': 'codearea', 'lang': 'en', 'dir': 'ltr'}
res.append(self.open('div', attr=attr))
# Add the script only in the first code area on the page
if self._code_area_js == 0 and self._code_area_state[1] >= 0:
res.append(self._toggleLineNumbersScript)
self._code_area_js = 1
# Add line number link, but only for JavaScript enabled browsers.
if self._code_area_state[1] >= 0:
toggleLineNumbersLink = r'''
<script type="text/javascript">
document.write('<a href="#" onClick="return togglenumber(\'%s\', %d, %d);" \
class="codenumbers">Toggle line numbers<\/a>');
</script>
''' % (self._code_area_state[0], self._code_area_state[2], self._code_area_state[3])
res.append(toggleLineNumbersLink)
# Open pre - using left to right always!
attr = {'id': self._code_area_state[0], 'lang': 'en', 'dir': 'ltr'}
res.append(self.open('pre', newline=True, attr=attr))
else:
# Close code area
res = []
if self._in_code_line:
res.append(self.code_line(0))
res.append(self.close('pre'))
res.append(self.close('div'))
# Update state
self._in_code_area = 0
self._code_area_num += 1
return ''.join(res)
def code_line(self, on):
res = ''
if not on or (on and self._in_code_line):
res += '</span>\n'
if on:
res += '<span class="line">'
if self._code_area_state[1] > 0:
res += '<span class="LineNumber">%4d </span>' % (self._code_area_state[4], )
self._code_area_state[4] += self._code_area_state[3]
self._in_code_line = on != 0
return res
def code_token(self, on, tok_type):
return ['<span class="%s">' % tok_type, '</span>'][not on]
# Paragraphs, Lines, Rules ###########################################
def linebreak(self, preformatted=1):
if self._in_code_area:
preformatted = 1
return ['\n', '<br>\n'][not preformatted]
def paragraph(self, on):
if self._terse:
return ''
FormatterBase.paragraph(self, on)
if self._in_li:
self._in_li = self._in_li + 1
tag = 'p'
if on:
return self.open(tag)
return self.close(tag)
def rule(self, size=None):
if size:
# Add hr class: hr1 - hr6
return self.open('hr', newline=1, attr={'class': 'hr%d' % size})
return self.open('hr', newline=1)
def icon(self, type):
return self.request.theme.make_icon(type)
def smiley(self, text):
w, h, b, img = config.smileys[text.strip()]
href = img
if not href.startswith('/'):
href = self.request.theme.img_url(img)
return self.image(src=href, alt=text, width=str(w), height=str(h))
# Lists ##############################################################
def number_list(self, on, type=None, start=None):
tag = 'ol'
if on:
attr = {}
if type is not None:
attr['type'] = type
if start is not None:
attr['start'] = start
return self.open(tag, newline=1, attr=attr)
return self.close(tag)
def bullet_list(self, on):
tag = 'ul'
if on:
return self.open(tag, newline=1)
return self.close(tag)
def listitem(self, on, **kw):
""" List item inherit its lang from the list. """
tag = 'li'
self._in_li = on != 0
if on:
attr = {}
css_class = kw.get('css_class', None)
if css_class:
attr['class'] = css_class
style = kw.get('style', None)
if style:
attr['style'] = style
return self.open(tag, attr=attr)
return self.close(tag)
def definition_list(self, on):
tag = 'dl'
if on:
return self.open(tag, newline=1)
return self.close(tag)
def definition_term(self, on):
tag = 'dt'
if on:
return self.open(tag)
return self.close(tag)
def definition_desc(self, on):
tag = 'dd'
if on:
return self.open(tag)
return self.close(tag)
def heading(self, on, depth, id = None, **kw):
# remember depth of first heading, and adapt counting depth accordingly
if not self._base_depth:
self._base_depth = depth
count_depth = max(depth - (self._base_depth - 1), 1)
# check numbering, possibly changing the default
if self._show_section_numbers is None:
self._show_section_numbers = self.cfg.show_section_numbers
numbering = self.request.getPragma('section-numbers', '').lower()
if numbering in ['0', 'off']:
self._show_section_numbers = 0
elif numbering in ['1', 'on']:
self._show_section_numbers = 1
elif numbering in ['2', '3', '4', '5', '6']:
# explicit base level for section number display
self._show_section_numbers = int(numbering)
heading_depth = depth + 1
# closing tag, with empty line after, to make source more readable
if not on:
return self.close('h%d' % heading_depth) + '\n'
# create section number
number = ''
if self._show_section_numbers:
# count headings on all levels
self.request._fmt_hd_counters = self.request._fmt_hd_counters[:count_depth]
while len(self.request._fmt_hd_counters) < count_depth:
self.request._fmt_hd_counters.append(0)
self.request._fmt_hd_counters[-1] = self.request._fmt_hd_counters[-1] + 1
number = '.'.join(map(str, self.request._fmt_hd_counters[self._show_section_numbers-1:]))
if number: number += ". "
attr = {}
if id:
attr['id'] = id
# Add space before heading, easier to check source code
result = '\n' + self.open('h%d' % heading_depth, attr=attr)
# TODO: convert this to readable code
if self.request.user.show_topbottom:
# TODO change top/bottom refs to content-specific top/bottom refs?
result = ("%s%s%s%s%s%s%s%s" %
(result,
kw.get('icons',''),
self.url(1, "#bottom", unescaped=1),
self.icon('bottom'),
self.url(0),
self.url(1, "#top", unescaped=1),
self.icon('top'),
self.url(0)))
return "%s%s%s" % (result, kw.get('icons',''), number)
# Tables #############################################################
_allowed_table_attrs = {
'table': ['class', 'id', 'style'],
'row': ['class', 'id', 'style'],
'': ['colspan', 'rowspan', 'class', 'id', 'style'],
}
def _checkTableAttr(self, attrs, prefix):
""" Check table attributes
Convert from wikitable attributes to html 4 attributes.
@param attrs: attribute dict
@param prefix: used in wiki table attributes
@rtyp: dict
@return: valid table attributes
"""
if not attrs:
return {}
result = {}
s = "" # we collect synthesized style in s
for key, val in attrs.items():
# Ignore keys that don't start with prefix
if prefix and key[:len(prefix)] != prefix:
continue
key = key[len(prefix):]
val = val.strip('"')
# remove invalid attrs from dict and synthesize style
if key == 'width':
s += "width: %s;" % val
elif key == 'bgcolor':
s += "background-color: %s;" % val
elif key == 'align':
s += "text-align: %s;" % val
elif key == 'valign':
s += "vertical-align: %s;" % val
# Ignore unknown keys
if key not in self._allowed_table_attrs[prefix]:
continue
result[key] = val
if s:
if result.has_key('style'):
result['style'] += s
else:
result['style'] = s
return result
def table(self, on, attrs=None):
""" Create table
@param on: start table
@param attrs: table attributes
@rtype: string
@return start or end tag of a table
"""
result = []
if on:
# Open div to get correct alignment with table width smaller
# then 100%
result.append(self.open('div', newline=1))
# Open table
if not attrs:
attrs = {}
else:
attrs = self._checkTableAttr(attrs, 'table')
result.append(self.open('table', newline=1, attr=attrs))
else:
# Close table then div
result.append(self.close('table'))
result.append(self.close('div'))
return ''.join(result)
def table_row(self, on, attrs=None):
tag = 'tr'
if on:
if not attrs:
attrs = {}
else:
attrs = self._checkTableAttr(attrs, 'row')
return self.open(tag, newline=1, attr=attrs)
return self.close(tag)
def table_cell(self, on, attrs=None):
tag = 'td'
if on:
if not attrs:
attrs = {}
else:
attrs = self._checkTableAttr(attrs, '')
return self.open(tag, newline=1, attr=attrs)
return self.close(tag)
def escapedText(self, text):
return wikiutil.escape(text)