roojs-core.js
[roojs1] / Roo / History.js
index 3a2b94f..91ae8d2 100644 (file)
@@ -1,10 +1,20 @@
 /**
  * Originally based of this code... - refactored for Roo...
- *
+ * https://github.com/browserstate/history.js
  * History.js Core
  * @author Benjamin Arthur Lupton <contact@balupton.com>
  * @copyright 2010-2011 Benjamin Arthur Lupton <contact@balupton.com>
  * @license New BSD License <http://creativecommons.org/licenses/BSD/>
+ *
+ * Hackily modifyed by alan@roojs.com
+ * 
+ * this is not initialized automatically..
+ * must call Roo.History.init( { ... options... });
+ *
+ *  TOTALLY UNTESTED...
+ *
+ *  Documentation to be done....
  */
 
 Roo.History = {
@@ -185,9 +195,14 @@ Roo.History = {
     
     
        // Initialise History
-       init : function(options){
+       init : function(options)
+    {
+        
+        var emptyFunction = function(){};
+        var _this = this;
         
-        initialTitle : window.document.title,
+        
+        this.initialTitle = window.document.title;
         this.store = {};
         this.idToState={};
                this.stateToId={};
@@ -198,29 +213,111 @@ Roo.History = {
         
         Roo.apply(this,options)
         
-               // Check Load Status of Adapter
-               //if ( typeof this.Adapter === 'undefined' ) {
-               //      return false;
-               //}
-
+               
                // Check Load Status of Core
-               if ( typeof this.initCore !== 'undefined' ) {
-                       this.initCore();
-               }
-
+               this.initCore();
+       
                // Check Load Status of HTML4 Support
-               if ( typeof this.initHtml4 !== 'undefined' ) {
-                       this.initHtml4();
-               }
+               //if ( typeof this.initHtml4 !== 'undefined' ) {
+               //      this.initHtml4();
+               //}
         
         this.initEmulated();
+        
        
                this.enabled = !this.emulated.pushState;
 
+        if ( this.emulated.pushState ) {
+                        
+                       // Prepare
+                       
+                       this.pushState =  emptyFunction;
+                       this.replaceState = emptyFunction;
+               }   
+
+        this.initBugs();
+        
+        
+        
+        
+        Roo.get(window).on('popstate',this.onPopState, this);
+        
          
+               /**
+                * Load the Store
+                */
+               if ( this.sessionStorage ) {
+                       // Fetch
+                       try {
+                               this.store = JSON.parse(this.sessionStorage.getItem('Roo.History.store'))||{};
+                       }
+                       catch ( err ) {
+                               this.store = {};
+                       }
+            this.intervalList.push(setInterval(this.onUnload,this.storeInterval));
+
+                       // For Other Browsers
+                       Roo.get(window).on('beforeunload',this.onUnload,this);
+                       Roo.get(window).on('unload',this.onUnload, this);
+
+               } else {
+            this.onUnload = emptyFunction;
+        }
+        
         
+        this.normalizeStore();
+               /**
+                * Clear Intervals on exit to prevent memory leaks
+                */
+               Roo.get(window).on('unload',this.clearAllIntervals, this);
+
+               /**
+                * Create the initial State
+                */
+               this.saveState(this.storeState(this.extractState(this.getLocationHref(),true)));
+
         
         
+        // Non-Native pushState Implementation
+               if ( !this.emulated.pushState ) {
+                       // Be aware, the following is only for native pushState implementations
+                       // If you are wanting to include something for all browsers
+                       // Then include it above this if block
+
+                       /**
+                        * Setup Safari Fix
+                        */
+                       if ( this.bugs.safariPoll ) {
+                               this.intervalList.push(setInterval(this.safariStatePoll, this.safariPollInterval));
+                       }
+
+                       /**
+                        * Ensure Cross Browser Compatibility
+                        */
+                       //if ( window.navigator.vendor === 'Apple Computer, Inc.' || (window.navigator.appCodeName||'') === 'Mozilla' ) {
+            if (Roo.isSafari) {
+                //code
+            
+                               /**
+                                * Fix Safari HashChange Issue
+                                */
+
+                               // Setup Alias
+                               Roo.get(window).on('hashchange',function(){
+                                       Roo.get(window).fireEvent('popstate');
+                               }, this);
+
+                               // Initialise Alias
+                               if ( this.getHash() ) {
+                                       Roo.onReady(function(){
+                                               Roo.get(window).fireEvent('hashchange');
+                                       });
+                               }
+                       }
+
+               } // !History.emulated.pushState
+
+       
         
 
                // Return true
@@ -365,9 +462,38 @@ Roo.History = {
                                        (this.isInternetExplorer() && this.getInternetExplorerMajorVersion() < 8)
                                );
                        
-       }
+       },
 
-        
+       initBugs : function ()
+    {
+        
+        
+       
+        /**
+         * Safari 5 and Safari iOS 4 fail to return to the correct state once a hash is replaced by a `replaceState` call
+         * https://bugs.webkit.org/show_bug.cgi?id=56249
+         */
+        this.bugs.setHash = Boolean(!this.emulated.pushState && navigator.vendor === 'Apple Computer, Inc.'
+                                    && /AppleWebKit\/5([0-2]|3[0-3])/.test(navigator.userAgent));
+
+        /**
+         * Safari 5 and Safari iOS 4 sometimes fail to apply the state change under busy conditions
+         * https://bugs.webkit.org/show_bug.cgi?id=42940
+         */
+        this.bugs.safariPoll = Boolean(!this.emulated.pushState && navigator.vendor === 'Apple Computer, Inc.'
+                                       && /AppleWebKit\/5([0-2]|3[0-3])/.test(navigator.userAgent));
+
+        /**
+         * MSIE 6 and 7 sometimes do not apply a hash even it was told to (requiring a second call to the apply function)
+         */
+        this.bugs.ieDoubleCheck = Boolean(this.isInternetExplorer() && this.getInternetExplorerMajorVersion() < 8);
+
+        /**
+         * MSIE 6 requires the entire hash to be encoded for the hashes to trigger the onHashChange event
+         */
+        this.bugs.hashEscape = Boolean(this.isInternetExplorer() && this.getInternetExplorerMajorVersion() < 7);
+    },
+    
 
     /**
      * isEmptyObject(obj)
@@ -375,14 +501,14 @@ Roo.History = {
      * @param {Object} obj
      * @return {boolean}
      */
-    isEmptyObject = function(obj) {
+    isEmptyObject : function(obj) {
         for ( var name in obj ) {
             if ( obj.hasOwnProperty(name) ) {
                 return false;
             }
         }
         return true;
-    };
+    },
 
     /**
      * cloneObject(obj)
@@ -390,7 +516,7 @@ Roo.History = {
      * @param {Object} obj
      * @return {Object}
      */
-    cloneObject = function(obj) {
+    cloneObject : function(obj) {
         var hash,newObj;
         if ( obj ) {
             hash = JSON.stringify(obj);
@@ -400,7 +526,7 @@ Roo.History = {
             newObj = {};
         }
         return newObj;
-    };
+    },
 
 
     // ====================================================================
@@ -411,7 +537,7 @@ Roo.History = {
      * Turns "http://mysite.com/dir/page.html?asd" into "http://mysite.com"
      * @return {String} rootUrl
      */
-    getRootUrl = function(){
+    getRootUrl : function(){
         // Create
         var rootUrl = window.document.location.protocol+'//'+(window.document.location.hostname||window.document.location.host);
         if ( window.document.location.port||false ) {
@@ -421,14 +547,14 @@ Roo.History = {
 
         // Return
         return rootUrl;
-    };
+    },
 
     /**
      * getBaseHref()
      * Fetches the `href` attribute of the `<base href="...">` element if it exists
      * @return {String} baseHref
      */
-    getBaseHref = function(){
+    getBaseHref : function(){
         // Create
         var
             baseElements = window.document.getElementsByTagName('base'),
@@ -448,27 +574,27 @@ Roo.History = {
 
         // Return
         return baseHref;
-    };
+    },
 
     /**
      * getBaseUrl()
      * Fetches the baseHref or basePageUrl or rootUrl (whichever one exists first)
      * @return {String} baseUrl
      */
-    getBaseUrl = function(){
+    getBaseUrl : function(){
         // Create
         var baseUrl = this.getBaseHref()||this.getBasePageUrl()||this.getRootUrl();
 
         // Return
         return baseUrl;
-    };
+    },
 
     /**
      * getPageUrl()
      * Fetches the URL of the current page
      * @return {String} pageUrl
      */
-    getPageUrl = function(){
+    getPageUrl : function(){
         // Fetch
         var
             State = this.getState(false,false),
@@ -482,14 +608,14 @@ Roo.History = {
 
         // Return
         return pageUrl;
-    };
+    },
 
     /**
      * getBasePageUrl()
      * Fetches the Url of the directory of the current page
      * @return {String} basePageUrl
      */
-    getBasePageUrl = function(){
+    getBasePageUrl : function(){
         // Create
         var basePageUrl = (this.getLocationHref()).replace(/[#\?].*/,'').replace(/[^\/]+$/,function(part,index,string){
             return (/[^\/]$/).test(part) ? '' : part;
@@ -497,7 +623,7 @@ Roo.History = {
 
         // Return
         return basePageUrl;
-    };
+    },
 
     /**
      * getFullUrl(url)
@@ -506,7 +632,7 @@ Roo.History = {
      * @param {Boolean} allowBaseHref
      * @return {string} fullUrl
      */
-    getFullUrl = function(url,allowBaseHref){
+    getFullUrl : function(url,allowBaseHref){
         // Prepare
         var fullUrl = url, firstChar = url.substring(0,1);
         allowBaseHref = (typeof allowBaseHref === 'undefined') ? true : allowBaseHref;
@@ -543,7 +669,7 @@ Roo.History = {
 
         // Return
         return fullUrl.replace(/\#$/,'');
-    };
+    },
 
     /**
      * getShortUrl(url)
@@ -551,7 +677,7 @@ Roo.History = {
      * @param {string} url
      * @return {string} url
      */
-    getShortUrl = function(url){
+    getShortUrl : function(url){
         // Prepare
         var shortUrl = url, baseUrl = this.getBaseUrl(), rootUrl = this.getRootUrl();
 
@@ -576,7 +702,7 @@ Roo.History = {
 
         // Return
         return shortUrl;
-    };
+    },
 
     /**
      * getLocationHref(document)
@@ -588,7 +714,7 @@ Roo.History = {
      * @param {object} document
      * @return {string} url
      */
-    getLocationHref = function(doc) {
+    getLocationHref : function(doc) {
         doc = doc || window.document;
 
         // most of the time, this will be true
@@ -609,7 +735,7 @@ Roo.History = {
             return doc.location.href;
         
         return doc.URL || doc.location.href;
-    };
+    },
 
 
                
@@ -617,11 +743,13 @@ Roo.History = {
      * noramlizeStore()
      * Noramlize the store by adding necessary values
      */
-    normalizeStore = function(){
+    normalizeStore : function()
+    {
+        
         this.store.idToState = this.store.idToState||{};
         this.store.urlToId = this.store.urlToId||{};
         this.store.stateToId = this.store.stateToId||{};
-    };
+    },
 
     /**
      * getState()
@@ -651,1351 +779,1265 @@ Roo.History = {
 
         // Return
         return State;
-    };
+    },
 
-               /**
-                * getIdByState(State)
-                * Gets a ID for a State
-                * @param {State} newState
-                * @return {String} id
-                */
-               getIdByState = function(newState){
+    /**
+     * getIdByState(State)
+     * Gets a ID for a State
+     * @param {State} newState
+     * @return {String} id
+     */
+    getIdByState : function(newState){
 
-                       // Fetch ID
-                       var id = this.extractId(newState.url),
-                               str;
+        // Fetch ID
+        var id = this.extractId(newState.url),
+            str;
 
-                       if ( !id ) {
-                               // Find ID via State String
-                               str = this.getStateString(newState);
-                               if ( typeof this.stateToId[str] !== 'undefined' ) {
-                                       id = this.stateToId[str];
-                               }
-                               else if ( typeof this.store.stateToId[str] !== 'undefined' ) {
-                                       id = this.store.stateToId[str];
-                               }
-                               else {
-                                       // Generate a new ID
-                                       while ( true ) {
-                                               id = (new Date()).getTime() + String(Math.random()).replace(/\D/g,'');
-                                               if ( typeof this.idToState[id] === 'undefined' && typeof this.store.idToState[id] === 'undefined' ) {
-                                                       break;
-                                               }
-                                       }
-
-                                       // Apply the new State to the ID
-                                       this.stateToId[str] = id;
-                                       this.idToState[id] = newState;
-                               }
-                       }
+        if ( !id ) {
+            // Find ID via State String
+            str = this.getStateString(newState);
+            if ( typeof this.stateToId[str] !== 'undefined' ) {
+                id = this.stateToId[str];
+            }
+            else if ( typeof this.store.stateToId[str] !== 'undefined' ) {
+                id = this.store.stateToId[str];
+            }
+            else {
+                // Generate a new ID
+                while ( true ) {
+                    id = (new Date()).getTime() + String(Math.random()).replace(/\D/g,'');
+                    if ( typeof this.idToState[id] === 'undefined' && typeof this.store.idToState[id] === 'undefined' ) {
+                        break;
+                    }
+                }
+
+                // Apply the new State to the ID
+                this.stateToId[str] = id;
+                this.idToState[id] = newState;
+            }
+        }
 
-                       // Return ID
-                       return id;
-               };
+        // Return ID
+        return id;
+    },
 
-               /**
-                * normalizeState(State)
-                * Expands a State Object
-                * @param {object} State
-                * @return {object}
-                */
-               normalizeState = function(oldState){
-                       // Variables
-                       var newState, dataNotEmpty;
+    /**
+     * normalizeState(State)
+     * Expands a State Object
+     * @param {object} State
+     * @return {object}
+     */
+    normalizeState : function(oldState){
+        // Variables
+        var newState, dataNotEmpty;
 
-                       // Prepare
-                       if ( !oldState || (typeof oldState !== 'object') ) {
-                               oldState = {};
-                       }
+        // Prepare
+        if ( !oldState || (typeof oldState !== 'object') ) {
+            oldState = {};
+        }
 
-                       // Check
-                       if ( typeof oldState.normalized !== 'undefined' ) {
-                               return oldState;
-                       }
+        // Check
+        if ( typeof oldState.normalized !== 'undefined' ) {
+            return oldState;
+        }
 
-                       // Adjust
-                       if ( !oldState.data || (typeof oldState.data !== 'object') ) {
-                               oldState.data = {};
-                       }
+        // Adjust
+        if ( !oldState.data || (typeof oldState.data !== 'object') ) {
+            oldState.data = {};
+        }
 
-                       // ----------------------------------------------------------------
+        // ----------------------------------------------------------------
 
-                       // Create
-                       newState = {};
-                       newState.normalized = true;
-                       newState.title = oldState.title||'';
-                       newState.url = this.getFullUrl(oldState.url?oldState.url:(this.getLocationHref()));
-                       newState.hash = this.getShortUrl(newState.url);
-                       newState.data = this.cloneObject(oldState.data);
+        // Create
+        newState = {};
+        newState.normalized = true;
+        newState.title = oldState.title||'';
+        newState.url = this.getFullUrl(oldState.url?oldState.url:(this.getLocationHref()));
+        newState.hash = this.getShortUrl(newState.url);
+        newState.data = this.cloneObject(oldState.data);
+
+        // Fetch ID
+        newState.id = this.getIdByState(newState);
+
+        // ----------------------------------------------------------------
+
+        // Clean the URL
+        newState.cleanUrl = newState.url.replace(/\??\&_suid.*/,'');
+        newState.url = newState.cleanUrl;
+
+        // Check to see if we have more than just a url
+        dataNotEmpty = !this.isEmptyObject(newState.data);
+
+        // Apply
+        if ( (newState.title || dataNotEmpty) && this.disableSuid !== true ) {
+            // Add ID to Hash
+            newState.hash = this.getShortUrl(newState.url).replace(/\??\&_suid.*/,'');
+            if ( !/\?/.test(newState.hash) ) {
+                newState.hash += '?';
+            }
+            newState.hash += '&_suid='+newState.id;
+        }
 
-                       // Fetch ID
-                       newState.id = this.getIdByState(newState);
+        // Create the Hashed URL
+        newState.hashedUrl = this.getFullUrl(newState.hash);
 
-                       // ----------------------------------------------------------------
+        // ----------------------------------------------------------------
 
-                       // Clean the URL
-                       newState.cleanUrl = newState.url.replace(/\??\&_suid.*/,'');
-                       newState.url = newState.cleanUrl;
+        // Update the URL if we have a duplicate
+        if ( (this.emulated.pushState || this.bugs.safariPoll) && this.hasUrlDuplicate(newState) ) {
+            newState.url = newState.hashedUrl;
+        }
 
-                       // Check to see if we have more than just a url
-                       dataNotEmpty = !this.isEmptyObject(newState.data);
+        // ----------------------------------------------------------------
 
-                       // Apply
-                       if ( (newState.title || dataNotEmpty) && this.disableSuid !== true ) {
-                               // Add ID to Hash
-                               newState.hash = this.getShortUrl(newState.url).replace(/\??\&_suid.*/,'');
-                               if ( !/\?/.test(newState.hash) ) {
-                                       newState.hash += '?';
-                               }
-                               newState.hash += '&_suid='+newState.id;
-                       }
+        // Return
+        return newState;
+    },
 
-                       // Create the Hashed URL
-                       newState.hashedUrl = this.getFullUrl(newState.hash);
+    /**
+     * createStateObject(data,title,url)
+     * Creates a object based on the data, title and url state params
+     * @param {object} data
+     * @param {string} title
+     * @param {string} url
+     * @return {object}
+     */
+    createStateObject : function(data,title,url){
+        // Hashify
+        var State = {
+            'data': data,
+            'title': title,
+            'url': url
+        };
+
+        // Expand the State
+        State = this.normalizeState(State);
+
+        // Return object
+        return State;
+    },
 
-                       // ----------------------------------------------------------------
+    /**
+     * getStateById(id)
+     * Get a state by it's UID
+     * @param {String} id
+     */
+    getStateById : function(id){
+        // Prepare
+        id = String(id);
 
-                       // Update the URL if we have a duplicate
-                       if ( (this.emulated.pushState || this.bugs.safariPoll) && this.hasUrlDuplicate(newState) ) {
-                               newState.url = newState.hashedUrl;
-                       }
+        // Retrieve
+        var State = this.idToState[id] || this.store.idToState[id] || undefined;
 
-                       // ----------------------------------------------------------------
+        // Return State
+        return State;
+    },
 
-                       // Return
-                       return newState;
-               };
+    /**
+     * Get a State's String
+     * @param {State} passedState
+     */
+    getStateString : function(passedState){
+        // Prepare
+        var State, cleanedState, str;
 
-               /**
-                * createStateObject(data,title,url)
-                * Creates a object based on the data, title and url state params
-                * @param {object} data
-                * @param {string} title
-                * @param {string} url
-                * @return {object}
-                */
-               createStateObject = function(data,title,url){
-                       // Hashify
-                       var State = {
-                               'data': data,
-                               'title': title,
-                               'url': url
-                       };
+        // Fetch
+        State = this.normalizeState(passedState);
 
-                       // Expand the State
-                       State = this.normalizeState(State);
+        // Clean
+        cleanedState = {
+            data: State.data,
+            title: passedState.title,
+            url: passedState.url
+        };
 
-                       // Return object
-                       return State;
-               };
+        // Fetch
+        str = JSON.stringify(cleanedState);
 
-               /**
-                * getStateById(id)
-                * Get a state by it's UID
-                * @param {String} id
-                */
-               getStateById = function(id){
-                       // Prepare
-                       id = String(id);
+        // Return
+        return str;
+    },
 
-                       // Retrieve
-                       var State = this.idToState[id] || this.store.idToState[id] || undefined;
+    /**
+     * Get a State's ID
+     * @param {State} passedState
+     * @return {String} id
+     */
+    getStateId : function(passedState){
+        // Prepare
+        var State, id;
 
-                       // Return State
-                       return State;
-               };
+        // Fetch
+        State = this.normalizeState(passedState);
 
-               /**
-                * Get a State's String
-                * @param {State} passedState
-                */
-               getStateString = function(passedState){
-                       // Prepare
-                       var State, cleanedState, str;
+        // Fetch
+        id = State.id;
 
-                       // Fetch
-                       State = this.normalizeState(passedState);
+        // Return
+        return id;
+    },
 
-                       // Clean
-                       cleanedState = {
-                               data: State.data,
-                               title: passedState.title,
-                               url: passedState.url
-                       };
+    /**
+     * getHashByState(State)
+     * Creates a Hash for the State Object
+     * @param {State} passedState
+     * @return {String} hash
+     */
+    getHashByState : function(passedState){
+        // Prepare
+        var State, hash;
 
-                       // Fetch
-                       str = JSON.stringify(cleanedState);
+        // Fetch
+        State = this.normalizeState(passedState);
 
-                       // Return
-                       return str;
-               };
+        // Hash
+        hash = State.hash;
 
-               /**
-                * Get a State's ID
-                * @param {State} passedState
-                * @return {String} id
-                */
-               getStateId = function(passedState){
-                       // Prepare
-                       var State, id;
+        // Return
+        return hash;
+    },
 
-                       // Fetch
-                       State = this.normalizeState(passedState);
+    /**
+     * extractId(url_or_hash)
+     * Get a State ID by it's URL or Hash
+     * @param {string} url_or_hash
+     * @return {string} id
+     */
+    extractId : function ( url_or_hash ) {
+        // Prepare
+        var id,parts,url, tmp;
 
-                       // Fetch
-                       id = State.id;
+        // Extract
+        
+        // If the URL has a #, use the id from before the #
+        if (url_or_hash.indexOf('#') != -1)
+        {
+            tmp = url_or_hash.split("#")[0];
+        }
+        else
+        {
+            tmp = url_or_hash;
+        }
+        
+        parts = /(.*)\&_suid=([0-9]+)$/.exec(tmp);
+        url = parts ? (parts[1]||url_or_hash) : url_or_hash;
+        id = parts ? String(parts[2]||'') : '';
 
-                       // Return
-                       return id;
-               };
+        // Return
+        return id||false;
+    },
 
-               /**
-                * getHashByState(State)
-                * Creates a Hash for the State Object
-                * @param {State} passedState
-                * @return {String} hash
-                */
-               getHashByState = function(passedState){
-                       // Prepare
-                       var State, hash;
+    /**
+     * isTraditionalAnchor
+     * Checks to see if the url is a traditional anchor or not
+     * @param {String} url_or_hash
+     * @return {Boolean}
+     */
+    isTraditionalAnchor : function(url_or_hash){
+        // Check
+        var isTraditional = !(/[\/\?\.]/.test(url_or_hash));
 
-                       // Fetch
-                       State = this.normalizeState(passedState);
+        // Return
+        return isTraditional;
+    },
 
-                       // Hash
-                       hash = State.hash;
+    /**
+     * extractState
+     * Get a State by it's URL or Hash
+     * @param {String} url_or_hash
+     * @return {State|null}
+     */
+    extractState : function(url_or_hash,create){
+        // Prepare
+        var State = null, id, url;
+        create = create||false;
 
-                       // Return
-                       return hash;
-               };
+        // Fetch SUID
+        id = this.extractId(url_or_hash);
+        if ( id ) {
+            State = this.getStateById(id);
+        }
 
-               /**
-                * extractId(url_or_hash)
-                * Get a State ID by it's URL or Hash
-                * @param {string} url_or_hash
-                * @return {string} id
-                */
-               this.extractId = function ( url_or_hash ) {
-                       // Prepare
-                       var id,parts,url, tmp;
+        // Fetch SUID returned no State
+        if ( !State ) {
+            // Fetch URL
+            url = this.getFullUrl(url_or_hash);
 
-                       // Extract
-                       
-                       // If the URL has a #, use the id from before the #
-                       if (url_or_hash.indexOf('#') != -1)
-                       {
-                               tmp = url_or_hash.split("#")[0];
-                       }
-                       else
-                       {
-                               tmp = url_or_hash;
-                       }
-                       
-                       parts = /(.*)\&_suid=([0-9]+)$/.exec(tmp);
-                       url = parts ? (parts[1]||url_or_hash) : url_or_hash;
-                       id = parts ? String(parts[2]||'') : '';
+            // Check URL
+            id = this.getIdByUrl(url)||false;
+            if ( id ) {
+                State = this.getStateById(id);
+            }
 
-                       // Return
-                       return id||false;
-               };
+            // Create State
+            if ( !State && create && !this.isTraditionalAnchor(url_or_hash) ) {
+                State = this.createStateObject(null,null,url);
+            }
+        }
 
-               /**
-                * isTraditionalAnchor
-                * Checks to see if the url is a traditional anchor or not
-                * @param {String} url_or_hash
-                * @return {Boolean}
-                */
-               isTraditionalAnchor = function(url_or_hash){
-                       // Check
-                       var isTraditional = !(/[\/\?\.]/.test(url_or_hash));
+        // Return
+        return State;
+    },
 
-                       // Return
-                       return isTraditional;
-               };
+    /**
+     * getIdByUrl()
+     * Get a State ID by a State URL
+     */
+    getIdByUrl : function(url){
+        // Fetch
+        var id = this.urlToId[url] || this.store.urlToId[url] || undefined;
 
-               /**
-                * extractState
-                * Get a State by it's URL or Hash
-                * @param {String} url_or_hash
-                * @return {State|null}
-                */
-               extractState = function(url_or_hash,create){
-                       // Prepare
-                       var State = null, id, url;
-                       create = create||false;
+        // Return
+        return id;
+    },
 
-                       // Fetch SUID
-                       id = this.extractId(url_or_hash);
-                       if ( id ) {
-                               State = this.getStateById(id);
-                       }
+    /**
+     * getLastSavedState()
+     * Get an object containing the data, title and url of the current state
+     * @return {Object} State
+     */
+    getLastSavedState : function(){
+        return this.savedStates[this.savedStates.length-1]||undefined;
+    },
 
-                       // Fetch SUID returned no State
-                       if ( !State ) {
-                               // Fetch URL
-                               url = this.getFullUrl(url_or_hash);
+    /**
+     * getLastStoredState()
+     * Get an object containing the data, title and url of the current state
+     * @return {Object} State
+     */
+    getLastStoredState : function(){
+        return this.storedStates[this.storedStates.length-1]||undefined;
+    },
 
-                               // Check URL
-                               id = this.getIdByUrl(url)||false;
-                               if ( id ) {
-                                       State = this.getStateById(id);
-                               }
+    /**
+     * hasUrlDuplicate
+     * Checks if a Url will have a url conflict
+     * @param {Object} newState
+     * @return {Boolean} hasDuplicate
+     */
+    hasUrlDuplicate : function(newState) {
+        // Prepare
+        var hasDuplicate = false,
+            oldState;
 
-                               // Create State
-                               if ( !State && create && !this.isTraditionalAnchor(url_or_hash) ) {
-                                       State = this.createStateObject(null,null,url);
-                               }
-                       }
+        // Fetch
+        oldState = this.extractState(newState.url);
 
-                       // Return
-                       return State;
-               };
+        // Check
+        hasDuplicate = oldState && oldState.id !== newState.id;
 
-               /**
-                * getIdByUrl()
-                * Get a State ID by a State URL
-                */
-               getIdByUrl = function(url){
-                       // Fetch
-                       var id = this.urlToId[url] || this.store.urlToId[url] || undefined;
+        // Return
+        return hasDuplicate;
+    },
 
-                       // Return
-                       return id;
-               };
+    /**
+     * storeState
+     * Store a State
+     * @param {Object} newState
+     * @return {Object} newState
+     */
+    storeState : function(newState){
+        // Store the State
+        this.urlToId[newState.url] = newState.id;
 
-               /**
-                * getLastSavedState()
-                * Get an object containing the data, title and url of the current state
-                * @return {Object} State
-                */
-               getLastSavedState = function(){
-                       return this.savedStates[this.savedStates.length-1]||undefined;
-               };
+        // Push the State
+        this.storedStates.push(this.cloneObject(newState));
 
-               /**
-                * getLastStoredState()
-                * Get an object containing the data, title and url of the current state
-                * @return {Object} State
-                */
-               getLastStoredState = function(){
-                       return this.storedStates[this.storedStates.length-1]||undefined;
-               };
-
-               /**
-                * hasUrlDuplicate
-                * Checks if a Url will have a url conflict
-                * @param {Object} newState
-                * @return {Boolean} hasDuplicate
-                */
-               hasUrlDuplicate = function(newState) {
-                       // Prepare
-                       var hasDuplicate = false,
-                               oldState;
-
-                       // Fetch
-                       oldState = this.extractState(newState.url);
-
-                       // Check
-                       hasDuplicate = oldState && oldState.id !== newState.id;
-
-                       // Return
-                       return hasDuplicate;
-               };
-
-               /**
-                * storeState
-                * Store a State
-                * @param {Object} newState
-                * @return {Object} newState
-                */
-               storeState = function(newState){
-                       // Store the State
-                       this.urlToId[newState.url] = newState.id;
-
-                       // Push the State
-                       this.storedStates.push(this.cloneObject(newState));
-
-                       // Return newState
-                       return newState;
-               };
-
-               /**
-                * isLastSavedState(newState)
-                * Tests to see if the state is the last state
-                * @param {Object} newState
-                * @return {boolean} isLast
-                */
-               isLastSavedState = function(newState){
-                       // Prepare
-                       var isLast = false,
-                               newId, oldState, oldId;
-
-                       // Check
-                       if ( this.savedStates.length ) {
-                               newId = newState.id;
-                               oldState = this.getLastSavedState();
-                               oldId = oldState.id;
-
-                               // Check
-                               isLast = (newId === oldId);
-                       }
+        // Return newState
+        return newState;
+    },
 
-                       // Return
-                       return isLast;
-               };
+    /**
+     * isLastSavedState(newState)
+     * Tests to see if the state is the last state
+     * @param {Object} newState
+     * @return {boolean} isLast
+     */
+    isLastSavedState : function(newState){
+        // Prepare
+        var isLast = false,
+            newId, oldState, oldId;
 
-               /**
-                * saveState
-                * Push a State
-                * @param {Object} newState
-                * @return {boolean} changed
-                */
-               saveState = function(newState){
-                       // Check Hash
-                       if ( this.isLastSavedState(newState) ) {
-                               return false;
-                       }
+        // Check
+        if ( this.savedStates.length ) {
+            newId = newState.id;
+            oldState = this.getLastSavedState();
+            oldId = oldState.id;
 
-                       // Push the State
-                       this.savedStates.push(this.cloneObject(newState));
+            // Check
+            isLast = (newId === oldId);
+        }
 
-                       // Return true
-                       return true;
-               };
+        // Return
+        return isLast;
+    },
 
-               /**
-                * getStateByIndex()
-                * Gets a state by the index
-                * @param {integer} index
-                * @return {Object}
-                */
-               getStateByIndex = function(index){
-                       // Prepare
-                       var State = null;
+    /**
+     * saveState
+     * Push a State
+     * @param {Object} newState
+     * @return {boolean} changed
+     */
+    saveState : function(newState){
+        // Check Hash
+        if ( this.isLastSavedState(newState) ) {
+            return false;
+        }
 
-                       // Handle
-                       if ( typeof index === 'undefined' ) {
-                               // Get the last inserted
-                               State = this.savedStates[this.savedStates.length-1];
-                       }
-                       else if ( index < 0 ) {
-                               // Get from the end
-                               State = this.savedStates[this.savedStates.length+index];
-                       }
-                       else {
-                               // Get from the beginning
-                               State = this.savedStates[index];
-                       }
+        // Push the State
+        this.savedStates.push(this.cloneObject(newState));
 
-                       // Return State
-                       return State;
-               };
-               
-               /**
-                * getCurrentIndex()
-                * Gets the current index
-                * @return (integer)
-               */
-               getCurrentIndex = function(){
-                       // Prepare
-                       var index = null;
-                       
-                       // No states saved
-                       if(this.savedStates.length < 1) {
-                               index = 0;
-                       }
-                       else {
-                               index = this.savedStates.length-1;
-                       }
-                       return index;
-               };
+        // Return true
+        return true;
+    },
 
-               // ====================================================================
-               // Hash Helpers
+    /**
+     * getStateByIndex()
+     * Gets a state by the index
+     * @param {integer} index
+     * @return {Object}
+     */
+    getStateByIndex : function(index){
+        // Prepare
+        var State = null;
 
-               /**
-                * getHash()
-                * @param {Location=} location
-                * Gets the current document hash
-                * Note: unlike location.hash, this is guaranteed to return the escaped hash in all browsers
-                * @return {string}
-                */
-               getHash = function(doc){
-                       var url = this.getLocationHref(doc),
-                               hash;
-                       hash = this.getHashByUrl(url);
-                       return hash;
-               };
+        // Handle
+        if ( typeof index === 'undefined' ) {
+            // Get the last inserted
+            State = this.savedStates[this.savedStates.length-1];
+        }
+        else if ( index < 0 ) {
+            // Get from the end
+            State = this.savedStates[this.savedStates.length+index];
+        }
+        else {
+            // Get from the beginning
+            State = this.savedStates[index];
+        }
 
-               /**
-                * unescapeHash()
-                * normalize and Unescape a Hash
-                * @param {String} hash
-                * @return {string}
-                */
-               unescapeHash = function(hash){
-                       // Prepare
-                       var result = this.normalizeHash(hash);
+        // Return State
+        return State;
+    },
+    
+    /**
+     * getCurrentIndex()
+     * Gets the current index
+     * @return (integer)
+    */
+    getCurrentIndex : function(){
+        // Prepare
+        var index = null;
+        
+        // No states saved
+        if(this.savedStates.length < 1) {
+            index = 0;
+        }
+        else {
+            index = this.savedStates.length-1;
+        }
+        return index;
+    },
 
-                       // Unescape hash
-                       result = decodeURIComponent(result);
+    // ====================================================================
+    // Hash Helpers
 
-                       // Return result
-                       return result;
-               };
+    /**
+     * getHash()
+     * @param {Location=} location
+     * Gets the current document hash
+     * Note: unlike location.hash, this is guaranteed to return the escaped hash in all browsers
+     * @return {string}
+     */
+    getHash : function(doc){
+        var url = this.getLocationHref(doc),
+            hash;
+        hash = this.getHashByUrl(url);
+        return hash;
+    },
 
-               /**
-                * normalizeHash()
-                * normalize a hash across browsers
-                * @return {string}
-                */
-               normalizeHash = function(hash){
-                       // Prepare
-                       var result = hash.replace(/[^#]*#/,'').replace(/#.*/, '');
+    /**
+     * unescapeHash()
+     * normalize and Unescape a Hash
+     * @param {String} hash
+     * @return {string}
+     */
+    unescapeHash : function(hash){
+        // Prepare
+        var result = this.normalizeHash(hash);
 
-                       // Return result
-                       return result;
-               };
+        // Unescape hash
+        result = decodeURIComponent(result);
 
-               /**
-                * setHash(hash)
-                * Sets the document hash
-                * @param {string} hash
-                * @return {Roo.History}
-                */
-               setHash = function(hash,queue){
-                       // Prepare
-                       var State, pageUrl;
-
-                       // Handle Queueing
-                       if ( queue !== false && this.busy() ) {
-                               // Wait + Push to Queue
-                               //this.debug('this.setHash: we must wait', arguments);
-                               this.pushQueue({
-                                       scope: this.
-                                       callback: this.setHash,
-                                       args: arguments,
-                                       queue: queue
-                               });
-                               return false;
-                       }
+        // Return result
+        return result;
+    },
 
-                       // Log
-                       //this.debug('this.setHash: called',hash);
+    /**
+     * normalizeHash()
+     * normalize a hash across browsers
+     * @return {string}
+     */
+    normalizeHash : function(hash){
+        // Prepare
+        var result = hash.replace(/[^#]*#/,'').replace(/#.*/, '');
 
-                       // Make Busy + Continue
-                       this.busy(true);
+        // Return result
+        return result;
+    },
 
-                       // Check if hash is a state
-                       State = this.extractState(hash,true);
-                       if ( State && !this.emulated.pushState ) {
-                               // Hash is a state so skip the setHash
-                               //this.debug('this.setHash: Hash is a state so skipping the hash set with a direct pushState call',arguments);
+    /**
+     * setHash(hash)
+     * Sets the document hash
+     * @param {string} hash
+     * @return {Roo.History}
+     */
+    setHash : function(hash,queue){
+        // Prepare
+        var State, pageUrl;
+
+        // Handle Queueing
+        if ( queue !== false && this.busy() ) {
+            // Wait + Push to Queue
+            //this.debug('this.setHash: we must wait', arguments);
+            this.pushQueue({
+                scope: this,
+                callback: this.setHash,
+                args: arguments,
+                queue: queue
+            });
+            return false;
+        }
 
-                               // PushState
-                               this.pushState(State.data,State.title,State.url,false);
-                       }
-                       else if ( this.getHash() !== hash ) {
-                               // Hash is a proper hash, so apply it
+        // Log
+        //this.debug('this.setHash: called',hash);
 
-                               // Handle browser bugs
-                               if ( this.bugs.setHash ) {
-                                       // Fix Safari Bug https://bugs.webkit.org/show_bug.cgi?id=56249
+        // Make Busy + Continue
+        this.busy(true);
 
-                                       // Fetch the base page
-                                       pageUrl = this.getPageUrl();
+        // Check if hash is a state
+        State = this.extractState(hash,true);
+        if ( State && !this.emulated.pushState ) {
+            // Hash is a state so skip the setHash
+            //this.debug('this.setHash: Hash is a state so skipping the hash set with a direct pushState call',arguments);
 
-                                       // Safari hash apply
-                                       this.pushState(null,null,pageUrl+'#'+hash,false);
-                               }
-                               else {
-                                       // Normal hash apply
-                                       window.document.location.hash = hash;
-                               }
-                       }
+            // PushState
+            this.pushState(State.data,State.title,State.url,false);
+        }
+        else if ( this.getHash() !== hash ) {
+            // Hash is a proper hash, so apply it
 
-                       // Chain
-                       return this;
-               };
+            // Handle browser bugs
+            if ( this.bugs.setHash ) {
+                // Fix Safari Bug https://bugs.webkit.org/show_bug.cgi?id=56249
 
-               /**
-                * escape()
-                * normalize and Escape a Hash
-                * @return {string}
-                */
-               escapeHash = function(hash){
-                       // Prepare
-                       var result = normalizeHash(hash);
-
-                       // Escape hash
-                       result = window.encodeURIComponent(result);
-
-                       // IE6 Escape Bug
-                       if ( !this.bugs.hashEscape ) {
-                               // Restore common parts
-                               result = result
-                                       .replace(/\%21/g,'!')
-                                       .replace(/\%26/g,'&')
-                                       .replace(/\%3D/g,'=')
-                                       .replace(/\%3F/g,'?');
-                       }
+                // Fetch the base page
+                pageUrl = this.getPageUrl();
 
-                       // Return result
-                       return result;
-               };
+                // Safari hash apply
+                this.pushState(null,null,pageUrl+'#'+hash,false);
+            }
+            else {
+                // Normal hash apply
+                window.document.location.hash = hash;
+            }
+        }
 
-               /**
-                * getHashByUrl(url)
-                * Extracts the Hash from a URL
-                * @param {string} url
-                * @return {string} url
-                */
-               getHashByUrl = function(url){
-                       // Extract the hash
-                       var hash = String(url)
-                               .replace(/([^#]*)#?([^#]*)#?(.*)/, '$2')
-                               ;
+        // Chain
+        return this;
+    },
 
-                       // Unescape hash
-                       hash = this.unescapeHash(hash);
+    /**
+     * escape()
+     * normalize and Escape a Hash
+     * @return {string}
+     */
+    escapeHash : function(hash){
+        // Prepare
+        var result = normalizeHash(hash);
+
+        // Escape hash
+        result = window.encodeURIComponent(result);
+
+        // IE6 Escape Bug
+        if ( !this.bugs.hashEscape ) {
+            // Restore common parts
+            result = result
+                .replace(/\%21/g,'!')
+                .replace(/\%26/g,'&')
+                .replace(/\%3D/g,'=')
+                .replace(/\%3F/g,'?');
+        }
 
-                       // Return hash
-                       return hash;
-               };
+        // Return result
+        return result;
+    },
 
-               /**
-                * setTitle(title)
-                * Applies the title to the document
-                * @param {State} newState
-                * @return {Boolean}
-                */
-               setTitle = function(newState){
-                       // Prepare
-                       var title = newState.title,
-                               firstState;
-
-                       // Initial
-                       if ( !title ) {
-                               firstState = this.getStateByIndex(0);
-                               if ( firstState && firstState.url === newState.url ) {
-                                       title = firstState.title||this.initialTitle;
-                               }
-                       }
+    /**
+     * getHashByUrl(url)
+     * Extracts the Hash from a URL
+     * @param {string} url
+     * @return {string} url
+     */
+    getHashByUrl : function(url){
+        // Extract the hash
+        var hash = String(url)
+            .replace(/([^#]*)#?([^#]*)#?(.*)/, '$2')
+            ;
 
-                       // Apply
-                       try {
-                               window.document.getElementsByTagName('title')[0].innerHTML = title.replace('<','&lt;').replace('>','&gt;').replace(' & ',' &amp; ');
-                       }
-                       catch ( Exception ) { }
-                       window.document.title = title;
+        // Unescape hash
+        hash = this.unescapeHash(hash);
 
-                       // Chain
-                       return this;
-               };
+        // Return hash
+        return hash;
+    },
 
+    /**
+     * setTitle(title)
+     * Applies the title to the document
+     * @param {State} newState
+     * @return {Boolean}
+     */
+    setTitle : function(newState){
+        // Prepare
+        var title = newState.title,
+            firstState;
+
+        // Initial
+        if ( !title ) {
+            firstState = this.getStateByIndex(0);
+            if ( firstState && firstState.url === newState.url ) {
+                title = firstState.title||this.initialTitle;
+            }
+        }
 
-               // ====================================================================
-               // Queueing
+        // Apply
+        try {
+            window.document.getElementsByTagName('title')[0].innerHTML = title.replace('<','&lt;').replace('>','&gt;').replace(' & ',' &amp; ');
+        }
+        catch ( Exception ) { }
+        window.document.title = title;
 
+        // Chain
+        return this;
+    },
 
-               /**
-                * busy(value)
-                * @param {boolean} value [optional]
-                * @return {boolean} busy
-                */
-               busy = function(value){
-                       // Apply
-                       if ( typeof value !== 'undefined' ) {
-                               //this.debug('this.busy: changing ['+(this.busy.flag||false)+'] to ['+(value||false)+']', this.queues.length);
-                               this.busy_flag = value;
-                       }
-                       // Default
-                       else if ( typeof this.busy_flag === 'undefined' ) {
-                               this.busy_flag = false;
-                       }
 
-                       // Queue
-                       if ( !this.busy_flag ) {
-                               // Execute the next item in the queue
-                               window.clearTimeout(this.busy.timeout);
-                               var fireNext = function(){
-                                       var i, queue, item;
-                                       if ( this.busy_flag ) return;
-                                       for ( i=this.queues.length-1; i >= 0; --i ) {
-                                               queue = this.queues[i];
-                                               if ( queue.length === 0 ) continue;
-                                               item = queue.shift();
-                                               this.fireQueueItem(item);
-                                               this.busy.timeout = window.setTimeout(fireNext,this.busyDelay);
-                                       }
-                               };
-                               this.busy.timeout = window.setTimeout(fireNext,this.busyDelay);
-                       }
+    // ====================================================================
+    // Queueing
 
-                       // Return
-                       return this.busy_flag;
-               };
 
-               
+    /**
+     * busy(value)
+     * @param {boolean} value [optional]
+     * @return {boolean} busy
+     */
+    busy : function(value){
+        // Apply
+        
+        var _this = this;
+        if ( typeof value !== 'undefined' ) {
+            //this.debug('this.busy: changing ['+(this.busy.flag||false)+'] to ['+(value||false)+']', this.queues.length);
+            this.busy_flag = value;
+        }
+        // Default
+        else if ( typeof this.busy_flag === 'undefined' ) {
+            this.busy_flag = false;
+        }
 
-               /**
-                * fireQueueItem(item)
-                * Fire a Queue Item
-                * @param {Object} item
-                * @return {Mixed} result
-                */
-               fireQueueItem = function(item){
-                       return item.callback.apply(item.scope||this,item.args||[]);
-               };
+        // Queue
+        if ( !this.busy_flag ) {
+            
+            
+            
+            // Execute the next item in the queue
+            window.clearTimeout(this.busy.timeout);
+            var fireNext = function(){
+                var i, queue, item;
+                if ( _this.busy_flag ) return;
+                for ( i=_this.queues.length-1; i >= 0; --i ) {
+                    queue = _this.queues[i];
+                    if ( queue.length === 0 ) continue;
+                    item = queue.shift();
+                    _this.fireQueueItem(item);
+                    _this.busy.timeout = window.setTimeout(fireNext,_this.busyDelay);
+                }
+            };
+            this.busy.timeout = window.setTimeout(fireNext,this.busyDelay);
+        }
 
-               /**
-                * pushQueue(callback,args)
-                * Add an item to the queue
-                * @param {Object} item [scope,callback,args,queue]
-                */
-               pushQueue = function(item){
-                       // Prepare the queue
-                       this.queues[item.queue||0] = this.queues[item.queue||0]||[];
+        // Return
+        return this.busy_flag;
+    },
 
-                       // Add to the queue
-                       this.queues[item.queue||0].push(item);
+    
 
-                       // Chain
-                       return this;
-               };
+    /**
+     * fireQueueItem(item)
+     * Fire a Queue Item
+     * @param {Object} item
+     * @return {Mixed} result
+     */
+    fireQueueItem : function(item){
+        return item.callback.apply(item.scope||this,item.args||[]);
+    },
 
-               /**
-                * queue (item,queue), (func,queue), (func), (item)
-                * Either firs the item now if not busy, or adds it to the queue
-                */
-               queue = function(item,queue){
-                       // Prepare
-                       if ( typeof item === 'function' ) {
-                               item = {
-                                       callback: item
-                               };
-                       }
-                       if ( typeof queue !== 'undefined' ) {
-                               item.queue = queue;
-                       }
+    /**
+     * pushQueue(callback,args)
+     * Add an item to the queue
+     * @param {Object} item [scope,callback,args,queue]
+     */
+    pushQueue : function(item){
+        // Prepare the queue
+        this.queues[item.queue||0] = this.queues[item.queue||0]||[];
 
-                       // Handle
-                       if ( this.busy() ) {
-                               this.pushQueue(item);
-                       } else {
-                               this.fireQueueItem(item);
-                       }
+        // Add to the queue
+        this.queues[item.queue||0].push(item);
 
-                       // Chain
-                       return this;
-               };
+        // Chain
+        return this;
+    },
 
-               /**
-                * clearQueue()
-                * Clears the Queue
-                */
-               clearQueue = function(){
-                       this.busy_flag = false;
-                       this.queues = [];
-                       return this;
-               };
+    /**
+     * queue (item,queue), (func,queue), (func), (item)
+     * Either firs the item now if not busy, or adds it to the queue
+     */
+    queue : function(item,queue){
+        // Prepare
+        if ( typeof item === 'function' ) {
+            item = {
+                callback: item
+            };
+        }
+        if ( typeof queue !== 'undefined' ) {
+            item.queue = queue;
+        }
 
+        // Handle
+        if ( this.busy() ) {
+            this.pushQueue(item);
+        } else {
+            this.fireQueueItem(item);
+        }
 
+        // Chain
+        return this;
+    },
 
-               /**
-                * doubleCheckComplete()
-                * Complete a double check
-                * @return {Roo.History}
-                */
-               doubleCheckComplete = function(){
-                       // Update
-                       this.stateChanged = true;
+    /**
+     * clearQueue()
+     * Clears the Queue
+     */
+    clearQueue : function(){
+        this.busy_flag = false;
+        this.queues = [];
+        return this;
+    },
 
-                       // Clear
-                       this.doubleCheckClear();
 
-                       // Chain
-                       return this;;
-               };
 
-               /**
-                * doubleCheckClear()
-                * Clear a double check
-                * @return {Roo.History}
-                */
-               doubleCheckClear = function(){
-                       // Clear
-                       if ( this.doubleChecker ) {
-                               window.clearTimeout(this.doubleChecker);
-                               this.doubleChecker = false;
-                       }
+    /**
+     * doubleCheckComplete()
+     * Complete a double check
+     * @return {Roo.History}
+     */
+    doubleCheckComplete : function(){
+        // Update
+        this.stateChanged = true;
 
-                       // Chain
-                       return this;
-               };
+        // Clear
+        this.doubleCheckClear();
 
-               /**
-                * doubleCheck()
-                * Create a double check
-                * @return {Roo.History}
-                */
-               doubleCheck = function(tryAgain){
-                       // Reset
-                       this.stateChanged = false;
-                       this.doubleCheckClear();
-
-                       // Fix IE6,IE7 bug where calling history.back or history.forward does not actually change the hash (whereas doing it manually does)
-                       // Fix Safari 5 bug where sometimes the state does not change: https://bugs.webkit.org/show_bug.cgi?id=42940
-                       if ( this.bugs.ieDoubleCheck ) {
-                               // Apply Check
-                               this.doubleChecker = window.setTimeout(
-                                       function(){
-                                               this.doubleCheckClear();
-                                               if ( !this.stateChanged ) {
-                                                       //this.debug('History.doubleCheck: State has not yet changed, trying again', arguments);
-                                                       // Re-Attempt
-                                                       tryAgain();
-                                               }
-                                               return true;
-                                       },
-                                       this.doubleCheckInterval
-                               );
-                       }
+        // Chain
+        return this;;
+    },
 
-                       // Chain
-                       return this;
-               };
+    /**
+     * doubleCheckClear()
+     * Clear a double check
+     * @return {Roo.History}
+     */
+    doubleCheckClear : function(){
+        // Clear
+        if ( this.doubleChecker ) {
+            window.clearTimeout(this.doubleChecker);
+            this.doubleChecker = false;
+        }
 
+        // Chain
+        return this;
+    },
 
-               // ====================================================================
-               // Safari Bug Fix
+    /**
+     * doubleCheck()
+     * Create a double check
+     * @return {Roo.History}
+     */
+    doubleCheck : function(tryAgain)
+    {
+        var _this = this;
+        // Reset
+        this.stateChanged = false;
+        this.doubleCheckClear();
+
+        // Fix IE6,IE7 bug where calling history.back or history.forward does not actually change the hash (whereas doing it manually does)
+        // Fix Safari 5 bug where sometimes the state does not change: https://bugs.webkit.org/show_bug.cgi?id=42940
+        if ( this.bugs.ieDoubleCheck ) {
+            // Apply Check
+            this.doubleChecker = window.setTimeout(
+                function(){
+                    _this.doubleCheckClear();
+                    if ( !_this.stateChanged ) {
+                        //this.debug('History.doubleCheck: State has not yet changed, trying again', arguments);
+                        // Re-Attempt
+                        tryAgain();
+                    }
+                    return true;
+                },
+                this.doubleCheckInterval
+            );
+        }
 
-               /**
-                * History.safariStatePoll()
-                * Poll the current state
-                * @return {History}
-                */
-               History.safariStatePoll = function(){
-                       // Poll the URL
+        // Chain
+        return this;
+    },
 
-                       // Get the Last State which has the new URL
-                       var
-                               urlState = History.extractState(History.getLocationHref()),
-                               newState;
 
-                       // Check for a difference
-                       if ( !History.isLastSavedState(urlState) ) {
-                               newState = urlState;
-                       }
-                       else {
-                               return;
-                       }
+    // ====================================================================
+    // Safari Bug Fix
 
-                       // Check if we have a state with that url
-                       // If not create it
-                       if ( !newState ) {
-                               //History.debug('History.safariStatePoll: new');
-                               newState = History.createStateObject();
-                       }
+    /**
+     * safariStatePoll()
+     * Poll the current state
+     * @return {Roo.History}
+     */
+    safariStatePoll : function(){
+        // Poll the URL
 
-                       // Apply the New State
-                       //History.debug('History.safariStatePoll: trigger');
-                       History.Adapter.trigger(window,'popstate');
+        // Get the Last State which has the new URL
+        var
+            urlState = this.extractState(this.getLocationHref()),
+            newState;
 
-                       // Chain
-                       return History;
-               };
+        // Check for a difference
+        if ( !this.isLastSavedState(urlState) ) {
+            newState = urlState;
+        }
+        else {
+            return;
+        }
 
+        // Check if we have a state with that url
+        // If not create it
+        if ( !newState ) {
+            //this.debug('this.safariStatePoll: new');
+            newState = this.createStateObject();
+        }
 
-               // ====================================================================
-               // State Aliases
+        // Apply the New State
+        //this.debug('this.safariStatePoll: trigger');
+        Roo.get(window).fireEvent('popstate');
 
-               /**
-                * History.back(queue)
-                * Send the browser history back one item
-                * @param {Integer} queue [optional]
-                */
-               History.back = function(queue){
-                       //History.debug('History.back: called', arguments);
-
-                       // Handle Queueing
-                       if ( queue !== false && History.busy() ) {
-                               // Wait + Push to Queue
-                               //History.debug('History.back: we must wait', arguments);
-                               History.pushQueue({
-                                       scope: History,
-                                       callback: History.back,
-                                       args: arguments,
-                                       queue: queue
-                               });
-                               return false;
-                       }
+        // Chain
+        return this;
+    },
 
-                       // Make Busy + Continue
-                       History.busy(true);
 
-                       // Fix certain browser bugs that prevent the state from changing
-                       History.doubleCheck(function(){
-                               History.back(false);
-                       });
+    // ====================================================================
+    // State Aliases
 
-                       // Go back
-                       history.go(-1);
+    /**
+     * back(queue)
+     * Send the browser history back one item
+     * @param {Integer} queue [optional]
+     */
+    back : function(queue)
+    {
+        //this.debug('this.back: called', arguments);
+        var _this = this;
+        // Handle Queueing
+        if ( queue !== false && this.busy() ) {
+            // Wait + Push to Queue
+            //this.debug('this.back: we must wait', arguments);
+            this.pushQueue({
+                scope: this,
+                callback: this.back,
+                args: arguments,
+                queue: queue
+            });
+            return false;
+        }
 
-                       // End back closure
-                       return true;
-               };
+        // Make Busy + Continue
+        this.busy(true);
 
-               /**
-                * History.forward(queue)
-                * Send the browser history forward one item
-                * @param {Integer} queue [optional]
-                */
-               History.forward = function(queue){
-                       //History.debug('History.forward: called', arguments);
-
-                       // Handle Queueing
-                       if ( queue !== false && History.busy() ) {
-                               // Wait + Push to Queue
-                               //History.debug('History.forward: we must wait', arguments);
-                               History.pushQueue({
-                                       scope: History,
-                                       callback: History.forward,
-                                       args: arguments,
-                                       queue: queue
-                               });
-                               return false;
-                       }
+        // Fix certain browser bugs that prevent the state from changing
+        this.doubleCheck(function(){
+            _this.back(false);
+        });
 
-                       // Make Busy + Continue
-                       History.busy(true);
+        // Go back
+        history.go(-1);
 
-                       // Fix certain browser bugs that prevent the state from changing
-                       History.doubleCheck(function(){
-                               History.forward(false);
-                       });
+        // End back closure
+        return true;
+    },
 
-                       // Go forward
-                       history.go(1);
+    /**
+     * forward(queue)
+     * Send the browser history forward one item
+     * @param {Integer} queue [optional]
+     */
+    forward : function(queue){
+        //this.debug('this.forward: called', arguments);
+
+        // Handle Queueing
+        if ( queue !== false && this.busy() ) {
+            // Wait + Push to Queue
+            //this.debug('this.forward: we must wait', arguments);
+            this.pushQueue({
+                scope: this,
+                callback: this.forward,
+                args: arguments,
+                queue: queue
+            });
+            return false;
+        }
 
-                       // End forward closure
-                       return true;
-               };
+        // Make Busy + Continue
+        this.busy(true);
+        
+        var _t = this;
+        // Fix certain browser bugs that prevent the state from changing
+        this.doubleCheck(function(){
+            _t.forward(false);
+        });
 
-               /**
-                * History.go(index,queue)
-                * Send the browser history back or forward index times
-                * @param {Integer} queue [optional]
-                */
-               History.go = function(index,queue){
-                       //History.debug('History.go: called', arguments);
+        // Go forward
+        history.go(1);
 
-                       // Prepare
-                       var i;
+        // End forward closure
+        return true;
+    },
 
-                       // Handle
-                       if ( index > 0 ) {
-                               // Forward
-                               for ( i=1; i<=index; ++i ) {
-                                       History.forward(queue);
-                               }
-                       }
-                       else if ( index < 0 ) {
-                               // Backward
-                               for ( i=-1; i>=index; --i ) {
-                                       History.back(queue);
-                               }
-                       }
-                       else {
-                               throw new Error('History.go: History.go requires a positive or negative integer passed.');
-                       }
+    /**
+     * go(index,queue)
+     * Send the browser history back or forward index times
+     * @param {Integer} queue [optional]
+     */
+    go : function(index,queue){
+        //this.debug('this.go: called', arguments);
 
-                       // Chain
-                       return History;
-               };
+        // Prepare
+        var i;
 
+        // Handle
+        if ( index > 0 ) {
+            // Forward
+            for ( i=1; i<=index; ++i ) {
+                this.forward(queue);
+            }
+        }
+        else if ( index < 0 ) {
+            // Backward
+            for ( i=-1; i>=index; --i ) {
+                this.back(queue);
+            }
+        }
+        else {
+            throw new Error('History.go: History.go requires a positive or negative integer passed.');
+        }
 
-               // ====================================================================
-               // HTML5 State Support
+        // Chain
+        return this;
+    },
 
-               // Non-Native pushState Implementation
-               if ( History.emulated.pushState ) {
-                       /*
-                        * Provide Skeleton for HTML4 Browsers
-                        */
 
-                       // Prepare
-                       var emptyFunction = function(){};
-                       History.pushState = History.pushState||emptyFunction;
-                       History.replaceState = History.replaceState||emptyFunction;
-               } // History.emulated.pushState
+    // ====================================================================
+    // HTML5 State Support
 
-               // Native pushState Implementation
-               else {
-                       /*
-                        * Use native HTML5 History API Implementation
-                        */
+     
+    /*
+     * Use native HTML5 History API Implementation
+     */
 
-                       /**
-                        * History.onPopState(event,extra)
-                        * Refresh the Current State
-                        */
-                       History.onPopState = function(event,extra){
-                               // Prepare
-                               var stateId = false, newState = false, currentHash, currentState;
-
-                               // Reset the double check
-                               History.doubleCheckComplete();
-
-                               // Check for a Hash, and handle apporiatly
-                               currentHash = History.getHash();
-                               if ( currentHash ) {
-                                       // Expand Hash
-                                       currentState = History.extractState(currentHash||History.getLocationHref(),true);
-                                       if ( currentState ) {
-                                               // We were able to parse it, it must be a State!
-                                               // Let's forward to replaceState
-                                               //History.debug('History.onPopState: state anchor', currentHash, currentState);
-                                               History.replaceState(currentState.data, currentState.title, currentState.url, false);
-                                       }
-                                       else {
-                                               // Traditional Anchor
-                                               //History.debug('History.onPopState: traditional anchor', currentHash);
-                                               History.Adapter.trigger(window,'anchorchange');
-                                               History.busy(false);
-                                       }
-
-                                       // We don't care for hashes
-                                       History.expectedStateId = false;
-                                       return false;
-                               }
+    /**
+     * onPopState(event,extra)
+     * Refresh the Current State
+     */
+    onPopState : function(event,extra){
+        // Prepare
+        var stateId = false, newState = false, currentHash, currentState;
+
+        // Reset the double check
+        this.doubleCheckComplete();
+
+        // Check for a Hash, and handle apporiatly
+        currentHash = this.getHash();
+        if ( currentHash ) {
+            // Expand Hash
+            currentState = this.extractState(currentHash||this.getLocationHref(),true);
+            if ( currentState ) {
+                // We were able to parse it, it must be a State!
+                // Let's forward to replaceState
+                //this.debug('this.onPopState: state anchor', currentHash, currentState);
+                this.replaceState(currentState.data, currentState.title, currentState.url, false);
+            }
+            else {
+                // Traditional Anchor
+                //this.debug('this.onPopState: traditional anchor', currentHash);
+                Roo.get(window).fireEvent('anchorchange');
+                this.busy(false);
+            }
 
-                               // Ensure
-                               stateId = History.Adapter.extractEventData('state',event,extra) || false;
+            // We don't care for hashes
+            this.expectedStateId = false;
+            return false;
+        }
+        stateId = (event && event.browserEvent && event.browserEvent['state']) || (extra && extra['state']) || undefined;
 
-                               // Fetch State
-                               if ( stateId ) {
-                                       // Vanilla: Back/forward button was used
-                                       newState = History.getStateById(stateId);
-                               }
-                               else if ( History.expectedStateId ) {
-                                       // Vanilla: A new state was pushed, and popstate was called manually
-                                       newState = History.getStateById(History.expectedStateId);
-                               }
-                               else {
-                                       // Initial State
-                                       newState = History.extractState(History.getLocationHref());
-                               }
+        // Ensure
+        //stateId = this.Adapter.extractEventData('state',event,extra) || false;
 
-                               // The State did not exist in our store
-                               if ( !newState ) {
-                                       // Regenerate the State
-                                       newState = History.createStateObject(null,null,History.getLocationHref());
-                               }
+        // Fetch State
+        if ( stateId ) {
+            // Vanilla: Back/forward button was used
+            newState = this.getStateById(stateId);
+        }
+        else if ( this.expectedStateId ) {
+            // Vanilla: A new state was pushed, and popstate was called manually
+            newState = this.getStateById(this.expectedStateId);
+        }
+        else {
+            // Initial State
+            newState = this.extractState(this.getLocationHref());
+        }
 
-                               // Clean
-                               History.expectedStateId = false;
+        // The State did not exist in our store
+        if ( !newState ) {
+            // Regenerate the State
+            newState = this.createStateObject(null,null,this.getLocationHref());
+        }
 
-                               // Check if we are the same state
-                               if ( History.isLastSavedState(newState) ) {
-                                       // There has been no change (just the page's hash has finally propagated)
-                                       //History.debug('History.onPopState: no change', newState, History.savedStates);
-                                       History.busy(false);
-                                       return false;
-                               }
+        // Clean
+        this.expectedStateId = false;
 
-                               // Store the State
-                               History.storeState(newState);
-                               History.saveState(newState);
+        // Check if we are the same state
+        if ( this.isLastSavedState(newState) ) {
+            // There has been no change (just the page's hash has finally propagated)
+            //this.debug('this.onPopState: no change', newState, this.savedStates);
+            this.busy(false);
+            return false;
+        }
 
-                               // Force update of the title
-                               History.setTitle(newState);
+        // Store the State
+        this.storeState(newState);
+        this.saveState(newState);
 
-                               // Fire Our Event
-                               History.Adapter.trigger(window,'statechange');
-                               History.busy(false);
+        // Force update of the title
+        this.setTitle(newState);
 
-                               // Return true
-                               return true;
-                       };
-                       History.Adapter.bind(window,'popstate',History.onPopState);
+        // Fire Our Event
+        Roo.get(window).fireEvent('statechange');
+        this.busy(false);
 
-                       /**
-                        * History.pushState(data,title,url)
-                        * Add a new State to the history object, become it, and trigger onpopstate
-                        * We have to trigger for HTML4 compatibility
-                        * @param {object} data
-                        * @param {string} title
-                        * @param {string} url
-                        * @return {true}
-                        */
-                       History.pushState = function(data,title,url,queue){
-                               //History.debug('History.pushState: called', arguments);
+        // Return true
+        return true;
+    },
+    
+    
+    
+    
+    
+        
 
-                               // Check the State
-                               if ( History.getHashByUrl(url) && History.emulated.pushState ) {
-                                       throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).');
-                               }
+    /**
+     * pushState(data,title,url)
+     * Add a new State to the history object, become it, and trigger onpopstate
+     * We have to trigger for HTML4 compatibility
+     * @param {object} data
+     * @param {string} title
+     * @param {string} url
+     * @return {true}
+     */
+    pushState : function(data,title,url,queue){
+        //this.debug('this.pushState: called', arguments);
 
-                               // Handle Queueing
-                               if ( queue !== false && History.busy() ) {
-                                       // Wait + Push to Queue
-                                       //History.debug('History.pushState: we must wait', arguments);
-                                       History.pushQueue({
-                                               scope: History,
-                                               callback: History.pushState,
-                                               args: arguments,
-                                               queue: queue
-                                       });
-                                       return false;
-                               }
+        // Check the State
+        if ( this.getHashByUrl(url) && this.emulated.pushState ) {
+            throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).');
+        }
 
-                               // Make Busy + Continue
-                               History.busy(true);
+        // Handle Queueing
+        if ( queue !== false && this.busy() ) {
+            // Wait + Push to Queue
+            //this.debug('this.pushState: we must wait', arguments);
+            this.pushQueue({
+                scope: this,
+                callback: this.pushState,
+                args: arguments,
+                queue: queue
+            });
+            return false;
+        }
 
-                               // Create the newState
-                               var newState = History.createStateObject(data,title,url);
+        // Make Busy + Continue
+        this.busy(true);
 
-                               // Check it
-                               if ( History.isLastSavedState(newState) ) {
-                                       // Won't be a change
-                                       History.busy(false);
-                               }
-                               else {
-                                       // Store the newState
-                                       History.storeState(newState);
-                                       History.expectedStateId = newState.id;
+        // Create the newState
+        var newState = this.createStateObject(data,title,url);
 
-                                       // Push the newState
-                                       history.pushState(newState.id,newState.title,newState.url);
+        // Check it
+        if ( this.isLastSavedState(newState) ) {
+            // Won't be a change
+            this.busy(false);
+        }
+        else {
+            // Store the newState
+            this.storeState(newState);
+            this.expectedStateId = newState.id;
 
-                                       // Fire HTML5 Event
-                                       History.Adapter.trigger(window,'popstate');
-                               }
+            // Push the newState
+            history.pushState(newState.id,newState.title,newState.url);
 
-                               // End pushState closure
-                               return true;
-                       };
+            // Fire HTML5 Event
+            Roo.get(window).fireEvent('popstate');
+        }
 
-                       /**
-                        * History.replaceState(data,title,url)
-                        * Replace the State and trigger onpopstate
-                        * We have to trigger for HTML4 compatibility
-                        * @param {object} data
-                        * @param {string} title
-                        * @param {string} url
-                        * @return {true}
-                        */
-                       History.replaceState = function(data,title,url,queue){
-                               //History.debug('History.replaceState: called', arguments);
+        // End pushState closure
+        return true;
+    },
 
-                               // Check the State
-                               if ( History.getHashByUrl(url) && History.emulated.pushState ) {
-                                       throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).');
-                               }
+    /**
+     * replaceState(data,title,url)
+     * Replace the State and trigger onpopstate
+     * We have to trigger for HTML4 compatibility
+     * @param {object} data
+     * @param {string} title
+     * @param {string} url
+     * @return {true}
+     */
+    replaceState : function(data,title,url,queue){
+        //this.debug('this.replaceState: called', arguments);
 
-                               // Handle Queueing
-                               if ( queue !== false && History.busy() ) {
-                                       // Wait + Push to Queue
-                                       //History.debug('History.replaceState: we must wait', arguments);
-                                       History.pushQueue({
-                                               scope: History,
-                                               callback: History.replaceState,
-                                               args: arguments,
-                                               queue: queue
-                                       });
-                                       return false;
-                               }
+        // Check the State
+        if ( this.getHashByUrl(url) && this.emulated.pushState ) {
+            throw new Error('this.js does not support states with fragement-identifiers (hashes/anchors).');
+        }
 
-                               // Make Busy + Continue
-                               History.busy(true);
+        // Handle Queueing
+        if ( queue !== false && this.busy() ) {
+            // Wait + Push to Queue
+            //this.debug('this.replaceState: we must wait', arguments);
+            this.pushQueue({
+                scope: this,
+                callback: this.replaceState,
+                args: arguments,
+                queue: queue
+            });
+            return false;
+        }
 
-                               // Create the newState
-                               var newState = History.createStateObject(data,title,url);
+        // Make Busy + Continue
+        this.busy(true);
 
-                               // Check it
-                               if ( History.isLastSavedState(newState) ) {
-                                       // Won't be a change
-                                       History.busy(false);
-                               }
-                               else {
-                                       // Store the newState
-                                       History.storeState(newState);
-                                       History.expectedStateId = newState.id;
+        // Create the newState
+        var newState = this.createStateObject(data,title,url);
 
-                                       // Push the newState
-                                       history.replaceState(newState.id,newState.title,newState.url);
+        // Check it
+        if ( this.isLastSavedState(newState) ) {
+            // Won't be a change
+            this.busy(false);
+        }
+        else {
+            // Store the newState
+            this.storeState(newState);
+            this.expectedStateId = newState.id;
 
-                                       // Fire HTML5 Event
-                                       History.Adapter.trigger(window,'popstate');
-                               }
+            // Push the newState
+            history.replaceState(newState.id,newState.title,newState.url);
 
-                               // End replaceState closure
-                               return true;
-                       };
+            // Fire HTML5 Event
+            Roo.get(window).fireEvent('popstate');
+        }
 
-               } // !History.emulated.pushState
+        // End replaceState closure
+        return true;
+    },
 
 
                // ====================================================================
                // Initialise
 
-               /**
-                * Load the Store
-                */
-               if ( sessionStorage ) {
-                       // Fetch
-                       try {
-                               History.store = JSON.parse(sessionStorage.getItem('History.store'))||{};
-                       }
-                       catch ( err ) {
-                               History.store = {};
-                       }
-
-                       // Normalize
-                       History.normalizeStore();
-               }
-               else {
-                       // Default Load
-                       History.store = {};
-                       History.normalizeStore();
-               }
-
-               /**
-                * Clear Intervals on exit to prevent memory leaks
-                */
-               History.Adapter.bind(window,"unload",History.clearAllIntervals);
-
-               /**
-                * Create the initial State
-                */
-               History.saveState(History.storeState(History.extractState(History.getLocationHref(),true)));
-
                /**
                 * Bind for Saving Store
                 */
-               if ( sessionStorage ) {
-                       // When the page is closed
-                       History.onUnload = function(){
-                               // Prepare
-                               var     currentStore, item, currentStoreString;
-
-                               // Fetch
-                               try {
-                                       currentStore = JSON.parse(sessionStorage.getItem('History.store'))||{};
-                               }
-                               catch ( err ) {
-                                       currentStore = {};
-                               }
-
-                               // Ensure
-                               currentStore.idToState = currentStore.idToState || {};
-                               currentStore.urlToId = currentStore.urlToId || {};
-                               currentStore.stateToId = currentStore.stateToId || {};
-
-                               // Sync
-                               for ( item in History.idToState ) {
-                                       if ( !History.idToState.hasOwnProperty(item) ) {
-                                               continue;
-                                       }
-                                       currentStore.idToState[item] = History.idToState[item];
-                               }
-                               for ( item in History.urlToId ) {
-                                       if ( !History.urlToId.hasOwnProperty(item) ) {
-                                               continue;
-                                       }
-                                       currentStore.urlToId[item] = History.urlToId[item];
-                               }
-                               for ( item in History.stateToId ) {
-                                       if ( !History.stateToId.hasOwnProperty(item) ) {
-                                               continue;
-                                       }
-                                       currentStore.stateToId[item] = History.stateToId[item];
-                               }
-
-                               // Update
-                               History.store = currentStore;
-                               History.normalizeStore();
-
-                               // In Safari, going into Private Browsing mode causes the
-                               // Session Storage object to still exist but if you try and use
-                               // or set any property/function of it it throws the exception
-                               // "QUOTA_EXCEEDED_ERR: DOM Exception 22: An attempt was made to
-                               // add something to storage that exceeded the quota." infinitely
-                               // every second.
-                               currentStoreString = JSON.stringify(currentStore);
-                               try {
-                                       // Store
-                                       sessionStorage.setItem('History.store', currentStoreString);
-                               }
-                               catch (e) {
-                                       if (e.code === DOMException.QUOTA_EXCEEDED_ERR) {
-                                               if (sessionStorage.length) {
-                                                       // Workaround for a bug seen on iPads. Sometimes the quota exceeded error comes up and simply
-                                                       // removing/resetting the storage can work.
-                                                       sessionStorage.removeItem('History.store');
-                                                       sessionStorage.setItem('History.store', currentStoreString);
-                                               } else {
-                                                       // Otherwise, we're probably private browsing in Safari, so we'll ignore the exception.
-                                               }
-                                       } else {
-                                               throw e;
-                                       }
-                               }
-                       };
-
-                       // For Internet Explorer
-                       History.intervalList.push(setInterval(History.onUnload,this.storeInterval));
-
-                       // For Other Browsers
-                       History.Adapter.bind(window,'beforeunload',History.onUnload);
-                       History.Adapter.bind(window,'unload',History.onUnload);
-
-                       // Both are enabled for consistency
-               }
-
-               // Non-Native pushState Implementation
-               if ( !History.emulated.pushState ) {
-                       // Be aware, the following is only for native pushState implementations
-                       // If you are wanting to include something for all browsers
-                       // Then include it above this if block
-
-                       /**
-                        * Setup Safari Fix
-                        */
-                       if ( History.bugs.safariPoll ) {
-                               History.intervalList.push(setInterval(History.safariStatePoll, this.safariPollInterval));
-                       }
-
-                       /**
-                        * Ensure Cross Browser Compatibility
-                        */
-                       if ( window.navigator.vendor === 'Apple Computer, Inc.' || (window.navigator.appCodeName||'') === 'Mozilla' ) {
-                               /**
-                                * Fix Safari HashChange Issue
-                                */
-
-                               // Setup Alias
-                               History.Adapter.bind(window,'hashchange',function(){
-                                       History.Adapter.trigger(window,'popstate');
-                               });
-
-                               // Initialise Alias
-                               if ( History.getHash() ) {
-                                       History.Adapter.onDomLoad(function(){
-                                               History.Adapter.trigger(window,'hashchange');
-                                       });
-                               }
-                       }
-
-               } // !History.emulated.pushState
-
-
-       }; // History.initCore
-
-       // Try to Initialise History
-       if (!History.options || !History.options.delayInit) {
-               History.init();
-       }
-
-})(window);
\ No newline at end of file
+    
+    // When the page is closed
+    onUnload : function(){
+        // Prepare
+        var    currentStore, item, currentStoreString;
+    
+        // Fetch
+        try {
+            currentStore = JSON.parse(this.sessionStorage.getItem('Roo.History.store'))||{};
+        }
+        catch ( err ) {
+            currentStore = {};
+        }
+    
+        // Ensure
+        currentStore.idToState = currentStore.idToState || {};
+        currentStore.urlToId = currentStore.urlToId || {};
+        currentStore.stateToId = currentStore.stateToId || {};
+    
+        // Sync
+        for ( item in this.idToState ) {
+            if ( !this.idToState.hasOwnProperty(item) ) {
+                continue;
+            }
+            currentStore.idToState[item] = this.idToState[item];
+        }
+        for ( item in this.urlToId ) {
+            if ( !this.urlToId.hasOwnProperty(item) ) {
+                continue;
+            }
+            currentStore.urlToId[item] = this.urlToId[item];
+        }
+        for ( item in this.stateToId ) {
+            if ( !this.stateToId.hasOwnProperty(item) ) {
+                continue;
+            }
+            currentStore.stateToId[item] = this.stateToId[item];
+        }
+    
+        // Update
+        this.store = currentStore;
+        this.normalizeStore();
+    
+        // In Safari, going into Private Browsing mode causes the
+        // Session Storage object to still exist but if you try and use
+        // or set any property/function of it it throws the exception
+        // "QUOTA_EXCEEDED_ERR: DOM Exception 22: An attempt was made to
+        // add something to storage that exceeded the quota." infinitely
+        // every second.
+        currentStoreString = JSON.stringify(currentStore);
+        try {
+            // Store
+            this.sessionStorage.setItem('Roo.History.store', currentStoreString);
+        }
+        catch (e) {
+            if (e.code === DOMException.QUOTA_EXCEEDED_ERR) {
+                if (this.sessionStorage.length) {
+                    // Workaround for a bug seen on iPads. Sometimes the quota exceeded error comes up and simply
+                    // removing/resetting the storage can work.
+                    this.sessionStorage.removeItem('Roo.History.store');
+                    this.sessionStorage.setItem('Roo.History.store', currentStoreString);
+                } else {
+                    // Otherwise, we're probably private browsing in Safari, so we'll ignore the exception.
+                }
+            } else {
+                throw e;
+            }
+        }
+    }
+};
\ No newline at end of file