comparison src/bbcode/Bbcode.luan @ 44:96f0c3d65698

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