Due to the success (and quality) of Asual's SWFAddress, I've decided to stop work on this project. SWFAddress is now capable of all the things that StateManager is, so it doesn't seem very beneficial to the open source community to continue development on StateManager. The source code and examples will remain available but no longer be updated, but keep checking xa for other open source code!

Within days after posting the above message, Rostislav Hristov (the creator of SWFAddress) sent me a very flattering email asking me to join the SWFAddress team. While I probably won't be able to contribute too much time to the project, Rostislav has informed me that he has incorporated my Safari findings into the SWFAddress code base.

After some input from Geoff Stearns, I decided to tweak the JavaScript file that powers the ActionScript StateManager class so that it can also be used to manage your AJAX states. If you've read the article that introduced the StateManager, you should be more than comfortable using it in an AJAX environment. The StateManager source and example files zip file includes two AJAX examples: the first shows how to create an AJAX application that utilizes state management, and the second shows how to implement state management into an already-existing project. However, since the usage is nearly identical to the ActionScript StateManager, I'll just give a brief walkthrough here. (In fact, the only real difference is how the event handlers are defined.)

Download the StateManager source and examples as a zip file or from my as3 subversion repository for all you need to start managing states in both your Flash and AJAX web applications or check out the AJAX StateManager in action.

Step 1: Import the Script

This time, all you need is in the file "statemanager.js". This is the exact same JavaScript file that the AS2 StateManager uses, so don't worry about getting them mixed up or anything. Add it to your html file with a <script> tag:

<script type="text/javascript" src="statemanager.js"></script>

You may also want to make a reference to the "class", so you don't have to keep typing the namespace out:

var StateManager = EXANIMO.managers.StateManager;

Step 2: Initializing the StateManager

Once the DOM is ready to be written to, you'll need to initialize the StateManager. In other words, call the initialize method in the window's onLoad handler:

window.onload = function()
{
    StateManager.initialize();
}

Since the StateManager uses an iframe to keep track of states in Internet Explorer, it's important that you not call initialize until the DOM can be written to.

In previous versions of the StateManager, the class would initialize itself as soon as the body tag was present. However, in large HTML files, the body tag is sometimes present before it becomes writable. Though the new version requires the user to manually initialize the StateManager, it avoids this problem without making assumptions about the user's load handling.

Step 3: Setting a State

Set a state by passing the ID of the state you'd like to set to the setState function. The following illustrates how to set the state "about" when the user clicks on the hyperlink with id "about-link".

window.onload = function()
{
    StateManager.initialize();

    document.getElementById('about-link').onclick = function()
    {
        StateManager.setState('about');
        return false;
    }
}

As in the ActionScript version of the StateManager, there is one special state: the default state of your application. This state is represented by an empty hash in the browser's address bar. The ID of the default state is stored in the property StateManager.defaultStateID. So, if you want a button to return your application to the default state (as we do in "ajax-1.fla", you would use the following code:

window.onload = function()
{
    StateManager.initialize();

    document.getElementById('home-link').onclick = function()
    {
        StateManager.setState(StateManager.defaultStateID);
        return false;
    }

    document.getElementById('about-link').onclick = function()
    {
        StateManager.setState('about');
        return false;
    }
}

In previous versions of the StateManager, the ID of the default state was always "home". In the new version, it defaults to "defaultState", but you can chose anything you want. To change it, simply set the defaultStateID property before you initialize the StateManager. But remember — the ID you chose will never show up in the URL; the default state is always represented with an empty hash value.

Step 4: Reacting to a State Change

By setting the StateManager's stateChange handler, we can determine how our application will react to a change of state. The handler recieves an object with an id property that holds the ID of the new state. In the code below, the contents of a heading tag are updated to reflect our state. We can also update the browser's title bar with the StateManager.setTitle function.

StateManager.onstatechange = function(e)
{
    document.getElementsByTagName('h1')[0].innerHTML =
        'The id of the current state is: ' + e.id;
    StateManager.setTitle('My Website :: ' + e.id);
}

Users familiar with prior versions of the StateManager should note that its event handler names have been changed to reflect JavaScript conventions. The "stateChangeHandler", "stateSetHandler", and "stateRevisitedHandler" functions have been renamed to "onstatechange", "onstateset", and "onstaterevisit", respectively.

Deja Vu?

