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