- History.normalizeState = function(oldState){
- // Variables
- var newState, dataNotEmpty;
-
- // Prepare
- if ( !oldState || (typeof oldState !== 'object') ) {
- oldState = {};
- }
-
- // Check
- if ( typeof oldState.normalized !== 'undefined' ) {
- return oldState;
- }
-
- // Adjust
- if ( !oldState.data || (typeof oldState.data !== 'object') ) {
- oldState.data = {};
- }
-
- // ----------------------------------------------------------------
-
- // Create
- newState = {};
- newState.normalized = true;
- newState.title = oldState.title||'';
- newState.url = History.getFullUrl(oldState.url?oldState.url:(History.getLocationHref()));
- newState.hash = History.getShortUrl(newState.url);
- newState.data = History.cloneObject(oldState.data);
-
- // Fetch ID
- newState.id = History.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 = !History.isEmptyObject(newState.data);
-
- // Apply
- if ( (newState.title || dataNotEmpty) && History.options.disableSuid !== true ) {
- // Add ID to Hash
- newState.hash = History.getShortUrl(newState.url).replace(/\??\&_suid.*/,'');
- if ( !/\?/.test(newState.hash) ) {
- newState.hash += '?';
- }
- newState.hash += '&_suid='+newState.id;
- }
-
- // Create the Hashed URL
- newState.hashedUrl = History.getFullUrl(newState.hash);
-
- // ----------------------------------------------------------------
-
- // Update the URL if we have a duplicate
- if ( (History.emulated.pushState || History.bugs.safariPoll) && History.hasUrlDuplicate(newState) ) {
- newState.url = newState.hashedUrl;
- }
-
- // ----------------------------------------------------------------
-
- // Return
- return newState;
- };
-
- /**
- * History.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}
- */
- History.createStateObject = function(data,title,url){
- // Hashify
- var State = {
- 'data': data,
- 'title': title,
- 'url': url
- };
-
- // Expand the State
- State = History.normalizeState(State);
-
- // Return object
- return State;
- };
-
- /**
- * History.getStateById(id)
- * Get a state by it's UID
- * @param {String} id
- */
- History.getStateById = function(id){
- // Prepare
- id = String(id);
-
- // Retrieve
- var State = History.idToState[id] || History.store.idToState[id] || undefined;
-
- // Return State
- return State;
- };
-
- /**
- * Get a State's String
- * @param {State} passedState
- */
- History.getStateString = function(passedState){
- // Prepare
- var State, cleanedState, str;
-
- // Fetch
- State = History.normalizeState(passedState);
-
- // Clean
- cleanedState = {
- data: State.data,
- title: passedState.title,
- url: passedState.url
- };
-
- // Fetch
- str = JSON.stringify(cleanedState);
-
- // Return
- return str;
- };
-
- /**
- * Get a State's ID
- * @param {State} passedState
- * @return {String} id
- */
- History.getStateId = function(passedState){
- // Prepare
- var State, id;
-
- // Fetch
- State = History.normalizeState(passedState);
-
- // Fetch
- id = State.id;
-
- // Return
- return id;
- };
-
- /**
- * History.getHashByState(State)
- * Creates a Hash for the State Object
- * @param {State} passedState
- * @return {String} hash
- */
- History.getHashByState = function(passedState){
- // Prepare
- var State, hash;
-
- // Fetch
- State = History.normalizeState(passedState);
-
- // Hash
- hash = State.hash;
-
- // Return
- return hash;
- };
-
- /**
- * History.extractId(url_or_hash)
- * Get a State ID by it's URL or Hash
- * @param {string} url_or_hash
- * @return {string} id
- */
- History.extractId = function ( url_or_hash ) {
- // Prepare
- var id,parts,url, tmp;
-
- // 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||false;
- };
-
- /**
- * History.isTraditionalAnchor
- * Checks to see if the url is a traditional anchor or not
- * @param {String} url_or_hash
- * @return {Boolean}
- */
- History.isTraditionalAnchor = function(url_or_hash){
- // Check
- var isTraditional = !(/[\/\?\.]/.test(url_or_hash));
-
- // Return
- return isTraditional;
- };
-
- /**
- * History.extractState
- * Get a State by it's URL or Hash
- * @param {String} url_or_hash
- * @return {State|null}
- */
- History.extractState = function(url_or_hash,create){
- // Prepare
- var State = null, id, url;
- create = create||false;
-
- // Fetch SUID
- id = History.extractId(url_or_hash);
- if ( id ) {
- State = History.getStateById(id);
- }
-
- // Fetch SUID returned no State
- if ( !State ) {
- // Fetch URL
- url = History.getFullUrl(url_or_hash);
-
- // Check URL
- id = History.getIdByUrl(url)||false;
- if ( id ) {
- State = History.getStateById(id);
- }
-
- // Create State
- if ( !State && create && !History.isTraditionalAnchor(url_or_hash) ) {
- State = History.createStateObject(null,null,url);
- }
- }
-
- // Return
- return State;
- };
-
- /**
- * History.getIdByUrl()
- * Get a State ID by a State URL
- */
- History.getIdByUrl = function(url){
- // Fetch
- var id = History.urlToId[url] || History.store.urlToId[url] || undefined;
-
- // Return
- return id;
- };
-
- /**
- * History.getLastSavedState()
- * Get an object containing the data, title and url of the current state
- * @return {Object} State
- */
- History.getLastSavedState = function(){
- return History.savedStates[History.savedStates.length-1]||undefined;
- };
-
- /**
- * History.getLastStoredState()
- * Get an object containing the data, title and url of the current state
- * @return {Object} State
- */
- History.getLastStoredState = function(){
- return History.storedStates[History.storedStates.length-1]||undefined;
- };
-
- /**
- * History.hasUrlDuplicate
- * Checks if a Url will have a url conflict
- * @param {Object} newState
- * @return {Boolean} hasDuplicate
- */
- History.hasUrlDuplicate = function(newState) {
- // Prepare
- var hasDuplicate = false,
- oldState;
-
- // Fetch
- oldState = History.extractState(newState.url);
-
- // Check
- hasDuplicate = oldState && oldState.id !== newState.id;
-
- // Return
- return hasDuplicate;
- };
-
- /**
- * History.storeState
- * Store a State
- * @param {Object} newState
- * @return {Object} newState
- */
- History.storeState = function(newState){
- // Store the State
- History.urlToId[newState.url] = newState.id;
-
- // Push the State
- History.storedStates.push(History.cloneObject(newState));
-
- // Return newState
- return newState;
- };
-
- /**
- * History.isLastSavedState(newState)
- * Tests to see if the state is the last state
- * @param {Object} newState
- * @return {boolean} isLast
- */
- History.isLastSavedState = function(newState){
- // Prepare
- var isLast = false,
- newId, oldState, oldId;
-
- // Check
- if ( History.savedStates.length ) {
- newId = newState.id;
- oldState = History.getLastSavedState();
- oldId = oldState.id;
-
- // Check
- isLast = (newId === oldId);
- }
-
- // Return
- return isLast;
- };
-
- /**
- * History.saveState
- * Push a State
- * @param {Object} newState
- * @return {boolean} changed
- */
- History.saveState = function(newState){
- // Check Hash
- if ( History.isLastSavedState(newState) ) {
- return false;
- }
-
- // Push the State
- History.savedStates.push(History.cloneObject(newState));
-
- // Return true
- return true;
- };
-
- /**
- * History.getStateByIndex()
- * Gets a state by the index
- * @param {integer} index
- * @return {Object}
- */
- History.getStateByIndex = function(index){
- // Prepare
- var State = null;
-
- // Handle
- if ( typeof index === 'undefined' ) {
- // Get the last inserted
- State = History.savedStates[History.savedStates.length-1];
- }
- else if ( index < 0 ) {
- // Get from the end
- State = History.savedStates[History.savedStates.length+index];
- }
- else {
- // Get from the beginning
- State = History.savedStates[index];
- }
-
- // Return State
- return State;
- };
-
- /**
- * History.getCurrentIndex()
- * Gets the current index
- * @return (integer)
- */
- History.getCurrentIndex = function(){
- // Prepare
- var index = null;
-
- // No states saved
- if(History.savedStates.length < 1) {
- index = 0;
- }
- else {
- index = History.savedStates.length-1;
- }
- return index;
- };
-
- // ====================================================================
- // Hash Helpers
-
- /**
- * History.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}
- */
- History.getHash = function(doc){
- var url = History.getLocationHref(doc),
- hash;
- hash = History.getHashByUrl(url);
- return hash;
- };
-
- /**
- * History.unescapeHash()
- * normalize and Unescape a Hash
- * @param {String} hash
- * @return {string}
- */
- History.unescapeHash = function(hash){
- // Prepare
- var result = History.normalizeHash(hash);
-
- // Unescape hash
- result = decodeURIComponent(result);
-
- // Return result
- return result;
- };
-
- /**
- * History.normalizeHash()
- * normalize a hash across browsers
- * @return {string}
- */
- History.normalizeHash = function(hash){
- // Prepare
- var result = hash.replace(/[^#]*#/,'').replace(/#.*/, '');
-
- // Return result
- return result;
- };
-
- /**
- * History.setHash(hash)
- * Sets the document hash
- * @param {string} hash
- * @return {History}
- */
- History.setHash = function(hash,queue){
- // Prepare
- var State, pageUrl;
-
- // Handle Queueing
- if ( queue !== false && History.busy() ) {
- // Wait + Push to Queue
- //History.debug('History.setHash: we must wait', arguments);
- History.pushQueue({
- scope: History,
- callback: History.setHash,
- args: arguments,
- queue: queue
- });
- return false;
- }
-
- // Log
- //History.debug('History.setHash: called',hash);
-
- // Make Busy + Continue
- History.busy(true);
-
- // Check if hash is a state
- State = History.extractState(hash,true);
- if ( State && !History.emulated.pushState ) {
- // Hash is a state so skip the setHash
- //History.debug('History.setHash: Hash is a state so skipping the hash set with a direct pushState call',arguments);
-
- // PushState
- History.pushState(State.data,State.title,State.url,false);
- }
- else if ( History.getHash() !== hash ) {
- // Hash is a proper hash, so apply it
-
- // Handle browser bugs
- if ( History.bugs.setHash ) {
- // Fix Safari Bug https://bugs.webkit.org/show_bug.cgi?id=56249
-
- // Fetch the base page
- pageUrl = History.getPageUrl();
-
- // Safari hash apply
- History.pushState(null,null,pageUrl+'#'+hash,false);
- }
- else {
- // Normal hash apply
- document.location.hash = hash;
- }
- }
-
- // Chain
- return History;
- };
-
- /**
- * History.escape()
- * normalize and Escape a Hash
- * @return {string}
- */
- History.escapeHash = function(hash){
- // Prepare
- var result = History.normalizeHash(hash);
-
- // Escape hash
- result = window.encodeURIComponent(result);
-
- // IE6 Escape Bug
- if ( !History.bugs.hashEscape ) {
- // Restore common parts
- result = result
- .replace(/\%21/g,'!')
- .replace(/\%26/g,'&')
- .replace(/\%3D/g,'=')
- .replace(/\%3F/g,'?');
- }
-
- // Return result
- return result;
- };
-
- /**
- * History.getHashByUrl(url)
- * Extracts the Hash from a URL
- * @param {string} url
- * @return {string} url
- */
- History.getHashByUrl = function(url){
- // Extract the hash
- var hash = String(url)
- .replace(/([^#]*)#?([^#]*)#?(.*)/, '$2')
- ;
-
- // Unescape hash
- hash = History.unescapeHash(hash);
-
- // Return hash
- return hash;
- };
-
- /**
- * History.setTitle(title)
- * Applies the title to the document
- * @param {State} newState
- * @return {Boolean}
- */
- History.setTitle = function(newState){
- // Prepare
- var title = newState.title,
- firstState;
-
- // Initial
- if ( !title ) {
- firstState = History.getStateByIndex(0);
- if ( firstState && firstState.url === newState.url ) {
- title = firstState.title||History.options.initialTitle;
- }
- }
-
- // Apply
- try {
- document.getElementsByTagName('title')[0].innerHTML = title.replace('<','<').replace('>','>').replace(' & ',' & ');
- }
- catch ( Exception ) { }
- document.title = title;
-
- // Chain
- return History;
- };
-
-
- // ====================================================================
- // Queueing
-
- /**
- * History.queues
- * The list of queues to use
- * First In, First Out
- */
- History.queues = [];
-
- /**
- * History.busy(value)
- * @param {boolean} value [optional]
- * @return {boolean} busy
- */
- History.busy = function(value){
- // Apply
- if ( typeof value !== 'undefined' ) {
- //History.debug('History.busy: changing ['+(History.busy.flag||false)+'] to ['+(value||false)+']', History.queues.length);
- History.busy.flag = value;
- }
- // Default
- else if ( typeof History.busy.flag === 'undefined' ) {
- History.busy.flag = false;
- }
-
- // Queue
- if ( !History.busy.flag ) {
- // Execute the next item in the queue
- clearTimeout(History.busy.timeout);
- var fireNext = function(){
- var i, queue, item;
- if ( History.busy.flag ) return;
- for ( i=History.queues.length-1; i >= 0; --i ) {
- queue = History.queues[i];
- if ( queue.length === 0 ) continue;
- item = queue.shift();
- History.fireQueueItem(item);
- History.busy.timeout = setTimeout(fireNext,History.options.busyDelay);
- }
- };
- History.busy.timeout = setTimeout(fireNext,History.options.busyDelay);
- }
-
- // Return
- return History.busy.flag;
- };
-
- /**
- * History.busy.flag
- */
- History.busy.flag = false;
-
- /**
- * History.fireQueueItem(item)
- * Fire a Queue Item
- * @param {Object} item
- * @return {Mixed} result
- */
- History.fireQueueItem = function(item){
- return item.callback.apply(item.scope||History,item.args||[]);
- };
-
- /**
- * History.pushQueue(callback,args)
- * Add an item to the queue
- * @param {Object} item [scope,callback,args,queue]
- */
- History.pushQueue = function(item){
- // Prepare the queue
- History.queues[item.queue||0] = History.queues[item.queue||0]||[];
-
- // Add to the queue
- History.queues[item.queue||0].push(item);
-
- // Chain
- return History;
- };
-
- /**
- * History.queue (item,queue), (func,queue), (func), (item)
- * Either firs the item now if not busy, or adds it to the queue
- */
- History.queue = function(item,queue){
- // Prepare
- if ( typeof item === 'function' ) {
- item = {
- callback: item
- };
- }
- if ( typeof queue !== 'undefined' ) {
- item.queue = queue;
- }
-
- // Handle
- if ( History.busy() ) {
- History.pushQueue(item);
- } else {
- History.fireQueueItem(item);
- }
-
- // Chain
- return History;
- };
-
- /**
- * History.clearQueue()
- * Clears the Queue
- */
- History.clearQueue = function(){
- History.busy.flag = false;
- History.queues = [];
- return History;
- };
-
-
- // ====================================================================
- // IE Bug Fix
-
- /**
- * History.stateChanged
- * States whether or not the state has changed since the last double check was initialised
- */
- History.stateChanged = false;
-
- /**
- * History.doubleChecker
- * Contains the timeout used for the double checks
- */
- History.doubleChecker = false;
-
- /**
- * History.doubleCheckComplete()
- * Complete a double check
- * @return {History}
- */
- History.doubleCheckComplete = function(){
- // Update
- History.stateChanged = true;
-
- // Clear
- History.doubleCheckClear();
-
- // Chain
- return History;
- };
-
- /**
- * History.doubleCheckClear()
- * Clear a double check
- * @return {History}
- */
- History.doubleCheckClear = function(){
- // Clear
- if ( History.doubleChecker ) {
- clearTimeout(History.doubleChecker);
- History.doubleChecker = false;
- }
-
- // Chain
- return History;
- };
-
- /**
- * History.doubleCheck()
- * Create a double check
- * @return {History}
- */
- History.doubleCheck = function(tryAgain){
- // Reset
- History.stateChanged = false;
- History.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 ( History.bugs.ieDoubleCheck ) {
- // Apply Check
- History.doubleChecker = setTimeout(
- function(){
- History.doubleCheckClear();
- if ( !History.stateChanged ) {
- //History.debug('History.doubleCheck: State has not yet changed, trying again', arguments);
- // Re-Attempt
- tryAgain();
- }
- return true;
- },
- History.options.doubleCheckInterval
- );
- }
-
- // Chain
- return History;
- };
-
-
- // ====================================================================
- // Safari Bug Fix
-
- /**
- * History.safariStatePoll()
- * Poll the current state
- * @return {History}
- */
- History.safariStatePoll = function(){
- // Poll the URL
-
- // 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;
- }
-
- // Check if we have a state with that url
- // If not create it
- if ( !newState ) {
- //History.debug('History.safariStatePoll: new');
- newState = History.createStateObject();
- }
-
- // Apply the New State
- //History.debug('History.safariStatePoll: trigger');
- History.Adapter.trigger(window,'popstate');
-
- // Chain
- return History;
- };
-
-
- // ====================================================================
- // State Aliases
-
- /**
- * 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;
- }
-
- // Make Busy + Continue
- History.busy(true);
-
- // Fix certain browser bugs that prevent the state from changing
- History.doubleCheck(function(){
- History.back(false);
- });
-
- // Go back
- history.go(-1);
-
- // End back closure
- return 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;
- }
-
- // Make Busy + Continue
- History.busy(true);
-
- // Fix certain browser bugs that prevent the state from changing
- History.doubleCheck(function(){
- History.forward(false);
- });
-
- // Go forward
- history.go(1);
-
- // End forward closure
- return true;
- };
-
- /**
- * 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);
-
- // Prepare
- var i;
-
- // 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.');
- }
-
- // Chain
- return History;
- };
-
-
- // ====================================================================
- // HTML5 State Support
-
- // 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
-
- // Native pushState Implementation
- else {
- /*
- * 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;
- }
-
- // Ensure
- stateId = History.Adapter.extractEventData('state',event,extra) || false;
-
- // 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());
- }
-
- // The State did not exist in our store
- if ( !newState ) {
- // Regenerate the State
- newState = History.createStateObject(null,null,History.getLocationHref());
- }
-
- // Clean
- History.expectedStateId = false;
-
- // 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;
- }
-
- // Store the State
- History.storeState(newState);
- History.saveState(newState);
-
- // Force update of the title
- History.setTitle(newState);
-
- // Fire Our Event
- History.Adapter.trigger(window,'statechange');
- History.busy(false);
-
- // Return true
- return true;
- };
- History.Adapter.bind(window,'popstate',History.onPopState);
-
- /**
- * 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);
-
- // Check the State
- if ( History.getHashByUrl(url) && History.emulated.pushState ) {
- throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).');
- }
-
- // 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;
- }
-
- // Make Busy + Continue
- History.busy(true);
-
- // Create the newState
- var newState = History.createStateObject(data,title,url);
-
- // 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;
-
- // Push the newState
- history.pushState(newState.id,newState.title,newState.url);
-
- // Fire HTML5 Event
- History.Adapter.trigger(window,'popstate');
- }
-
- // End pushState closure
- return true;
- };
-
- /**
- * 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);
-
- // Check the State
- if ( History.getHashByUrl(url) && History.emulated.pushState ) {
- throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).');
- }
-
- // 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;
- }
-
- // Make Busy + Continue
- History.busy(true);
-
- // Create the newState
- var newState = History.createStateObject(data,title,url);
-
- // 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;
-
- // Push the newState
- history.replaceState(newState.id,newState.title,newState.url);
-
- // Fire HTML5 Event
- History.Adapter.trigger(window,'popstate');
- }
-
- // End replaceState closure
- return true;
- };
-
- } // !History.emulated.pushState
-
-
- // ====================================================================
- // 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,History.options.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, History.options.safariPollInterval));
- }
-
- /**
- * Ensure Cross Browser Compatibility
- */
- if ( navigator.vendor === 'Apple Computer, Inc.' || (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