(function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory(); else if(typeof define === 'function' && define.amd) define("StateMachine", [], factory); else if(typeof exports === 'object') exports["StateMachine"] = factory(); else root["StateMachine"] = factory(); })(this, function() { return /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // identity function for calling harmony imports with the correct context /******/ __webpack_require__.i = function(value) { return value; }; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { /******/ configurable: false, /******/ enumerable: true, /******/ get: getter /******/ }); /******/ } /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = 5); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; module.exports = function(target, sources) { var n, source, key; for(n = 1 ; n < arguments.length ; n++) { source = arguments[n]; for(key in source) { if (source.hasOwnProperty(key)) target[key] = source[key]; } } return target; } /***/ }), /* 1 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; //------------------------------------------------------------------------------------------------- var mixin = __webpack_require__(0); //------------------------------------------------------------------------------------------------- module.exports = { build: function(target, config) { var n, max, plugin, plugins = config.plugins; for(n = 0, max = plugins.length ; n < max ; n++) { plugin = plugins[n]; if (plugin.methods) mixin(target, plugin.methods); if (plugin.properties) Object.defineProperties(target, plugin.properties); } }, hook: function(fsm, name, additional) { var n, max, method, plugin, plugins = fsm.config.plugins, args = [fsm.context]; if (additional) args = args.concat(additional) for(n = 0, max = plugins.length ; n < max ; n++) { plugin = plugins[n] method = plugins[n][name] if (method) method.apply(plugin, args); } } } //------------------------------------------------------------------------------------------------- /***/ }), /* 2 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; //------------------------------------------------------------------------------------------------- function camelize(label) { if (label.length === 0) return label; var n, result, word, words = label.split(/[_-]/); // single word with first character already lowercase, return untouched if ((words.length === 1) && (words[0][0].toLowerCase() === words[0][0])) return label; result = words[0].toLowerCase(); for(n = 1 ; n < words.length ; n++) { result = result + words[n].charAt(0).toUpperCase() + words[n].substring(1).toLowerCase(); } return result; } //------------------------------------------------------------------------------------------------- camelize.prepended = function(prepend, label) { label = camelize(label); return prepend + label[0].toUpperCase() + label.substring(1); } //------------------------------------------------------------------------------------------------- module.exports = camelize; /***/ }), /* 3 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; //------------------------------------------------------------------------------------------------- var mixin = __webpack_require__(0), camelize = __webpack_require__(2); //------------------------------------------------------------------------------------------------- function Config(options, StateMachine) { options = options || {}; this.options = options; // preserving original options can be useful (e.g visualize plugin) this.defaults = StateMachine.defaults; this.states = []; this.transitions = []; this.map = {}; this.lifecycle = this.configureLifecycle(); this.init = this.configureInitTransition(options.init); this.data = this.configureData(options.data); this.methods = this.configureMethods(options.methods); this.map[this.defaults.wildcard] = {}; this.configureTransitions(options.transitions || []); this.plugins = this.configurePlugins(options.plugins, StateMachine.plugin); } //------------------------------------------------------------------------------------------------- mixin(Config.prototype, { addState: function(name) { if (!this.map[name]) { this.states.push(name); this.addStateLifecycleNames(name); this.map[name] = {}; } }, addStateLifecycleNames: function(name) { this.lifecycle.onEnter[name] = camelize.prepended('onEnter', name); this.lifecycle.onLeave[name] = camelize.prepended('onLeave', name); this.lifecycle.on[name] = camelize.prepended('on', name); }, addTransition: function(name) { if (this.transitions.indexOf(name) < 0) { this.transitions.push(name); this.addTransitionLifecycleNames(name); } }, addTransitionLifecycleNames: function(name) { this.lifecycle.onBefore[name] = camelize.prepended('onBefore', name); this.lifecycle.onAfter[name] = camelize.prepended('onAfter', name); this.lifecycle.on[name] = camelize.prepended('on', name); }, mapTransition: function(transition) { var name = transition.name, from = transition.from, to = transition.to; this.addState(from); if (typeof to !== 'function') this.addState(to); this.addTransition(name); this.map[from][name] = transition; return transition; }, configureLifecycle: function() { return { onBefore: { transition: 'onBeforeTransition' }, onAfter: { transition: 'onAfterTransition' }, onEnter: { state: 'onEnterState' }, onLeave: { state: 'onLeaveState' }, on: { transition: 'onTransition' } }; }, configureInitTransition: function(init) { if (typeof init === 'string') { return this.mapTransition(mixin({}, this.defaults.init, { to: init, active: true })); } else if (typeof init === 'object') { return this.mapTransition(mixin({}, this.defaults.init, init, { active: true })); } else { this.addState(this.defaults.init.from); return this.defaults.init; } }, configureData: function(data) { if (typeof data === 'function') return data; else if (typeof data === 'object') return function() { return data; } else return function() { return {}; } }, configureMethods: function(methods) { return methods || {}; }, configurePlugins: function(plugins, builtin) { plugins = plugins || []; var n, max, plugin; for(n = 0, max = plugins.length ; n < max ; n++) { plugin = plugins[n]; if (typeof plugin === 'function') plugins[n] = plugin = plugin() if (plugin.configure) plugin.configure(this); } return plugins }, configureTransitions: function(transitions) { var i, n, transition, from, to, wildcard = this.defaults.wildcard; for(n = 0 ; n < transitions.length ; n++) { transition = transitions[n]; from = Array.isArray(transition.from) ? transition.from : [transition.from || wildcard] to = transition.to || wildcard; for(i = 0 ; i < from.length ; i++) { this.mapTransition({ name: transition.name, from: from[i], to: to }); } } }, transitionFor: function(state, transition) { var wildcard = this.defaults.wildcard; return this.map[state][transition] || this.map[wildcard][transition]; }, transitionsFor: function(state) { var wildcard = this.defaults.wildcard; return Object.keys(this.map[state]).concat(Object.keys(this.map[wildcard])); }, allStates: function() { return this.states; }, allTransitions: function() { return this.transitions; } }); //------------------------------------------------------------------------------------------------- module.exports = Config; //------------------------------------------------------------------------------------------------- /***/ }), /* 4 */ /***/ (function(module, exports, __webpack_require__) { var mixin = __webpack_require__(0), Exception = __webpack_require__(6), plugin = __webpack_require__(1), UNOBSERVED = [ null, [] ]; //------------------------------------------------------------------------------------------------- function JSM(context, config) { this.context = context; this.config = config; this.state = config.init.from; this.observers = [context]; } //------------------------------------------------------------------------------------------------- mixin(JSM.prototype, { init: function(args) { mixin(this.context, this.config.data.apply(this.context, args)); plugin.hook(this, 'init'); if (this.config.init.active) return this.fire(this.config.init.name, []); }, is: function(state) { return Array.isArray(state) ? (state.indexOf(this.state) >= 0) : (this.state === state); }, isPending: function() { return this.pending; }, can: function(transition) { return !this.isPending() && !!this.seek(transition); }, cannot: function(transition) { return !this.can(transition); }, allStates: function() { return this.config.allStates(); }, allTransitions: function() { return this.config.allTransitions(); }, transitions: function() { return this.config.transitionsFor(this.state); }, seek: function(transition, args) { var wildcard = this.config.defaults.wildcard, entry = this.config.transitionFor(this.state, transition), to = entry && entry.to; if (typeof to === 'function') return to.apply(this.context, args); else if (to === wildcard) return this.state else return to }, fire: function(transition, args) { return this.transit(transition, this.state, this.seek(transition, args), args); }, transit: function(transition, from, to, args) { var lifecycle = this.config.lifecycle, changed = this.config.options.observeUnchangedState || (from !== to); if (!to) return this.context.onInvalidTransition(transition, from, to); if (this.isPending()) return this.context.onPendingTransition(transition, from, to); this.config.addState(to); // might need to add this state if it's unknown (e.g. conditional transition or goto) this.beginTransit(); args.unshift({ // this context will be passed to each lifecycle event observer transition: transition, from: from, to: to, fsm: this.context }); return this.observeEvents([ this.observersForEvent(lifecycle.onBefore.transition), this.observersForEvent(lifecycle.onBefore[transition]), changed ? this.observersForEvent(lifecycle.onLeave.state) : UNOBSERVED, changed ? this.observersForEvent(lifecycle.onLeave[from]) : UNOBSERVED, this.observersForEvent(lifecycle.on.transition), changed ? [ 'doTransit', [ this ] ] : UNOBSERVED, changed ? this.observersForEvent(lifecycle.onEnter.state) : UNOBSERVED, changed ? this.observersForEvent(lifecycle.onEnter[to]) : UNOBSERVED, changed ? this.observersForEvent(lifecycle.on[to]) : UNOBSERVED, this.observersForEvent(lifecycle.onAfter.transition), this.observersForEvent(lifecycle.onAfter[transition]), this.observersForEvent(lifecycle.on[transition]) ], args); }, beginTransit: function() { this.pending = true; }, endTransit: function(result) { this.pending = false; return result; }, failTransit: function(result) { this.pending = false; throw result; }, doTransit: function(lifecycle) { this.state = lifecycle.to; }, observe: function(args) { if (args.length === 2) { var observer = {}; observer[args[0]] = args[1]; this.observers.push(observer); } else { this.observers.push(args[0]); } }, observersForEvent: function(event) { // TODO: this could be cached var n = 0, max = this.observers.length, observer, result = []; for( ; n < max ; n++) { observer = this.observers[n]; if (observer[event]) result.push(observer); } return [ event, result, true ] }, observeEvents: function(events, args, previousEvent, previousResult) { if (events.length === 0) { return this.endTransit(previousResult === undefined ? true : previousResult); } var event = events[0][0], observers = events[0][1], pluggable = events[0][2]; args[0].event = event; if (event && pluggable && event !== previousEvent) plugin.hook(this, 'lifecycle', args); if (observers.length === 0) { events.shift(); return this.observeEvents(events, args, event, previousResult); } else { var observer = observers.shift(), result = observer[event].apply(observer, args); if (result && typeof result.then === 'function') { return result.then(this.observeEvents.bind(this, events, args, event)) .catch(this.failTransit.bind(this)) } else if (result === false) { return this.endTransit(false); } else { return this.observeEvents(events, args, event, result); } } }, onInvalidTransition: function(transition, from, to) { throw new Exception("transition is invalid in current state", transition, from, to, this.state); }, onPendingTransition: function(transition, from, to) { throw new Exception("transition is invalid while previous transition is still in progress", transition, from, to, this.state); } }); //------------------------------------------------------------------------------------------------- module.exports = JSM; //------------------------------------------------------------------------------------------------- /***/ }), /* 5 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; //----------------------------------------------------------------------------------------------- var mixin = __webpack_require__(0), camelize = __webpack_require__(2), plugin = __webpack_require__(1), Config = __webpack_require__(3), JSM = __webpack_require__(4); //----------------------------------------------------------------------------------------------- var PublicMethods = { is: function(state) { return this._fsm.is(state) }, can: function(transition) { return this._fsm.can(transition) }, cannot: function(transition) { return this._fsm.cannot(transition) }, observe: function() { return this._fsm.observe(arguments) }, transitions: function() { return this._fsm.transitions() }, allTransitions: function() { return this._fsm.allTransitions() }, allStates: function() { return this._fsm.allStates() }, onInvalidTransition: function(t, from, to) { return this._fsm.onInvalidTransition(t, from, to) }, onPendingTransition: function(t, from, to) { return this._fsm.onPendingTransition(t, from, to) }, } var PublicProperties = { state: { configurable: false, enumerable: true, get: function() { return this._fsm.state; }, set: function(state) { throw Error('use transitions to change state') } } } //----------------------------------------------------------------------------------------------- function StateMachine(options) { return apply(this || {}, options); } function factory() { var cstor, options; if (typeof arguments[0] === 'function') { cstor = arguments[0]; options = arguments[1] || {}; } else { cstor = function() { this._fsm.apply(this, arguments) }; options = arguments[0] || {}; } var config = new Config(options, StateMachine); build(cstor.prototype, config); cstor.prototype._fsm.config = config; // convenience access to shared config without needing an instance return cstor; } //------------------------------------------------------------------------------------------------- function apply(instance, options) { var config = new Config(options, StateMachine); build(instance, config); instance._fsm(); return instance; } function build(target, config) { if ((typeof target !== 'object') || Array.isArray(target)) throw Error('StateMachine can only be applied to objects'); plugin.build(target, config); Object.defineProperties(target, PublicProperties); mixin(target, PublicMethods); mixin(target, config.methods); config.allTransitions().forEach(function(transition) { target[camelize(transition)] = function() { return this._fsm.fire(transition, [].slice.call(arguments)) } }); target._fsm = function() { this._fsm = new JSM(this, config); this._fsm.init(arguments); } } //----------------------------------------------------------------------------------------------- StateMachine.version = '3.0.1'; StateMachine.factory = factory; StateMachine.apply = apply; StateMachine.defaults = { wildcard: '*', init: { name: 'init', from: 'none' } } //=============================================================================================== module.exports = StateMachine; /***/ }), /* 6 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; module.exports = function(message, transition, from, to, current) { this.message = message; this.transition = transition; this.from = from; this.to = to; this.current = current; } /***/ }) /******/ ]); });