improved UX 3d vs 2d view
This commit is contained in:
parent
64e036c632
commit
1ff1b819ba
12 changed files with 117 additions and 729 deletions
|
|
@ -6,8 +6,6 @@ package.path = package.path .. ";plugin/?.lua"
|
||||||
|
|
||||||
util = require "util"
|
util = require "util"
|
||||||
json = require "json"
|
json = require "json"
|
||||||
liluat = require "liluat"
|
|
||||||
template = require "template"
|
|
||||||
|
|
||||||
-- special script called by main redbean process at startup
|
-- special script called by main redbean process at startup
|
||||||
HidePath('/usr/share/zoneinfo/')
|
HidePath('/usr/share/zoneinfo/')
|
||||||
|
|
@ -71,7 +69,10 @@ print = function(...) Log(kLogInfo, ... or "''") end -- nice redbean logging fro
|
||||||
-- init URL router
|
-- init URL router
|
||||||
app.url['^/data'] = '/data.lua' -- setup custom file endpoint
|
app.url['^/data'] = '/data.lua' -- setup custom file endpoint
|
||||||
|
|
||||||
app.url['^/$'] = '/page/index.lua'
|
app.get('^/$', function(req,res,next)
|
||||||
|
res.template = 'page/index.html'
|
||||||
|
next()
|
||||||
|
end)
|
||||||
app.url['^/admin[/]?$'] = '/page/admin/index.lua'
|
app.url['^/admin[/]?$'] = '/page/admin/index.lua'
|
||||||
|
|
||||||
app.get('^/design[/]?$', util.pagerender('page/design.html'))
|
app.get('^/design[/]?$', util.pagerender('page/design.html'))
|
||||||
|
|
|
||||||
|
|
@ -1,535 +0,0 @@
|
||||||
--[[
|
|
||||||
-- liluat - Lightweight Lua Template engine
|
|
||||||
--
|
|
||||||
-- Project page: https://github.com/FSMaxB/liluat
|
|
||||||
--
|
|
||||||
-- liluat is based on slt2 by henix, see https://github.com/henix/slt2
|
|
||||||
--
|
|
||||||
-- Copyright © 2016 Max Bruckner
|
|
||||||
-- Copyright © 2011-2016 henix
|
|
||||||
--
|
|
||||||
-- Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
-- of this software and associated documentation files (the "Software"), to deal
|
|
||||||
-- in the Software without restriction, including without limitation the rights
|
|
||||||
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
-- copies of the Software, and to permit persons to whom the Software is furnished
|
|
||||||
-- to do so, subject to the following conditions:
|
|
||||||
--
|
|
||||||
-- The above copyright notice and this permission notice shall be included in
|
|
||||||
-- all copies or substantial portions of the Software.
|
|
||||||
--
|
|
||||||
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
||||||
-- WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
|
||||||
-- IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
--]]
|
|
||||||
|
|
||||||
local liluat = {
|
|
||||||
private = {} --used to expose private functions for testing
|
|
||||||
}
|
|
||||||
|
|
||||||
-- print the current version
|
|
||||||
liluat.version = function ()
|
|
||||||
return "1.2.0"
|
|
||||||
end
|
|
||||||
|
|
||||||
-- returns a string containing the fist line until the last line
|
|
||||||
local function string_lines(lines, first, last)
|
|
||||||
-- allow negative line numbers
|
|
||||||
first = (first >= 1) and first or 1
|
|
||||||
|
|
||||||
local start_position
|
|
||||||
local current_position = 1
|
|
||||||
local line_counter = 1
|
|
||||||
repeat
|
|
||||||
if line_counter == first then
|
|
||||||
start_position = current_position
|
|
||||||
end
|
|
||||||
current_position = lines:find('\n', current_position + 1, true)
|
|
||||||
line_counter = line_counter + 1
|
|
||||||
until (line_counter == (last + 1)) or (not current_position)
|
|
||||||
|
|
||||||
return lines:sub(start_position, current_position)
|
|
||||||
end
|
|
||||||
liluat.private.string_lines = string_lines
|
|
||||||
|
|
||||||
-- escape a string for use in lua patterns
|
|
||||||
-- (this simply prepends all non alphanumeric characters with '%'
|
|
||||||
local function escape_pattern(text)
|
|
||||||
return text:gsub("([^%w])", "%%%1" --[[function (match) return "%"..match end--]])
|
|
||||||
end
|
|
||||||
liluat.private.escape_pattern = escape_pattern
|
|
||||||
|
|
||||||
-- recursively copy a table
|
|
||||||
local function clone_table(table)
|
|
||||||
local clone = {}
|
|
||||||
|
|
||||||
for key, value in pairs(table) do
|
|
||||||
if type(value) == "table" then
|
|
||||||
clone[key] = clone_table(value)
|
|
||||||
else
|
|
||||||
clone[key] = value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return clone
|
|
||||||
end
|
|
||||||
liluat.private.clone_table = clone_table
|
|
||||||
|
|
||||||
-- recursively merge two tables, the second one has precedence
|
|
||||||
-- if 'shallow' is set, the second table isn't copied recursively,
|
|
||||||
-- its content is only referenced instead
|
|
||||||
local function merge_tables(a, b, shallow)
|
|
||||||
a = a or {}
|
|
||||||
b = b or {}
|
|
||||||
|
|
||||||
local merged = clone_table(a)
|
|
||||||
|
|
||||||
for key, value in pairs(b) do
|
|
||||||
if (type(value) == "table") and (not shallow) then
|
|
||||||
if a[key] then
|
|
||||||
merged[key] = merge_tables(a[key], value)
|
|
||||||
else
|
|
||||||
merged[key] = clone_table(value)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
merged[key] = value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return merged
|
|
||||||
end
|
|
||||||
liluat.private.merge_tables = merge_tables
|
|
||||||
|
|
||||||
local default_options = {
|
|
||||||
start_tag = "{{",
|
|
||||||
end_tag = "}}",
|
|
||||||
trim_right = "code",
|
|
||||||
trim_left = "code"
|
|
||||||
}
|
|
||||||
|
|
||||||
-- initialise table of options (use the provided, default otherwise)
|
|
||||||
local function initialise_options(options)
|
|
||||||
return merge_tables(default_options, options)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- creates an iterator that iterates over all chunks in the given template
|
|
||||||
-- a chunk is either a template delimited by start_tag and end_tag or a normal text
|
|
||||||
-- the iterator also returns the type of the chunk as second return value
|
|
||||||
local function all_chunks(template, options)
|
|
||||||
options = initialise_options(options)
|
|
||||||
|
|
||||||
-- pattern to match a template chunk
|
|
||||||
local template_pattern = escape_pattern(options.start_tag) .. "([+-]?)(.-)([+-]?)" .. escape_pattern(options.end_tag)
|
|
||||||
local include_pattern = "^"..escape_pattern(options.start_tag) .. "[+-]?include:(.-)[+-]?" .. escape_pattern(options.end_tag)
|
|
||||||
local expression_pattern = "^"..escape_pattern(options.start_tag) .. "[+-]?=(.-)[+-]?" .. escape_pattern(options.end_tag)
|
|
||||||
local position = 1
|
|
||||||
|
|
||||||
return function ()
|
|
||||||
if not position then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local template_start, template_end, trim_left, template_capture, trim_right = template:find(template_pattern, position)
|
|
||||||
|
|
||||||
local chunk = {}
|
|
||||||
if template_start == position then -- next chunk is a template chunk
|
|
||||||
if trim_left == "+" then
|
|
||||||
chunk.trim_left = false
|
|
||||||
elseif trim_left == "-" then
|
|
||||||
chunk.trim_left = true
|
|
||||||
end
|
|
||||||
if trim_right == "+" then
|
|
||||||
chunk.trim_right = false
|
|
||||||
elseif trim_right == "-" then
|
|
||||||
chunk.trim_right = true
|
|
||||||
end
|
|
||||||
|
|
||||||
local include_start, include_end, include_capture = template:find(include_pattern, position)
|
|
||||||
local expression_start, expression_end, expression_capture
|
|
||||||
if not include_start then
|
|
||||||
expression_start, expression_end, expression_capture = template:find(expression_pattern, position)
|
|
||||||
end
|
|
||||||
|
|
||||||
if include_start then
|
|
||||||
chunk.type = "include"
|
|
||||||
chunk.text = include_capture
|
|
||||||
elseif expression_start then
|
|
||||||
chunk.type = "expression"
|
|
||||||
chunk.text = expression_capture
|
|
||||||
else
|
|
||||||
chunk.type = "code"
|
|
||||||
chunk.text = template_capture
|
|
||||||
end
|
|
||||||
|
|
||||||
position = template_end + 1
|
|
||||||
return chunk
|
|
||||||
elseif template_start then -- next chunk is a text chunk
|
|
||||||
chunk.type = "text"
|
|
||||||
chunk.text = template:sub(position, template_start - 1)
|
|
||||||
position = template_start
|
|
||||||
return chunk
|
|
||||||
else -- no template chunk found --> either text chunk until end of file or no chunk at all
|
|
||||||
chunk.text = template:sub(position)
|
|
||||||
chunk.type = "text"
|
|
||||||
position = nil
|
|
||||||
return (#chunk.text > 0) and chunk or nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
liluat.private.all_chunks = all_chunks
|
|
||||||
|
|
||||||
local function read_entire_file(path)
|
|
||||||
assert(path)
|
|
||||||
local file = assert(io.open(path))
|
|
||||||
local file_content = file:read('*a')
|
|
||||||
file:close()
|
|
||||||
return file_content
|
|
||||||
end
|
|
||||||
liluat.private.read_entire_file = read_entire_file
|
|
||||||
|
|
||||||
-- a whitelist of allowed functions
|
|
||||||
local sandbox_whitelist = {
|
|
||||||
ipairs = ipairs,
|
|
||||||
next = next,
|
|
||||||
pairs = pairs,
|
|
||||||
rawequal = rawequal,
|
|
||||||
rawget = rawget,
|
|
||||||
rawset = rawset,
|
|
||||||
select = select,
|
|
||||||
tonumber = tonumber,
|
|
||||||
tostring = tostring,
|
|
||||||
type = type,
|
|
||||||
unpack = unpack,
|
|
||||||
string = string,
|
|
||||||
table = table,
|
|
||||||
math = math,
|
|
||||||
os = {
|
|
||||||
date = os.date,
|
|
||||||
difftime = os.difftime,
|
|
||||||
time = os.time,
|
|
||||||
},
|
|
||||||
coroutine = coroutine
|
|
||||||
}
|
|
||||||
|
|
||||||
-- puts line numbers in front of a string and optionally highlights a single line
|
|
||||||
local function prepend_line_numbers(lines, first, highlight)
|
|
||||||
first = (first and (first >= 1)) and first or 1
|
|
||||||
lines = lines:gsub("\n$", "") -- make sure the last line isn't empty
|
|
||||||
lines = lines:gsub("^\n", "") -- make sure the first line isn't empty
|
|
||||||
|
|
||||||
local current_line = first + 1
|
|
||||||
return string.format("%3d: ", first) .. lines:gsub('\n', function ()
|
|
||||||
local highlight_char = ' '
|
|
||||||
if current_line == tonumber(highlight) then
|
|
||||||
highlight_char = '> '
|
|
||||||
end
|
|
||||||
|
|
||||||
local replacement = string.format("\n%3d:%s", current_line, highlight_char)
|
|
||||||
current_line = current_line + 1
|
|
||||||
|
|
||||||
return replacement
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
liluat.private.prepend_line_numbers = prepend_line_numbers
|
|
||||||
|
|
||||||
-- creates a function in a sandbox from a given code,
|
|
||||||
-- name of the execution context and an environment
|
|
||||||
-- that will be available inside the sandbox,
|
|
||||||
-- optionally overwrite the whitelist
|
|
||||||
local function sandbox(code, name, environment, whitelist, reference)
|
|
||||||
whitelist = whitelist or sandbox_whitelist
|
|
||||||
name = name or 'unknown'
|
|
||||||
|
|
||||||
-- prepare the environment
|
|
||||||
environment = merge_tables(whitelist, environment, reference)
|
|
||||||
|
|
||||||
local func
|
|
||||||
local error_message
|
|
||||||
if setfenv then --Lua 5.1 and compatible
|
|
||||||
if code:byte(1) == 27 then
|
|
||||||
error("Lua bytecode not permitted.", 2)
|
|
||||||
end
|
|
||||||
func, error_message = loadstring(code)
|
|
||||||
if func then
|
|
||||||
setfenv(func, environment)
|
|
||||||
end
|
|
||||||
else -- Lua 5.2 and later
|
|
||||||
func, error_message = load(code, name, 't', environment)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- handle compile error and print pretty error message
|
|
||||||
if not func then
|
|
||||||
local line_number, message = error_message:match(":(%d+):(.*)")
|
|
||||||
-- lines before and after the error
|
|
||||||
local lines = string_lines(code, line_number - 3, line_number + 3)
|
|
||||||
error(
|
|
||||||
'Syntax error in sandboxed code "' .. name .. '" in line ' .. line_number .. ':\n'
|
|
||||||
.. message .. '\n\n'
|
|
||||||
.. prepend_line_numbers(lines, line_number - 3, line_number),
|
|
||||||
3
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
return func
|
|
||||||
end
|
|
||||||
liluat.private.sandbox = sandbox
|
|
||||||
|
|
||||||
local function parse_string_literal(string_literal)
|
|
||||||
return sandbox('return' .. string_literal, nil, nil, {})()
|
|
||||||
end
|
|
||||||
liluat.private.parse_string_literal = parse_string_literal
|
|
||||||
|
|
||||||
-- add an include to the include_list and throw an error if
|
|
||||||
-- an inclusion cycle is detected
|
|
||||||
local function add_include_and_detect_cycles(include_list, path)
|
|
||||||
local parent = include_list[0]
|
|
||||||
while parent do -- while the root hasn't been reached
|
|
||||||
if parent[path] then
|
|
||||||
error("Cyclic inclusion detected")
|
|
||||||
end
|
|
||||||
|
|
||||||
parent = parent[0]
|
|
||||||
end
|
|
||||||
|
|
||||||
include_list[path] = {
|
|
||||||
[0] = include_list
|
|
||||||
}
|
|
||||||
end
|
|
||||||
liluat.private.add_include_and_detect_cycles = add_include_and_detect_cycles
|
|
||||||
|
|
||||||
-- extract the name of a directory from a path
|
|
||||||
local function dirname(path)
|
|
||||||
return path:match("^(.*/).-$") or ""
|
|
||||||
end
|
|
||||||
liluat.private.dirname = dirname
|
|
||||||
|
|
||||||
-- splits a template into chunks
|
|
||||||
-- chunks are either a template delimited by start_tag and end_tag
|
|
||||||
-- or a text chunk (everything else)
|
|
||||||
-- @return table
|
|
||||||
local function parse(template, options, output, include_list, current_path)
|
|
||||||
options = initialise_options(options)
|
|
||||||
current_path = current_path or "." -- current include path
|
|
||||||
|
|
||||||
include_list = include_list or {} -- a list of files that were included
|
|
||||||
local output = output or {}
|
|
||||||
|
|
||||||
for chunk in all_chunks(template, options) do
|
|
||||||
-- handle includes
|
|
||||||
if chunk.type == "include" then -- include chunk
|
|
||||||
local include_path_literal = chunk.text
|
|
||||||
local path = parse_string_literal(include_path_literal)
|
|
||||||
|
|
||||||
-- build complete path
|
|
||||||
if path:find("^/") then
|
|
||||||
--absolute path, don't modify
|
|
||||||
elseif options.base_path then
|
|
||||||
path = options.base_path .. "/" .. path
|
|
||||||
else
|
|
||||||
path = dirname(current_path) .. path
|
|
||||||
end
|
|
||||||
|
|
||||||
add_include_and_detect_cycles(include_list, path)
|
|
||||||
|
|
||||||
local included_template = read_entire_file(path)
|
|
||||||
parse(included_template, options, output, include_list[path], path)
|
|
||||||
elseif (chunk.type == "text") and output[#output] and (output[#output].type == "text") then
|
|
||||||
-- ensure that no two text chunks follow each other
|
|
||||||
output[#output].text = output[#output].text .. chunk.text
|
|
||||||
else -- other chunk
|
|
||||||
table.insert(output, chunk)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
return output
|
|
||||||
end
|
|
||||||
liluat.private.parse = parse
|
|
||||||
|
|
||||||
-- inline included template files
|
|
||||||
-- @return string
|
|
||||||
function liluat.inline(template, options, start_path)
|
|
||||||
options = initialise_options(options)
|
|
||||||
|
|
||||||
local output = {}
|
|
||||||
for _,chunk in ipairs(parse(template, options, nil, nil, start_path)) do
|
|
||||||
if chunk.type == "expression" then
|
|
||||||
table.insert(output, options.start_tag .. "=" .. chunk.text .. options.end_tag)
|
|
||||||
elseif chunk.type == "code" then
|
|
||||||
table.insert(output, options.start_tag .. chunk.text .. options.end_tag)
|
|
||||||
else
|
|
||||||
table.insert(output, chunk.text)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return table.concat(output)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- @return { string }
|
|
||||||
function liluat.get_dependencies(template, options, start_path)
|
|
||||||
options = initialise_options(options)
|
|
||||||
|
|
||||||
local include_list = {}
|
|
||||||
parse(template, options, nil, include_list, start_path)
|
|
||||||
|
|
||||||
local dependencies = {}
|
|
||||||
local have_seen = {} -- list of includes that were already added
|
|
||||||
local function recursive_traversal(list)
|
|
||||||
for key, value in pairs(list) do
|
|
||||||
if (type(key) == "string") and (not have_seen[key]) then
|
|
||||||
have_seen[key] = true
|
|
||||||
table.insert(dependencies, key)
|
|
||||||
recursive_traversal(value)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
recursive_traversal(include_list)
|
|
||||||
return dependencies
|
|
||||||
end
|
|
||||||
|
|
||||||
-- compile a template into lua code
|
|
||||||
-- @return { name = string, code = string / function}
|
|
||||||
function liluat.compile(template, options, template_name, start_path)
|
|
||||||
options = initialise_options(options)
|
|
||||||
template_name = template_name or 'liluat.compile'
|
|
||||||
|
|
||||||
local output_function = "__liluat_output_function"
|
|
||||||
|
|
||||||
-- split the template string into chunks
|
|
||||||
local lexed_template = parse(template, options, nil, nil, start_path)
|
|
||||||
|
|
||||||
-- table of code fragments the template is compiled into
|
|
||||||
local lua_code = {}
|
|
||||||
|
|
||||||
for i, chunk in ipairs(lexed_template) do
|
|
||||||
-- check if the chunk is a template (either code or expression)
|
|
||||||
if chunk.type == "expression" then
|
|
||||||
table.insert(lua_code, output_function..'('..chunk.text..')')
|
|
||||||
elseif chunk.type == "code" then
|
|
||||||
table.insert(lua_code, chunk.text)
|
|
||||||
else --text chunk
|
|
||||||
-- determine if this block needs to be trimmed right
|
|
||||||
-- (strip newline)
|
|
||||||
local trim_right = false
|
|
||||||
if lexed_template[i - 1] and (lexed_template[i - 1].trim_right == true) then
|
|
||||||
trim_right = true
|
|
||||||
elseif lexed_template[i - 1] and (lexed_template[i - 1].trim_right == false) then
|
|
||||||
trim_right = false
|
|
||||||
elseif options.trim_right == "all" then
|
|
||||||
trim_right = true
|
|
||||||
elseif options.trim_right == "code" then
|
|
||||||
trim_right = lexed_template[i - 1] and (lexed_template[i - 1].type == "code")
|
|
||||||
elseif options.trim_right == "expression" then
|
|
||||||
trim_right = lexed_template[i - 1] and (lexed_template[i - 1].type == "expression")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- determine if this block needs to be trimmed left
|
|
||||||
-- (strip whitespaces in front)
|
|
||||||
local trim_left = false
|
|
||||||
if lexed_template[i + 1] and (lexed_template[i + 1].trim_left == true) then
|
|
||||||
trim_left = true
|
|
||||||
elseif lexed_template[i + 1] and (lexed_template[i + 1].trim_left == false) then
|
|
||||||
trim_left = false
|
|
||||||
elseif options.trim_left == "all" then
|
|
||||||
trim_left = true
|
|
||||||
elseif options.trim_left == "code" then
|
|
||||||
trim_left = lexed_template[i + 1] and (lexed_template[i + 1].type == "code")
|
|
||||||
elseif options.trim_left == "expression" then
|
|
||||||
trim_left = lexed_template[i + 1] and (lexed_template[i + 1].type == "expression")
|
|
||||||
end
|
|
||||||
|
|
||||||
if trim_right and trim_left then
|
|
||||||
-- both at once
|
|
||||||
if i == 1 then
|
|
||||||
if chunk.text:find("^.*\n") then
|
|
||||||
chunk.text = chunk.text:match("^(.*\n)%s-$")
|
|
||||||
elseif chunk.text:find("^%s-$") then
|
|
||||||
chunk.text = ""
|
|
||||||
end
|
|
||||||
elseif chunk.text:find("^\n") then --have to trim a newline
|
|
||||||
if chunk.text:find("^\n.*\n") then --at least two newlines
|
|
||||||
chunk.text = chunk.text:match("^\n(.*\n)%s-$") or chunk.text:match("^\n(.*)$")
|
|
||||||
elseif chunk.text:find("^\n%s-$") then
|
|
||||||
chunk.text = ""
|
|
||||||
else
|
|
||||||
chunk.text = chunk.text:gsub("^\n", "")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
chunk.text = chunk.text:match("^(.*\n)%s-$") or chunk.text
|
|
||||||
end
|
|
||||||
elseif trim_left then
|
|
||||||
if i == 1 and chunk.text:find("^%s-$") then
|
|
||||||
chunk.text = ""
|
|
||||||
else
|
|
||||||
chunk.text = chunk.text:match("^(.*\n)%s-$") or chunk.text
|
|
||||||
end
|
|
||||||
elseif trim_right then
|
|
||||||
chunk.text = chunk.text:gsub("^\n", "")
|
|
||||||
end
|
|
||||||
if not (chunk.text == "") then
|
|
||||||
table.insert(lua_code, output_function..'('..string.format("%q", chunk.text)..')')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return {
|
|
||||||
name = template_name,
|
|
||||||
code = table.concat(lua_code, '\n')
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
-- compile a file
|
|
||||||
-- @return { name = string, code = string / function }
|
|
||||||
function liluat.compile_file(filename, options)
|
|
||||||
return liluat.compile(read_entire_file(filename), options, filename, filename)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- @return a coroutine function
|
|
||||||
function liluat.render_coroutine(template, environment, options)
|
|
||||||
options = initialise_options(options)
|
|
||||||
environment = merge_tables({__liluat_output_function = coroutine.yield}, environment, options.reference)
|
|
||||||
|
|
||||||
return sandbox(template.code, template.name, environment, nil, options.reference)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- @return string
|
|
||||||
function liluat.render(t, env, options)
|
|
||||||
options = initialise_options(options)
|
|
||||||
|
|
||||||
local result = {}
|
|
||||||
|
|
||||||
-- add closure that renders the text into the result table
|
|
||||||
env = merge_tables({
|
|
||||||
__liluat_output_function = function (text)
|
|
||||||
table.insert(result, text) end
|
|
||||||
},
|
|
||||||
env,
|
|
||||||
options.reference
|
|
||||||
)
|
|
||||||
|
|
||||||
-- compile and run the lua code
|
|
||||||
local render_function = sandbox(t.code, t.name, env, nil, options.reference)
|
|
||||||
local status, error_message = pcall(render_function)
|
|
||||||
if not status then
|
|
||||||
local line_number, message = error_message:match(":(%d+):(.*)")
|
|
||||||
message = message or ''
|
|
||||||
line_number = line_number or 0
|
|
||||||
-- lines before and after the error
|
|
||||||
local lines = string_lines(t.code, line_number - 3, line_number + 3)
|
|
||||||
error(
|
|
||||||
'Runtime error in sandboxed code "' .. t.name .. '" in line ' .. line_number .. ':\n'
|
|
||||||
.. message .. '\n\n'
|
|
||||||
.. prepend_line_numbers(lines, line_number - 3, line_number),
|
|
||||||
2
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
return table.concat(result)
|
|
||||||
end
|
|
||||||
|
|
||||||
return liluat
|
|
||||||
|
|
||||||
|
|
@ -1,130 +0,0 @@
|
||||||
-- local compile = require "tmpl"
|
|
||||||
--
|
|
||||||
-- local s = [[
|
|
||||||
-- <!DOCTYPE html>
|
|
||||||
-- <html>
|
|
||||||
-- <head>
|
|
||||||
-- {! this is a comment !}
|
|
||||||
-- <meta charset="utf-8">
|
|
||||||
-- <title>{{page.title}}</title>
|
|
||||||
-- <style>
|
|
||||||
-- body {
|
|
||||||
-- background-color: #{{page.bg}};
|
|
||||||
-- }
|
|
||||||
-- </style>
|
|
||||||
-- </head>
|
|
||||||
-- <body>
|
|
||||||
-- <h3>{{escaped}}</h3>
|
|
||||||
-- <h3>{* unescaped *}
|
|
||||||
-- <h3>{% echo 'haHAA' %}</h3>
|
|
||||||
-- {[template2]}
|
|
||||||
-- </body>
|
|
||||||
-- </html>
|
|
||||||
-- ]]
|
|
||||||
--
|
|
||||||
-- local s2 = [[
|
|
||||||
-- <ul>
|
|
||||||
-- {% for i = 1, 3 do %}
|
|
||||||
-- <li>{{i}}{{unit}}</li>
|
|
||||||
-- {% end %}
|
|
||||||
-- </ul>
|
|
||||||
-- {% if haHAA then %}
|
|
||||||
-- {[template2, sub]}
|
|
||||||
-- {% end %}
|
|
||||||
-- ]]
|
|
||||||
--
|
|
||||||
-- local template = compile(s)
|
|
||||||
-- local ctx = {
|
|
||||||
-- page = { title = 'Demo', bg = '332233' },
|
|
||||||
-- escaped = 'escaped: < &',
|
|
||||||
-- unescaped = 'unescaped: •</h3>',
|
|
||||||
-- template2 = compile(s2),
|
|
||||||
-- haHAA = true,
|
|
||||||
-- unit = 'g',
|
|
||||||
-- sub = {
|
|
||||||
-- -- custom escape function
|
|
||||||
-- escape = function(s)
|
|
||||||
-- i = tonumber(s)
|
|
||||||
-- if i then return i * 3 else return s end
|
|
||||||
-- end,
|
|
||||||
-- haHAA = false,
|
|
||||||
-- unit = 'kg'
|
|
||||||
-- }
|
|
||||||
-- }
|
|
||||||
--
|
|
||||||
-- for s in template(ctx) do
|
|
||||||
-- io.write(s)
|
|
||||||
-- end
|
|
||||||
return function(str)
|
|
||||||
local t = {[[
|
|
||||||
local ctx = ...
|
|
||||||
local _out = {}
|
|
||||||
local _ENV = setmetatable(ctx, { __index = _ENV })
|
|
||||||
|
|
||||||
local function echo(s)
|
|
||||||
if s and s ~= '' then
|
|
||||||
table.insert(_out, s)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function include(template, sub_ctx)
|
|
||||||
if sub_ctx then
|
|
||||||
sub_ctx.escape = sub_ctx.escape or _ENV.escape
|
|
||||||
else
|
|
||||||
sub_ctx = _ENV
|
|
||||||
end
|
|
||||||
table.insert(_out, template(sub_ctx))
|
|
||||||
end
|
|
||||||
|
|
||||||
_ENV.escape = escape or function(i)
|
|
||||||
return tostring(i or ''):gsub('[&<>"\'/]', {
|
|
||||||
['&'] = '&',
|
|
||||||
['<'] = '<',
|
|
||||||
['>'] = '>',
|
|
||||||
['"'] = '"',
|
|
||||||
["'"] = ''',
|
|
||||||
['/'] = '/'
|
|
||||||
})
|
|
||||||
end
|
|
||||||
]]}
|
|
||||||
|
|
||||||
local function f(pos)
|
|
||||||
local start, stop, c = pos - 1
|
|
||||||
repeat
|
|
||||||
start, stop = str:find('%b{}', start + 1)
|
|
||||||
if not start then
|
|
||||||
table.insert(t, ' echo([=====[\n')
|
|
||||||
table.insert(t, str:sub(pos, #str))
|
|
||||||
table.insert(t, ']=====]) ')
|
|
||||||
return
|
|
||||||
end
|
|
||||||
c = str:sub(start + 1, start + 1)
|
|
||||||
until c:match('[{%*%[%%!]')
|
|
||||||
|
|
||||||
table.insert(t, ' echo([=====[\n')
|
|
||||||
table.insert(t, str:sub(pos, start - 1))
|
|
||||||
table.insert(t, ']=====]) ')
|
|
||||||
|
|
||||||
if c == '{' then
|
|
||||||
table.insert(t, ' echo(escape(')
|
|
||||||
table.insert(t, str:sub(start + 2, stop - 2))
|
|
||||||
table.insert(t, ')) ')
|
|
||||||
elseif c == '*' then
|
|
||||||
table.insert(t, ' echo(')
|
|
||||||
table.insert(t, str:sub(start + 2, stop - 2))
|
|
||||||
table.insert(t, ') ')
|
|
||||||
elseif c == '[' then
|
|
||||||
table.insert(t, ' include(')
|
|
||||||
table.insert(t, str:sub(start + 2, stop - 2))
|
|
||||||
table.insert(t, ') ')
|
|
||||||
elseif c == '%' then
|
|
||||||
table.insert(t, str:sub(start + 2, stop - 2))
|
|
||||||
end
|
|
||||||
f(stop + 1)
|
|
||||||
end
|
|
||||||
f(1)
|
|
||||||
|
|
||||||
-- Return the concatenated string directly from the compiled function
|
|
||||||
table.insert(t, [[ return table.concat(_out) ]])
|
|
||||||
return load(table.concat(t))
|
|
||||||
end
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
/* color palette */
|
/* color palette */
|
||||||
.col1{ color:#f3f; }
|
.col1{ color:#f3f; }
|
||||||
|
.col12{ color:#83f; }
|
||||||
.col2{ color:#AAD; }
|
.col2{ color:#AAD; }
|
||||||
.col3{ color:#878; }
|
.col3{ color:#878; }
|
||||||
.col4{ color:#7070ff; }
|
.col4{ color:#7070ff; }
|
||||||
|
|
@ -97,11 +98,12 @@ input[type=submit]{
|
||||||
|
|
||||||
a, a:active, a:visited, a:link, input[type=submit] {
|
a, a:active, a:visited, a:link, input[type=submit] {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color:#FFF;
|
color:#8af;
|
||||||
border-bottom: 1px dotted #AAD;
|
border-bottom: 1px dotted #AAD;
|
||||||
}
|
}
|
||||||
|
|
||||||
a:hover{
|
a:hover,
|
||||||
|
.tile a{
|
||||||
color:#FFF !important;
|
color:#FFF !important;
|
||||||
cursor:pointer;
|
cursor:pointer;
|
||||||
border-bottom:1px dotted #f3f;
|
border-bottom:1px dotted #f3f;
|
||||||
|
|
@ -134,7 +136,7 @@ form > table > tbody > tr > td:nth-child(3) {
|
||||||
.primary { color: #ff33Ff;}
|
.primary { color: #ff33Ff;}
|
||||||
.secondary { color: #7070ff;}
|
.secondary { color: #7070ff;}
|
||||||
.btn a.primary{ background: #ff33Ff; color:#000}
|
.btn a.primary{ background: #ff33Ff; color:#000}
|
||||||
.btn a.secondary{ background: #7070ff; color:#000}
|
.btn a.secondary{ background: #224; color: #83f; }
|
||||||
|
|
||||||
.pad1{
|
.pad1{
|
||||||
padding:20px;
|
padding:20px;
|
||||||
|
|
@ -194,6 +196,7 @@ ul{
|
||||||
}
|
}
|
||||||
.menu a {
|
.menu a {
|
||||||
padding-right:30px;
|
padding-right:30px;
|
||||||
|
color:#FFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid .tile{ border-radius:5px; }
|
.grid .tile{ border-radius:5px; }
|
||||||
|
|
|
||||||
|
|
@ -154,6 +154,12 @@ u span{
|
||||||
box-shadow: 0px 0px 5px #F0F;
|
box-shadow: 0px 0px 5px #F0F;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.spectrum.span{
|
||||||
|
position: absolute;
|
||||||
|
left: 0px;
|
||||||
|
right: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
.spectrum > div,
|
.spectrum > div,
|
||||||
.ruler{
|
.ruler{
|
||||||
margin:3px 0px;
|
margin:3px 0px;
|
||||||
|
|
@ -237,4 +243,28 @@ div.tab-frame > input#info:checked ~ .info{
|
||||||
cursor:pointer;
|
cursor:pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* popup */
|
||||||
|
|
||||||
|
#popup{
|
||||||
|
position:fixed;
|
||||||
|
max-width:720px;
|
||||||
|
margin:auto auto;
|
||||||
|
top:20%;
|
||||||
|
left:5%;
|
||||||
|
right:5%;
|
||||||
|
max-height:60vh;
|
||||||
|
padding:0px 30px 30px 30px;
|
||||||
|
background:#112e;
|
||||||
|
z-index:1000;
|
||||||
|
overflow:hidden;
|
||||||
|
overflow-y:auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#popup .icon.close{
|
||||||
|
cursor:pointer;
|
||||||
|
width: 40px;
|
||||||
|
filter: invert(1);
|
||||||
|
position: absolute;
|
||||||
|
right: 20px;
|
||||||
|
top: 20px;
|
||||||
|
}
|
||||||
|
|
|
||||||
2
src/img/close.svg
Normal file
2
src/img/close.svg
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
<svg width="800px" height="800px" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="#000000"><line x1="16" y1="16" x2="48" y2="48"/><line x1="48" y1="16" x2="16" y2="48"/></svg>
|
||||||
|
After Width: | Height: | Size: 322 B |
|
|
@ -21,26 +21,3 @@ end}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
${footer}
|
${footer}
|
||||||
|
|
||||||
<!-- JanusXR portalroom, see https://janusxr.org for markup -->
|
|
||||||
<fireboxroom>
|
|
||||||
<assets>
|
|
||||||
<AssetShader id="xrforgeportal" src="${opts.baseurl}shader/xrforgeportal.shadertoy.frag" shadertype="shadertoy"></AssetShader>
|
|
||||||
{for i,v in pairs(items) do
|
|
||||||
print('\t<assetimage src="' .. v.thumbnail .. '" id="img' .. i .. '"/>')
|
|
||||||
end}
|
|
||||||
</assets>
|
|
||||||
<room pos="0 0 -4" skybox="true" showavatar="false" use_local_asset="room_plane">
|
|
||||||
<light js_id="dan9-janus:light/light_cone_angle=0-6" pos="0 3 2" collision_trigger="false" />
|
|
||||||
<text js_id="location" pos="0 6 -5" billboard="y" col="#BBFFFF" scale="10 10 1" text="${url}" rotation="-180 0 -180" emissive="0 0 0" roughness="0.3" metalness="0" />
|
|
||||||
{for i,v in pairs(items) do
|
|
||||||
local width = 3
|
|
||||||
local center = -((#items*width)/1.3) + (i*width)
|
|
||||||
print('\t\t<object js_id="portal_' .. i .. '" pos="' .. center .. ' 0 0" rotation="0 180 0">')
|
|
||||||
print('\t\t <link url="' .. v.url .. '" thumb_id="img' .. i .. '" title_col="0.4 0.37 0.4" pos="0 0 0.001" draw_glow="true" scale="3 3 0.1" />')
|
|
||||||
print('\t\t <link url="' .. v.url .. '" shader_id="xrforgeportal" pos="0 0 0.003" draw_glow="false" scale="3 3 0.1" />')
|
|
||||||
print('\t\t <text text="' .. v.title .. '" pos="0 3.4 0" col="1 1 1" font_scale="false" scale="0.2 0.2 0.2" />')
|
|
||||||
print('\t\t</object>')
|
|
||||||
end}
|
|
||||||
</room>
|
|
||||||
</fireboxroom>
|
|
||||||
|
|
|
||||||
|
|
@ -8,20 +8,26 @@ return {
|
||||||
local tile = require('page/partial/tile')
|
local tile = require('page/partial/tile')
|
||||||
local rootpath = "/" .. app.opts.plugin_diskscan_dirname
|
local rootpath = "/" .. app.opts.plugin_diskscan_dirname
|
||||||
local urlpath = GetPath()
|
local urlpath = GetPath()
|
||||||
|
res.items = {}
|
||||||
|
res.template = res.template or 'page/dir.html'
|
||||||
local isdir = urlpath:sub(-1) == "/"
|
local isdir = urlpath:sub(-1) == "/"
|
||||||
local nslashes = util.countChar('/',urlpath)
|
local nslashes = util.countChar('/',urlpath)
|
||||||
res.items = {}
|
|
||||||
local match = function(a,b) return util.stripPath(a) == util.stripPath(b) end
|
local match = function(a,b) return util.stripPath(a) == util.stripPath(b) end
|
||||||
if urlpath:match("^"..rootpath) and not urlpath:gsub(".*/",""):match(".") then
|
if urlpath == "/"
|
||||||
|
or urlpath:match("^"..rootpath)
|
||||||
|
and not urlpath:gsub(".*/",""):match("%.") then
|
||||||
|
|
||||||
local redirect = false
|
local redirect = false
|
||||||
if not app.cache.url[ urlpath ] then
|
if not app.cache.url[ urlpath ] then
|
||||||
|
local matchpath = urlpath
|
||||||
|
if urlpath == '/' then matchpath = rootpath end -- generate janusxr footer for root url
|
||||||
foreach( app.cache.items, function(k,v)
|
foreach( app.cache.items, function(k,v)
|
||||||
if v.parent then
|
if v.parent then
|
||||||
trace( "path = " .. util.stripPath(v.path) .. " " ..
|
trace( "path = " .. util.stripPath(v.path) .. " " ..
|
||||||
"parent = " .. util.stripPath(v.parent.path) .. " " ..
|
"parent = " .. util.stripPath(v.parent.path) .. " " ..
|
||||||
"urlpath = " .. util.stripPath(urlpath)
|
"matchpath = " .. util.stripPath(matchpath)
|
||||||
)
|
)
|
||||||
if match(v.path, urlpath) then -- show parent folder if any
|
if match(v.path, matchpath) then -- show parent folder if any
|
||||||
local parent = app.cache.items[ v.parent.id ]
|
local parent = app.cache.items[ v.parent.id ]
|
||||||
if v.portal then
|
if v.portal then
|
||||||
redirect = v.portal.url
|
redirect = v.portal.url
|
||||||
|
|
@ -33,19 +39,15 @@ return {
|
||||||
end
|
end
|
||||||
|
|
||||||
-- add items from dir
|
-- add items from dir
|
||||||
if match(v.parent.path, urlpath) then
|
if match(v.parent.path, matchpath) then
|
||||||
local parent = app.cache.items[ v.parent.id ]
|
local parent = app.cache.items[ v.parent.id ]
|
||||||
res.items[ v.id ] = v
|
res.items[ v.id ] = v
|
||||||
res.parent = parent
|
res.parent = parent
|
||||||
if v.thumbnail then
|
if v.thumbnail then
|
||||||
print_r(v.name .. " => " .. v.thumbnail)
|
print_r(v.name .. " => " .. v.thumbnail)
|
||||||
end
|
end
|
||||||
--if parent.parent then
|
|
||||||
-- res.items[ v.id ] = v
|
|
||||||
--else
|
|
||||||
-- res.items[ parent.id ] = parent
|
|
||||||
--end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
local data = util.merge({
|
local data = util.merge({
|
||||||
|
|
@ -70,10 +72,9 @@ return {
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
if data.item then
|
if data.item then
|
||||||
app.cache.url[ urlpath ] = util.templateFile( "page/item.html", data)
|
res.template = 'page/index.html'
|
||||||
else
|
|
||||||
app.cache.url[ urlpath ] = util.templateFile( "page/dir.html", data)
|
|
||||||
end
|
end
|
||||||
|
app.cache.url[ urlpath ] = util.templateFile( res.template, data)
|
||||||
end
|
end
|
||||||
if redirect then
|
if redirect then
|
||||||
print("REDIRECT! " .. redirect )
|
print("REDIRECT! " .. redirect )
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,9 @@
|
||||||
<a href="https://nlnet.nl">NLNET</a><br/>
|
<a href="https://nlnet.nl">NLNET</a><br/>
|
||||||
</td>
|
</td>
|
||||||
<td class="col3">
|
<td class="col3">
|
||||||
<a href="/experiences">Experiences</a><br/>
|
<a href="/">View from XR Device</a><br/>
|
||||||
|
<a href="/${opts.plugin_diskscan_dirname}">Content overview</a><br/>
|
||||||
<a href="/about">About</a><br/>
|
<a href="/about">About</a><br/>
|
||||||
<a href="/howto">Howto</a>
|
|
||||||
</td>
|
</td>
|
||||||
<td class="col3">
|
<td class="col3">
|
||||||
<a href="https://codeberg.org/coderofsalvation/xrforge">Sourcecode</a>
|
<a href="https://codeberg.org/coderofsalvation/xrforge">Sourcecode</a>
|
||||||
|
|
@ -31,9 +31,35 @@
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
<!-- JanusXR code
|
<!-- JanusXR portalroom, see https://janusxr.org for markup -->
|
||||||
|
<fireboxroom>
|
||||||
-->
|
<assets>
|
||||||
|
<assetshader id="xrforgeportal" src="${opts.baseurl}shader/xrforgeportal.shadertoy.frag" shadertype="shadertoy"></assetshader>
|
||||||
|
{if items and #items > 1 then
|
||||||
|
for i,v in pairs(items) do
|
||||||
|
print('\t<assetimage src="' .. v.thumbnail .. '" id="img' .. i .. '"/>')
|
||||||
|
end
|
||||||
|
end}
|
||||||
|
</assets>
|
||||||
|
<room pos="0 0 -4" skybox="true" showavatar="false" use_local_asset="room_plane">
|
||||||
|
<light js_id="dan9-janus:light/light_cone_angle=0-6" pos="0 3 2" collision_trigger="false" />
|
||||||
|
<text js_id="location" pos="0 6 -5" billboard="y" col="#BBFFFF" scale="10 10 1" text="${url}" rotation="-180 0 -180" emissive="0 0 0" roughness="0.3" metalness="0" />
|
||||||
|
{if item then
|
||||||
|
print('\t<object id="${url}"/>')
|
||||||
|
end}
|
||||||
|
{if items and #items > 1 then
|
||||||
|
for i,v in pairs(items) do
|
||||||
|
local width = 3.2
|
||||||
|
local center = -((#items*width)/1.3) + (i*width)
|
||||||
|
print('\t\t<object js_id="portal_' .. i .. '" pos="' .. center .. ' 0 0" rotation="0 180 0">')
|
||||||
|
print('\t\t <link url="' .. v.url .. '" thumb_id="img' .. i .. '" title_col="0.4 0.37 0.4" pos="0 0 0.001" draw_glow="true" scale="3 3 0.1" />')
|
||||||
|
print('\t\t <link url="' .. v.url .. '" shader_id="xrforgeportal" pos="0 0 0.003" draw_glow="false" scale="3 3 0.1" />')
|
||||||
|
print('\t\t <text text="' .. v.title .. '" pos="0 3.4 0" col="1 1 1" font_scale="false" scale="0.2 0.2 0.2" />')
|
||||||
|
print('\t\t</object>')
|
||||||
|
end
|
||||||
|
end}
|
||||||
|
</room>
|
||||||
|
</fireboxroom>
|
||||||
|
|
||||||
<!-- C-code
|
<!-- C-code
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,20 +2,32 @@ ${header}
|
||||||
|
|
||||||
<iframe border="0" frameborder="0" src="/janusweb/index.html#janus.url=${url}" id="viewer"></iframe>
|
<iframe border="0" frameborder="0" src="/janusweb/index.html#janus.url=${url}" id="viewer"></iframe>
|
||||||
|
|
||||||
|
{if item and item.parent and item.parent.info then
|
||||||
|
print([[
|
||||||
|
<div id="popup">
|
||||||
|
<img src="/img/close.svg" class="icon close" onclick="document.querySelector('#popup').style.display = 'none'"/>
|
||||||
|
<div class="spectrum span">
|
||||||
|
<div></div>
|
||||||
|
</div>
|
||||||
|
]] .. item.parent.info .. [[
|
||||||
|
</div>
|
||||||
|
]])
|
||||||
|
end}
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// minimal JS to sync parent/iframe URL-locations
|
//// minimal JS to sync parent/iframe URL-locations
|
||||||
const iframe = document.querySelector("#viewer")
|
//const iframe = document.querySelector("#viewer")
|
||||||
iframe.addEventListener('load', () => {
|
//iframe.addEventListener('load', () => {
|
||||||
const iframeWindow = iframe.contentWindow;
|
// const iframeWindow = iframe.contentWindow;
|
||||||
iframeWindow.addEventListener('hashchange', (event) => {
|
// iframeWindow.addEventListener('hashchange', (event) => {
|
||||||
const params = new URLSearchParams( String(iframeWindow.location.hash).replace(/^#/,'?') )
|
// const params = new URLSearchParams( String(iframeWindow.location.hash).replace(/^#/,'?') )
|
||||||
const url = params.get('janus.url')
|
// const url = params.get('janus.url')
|
||||||
window.history.pushState( url, iframe.contentDocument.title, url );
|
// window.history.pushState( url, iframe.contentDocument.title, url );
|
||||||
setTimeout( () => {
|
// setTimeout( () => {
|
||||||
document.title = iframe.contentDocument.title
|
// document.title = iframe.contentDocument.title
|
||||||
}, 1000 )
|
// }, 1000 )
|
||||||
});
|
// });
|
||||||
});
|
//});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ app.on('scan_experience_file', function(exp)
|
||||||
local patterns = util.split(app.opts.plugin_markdown_files," ")
|
local patterns = util.split(app.opts.plugin_markdown_files," ")
|
||||||
foreach( patterns, function(k,v)
|
foreach( patterns, function(k,v)
|
||||||
if exp.path:match(v) then
|
if exp.path:match(v) then
|
||||||
|
print("✅ └─ plugin/markdown: detected " .. v)
|
||||||
exp.parent.info = '<div id="info">' .. markdown( util.readfile(exp.path) ) .. '</div>'
|
exp.parent.info = '<div id="info">' .. markdown( util.readfile(exp.path) ) .. '</div>'
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
// Source: https://www.shadertoy.com/view/XtlczH
|
// Source: https://www.shadertoy.com/view/XtlczH
|
||||||
|
|
||||||
const float speed = 0.01;
|
const float speed = 0.01;
|
||||||
const float twistFrequency = .2;
|
const float twistFrequency = 4.2;
|
||||||
const float pi = 3.14;
|
const float pi = 3.14;
|
||||||
const float tau = pi * 2.0;
|
const float tau = pi * 2.0;
|
||||||
const vec4 bg = vec4(0);
|
const vec4 bg = vec4(0);
|
||||||
const float edgeWidth = 3.0;
|
const float edgeWidth = 3.0;
|
||||||
const float portalRadius = 9.0;
|
const float portalRadius = 4.0;
|
||||||
const float twistEdgeMag = 0.1;
|
const float twistEdgeMag = 1.1;
|
||||||
|
|
||||||
void mainImage( out vec4 fragColor, in vec2 fragCoord )
|
void mainImage( out vec4 fragColor, in vec2 fragCoord )
|
||||||
{
|
{
|
||||||
|
|
@ -37,5 +37,5 @@ void mainImage( out vec4 fragColor, in vec2 fragCoord )
|
||||||
|
|
||||||
vec4 portal = vec4((sin(v * 2.0) + 1.0) * 0.2, 0, (sin(v * 0.4 + 0.3 * pi) + 1.0) * 0.2 + 0.1, 1);
|
vec4 portal = vec4((sin(v * 2.0) + 1.0) * 0.2, 0, (sin(v * 0.4 + 0.3 * pi) + 1.0) * 0.2 + 0.1, 1);
|
||||||
portal.w = .9;
|
portal.w = .9;
|
||||||
fragColor = mix(bg, portal, smoothstep(portalRadius + edgeWidth + (sin(twist * twistEdgeMag) * 0.5 + 0.5), portalRadius, magnitude)) * vec4(1,1,1, (((sin(iTime*2.0)+1.0)/2.0) * 0.4) );
|
fragColor = mix(bg, portal, smoothstep(portalRadius + edgeWidth + (sin(twist * twistEdgeMag) * 0.5 + 0.5), portalRadius, magnitude)) * vec4(1,1,1, ((((sin(iTime*3.0)+1.0)/2.0) * 0.2) ) );
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue