/**
* History.js HTML5 Support
* @author Benjamin Arthur Lupton
* @copyright 2010-2011 Benjamin Arthur Lupton
* @license New BSD License
*/
(function(window,undefined){
// --------------------------------------------------------------------------
// Initialise
// History Object
window.History = window.History||{};
window._History = window._History||{};
// Localise Globals
var
console = window.console||undefined, // Prevent a JSLint complain
document = window.document, // Make sure we are using the correct document
_History = window._History, // Private History Object
History = window.History, // Public History Object
history = window.history; // Old History Object
// Check Existence of History.js
if ( typeof History.initHtml5 !== 'undefined' ) {
throw new Error('History.js HTML5 Support has already been loaded...');
}
// Initialise
History.initHtml5 = function(){
// ----------------------------------------------------------------------
// Check Status
if ( typeof History.Adapter === 'undefined' ) {
return false;
}
// ----------------------------------------------------------------------
// Debug Helpers
/**
* History.options
* Configurable options
*/
History.options = {
/**
* History.options.hashChangeCheckerDelay
* How long should the interval be before hashchange checks
*/
hashChangeCheckerDelay: 100,
/**
* History.options.busyDelay
* How long should we wait between busy events
*/
busyDelay: 250
};
// ----------------------------------------------------------------------
// Debug Helpers
/**
* History.debug(message,...)
* Logs the passed arguments if debug enabled
*/
History.debug = function(){
if ( (History.debug.enable||false) ) {
History.log.apply(History,arguments);
}
};
History.debug.enable = false;
/**
* History.log(message,...)
* Logs the passed arguments
*/
History.log = function(){
// Prepare
var
consoleExists = (typeof console !== 'undefined'),
textarea = document.getElementById('log'),
message = ("\n"+arguments[0]+"\n"),
i
;
// Write to Console
if ( consoleExists ) {
var
args = Array.prototype.slice.call(arguments),
message = args.shift();
if ( typeof console.debug !== 'undefined' ) {
console.debug.apply(console,[message,args]);
}
else {
console.log.apply(console,[message,args]);
}
}
// Write to log
for ( i=1,n=arguments.length; i
* @author James Padolsey
*/
_History.getInternetExplorerMajorVersion = function(){
var result = _History.getInternetExplorerMajorVersion.cached =
(typeof _History.getInternetExplorerMajorVersion.cached !== 'undefined')
? _History.getInternetExplorerMajorVersion.cached
: (function(){
var undef,
v = 3,
div = document.createElement('div'),
all = div.getElementsByTagName('i');
while (
div.innerHTML = '',
all[0]
);
return v > 4 ? v : undef;
})()
;
return result;
};
/**
* _History.isInternetExplorer()
* Are we using Internet Explorer?
* @return {boolean}
* @license Public Domain
* @author Benjamin Arthur Lupton
*/
_History.isInternetExplorer = function(){
var result = _History.isInternetExplorer.cached =
(typeof _History.isInternetExplorer.cached !== 'undefined')
? _History.isInternetExplorer.cached
: (_History.getInternetExplorerMajorVersion() !== 0)
;
return result;
};
/**
* History.emulated
* Which features require emulating?
*/
History.emulated = {
pushState: !Boolean(window.history && window.history.pushState && window.history.replaceState)
};
/**
* _History.isEmptyObject(obj)
* Checks to see if the Object is Empty
* @param {Object} obj
* @return {boolean}
*/
_History.isEmptyObject = function(obj) {
for ( var key in obj ) {
if ( !this.hasOwnProperty(key) ) {
continue;
}
return false;
}
return true;
};
/**
* _History.cloneObject(obj)
* Clones a object
* @param {Object} obj
* @return {Object}
*/
_History.cloneObject = function(obj) {
var hash,newObj;
if ( obj ) {
hash = JSON.stringify(obj);
newObj = JSON.parse(hash);
}
else {
newObj = {};
}
return newObj;
};
// ----------------------------------------------------------------------
// State Object Helpers
/**
* History.contractUrl(url)
* Ensures that we have a relative URL and not a absolute URL
* @param {string} url
* @return {string} url
*/
History.contractUrl = function(url){
// Prepare
url = History.expandUrl(url);
// Prepare for Base Domain
var baseDomain = document.location.protocol+'//'+(document.location.hostname||document.location.host);
if ( document.location.port||false ) {
baseDomain += ':'+document.location.port;
}
baseDomain += '/';
// Adjust for Base Domain
url = url.replace(baseDomain,'/');
// Return url
return url;
};
/**
* History.expandUrl(url)
* Ensures that we have an absolute URL and not a relative URL
* @param {string} url
* @return {string} url
*/
History.expandUrl = function(url){
// Prepare
url = url||'';
// Test for Full URL
if ( /[a-z]+\:\/\//.test(url) ) {
// We have a Full URL
}
// Relative URL
else {
// Test for Base Page
if ( url.length === 0 || url.substring(0,1) === '?' ) {
// Fetch Base Page
var basePage = document.location.href.replace(/[#\?].*/,'');
// Adjust Page
url = basePage+url;
}
// No Base Page
else {
// Prepare for Base Element
var
baseElements = document.getElementsByTagName('base'),
baseElement = null,
baseHref = '';
// Test for Base Element
if ( baseElements.length === 1 ) {
// Prepare for Base Element
baseElement = baseElements[0];
baseHref = baseElement.href;
if ( baseHref[baseHref.length-1] !== '/' ) baseHref += '/';
// Adjust for Base Element
url = baseHref+url.replace(/^\//,'');
}
// No Base Element
else {
// Test for Base URL
if ( url.substring(0,1) === '.' ) {
// Prepare for Base URL
var baseUrl = document.location.href.replace(/[#\?].*/,'').replace(/[^\/]+$/,'');
if ( baseUrl[baseUrl.length-1] !== '/' ) baseUrl += '/';
// Adjust for Base URL
url = baseUrl + url;
}
// No Base URL
else {
// Prepare for Base Domain
var baseDomain = document.location.protocol+'//'+(document.location.hostname||document.location.host);
if ( document.location.port||false ) {
baseDomain += ':'+document.location.port;
}
baseDomain += '/';
// Adjust for Base Domain
url = baseDomain+url.replace(/^\//,'');
}
}
}
}
// Return url
return url;
};
/**
* History.expandState(State)
* Expands a State Object
* @param {object} State
* @return {object}
*/
History.expandState = function(oldState){
oldState = oldState||{};
var newState = {
'data': oldState.data||{},
'url': History.expandUrl(oldState.url||''),
'title': oldState.title||''
};
newState.data.title = newState.data.title||newState.title;
newState.data.url = newState.data.url||newState.url;
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.expandState(State);
// Return object
return State;
};
/**
* History.expandHash(hash)
* Expands a Hash into a StateHash if applicable
* @param {string} hash
* @return {Object|null} State
*/
History.expandHash = function(hash){
// Prepare
var State = null;
// JSON
try {
State = JSON.parse(hash);
}
catch ( Exception ) {
var
parts = /(.*)\/uid=([0-9]+)$/.exec(hash),
url = parts ? (parts[1]||hash) : hash,
uid = parts ? String(parts[2]||'') : '';
if ( uid ) {
State = _History.getStateByUid(uid)||null;
}
if ( !State && /\//.test(hash) ) {
// Is a URL
var expandedUrl = History.expandUrl(hash);
State = History.createStateObject(null,null,expandedUrl);
}
else {
// Non State Hash
// do nothing
}
}
// Expand
State = State ? History.expandState(State) : null;
// Return State
return State;
};
/**
* History.contractState(State)
* Creates a Hash for the State Object
* @param {object} passedState
* @return {string} hash
*/
History.contractState = function(passedState){
// Check
if ( !passedState ) {
return null;
}
// Prepare
var
hash = null,
State = _History.cloneObject(passedState);
// Ensure State
if ( State ) {
// Clean
State.data = State.data||{};
delete State.data.title;
delete State.data.url;
// Handle
if ( _History.isEmptyObject(State) && !State.title ) {
hash = History.contractUrl(State.url);
}
else {
// Serialised Hash
hash = JSON.stringify(State);
// Has it been associated with a UID?
var uid;
if ( typeof _History.hashesToUids[hash] !== 'undefined' ) {
uid = _History.hashesToUids[hash];
}
else {
while ( true ) {
uid = String(Math.floor(Math.random()*1000));
if ( typeof _History.uidsToStates[uid] === 'undefined' ) {
break;
}
}
}
// Associate UID with Hash
_History.hashesToUids[hash] = uid;
_History.uidsToStates[uid] = State;
// Simplified Hash
hash = History.contractUrl(State.url)+'/uid='+uid;
}
}
// Return hash
return hash;
};
/**
* _History.uidsToStates
* UIDs to States
*/
_History.uidsToStates = {};
/**
* _History.hashesToUids
* Serialised States to UIDs
*/
_History.hashesToUids = {};
/**
* _History.getStateByUid(uid)
* Get a state by it's UID
* @param {string} uid
*/
_History.getStateByUid = function(uid){
uid = String(uid);
var State = _History.uidsToStates[uid]||undefined;
return State;
};
// ----------------------------------------------------------------------
// State Storage
/**
* _History.statesByUrl
* Store the states indexed by their URLs
*/
_History.statesByUrl = {};
/**
* _History.duplicateStateUrls
* Which urls have duplicate states (indexed by url)
*/
_History.duplicateStateUrls = {};
/**
* _History.statesByHash
* Store the states indexed by their Hashes
*/
_History.statesByHash = {};
/**
* _History.savedStates
* Store the states in an array
*/
_History.savedStates = [];
/**
* History.getState()
* Get an object containing the data, title and url of the current state
* @return {Object} State
*/
History.getState = function(){
return _History.getStateByIndex();
};
/**
* History.getStateHash()
* Get the hash of the current state
* @return {string} hash
*/
History.getStateHash = function(){
return History.contractState(History.getState());
};
/**
* _History.getStateByUrl
* Get a state by it's url
* @param {string} stateUrl
*/
_History.getStateByUrl = function(stateUrl){
var State = _History.statesByUrl[stateUrl]||undefined;
return State;
};
/**
* _History.getStateByHash
* Get a state by it's hash
* @param {string} stateHash
*/
_History.getStateByHash = function(stateHash){
var State = _History.statesByHash[stateHash]||undefined;
return State;
};
/**
* _History.storeState
* Store a State
* @param {object} State
* @return {boolean} true
*/
_History.storeState = function(newState){
// Prepare
var
newStateHash = History.contractState(newState),
oldState = _History.getStateByUrl(newState.url);
// Check for Conflict
if ( typeof oldState !== 'undefined' ) {
// Compare Hashes
var oldStateHash = History.contractState(oldState);
if ( oldStateHash !== newStateHash ) {
// We have a conflict
_History.duplicateStateUrls[newState.url] = true;
}
}
// Store the State
_History.statesByUrl[newState.url] = _History.statesByHash[newStateHash] = newState;
// Return true
return true;
};
/**
* _History.isLastState(newState)
* Tests to see if the state is the last state
* @param {Object} newState
* @return {boolean} isLast
*/
_History.isLastState = function(newState){
// Prepare
var
newStateHash = History.contractState(newState),
oldStateHash = History.getStateHash();
// Check
var isLast = _History.savedStates.length && newStateHash === oldStateHash;
// Return isLast
return isLast;
};
/**
* _History.saveState
* Push a State
* @param {Object} newState
* @return {boolean} changed
*/
_History.saveState = function(newState){
// Check Hash
if ( _History.isLastState(newState) ) {
return false;
}
// Push the State
_History.savedStates.push(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.stateUrlExists
* Checks if the State Url Exists
* @param {string} stateUrl
* @return {boolean} exists
*/
_History.stateUrlExists = function(stateUrl){
// Prepare
var exists = typeof _History.statesByUrl[stateUrl] !== 'undefined';
// Return exists
return exists;
};
/**
* _History.urlDuplicateExists
* Check if the url has multiple states associated to it
* @param {string} stateUrl
* @return {boolean} exists
*/
_History.urlDuplicateExists = function(stateUrl){
var exists = typeof _History.duplicateStateUrls[stateUrl] !== 'undefined';
return exists;
};
// ----------------------------------------------------------------------
// Hash Helpers
/**
* History.getHash()
* Gets the current document hash
* @return {string}
*/
History.getHash = function(){
var hash = _History.unescapeHash(document.location.hash);
return hash;
};
/**
* _History.unescapeHash()
* Normalise and Unescape a Hash
* @return {string}
*/
_History.unescapeHash = function(hash){
var result = _History.normalizeHash(hash);
// Unescape hash
if ( /[\%]/.test(result) ) {
result = unescape(result);
}
// Return result
return result;
};
/**
* _History.normalizeHash()
* Normalise a hash across browsers
* @return {string}
*/
_History.normalizeHash = function(hash){
var result = hash.replace(/[^#]*#/,'').replace(/#.*/, '');
// Return result
return result;
};
/**
* History.setHash(hash)
* Sets the document hash
* @param {string} hash
* @return {string}
*/
History.setHash = function(hash,queue){
// 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;
}
// Prepare
var adjustedHash = _History.escapeHash(hash);
// Log hash
History.debug('History.setHash',this,arguments,'hash:',hash,'adjustedHash:',adjustedHash,'oldHash:',document.location.hash);
// Make Busy + Continue
History.busy(true);
// Apply hash
document.location.hash = adjustedHash;
// Return hash
return hash;
};
/**
* _History.escape()
* Normalise and Escape a Hash
* @return {string}
*/
_History.escapeHash = function(hash){
var result = _History.normalizeHash(hash);
// Escape hash
if ( /[^a-zA-Z0-9\/\-\_\%\.]/.test(result) ) {
result = escape(result);
}
// Return result
return result;
};
/**
* History.extractHashFromUrl(url)
* Extracts the Hash from a URL
* @param {string} url
* @return {string} url
*/
History.extractHashFromUrl = function(url){
// Extract the hash
var hash = String(url)
.replace(/([^#]*)#?([^#]*)#?(.*)/, '$2')
;
// Unescape hash
hash = _History.unescapeHash(hash);
// Return hash
return hash;
};
/**
* History.isTraditionalAnchor(url)
* Checks to see if the url is a traditional anchor
* @param {string} url
* @return {boolean}
*/
History.isTraditionalAnchor = function(url){
var
hash = History.extractHashFromUrl(url),
el = document.getElementById(hash),
isTraditionalAnchor = typeof el !== 'undefined';
// Return isTraditionalAnchor
return isTraditionalAnchor;
};
// ----------------------------------------------------------------------
// 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){
History.debug('History.busy: called: changing ['+(History.busy.flag||false)+'] to ['+(value||false)+']', History.queues);
// Apply
if ( typeof value !== 'undefined' ) {
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(){
if ( History.busy.flag ) return;
for ( var i=History.queues.length-1; i >= 0; --i ) {
var queue = History.queues[i];
if ( queue.length === 0 ) continue;
var item = queue.shift();
History.debug('History.busy: firing', item);
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.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){
History.debug('History.pushQueue: called', arguments);
// Prepare the queue
History.queues[item.queue||0] = History.queues[item.queue||0]||[];
// Add to the queue
History.queues[item.queue||0].push(item);
// End pushQueue closure
return true;
};
/**
* 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);
}
// End queue closure
return true;
};
// ----------------------------------------------------------------------
// 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 a bug in IE6,IE7
if ( History.emulated.hashChange && _History.isInternetExplorer() ) {
// Prepare
var currentHash = History.getHash();
// Apply Check
setTimeout(function(){
var newHash = History.getHash();
if ( newHash === currentHash ) {
// No change occurred, try again
History.debug('History.back: trying again');
return History.back(false);
}
return true;
},History.options.hashChangeCheckerDelay*5);
}
// 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 a bug in IE6,IE7
if ( History.emulated.hashChange && _History.isInternetExplorer() ) {
// Prepare
var currentHash = History.getHash();
// Apply Check
setTimeout(function(){
var newHash = History.getHash();
if ( newHash === currentHash ) {
// No change occurred, try again
History.debug('History.forward: trying again');
return History.forward(false);
}
return true;
},History.options.hashChangeCheckerDelay*5);
}
// 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);
// Handle
if ( index > 0 ) {
// Forward
for ( var i=1; i<=index; ++i ) {
History.forward(queue);
}
}
else if ( index < 0 ) {
// Backward
for ( var i=-1; i>=index; --i ) {
History.back(queue);
}
}
else {
throw new Error('History.go: History.go requires a positive or negative integer passed.');
}
// End go closure
return true;
};
// ----------------------------------------------------------------------
// HTML5 State Support
if ( !History.emulated.pushState ) {
/*
* Use native HTML5 History API Implementation
*/
/**
* _History.onPopState(event,extra)
* Refresh the Current State
*/
_History.onPopState = function(event){
History.debug('_History.onPopState',this,arguments);
// Check for a Hash, and handle apporiatly
var currentHash = unescape(History.getHash());
if ( currentHash ) {
// Expand Hash
var currentState = History.expandHash(currentHash);
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.tite, 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
return false;
}
// Prepare
var
currentStateHashExits = null,
stateData = {},
stateTitle = null,
stateUrl = null,
newState = null;
// Prepare
event = event||{};
if ( typeof event.state === 'undefined' ) {
// jQuery
if ( typeof event.originalEvent !== 'undefined' && typeof event.originalEvent.state !== 'undefined' ) {
event.state = event.originalEvent.state;
}
// MooTools
else if ( typeof event.event !== 'undefined' && typeof event.event.state !== 'undefined' ) {
event.state = event.event.state;
}
}
// Fetch Data
if ( event.state === null ) {
// Vanilla: State has no data (new state, not pushed)
stateData = event.state;
}
else if ( typeof event.state !== 'undefined' ) {
// Vanilla: Back/forward button was used
// Using Chrome Fix
var
newStateUrl = History.expandUrl(document.location.href),
oldState = _History.getStateByUrl(newStateUrl),
duplicateExists = _History.urlDuplicateExists(newStateUrl);
// Does oldState Exist?
if ( typeof oldState !== 'undefined' && !duplicateExists ) {
stateData = oldState.data;
}
else {
stateData = event.state;
}
// Use the way that should work
// stateData = event.state;
}
else {
// Vanilla: A new state was pushed, and popstate was called manually
// Get State object from the last state
var
newStateUrl = History.expandUrl(document.location.href),
oldState = _History.getStateByUrl(newStateUrl);
// Check if the URLs match
if ( oldState && newStateUrl == oldState.url ) {
stateData = oldState.data;
}
else {
throw new Error('Unknown state');
}
}
// Resolve newState
stateData = (typeof stateData !== 'object' || stateData === null) ? {} : stateData;
stateTitle = stateData.title||'',
stateUrl = stateData.url||document.location.href,
newState = History.createStateObject(stateData,stateTitle,stateUrl);
// Check if we are the same state
if ( _History.isLastState(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;
}
// Log
History.debug(
'_History.onPopState',
'newState:', newState,
'oldState:', _History.getStateByUrl(History.expandUrl(document.location.href)),
'duplicateExists:', _History.urlDuplicateExists(History.expandUrl(document.location.href))
);
// Store the State
_History.storeState(newState);
_History.saveState(newState);
// Force update of the title
if ( newState.title ) {
document.title = newState.title
}
// 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){
// Check the State
if ( History.extractHashFromUrl(url) ) {
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);
// Store the newState
_History.storeState(newState);
// Push the newState
history.pushState(newState.data,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){
// Check the State
if ( History.extractHashFromUrl(url) ) {
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);
// Store the newState
_History.storeState(newState);
// Push the newState
history.replaceState(newState.data,newState.title,newState.url);
// Fire HTML5 Event
History.Adapter.trigger(window,'popstate');
// End replaceState closure
return true;
}
/**
* Ensure Cross Browser Compatibility
*/
if ( navigator.vendor === 'Apple Computer, Inc.' ) {
/**
* Fix Safari Initial State Issue
*/
History.Adapter.onDomLoad(function(){
History.debug('Safari Initial State Change Fix');
var currentState = History.createStateObject({},'',document.location.href);
History.pushState(currentState.data,currentState.title,currentState.url);
});
/**
* Fix Safari HashChange Issue
*/
History.Adapter.bind(window,'hashchange',function(){
History.Adapter.trigger(window,'popstate');
});
}
}
}; // init
// Try Load HTML5 Support
History.initHtml5();
})(window);