/* Copyright (c) 2020, 2023, The Unified Company. This code is part of Unify. This program is free software; you can redistribute it and/or modify it under the terms of the ESA Software Community License - Strong Copyleft LICENSE, as published by the ESA. See the ESA Software Community License - Strong Copyleft LICENSE, for more details. https://unifyjs.org */ import css from '../client/css.js'; import vector2 from './math/vector2.js'; import definitions from './definitions.js'; import consoleColors from './consoleColors.js'; class tools{ css = new css(); isUnifyObject( propertyName, propertyValue ) { const rejectClassNames = new Array( "core", "cssRules", "stateMachine", "socketManager", "animationManager", "permissionManager", "permissionObject", "fileLoader", "WriteStream", "client"); const rejectPropertyNames = new Array( "objectToManipulate", "parent", "client", "queryRoot", "queryChildren", "queryCount" ); if( propertyValue && typeof propertyValue.parseUnify == "boolean" && propertyValue.parseUnify == false ) { return false; } if( propertyValue == null ) { return false; } if( !propertyValue ) { return false; } var className = this.getClassName( propertyValue ); if( rejectPropertyNames.includes( propertyName ) ) { return false; } if( rejectClassNames.includes( className ) ) { return false; } if( Array.isArray( propertyValue ) ) { return false; } if( this.getEnvironment() == "Browser" ) { if( propertyValue instanceof HTMLElement ) { return false; } } if( typeof propertyValue == "object" ){ return true; } else { return false; } } htmlEntities( str ) { var escapeChars = { '¢' : 'cent', '£' : 'pound', '¥' : 'yen', '€': 'euro', '©' :'copy', '®' : 'reg', '<' : 'lt', '>' : 'gt', '"' : 'quot', '&' : 'amp', '\'' : '#39' }; var regexString = '['; for(var key in escapeChars) { regexString += key; } regexString += ']'; var regex = new RegExp( regexString, 'g'); return str.replace(regex, function( m ) { return '&' + escapeChars[ m ] + ';'; }); } reverseString( string ) { var splitString = string.split(""); var reverseArray = splitString.reverse(); var joinArray = reverseArray.join(""); return joinArray; } slash( path ) { const isExtendedLengthPath = /^\\\\\?\\/.test(path); if ( isExtendedLengthPath ) { return path; } return path.replace(/\\/g, '/'); } serializeString( json ) { if( Buffer.isBuffer( json ) ) { return json.toString(); } else { return json; } } isJsonString( str ) { try { JSON.parse( str ); } catch (e) { return false; } return true; } serializeJSON( json ) { if( this.isJsonString( json ) ) { return JSON.parse( json ); } } createOrderedArray( array ) { var keys = Object.keys( array ); var output = new Array(); for ( var i = 0; i < keys.length; i++ ) { var key = keys[i]; output[ i ] = array[ key ]; } return output; } getEnvironment() { if( typeof document != "undefined" ) { return "Browser"; } if( global ) { return "Node" } } // Security getObjectByPath( application, applicationPath ) { var objectName = applicationPath.pop(); //console.log("getObjectByPath", objectName); if( !application[ objectName ] || typeof objectName != "string" ) { throw new Error('Get object by Path Failed, Faulty path'); } var currentApplication = application[ objectName ]; if( !currentApplication ) { throw new Error( 'Get object by Path Failed, Faulty path' ); } if( applicationPath.length > 0 ) { return this.getObjectByPath( currentApplication, applicationPath ); } else { return currentApplication; } } isNumber( value ) { return ( typeof value === 'number' && isFinite( value ) ); } isNumberObject( n ) { return ( Object.prototype.toString.apply( n ) === '[object Number]' ); } isCustomNumber( n ) { return this.isNumber( n ) || this.isNumberObject( n ); } structuredClone( object ) { var prototype = Object.getPrototypeOf( object ); var create = Object.create( prototype ); return Object.assign( create , object ); } extendedClassIsColumn() { } isUnifyColumn( object ) { var extendedClasses = this.getExtendedClassesMulti( object ); var isColumn = false; for ( var j = 0; j < extendedClasses.length; j++ ) { var extendedClass = extendedClasses[j] var className = this.getClassName( extendedClass ); if( className == "column" ) { isColumn = object; } } return isColumn; } filterPrims( item, prims, type ) { if( prims[ type ].hasOwnProperty( item ) ) { return false; } else { prims[ type ][ item ] = true; return true; } } filterObjects( item ) { var filterObjects = this.filterObjects; if( filterObjects.indexOf( item ) >= 0 ) { return false; } else { filterObjects.push( item ); return filterObjects; } } uniqueFilter( item ) { var type = typeof item; var prims = this.prims; if( type in prims ) { return this.filterPrims( item, prims, type ); } else { return this.filterObjects( item ); } } uniqueArrayRows( a ) { this.prims = new Object(); this.prims.boolean = new Object(); this.prims.number = new Object(); this.prims.string = new Object(); this.filterObjects = new Array(); return a.filter( this.uniqueFilter.bind( this ) ); } simpleClone( object ) { var prototype = Object.getPrototypeOf( object ); var created = Object.create( prototype ); let clone = Object.assign( created, object ); return clone; } getPrototypeRecursive( object, properties = new Array() ) { properties.push( ...Object.getOwnPropertyNames( object ) ) var prototype = Object.getPrototypeOf( object ) if( prototype ) { return this.getPrototypeRecursive( prototype, properties ); } else { return properties; } } filterFunctions( element, index, arr ) { var nextElement = arr[ index + 1]; var elementType = typeof this[ element ]; if ( element != nextElement && elementType == "function" ){ return true; } } getAllFuncs( object ) { var properties = this.getPrototypeRecursive( object ); var sorted = properties.sort(); var filterd = sorted.filter( this.filterFunctions.bind( object ) ) return filterd; } createRandomKey( numCharacters ) { var token = ""; for ( var i = 0; i < numCharacters; i++ ) { token += Math.floor( Math.random() * 10 ); } return token; } isCSSProperty( propertyName ) { var object = definitions.css; if( typeof object[propertyName] !== 'undefined' ) { return true; } else { return false; } } hasUserPrototype( obj ) { return obj.constructor !== Object; } addTabToArrayLines( lines, numSpaces ) { var tabbedRows = new Array(); for ( var i = 0; i < lines.length; i++ ) { var row = lines[i]; tabbedRows[i] = "".padEnd( numSpaces ) + row; } return tabbedRows; } addTabToBeginOfString( string, numSpaces ) { var lines = string.split("\n"); var tabbedRows = this.addTabToArrayLines( lines, numSpaces ); return tabbedRows.join("\n"); } addTabToBegin( string, numSpaces ) { if( typeof string == "string" ) { return this.addTabToBeginOfString( string, numSpaces ); } else { return string; } } getChildFromEntry( parent, entry, children ) { const invalid = new Array( "parent", "table", "user" ); var name = this.getClassNameByEntry( entry ); var child = this.getObjectByEntry( entry ); if( !this.isUnifyObject( name, child ) ) { return false; } if( invalid.includes( name ) ) { return false; } child.propertyName = name; child.parent = parent; children.push( child ); } getChildren( object, bindParent = true ) { var children = new Array(); var entries = Object.entries( object ); for( var c = 0; c < entries.length; c++ ){ var entry = entries[ c ]; this.getChildFromEntry( object, entry, children ); } return children; } addPropertyToArray( property, properties ) { const invalid = new Array( "user" ); if( this.isUnifyObject( property.name, property.value ) ) { return false; } if( invalid.includes( property.name ) ) { return false; } properties.push( property ); } createPropertyFromEntry( entry ) { var name = this.getClassNameByEntry( entry ); var object = this.getObjectByEntry( entry ); var property = new Object(); property.name = name; property.value = object; return property; } getProperties( object ) { var properties = new Array(); var entries = Object.entries( object ); for( var c = 0; c < entries.length; c++ ){ var entry = entries[c]; var property = this.createPropertyFromEntry( entry ) ; this.addPropertyToArray( property, properties ) } return properties; } replaceChildWithFragment( source, newType ) { const frag = document.createDocumentFragment(); while ( source.firstChild ) { frag.appendChild( source.firstChild ); } const newElem = document.createElement( newType ); newElem.appendChild( frag ); source.parentNode.replaceChild( newElem, source ); } replaceElement( source, newType, parentElement ) { if( parentElement ) { source.parentNode = parentElement; } if( source.tagName != newType.toUpperCase() ) { this.replaceChildWithFragment( source, newType ); } } parseObject( object ) { var entries = Object.entries( object ); return entries[0]; } getFirstEntry( object ) { var entries = Object.entries( object ); if( entries[0] ) { return entries[0]; } else { return false; } } getExtendedClass( object ) { if( !object.prototype ) { object = object.constructor; } return object.prototype.__proto__; } removeDuplicates( arr ) { return arr.filter( ( item, index ) => arr.indexOf( item ) === index ); } removeEmptyRows( arr ) { return arr.filter( n => n ); } isVisible( element ) { if( !element ) { return false; } if( !element.offsetWidth ) { return false; } return element.offsetWidth > 0 || element.offsetHeight > 0; } getExtendedObjects( object ) { if( !object.getClassName ) { return false; } if( typeof document == "undefined" ){ var extendedObjects = global.extendMap[ object.getClassName() ]; } else { var extendedObjects = document.extendMap[ object.getClassName() ]; } return extendedObjects; } getSingleExtendedClassInArray( object ) { if( !object.prototype ) { object = object.constructor; } var extendedClass = this.getExtendedClass( object ); if( !extendedClass ) { return false; } else { return new Array( new extendedClass.constructor() ); } } getExtendedClassesMulti( object ) { var extendedObjects = this.getExtendedObjects( object ); if( extendedObjects ) { return extendedObjects; } else { return this.getSingleExtendedClassInArray( object ); } } getExtendedClassName( object ) { return this.getClassName( this.getExtendedClass( object ) ); } logError( message ) { console.log( this.consoleColors().red( message ) ); } debugVar( object, variable ) { var colors = this.consoleColors(); var path = this.getApplicationPath(object); var pathColor = colors.red( "(" + path.toString() + ")" ); var variableColor = colors.magenta( variable ); console.log( variableColor + pathColor ); } logParameter2( parameter2 ) { if( typeof parameter2 == "string" ) { console.log( parameter2 ); } else { this.logObject( parameter2 ); } } log( parameter1, parameter2, parameter3 ) { var colors = this.consoleColors(); if( typeof parameter1 == "string" ) { console.log( colors.yellow("\n\n" + parameter1) ); } else { this.logObject( parameter1 ); } if( parameter2 ) { this.logParameter2( parameter2 ); } } log( ...value ) { for (var i = 0; i < value.length; i++) { console.log( util.inspect( value[i], { showHidden: false, depth: 1, colors: true }) ) } } logObject( object ) { var colors = this.consoleColors(); var path = this.getApplicationPath(object); var pathColor = colors.red( "(" + path.reverse().toString() + ")" ); console.log( pathColor + " " + this.getClassName( object ) ); console.log( this.serialize( object ) ); } saveImportDefault( importObject ) { if( importObject.default ) { return importObject.default; } else { console.log("error2 ", url, " does not exist"); } } async saveImport( url ) { var importObject = await import( url ); if( importObject ) { return this.saveImportDefault( importObject ); } else { console.log("error1 ", url, " does not exist"); } } getApplicationDepth( object ) { return this.getApplicationPath( object ).length; } getParentApplicationPath( object, array ) { array.push( object.propertyName ); return this.getApplicationPath( object.parent, array ); } getApplicationPath( object, array = new Array() ) { if( object.parent ) { return this.getParentApplicationPath( object, array ) } else { return array; } } getClassName( object ) { if( object.__className ) { return object.__className; } if( !object ) { return false; } return object.constructor.name; //var rollupFix = object.constructor.name.split("$")[0].split("$")[0]; //var webPackFix = rollupFix.split("_") //return rollupFix; //return webPackFix[webPackFix.length-1]; } getID( object ){ if( object.id ){ return object.id; } else { return ""; } } getClassNameByObject( object ) { if( typeof object == "string" ) { return object; } var entry = this.parseObject( object ); var className = this.getClassNameByEntry( entry ); return className; } getClassNameByEntry( entry ) { return entry[0]; } getObjectByEntry( entry ) { if( entry[1] ) { return entry[1]; } else { return false; } } logObject( object, message ) { if( object.debug ) { console.log( message, object.getClassName(), "\n" ); } } getFirstEntryName( object ) { var entries = Object.entries( object ); return entries[0][0]; } delay( millisec ) { return new Promise(resolve => { setTimeout(() => { resolve('') }, millisec); }) } getPropertyNameWithID( object ) { if( object.id ) { // bug // + "_" + object.id; todo: Make that only id is added when css content differs from other rows/ids // object.propertyName.replace( object.id, "" ) is, Unify objects rendered by rendercollections have an id in the propertyName // This results in double ids return this.CamelCase( object.propertyName ) + "_" + object.id; } else { return this.CamelCase( object.propertyName ); } if( !propertyName ) { return ""; } } createCSSClassName( object ) { var propertyName = this.getPropertyNameWithID( object ); if( object.parent ) { object = object.parent; } return this.getClassName( object ) + propertyName; } CamelCase( string ){ if( !string ) { return false; } string = string.toUpperCase(); string = string[0].toUpperCase() + string.slice( 1, string.length ).toLowerCase(); return string; } firstLowerCase( string ){ if( string ) { return string[0].toLowerCase() + string.slice( 1, string.length ); } else { return false; } } cleanRollup( term ) { return term; // Deprecated //return term.replaceAll("$0", "").replaceAll("$1", "").replaceAll("$2", ""); } cleanEntry( entry ) { const invalid = new Array( "parent", "table", "user", "objectToManipulate" ); var name = this.getClassNameByEntry( entry ); var object = this.getObjectByEntry( entry ); if( invalid.includes( name ) ) { delete object[name]; } } cleanObject( object ) { var entries = Object.entries( this ); for ( var i = 0; i < entries.length; i++ ) { var entry = entries[i]; this.cleanEntry( entry ); } } objectExtendsTablePrimitive( object ) { var extendedClass = this.getExtendedClass( object ); if( !extendedClass ) { return false; } var className = this.getClassName( extendedClass ); if( className == "table" ) { return true; } else { return false; } } objectIsTable( object ) { if( !object ) { return false; } if( !object.prototype ) { object = object.constructor; } return this.objectExtendsTablePrimitive( object ); } extendIsRenderCollection( object ) { var extendedClass = this.getExtendedClass( object ); var className = this.getClassName( extendedClass ); if( className == "renderCollection" ) { return true; } else { return false; } } objectIsRenderCollection( object ) { var className = this.getClassName( object ); if( className == "renderCollection" ) { return true; } if( !object.prototype ) { object = object.constructor; } return this.extendIsRenderCollection( object ); } getTableName( object ) { if( !this.objectIsTable( object ) ) { object = this.getTableFromObject( object ); } var className = this.getClassName( object ); return className; } isTableInHierarchy( object ) { } getParentWithTable( object, tableName ) { if( this.getClassName( this.getTableFromObject( object ) ) == tableName ) { return object; } else { return this.getParentWithTable( object.parent, tableName ); } } classNameIsTable( className ) { // todo deprecated? //if( className == "table" || className == "unify_table_table" || className == "server_table_table" || className == "table_table" ) { if( className == "table" ){ return true; } else { return false; } } evaluateExtendedClass( object, extendedClass ) { var className = this.getClassName( extendedClass ); if( this.classNameIsTable( className ) ) { return object; } var table = this.getTableFromObject( extendedClass ); if( table ) { return table; } else { return false; } } getTableFromExtendedClasses( object, extendedClasses ) { for ( var i = 0; i < extendedClasses.length; i++ ) { var extendedClass = extendedClasses[i]; var table = this.evaluateExtendedClass( object, extendedClass ); if( table ) { return table; } } } getTableFromObject( object ) { if( object.table ) { return object.table; } var className = this.getClassName( object ); if( className ) { var extendedClasses = this.getExtendedClassesMulti( object ); return this.getTableFromExtendedClasses( object, extendedClasses ); } else { console.log("classname not found",object); } } isServerValue( name, value ){ const invalid = new Array( "style", "type", "label", "flexDirection", "boxPadding", "width", "DOM_ELEMENT" ); const valid = new Array( "rows" ); if( valid.includes( name ) ){ return true; } if( invalid.includes( name ) ){ return false; } if( this.css.propertyIsStyle( name ) ) { return false; } if( typeof value == "number" ){ return true; } if( typeof value == "string" ){ return true; } else { return false; } } deleteNormalBrowserEvent( object, browserEvent ) { if( object[ browserEvent ] ) { delete object[ browserEvent ]; } } deleteServerBrowserEvent( object, browserEvent ) { if( object[ "server" + this.CamelCase( browserEvent ) ] ) { delete object[ "server" + this.CamelCase( browserEvent ) ]; } } groomBrowserEvents( object ) { var browserEvents = definitions.browserEvents; for ( var i = 0; i < browserEvents.length; i++ ) { var browserEvent = browserEvents[ i ]; this.deleteNormalBrowserEvent( object, browserEvent ); this.deleteServerBrowserEvent( object, browserEvent ); } } getCircularReplacer( key, value ) { if ( typeof value === "object" && value !== null ) { if ( this.seen.has( value ) ) { return; } this.seen.add( value ); } return value; } sizeOf( object ) { this.seen = new WeakSet(); var stringObject = JSON.stringify( object, this.getCircularReplacer.bind( this ) ); // OR stringObject = JSON.stringify( object, ( key, value ) => this.getCircularReplacer( key, value ) ); var finalObject = JSON.parse( stringObject ); return global.objectSizeof( finalObject ); } groomCssProperties( object, propertyName ) { var propertyNameWithoutBox = propertyName.replace("box", ""); if( this.css.propertyHasStyle( propertyNameWithoutBox ) ) { delete object[ propertyName ]; } } groomRemainingProperties( object, propertyName, propertyValue ) { //if( !this.isServerValue( propertyName, propertyValue ) && !this.isUnifyObject(propertyName, propertyValue ) ) { // delete object[ propertyName ]; //} } groomPermissionMethods( object ) { if( object.enableWRITE ) { delete object.enableWRITE; } if( object.disableWRITE ) { delete object.disableWRITE; } if( object.enableREAD ) { delete object.enableREAD; } if( object.disableREAD ) { delete object.disableREAD; } if( object.enableDELETE ) { delete object.enableDELETE; } if( object.disableDELETE ) { delete object.disableDELETE; } } groomBrowserMethods( object ) { if( object.create ) { delete object.create; } if( object.update ) { delete object.update; } if( object.afterLoad ) { delete object.afterLoad; } } groomEntry( entry, object ) { var propertyName = this.getClassNameByEntry( entry ); var propertyValue = this.getObjectByEntry( entry ); this.groomPermissionMethods( object ); this.groomBrowserMethods( object ); this.groomBrowserEvents( object ); this.groomCssProperties( object, propertyName ); this.groomRemainingProperties( object, propertyName, propertyValue ); if( this.isUnifyObject(propertyName, propertyValue ) ) { this.groomApplicationObject( propertyValue ); } return object; } groomApplicationObject( object ) { var entries = Object.entries( object ); for( var c = 0; c < entries.length; c++ ) { var entry = entries[c]; this.groomEntry( entry, object ); } return object; } serializeEntry( entry, serializedObject ) { var propertyName = this.getClassNameByEntry( entry ); var propertyValue = this.getObjectByEntry( entry ); this.serializeProperty( propertyName, propertyValue, serializedObject ); } serializeEntries( entries ) { var serializedObject = new Object(); for( var c = 0; c < entries.length; c++ ) { var entry = entries[c]; this.serializeEntry( entry, serializedObject ); } return serializedObject; } serialize( object, is_root = true, cut ){ var entries = Object.entries( object ); this.serializeEntries( entries ); return this.createOuterObject( object, serializedObject, is_root ); } serializeServerProperty( object, propertyName, propertyValue ) { object[ propertyName ] = propertyValue; } serializedUnifyObject( object, propertyName, propertyValue ) { object[ propertyName ] = this.serialize( propertyValue, false ); } serializeProperty( propertyName, propertyValue, object ) { if( this.isServerValue( propertyName, propertyValue ) ) { this.serializeServerProperty( object, propertyName, propertyValue ); } if( this.isUnifyObject(propertyName, propertyValue ) ) { this.serializedUnifyObject( object, propertyName, propertyValue ); } return object; } isArray( array ) { if( !array ) { return false; } return Array.isArray( array ); } validateNumber( value ) { if( typeof value == "number" ) { return value; } else { return false; } } validateString( value ) { if( typeof value == "string" ) { return value; } else { return false; } } validateValue( value ) { if( typeof value == "string" || typeof value == "number" || typeof value == "boolean" ) { return value; } else { return false; } } validateBoolean( value ) { if( typeof value == "boolean" ) { return value; } else { return false; } } validateArray( values ) { if( !this.isArray( values ) ) { return false; } for( var c = 0; c < values.length; c++ ) { values[ c ] = this.validateString( values[ c ] ); } return values; } is_allowed_value( value ) { if( this.is_int( value ) ) { return true; } if( this.is_text( value ) ) { return true; } if( this.is_boolean( value ) ) { return true; } if( this.is_object( value ) ) { return true; } return false; } is_float( value ) { if( typeof value == "number" ) { return true; } else { return false; } } is_number( value ) { if( typeof value == "number" ) { return true; } else { return false; } } is_object( value ) { if( typeof value == "object" ) { return true; } else { return false; } } is_text( value ) { if( typeof value == "string" ) { return true; } else { return false; } } is_string( value ) { if( typeof value == "string" ) { return true; } else { return false; } } is_boolean() { if( typeof value == "boolean" ) { return true; } else { return false; } } is_int( value ) { if( typeof value == "number" ) { return true; } else { return false; } } createOuterObjectWithRoot() { var objectName = this.getClassName( object ); var output = new Object(); output[ objectName ] = serializedObject; return output; } createOuterObject( object, serializedObject, is_root ) { if( is_root ) { return this.createOuterObjectWithRoot( object, serializedObject ); } else { return serializedObject; } } consoleColors() { return consoleColors; } getFirstObjectFromEntries( entries ) { var firstChild = entries[0][1]; if( firstChild ) { return firstChild; } else { return object; } } getFirstChild( object ) { var entries = Object.entries( object ); if( entries[0] ) { return this.getFirstObjectFromEntries( entries ); } } } String.prototype.replaceAll = function replaceAll( search, replace ) { return this.split( search ).join( replace ); } export default new tools();