That's it! Hopefully you're well on your way to managing states in both AJAX and Flash applications. If you have any ideas for making things simpler and more intuitive, leave a comment! And be sure to check out the ajax-2 folder in the StateManager source and example files to see how the StateManager can be integrated into already-existing projects.


Comments

Gravatar Anton

Hey, mate,
you are truly genius :) … I have found NO bugs when trying it in Opera, IE, Firefox, Safari.

I have little request. Is there any code in StateManager that is used only for flash movies. If yes, can you make a compact version of StateManager, containing only functions and objects only for ajax back button functioning. If you have some icq or msn IM lets have a talk.

This is great, again.

Thanx in a advance!

Gravatar Anton (a different one - small world)

I've had some trouble with this script and Internet explorer causing an aborted operation and refusing to load the page. If you are having similar problems it seems that if you load the script after you close your body tag, the problem goes away.

Gravatar Anton

Hm .. Hey there different Anton :) .. I will have that in mind.. but I was experimenting with the example files (ajax 2) and I had no problems.. I removed some lines of code written for the flash support … And I now wonder how to test it under Safari.. My friend with the Mac is out of the country and I cant find him. Is there another way ?

Gravatar Anton (a different one - small world)

I think it's just because there is so much else going on in the page, something about IE not liking to add child objects before a page has finished loading.

Thankfully we've convinced the powers that be here to buy us one that we can all VPN into to use. However I know there are web services around that let you do the same thing – however they may be cost prohibitive if you're just doing it for fun.

Gravatar Anton

Yes, I saw that browsercamp or smth like that but they charge 2$ per day to use the live testing..
About the changes…what i've changed is just to remove the unnecessary code for ajax applications (no flash history) and i have 1kb down :)) Hip-hip-Horey.. 6kb now :) the whole thing … And Anton if you want I can send it you. And if you are so kind to test it on your mac does it work in safari with the changes. The boss around here is not keen on spending money on mac because he cant understand how important this is..
anyways if you agree here is my icq number: 250007310

Gravatar Mojo

truly wonderful work, used it for with the ANTHEM library, works like a charm. Thnx.

Gravatar M3nt0r

I return false on all functions, but links are still jumping to the ID, aka.. anchor..

How can i stop that? i even did a href='javascript:return false' but somehow this doesn't work.. i have even return false at places i usually don't need them, just because i am totally lost on this.. any reasons why it takes el.id to the location url?

Please help me.. what do i have to do, so that el.ids ain't treated like anchors?

navigate function -> return false
element.onclick -> return false
stateRevisitedHandler -> return false
a href -> javascript:return false;

