const { statusCodes } = require(".."); const PhSim = require(".."); /** * @file File for dealing with wFunctions. * @module widgets/wFunction.js * @author Mjduniverse * */ /** * A widget function is a function that used for the `WidgetFunction` widget. * The "this" keyword in the body of function usually refers to the current instance of * PhSim simulation or references an instance of {@link PhSim.DynObject}. * * To learn more, see the {@tutorial Widget Functions} * * @module wFunction * @typedef {Function} WFunction * @property {Function} _options - Options used to create WFunction * @property {Function|Number} _ref * @property {String} [_name] - WFunction name * @property {Function} _bodyFunction - Body Function * @property {String} _eventclass - Event class * */ /** * Array of widget functions * @memberof PhSim * @type {WFunctions[]} */ PhSim.prototype.wFunctions = []; /** * Create a widget function and push it to the wFunctions array. * @function * @memberof PhSim * @param {String|Function} arg - content of function if string, function if function * @param {Object} thisRef - * @returns {WFunction} */ // Simple Event Reference Array PhSim.prototype.wFunctionRefs = []; /** * * @typedef {"key"} keyTriggerString * * The "key" trigger means that the simple event will execute if a key is pressed. */ /** * * @typedef {"sensor"|"sensor_global"} sensorTriggerString * * The "sensor" trigger means that the simple event will execute if the trigger object * collides with an object that shares at least one of the sensor classes. However, * the "sensor_global" trigger means that the function will execute if any two * objects that share at least one sensor class collides. */ /** * * @typedef {"objclick"|"objclick_global"} objclickTriggerString * * The "objclick" trigger means that the simple event will execute if the mouse clicks on the trigger object. * However, the "objclick_global" trigger means that the simple event will execute if the mouse clicks on any * object in general. */ /** * @typedef {"objMouseDown"|"objmousedown_global"} objMouseDownTriggerString * * The "objmousedown" trigger means that the simple event call is executed if the mouse * is being pushed down on the object. The "objmousedown_global" trigger means that * the simple event will execute if the mouse clicks on any object in general. */ /** * @typedef {"firstslupdate"} firstslupdateTriggerString * * The "firstslupdate" trigger means that the simple event will execute during the first * update of the simulation. */ /** * @typedef {"objmouseup"|"objmouseup_global"} objmouseupTriggerString * * The "objmouseup" trigger means that the simple event will execute when the * mouse is let go of while the mouse is over an object. The "objmouseup_global" trigger * means that the simple event will execute if the mouse is let go of while it is * over an object. */ /** * @typedef {"objlink"} objlinkTriggerString * * The "objlink" trigger means that the simple event will execute if the trigger object * is linked to another object by the objlink widget. */ /** * @typedef {"afterslchange"} afterslchangeTriggerString * * The "afterslchange" trigger means that the simple event will execute after the * superlayer changes. * */ /** * @typedef {"time"} timeTriggerString * * The "time" trigger means that the simple event will execute by some interval of time. */ /** * @typedef {keyTriggerString|sensorTriggerString|objclickTriggerString| * objMouseDownTriggerString|firstslupdateTriggerString|objmouseupTriggerString| * objlinkTriggerString|afterslchangeTriggerString|timeTriggerString} wFunctionTrigger * * * The simple event trigger string is a string defining {@link WFunctionOptions.trigger} */ /** * Properties for a simple event. * The simple event options is an Object that is used for the {@link PhSim#createWFunction} function. * They are also used to configure various types of widgets. Especially widgets that utilize * wFunctions. * * @typedef {Object} WFunctionOptions * @property {KeyBoard} [key] - The event.key value for triggering a simple event. * @property {Number} [time] - The time interval between a repeated event or a delay time for timeouts. * @property {Number} [maxN] - The maximum number of times a repeated SimpleEvent can be executed. * @property {PhSim.DynObject} [wFunctionObj] - An object being affected by the wFunction. * @property {String} [name] - The name of the wFunction * */ /** * @callback WFunctionBody * @this {PhSim.DynObject} * @param {Event} e - event object */ /** * Function used to generate {@link WFunction|WFunctions.} * To learn more, see the {@tutorial Widget Functions} tutorial. * * @function * @memberof PhSim * * @param {wFunctionTrigger} trigger - The type of SimpleEvent. * * @param {WFunctionBody|Number} wFunctionBody - The JavaScript function to be wrapped. * If `wFunctionBody` is an integer `i`, the function body is deterimined by the * `{@link PhSim#options.wFunctions}[i]` * * @param {WFunctionOptions} options - [The Simple Event Options Object]{@link WFunctionOptions}. * @returns {WFunction} - The wFunction. * @this {PhSim} * */ PhSim.prototype.createWFunction = function(thisRef,wFunctionBody,options) { var self = this; if(typeof wFunctionBody === "number") { wFunctionBody = this.wFunctions[wFunctionBody]; } if(typeof wFunctionBody === "string") { wFunctionBody = new Function("e",options.function) } /** * * New WFunction * * @inner * @type {WFunction} */ var wFunction = function(event) { try { return wFunctionBody.apply(thisRef,event); } catch(e) { self.callEventClass("wfunctionerror",self,e); console.error(e); } } wFunction._options = options; wFunction._bodyFunction = wFunctionBody; wFunction._thisRef = thisRef; if(options._name) { self.wFunctionNames[options._name] = wFunction; } if(options.trigger === "key") { if(options.key) { wFunction._ref = function(e) { if( e.key.match( new RegExp("^" + options.key + "$","i") ) ) { wFunction(e); } }; } else { wFunction._ref = function(e) { wFunction(e); } } wFunction._eventclass = "keydown"; } else if(options.trigger === "sensor" || options.trigger === "sensor_global") { if(options.trigger === "sensor") { wFunction._ref = function(e) { var m = self.inSensorCollision(thisRef) if(m) { wFunction(e); } } } else { wFunction._ref = function(e) { wFunction(e); } } wFunction._eventclass = "collisionstart"; } else if(options.trigger === "update") { wFunction._ref = function() { wFunction(); } wFunction._eventclass = "beforeupdate"; } else if(options.trigger === "objclick" || options.trigger === "objclick_global") { var f; if(options.trigger === "objclick") { wFunction._ref = function(e) { if(self.objMouseArr[self.objMouseArr.length - 1] === thisRef) { wFunction(e); } }; } else { wFunction._ref = function(e) { wFunction(e); } } wFunction._eventclass = "objclick"; } else if(options.trigger === "objmousedown" || options.trigger === "objmousedown_global") { if(options.trigger === "objmousedown") { wFunction._ref = function(e) { if(self.objMouseArr[self.objMouseArr.length - 1] === thisRef) { wFunction(e); } } } else { wFunction._ref = function(e) { wFunction(e); } } wFunction._eventclass = "objmousedown"; } else if(options.trigger === "firstslupdate") { wFunction._ref = function(e) { wFunction(e) } wFunction._eventclass = "firstslupdate"; } else if(options.trigger === "objmouseup" || options.trigger === "objmouseup_global") { if(options.trigger === "objmouseup") { wFunction._ref = function(e) { if(self.objMouseArr[self.objMouseArr.length - 1] === thisRef) { wFunction(e); } }; } else { wFunction._ref = function(e) { wFunction(e); } } wFunction._eventclass = "objmouseup"; } else if(options.trigger === "objlink") { thisRef.objLinkFunctions = thisRef.objLinkFunctions || []; thisRef.objLinkFunctions.push(wFunction); wFunction._ref = f; return wFunction; } else if(options.trigger === "afterslchange") { wFunction._ref = function(e) { wFunction(e); } wFunction._eventclass = "afterslchange"; } else if(options.trigger === "time") { var getFunction = function() { var fn; if(Number.isInteger(options.maxN)) { fn = function() { if(fn.__n === options.maxN) { clearInterval(fn.__interN); } else { if(!self.paused) { wFunction(); fn.__n++; } } } fn.__n = 0; } else { fn = function() { if(!self.paused) { wFunction(); } } } fn.__phtime = options.time; fn.__interN = null; return fn; } var refFunc = getFunction(); // Making sure that the interval starts after the simulation has has it's first // Update and not run at the moment the wFunction is created. if(this.status === statusCodes.LOADED_SIMULATION) { refFunc.__interN = setInterval(refFunc,refFunc.__phtime); } else if(this.status < statusCodes.LOADED_SIMULATION) { self.on("firstslupdate",function(){ refFunc.__interN = setInterval(refFunc,refFunc.__phtime); }); } wFunction._ref = refFunc.__interN; return wFunction; } else { wFunction._ref = wFunction; wFunction._eventclass = options.trigger; } if(typeof wFunction._ref === "function") { wFunction._ref._wFunction = wFunction; } if(typeof wFunction._eventclass === "string") { wFunction._listener = wFunction._ref; self.on(wFunction._eventclass,wFunction._ref,{ "slEvent": true }); } // Return wFunction return wFunction } /** * * Disable wFunction * * @memberof PhSim * @function * @param {WFunction} o - Reference created by {@link PhSim#createWFunction}. * */ PhSim.prototype.disableWFunction = function(o) { if(typeof o._eventclass === "string") { this.off(o._eventclass,o._ref); } else if(o._options.trigger === "time") { clearInterval(o._ref) } else if(o._options.trigger === "objlink") { var i = o._thisRef.objLinkFunctions.indexOf(o) o._thisRef.objLinkFunctions.splice(i,1); } if(o._name) { delete this.wFunctionNames[o._name]; } } /** * * The `wFunction` widget is used to create wFunctions. * * @memberof PhSim * @function * @param {PhSim.DynObject|PhSim} o - Target object or simulation * @param {WFunctionOptions} widget - wFunction options */ PhSim.Widgets.wFunction = function(o,widget) { this.createWFunction(o,widget.function,widget); } PhSim.prototype.wFunctionNames = {}