From: Stephen Soltesz Date: Wed, 30 Jul 2008 20:05:07 +0000 (+0000) Subject: Adding third-party module used for Monitor's web pages. X-Git-Tag: Monitor-1.0-6~45 X-Git-Url: http://git.onelab.eu/?p=monitor.git;a=commitdiff_plain;h=9a5afb968bf6ee5c9b03470c957b98906d3b78a9 Adding third-party module used for Monitor's web pages. --- diff --git a/www/HyperText/Documents.py b/www/HyperText/Documents.py new file mode 100644 index 0000000..60c4f69 --- /dev/null +++ b/www/HyperText/Documents.py @@ -0,0 +1,109 @@ +__version__ = "$Revision: 1.4 $"[11:-4] + +import HTML +import sys +from string import join + +class Document: + + generator = HTML.META(name="generator", + content="HyperText package (Python)") + DOCTYPE = HTML.DOCTYPE + body_element = HTML.BODY + + def __init__(self, *content, **attrs): + from HTML import HEAD, HTML + self.doctype = self.DOCTYPE + self.body = apply(self.body_element, content, attrs) + self.head = HEAD(self.generator) + if hasattr(self, 'style'): self.head.append(self.style) + if hasattr(self, 'title'): self.head.append(self.title) + self.html = HTML(self.head, self.body) + self.setup() + + def setup(self): pass + + def append(self, *items): map(self.body.content.append, items) + + def __str__(self, indent=0, perlevel=2): + return join([self.doctype.__str__(indent, perlevel), + self.html.__str__(indent, perlevel)], '') + + def writeto(self, fp=sys.stdout, indent=0, perlevel=2): + self.doctype.writeto(fp, indent, perlevel) + self.html.writeto(fp, indent, perlevel) + + +class FramesetDocument(Document): + + DOCTYPE = HTML.DOCTYPE_frameset + body_element = HTML.FRAMESET + + +class CGIMixIn: + + def setup(self): + self.content_type = "text/html" + self.headers = [] + self.nobody = 0 + + def _str_content_type(self): + return 'Content-Type: %s\r\n\r\n' % self.content_type + + def __str__(self, indent=0, perlevel=2): + s = self.headers[:] + s.append(self._str_content_type()) + if not self.nobody: + s.append(self.doctype.__str__(indent, perlevel)) + s.append(self.html.__str__(indent, perlevel)) + return join(s, '') + + def writeto(self, fp=sys.stdout, indent=0, perlevel=2): + fp.writelines(self.headers) + fp.write(self._str_content_type()) + if not self.nobody: + self.doctype.writeto(fp, indent, perlevel) + self.html.writeto(fp, indent, perlevel) + + +class HTTPMixIn(CGIMixIn): + + def setup(self): + CGIMixIn.setup(self) + if not hasattr(self, 'response'): + self.response = (200, 'Output follows') + self.date = None + + http_response_str = "%s %s %s\r\nServer: %s %s\r\nDate: %s\r\n" + + def _str_http_response(self): + if hasattr(self, 'request'): + apply(self.request.log_request, self.response) + return self.http_response_str \ + % (self.request.request_version, + self.response[0], + self.response[1], + self.request.server_version, + self.request.sys_version, + self.date) + else: + return self.http_response_str \ + % ('HTTP/1.0', + self.response[0], + self.response[1], + "Dunno/0.0", + "BeatzMe/0.0", + self.date) + + def __str__(self, indent=0, perlevel=2): + return join([self._str_http_response(), + CGIMixIn.__str__(self, indent, perlevel)], '') + + def writeto(self, fp=sys.stdout, indent=0, perlevel=2): + fp.write(self._str_http_response()) + CGIMixIn.writeto(self, fp, indent, perlevel) + + + + + diff --git a/www/HyperText/HTML.py b/www/HyperText/HTML.py new file mode 100644 index 0000000..0c7b575 --- /dev/null +++ b/www/HyperText/HTML.py @@ -0,0 +1 @@ +from HTML40 import * diff --git a/www/HyperText/HTML40.py b/www/HyperText/HTML40.py new file mode 100644 index 0000000..13fd458 --- /dev/null +++ b/www/HyperText/HTML40.py @@ -0,0 +1,695 @@ +"""HTML40 -- generate HTML conformant to the 4.0 standard. See: + + http://www.w3.org/TR/REC-html40/ + +All HTML 4.0 elements are implemented except for a few which are +deprecated. All attributes should be implemented. HTML is generally +case-insensitive, whereas Python is not. All elements have been coded +in UPPER CASE, with attributes in lower case. General usage: + + e = ELEMENT(*content, **attr) + +i.e., the positional arguments become the content of the element, and +the keyword arguments set element attributes. All attributes MUST be +specified with keyword arguments, and the content MUST be a series of +positional arguments; if you use content="spam", it will set this as +the attribute content, not as the element content. Multiple content +arguments are simply joined with no separator. Example: + +>>> t = TABLE(TR(TH('SPAM'), TH('EGGS')), TR(TD('foo','bar', colspan=2)) ) +>>> print t + + + + + + +
SPAMEGGS
foobar
+ +As with HTMLgen and other HTML generators, you can print the object +and it makes one monster string and writes that to stdout. Unlike +HTMLgen, these objects all have a writeto(fp=stdout, indent=0, +perlevel=2) method. This method may save memory, and it might be +faster possibly (many fewer string joins), plus you get progressive +output. If you want to alter the indentation on the string output, +try: + +>> print t.__str__(indent=5, perlevel=6) + + + + + + +
SPAMEGGS
foobar
+ +The output from either method (__str__ or writeto) SHOULD be the +lexically equivalent, regardless of indentation; not all elements are +made pretty, only those which are insensitive to the addition of +whitespace before or after the start/end tags. If you don't like the +indenting, use writeto(perlevel=0) (or __str__(perlevel=0)). + +Element attributes can be set through the normal Python dictionary +operations on the object (they are not Python attributes). + +Note: There are a few HTML attributes with a dash in them. In these +cases, substitute an underscore and the output will be corrected. HTML +4.0 also defines a class attribute, which conflicts with Python's +class statement; use klass instead. The new LABEL element has a for +attribute which also conflicts; use label_for instead. + +>>> print META(http_equiv='refresh',content='60;/index2.html') + + +The output order of attributes is indeterminate (based on hash order), +but this is of no particular importance. The extent of attribute +checking is limited to checking that the attribute is legal for that +element; the values themselves are not checked, but must be +convertible to a string. The content items must be convertible to +strings and/or have a writeto() method. Some elements may have a few +attributes they shouldn't, particularly those which use intrinsic +events. + +Valid attributes are defined for each element with dictionaries, with +the keys being the attributes. If the value is false, it's a boolean; +otherwise the value is printed. + +Subclassing: If all you need to do is have some defaults, override the +defaults dictionary. You will also need to set name to the correct +element name. Example: + +>>> class Refresh(META): defaults = {'http_equiv': 'refresh'}; name = 'META' +... +>>> print Refresh(content='10; /index2.html') + + +Weirdness with Netscape 4.x: It recognizes a border attribute for the +FRAMESET element, though it is not defined in the HTML 4.0 spec. It +seems to recognize the frameborder attribute for FRAME, but border +only changes from a 3D shaded border to a flat, unresizable grey +border. Because of this, there is a border attribute defined for +FRAMESET. Similarly, HTML 4.0 does not define a border attribute for +INPUT (for use with type="image"), but one has been added anyway. + +Historical notes: My first experience with an HTML generator was with +the one which comes with "Internet Programming with Python" by Aaron +Watters, Guido van Rossum, and James C. Ahlstrom. I hate to dis it, +but the thing really drove me nuts after awhile. Horrible to debug +anything, but maybe my understanding of it was incomplete. I then +discovered HTMLgen by Robin Friedrich: + +http://starship.skyport.net/crew/friedrich/HTMLgen/html/main.html + +It worked much better, for me at least, good enough for a major +project. There were, however, some frustrations: Subclassing could +sometimes be difficult (in fairness, I think that was by design), and +there were some missing features I wanted. Plus the thing's huge, as +Python modules go. These are relatively minor gripes, and if you don't +like this module, definitely use HTMLgen. + +Mainly I did this because the methodology to do it just sorta dawned +on me. The result is, I think, some pretty clean code. Really, there's +hardly any actual code at all. Hey, and when was the last time saw a +subclass inherit from only one parent class with only a pass statement +and no attributes defined? There's 27 of them here. There's almost no +logic to it at all; it's pretty much all driven by dictionaries. + +Yes, there are a number of features missing which are present in +HTMLgen, namely the document classes. All the high-level abstractions +are going in another module or two. + +""" + +__version__ = "$Revision: 1.8 $"[11:-4] + +import string +from string import lower, join, replace +from sys import stdout + +coreattrs = {'id': 1, 'klass': 1, 'style': 1, 'title': 1} +i18n = {'lang': 1, 'dir': 1} +intrinsic_events = {'onload': 1, 'onunload': 1, 'onclick': 1, + 'ondblclick': 1, 'onmousedown': 1, 'onmouseup': 1, + 'onmouseover': 1, 'onmousemove': 1, 'onmouseout': 1, + 'onfocus': 1, 'onblur': 1, 'onkeypress': 1, + 'onkeydown': 1, 'onkeyup': 1, 'onsubmit': 1, + 'onreset': 1, 'onselect': 1, 'onchange': 1 } + +attrs = coreattrs.copy() +attrs.update(i18n) +attrs.update(intrinsic_events) + +alternate_text = {'alt': 1} +image_maps = {'shape': 1, 'coords': 1} +anchor_reference = {'href': 1} +target_frame_info = {'target': 1} +tabbing_navigation = {'tabindex': 1} +access_keys = {'accesskey': 1} + +tabbing_and_access = tabbing_navigation.copy() +tabbing_and_access.update(access_keys) + +visual_presentation = {'height': 1, 'width': 1, 'border': 1, 'align': 1, + 'hspace': 1, 'vspace': 1} + +cellhalign = {'align': 1, 'char': 1, 'charoff': 1} +cellvalign = {'valign': 1} + +font_modifiers = {'size': 1, 'color': 1, 'face': 1} + +links_and_anchors = {'href': 1, 'hreflang': 1, 'type': 1, 'rel': 1, 'rev': 1} +borders_and_rules = {'frame': 1, 'rules': 1, 'border': 1} + +from SGML import Markup, Comment +from XML import XMLPI + +DOCTYPE = Markup("DOCTYPE", + 'HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" ' \ + '"http://www.w3.org/TR/REC-html40/loose.dtd"') +DOCTYPE_frameset = Markup("DOCTYPE", + 'HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN" ' \ + '"http://www.w3.org/TR/REC-HTML/frameset.dtd"') + +class Element(XMLPI): + + defaults = {} + attr_translations = {'klass': 'class', + 'label_for': 'for', + 'http_equiv': 'http-equiv', + 'accept_charset': 'accept-charset'} + + def __init__(self, *content, **attr): + self.dict = {} + if not hasattr(self, 'name'): self.name = self.__class__.__name__ + if self.defaults: self.update(self.defaults) + self.update(attr) + if not self.content_model and content: + raise TypeError, "No content for this element" + self.content = list(content) + + def update(self, d2): + for k, v in d2.items(): self[k] = v + + def __setitem__(self, k, v): + kl = lower(k) + if self.attlist.has_key(kl): self.dict[kl] = v + else: raise KeyError, "Invalid attribute for this element" + + start_tag_string = "<%s %s>" + start_tag_no_attr_string = "<%s>" + end_tag_string = "" + + def str_attribute(self, k): + return self.attlist.get(k, 1) and '%s="%s"' % \ + (self.attr_translations.get(k, k), str(self[k])) \ + or self[k] and k or '' + + def start_tag(self): + a = self.str_attribute_list() + return a and self.start_tag_string % (self.name, a) \ + or self.start_tag_no_attr_string % self.name + + def end_tag(self): + return self.content_model and self.end_tag_string % self.name or '' + + +class PrettyTagsMixIn: + + def writeto(self, fp=stdout, indent=0, perlevel=2): + myindent = '\n' + " "*indent + fp.write(myindent+self.start_tag()) + for c in self.content: + if hasattr(c, 'writeto'): + getattr(c, 'writeto')(fp, indent+perlevel, perlevel) + else: + fp.write(str(c)) + fp.write(self.end_tag()) + + def __str__(self, indent=0, perlevel=2): + myindent = (perlevel and '\n' or '') + " "*indent + s = [myindent, self.start_tag()] + for c in self.content: + try: s.append(apply(c.__str__, (indent+perlevel, perlevel))) + except: s.append(str(c)) + s.append(self.end_tag()) + return join(s,'') + +class CommonElement(Element): attlist = attrs + +class PCElement(PrettyTagsMixIn, CommonElement): pass + +class A(CommonElement): + + attlist = {'name': 1, 'charset': 1} + attlist.update(CommonElement.attlist) + attlist.update(links_and_anchors) + attlist.update(image_maps) + attlist.update(target_frame_info) + attlist.update(tabbing_and_access) + + +class ABBR(CommonElement): pass +class ACRONYM(CommonElement): pass +class CITE(CommonElement): pass +class CODE(CommonElement): pass +class DFN(CommonElement): pass +class EM(CommonElement): pass +class KBD(CommonElement): pass +class PRE(CommonElement): pass +class SAMP(CommonElement): pass +class STRONG(CommonElement): pass +class VAR(CommonElement): pass +class ADDRESS(CommonElement): pass +class B(CommonElement): pass +class BIG(CommonElement): pass +class I(CommonElement): pass +class S(CommonElement): pass +class SMALL(CommonElement): pass +class STRIKE(CommonElement): pass +class TT(CommonElement): pass +class U(CommonElement): pass +class SUB(CommonElement): pass +class SUP(CommonElement): pass + +class DD(PCElement): pass +class DL(PCElement): pass +class DT(PCElement): pass +class NOFRAMES(PCElement): pass +class NOSCRIPTS(PCElement): pass +class P(PCElement): pass + +class AREA(PCElement): + + attlist = {'name': 1, 'nohref': 0} + attlist.update(PCElement.attlist) + attlist.update(image_maps) + attlist.update(anchor_reference) + attlist.update(tabbing_and_access) + attlist.update(alternate_text) + +class MAP(AREA): pass + +class BASE(PrettyTagsMixIn, Element): + + attlist = anchor_reference.copy() + attlist.update(target_frame_info) + content_model = None + +class BDO(Element): + + attlist = coreattrs.copy() + attlist.update(i18n) + +class BLOCKQUOTE(CommonElement): + + attlist = {'cite': 1} + attlist.update(CommonElement.attlist) + +class Q(BLOCKQUOTE): pass + +class BR(PrettyTagsMixIn, Element): + + attlist = coreattrs + content_model = None + +class BUTTON(CommonElement): + + attlist = {'name': 1, 'value': 1, 'type': 1, 'disabled': 0} + attlist.update(CommonElement.attlist) + attlist.update(tabbing_and_access) + +class CAPTION(Element): + + attlist = {'align': 1} + attlist.update(attrs) + +class COLGROUP(PCElement): + + attlist = {'span': 1, 'width': 1} + attlist.update(PCElement.attlist) + attlist.update(cellhalign) + attlist.update(cellvalign) + +class COL(COLGROUP): content_model = None + +class DEL(Element): + + attlist = {'cite': 1, 'datetime': 1} + attlist.update(attrs) + +class INS(DEL): pass + +class FIELDSET(PCElement): pass + +class LEGEND(PCElement): + + attlist = {'align': 1} + attlist.update(PCElement.attlist) + attlist.update(access_keys) + +class BASEFONT(Element): + + attlist = {'id': 1} + attlist.update(font_modifiers) + content_model = None + +class FONT(Element): + + attlist = font_modifiers.copy() + attlist.update(coreattrs) + attlist.update(i18n) + +class FORM(PCElement): + + attlist = {'action': 1, 'method': 1, 'enctype': 1, 'accept_charset': 1, + 'target': 1} + attlist.update(PCElement.attlist) + +class FRAME(PrettyTagsMixIn, Element): + + attlist = {'longdesc': 1, 'name': 1, 'src': 1, 'frameborder': 1, + 'marginwidth': 1, 'marginheight': 1, 'noresize': 0, + 'scrolling': 1} + attlist.update(coreattrs) + content_model = None + +class FRAMESET(PrettyTagsMixIn, Element): + + attlist = {'rows': 1, 'cols': 1, 'border': 1} + attlist.update(coreattrs) + attlist.update(intrinsic_events) + +class Heading(PCElement): + + attlist = {'align': 1} + attlist.update(attrs) + + def __init__(self, level, *content, **attr): + self.level = level + apply(PCElement.__init__, (self,)+content, attr) + + def start_tag(self): + a = self.str_attribute_list() + return a and "" % (self.level, a) or "" % self.level + + def end_tag(self): + return self.content_model and "\n" % self.level or '' + +class HEAD(PrettyTagsMixIn, Element): + + attlist = {'profile': 1} + attlist.update(i18n) + +class HR(Element): + + attlist = {'align': 1, 'noshade': 0, 'size': 1, 'width': 1} + attlist.update(coreattrs) + attlist.update(intrinsic_events) + content_model = None + +class HTML(PrettyTagsMixIn, Element): + + attlist = i18n + +class TITLE(HTML): pass + +class BODY(PCElement): + + attlist = {'background': 1, 'text': 1, 'link': 1, 'vlink': 1, 'alink': 1, + 'bgcolor': 1} + attlist.update(PCElement.attlist) + +class IFRAME(PrettyTagsMixIn, Element): + + attlist = {'longdesc': 1, 'name': 1, 'src': 1, 'frameborder': 1, + 'marginwidth': 1, 'marginheight': 1, 'scrolling': 1, + 'align': 1, 'height': 1, 'width': 1} + attlist.update(coreattrs) + +class IMG(CommonElement): + + attlist = {'src': 1, 'longdesc': 1, 'usemap': 1, 'ismap': 0} + attlist.update(PCElement.attlist) + attlist.update(visual_presentation) + attlist.update(alternate_text) + content_model = None + +class INPUT(CommonElement): + + attlist = {'type': 1, 'name': 1, 'value': 1, 'checked': 0, 'disabled': 0, + 'readonly': 0, 'size': 1, 'maxlength': 1, 'src': 1, + 'usemap': 1, 'accept': 1, 'border': 1} + attlist.update(CommonElement.attlist) + attlist.update(tabbing_and_access) + attlist.update(alternate_text) + content_model = None + +class LABEL(CommonElement): + + attlist = {'label_for': 1} + attlist.update(CommonElement.attlist) + attlist.update(access_keys) + +class UL(PCElement): + + attlist = {'compact': 0} + attlist.update(CommonElement.attlist) + +class OL(UL): + + attlist = {'start': 1} + attlist.update(UL.attlist) + +class LI(UL): + + attlist = {'value': 1, 'type': 1} + attlist.update(UL.attlist) + +class LINK(PCElement): + + attlist = {'charset': 1, 'media': 1} + attlist.update(PCElement.attlist) + attlist.update(links_and_anchors) + content_model = None + +class META(PrettyTagsMixIn, Element): + + attlist = {'http_equiv': 1, 'name': 1, 'content': 1, 'scheme': 1} + attlist.update(i18n) + content_model = None + +class OBJECT(PCElement): + + attlist = {'declare': 0, 'classid': 1, 'codebase': 1, 'data': 1, + 'type': 1, 'codetype': 1, 'archive': 1, 'standby': 1, + 'height': 1, 'width': 1, 'usemap': 1} + attlist.update(PCElement.attlist) + attlist.update(tabbing_navigation) + +class SELECT(PCElement): + + attlist = {'name': 1, 'size': 1, 'multiple': 0, 'disabled': 0} + attlist.update(CommonElement.attlist) + attlist.update(tabbing_navigation) + +class OPTGROUP(PCElement): + + attlist = {'disabled': 0, 'label': 1} + attlist.update(CommonElement.attlist) + +class OPTION(OPTGROUP): + + attlist = {'value': 1, 'selected': 0} + attlist.update(OPTGROUP.attlist) + +class PARAM(Element): + + attlist = {'id': 1, 'name': 1, 'value': 1, 'valuetype': 1, 'type': 1} + +class SCRIPT(Element): + + attlist = {'charset': 1, 'type': 1, 'src': 1, 'defer': 0} + +class SPAN(CommonElement): + + attlist = {'align': 1} + attlist.update(CommonElement.attlist) + +class DIV(PrettyTagsMixIn, SPAN): pass + +class STYLE(PrettyTagsMixIn, Element): + + attlist = {'type': 1, 'media': 1, 'title': 1} + attlist.update(i18n) + +class TABLE(PCElement): + + attlist = {'cellspacing': 1, 'cellpadding': 1, 'summary': 1, 'align': 1, + 'bgcolor': 1, 'width': 1} + attlist.update(CommonElement.attlist) + attlist.update(borders_and_rules) + +class TBODY(PCElement): + + attlist = CommonElement.attlist.copy() + attlist.update(cellhalign) + attlist.update(cellvalign) + +class THEAD(TBODY): pass +class TFOOT(TBODY): pass +class TR(TBODY): pass + +class TH(TBODY): + + attlist = {'abbv': 1, 'axis': 1, 'headers': 1, 'scope': 1, + 'rowspan': 1, 'colspan': 1, 'nowrap': 0, 'width': 1, + 'height': 1} + attlist.update(TBODY.attlist) + +class TD(TH): pass + +class TEXTAREA(CommonElement): + + attlist = {'name': 1, 'rows': 1, 'cols': 1, 'disabled': 0, 'readonly': 0} + attlist.update(CommonElement.attlist) + attlist.update(tabbing_and_access) + +def CENTER(*content, **attr): + c = apply(DIV, content, attr) + c['align'] = 'center' + return c + +def H1(content=[], **attr): return apply(Heading, (1, content), attr) +def H2(content=[], **attr): return apply(Heading, (2, content), attr) +def H3(content=[], **attr): return apply(Heading, (3, content), attr) +def H4(content=[], **attr): return apply(Heading, (4, content), attr) +def H5(content=[], **attr): return apply(Heading, (5, content), attr) +def H6(content=[], **attr): return apply(Heading, (6, content), attr) + +class CSSRule(PrettyTagsMixIn, Element): + + attlist = {'font': 1, 'font_family': 1, 'font_face': 1, 'font_size': 1, + 'border': 1, 'border_width': 1, 'color': 1, + 'background': 1, 'background_color': 1, 'background_image': 1, + 'text_align': 1, 'text_decoration': 1, 'text_indent': 1, + 'line_height': 1, 'margin_left': 1, 'margin_right': 1, + 'clear': 1, 'list_style_type': 1} + content = [] + content_model = None + + def __init__(self, selector, **decl): + self.dict = {} + self.update(decl) + self.name = selector + + start_tag_string = "%s { %s }" + + def end_tag(self): return '' + + def str_attribute(self, k): + kt = replace(k, '_', '-') + if self.attlist[k]: return '%s: %s' % (kt, str(self[k])) + else: return self[k] and kt or '' + + def str_attribute_list(self): + return join(map(self.str_attribute, self.dict.keys()), '; ') + +nbsp = " " + +def quote_body(s): + r=replace; return r(r(r(s, '&', '&'), '<', '<'), '>', '>') + +safe = string.letters + string.digits + '_,.-' + +def url_encode(s): + l = [] + for c in s: + if c in safe: l.append(c) + elif c == ' ': l.append('+') + else: l.append("%%%02x" % ord(c)) + return join(l, '') + +def URL(*args, **kwargs): + url_path = join(args, '/') + a = [] + for k, v in kwargs.items(): + a.append("%s=%s" % (url_encode(k), url_encode(v))) + url_vals = join(a, '&') + return url_vals and join([url_path, url_vals],'?') or url_path + +def Options(options, selected=[], **attrs): + opts = [] + for o, v in options: + opt = apply(OPTION, (o,), attrs) + opt['value'] = v + if v in selected: opt['selected'] = 1 + opts.append(opt) + return opts + +def Select(options, selected=[], **attrs): + return apply(SELECT, tuple(apply(Options, (options, selected))), attrs) + +def Href(url, text, **attrs): + h = apply(A, (text,), attrs) + h['href'] = url + return h + +def Mailto(address, text, subject='', **attrs): + if subject: + url = "mailto:%s?subject=%s" % (address, subject) + else: + url = "mailto:%s" % address + return apply(Href, (url, text), attrs) + +def Image(src, **attrs): + i = apply(IMG, (), a) + i['src'] = src + return i + +def StyledTR(element, row, klasses): + r = TR() + for i in range(len(row)): + r.append(klasses[i] and element(row[i], klass=klasses[i]) \ + or element(row[i])) + return r + +def StyledVTable(klasses, *rows, **attrs): + t = apply(TABLE, (), attrs) + t.append(COL(span=len(klasses))) + for row in rows: + r = StyledTR(TD, row[1:], klasses[1:]) + h = klasses[0] and TH(row[0], klass=klasses[0]) \ + or TH(row[0]) + r.content.insert(0,h) + t.append(r) + return t + +def VTable(*rows, **attrs): + t = apply(TABLE, (), attrs) + t.append(COL(span=len(rows[0]))) + for row in rows: + r = apply(TR, tuple(map(TD, row[1:]))) + r.content.insert(0, TH(row[0])) + t.append(r) + return t + +def StyledHTable(klasses, headers, *rows, **attrs): + t = apply(TABLE, (), attrs) + t.append(COL(span=len(headers))) + t.append(StyledTR(TH, headers, klasses)) + for row in rows: t.append(StyledTR(TD, row, klasses)) + return t + +def HTable(headers, *rows, **attrs): + t = apply(TABLE, (), attrs) + t.append(COL(span=len(headers))) + t.append(TR, tuple(map(TH, headers))) + for row in rows: t.append(TR(apply(TD, row))) + return t + +def DefinitionList(*items, **attrs): + dl = apply(DL, (), attrs) + for dt, dd in items: dl.append(DT(dt), DD(dd)) + return dl + + diff --git a/www/HyperText/SGML.py b/www/HyperText/SGML.py new file mode 100644 index 0000000..e8935ae --- /dev/null +++ b/www/HyperText/SGML.py @@ -0,0 +1,39 @@ +__version__ = "$Revision: 1.1 $"[11:-4] + +from sys import stdout +from string import lower, join, replace + +class Markup: + + content_model = 1 + + def __init__(self, name, *content): + self.name = name + self.dict = {} + self.content = list(content) + + start_tag_string = "\n" + + def writeto(self, fp=stdout, indent=0, perlevel=0): + fp.write(self.start_tag()) + for c in self.content: + if hasattr(c, 'writeto'): + getattr(c, 'writeto')(fp, indent+perlevel, perlevel) + else: + fp.write(str(c)) + fp.write(self.end_tag()) + + def __str__(self, indent=0, perlevel=0): + # we don't actually indent here, it's for later. + c = map(str, self.content) + return join([self.start_tag()]+c+[self.end_tag()],'') + + +def Comment(*comment): return apply(Markup, ('--',)+comment+(' --',)) + diff --git a/www/HyperText/XHTML.py b/www/HyperText/XHTML.py new file mode 100644 index 0000000..a5bd987 --- /dev/null +++ b/www/HyperText/XHTML.py @@ -0,0 +1 @@ +from XHTML10 import * diff --git a/www/HyperText/XHTML10.py b/www/HyperText/XHTML10.py new file mode 100644 index 0000000..92cc9bb --- /dev/null +++ b/www/HyperText/XHTML10.py @@ -0,0 +1,572 @@ +"""XHTML10 -- generate XHTML conformant to the 1.0 standard. See: + + http://www.w3.org/TR/xhtml1/ + +Implemented similarly to HTML40; see the docs in that module. + +""" + +__version__ = "$Revision: 1.4 $"[11:-4] + +import string +from string import lower, join, replace +from sys import stdout + +coreattrs = {'id': 1, 'klass': 1, 'style': 1, 'title': 1} +i18n = {'lang': 1, 'dir': 1} +intrinsic_events = {'onload': 1, 'onunload': 1, 'onclick': 1, + 'ondblclick': 1, 'onmousedown': 1, 'onmouseup': 1, + 'onmouseover': 1, 'onmousemove': 1, 'onmouseout': 1, + 'onfocus': 1, 'onblur': 1, 'onkeypress': 1, + 'onkeydown': 1, 'onkeyup': 1, 'onsubmit': 1, + 'onreset': 1, 'onselect': 1, 'onchange': 1 } + +attrs = coreattrs.copy() +attrs.update(i18n) +attrs.update(intrinsic_events) + +alternate_text = {'alt': 1} +image_maps = {'shape': 1, 'coords': 1} +anchor_reference = {'href': 1} +target_frame_info = {'target': 1} +tabbing_navigation = {'tabindex': 1} +access_keys = {'accesskey': 1} + +tabbing_and_access = tabbing_navigation.copy() +tabbing_and_access.update(access_keys) + +visual_presentation = {'height': 1, 'width': 1, 'border': 1, 'align': 1, + 'hspace': 1, 'vspace': 1} + +cellhalign = {'align': 1, 'char': 1, 'charoff': 1} +cellvalign = {'valign': 1} + +font_modifiers = {'size': 1, 'color': 1, 'face': 1} + +links_and_anchors = {'href': 1, 'hreflang': 1, 'type': 1, 'rel': 1, 'rev': 1} +borders_and_rules = {'frame': 1, 'rules': 1, 'border': 1} + +from SGML import Markup, Comment +from XML import XMLPI + +DOCTYPE = Markup("DOCTYPE", + 'html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" ' \ + '"DTD/xhtml1-transitional.dtd"') +DOCTYPE_frameset = Markup("DOCTYPE", + 'html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" ' \ + '"DTD/xhtml1-frameset.dtd"') + +class Element(XMLPI): + + defaults = {} + + def __init__(self, *content, **attr): + self.dict = {} + if not hasattr(self, 'name'): self.name = lower(self.__class__.__name__) + if self.defaults: self.update(self.defaults) + self.update(attr) + if not self.content_model and content: + raise TypeError, "No content for this element" + self.content = list(content) + + def update(self, d2): + for k, v in d2.items(): self[k] = v + + def __setitem__(self, k, v): + kl = lower(k) + if self.attlist.has_key(kl): self.dict[kl] = v + else: raise KeyError, "Invalid attribute for this element" + + start_tag_string = "<%s %s>" + start_tag_no_attr_string = "<%s>" + end_tag_string = "" + + def start_tag(self): + a = self.str_attribute_list() + return a and self.start_tag_string % (self.name, a) \ + or self.start_tag_no_attr_string % self.name + + def end_tag(self): + return self.end_tag_string % self.name + + +class PrettyTagsMixIn: + + def writeto(self, fp=stdout, indent=0, perlevel=2): + myindent = '\n' + " "*indent + fp.write(myindent+self.start_tag()) + for c in self.content: + if hasattr(c, 'writeto'): + getattr(c, 'writeto')(fp, indent+perlevel, perlevel) + else: + fp.write(str(c)) + fp.write(self.end_tag()) + + def __str__(self, indent=0, perlevel=2): + myindent = (perlevel and '\n' or '') + " "*indent + s = [myindent, self.start_tag()] + for c in self.content: + try: s.append(apply(c.__str__, (indent+perlevel, perlevel))) + except: s.append(str(c)) + s.append(self.end_tag()) + return join(s,'') + +class CommonElement(Element): attlist = attrs + +class PCElement(PrettyTagsMixIn, CommonElement): pass + +class A(CommonElement): + + attlist = {'charset': 1} + attlist.update(CommonElement.attlist) + attlist.update(links_and_anchors) + attlist.update(image_maps) + attlist.update(target_frame_info) + attlist.update(tabbing_and_access) + + +class ABBR(CommonElement): pass +class ACRONYM(CommonElement): pass +class CITE(CommonElement): pass +class CODE(CommonElement): pass +class DFN(CommonElement): pass +class EM(CommonElement): pass +class KBD(CommonElement): pass +class PRE(CommonElement): pass +class SAMP(CommonElement): pass +class STRONG(CommonElement): pass +class VAR(CommonElement): pass +class ADDRESS(CommonElement): pass +class B(CommonElement): pass +class BIG(CommonElement): pass +class I(CommonElement): pass +class S(CommonElement): pass +class SMALL(CommonElement): pass +class STRIKE(CommonElement): pass +class TT(CommonElement): pass +class U(CommonElement): pass +class SUB(CommonElement): pass +class SUP(CommonElement): pass + +class DD(PCElement): pass +class DL(PCElement): pass +class DT(PCElement): pass +class NOFRAMES(PCElement): pass +class NOSCRIPTS(PCElement): pass +class P(PCElement): pass + +class AREA(PCElement): + + attlist = {'nohref': 0} + attlist.update(PCElement.attlist) + attlist.update(image_maps) + attlist.update(anchor_reference) + attlist.update(tabbing_and_access) + attlist.update(alternate_text) + +class MAP(AREA): pass + +class BASE(PrettyTagsMixIn, Element): + + attlist = anchor_reference.copy() + attlist.update(target_frame_info) + content_model = None + +class BDO(Element): + + attlist = coreattrs.copy() + attlist.update(i18n) + +class BLOCKQUOTE(CommonElement): + + attlist = {'cite': 1} + attlist.update(CommonElement.attlist) + +class Q(BLOCKQUOTE): pass + +class BR(PrettyTagsMixIn, Element): + + attlist = coreattrs + content_model = None + +class BUTTON(CommonElement): + + attlist = {'value': 1, 'type': 1, 'disabled': 0} + attlist.update(CommonElement.attlist) + attlist.update(tabbing_and_access) + +class CAPTION(Element): + + attlist = {'align': 1} + attlist.update(attrs) + +class COLGROUP(PCElement): + + attlist = {'span': 1, 'width': 1} + attlist.update(PCElement.attlist) + attlist.update(cellhalign) + attlist.update(cellvalign) + +class COL(COLGROUP): content_model = None + +class DEL(Element): + + attlist = {'cite': 1, 'datetime': 1} + attlist.update(attrs) + +class INS(DEL): pass + +class FIELDSET(PCElement): pass + +class LEGEND(PCElement): + + attlist = {'align': 1} + attlist.update(PCElement.attlist) + attlist.update(access_keys) + +class BASEFONT(Element): + + attlist = {'id': 1} + attlist.update(font_modifiers) + content_model = None + +class FONT(Element): + + attlist = font_modifiers.copy() + attlist.update(coreattrs) + attlist.update(i18n) + +class FORM(PCElement): + + attlist = {'action': 1, 'method': 1, 'enctype': 1, 'accept_charset': 1, + 'target': 1} + attlist.update(PCElement.attlist) + +class FRAME(PrettyTagsMixIn, Element): + + attlist = {'longdesc': 1, 'src': 1, 'frameborder': 1, + 'marginwidth': 1, 'marginheight': 1, 'noresize': 0, + 'scrolling': 1} + attlist.update(coreattrs) + content_model = None + +class FRAMESET(PrettyTagsMixIn, Element): + + attlist = {'rows': 1, 'cols': 1, 'border': 1} + attlist.update(coreattrs) + attlist.update(intrinsic_events) + +class Heading(PCElement): + + attlist = {'align': 1} + attlist.update(attrs) + + def __init__(self, level, *content, **attr): + self.level = level + apply(PCElement.__init__, (self,)+content, attr) + + def start_tag(self): + a = self.str_attribute_list() + return a and "" % (self.level, a) or "" % self.level + + def end_tag(self): + return self.content_model and "\n" % self.level or '' + +class HEAD(PrettyTagsMixIn, Element): + + attlist = {'profile': 1} + attlist.update(i18n) + +class HR(Element): + + attlist = {'align': 1, 'noshade': 0, 'size': 1, 'width': 1} + attlist.update(coreattrs) + attlist.update(intrinsic_events) + content_model = None + +class HTML(PrettyTagsMixIn, Element): + + attlist = i18n + +class TITLE(HTML): pass + +class BODY(PCElement): + + attlist = {'background': 1, 'text': 1, 'link': 1, 'vlink': 1, 'alink': 1, + 'bgcolor': 1} + attlist.update(PCElement.attlist) + +class IFRAME(PrettyTagsMixIn, Element): + + attlist = {'longdesc': 1, 'src': 1, 'frameborder': 1, + 'marginwidth': 1, 'marginheight': 1, 'scrolling': 1, + 'align': 1, 'height': 1, 'width': 1} + attlist.update(coreattrs) + +class IMG(CommonElement): + + attlist = {'src': 1, 'longdesc': 1, 'usemap': 1, 'ismap': 0} + attlist.update(PCElement.attlist) + attlist.update(visual_presentation) + attlist.update(alternate_text) + content_model = None + +class INPUT(CommonElement): + + attlist = {'type': 1, 'value': 1, 'checked': 0, 'disabled': 0, + 'readonly': 0, 'size': 1, 'maxlength': 1, 'src': 1, + 'usemap': 1, 'accept': 1, 'border': 1} + attlist.update(CommonElement.attlist) + attlist.update(tabbing_and_access) + attlist.update(alternate_text) + content_model = None + +class LABEL(CommonElement): + + attlist = {'label_for': 1} + attlist.update(CommonElement.attlist) + attlist.update(access_keys) + +class UL(PCElement): + + attlist = {'compact': 0} + attlist.update(CommonElement.attlist) + +class OL(UL): + + attlist = {'start': 1} + attlist.update(UL.attlist) + +class LI(UL): + + attlist = {'value': 1, 'type': 1} + attlist.update(UL.attlist) + +class LINK(PCElement): + + attlist = {'charset': 1, 'media': 1} + attlist.update(PCElement.attlist) + attlist.update(links_and_anchors) + content_model = None + +class META(PrettyTagsMixIn, Element): + + attlist = {'http_equiv': 1, 'content': 1, 'scheme': 1} + attlist.update(i18n) + content_model = None + +class OBJECT(PCElement): + + attlist = {'declare': 0, 'classid': 1, 'codebase': 1, 'data': 1, + 'type': 1, 'codetype': 1, 'archive': 1, 'standby': 1, + 'height': 1, 'width': 1, 'usemap': 1} + attlist.update(PCElement.attlist) + attlist.update(tabbing_navigation) + +class SELECT(PCElement): + + attlist = {'size': 1, 'multiple': 0, 'disabled': 0} + attlist.update(CommonElement.attlist) + attlist.update(tabbing_navigation) + +class OPTGROUP(PCElement): + + attlist = {'disabled': 0, 'label': 1} + attlist.update(CommonElement.attlist) + +class OPTION(OPTGROUP): + + attlist = {'value': 1, 'selected': 0} + attlist.update(OPTGROUP.attlist) + +class PARAM(Element): + + attlist = {'id': 1, 'value': 1, 'valuetype': 1, 'type': 1} + +class SCRIPT(Element): + + attlist = {'charset': 1, 'type': 1, 'src': 1, 'defer': 0} + +class SPAN(CommonElement): + + attlist = {'align': 1} + attlist.update(CommonElement.attlist) + +class DIV(PrettyTagsMixIn, SPAN): pass + +class STYLE(PrettyTagsMixIn, Element): + + attlist = {'type': 1, 'media': 1, 'title': 1} + attlist.update(i18n) + +class TABLE(PCElement): + + attlist = {'cellspacing': 1, 'cellpadding': 1, 'summary': 1, 'align': 1, + 'bgcolor': 1, 'width': 1} + attlist.update(CommonElement.attlist) + attlist.update(borders_and_rules) + +class TBODY(PCElement): + + attlist = CommonElement.attlist.copy() + attlist.update(cellhalign) + attlist.update(cellvalign) + +class THEAD(TBODY): pass +class TFOOT(TBODY): pass +class TR(TBODY): pass + +class TH(TBODY): + + attlist = {'abbv': 1, 'axis': 1, 'headers': 1, 'scope': 1, + 'rowspan': 1, 'colspan': 1, 'nowrap': 0, 'width': 1, + 'height': 1} + attlist.update(TBODY.attlist) + +class TD(TH): pass + +class TEXTAREA(CommonElement): + + attlist = {'rows': 1, 'cols': 1, 'disabled': 0, 'readonly': 0} + attlist.update(CommonElement.attlist) + attlist.update(tabbing_and_access) + +def CENTER(*content, **attr): + c = apply(DIV, content, attr) + c['align'] = 'center' + return c + +def H1(content=[], **attr): return apply(Heading, (1, content), attr) +def H2(content=[], **attr): return apply(Heading, (2, content), attr) +def H3(content=[], **attr): return apply(Heading, (3, content), attr) +def H4(content=[], **attr): return apply(Heading, (4, content), attr) +def H5(content=[], **attr): return apply(Heading, (5, content), attr) +def H6(content=[], **attr): return apply(Heading, (6, content), attr) + +class CSSRule(PrettyTagsMixIn, Element): + + attlist = {'font': 1, 'font_family': 1, 'font_face': 1, 'font_size': 1, + 'border': 1, 'border_width': 1, 'color': 1, + 'background': 1, 'background_color': 1, 'background_image': 1, + 'text_align': 1, 'text_decoration': 1, 'text_indent': 1, + 'line_height': 1, 'margin_left': 1, 'margin_right': 1, + 'clear': 1, 'list_style_type': 1} + content = [] + content_model = None + + def __init__(self, selector, **decl): + self.dict = {} + self.update(decl) + self.name = selector + + start_tag_string = "%s { %s }" + + def end_tag(self): return '' + + def str_attribute(self, k): + kt = replace(k, '_', '-') + if self.attlist[k]: return '%s: %s' % (kt, str(self[k])) + else: return self[k] and kt or '' + + def str_attribute_list(self): + return join(map(self.str_attribute, self.dict.keys()), '; ') + +nbsp = " " + +def quote_body(s): + r=replace; return r(r(r(s, '&', '&'), '<', '<'), '>', '>') + +safe = string.letters + string.digits + '_,.-' + +def url_encode(s): + l = [] + for c in s: + if c in safe: l.append(c) + elif c == ' ': l.append('+') + else: l.append("%%%02x" % ord(c)) + return join(l, '') + +def URL(*args, **kwargs): + url_path = join(args, '/') + a = [] + for k, v in kwargs.items(): + a.append("%s=%s" % (url_encode(k), url_encode(v))) + url_vals = join(a, '&') + return url_vals and join([url_path, url_vals],'?') or url_path + +def Options(options, selected=[], **attrs): + opts = [] + for o, v in options: + opt = apply(OPTION, (o,), attrs) + opt['value'] = v + if v in selected: opt['selected'] = 1 + opts.append(opt) + return opts + +def Select(options, selected=[], **attrs): + return apply(SELECT, tuple(apply(Options, (options, selected))), attrs) + +def Href(url, text, **attrs): + h = apply(A, (text,), attrs) + h['href'] = url + return h + +def Mailto(address, text, subject='', **attrs): + if subject: + url = "mailto:%s?subject=%s" % (address, subject) + else: + url = "mailto:%s" % address + return apply(Href, (url, text), attrs) + +def Image(src, **attrs): + i = apply(IMG, (), a) + i['src'] = src + return i + +def StyledTR(element, row, klasses): + r = TR() + for i in range(len(row)): + r.append(klasses[i] and element(row[i], klass=klasses[i]) \ + or element(row[i])) + return r + +def StyledVTable(klasses, *rows, **attrs): + t = apply(TABLE, (), attrs) + t.append(COL(span=len(klasses))) + for row in rows: + r = StyledTR(TD, row[1:], klasses[1:]) + h = klasses[0] and TH(row[0], klass=klasses[0]) \ + or TH(row[0]) + r.content.insert(0,h) + t.append(r) + return t + +def VTable(*rows, **attrs): + t = apply(TABLE, (), attrs) + t.append(COL(span=len(rows[0]))) + for row in rows: + r = apply(TR, tuple(map(TD, row[1:]))) + r.content.insert(0, TH(row[0])) + t.append(r) + return t + +def StyledHTable(klasses, headers, *rows, **attrs): + t = apply(TABLE, (), attrs) + t.append(COL(span=len(headers))) + t.append(StyledTR(TH, headers, klasses)) + for row in rows: t.append(StyledTR(TD, row, klasses)) + return t + +def HTable(headers, *rows, **attrs): + t = apply(TABLE, (), attrs) + t.append(COL(span=len(headers))) + t.append(TR, tuple(map(TH, headers))) + for row in rows: t.append(TR(apply(TD, row))) + return t + +def DefinitionList(*items, **attrs): + dl = apply(DL, (), attrs) + for dt, dd in items: dl.append(DT(dt), DD(dd)) + return dl + + diff --git a/www/HyperText/XML.py b/www/HyperText/XML.py new file mode 100644 index 0000000..5177bc6 --- /dev/null +++ b/www/HyperText/XML.py @@ -0,0 +1,42 @@ +__version__ = "$Revision: 1.2 $"[11:-4] + +from SGML import Markup, Comment +from string import lower, join, replace + +class XMLPI(Markup): + + attlist = {} + defaults = {'version': '1.0'} + attr_translations = {'id': 'ID', + 'klass': 'class', + 'label_for': 'for', + 'http_equiv': 'http-equiv', + 'accept_charset': 'accept-charset'} + + def __init__(self, **attr): + self.dict = {} + self.content = () + self.name = 'xml' + self.dict.update(self.defaults) + self.dict.update(attr) + + def __getitem__(self, k): return self.dict[k] + + def str_attribute(self, k): + k2 = self.attr_translations.get(k, k) + return '%s="%s"' % \ + (k2, self.attlist.get(k, 1) and str(self[k]) or k2) \ + + def str_attribute_list(self): + return join(map(self.str_attribute, self.dict.keys())) + + start_tag_string = "\n" + + def start_tag(self): + a = self.str_attribute_list() + return self.start_tag_string % (self.name, a) + + def end_tag(self): + return self.end_tag_string + diff --git a/www/HyperText/__init__.py b/www/HyperText/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/www/HyperText/license.py b/www/HyperText/license.py new file mode 100644 index 0000000..4b7d6f7 --- /dev/null +++ b/www/HyperText/license.py @@ -0,0 +1,22 @@ +""" +Copyright 1999 by Comstar.net, Inc., Atlanta, GA, US. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Comstar.net, Inc. +or COMSTAR not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior permission. + +COMSTAR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO +EVENT SHALL COMSTAR BE LIABLE FOR ANY SPECIAL, INDIRECT OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF +USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. +""" +if __name__=="__main__": print __doc__