keeps jumping :(

Gravatar Aaron Fay

Remarkably easy to implement (to an existing custom framework even…) and it worked perfectly the first time.
Thanks a thousand times!

Aaron

Gravatar Aaron Fay

Ahah! I did run into one hitch, I am also gettin an error in IE6 when the page loads. It appears the page partially loads and then (maybe) is instantiating:
window.StateManager = EXANIMO.managers.StateManager;

Then IE changes the url to have '#' at the end and gives an error:

"Internet Explorer cannot open the Internet Site http://xxx.com/ Operation aborted"

I will dig into it further and see if I can come up with a cause,

Cheers,
Aaron

Gravatar Aaron Fay

I spent a little time on this one, and I’m pretty sure this is the fix, I googled ‘Operation aborted IE’ etc and found people were having trouble with part of the gmaps api.

[edit]
I can’t seem to post the solution, the blog is stripping the CDATA tag (which it should), check here

and put the cdata tags around the js blob in statemanager.js
[/edit]

it appears to have fixed the problem. Keep in mind a (line break) after the opening cdata is important or you’ll comment out quite a bit of code.

Hope that helps anyone,
Aaron

Gravatar Aaron Fay

Okay, (sorry about the multi-posts)

don't do anything I said up there ^^

The problem with the operation aborted is that the js file is trying to append elements to the body before the body is fully finished loading (maybe just for large pages?) I worked around this with a 'defer' on the js file, and then had to instantiate the ScriptManger = EXANIMO.managers.StateManager; at the end of the file, and then put the state change handler there as well so none of the statemanager stuff fired until it was all loaded up. whew

hope that helps
af

Gravatar matthew

@M3nt0r: What browser(s) do you see this in?

@Aaron:
Were you trying to call setState before the page had finished loading? That would be the first thing I would check..

The StateManager isn't actually instantiated so it doesn't matter where (or if) you put the line "window.StateManager = EXANIMO.managers.StateManager;". Nor should it matter where you define the stateChangeHandler function.

Gravatar Gordan

How can i save more than just id?
My function for loading content is

function reloadPanel(url,params,targetDiv)
So i need to save 3 variables, not just id.

Thanks.

Gravatar martin

i got my pages aborted in the same way as mentioned above in IE7.
I think it has something to do with char-encoding or wrong format within child-elements of the body.
The mentioned work-around by calling the script after the body works for me too.

Gravatar matthew

@Gordan:

The state has to be able to be recreated from just one variable — a string, in fact. If you think about it, this makes perfect sense: the StateManager must be able to recreate the state from the URL alone, right?

That means that, in your case, the stateChange handler is going to have to parse the state's ID and call the "reloadPanel" function with the appropriate arguments.

For example, one of your states may have the ID "five-blue-fish". Your stateChange handler could split this string, interpret it, and then call "reloadPanel" with arguments "5.html", "BLUE", and a div whose id is "fish". In any case, you'll have to find some way to represent all of your arguments in a (preferably intelligible) string, so that when somebody visits "http://gordan.com/#five-blue-fish&#8221;, they see what you intended.

Gravatar matthew

Just to update everybody: Aaron traced the problem to a conflict with a firebug script he was using.

Gravatar Will

Wow…it took me about 3 minutes to get this working perfectly. Thank you so much.

Gravatar Aaron Fay

Hi again Matthew,

I continued to have problems with the script in IE6 and 7 after emailing you last, I thought I resolved the issue a number of times. The problem was that, on larger pages (lots of html content) your script is using document.body.appendChild to attach the magic iframe to the page. IE had an issue with this because the appendChild seemed to be trying to happen before IE had rendered the entire body, and would throw the "Operation Aborted" error. A number of 'defers' and other workarounds didn't seem to cut the cake, so the final fix, although not as passive as your solution, was to manually add the iframe to the page with the attributes you have specified in your script (name, id, style, etc). After that, it appears to work perfectly.

Thanks again,
Aaron

Gravatar matthew

@Aaron:
Ah.. that must be because I’m polling for body instead of using window.onload. I’m working on some updates to the script now and should have it ready soon.

EDIT: New version is ready.

Gravatar statemanager fan

Hello, I had some trouble after updating sources using IE6 and IE7. The server log reported an infinite loop requesting "/defaultStatus". I solved this by setting defaultStateID to a string with a single dot and also using the uncompressed version instead the compressed one.

Gravatar statemanager fan

Replyng myself:
using about:blank instead the dot I mentioned before sounds more clever.

Gravatar matthew

@fan:
Could you give a little more detail or send me the files that exhibited this behavior, please?

Gravatar Michael McDaniel

I also am having trouble with IE6/7. I have to hit the back button 3 times to get back to the previous page. Here's my simple file:

http://michaelmcdaniel.net/files/statemanagerTest.html

Any advice? Thanks!

Gravatar matthew

@Michael:
The StateManager doesn't seem to like the fact that your link IDs match your state IDs. If you make sure that the id attributes of your anchor tags are different than your state IDs, everything works fine. Thanks for the simplest-case reduction, btw.

Gravatar yogesh

I want to hide url inaddress bar using this how can it possible
suppose in my page there one line home and when i click on it
page open in same window but url must not change in address bar

Gravatar Michael McDaniel

Thanks, Matthew! I appreciate that quick response! Fixing that did the trick!

Gravatar webbles

I just opened up the example site in Safari 3 and it doesn't seem to work as expected. Just thought you'd like to know.

Gravatar webstart

Can StateManager handle the following situation?
1. I have AJAX function to change some dhtml on the page.
2. User enters the address in the browser and navigates away from the page.
3. Then user realizes that he made a mistake, he hits the "Back" button to go back.
4. I want to be able to show the page/content where they left off (after the AJAX update in 1)

Can StateManager do this? and How?

Thanks in advance.

Gravatar Kyle

Hi,

I wanted to use statemanager in a private admin area using only FF and IE7, so I remade it without all the Safari stuff and so on. You could probably halve the file size if you didn't code it so fancy with private variables and such.

But anyway, what I made works fine except when you leave and go back to the page in IE7 only the last state is remembered. Basically the iframe's history is lost. How exactly does your statemanager manage to retain it?

Also, I experienced fan's infinite defaultState loop in IE7 in one of the ajax examples while constantly going backwards and forwards between pages and states. I don't know how to recreate it.

Gravatar Charles

I'm in the process of making this use prototype and taking out the flash support. I'll gladly post my changes when I'm finished if you want them. Just shoot me an email:

my name at the website linked to my name.

Also going to fix a bug with IE6 and firing the state change twice.

Gravatar Rvntone

I have a problem with the ie7, I had to click twice the back button every time to return to the preview page. the code works perfect in FF.
The code I used is:

var StateManager = EXANIMO.managers.StateManager;

StateManager.onstatechange = function(e)
{
a=e.id;
switch (a)
{
case 'busqueda':
case 'defaultState':
selecpesta(1);
new Ajax.Updater('centerajaxupdate',… "page1" … parameters:Form.serialize(document), requestHeaders:['X-Update','centerajaxupdate']}) ;
StateManager.setTitle('TU-CARGA.COM :: ' + 'Envia tu carga');
break;
case 'perfil':
selecpesta(2);
new Ajax.Updater('centerajaxupdate',… "page2" … parameters:Form.serialize(document), requestHeaders:['X-Update','centerajaxupdate']}) ;
StateManager.setTitle('TU-CARGA.COM :: ' + 'Tu perfil');
break;
….
}
}

