work in progress [might break]

This commit is contained in:
Leon van Kammen 2024-04-10 16:38:50 +00:00
parent 184eae64b0
commit 8de1988302
19 changed files with 499 additions and 520 deletions

View File

@ -106,7 +106,7 @@ chatComponent = {
div.classList.add.apply(div.classList, opts.class.concat(["envelope"]))
}
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'
if( frag.pos ) opts.pos = frag.pos.string
msg.classList.add('self')

View File

@ -274,7 +274,7 @@ window.frontend = (opts) => new Proxy({
// load original scene and overwrite with updates
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]
loader = new Loader().setPath( dir )
notify('exporting scene<br><br>please wait..')

View File

@ -314,7 +314,7 @@ window.matrix = (opts) => new Proxy({
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)
}
},

View File

@ -258,7 +258,7 @@ window.trystero = (opts) => new Proxy({
let isTeleport = href.match(/(pos=|http:)/)
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)
}

View File

@ -7,7 +7,7 @@ let pub = function( url, node_or_model, flags ){ // evaluate fragments in url
if( !url ) return
if( !url.match(/#/) ) url = `#${url}`
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 isNode = node_or_model && node_or_model.children

View File

@ -88,24 +88,6 @@ xrf.reset = () => {
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) => {
object.isXRF = true // mark for easy deletion when replacing scene
xrf.scene.add(object)

View File

@ -1,21 +1,22 @@
xrf.navigator = {URL:{}}
xrf.navigator = {URI:{}}
xrf.navigator.to = (url,flags,loader,data) => {
if( !url ) throw 'xrf.navigator.to(..) no url given'
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 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.URL = URL
let {directory,file,fragment,fileExt} = URL;
xrf.navigator.URI = URI
let {directory,file,fragment,fileExt} = URI;
console.dir({URI, nav: xrf.navigator.URI})
debugger
const evalFragment = () => {
if( URL.fragment ){
hashbus.pub( URL.fragment, xrf.model, flags ) // eval local URI XR fragments
if( URI.fragment ){
hashbus.pub( URI.fragment, xrf.model, flags ) // eval local URI XR fragments
xrf.navigator.updateHash(fragment) // which don't require
}
}
@ -25,16 +26,16 @@ xrf.navigator.to = (url,flags,loader,data) => {
.emit('navigate', {url,loader,data})
.then( () => {
console.log("URN: "+URL.URN)
const Loader = xrf.loaders[fileExt]
if( fileExt && !loader ){
const Loader = xrf.loaders[fileExt]
if( !Loader ) return resolve()
loader = loader || new Loader().setPath( URL.URN )
loader = loader || new Loader().setPath( URI.URN )
}
if( !URL.fragment && !URL.file && !URL.fileExt ) 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( xrf.model && hashChange && !hasPos ){
if( xrf.model && !fileChange && hashChange && !hasPos ){
evalFragment()
return resolve(xrf.model) // positional navigation
}
@ -42,7 +43,7 @@ xrf.navigator.to = (url,flags,loader,data) => {
xrf
.emit('navigateLoading', {url,loader,data})
.then( () => {
if( !fileChange && hashChange && hasPos ){ // we're already loaded
if( (!fileChange || !file) && hashChange && hasPos ){ // we're already loaded
evalFragment()
xrf.emit('navigateLoaded',{url})
return resolve(xrf.model)
@ -55,16 +56,15 @@ xrf.navigator.to = (url,flags,loader,data) => {
// force relative path for files which dont include protocol or relative path
if( directory ) directory = directory[0] == '.' || directory.match("://") ? directory : `.${directory}`
loader = loader || new Loader().setPath( URL.URN )
loader = loader || new Loader().setPath( URI.URN )
const onLoad = (model) => {
model.file = URL.file
model.file = URI.file
// only change url when loading *another* file
if( xrf.model ){
let path = URL.directory != document.location.pathname ? URL.directory : '';
xrf.navigator.pushState( `${path}${URL.file}`, fragment )
xrf.navigator.pushState( external ? URI.URN + URI.file : URI.file, fragment )
}
//if( xrf.model ) xrf.navigator.pushState( `${ document.location.pathname != URL.directory ? URL.directory: ''}${URL.file}`, fragment )
//if( xrf.model ) xrf.navigator.pushState( `${ document.location.pathname != URI.directory ? URI.directory: ''}${URI.file}`, fragment )
xrf.model = model
if( !model.isXRF ) xrf.parseModel(model,url.replace(directory,"")) // this marks the model as an XRF model
@ -75,13 +75,12 @@ xrf.navigator.to = (url,flags,loader,data) => {
xrf.XRWG.generate({model,scene:model.scene})
// 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) )
}
// spec: 1. execute the default predefined view '#' (if exist) (https://xrfragment.org/#predefined_view)
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
xrf.add( model.scene )
@ -108,7 +107,7 @@ 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)
xrf.navigator.URI = xrfragment.URI.parse(document.location.href)
window.addEventListener('popstate', function (event){
if( !xrf.navigator.updateHash.active ){ // ignore programmatic hash updates (causes infinite recursion)
@ -138,10 +137,10 @@ xrf.navigator.setupNavigateFallbacks = () => {
xrf.addEventListener('navigate', (opts) => {
let {url} = opts
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url)
let {fileExt} = xrfragment.URI.parse(url)
// handle http links
if( url.match(/^http/) && !xrf.loaders[ext] ){
if( url.match(/^http/) && !xrf.loaders[fileExt] ){
let inIframe
try { inIframe = window.self !== window.top; } catch (e) { inIframe = true; }
return inIframe ? window.parent.postMessage({ url }, '*') : window.open( url, '_blank')
@ -161,7 +160,7 @@ xrf.navigator.setupNavigateFallbacks = () => {
xrf.navigator.updateHash = (hash,opts) => {
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
document.location.hash = hash
xrf.navigator.updateHash.active = false
@ -172,3 +171,20 @@ xrf.navigator.pushState = (file,hash) => {
window.history.pushState({},`${file}#${hash}`, document.location.pathname + `?${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('&')
}
})
}

View File

@ -49,7 +49,6 @@ xrf.frag.href = function(v, opts){
.emit('href',{click:true,mesh,xrf:v}) // let all listeners agree
.then( () => {
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(v.string)
const isLocal = v.string[0] == '#'
const hasPos = isLocal && v.string.match(/pos=/)
const flags = isLocal ? xrf.XRF.PV_OVERRIDE : undefined

View File

@ -7,7 +7,7 @@ xrf.frag.src = function(v, opts){
if( mesh.isSRC ) return // only embed src once
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.isPortal = xrf.frag.src.renderAsPortal(mesh)
opts.isSRC = mesh.isSRC = true

View File

@ -8,8 +8,8 @@
let loadAudio = (mimetype) => function(url,opts){
let {mesh,src,camera,THREE} = opts
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url)
let frag = xrf.URI.parse( url )
let URL = xrfragment.URI.toAbsolute( xrf.navigator.URI, url )
let frag = URL.XRF
xrf.init.audio()
let isPositionalAudio = !(mesh.position.x == 0 && mesh.position.y == 0 && mesh.position.z == 0)
@ -20,7 +20,7 @@ let loadAudio = (mimetype) => function(url,opts){
mesh.media = mesh.media || {}
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)
audioLoader.load( finalUrl, function( buffer ) {

View File

@ -5,7 +5,8 @@
xrf.frag.src.type['fbx'] = function( url, opts ){
return new Promise( async (resolve,reject) => {
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 {THREE} = await import('https://unpkg.com/three@0.161.0/build/three.module.js')

View File

@ -5,14 +5,17 @@
xrf.frag.src.type['x-shader/x-fragment'] = function(url,opts){
let {mesh,THREE} = opts
let URL = xrfragment.URI.toAbsolute( xrf.navigator.URI, url )
let frag = URL.XRF
let isFragmentShader = /\.(fs|frag|glsl)$/
let isVertexShader = /\.(vs|vert)$/
let shaderReqs = []
let shaderCode = {}
let shader = {
fragment: { code: '', url: url.match( isFragmentShader ) ? url : '' },
vertex: { code: '', url: url.match( isVertexShader ) ? url : '' }
fragment: { code: '', url: url.match( isFragmentShader ) ? URL.URN + URL.file : '' },
vertex: { code: '', url: url.match( isVertexShader ) ? URL.URN + URL.file : '' }
}
var onShaderLoaded = ((args) => (type, status, code) => {

View File

@ -5,19 +5,15 @@
xrf.frag.src.type['gltf'] = function( url, opts ){
return new Promise( (resolve,reject) => {
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
const Loader = xrf.loaders[ext]
const Loader = xrf.loaders[fileExt]
if( !Loader ) throw 'xrfragment: no loader passed to xrfragment for extension .'+ext
if( !dir.match("://") ){ // force relative path
dir = dir.substr(0,2) == './' ? dir : `./${dir}`
loader = new Loader().setPath( dir )
}else{
loader = new Loader()
}
loader = new Loader().setPath( URN )
loader.load(url, (model) => {
loader.load(file, (model) => {
model.isSRC = true
resolve(model)
})

View File

@ -2,7 +2,7 @@
let loadHTML = (mimetype) => function(url,opts){
let {mesh,src,camera} = opts
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")
}

View File

@ -7,6 +7,8 @@
xrf.frag.src.type['image/png'] = function(url,opts){
let {mesh,THREE} = opts
let restrictTo3DBoundingBox = mesh.geometry
let URL = xrfragment.URI.toAbsolute( xrf.navigator.URI, url )
let frag = URL.XRF
mesh.material = new xrf.THREE.MeshBasicMaterial({
map: null,
@ -50,7 +52,7 @@ xrf.frag.src.type['image/png'] = function(url,opts){
renderImage(texture)
}
new THREE.TextureLoader().load( url, onLoad, null, console.error );
new THREE.TextureLoader().load( URL.URN + URL.file, onLoad, null, console.error );
}

View File

@ -1,9 +1,9 @@
let loadVideo = (mimetype) => function(url,opts){
let {mesh,src,camera} = opts
let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url)
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 || {}
@ -25,7 +25,7 @@ let loadVideo = (mimetype) => function(url,opts){
},false)
})
video.src = url
video.src = URL.URN + URL.file
video.speed = 1.0
video.looping = false
video.set = (mediafragment,v) => {

View File

@ -1,6 +1,5 @@
import xrfragment.Filter;
import xrfragment.URI;
import xrfragment.URL;
import xrfragment.XRF;
class Spec {
@ -14,7 +13,7 @@ class Spec {
class Test {
static var errors:Int = 0;
static var browser : xrfragment.URL = null;
static var browser : xrfragment.URI = null;
static public function main():Void {
test( "url.json", Spec.load("src/spec/url.json") );
@ -36,7 +35,7 @@ class Test {
var valid:Bool = false;
var item:Dynamic = spec[i];
f = new Filter(item.data);
res = URI.parse(item.data,null);
res = URI.parseFragment(item.data,null);
if( item.expect.fn == "test" ) valid = item.expect.out == f.test( item.expect.input[0] );
if( item.expect.fn == "testProperty" ) valid = item.expect.out == f.testProperty( item.expect.input[0], item.expect.input[1] );
if( item.expect.fn == "testPropertyInt" ) valid = item.expect.out == f.testProperty( item.expect.input[0], item.expect.input[1] );
@ -44,8 +43,8 @@ class Test {
if( item.expect.fn == "testParsed" ) valid = item.expect.out == res.exists(item.expect.input);
if( item.expect.fn == "testPredefinedView" ) valid = res.exists(item.expect.input) && item.expect.out == res.get(item.expect.input).is( XRF.PV_EXECUTE) ;
if( item.expect.fn == "testPropertyAssign" ) valid = res.exists(item.expect.input) && item.expect.out == res.get(item.expect.input).is( XRF.PROP_BIND) ;
if( item.expect.fn == "testBrowserOverride" ) valid = item.expect.out == (URI.parse(item.data,XRF.NAVIGATOR)).exists(item.expect.input);
if( item.expect.fn == "testEmbedOverride" ) valid = item.expect.out == (URI.parse(item.data,XRF.METADATA)).exists(item.expect.input);
if( item.expect.fn == "testBrowserOverride" ) valid = item.expect.out == (URI.parseFragment(item.data,XRF.NAVIGATOR)).exists(item.expect.input);
if( item.expect.fn == "testEmbedOverride" ) valid = item.expect.out == (URI.parseFragment(item.data,XRF.METADATA)).exists(item.expect.input);
if( item.expect.fn == "testURL" ) valid = testURL( item.data, item.expect.input, item.expect.out, false );
if( item.expect.fn == "testURLHash" ) valid = testURL( item.data, item.expect.input, item.expect.out, false );
if( item.expect.fn == "testURLBrowse" ) valid = testURL( item.data, item.expect.input, item.expect.out, true );
@ -87,11 +86,11 @@ 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);
var URI = xrfragment.URI;
var url:URI = URI.parse(_url,0);
if( browserMode ){
if( browser == null ) browser = url;
url = browser = URL.toAbsolute( browser, _url );
url = browser = URI.toAbsolute( browser, _url );
}
var parts:Array<String> = attr.split(".");
if( parts.length > 1 && parts[0] == "hash" && url.hash.exists( parts[1]) ){

View File

@ -1,5 +1,13 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2023 Leon van Kammen/NLNET
/*
* various snippets originate from:
*
* http://haxe.org/doc/snip/uri_parser,
* https://github.com/haxecocktail/cocktail-url/blob/master/cocktail/url/URI.hx
*/
package xrfragment;
import xrfragment.Parser;
@ -27,10 +35,44 @@ import xrfragment.XRF;
@:keep // <- avoids accidental removal by dead code elimination
class URI {
public static var fragment:haxe.DynamicAccess<Dynamic>;
/**
* URI parts names
*/
private static var _parts : Array<String> = ["source", "scheme", "authority", "userInfo", "user",
"password","host","port","relative","path","directory","file","query","fragment"];
/**
* URI parts
*/
public var url : String;
public var source : String;
public var scheme : String;
public var authority : String;
public var userInfo : String;
public var user : String;
public var password : String;
public var host : String;
public var port : String;
public var relative : String;
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<Dynamic> = {};
public var XRF : haxe.DynamicAccess<Dynamic> = {};
public var URN : String;
/**
* class constructor
*/
public function new( )
{
}
@:keep
public static function parse(url:String,filter:Int):haxe.DynamicAccess<Dynamic> {
public static function parseFragment(url:String,filter:Int):haxe.DynamicAccess<Dynamic> {
var store:haxe.DynamicAccess<Dynamic> = {}; // 1. store key/values into a associative array or dynamic object
if( url == null || url.indexOf("#") == -1 ) return store;
var fragment:Array<String> = url.split("#"); // 1. fragment URI starts with `#`
@ -71,6 +113,381 @@ class URI {
return parts.join("#");
}
/**
* Parse a string url and return a typed
* object from it.
*
* note : implementation originate from here :
* http://haxe.org/doc/snip/uri_parser
*/
public static function parse(stringUrl:String, flags:Int ):URI
{
// 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);
var url:URI = new URI();
// Use reflection to set each part
for (i in 0..._parts.length)
{
Reflect.setField(url, _parts[i], r.matched(i));
}
//hack for relative url with only a file
if (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:String;
for( key in url.XRF.keys() ){
var v:haxe.DynamicAccess<Dynamic> = url.XRF.get(key);
url.hash[key] = v.get("string");
}
}
computeVars(url);
return url;
}
private static function computeVars( url:URI ) {
// 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<String> = url.file.split(".");
if( parts.length > 1 ){
url.fileExt = parts.pop();
}
}
}
/**
* Serialize an URl OBJect into an
* URI string
*/
public static function toString(url:URI):String
{
var result:String = "";
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;
}
/**
* takes 2 urls and return a new url which is the result
* of appending the second url to the first.
*
* if the first url points to a file, the file is removed
* and the appended url is added after the last directory
*
* only the query string and fragment of the appended url are used
*/
public static function appendURI(url:URI, appendedURI:URI):URI
{
if (isRelative(url) == true)
{
return appendToRelativeURI(url, appendedURI);
}
else
{
return appendToAbsoluteURI(url, appendedURI);
}
}
/**
* return wether the url is relative (true)
* or absolute (false)
*/
public static function isRelative(url:URI):Bool
{
return url.scheme == null;
}
/**
* append the appended url to a relative url
*/
public static function appendToRelativeURI(url:URI, appendedURI:URI):URI
{
//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
//then only the appended url content is returned, as this method replace the file
//part of the base url anyway
if (url.directory == null || url.host == null)
{
return cloneURI(appendedURI);
}
var resultURI:URI = new 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)
{
//remove the initial '/' char if no host, as already present
//in base url
resultURI.directory += directory.substr(1);
}
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;
}
/**
* append the appended url to an absolute url
*/
public static function appendToAbsoluteURI(url:URI, appendedURI:URI):URI
{
var resultURI:URI = new URI();
if (url.scheme != null)
{
resultURI.scheme = url.scheme;
}
if (url.host != null)
{
resultURI.host = url.host;
}
var directory:String = "";
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;
}
/**
* append the appended url to an absolute url
*/
public static function toAbsolute(url:URI, newUrl:String ):URI
{
var newURI:URI = parse(newUrl,0);
var resultURI:URI = new URI();
resultURI.port = url.port;
if (newURI.scheme != null)
{
resultURI.scheme = newURI.scheme;
}else{
resultURI.scheme = url.scheme;
}
if (newURI.host != null && newURI.host.length > 0 )
{
trace("host: "+newURI.host);
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:String = "";
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;
computeVars(resultURI);
return resultURI;
}
/**
* clone the provided url
*/
private static function cloneURI(url:URI):URI
{
var clonedURI:URI = new 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;
}
}
/**

View File

@ -1,436 +0,0 @@
package xrfragment;
import xrfragment.URI;
/*
* Cocktail, HTML rendering engine
* http://haxe.org/com/libs/cocktail
*
* Copyright (c) Silex Labs
* Cocktail is available under the MIT license
* http://www.silexlabs.org/labs/cocktail-licensing/
* https://github.com/haxecocktail/cocktail-url/blob/master/cocktail/url/URL.hx
*/
/**
* Parse and serialize an URL.
*
* note : parts of the implementation originate from
* here : http://haxe.org/doc/snip/uri_parser,
* some part of the URL have been renamed to match
* w3c spec
*
* @author Yannick Dominguez
*/
@:expose // <- makes the class reachable from plain JavaScript
@:keep // <- avoids accidental removal by dead code elimination
class URL
{
/**
* URL parts names
*/
private static var _parts : Array<String> = ["source", "scheme", "authority", "userInfo", "user",
"password","host","port","relative","path","directory","file","query","fragment"];
/**
* URL parts
*/
public var url : String;
public var source : String;
public var scheme : String;
public var authority : String;
public var userInfo : String;
public var user : String;
public var password : String;
public var host : String;
public var port : String;
public var relative : String;
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<Dynamic> = {};
public var XRF : haxe.DynamicAccess<Dynamic> = {};
public var URN : String;
/**
* class constructor
*/
public function new( )
{
}
/**
* Parse a string url and return a typed
* object from it.
*
* note : implementation originate from here :
* http://haxe.org/doc/snip/uri_parser
*/
public static function parse(stringUrl:String ):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);
var url:URL = new URL();
// Use reflection to set each part
for (i in 0..._parts.length)
{
Reflect.setField(url, _parts[i], r.matched(i));
}
//hack for relative url with only a file
if (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.parse( "#"+url.fragment, 0 );
var key:String;
for( key in url.XRF.keys() ){
var v:haxe.DynamicAccess<Dynamic> = url.XRF.get(key);
url.hash[key] = v.get("string");
}
}
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<String> = url.file.split(".");
if( parts.length > 1 ){
url.fileExt = parts.pop();
}
}
}
/**
* Serialize an URl OBJect into an
* URL string
*/
public static function toString(url:URL):String
{
var result:String = "";
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;
}
/**
* takes 2 urls and return a new url which is the result
* of appending the second url to the first.
*
* if the first url points to a file, the file is removed
* and the appended url is added after the last directory
*
* only the query string and fragment of the appended url are used
*/
public static function appendURL(url:URL, appendedURL:URL):URL
{
if (isRelative(url) == true)
{
return appendToRelativeURL(url, appendedURL);
}
else
{
return appendToAbsoluteURL(url, appendedURL);
}
}
/**
* return wether the url is relative (true)
* or absolute (false)
*/
public static function isRelative(url:URL):Bool
{
return url.scheme == null;
}
/**
* append the appended url to a relative 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
//then only the appended url content is returned, as this method replace the file
//part of the base url anyway
if (url.directory == null || url.host == null)
{
return cloneURL(appendedURL);
}
var resultURL:URL = new URL();
resultURL.host = url.host;
resultURL.directory = url.directory;
if (appendedURL.host != null)
{
resultURL.directory += appendedURL.host;
}
if (appendedURL.directory != null)
{
var directory = appendedURL.directory;
if (appendedURL.host == null)
{
//remove the initial '/' char if no host, as already present
//in base url
resultURL.directory += directory.substr(1);
}
else
{
resultURL.directory += directory;
}
}
if (appendedURL.file != null)
{
resultURL.file = appendedURL.file;
}
resultURL.path = resultURL.directory + resultURL.file;
if (appendedURL.query != null)
{
resultURL.query = appendedURL.query;
}
if (appendedURL.fragment != null)
{
resultURL.fragment = appendedURL.fragment;
}
return resultURL;
}
/**
* append the appended url to an absolute url
*/
public static function appendToAbsoluteURL(url:URL, appendedURL:URL):URL
{
var resultURL:URL = new URL();
if (url.scheme != null)
{
resultURL.scheme = url.scheme;
}
if (url.host != null)
{
resultURL.host = url.host;
}
var directory:String = "";
if (url.directory != null)
{
directory = url.directory;
}
if (appendedURL.host != null)
{
appendedURL.directory += appendedURL.host;
}
if (appendedURL.directory != null)
{
directory += appendedURL.directory;
}
resultURL.directory = directory;
if (appendedURL.file != null)
{
resultURL.file = appendedURL.file;
}
resultURL.path = resultURL.directory + resultURL.file;
if (appendedURL.query != null)
{
resultURL.query = appendedURL.query;
}
if (appendedURL.fragment != null)
{
resultURL.fragment = appendedURL.fragment;
}
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
*/
private static function cloneURL(url:URL):URL
{
var clonedURL:URL = new URL();
clonedURL.url = url.url;
clonedURL.source = url.source;
clonedURL.scheme = url.scheme;
clonedURL.authority = url.authority;
clonedURL.userInfo = url.userInfo;
clonedURL.password = url.password;
clonedURL.host = url.host;
clonedURL.port = url.port;
clonedURL.relative = url.relative;
clonedURL.path = url.path;
clonedURL.directory = url.directory;
clonedURL.file = url.file;
clonedURL.query = url.query;
clonedURL.fragment = url.fragment;
return clonedURL;
}
}