151 lines
4.2 KiB
Lua
151 lines
4.2 KiB
Lua
local babycode = {}
|
|
|
|
local string_trim = require("lapis.util").trim
|
|
local emoji = require("lib.babycode-emoji")
|
|
|
|
local Parser = require("lib.babycode-parser")
|
|
|
|
local function s_split(s, delimiter, max_matches, trim, allow_empty)
|
|
local result = {}
|
|
if s == "" then
|
|
return result
|
|
end
|
|
trim = trim == nil and true or trim
|
|
local tr = function(subj)
|
|
if trim then return string_trim(subj) else return subj end
|
|
end
|
|
max_matches = max_matches or -1
|
|
allow_empty = allow_empty == nil and true or allow_empty
|
|
|
|
if delimiter == "" then
|
|
for i=1, #s do
|
|
local c = s:sub(i, 1)
|
|
if allow_empty or c ~= "" then
|
|
table.insert(result, c)
|
|
if max_matches > 0 and #result == max_matches then
|
|
break
|
|
end
|
|
end
|
|
end
|
|
return result
|
|
end
|
|
|
|
local current_pos = 1
|
|
local delim_len = #delimiter
|
|
|
|
while true do
|
|
if max_matches > 0 and #result >= max_matches then
|
|
break
|
|
end
|
|
---@diagnostic disable-next-line: param-type-mismatch
|
|
local start_pos, end_pos = s:find(delimiter, current_pos, true)
|
|
if not start_pos then
|
|
break
|
|
end
|
|
local substr = s:sub(current_pos, start_pos - 1)
|
|
if allow_empty or substr ~= "" then
|
|
table.insert(result, tr(substr))
|
|
end
|
|
current_pos = end_pos + 1
|
|
end
|
|
|
|
local substr = s:sub(current_pos)
|
|
if allow_empty or substr ~= "" then
|
|
table.insert(result, tr(substr))
|
|
end
|
|
|
|
return result
|
|
end
|
|
|
|
local function list(tag, children)
|
|
local list_body = children:gsub(" +\n", "<br>"):gsub("\n\n+", "\1")
|
|
local list_items = s_split(list_body, "\1")
|
|
local lis = ""
|
|
for _, li in ipairs(list_items) do
|
|
lis = lis .. "<li>" .. li .. "</li>"
|
|
end
|
|
return "<" .. tag .. ">" .. lis .. "</" .. tag .. ">"
|
|
end
|
|
|
|
local tags = {
|
|
b = "<strong>$S</strong>",
|
|
i = "<em>$S</em>",
|
|
s = "<del>$S</del>",
|
|
img = "<div class=\"post-img-container\"><img class=\"block-img\" src=$A alt=%S></div>",
|
|
url = "<a href=\"$A\">$S</a>",
|
|
quote = "<blockquote>$S</blockquote>",
|
|
code = function(children)
|
|
local is_inline = children:match("\n") == nil
|
|
if is_inline then
|
|
return "<code class=\"inline-code\">" .. children .. "</code>"
|
|
else
|
|
local t = string_trim(children)
|
|
local button = ("<button type=button class=\"copy-code\" value=\"%s\">Copy</button>"):format(t)
|
|
return "<pre><span class=\"copy-code-container\">"..button.."</span><code>"..t.."</code></pre>"
|
|
end
|
|
end,
|
|
ul = function(children)
|
|
return list("ul", children)
|
|
end,
|
|
ol = function(children)
|
|
return list("ol", children)
|
|
end,
|
|
}
|
|
|
|
local text_only = {
|
|
code = true,
|
|
}
|
|
|
|
---renders babycode to html
|
|
---@param s string input babycode
|
|
---@param html_escape fun(s: string): string function to escape html
|
|
function babycode.to_html(s, html_escape)
|
|
-- normalize line ending chars
|
|
local subj = string_trim(html_escape(s)):gsub("\r\n", "\n"):gsub("\r", "\n")
|
|
local parser = Parser.new(subj)
|
|
parser.valid_bbcode_tags = tags
|
|
parser.valid_emotes = emoji
|
|
parser.bbcode_tags_only_text_children = text_only
|
|
|
|
local elements = parser:parse()
|
|
local out = ""
|
|
local function fold(element, nobr)
|
|
if type(element) == "string" then
|
|
if nobr then
|
|
return element
|
|
end
|
|
return element:gsub(" +\n", "<br>"):gsub("\n\n+", "<br><br>")
|
|
end
|
|
if element.type == "bbcode" then
|
|
local c = ""
|
|
for _, child in ipairs(element.children) do
|
|
local _nobr = element.name == "code" or element.name == "ul" or element.name == "ol"
|
|
c = c .. fold(child, _nobr)
|
|
end
|
|
local res = ""
|
|
if type(tags[element.name]) == "string" then
|
|
res = (tags[element.name]):gsub("%$S", c)
|
|
if element.attribute then
|
|
res = res:gsub("%$A", element.attribute)
|
|
end
|
|
return res
|
|
elseif type(tags[element.name]) == "function" then
|
|
res = tags[element.name](c, element.attribute)
|
|
end
|
|
return res
|
|
elseif element.type == "link" then
|
|
return "<a href=\""..element.url.."\">"..element.url.."</a>"
|
|
elseif element.type == "emote" then
|
|
return emoji[element.name]
|
|
elseif element.type == "ruler" then
|
|
return "<hr>"
|
|
end
|
|
end
|
|
for _, e in ipairs(elements) do
|
|
out = out .. fold(e, false)
|
|
end
|
|
return out
|
|
end
|
|
|
|
return babycode
|