c2a262085308fa2ed986c52b239165bd7fa3e8d7
[bottlenecks.git] / vstf / vstf / common / pyhtml.py
1 #!/usr/bin/python
2 # -*- coding: utf8 -*-
3 # author: wly
4 # date: 2015-09-25
5 # see license for license details
6
7 from sys import stdout, modules
8
9 doc_type = '<!DOCTYPE HTML>\n'
10 default_title = "Html Page"
11 charset = '<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />\n'
12
13 html4_tags = {'a', 'abbr', 'acronym', 'address', 'area', 'b', 'base', 'bdo', 'big',
14               'blockquote', 'body', 'br', 'button', 'caption', 'cite', 'code', 'col',
15               'colgroup', 'dd', 'del', 'div', 'dfn', 'dl', 'dt', 'em', 'fieldset',
16               'form', 'frame', 'frameset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head',
17               'hr', 'html', 'i', 'iframe', 'img', 'input', 'ins', 'kbd',
18               'label', 'legend', 'li', 'link', 'map', 'menu', 'menuitem', 'meta',
19               'noframes', 'noscript', 'object', 'ol', 'optgroup', 'option', 'p',
20               'param', 'pre', 'q', 'samp', 'script', 'select', 'small', 'span', 'strong',
21               'style', 'sub', 'sup', 'table', 'tbody', 'td', 'textarea', 'tfoot', 'th',
22               'thead', 'title', 'tr', 'tt', 'ul', 'var'}
23 disused_tags = {'isindex', 'font', 'dir', 's', 'strike',
24                 'u', 'center', 'basefont', 'applet', 'xmp'}
25 html5_tags = {'article', 'aside', 'audio', 'bdi', 'canvas', 'command', 'datalist', 'details',
26               'dialog', 'embed', 'figcaption', 'figure', 'footer', 'header',
27               'keygen', 'mark', 'meter', 'nav', 'output', 'progress', 'rp', 'rt', 'ruby',
28               'section', 'source', 'summary', 'details', 'time', 'track', 'video', 'wbr'}
29
30 nl = '\n'
31 tags = html4_tags | disused_tags | html5_tags
32
33 __all__ = [x.title() for x in tags] + ['PyHtml']
34
35 self_close = {'input', 'img', 'link', 'br'}
36
37
38 class Tag(list):
39     tag_name = ''
40
41     def __init__(self, *args, **kwargs):
42         self.attributes = kwargs
43         if self.tag_name:
44             name = self.tag_name
45             self.is_seq = False
46         else:
47             name = 'sequence'
48             self.is_seq = True
49         self._id = kwargs.get('id', name)
50         for arg in args:
51             self.add_obj(arg)
52
53     def __iadd__(self, obj):
54         if isinstance(obj, Tag) and obj.is_seq:
55             for o in obj:
56                 self.add_obj(o)
57         else:
58             self.add_obj(obj)
59         return self
60
61     def add_obj(self, obj):
62         if not isinstance(obj, Tag):
63             obj = str(obj)
64         _id = self.set_id(obj)
65         setattr(self, _id, obj)
66         self.append(obj)
67
68     def set_id(self, obj):
69         if isinstance(obj, Tag):
70             _id = obj._id
71             obj_lst = filter(lambda t: isinstance(
72                 t, Tag) and t._id.startswith(_id), self)
73         else:
74             _id = 'content'
75             obj_lst = filter(lambda t: not isinstance(t, Tag), self)
76         length = len(obj_lst)
77         if obj_lst:
78             _id = '%s_%03i' % (_id, length)
79         if isinstance(obj, Tag):
80             obj._id = _id
81         return _id
82
83     def __add__(self, obj):
84         if self.tag_name:
85             return Tag(self, obj)
86         self.add_obj(obj)
87         return self
88
89     def __lshift__(self, obj):
90         if isinstance(obj, Tag):
91             self += obj
92             return obj
93         print "unknown obj: %s " % obj
94         return self
95
96     def render(self):
97         result = ''
98         if self.tag_name:
99             result += '<%s%s%s>' % (self.tag_name,
100                                     self._render_attr(), self._self_close() * ' /')
101         if not self._self_close():
102             isnl = True
103             for c in self:
104                 if isinstance(c, Tag):
105                     result += isnl * nl
106                     isnl = False
107                     result += c.render()
108                 else:
109                     result += c
110             if self.tag_name:
111                 result += '</%s>' % self.tag_name
112         result += nl
113         return result
114
115     def _render_attr(self):
116         result = ''
117         for key, value in self.attributes.iteritems():
118             if key != 'txt' and key != 'open':
119                 if key == 'cl':
120                     key = 'class'
121                 result += ' %s="%s"' % (key, value)
122         return result
123
124     def _self_close(self):
125         return self.tag_name in self_close
126
127 """
128 def tag_factory(tag):
129     class F(Tag):
130         tag_name = tag
131
132     F.__name__ = tag.title()
133     return F
134
135
136 THIS = modules[__name__]
137
138 for t in tags:
139     setattr(THIS, t.title(), tag_factory(t))
140 """
141 THIS = modules[__name__]
142 for t in tags:
143     obj = type(t.title(), (Tag, ), {'tag_name': t})
144     setattr(THIS, t.title(), obj)
145
146
147 def _render_style(style):
148     result = ''
149     for item in style:
150         result += item
151         result += '\n{\n'
152         values = style[item]
153         for key, value in values.iteritems():
154             result += "    %s: %s;\n" % (key, value)
155         result += '}\n'
156     if result:
157         result = '\n' + result
158     return result
159
160
161 class PyHtml(Tag):
162     tag_name = 'html'
163
164     def __init__(self, title=default_title):
165         self._id = 'html'
166         self += Head()
167         self += Body()
168         self.attributes = dict(xmlns='http://www.w3.org/1999/xhtml', lang='en')
169         self.head += Title(title)
170
171     def __iadd__(self, obj):
172         if isinstance(obj, Head) or isinstance(obj, Body):
173             self.add_obj(obj)
174         elif isinstance(obj, Meta) or isinstance(obj, Link):
175             self.head += obj
176         else:
177             self.body += obj
178             _id = self.set_id(obj)
179             setattr(self, _id, obj)
180         return self
181
182     def add_js(self, *arg):
183         for f in arg:
184             self.head += Script(type='text/javascript', src=f)
185
186     def add_css(self, *arg):
187         for f in arg:
188             self.head += Link(rel='stylesheet', type='text/css', href=f)
189
190     def output(self, name=''):
191         if name:
192             fil = open(name, 'w')
193         else:
194             fil = stdout
195         fil.write(self.as_string())
196         fil.flush()
197         if name:
198             fil.close()
199
200     def as_string(self):
201         return doc_type + self.render()
202
203     def add_style(self, style):
204         self.head += Style(_render_style(style))
205
206     def add_table(self, data):
207         table = self << Table()
208         rows = len(data)
209         cols = len(zip(*data))
210
211         for i in range(rows):
212             tr = table << Tr()
213             tr << Th(data[i][0])
214             for j in range(1, cols):
215                 tr << Td(data[i][j])