2023-05-23 11:30:39 +02:00
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2023 Leon van Kammen/NLNET
2023-04-14 14:45:50 +02:00
package xrfragment ;
2023-04-28 14:27:30 +02:00
@ : expose // <- makes the class reachable from plain JavaScript
@ : keep // <- avoids accidental removal by dead code elimination
2023-04-14 14:45:50 +02:00
class XRF {
/ *
* this class r e p r e s e n t s a f r a g m e n t ( v a l u e )
* /
2024-02-08 19:40:43 +01:00
// public static inline readonly IMMUTABLE
2023-04-14 14:45:50 +02:00
2023-04-14 17:37:33 +02:00
// scope types (powers of 2)
2024-02-08 19:40:43 +01:00
public static var IMMUTABLE: Int = 1 ; // fragment is immutable
2023-04-28 17:16:13 +02:00
public static var PROP_BIND: Int = 2 ; // fragment binds/controls one property with another
2023-11-10 18:22:47 +01:00
public static var QUERY_OPERATOR: Int = 4 ; // fragment will be applied to result of filterselecto
2023-04-28 17:16:13 +02:00
public static var PROMPT: Int = 8 ; // ask user whether this fragment value can be changed
2024-02-08 19:40:43 +01:00
public static var CUSTOMFRAG: Int = 16 ; // evaluation of this (multi) value can be roundrobined
2023-06-22 08:48:52 +02:00
public static var NAVIGATOR: Int = 32 ; // fragment can be overridden by (manual) browser URI change
2023-08-15 18:27:26 +02:00
public static var METADATA: Int = 64 ; // fragment can be overridden by an embedded URL
2023-06-22 08:48:52 +02:00
public static var PV_OVERRIDE: Int = 128 ; // embedded fragment can be overridden when specified in predefined view value
public static var PV_EXECUTE: Int = 256 ; // predefined view
2023-04-14 15:19:52 +02:00
2023-04-14 17:37:33 +02:00
// high-level value-types (powers of 2)
2023-04-28 17:16:13 +02:00
public static var T_COLOR: Int = 8192 ;
public static var T_INT: Int = 16384 ;
public static var T_FLOAT: Int = 32768 ;
public static var T_VECTOR2: Int = 65536 ;
public static var T_VECTOR3: Int = 131072 ;
public static var T_URL: Int = 262144 ;
public static var T_PREDEFINED_VIEW: Int = 524288 ;
public static var T_STRING: Int = 1048576 ;
2024-02-01 12:28:17 +00:00
public static var T_MEDIAFRAG: Int = 2097152 ;
2024-02-08 19:40:43 +01:00
public static var T_DYNAMICKEY: Int = 4194304 ;
public static var T_DYNAMICKEYVALUE: Int = 8388608 ;
2023-04-14 14:45:50 +02:00
// regexes
2023-11-24 17:32:53 +01:00
public static var isColor: EReg = ~/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/ ; // 1. hex colors are detected using regex `/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/`
public static var isInt: EReg = ~/^[-0-9]+$/ ; // 1. integers are detected using regex `/^[0-9]+$/`
public static var isFloat: EReg = ~/^[-0-9]+\.[0-9]+$/ ; // 1. floats are detected using regex `/^[0-9]+\.[0-9]+$/`
public static var isVector: EReg = ~/([,]+|\w)/ ; // 1. vectors are detected using regex `/[,]/` (but can also be an string referring to an entity-ID in the asset)
public static var isUrl: EReg = ~/(:\/\/)?\..*/ ; // 1. url/file */`
2024-02-01 12:28:17 +00:00
public static var isUrlOrPretypedView: EReg = ~/(^#|:\/\/)?\..*/ ; // 1. url/file */`
2023-11-24 17:32:53 +01:00
public static var isString: EReg = ~/.*/ ; // 1. anything else is string `/.*/`
2024-02-08 19:40:43 +01:00
public static var operators: EReg = ~/(^[-]|^[!]|[\*]$)/g ; // 1. detect operators so you can easily strip keys (reference regex= `~/(^-)?(\*)?/` )
2023-11-24 17:32:53 +01:00
public static var isProp: EReg = ~/^.*=[><=]?/ ; // 1. detect object id's & properties `foo=1` and `foo` (reference regex= `~/^.*=[><=]?/` )
public static var isExclude: EReg = ~/^-/ ; // 1. detect excluders like `-foo`,`-foo=1`,`-.foo`,`-/foo` (reference regex= `/^-/` )
public static var isDeep: EReg = ~/\*/ ; // 1. detect deep selectors like `foo*` (reference regex= `/\*$/` )
public static var isNumber: EReg = ~/^[0-9\.]+$/ ; // 1. detect number values like `foo=1` (reference regex= `/^[0-9\.]+$/` )
2024-02-09 18:00:22 +01:00
public static var isMediaFrag: EReg = ~/^([0-9\.,\*+-]+)$/ ; // 1. detect (extended) media fragment
2024-02-08 19:40:43 +01:00
public static var isReset: EReg = ~/^!/ ; // 1. detect reset operation
2024-02-14 15:47:34 +00:00
public static var isShift: EReg = ~/^(\+|--)/ ;
public static var isXRFScheme = ~/^xrf:\/\// ;
2023-04-14 14:45:50 +02:00
// value holder(s) // |------|------|--------|----------------------------------|
public var fragment: String ;
public var flags: Int ;
2023-11-16 14:50:57 +01:00
public var index: Int ;
2023-04-14 14:45:50 +02:00
public var x: Float ; // |vector| x,y,z| comma-separated | #pos=1,2,3 |
public var y: Float ;
public var z: Float ;
2024-02-09 18:00:22 +01:00
public var shift: Array < Bool > = new Array < Bool > ( ) ;
2024-02-01 12:28:17 +00:00
public var floats: Array < Float > = new Array < Float > ( ) ;
2023-04-14 14:45:50 +02:00
public var color: String ; // |string| color| FFFFFF (hex) | #fog=5m,FFAACC |
public var string: String ; // |string| | | #q=-sun |
public var int: Int ; // |int | | [-]x[xxxxx] | #price:>=100 |
public var float: Float ; // |float | | [-]x[.xxxx] (ieee)| #prio=-20 |
2023-11-10 18:22:47 +01:00
public var filter: Filter ;
2024-02-08 19:40:43 +01:00
public var reset: Bool ;
2024-02-05 14:11:29 +01:00
public var loop: Bool ;
2024-02-14 15:47:34 +00:00
public var xrfScheme: Bool ;
2023-04-14 14:45:50 +02:00
//
2023-11-16 14:50:57 +01:00
public function n e w ( _fragment : String , _flags : Int , ? _index : Int ) {
2023-04-14 14:45:50 +02:00
fragment = _fragment ;
flags = _flags ;
2023-11-16 14:50:57 +01:00
index = _index ;
2023-04-14 14:45:50 +02:00
}
public function is ( flag : Int ) : Bool {
2023-10-30 16:15:08 +01:00
if ( ! Std . isOfType ( flags , Int ) ) flags = 0 ;
2023-04-14 14:45:50 +02:00
return ( flags & flag ) != 0 ;
}
public static function set ( flag : Int , flags : Int ) : Int {
return flags | flag ;
}
public static function unset ( flag : Int , flags : Int ) : Int {
return flags & ~ flag ;
}
public function validate ( value : String ) : Bool {
guessType ( this , value ) ; // 1. extract the type
// validate
var ok: Bool = true ;
2024-03-19 10:06:08 +00:00
if ( value . length == 0 && ! is ( T_PREDEFINED_VIEW ) ) ok = false ;
2023-10-30 16:15:08 +01:00
if ( ! is ( T_FLOAT ) && is ( T_VECTOR2 ) && ! ( Std . isOfType ( x , Float ) && Std . isOfType ( y , Float ) ) ) ok = false ;
2023-11-24 17:32:53 +01:00
if ( ! ( is ( T_VECTOR2 ) || is ( T_STRING ) ) && is ( T_VECTOR3 ) && ! ( Std . isOfType ( x , Float ) && Std . isOfType ( y , Float ) && Std . isOfType ( z , Float ) ) ) ok = false ;
2023-04-14 14:45:50 +02:00
return ok ;
}
@ : keep
public function guessType ( v : XRF , str : String ) : Void {
v . string = str ;
2024-02-08 19:40:43 +01:00
if ( isReset . match ( v . fragment ) ) v . reset = true ;
2024-02-09 18:00:22 +01:00
if ( v . fragment == ' l o o p ' ) v . loop = true ;
2023-11-10 18:22:47 +01:00
if ( ! Std . isOfType ( str , String ) ) return ;
2024-02-09 18:00:22 +01:00
2023-11-10 18:22:47 +01:00
if ( str . length > 0 ) {
2024-02-09 18:00:22 +01:00
2024-02-14 15:47:34 +00:00
if ( isXRFScheme . match ( str ) ) {
v . xrfScheme = true ;
str = isXRFScheme . replace ( str , " " ) ;
v . string = str ;
}
2023-11-10 18:22:47 +01:00
if ( str . split ( " , " ) . length > 1 ) { // 1. `,` assumes 1D/2D/3D vector-values like x[,y[,z]]
2024-02-01 12:28:17 +00:00
var xyzn: Array < String > = str . split ( " , " ) ; // 1. parseFloat(..) and parseInt(..) is applied to vector/float and int values
if ( xyzn . length > 0 ) v . x = Std . parseFloat ( xyzn [ 0 ] ) ; // 1. anything else will be treated as string-value
if ( xyzn . length > 1 ) v . y = Std . parseFloat ( xyzn [ 1 ] ) ; // 1. incompatible value-types will be dropped / not used
if ( xyzn . length > 2 ) v . z = Std . parseFloat ( xyzn [ 2 ] ) ; //
for ( i in 0 ... xyzn . length ) {
2024-02-09 18:00:22 +01:00
v . shift . push ( isShift . match ( xyzn [ i ] ) ) ;
2024-02-14 15:47:34 +00:00
v . floats . push ( Std . parseFloat ( isShift . replace ( xyzn [ i ] , ' ' ) ) ) ;
2024-02-01 15:16:16 +00:00
}
2023-11-10 18:22:47 +01:00
} // > the xrfragment specification should stay simple enough
// > for anyone to write a parser using either regexes or grammar/lexers
if ( isColor . match ( str ) ) v . color = str ; // > therefore expressions/comprehensions are not supported (max wildcard/comparison operators for queries e.g.)
if ( isFloat . match ( str ) ) {
v . x = Std . parseFloat ( str ) ;
v . float = v . x ;
}
if ( isInt . match ( str ) ) {
v . int = Std . parseInt ( str ) ;
v . x = cast ( v . int ) ;
2024-02-08 19:40:43 +01:00
v . floats . push ( cast ( v . x ) ) ;
2023-11-10 18:22:47 +01:00
}
2024-02-09 18:00:22 +01:00
2023-11-16 14:50:57 +01:00
v . filter = new Filter ( v . fragment + " = " + v . string ) ;
} e lse v . filter = new Filter ( v . fragment ) ;
2023-04-14 14:45:50 +02:00
}
2024-02-17 12:02:07 +00:00
#if python
@ keep
public static function toDict ( o : { } ) {
return python . Lib . anonToDict ( o ) ;
}
#end
2023-04-14 14:45:50 +02:00
}