improved xrfragment imports + initial commit for gazecontrol extension
This commit is contained in:
parent
4c72581493
commit
14500b44a2
15 changed files with 671 additions and 603 deletions
|
|
@ -1,16 +1,8 @@
|
|||
-- sync lovr/love positional cli arguments
|
||||
if arg[0] == nil and arg[1] ~= nil then arg[0] = arg[1] end
|
||||
|
||||
package.path = package.path .. ';' ..
|
||||
'?.lua;' ..
|
||||
'?/init.lua;' ..
|
||||
'lib/?.lua;'
|
||||
package.path = package.path .. ';' .. '?.lua;' .. 'lib/?.lua'
|
||||
|
||||
local runtime = require("runtime/detect")
|
||||
|
||||
package.path = package.path .. ';' ..
|
||||
arg[0] .. '/' .. runtime.path .. '/?.lua;' ..
|
||||
arg[0] .. '/' .. runtime.path .. '/?/init.lua;' ..
|
||||
arg[0] .. '/lib/?.lua'
|
||||
|
||||
require( runtime.path .. "/conf")
|
||||
|
|
|
|||
|
|
@ -16,12 +16,21 @@ return {
|
|||
obj.root = true
|
||||
api.ext.URI.current = obj.URL -- set current url
|
||||
end
|
||||
if type(obj.URI.target) == 'table' and obj.URI.target.obj ~= nil then
|
||||
local parent = obj.URI.target.obj
|
||||
-- TODO: the runtime/lovr/render/model code should calculate parent offset
|
||||
obj.x = parent.x
|
||||
obj.y = parent.y
|
||||
obj.z = parent.z
|
||||
obj.scale = parent.scale
|
||||
else
|
||||
obj.x = obj.x or 0
|
||||
obj.y = obj.y or 0
|
||||
obj.z = obj.z or 0
|
||||
obj.scale = obj.scale or 1
|
||||
end
|
||||
if obj.URL.protocol == 'file' then
|
||||
obj.model = api.graphics.newModel( obj.URL.string )
|
||||
obj.model = api.graphics.newModel( obj.URL.file )
|
||||
else
|
||||
obj.model = api.graphics.newModel( api.data.newBlob( obj.URLResponse.data) )
|
||||
end
|
||||
|
|
|
|||
35
xurfer/ext/gazecontrol/main.lua
Normal file
35
xurfer/ext/gazecontrol/main.lua
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
local api = ...
|
||||
local ecs = api.ecs
|
||||
|
||||
local gctl
|
||||
|
||||
gctl = {
|
||||
|
||||
name = "gctl",
|
||||
enabled = true,
|
||||
dtmax = 1,
|
||||
dt = 0,
|
||||
|
||||
init = function()
|
||||
lovr.mouse.setRelativeMode(true)
|
||||
|
||||
camera = {
|
||||
transform = lovr.math.newMat4(),
|
||||
position = vector(),
|
||||
movespeed = 10,
|
||||
pitch = 0,
|
||||
yaw = 0
|
||||
}
|
||||
end,
|
||||
|
||||
update = function(dt)
|
||||
end,
|
||||
|
||||
load = function() end,
|
||||
|
||||
draw = function(pass,dt)
|
||||
end,
|
||||
|
||||
}
|
||||
|
||||
return gctl
|
||||
|
|
@ -7,12 +7,18 @@ return {
|
|||
enabled = true,
|
||||
|
||||
onURI = function(obj)
|
||||
if obj.URL ~= nil and obj.URLResponse ~= nil and obj.URLResponse.ok then
|
||||
if obj.URL ~= nil and (obj.URLResponse ~= nil or obj.URL.protocol == 'file') then
|
||||
if obj.URL.protocol == 'file' and obj.URLResponse.ok == nil then
|
||||
local data = util.readfile(obj.URL.file)
|
||||
obj.URLResponse = { data = data, ok = (data ~= nil) }
|
||||
end
|
||||
if obj.URLResponse.ok then
|
||||
local res = obj.URLResponse
|
||||
-- JML heuristic
|
||||
if res.data:lower():match("<fireboxroom>") or res.data:lower():match("<room[ >]") then
|
||||
local JML0
|
||||
local JML
|
||||
print_r(api.parser)
|
||||
res.xml = api.parser.xml.newParser()
|
||||
local xmlstr = res.data
|
||||
-- cleanup JML0
|
||||
|
|
@ -20,6 +26,9 @@ return {
|
|||
JML0 = JML0:gsub("</[Rr]oom>.*","</room>")
|
||||
api.ext.janusxr.loadXML( JML0, res, obj )
|
||||
end
|
||||
else
|
||||
print("[janusxr] error: could not load " .. obj.URI.url .. ": " .. (obj.URLResponse.data or '') )
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
|
|
|
|||
|
|
@ -9,21 +9,11 @@ return {
|
|||
--api.ext.skeleton.extendECS()
|
||||
end,
|
||||
|
||||
update = function()
|
||||
local iui = api.iui
|
||||
local backend = api.backend
|
||||
local mainWindow = api.mainWindow
|
||||
end,
|
||||
update = function(dt) end,
|
||||
|
||||
load = function() end,
|
||||
|
||||
draw = function(pass)
|
||||
if api.iui.idiom == "vr" then
|
||||
|
||||
else
|
||||
|
||||
end
|
||||
end,
|
||||
draw = function(pass,dt) end,
|
||||
|
||||
-- ECS API docs: https://bakpakin.github.io/tiny-ecs/doc
|
||||
extendECS = function()
|
||||
|
|
|
|||
|
|
@ -1,386 +0,0 @@
|
|||
--
|
||||
-- json.lua
|
||||
--
|
||||
-- Copyright (c) 2020 rxi
|
||||
--
|
||||
-- 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 json = { _version = "0.1.2" }
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Encode
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local encode
|
||||
|
||||
local escape_char_map = {
|
||||
[ "\\" ] = "\\",
|
||||
[ "\"" ] = "\"",
|
||||
[ "\b" ] = "b",
|
||||
[ "\f" ] = "f",
|
||||
[ "\n" ] = "n",
|
||||
[ "\r" ] = "r",
|
||||
[ "\t" ] = "t",
|
||||
}
|
||||
|
||||
local escape_char_map_inv = { [ "/" ] = "/" }
|
||||
for k, v in pairs(escape_char_map) do
|
||||
escape_char_map_inv[v] = k
|
||||
end
|
||||
|
||||
|
||||
local function escape_char(c)
|
||||
return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte()))
|
||||
end
|
||||
|
||||
|
||||
local function encode_nil(val)
|
||||
return "null"
|
||||
end
|
||||
|
||||
|
||||
local function encode_table(val, stack)
|
||||
local res = {}
|
||||
stack = stack or {}
|
||||
|
||||
-- Circular reference?
|
||||
if stack[val] then error("circular reference") end
|
||||
|
||||
stack[val] = true
|
||||
|
||||
if rawget(val, 1) ~= nil or next(val) == nil then
|
||||
-- Treat as array -- check keys are valid and it is not sparse
|
||||
local n = 0
|
||||
for k in pairs(val) do
|
||||
if type(k) ~= "number" then
|
||||
error("invalid table: mixed or invalid key types")
|
||||
end
|
||||
n = n + 1
|
||||
end
|
||||
if n ~= #val then
|
||||
error("invalid table: sparse array")
|
||||
end
|
||||
-- Encode
|
||||
for i, v in ipairs(val) do
|
||||
table.insert(res, encode(v, stack))
|
||||
end
|
||||
stack[val] = nil
|
||||
return "[" .. table.concat(res, ",") .. "]"
|
||||
|
||||
else
|
||||
-- Treat as an object
|
||||
for k, v in pairs(val) do
|
||||
if type(k) ~= "string" then
|
||||
error("invalid table: mixed or invalid key types")
|
||||
end
|
||||
table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
|
||||
end
|
||||
stack[val] = nil
|
||||
return "{" .. table.concat(res, ",") .. "}"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function encode_string(val)
|
||||
return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
|
||||
end
|
||||
|
||||
|
||||
local function encode_number(val)
|
||||
-- Check for NaN, -inf and inf
|
||||
if val ~= val or val <= -math.huge or val >= math.huge then
|
||||
error("unexpected number value '" .. tostring(val) .. "'")
|
||||
end
|
||||
return string.format("%.14g", val)
|
||||
end
|
||||
|
||||
|
||||
local type_func_map = {
|
||||
[ "nil" ] = encode_nil,
|
||||
[ "table" ] = encode_table,
|
||||
[ "string" ] = encode_string,
|
||||
[ "number" ] = encode_number,
|
||||
[ "boolean" ] = tostring,
|
||||
}
|
||||
|
||||
|
||||
encode = function(val, stack)
|
||||
local t = type(val)
|
||||
local f = type_func_map[t]
|
||||
if f then
|
||||
return f(val, stack)
|
||||
end
|
||||
error("unexpected type '" .. t .. "'")
|
||||
end
|
||||
|
||||
|
||||
function json.encode(val)
|
||||
return ( encode(val) )
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Decode
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local parse
|
||||
|
||||
local function create_set(...)
|
||||
local res = {}
|
||||
for i = 1, select("#", ...) do
|
||||
res[ select(i, ...) ] = true
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
local space_chars = create_set(" ", "\t", "\r", "\n")
|
||||
local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
|
||||
local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
|
||||
local literals = create_set("true", "false", "null")
|
||||
|
||||
local literal_map = {
|
||||
[ "true" ] = true,
|
||||
[ "false" ] = false,
|
||||
[ "null" ] = nil,
|
||||
}
|
||||
|
||||
|
||||
local function next_char(str, idx, set, negate)
|
||||
for i = idx, #str do
|
||||
if set[str:sub(i, i)] ~= negate then
|
||||
return i
|
||||
end
|
||||
end
|
||||
return #str + 1
|
||||
end
|
||||
|
||||
|
||||
local function decode_error(str, idx, msg)
|
||||
local line_count = 1
|
||||
local col_count = 1
|
||||
for i = 1, idx - 1 do
|
||||
col_count = col_count + 1
|
||||
if str:sub(i, i) == "\n" then
|
||||
line_count = line_count + 1
|
||||
col_count = 1
|
||||
end
|
||||
end
|
||||
error( string.format("%s at line %d col %d", msg, line_count, col_count) )
|
||||
end
|
||||
|
||||
|
||||
local function codepoint_to_utf8(n)
|
||||
-- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
|
||||
local f = math.floor
|
||||
if n <= 0x7f then
|
||||
return string.char(n)
|
||||
elseif n <= 0x7ff then
|
||||
return string.char(f(n / 64) + 192, n % 64 + 128)
|
||||
elseif n <= 0xffff then
|
||||
return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||
elseif n <= 0x10ffff then
|
||||
return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
|
||||
f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||
end
|
||||
error( string.format("invalid unicode codepoint '%x'", n) )
|
||||
end
|
||||
|
||||
|
||||
local function parse_unicode_escape(s)
|
||||
local n1 = tonumber( s:sub(1, 4), 16 )
|
||||
local n2 = tonumber( s:sub(7, 10), 16 )
|
||||
-- Surrogate pair?
|
||||
if n2 then
|
||||
return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
|
||||
else
|
||||
return codepoint_to_utf8(n1)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function parse_string(str, i)
|
||||
local res = ""
|
||||
local j = i + 1
|
||||
local k = j
|
||||
|
||||
while j <= #str do
|
||||
local x = str:byte(j)
|
||||
|
||||
if x < 32 then
|
||||
decode_error(str, j, "control character in string")
|
||||
|
||||
elseif x == 92 then -- `\`: Escape
|
||||
res = res .. str:sub(k, j - 1)
|
||||
j = j + 1
|
||||
local c = str:sub(j, j)
|
||||
if c == "u" then
|
||||
local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1)
|
||||
or str:match("^%x%x%x%x", j + 1)
|
||||
or decode_error(str, j - 1, "invalid unicode escape in string")
|
||||
res = res .. parse_unicode_escape(hex)
|
||||
j = j + #hex
|
||||
else
|
||||
if not escape_chars[c] then
|
||||
decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string")
|
||||
end
|
||||
res = res .. escape_char_map_inv[c]
|
||||
end
|
||||
k = j + 1
|
||||
|
||||
elseif x == 34 then -- `"`: End of string
|
||||
res = res .. str:sub(k, j - 1)
|
||||
return res, j + 1
|
||||
end
|
||||
|
||||
j = j + 1
|
||||
end
|
||||
|
||||
decode_error(str, i, "expected closing quote for string")
|
||||
end
|
||||
|
||||
|
||||
local function parse_number(str, i)
|
||||
local x = next_char(str, i, delim_chars)
|
||||
local s = str:sub(i, x - 1)
|
||||
local n = tonumber(s)
|
||||
if not n then
|
||||
decode_error(str, i, "invalid number '" .. s .. "'")
|
||||
end
|
||||
return n, x
|
||||
end
|
||||
|
||||
|
||||
local function parse_literal(str, i)
|
||||
local x = next_char(str, i, delim_chars)
|
||||
local word = str:sub(i, x - 1)
|
||||
if not literals[word] then
|
||||
decode_error(str, i, "invalid literal '" .. word .. "'")
|
||||
end
|
||||
return literal_map[word], x
|
||||
end
|
||||
|
||||
|
||||
local function parse_array(str, i)
|
||||
local res = {}
|
||||
local n = 1
|
||||
i = i + 1
|
||||
while 1 do
|
||||
local x
|
||||
i = next_char(str, i, space_chars, true)
|
||||
-- Empty / end of array?
|
||||
if str:sub(i, i) == "]" then
|
||||
i = i + 1
|
||||
break
|
||||
end
|
||||
-- Read token
|
||||
x, i = parse(str, i)
|
||||
res[n] = x
|
||||
n = n + 1
|
||||
-- Next token
|
||||
i = next_char(str, i, space_chars, true)
|
||||
local chr = str:sub(i, i)
|
||||
i = i + 1
|
||||
if chr == "]" then break end
|
||||
if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
|
||||
end
|
||||
return res, i
|
||||
end
|
||||
|
||||
|
||||
local function parse_object(str, i)
|
||||
local res = {}
|
||||
i = i + 1
|
||||
while 1 do
|
||||
local key, val
|
||||
i = next_char(str, i, space_chars, true)
|
||||
-- Empty / end of object?
|
||||
if str:sub(i, i) == "}" then
|
||||
i = i + 1
|
||||
break
|
||||
end
|
||||
-- Read key
|
||||
if str:sub(i, i) ~= '"' then
|
||||
decode_error(str, i, "expected string for key")
|
||||
end
|
||||
key, i = parse(str, i)
|
||||
-- Read ':' delimiter
|
||||
i = next_char(str, i, space_chars, true)
|
||||
if str:sub(i, i) ~= ":" then
|
||||
decode_error(str, i, "expected ':' after key")
|
||||
end
|
||||
i = next_char(str, i + 1, space_chars, true)
|
||||
-- Read value
|
||||
val, i = parse(str, i)
|
||||
-- Set
|
||||
res[key] = val
|
||||
-- Next token
|
||||
i = next_char(str, i, space_chars, true)
|
||||
local chr = str:sub(i, i)
|
||||
i = i + 1
|
||||
if chr == "}" then break end
|
||||
if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
|
||||
end
|
||||
return res, i
|
||||
end
|
||||
|
||||
|
||||
local char_func_map = {
|
||||
[ '"' ] = parse_string,
|
||||
[ "0" ] = parse_number,
|
||||
[ "1" ] = parse_number,
|
||||
[ "2" ] = parse_number,
|
||||
[ "3" ] = parse_number,
|
||||
[ "4" ] = parse_number,
|
||||
[ "5" ] = parse_number,
|
||||
[ "6" ] = parse_number,
|
||||
[ "7" ] = parse_number,
|
||||
[ "8" ] = parse_number,
|
||||
[ "9" ] = parse_number,
|
||||
[ "-" ] = parse_number,
|
||||
[ "t" ] = parse_literal,
|
||||
[ "f" ] = parse_literal,
|
||||
[ "n" ] = parse_literal,
|
||||
[ "[" ] = parse_array,
|
||||
[ "{" ] = parse_object,
|
||||
}
|
||||
|
||||
|
||||
parse = function(str, idx)
|
||||
local chr = str:sub(idx, idx)
|
||||
local f = char_func_map[chr]
|
||||
if f then
|
||||
return f(str, idx)
|
||||
end
|
||||
decode_error(str, idx, "unexpected character '" .. chr .. "'")
|
||||
end
|
||||
|
||||
|
||||
function json.decode(str)
|
||||
if type(str) ~= "string" then
|
||||
error("expected argument of type string, got " .. type(str))
|
||||
end
|
||||
local res, idx = parse(str, next_char(str, 1, space_chars, true))
|
||||
idx = next_char(str, idx, space_chars, true)
|
||||
if idx <= #str then
|
||||
decode_error(str, idx, "trailing garbage")
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
return json
|
||||
|
|
@ -107,7 +107,7 @@ xrf.makeClickable = function( physicsWorld, cb)
|
|||
scale[3] * (d + 0.01) -- planes e.g.
|
||||
)
|
||||
print("making node " .. node['name'] .. " clickable: " .. node.extras.href )
|
||||
collider:setUserData( {name = node['name'], node = node, model = model, mesh = mesh, onclick = cb })
|
||||
collider:setUserData( {name = node['name'], node = node, model = model, mesh = mesh, onclick = cb, obj = obj })
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ xrfimpl = {
|
|||
init = function() end,
|
||||
|
||||
on3DFile = function(obj)
|
||||
local referer = obj.URI.url
|
||||
if lovr ~= nil then
|
||||
xrf.traverseNodesContaining('href', obj,
|
||||
xrf.makeClickable( api.ecs.worldPhysics,
|
||||
|
|
@ -18,10 +19,9 @@ xrfimpl = {
|
|||
local href = obj.node.extras.href
|
||||
trace("\n[xrf] href was clicked: " .. href)
|
||||
api.ext.exec("onClick", obj, collider)
|
||||
|
||||
local ok = xrf.level2.load( href, obj, api.ext.URI.current, xrfimpl.onLoad)
|
||||
or
|
||||
xrf.level4.import( href, obj, api.ext.URI.current, xrfimpl.onImport)
|
||||
xrf.level4.import( href, obj, referer, xrfimpl.onImport)
|
||||
|
||||
end
|
||||
)
|
||||
|
|
@ -51,10 +51,12 @@ xrfimpl = {
|
|||
local filter = function(o)
|
||||
return (o.URL ~= nil and o.URL.URN:gsub("#.*","") == absoluteHref:gsub("#.*",""))
|
||||
end
|
||||
local obj = api.world.get(filter)
|
||||
if obj then
|
||||
obj.URL = url.parse(absoluteHref) -- re-parse to detect media fragment change
|
||||
local newobj = api.world.get(filter)
|
||||
if newobj then
|
||||
if newobj.URL.audio ~= nil then
|
||||
newobj.URL = url.parse(absoluteHref) -- re-parse to detect media fragment change
|
||||
xrfimpl.onAudioFile(obj)
|
||||
end
|
||||
else
|
||||
api.ecs.add( api.world, { URI = { url = absoluteHref, method = 'GET', target = obj } })
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,166 +0,0 @@
|
|||
-- Copyright 2026 Leon van Kammen / coderofsalvation. All rights reserved.
|
||||
--
|
||||
-- Redistribution and use in source and binary forms, with or without modification, are
|
||||
-- permitted provided that the following conditions are met:
|
||||
--
|
||||
-- 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
-- conditions and the following disclaimer.
|
||||
--
|
||||
-- 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
-- of conditions and the following disclaimer in the documentation and/or other materials
|
||||
-- provided with the distribution.
|
||||
--
|
||||
-- THIS SOFTWARE IS PROVIDED BY AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
-- WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
-- FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR
|
||||
-- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
-- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
-- ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
-- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
--
|
||||
-- ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
--
|
||||
-- The views and conclusions contained in the software and documentation are those of the
|
||||
-- authors and should not be interpreted as representing official policies, either expressed
|
||||
-- or implied, of
|
||||
|
||||
-- below is a lua adaptation of Godot's URI.gd
|
||||
-- https://gist.github.com/coderofsalvation/b2b111a2631fbdc8e76d6cab3bea8f17
|
||||
|
||||
local url = {}
|
||||
|
||||
-- Helper function to split a string by a delimiter
|
||||
local function split(str, delimiter)
|
||||
local result = {}
|
||||
if delimiter == "" then return {str} end
|
||||
local pattern = string.format("([^%s]+)", delimiter)
|
||||
for match in string.gmatch(str, pattern) do
|
||||
table.insert(result, match)
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
-- Helper for guess_type (converts numbers/booleans from strings)
|
||||
local function guess_type(val)
|
||||
if tonumber(val) then return tonumber(val) end
|
||||
if val == "true" then return true end
|
||||
if val == "false" then return false end
|
||||
return val
|
||||
end
|
||||
|
||||
-- Helper to parse query parameters and fragments
|
||||
local function parseArgs(fragment)
|
||||
local ARG = {}
|
||||
if not fragment or fragment == "" then return ARG end
|
||||
|
||||
local items = split(fragment, "&")
|
||||
for _, item in ipairs(items) do
|
||||
local key_value = split(item, "=")
|
||||
if #key_value > 1 then
|
||||
local k = key_value[1]
|
||||
ARG[k] = guess_type(key_value[2]) -- xr fragment operators preserved
|
||||
ARG[k:gsub("[^%w]","") ] = guess_type(key_value[2]) -- alphanumeric only
|
||||
elseif #key_value == 1 then
|
||||
ARG[key_value[1]] = ""
|
||||
end
|
||||
end
|
||||
return ARG
|
||||
end
|
||||
|
||||
-- Main URL Parser function attached to the module object
|
||||
function url.parse(url_string)
|
||||
if type(url_string) == 'table' then return url_string end -- already parsed
|
||||
-- Setup initial URI table structure
|
||||
local URI = {
|
||||
domain = "",
|
||||
fragment = {},
|
||||
file = "",
|
||||
extension = "",
|
||||
string = url_string,
|
||||
protocol = "",
|
||||
path = "",
|
||||
query = {},
|
||||
hash = "",
|
||||
URN = "",
|
||||
isLocal = false
|
||||
}
|
||||
local remaining = url_string
|
||||
local protocol = string.match(remaining, "^(%w+://)") -- protocol
|
||||
if protocol then
|
||||
URI.protocol = protocol:gsub("://", "")
|
||||
remaining = remaining:sub(#protocol + 1)
|
||||
else
|
||||
URI.protocol = "file"
|
||||
end
|
||||
local hash = string.match(remaining, "(#.*)$") -- fragment
|
||||
if hash then
|
||||
URI.hash = hash
|
||||
remaining = remaining:sub(1, #remaining - #hash)
|
||||
end
|
||||
local query = string.match(remaining, "(%?[^#]+)$")
|
||||
if query then
|
||||
URI.query = query
|
||||
remaining = remaining:sub(1, #remaining - #query)
|
||||
end
|
||||
URI.path = remaining
|
||||
-- Process Path, Domain, and File
|
||||
if URI.path and URI.path ~= "" then
|
||||
local pathParts = split(URI.path, "/")
|
||||
|
||||
if #pathParts > 1 then
|
||||
local firstPart = pathParts[1]
|
||||
-- Check if the first part looks like a domain (contains '.' or ':')
|
||||
if string.find(firstPart, "%.") or string.find(firstPart, ":") then
|
||||
URI.domain = table.remove(pathParts, 1)
|
||||
end
|
||||
end
|
||||
-- Reconstruct path
|
||||
URI.path = table.concat(pathParts, "/")
|
||||
-- Extract file if present
|
||||
if #pathParts > 0 then
|
||||
local lastPart = pathParts[#pathParts]
|
||||
if string.find(lastPart, "%.") then
|
||||
URI.file = lastPart
|
||||
URI.extension = URI.file:match("%.([^%.]+)$"):upper()
|
||||
end
|
||||
end
|
||||
end
|
||||
if URI.hash and URI.hash ~= "" then
|
||||
URI.fragment = parseArgs(URI.hash:sub(2)) -- sub(2) skips the '#'
|
||||
else
|
||||
URI.fragment = {}
|
||||
end
|
||||
|
||||
if URI.query and type(URI.query) == "string" and URI.query ~= "" then
|
||||
URI.query = parseArgs(URI.query:sub(2)) -- sub(2) skips the '?'
|
||||
else
|
||||
URI.query = {}
|
||||
end
|
||||
if URI.domain ~= "" then
|
||||
URI.URN = URI.string:gsub("%?.*", "")
|
||||
else
|
||||
URI.URN = ""
|
||||
end
|
||||
URI.isLocal = (URI.domain == "")
|
||||
return URI
|
||||
end
|
||||
|
||||
url.getAbsolute = function(href,referer,keephash)
|
||||
local URL = url.parse(href)
|
||||
local URLref = url.parse(referer)
|
||||
if referer ~= nil and URL.protocol == 'file' and URLref.protocol ~= 'file' then
|
||||
URL.protocol = URLref.protocol
|
||||
URL.domain = URLref.domain
|
||||
URL.string = URLref.protocol .. '://' .. URLref.domain .. '/' .. URL.path .. URL.hash
|
||||
if keephash then
|
||||
URL.URN = URL.string
|
||||
else
|
||||
URL.URN = URL.string:gsub("#.*", "")
|
||||
end
|
||||
end
|
||||
return URL
|
||||
end
|
||||
|
||||
-- Return the module table so it can be assigned via require()
|
||||
return url
|
||||
|
|
@ -1 +0,0 @@
|
|||
../ext/xrfragments/json.lua
|
||||
386
xurfer/lib/json.lua
Normal file
386
xurfer/lib/json.lua
Normal file
|
|
@ -0,0 +1,386 @@
|
|||
--
|
||||
-- json.lua
|
||||
--
|
||||
-- Copyright (c) 2020 rxi
|
||||
--
|
||||
-- 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 json = { _version = "0.1.2" }
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Encode
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local encode
|
||||
|
||||
local escape_char_map = {
|
||||
[ "\\" ] = "\\",
|
||||
[ "\"" ] = "\"",
|
||||
[ "\b" ] = "b",
|
||||
[ "\f" ] = "f",
|
||||
[ "\n" ] = "n",
|
||||
[ "\r" ] = "r",
|
||||
[ "\t" ] = "t",
|
||||
}
|
||||
|
||||
local escape_char_map_inv = { [ "/" ] = "/" }
|
||||
for k, v in pairs(escape_char_map) do
|
||||
escape_char_map_inv[v] = k
|
||||
end
|
||||
|
||||
|
||||
local function escape_char(c)
|
||||
return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte()))
|
||||
end
|
||||
|
||||
|
||||
local function encode_nil(val)
|
||||
return "null"
|
||||
end
|
||||
|
||||
|
||||
local function encode_table(val, stack)
|
||||
local res = {}
|
||||
stack = stack or {}
|
||||
|
||||
-- Circular reference?
|
||||
if stack[val] then error("circular reference") end
|
||||
|
||||
stack[val] = true
|
||||
|
||||
if rawget(val, 1) ~= nil or next(val) == nil then
|
||||
-- Treat as array -- check keys are valid and it is not sparse
|
||||
local n = 0
|
||||
for k in pairs(val) do
|
||||
if type(k) ~= "number" then
|
||||
error("invalid table: mixed or invalid key types")
|
||||
end
|
||||
n = n + 1
|
||||
end
|
||||
if n ~= #val then
|
||||
error("invalid table: sparse array")
|
||||
end
|
||||
-- Encode
|
||||
for i, v in ipairs(val) do
|
||||
table.insert(res, encode(v, stack))
|
||||
end
|
||||
stack[val] = nil
|
||||
return "[" .. table.concat(res, ",") .. "]"
|
||||
|
||||
else
|
||||
-- Treat as an object
|
||||
for k, v in pairs(val) do
|
||||
if type(k) ~= "string" then
|
||||
error("invalid table: mixed or invalid key types")
|
||||
end
|
||||
table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
|
||||
end
|
||||
stack[val] = nil
|
||||
return "{" .. table.concat(res, ",") .. "}"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function encode_string(val)
|
||||
return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
|
||||
end
|
||||
|
||||
|
||||
local function encode_number(val)
|
||||
-- Check for NaN, -inf and inf
|
||||
if val ~= val or val <= -math.huge or val >= math.huge then
|
||||
error("unexpected number value '" .. tostring(val) .. "'")
|
||||
end
|
||||
return string.format("%.14g", val)
|
||||
end
|
||||
|
||||
|
||||
local type_func_map = {
|
||||
[ "nil" ] = encode_nil,
|
||||
[ "table" ] = encode_table,
|
||||
[ "string" ] = encode_string,
|
||||
[ "number" ] = encode_number,
|
||||
[ "boolean" ] = tostring,
|
||||
}
|
||||
|
||||
|
||||
encode = function(val, stack)
|
||||
local t = type(val)
|
||||
local f = type_func_map[t]
|
||||
if f then
|
||||
return f(val, stack)
|
||||
end
|
||||
error("unexpected type '" .. t .. "'")
|
||||
end
|
||||
|
||||
|
||||
function json.encode(val)
|
||||
return ( encode(val) )
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Decode
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local parse
|
||||
|
||||
local function create_set(...)
|
||||
local res = {}
|
||||
for i = 1, select("#", ...) do
|
||||
res[ select(i, ...) ] = true
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
local space_chars = create_set(" ", "\t", "\r", "\n")
|
||||
local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
|
||||
local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
|
||||
local literals = create_set("true", "false", "null")
|
||||
|
||||
local literal_map = {
|
||||
[ "true" ] = true,
|
||||
[ "false" ] = false,
|
||||
[ "null" ] = nil,
|
||||
}
|
||||
|
||||
|
||||
local function next_char(str, idx, set, negate)
|
||||
for i = idx, #str do
|
||||
if set[str:sub(i, i)] ~= negate then
|
||||
return i
|
||||
end
|
||||
end
|
||||
return #str + 1
|
||||
end
|
||||
|
||||
|
||||
local function decode_error(str, idx, msg)
|
||||
local line_count = 1
|
||||
local col_count = 1
|
||||
for i = 1, idx - 1 do
|
||||
col_count = col_count + 1
|
||||
if str:sub(i, i) == "\n" then
|
||||
line_count = line_count + 1
|
||||
col_count = 1
|
||||
end
|
||||
end
|
||||
error( string.format("%s at line %d col %d", msg, line_count, col_count) )
|
||||
end
|
||||
|
||||
|
||||
local function codepoint_to_utf8(n)
|
||||
-- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
|
||||
local f = math.floor
|
||||
if n <= 0x7f then
|
||||
return string.char(n)
|
||||
elseif n <= 0x7ff then
|
||||
return string.char(f(n / 64) + 192, n % 64 + 128)
|
||||
elseif n <= 0xffff then
|
||||
return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||
elseif n <= 0x10ffff then
|
||||
return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
|
||||
f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||
end
|
||||
error( string.format("invalid unicode codepoint '%x'", n) )
|
||||
end
|
||||
|
||||
|
||||
local function parse_unicode_escape(s)
|
||||
local n1 = tonumber( s:sub(1, 4), 16 )
|
||||
local n2 = tonumber( s:sub(7, 10), 16 )
|
||||
-- Surrogate pair?
|
||||
if n2 then
|
||||
return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
|
||||
else
|
||||
return codepoint_to_utf8(n1)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function parse_string(str, i)
|
||||
local res = ""
|
||||
local j = i + 1
|
||||
local k = j
|
||||
|
||||
while j <= #str do
|
||||
local x = str:byte(j)
|
||||
|
||||
if x < 32 then
|
||||
decode_error(str, j, "control character in string")
|
||||
|
||||
elseif x == 92 then -- `\`: Escape
|
||||
res = res .. str:sub(k, j - 1)
|
||||
j = j + 1
|
||||
local c = str:sub(j, j)
|
||||
if c == "u" then
|
||||
local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1)
|
||||
or str:match("^%x%x%x%x", j + 1)
|
||||
or decode_error(str, j - 1, "invalid unicode escape in string")
|
||||
res = res .. parse_unicode_escape(hex)
|
||||
j = j + #hex
|
||||
else
|
||||
if not escape_chars[c] then
|
||||
decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string")
|
||||
end
|
||||
res = res .. escape_char_map_inv[c]
|
||||
end
|
||||
k = j + 1
|
||||
|
||||
elseif x == 34 then -- `"`: End of string
|
||||
res = res .. str:sub(k, j - 1)
|
||||
return res, j + 1
|
||||
end
|
||||
|
||||
j = j + 1
|
||||
end
|
||||
|
||||
decode_error(str, i, "expected closing quote for string")
|
||||
end
|
||||
|
||||
|
||||
local function parse_number(str, i)
|
||||
local x = next_char(str, i, delim_chars)
|
||||
local s = str:sub(i, x - 1)
|
||||
local n = tonumber(s)
|
||||
if not n then
|
||||
decode_error(str, i, "invalid number '" .. s .. "'")
|
||||
end
|
||||
return n, x
|
||||
end
|
||||
|
||||
|
||||
local function parse_literal(str, i)
|
||||
local x = next_char(str, i, delim_chars)
|
||||
local word = str:sub(i, x - 1)
|
||||
if not literals[word] then
|
||||
decode_error(str, i, "invalid literal '" .. word .. "'")
|
||||
end
|
||||
return literal_map[word], x
|
||||
end
|
||||
|
||||
|
||||
local function parse_array(str, i)
|
||||
local res = {}
|
||||
local n = 1
|
||||
i = i + 1
|
||||
while 1 do
|
||||
local x
|
||||
i = next_char(str, i, space_chars, true)
|
||||
-- Empty / end of array?
|
||||
if str:sub(i, i) == "]" then
|
||||
i = i + 1
|
||||
break
|
||||
end
|
||||
-- Read token
|
||||
x, i = parse(str, i)
|
||||
res[n] = x
|
||||
n = n + 1
|
||||
-- Next token
|
||||
i = next_char(str, i, space_chars, true)
|
||||
local chr = str:sub(i, i)
|
||||
i = i + 1
|
||||
if chr == "]" then break end
|
||||
if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
|
||||
end
|
||||
return res, i
|
||||
end
|
||||
|
||||
|
||||
local function parse_object(str, i)
|
||||
local res = {}
|
||||
i = i + 1
|
||||
while 1 do
|
||||
local key, val
|
||||
i = next_char(str, i, space_chars, true)
|
||||
-- Empty / end of object?
|
||||
if str:sub(i, i) == "}" then
|
||||
i = i + 1
|
||||
break
|
||||
end
|
||||
-- Read key
|
||||
if str:sub(i, i) ~= '"' then
|
||||
decode_error(str, i, "expected string for key")
|
||||
end
|
||||
key, i = parse(str, i)
|
||||
-- Read ':' delimiter
|
||||
i = next_char(str, i, space_chars, true)
|
||||
if str:sub(i, i) ~= ":" then
|
||||
decode_error(str, i, "expected ':' after key")
|
||||
end
|
||||
i = next_char(str, i + 1, space_chars, true)
|
||||
-- Read value
|
||||
val, i = parse(str, i)
|
||||
-- Set
|
||||
res[key] = val
|
||||
-- Next token
|
||||
i = next_char(str, i, space_chars, true)
|
||||
local chr = str:sub(i, i)
|
||||
i = i + 1
|
||||
if chr == "}" then break end
|
||||
if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
|
||||
end
|
||||
return res, i
|
||||
end
|
||||
|
||||
|
||||
local char_func_map = {
|
||||
[ '"' ] = parse_string,
|
||||
[ "0" ] = parse_number,
|
||||
[ "1" ] = parse_number,
|
||||
[ "2" ] = parse_number,
|
||||
[ "3" ] = parse_number,
|
||||
[ "4" ] = parse_number,
|
||||
[ "5" ] = parse_number,
|
||||
[ "6" ] = parse_number,
|
||||
[ "7" ] = parse_number,
|
||||
[ "8" ] = parse_number,
|
||||
[ "9" ] = parse_number,
|
||||
[ "-" ] = parse_number,
|
||||
[ "t" ] = parse_literal,
|
||||
[ "f" ] = parse_literal,
|
||||
[ "n" ] = parse_literal,
|
||||
[ "[" ] = parse_array,
|
||||
[ "{" ] = parse_object,
|
||||
}
|
||||
|
||||
|
||||
parse = function(str, idx)
|
||||
local chr = str:sub(idx, idx)
|
||||
local f = char_func_map[chr]
|
||||
if f then
|
||||
return f(str, idx)
|
||||
end
|
||||
decode_error(str, idx, "unexpected character '" .. chr .. "'")
|
||||
end
|
||||
|
||||
|
||||
function json.decode(str)
|
||||
if type(str) ~= "string" then
|
||||
error("expected argument of type string, got " .. type(str))
|
||||
end
|
||||
local res, idx = parse(str, next_char(str, 1, space_chars, true))
|
||||
idx = next_char(str, idx, space_chars, true)
|
||||
if idx <= #str then
|
||||
decode_error(str, idx, "trailing garbage")
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
return json
|
||||
|
|
@ -695,7 +695,9 @@ function tiny_manageEntities(world)
|
|||
system.modified = true
|
||||
local tmpEntity = ses[#ses]
|
||||
ses[index] = tmpEntity
|
||||
if tmpEntity ~= nil then
|
||||
seis[tmpEntity] = index
|
||||
end
|
||||
seis[entity] = nil
|
||||
ses[#ses] = nil
|
||||
local onRemove = system.onRemove
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
../ext/xrfragments/url.lua
|
||||
167
xurfer/lib/url.lua
Normal file
167
xurfer/lib/url.lua
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
-- Copyright 2026 Leon van Kammen / coderofsalvation. All rights reserved.
|
||||
--
|
||||
-- Redistribution and use in source and binary forms, with or without modification, are
|
||||
-- permitted provided that the following conditions are met:
|
||||
--
|
||||
-- 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
-- conditions and the following disclaimer.
|
||||
--
|
||||
-- 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
-- of conditions and the following disclaimer in the documentation and/or other materials
|
||||
-- provided with the distribution.
|
||||
--
|
||||
-- THIS SOFTWARE IS PROVIDED BY AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
-- WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
-- FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OR
|
||||
-- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
-- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
-- SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
-- ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
-- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
--
|
||||
-- ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
--
|
||||
-- The views and conclusions contained in the software and documentation are those of the
|
||||
-- authors and should not be interpreted as representing official policies, either expressed
|
||||
-- or implied, of
|
||||
|
||||
-- below is a lua adaptation of Godot's URI.gd
|
||||
-- https://gist.github.com/coderofsalvation/b2b111a2631fbdc8e76d6cab3bea8f17
|
||||
|
||||
local url = {}
|
||||
|
||||
-- Helper function to split a string by a delimiter
|
||||
local function split(str, delimiter)
|
||||
local result = {}
|
||||
if delimiter == "" then return {str} end
|
||||
local pattern = string.format("([^%s]+)", delimiter)
|
||||
for match in string.gmatch(str, pattern) do
|
||||
table.insert(result, match)
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
-- Helper for guess_type (converts numbers/booleans from strings)
|
||||
local function guess_type(val)
|
||||
if tonumber(val) then return tonumber(val) end
|
||||
if val == "true" then return true end
|
||||
if val == "false" then return false end
|
||||
return val
|
||||
end
|
||||
|
||||
-- Helper to parse query parameters and fragments
|
||||
local function parseArgs(fragment)
|
||||
local ARG = {}
|
||||
if not fragment or fragment == "" then return ARG end
|
||||
|
||||
local items = split(fragment, "&")
|
||||
for _, item in ipairs(items) do
|
||||
local key_value = split(item, "=")
|
||||
if #key_value > 1 then
|
||||
local k = key_value[1]
|
||||
ARG[k] = guess_type(key_value[2]) -- xr fragment operators preserved
|
||||
ARG[k:gsub("[^%w]","") ] = guess_type(key_value[2]) -- alphanumeric only
|
||||
elseif #key_value == 1 then
|
||||
ARG[key_value[1]] = ""
|
||||
end
|
||||
end
|
||||
return ARG
|
||||
end
|
||||
|
||||
-- Main URL Parser function attached to the module object
|
||||
function url.parse(url_string)
|
||||
if type(url_string) == 'table' then return url_string end -- already parsed
|
||||
-- Setup initial URI table structure
|
||||
local URI = {
|
||||
domain = "",
|
||||
fragment = {},
|
||||
file = "",
|
||||
extension = "",
|
||||
string = url_string,
|
||||
protocol = "",
|
||||
path = "",
|
||||
query = {},
|
||||
hash = "",
|
||||
URN = "",
|
||||
isLocal = false
|
||||
}
|
||||
local remaining = url_string
|
||||
local protocol = string.match(remaining, "^(%w+://)") -- protocol
|
||||
if protocol then
|
||||
URI.protocol = protocol:gsub("://", "")
|
||||
remaining = remaining:sub(#protocol + 1)
|
||||
else
|
||||
URI.protocol = "file"
|
||||
end
|
||||
local hash = string.match(remaining, "(#.*)$") -- fragment
|
||||
if hash then
|
||||
URI.hash = hash
|
||||
remaining = remaining:sub(1, #remaining - #hash)
|
||||
end
|
||||
local query = string.match(remaining, "(%?[^#]+)$")
|
||||
if query then
|
||||
URI.query = query
|
||||
remaining = remaining:sub(1, #remaining - #query)
|
||||
end
|
||||
URI.path = remaining
|
||||
-- Process Path, Domain, and File
|
||||
if URI.path and URI.path ~= "" then
|
||||
local pathParts = split(URI.path, "/")
|
||||
|
||||
if #pathParts > 1 then
|
||||
local firstPart = pathParts[1]
|
||||
-- Check if the first part looks like a domain (contains '.' or ':')
|
||||
if string.find(firstPart, "%.") or string.find(firstPart, ":") then
|
||||
URI.domain = table.remove(pathParts, 1)
|
||||
end
|
||||
end
|
||||
-- Reconstruct path
|
||||
URI.path = table.concat(pathParts, "/")
|
||||
-- Extract file if present
|
||||
if #pathParts > 0 then
|
||||
local lastPart = pathParts[#pathParts]
|
||||
if string.find(lastPart, "%.") then
|
||||
URI.file = lastPart
|
||||
URI.extension = URI.file:match("%.([^%.]+)$"):upper()
|
||||
end
|
||||
end
|
||||
end
|
||||
if URI.hash and URI.hash ~= "" then
|
||||
URI.fragment = parseArgs(URI.hash:sub(2)) -- sub(2) skips the '#'
|
||||
else
|
||||
URI.fragment = {}
|
||||
end
|
||||
|
||||
if URI.query and type(URI.query) == "string" and URI.query ~= "" then
|
||||
URI.query = parseArgs(URI.query:sub(2)) -- sub(2) skips the '?'
|
||||
else
|
||||
URI.query = {}
|
||||
end
|
||||
if URI.domain ~= "" then
|
||||
URI.URN = URI.string:gsub("%?.*", "")
|
||||
else
|
||||
URI.URN = ""
|
||||
end
|
||||
URI.isLocal = (URI.domain == "")
|
||||
return URI
|
||||
end
|
||||
|
||||
url.getAbsolute = function(href,referer,keephash)
|
||||
local URL = url.parse(href)
|
||||
local URLref = url.parse(referer)
|
||||
if referer ~= nil and URL.protocol == 'file' and URLref.protocol ~= 'file' then
|
||||
URL.protocol = URLref.protocol or ''
|
||||
URL.domain = URLref.domain or ''
|
||||
URL.string = URL.protocol .. '://' .. URL.domain .. '/' .. URL.path .. URL.hash
|
||||
if keephash then
|
||||
URL.URN = URL.string
|
||||
else
|
||||
URL.URN = URL.string:gsub("#.*", "")
|
||||
end
|
||||
end
|
||||
URL.referer = referer
|
||||
return URL
|
||||
end
|
||||
|
||||
-- Return the module table so it can be assigned via require()
|
||||
return url
|
||||
|
|
@ -167,3 +167,8 @@ function newNode(name)
|
|||
return node
|
||||
end
|
||||
|
||||
|
||||
return {
|
||||
newNode = newNode,
|
||||
newParser = newParser
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,22 @@
|
|||
local runtime = require("runtime/detect")
|
||||
-- sync lovr/love positional cli arguments
|
||||
if arg[0] == nil and arg[1] ~= nil then arg[0] = arg[1] end
|
||||
|
||||
local runtime = require("runtime/detect")
|
||||
|
||||
-- setup path
|
||||
print("!!!!!!!")
|
||||
package.path = package.path .. ';' ..
|
||||
'./runtime/lovr/?.lua;' ..
|
||||
'./runtime/lovr/?/init.lua;' ..
|
||||
'./lib/?.lua;' ..
|
||||
'./?.lua'
|
||||
--arg[0] .. 'runtime/lovr/?.lua;' ..
|
||||
--arg[0] .. 'runtime/lovr/?/init.lua;' ..
|
||||
--arg[0] .. 'lib/?.lua;' ..
|
||||
|
||||
local util = require("util")
|
||||
local launch = require "launch"
|
||||
local launch = require "runtime/lovr/launch"
|
||||
|
||||
local function isVR()
|
||||
return launch.mode == "vr" or util.match(arg,"--desktop") == false
|
||||
|
|
|
|||
|
|
@ -1,5 +1,13 @@
|
|||
local util = { ext = {} }
|
||||
|
||||
util.readfile = function(filename, mode)
|
||||
local file, err = io.open(filename, mode or "r")
|
||||
if not file then return print("Error opening file:", err) end
|
||||
local content = file:read("*a")
|
||||
file:close()
|
||||
return content
|
||||
end
|
||||
|
||||
util.loaddir = function( path, api, target)
|
||||
local dir = api.filesystem.getDirectoryItems(path)
|
||||
foreach( dir, function(k,v)
|
||||
|
|
@ -153,8 +161,8 @@ end
|
|||
-- entity.commit() -- this recalculates the filtercache of systems
|
||||
util.commit = function(world, obj, api)
|
||||
return function(extCb)
|
||||
for j = 1, #world.entities do
|
||||
local system = world.systems[j]
|
||||
for i = 1, #world.systems do
|
||||
local system = world.systems[i]
|
||||
system.entities = {}
|
||||
for j = 1, #world.entities do
|
||||
local entity = world.entities[j]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue