Skip to content

Instantly share code, notes, and snippets.

@neckro
Created September 19, 2013 20:02
Show Gist options
  • Save neckro/6629052 to your computer and use it in GitHub Desktop.
Save neckro/6629052 to your computer and use it in GitHub Desktop.
state-based event handler helper
// State machine, for event handlers and other mischief
define([], function() { "use strict";
///////////////////////
var stateM = {
create: function(target, config) {
var instance = Object.create(stateM.proto);
instance.statesHash = {};
instance.statesStack = [];
instance.begunStates = {};
instance.listeners = [];
instance.target = target;
instance.config = config;
return instance;
},
proto: {}
};
// Calls the state handler for given phase/state
// Handler is bound to stateM.target.
stateM.proto.callState = function(phase, args) {
var retval, d;
var state = args[0];
// return if appropriate handler doesn't exist
if (
!state ||
!this.config[state] ||
!this.config[state][phase] ||
typeof this.config[state][phase] !== 'function'
) {
return;
}
args = [].slice.call(args, 1);
if (this.debug) {
d = phase + " handler";
if (args[0] && args[0].type) d += ":" + args[0].type;
this.debugPhase(state, d);
}
return this.config[state][phase].apply(this.target, args);
};
// Returns a callback function bound to a state change
stateM.proto.bindFunc = function(phase, args) {
if (!this[phase] || typeof this[phase] !== 'function') return;
args = [].slice.call(args);
args.unshift(this);
return this[phase].bind.apply(this[phase], args);
};
stateM.proto.enter = function(state) {
// return if state was already entered
if (this.statesHash[state]) return;
this.debugPhase(state, 'enter');
this.statesHash[state] = true;
this.currentState = state;
this.statesStack.push(state);
if (!this.begunStates[state]) {
// run begin handler
this.begunStates[state] = true;
this.callState('begin', arguments);
}
return this.callState('enter', arguments);
};
stateM.proto.enterH = function(state) {
return this.bindFunc('enter', arguments);
};
stateM.proto.leave = function(state) {
var retval;
// return if state is not entered
if (!this.statesHash[state]) return;
this.debugPhase(state, 'leave');
this.currentState = this.statesStack.pop();
this.statesHash[state] = false;
return this.callState('leave', arguments);
};
stateM.proto.leaveH = function(state) {
return this.bindFunc('leave', arguments);
};
stateM.proto.change = function(state) {
this.debugPhase(state, 'change');
this.leaveAll(arguments);
return this.enter.apply(this, arguments);
};
stateM.proto.changeH = function(state) {
return this.bindFunc('change', arguments);
};
stateM.proto.leaveAll = function() {
var state, args = [].slice.call(arguments);
while (state = this.statesStack.pop()) {
args[0] = state;
this.leave.apply(this, args);
}
};
stateM.proto.debugPhase = function(state, phase) {
if (!this.debug) return;
console.log('[' + this.statesStack.join(' ') + '] ' + state + '/' + phase);
};
///////////////////////
return stateM; });
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment