wiselinks 0.3.0 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1 +1,1946 @@
1
- window.JSON||(window.JSON={}),function(){function f(a){return a<10?"0"+a:a}function quote(a){return escapable.lastIndex=0,escapable.test(a)?'"'+a.replace(escapable,function(a){var b=meta[a];return typeof b=="string"?b:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+a+'"'}function str(a,b){var c,d,e,f,g=gap,h,i=b[a];i&&typeof i=="object"&&typeof i.toJSON=="function"&&(i=i.toJSON(a)),typeof rep=="function"&&(i=rep.call(b,a,i));switch(typeof i){case"string":return quote(i);case"number":return isFinite(i)?String(i):"null";case"boolean":case"null":return String(i);case"object":if(!i)return"null";gap+=indent,h=[];if(Object.prototype.toString.apply(i)==="[object Array]"){f=i.length;for(c=0;c<f;c+=1)h[c]=str(c,i)||"null";return e=h.length===0?"[]":gap?"[\n"+gap+h.join(",\n"+gap)+"\n"+g+"]":"["+h.join(",")+"]",gap=g,e}if(rep&&typeof rep=="object"){f=rep.length;for(c=0;c<f;c+=1)d=rep[c],typeof d=="string"&&(e=str(d,i),e&&h.push(quote(d)+(gap?": ":":")+e))}else for(d in i)Object.hasOwnProperty.call(i,d)&&(e=str(d,i),e&&h.push(quote(d)+(gap?": ":":")+e));return e=h.length===0?"{}":gap?"{\n"+gap+h.join(",\n"+gap)+"\n"+g+"}":"{"+h.join(",")+"}",gap=g,e}}"use strict",typeof Date.prototype.toJSON!="function"&&(Date.prototype.toJSON=function(a){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null},String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(a){return this.valueOf()});var JSON=window.JSON,cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={"\b":"\\b","\t":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},rep;typeof JSON.stringify!="function"&&(JSON.stringify=function(a,b,c){var d;gap="",indent="";if(typeof c=="number")for(d=0;d<c;d+=1)indent+=" ";else typeof c=="string"&&(indent=c);rep=b;if(!b||typeof b=="function"||typeof b=="object"&&typeof b.length=="number")return str("",{"":a});throw new Error("JSON.stringify")}),typeof JSON.parse!="function"&&(JSON.parse=function(text,reviver){function walk(a,b){var c,d,e=a[b];if(e&&typeof e=="object")for(c in e)Object.hasOwnProperty.call(e,c)&&(d=walk(e,c),d!==undefined?e[c]=d:delete e[c]);return reviver.call(a,b,e)}var j;text=String(text),cx.lastIndex=0,cx.test(text)&&(text=text.replace(cx,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)}));if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return j=eval("("+text+")"),typeof reviver=="function"?walk({"":j},""):j;throw new SyntaxError("JSON.parse")})}(),function(a,b){"use strict";var c=a.History=a.History||{};if(typeof c.Adapter!="undefined")throw new Error("History.js Adapter has already been loaded...");c.Adapter={handlers:{},_uid:1,uid:function(a){return a._uid||(a._uid=c.Adapter._uid++)},bind:function(a,b,d){var e=c.Adapter.uid(a);c.Adapter.handlers[e]=c.Adapter.handlers[e]||{},c.Adapter.handlers[e][b]=c.Adapter.handlers[e][b]||[],c.Adapter.handlers[e][b].push(d),a["on"+b]=function(a,b){return function(d){c.Adapter.trigger(a,b,d)}}(a,b)},trigger:function(a,b,d){d=d||{};var e=c.Adapter.uid(a),f,g;c.Adapter.handlers[e]=c.Adapter.handlers[e]||{},c.Adapter.handlers[e][b]=c.Adapter.handlers[e][b]||[];for(f=0,g=c.Adapter.handlers[e][b].length;f<g;++f)c.Adapter.handlers[e][b][f].apply(this,[d])},extractEventData:function(a,c){var d=c&&c[a]||b;return d},onDomLoad:function(b){var c=a.setTimeout(function(){b()},2e3);a.onload=function(){clearTimeout(c),b()}}},typeof c.init!="undefined"&&c.init()}(window),function(a,b){"use strict";var c=a.document,d=a.setTimeout||d,e=a.clearTimeout||e,f=a.setInterval||f,g=a.History=a.History||{};if(typeof g.initHtml4!="undefined")throw new Error("History.js HTML4 Support has already been loaded...");g.initHtml4=function(){if(typeof g.initHtml4.initialized!="undefined")return!1;g.initHtml4.initialized=!0,g.enabled=!0,g.savedHashes=[],g.isLastHash=function(a){var b=g.getHashByIndex(),c;return c=a===b,c},g.saveHash=function(a){return g.isLastHash(a)?!1:(g.savedHashes.push(a),!0)},g.getHashByIndex=function(a){var b=null;return typeof a=="undefined"?b=g.savedHashes[g.savedHashes.length-1]:a<0?b=g.savedHashes[g.savedHashes.length+a]:b=g.savedHashes[a],b},g.discardedHashes={},g.discardedStates={},g.discardState=function(a,b,c){var d=g.getHashByState(a),e;return e={discardedState:a,backState:c,forwardState:b},g.discardedStates[d]=e,!0},g.discardHash=function(a,b,c){var d={discardedHash:a,backState:c,forwardState:b};return g.discardedHashes[a]=d,!0},g.discardedState=function(a){var b=g.getHashByState(a),c;return c=g.discardedStates[b]||!1,c},g.discardedHash=function(a){var b=g.discardedHashes[a]||!1;return b},g.recycleState=function(a){var b=g.getHashByState(a);return g.discardedState(a)&&delete g.discardedStates[b],!0},g.emulated.hashChange&&(g.hashChangeInit=function(){g.checkerFunction=null;var b="",d,e,h,i;return g.isInternetExplorer()?(d="historyjs-iframe",e=c.createElement("iframe"),e.setAttribute("id",d),e.style.display="none",c.body.appendChild(e),e.contentWindow.document.open(),e.contentWindow.document.close(),h="",i=!1,g.checkerFunction=function(){if(i)return!1;i=!0;var c=g.getHash()||"",d=g.unescapeHash(e.contentWindow.document.location.hash)||"";return c!==b?(b=c,d!==c&&(h=d=c,e.contentWindow.document.open(),e.contentWindow.document.close(),e.contentWindow.document.location.hash=g.escapeHash(c)),g.Adapter.trigger(a,"hashchange")):d!==h&&(h=d,g.setHash(d,!1)),i=!1,!0}):g.checkerFunction=function(){var c=g.getHash();return c!==b&&(b=c,g.Adapter.trigger(a,"hashchange")),!0},g.intervalList.push(f(g.checkerFunction,g.options.hashChangeInterval)),!0},g.Adapter.onDomLoad(g.hashChangeInit)),g.emulated.pushState&&(g.onHashChange=function(b){var d=b&&b.newURL||c.location.href,e=g.getHashByUrl(d),f=null,h=null,i=null,j;return g.isLastHash(e)?(g.busy(!1),!1):(g.doubleCheckComplete(),g.saveHash(e),e&&g.isTraditionalAnchor(e)?(g.Adapter.trigger(a,"anchorchange"),g.busy(!1),!1):(f=g.extractState(g.getFullUrl(e||c.location.href,!1),!0),g.isLastSavedState(f)?(g.busy(!1),!1):(h=g.getHashByState(f),j=g.discardedState(f),j?(g.getHashByIndex(-2)===g.getHashByState(j.forwardState)?g.back(!1):g.forward(!1),!1):(g.pushState(f.data,f.title,f.url,!1),!0))))},g.Adapter.bind(a,"hashchange",g.onHashChange),g.pushState=function(b,d,e,f){if(g.getHashByUrl(e))throw new Error("History.js does not support states with fragement-identifiers (hashes/anchors).");if(f!==!1&&g.busy())return g.pushQueue({scope:g,callback:g.pushState,args:arguments,queue:f}),!1;g.busy(!0);var h=g.createStateObject(b,d,e),i=g.getHashByState(h),j=g.getState(!1),k=g.getHashByState(j),l=g.getHash();return g.storeState(h),g.expectedStateId=h.id,g.recycleState(h),g.setTitle(h),i===k?(g.busy(!1),!1):i!==l&&i!==g.getShortUrl(c.location.href)?(g.setHash(i,!1),!1):(g.saveState(h),g.Adapter.trigger(a,"statechange"),g.busy(!1),!0)},g.replaceState=function(a,b,c,d){if(g.getHashByUrl(c))throw new Error("History.js does not support states with fragement-identifiers (hashes/anchors).");if(d!==!1&&g.busy())return g.pushQueue({scope:g,callback:g.replaceState,args:arguments,queue:d}),!1;g.busy(!0);var e=g.createStateObject(a,b,c),f=g.getState(!1),h=g.getStateByIndex(-2);return g.discardState(f,e,h),g.pushState(e.data,e.title,e.url,!1),!0}),g.emulated.pushState&&g.getHash()&&!g.emulated.hashChange&&g.Adapter.onDomLoad(function(){g.Adapter.trigger(a,"hashchange")})},typeof g.init!="undefined"&&g.init()}(window),function(a,b){"use strict";var c=a.console||b,d=a.document,e=a.navigator,f=a.sessionStorage||!1,g=a.setTimeout,h=a.clearTimeout,i=a.setInterval,j=a.clearInterval,k=a.JSON,l=a.alert,m=a.History=a.History||{},n=a.history;k.stringify=k.stringify||k.encode,k.parse=k.parse||k.decode;if(typeof m.init!="undefined")throw new Error("History.js Core has already been loaded...");m.init=function(){return typeof m.Adapter=="undefined"?!1:(typeof m.initCore!="undefined"&&m.initCore(),typeof m.initHtml4!="undefined"&&m.initHtml4(),!0)},m.initCore=function(){if(typeof m.initCore.initialized!="undefined")return!1;m.initCore.initialized=!0,m.options=m.options||{},m.options.hashChangeInterval=m.options.hashChangeInterval||100,m.options.safariPollInterval=m.options.safariPollInterval||500,m.options.doubleCheckInterval=m.options.doubleCheckInterval||500,m.options.storeInterval=m.options.storeInterval||1e3,m.options.busyDelay=m.options.busyDelay||250,m.options.debug=m.options.debug||!1,m.options.initialTitle=m.options.initialTitle||d.title,m.intervalList=[],m.clearAllIntervals=function(){var a,b=m.intervalList;if(typeof b!="undefined"&&b!==null){for(a=0;a<b.length;a++)j(b[a]);m.intervalList=null}},m.debug=function(){(m.options.debug||!1)&&m.log.apply(m,arguments)},m.log=function(){var a=typeof c!="undefined"&&typeof c.log!="undefined"&&typeof c.log.apply!="undefined",b=d.getElementById("log"),e,f,g,h,i;a?(h=Array.prototype.slice.call(arguments),e=h.shift(),typeof c.debug!="undefined"?c.debug.apply(c,[e,h]):c.log.apply(c,[e,h])):e="\n"+arguments[0]+"\n";for(f=1,g=arguments.length;f<g;++f){i=arguments[f];if(typeof i=="object"&&typeof k!="undefined")try{i=k.stringify(i)}catch(j){}e+="\n"+i+"\n"}return b?(b.value+=e+"\n-----\n",b.scrollTop=b.scrollHeight-b.clientHeight):a||l(e),!0},m.getInternetExplorerMajorVersion=function(){var a=m.getInternetExplorerMajorVersion.cached=typeof m.getInternetExplorerMajorVersion.cached!="undefined"?m.getInternetExplorerMajorVersion.cached:function(){var a=3,b=d.createElement("div"),c=b.getElementsByTagName("i");while((b.innerHTML="<!--[if gt IE "+ ++a+"]><i></i><![endif]-->")&&c[0]);return a>4?a:!1}();return a},m.isInternetExplorer=function(){var a=m.isInternetExplorer.cached=typeof m.isInternetExplorer.cached!="undefined"?m.isInternetExplorer.cached:Boolean(m.getInternetExplorerMajorVersion());return a},m.emulated={pushState:!Boolean(a.history&&a.history.pushState&&a.history.replaceState&&!/ Mobile\/([1-7][a-z]|(8([abcde]|f(1[0-8]))))/i.test(e.userAgent)&&!/AppleWebKit\/5([0-2]|3[0-2])/i.test(e.userAgent)),hashChange:Boolean(!("onhashchange"in a||"onhashchange"in d)||m.isInternetExplorer()&&m.getInternetExplorerMajorVersion()<8)},m.enabled=!m.emulated.pushState,m.bugs={setHash:Boolean(!m.emulated.pushState&&e.vendor==="Apple Computer, Inc."&&/AppleWebKit\/5([0-2]|3[0-3])/.test(e.userAgent)),safariPoll:Boolean(!m.emulated.pushState&&e.vendor==="Apple Computer, Inc."&&/AppleWebKit\/5([0-2]|3[0-3])/.test(e.userAgent)),ieDoubleCheck:Boolean(m.isInternetExplorer()&&m.getInternetExplorerMajorVersion()<8),hashEscape:Boolean(m.isInternetExplorer()&&m.getInternetExplorerMajorVersion()<7)},m.isEmptyObject=function(a){for(var b in a)return!1;return!0},m.cloneObject=function(a){var b,c;return a?(b=k.stringify(a),c=k.parse(b)):c={},c},m.getRootUrl=function(){var a=d.location.protocol+"//"+(d.location.hostname||d.location.host);if(d.location.port||!1)a+=":"+d.location.port;return a+="/",a},m.getBaseHref=function(){var a=d.getElementsByTagName("base"),b=null,c="";return a.length===1&&(b=a[0],c=b.href.replace(/[^\/]+$/,"")),c=c.replace(/\/+$/,""),c&&(c+="/"),c},m.getBaseUrl=function(){var a=m.getBaseHref()||m.getBasePageUrl()||m.getRootUrl();return a},m.getPageUrl=function(){var a=m.getState(!1,!1),b=(a||{}).url||d.location.href,c;return c=b.replace(/\/+$/,"").replace(/[^\/]+$/,function(a,b,c){return/\./.test(a)?a:a+"/"}),c},m.getBasePageUrl=function(){var a=d.location.href.replace(/[#\?].*/,"").replace(/[^\/]+$/,function(a,b,c){return/[^\/]$/.test(a)?"":a}).replace(/\/+$/,"")+"/";return a},m.getFullUrl=function(a,b){var c=a,d=a.substring(0,1);return b=typeof b=="undefined"?!0:b,/[a-z]+\:\/\//.test(a)||(d==="/"?c=m.getRootUrl()+a.replace(/^\/+/,""):d==="#"?c=m.getPageUrl().replace(/#.*/,"")+a:d==="?"?c=m.getPageUrl().replace(/[\?#].*/,"")+a:b?c=m.getBaseUrl()+a.replace(/^(\.\/)+/,""):c=m.getBasePageUrl()+a.replace(/^(\.\/)+/,"")),c.replace(/\#$/,"")},m.getShortUrl=function(a){var b=a,c=m.getBaseUrl(),d=m.getRootUrl();return m.emulated.pushState&&(b=b.replace(c,"")),b=b.replace(d,"/"),m.isTraditionalAnchor(b)&&(b="./"+b),b=b.replace(/^(\.\/)+/g,"./").replace(/\#$/,""),b},m.store={},m.idToState=m.idToState||{},m.stateToId=m.stateToId||{},m.urlToId=m.urlToId||{},m.storedStates=m.storedStates||[],m.savedStates=m.savedStates||[],m.normalizeStore=function(){m.store.idToState=m.store.idToState||{},m.store.urlToId=m.store.urlToId||{},m.store.stateToId=m.store.stateToId||{}},m.getState=function(a,b){typeof a=="undefined"&&(a=!0),typeof b=="undefined"&&(b=!0);var c=m.getLastSavedState();return!c&&b&&(c=m.createStateObject()),a&&(c=m.cloneObject(c),c.url=c.cleanUrl||c.url),c},m.getIdByState=function(a){var b=m.extractId(a.url),c;if(!b){c=m.getStateString(a);if(typeof m.stateToId[c]!="undefined")b=m.stateToId[c];else if(typeof m.store.stateToId[c]!="undefined")b=m.store.stateToId[c];else{for(;;){b=(new Date).getTime()+String(Math.random()).replace(/\D/g,"");if(typeof m.idToState[b]=="undefined"&&typeof m.store.idToState[b]=="undefined")break}m.stateToId[c]=b,m.idToState[b]=a}}return b},m.normalizeState=function(a){var b,c;if(!a||typeof a!="object")a={};if(typeof a.normalized!="undefined")return a;if(!a.data||typeof a.data!="object")a.data={};b={},b.normalized=!0,b.title=a.title||"",b.url=m.getFullUrl(m.unescapeString(a.url||d.location.href)),b.hash=m.getShortUrl(b.url),b.data=m.cloneObject(a.data),b.id=m.getIdByState(b),b.cleanUrl=b.url.replace(/\??\&_suid.*/,""),b.url=b.cleanUrl,c=!m.isEmptyObject(b.data);if(b.title||c)b.hash=m.getShortUrl(b.url).replace(/\??\&_suid.*/,""),/\?/.test(b.hash)||(b.hash+="?"),b.hash+="&_suid="+b.id;return b.hashedUrl=m.getFullUrl(b.hash),(m.emulated.pushState||m.bugs.safariPoll)&&m.hasUrlDuplicate(b)&&(b.url=b.hashedUrl),b},m.createStateObject=function(a,b,c){var d={data:a,title:b,url:c};return d=m.normalizeState(d),d},m.getStateById=function(a){a=String(a);var c=m.idToState[a]||m.store.idToState[a]||b;return c},m.getStateString=function(a){var b,c,d;return b=m.normalizeState(a),c={data:b.data,title:a.title,url:a.url},d=k.stringify(c),d},m.getStateId=function(a){var b,c;return b=m.normalizeState(a),c=b.id,c},m.getHashByState=function(a){var b,c;return b=m.normalizeState(a),c=b.hash,c},m.extractId=function(a){var b,c,d;return c=/(.*)\&_suid=([0-9]+)$/.exec(a),d=c?c[1]||a:a,b=c?String(c[2]||""):"",b||!1},m.isTraditionalAnchor=function(a){var b=!/[\/\?\.]/.test(a);return b},m.extractState=function(a,b){var c=null,d,e;return b=b||!1,d=m.extractId(a),d&&(c=m.getStateById(d)),c||(e=m.getFullUrl(a),d=m.getIdByUrl(e)||!1,d&&(c=m.getStateById(d)),!c&&b&&!m.isTraditionalAnchor(a)&&(c=m.createStateObject(null,null,e))),c},m.getIdByUrl=function(a){var c=m.urlToId[a]||m.store.urlToId[a]||b;return c},m.getLastSavedState=function(){return m.savedStates[m.savedStates.length-1]||b},m.getLastStoredState=function(){return m.storedStates[m.storedStates.length-1]||b},m.hasUrlDuplicate=function(a){var b=!1,c;return c=m.extractState(a.url),b=c&&c.id!==a.id,b},m.storeState=function(a){return m.urlToId[a.url]=a.id,m.storedStates.push(m.cloneObject(a)),a},m.isLastSavedState=function(a){var b=!1,c,d,e;return m.savedStates.length&&(c=a.id,d=m.getLastSavedState(),e=d.id,b=c===e),b},m.saveState=function(a){return m.isLastSavedState(a)?!1:(m.savedStates.push(m.cloneObject(a)),!0)},m.getStateByIndex=function(a){var b=null;return typeof a=="undefined"?b=m.savedStates[m.savedStates.length-1]:a<0?b=m.savedStates[m.savedStates.length+a]:b=m.savedStates[a],b},m.getHash=function(){var a=m.unescapeHash(d.location.hash);return a},m.unescapeString=function(b){var c=b,d;for(;;){d=a.unescape(c);if(d===c)break;c=d}return c},m.unescapeHash=function(a){var b=m.normalizeHash(a);return b=m.unescapeString(b),b},m.normalizeHash=function(a){var b=a.replace(/[^#]*#/,"").replace(/#.*/,"");return b},m.setHash=function(a,b){var c,e,f;return b!==!1&&m.busy()?(m.pushQueue({scope:m,callback:m.setHash,args:arguments,queue:b}),!1):(c=m.escapeHash(a),m.busy(!0),e=m.extractState(a,!0),e&&!m.emulated.pushState?m.pushState(e.data,e.title,e.url,!1):d.location.hash!==c&&(m.bugs.setHash?(f=m.getPageUrl(),m.pushState(null,null,f+"#"+c,!1)):d.location.hash=c),m)},m.escapeHash=function(b){var c=m.normalizeHash(b);return c=a.escape(c),m.bugs.hashEscape||(c=c.replace(/\%21/g,"!").replace(/\%26/g,"&").replace(/\%3D/g,"=").replace(/\%3F/g,"?")),c},m.getHashByUrl=function(a){var b=String(a).replace(/([^#]*)#?([^#]*)#?(.*)/,"$2");return b=m.unescapeHash(b),b},m.setTitle=function(a){var b=a.title,c;b||(c=m.getStateByIndex(0),c&&c.url===a.url&&(b=c.title||m.options.initialTitle));try{d.getElementsByTagName("title")[0].innerHTML=b.replace("<","&lt;").replace(">","&gt;").replace(" & "," &amp; ")}catch(e){}return d.title=b,m},m.queues=[],m.busy=function(a){typeof a!="undefined"?m.busy.flag=a:typeof m.busy.flag=="undefined"&&(m.busy.flag=!1);if(!m.busy.flag){h(m.busy.timeout);var b=function(){var a,c,d;if(m.busy.flag)return;for(a=m.queues.length-1;a>=0;--a){c=m.queues[a];if(c.length===0)continue;d=c.shift(),m.fireQueueItem(d),m.busy.timeout=g(b,m.options.busyDelay)}};m.busy.timeout=g(b,m.options.busyDelay)}return m.busy.flag},m.busy.flag=!1,m.fireQueueItem=function(a){return a.callback.apply(a.scope||m,a.args||[])},m.pushQueue=function(a){return m.queues[a.queue||0]=m.queues[a.queue||0]||[],m.queues[a.queue||0].push(a),m},m.queue=function(a,b){return typeof a=="function"&&(a={callback:a}),typeof b!="undefined"&&(a.queue=b),m.busy()?m.pushQueue(a):m.fireQueueItem(a),m},m.clearQueue=function(){return m.busy.flag=!1,m.queues=[],m},m.stateChanged=!1,m.doubleChecker=!1,m.doubleCheckComplete=function(){return m.stateChanged=!0,m.doubleCheckClear(),m},m.doubleCheckClear=function(){return m.doubleChecker&&(h(m.doubleChecker),m.doubleChecker=!1),m},m.doubleCheck=function(a){return m.stateChanged=!1,m.doubleCheckClear(),m.bugs.ieDoubleCheck&&(m.doubleChecker=g(function(){return m.doubleCheckClear(),m.stateChanged||a(),!0},m.options.doubleCheckInterval)),m},m.safariStatePoll=function(){var b=m.extractState(d.location.href),c;if(!m.isLastSavedState(b))c=b;else return;return c||(c=m.createStateObject()),m.Adapter.trigger(a,"popstate"),m},m.back=function(a){return a!==!1&&m.busy()?(m.pushQueue({scope:m,callback:m.back,args:arguments,queue:a}),!1):(m.busy(!0),m.doubleCheck(function(){m.back(!1)}),n.go(-1),!0)},m.forward=function(a){return a!==!1&&m.busy()?(m.pushQueue({scope:m,callback:m.forward,args:arguments,queue:a}),!1):(m.busy(!0),m.doubleCheck(function(){m.forward(!1)}),n.go(1),!0)},m.go=function(a,b){var c;if(a>0)for(c=1;c<=a;++c)m.forward(b);else{if(!(a<0))throw new Error("History.go: History.go requires a positive or negative integer passed.");for(c=-1;c>=a;--c)m.back(b)}return m};if(m.emulated.pushState){var o=function(){};m.pushState=m.pushState||o,m.replaceState=m.replaceState||o}else m.onPopState=function(b,c){var e=!1,f=!1,g,h;return m.doubleCheckComplete(),g=m.getHash(),g?(h=m.extractState(g||d.location.href,!0),h?m.replaceState(h.data,h.title,h.url,!1):(m.Adapter.trigger(a,"anchorchange"),m.busy(!1)),m.expectedStateId=!1,!1):(e=m.Adapter.extractEventData("state",b,c)||!1,e?f=m.getStateById(e):m.expectedStateId?f=m.getStateById(m.expectedStateId):f=m.extractState(d.location.href),f||(f=m.createStateObject(null,null,d.location.href)),m.expectedStateId=!1,m.isLastSavedState(f)?(m.busy(!1),!1):(m.storeState(f),m.saveState(f),m.setTitle(f),m.Adapter.trigger(a,"statechange"),m.busy(!1),!0))},m.Adapter.bind(a,"popstate",m.onPopState),m.pushState=function(b,c,d,e){if(m.getHashByUrl(d)&&m.emulated.pushState)throw new Error("History.js does not support states with fragement-identifiers (hashes/anchors).");if(e!==!1&&m.busy())return m.pushQueue({scope:m,callback:m.pushState,args:arguments,queue:e}),!1;m.busy(!0);var f=m.createStateObject(b,c,d);return m.isLastSavedState(f)?m.busy(!1):(m.storeState(f),m.expectedStateId=f.id,n.pushState(f.id,f.title,f.url),m.Adapter.trigger(a,"popstate")),!0},m.replaceState=function(b,c,d,e){if(m.getHashByUrl(d)&&m.emulated.pushState)throw new Error("History.js does not support states with fragement-identifiers (hashes/anchors).");if(e!==!1&&m.busy())return m.pushQueue({scope:m,callback:m.replaceState,args:arguments,queue:e}),!1;m.busy(!0);var f=m.createStateObject(b,c,d);return m.isLastSavedState(f)?m.busy(!1):(m.storeState(f),m.expectedStateId=f.id,n.replaceState(f.id,f.title,f.url),m.Adapter.trigger(a,"popstate")),!0};if(f){try{m.store=k.parse(f.getItem("History.store"))||{}}catch(p){m.store={}}m.normalizeStore()}else m.store={},m.normalizeStore();m.Adapter.bind(a,"beforeunload",m.clearAllIntervals),m.Adapter.bind(a,"unload",m.clearAllIntervals),m.saveState(m.storeState(m.extractState(d.location.href,!0))),f&&(m.onUnload=function(){var a,b;try{a=k.parse(f.getItem("History.store"))||{}}catch(c){a={}}a.idToState=a.idToState||{},a.urlToId=a.urlToId||{},a.stateToId=a.stateToId||{};for(b in m.idToState){if(!m.idToState.hasOwnProperty(b))continue;a.idToState[b]=m.idToState[b]}for(b in m.urlToId){if(!m.urlToId.hasOwnProperty(b))continue;a.urlToId[b]=m.urlToId[b]}for(b in m.stateToId){if(!m.stateToId.hasOwnProperty(b))continue;a.stateToId[b]=m.stateToId[b]}m.store=a,m.normalizeStore(),f.setItem("History.store",k.stringify(a))},m.intervalList.push(i(m.onUnload,m.options.storeInterval)),m.Adapter.bind(a,"beforeunload",m.onUnload),m.Adapter.bind(a,"unload",m.onUnload));if(!m.emulated.pushState){m.bugs.safariPoll&&m.intervalList.push(i(m.safariStatePoll,m.options.safariPollInterval));if(e.vendor==="Apple Computer, Inc."||(e.appCodeName||"")==="Mozilla")m.Adapter.bind(a,"hashchange",function(){m.Adapter.trigger(a,"popstate")}),m.getHash()&&m.Adapter.onDomLoad(function(){m.Adapter.trigger(a,"hashchange")})}},m.init()}(window)
1
+ /**
2
+ * History.js Core
3
+ * @author Benjamin Arthur Lupton <contact@balupton.com>
4
+ * @copyright 2010-2011 Benjamin Arthur Lupton <contact@balupton.com>
5
+ * @license New BSD License <http://creativecommons.org/licenses/BSD/>
6
+ */
7
+
8
+ (function(window,undefined){
9
+ "use strict";
10
+
11
+ // ========================================================================
12
+ // Initialise
13
+
14
+ // Localise Globals
15
+ var
16
+ console = window.console||undefined, // Prevent a JSLint complain
17
+ document = window.document, // Make sure we are using the correct document
18
+ navigator = window.navigator, // Make sure we are using the correct navigator
19
+ sessionStorage = window.sessionStorage||false, // sessionStorage
20
+ setTimeout = window.setTimeout,
21
+ clearTimeout = window.clearTimeout,
22
+ setInterval = window.setInterval,
23
+ clearInterval = window.clearInterval,
24
+ JSON = window.JSON,
25
+ alert = window.alert,
26
+ History = window.History = window.History||{}, // Public History Object
27
+ history = window.history; // Old History Object
28
+
29
+ // MooTools Compatibility
30
+ JSON.stringify = JSON.stringify||JSON.encode;
31
+ JSON.parse = JSON.parse||JSON.decode;
32
+
33
+ // Check Existence
34
+ if ( typeof History.init !== 'undefined' ) {
35
+ throw new Error('History.js Core has already been loaded...');
36
+ }
37
+
38
+ // Initialise History
39
+ History.init = function(){
40
+ // Check Load Status of Adapter
41
+ if ( typeof History.Adapter === 'undefined' ) {
42
+ return false;
43
+ }
44
+
45
+ // Check Load Status of Core
46
+ if ( typeof History.initCore !== 'undefined' ) {
47
+ History.initCore();
48
+ }
49
+
50
+ // Check Load Status of HTML4 Support
51
+ if ( typeof History.initHtml4 !== 'undefined' ) {
52
+ History.initHtml4();
53
+ }
54
+
55
+ // Return true
56
+ return true;
57
+ };
58
+
59
+
60
+ // ========================================================================
61
+ // Initialise Core
62
+
63
+ // Initialise Core
64
+ History.initCore = function(){
65
+ // Initialise
66
+ if ( typeof History.initCore.initialized !== 'undefined' ) {
67
+ // Already Loaded
68
+ return false;
69
+ }
70
+ else {
71
+ History.initCore.initialized = true;
72
+ }
73
+
74
+
75
+ // ====================================================================
76
+ // Options
77
+
78
+ /**
79
+ * History.options
80
+ * Configurable options
81
+ */
82
+ History.options = History.options||{};
83
+
84
+ /**
85
+ * History.options.hashChangeInterval
86
+ * How long should the interval be before hashchange checks
87
+ */
88
+ History.options.hashChangeInterval = History.options.hashChangeInterval || 100;
89
+
90
+ /**
91
+ * History.options.safariPollInterval
92
+ * How long should the interval be before safari poll checks
93
+ */
94
+ History.options.safariPollInterval = History.options.safariPollInterval || 500;
95
+
96
+ /**
97
+ * History.options.doubleCheckInterval
98
+ * How long should the interval be before we perform a double check
99
+ */
100
+ History.options.doubleCheckInterval = History.options.doubleCheckInterval || 500;
101
+
102
+ /**
103
+ * History.options.storeInterval
104
+ * How long should we wait between store calls
105
+ */
106
+ History.options.storeInterval = History.options.storeInterval || 1000;
107
+
108
+ /**
109
+ * History.options.busyDelay
110
+ * How long should we wait between busy events
111
+ */
112
+ History.options.busyDelay = History.options.busyDelay || 250;
113
+
114
+ /**
115
+ * History.options.debug
116
+ * If true will enable debug messages to be logged
117
+ */
118
+ History.options.debug = History.options.debug || false;
119
+
120
+ /**
121
+ * History.options.initialTitle
122
+ * What is the title of the initial state
123
+ */
124
+ History.options.initialTitle = History.options.initialTitle || document.title;
125
+
126
+
127
+ // ====================================================================
128
+ // Interval record
129
+
130
+ /**
131
+ * History.intervalList
132
+ * List of intervals set, to be cleared when document is unloaded.
133
+ */
134
+ History.intervalList = [];
135
+
136
+ /**
137
+ * History.clearAllIntervals
138
+ * Clears all setInterval instances.
139
+ */
140
+ History.clearAllIntervals = function(){
141
+ var i, il = History.intervalList;
142
+ if (typeof il !== "undefined" && il !== null) {
143
+ for (i = 0; i < il.length; i++) {
144
+ clearInterval(il[i]);
145
+ }
146
+ History.intervalList = null;
147
+ }
148
+ };
149
+
150
+
151
+ // ====================================================================
152
+ // Debug
153
+
154
+ /**
155
+ * History.debug(message,...)
156
+ * Logs the passed arguments if debug enabled
157
+ */
158
+ History.debug = function(){
159
+ if ( (History.options.debug||false) ) {
160
+ History.log.apply(History,arguments);
161
+ }
162
+ };
163
+
164
+ /**
165
+ * History.log(message,...)
166
+ * Logs the passed arguments
167
+ */
168
+ History.log = function(){
169
+ // Prepare
170
+ var
171
+ consoleExists = !(typeof console === 'undefined' || typeof console.log === 'undefined' || typeof console.log.apply === 'undefined'),
172
+ textarea = document.getElementById('log'),
173
+ message,
174
+ i,n,
175
+ args,arg
176
+ ;
177
+
178
+ // Write to Console
179
+ if ( consoleExists ) {
180
+ args = Array.prototype.slice.call(arguments);
181
+ message = args.shift();
182
+ if ( typeof console.debug !== 'undefined' ) {
183
+ console.debug.apply(console,[message,args]);
184
+ }
185
+ else {
186
+ console.log.apply(console,[message,args]);
187
+ }
188
+ }
189
+ else {
190
+ message = ("\n"+arguments[0]+"\n");
191
+ }
192
+
193
+ // Write to log
194
+ for ( i=1,n=arguments.length; i<n; ++i ) {
195
+ arg = arguments[i];
196
+ if ( typeof arg === 'object' && typeof JSON !== 'undefined' ) {
197
+ try {
198
+ arg = JSON.stringify(arg);
199
+ }
200
+ catch ( Exception ) {
201
+ // Recursive Object
202
+ }
203
+ }
204
+ message += "\n"+arg+"\n";
205
+ }
206
+
207
+ // Textarea
208
+ if ( textarea ) {
209
+ textarea.value += message+"\n-----\n";
210
+ textarea.scrollTop = textarea.scrollHeight - textarea.clientHeight;
211
+ }
212
+ // No Textarea, No Console
213
+ else if ( !consoleExists ) {
214
+ alert(message);
215
+ }
216
+
217
+ // Return true
218
+ return true;
219
+ };
220
+
221
+
222
+ // ====================================================================
223
+ // Emulated Status
224
+
225
+ /**
226
+ * History.getInternetExplorerMajorVersion()
227
+ * Get's the major version of Internet Explorer
228
+ * @return {integer}
229
+ * @license Public Domain
230
+ * @author Benjamin Arthur Lupton <contact@balupton.com>
231
+ * @author James Padolsey <https://gist.github.com/527683>
232
+ */
233
+ History.getInternetExplorerMajorVersion = function(){
234
+ var result = History.getInternetExplorerMajorVersion.cached =
235
+ (typeof History.getInternetExplorerMajorVersion.cached !== 'undefined')
236
+ ? History.getInternetExplorerMajorVersion.cached
237
+ : (function(){
238
+ var v = 3,
239
+ div = document.createElement('div'),
240
+ all = div.getElementsByTagName('i');
241
+ while ( (div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->') && all[0] ) {}
242
+ return (v > 4) ? v : false;
243
+ })()
244
+ ;
245
+ return result;
246
+ };
247
+
248
+ /**
249
+ * History.isInternetExplorer()
250
+ * Are we using Internet Explorer?
251
+ * @return {boolean}
252
+ * @license Public Domain
253
+ * @author Benjamin Arthur Lupton <contact@balupton.com>
254
+ */
255
+ History.isInternetExplorer = function(){
256
+ var result =
257
+ History.isInternetExplorer.cached =
258
+ (typeof History.isInternetExplorer.cached !== 'undefined')
259
+ ? History.isInternetExplorer.cached
260
+ : Boolean(History.getInternetExplorerMajorVersion())
261
+ ;
262
+ return result;
263
+ };
264
+
265
+ /**
266
+ * History.emulated
267
+ * Which features require emulating?
268
+ */
269
+ History.emulated = {
270
+ pushState: !Boolean(
271
+ window.history && window.history.pushState && window.history.replaceState
272
+ && !(
273
+ (/ Mobile\/([1-7][a-z]|(8([abcde]|f(1[0-8]))))/i).test(navigator.userAgent) /* disable for versions of iOS before version 4.3 (8F190) */
274
+ || (/AppleWebKit\/5([0-2]|3[0-2])/i).test(navigator.userAgent) /* disable for the mercury iOS browser, or at least older versions of the webkit engine */
275
+ )
276
+ ),
277
+ hashChange: Boolean(
278
+ !(('onhashchange' in window) || ('onhashchange' in document))
279
+ ||
280
+ (History.isInternetExplorer() && History.getInternetExplorerMajorVersion() < 8)
281
+ )
282
+ };
283
+
284
+ /**
285
+ * History.enabled
286
+ * Is History enabled?
287
+ */
288
+ History.enabled = !History.emulated.pushState;
289
+
290
+ /**
291
+ * History.bugs
292
+ * Which bugs are present
293
+ */
294
+ History.bugs = {
295
+ /**
296
+ * Safari 5 and Safari iOS 4 fail to return to the correct state once a hash is replaced by a `replaceState` call
297
+ * https://bugs.webkit.org/show_bug.cgi?id=56249
298
+ */
299
+ setHash: Boolean(!History.emulated.pushState && navigator.vendor === 'Apple Computer, Inc.' && /AppleWebKit\/5([0-2]|3[0-3])/.test(navigator.userAgent)),
300
+
301
+ /**
302
+ * Safari 5 and Safari iOS 4 sometimes fail to apply the state change under busy conditions
303
+ * https://bugs.webkit.org/show_bug.cgi?id=42940
304
+ */
305
+ safariPoll: Boolean(!History.emulated.pushState && navigator.vendor === 'Apple Computer, Inc.' && /AppleWebKit\/5([0-2]|3[0-3])/.test(navigator.userAgent)),
306
+
307
+ /**
308
+ * MSIE 6 and 7 sometimes do not apply a hash even it was told to (requiring a second call to the apply function)
309
+ */
310
+ ieDoubleCheck: Boolean(History.isInternetExplorer() && History.getInternetExplorerMajorVersion() < 8),
311
+
312
+ /**
313
+ * MSIE 6 requires the entire hash to be encoded for the hashes to trigger the onHashChange event
314
+ */
315
+ hashEscape: Boolean(History.isInternetExplorer() && History.getInternetExplorerMajorVersion() < 7)
316
+ };
317
+
318
+ /**
319
+ * History.isEmptyObject(obj)
320
+ * Checks to see if the Object is Empty
321
+ * @param {Object} obj
322
+ * @return {boolean}
323
+ */
324
+ History.isEmptyObject = function(obj) {
325
+ for ( var name in obj ) {
326
+ return false;
327
+ }
328
+ return true;
329
+ };
330
+
331
+ /**
332
+ * History.cloneObject(obj)
333
+ * Clones a object and eliminate all references to the original contexts
334
+ * @param {Object} obj
335
+ * @return {Object}
336
+ */
337
+ History.cloneObject = function(obj) {
338
+ var hash,newObj;
339
+ if ( obj ) {
340
+ hash = JSON.stringify(obj);
341
+ newObj = JSON.parse(hash);
342
+ }
343
+ else {
344
+ newObj = {};
345
+ }
346
+ return newObj;
347
+ };
348
+
349
+
350
+ // ====================================================================
351
+ // URL Helpers
352
+
353
+ /**
354
+ * History.getRootUrl()
355
+ * Turns "http://mysite.com/dir/page.html?asd" into "http://mysite.com"
356
+ * @return {String} rootUrl
357
+ */
358
+ History.getRootUrl = function(){
359
+ // Create
360
+ var rootUrl = document.location.protocol+'//'+(document.location.hostname||document.location.host);
361
+ if ( document.location.port||false ) {
362
+ rootUrl += ':'+document.location.port;
363
+ }
364
+ rootUrl += '/';
365
+
366
+ // Return
367
+ return rootUrl;
368
+ };
369
+
370
+ /**
371
+ * History.getBaseHref()
372
+ * Fetches the `href` attribute of the `<base href="...">` element if it exists
373
+ * @return {String} baseHref
374
+ */
375
+ History.getBaseHref = function(){
376
+ // Create
377
+ var
378
+ baseElements = document.getElementsByTagName('base'),
379
+ baseElement = null,
380
+ baseHref = '';
381
+
382
+ // Test for Base Element
383
+ if ( baseElements.length === 1 ) {
384
+ // Prepare for Base Element
385
+ baseElement = baseElements[0];
386
+ baseHref = baseElement.href.replace(/[^\/]+$/,'');
387
+ }
388
+
389
+ // Adjust trailing slash
390
+ baseHref = baseHref.replace(/\/+$/,'');
391
+ if ( baseHref ) baseHref += '/';
392
+
393
+ // Return
394
+ return baseHref;
395
+ };
396
+
397
+ /**
398
+ * History.getBaseUrl()
399
+ * Fetches the baseHref or basePageUrl or rootUrl (whichever one exists first)
400
+ * @return {String} baseUrl
401
+ */
402
+ History.getBaseUrl = function(){
403
+ // Create
404
+ var baseUrl = History.getBaseHref()||History.getBasePageUrl()||History.getRootUrl();
405
+
406
+ // Return
407
+ return baseUrl;
408
+ };
409
+
410
+ /**
411
+ * History.getPageUrl()
412
+ * Fetches the URL of the current page
413
+ * @return {String} pageUrl
414
+ */
415
+ History.getPageUrl = function(){
416
+ // Fetch
417
+ var
418
+ State = History.getState(false,false),
419
+ stateUrl = (State||{}).url||document.location.href,
420
+ pageUrl;
421
+
422
+ // Create
423
+ pageUrl = stateUrl.replace(/\/+$/,'').replace(/[^\/]+$/,function(part,index,string){
424
+ return (/\!/).test(part) ? part : part+'/';
425
+ });
426
+
427
+ // Return
428
+ return pageUrl;
429
+ };
430
+
431
+ /**
432
+ * History.getBasePageUrl()
433
+ * Fetches the Url of the directory of the current page
434
+ * @return {String} basePageUrl
435
+ */
436
+ History.getBasePageUrl = function(){
437
+ // Create
438
+ var basePageUrl = document.location.href.replace(/[#\?].*/,'').replace(/[^\/]+$/,function(part,index,string){
439
+ return (/[^\/]$/).test(part) ? '' : part;
440
+ }).replace(/\/+$/,'')+'/';
441
+
442
+ // Return
443
+ return basePageUrl;
444
+ };
445
+
446
+ /**
447
+ * History.getFullUrl(url)
448
+ * Ensures that we have an absolute URL and not a relative URL
449
+ * @param {string} url
450
+ * @param {Boolean} allowBaseHref
451
+ * @return {string} fullUrl
452
+ */
453
+ History.getFullUrl = function(url,allowBaseHref){
454
+ // Prepare
455
+ var fullUrl = url, firstChar = url.substring(0,1);
456
+ allowBaseHref = (typeof allowBaseHref === 'undefined') ? true : allowBaseHref;
457
+
458
+ // Check
459
+ if ( /[a-z]+\:\/\//.test(url) ) {
460
+ // Full URL
461
+ }
462
+ else if ( firstChar === '/' ) {
463
+ // Root URL
464
+ fullUrl = History.getRootUrl()+url.replace(/^\/+/,'');
465
+ }
466
+ else if ( firstChar === '#' ) {
467
+ // Anchor URL
468
+ fullUrl = History.getPageUrl().replace(/#!*/,'')+url;
469
+ }
470
+ else if ( firstChar === '?' ) {
471
+ // Query URL
472
+ fullUrl = History.getPageUrl().replace(/[\?#]!*/,'')+url;
473
+ }
474
+ else {
475
+ // Relative URL
476
+ if ( allowBaseHref ) {
477
+ fullUrl = History.getBaseUrl()+url.replace(/^(\!\/)+/,'');
478
+ } else {
479
+ fullUrl = History.getBasePageUrl()+url.replace(/^(\!\/)+/,'');
480
+ }
481
+ // We have an if condition above as we do not want hashes
482
+ // which are relative to the baseHref in our URLs
483
+ // as if the baseHref changes, then all our bookmarks
484
+ // would now point to different locations
485
+ // whereas the basePageUrl will always stay the same
486
+ }
487
+
488
+ // Return
489
+ return fullUrl.replace(/\#$/,'');
490
+ };
491
+
492
+ /**
493
+ * History.getShortUrl(url)
494
+ * Ensures that we have a relative URL and not a absolute URL
495
+ * @param {string} url
496
+ * @return {string} url
497
+ */
498
+ History.getShortUrl = function(url){
499
+ // Prepare
500
+ var shortUrl = url, baseUrl = History.getBaseUrl(), rootUrl = History.getRootUrl();
501
+
502
+ // Trim baseUrl
503
+ if ( History.emulated.pushState ) {
504
+ // We are in a if statement as when pushState is not emulated
505
+ // The actual url these short urls are relative to can change
506
+ // So within the same session, we the url may end up somewhere different
507
+ shortUrl = shortUrl.replace(baseUrl,'');
508
+ }
509
+
510
+ // Trim rootUrl
511
+ shortUrl = shortUrl.replace(rootUrl,'/');
512
+
513
+ // Ensure we can still detect it as a state
514
+ // if ( History.isTraditionalAnchor(shortUrl) ) {
515
+ // shortUrl = './'+shortUrl;
516
+ // // shortUrl = '/'+shortUrl;
517
+ // }
518
+
519
+ shortUrl = '!/'+shortUrl;
520
+
521
+ // Clean It
522
+ shortUrl = shortUrl.replace(/^(\!\/)+/g,'!/').replace(/\#$/,'');
523
+
524
+ // Return
525
+ return shortUrl;
526
+ };
527
+
528
+
529
+ // ====================================================================
530
+ // State Storage
531
+
532
+ /**
533
+ * History.store
534
+ * The store for all session specific data
535
+ */
536
+ History.store = {};
537
+
538
+ /**
539
+ * History.idToState
540
+ * 1-1: State ID to State Object
541
+ */
542
+ History.idToState = History.idToState||{};
543
+
544
+ /**
545
+ * History.stateToId
546
+ * 1-1: State String to State ID
547
+ */
548
+ History.stateToId = History.stateToId||{};
549
+
550
+ /**
551
+ * History.urlToId
552
+ * 1-1: State URL to State ID
553
+ */
554
+ History.urlToId = History.urlToId||{};
555
+
556
+ /**
557
+ * History.storedStates
558
+ * Store the states in an array
559
+ */
560
+ History.storedStates = History.storedStates||[];
561
+
562
+ /**
563
+ * History.savedStates
564
+ * Saved the states in an array
565
+ */
566
+ History.savedStates = History.savedStates||[];
567
+
568
+ /**
569
+ * History.noramlizeStore()
570
+ * Noramlize the store by adding necessary values
571
+ */
572
+ History.normalizeStore = function(){
573
+ History.store.idToState = History.store.idToState||{};
574
+ History.store.urlToId = History.store.urlToId||{};
575
+ History.store.stateToId = History.store.stateToId||{};
576
+ };
577
+
578
+ /**
579
+ * History.getState()
580
+ * Get an object containing the data, title and url of the current state
581
+ * @param {Boolean} friendly
582
+ * @param {Boolean} create
583
+ * @return {Object} State
584
+ */
585
+ History.getState = function(friendly,create){
586
+ // Prepare
587
+ if ( typeof friendly === 'undefined' ) { friendly = true; }
588
+ if ( typeof create === 'undefined' ) { create = true; }
589
+
590
+ // Fetch
591
+ var State = History.getLastSavedState();
592
+
593
+ // Create
594
+ if ( !State && create ) {
595
+ State = History.createStateObject();
596
+ }
597
+
598
+ // Adjust
599
+ if ( friendly ) {
600
+ State = History.cloneObject(State);
601
+ State.url = State.cleanUrl||State.url;
602
+ }
603
+
604
+ // Return
605
+ return State;
606
+ };
607
+
608
+ /**
609
+ * History.getIdByState(State)
610
+ * Gets a ID for a State
611
+ * @param {State} newState
612
+ * @return {String} id
613
+ */
614
+ History.getIdByState = function(newState){
615
+
616
+ // Fetch ID
617
+ var id = History.extractId(newState.url),
618
+ str;
619
+
620
+ if ( !id ) {
621
+ // Find ID via State String
622
+ str = History.getStateString(newState);
623
+ if ( typeof History.stateToId[str] !== 'undefined' ) {
624
+ id = History.stateToId[str];
625
+ }
626
+ else if ( typeof History.store.stateToId[str] !== 'undefined' ) {
627
+ id = History.store.stateToId[str];
628
+ }
629
+ else {
630
+ // Generate a new ID
631
+ while ( true ) {
632
+ id = (new Date()).getTime() + String(Math.random()).replace(/\D/g,'');
633
+ if ( typeof History.idToState[id] === 'undefined' && typeof History.store.idToState[id] === 'undefined' ) {
634
+ break;
635
+ }
636
+ }
637
+
638
+ // Apply the new State to the ID
639
+ History.stateToId[str] = id;
640
+ History.idToState[id] = newState;
641
+ }
642
+ }
643
+
644
+ // Return ID
645
+ return id;
646
+ };
647
+
648
+ /**
649
+ * History.normalizeState(State)
650
+ * Expands a State Object
651
+ * @param {object} State
652
+ * @return {object}
653
+ */
654
+ History.normalizeState = function(oldState){
655
+ // Variables
656
+ var newState, dataNotEmpty;
657
+
658
+ // Prepare
659
+ if ( !oldState || (typeof oldState !== 'object') ) {
660
+ oldState = {};
661
+ }
662
+
663
+ // Check
664
+ if ( typeof oldState.normalized !== 'undefined' ) {
665
+ return oldState;
666
+ }
667
+
668
+ // Adjust
669
+ if ( !oldState.data || (typeof oldState.data !== 'object') ) {
670
+ oldState.data = {};
671
+ }
672
+
673
+ // ----------------------------------------------------------------
674
+
675
+ // Create
676
+ newState = {};
677
+ newState.normalized = true;
678
+ newState.title = oldState.title||'';
679
+ newState.url = History.getFullUrl(History.unescapeString(oldState.url||document.location.href));
680
+ newState.hash = History.getShortUrl(newState.url);
681
+ newState.data = History.cloneObject(oldState.data);
682
+
683
+ // Fetch ID
684
+ newState.id = History.getIdByState(newState);
685
+
686
+ // ----------------------------------------------------------------
687
+
688
+ // Clean the URL
689
+ newState.cleanUrl = newState.url.replace(/\??\&_suid.*/,'');
690
+ newState.url = newState.cleanUrl;
691
+
692
+ // Check to see if we have more than just a url
693
+ dataNotEmpty = !History.isEmptyObject(newState.data);
694
+
695
+ // Apply
696
+ if ( newState.title || dataNotEmpty ) {
697
+ // Add ID to Hash
698
+ newState.hash = History.getShortUrl(newState.url).replace(/\??\&_suid.*/,'');
699
+ if ( !/\?/.test(newState.hash) ) {
700
+ newState.hash += '?';
701
+ }
702
+ newState.hash += '&_suid='+newState.id;
703
+ }
704
+
705
+ // Create the Hashed URL
706
+ newState.hashedUrl = History.getFullUrl(newState.hash);
707
+
708
+ // ----------------------------------------------------------------
709
+
710
+ // Update the URL if we have a duplicate
711
+ if ( (History.emulated.pushState || History.bugs.safariPoll) && History.hasUrlDuplicate(newState) ) {
712
+ newState.url = newState.hashedUrl;
713
+ }
714
+
715
+ // ----------------------------------------------------------------
716
+
717
+ // Return
718
+ return newState;
719
+ };
720
+
721
+ /**
722
+ * History.createStateObject(data,title,url)
723
+ * Creates a object based on the data, title and url state params
724
+ * @param {object} data
725
+ * @param {string} title
726
+ * @param {string} url
727
+ * @return {object}
728
+ */
729
+ History.createStateObject = function(data,title,url){
730
+ // Hashify
731
+ var State = {
732
+ 'data': data,
733
+ 'title': title,
734
+ 'url': url
735
+ };
736
+
737
+ // Expand the State
738
+ State = History.normalizeState(State);
739
+
740
+ // Return object
741
+ return State;
742
+ };
743
+
744
+ /**
745
+ * History.getStateById(id)
746
+ * Get a state by it's UID
747
+ * @param {String} id
748
+ */
749
+ History.getStateById = function(id){
750
+ // Prepare
751
+ id = String(id);
752
+
753
+ // Retrieve
754
+ var State = History.idToState[id] || History.store.idToState[id] || undefined;
755
+
756
+ // Return State
757
+ return State;
758
+ };
759
+
760
+ /**
761
+ * Get a State's String
762
+ * @param {State} passedState
763
+ */
764
+ History.getStateString = function(passedState){
765
+ // Prepare
766
+ var State, cleanedState, str;
767
+
768
+ // Fetch
769
+ State = History.normalizeState(passedState);
770
+
771
+ // Clean
772
+ cleanedState = {
773
+ data: State.data,
774
+ title: passedState.title,
775
+ url: passedState.url
776
+ };
777
+
778
+ // Fetch
779
+ str = JSON.stringify(cleanedState);
780
+
781
+ // Return
782
+ return str;
783
+ };
784
+
785
+ /**
786
+ * Get a State's ID
787
+ * @param {State} passedState
788
+ * @return {String} id
789
+ */
790
+ History.getStateId = function(passedState){
791
+ // Prepare
792
+ var State, id;
793
+
794
+ // Fetch
795
+ State = History.normalizeState(passedState);
796
+
797
+ // Fetch
798
+ id = State.id;
799
+
800
+ // Return
801
+ return id;
802
+ };
803
+
804
+ /**
805
+ * History.getHashByState(State)
806
+ * Creates a Hash for the State Object
807
+ * @param {State} passedState
808
+ * @return {String} hash
809
+ */
810
+ History.getHashByState = function(passedState){
811
+ // Prepare
812
+ var State, hash;
813
+
814
+ // Fetch
815
+ State = History.normalizeState(passedState);
816
+
817
+ // Hash
818
+ hash = State.hash;
819
+
820
+ // Return
821
+ return hash;
822
+ };
823
+
824
+ /**
825
+ * History.extractId(url_or_hash)
826
+ * Get a State ID by it's URL or Hash
827
+ * @param {string} url_or_hash
828
+ * @return {string} id
829
+ */
830
+ History.extractId = function ( url_or_hash ) {
831
+ // Prepare
832
+ var id,parts,url;
833
+
834
+ // Extract
835
+ parts = /(.*)\&_suid=([0-9]+)$/.exec(url_or_hash);
836
+ url = parts ? (parts[1]||url_or_hash) : url_or_hash;
837
+ id = parts ? String(parts[2]||'') : '';
838
+
839
+ // Return
840
+ return id||false;
841
+ };
842
+
843
+ /**
844
+ * History.isTraditionalAnchor
845
+ * Checks to see if the url is a traditional anchor or not
846
+ * @param {String} url_or_hash
847
+ * @return {Boolean}
848
+ */
849
+ History.isTraditionalAnchor = function(url_or_hash){
850
+ // Check
851
+ var isTraditional = !(/[\/\?\.]/.test(url_or_hash));
852
+
853
+ // Return
854
+ return isTraditional;
855
+ };
856
+
857
+ /**
858
+ * History.extractState
859
+ * Get a State by it's URL or Hash
860
+ * @param {String} url_or_hash
861
+ * @return {State|null}
862
+ */
863
+ History.extractState = function(url_or_hash,create){
864
+ // Prepare
865
+ var State = null, id, url;
866
+ create = create||false;
867
+
868
+ // Fetch SUID
869
+ id = History.extractId(url_or_hash);
870
+ if ( id ) {
871
+ State = History.getStateById(id);
872
+ }
873
+
874
+ // Fetch SUID returned no State
875
+ if ( !State ) {
876
+ // Fetch URL
877
+ url = History.getFullUrl(url_or_hash);
878
+
879
+ // Check URL
880
+ id = History.getIdByUrl(url)||false;
881
+ if ( id ) {
882
+ State = History.getStateById(id);
883
+ }
884
+
885
+ // Create State
886
+ if ( !State && create && !History.isTraditionalAnchor(url_or_hash) ) {
887
+ State = History.createStateObject(null,null,url);
888
+ }
889
+ }
890
+
891
+ // Return
892
+ return State;
893
+ };
894
+
895
+ /**
896
+ * History.getIdByUrl()
897
+ * Get a State ID by a State URL
898
+ */
899
+ History.getIdByUrl = function(url){
900
+ // Fetch
901
+ var id = History.urlToId[url] || History.store.urlToId[url] || undefined;
902
+
903
+ // Return
904
+ return id;
905
+ };
906
+
907
+ /**
908
+ * History.getLastSavedState()
909
+ * Get an object containing the data, title and url of the current state
910
+ * @return {Object} State
911
+ */
912
+ History.getLastSavedState = function(){
913
+ return History.savedStates[History.savedStates.length-1]||undefined;
914
+ };
915
+
916
+ /**
917
+ * History.getLastStoredState()
918
+ * Get an object containing the data, title and url of the current state
919
+ * @return {Object} State
920
+ */
921
+ History.getLastStoredState = function(){
922
+ return History.storedStates[History.storedStates.length-1]||undefined;
923
+ };
924
+
925
+ /**
926
+ * History.hasUrlDuplicate
927
+ * Checks if a Url will have a url conflict
928
+ * @param {Object} newState
929
+ * @return {Boolean} hasDuplicate
930
+ */
931
+ History.hasUrlDuplicate = function(newState) {
932
+ // Prepare
933
+ var hasDuplicate = false,
934
+ oldState;
935
+
936
+ // Fetch
937
+ oldState = History.extractState(newState.url);
938
+
939
+ // Check
940
+ hasDuplicate = oldState && oldState.id !== newState.id;
941
+
942
+ // Return
943
+ return hasDuplicate;
944
+ };
945
+
946
+ /**
947
+ * History.storeState
948
+ * Store a State
949
+ * @param {Object} newState
950
+ * @return {Object} newState
951
+ */
952
+ History.storeState = function(newState){
953
+ // Store the State
954
+ History.urlToId[newState.url] = newState.id;
955
+
956
+ // Push the State
957
+ History.storedStates.push(History.cloneObject(newState));
958
+
959
+ // Return newState
960
+ return newState;
961
+ };
962
+
963
+ /**
964
+ * History.isLastSavedState(newState)
965
+ * Tests to see if the state is the last state
966
+ * @param {Object} newState
967
+ * @return {boolean} isLast
968
+ */
969
+ History.isLastSavedState = function(newState){
970
+ // Prepare
971
+ var isLast = false,
972
+ newId, oldState, oldId;
973
+
974
+ // Check
975
+ if ( History.savedStates.length ) {
976
+ newId = newState.id;
977
+ oldState = History.getLastSavedState();
978
+ oldId = oldState.id;
979
+
980
+ // Check
981
+ isLast = (newId === oldId);
982
+ }
983
+
984
+ // Return
985
+ return isLast;
986
+ };
987
+
988
+ /**
989
+ * History.saveState
990
+ * Push a State
991
+ * @param {Object} newState
992
+ * @return {boolean} changed
993
+ */
994
+ History.saveState = function(newState){
995
+ // Check Hash
996
+ if ( History.isLastSavedState(newState) ) {
997
+ return false;
998
+ }
999
+
1000
+ // Push the State
1001
+ History.savedStates.push(History.cloneObject(newState));
1002
+
1003
+ // Return true
1004
+ return true;
1005
+ };
1006
+
1007
+ /**
1008
+ * History.getStateByIndex()
1009
+ * Gets a state by the index
1010
+ * @param {integer} index
1011
+ * @return {Object}
1012
+ */
1013
+ History.getStateByIndex = function(index){
1014
+ // Prepare
1015
+ var State = null;
1016
+
1017
+ // Handle
1018
+ if ( typeof index === 'undefined' ) {
1019
+ // Get the last inserted
1020
+ State = History.savedStates[History.savedStates.length-1];
1021
+ }
1022
+ else if ( index < 0 ) {
1023
+ // Get from the end
1024
+ State = History.savedStates[History.savedStates.length+index];
1025
+ }
1026
+ else {
1027
+ // Get from the beginning
1028
+ State = History.savedStates[index];
1029
+ }
1030
+
1031
+ // Return State
1032
+ return State;
1033
+ };
1034
+
1035
+
1036
+ // ====================================================================
1037
+ // Hash Helpers
1038
+
1039
+ /**
1040
+ * History.getHash()
1041
+ * Gets the current document hash
1042
+ * @return {string}
1043
+ */
1044
+ History.getHash = function(){
1045
+ var hash = History.unescapeHash(document.location.hash);
1046
+ return hash;
1047
+ };
1048
+
1049
+ /**
1050
+ * History.unescapeString()
1051
+ * Unescape a string
1052
+ * @param {String} str
1053
+ * @return {string}
1054
+ */
1055
+ History.unescapeString = function(str){
1056
+ // Prepare
1057
+ var result = str,
1058
+ tmp;
1059
+
1060
+ // Unescape hash
1061
+ while ( true ) {
1062
+ tmp = window.decodeURI(result);
1063
+ if ( tmp === result ) {
1064
+ break;
1065
+ }
1066
+ result = tmp;
1067
+ }
1068
+
1069
+ // Return result
1070
+ return result;
1071
+ };
1072
+
1073
+ /**
1074
+ * History.unescapeHash()
1075
+ * normalize and Unescape a Hash
1076
+ * @param {String} hash
1077
+ * @return {string}
1078
+ */
1079
+ History.unescapeHash = function(hash){
1080
+ // Prepare
1081
+ var result = History.normalizeHash(hash);
1082
+
1083
+ // Unescape hash
1084
+ result = History.unescapeString(result);
1085
+
1086
+ // Return result
1087
+ return result;
1088
+ };
1089
+
1090
+ /**
1091
+ * History.normalizeHash()
1092
+ * normalize a hash across browsers
1093
+ * @return {string}
1094
+ */
1095
+ History.normalizeHash = function(hash){
1096
+ // Prepare
1097
+ var result = hash.replace(/[^#]*#/,'').replace(/#!*/, '');
1098
+
1099
+ // Return result
1100
+ return result;
1101
+ };
1102
+
1103
+ /**
1104
+ * History.setHash(hash)
1105
+ * Sets the document hash
1106
+ * @param {string} hash
1107
+ * @return {History}
1108
+ */
1109
+ History.setHash = function(hash,queue){
1110
+ // Prepare
1111
+ var adjustedHash, State, pageUrl;
1112
+
1113
+ // Handle Queueing
1114
+ if ( queue !== false && History.busy() ) {
1115
+ // Wait + Push to Queue
1116
+ //History.debug('History.setHash: we must wait', arguments);
1117
+ History.pushQueue({
1118
+ scope: History,
1119
+ callback: History.setHash,
1120
+ args: arguments,
1121
+ queue: queue
1122
+ });
1123
+ return false;
1124
+ }
1125
+
1126
+ // Log
1127
+ //History.debug('History.setHash: called',hash);
1128
+
1129
+ // Prepare
1130
+ adjustedHash = History.escapeHash(hash);
1131
+
1132
+ // Make Busy + Continue
1133
+ History.busy(true);
1134
+
1135
+ // Check if hash is a state
1136
+ State = History.extractState(hash,true);
1137
+ if ( State && !History.emulated.pushState ) {
1138
+ // Hash is a state so skip the setHash
1139
+ //History.debug('History.setHash: Hash is a state so skipping the hash set with a direct pushState call',arguments);
1140
+
1141
+ // PushState
1142
+ History.pushState(State.data,State.title,State.url,false);
1143
+ }
1144
+ else if ( document.location.hash !== adjustedHash ) {
1145
+ // Hash is a proper hash, so apply it
1146
+
1147
+ // Handle browser bugs
1148
+ if ( History.bugs.setHash ) {
1149
+ // Fix Safari Bug https://bugs.webkit.org/show_bug.cgi?id=56249
1150
+
1151
+ // Fetch the base page
1152
+ pageUrl = History.getPageUrl();
1153
+
1154
+ // Safari hash apply
1155
+ History.pushState(null,null,pageUrl+'#'+adjustedHash,false);
1156
+ }
1157
+ else {
1158
+ // Normal hash apply
1159
+ document.location.hash = adjustedHash;
1160
+ }
1161
+ }
1162
+
1163
+ // Chain
1164
+ return History;
1165
+ };
1166
+
1167
+ /**
1168
+ * History.escape()
1169
+ * normalize and Escape a Hash
1170
+ * @return {string}
1171
+ */
1172
+ History.escapeHash = function(hash){
1173
+ // Prepare
1174
+ var result = History.normalizeHash(hash);
1175
+
1176
+ // Escape hash
1177
+ result = window.encodeURI(result);
1178
+
1179
+ // IE6 Escape Bug
1180
+ if ( !History.bugs.hashEscape ) {
1181
+ // Restore common parts
1182
+ result = result
1183
+ .replace(/\%21/g,'!')
1184
+ .replace(/\%26/g,'&')
1185
+ .replace(/\%3D/g,'=')
1186
+ .replace(/\%3F/g,'?');
1187
+ }
1188
+
1189
+ // Return result
1190
+ return result;
1191
+ };
1192
+
1193
+ /**
1194
+ * History.getHashByUrl(url)
1195
+ * Extracts the Hash from a URL
1196
+ * @param {string} url
1197
+ * @return {string} url
1198
+ */
1199
+ History.getHashByUrl = function(url){
1200
+ // Extract the hash
1201
+ var hash = String(url)
1202
+ .replace(/([^#]*)#?([^#]*)#?(.*)/, '$2')
1203
+ ;
1204
+
1205
+ // Unescape hash
1206
+ hash = History.unescapeHash(hash);
1207
+
1208
+ // Return hash
1209
+ return hash;
1210
+ };
1211
+
1212
+ /**
1213
+ * History.setTitle(title)
1214
+ * Applies the title to the document
1215
+ * @param {State} newState
1216
+ * @return {Boolean}
1217
+ */
1218
+ History.setTitle = function(newState){
1219
+ // Prepare
1220
+ var title = newState.title,
1221
+ firstState;
1222
+
1223
+ // Initial
1224
+ if ( !title ) {
1225
+ firstState = History.getStateByIndex(0);
1226
+ if ( firstState && firstState.url === newState.url ) {
1227
+ title = firstState.title||History.options.initialTitle;
1228
+ }
1229
+ }
1230
+
1231
+ // Apply
1232
+ try {
1233
+ document.getElementsByTagName('title')[0].innerHTML = title.replace('<','&lt;').replace('>','&gt;').replace(' & ',' &amp; ');
1234
+ }
1235
+ catch ( Exception ) { }
1236
+ document.title = title;
1237
+
1238
+ // Chain
1239
+ return History;
1240
+ };
1241
+
1242
+
1243
+ // ====================================================================
1244
+ // Queueing
1245
+
1246
+ /**
1247
+ * History.queues
1248
+ * The list of queues to use
1249
+ * First In, First Out
1250
+ */
1251
+ History.queues = [];
1252
+
1253
+ /**
1254
+ * History.busy(value)
1255
+ * @param {boolean} value [optional]
1256
+ * @return {boolean} busy
1257
+ */
1258
+ History.busy = function(value){
1259
+ // Apply
1260
+ if ( typeof value !== 'undefined' ) {
1261
+ //History.debug('History.busy: changing ['+(History.busy.flag||false)+'] to ['+(value||false)+']', History.queues.length);
1262
+ History.busy.flag = value;
1263
+ }
1264
+ // Default
1265
+ else if ( typeof History.busy.flag === 'undefined' ) {
1266
+ History.busy.flag = false;
1267
+ }
1268
+
1269
+ // Queue
1270
+ if ( !History.busy.flag ) {
1271
+ // Execute the next item in the queue
1272
+ clearTimeout(History.busy.timeout);
1273
+ var fireNext = function(){
1274
+ var i, queue, item;
1275
+ if ( History.busy.flag ) return;
1276
+ for ( i=History.queues.length-1; i >= 0; --i ) {
1277
+ queue = History.queues[i];
1278
+ if ( queue.length === 0 ) continue;
1279
+ item = queue.shift();
1280
+ History.fireQueueItem(item);
1281
+ History.busy.timeout = setTimeout(fireNext,History.options.busyDelay);
1282
+ }
1283
+ };
1284
+ History.busy.timeout = setTimeout(fireNext,History.options.busyDelay);
1285
+ }
1286
+
1287
+ // Return
1288
+ return History.busy.flag;
1289
+ };
1290
+
1291
+ /**
1292
+ * History.busy.flag
1293
+ */
1294
+ History.busy.flag = false;
1295
+
1296
+ /**
1297
+ * History.fireQueueItem(item)
1298
+ * Fire a Queue Item
1299
+ * @param {Object} item
1300
+ * @return {Mixed} result
1301
+ */
1302
+ History.fireQueueItem = function(item){
1303
+ return item.callback.apply(item.scope||History,item.args||[]);
1304
+ };
1305
+
1306
+ /**
1307
+ * History.pushQueue(callback,args)
1308
+ * Add an item to the queue
1309
+ * @param {Object} item [scope,callback,args,queue]
1310
+ */
1311
+ History.pushQueue = function(item){
1312
+ // Prepare the queue
1313
+ History.queues[item.queue||0] = History.queues[item.queue||0]||[];
1314
+
1315
+ // Add to the queue
1316
+ History.queues[item.queue||0].push(item);
1317
+
1318
+ // Chain
1319
+ return History;
1320
+ };
1321
+
1322
+ /**
1323
+ * History.queue (item,queue), (func,queue), (func), (item)
1324
+ * Either firs the item now if not busy, or adds it to the queue
1325
+ */
1326
+ History.queue = function(item,queue){
1327
+ // Prepare
1328
+ if ( typeof item === 'function' ) {
1329
+ item = {
1330
+ callback: item
1331
+ };
1332
+ }
1333
+ if ( typeof queue !== 'undefined' ) {
1334
+ item.queue = queue;
1335
+ }
1336
+
1337
+ // Handle
1338
+ if ( History.busy() ) {
1339
+ History.pushQueue(item);
1340
+ } else {
1341
+ History.fireQueueItem(item);
1342
+ }
1343
+
1344
+ // Chain
1345
+ return History;
1346
+ };
1347
+
1348
+ /**
1349
+ * History.clearQueue()
1350
+ * Clears the Queue
1351
+ */
1352
+ History.clearQueue = function(){
1353
+ History.busy.flag = false;
1354
+ History.queues = [];
1355
+ return History;
1356
+ };
1357
+
1358
+
1359
+ // ====================================================================
1360
+ // IE Bug Fix
1361
+
1362
+ /**
1363
+ * History.stateChanged
1364
+ * States whether or not the state has changed since the last double check was initialised
1365
+ */
1366
+ History.stateChanged = false;
1367
+
1368
+ /**
1369
+ * History.doubleChecker
1370
+ * Contains the timeout used for the double checks
1371
+ */
1372
+ History.doubleChecker = false;
1373
+
1374
+ /**
1375
+ * History.doubleCheckComplete()
1376
+ * Complete a double check
1377
+ * @return {History}
1378
+ */
1379
+ History.doubleCheckComplete = function(){
1380
+ // Update
1381
+ History.stateChanged = true;
1382
+
1383
+ // Clear
1384
+ History.doubleCheckClear();
1385
+
1386
+ // Chain
1387
+ return History;
1388
+ };
1389
+
1390
+ /**
1391
+ * History.doubleCheckClear()
1392
+ * Clear a double check
1393
+ * @return {History}
1394
+ */
1395
+ History.doubleCheckClear = function(){
1396
+ // Clear
1397
+ if ( History.doubleChecker ) {
1398
+ clearTimeout(History.doubleChecker);
1399
+ History.doubleChecker = false;
1400
+ }
1401
+
1402
+ // Chain
1403
+ return History;
1404
+ };
1405
+
1406
+ /**
1407
+ * History.doubleCheck()
1408
+ * Create a double check
1409
+ * @return {History}
1410
+ */
1411
+ History.doubleCheck = function(tryAgain){
1412
+ // Reset
1413
+ History.stateChanged = false;
1414
+ History.doubleCheckClear();
1415
+
1416
+ // Fix IE6,IE7 bug where calling history.back or history.forward does not actually change the hash (whereas doing it manually does)
1417
+ // Fix Safari 5 bug where sometimes the state does not change: https://bugs.webkit.org/show_bug.cgi?id=42940
1418
+ if ( History.bugs.ieDoubleCheck ) {
1419
+ // Apply Check
1420
+ History.doubleChecker = setTimeout(
1421
+ function(){
1422
+ History.doubleCheckClear();
1423
+ if ( !History.stateChanged ) {
1424
+ //History.debug('History.doubleCheck: State has not yet changed, trying again', arguments);
1425
+ // Re-Attempt
1426
+ tryAgain();
1427
+ }
1428
+ return true;
1429
+ },
1430
+ History.options.doubleCheckInterval
1431
+ );
1432
+ }
1433
+
1434
+ // Chain
1435
+ return History;
1436
+ };
1437
+
1438
+
1439
+ // ====================================================================
1440
+ // Safari Bug Fix
1441
+
1442
+ /**
1443
+ * History.safariStatePoll()
1444
+ * Poll the current state
1445
+ * @return {History}
1446
+ */
1447
+ History.safariStatePoll = function(){
1448
+ // Poll the URL
1449
+
1450
+ // Get the Last State which has the new URL
1451
+ var
1452
+ urlState = History.extractState(document.location.href),
1453
+ newState;
1454
+
1455
+ // Check for a difference
1456
+ if ( !History.isLastSavedState(urlState) ) {
1457
+ newState = urlState;
1458
+ }
1459
+ else {
1460
+ return;
1461
+ }
1462
+
1463
+ // Check if we have a state with that url
1464
+ // If not create it
1465
+ if ( !newState ) {
1466
+ //History.debug('History.safariStatePoll: new');
1467
+ newState = History.createStateObject();
1468
+ }
1469
+
1470
+ // Apply the New State
1471
+ //History.debug('History.safariStatePoll: trigger');
1472
+ History.Adapter.trigger(window,'popstate');
1473
+
1474
+ // Chain
1475
+ return History;
1476
+ };
1477
+
1478
+
1479
+ // ====================================================================
1480
+ // State Aliases
1481
+
1482
+ /**
1483
+ * History.back(queue)
1484
+ * Send the browser history back one item
1485
+ * @param {Integer} queue [optional]
1486
+ */
1487
+ History.back = function(queue){
1488
+ //History.debug('History.back: called', arguments);
1489
+
1490
+ // Handle Queueing
1491
+ if ( queue !== false && History.busy() ) {
1492
+ // Wait + Push to Queue
1493
+ //History.debug('History.back: we must wait', arguments);
1494
+ History.pushQueue({
1495
+ scope: History,
1496
+ callback: History.back,
1497
+ args: arguments,
1498
+ queue: queue
1499
+ });
1500
+ return false;
1501
+ }
1502
+
1503
+ // Make Busy + Continue
1504
+ History.busy(true);
1505
+
1506
+ // Fix certain browser bugs that prevent the state from changing
1507
+ History.doubleCheck(function(){
1508
+ History.back(false);
1509
+ });
1510
+
1511
+ // Go back
1512
+ history.go(-1);
1513
+
1514
+ // End back closure
1515
+ return true;
1516
+ };
1517
+
1518
+ /**
1519
+ * History.forward(queue)
1520
+ * Send the browser history forward one item
1521
+ * @param {Integer} queue [optional]
1522
+ */
1523
+ History.forward = function(queue){
1524
+ //History.debug('History.forward: called', arguments);
1525
+
1526
+ // Handle Queueing
1527
+ if ( queue !== false && History.busy() ) {
1528
+ // Wait + Push to Queue
1529
+ //History.debug('History.forward: we must wait', arguments);
1530
+ History.pushQueue({
1531
+ scope: History,
1532
+ callback: History.forward,
1533
+ args: arguments,
1534
+ queue: queue
1535
+ });
1536
+ return false;
1537
+ }
1538
+
1539
+ // Make Busy + Continue
1540
+ History.busy(true);
1541
+
1542
+ // Fix certain browser bugs that prevent the state from changing
1543
+ History.doubleCheck(function(){
1544
+ History.forward(false);
1545
+ });
1546
+
1547
+ // Go forward
1548
+ history.go(1);
1549
+
1550
+ // End forward closure
1551
+ return true;
1552
+ };
1553
+
1554
+ /**
1555
+ * History.go(index,queue)
1556
+ * Send the browser history back or forward index times
1557
+ * @param {Integer} queue [optional]
1558
+ */
1559
+ History.go = function(index,queue){
1560
+ //History.debug('History.go: called', arguments);
1561
+
1562
+ // Prepare
1563
+ var i;
1564
+
1565
+ // Handle
1566
+ if ( index > 0 ) {
1567
+ // Forward
1568
+ for ( i=1; i<=index; ++i ) {
1569
+ History.forward(queue);
1570
+ }
1571
+ }
1572
+ else if ( index < 0 ) {
1573
+ // Backward
1574
+ for ( i=-1; i>=index; --i ) {
1575
+ History.back(queue);
1576
+ }
1577
+ }
1578
+ else {
1579
+ throw new Error('History.go: History.go requires a positive or negative integer passed.');
1580
+ }
1581
+
1582
+ // Chain
1583
+ return History;
1584
+ };
1585
+
1586
+
1587
+ // ====================================================================
1588
+ // HTML5 State Support
1589
+
1590
+ // Non-Native pushState Implementation
1591
+ if ( History.emulated.pushState ) {
1592
+ /*
1593
+ * Provide Skeleton for HTML4 Browsers
1594
+ */
1595
+
1596
+ // Prepare
1597
+ var emptyFunction = function(){};
1598
+ History.pushState = History.pushState||emptyFunction;
1599
+ History.replaceState = History.replaceState||emptyFunction;
1600
+ } // History.emulated.pushState
1601
+
1602
+ // Native pushState Implementation
1603
+ else {
1604
+ /*
1605
+ * Use native HTML5 History API Implementation
1606
+ */
1607
+
1608
+ /**
1609
+ * History.onPopState(event,extra)
1610
+ * Refresh the Current State
1611
+ */
1612
+ History.onPopState = function(event,extra){
1613
+ // Prepare
1614
+ var stateId = false, newState = false, currentHash, currentState;
1615
+
1616
+ // Reset the double check
1617
+ History.doubleCheckComplete();
1618
+
1619
+ // Check for a Hash, and handle apporiatly
1620
+ currentHash = History.getHash();
1621
+ if ( currentHash ) {
1622
+ // Expand Hash
1623
+ currentState = History.extractState(currentHash||document.location.href,true);
1624
+ if ( currentState ) {
1625
+ // We were able to parse it, it must be a State!
1626
+ // Let's forward to replaceState
1627
+ //History.debug('History.onPopState: state anchor', currentHash, currentState);
1628
+ History.replaceState(currentState.data, currentState.title, currentState.url, false);
1629
+ }
1630
+ else {
1631
+ // Traditional Anchor
1632
+ //History.debug('History.onPopState: traditional anchor', currentHash);
1633
+ History.Adapter.trigger(window,'anchorchange');
1634
+ History.busy(false);
1635
+ }
1636
+
1637
+ // We don't care for hashes
1638
+ History.expectedStateId = false;
1639
+ return false;
1640
+ }
1641
+
1642
+ // Ensure
1643
+ stateId = History.Adapter.extractEventData('state',event,extra) || false;
1644
+
1645
+ // Fetch State
1646
+ if ( stateId ) {
1647
+ // Vanilla: Back/forward button was used
1648
+ newState = History.getStateById(stateId);
1649
+ }
1650
+ else if ( History.expectedStateId ) {
1651
+ // Vanilla: A new state was pushed, and popstate was called manually
1652
+ newState = History.getStateById(History.expectedStateId);
1653
+ }
1654
+ else {
1655
+ // Initial State
1656
+ newState = History.extractState(document.location.href);
1657
+ }
1658
+
1659
+ // The State did not exist in our store
1660
+ if ( !newState ) {
1661
+ // Regenerate the State
1662
+ newState = History.createStateObject(null,null,document.location.href);
1663
+ }
1664
+
1665
+ // Clean
1666
+ History.expectedStateId = false;
1667
+
1668
+ // Check if we are the same state
1669
+ if ( History.isLastSavedState(newState) ) {
1670
+ // There has been no change (just the page's hash has finally propagated)
1671
+ //History.debug('History.onPopState: no change', newState, History.savedStates);
1672
+ History.busy(false);
1673
+ return false;
1674
+ }
1675
+
1676
+ // Store the State
1677
+ History.storeState(newState);
1678
+ History.saveState(newState);
1679
+
1680
+ // Force update of the title
1681
+ History.setTitle(newState);
1682
+
1683
+ // Fire Our Event
1684
+ History.Adapter.trigger(window,'statechange');
1685
+ History.busy(false);
1686
+
1687
+ // Return true
1688
+ return true;
1689
+ };
1690
+ History.Adapter.bind(window,'popstate',History.onPopState);
1691
+
1692
+ /**
1693
+ * History.pushState(data,title,url)
1694
+ * Add a new State to the history object, become it, and trigger onpopstate
1695
+ * We have to trigger for HTML4 compatibility
1696
+ * @param {object} data
1697
+ * @param {string} title
1698
+ * @param {string} url
1699
+ * @return {true}
1700
+ */
1701
+ History.pushState = function(data,title,url,queue){
1702
+ //History.debug('History.pushState: called', arguments);
1703
+
1704
+ // Check the State
1705
+ if ( History.getHashByUrl(url) && History.emulated.pushState ) {
1706
+ throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).');
1707
+ }
1708
+
1709
+ // Handle Queueing
1710
+ if ( queue !== false && History.busy() ) {
1711
+ // Wait + Push to Queue
1712
+ //History.debug('History.pushState: we must wait', arguments);
1713
+ History.pushQueue({
1714
+ scope: History,
1715
+ callback: History.pushState,
1716
+ args: arguments,
1717
+ queue: queue
1718
+ });
1719
+ return false;
1720
+ }
1721
+
1722
+ // Make Busy + Continue
1723
+ History.busy(true);
1724
+
1725
+ // Create the newState
1726
+ var newState = History.createStateObject(data,title,url);
1727
+
1728
+ // Check it
1729
+ if ( History.isLastSavedState(newState) ) {
1730
+ // Won't be a change
1731
+ History.busy(false);
1732
+ }
1733
+ else {
1734
+ // Store the newState
1735
+ History.storeState(newState);
1736
+ History.expectedStateId = newState.id;
1737
+
1738
+ // Push the newState
1739
+ history.pushState(newState.id,newState.title,newState.url);
1740
+
1741
+ // Fire HTML5 Event
1742
+ History.Adapter.trigger(window,'popstate');
1743
+ }
1744
+
1745
+ // End pushState closure
1746
+ return true;
1747
+ };
1748
+
1749
+ /**
1750
+ * History.replaceState(data,title,url)
1751
+ * Replace the State and trigger onpopstate
1752
+ * We have to trigger for HTML4 compatibility
1753
+ * @param {object} data
1754
+ * @param {string} title
1755
+ * @param {string} url
1756
+ * @return {true}
1757
+ */
1758
+ History.replaceState = function(data,title,url,queue){
1759
+ //History.debug('History.replaceState: called', arguments);
1760
+
1761
+ // Check the State
1762
+ if ( History.getHashByUrl(url) && History.emulated.pushState ) {
1763
+ throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).');
1764
+ }
1765
+
1766
+ // Handle Queueing
1767
+ if ( queue !== false && History.busy() ) {
1768
+ // Wait + Push to Queue
1769
+ //History.debug('History.replaceState: we must wait', arguments);
1770
+ History.pushQueue({
1771
+ scope: History,
1772
+ callback: History.replaceState,
1773
+ args: arguments,
1774
+ queue: queue
1775
+ });
1776
+ return false;
1777
+ }
1778
+
1779
+ // Make Busy + Continue
1780
+ History.busy(true);
1781
+
1782
+ // Create the newState
1783
+ var newState = History.createStateObject(data,title,url);
1784
+
1785
+ // Check it
1786
+ if ( History.isLastSavedState(newState) ) {
1787
+ // Won't be a change
1788
+ History.busy(false);
1789
+ }
1790
+ else {
1791
+ // Store the newState
1792
+ History.storeState(newState);
1793
+ History.expectedStateId = newState.id;
1794
+
1795
+ // Push the newState
1796
+ history.replaceState(newState.id,newState.title,newState.url);
1797
+
1798
+ // Fire HTML5 Event
1799
+ History.Adapter.trigger(window,'popstate');
1800
+ }
1801
+
1802
+ // End replaceState closure
1803
+ return true;
1804
+ };
1805
+
1806
+ } // !History.emulated.pushState
1807
+
1808
+
1809
+ // ====================================================================
1810
+ // Initialise
1811
+
1812
+ /**
1813
+ * Load the Store
1814
+ */
1815
+ if ( sessionStorage ) {
1816
+ // Fetch
1817
+ try {
1818
+ History.store = JSON.parse(sessionStorage.getItem('History.store'))||{};
1819
+ }
1820
+ catch ( err ) {
1821
+ History.store = {};
1822
+ }
1823
+
1824
+ // Normalize
1825
+ History.normalizeStore();
1826
+ }
1827
+ else {
1828
+ // Default Load
1829
+ History.store = {};
1830
+ History.normalizeStore();
1831
+ }
1832
+
1833
+ /**
1834
+ * Clear Intervals on exit to prevent memory leaks
1835
+ */
1836
+ History.Adapter.bind(window,"beforeunload",History.clearAllIntervals);
1837
+ History.Adapter.bind(window,"unload",History.clearAllIntervals);
1838
+
1839
+ /**
1840
+ * Create the initial State
1841
+ */
1842
+ History.saveState(History.storeState(History.extractState(document.location.href,true)));
1843
+
1844
+ /**
1845
+ * Bind for Saving Store
1846
+ */
1847
+ if ( sessionStorage ) {
1848
+ // When the page is closed
1849
+ History.onUnload = function(){
1850
+ // Prepare
1851
+ var currentStore, item;
1852
+
1853
+ // Fetch
1854
+ try {
1855
+ currentStore = JSON.parse(sessionStorage.getItem('History.store'))||{};
1856
+ }
1857
+ catch ( err ) {
1858
+ currentStore = {};
1859
+ }
1860
+
1861
+ // Ensure
1862
+ currentStore.idToState = currentStore.idToState || {};
1863
+ currentStore.urlToId = currentStore.urlToId || {};
1864
+ currentStore.stateToId = currentStore.stateToId || {};
1865
+
1866
+ // Sync
1867
+ for ( item in History.idToState ) {
1868
+ if ( !History.idToState.hasOwnProperty(item) ) {
1869
+ continue;
1870
+ }
1871
+ currentStore.idToState[item] = History.idToState[item];
1872
+ }
1873
+ for ( item in History.urlToId ) {
1874
+ if ( !History.urlToId.hasOwnProperty(item) ) {
1875
+ continue;
1876
+ }
1877
+ currentStore.urlToId[item] = History.urlToId[item];
1878
+ }
1879
+ for ( item in History.stateToId ) {
1880
+ if ( !History.stateToId.hasOwnProperty(item) ) {
1881
+ continue;
1882
+ }
1883
+ currentStore.stateToId[item] = History.stateToId[item];
1884
+ }
1885
+
1886
+ // Update
1887
+ History.store = currentStore;
1888
+ History.normalizeStore();
1889
+
1890
+ // Store
1891
+ sessionStorage.setItem('History.store',JSON.stringify(currentStore));
1892
+ };
1893
+
1894
+ // For Internet Explorer
1895
+ History.intervalList.push(setInterval(History.onUnload,History.options.storeInterval));
1896
+
1897
+ // For Other Browsers
1898
+ History.Adapter.bind(window,'beforeunload',History.onUnload);
1899
+ History.Adapter.bind(window,'unload',History.onUnload);
1900
+
1901
+ // Both are enabled for consistency
1902
+ }
1903
+
1904
+ // Non-Native pushState Implementation
1905
+ if ( !History.emulated.pushState ) {
1906
+ // Be aware, the following is only for native pushState implementations
1907
+ // If you are wanting to include something for all browsers
1908
+ // Then include it above this if block
1909
+
1910
+ /**
1911
+ * Setup Safari Fix
1912
+ */
1913
+ if ( History.bugs.safariPoll ) {
1914
+ History.intervalList.push(setInterval(History.safariStatePoll, History.options.safariPollInterval));
1915
+ }
1916
+
1917
+ /**
1918
+ * Ensure Cross Browser Compatibility
1919
+ */
1920
+ if ( navigator.vendor === 'Apple Computer, Inc.' || (navigator.appCodeName||'') === 'Mozilla' ) {
1921
+ /**
1922
+ * Fix Safari HashChange Issue
1923
+ */
1924
+
1925
+ // Setup Alias
1926
+ History.Adapter.bind(window,'hashchange',function(){
1927
+ History.Adapter.trigger(window,'popstate');
1928
+ });
1929
+
1930
+ // Initialise Alias
1931
+ if ( History.getHash() ) {
1932
+ History.Adapter.onDomLoad(function(){
1933
+ History.Adapter.trigger(window,'hashchange');
1934
+ });
1935
+ }
1936
+ }
1937
+
1938
+ } // !History.emulated.pushState
1939
+
1940
+
1941
+ }; // History.initCore
1942
+
1943
+ // Try and Initialise History
1944
+ History.init();
1945
+
1946
+ })(window);