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