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