diff --git a/doc/RFC_XR_Fragments.md b/doc/RFC_XR_Fragments.md index f6619d6..3e7f3fb 100644 --- a/doc/RFC_XR_Fragments.md +++ b/doc/RFC_XR_Fragments.md @@ -94,7 +94,7 @@ value: draft-XRFRAGMENTS-leonvankammen-00 .# Abstract This draft is a specification for 4D URLs & [hypermediatic](https://github.com/coderofsalvation/hypermediatic) navigation, which links together space, time & text together, for hypermedia browsers with- or without a network-connection.
-The specification promotes spatial addressibility, sharing, navigation, query-ing and databinding objects for (XR) Browsers.
+The specification promotes spatial addressibility, sharing, navigation, filtering and databinding objects for (XR) Browsers.
XR Fragments allows us to better use existing metadata inside 3D scene(files), by connecting it to proven technologies like [URI Fragments](https://en.wikipedia.org/wiki/URI_fragment). > Almost every idea in this document is demonstrated at [https://xrfragment.org](https://xrfragment.org) @@ -134,7 +134,7 @@ Instead of combining them (in a game-editor e.g.), XR Fragments **integrates all | href metadata | triggers predefined view | Media fragments | | href metadata | triggers camera/scene/object/projections | n/a | | href metadata | draws visible connection(s) for XRWG 'tag' | n/a | -| href metadata | queries certain (in)visible objects | n/a | +| href metadata | filters certain (in)visible objects | n/a | > XR Fragments does not look at XR (or the web) thru the lens of HTML.
But approaches things from a higherlevel feedbackloop/hypermedia browser-perspective: @@ -146,7 +146,7 @@ Instead of combining them (in a game-editor e.g.), XR Fragments **integrates all │ 2D URL: ://library.com /document ?search #chapter │ │ │ │ 4D URL: ://park.com /4Dscene.fbx ──> ?misc ──> #view ───> hashbus │ - │ │ #query │ │ + │ │ #filter │ │ │ │ #tag │ │ │ │ #material │ │ │ │ #animation │ │ @@ -187,7 +187,7 @@ sub-delims = "," / "=" | Demo | Explanation | |-------------------------------|---------------------------------| | `pos=1,2,3` | vector/coordinate argument e.g. | -| `pos=1,2,3&rot=0,90,0&q=foo` | combinators | +| `pos=1,2,3&rot=0,90,0&foo` | combinators | > this is already implemented in all browsers @@ -198,7 +198,6 @@ sub-delims = "," / "=" | `#pos` | vector3 | `#pos=0.5,0,0` | positions camera (or XR floor) to xyz-coord 0.5,0,0, | | `#rot` | vector3 | `#rot=0,90,0` | rotates camera to xyz-coord 0.5,0,0 | | `#t` | timevector | `#t=2,2000,1` | play animation-loop range between frame 2 and 2000 at (normal) speed 1 | -| `#q` | vector3 | `#q=-sky -tag:hide`| queries scene-graph (and removes object with name `cube` or `tag: hide`) | ## List of metadata for 3D nodes @@ -206,7 +205,7 @@ sub-delims = "," / "=" |--------------|----------|------------------------|---------------------|----------------------------------------| | `href` | string | `"href": "b.gltf"` | XR teleport | custom property in 3D fileformats | | `src` | string | `"src": "#cube"` | XR embed / teleport | custom property in 3D fileformats | -| `tag` | string | `"tag": "cubes geo"` | tag object (for query-use / XRWG highlighting) | custom property in 3D fileformats | +| `tag` | string | `"tag": "cubes geo"` | tag object (for filter-use / XRWG highlighting) | custom property in 3D fileformats | > Supported popular compatible 3D fileformats: `.gltf`, `.obj`, `.fbx`, `.usdz`, `.json` (THREE.js), `.dae` and so on. @@ -326,27 +325,27 @@ The URL-processing-flow for hypermedia browsers goes like this: 5. IF a `#cube` matches anything else in the XR Word Graph (XRWG) draw wires to them (text or related objects). -# Embedding XR content (src-instancing) +# Embedding XR content using src `src` is the 3D version of the iframe.
It instances content (in objects) in the current scene/asset. | fragment | type | example value | |----------|------|---------------| -|`src`| string (uri, hashtag/query) | `#cube`
`#sometag`
#q=-ball_inside_cube`
`#q=-/sky -rain`
`#q=-.language .english`
`#q=price:>2 price:<5`
`https://linux.org/penguin.png`
`https://linux.world/distrowatch.gltf#t=1,100`
`linuxapp://conference/nixworkshop/apply.gltf#q=flyer`
`androidapp://page1?tutorial#pos=0,0,1&t1,100`
`foo.mp3#0,0,0`| +|`src`| string (uri, hashtag/filter) | `#cube`
`#sometag`
#cube&-ball_inside_cube`
`#-sky&-rain`
`#-language&english`
`#price=>5`
`https://linux.org/penguin.png`
`https://linux.world/distrowatch.gltf#t=1,100`
`linuxapp://conference/nixworkshop/apply.gltf#-cta&cta_apply`
`androidapp://page1?tutorial#pos=0,0,1&t1,100`
`foo.mp3#0,0,0`| -Here's an ascii representation of a 3D scene-graph with 3D objects `◻` which embeds remote & local 3D objects `◻` with/out using queries: +Here's an ascii representation of a 3D scene-graph with 3D objects `◻` which embeds remote & local 3D objects `◻` with/out using filters: ``` +────────────────────────────────────────────────────────+ +─────────────────────────+ │ │ │ │ │ index.gltf │ │ ocean.com/aquarium.fbx │ - │ │ │ │ │ │ + │ │ │ │ ├ room │ │ ├── ◻ canvas │ │ └── ◻ fishbowl │ │ │ └ src: painting.png │ │ ├─ ◻ bass │ │ │ │ │ └─ ◻ tuna │ │ ├── ◻ aquariumcube │ │ │ - │ │ └ src: ://rescue.com/fish.gltf#bass%20tuna │ +─────────────────────────+ + │ │ └ src: ://rescue.com/fish.gltf#fishbowl │ +─────────────────────────+ │ │ │ │ ├── ◻ bedroom │ │ │ └ src: #canvas │ @@ -358,31 +357,32 @@ Here's an ascii representation of a 3D scene-graph with 3D objects `◻` which e ``` An XR Fragment-compatible browser viewing this scene, lazy-loads and projects `painting.png` onto the (plane) object called `canvas` (which is copy-instanced in the bed and livingroom).
-Also, after lazy-loading `ocean.com/aquarium.gltf`, only the queried objects `bass` and `tuna` will be instanced inside `aquariumcube`.
+Also, after lazy-loading `ocean.com/aquarium.gltf`, only the queried objects `fishbowl` (and `bass` and `tuna`) will be instanced inside `aquariumcube`.
Resizing will be happen accordingly to its placeholder object `aquariumcube`, see chapter Scaling.
-> Instead of cherrypicking objects with `#bass&tuna` thru `src`, queries can be used to import the whole scene (and filter out certain objects). See next chapter below. +> Instead of cherrypicking a rootobject `#fishbowl` with `src`, additional filters can be used to include/exclude certain objects. See next chapter on filtering below. **Specification**: -1. local/remote content is instanced by the `src` (query) value (and attaches it to the placeholder mesh containing the `src` property) -1. local `src` values (URL **starting** with `#`, like `#cube&foo`) means **only** the mentioned objectnames will be copied to the instanced scene (from the current scene) while preserving their names (to support recursive selectors). [(example code)](https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/three/xrf/src.js) -1. local `src` values indicating a query (`#q=`), means that all included objects (from the current scene) will be copied to the instanced scene (before applying the query) while preserving their names (to support recursive selectors). [(example code)](https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/three/xrf/src.js) -1. the instanced scene (from a `src` value) should be scaled accordingly to its placeholder object or scaled relatively based on the scale-property (of a geometry-less placeholder, an 'empty'-object in blender e.g.). For more info see Chapter Scaling. -1. external `src` values should be served with appropriate mimetype (so the XR Fragment-compatible browser will now how to render it). The bare minimum supported mimetypes are: -1. `src` values should make its placeholder object invisible, and only flush its children when the resolved content can succesfully be retrieved (see [broken links](#links)) -1. external `src` values should respect the fallback link mechanism (see [broken links](#broken-links) -1. when the placeholder object is a 2D plane, but the mimetype is 3D, then render the spatial content on that plane via a stencil buffer. -1. src-values are non-recursive: when linking to an external object (`src: foo.fbx#bar`), then `src`-metadata on object `bar` should be ignored. -1. clicking on external `src`-values always allow sourceportation: teleporting to the origin URI to which the object belongs. -1. when only one object was cherrypicked (`#cube` e.g.), set its position to `0,0,0` -1. equirectangular detection: when the width of an image is twice the height (aspect 2:1), an equirectangular projection is assumed. -1. when the enduser clicks an href with `#t=1,0,0` (play) will be applied to all src mediacontent with a timeline (mp4/mp3 e.g.) +1. local/remote content is instanced by the `src` (filter) value (and attaches it to the placeholder mesh containing the `src` property) +2. by default all objects are loaded into the instanced src (scene) object (but not shown yet) +2. local `src` values (`#...` e.g.) starting with a non-negating filter (`#cube` e.g.) will make that object (with name `cube`) the new root of the scene at position 0,0,0 +3. local `src` values should respect (negative) filters (`#-foo&price=>3`) +4. the instanced scene (from a `src` value) should be scaled accordingly to its placeholder object or scaled relatively based on the scale-property (of a geometry-less placeholder, an 'empty'-object in blender e.g.). For more info see Chapter Scaling. +5. external `src` values should be served with appropriate mimetype (so the XR Fragment-compatible browser will now how to render it). The bare minimum supported mimetypes are: +6. `src` values should make its placeholder object invisible, and only flush its children when the resolved content can succesfully be retrieved (see [broken links](#links)) +7. external `src` values should respect the fallback link mechanism (see [broken links](#broken-links) +8. when the placeholder object is a 2D plane, but the mimetype is 3D, then render the spatial content on that plane via a stencil buffer. +9. src-values are non-recursive: when linking to an external object (`src: foo.fbx#bar`), then `src`-metadata on object `bar` should be ignored. +10. clicking on external `src`-values always allow sourceportation: teleporting to the origin URI to which the object belongs. +11. when only one object was cherrypicked (`#cube` e.g.), set its position to `0,0,0` +12. equirectangular detection: when the width of an image is twice the height (aspect 2:1), an equirectangular projection is assumed. +13. when the enduser clicks an href with `#t=1,0,0` (play) will be applied to all src mediacontent with a timeline (mp4/mp3 e.g.) * `model/gltf+json` * `image/png` * `image/jpg` -* `text/plain;charset=utf-8;bib=^@` +* `text/plain;charset=utf-8` [» example implementation](https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/three/xrf/src.js)
[» example 3D asset](https://github.com/coderofsalvation/xrfragment/blob/main/example/assets/src.gltf#L192)
@@ -484,58 +484,53 @@ To play global audio/video items: > NOTE: hardcoded framestart/framestop uses sampleRate/fps of embedded audio/video, otherwise the global fps applies. For more info see [[#t|t]]. -# XR Fragment queries +# XR Fragment filters Include, exclude, hide/shows objects using space-separated strings: | example | outcome | |----------------------------------|------------------------------------------------------------------------------------| -| `#q=-sky` | show everything except object named `sky` | -| `#q=-tag:language tag:english` | hide everything with tag `language`, but show all tag `english` objects | -| `#q=price:>2 price:<5` | of all objects with property `price`, show only objects with value between 2 and 5 | +| `#-sky` | show everything except object named `sky` | +| `#-language&english` | hide everything with tag `language`, but show all tag `english` objects | +| `#-price&price=>10` | hide all objects with property `price`, then only show object with price above 10 | It's simple but powerful syntax which allows filtering the scene using searchengine prompt-style feeling: -1. queries are a way to traverse a scene, and filter objects based on their tag- or property-values. -1. words like `german` match tag-metadata of 3D objects like `"tag":"german"` -1. words like `german` match (XR Text) objects with (Bib(s)TeX) tags like `#KarlHeinz@german` or `@german{KarlHeinz, ...` e.g. +1. filters are a way to traverse a scene, and filter objects based on their name, tag- or property-values. -* see [an (outdated) example video here](https://coderofsalvation.github.io/xrfragment.media/queries.mp4) +* see [an (outdated) example video here](https://coderofsalvation.github.io/xrfragment.media/queries.mp4) which used a dedicated `q=` variable (now deprecated) ## including/excluding | operator | info | |----------|-------------------------------------------------------------------------------------------------------------------------------| -| `-` | removes/hides object(s) | -| `:` | indicates an object-embedded custom property key/value | -| `>` `<` | compare float or int number | -| `/` | reference to root-scene.
Useful in case of (preventing) showing/hiding objects in nested scenes (instanced by `src`) (*) | +| `-` | hides object(s) (`#-myobject&-objects` e.g. | +| `=` | indicates an object-embedded custom property key/value (`#price=4&category=foo` e.g.) | +| `=>` `=<`| compare float or int number (`#price=>4` e.g.) | +| `/` | reference to root-scene.
Useful in case of (preventing) showing/hiding objects in nested scenes (instanced by `src`) (*) | > \* = `#q=-/cube` hides object `cube` only in the root-scene (not nested `cube` objects)
`#q=-cube` hides both object `cube` in the root-scene AND nested `skybox` objects | [» example implementation](https://github.com/coderofsalvation/xrfragment/blob/main/src/3rd/js/three/xrf/q.js) -[» example 3D asset](https://github.com/coderofsalvation/xrfragment/blob/main/example/assets/query.gltf#L192) +[» example 3D asset](https://github.com/coderofsalvation/xrfragment/blob/main/example/assets/filter.gltf#L192) [» discussion](https://github.com/coderofsalvation/xrfragment/issues/3) -## Query Parser +## Filter Parser -Here's how to write a query parser: +Here's how to write a filter parser: -1. create an associative array/object to store query-arguments as objects -1. detect object id's & properties `foo:1` and `foo` (reference regex: `/^.*:[><=!]?/` ) -1. detect excluders like `-foo`,`-foo:1`,`-.foo`,`-/foo` (reference regex: `/^-/` ) -1. detect root selectors like `/foo` (reference regex: `/^[-]?\//` ) -1. detect number values like `foo:1` (reference regex: `/^[0-9\.]+$/` ) -1. for every query token split string on `:` -1. create an empty array `rules` -1. then strip key-operator: convert "-foo" into "foo" -1. add operator and value to rule-array -1. therefore we we set `id` to `true` or `false` (false=excluder `-`) +1. create an associative array/object to store filter-arguments as objects +1. detect object id's & properties `foo=1` and `foo` (reference regex= `~/^.*=[><=]?/` ) +1. detect excluders like `-foo`,`-foo=1`,`-.foo`,`-/foo` (reference regex= `/^-/` ) +1. detect root selectors like `/foo` (reference regex= `/^[-]?\//` ) +1. detect number values like `foo=1` (reference regex= `/^[0-9\.]+$/` ) +1. detect operators so you can easily strip keys (reference regex= `/(^-|\*$)/` ) +1. detect exclude keys like `-foo` (reference regex= `/^-/` ) +1. for every filter token split string on `=` 1. and we set `root` to `true` or `false` (true=`/` root selector is present) -1. we convert key '/foo' into 'foo' -1. finally we add the key/value to the store like `store.foo = {id:false,root:true}` e.g. +1. therefore we we set `show` to `true` or `false` (false=excluder `-`) -> An example query-parser (which compiles to many languages) can be [found here](https://github.com/coderofsalvation/xrfragment/blob/main/src/xrfragment/Query.hx) +> An example filter-parser (which compiles to many languages) can be [found here](https://github.com/coderofsalvation/xrfragment/blob/main/src/xrfragment/Filter.hx) # Visible links @@ -893,7 +888,7 @@ Consider 3D scenes linking to eachother using these `href` values: * `href: university.edu/projects.gltf#math` These links would all show visible links to math-tagged objects in the scene.
-To filter out non-related objects one could take it a step further using queries: +To filter out non-related objects one could take it a step further using filters: * `href: schoolA.edu/projects.gltf#math&q=-topics math` * `href: schoolB.edu/projects.gltf#math&q=-courses math` @@ -949,7 +944,7 @@ This document has no IANA actions. |placeholder object | a 3D object which with src-metadata (which will be replaced by the src-data.) | |src | (HTML-piggybacked) metadata of a 3D object which instances content | |href | (HTML-piggybacked) metadata of a 3D object which links to content | -|query | an URI Fragment-operator which queries object(s) from a scene like `#q=cube` | +|filter | URI Fragment(s) which show/hide object(s) in a scene based on name/tag/property (`#q=cube&-price=>3`) | |visual-meta | [visual-meta](https://visual.meta.info) data appended to text/books/papers which is indirectly visible/editable in XR. | |requestless metadata | metadata which never spawns new requests (unlike RDF/HTML, which can cause framerate-dropping, hence not used a lot in games) | |FPS | frames per second in spatial experiences (games,VR,AR e.g.), should be as high as possible | diff --git a/example/assets/index.glb b/example/assets/index.glb index 0a7c03d..e45c67d 100644 Binary files a/example/assets/index.glb and b/example/assets/index.glb differ diff --git a/src/3rd/js/three/xrf/dynamic/filter.js b/src/3rd/js/three/xrf/dynamic/filter.js index 09bdfa8..4feafda 100644 --- a/src/3rd/js/three/xrf/dynamic/filter.js +++ b/src/3rd/js/three/xrf/dynamic/filter.js @@ -2,8 +2,12 @@ * TODO: refactor/fix this (queries are being refactored to filters) */ // spec: https://xrfragment.org/#filters -xrf.filter = function(){ - +xrf.filter = function(query, cb){ + let result = [] + if( !query ) return result + if( query[0] != '#' ) query = '#'+query + // *TODO* jquery like utility func + return result } xrf.filter.scene = function(opts){ @@ -28,13 +32,15 @@ xrf.filter.sort = function(frag){ xrf.filter.process = function(frag,scene,opts){ const hasName = (m,name,filter) => m.name == name - const hasNameOrTag = (m,name_or_tag,filter) => hasName(m,name_or_tag) || m.userData[filter.key] + const hasNameOrTag = (m,name_or_tag,filter) => hasName(m,name_or_tag) || + String(m.userData['tag']).match( new RegExp("(^| )"+name_or_tag) ) const cleanupKey = (k) => k.replace(/[-\*]/g,'') let firstFilter = frag.filters[0].filter.get() let showers = frag.filters.filter( (v) => v.filter.get().show === true ) - // reparent scene based on object in case it matches a primary (non-negating) selector + // spec 2: https://xrfragment.org/doc/RFC_XR_Macros.html#embedding-xr-content-using-src + // reparent scene based on objectname in case it matches a (non-negating) selector if( !firstFilter.value && firstFilter.show === true ){ let obj scene.traverse( (n) => hasName(n, firstFilter.key,firstFilter) && (obj = n) ) @@ -44,34 +50,43 @@ xrf.filter.process = function(frag,scene,opts){ } } + const setVisible = (n,visible,processed) => { + if( processed && processed[n.uuid] ) return + n.visible = visible + n.traverse( (n) => n.visible = visible ) + + // for hidden parents, clone material and set material to invisible + // otherwise n will not be rendered + if( visible ){ + n.traverseAncestors( (parent) => { + if( !parent.visible ){ + parent.visible = true + if( parent.material && !parent.material.isXRF ){ + parent.material = parent.material.clone() + parent.material.visible = false + } + } + }) + } + if( processed ) processed[n.uuid] == true + } + // then show/hide things based on secondary selectors frag.filters.map( (v) => { const filter = v.filter.get() const name_or_tag = cleanupKey(v.fragment) - let seen = {} - - const setVisibleUnseen = (m,visible) => { - if( seen[m.uuid] ) return - m.visible = visible - seen[ m.uuid ] = true - } + let processed = {} scene.traverse( (m) => { - // filter on value(expression) #foo=>3 e.g. if( filter.value && m.userData[filter.key] ){ const visible = v.filter.testProperty(filter.key, m.userData[filter.key], filter.show === false ) - setVisibleUnseen(m,visible) - if( filter.deep ){ - m.traverse( (n) => setVisibleUnseen(n,visible) ) - } + setVisible(m,visible,processed) return } - // include/exclude object(s) when id/tag matches (#foo or #-foo e.g.) if( hasNameOrTag(m,name_or_tag,filter) ){ - m.visible = filter.show - if( filter.deep ) m.traverse( (n) => n.visible = m.visible ) + setVisible(m,filter.show) } }) }) diff --git a/src/3rd/js/three/xrf/src.js b/src/3rd/js/three/xrf/src.js index f921b08..a2b1625 100644 --- a/src/3rd/js/three/xrf/src.js +++ b/src/3rd/js/three/xrf/src.js @@ -22,8 +22,6 @@ xrf.frag.src = function(v, opts){ let scene = model.scene xrf.frag.src.filterScene(scene,{...opts,frag}) xrf.frag.src.scale( scene, opts, url ) - xrf.frag.src.eval( scene, opts, url ) - // allow 't'-fragment to setup separate animmixer //enableSourcePortation(scene) mesh.add(model.scene) mesh.traverse( (n) => n.isSRC = n.isXRF = true ) // mark everything SRC @@ -32,6 +30,7 @@ xrf.frag.src = function(v, opts){ } const enableSourcePortation = (src) => { + // show sourceportation clickable plane if( vfrag.href || v.string[0] == '#' ) return let scale = new THREE.Vector3() let size = new THREE.Vector3() @@ -45,7 +44,6 @@ xrf.frag.src = function(v, opts){ mat.opacity = 0 const cube = new THREE.Mesh( geo, mat ) console.log("todo: sourceportate") - //mesh.add(cube) } const externalSRC = (url,frag,src) => { @@ -75,20 +73,6 @@ xrf.frag.src = function(v, opts){ }else externalSRC(url,vfrag) // external file } -xrf.frag.src.eval = function(scene, opts, url){ - let { mesh, model, camera, renderer, THREE, hashbus} = opts - if( url ){ - //let {urlObj,dir,file,hash,ext} = xrf.parseUrl(url) - //let frag = xrfragment.URI.parse(url) - //// scale URI XR Fragments (queries) inside src-value - //for( var i in frag ){ - // hashbus.pub.fragment(i, Object.assign(opts,{frag, model:{scene},scene})) - //} - //hashbus.pub( '#', {scene} ) // execute the default projection '#' (if exist) - //hashbus.pub( url, {scene} ) // and eval URI XR fragments - } -} - // scale embedded XR fragments https://xrfragment.org/#scaling%20of%20instanced%20objects xrf.frag.src.scale = function(scene, opts, url){ let { mesh, model, camera, renderer, THREE} = opts @@ -97,7 +81,8 @@ xrf.frag.src.scale = function(scene, opts, url){ let cleanScene = scene.clone() if( !cleanScene ) debugger let remove = [] - cleanScene.traverse( (n) => !n.visible && n.children.length == 0 && (remove.push(n)) ) + const notVisible = (n) => !n.visible || (n.material && !n.material.visible) + cleanScene.traverse( (n) => notVisible(n) && n.children.length == 0 && (remove.push(n)) ) remove.map( (n) => n.removeFromParent() ) let restrictTo3DBoundingBox = mesh.geometry diff --git a/src/3rd/js/three/xrf/src/non-euclidian.js b/src/3rd/js/three/xrf/src/non-euclidian.js index e632d64..36b7e96 100644 --- a/src/3rd/js/three/xrf/src/non-euclidian.js +++ b/src/3rd/js/three/xrf/src/non-euclidian.js @@ -1,3 +1,5 @@ +// spec 8: https://xrfragment.org/doc/RFC_XR_Macros.html#embedding-xr-content-using-src + xrf.portalNonEuclidian = function(opts){ let { frag, mesh, model, camera, scene, renderer} = opts diff --git a/src/xrfragment/Filter.hx b/src/xrfragment/Filter.hx index 02b0eca..f23e68d 100644 --- a/src/xrfragment/Filter.hx +++ b/src/xrfragment/Filter.hx @@ -52,11 +52,11 @@ class Filter { private var str:String = ""; private var q:haxe.DynamicAccess = {}; // 1. create an associative array/object to store filter-arguments as objects - private var isProp:EReg = ~/^.*=[><=!]?/; // 1. detect object id's & properties `foo=1` and `foo` (reference regex= `/^.*=[><=!]?/` ) + private var isProp:EReg = ~/^.*=[><=]?/; // 1. detect object id's & properties `foo=1` and `foo` (reference regex= `~/^.*=[><=]?/` ) private var isExclude:EReg = ~/^-/; // 1. detect excluders like `-foo`,`-foo=1`,`-.foo`,`-/foo` (reference regex= `/^-/` ) private var isRoot:EReg = ~/^[-]?\//; // 1. detect root selectors like `/foo` (reference regex= `/^[-]?\//` ) private var isNumber:EReg = ~/^[0-9\.]+$/; // 1. detect number values like `foo=1` (reference regex= `/^[0-9\.]+$/` ) - private var isDeepSelect:EReg = ~/(^-|\*$)/; // 1. detect nested keys like 'foo*' (reference regex= `/\*$/` ) + private var operators:EReg = ~/(^-|\*$)/; // 1. detect operators so you can easily strip keys (reference regex= `/(^-|\*$)/` ) private var isSelectorExclude:EReg = ~/^-/; // 1. detect exclude keys like `-foo` (reference regex= `/^-/` ) public function new(str:String){ @@ -78,22 +78,21 @@ class Filter { function process(str,prefix = ""){ str = StringTools.trim(str); - var k:String = str.split("=")[0]; // 1. for every filter token split string on `=` + var k:String = str.split("=")[0]; // 1. for every filter token split string on `=` var v:String = str.split("=")[1]; // retrieve existing filter if any var filter:haxe.DynamicAccess = {}; if( q.get(prefix+k) ) filter = q.get(prefix+k); - if( isProp.match(str) ){ // 1. WHEN when a `:` key/value is detected: + if( isProp.match(str) ){ // 1. WHEN when a `=` key/value is detected: var oper:String = ""; - if( str.indexOf("*") != -1 ) oper = "*"; // 1. then scan for `*` operator (means include all objects for [src](#src) embedded fragment) if( str.indexOf(">") != -1 ) oper = ">"; // 1. then scan for `>` operator if( str.indexOf("<") != -1 ) oper = "<"; // 1. then scan for `<` operator if( isExclude.match(k) ){ - k = k.substr(1); // 1. then strip key-operator: convert "-foo" into "foo" + k = k.substr(1); // 1. then strip operators from key: convert "-foo" into "foo" } - v = v.substr(oper.length); // 1. then strip value operator: change value ">=foo" into "foo" - if( oper.length == 0 ) oper = "="; + v = v.substr(oper.length); // 1. then strip operators from value: change value ">=foo" into "foo" + if( oper.length == 0 ) oper = "="; // 1. when no operators detected, assume operator '=' var rule:haxe.DynamicAccess = {}; if( isNumber.match(v) ) rule[ oper ] = Std.parseFloat(v); else rule[oper] = v; @@ -102,8 +101,7 @@ class Filter { q.set("root", isRoot.match(str) ? true : false ); // 1. and we set `root` to `true` or `false` (true=`/` root selector is present) } q.set("show", isExclude.match(str) ? false : true ); // 1. therefore we we set `show` to `true` or `false` (false=excluder `-`) - q.set("deep", isDeepSelect.match(k) ? true : false ); // 1. set `deep` (for objectnames with * suffix or negative selectors) - q.set("key", isDeepSelect.replace(k,'') ); + q.set("key", operators.replace(k,'') ); q.set("value",v); } for( i in 0...token.length ) process( token[i] );