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