diff --git a/htmlmin/main.py b/htmlmin/main.py index 43a9bab..fa3923c 100644 --- a/htmlmin/main.py +++ b/htmlmin/main.py @@ -37,6 +37,7 @@ def minify(input, reduce_empty_attributes=True, reduce_boolean_attributes=False, remove_optional_attribute_quotes=True, + escape_tags=True, keep_pre=False, pre_tags=parser.PRE_TAGS, pre_attr='pre'): @@ -93,6 +94,7 @@ def minify(input, reduce_empty_attributes=reduce_empty_attributes, reduce_boolean_attributes=reduce_boolean_attributes, remove_optional_attribute_quotes=remove_optional_attribute_quotes, + escape_tags=escape_tags, keep_pre=keep_pre, pre_tags=pre_tags, pre_attr=pre_attr) @@ -118,6 +120,7 @@ def __init__(self, reduce_empty_attributes=True, reduce_boolean_attributes=False, remove_optional_attribute_quotes=True, + escape_tags=True, keep_pre=False, pre_tags=parser.PRE_TAGS, pre_attr='pre'): @@ -132,6 +135,7 @@ def __init__(self, reduce_empty_attributes=reduce_empty_attributes, reduce_boolean_attributes=reduce_boolean_attributes, remove_optional_attribute_quotes=remove_optional_attribute_quotes, + escape_tags=escape_tags, keep_pre=keep_pre, pre_tags=pre_tags, pre_attr=pre_attr) diff --git a/htmlmin/parser.py b/htmlmin/parser.py index 5ff2211..9114301 100644 --- a/htmlmin/parser.py +++ b/htmlmin/parser.py @@ -80,6 +80,9 @@ # Tag omission rules: # http://www.w3.org/TR/html51/syntax.html#optional-tags +def escape_noop(v, **kwargs): + return v + class HTMLMinError(Exception): pass class ParseError(HTMLMinError): pass class OpenTagNotFoundError(ParseError): pass @@ -92,6 +95,7 @@ def __init__(self, reduce_empty_attributes=True, reduce_boolean_attributes=False, remove_optional_attribute_quotes=True, + escape_tags=True, keep_pre=False, pre_tags=PRE_TAGS, pre_attr='pre'): @@ -113,6 +117,10 @@ def __init__(self, self._tag_stack = [] self._title_newly_opened = False self.__title_trailing_whitespace = False + if escape_tags: + self.escape = escape + else: + self.escape = escape_noop def _has_pre(self, attrs): for k,v in attrs: @@ -123,11 +131,11 @@ def _has_pre(self, attrs): def build_tag(self, tag, attrs, close_tag): result = StringIO() result.write('<') - result.write(escape(tag)) + result.write(self.escape(tag)) needs_closing_space = False for k,v in attrs: result.write(' ') - result.write(escape(k)) + result.write(self.escape(k)) if v: if self.reduce_boolean_attributes and ( k in BOOLEAN_ATTRIBUTES.get(tag,[]) or @@ -135,12 +143,14 @@ def build_tag(self, tag, attrs, close_tag): pass elif self.remove_optional_attribute_quotes and not any((c in v for c in ('"', "'", ' ', '<', '>', '='))): result.write('=') - result.write(escape(v, quote=True)) + result.write(self.escape(v, quote=True)) needs_closing_space = v.endswith('/') else: - result.write('="') - result.write(escape(v, quote=True).replace(''', "'")) - result.write('"') + attr_val = self.escape(v, quote=True).replace("'", ''') + if '"' in attr_val: + result.write("='{}'".format(attr_val)) + else: + result.write('="{}"'.format(attr_val)) elif not self.reduce_empty_attributes: result.write('=""') if needs_closing_space: @@ -256,7 +266,7 @@ def handle_endtag(self, tag): # results in a '

' in Chrome. pass if tag not in NO_CLOSE_TAGS: - self._data_buffer.extend(['']) + self._data_buffer.extend(['']) def handle_startendtag(self, tag, attrs): self._after_doctype = False diff --git a/htmlmin/tests/tests.py b/htmlmin/tests/tests.py index 431061f..4d769c0 100644 --- a/htmlmin/tests/tests.py +++ b/htmlmin/tests/tests.py @@ -69,6 +69,14 @@ } FEATURES_TEXTS = { + 'escape_tags_true': ( + ' hello ', + ' hello ', + ), + 'escape_tags_false': ( + ' hello ', + ' hello ', + ), 'remove_quotes': ( '
', '
', @@ -342,6 +350,14 @@ def test_buffered_input(self): class TestMinifyFeatures(HTMLMinTestCase): __reference_texts__ = FEATURES_TEXTS + def test_escape_tags_true(self): + text = self.__reference_texts__['escape_tags_true'] + self.assertEqual(htmlmin.minify(text[0], escape_tags=True), text[1]) + + def test_escape_tags_false(self): + text = self.__reference_texts__['escape_tags_false'] + self.assertEqual(htmlmin.minify(text[0], escape_tags=False), text[1]) + def test_remove_comments(self): text = self.__reference_texts__['remove_comments'] self.assertEqual(htmlmin.minify(text[0], remove_comments=True), text[1])