Completed
Push — 0.5.3 ( eba748...a7ccc1 )
by Felipe A.
01:14
created

SGMLCompressContext.feed()   C

Complexity

Conditions 7

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
c 1
b 0
f 0
dl 0
loc 22
rs 5.7894
1
import re
2
3
import jinja2
4
import jinja2.ext
5
import jinja2.lexer
6
7
from browsepy.transform import StateMachine
8
9
10
class SGMLCompressContext(StateMachine):
11
    re_whitespace = re.compile('[ \\t\\r\\n]+')
12
    token_class = jinja2.lexer.Token
13
    block_tokens = {
14
        'variable_begin': 'variable_end',
15
        'block_begin': 'block_end'
16
        }
17
    block_tags = {}  # block content will be treated as literal text
18
    jumps = {  # state machine jumps
19
        'text': {
20
            '<': 'tag',
21
            '<!--': 'comment',
22
            '<![CDATA[': 'cdata',
23
            },
24
        'lit1': {'"': 'tag'},
25
        'lit2': ("'", 'tag'),
26
        'tag': {
27
            '>': 'text',
28
            '"': 'lit1',
29
            "'": 'lit2'
30
            },
31
        'comment': {'-->': 'text'},
32
        'cdata': {']]>': 'text'}
33
        }
34
    lineno = 0  # current token lineno
35
    skip_until_token = None  # inside token until this is met
36
    skip_until_text = None  # inside text until this is met
37
    current = 'text'
38
39
    def look(self, value, current, start):
40
        offset = len(start)
41
        if self.skip_until_text and current == 'text':
42
            mark = self.skip_until_text
43
            index = value.find(mark, offset)
44
            if -1 != index:
45
                yield index, mark, current
46
        else:
47
            super_look = super(SGMLCompressContext, self).look
48
            for result in super_look(value, current, start):
49
                yield result
50
        yield len(value), '', None
51
52
    def finalize(self):
53
        for data in super(SGMLCompressContext, self).finalize():
54
            yield self.token_class(self.lineno, 'data', data)
55
56
    def transform_tag(self, data, current, start, partial=False):
57
        tagstart = start == '<'
58
        data = self.re_whitespace.sub(' ', data[1:] if tagstart else data)
59
        if tagstart:
60
            data = data.lstrip() if partial else data.strip()
61
            tagname = data.split(' ', 1)[0]
62
            self.skip_until_text = self.block_tags.get(tagname)
63
            return '<' + data
64
        elif partial:
65
            return data.rstrip()
66
        return start if data.strip() == start else data
67
68
    def transform_text(self, data, current, start, partial=False):
69
        if not self.skip_until_text:
70
            return start if data.strip() == start else data
71
        elif not partial:
72
            self.skip_until_text = None
73
        return data
74
75
    def feed(self, token):
76
        if self.skip_until_token:
77
            yield token
78
            if token.type == self.skip_until_token:
79
                self.skip_until_token = None
80
            return
81
82
        if token.type in self.block_tokens:
83
            for data in self.finalize():
84
                yield data
85
            yield token
86
            self.skip_until_token = self.block_tokens[token.type]
87
            return
88
89
        size = len(token.value)
90
        lineno = token.lineno
91
        self.pending += token.value
92
        pending_size = len(self.pending)
93
        for data in self:
94
            self.lineno = lineno if size > pending_size else self.lineno
95
            yield self.token_class(self.lineno, 'data', data)
96
            pending_size = len(self.pending)
97
98
99
class HTMLCompressContext(SGMLCompressContext):
100
    block_tags = {
101
        'textarea': '</textarea>',
102
        'pre': '</pre>',
103
        'script': '</script>',
104
        'style': '</style>',
105
        }
106
107
108
class HTMLCompress(jinja2.ext.Extension):
109
    context_class = HTMLCompressContext
110
111
    def filter_stream(self, stream):
112
        feed = self.context_class()
113
        for token in stream:
114
            for data in feed.feed(token):
115
                yield data
116
        for data in feed.finalize():
117
            yield data
118