release v0.5.2

This commit is contained in:
Leon van Kammen 2024-04-16 13:19:08 +00:00
parent 45b46f788c
commit 6ad8f146c7
21 changed files with 5714 additions and 373 deletions

Binary file not shown.

4
dist/aframe.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,5 @@
/* /*
* v0.5.1 generated at Tue Mar 19 10:04:25 AM UTC 2024 * v0.5.1 generated at Tue Apr 16 12:47:01 PM UTC 2024
* https://xrfragment.org * https://xrfragment.org
* SPDX-License-Identifier: MPL-2.0 * SPDX-License-Identifier: MPL-2.0
*/ */
@ -1138,9 +1138,13 @@ xrfragment_Parser.getMetaData = function() {
var meta = { title : ["title","og:title","dc.title"], description : ["aria-description","og:description","dc.description"], author : ["author","dc.creator"], publisher : ["publisher","dc.publisher"], website : ["og:site_name","og:url","dc.publisher"], license : ["SPDX","dc.rights"]}; var meta = { title : ["title","og:title","dc.title"], description : ["aria-description","og:description","dc.description"], author : ["author","dc.creator"], publisher : ["publisher","dc.publisher"], website : ["og:site_name","og:url","dc.publisher"], license : ["SPDX","dc.rights"]};
return meta; return meta;
}; };
var xrfragment_URI = $hx_exports["xrfragment"]["URI"] = function() { }; var xrfragment_URI = $hx_exports["xrfragment"]["URI"] = function() {
this.XRF = { };
this.hash = { };
this.fragment = "";
};
xrfragment_URI.__name__ = true; xrfragment_URI.__name__ = true;
xrfragment_URI.parse = function(url,filter) { xrfragment_URI.parseFragment = function(url,filter) {
var store = { }; var store = { };
if(url == null || url.indexOf("#") == -1) { if(url == null || url.indexOf("#") == -1) {
return store; return store;
@ -1192,6 +1196,232 @@ xrfragment_URI.template = function(uri,vars) {
parts[1] = frag; parts[1] = frag;
return parts.join("#"); return parts.join("#");
}; };
xrfragment_URI.parse = function(stringUrl,flags) {
var r = new EReg("^(?:(?![^:@]+:[^:@/]*@)([^:/?#.]+):)?(?://)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:/?#]*)(?::(\\d*))?)(((/(?:[^?#](?![^?#/]*\\.[^?#/.]+(?:[?#]|$)))*/?)?([^?#/]*))(?:\\?([^#]*))?(?:#(.*))?)","");
if(stringUrl.indexOf("://") == -1 && stringUrl.charAt(0) != "/") {
stringUrl = "/" + stringUrl;
}
r.match(stringUrl);
var url = new xrfragment_URI();
var _g = 0;
var _g1 = xrfragment_URI._parts.length;
while(_g < _g1) {
var i = _g++;
url[xrfragment_URI._parts[i]] = r.matched(i);
}
if(xrfragment_URI.isRelative(url) == true) {
if(url.directory == null && url.host != null) {
url.file = url.host;
}
}
url.hash = { };
if(url.fragment != null && url.fragment.length > 0) {
url.XRF = xrfragment_URI.parseFragment("#" + url.fragment,flags);
var key;
var _g = 0;
var _g1 = Reflect.fields(url.XRF);
while(_g < _g1.length) {
var key = _g1[_g];
++_g;
var v = url.XRF[key];
url.hash[key] = v["string"];
}
}
xrfragment_URI.computeVars(url);
return url;
};
xrfragment_URI.computeVars = function(url) {
var r_r = new RegExp("//","g".split("u").join(""));
if(url.directory != null && url.directory.indexOf("//") != -1) {
url.directory = url.directory.replace(r_r,"/");
}
if(url.path != null && url.path.indexOf("//") != -1) {
url.path = url.path.replace(r_r,"/");
}
if(url.file != null && url.file.indexOf("//") != -1) {
url.file = url.file.replace(r_r,"/");
}
url.URN = url.scheme + "://" + url.host;
if(url.port != null) {
url.URN += ":" + url.port;
}
url.URN += url.directory;
if(url.file != null) {
var parts = url.file.split(".");
if(parts.length > 1) {
url.fileExt = parts.pop();
}
}
};
xrfragment_URI.toString = function(url) {
var result = "";
if(url.scheme != null) {
result += url.scheme + "://";
}
if(url.user != null) {
result += url.user + ":";
}
if(url.password != null) {
result += url.password + "@";
}
if(url.host != null) {
result += url.host;
}
if(url.port != null) {
result += ":" + url.port;
}
if(url.directory != null) {
result += url.directory;
}
if(url.file != null) {
result += url.file;
}
if(url.query != null) {
result += "?" + url.query;
}
if(url.fragment != null) {
result += "#" + url.fragment;
}
return result;
};
xrfragment_URI.appendURI = function(url,appendedURI) {
if(xrfragment_URI.isRelative(url) == true) {
return xrfragment_URI.appendToRelativeURI(url,appendedURI);
} else {
return xrfragment_URI.appendToAbsoluteURI(url,appendedURI);
}
};
xrfragment_URI.isRelative = function(url) {
return url.scheme == null;
};
xrfragment_URI.appendToRelativeURI = function(url,appendedURI) {
if(url.directory == null || url.host == null) {
return xrfragment_URI.cloneURI(appendedURI);
}
var resultURI = new xrfragment_URI();
resultURI.host = url.host;
resultURI.directory = url.directory;
if(appendedURI.host != null) {
resultURI.directory += appendedURI.host;
}
if(appendedURI.directory != null) {
var directory = appendedURI.directory;
if(appendedURI.host == null) {
resultURI.directory += HxOverrides.substr(directory,1,null);
} else {
resultURI.directory += directory;
}
}
if(appendedURI.file != null) {
resultURI.file = appendedURI.file;
}
resultURI.path = resultURI.directory + resultURI.file;
if(appendedURI.query != null) {
resultURI.query = appendedURI.query;
}
if(appendedURI.fragment != null) {
resultURI.fragment = appendedURI.fragment;
}
return resultURI;
};
xrfragment_URI.appendToAbsoluteURI = function(url,appendedURI) {
var resultURI = new xrfragment_URI();
if(url.scheme != null) {
resultURI.scheme = url.scheme;
}
if(url.host != null) {
resultURI.host = url.host;
}
var directory = "";
if(url.directory != null) {
directory = url.directory;
}
if(appendedURI.host != null) {
appendedURI.directory += appendedURI.host;
}
if(appendedURI.directory != null) {
directory += appendedURI.directory;
}
resultURI.directory = directory;
if(appendedURI.file != null) {
resultURI.file = appendedURI.file;
}
resultURI.path = resultURI.directory + resultURI.file;
if(appendedURI.query != null) {
resultURI.query = appendedURI.query;
}
if(appendedURI.fragment != null) {
resultURI.fragment = appendedURI.fragment;
}
return resultURI;
};
xrfragment_URI.toAbsolute = function(url,newUrl) {
var newURI = xrfragment_URI.parse(newUrl,0);
var resultURI = new xrfragment_URI();
resultURI.port = url.port;
resultURI.source = newUrl;
if(newURI.scheme != null) {
resultURI.scheme = newURI.scheme;
} else {
resultURI.scheme = url.scheme;
}
if(newURI.host != null && newURI.host.length > 0) {
resultURI.host = newURI.host;
resultURI.port = null;
resultURI.fragment = null;
resultURI.hash = { };
resultURI.XRF = { };
if(newURI.port != null) {
resultURI.port = newURI.port;
}
} else {
resultURI.host = url.host;
}
var directory = "";
if(url.directory != null) {
directory = url.directory;
}
if(newURI.directory != null) {
if(newUrl.charAt(0) != "/" && newUrl.indexOf("://") == -1) {
directory += newURI.directory;
} else {
directory = newURI.directory;
}
}
resultURI.directory = directory;
if(newURI.file != null) {
resultURI.file = newURI.file;
}
resultURI.path = resultURI.directory + resultURI.file;
if(newURI.query != null) {
resultURI.query = newURI.query;
}
if(newURI.fragment != null) {
resultURI.fragment = newURI.fragment;
}
resultURI.hash = newURI.hash;
resultURI.XRF = newURI.XRF;
xrfragment_URI.computeVars(resultURI);
return resultURI;
};
xrfragment_URI.cloneURI = function(url) {
var clonedURI = new xrfragment_URI();
clonedURI.url = url.url;
clonedURI.source = url.source;
clonedURI.scheme = url.scheme;
clonedURI.authority = url.authority;
clonedURI.userInfo = url.userInfo;
clonedURI.password = url.password;
clonedURI.host = url.host;
clonedURI.port = url.port;
clonedURI.relative = url.relative;
clonedURI.path = url.path;
clonedURI.directory = url.directory;
clonedURI.file = url.file;
clonedURI.query = url.query;
clonedURI.fragment = url.fragment;
return clonedURI;
};
var xrfragment_XRF = $hx_exports["xrfragment"]["XRF"] = function(_fragment,_flags,_index) { var xrfragment_XRF = $hx_exports["xrfragment"]["XRF"] = function(_fragment,_flags,_index) {
this.floats = []; this.floats = [];
this.shift = []; this.shift = [];
@ -1302,6 +1532,7 @@ haxe_Template.hxKeepArrayIterator = new haxe_iterators_ArrayIterator([]);
xrfragment_Parser.error = ""; xrfragment_Parser.error = "";
xrfragment_Parser.debug = false; xrfragment_Parser.debug = false;
xrfragment_URI.__meta__ = { statics : { template : { keep : null}}}; xrfragment_URI.__meta__ = { statics : { template : { keep : null}}};
xrfragment_URI._parts = ["source","scheme","authority","userInfo","user","password","host","port","relative","path","directory","file","query","fragment"];
xrfragment_XRF.IMMUTABLE = 1; xrfragment_XRF.IMMUTABLE = 1;
xrfragment_XRF.PROP_BIND = 2; xrfragment_XRF.PROP_BIND = 2;
xrfragment_XRF.QUERY_OPERATOR = 4; xrfragment_XRF.QUERY_OPERATOR = 4;
@ -1589,7 +1820,7 @@ let pub = function( url, node_or_model, flags ){ // evaluate fragments in url
if( !url ) return if( !url ) return
if( !url.match(/#/) ) url = `#${url}` if( !url.match(/#/) ) url = `#${url}`
let { THREE, camera } = xrf let { THREE, camera } = xrf
let frag = xrf.URI.parse( url, flags ) let frag = xrf.URI.parse( url, flags ).XRF
let fromNode = node_or_model != xrf.model let fromNode = node_or_model != xrf.model
let isNode = node_or_model && node_or_model.children let isNode = node_or_model && node_or_model.children
@ -1756,24 +1987,6 @@ xrf.reset = () => {
xrf.layers = 0 xrf.layers = 0
} }
xrf.parseUrl = (url) => {
let urlExHash = url.replace(/#.*/,'')
let urlObj,file
let store = {}
try{
urlObj = new URL( urlExHash.match(/:\/\//) ? urlExHash : String(`https://fake.com/${url}`).replace(/\/\//,'/') )
file = urlObj.pathname.substring(urlObj.pathname.lastIndexOf('/') + 1);
let search = urlObj.search.substr(1).split("&")
for( let i in search ) store[ (search[i].split("=")[0]) ] = search[i].split("=")[1] || ''
}catch(e){ }
let hashmap = url.match("#") ? url.replace(/.*#/,'').split("&") : []
for( let i in hashmap ) store[ (hashmap[i].split("=")[0]) ] = hashmap[i].split("=")[1] || ''
let dir = url.substring(0, url.lastIndexOf('/') + 1)
const hash = url.match(/#/) ? url.replace(/.*#/,'') : ''
const ext = file.split('.').pop()
return {urlObj,dir,file,hash,ext,store}
}
xrf.add = (object) => { xrf.add = (object) => {
object.isXRF = true // mark for easy deletion when replacing scene object.isXRF = true // mark for easy deletion when replacing scene
xrf.scene.add(object) xrf.scene.add(object)
@ -1784,42 +1997,52 @@ xrf.hasNoMaterial = (mesh) => {
const hasMaterialName = mesh.material && mesh.material.name.length > 0 const hasMaterialName = mesh.material && mesh.material.name.length > 0
return mesh.geometry && !hasMaterialName && !hasTexture return mesh.geometry && !hasMaterialName && !hasTexture
} }
xrf.navigator = {} xrf.navigator = {URI:{}}
xrf.navigator.to = (url,flags,loader,data) => { xrf.navigator.to = (url,flags,loader,data) => {
if( !url ) throw 'xrf.navigator.to(..) no url given' if( !url ) throw 'xrf.navigator.to(..) no url given'
let {urlObj,dir,file,hash,ext} = xrf.navigator.origin = xrf.parseUrl(url)
let hashChange = (!file && hash) || !data && xrf.model.file == file
let hasPos = String(hash).match(/pos=/)
let URI = xrfragment.URI.toAbsolute( xrf.navigator.URI, url )
URI.hash = xrf.navigator.reactifyHash(URI.hash)
let fileChange = URI.URN + URI.file != xrf.navigator.URI.URN + xrf.navigator.URI.file
let external = URI.URN != document.location.origin + document.location.pathname
let hasPos = URI.hash.pos
let hashChange = String(xrf.navigator.URI.fragment||"") != String(URI.fragment||"")
let hashbus = xrf.hashbus
xrf.navigator.URI = URI
let {directory,file,fragment,fileExt} = URI;
let hashbus = xrf.hashbus const evalFragment = () => {
if( URI.fragment ){
hashbus.pub( URI.fragment, xrf.model, flags ) // eval local URI XR fragments
xrf.navigator.updateHash(fragment) // which don't require
}
}
return new Promise( (resolve,reject) => { return new Promise( (resolve,reject) => {
xrf xrf
.emit('navigate', {url,loader,data}) .emit('navigate', {url,loader,data})
.then( () => { .then( () => {
if( ext && !loader ){ const Loader = xrf.loaders[fileExt]
const Loader = xrf.loaders[ext]
if( fileExt && !loader ){
if( !Loader ) return resolve() if( !Loader ) return resolve()
loader = loader || new Loader().setPath( dir ) loader = loader || new Loader().setPath( URI.URN )
} }
if( !hash && !file && !ext ) return resolve(xrf.model) // nothing we can do here if( !URI.fragment && !URI.file && !URI.fileExt ) return resolve(xrf.model) // nothing we can do here
if( hashChange && !hasPos ){ if( xrf.model && !fileChange && hashChange && !hasPos ){
hashbus.pub( url, xrf.model, flags ) // eval local URI XR fragments evalFragment()
xrf.navigator.updateHash(hash) // which don't require return resolve(xrf.model) // positional navigation
return resolve(xrf.model) // positional navigation
} }
xrf xrf
.emit('navigateLoading', {url,loader,data}) .emit('navigateLoading', {url,loader,data})
.then( () => { .then( () => {
if( hashChange && hasPos ){ // we're already loaded if( (!fileChange || !file) && hashChange && hasPos ){ // we're already loaded
hashbus.pub( url, xrf.model, flags ) // and eval local URI XR fragments evalFragment()
xrf.navigator.updateHash(hash)
xrf.emit('navigateLoaded',{url}) xrf.emit('navigateLoaded',{url})
return resolve(xrf.model) return resolve(xrf.model)
} }
@ -1829,17 +2052,20 @@ xrf.navigator.to = (url,flags,loader,data) => {
xrf.reset() xrf.reset()
// force relative path for files which dont include protocol or relative path // force relative path for files which dont include protocol or relative path
if( dir ) dir = dir[0] == '.' || dir.match("://") ? dir : `.${dir}` if( directory ) directory = directory[0] == '.' || directory.match("://") ? directory : `.${directory}`
url = url.replace(dir,"")
loader = loader || new Loader().setPath( dir ) loader = loader || new Loader().setPath( URI.URN )
const onLoad = (model) => { const onLoad = (model) => {
model.file = file model.file = URI.file
// only change url when loading *another* file // only change url when loading *another* file
if( xrf.model ) xrf.navigator.pushState( `${dir}${file}`, hash ) if( xrf.model ){
xrf.navigator.pushState( external ? URI.URN + URI.file : URI.file, fragment )
}
//if( xrf.model ) xrf.navigator.pushState( `${ document.location.pathname != URI.directory ? URI.directory: ''}${URI.file}`, fragment )
xrf.model = model xrf.model = model
if( !model.isXRF ) xrf.parseModel(model,url) // this marks the model as an XRF model if( !model.isXRF ) xrf.parseModel(model,url.replace(directory,"")) // this marks the model as an XRF model
if(xrf.debug ) model.animations.map( (a) => console.log("anim: "+a.name) ) if(xrf.debug ) model.animations.map( (a) => console.log("anim: "+a.name) )
@ -1847,17 +2073,16 @@ xrf.navigator.to = (url,flags,loader,data) => {
xrf.XRWG.generate({model,scene:model.scene}) xrf.XRWG.generate({model,scene:model.scene})
// spec: 2. init metadata inside model for non-SRC data // spec: 2. init metadata inside model for non-SRC data
if( !model.isSRC ){ if( !model.isSRC ){
model.scene.traverse( (mesh) => xrf.parseModel.metadataInMesh(mesh,model) ) model.scene.traverse( (mesh) => xrf.parseModel.metadataInMesh(mesh,model) )
} }
// spec: 1. execute the default predefined view '#' (if exist) (https://xrfragment.org/#predefined_view) // spec: 1. execute the default predefined view '#' (if exist) (https://xrfragment.org/#predefined_view)
xrf.frag.defaultPredefinedViews({model,scene:model.scene}) xrf.frag.defaultPredefinedViews({model,scene:model.scene})
// spec: predefined view(s) & objects-of-interest-in-XRWG from URL (https://xrfragment.org/#predefined_view) // spec: predefined view(s) & objects-of-interest-in-XRWG from URI (https://xrfragment.org/#predefined_view)
let frag = xrf.hashbus.pub( url, model) // and eval URI XR fragments let frag = xrf.hashbus.pub( url, model) // and eval URI XR fragments
xrf.add( model.scene ) xrf.add( model.scene )
if( hash ) xrf.navigator.updateHash(hash) if( fragment ) xrf.navigator.updateHash(fragment)
xrf.emit('navigateLoaded',{url,model}) xrf.emit('navigateLoaded',{url,model})
resolve(model) resolve(model)
} }
@ -1866,7 +2091,7 @@ xrf.navigator.to = (url,flags,loader,data) => {
loader.parse(data, "", onLoad ) loader.parse(data, "", onLoad )
}else{ }else{
try{ try{
loader.load(url, onLoad ) loader.load(file, onLoad )
}catch(e){ }catch(e){
console.error(e) console.error(e)
xrf.emit('navigateError',{url}) xrf.emit('navigateError',{url})
@ -1880,9 +2105,12 @@ xrf.navigator.to = (url,flags,loader,data) => {
xrf.navigator.init = () => { xrf.navigator.init = () => {
if( xrf.navigator.init.inited ) return if( xrf.navigator.init.inited ) return
xrf.navigator.URI = xrfragment.URI.parse(document.location.href)
window.addEventListener('popstate', function (event){ window.addEventListener('popstate', function (event){
if( !xrf.navigator.updateHash.active ){ // ignore programmatic hash updates (causes infinite recursion) if( !xrf.navigator.updateHash.active ){ // ignore programmatic hash updates (causes infinite recursion)
xrf.navigator.to( document.location.search.substr(1) + document.location.hash ) //xrf.navigator.to( document.location.search.substr(1) + document.location.hash )
xrf.navigator.to( document.location.href.replace(/\?/,'') )
} }
}) })
@ -1908,10 +2136,10 @@ xrf.navigator.setupNavigateFallbacks = () => {
xrf.addEventListener('navigate', (opts) => { xrf.addEventListener('navigate', (opts) => {
let {url} = opts let {url} = opts
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url) let {fileExt} = xrfragment.URI.parse(url)
// handle http links // handle http links
if( url.match(/^http/) && !xrf.loaders[ext] ){ if( url.match(/^http/) && !xrf.loaders[fileExt] ){
let inIframe let inIframe
try { inIframe = window.self !== window.top; } catch (e) { inIframe = true; } try { inIframe = window.self !== window.top; } catch (e) { inIframe = true; }
return inIframe ? window.parent.postMessage({ url }, '*') : window.open( url, '_blank') return inIframe ? window.parent.postMessage({ url }, '*') : window.open( url, '_blank')
@ -1931,7 +2159,7 @@ xrf.navigator.setupNavigateFallbacks = () => {
xrf.navigator.updateHash = (hash,opts) => { xrf.navigator.updateHash = (hash,opts) => {
if( hash.replace(/^#/,'') == document.location.hash.substr(1) || hash.match(/\|/) ) return // skip unnecesary pushState triggers if( hash.replace(/^#/,'') == document.location.hash.substr(1) || hash.match(/\|/) ) return // skip unnecesary pushState triggers
console.log(`URL: ${document.location.search.substr(1)}#${hash}`) console.log(`URI: ${document.location.search.substr(1)}#${hash}`)
xrf.navigator.updateHash.active = true // important to prevent recursion xrf.navigator.updateHash.active = true // important to prevent recursion
document.location.hash = hash document.location.hash = hash
xrf.navigator.updateHash.active = false xrf.navigator.updateHash.active = false
@ -1942,6 +2170,23 @@ xrf.navigator.pushState = (file,hash) => {
window.history.pushState({},`${file}#${hash}`, document.location.pathname + `?${file}#${hash}` ) window.history.pushState({},`${file}#${hash}`, document.location.pathname + `?${file}#${hash}` )
xrf.emit('pushState', {file, hash} ) xrf.emit('pushState', {file, hash} )
} }
xrf.navigator.reactifyHash = ( obj ) => {
return new Proxy(obj,{
get(me,k) { return me[k] },
set(me,k,v){
me[k] = v
xrf.navigator.to( "#" + this.toString(me) )
},
toString(me){
let parts = []
Object.keys(me).map( (k) => {
parts.push( me[k] ? `${k}=${encodeURIComponent(me[k])}` : k )
})
return parts.join('&')
}
})
}
/** /**
* *
* navigation, portals & mutations * navigation, portals & mutations
@ -1993,7 +2238,6 @@ xrf.frag.href = function(v, opts){
.emit('href',{click:true,mesh,xrf:v}) // let all listeners agree .emit('href',{click:true,mesh,xrf:v}) // let all listeners agree
.then( () => { .then( () => {
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(v.string)
const isLocal = v.string[0] == '#' const isLocal = v.string[0] == '#'
const hasPos = isLocal && v.string.match(/pos=/) const hasPos = isLocal && v.string.match(/pos=/)
const flags = isLocal ? xrf.XRF.PV_OVERRIDE : undefined const flags = isLocal ? xrf.XRF.PV_OVERRIDE : undefined
@ -2153,6 +2397,32 @@ xrf.frag.pos = function(v, opts){
camera.updateMatrixWorld() camera.updateMatrixWorld()
} }
xrf.frag.pos.get = function(precision,randomize){
if( !precision ) precision = 2;
if( typeof THREE == 'undefined' ) THREE = xrf.THREE
let radToDeg = THREE.MathUtils.radToDeg
let toDeg = (x) => x / (Math.PI / 180)
let camera = xrf.camera
if( randomize ){
camera.position.x += Math.random()/10
camera.position.z += Math.random()/10
}
// *TODO* add camera direction
let direction = new xrf.THREE.Vector3()
camera.getWorldDirection(direction)
const pitch = Math.asin(direction.y);
const yaw = Math.atan2(direction.x, direction.z);
const pitchInDegrees = pitch * 180 / Math.PI;
const yawInDegrees = yaw * 180 / Math.PI;
return {
x: String(camera.position.x.toFixed(2)),
y: String(camera.position.y.toFixed(2)),
z: String(camera.position.z.toFixed(2)),
}
}
xrf.addEventListener('reset', (opts) => { xrf.addEventListener('reset', (opts) => {
// set the player to position 0,0,0 // set the player to position 0,0,0
xrf.camera.position.set(0,0,0) xrf.camera.position.set(0,0,0)
@ -2207,7 +2477,7 @@ xrf.frag.src = function(v, opts){
if( mesh.isSRC ) return // only embed src once if( mesh.isSRC ) return // only embed src once
let url = xrf.frag.src.expandURI( mesh, v.string ) let url = xrf.frag.src.expandURI( mesh, v.string )
let srcFrag = opts.srcFrag = xrfragment.URI.parse(url) let srcFrag = opts.srcFrag = xrfragment.URI.parse(url).XRF
opts.isLocal = v.string[0] == '#' opts.isLocal = v.string[0] == '#'
opts.isPortal = xrf.frag.src.renderAsPortal(mesh) opts.isPortal = xrf.frag.src.renderAsPortal(mesh)
opts.isSRC = mesh.isSRC = true opts.isSRC = mesh.isSRC = true
@ -2294,7 +2564,7 @@ xrf.frag.src.externalSRC = (url,frag,opts) => {
fetch(url, { method: 'HEAD' }) fetch(url, { method: 'HEAD' })
.then( (res) => { .then( (res) => {
let mimetype = res.headers.get('Content-type') let mimetype = res.headers.get('Content-type')
if(xrf.debug != undefined ) console.log("HEAD "+url+" => "+mimetype) if(xrf.debug > 0 ) console.log("HEAD "+url+" => "+mimetype)
if( url.replace(/#.*/,'').match(/\.(gltf|glb)$/) ) mimetype = 'gltf' if( url.replace(/#.*/,'').match(/\.(gltf|glb)$/) ) mimetype = 'gltf'
if( url.replace(/#.*/,'').match(/\.(frag|fs|glsl)$/) ) mimetype = 'x-shader/x-fragment' if( url.replace(/#.*/,'').match(/\.(frag|fs|glsl)$/) ) mimetype = 'x-shader/x-fragment'
if( url.replace(/#.*/,'').match(/\.(vert|vs)$/) ) mimetype = 'x-shader/x-fragment' if( url.replace(/#.*/,'').match(/\.(vert|vs)$/) ) mimetype = 'x-shader/x-fragment'
@ -3195,8 +3465,8 @@ xrf.addEventListener('render', (opts) => {
let loadAudio = (mimetype) => function(url,opts){ let loadAudio = (mimetype) => function(url,opts){
let {mesh,src,camera,THREE} = opts let {mesh,src,camera,THREE} = opts
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url) let URL = xrfragment.URI.toAbsolute( xrf.navigator.URI, url )
let frag = xrf.URI.parse( url ) let frag = URL.XRF
xrf.init.audio() xrf.init.audio()
let isPositionalAudio = !(mesh.position.x == 0 && mesh.position.y == 0 && mesh.position.z == 0) let isPositionalAudio = !(mesh.position.x == 0 && mesh.position.y == 0 && mesh.position.z == 0)
@ -3207,8 +3477,8 @@ let loadAudio = (mimetype) => function(url,opts){
mesh.media = mesh.media || {} mesh.media = mesh.media || {}
mesh.media.audio = { set: (mediafragment,v) => mesh.media.audio[mediafragment] = v } mesh.media.audio = { set: (mediafragment,v) => mesh.media.audio[mediafragment] = v }
let finalUrl = url.replace(/#.*/,'') let finalUrl = URL.URN + URL.file
if( xrf.debug != undefined ) console.log("GET "+finalUrl) if( xrf.debug > 0 ) console.log("GET "+finalUrl)
audioLoader.load( finalUrl, function( buffer ) { audioLoader.load( finalUrl, function( buffer ) {
sound.setBuffer( buffer ); sound.setBuffer( buffer );
@ -3318,7 +3588,8 @@ audioMimeTypes.map( (mimetype) => xrf.frag.src.type[ mimetype ] = loadAudio(mim
xrf.frag.src.type['fbx'] = function( url, opts ){ xrf.frag.src.type['fbx'] = function( url, opts ){
return new Promise( async (resolve,reject) => { return new Promise( async (resolve,reject) => {
let {mesh,src} = opts let {mesh,src} = opts
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url) let URL = xrfragment.URI.toAbsolute( xrf.navigator.URI, url )
let frag = URL.XRF
let loader let loader
let {THREE} = await import('https://unpkg.com/three@0.161.0/build/three.module.js') let {THREE} = await import('https://unpkg.com/three@0.161.0/build/three.module.js')
@ -3346,14 +3617,17 @@ xrf.frag.src.type['fbx'] = function( url, opts ){
xrf.frag.src.type['x-shader/x-fragment'] = function(url,opts){ xrf.frag.src.type['x-shader/x-fragment'] = function(url,opts){
let {mesh,THREE} = opts let {mesh,THREE} = opts
let URL = xrfragment.URI.toAbsolute( xrf.navigator.URI, url )
let frag = URL.XRF
let isFragmentShader = /\.(fs|frag|glsl)$/ let isFragmentShader = /\.(fs|frag|glsl)$/
let isVertexShader = /\.(vs|vert)$/ let isVertexShader = /\.(vs|vert)$/
let shaderReqs = [] let shaderReqs = []
let shaderCode = {} let shaderCode = {}
let shader = { let shader = {
fragment: { code: '', url: url.match( isFragmentShader ) ? url : '' }, fragment: { code: '', url: url.match( isFragmentShader ) ? URL.URN + URL.file : '' },
vertex: { code: '', url: url.match( isVertexShader ) ? url : '' } vertex: { code: '', url: url.match( isVertexShader ) ? URL.URN + URL.file : '' }
} }
var onShaderLoaded = ((args) => (type, status, code) => { var onShaderLoaded = ((args) => (type, status, code) => {
@ -3412,19 +3686,15 @@ xrf.frag.src.type['x-shader/x-vertex'] = xrf.frag.src.type['x-shader/x-fragmen
xrf.frag.src.type['gltf'] = function( url, opts ){ xrf.frag.src.type['gltf'] = function( url, opts ){
return new Promise( (resolve,reject) => { return new Promise( (resolve,reject) => {
let {mesh,src} = opts let {mesh,src} = opts
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url) let URL = xrfragment.URI.toAbsolute( xrf.navigator.URI, url )
let {directory,file,fileExt,URN} = URL;
let loader let loader
const Loader = xrf.loaders[ext] const Loader = xrf.loaders[fileExt]
if( !Loader ) throw 'xrfragment: no loader passed to xrfragment for extension .'+ext if( !Loader ) throw 'xrfragment: no loader passed to xrfragment for extension .'+ext
if( !dir.match("://") ){ // force relative path loader = new Loader().setPath( URN )
dir = dir.substr(0,2) == './' ? dir : `./${dir}`
loader = new Loader().setPath( dir )
}else{
loader = new Loader()
}
loader.load(url, (model) => { loader.load(file, (model) => {
model.isSRC = true model.isSRC = true
resolve(model) resolve(model)
}) })
@ -3435,7 +3705,7 @@ xrf.frag.src.type['gltf'] = function( url, opts ){
let loadHTML = (mimetype) => function(url,opts){ let loadHTML = (mimetype) => function(url,opts){
let {mesh,src,camera} = opts let {mesh,src,camera} = opts
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url) let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url)
let frag = xrf.URI.parse( url ) let frag = xrf.URI.parse( url ).XRF
console.warn("todo: html viewer for src not implemented") console.warn("todo: html viewer for src not implemented")
} }
@ -3452,6 +3722,8 @@ htmlMimeTypes.map( (mimetype) => xrf.frag.src.type[ mimetype ] = loadHTML(mimet
xrf.frag.src.type['image/png'] = function(url,opts){ xrf.frag.src.type['image/png'] = function(url,opts){
let {mesh,THREE} = opts let {mesh,THREE} = opts
let restrictTo3DBoundingBox = mesh.geometry let restrictTo3DBoundingBox = mesh.geometry
let URL = xrfragment.URI.toAbsolute( xrf.navigator.URI, url )
let frag = URL.XRF
mesh.material = new xrf.THREE.MeshBasicMaterial({ mesh.material = new xrf.THREE.MeshBasicMaterial({
map: null, map: null,
@ -3495,7 +3767,7 @@ xrf.frag.src.type['image/png'] = function(url,opts){
renderImage(texture) renderImage(texture)
} }
new THREE.TextureLoader().load( url, onLoad, null, console.error ); new THREE.TextureLoader().load( URL.URN + URL.file, onLoad, null, console.error );
} }
@ -3701,9 +3973,9 @@ xrf.portalNonEuclidian.stencilRef = 1
let loadVideo = (mimetype) => function(url,opts){ let loadVideo = (mimetype) => function(url,opts){
let {mesh,src,camera} = opts let {mesh,src,camera} = opts
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url)
const THREE = xrf.THREE const THREE = xrf.THREE
let frag = xrf.URI.parse( url ) let URL = xrfragment.URI.toAbsolute( xrf.navigator.URI, url )
let frag = URL.XRF
mesh.media = mesh.media || {} mesh.media = mesh.media || {}
@ -3725,7 +3997,7 @@ let loadVideo = (mimetype) => function(url,opts){
},false) },false)
}) })
video.src = url video.src = URL.URN + URL.file
video.speed = 1.0 video.speed = 1.0
video.looping = false video.looping = false
video.set = (mediafragment,v) => { video.set = (mediafragment,v) => {
@ -4161,6 +4433,17 @@ AFRAME.registerComponent('movement-controls', {
}()) }())
}); });
AFRAME.components['hand-tracking-controls'].Component.prototype.onModelLoaded = function(onModelLoaded){
return function(e){
onModelLoaded.apply(this);
// re-attach children
([...this.el.children]).map( (c) => {
if( c.object3D ){
this.el.object3D.getObjectByName("wrist").add(c.object3D)
}
})
}
}(AFRAME.components['hand-tracking-controls'].Component.prototype.onModelLoaded)
// look-controls turns off autoUpdateMatrix (of player) which // look-controls turns off autoUpdateMatrix (of player) which
// will break teleporting and other stuff // will break teleporting and other stuff
// overriding this is easier then adding updateMatrixWorld() everywhere else // overriding this is easier then adding updateMatrixWorld() everywhere else
@ -4280,7 +4563,7 @@ AFRAME.registerComponent('pressable', {
this.el.emit('click'); this.el.emit('click');
this.pressed = setTimeout( () => { this.pressed = setTimeout( () => {
this.el.emit('pressedended'); this.el.emit('pressedended');
this.pressed = null this.pressed = false
},300) },300)
} }
} }
@ -4438,8 +4721,10 @@ window.AFRAME.registerComponent('xrf-button', {
this.el.addEventListener('mouseenter', (e) => this.onMouseEnter(e) ); this.el.addEventListener('mouseenter', (e) => this.onMouseEnter(e) );
this.el.addEventListener('mouseleave', (e) => this.onMouseLeave(e) ); this.el.addEventListener('mouseleave', (e) => this.onMouseLeave(e) );
let cb = new Function(this.data.action)
if( this.data.action ){ if( this.data.action ){
this.el.addEventListener('click', new Function(this.data.action) ) this.el.addEventListener('click', AFRAME.utils.throttle(cb, 500 ) )
} }
}, },
bindMethods: function() { bindMethods: function() {
@ -4611,6 +4896,8 @@ window.AFRAME.registerComponent('xrf-get', {
var el = this.el; var el = this.el;
var meshname = this.data.name || this.data; var meshname = this.data.name || this.data;
if( !meshname || typeof meshname != 'string' ) return
this.el.addEventListener('update', (evt) => { this.el.addEventListener('update', (evt) => {
setTimeout( () => { setTimeout( () => {
@ -4647,7 +4934,8 @@ window.AFRAME.registerComponent('xrf-get', {
this.el.object3D.child = mesh // keep reference (because .children will be empty) this.el.object3D.child = mesh // keep reference (because .children will be empty)
if( !this.el.id ) this.el.setAttribute("id",`xrf-${mesh.name}`) if( !this.el.id ) this.el.setAttribute("id",`xrf-${mesh.name}`)
}else console.warn("xrf-get ignore: "+JSON.stringify(this.data)) }
}, evt && evt.timeout ? evt.timeout: 500) }, evt && evt.timeout ? evt.timeout: 500)
}) })

235
dist/xrfragment.js vendored
View File

@ -1133,9 +1133,13 @@ xrfragment_Parser.getMetaData = function() {
var meta = { title : ["title","og:title","dc.title"], description : ["aria-description","og:description","dc.description"], author : ["author","dc.creator"], publisher : ["publisher","dc.publisher"], website : ["og:site_name","og:url","dc.publisher"], license : ["SPDX","dc.rights"]}; var meta = { title : ["title","og:title","dc.title"], description : ["aria-description","og:description","dc.description"], author : ["author","dc.creator"], publisher : ["publisher","dc.publisher"], website : ["og:site_name","og:url","dc.publisher"], license : ["SPDX","dc.rights"]};
return meta; return meta;
}; };
var xrfragment_URI = $hx_exports["xrfragment"]["URI"] = function() { }; var xrfragment_URI = $hx_exports["xrfragment"]["URI"] = function() {
this.XRF = { };
this.hash = { };
this.fragment = "";
};
xrfragment_URI.__name__ = true; xrfragment_URI.__name__ = true;
xrfragment_URI.parse = function(url,filter) { xrfragment_URI.parseFragment = function(url,filter) {
var store = { }; var store = { };
if(url == null || url.indexOf("#") == -1) { if(url == null || url.indexOf("#") == -1) {
return store; return store;
@ -1187,6 +1191,232 @@ xrfragment_URI.template = function(uri,vars) {
parts[1] = frag; parts[1] = frag;
return parts.join("#"); return parts.join("#");
}; };
xrfragment_URI.parse = function(stringUrl,flags) {
var r = new EReg("^(?:(?![^:@]+:[^:@/]*@)([^:/?#.]+):)?(?://)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:/?#]*)(?::(\\d*))?)(((/(?:[^?#](?![^?#/]*\\.[^?#/.]+(?:[?#]|$)))*/?)?([^?#/]*))(?:\\?([^#]*))?(?:#(.*))?)","");
if(stringUrl.indexOf("://") == -1 && stringUrl.charAt(0) != "/") {
stringUrl = "/" + stringUrl;
}
r.match(stringUrl);
var url = new xrfragment_URI();
var _g = 0;
var _g1 = xrfragment_URI._parts.length;
while(_g < _g1) {
var i = _g++;
url[xrfragment_URI._parts[i]] = r.matched(i);
}
if(xrfragment_URI.isRelative(url) == true) {
if(url.directory == null && url.host != null) {
url.file = url.host;
}
}
url.hash = { };
if(url.fragment != null && url.fragment.length > 0) {
url.XRF = xrfragment_URI.parseFragment("#" + url.fragment,flags);
var key;
var _g = 0;
var _g1 = Reflect.fields(url.XRF);
while(_g < _g1.length) {
var key = _g1[_g];
++_g;
var v = url.XRF[key];
url.hash[key] = v["string"];
}
}
xrfragment_URI.computeVars(url);
return url;
};
xrfragment_URI.computeVars = function(url) {
var r_r = new RegExp("//","g".split("u").join(""));
if(url.directory != null && url.directory.indexOf("//") != -1) {
url.directory = url.directory.replace(r_r,"/");
}
if(url.path != null && url.path.indexOf("//") != -1) {
url.path = url.path.replace(r_r,"/");
}
if(url.file != null && url.file.indexOf("//") != -1) {
url.file = url.file.replace(r_r,"/");
}
url.URN = url.scheme + "://" + url.host;
if(url.port != null) {
url.URN += ":" + url.port;
}
url.URN += url.directory;
if(url.file != null) {
var parts = url.file.split(".");
if(parts.length > 1) {
url.fileExt = parts.pop();
}
}
};
xrfragment_URI.toString = function(url) {
var result = "";
if(url.scheme != null) {
result += url.scheme + "://";
}
if(url.user != null) {
result += url.user + ":";
}
if(url.password != null) {
result += url.password + "@";
}
if(url.host != null) {
result += url.host;
}
if(url.port != null) {
result += ":" + url.port;
}
if(url.directory != null) {
result += url.directory;
}
if(url.file != null) {
result += url.file;
}
if(url.query != null) {
result += "?" + url.query;
}
if(url.fragment != null) {
result += "#" + url.fragment;
}
return result;
};
xrfragment_URI.appendURI = function(url,appendedURI) {
if(xrfragment_URI.isRelative(url) == true) {
return xrfragment_URI.appendToRelativeURI(url,appendedURI);
} else {
return xrfragment_URI.appendToAbsoluteURI(url,appendedURI);
}
};
xrfragment_URI.isRelative = function(url) {
return url.scheme == null;
};
xrfragment_URI.appendToRelativeURI = function(url,appendedURI) {
if(url.directory == null || url.host == null) {
return xrfragment_URI.cloneURI(appendedURI);
}
var resultURI = new xrfragment_URI();
resultURI.host = url.host;
resultURI.directory = url.directory;
if(appendedURI.host != null) {
resultURI.directory += appendedURI.host;
}
if(appendedURI.directory != null) {
var directory = appendedURI.directory;
if(appendedURI.host == null) {
resultURI.directory += HxOverrides.substr(directory,1,null);
} else {
resultURI.directory += directory;
}
}
if(appendedURI.file != null) {
resultURI.file = appendedURI.file;
}
resultURI.path = resultURI.directory + resultURI.file;
if(appendedURI.query != null) {
resultURI.query = appendedURI.query;
}
if(appendedURI.fragment != null) {
resultURI.fragment = appendedURI.fragment;
}
return resultURI;
};
xrfragment_URI.appendToAbsoluteURI = function(url,appendedURI) {
var resultURI = new xrfragment_URI();
if(url.scheme != null) {
resultURI.scheme = url.scheme;
}
if(url.host != null) {
resultURI.host = url.host;
}
var directory = "";
if(url.directory != null) {
directory = url.directory;
}
if(appendedURI.host != null) {
appendedURI.directory += appendedURI.host;
}
if(appendedURI.directory != null) {
directory += appendedURI.directory;
}
resultURI.directory = directory;
if(appendedURI.file != null) {
resultURI.file = appendedURI.file;
}
resultURI.path = resultURI.directory + resultURI.file;
if(appendedURI.query != null) {
resultURI.query = appendedURI.query;
}
if(appendedURI.fragment != null) {
resultURI.fragment = appendedURI.fragment;
}
return resultURI;
};
xrfragment_URI.toAbsolute = function(url,newUrl) {
var newURI = xrfragment_URI.parse(newUrl,0);
var resultURI = new xrfragment_URI();
resultURI.port = url.port;
resultURI.source = newUrl;
if(newURI.scheme != null) {
resultURI.scheme = newURI.scheme;
} else {
resultURI.scheme = url.scheme;
}
if(newURI.host != null && newURI.host.length > 0) {
resultURI.host = newURI.host;
resultURI.port = null;
resultURI.fragment = null;
resultURI.hash = { };
resultURI.XRF = { };
if(newURI.port != null) {
resultURI.port = newURI.port;
}
} else {
resultURI.host = url.host;
}
var directory = "";
if(url.directory != null) {
directory = url.directory;
}
if(newURI.directory != null) {
if(newUrl.charAt(0) != "/" && newUrl.indexOf("://") == -1) {
directory += newURI.directory;
} else {
directory = newURI.directory;
}
}
resultURI.directory = directory;
if(newURI.file != null) {
resultURI.file = newURI.file;
}
resultURI.path = resultURI.directory + resultURI.file;
if(newURI.query != null) {
resultURI.query = newURI.query;
}
if(newURI.fragment != null) {
resultURI.fragment = newURI.fragment;
}
resultURI.hash = newURI.hash;
resultURI.XRF = newURI.XRF;
xrfragment_URI.computeVars(resultURI);
return resultURI;
};
xrfragment_URI.cloneURI = function(url) {
var clonedURI = new xrfragment_URI();
clonedURI.url = url.url;
clonedURI.source = url.source;
clonedURI.scheme = url.scheme;
clonedURI.authority = url.authority;
clonedURI.userInfo = url.userInfo;
clonedURI.password = url.password;
clonedURI.host = url.host;
clonedURI.port = url.port;
clonedURI.relative = url.relative;
clonedURI.path = url.path;
clonedURI.directory = url.directory;
clonedURI.file = url.file;
clonedURI.query = url.query;
clonedURI.fragment = url.fragment;
return clonedURI;
};
var xrfragment_XRF = $hx_exports["xrfragment"]["XRF"] = function(_fragment,_flags,_index) { var xrfragment_XRF = $hx_exports["xrfragment"]["XRF"] = function(_fragment,_flags,_index) {
this.floats = []; this.floats = [];
this.shift = []; this.shift = [];
@ -1297,6 +1527,7 @@ haxe_Template.hxKeepArrayIterator = new haxe_iterators_ArrayIterator([]);
xrfragment_Parser.error = ""; xrfragment_Parser.error = "";
xrfragment_Parser.debug = false; xrfragment_Parser.debug = false;
xrfragment_URI.__meta__ = { statics : { template : { keep : null}}}; xrfragment_URI.__meta__ = { statics : { template : { keep : null}}};
xrfragment_URI._parts = ["source","scheme","authority","userInfo","user","password","host","port","relative","path","directory","file","query","fragment"];
xrfragment_XRF.IMMUTABLE = 1; xrfragment_XRF.IMMUTABLE = 1;
xrfragment_XRF.PROP_BIND = 2; xrfragment_XRF.PROP_BIND = 2;
xrfragment_XRF.QUERY_OPERATOR = 4; xrfragment_XRF.QUERY_OPERATOR = 4;

383
dist/xrfragment.lua vendored
View File

@ -2899,10 +2899,19 @@ __xrfragment_Parser.getMetaData = function()
do return meta end; do return meta end;
end end
__xrfragment_URI.new = {} __xrfragment_URI.new = function()
local self = _hx_new(__xrfragment_URI.prototype)
__xrfragment_URI.super(self)
return self
end
__xrfragment_URI.super = function(self)
self.XRF = _hx_e();
self.hash = _hx_e();
self.fragment = "";
end
_hx_exports["xrfragment"]["URI"] = __xrfragment_URI _hx_exports["xrfragment"]["URI"] = __xrfragment_URI
__xrfragment_URI.__name__ = true __xrfragment_URI.__name__ = true
__xrfragment_URI.parse = function(url,filter) __xrfragment_URI.parseFragment = function(url,filter)
local store = _hx_e(); local store = _hx_e();
local tmp; local tmp;
if (url ~= nil) then if (url ~= nil) then
@ -3063,6 +3072,374 @@ __xrfragment_URI.template = function(uri,vars)
parts[1] = frag; parts[1] = frag;
do return parts:join("#") end; do return parts:join("#") end;
end end
__xrfragment_URI.parse = function(stringUrl,flags)
local r = EReg.new("^(?:(?![^:@]+:[^:@/]*@)([^:/?#.]+):)?(?://)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:/?#]*)(?::(\\d*))?)(((/(?:[^?#](?![^?#/]*\\.[^?#/.]+(?:[?#]|$)))*/?)?([^?#/]*))(?:\\?([^#]*))?(?:#(.*))?)", "");
local startIndex = nil;
if (startIndex == nil) then
startIndex = 1;
else
startIndex = startIndex + 1;
end;
local r1 = __lua_lib_luautf8_Utf8.find(stringUrl, "://", startIndex, true);
if (((function()
local _hx_1
if ((r1 ~= nil) and (r1 > 0)) then
_hx_1 = r1 - 1; else
_hx_1 = -1; end
return _hx_1
end )() == -1) and (__lua_lib_luautf8_Utf8.sub(stringUrl, 1, 1) ~= "/")) then
stringUrl = Std.string("/") .. Std.string(stringUrl);
end;
r:match(stringUrl);
local url = __xrfragment_URI.new();
local _g = 0;
local _g1 = __xrfragment_URI._parts.length;
while (_g < _g1) do
_g = _g + 1;
local i = _g - 1;
url[__xrfragment_URI._parts[i]] = r:matched(i);
end;
if (__xrfragment_URI.isRelative(url) == true) then
if ((url.directory == nil) and (url.host ~= nil)) then
url.file = url.host;
end;
end;
url.hash = _hx_e();
if ((url.fragment ~= nil) and (__lua_lib_luautf8_Utf8.len(url.fragment) > 0)) then
url.XRF = __xrfragment_URI.parseFragment(Std.string("#") .. Std.string(url.fragment), flags);
local key;
local _g = 0;
local _g1 = Reflect.fields(url.XRF);
while (_g < _g1.length) do
local key = _g1[_g];
_g = _g + 1;
local v = Reflect.field(url.XRF, key);
local this1 = url.hash;
local value = Reflect.field(v, "string");
this1[key] = value;
end;
end;
__xrfragment_URI.computeVars(url);
do return url end;
end
__xrfragment_URI.computeVars = function(url)
local r = EReg.new("//", "g");
local tmp;
if (url.directory ~= nil) then
local _this = url.directory;
local startIndex = nil;
if (startIndex == nil) then
startIndex = 1;
else
startIndex = startIndex + 1;
end;
local r = __lua_lib_luautf8_Utf8.find(_this, "//", startIndex, true);
tmp = (function()
local _hx_1
if ((r ~= nil) and (r > 0)) then
_hx_1 = r - 1; else
_hx_1 = -1; end
return _hx_1
end )() ~= -1;
else
tmp = false;
end;
if (tmp) then
url.directory = r:replace(url.directory, "/");
end;
local tmp;
if (url.path ~= nil) then
local _this = url.path;
local startIndex = nil;
if (startIndex == nil) then
startIndex = 1;
else
startIndex = startIndex + 1;
end;
local r = __lua_lib_luautf8_Utf8.find(_this, "//", startIndex, true);
tmp = (function()
local _hx_2
if ((r ~= nil) and (r > 0)) then
_hx_2 = r - 1; else
_hx_2 = -1; end
return _hx_2
end )() ~= -1;
else
tmp = false;
end;
if (tmp) then
url.path = r:replace(url.path, "/");
end;
local tmp;
if (url.file ~= nil) then
local _this = url.file;
local startIndex = nil;
if (startIndex == nil) then
startIndex = 1;
else
startIndex = startIndex + 1;
end;
local r = __lua_lib_luautf8_Utf8.find(_this, "//", startIndex, true);
tmp = (function()
local _hx_3
if ((r ~= nil) and (r > 0)) then
_hx_3 = r - 1; else
_hx_3 = -1; end
return _hx_3
end )() ~= -1;
else
tmp = false;
end;
if (tmp) then
url.file = r:replace(url.file, "/");
end;
url.URN = Std.string(Std.string(url.scheme) .. Std.string("://")) .. Std.string(url.host);
if (url.port ~= nil) then
local url1 = url;
url1.URN = Std.string(url1.URN) .. Std.string((Std.string(":") .. Std.string(url.port)));
end;
local url1 = url;
url1.URN = Std.string(url1.URN) .. Std.string(url.directory);
if (url.file ~= nil) then
local _this = url.file;
local idx = 1;
local ret = _hx_tab_array({}, 0);
while (idx ~= nil) do
local newidx = 0;
if (__lua_lib_luautf8_Utf8.len(".") > 0) then
newidx = __lua_lib_luautf8_Utf8.find(_this, ".", idx, true);
else
if (idx >= __lua_lib_luautf8_Utf8.len(_this)) then
newidx = nil;
else
newidx = idx + 1;
end;
end;
if (newidx ~= nil) then
local match = __lua_lib_luautf8_Utf8.sub(_this, idx, newidx - 1);
ret:push(match);
idx = newidx + __lua_lib_luautf8_Utf8.len(".");
else
ret:push(__lua_lib_luautf8_Utf8.sub(_this, idx, __lua_lib_luautf8_Utf8.len(_this)));
idx = nil;
end;
end;
local parts = ret;
if (parts.length > 1) then
url.fileExt = parts:pop();
end;
end;
end
__xrfragment_URI.toString = function(url)
local result = "";
if (url.scheme ~= nil) then
result = Std.string(result) .. Std.string((Std.string(url.scheme) .. Std.string("://")));
end;
if (url.user ~= nil) then
result = Std.string(result) .. Std.string((Std.string(url.user) .. Std.string(":")));
end;
if (url.password ~= nil) then
result = Std.string(result) .. Std.string((Std.string(url.password) .. Std.string("@")));
end;
if (url.host ~= nil) then
result = Std.string(result) .. Std.string(url.host);
end;
if (url.port ~= nil) then
result = Std.string(result) .. Std.string((Std.string(":") .. Std.string(url.port)));
end;
if (url.directory ~= nil) then
result = Std.string(result) .. Std.string(url.directory);
end;
if (url.file ~= nil) then
result = Std.string(result) .. Std.string(url.file);
end;
if (url.query ~= nil) then
result = Std.string(result) .. Std.string((Std.string("?") .. Std.string(url.query)));
end;
if (url.fragment ~= nil) then
result = Std.string(result) .. Std.string((Std.string("#") .. Std.string(url.fragment)));
end;
do return result end;
end
__xrfragment_URI.appendURI = function(url,appendedURI)
if (__xrfragment_URI.isRelative(url) == true) then
do return __xrfragment_URI.appendToRelativeURI(url, appendedURI) end;
else
do return __xrfragment_URI.appendToAbsoluteURI(url, appendedURI) end;
end;
end
__xrfragment_URI.isRelative = function(url)
do return url.scheme == nil end;
end
__xrfragment_URI.appendToRelativeURI = function(url,appendedURI)
if ((url.directory == nil) or (url.host == nil)) then
do return __xrfragment_URI.cloneURI(appendedURI) end;
end;
local resultURI = __xrfragment_URI.new();
resultURI.host = url.host;
resultURI.directory = url.directory;
if (appendedURI.host ~= nil) then
local resultURI = resultURI;
resultURI.directory = Std.string(resultURI.directory) .. Std.string(appendedURI.host);
end;
if (appendedURI.directory ~= nil) then
local directory = appendedURI.directory;
if (appendedURI.host == nil) then
local resultURI = resultURI;
local pos = 1;
local len = nil;
if ((len == nil) or (len > (pos + __lua_lib_luautf8_Utf8.len(directory)))) then
len = __lua_lib_luautf8_Utf8.len(directory);
else
if (len < 0) then
len = __lua_lib_luautf8_Utf8.len(directory) + len;
end;
end;
if (pos < 0) then
pos = __lua_lib_luautf8_Utf8.len(directory) + pos;
end;
if (pos < 0) then
pos = 0;
end;
resultURI.directory = Std.string(resultURI.directory) .. Std.string(__lua_lib_luautf8_Utf8.sub(directory, pos + 1, pos + len));
else
local resultURI = resultURI;
resultURI.directory = Std.string(resultURI.directory) .. Std.string(directory);
end;
end;
if (appendedURI.file ~= nil) then
resultURI.file = appendedURI.file;
end;
resultURI.path = Std.string(resultURI.directory) .. Std.string(resultURI.file);
if (appendedURI.query ~= nil) then
resultURI.query = appendedURI.query;
end;
if (appendedURI.fragment ~= nil) then
resultURI.fragment = appendedURI.fragment;
end;
do return resultURI end;
end
__xrfragment_URI.appendToAbsoluteURI = function(url,appendedURI)
local resultURI = __xrfragment_URI.new();
if (url.scheme ~= nil) then
resultURI.scheme = url.scheme;
end;
if (url.host ~= nil) then
resultURI.host = url.host;
end;
local directory = "";
if (url.directory ~= nil) then
directory = url.directory;
end;
if (appendedURI.host ~= nil) then
local appendedURI1 = appendedURI;
appendedURI1.directory = Std.string(appendedURI1.directory) .. Std.string(appendedURI.host);
end;
if (appendedURI.directory ~= nil) then
directory = Std.string(directory) .. Std.string(appendedURI.directory);
end;
resultURI.directory = directory;
if (appendedURI.file ~= nil) then
resultURI.file = appendedURI.file;
end;
resultURI.path = Std.string(resultURI.directory) .. Std.string(resultURI.file);
if (appendedURI.query ~= nil) then
resultURI.query = appendedURI.query;
end;
if (appendedURI.fragment ~= nil) then
resultURI.fragment = appendedURI.fragment;
end;
do return resultURI end;
end
__xrfragment_URI.toAbsolute = function(url,newUrl)
local newURI = __xrfragment_URI.parse(newUrl, 0);
local resultURI = __xrfragment_URI.new();
resultURI.port = url.port;
resultURI.source = newUrl;
if (newURI.scheme ~= nil) then
resultURI.scheme = newURI.scheme;
else
resultURI.scheme = url.scheme;
end;
if ((newURI.host ~= nil) and (__lua_lib_luautf8_Utf8.len(newURI.host) > 0)) then
resultURI.host = newURI.host;
resultURI.port = nil;
resultURI.fragment = nil;
resultURI.hash = _hx_e();
resultURI.XRF = _hx_e();
if (newURI.port ~= nil) then
resultURI.port = newURI.port;
end;
else
resultURI.host = url.host;
end;
local directory = "";
if (url.directory ~= nil) then
directory = url.directory;
end;
if (newURI.directory ~= nil) then
local tmp;
if (__lua_lib_luautf8_Utf8.sub(newUrl, 1, 1) ~= "/") then
local startIndex = nil;
if (startIndex == nil) then
startIndex = 1;
else
startIndex = startIndex + 1;
end;
local r = __lua_lib_luautf8_Utf8.find(newUrl, "://", startIndex, true);
tmp = (function()
local _hx_1
if ((r ~= nil) and (r > 0)) then
_hx_1 = r - 1; else
_hx_1 = -1; end
return _hx_1
end )() == -1;
else
tmp = false;
end;
if (tmp) then
directory = Std.string(directory) .. Std.string(newURI.directory);
else
directory = newURI.directory;
end;
end;
resultURI.directory = directory;
if (newURI.file ~= nil) then
resultURI.file = newURI.file;
end;
resultURI.path = Std.string(resultURI.directory) .. Std.string(resultURI.file);
if (newURI.query ~= nil) then
resultURI.query = newURI.query;
end;
if (newURI.fragment ~= nil) then
resultURI.fragment = newURI.fragment;
end;
resultURI.hash = newURI.hash;
resultURI.XRF = newURI.XRF;
__xrfragment_URI.computeVars(resultURI);
do return resultURI end;
end
__xrfragment_URI.cloneURI = function(url)
local clonedURI = __xrfragment_URI.new();
clonedURI.url = url.url;
clonedURI.source = url.source;
clonedURI.scheme = url.scheme;
clonedURI.authority = url.authority;
clonedURI.userInfo = url.userInfo;
clonedURI.password = url.password;
clonedURI.host = url.host;
clonedURI.port = url.port;
clonedURI.relative = url.relative;
clonedURI.path = url.path;
clonedURI.directory = url.directory;
clonedURI.file = url.file;
clonedURI.query = url.query;
clonedURI.fragment = url.fragment;
do return clonedURI end;
end
__xrfragment_URI.prototype = _hx_e();
__xrfragment_URI.prototype.__class__ = __xrfragment_URI
__xrfragment_XRF.new = function(_fragment,_flags,_index) __xrfragment_XRF.new = function(_fragment,_flags,_index)
local self = _hx_new(__xrfragment_XRF.prototype) local self = _hx_new(__xrfragment_XRF.prototype)
@ -3281,6 +3658,8 @@ local _hx_static_init = function()
__xrfragment_URI.__meta__ = _hx_o({__fields__={statics=true},statics=_hx_o({__fields__={template=true},template=_hx_o({__fields__={keep=true},keep=nil})})}); __xrfragment_URI.__meta__ = _hx_o({__fields__={statics=true},statics=_hx_o({__fields__={template=true},template=_hx_o({__fields__={keep=true},keep=nil})})});
__xrfragment_URI._parts = _hx_tab_array({[0]="source", "scheme", "authority", "userInfo", "user", "password", "host", "port", "relative", "path", "directory", "file", "query", "fragment"}, 14);
__xrfragment_XRF.IMMUTABLE = 1; __xrfragment_XRF.IMMUTABLE = 1;
__xrfragment_XRF.PROP_BIND = 2; __xrfragment_XRF.PROP_BIND = 2;

File diff suppressed because it is too large Load Diff

View File

@ -750,7 +750,7 @@ window.frontend = (opts) => new Proxy({
// load original scene and overwrite with updates // load original scene and overwrite with updates
let url = document.location.search.replace(/\?/,'') let url = document.location.search.replace(/\?/,'')
let {urlObj,dir,file,hash,ext} = xrf.navigator.origin = xrf.parseUrl(url) let {urlObj,dir,file,hash,ext} = xrf.navigator.origin = xrf.URI.parse(url)
const Loader = xrf.loaders[ext] const Loader = xrf.loaders[ext]
loader = new Loader().setPath( dir ) loader = new Loader().setPath( dir )
notify('exporting scene<br><br>please wait..') notify('exporting scene<br><br>please wait..')
@ -760,29 +760,8 @@ window.frontend = (opts) => new Proxy({
}, },
updateHashPosition(randomize){ updateHashPosition(randomize){
// *TODO* this should be part of the XRF Threejs framework const pos = xrf.frag.pos.get()
if( typeof THREE == 'undefined' ) THREE = xrf.THREE xrf.navigator.URI.hash.pos = `${pos.x},${pos.y},${pos.z}`
let radToDeg = THREE.MathUtils.radToDeg
let toDeg = (x) => x / (Math.PI / 180)
let camera = document.querySelector('[camera]').object3D.parent // *TODO* fix for threejs
camera.position.x += Math.random()/10
camera.position.z += Math.random()/10
// *TODO* add camera direction
let direction = new xrf.THREE.Vector3()
camera.getWorldDirection(direction)
const pitch = Math.asin(direction.y);
const yaw = Math.atan2(direction.x, direction.z);
const pitchInDegrees = pitch * 180 / Math.PI;
const yawInDegrees = yaw * 180 / Math.PI;
let lastPos = `pos=${camera.position.x.toFixed(2)},${camera.position.y.toFixed(2)},${camera.position.z.toFixed(2)}`
let newHash = document.location.hash.replace(/[&]?(pos|rot)=[0-9\.-]+,[0-9\.-]+,[0-9\.-]+/,'')
if( lastPos != "pos=" ){
newHash += `&${lastPos}`
document.location.hash = newHash.replace(/&&/,'&')
.replace(/#&/,'')
}
this.copyToClipboard( window.location.href ); this.copyToClipboard( window.location.href );
}, },
@ -802,8 +781,9 @@ window.frontend = (opts) => new Proxy({
document.location.hash += `&meet=${network.meetingLink}` document.location.hash += `&meet=${network.meetingLink}`
} }
if( !document.location.hash.match(/pos=/) && (network.posName || network.pos) ){ if( !document.location.hash.match(/pos=/) && (network.posName || network.pos) ){
document.location.hash += `&pos=${ network.posName || network.pos }` xrf.navigator.URI.hash.pos = network.posName || network.pos
} }else frontend.updateHashPosition()
let url = window.location.href let url = window.location.href
if( opts.linkonly ) return url if( opts.linkonly ) return url
this.copyToClipboard( url ) this.copyToClipboard( url )

View File

@ -316,7 +316,7 @@
this.ydoc.scene.set('href',document.location.hash ) this.ydoc.scene.set('href',document.location.hash )
} }
}) })
let hashvars = xrf.URI.parse( document.location.hash ) let hashvars = xrf.URI.parse( document.location.hash ).XRF
if( hashvars.meet ) this.parseLink(hashvars.meet.string) if( hashvars.meet ) this.parseLink(hashvars.meet.string)
} }
}, },

View File

@ -489,7 +489,7 @@ chatComponent = {
div.classList.add.apply(div.classList, opts.class.concat(["envelope"])) div.classList.add.apply(div.classList, opts.class.concat(["envelope"]))
} }
if( !msg.className.match(/(info|guide|ui)/) ){ if( !msg.className.match(/(info|guide|ui)/) ){
let frag = xrf.URI.parse(document.location.hash) let frag = xrf.URI.parse(document.location.hash).XRF
opts.from = 'you' opts.from = 'you'
if( frag.pos ) opts.pos = frag.pos.string if( frag.pos ) opts.pos = frag.pos.string
msg.classList.add('self') msg.classList.add('self')

View File

@ -260,7 +260,7 @@ window.trystero = (opts) => new Proxy({
let isTeleport = href.match(/(pos=|http:)/) let isTeleport = href.match(/(pos=|http:)/)
if( isLocal && !isTeleport && this.href.send ) this.href.send({href}) if( isLocal && !isTeleport && this.href.send ) this.href.send({href})
}) })
let hashvars = xrf.URI.parse( document.location.hash ) let hashvars = xrf.URI.parse( document.location.hash ).XRF
if( hashvars.meet ) this.parseLink(hashvars.meet.string) if( hashvars.meet ) this.parseLink(hashvars.meet.string)
} }

253
dist/xrfragment.py vendored
View File

@ -2303,11 +2303,33 @@ xrfragment_Parser._hx_class = xrfragment_Parser
class xrfragment_URI: class xrfragment_URI:
_hx_class_name = "xrfragment.URI" _hx_class_name = "xrfragment.URI"
__slots__ = () __slots__ = ("url", "source", "scheme", "authority", "userInfo", "user", "password", "host", "port", "relative", "path", "directory", "file", "fileExt", "query", "fragment", "hash", "XRF", "URN")
_hx_statics = ["__meta__", "parse", "template"] _hx_fields = ["url", "source", "scheme", "authority", "userInfo", "user", "password", "host", "port", "relative", "path", "directory", "file", "fileExt", "query", "fragment", "hash", "XRF", "URN"]
_hx_statics = ["__meta__", "_parts", "parseFragment", "template", "parse", "computeVars", "toString", "appendURI", "isRelative", "appendToRelativeURI", "appendToAbsoluteURI", "toAbsolute", "cloneURI"]
def __init__(self):
self.URN = None
self.query = None
self.fileExt = None
self.file = None
self.directory = None
self.path = None
self.relative = None
self.port = None
self.host = None
self.password = None
self.user = None
self.userInfo = None
self.authority = None
self.scheme = None
self.source = None
self.url = None
self.XRF = _hx_AnonObject({})
self.hash = _hx_AnonObject({})
self.fragment = ""
@staticmethod @staticmethod
def parse(url,_hx_filter): def parseFragment(url,_hx_filter):
store = _hx_AnonObject({}) store = _hx_AnonObject({})
tmp = None tmp = None
if (url is not None): if (url is not None):
@ -2362,6 +2384,230 @@ class xrfragment_URI:
frag = StringTools.replace(frag,"null","") frag = StringTools.replace(frag,"null","")
python_internal_ArrayImpl._set(parts, 1, frag) python_internal_ArrayImpl._set(parts, 1, frag)
return "#".join([python_Boot.toString1(x1,'') for x1 in parts]) return "#".join([python_Boot.toString1(x1,'') for x1 in parts])
@staticmethod
def parse(stringUrl,flags):
r = EReg("^(?:(?![^:@]+:[^:@/]*@)([^:/?#.]+):)?(?://)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:/?#]*)(?::(\\d*))?)(((/(?:[^?#](?![^?#/]*\\.[^?#/.]+(?:[?#]|$)))*/?)?([^?#/]*))(?:\\?([^#]*))?(?:#(.*))?)","")
startIndex = None
if ((((stringUrl.find("://") if ((startIndex is None)) else HxString.indexOfImpl(stringUrl,"://",startIndex))) == -1) and (((("" if ((0 >= len(stringUrl))) else stringUrl[0])) != "/"))):
stringUrl = ("/" + ("null" if stringUrl is None else stringUrl))
r.matchObj = python_lib_Re.search(r.pattern,stringUrl)
url = xrfragment_URI()
_g = 0
_g1 = len(xrfragment_URI._parts)
while (_g < _g1):
i = _g
_g = (_g + 1)
field = python_internal_ArrayImpl._get(xrfragment_URI._parts, i)
value = r.matchObj.group(i)
setattr(url,(("_hx_" + field) if ((field in python_Boot.keywords)) else (("_hx_" + field) if (((((len(field) > 2) and ((ord(field[0]) == 95))) and ((ord(field[1]) == 95))) and ((ord(field[(len(field) - 1)]) != 95)))) else field)),value)
if (xrfragment_URI.isRelative(url) == True):
if ((url.directory is None) and ((url.host is not None))):
url.file = url.host
url.hash = _hx_AnonObject({})
if ((url.fragment is not None) and ((len(url.fragment) > 0))):
url.XRF = xrfragment_URI.parseFragment(("#" + HxOverrides.stringOrNull(url.fragment)),flags)
key = None
_g = 0
_g1 = python_Boot.fields(url.XRF)
while (_g < len(_g1)):
key = (_g1[_g] if _g >= 0 and _g < len(_g1) else None)
_g = (_g + 1)
v = Reflect.field(url.XRF,key)
this1 = url.hash
value = Reflect.field(v,"string")
setattr(this1,(("_hx_" + key) if ((key in python_Boot.keywords)) else (("_hx_" + key) if (((((len(key) > 2) and ((ord(key[0]) == 95))) and ((ord(key[1]) == 95))) and ((ord(key[(len(key) - 1)]) != 95)))) else key)),value)
xrfragment_URI.computeVars(url)
return url
@staticmethod
def computeVars(url):
r = EReg("//","g")
tmp = None
if (url.directory is not None):
_this = url.directory
startIndex = None
tmp = (((_this.find("//") if ((startIndex is None)) else HxString.indexOfImpl(_this,"//",startIndex))) != -1)
else:
tmp = False
if tmp:
url.directory = r.replace(url.directory,"/")
tmp = None
if (url.path is not None):
_this = url.path
startIndex = None
tmp = (((_this.find("//") if ((startIndex is None)) else HxString.indexOfImpl(_this,"//",startIndex))) != -1)
else:
tmp = False
if tmp:
url.path = r.replace(url.path,"/")
tmp = None
if (url.file is not None):
_this = url.file
startIndex = None
tmp = (((_this.find("//") if ((startIndex is None)) else HxString.indexOfImpl(_this,"//",startIndex))) != -1)
else:
tmp = False
if tmp:
url.file = r.replace(url.file,"/")
url.URN = ((HxOverrides.stringOrNull(url.scheme) + "://") + HxOverrides.stringOrNull(url.host))
if (url.port is not None):
url.URN = (HxOverrides.stringOrNull(url.URN) + HxOverrides.stringOrNull(((":" + HxOverrides.stringOrNull(url.port)))))
url.URN = (HxOverrides.stringOrNull(url.URN) + HxOverrides.stringOrNull(url.directory))
if (url.file is not None):
_this = url.file
parts = _this.split(".")
if (len(parts) > 1):
url.fileExt = (None if ((len(parts) == 0)) else parts.pop())
@staticmethod
def toString(url):
result = ""
if (url.scheme is not None):
result = (("null" if result is None else result) + HxOverrides.stringOrNull(((HxOverrides.stringOrNull(url.scheme) + "://"))))
if (url.user is not None):
result = (("null" if result is None else result) + HxOverrides.stringOrNull(((HxOverrides.stringOrNull(url.user) + ":"))))
if (url.password is not None):
result = (("null" if result is None else result) + HxOverrides.stringOrNull(((HxOverrides.stringOrNull(url.password) + "@"))))
if (url.host is not None):
result = (("null" if result is None else result) + HxOverrides.stringOrNull(url.host))
if (url.port is not None):
result = (("null" if result is None else result) + HxOverrides.stringOrNull(((":" + HxOverrides.stringOrNull(url.port)))))
if (url.directory is not None):
result = (("null" if result is None else result) + HxOverrides.stringOrNull(url.directory))
if (url.file is not None):
result = (("null" if result is None else result) + HxOverrides.stringOrNull(url.file))
if (url.query is not None):
result = (("null" if result is None else result) + HxOverrides.stringOrNull((("?" + HxOverrides.stringOrNull(url.query)))))
if (url.fragment is not None):
result = (("null" if result is None else result) + HxOverrides.stringOrNull((("#" + HxOverrides.stringOrNull(url.fragment)))))
return result
@staticmethod
def appendURI(url,appendedURI):
if (xrfragment_URI.isRelative(url) == True):
return xrfragment_URI.appendToRelativeURI(url,appendedURI)
else:
return xrfragment_URI.appendToAbsoluteURI(url,appendedURI)
@staticmethod
def isRelative(url):
return (url.scheme is None)
@staticmethod
def appendToRelativeURI(url,appendedURI):
if ((url.directory is None) or ((url.host is None))):
return xrfragment_URI.cloneURI(appendedURI)
resultURI = xrfragment_URI()
resultURI.host = url.host
resultURI.directory = url.directory
if (appendedURI.host is not None):
resultURI.directory = (HxOverrides.stringOrNull(resultURI.directory) + HxOverrides.stringOrNull(appendedURI.host))
if (appendedURI.directory is not None):
directory = appendedURI.directory
if (appendedURI.host is None):
resultURI.directory = (HxOverrides.stringOrNull(resultURI.directory) + HxOverrides.stringOrNull(HxString.substr(directory,1,None)))
else:
resultURI.directory = (HxOverrides.stringOrNull(resultURI.directory) + ("null" if directory is None else directory))
if (appendedURI.file is not None):
resultURI.file = appendedURI.file
resultURI.path = (HxOverrides.stringOrNull(resultURI.directory) + HxOverrides.stringOrNull(resultURI.file))
if (appendedURI.query is not None):
resultURI.query = appendedURI.query
if (appendedURI.fragment is not None):
resultURI.fragment = appendedURI.fragment
return resultURI
@staticmethod
def appendToAbsoluteURI(url,appendedURI):
resultURI = xrfragment_URI()
if (url.scheme is not None):
resultURI.scheme = url.scheme
if (url.host is not None):
resultURI.host = url.host
directory = ""
if (url.directory is not None):
directory = url.directory
if (appendedURI.host is not None):
appendedURI.directory = (HxOverrides.stringOrNull(appendedURI.directory) + HxOverrides.stringOrNull(appendedURI.host))
if (appendedURI.directory is not None):
directory = (("null" if directory is None else directory) + HxOverrides.stringOrNull(appendedURI.directory))
resultURI.directory = directory
if (appendedURI.file is not None):
resultURI.file = appendedURI.file
resultURI.path = (HxOverrides.stringOrNull(resultURI.directory) + HxOverrides.stringOrNull(resultURI.file))
if (appendedURI.query is not None):
resultURI.query = appendedURI.query
if (appendedURI.fragment is not None):
resultURI.fragment = appendedURI.fragment
return resultURI
@staticmethod
def toAbsolute(url,newUrl):
newURI = xrfragment_URI.parse(newUrl,0)
resultURI = xrfragment_URI()
resultURI.port = url.port
resultURI.source = newUrl
if (newURI.scheme is not None):
resultURI.scheme = newURI.scheme
else:
resultURI.scheme = url.scheme
if ((newURI.host is not None) and ((len(newURI.host) > 0))):
resultURI.host = newURI.host
resultURI.port = None
resultURI.fragment = None
resultURI.hash = _hx_AnonObject({})
resultURI.XRF = _hx_AnonObject({})
if (newURI.port is not None):
resultURI.port = newURI.port
else:
resultURI.host = url.host
directory = ""
if (url.directory is not None):
directory = url.directory
if (newURI.directory is not None):
tmp = None
if ((("" if ((0 >= len(newUrl))) else newUrl[0])) != "/"):
startIndex = None
tmp = (((newUrl.find("://") if ((startIndex is None)) else HxString.indexOfImpl(newUrl,"://",startIndex))) == -1)
else:
tmp = False
if tmp:
directory = (("null" if directory is None else directory) + HxOverrides.stringOrNull(newURI.directory))
else:
directory = newURI.directory
resultURI.directory = directory
if (newURI.file is not None):
resultURI.file = newURI.file
resultURI.path = (HxOverrides.stringOrNull(resultURI.directory) + HxOverrides.stringOrNull(resultURI.file))
if (newURI.query is not None):
resultURI.query = newURI.query
if (newURI.fragment is not None):
resultURI.fragment = newURI.fragment
resultURI.hash = newURI.hash
resultURI.XRF = newURI.XRF
xrfragment_URI.computeVars(resultURI)
return resultURI
@staticmethod
def cloneURI(url):
clonedURI = xrfragment_URI()
clonedURI.url = url.url
clonedURI.source = url.source
clonedURI.scheme = url.scheme
clonedURI.authority = url.authority
clonedURI.userInfo = url.userInfo
clonedURI.password = url.password
clonedURI.host = url.host
clonedURI.port = url.port
clonedURI.relative = url.relative
clonedURI.path = url.path
clonedURI.directory = url.directory
clonedURI.file = url.file
clonedURI.query = url.query
clonedURI.fragment = url.fragment
return clonedURI
xrfragment_URI._hx_class = xrfragment_URI xrfragment_URI._hx_class = xrfragment_URI
@ -2496,6 +2742,7 @@ python_Boot.prefixLength = len("_hx_")
xrfragment_Parser.error = "" xrfragment_Parser.error = ""
xrfragment_Parser.debug = False xrfragment_Parser.debug = False
xrfragment_URI.__meta__ = _hx_AnonObject({'statics': _hx_AnonObject({'template': _hx_AnonObject({'keep': None})})}) xrfragment_URI.__meta__ = _hx_AnonObject({'statics': _hx_AnonObject({'template': _hx_AnonObject({'keep': None})})})
xrfragment_URI._parts = ["source", "scheme", "authority", "userInfo", "user", "password", "host", "port", "relative", "path", "directory", "file", "query", "fragment"]
xrfragment_XRF.__meta__ = _hx_AnonObject({'statics': _hx_AnonObject({'toDict': _hx_AnonObject({'keep': None})})}) xrfragment_XRF.__meta__ = _hx_AnonObject({'statics': _hx_AnonObject({'toDict': _hx_AnonObject({'keep': None})})})
xrfragment_XRF.IMMUTABLE = 1 xrfragment_XRF.IMMUTABLE = 1
xrfragment_XRF.PROP_BIND = 2 xrfragment_XRF.PROP_BIND = 2

View File

@ -1,5 +1,5 @@
/* /*
* v0.5.1 generated at Tue Mar 19 10:04:25 AM UTC 2024 * v0.5.1 generated at Tue Apr 16 12:47:01 PM UTC 2024
* https://xrfragment.org * https://xrfragment.org
* SPDX-License-Identifier: MPL-2.0 * SPDX-License-Identifier: MPL-2.0
*/ */
@ -1138,9 +1138,13 @@ xrfragment_Parser.getMetaData = function() {
var meta = { title : ["title","og:title","dc.title"], description : ["aria-description","og:description","dc.description"], author : ["author","dc.creator"], publisher : ["publisher","dc.publisher"], website : ["og:site_name","og:url","dc.publisher"], license : ["SPDX","dc.rights"]}; var meta = { title : ["title","og:title","dc.title"], description : ["aria-description","og:description","dc.description"], author : ["author","dc.creator"], publisher : ["publisher","dc.publisher"], website : ["og:site_name","og:url","dc.publisher"], license : ["SPDX","dc.rights"]};
return meta; return meta;
}; };
var xrfragment_URI = $hx_exports["xrfragment"]["URI"] = function() { }; var xrfragment_URI = $hx_exports["xrfragment"]["URI"] = function() {
this.XRF = { };
this.hash = { };
this.fragment = "";
};
xrfragment_URI.__name__ = true; xrfragment_URI.__name__ = true;
xrfragment_URI.parse = function(url,filter) { xrfragment_URI.parseFragment = function(url,filter) {
var store = { }; var store = { };
if(url == null || url.indexOf("#") == -1) { if(url == null || url.indexOf("#") == -1) {
return store; return store;
@ -1192,6 +1196,232 @@ xrfragment_URI.template = function(uri,vars) {
parts[1] = frag; parts[1] = frag;
return parts.join("#"); return parts.join("#");
}; };
xrfragment_URI.parse = function(stringUrl,flags) {
var r = new EReg("^(?:(?![^:@]+:[^:@/]*@)([^:/?#.]+):)?(?://)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:/?#]*)(?::(\\d*))?)(((/(?:[^?#](?![^?#/]*\\.[^?#/.]+(?:[?#]|$)))*/?)?([^?#/]*))(?:\\?([^#]*))?(?:#(.*))?)","");
if(stringUrl.indexOf("://") == -1 && stringUrl.charAt(0) != "/") {
stringUrl = "/" + stringUrl;
}
r.match(stringUrl);
var url = new xrfragment_URI();
var _g = 0;
var _g1 = xrfragment_URI._parts.length;
while(_g < _g1) {
var i = _g++;
url[xrfragment_URI._parts[i]] = r.matched(i);
}
if(xrfragment_URI.isRelative(url) == true) {
if(url.directory == null && url.host != null) {
url.file = url.host;
}
}
url.hash = { };
if(url.fragment != null && url.fragment.length > 0) {
url.XRF = xrfragment_URI.parseFragment("#" + url.fragment,flags);
var key;
var _g = 0;
var _g1 = Reflect.fields(url.XRF);
while(_g < _g1.length) {
var key = _g1[_g];
++_g;
var v = url.XRF[key];
url.hash[key] = v["string"];
}
}
xrfragment_URI.computeVars(url);
return url;
};
xrfragment_URI.computeVars = function(url) {
var r_r = new RegExp("//","g".split("u").join(""));
if(url.directory != null && url.directory.indexOf("//") != -1) {
url.directory = url.directory.replace(r_r,"/");
}
if(url.path != null && url.path.indexOf("//") != -1) {
url.path = url.path.replace(r_r,"/");
}
if(url.file != null && url.file.indexOf("//") != -1) {
url.file = url.file.replace(r_r,"/");
}
url.URN = url.scheme + "://" + url.host;
if(url.port != null) {
url.URN += ":" + url.port;
}
url.URN += url.directory;
if(url.file != null) {
var parts = url.file.split(".");
if(parts.length > 1) {
url.fileExt = parts.pop();
}
}
};
xrfragment_URI.toString = function(url) {
var result = "";
if(url.scheme != null) {
result += url.scheme + "://";
}
if(url.user != null) {
result += url.user + ":";
}
if(url.password != null) {
result += url.password + "@";
}
if(url.host != null) {
result += url.host;
}
if(url.port != null) {
result += ":" + url.port;
}
if(url.directory != null) {
result += url.directory;
}
if(url.file != null) {
result += url.file;
}
if(url.query != null) {
result += "?" + url.query;
}
if(url.fragment != null) {
result += "#" + url.fragment;
}
return result;
};
xrfragment_URI.appendURI = function(url,appendedURI) {
if(xrfragment_URI.isRelative(url) == true) {
return xrfragment_URI.appendToRelativeURI(url,appendedURI);
} else {
return xrfragment_URI.appendToAbsoluteURI(url,appendedURI);
}
};
xrfragment_URI.isRelative = function(url) {
return url.scheme == null;
};
xrfragment_URI.appendToRelativeURI = function(url,appendedURI) {
if(url.directory == null || url.host == null) {
return xrfragment_URI.cloneURI(appendedURI);
}
var resultURI = new xrfragment_URI();
resultURI.host = url.host;
resultURI.directory = url.directory;
if(appendedURI.host != null) {
resultURI.directory += appendedURI.host;
}
if(appendedURI.directory != null) {
var directory = appendedURI.directory;
if(appendedURI.host == null) {
resultURI.directory += HxOverrides.substr(directory,1,null);
} else {
resultURI.directory += directory;
}
}
if(appendedURI.file != null) {
resultURI.file = appendedURI.file;
}
resultURI.path = resultURI.directory + resultURI.file;
if(appendedURI.query != null) {
resultURI.query = appendedURI.query;
}
if(appendedURI.fragment != null) {
resultURI.fragment = appendedURI.fragment;
}
return resultURI;
};
xrfragment_URI.appendToAbsoluteURI = function(url,appendedURI) {
var resultURI = new xrfragment_URI();
if(url.scheme != null) {
resultURI.scheme = url.scheme;
}
if(url.host != null) {
resultURI.host = url.host;
}
var directory = "";
if(url.directory != null) {
directory = url.directory;
}
if(appendedURI.host != null) {
appendedURI.directory += appendedURI.host;
}
if(appendedURI.directory != null) {
directory += appendedURI.directory;
}
resultURI.directory = directory;
if(appendedURI.file != null) {
resultURI.file = appendedURI.file;
}
resultURI.path = resultURI.directory + resultURI.file;
if(appendedURI.query != null) {
resultURI.query = appendedURI.query;
}
if(appendedURI.fragment != null) {
resultURI.fragment = appendedURI.fragment;
}
return resultURI;
};
xrfragment_URI.toAbsolute = function(url,newUrl) {
var newURI = xrfragment_URI.parse(newUrl,0);
var resultURI = new xrfragment_URI();
resultURI.port = url.port;
resultURI.source = newUrl;
if(newURI.scheme != null) {
resultURI.scheme = newURI.scheme;
} else {
resultURI.scheme = url.scheme;
}
if(newURI.host != null && newURI.host.length > 0) {
resultURI.host = newURI.host;
resultURI.port = null;
resultURI.fragment = null;
resultURI.hash = { };
resultURI.XRF = { };
if(newURI.port != null) {
resultURI.port = newURI.port;
}
} else {
resultURI.host = url.host;
}
var directory = "";
if(url.directory != null) {
directory = url.directory;
}
if(newURI.directory != null) {
if(newUrl.charAt(0) != "/" && newUrl.indexOf("://") == -1) {
directory += newURI.directory;
} else {
directory = newURI.directory;
}
}
resultURI.directory = directory;
if(newURI.file != null) {
resultURI.file = newURI.file;
}
resultURI.path = resultURI.directory + resultURI.file;
if(newURI.query != null) {
resultURI.query = newURI.query;
}
if(newURI.fragment != null) {
resultURI.fragment = newURI.fragment;
}
resultURI.hash = newURI.hash;
resultURI.XRF = newURI.XRF;
xrfragment_URI.computeVars(resultURI);
return resultURI;
};
xrfragment_URI.cloneURI = function(url) {
var clonedURI = new xrfragment_URI();
clonedURI.url = url.url;
clonedURI.source = url.source;
clonedURI.scheme = url.scheme;
clonedURI.authority = url.authority;
clonedURI.userInfo = url.userInfo;
clonedURI.password = url.password;
clonedURI.host = url.host;
clonedURI.port = url.port;
clonedURI.relative = url.relative;
clonedURI.path = url.path;
clonedURI.directory = url.directory;
clonedURI.file = url.file;
clonedURI.query = url.query;
clonedURI.fragment = url.fragment;
return clonedURI;
};
var xrfragment_XRF = $hx_exports["xrfragment"]["XRF"] = function(_fragment,_flags,_index) { var xrfragment_XRF = $hx_exports["xrfragment"]["XRF"] = function(_fragment,_flags,_index) {
this.floats = []; this.floats = [];
this.shift = []; this.shift = [];
@ -1302,6 +1532,7 @@ haxe_Template.hxKeepArrayIterator = new haxe_iterators_ArrayIterator([]);
xrfragment_Parser.error = ""; xrfragment_Parser.error = "";
xrfragment_Parser.debug = false; xrfragment_Parser.debug = false;
xrfragment_URI.__meta__ = { statics : { template : { keep : null}}}; xrfragment_URI.__meta__ = { statics : { template : { keep : null}}};
xrfragment_URI._parts = ["source","scheme","authority","userInfo","user","password","host","port","relative","path","directory","file","query","fragment"];
xrfragment_XRF.IMMUTABLE = 1; xrfragment_XRF.IMMUTABLE = 1;
xrfragment_XRF.PROP_BIND = 2; xrfragment_XRF.PROP_BIND = 2;
xrfragment_XRF.QUERY_OPERATOR = 4; xrfragment_XRF.QUERY_OPERATOR = 4;
@ -1589,7 +1820,7 @@ let pub = function( url, node_or_model, flags ){ // evaluate fragments in url
if( !url ) return if( !url ) return
if( !url.match(/#/) ) url = `#${url}` if( !url.match(/#/) ) url = `#${url}`
let { THREE, camera } = xrf let { THREE, camera } = xrf
let frag = xrf.URI.parse( url, flags ) let frag = xrf.URI.parse( url, flags ).XRF
let fromNode = node_or_model != xrf.model let fromNode = node_or_model != xrf.model
let isNode = node_or_model && node_or_model.children let isNode = node_or_model && node_or_model.children
@ -1756,24 +1987,6 @@ xrf.reset = () => {
xrf.layers = 0 xrf.layers = 0
} }
xrf.parseUrl = (url) => {
let urlExHash = url.replace(/#.*/,'')
let urlObj,file
let store = {}
try{
urlObj = new URL( urlExHash.match(/:\/\//) ? urlExHash : String(`https://fake.com/${url}`).replace(/\/\//,'/') )
file = urlObj.pathname.substring(urlObj.pathname.lastIndexOf('/') + 1);
let search = urlObj.search.substr(1).split("&")
for( let i in search ) store[ (search[i].split("=")[0]) ] = search[i].split("=")[1] || ''
}catch(e){ }
let hashmap = url.match("#") ? url.replace(/.*#/,'').split("&") : []
for( let i in hashmap ) store[ (hashmap[i].split("=")[0]) ] = hashmap[i].split("=")[1] || ''
let dir = url.substring(0, url.lastIndexOf('/') + 1)
const hash = url.match(/#/) ? url.replace(/.*#/,'') : ''
const ext = file.split('.').pop()
return {urlObj,dir,file,hash,ext,store}
}
xrf.add = (object) => { xrf.add = (object) => {
object.isXRF = true // mark for easy deletion when replacing scene object.isXRF = true // mark for easy deletion when replacing scene
xrf.scene.add(object) xrf.scene.add(object)
@ -1784,42 +1997,52 @@ xrf.hasNoMaterial = (mesh) => {
const hasMaterialName = mesh.material && mesh.material.name.length > 0 const hasMaterialName = mesh.material && mesh.material.name.length > 0
return mesh.geometry && !hasMaterialName && !hasTexture return mesh.geometry && !hasMaterialName && !hasTexture
} }
xrf.navigator = {} xrf.navigator = {URI:{}}
xrf.navigator.to = (url,flags,loader,data) => { xrf.navigator.to = (url,flags,loader,data) => {
if( !url ) throw 'xrf.navigator.to(..) no url given' if( !url ) throw 'xrf.navigator.to(..) no url given'
let {urlObj,dir,file,hash,ext} = xrf.navigator.origin = xrf.parseUrl(url)
let hashChange = (!file && hash) || !data && xrf.model.file == file
let hasPos = String(hash).match(/pos=/)
let URI = xrfragment.URI.toAbsolute( xrf.navigator.URI, url )
URI.hash = xrf.navigator.reactifyHash(URI.hash)
let fileChange = URI.URN + URI.file != xrf.navigator.URI.URN + xrf.navigator.URI.file
let external = URI.URN != document.location.origin + document.location.pathname
let hasPos = URI.hash.pos
let hashChange = String(xrf.navigator.URI.fragment||"") != String(URI.fragment||"")
let hashbus = xrf.hashbus
xrf.navigator.URI = URI
let {directory,file,fragment,fileExt} = URI;
let hashbus = xrf.hashbus const evalFragment = () => {
if( URI.fragment ){
hashbus.pub( URI.fragment, xrf.model, flags ) // eval local URI XR fragments
xrf.navigator.updateHash(fragment) // which don't require
}
}
return new Promise( (resolve,reject) => { return new Promise( (resolve,reject) => {
xrf xrf
.emit('navigate', {url,loader,data}) .emit('navigate', {url,loader,data})
.then( () => { .then( () => {
if( ext && !loader ){ const Loader = xrf.loaders[fileExt]
const Loader = xrf.loaders[ext]
if( fileExt && !loader ){
if( !Loader ) return resolve() if( !Loader ) return resolve()
loader = loader || new Loader().setPath( dir ) loader = loader || new Loader().setPath( URI.URN )
} }
if( !hash && !file && !ext ) return resolve(xrf.model) // nothing we can do here if( !URI.fragment && !URI.file && !URI.fileExt ) return resolve(xrf.model) // nothing we can do here
if( hashChange && !hasPos ){ if( xrf.model && !fileChange && hashChange && !hasPos ){
hashbus.pub( url, xrf.model, flags ) // eval local URI XR fragments evalFragment()
xrf.navigator.updateHash(hash) // which don't require return resolve(xrf.model) // positional navigation
return resolve(xrf.model) // positional navigation
} }
xrf xrf
.emit('navigateLoading', {url,loader,data}) .emit('navigateLoading', {url,loader,data})
.then( () => { .then( () => {
if( hashChange && hasPos ){ // we're already loaded if( (!fileChange || !file) && hashChange && hasPos ){ // we're already loaded
hashbus.pub( url, xrf.model, flags ) // and eval local URI XR fragments evalFragment()
xrf.navigator.updateHash(hash)
xrf.emit('navigateLoaded',{url}) xrf.emit('navigateLoaded',{url})
return resolve(xrf.model) return resolve(xrf.model)
} }
@ -1829,17 +2052,20 @@ xrf.navigator.to = (url,flags,loader,data) => {
xrf.reset() xrf.reset()
// force relative path for files which dont include protocol or relative path // force relative path for files which dont include protocol or relative path
if( dir ) dir = dir[0] == '.' || dir.match("://") ? dir : `.${dir}` if( directory ) directory = directory[0] == '.' || directory.match("://") ? directory : `.${directory}`
url = url.replace(dir,"")
loader = loader || new Loader().setPath( dir ) loader = loader || new Loader().setPath( URI.URN )
const onLoad = (model) => { const onLoad = (model) => {
model.file = file model.file = URI.file
// only change url when loading *another* file // only change url when loading *another* file
if( xrf.model ) xrf.navigator.pushState( `${dir}${file}`, hash ) if( xrf.model ){
xrf.navigator.pushState( external ? URI.URN + URI.file : URI.file, fragment )
}
//if( xrf.model ) xrf.navigator.pushState( `${ document.location.pathname != URI.directory ? URI.directory: ''}${URI.file}`, fragment )
xrf.model = model xrf.model = model
if( !model.isXRF ) xrf.parseModel(model,url) // this marks the model as an XRF model if( !model.isXRF ) xrf.parseModel(model,url.replace(directory,"")) // this marks the model as an XRF model
if(xrf.debug ) model.animations.map( (a) => console.log("anim: "+a.name) ) if(xrf.debug ) model.animations.map( (a) => console.log("anim: "+a.name) )
@ -1847,17 +2073,16 @@ xrf.navigator.to = (url,flags,loader,data) => {
xrf.XRWG.generate({model,scene:model.scene}) xrf.XRWG.generate({model,scene:model.scene})
// spec: 2. init metadata inside model for non-SRC data // spec: 2. init metadata inside model for non-SRC data
if( !model.isSRC ){ if( !model.isSRC ){
model.scene.traverse( (mesh) => xrf.parseModel.metadataInMesh(mesh,model) ) model.scene.traverse( (mesh) => xrf.parseModel.metadataInMesh(mesh,model) )
} }
// spec: 1. execute the default predefined view '#' (if exist) (https://xrfragment.org/#predefined_view) // spec: 1. execute the default predefined view '#' (if exist) (https://xrfragment.org/#predefined_view)
xrf.frag.defaultPredefinedViews({model,scene:model.scene}) xrf.frag.defaultPredefinedViews({model,scene:model.scene})
// spec: predefined view(s) & objects-of-interest-in-XRWG from URL (https://xrfragment.org/#predefined_view) // spec: predefined view(s) & objects-of-interest-in-XRWG from URI (https://xrfragment.org/#predefined_view)
let frag = xrf.hashbus.pub( url, model) // and eval URI XR fragments let frag = xrf.hashbus.pub( url, model) // and eval URI XR fragments
xrf.add( model.scene ) xrf.add( model.scene )
if( hash ) xrf.navigator.updateHash(hash) if( fragment ) xrf.navigator.updateHash(fragment)
xrf.emit('navigateLoaded',{url,model}) xrf.emit('navigateLoaded',{url,model})
resolve(model) resolve(model)
} }
@ -1866,7 +2091,7 @@ xrf.navigator.to = (url,flags,loader,data) => {
loader.parse(data, "", onLoad ) loader.parse(data, "", onLoad )
}else{ }else{
try{ try{
loader.load(url, onLoad ) loader.load(file, onLoad )
}catch(e){ }catch(e){
console.error(e) console.error(e)
xrf.emit('navigateError',{url}) xrf.emit('navigateError',{url})
@ -1880,9 +2105,12 @@ xrf.navigator.to = (url,flags,loader,data) => {
xrf.navigator.init = () => { xrf.navigator.init = () => {
if( xrf.navigator.init.inited ) return if( xrf.navigator.init.inited ) return
xrf.navigator.URI = xrfragment.URI.parse(document.location.href)
window.addEventListener('popstate', function (event){ window.addEventListener('popstate', function (event){
if( !xrf.navigator.updateHash.active ){ // ignore programmatic hash updates (causes infinite recursion) if( !xrf.navigator.updateHash.active ){ // ignore programmatic hash updates (causes infinite recursion)
xrf.navigator.to( document.location.search.substr(1) + document.location.hash ) //xrf.navigator.to( document.location.search.substr(1) + document.location.hash )
xrf.navigator.to( document.location.href.replace(/\?/,'') )
} }
}) })
@ -1908,10 +2136,10 @@ xrf.navigator.setupNavigateFallbacks = () => {
xrf.addEventListener('navigate', (opts) => { xrf.addEventListener('navigate', (opts) => {
let {url} = opts let {url} = opts
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url) let {fileExt} = xrfragment.URI.parse(url)
// handle http links // handle http links
if( url.match(/^http/) && !xrf.loaders[ext] ){ if( url.match(/^http/) && !xrf.loaders[fileExt] ){
let inIframe let inIframe
try { inIframe = window.self !== window.top; } catch (e) { inIframe = true; } try { inIframe = window.self !== window.top; } catch (e) { inIframe = true; }
return inIframe ? window.parent.postMessage({ url }, '*') : window.open( url, '_blank') return inIframe ? window.parent.postMessage({ url }, '*') : window.open( url, '_blank')
@ -1931,7 +2159,7 @@ xrf.navigator.setupNavigateFallbacks = () => {
xrf.navigator.updateHash = (hash,opts) => { xrf.navigator.updateHash = (hash,opts) => {
if( hash.replace(/^#/,'') == document.location.hash.substr(1) || hash.match(/\|/) ) return // skip unnecesary pushState triggers if( hash.replace(/^#/,'') == document.location.hash.substr(1) || hash.match(/\|/) ) return // skip unnecesary pushState triggers
console.log(`URL: ${document.location.search.substr(1)}#${hash}`) console.log(`URI: ${document.location.search.substr(1)}#${hash}`)
xrf.navigator.updateHash.active = true // important to prevent recursion xrf.navigator.updateHash.active = true // important to prevent recursion
document.location.hash = hash document.location.hash = hash
xrf.navigator.updateHash.active = false xrf.navigator.updateHash.active = false
@ -1942,6 +2170,23 @@ xrf.navigator.pushState = (file,hash) => {
window.history.pushState({},`${file}#${hash}`, document.location.pathname + `?${file}#${hash}` ) window.history.pushState({},`${file}#${hash}`, document.location.pathname + `?${file}#${hash}` )
xrf.emit('pushState', {file, hash} ) xrf.emit('pushState', {file, hash} )
} }
xrf.navigator.reactifyHash = ( obj ) => {
return new Proxy(obj,{
get(me,k) { return me[k] },
set(me,k,v){
me[k] = v
xrf.navigator.to( "#" + this.toString(me) )
},
toString(me){
let parts = []
Object.keys(me).map( (k) => {
parts.push( me[k] ? `${k}=${encodeURIComponent(me[k])}` : k )
})
return parts.join('&')
}
})
}
/** /**
* *
* navigation, portals & mutations * navigation, portals & mutations
@ -1993,7 +2238,6 @@ xrf.frag.href = function(v, opts){
.emit('href',{click:true,mesh,xrf:v}) // let all listeners agree .emit('href',{click:true,mesh,xrf:v}) // let all listeners agree
.then( () => { .then( () => {
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(v.string)
const isLocal = v.string[0] == '#' const isLocal = v.string[0] == '#'
const hasPos = isLocal && v.string.match(/pos=/) const hasPos = isLocal && v.string.match(/pos=/)
const flags = isLocal ? xrf.XRF.PV_OVERRIDE : undefined const flags = isLocal ? xrf.XRF.PV_OVERRIDE : undefined
@ -2153,6 +2397,32 @@ xrf.frag.pos = function(v, opts){
camera.updateMatrixWorld() camera.updateMatrixWorld()
} }
xrf.frag.pos.get = function(precision,randomize){
if( !precision ) precision = 2;
if( typeof THREE == 'undefined' ) THREE = xrf.THREE
let radToDeg = THREE.MathUtils.radToDeg
let toDeg = (x) => x / (Math.PI / 180)
let camera = xrf.camera
if( randomize ){
camera.position.x += Math.random()/10
camera.position.z += Math.random()/10
}
// *TODO* add camera direction
let direction = new xrf.THREE.Vector3()
camera.getWorldDirection(direction)
const pitch = Math.asin(direction.y);
const yaw = Math.atan2(direction.x, direction.z);
const pitchInDegrees = pitch * 180 / Math.PI;
const yawInDegrees = yaw * 180 / Math.PI;
return {
x: String(camera.position.x.toFixed(2)),
y: String(camera.position.y.toFixed(2)),
z: String(camera.position.z.toFixed(2)),
}
}
xrf.addEventListener('reset', (opts) => { xrf.addEventListener('reset', (opts) => {
// set the player to position 0,0,0 // set the player to position 0,0,0
xrf.camera.position.set(0,0,0) xrf.camera.position.set(0,0,0)
@ -2207,7 +2477,7 @@ xrf.frag.src = function(v, opts){
if( mesh.isSRC ) return // only embed src once if( mesh.isSRC ) return // only embed src once
let url = xrf.frag.src.expandURI( mesh, v.string ) let url = xrf.frag.src.expandURI( mesh, v.string )
let srcFrag = opts.srcFrag = xrfragment.URI.parse(url) let srcFrag = opts.srcFrag = xrfragment.URI.parse(url).XRF
opts.isLocal = v.string[0] == '#' opts.isLocal = v.string[0] == '#'
opts.isPortal = xrf.frag.src.renderAsPortal(mesh) opts.isPortal = xrf.frag.src.renderAsPortal(mesh)
opts.isSRC = mesh.isSRC = true opts.isSRC = mesh.isSRC = true
@ -2294,7 +2564,7 @@ xrf.frag.src.externalSRC = (url,frag,opts) => {
fetch(url, { method: 'HEAD' }) fetch(url, { method: 'HEAD' })
.then( (res) => { .then( (res) => {
let mimetype = res.headers.get('Content-type') let mimetype = res.headers.get('Content-type')
if(xrf.debug != undefined ) console.log("HEAD "+url+" => "+mimetype) if(xrf.debug > 0 ) console.log("HEAD "+url+" => "+mimetype)
if( url.replace(/#.*/,'').match(/\.(gltf|glb)$/) ) mimetype = 'gltf' if( url.replace(/#.*/,'').match(/\.(gltf|glb)$/) ) mimetype = 'gltf'
if( url.replace(/#.*/,'').match(/\.(frag|fs|glsl)$/) ) mimetype = 'x-shader/x-fragment' if( url.replace(/#.*/,'').match(/\.(frag|fs|glsl)$/) ) mimetype = 'x-shader/x-fragment'
if( url.replace(/#.*/,'').match(/\.(vert|vs)$/) ) mimetype = 'x-shader/x-fragment' if( url.replace(/#.*/,'').match(/\.(vert|vs)$/) ) mimetype = 'x-shader/x-fragment'
@ -3195,8 +3465,8 @@ xrf.addEventListener('render', (opts) => {
let loadAudio = (mimetype) => function(url,opts){ let loadAudio = (mimetype) => function(url,opts){
let {mesh,src,camera,THREE} = opts let {mesh,src,camera,THREE} = opts
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url) let URL = xrfragment.URI.toAbsolute( xrf.navigator.URI, url )
let frag = xrf.URI.parse( url ) let frag = URL.XRF
xrf.init.audio() xrf.init.audio()
let isPositionalAudio = !(mesh.position.x == 0 && mesh.position.y == 0 && mesh.position.z == 0) let isPositionalAudio = !(mesh.position.x == 0 && mesh.position.y == 0 && mesh.position.z == 0)
@ -3207,8 +3477,8 @@ let loadAudio = (mimetype) => function(url,opts){
mesh.media = mesh.media || {} mesh.media = mesh.media || {}
mesh.media.audio = { set: (mediafragment,v) => mesh.media.audio[mediafragment] = v } mesh.media.audio = { set: (mediafragment,v) => mesh.media.audio[mediafragment] = v }
let finalUrl = url.replace(/#.*/,'') let finalUrl = URL.URN + URL.file
if( xrf.debug != undefined ) console.log("GET "+finalUrl) if( xrf.debug > 0 ) console.log("GET "+finalUrl)
audioLoader.load( finalUrl, function( buffer ) { audioLoader.load( finalUrl, function( buffer ) {
sound.setBuffer( buffer ); sound.setBuffer( buffer );
@ -3318,7 +3588,8 @@ audioMimeTypes.map( (mimetype) => xrf.frag.src.type[ mimetype ] = loadAudio(mim
xrf.frag.src.type['fbx'] = function( url, opts ){ xrf.frag.src.type['fbx'] = function( url, opts ){
return new Promise( async (resolve,reject) => { return new Promise( async (resolve,reject) => {
let {mesh,src} = opts let {mesh,src} = opts
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url) let URL = xrfragment.URI.toAbsolute( xrf.navigator.URI, url )
let frag = URL.XRF
let loader let loader
let {THREE} = await import('https://unpkg.com/three@0.161.0/build/three.module.js') let {THREE} = await import('https://unpkg.com/three@0.161.0/build/three.module.js')
@ -3346,14 +3617,17 @@ xrf.frag.src.type['fbx'] = function( url, opts ){
xrf.frag.src.type['x-shader/x-fragment'] = function(url,opts){ xrf.frag.src.type['x-shader/x-fragment'] = function(url,opts){
let {mesh,THREE} = opts let {mesh,THREE} = opts
let URL = xrfragment.URI.toAbsolute( xrf.navigator.URI, url )
let frag = URL.XRF
let isFragmentShader = /\.(fs|frag|glsl)$/ let isFragmentShader = /\.(fs|frag|glsl)$/
let isVertexShader = /\.(vs|vert)$/ let isVertexShader = /\.(vs|vert)$/
let shaderReqs = [] let shaderReqs = []
let shaderCode = {} let shaderCode = {}
let shader = { let shader = {
fragment: { code: '', url: url.match( isFragmentShader ) ? url : '' }, fragment: { code: '', url: url.match( isFragmentShader ) ? URL.URN + URL.file : '' },
vertex: { code: '', url: url.match( isVertexShader ) ? url : '' } vertex: { code: '', url: url.match( isVertexShader ) ? URL.URN + URL.file : '' }
} }
var onShaderLoaded = ((args) => (type, status, code) => { var onShaderLoaded = ((args) => (type, status, code) => {
@ -3412,19 +3686,15 @@ xrf.frag.src.type['x-shader/x-vertex'] = xrf.frag.src.type['x-shader/x-fragmen
xrf.frag.src.type['gltf'] = function( url, opts ){ xrf.frag.src.type['gltf'] = function( url, opts ){
return new Promise( (resolve,reject) => { return new Promise( (resolve,reject) => {
let {mesh,src} = opts let {mesh,src} = opts
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url) let URL = xrfragment.URI.toAbsolute( xrf.navigator.URI, url )
let {directory,file,fileExt,URN} = URL;
let loader let loader
const Loader = xrf.loaders[ext] const Loader = xrf.loaders[fileExt]
if( !Loader ) throw 'xrfragment: no loader passed to xrfragment for extension .'+ext if( !Loader ) throw 'xrfragment: no loader passed to xrfragment for extension .'+ext
if( !dir.match("://") ){ // force relative path loader = new Loader().setPath( URN )
dir = dir.substr(0,2) == './' ? dir : `./${dir}`
loader = new Loader().setPath( dir )
}else{
loader = new Loader()
}
loader.load(url, (model) => { loader.load(file, (model) => {
model.isSRC = true model.isSRC = true
resolve(model) resolve(model)
}) })
@ -3435,7 +3705,7 @@ xrf.frag.src.type['gltf'] = function( url, opts ){
let loadHTML = (mimetype) => function(url,opts){ let loadHTML = (mimetype) => function(url,opts){
let {mesh,src,camera} = opts let {mesh,src,camera} = opts
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url) let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url)
let frag = xrf.URI.parse( url ) let frag = xrf.URI.parse( url ).XRF
console.warn("todo: html viewer for src not implemented") console.warn("todo: html viewer for src not implemented")
} }
@ -3452,6 +3722,8 @@ htmlMimeTypes.map( (mimetype) => xrf.frag.src.type[ mimetype ] = loadHTML(mimet
xrf.frag.src.type['image/png'] = function(url,opts){ xrf.frag.src.type['image/png'] = function(url,opts){
let {mesh,THREE} = opts let {mesh,THREE} = opts
let restrictTo3DBoundingBox = mesh.geometry let restrictTo3DBoundingBox = mesh.geometry
let URL = xrfragment.URI.toAbsolute( xrf.navigator.URI, url )
let frag = URL.XRF
mesh.material = new xrf.THREE.MeshBasicMaterial({ mesh.material = new xrf.THREE.MeshBasicMaterial({
map: null, map: null,
@ -3495,7 +3767,7 @@ xrf.frag.src.type['image/png'] = function(url,opts){
renderImage(texture) renderImage(texture)
} }
new THREE.TextureLoader().load( url, onLoad, null, console.error ); new THREE.TextureLoader().load( URL.URN + URL.file, onLoad, null, console.error );
} }
@ -3701,9 +3973,9 @@ xrf.portalNonEuclidian.stencilRef = 1
let loadVideo = (mimetype) => function(url,opts){ let loadVideo = (mimetype) => function(url,opts){
let {mesh,src,camera} = opts let {mesh,src,camera} = opts
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url)
const THREE = xrf.THREE const THREE = xrf.THREE
let frag = xrf.URI.parse( url ) let URL = xrfragment.URI.toAbsolute( xrf.navigator.URI, url )
let frag = URL.XRF
mesh.media = mesh.media || {} mesh.media = mesh.media || {}
@ -3725,7 +3997,7 @@ let loadVideo = (mimetype) => function(url,opts){
},false) },false)
}) })
video.src = url video.src = URL.URN + URL.file
video.speed = 1.0 video.speed = 1.0
video.looping = false video.looping = false
video.set = (mediafragment,v) => { video.set = (mediafragment,v) => {

View File

@ -1,5 +1,5 @@
/* /*
* v0.5.1 generated at Tue Mar 19 10:04:25 AM UTC 2024 * v0.5.1 generated at Tue Apr 16 12:47:01 PM UTC 2024
* https://xrfragment.org * https://xrfragment.org
* SPDX-License-Identifier: MPL-2.0 * SPDX-License-Identifier: MPL-2.0
*/ */
@ -1138,9 +1138,13 @@ xrfragment_Parser.getMetaData = function() {
var meta = { title : ["title","og:title","dc.title"], description : ["aria-description","og:description","dc.description"], author : ["author","dc.creator"], publisher : ["publisher","dc.publisher"], website : ["og:site_name","og:url","dc.publisher"], license : ["SPDX","dc.rights"]}; var meta = { title : ["title","og:title","dc.title"], description : ["aria-description","og:description","dc.description"], author : ["author","dc.creator"], publisher : ["publisher","dc.publisher"], website : ["og:site_name","og:url","dc.publisher"], license : ["SPDX","dc.rights"]};
return meta; return meta;
}; };
var xrfragment_URI = $hx_exports["xrfragment"]["URI"] = function() { }; var xrfragment_URI = $hx_exports["xrfragment"]["URI"] = function() {
this.XRF = { };
this.hash = { };
this.fragment = "";
};
xrfragment_URI.__name__ = true; xrfragment_URI.__name__ = true;
xrfragment_URI.parse = function(url,filter) { xrfragment_URI.parseFragment = function(url,filter) {
var store = { }; var store = { };
if(url == null || url.indexOf("#") == -1) { if(url == null || url.indexOf("#") == -1) {
return store; return store;
@ -1192,6 +1196,232 @@ xrfragment_URI.template = function(uri,vars) {
parts[1] = frag; parts[1] = frag;
return parts.join("#"); return parts.join("#");
}; };
xrfragment_URI.parse = function(stringUrl,flags) {
var r = new EReg("^(?:(?![^:@]+:[^:@/]*@)([^:/?#.]+):)?(?://)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:/?#]*)(?::(\\d*))?)(((/(?:[^?#](?![^?#/]*\\.[^?#/.]+(?:[?#]|$)))*/?)?([^?#/]*))(?:\\?([^#]*))?(?:#(.*))?)","");
if(stringUrl.indexOf("://") == -1 && stringUrl.charAt(0) != "/") {
stringUrl = "/" + stringUrl;
}
r.match(stringUrl);
var url = new xrfragment_URI();
var _g = 0;
var _g1 = xrfragment_URI._parts.length;
while(_g < _g1) {
var i = _g++;
url[xrfragment_URI._parts[i]] = r.matched(i);
}
if(xrfragment_URI.isRelative(url) == true) {
if(url.directory == null && url.host != null) {
url.file = url.host;
}
}
url.hash = { };
if(url.fragment != null && url.fragment.length > 0) {
url.XRF = xrfragment_URI.parseFragment("#" + url.fragment,flags);
var key;
var _g = 0;
var _g1 = Reflect.fields(url.XRF);
while(_g < _g1.length) {
var key = _g1[_g];
++_g;
var v = url.XRF[key];
url.hash[key] = v["string"];
}
}
xrfragment_URI.computeVars(url);
return url;
};
xrfragment_URI.computeVars = function(url) {
var r_r = new RegExp("//","g".split("u").join(""));
if(url.directory != null && url.directory.indexOf("//") != -1) {
url.directory = url.directory.replace(r_r,"/");
}
if(url.path != null && url.path.indexOf("//") != -1) {
url.path = url.path.replace(r_r,"/");
}
if(url.file != null && url.file.indexOf("//") != -1) {
url.file = url.file.replace(r_r,"/");
}
url.URN = url.scheme + "://" + url.host;
if(url.port != null) {
url.URN += ":" + url.port;
}
url.URN += url.directory;
if(url.file != null) {
var parts = url.file.split(".");
if(parts.length > 1) {
url.fileExt = parts.pop();
}
}
};
xrfragment_URI.toString = function(url) {
var result = "";
if(url.scheme != null) {
result += url.scheme + "://";
}
if(url.user != null) {
result += url.user + ":";
}
if(url.password != null) {
result += url.password + "@";
}
if(url.host != null) {
result += url.host;
}
if(url.port != null) {
result += ":" + url.port;
}
if(url.directory != null) {
result += url.directory;
}
if(url.file != null) {
result += url.file;
}
if(url.query != null) {
result += "?" + url.query;
}
if(url.fragment != null) {
result += "#" + url.fragment;
}
return result;
};
xrfragment_URI.appendURI = function(url,appendedURI) {
if(xrfragment_URI.isRelative(url) == true) {
return xrfragment_URI.appendToRelativeURI(url,appendedURI);
} else {
return xrfragment_URI.appendToAbsoluteURI(url,appendedURI);
}
};
xrfragment_URI.isRelative = function(url) {
return url.scheme == null;
};
xrfragment_URI.appendToRelativeURI = function(url,appendedURI) {
if(url.directory == null || url.host == null) {
return xrfragment_URI.cloneURI(appendedURI);
}
var resultURI = new xrfragment_URI();
resultURI.host = url.host;
resultURI.directory = url.directory;
if(appendedURI.host != null) {
resultURI.directory += appendedURI.host;
}
if(appendedURI.directory != null) {
var directory = appendedURI.directory;
if(appendedURI.host == null) {
resultURI.directory += HxOverrides.substr(directory,1,null);
} else {
resultURI.directory += directory;
}
}
if(appendedURI.file != null) {
resultURI.file = appendedURI.file;
}
resultURI.path = resultURI.directory + resultURI.file;
if(appendedURI.query != null) {
resultURI.query = appendedURI.query;
}
if(appendedURI.fragment != null) {
resultURI.fragment = appendedURI.fragment;
}
return resultURI;
};
xrfragment_URI.appendToAbsoluteURI = function(url,appendedURI) {
var resultURI = new xrfragment_URI();
if(url.scheme != null) {
resultURI.scheme = url.scheme;
}
if(url.host != null) {
resultURI.host = url.host;
}
var directory = "";
if(url.directory != null) {
directory = url.directory;
}
if(appendedURI.host != null) {
appendedURI.directory += appendedURI.host;
}
if(appendedURI.directory != null) {
directory += appendedURI.directory;
}
resultURI.directory = directory;
if(appendedURI.file != null) {
resultURI.file = appendedURI.file;
}
resultURI.path = resultURI.directory + resultURI.file;
if(appendedURI.query != null) {
resultURI.query = appendedURI.query;
}
if(appendedURI.fragment != null) {
resultURI.fragment = appendedURI.fragment;
}
return resultURI;
};
xrfragment_URI.toAbsolute = function(url,newUrl) {
var newURI = xrfragment_URI.parse(newUrl,0);
var resultURI = new xrfragment_URI();
resultURI.port = url.port;
resultURI.source = newUrl;
if(newURI.scheme != null) {
resultURI.scheme = newURI.scheme;
} else {
resultURI.scheme = url.scheme;
}
if(newURI.host != null && newURI.host.length > 0) {
resultURI.host = newURI.host;
resultURI.port = null;
resultURI.fragment = null;
resultURI.hash = { };
resultURI.XRF = { };
if(newURI.port != null) {
resultURI.port = newURI.port;
}
} else {
resultURI.host = url.host;
}
var directory = "";
if(url.directory != null) {
directory = url.directory;
}
if(newURI.directory != null) {
if(newUrl.charAt(0) != "/" && newUrl.indexOf("://") == -1) {
directory += newURI.directory;
} else {
directory = newURI.directory;
}
}
resultURI.directory = directory;
if(newURI.file != null) {
resultURI.file = newURI.file;
}
resultURI.path = resultURI.directory + resultURI.file;
if(newURI.query != null) {
resultURI.query = newURI.query;
}
if(newURI.fragment != null) {
resultURI.fragment = newURI.fragment;
}
resultURI.hash = newURI.hash;
resultURI.XRF = newURI.XRF;
xrfragment_URI.computeVars(resultURI);
return resultURI;
};
xrfragment_URI.cloneURI = function(url) {
var clonedURI = new xrfragment_URI();
clonedURI.url = url.url;
clonedURI.source = url.source;
clonedURI.scheme = url.scheme;
clonedURI.authority = url.authority;
clonedURI.userInfo = url.userInfo;
clonedURI.password = url.password;
clonedURI.host = url.host;
clonedURI.port = url.port;
clonedURI.relative = url.relative;
clonedURI.path = url.path;
clonedURI.directory = url.directory;
clonedURI.file = url.file;
clonedURI.query = url.query;
clonedURI.fragment = url.fragment;
return clonedURI;
};
var xrfragment_XRF = $hx_exports["xrfragment"]["XRF"] = function(_fragment,_flags,_index) { var xrfragment_XRF = $hx_exports["xrfragment"]["XRF"] = function(_fragment,_flags,_index) {
this.floats = []; this.floats = [];
this.shift = []; this.shift = [];
@ -1302,6 +1532,7 @@ haxe_Template.hxKeepArrayIterator = new haxe_iterators_ArrayIterator([]);
xrfragment_Parser.error = ""; xrfragment_Parser.error = "";
xrfragment_Parser.debug = false; xrfragment_Parser.debug = false;
xrfragment_URI.__meta__ = { statics : { template : { keep : null}}}; xrfragment_URI.__meta__ = { statics : { template : { keep : null}}};
xrfragment_URI._parts = ["source","scheme","authority","userInfo","user","password","host","port","relative","path","directory","file","query","fragment"];
xrfragment_XRF.IMMUTABLE = 1; xrfragment_XRF.IMMUTABLE = 1;
xrfragment_XRF.PROP_BIND = 2; xrfragment_XRF.PROP_BIND = 2;
xrfragment_XRF.QUERY_OPERATOR = 4; xrfragment_XRF.QUERY_OPERATOR = 4;
@ -1589,7 +1820,7 @@ let pub = function( url, node_or_model, flags ){ // evaluate fragments in url
if( !url ) return if( !url ) return
if( !url.match(/#/) ) url = `#${url}` if( !url.match(/#/) ) url = `#${url}`
let { THREE, camera } = xrf let { THREE, camera } = xrf
let frag = xrf.URI.parse( url, flags ) let frag = xrf.URI.parse( url, flags ).XRF
let fromNode = node_or_model != xrf.model let fromNode = node_or_model != xrf.model
let isNode = node_or_model && node_or_model.children let isNode = node_or_model && node_or_model.children
@ -1756,24 +1987,6 @@ xrf.reset = () => {
xrf.layers = 0 xrf.layers = 0
} }
xrf.parseUrl = (url) => {
let urlExHash = url.replace(/#.*/,'')
let urlObj,file
let store = {}
try{
urlObj = new URL( urlExHash.match(/:\/\//) ? urlExHash : String(`https://fake.com/${url}`).replace(/\/\//,'/') )
file = urlObj.pathname.substring(urlObj.pathname.lastIndexOf('/') + 1);
let search = urlObj.search.substr(1).split("&")
for( let i in search ) store[ (search[i].split("=")[0]) ] = search[i].split("=")[1] || ''
}catch(e){ }
let hashmap = url.match("#") ? url.replace(/.*#/,'').split("&") : []
for( let i in hashmap ) store[ (hashmap[i].split("=")[0]) ] = hashmap[i].split("=")[1] || ''
let dir = url.substring(0, url.lastIndexOf('/') + 1)
const hash = url.match(/#/) ? url.replace(/.*#/,'') : ''
const ext = file.split('.').pop()
return {urlObj,dir,file,hash,ext,store}
}
xrf.add = (object) => { xrf.add = (object) => {
object.isXRF = true // mark for easy deletion when replacing scene object.isXRF = true // mark for easy deletion when replacing scene
xrf.scene.add(object) xrf.scene.add(object)
@ -1784,42 +1997,52 @@ xrf.hasNoMaterial = (mesh) => {
const hasMaterialName = mesh.material && mesh.material.name.length > 0 const hasMaterialName = mesh.material && mesh.material.name.length > 0
return mesh.geometry && !hasMaterialName && !hasTexture return mesh.geometry && !hasMaterialName && !hasTexture
} }
xrf.navigator = {} xrf.navigator = {URI:{}}
xrf.navigator.to = (url,flags,loader,data) => { xrf.navigator.to = (url,flags,loader,data) => {
if( !url ) throw 'xrf.navigator.to(..) no url given' if( !url ) throw 'xrf.navigator.to(..) no url given'
let {urlObj,dir,file,hash,ext} = xrf.navigator.origin = xrf.parseUrl(url)
let hashChange = (!file && hash) || !data && xrf.model.file == file
let hasPos = String(hash).match(/pos=/)
let URI = xrfragment.URI.toAbsolute( xrf.navigator.URI, url )
URI.hash = xrf.navigator.reactifyHash(URI.hash)
let fileChange = URI.URN + URI.file != xrf.navigator.URI.URN + xrf.navigator.URI.file
let external = URI.URN != document.location.origin + document.location.pathname
let hasPos = URI.hash.pos
let hashChange = String(xrf.navigator.URI.fragment||"") != String(URI.fragment||"")
let hashbus = xrf.hashbus
xrf.navigator.URI = URI
let {directory,file,fragment,fileExt} = URI;
let hashbus = xrf.hashbus const evalFragment = () => {
if( URI.fragment ){
hashbus.pub( URI.fragment, xrf.model, flags ) // eval local URI XR fragments
xrf.navigator.updateHash(fragment) // which don't require
}
}
return new Promise( (resolve,reject) => { return new Promise( (resolve,reject) => {
xrf xrf
.emit('navigate', {url,loader,data}) .emit('navigate', {url,loader,data})
.then( () => { .then( () => {
if( ext && !loader ){ const Loader = xrf.loaders[fileExt]
const Loader = xrf.loaders[ext]
if( fileExt && !loader ){
if( !Loader ) return resolve() if( !Loader ) return resolve()
loader = loader || new Loader().setPath( dir ) loader = loader || new Loader().setPath( URI.URN )
} }
if( !hash && !file && !ext ) return resolve(xrf.model) // nothing we can do here if( !URI.fragment && !URI.file && !URI.fileExt ) return resolve(xrf.model) // nothing we can do here
if( hashChange && !hasPos ){ if( xrf.model && !fileChange && hashChange && !hasPos ){
hashbus.pub( url, xrf.model, flags ) // eval local URI XR fragments evalFragment()
xrf.navigator.updateHash(hash) // which don't require return resolve(xrf.model) // positional navigation
return resolve(xrf.model) // positional navigation
} }
xrf xrf
.emit('navigateLoading', {url,loader,data}) .emit('navigateLoading', {url,loader,data})
.then( () => { .then( () => {
if( hashChange && hasPos ){ // we're already loaded if( (!fileChange || !file) && hashChange && hasPos ){ // we're already loaded
hashbus.pub( url, xrf.model, flags ) // and eval local URI XR fragments evalFragment()
xrf.navigator.updateHash(hash)
xrf.emit('navigateLoaded',{url}) xrf.emit('navigateLoaded',{url})
return resolve(xrf.model) return resolve(xrf.model)
} }
@ -1829,17 +2052,20 @@ xrf.navigator.to = (url,flags,loader,data) => {
xrf.reset() xrf.reset()
// force relative path for files which dont include protocol or relative path // force relative path for files which dont include protocol or relative path
if( dir ) dir = dir[0] == '.' || dir.match("://") ? dir : `.${dir}` if( directory ) directory = directory[0] == '.' || directory.match("://") ? directory : `.${directory}`
url = url.replace(dir,"")
loader = loader || new Loader().setPath( dir ) loader = loader || new Loader().setPath( URI.URN )
const onLoad = (model) => { const onLoad = (model) => {
model.file = file model.file = URI.file
// only change url when loading *another* file // only change url when loading *another* file
if( xrf.model ) xrf.navigator.pushState( `${dir}${file}`, hash ) if( xrf.model ){
xrf.navigator.pushState( external ? URI.URN + URI.file : URI.file, fragment )
}
//if( xrf.model ) xrf.navigator.pushState( `${ document.location.pathname != URI.directory ? URI.directory: ''}${URI.file}`, fragment )
xrf.model = model xrf.model = model
if( !model.isXRF ) xrf.parseModel(model,url) // this marks the model as an XRF model if( !model.isXRF ) xrf.parseModel(model,url.replace(directory,"")) // this marks the model as an XRF model
if(xrf.debug ) model.animations.map( (a) => console.log("anim: "+a.name) ) if(xrf.debug ) model.animations.map( (a) => console.log("anim: "+a.name) )
@ -1847,17 +2073,16 @@ xrf.navigator.to = (url,flags,loader,data) => {
xrf.XRWG.generate({model,scene:model.scene}) xrf.XRWG.generate({model,scene:model.scene})
// spec: 2. init metadata inside model for non-SRC data // spec: 2. init metadata inside model for non-SRC data
if( !model.isSRC ){ if( !model.isSRC ){
model.scene.traverse( (mesh) => xrf.parseModel.metadataInMesh(mesh,model) ) model.scene.traverse( (mesh) => xrf.parseModel.metadataInMesh(mesh,model) )
} }
// spec: 1. execute the default predefined view '#' (if exist) (https://xrfragment.org/#predefined_view) // spec: 1. execute the default predefined view '#' (if exist) (https://xrfragment.org/#predefined_view)
xrf.frag.defaultPredefinedViews({model,scene:model.scene}) xrf.frag.defaultPredefinedViews({model,scene:model.scene})
// spec: predefined view(s) & objects-of-interest-in-XRWG from URL (https://xrfragment.org/#predefined_view) // spec: predefined view(s) & objects-of-interest-in-XRWG from URI (https://xrfragment.org/#predefined_view)
let frag = xrf.hashbus.pub( url, model) // and eval URI XR fragments let frag = xrf.hashbus.pub( url, model) // and eval URI XR fragments
xrf.add( model.scene ) xrf.add( model.scene )
if( hash ) xrf.navigator.updateHash(hash) if( fragment ) xrf.navigator.updateHash(fragment)
xrf.emit('navigateLoaded',{url,model}) xrf.emit('navigateLoaded',{url,model})
resolve(model) resolve(model)
} }
@ -1866,7 +2091,7 @@ xrf.navigator.to = (url,flags,loader,data) => {
loader.parse(data, "", onLoad ) loader.parse(data, "", onLoad )
}else{ }else{
try{ try{
loader.load(url, onLoad ) loader.load(file, onLoad )
}catch(e){ }catch(e){
console.error(e) console.error(e)
xrf.emit('navigateError',{url}) xrf.emit('navigateError',{url})
@ -1880,9 +2105,12 @@ xrf.navigator.to = (url,flags,loader,data) => {
xrf.navigator.init = () => { xrf.navigator.init = () => {
if( xrf.navigator.init.inited ) return if( xrf.navigator.init.inited ) return
xrf.navigator.URI = xrfragment.URI.parse(document.location.href)
window.addEventListener('popstate', function (event){ window.addEventListener('popstate', function (event){
if( !xrf.navigator.updateHash.active ){ // ignore programmatic hash updates (causes infinite recursion) if( !xrf.navigator.updateHash.active ){ // ignore programmatic hash updates (causes infinite recursion)
xrf.navigator.to( document.location.search.substr(1) + document.location.hash ) //xrf.navigator.to( document.location.search.substr(1) + document.location.hash )
xrf.navigator.to( document.location.href.replace(/\?/,'') )
} }
}) })
@ -1908,10 +2136,10 @@ xrf.navigator.setupNavigateFallbacks = () => {
xrf.addEventListener('navigate', (opts) => { xrf.addEventListener('navigate', (opts) => {
let {url} = opts let {url} = opts
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url) let {fileExt} = xrfragment.URI.parse(url)
// handle http links // handle http links
if( url.match(/^http/) && !xrf.loaders[ext] ){ if( url.match(/^http/) && !xrf.loaders[fileExt] ){
let inIframe let inIframe
try { inIframe = window.self !== window.top; } catch (e) { inIframe = true; } try { inIframe = window.self !== window.top; } catch (e) { inIframe = true; }
return inIframe ? window.parent.postMessage({ url }, '*') : window.open( url, '_blank') return inIframe ? window.parent.postMessage({ url }, '*') : window.open( url, '_blank')
@ -1931,7 +2159,7 @@ xrf.navigator.setupNavigateFallbacks = () => {
xrf.navigator.updateHash = (hash,opts) => { xrf.navigator.updateHash = (hash,opts) => {
if( hash.replace(/^#/,'') == document.location.hash.substr(1) || hash.match(/\|/) ) return // skip unnecesary pushState triggers if( hash.replace(/^#/,'') == document.location.hash.substr(1) || hash.match(/\|/) ) return // skip unnecesary pushState triggers
console.log(`URL: ${document.location.search.substr(1)}#${hash}`) console.log(`URI: ${document.location.search.substr(1)}#${hash}`)
xrf.navigator.updateHash.active = true // important to prevent recursion xrf.navigator.updateHash.active = true // important to prevent recursion
document.location.hash = hash document.location.hash = hash
xrf.navigator.updateHash.active = false xrf.navigator.updateHash.active = false
@ -1942,6 +2170,23 @@ xrf.navigator.pushState = (file,hash) => {
window.history.pushState({},`${file}#${hash}`, document.location.pathname + `?${file}#${hash}` ) window.history.pushState({},`${file}#${hash}`, document.location.pathname + `?${file}#${hash}` )
xrf.emit('pushState', {file, hash} ) xrf.emit('pushState', {file, hash} )
} }
xrf.navigator.reactifyHash = ( obj ) => {
return new Proxy(obj,{
get(me,k) { return me[k] },
set(me,k,v){
me[k] = v
xrf.navigator.to( "#" + this.toString(me) )
},
toString(me){
let parts = []
Object.keys(me).map( (k) => {
parts.push( me[k] ? `${k}=${encodeURIComponent(me[k])}` : k )
})
return parts.join('&')
}
})
}
/** /**
* *
* navigation, portals & mutations * navigation, portals & mutations
@ -1993,7 +2238,6 @@ xrf.frag.href = function(v, opts){
.emit('href',{click:true,mesh,xrf:v}) // let all listeners agree .emit('href',{click:true,mesh,xrf:v}) // let all listeners agree
.then( () => { .then( () => {
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(v.string)
const isLocal = v.string[0] == '#' const isLocal = v.string[0] == '#'
const hasPos = isLocal && v.string.match(/pos=/) const hasPos = isLocal && v.string.match(/pos=/)
const flags = isLocal ? xrf.XRF.PV_OVERRIDE : undefined const flags = isLocal ? xrf.XRF.PV_OVERRIDE : undefined
@ -2153,6 +2397,32 @@ xrf.frag.pos = function(v, opts){
camera.updateMatrixWorld() camera.updateMatrixWorld()
} }
xrf.frag.pos.get = function(precision,randomize){
if( !precision ) precision = 2;
if( typeof THREE == 'undefined' ) THREE = xrf.THREE
let radToDeg = THREE.MathUtils.radToDeg
let toDeg = (x) => x / (Math.PI / 180)
let camera = xrf.camera
if( randomize ){
camera.position.x += Math.random()/10
camera.position.z += Math.random()/10
}
// *TODO* add camera direction
let direction = new xrf.THREE.Vector3()
camera.getWorldDirection(direction)
const pitch = Math.asin(direction.y);
const yaw = Math.atan2(direction.x, direction.z);
const pitchInDegrees = pitch * 180 / Math.PI;
const yawInDegrees = yaw * 180 / Math.PI;
return {
x: String(camera.position.x.toFixed(2)),
y: String(camera.position.y.toFixed(2)),
z: String(camera.position.z.toFixed(2)),
}
}
xrf.addEventListener('reset', (opts) => { xrf.addEventListener('reset', (opts) => {
// set the player to position 0,0,0 // set the player to position 0,0,0
xrf.camera.position.set(0,0,0) xrf.camera.position.set(0,0,0)
@ -2207,7 +2477,7 @@ xrf.frag.src = function(v, opts){
if( mesh.isSRC ) return // only embed src once if( mesh.isSRC ) return // only embed src once
let url = xrf.frag.src.expandURI( mesh, v.string ) let url = xrf.frag.src.expandURI( mesh, v.string )
let srcFrag = opts.srcFrag = xrfragment.URI.parse(url) let srcFrag = opts.srcFrag = xrfragment.URI.parse(url).XRF
opts.isLocal = v.string[0] == '#' opts.isLocal = v.string[0] == '#'
opts.isPortal = xrf.frag.src.renderAsPortal(mesh) opts.isPortal = xrf.frag.src.renderAsPortal(mesh)
opts.isSRC = mesh.isSRC = true opts.isSRC = mesh.isSRC = true
@ -2294,7 +2564,7 @@ xrf.frag.src.externalSRC = (url,frag,opts) => {
fetch(url, { method: 'HEAD' }) fetch(url, { method: 'HEAD' })
.then( (res) => { .then( (res) => {
let mimetype = res.headers.get('Content-type') let mimetype = res.headers.get('Content-type')
if(xrf.debug != undefined ) console.log("HEAD "+url+" => "+mimetype) if(xrf.debug > 0 ) console.log("HEAD "+url+" => "+mimetype)
if( url.replace(/#.*/,'').match(/\.(gltf|glb)$/) ) mimetype = 'gltf' if( url.replace(/#.*/,'').match(/\.(gltf|glb)$/) ) mimetype = 'gltf'
if( url.replace(/#.*/,'').match(/\.(frag|fs|glsl)$/) ) mimetype = 'x-shader/x-fragment' if( url.replace(/#.*/,'').match(/\.(frag|fs|glsl)$/) ) mimetype = 'x-shader/x-fragment'
if( url.replace(/#.*/,'').match(/\.(vert|vs)$/) ) mimetype = 'x-shader/x-fragment' if( url.replace(/#.*/,'').match(/\.(vert|vs)$/) ) mimetype = 'x-shader/x-fragment'
@ -3195,8 +3465,8 @@ xrf.addEventListener('render', (opts) => {
let loadAudio = (mimetype) => function(url,opts){ let loadAudio = (mimetype) => function(url,opts){
let {mesh,src,camera,THREE} = opts let {mesh,src,camera,THREE} = opts
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url) let URL = xrfragment.URI.toAbsolute( xrf.navigator.URI, url )
let frag = xrf.URI.parse( url ) let frag = URL.XRF
xrf.init.audio() xrf.init.audio()
let isPositionalAudio = !(mesh.position.x == 0 && mesh.position.y == 0 && mesh.position.z == 0) let isPositionalAudio = !(mesh.position.x == 0 && mesh.position.y == 0 && mesh.position.z == 0)
@ -3207,8 +3477,8 @@ let loadAudio = (mimetype) => function(url,opts){
mesh.media = mesh.media || {} mesh.media = mesh.media || {}
mesh.media.audio = { set: (mediafragment,v) => mesh.media.audio[mediafragment] = v } mesh.media.audio = { set: (mediafragment,v) => mesh.media.audio[mediafragment] = v }
let finalUrl = url.replace(/#.*/,'') let finalUrl = URL.URN + URL.file
if( xrf.debug != undefined ) console.log("GET "+finalUrl) if( xrf.debug > 0 ) console.log("GET "+finalUrl)
audioLoader.load( finalUrl, function( buffer ) { audioLoader.load( finalUrl, function( buffer ) {
sound.setBuffer( buffer ); sound.setBuffer( buffer );
@ -3318,7 +3588,8 @@ audioMimeTypes.map( (mimetype) => xrf.frag.src.type[ mimetype ] = loadAudio(mim
xrf.frag.src.type['fbx'] = function( url, opts ){ xrf.frag.src.type['fbx'] = function( url, opts ){
return new Promise( async (resolve,reject) => { return new Promise( async (resolve,reject) => {
let {mesh,src} = opts let {mesh,src} = opts
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url) let URL = xrfragment.URI.toAbsolute( xrf.navigator.URI, url )
let frag = URL.XRF
let loader let loader
let {THREE} = await import('https://unpkg.com/three@0.161.0/build/three.module.js') let {THREE} = await import('https://unpkg.com/three@0.161.0/build/three.module.js')
@ -3346,14 +3617,17 @@ xrf.frag.src.type['fbx'] = function( url, opts ){
xrf.frag.src.type['x-shader/x-fragment'] = function(url,opts){ xrf.frag.src.type['x-shader/x-fragment'] = function(url,opts){
let {mesh,THREE} = opts let {mesh,THREE} = opts
let URL = xrfragment.URI.toAbsolute( xrf.navigator.URI, url )
let frag = URL.XRF
let isFragmentShader = /\.(fs|frag|glsl)$/ let isFragmentShader = /\.(fs|frag|glsl)$/
let isVertexShader = /\.(vs|vert)$/ let isVertexShader = /\.(vs|vert)$/
let shaderReqs = [] let shaderReqs = []
let shaderCode = {} let shaderCode = {}
let shader = { let shader = {
fragment: { code: '', url: url.match( isFragmentShader ) ? url : '' }, fragment: { code: '', url: url.match( isFragmentShader ) ? URL.URN + URL.file : '' },
vertex: { code: '', url: url.match( isVertexShader ) ? url : '' } vertex: { code: '', url: url.match( isVertexShader ) ? URL.URN + URL.file : '' }
} }
var onShaderLoaded = ((args) => (type, status, code) => { var onShaderLoaded = ((args) => (type, status, code) => {
@ -3412,19 +3686,15 @@ xrf.frag.src.type['x-shader/x-vertex'] = xrf.frag.src.type['x-shader/x-fragmen
xrf.frag.src.type['gltf'] = function( url, opts ){ xrf.frag.src.type['gltf'] = function( url, opts ){
return new Promise( (resolve,reject) => { return new Promise( (resolve,reject) => {
let {mesh,src} = opts let {mesh,src} = opts
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url) let URL = xrfragment.URI.toAbsolute( xrf.navigator.URI, url )
let {directory,file,fileExt,URN} = URL;
let loader let loader
const Loader = xrf.loaders[ext] const Loader = xrf.loaders[fileExt]
if( !Loader ) throw 'xrfragment: no loader passed to xrfragment for extension .'+ext if( !Loader ) throw 'xrfragment: no loader passed to xrfragment for extension .'+ext
if( !dir.match("://") ){ // force relative path loader = new Loader().setPath( URN )
dir = dir.substr(0,2) == './' ? dir : `./${dir}`
loader = new Loader().setPath( dir )
}else{
loader = new Loader()
}
loader.load(url, (model) => { loader.load(file, (model) => {
model.isSRC = true model.isSRC = true
resolve(model) resolve(model)
}) })
@ -3435,7 +3705,7 @@ xrf.frag.src.type['gltf'] = function( url, opts ){
let loadHTML = (mimetype) => function(url,opts){ let loadHTML = (mimetype) => function(url,opts){
let {mesh,src,camera} = opts let {mesh,src,camera} = opts
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url) let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url)
let frag = xrf.URI.parse( url ) let frag = xrf.URI.parse( url ).XRF
console.warn("todo: html viewer for src not implemented") console.warn("todo: html viewer for src not implemented")
} }
@ -3452,6 +3722,8 @@ htmlMimeTypes.map( (mimetype) => xrf.frag.src.type[ mimetype ] = loadHTML(mimet
xrf.frag.src.type['image/png'] = function(url,opts){ xrf.frag.src.type['image/png'] = function(url,opts){
let {mesh,THREE} = opts let {mesh,THREE} = opts
let restrictTo3DBoundingBox = mesh.geometry let restrictTo3DBoundingBox = mesh.geometry
let URL = xrfragment.URI.toAbsolute( xrf.navigator.URI, url )
let frag = URL.XRF
mesh.material = new xrf.THREE.MeshBasicMaterial({ mesh.material = new xrf.THREE.MeshBasicMaterial({
map: null, map: null,
@ -3495,7 +3767,7 @@ xrf.frag.src.type['image/png'] = function(url,opts){
renderImage(texture) renderImage(texture)
} }
new THREE.TextureLoader().load( url, onLoad, null, console.error ); new THREE.TextureLoader().load( URL.URN + URL.file, onLoad, null, console.error );
} }
@ -3701,9 +3973,9 @@ xrf.portalNonEuclidian.stencilRef = 1
let loadVideo = (mimetype) => function(url,opts){ let loadVideo = (mimetype) => function(url,opts){
let {mesh,src,camera} = opts let {mesh,src,camera} = opts
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url)
const THREE = xrf.THREE const THREE = xrf.THREE
let frag = xrf.URI.parse( url ) let URL = xrfragment.URI.toAbsolute( xrf.navigator.URI, url )
let frag = URL.XRF
mesh.media = mesh.media || {} mesh.media = mesh.media || {}
@ -3725,7 +3997,7 @@ let loadVideo = (mimetype) => function(url,opts){
},false) },false)
}) })
video.src = url video.src = URL.URN + URL.file
video.speed = 1.0 video.speed = 1.0
video.looping = false video.looping = false
video.set = (mediafragment,v) => { video.set = (mediafragment,v) => {

File diff suppressed because one or more lines are too long

View File

@ -10,6 +10,8 @@ window.AFRAME.registerComponent('xrf-get', {
var el = this.el; var el = this.el;
var meshname = this.data.name || this.data; var meshname = this.data.name || this.data;
if( !meshname || typeof meshname != 'string' ) return
this.el.addEventListener('update', (evt) => { this.el.addEventListener('update', (evt) => {
setTimeout( () => { setTimeout( () => {
@ -46,7 +48,8 @@ window.AFRAME.registerComponent('xrf-get', {
this.el.object3D.child = mesh // keep reference (because .children will be empty) this.el.object3D.child = mesh // keep reference (because .children will be empty)
if( !this.el.id ) this.el.setAttribute("id",`xrf-${mesh.name}`) if( !this.el.id ) this.el.setAttribute("id",`xrf-${mesh.name}`)
}else console.warn("xrf-get ignore: "+JSON.stringify(this.data)) }
}, evt && evt.timeout ? evt.timeout: 500) }, evt && evt.timeout ? evt.timeout: 500)
}) })

View File

@ -110,7 +110,7 @@ xrf.navigator.init = () => {
window.addEventListener('popstate', function (event){ window.addEventListener('popstate', function (event){
if( !xrf.navigator.updateHash.active ){ // ignore programmatic hash updates (causes infinite recursion) if( !xrf.navigator.updateHash.active ){ // ignore programmatic hash updates (causes infinite recursion)
xrf.navigator.to( document.location.search.substr(1) + document.location.hash ) xrf.navigator.to( document.location.href.replace(/\?/,'') )
} }
}) })

View File

@ -21,7 +21,7 @@ let loadAudio = (mimetype) => function(url,opts){
mesh.media.audio = { set: (mediafragment,v) => mesh.media.audio[mediafragment] = v } mesh.media.audio = { set: (mediafragment,v) => mesh.media.audio[mediafragment] = v }
let finalUrl = URL.URN + URL.file let finalUrl = URL.URN + URL.file
if( xrf.debug != undefined ) console.log("GET "+finalUrl) if( xrf.debug > 0 ) console.log("GET "+finalUrl)
audioLoader.load( finalUrl, function( buffer ) { audioLoader.load( finalUrl, function( buffer ) {
sound.setBuffer( buffer ); sound.setBuffer( buffer );

View File

@ -401,6 +401,8 @@ class URI {
resultURI.port = url.port; resultURI.port = url.port;
resultURI.source = newUrl;
if (newURI.scheme != null) if (newURI.scheme != null)
{ {
resultURI.scheme = newURI.scheme; resultURI.scheme = newURI.scheme;

View File

@ -33,7 +33,7 @@ createScene = (noadd) => {
filterScene = (URI,opts) => { filterScene = (URI,opts) => {
opts = opts || {} opts = opts || {}
frag = xrf.URI.parse(URI) frag = xrf.URI.parse(URI).XRF
var {a,b,c,d,extembed,scene} = createScene() var {a,b,c,d,extembed,scene} = createScene()
xrf.filter.scene({...opts,scene,frag}) xrf.filter.scene({...opts,scene,frag})

View File

@ -15,9 +15,9 @@ console.assert = ((assert) => (a,b) => {
/* /*
* parser checks * parser checks
*/ */
let frags = xrf.URI.parse('://foo.com/1.gltf#pos=1.0,2.0,3.0&t=0,100') let frags = xrf.URI.parse('://foo.com/1.gltf#pos=1.0,2.0,3.0&t=0,100').XRF
console.assert( frags.t, {frags, reason:'URI.parse(): t needs to be set'}) console.assert( frags.t, {frags, reason:'URI.parse(): t needs to be set'})
let frag = xrf.URI.parse("#foo=1") let frag = xrf.URI.parse("#foo=1").XRF
console.assert( frag, {reason: 'xrf.URI.parse() should be available'}) console.assert( frag, {reason: 'xrf.URI.parse() should be available'})