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

No comments have been posted for this page.
Name
Site
Comment