812 lines
22 KiB
Python
Executable File
812 lines
22 KiB
Python
Executable File
# -*- coding: iso-8859-15 -*-
|
|
|
|
|
|
# Glasnost
|
|
# By: Odile Bénassy <obenassy@entrouvert.com>
|
|
# Romain Chantereau <rchantereau@entrouvert.com>
|
|
# Nicolas Clapiès <nclapies@easter-eggs.org>
|
|
# Pierre-Antoine Dejace <padejace@entrouvert.be>
|
|
# Thierry Dulieu <tdulieu@easter-eggs.com>
|
|
# Florent Monnier <monnier@codelutin.com>
|
|
# Cédric Musso <cmusso@easter-eggs.org>
|
|
# Frédéric Péters <fpeters@entrouvert.be>
|
|
# Benjamin Poussin <poussin@codelutin.com>
|
|
# Emmanuel Raviart <eraviart@entrouvert.com>
|
|
# Sébastien Régnier <regnier@codelutin.com>
|
|
# Emmanuel Saracco <esaracco@easter-eggs.com>
|
|
#
|
|
# Copyright (C) 2000, 2001 Easter-eggs & Emmanuel Raviart
|
|
# Copyright (C) 2002 Odile Bénassy, Code Lutin, Thierry Dulieu, Easter-eggs,
|
|
# Entr'ouvert, Frédéric Péters, Benjamin Poussin, Emmanuel Raviart,
|
|
# Emmanuel Saracco & Théridion
|
|
# Copyright (C) 2003 Odile Bénassy, Romain Chantereau, Nicolas Clapiès,
|
|
# Code Lutin, Pierre-Antoine Dejace, Thierry Dulieu, Easter-eggs,
|
|
# Entr'ouvert, Florent Monnier, Cédric Musso, Ouvaton, Frédéric Péters,
|
|
# Benjamin Poussin, Rodolphe Quiédeville, Emmanuel Raviart, Sébastien
|
|
# Régnier, Emmanuel Saracco, Théridion & Vecam
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License
|
|
# as published by the Free Software Foundation; either version 2
|
|
# of the License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
|
|
__doc__ = """XHTML Generator (not Glasnost specific)"""
|
|
|
|
__version__ = '$Revision$'[11:-2]
|
|
|
|
|
|
import types
|
|
import urllib
|
|
|
|
import context
|
|
import tools_new as commonTools
|
|
|
|
|
|
class asIs:
|
|
children = None
|
|
inline = 1
|
|
|
|
def __init__(self, *children):
|
|
if children:
|
|
self.children = list(children)
|
|
|
|
def __iadd__(self, child):
|
|
self.append(child)
|
|
return self
|
|
|
|
def __len__(self):
|
|
return len(str(self))
|
|
|
|
def __nonzero__(self):
|
|
if self.children:
|
|
return 1
|
|
else:
|
|
return 0
|
|
|
|
def __str__(self):
|
|
return self.getAsIs()
|
|
|
|
def append(self, child):
|
|
if self.children is None:
|
|
self.children = []
|
|
self.children.append(child)
|
|
|
|
def getAsIs(self, **keywords):
|
|
if not self.children:
|
|
return ''
|
|
else:
|
|
return ''.join([getAsIs(child, **keywords)
|
|
for child in self.children])
|
|
|
|
def getAsXml(self, parent = None, indent = 0, **keywords):
|
|
return self.getAsIs(**keywords)
|
|
|
|
def isInline(self):
|
|
return self.inline
|
|
|
|
|
|
class Character:
|
|
name = None
|
|
|
|
def __len__(self):
|
|
return len(str(self))
|
|
|
|
def __nonzero__(self):
|
|
return 1
|
|
|
|
def __str__(self):
|
|
return self.getAsXml()
|
|
|
|
def getAsIs(self, **keywords):
|
|
return self.getAsXml(**keywords)
|
|
|
|
def getAsXml(self, parent = None, indent = 0, **keywords):
|
|
if parent is not None and parent.preserveSpacing:
|
|
return '&%s;' % self.name
|
|
else:
|
|
return """\
|
|
%(spacing)s&%(name)s;
|
|
""" % {
|
|
'name': self.name,
|
|
'spacing': ' ' * indent,
|
|
}
|
|
|
|
def isInline(self):
|
|
return 1
|
|
|
|
|
|
class array:
|
|
children = None
|
|
|
|
def __init__(self, *children):
|
|
if children:
|
|
self.children = list(children)
|
|
|
|
def __iadd__(self, child):
|
|
self.append(child)
|
|
return self
|
|
|
|
def __len__(self):
|
|
return len(str(self))
|
|
|
|
def __nonzero__(self):
|
|
if self.children:
|
|
return 1
|
|
else:
|
|
return 0
|
|
|
|
def __str__(self):
|
|
return self.getAsXml()
|
|
|
|
def append(self, child):
|
|
if self.children is None:
|
|
self.children = []
|
|
self.children.append(child)
|
|
|
|
def getAsIs(self, **keywords):
|
|
if not self.children:
|
|
return ''
|
|
else:
|
|
return ''.join([ getAsIs(child, **keywords)
|
|
for child in self.children])
|
|
|
|
def getAsXml(self, parent = None, indent = 0, **keywords):
|
|
if not self.children:
|
|
return ''
|
|
else:
|
|
return ''.join([
|
|
getAsXml(child, parent = parent, indent = indent, **keywords)
|
|
for child in self.children])
|
|
|
|
def isInline(self):
|
|
if self.children:
|
|
for child in self.children:
|
|
if not isInline(child):
|
|
return 0
|
|
return 1
|
|
|
|
|
|
class Tag:
|
|
children = None
|
|
emptyElement = 0 # Can the tag use empty tag shorthand?
|
|
inline = 1 # Should the tag be enclosed in span (when inline is true) or
|
|
# div?
|
|
name = None
|
|
preserveSpacing = 0
|
|
typoEnhancements = 0
|
|
|
|
def __init__(self, *children, **attributes):
|
|
if children:
|
|
assert not attributes
|
|
self.children = list(children)
|
|
for name, value in attributes.items():
|
|
if name[0] == '_':
|
|
name = name[1:]
|
|
setattr(self, 'attribute_' + name, value)
|
|
|
|
def __call__(self, *children):
|
|
assert self.children is None
|
|
self.children = list(children)
|
|
return self
|
|
|
|
def __iadd__(self, child):
|
|
self.append(child)
|
|
return self
|
|
|
|
def __len__(self):
|
|
return len(str(self))
|
|
|
|
def __nonzero__(self):
|
|
return 1
|
|
|
|
def __str__(self):
|
|
return self.getAsXml()
|
|
|
|
def append(self, child):
|
|
if self.children is None:
|
|
self.children = []
|
|
self.children.append(child)
|
|
|
|
def getAsIs(self, **keywords):
|
|
return self.getAsXml(**keywords)
|
|
|
|
def getAsXml(self, parent = None, indent = 0, **keywords):
|
|
attributes = ' '.join([
|
|
'%s="%s"' % (name, getAsXmlAttributeValue(value, tag = self,
|
|
**keywords))
|
|
for name, value in self.getAttributes()])
|
|
if attributes:
|
|
attributes = ' ' + attributes
|
|
if not self.children:
|
|
children = ''
|
|
else:
|
|
children = ''.join([
|
|
self.getChildAsXml(child, parent = self, indent = indent + 1,
|
|
**keywords)
|
|
for child in self.children])
|
|
if not children and self.emptyElement:
|
|
if parent is not None and parent.preserveSpacing:
|
|
return '<%(tag)s%(attributes)s />' % {
|
|
'attributes': attributes,
|
|
'tag': self.name,
|
|
}
|
|
else:
|
|
return """\
|
|
%(spacing)s<%(tag)s%(attributes)s />
|
|
""" % {
|
|
'attributes': attributes,
|
|
'spacing': ' ' * indent,
|
|
'tag': self.name,
|
|
}
|
|
else:
|
|
if parent is not None and parent.preserveSpacing:
|
|
if self.preserveSpacing:
|
|
return '<%(tag)s%(attributes)s>%(children)s</%(tag)s>' % {
|
|
'attributes': attributes,
|
|
'children': children,
|
|
'tag': self.name,
|
|
}
|
|
else:
|
|
return """\
|
|
<%(tag)s%(attributes)s>
|
|
%(children)s\
|
|
%(spacing)s</%(tag)s>\
|
|
""" % {
|
|
'attributes': attributes,
|
|
'children': children,
|
|
'spacing': ' ' * indent,
|
|
'tag': self.name,
|
|
}
|
|
elif self.preserveSpacing:
|
|
return """\
|
|
%(spacing)s<%(tag)s%(attributes)s>%(children)s</%(tag)s>
|
|
""" % {
|
|
'attributes': attributes,
|
|
'children': children,
|
|
'spacing': ' ' * indent,
|
|
'tag': self.name,
|
|
}
|
|
else:
|
|
return """\
|
|
%(spacing)s<%(tag)s%(attributes)s>
|
|
%(children)s\
|
|
%(spacing)s</%(tag)s>
|
|
""" % {
|
|
'attributes': attributes,
|
|
'children': children,
|
|
'spacing': ' ' * indent,
|
|
'tag': self.name,
|
|
}
|
|
|
|
def getAttribute(self, name):
|
|
if name[0] == '_':
|
|
name = name[1:]
|
|
try:
|
|
return getattr(self, 'attribute_' + name)
|
|
except KeyError:
|
|
return ''
|
|
|
|
def getAttributeNames(self):
|
|
baseClasses = self.getC3ClassLinearization()
|
|
names = [ name
|
|
for name in self.__dict__.keys()
|
|
if name.startswith('attribute_')]
|
|
for baseClass in baseClasses:
|
|
for name in baseClass.__dict__.keys():
|
|
if name.startswith('attribute_') and name not in names:
|
|
names.append(name)
|
|
return [ name[10:]
|
|
for name in names
|
|
if getattr(self, name) is not None]
|
|
|
|
def getAttributes(self):
|
|
names = self.getAttributeNames()
|
|
names.sort()
|
|
return [ (name, getattr(self, 'attribute_' + name))
|
|
for name in names]
|
|
|
|
def getC3ClassLinearization(self):
|
|
return commonTools.getC3ClassLinearization(self.__class__)
|
|
|
|
def getChildAsXml(self, child, **keywords):
|
|
return getAsXml(child, **keywords)
|
|
|
|
def getPython22ClassLinearization(self):
|
|
baseClasses = []
|
|
commonTools.buildPython22ClassLinearization(
|
|
self.__class__, baseClasses)
|
|
return baseClasses
|
|
|
|
def isInline(self):
|
|
if not self.inline:
|
|
return 0
|
|
if self.children:
|
|
for child in self.children:
|
|
if not isInline(child):
|
|
return 0
|
|
return 1
|
|
|
|
def setAttribute(self, name, value):
|
|
if name[0] == '_':
|
|
name = name[1:]
|
|
setattr(self, 'attribute_' + name, value)
|
|
if value is None:
|
|
delattr(self, 'attribute_' + name)
|
|
|
|
|
|
class urlCommon:
|
|
hostName = None
|
|
port = None # Must be a string, not a integer.
|
|
protocol = None
|
|
|
|
def __len__(self):
|
|
return len(str(self))
|
|
|
|
def __nonzero__(self):
|
|
return 1
|
|
|
|
def __str__(self):
|
|
return self.getAsUrl()
|
|
|
|
def getAsIs(self, **keywords):
|
|
return self.getAsUrl(**keywords)
|
|
|
|
def getAsXmlAttributeValue(self, tag = None, **keywords):
|
|
return convertStringToXmlAttributeValue(self.getAsUrl(**keywords))
|
|
|
|
|
|
class httpUrl(urlCommon):
|
|
fragment = None
|
|
parameters = None
|
|
path = None
|
|
# protocol = http or https
|
|
query = None
|
|
|
|
def __init__(self, protocol = None, hostNameAndPort = None,
|
|
hostName = None, port = None, path = None, parameters = None,
|
|
query = None, fragment = None):
|
|
if protocol is not None:
|
|
self.protocol = protocol
|
|
else:
|
|
httpProtocol = context.getVar('httpProtocol')
|
|
if httpProtocol is not None:
|
|
self.protocol = httpProtocol
|
|
if hostNameAndPort is not None:
|
|
infos = hostNameAndPort.split(':', 1)
|
|
hostName = infos[0]
|
|
if len(infos) < 2:
|
|
port = None
|
|
else:
|
|
port = infos[1]
|
|
if hostName is not None:
|
|
self.hostName = hostName
|
|
else:
|
|
httpHostName = context.getVar('httpHostName')
|
|
if httpHostName is not None:
|
|
self.hostName = httpHostName
|
|
if port is not None:
|
|
self.port = port
|
|
else:
|
|
httpPort = context.getVar('httpPort')
|
|
if httpPort is not None:
|
|
self.port = httpPort
|
|
if path is not None:
|
|
self.path = path
|
|
assert self.path
|
|
if self.path[0] != '/':
|
|
self.path = '/' + self.path
|
|
else:
|
|
httpPath = context.getVar('httpPath')
|
|
if httpPath is not None:
|
|
self.path = httpPath
|
|
assert self.path
|
|
if self.path[0] != '/':
|
|
self.path = '/' + self.path
|
|
if parameters is not None:
|
|
self.parameters = parameters
|
|
if query is not None:
|
|
self.query = query
|
|
if fragment is not None:
|
|
self.fragment = fragment
|
|
|
|
def add(self, name, value):
|
|
setattr(self, 'argument_' + name, value)
|
|
return self
|
|
|
|
def getArgumentNames(self):
|
|
baseClasses = self.getC3ClassLinearization()
|
|
names = [ name
|
|
for name in self.__dict__.keys()
|
|
if name.startswith('argument_')]
|
|
for baseClass in baseClasses:
|
|
for name in baseClass.__dict__.keys():
|
|
if name.startswith('argument_') and name not in names:
|
|
names.append(name)
|
|
return [ name[9:]
|
|
for name in names
|
|
if getattr(self, name) is not None]
|
|
|
|
def getArguments(self):
|
|
names = self.getArgumentNames()
|
|
names.sort()
|
|
return [ (name, getattr(self, 'argument_' + name))
|
|
for name in names]
|
|
|
|
def getAsAbsoluteUrl(self, **keywords):
|
|
if self.protocol is not None:
|
|
protocol = self.protocol
|
|
else:
|
|
httpProtocol = context.getVar('httpProtocol')
|
|
if httpProtocol is not None:
|
|
protocol = httpProtocol
|
|
else:
|
|
protocol = 'http'
|
|
if self.hostName is not None:
|
|
hostName = self.hostName
|
|
else:
|
|
httpHostName = context.getVar('httpHostName')
|
|
if httpHostName is not None:
|
|
hostName = httpHostName
|
|
else:
|
|
hostName = 'localhost'
|
|
if self.port is not None:
|
|
port = self.port
|
|
else:
|
|
httpPort = context.getVar('httpPort')
|
|
if httpPort is not None:
|
|
port = httpPort
|
|
else:
|
|
port = None # Default http or https port.
|
|
pathAndQuery = self.getAsRelativeUrl()
|
|
if port:
|
|
return '%s://%s:%s%s' % (protocol, hostName, port, pathAndQuery)
|
|
else:
|
|
return '%s://%s%s' % (protocol, hostName, pathAndQuery)
|
|
|
|
def getAsRelativeUrl(self, **keywords):
|
|
relativeUrl = urllib.quote(self.getPath(**keywords))
|
|
if self.parameters:
|
|
relativeUrl = '%s;%s' % (relativeUrl, self.parameters)
|
|
query = '&'.join([ '%s=%s' % (name, urllib.quote(str(value)))
|
|
for name, value in self.getArguments()])
|
|
if self.query and query:
|
|
query = '%s&%s' % (self.query, query)
|
|
elif self.query:
|
|
query = self.query
|
|
if query:
|
|
relativeUrl = '%s?%s' % (relativeUrl, query)
|
|
if self.fragment:
|
|
relativeUrl = '%s#%s' % (relativeUrl, self.fragment)
|
|
return relativeUrl
|
|
|
|
def getAsIs(self, **keywords):
|
|
return self.getAsUrl(**keywords)
|
|
|
|
def getAsUrl(self, **keywords):
|
|
httpProtocol = context.getVar('httpProtocol')
|
|
httpHostName = context.getVar('httpHostName')
|
|
httpPort = context.getVar('httpPort')
|
|
if (self.protocol is None or self.protocol == httpProtocol) \
|
|
and (self.hostName is None or self.hostName == httpHostName) \
|
|
and (self.port is None or self.port == httpPort):
|
|
return self.getAsRelativeUrl(**keywords)
|
|
else:
|
|
return self.getAsAbsoluteUrl(**keywords)
|
|
|
|
def getC3ClassLinearization(self):
|
|
return commonTools.getC3ClassLinearization(self.__class__)
|
|
|
|
def getPath(self, **keywords):
|
|
httpPath = context.getVar('httpPath')
|
|
if self.path is not None:
|
|
path = self.path
|
|
elif httpPath is not None:
|
|
path = httpPath
|
|
else:
|
|
path = '/'
|
|
return path
|
|
|
|
def getPython22ClassLinearization(self):
|
|
baseClasses = []
|
|
commonTools.buildPython22ClassLinearization(
|
|
self.__class__, baseClasses)
|
|
return baseClasses
|
|
|
|
|
|
# Character entity references.
|
|
|
|
|
|
class Nbsp(Character):
|
|
name = 'nbsp'
|
|
nbsp = Nbsp()
|
|
|
|
|
|
# Tags.
|
|
|
|
class a(Tag):
|
|
name = 'a'
|
|
preserveSpacing = 1
|
|
|
|
class body(Tag):
|
|
name = 'body'
|
|
|
|
class br(Tag):
|
|
emptyElement = 1
|
|
name = 'br'
|
|
|
|
class button(Tag):
|
|
name = 'button'
|
|
preserveSpacing = 1
|
|
|
|
class code(Tag):
|
|
name = 'code'
|
|
preserveSpacing = 1
|
|
|
|
class col(Tag):
|
|
emptyElement = 1
|
|
name = 'col'
|
|
|
|
class colgroup(Tag):
|
|
name = 'colgroup'
|
|
|
|
class div(Tag):
|
|
inline = 0
|
|
name = 'div'
|
|
preserveSpacing = 0
|
|
|
|
class em(Tag):
|
|
name = 'em'
|
|
preserveSpacing = 1
|
|
|
|
class fieldset(Tag):
|
|
inline = 0
|
|
name = 'fieldset'
|
|
|
|
class form(Tag):
|
|
name = 'form'
|
|
|
|
class h1(Tag):
|
|
name = 'h1'
|
|
preserveSpacing = 1
|
|
|
|
class h2(Tag):
|
|
name = 'h2'
|
|
preserveSpacing = 1
|
|
|
|
class h3(Tag):
|
|
name = 'h3'
|
|
preserveSpacing = 1
|
|
|
|
class h4(Tag):
|
|
name = 'h4'
|
|
preserveSpacing = 1
|
|
|
|
class head(Tag):
|
|
name = 'head'
|
|
|
|
class html(Tag):
|
|
name = 'html'
|
|
|
|
class hr(Tag):
|
|
emptyElement = 1
|
|
name = 'hr'
|
|
|
|
class img(Tag):
|
|
emptyElement = 1
|
|
name = 'img'
|
|
|
|
class input(Tag):
|
|
emptyElement = 1
|
|
name = 'input'
|
|
|
|
class label(Tag):
|
|
name = 'label'
|
|
preserveSpacing = 1
|
|
|
|
class li(Tag):
|
|
name = 'li'
|
|
|
|
class optgroup(Tag):
|
|
name = 'optgroup'
|
|
preserveSpacing = 1
|
|
|
|
class option(Tag):
|
|
name = 'option'
|
|
preserveSpacing = 1
|
|
typoEnhancements = 1
|
|
|
|
class p(Tag):
|
|
inline = 0
|
|
name = 'p'
|
|
preserveSpacing = 0
|
|
typoEnhancements = 1
|
|
|
|
class pre(Tag):
|
|
inline = 0
|
|
name = 'pre'
|
|
preserveSpacing = 1
|
|
|
|
class select(Tag):
|
|
name = 'select'
|
|
|
|
class span(Tag):
|
|
name = 'span'
|
|
preserveSpacing = 1
|
|
|
|
class strong(Tag):
|
|
name = 'strong'
|
|
preserveSpacing = 1
|
|
|
|
class caption(Tag):
|
|
name = 'caption'
|
|
preserveSpacing = 1
|
|
|
|
class ol(Tag):
|
|
inline = 0
|
|
name = 'ol'
|
|
|
|
class table(Tag):
|
|
inline = 0
|
|
name = 'table'
|
|
|
|
class td(Tag):
|
|
name = 'td'
|
|
preserveSpacing = 1
|
|
|
|
class th(Tag):
|
|
name = 'th'
|
|
preserveSpacing = 1
|
|
|
|
class thead(Tag):
|
|
name = 'thead'
|
|
preserveSpacing = 1
|
|
|
|
class tbody(Tag):
|
|
name = 'tbody'
|
|
preserveSpacing = 1
|
|
|
|
class tfoot(Tag):
|
|
name = 'tfoot'
|
|
preserveSpacing = 1
|
|
|
|
class textarea(Tag):
|
|
name = 'textarea'
|
|
preserveSpacing = 1
|
|
|
|
class title(Tag):
|
|
name = 'title'
|
|
preserveSpacing = 1
|
|
|
|
class tr(Tag):
|
|
name = 'tr'
|
|
|
|
class tt(Tag):
|
|
name = 'tt'
|
|
preserveSpacing = 1
|
|
|
|
class ul(Tag):
|
|
inline = 0
|
|
name = 'ul'
|
|
|
|
|
|
class gridBagLayout(table):
|
|
# FIXME: Perhaps modify this component, if we can do better html code
|
|
# for this component feature.
|
|
_class = 'gridBagLayout'
|
|
|
|
|
|
def convertStringToXml(s):
|
|
s = s.replace('&', '&')
|
|
s = s.replace('<', '<')
|
|
return s
|
|
|
|
|
|
def convertStringToXmlAttributeValue(s):
|
|
### FIXME: Which characters should be transcoded?
|
|
s = s.replace('&', '&') # ?
|
|
s = s.replace('"', '"') # '"' must be converted.
|
|
s = s.replace('<', '<') # ?
|
|
s = s.replace('>', '>') # ?
|
|
return s
|
|
|
|
|
|
def enclose(object, **enclosingAttributes):
|
|
if not object:
|
|
return None
|
|
if isInline(object):
|
|
return span(**enclosingAttributes)(object)
|
|
else:
|
|
return div(**enclosingAttributes)(object)
|
|
|
|
|
|
def isInline(object):
|
|
if not object:
|
|
return 1
|
|
elif type(object) == types.InstanceType and hasattr(object, 'isInline'):
|
|
return object.isInline()
|
|
elif type(object) in [types.ListType, types.TupleType]:
|
|
for child in object:
|
|
if not isInline(object):
|
|
return 0
|
|
return 1
|
|
else:
|
|
return 1
|
|
|
|
|
|
def enhanceTypo(s):
|
|
# typographic junkie
|
|
# idea from: http://daringfireball.net/projects/smartypants/
|
|
s = s.replace('...', '…') # ellipsis (...)
|
|
s = s.replace(' -- ', ' — ') # em-dash
|
|
s = s.replace('(c)', '© ') # copyright symbol
|
|
return s
|
|
|
|
|
|
def getAsIs(object, **keywords):
|
|
if object is None:
|
|
return ''
|
|
elif type(object) in [types.ListType, types.TupleType]:
|
|
return ''.join([
|
|
getAsIs(item, **keywords)
|
|
for item in object])
|
|
else:
|
|
return str(object)
|
|
|
|
|
|
def getAsXml(object, parent = None, indent = 0, **keywords):
|
|
if object is None:
|
|
return ''
|
|
elif type(object) == types.InstanceType and hasattr(object, 'getAsXml'):
|
|
return object.getAsXml(parent = parent, indent = indent, **keywords)
|
|
elif type(object) in [types.ListType, types.TupleType]:
|
|
return ''.join([
|
|
getAsXml(item, parent = parent, indent = indent, **keywords)
|
|
for item in object])
|
|
else:
|
|
strObject = convertStringToXml(str(object))
|
|
if parent is not None and parent.typoEnhancements:
|
|
strObject = enhanceTypo(strObject)
|
|
if parent is not None and parent.preserveSpacing:
|
|
return strObject
|
|
else:
|
|
return """\
|
|
%(spacing)s%(object)s
|
|
""" % {
|
|
'object': strObject,
|
|
'spacing': ' ' * indent,
|
|
}
|
|
|
|
|
|
def getAsXmlAttributeValue(object, tag = None, **keywords):
|
|
if object is None:
|
|
return ''
|
|
elif isinstance(object, urlCommon):
|
|
return object.getAsXmlAttributeValue(tag = tag, **keywords)
|
|
else:
|
|
return convertStringToXmlAttributeValue(str(object))
|
|
|
|
|
|
def main():
|
|
print html(
|
|
head(title('Test page')),
|
|
body(a(href = 'http://www.entrouvert.org')('entr\'ouvert'),
|
|
a(href = httpUrl(path = '/index.html'))('Index'),
|
|
a(href = httpUrl(port = '8080', path = '/index.html'))('Index'),
|
|
)
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|
|
|