Creating a History with Cookies


History is a known issue with Ajax because when a page is not refreshed, it is not added to the browser history. In this section, we will create a solution that will solve many issues with regard to application history and can also be used to provide additional functionality to an application, such as Undo. Although this object will not completely fix the Back button issues, which occurs due to a user never leaving the same page and therefore never creating a history, it will allow users to click the Back button and come back to the site where they left off if they allow cookies to be saved to their local machine.

The Historian Object

The Historian object will be a different type of object than the others we have created in the book. It uses the object constructor method, which we covered in Chapter 5, "Object-Oriented JavaScript." We are using this method because I want to lend flexibility in terms of being able to use multiple Historian instances in one application, during one session. This could come in handy if we wanted to save a separate history for specific features in an application. The Historian object will save data to a cookie to allow users to leave the page and come back to where they left off. The first thing we will do is create a constructor for the object and, since it saves a cookie, we will pass the URL that the cookie should be saved to. The following code snippet shows how we are going to accomplish this:

function Historian(url){}


After we have the constructor completed and we are able to pass a unique URL, we will create a local array called collection and a method called push. The push method will enable us to add data to the array, and will ultimately save the array to a cookie at the URL specified as the constructor's parameter. It checks to see whether the collection contains the value we are pushing through a custom method that we will create called containsValue. If the value does not already exist, we will push the new value and save the collection array. The following code shows how we will push new values to the collection, call another method to save the data to a cookie, and check the collection to make sure that the value does not already exist.

this.collection = new Array(); this.push = function(_index) {     if(!this.containsValue(this.collection, _index))     {         this.collection.push(_index);         this.save(this.collection, 1, "/", url, false);     } } this.containsValue = function(_arr, _val) {     for(var i=0; i<_arr.length; i++)     {         if(_arr[i] == _val)         {             return true;             break;         }     }     return false; }


The save method we are calling from the push method is what creates and stores the cookie. It receives five parameters, all of which are required to create the cookie:

  • The value we are saving, which in this case is our collection array

  • The number of days for which it is saved

  • The path on the server where it is saved

  • The domain we are passing through the constructor

  • A Boolean for whether the cookie is saved securely

After the method is triggered, it simply creates a date object in order to set the expiration date relative to the amount of days that are passed to the method. After we configure the date method, we create the cookie with all the data we have received:

this.save = function(value, days, path, domain, secure) {     var expdate = new Date();     expdate.setTime(expdate.getTime() + days *24*60*60*1000);     document.cookie= "history=" + escape(value) +         ((expdate) ? "; expires=" + expdate.toGMTString() : "") +         ((path) ? "; path=" + path : "") +         ((domain) ? "; domain=" + domain : "") +         ((secure) ? "; secure" : ""); }


Now that we have a way to save the data as a cookie, we need to create a method for retrieving it. We will create a method called getSaved, which will retrieve the cookie, and if one exists, repopulate the collection with the saved data. Since cookies are saved as strings, we need to parse the saved data in a number of ways in order to retrieve the values we need. The first few things we will do are get the cookie, set a variable to the prefix that we saved the data as, and look for an index of the prefix in the cookie. If a prefix does not exist, the method will simply return a null value. Otherwise, we will have an index in which to look for the values, which we will set to a variable called begin. Last, we will return the unescaped value of the cookie after performing a substring from the begin number plus the prefix length to the end of the cookie string.

this.getSaved = function() {     var dc = document.cookie;     var prefix = "history=";     var begin = dc.indexOf(prefix);     if (begin != 0)     {         return null;     }     return unescape(dc.substring(begin + prefix.length, dc.length)); }


Now that we have created this method, we can set the collection to the returned value of the method. We will do this by simply adding a try, catch to the object directly after the getSaved method. We will use the TRy, catch in in order to determine whether the code will perform before we follow through with it. We will try to set the collection to the getSaved data after we split it at all the commas into an array value. If we get an error, we will simply catch it and set the collection to a new array.

try {     this.collection = this.getSaved().split(","); } catch(err) {     this.collection = new Array(); }


In order to clean up and clear the cookie through the Historian, we will create a method named clear. This method takes two parameters: the path and the domain of the cookie. It first checks to see whether any data is saved in the cookie by calling the getSaved method. If the method returns a value other than null, we simply set a new cookie with all the previous data to a past date, which renders the cookie expired and therefore clears it from memory.

this.clear = function(path, domain) {     if(this.getSaved())     {         document.cookie = "history=" +             ((path) ? "; path=" + path : "") +             ((domain) ? "; domain=" + domain : "") +             "; expires=Thu, 01-Jan-70 00:00:01 GMT";     } }


Now that we have the object created, we can put it to use, but we first need to create some sample data to load and play with in the HTML.

Creating and Displaying the XML

In this section, we will first create an XML structure in which to sample our Historian and then create an HTML file to display it all. Our XML file will contain a number of references to images that represent screens, which are grouped into navigation nodes. These screens will become what a user navigates through in the sample. Following is the sample XML data:

<?xml version="1.0" encoding="iso-8859-1"?> <navigation>     <screen><![CDATA[<img src="/books/1/87/1/html/2/img/screen1.jpg">]]></screen>     <screen><![CDATA[<img src="/books/1/87/1/html/2/img/screen2.jpg">]]></screen>     <screen><![CDATA[<img src="/books/1/87/1/html/2/img/screen3.jpg">]]></screen>     <screen><![CDATA[<img src="/books/1/87/1/html/2/img/screen4.jpg">]]></screen>     <screen><![CDATA[<img src="/books/1/87/1/html/2/img/screen5.jpg">]]></screen>     <screen><![CDATA[<img src="/books/1/87/1/html/2/img/screen6.jpg">]]></screen>     <screen><![CDATA[<img src="/books/1/87/1/html/2/img/screen7.jpg">]]></screen>     <screen><![CDATA[<img src="/books/1/87/1/html/2/img/screen8.jpg">]]></screen>     <screen><![CDATA[<img src="/books/1/87/1/html/2/img/screen9.jpg">]]></screen>     <screen><![CDATA[<img src="/books/1/87/1/html/2/img/screen10.jpg">]]></screen> </navigation>


