Mercurial Hosting > freedit
annotate src/bbcode/Bbcode.luan @ 57:169ac5fdb320
add change email
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Mon, 28 Nov 2022 23:47:19 -0700 |
parents | cac477dd1f82 |
children |
rev | line source |
---|---|
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) |
53
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
85 %><img src="<%= html_encode(bbcode.contents) %>"<% |
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
86 if bbcode.param ~= nil then |
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
87 %> width="<%=bbcode.param%>"<% |
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
88 end |
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
89 %>><% |
12 | 90 end |
91 | |
20 | 92 function html.color(bbcode,options) |
93 %><span style="color:<%=bbcode.param%>"><% to_html(bbcode.contents,options) %></span><% | |
12 | 94 end |
95 | |
20 | 96 function html.size(bbcode,options) |
23 | 97 %><span style="font-size:<%=bbcode.param%>"><% to_html(bbcode.contents,options) %></span><% |
12 | 98 end |
99 | |
20 | 100 function html.quote(bbcode,options) |
12 | 101 %><blockquote><% |
102 local user_name = bbcode.param | |
103 if user_name ~= nil then | |
104 local user = User.get_by_name(user_name) | |
105 if user == nil then | |
106 %><%= user_name %> wrote:<% | |
107 else | |
108 %><a href="/user_something"><%= user_name %></a> wrote:<% | |
109 end | |
20 | 110 else |
111 options.strip_newline = true | |
12 | 112 end |
20 | 113 to_html(bbcode.contents,options) |
12 | 114 %></blockquote><% |
20 | 115 options.strip_newline = true |
12 | 116 end |
117 | |
28 | 118 local function video_iframe(url) |
119 %><iframe width="560" height="315" frameborder="0" allowfullscreen src="<%=url%>"></iframe><% | |
120 end | |
121 | |
53
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
122 local video_urls = { |
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
123 [[\.mp4$]] |
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
124 } |
28 | 125 local video_handlers = {} |
53
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
126 local function regex_video(s) |
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
127 video_urls[#video_urls+1] = s |
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
128 return regex(s) |
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
129 end |
28 | 130 do |
53
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
131 local ptn1 = regex_video[[^\Qhttps://youtu.be/\E([a-zA-Z0-9_-]+)(?:\?t=([0-9]+))?]] |
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
132 local ptn2 = regex_video[[^\Qhttps://www.youtube.com/watch?v=\E([a-zA-Z0-9_-]+)(?:&t=([0-9]+)s)?]] |
28 | 133 function video_handlers.youtube(url) |
29 | 134 local id, start = ptn1.match(url) |
28 | 135 if id == nil then |
29 | 136 id, start = ptn2.match(url) |
28 | 137 end |
138 if id == nil then | |
139 return false | |
140 end | |
141 url = "https://www.youtube.com/embed/"..id | |
142 if start ~= nil then | |
143 url = url.."?start="..start | |
144 end | |
145 video_iframe(url) | |
146 return true | |
147 end | |
148 end | |
149 do | |
53
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
150 local ptn = regex_video[[^\Qhttps://rumble.com/embed/\E[a-z0-9]+/\?pub=[a-z0-9]+]] |
28 | 151 function video_handlers.rumble(url) |
29 | 152 if not ptn.matches(url) then |
28 | 153 return false |
154 end | |
155 video_iframe(url) | |
156 return true | |
157 end | |
158 end | |
159 do | |
53
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
160 local ptn = regex_video[[^\Qhttps://www.bitchute.com/video/\E([a-zA-Z0-9]+)/]] |
28 | 161 function video_handlers.bitchute(url) |
29 | 162 local id = ptn.match(url) |
28 | 163 if id == nil then |
164 return false | |
165 end | |
166 url = "https://www.bitchute.com/embed/"..id.."/" | |
167 video_iframe(url) | |
168 return true | |
169 end | |
170 end | |
171 do | |
53
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
172 local ptn = regex_video[[^\Qhttps://vimeo.com/\E([0-9]+)]] |
28 | 173 function video_handlers.vimeo(url) |
29 | 174 local id = ptn.match(url) |
28 | 175 if id == nil then |
176 return false | |
177 end | |
178 url = "https://player.vimeo.com/video/"..id | |
179 video_iframe(url) | |
180 return true | |
181 end | |
182 end | |
183 do | |
53
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
184 local ptn = regex_video[[^\Qhttps://dai.ly/\E([a-z0-9]+)]] |
28 | 185 function video_handlers.dailymotion(url) |
29 | 186 local id = ptn.match(url) |
28 | 187 if id == nil then |
188 return false | |
189 end | |
190 url = "https://www.dailymotion.com/embed/video/"..id | |
191 video_iframe(url) | |
192 return true | |
193 end | |
194 end | |
195 do | |
53
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
196 local ptn = regex_video[[^\Qhttps://www.tiktok.com/\E[^/]+/video/([0-9]+)]] |
28 | 197 function video_handlers.tiktok(url) |
29 | 198 local id = ptn.match(url) |
28 | 199 if id == nil then |
200 return false | |
201 end | |
53
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
202 %><iframe allowfullscreen scrolling="no" width="325" height="720" src="https://www.tiktok.com/embed/<%=id%>"></iframe><% |
28 | 203 return true |
204 end | |
205 end | |
206 do | |
29 | 207 local ptn = regex[[\.[a-zA-Z0-9]+$]] |
28 | 208 function video_handlers.file(url) |
29 | 209 if not ptn.matches(url) then |
28 | 210 return false |
211 end | |
212 %><video controls width="560"><source src="<%=html_encode(url)%>"></video><% | |
213 return true | |
214 end | |
215 end | |
23 | 216 |
53
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
217 local video_regex = regex(concat(video_urls,"|")) |
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
218 |
20 | 219 function html.video(bbcode,options) |
23 | 220 local url = bbcode.contents |
28 | 221 for _, handle in pairs(video_handlers) do |
222 if handle(url) then return end | |
27 | 223 end |
23 | 224 url = html_encode(url) |
225 %><a href="<%=url%>"><%=url%></a><% | |
12 | 226 end |
227 | |
20 | 228 local function list_to_html(bbcode,options) |
229 local list = to_list(bbcode.contents) | |
230 for _, item in ipairs(list) do | |
48 | 231 if type(item) == "table" and item.name == "item" then |
20 | 232 %><li><% to_html(item.contents,options) %></li><% |
233 end | |
234 end | |
235 options.strip_newline = true | |
236 end | |
237 | |
48 | 238 function html.list(bbcode,options) |
239 local tag = bbcode.param == "1" and "ol" or "ul" | |
240 %><<%=tag%>><% | |
20 | 241 list_to_html(bbcode,options) |
48 | 242 %></<%=tag%>><% |
20 | 243 end |
244 | |
245 function to_html(bbcode,options) | |
246 if options.strip_newline then | |
247 if type(bbcode) == "string" then | |
29 | 248 bbcode = starting_cr_regex.gsub(bbcode,"") |
20 | 249 end |
250 options.strip_newline = false | |
251 end | |
12 | 252 if type(bbcode) == "string" then |
253 %><%= html_encode(bbcode) %><% | |
254 else | |
255 type(bbcode) == "table" or error() | |
256 if is_list(bbcode) then | |
257 for _, v in ipairs(bbcode) do | |
20 | 258 to_html(v,options) |
12 | 259 end |
260 else | |
261 local fn = html[bbcode.name] or error(bbcode.name.." not handled") | |
20 | 262 fn(bbcode,options) |
12 | 263 end |
264 end | |
265 end | |
266 | |
267 function Bbcode.to_html(bbcode) | |
268 bbcode = bbcode_parse(bbcode) | |
49 | 269 %><div from_bbcode><% |
20 | 270 to_html(bbcode,{strip_newline=false}) |
271 %></div><% | |
12 | 272 end |
273 | |
13 | 274 |
275 local doesnt_nest = list_to_set{ | |
276 "url" | |
277 "code" | |
278 "img" | |
279 "video" | |
280 } | |
281 | |
29 | 282 local url_regex = regex[[(^|\s)(https?://\S+)]] |
53
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
283 local img_regex |
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
284 do |
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
285 local endings = { |
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
286 "jpg" |
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
287 "jpeg" |
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
288 "png" |
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
289 "gif" |
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
290 "svg" |
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
291 } |
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
292 local t = {} |
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
293 for _, ending in ipairs(endings) do |
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
294 t[#t+1] = [[\.]]..ending..[[$]] |
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
295 end |
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
296 img_regex = regex(concat(t,"|")) |
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
297 end |
29 | 298 |
13 | 299 local function preprocess(bbcode) |
300 if type(bbcode) == "string" then | |
53
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
301 bbcode = url_regex.gsub( bbcode, function(prefix,url) |
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
302 if video_regex.matches(url) then |
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
303 return prefix.."[video]"..url.."[/video]" |
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
304 elseif img_regex.matches(url) then |
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
305 return prefix.."[img]"..url.."[/img]" |
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
306 else |
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
307 return prefix.."[url]"..url.."[/url]" |
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
308 end |
cac477dd1f82
convert image and video urls
Franklin Schmidt <fschmidt@gmail.com>
parents:
49
diff
changeset
|
309 end ) |
13 | 310 %><%= bbcode %><% |
311 else | |
312 type(bbcode) == "table" or error() | |
313 if is_list(bbcode) then | |
314 for _, v in ipairs(bbcode) do | |
315 preprocess(v) | |
316 end | |
317 else | |
318 local name = bbcode.name | |
319 local param = bbcode.param | |
320 %>[<%=name%><% | |
321 if param ~= nil then | |
322 %>=<%=param%><% | |
323 end | |
324 %>]<% | |
325 if doesnt_nest[name] then | |
326 %><%=bbcode.contents%><% | |
327 else | |
328 preprocess(bbcode.contents) | |
329 end | |
20 | 330 if name == "code" and param ~= nil then |
331 %>[/<%=name%>=<%=param%>]<% | |
332 else | |
333 %>[/<%=name%>]<% | |
334 end | |
13 | 335 end |
336 end | |
337 end | |
338 | |
43 | 339 -- url handling |
13 | 340 function Bbcode.preprocess(bbcode) |
341 bbcode = bbcode_parse(bbcode) | |
342 return output_of(function() | |
343 preprocess(bbcode) | |
344 end) | |
345 end | |
346 | |
21 | 347 function Bbcode.remove_html(text) |
29 | 348 local ends_with_newline = ends_with(text,"\n") |
21 | 349 local t = {} |
350 local html = html_parse(text) | |
351 local is_first = true | |
352 for _, v in ipairs(html) do | |
353 if type(v) == "string" then | |
354 t[#t+1] = v | |
355 else | |
356 local name = v.name | |
357 if name == "div" then | |
358 if not is_first then | |
359 t[#t+1] = "\n" | |
360 end | |
361 elseif name == "/div" or name == "br" then | |
362 -- ignore | |
363 else | |
364 error("unexpected tag: "..name) | |
365 end | |
366 end | |
367 is_first = false | |
368 end | |
369 if not ends_with_newline then | |
370 t[#t+1] = "\n" | |
371 end | |
372 return concat(t) | |
373 end | |
374 | |
375 | |
12 | 376 return Bbcode |