wiselinks 0.3.0 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +28 -4
- data/VERSION +1 -1
- data/lib/assets/javascripts/_history.adapter.jquery.js +76 -0
- data/lib/assets/javascripts/_history.html4.js +621 -0
- data/lib/assets/javascripts/_history.js +1946 -1
- data/lib/assets/javascripts/wiselinks.js.coffee +34 -16
- data/lib/wiselinks/headers.rb +4 -3
- data/wiselinks.gemspec +4 -2
- metadata +5 -3
@@ -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("<","<").replace(">",">").replace(" & "," & ")}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('<','<').replace('>','>').replace(' & ',' & ');
|
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);
|