Passed
Push — master ( 0c68df...e3ee72 )
by Dongxin
01:27
created

HtmlRenderer.list()   B

Complexity

Conditions 5

Size

Total Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
c 1
b 0
f 0
dl 0
loc 15
rs 8.5454
1
from __future__ import unicode_literals
2
3
4
import re
5
from builtins import str
6
from CommonMark.common import escape_xml
7
from CommonMark.render.renderer import Renderer
8
9
10
reUnsafeProtocol = re.compile(
11
    r'^javascript:|vbscript:|file:|data:', re.IGNORECASE)
12
reSafeDataProtocol = re.compile(
13
    r'^data:image\/(?:png|gif|jpeg|webp)', re.IGNORECASE)
14
15
16
def potentially_unsafe(url):
17
    return re.match(reUnsafeProtocol, url) and \
18
        (not re.match(reSafeDataProtocol, url))
19
20
21
class HtmlRenderer(Renderer):
22
    def __init__(self, options={}):
23
        #  by default, soft breaks are rendered as newlines in HTML
24
        options['softbreak'] = options.get('softbreak') or '\n'
25
        # set to "<br />" to make them hard breaks
26
        # set to " " if you want to ignore line wrapping in source
27
28
        self.disable_tags = 0
29
        self.last_out = '\n'
30
        self.options = options
31
32
    def escape(self, text, preserve_entities):
33
        return escape_xml(text, preserve_entities)
34
35
    def tag(self, name, attrs=None, selfclosing=None):
36
        """Helper function to produce an HTML tag."""
37
        if self.disable_tags > 0:
38
            return
39
40
        self.buf += '<' + name
41
        if attrs and len(attrs) > 0:
42
            for attrib in attrs:
43
                self.buf += ' ' + attrib[0] + '="' + attrib[1] + '"'
44
45
        if selfclosing:
46
            self.buf += ' /'
47
48
        self.buf += '>'
49
        self.last_out = '>'
50
51
    # Node methods #
52
53
    def text(self, node, entering=None):
54
        self.out(node.literal)
55
56
    def softbreak(self, node=None, entering=None):
57
        self.lit(self.options['softbreak'])
58
59
    def linebreak(self, node=None, entering=None):
60
        self.tag('br', [], True)
61
        self.cr()
62
63
    def link(self, node, entering):
64
        attrs = self.attrs(node)
65
        if entering:
66
            if not (self.options.get('safe') and
67
                    potentially_unsafe(node.destination)):
68
                attrs.append(['href', self.escape(node.destination, True)])
69
70
            if node.title:
71
                attrs.append(['title', self.escape(node.title, True)])
72
73
            self.tag('a', attrs)
74
        else:
75
            self.tag('/a')
76
77
    def image(self, node, entering):
78
        if entering:
79
            if self.disable_tags == 0:
80
                if self.options.get('safe') and \
81
                   potentially_unsafe(node.destination):
82
                    self.lit('<img src="" alt="')
83
                else:
84
                    self.lit('<img src="' +
85
                             self.escape(node.destination, True) +
86
                             '" alt="')
87
            self.disable_tags += 1
88
        else:
89
            self.disable_tags -= 1
90
            if self.disable_tags == 0:
91
                if node.title:
92
                    self.lit('" title="' + self.escape(node.title, True))
93
                self.lit('" />')
94
95
    def emph(self, node, entering):
96
        self.tag('em' if entering else '/em')
97
98
    def strong(self, node, entering):
99
        self.tag('strong' if entering else '/strong')
100
101
    def paragraph(self, node, entering):
102
        grandparent = node.parent.parent
103
        attrs = self.attrs(node)
104
        if grandparent is not None and grandparent.t == 'list':
105
            if grandparent.list_data['tight']:
106
                return
107
108
        if entering:
109
            self.cr()
110
            self.tag('p', attrs)
111
        else:
112
            self.tag('/p')
113
            self.cr()
114
115
    def heading(self, node, entering):
116
        tagname = 'h' + str(node.level)
117
        attrs = self.attrs(node)
118
        if entering:
119
            self.cr()
120
            self.tag(tagname, attrs)
121
        else:
122
            self.tag('/' + tagname)
123
            self.cr()
124
125
    def code(self, node, entering):
126
        self.tag('code')
127
        self.out(node.literal)
128
        self.tag('/code')
129
130
    def code_block(self, node, entering):
131
        info_words = node.info.split() if node.info else []
132
        attrs = self.attrs(node)
133
        if len(info_words) > 0 and len(info_words[0]) > 0:
134
            attrs.append(['class', 'language-' +
135
                          self.escape(info_words[0], True)])
136
137
        self.cr()
138
        self.tag('pre')
139
        self.tag('code', attrs)
140
        self.out(node.literal)
141
        self.tag('/code')
142
        self.tag('/pre')
143
        self.cr()
144
145
    def thematic_break(self, node, entering):
146
        attrs = self.attrs(node)
147
        self.cr()
148
        self.tag('hr', attrs, True)
149
        self.cr()
150
151
    def block_quote(self, node, entering):
152
        attrs = self.attrs(node)
153
        if entering:
154
            self.cr()
155
            self.tag('blockquote', attrs)
156
            self.cr()
157
        else:
158
            self.cr()
159
            self.tag('/blockquote')
160
            self.cr()
161
162
    def list(self, node, entering):
163
        tagname = 'ul' if node.list_data['type'] == 'bullet' else 'ol'
164
        attrs = self.attrs(node)
165
        if entering:
166
            start = node.list_data['start']
167
            if start is not None and start != 1:
168
                attrs.append(['start', str(start)])
169
170
            self.cr()
171
            self.tag(tagname, attrs)
172
            self.cr()
173
        else:
174
            self.cr()
175
            self.tag('/' + tagname)
176
            self.cr()
177
178
    def item(self, node, entering):
179
        attrs = self.attrs(node)
180
        if entering:
181
            self.tag('li', attrs)
182
        else:
183
            self.tag('/li')
184
            self.cr()
185
186
    def html_inline(self, node, entering):
187
        if self.options.get('safe'):
188
            self.lit('<!-- raw HTML omitted -->')
189
        else:
190
            self.lit(node.literal)
191
192
    def html_block(self, node, entering):
193
        self.cr()
194
        if self.options.get('safe'):
195
            self.lit('<!-- raw HTML omitted -->')
196
        else:
197
            self.lit(node.literal)
198
        self.cr()
199
200
    def custom_inline(self, node, entering):
201
        if entering and node.on_enter:
202
            self.lit(node.on_enter)
203
        elif (not entering) and node.on_exit:
204
            self.lit(node.on_exit)
205
206
    def custom_block(self, node, entering):
207
        self.cr()
208
        if entering and node.on_enter:
209
            self.lit(node.on_enter)
210
        elif (not entering) and node.on_exit:
211
            self.lit(node.on_exit)
212
        self.cr()
213
214
    # Helper methods #
215
216
    def out(self, s):
217
        self.lit(self.escape(s, False))
218
219
    def attrs(self, node):
220
        att = []
221
        if self.options.get('sourcepos'):
222
            pos = node.sourcepos
223
            if pos:
224
                att.append(['data-sourcepos', str(pos[0][0]) + ':' +
225
                            str(pos[0][1]) + '-' + str(pos[1][0]) + ':' +
226
                            str(pos[1][1])])
227
228
        return att
229