File: __init__.py

package info (click to toggle)
flask-htmlmin 3.0.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 208 kB
  • sloc: python: 188; sh: 14; makefile: 6
file content (132 lines) | stat: -rw-r--r-- 4,177 bytes parent folder | download
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
from functools import wraps
from htmlmin import Minifier
from flask import request, current_app
import warnings
import cssmin
import re


class HTMLMIN(object):
    def __init__(self, app=None, **kwargs):
        self.app = app
        if app is not None:
            self.init_app(app)

        default_options = {
            'remove_comments': True,
            'reduce_empty_attributes': True,
            'remove_optional_attribute_quotes': False
        }

        self.disable_css_min = kwargs.pop('disable_css_min', False)
        default_options.update(kwargs)
        self.opts = default_options

        self._exempt_routes = set()
        self._html_minify = Minifier(
            **default_options)

    def init_app(self, app):
        app.config.setdefault('MINIFY_HTML', False)

        if 'MINIFY_PAGE' in app.config:
            app.config['MINIFY_HTML'] = app.config['MINIFY_PAGE']
            warnings.warn(
                'MINIFY_PAGE is deprecated, use MINIFY_HTML instead',
                DeprecationWarning,
                stacklevel=2
            )

        if app.config['MINIFY_HTML']:
            app.after_request(self.response_minify)

    def response_minify(self, response):
        """
        minify response html to decrease traffic
        """

        if response.content_type == u'text/html; charset=utf-8':
            endpoint = request.endpoint or ''
            view_func = current_app.view_functions.get(endpoint, None)
            name = (
                '%s.%s' % (view_func.__module__, view_func.__name__)
                if view_func else ''
            )
            if name in self._exempt_routes:
                return response

            response.direct_passthrough = False
            if self.disable_css_min:
                response.set_data(
                    self._html_minify.minify(response.get_data(as_text=True))
                )
            else:
                response.set_data(
                    self._css_minify(
                      self._html_minify.minify(
                           response.get_data(as_text=True)
                       )
                     )
                )

            return response
        return response

    def _css_minify(self, response):
        """
            Minify inline css
        """

        # Minify internal css
        out = ''
        text = response
        opening_tags = re.findall(r"<style\s*[^>]*>", text, re.M | re.I)
        for tag in opening_tags:
            i = text.find(tag)+len(tag)-1
            e = text.find("</style>")+9
            css = text[i:e]
            out += text[0:i] + self._min_css(css)
            text = text[e:]
        out = out+text

        # Minify inline css
        out2 = ''
        inlines = re.findall(r"<[A-Za-z0-9-]+[^>]+?style=\"[\s\S]+?\"",
                             out,  re.M | re.I)
        for inline in inlines:
            i = out.find(inline)
            j = out[i:].find("style=")+7
            k = out[i+j:].find('"')
            css = out[i+j:i+j+k+1]
            out2 += out[0:i+j] + re.sub(r";+\s*(?=(\"|\'))", "",
                                        self._min_css(css), re.I | re.M)
            out = out[i+j+k+1:]
        out2 += out
        return out2

    def _min_css(self, css):
        if self.opts.get("remove_comments"):
            css = cssmin.remove_comments(css)
        css = cssmin.condense_whitespace(css)
        css = css.replace('"\\"}\\""', "___PSEUDOCLASSBMH___")
        css = cssmin.remove_unnecessary_whitespace(css)
        css = cssmin.remove_unnecessary_semicolons(css)
        css = cssmin.condense_zero_units(css)
        css = cssmin.condense_multidimensional_zeros(css)
        css = cssmin.condense_floating_points(css)
        css = cssmin.normalize_rgb_colors_to_hex(css)
        css = cssmin.condense_hex_colors(css)
        return css

    def exempt(self, obj):
        """
        decorator to mark a view as exempt from htmlmin.
        """
        name = '%s.%s' % (obj.__module__, obj.__name__)

        @wraps(obj)
        def __inner(*a, **k):
            return obj(*a, **k)

        self._exempt_routes.add(name)
        return __inner