Recent Weblogs

Links I like

An Evolution of Service tackles the Ajax History paradigm

The idea of an Ajax Service is a class instance that handles all interactions with remote requests(XHRs). Coupled through an observer pattern is the interface components that render the data into DOM elements.

Ajax.HistoryService

The idea of the Ajax Service quickly lead to realization that if instead of dispatching this information per request, I could save it, then potentially have it available for dispatching that same request, thus bringing my interface back to that state.

An HTML Microformat, HistoryElement

I had looked into previous methods of the history paradigm and the problem most people were running into was that JS created objects had horrible problems, but an object created from HTML and the browser's native processing worked much better across the board. So instead of attempting to create objects inside the JS class I have used an HTML object which follows a microformat. The microformat being that it will contain at least two elements, one being an IFrame and the other being a Form. We will use these objects to mimic form submissions in the window without a top level reload. This action will register with the browser's history module as that is what we're going for to capture the back and forth buttons on our page, and how can we do that? By capturing the load event of the IFrame.

Look Ma, No Polling

Instead of polling the window.location like other history solutions. The HistoryService just listens to the iframe's load event, which occurs everytime the service registers a request, or when the user hits back and forward. So basically it only takes action when you'd expect, and doesn't slow already intense applications with polling intervals.

Nuts and Bolts

Instead of the service directly dispatching events upon receiving the data it stashes the data and the event type in an object and pushes it onto the historyArray that it stores as a property of the instance. Upon stashing it immediately submits the form which initiates a load event which will infact dispatch that event. The advantage of this is that the event architecture is completely reliant on the iframe's reloading, such that when the user clicks back it is dispatching previously registered requests, thus updating the information in each interface component.

For a page dedicated to displaying all class extensions on top of prototype.js needed to get your own copy of Ajax.HistoryService working, check out the code base

The Demo

Current History Index :
Main Number
0
Addition Multiply Factorial
0
0
0

A Grander Demonstration

I understand that the math demo doesn't entail all aspects of an application, I've set up a Google Map interface so you can progress into deeper, richer levels of state and see how the interface responds to the actions of back and forward. View Google Map demo

The Code

This code relies on the EventDispatcher class and Ajax.Application.Base to build the Ajax Service which is what the code below is built upon.

