added baseURLify() to streamline server vs staticwebgen links
This commit is contained in:
parent
c4d5060e32
commit
6bf8b40d1c
17 changed files with 176 additions and 70 deletions
56
README.md
56
README.md
|
|
@ -8,8 +8,12 @@ An open, seamless XR web built from simple URLs and directories, without gatekee
|
||||||
<img src="screenshot.jpg"/>
|
<img src="screenshot.jpg"/>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
XRForge transforms standard local or remote file directories into interconnected 3D spaces.
|
* turn everyday directories into 3D portals.
|
||||||
<br>Users can seamlessly "surf" the spatial web, turning everyday directories into 3D portals.
|
* seamlessly "surf" interconnected spatial webs on XR headsets
|
||||||
|
* transforms local/remote filesystems into interconnected 3D spaces.
|
||||||
|
* 3D (hyperlink) file browser for AR/VR
|
||||||
|
* automatic portal system, recursive symlinks supported
|
||||||
|
* infinite horizontally scaling ecosystems thanks to URLs (not databases)
|
||||||
|
|
||||||
```
|
```
|
||||||
┌───────────────────────────────────┐ ┌──────────────────────────────────────────┐
|
┌───────────────────────────────────┐ ┌──────────────────────────────────────────┐
|
||||||
|
|
@ -56,7 +60,7 @@ Usage: ./xrforge.com <cmd> [opts]
|
||||||
Extract and run example 3D content in your browser:
|
Extract and run example 3D content in your browser:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ unzip xrforge.com myverse/* # extract example 3D verse
|
$ unzip xrforge.com myverse/* # extract example 3D verse files
|
||||||
$ ./xrforge.com server # run as server
|
$ ./xrforge.com server # run as server
|
||||||
opening your browser at https://localhost:8080
|
opening your browser at https://localhost:8080
|
||||||
```
|
```
|
||||||
|
|
@ -149,7 +153,7 @@ page css
|
||||||
|
|
||||||
Optionally you can re-distribute `xrforge.com` with your modifications by running `zip xrforge.com page`
|
Optionally you can re-distribute `xrforge.com` with your modifications by running `zip xrforge.com page`
|
||||||
|
|
||||||
## Distribute your own XRForge
|
## Distribute: your own executable
|
||||||
|
|
||||||
You can preload the `xrforge.com` binary with your own experiences <b>as a single-executable-file</b>:
|
You can preload the `xrforge.com` binary with your own experiences <b>as a single-executable-file</b>:
|
||||||
|
|
||||||
|
|
@ -160,18 +164,21 @@ $ cp xrforge.com myproject.com
|
||||||
|
|
||||||
> Profit!
|
> Profit!
|
||||||
|
|
||||||
## Plugins
|
## Distribute: static website
|
||||||
|
|
||||||
xrforge is basically plugins working together.<br>
|
Generate a static website:
|
||||||
You can create your own plugins by running:
|
|
||||||
|
|
||||||
```bash
|
```
|
||||||
$ ./xrforge.com createplugin > xrforge.myplugin.lua
|
$ ./xrforge.com generate baseurl=/mysubfolder/
|
||||||
|
✅ finished generating 'www' folder
|
||||||
```
|
```
|
||||||
|
|
||||||
Look in the [src/plugin](src/plugin) directory for examples
|
A convenient way is forking [this github repo](https://github.com/coderofsalvation/xrforge-website-example)
|
||||||
|
to automatically obtain a xrforge-generated github page.
|
||||||
|
|
||||||
## Run as server
|
> NOTE: passing extra flags (run `xrforge.com help` for all flags) is important when running the website in a subfolder (`baseurl=...` e.g.)
|
||||||
|
|
||||||
|
## Distribute: selfhosting on a server
|
||||||
|
|
||||||
<b>BAREMETAL:</b>
|
<b>BAREMETAL:</b>
|
||||||
|
|
||||||
|
|
@ -188,16 +195,17 @@ $ docker run -d -p 8080:8080 -w /app -v $(pwd):/app busybox /app/xrforge.com ser
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Plugins
|
||||||
|
|
||||||
## Scan manyfold
|
xrforge is basically plugins working together.<br>
|
||||||
|
You can create your own plugins by running:
|
||||||
|
|
||||||
-------------
|
```bash
|
||||||
```
|
$ ./xrforge.com createplugin > xrforge.myplugin.lua
|
||||||
manual scan (via cli?)
|
|
||||||
curl -L -X 'GET' 'http://xrforge.isvery.ninja/models?page=1&order=name' -H 'accept: application/vnd.manyfold.v0+json'
|
|
||||||
curl -L -X 'GET' 'http://xrforge.isvery.ninja/models?page=1&order=recent' -H 'accept: application/vnd.manyfold.v0+json'
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Look in the [src/plugin](src/plugin) directory for examples
|
||||||
|
|
||||||
## Developing
|
## Developing
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
@ -216,6 +224,8 @@ $ DEBUG=1 result/bin/xrforge.com
|
||||||
|
|
||||||
Use `ARCH=x86_64 build` to enforce x86_64 output (instead of a cosmopolitan binary)
|
Use `ARCH=x86_64 build` to enforce x86_64 output (instead of a cosmopolitan binary)
|
||||||
|
|
||||||
|
> **NOTE**: this is a **static webgenerator-first**-and-server-**last** project, hence it is restricted to [GET requests](src/middleware/onlyGET.lua) (unless authenticated as admin). Going beyond this paradigm is considered unsafe and out of scope.
|
||||||
|
|
||||||
## Permacomputing Spirit
|
## Permacomputing Spirit
|
||||||
|
|
||||||
* developed on [Dillo](https://dillo-browser.github.io/) as lowest common denominator / compatibility
|
* developed on [Dillo](https://dillo-browser.github.io/) as lowest common denominator / compatibility
|
||||||
|
|
@ -223,7 +233,7 @@ Use `ARCH=x86_64 build` to enforce x86_64 output (instead of a cosmopolitan bina
|
||||||
* gracefully degrades to admin/filebrowser for >noscript>/potato-browsers
|
* gracefully degrades to admin/filebrowser for >noscript>/potato-browsers
|
||||||
* server is using lua-scripting via cosmopolitan libc's [redbean](https://redbean.dev)
|
* server is using lua-scripting via cosmopolitan libc's [redbean](https://redbean.dev)
|
||||||
|
|
||||||
> So yes, the codebase/html/css is intentionally 90-ish: that's **exactly why it works** on potato-devices AND modern devices.
|
> So yes, the codebase/html/css is intentionally 90-ish: that's **exactly why it gracefully degrades** on potato-devices.
|
||||||
|
|
||||||
## Credits / Made with
|
## Credits / Made with
|
||||||
|
|
||||||
|
|
@ -243,6 +253,16 @@ Use `ARCH=x86_64 build` to enforce x86_64 output (instead of a cosmopolitan bina
|
||||||
|
|
||||||
NOTES:
|
NOTES:
|
||||||
|
|
||||||
|
## Scan manyfold
|
||||||
|
|
||||||
|
-------------
|
||||||
|
```
|
||||||
|
manual scan (via cli?)
|
||||||
|
curl -L -X 'GET' 'http://xrforge.isvery.ninja/models?page=1&order=name' -H 'accept: application/vnd.manyfold.v0+json'
|
||||||
|
curl -L -X 'GET' 'http://xrforge.isvery.ninja/models?page=1&order=recent' -H 'accept: application/vnd.manyfold.v0+json'
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
/admin.lua <--- define other instances..for CORS allow / mount http
|
/admin.lua <--- define other instances..for CORS allow / mount http
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,7 @@ 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.use( require('middleware/onlyGET')({exclude={'^/admin'}}) ) -- we are staticwebgen-first server-last
|
||||||
app.use( app.plugin.global.middleware.homepage_2D_or_3D )
|
app.use( app.plugin.global.middleware.homepage_2D_or_3D )
|
||||||
app.url['^/admin[/]?$'] = '/page/admin/index.lua'
|
app.url['^/admin[/]?$'] = '/page/admin/index.lua'
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ return function(app)
|
||||||
local config = { -- default
|
local config = { -- default
|
||||||
name = "XRForge",
|
name = "XRForge",
|
||||||
baseurl="/",
|
baseurl="/",
|
||||||
|
host="localhost",
|
||||||
admin_title = "admin panel",
|
admin_title = "admin panel",
|
||||||
admin_subtitle = "Manage your XR experiences here",
|
admin_subtitle = "Manage your XR experiences here",
|
||||||
admin_user = "admin",
|
admin_user = "admin",
|
||||||
|
|
|
||||||
|
|
@ -87,12 +87,23 @@ sb = {
|
||||||
|
|
||||||
tpl = function(s, __data)
|
tpl = function(s, __data)
|
||||||
local __env = {}
|
local __env = {}
|
||||||
|
local fallback = function(key)
|
||||||
|
if os.getenv('DEBUG') then
|
||||||
|
print("⚠️ template '" .. key .. "' does not exist")
|
||||||
|
end
|
||||||
|
return setmetatable({}, {
|
||||||
|
__tostring = function() return "" end,
|
||||||
|
__concat = function(a, b) return tostring(a) .. tostring(b) end,
|
||||||
|
__call = function(...) return "" end
|
||||||
|
})
|
||||||
|
end
|
||||||
setmetatable(__env, {
|
setmetatable(__env, {
|
||||||
__index = function(t, key) return __data[key] or _G[key] end,
|
__index = function(t, key) return __data[key] or _G[key] or fallback(key) end,
|
||||||
__newindex = function(t, key, value) __data[key] = value end
|
__newindex = function(t, key, value) __data[key] = value end
|
||||||
})
|
})
|
||||||
-- lets evaluate inline lua linearly
|
-- lets evaluate inline lua linearly
|
||||||
local luastr = 'local str=""\n print = function(s) str = str .. s .. "\\n" end\n'
|
local luastr = 'local str=""\n print = function(s) str = str .. s .. "\\n" end\n'
|
||||||
|
luastr = luastr .. 'defined = function(v) return tostring(v or "") ~= "" end\n'
|
||||||
local islua = false
|
local islua = false
|
||||||
for str in string.gmatch(s, "([^\n]+)") do
|
for str in string.gmatch(s, "([^\n]+)") do
|
||||||
if str:match("{if ") or str:match("{for ") then
|
if str:match("{if ") or str:match("{for ") then
|
||||||
|
|
@ -196,7 +207,10 @@ sb = {
|
||||||
local res={
|
local res={
|
||||||
_status=nil,
|
_status=nil,
|
||||||
_header={},
|
_header={},
|
||||||
_body=""
|
_body="",
|
||||||
|
send = function(req,res,next)
|
||||||
|
sb.response()(req,res,next)
|
||||||
|
end
|
||||||
}
|
}
|
||||||
local params = internal and {} or GetParams()
|
local params = internal and {} or GetParams()
|
||||||
if params ~= nil then
|
if params ~= nil then
|
||||||
|
|
@ -251,7 +265,9 @@ sb = {
|
||||||
if type(res._body) == "string" then
|
if type(res._body) == "string" then
|
||||||
Write( res._body )
|
Write( res._body )
|
||||||
else print("[ERROR] res.body is not a string (HINT: use json middleware)") end
|
else print("[ERROR] res.body is not a string (HINT: use json middleware)") end
|
||||||
else next() end
|
else
|
||||||
|
if next then next() end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
|
@ -311,6 +327,7 @@ sb = {
|
||||||
return function(data)
|
return function(data)
|
||||||
local app = {}
|
local app = {}
|
||||||
setmetatable(app,sb)
|
setmetatable(app,sb)
|
||||||
|
sb.arg = arg
|
||||||
sb.bin = arg[-1]:gsub(".*/","")
|
sb.bin = arg[-1]:gsub(".*/","")
|
||||||
sb.bindir = arg[-1]:gsub("/[a-zA-Z0-9-_ ]+.com$","/")
|
sb.bindir = arg[-1]:gsub("/[a-zA-Z0-9-_ ]+.com$","/")
|
||||||
sb.app = app
|
sb.app = app
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,12 @@ util.templateFile = function(file,data)
|
||||||
return util.template( LoadAsset(file), data)
|
return util.template( LoadAsset(file), data)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
util.baseURLify = function(url)
|
||||||
|
if url:match("^" .. app.opts.baseurl) then return url end -- only do once
|
||||||
|
if url:match("://") then return url end
|
||||||
|
return url:gsub("^[/]?", app.opts.baseurl)
|
||||||
|
end
|
||||||
|
|
||||||
util.getURL = function(url,opts)
|
util.getURL = function(url,opts)
|
||||||
opts = opts or {}
|
opts = opts or {}
|
||||||
local port = ''
|
local port = ''
|
||||||
|
|
@ -83,8 +89,9 @@ util.getURL = function(url,opts)
|
||||||
return 'http://localhost:' .. app.port .. url
|
return 'http://localhost:' .. app.port .. url
|
||||||
else
|
else
|
||||||
port = ':' .. GetPort()
|
port = ':' .. GetPort()
|
||||||
if port == 80 then port = '' end
|
if port == ':80' then port = '' end
|
||||||
return GetScheme() .. '://' .. GetHost() .. port .. (url or GetPath())
|
if arg[1] == 'generate' and port == ':8080' then port = '' end
|
||||||
|
return GetScheme() .. '://' .. (app.opts.host or GetHost()) .. port .. (url or GetPath())
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -92,8 +99,8 @@ util.template = function(content,data)
|
||||||
local mdata = util.merge(data or app,{})
|
local mdata = util.merge(data or app,{})
|
||||||
mdata = util.merge(mdata, {opts = app.opts})
|
mdata = util.merge(mdata, {opts = app.opts})
|
||||||
-- set fallback vars
|
-- set fallback vars
|
||||||
mdata.urlpath = GetPath()
|
mdata.urlpath = util.baseURLify( GetPath() )
|
||||||
mdata.url = mdata.url or util.getURL()
|
mdata.url = mdata.url or mdata.urlpath
|
||||||
mdata.title = mdata.title or app.opts.subtitle
|
mdata.title = mdata.title or app.opts.subtitle
|
||||||
mdata.header_include = mdata.header_include or ''
|
mdata.header_include = mdata.header_include or ''
|
||||||
mdata.footerlinks = {}
|
mdata.footerlinks = {}
|
||||||
|
|
@ -330,6 +337,13 @@ function util.stripPath(str)
|
||||||
return str:gsub("^/zip/",""):gsub("^./",""):gsub("^/",""):gsub("/$","")
|
return str:gsub("^/zip/",""):gsub("^./",""):gsub("^/",""):gsub("/$","")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function util.truncate(str,maxchar,filler)
|
||||||
|
filler = filler or '..'
|
||||||
|
str = str:gsub(".*://",""):gsub("/$",""):gsub("/.*/","/.../")
|
||||||
|
if maxchar ~= nil and #str > maxchar then str = str:sub(0,maxchar) .. filler end
|
||||||
|
return str
|
||||||
|
end
|
||||||
|
|
||||||
-- WARNING: this is a destructive function which renders the redbean server inactive
|
-- WARNING: this is a destructive function which renders the redbean server inactive
|
||||||
-- only to be used in tests or staticwebgen plugin
|
-- only to be used in tests or staticwebgen plugin
|
||||||
util.queueServerRequest = function(method, path, params, host, headers, body, scheme, port)
|
util.queueServerRequest = function(method, path, params, host, headers, body, scheme, port)
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ body,html {
|
||||||
}
|
}
|
||||||
|
|
||||||
body{
|
body{
|
||||||
|
overflow-wrap: break-word;
|
||||||
background:#000;
|
background:#000;
|
||||||
margin:20px;
|
margin:20px;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
24
src/middleware/onlyGET.lua
Normal file
24
src/middleware/onlyGET.lua
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
-- soakbean middleware function
|
||||||
|
return function(opts)
|
||||||
|
opts = opts or {exclude={"^/admin"}}
|
||||||
|
return function(req,res,next)
|
||||||
|
local pass = false
|
||||||
|
if req.method == "GET" then
|
||||||
|
pass = true
|
||||||
|
else
|
||||||
|
foreach(opts.exclude, function(k,path)
|
||||||
|
if req.url:match(path) then
|
||||||
|
pass = true
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
if pass then
|
||||||
|
next()
|
||||||
|
else
|
||||||
|
res.status(405)
|
||||||
|
res.body("not allowed")
|
||||||
|
res.send(req,res)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
@ -19,6 +19,18 @@ end}
|
||||||
|
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
{if items then
|
{if items then
|
||||||
|
|
||||||
|
function getURL(url)
|
||||||
|
if app.arg[1] == 'generate' and url:match("://") then
|
||||||
|
url = 'janusweb/index.html#janus.url=' ..
|
||||||
|
EscapeFragment( url ) .. '&janus.referrer=' .. util.getURL(urlpath )
|
||||||
|
url = util.baseURLify(url)
|
||||||
|
else
|
||||||
|
url = util.stripPath( url )
|
||||||
|
end
|
||||||
|
return url
|
||||||
|
end
|
||||||
|
|
||||||
for i=1,#items do
|
for i=1,#items do
|
||||||
local item = items[i]
|
local item = items[i]
|
||||||
local visible = item.view2D == nil or (item.view2D ~= nil and item.view2D ~= false)
|
local visible = item.view2D == nil or (item.view2D ~= nil and item.view2D ~= false)
|
||||||
|
|
@ -29,10 +41,10 @@ end}
|
||||||
if item.view3D or item.portal3D == false then icon = 'play' end
|
if item.view3D or item.portal3D == false then icon = 'play' end
|
||||||
print([[
|
print([[
|
||||||
<div class="tile">
|
<div class="tile">
|
||||||
<a href="]] .. util.stripPath(item.url) .. [[">
|
<a href="]] .. getURL(item.url) .. [[">
|
||||||
<div class="img" ]] .. util.stripPath(thumbnail) .. [[></div>
|
<div class="img" ]] .. util.stripPath(thumbnail) .. [[></div>
|
||||||
<div class="text">
|
<div class="text">
|
||||||
<img src="img/]] .. icon .. [[.svg" class="icon ]] .. icon .. [["/> <a href="]] .. util.stripPath(item.url) .. [[">]] .. item.title .. [[</a>
|
<img src="img/]] .. icon .. [[.svg" class="icon ]] .. icon .. [["/> <a href="]] .. getURL(item.url) .. [[">]] .. item.title .. [[</a>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -49,10 +61,10 @@ end}
|
||||||
if item.view3D or item.portal3D == false then icon = 'play' end
|
if item.view3D or item.portal3D == false then icon = 'play' end
|
||||||
print([[
|
print([[
|
||||||
<div class="tile">
|
<div class="tile">
|
||||||
<a href="]] .. util.stripPath(item.url) .. [[">
|
<a href="]] .. getURL(item.url) .. [[">
|
||||||
<div class="img" ]] .. util.stripPath(thumbnail) .. [[></div>
|
<div class="img" ]] .. util.stripPath(thumbnail) .. [[></div>
|
||||||
<div class="text">
|
<div class="text">
|
||||||
<img src="img/]] .. icon .. [[.svg" class="icon ]] .. icon .. [["/> <a href="]] .. util.stripPath(item.url) .. [[">]] .. item.title .. [[</a>
|
<img src="img/]] .. icon .. [[.svg" class="icon ]] .. icon .. [["/> <a href="]] .. getURL(item.url) .. [[">]] .. util.truncate(item.title) .. [[</a>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,12 @@ return {
|
||||||
local isdir = urlpath:sub(-1) == "/"
|
local isdir = urlpath:sub(-1) == "/"
|
||||||
local nslashes = util.countChar('/',urlpath)
|
local nslashes = util.countChar('/',urlpath)
|
||||||
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
|
||||||
local urlhash = urlpath .. util.dump( req.param )
|
local urlhash = urlpath .. util.dump( req.param )
|
||||||
|
local referrer = util.baseURLify( (isroot and not req.param.url) and "" or urlpath )
|
||||||
|
|
||||||
if (isroot or urlpath:match("^"..rootpath))
|
if (isroot or urlpath:match("^"..rootpath))
|
||||||
and not urlpath:gsub(".*/",""):match("%.") -- no regular file with extension
|
and not urlpath:gsub(".*/",""):match("%.") -- no regular file with extension
|
||||||
and not req.param.url then
|
then
|
||||||
|
|
||||||
local redirect = false
|
local redirect = false
|
||||||
if not app.cache.url[ urlhash ] or util.count(req.params) > 0 then
|
if not app.cache.url[ urlhash ] or util.count(req.params) > 0 then
|
||||||
|
|
@ -45,14 +46,10 @@ return {
|
||||||
res.items[ v.id ] = v
|
res.items[ v.id ] = v
|
||||||
res.parent = parent
|
res.parent = parent
|
||||||
end
|
end
|
||||||
if not isroot then
|
if isroot then
|
||||||
res.items[ v.parent.id ] = {
|
referrer = app.opts.plugin_federation_parent_url
|
||||||
view2D = false,
|
else
|
||||||
url = v.parent.parent.url,
|
referrer = v.parent.parent.url
|
||||||
title = app.opts.plugin_janusxr_portal_up,
|
|
||||||
name = app.opts.plugin_janusxr_portal_up,
|
|
||||||
thumbnail = '',
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -62,7 +59,8 @@ return {
|
||||||
local data = util.merge({
|
local data = util.merge({
|
||||||
url = req.param.url, -- query hint to render janus.url in iframe (index.html)
|
url = req.param.url, -- query hint to render janus.url in iframe (index.html)
|
||||||
items = {},
|
items = {},
|
||||||
referrer = isroot and app.opts.plugin_federation_parent_url or util.getURL(),
|
isroot = isroot,
|
||||||
|
referrer = referrer,
|
||||||
parent=res.parent,
|
parent=res.parent,
|
||||||
template=res.template
|
template=res.template
|
||||||
},{ opts = app.opts })
|
},{ opts = app.opts })
|
||||||
|
|
@ -78,7 +76,7 @@ return {
|
||||||
-- prioritize markup experience above binary 3D files as experience containers
|
-- prioritize markup experience above binary 3D files as experience containers
|
||||||
if not data.item or (isMarkupExperience(v) and not isMarkupExperience(data.item)) then
|
if not data.item or (isMarkupExperience(v) and not isMarkupExperience(data.item)) then
|
||||||
-- update referrer url
|
-- update referrer url
|
||||||
data.referrer = util.getURL( v.view3D and v.parent.parent.url or v.parent.url )
|
data.referrer = util.baseURLify(v.view3D and v.parent.parent.url or v.parent.url)
|
||||||
data.item = v
|
data.item = v
|
||||||
data.title = data.item.title
|
data.title = data.item.title
|
||||||
data.parent = v.parent
|
data.parent = v.parent
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@
|
||||||
<a href="${opts.baseurl}">
|
<a href="${opts.baseurl}">
|
||||||
<div class="logo max">
|
<div class="logo max">
|
||||||
<span class="col1">[</span><span class="col2"><></span><span class="col1">]</span> <span class="col3">${opts.plugin_global_title}
|
<span class="col1">[</span><span class="col2"><></span><span class="col1">]</span> <span class="col3">${opts.plugin_global_title}
|
||||||
{if url:match("admin") then
|
{if urlpath:match("^/admin") then
|
||||||
print('<span class="col1">//<span>')
|
print('<span class="col1">//<span>')
|
||||||
print('<span class="col0">${opts.admin_title}</span>')
|
print('<span class="col0">${opts.admin_title}</span>')
|
||||||
end}
|
end}
|
||||||
|
|
@ -38,7 +38,16 @@
|
||||||
print( app.opts.plugin_global_topmenu_html:gsub("\n"," ") )
|
print( app.opts.plugin_global_topmenu_html:gsub("\n"," ") )
|
||||||
end}
|
end}
|
||||||
{if template == "page/dir.html" then
|
{if template == "page/dir.html" then
|
||||||
print([[<div class="btn XR"><a class="border secondary" href="${urlpath}?view=3D">3D</a></div> ]])
|
local url = urlpath
|
||||||
|
if not urlpath:match(app.opts.plugin_diskscan_dirname) then
|
||||||
|
-- janusweb workaround (it does not support '/' url somehow)
|
||||||
|
urlpath = urlpath .. app.opts.plugin_diskscan_dirname
|
||||||
|
end
|
||||||
|
if app.arg[1] == "generate" then
|
||||||
|
print([[<div class="btn XR"><a class="border secondary" href="janusweb/#janus.url=]] .. urlpath .. [[&janus.referrer=${referrer}">3D</a></div> ]])
|
||||||
|
else
|
||||||
|
print([[<div class="btn XR"><a class="border secondary" href="${urlpath}?view=3D">3D</a></div> ]])
|
||||||
|
end
|
||||||
end}
|
end}
|
||||||
{if template == "page/index.html" then
|
{if template == "page/index.html" then
|
||||||
local sharedata = {
|
local sharedata = {
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ ${header}
|
||||||
|
|
||||||
<iframe border="0" frameborder="0" src="janusweb/index.html#janus.url=${url}&janus.referrer=${referrer}" id="viewer"></iframe>
|
<iframe border="0" frameborder="0" src="janusweb/index.html#janus.url=${url}&janus.referrer=${referrer}" id="viewer"></iframe>
|
||||||
|
|
||||||
{if item and item.parent and item.parent.info then
|
{if defined(item) and item.parent and item.parent.info then
|
||||||
print([[
|
print([[
|
||||||
<div id="popup" class="content">
|
<div id="popup" class="content">
|
||||||
<div class="spectrum span" style="top:-1px">
|
<div class="spectrum span" style="top:-1px">
|
||||||
|
|
|
||||||
|
|
@ -5,22 +5,23 @@
|
||||||
-- optimize janus resource URLs for archive.org
|
-- optimize janus resource URLs for archive.org
|
||||||
app.plugin.archive.flush()
|
app.plugin.archive.flush()
|
||||||
function URL(url)
|
function URL(url)
|
||||||
|
url = util.baseURLify(url)
|
||||||
app.plugin.archive.add(url)
|
app.plugin.archive.add(url)
|
||||||
return url
|
return url
|
||||||
end
|
end
|
||||||
end}
|
|
||||||
|
|
||||||
|
end}
|
||||||
|
|
||||||
<fireboxroom>
|
<fireboxroom>
|
||||||
<assets>
|
<assets>
|
||||||
{if items and #items > 0 then
|
{if defined(items) and #items > 0 then
|
||||||
for i,v in pairs(items) do
|
for i,v in pairs(items) do
|
||||||
if #v.thumbnail > 0 then
|
if #v.thumbnail > 0 then
|
||||||
print('\t<assetimage src="' .. URL(v.thumbnail) .. '" id="img' .. i .. '"/>')
|
print('\t<assetimage src="' .. URL(v.thumbnail) .. '" id="img' .. i .. '"/>')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end}
|
end}
|
||||||
{if itemsremote and #itemsremote > 0 then
|
{if defined(itemsremote) and #itemsremote > 0 then
|
||||||
for i,v in pairs(itemsremote) do
|
for i,v in pairs(itemsremote) do
|
||||||
if #v.thumbnail > 0 then
|
if #v.thumbnail > 0 then
|
||||||
print('\t<assetimage src="' .. URL(v.thumbnail) .. '" id="imgremote' .. i .. '"/>')
|
print('\t<assetimage src="' .. URL(v.thumbnail) .. '" id="imgremote' .. i .. '"/>')
|
||||||
|
|
@ -38,28 +39,27 @@ end}
|
||||||
end
|
end
|
||||||
end}
|
end}
|
||||||
</assets>
|
</assets>
|
||||||
{if item then
|
{if true then
|
||||||
print([[
|
|
||||||
<room pos="0 0 0" skybox="true" showavatar="false">
|
|
||||||
<object id="${url}"/>
|
|
||||||
]])
|
|
||||||
else
|
|
||||||
local room = app.opts.plugin_janusxr_portalroom
|
local room = app.opts.plugin_janusxr_portalroom
|
||||||
if item and item.janusxr and item.janusxr.room then
|
if defined(item) and item.janusxr and item.janusxr.room then
|
||||||
room = item.janusxr.room
|
room = item.janusxr.room
|
||||||
end
|
end
|
||||||
if room == 'random' then room = app.plugin.janusxr.getRandomRoom() end
|
if room == 'random' then room = app.plugin.janusxr.getRandomRoom() end
|
||||||
|
if defined(item) and item.tag.xrfragments and item.tag.xrfragments.qualify then
|
||||||
|
room = '' -- xrfragments 3D files dont need to be wrapped with room
|
||||||
|
end
|
||||||
print(' <room skybox="true" showavatar="false" use_local_asset="' .. room .. '">')
|
print(' <room skybox="true" showavatar="false" use_local_asset="' .. room .. '">')
|
||||||
|
if defined(item) then
|
||||||
|
print(' <object id="${url}"/>')
|
||||||
|
end
|
||||||
end}
|
end}
|
||||||
<light js_id="dan9-janus:light/light_cone_angle=0-6" pos="0 3 2" collision_trigger="false" />
|
<light js_id="dan9-janus:light/light_cone_angle=0-6" pos="0 3 2" collision_trigger="false" />
|
||||||
{if true then
|
{if true then
|
||||||
function addPortal(id,x,v,url,i,z,thumb,rotation,scale)
|
function addPortal(id,x,v,url,i,z,thumb,rotation,scale)
|
||||||
local title = v.title
|
local title = v.title
|
||||||
local maxchar = 20
|
|
||||||
if #title > maxchar then title = title:sub(0,maxchar) .. '..' end
|
|
||||||
print('\t<object js_id="' .. id .. '_' .. i .. '" pos="' .. x .. ' 0 ' .. z .. '" rotation="' .. rotation .. '">')
|
print('\t<object js_id="' .. id .. '_' .. i .. '" pos="' .. x .. ' 0 ' .. z .. '" rotation="' .. rotation .. '">')
|
||||||
print('\t <link url="' .. url .. '" ' .. thumb .. ' title_col="0.4 0.37 0.4" pos="0 0 0.001" draw_glow="' .. (thumb:match("shader") and 'false' or 'true') .. '" scale="3 3 0.1" />')
|
print('\t <link url="' .. util.baseURLify(url) .. '" ' .. thumb .. ' title_col="0.4 0.37 0.4" pos="0 0 0.001" draw_glow="' .. (thumb:match("shader") and 'false' or 'true') .. '" scale="3 3 0.1" />')
|
||||||
print('\t <text text="' .. title .. '" pos="0 3.4 0" col="1 1 1" font_scale="false" scale="' .. scale .. '" />')
|
print('\t <text text="' .. util.truncate(title,20) .. '" pos="0 3.4 0" col="1 1 1" font_scale="false" scale="' .. scale .. '" />')
|
||||||
--TODO: fix janusweb paragraph for markdown urls
|
--TODO: fix janusweb paragraph for markdown urls
|
||||||
--if v.README ~= nil then
|
--if v.README ~= nil then
|
||||||
-- print('\t <paragraph js_id="README" url="' .. v.README .. '" width="600" height="900" pos="0 1 2" transparent="true"/>')
|
-- print('\t <paragraph js_id="README" url="' .. v.README .. '" width="600" height="900" pos="0 1 2" transparent="true"/>')
|
||||||
|
|
@ -68,7 +68,7 @@ end}
|
||||||
end
|
end
|
||||||
end}
|
end}
|
||||||
<text js_id="location" pos="0 6 -5" billboard="y" col="#BBFFFF" scale="10 10 1" text="${url}" emissive="0 0 0" roughness="0.3" metalness="0" />
|
<text js_id="location" pos="0 6 -5" billboard="y" col="#BBFFFF" scale="10 10 1" text="${url}" emissive="0 0 0" roughness="0.3" metalness="0" />
|
||||||
{if not item and items and #items > 0 then
|
{if not defined(item) and defined(items) and #items > 0 then
|
||||||
for i,v in pairs(items) do
|
for i,v in pairs(items) do
|
||||||
local width = 3.2
|
local width = 3.2
|
||||||
local center = -((#items*width)/1.3) + (i*width)
|
local center = -((#items*width)/1.3) + (i*width)
|
||||||
|
|
@ -82,7 +82,7 @@ end}
|
||||||
addPortal('dir',center, v, url,i, -4, thumb, '0 0 0', '0.2 0.2 0.2')
|
addPortal('dir',center, v, url,i, -4, thumb, '0 0 0', '0.2 0.2 0.2')
|
||||||
end
|
end
|
||||||
end}
|
end}
|
||||||
{if not item and itemsremote and #itemsremote > 0 then
|
{if not defined(item) and defined(itemsremote) and #itemsremote > 0 then
|
||||||
for i,v in pairs(itemsremote) do
|
for i,v in pairs(itemsremote) do
|
||||||
local width = 3.2
|
local width = 3.2
|
||||||
local center = -((#itemsremote*width)/1.3) + (i*width)
|
local center = -((#itemsremote*width)/1.3) + (i*width)
|
||||||
|
|
@ -100,8 +100,19 @@ end}
|
||||||
]])
|
]])
|
||||||
end}
|
end}
|
||||||
|
|
||||||
|
{if #referrer > 0 then
|
||||||
|
-- hardcoded reciprocal portal
|
||||||
|
local v = {
|
||||||
|
url = referrer,
|
||||||
|
title = app.opts.plugin_janusxr_portal_up,
|
||||||
|
}
|
||||||
|
addPortal('parent',4, v, v.url, 0, 0, ' shader_id="defaultportal"', '0 -90 0', '0.2 0.2 0.2')
|
||||||
|
end}
|
||||||
|
|
||||||
<object id="cube" rotation="0 0 0" position="0 0 0" scale="2 0.1 2"/> <!-- something to stand on -->
|
<object id="cube" rotation="0 0 0" position="0 0 0" scale="2 0.1 2"/> <!-- something to stand on -->
|
||||||
${janusxr_jml_deco}
|
{if not defined(item) then
|
||||||
|
print('${janusxr_jml_deco}')
|
||||||
|
end}
|
||||||
${opts.plugin_janusxr_jml}
|
${opts.plugin_janusxr_jml}
|
||||||
</room>
|
</room>
|
||||||
</fireboxroom>
|
</fireboxroom>
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,10 @@ app.on('init', function()
|
||||||
local me = app.plugin.archive
|
local me = app.plugin.archive
|
||||||
if arg[1] == 'server' then
|
if arg[1] == 'server' then
|
||||||
local code, pid = unix.fork()
|
local code, pid = unix.fork()
|
||||||
|
|
||||||
-- kill thread when main app gets CTRL-c
|
-- kill thread when main app gets CTRL-c
|
||||||
unix.sigaction(unix.SIGINT, function()
|
unix.sigaction(unix.SIGINT, function()
|
||||||
unix.exit()
|
unix.exit()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
function notify()
|
function notify()
|
||||||
app.plugin.archive.notify()
|
app.plugin.archive.notify()
|
||||||
--notify() -- loop forever
|
--notify() -- loop forever
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,7 @@ app.on('dir', function(opts)
|
||||||
table.insert(itemsremote, {
|
table.insert(itemsremote, {
|
||||||
title = url:gsub(".*//",""),
|
title = url:gsub(".*//",""),
|
||||||
thumbnail = thumburl,
|
thumbnail = thumburl,
|
||||||
url = '/janusweb/index.html#janus.url=' .. EscapeFragment(url)
|
url = url
|
||||||
.. '&janus.referrer=' .. EscapeFragment( util.getURL(opts.urlpath ) )
|
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,7 @@ app.on('dir', function(opts)
|
||||||
table.insert(itemsremote, {
|
table.insert(itemsremote, {
|
||||||
title = url:gsub(".*//",""),
|
title = url:gsub(".*//",""),
|
||||||
thumbnail = thumburl,
|
thumbnail = thumburl,
|
||||||
url = '/janusweb/index.html#janus.url=' .. EscapeFragment(url)
|
url = url
|
||||||
.. '&janus.referrer=' .. EscapeFragment( util.getURL(opts.urlpath ) )
|
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,7 @@ app.on('dir', function(opts)
|
||||||
table.insert(itemsremote, {
|
table.insert(itemsremote, {
|
||||||
title = url:gsub(".*//",""),
|
title = url:gsub(".*//",""),
|
||||||
thumbnail = thumburl,
|
thumbnail = thumburl,
|
||||||
url = '/janusweb/index.html#janus.url=' .. EscapeFragment(url)
|
url = url
|
||||||
.. '&janus.referrer=' .. EscapeFragment( util.getURL(opts.urlpath ) )
|
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,10 @@ app.on('scan_experience_file', function(exp)
|
||||||
if util.fileExist( jsonfile ) then
|
if util.fileExist( jsonfile ) then
|
||||||
exp.sidecarfile.JSONXRF = me.detectHrefInStruct( json.decode( util.readfile(jsonfile) ) )
|
exp.sidecarfile.JSONXRF = me.detectHrefInStruct( json.decode( util.readfile(jsonfile) ) )
|
||||||
end
|
end
|
||||||
exp.tag.xrfragments = { qualify = hasHref or hasPNG or hasWebVTT or hasOGG or hasJSONref, href = hasHref }
|
exp.tag.xrfragments = {
|
||||||
|
qualify = (hasHref or exp.sidecarfile.png or exp.sidecarfile.vtt or exp.sidecarfile.ogg or exp.sidecarfile.json) and true or false,
|
||||||
|
href = hasHref
|
||||||
|
}
|
||||||
if exp.tag.xrfragments.qualify then
|
if exp.tag.xrfragments.qualify then
|
||||||
exp.view3D = true
|
exp.view3D = true
|
||||||
print("✅ └─ plugin/xrfragments: detected xrfragments experience")
|
print("✅ └─ plugin/xrfragments: detected xrfragments experience")
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue