Files
Unify/framework/client/core.js

1524 lines
24 KiB
JavaScript
Raw Normal View History

2025-12-25 11:16:59 +01:00
/*
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 GNU AFFERO GENERAL PUBLIC LICENSE,
as published by the Free Software Foundation.
See the GNU AFFERO GENERAL PUBLIC LICENSE, for more details.
https://unifyjs.org
*/
import dom from './dom.js';
import css from './css.js';
import eventManager from './eventManager.js';
import socketManager from './socketManager.js';
import animationManager from './animation/animationManager.js';
import themeSwitcher from './themeSwitcher.js';
import fileLoader from './fileLoader.js';
import collection from './collection.js';
import tools from '../unify/tools.js';
import defaultObject from '../unify/defaultObject.js';
import vector2 from '../unify/math/vector2.js';
import definitions from "../unify/definitions.js"
import stateMachine from './stateMachine.js';
document.stateMachine = new stateMachine();
var objectsPendingToCreate = new Array();
export default class core {
dom = new dom();
css = new css();
eventManager = new eventManager();
themeSwitcher = new themeSwitcher();
fileLoader = new fileLoader();
stateMachine = document.stateMachine;
type = "core";
objects = new Array();
pathObjects = new Array();
root = false;
user = false;
wait = false;
addDOMToSelector = true;
loadAnimations = true;
createStyleSheet = true;
initializeRenderLoop = true;
parseEvents = true;
executeMethods = true;
classObjects = [];
sourcePathObjects = [];
visibleObjectClassNames = "";
constructor() {
this.themeSwitcher.setCore( this );
}
setUser( user ) {
this.user = user;
for (var i = 0; i < this.objects.length; i++) {
this.objects[i].user = user;
}
}
clearCache() {
this.css.clearCache();
}
mapClassDependencies( idObjects ) {
idObjects = idObjects.filter( function() { return true; } );
if( idObjects.length > 0 ) {
for ( var i = 0; i < idObjects.length; i++ ) {
var currentClass = idObjects[i];
var extendObjects = currentClass.getExtends();
this.mapExtendsDependencies( extendObjects, currentClass );
}
}
}
mapExtendsDependencies( extendObjects, currentClass ) {
for ( var c = 0; c < extendObjects.length; c++ ) {
var extendName = extendObjects[c];
if( !this.dependencieMap[ extendName ] ) {
this.dependencieMap[ extendName ] = new Array();
} else {
this.dependencieMap[ extendName ].push( currentClass );
}
}
}
createDependencyMap() {
var classObjects = this.classObjects;
var keys = Object.keys( classObjects );
this.dependencieMap = new Array();
for ( let className of keys ) {
//console.log("create map for key: ", className);
var idObjects = classObjects[ className ];
this.mapClassDependencies( idObjects );
}
}
getFirstByClassName( classname, object = false ) {
this.extendDefaultObject( object );
if( object.getClassName( ) == classname ) {
//console.log("found object", object.getClassName( ) );
return object;
}
var children = object.getChildren();
for( var c = 0; c < children.length; c++ ) {
var child = children[c];
var result = this.getFirstByClassName( classname, child ) ;
if( result ) {
return result;
}
}
}
getObjectByPath( applicationPath, application = false ) {
if( !application ) {
application = this.root;
}
var objectName = applicationPath.pop();
var currentApplication = application[ objectName ];
if( applicationPath.length > 0 ) {
return this.getObjectByPath( applicationPath, currentApplication );
} else {
return currentApplication;
}
}
removeCollection( collection ) {
var rows = collection.rows;
for( var b = 0; b < object.rows.length; b++ ) {
var row = object.rows[b];
if( row.getClassName ) {
this.removeObject( row.getClassName(), row.id );
}
}
}
removeChildren( object ) {
var children = object.getChildren();
for( var b = 0; b<children.length; b++ ) {
var child = children[b];
if( child.getClassName ) {
this.removeObject( child.getClassName(), child.id );
}
}
if( object.getCollection ) {
if( collection ) {
var collection = object.getCollection();
this.removeCollection( collection );
}
}
}
removeObject( className, id ) {
if( className && id ) {
var classObjects = this.classObjects[ className ];
if( classObjects ) {
var object = classObjects[ id ];
if( object ) {
// todo bug This causes an infinity loop error
//this.removeChildren( object );
delete this.classObjects[ className ][ id ];
}
}
}
}
getObjectByClassName( className, id ) {
if( !this.classObjects[ className ] ) {
return false;
}
var objects = this.classObjects[ className ][ id ];
if( objects ) {
return objects;
} else {
return false;
}
}
getObjectsByClassName( className ) {
var objects = this.classObjects[ tools.validateString( className ) ];
if( objects ) {
var sortedArray = new Array();
for ( var key in objects ) {
if ( key === 'length' || !objects.hasOwnProperty( key ) ) {
continue;
}
sortedArray.push( objects[key] );
}
return sortedArray;
} else {
return false;
}
}
getObjectsByPropertyName( propertyName ) {
var objects = this.objects;
var matches = new Array();
for( var c = 0; c < objects.length; c++) {
var object = objects[ c ];
if( object.propertyName == propertyName ) {
matches.push( object );
}
}
return matches;
}
setObjectClasses( object ) {
object.__proto__.core = this;
object.__proto__.socketManager = this.socketManager;
object.__proto__.stateMachine = this.stateMachine;
if( this.loadAnimations ) {
object.__proto__.animationManager = new animationManager();
}
object.__proto__.fileLoader = this.fileLoader;
}
callAfterLoad( object ) {
//console.log("callAfterLoad", object);
if( object.afterLoad ) {
object.afterLoad();
}
var children = object.getChildren();
for( var c = 0; c < children.length; c++ ) {
var child = children[c];
if( this.allowParseChild( object, child ) ) {
this.callAfterLoad( child );
}
}
}
callAfterThemeLoad( object ) {
if( object.afterThemeLoad ) {
object.afterThemeLoad();
}
var children = object.getChildren();
for( var c = 0; c < children.length; c++ ) {
var child = children[c];
if( this.allowParseChild( object, child ) ) {
this.callAfterThemeLoad( child );
}
}
}
extendDefaultObject( object ) {
var defaultObjectInstance = new defaultObject();
defaultObjectInstance.exposeMethodsToObject( object );
}
filterVisibleObjects( objects ) {
var visibleObjectClassNames = new Array();
for ( var i = 0; i < objects.length; i++ ) {
var object = objects[i];
if( tools.isVisible( object.element ) ) {
visibleObjectClassNames.push( object.getClassName() );
}
}
visibleObjectClassNames = tools.uniqueArrayRows( visibleObjectClassNames );
return visibleObjectClassNames;
}
communicateVisibleElements() {
if( document.mode == "development" ) {
var objects = this.objects;
var visibleObjectClassNames = this.filterVisibleObjects( objects );
if( this.visibleObjectClassNames != visibleObjectClassNames.join() ) {
this.visibleObjectClassNames = visibleObjectClassNames.join();
this.socketManager.get( "settings", "setVisibleElements", visibleObjectClassNames.join() );
}
}
}
communicateTheme( os, device, tint ) {
this.themeSwitcher.communicateTheme( os, device, tint );
}
createObjectCustomVariables( object ) {
if( typeof object.sanitize != "boolean" ) {
object.sanitize = true;
}
if( !typeof object.display ) {
object.display = "flex";
}
if( !typeof object.transition ) {
object.transition = "all 0.1s ease-out";
}
if( typeof object.propegateEvent != "boolean" ) {
object.propegateEvent = true;
}
if( object.editable ) {
object.useCustomElement = true;
}
if( typeof object.static != "boolean" ) {
object.static = false;
}
if( typeof object.setZIndex != "boolean" ) {
object.setZIndex = true;
}
if( typeof object.created != "boolean" ) {
object.created = false;
}
if( typeof object.enabled != "boolean" ) {
object.enabled = true;
}
if( typeof object.addToDom != "boolean" ) {
object.addToDom = true;
}
if( typeof object.loadThemes != "boolean" ) {
object.loadThemes = true;
}
if( typeof object.isActive != "boolean" ) {
object.isActive = true;
}
if( typeof object.parse != "boolean" ) {
object.parse = true;
}
if( typeof object.parseChildren != "boolean" ) {
object.parseChildren = true;
}
if( typeof object.parseTable != "boolean" ) {
object.parseTable = true;
}
if( !object.layers ) {
object.layers = 2;
}
if( object.layers > 1 ) {
if( !object.boxGridArea ) {
object.boxGridArea = object.propertyName;
}
} else {
if( !object.gridArea ) {
object.gridArea = object.propertyName;
}
}
if( typeof object.showValue == "undefined" ) {
object.showValue = true;
}
if( !object.display ) {
object.display = "flex";
}
if( !object.user ) {
if( this.user ) {
object.user = this.user;
}
}
if( typeof object.renderToDOM != "boolean" ) {
if( object.datatype ) {
object.renderToDOM = false;
} else {
object.renderToDOM = true;
}
}
object.callbacks = new Array();
}
createCustomMethod( object, nodeMethod ) {
object[ nodeMethod ] = async function( ...nodeArguments ) {
this.nodeMethodArguments = JSON.stringify( nodeArguments );
return this.socketManager.get( "object", "callNodeMethod", this, nodeMethod );
}
}
parseStateMethods( object ) {
var stateMethodString = object.__stateMethods;
if( stateMethodString ) {
var stateMethods = stateMethodString.split(",")
for (var i = 0; i < stateMethods.length; i++) {
const stateMethod = stateMethods[i];
//console.log("found state method", object, stateMethod )
var originalMethod = object[ stateMethod ];
object["__" + stateMethod] = originalMethod;
var stateMachine = this.stateMachine;
object[ stateMethod ] = function( ...args ) {
//console.log( "this function is now also mocked", args, object, stateMethod );
// register in stateMachine
stateMachine.registerEvent( object, stateMethod, args );
object["__" + stateMethod]( args[0], args[1], args[2], args[3], args[4] );
}
}
}
}
createObjectCustomMethods( object ) {
var nodeMethodsString = object.__nodeMethods;
if( nodeMethodsString ) {
var nodeMethods = nodeMethodsString.split(",")
for (var i = 0; i < nodeMethods.length; i++) {
const nodeMethod = nodeMethods[i];
//const index = i;
this.createCustomMethod( object, nodeMethod );
}
}
object.save = async function() {
return this.socketManager.get( "table", "save", this, "save" );
}
object.delete = async function() {
return this.socketManager.get( "table", "delete", this, "delete" );
}
object.process = async function() {
return this.socketManager.get( "table", "process", this, this.lastEvent );
}
}
attachAnimationManager(object) {
//object.animationManager.attach( object );
}
createRenderTimer( object ) {
if(!object.mouse) {
object.mouse = new vector2(0,0);
}
if( !object.createdTimer ) {
object.createdTimer = true;
object.filterStrength = 60;
object.frameTime = 0;
object.lastLoop = performance.timing.navigationStart + performance.now();
object.timeID = 0;
object.thisLoop;
}
}
renderLoop( start, previousProgress ) {
var object = this;
object.requestAnimationFrameID = window.requestAnimationFrame( function( timestamp ) {
var thisFrameTime = ( object.thisLoop = performance.timing.navigationStart + performance.now() ) - object.lastLoop;
object.frameTime += ( thisFrameTime - object.frameTime ) / object.filterStrength;
object.lastLoop = object.thisLoop;
if( object.showFps ) {
object.customElement.innerHTML = ( 1000 / object.frameTime ).toFixed(1) + " fps";
}
if( !start ) {
start = timestamp;
}
var progress = ( timestamp - start ) / 10;
if( !previousProgress ) {
var delta = 0;
} else {
var delta = progress - previousProgress;
}
object.render( delta, progress );
object.renderLoop( start, progress );
});
}
attachRenderLoop( object ) {
this.createRenderTimer( object );
if( object.create && !object.isCreated ) {
object.create();
object.isCreated = true;
}
object.renderLoop = this.renderLoop;
object.renderLoop();
}
createObjects() {
var objects = objectsPendingToCreate
console.log("objectsPendingToCreate", objectsPendingToCreate.length);
//for (var i = 0; i < objects.length; i++) {
while( objects.length > 0 ) {
var object = objects.pop();
if( object.create && object.type != "renderCollection" ) {
//object.create();
}
}
console.log("finished", objectsPendingToCreate);
}
callObjectMethods( object ) {
if( object.sync ) {
//object.sync(); // await
}
if( typeof object.render == "function" && this.initializeRenderLoop ) {
this.attachRenderLoop( object );
var that = this;
object.startRenderLoop = function() {
that.attachRenderLoop( object );
}
}
if( this.loadAnimations ) {
object.animationManager.attach( object );
}
if( object.create && this.executeMethods ) {
if( object.type != "renderCollection" && !object.isCreated ) {
object.create();
object.isCreated = true;
} else {
if( !object.isHidden() ) {
object.create();
}
}
}
if( object.setup ) {
object.setup();
}
if( object.permissions ) {
object.updatePermissions( object.permissions );
}
if( object.windowResize && this.executeMethods ) {
object.windowResize();
}
}
validateUser( object ) {
if( object.permitted ) {
if( object.allowed ) {
object.allowed();
}
} else {
if( object.denied ) {
object.denied();
}
}
}
async prepareObject( object ) {
this.setObjectClasses( object );
this.createObjectCustomVariables( object );
}
parseObject( object ) {
if( object.type != "collection" && object.renderToDOM ) {
this.dom.parseObject( object );
this.css.parseObject( object );
if( this.parseEvents ) {
this.eventManager.parseEvents( object );
}
}
}
parseRows( object ) {
if( object.type == "renderCollection" ) {
var childRows = object.rows;
for( var c = 0; c < childRows.length; c++ ) {
object.addRow( childRows[ c ] );
}
}
}
acceptObject( object ) {
if( object.scope == "private" ) {
return false;
}
if( object.enabled == false ) {
return false;
}
if( object.disable == true ) {
return false;
}
if( object.getClassName() == "signed" ) {
return false;
}
return true;
}
getClassObjects() {
var classObjects = this.classObjects;
var keys = Object.keys( classObjects );
var array = new Array();
for ( let x of keys ) {
var idObjects = classObjects[x];
idObjects = idObjects.filter(function() { return true; });
if( idObjects.length > 0 ) {
array.push( idObjects );
}
}
return array;
}
getClassObjectsOrdered() {
if(!this.classObjectsOrdered) {
this.classObjectsOrdered = this.getClassObjects();
}
return this.classObjectsOrdered;
}
addObjects( object, addToObjects ) {
if( addToObjects ) {
this.objects.push( object );
this.pathObjects[object.getApplicationPathString()] = object;
}
if( !this.getObjectByClassName( object.getClassName(), object.id ) ) {
if( !this.classObjects[ object.getClassName() ] ) {
this.classObjects[ object.getClassName() ] = new Array();
}
if( object.id ) {
this.classObjects[ object.getClassName() ][ object.id ] = object;
} else {
this.classObjects[ object.getClassName() ].push( object );
}
}
this.classObjects[ object.getClassName() ].push( object );
if( !this.sourcePathObjects[ object["__sourcePath"] ] ) {
this.sourcePathObjects[ object["__sourcePath"] ] = new Array();
}
this.sourcePathObjects[ object["__sourcePath"] ].push( object );
}
updateCore( object, addToObjects ) {
if( !this.root ) {
this.root = object;
}
this.extendDefaultObject( object );
if( object.getClassName ) {
this.addObjects( object, addToObjects );
}
}
async prepareObjects( object ) {
this.setObjectClasses( object );
var children = object.getChildren();
for( var c = 0; c < children.length; c++ ) {
var child = children[c];
await this.prepareObjects( child );
}
}
runDefault( object ) {
if( object.default ) {
//object.showParents();
object.default();
}
if( object.getChildren ) {
var children = object.getChildren();
for( var c = 0; c < children.length; c++ ) {
var child = children[c];
this.runDefault( child );
}
}
}
appendAllToSelector( object = this.root ) {
/*
if( object.appendToSelector ) {
object.appendToSelector();
}
if( object.getChildren ) {
var children = object.getChildren();
for( var c = 0; c < children.length; c++ ) {
var child = children[c];
this.appendAllToSelector( child );
}
}
*/
}
fixCustomElementInheritence( object ) {
// multi extend fixed this
}
removeUnifyElement( object, depth = 0 ) {
var extended = tools.getExtendedClass( object );
this.extendDefaultObject( object );
if( extended ) {
var extendConstructor = extended.constructor;
var instance = new extendConstructor();
depth++;
if( instance.customElement && instance.getClassName ) {
return this.removeUnifyElement( extended, depth )
} else {
return depth;
}
} else {
return depth;
}
}
setTheme() {
this.dom.tint = this.tint;
this.dom.device = this.device;
this.dom.os = this.os;
this.dom.addDOMToSelector = this.addDOMToSelector;
this.css.tint = this.tint;
this.css.device = this.device;
this.css.os = this.os;
}
createDefaultStyleProperties( object ) {
// if developer mode
const array = Object.entries( definitions.css );
for (var i = 0; i < array.length; i++) {
var propertyName = array[i][0];
if( !object[propertyName] && propertyName != "gridArea" ) {
object.__proto__[propertyName] = "";
}
}
console.log( object );
// end if developer mode
}
createCustomEvents( object ) {
object.submit = function( event ) {
event.preventDefault();
}
document.body.addEventListener('mousemove', function( event ) {
object.mouse = document.mouse;
object.mouseVelocity = document.mouseVelocity;
});
}
async parse( object, addToObjects = true ) {
if( !this.acceptObject( object ) ) {
return false;
}
this.createCustomEvents( object );
this.fixCustomElementInheritence( object );
//this.createDefaultStyleProperties( object );
this.createObjectCustomMethods( object );
this.parseStateMethods( object );
this.updateCore( object, addToObjects );
this.prepareObject( object );
this.parseObject( object );
this.attachAnimationManager( object );
if( object.parseChildren ) {
this.parseChildren( object );
}
this.callObjectMethods( object );
if( object.type != "collection" ) {
this.createHTML( object );
}
this.themeSwitcher.parseObject( object );
if( object.prepare ) {
object.prepare( this.socketManager );
}
}
parseCSSProperties( object ) {
var properties = object.getProperties();
for( var c = 0; c < properties.length; c++ ) {
var property = properties[c];
this.css.parseProperty( property, object );
}
}
addObjectToDOM( object ) {
this.dom.parseInnerElements( object );
if( this.createStyleSheet ) {
this.css.createStyleSheet( object );
}
}
createHTML( object ) {
if( object.renderToDOM ) {
this.parseCSSProperties( object );
if( ( object.isActive || object.type != "collection" ) || object.enabled ) {
this.addObjectToDOM( object );
}
}
}
parseRenderCollectionObject( child, index ) {
if( child.getCollection ) {
if( child.object ) {
var newObject = new child.object();
this.extendDefaultObject( newObject );
newObject.addToDom = false;
newObject.parent = child;
newObject.order = index;
newObject.propertyName = newObject.getClassName();
//this.parse( newObject );
}
}
}
async parseChildren( object ) {
var children = object.getChildren();
for( var c = 0; c < children.length; c++ ) {
var child = children[c];
child.order = c;
child.parent = object;
//this.extendDefaultObject( child );
if( this.allowParseChild( object, child ) ) {
this.parse( child );
}
if( child.type == "renderCollection" ) {
this.parseRenderCollectionObject( child, c );
}
}
}
allowParseChild( parent, child ) {
if( !child.extendsClass ) {
this.extendDefaultObject( child );
}
// If class doesnt extend table and thus is not a tableObject.
if( child.constructor.name != "Object" && !child.extendsClass( "table" ) ) {
// If the object is table and child a collumn but the table's parsetable property is not true
// allow parsing, If parsetable is not set, it is automatically set to true. so then
// allow parsing.
if( !( !parent.parseTable && child.datatype ) ) {
return true;
}
}
return false;
}
updatePermissions( objectPermissions ) {
//console.log("objectPermissions", objectPermissions);
for(var c = 0; c<objectPermissions.length;c++) {
var objectPermission = objectPermissions[c];
var path = objectPermission.path.split("/");
var object = this.getObjectByPath( path );
var permissions = objectPermission.permission;
if( object ) {
//console.log( "updated permission", objectPermission.path, permissions, object.getClassName() );
object.updatePermissions( permissions );
} else {
//console.log("path not found", path, this.objects);
}
}
}
}