window['HashHistory'] = (function() {
var
win = window,
doc = document,
loc = location,
his = history,
isOpera
= Object.
prototype.
toString.
call(win.
opera) == '[object Opera]',
isIE = !!win.attachEvent && !isOpera,
// documentMode logic from Modernizr as they have from YUI to filter out IE8 Compat Mode which false positives.
SUPPORTS_PUSHSTATE = ('pushState' in his),
SUPPORTS_HASHCHANGE = (("onhashchange" in win) && ( doc.documentMode === undefined || doc.documentMode > 7 )),
NEED_IFRAME = (isIE && !SUPPORTS_HASHCHANGE),
USE_PUSHSTATE = ("HashHistory_PushState" in win) ? !!win.HashHistory_PushState : true,
currentHash = ( (USE_PUSHSTATE && SUPPORTS_PUSHSTATE) ? loc.pathname + loc.search : '') + loc.hash,
iframe,
waitTime = (isIE || isOpera) ? 400 : 200,
currentWaitTime = 0,
listeners = {all: [], push: [], change: []},
skipNext = false
;
function callback(hash, type) {
currentHash = hash;
hash = hash.substr(1);
var i, l;
for (i = 0, l = listeners.all.length; i < l; i++) {
listeners.all[i](hash);
}
for (i = 0, l = listeners[type].length; i < l; i++) {
listeners[type][i](hash);
}
}
function check() {
var hash = (NEED_IFRAME ? (iframe.contentDocument || iframe.contentWindow.document) : win).location.hash;
if(currentHash != hash) {
callback(hash, 'change');
if (NEED_IFRAME) { loc.hash = hash; }
return true;
}
return false;
}
function wait() {
if (currentWaitTime > 0) {
currentWaitTime = currentWaitTime - waitTime;
}
if (NEED_IFRAME) {
var ifr = iframe.contentWindow.document;
ifr.open();
ifr.close();
ifr.location.hash = currentHash;
}
loc.hash = currentHash;
callback(currentHash, 'push');
skipNext = true;
}
function pushState(newhash) {
if (USE_PUSHSTATE && SUPPORTS_PUSHSTATE) {
currentHash = '/'+newhash;
his.pushState({}, document.title, currentHash);
callback(currentHash, 'push');
}
else {
currentHash = '#'+newhash;
/*Now queue up this add request*/
win.setTimeout(wait, currentWaitTime);
/*Indicate that the next request will have to wait for awhile*/
currentWaitTime = currentWaitTime + waitTime;
}
}
function addListener(type, handle) {
if (typeof handle === 'function') { listeners[type][listeners[type].length] = handle; }
}
function getCurrent() {
return currentHash.substr(1);
}
function getHash() {
if (USE_PUSHSTATE && SUPPORTS_PUSHSTATE) {
return loc.hash.substr(1);
}
else if (/#/.test(getCurrent())) {
return currentHash.split('#')[2];
}
return '';
}
function onhashchange() {
if (skipNext) { skipNext = false; }
else { callback(loc.hash, 'change'); }
}
if (USE_PUSHSTATE && SUPPORTS_PUSHSTATE) {
skipNext = true;
win.addEventListener('popstate', function() {
if (!skipNext) { callback(loc.pathname+loc.hash, 'change'); }
else { skipNext = false; }
}, false);
}
else if (SUPPORTS_HASHCHANGE) {
(win.addEventListener || win.attachEvent)('hashchange', onhashchange, false);
}
else {
if (NEED_IFRAME) {
// add hidden iframe for IE
iframe = doc.createElement('iframe');
iframe.src = 'javascript:false;';
iframe.style.display = 'none';
doc.documentElement.appendChild(iframe);
var ifr = iframe.contentWindow.document;
ifr.open();
ifr.close();
ifr.location.hash = currentHash;
}
else if (isOpera) {
// http://my.opera.com/community/forums/topic.dml?id=197771&t=1189129479&page=1
// http://weblogs.asp.net/bleroy/archive/2007/09/07/how-to-build-a-cross-browser-history-management-system.aspx
win.setTimeout(function() {
var div = document.createElement('div');
div.innerHTML = '<img src="javascript:location.href=\"javascript:setInterval(HashHistory.check, 100);\";" style="position:absolute;left:-1000px;top:-1000px;width:1px;height:1px;border:0;">';
document.body.appendChild(div.firstChild);
}, 1);
}
win.setInterval(check, 100);
}
return {
"pushState": pushState,
"check": check,
"addListener": addListener,
"getCurrent": getCurrent,
"getHash": getHash
};
})();