diff --git a/.r3_history b/.r3_history index 96b0c4d..8560676 100644 --- a/.r3_history +++ b/.r3_history @@ -1,8 +1,5 @@ -r3 create VectorApplierAgain extends Event -r3 create AnotherClass extends R3Object -r3 create OneMore extends R3Object -r3 create AgainMore extends R3Object -r3 create AgainMoreAgain extends R3Object -r3 create Event normal +r3 create Event normal r3 create R3Object extends Event -r3 create SomeTest extends Event +r3 create Utils normal +r3 create Component extends R3Object +r3 create Project extends R3Object diff --git a/dist/r3-node/r3-vector-applier-again.js b/dist/r3-node/r3-component.js similarity index 67% rename from dist/r3-node/r3-vector-applier-again.js rename to dist/r3-node/r3-component.js index fcbba54..201c0d4 100644 --- a/dist/r3-node/r3-vector-applier-again.js +++ b/dist/r3-node/r3-component.js @@ -1,12 +1,11 @@ +const Event = require('r3-event'); const Utils = require('r3-utils'); -const Event = require('r3-event.js'); +const R3Object = require('r3-r3-object.js'); /** OPTIONS_START - a=2.0 - b=3.0 - something='hello' + runtime='default' OPTIONS_END INSTANCE_OPTIONS_MAPPING_START @@ -16,12 +15,12 @@ const Event = require('r3-event.js'); LINKED_OBJECTS_END EXCLUDED_FROM_INSTANCE_OPTIONS_START - b + runtime EXCLUDED_FROM_INSTANCE_OPTIONS_END **/ -class VectorApplierAgain extends Event { +class Component extends R3Object { //CONSTRUCTOR_EXTENDS_TEMPLATE_START constructor(options) { @@ -30,22 +29,16 @@ class VectorApplierAgain extends Event { options = {}; } - options.id = Utils.RandomId(10); - options.name = 'VectorApplierAgain (' + options.id + ')'; - super(options); this.emit(Event.OBJECT_CREATED, this); //OPTIONS_INIT_START - if (Utils.UndefinedOrNull(options.a)) { - options.a = 2.0; + if (typeof options === 'undefined') { + options = {}; } - if (Utils.UndefinedOrNull(options.b)) { - options.b = 3.0; - } - if (Utils.UndefinedOrNull(options.something)) { - options.something = 'hello'; + if (Utils.UndefinedOrNull(options.runtime)) { + options.runtime = 'default'; } //OPTIONS_INIT_END @@ -55,6 +48,7 @@ class VectorApplierAgain extends Event { Object.assign(this, options); //CUSTOM_BEFORE_INIT_START + this.emit(Event.COMPONENT_INITIALIZED, this); //CUSTOM_BEFORE_INIT_END this.emit(Event.OBJECT_INITIALIZED, this); @@ -65,26 +59,21 @@ class VectorApplierAgain extends Event { createInstance() { //CREATE_INSTANCE_BEFORE_START - super.createInstance(); - this.emit(Event.CREATE_INSTANCE, this); - - if (this.runtime === 'graphics') { - this.graphics.createInstance( - { - //CREATE_INSTANCE_OPTIONS_START - 'a': this.a, - 'something': this.something - //CREATE_INSTANCE_OPTIONS_END - }, - this - ) - } + this.emit(Event.CREATE_INSTANCE_BEFORE, this); //CREATE_INSTANCE_BEFORE_END //CUSTOM_CREATE_INSTANCE_START //CUSTOM_CREATE_INSTANCE_END //CREATE_INSTANCE_AFTER_START + this[this.runtime].createInstance( + this, + { + //CREATE_INSTANCE_OPTIONS_START + //CREATE_INSTANCE_OPTIONS_END + } + ) + this.emit(Event.INSTANCE_CREATED, this); //CREATE_INSTANCE_AFTER_END @@ -99,14 +88,6 @@ class VectorApplierAgain extends Event { //UPDATE_INSTANCE_BEFORE_END //UPDATE_INSTANCE_OPTIONS_START - if (property === 'a') { - this.instance.a = this.a; - return; - } - if (property === 'something') { - this.instance.something = this.something; - return; - } //UPDATE_INSTANCE_OPTIONS_END //CUSTOM_UPDATE_INSTANCE_START @@ -127,14 +108,6 @@ class VectorApplierAgain extends Event { //UPDATE_FROM_INSTANCE_BEFORE_END //UPDATE_FROM_INSTANCE_OPTIONS_START - if (property === 'a') { - this.a = this.instance.a; - return; - } - if (property === 'something') { - this.something = this.instance.something; - return; - } //UPDATE_FROM_INSTANCE_OPTIONS_END //CUSTOM_UPDATE_FROM_INSTANCE_START @@ -151,14 +124,21 @@ class VectorApplierAgain extends Event { dispose() { //DISPOSE_BEFORE_START - this.emit(Event.DISPOSE_OBJECT, this); + this.subscribe( + Event.INSTANCE_DISPOSED, + function(object) { + if (object === this) { + this.emit(Event.DISPOSE_OBJECT, this); + } + } + ); //DISPOSE_BEFORE_END //CUSTOM_DISPOSE_START //CUSTOM_DISPOSE_END //DISPOSE_AFTER_START - this.emit(Event.OBJECT_DISPOSED, this); + this.disposeInstance(); //DISPOSE_AFTER_END } @@ -168,15 +148,14 @@ class VectorApplierAgain extends Event { disposeInstance() { //DISPOSE_INSTANCE_BEFORE_START - super.disposeInstance(); - this.emit(Event.DISPOSE_INSTANCE, this); + console.log('Disposing instance of ' + this.name); //DISPOSE_INSTANCE_BEFORE_END //CUSTOM_DISPOSE_INSTANCE_START //CUSTOM_DISPOSE_INSTANCE_END //DISPOSE_INSTANCE_AFTER_START - this.emit(Event.INSTANCE_DISPOSED, this); + this.emit(Event.DISPOSE_INSTANCE, this); //DISPOSE_INSTANCE_AFTER_END } @@ -190,4 +169,4 @@ class VectorApplierAgain extends Event { //CUSTOM_OUT_OF_CLASS_IMPLEMENTATION_START //CUSTOM_OUT_OF_CLASS_IMPLEMENTATION_END -module.exports = VectorApplierAgain; \ No newline at end of file +module.exports = Component; \ No newline at end of file diff --git a/dist/r3-node/r3-event.js b/dist/r3-node/r3-event.js index 01f62bd..de8d73d 100644 --- a/dist/r3-node/r3-event.js +++ b/dist/r3-node/r3-event.js @@ -1,4 +1,3 @@ -const Event = require('r3-event'); const Utils = require('r3-utils'); /** @@ -6,15 +5,6 @@ const Utils = require('r3-utils'); OPTIONS_START OPTIONS_END - INSTANCE_OPTIONS_MAPPING_START - INSTANCE_OPTIONS_MAPPING_END - - LINKED_OBJECTS_START - LINKED_OBJECTS_END - - EXCLUDED_FROM_INSTANCE_OPTIONS_START - EXCLUDED_FROM_INSTANCE_OPTIONS_END - **/ class Event { @@ -22,16 +12,12 @@ class Event { //CONSTRUCTOR_TEMPLATE_START constructor(options) { - if (Utils.UndefinedOrNull(options)) { - options = {}; - } - - options.id = Utils.RandomId(10); - options.name = 'Event (' + options.id + ')'; - Event.Emit(Event.OBJECT_CREATED, this); //OPTIONS_INIT_START + if (typeof options === 'undefined') { + options = {}; + } //OPTIONS_INIT_END //CUSTOM_OPTIONS_INIT_START @@ -46,110 +32,6 @@ class Event { } //CONSTRUCTOR_TEMPLATE_END - //CREATE_INSTANCE_TEMPLATE_START - createInstance() { - - //CREATE_INSTANCE_BEFORE_START - super.createInstance(); - this.emit(Event.CREATE_INSTANCE, this); - - if (this.runtime === 'graphics') { - this.graphics.createInstance( - { - //CREATE_INSTANCE_OPTIONS_START - - //CREATE_INSTANCE_OPTIONS_END - }, - this - ) - } - //CREATE_INSTANCE_BEFORE_END - - //CUSTOM_CREATE_INSTANCE_START - //CUSTOM_CREATE_INSTANCE_END - - //CREATE_INSTANCE_AFTER_START - this.emit(Event.INSTANCE_CREATED, this); - //CREATE_INSTANCE_AFTER_END - - } - //CREATE_INSTANCE_TEMPLATE_END - - //UPDATE_INSTANCE_TEMPLATE_START - updateInstance(property) { - - //UPDATE_INSTANCE_BEFORE_START - this.emit(Event.UPDATE_INSTANCE_BEFORE, this); - //UPDATE_INSTANCE_BEFORE_END - - //UPDATE_INSTANCE_OPTIONS_START - //UPDATE_INSTANCE_OPTIONS_END - - //CUSTOM_UPDATE_INSTANCE_START - //CUSTOM_UPDATE_INSTANCE_END - - //UPDATE_INSTANCE_AFTER_START - this.emit(Event.UPDATE_INSTANCE_AFTER, this); - //UPDATE_INSTANCE_AFTER_END - - } - //UPDATE_INSTANCE_TEMPLATE_END - - //UPDATE_FROM_INSTANCE_TEMPLATE_START - updateFromInstance(property) { - - //UPDATE_FROM_INSTANCE_BEFORE_START - this.emit(Event.UPDATE_FROM_INSTANCE_BEFORE, this); - //UPDATE_FROM_INSTANCE_BEFORE_END - - //UPDATE_FROM_INSTANCE_OPTIONS_START - //UPDATE_FROM_INSTANCE_OPTIONS_END - - //CUSTOM_UPDATE_FROM_INSTANCE_START - //CUSTOM_UPDATE_FROM_INSTANCE_END - - //UPDATE_FROM_INSTANCE_AFTER_START - this.emit(Event.UPDATE_FROM_INSTANCE_AFTER, this); - //UPDATE_FROM_INSTANCE_AFTER_END - - } - //UPDATE_FROM_INSTANCE_TEMPLATE_END - - //DISPOSE_TEMPLATE_START - dispose() { - - //DISPOSE_BEFORE_START - this.emit(Event.DISPOSE_OBJECT, this); - //DISPOSE_BEFORE_END - - //CUSTOM_DISPOSE_START - //CUSTOM_DISPOSE_END - - //DISPOSE_AFTER_START - this.emit(Event.OBJECT_DISPOSED, this); - //DISPOSE_AFTER_END - - } - //DISPOSE_TEMPLATE_END - - //DISPOSE_INSTANCE_TEMPLATE_START - disposeInstance() { - - //DISPOSE_INSTANCE_BEFORE_START - super.disposeInstance(); - this.emit(Event.DISPOSE_INSTANCE, this); - //DISPOSE_INSTANCE_BEFORE_END - - //CUSTOM_DISPOSE_INSTANCE_START - //CUSTOM_DISPOSE_INSTANCE_END - - //DISPOSE_INSTANCE_AFTER_START - this.emit(Event.INSTANCE_DISPOSED, this); - //DISPOSE_INSTANCE_AFTER_END - - } - //DISPOSE_INSTANCE_TEMPLATE_END - //CUSTOM_IMPLEMENTATION_START /** @@ -317,15 +199,15 @@ class Event { //CUSTOM_OUT_OF_CLASS_IMPLEMENTATION_START //EVENT_GENERATED_START -Event.CREATE_INSTANCE = 0x1; -Event.DISPOSE_INSTANCE = 0x2; -Event.DISPOSE_OBJECT = 0x3; -Event.GET_RUNTIME = 0x4; -Event.GET_WINDOW_SIZE = 0x5; -Event.INSTANCE_CREATED = 0x6; -Event.INSTANCE_DISPOSED = 0x7; -Event.OBJECT_CREATED = 0x8; -Event.OBJECT_DISPOSED = 0x9; +Event.COMPONENT_INITIALIZED = 0x1; +Event.CREATE_INSTANCE_BEFORE = 0x2; +Event.DISPOSE_INSTANCE = 0x3; +Event.DISPOSE_OBJECT = 0x4; +Event.GET_RUNTIME = 0x5; +Event.GET_WINDOW_SIZE = 0x6; +Event.INSTANCE_CREATED = 0x7; +Event.INSTANCE_DISPOSED = 0x8; +Event.OBJECT_CREATED = 0x9; Event.OBJECT_INITIALIZED = 0xa; Event.PAUSE = 0xb; Event.RESTART = 0xc; @@ -339,15 +221,15 @@ Event.MAX_EVENTS = 0x12; Event.GetEventName = function(eventId) { switch(eventId) { - case 0x1 : return 'create_instance'; - case 0x2 : return 'dispose_instance'; - case 0x3 : return 'dispose_object'; - case 0x4 : return 'get_runtime'; - case 0x5 : return 'get_window_size'; - case 0x6 : return 'instance_created'; - case 0x7 : return 'instance_disposed'; - case 0x8 : return 'object_created'; - case 0x9 : return 'object_disposed'; + case 0x1 : return 'component_initialized'; + case 0x2 : return 'create_instance_before'; + case 0x3 : return 'dispose_instance'; + case 0x4 : return 'dispose_object'; + case 0x5 : return 'get_runtime'; + case 0x6 : return 'get_window_size'; + case 0x7 : return 'instance_created'; + case 0x8 : return 'instance_disposed'; + case 0x9 : return 'object_created'; case 0xa : return 'object_initialized'; case 0xb : return 'pause'; case 0xc : return 'restart'; diff --git a/dist/r3-node/r3-another-class.js b/dist/r3-node/r3-project.js similarity index 62% rename from dist/r3-node/r3-another-class.js rename to dist/r3-node/r3-project.js index 69204d8..87ff87f 100644 --- a/dist/r3-node/r3-another-class.js +++ b/dist/r3-node/r3-project.js @@ -5,28 +5,26 @@ const R3Object = require('r3-r3-object.js'); /** OPTIONS_START - x=1 - y=2 - z=3 - register=true + isPublic=true + applicationMode=Project.APPLICATION_MODE_EDIT OPTIONS_END INSTANCE_OPTIONS_MAPPING_START - x=side.x - y=side.y - z=side.z INSTANCE_OPTIONS_MAPPING_END LINKED_OBJECTS_START + entities=[Entity] + controls=[Control] + images=[Image] + code=[CustomCode] LINKED_OBJECTS_END EXCLUDED_FROM_INSTANCE_OPTIONS_START - register EXCLUDED_FROM_INSTANCE_OPTIONS_END **/ -class AnotherClass extends R3Object { +class Project extends R3Object { //CONSTRUCTOR_EXTENDS_TEMPLATE_START constructor(options) { @@ -35,25 +33,19 @@ class AnotherClass extends R3Object { options = {}; } - options.id = Utils.RandomId(10); - options.name = 'AnotherClass (' + options.id + ')'; - super(options); this.emit(Event.OBJECT_CREATED, this); //OPTIONS_INIT_START - if (Utils.UndefinedOrNull(options.x)) { - options.x = 1; + if (typeof options === 'undefined') { + options = {}; } - if (Utils.UndefinedOrNull(options.y)) { - options.y = 2; + if (Utils.UndefinedOrNull(options.isPublic)) { + options.isPublic = true; } - if (Utils.UndefinedOrNull(options.z)) { - options.z = 3; - } - if (Utils.UndefinedOrNull(options.register)) { - options.register = true; + if (Utils.UndefinedOrNull(options.applicationMode)) { + options.applicationMode = Project.APPLICATION_MODE_EDIT; } //OPTIONS_INIT_END @@ -73,27 +65,23 @@ class AnotherClass extends R3Object { createInstance() { //CREATE_INSTANCE_BEFORE_START - super.createInstance(); - this.emit(Event.CREATE_INSTANCE, this); - - if (this.runtime === 'graphics') { - this.graphics.createInstance( - { - //CREATE_INSTANCE_OPTIONS_START - 'side.x': this.x, - 'side.y': this.y, - 'side.z': this.z - //CREATE_INSTANCE_OPTIONS_END - }, - this - ) - } + this.emit(Event.CREATE_INSTANCE_BEFORE, this); //CREATE_INSTANCE_BEFORE_END //CUSTOM_CREATE_INSTANCE_START //CUSTOM_CREATE_INSTANCE_END //CREATE_INSTANCE_AFTER_START + this[this.runtime].createInstance( + this, + { + //CREATE_INSTANCE_OPTIONS_START + 'isPublic': this.isPublic, + 'applicationMode': this.applicationMode + //CREATE_INSTANCE_OPTIONS_END + } + ) + this.emit(Event.INSTANCE_CREATED, this); //CREATE_INSTANCE_AFTER_END @@ -108,16 +96,12 @@ class AnotherClass extends R3Object { //UPDATE_INSTANCE_BEFORE_END //UPDATE_INSTANCE_OPTIONS_START - if (property === 'x') { - this.instance.side.x = this.x; + if (property === 'isPublic') { + this.instance.isPublic = this.isPublic; return; } - if (property === 'y') { - this.instance.side.y = this.y; - return; - } - if (property === 'z') { - this.instance.side.z = this.z; + if (property === 'applicationMode') { + this.instance.applicationMode = this.applicationMode; return; } //UPDATE_INSTANCE_OPTIONS_END @@ -140,17 +124,17 @@ class AnotherClass extends R3Object { //UPDATE_FROM_INSTANCE_BEFORE_END //UPDATE_FROM_INSTANCE_OPTIONS_START - if (property === 'x') { - this.x = this.instance.side.x; - return; + if (property === 'isPublic' || property === 'all') { + this.isPublic = this.instance.isPublic; + if (property !== 'all') { + return; + } } - if (property === 'y') { - this.y = this.instance.side.y; - return; - } - if (property === 'z') { - this.z = this.instance.side.z; - return; + if (property === 'applicationMode' || property === 'all') { + this.applicationMode = this.instance.applicationMode; + if (property !== 'all') { + return; + } } //UPDATE_FROM_INSTANCE_OPTIONS_END @@ -168,14 +152,21 @@ class AnotherClass extends R3Object { dispose() { //DISPOSE_BEFORE_START - this.emit(Event.DISPOSE_OBJECT, this); + this.subscribe( + Event.INSTANCE_DISPOSED, + function(object) { + if (object === this) { + this.emit(Event.DISPOSE_OBJECT, this); + } + } + ); //DISPOSE_BEFORE_END //CUSTOM_DISPOSE_START //CUSTOM_DISPOSE_END //DISPOSE_AFTER_START - this.emit(Event.OBJECT_DISPOSED, this); + this.disposeInstance(); //DISPOSE_AFTER_END } @@ -185,15 +176,14 @@ class AnotherClass extends R3Object { disposeInstance() { //DISPOSE_INSTANCE_BEFORE_START - super.disposeInstance(); - this.emit(Event.DISPOSE_INSTANCE, this); + console.log('Disposing instance of ' + this.name); //DISPOSE_INSTANCE_BEFORE_END //CUSTOM_DISPOSE_INSTANCE_START //CUSTOM_DISPOSE_INSTANCE_END //DISPOSE_INSTANCE_AFTER_START - this.emit(Event.INSTANCE_DISPOSED, this); + this.emit(Event.DISPOSE_INSTANCE, this); //DISPOSE_INSTANCE_AFTER_END } @@ -205,6 +195,15 @@ class AnotherClass extends R3Object { } //CUSTOM_OUT_OF_CLASS_IMPLEMENTATION_START +Project.CAMERA_INDEX_EDIT = 0x0; +Project.CAMERA_INDEX_RUN = 0x1; + +Project.APPLICATION_MODE_EDIT = Project.CAMERA_INDEX_EDIT; +Project.APPLICATION_MODE_RUN = Project.CAMERA_INDEX_RUN; + +Project.RENDERER_INDEX_MAIN = 0x0; + +Project.RENDER_TARGET_INDEX_NONE = -0x1; //CUSTOM_OUT_OF_CLASS_IMPLEMENTATION_END -module.exports = AnotherClass; \ No newline at end of file +module.exports = Project; \ No newline at end of file diff --git a/dist/r3-node/r3-r3-object.js b/dist/r3-node/r3-r3-object.js index e181874..9dc48aa 100644 --- a/dist/r3-node/r3-r3-object.js +++ b/dist/r3-node/r3-r3-object.js @@ -4,6 +4,9 @@ const Event = require('r3-event.js'); /** OPTIONS_START + id=Utils.RandomId(10) + name=this.constructor.name + '(' + options.id + ')' + register=true OPTIONS_END INSTANCE_OPTIONS_MAPPING_START @@ -13,6 +16,9 @@ const Event = require('r3-event.js'); LINKED_OBJECTS_END EXCLUDED_FROM_INSTANCE_OPTIONS_START + id + name + register EXCLUDED_FROM_INSTANCE_OPTIONS_END **/ @@ -26,14 +32,23 @@ class R3Object extends Event { options = {}; } - options.id = Utils.RandomId(10); - options.name = 'R3Object (' + options.id + ')'; - super(options); this.emit(Event.OBJECT_CREATED, this); //OPTIONS_INIT_START + if (typeof options === 'undefined') { + options = {}; + } + if (Utils.UndefinedOrNull(options.id)) { + options.id = Utils.RandomId(10); + } + if (Utils.UndefinedOrNull(options.name)) { + options.name = this.constructor.name + '(' + options.id + ')'; + } + if (Utils.UndefinedOrNull(options.register)) { + options.register = true; + } //OPTIONS_INIT_END //CUSTOM_OPTIONS_INIT_START @@ -52,25 +67,21 @@ class R3Object extends Event { createInstance() { //CREATE_INSTANCE_BEFORE_START - super.createInstance(); - this.emit(Event.CREATE_INSTANCE, this); - - if (this.runtime === 'graphics') { - this.graphics.createInstance( - { - //CREATE_INSTANCE_OPTIONS_START - - //CREATE_INSTANCE_OPTIONS_END - }, - this - ) - } + this.emit(Event.CREATE_INSTANCE_BEFORE, this); //CREATE_INSTANCE_BEFORE_END //CUSTOM_CREATE_INSTANCE_START //CUSTOM_CREATE_INSTANCE_END //CREATE_INSTANCE_AFTER_START + this[this.runtime].createInstance( + this, + { + //CREATE_INSTANCE_OPTIONS_START + //CREATE_INSTANCE_OPTIONS_END + } + ) + this.emit(Event.INSTANCE_CREATED, this); //CREATE_INSTANCE_AFTER_END @@ -121,14 +132,21 @@ class R3Object extends Event { dispose() { //DISPOSE_BEFORE_START - this.emit(Event.DISPOSE_OBJECT, this); + this.subscribe( + Event.INSTANCE_DISPOSED, + function(object) { + if (object === this) { + this.emit(Event.DISPOSE_OBJECT, this); + } + } + ); //DISPOSE_BEFORE_END //CUSTOM_DISPOSE_START //CUSTOM_DISPOSE_END //DISPOSE_AFTER_START - this.emit(Event.OBJECT_DISPOSED, this); + this.disposeInstance(); //DISPOSE_AFTER_END } @@ -138,15 +156,14 @@ class R3Object extends Event { disposeInstance() { //DISPOSE_INSTANCE_BEFORE_START - super.disposeInstance(); - this.emit(Event.DISPOSE_INSTANCE, this); + console.log('Disposing instance of ' + this.name); //DISPOSE_INSTANCE_BEFORE_END //CUSTOM_DISPOSE_INSTANCE_START //CUSTOM_DISPOSE_INSTANCE_END //DISPOSE_INSTANCE_AFTER_START - this.emit(Event.INSTANCE_DISPOSED, this); + this.emit(Event.DISPOSE_INSTANCE, this); //DISPOSE_INSTANCE_AFTER_END } diff --git a/dist/r3-node/r3-utils.js b/dist/r3-node/r3-utils.js index b3a06e7..250ffeb 100644 --- a/dist/r3-node/r3-utils.js +++ b/dist/r3-node/r3-utils.js @@ -1,1247 +1,1275 @@ +const Event = require('r3-event'); + +/** + + OPTIONS_START + OPTIONS_END + + **/ + class Utils { - constructor() { - - } - - /** - * Gets the first parent of the object which is an instance of constructor - * @param object - * @param constructor - * @returns {*} - * @constructor - */ - 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 ObjectPropertiesAsBoolean(object) { - return Object.keys(object).reduce( - function(result, propertyId) { - - if (typeof object[propertyId] === 'function') { - return result; - } - - result[propertyId] = false; - - // if (object[propertyId] instanceof R3.Vector2) { - // Utils.BuildVectorSource(result, propertyId, 2); - // } - // - // if (object[propertyId] instanceof R3.Vector3) { - // Utils.BuildVectorSource(result, propertyId, 3); - // } - // - // if (object[propertyId] instanceof R3.Vector4) { - // Utils.BuildVectorSource(result, propertyId, 4); - // } - // - // if (object[propertyId] instanceof R3.Quaternion) { - // Utils.BuildQuaternionSource(result, propertyId); - // } - - return result; - - }.bind(this), - {} - ); - }; - - 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 - ); - }; - - static LoadIdsFromArrayToIdObject(array, idToObject) { - - }; - - static LoadIdsFromObjectToIdObject(object, idToObject) { - - - }; - - /** - * 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*(_?)(.+?)\1\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 - }); - } - }; - - /** - * Links an object to its parent through idToObject array - * @param propertyString - * @param idToObject - * @param parentObject - * @param id - * @constructor - */ - static Link(propertyString, idToObject, parentObject, id) { - - if (!Utils.UndefinedOrNull(parentObject[propertyString])) { - - if (!idToObject.hasOwnProperty(id)) { - console.warn('Linking failed for object:' + parentObject.name); - } - - parentObject[propertyString] = idToObject[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); - } - }; - - /** - * 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; - }; + //CONSTRUCTOR_TEMPLATE_START + constructor(options) { + + Event.Emit(Event.OBJECT_CREATED, this); + + //OPTIONS_INIT_START + if (typeof options === 'undefined') { + options = {}; + } + //OPTIONS_INIT_END + + //CUSTOM_OPTIONS_INIT_START + //CUSTOM_OPTIONS_INIT_END + + Object.assign(this, options); + + //CUSTOM_BEFORE_INIT_START + //CUSTOM_BEFORE_INIT_END + + Event.Emit(Event.OBJECT_INITIALIZED, this); + } + //CONSTRUCTOR_TEMPLATE_END + + //CUSTOM_IMPLEMENTATION_START + 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 ObjectPropertiesAsBoolean(object) { + return Object.keys(object).reduce( + function(result, propertyId) { + + if (typeof object[propertyId] === 'function') { + return result; + } + + result[propertyId] = false; + + // if (object[propertyId] instanceof R3.Vector2) { + // Utils.BuildVectorSource(result, propertyId, 2); + // } + // + // if (object[propertyId] instanceof R3.Vector3) { + // Utils.BuildVectorSource(result, propertyId, 3); + // } + // + // if (object[propertyId] instanceof R3.Vector4) { + // Utils.BuildVectorSource(result, propertyId, 4); + // } + // + // if (object[propertyId] instanceof R3.Quaternion) { + // Utils.BuildQuaternionSource(result, propertyId); + // } + + return result; + + }.bind(this), + {} + ); + }; + + 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 + ); + }; + + static LoadIdsFromArrayToIdObject(array, idToObject) { + + }; + + static LoadIdsFromObjectToIdObject(object, idToObject) { + + + }; + + /** + * 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*(_?)(.+?)\1\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 + }); + } + }; + + /** + * Links an object to its parent through idToObject array + * @param propertyString + * @param idToObject + * @param parentObject + * @param id + * @constructor + */ + static Link(propertyString, idToObject, parentObject, id) { + + if (!Utils.UndefinedOrNull(parentObject[propertyString])) { + + if (!idToObject.hasOwnProperty(id)) { + console.warn('Linking failed for object:' + parentObject.name); + } + + parentObject[propertyString] = idToObject[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); + } + }; + + /** + * 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; + }; + //CUSTOM_IMPLEMENTATION_END + } +//CUSTOM_OUT_OF_CLASS_IMPLEMENTATION_START +//CUSTOM_OUT_OF_CLASS_IMPLEMENTATION_END + module.exports = Utils; \ No newline at end of file diff --git a/dist/r3-node/r3.js b/dist/r3-node/r3.js index 0557603..3abc49a 100644 --- a/dist/r3-node/r3.js +++ b/dist/r3-node/r3.js @@ -7,7 +7,7 @@ class R3 { } static version() { - return 'Sun Jun 20 2021 11:02:42 GMT+0000 (Coordinated Universal Time)'; + return 'Sun Jun 20 2021 18:45:42 GMT+0000 (Coordinated Universal Time)'; } } diff --git a/dist/r3.js b/dist/r3.js index 8acdea3..c34c9e6 100644 --- a/dist/r3.js +++ b/dist/r3.js @@ -106,9 +106,266 @@ module.exports = R3; !*** ./src/r3/r3-event.js ***! \****************************/ /*! no static exports found */ -/***/ (function(module, exports) { +/***/ (function(module, exports, __webpack_require__) { -throw new Error("Module build failed (from ./node_modules/babel-loader/lib/index.js):\nSyntaxError: /app/src/r3/r3-event.js: Identifier 'Event' has already been declared. (20:6)\n\n 18 | **/\n 19 |\n> 20 | class Event {\n | ^\n 21 |\n 22 | //CONSTRUCTOR_TEMPLATE_START\n 23 | constructor(options) {\n at Parser._raise (/app/node_modules/@babel/parser/lib/index.js:816:17)\n at Parser.raiseWithData (/app/node_modules/@babel/parser/lib/index.js:809:17)\n at Parser.raise (/app/node_modules/@babel/parser/lib/index.js:770:17)\n at ScopeHandler.checkRedeclarationInScope (/app/node_modules/@babel/parser/lib/index.js:1432:12)\n at ScopeHandler.declareName (/app/node_modules/@babel/parser/lib/index.js:1398:12)\n at Parser.checkLVal (/app/node_modules/@babel/parser/lib/index.js:10431:24)\n at Parser.parseClassId (/app/node_modules/@babel/parser/lib/index.js:13617:14)\n at Parser.parseClass (/app/node_modules/@babel/parser/lib/index.js:13301:10)\n at Parser.parseStatementContent (/app/node_modules/@babel/parser/lib/index.js:12587:21)\n at Parser.parseStatement (/app/node_modules/@babel/parser/lib/index.js:12545:17)"); +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } + +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +var Utils = __webpack_require__(!(function webpackMissingModule() { var e = new Error("Cannot find module 'r3-utils'"); e.code = 'MODULE_NOT_FOUND'; throw e; }())); +/** + + OPTIONS_START + OPTIONS_END + + **/ + + +var Event = /*#__PURE__*/function () { + //CONSTRUCTOR_TEMPLATE_START + function Event(options) { + _classCallCheck(this, Event); + + Event.Emit(Event.OBJECT_CREATED, this); //OPTIONS_INIT_START + + if (typeof options === 'undefined') { + options = {}; + } //OPTIONS_INIT_END + //CUSTOM_OPTIONS_INIT_START + //CUSTOM_OPTIONS_INIT_END + + + Object.assign(this, options); //CUSTOM_BEFORE_INIT_START + //CUSTOM_BEFORE_INIT_END + + Event.Emit(Event.OBJECT_INITIALIZED, this); + } //CONSTRUCTOR_TEMPLATE_END + //CUSTOM_IMPLEMENTATION_START + + /** + * Some nice Events handling + * @type {{}} + */ + + + _createClass(Event, [{ + key: "subscribe", + value: + /** + * Subscribe to some events + * @param eventName + * @param callback + */ + function subscribe(eventName, callback) { + return Event.Subscribe(eventName, callback.bind(this)); + } + }, { + key: "emit", + value: function emit(eventName, data, clientCallback, clientErrorCallback) { + return Event.Emit(eventName, data, clientCallback, clientErrorCallback); + } + /** + * Execute the functions which subscribe to this event, but don't process the client callback - the subscription function + * should execute the client callback + * @param eventId + * @param data + * @param clientCallback + * @param clientErrorCallback + * @returns {number} + * @constructor + */ + + }], [{ + key: "Subscribe", + value: function Subscribe(eventName, fn) { + /** + * Todo - maybe eventually store a boolean which indicates if the function has been executed + */ + var subscriptionId = Utils.RandomId(10); + + if (Event.Subscriptions.hasOwnProperty(eventName)) { + if (Event.Subscriptions[eventName][subscriptionId]) { + throw new Error('A component can only subscribe to a particular event ID once'); + } + + Event.Subscriptions[eventName][subscriptionId] = fn; + } else { + Event.Subscriptions[eventName] = {}; + Event.Subscriptions[eventName][subscriptionId] = fn; + } + /** + * Return a handle to the caller to allow us to unsubscribe to this event + */ + + + return { + fn: fn, + 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[]} + */ + + var listeners = Object.keys(Event.Subscriptions[eventId]); + + if (listeners.length === 0) { + delete Event.Subscriptions[eventId]; + } + }; + }(eventName, subscriptionId), + subscriptionId: subscriptionId + }; + } + }, { + key: "Emit", + value: + /** + * Static Synchronous Event - Calls clientCallback directly after the event result is obtained + * @param eventId + * @param data + * @param clientCallback is executed ideally when the event completed + * @param clientErrorCallback + * @returns {number} of callbacks executed + * @constructor + */ + function Emit(eventId, data, clientCallback, clientErrorCallback) { + if (Event.Subscriptions.hasOwnProperty(eventId)) { + var subscriptionIds = Object.keys(Event.Subscriptions[eventId]); + subscriptionIds.map(function (subscriptionId) { + try { + var result = Event.Subscriptions[eventId][subscriptionId](data); + + if (clientCallback) { + clientCallback(result); + } + } catch (error) { + if (clientErrorCallback) { + clientErrorCallback(error); + } else { + console.error(error); + throw error; + } + } + }); + } + } + }, { + key: "Async", + value: function Async(eventId, data, clientCallback, clientErrorCallback) { + if (Event.Subscriptions.hasOwnProperty(eventId)) { + var 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; + } + } + }); + } + } + }]); + + return Event; +}(); + +_defineProperty(Event, "Subscriptions", {}); + +//CUSTOM_OUT_OF_CLASS_IMPLEMENTATION_START +//EVENT_GENERATED_START +Event.COMPONENT_INITIALIZED = 0x1; +Event.CREATE_INSTANCE_BEFORE = 0x2; +Event.DISPOSE_INSTANCE = 0x3; +Event.DISPOSE_OBJECT = 0x4; +Event.GET_RUNTIME = 0x5; +Event.GET_WINDOW_SIZE = 0x6; +Event.INSTANCE_CREATED = 0x7; +Event.INSTANCE_DISPOSED = 0x8; +Event.OBJECT_CREATED = 0x9; +Event.OBJECT_INITIALIZED = 0xa; +Event.PAUSE = 0xb; +Event.RESTART = 0xc; +Event.START = 0xd; +Event.UPDATE_FROM_INSTANCE_AFTER = 0xe; +Event.UPDATE_FROM_INSTANCE_BEFORE = 0xf; +Event.UPDATE_INSTANCE_AFTER = 0x10; +Event.UPDATE_INSTANCE_BEFORE = 0x11; +Event.MAX_EVENTS = 0x12; + +Event.GetEventName = function (eventId) { + switch (eventId) { + case 0x1: + return 'component_initialized'; + + case 0x2: + return 'create_instance_before'; + + case 0x3: + return 'dispose_instance'; + + case 0x4: + return 'dispose_object'; + + case 0x5: + return 'get_runtime'; + + case 0x6: + return 'get_window_size'; + + case 0x7: + return 'instance_created'; + + case 0x8: + return 'instance_disposed'; + + case 0x9: + return 'object_created'; + + case 0xa: + return 'object_initialized'; + + case 0xb: + return 'pause'; + + case 0xc: + return 'restart'; + + case 0xd: + return 'start'; + + case 0xe: + return 'update_from_instance_after'; + + case 0xf: + return 'update_from_instance_before'; + + case 0x10: + return 'update_instance_after'; + + case 0x11: + return 'update_instance_before'; + + default: + throw new Error('Event type not defined : ' + eventId); + } +}; //EVENT_GENERATED_END +//CUSTOM_OUT_OF_CLASS_IMPLEMENTATION_END + + +module.exports = Event; /***/ }), @@ -127,10 +384,6 @@ function _defineProperties(target, props) { for (var i = 0; i < props.length; i+ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } -function _get(target, property, receiver) { if (typeof Reflect !== "undefined" && Reflect.get) { _get = Reflect.get; } else { _get = function _get(target, property, receiver) { var base = _superPropBase(target, property); if (!base) return; var desc = Object.getOwnPropertyDescriptor(base, property); if (desc.get) { return desc.get.call(receiver); } return desc.value; }; } return _get(target, property, receiver || target); } - -function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; } - function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } @@ -151,6 +404,9 @@ var Event = __webpack_require__(!(function webpackMissingModule() { var e = new /** OPTIONS_START + id=Utils.RandomId(10) + name=this.constructor.name + '(' + options.id + ')' + register=true OPTIONS_END INSTANCE_OPTIONS_MAPPING_START @@ -160,6 +416,9 @@ var Event = __webpack_require__(!(function webpackMissingModule() { var e = new LINKED_OBJECTS_END EXCLUDED_FROM_INSTANCE_OPTIONS_START + id + name + register EXCLUDED_FROM_INSTANCE_OPTIONS_END **/ @@ -180,12 +439,26 @@ var R3Object = /*#__PURE__*/function (_Event) { options = {}; } - options.id = Utils.RandomId(10); - options.name = 'R3Object (' + options.id + ')'; _this = _super.call(this, options); _this.emit(Event.OBJECT_CREATED, _assertThisInitialized(_this)); //OPTIONS_INIT_START - //OPTIONS_INIT_END + + + if (typeof options === 'undefined') { + options = {}; + } + + if (Utils.UndefinedOrNull(options.id)) { + options.id = Utils.RandomId(10); + } + + if (Utils.UndefinedOrNull(options.name)) { + options.name = _this.constructor.name + '(' + options.id + ')'; + } + + if (Utils.UndefinedOrNull(options.register)) { + options.register = true; + } //OPTIONS_INIT_END //CUSTOM_OPTIONS_INIT_START //CUSTOM_OPTIONS_INIT_END @@ -204,20 +477,14 @@ var R3Object = /*#__PURE__*/function (_Event) { key: "createInstance", value: function createInstance() { //CREATE_INSTANCE_BEFORE_START - _get(_getPrototypeOf(R3Object.prototype), "createInstance", this).call(this); - - this.emit(Event.CREATE_INSTANCE, this); - - if (this.runtime === 'graphics') { - this.graphics.createInstance({//CREATE_INSTANCE_OPTIONS_START - //CREATE_INSTANCE_OPTIONS_END - }, this); - } //CREATE_INSTANCE_BEFORE_END + this.emit(Event.CREATE_INSTANCE_BEFORE, this); //CREATE_INSTANCE_BEFORE_END //CUSTOM_CREATE_INSTANCE_START //CUSTOM_CREATE_INSTANCE_END //CREATE_INSTANCE_AFTER_START - + this[this.runtime].createInstance(this, {//CREATE_INSTANCE_OPTIONS_START + //CREATE_INSTANCE_OPTIONS_END + }); this.emit(Event.INSTANCE_CREATED, this); //CREATE_INSTANCE_AFTER_END } //CREATE_INSTANCE_TEMPLATE_END //UPDATE_INSTANCE_TEMPLATE_START @@ -256,12 +523,16 @@ var R3Object = /*#__PURE__*/function (_Event) { key: "dispose", value: function dispose() { //DISPOSE_BEFORE_START - this.emit(Event.DISPOSE_OBJECT, this); //DISPOSE_BEFORE_END + this.subscribe(Event.INSTANCE_DISPOSED, function (object) { + if (object === this) { + this.emit(Event.DISPOSE_OBJECT, this); + } + }); //DISPOSE_BEFORE_END //CUSTOM_DISPOSE_START //CUSTOM_DISPOSE_END //DISPOSE_AFTER_START - this.emit(Event.OBJECT_DISPOSED, this); //DISPOSE_AFTER_END + this.disposeInstance(); //DISPOSE_AFTER_END } //DISPOSE_TEMPLATE_END //DISPOSE_INSTANCE_TEMPLATE_START @@ -269,14 +540,12 @@ var R3Object = /*#__PURE__*/function (_Event) { key: "disposeInstance", value: function disposeInstance() { //DISPOSE_INSTANCE_BEFORE_START - _get(_getPrototypeOf(R3Object.prototype), "disposeInstance", this).call(this); - - this.emit(Event.DISPOSE_INSTANCE, this); //DISPOSE_INSTANCE_BEFORE_END + console.log('Disposing instance of ' + this.name); //DISPOSE_INSTANCE_BEFORE_END //CUSTOM_DISPOSE_INSTANCE_START //CUSTOM_DISPOSE_INSTANCE_END //DISPOSE_INSTANCE_AFTER_START - this.emit(Event.INSTANCE_DISPOSED, this); //DISPOSE_INSTANCE_AFTER_END + this.emit(Event.DISPOSE_INSTANCE, this); //DISPOSE_INSTANCE_AFTER_END } //DISPOSE_INSTANCE_TEMPLATE_END //CUSTOM_IMPLEMENTATION_START //CUSTOM_IMPLEMENTATION_END @@ -507,7 +776,7 @@ var R3 = /*#__PURE__*/function () { _createClass(R3, null, [{ key: "version", value: function version() { - return 'Sun Jun 20 2021 11:02:42 GMT+0000 (Coordinated Universal Time)'; + return 'Sun Jun 20 2021 18:45:42 GMT+0000 (Coordinated Universal Time)'; } }]); @@ -523,4 +792,4 @@ module.exports = R3; /***/ }) /******/ }); -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vc3JjL3IzL2luZGV4LmpzIiwid2VicGFjazovLy8uL3NyYy9yMy9yMy1yMy1vYmplY3QuanMiLCJ3ZWJwYWNrOi8vLy4vc3JjL3IzL3IzLXN5c3RlbS9pbmRleC5qcyIsIndlYnBhY2s6Ly8vLi9zcmMvcjMvcjMtc3lzdGVtL3IzLXN5c3RlbS1saW5raW5nLmpzIiwid2VicGFjazovLy8uL3NyYy9yMy9yMy1zeXN0ZW0vcjMtc3lzdGVtLXNvY2tldC5qcyIsIndlYnBhY2s6Ly8vLi9zcmMvcjMvcjMtc3lzdGVtL3IzLXN5c3RlbS5qcyIsIndlYnBhY2s6Ly8vLi9zcmMvcjMvcjMuanMiXSwibmFtZXMiOlsiUjMiLCJyZXF1aXJlIiwiU3lzdGVtIiwiTGlua2luZyIsInN0YXJ0IiwiU29ja2V0IiwibW9kdWxlIiwiZXhwb3J0cyIsIlV0aWxzIiwiRXZlbnQiLCJSM09iamVjdCIsIm9wdGlvbnMiLCJVbmRlZmluZWRPck51bGwiLCJpZCIsIlJhbmRvbUlkIiwibmFtZSIsImVtaXQiLCJPQkpFQ1RfQ1JFQVRFRCIsIk9iamVjdCIsImFzc2lnbiIsIk9CSkVDVF9JTklUSUFMSVpFRCIsIkNSRUFURV9JTlNUQU5DRSIsInJ1bnRpbWUiLCJncmFwaGljcyIsImNyZWF0ZUluc3RhbmNlIiwiSU5TVEFOQ0VfQ1JFQVRFRCIsInByb3BlcnR5IiwiVVBEQVRFX0lOU1RBTkNFX0JFRk9SRSIsIlVQREFURV9JTlNUQU5DRV9BRlRFUiIsIlVQREFURV9GUk9NX0lOU1RBTkNFX0JFRk9SRSIsIlVQREFURV9GUk9NX0lOU1RBTkNFX0FGVEVSIiwiRElTUE9TRV9PQkpFQ1QiLCJPQkpFQ1RfRElTUE9TRUQiLCJESVNQT1NFX0lOU1RBTkNFIiwiSU5TVEFOQ0VfRElTUE9TRUQiLCJTeXN0ZW1MaW5raW5nIiwiU3lzdGVtU29ja2V0IiwiTGlua2luZ1N5c3RlbSIsImNvbnNvbGUiLCJsb2ciLCJTdWJz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QixDOzs7Ozs7Ozs7Ozs7Ozs7OztBQ1JBLGVBQThDRCxtQkFBTyxDQUFDLGdEQUFELENBQXJEO0FBQUEsSUFBT0MsTUFBUCxZQUFPQSxNQUFQO0FBQUEsSUFBZWlDLGFBQWYsWUFBZUEsYUFBZjtBQUFBLElBQThCQyxZQUE5QixZQUE4QkEsWUFBOUI7O0FBQ0EsSUFBTTFCLFFBQVEsR0FBR1QsbUJBQU8sQ0FBQyxnREFBRCxDQUF4Qjs7SUFFTUQsRTtBQUVMLGdCQUFjO0FBQUE7QUFDYjs7OztXQUVELG1CQUFpQjtBQUNoQixhQUFPLFVBQVA7QUFDQTs7Ozs7O0FBSUZBLEVBQUUsQ0FBQ1UsUUFBSCxHQUFnQkEsUUFBaEI7QUFDQVYsRUFBRSxDQUFDRSxNQUFILEdBQWVBLE1BQWY7QUFDQUYsRUFBRSxDQUFDRSxNQUFILENBQVVDLE9BQVYsR0FBb0JnQyxhQUFwQjtBQUNBbkMsRUFBRSxDQUFDRSxNQUFILENBQVVHLE1BQVYsR0FBbUIrQixZQUFuQjtBQUVBOUIsTUFBTSxDQUFDQyxPQUFQLEdBQWlCUCxFQUFqQixDIiwiZmlsZSI6InIzLmpzIiwic291cmNlc0NvbnRlbnQiOlsiIFx0Ly8gVGhlIG1vZHVsZSBjYWNoZVxuIFx0dmFyIGluc3RhbGxlZE1vZHVsZXMgPSB7fTtcblxuIFx0Ly8gVGhlIHJlcXVpcmUgZnVuY3Rpb25cbiBcdGZ1bmN0aW9uIF9fd2VicGFja19yZXF1aXJlX18obW9kdWxlSWQpIHtcblxuIFx0XHQvLyBDaGVjayBpZiBtb2R1bGUgaXMgaW4gY2FjaGVcbiBcdFx0aWYoaW5zdGFsbGVkTW9kdWxlc1ttb2R1bGVJZF0pIHtcbiBcdFx0XHRyZXR1cm4gaW5zdGFsbGVkTW9kdWxlc1ttb2R1bGVJZF0uZXhwb3J0cztcbiBcdFx0fVxuIFx0XHQvLyBDcmVhdGUgYSBuZXcgbW9kdWxlIChhbmQgcHV0IGl0IGludG8gdGhlIGNhY2hlKVxuIFx0XHR2YXIgbW9kdWxlID0gaW5zdGFsbGVkTW9kdWxlc1ttb2R1bGVJZF0gPSB7XG4gXHRcdFx0aTogbW9kdWxlSWQsXG4gXHRcdFx0bDogZmFsc2UsXG4gXHRcdFx0ZXhwb3J0czoge31cbiBcdFx0fTtcblxuIFx0XHQvLyBFeGVjdXRlIHRoZSBtb2R1bGUgZnVuY3Rpb25cbiBcdFx0bW9kdWxlc1ttb2R1bGVJZF0uY2FsbChtb2R1bGUuZXhwb3J0cywgbW9kdWxlLCBtb2R1bGUuZXhwb3J0cywgX193ZWJwYWNrX3JlcXVpcmVfXyk7XG5cbiBcdFx0Ly8gRmxhZyB0aGUgbW9kdWxlIGFzIGxvYWRlZFxuIFx0XHRtb2R1bGUubCA9IHRydWU7XG5cbiBcdFx0Ly8gUmV0dXJuIHRoZSBleHBvcnRzIG9mIHRoZSBtb2R1bGVcbiBcdFx0cmV0dXJuIG1vZHVsZS5leHBvcnRzO1xuIFx0fVxuXG5cbiBcdC8vIGV4cG9zZSB0aGUgbW9kdWxlcyBvYmplY3QgKF9fd2VicGFja19tb2R1bGVzX18pXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm0gPSBtb2R1bGVzO1xuXG4gXHQvLyBleHBvc2UgdGhlIG1vZHVsZSBjYWNoZVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5jID0gaW5zdGFsbGVkTW9kdWxlcztcblxuIFx0Ly8gZGVmaW5lIGdldHRlciBmdW5jdGlvbiBmb3IgaGFybW9ueSBleHBvcnRzXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmQgPSBmdW5jdGlvbihleHBvcnRzLCBuYW1lLCBnZXR0ZXIpIHtcbiBcdFx0aWYoIV9fd2VicGFja19yZXF1aXJlX18ubyhleHBvcnRzLCBuYW1lKSkge1xuIFx0XHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBuYW1lLCB7IGVudW1lcmFibGU6IHRydWUsIGdldDogZ2V0dGVyIH0pO1xuIFx0XHR9XG4gXHR9O1xuXG4gXHQvLyBkZWZpbmUgX19lc01vZHVsZSBvbiBleHBvcnRzXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnIgPSBmdW5jdGlvbihleHBvcnRzKSB7XG4gXHRcdGlmKHR5cGVvZiBTeW1ib2wgIT09ICd1bmRlZmluZWQnICYmIFN5bWJvbC50b1N0cmluZ1RhZykge1xuIFx0XHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBTeW1ib2wudG9TdHJpbmdUYWcsIHsgdmFsdWU6ICdNb2R1bGUnIH0pO1xuIFx0XHR9XG4gXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCAnX19lc01vZHVsZScsIHsgdmFsdWU6IHRydWUgfSk7XG4gXHR9O1xuXG4gXHQvLyBjcmVhdGUgYSBmYWtlIG5hbWVzcGFjZSBvYmplY3RcbiBcdC8vIG1vZGUgJiAxOiB2YWx1ZSBpcyBhIG1vZHVsZSBpZCwgcmVxdWlyZSBpdFxuIFx0Ly8gbW9kZSAmIDI6IG1lcmdlIGFsbCBwcm9wZXJ0aWVzIG9mIHZhbHVlIGludG8gdGhlIG5zXG4gXHQvLyBtb2RlICYgNDogcmV0dXJuIHZhbHVlIHdoZW4gYWxyZWFkeSBucyBvYmplY3RcbiBcdC8vIG1vZGUgJiA4fDE6IGJlaGF2ZSBsaWtlIHJlcXVpcmVcbiBcdF9fd2VicGFja19yZXF1aXJlX18udCA9IGZ1bmN0aW9uKHZhbHVlLCBtb2RlKSB7XG4gXHRcdGlmKG1vZGUgJiAxKSB2YWx1ZSA9IF9fd2VicGFja19yZXF1aXJlX18odmFsdWUpO1xuIFx0XHRpZihtb2RlICYgOCkgcmV0dXJuIHZhbHVlO1xuIFx0XHRpZigobW9kZSAmIDQpICYmIHR5cGVvZiB2YWx1ZSA9PT0gJ29iamVjdCcgJiYgdmFsdWUgJiYgdmFsdWUuX19lc01vZHVsZSkgcmV0dXJuIHZhbHVlO1xuIFx0XHR2YXIgbnMgPSBPYmplY3QuY3JlYXRlKG51bGwpO1xuIFx0XHRfX3dlYnBhY2tfcmVxdWlyZV9fLnIobnMpO1xuIFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkobnMsICdkZWZhdWx0JywgeyBlbnVtZXJhYmxlOiB0cnVlLCB2YWx1ZTogdmFsdWUgfSk7XG4gXHRcdGlmKG1vZGUgJiAyICYmIHR5cGVvZiB2YWx1ZSAhPSAnc3RyaW5nJykgZm9yKHZhciBrZXkgaW4gdmFsdWUpIF9fd2VicGFja19yZXF1aXJlX18uZChucywga2V5LCBmdW5jdGlvbihrZXkpIHsgcmV0dXJuIHZhbHVlW2tleV07IH0uYmluZChudWxsLCBrZXkpKTtcbiBcdFx0cmV0dXJuIG5zO1xuIFx0fTtcblxuIFx0Ly8gZ2V0RGVmYXVsdEV4cG9ydCBmdW5jdGlvbiBmb3IgY29tcGF0aWJpbGl0eSB3aXRoIG5vbi1oYXJtb255IG1vZHVsZXNcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubiA9IGZ1bmN0aW9uKG1vZHVsZSkge1xuIFx0XHR2YXIgZ2V0dGVyID0gbW9kdWxlICYmIG1vZHVsZS5fX2VzTW9kdWxlID9cbiBcdFx0XHRmdW5jdGlvbiBnZXREZWZhdWx0KCkgeyByZXR1cm4gbW9kdWxlWydkZWZhdWx0J107IH0gOlxuIFx0XHRcdGZ1bmN0aW9uIGdldE1vZHVsZUV4cG9ydHMoKSB7IHJldHVybiBtb2R1bGU7IH07XG4gXHRcdF9fd2VicGFja19yZXF1aXJlX18uZChnZXR0ZXIsICdhJywgZ2V0dGVyKTtcbiBcdFx0cmV0dXJuIGdldHRlcjtcbiBcdH07XG5cbiBcdC8vIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbFxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5vID0gZnVuY3Rpb24ob2JqZWN0LCBwcm9wZXJ0eSkgeyByZXR1cm4gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9iamVjdCwgcHJvcGVydHkpOyB9O1xuXG4gXHQvLyBfX3dlYnBhY2tfcHVibGljX3BhdGhfX1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5wID0gXCJcIjtcblxuXG4gXHQvLyBMb2FkIGVudHJ5IG1vZHVsZSBhbmQgcmV0dXJuIGV4cG9ydHNcbiBcdHJldHVybiBfX3dlYnBhY2tfcmVxdWlyZV9fKF9fd2VicGFja19yZXF1aXJlX18ucyA9IFwiLi9zcmMvcjMvaW5kZXguanNcIik7XG4iLCJjb25zdCBSMyA9IHJlcXVpcmUoJy4vcjMuanMnKTtcblxuUjMuU3lzdGVtLkxpbmtpbmcuc3RhcnQoKTtcblIzLlN5c3RlbS5Tb2NrZXQuc3RhcnQoKTtcblxubW9kdWxlLmV4cG9ydHMgPSBSMztcbiIsImNvbnN0IFV0aWxzID0gcmVxdWlyZSgncjMtdXRpbHMnKTtcbmNvbnN0IEV2ZW50ID0gcmVxdWlyZSgncjMtZXZlbnQuanMnKTtcblxuLyoqXG5cbiBPUFRJT05TX1NUQVJUXG4gT1BUSU9OU19FTkRcblxuIElOU1RBTkNFX09QVElPTlNfTUFQUElOR19TVEFSVFxuIElOU1RBTkNFX09QVElPTlNfTUFQUElOR19FTkRcblxuIExJTktFRF9PQkpFQ1RTX1NUQVJUXG4gTElOS0VEX09CSkVDVFNfRU5EXG5cbiBFWENMVURFRF9GUk9NX0lOU1RBTkNFX09QVElPTlNfU1RBUlRcbiBFWENMVURFRF9GUk9NX0lOU1RBTkNFX09QVElPTlNfRU5EXG5cbiAqKi9cblxuY2xhc3MgUjNPYmplY3QgZXh0ZW5kcyBFdmVudCB7XG5cbiAgLy9DT05TVFJVQ1RPUl9FWFRFTkRTX1RFTVBMQVRFX1NUQVJUXG4gIGNvbnN0cnVjdG9yKG9wdGlvbnMpIHtcblxuICAgIGlmIChVdGlscy5VbmRlZmluZWRPck51bGwob3B0aW9ucykpIHtcbiAgICAgIG9wdGlvbnMgPSB7fTtcbiAgICB9XG5cbiAgICBvcHRpb25zLmlkID0gVXRpbHMuUmFuZG9tSWQoMTApO1xuICAgIG9wdGlvbnMubmFtZSA9ICdSM09iamVjdCAoJyArIG9wdGlvbnMuaWQgKyAnKSc7XG5cbiAgICBzdXBlcihvcHRpb25zKTtcblxuICAgIHRoaXMuZW1pdChFdmVudC5PQkpFQ1RfQ1JFQVRFRCwgdGhpcyk7XG5cbiAgICAvL09QVElPTlNfSU5JVF9TVEFSVFxuXHRcdC8vT1BUSU9OU19JTklUX0VORFxuXG4gICAgLy9DVVNUT01fT1BUSU9OU19JTklUX1NUQVJUXG4gICAgLy9DVVNUT01fT1BUSU9OU19JTklUX0VORFxuXG4gICAgT2JqZWN0LmFzc2lnbih0aGlzLCBvcHRpb25zKTtcblxuICAgIC8vQ1VTVE9NX0JFRk9SRV9JTklUX1NUQVJUXG4gICAgLy9DVVNUT01fQkVGT1JFX0lOSVRfRU5EXG5cbiAgICB0aGlzLmVtaXQoRXZlbnQuT0JKRUNUX0lOSVRJQUxJWkVELCB0aGlzKTtcbiAgfVxuXHQvL0NPTlNUUlVDVE9SX0VYVEVORFNfVEVNUExBVEVfRU5EXG5cbiAgLy9DUkVBVEVfSU5TVEFOQ0VfVEVNUExBVEVfU1RBUlRcbiAgY3JlYXRlSW5zdGFuY2UoKSB7XG5cbiAgICAvL0NSRUFURV9JTlNUQU5DRV9CRUZPUkVfU1RBUlRcbiAgICBzdXBlci5jcmVhdGVJbnN0YW5jZSgpO1xuICAgIHRoaXMuZW1pdChFdmVudC5DUkVBVEVfSU5TVEFOQ0UsIHRoaXMpO1xuXG4gICAgaWYgKHRoaXMucnVudGltZSA9PT0gJ2dyYXBoaWNzJykge1xuICAgICAgdGhpcy5ncmFwaGljcy5jcmVhdGVJbnN0YW5jZShcbiAgICAgICAge1xuICAgICAgICAgIC8vQ1JFQVRFX0lOU1RBTkNFX09QVElPTlNfU1RBUlRcblxuXHRcdFx0XHRcdC8vQ1JFQVRFX0lOU1RBTkNFX09QVElPTlNfRU5EXG4gICAgICAgIH0sXG4gICAgICAgIHRoaXNcbiAgICAgIClcbiAgICB9XG5cdFx0Ly9DUkVBVEVfSU5TVEFOQ0VfQkVGT1JFX0VORFxuXG4gICAgLy9DVVNUT01fQ1JFQVRFX0lOU1RBTkNFX1NUQVJUXG4gICAgLy9DVVNUT01fQ1JFQVRFX0lOU1RBTkNFX0VORFxuXG4gICAgLy9DUkVBVEVfSU5TVEFOQ0VfQUZURVJfU1RBUlRcbiAgICB0aGlzLmVtaXQoRXZlbnQuSU5TVEFOQ0VfQ1JFQVRFRCwgdGhpcyk7XG5cdFx0Ly9DUkVBVEVfSU5TVEFOQ0VfQUZURVJfRU5EXG5cbiAgfVxuXHQvL0NSRUFURV9JTlNUQU5DRV9URU1QTEFURV9FTkRcblxuICAvL1VQREFURV9JTlNUQU5DRV9URU1QTEFURV9TVEFSVFxuICB1cGRhdGVJbnN0YW5jZShwcm9wZXJ0eSkge1xuXG4gICAgLy9VUERBVEVfSU5TVEFOQ0VfQkVGT1JFX1NUQVJUXG4gICAgdGhpcy5lbWl0KEV2ZW50LlVQREFURV9JTlNUQU5DRV9CRUZPUkUsIHRoaXMpO1xuXHRcdC8vVVBEQVRFX0lOU1RBTkNFX0JFRk9SRV9FTkRcblxuICAgIC8vVVBEQVRFX0lOU1RBTkNFX09QVElPTlNfU1RBUlRcblx0XHQvL1VQREFURV9JTlNUQU5DRV9PUFRJT05TX0VORFxuXG4gICAgLy9DVVNUT01fVVBEQVRFX0lOU1RBTkNFX1NUQVJUXG4gICAgLy9DVVNUT01fVVBEQVRFX0lOU1RBTkNFX0VORFxuXG4gICAgLy9VUERBVEVfSU5TVEFOQ0VfQUZURVJfU1RBUlRcbiAgICB0aGlzLmVtaXQoRXZlbnQuVVBEQVRFX0lOU1RBTkNFX0FGVEVSLCB0aGlzKTtcblx0XHQvL1VQREFURV9JTlNUQU5DRV9BRlRFUl9FTkRcblxuICB9XG5cdC8vVVBEQVRFX0lOU1RBTkNFX1RFTVBMQVRFX0VORFxuXG4gIC8vVVBEQVRFX0ZST01fSU5TVEFOQ0VfVEVNUExBVEVfU1RBUlRcbiAgdXBkYXRlRnJvbUluc3RhbmNlKHByb3BlcnR5KSB7XG5cbiAgICAvL1VQREFURV9GUk9NX0lOU1RBTkNFX0JFRk9SRV9TVEFSVFxuICAgIHRoaXMuZW1pdChFdmVudC5VUERBVEVfRlJPTV9JTlNUQU5DRV9CRUZPUkUsIHRoaXMpO1xuXHRcdC8vVVBEQVRFX0ZST01fSU5TVEFOQ0VfQkVGT1JFX0VORFxuXG4gICAgLy9VUERBVEVfRlJPTV9JTlNUQU5DRV9PUFRJT05TX1NUQVJUXG5cdFx0Ly9VUERBVEVfRlJPTV9JTlNUQU5DRV9PUFRJT05TX0VORFxuXG4gICAgLy9DVVNUT01fVVBEQVRFX0ZST01fSU5TVEFOQ0VfU1RBUlRcbiAgICAvL0NVU1RPTV9VUERBVEVfRlJPTV9JTlNUQU5DRV9FTkRcblxuICAgIC8vVVBEQVRFX0ZST01fSU5TVEFOQ0VfQUZURVJfU1RBUlRcbiAgICB0aGlzLmVtaXQoRXZlbnQuVVBEQVRFX0ZST01fSU5TVEFOQ0VfQUZURVIsIHRoaXMpO1xuXHRcdC8vVVBEQVRFX0ZST01fSU5TVEFOQ0VfQUZURVJfRU5EXG5cbiAgfVxuXHQvL1VQREFURV9GUk9NX0lOU1RBTkNFX1RFTVBMQVRFX0VORFxuXG4gIC8vRElTUE9TRV9URU1QTEFURV9TVEFSVFxuICBkaXNwb3NlKCkge1xuXG4gICAgLy9ESVNQT1NFX0JFRk9SRV9TVEFSVFxuICAgIHRoaXMuZW1pdChFdmVudC5ESVNQT1NFX09CSkVDVCwgdGhpcyk7XG5cdFx0Ly9ESVNQT1NFX0JFRk9SRV9FTkRcblxuICAgIC8vQ1VTVE9NX0RJU1BPU0VfU1RBUlRcbiAgICAvL0NVU1RPTV9ESVNQT1NFX0VORFxuXG4gICAgLy9ESVNQT1NFX0FGVEVSX1NUQVJUXG4gICAgdGhpcy5lbWl0KEV2ZW50Lk9CSkVDVF9ESVNQT1NFRCwgdGhpcyk7XG5cdFx0Ly9ESVNQT1NFX0FGVEVSX0VORFxuXG4gIH1cblx0Ly9ESVNQT1NFX1RFTVBMQVRFX0VORFxuXG4gIC8vRElTUE9TRV9JTlNUQU5DRV9URU1QTEFURV9TVEFSVFxuICBkaXNwb3NlSW5zdGFuY2UoKSB7XG5cbiAgICAvL0RJU1BPU0VfSU5TVEFOQ0VfQkVGT1JFX1NUQVJUXG4gICAgc3VwZXIuZGlzcG9zZUluc3RhbmNlKCk7XG4gICAgdGhpcy5lbWl0KEV2ZW50LkRJU1BPU0VfSU5TVEFOQ0UsIHRoaXMpO1xuXHRcdC8vRElTUE9TRV9JTlNUQU5DRV9CRUZPUkVfRU5EXG5cbiAgICAvL0NVU1RPTV9ESVNQT1NFX0lOU1RBTkNFX1NUQVJUXG4gICAgLy9DVVNUT01fRElTUE9TRV9JTlNUQU5DRV9FTkRcblxuICAgIC8vRElTUE9TRV9JTlNUQU5DRV9BRlRFUl9TVEFSVFxuICAgIHRoaXMuZW1pdChFdmVudC5JTlNUQU5DRV9ESVNQT1NFRCwgdGhpcyk7XG5cdFx0Ly9ESVNQT1NFX0lOU1RBTkNFX0FGVEVSX0VORFxuXG4gIH1cblx0Ly9ESVNQT1NFX0lOU1RBTkNFX1RFTVBMQVRFX0VORFxuXG4gIC8vQ1VTVE9NX0lNUExFTUVOVEFUSU9OX1NUQVJUXG4gIC8vQ1VTVE9NX0lNUExFTUVOVEFUSU9OX0VORFxuXG59XG5cbi8vQ1VTVE9NX09VVF9PRl9DTEFTU19JTVBMRU1FTlRBVElPTl9TVEFSVFxuLy9DVVNUT01fT1VUX09GX0NMQVNTX0lNUExFTUVOVEFUSU9OX0VORFxuXG5tb2R1bGUuZXhwb3J0cyA9IFIzT2JqZWN0OyIsImNvbnN0IFN5c3RlbSA9IHJlcXVpcmUoJy4vcjMtc3lzdGVtLmpzJyk7XG5jb25zdCBTeXN0ZW1MaW5raW5nID0gcmVxdWlyZSgnLi9yMy1zeXN0ZW0tbGlua2luZy5qcycpO1xuY29uc3QgU3lzdGVtU29ja2V0ID0gcmVxdWlyZSgnLi9yMy1zeXN0ZW0tc29ja2V0LmpzJyk7XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICBTeXN0ZW0sXG4gIFN5c3RlbUxpbmtpbmcsXG4gIFN5c3RlbVNvY2tldFxufSIsImNvbnN0IFN5c3RlbSA9IHJlcXVpcmUoJy4vcjMtc3lzdGVtJyk7XG5jb25zdCBSM09iamVjdCA9IHJlcXVpcmUoJy4uL3IzLXIzLW9iamVjdCcpO1xuY29uc3QgRXZlbnQgPSByZXF1aXJlKCcuLi9yMy1ldmVudCcpO1xuXG5jbGFzcyBMaW5raW5nU3lzdGVtIGV4dGVuZHMgU3lzdGVtIHtcblxuXHRzdGF0aWMgc3RhcnQoKSB7XG5cblx0XHRzdXBlci5zdGFydCgpO1xuXHRcdFxuXHRcdGNvbnNvbGUubG9nKCdzdGFydGluZyBsaW5raW5nIHN5c3RlbScpO1xuXG5cdFx0RXZlbnQuU3Vic2NyaWJlKFxuXHRcdFx0RXZlbnQuT0JKRUNUX0NSRUFURUQsXG5cdFx0XHQoKSA9PiB7XG5cdFx0XHRcdGNvbnNvbGUubG9nKCdsaW5raW5nIHN5c3RlbSBkaXNjb3ZlcmVkIGFuIG9iamVjdCcpO1xuXHRcdFx0fVxuXHRcdCk7XG5cblx0XHRsZXQgb2JqZWN0ID0gbmV3IFIzT2JqZWN0KCk7XG5cblx0XHRyZXR1cm4gdHJ1ZTtcblxuXHR9XHRcblxufVxuXG5tb2R1bGUuZXhwb3J0cyA9IExpbmtpbmdTeXN0ZW07XG4iLCJjb25zdCBTeXN0ZW0gPSByZXF1aXJlKCcuL3IzLXN5c3RlbS5qcycpO1xuXG5jbGFzcyBTb2NrZXRTeXN0ZW0gZXh0ZW5kcyBTeXN0ZW0ge1xuXG5cdHN0YXRpYyBzdGFydCgpIHtcblxuXHRcdHN1cGVyLnN0YXJ0KCk7XG5cdFx0XG5cdFx0Y29uc29sZS5sb2coJ3N0YXJ0aW5nIHNvY2tldCBzeXN0ZW0nKTtcblxuXHRcdHJldHVybiB0cnVlO1xuXG5cdH1cblxufVxuXG5tb2R1bGUuZXhwb3J0cyA9IFNvY2tldFN5c3RlbTsiLCJjbGFzcyBTeXN0ZW0ge1xuXG5cdHN0YXRpYyBzdGFydCgpIHtcblx0XHRjb25zb2xlLmxvZygnc3RhcnRpbmcgYSBzeXN0ZW0nKTtcdFxuXHR9XG5cbn1cblxubW9kdWxlLmV4cG9ydHMgPSBTeXN0ZW07XG4iLCJjb25zdCB7U3lzdGVtLCBTeXN0ZW1MaW5raW5nLCBTeXN0ZW1Tb2NrZXR9ID0gcmVxdWlyZSgnLi9yMy1zeXN0ZW0nKTtcbmNvbnN0IFIzT2JqZWN0ID0gcmVxdWlyZSgnLi9yMy1yMy1vYmplY3QnKTtcblxuY2xhc3MgUjMge1xuXG5cdGNvbnN0cnVjdG9yKCkge1xuXHR9XG5cblx0c3RhdGljIHZlcnNpb24oKSB7XG5cdFx0cmV0dXJuICdfX0RBVEVfXyc7XG5cdH1cblxufVxuXG5SMy5SM09iamVjdCBcdFx0PSBSM09iamVjdDtcblIzLlN5c3RlbSBcdFx0XHQ9IFN5c3RlbTtcblIzLlN5c3RlbS5MaW5raW5nXHQ9IFN5c3RlbUxpbmtpbmc7XG5SMy5TeXN0ZW0uU29ja2V0XHQ9IFN5c3RlbVNvY2tldDtcblxubW9kdWxlLmV4cG9ydHMgPSBSMztcbiJdLCJzb3VyY2VSb290IjoiIn0= \ No newline at end of file +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vLy4vc3JjL3IzL2luZGV4LmpzIiwid2VicGFjazovLy8uL3NyYy9yMy9yMy1ldmVudC5qcyIsIndlYnBhY2s6Ly8vLi9zcmMvcjMvcjMtcjMtb2JqZWN0LmpzIiwid2VicGFjazovLy8uL3NyYy9yMy9yMy1zeXN0ZW0vaW5kZXguanMiLCJ3ZWJwYWNrOi8vLy4vc3JjL3IzL3IzLXN5c3RlbS9yMy1zeXN0ZW0tbGlua2luZy5qcyIsIndlYnBhY2s6Ly8vLi9zcmMvcjMvcjMtc3lzdGVtL3IzLXN5c3RlbS1zb2NrZXQuanMiLCJ3ZWJwYWNrOi8vLy4vc3JjL3IzL3IzLXN5c3RlbS9yMy1zeXN0ZW0uanMiLCJ3ZWJwYWNrOi8vLy4vc3JjL3IzL3IzLmpzIl0sIm5hbWVzIjpbIlIzIiwicmVxdWlyZSIsIlN5c3RlbSIsIkxpbmtpbmciLCJzdGFydCIsIlNvY2tldCIsIm1vZHVsZSIsImV4cG9ydHMiLCJVdGlscyIsIkV2ZW50Iiwib3B0aW9ucyIsIkVtaXQiLCJPQkpFQ1RfQ1JFQVRFRCIsIk9iamVjdCIsImFzc2lnbiIsIk9CSkVDVF9JTklUSUFMSVpFRCIsImV2ZW50TmFtZSIsImNhbGxiYWNrIiwiU3Vic2NyaWJlIiwiYmluZCIsImRhdGEiLCJjbGllbnRDYWxsYmFjayIsImNsaWVudEVycm9yQ2FsbGJhY2siLCJmbiIsInN1YnNjcmlwdGlvbklkIiwiUmFuZG9tSWQiLCJTdWJzY3JpcHRpb25zIiwiaGFzT3duUHJvcGVydHkiLCJFcnJvciIsInJlbW92ZSIsImV2ZW50SWQiLCJsaXN0ZW5lcnMiLCJrZXlzIiwibGVuZ3RoIiwic3Vic2NyaXB0aW9uSWRzIiwibWFwIiwicmVzdWx0IiwiZXJyb3IiLCJjb25zb2xlIiwiQ09NUE9ORU5UX0lOSVRJQUxJWkVEIiwiQ1JFQVRFX0lOU1RBTkNFX0JFRk9SRSIsIkRJU1BPU0VfSU5TVEFOQ0UiLCJESVNQT1NFX09CSkVDVCIsIkdFVF9SVU5USU1FIiwiR0VUX1dJTkRPV19TSVpFIiwiSU5TVEFOQ0VfQ1JFQVRFRCIsIklOU1RBTkNFX0RJU1BPU0VEIiwiUEFVU0UiLCJSRVNUQVJUIiwiU1RBUlQiLCJVUERBVEVfRlJPTV9JTlNUQU5DRV9BRlRFUiIsIlVQREFURV9GUk9NX0lOU1RBTkNFX0JFRk9SRSIsIlVQREFURV9JTlNUQU5DRV9BRlRFUiIsIlVQREFURV9JTlNUQU5DRV9CRUZPUkUiLCJNQVhfRVZFTlRTIiwiR2V0RXZlbnROYW1lIiwiUjNPYmplY3QiLCJVbmRlZmluZWRPck51bGwiLCJlbWl0IiwiaWQiLCJuYW1lIiwiY29uc3RydWN0b3IiLCJyZWdpc3RlciIsInJ1bnRpbWUiLCJjcmVhdGVJbnN0YW5jZSIsInByb3BlcnR5Iiwic3Vic2NyaWJlIiwib2JqZWN0IiwiZGlzcG9zZUluc3RhbmNlIiwibG9nIiwiU3lzdGVtTGlua2luZyIsIlN5c3RlbVNvY2tldCIsIkxpbmtpbmdTeXN0ZW0iLCJTb2NrZXRTeXN0ZW0iXSwibWFwcGluZ3MiOiI7UUFBQTtRQUNBOztRQUVBO1FBQ0E7O1FBRUE7UUFDQTtRQUNBO1FBQ0E7UUFDQTtRQUNBO1FBQ0E7UUFDQTtRQUNBO1FBQ0E7O1FBRUE7UUFDQTs7UUFFQTtRQUNBOztRQUVBO1FBQ0E7UUFDQTs7O1FBR0E7UUFDQTs7UUFFQTtRQUNBOztRQUVBO1FBQ0E7UUFDQTtRQUNBLDBDQUEwQyxnQ0FBZ0M7UUFDMUU7UUFDQTs7UUFFQTtRQUNBO1FBQ0E7UUFDQSx3REFBd0Qsa0JBQWtCO1FBQzFFO1FBQ0EsaURBQWlELGNBQWM7UUFDL0Q7O1FBRUE7UUFDQTtRQUNBO1FBQ0E7UUFDQTtRQUNBO1FBQ0E7UUFDQTtRQUNBO1FBQ0E7UUFDQTtRQUNBLHlDQUF5QyxpQ0FBaUM7UUFDMUUsZ0hBQWdILG1CQUFtQixFQUFFO1FBQ3JJO1FBQ0E7O1FBRUE7UUFDQTtRQUNBO1FBQ0EsMkJBQTJCLDBCQUEwQixFQUFFO1FBQ3ZELGlDQUFpQyxlQUFlO1FBQ2hEO1FBQ0E7UUFDQTs7UUFFQTtRQUNBLHNEQUFz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Q0FBeEI7O0lBRU1ELEU7QUFFTCxnQkFBYztBQUFBO0FBQ2I7Ozs7V0FFRCxtQkFBaUI7QUFDaEIsYUFBTyxVQUFQO0FBQ0E7Ozs7OztBQUlGQSxFQUFFLENBQUN3RCxRQUFILEdBQWdCQSxRQUFoQjtBQUNBeEQsRUFBRSxDQUFDRSxNQUFILEdBQWVBLE1BQWY7QUFDQUYsRUFBRSxDQUFDRSxNQUFILENBQVVDLE9BQVYsR0FBb0JtRSxhQUFwQjtBQUNBdEUsRUFBRSxDQUFDRSxNQUFILENBQVVHLE1BQVYsR0FBbUJrRSxZQUFuQjtBQUVBakUsTUFBTSxDQUFDQyxPQUFQLEdBQWlCUCxFQUFqQixDIiwiZmlsZSI6InIzLmpzIiwic291cmNlc0NvbnRlbnQiOlsiIFx0Ly8gVGhlIG1vZHVsZSBjYWNoZVxuIFx0dmFyIGluc3RhbGxlZE1vZHVsZXMgPSB7fTtcblxuIFx0Ly8gVGhlIHJlcXVpcmUgZnVuY3Rpb25cbiBcdGZ1bmN0aW9uIF9fd2VicGFja19yZXF1aXJlX18obW9kdWxlSWQpIHtcblxuIFx0XHQvLyBDaGVjayBpZiBtb2R1bGUgaXMgaW4gY2FjaGVcbiBcdFx0aWYoaW5zdGFsbGVkTW9kdWxlc1ttb2R1bGVJZF0pIHtcbiBcdFx0XHRyZXR1cm4gaW5zdGFsbGVkTW9kdWxlc1ttb2R1bGVJZF0uZXhwb3J0cztcbiBcdFx0fVxuIFx0XHQvLyBDcmVhdGUgYSBuZXcgbW9kdWxlIChhbmQgcHV0IGl0IGludG8gdGhlIGNhY2hlKVxuIFx0XHR2YXIgbW9kdWxlID0gaW5zdGFsbGVkTW9kdWxlc1ttb2R1bGVJZF0gPSB7XG4gXHRcdFx0aTogbW9kdWxlSWQsXG4gXHRcdFx0bDogZmFsc2UsXG4gXHRcdFx0ZXhwb3J0czoge31cbiBcdFx0fTtcblxuIFx0XHQvLyBFeGVjdXRlIHRoZSBtb2R1bGUgZnVuY3Rpb25cbiBcdFx0bW9kdWxlc1ttb2R1bGVJZF0uY2FsbChtb2R1bGUuZXhwb3J0cywgbW9kdWxlLCBtb2R1bGUuZXhwb3J0cywgX193ZWJwYWNrX3JlcXVpcmVfXyk7XG5cbiBcdFx0Ly8gRmxhZyB0aGUgbW9kdWxlIGFzIGxvYWRlZFxuIFx0XHRtb2R1bGUubCA9IHRydWU7XG5cbiBcdFx0Ly8gUmV0dXJuIHRoZSBleHBvcnRzIG9mIHRoZSBtb2R1bGVcbiBcdFx0cmV0dXJuIG1vZHVsZS5leHBvcnRzO1xuIFx0fVxuXG5cbiBcdC8vIGV4cG9zZSB0aGUgbW9kdWxlcyBvYmplY3QgKF9fd2VicGFja19tb2R1bGVzX18pXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm0gPSBtb2R1bGVzO1xuXG4gXHQvLyBleHBvc2UgdGhlIG1vZHVsZSBjYWNoZVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5jID0gaW5zdGFsbGVkTW9kdWxlcztcblxuIFx0Ly8gZGVmaW5lIGdldHRlciBmdW5jdGlvbiBmb3IgaGFybW9ueSBleHBvcnRzXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmQgPSBmdW5jdGlvbihleHBvcnRzLCBuYW1lLCBnZXR0ZXIpIHtcbiBcdFx0aWYoIV9fd2VicGFja19yZXF1aXJlX18ubyhleHBvcnRzLCBuYW1lKSkge1xuIFx0XHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBuYW1lLCB7IGVudW1lcmFibGU6IHRydWUsIGdldDogZ2V0dGVyIH0pO1xuIFx0XHR9XG4gXHR9O1xuXG4gXHQvLyBkZWZpbmUgX19lc01vZHVsZSBvbiBleHBvcnRzXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnIgPSBmdW5jdGlvbihleHBvcnRzKSB7XG4gXHRcdGlmKHR5cGVvZiBTeW1ib2wgIT09ICd1bmRlZmluZWQnICYmIFN5bWJvbC50b1N0cmluZ1RhZykge1xuIFx0XHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBTeW1ib2wudG9TdHJpbmdUYWcsIHsgdmFsdWU6ICdNb2R1bGUnIH0pO1xuIFx0XHR9XG4gXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCAnX19lc01vZHVsZScsIHsgdmFsdWU6IHRydWUgfSk7XG4gXHR9O1xuXG4gXHQvLyBjcmVhdGUgYSBmYWtlIG5hbWVzcGFjZSBvYmplY3RcbiBcdC8vIG1vZGUgJiAxOiB2YWx1ZSBpcyBhIG1vZHVsZSBpZCwgcmVxdWlyZSBpdFxuIFx0Ly8gbW9kZSAmIDI6IG1lcmdlIGFsbCBwcm9wZXJ0aWVzIG9mIHZhbHVlIGludG8gdGhlIG5zXG4gXHQvLyBtb2RlICYgNDogcmV0dXJuIHZhbHVlIHdoZW4gYWxyZWFkeSBucyBvYmplY3RcbiBcdC8vIG1vZGUgJiA4fDE6IGJlaGF2ZSBsaWtlIHJlcXVpcmVcbiBcdF9fd2VicGFja19yZXF1aXJlX18udCA9IGZ1bmN0aW9uKHZhbHVlLCBtb2RlKSB7XG4gXHRcdGlmKG1vZGUgJiAxKSB2YWx1ZSA9IF9fd2VicGFja19yZXF1aXJlX18odmFsdWUpO1xuIFx0XHRpZihtb2RlICYgOCkgcmV0dXJuIHZhbHVlO1xuIFx0XHRpZigobW9kZSAmIDQpICYmIHR5cGVvZiB2YWx1ZSA9PT0gJ29iamVjdCcgJiYgdmFsdWUgJiYgdmFsdWUuX19lc01vZHVsZSkgcmV0dXJuIHZhbHVlO1xuIFx0XHR2YXIgbnMgPSBPYmplY3QuY3JlYXRlKG51bGwpO1xuIFx0XHRfX3dlYnBhY2tfcmVxdWlyZV9fLnIobnMpO1xuIFx0XHRPYmplY3QuZGVmaW5lUHJvcGVydHkobnMsICdkZWZhdWx0JywgeyBlbnVtZXJhYmxlOiB0cnVlLCB2YWx1ZTogdmFsdWUgfSk7XG4gXHRcdGlmKG1vZGUgJiAyICYmIHR5cGVvZiB2YWx1ZSAhPSAnc3RyaW5nJykgZm9yKHZhciBrZXkgaW4gdmFsdWUpIF9fd2VicGFja19yZXF1aXJlX18uZChucywga2V5LCBmdW5jdGlvbihrZXkpIHsgcmV0dXJuIHZhbHVlW2tleV07IH0uYmluZChudWxsLCBrZXkpKTtcbiBcdFx0cmV0dXJuIG5zO1xuIFx0fTtcblxuIFx0Ly8gZ2V0RGVmYXVsdEV4cG9ydCBmdW5jdGlvbiBmb3IgY29tcGF0aWJpbGl0eSB3aXRoIG5vbi1oYXJtb255IG1vZHVsZXNcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubiA9IGZ1bmN0aW9uKG1vZHVsZSkge1xuIFx0XHR2YXIgZ2V0dGVyID0gbW9kdWxlICYmIG1vZHVsZS5fX2VzTW9kdWxlID9cbiBcdFx0XHRmdW5jdGlvbiBnZXREZWZhdWx0KCkgeyByZXR1cm4gbW9kdWxlWydkZWZhdWx0J107IH0gOlxuIFx0XHRcdGZ1bmN0aW9uIGdldE1vZHVsZUV4cG9ydHMoKSB7IHJldHVybiBtb2R1bGU7IH07XG4gXHRcdF9fd2VicGFja19yZXF1aXJlX18uZChnZXR0ZXIsICdhJywgZ2V0dGVyKTtcbiBcdFx0cmV0dXJuIGdldHRlcjtcbiBcdH07XG5cbiBcdC8vIE9iamVjdC5wcm90b3R5cGUuaGFzT3duUHJvcGVydHkuY2FsbFxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5vID0gZnVuY3Rpb24ob2JqZWN0LCBwcm9wZXJ0eSkgeyByZXR1cm4gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsKG9iamVjdCwgcHJvcGVydHkpOyB9O1xuXG4gXHQvLyBfX3dlYnBhY2tfcHVibGljX3BhdGhfX1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5wID0gXCJcIjtcblxuXG4gXHQvLyBMb2FkIGVudHJ5IG1vZHVsZSBhbmQgcmV0dXJuIGV4cG9ydHNcbiBcdHJldHVybiBfX3dlYnBhY2tfcmVxdWlyZV9fKF9fd2VicGFja19yZXF1aXJlX18ucyA9IFwiLi9zcmMvcjMvaW5kZXguanNcIik7XG4iLCJjb25zdCBSMyA9IHJlcXVpcmUoJy4vcjMuanMnKTtcblxuUjMuU3lzdGVtLkxpbmtpbmcuc3RhcnQoKTtcblIzLlN5c3RlbS5Tb2NrZXQuc3RhcnQoKTtcblxubW9kdWxlLmV4cG9ydHMgPSBSMztcbiIsImNvbnN0IFV0aWxzID0gcmVxdWlyZSgncjMtdXRpbHMnKTtcblxuLyoqXG5cbiBPUFRJT05TX1NUQVJUXG4gT1BUSU9OU19FTkRcblxuICoqL1xuXG5jbGFzcyBFdmVudCB7XG5cbiAgLy9DT05TVFJVQ1RPUl9URU1QTEFURV9TVEFSVFxuICBjb25zdHJ1Y3RvcihvcHRpb25zKSB7XG5cbiAgICBFdmVudC5FbWl0KEV2ZW50Lk9CSkVDVF9DUkVBVEVELCB0aGlzKTtcblxuICAgIC8vT1BUSU9OU19JTklUX1NUQVJUXG4gICAgaWYgKHR5cGVvZiBvcHRpb25zID09PSAndW5kZWZpbmVkJykge1xuICAgICAgICBvcHRpb25zID0ge307XG4gICAgfVxuXHRcdC8vT1BUSU9OU19JTklUX0VORFxuXG4gICAgLy9DVVNUT01fT1BUSU9OU19JTklUX1NUQVJUXG4gICAgLy9DVVNUT01fT1BUSU9OU19JTklUX0VORFxuXG4gICAgT2JqZWN0LmFzc2lnbih0aGlzLCBvcHRpb25zKTtcblxuICAgIC8vQ1VTVE9NX0JFRk9SRV9JTklUX1NUQVJUXG4gICAgLy9DVVNUT01fQkVGT1JFX0lOSVRfRU5EXG5cbiAgICBFdmVudC5FbWl0KEV2ZW50Lk9CSkVDVF9JTklUSUFMSVpFRCwgdGhpcyk7XG4gIH1cblx0Ly9DT05TVFJVQ1RPUl9URU1QTEFURV9FTkRcblxuICAvL0NVU1RPTV9JTVBMRU1FTlRBVElPTl9TVEFSVFxuXG5cdC8qKlxuXHQgKiBTb21lIG5pY2UgRXZlbnRzIGhhbmRsaW5nXG5cdCAqIEB0eXBlIHt7fX1cblx0ICovXG5cdHN0YXRpYyBTdWJzY3JpcHRpb25zID0ge307XG5cblx0c3RhdGljIFN1YnNjcmliZShcblx0XHRldmVudE5hbWUsXG5cdFx0Zm5cblx0KSB7XG5cdFx0LyoqXG5cdFx0ICogVG9kbyAtIG1heWJlIGV2ZW50dWFsbHkgc3RvcmUgYSBib29sZWFuIHdoaWNoIGluZGljYXRlcyBpZiB0aGUgZnVuY3Rpb24gaGFzIGJlZW4gZXhlY3V0ZWRcblx0XHQgKi9cblx0XHRsZXQgc3Vic2NyaXB0aW9uSWQgPSBVdGlscy5SYW5kb21JZCgxMCk7XG5cblx0XHRpZiAoRXZlbnQuU3Vic2NyaXB0aW9ucy5oYXNPd25Qcm9wZXJ0eShldmVudE5hbWUpKSB7XG5cblx0XHRcdGlmIChFdmVudC5TdWJzY3JpcHRpb25zW2V2ZW50TmFtZV1bc3Vic2NyaXB0aW9uSWRdKSB7XG5cdFx0XHRcdHRocm93IG5ldyBFcnJvcignQSBjb21wb25lbnQgY2FuIG9ubHkgc3Vic2NyaWJlIHRvIGEgcGFydGljdWxhciBldmVudCBJRCBvbmNlJyk7XG5cdFx0XHR9XG5cblx0XHRcdEV2ZW50LlN1YnNjcmlwdGlvbnNbZXZlbnROYW1lXVtzdWJzY3JpcHRpb25JZF0gPSBmbjtcblx0XHR9IGVsc2Uge1xuXHRcdFx0RXZlbnQuU3Vic2NyaXB0aW9uc1tldmVudE5hbWVdID0ge307XG5cdFx0XHRFdmVudC5TdWJzY3JpcHRpb25zW2V2ZW50TmFtZV1bc3Vic2NyaXB0aW9uSWRdID0gZm47XG5cdFx0fVxuXG5cdFx0LyoqXG5cdFx0ICogUmV0dXJuIGEgaGFuZGxlIHRvIHRoZSBjYWxsZXIgdG8gYWxsb3cgdXMgdG8gdW5zdWJzY3JpYmUgdG8gdGhpcyBldmVudFxuXHRcdCAqL1xuXHRcdHJldHVybiB7XG5cdFx0XHRmbjogZm4sXG5cdFx0XHRyZW1vdmU6IGZ1bmN0aW9uIChldmVudElkLCBzdWJzY3JpcHRpb25JZCkge1xuXG5cdFx0XHRcdHJldHVybiBmdW5jdGlvbiAoKSB7XG5cblx0XHRcdFx0XHQvKipcblx0XHRcdFx0XHQgKiBTdG9wIGxpc3RlbmluZyBmb3IgdGhpcyBldmVudCBmcm9tIHRoaXMgY29tcG9uZW50XG5cdFx0XHRcdFx0ICovXG5cdFx0XHRcdFx0ZGVsZXRlIEV2ZW50LlN1YnNjcmlwdGlvbnNbZXZlbnRJZF1bc3Vic2NyaXB0aW9uSWRdO1xuXG5cdFx0XHRcdFx0LyoqXG5cdFx0XHRcdFx0ICogSWYgdGhlIGxlbmd0aCBvZiBsaXN0ZW5lcnMgaXMgMCwgc3RvcCByZWZlcmVuY2luZyB0aGlzIGV2ZW50XG5cdFx0XHRcdFx0ICogQHR5cGUge3N0cmluZ1tdfVxuXHRcdFx0XHRcdCAqL1xuXHRcdFx0XHRcdGxldCBsaXN0ZW5lcnMgPSBPYmplY3Qua2V5cyhFdmVudC5TdWJzY3JpcHRpb25zW2V2ZW50SWRdKTtcblx0XHRcdFx0XHRpZiAobGlzdGVuZXJzLmxlbmd0aCA9PT0gMCkge1xuXHRcdFx0XHRcdFx0ZGVsZXRlIEV2ZW50LlN1YnNjcmlwdGlvbnNbZXZlbnRJZF07XG5cdFx0XHRcdFx0fVxuXHRcdFx0XHR9XG5cblx0XHRcdH0oZXZlbnROYW1lLCBzdWJzY3JpcHRpb25JZCksXG5cdFx0XHRzdWJzY3JpcHRpb25JZCA6IHN1YnNjcmlwdGlvbklkXG5cdFx0fTtcblx0fTtcblxuXHQvKipcblx0ICogU3Vic2NyaWJlIHRvIHNvbWUgZXZlbnRzXG5cdCAqIEBwYXJhbSBldmVudE5hbWVcblx0ICogQHBhcmFtIGNhbGxiYWNrXG5cdCAqL1xuXHRzdWJzY3JpYmUoXG5cdFx0ZXZlbnROYW1lLFxuXHRcdGNhbGxiYWNrXG5cdCkge1xuXHRcdHJldHVybiBFdmVudC5TdWJzY3JpYmUoZXZlbnROYW1lLCBjYWxsYmFjay5iaW5kKHRoaXMpKTtcblx0fTtcblxuXHQvKipcblx0ICogU3RhdGljIFN5bmNocm9ub3VzIEV2ZW50IC0gQ2FsbHMgY2xpZW50Q2FsbGJhY2sgZGlyZWN0bHkgYWZ0ZXIgdGhlIGV2ZW50IHJlc3VsdCBpcyBvYnRhaW5lZFxuXHQgKiBAcGFyYW0gZXZlbnRJZFxuXHQgKiBAcGFyYW0gZGF0YVxuXHQgKiBAcGFyYW0gY2xpZW50Q2FsbGJhY2sgaXMgZXhlY3V0ZWQgaWRlYWxseSB3aGVuIHRoZSBldmVudCBjb21wbGV0ZWRcblx0ICogQHBhcmFtIGNsaWVudEVycm9yQ2FsbGJhY2tcblx0ICogQHJldHVybnMge251bWJlcn0gb2YgY2FsbGJhY2tzIGV4ZWN1dGVkXG5cdCAqIEBjb25zdHJ1Y3RvclxuXHQgKi9cblx0c3RhdGljIEVtaXQoXG5cdFx0ZXZlbnRJZCxcblx0XHRkYXRhLFxuXHRcdGNsaWVudENhbGxiYWNrLFxuXHRcdGNsaWVudEVycm9yQ2FsbGJhY2tcblx0KSB7XG5cdFx0aWYgKEV2ZW50LlN1YnNjcmlwdGlvbnMuaGFzT3duUHJvcGVydHkoZXZlbnRJZCkpIHtcblxuXHRcdFx0bGV0IHN1YnNjcmlwdGlvbklkcyA9IE9iamVjdC5rZXlzKEV2ZW50LlN1YnNjcmlwdGlvbnNbZXZlbnRJZF0pO1xuXG5cdFx0XHRzdWJzY3JpcHRpb25JZHMubWFwKFxuXHRcdFx0XHRmdW5jdGlvbihzdWJzY3JpcHRpb25JZCkge1xuXHRcdFx0XHRcdHRyeSB7XG5cdFx0XHRcdFx0XHRsZXQgcmVzdWx0ID0gRXZlbnQuU3Vic2NyaXB0aW9uc1tldmVudElkXVtzdWJzY3JpcHRpb25JZF0oZGF0YSk7XG5cblx0XHRcdFx0XHRcdGlmIChjbGllbnRDYWxsYmFjaykge1xuXHRcdFx0XHRcdFx0XHRjbGllbnRDYWxsYmFjayhyZXN1bHQpO1xuXHRcdFx0XHRcdFx0fVxuXHRcdFx0XHRcdH0gY2F0Y2ggKGVycm9yKSB7XG5cdFx0XHRcdFx0XHRpZiAoY2xpZW50RXJyb3JDYWxsYmFjaykge1xuXHRcdFx0XHRcdFx0XHRjbGllbnRFcnJvckNhbGxiYWNrKGVycm9yKTtcblx0XHRcdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0XHRcdGNvbnNvbGUuZXJyb3IoZXJyb3IpO1xuXHRcdFx0XHRcdFx0XHR0aHJvdyBlcnJvcjtcblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHR9XG5cdFx0XHRcdH1cblx0XHRcdClcblx0XHR9XG5cdH1cblxuXHRlbWl0KFxuXHRcdGV2ZW50TmFtZSxcblx0XHRkYXRhLFxuXHRcdGNsaWVudENhbGxiYWNrLFxuXHRcdGNsaWVudEVycm9yQ2FsbGJhY2tcblx0KSB7XG5cdFx0cmV0dXJuIEV2ZW50LkVtaXQoXG5cdFx0XHRldmVudE5hbWUsXG5cdFx0XHRkYXRhLFxuXHRcdFx0Y2xpZW50Q2FsbGJhY2ssXG5cdFx0XHRjbGllbnRFcnJvckNhbGxiYWNrXG5cdFx0KTtcblx0fVxuXG5cdC8qKlxuXHQgKiBFeGVjdXRlIHRoZSBmdW5jdGlvbnMgd2hpY2ggc3Vic2NyaWJlIHRvIHRoaXMgZXZlbnQsIGJ1dCBkb24ndCBwcm9jZXNzIHRoZSBjbGllbnQgY2FsbGJhY2sgLSB0aGUgc3Vic2NyaXB0aW9uIGZ1bmN0aW9uXG5cdCAqIHNob3VsZCBleGVjdXRlIHRoZSBjbGllbnQgY2FsbGJhY2tcblx0ICogQHBhcmFtIGV2ZW50SWRcblx0ICogQHBhcmFtIGRhdGFcblx0ICogQHBhcmFtIGNsaWVudENhbGxiYWNrXG5cdCAqIEBwYXJhbSBjbGllbnRFcnJvckNhbGxiYWNrXG5cdCAqIEByZXR1cm5zIHtudW1iZXJ9XG5cdCAqIEBjb25zdHJ1Y3RvclxuXHQgKi9cblx0c3RhdGljIEFzeW5jKFxuXHRcdGV2ZW50SWQsXG5cdFx0ZGF0YSxcblx0XHRjbGllbnRDYWxsYmFjayxcblx0XHRjbGllbnRFcnJvckNhbGxiYWNrXG5cdCkge1xuXHRcdGlmIChFdmVudC5TdWJzY3JpcHRpb25zLmhhc093blByb3BlcnR5KGV2ZW50SWQpKSB7XG5cblx0XHRcdGxldCBzdWJzY3JpcHRpb25JZHMgPSBPYmplY3Qua2V5cyhFdmVudC5TdWJzY3JpcHRpb25zW2V2ZW50SWRdKTtcblxuXHRcdFx0c3Vic2NyaXB0aW9uSWRzLm1hcChcblx0XHRcdFx0ZnVuY3Rpb24oc3Vic2NyaXB0aW9uSWQpIHtcblx0XHRcdFx0XHR0cnkge1xuXHRcdFx0XHRcdFx0RXZlbnQuU3Vic2NyaXB0aW9uc1tldmVudElkXVtzdWJzY3JpcHRpb25JZF0oZGF0YSwgY2xpZW50Q2FsbGJhY2ssIGNsaWVudEVycm9yQ2FsbGJhY2spO1xuXHRcdFx0XHRcdH0gY2F0Y2ggKGVycm9yKSB7XG5cdFx0XHRcdFx0XHRpZiAoY2xpZW50RXJyb3JDYWxsYmFjaykge1xuXHRcdFx0XHRcdFx0XHRjbGllbnRFcnJvckNhbGxiYWNrKGVycm9yKTtcblx0XHRcdFx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdFx0XHRcdGNvbnNvbGUuZXJyb3IoZXJyb3IpO1xuXHRcdFx0XHRcdFx0XHR0aHJvdyBlcnJvcjtcblx0XHRcdFx0XHRcdH1cblx0XHRcdFx0XHR9XG5cdFx0XHRcdH1cblx0XHRcdClcblx0XHR9XG5cdH07XG4gIC8vQ1VTVE9NX0lNUExFTUVOVEFUSU9OX0VORFxuXG59XG5cbi8vQ1VTVE9NX09VVF9PRl9DTEFTU19JTVBMRU1FTlRBVElPTl9TVEFSVFxuXG4vL0VWRU5UX0dFTkVSQVRFRF9TVEFSVFxuRXZlbnQuQ09NUE9ORU5UX0lOSVRJQUxJWkVEID0gMHgxO1xuRXZlbnQuQ1JFQVRFX0lOU1RBTkNFX0JFRk9SRSA9IDB4MjtcbkV2ZW50LkRJU1BPU0VfSU5TVEFOQ0UgPSAweDM7XG5FdmVudC5ESVNQT1NFX09CSkVDVCA9IDB4NDtcbkV2ZW50LkdFVF9SVU5USU1FID0gMHg1O1xuRXZlbnQuR0VUX1dJTkRPV19TSVpFID0gMHg2O1xuRXZlbnQuSU5TVEFOQ0VfQ1JFQVRFRCA9IDB4NztcbkV2ZW50LklOU1RBTkNFX0RJU1BPU0VEID0gMHg4O1xuRXZlbnQuT0JKRUNUX0NSRUFURUQgPSAweDk7XG5FdmVudC5PQkpFQ1RfSU5JVElBTElaRUQgPSAweGE7XG5FdmVudC5QQVVTRSA9IDB4YjtcbkV2ZW50LlJFU1RBUlQgPSAweGM7XG5FdmVudC5TVEFSVCA9IDB4ZDtcbkV2ZW50LlVQREFURV9GUk9NX0lOU1RBTkNFX0FGVEVSID0gMHhlO1xuRXZlbnQuVVBEQVRFX0ZST01fSU5TVEFOQ0VfQkVGT1JFID0gMHhmO1xuRXZlbnQuVVBEQVRFX0lOU1RBTkNFX0FGVEVSID0gMHgxMDtcbkV2ZW50LlVQREFURV9JTlNUQU5DRV9CRUZPUkUgPSAweDExO1xuRXZlbnQuTUFYX0VWRU5UUyA9IDB4MTI7XG5cbkV2ZW50LkdldEV2ZW50TmFtZSA9IGZ1bmN0aW9uKGV2ZW50SWQpIHtcblxuXHRzd2l0Y2goZXZlbnRJZCkge1xuXHRcdGNhc2UgMHgxIDogcmV0dXJuICdjb21wb25lbnRfaW5pdGlhbGl6ZWQnO1xuXHRcdGNhc2UgMHgyIDogcmV0dXJuICdjcmVhdGVfaW5zdGFuY2VfYmVmb3JlJztcblx0XHRjYXNlIDB4MyA6IHJldHVybiAnZGlzcG9zZV9pbnN0YW5jZSc7XG5cdFx0Y2FzZSAweDQgOiByZXR1cm4gJ2Rpc3Bvc2Vfb2JqZWN0Jztcblx0XHRjYXNlIDB4NSA6IHJldHVybiAnZ2V0X3J1bnRpbWUnO1xuXHRcdGNhc2UgMHg2IDogcmV0dXJuICdnZXRfd2luZG93X3NpemUnO1xuXHRcdGNhc2UgMHg3IDogcmV0dXJuICdpbnN0YW5jZV9jcmVhdGVkJztcblx0XHRjYXNlIDB4OCA6IHJldHVybiAnaW5zdGFuY2VfZGlzcG9zZWQnO1xuXHRcdGNhc2UgMHg5IDogcmV0dXJuICdvYmplY3RfY3JlYXRlZCc7XG5cdFx0Y2FzZSAweGEgOiByZXR1cm4gJ29iamVjdF9pbml0aWFsaXplZCc7XG5cdFx0Y2FzZSAweGIgOiByZXR1cm4gJ3BhdXNlJztcblx0XHRjYXNlIDB4YyA6IHJldHVybiAncmVzdGFydCc7XG5cdFx0Y2FzZSAweGQgOiByZXR1cm4gJ3N0YXJ0Jztcblx0XHRjYXNlIDB4ZSA6IHJldHVybiAndXBkYXRlX2Zyb21faW5zdGFuY2VfYWZ0ZXInO1xuXHRcdGNhc2UgMHhmIDogcmV0dXJuICd1cGRhdGVfZnJvbV9pbnN0YW5jZV9iZWZvcmUnO1xuXHRcdGNhc2UgMHgxMCA6IHJldHVybiAndXBkYXRlX2luc3RhbmNlX2FmdGVyJztcblx0XHRjYXNlIDB4MTEgOiByZXR1cm4gJ3VwZGF0ZV9pbnN0YW5jZV9iZWZvcmUnO1xuXHRcdGRlZmF1bHQgOlxuXHRcdFx0dGhyb3cgbmV3IEVycm9yKCdFdmVudCB0eXBlIG5vdCBkZWZpbmVkIDogJyArIGV2ZW50SWQpO1xuXHR9XG5cbn07XG4vL0VWRU5UX0dFTkVSQVRFRF9FTkRcblxuLy9DVVNUT01fT1VUX09GX0NMQVNTX0lNUExFTUVOVEFUSU9OX0VORFxuXG5tb2R1bGUuZXhwb3J0cyA9IEV2ZW50OyIsImNvbnN0IFV0aWxzID0gcmVxdWlyZSgncjMtdXRpbHMnKTtcbmNvbnN0IEV2ZW50ID0gcmVxdWlyZSgncjMtZXZlbnQuanMnKTtcblxuLyoqXG5cbiBPUFRJT05TX1NUQVJUXG4gaWQ9VXRpbHMuUmFuZG9tSWQoMTApXG4gbmFtZT10aGlzLmNvbnN0cnVjdG9yLm5hbWUgKyAnKCcgKyBvcHRpb25zLmlkICsgJyknXG4gcmVnaXN0ZXI9dHJ1ZVxuIE9QVElPTlNfRU5EXG5cbiBJTlNUQU5DRV9PUFRJT05TX01BUFBJTkdfU1RBUlRcbiBJTlNUQU5DRV9PUFRJT05TX01BUFBJTkdfRU5EXG5cbiBMSU5LRURfT0JKRUNUU19TVEFSVFxuIExJTktFRF9PQkpFQ1RTX0VORFxuXG4gRVhDTFVERURfRlJPTV9JTlNUQU5DRV9PUFRJT05TX1NUQVJUXG4gaWRcbiBuYW1lXG4gcmVnaXN0ZXJcbiBFWENMVURFRF9GUk9NX0lOU1RBTkNFX09QVElPTlNfRU5EXG5cbiAqKi9cblxuY2xhc3MgUjNPYmplY3QgZXh0ZW5kcyBFdmVudCB7XG5cbiAgLy9DT05TVFJVQ1RPUl9FWFRFTkRTX1RFTVBMQVRFX1NUQVJUXG4gIGNvbnN0cnVjdG9yKG9wdGlvbnMpIHtcblxuICAgIGlmIChVdGlscy5VbmRlZmluZWRPck51bGwob3B0aW9ucykpIHtcbiAgICAgIG9wdGlvbnMgPSB7fTtcbiAgICB9XG5cbiAgICBzdXBlcihvcHRpb25zKTtcblxuICAgIHRoaXMuZW1pdChFdmVudC5PQkpFQ1RfQ1JFQVRFRCwgdGhpcyk7XG5cbiAgICAvL09QVElPTlNfSU5JVF9TVEFSVFxuICAgIGlmICh0eXBlb2Ygb3B0aW9ucyA9PT0gJ3VuZGVmaW5lZCcpIHtcbiAgICAgICAgb3B0aW9ucyA9IHt9O1xuICAgIH1cbiAgICBpZiAoVXRpbHMuVW5kZWZpbmVkT3JOdWxsKG9wdGlvbnMuaWQpKSB7XG4gICAgICBvcHRpb25zLmlkID0gVXRpbHMuUmFuZG9tSWQoMTApO1xuICAgIH1cbiAgICBpZiAoVXRpbHMuVW5kZWZpbmVkT3JOdWxsKG9wdGlvbnMubmFtZSkpIHtcbiAgICAgIG9wdGlvbnMubmFtZSA9IHRoaXMuY29uc3RydWN0b3IubmFtZSArICcoJyArIG9wdGlvbnMuaWQgKyAnKSc7XG4gICAgfVxuICAgIGlmIChVdGlscy5VbmRlZmluZWRPck51bGwob3B0aW9ucy5yZWdpc3RlcikpIHtcbiAgICAgIG9wdGlvbnMucmVnaXN0ZXIgPSB0cnVlO1xuICAgIH1cblx0XHQvL09QVElPTlNfSU5JVF9FTkRcblxuICAgIC8vQ1VTVE9NX09QVElPTlNfSU5JVF9TVEFSVFxuICAgIC8vQ1VTVE9NX09QVElPTlNfSU5JVF9FTkRcblxuICAgIE9iamVjdC5hc3NpZ24odGhpcywgb3B0aW9ucyk7XG5cbiAgICAvL0NVU1RPTV9CRUZPUkVfSU5JVF9TVEFSVFxuICAgIC8vQ1VTVE9NX0JFRk9SRV9JTklUX0VORFxuXG4gICAgdGhpcy5lbWl0KEV2ZW50Lk9CSkVDVF9JTklUSUFMSVpFRCwgdGhpcyk7XG4gIH1cblx0Ly9DT05TVFJVQ1RPUl9FWFRFTkRTX1RFTVBMQVRFX0VORFxuXG4gIC8vQ1JFQVRFX0lOU1RBTkNFX1RFTVBMQVRFX1NUQVJUXG4gIGNyZWF0ZUluc3RhbmNlKCkge1xuXG4gICAgLy9DUkVBVEVfSU5TVEFOQ0VfQkVGT1JFX1NUQVJUXG4gICAgdGhpcy5lbWl0KEV2ZW50LkNSRUFURV9JTlNUQU5DRV9CRUZPUkUsIHRoaXMpO1xuXHRcdC8vQ1JFQVRFX0lOU1RBTkNFX0JFRk9SRV9FTkRcblxuICAgIC8vQ1VTVE9NX0NSRUFURV9JTlNUQU5DRV9TVEFSVFxuICAgIC8vQ1VTVE9NX0NSRUFURV9JTlNUQU5DRV9FTkRcblxuICAgIC8vQ1JFQVRFX0lOU1RBTkNFX0FGVEVSX1NUQVJUXG4gICAgdGhpc1t0aGlzLnJ1bnRpbWVdLmNyZWF0ZUluc3RhbmNlKFxuICAgICAgdGhpcyxcbiAgICAgIHtcbiAgICAgICAgLy9DUkVBVEVfSU5TVEFOQ0VfT1BUSU9OU19TVEFSVFxuICAgICAgICAvL0NSRUFURV9JTlNUQU5DRV9PUFRJT05TX0VORFxuICAgICAgfVxuICAgIClcblxuICAgIHRoaXMuZW1pdChFdmVudC5JTlNUQU5DRV9DUkVBVEVELCB0aGlzKTtcblx0XHQvL0NSRUFURV9JTlNUQU5DRV9BRlRFUl9FTkRcblxuICB9XG5cdC8vQ1JFQVRFX0lOU1RBTkNFX1RFTVBMQVRFX0VORFxuXG4gIC8vVVBEQVRFX0lOU1RBTkNFX1RFTVBMQVRFX1NUQVJUXG4gIHVwZGF0ZUluc3RhbmNlKHByb3BlcnR5KSB7XG5cbiAgICAvL1VQREFURV9JTlNUQU5DRV9CRUZPUkVfU1RBUlRcbiAgICB0aGlzLmVtaXQoRXZlbnQuVVBEQVRFX0lOU1RBTkNFX0JFRk9SRSwgdGhpcyk7XG5cdFx0Ly9VUERBVEVfSU5TVEFOQ0VfQkVGT1JFX0VORFxuXG4gICAgLy9VUERBVEVfSU5TVEFOQ0VfT1BUSU9OU19TVEFSVFxuXHRcdC8vVVBEQVRFX0lOU1RBTkNFX09QVElPTlNfRU5EXG5cbiAgICAvL0NVU1RPTV9VUERBVEVfSU5TVEFOQ0VfU1RBUlRcbiAgICAvL0NVU1RPTV9VUERBVEVfSU5TVEFOQ0VfRU5EXG5cbiAgICAvL1VQREFURV9JTlNUQU5DRV9BRlRFUl9TVEFSVFxuICAgIHRoaXMuZW1pdChFdmVudC5VUERBVEVfSU5TVEFOQ0VfQUZURVIsIHRoaXMpO1xuXHRcdC8vVVBEQVRFX0lOU1RBTkNFX0FGVEVSX0VORFxuXG4gIH1cblx0Ly9VUERBVEVfSU5TVEFOQ0VfVEVNUExBVEVfRU5EXG5cbiAgLy9VUERBVEVfRlJPTV9JTlNUQU5DRV9URU1QTEFURV9TVEFSVFxuICB1cGRhdGVGcm9tSW5zdGFuY2UocHJvcGVydHkpIHtcblxuICAgIC8vVVBEQVRFX0ZST01fSU5TVEFOQ0VfQkVGT1JFX1NUQVJUXG4gICAgdGhpcy5lbWl0KEV2ZW50LlVQREFURV9GUk9NX0lOU1RBTkNFX0JFRk9SRSwgdGhpcyk7XG5cdFx0Ly9VUERBVEVfRlJPTV9JTlNUQU5DRV9CRUZPUkVfRU5EXG5cbiAgICAvL1VQREFURV9GUk9NX0lOU1RBTkNFX09QVElPTlNfU1RBUlRcblx0XHQvL1VQREFURV9GUk9NX0lOU1RBTkNFX09QVElPTlNfRU5EXG5cbiAgICAvL0NVU1RPTV9VUERBVEVfRlJPTV9JTlNUQU5DRV9TVEFSVFxuICAgIC8vQ1VTVE9NX1VQREFURV9GUk9NX0lOU1RBTkNFX0VORFxuXG4gICAgLy9VUERBVEVfRlJPTV9JTlNUQU5DRV9BRlRFUl9TVEFSVFxuICAgIHRoaXMuZW1pdChFdmVudC5VUERBVEVfRlJPTV9JTlNUQU5DRV9BRlRFUiwgdGhpcyk7XG5cdFx0Ly9VUERBVEVfRlJPTV9JTlNUQU5DRV9BRlRFUl9FTkRcblxuICB9XG5cdC8vVVBEQVRFX0ZST01fSU5TVEFOQ0VfVEVNUExBVEVfRU5EXG5cbiAgLy9ESVNQT1NFX1RFTVBMQVRFX1NUQVJUXG4gIGRpc3Bvc2UoKSB7XG5cbiAgICAvL0RJU1BPU0VfQkVGT1JFX1NUQVJUXG4gICAgdGhpcy5zdWJzY3JpYmUoXG4gICAgICBFdmVudC5JTlNUQU5DRV9ESVNQT1NFRCxcbiAgICAgIGZ1bmN0aW9uKG9iamVjdCkge1xuICAgICAgICBpZiAob2JqZWN0ID09PSB0aGlzKSB7XG4gICAgICAgICAgdGhpcy5lbWl0KEV2ZW50LkRJU1BPU0VfT0JKRUNULCB0aGlzKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICk7XG5cdFx0Ly9ESVNQT1NFX0JFRk9SRV9FTkRcblxuICAgIC8vQ1VTVE9NX0RJU1BPU0VfU1RBUlRcbiAgICAvL0NVU1RPTV9ESVNQT1NFX0VORFxuXG4gICAgLy9ESVNQT1NFX0FGVEVSX1NUQVJUXG4gICAgdGhpcy5kaXNwb3NlSW5zdGFuY2UoKTtcblx0XHQvL0RJU1BPU0VfQUZURVJfRU5EXG5cbiAgfVxuXHQvL0RJU1BPU0VfVEVNUExBVEVfRU5EXG5cbiAgLy9ESVNQT1NFX0lOU1RBTkNFX1RFTVBMQVRFX1NUQVJUXG4gIGRpc3Bvc2VJbnN0YW5jZSgpIHtcblxuICAgIC8vRElTUE9TRV9JTlNUQU5DRV9CRUZPUkVfU1RBUlRcbiAgICBjb25zb2xlLmxvZygnRGlzcG9zaW5nIGluc3RhbmNlIG9mICcgKyB0aGlzLm5hbWUpO1xuXHRcdC8vRElTUE9TRV9JTlNUQU5DRV9CRUZPUkVfRU5EXG5cbiAgICAvL0NVU1RPTV9ESVNQT1NFX0lOU1RBTkNFX1NUQVJUXG4gICAgLy9DVVNUT01fRElTUE9TRV9JTlNUQU5DRV9FTkRcblxuICAgIC8vRElTUE9TRV9JTlNUQU5DRV9BRlRFUl9TVEFSVFxuICAgIHRoaXMuZW1pdChFdmVudC5ESVNQT1NFX0lOU1RBTkNFLCB0aGlzKTtcblx0XHQvL0RJU1BPU0VfSU5TVEFOQ0VfQUZURVJfRU5EXG5cbiAgfVxuXHQvL0RJU1BPU0VfSU5TVEFOQ0VfVEVNUExBVEVfRU5EXG5cbiAgLy9DVVNUT01fSU1QTEVNRU5UQVRJT05fU1RBUlRcbiAgLy9DVVNUT01fSU1QTEVNRU5UQVRJT05fRU5EXG5cbn1cblxuLy9DVVNUT01fT1VUX09GX0NMQVNTX0lNUExFTUVOVEFUSU9OX1NUQVJUXG4vL0NVU1RPTV9PVVRfT0ZfQ0xBU1NfSU1QTEVNRU5UQVRJT05fRU5EXG5cbm1vZHVsZS5leHBvcnRzID0gUjNPYmplY3Q7IiwiY29uc3QgU3lzdGVtID0gcmVxdWlyZSgnLi9yMy1zeXN0ZW0uanMnKTtcbmNvbnN0IFN5c3RlbUxpbmtpbmcgPSByZXF1aXJlKCcuL3IzLXN5c3RlbS1saW5raW5nLmpzJyk7XG5jb25zdCBTeXN0ZW1Tb2NrZXQgPSByZXF1aXJlKCcuL3IzLXN5c3RlbS1zb2NrZXQuanMnKTtcblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gIFN5c3RlbSxcbiAgU3lzdGVtTGlua2luZyxcbiAgU3lzdGVtU29ja2V0XG59IiwiY29uc3QgU3lzdGVtID0gcmVxdWlyZSgnLi9yMy1zeXN0ZW0nKTtcbmNvbnN0IFIzT2JqZWN0ID0gcmVxdWlyZSgnLi4vcjMtcjMtb2JqZWN0Jyk7XG5jb25zdCBFdmVudCA9IHJlcXVpcmUoJy4uL3IzLWV2ZW50Jyk7XG5cbmNsYXNzIExpbmtpbmdTeXN0ZW0gZXh0ZW5kcyBTeXN0ZW0ge1xuXG5cdHN0YXRpYyBzdGFydCgpIHtcblxuXHRcdHN1cGVyLnN0YXJ0KCk7XG5cdFx0XG5cdFx0Y29uc29sZS5sb2coJ3N0YXJ0aW5nIGxpbmtpbmcgc3lzdGVtJyk7XG5cblx0XHRFdmVudC5TdWJzY3JpYmUoXG5cdFx0XHRFdmVudC5PQkpFQ1RfQ1JFQVRFRCxcblx0XHRcdCgpID0+IHtcblx0XHRcdFx0Y29uc29sZS5sb2coJ2xpbmtpbmcgc3lzdGVtIGRpc2NvdmVyZWQgYW4gb2JqZWN0Jyk7XG5cdFx0XHR9XG5cdFx0KTtcblxuXHRcdGxldCBvYmplY3QgPSBuZXcgUjNPYmplY3QoKTtcblxuXHRcdHJldHVybiB0cnVlO1xuXG5cdH1cdFxuXG59XG5cbm1vZHVsZS5leHBvcnRzID0gTGlua2luZ1N5c3RlbTtcbiIsImNvbnN0IFN5c3RlbSA9IHJlcXVpcmUoJy4vcjMtc3lzdGVtLmpzJyk7XG5cbmNsYXNzIFNvY2tldFN5c3RlbSBleHRlbmRzIFN5c3RlbSB7XG5cblx0c3RhdGljIHN0YXJ0KCkge1xuXG5cdFx0c3VwZXIuc3RhcnQoKTtcblx0XHRcblx0XHRjb25zb2xlLmxvZygnc3RhcnRpbmcgc29ja2V0IHN5c3RlbScpO1xuXG5cdFx0cmV0dXJuIHRydWU7XG5cblx0fVxuXG59XG5cbm1vZHVsZS5leHBvcnRzID0gU29ja2V0U3lzdGVtOyIsImNsYXNzIFN5c3RlbSB7XG5cblx0c3RhdGljIHN0YXJ0KCkge1xuXHRcdGNvbnNvbGUubG9nKCdzdGFydGluZyBhIHN5c3RlbScpO1x0XG5cdH1cblxufVxuXG5tb2R1bGUuZXhwb3J0cyA9IFN5c3RlbTtcbiIsImNvbnN0IHtTeXN0ZW0sIFN5c3RlbUxpbmtpbmcsIFN5c3RlbVNvY2tldH0gPSByZXF1aXJlKCcuL3IzLXN5c3RlbScpO1xuY29uc3QgUjNPYmplY3QgPSByZXF1aXJlKCcuL3IzLXIzLW9iamVjdCcpO1xuXG5jbGFzcyBSMyB7XG5cblx0Y29uc3RydWN0b3IoKSB7XG5cdH1cblxuXHRzdGF0aWMgdmVyc2lvbigpIHtcblx0XHRyZXR1cm4gJ19fREFURV9fJztcblx0fVxuXG59XG5cblIzLlIzT2JqZWN0IFx0XHQ9IFIzT2JqZWN0O1xuUjMuU3lzdGVtIFx0XHRcdD0gU3lzdGVtO1xuUjMuU3lzdGVtLkxpbmtpbmdcdD0gU3lzdGVtTGlua2luZztcblIzLlN5c3RlbS5Tb2NrZXRcdD0gU3lzdGVtU29ja2V0O1xuXG5tb2R1bGUuZXhwb3J0cyA9IFIzO1xuIl0sInNvdXJjZVJvb3QiOiIifQ== \ No newline at end of file diff --git a/src/r3/r3-vector-applier-again.js b/src/r3/r3-component.js similarity index 67% rename from src/r3/r3-vector-applier-again.js rename to src/r3/r3-component.js index fcbba54..201c0d4 100644 --- a/src/r3/r3-vector-applier-again.js +++ b/src/r3/r3-component.js @@ -1,12 +1,11 @@ +const Event = require('r3-event'); const Utils = require('r3-utils'); -const Event = require('r3-event.js'); +const R3Object = require('r3-r3-object.js'); /** OPTIONS_START - a=2.0 - b=3.0 - something='hello' + runtime='default' OPTIONS_END INSTANCE_OPTIONS_MAPPING_START @@ -16,12 +15,12 @@ const Event = require('r3-event.js'); LINKED_OBJECTS_END EXCLUDED_FROM_INSTANCE_OPTIONS_START - b + runtime EXCLUDED_FROM_INSTANCE_OPTIONS_END **/ -class VectorApplierAgain extends Event { +class Component extends R3Object { //CONSTRUCTOR_EXTENDS_TEMPLATE_START constructor(options) { @@ -30,22 +29,16 @@ class VectorApplierAgain extends Event { options = {}; } - options.id = Utils.RandomId(10); - options.name = 'VectorApplierAgain (' + options.id + ')'; - super(options); this.emit(Event.OBJECT_CREATED, this); //OPTIONS_INIT_START - if (Utils.UndefinedOrNull(options.a)) { - options.a = 2.0; + if (typeof options === 'undefined') { + options = {}; } - if (Utils.UndefinedOrNull(options.b)) { - options.b = 3.0; - } - if (Utils.UndefinedOrNull(options.something)) { - options.something = 'hello'; + if (Utils.UndefinedOrNull(options.runtime)) { + options.runtime = 'default'; } //OPTIONS_INIT_END @@ -55,6 +48,7 @@ class VectorApplierAgain extends Event { Object.assign(this, options); //CUSTOM_BEFORE_INIT_START + this.emit(Event.COMPONENT_INITIALIZED, this); //CUSTOM_BEFORE_INIT_END this.emit(Event.OBJECT_INITIALIZED, this); @@ -65,26 +59,21 @@ class VectorApplierAgain extends Event { createInstance() { //CREATE_INSTANCE_BEFORE_START - super.createInstance(); - this.emit(Event.CREATE_INSTANCE, this); - - if (this.runtime === 'graphics') { - this.graphics.createInstance( - { - //CREATE_INSTANCE_OPTIONS_START - 'a': this.a, - 'something': this.something - //CREATE_INSTANCE_OPTIONS_END - }, - this - ) - } + this.emit(Event.CREATE_INSTANCE_BEFORE, this); //CREATE_INSTANCE_BEFORE_END //CUSTOM_CREATE_INSTANCE_START //CUSTOM_CREATE_INSTANCE_END //CREATE_INSTANCE_AFTER_START + this[this.runtime].createInstance( + this, + { + //CREATE_INSTANCE_OPTIONS_START + //CREATE_INSTANCE_OPTIONS_END + } + ) + this.emit(Event.INSTANCE_CREATED, this); //CREATE_INSTANCE_AFTER_END @@ -99,14 +88,6 @@ class VectorApplierAgain extends Event { //UPDATE_INSTANCE_BEFORE_END //UPDATE_INSTANCE_OPTIONS_START - if (property === 'a') { - this.instance.a = this.a; - return; - } - if (property === 'something') { - this.instance.something = this.something; - return; - } //UPDATE_INSTANCE_OPTIONS_END //CUSTOM_UPDATE_INSTANCE_START @@ -127,14 +108,6 @@ class VectorApplierAgain extends Event { //UPDATE_FROM_INSTANCE_BEFORE_END //UPDATE_FROM_INSTANCE_OPTIONS_START - if (property === 'a') { - this.a = this.instance.a; - return; - } - if (property === 'something') { - this.something = this.instance.something; - return; - } //UPDATE_FROM_INSTANCE_OPTIONS_END //CUSTOM_UPDATE_FROM_INSTANCE_START @@ -151,14 +124,21 @@ class VectorApplierAgain extends Event { dispose() { //DISPOSE_BEFORE_START - this.emit(Event.DISPOSE_OBJECT, this); + this.subscribe( + Event.INSTANCE_DISPOSED, + function(object) { + if (object === this) { + this.emit(Event.DISPOSE_OBJECT, this); + } + } + ); //DISPOSE_BEFORE_END //CUSTOM_DISPOSE_START //CUSTOM_DISPOSE_END //DISPOSE_AFTER_START - this.emit(Event.OBJECT_DISPOSED, this); + this.disposeInstance(); //DISPOSE_AFTER_END } @@ -168,15 +148,14 @@ class VectorApplierAgain extends Event { disposeInstance() { //DISPOSE_INSTANCE_BEFORE_START - super.disposeInstance(); - this.emit(Event.DISPOSE_INSTANCE, this); + console.log('Disposing instance of ' + this.name); //DISPOSE_INSTANCE_BEFORE_END //CUSTOM_DISPOSE_INSTANCE_START //CUSTOM_DISPOSE_INSTANCE_END //DISPOSE_INSTANCE_AFTER_START - this.emit(Event.INSTANCE_DISPOSED, this); + this.emit(Event.DISPOSE_INSTANCE, this); //DISPOSE_INSTANCE_AFTER_END } @@ -190,4 +169,4 @@ class VectorApplierAgain extends Event { //CUSTOM_OUT_OF_CLASS_IMPLEMENTATION_START //CUSTOM_OUT_OF_CLASS_IMPLEMENTATION_END -module.exports = VectorApplierAgain; \ No newline at end of file +module.exports = Component; \ No newline at end of file diff --git a/src/r3/r3-event.bak.js b/src/r3/r3-event.bak.js deleted file mode 100644 index 48aa5fa..0000000 --- a/src/r3/r3-event.bak.js +++ /dev/null @@ -1,60 +0,0 @@ -const Utils = require('./r3-utils'); - -class Event { - - constructor(options) { - console.log('Event created'); - } - - - -} - -//EVENT_GENERATED_START -Event.CREATE_INSTANCE = 0x1; -Event.DISPOSE_INSTANCE = 0x2; -Event.DISPOSE_OBJECT = 0x3; -Event.GET_RUNTIME = 0x4; -Event.GET_WINDOW_SIZE = 0x5; -Event.INSTANCE_CREATED = 0x6; -Event.INSTANCE_DISPOSED = 0x7; -Event.OBJECT_CREATED = 0x8; -Event.OBJECT_DISPOSED = 0x9; -Event.OBJECT_INITIALIZED = 0xa; -Event.PAUSE = 0xb; -Event.RESTART = 0xc; -Event.START = 0xd; -Event.UPDATE_FROM_INSTANCE_AFTER = 0xe; -Event.UPDATE_FROM_INSTANCE_BEFORE = 0xf; -Event.UPDATE_INSTANCE_AFTER = 0x10; -Event.UPDATE_INSTANCE_BEFORE = 0x11; -Event.MAX_EVENTS = 0x12; - -Event.GetEventName = function(eventId) { - - switch(eventId) { - case 0x1 : return 'create_instance'; - case 0x2 : return 'dispose_instance'; - case 0x3 : return 'dispose_object'; - case 0x4 : return 'get_runtime'; - case 0x5 : return 'get_window_size'; - case 0x6 : return 'instance_created'; - case 0x7 : return 'instance_disposed'; - case 0x8 : return 'object_created'; - case 0x9 : return 'object_disposed'; - case 0xa : return 'object_initialized'; - case 0xb : return 'pause'; - case 0xc : return 'restart'; - case 0xd : return 'start'; - case 0xe : return 'update_from_instance_after'; - case 0xf : return 'update_from_instance_before'; - case 0x10 : return 'update_instance_after'; - case 0x11 : return 'update_instance_before'; - default : - throw new Error('Event type not defined : ' + eventId); - } - -}; -//EVENT_GENERATED_END - -module.exports = Event; diff --git a/src/r3/r3-event.js b/src/r3/r3-event.js index 01f62bd..de8d73d 100644 --- a/src/r3/r3-event.js +++ b/src/r3/r3-event.js @@ -1,4 +1,3 @@ -const Event = require('r3-event'); const Utils = require('r3-utils'); /** @@ -6,15 +5,6 @@ const Utils = require('r3-utils'); OPTIONS_START OPTIONS_END - INSTANCE_OPTIONS_MAPPING_START - INSTANCE_OPTIONS_MAPPING_END - - LINKED_OBJECTS_START - LINKED_OBJECTS_END - - EXCLUDED_FROM_INSTANCE_OPTIONS_START - EXCLUDED_FROM_INSTANCE_OPTIONS_END - **/ class Event { @@ -22,16 +12,12 @@ class Event { //CONSTRUCTOR_TEMPLATE_START constructor(options) { - if (Utils.UndefinedOrNull(options)) { - options = {}; - } - - options.id = Utils.RandomId(10); - options.name = 'Event (' + options.id + ')'; - Event.Emit(Event.OBJECT_CREATED, this); //OPTIONS_INIT_START + if (typeof options === 'undefined') { + options = {}; + } //OPTIONS_INIT_END //CUSTOM_OPTIONS_INIT_START @@ -46,110 +32,6 @@ class Event { } //CONSTRUCTOR_TEMPLATE_END - //CREATE_INSTANCE_TEMPLATE_START - createInstance() { - - //CREATE_INSTANCE_BEFORE_START - super.createInstance(); - this.emit(Event.CREATE_INSTANCE, this); - - if (this.runtime === 'graphics') { - this.graphics.createInstance( - { - //CREATE_INSTANCE_OPTIONS_START - - //CREATE_INSTANCE_OPTIONS_END - }, - this - ) - } - //CREATE_INSTANCE_BEFORE_END - - //CUSTOM_CREATE_INSTANCE_START - //CUSTOM_CREATE_INSTANCE_END - - //CREATE_INSTANCE_AFTER_START - this.emit(Event.INSTANCE_CREATED, this); - //CREATE_INSTANCE_AFTER_END - - } - //CREATE_INSTANCE_TEMPLATE_END - - //UPDATE_INSTANCE_TEMPLATE_START - updateInstance(property) { - - //UPDATE_INSTANCE_BEFORE_START - this.emit(Event.UPDATE_INSTANCE_BEFORE, this); - //UPDATE_INSTANCE_BEFORE_END - - //UPDATE_INSTANCE_OPTIONS_START - //UPDATE_INSTANCE_OPTIONS_END - - //CUSTOM_UPDATE_INSTANCE_START - //CUSTOM_UPDATE_INSTANCE_END - - //UPDATE_INSTANCE_AFTER_START - this.emit(Event.UPDATE_INSTANCE_AFTER, this); - //UPDATE_INSTANCE_AFTER_END - - } - //UPDATE_INSTANCE_TEMPLATE_END - - //UPDATE_FROM_INSTANCE_TEMPLATE_START - updateFromInstance(property) { - - //UPDATE_FROM_INSTANCE_BEFORE_START - this.emit(Event.UPDATE_FROM_INSTANCE_BEFORE, this); - //UPDATE_FROM_INSTANCE_BEFORE_END - - //UPDATE_FROM_INSTANCE_OPTIONS_START - //UPDATE_FROM_INSTANCE_OPTIONS_END - - //CUSTOM_UPDATE_FROM_INSTANCE_START - //CUSTOM_UPDATE_FROM_INSTANCE_END - - //UPDATE_FROM_INSTANCE_AFTER_START - this.emit(Event.UPDATE_FROM_INSTANCE_AFTER, this); - //UPDATE_FROM_INSTANCE_AFTER_END - - } - //UPDATE_FROM_INSTANCE_TEMPLATE_END - - //DISPOSE_TEMPLATE_START - dispose() { - - //DISPOSE_BEFORE_START - this.emit(Event.DISPOSE_OBJECT, this); - //DISPOSE_BEFORE_END - - //CUSTOM_DISPOSE_START - //CUSTOM_DISPOSE_END - - //DISPOSE_AFTER_START - this.emit(Event.OBJECT_DISPOSED, this); - //DISPOSE_AFTER_END - - } - //DISPOSE_TEMPLATE_END - - //DISPOSE_INSTANCE_TEMPLATE_START - disposeInstance() { - - //DISPOSE_INSTANCE_BEFORE_START - super.disposeInstance(); - this.emit(Event.DISPOSE_INSTANCE, this); - //DISPOSE_INSTANCE_BEFORE_END - - //CUSTOM_DISPOSE_INSTANCE_START - //CUSTOM_DISPOSE_INSTANCE_END - - //DISPOSE_INSTANCE_AFTER_START - this.emit(Event.INSTANCE_DISPOSED, this); - //DISPOSE_INSTANCE_AFTER_END - - } - //DISPOSE_INSTANCE_TEMPLATE_END - //CUSTOM_IMPLEMENTATION_START /** @@ -317,15 +199,15 @@ class Event { //CUSTOM_OUT_OF_CLASS_IMPLEMENTATION_START //EVENT_GENERATED_START -Event.CREATE_INSTANCE = 0x1; -Event.DISPOSE_INSTANCE = 0x2; -Event.DISPOSE_OBJECT = 0x3; -Event.GET_RUNTIME = 0x4; -Event.GET_WINDOW_SIZE = 0x5; -Event.INSTANCE_CREATED = 0x6; -Event.INSTANCE_DISPOSED = 0x7; -Event.OBJECT_CREATED = 0x8; -Event.OBJECT_DISPOSED = 0x9; +Event.COMPONENT_INITIALIZED = 0x1; +Event.CREATE_INSTANCE_BEFORE = 0x2; +Event.DISPOSE_INSTANCE = 0x3; +Event.DISPOSE_OBJECT = 0x4; +Event.GET_RUNTIME = 0x5; +Event.GET_WINDOW_SIZE = 0x6; +Event.INSTANCE_CREATED = 0x7; +Event.INSTANCE_DISPOSED = 0x8; +Event.OBJECT_CREATED = 0x9; Event.OBJECT_INITIALIZED = 0xa; Event.PAUSE = 0xb; Event.RESTART = 0xc; @@ -339,15 +221,15 @@ Event.MAX_EVENTS = 0x12; Event.GetEventName = function(eventId) { switch(eventId) { - case 0x1 : return 'create_instance'; - case 0x2 : return 'dispose_instance'; - case 0x3 : return 'dispose_object'; - case 0x4 : return 'get_runtime'; - case 0x5 : return 'get_window_size'; - case 0x6 : return 'instance_created'; - case 0x7 : return 'instance_disposed'; - case 0x8 : return 'object_created'; - case 0x9 : return 'object_disposed'; + case 0x1 : return 'component_initialized'; + case 0x2 : return 'create_instance_before'; + case 0x3 : return 'dispose_instance'; + case 0x4 : return 'dispose_object'; + case 0x5 : return 'get_runtime'; + case 0x6 : return 'get_window_size'; + case 0x7 : return 'instance_created'; + case 0x8 : return 'instance_disposed'; + case 0x9 : return 'object_created'; case 0xa : return 'object_initialized'; case 0xb : return 'pause'; case 0xc : return 'restart'; diff --git a/src/r3/r3-another-class.js b/src/r3/r3-project.js similarity index 62% rename from src/r3/r3-another-class.js rename to src/r3/r3-project.js index 69204d8..87ff87f 100644 --- a/src/r3/r3-another-class.js +++ b/src/r3/r3-project.js @@ -5,28 +5,26 @@ const R3Object = require('r3-r3-object.js'); /** OPTIONS_START - x=1 - y=2 - z=3 - register=true + isPublic=true + applicationMode=Project.APPLICATION_MODE_EDIT OPTIONS_END INSTANCE_OPTIONS_MAPPING_START - x=side.x - y=side.y - z=side.z INSTANCE_OPTIONS_MAPPING_END LINKED_OBJECTS_START + entities=[Entity] + controls=[Control] + images=[Image] + code=[CustomCode] LINKED_OBJECTS_END EXCLUDED_FROM_INSTANCE_OPTIONS_START - register EXCLUDED_FROM_INSTANCE_OPTIONS_END **/ -class AnotherClass extends R3Object { +class Project extends R3Object { //CONSTRUCTOR_EXTENDS_TEMPLATE_START constructor(options) { @@ -35,25 +33,19 @@ class AnotherClass extends R3Object { options = {}; } - options.id = Utils.RandomId(10); - options.name = 'AnotherClass (' + options.id + ')'; - super(options); this.emit(Event.OBJECT_CREATED, this); //OPTIONS_INIT_START - if (Utils.UndefinedOrNull(options.x)) { - options.x = 1; + if (typeof options === 'undefined') { + options = {}; } - if (Utils.UndefinedOrNull(options.y)) { - options.y = 2; + if (Utils.UndefinedOrNull(options.isPublic)) { + options.isPublic = true; } - if (Utils.UndefinedOrNull(options.z)) { - options.z = 3; - } - if (Utils.UndefinedOrNull(options.register)) { - options.register = true; + if (Utils.UndefinedOrNull(options.applicationMode)) { + options.applicationMode = Project.APPLICATION_MODE_EDIT; } //OPTIONS_INIT_END @@ -73,27 +65,23 @@ class AnotherClass extends R3Object { createInstance() { //CREATE_INSTANCE_BEFORE_START - super.createInstance(); - this.emit(Event.CREATE_INSTANCE, this); - - if (this.runtime === 'graphics') { - this.graphics.createInstance( - { - //CREATE_INSTANCE_OPTIONS_START - 'side.x': this.x, - 'side.y': this.y, - 'side.z': this.z - //CREATE_INSTANCE_OPTIONS_END - }, - this - ) - } + this.emit(Event.CREATE_INSTANCE_BEFORE, this); //CREATE_INSTANCE_BEFORE_END //CUSTOM_CREATE_INSTANCE_START //CUSTOM_CREATE_INSTANCE_END //CREATE_INSTANCE_AFTER_START + this[this.runtime].createInstance( + this, + { + //CREATE_INSTANCE_OPTIONS_START + 'isPublic': this.isPublic, + 'applicationMode': this.applicationMode + //CREATE_INSTANCE_OPTIONS_END + } + ) + this.emit(Event.INSTANCE_CREATED, this); //CREATE_INSTANCE_AFTER_END @@ -108,16 +96,12 @@ class AnotherClass extends R3Object { //UPDATE_INSTANCE_BEFORE_END //UPDATE_INSTANCE_OPTIONS_START - if (property === 'x') { - this.instance.side.x = this.x; + if (property === 'isPublic') { + this.instance.isPublic = this.isPublic; return; } - if (property === 'y') { - this.instance.side.y = this.y; - return; - } - if (property === 'z') { - this.instance.side.z = this.z; + if (property === 'applicationMode') { + this.instance.applicationMode = this.applicationMode; return; } //UPDATE_INSTANCE_OPTIONS_END @@ -140,17 +124,17 @@ class AnotherClass extends R3Object { //UPDATE_FROM_INSTANCE_BEFORE_END //UPDATE_FROM_INSTANCE_OPTIONS_START - if (property === 'x') { - this.x = this.instance.side.x; - return; + if (property === 'isPublic' || property === 'all') { + this.isPublic = this.instance.isPublic; + if (property !== 'all') { + return; + } } - if (property === 'y') { - this.y = this.instance.side.y; - return; - } - if (property === 'z') { - this.z = this.instance.side.z; - return; + if (property === 'applicationMode' || property === 'all') { + this.applicationMode = this.instance.applicationMode; + if (property !== 'all') { + return; + } } //UPDATE_FROM_INSTANCE_OPTIONS_END @@ -168,14 +152,21 @@ class AnotherClass extends R3Object { dispose() { //DISPOSE_BEFORE_START - this.emit(Event.DISPOSE_OBJECT, this); + this.subscribe( + Event.INSTANCE_DISPOSED, + function(object) { + if (object === this) { + this.emit(Event.DISPOSE_OBJECT, this); + } + } + ); //DISPOSE_BEFORE_END //CUSTOM_DISPOSE_START //CUSTOM_DISPOSE_END //DISPOSE_AFTER_START - this.emit(Event.OBJECT_DISPOSED, this); + this.disposeInstance(); //DISPOSE_AFTER_END } @@ -185,15 +176,14 @@ class AnotherClass extends R3Object { disposeInstance() { //DISPOSE_INSTANCE_BEFORE_START - super.disposeInstance(); - this.emit(Event.DISPOSE_INSTANCE, this); + console.log('Disposing instance of ' + this.name); //DISPOSE_INSTANCE_BEFORE_END //CUSTOM_DISPOSE_INSTANCE_START //CUSTOM_DISPOSE_INSTANCE_END //DISPOSE_INSTANCE_AFTER_START - this.emit(Event.INSTANCE_DISPOSED, this); + this.emit(Event.DISPOSE_INSTANCE, this); //DISPOSE_INSTANCE_AFTER_END } @@ -205,6 +195,15 @@ class AnotherClass extends R3Object { } //CUSTOM_OUT_OF_CLASS_IMPLEMENTATION_START +Project.CAMERA_INDEX_EDIT = 0x0; +Project.CAMERA_INDEX_RUN = 0x1; + +Project.APPLICATION_MODE_EDIT = Project.CAMERA_INDEX_EDIT; +Project.APPLICATION_MODE_RUN = Project.CAMERA_INDEX_RUN; + +Project.RENDERER_INDEX_MAIN = 0x0; + +Project.RENDER_TARGET_INDEX_NONE = -0x1; //CUSTOM_OUT_OF_CLASS_IMPLEMENTATION_END -module.exports = AnotherClass; \ No newline at end of file +module.exports = Project; \ No newline at end of file diff --git a/src/r3/r3-r3-object.js b/src/r3/r3-r3-object.js index e181874..9dc48aa 100644 --- a/src/r3/r3-r3-object.js +++ b/src/r3/r3-r3-object.js @@ -4,6 +4,9 @@ const Event = require('r3-event.js'); /** OPTIONS_START + id=Utils.RandomId(10) + name=this.constructor.name + '(' + options.id + ')' + register=true OPTIONS_END INSTANCE_OPTIONS_MAPPING_START @@ -13,6 +16,9 @@ const Event = require('r3-event.js'); LINKED_OBJECTS_END EXCLUDED_FROM_INSTANCE_OPTIONS_START + id + name + register EXCLUDED_FROM_INSTANCE_OPTIONS_END **/ @@ -26,14 +32,23 @@ class R3Object extends Event { options = {}; } - options.id = Utils.RandomId(10); - options.name = 'R3Object (' + options.id + ')'; - super(options); this.emit(Event.OBJECT_CREATED, this); //OPTIONS_INIT_START + if (typeof options === 'undefined') { + options = {}; + } + if (Utils.UndefinedOrNull(options.id)) { + options.id = Utils.RandomId(10); + } + if (Utils.UndefinedOrNull(options.name)) { + options.name = this.constructor.name + '(' + options.id + ')'; + } + if (Utils.UndefinedOrNull(options.register)) { + options.register = true; + } //OPTIONS_INIT_END //CUSTOM_OPTIONS_INIT_START @@ -52,25 +67,21 @@ class R3Object extends Event { createInstance() { //CREATE_INSTANCE_BEFORE_START - super.createInstance(); - this.emit(Event.CREATE_INSTANCE, this); - - if (this.runtime === 'graphics') { - this.graphics.createInstance( - { - //CREATE_INSTANCE_OPTIONS_START - - //CREATE_INSTANCE_OPTIONS_END - }, - this - ) - } + this.emit(Event.CREATE_INSTANCE_BEFORE, this); //CREATE_INSTANCE_BEFORE_END //CUSTOM_CREATE_INSTANCE_START //CUSTOM_CREATE_INSTANCE_END //CREATE_INSTANCE_AFTER_START + this[this.runtime].createInstance( + this, + { + //CREATE_INSTANCE_OPTIONS_START + //CREATE_INSTANCE_OPTIONS_END + } + ) + this.emit(Event.INSTANCE_CREATED, this); //CREATE_INSTANCE_AFTER_END @@ -121,14 +132,21 @@ class R3Object extends Event { dispose() { //DISPOSE_BEFORE_START - this.emit(Event.DISPOSE_OBJECT, this); + this.subscribe( + Event.INSTANCE_DISPOSED, + function(object) { + if (object === this) { + this.emit(Event.DISPOSE_OBJECT, this); + } + } + ); //DISPOSE_BEFORE_END //CUSTOM_DISPOSE_START //CUSTOM_DISPOSE_END //DISPOSE_AFTER_START - this.emit(Event.OBJECT_DISPOSED, this); + this.disposeInstance(); //DISPOSE_AFTER_END } @@ -138,15 +156,14 @@ class R3Object extends Event { disposeInstance() { //DISPOSE_INSTANCE_BEFORE_START - super.disposeInstance(); - this.emit(Event.DISPOSE_INSTANCE, this); + console.log('Disposing instance of ' + this.name); //DISPOSE_INSTANCE_BEFORE_END //CUSTOM_DISPOSE_INSTANCE_START //CUSTOM_DISPOSE_INSTANCE_END //DISPOSE_INSTANCE_AFTER_START - this.emit(Event.INSTANCE_DISPOSED, this); + this.emit(Event.DISPOSE_INSTANCE, this); //DISPOSE_INSTANCE_AFTER_END } diff --git a/src/r3/r3-utils.js b/src/r3/r3-utils.js index b3a06e7..250ffeb 100644 --- a/src/r3/r3-utils.js +++ b/src/r3/r3-utils.js @@ -1,1247 +1,1275 @@ +const Event = require('r3-event'); + +/** + + OPTIONS_START + OPTIONS_END + + **/ + class Utils { - constructor() { - - } - - /** - * Gets the first parent of the object which is an instance of constructor - * @param object - * @param constructor - * @returns {*} - * @constructor - */ - 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 ObjectPropertiesAsBoolean(object) { - return Object.keys(object).reduce( - function(result, propertyId) { - - if (typeof object[propertyId] === 'function') { - return result; - } - - result[propertyId] = false; - - // if (object[propertyId] instanceof R3.Vector2) { - // Utils.BuildVectorSource(result, propertyId, 2); - // } - // - // if (object[propertyId] instanceof R3.Vector3) { - // Utils.BuildVectorSource(result, propertyId, 3); - // } - // - // if (object[propertyId] instanceof R3.Vector4) { - // Utils.BuildVectorSource(result, propertyId, 4); - // } - // - // if (object[propertyId] instanceof R3.Quaternion) { - // Utils.BuildQuaternionSource(result, propertyId); - // } - - return result; - - }.bind(this), - {} - ); - }; - - 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 - ); - }; - - static LoadIdsFromArrayToIdObject(array, idToObject) { - - }; - - static LoadIdsFromObjectToIdObject(object, idToObject) { - - - }; - - /** - * 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*(_?)(.+?)\1\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 - }); - } - }; - - /** - * Links an object to its parent through idToObject array - * @param propertyString - * @param idToObject - * @param parentObject - * @param id - * @constructor - */ - static Link(propertyString, idToObject, parentObject, id) { - - if (!Utils.UndefinedOrNull(parentObject[propertyString])) { - - if (!idToObject.hasOwnProperty(id)) { - console.warn('Linking failed for object:' + parentObject.name); - } - - parentObject[propertyString] = idToObject[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); - } - }; - - /** - * 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; - }; + //CONSTRUCTOR_TEMPLATE_START + constructor(options) { + + Event.Emit(Event.OBJECT_CREATED, this); + + //OPTIONS_INIT_START + if (typeof options === 'undefined') { + options = {}; + } + //OPTIONS_INIT_END + + //CUSTOM_OPTIONS_INIT_START + //CUSTOM_OPTIONS_INIT_END + + Object.assign(this, options); + + //CUSTOM_BEFORE_INIT_START + //CUSTOM_BEFORE_INIT_END + + Event.Emit(Event.OBJECT_INITIALIZED, this); + } + //CONSTRUCTOR_TEMPLATE_END + + //CUSTOM_IMPLEMENTATION_START + 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 ObjectPropertiesAsBoolean(object) { + return Object.keys(object).reduce( + function(result, propertyId) { + + if (typeof object[propertyId] === 'function') { + return result; + } + + result[propertyId] = false; + + // if (object[propertyId] instanceof R3.Vector2) { + // Utils.BuildVectorSource(result, propertyId, 2); + // } + // + // if (object[propertyId] instanceof R3.Vector3) { + // Utils.BuildVectorSource(result, propertyId, 3); + // } + // + // if (object[propertyId] instanceof R3.Vector4) { + // Utils.BuildVectorSource(result, propertyId, 4); + // } + // + // if (object[propertyId] instanceof R3.Quaternion) { + // Utils.BuildQuaternionSource(result, propertyId); + // } + + return result; + + }.bind(this), + {} + ); + }; + + 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 + ); + }; + + static LoadIdsFromArrayToIdObject(array, idToObject) { + + }; + + static LoadIdsFromObjectToIdObject(object, idToObject) { + + + }; + + /** + * 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*(_?)(.+?)\1\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 + }); + } + }; + + /** + * Links an object to its parent through idToObject array + * @param propertyString + * @param idToObject + * @param parentObject + * @param id + * @constructor + */ + static Link(propertyString, idToObject, parentObject, id) { + + if (!Utils.UndefinedOrNull(parentObject[propertyString])) { + + if (!idToObject.hasOwnProperty(id)) { + console.warn('Linking failed for object:' + parentObject.name); + } + + parentObject[propertyString] = idToObject[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); + } + }; + + /** + * 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; + }; + //CUSTOM_IMPLEMENTATION_END + } +//CUSTOM_OUT_OF_CLASS_IMPLEMENTATION_START +//CUSTOM_OUT_OF_CLASS_IMPLEMENTATION_END + module.exports = Utils; \ No newline at end of file diff --git a/src/templates/constructor.template b/src/templates/constructor.template index c0e53de..5960e0d 100644 --- a/src/templates/constructor.template +++ b/src/templates/constructor.template @@ -1,12 +1,5 @@ constructor(options) { - if (Utils.UndefinedOrNull(options)) { - options = {}; - } - - options.id = Utils.RandomId(10); - options.name = 'CLASS_NAME (' + options.id + ')'; - Event.Emit(Event.OBJECT_CREATED, this); //OPTIONS_INIT_START diff --git a/src/templates/constructor_extends.template b/src/templates/constructor_extends.template index 9644f3e..cee154f 100644 --- a/src/templates/constructor_extends.template +++ b/src/templates/constructor_extends.template @@ -4,9 +4,6 @@ options = {}; } - options.id = Utils.RandomId(10); - options.name = 'CLASS_NAME (' + options.id + ')'; - super(options); this.emit(Event.OBJECT_CREATED, this); diff --git a/src/templates/create_instance_after.template b/src/templates/create_instance_after.template index b45c537..90cfb15 100644 --- a/src/templates/create_instance_after.template +++ b/src/templates/create_instance_after.template @@ -1 +1,9 @@ + this[this.runtime].createInstance( + this, + { + //CREATE_INSTANCE_OPTIONS_START + //CREATE_INSTANCE_OPTIONS_END + } + ) + this.emit(Event.INSTANCE_CREATED, this); \ No newline at end of file diff --git a/src/templates/create_instance_before.template b/src/templates/create_instance_before.template index 9b66076..0395cc6 100644 --- a/src/templates/create_instance_before.template +++ b/src/templates/create_instance_before.template @@ -1,12 +1 @@ - super.createInstance(); - this.emit(Event.CREATE_INSTANCE, this); - - if (this.runtime === 'graphics') { - this.graphics.createInstance( - { - //CREATE_INSTANCE_OPTIONS_START - //CREATE_INSTANCE_OPTIONS_END - }, - this - ) - } + this.emit(Event.CREATE_INSTANCE_BEFORE, this); \ No newline at end of file diff --git a/src/templates/create_instance_options.template b/src/templates/create_instance_options.template index 9b63302..6ac3651 100644 --- a/src/templates/create_instance_options.template +++ b/src/templates/create_instance_options.template @@ -1 +1 @@ - 'INSTANCE_KEY': this.KEY \ No newline at end of file + 'INSTANCE_KEY': this.KEY \ No newline at end of file diff --git a/src/templates/dispose_after.template b/src/templates/dispose_after.template index 7f58934..30d8d5d 100644 --- a/src/templates/dispose_after.template +++ b/src/templates/dispose_after.template @@ -1 +1 @@ - this.emit(Event.OBJECT_DISPOSED, this); \ No newline at end of file + this.disposeInstance(); \ No newline at end of file diff --git a/src/templates/dispose_before.template b/src/templates/dispose_before.template index 65c3a8c..2a39b52 100644 --- a/src/templates/dispose_before.template +++ b/src/templates/dispose_before.template @@ -1 +1,8 @@ - this.emit(Event.DISPOSE_OBJECT, this); \ No newline at end of file + this.subscribe( + Event.INSTANCE_DISPOSED, + function(object) { + if (object === this) { + this.emit(Event.DISPOSE_OBJECT, this); + } + } + ); \ No newline at end of file diff --git a/src/templates/dispose_instance_after.template b/src/templates/dispose_instance_after.template index 6a8293f..264796c 100644 --- a/src/templates/dispose_instance_after.template +++ b/src/templates/dispose_instance_after.template @@ -1 +1 @@ - this.emit(Event.INSTANCE_DISPOSED, this); \ No newline at end of file + this.emit(Event.DISPOSE_INSTANCE, this); \ No newline at end of file diff --git a/src/templates/dispose_instance_before.template b/src/templates/dispose_instance_before.template index 5b1a418..596b4dd 100644 --- a/src/templates/dispose_instance_before.template +++ b/src/templates/dispose_instance_before.template @@ -1,2 +1 @@ - super.disposeInstance(); - this.emit(Event.DISPOSE_INSTANCE, this); \ No newline at end of file + console.log('Disposing instance of ' + this.name); \ No newline at end of file diff --git a/src/templates/normal.template b/src/templates/normal.template index 56d7bfd..45bcbd9 100644 --- a/src/templates/normal.template +++ b/src/templates/normal.template @@ -6,15 +6,6 @@ const Utils = require('r3-utils'); OPTIONS_START OPTIONS_END - INSTANCE_OPTIONS_MAPPING_START - INSTANCE_OPTIONS_MAPPING_END - - LINKED_OBJECTS_START - LINKED_OBJECTS_END - - EXCLUDED_FROM_INSTANCE_OPTIONS_START - EXCLUDED_FROM_INSTANCE_OPTIONS_END - **/ class CLASS_NAME { @@ -22,21 +13,6 @@ class CLASS_NAME { //CONSTRUCTOR_TEMPLATE_START //CONSTRUCTOR_TEMPLATE_END - //CREATE_INSTANCE_TEMPLATE_START - //CREATE_INSTANCE_TEMPLATE_END - - //UPDATE_INSTANCE_TEMPLATE_START - //UPDATE_INSTANCE_TEMPLATE_END - - //UPDATE_FROM_INSTANCE_TEMPLATE_START - //UPDATE_FROM_INSTANCE_TEMPLATE_END - - //DISPOSE_TEMPLATE_START - //DISPOSE_TEMPLATE_END - - //DISPOSE_INSTANCE_TEMPLATE_START - //DISPOSE_INSTANCE_TEMPLATE_END - //CUSTOM_IMPLEMENTATION_START //CUSTOM_IMPLEMENTATION_END diff --git a/src/templates/options_init_header.template b/src/templates/options_init_header.template new file mode 100644 index 0000000..0384ea5 --- /dev/null +++ b/src/templates/options_init_header.template @@ -0,0 +1,3 @@ + if (typeof options === 'undefined') { + options = {}; + } diff --git a/src/templates/update_from_instance_options.template b/src/templates/update_from_instance_options.template index 519906f..ac5e593 100644 --- a/src/templates/update_from_instance_options.template +++ b/src/templates/update_from_instance_options.template @@ -1,4 +1,6 @@ - if (property === 'KEY') { + if (property === 'KEY' || property === 'all') { this.KEY = this.instance.INSTANCE_KEY; - return; + if (property !== 'all') { + return; + } } diff --git a/update_options.php b/update_options.php index 8e21bcf..2c3ef09 100755 --- a/update_options.php +++ b/update_options.php @@ -157,9 +157,10 @@ foreach ($files as $file) { /** * Process the constructor options */ + $header = file_get_contents('src/templates/options_init_header.template'); $template = file_get_contents('src/templates/options_init.template'); - $newOptions = ''; + $newOptions = $header; foreach ($options as $key => $value) { $updated = str_replace('KEY', $key, $template); @@ -200,16 +201,18 @@ foreach ($files as $file) { array_push($newOptions, $updated); } - $contents = file_get_contents($file); + if (sizeof($newOptions) > 0) { - $contents = preg_replace( - '/\/\/\bCREATE_INSTANCE_OPTIONS_START\b.*?\/\/\bCREATE_INSTANCE_OPTIONS_END\b/s', - "//CREATE_INSTANCE_OPTIONS_START\n" . implode(",\n", $newOptions) . "\n\t\t\t\t\t//CREATE_INSTANCE_OPTIONS_END", - $contents - ); + $contents = file_get_contents($file); - file_put_contents($file, $contents); + $contents = preg_replace( + '/\/\/\bCREATE_INSTANCE_OPTIONS_START\b.*?\/\/\bCREATE_INSTANCE_OPTIONS_END\b/s', + "//CREATE_INSTANCE_OPTIONS_START\n" . implode(",\n", $newOptions) . "\n\t\t\t\t//CREATE_INSTANCE_OPTIONS_END", + $contents + ); + file_put_contents($file, $contents); + } /** * Process updateInstance options */ diff --git a/update_templates.php b/update_templates.php index e27b1c3..417d327 100755 --- a/update_templates.php +++ b/update_templates.php @@ -161,6 +161,7 @@ foreach ($files as $file) { echo "Restored file " . $file; + unlink($saveFile); } }