class R3 { constructor(options) { if (typeof options === 'undefined') { options = {}; } Object.assign(this, options); } } /** * static Version - Current R3 version */ R3.Version = '3.0.338'; /** * static CompileDate - Current compile date of R3 */ R3.CompileDate = '2021 Oct 10 - 15:24:05 pm'; class System { constructor(options) { if (typeof options === 'undefined') { options = {}; } Object.assign(this, options); } } System.DOM = 0x0; System.INPUT = 0x1; System.LINKING = 0x2; System.RENDER = 0x3; System.RUNTIME = 0x4; System.SOCKET = 0x5; System.STORAGE = 0x6; System.MAX_SYSTEM = 0x7; class SystemDOM extends System { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); Object.assign(this, options); } /** * Start() * - Starts the system by registering subscriptions to events * @param options */ static Start(options) { SystemDOM.Subscriptions['DOM_COMPONENT_INITIALIZED'] = Event.Subscribe( Event.DOM_COMPONENT_INITIALIZED, SystemDOM.OnDomComponentInitialized ); SystemDOM.Started = true; console.log('Started system: SystemDOM'); } /** * Stop() * - Stops the system by removing these subscriptions to events * @param options */ static Stop(options) { SystemDOM.Subscriptions['DOM_COMPONENT_INITIALIZED'].remove(); delete SystemDOM.Subscriptions['DOM_COMPONENT_INITIALIZED']; SystemDOM.Started = false; console.log('Stopped system: SystemDOM'); } /** * OnDomComponentInitialized() * - Listens to events of type Event.DOM_COMPONENT_INITIALIZED and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnDomComponentInitialized(object) { } } /** * static Started - Indicates whether or not this system is running or not */ SystemDOM.Started = false; /** * static Subscriptions - An association object which hold the subscription handles for Events this system is * listening to. The system can stop receiving events by calling remove() on a handle. */ SystemDOM.Subscriptions = {}; class SystemInput extends System { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); Object.assign(this, options); } /** * Start() * - Starts the system by registering subscriptions to events * @param options */ static Start(options) { SystemInput.Subscriptions['SLIDER_ENTITY_INITIALIZED'] = Event.Subscribe( Event.SLIDER_ENTITY_INITIALIZED, SystemInput.OnSliderEntityInitialized ); SystemInput.Subscriptions['TOUCH_START'] = Event.Subscribe( Event.TOUCH_START, SystemInput.OnTouchStart ); SystemInput.Subscriptions['TOUCH_END'] = Event.Subscribe( Event.TOUCH_END, SystemInput.OnTouchEnd ); SystemInput.Subscriptions['TOUCH_MOVE'] = Event.Subscribe( Event.TOUCH_MOVE, SystemInput.OnTouchMove ); SystemInput.Subscriptions['TOUCH_CANCEL'] = Event.Subscribe( Event.TOUCH_CANCEL, SystemInput.OnTouchCancel ); SystemInput.Subscriptions['KEYBOARD_DOWN'] = Event.Subscribe( Event.KEYBOARD_DOWN, SystemInput.OnKeyboardDown ); SystemInput.Subscriptions['KEYBOARD_UP'] = Event.Subscribe( Event.KEYBOARD_UP, SystemInput.OnKeyboardUp ); SystemInput.Subscriptions['MOUSE_DOWN'] = Event.Subscribe( Event.MOUSE_DOWN, SystemInput.OnMouseDown ); SystemInput.Subscriptions['MOUSE_UP'] = Event.Subscribe( Event.MOUSE_UP, SystemInput.OnMouseUp ); SystemInput.Subscriptions['MOUSE_MOVE'] = Event.Subscribe( Event.MOUSE_MOVE, SystemInput.OnMouseMove ); SystemInput.Subscriptions['MOUSE_WHEEL'] = Event.Subscribe( Event.MOUSE_WHEEL, SystemInput.OnMouseWheel ); SystemInput.Started = true; console.log('Started system: SystemInput'); } /** * Stop() * - Stops the system by removing these subscriptions to events * @param options */ static Stop(options) { SystemInput.Subscriptions['SLIDER_ENTITY_INITIALIZED'].remove(); delete SystemInput.Subscriptions['SLIDER_ENTITY_INITIALIZED']; SystemInput.Subscriptions['TOUCH_START'].remove(); delete SystemInput.Subscriptions['TOUCH_START']; SystemInput.Subscriptions['TOUCH_END'].remove(); delete SystemInput.Subscriptions['TOUCH_END']; SystemInput.Subscriptions['TOUCH_MOVE'].remove(); delete SystemInput.Subscriptions['TOUCH_MOVE']; SystemInput.Subscriptions['TOUCH_CANCEL'].remove(); delete SystemInput.Subscriptions['TOUCH_CANCEL']; SystemInput.Subscriptions['KEYBOARD_DOWN'].remove(); delete SystemInput.Subscriptions['KEYBOARD_DOWN']; SystemInput.Subscriptions['KEYBOARD_UP'].remove(); delete SystemInput.Subscriptions['KEYBOARD_UP']; SystemInput.Subscriptions['MOUSE_DOWN'].remove(); delete SystemInput.Subscriptions['MOUSE_DOWN']; SystemInput.Subscriptions['MOUSE_UP'].remove(); delete SystemInput.Subscriptions['MOUSE_UP']; SystemInput.Subscriptions['MOUSE_MOVE'].remove(); delete SystemInput.Subscriptions['MOUSE_MOVE']; SystemInput.Subscriptions['MOUSE_WHEEL'].remove(); delete SystemInput.Subscriptions['MOUSE_WHEEL']; SystemInput.Started = false; console.log('Stopped system: SystemInput'); } /** * OnSliderEntityInitialized() * - Listens to events of type Event.SLIDER_ENTITY_INITIALIZED and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnSliderEntityInitialized(object) { console.log('Slider Entity Initialized'); } /** * OnTouchStart() * - Listens to events of type Event.TOUCH_START and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnTouchStart(object) { } /** * OnTouchEnd() * - Listens to events of type Event.TOUCH_END and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnTouchEnd(object) { } /** * OnTouchMove() * - Listens to events of type Event.TOUCH_MOVE and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnTouchMove(object) { } /** * OnTouchCancel() * - Listens to events of type Event.TOUCH_CANCEL and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnTouchCancel(object) { } /** * OnKeyboardDown() * - Listens to events of type Event.KEYBOARD_DOWN and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnKeyboardDown(object) { } /** * OnKeyboardUp() * - Listens to events of type Event.KEYBOARD_UP and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnKeyboardUp(object) { } /** * OnMouseDown() * - Listens to events of type Event.MOUSE_DOWN and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnMouseDown(object) { } /** * OnMouseUp() * - Listens to events of type Event.MOUSE_UP and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnMouseUp(object) { } /** * OnMouseMove() * - Listens to events of type Event.MOUSE_MOVE and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnMouseMove(object) { } /** * OnMouseWheel() * - Listens to events of type Event.MOUSE_WHEEL and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnMouseWheel(object) { } } /** * static Started - Indicates whether or not this system is running or not */ SystemInput.Started = false; /** * static Subscriptions - An association object which hold the subscription handles for Events this system is * listening to. The system can stop receiving events by calling remove() on a handle. */ SystemInput.Subscriptions = {}; class SystemLinking extends System { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); Object.assign(this, options); } /** * Start() * - Starts the system by registering subscriptions to events * @param options */ static Start(options) { SystemLinking.Subscriptions['OBJECT_CREATED'] = Event.Subscribe( Event.OBJECT_CREATED, SystemLinking.OnObjectCreated ); SystemLinking.Subscriptions['COMPONENT_INITIALIZED'] = Event.Subscribe( Event.COMPONENT_INITIALIZED, SystemLinking.OnComponentInitialized ); SystemLinking.Subscriptions['ENTITY_INITIALIZED'] = Event.Subscribe( Event.ENTITY_INITIALIZED, SystemLinking.OnEntityInitialized ); SystemLinking.Subscriptions['INSTANCE_CREATED'] = Event.Subscribe( Event.INSTANCE_CREATED, SystemLinking.OnInstanceCreated ); SystemLinking.Subscriptions['CREATE_INSTANCE_BEFORE'] = Event.Subscribe( Event.CREATE_INSTANCE_BEFORE, SystemLinking.OnCreateInstanceBefore ); SystemLinking.Subscriptions['OBJECT_PROPERTY_UPDATE'] = Event.Subscribe( Event.OBJECT_PROPERTY_UPDATE, SystemLinking.OnObjectPropertyUpdate ); SystemLinking.Subscriptions['OBJECT_PROPERTY_UPDATED'] = Event.Subscribe( Event.OBJECT_PROPERTY_UPDATED, SystemLinking.OnObjectPropertyUpdated ); SystemLinking.Subscriptions['ON_READY_STATE_CHANGE'] = Event.Subscribe( Event.ON_READY_STATE_CHANGE, SystemLinking.OnOnReadyStateChange ); SystemLinking.Subscriptions['FINAL_INITIALIZE'] = Event.Subscribe( Event.FINAL_INITIALIZE, SystemLinking.OnFinalInitialize ); SystemLinking.Started = true; console.log('Started system: SystemLinking'); } /** * Stop() * - Stops the system by removing these subscriptions to events * @param options */ static Stop(options) { SystemLinking.Subscriptions['OBJECT_CREATED'].remove(); delete SystemLinking.Subscriptions['OBJECT_CREATED']; SystemLinking.Subscriptions['COMPONENT_INITIALIZED'].remove(); delete SystemLinking.Subscriptions['COMPONENT_INITIALIZED']; SystemLinking.Subscriptions['ENTITY_INITIALIZED'].remove(); delete SystemLinking.Subscriptions['ENTITY_INITIALIZED']; SystemLinking.Subscriptions['INSTANCE_CREATED'].remove(); delete SystemLinking.Subscriptions['INSTANCE_CREATED']; SystemLinking.Subscriptions['CREATE_INSTANCE_BEFORE'].remove(); delete SystemLinking.Subscriptions['CREATE_INSTANCE_BEFORE']; SystemLinking.Subscriptions['OBJECT_PROPERTY_UPDATE'].remove(); delete SystemLinking.Subscriptions['OBJECT_PROPERTY_UPDATE']; SystemLinking.Subscriptions['OBJECT_PROPERTY_UPDATED'].remove(); delete SystemLinking.Subscriptions['OBJECT_PROPERTY_UPDATED']; SystemLinking.Subscriptions['ON_READY_STATE_CHANGE'].remove(); delete SystemLinking.Subscriptions['ON_READY_STATE_CHANGE']; SystemLinking.Subscriptions['FINAL_INITIALIZE'].remove(); delete SystemLinking.Subscriptions['FINAL_INITIALIZE']; SystemLinking.Started = false; console.log('Stopped system: SystemLinking'); } /** * SanityChecks() * - When an object gets some value assigned to a property, we need to check if this property is a required * property, and if so, the value assigned to this property needs to conform to the type restrictions enforced by * the requirements objects. * @param r3Object * @param property * @param value */ static SanityChecks( r3Object, property, value ) { for (let r = 0; r < r3Object.required.length; r++) { /** * First we check if this is a required property */ if (!r3Object.required[r].hasOwnProperty(property)) { return; } /** * We know this property is required - so continue.. */ if (r3Object.required[r][property] instanceof Array) { /** * First we need to check that this value conforms to the requirement of being an Array */ if (!(value instanceof Array)) { if (r3Object.underConstruction) { /** * We are about to throw during a construction process. This means this r3Object will not * be created - and so we need tell all of its children that this object is no longer their * parent so that it can be garbage collected */ r3Object.children.map( function(child) { Utils.RemoveFromArray(child.parents, r3Object); } ); } throw new TypeError('The property ' + property + ' of ' + r3Object.name + ' was not properly defined - it should be an array'); } if (value.length === 0) { return; } /** * Check all items in the array are valid objects of type r3Object.required[property] */ for (let i = 0; i < value.length; i++) { r3Object.required[r][property].map( function (constructor) { if (!(value[i] instanceof constructor)) { if (r3Object.underConstruction) { /** * We are about to throw during a construction process. This means this r3Object will not * be created - and so we need tell all of its children that this object is no longer their * parent so that it can be garbage collected */ r3Object.children.map( function(child) { Utils.RemoveFromArray(child.parents, r3Object); } ); } throw new TypeError('The child item inside ' + property + '[] of this object is not of the correct type'); } } ); } } else { if (value === null) { return; } if (typeof value === 'undefined') { return; } if (!(value instanceof r3Object.required[r][property])) { if (r3Object.underConstruction) { /** * We are about to throw during a construction process. This means this r3Object will not * be created - and so we need tell all of its children that this object is no longer their * parent so that it can be garbage collected */ r3Object.children.map( function(child) { Utils.RemoveFromArray(child.parents, r3Object); } ); } throw new TypeError('The property ' + property + ' of ' + r3Object.name + ' is not of the correct type') } } } } /** * SetParentChildRelationships() * - When an object gets some value assigned to a property, and it is a required property, we need to ensure that * the proper parent and child relationships are maintained * @param r3Object * @param property * @param value */ static SetParentChildRelationships( r3Object, property, value ) { if (value instanceof Array) { /** * We need to check if we removed an item from the existing array */ if (r3Object[property].length > 0) { /** * Find the missing value (if any) from the old array */ let unlinked = r3Object[property].reduce( function(result, object) { if (value.indexOf(object) === -1) { result.push(object); } return result; }, [] ); unlinked.map( function(unlinkObject) { Utils.RemoveFromArray(unlinkObject.parents, r3Object); Utils.RemoveFromArray(r3Object.children, unlinkObject); if (r3Object instanceof Entity) { Utils.RemoveFromArray(r3Object.components, unlinkObject); } } ); } for (let i = 0; i < value.length; i++) { if (value[i] instanceof R3Object) { Utils.PushUnique(value[i].parents, r3Object); Utils.PushUnique(r3Object.children, value[i]); if (r3Object instanceof Entity) { Utils.PushUnique(r3Object.components, value[i]); } } } } if (value instanceof R3Object) { Utils.PushUnique(value.parents, r3Object); Utils.PushUnique(r3Object.children, value); if (r3Object instanceof Entity) { Utils.PushUnique(r3Object.components, value); } } if (value === null || typeof value === 'undefined') { if (r3Object[property] instanceof R3Object) { Utils.RemoveFromArray(r3Object[property].parents, r3Object); Utils.RemoveFromArray(r3Object.children, r3Object[property]); if (r3Object instanceof Entity) { Utils.RemoveFromArray(r3Object.components, r3Object[property]); } } } } /** * DetermineFutureReadyState() * - When an object is assigned some value to one of its required properties, we should determine the future * 'readiness' state of this R3 Object. Only if its ready state becomes false, we need to cascade this * information to all parents forcing them to emit a Only if its ready state becomes false, we force all parents * to emit a READY_STATE_PENDING_FALSE event. * @param r3Object * @param property * @param value */ static DetermineFutureReadyState( r3Object, property, value ) { console.log('Determining future ready state of ' + r3Object.name + ' because of property change : ' + property); for (let r = 0; r < r3Object.required.length; r++) { if (r3Object.ready[r]) { /** * This object is currently ready - we need to check if this object will fall out of a ready state. * We only need to compare the current property. */ let ready = true; let requirements = r3Object.required[r]; if (requirements.hasOwnProperty(property)) { ready = r3Object.requirementValueCheck(requirements[property], value); } if (!ready) { /** * This object will fall out of readiness state - notify all systems */ console.log('Future ready state will transition from true to false'); SystemLinking.CascadeFutureReadyState(r3Object, R3Object.READY_STATE_PENDING_FALSE); } } } } /** * CascadeFutureReadyState() * - We start at the current node - emit READY_STATE_PENDING_FALSE event, afterwards calling asking all parents to * emit a READY_STATE_PENDING_FALSE event. This allows the SystemLinking to stop Entities before the cleanup * occurs. * @param r3Object * @param readyState */ static CascadeFutureReadyState( r3Object, readyState ) { let node = new Node( { object : r3Object } ); let graph = new Graph( { startNode : node, relevant : ['parents'] } ) let nodes = graph.closed; /** * nodes contains a list of current node and parent Objects which are affected by the ready state of this object. */ for (let n = 0; n < nodes.length; n++) { let node = nodes[n]; /** * The parent does not know directly why it falls out of ready state necessarily - because from its perspective * it may have all its children requirements met - however a child of one of its children is going out of ready * state. So - without adding too much complexity here, we just notify all parents (including the current node) * that it's impending ready state will be false. We don't care about the readyStateIndex because of this. (for * the sake of complexity) */ Event.Emit( Event.ON_READY_STATE_CHANGE, { r3Object: node.object, readyState: readyState } ); } } /** * CascadeActualReadyState() * - We start at the current node, determine its current ready state (it triggers the appropriate events), * afterwards we work from the first parent upwards, determining it's ready state, taking appropriate action and * cascading upwards. * @param r3Object */ static CascadeActualReadyState(r3Object) { console.log('Cascading actual ready state of ' + r3Object.name); let node = new Node( { object : r3Object } ); let graph = new Graph( { startNode : node, relevant : ['parents'] } ) let nodes = graph.closed; /** * nodes contains the current node as it's first object, followed by its parents */ for (let n = 0; n < nodes.length; n++) { let node = nodes[n]; /** * A simple node.object.isReady() check will trigger the required READY_STATE_CHANGE events */ node.object.isReady(); } } /** * OnObjectCreated() * - Listens to events of type Event.OBJECT_CREATED and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnObjectCreated(object) { console.log('Object created : ' + object.name); } /** * OnComponentInitialized() * - Listens to events of type Event.COMPONENT_INITIALIZED and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnComponentInitialized(object) { console.log('component initialized'); } /** * OnEntityInitialized() * - Listens to events of type Event.ENTITY_INITIALIZED and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnEntityInitialized(object) { console.log('entity initialized'); } /** * OnInstanceCreated() * - Listens to events of type Event.INSTANCE_CREATED and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnInstanceCreated(object) { console.log('instance created'); } /** * OnCreateInstanceBefore() * - Listens to events of type Event.CREATE_INSTANCE_BEFORE and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return boolean delayInstance which indicates whether or not instance creation is delayed (handled) by * another system. (i.e. where instance creation order is of importance) */ static OnCreateInstanceBefore(object) { for (let i = 0; i < SystemLinking.BlacklistedComponents.length; i++) { if (object instanceof SystemLinking.BlacklistedComponents) { /** * If an object is 'Blacklisted' - we need to fire an event * to notify other systems that this component requests * to have its instance created. * * Should no system respond to this event we should continue * to create the instance */ let delayInstance = false; Event.Emit( Event.BLACKLISTED_COMPONENT_INSTANCE_REQUEST, object, function(response) { delayInstance = response; } ); return delayInstance; } } return false; } /** * OnObjectPropertyUpdate() * - Listens to events of type Event.OBJECT_PROPERTY_UPDATE and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnObjectPropertyUpdate(object) { let {value, property, r3Object} = object; // object = object.object; console.log('Object property update: ' + r3Object.constructor.name + '.' + property); /** * First we need to perform some sanity checks on the property */ SystemLinking.SanityChecks(r3Object, property, value); /** * Set the parent relationships */ SystemLinking.SetParentChildRelationships(r3Object, property, value); /** * Determine the future ready state of the r3Object */ SystemLinking.DetermineFutureReadyState(r3Object, property, value); /** * The value was unassigned - remove the parent relationships of the existing * components (if available) */ // if (value === null || typeof value === 'undefined') { // if (r3Object[property] instanceof R3Object) { // r3Object.dirty = true; // Utils.RemoveFromArray(r3Object[property].parents, r3Object); // } // } // // if (!r3Object.underConstruction) { // // if (r3Object.initialized) { // /** // * Check if this object will still be initialized after this assignment completes // */ // r3Object.setInitialized(property, value); // if (!r3Object.initialized) { // //We set this object back to initialized because it is still initialized - it WILL be not initialized in the future // r3Object.initialized = true; // r3Object.stop(); // } // } // } } /** * OnObjectPropertyUpdated() * - Listens to events of type Event.OBJECT_PROPERTY_UPDATED and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnObjectPropertyUpdated(object) { let {r3Object} = object; /** * Determine the actual ready state of the r3Object */ SystemLinking.CascadeActualReadyState(r3Object); } /** * OnOnReadyStateChange() * - Listens to events of type Event.ON_READY_STATE_CHANGE and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnOnReadyStateChange(object) { let {r3Object, readyState, readyStateIndex} = object; if (readyState === R3Object.READY_STATE_TRUE) { console.log('Ready state changed to true for ' + r3Object.name + ' by passing requirements set ' + readyStateIndex); /** * If it is a Component and it has no instance, call its createInstance(). */ if (r3Object instanceof Component && !r3Object.instance) { r3Object.createInstance(); } /** * If it is an Entity and it is stopped, call the start() method. */ if (r3Object instanceof Entity && r3Object.started === false) { r3Object.start(); } } if (readyState === R3Object.READY_STATE_FALSE) { console.log('Ready state false for : ' + r3Object.name); /** * If it is a Component and it has an instance, dispose of it */ if (r3Object instanceof Component && r3Object.instance) { r3Object.disposeInstance(); } } if (readyState === R3Object.READY_STATE_PENDING_FALSE) { console.log('Ready state pending false for : ' + r3Object.name); /** * If it is an Entity and is started, stop it. */ if (r3Object instanceof Entity && r3Object.started === true) { r3Object.stop(); } } if (readyState === R3Object.READY_STATE_PENDING_TRUE) { /** * We don't do anything at the moment - we just log this information. */ console.log('Ready state pending true for ' + r3Object.name + ' by passing requirements set ' + readyStateIndex); } } /** * OnFinalInitialize() * - Listens to events of type Event.FINAL_INITIALIZE and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnFinalInitialize(object) { if (object instanceof Component) { if (object.required.length === 0) { object.createInstance(); } } if (object instanceof Entity) { if (object.required.length === 0) { object.start(); } } } } /** * static Started - Indicates whether or not this system is running or not */ SystemLinking.Started = false; /** * static Subscriptions - An association object which hold the subscription handles for Events this system is * listening to. The system can stop receiving events by calling remove() on a handle. */ SystemLinking.Subscriptions = {}; /** * static BlacklistedComponents - A list of component constructors which should not be permitted to create instances * immediately */ SystemLinking.BlacklistedComponents = []; class SystemRender extends System { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); Object.assign(this, options); } /** * Start() * - Starts the system by registering subscriptions to events * @param options */ static Start(options) { SystemRender.Subscriptions['INSTANCE_CREATED'] = Event.Subscribe( Event.INSTANCE_CREATED, SystemRender.OnInstanceCreated ); SystemRender.Started = true; console.log('Started system: SystemRender'); } /** * Stop() * - Stops the system by removing these subscriptions to events * @param options */ static Stop(options) { SystemRender.Subscriptions['INSTANCE_CREATED'].remove(); delete SystemRender.Subscriptions['INSTANCE_CREATED']; SystemRender.Started = false; console.log('Stopped system: SystemRender'); } /** * OnInstanceCreated() * - Listens to events of type Event.INSTANCE_CREATED and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnInstanceCreated(object) { if (object instanceof ComponentDOM) { if (object.runtime instanceof RuntimeDocument) { document.body.appendChild(object.instance); } } if (object instanceof ComponentImage) { if (object.runtime instanceof RuntimeWebImage) { document.body.appendChild(object.instance); } } } } /** * static Started - Indicates whether or not this system is running or not */ SystemRender.Started = false; /** * static Subscriptions - An association object which hold the subscription handles for Events this system is * listening to. The system can stop receiving events by calling remove() on a handle. */ SystemRender.Subscriptions = {}; class SystemRuntime extends System { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); Object.assign(this, options); } /** * Start() * - Starts the system by registering subscriptions to events * @param options */ static Start(options) { SystemRuntime.Subscriptions['RUNTIME_CREATED'] = Event.Subscribe( Event.RUNTIME_CREATED, SystemRuntime.OnRuntimeCreated ); SystemRuntime.Subscriptions['GET_RUNTIME'] = Event.Subscribe( Event.GET_RUNTIME, SystemRuntime.OnGetRuntime ); SystemRuntime.Subscriptions['PROJECT_INITIALIZED'] = Event.Subscribe( Event.PROJECT_INITIALIZED, SystemRuntime.OnProjectInitialized ); SystemRuntime.Subscriptions['GET_API_URL'] = Event.Subscribe( Event.GET_API_URL, SystemRuntime.OnGetApiUrl ); SystemRuntime.Started = true; console.log('Started system: SystemRuntime'); } /** * Stop() * - Stops the system by removing these subscriptions to events * @param options */ static Stop(options) { SystemRuntime.Subscriptions['RUNTIME_CREATED'].remove(); delete SystemRuntime.Subscriptions['RUNTIME_CREATED']; SystemRuntime.Subscriptions['GET_RUNTIME'].remove(); delete SystemRuntime.Subscriptions['GET_RUNTIME']; SystemRuntime.Subscriptions['PROJECT_INITIALIZED'].remove(); delete SystemRuntime.Subscriptions['PROJECT_INITIALIZED']; SystemRuntime.Subscriptions['GET_API_URL'].remove(); delete SystemRuntime.Subscriptions['GET_API_URL']; SystemRuntime.Started = false; console.log('Stopped system: SystemRuntime'); } /** * OnRuntimeCreated() * - Listens to events of type Event.RUNTIME_CREATED and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnRuntimeCreated(object) { console.log('Runtime created : ' + object.name); } /** * OnGetRuntime() * - Listens to events of type Event.GET_RUNTIME and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnGetRuntime(object) { /** * DOM and Input Components are typically managed through * the DOM. */ if ( object instanceof ComponentDOM || object instanceof ComponentImage || object instanceof ComponentInput ) { if (SystemRuntime.CurrentProject === null) { if (typeof process === 'undefined') { if (!SystemRuntime.RuntimeDocument) { SystemRuntime.RuntimeDocument = new RuntimeDocument(); } return SystemRuntime.RuntimeDocument; } else { if (!SystemRuntime.RuntimeJsDOM) { SystemRuntime.RuntimeJsDOM = new RuntimeJsDOM(); } return SystemRuntime.RuntimeJsDOM; } } else { console.log('TODO: implement a project based DOM runtime'); } } // if (object instanceof ComponentImage) { // if (SystemRuntime.CurrentProject === null) { // return new RuntimeWebImage(); // } else { // console.log('TODO: implement a project based Image runtime'); // } // } } /** * OnProjectInitialized() * - Listens to events of type Event.PROJECT_INITIALIZED and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnProjectInitialized(object) { Utils.PushUnique(SystemRuntime.Projects, object); SystemRuntime.CurrentProject = object; } /** * OnGetApiUrl() * - Listens to events of type Event.GET_API_URL and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnGetApiUrl(object) { if (SystemRuntime.CurrentProject === null) { /** * Check if we are running server side or client side */ if (process && process.env) { const {API_URL} = process.env; if (typeof API_URL === 'undefined') { throw new Error('No API_URL defined for this process'); } return API_URL; } else { return 'http://api.r3js.local'; } } } } /** * static Started - Indicates whether or not this system is running or not */ SystemRuntime.Started = false; /** * static Subscriptions - An association object which hold the subscription handles for Events this system is * listening to. The system can stop receiving events by calling remove() on a handle. */ SystemRuntime.Subscriptions = {}; /** * static Projects - No comment */ SystemRuntime.Projects = []; /** * static CurrentProject - No comment */ SystemRuntime.CurrentProject = null; /** * static ServerSide - No comment */ SystemRuntime.ServerSide = (typeof process !== 'undefined'); /** * static RuntimeCoder - No comment */ SystemRuntime.RuntimeCoder = {}; /** * static RuntimeDOM - No comment */ SystemRuntime.RuntimeDOM = {}; /** * static RuntimeGUI - No comment */ SystemRuntime.RuntimeGUI = {}; /** * static RuntimeGraphics - No comment */ SystemRuntime.RuntimeGraphics = {}; /** * static RuntimePhysics - No comment */ SystemRuntime.RuntimePhysics = {}; /** * static RuntimeStatistics - No comment */ SystemRuntime.RuntimeStatistics = {}; /** * static RuntimeJsDOM - No comment */ SystemRuntime.RuntimeJsDOM = null; /** * static RuntimeDocument - No comment */ SystemRuntime.RuntimeDocument = null; class SystemSocket extends System { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); Object.assign(this, options); } /** * Start() * - Starts the system by registering subscriptions to events * @param options */ static Start(options) { SystemSocket.Started = true; console.log('Started system: SystemSocket'); } /** * Stop() * - Stops the system by removing these subscriptions to events * @param options */ static Stop(options) { SystemSocket.Started = false; console.log('Stopped system: SystemSocket'); } } /** * static Started - Indicates whether or not this system is running or not */ SystemSocket.Started = false; /** * static Subscriptions - An association object which hold the subscription handles for Events this system is * listening to. The system can stop receiving events by calling remove() on a handle. */ SystemSocket.Subscriptions = {}; class SystemStorage extends System { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); Object.assign(this, options); } /** * Start() * - Starts the system by registering subscriptions to events * @param options */ static Start(options) { SystemStorage.Subscriptions['IMAGE_INSTANCE_CREATED'] = Event.Subscribe( Event.IMAGE_INSTANCE_CREATED, SystemStorage.OnImageInstanceCreated ); SystemStorage.Started = true; console.log('Started system: SystemStorage'); } /** * Stop() * - Stops the system by removing these subscriptions to events * @param options */ static Stop(options) { SystemStorage.Subscriptions['IMAGE_INSTANCE_CREATED'].remove(); delete SystemStorage.Subscriptions['IMAGE_INSTANCE_CREATED']; SystemStorage.Started = false; console.log('Stopped system: SystemStorage'); } /** * OnImageInstanceCreated() * - Listens to events of type Event.IMAGE_INSTANCE_CREATED and executes this function. * @param object (The event data passed as argument - typically an R3Object) * @return */ static OnImageInstanceCreated(object) { Event.Emit( Event.GET_API_URL, null, function(apiUrl) { console.log('about to download image at: ' + apiUrl + object.external_path); } ); } } /** * static Started - Indicates whether or not this system is running or not */ SystemStorage.Started = false; /** * static Subscriptions - An association object which hold the subscription handles for Events this system is * listening to. The system can stop receiving events by calling remove() on a handle. */ SystemStorage.Subscriptions = {}; class Runtime { constructor(options) { if (typeof options === 'undefined') { options = {}; } Object.assign(this, options); } } Runtime.BASE_CODER = 0x0; Runtime.BASE_DOM = 0x1; Runtime.BASE_GUI = 0x2; Runtime.BASE_GRAPHICS = 0x3; Runtime.BASE_IMAGE = 0x4; Runtime.BASE_PHYSICS = 0x5; Runtime.BASE_SOCKET = 0x6; Runtime.BASE_STATISTICS = 0x7; Runtime.CODE_MIRROR = 0x8; Runtime.DOCUMENT = 0x9; Runtime.JS_DOM = 0xa; Runtime.CONTROL_KIT = 0xb; Runtime.THREE = 0xc; Runtime.NODE_JS_IMAGE = 0xd; Runtime.WEB_IMAGE = 0xe; Runtime.BULLET = 0xf; Runtime.STATS = 0x10; class RuntimeCoder extends Runtime { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); /** * name - Name of the runtime */ if (typeof options.name === 'undefined') { options.name = this.constructor.name; } Object.assign(this, options); Event.Emit(Event.RUNTIME_CREATED, this); } } class RuntimeCodeMirror extends RuntimeCoder { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); Object.assign(this, options); } /** * buildInstance() * - Creates a runtime instance object based on the R3.Component representing it. * @param component */ buildInstance(component) { } } class RuntimeDOM extends Runtime { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); /** * name - Name of the runtime */ if (typeof options.name === 'undefined') { options.name = this.constructor.name; } Object.assign(this, options); Event.Emit(Event.RUNTIME_CREATED, this); } } class RuntimeDocument extends RuntimeDOM { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); Object.assign(this, options); } /** * buildInstance() * - Creates a runtime instance object based on the R3.Component representing it. * @param component */ buildInstance(component) { if (component instanceof ComponentCanvas) { let canvas = document.createElement('canvas'); canvas.setAttribute('width', component.width); canvas.setAttribute('height', component.height); canvas.setAttribute('style', component.style); return canvas; } } } class RuntimeJsDOM extends RuntimeDOM { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); /** * dom - No comment */ if (typeof options.dom === 'undefined') { options.dom = new JSDOM(`

