##// END OF EJS Templates
Show even number of spaces before and after spoiler
neko259 -
r1459:be8f90ac default
parent child Browse files
Show More
@@ -1,236 +1,236 b''
1 1 # coding=utf-8
2 2
3 3 import re
4 4 import random
5 5 import bbcode
6 6
7 7 from urllib.parse import unquote
8 8
9 9 from django.core.exceptions import ObjectDoesNotExist
10 10 from django.core.urlresolvers import reverse
11 11
12 12 import boards
13 13
14 14
15 15 __author__ = 'neko259'
16 16
17 17
18 18 REFLINK_PATTERN = re.compile(r'^\d+$')
19 19 MULTI_NEWLINES_PATTERN = re.compile(r'(\r?\n){2,}')
20 20 ONE_NEWLINE = '\n'
21 21 REGEX_URL = re.compile(r'https?\://[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(/\S*)?')
22 22 LINE_BREAK_HTML = '<div class="br"></div>'
23 23
24 24
25 25 class TextFormatter():
26 26 """
27 27 An interface for formatter that can be used in the text format panel
28 28 """
29 29
30 30 def __init__(self):
31 31 pass
32 32
33 33 name = ''
34 34
35 35 # Left and right tags for the button preview
36 36 preview_left = ''
37 37 preview_right = ''
38 38
39 39 # Left and right characters for the textarea input
40 40 format_left = ''
41 41 format_right = ''
42 42
43 43
44 44 class AutolinkPattern():
45 45 def handleMatch(self, m):
46 46 link_element = etree.Element('a')
47 47 href = m.group(2)
48 48 link_element.set('href', href)
49 49 link_element.text = href
50 50
51 51 return link_element
52 52
53 53
54 54 class QuotePattern(TextFormatter):
55 55 name = '>q'
56 56 preview_left = '<span class="quote">'
57 57 preview_right = '</span>'
58 58
59 59 format_left = '[quote]'
60 60 format_right = '[/quote]'
61 61
62 62
63 63 class SpoilerPattern(TextFormatter):
64 64 name = 'spoiler'
65 65 preview_left = '<span class="spoiler">'
66 66 preview_right = '</span>'
67 67
68 68 format_left = '[spoiler]'
69 69 format_right = '[/spoiler]'
70 70
71 71
72 72 class CommentPattern(TextFormatter):
73 73 name = ''
74 74 preview_left = '<span class="comment">// '
75 75 preview_right = '</span>'
76 76
77 77 format_left = '[comment]'
78 78 format_right = '[/comment]'
79 79
80 80
81 81 # TODO Use <s> tag here
82 82 class StrikeThroughPattern(TextFormatter):
83 83 name = 's'
84 84 preview_left = '<span class="strikethrough">'
85 85 preview_right = '</span>'
86 86
87 87 format_left = '[s]'
88 88 format_right = '[/s]'
89 89
90 90
91 91 class ItalicPattern(TextFormatter):
92 92 name = 'i'
93 93 preview_left = '<i>'
94 94 preview_right = '</i>'
95 95
96 96 format_left = '[i]'
97 97 format_right = '[/i]'
98 98
99 99
100 100 class BoldPattern(TextFormatter):
101 101 name = 'b'
102 102 preview_left = '<b>'
103 103 preview_right = '</b>'
104 104
105 105 format_left = '[b]'
106 106 format_right = '[/b]'
107 107
108 108
109 109 class CodePattern(TextFormatter):
110 110 name = 'code'
111 111 preview_left = '<code>'
112 112 preview_right = '</code>'
113 113
114 114 format_left = '[code]'
115 115 format_right = '[/code]'
116 116
117 117
118 118 def render_reflink(tag_name, value, options, parent, context):
119 119 result = '>>%s' % value
120 120
121 121 if REFLINK_PATTERN.match(value):
122 122 post_id = int(value)
123 123
124 124 try:
125 125 post = boards.models.Post.objects.get(id=post_id)
126 126
127 127 result = post.get_link_view()
128 128 except ObjectDoesNotExist:
129 129 pass
130 130
131 131 return result
132 132
133 133
134 134 def render_quote(tag_name, value, options, parent, context):
135 135 source = ''
136 136 if 'source' in options:
137 137 source = options['source']
138 138 elif 'quote' in options:
139 139 source = options['quote']
140 140
141 141 if source:
142 142 result = '<div class="multiquote"><div class="quote-header">%s</div><div class="quote-text">%s</div></div>' % (source, value)
143 143 else:
144 144 # Insert a ">" at the start of every line
145 145 result = '<span class="quote">&gt;{}</span>'.format(
146 146 value.replace(LINE_BREAK_HTML,
147 147 '{}&gt;'.format(LINE_BREAK_HTML)))
148 148
149 149 return result
150 150
151 151
152 152 def render_notification(tag_name, value, options, parent, content):
153 153 username = value.lower()
154 154
155 155 return '<a href="{}" class="user-cast">@{}</a>'.format(
156 156 reverse('notifications', kwargs={'username': username}), username)
157 157
158 158
159 159 def render_tag(tag_name, value, options, parent, context):
160 160 tag_name = value.lower()
161 161
162 162 try:
163 163 url = boards.models.Tag.objects.get(name=tag_name).get_view()
164 164 except ObjectDoesNotExist:
165 165 url = tag_name
166 166
167 167 return url
168 168
169 169
170 170 def render_spoiler(tag_name, value, options, parent, context):
171 171 text_len = len(value)
172 pre_spaces = '&nbsp;' * max(random.randint(0, text_len // 2), 2)
173 post_spaces = '&nbsp;' * max(random.randint(0, text_len // 2), 2)
174 return '<span class="spoiler">{}{}{}</span>'.format(pre_spaces, value,
175 post_spaces)
172 space_count = random.randint(0, text_len * 2)
173 side_spaces = '&nbsp;' * (space_count // 2)
174 return '<span class="spoiler">{}{}{}</span>'.format(side_spaces, value,
175 side_spaces)
176 176
177 177 return quote_element
178 178
179 179
180 180
181 181 formatters = [
182 182 QuotePattern,
183 183 SpoilerPattern,
184 184 ItalicPattern,
185 185 BoldPattern,
186 186 CommentPattern,
187 187 StrikeThroughPattern,
188 188 CodePattern,
189 189 ]
190 190
191 191
192 192 PREPARSE_PATTERNS = {
193 193 r'(?<!>)>>(\d+)': r'[post]\1[/post]', # Reflink ">>123"
194 194 r'^>([^>].+)': r'[quote]\1[/quote]', # Quote ">text"
195 195 r'^//(.+)': r'[comment]\1[/comment]', # Comment "//text"
196 196 r'\B@(\w+)': r'[user]\1[/user]', # User notification "@user"
197 197 }
198 198
199 199
200 200 class Parser:
201 201 def __init__(self):
202 202 # The newline hack is added because br's margin does not work in all
203 203 # browsers except firefox, when the div's does.
204 204 self.parser = bbcode.Parser(newline=LINE_BREAK_HTML)
205 205
206 206 self.parser.add_formatter('post', render_reflink, strip=True)
207 207 self.parser.add_formatter('quote', render_quote, strip=True)
208 208 self.parser.add_formatter('user', render_notification, strip=True)
209 209 self.parser.add_formatter('tag', render_tag, strip=True)
210 210 self.parser.add_formatter('spoiler', render_spoiler, strip=True)
211 211 self.parser.add_simple_formatter(
212 212 'comment', '<span class="comment">//%(value)s</span>')
213 213 self.parser.add_simple_formatter(
214 214 's', '<span class="strikethrough">%(value)s</span>')
215 215 # TODO Why not use built-in tag?
216 216 self.parser.add_simple_formatter('code',
217 217 '<pre><code>%(value)s</pre></code>',
218 218 render_embedded=False)
219 219
220 220 def preparse(self, text):
221 221 """
222 222 Performs manual parsing before the bbcode parser is used.
223 223 Preparsed text is saved as raw and the text before preparsing is lost.
224 224 """
225 225 new_text = MULTI_NEWLINES_PATTERN.sub(ONE_NEWLINE, text)
226 226
227 227 for key, value in PREPARSE_PATTERNS.items():
228 228 new_text = re.sub(key, value, new_text, flags=re.MULTILINE)
229 229
230 230 for link in REGEX_URL.findall(text):
231 231 new_text = new_text.replace(link, unquote(link))
232 232
233 233 return new_text
234 234
235 235 def parse(self, text):
236 236 return self.parser.format(text)
General Comments 0
You need to be logged in to leave comments. Login now