1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
|
import cgi
import sys
class HtmlGenerator(object):
def __init__(self):
self._stack = []
self._fragments = []
def text(self, text):
if text:
self._write_all()
self._fragments.append(_escape_html(text))
def start(self, name, attributes=None, always_write=False):
self._stack.append(_Element(name, attributes))
if always_write:
self._write_all()
def end(self):
element = self._stack.pop()
if element.written:
self._fragments.append("</{0}>".format(element.name))
def end_all(self):
while self._stack:
self.end()
def self_closing(self, name, attributes=None):
attribute_string = _generate_attribute_string(attributes)
self._fragments.append("<{0}{1} />".format(name, attribute_string))
def _write_all(self):
for element in self._stack:
if not element.written:
element.written = True
self._write_element(element)
def _write_element(self, element):
attribute_string = _generate_attribute_string(element.attributes)
self._fragments.append("<{0}{1}>".format(element.name, attribute_string))
def append(self, other):
if other._fragments:
self._write_all()
for fragment in other._fragments:
self._fragments.append(fragment)
def html_string(self):
return u("").join(self._fragments)
class _Element(object):
def __init__(self, name, attributes):
if attributes is None:
attributes = {}
self.name = name
self.attributes = attributes
self.written = False
def _escape_html(text):
return cgi.escape(text, quote=True)
def _generate_attribute_string(attributes):
if attributes is None:
return ""
else:
return "".join(
' {0}="{1}"'.format(key, _escape_html(value))
for key, value in _iteritems(attributes)
)
def satisfy_html_path(generator, path):
first_unsatisfied_index = _find_first_unsatisfied_index(generator, path)
while len(generator._stack) > first_unsatisfied_index:
generator.end()
for element in path.elements[first_unsatisfied_index:]:
attributes = {}
if element.class_names:
attributes["class"] = _generate_class_attribute(element)
generator.start(element.names[0], attributes=attributes)
def _find_first_unsatisfied_index(generator, path):
for index, (generated_element, path_element) in enumerate(zip(generator._stack, path.elements)):
if not _is_element_match(generated_element, path_element):
return index
return len(generator._stack)
def _is_element_match(generated_element, path_element):
return (
not path_element.fresh and
generated_element.name in path_element.names and
generated_element.attributes.get("class", "") == _generate_class_attribute(path_element)
)
def _generate_class_attribute(path_element):
return " ".join(path_element.class_names)
if sys.version_info[0] == 2:
_iteritems = lambda x: x.iteritems()
u = lambda value: unicode(value, "unicode_escape")
else:
_iteritems = lambda x: x.items()
u = lambda value: value
|