[
  {
    "path": ".ackrc",
    "content": "--ignore-dir=coverage\n--ignore-dir=node_modules\n--ignore-dir=.nyc_output\n\n"
  },
  {
    "path": ".gitignore",
    "content": "node_modules\ncoverage\n.nyc_output\n*.swp\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: node_js\nnode_js:\n - '4'\n - '6'\nsudo: false\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright (c) 2012, 2013, 2014, 2015, 2016, 2017, 2018, Jake Gordon and contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
  },
  {
    "path": "README.md",
    "content": "# Javascript State Machine\n\n[![NPM version](https://badge.fury.io/js/javascript-state-machine.svg)](https://badge.fury.io/js/javascript-state-machine)\n[![Build Status](https://travis-ci.org/jakesgordon/javascript-state-machine.svg?branch=master)](https://travis-ci.org/jakesgordon/javascript-state-machine)\n\nA library for finite state machines.\n\n![matter state machine](examples/matter.png)\n\n<br>\n\n### NOTE for existing users\n\n> **VERSION 3.0** Is a significant rewrite from earlier versions.\n  Existing 2.x users should be sure to read the [Upgrade Guide](docs/upgrading-from-v2.md).\n\n<br>\n\n# Installation\n\nIn a browser:\n\n```html\n  <script src='state-machine.js'></script>\n```\n\n> after downloading the [source](dist/state-machine.js) or the [minified version](dist/state-machine.min.js)\n\nUsing npm:\n\n```shell\n  npm install --save-dev javascript-state-machine\n```\n\nIn Node.js:\n\n```javascript\n  var StateMachine = require('javascript-state-machine');\n```\n\n# Usage\n\nA state machine can be constructed using:\n\n```javascript\n  var fsm = new StateMachine({\n    init: 'solid',\n    transitions: [\n      { name: 'melt',     from: 'solid',  to: 'liquid' },\n      { name: 'freeze',   from: 'liquid', to: 'solid'  },\n      { name: 'vaporize', from: 'liquid', to: 'gas'    },\n      { name: 'condense', from: 'gas',    to: 'liquid' }\n    ],\n    methods: {\n      onMelt:     function() { console.log('I melted')    },\n      onFreeze:   function() { console.log('I froze')     },\n      onVaporize: function() { console.log('I vaporized') },\n      onCondense: function() { console.log('I condensed') }\n    }\n  });\n```\n\n... which creates an object with a current state property:\n\n  * `fsm.state`\n\n... methods to transition to a different state:\n\n  * `fsm.melt()`\n  * `fsm.freeze()`\n  * `fsm.vaporize()`\n  * `fsm.condense()`\n\n... observer methods called automatically during the lifecycle of a transition:\n\n  * `onMelt()`\n  * `onFreeze()`\n  * `onVaporize()`\n  * `onCondense()`\n\n... along with the following helper methods:\n\n  * `fsm.is(s)`            - return true if state `s` is the current state\n  * `fsm.can(t)`           - return true if transition `t` can occur from the current state\n  * `fsm.cannot(t)`        - return true if transition `t` cannot occur from the current state\n  * `fsm.transitions()`    - return list of transitions that are allowed from the current state\n  * `fsm.allTransitions()` - return list of all possible transitions\n  * `fsm.allStates()`      - return list of all possible states\n\n# Terminology\n\nA state machine consists of a set of [**States**](docs/states-and-transitions.md)\n\n  * solid\n  * liquid\n  * gas\n\nA state machine changes state by using [**Transitions**](docs/states-and-transitions.md)\n\n  * melt\n  * freeze\n  * vaporize\n  * condense\n\nA state machine can perform actions during a transition by observing [**Lifecycle Events**](docs/lifecycle-events.md)\n\n  * onBeforeMelt\n  * onAfterMelt\n  * onLeaveSolid\n  * onEnterLiquid\n  * ...\n\nA state machine can also have arbitrary [**Data and Methods**](docs/data-and-methods.md).\n\nMultiple instances of a state machine can be created using a [**State Machine Factory**](docs/state-machine-factory.md).\n\n# Documentation\n\nRead more about\n\n  * [States and Transitions](docs/states-and-transitions.md)\n  * [Data and Methods](docs/data-and-methods.md)\n  * [Lifecycle Events](docs/lifecycle-events.md)\n  * [Asynchronous Transitions](docs/async-transitions.md)\n  * [Initialization](docs/initialization.md)\n  * [Error Handling](docs/error-handling.md)\n  * [State History](docs/state-history.md)\n  * [Visualization](docs/visualization.md)\n  * [State Machine Factory](docs/state-machine-factory.md)\n  * [Upgrading from 2.x](docs/upgrading-from-v2.md)\n \n# Contributing\n\nYou can [Contribute](docs/contributing.md) to this project with issues or pull requests.\n\n# Release Notes\n\nSee [RELEASE NOTES](RELEASE_NOTES.md) file.\n\n# License\n\nSee [MIT LICENSE](https://github.com/jakesgordon/javascript-state-machine/blob/master/LICENSE) file.\n\n# Contact\n\nIf you have any ideas, feedback, requests or bug reports, you can reach me at\n[jakesgordon@gmail.com](mailto:jakesgordon@gmail.com), or via\nmy website: [jakesgordon.com](https://jakesgordon.com/)\n"
  },
  {
    "path": "RELEASE_NOTES.md",
    "content": "Version 3.1.0 (July 12th 2018)\n------------------------------\n\n * Changed back to MIT license\n\nVersion 3.0.1 (June 10th 2017)\n------------------------------\n\n * First 3.x release - see 3.0.0-rc.1 release notes below\n\n * fix issue #109 - rejection from async lifecycle method does not reject transitions promise\n * fix issue #106 - async transition: forward resolved value\n * fix issue #107 - lifecycle event name breaks for all uppercase \n\nVersion 3.0.0-rc.1 (January 10 2017)\n------------------------------------\n\n**IMPORTANT NOTE**: this version includes **breaking changes** that will require code updates.\n\nPlease read [UPGRADING FROM 2.x](docs/upgrading-from-v2.md) for details. Highlights include:\n\n  * Improved Construction.\n  * Arbitrary Data and Methods.\n  * Observable Transitions\n  * Conditional Transitions\n  * Promise-based Asynchronous Transitions\n  * Improved Transition Lifecycle Events\n  * State History\n  * Visualization\n  * Webpack build system\n  * ...\n\n<br>\n<br>\n\nVersion 2.4.0 (November 20 2016)\n--------------------------------\n\n * added npm install instructions to readme\n * fix for javascript error when running in jasmine/node (issue #88)\n * exclude build files from bower install (pull request #75)\n * ensure WILDCARD events are included in list of available transitions() (issue #93)\n * fix FSM getting stuck into \"*\" state when using double wildcard (issue #64)\n * function (fsm.states) returning list of all available states in the machine would help automated testing (issue #54)\n * state machine hides callback exceptions (issue #62)\n * replaced (dev dependency) YUI compressor with uglify-js for building minified version\n\nVersion 2.3.5 (January 20 2014)\n-------------------------------\n\n * fix for broken transitions() method (issue #74)\n\nVersion 2.3.4 (January 17 2014)\n-------------------------------\n\n * helper method to list which events are allowed from the current state (issue #71 - thanks to @mgoldsborough and @chopj)\n\nVersion 2.3.3 (October 17 2014)\n-------------------------------\n\n * added web worker compatability (issue #65 - thanks to @offirmo)\n\nVersion 2.3.2 (March 16 2014)\n-----------------------------\n\n * had to bump the version number after messing up npmjs.org package registration\n\nVersion 2.3.0 (March 15 2014)\n-----------------------------\n\n * Added support for bower\n * Added support for nodejs (finally)\n * Added ability to run tests in console via nodejs (\"npm install\" to get node-qunit, then \"node test/runner.js\")\n\nVersion 2.2.0 (January 26th 2013)\n---------------------------------\n \n * Added optional `final` state(s) and `isFinished()` helper method (issue #23)\n * extended `fsm.is()` to accept an array of states (in addition to a single state)\n * Added generic event callbacks 'onbeforeevent' and 'onafterevent' (issue #28)\n * Added generic state callbacks 'onleavestate' and 'onenterstate'  (issue #28)\n * Fixed 'undefined' event return codes (issue #34) - pull from gentooboontoo (thanks!)\n * Allow async event transition to be cancelled (issue #22)\n * [read more...](https://jakesgordon.com/writing/javascript-state-machine-v2-2-0/)\n\nVersion 2.1.0 (January 7th 2012)\n--------------------------------\n\n * Wrapped in self executing function to be more easily used with loaders like `require.js` or `curl.js` (issue #15)\n * Allow event to be cancelled by returning `false` from `onleavestate` handler (issue #13) - WARNING: this breaks backward compatibility for async transitions (you now need to return `StateMachine.ASYNC` instead of `false`)\n * Added explicit return values for event methods (issue #12)\n * Added support for wildcard events that can be fired 'from' any state (issue #11)\n * Added support for no-op events that transition 'to' the same state  (issue #5)\n * extended custom error callback to handle any exceptions caused by caller provided callbacks\n * added custom error callback to override exception when an illegal state transition is attempted (thanks to cboone)\n * fixed typos (thanks to cboone)\n * fixed issue #4 - ensure before/after event hooks are called even if the event doesn't result in a state change \n\nVersion 2.0.0 (August 19th 2011)\n--------------------------------\n\n * adding support for asynchronous state transitions (see README) - with lots of qunit tests (see test/async.js).\n * consistent arguments for ALL callbacks, first 3 args are ALWAYS event name, from state and to state, followed by whatever arguments the user passed to the original event method.\n * added a generic `onchangestate(event,from,to)` callback to detect all state changes with a single function.\n * allow callbacks to be declared at creation time (instead of having to attach them afterwards)\n * renamed 'hooks' => 'callbacks'\n * [read more...](https://jakesgordon.com/writing/javascript-state-machine-v2/)\n\nVersion 1.2.0 (June 21st 2011)\n------------------------------\n * allows the same event to transition to different states, depending on the current state (see 'Multiple...' section in README.md)\n * [read more...](https://jakesgordon.com/writing/javascript-state-machine-v1-2-0/)\n\nVersion 1.0.0 (June 1st 2011)\n-----------------------------\n * initial version\n * [read more...](https://jakesgordon.com/writing/javascript-state-machine/)\n"
  },
  {
    "path": "bin/examples",
    "content": "#!/usr/bin/env node\n\n//=================================================================================================\n//\n// This script is used to regenerate the example visualizations\n//\n//=================================================================================================\n\nvar fs    = require('fs'),\n    path  = require('path'),\n    child = require('child_process');\n\n//-------------------------------------------------------------------------------------------------\n\nfs.readdirSync('examples')\n  .filter(function(file) { return path.extname(file) === \".js\" })\n  .map(visualize);\n\n//-------------------------------------------------------------------------------------------------\n\nfunction visualize(example) {\n  var name = path.basename(example, '.js'),\n      fsm  = require('../examples/' + example),\n      dot  = fsm.visualize(),\n      svg  = dot2svg(dot),\n      png  = dot2png(dot);\n  console.log('visualizing examples/' + example);\n  fs.writeFileSync('examples/' + name + '.dot', dot);\n  fs.writeFileSync('examples/' + name + '.svg', svg);\n  fs.writeFileSync('examples/' + name + '.png', png, 'binary');\n}\n\n//-------------------------------------------------------------------------------------------------\n\nfunction dot2svg(dot) {\n  var result = child.spawnSync(\"dot\", [\"-Tsvg\"], { input: dot });\n  if (result.error)\n    dotError(result.error.errno);\n  return result.stdout.toString();\n}\n\n//-------------------------------------------------------------------------------------------------\n\nfunction dot2png(dot) {\n  var result = child.spawnSync(\"dot\", [\"-Tpng\"], { input: dot });\n  if (result.error)\n    dotError(result.error.errno);\n  return result.stdout;\n}\n\n//-------------------------------------------------------------------------------------------------\n\nfunction dotError(errno) {\n  if (errno === 'ENOENT')\n    throw new Error(\"dot program not found. Install graphviz (http://graphviz.org)\")\n  else\n    throw new Error(\"unexpected error: \" + errno)\n}\n\n//-------------------------------------------------------------------------------------------------\n"
  },
  {
    "path": "bin/minify",
    "content": "#!/usr/bin/env node\n\n//=================================================================================================\n//\n// This script is used (by npm run build) to minify the distributed source code\n//\n//=================================================================================================\n\nvar fs     = require('fs-sync'),\n    path   = require('path'),\n    uglify = require('uglify-js'),\n    target = 'dist';\n\n//-------------------------------------------------------------------------------------------------\n\nfs.expand(\"lib/**/*.js\")\n    .map(minify);\n\n//-------------------------------------------------------------------------------------------------\n\nfunction minify(file) {\n  var name     = output_name(file),\n      expanded = path.join(target, name + '.js'),\n      minified = path.join(target, name + '.min.js')\n\n  console.log('copied ' + file + ' to ' + expanded + ' and minified as ' + minified);\n\n  fs.copy(file, expanded, { force: true });\n  fs.write(minified, uglify.minify(expanded).code);\n}\n\nfunction output_name(file) {\n  var name = path.basename(file, '.js');\n  if (name === 'state-machine')\n    return 'state-machine'\n  else\n    return 'state-machine-' + name\n}\n\n//-------------------------------------------------------------------------------------------------\n"
  },
  {
    "path": "dist/state-machine-history.js",
    "content": "(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine(\"StateMachineHistory\", [], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"StateMachineHistory\"] = factory();\n\telse\n\t\troot[\"StateMachineHistory\"] = factory();\n})(this, function() {\nreturn /******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId]) {\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\ti: moduleId,\n/******/ \t\t\tl: false,\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.l = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// identity function for calling harmony imports with the correct context\n/******/ \t__webpack_require__.i = function(value) { return value; };\n/******/\n/******/ \t// define getter function for harmony exports\n/******/ \t__webpack_require__.d = function(exports, name, getter) {\n/******/ \t\tif(!__webpack_require__.o(exports, name)) {\n/******/ \t\t\tObject.defineProperty(exports, name, {\n/******/ \t\t\t\tconfigurable: false,\n/******/ \t\t\t\tenumerable: true,\n/******/ \t\t\t\tget: getter\n/******/ \t\t\t});\n/******/ \t\t}\n/******/ \t};\n/******/\n/******/ \t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t__webpack_require__.n = function(module) {\n/******/ \t\tvar getter = module && module.__esModule ?\n/******/ \t\t\tfunction getDefault() { return module['default']; } :\n/******/ \t\t\tfunction getModuleExports() { return module; };\n/******/ \t\t__webpack_require__.d(getter, 'a', getter);\n/******/ \t\treturn getter;\n/******/ \t};\n/******/\n/******/ \t// Object.prototype.hasOwnProperty.call\n/******/ \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"\";\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(__webpack_require__.s = 1);\n/******/ })\n/************************************************************************/\n/******/ ([\n/* 0 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n//-------------------------------------------------------------------------------------------------\n\nfunction camelize(label) {\n\n  if (label.length === 0)\n    return label;\n\n  var n, result, word, words = label.split(/[_-]/);\n\n  // single word with first character already lowercase, return untouched\n  if ((words.length === 1) && (words[0][0].toLowerCase() === words[0][0]))\n    return label;\n\n  result = words[0].toLowerCase();\n  for(n = 1 ; n < words.length ; n++) {\n    result = result + words[n].charAt(0).toUpperCase() + words[n].substring(1).toLowerCase();\n  }\n\n  return result;\n}\n\n//-------------------------------------------------------------------------------------------------\n\ncamelize.prepended = function(prepend, label) {\n  label = camelize(label);\n  return prepend + label[0].toUpperCase() + label.substring(1);\n}\n\n//-------------------------------------------------------------------------------------------------\n\nmodule.exports = camelize;\n\n\n/***/ }),\n/* 1 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n//-------------------------------------------------------------------------------------------------\n\nvar camelize = __webpack_require__(0);\n\n//-------------------------------------------------------------------------------------------------\n\nmodule.exports = function(options) { options = options || {};\n\n  var past       = camelize(options.name || options.past   || 'history'),\n      future     = camelize(                options.future || 'future'),\n      clear      = camelize.prepended('clear', past),\n      back       = camelize.prepended(past,   'back'),\n      forward    = camelize.prepended(past,   'forward'),\n      canBack    = camelize.prepended('can',   back),\n      canForward = camelize.prepended('can',   forward),\n      max        = options.max;\n\n  var plugin = {\n\n    configure: function(config) {\n      config.addTransitionLifecycleNames(back);\n      config.addTransitionLifecycleNames(forward);\n    },\n\n    init: function(instance) {\n      instance[past]   = [];\n      instance[future] = [];\n    },\n\n    lifecycle: function(instance, lifecycle) {\n      if (lifecycle.event === 'onEnterState') {\n        instance[past].push(lifecycle.to);\n        if (max && instance[past].length > max)\n          instance[past].shift();\n        if (lifecycle.transition !== back && lifecycle.transition !== forward)\n          instance[future].length = 0;\n      }\n    },\n\n    methods:    {},\n    properties: {}\n\n  }\n\n  plugin.methods[clear] = function() {\n    this[past].length = 0\n    this[future].length = 0\n  }\n\n  plugin.properties[canBack] = {\n    get: function() {\n      return this[past].length > 1\n    }\n  }\n\n  plugin.properties[canForward] = {\n    get: function() {\n      return this[future].length > 0\n    }\n  }\n\n  plugin.methods[back] = function() {\n    if (!this[canBack])\n      throw Error('no history');\n    var from = this[past].pop(),\n        to   = this[past].pop();\n    this[future].push(from);\n    this._fsm.transit(back, from, to, []);\n  }\n\n  plugin.methods[forward] = function() {\n    if (!this[canForward])\n      throw Error('no history');\n    var from = this.state,\n        to = this[future].pop();\n    this._fsm.transit(forward, from, to, []);\n  }\n\n  return plugin;\n\n}\n\n\n/***/ })\n/******/ ]);\n});"
  },
  {
    "path": "dist/state-machine-visualize.js",
    "content": "(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine(\"StateMachineVisualize\", [], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"StateMachineVisualize\"] = factory();\n\telse\n\t\troot[\"StateMachineVisualize\"] = factory();\n})(this, function() {\nreturn /******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId]) {\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\ti: moduleId,\n/******/ \t\t\tl: false,\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.l = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// identity function for calling harmony imports with the correct context\n/******/ \t__webpack_require__.i = function(value) { return value; };\n/******/\n/******/ \t// define getter function for harmony exports\n/******/ \t__webpack_require__.d = function(exports, name, getter) {\n/******/ \t\tif(!__webpack_require__.o(exports, name)) {\n/******/ \t\t\tObject.defineProperty(exports, name, {\n/******/ \t\t\t\tconfigurable: false,\n/******/ \t\t\t\tenumerable: true,\n/******/ \t\t\t\tget: getter\n/******/ \t\t\t});\n/******/ \t\t}\n/******/ \t};\n/******/\n/******/ \t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t__webpack_require__.n = function(module) {\n/******/ \t\tvar getter = module && module.__esModule ?\n/******/ \t\t\tfunction getDefault() { return module['default']; } :\n/******/ \t\t\tfunction getModuleExports() { return module; };\n/******/ \t\t__webpack_require__.d(getter, 'a', getter);\n/******/ \t\treturn getter;\n/******/ \t};\n/******/\n/******/ \t// Object.prototype.hasOwnProperty.call\n/******/ \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"\";\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(__webpack_require__.s = 1);\n/******/ })\n/************************************************************************/\n/******/ ([\n/* 0 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nmodule.exports = function(target, sources) {\n  var n, source, key;\n  for(n = 1 ; n < arguments.length ; n++) {\n    source = arguments[n];\n    for(key in source) {\n      if (source.hasOwnProperty(key))\n        target[key] = source[key];\n    }\n  }\n  return target;\n}\n\n\n/***/ }),\n/* 1 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n//-------------------------------------------------------------------------------------------------\n\nvar mixin = __webpack_require__(0)\n\n//-------------------------------------------------------------------------------------------------\n\nfunction visualize(fsm, options) {\n  return dotify(dotcfg(fsm, options));\n}\n\n//-------------------------------------------------------------------------------------------------\n\nfunction dotcfg(fsm, options) {\n\n  options = options || {}\n\n  var config      = dotcfg.fetch(fsm),\n      name        = options.name,\n      rankdir     = dotcfg.rankdir(options.orientation),\n      states      = dotcfg.states(config, options),\n      transitions = dotcfg.transitions(config, options),\n      result      = { }\n\n  if (name)\n    result.name = name\n\n  if (rankdir)\n    result.rankdir = rankdir\n\n  if (states && states.length > 0)\n    result.states = states\n\n  if (transitions && transitions.length > 0)\n    result.transitions = transitions\n\n  return result\n}\n\n//-------------------------------------------------------------------------------------------------\n\ndotcfg.fetch = function(fsm) {\n  return (typeof fsm === 'function') ? fsm.prototype._fsm.config\n                                     : fsm._fsm.config\n}\n\ndotcfg.rankdir = function(orientation) {\n  if (orientation === 'horizontal')\n    return 'LR';\n  else if (orientation === 'vertical')\n    return 'TB';\n}\n\ndotcfg.states = function(config, options) {\n  var index, states = config.states;\n  if (!options.init) { // if not showing init transition, then slice out the implied init :from state\n    index  = states.indexOf(config.init.from);\n    states = states.slice(0, index).concat(states.slice(index+1));\n  }\n  return states;\n}\n\ndotcfg.transitions = function(config, options) {\n  var n, max, transition,\n      init        = config.init,\n      transitions = config.options.transitions || [], // easier to visualize using the ORIGINAL transition declarations rather than our run-time mapping\n      output = [];\n  if (options.init && init.active)\n    dotcfg.transition(init.name, init.from, init.to, init.dot, config, options, output)\n  for (n = 0, max = transitions.length ; n < max ; n++) {\n    transition = config.options.transitions[n]\n    dotcfg.transition(transition.name, transition.from, transition.to, transition.dot, config, options, output)\n  }\n  return output\n}\n\ndotcfg.transition = function(name, from, to, dot, config, options, output) {\n  var n, max, wildcard = config.defaults.wildcard\n\n  if (Array.isArray(from)) {\n    for(n = 0, max = from.length ; n < max ; n++)\n      dotcfg.transition(name, from[n], to, dot, config, options, output)\n  }\n  else if (from === wildcard || from === undefined) {\n    for(n = 0, max = config.states.length ; n < max ; n++)\n      dotcfg.transition(name, config.states[n], to, dot, config, options, output)\n  }\n  else if (to === wildcard || to === undefined) {\n    dotcfg.transition(name, from, from, dot, config, options, output)\n  }\n  else if (typeof to === 'function') {\n    // do nothing, can't display conditional transition\n  }\n  else {\n    output.push(mixin({}, { from: from, to: to, label: pad(name) }, dot || {}))\n  }\n\n}\n\n//-------------------------------------------------------------------------------------------------\n\nfunction pad(name) {\n  return \" \" + name + \" \"\n}\n\nfunction quote(name) {\n  return \"\\\"\" + name + \"\\\"\"\n}\n\nfunction dotify(dotcfg) {\n\n  dotcfg = dotcfg || {};\n\n  var name        = dotcfg.name || 'fsm',\n      states      = dotcfg.states || [],\n      transitions = dotcfg.transitions || [],\n      rankdir     = dotcfg.rankdir,\n      output      = [],\n      n, max;\n\n  output.push(\"digraph \" + quote(name) + \" {\")\n  if (rankdir)\n    output.push(\"  rankdir=\" + rankdir + \";\")\n  for(n = 0, max = states.length ; n < max ; n++)\n    output.push(dotify.state(states[n]))\n  for(n = 0, max = transitions.length ; n < max ; n++)\n    output.push(dotify.edge(transitions[n]))\n  output.push(\"}\")\n  return output.join(\"\\n\")\n\n}\n\ndotify.state = function(state) {\n  return \"  \" + quote(state) + \";\"\n}\n\ndotify.edge = function(edge) {\n  return \"  \" + quote(edge.from) + \" -> \" + quote(edge.to) + dotify.edge.attr(edge) + \";\"\n}\n\ndotify.edge.attr = function(edge) {\n  var n, max, key, keys = Object.keys(edge).sort(), output = [];\n  for(n = 0, max = keys.length ; n < max ; n++) {\n    key = keys[n];\n    if (key !== 'from' && key !== 'to')\n      output.push(key + \"=\" + quote(edge[key]))\n  }\n  return output.length > 0 ? \" [ \" + output.join(\" ; \") + \" ]\" : \"\"\n}\n\n//-------------------------------------------------------------------------------------------------\n\nvisualize.dotcfg = dotcfg;\nvisualize.dotify = dotify;\n\n//-------------------------------------------------------------------------------------------------\n\nmodule.exports = visualize;\n\n//-------------------------------------------------------------------------------------------------\n\n\n/***/ })\n/******/ ]);\n});"
  },
  {
    "path": "dist/state-machine.js",
    "content": "(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine(\"StateMachine\", [], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"StateMachine\"] = factory();\n\telse\n\t\troot[\"StateMachine\"] = factory();\n})(this, function() {\nreturn /******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId]) {\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\ti: moduleId,\n/******/ \t\t\tl: false,\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.l = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// identity function for calling harmony imports with the correct context\n/******/ \t__webpack_require__.i = function(value) { return value; };\n/******/\n/******/ \t// define getter function for harmony exports\n/******/ \t__webpack_require__.d = function(exports, name, getter) {\n/******/ \t\tif(!__webpack_require__.o(exports, name)) {\n/******/ \t\t\tObject.defineProperty(exports, name, {\n/******/ \t\t\t\tconfigurable: false,\n/******/ \t\t\t\tenumerable: true,\n/******/ \t\t\t\tget: getter\n/******/ \t\t\t});\n/******/ \t\t}\n/******/ \t};\n/******/\n/******/ \t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t__webpack_require__.n = function(module) {\n/******/ \t\tvar getter = module && module.__esModule ?\n/******/ \t\t\tfunction getDefault() { return module['default']; } :\n/******/ \t\t\tfunction getModuleExports() { return module; };\n/******/ \t\t__webpack_require__.d(getter, 'a', getter);\n/******/ \t\treturn getter;\n/******/ \t};\n/******/\n/******/ \t// Object.prototype.hasOwnProperty.call\n/******/ \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"\";\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(__webpack_require__.s = 5);\n/******/ })\n/************************************************************************/\n/******/ ([\n/* 0 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nmodule.exports = function(target, sources) {\n  var n, source, key;\n  for(n = 1 ; n < arguments.length ; n++) {\n    source = arguments[n];\n    for(key in source) {\n      if (source.hasOwnProperty(key))\n        target[key] = source[key];\n    }\n  }\n  return target;\n}\n\n\n/***/ }),\n/* 1 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n//-------------------------------------------------------------------------------------------------\n\nvar mixin = __webpack_require__(0);\n\n//-------------------------------------------------------------------------------------------------\n\nmodule.exports = {\n\n  build: function(target, config) {\n    var n, max, plugin, plugins = config.plugins;\n    for(n = 0, max = plugins.length ; n < max ; n++) {\n      plugin = plugins[n];\n      if (plugin.methods)\n        mixin(target, plugin.methods);\n      if (plugin.properties)\n        Object.defineProperties(target, plugin.properties);\n    }\n  },\n\n  hook: function(fsm, name, additional) {\n    var n, max, method, plugin,\n        plugins = fsm.config.plugins,\n        args    = [fsm.context];\n\n    if (additional)\n      args = args.concat(additional)\n\n    for(n = 0, max = plugins.length ; n < max ; n++) {\n      plugin = plugins[n]\n      method = plugins[n][name]\n      if (method)\n        method.apply(plugin, args);\n    }\n  }\n\n}\n\n//-------------------------------------------------------------------------------------------------\n\n\n/***/ }),\n/* 2 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n//-------------------------------------------------------------------------------------------------\n\nfunction camelize(label) {\n\n  if (label.length === 0)\n    return label;\n\n  var n, result, word, words = label.split(/[_-]/);\n\n  // single word with first character already lowercase, return untouched\n  if ((words.length === 1) && (words[0][0].toLowerCase() === words[0][0]))\n    return label;\n\n  result = words[0].toLowerCase();\n  for(n = 1 ; n < words.length ; n++) {\n    result = result + words[n].charAt(0).toUpperCase() + words[n].substring(1).toLowerCase();\n  }\n\n  return result;\n}\n\n//-------------------------------------------------------------------------------------------------\n\ncamelize.prepended = function(prepend, label) {\n  label = camelize(label);\n  return prepend + label[0].toUpperCase() + label.substring(1);\n}\n\n//-------------------------------------------------------------------------------------------------\n\nmodule.exports = camelize;\n\n\n/***/ }),\n/* 3 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n//-------------------------------------------------------------------------------------------------\n\nvar mixin    = __webpack_require__(0),\n    camelize = __webpack_require__(2);\n\n//-------------------------------------------------------------------------------------------------\n\nfunction Config(options, StateMachine) {\n\n  options = options || {};\n\n  this.options     = options; // preserving original options can be useful (e.g visualize plugin)\n  this.defaults    = StateMachine.defaults;\n  this.states      = [];\n  this.transitions = [];\n  this.map         = {};\n  this.lifecycle   = this.configureLifecycle();\n  this.init        = this.configureInitTransition(options.init);\n  this.data        = this.configureData(options.data);\n  this.methods     = this.configureMethods(options.methods);\n\n  this.map[this.defaults.wildcard] = {};\n\n  this.configureTransitions(options.transitions || []);\n\n  this.plugins = this.configurePlugins(options.plugins, StateMachine.plugin);\n\n}\n\n//-------------------------------------------------------------------------------------------------\n\nmixin(Config.prototype, {\n\n  addState: function(name) {\n    if (!this.map[name]) {\n      this.states.push(name);\n      this.addStateLifecycleNames(name);\n      this.map[name] = {};\n    }\n  },\n\n  addStateLifecycleNames: function(name) {\n    this.lifecycle.onEnter[name] = camelize.prepended('onEnter', name);\n    this.lifecycle.onLeave[name] = camelize.prepended('onLeave', name);\n    this.lifecycle.on[name]      = camelize.prepended('on',      name);\n  },\n\n  addTransition: function(name) {\n    if (this.transitions.indexOf(name) < 0) {\n      this.transitions.push(name);\n      this.addTransitionLifecycleNames(name);\n    }\n  },\n\n  addTransitionLifecycleNames: function(name) {\n    this.lifecycle.onBefore[name] = camelize.prepended('onBefore', name);\n    this.lifecycle.onAfter[name]  = camelize.prepended('onAfter',  name);\n    this.lifecycle.on[name]       = camelize.prepended('on',       name);\n  },\n\n  mapTransition: function(transition) {\n    var name = transition.name,\n        from = transition.from,\n        to   = transition.to;\n    this.addState(from);\n    if (typeof to !== 'function')\n      this.addState(to);\n    this.addTransition(name);\n    this.map[from][name] = transition;\n    return transition;\n  },\n\n  configureLifecycle: function() {\n    return {\n      onBefore: { transition: 'onBeforeTransition' },\n      onAfter:  { transition: 'onAfterTransition'  },\n      onEnter:  { state:      'onEnterState'       },\n      onLeave:  { state:      'onLeaveState'       },\n      on:       { transition: 'onTransition'       }\n    };\n  },\n\n  configureInitTransition: function(init) {\n    if (typeof init === 'string') {\n      return this.mapTransition(mixin({}, this.defaults.init, { to: init, active: true }));\n    }\n    else if (typeof init === 'object') {\n      return this.mapTransition(mixin({}, this.defaults.init, init, { active: true }));\n    }\n    else {\n      this.addState(this.defaults.init.from);\n      return this.defaults.init;\n    }\n  },\n\n  configureData: function(data) {\n    if (typeof data === 'function')\n      return data;\n    else if (typeof data === 'object')\n      return function() { return data; }\n    else\n      return function() { return {};  }\n  },\n\n  configureMethods: function(methods) {\n    return methods || {};\n  },\n\n  configurePlugins: function(plugins, builtin) {\n    plugins = plugins || [];\n    var n, max, plugin;\n    for(n = 0, max = plugins.length ; n < max ; n++) {\n      plugin = plugins[n];\n      if (typeof plugin === 'function')\n        plugins[n] = plugin = plugin()\n      if (plugin.configure)\n        plugin.configure(this);\n    }\n    return plugins\n  },\n\n  configureTransitions: function(transitions) {\n    var i, n, transition, from, to, wildcard = this.defaults.wildcard;\n    for(n = 0 ; n < transitions.length ; n++) {\n      transition = transitions[n];\n      from  = Array.isArray(transition.from) ? transition.from : [transition.from || wildcard]\n      to    = transition.to || wildcard;\n      for(i = 0 ; i < from.length ; i++) {\n        this.mapTransition({ name: transition.name, from: from[i], to: to });\n      }\n    }\n  },\n\n  transitionFor: function(state, transition) {\n    var wildcard = this.defaults.wildcard;\n    return this.map[state][transition] ||\n           this.map[wildcard][transition];\n  },\n\n  transitionsFor: function(state) {\n    var wildcard = this.defaults.wildcard;\n    return Object.keys(this.map[state]).concat(Object.keys(this.map[wildcard]));\n  },\n\n  allStates: function() {\n    return this.states;\n  },\n\n  allTransitions: function() {\n    return this.transitions;\n  }\n\n});\n\n//-------------------------------------------------------------------------------------------------\n\nmodule.exports = Config;\n\n//-------------------------------------------------------------------------------------------------\n\n\n/***/ }),\n/* 4 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\nvar mixin      = __webpack_require__(0),\n    Exception  = __webpack_require__(6),\n    plugin     = __webpack_require__(1),\n    UNOBSERVED = [ null, [] ];\n\n//-------------------------------------------------------------------------------------------------\n\nfunction JSM(context, config) {\n  this.context   = context;\n  this.config    = config;\n  this.state     = config.init.from;\n  this.observers = [context];\n}\n\n//-------------------------------------------------------------------------------------------------\n\nmixin(JSM.prototype, {\n\n  init: function(args) {\n    mixin(this.context, this.config.data.apply(this.context, args));\n    plugin.hook(this, 'init');\n    if (this.config.init.active)\n      return this.fire(this.config.init.name, []);\n  },\n\n  is: function(state) {\n    return Array.isArray(state) ? (state.indexOf(this.state) >= 0) : (this.state === state);\n  },\n\n  isPending: function() {\n    return this.pending;\n  },\n\n  can: function(transition) {\n    return !this.isPending() && !!this.seek(transition);\n  },\n\n  cannot: function(transition) {\n    return !this.can(transition);\n  },\n\n  allStates: function() {\n    return this.config.allStates();\n  },\n\n  allTransitions: function() {\n    return this.config.allTransitions();\n  },\n\n  transitions: function() {\n    return this.config.transitionsFor(this.state);\n  },\n\n  seek: function(transition, args) {\n    var wildcard = this.config.defaults.wildcard,\n        entry    = this.config.transitionFor(this.state, transition),\n        to       = entry && entry.to;\n    if (typeof to === 'function')\n      return to.apply(this.context, args);\n    else if (to === wildcard)\n      return this.state\n    else\n      return to\n  },\n\n  fire: function(transition, args) {\n    return this.transit(transition, this.state, this.seek(transition, args), args);\n  },\n\n  transit: function(transition, from, to, args) {\n\n    var lifecycle = this.config.lifecycle,\n        changed   = this.config.options.observeUnchangedState || (from !== to);\n\n    if (!to)\n      return this.context.onInvalidTransition(transition, from, to);\n\n    if (this.isPending())\n      return this.context.onPendingTransition(transition, from, to);\n\n    this.config.addState(to);  // might need to add this state if it's unknown (e.g. conditional transition or goto)\n\n    this.beginTransit();\n\n    args.unshift({             // this context will be passed to each lifecycle event observer\n      transition: transition,\n      from:       from,\n      to:         to,\n      fsm:        this.context\n    });\n\n    return this.observeEvents([\n                this.observersForEvent(lifecycle.onBefore.transition),\n                this.observersForEvent(lifecycle.onBefore[transition]),\n      changed ? this.observersForEvent(lifecycle.onLeave.state) : UNOBSERVED,\n      changed ? this.observersForEvent(lifecycle.onLeave[from]) : UNOBSERVED,\n                this.observersForEvent(lifecycle.on.transition),\n      changed ? [ 'doTransit', [ this ] ]                       : UNOBSERVED,\n      changed ? this.observersForEvent(lifecycle.onEnter.state) : UNOBSERVED,\n      changed ? this.observersForEvent(lifecycle.onEnter[to])   : UNOBSERVED,\n      changed ? this.observersForEvent(lifecycle.on[to])        : UNOBSERVED,\n                this.observersForEvent(lifecycle.onAfter.transition),\n                this.observersForEvent(lifecycle.onAfter[transition]),\n                this.observersForEvent(lifecycle.on[transition])\n    ], args);\n  },\n\n  beginTransit: function()          { this.pending = true;                 },\n  endTransit:   function(result)    { this.pending = false; return result; },\n  failTransit:  function(result)    { this.pending = false; throw result;  },\n  doTransit:    function(lifecycle) { this.state = lifecycle.to;           },\n\n  observe: function(args) {\n    if (args.length === 2) {\n      var observer = {};\n      observer[args[0]] = args[1];\n      this.observers.push(observer);\n    }\n    else {\n      this.observers.push(args[0]);\n    }\n  },\n\n  observersForEvent: function(event) { // TODO: this could be cached\n    var n = 0, max = this.observers.length, observer, result = [];\n    for( ; n < max ; n++) {\n      observer = this.observers[n];\n      if (observer[event])\n        result.push(observer);\n    }\n    return [ event, result, true ]\n  },\n\n  observeEvents: function(events, args, previousEvent, previousResult) {\n    if (events.length === 0) {\n      return this.endTransit(previousResult === undefined ? true : previousResult);\n    }\n\n    var event     = events[0][0],\n        observers = events[0][1],\n        pluggable = events[0][2];\n\n    args[0].event = event;\n    if (event && pluggable && event !== previousEvent)\n      plugin.hook(this, 'lifecycle', args);\n\n    if (observers.length === 0) {\n      events.shift();\n      return this.observeEvents(events, args, event, previousResult);\n    }\n    else {\n      var observer = observers.shift(),\n          result = observer[event].apply(observer, args);\n      if (result && typeof result.then === 'function') {\n        return result.then(this.observeEvents.bind(this, events, args, event))\n                     .catch(this.failTransit.bind(this))\n      }\n      else if (result === false) {\n        return this.endTransit(false);\n      }\n      else {\n        return this.observeEvents(events, args, event, result);\n      }\n    }\n  },\n\n  onInvalidTransition: function(transition, from, to) {\n    throw new Exception(\"transition is invalid in current state\", transition, from, to, this.state);\n  },\n\n  onPendingTransition: function(transition, from, to) {\n    throw new Exception(\"transition is invalid while previous transition is still in progress\", transition, from, to, this.state);\n  }\n\n});\n\n//-------------------------------------------------------------------------------------------------\n\nmodule.exports = JSM;\n\n//-------------------------------------------------------------------------------------------------\n\n\n/***/ }),\n/* 5 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n//-----------------------------------------------------------------------------------------------\n\nvar mixin    = __webpack_require__(0),\n    camelize = __webpack_require__(2),\n    plugin   = __webpack_require__(1),\n    Config   = __webpack_require__(3),\n    JSM      = __webpack_require__(4);\n\n//-----------------------------------------------------------------------------------------------\n\nvar PublicMethods = {\n  is:                  function(state)       { return this._fsm.is(state)                                     },\n  can:                 function(transition)  { return this._fsm.can(transition)                               },\n  cannot:              function(transition)  { return this._fsm.cannot(transition)                            },\n  observe:             function()            { return this._fsm.observe(arguments)                            },\n  transitions:         function()            { return this._fsm.transitions()                                 },\n  allTransitions:      function()            { return this._fsm.allTransitions()                              },\n  allStates:           function()            { return this._fsm.allStates()                                   },\n  onInvalidTransition: function(t, from, to) { return this._fsm.onInvalidTransition(t, from, to)              },\n  onPendingTransition: function(t, from, to) { return this._fsm.onPendingTransition(t, from, to)              },\n}\n\nvar PublicProperties = {\n  state: {\n    configurable: false,\n    enumerable:   true,\n    get: function() {\n      return this._fsm.state;\n    },\n    set: function(state) {\n      throw Error('use transitions to change state')\n    }\n  }\n}\n\n//-----------------------------------------------------------------------------------------------\n\nfunction StateMachine(options) {\n  return apply(this || {}, options);\n}\n\nfunction factory() {\n  var cstor, options;\n  if (typeof arguments[0] === 'function') {\n    cstor   = arguments[0];\n    options = arguments[1] || {};\n  }\n  else {\n    cstor   = function() { this._fsm.apply(this, arguments) };\n    options = arguments[0] || {};\n  }\n  var config = new Config(options, StateMachine);\n  build(cstor.prototype, config);\n  cstor.prototype._fsm.config = config; // convenience access to shared config without needing an instance\n  return cstor;\n}\n\n//-------------------------------------------------------------------------------------------------\n\nfunction apply(instance, options) {\n  var config = new Config(options, StateMachine);\n  build(instance, config);\n  instance._fsm();\n  return instance;\n}\n\nfunction build(target, config) {\n  if ((typeof target !== 'object') || Array.isArray(target))\n    throw Error('StateMachine can only be applied to objects');\n  plugin.build(target, config);\n  Object.defineProperties(target, PublicProperties);\n  mixin(target, PublicMethods);\n  mixin(target, config.methods);\n  config.allTransitions().forEach(function(transition) {\n    target[camelize(transition)] = function() {\n      return this._fsm.fire(transition, [].slice.call(arguments))\n    }\n  });\n  target._fsm = function() {\n    this._fsm = new JSM(this, config);\n    this._fsm.init(arguments);\n  }\n}\n\n//-----------------------------------------------------------------------------------------------\n\nStateMachine.version  = '3.0.1';\nStateMachine.factory  = factory;\nStateMachine.apply    = apply;\nStateMachine.defaults = {\n  wildcard: '*',\n  init: {\n    name: 'init',\n    from: 'none'\n  }\n}\n\n//===============================================================================================\n\nmodule.exports = StateMachine;\n\n\n/***/ }),\n/* 6 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nmodule.exports = function(message, transition, from, to, current) {\n  this.message    = message;\n  this.transition = transition;\n  this.from       = from;\n  this.to         = to;\n  this.current    = current;\n}\n\n\n/***/ })\n/******/ ]);\n});"
  },
  {
    "path": "docs/async-transitions.md",
    "content": "# Asynchronous Transitions\n\n> You should be familiar with the state machine [Lifecycle Events](lifecycle-events.md) before reading this article.\n\nSometimes, you need to execute some asynchronous code during a state transition and ensure the new\nstate is not entered until your code has completed. A good example of this is when you transition\nout of a state and want to gradually fade a UI component away, or slide it off the screen, and\ndon't want to transition to the next state until after that animation has completed.\n\nYou can achieve this by returning a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)\nobject from any of the [Lifecycle Events](lifecycle-events.md).\n\nReturning a Promise from a lifecycle event will cause the lifecycle for that transition to\npause. It can be continued by resolving the promise, or cancelled by rejecting the promise.\n\nFor example (using jQuery effects):\n\n```javascript\n  var fsm = new StateMachine({\n\n    init: 'menu',\n\n    transitions: [\n      { name: 'play', from: 'menu', to: 'game' },\n      { name: 'quit', from: 'game', to: 'menu' }\n    ],\n\n    methods: {\n\n      onEnterMenu: function() {\n        return new Promise(function(resolve, reject) {\n          $('#menu').fadeIn('fast', resolve)\n        })\n      },\n\n      onEnterGame: function() {\n        return new Promise(function(resolve, reject) {\n          $('#game').fadeIn('fast', resolve)\n        })\n      },\n\n      onLeaveMenu: function() {\n        return new Promise(function(resolve, reject) {\n          $('#menu').fadeOut('fast', resolve)\n        })\n      },\n\n      onLeaveGame: function() {\n        return new Promise(function(resolve, reject) {\n          $('#game').fadeOut('fast', resolve)\n        })\n      }\n    }\n\n  })\n```\n\n> Be sure that you always resolve (or reject) your Promise eventually, otherwise the state\n  machine will be stuck forever within that pending transition.\n"
  },
  {
    "path": "docs/contributing.md",
    "content": "# Contributing\n\nThe `javascript-state-machine` library is built using:\n\n  * [Webpack 2](https://webpack.js.org/concepts/) - for bundling javascript modules together\n  * [UglifyJS2](https://github.com/mishoo/UglifyJS2) - for minifying bundled javascript files\n  * [Ava](https://github.com/avajs/ava) - for testing\n\nThe directory structure includes:\n\n```shell\n  /bin              # - build scripts\n  /dist             # - minified bundles for distribution\n  /docs             # - documentation\n  /examples         # - example visualizations\n  /lib              # - bundled source code for npm\n  /src              # - source code\n  /test             # - unit tests\n\n  package.json      # - npm configuration\n  webpack.config.js # - webpack configuration\n\n  LICENSE           # - the project licensing terms\n  README.md         # - the project readme\n  RELEASE_NOTES.md  # - the project release notes\n\n```\n\nBuild time dependencies can be installed using npm:\n\n```shell\n  > npm install\n```\n\nA number of npm scripts are available:\n\n```shell\n  > npm run test    # run unit tests\n  > npm run build   # bundle and minify files for distribution\n  > npm run watch   # run tests if source files change\n```\n\n## Source Code\n\nThe source code is written in es5 syntax and should be supported by all [es5 compatible browsers](http://caniuse.com/#feat=es5).\n[Babel](https://babeljs.io/) is **NOT** used for this project. Webpack is used to\nbundle modules together for distribution.\n\n## Submitting Pull Requests\n\nGenerally speaking, please raise an issue first and lets discuss the problem and the\nproposed solution. The next step would be a pull-request - fantastic and thank you for helping out - but\nplease try to...\n\n  * ensure the tests pass (`npm test`).\n  * rebuild distribution files (`npm run build`).\n  * include tests for your changes.\n  * include documentation for your changes.\n  * include a great commit message.\n"
  },
  {
    "path": "docs/data-and-methods.md",
    "content": "# Data and Methods\n\nIn addition to [States](states-and-transitions.md) and [Transitions](states-and-transitions.md), a state machine can\nalso contain arbitrary data and methods:\n\n```javascript\n  var fsm = new StateMachine({\n    init: 'A',\n    transitions: [\n      { name: 'step', from: 'A', to: 'B' }\n    ],\n    data: {\n      color: 'red'\n    },\n    methods: {\n      describe: function() {\n        console.log('I am ' + this.color);\n      }\n    }\n  });\n\n  fsm.state;      // 'A'\n  fsm.color;      // 'red'\n  fsm.describe(); // 'I am red'\n```\n\n## Data and State Machine Factories\n\nIf you are constructing multiple instances from a [State Machine Factory](state-machine-factory.md) then the\n`data` object will be shared amongst them. This is almost certainly **NOT** what you want! To\nensure that each instance gets unique data you should use a `data` method instead:\n\n```javascript\n  var FSM = StateMachine.factory({\n    init: 'A',\n    transitions: [\n      { name: 'step', from: 'A', to: 'B' }\n    ],\n    data: function(color) {      //  <-- use a method that can be called for each instance\n      return {\n        color: color\n      }\n    },\n    methods: {\n      describe: function() {\n        console.log('I am ' + this.color);\n      }\n    }\n  });\n\n  var a = new FSM('red'),\n      b = new FSM('blue');\n\n  a.state; // 'A'\n  b.state; // 'A'\n\n  a.color; // 'red'\n  b.color; // 'blue'\n\n  a.describe(); // 'I am red'\n  b.describe(); // 'I am blue'\n```\n\n> NOTE: that arguments used when constructing each instance are passed thru to the `data` method directly.\n"
  },
  {
    "path": "docs/error-handling.md",
    "content": "# Error Handling\n\n## Invalid Transitions\n\nBy default, if you try to fire a transition that is not allowed in the current state, the\nstate machine will throw an exception. If you prefer to handle the problem yourself, you can\ndefine a custom `onInvalidTransition` handler:\n\n```javascript\n  var fsm = new StateMachine({\n    init: 'A',\n    transitions: [\n      { name: 'step',  from: 'A', to: 'B' },\n      { name: 'reset', from: 'B', to: 'A' }\n    ],\n    methods: {\n      onInvalidTransition: function(transition, from, to) {\n        throw new Exception(\"transition not allowed from that state\");\n      }\n    }\n  });\n\n  fsm.state;        // 'A'\n  fsm.can('step');  // true\n  fsm.can('reset'); // false\n\n  fsm.reset();      //  <-- throws \"transition not allowed from that state\"\n```\n\n## Pending Transitions\n\nBy default, if you try to fire a transition during a [Lifecycle Event](lifecycle-events.md) for a\npending transition, the state machine will throw an exception. If you prefer to handle the problem\nyourself, you can define a custom `onPendingTransition` handler:\n\n```javascript\n  var fsm = new StateMachine({\n    init: 'A',\n    transitions: [\n      { name: 'step', from: 'A', to: 'B' },\n      { name: 'step', from: 'B', to: 'C' }\n    ],\n    methods: {\n      onLeaveA: function() {\n        this.step();    //  <-- uh oh, trying to transition from within a lifecycle event is not allowed\n      },\n      onPendingTransition: function(transition, from, to) {\n        throw new Exception(\"transition already in progress\");\n      }\n    }\n  });\n\n  fsm.state;       // 'A'\n  fsm.can('step'), // true\n  fsm.step();      //  <-- throws \"transition already in progress\"\n```\n"
  },
  {
    "path": "docs/initialization.md",
    "content": "# Initialization Options\n\n## Explicit Init Transition\n\nBy default, if you don't specify an initial state, the state machine will be in the `none`\nstate, no lifecycle events will fire during construction, and you will need to provide an\nexplicit transition to advance out of this state:\n\n```javascript\n  var fsm = new StateMachine({\n    transitions: [\n      { name: 'init', from: 'none', to: 'A' },\n      { name: 'step', from: 'A',    to: 'B' },\n      { name: 'step', from: 'B',    to: 'C' }\n    ]\n  });\n  fsm.state;    // 'none'\n  fsm.init();   // 'init()' transition is fired explicitly\n  fsm.state;    // 'A'\n```\n\n## Implicit Init Transition\n\nIf you specify the name of your initial state (as in most of the examples in this documentation),\nthen an implicit `init` transition will be created for you and fired (along with appropriate\nlifecycle events) when the state machine is constructed.\n\n> This is the most common initialization strategy, and the one you should use 90% of the time\n\n```javascript\n  var fsm = new StateMachine({\n    init: 'A',\n    transitions: [\n      { name: 'step', from: 'A', to: 'B' },\n      { name: 'step', from: 'B', to: 'C' }\n    ]\n  });           // 'init()' transition fires from 'none' to 'A' during construction\n  fsm.state;    // 'A'\n```\n\n## Initialization and State Machine Factories\n\nFor [State Machine Factories](state-machine-factory.md), the `init` transition\nis triggered for each constructed instance.\n\n```javascript\n  var FSM = StateMachine.factory({\n    init: 'A',\n    transitions: [\n      { name: 'step', from: 'A', to: 'B' },\n      { name: 'step', from: 'B', to: 'C' }\n    ]\n  });\n\n  var fsm1 = new FSM(),   // 'init()' transition fires from 'none' to 'A' for fsm1\n      fsm2 = new FSM();   // 'init()' transition fires from 'none' to 'A' for fsm2\n```\n"
  },
  {
    "path": "docs/lifecycle-events.md",
    "content": "# Lifecycle Events\n\nIn order to track or perform an action when a transition occurs, five\ngeneral-purpose lifecycle events can be observed:\n\n  * `onBeforeTransition` - fired before any transition\n  * `onLeaveState`       - fired when leaving any state\n  * `onTransition`       - fired during any transition\n  * `onEnterState`       - fired when entering any state\n  * `onAfterTransition`  - fired after any transition\n\nIn addition to the general-purpose events, transitions can be observed\nusing your specific transition and state names:\n\n  * `onBefore<TRANSITION>` - fired before a specific TRANSITION begins\n  * `onLeave<STATE>`       - fired when leaving a specific STATE\n  * `onEnter<STATE>`       - fired when entering a specific STATE\n  * `onAfter<TRANSITION>`  - fired after a specific TRANSITION completes\n\nFor convenience, the 2 most useful events can be shortened:\n\n  * `on<TRANSITION>` - convenience shorthand for `onAfter<TRANSITION>`\n  * `on<STATE>`      - convenience shorthand for `onEnter<STATE>`\n\n## Observing Lifecycle Events\n\nIndividual lifecycle events can be observed using an observer method:\n\n```javascript\n  fsm.observe('onStep', function() {\n    console.log('stepped');\n  });\n```\n\nMultiple events can be observed using an observer object:\n\n```javascript\n  fsm.observe({\n    onStep: function() { console.log('stepped');         }\n    onA:    function() { console.log('entered state A'); }\n    onB:    function() { console.log('entered state B'); }\n  });\n```\n\nA state machine always observes its own lifecycle events:\n\n```javascript\n  var fsm = new StateMachine({\n    init: 'A',\n    transitions: [\n      { name: 'step', from: 'A', to: 'B' }\n    ],\n    methods: {\n      onStep: function() { console.log('stepped');         }\n      onA:    function() { console.log('entered state A'); }\n      onB:    function() { console.log('entered state B'); }\n    }\n  });\n```\n\n## Lifecycle Event Arguments\n\nObservers will be passed a single argument containing a `lifecycle` object with the following attributes:\n\n  * **transition** - the transition name\n  * **from**       - the previous state\n  * **to**         - the next state\n\nIn addition to the `lifecycle` argument, the observer will receive any arbitrary arguments passed\ninto the transition method\n\n```javascript\n  var fsm = new StateMachine({\n    transitions: [\n      { name: 'step', from: 'A', to: 'B' }\n    ],\n    methods: {\n      onTransition: function(lifecycle, arg1, arg2) {\n        console.log(lifecycle.transition); // 'step'\n        console.log(lifecycle.from);       // 'A'\n        console.log(lifecycle.to);         // 'B'\n        console.log(arg1);                 // 42\n        console.log(arg2);                 // 'hello'\n      }\n    }\n  });\n\n  fsm.step(42, 'hello');\n```\n\n## Lifecycle Event Names\n\nLifecycle event names always use standard javascipt camelCase, even if your transition and\nstate names do not:\n\n```javascript\n  var fsm = new StateMachine({\n    transitions: [\n      { name: 'do-with-dash',       from: 'has-dash',        to: 'has_underscore'   },\n      { name: 'do_with_underscore', from: 'has_underscore',  to: 'alreadyCamelized' },\n      { name: 'doAlreadyCamelized', from: 'alreadyCamelize', to: 'has-dash'         }\n    ],\n    methods: {\n      onBeforeDoWithDash:         function() { /* ... */ },\n      onBeforeDoWithUnderscore:   function() { /* ... */ },\n      onBeforeDoAlreadyCamelized: function() { /* ... */ },\n      onLeaveHasDash:             function() { /* ... */ },\n      onLeaveHasUnderscore:       function() { /* ... */ },\n      onLeaveAlreadyCamelized:    function() { /* ... */ },\n      onEnterHasDash:             function() { /* ... */ },\n      onEnterHasUnderscore:       function() { /* ... */ },\n      onEnterAlreadyCamelized:    function() { /* ... */ },\n      onAfterDoWithDash:          function() { /* ... */ },\n      onAfterDoWithUnderscore:    function() { /* ... */ },\n      onAfterDoAlreadyCamelized:  function() { /* ... */ }\n    }\n  });\n```\n\n# Lifecycle Events Listed in Order\n\nTo recap, the lifecycle of a transition occurs in the following order:\n\n  * `onBeforeTransition`   - fired before any transition\n  * `onBefore<TRANSITION>` - fired before a specific TRANSITION\n  * `onLeaveState`         - fired when leaving any state\n  * `onLeave<STATE>`       - fired when leaving a specific STATE\n  * `onTransition`         - fired during any transition\n  * `onEnterState`         - fired when entering any state\n  * `onEnter<STATE>`       - fired when entering a specific STATE\n  * `on<STATE>`            - convenience shorthand for `onEnter<STATE>`\n  * `onAfterTransition`    - fired after any transition\n  * `onAfter<TRANSITION>`  - fired after a specific TRANSITION\n  * `on<TRANSITION>`       - convenience shorthand for `onAfter<TRANSITION>`\n\n# Cancelling a Transition\n\nAny observer can cancel a transition by explicitly returning `false` during any of the following\nlifecycle events:\n\n  * `onBeforeTransition`\n  * `onBefore<TRANSITION>`\n  * `onLeaveState`\n  * `onLeave<STATE>`\n  * `onTransition`\n\nAll subsequent lifecycle events will be cancelled and the state will remain unchanged.\n\n"
  },
  {
    "path": "docs/state-history.md",
    "content": "# Remembering State History\n\nBy default, a state machine only tracks its current state. If you wish to track the state history\nyou can extend the state machine with the `state-machine-history` plugin.\n\n```javascript\n  var StateMachineHistory = require('javascript-state-machine/lib/history')\n```\n\n```javascript\n\n  var fsm = new StateMachine({\n    init: 'A',\n    transitions: [\n      { name: 'step', from: 'A', to: 'B' },\n      { name: 'step', from: 'B', to: 'C' },\n      { name: 'step', from: 'C', to: 'D' }\n    ],\n    plugins: [\n      new StateMachineHistory()     //  <-- plugin enabled here\n    ]\n  })\n\n  fsm.history;        // [ 'A' ]\n  fsm.step();\n  fsm.history;        // [ 'A', 'B' ]\n  fsm.step();\n  fsm.history;        // [ 'A', 'B', 'C' ]\n\n  fsm.clearHistory();\n\n  fsm.history;        // [ ]\n\n```\n## Traversing History\n\nYou can traverse back through history using the `historyBack` and `historyForward` methods:\n\n```javascript\n  var fsm = new StateMachine({\n    init: 'A',\n    transitions: [\n      { name: 'step', from: 'A', to: 'B' },\n      { name: 'step', from: 'B', to: 'C' },\n      { name: 'step', from: 'C', to: 'D' }\n    ]\n  })\n\n  fsm.step();\n  fsm.step();\n  fsm.step();\n\n  fsm.state;    //                  'D'\n  fsm.history;  // [ 'A', 'B', 'C', 'D' ]\n\n  fsm.historyBack();\n\n  fsm.state;    //             'C'\n  fsm.history;  // [ 'A', 'B', 'C' ]\n\n  fsm.historyBack();\n\n  fsm.state;    //        'B'\n  fsm.history;  // [ 'A', 'B' ]\n\n  fsm.historyForward();\n\n  fsm.state;    // 'C'\n  fsm.history;  // [ 'A', 'B', 'C' ]\n```\n\nYou can test if history traversal is allowed using the following properties:\n\n```javascript\n  fsm.canHistoryBack;     // true/false\n  fsm.canHistoryForward;  // true/false\n```\n\nA full set of [Lifecycle Events](lifecycle-events.md) will still apply when traversing history with\n`historyBack` and `historyForward`.\n\n## Limiting History\n\nBy default, the state machine history is unbounded and will continue to grow until cleared. You\ncan limit storage to only the last N states by configuring the plugin:\n\n``` javascript\n  var fsm = new StateMachine({\n    plugins: [\n      new StateMachineHistory({ max: 100 })      //  <-- plugin configuration\n    ]\n  })\n```\n\n## Customizing History\n\nIf the `history` terminology clashes with your existing state machine attributes or methods, you\ncan enable the plugin with a different name:\n\n```javascript\n  var fsm = new StateMachine({\n    init: 'A',\n    transitions: [\n      { name: 'step', from: 'A', to: 'B' },\n      { name: 'step', from: 'B', to: 'C' },\n      { name: 'step', from: 'C', to: 'D' }\n    ],\n    plugins: [\n      new StateMachineHistory({ name: 'memory' })\n    ]\n  })\n\n  fsm.step();\n  fsm.step();\n\n  fsm.memory;         // [ 'A', 'B', 'C' ]\n\n  fsm.memoryBack();\n  fsm.memory;         // [ 'A', 'B' ]\n\n  fsm.memoryForward();\n  fsm.memory;         // [ 'A', 'B', 'C' ]\n\n  fsm.clearMemory();\n  fsm.memory;         // [ ]\n```\n\n"
  },
  {
    "path": "docs/state-machine-factory.md",
    "content": "# State Machine Factory\n\nMost examples in this documentation construct a single state machine instance, for example:\n\n```javascript\n  var fsm = new StateMachine({\n    init: 'solid',\n    transitions: [\n      { name: 'melt',     from: 'solid',  to: 'liquid' },\n      { name: 'freeze',   from: 'liquid', to: 'solid'  },\n      { name: 'vaporize', from: 'liquid', to: 'gas'    },\n      { name: 'condense', from: 'gas',    to: 'liquid' }\n    ]\n  });\n```\n\nIf you wish to construct multiple instances using the same configuration you should use a State\nMachine Factory. A State Machine Factory provides a javascript constructor function (e.g. a 'class')\nthat can be instantiated multiple times:\n\n```javascript\n  var Matter = StateMachine.factory({     //  <-- the factory is constructed here\n      init: 'solid',\n      transitions: [\n        { name: 'melt',     from: 'solid',  to: 'liquid' },\n        { name: 'freeze',   from: 'liquid', to: 'solid'  },\n        { name: 'vaporize', from: 'liquid', to: 'gas'    },\n        { name: 'condense', from: 'gas',    to: 'liquid' }\n      ]\n  });\n\n  var a = new Matter(),    //  <-- instances are constructed here\n      b = new Matter(),\n      c = new Matter();\n\n  b.melt();\n  c.melt();\n  c.vaporize();\n\n  a.state;    // solid\n  b.state;    // liquid\n  c.state;    // gas\n```\n\nUsing the factory, each state machine instance is a unique javascript object. Each instance manages\nits own `state` property, but methods are shared via the normal javascript prototype mechanism.\n\n> NOTE: be aware of special case handling required for [Data and State Machine Factories](data-and-methods.md#data-and-state-machine-factories)\n \n## Applying State Machine Behavior to Existing Objects\n\nOccasionally, you may wish to apply state machine behavior to an already existing\nobject (e.g. a react component). You can achieve this using the `StateMachine.apply` method:\n\n```javascript\n  var component = { /* ... */ };\n\n  StateMachine.apply(component, {\n    init: 'A',\n    transitions: {\n      { name: 'step', from: 'A', to: 'B' }\n    }\n  });\n```\n\n> Be careful not to use state or transition names that will clash with existing object properties.\n\n## Applying State Machine Factory Behavior to Existing Classes\n\nYou can also apply state machine factory behavior to an existing class, however you must now\ntake responsibility for initialization by calling `this._fsm()` from within your class\nconstructor method:\n\n```javascript\n  function Person(name) {\n    this.name = name;\n    this._fsm(); //  <-- IMPORTANT\n  }\n\n  Person.prototype = {\n    speak: function() {\n      console.log('my name is ' + this.name + ' and I am ' + this.state);\n    }\n  }\n\n  StateMachine.factory(Person, {\n    init: 'idle',\n    transitions: {\n      { name: 'sleep', from: 'idle',     to: 'sleeping' },\n      { name: 'wake',  from: 'sleeping', to: 'idle'     }\n    }\n  });\n\n  var amy = new Person('amy'),\n      bob = new Person('bob');\n\n  bob.sleep();\n\n  amy.state;   // 'idle'\n  bob.state;   // 'sleeping'\n\n  amy.speak(); // 'my name is amy and I am idle'\n  bob.speak(); // 'my name is bob and I am sleeping'\n```\n"
  },
  {
    "path": "docs/states-and-transitions.md",
    "content": "# States and Transitions\n\n![matter state machine](../examples/matter.png)\n\nA state machine consists of a set of **states**, e.g:\n\n  * solid\n  * liquid\n  * gas\n\n.. and a set of **transitions**, e.g:\n\n  * melt\n  * freeze\n  * vaporize\n  * condense\n\n```javascript\n  var fsm = new StateMachine({\n    init: 'solid',\n    transitions: [\n      { name: 'melt',     from: 'solid',  to: 'liquid' },\n      { name: 'freeze',   from: 'liquid', to: 'solid'  },\n      { name: 'vaporize', from: 'liquid', to: 'gas'    },\n      { name: 'condense', from: 'gas',    to: 'liquid' }\n    ]\n  });\n\n  fsm.state;             // 'solid'\n  fsm.melt();\n  fsm.state;             // 'liquid'\n  fsm.vaporize();\n  fsm.state;             // 'gas'\n```\n\n## Multiple states for a transition\n\n![wizard state machine](../examples/wizard.png)\n\nIf a transition is allowed `from` multiple states then declare the transitions with the same name:\n\n```javascript\n  { name: 'step',  from: 'A', to: 'B' },\n  { name: 'step',  from: 'B', to: 'C' },\n  { name: 'step',  from: 'C', to: 'D' }\n```\n\nIf a transition with multiple `from` states always transitions `to` the same state, e.g:\n\n```javascript\n  { name: 'reset', from: 'B', to: 'A' },\n  { name: 'reset', from: 'C', to: 'A' },\n  { name: 'reset', from: 'D', to: 'A' }\n```\n\n... then it can be abbreviated using an array of `from` states:\n\n```javascript\n  { name: 'reset', from: [ 'B', 'C', 'D' ], to: 'A' }\n```\n\nCombining these into a single example:\n\n```javascript\n  var fsm = new StateMachine({\n    init: 'A',\n    transitions: [\n      { name: 'step',  from: 'A',               to: 'B' },\n      { name: 'step',  from: 'B',               to: 'C' },\n      { name: 'step',  from: 'C',               to: 'D' },\n      { name: 'reset', from: [ 'B', 'C', 'D' ], to: 'A' }\n    ]\n  })\n```\n\nThis example will create an object with 2 transition methods:\n\n  * `fsm.step()`\n  * `fsm.reset()`\n\nThe `reset` transition will always end up in the `A` state, while the `step` transition\nwill end up in a state that is dependent on the current state.\n\n## Wildcard Transitions\n\nIf a transition is appropriate from **any** state, then a wildcard '*' `from` state can be used:\n\n```javascript\n  var fsm = new StateMachine({\n    transitions: [\n      // ...\n      { name: 'reset', from: '*', to: 'A' }\n    ]\n  });\n```\n\n## Conditional Transitions\n\nA transition can choose the target state at run-time by providing a function as the `to` attribute:\n\n```javascript\n  var fsm = new StateMachine({\n    init: 'A',\n    transitions: [\n      { name: 'step', from: '*', to: function(n) { return increaseCharacter(this.state, n || 1) } }\n    ]\n  });\n\n  fsm.state;      // A\n  fsm.step();\n  fsm.state;      // B\n  fsm.step(5);\n  fsm.state;      // G\n\n  // helper method to perform (c = c + n) on the 1st character in str\n  function increaseCharacter(str, n) {\n    return String.fromCharCode(str.charCodeAt(0) + n);\n  }\n```\n\nThe `allStates` method will only include conditional states once they have been seen at run-time:\n\n```javascript\n  fsm.state;        // A\n  fsm.allStates();  // [ 'A' ]\n  fsm.step();\n  fsm.state;        // B\n  fsm.allStates();  // [ 'A', 'B' ]\n  fsm.step(5);\n  fsm.state;        // G\n  fsm.allStates();  // [ 'A', 'B', 'G' ]\n```\n\n## GOTO - Changing State Without a Transition\n\nYou can use a conditional transition, combined with a wildcard `from`, to implement\narbitrary `goto` behavior:\n\n```javascript\n  var fsm = new StateMachine({\n    init: 'A'\n    transitions: [\n      { name: 'step', from: 'A', to: 'B'                      },\n      { name: 'step', from: 'B', to: 'C'                      },\n      { name: 'step', from: 'C', to: 'D'                      },\n      { name: 'goto', from: '*', to: function(s) { return s } }\n    ]\n  })\n\n  fsm.state;     // 'A'\n  fsm.goto('D');\n  fsm.state;     // 'D'\n```\n\nA full set of [Lifecycle Events](lifecycle-events.md) still apply when using `goto`.\n\n"
  },
  {
    "path": "docs/upgrading-from-v2.md",
    "content": "# Upgrading from Version 2.x\n\nVersion 3.0 is a significant rewrite from earlier versions in order to support more\nadvanced use cases and to improve the existing use cases. Unfortunately, many of these\nupdates are incompatible with earlier versions, so changes are required in your code when you upgrade\nto version 3.x. We want to tackle those all in one swoop and avoid any more big-bang changes\nin the future.\n\nPlease read this article carefully if you are upgrading from version 2.x to 3.x.\n\n> A [summary](#upgrade-summary) of the changes required can be found at the end of the article.\n\n### Table of Contents\n\n  * [**Construction**](#construction) - constructing single instances follows a more idomatic javascript pattern.\n  * [**State Machine Factory**](#state-machine-factory) - constructing multiple instances from a class has been simplified.\n  * [**Data and Methods**](#data-and-methods) - A state machine can now have additional data and methods.\n  * [**Renamed Terminology**](#renamed-terminology) - A more consistent terminology has been applied.\n  * [**Lifecycle Events**](#lifecycle-events) - (previously called 'callbacks') are camelCased and observable.\n  * [**Async Transitions**](#promise-based-asynchronous-transitions) - Asynchronous transitions now use standard Promises.\n  * [**Conditional Transitions**](#conditional-transitions) - A transition can now dynamically choose its target state at run-time.\n  * [**Goto**](#goto) - The state can be changed without a defined transition using `goto`.\n  * [**State History**](#state-history) - The state history can now be retained and traversed with back/forward semantics.\n  * [**Visualization**](#visualization) - A state machine can now be visualized using GraphViz.\n  * [**Build System**](#build-system) - A new webpack-based build system has been implemented.\n\n## Construction\n\nConstructing a single state machine now follows a more idiomatic javascript pattern:\n\nVersion 2.x:\n\n```javascript\n  var fsm = StateMachine.create({ /* ... */ })\n```\n\n**Version 3.x**:\n\n```javascript\n  var fsm = new StateMachine({ /* ... */ })    //  <-- more idomatic \n```\n\n## State Machine Factory\n\nConstructing multiple instances from a state machine 'class' has been simplified:\n\nVersion 2.x:\n\n```javascript\n  function FSM() { }\n\n  StateMachine.create({\n    target: FSM.prototype,\n    // ...\n  })\n\n  var a = new FSM(),\n      b = new FSM();\n```\n\n**Version 3.x**:\n\n```javascript\n  var FSM = StateMachine.factory({ /* ... */ }),    //  <-- generate a factory (a constructor function)\n      a   = new FSM(),                              //  <-- then create instances\n      b   = new FSM();\n```\n\n## Data and Methods\n\nA state machine can now have additional (arbitrary) data and methods defined:\n\nVersion 2.x: _not supported_.\n\n**Version 3.x**:\n\n```javascript\n  var fsm = new StateMachine({\n    data: {\n      color: 'red'\n    },\n    methods: {\n      speak: function() { console.log('hello') }\n    }\n  });\n\n  fsm.color;   // 'red'\n  fsm.speak(); // 'hello'\n```\n\n## Renamed Terminology\n\nA more consistent terminology has been applied:\n\n  * A state machine consists of a set of [**States**](states-and-transitions.md).\n  * A state machine changes state by using [**Transitions**](states-and-transitions.md).\n  * A state machine can perform actions during a transition by observing [**Lifecycle Events**](lifecycle-events.md).\n  * A state machine can also have arbitrary [**Data and Methods**](data-and-methods.md).\n\nVersion 2.x:\n\n```javascript\n  var fsm = StateMachine.create({\n    initial: 'ready',\n    events:     [ /* ... */ ],\n    callbacks:  { /* ... */ }\n  });\n\n  fsm.current;  // 'ready'\n```\n\n**Version 3.x**:\n\n```javascript\n  var fsm = new StateMachine({\n    init:        'ready',             //  <-- renamed s/initial/init/\n    transitions: [ /* ... */ ],       //  <-- renamed s/events/transitions/\n    data:        { /* ... */ },       //  <-- new\n    methods:     { /* ... */ }        //  <-- renamed s/callbacks/methods/\n                                      //      ... which can contain arbitrary methods AND lifecycle event callbacks\n  });\n\n  fsm.state;  // 'ready'              //  <-- renamed s/current/state/\n```\n\n## Lifecycle Events\n\n**Callbacks** have been renamed **Lifecycle Events** and are now declared as `methods` on the\nstate machine using a more traditional javascript camelCase for the method names:\n\nVersion 2.x:\n\n```javascript\n  var fsm = StateMachine.create({\n    initial: 'initial-state',\n    events: [\n      { name: 'do-something', from: 'initial-state', to: 'final-state' }\n    ],\n    callbacks: {\n      onbeforedosomething: function() { /* ... */ },\n      onleaveinitialstate: function() { /* ... */ },\n      onenterfinalstate:   function() { /* ... */ },\n      onafterdosomething:  function() { /* ... */ }\n    }\n  })\n```\n\n**Version 3.x**:\n\n```javascript\n  var fsm = new StateMachine({\n    init: 'initial-state',\n    transitions: [\n      { name: 'do-something', from: 'initial-state', to: 'final-state' }\n    ],\n    methods: {                                         //  <-- renamed s/callbacks/methods/\n      onBeforeDoSomething: function() { /* ... */ },   //  <-- camelCase naming convention\n      onLeaveInitialState: function() { /* ... */ },   //  <--\n      onEnterFinalState:   function() { /* ... */ },   //  <--\n      onAfterDoSomething:  function() { /* ... */ }    //  <--\n    }\n  })\n```\n\n<hr>\nLifecycle events are now passed information in a single `lifecycle` argument:\n\nVersion 2.x:\n\n```javascript\n  var fsm = StateMachine.create({\n    events: [\n      { name: 'step', from: 'none', to: 'complete' }\n    ],\n    callbacks: {\n      onbeforestep: function(event, from, to) {\n        console.log('event: ' + event);   // 'step'\n        console.log('from: '  + from);    // 'none'\n        console.log('to: '    + to);      // 'complete'\n      },\n    }\n  });\n```\n\n**Version 3.x**:\n\n```javascript\n  var fsm = new StateMachine({\n    transitions: [\n      { name: 'step', from: 'none', to: 'complete' }\n    ],\n    methods: {\n      onBeforeStep: function(lifecycle) {                   //  <-- combined into a single argument\n        console.log('transition: ' + lifecycle.transition); //  'step'\n        console.log('from: '       + lifecycle.from);       //  'none'\n        console.log('to: '         + lifecycle.to);         //  'complete'\n      }\n    }\n  });\n```\n\n> This change allows us to include additional information in the future without having to have a ridiculous\nnumber of arguments to lifecycle event observer methods\n\n<hr>\nLifecycle events are also now observable by others:\n \nVersion 2.x: _not supported_.\n\n**Version 3.x**:\n\n```javascript\n  var fsm = new StateMachine({ /* ... */ });\n\n  // observe individual lifecycle events with observer methods\n  fsm.observe('onBeforeTransition', function() { /* ... */ });\n  fsm.observe('onLeaveState',       function() { /* ... */ });\n\n  // or observe multiple lifecycle events with an observer object\n  fsm.observe({\n    onBeforeTransition: function() { /* ... */ },\n    onLeaveState:       function() { /* ... */ }\n  });\n```\n\n<hr>\nThe general purpose lifecycle events now use the word `transition` instead of `event` and\noccur **before** their specialized versions:\n\nVersion 2.x, the lifecycle order was:\n\n  * `onbefore<EVENT>`\n  * `onbeforeevent`\n  * `onleave<STATE>`\n  * `onleavestate`\n  * `onenter<STATE>`\n  * `onenterstate`\n  * `on<STATE>`\n  * `onafter<EVENT>`\n  * `onafterevent`\n  * `on<EVENT>`\n\n**Version 3.x**, the lifecycle order is:\n\n  * `onBeforeTransition`   - fired before any transition\n  * `onBefore<TRANSITION>` - fired before a specific TRANSITION\n  * `onLeaveState`         - fired when leaving any state\n  * `onLeave<STATE>`       - fired when leaving a specific STATE\n  * `onTransition`         - fired during any transition\n  * `onEnterState`         - fired when entering any state\n  * `onEnter<STATE>`       - fired when entering a specific STATE\n  * `on<STATE>`            - convenience shorthand for `onEnter<STATE>`\n  * `onAfterTransition`    - fired after any transition\n  * `onAfter<TRANSITION>`  - fired after a specific TRANSITION\n  * `on<TRANSITION>`       - convenience shorthand for `onAfter<TRANSITION>`\n\n> For more details, read [Lifecycle Events](lifecycle-events.md)\n\n## Promise-Based Asynchronous Transitions\n\nAsynchronous transitions are now implemented using standard javascript [Promises](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).\n\nIf you return a Promise from **any** lifecycle event then the entire lifecycle for that transition\nis put on hold until that Promise gets resolved. If the promise is rejected then the transition\nis cancelled.\n\nVersion 2.x:\n\n```javascript\n  var fsm = StateMachine.create({\n    events: [\n      { name: 'step', from: 'none', to: 'complete' }\n    ],\n    callbacks: {\n      onbeforestep: function() {\n        $('#ui').fadeOut('fast', function() {\n          fsm.transition();\n        });\n        return StateMachine.ASYNC;\n      }\n    }\n  });\n```\n\n**Version 3.x**:\n\n```javascript\n  var fsm = new StateMachine({\n    transitions: [\n      { name: 'step', from: 'none', to: 'complete' }\n    ],\n    methods: {\n      onBeforeStep: function() {\n        return new Promise(function(resolve, reject) {  //  <-- return a Promise instead of StateMachine.ASYNC\n          $('#ui').fadeOut('fast', resolve);            //  <-- resolve the promise instead of calling .transition()\n        });\n      }\n    }\n  });\n```\n\n> For more details, read [Asynchronous Transitions](async-transitions.md)\n\n## Conditional Transitions\n\nA transition can now be conditional and choose the target state at run-time by providing a function\nas the `to` attribute.\n\nVersion 2.x: _not supported_.\n\n**Version 3.x**: See [Conditional Transitions](states-and-transitions.md#conditional-transitions)\n\n## Goto\n\nThe state can now be changed without the need for a predefined transition using a conditional `goto`\ntransition:\n\nVersion 2.x: _not_supported_.\n\n**Version 3.x**: See [Goto](states-and-transitions.md#goto---changing-state-without-a-transition)\n\n## State History\n\nA state machine can now track and traverse (back/forward) its state history.\n\nVersion 2.x: _not supported_.\n\n**Version 3.x**: See [State History](state-history.md)\n\n## Visualization\n\nA state machine can now be visualized as a directed graph using GraphViz `.dot` syntax.\n\nVersion 2.x: _not_supported_.\n\n**Version 3.x**: See [Visualization](visualization.md)\n\n## Build System\n\nA new [Webpack](https://webpack.js.org/concepts/) based build system has been provided along\nwith an [Ava](https://github.com/avajs/ava) based unit test suite.\n\nVersion 2.x: _not_supported_.\n\n**Version 3.x**: See [Contributing](contributing.md)\n\n## Other Breaking Changes in Version 3.0\n\n`isFinished` is no longer built-in, you can easily add it to your state machine with a custom method:\n\n```javascript\n  var fsm = new StateMachine({\n    methods: {\n      isFinished: function() { return this.state === 'done' }\n    }\n  })\n```\n\n# UPGRADE SUMMARY\n\nThe following list summarizes the above changes you might need when upgrading to version 3.0\n\n  * replace `StateMachine.create()` with `new StateMachine()`\n  * rename:\n    * `initial` to `init`\n    * `events` to `transitions`\n    * `callbacks` to `methods`\n    * `fsm.current` to `fsm.state`\n  * update your callback methods:\n    * rename them to use traditional javascript `camelCasing`\n    * refactor them to use the single `lifecycle` argument instead of individual `event,from,to` arguments\n  * update any asynchronous callback methods:\n    * return a `Promise` instead of `StateMachine.ASYNC`\n    * `resolve()` the promise when ready instead of calling `fsm.transition()`\n  * replace `StateMachine.create({ target: FOO })` with:\n    * if FOO is a class - `StateMachine.factory(FOO, {})`\n    * if FOO is an object - `StateMachine.apply(FOO, {})`\n\n"
  },
  {
    "path": "docs/visualization.md",
    "content": "# Visualization\n\nIt can be very helpful to visualize your state machine as a directed graph. This is possible\nwith the open source [GraphViz](http://www.graphviz.org/) library if we convert from our\nstate machine configuration to the `.dot` language expected by GraphViz using the\n`visualize` method:\n\n```javascript\n  var visualize = require('javascript-state-machine/lib/visualize');\n\n  var fsm = new StateMachine({\n    init: 'open',\n    transitions: [\n      { name: 'close', from: 'open',   to: 'closed' },\n      { name: 'open',  from: 'closed', to: 'open'   }\n    ]\n  });\n\n  visualize(fsm)\n```\n\nGenerates the following .dot syntax:\n\n```dot\n  digraph \"fsm\" {\n    \"closed\";\n    \"open\";\n    \"closed\" -> \"open\" [ label=\" open \" ];\n    \"open\" -> \"closed\" [ label=\" close \" ];\n  }\n```\n\nWhich GraphViz displays as:\n\n![door](../examples/vertical_door.png)\n\n## Enhanced Visualization\n\nYou can customize the generated `.dot` output - and hence the graphviz visualization - by attaching \n`dot` attributes to your transitions and (optionally) declaring an `orientation`:\n\n```javascript\n  var fsm = new StateMachine({\n    init: 'closed',\n    transitions: [\n      { name: 'open',  from: 'closed', to: 'open',   dot: { color: 'blue', headport: 'n', tailport: 'n' } },\n      { name: 'close', from: 'open',   to: 'closed', dot: { color: 'red',  headport: 's', tailport: 's' } }\n    ]\n  });\n  visualize(fsm, { name: 'door', orientation: 'horizontal' });\n```\n\nGenerates the following (enhanced) `.dot` syntax:\n\n```dot\n  digraph \"door\" {\n    rankdir=LR;\n    \"closed\";\n    \"open\";\n    \"closed\" -> \"open\" [ color=\"blue\" ; headport=\"n\" ; label=\" open \" ; tailport=\"n\" ];\n    \"open\" -> \"closed\" [ color=\"red\" ; headport=\"s\" ; label=\" close \" ; tailport=\"s\" ];\n  }\n```\n\nWhich GraphViz displays as:\n\n![door](../examples/horizontal_door.png)\n\n## Visualizing State Machine Factories\n\nYou can use the same `visualize` method to generate `.dot` output for a state machine factory:\n\n```javascript\n  var Matter = StateMachine.factory({\n    init: 'solid',\n    transitions: [\n      { name: 'melt',     from: 'solid',  to: 'liquid', dot: { headport: 'nw' } },\n      { name: 'freeze',   from: 'liquid', to: 'solid',  dot: { headport: 'se' } },\n      { name: 'vaporize', from: 'liquid', to: 'gas',    dot: { headport: 'nw' } },\n      { name: 'condense', from: 'gas',    to: 'liquid', dot: { headport: 'se' } }\n    ]\n  });\n\n  visualize(Matter, { name: 'matter', orientation: 'horizontal' })\n```\n\nGenerates the following .dot syntax:\n\n```dot\n  digraph \"matter\" {\n    rankdir=LR;\n    \"solid\";\n    \"liquid\";\n    \"gas\";\n    \"solid\" -> \"liquid\" [ headport=\"nw\" ; label=\" melt \" ];\n    \"liquid\" -> \"solid\" [ headport=\"se\" ; label=\" freeze \" ];\n    \"liquid\" -> \"gas\" [ headport=\"nw\" ; label=\" vaporize \" ];\n    \"gas\" -> \"liquid\" [ headport=\"se\" ; label=\" condense \" ];\n  }\n```\n\nWhich GraphViz displays as:\n\n![matter](../examples/matter.png)\n\n## Other Examples\n\n```javascript\n  var Wizard = StateMachine.factory({\n    init: 'A',\n    transitions: [\n      { name: 'step',  from: 'A',               to: 'B', dot: { headport: 'w',  tailport: 'ne' } },\n      { name: 'step',  from: 'B',               to: 'C', dot: { headport: 'w',  tailport: 'e' } },\n      { name: 'step',  from: 'C',               to: 'D', dot: { headport: 'w',  tailport: 'e' } },\n      { name: 'reset', from: [ 'B', 'C', 'D' ], to: 'A', dot: { headport: 'se', tailport: 's' } }\n    ]\n  });\n\n  visualize(Wizard, { orientation: 'horizontal' })\n```\n\nGenerates:\n\n```dot\n  digraph \"wizard\" {\n    rankdir=LR;\n    \"A\";\n    \"B\";\n    \"C\";\n    \"D\";\n    \"A\" -> \"B\" [ headport=\"w\" ; label=\" step \" ; tailport=\"ne\" ];\n    \"B\" -> \"C\" [ headport=\"w\" ; label=\" step \" ; tailport=\"e\" ];\n    \"C\" -> \"D\" [ headport=\"w\" ; label=\" step \" ; tailport=\"e\" ];\n    \"B\" -> \"A\" [ headport=\"se\" ; label=\" reset \" ; tailport=\"s\" ];\n    \"C\" -> \"A\" [ headport=\"se\" ; label=\" reset \" ; tailport=\"s\" ];\n    \"D\" -> \"A\" [ headport=\"se\" ; label=\" reset \" ; tailport=\"s\" ];\n  }\n```\n\nDisplays:\n\n![wizard](../examples/wizard.png)\n\n```javascript\n  var ATM = StateMachine.factory({\n    init: 'ready',\n    transitions: [\n      { name: 'insert-card', from: 'ready',              to: 'pin'                },\n      { name: 'confirm',     from: 'pin',                to: 'action'             },\n      { name: 'reject',      from: 'pin',                to: 'return-card'        },\n      { name: 'withdraw',    from: 'return-card',        to: 'ready'              },\n\n      { name: 'deposit',     from: 'action',             to: 'deposit-account'    },\n      { name: 'provide',     from: 'deposit-account',    to: 'deposit-amount'     },\n      { name: 'provide',     from: 'deposit-amount',     to: 'confirm-deposit'    },\n      { name: 'confirm',     from: 'confirm-deposit',    to: 'collect-envelope'   },\n      { name: 'provide',     from: 'collect-envelope',   to: 'continue'           },\n\n      { name: 'withdraw',    from: 'action',             to: 'withdrawal-account' },\n      { name: 'provide',     from: 'withdrawal-account', to: 'withdrawal-amount'  },\n      { name: 'provide',     from: 'withdrawal-amount',  to: 'confirm-withdrawal' },\n      { name: 'confirm',     from: 'confirm-withdrawal', to: 'dispense-cash'      },\n      { name: 'withdraw',    from: 'dispense-cash',      to: 'continue'           },\n\n      { name: 'continue',    from: 'continue',           to: 'action'             },\n      { name: 'finish',      from: 'continue',           to: 'return-card'        }\n    ]\n  })\n\n  visualize(ATM)\n```\n\nGenerates:\n\n```dot\n  digraph \"ATM\" {\n    \"ready\";\n    \"pin\";\n    \"action\";\n    \"return-card\";\n    \"deposit-account\";\n    \"deposit-amount\";\n    \"confirm-deposit\";\n    \"collect-envelope\";\n    \"continue\";\n    \"withdrawal-account\";\n    \"withdrawal-amount\";\n    \"confirm-withdrawal\";\n    \"dispense-cash\";\n    \"ready\" -> \"pin\" [ label=\" insert-card \" ];\n    \"pin\" -> \"action\" [ label=\" confirm \" ];\n    \"pin\" -> \"return-card\" [ label=\" reject \" ];\n    \"return-card\" -> \"ready\" [ label=\" withdraw \" ];\n    \"action\" -> \"deposit-account\" [ label=\" deposit \" ];\n    \"deposit-account\" -> \"deposit-amount\" [ label=\" provide \" ];\n    \"deposit-amount\" -> \"confirm-deposit\" [ label=\" provide \" ];\n    \"confirm-deposit\" -> \"collect-envelope\" [ label=\" confirm \" ];\n    \"collect-envelope\" -> \"continue\" [ label=\" provide \" ];\n    \"action\" -> \"withdrawal-account\" [ label=\" withdraw \" ];\n    \"withdrawal-account\" -> \"withdrawal-amount\" [ label=\" provide \" ];\n    \"withdrawal-amount\" -> \"confirm-withdrawal\" [ label=\" provide \" ];\n    \"confirm-withdrawal\" -> \"dispense-cash\" [ label=\" confirm \" ];\n    \"dispense-cash\" -> \"continue\" [ label=\" withdraw \" ];\n    \"continue\" -> \"action\" [ label=\" continue \" ];\n    \"continue\" -> \"return-card\" [ label=\" finish \" ];\n  }\n```\n\nDisplays:\n\n![atm](../examples/atm.png)\n"
  },
  {
    "path": "examples/atm.dot",
    "content": "digraph \"ATM\" {\n  \"ready\";\n  \"pin\";\n  \"action\";\n  \"return-card\";\n  \"deposit-account\";\n  \"deposit-amount\";\n  \"confirm-deposit\";\n  \"collect-envelope\";\n  \"continue\";\n  \"withdrawal-account\";\n  \"withdrawal-amount\";\n  \"confirm-withdrawal\";\n  \"dispense-cash\";\n  \"ready\" -> \"pin\" [ label=\" insert-card \" ];\n  \"pin\" -> \"action\" [ label=\" confirm \" ];\n  \"pin\" -> \"return-card\" [ label=\" reject \" ];\n  \"return-card\" -> \"ready\" [ label=\" withdraw \" ];\n  \"action\" -> \"deposit-account\" [ label=\" deposit \" ];\n  \"deposit-account\" -> \"deposit-amount\" [ label=\" provide \" ];\n  \"deposit-amount\" -> \"confirm-deposit\" [ label=\" provide \" ];\n  \"confirm-deposit\" -> \"collect-envelope\" [ label=\" confirm \" ];\n  \"collect-envelope\" -> \"continue\" [ label=\" provide \" ];\n  \"action\" -> \"withdrawal-account\" [ label=\" withdraw \" ];\n  \"withdrawal-account\" -> \"withdrawal-amount\" [ label=\" provide \" ];\n  \"withdrawal-amount\" -> \"confirm-withdrawal\" [ label=\" provide \" ];\n  \"confirm-withdrawal\" -> \"dispense-cash\" [ label=\" confirm \" ];\n  \"dispense-cash\" -> \"continue\" [ label=\" withdraw \" ];\n  \"continue\" -> \"action\" [ label=\" continue \" ];\n  \"continue\" -> \"return-card\" [ label=\" finish \" ];\n}"
  },
  {
    "path": "examples/atm.js",
    "content": "var StateMachine = require('../src/app'),\n    visualize    = require('../src/plugin/visualize');\n\nvar ATM = StateMachine.factory({\n  init: 'ready',\n  transitions: [\n    { name: 'insert-card', from: 'ready',                     to: 'pin'               },\n    { name: 'confirm',     from: 'pin',               to: 'action'             },\n    { name: 'reject',      from: 'pin',               to: 'return-card'               },\n    { name: 'withdraw',    from: 'return-card',               to: 'ready'                     },\n\n    { name: 'deposit',     from: 'action',             to: 'deposit-account'    },\n    { name: 'provide',     from: 'deposit-account',    to: 'deposit-amount'     },\n    { name: 'provide',     from: 'deposit-amount',     to: 'confirm-deposit'    },\n    { name: 'confirm',     from: 'confirm-deposit',    to: 'collect-envelope'          },\n    { name: 'provide',     from: 'collect-envelope',          to: 'continue'                  },\n\n    { name: 'withdraw',    from: 'action',             to: 'withdrawal-account' },\n    { name: 'provide',     from: 'withdrawal-account', to: 'withdrawal-amount'  },\n    { name: 'provide',     from: 'withdrawal-amount',  to: 'confirm-withdrawal' },\n    { name: 'confirm',     from: 'confirm-withdrawal', to: 'dispense-cash'             },\n    { name: 'withdraw',    from: 'dispense-cash',             to: 'continue'                  },\n\n    { name: 'continue',    from: 'continue',                  to: 'action'             },\n    { name: 'finish',      from: 'continue',                  to: 'return-card'               }\n  ]\n})\n\nATM.visualize = function() {\n  return visualize(ATM, { name: 'ATM' })\n}\n\nmodule.exports = ATM\n"
  },
  {
    "path": "examples/demo/demo.css",
    "content": "#demo { width: 400px; margin: 0 auto; text-align: center; }\n\n#controls { text-align: center; }\n\n#demo #notes   { margin-bottom: 1em; }\n#demo #diagram { width: 400px; height: 275px; }\n#demo #output  { width: 100%;  height: 30em;  }\n\n#demo.green  #diagram { background: url(images/alerts.green.png);  }\n#demo.yellow #diagram { background: url(images/alerts.yellow.png); }\n#demo.red    #diagram { background: url(images/alerts.red.png);    }\n\n"
  },
  {
    "path": "examples/demo/demo.js",
    "content": "Demo = function() {\n\n  var output = document.getElementById('output'),\n      demo   = document.getElementById('demo'),\n      panic  = document.getElementById('panic'),\n      warn   = document.getElementById('warn'),\n      calm   = document.getElementById('calm'),\n      clear  = document.getElementById('clear'),\n      count  = 0;\n\n  var log = function(msg, separate) {\n    count = count + (separate ? 1 : 0);\n    output.value = count + \": \" + msg + \"\\n\" + (separate ? \"\\n\" : \"\") + output.value;\n    refreshUI();\n  };\n\n  var refreshUI = function() {\n    setTimeout(function() {\n      demo.className = fsm.state;\n      panic.disabled = fsm.cannot('panic', true);\n      warn.disabled  = fsm.cannot('warn',  true);\n      calm.disabled  = fsm.cannot('calm',  true);\n      clear.disabled = fsm.cannot('clear', true);\n    }, 0); // defer until end of current tick to allow fsm to complete transaction\n  };\n\n  var fsm = new StateMachine({\n\n    transitions: [\n      { name: 'start', from: 'none',   to: 'green'  },\n      { name: 'warn',  from: 'green',  to: 'yellow' },\n      { name: 'panic', from: 'green',  to: 'red'    },\n      { name: 'panic', from: 'yellow', to: 'red'    },\n      { name: 'calm',  from: 'red',    to: 'yellow' },\n      { name: 'clear', from: 'red',    to: 'green'  },\n      { name: 'clear', from: 'yellow', to: 'green'  },\n    ],\n\n    methods: {\n\n      onBeforeTransition: function(lifecycle) {\n        log(\"BEFORE: \" + lifecycle.transition, true);\n      },\n\n      onLeaveState: function(lifecycle) {\n        log(\"LEAVE: \" + lifecycle.from);\n      },\n\n      onEnterState: function(lifecycle) {\n        log(\"ENTER: \" + lifecycle.to);\n      },\n\n      onAfterTransition: function(lifecycle) {\n        log(\"AFTER: \" + lifecycle.transition);\n      },\n\n      onTransition: function(lifecycle) {\n        log(\"DURING: \" + lifecycle.transition + \" (from \" + lifecycle.from + \" to \" + lifecycle.to + \")\");\n      },\n\n      onLeaveRed: function(lifecycle) {\n        return new Promise(function(resolve, reject) {\n          var msg = lifecycle.transition + ' to ' + lifecycle.to;\n          log(\"PENDING \" + msg + \" in ...3\");\n          setTimeout(function() {\n            log(\"PENDING \" + msg + \" in ...2\");\n            setTimeout(function() {\n              log(\"PENDING \" + msg + \" in ...1\");\n              setTimeout(function() {\n                resolve();\n              }, 1000);\n            }, 1000);\n          }, 1000);\n        });\n      }\n\n    }\n  });\n\n  fsm.start();\n  return fsm;\n\n}();\n\n"
  },
  {
    "path": "examples/horizontal_door.dot",
    "content": "digraph \"door\" {\n  rankdir=LR;\n  \"closed\";\n  \"open\";\n  \"closed\" -> \"open\" [ color=\"blue\" ; headport=\"n\" ; label=\" open \" ; tailport=\"n\" ];\n  \"open\" -> \"closed\" [ color=\"red\" ; headport=\"s\" ; label=\" close \" ; tailport=\"s\" ];\n}"
  },
  {
    "path": "examples/horizontal_door.js",
    "content": "var StateMachine = require('../src/app'),\n    visualize    = require('../src/plugin/visualize');\n\nvar Door = StateMachine.factory({\n  init: 'closed',\n  transitions: [\n    { name: 'open',  from: 'closed', to: 'open',   dot: { color: 'blue', headport: 'n', tailport: 'n' } },\n    { name: 'close', from: 'open',   to: 'closed', dot: { color: 'red',   headport: 's', tailport: 's' } }\n  ]\n});\n\nDoor.visualize = function() {\n  return visualize(Door, { name: 'door', orientation: 'horizontal' })\n}\n\nmodule.exports = Door\n"
  },
  {
    "path": "examples/matter.dot",
    "content": "digraph \"matter\" {\n  rankdir=LR;\n  \"solid\";\n  \"liquid\";\n  \"gas\";\n  \"solid\" -> \"liquid\" [ headport=\"nw\" ; label=\" melt \" ];\n  \"liquid\" -> \"solid\" [ headport=\"se\" ; label=\" freeze \" ];\n  \"liquid\" -> \"gas\" [ headport=\"nw\" ; label=\" vaporize \" ];\n  \"gas\" -> \"liquid\" [ headport=\"se\" ; label=\" condense \" ];\n}"
  },
  {
    "path": "examples/matter.js",
    "content": "var StateMachine = require('../src/app'),\n    visualize    = require('../src/plugin/visualize');\n\nvar Matter = StateMachine.factory({\n  init: 'solid',\n  transitions: [\n    { name: 'melt',     from: 'solid',  to: 'liquid', dot: { headport: 'nw' } },\n    { name: 'freeze',   from: 'liquid', to: 'solid',  dot: { headport: 'se' } },\n    { name: 'vaporize', from: 'liquid', to: 'gas',    dot: { headport: 'nw' } },\n    { name: 'condense', from: 'gas',    to: 'liquid', dot: { headport: 'se' } }\n  ]\n});\n\nMatter.visualize = function() {\n  return visualize(Matter, { name: 'matter', orientation: 'horizontal' })\n}\n\nmodule.exports = Matter\n"
  },
  {
    "path": "examples/vertical_door.dot",
    "content": "digraph \"fsm\" {\n  \"closed\";\n  \"open\";\n  \"closed\" -> \"open\" [ label=\" open \" ];\n  \"open\" -> \"closed\" [ label=\" close \" ];\n}"
  },
  {
    "path": "examples/vertical_door.js",
    "content": "var StateMachine = require('../src/app'),\n    visualize    = require('../src/plugin/visualize');\n\nvar Door = StateMachine.factory({\n  init: 'closed',\n  transitions: [\n    { name: 'open',  from: 'closed', to: 'open'   },\n    { name: 'close', from: 'open',   to: 'closed' }\n  ]\n});\n\nDoor.visualize = function() {\n  return visualize(Door)\n}\n\nmodule.exports = Door\n"
  },
  {
    "path": "examples/wizard.dot",
    "content": "digraph \"wizard\" {\n  rankdir=LR;\n  \"A\";\n  \"B\";\n  \"C\";\n  \"D\";\n  \"A\" -> \"B\" [ headport=\"w\" ; label=\" step \" ; tailport=\"ne\" ];\n  \"B\" -> \"C\" [ headport=\"w\" ; label=\" step \" ; tailport=\"e\" ];\n  \"C\" -> \"D\" [ headport=\"w\" ; label=\" step \" ; tailport=\"e\" ];\n  \"B\" -> \"A\" [ headport=\"se\" ; label=\" reset \" ; tailport=\"s\" ];\n  \"C\" -> \"A\" [ headport=\"se\" ; label=\" reset \" ; tailport=\"s\" ];\n  \"D\" -> \"A\" [ headport=\"se\" ; label=\" reset \" ; tailport=\"s\" ];\n}"
  },
  {
    "path": "examples/wizard.js",
    "content": "var StateMachine = require('../src/app'),\n    visualize    = require('../src/plugin/visualize');\n\nvar Wizard = StateMachine.factory({\n  init: 'A',\n  transitions: [\n    { name: 'step',  from: 'A',               to: 'B', dot: { headport: 'w',  tailport: 'ne' } },\n    { name: 'step',  from: 'B',               to: 'C', dot: { headport: 'w',  tailport: 'e' } },\n    { name: 'step',  from: 'C',               to: 'D', dot: { headport: 'w',  tailport: 'e' } },\n    { name: 'reset', from: [ 'B', 'C', 'D' ], to: 'A', dot: { headport: 'se', tailport: 's' } }\n  ]\n});\n\nWizard.visualize = function() {\n  return visualize(Wizard, { name: 'wizard', orientation: 'horizontal' })\n}\n\nmodule.exports = Wizard\n"
  },
  {
    "path": "index.html",
    "content": "<!DOCTYPE html> \n<html>\n<head>\n  <title>Javascript Finite State Machine</title>\n  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/> \n  <link href=\"examples/demo/demo.css\" media=\"screen, print\" rel=\"stylesheet\" type=\"text/css\" /> \n</head> \n \n<body> \n\n  <div id=\"demo\" class='green'>\n\n    <h1> Finite State Machine </h1>\n\n    <div id=\"controls\">\n      <button id=\"clear\" onclick=\"Demo.clear();\">clear</button>\n      <button id=\"calm\"  onclick=\"Demo.calm();\">calm</button>\n      <button id=\"warn\"  onclick=\"Demo.warn();\">warn</button>\n      <button id=\"panic\" onclick=\"Demo.panic();\">panic!</button>\n    </div>\n\n    <div id=\"diagram\">\n    </div>\n\n    <div id=\"notes\">\n      <i>dashed lines are asynchronous state transitions (3 seconds)</i>\n    </div>\n\n    <textarea id=\"output\">\n    </textarea>\n\n  </div>\n\n\n  <script src=\"dist/state-machine.js\"></script>\n  <script src=\"examples/demo/demo.js\"></script>\n\n</body> \n</html>\n"
  },
  {
    "path": "lib/history.js",
    "content": "(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine(\"StateMachineHistory\", [], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"StateMachineHistory\"] = factory();\n\telse\n\t\troot[\"StateMachineHistory\"] = factory();\n})(this, function() {\nreturn /******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId]) {\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\ti: moduleId,\n/******/ \t\t\tl: false,\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.l = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// identity function for calling harmony imports with the correct context\n/******/ \t__webpack_require__.i = function(value) { return value; };\n/******/\n/******/ \t// define getter function for harmony exports\n/******/ \t__webpack_require__.d = function(exports, name, getter) {\n/******/ \t\tif(!__webpack_require__.o(exports, name)) {\n/******/ \t\t\tObject.defineProperty(exports, name, {\n/******/ \t\t\t\tconfigurable: false,\n/******/ \t\t\t\tenumerable: true,\n/******/ \t\t\t\tget: getter\n/******/ \t\t\t});\n/******/ \t\t}\n/******/ \t};\n/******/\n/******/ \t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t__webpack_require__.n = function(module) {\n/******/ \t\tvar getter = module && module.__esModule ?\n/******/ \t\t\tfunction getDefault() { return module['default']; } :\n/******/ \t\t\tfunction getModuleExports() { return module; };\n/******/ \t\t__webpack_require__.d(getter, 'a', getter);\n/******/ \t\treturn getter;\n/******/ \t};\n/******/\n/******/ \t// Object.prototype.hasOwnProperty.call\n/******/ \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"\";\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(__webpack_require__.s = 1);\n/******/ })\n/************************************************************************/\n/******/ ([\n/* 0 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n//-------------------------------------------------------------------------------------------------\n\nfunction camelize(label) {\n\n  if (label.length === 0)\n    return label;\n\n  var n, result, word, words = label.split(/[_-]/);\n\n  // single word with first character already lowercase, return untouched\n  if ((words.length === 1) && (words[0][0].toLowerCase() === words[0][0]))\n    return label;\n\n  result = words[0].toLowerCase();\n  for(n = 1 ; n < words.length ; n++) {\n    result = result + words[n].charAt(0).toUpperCase() + words[n].substring(1).toLowerCase();\n  }\n\n  return result;\n}\n\n//-------------------------------------------------------------------------------------------------\n\ncamelize.prepended = function(prepend, label) {\n  label = camelize(label);\n  return prepend + label[0].toUpperCase() + label.substring(1);\n}\n\n//-------------------------------------------------------------------------------------------------\n\nmodule.exports = camelize;\n\n\n/***/ }),\n/* 1 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n//-------------------------------------------------------------------------------------------------\n\nvar camelize = __webpack_require__(0);\n\n//-------------------------------------------------------------------------------------------------\n\nmodule.exports = function(options) { options = options || {};\n\n  var past       = camelize(options.name || options.past   || 'history'),\n      future     = camelize(                options.future || 'future'),\n      clear      = camelize.prepended('clear', past),\n      back       = camelize.prepended(past,   'back'),\n      forward    = camelize.prepended(past,   'forward'),\n      canBack    = camelize.prepended('can',   back),\n      canForward = camelize.prepended('can',   forward),\n      max        = options.max;\n\n  var plugin = {\n\n    configure: function(config) {\n      config.addTransitionLifecycleNames(back);\n      config.addTransitionLifecycleNames(forward);\n    },\n\n    init: function(instance) {\n      instance[past]   = [];\n      instance[future] = [];\n    },\n\n    lifecycle: function(instance, lifecycle) {\n      if (lifecycle.event === 'onEnterState') {\n        instance[past].push(lifecycle.to);\n        if (max && instance[past].length > max)\n          instance[past].shift();\n        if (lifecycle.transition !== back && lifecycle.transition !== forward)\n          instance[future].length = 0;\n      }\n    },\n\n    methods:    {},\n    properties: {}\n\n  }\n\n  plugin.methods[clear] = function() {\n    this[past].length = 0\n    this[future].length = 0\n  }\n\n  plugin.properties[canBack] = {\n    get: function() {\n      return this[past].length > 1\n    }\n  }\n\n  plugin.properties[canForward] = {\n    get: function() {\n      return this[future].length > 0\n    }\n  }\n\n  plugin.methods[back] = function() {\n    if (!this[canBack])\n      throw Error('no history');\n    var from = this[past].pop(),\n        to   = this[past].pop();\n    this[future].push(from);\n    this._fsm.transit(back, from, to, []);\n  }\n\n  plugin.methods[forward] = function() {\n    if (!this[canForward])\n      throw Error('no history');\n    var from = this.state,\n        to = this[future].pop();\n    this._fsm.transit(forward, from, to, []);\n  }\n\n  return plugin;\n\n}\n\n\n/***/ })\n/******/ ]);\n});"
  },
  {
    "path": "lib/state-machine.js",
    "content": "(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine(\"StateMachine\", [], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"StateMachine\"] = factory();\n\telse\n\t\troot[\"StateMachine\"] = factory();\n})(this, function() {\nreturn /******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId]) {\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\ti: moduleId,\n/******/ \t\t\tl: false,\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.l = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// identity function for calling harmony imports with the correct context\n/******/ \t__webpack_require__.i = function(value) { return value; };\n/******/\n/******/ \t// define getter function for harmony exports\n/******/ \t__webpack_require__.d = function(exports, name, getter) {\n/******/ \t\tif(!__webpack_require__.o(exports, name)) {\n/******/ \t\t\tObject.defineProperty(exports, name, {\n/******/ \t\t\t\tconfigurable: false,\n/******/ \t\t\t\tenumerable: true,\n/******/ \t\t\t\tget: getter\n/******/ \t\t\t});\n/******/ \t\t}\n/******/ \t};\n/******/\n/******/ \t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t__webpack_require__.n = function(module) {\n/******/ \t\tvar getter = module && module.__esModule ?\n/******/ \t\t\tfunction getDefault() { return module['default']; } :\n/******/ \t\t\tfunction getModuleExports() { return module; };\n/******/ \t\t__webpack_require__.d(getter, 'a', getter);\n/******/ \t\treturn getter;\n/******/ \t};\n/******/\n/******/ \t// Object.prototype.hasOwnProperty.call\n/******/ \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"\";\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(__webpack_require__.s = 5);\n/******/ })\n/************************************************************************/\n/******/ ([\n/* 0 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nmodule.exports = function(target, sources) {\n  var n, source, key;\n  for(n = 1 ; n < arguments.length ; n++) {\n    source = arguments[n];\n    for(key in source) {\n      if (source.hasOwnProperty(key))\n        target[key] = source[key];\n    }\n  }\n  return target;\n}\n\n\n/***/ }),\n/* 1 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n//-------------------------------------------------------------------------------------------------\n\nvar mixin = __webpack_require__(0);\n\n//-------------------------------------------------------------------------------------------------\n\nmodule.exports = {\n\n  build: function(target, config) {\n    var n, max, plugin, plugins = config.plugins;\n    for(n = 0, max = plugins.length ; n < max ; n++) {\n      plugin = plugins[n];\n      if (plugin.methods)\n        mixin(target, plugin.methods);\n      if (plugin.properties)\n        Object.defineProperties(target, plugin.properties);\n    }\n  },\n\n  hook: function(fsm, name, additional) {\n    var n, max, method, plugin,\n        plugins = fsm.config.plugins,\n        args    = [fsm.context];\n\n    if (additional)\n      args = args.concat(additional)\n\n    for(n = 0, max = plugins.length ; n < max ; n++) {\n      plugin = plugins[n]\n      method = plugins[n][name]\n      if (method)\n        method.apply(plugin, args);\n    }\n  }\n\n}\n\n//-------------------------------------------------------------------------------------------------\n\n\n/***/ }),\n/* 2 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n//-------------------------------------------------------------------------------------------------\n\nfunction camelize(label) {\n\n  if (label.length === 0)\n    return label;\n\n  var n, result, word, words = label.split(/[_-]/);\n\n  // single word with first character already lowercase, return untouched\n  if ((words.length === 1) && (words[0][0].toLowerCase() === words[0][0]))\n    return label;\n\n  result = words[0].toLowerCase();\n  for(n = 1 ; n < words.length ; n++) {\n    result = result + words[n].charAt(0).toUpperCase() + words[n].substring(1).toLowerCase();\n  }\n\n  return result;\n}\n\n//-------------------------------------------------------------------------------------------------\n\ncamelize.prepended = function(prepend, label) {\n  label = camelize(label);\n  return prepend + label[0].toUpperCase() + label.substring(1);\n}\n\n//-------------------------------------------------------------------------------------------------\n\nmodule.exports = camelize;\n\n\n/***/ }),\n/* 3 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n//-------------------------------------------------------------------------------------------------\n\nvar mixin    = __webpack_require__(0),\n    camelize = __webpack_require__(2);\n\n//-------------------------------------------------------------------------------------------------\n\nfunction Config(options, StateMachine) {\n\n  options = options || {};\n\n  this.options     = options; // preserving original options can be useful (e.g visualize plugin)\n  this.defaults    = StateMachine.defaults;\n  this.states      = [];\n  this.transitions = [];\n  this.map         = {};\n  this.lifecycle   = this.configureLifecycle();\n  this.init        = this.configureInitTransition(options.init);\n  this.data        = this.configureData(options.data);\n  this.methods     = this.configureMethods(options.methods);\n\n  this.map[this.defaults.wildcard] = {};\n\n  this.configureTransitions(options.transitions || []);\n\n  this.plugins = this.configurePlugins(options.plugins, StateMachine.plugin);\n\n}\n\n//-------------------------------------------------------------------------------------------------\n\nmixin(Config.prototype, {\n\n  addState: function(name) {\n    if (!this.map[name]) {\n      this.states.push(name);\n      this.addStateLifecycleNames(name);\n      this.map[name] = {};\n    }\n  },\n\n  addStateLifecycleNames: function(name) {\n    this.lifecycle.onEnter[name] = camelize.prepended('onEnter', name);\n    this.lifecycle.onLeave[name] = camelize.prepended('onLeave', name);\n    this.lifecycle.on[name]      = camelize.prepended('on',      name);\n  },\n\n  addTransition: function(name) {\n    if (this.transitions.indexOf(name) < 0) {\n      this.transitions.push(name);\n      this.addTransitionLifecycleNames(name);\n    }\n  },\n\n  addTransitionLifecycleNames: function(name) {\n    this.lifecycle.onBefore[name] = camelize.prepended('onBefore', name);\n    this.lifecycle.onAfter[name]  = camelize.prepended('onAfter',  name);\n    this.lifecycle.on[name]       = camelize.prepended('on',       name);\n  },\n\n  mapTransition: function(transition) {\n    var name = transition.name,\n        from = transition.from,\n        to   = transition.to;\n    this.addState(from);\n    if (typeof to !== 'function')\n      this.addState(to);\n    this.addTransition(name);\n    this.map[from][name] = transition;\n    return transition;\n  },\n\n  configureLifecycle: function() {\n    return {\n      onBefore: { transition: 'onBeforeTransition' },\n      onAfter:  { transition: 'onAfterTransition'  },\n      onEnter:  { state:      'onEnterState'       },\n      onLeave:  { state:      'onLeaveState'       },\n      on:       { transition: 'onTransition'       }\n    };\n  },\n\n  configureInitTransition: function(init) {\n    if (typeof init === 'string') {\n      return this.mapTransition(mixin({}, this.defaults.init, { to: init, active: true }));\n    }\n    else if (typeof init === 'object') {\n      return this.mapTransition(mixin({}, this.defaults.init, init, { active: true }));\n    }\n    else {\n      this.addState(this.defaults.init.from);\n      return this.defaults.init;\n    }\n  },\n\n  configureData: function(data) {\n    if (typeof data === 'function')\n      return data;\n    else if (typeof data === 'object')\n      return function() { return data; }\n    else\n      return function() { return {};  }\n  },\n\n  configureMethods: function(methods) {\n    return methods || {};\n  },\n\n  configurePlugins: function(plugins, builtin) {\n    plugins = plugins || [];\n    var n, max, plugin;\n    for(n = 0, max = plugins.length ; n < max ; n++) {\n      plugin = plugins[n];\n      if (typeof plugin === 'function')\n        plugins[n] = plugin = plugin()\n      if (plugin.configure)\n        plugin.configure(this);\n    }\n    return plugins\n  },\n\n  configureTransitions: function(transitions) {\n    var i, n, transition, from, to, wildcard = this.defaults.wildcard;\n    for(n = 0 ; n < transitions.length ; n++) {\n      transition = transitions[n];\n      from  = Array.isArray(transition.from) ? transition.from : [transition.from || wildcard]\n      to    = transition.to || wildcard;\n      for(i = 0 ; i < from.length ; i++) {\n        this.mapTransition({ name: transition.name, from: from[i], to: to });\n      }\n    }\n  },\n\n  transitionFor: function(state, transition) {\n    var wildcard = this.defaults.wildcard;\n    return this.map[state][transition] ||\n           this.map[wildcard][transition];\n  },\n\n  transitionsFor: function(state) {\n    var wildcard = this.defaults.wildcard;\n    return Object.keys(this.map[state]).concat(Object.keys(this.map[wildcard]));\n  },\n\n  allStates: function() {\n    return this.states;\n  },\n\n  allTransitions: function() {\n    return this.transitions;\n  }\n\n});\n\n//-------------------------------------------------------------------------------------------------\n\nmodule.exports = Config;\n\n//-------------------------------------------------------------------------------------------------\n\n\n/***/ }),\n/* 4 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\nvar mixin      = __webpack_require__(0),\n    Exception  = __webpack_require__(6),\n    plugin     = __webpack_require__(1),\n    UNOBSERVED = [ null, [] ];\n\n//-------------------------------------------------------------------------------------------------\n\nfunction JSM(context, config) {\n  this.context   = context;\n  this.config    = config;\n  this.state     = config.init.from;\n  this.observers = [context];\n}\n\n//-------------------------------------------------------------------------------------------------\n\nmixin(JSM.prototype, {\n\n  init: function(args) {\n    mixin(this.context, this.config.data.apply(this.context, args));\n    plugin.hook(this, 'init');\n    if (this.config.init.active)\n      return this.fire(this.config.init.name, []);\n  },\n\n  is: function(state) {\n    return Array.isArray(state) ? (state.indexOf(this.state) >= 0) : (this.state === state);\n  },\n\n  isPending: function() {\n    return this.pending;\n  },\n\n  can: function(transition) {\n    return !this.isPending() && !!this.seek(transition);\n  },\n\n  cannot: function(transition) {\n    return !this.can(transition);\n  },\n\n  allStates: function() {\n    return this.config.allStates();\n  },\n\n  allTransitions: function() {\n    return this.config.allTransitions();\n  },\n\n  transitions: function() {\n    return this.config.transitionsFor(this.state);\n  },\n\n  seek: function(transition, args) {\n    var wildcard = this.config.defaults.wildcard,\n        entry    = this.config.transitionFor(this.state, transition),\n        to       = entry && entry.to;\n    if (typeof to === 'function')\n      return to.apply(this.context, args);\n    else if (to === wildcard)\n      return this.state\n    else\n      return to\n  },\n\n  fire: function(transition, args) {\n    return this.transit(transition, this.state, this.seek(transition, args), args);\n  },\n\n  transit: function(transition, from, to, args) {\n\n    var lifecycle = this.config.lifecycle,\n        changed   = this.config.options.observeUnchangedState || (from !== to);\n\n    if (!to)\n      return this.context.onInvalidTransition(transition, from, to);\n\n    if (this.isPending())\n      return this.context.onPendingTransition(transition, from, to);\n\n    this.config.addState(to);  // might need to add this state if it's unknown (e.g. conditional transition or goto)\n\n    this.beginTransit();\n\n    args.unshift({             // this context will be passed to each lifecycle event observer\n      transition: transition,\n      from:       from,\n      to:         to,\n      fsm:        this.context\n    });\n\n    return this.observeEvents([\n                this.observersForEvent(lifecycle.onBefore.transition),\n                this.observersForEvent(lifecycle.onBefore[transition]),\n      changed ? this.observersForEvent(lifecycle.onLeave.state) : UNOBSERVED,\n      changed ? this.observersForEvent(lifecycle.onLeave[from]) : UNOBSERVED,\n                this.observersForEvent(lifecycle.on.transition),\n      changed ? [ 'doTransit', [ this ] ]                       : UNOBSERVED,\n      changed ? this.observersForEvent(lifecycle.onEnter.state) : UNOBSERVED,\n      changed ? this.observersForEvent(lifecycle.onEnter[to])   : UNOBSERVED,\n      changed ? this.observersForEvent(lifecycle.on[to])        : UNOBSERVED,\n                this.observersForEvent(lifecycle.onAfter.transition),\n                this.observersForEvent(lifecycle.onAfter[transition]),\n                this.observersForEvent(lifecycle.on[transition])\n    ], args);\n  },\n\n  beginTransit: function()          { this.pending = true;                 },\n  endTransit:   function(result)    { this.pending = false; return result; },\n  failTransit:  function(result)    { this.pending = false; throw result;  },\n  doTransit:    function(lifecycle) { this.state = lifecycle.to;           },\n\n  observe: function(args) {\n    if (args.length === 2) {\n      var observer = {};\n      observer[args[0]] = args[1];\n      this.observers.push(observer);\n    }\n    else {\n      this.observers.push(args[0]);\n    }\n  },\n\n  observersForEvent: function(event) { // TODO: this could be cached\n    var n = 0, max = this.observers.length, observer, result = [];\n    for( ; n < max ; n++) {\n      observer = this.observers[n];\n      if (observer[event])\n        result.push(observer);\n    }\n    return [ event, result, true ]\n  },\n\n  observeEvents: function(events, args, previousEvent, previousResult) {\n    if (events.length === 0) {\n      return this.endTransit(previousResult === undefined ? true : previousResult);\n    }\n\n    var event     = events[0][0],\n        observers = events[0][1],\n        pluggable = events[0][2];\n\n    args[0].event = event;\n    if (event && pluggable && event !== previousEvent)\n      plugin.hook(this, 'lifecycle', args);\n\n    if (observers.length === 0) {\n      events.shift();\n      return this.observeEvents(events, args, event, previousResult);\n    }\n    else {\n      var observer = observers.shift(),\n          result = observer[event].apply(observer, args);\n      if (result && typeof result.then === 'function') {\n        return result.then(this.observeEvents.bind(this, events, args, event))\n                     .catch(this.failTransit.bind(this))\n      }\n      else if (result === false) {\n        return this.endTransit(false);\n      }\n      else {\n        return this.observeEvents(events, args, event, result);\n      }\n    }\n  },\n\n  onInvalidTransition: function(transition, from, to) {\n    throw new Exception(\"transition is invalid in current state\", transition, from, to, this.state);\n  },\n\n  onPendingTransition: function(transition, from, to) {\n    throw new Exception(\"transition is invalid while previous transition is still in progress\", transition, from, to, this.state);\n  }\n\n});\n\n//-------------------------------------------------------------------------------------------------\n\nmodule.exports = JSM;\n\n//-------------------------------------------------------------------------------------------------\n\n\n/***/ }),\n/* 5 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n//-----------------------------------------------------------------------------------------------\n\nvar mixin    = __webpack_require__(0),\n    camelize = __webpack_require__(2),\n    plugin   = __webpack_require__(1),\n    Config   = __webpack_require__(3),\n    JSM      = __webpack_require__(4);\n\n//-----------------------------------------------------------------------------------------------\n\nvar PublicMethods = {\n  is:                  function(state)       { return this._fsm.is(state)                                     },\n  can:                 function(transition)  { return this._fsm.can(transition)                               },\n  cannot:              function(transition)  { return this._fsm.cannot(transition)                            },\n  observe:             function()            { return this._fsm.observe(arguments)                            },\n  transitions:         function()            { return this._fsm.transitions()                                 },\n  allTransitions:      function()            { return this._fsm.allTransitions()                              },\n  allStates:           function()            { return this._fsm.allStates()                                   },\n  onInvalidTransition: function(t, from, to) { return this._fsm.onInvalidTransition(t, from, to)              },\n  onPendingTransition: function(t, from, to) { return this._fsm.onPendingTransition(t, from, to)              },\n}\n\nvar PublicProperties = {\n  state: {\n    configurable: false,\n    enumerable:   true,\n    get: function() {\n      return this._fsm.state;\n    },\n    set: function(state) {\n      throw Error('use transitions to change state')\n    }\n  }\n}\n\n//-----------------------------------------------------------------------------------------------\n\nfunction StateMachine(options) {\n  return apply(this || {}, options);\n}\n\nfunction factory() {\n  var cstor, options;\n  if (typeof arguments[0] === 'function') {\n    cstor   = arguments[0];\n    options = arguments[1] || {};\n  }\n  else {\n    cstor   = function() { this._fsm.apply(this, arguments) };\n    options = arguments[0] || {};\n  }\n  var config = new Config(options, StateMachine);\n  build(cstor.prototype, config);\n  cstor.prototype._fsm.config = config; // convenience access to shared config without needing an instance\n  return cstor;\n}\n\n//-------------------------------------------------------------------------------------------------\n\nfunction apply(instance, options) {\n  var config = new Config(options, StateMachine);\n  build(instance, config);\n  instance._fsm();\n  return instance;\n}\n\nfunction build(target, config) {\n  if ((typeof target !== 'object') || Array.isArray(target))\n    throw Error('StateMachine can only be applied to objects');\n  plugin.build(target, config);\n  Object.defineProperties(target, PublicProperties);\n  mixin(target, PublicMethods);\n  mixin(target, config.methods);\n  config.allTransitions().forEach(function(transition) {\n    target[camelize(transition)] = function() {\n      return this._fsm.fire(transition, [].slice.call(arguments))\n    }\n  });\n  target._fsm = function() {\n    this._fsm = new JSM(this, config);\n    this._fsm.init(arguments);\n  }\n}\n\n//-----------------------------------------------------------------------------------------------\n\nStateMachine.version  = '3.0.1';\nStateMachine.factory  = factory;\nStateMachine.apply    = apply;\nStateMachine.defaults = {\n  wildcard: '*',\n  init: {\n    name: 'init',\n    from: 'none'\n  }\n}\n\n//===============================================================================================\n\nmodule.exports = StateMachine;\n\n\n/***/ }),\n/* 6 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nmodule.exports = function(message, transition, from, to, current) {\n  this.message    = message;\n  this.transition = transition;\n  this.from       = from;\n  this.to         = to;\n  this.current    = current;\n}\n\n\n/***/ })\n/******/ ]);\n});"
  },
  {
    "path": "lib/visualize.js",
    "content": "(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine(\"StateMachineVisualize\", [], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"StateMachineVisualize\"] = factory();\n\telse\n\t\troot[\"StateMachineVisualize\"] = factory();\n})(this, function() {\nreturn /******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId]) {\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\ti: moduleId,\n/******/ \t\t\tl: false,\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.l = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// identity function for calling harmony imports with the correct context\n/******/ \t__webpack_require__.i = function(value) { return value; };\n/******/\n/******/ \t// define getter function for harmony exports\n/******/ \t__webpack_require__.d = function(exports, name, getter) {\n/******/ \t\tif(!__webpack_require__.o(exports, name)) {\n/******/ \t\t\tObject.defineProperty(exports, name, {\n/******/ \t\t\t\tconfigurable: false,\n/******/ \t\t\t\tenumerable: true,\n/******/ \t\t\t\tget: getter\n/******/ \t\t\t});\n/******/ \t\t}\n/******/ \t};\n/******/\n/******/ \t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t__webpack_require__.n = function(module) {\n/******/ \t\tvar getter = module && module.__esModule ?\n/******/ \t\t\tfunction getDefault() { return module['default']; } :\n/******/ \t\t\tfunction getModuleExports() { return module; };\n/******/ \t\t__webpack_require__.d(getter, 'a', getter);\n/******/ \t\treturn getter;\n/******/ \t};\n/******/\n/******/ \t// Object.prototype.hasOwnProperty.call\n/******/ \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"\";\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(__webpack_require__.s = 1);\n/******/ })\n/************************************************************************/\n/******/ ([\n/* 0 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\nmodule.exports = function(target, sources) {\n  var n, source, key;\n  for(n = 1 ; n < arguments.length ; n++) {\n    source = arguments[n];\n    for(key in source) {\n      if (source.hasOwnProperty(key))\n        target[key] = source[key];\n    }\n  }\n  return target;\n}\n\n\n/***/ }),\n/* 1 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\"use strict\";\n\n\n//-------------------------------------------------------------------------------------------------\n\nvar mixin = __webpack_require__(0)\n\n//-------------------------------------------------------------------------------------------------\n\nfunction visualize(fsm, options) {\n  return dotify(dotcfg(fsm, options));\n}\n\n//-------------------------------------------------------------------------------------------------\n\nfunction dotcfg(fsm, options) {\n\n  options = options || {}\n\n  var config      = dotcfg.fetch(fsm),\n      name        = options.name,\n      rankdir     = dotcfg.rankdir(options.orientation),\n      states      = dotcfg.states(config, options),\n      transitions = dotcfg.transitions(config, options),\n      result      = { }\n\n  if (name)\n    result.name = name\n\n  if (rankdir)\n    result.rankdir = rankdir\n\n  if (states && states.length > 0)\n    result.states = states\n\n  if (transitions && transitions.length > 0)\n    result.transitions = transitions\n\n  return result\n}\n\n//-------------------------------------------------------------------------------------------------\n\ndotcfg.fetch = function(fsm) {\n  return (typeof fsm === 'function') ? fsm.prototype._fsm.config\n                                     : fsm._fsm.config\n}\n\ndotcfg.rankdir = function(orientation) {\n  if (orientation === 'horizontal')\n    return 'LR';\n  else if (orientation === 'vertical')\n    return 'TB';\n}\n\ndotcfg.states = function(config, options) {\n  var index, states = config.states;\n  if (!options.init) { // if not showing init transition, then slice out the implied init :from state\n    index  = states.indexOf(config.init.from);\n    states = states.slice(0, index).concat(states.slice(index+1));\n  }\n  return states;\n}\n\ndotcfg.transitions = function(config, options) {\n  var n, max, transition,\n      init        = config.init,\n      transitions = config.options.transitions || [], // easier to visualize using the ORIGINAL transition declarations rather than our run-time mapping\n      output = [];\n  if (options.init && init.active)\n    dotcfg.transition(init.name, init.from, init.to, init.dot, config, options, output)\n  for (n = 0, max = transitions.length ; n < max ; n++) {\n    transition = config.options.transitions[n]\n    dotcfg.transition(transition.name, transition.from, transition.to, transition.dot, config, options, output)\n  }\n  return output\n}\n\ndotcfg.transition = function(name, from, to, dot, config, options, output) {\n  var n, max, wildcard = config.defaults.wildcard\n\n  if (Array.isArray(from)) {\n    for(n = 0, max = from.length ; n < max ; n++)\n      dotcfg.transition(name, from[n], to, dot, config, options, output)\n  }\n  else if (from === wildcard || from === undefined) {\n    for(n = 0, max = config.states.length ; n < max ; n++)\n      dotcfg.transition(name, config.states[n], to, dot, config, options, output)\n  }\n  else if (to === wildcard || to === undefined) {\n    dotcfg.transition(name, from, from, dot, config, options, output)\n  }\n  else if (typeof to === 'function') {\n    // do nothing, can't display conditional transition\n  }\n  else {\n    output.push(mixin({}, { from: from, to: to, label: pad(name) }, dot || {}))\n  }\n\n}\n\n//-------------------------------------------------------------------------------------------------\n\nfunction pad(name) {\n  return \" \" + name + \" \"\n}\n\nfunction quote(name) {\n  return \"\\\"\" + name + \"\\\"\"\n}\n\nfunction dotify(dotcfg) {\n\n  dotcfg = dotcfg || {};\n\n  var name        = dotcfg.name || 'fsm',\n      states      = dotcfg.states || [],\n      transitions = dotcfg.transitions || [],\n      rankdir     = dotcfg.rankdir,\n      output      = [],\n      n, max;\n\n  output.push(\"digraph \" + quote(name) + \" {\")\n  if (rankdir)\n    output.push(\"  rankdir=\" + rankdir + \";\")\n  for(n = 0, max = states.length ; n < max ; n++)\n    output.push(dotify.state(states[n]))\n  for(n = 0, max = transitions.length ; n < max ; n++)\n    output.push(dotify.edge(transitions[n]))\n  output.push(\"}\")\n  return output.join(\"\\n\")\n\n}\n\ndotify.state = function(state) {\n  return \"  \" + quote(state) + \";\"\n}\n\ndotify.edge = function(edge) {\n  return \"  \" + quote(edge.from) + \" -> \" + quote(edge.to) + dotify.edge.attr(edge) + \";\"\n}\n\ndotify.edge.attr = function(edge) {\n  var n, max, key, keys = Object.keys(edge).sort(), output = [];\n  for(n = 0, max = keys.length ; n < max ; n++) {\n    key = keys[n];\n    if (key !== 'from' && key !== 'to')\n      output.push(key + \"=\" + quote(edge[key]))\n  }\n  return output.length > 0 ? \" [ \" + output.join(\" ; \") + \" ]\" : \"\"\n}\n\n//-------------------------------------------------------------------------------------------------\n\nvisualize.dotcfg = dotcfg;\nvisualize.dotify = dotify;\n\n//-------------------------------------------------------------------------------------------------\n\nmodule.exports = visualize;\n\n//-------------------------------------------------------------------------------------------------\n\n\n/***/ })\n/******/ ]);\n});"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"javascript-state-machine\",\n  \"description\": \"A finite state machine library\",\n  \"homepage\": \"https://github.com/jakesgordon/javascript-state-machine\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git://github.com/jakesgordon/javascript-state-machine.git\"\n  },\n  \"keywords\": [\n    \"finite state machine\",\n    \"state machine\",\n    \"server\",\n    \"client\"\n  ],\n  \"author\": {\n    \"name\": \"Jake Gordon\",\n    \"email\": \"jakesgordon@gmail.com\"\n  },\n  \"maintainers\": [\n    {\n      \"name\": \"Jake Gordon\",\n      \"email\": \"jakesgordon@gmail.com\"\n    }\n  ],\n  \"license\": \"MIT\",\n  \"main\": \"lib/state-machine.js\",\n  \"files\": [\n    \"lib/**/*.js\",\n    \"dist/**/*.js\"\n  ],\n  \"directories\": {},\n  \"devDependencies\": {\n    \"ava\": \"^0.17.0\",\n    \"fs-sync\": \"^1.0.3\",\n    \"glob\": \"^7.1.1\",\n    \"nyc\": \"^10.0.0\",\n    \"pascal-case\": \"^2.0.0\",\n    \"uglify-js\": \"^2.7.5\",\n    \"webpack\": \"^2.2.0-rc.1\"\n  },\n  \"version\": \"3.1.0\",\n  \"scripts\": {\n    \"start\": \"npm run watch\",\n    \"build\": \"npm run bundle && npm run minify\",\n    \"bundle\": \"webpack\",\n    \"minify\": \"bin/minify\",\n    \"watch\": \"ava --watch\",\n    \"test\": \"nyc ava -v && nyc report --reporter=html\"\n  },\n  \"ava\": {\n    \"files\": [\n      \"test/**/*.js\"\n    ],\n    \"source\": [\n      \"src/**/*.js\"\n    ]\n  }\n}\n"
  },
  {
    "path": "src/app.js",
    "content": "'use strict'\n\n//-----------------------------------------------------------------------------------------------\n\nvar mixin    = require('./util/mixin'),\n    camelize = require('./util/camelize'),\n    plugin   = require('./plugin'),\n    Config   = require('./config'),\n    JSM      = require('./jsm');\n\n//-----------------------------------------------------------------------------------------------\n\nvar PublicMethods = {\n  is:                  function(state)       { return this._fsm.is(state)                                     },\n  can:                 function(transition)  { return this._fsm.can(transition)                               },\n  cannot:              function(transition)  { return this._fsm.cannot(transition)                            },\n  observe:             function()            { return this._fsm.observe(arguments)                            },\n  transitions:         function()            { return this._fsm.transitions()                                 },\n  allTransitions:      function()            { return this._fsm.allTransitions()                              },\n  allStates:           function()            { return this._fsm.allStates()                                   },\n  onInvalidTransition: function(t, from, to) { return this._fsm.onInvalidTransition(t, from, to)              },\n  onPendingTransition: function(t, from, to) { return this._fsm.onPendingTransition(t, from, to)              },\n}\n\nvar PublicProperties = {\n  state: {\n    configurable: false,\n    enumerable:   true,\n    get: function() {\n      return this._fsm.state;\n    },\n    set: function(state) {\n      throw Error('use transitions to change state')\n    }\n  }\n}\n\n//-----------------------------------------------------------------------------------------------\n\nfunction StateMachine(options) {\n  return apply(this || {}, options);\n}\n\nfunction factory() {\n  var cstor, options;\n  if (typeof arguments[0] === 'function') {\n    cstor   = arguments[0];\n    options = arguments[1] || {};\n  }\n  else {\n    cstor   = function() { this._fsm.apply(this, arguments) };\n    options = arguments[0] || {};\n  }\n  var config = new Config(options, StateMachine);\n  build(cstor.prototype, config);\n  cstor.prototype._fsm.config = config; // convenience access to shared config without needing an instance\n  return cstor;\n}\n\n//-------------------------------------------------------------------------------------------------\n\nfunction apply(instance, options) {\n  var config = new Config(options, StateMachine);\n  build(instance, config);\n  instance._fsm();\n  return instance;\n}\n\nfunction build(target, config) {\n  if ((typeof target !== 'object') || Array.isArray(target))\n    throw Error('StateMachine can only be applied to objects');\n  plugin.build(target, config);\n  Object.defineProperties(target, PublicProperties);\n  mixin(target, PublicMethods);\n  mixin(target, config.methods);\n  config.allTransitions().forEach(function(transition) {\n    target[camelize(transition)] = function() {\n      return this._fsm.fire(transition, [].slice.call(arguments))\n    }\n  });\n  target._fsm = function() {\n    this._fsm = new JSM(this, config);\n    this._fsm.init(arguments);\n  }\n}\n\n//-----------------------------------------------------------------------------------------------\n\nStateMachine.version  = '3.0.1';\nStateMachine.factory  = factory;\nStateMachine.apply    = apply;\nStateMachine.defaults = {\n  wildcard: '*',\n  init: {\n    name: 'init',\n    from: 'none'\n  }\n}\n\n//===============================================================================================\n\nmodule.exports = StateMachine;\n"
  },
  {
    "path": "src/config.js",
    "content": "'use strict'\n\n//-------------------------------------------------------------------------------------------------\n\nvar mixin    = require('./util/mixin'),\n    camelize = require('./util/camelize');\n\n//-------------------------------------------------------------------------------------------------\n\nfunction Config(options, StateMachine) {\n\n  options = options || {};\n\n  this.options     = options; // preserving original options can be useful (e.g visualize plugin)\n  this.defaults    = StateMachine.defaults;\n  this.states      = [];\n  this.transitions = [];\n  this.map         = {};\n  this.lifecycle   = this.configureLifecycle();\n  this.init        = this.configureInitTransition(options.init);\n  this.data        = this.configureData(options.data);\n  this.methods     = this.configureMethods(options.methods);\n\n  this.map[this.defaults.wildcard] = {};\n\n  this.configureTransitions(options.transitions || []);\n\n  this.plugins = this.configurePlugins(options.plugins, StateMachine.plugin);\n\n}\n\n//-------------------------------------------------------------------------------------------------\n\nmixin(Config.prototype, {\n\n  addState: function(name) {\n    if (!this.map[name]) {\n      this.states.push(name);\n      this.addStateLifecycleNames(name);\n      this.map[name] = {};\n    }\n  },\n\n  addStateLifecycleNames: function(name) {\n    this.lifecycle.onEnter[name] = camelize.prepended('onEnter', name);\n    this.lifecycle.onLeave[name] = camelize.prepended('onLeave', name);\n    this.lifecycle.on[name]      = camelize.prepended('on',      name);\n  },\n\n  addTransition: function(name) {\n    if (this.transitions.indexOf(name) < 0) {\n      this.transitions.push(name);\n      this.addTransitionLifecycleNames(name);\n    }\n  },\n\n  addTransitionLifecycleNames: function(name) {\n    this.lifecycle.onBefore[name] = camelize.prepended('onBefore', name);\n    this.lifecycle.onAfter[name]  = camelize.prepended('onAfter',  name);\n    this.lifecycle.on[name]       = camelize.prepended('on',       name);\n  },\n\n  mapTransition: function(transition) {\n    var name = transition.name,\n        from = transition.from,\n        to   = transition.to;\n    this.addState(from);\n    if (typeof to !== 'function')\n      this.addState(to);\n    this.addTransition(name);\n    this.map[from][name] = transition;\n    return transition;\n  },\n\n  configureLifecycle: function() {\n    return {\n      onBefore: { transition: 'onBeforeTransition' },\n      onAfter:  { transition: 'onAfterTransition'  },\n      onEnter:  { state:      'onEnterState'       },\n      onLeave:  { state:      'onLeaveState'       },\n      on:       { transition: 'onTransition'       }\n    };\n  },\n\n  configureInitTransition: function(init) {\n    if (typeof init === 'string') {\n      return this.mapTransition(mixin({}, this.defaults.init, { to: init, active: true }));\n    }\n    else if (typeof init === 'object') {\n      return this.mapTransition(mixin({}, this.defaults.init, init, { active: true }));\n    }\n    else {\n      this.addState(this.defaults.init.from);\n      return this.defaults.init;\n    }\n  },\n\n  configureData: function(data) {\n    if (typeof data === 'function')\n      return data;\n    else if (typeof data === 'object')\n      return function() { return data; }\n    else\n      return function() { return {};  }\n  },\n\n  configureMethods: function(methods) {\n    return methods || {};\n  },\n\n  configurePlugins: function(plugins, builtin) {\n    plugins = plugins || [];\n    var n, max, plugin;\n    for(n = 0, max = plugins.length ; n < max ; n++) {\n      plugin = plugins[n];\n      if (typeof plugin === 'function')\n        plugins[n] = plugin = plugin()\n      if (plugin.configure)\n        plugin.configure(this);\n    }\n    return plugins\n  },\n\n  configureTransitions: function(transitions) {\n    var i, n, transition, from, to, wildcard = this.defaults.wildcard;\n    for(n = 0 ; n < transitions.length ; n++) {\n      transition = transitions[n];\n      from  = Array.isArray(transition.from) ? transition.from : [transition.from || wildcard]\n      to    = transition.to || wildcard;\n      for(i = 0 ; i < from.length ; i++) {\n        this.mapTransition({ name: transition.name, from: from[i], to: to });\n      }\n    }\n  },\n\n  transitionFor: function(state, transition) {\n    var wildcard = this.defaults.wildcard;\n    return this.map[state][transition] ||\n           this.map[wildcard][transition];\n  },\n\n  transitionsFor: function(state) {\n    var wildcard = this.defaults.wildcard;\n    return Object.keys(this.map[state]).concat(Object.keys(this.map[wildcard]));\n  },\n\n  allStates: function() {\n    return this.states;\n  },\n\n  allTransitions: function() {\n    return this.transitions;\n  }\n\n});\n\n//-------------------------------------------------------------------------------------------------\n\nmodule.exports = Config;\n\n//-------------------------------------------------------------------------------------------------\n"
  },
  {
    "path": "src/jsm.js",
    "content": "\nvar mixin      = require('./util/mixin'),\n    Exception  = require('./util/exception'),\n    plugin     = require('./plugin'),\n    UNOBSERVED = [ null, [] ];\n\n//-------------------------------------------------------------------------------------------------\n\nfunction JSM(context, config) {\n  this.context   = context;\n  this.config    = config;\n  this.state     = config.init.from;\n  this.observers = [context];\n}\n\n//-------------------------------------------------------------------------------------------------\n\nmixin(JSM.prototype, {\n\n  init: function(args) {\n    mixin(this.context, this.config.data.apply(this.context, args));\n    plugin.hook(this, 'init');\n    if (this.config.init.active)\n      return this.fire(this.config.init.name, []);\n  },\n\n  is: function(state) {\n    return Array.isArray(state) ? (state.indexOf(this.state) >= 0) : (this.state === state);\n  },\n\n  isPending: function() {\n    return this.pending;\n  },\n\n  can: function(transition) {\n    return !this.isPending() && !!this.seek(transition);\n  },\n\n  cannot: function(transition) {\n    return !this.can(transition);\n  },\n\n  allStates: function() {\n    return this.config.allStates();\n  },\n\n  allTransitions: function() {\n    return this.config.allTransitions();\n  },\n\n  transitions: function() {\n    return this.config.transitionsFor(this.state);\n  },\n\n  seek: function(transition, args) {\n    var wildcard = this.config.defaults.wildcard,\n        entry    = this.config.transitionFor(this.state, transition),\n        to       = entry && entry.to;\n    if (typeof to === 'function')\n      return to.apply(this.context, args);\n    else if (to === wildcard)\n      return this.state\n    else\n      return to\n  },\n\n  fire: function(transition, args) {\n    return this.transit(transition, this.state, this.seek(transition, args), args);\n  },\n\n  transit: function(transition, from, to, args) {\n\n    var lifecycle = this.config.lifecycle,\n        changed   = this.config.options.observeUnchangedState || (from !== to);\n\n    if (!to)\n      return this.context.onInvalidTransition(transition, from, to);\n\n    if (this.isPending())\n      return this.context.onPendingTransition(transition, from, to);\n\n    this.config.addState(to);  // might need to add this state if it's unknown (e.g. conditional transition or goto)\n\n    this.beginTransit();\n\n    args.unshift({             // this context will be passed to each lifecycle event observer\n      transition: transition,\n      from:       from,\n      to:         to,\n      fsm:        this.context\n    });\n\n    return this.observeEvents([\n                this.observersForEvent(lifecycle.onBefore.transition),\n                this.observersForEvent(lifecycle.onBefore[transition]),\n      changed ? this.observersForEvent(lifecycle.onLeave.state) : UNOBSERVED,\n      changed ? this.observersForEvent(lifecycle.onLeave[from]) : UNOBSERVED,\n                this.observersForEvent(lifecycle.on.transition),\n      changed ? [ 'doTransit', [ this ] ]                       : UNOBSERVED,\n      changed ? this.observersForEvent(lifecycle.onEnter.state) : UNOBSERVED,\n      changed ? this.observersForEvent(lifecycle.onEnter[to])   : UNOBSERVED,\n      changed ? this.observersForEvent(lifecycle.on[to])        : UNOBSERVED,\n                this.observersForEvent(lifecycle.onAfter.transition),\n                this.observersForEvent(lifecycle.onAfter[transition]),\n                this.observersForEvent(lifecycle.on[transition])\n    ], args);\n  },\n\n  beginTransit: function()          { this.pending = true;                 },\n  endTransit:   function(result)    { this.pending = false; return result; },\n  failTransit:  function(result)    { this.pending = false; throw result;  },\n  doTransit:    function(lifecycle) { this.state = lifecycle.to;           },\n\n  observe: function(args) {\n    if (args.length === 2) {\n      var observer = {};\n      observer[args[0]] = args[1];\n      this.observers.push(observer);\n    }\n    else {\n      this.observers.push(args[0]);\n    }\n  },\n\n  observersForEvent: function(event) { // TODO: this could be cached\n    var n = 0, max = this.observers.length, observer, result = [];\n    for( ; n < max ; n++) {\n      observer = this.observers[n];\n      if (observer[event])\n        result.push(observer);\n    }\n    return [ event, result, true ]\n  },\n\n  observeEvents: function(events, args, previousEvent, previousResult) {\n    if (events.length === 0) {\n      return this.endTransit(previousResult === undefined ? true : previousResult);\n    }\n\n    var event     = events[0][0],\n        observers = events[0][1],\n        pluggable = events[0][2];\n\n    args[0].event = event;\n    if (event && pluggable && event !== previousEvent)\n      plugin.hook(this, 'lifecycle', args);\n\n    if (observers.length === 0) {\n      events.shift();\n      return this.observeEvents(events, args, event, previousResult);\n    }\n    else {\n      var observer = observers.shift(),\n          result = observer[event].apply(observer, args);\n      if (result && typeof result.then === 'function') {\n        return result.then(this.observeEvents.bind(this, events, args, event))\n                     .catch(this.failTransit.bind(this))\n      }\n      else if (result === false) {\n        return this.endTransit(false);\n      }\n      else {\n        return this.observeEvents(events, args, event, result);\n      }\n    }\n  },\n\n  onInvalidTransition: function(transition, from, to) {\n    throw new Exception(\"transition is invalid in current state\", transition, from, to, this.state);\n  },\n\n  onPendingTransition: function(transition, from, to) {\n    throw new Exception(\"transition is invalid while previous transition is still in progress\", transition, from, to, this.state);\n  }\n\n});\n\n//-------------------------------------------------------------------------------------------------\n\nmodule.exports = JSM;\n\n//-------------------------------------------------------------------------------------------------\n"
  },
  {
    "path": "src/plugin/history.js",
    "content": "'use strict'\n\n//-------------------------------------------------------------------------------------------------\n\nvar camelize = require('../util/camelize');\n\n//-------------------------------------------------------------------------------------------------\n\nmodule.exports = function(options) { options = options || {};\n\n  var past       = camelize(options.name || options.past   || 'history'),\n      future     = camelize(                options.future || 'future'),\n      clear      = camelize.prepended('clear', past),\n      back       = camelize.prepended(past,   'back'),\n      forward    = camelize.prepended(past,   'forward'),\n      canBack    = camelize.prepended('can',   back),\n      canForward = camelize.prepended('can',   forward),\n      max        = options.max;\n\n  var plugin = {\n\n    configure: function(config) {\n      config.addTransitionLifecycleNames(back);\n      config.addTransitionLifecycleNames(forward);\n    },\n\n    init: function(instance) {\n      instance[past]   = [];\n      instance[future] = [];\n    },\n\n    lifecycle: function(instance, lifecycle) {\n      if (lifecycle.event === 'onEnterState') {\n        instance[past].push(lifecycle.to);\n        if (max && instance[past].length > max)\n          instance[past].shift();\n        if (lifecycle.transition !== back && lifecycle.transition !== forward)\n          instance[future].length = 0;\n      }\n    },\n\n    methods:    {},\n    properties: {}\n\n  }\n\n  plugin.methods[clear] = function() {\n    this[past].length = 0\n    this[future].length = 0\n  }\n\n  plugin.properties[canBack] = {\n    get: function() {\n      return this[past].length > 1\n    }\n  }\n\n  plugin.properties[canForward] = {\n    get: function() {\n      return this[future].length > 0\n    }\n  }\n\n  plugin.methods[back] = function() {\n    if (!this[canBack])\n      throw Error('no history');\n    var from = this[past].pop(),\n        to   = this[past].pop();\n    this[future].push(from);\n    this._fsm.transit(back, from, to, []);\n  }\n\n  plugin.methods[forward] = function() {\n    if (!this[canForward])\n      throw Error('no history');\n    var from = this.state,\n        to = this[future].pop();\n    this._fsm.transit(forward, from, to, []);\n  }\n\n  return plugin;\n\n}\n"
  },
  {
    "path": "src/plugin/visualize.js",
    "content": "'use strict'\n\n//-------------------------------------------------------------------------------------------------\n\nvar mixin = require('../util/mixin')\n\n//-------------------------------------------------------------------------------------------------\n\nfunction visualize(fsm, options) {\n  return dotify(dotcfg(fsm, options));\n}\n\n//-------------------------------------------------------------------------------------------------\n\nfunction dotcfg(fsm, options) {\n\n  options = options || {}\n\n  var config      = dotcfg.fetch(fsm),\n      name        = options.name,\n      rankdir     = dotcfg.rankdir(options.orientation),\n      states      = dotcfg.states(config, options),\n      transitions = dotcfg.transitions(config, options),\n      result      = { }\n\n  if (name)\n    result.name = name\n\n  if (rankdir)\n    result.rankdir = rankdir\n\n  if (states && states.length > 0)\n    result.states = states\n\n  if (transitions && transitions.length > 0)\n    result.transitions = transitions\n\n  return result\n}\n\n//-------------------------------------------------------------------------------------------------\n\ndotcfg.fetch = function(fsm) {\n  return (typeof fsm === 'function') ? fsm.prototype._fsm.config\n                                     : fsm._fsm.config\n}\n\ndotcfg.rankdir = function(orientation) {\n  if (orientation === 'horizontal')\n    return 'LR';\n  else if (orientation === 'vertical')\n    return 'TB';\n}\n\ndotcfg.states = function(config, options) {\n  var index, states = config.states;\n  if (!options.init) { // if not showing init transition, then slice out the implied init :from state\n    index  = states.indexOf(config.init.from);\n    states = states.slice(0, index).concat(states.slice(index+1));\n  }\n  return states;\n}\n\ndotcfg.transitions = function(config, options) {\n  var n, max, transition,\n      init        = config.init,\n      transitions = config.options.transitions || [], // easier to visualize using the ORIGINAL transition declarations rather than our run-time mapping\n      output = [];\n  if (options.init && init.active)\n    dotcfg.transition(init.name, init.from, init.to, init.dot, config, options, output)\n  for (n = 0, max = transitions.length ; n < max ; n++) {\n    transition = config.options.transitions[n]\n    dotcfg.transition(transition.name, transition.from, transition.to, transition.dot, config, options, output)\n  }\n  return output\n}\n\ndotcfg.transition = function(name, from, to, dot, config, options, output) {\n  var n, max, wildcard = config.defaults.wildcard\n\n  if (Array.isArray(from)) {\n    for(n = 0, max = from.length ; n < max ; n++)\n      dotcfg.transition(name, from[n], to, dot, config, options, output)\n  }\n  else if (from === wildcard || from === undefined) {\n    for(n = 0, max = config.states.length ; n < max ; n++)\n      dotcfg.transition(name, config.states[n], to, dot, config, options, output)\n  }\n  else if (to === wildcard || to === undefined) {\n    dotcfg.transition(name, from, from, dot, config, options, output)\n  }\n  else if (typeof to === 'function') {\n    // do nothing, can't display conditional transition\n  }\n  else {\n    output.push(mixin({}, { from: from, to: to, label: pad(name) }, dot || {}))\n  }\n\n}\n\n//-------------------------------------------------------------------------------------------------\n\nfunction pad(name) {\n  return \" \" + name + \" \"\n}\n\nfunction quote(name) {\n  return \"\\\"\" + name + \"\\\"\"\n}\n\nfunction dotify(dotcfg) {\n\n  dotcfg = dotcfg || {};\n\n  var name        = dotcfg.name || 'fsm',\n      states      = dotcfg.states || [],\n      transitions = dotcfg.transitions || [],\n      rankdir     = dotcfg.rankdir,\n      output      = [],\n      n, max;\n\n  output.push(\"digraph \" + quote(name) + \" {\")\n  if (rankdir)\n    output.push(\"  rankdir=\" + rankdir + \";\")\n  for(n = 0, max = states.length ; n < max ; n++)\n    output.push(dotify.state(states[n]))\n  for(n = 0, max = transitions.length ; n < max ; n++)\n    output.push(dotify.edge(transitions[n]))\n  output.push(\"}\")\n  return output.join(\"\\n\")\n\n}\n\ndotify.state = function(state) {\n  return \"  \" + quote(state) + \";\"\n}\n\ndotify.edge = function(edge) {\n  return \"  \" + quote(edge.from) + \" -> \" + quote(edge.to) + dotify.edge.attr(edge) + \";\"\n}\n\ndotify.edge.attr = function(edge) {\n  var n, max, key, keys = Object.keys(edge).sort(), output = [];\n  for(n = 0, max = keys.length ; n < max ; n++) {\n    key = keys[n];\n    if (key !== 'from' && key !== 'to')\n      output.push(key + \"=\" + quote(edge[key]))\n  }\n  return output.length > 0 ? \" [ \" + output.join(\" ; \") + \" ]\" : \"\"\n}\n\n//-------------------------------------------------------------------------------------------------\n\nvisualize.dotcfg = dotcfg;\nvisualize.dotify = dotify;\n\n//-------------------------------------------------------------------------------------------------\n\nmodule.exports = visualize;\n\n//-------------------------------------------------------------------------------------------------\n"
  },
  {
    "path": "src/plugin.js",
    "content": "'use strict'\n\n//-------------------------------------------------------------------------------------------------\n\nvar mixin = require('./util/mixin');\n\n//-------------------------------------------------------------------------------------------------\n\nmodule.exports = {\n\n  build: function(target, config) {\n    var n, max, plugin, plugins = config.plugins;\n    for(n = 0, max = plugins.length ; n < max ; n++) {\n      plugin = plugins[n];\n      if (plugin.methods)\n        mixin(target, plugin.methods);\n      if (plugin.properties)\n        Object.defineProperties(target, plugin.properties);\n    }\n  },\n\n  hook: function(fsm, name, additional) {\n    var n, max, method, plugin,\n        plugins = fsm.config.plugins,\n        args    = [fsm.context];\n\n    if (additional)\n      args = args.concat(additional)\n\n    for(n = 0, max = plugins.length ; n < max ; n++) {\n      plugin = plugins[n]\n      method = plugins[n][name]\n      if (method)\n        method.apply(plugin, args);\n    }\n  }\n\n}\n\n//-------------------------------------------------------------------------------------------------\n"
  },
  {
    "path": "src/util/camelize.js",
    "content": "'use strict'\n\n//-------------------------------------------------------------------------------------------------\n\nfunction camelize(label) {\n\n  if (label.length === 0)\n    return label;\n\n  var n, result, word, words = label.split(/[_-]/);\n\n  // single word with first character already lowercase, return untouched\n  if ((words.length === 1) && (words[0][0].toLowerCase() === words[0][0]))\n    return label;\n\n  result = words[0].toLowerCase();\n  for(n = 1 ; n < words.length ; n++) {\n    result = result + words[n].charAt(0).toUpperCase() + words[n].substring(1).toLowerCase();\n  }\n\n  return result;\n}\n\n//-------------------------------------------------------------------------------------------------\n\ncamelize.prepended = function(prepend, label) {\n  label = camelize(label);\n  return prepend + label[0].toUpperCase() + label.substring(1);\n}\n\n//-------------------------------------------------------------------------------------------------\n\nmodule.exports = camelize;\n"
  },
  {
    "path": "src/util/exception.js",
    "content": "'use strict'\n\nmodule.exports = function(message, transition, from, to, current) {\n  this.message    = message;\n  this.transition = transition;\n  this.from       = from;\n  this.to         = to;\n  this.current    = current;\n}\n"
  },
  {
    "path": "src/util/mixin.js",
    "content": "'use strict'\n\nmodule.exports = function(target, sources) {\n  var n, source, key;\n  for(n = 1 ; n < arguments.length ; n++) {\n    source = arguments[n];\n    for(key in source) {\n      if (source.hasOwnProperty(key))\n        target[key] = source[key];\n    }\n  }\n  return target;\n}\n"
  },
  {
    "path": "test/basics.js",
    "content": "import test         from 'ava';\nimport StateMachine from '../src/app';\n\n//-------------------------------------------------------------------------------------------------\n\ntest('version', t => {\n  t.is(StateMachine.version, '3.0.1');\n});\n\n//-------------------------------------------------------------------------------------------------\n\ntest('state machine', t => {\n\n  var fsm = new StateMachine({\n    init: 'green',\n    transitions: [\n      { name: 'warn',  from: 'green',  to: 'yellow' },\n      { name: 'panic', from: 'yellow', to: 'red'    },\n      { name: 'calm',  from: 'red',    to: 'yellow' },\n      { name: 'clear', from: 'yellow', to: 'green'  }\n    ]\n  })\n\n  t.is(fsm.state, 'green')\n\n  fsm.warn();  t.is(fsm.state, 'yellow')\n  fsm.panic(); t.is(fsm.state, 'red')\n  fsm.calm();  t.is(fsm.state, 'yellow')\n  fsm.clear(); t.is(fsm.state, 'green')\n\n});\n\n//-----------------------------------------------------------------------------\n\ntest('state machine factory', t => {\n\n  var Alarm = StateMachine.factory({\n        init: 'green',\n        transitions: [\n          { name: 'warn',  from: 'green',  to: 'yellow' },\n          { name: 'panic', from: 'yellow', to: 'red'    },\n          { name: 'calm',  from: 'red',    to: 'yellow' },\n          { name: 'clear', from: 'yellow', to: 'green'  }\n        ]\n      }),\n      a = new Alarm(),\n      b = new Alarm();\n\n  t.is(a.state, 'green')\n  t.is(b.state, 'green')\n\n  a.warn();  t.is(a.state, 'yellow'); t.is(b.state, 'green')\n  a.panic(); t.is(a.state, 'red');    t.is(b.state, 'green')\n  a.calm();  t.is(a.state, 'yellow'); t.is(b.state, 'green')\n  a.clear(); t.is(a.state, 'green');  t.is(b.state, 'green')\n\n  b.warn();  t.is(a.state, 'green');  t.is(b.state, 'yellow')\n  b.panic(); t.is(a.state, 'green');  t.is(b.state, 'red')\n  b.calm();  t.is(a.state, 'green');  t.is(b.state, 'yellow')\n  b.clear(); t.is(a.state, 'green');  t.is(b.state, 'green')\n\n});\n\n//-----------------------------------------------------------------------------\n\ntest('state machine - applied to existing object', t => {\n\n  var obj = { name: 'alarm' }\n\n  StateMachine.apply(obj, {\n    init: 'green',\n    transitions: [\n      { name: 'warn',  from: 'green',  to: 'yellow' },\n      { name: 'panic', from: 'yellow', to: 'red'    },\n      { name: 'calm',  from: 'red',    to: 'yellow' },\n      { name: 'clear', from: 'yellow', to: 'green'  }\n    ]\n  });\n\n  t.is(obj.name,  'alarm');\n  t.is(obj.state, 'green');\n\n  obj.warn();  t.is(obj.state, 'yellow')\n  obj.panic(); t.is(obj.state, 'red')\n  obj.calm();  t.is(obj.state, 'yellow')\n  obj.clear(); t.is(obj.state, 'green')\n\n});\n\n//-----------------------------------------------------------------------------\n\ntest('state machine factory - applied to existing class', t => {\n\n  function Alarm(name) {\n    this.name = name\n    this._fsm(); // manual step needed to construct this FSM instance\n  }\n\n  StateMachine.factory(Alarm, {\n    init: 'green',\n    transitions: [\n      { name: 'warn',  from: 'green',  to: 'yellow' },\n      { name: 'panic', from: 'yellow', to: 'red'    },\n      { name: 'calm',  from: 'red',    to: 'yellow' },\n      { name: 'clear', from: 'yellow', to: 'green'  }\n    ]\n  });\n\n  var a = new Alarm('A'),\n      b = new Alarm('B');\n\n  t.is(a.name, 'A')\n  t.is(b.name, 'B')\n\n  t.is(a.state, 'green')\n  t.is(b.state, 'green')\n\n  a.warn();  t.is(a.state, 'yellow'); t.is(b.state, 'green')\n  a.panic(); t.is(a.state, 'red');    t.is(b.state, 'green')\n  a.calm();  t.is(a.state, 'yellow'); t.is(b.state, 'green')\n  a.clear(); t.is(a.state, 'green');  t.is(b.state, 'green')\n\n  b.warn();  t.is(a.state, 'green'); t.is(b.state, 'yellow')\n  b.panic(); t.is(a.state, 'green'); t.is(b.state, 'red')\n  b.calm();  t.is(a.state, 'green'); t.is(b.state, 'yellow')\n  b.clear(); t.is(a.state, 'green'); t.is(b.state, 'green')\n\n});\n\n//-----------------------------------------------------------------------------\n"
  },
  {
    "path": "test/construction.js",
    "content": "import test         from 'ava'\nimport StateMachine from '../src/app'\n\n//-------------------------------------------------------------------------------------------------\n\ntest('singleton construction', t => {\n\n  var fsm = new StateMachine({\n    transitions: [\n      { name: 'init',  from: 'none', to: 'A' },\n      { name: 'step1', from: 'A',    to: 'B' },\n      { name: 'step2', from: 'B',    to: 'C' }\n    ]\n  });\n\n  t.is(fsm.state, 'none')\n\n  t.deepEqual(fsm.allStates(),      [ 'none', 'A', 'B', 'C' ])\n  t.deepEqual(fsm.allTransitions(), [ 'init', 'step1', 'step2' ])\n  t.deepEqual(fsm.transitions(),    [ 'init' ])\n\n})\n\n\n//-------------------------------------------------------------------------------------------------\n\ntest('singleton construction - with init state', t => {\n\n  var fsm = new StateMachine({\n    init: 'A',\n    transitions: [\n      { name: 'step1', from: 'A', to: 'B' },\n      { name: 'step2', from: 'B', to: 'C' }\n    ]\n  });\n\n  t.is(fsm.state, 'A')\n\n  t.deepEqual(fsm.allStates(),      [ 'none', 'A', 'B', 'C' ])\n  t.deepEqual(fsm.allTransitions(), [ 'init', 'step1', 'step2' ])\n  t.deepEqual(fsm.transitions(),    [ 'step1' ])\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('singleton construction - with init state and transition', t => {\n\n  var fsm = new StateMachine({\n    init: { name: 'boot', to: 'A' },\n    transitions: [\n      { name: 'step1', from: 'A', to: 'B' },\n      { name: 'step2', from: 'B', to: 'C' }\n    ]\n  });\n\n  t.is(fsm.state, 'A')\n\n  t.deepEqual(fsm.allStates(),      [ 'none', 'A', 'B', 'C' ])\n  t.deepEqual(fsm.allTransitions(), [ 'boot', 'step1', 'step2' ])\n  t.deepEqual(fsm.transitions(),    [ 'step1' ])\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('singleton construction - with init state, transition, AND from state', t => {\n\n  var fsm = new StateMachine({\n    init: { name: 'boot', from: 'booting', to: 'A' },\n    transitions: [\n      { name: 'step1', from: 'A', to: 'B' },\n      { name: 'step2', from: 'B', to: 'C' }\n    ]\n  });\n\n  t.is(fsm.state, 'A')\n\n  t.deepEqual(fsm.allStates(),      [ 'booting', 'A', 'B', 'C' ])\n  t.deepEqual(fsm.allTransitions(), [ 'boot', 'step1', 'step2' ])\n  t.deepEqual(fsm.transitions(),    [ 'step1' ])\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('singleton construction - with custom data and methods', t => {\n\n  var fsm = new StateMachine({\n    init: 'A',\n    transitions: [\n      { name: 'step1', from: 'A', to: 'B' },\n      { name: 'step2', from: 'B', to: 'C' }\n    ],\n    data: {\n      value: 42\n    },\n    methods: {\n      talk: function() {\n        return this.state + ' - ' + this.value\n      }\n    }\n  });\n\n  t.is(fsm.state,  'A')\n  t.is(fsm.value,  42)\n  t.is(fsm.talk(), 'A - 42')\n\n  fsm.step1()\n\n  t.is(fsm.state,  'B')\n  t.is(fsm.value,  42)\n  t.is(fsm.talk(), 'B - 42')\n\n  fsm.value = 99\n\n  t.is(fsm.state,  'B')\n  t.is(fsm.value,  99)\n  t.is(fsm.talk(), 'B - 99')\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('factory construction', t => {\n\n  var MyClass = StateMachine.factory({\n    init: 'A',\n    transitions: [\n      { name: 'step1', from: 'A', to: 'B' },\n      { name: 'step2', from: 'B', to: 'C' }\n    ]\n  });\n\n  var fsm1 = new MyClass(),\n      fsm2 = new MyClass(),\n      fsm3 = new MyClass();\n\n  fsm2.step1()\n  fsm3.step1()\n  fsm3.step2()\n\n  t.is(fsm1.state, 'A')\n  t.is(fsm2.state, 'B')\n  t.is(fsm3.state, 'C')\n\n  t.deepEqual(fsm1.allStates(), [ 'none', 'A', 'B', 'C' ])\n  t.deepEqual(fsm2.allStates(), [ 'none', 'A', 'B', 'C' ])\n  t.deepEqual(fsm3.allStates(), [ 'none', 'A', 'B', 'C' ])\n\n  t.deepEqual(fsm1.allTransitions(), [ 'init', 'step1', 'step2' ])\n  t.deepEqual(fsm2.allTransitions(), [ 'init', 'step1', 'step2' ])\n  t.deepEqual(fsm3.allTransitions(), [ 'init', 'step1', 'step2' ])\n\n  t.deepEqual(fsm1.transitions(), [ 'step1' ])\n  t.deepEqual(fsm2.transitions(), [ 'step2' ])\n  t.deepEqual(fsm3.transitions(), [         ])\n\n  t.is(fsm1.allStates, MyClass.prototype.allStates)\n  t.is(fsm2.allStates, MyClass.prototype.allStates)\n  t.is(fsm3.allStates, MyClass.prototype.allStates)\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('factory construction - with custom data and methods', t => {\n\n  var MyClass = StateMachine.factory({\n    init: 'A',\n    transitions: [\n      { name: 'step1', from: 'A', to: 'B' },\n      { name: 'step2', from: 'B', to: 'C' }\n    ],\n    data: function(value) {\n      return {\n        value: value\n      }\n    },\n    methods: {\n      talk: function() {\n        return this.state + ' - ' + this.value\n      }\n    }\n  });\n\n  var fsm1 = new MyClass(1),\n      fsm2 = new MyClass(2),\n      fsm3 = new MyClass(3);\n\n  t.is(fsm1.state, 'A')\n  t.is(fsm2.state, 'A')\n  t.is(fsm3.state, 'A')\n\n  t.is(fsm1.talk(), 'A - 1')\n  t.is(fsm2.talk(), 'A - 2')\n  t.is(fsm3.talk(), 'A - 3')\n\n  fsm2.step1()\n  fsm3.step1()\n  fsm3.step2()\n\n  t.is(fsm1.state, 'A')\n  t.is(fsm2.state, 'B')\n  t.is(fsm3.state, 'C')\n\n  t.is(fsm1.talk(), 'A - 1')\n  t.is(fsm2.talk(), 'B - 2')\n  t.is(fsm3.talk(), 'C - 3')\n\n  t.deepEqual(fsm1.allStates(), [ 'none', 'A', 'B', 'C' ])\n  t.deepEqual(fsm2.allStates(), [ 'none', 'A', 'B', 'C' ])\n  t.deepEqual(fsm3.allStates(), [ 'none', 'A', 'B', 'C' ])\n\n  t.deepEqual(fsm1.allTransitions(), [ 'init', 'step1', 'step2' ])\n  t.deepEqual(fsm2.allTransitions(), [ 'init', 'step1', 'step2' ])\n  t.deepEqual(fsm3.allTransitions(), [ 'init', 'step1', 'step2' ])\n\n  t.deepEqual(fsm1.transitions(), [ 'step1' ])\n  t.deepEqual(fsm2.transitions(), [ 'step2' ])\n  t.deepEqual(fsm3.transitions(), [         ])\n\n  t.is(fsm1.allStates, MyClass.prototype.allStates)\n  t.is(fsm2.allStates, MyClass.prototype.allStates)\n  t.is(fsm3.allStates, MyClass.prototype.allStates)\n\n})\n\n//-------------------------------------------------------------------------------------------------\n"
  },
  {
    "path": "test/defaults.js",
    "content": "import test         from 'ava';\nimport StateMachine from '../src/app';\n\n//-------------------------------------------------------------------------------------------------\n\nconst defaults = JSON.stringify(StateMachine.defaults);\n\ntest.afterEach.always('restore defaults', t => {\n  StateMachine.defaults = JSON.parse(defaults);\n});\n\n//-------------------------------------------------------------------------------------------------\n\ntest.serial('override global initialization defaults', t => {\n\n  StateMachine.defaults.init = {\n    name: 'boot',\n    from: 'booting'\n  }\n\n  var fsm = new StateMachine({\n    init: 'A',\n    transitions: [\n      { name: 'step1', from: 'A', to: 'B' },\n      { name: 'step2', from: 'B', to: 'C' }\n    ]\n  });\n\n  t.is(fsm.state, 'A');\n\n  t.deepEqual(fsm.allStates(),      [ 'booting', 'A', 'B', 'C' ]);\n  t.deepEqual(fsm.allTransitions(), [ 'boot', 'step1', 'step2' ]);\n  t.deepEqual(fsm.transitions(),    [ 'step1' ]);\n\n});\n\n//-------------------------------------------------------------------------------------------------\n\ntest.serial('override global initialization defaults (again)', t => {\n\n  StateMachine.defaults.init = {\n    name: 'start',\n    from: 'unknown'\n  }\n\n  var fsm = new StateMachine({\n    init: 'A',\n    transitions: [\n      { name: 'step1', from: 'A', to: 'B' },\n      { name: 'step2', from: 'B', to: 'C' }\n    ]\n  });\n\n  t.is(fsm.state, 'A');\n\n  t.deepEqual(fsm.allStates(),      [ 'unknown', 'A', 'B', 'C' ]);\n  t.deepEqual(fsm.allTransitions(), [ 'start', 'step1', 'step2' ]);\n  t.deepEqual(fsm.transitions(),    [ 'step1' ]);\n\n});\n\n//-------------------------------------------------------------------------------------------------\n"
  },
  {
    "path": "test/empty.js",
    "content": "import test         from 'ava'\nimport StateMachine from '../src/app'\n\n//-------------------------------------------------------------------------------------------------\n\ntest('empty state machine', t => {\n\n  var fsm = new StateMachine();\n\n  t.is(fsm.state, 'none')\n\n  t.deepEqual(fsm.allStates(),      [ 'none' ])\n  t.deepEqual(fsm.allTransitions(), [ ])\n  t.deepEqual(fsm.transitions(),    [ ])\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('empty state machine - but caller forgot new keyword', t => {\n\n  var fsm = StateMachine() // NOTE: missing 'new'\n\n  t.is(fsm.state, 'none')\n\n  t.deepEqual(fsm.allStates(),      [ 'none' ])\n  t.deepEqual(fsm.allTransitions(), [ ])\n  t.deepEqual(fsm.transitions(),    [ ])\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('empty state machine - applied to existing object', t => {\n\n  var fsm = {};\n\n  StateMachine.apply(fsm)\n\n  t.is(fsm.state, 'none')\n\n  t.deepEqual(fsm.allStates(),      [ 'none' ])\n  t.deepEqual(fsm.allTransitions(), [ ])\n  t.deepEqual(fsm.transitions(),    [ ])\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('empty state machine factory', t => {\n\n  var FSM = StateMachine.factory(),\n      fsm = new FSM();\n\n  t.is(fsm.state, 'none')\n  t.deepEqual(fsm.allStates(),      [ 'none' ])\n  t.deepEqual(fsm.allTransitions(), [ ])\n  t.deepEqual(fsm.transitions(),    [ ])\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('empty state machine factory - applied to existing class', t => {\n\n  var FSM = function() { this._fsm() };\n\n  StateMachine.factory(FSM)\n\n  var fsm = new FSM()\n\n  t.is(fsm.state, 'none')\n  t.deepEqual(fsm.allStates(),      [ 'none' ])\n  t.deepEqual(fsm.allTransitions(), [ ])\n  t.deepEqual(fsm.transitions(),    [ ])\n\n})\n\n//-------------------------------------------------------------------------------------------------\n"
  },
  {
    "path": "test/errors.js",
    "content": "import test         from 'ava'\nimport StateMachine from '../src/app'\n\n//-------------------------------------------------------------------------------------------------\n\ntest('state cannot be modified directly', t => {\n\n  var fsm = new StateMachine({\n    transitions: [\n      { name: 'step', from: 'none', to: 'complete' }\n    ]\n  })\n\n  t.is(fsm.state, 'none')\n  var error = t.throws(() => {\n    fsm.state = 'other'\n  })\n  t.is(error.message, 'use transitions to change state')\n  t.is(fsm.state, 'none')\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('StateMachine.apply only allowed on objects', t => {\n\n  var config = {\n    transitions: [\n      { name: 'step', from: 'none', to: 'complete' }\n    ]\n  };\n\n  var error = t.throws(() => {\n    StateMachine.apply(function() {}, config)\n  })\n  t.is(error.message, 'StateMachine can only be applied to objects')\n\n  error = t.throws(() => {\n    StateMachine.apply([], config)\n  })\n  t.is(error.message, 'StateMachine can only be applied to objects')\n\n  error = t.throws(() => {\n    StateMachine.apply(42, config)\n  })\n  t.is(error.message, 'StateMachine can only be applied to objects')\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('invalid transition raises an exception', t => {\n\n  var fsm = new StateMachine({\n        transitions: [\n          { name: 'step1', from: 'none', to: 'A' },\n          { name: 'step2', from: 'A',    to: 'B' }\n        ]\n      });\n\n  t.is(fsm.state,        'none')\n  t.is(fsm.can('step1'),  true)\n  t.is(fsm.can('step2'), false)\n\n  const error = t.throws(() => {\n    fsm.step2();\n  })\n\n  t.is(error.message,    'transition is invalid in current state')\n  t.is(error.transition, 'step2')\n  t.is(error.from,       'none')\n  t.is(error.to,         undefined)\n  t.is(error.current,    'none')\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('invalid transition handler can be customized', t => {\n\n  var fsm = new StateMachine({\n        transitions: [\n          { name: 'step1', from: 'none', to: 'A' },\n          { name: 'step2', from: 'A',    to: 'B' }\n        ],\n        methods: {\n          onInvalidTransition: function() { return 'custom error'; }\n        }\n      });\n\n  t.is(fsm.state,        'none')\n  t.is(fsm.can('step1'),  true)\n  t.is(fsm.can('step2'), false)\n  t.is(fsm.step2(),      'custom error')\n  t.is(fsm.state,        'none')\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('fire transition while existing transition is still in process raises an exception', t => {\n\n  var fsm = new StateMachine({\n        transitions: [\n          { name: 'step', from:  'none', to: 'A' },\n          { name: 'other', from: '*',    to: 'X' }\n        ],\n        methods: {\n          onBeforeStep:  function() { this.other();                 },\n          onBeforeOther: function() { t.fail('should never happen') },\n          onEnterX:      function() { t.fail('should never happen') }\n        }\n      });\n\n  t.is(fsm.state,        'none')\n  t.is(fsm.can('step'),  true)\n  t.is(fsm.can('other'), true)\n\n  const error = t.throws(() => {\n    fsm.step()\n  })\n\n  t.is(error.message, 'transition is invalid while previous transition is still in progress')\n  t.is(error.transition, 'other')\n  t.is(error.from,       'none')\n  t.is(error.to,         'X')\n  t.is(error.current,    'none')\n\n  t.is(fsm.state, 'none', 'entire transition was cancelled by the exception')\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('pending transition handler can be customized', t => {\n\n  var error = \"\",\n      fsm = new StateMachine({\n        transitions: [\n          { name: 'step', from:  'none', to: 'A' },\n          { name: 'other', from: '*',    to: 'X' }\n        ],\n        methods: {\n          onBeforeStep:        function() { error = this.other(); return false },\n          onPendingTransition: function() { return 'custom error' },\n          onBeforeOther:       function() { t.fail('should never happen') },\n          onEnterX:            function() { t.fail('should never happen') }\n        }\n      });\n\n  t.is(fsm.state,        'none')\n  t.is(fsm.can('step'),  true)\n  t.is(fsm.can('other'), true)\n  t.is(fsm.step(),       false)\n  t.is(fsm.state,        'none')\n  t.is(error,            'custom error')\n\n})\n\n//-------------------------------------------------------------------------------------------------\n"
  },
  {
    "path": "test/goto.js",
    "content": "import test            from 'ava'\nimport StateMachine    from '../src/app'\nimport LifecycleLogger from './helpers/lifecycle_logger'\n\n//-------------------------------------------------------------------------------------------------\n\nfunction goto(state) {\n  return state\n}\n\n//-------------------------------------------------------------------------------------------------\n\ntest('goto transition', t => {\n\n  var logger = new LifecycleLogger(),\n      fsm = new StateMachine({\n        init: 'A',\n        transitions: [\n          { name: 'step', from: 'A', to: 'B'  },\n          { name: 'step', from: 'B', to: 'C'  },\n          { name: 'step', from: 'C', to: 'D'  },\n          { name: 'goto', from: '*', to: goto }\n        ],\n        methods: {\n          onBeforeTransition: logger,\n          onBeforeInit:       logger,\n          onBeforeStep:       logger,\n          onBeforeGoto:       logger,\n          onLeaveState:       logger,\n          onLeaveNone:        logger,\n          onLeaveA:           logger,\n          onLeaveB:           logger,\n          onLeaveC:           logger,\n          onLeaveD:           logger,\n          onTransition:       logger,\n          onEnterState:       logger,\n          onEnterNone:        logger,\n          onEnterA:           logger,\n          onEnterB:           logger,\n          onEnterC:           logger,\n          onEnterD:           logger,\n          onAfterTransition:  logger,\n          onAfterInit:        logger,\n          onAfterStep:        logger,\n          onAfterGoto:        logger\n        }\n      });\n\n  t.is(fsm.state, 'A')\n  t.deepEqual(fsm.allStates(), [ 'none', 'A', 'B', 'C', 'D' ])\n  t.deepEqual(fsm.allTransitions(), [ 'init', 'step', 'goto' ])\n\n  logger.clear()\n\n  fsm.goto('C')\n\n  t.is(fsm.state, 'C')\n  t.deepEqual(logger.log, [\n    { event: 'onBeforeTransition', transition: 'goto', from: 'A', to: 'C', current: 'A', args: [ 'C' ] },\n    { event: 'onBeforeGoto',       transition: 'goto', from: 'A', to: 'C', current: 'A', args: [ 'C' ] },\n    { event: 'onLeaveState',       transition: 'goto', from: 'A', to: 'C', current: 'A', args: [ 'C' ] },\n    { event: 'onLeaveA',           transition: 'goto', from: 'A', to: 'C', current: 'A', args: [ 'C' ] },\n    { event: 'onTransition',       transition: 'goto', from: 'A', to: 'C', current: 'A', args: [ 'C' ] },\n    { event: 'onEnterState',       transition: 'goto', from: 'A', to: 'C', current: 'C', args: [ 'C' ] },\n    { event: 'onEnterC',           transition: 'goto', from: 'A', to: 'C', current: 'C', args: [ 'C' ] },\n    { event: 'onAfterTransition',  transition: 'goto', from: 'A', to: 'C', current: 'C', args: [ 'C' ] },\n    { event: 'onAfterGoto',        transition: 'goto', from: 'A', to: 'C', current: 'C', args: [ 'C' ] }\n  ])\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('goto can have additional arguments', t => {\n\n  var logger = new LifecycleLogger(),\n      fsm = new StateMachine({\n        init: 'A',\n        transitions: [\n          { name: 'step', from: 'A', to: 'B'  },\n          { name: 'step', from: 'B', to: 'C'  },\n          { name: 'step', from: 'C', to: 'D'  },\n          { name: 'goto', from: '*', to: goto }\n        ],\n        methods: {\n          onStep: logger,\n          onGoto: logger\n        }\n      });\n\n  t.is(fsm.state, 'A')\n  t.deepEqual(fsm.allStates(), [ 'none', 'A', 'B', 'C', 'D' ])\n  t.deepEqual(fsm.allTransitions(), [ 'init', 'step', 'goto' ])\n\n  logger.clear()\n\n  fsm.goto('C', 'with', 4, 'additional', 'arguments')\n\n  t.is(fsm.state, 'C')\n  t.deepEqual(logger.log, [\n    { event: 'onGoto', transition: 'goto', from: 'A', to: 'C', current: 'C', args: [ 'C', 'with', 4, 'additional', 'arguments' ] }\n  ])\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('goto can go to an unknown state', t => {\n\n  var fsm = new StateMachine({\n    init: 'A',\n    transitions: [\n      { name: 'step', from: 'A', to: 'B'  },\n      { name: 'step', from: 'B', to: 'C'  },\n      { name: 'step', from: 'C', to: 'D'  },\n      { name: 'goto', from: '*', to: goto }\n    ]\n  })\n\n  t.is(fsm.state, 'A')\n  t.deepEqual(fsm.allStates(), [ 'none', 'A', 'B', 'C', 'D' ]);\n\n  fsm.goto('B')\n  t.is(fsm.state, 'B')\n  t.deepEqual(fsm.allStates(), [ 'none', 'A', 'B', 'C', 'D' ]);\n\n  fsm.goto('X')\n  t.is(fsm.state, 'X')\n  t.deepEqual(fsm.allStates(), [ 'none', 'A', 'B', 'C', 'D', 'X' ]);\n  \n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('goto can be configured with a custom name', t => {\n\n  var logger = new LifecycleLogger(),\n      fsm = new StateMachine({\n        init: 'A',\n        transitions: [\n          { name: 'step', from: 'A', to: 'B'  },\n          { name: 'step', from: 'B', to: 'C'  },\n          { name: 'step', from: 'C', to: 'D'  },\n          { name: 'jump', from: '*', to: goto }\n        ],\n        methods: {\n          onBeforeTransition: logger,\n          onBeforeInit:       logger,\n          onBeforeStep:       logger,\n          onBeforeJump:       logger,\n          onLeaveState:       logger,\n          onLeaveNone:        logger,\n          onLeaveA:           logger,\n          onLeaveB:           logger,\n          onLeaveC:           logger,\n          onLeaveD:           logger,\n          onTransition:       logger,\n          onEnterState:       logger,\n          onEnterNone:        logger,\n          onEnterA:           logger,\n          onEnterB:           logger,\n          onEnterC:           logger,\n          onEnterD:           logger,\n          onAfterTransition:  logger,\n          onAfterInit:        logger,\n          onAfterStep:        logger,\n          onAfterJump:        logger\n        }\n      });\n\n  t.is(fsm.state, 'A')\n  t.deepEqual(fsm.allStates(), [ 'none', 'A', 'B', 'C', 'D' ])\n  t.deepEqual(fsm.allTransitions(), [ 'init', 'step', 'jump' ])\n  t.is(fsm.goto, undefined)\n\n  logger.clear()\n\n  fsm.jump('C')\n\n  t.is(fsm.state, 'C')\n  t.deepEqual(logger.log, [\n    { event: 'onBeforeTransition', transition: 'jump', from: 'A', to: 'C', current: 'A', args: [ 'C' ] },\n    { event: 'onBeforeJump',       transition: 'jump', from: 'A', to: 'C', current: 'A', args: [ 'C' ] },\n    { event: 'onLeaveState',       transition: 'jump', from: 'A', to: 'C', current: 'A', args: [ 'C' ] },\n    { event: 'onLeaveA',           transition: 'jump', from: 'A', to: 'C', current: 'A', args: [ 'C' ] },\n    { event: 'onTransition',       transition: 'jump', from: 'A', to: 'C', current: 'A', args: [ 'C' ] },\n    { event: 'onEnterState',       transition: 'jump', from: 'A', to: 'C', current: 'C', args: [ 'C' ] },\n    { event: 'onEnterC',           transition: 'jump', from: 'A', to: 'C', current: 'C', args: [ 'C' ] },\n    { event: 'onAfterTransition',  transition: 'jump', from: 'A', to: 'C', current: 'C', args: [ 'C' ] },\n    { event: 'onAfterJump',        transition: 'jump', from: 'A', to: 'C', current: 'C', args: [ 'C' ] }\n  ])\n\n})\n\n//-------------------------------------------------------------------------------------------------\n"
  },
  {
    "path": "test/helpers/lifecycle_logger.js",
    "content": "\nmodule.exports = function() {\n\n  'use strict'\n\n  var entries = [],\n      logger = function(lifecycle) {\n        var entry = {\n              event:      lifecycle.event,\n              transition: lifecycle.transition,\n              from:       lifecycle.from,\n              to:         lifecycle.to,\n              current:    lifecycle.fsm.state\n            }\n        if (arguments.length > 1)\n          entry.args = [].slice.call(arguments, 1);\n        entries.push(entry);\n      };\n\n  logger.clear = function() {\n    entries.length = 0;\n  }\n\n  logger.log = entries;\n\n  return logger;\n}\n"
  },
  {
    "path": "test/introspection.js",
    "content": "import test         from 'ava';\nimport StateMachine from '../src/app';\n\n//-----------------------------------------------------------------------------\n\ntest('is', t => {\n\n  var fsm = new StateMachine({\n    init: 'green',\n    transitions: [\n      { name: 'warn',  from: 'green',  to: 'yellow' },\n      { name: 'panic', from: 'yellow', to: 'red'    },\n      { name: 'calm',  from: 'red',    to: 'yellow' },\n      { name: 'clear', from: 'yellow', to: 'green'  }\n  ]});\n\n  t.is(fsm.state, 'green')\n\n  t.is(fsm.is('green'),           true)\n  t.is(fsm.is('yellow'),          false)\n  t.is(fsm.is(['green',  'red']), true,   'current state should match when included in array')\n  t.is(fsm.is(['yellow', 'red']), false,  'current state should NOT match when not included in array')\n\n  fsm.warn()\n\n  t.is(fsm.state, 'yellow')\n  t.is(fsm.is('green'),           false)\n  t.is(fsm.is('yellow'),          true)\n  t.is(fsm.is(['green',  'red']), false, 'current state should NOT match when not included in array')\n  t.is(fsm.is(['yellow', 'red']), true,  'current state should match when included in array')\n\n});\n\n//-----------------------------------------------------------------------------\n\ntest('can & cannot', t => {\n\n  var fsm = new StateMachine({\n    init: 'green',\n    transitions: [\n      { name: 'warn',  from: 'green',  to: 'yellow' },\n      { name: 'panic', from: 'yellow', to: 'red'    },\n      { name: 'calm',  from: 'red',    to: 'yellow' },\n    ]\n  });\n\n  t.is(fsm.state, 'green')\n  t.is(fsm.can('warn'),     true)\n  t.is(fsm.can('panic'),    false)\n  t.is(fsm.can('calm'),     false)\n  t.is(fsm.cannot('warn'),  false)\n  t.is(fsm.cannot('panic'), true)\n  t.is(fsm.cannot('calm'),  true)\n\n  fsm.warn();\n  t.is(fsm.state, 'yellow')\n  t.is(fsm.can('warn'),     false)\n  t.is(fsm.can('panic'),    true)\n  t.is(fsm.can('calm'),     false)\n  t.is(fsm.cannot('warn'),  true)\n  t.is(fsm.cannot('panic'), false)\n  t.is(fsm.cannot('calm'),  true)\n\n  fsm.panic();\n  t.is(fsm.state, 'red')\n  t.is(fsm.can('warn'),     false)\n  t.is(fsm.can('panic'),    false)\n  t.is(fsm.can('calm'),     true)\n  t.is(fsm.cannot('warn'),  true)\n  t.is(fsm.cannot('panic'), true)\n  t.is(fsm.cannot('calm'),  false)\n\n  t.is(fsm.can('jibber'),    false, \"unknown event should not crash\")\n  t.is(fsm.cannot('jabber'), true,  \"unknown event should not crash\")\n\n});\n\n//-----------------------------------------------------------------------------\n\ntest('can is always false during lifecycle events', t => {\n\n  t.plan(81);\n\n  var fsm = new StateMachine({\n    init: 'green',\n    transitions: [\n      { name: 'warn',  from: 'green',  to: 'yellow' },\n      { name: 'panic', from: 'yellow', to: 'red'    },\n      { name: 'calm',  from: 'red',    to: 'yellow' },\n    ],\n    methods: {\n      assertTransitionsNotAllowed: function() {\n        t.false(this.can('warn'))\n        t.false(this.can('panic'))\n        t.false(this.can('calm'))\n      },\n      onBeforeTransition: function() { this.assertTransitionsNotAllowed(); },\n      onBeforeWarn:       function() { this.assertTransitionsNotAllowed(); },\n      onBeforePanic:      function() { this.assertTransitionsNotAllowed(); },\n      onBeforeCalm:       function() { this.assertTransitionsNotAllowed(); },\n      onLeaveState:       function() { this.assertTransitionsNotAllowed(); },\n      onLeaveNone:        function() { this.assertTransitionsNotAllowed(); },\n      onLeaveGreen:       function() { this.assertTransitionsNotAllowed(); },\n      onLeaveYellow:      function() { this.assertTransitionsNotAllowed(); },\n      onLeaveRed:         function() { this.assertTransitionsNotAllowed(); },\n      onTransition:       function() { this.assertTransitionsNotAllowed(); },\n      onEnterState:       function() { this.assertTransitionsNotAllowed(); },\n      onEnterNone:        function() { this.assertTransitionsNotAllowed(); },\n      onEnterGreen:       function() { this.assertTransitionsNotAllowed(); },\n      onEnterYellow:      function() { this.assertTransitionsNotAllowed(); },\n      onEnterRed:         function() { this.assertTransitionsNotAllowed(); },\n      onAfterTransition:  function() { this.assertTransitionsNotAllowed(); },\n      onAfterInit:        function() { this.assertTransitionsNotAllowed(); },\n      onAfterWarn:        function() { this.assertTransitionsNotAllowed(); },\n      onAfterPanic:       function() { this.assertTransitionsNotAllowed(); },\n      onAfterCalm:        function() { this.assertTransitionsNotAllowed(); }\n    }\n  });\n\n  t.is(fsm.state, 'green')\n  fsm.warn()\n  t.is(fsm.state, 'yellow')\n  fsm.panic()\n  t.is(fsm.state, 'red')\n\n});\n\n//-----------------------------------------------------------------------------\n\ntest('all states', t => {\n\n  var fsm = new StateMachine({\n    init: 'green',\n    transitions: [\n      { name: 'warn',   from: 'green',  to: 'yellow' },\n      { name: 'panic',  from: 'yellow', to: 'red'    },\n      { name: 'calm',   from: 'red',    to: 'yellow' },\n      { name: 'clear',  from: 'yellow', to: 'green'  },\n      { name: 'finish', from: 'green',  to: 'done'   },\n  ]});\n\n  t.deepEqual(fsm.allStates(), [ 'none', 'green', 'yellow', 'red', 'done' ]);\n\n});\n\n//-----------------------------------------------------------------------------\n\ntest(\"all transitions\", t => {\n\n  var fsm = new StateMachine({\n    init: 'green',\n    transitions: [\n      { name: 'warn',   from: 'green',  to: 'yellow' },\n      { name: 'panic',  from: 'yellow', to: 'red'    },\n      { name: 'calm',   from: 'red',    to: 'yellow' },\n      { name: 'clear',  from: 'yellow', to: 'green'  },\n      { name: 'finish', from: 'green',  to: 'done'   },\n  ]});\n\n  t.deepEqual(fsm.allTransitions(), [\n    'init', 'warn', 'panic', 'calm', 'clear', 'finish'\n  ]);\n})\n\n//-----------------------------------------------------------------------------\n\ntest(\"valid transitions\", t => {\n\n  var fsm = new StateMachine({\n    init: 'green',\n    transitions: [\n      { name: 'warn',   from: 'green',  to: 'yellow' },\n      { name: 'panic',  from: 'yellow', to: 'red'    },\n      { name: 'calm',   from: 'red',    to: 'yellow' },\n      { name: 'clear',  from: 'yellow', to: 'green'  },\n      { name: 'finish', from: 'green',  to: 'done'   },\n  ]});\n\n  t.is(fsm.state, 'green')\n  t.deepEqual(fsm.transitions(), ['warn', 'finish'])\n\n  fsm.warn();\n  t.is(fsm.state, 'yellow')\n  t.deepEqual(fsm.transitions(), ['panic', 'clear'])\n\n  fsm.panic();\n  t.is(fsm.state, 'red')\n  t.deepEqual(fsm.transitions(), ['calm'])\n\n  fsm.calm();\n  t.is(fsm.state, 'yellow')\n  t.deepEqual(fsm.transitions(), ['panic', 'clear'])\n\n  fsm.clear();\n  t.is(fsm.state, 'green')\n  t.deepEqual(fsm.transitions(), ['warn', 'finish'])\n\n  fsm.finish();\n  t.is(fsm.state, 'done')\n  t.deepEqual(fsm.transitions(), [])\n\n});\n\n//-----------------------------------------------------------------------------\n"
  },
  {
    "path": "test/issues.js",
    "content": "import test            from 'ava'\nimport StateMachine    from '../src/app'\nimport LifecycleLogger from './helpers/lifecycle_logger'\n\n//-------------------------------------------------------------------------------------------------\n\ntest('github issue #12 - transition return values', t => {\n\n  var fsm = new StateMachine({\n    transitions: [\n      { name: 'init',      from: 'none', to: 'A' },\n      { name: 'cancelled', from: 'A',    to: 'X' },\n      { name: 'async',     from: 'A',    to: 'X' }\n    ],\n    methods: {\n      onBeforeCancelled: function() { return false; },\n      onBeforeAsync:     function() { return new Promise(function(resolve, reject) {}); }\n    }\n  });\n\n  t.is(fsm.init(),      true,  'successful (synchronous) transition returns true')\n  t.is(fsm.cancelled(), false, 'cancelled (synchronous) transition returns true')\n\n  var promise = fsm.async();\n  t.is(typeof promise.then, 'function', 'asynchronous transition returns a promise');\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('github issue #17 - exceptions in lifecycle events are NOT swallowed', t => {\n\n  var fsm = new StateMachine({\n              transitions: [\n                { name: 'step', from: 'none', to: 'complete' }\n              ],\n              methods: {\n                onTransition: function() { throw Error('oops') }\n              }\n            });\n\n  t.is(fsm.state, 'none')\n\n  const error = t.throws(() => {\n    fsm.step();\n  })\n\n  t.is(error.message, 'oops')\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('github issue #19 - lifecycle events have correct this when applying StateMachine to a custom class', t => {\n\n  var FSM = function() {\n    this.stepped = false;\n    this._fsm();\n  }\n\n  FSM.prototype.onStep = function(lifecycle) { this.stepped = true }\n\n  StateMachine.factory(FSM, {\n    transitions: [\n      { name: 'step', from: 'none', to: 'complete' }\n    ]\n  })\n\n  var a = new FSM(),\n      b = new FSM();\n\n  t.is(a.state, 'none')\n  t.is(b.state, 'none')\n  t.is(a.stepped, false)\n  t.is(b.stepped, false)\n\n  a.step();\n\n  t.is(a.state, 'complete')\n  t.is(b.state, 'none')\n  t.is(a.stepped, true)\n  t.is(b.stepped, false)\n\n});\n\n//-------------------------------------------------------------------------------------------------\n\ntest('github issue #64 - double wildcard transition does not change state', t => {\n\n  var fsm = new StateMachine({\n    transitions: [\n      { name: 'step', from: '*' /* no-op */ }\n    ]\n  });\n\n  t.is(fsm.state, 'none')\n\n  fsm.step(); t.is(fsm.state, 'none')\n  fsm.step(); t.is(fsm.state, 'none')\n\n})\n\n//-------------------------------------------------------------------------------------------------\n"
  },
  {
    "path": "test/lifecycle.js",
    "content": "import test            from 'ava'\nimport StateMachine    from '../src/app'\nimport LifecycleLogger from './helpers/lifecycle_logger'\n\n//-------------------------------------------------------------------------------------------------\n\ntest('lifecycle events occur in correct order', t => {\n\n  var logger = new LifecycleLogger(),\n      fsm = new StateMachine({\n        transitions: [\n          { name: 'step', from: 'none',  to: 'complete' }\n        ],\n        methods: {\n          onBeforeTransition: logger,\n          onBeforeStep:       logger,\n          onLeaveState:       logger,\n          onLeaveNone:        logger,\n          onLeaveComplete:    logger,\n          onTransition:       logger,\n          onEnterState:       logger,\n          onEnterNone:        logger,\n          onEnterComplete:    logger,\n          onAfterTransition:  logger,\n          onAfterStep:        logger\n        }\n      });\n\n  t.is(fsm.state, 'none')\n\n  fsm.step()\n\n  t.is(fsm.state, 'complete')\n  t.deepEqual(logger.log, [\n    { event: 'onBeforeTransition', transition: 'step', from: 'none', to: 'complete', current: 'none'     },\n    { event: 'onBeforeStep',       transition: 'step', from: 'none', to: 'complete', current: 'none'     },\n    { event: 'onLeaveState',       transition: 'step', from: 'none', to: 'complete', current: 'none'     },\n    { event: 'onLeaveNone',        transition: 'step', from: 'none', to: 'complete', current: 'none'     },\n    { event: 'onTransition',       transition: 'step', from: 'none', to: 'complete', current: 'none'     },\n    { event: 'onEnterState',       transition: 'step', from: 'none', to: 'complete', current: 'complete' },\n    { event: 'onEnterComplete',    transition: 'step', from: 'none', to: 'complete', current: 'complete' },\n    { event: 'onAfterTransition',  transition: 'step', from: 'none', to: 'complete', current: 'complete' },\n    { event: 'onAfterStep',        transition: 'step', from: 'none', to: 'complete', current: 'complete' }\n  ])\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('lifecycle events occur in correct order - for same state transition', t => {\n\n  var logger = new LifecycleLogger(),\n      fsm = new StateMachine({\n        transitions: [\n          { name: 'noop', from: 'none',  to: 'none' }\n        ],\n        methods: {\n          onBeforeTransition: logger,\n          onBeforeNoop:       logger,\n          onLeaveState:       logger,\n          onLeaveNone:        logger,\n          onLeaveComplete:    logger,\n          onTransition:       logger,\n          onEnterState:       logger,\n          onEnterNone:        logger,\n          onEnterComplete:    logger,\n          onAfterTransition:  logger,\n          onAfterNoop:        logger\n        }\n      });\n\n  t.is(fsm.state, 'none')\n\n  fsm.noop()\n\n  t.is(fsm.state, 'none')\n  t.deepEqual(logger.log, [\n    { event: 'onBeforeTransition', transition: 'noop', from: 'none', to: 'none', current: 'none' },\n    { event: 'onBeforeNoop',       transition: 'noop', from: 'none', to: 'none', current: 'none' },\n    { event: 'onTransition',       transition: 'noop', from: 'none', to: 'none', current: 'none' },\n    { event: 'onAfterTransition',  transition: 'noop', from: 'none', to: 'none', current: 'none' },\n    { event: 'onAfterNoop',        transition: 'noop', from: 'none', to: 'none', current: 'none' }\n  ])\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('lifecycle events using shortcut names', t => {\n\n  var logger = new LifecycleLogger(),\n      fsm = new StateMachine({\n        init: 'solid',\n        transitions: [\n          { name: 'melt',     from: 'solid',  to: 'liquid' },\n          { name: 'freeze',   from: 'liquid', to: 'solid'  },\n          { name: 'vaporize', from: 'liquid', to: 'gas'    },\n          { name: 'condense', from: 'gas',    to: 'liquid' }\n        ],\n        methods: {\n          onNone:     logger,\n          onSolid:    logger,\n          onLiquid:   logger,\n          onGas:      logger,\n          onInit:     logger,\n          onMelt:     logger,\n          onFreeze:   logger,\n          onVaporize: logger,\n          onCondense: logger\n        }\n      });\n\n  t.is(fsm.state, 'solid')\n\n  t.deepEqual(logger.log, [\n    { event: \"onSolid\", transition: \"init\", from: \"none\", to: \"solid\", current: \"solid\" },\n    { event: \"onInit\",  transition: \"init\", from: \"none\", to: \"solid\", current: \"solid\" }\n  ])\n\n  logger.clear()\n  fsm.melt()\n  t.is(fsm.state, 'liquid')\n\n  t.deepEqual(logger.log, [\n    { event: \"onLiquid\", transition: \"melt\", from: \"solid\", to: \"liquid\", current: \"liquid\" },\n    { event: \"onMelt\",   transition: \"melt\", from: \"solid\", to: \"liquid\", current: \"liquid\" }\n  ]);\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('lifecycle events with dash or underscore are camelized', t => {\n\n  var logger = new LifecycleLogger(),\n      fsm = new StateMachine({\n        init: 'has-dash',\n        transitions: [\n          { name: 'do-with-dash',       from: 'has-dash',         to: 'has_underscore' },\n          { name: 'do_with_underscore', from: 'has_underscore',   to: 'alreadyCamelized' },\n          { name: 'doAlreadyCamelized', from: 'alreadyCamelized', to: 'has-dash' }\n        ],\n        methods: {\n          onBeforeTransition:         logger,\n          onBeforeInit:               logger,\n          onBeforeDoWithDash:         logger,\n          onBeforeDoWithUnderscore:   logger,\n          onBeforeDoAlreadyCamelized: logger,\n          onLeaveState:               logger,\n          onLeaveNone:                logger,\n          onLeaveHasDash:             logger,\n          onLeaveHasUnderscore:       logger,\n          onLeaveAlreadyCamelized:    logger,\n          onTransition:               logger,\n          onEnterState:               logger,\n          onEnterNone:                logger,\n          onEnterHasDash:             logger,\n          onEnterHasUnderscore:       logger,\n          onEnterAlreadyCamelized:    logger,\n          onAfterTransition:          logger,\n          onAfterInit:                logger,\n          onAfterDoWithDash:          logger,\n          onAfterDoWithUnderscore:    logger,\n          onAfterDoAlreadyCamelized:  logger\n        }\n      });\n\n  t.is(fsm.state, 'has-dash')\n  t.deepEqual(logger.log, [\n    { event: 'onBeforeTransition', transition: 'init', from: 'none', to: 'has-dash', current: 'none'     },\n    { event: 'onBeforeInit',       transition: 'init', from: 'none', to: 'has-dash', current: 'none'     },\n    { event: 'onLeaveState',       transition: 'init', from: 'none', to: 'has-dash', current: 'none'     },\n    { event: 'onLeaveNone',        transition: 'init', from: 'none', to: 'has-dash', current: 'none'     },\n    { event: 'onTransition',       transition: 'init', from: 'none', to: 'has-dash', current: 'none'     },\n    { event: 'onEnterState',       transition: 'init', from: 'none', to: 'has-dash', current: 'has-dash' },\n    { event: 'onEnterHasDash',     transition: 'init', from: 'none', to: 'has-dash', current: 'has-dash' },\n    { event: 'onAfterTransition',  transition: 'init', from: 'none', to: 'has-dash', current: 'has-dash' },\n    { event: 'onAfterInit',        transition: 'init', from: 'none', to: 'has-dash', current: 'has-dash' }\n  ])\n\n  logger.clear()\n  fsm.doWithDash()\n  t.is(fsm.state, 'has_underscore')\n  t.deepEqual(logger.log, [\n    { event: 'onBeforeTransition',   transition: 'do-with-dash', from: 'has-dash', to: 'has_underscore', current: 'has-dash'       },\n    { event: 'onBeforeDoWithDash',   transition: 'do-with-dash', from: 'has-dash', to: 'has_underscore', current: 'has-dash'       },\n    { event: 'onLeaveState',         transition: 'do-with-dash', from: 'has-dash', to: 'has_underscore', current: 'has-dash'       },\n    { event: 'onLeaveHasDash',       transition: 'do-with-dash', from: 'has-dash', to: 'has_underscore', current: 'has-dash'       },\n    { event: 'onTransition',         transition: 'do-with-dash', from: 'has-dash', to: 'has_underscore', current: 'has-dash'       },\n    { event: 'onEnterState',         transition: 'do-with-dash', from: 'has-dash', to: 'has_underscore', current: 'has_underscore' },\n    { event: 'onEnterHasUnderscore', transition: 'do-with-dash', from: 'has-dash', to: 'has_underscore', current: 'has_underscore' },\n    { event: 'onAfterTransition',    transition: 'do-with-dash', from: 'has-dash', to: 'has_underscore', current: 'has_underscore' },\n    { event: 'onAfterDoWithDash',    transition: 'do-with-dash', from: 'has-dash', to: 'has_underscore', current: 'has_underscore' }\n  ])\n\n  logger.clear()\n  fsm.doWithUnderscore()\n  t.is(fsm.state, 'alreadyCamelized')\n  t.deepEqual(logger.log, [\n    { event: 'onBeforeTransition',       transition: 'do_with_underscore', from: 'has_underscore', to: 'alreadyCamelized', current: 'has_underscore'   },\n    { event: 'onBeforeDoWithUnderscore', transition: 'do_with_underscore', from: 'has_underscore', to: 'alreadyCamelized', current: 'has_underscore'   },\n    { event: 'onLeaveState',             transition: 'do_with_underscore', from: 'has_underscore', to: 'alreadyCamelized', current: 'has_underscore'   },\n    { event: 'onLeaveHasUnderscore',     transition: 'do_with_underscore', from: 'has_underscore', to: 'alreadyCamelized', current: 'has_underscore'   },\n    { event: 'onTransition',             transition: 'do_with_underscore', from: 'has_underscore', to: 'alreadyCamelized', current: 'has_underscore'   },\n    { event: 'onEnterState',             transition: 'do_with_underscore', from: 'has_underscore', to: 'alreadyCamelized', current: 'alreadyCamelized' },\n    { event: 'onEnterAlreadyCamelized',  transition: 'do_with_underscore', from: 'has_underscore', to: 'alreadyCamelized', current: 'alreadyCamelized' },\n    { event: 'onAfterTransition',        transition: 'do_with_underscore', from: 'has_underscore', to: 'alreadyCamelized', current: 'alreadyCamelized' },\n    { event: 'onAfterDoWithUnderscore',  transition: 'do_with_underscore', from: 'has_underscore', to: 'alreadyCamelized', current: 'alreadyCamelized' }\n  ])\n\n  logger.clear()\n  fsm.doAlreadyCamelized()\n  t.is(fsm.state, 'has-dash')\n  t.deepEqual(logger.log, [\n    { event: 'onBeforeTransition',         transition: 'doAlreadyCamelized', from: 'alreadyCamelized', to: 'has-dash', current: 'alreadyCamelized' },\n    { event: 'onBeforeDoAlreadyCamelized', transition: 'doAlreadyCamelized', from: 'alreadyCamelized', to: 'has-dash', current: 'alreadyCamelized' },\n    { event: 'onLeaveState',               transition: 'doAlreadyCamelized', from: 'alreadyCamelized', to: 'has-dash', current: 'alreadyCamelized' },\n    { event: 'onLeaveAlreadyCamelized',    transition: 'doAlreadyCamelized', from: 'alreadyCamelized', to: 'has-dash', current: 'alreadyCamelized' },\n    { event: 'onTransition',               transition: 'doAlreadyCamelized', from: 'alreadyCamelized', to: 'has-dash', current: 'alreadyCamelized' },\n    { event: 'onEnterState',               transition: 'doAlreadyCamelized', from: 'alreadyCamelized', to: 'has-dash', current: 'has-dash'         },\n    { event: 'onEnterHasDash',             transition: 'doAlreadyCamelized', from: 'alreadyCamelized', to: 'has-dash', current: 'has-dash'         },\n    { event: 'onAfterTransition',          transition: 'doAlreadyCamelized', from: 'alreadyCamelized', to: 'has-dash', current: 'has-dash'         },\n    { event: 'onAfterDoAlreadyCamelized',  transition: 'doAlreadyCamelized', from: 'alreadyCamelized', to: 'has-dash', current: 'has-dash'         }\n  ])\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('lifecycle event names that are all uppercase are camelized', t => {\n\n  var logger = new LifecycleLogger(),\n      fsm = new StateMachine({\n        init: 'FIRST',\n        transitions: [\n          { name: 'GO',    from: 'FIRST',        to: 'SECOND_STATE' },\n          { name: 'DO_IT', from: 'SECOND_STATE', to: 'FIRST'        }\n        ],\n        methods: {\n          onBeforeGo:         logger,\n          onBeforeDoIt:       logger,\n          onLeaveFirst:       logger,\n          onLeaveSecondState: logger,\n          onEnterFirst:       logger,\n          onEnterSecondState: logger,\n          onAfterGo:          logger,\n          onAfterDoIt:        logger\n        }\n      });\n\n  t.is(fsm.state, 'FIRST')\n  t.deepEqual(logger.log, [\n    { event: 'onEnterFirst', transition: 'init', from: 'none', to: 'FIRST', current: 'FIRST' },\n  ])\n\n  logger.clear()\n  fsm.go()\n  t.is(fsm.state, 'SECOND_STATE')\n  t.deepEqual(logger.log, [\n    { event: 'onBeforeGo',         transition: 'GO', from: 'FIRST', to: 'SECOND_STATE', current: 'FIRST'        },\n    { event: 'onLeaveFirst',       transition: 'GO', from: 'FIRST', to: 'SECOND_STATE', current: 'FIRST'        },\n    { event: 'onEnterSecondState', transition: 'GO', from: 'FIRST', to: 'SECOND_STATE', current: 'SECOND_STATE' },\n    { event: 'onAfterGo',          transition: 'GO', from: 'FIRST', to: 'SECOND_STATE', current: 'SECOND_STATE' }\n  ])\n\n  logger.clear();\n  fsm.doIt();\n  t.is(fsm.state, 'FIRST')\n  t.deepEqual(logger.log, [\n    { event: 'onBeforeDoIt',       transition: 'DO_IT', from: 'SECOND_STATE', to: 'FIRST', current: 'SECOND_STATE' },\n    { event: 'onLeaveSecondState', transition: 'DO_IT', from: 'SECOND_STATE', to: 'FIRST', current: 'SECOND_STATE' },\n    { event: 'onEnterFirst',       transition: 'DO_IT', from: 'SECOND_STATE', to: 'FIRST', current: 'FIRST'        },\n    { event: 'onAfterDoIt',        transition: 'DO_IT', from: 'SECOND_STATE', to: 'FIRST', current: 'FIRST'        }\n  ])\n\n});\n\n//-------------------------------------------------------------------------------------------------\n\ntest('lifecycle events receive arbitrary transition arguments', t => {\n\n  var logger = new LifecycleLogger(),\n      fsm = new StateMachine({\n        transitions: [\n          { name: 'init', from: 'none', to: 'A' },\n          { name: 'step', from: 'A',    to: 'B' }\n        ],\n        methods: {\n          onBeforeTransition: logger,\n          onBeforeInit:       logger,\n          onBeforeStep:       logger,\n          onLeaveState:       logger,\n          onLeaveNone:        logger,\n          onLeaveA:           logger,\n          onLeaveB:           logger,\n          onTransition:       logger,\n          onEnterState:       logger,\n          onEnterNone:        logger,\n          onEnterA:           logger,\n          onEnterB:           logger,\n          onAfterTransition:  logger,\n          onAfterInit:        logger,\n          onAfterStep:        logger\n        }\n      });\n\n  t.is(fsm.state, 'none')\n  t.deepEqual(logger.log, [])\n\n  fsm.init()\n\n  t.is(fsm.state, 'A')\n  t.deepEqual(logger.log, [\n    { event: 'onBeforeTransition', transition: 'init', from: 'none', to: 'A', current: 'none' },\n    { event: 'onBeforeInit',       transition: 'init', from: 'none', to: 'A', current: 'none' },\n    { event: 'onLeaveState',       transition: 'init', from: 'none', to: 'A', current: 'none' },\n    { event: 'onLeaveNone',        transition: 'init', from: 'none', to: 'A', current: 'none' },\n    { event: 'onTransition',       transition: 'init', from: 'none', to: 'A', current: 'none' },\n    { event: 'onEnterState',       transition: 'init', from: 'none', to: 'A', current: 'A'    },\n    { event: 'onEnterA',           transition: 'init', from: 'none', to: 'A', current: 'A'    },\n    { event: 'onAfterTransition',  transition: 'init', from: 'none', to: 'A', current: 'A'    },\n    { event: 'onAfterInit',        transition: 'init', from: 'none', to: 'A', current: 'A'    }\n  ])\n  logger.clear()\n\n  fsm.step('with', 4, 'more', 'arguments')\n\n  t.is(fsm.state, 'B')\n  t.deepEqual(logger.log, [\n    { event: 'onBeforeTransition', transition: 'step', from: 'A', to: 'B', current: 'A', args: [ 'with', 4, 'more', 'arguments' ] },\n    { event: 'onBeforeStep',       transition: 'step', from: 'A', to: 'B', current: 'A', args: [ 'with', 4, 'more', 'arguments' ] },\n    { event: 'onLeaveState',       transition: 'step', from: 'A', to: 'B', current: 'A', args: [ 'with', 4, 'more', 'arguments' ] },\n    { event: 'onLeaveA',           transition: 'step', from: 'A', to: 'B', current: 'A', args: [ 'with', 4, 'more', 'arguments' ] },\n    { event: 'onTransition',       transition: 'step', from: 'A', to: 'B', current: 'A', args: [ 'with', 4, 'more', 'arguments' ] },\n    { event: 'onEnterState',       transition: 'step', from: 'A', to: 'B', current: 'B', args: [ 'with', 4, 'more', 'arguments' ] },\n    { event: 'onEnterB',           transition: 'step', from: 'A', to: 'B', current: 'B', args: [ 'with', 4, 'more', 'arguments' ] },\n    { event: 'onAfterTransition',  transition: 'step', from: 'A', to: 'B', current: 'B', args: [ 'with', 4, 'more', 'arguments' ] },\n    { event: 'onAfterStep',        transition: 'step', from: 'A', to: 'B', current: 'B', args: [ 'with', 4, 'more', 'arguments' ] }\n  ])\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('lifecycle events are cancelable', t => {\n\n  var FSM = StateMachine.factory({\n    transitions: [\n      { name: 'step', from: 'none', to: 'complete' }\n    ],\n    data: function(cancel) {\n      return {\n        cancel: cancel,\n        logger: new LifecycleLogger()\n      }\n    },\n    methods: {\n      onBeforeTransition: function(lifecycle) { this.logger(lifecycle); return lifecycle.event !== this.cancel },\n      onBeforeStep:       function(lifecycle) { this.logger(lifecycle); return lifecycle.event !== this.cancel },\n      onEnterState:       function(lifecycle) { this.logger(lifecycle); return lifecycle.event !== this.cancel },\n      onEnterNone:        function(lifecycle) { this.logger(lifecycle); return lifecycle.event !== this.cancel },\n      onEnterComplete:    function(lifecycle) { this.logger(lifecycle); return lifecycle.event !== this.cancel },\n      onTransition:       function(lifecycle) { this.logger(lifecycle); return lifecycle.event !== this.cancel },\n      onLeaveState:       function(lifecycle) { this.logger(lifecycle); return lifecycle.event !== this.cancel },\n      onLeaveNone:        function(lifecycle) { this.logger(lifecycle); return lifecycle.event !== this.cancel },\n      onLeaveComplete:    function(lifecycle) { this.logger(lifecycle); return lifecycle.event !== this.cancel },\n      onAfterTransition:  function(lifecycle) { this.logger(lifecycle); return lifecycle.event !== this.cancel },\n      onAfterStep:        function(lifecycle) { this.logger(lifecycle); return lifecycle.event !== this.cancel }\n    }\n  });\n\n  var cancelledBeforeTransition = new FSM('onBeforeTransition'),\n      cancelledBeforeStep       = new FSM('onBeforeStep'),\n      cancelledLeaveState       = new FSM('onLeaveState'),\n      cancelledLeaveNone        = new FSM('onLeaveNone'),\n      cancelledTransition       = new FSM('onTransition'),\n      cancelledEnterState       = new FSM('onEnterState'),\n      cancelledEnterComplete    = new FSM('onEnterComplete'),\n      cancelledAfterTransition  = new FSM('onAfterTransition'),\n      cancelledAfterStep        = new FSM('onAfterStep');\n\n  t.is(cancelledBeforeTransition.state, 'none')\n  t.is(cancelledBeforeStep.state,       'none')\n  t.is(cancelledLeaveState.state,       'none')\n  t.is(cancelledLeaveNone.state,        'none')\n  t.is(cancelledTransition.state,       'none')\n  t.is(cancelledEnterState.state,       'none')\n  t.is(cancelledEnterComplete.state,    'none')\n  t.is(cancelledAfterTransition.state,  'none')\n  t.is(cancelledAfterStep.state,        'none')\n\n  cancelledBeforeTransition.step()\n  cancelledBeforeStep.step()\n  cancelledLeaveState.step()\n  cancelledLeaveNone.step()\n  cancelledTransition.step()\n  cancelledEnterState.step()\n  cancelledEnterComplete.step()\n  cancelledAfterTransition.step()\n  cancelledAfterStep.step()\n\n  t.is(cancelledBeforeTransition.state, 'none')\n  t.is(cancelledBeforeStep.state,       'none')\n  t.is(cancelledLeaveState.state,       'none')\n  t.is(cancelledLeaveNone.state,        'none')\n  t.is(cancelledTransition.state,       'none')\n  t.is(cancelledEnterState.state,       'complete')\n  t.is(cancelledEnterComplete.state,    'complete')\n  t.is(cancelledAfterTransition.state,  'complete')\n  t.is(cancelledAfterStep.state,        'complete')\n\n  t.deepEqual(cancelledBeforeTransition.logger.log, [\n    { event: 'onBeforeTransition', transition: 'step', from: 'none', to: 'complete', current: 'none' }\n  ])\n\n  t.deepEqual(cancelledBeforeStep.logger.log, [\n    { event: 'onBeforeTransition', transition: 'step', from: 'none', to: 'complete', current: 'none' },\n    { event: 'onBeforeStep',       transition: 'step', from: 'none', to: 'complete', current: 'none' }\n  ])\n\n  t.deepEqual(cancelledLeaveState.logger.log, [\n    { event: 'onBeforeTransition', transition: 'step', from: 'none', to: 'complete', current: 'none' },\n    { event: 'onBeforeStep',       transition: 'step', from: 'none', to: 'complete', current: 'none' },\n    { event: 'onLeaveState',       transition: 'step', from: 'none', to: 'complete', current: 'none' }\n  ])\n\n  t.deepEqual(cancelledLeaveNone.logger.log, [\n    { event: 'onBeforeTransition', transition: 'step', from: 'none', to: 'complete', current: 'none' },\n    { event: 'onBeforeStep',       transition: 'step', from: 'none', to: 'complete', current: 'none' },\n    { event: 'onLeaveState',       transition: 'step', from: 'none', to: 'complete', current: 'none' },\n    { event: 'onLeaveNone',        transition: 'step', from: 'none', to: 'complete', current: 'none' }\n  ])\n\n  t.deepEqual(cancelledTransition.logger.log, [\n    { event: 'onBeforeTransition', transition: 'step', from: 'none', to: 'complete', current: 'none' },\n    { event: 'onBeforeStep',       transition: 'step', from: 'none', to: 'complete', current: 'none' },\n    { event: 'onLeaveState',       transition: 'step', from: 'none', to: 'complete', current: 'none' },\n    { event: 'onLeaveNone',        transition: 'step', from: 'none', to: 'complete', current: 'none' },\n    { event: 'onTransition',       transition: 'step', from: 'none', to: 'complete', current: 'none' }\n  ])\n\n  t.deepEqual(cancelledEnterState.logger.log, [\n    { event: 'onBeforeTransition', transition: 'step', from: 'none', to: 'complete', current: 'none' },\n    { event: 'onBeforeStep',       transition: 'step', from: 'none', to: 'complete', current: 'none' },\n    { event: 'onLeaveState',       transition: 'step', from: 'none', to: 'complete', current: 'none' },\n    { event: 'onLeaveNone',        transition: 'step', from: 'none', to: 'complete', current: 'none' },\n    { event: 'onTransition',       transition: 'step', from: 'none', to: 'complete', current: 'none' },\n    { event: 'onEnterState',       transition: 'step', from: 'none', to: 'complete', current: 'complete' }\n  ])\n\n  t.deepEqual(cancelledEnterComplete.logger.log, [\n    { event: 'onBeforeTransition', transition: 'step', from: 'none', to: 'complete', current: 'none' },\n    { event: 'onBeforeStep',       transition: 'step', from: 'none', to: 'complete', current: 'none' },\n    { event: 'onLeaveState',       transition: 'step', from: 'none', to: 'complete', current: 'none' },\n    { event: 'onLeaveNone',        transition: 'step', from: 'none', to: 'complete', current: 'none' },\n    { event: 'onTransition',       transition: 'step', from: 'none', to: 'complete', current: 'none' },\n    { event: 'onEnterState',       transition: 'step', from: 'none', to: 'complete', current: 'complete' },\n    { event: 'onEnterComplete',    transition: 'step', from: 'none', to: 'complete', current: 'complete' }\n  ])\n\n  t.deepEqual(cancelledAfterTransition.logger.log, [\n    { event: 'onBeforeTransition', transition: 'step', from: 'none', to: 'complete', current: 'none' },\n    { event: 'onBeforeStep',       transition: 'step', from: 'none', to: 'complete', current: 'none' },\n    { event: 'onLeaveState',       transition: 'step', from: 'none', to: 'complete', current: 'none' },\n    { event: 'onLeaveNone',        transition: 'step', from: 'none', to: 'complete', current: 'none' },\n    { event: 'onTransition',       transition: 'step', from: 'none', to: 'complete', current: 'none' },\n    { event: 'onEnterState',       transition: 'step', from: 'none', to: 'complete', current: 'complete' },\n    { event: 'onEnterComplete',    transition: 'step', from: 'none', to: 'complete', current: 'complete' },\n    { event: 'onAfterTransition',  transition: 'step', from: 'none', to: 'complete', current: 'complete' }\n  ])\n\n  t.deepEqual(cancelledAfterStep.logger.log, [\n    { event: 'onBeforeTransition', transition: 'step', from: 'none', to: 'complete', current: 'none' },\n    { event: 'onBeforeStep',       transition: 'step', from: 'none', to: 'complete', current: 'none' },\n    { event: 'onLeaveState',       transition: 'step', from: 'none', to: 'complete', current: 'none' },\n    { event: 'onLeaveNone',        transition: 'step', from: 'none', to: 'complete', current: 'none' },\n    { event: 'onTransition',       transition: 'step', from: 'none', to: 'complete', current: 'none' },\n    { event: 'onEnterState',       transition: 'step', from: 'none', to: 'complete', current: 'complete' },\n    { event: 'onEnterComplete',    transition: 'step', from: 'none', to: 'complete', current: 'complete' },\n    { event: 'onAfterTransition',  transition: 'step', from: 'none', to: 'complete', current: 'complete' },\n    { event: 'onAfterStep',        transition: 'step', from: 'none', to: 'complete', current: 'complete' }\n  ])\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('lifecycle events can be deferred using a promise', t => {\n  return new Promise(function(resolveTest, rejectTest) {\n\n    var logger = new LifecycleLogger(),\n        start  = Date.now(),\n        pause  = function(ms) { return new Promise(function(resolve, reject) { setTimeout(function() { resolve('resolved') }, ms); }); },\n        fsm    = new StateMachine({\n          transitions: [\n            { name: 'step', from: 'none', to: 'complete' }\n          ],\n          methods: {\n            onBeforeTransition: function(lifecycle, a, b) { logger(lifecycle, a, b); return pause(100); },\n            onBeforeStep:       function(lifecycle, a, b) { logger(lifecycle, a, b); return pause(100); },\n            onEnterState:       function(lifecycle, a, b) { logger(lifecycle, a, b); return pause(100); },\n            onEnterNone:        function(lifecycle, a, b) { logger(lifecycle, a, b); return pause(100); },\n            onEnterComplete:    function(lifecycle, a, b) { logger(lifecycle, a, b); return pause(100); },\n            onTransition:       function(lifecycle, a, b) { logger(lifecycle, a, b); return pause(100); },\n            onLeaveState:       function(lifecycle, a, b) { logger(lifecycle, a, b); return pause(100); },\n            onLeaveNone:        function(lifecycle, a, b) { logger(lifecycle, a, b); return pause(100); },\n            onLeaveComplete:    function(lifecycle, a, b) { logger(lifecycle, a, b); return pause(100); },\n            onAfterTransition:  function(lifecycle, a, b) { logger(lifecycle, a, b); return pause(100); },\n            onAfterStep:        function(lifecycle, a, b) { logger(lifecycle, a, b); return pause(100); }\n          }\n        });\n\n    function done(answer) {\n      var duration = Date.now() - start;\n      t.is(fsm.state, 'complete')\n      t.is(duration > 600, true)\n      t.deepEqual(logger.log, [\n        { event: 'onBeforeTransition', transition: 'step', from: 'none', to: 'complete', current: 'none',     args: [ 'additional', 'arguments' ] },\n        { event: 'onBeforeStep',       transition: 'step', from: 'none', to: 'complete', current: 'none',     args: [ 'additional', 'arguments' ] },\n        { event: 'onLeaveState',       transition: 'step', from: 'none', to: 'complete', current: 'none',     args: [ 'additional', 'arguments' ] },\n        { event: 'onLeaveNone',        transition: 'step', from: 'none', to: 'complete', current: 'none',     args: [ 'additional', 'arguments' ] },\n        { event: 'onTransition',       transition: 'step', from: 'none', to: 'complete', current: 'none',     args: [ 'additional', 'arguments' ] },\n        { event: 'onEnterState',       transition: 'step', from: 'none', to: 'complete', current: 'complete', args: [ 'additional', 'arguments' ] },\n        { event: 'onEnterComplete',    transition: 'step', from: 'none', to: 'complete', current: 'complete', args: [ 'additional', 'arguments' ] },\n        { event: 'onAfterTransition',  transition: 'step', from: 'none', to: 'complete', current: 'complete', args: [ 'additional', 'arguments' ] },\n        { event: 'onAfterStep',        transition: 'step', from: 'none', to: 'complete', current: 'complete', args: [ 'additional', 'arguments' ] },\n      ])\n      t.is(answer, 'resolved');\n      resolveTest()\n    }\n\n    fsm.step('additional', 'arguments')\n       .then(done);\n\n  });\n});\n\n//-------------------------------------------------------------------------------------------------\n\ntest('lifecycle events can be cancelled using a promise', t => {\n  return new Promise(function(resolveTest, rejectTest) {\n\n    var logger = new LifecycleLogger(),\n        start  = Date.now(),\n        pause  = function(ms) {\n          return new Promise(function(resolve, reject) {\n            setTimeout(function() { resolve('resolved') }, ms);\n          });\n        },\n        cancel = function(ms) {\n          return new Promise(function(resolve, reject) {\n            setTimeout(function() { reject('rejected'); }, ms);\n          });\n        },\n        fsm = new StateMachine({\n          transitions: [\n            { name: 'step', from: 'none', to: 'complete' }\n          ],\n          methods: {\n            onBeforeTransition: function(lifecycle, a, b) { logger(lifecycle, a, b); return pause(100);  },\n            onBeforeStep:       function(lifecycle, a, b) { logger(lifecycle, a, b); return pause(100);  },\n            onEnterState:       function(lifecycle, a, b) { logger(lifecycle, a, b); return pause(100);  },\n            onEnterNone:        function(lifecycle, a, b) { logger(lifecycle, a, b); return pause(100);  },\n            onEnterComplete:    function(lifecycle, a, b) { logger(lifecycle, a, b); return pause(100);  },\n            onTransition:       function(lifecycle, a, b) { logger(lifecycle, a, b); return cancel(100); },\n            onLeaveState:       function(lifecycle, a, b) { logger(lifecycle, a, b); },\n            onLeaveNone:        function(lifecycle, a, b) { logger(lifecycle, a, b); },\n            onLeaveComplete:    function(lifecycle, a, b) { logger(lifecycle, a, b); },\n            onAfterTransition:  function(lifecycle, a, b) { logger(lifecycle, a, b); },\n            onAfterStep:        function(lifecycle, a, b) { logger(lifecycle, a, b); }\n          }\n        });\n\n    function done(answer) {\n      var duration = Date.now() - start;\n      t.is(fsm.state, 'none');\n      t.is(duration > 300, true);\n      t.deepEqual(logger.log, [\n        { event: 'onBeforeTransition', transition: 'step', from: 'none', to: 'complete', current: 'none', args: [ 'additional', 'arguments' ] },\n        { event: 'onBeforeStep',       transition: 'step', from: 'none', to: 'complete', current: 'none', args: [ 'additional', 'arguments' ] },\n        { event: 'onLeaveState',       transition: 'step', from: 'none', to: 'complete', current: 'none', args: [ 'additional', 'arguments' ] },\n        { event: 'onLeaveNone',        transition: 'step', from: 'none', to: 'complete', current: 'none', args: [ 'additional', 'arguments' ] },\n        { event: 'onTransition',       transition: 'step', from: 'none', to: 'complete', current: 'none', args: [ 'additional', 'arguments' ] }\n      ]);\n      t.is(answer, 'rejected');\n      resolveTest();\n    }\n\n    fsm.step('additional', 'arguments')\n       .then(function() { done('promise was rejected so this should never happen'); })\n       .catch(done)\n\n  })\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('transition cannot fire while lifecycle event is in progress', t => {\n\n  t.plan(20);\n\n  var fsm = new StateMachine({\n        init: 'A',\n        transitions: [\n          { name: 'step',  from: 'A', to: 'B' },\n          { name: 'other', from: '*', to: 'X' }\n        ],\n        methods: {\n\n          onBeforeStep: function(lifecycle) {\n            t.false(this.can('other'));\n            const error = t.throws(function() {\n              fsm.other();\n            });\n            t.is(error.message, 'transition is invalid while previous transition is still in progress');\n            t.is(error.transition, 'other');\n            t.is(error.from,       'A');\n            t.is(error.to,         'X');\n            t.is(error.current,    'A');\n          },\n\n          onAfterStep: function(lifecycle) {\n            t.false(this.can('other'));\n            const error = t.throws(function() {\n              fsm.other();\n            });\n            t.is(error.message, 'transition is invalid while previous transition is still in progress');\n            t.is(error.transition, 'other');\n            t.is(error.from,       'B');\n            t.is(error.to,         'X');\n            t.is(error.current,    'B');\n          },\n\n          onBeforeOther: function(lifecycle) { t.fail('should never happen') },\n          onAfterOther:  function(lifecycle) { t.fail('should never happen') },\n          onLeaveA:      function(lifecycle) { t.false(this.can('other'))    },\n          onEnterB:      function(lifecycle) { t.false(this.can('other'))    },\n          onLeaveB:      function(lifecycle) { t.fail('should never happen') },\n          onEnterX:      function(lifecycle) { t.fail('should never happen') },\n          onLeaveX:      function(lifecycle) { t.fail('should never happen') }\n\n        }\n      });\n\n  t.is(fsm.state, 'A')\n  t.true(fsm.can('other'))\n\n  fsm.step()\n\n  t.is(fsm.state, 'B')\n  t.true(fsm.can('other'))\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('transition cannot fire while asynchronous lifecycle event is in progress', t => {\n  return new Promise(function(resolveTest, rejectTest) {\n\n    t.plan(20);\n\n    var fsm = new StateMachine({\n          init: 'A',\n          transitions: [\n            { name: 'step',  from: 'A', to: 'B' },\n            { name: 'other', from: '*', to: 'X' }\n          ],\n          methods: {\n\n            onBeforeStep: function(lifecycle) {\n              return new Promise(function(resolve, reject) {\n                setTimeout(function() {\n                  t.false(fsm.can('other'));\n                  const error = t.throws(function() {\n                    fsm.other();\n                  });\n                  t.is(error.message, 'transition is invalid while previous transition is still in progress');\n                  t.is(error.transition, 'other');\n                  t.is(error.from,       'A');\n                  t.is(error.to,         'X');\n                  t.is(error.current,    'A');\n                  resolve();\n                }, 200);\n              });\n            },\n\n            onAfterStep: function(lifecycle) {\n              return new Promise(function(resolve, reject) {\n                setTimeout(function() {\n                  t.false(fsm.can('other'));\n                  const error = t.throws(function() {\n                    fsm.other();\n                  });\n                  t.is(error.message, 'transition is invalid while previous transition is still in progress');\n                  t.is(error.transition, 'other');\n                  t.is(error.from,       'B');\n                  t.is(error.to,         'X');\n                  t.is(error.current,    'B');\n                  resolve();\n                  setTimeout(done, 0); // HACK - let lifecycle finish before calling done()\n                }, 200);\n              });\n            },\n\n            onBeforeOther: function(lifecycle) { t.fail('should never happen') },\n            onAfterOther:  function(lifecycle) { t.fail('should never happen') },\n            onLeaveA:      function(lifecycle) { t.false(this.can('other'))    },\n            onEnterB:      function(lifecycle) { t.false(this.can('other'))    },\n            onLeaveB:      function(lifecycle) { t.fail('should never happen') },\n            onEnterX:      function(lifecycle) { t.fail('should never happen') },\n            onLeaveX:      function(lifecycle) { t.fail('should never happen') }\n\n          }\n        });\n\n    t.is(fsm.state, 'A')\n    t.true(fsm.can('other'))\n\n    function done() {\n      t.is(fsm.state, 'B');\n      t.true(fsm.can('other'));\n      resolveTest();\n    }\n\n    fsm.step(); // kick off the async behavior\n\n  })\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('lifecycle events for transitions with multiple :from or :to states', t => {\n\n  var logger = new LifecycleLogger(),\n      fsm = new StateMachine({\n        init: 'hungry',\n        transitions: [\n          { name: 'eat',  from: 'hungry',                to: 'satisfied' },\n          { name: 'eat',  from: 'satisfied',             to: 'full'      },\n          { name: 'rest', from: [ 'satisfied', 'full' ], to: 'hungry'    }\n        ],\n        methods: {\n          onBeforeTransition: logger,\n          onBeforeEat:        logger,\n          onBeforeRest:       logger,\n          onLeaveState:       logger,\n          onLeaveHungry:      logger,\n          onLeaveSatisfied:   logger,\n          onLeaveFull:        logger,\n          onTransition:       logger,\n          onEnterState:       logger,\n          onEnterHungry:      logger,\n          onEnterSatisfied:   logger,\n          onEnterFull:        logger,\n          onAfterTransition:  logger,\n          onAfterEat:         logger,\n          onAfterRest:        logger\n        }\n      });\n\n\n  t.is(fsm.state, 'hungry')\n  logger.clear()\n\n  fsm.eat()\n  t.is(fsm.state, 'satisfied')\n  t.deepEqual(logger.log, [\n    { event: 'onBeforeTransition', transition: 'eat', from: 'hungry', to: 'satisfied', current: 'hungry' },\n    { event: 'onBeforeEat',        transition: 'eat', from: 'hungry', to: 'satisfied', current: 'hungry' },\n    { event: 'onLeaveState',       transition: 'eat', from: 'hungry', to: 'satisfied', current: 'hungry' },\n    { event: 'onLeaveHungry',      transition: 'eat', from: 'hungry', to: 'satisfied', current: 'hungry' },\n    { event: 'onTransition',       transition: 'eat', from: 'hungry', to: 'satisfied', current: 'hungry' },\n    { event: 'onEnterState',       transition: 'eat', from: 'hungry', to: 'satisfied', current: 'satisfied' },\n    { event: 'onEnterSatisfied',   transition: 'eat', from: 'hungry', to: 'satisfied', current: 'satisfied' },\n    { event: 'onAfterTransition',  transition: 'eat', from: 'hungry', to: 'satisfied', current: 'satisfied' },\n    { event: 'onAfterEat',         transition: 'eat', from: 'hungry', to: 'satisfied', current: 'satisfied' }\n  ])\n\n  logger.clear()\n  fsm.eat()\n  t.is(fsm.state, 'full')\n  t.deepEqual(logger.log, [\n    { event: 'onBeforeTransition', transition: 'eat', from: 'satisfied', to: 'full', current: 'satisfied' },\n    { event: 'onBeforeEat',        transition: 'eat', from: 'satisfied', to: 'full', current: 'satisfied' },\n    { event: 'onLeaveState',       transition: 'eat', from: 'satisfied', to: 'full', current: 'satisfied' },\n    { event: 'onLeaveSatisfied',   transition: 'eat', from: 'satisfied', to: 'full', current: 'satisfied' },\n    { event: 'onTransition',       transition: 'eat', from: 'satisfied', to: 'full', current: 'satisfied' },\n    { event: 'onEnterState',       transition: 'eat', from: 'satisfied', to: 'full', current: 'full' },\n    { event: 'onEnterFull',        transition: 'eat', from: 'satisfied', to: 'full', current: 'full' },\n    { event: 'onAfterTransition',  transition: 'eat', from: 'satisfied', to: 'full', current: 'full' },\n    { event: 'onAfterEat',         transition: 'eat', from: 'satisfied', to: 'full', current: 'full' }\n  ])\n\n  logger.clear()\n  fsm.rest()\n  t.is(fsm.state, 'hungry')\n  t.deepEqual(logger.log, [\n    { event: 'onBeforeTransition', transition: 'rest', from: 'full', to: 'hungry', current: 'full'   },\n    { event: 'onBeforeRest',       transition: 'rest', from: 'full', to: 'hungry', current: 'full'   },\n    { event: 'onLeaveState',       transition: 'rest', from: 'full', to: 'hungry', current: 'full'   },\n    { event: 'onLeaveFull',        transition: 'rest', from: 'full', to: 'hungry', current: 'full'   },\n    { event: 'onTransition',       transition: 'rest', from: 'full', to: 'hungry', current: 'full'   },\n    { event: 'onEnterState',       transition: 'rest', from: 'full', to: 'hungry', current: 'hungry' },\n    { event: 'onEnterHungry',      transition: 'rest', from: 'full', to: 'hungry', current: 'hungry' },\n    { event: 'onAfterTransition',  transition: 'rest', from: 'full', to: 'hungry', current: 'hungry' },\n    { event: 'onAfterRest',        transition: 'rest', from: 'full', to: 'hungry', current: 'hungry' }\n  ])\n  \n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('lifecycle events for factory generated state machines', t => {\n\n  var FSM = StateMachine.factory({\n    transitions: [\n      { name: 'stepA', from: 'none', to: 'A' },\n      { name: 'stepB', from: 'none', to: 'B' }\n    ],\n    data: function(name) {\n      return {\n        name:   name,\n        logger: new LifecycleLogger()\n      }\n    },\n    methods: {\n      onBeforeTransition: function(lifecycle) { this.logger(lifecycle) },\n      onBeforeStepA:      function(lifecycle) { this.logger(lifecycle) },\n      onBeforeStepB:      function(lifecycle) { this.logger(lifecycle) },\n      onLeaveState:       function(lifecycle) { this.logger(lifecycle) },\n      onLeaveNone:        function(lifecycle) { this.logger(lifecycle) },\n      onLeaveA:           function(lifecycle) { this.logger(lifecycle) },\n      onLeaveB:           function(lifecycle) { this.logger(lifecycle) },\n      onTransition:       function(lifecycle) { this.logger(lifecycle) },\n      onEnterState:       function(lifecycle) { this.logger(lifecycle) },\n      onEnterNone:        function(lifecycle) { this.logger(lifecycle) },\n      onEnterA:           function(lifecycle) { this.logger(lifecycle) },\n      onEnterB:           function(lifecycle) { this.logger(lifecycle) },\n      onAfterTransition:  function(lifecycle) { this.logger(lifecycle) },\n      onAfterStepA:       function(lifecycle) { this.logger(lifecycle) },\n      onAfterStepB:       function(lifecycle) { this.logger(lifecycle) }\n    }\n  });\n\n  var a = new FSM('a'),\n      b = new FSM('b');\n\n  t.is(a.state, 'none')\n  t.is(b.state, 'none')\n\n  t.deepEqual(a.logger.log, [])\n  t.deepEqual(b.logger.log, [])\n\n  a.stepA()\n  b.stepB()\n\n  t.is(a.state, 'A')\n  t.is(b.state, 'B')\n\n  t.deepEqual(a.logger.log, [\n    { event: 'onBeforeTransition', transition: 'stepA', from: 'none', to: 'A', current: 'none' },\n    { event: 'onBeforeStepA',      transition: 'stepA', from: 'none', to: 'A', current: 'none' },\n    { event: 'onLeaveState',       transition: 'stepA', from: 'none', to: 'A', current: 'none' },\n    { event: 'onLeaveNone',        transition: 'stepA', from: 'none', to: 'A', current: 'none' },\n    { event: 'onTransition',       transition: 'stepA', from: 'none', to: 'A', current: 'none' },\n    { event: 'onEnterState',       transition: 'stepA', from: 'none', to: 'A', current: 'A'    },\n    { event: 'onEnterA',           transition: 'stepA', from: 'none', to: 'A', current: 'A'    },\n    { event: 'onAfterTransition',  transition: 'stepA', from: 'none', to: 'A', current: 'A'    },\n    { event: 'onAfterStepA',       transition: 'stepA', from: 'none', to: 'A', current: 'A'    }\n  ])\n\n  t.deepEqual(b.logger.log, [\n    { event: 'onBeforeTransition', transition: 'stepB', from: 'none', to: 'B', current: 'none' },\n    { event: 'onBeforeStepB',      transition: 'stepB', from: 'none', to: 'B', current: 'none' },\n    { event: 'onLeaveState',       transition: 'stepB', from: 'none', to: 'B', current: 'none' },\n    { event: 'onLeaveNone',        transition: 'stepB', from: 'none', to: 'B', current: 'none' },\n    { event: 'onTransition',       transition: 'stepB', from: 'none', to: 'B', current: 'none' },\n    { event: 'onEnterState',       transition: 'stepB', from: 'none', to: 'B', current: 'B'    },\n    { event: 'onEnterB',           transition: 'stepB', from: 'none', to: 'B', current: 'B'    },\n    { event: 'onAfterTransition',  transition: 'stepB', from: 'none', to: 'B', current: 'B'    },\n    { event: 'onAfterStepB',       transition: 'stepB', from: 'none', to: 'B', current: 'B'    }\n  ])\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('lifecycle events for custom init transition', t => {\n\n  var logger = new LifecycleLogger(),\n      fsm = new StateMachine({\n        init: { name: 'boot', from: 'booting', to: 'complete' },\n        methods: {\n          onBeforeTransition: logger,\n          onBeforeInit:       logger,\n          onBeforeBoot:       logger,\n          onLeaveState:       logger,\n          onLeaveNone:        logger,\n          onLeaveBooting:     logger,\n          onLeaveComplete:    logger,\n          onTransition:       logger,\n          onEnterState:       logger,\n          onEnterNone:        logger,\n          onEnterBooting:     logger,\n          onEnterComplete:    logger,\n          onAfterTransition:  logger,\n          onAfterInit:        logger,\n          onAfterBoot:        logger\n        }\n      });\n\n  t.is(fsm.state, 'complete')\n\n  t.deepEqual(fsm.allStates(),      [ 'booting', 'complete' ])\n  t.deepEqual(fsm.allTransitions(), [ 'boot' ])\n  t.deepEqual(fsm.transitions(),    [ ])\n\n  t.deepEqual(logger.log, [\n    { event: 'onBeforeTransition', transition: 'boot', from: 'booting', to: 'complete', current: 'booting'  },\n    { event: 'onBeforeBoot',       transition: 'boot', from: 'booting', to: 'complete', current: 'booting'  },\n    { event: 'onLeaveState',       transition: 'boot', from: 'booting', to: 'complete', current: 'booting'  },\n    { event: 'onLeaveBooting',     transition: 'boot', from: 'booting', to: 'complete', current: 'booting'  },\n    { event: 'onTransition',       transition: 'boot', from: 'booting', to: 'complete', current: 'booting'  },\n    { event: 'onEnterState',       transition: 'boot', from: 'booting', to: 'complete', current: 'complete' },\n    { event: 'onEnterComplete',    transition: 'boot', from: 'booting', to: 'complete', current: 'complete' },\n    { event: 'onAfterTransition',  transition: 'boot', from: 'booting', to: 'complete', current: 'complete' },\n    { event: 'onAfterBoot',        transition: 'boot', from: 'booting', to: 'complete', current: 'complete' }\n  ])\n\n})\n\n//-------------------------------------------------------------------------------------------------\n"
  },
  {
    "path": "test/observers.js",
    "content": "import test            from 'ava'\nimport StateMachine    from '../src/app'\nimport LifecycleLogger from './helpers/lifecycle_logger'\n\n//-------------------------------------------------------------------------------------------------\n\ntest('lifecycle events can be observed by external observer methods', t => {\n\n  var logger = new LifecycleLogger(),\n      fsm = new StateMachine({\n        transitions: [\n          { name: 'step', from: 'none', to: 'complete' }\n        ]\n      });\n\n  fsm.observe(\"onBeforeTransition\", logger)\n  fsm.observe(\"onBeforeStep\",       logger)\n  fsm.observe(\"onLeaveState\",       logger)\n  fsm.observe(\"onLeaveNone\",        logger)\n  fsm.observe(\"onLeaveComplete\",    logger)\n  fsm.observe(\"onTransition\",       logger)\n  fsm.observe(\"onEnterState\",       logger)\n  fsm.observe(\"onEnterNone\",        logger)\n  fsm.observe(\"onEnterComplete\",    logger)\n  fsm.observe(\"onAfterTransition\",  logger)\n  fsm.observe(\"onAfterStep\",        logger)\n\n  fsm.step('additional', 'arguments')\n\n  t.deepEqual(logger.log, [\n    { event: 'onBeforeTransition', transition: 'step', from: 'none', to: 'complete', current: 'none',     args: [ 'additional', 'arguments' ] },\n    { event: 'onBeforeStep',       transition: 'step', from: 'none', to: 'complete', current: 'none',     args: [ 'additional', 'arguments' ] },\n    { event: 'onLeaveState',       transition: 'step', from: 'none', to: 'complete', current: 'none',     args: [ 'additional', 'arguments' ] },\n    { event: 'onLeaveNone',        transition: 'step', from: 'none', to: 'complete', current: 'none',     args: [ 'additional', 'arguments' ] },\n    { event: 'onTransition',       transition: 'step', from: 'none', to: 'complete', current: 'none',     args: [ 'additional', 'arguments' ] },\n    { event: 'onEnterState',       transition: 'step', from: 'none', to: 'complete', current: 'complete', args: [ 'additional', 'arguments' ] },\n    { event: 'onEnterComplete',    transition: 'step', from: 'none', to: 'complete', current: 'complete', args: [ 'additional', 'arguments' ] },\n    { event: 'onAfterTransition',  transition: 'step', from: 'none', to: 'complete', current: 'complete', args: [ 'additional', 'arguments' ] },\n    { event: 'onAfterStep',        transition: 'step', from: 'none', to: 'complete', current: 'complete', args: [ 'additional', 'arguments' ] }\n  ])\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('lifecycle events can be observed by external observer classes', t => {\n\n  var logger = new LifecycleLogger(),\n      fsm = new StateMachine({\n        transitions: [\n          { name: 'step', from: 'none', to: 'complete' }\n        ]\n      });\n\n  fsm.observe({\n    \"onBeforeTransition\": logger,\n    \"onBeforeStep\":       logger,\n    \"onLeaveState\":       logger,\n    \"onLeaveNone\":        logger,\n    \"onLeaveComplete\":    logger,\n    \"onTransition\":       logger,\n    \"onEnterState\":       logger,\n    \"onEnterNone\":        logger,\n    \"onEnterComplete\":    logger,\n    \"onAfterTransition\":  logger,\n    \"onAfterStep\":        logger,\n  })\n\n  fsm.step('additional', 'arguments')\n\n  t.deepEqual(logger.log, [\n    { event: 'onBeforeTransition', transition: 'step', from: 'none', to: 'complete', current: 'none',     args: [ 'additional', 'arguments' ] },\n    { event: 'onBeforeStep',       transition: 'step', from: 'none', to: 'complete', current: 'none',     args: [ 'additional', 'arguments' ] },\n    { event: 'onLeaveState',       transition: 'step', from: 'none', to: 'complete', current: 'none',     args: [ 'additional', 'arguments' ] },\n    { event: 'onLeaveNone',        transition: 'step', from: 'none', to: 'complete', current: 'none',     args: [ 'additional', 'arguments' ] },\n    { event: 'onTransition',       transition: 'step', from: 'none', to: 'complete', current: 'none',     args: [ 'additional', 'arguments' ] },\n    { event: 'onEnterState',       transition: 'step', from: 'none', to: 'complete', current: 'complete', args: [ 'additional', 'arguments' ] },\n    { event: 'onEnterComplete',    transition: 'step', from: 'none', to: 'complete', current: 'complete', args: [ 'additional', 'arguments' ] },\n    { event: 'onAfterTransition',  transition: 'step', from: 'none', to: 'complete', current: 'complete', args: [ 'additional', 'arguments' ] },\n    { event: 'onAfterStep',        transition: 'step', from: 'none', to: 'complete', current: 'complete', args: [ 'additional', 'arguments' ] }\n  ])\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('lifecycle events can be observed by multiple observers', t => {\n\n  var logger1 = new LifecycleLogger(),\n      logger2 = new LifecycleLogger(),\n      fsm = new StateMachine({\n        transitions: [\n          { name: 'step', from: 'none', to: 'complete' }\n        ]\n      });\n\n  fsm.observe(\"onBeforeTransition\", logger1)\n  fsm.observe(\"onBeforeStep\",       logger1)\n  fsm.observe(\"onLeaveState\",       logger1)\n  fsm.observe(\"onLeaveNone\",        logger1)\n  fsm.observe(\"onLeaveComplete\",    logger1)\n  fsm.observe(\"onTransition\",       logger1)\n  fsm.observe(\"onEnterState\",       logger1)\n  fsm.observe(\"onEnterNone\",        logger1)\n  fsm.observe(\"onEnterComplete\",    logger1)\n  fsm.observe(\"onAfterTransition\",  logger1)\n  fsm.observe(\"onAfterStep\",        logger1)\n\n  fsm.observe({\n    \"onBeforeTransition\": logger2,\n    \"onBeforeStep\":       logger2,\n    \"onLeaveState\":       logger2,\n    \"onLeaveNone\":        logger2,\n    \"onLeaveComplete\":    logger2,\n    \"onTransition\":       logger2,\n    \"onEnterState\":       logger2,\n    \"onEnterNone\":        logger2,\n    \"onEnterComplete\":    logger2,\n    \"onAfterTransition\":  logger2,\n    \"onAfterStep\":        logger2,\n  })\n\n  fsm.step('additional', 'arguments')\n\n  t.deepEqual(logger1.log, [\n    { event: 'onBeforeTransition', transition: 'step', from: 'none', to: 'complete', current: 'none',     args: [ 'additional', 'arguments' ] },\n    { event: 'onBeforeStep',       transition: 'step', from: 'none', to: 'complete', current: 'none',     args: [ 'additional', 'arguments' ] },\n    { event: 'onLeaveState',       transition: 'step', from: 'none', to: 'complete', current: 'none',     args: [ 'additional', 'arguments' ] },\n    { event: 'onLeaveNone',        transition: 'step', from: 'none', to: 'complete', current: 'none',     args: [ 'additional', 'arguments' ] },\n    { event: 'onTransition',       transition: 'step', from: 'none', to: 'complete', current: 'none',     args: [ 'additional', 'arguments' ] },\n    { event: 'onEnterState',       transition: 'step', from: 'none', to: 'complete', current: 'complete', args: [ 'additional', 'arguments' ] },\n    { event: 'onEnterComplete',    transition: 'step', from: 'none', to: 'complete', current: 'complete', args: [ 'additional', 'arguments' ] },\n    { event: 'onAfterTransition',  transition: 'step', from: 'none', to: 'complete', current: 'complete', args: [ 'additional', 'arguments' ] },\n    { event: 'onAfterStep',        transition: 'step', from: 'none', to: 'complete', current: 'complete', args: [ 'additional', 'arguments' ] }\n  ])\n\n  t.deepEqual(logger2.log, [\n    { event: 'onBeforeTransition', transition: 'step', from: 'none', to: 'complete', current: 'none',     args: [ 'additional', 'arguments' ] },\n    { event: 'onBeforeStep',       transition: 'step', from: 'none', to: 'complete', current: 'none',     args: [ 'additional', 'arguments' ] },\n    { event: 'onLeaveState',       transition: 'step', from: 'none', to: 'complete', current: 'none',     args: [ 'additional', 'arguments' ] },\n    { event: 'onLeaveNone',        transition: 'step', from: 'none', to: 'complete', current: 'none',     args: [ 'additional', 'arguments' ] },\n    { event: 'onTransition',       transition: 'step', from: 'none', to: 'complete', current: 'none',     args: [ 'additional', 'arguments' ] },\n    { event: 'onEnterState',       transition: 'step', from: 'none', to: 'complete', current: 'complete', args: [ 'additional', 'arguments' ] },\n    { event: 'onEnterComplete',    transition: 'step', from: 'none', to: 'complete', current: 'complete', args: [ 'additional', 'arguments' ] },\n    { event: 'onAfterTransition',  transition: 'step', from: 'none', to: 'complete', current: 'complete', args: [ 'additional', 'arguments' ] },\n    { event: 'onAfterStep',        transition: 'step', from: 'none', to: 'complete', current: 'complete', args: [ 'additional', 'arguments' ] }\n  ])\n\n})\n\n//-------------------------------------------------------------------------------------------------\n"
  },
  {
    "path": "test/plugin/history.js",
    "content": "import test                from 'ava'\nimport StateMachine        from '../../src/app'\nimport StateMachineHistory from '../../src/plugin/history'\nimport LifecycleLogger     from '../helpers/lifecycle_logger'\n\n//-------------------------------------------------------------------------------------------------\n\ntest('history', t => {\n\n  var fsm = new StateMachine({\n    init: 'solid',\n    transitions: [\n      { name: 'melt',     from: 'solid',  to: 'liquid' },\n      { name: 'freeze',   from: 'liquid', to: 'solid'  },\n      { name: 'vaporize', from: 'liquid', to: 'gas'    },\n      { name: 'condense', from: 'gas',    to: 'liquid' }\n    ],\n    plugins: [\n      StateMachineHistory\n    ]\n  })\n\n                  t.is(fsm.state, 'solid');  t.deepEqual(fsm.history, [ 'solid' ])\n  fsm.melt();     t.is(fsm.state, 'liquid'); t.deepEqual(fsm.history, [ 'solid', 'liquid' ])\n  fsm.vaporize(); t.is(fsm.state, 'gas');    t.deepEqual(fsm.history, [ 'solid', 'liquid', 'gas' ])\n  fsm.condense(); t.is(fsm.state, 'liquid'); t.deepEqual(fsm.history, [ 'solid', 'liquid', 'gas', 'liquid' ]);\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('history can be cleared', t => {\n\n  var fsm = new StateMachine({\n    transitions: [\n      { name: 'init', from: 'none', to: 'A' },\n      { name: 'step', from: 'A',    to: 'B' },\n      { name: 'step', from: 'B',    to: 'C' },\n      { name: 'step', from: 'C',    to: 'A' }\n    ],\n    plugins: [\n      StateMachineHistory\n    ]\n  })\n\n  fsm.init()\n  fsm.step()\n\n  t.is(fsm.state, 'B')\n  t.deepEqual(fsm.history, ['A', 'B'])\n\n  fsm.clearHistory()\n\n  t.is(fsm.state, 'B')\n  t.deepEqual(fsm.history, [])\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('history does not record no-op transitions', t => {\n\n  var fsm = new StateMachine({\n    init: 'solid',\n    transitions: [\n      { name: 'melt',     from: 'solid',  to: 'liquid' },\n      { name: 'freeze',   from: 'liquid', to: 'solid'  },\n      { name: 'vaporize', from: 'liquid', to: 'gas'    },\n      { name: 'condense', from: 'gas',    to: 'liquid' },\n      { name: 'noop',     from: '*',      to: '*'      }\n    ],\n    plugins: [\n      StateMachineHistory\n    ]\n  })\n\n                  t.is(fsm.state, 'solid');  t.deepEqual(fsm.history, [ 'solid' ])\n  fsm.noop();     t.is(fsm.state, 'solid');  t.deepEqual(fsm.history, [ 'solid' ])\n  fsm.melt();     t.is(fsm.state, 'liquid'); t.deepEqual(fsm.history, [ 'solid', 'liquid' ])\n  fsm.noop();     t.is(fsm.state, 'liquid'); t.deepEqual(fsm.history, [ 'solid', 'liquid' ])\n  fsm.vaporize(); t.is(fsm.state, 'gas');    t.deepEqual(fsm.history, [ 'solid', 'liquid', 'gas' ])\n  fsm.noop();     t.is(fsm.state, 'gas');    t.deepEqual(fsm.history, [ 'solid', 'liquid', 'gas' ])\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('history with configurable names', t => {\n\n  var fsm = new StateMachine({\n    init: 'solid',\n    transitions: [\n      { name: 'melt',     from: 'solid',  to: 'liquid' },\n      { name: 'freeze',   from: 'liquid', to: 'solid'  },\n      { name: 'vaporize', from: 'liquid', to: 'gas'    },\n      { name: 'condense', from: 'gas',    to: 'liquid' }\n    ],\n    plugins: [\n      new StateMachineHistory({ name: 'memory', future: 'yonder' })\n    ]\n  })\n\n                  t.is(fsm.state, 'solid');  t.deepEqual(fsm.memory, [ 'solid' ])\n  fsm.melt();     t.is(fsm.state, 'liquid'); t.deepEqual(fsm.memory, [ 'solid', 'liquid' ])\n  fsm.vaporize(); t.is(fsm.state, 'gas');    t.deepEqual(fsm.memory, [ 'solid', 'liquid', 'gas' ])\n  fsm.condense(); t.is(fsm.state, 'liquid'); t.deepEqual(fsm.memory, [ 'solid', 'liquid', 'gas', 'liquid' ])\n\n  t.is(fsm.canMemoryBack, true)\n  t.is(fsm.canMemoryForward, false)\n  t.deepEqual(fsm.yonder, [ ])\n\n  fsm.memoryBack()\n\n  t.is(fsm.state, 'gas')\n  t.deepEqual(fsm.memory, [ 'solid', 'liquid', 'gas' ])\n  t.deepEqual(fsm.yonder, [ 'liquid' ])\n\n  fsm.clearMemory()\n  t.deepEqual(fsm.memory, [])\n  t.deepEqual(fsm.yonder, [])\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('history, by default, just keeps growing', t => {\n\n  var fsm = new StateMachine({\n    init: 'solid',\n    transitions: [\n      { name: 'melt',     from: 'solid',  to: 'liquid' },\n      { name: 'freeze',   from: 'liquid', to: 'solid'  },\n      { name: 'vaporize', from: 'liquid', to: 'gas'    },\n      { name: 'condense', from: 'gas',    to: 'liquid' }\n    ],\n    plugins: [\n      new StateMachineHistory()\n    ]\n  })\n\n  t.is(fsm.state, 'solid')\n  t.deepEqual(fsm.history, [ 'solid' ])\n\n  fsm.melt();     t.deepEqual(fsm.history, [ 'solid', 'liquid' ])\n  fsm.vaporize(); t.deepEqual(fsm.history, [ 'solid', 'liquid', 'gas' ])\n  fsm.condense(); t.deepEqual(fsm.history, [ 'solid', 'liquid', 'gas', 'liquid' ])\n  fsm.freeze();   t.deepEqual(fsm.history, [ 'solid', 'liquid', 'gas', 'liquid', 'solid' ])\n  fsm.melt();     t.deepEqual(fsm.history, [ 'solid', 'liquid', 'gas', 'liquid', 'solid', 'liquid' ])\n  fsm.vaporize(); t.deepEqual(fsm.history, [ 'solid', 'liquid', 'gas', 'liquid', 'solid', 'liquid', 'gas' ])\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('history can be limited to N entries', t => {\n\n  var fsm = new StateMachine({\n    init: 'solid',\n    transitions: [\n      { name: 'melt',     from: 'solid',  to: 'liquid' },\n      { name: 'freeze',   from: 'liquid', to: 'solid'  },\n      { name: 'vaporize', from: 'liquid', to: 'gas'    },\n      { name: 'condense', from: 'gas',    to: 'liquid' }\n    ],\n    plugins: [\n      new StateMachineHistory({ max: 3 })\n    ]\n  })\n\n  t.is(fsm.state, 'solid')\n  t.deepEqual(fsm.history, [ 'solid' ])\n\n  fsm.melt();     t.deepEqual(fsm.history, [ 'solid', 'liquid' ])\n  fsm.vaporize(); t.deepEqual(fsm.history, [ 'solid', 'liquid', 'gas' ])\n  fsm.condense(); t.deepEqual(fsm.history, [ 'liquid', 'gas', 'liquid' ])\n  fsm.freeze();   t.deepEqual(fsm.history, [ 'gas', 'liquid', 'solid' ])\n  fsm.melt();     t.deepEqual(fsm.history, [ 'liquid', 'solid', 'liquid' ])\n  fsm.vaporize(); t.deepEqual(fsm.history, [ 'solid', 'liquid', 'gas' ])\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('history back and forward', t => {\n\n  var fsm = new StateMachine({\n    init: 'A',\n    transitions: [\n      { name: 'step', from: 'A', to: 'B' },\n      { name: 'step', from: 'B', to: 'C' },\n      { name: 'step', from: 'C', to: 'D' }\n    ],\n    plugins: [\n      StateMachineHistory\n    ]\n  })\n\n  t.is(fsm.state,          'A')\n  t.is(fsm.canHistoryBack, false)\n  t.deepEqual(fsm.history, [ 'A' ])\n  t.deepEqual(fsm.future, [ ])\n\n  var error = t.throws(() => {\n    fsm.historyBack()\n  })\n  t.is(error.message, 'no history')\n\n  fsm.step()\n  fsm.step()\n  fsm.step()\n\n  t.is(fsm.state, 'D')\n  t.is(fsm.canHistoryBack, true)\n  t.is(fsm.canHistoryForward, false)\n  t.deepEqual(fsm.history, [ 'A', 'B', 'C', 'D' ])\n  t.deepEqual(fsm.future, [])\n\n  fsm.historyBack()\n  t.is(fsm.state, 'C')\n  t.deepEqual(fsm.history, [ 'A', 'B', 'C' ])\n  t.deepEqual(fsm.future, [ 'D' ])\n  t.is(fsm.canHistoryBack, true)\n  t.is(fsm.canHistoryForward, true)\n\n  fsm.historyBack()\n  t.is(fsm.state, 'B')\n  t.deepEqual(fsm.history, [ 'A', 'B' ])\n  t.deepEqual(fsm.future, [ 'D', 'C' ])\n  t.is(fsm.canHistoryBack, true)\n  t.is(fsm.canHistoryForward, true)\n\n  fsm.historyBack()\n  t.is(fsm.state, 'A')\n  t.deepEqual(fsm.history, [ 'A' ])\n  t.deepEqual(fsm.future, [ 'D', 'C', 'B' ])\n  t.is(fsm.canHistoryBack, false)\n  t.is(fsm.canHistoryForward, true)\n\n  fsm.historyForward()\n  t.is(fsm.state, 'B')\n  t.deepEqual(fsm.history, [ 'A', 'B' ])\n  t.deepEqual(fsm.future, [ 'D', 'C' ])\n  t.is(fsm.canHistoryBack, true)\n  t.is(fsm.canHistoryForward, true)\n\n  fsm.historyForward()\n  t.is(fsm.state, 'C')\n  t.deepEqual(fsm.history, [ 'A', 'B', 'C' ])\n  t.deepEqual(fsm.future, [ 'D' ])\n  t.is(fsm.canHistoryBack, true)\n  t.is(fsm.canHistoryForward, true)\n\n  fsm.step()\n  t.is(fsm.state, 'D')\n  t.deepEqual(fsm.history, [ 'A', 'B', 'C', 'D' ])\n  t.deepEqual(fsm.future, [ ])\n  t.is(fsm.canHistoryBack, true)\n  t.is(fsm.canHistoryForward, false)\n\n  error = t.throws(() => {\n    fsm.historyForward()\n  })\n  t.is(error.message, 'no history')\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('history back and forward lifecycle events', t => {\n\n  var logger = new LifecycleLogger(),\n      fsm = new StateMachine({\n        init: 'A',\n        transitions: [\n          { name: 'step', from: 'A', to: 'B' },\n          { name: 'step', from: 'B', to: 'C' },\n          { name: 'step', from: 'C', to: 'D' }\n        ],\n        methods: {\n          onBeforeTransition:     logger,\n          onBeforeStep:           logger,\n          onBeforeHistoryBack:    logger,\n          onBeforeHistoryForward: logger,\n          onLeaveState:           logger,\n          onLeaveA:               logger,\n          onLeaveB:               logger,\n          onLeaveC:               logger,\n          onLeaveD:               logger,\n          onTransition:           logger,\n          onEnterState:           logger,\n          onEnterA:               logger,\n          onEnterB:               logger,\n          onEnterC:               logger,\n          onEnterD:               logger,\n          onAfterTransition:      logger,\n          onAfterStep:            logger,\n          onAfterHistoryBack:     logger,\n          onAfterHistoryForward:  logger\n        },\n        plugins: [\n          StateMachineHistory\n        ]\n      })\n\n  fsm.step()\n  fsm.step()\n  fsm.step()\n  logger.clear()\n\n  t.is(fsm.state, 'D')\n  t.deepEqual(fsm.history, [ 'A', 'B', 'C', 'D' ])\n  t.deepEqual(fsm.future, [ ])\n\n  fsm.historyBack()\n\n  t.is(fsm.state, 'C')\n  t.deepEqual(fsm.history, [ 'A', 'B', 'C' ])\n  t.deepEqual(fsm.future, [ 'D' ])\n\n  t.deepEqual(logger.log, [\n    { event: 'onBeforeTransition',  transition: 'historyBack', from: 'D', to: 'C', current: 'D' },\n    { event: 'onBeforeHistoryBack', transition: 'historyBack', from: 'D', to: 'C', current: 'D' },\n    { event: 'onLeaveState',        transition: 'historyBack', from: 'D', to: 'C', current: 'D' },\n    { event: 'onLeaveD',            transition: 'historyBack', from: 'D', to: 'C', current: 'D' },\n    { event: 'onTransition',        transition: 'historyBack', from: 'D', to: 'C', current: 'D' },\n    { event: 'onEnterState',        transition: 'historyBack', from: 'D', to: 'C', current: 'C' },\n    { event: 'onEnterC',            transition: 'historyBack', from: 'D', to: 'C', current: 'C' },\n    { event: 'onAfterTransition',   transition: 'historyBack', from: 'D', to: 'C', current: 'C' },\n    { event: 'onAfterHistoryBack',  transition: 'historyBack', from: 'D', to: 'C', current: 'C' }\n  ])\n\n  logger.clear()\n\n  fsm.historyForward()\n\n  t.is(fsm.state, 'D')\n  t.deepEqual(fsm.history, [ 'A', 'B', 'C', 'D' ])\n  t.deepEqual(fsm.future, [ ])\n\n  t.deepEqual(logger.log, [\n    { event: 'onBeforeTransition',     transition: 'historyForward', from: 'C', to: 'D', current: 'C' },\n    { event: 'onBeforeHistoryForward', transition: 'historyForward', from: 'C', to: 'D', current: 'C' },\n    { event: 'onLeaveState',           transition: 'historyForward', from: 'C', to: 'D', current: 'C' },\n    { event: 'onLeaveC',               transition: 'historyForward', from: 'C', to: 'D', current: 'C' },\n    { event: 'onTransition',           transition: 'historyForward', from: 'C', to: 'D', current: 'C' },\n    { event: 'onEnterState',           transition: 'historyForward', from: 'C', to: 'D', current: 'D' },\n    { event: 'onEnterD',               transition: 'historyForward', from: 'C', to: 'D', current: 'D' },\n    { event: 'onAfterTransition',      transition: 'historyForward', from: 'C', to: 'D', current: 'D' },\n    { event: 'onAfterHistoryForward',  transition: 'historyForward', from: 'C', to: 'D', current: 'D' }\n  ])\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('history can be used with a state machine factory', t => {\n\n  var FSM = StateMachine.factory({\n    init: 'solid',\n    transitions: [\n      { name: 'melt',     from: 'solid',  to: 'liquid' },\n      { name: 'freeze',   from: 'liquid', to: 'solid'  },\n      { name: 'vaporize', from: 'liquid', to: 'gas'    },\n      { name: 'condense', from: 'gas',    to: 'liquid' }\n    ],\n    plugins: [\n      StateMachineHistory\n    ]\n  })\n\n  var a = new FSM(),\n      b = new FSM();\n\n  t.is(a.state, 'solid')\n  t.is(b.state, 'solid')\n  t.deepEqual(a.history, [ 'solid' ])\n  t.deepEqual(b.history, [ 'solid' ])\n\n  a.melt()\n  a.vaporize()\n  a.condense()\n  a.freeze()\n\n  t.is(a.state, 'solid')\n  t.is(b.state, 'solid')\n  t.deepEqual(a.history, [ 'solid', 'liquid', 'gas', 'liquid', 'solid' ])\n  t.deepEqual(b.history, [ 'solid' ])\n\n  b.melt()\n  b.freeze()\n\n  t.is(a.state, 'solid')\n  t.is(b.state, 'solid')\n\n  t.deepEqual(a.history, [ 'solid', 'liquid', 'gas', 'liquid', 'solid' ])\n  t.deepEqual(b.history, [ 'solid', 'liquid', 'solid' ])\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('history can be used with a singleton state machine applied to existing object', t => {\n\n  var fsm = {\n    name: 'jake'\n  }\n\n  StateMachine.apply(fsm, {\n    init: 'solid',\n    transitions: [\n      { name: 'melt',     from: 'solid',  to: 'liquid' },\n      { name: 'freeze',   from: 'liquid', to: 'solid'  },\n      { name: 'vaporize', from: 'liquid', to: 'gas'    },\n      { name: 'condense', from: 'gas',    to: 'liquid' }\n    ],\n    plugins: [\n      StateMachineHistory\n    ]\n  })\n\n  t.is(fsm.name, 'jake')\n  t.is(fsm.state, 'solid')\n  t.deepEqual(fsm.history, [ 'solid' ])\n\n  fsm.melt();\n  t.is(fsm.state, 'liquid')\n  t.deepEqual(fsm.history, [ 'solid', 'liquid' ])\n\n  fsm.vaporize();\n  t.is(fsm.state, 'gas')\n  t.deepEqual(fsm.history, [ 'solid', 'liquid', 'gas' ])\n\n  fsm.condense()\n  t.is(fsm.state, 'liquid')\n  t.deepEqual(fsm.history, [ 'solid', 'liquid', 'gas', 'liquid' ])\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('history can be used with a state machine factory applied to existing class', t => {\n\n  function FSM(name) {\n    this.name = name\n    this._fsm()\n  }\n\n  StateMachine.factory(FSM, {\n    init: 'solid',\n    transitions: [\n      { name: 'melt',     from: 'solid',  to: 'liquid' },\n      { name: 'freeze',   from: 'liquid', to: 'solid'  },\n      { name: 'vaporize', from: 'liquid', to: 'gas'    },\n      { name: 'condense', from: 'gas',    to: 'liquid' }\n    ],\n    plugins: [\n      StateMachineHistory\n    ]\n  })\n\n  var a = new FSM('A'),\n      b = new FSM('B');\n\n  t.is(a.name, 'A')\n  t.is(b.name, 'B')\n\n  t.is(a.state, 'solid')\n  t.is(b.state, 'solid')\n  t.deepEqual(a.history, [ 'solid' ])\n  t.deepEqual(b.history, [ 'solid' ])\n\n  a.melt()\n  a.vaporize()\n  a.condense()\n  a.freeze()\n\n  t.is(a.state, 'solid')\n  t.is(b.state, 'solid')\n  t.deepEqual(a.history, [ 'solid', 'liquid', 'gas', 'liquid', 'solid' ])\n  t.deepEqual(b.history, [ 'solid' ])\n\n  b.melt()\n  b.freeze()\n\n  t.is(a.state, 'solid')\n  t.is(b.state, 'solid')\n\n  t.deepEqual(a.history, [ 'solid', 'liquid', 'gas', 'liquid', 'solid' ])\n  t.deepEqual(b.history, [ 'solid', 'liquid', 'solid' ])\n\n})\n\n//-------------------------------------------------------------------------------------------------\n"
  },
  {
    "path": "test/plugin/visualize.js",
    "content": "import test         from 'ava'\nimport StateMachine from '../../src/app'\nimport visualize    from '../../src/plugin/visualize'\n\nvar dotcfg    = visualize.dotcfg, // converts FSM        to DOT CONFIG\n    dotify    = visualize.dotify; // converts DOT CONFIG to DOT OUTPUT\n\n//-------------------------------------------------------------------------------------------------\n\ntest('visualize state machine', t => {\n\n  var fsm = new StateMachine({\n    init: 'solid',\n    transitions: [\n      { name: 'melt',     from: 'solid',  to: 'liquid' },\n      { name: 'freeze',   from: 'liquid', to: 'solid'  },\n      { name: 'vaporize', from: 'liquid', to: 'gas'    },\n      { name: 'condense', from: 'gas',    to: 'liquid' }\n    ]\n  })\n\n  t.is(visualize(fsm), `digraph \"fsm\" {\n  \"solid\";\n  \"liquid\";\n  \"gas\";\n  \"solid\" -> \"liquid\" [ label=\" melt \" ];\n  \"liquid\" -> \"solid\" [ label=\" freeze \" ];\n  \"liquid\" -> \"gas\" [ label=\" vaporize \" ];\n  \"gas\" -> \"liquid\" [ label=\" condense \" ];\n}`)\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('visualize state machine factory', t => {\n\n  var FSM = StateMachine.factory({\n    init: 'solid',\n    transitions: [\n      { name: 'melt',     from: 'solid',  to: 'liquid' },\n      { name: 'freeze',   from: 'liquid', to: 'solid'  },\n      { name: 'vaporize', from: 'liquid', to: 'gas'    },\n      { name: 'condense', from: 'gas',    to: 'liquid' }\n    ]\n  })\n\n  t.is(visualize(FSM), `digraph \"fsm\" {\n  \"solid\";\n  \"liquid\";\n  \"gas\";\n  \"solid\" -> \"liquid\" [ label=\" melt \" ];\n  \"liquid\" -> \"solid\" [ label=\" freeze \" ];\n  \"liquid\" -> \"gas\" [ label=\" vaporize \" ];\n  \"gas\" -> \"liquid\" [ label=\" condense \" ];\n}`)\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('visualize with custom .dot markup', t => {\n\n  var fsm = new StateMachine({\n    init: 'solid',\n    transitions: [\n      { name: 'melt',     from: 'solid',  to: 'liquid', dot: { color: 'red',    headport: 'nw', tailport: 'ne' } },\n      { name: 'freeze',   from: 'liquid', to: 'solid',  dot: { color: 'grey',  headport: 'se', tailport: 'sw' } },\n      { name: 'vaporize', from: 'liquid', to: 'gas',    dot: { color: 'yellow', headport: 'nw', tailport: 'ne' } },\n      { name: 'condense', from: 'gas',    to: 'liquid', dot: { color: 'brown',  headport: 'se', tailport: 'sw' } }\n    ]\n  })\n\n  t.is(visualize(fsm, { name: 'matter', orientation: 'horizontal' }), `digraph \"matter\" {\n  rankdir=LR;\n  \"solid\";\n  \"liquid\";\n  \"gas\";\n  \"solid\" -> \"liquid\" [ color=\"red\" ; headport=\"nw\" ; label=\" melt \" ; tailport=\"ne\" ];\n  \"liquid\" -> \"solid\" [ color=\"grey\" ; headport=\"se\" ; label=\" freeze \" ; tailport=\"sw\" ];\n  \"liquid\" -> \"gas\" [ color=\"yellow\" ; headport=\"nw\" ; label=\" vaporize \" ; tailport=\"ne\" ];\n  \"gas\" -> \"liquid\" [ color=\"brown\" ; headport=\"se\" ; label=\" condense \" ; tailport=\"sw\" ];\n}`)\n})\n\n//=================================================================================================\n// TEST FSM => DOTCFG\n//=================================================================================================\n\ntest('dotcfg simple state machine', t => {\n\n  var fsm = new StateMachine({\n    init: 'solid',\n    transitions: [\n      { name: 'melt',     from: 'solid',  to: 'liquid' },\n      { name: 'freeze',   from: 'liquid', to: 'solid'  },\n      { name: 'vaporize', from: 'liquid', to: 'gas'    },\n      { name: 'condense', from: 'gas',    to: 'liquid' }\n    ]\n  })\n\n  t.deepEqual(dotcfg(fsm), {\n    states: [ 'solid', 'liquid', 'gas' ],\n    transitions: [\n      { from: 'solid',  to: 'liquid', label: ' melt '     },\n      { from: 'liquid', to: 'solid',  label: ' freeze '   },\n      { from: 'liquid', to: 'gas',    label: ' vaporize ' },\n      { from: 'gas',    to: 'liquid', label: ' condense ' }\n    ]\n  })\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('dotcfg for state machine - optionally include :init transition', t => {\n\n  var fsm = new StateMachine({\n    init: { name: 'boot', from: 'booting', to: 'ready', dot: { color: 'red' } }\n  })\n\n  t.deepEqual(dotcfg(fsm, { init: false }), {\n    states: [ 'ready' ]\n  })\n\n  t.deepEqual(dotcfg(fsm, { init: true }), {\n    states: [ 'booting', 'ready' ],\n    transitions: [\n      { from: 'booting', to: 'ready', label: ' boot ', color: 'red' }\n    ]\n  })\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('dotcfg for fsm with multiple transitions with same :name', t => {\n\n  var fsm = new StateMachine({\n    init: 'A',\n    transitions: [\n      { name: 'step', from: 'A', to: 'B' },\n      { name: 'step', from: 'B', to: 'C' },\n      { name: 'step', from: 'C', to: 'D' }\n    ]\n  })\n\n  t.deepEqual(dotcfg(fsm), {\n    states: [ 'A', 'B', 'C', 'D' ],\n    transitions: [\n      { from: 'A', to: 'B', label: ' step ' },\n      { from: 'B', to: 'C', label: ' step ' },\n      { from: 'C', to: 'D', label: ' step ' }\n    ]\n  })\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('dotcfg for fsm transition with multiple :from', t => {\n\n  var fsm = new StateMachine({\n    init: 'A',\n    transitions: [\n      { name: 'step',  from: 'A',          to: 'B' },\n      { name: 'step',  from: 'B',          to: 'C' },\n      { name: 'step',  from: 'C',          to: 'D' },\n      { name: 'reset', from: [ 'A', 'B' ], to: 'A' }\n    ]\n  })\n\n  t.deepEqual(dotcfg(fsm), {\n    states: [ 'A', 'B', 'C', 'D' ],\n    transitions: [\n      { from: 'A', to: 'B', label: ' step '  },\n      { from: 'B', to: 'C', label: ' step '  },\n      { from: 'C', to: 'D', label: ' step '  },\n      { from: 'A', to: 'A', label: ' reset ' },\n      { from: 'B', to: 'A', label: ' reset ' }\n    ]\n  })\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('dotcfg for fsm with wildcard/missing :from', t => {\n\n  var fsm = new StateMachine({\n    init: 'A',\n    transitions: [\n      { name: 'step',   from: 'A',     to: 'B' },\n      { name: 'step',   from: 'B',     to: 'C' },\n      { name: 'step',   from: 'C',     to: 'D' },\n      { name: 'reset',  from: '*',     to: 'A' },\n      { name: 'finish', /* missing */  to: 'X' }\n    ]\n  })\n\n  t.deepEqual(dotcfg(fsm), {\n    states: [ 'A', 'B', 'C', 'D', 'X' ],\n    transitions: [\n      { from: 'A',    to: 'B', label: ' step '   },\n      { from: 'B',    to: 'C', label: ' step '   },\n      { from: 'C',    to: 'D', label: ' step '   },\n      { from: 'none', to: 'A', label: ' reset '  },\n      { from: 'A',    to: 'A', label: ' reset '  },\n      { from: 'B',    to: 'A', label: ' reset '  },\n      { from: 'C',    to: 'A', label: ' reset '  },\n      { from: 'D',    to: 'A', label: ' reset '  },\n      { from: 'X',    to: 'A', label: ' reset '  },\n      { from: 'none', to: 'X', label: ' finish ' },\n      { from: 'A',    to: 'X', label: ' finish ' },\n      { from: 'B',    to: 'X', label: ' finish ' },\n      { from: 'C',    to: 'X', label: ' finish ' },\n      { from: 'D',    to: 'X', label: ' finish ' },\n      { from: 'X',    to: 'X', label: ' finish ' }\n    ]\n  })\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('dotcfg for fsm with wildcard/missing :to', t => {\n\n  var fsm = new StateMachine({\n    init: 'A',\n    transitions: [\n      { name: 'step', from: 'A', to: 'B'       },\n      { name: 'step', from: 'B', to: 'C'       },\n      { name: 'step', from: 'C', to: 'D'       },\n      { name: 'stay', from: 'A', to: 'A'       },\n      { name: 'stay', from: 'B', to: '*'       },\n      { name: 'stay', from: 'C'  /* missing */ },\n      { name: 'noop', from: '*', to: '*'       }\n    ]\n  })\n\n  t.deepEqual(dotcfg(fsm), {\n    states: [ 'A', 'B', 'C', 'D' ],\n    transitions: [\n      { from: 'A',    to: 'B',    label: ' step ' },\n      { from: 'B',    to: 'C',    label: ' step ' },\n      { from: 'C',    to: 'D',    label: ' step ' },\n      { from: 'A',    to: 'A',    label: ' stay ' },\n      { from: 'B',    to: 'B',    label: ' stay ' },\n      { from: 'C',    to: 'C',    label: ' stay ' },\n      { from: 'none', to: 'none', label: ' noop ' },\n      { from: 'A',    to: 'A',    label: ' noop ' },\n      { from: 'B',    to: 'B',    label: ' noop ' },\n      { from: 'C',    to: 'C',    label: ' noop ' },\n      { from: 'D',    to: 'D',    label: ' noop ' }\n    ]\n  })\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('dotcfg for fsm - conditional transition is not displayed', t => {\n\n  var fsm = new StateMachine({\n        init: 'A',\n        transitions: [\n          { name: 'step', from: '*', to: function(n) { return this.skip(n) } },\n        ],\n        methods: {\n          skip: function(amount) {\n            var code = this.state.charCodeAt(0);\n            return String.fromCharCode(code + (amount || 1));\n          }\n        }\n      });\n\n  t.deepEqual(dotcfg(fsm), {\n    states: [ 'A' ]\n  })\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('dotcfg with custom transition .dot edge markup', t => {\n\n  var fsm = new StateMachine({\n    init: 'A',\n    transitions: [\n      { name: 'step', from: 'A', to: 'B', dot: { color: \"red\",   headport: 'nw', tailport: 'ne', label: 'A2B' } },\n      { name: 'step', from: 'B', to: 'C', dot: { color: \"green\", headport: 'sw', tailport: 'se', label: 'B2C' } }\n    ]\n  })\n\n  t.deepEqual(dotcfg(fsm), {\n    states: [ 'A', 'B', 'C' ],\n    transitions: [\n      { from: 'A', to: 'B', label: 'A2B',  color: \"red\",   headport: \"nw\", tailport: \"ne\" },\n      { from: 'B', to: 'C', label: 'B2C',  color: \"green\", headport: \"sw\", tailport: \"se\" }\n    ]\n  })\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('dotcfg with custom name', t => {\n\n  var fsm = new StateMachine();\n\n  t.deepEqual(dotcfg(fsm, { name: 'bob' }), {\n    name: 'bob',\n  })\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('dotcfg with custom orientation', t => {\n\n  var fsm = new StateMachine();\n\n  t.deepEqual(dotcfg(fsm, { orientation: 'horizontal' }), {\n    rankdir: 'LR',\n  })\n\n  t.deepEqual(dotcfg(fsm, { orientation: 'vertical' }), {\n    rankdir: 'TB',\n  })\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('dotcfg for empty state machine', t => {\n\n  var fsm = new StateMachine();\n\n  t.deepEqual(dotcfg(fsm), {})\n\n})\n\n//=================================================================================================\n// TEST DOTCFG => DOT OUTPUT\n//=================================================================================================\n\ntest('dotify empty', t => {\n  var expected = `digraph \"fsm\" {\n}`\n  t.is(dotify(),   expected)\n  t.is(dotify({}), expected)\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('dotify name', t => {\n  t.is(dotify({ name: 'bob' }), `digraph \"bob\" {\n}`)\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('dotify rankdir', t => {\n  t.is(dotify({ rankdir: 'LR' }), `digraph \"fsm\" {\n  rankdir=LR;\n}`)\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('dotify states', t => {\n  var states = [ 'A', 'B' ];\n  t.is(dotify({ states: states }), `digraph \"fsm\" {\n  \"A\";\n  \"B\";\n}`)\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('dotify transitions', t => {\n  var transitions = [\n    { from: 'A', to: 'B' },\n    { from: 'B', to: 'C' },\n  ];\n  t.is(dotify({ transitions: transitions }), `digraph \"fsm\" {\n  \"A\" -> \"B\";\n  \"B\" -> \"C\";\n}`)\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('dotify transitions with labels', t => {\n  var transitions = [\n    { from: 'A', to: 'B', label: 'first'  },\n    { from: 'B', to: 'C', label: 'second' }\n  ];\n  t.is(dotify({ transitions: transitions }), `digraph \"fsm\" {\n  \"A\" -> \"B\" [ label=\"first\" ];\n  \"B\" -> \"C\" [ label=\"second\" ];\n}`)\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('dotify transitions with custom .dot edge markup', t => {\n  var transitions = [\n    { from: 'A', to: 'B', label: 'first',  color: 'red',   headport: 'nw', tailport: 'ne' },\n    { from: 'B', to: 'A', label: 'second', color: 'green', headport: 'se', tailport: 'sw' }\n  ]\n  t.is(dotify({ transitions: transitions }), `digraph \"fsm\" {\n  \"A\" -> \"B\" [ color=\"red\" ; headport=\"nw\" ; label=\"first\" ; tailport=\"ne\" ];\n  \"B\" -> \"A\" [ color=\"green\" ; headport=\"se\" ; label=\"second\" ; tailport=\"sw\" ];\n}`)\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('dotify kitchen sink', t => {\n  var name = \"my fsm\",\n      rankdir = \"LR\",\n      states = [ 'none', 'solid', 'liquid', 'gas' ],\n      transitions = [\n        { from: 'none',   to: 'solid',  color: 'red',   label: 'init'     },\n        { from: 'solid',  to: 'liquid', color: 'red',   label: 'melt'     },\n        { from: 'liquid', to: 'solid',  color: 'green', label: 'freeze'   },\n        { from: 'liquid', to: 'gas',    color: 'red',   label: 'vaporize' }, \n        { from: 'gas',    to: 'liquid', color: 'green', label: 'condense' }\n      ];\n  t.is(dotify({ name: name, rankdir: rankdir, states: states, transitions: transitions }), `digraph \"my fsm\" {\n  rankdir=LR;\n  \"none\";\n  \"solid\";\n  \"liquid\";\n  \"gas\";\n  \"none\" -> \"solid\" [ color=\"red\" ; label=\"init\" ];\n  \"solid\" -> \"liquid\" [ color=\"red\" ; label=\"melt\" ];\n  \"liquid\" -> \"solid\" [ color=\"green\" ; label=\"freeze\" ];\n  \"liquid\" -> \"gas\" [ color=\"red\" ; label=\"vaporize\" ];\n  \"gas\" -> \"liquid\" [ color=\"green\" ; label=\"condense\" ];\n}`)\n})\n\n//=================================================================================================\n"
  },
  {
    "path": "test/plugins.js",
    "content": "import test            from 'ava'\nimport StateMachine    from '../src/app'\nimport LifecycleLogger from './helpers/lifecycle_logger'\n\n//-------------------------------------------------------------------------------------------------\n\ntest('an empty plugin object', t => {\n\n  var plugin = {\n        init: function(instance) {\n          instance.plugged = true\n        }\n      };\n\n  var fsm = new StateMachine({\n        plugins: [ plugin ]\n      });\n\n  t.is(fsm.state, 'none')\n  t.is(fsm.plugged, true)\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('an empty plugin function', t => {\n\n  var plugin = function() {\n    return {\n      init: function(instance) {\n        instance.plugged = true\n      }\n    }\n  };\n\n  var fsm = new StateMachine({\n    plugins: [ plugin ]\n  });\n\n  t.is(fsm.state, 'none')\n  t.is(fsm.plugged, true)\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('an empty plugin function with configuration', t => {\n\n  var plugin = function(value) {\n    return {\n      init: function(instance) {\n        instance.plugged = value\n      }\n    }\n  };\n\n  var fsm = new StateMachine({\n    plugins: [ new plugin(42) ]\n  });\n\n  t.is(fsm.state, 'none')\n  t.is(fsm.plugged, 42)\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('plugin can add methods', t => {\n\n  var plugin = {\n    methods: {\n      foo: function() { return 'FOO' },\n      bar: function() { return 'BAR' }\n    }\n  };\n\n  var fsm = new StateMachine({\n    plugins: [ plugin ]\n  });\n\n  t.is(fsm.state, 'none')\n  t.is(fsm.foo(), 'FOO')\n  t.is(fsm.bar(), 'BAR')\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('plugin can add properties', t => {\n\n  var plugin = {\n    properties: {\n      color: { get: function() { return 'red' } }\n    }\n  };\n\n  var fsm = new StateMachine({\n    plugins: [ plugin ]\n  });\n\n  t.is(fsm.state, 'none')\n  t.is(fsm.color, 'red')\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('plugin lifecycle hook', t => {\n\n  var plugin = {\n\n    init: function(instance) {\n      instance.logger = new LifecycleLogger();\n    },\n\n    lifecycle: function(instance, lifecycle) {\n      instance.logger(lifecycle)\n    }\n\n  };\n\n  var fsm = new StateMachine({\n    transitions: [\n      { name: 'step', from: 'none', to: 'complete' }\n    ],\n    plugins: [ plugin ]\n  });\n\n  t.is(fsm.state, 'none')\n  t.deepEqual(fsm.logger.log, [])\n\n  fsm.step()\n\n  t.is(fsm.state, 'complete')\n  t.deepEqual(fsm.logger.log, [\n    { event: 'onBeforeTransition', transition: 'step', from: 'none', to: 'complete', current: 'none' },\n    { event: 'onBeforeStep',       transition: 'step', from: 'none', to: 'complete', current: 'none' },\n    { event: 'onLeaveState',       transition: 'step', from: 'none', to: 'complete', current: 'none' },\n    { event: 'onLeaveNone',        transition: 'step', from: 'none', to: 'complete', current: 'none' },\n    { event: 'onTransition',       transition: 'step', from: 'none', to: 'complete', current: 'none' },\n    { event: 'onEnterState',       transition: 'step', from: 'none', to: 'complete', current: 'complete' },\n    { event: 'onEnterComplete',    transition: 'step', from: 'none', to: 'complete', current: 'complete' },\n    { event: 'onComplete',         transition: 'step', from: 'none', to: 'complete', current: 'complete' },\n    { event: 'onAfterTransition',  transition: 'step', from: 'none', to: 'complete', current: 'complete' },\n    { event: 'onAfterStep',        transition: 'step', from: 'none', to: 'complete', current: 'complete' },\n    { event: 'onStep',             transition: 'step', from: 'none', to: 'complete', current: 'complete' }\n  ])\n\n})\n\n//-------------------------------------------------------------------------------------------------\n"
  },
  {
    "path": "test/transitions.js",
    "content": "import test            from 'ava'\nimport StateMachine    from '../src/app'\nimport LifecycleLogger from './helpers/lifecycle_logger'\n\n//-----------------------------------------------------------------------------\n\ntest('basic transition from state to state', t => {\n\n  var fsm = new StateMachine({\n    init: 'A',\n    transitions: [\n      { name: 'step1', from: 'A', to: 'B' },\n      { name: 'step2', from: 'B', to: 'C' },\n      { name: 'step3', from: 'C', to: 'D' }\n    ]\n  });\n\n  t.is(fsm.state, 'A')\n\n  fsm.step1(); t.is(fsm.state, 'B')\n  fsm.step2(); t.is(fsm.state, 'C')\n  fsm.step3(); t.is(fsm.state, 'D')\n\n})\n\n//-----------------------------------------------------------------------------\n\ntest('multiple transitions with same name', t => {\n\n  var fsm = new StateMachine({\n    init: 'hungry',\n    transitions: [\n      { name: 'eat',  from: 'hungry',    to: 'satisfied' },\n      { name: 'eat',  from: 'satisfied', to: 'full'      },\n      { name: 'eat',  from: 'full',      to: 'sick'      },\n      { name: 'rest', from: '*',         to: 'hungry'    }\n    ]\n  });\n\n  t.is(fsm.state, 'hungry')\n  t.is(fsm.can('eat'),  true)\n  t.is(fsm.can('rest'), true)\n\n  fsm.eat()\n\n  t.is(fsm.state, 'satisfied')\n  t.is(fsm.can('eat'),  true)\n  t.is(fsm.can('rest'), true)\n\n  fsm.eat()\n\n  t.is(fsm.state, 'full')\n  t.is(fsm.can('eat'),  true)\n  t.is(fsm.can('rest'), true)\n\n  fsm.eat()\n\n  t.is(fsm.state, 'sick')\n  t.is(fsm.can('eat'),  false)\n  t.is(fsm.can('rest'), true)\n\n  fsm.rest()\n\n  t.is(fsm.state, 'hungry')\n  t.is(fsm.can('eat'),  true)\n  t.is(fsm.can('rest'), true)\n\n})\n\n//-----------------------------------------------------------------------------\n\ntest('transitions with multiple from states', t => {\n\n  var fsm = new StateMachine({\n    transitions: [\n      { name: 'start', from: 'none',              to: 'green'  },\n      { name: 'warn',  from: ['green', 'red'],    to: 'yellow' },\n      { name: 'panic', from: ['green', 'yellow'], to: 'red'    },\n      { name: 'clear', from: ['red', 'yellow'],   to: 'green'  }\n    ]\n  });\n\n  t.deepEqual(fsm.allStates(),      [ 'none', 'green', 'yellow', 'red'  ])\n  t.deepEqual(fsm.allTransitions(), [ 'start', 'warn', 'panic', 'clear' ])\n\n  t.is(fsm.state, 'none')\n  t.is(fsm.can('start'), true)\n  t.is(fsm.can('warn'),  false)\n  t.is(fsm.can('panic'), false)\n  t.is(fsm.can('clear'), false)\n  t.deepEqual(fsm.transitions(), ['start'])\n\n  fsm.start()\n  t.is(fsm.state, 'green')\n  t.is(fsm.can('start'), false)\n  t.is(fsm.can('warn'),  true)\n  t.is(fsm.can('panic'), true)\n  t.is(fsm.can('clear'), false)\n  t.deepEqual(fsm.transitions(), ['warn', 'panic'])\n\n  fsm.warn()\n  t.is(fsm.state, 'yellow')\n  t.is(fsm.can('start'), false)\n  t.is(fsm.can('warn'),  false)\n  t.is(fsm.can('panic'), true)\n  t.is(fsm.can('clear'), true)\n  t.deepEqual(fsm.transitions(), ['panic', 'clear'])\n\n  fsm.panic()\n  t.is(fsm.state, 'red')\n  t.is(fsm.can('start'), false)\n  t.is(fsm.can('warn'),  true)\n  t.is(fsm.can('panic'), false)\n  t.is(fsm.can('clear'), true)\n  t.deepEqual(fsm.transitions(), ['warn', 'clear'])\n\n  fsm.clear()\n  t.is(fsm.state, 'green')\n  t.is(fsm.can('start'), false)\n  t.is(fsm.can('warn'),  true)\n  t.is(fsm.can('panic'), true)\n  t.is(fsm.can('clear'), false)\n  t.deepEqual(fsm.transitions(), ['warn', 'panic'])\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest(\"transitions that dont change state, dont trigger enter/leave lifecycle events\", t => {\n\n  var logger = new LifecycleLogger(),\n      fsm = new StateMachine({\n        transitions: [\n          { name: 'noop', from: 'none', to: 'none' }\n        ],\n        methods: {\n          onBeforeTransition: logger,\n          onBeforeNoop:       logger,\n          onLeaveState:       logger,\n          onLeaveNone:        logger,\n          onTransition:       logger,\n          onEnterState:       logger,\n          onEnterNone:        logger,\n          onNone:             logger,\n          onAfterTransition:  logger,\n          onAfterNoop:        logger,\n          onNoop:             logger\n        }\n      })\n\n  t.is(fsm.state, 'none')\n  t.deepEqual(logger.log, [])\n\n  fsm.noop()\n\n  t.is(fsm.state, 'none')\n  t.deepEqual(logger.log, [\n    { event: 'onBeforeTransition', transition: 'noop', from: 'none', to: 'none', current: 'none' },\n    { event: 'onBeforeNoop',       transition: 'noop', from: 'none', to: 'none', current: 'none' },\n    { event: 'onTransition',       transition: 'noop', from: 'none', to: 'none', current: 'none' },\n    { event: 'onAfterTransition',  transition: 'noop', from: 'none', to: 'none', current: 'none' },\n    { event: 'onAfterNoop',        transition: 'noop', from: 'none', to: 'none', current: 'none' },\n    { event: 'onNoop',             transition: 'noop', from: 'none', to: 'none', current: 'none' }\n  ])\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest(\"transitions that dont change state, can be configured to trigger enter/leave lifecycle events\", t => {\n\n  var logger = new LifecycleLogger(),\n      fsm = new StateMachine({\n        observeUnchangedState: true,\n        transitions: [\n          { name: 'noop', from: 'none', to: 'none' }\n        ],\n        methods: {\n          onBeforeTransition: logger,\n          onBeforeNoop:       logger,\n          onLeaveState:       logger,\n          onLeaveNone:        logger,\n          onTransition:       logger,\n          onEnterState:       logger,\n          onEnterNone:        logger,\n          onNone:             logger,\n          onAfterTransition:  logger,\n          onAfterNoop:        logger,\n          onNoop:             logger\n        }\n      })\n\n  t.is(fsm.state, 'none')\n  t.deepEqual(logger.log, [])\n\n  fsm.noop()\n\n  t.is(fsm.state, 'none')\n  t.deepEqual(logger.log, [\n    { event: 'onBeforeTransition', transition: 'noop', from: 'none', to: 'none', current: 'none' },\n    { event: 'onBeforeNoop',       transition: 'noop', from: 'none', to: 'none', current: 'none' },\n    { event: 'onLeaveState',       transition: 'noop', from: 'none', to: 'none', current: 'none' },\n    { event: 'onLeaveNone',        transition: 'noop', from: 'none', to: 'none', current: 'none' },\n    { event: 'onTransition',       transition: 'noop', from: 'none', to: 'none', current: 'none' },\n    { event: 'onEnterState',       transition: 'noop', from: 'none', to: 'none', current: 'none' },\n    { event: 'onEnterNone',        transition: 'noop', from: 'none', to: 'none', current: 'none' },\n    { event: 'onNone',             transition: 'noop', from: 'none', to: 'none', current: 'none' },\n    { event: 'onAfterTransition',  transition: 'noop', from: 'none', to: 'none', current: 'none' },\n    { event: 'onAfterNoop',        transition: 'noop', from: 'none', to: 'none', current: 'none' },\n    { event: 'onNoop',             transition: 'noop', from: 'none', to: 'none', current: 'none' }\n  ])\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest(\"transition methods with dash or underscore are camelized\", t => {\n\n  var fsm = new StateMachine({\n        init: 'A',\n        transitions: [\n          { name: 'do-with-dash',       from: 'A', to: 'B' },\n          { name: 'do_with_underscore', from: 'B', to: 'C' },\n          { name: 'doAlreadyCamelized', from: 'C', to: 'D' }\n        ]\n      });\n\n                            t.is(fsm.state, 'A')\n  fsm.doWithDash();         t.is(fsm.state, 'B')\n  fsm.doWithUnderscore();   t.is(fsm.state, 'C')\n  fsm.doAlreadyCamelized(); t.is(fsm.state, 'D')\n\n})\n\n//-------------------------------------------------------------------------------------------------\n\ntest('conditional transitions', t => {\n\n  var logger = new LifecycleLogger(),\n      fsm = new StateMachine({\n        init: 'A',\n        transitions: [\n          { name: 'step', from: '*', to: function(n) { return this.skip(n) } },\n        ],\n        methods: {\n          skip: function(amount) {\n            var code = this.state.charCodeAt(0);\n            return String.fromCharCode(code + (amount || 1));\n          },\n          onBeforeTransition: logger,\n          onBeforeInit:       logger,\n          onBeforeStep:       logger,\n          onLeaveState:       logger,\n          onLeaveNone:        logger,\n          onLeaveA:           logger,\n          onLeaveB:           logger,\n          onLeaveG:           logger,\n          onTransition:       logger,\n          onEnterState:       logger,\n          onEnterNone:        logger,\n          onEnterA:           logger,\n          onEnterB:           logger,\n          onEnterG:           logger,\n          onAfterTransition:  logger,\n          onAfterInit:        logger,\n          onAfterStep:        logger\n        }\n      });\n\n  t.is(fsm.state, 'A')\n\n  t.deepEqual(fsm.allStates(), [ 'none', 'A' ]);\n\n  fsm.step();  t.is(fsm.state, 'B'); t.deepEqual(fsm.allStates(), [ 'none', 'A', 'B' ])\n  fsm.step(5); t.is(fsm.state, 'G'); t.deepEqual(fsm.allStates(), [ 'none', 'A', 'B', 'G' ])\n\n  t.deepEqual(logger.log, [\n    { event: 'onBeforeTransition', transition: 'init', from: 'none', to: 'A', current: 'none'           },\n    { event: 'onBeforeInit',       transition: 'init', from: 'none', to: 'A', current: 'none'           },\n    { event: 'onLeaveState',       transition: 'init', from: 'none', to: 'A', current: 'none'           },\n    { event: 'onLeaveNone',        transition: 'init', from: 'none', to: 'A', current: 'none'           },\n    { event: 'onTransition',       transition: 'init', from: 'none', to: 'A', current: 'none'           },\n    { event: 'onEnterState',       transition: 'init', from: 'none', to: 'A', current: 'A'              },\n    { event: 'onEnterA',           transition: 'init', from: 'none', to: 'A', current: 'A'              },\n    { event: 'onAfterTransition',  transition: 'init', from: 'none', to: 'A', current: 'A'              },\n    { event: 'onAfterInit',        transition: 'init', from: 'none', to: 'A', current: 'A'              },\n\n    { event: 'onBeforeTransition', transition: 'step', from: 'A',    to: 'B', current: 'A'              },\n    { event: 'onBeforeStep',       transition: 'step', from: 'A',    to: 'B', current: 'A'              },\n    { event: 'onLeaveState',       transition: 'step', from: 'A',    to: 'B', current: 'A'              },\n    { event: 'onLeaveA',           transition: 'step', from: 'A',    to: 'B', current: 'A'              },\n    { event: 'onTransition',       transition: 'step', from: 'A',    to: 'B', current: 'A'              },\n    { event: 'onEnterState',       transition: 'step', from: 'A',    to: 'B', current: 'B'              },\n    { event: 'onEnterB',           transition: 'step', from: 'A',    to: 'B', current: 'B'              },\n    { event: 'onAfterTransition',  transition: 'step', from: 'A',    to: 'B', current: 'B'              },\n    { event: 'onAfterStep',        transition: 'step', from: 'A',    to: 'B', current: 'B'              },\n\n    { event: 'onBeforeTransition', transition: 'step', from: 'B',    to: 'G', current: 'B', args: [ 5 ] },\n    { event: 'onBeforeStep',       transition: 'step', from: 'B',    to: 'G', current: 'B', args: [ 5 ] },\n    { event: 'onLeaveState',       transition: 'step', from: 'B',    to: 'G', current: 'B', args: [ 5 ] },\n    { event: 'onLeaveB',           transition: 'step', from: 'B',    to: 'G', current: 'B', args: [ 5 ] },\n    { event: 'onTransition',       transition: 'step', from: 'B',    to: 'G', current: 'B', args: [ 5 ] },\n    { event: 'onEnterState',       transition: 'step', from: 'B',    to: 'G', current: 'G', args: [ 5 ] },\n    { event: 'onEnterG',           transition: 'step', from: 'B',    to: 'G', current: 'G', args: [ 5 ] },\n    { event: 'onAfterTransition',  transition: 'step', from: 'B',    to: 'G', current: 'G', args: [ 5 ] },\n    { event: 'onAfterStep',        transition: 'step', from: 'B',    to: 'G', current: 'G', args: [ 5 ] },\n  ])\n\n})\n\n//-------------------------------------------------------------------------------------------------\n"
  },
  {
    "path": "test/util/camelize.js",
    "content": "import test     from 'ava';\nimport camelize from '../../src/util/camelize';\n\ntest('camelize', t => {\n  t.is(camelize(\"\"),                           \"\");\n  t.is(camelize(\"word\"),                       \"word\");\n  t.is(camelize(\"Word\"),                       \"word\");\n  t.is(camelize(\"WORD\"),                       \"word\");\n  t.is(camelize(\"word-with-dash\"),             \"wordWithDash\");\n  t.is(camelize(\"word_with_underscore\"),       \"wordWithUnderscore\");\n  t.is(camelize(\"word--with--double--dash\"),   \"wordWithDoubleDash\");\n  t.is(camelize(\"word_WITH_mixed_CASE\"),       \"wordWithMixedCase\");\n  t.is(camelize(\"alreadyCamelizedString\"),     \"alreadyCamelizedString\");\n});\n"
  },
  {
    "path": "test/util/mixin.js",
    "content": "import test from 'ava';\nimport mixin from '../../src/util/mixin';\n\n//-------------------------------------------------------------------------------------------------\n\ntest('mixin', t => {\n\n  var a = { first: 'Jake',  key: 'a' },\n      b = { last: 'Gordon', key: 'b' };\n\n  t.deepEqual(mixin({}, a),    { first: 'Jake',   key: 'a' });\n  t.deepEqual(mixin({}, b),    { last:  'Gordon', key: 'b' });\n  t.deepEqual(mixin({}, a, b), { first: 'Jake', last: 'Gordon', key: 'b' });\n  t.deepEqual(mixin({}, b, a), { first: 'Jake', last: 'Gordon', key: 'a' });\n\n});\n\n//-------------------------------------------------------------------------------------------------\n\ntest('mixin only mixes in owned properties', t => {\n\n  var MyClass = function(name) { this.name = name }\n\n  MyClass.prototype = {\n    answer: 42\n  }\n\n  var a = new MyClass('a'),\n      b = new MyClass('b');\n\n  t.is(a.name,  'a');\n  t.is(a.answer, 42);\n  t.is(b.name,   'b');\n  t.is(b.answer, 42);\n\n  t.is(a.hasOwnProperty('name'),   true);\n  t.is(a.hasOwnProperty('answer'), false);\n  t.is(b.hasOwnProperty('name'),   true);\n  t.is(b.hasOwnProperty('answer'), false);\n\n  t.deepEqual(mixin({}, a),    { name: 'a' });\n  t.deepEqual(mixin({}, b),    { name: 'b' });\n  t.deepEqual(mixin({}, a, b), { name: 'b' });\n  t.deepEqual(mixin({}, b, a), { name: 'a' });\n\n  b.answer = 99;\n\n  t.is(a.name,  'a');\n  t.is(a.answer, 42);\n  t.is(b.name,   'b');\n  t.is(b.answer, 99);\n\n  t.is(a.hasOwnProperty('name'),   true);\n  t.is(a.hasOwnProperty('answer'), false);\n  t.is(b.hasOwnProperty('name'),   true);\n  t.is(b.hasOwnProperty('answer'), true);\n\n  t.deepEqual(mixin({}, a),    { name: 'a'             });\n  t.deepEqual(mixin({}, b),    { name: 'b', answer: 99 });\n  t.deepEqual(mixin({}, a, b), { name: 'b', answer: 99 });\n  t.deepEqual(mixin({}, b, a), { name: 'a', answer: 99 });\n\n});\n\n//-------------------------------------------------------------------------------------------------\n"
  },
  {
    "path": "test/wildcards.js",
    "content": "import test         from 'ava'\nimport StateMachine from '../src/app'\n\n//-----------------------------------------------------------------------------\n\ntest('wildcard :from allows transition from any state', t => {\n\n  var fsm = new StateMachine({\n    init: 'stopped',\n    transitions: [\n      { name: 'prepare', from: 'stopped',      to: 'ready'   },\n      { name: 'start',   from: 'ready',        to: 'running' },\n      { name: 'resume',  from: 'paused',       to: 'running' },\n      { name: 'pause',   from: 'running',      to: 'paused'  },\n      { name: 'stop',    from: '*',            to: 'stopped' }\n  ]});\n\n  t.is(fsm.state, 'stopped', \"initial state should be stopped\");\n\n  fsm.prepare(); t.is(fsm.state, 'ready')\n  fsm.stop();    t.is(fsm.state, 'stopped')\n\n  fsm.prepare(); t.is(fsm.state, 'ready')\n  fsm.start();   t.is(fsm.state, 'running')\n  fsm.stop();    t.is(fsm.state, 'stopped')\n\n  fsm.prepare(); t.is(fsm.state, 'ready')\n  fsm.start();   t.is(fsm.state, 'running')\n  fsm.pause();   t.is(fsm.state, 'paused')\n  fsm.stop();    t.is(fsm.state, 'stopped')\n  fsm.stop();    t.is(fsm.state, 'stopped')\n\n                 t.deepEqual(fsm.transitions(), [\"prepare\", \"stop\"], \"ensure wildcard transition (stop) is included in available transitions\")\n  fsm.prepare(); t.deepEqual(fsm.transitions(), [\"start\",   \"stop\"], \"ensure wildcard transition (stop) is included in available transitions\")\n  fsm.start();   t.deepEqual(fsm.transitions(), [\"pause\",   \"stop\"], \"ensure wildcard transition (stop) is included in available transitions\")\n  fsm.stop();    t.deepEqual(fsm.transitions(), [\"prepare\", \"stop\"], \"ensure wildcard transition (stop) is included in available transitions\")\n\n})\n\n//-----------------------------------------------------------------------------\n\ntest('missing :from allows transition from any state', t => {\n\n  var fsm = new StateMachine({\n    init: 'stopped',\n    transitions: [\n      { name: 'prepare', from: 'stopped',      to: 'ready'   },\n      { name: 'start',   from: 'ready',        to: 'running' },\n      { name: 'resume',  from: 'paused',       to: 'running' },\n      { name: 'pause',   from: 'running',      to: 'paused'  },\n      { name: 'stop',    /* any from state */  to: 'stopped' }\n  ]});\n\n  t.is(fsm.state, 'stopped', \"initial state should be stopped\")\n\n  fsm.prepare(); t.is(fsm.state, 'ready')\n  fsm.stop();    t.is(fsm.state, 'stopped')\n\n  fsm.prepare(); t.is(fsm.state, 'ready')\n  fsm.start();   t.is(fsm.state, 'running')\n  fsm.stop();    t.is(fsm.state, 'stopped')\n\n  fsm.prepare(); t.is(fsm.state, 'ready')\n  fsm.start();   t.is(fsm.state, 'running')\n  fsm.pause();   t.is(fsm.state, 'paused')\n  fsm.stop();    t.is(fsm.state, 'stopped')\n\n                 t.deepEqual(fsm.transitions(), [\"prepare\", \"stop\"], \"ensure missing :from transition (stop) is included in available transitions\")\n  fsm.prepare(); t.deepEqual(fsm.transitions(), [\"start\",   \"stop\"], \"ensure missing :from transition (stop) is included in available transitions\")\n  fsm.start();   t.deepEqual(fsm.transitions(), [\"pause\",   \"stop\"], \"ensure missing :from transition (stop) is included in available transitions\")\n  fsm.stop();    t.deepEqual(fsm.transitions(), [\"prepare\", \"stop\"], \"ensure missing :from transition (stop) is included in available transitions\")\n\n})\n\n//-----------------------------------------------------------------------------\n\ntest('wildcard :from allows transition to a state that is never declared in any other :from transition ', t => {\n\n  var fsm = new StateMachine({\n        transitions: [\n          { name: 'step',  from: 'none', to: 'mystery'  }, // NOTE: 'mystery' is only ever declared in :to, never :from\n          { name: 'other', from: '*',    to: 'complete' }\n        ]\n      });\n\n  t.is(fsm.state, 'none')\n  t.is(fsm.can('step'),  true)\n  t.is(fsm.can('other'), true)\n\n  fsm.step()\n\n  t.is(fsm.state, 'mystery')\n  t.is(fsm.can('step'), false)\n  t.is(fsm.can('other'), true)\n\n})\n\n//-----------------------------------------------------------------------------\n\ntest('wildcard :to allows no-op transitions', t => {\n\n  var fsm = new StateMachine({\n    init: 'A',\n    transitions: [\n      { name: 'stayA', from: 'A', to: '*' },\n      { name: 'stayB', from: 'B', to: '*' },\n      { name: 'noop',  from: '*', to: '*' },\n      { name: 'step',  from: 'A', to: 'B' }\n    ]\n  });\n\n  t.is(fsm.state, 'A')\n  t.is(fsm.can('noop'),  true)\n  t.is(fsm.can('step'),  true)\n  t.is(fsm.can('stayA'), true)\n  t.is(fsm.can('stayB'), false)\n\n  fsm.stayA(); t.is(fsm.state, 'A')\n  fsm.noop();  t.is(fsm.state, 'A')\n\n  fsm.step();\n\n  t.is(fsm.state, 'B')\n  t.is(fsm.can('noop'),  true)\n  t.is(fsm.can('step'),  false)\n  t.is(fsm.can('stayA'), false)\n  t.is(fsm.can('stayB'), true)\n\n  fsm.stayB(); t.is(fsm.state, 'B')\n  fsm.noop();  t.is(fsm.state, 'B')\n\n})\n\n//-----------------------------------------------------------------------------\n\ntest('missing :to allows no-op transitions', t => {\n\n  var fsm = new StateMachine({\n    init: 'A',\n    transitions: [\n      { name: 'stayA', from: 'A'  /* no-op */ },\n      { name: 'stayB', from: 'B'  /* no-op */ },\n      { name: 'noop',  from: '*'  /* no-op */ },\n      { name: 'step',  from: 'A', to: 'B'     }\n    ]\n  });\n\n  t.is(fsm.state, 'A')\n  t.is(fsm.can('noop'),  true)\n  t.is(fsm.can('step'),  true)\n  t.is(fsm.can('stayA'), true)\n  t.is(fsm.can('stayB'), false)\n\n  fsm.stayA(); t.is(fsm.state, 'A')\n  fsm.noop();  t.is(fsm.state, 'A')\n\n  fsm.step();\n\n  t.is(fsm.state, 'B')\n  t.is(fsm.can('noop'),  true)\n  t.is(fsm.can('step'),  false)\n  t.is(fsm.can('stayA'), false)\n  t.is(fsm.can('stayB'), true)\n\n  fsm.stayB(); t.is(fsm.state, 'B')\n  fsm.noop();  t.is(fsm.state, 'B')\n\n})\n\n//-----------------------------------------------------------------------------\n\ntest('no-op transitions with multiple from states', t => {\n\n  var fsm = new StateMachine({\n    init: 'A',\n    transitions: [\n      { name: 'step',  from: 'A',        to: 'B' },\n      { name: 'noop1', from: ['A', 'B']  /* no-op */ },\n      { name: 'noop2', from: '*'         /* no-op */ },\n      { name: 'noop3', from: ['A', 'B'], to: '*' },\n      { name: 'noop4', from: '*',        to: '*' }\n    ]\n  });\n\n  t.is(fsm.state, 'A')\n  t.is(fsm.can('step'),  true)\n  t.is(fsm.can('noop1'), true)\n  t.is(fsm.can('noop2'), true)\n  t.is(fsm.can('noop3'), true)\n  t.is(fsm.can('noop4'), true)\n\n  fsm.noop1(); t.is(fsm.state, 'A')\n  fsm.noop2(); t.is(fsm.state, 'A')\n  fsm.noop3(); t.is(fsm.state, 'A')\n  fsm.noop4(); t.is(fsm.state, 'A')\n\n  fsm.step();\n  t.is(fsm.state, 'B')\n  t.is(fsm.can('step'), false)\n  t.is(fsm.can('noop1'), true)\n  t.is(fsm.can('noop2'), true)\n  t.is(fsm.can('noop3'), true)\n  t.is(fsm.can('noop4'), true)\n\n  fsm.noop1(); t.is(fsm.state, 'B')\n  fsm.noop2(); t.is(fsm.state, 'B')\n  fsm.noop3(); t.is(fsm.state, 'B')\n  fsm.noop4(); t.is(fsm.state, 'B')\n\n})\n\n//-----------------------------------------------------------------------------\n"
  },
  {
    "path": "webpack.config.js",
    "content": "module.exports = function(env) {\n\n  'use strict'\n\n  const webpack   = require('webpack'),\n        glob      = require('glob'),\n        path      = require('path'),\n        pascalize = require('pascal-case'),\n        source    = 'src',\n        output    = 'lib',\n        config    = [];\n\n  config.push({\n    name:     'state-machine',\n    library:  'StateMachine',\n    entry:    'app'\n  })\n\n  glob.sync(\"src/plugin/*.js\").forEach(function(plugin) {\n    const name = path.basename(plugin, '.js');\n    config.push({\n      library:  pascalize('state-machine-' + name),\n      entry:    'plugin/' + name,\n      name:     name\n    })\n  });\n\n  return config.map(function(cfg) {\n    return {\n      entry: cfg.entry,\n      resolve: {\n        modules: [ source ]\n      },\n      output: {\n        filename: path.join(output, cfg.name + '.js'),\n        library: cfg.library,\n        libraryTarget: 'umd',\n        umdNamedDefine: true\n      }\n    }\n  });\n\n}\n"
  }
]