Comparing version 1.0.0 to 1.1.0
@@ -36,3 +36,3 @@ { | ||
"main": "Stately.js", | ||
"version": "1.0.0" | ||
"version": "1.1.0" | ||
} |
@@ -8,2 +8,28 @@ ![Stately.js Logo](https://github.com/fschaefer/Stately.js/raw/master/misc/Stately.js.png) | ||
## Installation | ||
In Node.js you can install Stately.js with `npm`: | ||
$ npm install stately.js | ||
and include it to your project by: | ||
var Stately = require('stately.js'); | ||
In browsers you can include it directly by adding it to the document head section: | ||
<script type="text/javascript" src="https://raw.github.com/fschaefer/Stately.js/master/Stately.js"></script> | ||
<script type="text/javascript"> | ||
// use Stately | ||
</script> | ||
or with Asynchronous Module Definition by e.g.: | ||
<script type="text/javascript" src="https://raw.github.com/jrburke/requirejs/master/require.js"></script> | ||
<script type="text/javascript"> | ||
require(['https://raw.github.com/fschaefer/Stately.js/master/Stately.js'], function (Stately) { | ||
// use Stately | ||
}); | ||
</script> | ||
## Usage | ||
@@ -10,0 +36,0 @@ |
111
Stately.js
@@ -21,42 +21,29 @@ /* | ||
var | ||
//helper to identify options type | ||
toString = Object.prototype.toString, | ||
//custom exception for invalid states | ||
InvalidStateError = (function () { | ||
//custom event constructor | ||
function InvalidStateError(message) { | ||
//set error name | ||
this.name = 'InvalidStateError'; | ||
//the error message | ||
this.message = message; | ||
} | ||
//inherit from error object | ||
InvalidStateError.prototype = new Error(); | ||
//set custom error constructor | ||
InvalidStateError.prototype.constructor = InvalidStateError; | ||
//return custom event | ||
return InvalidStateError; | ||
})(); | ||
//constructor | ||
function Stately(statesObject) { | ||
//if statesObject is a function | ||
if (typeof statesObject === 'function') { | ||
//avaluate it | ||
statesObject = statesObject(); | ||
} | ||
//if no valid statesObject provided | ||
if (toString.call(statesObject) !== '[object Object]') { | ||
//bail out | ||
throw new InvalidStateError('Stately.js: Invalid states object: `' + statesObject + '`.'); | ||
@@ -66,18 +53,12 @@ } | ||
var | ||
//current state of the machine | ||
currentState, | ||
//storage for notification callbacks | ||
notificationStore = [], | ||
//notify callbacks about a transition | ||
notify = function () { | ||
//make copy of notification storage | ||
var notifications = notificationStore.slice(); | ||
//walk over stored callbacks | ||
for (var i = 0, l = notifications.length; i < l; i++) { | ||
//and notify them | ||
notifications[i].apply(this, arguments); | ||
@@ -87,94 +68,65 @@ } | ||
//storage for machine states | ||
stateStore = { | ||
//evaluates the current state | ||
getMachineState: function getMachineState() { | ||
//return current state as string | ||
return currentState.name; | ||
}, | ||
//function to transition into another state | ||
setMachineState: function setMachineState(nextState /*, eventName */) { | ||
var | ||
//event that triggered the transition | ||
eventName = arguments[1], | ||
//before state hook | ||
onBeforeState, | ||
//enter state hook | ||
onEnterState, | ||
//leave state hook | ||
onLeaveState, | ||
//store last machine state | ||
lastState = currentState; | ||
//if state machine cannot handle returned state | ||
if (!nextState || !nextState.name || !stateStore[nextState.name]) { | ||
//throw invalid state exception | ||
throw new InvalidStateError('Stately.js: Transitioned into invalid state: `' + setMachineState.caller + '`.'); | ||
} | ||
//transition into next state | ||
currentState = nextState; | ||
//retrieve enter state hook | ||
onBeforeState = stateMachine['onbefore' + currentState.name]; | ||
//if a hook is attached | ||
if (onBeforeState && typeof onBeforeState === 'function') { | ||
//apply it | ||
onBeforeState.call(stateStore, eventName, lastState.name, nextState.name); | ||
} | ||
//retrieve enter state hook | ||
onEnterState = stateMachine['onenter' + currentState.name] || stateMachine['on' + currentState.name]; | ||
//if a hook is attached | ||
if (onEnterState && typeof onEnterState === 'function') { | ||
//apply it | ||
onEnterState.call(stateStore, eventName, lastState.name, nextState.name); | ||
} | ||
//retrieve leave state hook | ||
onLeaveState = stateMachine['onleave' + currentState.name]; | ||
onLeaveState = stateMachine['onleave' + lastState.name]; | ||
//if a hook is attached | ||
if (onLeaveState && typeof onLeaveState === 'function') { | ||
//apply it | ||
onLeaveState.call(stateStore, eventName, lastState.name, nextState.name); | ||
} | ||
//notify notification callbacks about transition | ||
notify.call(stateStore, eventName, lastState.name, nextState.name); | ||
//return the state store | ||
return this; | ||
}, | ||
//function returns the possible events in current state | ||
getMachineEvents: function getMachineEvents() { | ||
//storage for the events in current state | ||
var events = []; | ||
//walk over the events of the current state | ||
for (var property in currentState) { | ||
//ensure to only walk over own properties | ||
if (currentState.hasOwnProperty(property)) { | ||
//if it is an event function | ||
if (typeof currentState[property] === 'function') { | ||
//store it in events storage | ||
events.push(property); | ||
@@ -185,3 +137,2 @@ } | ||
//return the possible events | ||
return events; | ||
@@ -192,32 +143,22 @@ } | ||
//the state machine | ||
stateMachine = { | ||
//copy function to public state machine object | ||
getMachineState: stateStore.getMachineState, | ||
//copy function to public state machine object | ||
getMachineEvents: stateStore.getMachineEvents, | ||
//store a new notification callback | ||
bind: function bind(callback) { | ||
//if we have a new notification callback | ||
if (callback) { | ||
//store it in notification storage | ||
notificationStore.push(callback); | ||
} | ||
//return the state machine | ||
return this; | ||
}, | ||
//remove a notification callback from storage | ||
unbind: function unbind(callback) { | ||
//if no callback is given | ||
if (!callback) { | ||
//reset notification storage | ||
notificationStore = []; | ||
@@ -227,9 +168,6 @@ | ||
//walk over stored callbacks | ||
for (var i = 0, l = notificationStore.length; i < l; i++) { | ||
//if callback is found in notification storage | ||
if (notificationStore[i] === callback) { | ||
//remove it | ||
notificationStore.splice(i, 1); | ||
@@ -240,3 +178,2 @@ } | ||
//return the state machine | ||
return this; | ||
@@ -246,55 +183,38 @@ } | ||
//event decorator factory function | ||
transition = function transition(stateName, eventName, nextEvent) { | ||
//the decorator | ||
return function event() { | ||
var | ||
//before event hook | ||
onBeforeEvent, | ||
//after event hook | ||
onAfterEvent, | ||
//new state machine changed into | ||
nextState, | ||
//return the state machine if no event returns something | ||
eventValue = stateMachine; | ||
//if attached event handler doesn't handle this event | ||
if (stateStore[stateName] !== currentState) { | ||
//try other events in chain | ||
if (nextEvent) { | ||
//let next event function handle this event | ||
eventValue = nextEvent.apply(stateStore, arguments); | ||
} | ||
//or return value of action | ||
return eventValue; | ||
} | ||
//retrieve before event hook | ||
onBeforeEvent = stateMachine['onbefore' + eventName]; | ||
//if a hook is attached | ||
if (onBeforeEvent && typeof onBeforeEvent === 'function') { | ||
//apply it | ||
onBeforeEvent.call(stateStore, eventName, currentState.name, currentState.name); | ||
} | ||
//run action | ||
eventValue = stateStore[stateName][eventName].apply(stateStore, arguments); | ||
//check return value of action | ||
if (typeof eventValue === 'undefined') { | ||
//nothing returned, stay in current state | ||
nextState = currentState; | ||
//return state machine | ||
eventValue = stateMachine; | ||
@@ -304,6 +224,4 @@ | ||
//if state store object is returned ('this' in action function) stay in current state | ||
nextState = (eventValue === stateStore ? currentState : eventValue); | ||
//return state machine | ||
eventValue = stateMachine; | ||
@@ -313,23 +231,16 @@ | ||
//else first element is next state | ||
nextState = eventValue[0]; | ||
//second element is return value | ||
eventValue = eventValue[1]; | ||
} | ||
//retrieve after event hook | ||
onAfterEvent = stateMachine['onafter' + eventName] || stateMachine['on' + eventName]; | ||
//if a hook is attached | ||
if (onAfterEvent && typeof onAfterEvent === 'function') { | ||
//apply it | ||
onAfterEvent.call(stateStore, eventName, currentState.name, nextState.name); | ||
} | ||
//transition into next state | ||
stateStore.setMachineState(nextState, eventName); | ||
//return desired value | ||
return eventValue; | ||
@@ -339,27 +250,18 @@ }; | ||
//walk over states object | ||
for (var stateName in statesObject) { | ||
//check own properties | ||
if (statesObject.hasOwnProperty(stateName)) { | ||
//store states in storage | ||
stateStore[stateName] = statesObject[stateName]; | ||
//walk over events | ||
for (var eventName in stateStore[stateName]) { | ||
//check for own property | ||
if (stateStore[stateName].hasOwnProperty(eventName)) { | ||
//if type is a string, assume it is a state | ||
if (typeof stateStore[stateName][eventName] === 'string') { | ||
//decorate it | ||
stateStore[stateName][eventName] = (function (stateName) { | ||
//with a function | ||
return function event() { | ||
//returning the given state | ||
return this[stateName]; | ||
@@ -371,6 +273,4 @@ }; | ||
//if type function | ||
if (typeof stateStore[stateName][eventName] === 'function') { | ||
//assign decorated events to state machine | ||
stateMachine[eventName] = transition(stateName, eventName, stateMachine[eventName]); | ||
@@ -381,9 +281,6 @@ } | ||
//attach states name to object in storage | ||
stateStore[stateName].name = stateName; | ||
//initial state is the first passed in state | ||
if (!currentState) { | ||
//make initial state the current state | ||
currentState = stateStore[stateName]; | ||
@@ -394,14 +291,10 @@ } | ||
//if there is no initial state | ||
if (!currentState) { | ||
//throw invalid state exception | ||
throw new InvalidStateError('Stately.js: Invalid initial state.'); | ||
} | ||
//return the new state machine | ||
return stateMachine; | ||
} | ||
//a factory for new machines | ||
Stately.machine = function machine(statesObject) { | ||
@@ -411,8 +304,6 @@ return new Stately(statesObject); | ||
//InvalidStateError exception | ||
Stately.InvalidStateError = InvalidStateError; | ||
//export Stately object | ||
return Stately; | ||
}); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
355
54579
482