2019-12-15 19:32:14 +01:00
class excitationTypes {
2020-03-20 11:09:48 +01:00
static get Valence ( ) { return new excitationType ( 1 << 1 , new description ( "Valence" ) ) }
static get Rydberg ( ) { return new excitationType ( 1 << 2 , new description ( "Rydberg" ) ) }
2020-03-08 16:17:04 +01:00
static get PiPis ( ) { return new excitationType ( 1 << 3 , new description ( String . raw ` \p i \r ightarrow \p i^ \s tar ` , true ) ) }
static get nPis ( ) { return new excitationType ( 1 << 4 , new description ( String . raw ` n \r ightarrow \p i^ \s tar ` , true ) ) }
static get Single ( ) { return new excitationType ( 1 << 5 , new description ( "Single" ) ) }
static get Double ( ) { return new excitationType ( 1 << 6 , new description ( "Double" ) ) }
2020-03-20 11:09:48 +01:00
static get SingletSinglet ( ) { return new excitationType ( 1 << 7 , new description ( String . raw ` \m athrm{Singlet} \r ightarrow \m athrm{Singlet} ` , true ) ) }
static get SingletTriplet ( ) { return new excitationType ( 1 << 8 , new description ( String . raw ` \m athrm{Singlet} \r ightarrow \m athrm{Triplet} ` , true ) ) }
2020-01-07 10:22:33 +01:00
// Max bit shifts is 31 because int are int32 So 1 << 31 are -2147483648
2020-03-08 16:17:04 +01:00
static get Others ( ) { return new excitationType ( 1 << 31 , new description ( "Others" ) ) }
2020-01-28 17:25:49 +01:00
static get All ( ) { return EnumUltils . getAll ( this , excitationType ) }
static GetFlags ( value ) { return EnumUltils . GetFlags ( value , this , excitationType ) }
}
class EnumUltils {
static getAll ( enumClass , valueType ) {
2020-01-06 14:35:29 +01:00
var lst = [ ]
2020-01-28 17:25:49 +01:00
for ( const prop of Object . getOwnPropertyNames ( enumClass ) ) {
2020-01-06 14:35:29 +01:00
if ( prop != "All" ) {
2020-01-28 17:25:49 +01:00
const value = enumClass [ prop ]
if ( trueTypeOf ( value ) == valueType . name ) {
2020-01-06 14:35:29 +01:00
lst . push ( [ prop , value ] )
2019-12-16 13:49:41 +01:00
}
2020-01-06 14:35:29 +01:00
}
2019-12-16 13:49:41 +01:00
}
return lst
}
2020-01-28 17:25:49 +01:00
static GetFlags ( value , enumClass , valueType ) {
return this . getAll ( enumClass , valueType ) . filter ( ( x ) => { value & x [ 1 ] } )
2019-12-16 13:49:41 +01:00
}
2019-12-15 19:32:14 +01:00
}
2020-01-27 11:09:35 +01:00
2020-03-08 16:17:04 +01:00
class description {
constructor ( string , isLaTeX = false ) {
this . string = string
this . isLaTeX = isLaTeX
}
}
class DescribedValueBase {
constructor ( value , description ) {
2020-01-06 14:35:29 +01:00
this . Value = value ;
2020-03-08 16:17:04 +01:00
this . description = description
2019-12-15 19:32:14 +01:00
}
2020-01-06 14:35:29 +01:00
valueOf ( ) {
2019-12-16 13:49:41 +01:00
return this . Value ;
}
2019-12-01 18:14:45 +01:00
}
2020-01-27 11:09:35 +01:00
2020-03-08 16:17:04 +01:00
class excitationType extends DescribedValueBase {
2020-01-27 11:09:35 +01:00
}
2020-03-08 16:17:04 +01:00
class VertExcitationKind extends DescribedValueBase {
2020-01-27 11:09:35 +01:00
}
2019-08-28 11:59:18 +02:00
class code {
2019-09-26 20:25:51 +02:00
constructor ( name , version ) {
this . name = name ;
this . version = version ;
2019-09-03 15:36:32 +02:00
} ;
2019-09-26 20:25:51 +02:00
toString ( ) {
var str = this . name ;
2019-09-03 15:36:32 +02:00
if ( this . version ) {
2019-09-26 20:25:51 +02:00
str = str + ' (' + this . version + ')' ;
2019-09-03 15:36:32 +02:00
}
return str ;
2019-08-28 11:59:18 +02:00
}
2019-09-19 15:05:07 +02:00
static fromString ( str ) {
2019-09-26 20:25:51 +02:00
var vals = str . split ( "," )
if ( vals . length >= 2 ) {
return new code ( vals [ 0 ] , vals [ 1 ] ) ;
2019-09-19 15:05:07 +02:00
} else {
2019-09-26 20:25:51 +02:00
return new code ( vals [ 0 ] , null ) ;
2019-09-19 15:05:07 +02:00
}
}
2019-08-28 11:59:18 +02:00
}
2019-09-17 17:19:00 +02:00
class method {
2020-01-06 14:35:29 +01:00
constructor ( name , basis = null ) {
2019-09-26 20:25:51 +02:00
this . name = name ;
this . basis = basis ;
2019-09-03 15:36:32 +02:00
}
2019-09-19 15:05:07 +02:00
static fromString ( str ) {
2019-09-26 20:25:51 +02:00
var vals = str . split ( "," )
2019-11-24 19:40:57 +01:00
if ( vals . length == 2 ) {
return new method ( vals [ 0 ] , vals [ 1 ] ) ;
}
else {
return new method ( vals [ 0 ] , null )
2019-09-26 09:32:03 +02:00
}
2019-09-19 15:05:07 +02:00
}
2020-02-20 11:54:50 +01:00
toString ( separator = "/" ) {
2019-09-26 20:25:51 +02:00
var str = this . name ;
2019-09-28 14:38:14 +02:00
if ( this . basis ) {
2020-02-20 11:54:50 +01:00
str = str + separator + this . basis ;
2019-09-03 15:36:32 +02:00
}
return str ;
2019-08-22 10:34:07 +02:00
}
2020-01-06 14:35:29 +01:00
get isTBE ( ) {
2020-01-03 17:12:43 +01:00
return /^TBE/ . test ( this . name )
}
2019-08-22 10:34:07 +02:00
}
2019-09-26 20:25:51 +02:00
class state {
constructor ( number , multiplicity , symetry ) {
this . number = number ;
this . multiplicity = multiplicity ;
this . symetry = symetry ;
2019-09-03 15:36:32 +02:00
} ;
2019-09-26 20:25:51 +02:00
toString ( ) {
var str = this . number + ' ^' + this . multiplicity + this . symetry ;
2019-09-03 15:36:32 +02:00
return str ;
} ;
toLaTeX ( ) {
2019-11-12 15:27:53 +01:00
var tex = String . raw ` ${ this . number } \: ^{ ${ this . multiplicity } } \m athrm{ ${ this . symetry } } ` ;
2019-09-03 15:36:32 +02:00
return tex ;
} ;
2019-08-22 10:34:07 +02:00
}
2019-11-02 15:10:57 +01:00
class DOI {
2020-01-08 11:02:15 +01:00
constructor ( doistring , IsSupporting = false ) {
2019-09-26 20:25:51 +02:00
this . string = doistring
2020-01-08 11:02:15 +01:00
this . IsSupporting = IsSupporting
2019-09-03 15:36:32 +02:00
} ;
2020-01-08 11:02:15 +01:00
static fromString ( str ) {
const vals = str . split ( "," )
2020-01-08 15:48:35 +01:00
if ( vals . length == 1 ) {
2020-01-08 11:02:15 +01:00
return new DOI ( vals [ 0 ] . toString ( ) )
}
else {
2020-01-08 15:48:35 +01:00
return new DOI ( vals [ 0 ] . toString ( ) , ( true ? vals [ 1 ] === true . toString ( ) : false ) )
2020-01-08 11:02:15 +01:00
}
}
2019-09-26 20:25:51 +02:00
toString ( ) {
2020-01-08 11:02:15 +01:00
var str = this . string ;
if ( this . IsSupporting ) {
str += " " + "(SI)"
}
2020-01-09 10:55:24 +01:00
return str
2019-09-03 15:36:32 +02:00
} ;
2019-08-28 11:59:18 +02:00
get url ( ) {
2020-01-06 14:35:29 +01:00
return new URL ( this . string , 'https://doi.org' ) . toString ( )
2019-12-10 15:15:46 +01:00
}
2019-08-28 11:59:18 +02:00
}
2019-09-26 09:32:03 +02:00
2019-09-26 20:25:51 +02:00
class excitationBase {
2020-01-06 14:35:29 +01:00
constructor ( initial , final , type = '' , T1 = null ) {
2019-09-26 20:25:51 +02:00
this . initial = initial ;
this . final = final
2020-03-08 16:17:04 +01:00
this . type = new excitationType ( 0 , new description ( type , true ) )
2020-03-31 14:24:01 +02:00
if ( type ) {
2019-12-10 13:55:32 +01:00
const tys = type . split ( ";" )
const arrow = String . raw ` \r ightarrow `
for ( const ty of tys ) {
if ( ty . includes ( arrow ) ) {
const [ initialt , finalt ] = ty . split ( arrow , 2 )
2020-01-06 14:35:29 +01:00
const initialts = initialt . split ( "," ) . map ( x => x . trim ( ) )
const finalts = finalt . split ( "," ) . map ( x => x . trim ( ) )
2020-01-08 16:30:55 +01:00
if ( initialts . length == 2 && finalts . length == 2 ) {
2020-01-06 14:35:29 +01:00
this . type . Value = this . type | excitationTypes . Double
2019-12-03 11:32:14 +01:00
}
2020-01-14 14:20:43 +01:00
else if ( initialts . length == 1 && finalts . length == 1 ) {
2020-01-06 14:35:29 +01:00
this . type . Value = this . type | excitationTypes . Single
2019-12-03 11:32:14 +01:00
}
2019-12-13 10:32:25 +01:00
if ( initialts . includes ( "n" ) && finalts . includes ( String . raw ` \p i^ \s tar ` ) ) {
2020-01-06 14:35:29 +01:00
this . type . Value = this . type | excitationTypes . nPis
2020-01-07 09:28:41 +01:00
} else if ( initialts . includes ( String . raw ` \p i ` ) && finalts . includes ( String . raw ` \p i^ \s tar ` ) ) {
2020-01-06 14:35:29 +01:00
this . type . Value = this . type | excitationTypes . PiPis
2019-12-01 18:14:45 +01:00
}
2020-01-06 14:35:29 +01:00
} else if ( ty . includes ( String . raw ` \m athrm{R} ` ) ) {
2020-03-20 11:09:48 +01:00
this . type . Value = this . type | excitationTypes . Rydberg
2020-01-06 14:35:29 +01:00
} else if ( ty . includes ( String . raw ` \m athrm{V} ` ) ) {
2020-03-20 11:09:48 +01:00
this . type . Value = this . type | excitationTypes . Valence
2019-12-01 18:14:45 +01:00
}
}
}
2020-01-07 11:42:44 +01:00
switch ( final . multiplicity ) {
case 1 :
2020-03-20 11:09:48 +01:00
this . type . Value = this . type | excitationTypes . SingletSinglet
2020-01-07 11:42:44 +01:00
break ;
case 3 :
2020-03-20 11:09:48 +01:00
this . type . Value = this . type | excitationTypes . SingletTriplet
2020-01-07 11:42:44 +01:00
break ;
}
2020-01-07 09:37:32 +01:00
if ( this . type . Value == 0 ) {
this . type . Value = excitationTypes . Others . Value ;
2020-01-06 21:03:06 +01:00
}
2019-12-01 18:14:45 +01:00
this . T1 = T1
2019-09-26 09:32:03 +02:00
}
}
2019-09-26 20:25:51 +02:00
class excitationValue extends excitationBase {
2020-01-06 14:35:29 +01:00
constructor ( initial , final , type , value , oscilatorForces = null , T1 = null , isUnsafe = false ) {
2019-12-10 13:55:32 +01:00
super ( initial , final , type , T1 )
2019-09-26 20:25:51 +02:00
this . value = value
2019-11-25 14:49:40 +01:00
this . oscilatorForces = oscilatorForces
2019-12-17 09:59:57 +01:00
this . isUnsafe = isUnsafe
2019-11-20 20:15:53 +01:00
}
}
2019-09-26 20:25:51 +02:00
class excitation extends excitationBase {
constructor ( initial , final , Eabs , Efluo , EZPE ) {
super ( initial , final )
this . Eabs = Eabs
this . Efluo = Efluo
this . EZPE = EZPE
2019-09-19 15:05:07 +02:00
}
2019-09-19 18:54:23 +02:00
get Eadia ( ) {
2019-09-26 20:25:51 +02:00
return ( this . Eabs + this . Efluo ) / 2
}
2019-09-19 15:05:07 +02:00
get Ezz ( ) {
2019-09-26 20:25:51 +02:00
return this . Eadia - this . EZPE
2019-08-28 11:59:18 +02:00
}
2019-09-03 15:36:32 +02:00
toString ( ) {
2019-09-26 20:25:51 +02:00
return this . start + ', ' + this . end + ', ' + noNanPrecision ( this . Eabs , 3 ) ;
2019-09-03 15:36:32 +02:00
}
2019-08-28 11:59:18 +02:00
}
2019-09-26 09:32:03 +02:00
class dataFileBase {
2019-09-26 20:25:51 +02:00
constructor ( ) {
this . molecule = ''
2019-11-28 15:55:05 +01:00
this . comment = ""
2019-09-26 20:25:51 +02:00
this . code = null
this . method = null
this . excitations = [ ]
this . DOI = null
2020-01-06 14:35:29 +01:00
this . sourceFile = null
2019-08-28 11:59:18 +02:00
}
2020-01-27 11:09:35 +01:00
static _GetMetaRexEx ( ) {
//metadata RegExp (start with #; maybe somme spaces; : ; maybe somme space; datas)
return /^#\s*([A-Za-z_]+)\s*:\s*(.*)$/ ;
}
2020-01-15 13:10:21 +01:00
CopyExcitationsTypeFrom ( data ) {
for ( const ex of this . excitations ) {
const ex2 = data . excitations . find ( ( e ) => {
return ( JSON . stringify ( e . initial ) === JSON . stringify ( ex . initial ) ) && ( JSON . stringify ( e . final ) === JSON . stringify ( ex . final ) )
} )
if ( ex2 !== undefined ) {
2020-03-20 11:09:48 +01:00
console . assert ( ex . type == 0 || ( ex2 . type ^ ( excitationTypes . Rydberg | excitationTypes . Valence ) == ex . type ^ ( excitationTypes . Rydberg | excitationTypes . Valence ) ) , "Excitation type error" , [ ex , ex2 , data . sourceFile ] )
2020-01-15 13:10:21 +01:00
ex . type = ex2 . type
}
}
}
2020-01-27 11:09:35 +01:00
static async loadAsync ( file , kind = undefined ) {
2019-10-02 19:20:37 +02:00
switch ( trueTypeOf ( file ) ) {
2019-11-02 16:21:11 +01:00
case String . name :
2020-01-06 14:35:29 +01:00
file = getFullDataPath ( file )
var str = await getTextFromFileUrlAsync ( file )
2019-10-02 19:20:37 +02:00
break ;
2019-11-02 16:21:11 +01:00
case File . name :
2020-01-06 14:35:29 +01:00
var str = await getTextFromUploadedFileAsync ( file )
2019-10-02 19:20:37 +02:00
break
}
2020-01-27 11:09:35 +01:00
var dat = this . loadString ( str , kind ) ;
2020-01-06 14:35:29 +01:00
dat . sourceFile = new websiteFile ( file )
2019-09-30 10:53:13 +02:00
return dat
2019-09-26 09:32:03 +02:00
}
2020-01-27 11:09:35 +01:00
_OnReadMetaPair ( key , value ) {
2019-09-26 20:25:51 +02:00
switch ( key ) {
2019-09-26 09:32:03 +02:00
case "molecule" :
2020-01-27 11:09:35 +01:00
this . molecule = value
2019-09-26 09:32:03 +02:00
break ;
case "comment" :
2020-01-27 11:09:35 +01:00
this . comment = value
2019-09-26 09:32:03 +02:00
break ;
case "code" :
2020-01-27 11:09:35 +01:00
this . code = code . fromString ( value )
2019-09-26 09:32:03 +02:00
break ;
case "method" :
2020-01-27 11:09:35 +01:00
this . method = method . fromString ( value )
2019-09-26 20:25:51 +02:00
break ;
2019-09-26 09:32:03 +02:00
case "doi" :
2020-01-27 11:09:35 +01:00
this . DOI = DOI . fromString ( value ) ;
2019-09-26 09:32:03 +02:00
break ;
default :
}
2019-08-28 11:59:18 +02:00
}
2020-01-27 11:09:35 +01:00
_OnReadRow ( line ) {
var vals = line . match ( /\([^\)]+\)|\S+/g )
var start = new state ( parseInt ( vals [ 0 ] , 10 ) , parseInt ( vals [ 1 ] , 10 ) , vals [ 2 ] ) ;
var end = new state ( parseInt ( vals [ 3 ] , 10 ) , parseInt ( vals [ 4 ] , 10 ) , vals [ 5 ] ) ;
var hasType = vals . length >= 7 && isNaN ( vals [ 6 ] )
var type = ( ( vals . length >= 7 && hasType ) ? vals [ 6 ] : null )
2020-03-31 14:24:01 +02:00
if ( type === "_" ) {
type = null
}
2020-01-27 11:09:35 +01:00
if ( type ) {
const m = type . match ( /^\(([^\)]*)\)$/ )
if ( m ) {
type = m [ 1 ]
2019-08-28 11:59:18 +02:00
}
}
2020-02-04 09:14:57 +01:00
var val = ( ( vals . length >= 7 + hasType ) ? new stringNumber ( vals [ 6 + hasType ] ) : NaN )
2020-03-17 14:00:31 +01:00
var T1 = ( ( vals . length >= 8 + hasType ) ? new stringNumber ( vals [ 7 + hasType ] ) : NaN )
var oscilatorForces = ( ( vals . length >= 9 + hasType ) ? new stringNumber ( vals [ 8 + hasType ] ) : NaN )
2020-01-27 11:09:35 +01:00
var isUnsafe = ( ( vals . length >= 10 + hasType ) ? vals [ 9 + hasType ] === true . toString ( ) : false )
var ex = new excitationValue ( start , end , type , val , oscilatorForces , T1 , isUnsafe ) ;
if ( this . VertExcitationKind ) {
ex . VertExcitationKind = this . VertExcitationKind
}
return ex ;
} ;
_OnReadMeta ( line ) {
// get key value
var match = line . match ( dataFileBase . _GetMetaRexEx ( ) )
// normalize key to lower
var key = match [ 1 ] . toLowerCase ( )
//if data has value
if ( match . length == 3 && match [ 2 ] ) {
var val = match [ 2 ]
this . _OnReadMetaPair ( key , val )
}
}
static loadString ( text , kind = null ) {
// for each line with metadata
var ismetaArea = true ;
var dat = new VertDataFile ( )
2019-09-26 20:25:51 +02:00
for ( var line of text . split ( "\n" ) ) {
2019-08-28 11:59:18 +02:00
//if it's not empty line
2019-09-26 20:25:51 +02:00
line = line . trim ( ) ;
if ( line ) {
2019-08-28 11:59:18 +02:00
//if # may be metadata or comment
2019-09-26 20:25:51 +02:00
if ( line . charAt ( 0 ) == "#" ) {
2019-08-28 11:59:18 +02:00
//if it's metadata
2020-01-27 11:09:35 +01:00
if ( ismetaArea && dataFileBase . _GetMetaRexEx ( ) . test ( line ) ) {
dat . _OnReadMeta ( line ) ;
2019-09-26 20:25:51 +02:00
}
2019-08-28 11:59:18 +02:00
} else { //else its row
2019-09-26 20:25:51 +02:00
ismetaArea = false ;
2020-01-27 11:09:35 +01:00
dat . excitations . push ( dat . _OnReadRow ( line , kind ) ) ;
2019-08-28 11:59:18 +02:00
}
}
2019-09-26 20:25:51 +02:00
}
2020-03-14 17:29:47 +01:00
var stfy = dat . excitations . map ( e => JSON . stringify ( [ e . initial , e . final ] ) )
var double = [ ]
stfy . forEach ( function ( element , i ) {
// Find if there is a duplicate or not
if ( stfy . indexOf ( element , i + 1 ) >= 0 ) {
// Find if the element is already in the result array or not
2020-03-26 17:53:42 +01:00
if ( double . indexOf ( element ) === - 1 ) {
2020-03-14 17:29:47 +01:00
double . push ( dat . excitations [ i ] )
}
}
} ) ;
console . assert ( double . length === 0 , "Double found" , double , dat . sourceFile )
2020-03-17 13:48:42 +01:00
for ( const ex of dat . excitations ) {
console . assert ( Number . isNaN ( ex . T1 . valueOf ( ) ) | ex . T1 > 50 | ex . isUnsafe == true , "Must be unsafe" , dat , ex )
}
2019-08-28 11:59:18 +02:00
return dat
}
2019-08-22 10:34:07 +02:00
}
2020-01-27 11:09:35 +01:00
class VertExcitationKinds {
2020-03-08 16:17:04 +01:00
static get Absorbtion ( ) { return new VertExcitationKind ( 1 , new description ( "Absorption" ) ) }
static get Fluorescence ( ) { return new VertExcitationKind ( 1 << 1 , new description ( "Fluorescence" ) ) }
2020-01-28 17:25:49 +01:00
static get All ( ) { return EnumUltils . getAll ( this , VertExcitationKind ) }
static GetFlags ( value ) { return EnumUltils . GetFlags ( value , this , VertExcitationKind ) }
2020-01-27 11:09:35 +01:00
}
class VertDataFile extends dataFileBase {
constructor ( VertExcitationKind ) {
2019-09-26 09:32:03 +02:00
super ( )
2020-01-27 11:09:35 +01:00
this . VertExcitationKind = VertExcitationKind
2019-09-26 20:25:51 +02:00
this . geometry = null
2019-09-26 09:32:03 +02:00
}
2020-01-27 11:09:35 +01:00
_OnReadMetaPair ( key , value ) {
2019-09-26 20:25:51 +02:00
if ( key == "geom" ) {
2020-01-27 11:09:35 +01:00
this . geometry = method . fromString ( value )
2019-09-26 09:32:03 +02:00
}
2019-09-26 20:25:51 +02:00
else {
2020-01-27 11:09:35 +01:00
super . _OnReadMetaPair ( key , value )
2019-09-26 09:32:03 +02:00
}
}
2020-01-27 11:09:35 +01:00
_OnReadRow ( line , kind ) {
var ex = super . _OnReadRow ( line )
ex . VertExcitationKind = kind
return ex
2019-09-26 09:32:03 +02:00
}
}
2019-09-26 20:25:51 +02:00
class CombinedData {
constructor ( ) {
this . Abs = null
this . Fluo = null
this . ZPE = null
2019-09-26 09:32:03 +02:00
}
get excitations ( ) {
2019-09-26 20:25:51 +02:00
var exs = [ ]
var dic = new Map ( )
if ( this . Abs != null ) {
for ( const el of this . Abs . excitations ) {
var key = JSON . stringify ( [ el . initial , el . final ] )
if ( ! dic . has ( key ) ) {
dic . set ( key , { } )
2019-09-26 09:32:03 +02:00
}
2019-09-26 20:25:51 +02:00
dic . get ( key ) [ "abs" ] = el . value
}
if ( this . Fluo != null ) {
for ( const el of this . Fluo . excitations ) {
var key = JSON . stringify ( [ el . initial , el . final ] )
if ( ! dic . has ( key ) ) {
dic . set ( key , { } )
}
dic . get ( key ) [ "fluo" ] = el . value
2019-09-26 09:32:03 +02:00
}
2019-09-26 20:25:51 +02:00
}
if ( this . ZPE != null ) {
for ( const el of this . ZPE . excitations ) {
var key = JSON . stringify ( [ el . initial , el . final ] )
if ( ! dic . has ( key ) ) {
dic . set ( key , { } )
}
dic . get ( key ) [ "ZPE" ] = el . value
}
}
dic . forEach ( ( value , key ) => {
var eabs = NaN
var efluo = NaN
var eZPE = NaN
var mykey = JSON . parse ( key )
for ( var el of mykey ) {
Reflect . setPrototypeOf ( el , state . prototype )
}
if ( "abs" in value ) {
eabs = value [ "abs" ]
}
if ( "fluo" in value ) {
efluo = value [ "fluo" ]
}
if ( "ZPE" in value ) {
eZPE = value [ "ZPE" ]
2019-09-26 09:32:03 +02:00
}
2019-09-26 20:25:51 +02:00
exs . push ( new excitation ( mykey [ 0 ] , mykey [ 1 ] , eabs , efluo , eZPE ) )
2019-09-26 09:32:03 +02:00
} )
2019-09-26 20:25:51 +02:00
return exs
2019-09-26 09:32:03 +02:00
}
2019-09-26 20:25:51 +02:00
}
2019-09-26 09:32:03 +02:00
}