/**

Copyright (c) 2007 Matthew E. Foster

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

Ajax.Application.Event = function(){};
        
Object.extend(Object.extend(Ajax.Application.Event.prototype, Ajax.Application.Base.prototype), EventDispatcher.prototype);

Ajax.Service = {};
    
Ajax.Service.Base = function(){};

Ajax.Service.History = function(){};
    
Object.extend(Object.extend(Ajax.Service.Base.prototype, Ajax.Application.Event.prototype),
                    {
                        sendRequest : function(dto, cb){
                            
                            this.dispatchEvent("request", { dto : dto, callback : cb });
                            
                            Ajax.Application.Event.prototype.sendRequest.apply(this, [dto, cb]);
                        
                        },
                        receiveRequest : function(cb, eAja){
                            
                            this.dispatchEvent("response", eAja);
                            
                            Ajax.Application.Event.prototype.receiveRequest.apply(this, [cb, eAja]);
                        
                        }
                        
                    }
                );


Object.extend(Object.extend(Ajax.Service.History.prototype, Ajax.Service.Base.prototype),
            {
                
                    buildInterface : function(obj){
                        
                        this.historyArr = [];
                        this.historyIndex = undefined;
                        
                        
                        this.container = $(obj);
                        this.historyFrame = this.container.down("iframe");
                        this.form = this.container.down("form");
                    
                    },
                    attachListener : function(){
                    
                        Event.observe(this.historyFrame, "load", this.reloadHandle);
                    
                    },
                    createListener : function(){
                                                        
                        this.reloadHandle = this.handleReload.bindAsEventListener(this);
                    
                    },
                    handleReload : function(e){
                                    
                        var index = parseInt(this.getHistoryIndex());
                        
                        var obj = this.historyArr[index];
                        
                        if(!obj)
                            return true;
                        
                        this.historyIndex = index+1;
                        
                        this.dispatchEvent("reload", [obj, index]);
                        this.dispatchEvent(obj.type, obj.arg);
                        
                    
                    },
                    
                    getHistoryIndex : function(){
                        
                        return this.getIndex(this.historyFrame.contentWindow.location.toString());
                        
                    },
                    getIndex : function(str){
                        
                        return str.replace(/.*index=/gi, "");
                    
                    },
                    getQuery : function(str){
                        
                        return str.replace(/[^?]+?/gi, "");
                    
                    },
                    
                    registerRequest : function(type, eAja){
                    
                        if(this.historyIndex && this.historyIndex < this.historyArr.length)
                            this.historyArr.length = this.historyIndex;
                            
                        this.form.index.value = this.historyArr.length;    
                        
                        this.historyArr.push({ type : type, arg : eAja});
                                                        
                        this.form.submit();
                    
                    }
            
            
            
            }
        );
					

Comments

August 17, 2007Gregory

This code is pretty genius, you're the only one who seems to have cracked the safari problem.
How does this degrade for a browser that does not support iFrames? Does it still load the new content, or will it fail at the iFrame step?

September 12, 2007Casper

This is great. Any chance this could somehow be used to tackle the Ajax bookmarking problem also?
I.e. let's say I press the "Get Number" button in your demo a couple of times. I find a great number and I want to bookmark it. Right now it is not possible to do..

October 15, 2007the friendly ghost

Sweet! Since you're not polling this even works on Opera 9.23.

October 27, 2007Mr Friendly

Yeah this code seems great, just need some help implementing it lol, if anyone can help me out let me know. Sent you an email with my email too hope u got 5-10 mins to spare cheers Mr Friendly.

November 08, 2007Ehsun

Hi,
Is it using IFrames or XHRs???
It seems to use IFrames somewhere, right?

November 27, 2007Steve

I'm not quite sure how to integrate this .
I've been staring at the HistoryService for a while, and gather that this getNumber() notion could be altered into some other function like getCircumference(d) -
I've got a dropdown that calls a function that essentially means getPage(this.value); I'd like these page calls to be entered into history. How would you think one could use this framework for this?

January 12, 2008Yusuf Akyol

Hi,
Wolud you please send simple demo files? Thanks . best regards. yusufakyol@gmail.com

May 29, 2008Rushiraj Yadav

Would please send me some demo files how to implement the provided code.
It would be very helpful if i have some illustrative code.
If possible please send it on rushi.yadav@gmail.com

July 09, 2008Jimmy

First of all, love your work! So thank you for sharing it with us.
The number demo above does not work for me in Firefox 2.0 on Win. Works in IE7. Is this a known issue or am I missing something?
Thanks!

August 19, 2008John King

I too like the looks of your script and would love a tutorial of how to implement it. I have been trying to work it out from this page .. but there seems to be something missing.
my email is johnk@bluecardinduction.com.au
help is appreciated.

September 18, 2008Driver indirme sitesi site haritası

Thanks

September 22, 2008joelkoe

I would like some help implementing it too... I've got the code base, but I'm struggling...
joel@joelkoe.com

September 27, 2008Games

Thanks

October 20, 2008evden eve nakliyat

Thank you very much for this information. I like this site.

October 23, 2008evden eve nakliyat

Thanks

October 29, 2008figurin

Thank you very much for this information.
Good post thanks for sharing.
I like this site ;)

November 09, 2008laptop battery

a good read.

December 22, 2008Gazeteler

Thank you very much for this information.
Good post thanks for sharing.
I like this site ;)

January 03, 2009daran rajah

I am trying integrate this code.
I am struggling to do this.
Could you pls send a simplified example code to alvai@hotmail.com
Thanks.

January 05, 2009penis büyütücü

than k you very much

January 06, 2009Shop

Thank you very much for this information.
Good post thanks for sharing.
I like this site ;)

January 09, 2009SANANE

izlesene video film

January 09, 2009evden eve nakliyat

great entry thanx

January 12, 2009pornohttp://bgobiavi.info/porn-sex-seks-shop-adult.html

thanx

January 13, 2009izlesene18

izlesene video film

January 15, 2009Cheese Lover

I Like Cheese

January 16, 2009airbag tamiri

thanks

January 17, 2009cem

www.adultt.org
adult
adult

Name
Site
Comment
  CAPTCHA Image
Reload Captcha Image
Captcha