12
|
1 local Luan = require "luan:Luan.luan"
|
|
2 local error = Luan.error
|
|
3 local type = Luan.type or error()
|
|
4 local ipairs = Luan.ipairs or error()
|
28
|
5 local pairs = Luan.pairs or error()
|
12
|
6 local stringify = Luan.stringify or error()
|
13
|
7 local Io = require "luan:Io.luan"
|
|
8 local output_of = Io.output_of or error()
|
12
|
9 local Parsers = require "luan:Parsers.luan"
|
|
10 local bbcode_parse = Parsers.bbcode_parse or error()
|
|
11 local Html = require "luan:Html.luan"
|
|
12 local html_encode = Html.encode or error()
|
21
|
13 local html_parse = Html.parse or error()
|
12
|
14 local Table = require "luan:Table.luan"
|
|
15 local is_list = Table.is_list or error()
|
21
|
16 local concat = Table.concat or error()
|
13
|
17 local String = require "luan:String.luan"
|
29
|
18 local regex = String.regex or error()
|
|
19 local ends_with = String.ends_with or error()
|
12
|
20 local User = require "site:/lib/User.luan"
|
13
|
21 local Shared = require "site:/lib/Shared.luan"
|
|
22 local list_to_set = Shared.list_to_set or error()
|
20
|
23 local to_list = Shared.to_list or error()
|
12
|
24 local Logging = require "luan:logging/Logging.luan"
|
|
25 local logger = Logging.logger "Bbcode"
|
|
26
|
|
27
|
|
28 local Bbcode = {}
|
|
29
|
29
|
30 local starting_cr_regex = regex[[^\n]]
|
|
31
|
12
|
32 local to_html
|
|
33 local html = {}
|
|
34
|
20
|
35 function html.b(bbcode,options)
|
|
36 %><b><% to_html(bbcode.contents,options) %></b><%
|
|
37 end
|
|
38
|
|
39 function html.i(bbcode,options)
|
|
40 %><i><% to_html(bbcode.contents,options) %></i><%
|
|
41 end
|
|
42
|
|
43 function html.u(bbcode,options)
|
|
44 %><u><% to_html(bbcode.contents,options) %></u><%
|
12
|
45 end
|
|
46
|
20
|
47 function html.s(bbcode,options)
|
|
48 %><s><% to_html(bbcode.contents,options) %></s><%
|
12
|
49 end
|
|
50
|
20
|
51 function html.sup(bbcode,options)
|
|
52 %><sup><% to_html(bbcode.contents,options) %></sup><%
|
12
|
53 end
|
|
54
|
45
|
55 function html.sub(bbcode,options)
|
|
56 %><sub><% to_html(bbcode.contents,options) %></sub><%
|
|
57 end
|
|
58
|
20
|
59 function html.brackets(bbcode,options)
|
|
60 %>[<% to_html(bbcode.contents,options) %>]<%
|
|
61 end
|
|
62
|
|
63 function html.url(bbcode,options)
|
12
|
64 local url = bbcode.param
|
|
65 if url == nil then
|
|
66 url = html_encode(bbcode.contents)
|
|
67 %><a href="<%=url%>"><%=url%></a><%
|
|
68 else
|
|
69 url = html_encode(url)
|
20
|
70 %><a href="<%=url%>"><% to_html(bbcode.contents,options) %></a><%
|
12
|
71 end
|
|
72 end
|
|
73
|
20
|
74 function html.code(bbcode,options)
|
49
|
75 if bbcode.param == "inline" then
|
|
76 %><code inline><%= html_encode(bbcode.contents) %></code><%
|
|
77 else
|
|
78 local s = starting_cr_regex.gsub(bbcode.contents,"")
|
|
79 %><code><%= html_encode(s) %></code><%
|
|
80 options.strip_newline = true
|
|
81 end
|
12
|
82 end
|
|
83
|
20
|
84 function html.img(bbcode,options)
|
12
|
85 %><img src="<%= html_encode(bbcode.contents) %>"><%
|
|
86 end
|
|
87
|
20
|
88 function html.color(bbcode,options)
|
|
89 %><span style="color:<%=bbcode.param%>"><% to_html(bbcode.contents,options) %></span><%
|
12
|
90 end
|
|
91
|
20
|
92 function html.size(bbcode,options)
|
23
|
93 %><span style="font-size:<%=bbcode.param%>"><% to_html(bbcode.contents,options) %></span><%
|
12
|
94 end
|
|
95
|
20
|
96 function html.quote(bbcode,options)
|
12
|
97 %><blockquote><%
|
|
98 local user_name = bbcode.param
|
|
99 if user_name ~= nil then
|
|
100 local user = User.get_by_name(user_name)
|
|
101 if user == nil then
|
|
102 %><%= user_name %> wrote:<%
|
|
103 else
|
|
104 %><a href="/user_something"><%= user_name %></a> wrote:<%
|
|
105 end
|
20
|
106 else
|
|
107 options.strip_newline = true
|
12
|
108 end
|
20
|
109 to_html(bbcode.contents,options)
|
12
|
110 %></blockquote><%
|
20
|
111 options.strip_newline = true
|
12
|
112 end
|
|
113
|
28
|
114 local function video_iframe(url)
|
|
115 %><iframe width="560" height="315" frameborder="0" allowfullscreen src="<%=url%>"></iframe><%
|
|
116 end
|
|
117
|
|
118 local video_handlers = {}
|
|
119 do
|
30
|
120 local ptn1 = regex[[^\Qhttps://youtu.be/\E([a-zA-Z0-9_-]+)(?:\?t=([0-9]+))?]]
|
|
121 local ptn2 = regex[[^\Qhttps://www.youtube.com/watch?v=\E([a-zA-Z0-9_-]+)(?:&t=([0-9]+)s)?]]
|
28
|
122 function video_handlers.youtube(url)
|
29
|
123 local id, start = ptn1.match(url)
|
28
|
124 if id == nil then
|
29
|
125 id, start = ptn2.match(url)
|
28
|
126 end
|
|
127 if id == nil then
|
|
128 return false
|
|
129 end
|
|
130 url = "https://www.youtube.com/embed/"..id
|
|
131 if start ~= nil then
|
|
132 url = url.."?start="..start
|
|
133 end
|
|
134 video_iframe(url)
|
|
135 return true
|
|
136 end
|
|
137 end
|
|
138 do
|
30
|
139 local ptn = regex[[^\Qhttps://rumble.com/embed/\E[a-z0-9]+/\?pub=[a-z0-9]+]]
|
28
|
140 function video_handlers.rumble(url)
|
29
|
141 if not ptn.matches(url) then
|
28
|
142 return false
|
|
143 end
|
|
144 video_iframe(url)
|
|
145 return true
|
|
146 end
|
|
147 end
|
|
148 do
|
30
|
149 local ptn = regex[[^\Qhttps://www.bitchute.com/video/\E([a-zA-Z0-9]+)/]]
|
28
|
150 function video_handlers.bitchute(url)
|
29
|
151 local id = ptn.match(url)
|
28
|
152 if id == nil then
|
|
153 return false
|
|
154 end
|
|
155 url = "https://www.bitchute.com/embed/"..id.."/"
|
|
156 video_iframe(url)
|
|
157 return true
|
|
158 end
|
|
159 end
|
|
160 do
|
30
|
161 local ptn = regex[[^\Qhttps://vimeo.com/\E([0-9]+)]]
|
28
|
162 function video_handlers.vimeo(url)
|
29
|
163 local id = ptn.match(url)
|
28
|
164 if id == nil then
|
|
165 return false
|
|
166 end
|
|
167 url = "https://player.vimeo.com/video/"..id
|
|
168 video_iframe(url)
|
|
169 return true
|
|
170 end
|
|
171 end
|
|
172 do
|
30
|
173 local ptn = regex[[^\Qhttps://dai.ly/\E([a-z0-9]+)]]
|
28
|
174 function video_handlers.dailymotion(url)
|
29
|
175 local id = ptn.match(url)
|
28
|
176 if id == nil then
|
|
177 return false
|
|
178 end
|
|
179 url = "https://www.dailymotion.com/embed/video/"..id
|
|
180 video_iframe(url)
|
|
181 return true
|
|
182 end
|
|
183 end
|
|
184 do
|
30
|
185 local ptn = regex[[^\Qhttps://www.tiktok.com/\E[^/]+/video/([0-9]+)]]
|
28
|
186 function video_handlers.tiktok(url)
|
29
|
187 local id = ptn.match(url)
|
28
|
188 if id == nil then
|
|
189 return false
|
|
190 end
|
|
191 %><blockquote class="tiktok-embed" data-video-id="<%=id%>" style="max-width: 560px; margin-left: 0;"><section></section></blockquote><%
|
|
192 %><script async src="https://www.tiktok.com/embed.js"></script><%
|
|
193 return true
|
|
194 end
|
|
195 end
|
|
196 do
|
29
|
197 local ptn = regex[[\.[a-zA-Z0-9]+$]]
|
28
|
198 function video_handlers.file(url)
|
29
|
199 if not ptn.matches(url) then
|
28
|
200 return false
|
|
201 end
|
|
202 %><video controls width="560"><source src="<%=html_encode(url)%>"></video><%
|
|
203 return true
|
|
204 end
|
|
205 end
|
23
|
206
|
20
|
207 function html.video(bbcode,options)
|
23
|
208 local url = bbcode.contents
|
28
|
209 for _, handle in pairs(video_handlers) do
|
|
210 if handle(url) then return end
|
27
|
211 end
|
23
|
212 url = html_encode(url)
|
|
213 %><a href="<%=url%>"><%=url%></a><%
|
12
|
214 end
|
|
215
|
20
|
216 local function list_to_html(bbcode,options)
|
|
217 local list = to_list(bbcode.contents)
|
|
218 for _, item in ipairs(list) do
|
48
|
219 if type(item) == "table" and item.name == "item" then
|
20
|
220 %><li><% to_html(item.contents,options) %></li><%
|
|
221 end
|
|
222 end
|
|
223 options.strip_newline = true
|
|
224 end
|
|
225
|
48
|
226 function html.list(bbcode,options)
|
|
227 local tag = bbcode.param == "1" and "ol" or "ul"
|
|
228 %><<%=tag%>><%
|
20
|
229 list_to_html(bbcode,options)
|
48
|
230 %></<%=tag%>><%
|
20
|
231 end
|
|
232
|
|
233 function to_html(bbcode,options)
|
|
234 if options.strip_newline then
|
|
235 if type(bbcode) == "string" then
|
29
|
236 bbcode = starting_cr_regex.gsub(bbcode,"")
|
20
|
237 end
|
|
238 options.strip_newline = false
|
|
239 end
|
12
|
240 if type(bbcode) == "string" then
|
|
241 %><%= html_encode(bbcode) %><%
|
|
242 else
|
|
243 type(bbcode) == "table" or error()
|
|
244 if is_list(bbcode) then
|
|
245 for _, v in ipairs(bbcode) do
|
20
|
246 to_html(v,options)
|
12
|
247 end
|
|
248 else
|
|
249 local fn = html[bbcode.name] or error(bbcode.name.." not handled")
|
20
|
250 fn(bbcode,options)
|
12
|
251 end
|
|
252 end
|
|
253 end
|
|
254
|
|
255 function Bbcode.to_html(bbcode)
|
|
256 bbcode = bbcode_parse(bbcode)
|
49
|
257 %><div from_bbcode><%
|
20
|
258 to_html(bbcode,{strip_newline=false})
|
|
259 %></div><%
|
12
|
260 end
|
|
261
|
13
|
262
|
|
263 local doesnt_nest = list_to_set{
|
|
264 "url"
|
|
265 "code"
|
|
266 "img"
|
|
267 "video"
|
|
268 }
|
|
269
|
29
|
270 local url_regex = regex[[(^|\s)(https?://\S+)]]
|
|
271
|
13
|
272 local function preprocess(bbcode)
|
|
273 if type(bbcode) == "string" then
|
29
|
274 bbcode = url_regex.gsub( bbcode, "$1[url]$2[/url]" )
|
13
|
275 %><%= bbcode %><%
|
|
276 else
|
|
277 type(bbcode) == "table" or error()
|
|
278 if is_list(bbcode) then
|
|
279 for _, v in ipairs(bbcode) do
|
|
280 preprocess(v)
|
|
281 end
|
|
282 else
|
|
283 local name = bbcode.name
|
|
284 local param = bbcode.param
|
|
285 %>[<%=name%><%
|
|
286 if param ~= nil then
|
|
287 %>=<%=param%><%
|
|
288 end
|
|
289 %>]<%
|
|
290 if doesnt_nest[name] then
|
|
291 %><%=bbcode.contents%><%
|
|
292 else
|
|
293 preprocess(bbcode.contents)
|
|
294 end
|
20
|
295 if name == "code" and param ~= nil then
|
|
296 %>[/<%=name%>=<%=param%>]<%
|
|
297 else
|
|
298 %>[/<%=name%>]<%
|
|
299 end
|
13
|
300 end
|
|
301 end
|
|
302 end
|
|
303
|
43
|
304 -- url handling
|
13
|
305 function Bbcode.preprocess(bbcode)
|
|
306 bbcode = bbcode_parse(bbcode)
|
|
307 return output_of(function()
|
|
308 preprocess(bbcode)
|
|
309 end)
|
|
310 end
|
|
311
|
21
|
312 function Bbcode.remove_html(text)
|
29
|
313 local ends_with_newline = ends_with(text,"\n")
|
21
|
314 local t = {}
|
|
315 local html = html_parse(text)
|
|
316 local is_first = true
|
|
317 for _, v in ipairs(html) do
|
|
318 if type(v) == "string" then
|
|
319 t[#t+1] = v
|
|
320 else
|
|
321 local name = v.name
|
|
322 if name == "div" then
|
|
323 if not is_first then
|
|
324 t[#t+1] = "\n"
|
|
325 end
|
|
326 elseif name == "/div" or name == "br" then
|
|
327 -- ignore
|
|
328 else
|
|
329 error("unexpected tag: "..name)
|
|
330 end
|
|
331 end
|
|
332 is_first = false
|
|
333 end
|
|
334 if not ends_with_newline then
|
|
335 t[#t+1] = "\n"
|
|
336 end
|
|
337 return concat(t)
|
|
338 end
|
|
339
|
|
340
|
12
|
341 return Bbcode
|