summaryrefslogtreecommitdiff
path: root/addons/base_import/static/lib/javascript-state-machine/state-machine.js
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/base_import/static/lib/javascript-state-machine/state-machine.js
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/base_import/static/lib/javascript-state-machine/state-machine.js')
-rw-r--r--addons/base_import/static/lib/javascript-state-machine/state-machine.js155
1 files changed, 155 insertions, 0 deletions
diff --git a/addons/base_import/static/lib/javascript-state-machine/state-machine.js b/addons/base_import/static/lib/javascript-state-machine/state-machine.js
new file mode 100644
index 00000000..0d503ee7
--- /dev/null
+++ b/addons/base_import/static/lib/javascript-state-machine/state-machine.js
@@ -0,0 +1,155 @@
+(function (window) {
+
+ StateMachine = {
+
+ //---------------------------------------------------------------------------
+
+ VERSION: "2.1.0",
+
+ //---------------------------------------------------------------------------
+
+ Result: {
+ SUCCEEDED: 1, // the event transitioned successfully from one state to another
+ NOTRANSITION: 2, // the event was successfull but no state transition was necessary
+ CANCELLED: 3, // the event was cancelled by the caller in a beforeEvent callback
+ ASYNC: 4, // the event is asynchronous and the caller is in control of when the transition occurs
+ },
+
+ Error: {
+ INVALID_TRANSITION: 100, // caller tried to fire an event that was innapropriate in the current state
+ PENDING_TRANSITION: 200, // caller tried to fire an event while an async transition was still pending
+ INVALID_CALLBACK: 300, // caller provided callback function threw an exception
+ },
+
+ WILDCARD: '*',
+ ASYNC: 'async',
+
+ //---------------------------------------------------------------------------
+
+ create: function(cfg, target) {
+
+ var initial = (typeof cfg.initial == 'string') ? { state: cfg.initial } : cfg.initial; // allow for a simple string, or an object with { state: 'foo', event: 'setup', defer: true|false }
+ var fsm = target || cfg.target || {};
+ var events = cfg.events || [];
+ var callbacks = cfg.callbacks || {};
+ var map = {};
+
+ var add = function(e) {
+ var from = (e.from instanceof Array) ? e.from : (e.from ? [e.from] : [StateMachine.WILDCARD]); // allow 'wildcard' transition if 'from' is not specified
+ map[e.name] = map[e.name] || {};
+ for (var n = 0 ; n < from.length ; n++)
+ map[e.name][from[n]] = e.to || from[n]; // allow no-op transition if 'to' is not specified
+ };
+
+ if (initial) {
+ initial.event = initial.event || 'startup';
+ add({ name: initial.event, from: 'none', to: initial.state });
+ }
+
+ for(var n = 0 ; n < events.length ; n++)
+ add(events[n]);
+
+ for(var name in map) {
+ if (map.hasOwnProperty(name))
+ fsm[name] = StateMachine.buildEvent(name, map[name]);
+ }
+
+ for(var name in callbacks) {
+ if (callbacks.hasOwnProperty(name))
+ fsm[name] = callbacks[name]
+ }
+
+ fsm.current = 'none';
+ fsm.is = function(state) { return this.current == state; };
+ fsm.can = function(event) { return !this.transition && (map[event].hasOwnProperty(this.current) || map[event].hasOwnProperty(StateMachine.WILDCARD)); }
+ fsm.cannot = function(event) { return !this.can(event); };
+ fsm.error = cfg.error || function(name, from, to, args, error, msg) { throw msg; }; // default behavior when something unexpected happens is to throw an exception, but caller can override this behavior if desired (see github issue #3)
+
+ if (initial && !initial.defer)
+ fsm[initial.event]();
+
+ return fsm;
+
+ },
+
+ //===========================================================================
+
+ doCallback: function(fsm, func, name, from, to, args) {
+ if (func) {
+ try {
+ return func.apply(fsm, [name, from, to].concat(args));
+ }
+ catch(e) {
+ return fsm.error(name, from, to, args, StateMachine.Error.INVALID_CALLBACK, "an exception occurred in a caller-provided callback function");
+ }
+ }
+ },
+
+ beforeEvent: function(fsm, name, from, to, args) { return StateMachine.doCallback(fsm, fsm['onbefore' + name], name, from, to, args); },
+ afterEvent: function(fsm, name, from, to, args) { return StateMachine.doCallback(fsm, fsm['onafter' + name] || fsm['on' + name], name, from, to, args); },
+ leaveState: function(fsm, name, from, to, args) { return StateMachine.doCallback(fsm, fsm['onleave' + from], name, from, to, args); },
+ enterState: function(fsm, name, from, to, args) { return StateMachine.doCallback(fsm, fsm['onenter' + to] || fsm['on' + to], name, from, to, args); },
+ changeState: function(fsm, name, from, to, args) { return StateMachine.doCallback(fsm, fsm['onchangestate'], name, from, to, args); },
+
+
+ buildEvent: function(name, map) {
+ return function() {
+
+ var from = this.current;
+ var to = map[from] || map[StateMachine.WILDCARD] || from;
+ var args = Array.prototype.slice.call(arguments); // turn arguments into pure array
+
+ if (this.transition)
+ return this.error(name, from, to, args, StateMachine.Error.PENDING_TRANSITION, "event " + name + " inappropriate because previous transition did not complete");
+
+ if (this.cannot(name))
+ return this.error(name, from, to, args, StateMachine.Error.INVALID_TRANSITION, "event " + name + " inappropriate in current state " + this.current);
+
+ if (false === StateMachine.beforeEvent(this, name, from, to, args))
+ return StateMachine.CANCELLED;
+
+ if (from === to) {
+ StateMachine.afterEvent(this, name, from, to, args);
+ return StateMachine.NOTRANSITION;
+ }
+
+ // prepare a transition method for use EITHER lower down, or by caller if they want an async transition (indicated by an ASYNC return value from leaveState)
+ var fsm = this;
+ this.transition = function() {
+ fsm.transition = null; // this method should only ever be called once
+ fsm.current = to;
+ StateMachine.enterState( fsm, name, from, to, args);
+ StateMachine.changeState(fsm, name, from, to, args);
+ StateMachine.afterEvent( fsm, name, from, to, args);
+ };
+
+ var leave = StateMachine.leaveState(this, name, from, to, args);
+ if (false === leave) {
+ this.transition = null;
+ return StateMachine.CANCELLED;
+ }
+ else if ("async" === leave) {
+ return StateMachine.ASYNC;
+ }
+ else {
+ if (this.transition)
+ this.transition(); // in case user manually called transition() but forgot to return ASYNC
+ return StateMachine.SUCCEEDED;
+ }
+
+ };
+ }
+
+ }; // StateMachine
+
+ //===========================================================================
+
+ if ("function" === typeof define) {
+ define("statemachine", [], function() { return StateMachine; });
+ }
+ else {
+ window.StateMachine = StateMachine;
+ }
+
+}(this));
+