I wrote a simple Javascript State Machine.
It is intended for keeping track of what the current state is for my heavy AJAX app at work.
I also learned a lot about OO in javascript without using jQuery or the prototype framework, by using prototypes. I wanted to do that so that it would be framework independent.
Below is a simple example of how I use the state machine, and shows a bit of how I connected it with Really Simple History
I have included in this example some comments to show my thinking throughout, and perhaps how to use it.
Site.prototype = new StateMachine(); Site.prototype.states = { Begin: { enter: function(){ // do some animation }, exit: function(){ // hide some stuff } }, Page: { enter: function(){ // show some page }, exit: function(){ // hide it }, change: function(page){ // example of some event this.pages.change_page(page); // events can call events this.handleEvent('setURL'); }, setURL: function(){ //example of how I integrate with really simple history dhtmlHistory.add(['pages'].concat(this.pages.current_page.split('/')).join('.').toLowerCase()); } } }; // These are some call backs I used for debugging with firebug to ensure // that events were firing Site.prototype.beforeEvent = function(name){ console.log('begin event: ', name); } Site.prototype.afterEvent = function(name){ console.log('end event: ', name); } Site.prototype.beforeStateChange = function(to, from){ console.log('begin state change to: ', to, ' from: ', from); // as an example of intercepting, I can stop a state from changing by returning false if(to == 'Page' && this.pages.blocker == 'Page'){ // some logic return false; } return true; } Site.prototype.afterStateChange = function(to, from){ console.log('end state change to: ', to, ' from: ', from); } // The actual defenition. Instance vars can be set in it. function Site(){ this.pages = new Page(); } // global scope, so that it can be accessed globally (as it wouldn't normally // be because the scope that I actually instantiate it inside prorotype's window onload) var Davis; // Once again, just an example of how I integrate it with Really Simple History function historyChange(newLocation, historyData) { var hash = newLocation.split('.'); var newState = hash.shift().capitalize(); if(newState == '') newState = 'Begin'; if(Davis.changeState(newState)) { if(hash.length > 0) Davis.handleEvent('change', hash.join('/')); } } // This window observe for onload is prototype specific... But you can achieve the same other ways. Event.observe(window, 'load', function() { Davis = new Site(); // my requirements need me to initialize after load // Really Simple History stuff dhtmlHistory.initialize(historyChange); historyChange(dhtmlHistory.currentLocation || ''); });
While implementing this, I also learned a bit about bookmarking and enabling the back button of ajax states. Really Simple History has worked ok for me, but it has some quirks… some of which I haven’t solved yet (especially in IE) and those bugs may be above.
I also got my first dose of unit testing in javascript… and found some really cool library to assist in the development called newjs . However, at the time of this writing, the unit tests are not up to the current code, as a lot has changed… I will deal with that later.
Anyway, while my implementation surely isn’t the best in the world, I hope someone can find use from it.