From 184eae64b0f49814e6be1841b72599a4ccbfc879 Mon Sep 17 00:00:00 2001 From: Leon van Kammen Date: Wed, 10 Apr 2024 10:54:24 +0000 Subject: [PATCH] work in progress [might break] --- src/3rd/js/three/navigator.js | 64 ++++++++++------ src/Test.hx | 4 +- src/spec/url.json | 12 ++- src/xrfragment/URL.hx | 139 +++++++++++++++++++++++++++------- 4 files changed, 163 insertions(+), 56 deletions(-) diff --git a/src/3rd/js/three/navigator.js b/src/3rd/js/three/navigator.js index ef0f8d5..c9339b1 100644 --- a/src/3rd/js/three/navigator.js +++ b/src/3rd/js/three/navigator.js @@ -1,39 +1,49 @@ -xrf.navigator = {} +xrf.navigator = {URL:{}} xrf.navigator.to = (url,flags,loader,data) => { 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 URL = xrfragment.URL.toAbsolute( xrf.navigator.URL, url ) + console.dir({URL, nav: xrf.navigator.URL}) + let fileChange = URL.file && URL.file != xrf.navigator.URL.file + let hasPos = URL.hash.pos + let hashChange = String(xrf.navigator.URL.fragment||"") != String(URL.fragment||"") + let hashbus = xrf.hashbus + xrf.navigator.URL = URL + let {directory,file,fragment,fileExt} = URL; - let hashbus = xrf.hashbus + debugger + const evalFragment = () => { + if( URL.fragment ){ + hashbus.pub( URL.fragment, xrf.model, flags ) // eval local URI XR fragments + xrf.navigator.updateHash(fragment) // which don't require + } + } return new Promise( (resolve,reject) => { xrf .emit('navigate', {url,loader,data}) .then( () => { - if( ext && !loader ){ - const Loader = xrf.loaders[ext] + console.log("URN: "+URL.URN) + if( fileExt && !loader ){ + const Loader = xrf.loaders[fileExt] if( !Loader ) return resolve() - loader = loader || new Loader().setPath( dir ) + loader = loader || new Loader().setPath( URL.URN ) } - if( !hash && !file && !ext ) return resolve(xrf.model) // nothing we can do here + if( !URL.fragment && !URL.file && !URL.fileExt ) return resolve(xrf.model) // nothing we can do here - if( hashChange && !hasPos ){ - hashbus.pub( url, xrf.model, flags ) // eval local URI XR fragments - xrf.navigator.updateHash(hash) // which don't require - return resolve(xrf.model) // positional navigation + if( xrf.model && hashChange && !hasPos ){ + evalFragment() + return resolve(xrf.model) // positional navigation } xrf .emit('navigateLoading', {url,loader,data}) .then( () => { - if( hashChange && hasPos ){ // we're already loaded - hashbus.pub( url, xrf.model, flags ) // and eval local URI XR fragments - xrf.navigator.updateHash(hash) + if( !fileChange && hashChange && hasPos ){ // we're already loaded + evalFragment() xrf.emit('navigateLoaded',{url}) return resolve(xrf.model) } @@ -43,17 +53,21 @@ xrf.navigator.to = (url,flags,loader,data) => { xrf.reset() // force relative path for files which dont include protocol or relative path - if( dir ) dir = dir[0] == '.' || dir.match("://") ? dir : `.${dir}` - url = url.replace(dir,"") - loader = loader || new Loader().setPath( dir ) + if( directory ) directory = directory[0] == '.' || directory.match("://") ? directory : `.${directory}` + + loader = loader || new Loader().setPath( URL.URN ) const onLoad = (model) => { - model.file = file + model.file = URL.file // only change url when loading *another* file - if( xrf.model ) xrf.navigator.pushState( `${dir}${file}`, hash ) + if( xrf.model ){ + let path = URL.directory != document.location.pathname ? URL.directory : ''; + xrf.navigator.pushState( `${path}${URL.file}`, fragment ) + } + //if( xrf.model ) xrf.navigator.pushState( `${ document.location.pathname != URL.directory ? URL.directory: ''}${URL.file}`, fragment ) 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) ) @@ -71,7 +85,7 @@ xrf.navigator.to = (url,flags,loader,data) => { let frag = xrf.hashbus.pub( url, model) // and eval URI XR fragments xrf.add( model.scene ) - if( hash ) xrf.navigator.updateHash(hash) + if( fragment ) xrf.navigator.updateHash(fragment) xrf.emit('navigateLoaded',{url,model}) resolve(model) } @@ -80,7 +94,7 @@ xrf.navigator.to = (url,flags,loader,data) => { loader.parse(data, "", onLoad ) }else{ try{ - loader.load(url, onLoad ) + loader.load(file, onLoad ) }catch(e){ console.error(e) xrf.emit('navigateError',{url}) @@ -94,6 +108,8 @@ xrf.navigator.to = (url,flags,loader,data) => { xrf.navigator.init = () => { if( xrf.navigator.init.inited ) return + xrf.navigator.URL = xrfragment.URL.parse(document.location.href) + window.addEventListener('popstate', function (event){ if( !xrf.navigator.updateHash.active ){ // ignore programmatic hash updates (causes infinite recursion) xrf.navigator.to( document.location.search.substr(1) + document.location.hash ) diff --git a/src/Test.hx b/src/Test.hx index 6db380f..59990fd 100644 --- a/src/Test.hx +++ b/src/Test.hx @@ -88,10 +88,10 @@ class Test { static public function testURL( _url:String, attr:String, output:String, browserMode: Bool = false): Bool { var URL = xrfragment.URL; - var url:URL = URL.parse(_url,true); + var url:URL = URL.parse(_url); if( browserMode ){ if( browser == null ) browser = url; - url = URL.toAbsolute( browser, _url ); + url = browser = URL.toAbsolute( browser, _url ); } var parts:Array = attr.split("."); if( parts.length > 1 && parts[0] == "hash" && url.hash.exists( parts[1]) ){ diff --git a/src/spec/url.json b/src/spec/url.json index 71b74fe..d744a13 100644 --- a/src/spec/url.json +++ b/src/spec/url.json @@ -6,5 +6,15 @@ {"fn":"url","data":"http://foo.com?foo=1#mycustom=foo", "expect":{ "fn":"testURL", "input":"scheme","out":"http"},"label":"test URL scheme http"}, {"fn":"url","data":"http://foo.com/a/b?foo=1#mycustom=foo", "expect":{ "fn":"testURL", "input":"path","out":"/a/b"},"label":"test URL path /a/b"}, {"fn":"url","data":"http://foo.com/a/b?foo=1#mycustom=foo", "expect":{ "fn":"testURL", "input":"hash.mycustom","out":"foo"},"label":"test URL hash #mycustom == foo"}, - {"fn":"url","data":"http://foo.com/a/b?foo=1#mycustom=foo", "expect":{ "fn":"testURLBrowse", "input":"host","out":"foo.com"},"label":"test URLBrowser"} + {"fn":"url","data":"http://foo.com/a/b", "expect":{ "fn":"testURL", "input":"path","out":"/a/b"},"label":"test URL path /a/b/"}, + {"fn":"url","data":"http://foo.com/a/b?foo=1#mycustom=foo", "expect":{ "fn":"testURLBrowse", "input":"host","out":"foo.com"},"label":"test URLBrowser"}, + {"fn":"url","data":"http://foo.com/a/b?https://foo.com/abc#mycustom=foo", "expect":{ "fn":"testURL", "input":"path","out":"/a/b"},"label":"test URL url-in-query"}, + {"fn":"url","data":"http://foo.com/a/b?https://foo.com/abc#mycustom=foo", "expect":{ "fn":"testURLBrowse", "input":"path","out":"/a/b"},"label":"test URLBrowser url-in-query"}, + {"fn":"url","data":"/bar/flop#mycustom=foo", "expect":{ "fn":"testURLBrowse", "input":"host","out":"foo.com"},"label":"test URLBrowser (maintain host)"}, + {"fn":"url","data":"/bar/flop?foo=1#mycustom=foo", "expect":{ "fn":"testURLBrowse", "input":"query","out":"foo=1"},"label":"test URLBrowser (parse query)"}, + {"fn":"url","data":"/flop/flap#mycustom=foo", "expect":{ "fn":"testURLBrowse", "input":"path","out":"/flop/flap"},"label":"test URLBrowser (overwrite path)"}, + {"fn":"url","data":"/bar/flop#mycustom=foo", "expect":{ "fn":"testURLBrowse", "input":"path","out":"/bar/flop"},"label":"test URLBrowser (overwrite path 2)"}, + {"fn":"url","data":"/bar/flop#mycustom=foo", "expect":{ "fn":"testURLBrowse", "input":"host","out":"foo.com"},"label":"test URLBrowser (maintain host)"}, + {"fn":"url","data":"c/d?foo=1#mycustom=foo", "expect":{ "fn":"testURLBrowse", "input":"path","out":"/bar/flop/c/d"},"label":"test URLBrowser (append path+query)"}, + {"fn":"url","data":"a/b#foo=bar", "expect":{ "fn":"testURL", "input":"hash.foo","out":"bar"},"label":"test URL hash #mycustom == foo"} ] diff --git a/src/xrfragment/URL.hx b/src/xrfragment/URL.hx index 8aa55f7..5772f0e 100644 --- a/src/xrfragment/URL.hx +++ b/src/xrfragment/URL.hx @@ -48,10 +48,12 @@ class URL public var path : String; public var directory : String; public var file : String; + public var fileExt : String; public var query : String; public var fragment : String; - public var hash : haxe.DynamicAccess; - public var XRF : haxe.DynamicAccess; + public var hash : haxe.DynamicAccess = {}; + public var XRF : haxe.DynamicAccess = {}; + public var URN : String; /** * class constructor @@ -71,6 +73,10 @@ class URL { // The almighty regexp (courtesy of http://blog.stevenlevithan.com/archives/parseuri) var r : EReg = ~/^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/; + + if( stringUrl.indexOf("://") == -1 && stringUrl.charAt(0) != '/' ){ + stringUrl = "/" + stringUrl; // workaround for relative urls + } // Match the regexp to the url r.match(stringUrl); @@ -93,7 +99,7 @@ class URL } url.hash = {}; - if( url.fragment.length > 0 ){ + if( url.fragment != null && url.fragment.length > 0 ){ url.XRF = xrfragment.URI.parse( "#"+url.fragment, 0 ); var key:String; for( key in url.XRF.keys() ){ @@ -101,24 +107,38 @@ class URL url.hash[key] = v.get("string"); } } - trace("host:"+url.host); - trace("path:"+url.path); - trace("frag:"+url.fragment); - trace("source:"+url.source); - trace("scheme:"+url.scheme); - trace("authority:"+url.authority); - trace("userInfo:"+url.userInfo); - trace("user:"+url.user); - trace("password:"+url.password); - trace("host:"+url.host); - trace("port:"+url.port); - trace("relative:"+url.relative); - trace("path:"+url.path); - trace("directory:"+url.directory); - trace("file:"+url.file); - trace("query:"+url.query); + + computeVars(url); + return url; } + + private static function computeVars( url:URL ) { + // clean up url + var r = ~/\/\//g; + if( url.directory != null && url.directory.indexOf("//") != -1 ){ + url.directory = r.replace(url.directory,"/"); + } + if( url.path != null && url.path.indexOf("//") != -1 ){ + url.path = r.replace(url.path,"/"); + } + if( url.file != null && url.file.indexOf("//") != -1 ){ + url.file = r.replace(url.file,"/"); + } + // generate URN + url.URN = url.scheme + "://" + url.host; + if( url.port != null ) url.URN += ":"+url.port; + url.URN += url.directory; + + // extract file extension if any + if( url.file != null){ + var parts:Array = url.file.split("."); + if( parts.length > 1 ){ + url.fileExt = parts.pop(); + } + } + + } /** * Serialize an URl OBJect into an @@ -205,19 +225,11 @@ class URL { return url.scheme == null; } - - /* - * - */ - public static function toAbsolute( oldUrl:URL, newUrl:String ) : URL { - var newURL:URL = new URL(newUrl); - return appendURL( oldUrl, newURL ); - } /** * append the appended url to a relative url */ - private static function appendToRelativeURL(url:URL, appendedURL:URL):URL + public static function appendToRelativeURL(url:URL, appendedURL:URL):URL { //when relative url parsed, if it contains only a file (ex : "style.css") //then it will store it in the host attribute. So if the url has no directory @@ -276,7 +288,7 @@ class URL /** * append the appended url to an absolute url */ - private static function appendToAbsoluteURL(url:URL, appendedURL:URL):URL + public static function appendToAbsoluteURL(url:URL, appendedURL:URL):URL { var resultURL:URL = new URL(); @@ -327,6 +339,75 @@ class URL return resultURL; } + + /** + * append the appended url to an absolute url + */ + public static function toAbsolute(url:URL, newUrl:String ):URL + { + var newURL:URL = parse(newUrl); + var resultURL:URL = new URL(); + + resultURL.port = url.port; + + if (newURL.scheme != null) + { + resultURL.scheme = newURL.scheme; + }else{ + resultURL.scheme = url.scheme; + } + + if (newURL.host != null && newURL.host.length > 0 ) + { + trace("host: "+newURL.host); + resultURL.host = newURL.host; + resultURL.port = null; + if( newURL.port != null ){ + resultURL.port = newURL.port; + } + }else{ + resultURL.host = url.host; + } + + var directory:String = ""; + if (url.directory != null) + { + directory = url.directory; + } + + if (newURL.directory != null) + { + if( newUrl.charAt(0) != '/' && newUrl.indexOf("://") == -1 ){ + directory += newURL.directory; + }else{ + directory = newURL.directory; + } + } + + resultURL.directory = directory; + + if (newURL.file != null) + { + resultURL.file = newURL.file; + } + + resultURL.path = resultURL.directory + resultURL.file; + + if (newURL.query != null) + { + resultURL.query = newURL.query; + } + + if (newURL.fragment != null) + { + resultURL.fragment = newURL.fragment; + } + resultURL.hash = newURL.hash; + resultURL.XRF = newURL.XRF; + computeVars(resultURL); + + return resultURL; + } /** * clone the provided url