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