uml-state-machine
Version:
UML state machine in Javascript.
242 lines (194 loc) • 7.37 kB
JavaScript
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.traverseState = traverseState;
exports.buildStateMap = buildStateMap;
exports.isAncestor = isAncestor;
exports.walkOnEntry = walkOnEntry;
exports.walkOnExit = walkOnExit;
exports.findInitialState = findInitialState;
exports.State = State;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _lodash = _interopRequireDefault(require("lodash"));
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { (0, _defineProperty2["default"])(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
function traverseState(currentState, parent) {
var callback = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _lodash["default"].noop;
callback(currentState, parent);
_lodash["default"].map(currentState.states, function (state, name) {
state.name = name;
traverseState(state, currentState, callback);
});
}
function buildStateMap(smDef) {
var stateMap = new Map();
if (!smDef.state) {
throw {
name: "Parser",
message: "Root state is missing: " + smDef,
details: smDef
};
}
smDef.state.name = "Root";
var callback = function callback(state, parent) {
var parentModel = parent ? stateMap.get(parent.name) : undefined;
var stateModel = State(smDef, state, parentModel);
stateMap.set(state.name, stateModel);
};
traverseState(smDef.state, null, callback);
return stateMap;
}
function isEqualState(s1, s2) {
return s1.name() === s2.name();
}
function isAncestor(ancestorState, childState) {
if (isEqualState(ancestorState, childState)) {
return true;
}
var parent = childState.parent();
if (!parent) {
return false;
}
if (isEqualState(ancestorState, parent)) {
return true;
} else {
return isAncestor(ancestorState, parent);
}
}
function processTransactionPre(context, stateNext) {
var statePrevious = context.setStatePreviousFromCurrent();
context.setStateNext(stateNext);
context.observers.onTransitionBegin(context, statePrevious.name(), stateNext.name());
walkOnExit(context, statePrevious, stateNext);
}
function processTransactionPost(context, stateNext) {
var statePrevious = context.getStatePrevious();
walkOnEntry(context, statePrevious, stateNext);
context.setStatePrevious(null);
context.setStateNext(null);
var stateCurrent = stateNext ? stateNext : statePrevious;
context.setStateCurrent(stateCurrent.name());
context.observers.onTransitionEnd(context, statePrevious.name(), stateNext.name());
}
function walkOnEntry(context, previousState, nextState) {
if (!nextState) {
return;
}
function onEntry(nextState) {
context.observers.onEntry(context, nextState.name());
nextState.onEntry(context.actioner);
}
if (isEqualState(previousState, nextState)) {
onEntry(nextState);
} else if (!isAncestor(nextState, previousState)) {
var parent = nextState.parent();
if (parent) {
walkOnEntry(context, previousState, parent);
onEntry(nextState);
}
}
}
function walkOnExit(context, previousState, nextState) {
if (!nextState) {
return;
} //console.log("walkOnExit ", previousState.name(), nextState.name())
function onExit(state) {
context.observers.onExit(context, state.name());
state.onExit(context.actioner);
}
if (isEqualState(previousState, nextState)) {
onExit(nextState);
} else if (!isAncestor(previousState, nextState)) {
onExit(previousState);
var parent = previousState.parent();
if (parent) {
//console.log("walkOnExit parent ", parent.name())
walkOnExit(context, parent, nextState);
}
}
}
function findInitialState(stateInfo) {
var states = stateInfo.states;
var firstChildName = _lodash["default"].keys(states)[0];
if (firstChildName) {
var firstChild = stateInfo.states[firstChildName];
firstChild.name = firstChildName;
return findInitialState(firstChild);
} else {
return stateInfo.name;
}
}
function createAllEvents(state, events) {
return _lodash["default"].reduce(events, function (state, event) {
//console.log("createAllEvents event ", state.name(), event)
state[event] = function (context) {//console.log("unhandle event ", event, ", state: ", context.getStateCurrent().name())
};
return state;
}, state);
}
function createEvents(state, transitions) {
if (!transitions) {
return state;
}
var events = _lodash["default"].groupBy(transitions, "event"); //console.log("event ", events)
return _lodash["default"].reduce(events, function (state, transitions, key) {
//console.log("adding event key ", key, " to state ", state.name()),
//console.log("transitions ", transitions)
state[key] = function (context, payload) {
transitions.some(function (transition) {
if (transition.condition && !transition.condition(context, payload)) {
return false;
}
var nextStateName = transition.nextState;
if (nextStateName) {
var nextState = context.stateMap().get(nextStateName);
processTransactionPre(context, nextState);
if (transition.actions) {
//console.log("transition.actions ", transition.actions.length)
transition.actions.forEach(function (action) {
return action(context.actioner);
});
}
processTransactionPost(context, nextState);
}
return true;
});
};
return state;
}, state);
}
function State(smDef) {
var stateInfo = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var stateParent = arguments.length > 2 ? arguments[2] : undefined;
//console.log("State ", stateInfo.name);
var state = _objectSpread({}, stateParent, {}, {
isRoot: function isRoot() {
return !stateParent;
},
isLeaf: function isLeaf() {
return !stateInfo.states;
},
name: function name() {
return stateInfo.name;
},
parent: function parent() {
return stateParent;
},
info: function info() {
return stateInfo;
},
onEntry: function onEntry(context) {
if (stateInfo.onEntry) stateInfo.onEntry(context);
},
onExit: function onExit(context) {
if (stateInfo.onExit) stateInfo.onExit(context);
}
});
if (!stateParent) {
state = createAllEvents(state, smDef.events);
}
state = createEvents(state, stateInfo.transitions);
return state;
}
;