".
-
-
-So then, for more fun, I had it generate callable tables instead of raw
-functions. As tables, the HTML-wrappers can be further indexed to get
-more-specific, class-tagged versions to fit a little more nicely with
-today's CSS frameworks:
-
-return html {
- body {
- div.class {
- p "Hello"
- },
- div.two.classes()
- }
-}
-
-yields "
Hello
"
-
-http://fossil.tangent128.name/LuaForum/artifact/adbc426fafc3cb1f37e0a1a2d7f4f50cc4a498c0
-
---]]
-
--- module for easy production of HTML via Lua table constructors
--- nice usage pattern:
---[[
-local htmlua = require"path.to.htmlua".new()
-local result
--- set userData, htmlData
-do _ENV = htmlua
- result = html{
- head {
- title "Page"
- },
- body {
- div.class {
- p {
- "User data: ", userData, " (escaped)"
- },
- p.class {
- "Raw HTML: ", rawHTML(htmlData)
- }
- }
- }
- }
-end
--- use tostring(result)
-
---]]
-
-local concat, tostring, string = table.concat, tostring, string
-local setmetatable, getmetatable, pairs, type = setmetatable, getmetatable, pairs, type
-
-local _ENV = {}
-
-local gen_mt, tag_mt, safe_mt, genHTML, safeHTML, escapeHTML, rawHTML
-
--- create HTML from a table
-function genHTML(tag, content)
-
- -- convenience; can call with table or string parameter
- if type(content) == "string" then
- return genHTML(tag, {content})
- end
-
- local name = tag._name
- local tagClass = tag._class
-
- content.class = content.class and ((tagClass or " ") .. content.class) or tagClass or nil
-
- local attTable = {}
- local bodyTable = {}
- for k,v in pairs(content) do
- local t = type(k)
- if t == "string" then
- attTable[#attTable + 1] = ('%s="%s"'):format(k,safeHTML(v))
- elseif t == "number" then
- bodyTable[#bodyTable + 1] = safeHTML(v)
- end
- end
-
- local atts = concat(attTable, " ")
- if #atts > 0 then atts = " " .. atts end
-
- local body = concat(bodyTable)
-
- local html = ("<%s%s>%s%s>"):format(name, atts, body, name)
-
- return rawHTML(html)
-
-end
-
--- autocreates callable tables that generate HTML for a tag
-gen_mt = {
- __index = function(context, name)
- local tag = setmetatable({
- _name = name,
- _class = false
- }, tag_mt)
- context[name] = tag
- return tag
- end
-}
-
--- make callable HTML generating tables, which can be autoindexed
--- for versions which autoadd class attributes
-tag_mt = {
- __call = genHTML,
- __index = function(tag, class)
- local oldClass = tag._class or ""
-
- local tagWithClass = setmetatable({
- _name = tag._name,
- _class = oldClass .. " " .. class
- }, tag_mt)
-
- tag[class] = tagWithClass
- return tagWithClass
- end
-}
-
--- make object that wraps a string known to be safe HTML
-safe_mt = {
- __tostring = function(self)
- return self.html
- end
-}
-
-function rawHTML(html)
- if type(html) == "string" then
- return setmetatable({
- html = html
- }, safe_mt)
- else
- return
- end
-end
-
--- get printable HTML, escaping unless marked to be used raw
-function safeHTML(text)
- if getmetatable(text) == safe_mt then
- return text.html
- end
-
- return escapeHTML(text)
-end
-
--- always escape HTML content
-function escapeHTML(text)
- -- escape special chars (<, >, ", and &)
- -- double quotes are assumed for attributes
- text = tostring(text):gsub("&", "&")
- text = text:gsub("<", "<")
- text = text:gsub(">", ">")
- text = text:gsub('"', """)
-
- -- unescape numeric entities and explicit &
- -- disabled due to expected confusion, belongs elsewhere
- --[[ text = text:gsub("&#(x%x+);", "%1;")
- text = text:gsub("&#(%d+);", "%1;")
- text = text:gsub("&", "&") ]]
-
- return text
-end
-
--- construct new context
-function new()
- return setmetatable({
- rawHTML = rawHTML,
- safeHTML = safeHTML,
- escapeHTML = escapeHTML,
- }, gen_mt)
-end
-
-return _ENV
diff --git a/utils/html.py b/utils/html.py
index a02852d..a288f00 100644
--- a/utils/html.py
+++ b/utils/html.py
@@ -9,10 +9,6 @@
from utils.utils import export
from utils.simpledoc import SimpleDoc, _attributes
-__all__ = [
- "jslibs",
-]
-
class Encoder(object):
__slots__ = (
"encoder",
@@ -178,28 +174,29 @@ def doi(self, number, **kwargs):
-def make_encodable(name, mode):
+def make_encodable(name, mode, attrib):
if mode == "stag":
def inner(self, *args, **kwargs):
- kwargs["src"] = self.encoder(kwargs.pop("src"))
+ kwargs[attrib] = self.encoder(kwargs.pop(attrib))
self.stag(name, *args, **kwargs)
elif mode == "tag":
def inner(self, *args, **kwargs):
- kwargs["src"] = self.encoder(kwargs.pop("src"))
+ kwargs[attrib] = self.encoder(kwargs.pop(attrib))
return self.tag(self, name, *args, **kwargs)
return inner
encodable = {
- "img": "stag",
- "source": "stag",
+ "img": ("stag", "src"),
+ "source": ("stag", "src"),
+ "link" : ("stag", "href"),
}
-for enc, mode in encodable.items():
- setattr(HTML, enc, make_encodable(enc, mode))
+for enc, (mode, attrib) in encodable.items():
+ setattr(HTML, enc, make_encodable(enc, mode, attrib))
########
@@ -231,7 +228,7 @@ def inner(self, *args, **kwargs):
return inner
stags = {
- "meta", "link", "iframe",
+ "meta", "iframe",
}
for stag in stags:
@@ -284,31 +281,6 @@ class CSSLib(Library):
pass
-class JSLib(Library):
- __slots__ = (
- "_async",
- )
-
- def __init__(self, *args, **kwargs):
- self._async = bool(kwargs.get("async", True))
- Library.__init__(self, kwargs["path"])
-
- def add(self, doc):
- if self._async:
- with doc.tag("script", "async", src=self.path):
- pass
- else:
- with doc.tag("script", src=self.path):
- pass
-
-
-jslibs = type("JSLibs", (object,), {
- "shower": JSLib(path="https://shwr.me/shower/shower.min.js"),
- "mathjax": JSLib(path="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"),
- "plotly": JSLib(path="https://cdn.plot.ly/plotly-latest.min.js"),
-})
-
-
class Presentation(HTML):
def slide(self, *args, **kwargs):
kwargs["klass"] = "slide"
diff --git a/utils/preproc.lua b/utils/preproc.lua
deleted file mode 100644
index 25290bf..0000000
--- a/utils/preproc.lua
+++ /dev/null
@@ -1,653 +0,0 @@
--- luapp.lua
-
-local M = {}
-
-M.VERSION = '0.3.1'
-
--- Lua 5.1 and 5.2 compat
-local load = pcall(load, '') and load or function(ld, source, mode_, env)
- local f, err = loadstring(ld, source)
- if not f then return f, err end
- return setfenv(f, env or _G)
-end
-
--- Count number of chars c in string s.
-local function countchar(s, c)
- local count = 0
- local i = 1
- while true do
- i = string.find(s, c, i)
- if i then count = count + 1; i = i + 1 else break end
- end
- return count
-end
-
--- In error message string, translate line numbers from
--- processed file to source file.
--- linenums is translation array (processed line number ->
--- source line number) or source line number.
-local function fix_linenums(message, linenums)
- message = message:gsub("(%b[]:)(%d+)", function(a,n)
- n = tonumber(n)
- local source_linenum =
- type(linenums) == "table" and (linenums[n] or '?') or
- type(linenums) == "number" and linenums + n - 1 or
- '?'
- return a .. source_linenum
- end)
- return message
-end
-
-
--- Expands $(...) syntax.
-local function parse_dollar_paren(pieces, chunk, name, linenum)
- local is = 1
- for ibegin, iend in chunk:gmatch("()$%b()()") do
- local text = chunk:sub(is, ibegin - 1)
- local executed = chunk:sub(ibegin+2, iend-2) -- remove parens
-
- local name2 = name .. ":" .. executed
- linenum = linenum + countchar(text, '\n')
- local may_have_comment = executed:find("%-%-")
- local nl = may_have_comment and "\n" or ""
-
- pieces[#pieces+1] = ("_put(%q)"):format(text)
- if load("return " .. executed, name2) then -- is expression list
- pieces[#pieces+1] = "_put(" .. executed .. nl .. ")"
- else -- assume chunk
- local status, message = load(executed, name2)
- if not status then -- unrecognized
- if message then
- message = fix_linenums(message, linenum)
- end
- return status, message
- end
- pieces[#pieces+1] = " " .. executed .. nl .. " "
- linenum = linenum + countchar(executed, '\n')
- end
- is = iend
- end
- pieces[#pieces+1] = ("_put(%q)"):format(chunk:sub(is))
- return true
-end
-
--- Expands #... syntax.
-local function parse_hash_lines(chunk, name, env)
- local pieces = {}
-
- local luas = {} -- for improved error reporting
- local linenums = {}
- local linenum = 1
-
- pieces[#pieces+1] = "local _put = ... "
-
- local is = 1
- while true do
- local _, ie, lua = chunk:find("^#+([^\n]*\n?)", is)
- if not ie then
- local iss; iss, ie, lua = chunk:find("\n#+([^\n]*\n?)", is)
- local text = chunk:sub(is, iss)
- local status, message = parse_dollar_paren(pieces, text, name, linenum)
- if not status then return status, message end
- if not ie then break end
- linenum = linenum + countchar(text, '\n')
- end
-
- luas[#luas+1] = lua
- linenums[#linenums+1] = linenum
- linenum = linenum + 1
-
- pieces[#pieces+1] = ' ' .. lua .. ' '
-
- is = ie + 1
- end
-
- local code = table.concat(pieces, ' ')
-
- -- Attempt to compile.
- local f, message = load(code, name, 't', env)
- if not f then
- -- Attempt to compile only user-written Lua
- -- (for cleaner error message)
- local lua = table.concat(luas)
- local f2, message2 = load(lua, name, 't', env)
- if not f2 then
- message = fix_linenums(message2, linenums)
- else -- unexpected
- message = fix_linenums(message, nil)
- end
- end
-
- return f, message
-end
-
--- Abstraction of string output stream.
-local function string_writer()
- local t = {}
- local function write(...)
- local n = select('#', ...)
- if n > 0 then
- t[#t+1] = tostring((...))
- write(select(2, ...))
- end
- end
- local function close()
- return table.concat(t)
- end
- return {write=write, close=close}
-end
-
--- Abstraction of file output stream.
-local function file_writer(fh, is_close)
- local function write(...)
- local n = select('#', ...)
- if n > 0 then
- fh:write(tostring((...)))
- write(select(2, ...))
- end
- end
- local function close()
- if is_close then fh:close() end
- end
- return {write=write, close=close}
-end
-
--- Convert output specification to output stream.
--- A helper function for C.
-local function make_output(output)
- if type(output) == 'string' then
- output = string_writer()
- elseif type(output) == 'table' then
- assert(#output == 1, 'table size must be 1')
- local filename = output[1]
- local fh, message = io.open(filename, 'w')
- if not fh then return false, message end
- output = file_writer(fh, true)
- elseif io.type(output) == 'file' then
- output = file_writer(output, false)
- else
- error('unrecognized', 2)
- end
- return output
-end
-
--- Convert input specification to input stream.
--- A helper function for C.
-local function make_input(input)
- if type(input) == 'string' then
- input = {text = input, name = 'source'}
- elseif type(input) == 'table' then
- assert(#input == 1, 'table size must be 1')
- local filename = input[1]
- local fh, message = io.open(filename)
- if not fh then return false, message end
- input = {text = fh:read'*a', name = filename}
- fh:close()
- elseif io.type(input) == 'file' then
- input = {text = input:read'*a', name = nil}
- else
- error('unrecognized', 2)
- end
- return input
-end
-
-function M.preprocess(t)
- if type(t) == 'string' then t = {input = t} end
- local input = t.input or io.stdin
- local output = t.output or
- (type(input) == 'string' and 'string') or io.stdout
- local lookup = t.lookup or _G
- local strict = t.strict; if strict == nil then strict = true end
-
- local err; input, err = make_input(input)
- if not input then error(err, 2) end
-
- local name = input.name or "