cargado= function(){ // onload function
StateManager.initialize();
document.getElementById('busqueda').onclick = function()
{
StateManager.setState('busqueda');
return false;
}
….
}

what is worng?

PD: sorry for the code, I tried to index it but I coudn't. :S

Gravatar Yonel Meza

when I comment the line: "parent.document.location.hash = "' + (stateID == _self().defaultStateID ? '#' : stateID) + '"; "
of the _setHash function in the line
"frames[_iframeID].document.write('parent.document.location.hash = "' + (stateID == _self().defaultStateID ? '#' : stateID) + '"; /* Wait for IE to impose its title before setting ours. */setTimeout( function(){ parent.EXANIMO.managers.StateManager._updateIFrame("' + stateID + '"); }, 10);');
"

it work perfect in the explorer but don't send de "#something" to the address bar

Gravatar yysiow

hi,

May I know all the link much put in window.onload function??? because i write another onlclick function,
if all work well, but problem is , when i put in window.onload, URL can show xxx.php#name, but if i use my
onclick function, when click will show xxx.php#name, but after is will go to xxx.php#.
so when use firefox click back button, need to click two time. may i know do you have any sujection??
Thanks..

window.onload = function()
{
StateManager.initialize();

document.getElementById('home-link').onclick = function()
{
StateManager.setState('index.right.main');
return false;
}

}

this is my function.

function linkclick(statestring, titlestr){
StateManager.setState(statestring);
return false;
}

Gravatar Alex

REALLY AWESOME!
I've been looking for this for a week,
trying to hack together my own basically…
and then I finally found THIS!!!!!
We really needed this for our new satellite site,
and I spent HOURS fiddling with dsHistory, ug.
This is GOLD.
THANK YOU THANK YOU THANK YOU

Gravatar Cadpax

Well, i'm using this code and try to make a more dynamicly framework for me, but got one big problem in the ie7.
I have to click 2 or three times, to jump him back to the last page. Deeplinking don't work too, but isn't necessary.

Bit of Code will be found: http://paste.bradleygill.com/index.php?paste_id=20317

In FF 2/3, Opera 9.6/10 everything works absolutely fine

Anyone ideas?!

Gravatar Cadpax

Okay. After hours Problem is solved.

Links look like this:

http://paste.bradleygill.com/index.php?paste_id=20350

Now i can call a switch-page with Ajax and build-in parameters without the problme, that the hashtag is the ID of the link.

Works fine.

@Rvntone Should solve your Problem

Gravatar Alex

So I'm using this awesome library of yours,
but now I'm needing to implement Google's new crawlable AJAX schema which requires the URLs to be !# rather than just #.
Any ideas?

Gravatar Alex

see this to get an idea/see what I mean:
http://code.google.com/web/ajaxcrawling/