R3 NodeJS DOM

`);; } Object.assign(this, options); } /** * buildInstance() * - Creates a runtime instance object based on the R3.Component representing it. * @param component */ buildInstance(component) { if (component instanceof ComponentCanvas) { let canvas = this.dom.window.document.createElement('canvas'); canvas.setAttribute('width', component.width); canvas.setAttribute('height', component.height); canvas.setAttribute('style', component.style); return canvas; } } } class RuntimeGUI extends Runtime { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); /** * name - Name of the runtime */ if (typeof options.name === 'undefined') { options.name = this.constructor.name; } Object.assign(this, options); Event.Emit(Event.RUNTIME_CREATED, this); } } class RuntimeControlKit extends RuntimeGUI { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); Object.assign(this, options); } /** * buildInstance() * - Creates a runtime instance object based on the R3.Component representing it. * @param component */ buildInstance(component) { } } class RuntimeGraphics extends Runtime { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); /** * name - Name of the runtime */ if (typeof options.name === 'undefined') { options.name = this.constructor.name; } Object.assign(this, options); Event.Emit(Event.RUNTIME_CREATED, this); } } class RuntimeThree extends RuntimeGraphics { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); Object.assign(this, options); } /** * buildInstance() * - Creates a runtime instance object based on the R3.Component representing it. * @param component */ buildInstance(component) { } } class RuntimeImage extends Runtime { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); /** * name - Name of the runtime */ if (typeof options.name === 'undefined') { options.name = this.constructor.name; } Object.assign(this, options); Event.Emit(Event.RUNTIME_CREATED, this); } } class RuntimeNodeJSImage extends RuntimeImage { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); Object.assign(this, options); } /** * buildInstance() * - Creates a runtime instance object based on the R3.Component representing it. * @param component */ buildInstance(component) { } } class RuntimeWebImage extends RuntimeImage { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); Object.assign(this, options); } /** * buildInstance() * - Creates a runtime instance object based on the R3.Component representing it. * @param component */ buildInstance(component) { if (component instanceof R3.Component.Graphics.Image) { let image = document.createElement('img'); image.setAttribute('src', component.src); image.setAttribute('alt', component.alt); return image; } } } class RuntimePhysics extends Runtime { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); /** * name - Name of the runtime */ if (typeof options.name === 'undefined') { options.name = this.constructor.name; } Object.assign(this, options); Event.Emit(Event.RUNTIME_CREATED, this); } } class RuntimeBullet extends RuntimePhysics { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); Object.assign(this, options); } /** * buildInstance() * - Creates a runtime instance object based on the R3.Component representing it. * @param component */ buildInstance(component) { } } class RuntimeSocket extends Runtime { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); /** * name - Name of the runtime */ if (typeof options.name === 'undefined') { options.name = this.constructor.name; } Object.assign(this, options); Event.Emit(Event.RUNTIME_CREATED, this); } } class RuntimeStatistics extends Runtime { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); /** * name - Name of the runtime */ if (typeof options.name === 'undefined') { options.name = this.constructor.name; } Object.assign(this, options); Event.Emit(Event.RUNTIME_CREATED, this); } } class RuntimeStats extends RuntimeStatistics { constructor(options) { if (typeof options === 'undefined') { options = {}; } super(options); Object.assign(this, options); } /** * buildInstance() * - Creates a runtime instance object based on the R3.Component representing it. * @param component */ buildInstance(component) { } } class Event { constructor(options) { if (typeof options === 'undefined') { options = {}; } Object.assign(this, options); } /** * async() * - Simply calls 'Async()' passing it the arguments * @param eventId * @param data * @param clientCallback * @param clientErrorCallback */ async( eventId, data, clientCallback, clientErrorCallback ) { return Event.Async( eventId, data, clientCallback, clientErrorCallback ); } /** * emit() * - Simply calls 'Emit()' passing it the arguments * @param eventId * @param data * @param clientCallback * @param clientErrorCallback */ emit( eventId, data, clientCallback, clientErrorCallback ) { return Event.Emit( eventId, data, clientCallback, clientErrorCallback ); } /** * subscribe() * - Simply calls 'Subscribe()' passing it the arguments * @param eventId * @param callback */ subscribe( eventId, callback ) { return Event.Subscribe(eventId, callback.bind(this)); } /** * Async() * - Calls all subscription functions registered to eventId with data, clientCallback and clientErrorCallback as * arguments. If an error occurs during clientCallback it additionally will execute clientErrorCallback with the * error as argument. * @param eventId * @param data * @param clientCallback * @param clientErrorCallback */ static Async( eventId, data, clientCallback, clientErrorCallback ) { if (Event.Subscriptions.hasOwnProperty(eventId)) { let subscriptionIds = Object.keys(Event.Subscriptions[eventId]); subscriptionIds.map( function(subscriptionId) { try { Event.Subscriptions[eventId][subscriptionId](data, clientCallback, clientErrorCallback); } catch (error) { if (clientErrorCallback) { clientErrorCallback(error); } else { console.error(error); throw error; } } } ) } } /** * Emit() * - Calls all subscription functions registered to eventId with data as arg. Calls clientCallback directly after * the event result is obtained, passing it the result. If an exception occurs during execution, the * clientErrorCallback is called with the error as argument. * @param eventId * @param data * @param clientCallback * @param clientErrorCallback */ static Emit( eventId, data, clientCallback, clientErrorCallback ) { if (Event.Subscriptions.hasOwnProperty(eventId)) { let subscriptionIds = Object.keys(Event.Subscriptions[eventId]); subscriptionIds.map( function(subscriptionId) { try { let result = Event.Subscriptions[eventId][subscriptionId](data); if (clientCallback) { clientCallback(result); } } catch (error) { if (clientErrorCallback) { clientErrorCallback(error); } else { console.error(error); throw error; } } } ) } else { if (clientCallback) { clientCallback(); } } } /** * Subscribe() * - Subscribes to 'eventName', ex. Event.BEFORE_RENDER and executes 'callback()' when eventName is raised * @param eventId * @param callback * @returns {Object} - A handle to the subscription which can be removed by calling handle.remove() */ static Subscribe( eventId, callback ) { let subscriptionId = Utils.RandomId(10); if (Event.Subscriptions.hasOwnProperty(eventId)) { if (Event.Subscriptions[eventId][subscriptionId]) { throw new Error('A component can only subscribe to a particular event ID once'); } Event.Subscriptions[eventId][subscriptionId] = callback; } else { Event.Subscriptions[eventId] = {}; Event.Subscriptions[eventId][subscriptionId] = callback; } /** * Return a handle to the caller to allow us to unsubscribe to this event */ return { fn: callback, remove: function (eventId, subscriptionId) { return function () { /** * Stop listening for this event from this component */ delete Event.Subscriptions[eventId][subscriptionId]; /** * If the length of listeners is 0, stop referencing this event * @type {string[]} */ let listeners = Object.keys(Event.Subscriptions[eventId]); if (listeners.length === 0) { delete Event.Subscriptions[eventId]; } return true; } }(eventId, subscriptionId), subscriptionId : subscriptionId }; } /** * Some nice Events handling * @type {{}} */ static Subscriptions = {}; } Event.BEFORE_RENDER = 0x1; Event.BLACKLISTED_COMPONENT_INSTANCE_REQUEST = 0x2; Event.CANVAS_COMPONENT_INITIALIZED = 0x3; Event.COMPONENT_CREATED = 0x4; Event.COMPONENT_INITIALIZED = 0x5; Event.CREATE_INSTANCE_BEFORE = 0x6; Event.DISPOSE_INSTANCE = 0x7; Event.DISPOSE_OBJECT = 0x8; Event.DOM_COMPONENT_INITIALIZED = 0x9; Event.ENTITY_CREATED = 0xa; Event.ENTITY_INITIALIZED = 0xb; Event.ENTITY_STARTED = 0xc; Event.FINAL_INITIALIZE = 0xd; Event.GET_API_URL = 0xe; Event.GET_RUNTIME = 0xf; Event.GET_WINDOW_SIZE = 0x10; Event.GRAPHICS_COMPONENT_INITIALIZED = 0x11; Event.IMAGE_COMPONENT_INITIALIZED = 0x12; Event.IMAGE_INSTANCE_CREATED = 0x13; Event.INPUT_COMPONENT_INITIALIZED = 0x14; Event.INSTANCE_CREATED = 0x15; Event.INSTANCE_DISPOSED = 0x16; Event.KEYBOARD_DOWN = 0x17; Event.KEYBOARD_UP = 0x18; Event.MOUSE_DOWN = 0x19; Event.MOUSE_MOVE = 0x1a; Event.MOUSE_UP = 0x1b; Event.MOUSE_WHEEL = 0x1c; Event.OBJECT_CREATED = 0x1d; Event.OBJECT_PROPERTY_UPDATE = 0x1e; Event.OBJECT_PROPERTY_UPDATED = 0x1f; Event.ON_READY_STATE_CHANGE = 0x20; Event.PAUSE = 0x21; Event.PROJECT_INITIALIZED = 0x22; Event.RESTART = 0x23; Event.RUNTIME_CREATED = 0x24; Event.SLIDER_ENTITY_INITIALIZED = 0x25; Event.START = 0x26; Event.TOUCH_CANCEL = 0x27; Event.TOUCH_COMPONENT_INITIALIZED = 0x28; Event.TOUCH_END = 0x29; Event.TOUCH_MOVE = 0x2a; Event.TOUCH_START = 0x2b; Event.UPDATE_FROM_INSTANCE_AFTER = 0x2c; Event.UPDATE_FROM_INSTANCE_BEFORE = 0x2d; Event.UPDATE_INSTANCE_AFTER = 0x2e; Event.UPDATE_INSTANCE_BEFORE = 0x2f; Event.UPDATE_INSTANCE_PROPERTY = 0x30; Event.UPDATE_PROPERTY_FROM_INSTANCE = 0x31; Event.MAX_EVENTS = 0x32; Event.GetEventName = function(eventId) { switch(eventId) { case 0x1 : return 'before_render'; case 0x2 : return 'blacklisted_component_instance_request'; case 0x3 : return 'canvas_component_initialized'; case 0x4 : return 'component_created'; case 0x5 : return 'component_initialized'; case 0x6 : return 'create_instance_before'; case 0x7 : return 'dispose_instance'; case 0x8 : return 'dispose_object'; case 0x9 : return 'dom_component_initialized'; case 0xa : return 'entity_created'; case 0xb : return 'entity_initialized'; case 0xc : return 'entity_started'; case 0xd : return 'final_initialize'; case 0xe : return 'get_api_url'; case 0xf : return 'get_runtime'; case 0x10 : return 'get_window_size'; case 0x11 : return 'graphics_component_initialized'; case 0x12 : return 'image_component_initialized'; case 0x13 : return 'image_instance_created'; case 0x14 : return 'input_component_initialized'; case 0x15 : return 'instance_created'; case 0x16 : return 'instance_disposed'; case 0x17 : return 'keyboard_down'; case 0x18 : return 'keyboard_up'; case 0x19 : return 'mouse_down'; case 0x1a : return 'mouse_move'; case 0x1b : return 'mouse_up'; case 0x1c : return 'mouse_wheel'; case 0x1d : return 'object_created'; case 0x1e : return 'object_property_update'; case 0x1f : return 'object_property_updated'; case 0x20 : return 'on_ready_state_change'; case 0x21 : return 'pause'; case 0x22 : return 'project_initialized'; case 0x23 : return 'restart'; case 0x24 : return 'runtime_created'; case 0x25 : return 'slider_entity_initialized'; case 0x26 : return 'start'; case 0x27 : return 'touch_cancel'; case 0x28 : return 'touch_component_initialized'; case 0x29 : return 'touch_end'; case 0x2a : return 'touch_move'; case 0x2b : return 'touch_start'; case 0x2c : return 'update_from_instance_after'; case 0x2d : return 'update_from_instance_before'; case 0x2e : return 'update_instance_after'; case 0x2f : return 'update_instance_before'; case 0x30 : return 'update_instance_property'; case 0x31 : return 'update_property_from_instance'; default : throw new Error('Event type not defined : ' + eventId); } }; class R3Object extends Event { constructor(options) { if (typeof options === 'undefined') { options = {}; } if (typeof options.maxDepth === 'undefined') { options.maxDepth = 0; } if (typeof options.callDepth === 'undefined') { options.callDepth = 0; } else { options.callDepth++; } options.maxDepth = options.callDepth; super(options); /** * id - Each Object receives an 15 digit random ID which uniquely identifies it everywhere (client * and server side) */ if (typeof options.id === 'undefined') { options.id = Utils.RandomId(15); } /** * name - Each Object has a name */ if (typeof options.name === 'undefined') { options.name = this.constructor.name + '(' + options.id + ')'; } /** * ready - Normally all objects are 'ready' to have their instances created, unless it has some * required Object which is not present at time of construction and will be assigned later. * All objects have multiple sets of 'required' objects which affect its 'ready' state. */ if (typeof options.ready === 'undefined') { options.ready = []; } /** * dirty - When dirty is true, it means that this object is in transition from having a child Object * to not having this child Object, and will probably end up in an uninitialized state. * During the next few clock cycles this child Object will have its parent reference to this * Object removed. */ if (typeof options.dirty === 'undefined') { options.dirty = false; } /** * initialized - A boolean which indicates whether or not this Object has initialized */ if (typeof options.initialized === 'undefined') { options.initialized = false; } /** * initializeDepth - The amount of times this Object passed through initialize() functions */ if (typeof options.initializeDepth === 'undefined') { options.initializeDepth = 0; } /** * parents - All Objects could have parent(s) */ if (typeof options.parents === 'undefined') { options.parents = []; } /** * children - All Objects could have some children */ if (typeof options.children === 'undefined') { options.children = []; } /** * required - All Objects could have some required Objects which need to initialize before this Object * can initialize */ if (typeof options.required === 'undefined') { options.required = []; } Object.assign(this, options); this.emit(Event.OBJECT_CREATED, this); if (options.callDepth === 0) { this.initialize(); } else { options.callDepth--; } } /** * initialize() * - Initializes this Object if all of its required Objects are initialized */ initialize() { this.initialized = true; if (this.initializeDepth === this.maxDepth) { throw new Error('You should not try to instantiate this base class - extend it rather...'); } else { this.initializeDepth++; } } /** * isReady() * - Checks whether or not at least one set of requirements pass the ready state (i.e. required objects are * assigned and ready) */ isReady() { if (this.required.length === 0) { return true; } for (let r = 0; r < this.required.length; r++) { /** * We assume it is ready and will fail the ready test if a requirementValueCheck() fails */ let ready = true; let requirements = this.required[r]; for (let property in requirements) { if (requirements.hasOwnProperty(property)) { if (!this.requirementValueCheck(requirements[property], this[property])) { ready = false; } } } /** * The ready state could have changed or not - regardless it should cascade this information in case * new instances need to be created (ex. an image is added to an array and needs its instance created). */ let readyStateChange = false; if (this.ready[r] !== ready) { readyStateChange = true; } this.ready[r] = ready; if (readyStateChange) { if (this.ready[r]) { this.emit( Event.ON_READY_STATE_CHANGE, { r3Object: this, readyState: R3Object.READY_STATE_TRUE, readyStateIndex: r } ); } else { this.emit( Event.ON_READY_STATE_CHANGE, { r3Object: this, readyState: R3Object.READY_STATE_FALSE, readyStateIndex: r } ); } } } /** * If at least one requirements set pass - return true. */ for (let r = 0; r < this.required.length; r++) { if (this.ready[r]) { return true; } } return false; } /** * requirementValueCheck() * - Checks whether or not the property / value pair meets the requirements of setting this ready to true. It * handles values of type Object and Array properly. * @param property * @param value */ requirementValueCheck( property, value ) { /** * The property change is one of the required properties of this Object, so it affects the overall * 'readiness' of this object. */ if (property instanceof Array) { if (!(value instanceof Array)) { return false; } if (value.length === 0) { return false; } for (let v = 0; v < value.length; v++) { if (!(value[v] instanceof property[0])) { return false; } if (!(value[v].isReady())) { return false; } } return true; } else { if (!(value instanceof property)) { return false; } return value.isReady(); } } } R3Object.PROJECT = 0x0; R3Object.ENTITY = 0x1; R3Object.ENTITY_SLIDER = 0x2; R3Object.COMPONENT = 0x3; R3Object.COMPONENT_CODE = 0x4; R3Object.COMPONENT_DOM = 0x5; R3Object.COMPONENT_CANVAS = 0x6; R3Object.COMPONENT_GEOMETRY = 0x7; R3Object.COMPONENT_BUFFER_GEOMETRY = 0x8; R3Object.COMPONENT_PLANE_GEOMETRY = 0x9; R3Object.COMPONENT_GRAPHICS = 0xa; R3Object.COMPONENT_IMAGE = 0xb; R3Object.COMPONENT_INPUT = 0xc; R3Object.COMPONENT_TOUCH = 0xd; R3Object.COMPONENT_MATERIAL = 0xe; R3Object.COMPONENT_MESH = 0xf; R3Object.COMPONENT_TEXTURE = 0x10; R3Object.MAX_R3OBJECT = 0x11; R3Object.READY_STATE_FALSE = 0x1; R3Object.READY_STATE_PENDING_FALSE = 0x2; R3Object.READY_STATE_PENDING_TRUE = 0x3; R3Object.READY_STATE_TRUE = 0x4; class Project extends R3Object { constructor(options) { if (typeof options === 'undefined') { options = {}; } if (typeof options.maxDepth === 'undefined') { options.maxDepth = 0; } if (typeof options.initialized === 'undefined') { options.initialized = false; } if (typeof options.callDepth === 'undefined') { options.callDepth = 0; } else { options.callDepth++; } options.maxDepth = options.callDepth; super(options); this.underConstruction = true; Object.assign(this, options); this.underConstruction = false; if (options.callDepth === 0) { this.initialize(); } else { options.callDepth--; } } /** * initialize() * - Initializes this Object if all of its required Objects are initialized */ initialize() { super.initialize(); if (this.initializeDepth === this.maxDepth) { this.emit(Event.FINAL_INITIALIZE, this); } else { this.initializeDepth++; } } } class Entity extends R3Object { constructor(options) { if (typeof options === 'undefined') { options = {}; } if (typeof options.maxDepth === 'undefined') { options.maxDepth = 0; } if (typeof options.callDepth === 'undefined') { options.callDepth = 0; } else { options.callDepth++; } options.maxDepth = options.callDepth; super(options); Object.assign(this, options); this.emit(Event.ENTITY_CREATED, this); if (options.callDepth === 0) { this.initialize(); } else { options.callDepth--; } } /** * initialize() * - Starts the Entity if all of its sub-Components are initialized */ initialize() { super.initialize(); Event.Emit(Event.ENTITY_INITIALIZED, this); if (this.initializeDepth === this.maxDepth) { throw new Error('You should not try to instantiate this base class - extend it rather...'); } else { this.initializeDepth++; } } } Entity.SLIDER = 0x0; Entity.MAX_ENTITY = 0x1; /** [0]images=[ComponentImage] - We need a list of at least one Image which to slide [0]canvas=ComponentCanvas - We need a Canvas to attach our Input events [0]touch=ComponentTouch - We need a Touch Component to respond to TOUCH Events **/ class EntitySlider extends Entity { constructor(options) { if (typeof options === 'undefined') { options = {}; } if (typeof options.maxDepth === 'undefined') { options.maxDepth = 0; } if (typeof options.callDepth === 'undefined') { options.callDepth = 0; } else { options.callDepth++; } options.maxDepth = options.callDepth; super(options); /** * components - A list of components that this entity is composed of */ if (typeof options.components === 'undefined') { options.components = []; } /** * started - Indicates whether or not this entity is active (subscribing to events) or not */ if (typeof options.started === 'undefined') { options.started = false; } /** * subscriptions - An association object which hold the subscription handles for Events this system is * listening to. The system can stop receiving events by calling remove() on a handle. */ if (typeof options.subscriptions === 'undefined') { options.subscriptions = {}; } /** * We need to assign some options here to the entity * because SystemLinking requires 'components[]' to * exist when determining parent / child relationships */ Object.assign(this, options); /** * canvas - The Canvas Component to which this entity binds its custom code components */ if (typeof options.canvas === 'undefined') { options.canvas = null; } /** * touch - The slider component reacts to TOUCH Events, for this a touch component is required */ if (typeof options.touch === 'undefined') { options.touch = null; } /** * images - The Image Components which will be used to slide from one image to the next */ if (typeof options.images === 'undefined') { options.images = []; } /** * images - We need a list of at least one Image which to slide */ if (typeof options.required[0] === 'undefined') { options.required[0] = {}; } options.required[0].images = [ComponentImage]; if (typeof this._cache === 'undefined') { this._cache = {}; } this._cache['images'] = options.images; Object.defineProperty( this, 'images', { configurable : true, enumerable : true, set: (x) => { Event.Emit( Event.OBJECT_PROPERTY_UPDATE, { r3Object : this, property : 'images', value : x } ); this._cache['images'] = x; Event.Emit( Event.OBJECT_PROPERTY_UPDATED, { r3Object : this, property : 'images', value : x } ); return x; }, get : () => { return this._cache['images']; } } ) /** * canvas - We need a Canvas to attach our Input events */ if (typeof options.required[0] === 'undefined') { options.required[0] = {}; } options.required[0].canvas = ComponentCanvas; if (typeof this._cache === 'undefined') { this._cache = {}; } this._cache['canvas'] = options.canvas; Object.defineProperty( this, 'canvas', { configurable : true, enumerable : true, set: (x) => { Event.Emit( Event.OBJECT_PROPERTY_UPDATE, { r3Object : this, property : 'canvas', value : x } ); this._cache['canvas'] = x; Event.Emit( Event.OBJECT_PROPERTY_UPDATED, { r3Object : this, property : 'canvas', value : x } ); return x; }, get : () => { return this._cache['canvas']; } } ) /** * touch - We need a Touch Component to respond to TOUCH Events */ if (typeof options.required[0] === 'undefined') { options.required[0] = {}; } options.required[0].touch = ComponentTouch; if (typeof this._cache === 'undefined') { this._cache = {}; } this._cache['touch'] = options.touch; Object.defineProperty( this, 'touch', { configurable : true, enumerable : true, set: (x) => { Event.Emit( Event.OBJECT_PROPERTY_UPDATE, { r3Object : this, property : 'touch', value : x } ); this._cache['touch'] = x; Event.Emit( Event.OBJECT_PROPERTY_UPDATED, { r3Object : this, property : 'touch', value : x } ); return x; }, get : () => { return this._cache['touch']; } } ) for (let r = 0; r < this.required.length; r++) { this.ready[r] = true; } this.underConstruction = true; Object.assign(this, options); this.underConstruction = false; if (options.callDepth === 0) { this.initialize(); } else { options.callDepth--; } } /** * initialize() * - Starts the Entity if all of its sub-Components are initialized. Calls the parent initialize(). */ initialize() { super.initialize(); Event.Emit(Event.SLIDER_ENTITY_INITIALIZED, this); if (this.initializeDepth === this.maxDepth) { this.emit(Event.FINAL_INITIALIZE, this); } else { this.initializeDepth++; } } /** * start() * - Starts the entity by subscribing to all events */ start() { this.subscriptions['BEFORE_RENDER'] = this.subscribe( Event.BEFORE_RENDER, this.OnBeforeRender ); this.subscriptions['TOUCH_START'] = this.subscribe( Event.TOUCH_START, this.OnTouchStart ); this.subscriptions['TOUCH_MOVE'] = this.subscribe( Event.TOUCH_MOVE, this.OnTouchMove ); this.subscriptions['TOUCH_END'] = this.subscribe( Event.TOUCH_END, this.OnTouchEnd ); this.started = true; if (this instanceof Entity) { this.emit( Event.ENTITY_STARTED, this ); } console.log('Started transient system: EntitySlider'); } /** * stop() * - Stops this entity by removing all subscriptions to events */ stop() { this.subscriptions['BEFORE_RENDER'].remove(); delete this.subscriptions['BEFORE_RENDER']; this.subscriptions['TOUCH_START'].remove(); delete this.subscriptions['TOUCH_START']; this.subscriptions['TOUCH_MOVE'].remove(); delete this.subscriptions['TOUCH_MOVE']; this.subscriptions['TOUCH_END'].remove(); delete this.subscriptions['TOUCH_END']; this.started = false; console.log('Stopped transient system: EntitySlider'); } /** * OnBeforeRender() * - Listens to events of type Event.BEFORE_RENDER and executes this function. * @param data (The event data passed as argument - typically an R3Object) * @return null */ OnBeforeRender(data) { } /** * OnTouchStart() * - Listens to events of type Event.TOUCH_START and executes this function. * @param data (The event data passed as argument - typically an R3Object) * @return null */ OnTouchStart(data) { } /** * OnTouchMove() * - Listens to events of type Event.TOUCH_MOVE and executes this function. * @param data (The event data passed as argument - typically an R3Object) * @return null */ OnTouchMove(data) { } /** * OnTouchEnd() * - Listens to events of type Event.TOUCH_END and executes this function. * @param data (The event data passed as argument - typically an R3Object) * @return null */ OnTouchEnd(data) { } } class Component extends R3Object { constructor(options) { if (typeof options === 'undefined') { options = {}; } if (typeof options.maxDepth === 'undefined') { options.maxDepth = 0; } if (typeof options.callDepth === 'undefined') { options.callDepth = 0; } else { options.callDepth++; } options.maxDepth = options.callDepth; super(options); Object.assign(this, options); this.emit(Event.COMPONENT_CREATED, this); if (options.callDepth === 0) { this.initialize(); } else { options.callDepth--; } } /** * initialize() * - Should raises an event(s) which indicates that this object initialized */ initialize() { super.initialize(); Event.Emit(Event.COMPONENT_INITIALIZED, this); if (this.initializeDepth === this.maxDepth) { throw new Error('You should not try to instantiate this base class - extend it rather...'); } else { this.initializeDepth++; } } /** * createInstance() * - Creates an instance of this object based on the current runtime */ createInstance() { this.emit( Event.CREATE_INSTANCE_BEFORE, this, function(delayInstance) { if (delayInstance === true) { console.log('Instance creation delayed for ' + this.name); } else { /** * Set the runtime */ this.setRuntime(); /** * Let the runtime build the instance */ this.instance = this.runtime.buildInstance(this); /** * Notify anyone who might be interested */ this.emit(Event.INSTANCE_CREATED, this); } }.bind(this) ); } /** * dispose() * - Disposes of this object by disposing the instance first. */ dispose() { this.subscribe( Event.INSTANCE_DISPOSED, function(object) { if (object === this) { this.emit(Event.DISPOSE_OBJECT, this); } } ); this.disposeInstance(); } /** * disposeInstance() * - Disposes of the runtime instance. */ disposeInstance() { console.log('Disposing instance of ' + this.name); this.emit(Event.DISPOSE_INSTANCE, this); } /** * setRuntime() * - Sets the runtime property of this component required for constructing an instance of this component. */ setRuntime() { this.emit( Event.GET_RUNTIME, this, function(runtime) { this.runtime = runtime; }.bind(this) ) } } Component.CODE = 0x0; Component.DOM = 0x1; Component.CANVAS = 0x2; Component.GEOMETRY = 0x3; Component.BUFFER_GEOMETRY = 0x4; Component.PLANE_GEOMETRY = 0x5; Component.GRAPHICS = 0x6; Component.IMAGE = 0x7; Component.INPUT = 0x8; Component.TOUCH = 0x9; Component.MATERIAL = 0xa; Component.MESH = 0xb; Component.TEXTURE = 0xc; Component.MAX_COMPONENT = 0xd; class ComponentCode extends Component { constructor(options) { if (typeof options === 'undefined') { options = {}; } if (typeof options.maxDepth === 'undefined') { options.maxDepth = 0; } if (typeof options.callDepth === 'undefined') { options.callDepth = 0; } else { options.callDepth++; } options.maxDepth = options.callDepth; super(options); /** * instance - Holds the current instance of this object as determined (built) by the runtime object. */ if (typeof options.instance === 'undefined') { options.instance = null; } for (let r = 0; r < this.required.length; r++) { this.ready[r] = true; } this.underConstruction = true; Object.assign(this, options); this.underConstruction = false; if (options.callDepth === 0) { this.initialize(); } else { options.callDepth--; } } /** * initialize() * - Notifies all systems listening that this component initialized. */ initialize() { super.initialize(); if (this.initializeDepth === this.maxDepth) { this.emit(Event.FINAL_INITIALIZE, this); } else { this.initializeDepth++; } } /** * updateInstance() * - Updates this object by copying the values of the current object into it's instance object. * @param property */ updateInstance(property) { this.emit(Event.UPDATE_INSTANCE_BEFORE, this); this.emit(Event.UPDATE_INSTANCE_AFTER, this); } /** * updateFromInstance() * - Updates this object by copying the values of its instance into the current object. * @param property */ updateFromInstance(property) { this.emit(Event.UPDATE_FROM_INSTANCE_BEFORE, this); this.emit(Event.UPDATE_FROM_INSTANCE_AFTER, this); } } class ComponentDOM extends Component { constructor(options) { if (typeof options === 'undefined') { options = {}; } if (typeof options.maxDepth === 'undefined') { options.maxDepth = 0; } if (typeof options.callDepth === 'undefined') { options.callDepth = 0; } else { options.callDepth++; } options.maxDepth = options.callDepth; super(options); /** * instance - Holds the current instance of this object as determined (built) by the runtime object. */ if (typeof options.instance === 'undefined') { options.instance = null; } /** * instance - Holds the current instance of this object as determined (built) by the runtime object. */ if (typeof options.instance === 'undefined') { options.instance = null; } for (let r = 0; r < this.required.length; r++) { this.ready[r] = true; } this.underConstruction = true; Object.assign(this, options); this.underConstruction = false; if (options.callDepth === 0) { this.initialize(); } else { options.callDepth--; } } /** * initialize() * - Notifies all systems listening that this component initialized. */ initialize() { super.initialize(); Event.Emit(Event.DOM_COMPONENT_INITIALIZED, this); if (this.initializeDepth === this.maxDepth) { this.emit(Event.FINAL_INITIALIZE, this); } else { this.initializeDepth++; } } /** * updateInstance() * - Updates this object by copying the values of the current object into it's instance object. * @param property */ updateInstance(property) { this.emit(Event.UPDATE_INSTANCE_BEFORE, this); if (property === 'instance') { this.instance.instance = this.instance; this.emit( Event.UPDATE_INSTANCE_PROPERTY, { component : this, property : 'instance', instanceProperty : 'instance' } ); if (property !== 'all') { return; } } this.emit(Event.UPDATE_INSTANCE_AFTER, this); } /** * updateFromInstance() * - Updates this object by copying the values of its instance into the current object. * @param property */ updateFromInstance(property) { this.emit(Event.UPDATE_FROM_INSTANCE_BEFORE, this); if (property === 'instance' || property === 'all') { this.instance = this.instance.instance; this.emit( Event.UPDATE_PROPERTY_FROM_INSTANCE, { component : this, property : 'instance', instanceProperty : 'instance' } ); if (property !== 'all') { return; } } this.emit(Event.UPDATE_FROM_INSTANCE_AFTER, this); } } class ComponentCanvas extends ComponentDOM { constructor(options) { if (typeof options === 'undefined') { options = {}; } if (typeof options.maxDepth === 'undefined') { options.maxDepth = 0; } if (typeof options.callDepth === 'undefined') { options.callDepth = 0; } else { options.callDepth++; } options.maxDepth = options.callDepth; super(options); /** * instance - Holds the current instance of this object as determined (built) by the runtime object. */ if (typeof options.instance === 'undefined') { options.instance = null; } /** * type - No comment */ if (typeof options.type === 'undefined') { options.type = 'canvas'; } /** * width - The initial width of the canvas (You can override it with CSS) */ if (typeof options.width === 'undefined') { options.width = 500; } /** * height - The initial height of the canvas (You can override it with CSS) */ if (typeof options.height === 'undefined') { options.height = 500; } /** * style - No comment */ if (typeof options.style === 'undefined') { options.style = 'border:1px solid #00bb00;'; } for (let r = 0; r < this.required.length; r++) { this.ready[r] = true; } this.underConstruction = true; Object.assign(this, options); this.underConstruction = false; if (options.callDepth === 0) { this.initialize(); } else { options.callDepth--; } } /** * initialize() * - Notifies all systems listening that this component initialized. */ initialize() { super.initialize(); Event.Emit(Event.CANVAS_COMPONENT_INITIALIZED, this); if (this.initializeDepth === this.maxDepth) { this.emit(Event.FINAL_INITIALIZE, this); } else { this.initializeDepth++; } } /** * updateInstance() * - Updates this object by copying the values of the current object into it's instance object. * @param property */ updateInstance(property) { this.emit(Event.UPDATE_INSTANCE_BEFORE, this); if (property === 'width') { this.instance.width = this.width; this.emit( Event.UPDATE_INSTANCE_PROPERTY, { component : this, property : 'width', instanceProperty : 'width' } ); if (property !== 'all') { return; } } if (property === 'height') { this.instance.height = this.height; this.emit( Event.UPDATE_INSTANCE_PROPERTY, { component : this, property : 'height', instanceProperty : 'height' } ); if (property !== 'all') { return; } } if (property === 'style') { this.instance.style = this.style; this.emit( Event.UPDATE_INSTANCE_PROPERTY, { component : this, property : 'style', instanceProperty : 'style' } ); if (property !== 'all') { return; } } this.emit(Event.UPDATE_INSTANCE_AFTER, this); } /** * updateFromInstance() * - Updates this object by copying the values of its instance into the current object. * @param property */ updateFromInstance(property) { this.emit(Event.UPDATE_FROM_INSTANCE_BEFORE, this); if (property === 'width' || property === 'all') { this.width = this.instance.width; this.emit( Event.UPDATE_PROPERTY_FROM_INSTANCE, { component : this, property : 'width', instanceProperty : 'width' } ); if (property !== 'all') { return; } } if (property === 'height' || property === 'all') { this.height = this.instance.height; this.emit( Event.UPDATE_PROPERTY_FROM_INSTANCE, { component : this, property : 'height', instanceProperty : 'height' } ); if (property !== 'all') { return; } } if (property === 'style' || property === 'all') { this.style = this.instance.style; this.emit( Event.UPDATE_PROPERTY_FROM_INSTANCE, { component : this, property : 'style', instanceProperty : 'style' } ); if (property !== 'all') { return; } } this.emit(Event.UPDATE_FROM_INSTANCE_AFTER, this); } } class ComponentGeometry extends Component { constructor(options) { if (typeof options === 'undefined') { options = {}; } if (typeof options.maxDepth === 'undefined') { options.maxDepth = 0; } if (typeof options.callDepth === 'undefined') { options.callDepth = 0; } else { options.callDepth++; } options.maxDepth = options.callDepth; super(options); /** * instance - Holds the current instance of this object as determined (built) by the runtime object. */ if (typeof options.instance === 'undefined') { options.instance = null; } for (let r = 0; r < this.required.length; r++) { this.ready[r] = true; } this.underConstruction = true; Object.assign(this, options); this.underConstruction = false; if (options.callDepth === 0) { this.initialize(); } else { options.callDepth--; } } /** * initialize() * - Notifies all systems listening that this component initialized. */ initialize() { super.initialize(); if (this.initializeDepth === this.maxDepth) { this.emit(Event.FINAL_INITIALIZE, this); } else { this.initializeDepth++; } } /** * updateInstance() * - Updates this object by copying the values of the current object into it's instance object. * @param property */ updateInstance(property) { this.emit(Event.UPDATE_INSTANCE_BEFORE, this); this.emit(Event.UPDATE_INSTANCE_AFTER, this); } /** * updateFromInstance() * - Updates this object by copying the values of its instance into the current object. * @param property */ updateFromInstance(property) { this.emit(Event.UPDATE_FROM_INSTANCE_BEFORE, this); this.emit(Event.UPDATE_FROM_INSTANCE_AFTER, this); } } class ComponentBufferGeometry extends ComponentGeometry { constructor(options) { if (typeof options === 'undefined') { options = {}; } if (typeof options.maxDepth === 'undefined') { options.maxDepth = 0; } if (typeof options.callDepth === 'undefined') { options.callDepth = 0; } else { options.callDepth++; } options.maxDepth = options.callDepth; super(options); /** * instance - Holds the current instance of this object as determined (built) by the runtime object. */ if (typeof options.instance === 'undefined') { options.instance = null; } for (let r = 0; r < this.required.length; r++) { this.ready[r] = true; } this.underConstruction = true; Object.assign(this, options); this.underConstruction = false; if (options.callDepth === 0) { this.initialize(); } else { options.callDepth--; } } /** * initialize() * - Notifies all systems listening that this component initialized. */ initialize() { super.initialize(); if (this.initializeDepth === this.maxDepth) { this.emit(Event.FINAL_INITIALIZE, this); } else { this.initializeDepth++; } } /** * updateInstance() * - Updates this object by copying the values of the current object into it's instance object. * @param property */ updateInstance(property) { this.emit(Event.UPDATE_INSTANCE_BEFORE, this); this.emit(Event.UPDATE_INSTANCE_AFTER, this); } /** * updateFromInstance() * - Updates this object by copying the values of its instance into the current object. * @param property */ updateFromInstance(property) { this.emit(Event.UPDATE_FROM_INSTANCE_BEFORE, this); this.emit(Event.UPDATE_FROM_INSTANCE_AFTER, this); } } class ComponentPlaneGeometry extends ComponentBufferGeometry { constructor(options) { if (typeof options === 'undefined') { options = {}; } if (typeof options.maxDepth === 'undefined') { options.maxDepth = 0; } if (typeof options.callDepth === 'undefined') { options.callDepth = 0; } else { options.callDepth++; } options.maxDepth = options.callDepth; super(options); /** * instance - Holds the current instance of this object as determined (built) by the runtime object. */ if (typeof options.instance === 'undefined') { options.instance = null; } for (let r = 0; r < this.required.length; r++) { this.ready[r] = true; } this.underConstruction = true; Object.assign(this, options); this.underConstruction = false; if (options.callDepth === 0) { this.initialize(); } else { options.callDepth--; } } /** * initialize() * - Notifies all systems listening that this component initialized. */ initialize() { super.initialize(); if (this.initializeDepth === this.maxDepth) { this.emit(Event.FINAL_INITIALIZE, this); } else { this.initializeDepth++; } } /** * updateInstance() * - Updates this object by copying the values of the current object into it's instance object. * @param property */ updateInstance(property) { this.emit(Event.UPDATE_INSTANCE_BEFORE, this); this.emit(Event.UPDATE_INSTANCE_AFTER, this); } /** * updateFromInstance() * - Updates this object by copying the values of its instance into the current object. * @param property */ updateFromInstance(property) { this.emit(Event.UPDATE_FROM_INSTANCE_BEFORE, this); this.emit(Event.UPDATE_FROM_INSTANCE_AFTER, this); } } class ComponentGraphics extends Component { constructor(options) { if (typeof options === 'undefined') { options = {}; } if (typeof options.maxDepth === 'undefined') { options.maxDepth = 0; } if (typeof options.callDepth === 'undefined') { options.callDepth = 0; } else { options.callDepth++; } options.maxDepth = options.callDepth; super(options); /** * instance - Holds the current instance of this object as determined (built) by the runtime object. */ if (typeof options.instance === 'undefined') { options.instance = null; } for (let r = 0; r < this.required.length; r++) { this.ready[r] = true; } this.underConstruction = true; Object.assign(this, options); this.underConstruction = false; if (options.callDepth === 0) { this.initialize(); } else { options.callDepth--; } } /** * initialize() * - Notifies all systems listening that this component initialized. */ initialize() { super.initialize(); Event.Emit(Event.GRAPHICS_COMPONENT_INITIALIZED, this); if (this.initializeDepth === this.maxDepth) { this.emit(Event.FINAL_INITIALIZE, this); } else { this.initializeDepth++; } } /** * updateInstance() * - Updates this object by copying the values of the current object into it's instance object. * @param property */ updateInstance(property) { this.emit(Event.UPDATE_INSTANCE_BEFORE, this); this.emit(Event.UPDATE_INSTANCE_AFTER, this); } /** * updateFromInstance() * - Updates this object by copying the values of its instance into the current object. * @param property */ updateFromInstance(property) { this.emit(Event.UPDATE_FROM_INSTANCE_BEFORE, this); this.emit(Event.UPDATE_FROM_INSTANCE_AFTER, this); } } class ComponentImage extends ComponentGraphics { constructor(options) { if (typeof options === 'undefined') { options = {}; } if (typeof options.maxDepth === 'undefined') { options.maxDepth = 0; } if (typeof options.callDepth === 'undefined') { options.callDepth = 0; } else { options.callDepth++; } options.maxDepth = options.callDepth; super(options); /** * instance - Holds the current instance of this object as determined (built) by the runtime object. */ if (typeof options.instance === 'undefined') { options.instance = null; } /** * src - The src attribute of this image, defaults to a 15% opaque 1x1 green pixel */ if (typeof options.src === 'undefined') { options.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAABhGlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AcxV9TpSotDu0g4pChumhBVMRRq1CECqFWaNXB5NIvaNKQpLg4Cq4FBz8Wqw4uzro6uAqC4AeIm5uToouU+L+k0CLWg+N+vLv3uHsHCPUy06yucUDTbTOViIuZ7KoYeEUvwgghgFGZWcacJCXRcXzdw8fXuxjP6nzuzxFScxYDfCLxLDNMm3iDeHrTNjjvE0dYUVaJz4nHTLog8SPXFY/fOBdcFnhmxEyn5okjxGKhjZU2ZkVTI54ijqqaTvlCxmOV8xZnrVxlzXvyFwZz+soy12kOIYFFLEGCCAVVlFCGjRitOikWUrQf7+AfdP0SuRRylcDIsYAKNMiuH/wPfndr5ScnvKRgHOh+cZyPYSCwCzRqjvN97DiNE8D/DFzpLX+lDsx8kl5radEjoH8buLhuacoecLkDDDwZsim7kp+mkM8D72f0TVkgfAv0rXm9Nfdx+gCkqavkDXBwCIwUKHu9w7t72nv790yzvx9QEXKZwXQv9wAAAAZiS0dEADIANAAxUB4d7wAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+UJEw0JHX/9/lIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAADUlEQVQI12Ng+M+gBgADKAEm2Ka93QAAAABJRU5ErkJggg=='; } /** * alt - The alt attribute of this image */ if (typeof options.alt === 'undefined') { options.alt = '15% opaque 1x1 green pixel'; } /** * fileName - Name of the image under which it is stored */ if (typeof options.fileName === 'undefined') { options.fileName = Utils.LowerUnderscore(options.name); } /** * extension - Extension of the file name including the '.' (ex. '.jpg') */ if (typeof options.extension === 'undefined') { options.extension = '.png'; } /** * external_path - Path to the image relative to the project defined API URL */ if (typeof options.external_path === 'undefined') { options.external_path = '/images/' + options.id + '.png'; } /** * internal_path - Server side path on the server to the file, excluding filename */ if (typeof options.internal_path === 'undefined') { options.internal_path = '/tmp/' + options.id + '.png'; } /** * contentType - Content type of the file (based on the extension, ex. 'image/jpeg') */ if (typeof options.contentType === 'undefined') { options.contentType = 'image/png'; } /** * size - Size of the file in bytes */ if (typeof options.size === 'undefined') { options.size = 565; } /** * width - Width of the image in pixels */ if (typeof options.width === 'undefined') { options.width = 1; } /** * height - height of the image in pixels */ if (typeof options.height === 'undefined') { options.height = 1; } /** * orientation - The orientation of the image, one of 'square', 'landscape', 'portrait' */ if (typeof options.orientation === 'undefined') { options.orientation = 'square'; } for (let r = 0; r < this.required.length; r++) { this.ready[r] = true; } if (options.extension.match(/(png)$/i)) { options.contentType = 'image/png'; } if (options.extension.match(/(jpg|jpeg)$/i)) { options.contentType = 'image/jpeg'; } if (options.extension.match(/(gif)$/i)) { options.contentType = 'image/gif'; } if (options.width > options.height) { options.orientation = 'landscape'; } if (options.width < options.height) { options.orientation = 'portrait'; } if (options.width === options.height) { options.orientation = 'square'; } this.underConstruction = true; Object.assign(this, options); this.underConstruction = false; if (options.callDepth === 0) { this.initialize(); } else { options.callDepth--; } } /** * initialize() * - Notifies all systems listening that this component initialized. */ initialize() { super.initialize(); Event.Emit(Event.IMAGE_COMPONENT_INITIALIZED, this); if (this.initializeDepth === this.maxDepth) { this.emit(Event.FINAL_INITIALIZE, this); } else { this.initializeDepth++; } } /** * updateInstance() * - Updates this object by copying the values of the current object into it's instance object. * @param property */ updateInstance(property) { this.emit(Event.UPDATE_INSTANCE_BEFORE, this); if (property === 'src') { this.instance.src = this.src; this.emit( Event.UPDATE_INSTANCE_PROPERTY, { component : this, property : 'src', instanceProperty : 'src' } ); if (property !== 'all') { return; } } if (property === 'alt') { this.instance.alt = this.alt; this.emit( Event.UPDATE_INSTANCE_PROPERTY, { component : this, property : 'alt', instanceProperty : 'alt' } ); if (property !== 'all') { return; } } if (property === 'external_path') { this.instance.external_path = this.external_path; this.emit( Event.UPDATE_INSTANCE_PROPERTY, { component : this, property : 'external_path', instanceProperty : 'external_path' } ); if (property !== 'all') { return; } } if (property === 'internal_path') { this.instance.internal_path = this.internal_path; this.emit( Event.UPDATE_INSTANCE_PROPERTY, { component : this, property : 'internal_path', instanceProperty : 'internal_path' } ); if (property !== 'all') { return; } } if (property === 'size') { this.instance.size = this.size; this.emit( Event.UPDATE_INSTANCE_PROPERTY, { component : this, property : 'size', instanceProperty : 'size' } ); if (property !== 'all') { return; } } if (property === 'width') { this.instance.width = this.width; this.emit( Event.UPDATE_INSTANCE_PROPERTY, { component : this, property : 'width', instanceProperty : 'width' } ); if (property !== 'all') { return; } } if (property === 'height') { this.instance.height = this.height; this.emit( Event.UPDATE_INSTANCE_PROPERTY, { component : this, property : 'height', instanceProperty : 'height' } ); if (property !== 'all') { return; } } if (property === 'orientation') { this.instance.orientation = this.orientation; this.emit( Event.UPDATE_INSTANCE_PROPERTY, { component : this, property : 'orientation', instanceProperty : 'orientation' } ); if (property !== 'all') { return; } } this.emit(Event.UPDATE_INSTANCE_AFTER, this); } /** * updateFromInstance() * - Updates this object by copying the values of its instance into the current object. * @param property */ updateFromInstance(property) { this.emit(Event.UPDATE_FROM_INSTANCE_BEFORE, this); if (property === 'src' || property === 'all') { this.src = this.instance.src; this.emit( Event.UPDATE_PROPERTY_FROM_INSTANCE, { component : this, property : 'src', instanceProperty : 'src' } ); if (property !== 'all') { return; } } if (property === 'alt' || property === 'all') { this.alt = this.instance.alt; this.emit( Event.UPDATE_PROPERTY_FROM_INSTANCE, { component : this, property : 'alt', instanceProperty : 'alt' } ); if (property !== 'all') { return; } } if (property === 'external_path' || property === 'all') { this.external_path = this.instance.external_path; this.emit( Event.UPDATE_PROPERTY_FROM_INSTANCE, { component : this, property : 'external_path', instanceProperty : 'external_path' } ); if (property !== 'all') { return; } } if (property === 'internal_path' || property === 'all') { this.internal_path = this.instance.internal_path; this.emit( Event.UPDATE_PROPERTY_FROM_INSTANCE, { component : this, property : 'internal_path', instanceProperty : 'internal_path' } ); if (property !== 'all') { return; } } if (property === 'size' || property === 'all') { this.size = this.instance.size; this.emit( Event.UPDATE_PROPERTY_FROM_INSTANCE, { component : this, property : 'size', instanceProperty : 'size' } ); if (property !== 'all') { return; } } if (property === 'width' || property === 'all') { this.width = this.instance.width; this.emit( Event.UPDATE_PROPERTY_FROM_INSTANCE, { component : this, property : 'width', instanceProperty : 'width' } ); if (property !== 'all') { return; } } if (property === 'height' || property === 'all') { this.height = this.instance.height; this.emit( Event.UPDATE_PROPERTY_FROM_INSTANCE, { component : this, property : 'height', instanceProperty : 'height' } ); if (property !== 'all') { return; } } if (property === 'orientation' || property === 'all') { this.orientation = this.instance.orientation; this.emit( Event.UPDATE_PROPERTY_FROM_INSTANCE, { component : this, property : 'orientation', instanceProperty : 'orientation' } ); if (property !== 'all') { return; } } this.emit(Event.UPDATE_FROM_INSTANCE_AFTER, this); } } class ComponentInput extends Component { constructor(options) { if (typeof options === 'undefined') { options = {}; } if (typeof options.maxDepth === 'undefined') { options.maxDepth = 0; } if (typeof options.callDepth === 'undefined') { options.callDepth = 0; } else { options.callDepth++; } options.maxDepth = options.callDepth; super(options); /** * instance - Holds the current instance of this object as determined (built) by the runtime object. */ if (typeof options.instance === 'undefined') { options.instance = null; } for (let r = 0; r < this.required.length; r++) { this.ready[r] = true; } this.underConstruction = true; Object.assign(this, options); this.underConstruction = false; if (options.callDepth === 0) { this.initialize(); } else { options.callDepth--; } } /** * initialize() * - Notifies all systems listening that this component initialized. */ initialize() { super.initialize(); Event.Emit(Event.INPUT_COMPONENT_INITIALIZED, this); if (this.initializeDepth === this.maxDepth) { this.emit(Event.FINAL_INITIALIZE, this); } else { this.initializeDepth++; } } /** * updateInstance() * - Updates this object by copying the values of the current object into it's instance object. * @param property */ updateInstance(property) { this.emit(Event.UPDATE_INSTANCE_BEFORE, this); this.emit(Event.UPDATE_INSTANCE_AFTER, this); } /** * updateFromInstance() * - Updates this object by copying the values of its instance into the current object. * @param property */ updateFromInstance(property) { this.emit(Event.UPDATE_FROM_INSTANCE_BEFORE, this); this.emit(Event.UPDATE_FROM_INSTANCE_AFTER, this); } } /** [0]domComponent=ComponentDOM **/ class ComponentTouch extends ComponentInput { constructor(options) { if (typeof options === 'undefined') { options = {}; } if (typeof options.maxDepth === 'undefined') { options.maxDepth = 0; } if (typeof options.callDepth === 'undefined') { options.callDepth = 0; } else { options.callDepth++; } options.maxDepth = options.callDepth; super(options); /** * instance - Holds the current instance of this object as determined (built) by the runtime object. */ if (typeof options.instance === 'undefined') { options.instance = null; } /** * domComponent - A Touch Component requires a Canvas component to bind to. */ if (typeof options.domComponent === 'undefined') { options.domComponent = null; } /** * domComponent - No comment */ if (typeof options.required[0] === 'undefined') { options.required[0] = {}; } options.required[0].domComponent = ComponentDOM; if (typeof this._cache === 'undefined') { this._cache = {}; } this._cache['domComponent'] = options.domComponent; Object.defineProperty( this, 'domComponent', { configurable : true, enumerable : true, set: (x) => { Event.Emit( Event.OBJECT_PROPERTY_UPDATE, { r3Object : this, property : 'domComponent', value : x } ); this._cache['domComponent'] = x; Event.Emit( Event.OBJECT_PROPERTY_UPDATED, { r3Object : this, property : 'domComponent', value : x } ); return x; }, get : () => { return this._cache['domComponent']; } } ) for (let r = 0; r < this.required.length; r++) { this.ready[r] = true; } this.underConstruction = true; Object.assign(this, options); this.underConstruction = false; if (options.callDepth === 0) { this.initialize(); } else { options.callDepth--; } } /** * initialize() * - Notifies all systems listening that this component initialized. */ initialize() { super.initialize(); Event.Emit(Event.TOUCH_COMPONENT_INITIALIZED, this); if (this.initializeDepth === this.maxDepth) { this.emit(Event.FINAL_INITIALIZE, this); } else { this.initializeDepth++; } } /** * updateInstance() * - Updates this object by copying the values of the current object into it's instance object. * @param property */ updateInstance(property) { this.emit(Event.UPDATE_INSTANCE_BEFORE, this); if (property === 'domComponent') { this.instance.domComponent = this.domComponent; this.emit( Event.UPDATE_INSTANCE_PROPERTY, { component : this, property : 'domComponent', instanceProperty : 'domComponent' } ); if (property !== 'all') { return; } } this.emit(Event.UPDATE_INSTANCE_AFTER, this); } /** * updateFromInstance() * - Updates this object by copying the values of its instance into the current object. * @param property */ updateFromInstance(property) { this.emit(Event.UPDATE_FROM_INSTANCE_BEFORE, this); if (property === 'domComponent' || property === 'all') { this.domComponent = this.instance.domComponent; this.emit( Event.UPDATE_PROPERTY_FROM_INSTANCE, { component : this, property : 'domComponent', instanceProperty : 'domComponent' } ); if (property !== 'all') { return; } } this.emit(Event.UPDATE_FROM_INSTANCE_AFTER, this); } } class ComponentMaterial extends Component { constructor(options) { if (typeof options === 'undefined') { options = {}; } if (typeof options.maxDepth === 'undefined') { options.maxDepth = 0; } if (typeof options.callDepth === 'undefined') { options.callDepth = 0; } else { options.callDepth++; } options.maxDepth = options.callDepth; super(options); /** * instance - Holds the current instance of this object as determined (built) by the runtime object. */ if (typeof options.instance === 'undefined') { options.instance = null; } for (let r = 0; r < this.required.length; r++) { this.ready[r] = true; } this.underConstruction = true; Object.assign(this, options); this.underConstruction = false; if (options.callDepth === 0) { this.initialize(); } else { options.callDepth--; } } /** * initialize() * - Notifies all systems listening that this component initialized. */ initialize() { super.initialize(); if (this.initializeDepth === this.maxDepth) { this.emit(Event.FINAL_INITIALIZE, this); } else { this.initializeDepth++; } } /** * updateInstance() * - Updates this object by copying the values of the current object into it's instance object. * @param property */ updateInstance(property) { this.emit(Event.UPDATE_INSTANCE_BEFORE, this); this.emit(Event.UPDATE_INSTANCE_AFTER, this); } /** * updateFromInstance() * - Updates this object by copying the values of its instance into the current object. * @param property */ updateFromInstance(property) { this.emit(Event.UPDATE_FROM_INSTANCE_BEFORE, this); this.emit(Event.UPDATE_FROM_INSTANCE_AFTER, this); } } /** [0]material=ComponentMaterial [0]geometry=ComponentGeometry [1]material=[ComponentMaterial] [1]geometry=ComponentGeometry **/ class ComponentMesh extends Component { constructor(options) { if (typeof options === 'undefined') { options = {}; } if (typeof options.maxDepth === 'undefined') { options.maxDepth = 0; } if (typeof options.callDepth === 'undefined') { options.callDepth = 0; } else { options.callDepth++; } options.maxDepth = options.callDepth; super(options); /** * instance - Holds the current instance of this object as determined (built) by the runtime object. */ if (typeof options.instance === 'undefined') { options.instance = null; } /** * material - No comment */ if (typeof options.material === 'undefined') { options.material = null; } /** * material - No comment */ if (typeof options.required[0] === 'undefined') { options.required[0] = {}; } options.required[0].material = ComponentMaterial; if (typeof this._cache === 'undefined') { this._cache = {}; } this._cache['material'] = options.material; Object.defineProperty( this, 'material', { configurable : true, enumerable : true, set: (x) => { Event.Emit( Event.OBJECT_PROPERTY_UPDATE, { r3Object : this, property : 'material', value : x } ); this._cache['material'] = x; Event.Emit( Event.OBJECT_PROPERTY_UPDATED, { r3Object : this, property : 'material', value : x } ); return x; }, get : () => { return this._cache['material']; } } ) /** * geometry - No comment */ if (typeof options.required[0] === 'undefined') { options.required[0] = {}; } options.required[0].geometry = ComponentGeometry; if (typeof this._cache === 'undefined') { this._cache = {}; } this._cache['geometry'] = options.geometry; Object.defineProperty( this, 'geometry', { configurable : true, enumerable : true, set: (x) => { Event.Emit( Event.OBJECT_PROPERTY_UPDATE, { r3Object : this, property : 'geometry', value : x } ); this._cache['geometry'] = x; Event.Emit( Event.OBJECT_PROPERTY_UPDATED, { r3Object : this, property : 'geometry', value : x } ); return x; }, get : () => { return this._cache['geometry']; } } ) /** * material - No comment */ if (typeof options.required[1] === 'undefined') { options.required[1] = {}; } options.required[1].material = [ComponentMaterial]; if (typeof this._cache === 'undefined') { this._cache = {}; } this._cache['material'] = options.material; Object.defineProperty( this, 'material', { configurable : true, enumerable : true, set: (x) => { Event.Emit( Event.OBJECT_PROPERTY_UPDATE, { r3Object : this, property : 'material', value : x } ); this._cache['material'] = x; Event.Emit( Event.OBJECT_PROPERTY_UPDATED, { r3Object : this, property : 'material', value : x } ); return x; }, get : () => { return this._cache['material']; } } ) /** * geometry - No comment */ if (typeof options.required[1] === 'undefined') { options.required[1] = {}; } options.required[1].geometry = ComponentGeometry; if (typeof this._cache === 'undefined') { this._cache = {}; } this._cache['geometry'] = options.geometry; Object.defineProperty( this, 'geometry', { configurable : true, enumerable : true, set: (x) => { Event.Emit( Event.OBJECT_PROPERTY_UPDATE, { r3Object : this, property : 'geometry', value : x } ); this._cache['geometry'] = x; Event.Emit( Event.OBJECT_PROPERTY_UPDATED, { r3Object : this, property : 'geometry', value : x } ); return x; }, get : () => { return this._cache['geometry']; } } ) for (let r = 0; r < this.required.length; r++) { this.ready[r] = true; } this.underConstruction = true; Object.assign(this, options); this.underConstruction = false; if (options.callDepth === 0) { this.initialize(); } else { options.callDepth--; } } /** * initialize() * - Notifies all systems listening that this component initialized. */ initialize() { super.initialize(); if (this.initializeDepth === this.maxDepth) { this.emit(Event.FINAL_INITIALIZE, this); } else { this.initializeDepth++; } } /** * updateInstance() * - Updates this object by copying the values of the current object into it's instance object. * @param property */ updateInstance(property) { this.emit(Event.UPDATE_INSTANCE_BEFORE, this); if (property === 'material') { this.instance.material = this.material; this.emit( Event.UPDATE_INSTANCE_PROPERTY, { component : this, property : 'material', instanceProperty : 'material' } ); if (property !== 'all') { return; } } this.emit(Event.UPDATE_INSTANCE_AFTER, this); } /** * updateFromInstance() * - Updates this object by copying the values of its instance into the current object. * @param property */ updateFromInstance(property) { this.emit(Event.UPDATE_FROM_INSTANCE_BEFORE, this); if (property === 'material' || property === 'all') { this.material = this.instance.material; this.emit( Event.UPDATE_PROPERTY_FROM_INSTANCE, { component : this, property : 'material', instanceProperty : 'material' } ); if (property !== 'all') { return; } } this.emit(Event.UPDATE_FROM_INSTANCE_AFTER, this); } } class ComponentTexture extends Component { constructor(options) { if (typeof options === 'undefined') { options = {}; } if (typeof options.maxDepth === 'undefined') { options.maxDepth = 0; } if (typeof options.callDepth === 'undefined') { options.callDepth = 0; } else { options.callDepth++; } options.maxDepth = options.callDepth; super(options); /** * instance - Holds the current instance of this object as determined (built) by the runtime object. */ if (typeof options.instance === 'undefined') { options.instance = null; } for (let r = 0; r < this.required.length; r++) { this.ready[r] = true; } this.underConstruction = true; Object.assign(this, options); this.underConstruction = false; if (options.callDepth === 0) { this.initialize(); } else { options.callDepth--; } } /** * initialize() * - Notifies all systems listening that this component initialized. */ initialize() { super.initialize(); if (this.initializeDepth === this.maxDepth) { this.emit(Event.FINAL_INITIALIZE, this); } else { this.initializeDepth++; } } /** * updateInstance() * - Updates this object by copying the values of the current object into it's instance object. * @param property */ updateInstance(property) { this.emit(Event.UPDATE_INSTANCE_BEFORE, this); this.emit(Event.UPDATE_INSTANCE_AFTER, this); } /** * updateFromInstance() * - Updates this object by copying the values of its instance into the current object. * @param property */ updateFromInstance(property) { this.emit(Event.UPDATE_FROM_INSTANCE_BEFORE, this); this.emit(Event.UPDATE_FROM_INSTANCE_AFTER, this); } } class Graph { constructor(options) { if (typeof options === 'undefined') { options = {}; } /** * open - Internal structure required for traversing the graph */ if (typeof options.open === 'undefined') { options.open = []; } /** * closed - Internal structure required for traversing the graph */ if (typeof options.closed === 'undefined') { options.closed = []; } /** * relevant - By default we traverse all parents and children of a node when performing a search */ if (typeof options.relevant === 'undefined') { options.relevant = ['parents', 'children']; } /** * solutions - Internal structure for holding solutions from n0 to n. n passed the goalFunction() test. */ if (typeof options.solutions === 'undefined') { options.solutions = []; } /** * terminateOnFirstGoal - By default we want to collect all goal nodes which pass the goalFunction(). Set this to * true to terminate immediately after discovering the first goal node. */ if (typeof options.terminateOnFirstGoal === 'undefined') { options.terminateOnFirstGoal = false; } /** * startNode - The start node from which the graph will build */ if (typeof options.startNode === 'undefined') { options.startNode = null; } /** * goalFunction - By having no goal as a default, on construction of a Graph object we will traverse and * build the entire graph starting from the startNode. */ if (typeof options.goalFunction === 'undefined') { options.goalFunction = (node)=>{return false;}; } Object.assign(this, options); if (this.startNode) { this.build(); } } /** * build() * - Uses A* algorithm to build a graph of all nodes which represents the dependency structure of this graph. */ build() { if (this.startNode === null) { throw new Error('startNode cannot be null'); } /** * Step 1. Push start node on open[] */ this.open.push(this.startNode); /** * Step 2. Clear the closed[] list (we also clear the solutions[] list because we maintain it this way) */ this.closed = []; this.solutions = []; /** * Start with step 3 to 8 */ this.process(); } /** * process() * - Repeats steps 3 to 8. Should indicate whether a goal node was found. */ process() { /** * Step 3. If open[] is empty - exit with failure */ if (this.open.length === 0) { console.log('open[] is empty'); return false; } /** * Step 4. Select first node on open[] - remove it from open[] and put it on closed[] */ let node = this.open.pop(); this.closed.push(node); /** * Step 5. If node is a goal function - exit successfully (or continue if we do not terminate on first goal) */ if (this.goalFunction(node)) { this.solutions.push(this.buildSolution(node)); if (this.terminateOnFirstGoal) { return this.solutions[0]; } } /** * Step 6.a Expand node n, generating the set M of its successors that are not already ancestors of n in G. */ let M = this.expand(node); /** * Step 7 - Establish pointers to node */ this.establishPointers(M, node); /** * Step 8 - Re-Order nodes in OPEN in order of increasing ~f values. Ties among minimal ~f values are resolved * in favor of the deepest node in the search tree. * We skip this step because we are not so interested in the shortest path to the solution (yet, for now at least) */ //this.reOrder(); /** * Continue back to step 3 */ this.process(); } /** * expand() * - Expands a node generating a set M of its successors which are not already ancestors of the node (n) in the * graph (G). i.e. *NOT* already in either OPEN or CLOSED. Install those members of M as successors of n in G. * @param node */ expand(node) { let successors = { onOpen : [], onClosed : [], notInG : [] }; for (let r = 0; r < this.relevant.length; r++) { let relevant = this.relevant[r]; for (let n = 0; n < node.object[relevant].length; n++) { let notInG = true; /** * Check if this child is part of an open[] node */ let onOpen = this.open.reduce( (onOpen, openNode) => { if (openNode.object === node.object[relevant][n]) { onOpen = true; notInG = false; } return onOpen; }, false ); let onClosed = this.closed.reduce( (onClosed, closedNode) => { if (closedNode.object === node.object[relevant][n]) { onClosed = true; notInG = false; } return onClosed; }, false ); let type = 'child'; if (relevant === 'parents') { type = 'parent'; } let newNode = new Node( { object: node.object[relevant][n], type: type } ); if (onOpen) { successors.onOpen.push(newNode); } if (onClosed) { successors.onClosed.push(newNode); } if (notInG) { successors.notInG.push(newNode); } } } /** * Return the set of successors (M) to be used later in processing */ return successors; } /** * establishPointers() * - Establish a pointer to n from each of those members of M that were not already in G. Add these members of M to * OPEN. For each member M that was already on OPEN or CLOSED, update the pointer to point to n if the best path * so far is through n. For each member of M that was already on CLOSED, redirect the pointers of each of its * descendants in G so that they point backward along the best paths found so far to these descendants. * @param M * @param node */ establishPointers( M, node ) { /** * We now have a list of successors (M) which is not already an ancestor of n in G. * Install them as successors of n in G. We do part of step 7 here to save some processing steps: * We additionally set the pointer of the successor to n, and we push the successor to OPEN. */ M.notInG.map( (successor) => { successor.pointer = node; node.successors.push(successor); this.open.push(successor); } ) /** * Redirect pointers of successors that were in OPEN to n */ M.onOpen.map( (successor) => { successor.pointer = node; } ); /** * Redirect pointers of successors that were in CLOSED to n */ M.onClosed.map( (successor) => { successor.pointer = node; /** * We skip a part of 7 here - we could alternatively redirect the pointers of each descendant of the successor * in G to point backward along the best paths found so far to these descendants - but we don't do this because * we are not really interested in the shortest paths - */ } ) } /** * reOrder() * - Re-Order nodes in OPEN in order of increasing ~f values. Ties among minimal ~f values are resolved in favor of * the deepest node in the search tree. */ reOrder() { } /** * buildSolution() * - Traverses node pointers from n (goal node) to n0 (start node) and stores the path in a solution list. * @param node * @param solution=[] */ buildSolution( node, solution=[] ) { solution.push(node); if (node.pointer) { this.buildSolution(node.pointer, solution); } else { return solution; } } /** * isGoal() * - No comment * @param callback */ isGoal(callback) { return callback(); } } class Node { constructor(options) { if (typeof options === 'undefined') { options = {}; } /** * object - No comment */ if (typeof options.object === 'undefined') { options.object = null; } /** * pointer - No comment */ if (typeof options.pointer === 'undefined') { options.pointer = null; } /** * type - No comment */ if (typeof options.type === 'undefined') { options.type = 'start-node'; } /** * successors - No comment */ if (typeof options.successors === 'undefined') { options.successors = []; } Object.assign(this, options); } } class Utils { constructor(options) { if (typeof options === 'undefined') { options = {}; } Object.assign(this, options); } static GetFirstParent(object, constructor) { if (Utils.UndefinedOrNull(constructor)) { throw new Error('You need to specify a constructor'); } if (object.parent === null) { return null; } if (object.parent instanceof constructor) { return object.parent; } else { return Utils.GetFirstParent(object.parent, constructor); } }; static SyntaxHighlight(json) { if (typeof json != 'string') { json = JSON.stringify(json, undefined, 2); } json = json.replace(/&/g, '&').replace(//g, '>'); return json.replace(/("(\u[a-zA-Z0-9]{4}|\[^u]|[^\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) { let cls = 'number'; if (/^"/.test(match)) { if (/:$/.test(match)) { cls = 'key'; } else { cls = 'string'; } } else if (/true|false/.test(match)) { cls = 'boolean'; } else if (/null/.test(match)) { cls = 'null'; } return '' + match + ''; }); }; static GetParentProject(component) { if (Utils.UndefinedOrNull(component.parent)) { throw new Error('Parent not found'); } if (component.parent instanceof R3.Project) { return component.parent; } return Utils.GetParentProject(component.parent); }; static GetParents(component, parents) { if (Utils.UndefinedOrNull(parents)) { parents = []; } if (Utils.UndefinedOrNull(component.parent)) { return parents; } parents.push(component.parent); return Utils.GetParents(component.parent, parents); }; /** * @return {boolean} */ static Instance(component) { return Utils.Defined(component) && Utils.Defined(component.instance); }; /** * Utils.RemoveFromSelect * @param select * @param id * @returns {boolean} * @constructor */ static RemoveFromSelect(select, id) { let i; for (i = 0; i < select.options.length; i++) { if (select.options[i].value === id) { select.remove(i); return true; } } return false; }; /** * Utils.GetSelectIndex * * Get the select index of given id * * @param select * @param id * @returns boolean true if successful * * @constructor */ static SetSelectIndex(select, id) { for (let i = 0; i < select.options.length; i++) { if (select.options[i].value === id) { select.selectedIndex = i; return true; } } return false; }; static SortSelect(select) { let tmp = []; let i; for (i = 1; i < select.options.length; i++) { tmp[i-1] = []; tmp[i-1][0] = select.options[i].text; tmp[i-1][1] = select.options[i].value; } tmp.sort(); select.options = [select.options[0]]; for (i = 0; i < tmp.length; i++) { select.options[i+1] = new Option(tmp[i][0], tmp[i][1]); } return; }; /** * Gets the parent of object whith property of optional type constructor. If index is specified, get the parent of the * object with property[index] - which means the property should be an array * @param object * @param property * @param index * @param constructor * @returns {*} * @constructor */ static GetParent(object, property, index, constructor) { if (Utils.UndefinedOrNull(constructor)) { constructor = null; } if (Utils.UndefinedOrNull(index)) { index = null; } if (object.parent) { /** * Parent defined */ if (object.parent.hasOwnProperty(property)) { if (constructor) { if (index) { if (object.parent[property][index] instanceof constructor) { return object.parent[property][index]; } else { if (typeof object.parent.getParent === 'function') { return object.parent.getParent(property, index, constructor); } else { console.warn('getParent not defined on API object : ' + object.parent + ' - you should avoid having these messsages'); return null; } } } else { if (object.parent[property] instanceof constructor) { return object.parent[property]; } else { if (typeof object.parent.getParent === 'function') { return object.parent.getParent(property, index, constructor); } else { console.warn('getParent not defined on API object : ' + object.parent + ' - you should avoid having these messsages'); return null; } } } } else { if (index) { return object.parent[property][index]; } else { return object.parent[property]; } } } else { /** * This parent does not have the property - go a level higher */ if (typeof object.parent.getParent === 'function') { return object.parent.getParent(property, index, constructor); } else { console.warn('getParent not defined on API object : ' + object.parent + ' - you should avoid having these messsages'); return null; } } } else { /** * No parent defined */ console.warn('property : ' + property + ' of type ' + constructor + ' was not found in the parent chain'); return null; } }; /** * Strips image extension from given path * @param imagePath * @constructor */ static StripImageExtension(imagePath) { return imagePath.replace(/(\.png$|\.gif$|\.jpeg$|\.jpg$)/,'') }; /** * Returns true if unloaded * @param component * @returns {boolean} * @constructor */ static Unloaded(component) { if ( Utils.UndefinedOrNull(component) || Utils.UndefinedOrNull(component.instance) ) { return true; } return false; }; /** * * @param component * @returns {boolean} * @constructor */ static Loaded(component) { if (component && component.instance) { return true; } return false; }; static BuildVectorSource(result, name, dimension) { if (dimension === 2) { result[name] = {}; result[name].x = false; result[name].y = false; return; } if (dimension === 3) { result[name] = {}; result[name].x = false; result[name].y = false; result[name].y = false; return; } if (dimension === 4) { result[name] = {}; result[name].x = false; result[name].y = false; result[name].z = false; result[name].w = false; return; } console.warn('unknown dimension : ' + dimension); }; /** * Returns all 'instances' of the array, or null if an 'instance' is undefined * @constructor * @param array */ static GetArrayInstances(array) { return array.reduce( function(result, object) { if (result === null) { return result; } if (Utils.UndefinedOrNull(object.instance)) { result = null; } else { result.push(object.instance); } return result; }, [] ); }; static SortFacesByMaterialIndex(faces) { /** * Sorts faces according to material index because later we will create * groups for each vertice group */ faces.sort(function(a, b) { if (a.materialIndex < b.materialIndex) { return -1; } if (a.materialIndex > b.materialIndex) { return 1; } return 0; }); return faces; }; static BuildQuaternionSource(result, name) { result[name] = {}; result[name].axis = {}; result[name].axis.x = false; result[name].axis.y = false; result[name].axis.z = false; result[name].angle = false; result[name].x = false; result[name].y = false; result[name].z = false; result[name].w = false; }; static GetRuntime() { let result = null; R3.Event.Emit( R3.Event.GET_RUNTIME, null, function(runtime) { result = runtime; } ); return result; }; /** * Returns the window size or null * @returns {*} * @constructor */ static GetWindowSize() { let size = null; R3.Event.Emit( R3.Event.GET_WINDOW_SIZE, null, function(data) { size = data; }.bind(this) ); return size; }; /** * Convenience function to update object width and height members with window size * @param object * @constructor */ static UpdateWindowSize(object) { let size = Utils.GetWindowSize(); object.width = size.width; object.height = size.height; }; /** * Returns id of object with the name if it exists in the array, otherwise null * @param name * @param array * @returns {*} * @constructor */ static ObjectIdWithNameInArray(name, array) { return array.reduce( function(result, object) { if (result) { return result; } if (name === object.name) { return object.id; } return null; }, null ); }; /** * Gets random int exclusive of maximum but inclusive of minimum * @param min * @param max * @returns {*} * @constructor */ static GetRandomInt(min, max) { min = Math.ceil(min); max = Math.floor(max); return Math.floor(Math.random() * (max - min)) + min; //The maximum is exclusive and the minimum is inclusive }; /** * Gets random int inclusive of minimum and maximum * @param min * @param max * @returns {*} * @constructor */ static GetRandomIntInclusive(min, max) { min = Math.ceil(min); max = Math.floor(max); return Math.floor(Math.random() * (max - min + 1)) + min; //The maximum is inclusive and the minimum is inclusive }; static InterpolateArray(data, fitCount) { let linearInterpolate = function(before, after, atPoint) { return before + (after - before) * atPoint; }; let newData = []; let springFactor = Number((data.length - 1) / (fitCount - 1)); newData[0] = data[0]; // for new allocation for ( let i = 1; i < fitCount - 1; i++) { let tmp = i * springFactor; let before = Number(Math.floor(tmp)).toFixed(); let after = Number(Math.ceil(tmp)).toFixed(); let atPoint = tmp - before; newData[i] = linearInterpolate(data[before], data[after], atPoint); } newData[fitCount - 1] = data[data.length - 1]; // for new allocation return newData; }; /** * Undefined or null check * @param variable * @returns {boolean} * @constructor */ static UndefinedOrNull( variable ) { return typeof variable === 'undefined' || variable === null; }; /** * The variable is not undefined and not null * @param variable * @returns {boolean} * @constructor */ static Defined( variable ) { return typeof variable !== 'undefined' && variable !== null; }; /** * Gets function parameters * @param fn * @constructor */ static GetParameters(fn) { let FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m; let FN_ARG_SPLIT = /,/; let FN_ARG = /^\s*(_?)(.+?)\s*$/; let STRIP_COMMENTS = /(\/\/.*$)|(\/\*[\s\S]*?\*\/)|(\s*=[^,\)]*(('(?:\'|[^'\r\n])*')|("(?:\"|[^"\r\n])*"))|(\s*=[^,\)]*))/mg; let parameters, fnText, argDecl; if (typeof fn !== 'function') { parameters = []; fnText = fn.toString().replace(STRIP_COMMENTS, ''); argDecl = fnText.match(FN_ARGS); argDecl[1].split(FN_ARG_SPLIT).forEach(function(arg) { arg.replace(FN_ARG, function(all, underscore, name) { parameters.push(name); }); }); } else { throw Error("not a function") } return parameters; }; /** * Returns either an ID of the object or Null * @param object * @returns {null} * @constructor */ static IdOrNull(object) { if (Utils.UndefinedOrNull(object)) { return null; } else { if (Utils.UndefinedOrNull(object.id)) { console.warn('saving an object reference with no ID : ', object); return null; } return object.id; } }; /** * Limit a property to values between -pi and +pi * @param property * @param objectProperty * @returns {{configurable?: boolean, enumerable?: boolean, value?, writable?: boolean, get?: Function, set?: Function}} * @constructor */ static LimitToPI(property, objectProperty) { let store = objectProperty; return { get : function() { return store; }, set : function(value) { while (value > Math.PI) { value -= (Math.PI * 2); } while (value < -(Math.PI)) { value += (Math.PI * 2); } store = value; } }; }; /** * Returns an array of IDs representing the objects * @param array * @returns [] * @constructor */ static IdArrayOrEmptyArray(array) { if (Utils.UndefinedOrNull(array)) { return []; } else { return array.map(function(item) { if (Utils.UndefinedOrNull(item.id)) { throw new Error('No ID found while trying to store IDs to array'); } return item.id }); } }; /** * Generates a random ID * @returns {string} * @constructor */ static RandomId(length) { if (Utils.UndefinedOrNull(length)) { length = 10; } return Math.random().toString(36).substr(2, length); }; static InvertWindingOrder(triangles) { for (let i = 0; i < triangles.length; i++) { let v1 = triangles[i].v1; triangles[i].v1 = triangles[i].v2; triangles[i].v2 = v1; let backupUV = triangles[i].triangle.v1uv; triangles[i].triangle.v1uv = triangles[i].triangle.v2uv; triangles[i].triangle.v2uv = backupUV; } return triangles; }; /** * Inverts a mesh winding order (and its instance) * @param mesh R3.D3.Mesh * @returns {*} * @constructor */ static InvertMeshWindingOrder(mesh) { mesh.faces.forEach( function(face) { let tmpV1 = face.v1; face.v1 = face.v2; face.v2 = tmpV1; let tmpV1uv = face.v1uv; face.v1uv = face.v2uv; face.v2uv = tmpV1uv; }.bind(this) ); //mesh.computeNormals = true; //mesh.createInstance(); }; /** * This function resets a the winding order of a mesh from a reference point V (the average center of the mesh) */ static ResetWindingOrder(faces, vertices) { let vertexList = new R3.API.Vector3.Points(); for (let v = 0; v < vertices.length; v++) { vertexList.add(new R3.API.Vector3( vertices[v].position.x, vertices[v].position.y, vertices[v].position.z )); } let V = vertexList.average(); let triangles = []; for (let s = 0; s < faces.length; s += 3) { let v0 = faces[s]; let v1 = faces[s+1]; let v2 = faces[s+2]; triangles.push( { v0 : v0, v1 : v1, v2 : v2, edges : [ {v0: v0, v1: v1}, {v0: v1, v1: v2}, {v0: v2, v1: v0} ], winding : 0, edgeIndex : -1, processed : false } ); } for (let i = 0; i < triangles.length; i++) { if ( R3.API.Vector3.clockwise( vertices[triangles[i].v0].position, vertices[triangles[i].v1].position, vertices[triangles[i].v2].position, V ) ) { console.log('clockwise'); let bv1 = triangles[i].v1; triangles[i].v1 = triangles[i].v2; triangles[i].v2 = bv1; } else { console.log('not clockwise'); } } return triangles; }; /** * This function resets the winding order for triangles in faces, given an initial triangle and orientation edge * used pseudocode from * http://stackoverflow.com/questions/17036970/how-to-correct-winding-of-triangles-to-counter-clockwise-direction-of-a-3d-mesh * We need to use a graph traversal algorithm, * lets assume we have method that returns neighbor of triangle on given edge * * neighbor_on_egde( next_tria, edge ) * * to_process = set of pairs triangle and orientation edge, initial state is one good oriented triangle with any edge on it * processed = set of processed triangles; initial empty * * while to_process is not empty: * next_tria, orientation_edge = to_process.pop() * add next_tria in processed * if next_tria is not opposite oriented than orientation_edge: * change next_tria (ABC) orientation (B<->C) * for each edge (AB) in next_tria: * neighbor_tria = neighbor_on_egde( next_tria, edge ) * if neighbor_tria exists and neighbor_tria not in processed: * to_process add (neighbor_tria, edge opposite oriented (BA)) * @param faces R3.D3.Face[] * @param orientationEdge R3.API.Vector2 * @returns {Array} */ static FixWindingOrder(faces, orientationEdge) { /** * Checks if a Face belonging to a TriangleEdge has already been processed * @param processed TriangleEdge[] * @param triangle Face * @returns {boolean} */ function inProcessed(processed, triangle) { for (let i = 0; i < processed.length; i++) { if (processed[i].triangle.equals(triangle)) { return true; } } return false; } /** * Returns a neighbouring triangle on a specific edge - preserving the edge orientation * @param edge R3.API.Vector2 * @param faces R3.D3.Face[] * @param currentTriangle * @returns {*} */ function neighbourOnEdge(edge, faces, currentTriangle) { for (let i = 0; i < faces.length; i++) { if ( (faces[i].v0 === edge.x && faces[i].v1 === edge.y) || (faces[i].v1 === edge.x && faces[i].v2 === edge.y) || (faces[i].v2 === edge.x && faces[i].v0 === edge.y) || (faces[i].v0 === edge.y && faces[i].v1 === edge.x) || (faces[i].v1 === edge.y && faces[i].v2 === edge.x) || (faces[i].v2 === edge.y && faces[i].v0 === edge.x) ) { let triangle = new R3.D3.API.Face( null, null, faces[i].v0index, faces[i].v1index, faces[i].v2index, faces[i].materialIndex, faces[i].uvs ); if (triangle.equals(currentTriangle)) { continue; } return new R3.D3.TriangleEdge( triangle, edge ); } } return null; } let toProcess = [ new R3.D3.TriangleEdge( new R3.D3.API.Face( null, null, faces[0].v0index, faces[0].v1index, faces[0].v2index, faces[0].materialIndex, faces[0].uvs ), orientationEdge ) ]; let processed = []; while (toProcess.length > 0) { let triangleEdge = toProcess.pop(); /** * If edge is the same orientation (i.e. the edge order is the same as the given triangle edge) it needs to be reversed * to have the same winding order) */ if ( (triangleEdge.triangle.v0index === triangleEdge.edge.x && triangleEdge.triangle.v1index === triangleEdge.edge.y) || (triangleEdge.triangle.v1index === triangleEdge.edge.x && triangleEdge.triangle.v2index === triangleEdge.edge.y) || (triangleEdge.triangle.v2index === triangleEdge.edge.x && triangleEdge.triangle.v0index === triangleEdge.edge.y) ) { let backupV = triangleEdge.triangle.v1index; triangleEdge.triangle.v1index = triangleEdge.triangle.v2index; triangleEdge.triangle.v2index = backupV; // let backupUV = triangleEdge.triangle.v1uv; // triangleEdge.triangle.v1uv = triangleEdge.triangle.v2uv; // triangleEdge.triangle.v2uv = backupUV; // let backupUV = triangleEdge.triangle.uvs[0][1]; triangleEdge.triangle.uvs[0][1] = triangleEdge.triangle.uvs[0][2]; triangleEdge.triangle.uvs[0][2] = backupUV; } processed.push(triangleEdge); let edges = [ new R3.API.Vector2( triangleEdge.triangle.v0index, triangleEdge.triangle.v1index ), new R3.API.Vector2( triangleEdge.triangle.v1index, triangleEdge.triangle.v2index ), new R3.API.Vector2( triangleEdge.triangle.v2index, triangleEdge.triangle.v0index ) ]; for (let j = 0; j < edges.length; j++) { let neighbour = neighbourOnEdge(edges[j], faces, triangleEdge.triangle); if (neighbour && !inProcessed(processed, neighbour.triangle)) { toProcess.push(neighbour); } } } /** * In processed - we will have some duplicates - only add the unique ones * @type {Array} */ let triangles = []; for (let i = 0; i < processed.length; i++) { let found = false; for (let k = 0; k < triangles.length; k++) { if (triangles[k].equals(processed[i].triangle)){ found = true; break; } } if (!found) { triangles.push(processed[i].triangle); } } return triangles; }; /** * This is a work-around function to fix polys which don't triangulate because * they could lie on Z-plane (XZ or YZ)) - we translate the poly to the origin, systematically rotate the poly around * Z then Y axis * @param verticesFlat [] * @param grain is the amount to systematically rotate the poly by - a finer grain means a more accurate maximum XY * @return [] */ static FixPolyZPlane(verticesFlat, grain) { if ((verticesFlat.length % 3) !== 0 && !(verticesFlat.length > 9)) { console.log("The vertices are not in the right length : " + verticesFlat.length); } let vertices = []; let points = new R3.API.Quaternion.Points(); for (let i = 0; i < verticesFlat.length; i += 3) { points.add(new R3.API.Vector3( verticesFlat[i], verticesFlat[i + 1], verticesFlat[i + 2] )); } points.toOrigin(); points.maximizeXDistance(grain); points.maximizeYDistance(grain); for (i = 0; i < points.vectors.length; i++) { vertices.push( [ points.vectors[i].x, points.vectors[i].y ] ); } return vertices; }; static MovingAverage(period) { let nums = []; return function(num) { nums.push(num); if (nums.length > period) nums.splice(0,1); // remove the first element of the array let sum = 0; for (let i in nums) sum += nums[i]; let n = period; if (nums.length < period) n = nums.length; return(sum/n); } }; static Intersect(a, b) { let t; /** * Loop over shortest array */ if (b.length > a.length) { t = b; b = a; a = t; } return a.filter( /** * Check if exists * @param e * @returns {boolean} */ function(e) { return (b.indexOf(e) > -1); } ).filter( /** * Remove Duplicates * @param e * @param i * @param c * @returns {boolean} */ function(e, i, c) { return c.indexOf(e) === i; } ); }; static Difference(a, b) { let t; /** * Loop over shortest array */ if (b.length > a.length) { t = b; b = a; a = t; } return a.filter( /** * Check if exists * @param e * @returns {boolean} */ function(e) { return (b.indexOf(e) === -1); } ).filter( /** * Remove Duplicates * @param e * @param i * @param c * @returns {boolean} */ function(e, i, c) { return c.indexOf(e) === i; } ); }; /** * Push only if not in there already * @param array * @param object * @constructor */ static PushUnique(array, object) { if (array.indexOf(object) === -1) { array.push(object); } }; static RemoveFromArray(array, object) { let index = array.indexOf(object); if (index !== -1) { array.splice(index, 1); } } /** * Checks whether or not the object is empty * @param obj * @returns {boolean} * @constructor */ static IsEmpty(obj) { return (Object.keys(obj).length === 0 && obj.constructor === Object); }; static IsString(member) { return (typeof member === 'string'); }; static IsBoolean(member) { return (member === true || member === false); }; static IsColor(member) { return (member instanceof R3.Color); }; static IsNumber(member) { return (typeof member === 'number'); }; static IsVector2(member) { return ( member instanceof R3.API.Vector2 || member instanceof R3.Vector2 ); }; static IsVector3(member) { return ( member instanceof R3.API.Vector3 || member instanceof R3.Vector3 ); }; static IsVector4(member) { return ( member instanceof R3.API.Vector4 || member instanceof R3.Vector4 || member instanceof R3.API.Quaternion || member instanceof R3.Quaternion ); }; static IsObject(member) { let type = typeof member; return type === 'function' || type === 'object' && !!member; }; /** * @return {string} */ static LowerUnderscore(name) { let string = name.toLowerCase().replace(/\s+/g, '_'); string = string.replace(/-/g, '_'); string = string.replace(/\_+/g, '_'); return string; }; static UpperCaseWordsSpaces(input) { let word = input.replace(/[-_]/g, ' '); word = word.replace(/\s+/, ' '); let words = word.split(' '); return words.reduce( function(result, word) { result += word[0].toUpperCase() + word.substr(1); return result + ' '; }, '' ).trim(); }; /** * @return {string} */ static UpperCaseUnderscore(word) { let str = ''; word.split('').map( function(letter){ if (letter === letter.toUpperCase()) { str += '_' + letter; } else { str += letter.toUpperCase(); } }); str = str.replace(new RegExp('^_'),''); return str; }; /** * Returns Left Padded Text - ex. length 5, padchar 0, string abc = '00abc' * @param length * @param padChar * @param string * @returns {string} * @constructor */ static PaddedText(length, padChar, string) { let pad = ""; for (let x = 0; x < length; x++) { pad += padChar; } return pad.substring(0, pad.length - string.length) + string; }; } console.log('r3.js - version ' + R3.Version + ' compiled ' + R3.CompileDate); Component.Code = ComponentCode; Component.DOM = ComponentDOM; Component.DOM.Canvas = ComponentCanvas; Component.Geometry = ComponentGeometry; Component.Geometry.BufferGeometry = ComponentBufferGeometry; Component.Geometry.BufferGeometry.PlaneGeometry = ComponentPlaneGeometry; Component.Graphics = ComponentGraphics; Component.Graphics.Image = ComponentImage; Component.Input = ComponentInput; Component.Input.Touch = ComponentTouch; Component.Material = ComponentMaterial; Component.Mesh = ComponentMesh; Component.Texture = ComponentTexture; Entity.Slider = EntitySlider; R3Object.Project = Project; R3Object.Entity = Entity; R3Object.Entity.Slider = EntitySlider; R3Object.Component = Component; R3Object.Component.Code = ComponentCode; R3Object.Component.DOM = ComponentDOM; R3Object.Component.DOM.Canvas = ComponentCanvas; R3Object.Component.Geometry = ComponentGeometry; R3Object.Component.Geometry.BufferGeometry = ComponentBufferGeometry; R3Object.Component.Geometry.BufferGeometry.PlaneGeometry = ComponentPlaneGeometry; R3Object.Component.Graphics = ComponentGraphics; R3Object.Component.Graphics.Image = ComponentImage; R3Object.Component.Input = ComponentInput; R3Object.Component.Input.Touch = ComponentTouch; R3Object.Component.Material = ComponentMaterial; R3Object.Component.Mesh = ComponentMesh; R3Object.Component.Texture = ComponentTexture; Runtime.Coder = RuntimeCoder; Runtime.Coder.CodeMirror = RuntimeCodeMirror; Runtime.DOM = RuntimeDOM; Runtime.DOM.Document = RuntimeDocument; Runtime.DOM.JsDOM = RuntimeJsDOM; Runtime.GUI = RuntimeGUI; Runtime.GUI.ControlKit = RuntimeControlKit; Runtime.Graphics = RuntimeGraphics; Runtime.Graphics.Three = RuntimeThree; Runtime.Image = RuntimeImage; Runtime.Image.NodeJSImage = RuntimeNodeJSImage; Runtime.Image.WebImage = RuntimeWebImage; Runtime.Physics = RuntimePhysics; Runtime.Physics.Bullet = RuntimeBullet; Runtime.Socket = RuntimeSocket; Runtime.Statistics = RuntimeStatistics; Runtime.Statistics.Stats = RuntimeStats; System.DOM = SystemDOM; System.Input = SystemInput; System.Linking = SystemLinking; System.Render = SystemRender; System.Runtime = SystemRuntime; System.Socket = SystemSocket; System.Storage = SystemStorage; R3.System = System; R3.Runtime = Runtime; R3.Event = Event; R3.Graph = Graph; R3.Node = Node; R3.Utils = Utils; R3.Object = R3Object; R3.Project = Project; R3.Entity = Entity; R3.Component = Component; R3.Code = R3Object.Component.Code; R3.Canvas = R3Object.Component.DOM.Canvas; R3.PlaneGeometry = R3Object.Component.Geometry.BufferGeometry.PlaneGeometry; R3.Image = R3Object.Component.Graphics.Image; R3.Touch = R3Object.Component.Input.Touch; R3.Material = R3Object.Component.Material; R3.Mesh = R3Object.Component.Mesh; R3.Texture = R3Object.Component.Texture; R3.System.DOM.Start(); R3.System.Input.Start(); R3.System.Linking.Start(); R3.System.Render.Start(); R3.System.Runtime.Start(); R3.System.Socket.Start(); R3.System.Storage.Start();