In order to navigate from screen to screen and clear the history, we need to create an HTML page that has Next, Previous, and Clear History buttons. This page must also import our CSS and JavaScript files in order to render the screens and make the Ajax requests. The following is the HTML for the Historian HTML page:

<html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> <title>Ajax Historian</title> <link href="css/historian.css" rel="stylesheet" type="text/css" /> <script type="text/javascript" src="/books/1/87/1/html/2/javascript/utils/Historian.js"></script> <script type="text/javascript" src="/books/1/87/1/html/2/javascript/controller/Navigation.js"></script> <script type="text/javascript" src="/books/1/87/1/html/2/javascript/model/AjaxUpdater.js"></script> <script type="text/javascript" src="/books/1/87/1/html/2/javascript/model/Ajax.js"></script> <script type="text/javascript" src="/books/1/87/1/html/2/javascript/model/HTTP.js"></script> </head> <body onload="javascript:AjaxUpdater.Update('GET', 'services/navigation.xml', Navigation.loadScreen);"> <div ></div> <br/><br/> <a href="javascript:Navigation.previous();">Previous</a> | <a href="javascript:Navigation.next();">Next</a> | <a href="javascript:Navigation.history.clear('/', 'www.yourdomain.com'); document.location = document.location;">Clear History</a> </body> </html>


As you can see from the source code for our HTML page, we are referencing an object called Navigation. This object will allow us to navigate from one screen to the next and reverse, plus clear the history. Now that we have all our other code in place, let's create the Navigation object that will pull it all together.

The Navigation Object

The example we will be creating to use the Historian reminds me a lot of my days working in e-learning. All the courses we built had a number of interactive screens with navigation to click from one to the nextsort of like a slideshow, but with a lot of additional functionality. These courses would also remember where you left off or bookmark the last place you were located in the course so that if you left and came back, it would take you to the bookmark. We will be creating this bookmark functionality in our sample; therefore, we will be creating a Navigation object to take us from one page to another. The Navigation object is a Singleton object that increments and decrements which screen displays from the navigation collection in the XML. In order to save the history with our Navigation object, we must instantiate the Historian object when we initialize it. Here are the Navigation's instantiation and initialize methods.

[View full width]

Navigation = {}; Navigation.initialize = function() { Navigation.history = new Historian("www.yourdomian.com"); Navigation.index = (Navigation.history.collection.length == 0) ? 0 : Navigation .history.collection.length-1; Navigation.screenArray = new Array(); } Navigation.initialize ();


In the initialize method, we instantiate the Historian object and pass it the local domain in which we want to save the cookie. I have added a sample domain; this will need to be changed to reflect the local domain of the server where you will be deploying in the sample. After we have our Historian object instantiated, we will get the index for the navigation array in order to display the screen that was bookmarked, if one exists. Otherwise, we will show the first in the collection by setting the value to 0. Last, we will create an array called screenArray. This array will be set to the collection of screens we receive in the response from the XHR, which occurs in the loadScreen method. The loadScreen method will be used throughout the rest of this object's methods to load each screen. This method is used as the callback method for the AjaxUpdater from the HTML page in the onload event of the body. When it is triggered and the response is "OK", we simply retrieve the screens from the response and add them to our screenArray. After we have an array of screens, we display the index in the screenArray that is currently set in the Navigation object. By default, this index is 0, but if the Historian has a saved cookie of history, it is set to the last screen index that was visited by the current user. Last, we push the index to the Historian, which will be stored in the collection array and saved to the cookie if it does not already exist in the collection.

[View full width]

Navigation.loadScreen = function() { if(Ajax.checkReadyState('navigation') == "OK") { Navigation.screenArray = Ajax.getResponse().getElementsByTagName('screen'); document.getElementById('navigation').innerHTML = Navigation.screenArray[ Navigation.index ].firstChild.data; Navigation.history.push(Navigation.index); } }


In order to navigate from one screen to the next, we will create a method called next. This method will first make sure we are not on the last screen. If we are not, it will increment the index and fire the loadScreen method. If we are on the last index of the screenArray, we will not do anything because we would end up with null values from the array, which would break the navigation.

Navigation.next = function() {     if(Navigation.index < (Navigation.screenArray.length-1))     {         Navigation.index++;         Navigation.loadScreen();     } }


In order to navigate in reverse, we simply check to make sure we are not currently at the 0 index. If not, we decrement the index and fire the loadScreen method. When the loadScreen method is fired, it has the index from the previous screen and it renders it in the page.

Navigation.previous = function() {     if(Navigation.index > 0)     {         Navigation.index--;         Navigation.loadScreen();     } }


This object is pretty powerful and can fix a lot of issues related to not refreshing the browser when using Ajax. I hope that you experiment with the objects in this book and create some extremely powerful custom functionality. The next section of this chapter will cover adding drag-and-drop functionality to elements in your applications, so let's get to it.



Ajax for Web Application Developers
Ajax for Web Application Developers
ISBN: 0672329123
EAN: 2147483647
Year: 2007
Pages: 129
Authors: Kris Hadlock

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net