xrfragment-haxe/src/xrfragment/Query.hx
2023-03-10 18:49:16 +01:00

177 lines
5.7 KiB
Haxe

package xrfragment;
@:expose // <- makes the class reachable from plain JavaScript
@:keep // <- avoids accidental removal by dead code elimination
//return untyped __js__("window.location.search");
#if js
var ok:Bool = js.Syntax.code('
// haxe workarounds
Array.prototype.contains = Array.prototype.includes
if (typeof Array.prototype.remove !== "function") {
Array.prototype.remove = function (item) {
const oldLength = this.length
let newLength = 0
for (let i = 0; i < oldLength; i++) {
const entry = this[i]
if (entry === item) {
let newLength = i++
while (i !== this.length) {
const entry = this[i]
if (entry !== item) this[newLength++] = entry
i++
}
this.length = newLength
for (let i = newLength; i < oldLength; i++) delete this[i]
return true
}
}
return false
}
}
');
#end
class Query {
private var str:String = "";
private var q:Dynamic = {};
private var include:Array<String> = new Array();
private var exclude:Array<String> = new Array();
private var accept:Bool = false;
private var preset:String = "";
public function new(str:String){
if( str != null ) this.parse(str);
}
public function toObject() : Dynamic {
return this.q;
}
@:keep
public function qualify( nodename:String ): Bool {
if( this.q.copy_all ) this.accept = true;
if( this.include.contains(nodename) ) this.accept = true;
if( this.exclude.contains(nodename) ) this.accept = false;
return this.accept;
}
public function parse(str:String,recurse:Bool = false) : Dynamic {
var copyAll:Bool = recurse ? this.q.copy_all : str.substr(0,1) == "-" || str.substr(0,1) == "?" || str == "";
var isOr:EReg = ~/^or$/;
var isProp:EReg = ~/.*:[><=!]?/;
var isName:EReg = ~/[^:\/]/;
var isExclude:EReg = ~/^-/;
var isInclude:EReg = ~/^\+/;
var isPreset:EReg = ~/^\?/;
var token = str.split(" ");
var ors = new Array();
var q:haxe.DynamicAccess<Dynamic> = {};
function composeQuery() : Dynamic {
q = {};
q.set("object", new Array() );
q.set("-object", new Array() );
ors.push(q);
return q;
}
composeQuery();
function match(str,prefix = ""){
if( isPreset.match(str) && !recurse ){
this.preset = str;
return;
}
if( isExclude.match(str) || isInclude.match(str) ){
var t = str.substr(1);
match(t, str.substr(0,1) );
return;
}
if( isProp.match(str) ){
var skip = 0;
var type = "=";
if( str.indexOf("*") != -1 ) type = "*";
if( str.indexOf(">") != -1 ) type = ">";
if( str.indexOf("<") != -1 ) type = "<";
if( str.indexOf("!=") != -1 ) type = "!=";
if( str.indexOf(">=") != -1 ) type = ">=";
if( str.indexOf("<=") != -1 ) type = "<=";
if( type != "=" ) skip += type.length;
var property = str.split(":")[0];
var value:haxe.DynamicAccess<Dynamic>;
if( q.get(prefix+property) ) value = q.get(prefix+property);
else value = {};
value[ type ] = str.split(":")[1].substr(skip);
q.set(prefix+property,value);
return;
}
if( isName.match(str) ){
if( prefix == '-' ){
q["-object"].push(str);
while( q["object"].contains(str) == true) {
q["object"].remove(str);
}
}else{
q["object"].push(str);
while( q["-object"].contains(str) == true ){
q["-object"].remove(str);
}
}
return;
}
}
for( i in 0...token.length ) {
if( isOr.match(token[i]) ){
composeQuery();
}else match(token[i]);
}
for ( i in 0...ors.length ) {
var or:Dynamic = ors[i];
if( Reflect.field(or,'object') != null ) this.include = this.include.concat( Reflect.field(or,'object') );
if( Reflect.field(or,'-object') != null ) this.exclude = this.exclude.concat( Reflect.field(or,'-object') );
}
this.q = { or: ors, copy_all: copyAll };
return this.q;
}
@:keep
public function test( property:String, ?value:Dynamic ):Void{
if( this.preset == property ){
this.parse( value, true );
}
for ( i in 0...this.q.or.length ) {
var or:Dynamic = this.q.or[i];
var conds:Int = 0;
var fails:Int = 0;
var pass:Int = 0;
var when = function(expr:Bool) : Bool {
conds+=1;
fails+= expr ? 0 : 1;
return expr;
}
for ( k in Reflect.fields(or) ){
var orval:Dynamic = Reflect.field(or,k);
if( k != property ) continue;
if( Reflect.field(orval,'=') != null && when( value == Reflect.field(orval,'=') ) ) pass += 1;
if( Reflect.field(orval,'*') != null && when( value != null ) ) pass += 1;
if( Reflect.field(orval,'>') != null && when( value > Std.parseInt(Reflect.field(orval,'>' )) ) ) pass += 1;
if( Reflect.field(orval,'<') != null && when( value < Std.parseInt(Reflect.field(orval,'<' )) ) ) pass += 1;
if( Reflect.field(orval,'>=') != null && when( value >= Std.parseInt(Reflect.field(orval,'>=')) ) ) pass += 1;
if( Reflect.field(orval,'<=') != null && when( value >= Std.parseInt(Reflect.field(orval,'<=')) ) ) pass += 1;
if( Reflect.field(orval,'!=') != null && when( value != Std.parseInt(Reflect.field(orval,'!=')) ) ) pass += 1;
}
if( this.accept && conds > 0 && fails > 0 ) this.accept = false;
if( conds > 0 && pass > 0 && fails == 0 ) this.accept = true;
}
}
}