summaryrefslogtreecommitdiff
path: root/lib/dojo/hash.js
diff options
context:
space:
mode:
authorAndrew Dolgov <[email protected]>2011-03-04 19:02:28 +0300
committerAndrew Dolgov <[email protected]>2011-03-04 19:02:59 +0300
commita089699c8915636ba4f158d77dba9b012bc93208 (patch)
treeb2d7d051f1f55d44a6be07d3ee137e5a7ccfcefb /lib/dojo/hash.js
parentcfad9259a6feacfa8194b1312770ae6db1ecce50 (diff)
build custom layer of Dojo to speed up loading of tt-rss (refs #293)
Diffstat (limited to 'lib/dojo/hash.js')
-rw-r--r--lib/dojo/hash.js354
1 files changed, 228 insertions, 126 deletions
diff --git a/lib/dojo/hash.js b/lib/dojo/hash.js
index b73d37058..6ef74b521 100644
--- a/lib/dojo/hash.js
+++ b/lib/dojo/hash.js
@@ -5,133 +5,235 @@
*/
-if(!dojo._hasResource["dojo.hash"]){
-dojo._hasResource["dojo.hash"]=true;
+if(!dojo._hasResource["dojo.hash"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
+dojo._hasResource["dojo.hash"] = true;
dojo.provide("dojo.hash");
+//TODOC: where does this go?
+// summary:
+// Methods for monitoring and updating the hash in the browser URL.
+//
+// example:
+// dojo.subscribe("/dojo/hashchange", context, callback);
+//
+// function callback (hashValue){
+// // do something based on the hash value.
+// }
+
(function(){
-dojo.hash=function(_1,_2){
-if(!arguments.length){
-return _3();
-}
-if(_1.charAt(0)=="#"){
-_1=_1.substring(1);
-}
-if(_2){
-_4(_1);
-}else{
-location.href="#"+_1;
-}
-return _1;
-};
-var _5=null,_6=null,_7=dojo.config.hashPollFrequency||100;
-function _8(_9,_a){
-var i=_9.indexOf(_a);
-return (i>=0)?_9.substring(i+1):"";
-};
-function _3(){
-return _8(location.href,"#");
-};
-function _b(){
-dojo.publish("/dojo/hashchange",[_3()]);
-};
-function _c(){
-if(_3()===_5){
-return;
-}
-_5=_3();
-_b();
-};
-function _4(_d){
-if(_6){
-if(_6.isTransitioning()){
-setTimeout(dojo.hitch(null,_4,_d),_7);
-return;
-}
-var _e=_6.iframe.location.href;
-var _f=_e.indexOf("?");
-_6.iframe.location.replace(_e.substring(0,_f)+"?"+_d);
-return;
-}
-location.replace("#"+_d);
-_c();
-};
-function _10(){
-var ifr=document.createElement("iframe"),_11="dojo-hash-iframe",_12=dojo.config.dojoBlankHtmlUrl||dojo.moduleUrl("dojo","resources/blank.html");
-ifr.id=_11;
-ifr.src=_12+"?"+_3();
-ifr.style.display="none";
-document.body.appendChild(ifr);
-this.iframe=dojo.global[_11];
-var _13,_14,_15,_16,_17,_18=this.iframe.location;
-function _19(){
-_5=_3();
-_13=_17?_5:_8(_18.href,"?");
-_14=false;
-_15=null;
-};
-this.isTransitioning=function(){
-return _14;
-};
-this.pollLocation=function(){
-if(!_17){
-try{
-var _1a=_8(_18.href,"?");
-if(document.title!=_16){
-_16=this.iframe.document.title=document.title;
-}
-}
-catch(e){
-_17=true;
-console.error("dojo.hash: Error adding history entry. Server unreachable.");
-}
-}
-var _1b=_3();
-if(_14&&_5===_1b){
-if(_17||_1a===_15){
-_19();
-_b();
-}else{
-setTimeout(dojo.hitch(this,this.pollLocation),0);
-return;
-}
-}else{
-if(_5===_1b&&(_17||_13===_1a)){
-}else{
-if(_5!==_1b){
-_5=_1b;
-_14=true;
-_15=_1b;
-ifr.src=_12+"?"+_15;
-_17=false;
-setTimeout(dojo.hitch(this,this.pollLocation),0);
-return;
-}else{
-if(!_17){
-location.href="#"+_18.search.substring(1);
-_19();
-_b();
-}
-}
-}
-}
-setTimeout(dojo.hitch(this,this.pollLocation),_7);
-};
-_19();
-setTimeout(dojo.hitch(this,this.pollLocation),_7);
-};
-dojo.addOnLoad(function(){
-if("onhashchange" in dojo.global&&(!dojo.isIE||(dojo.isIE>=8&&document.compatMode!="BackCompat"))){
-dojo.connect(dojo.global,"onhashchange",_b);
-}else{
-if(document.addEventListener){
-_5=_3();
-setInterval(_c,_7);
-}else{
-if(document.attachEvent){
-_6=new _10();
-}
-}
-}
-});
+ dojo.hash = function(/* String? */ hash, /* Boolean? */ replace){
+ // summary:
+ // Gets or sets the hash string.
+ // description:
+ // Handles getting and setting of location.hash.
+ // - If no arguments are passed, acts as a getter.
+ // - If a string is passed, acts as a setter.
+ // hash:
+ // String: the hash is set - #string.
+ // replace:
+ // Boolean: If true, updates the hash value in the current history
+ // state instead of creating a new history state.
+ // returns:
+ // when used as a getter, returns the current hash string.
+ // when used as a setter, returns the new hash string.
+
+ // getter
+ if(!arguments.length){
+ return _getHash();
+ }
+ // setter
+ if(hash.charAt(0) == "#"){
+ hash = hash.substring(1);
+ }
+ if(replace){
+ _replace(hash);
+ }else{
+ location.href = "#" + hash;
+ }
+ return hash; // String
+ }
+
+ // Global vars
+ var _recentHash = null,
+ _ieUriMonitor = null,
+ _pollFrequency = dojo.config.hashPollFrequency || 100;
+
+ //Internal functions
+ function _getSegment(str, delimiter){
+ var i = str.indexOf(delimiter);
+ return (i >= 0) ? str.substring(i+1) : "";
+ }
+
+ function _getHash(){
+ return _getSegment(location.href, "#");
+ }
+
+ function _dispatchEvent(){
+ dojo.publish("/dojo/hashchange", [_getHash()]);
+ }
+
+ function _pollLocation(){
+ if(_getHash() === _recentHash){
+ return;
+ }
+ _recentHash = _getHash();
+ _dispatchEvent();
+ }
+
+ function _replace(hash){
+ if(_ieUriMonitor){
+ if(_ieUriMonitor.isTransitioning()){
+ setTimeout(dojo.hitch(null,_replace,hash), _pollFrequency);
+ return;
+ }
+ var href = _ieUriMonitor.iframe.location.href;
+ var index = href.indexOf('?');
+ // main frame will detect and update itself
+ _ieUriMonitor.iframe.location.replace(href.substring(0, index) + "?" + hash);
+ return;
+ }
+ location.replace("#"+hash);
+ _pollLocation();
+ }
+
+ function IEUriMonitor(){
+ // summary:
+ // Determine if the browser's URI has changed or if the user has pressed the
+ // back or forward button. If so, call _dispatchEvent.
+ //
+ // description:
+ // IE doesn't add changes to the URI's hash into the history unless the hash
+ // value corresponds to an actual named anchor in the document. To get around
+ // this IE difference, we use a background IFrame to maintain a back-forward
+ // history, by updating the IFrame's query string to correspond to the
+ // value of the main browser location's hash value.
+ //
+ // E.g. if the value of the browser window's location changes to
+ //
+ // #action=someAction
+ //
+ // ... then we'd update the IFrame's source to:
+ //
+ // ?action=someAction
+ //
+ // This design leads to a somewhat complex state machine, which is
+ // described below:
+ //
+ // s1: Stable state - neither the window's location has changed nor
+ // has the IFrame's location. Note that this is the 99.9% case, so
+ // we optimize for it.
+ // Transitions: s1, s2, s3
+ // s2: Window's location changed - when a user clicks a hyperlink or
+ // code programmatically changes the window's URI.
+ // Transitions: s4
+ // s3: Iframe's location changed as a result of user pressing back or
+ // forward - when the user presses back or forward, the location of
+ // the background's iframe changes to the previous or next value in
+ // its history.
+ // Transitions: s1
+ // s4: IEUriMonitor has programmatically changed the location of the
+ // background iframe, but it's location hasn't yet changed. In this
+ // case we do nothing because we need to wait for the iframe's
+ // location to reflect its actual state.
+ // Transitions: s4, s5
+ // s5: IEUriMonitor has programmatically changed the location of the
+ // background iframe, and the iframe's location has caught up with
+ // reality. In this case we need to transition to s1.
+ // Transitions: s1
+ //
+ // The hashchange event is always dispatched on the transition back to s1.
+ //
+
+ // create and append iframe
+ var ifr = document.createElement("iframe"),
+ IFRAME_ID = "dojo-hash-iframe",
+ ifrSrc = dojo.config.dojoBlankHtmlUrl || dojo.moduleUrl("dojo", "resources/blank.html");
+ ifr.id = IFRAME_ID;
+ ifr.src = ifrSrc + "?" + _getHash();
+ ifr.style.display = "none";
+ document.body.appendChild(ifr);
+
+ this.iframe = dojo.global[IFRAME_ID];
+ var recentIframeQuery, transitioning, expectedIFrameQuery, docTitle, ifrOffline,
+ iframeLoc = this.iframe.location;
+
+ function resetState(){
+ _recentHash = _getHash();
+ recentIframeQuery = ifrOffline ? _recentHash : _getSegment(iframeLoc.href, "?");
+ transitioning = false;
+ expectedIFrameQuery = null;
+ }
+
+ this.isTransitioning = function(){
+ return transitioning;
+ }
+
+ this.pollLocation = function(){
+ if(!ifrOffline) {
+ try{
+ //see if we can access the iframe's location without a permission denied error
+ var iframeSearch = _getSegment(iframeLoc.href, "?");
+ //good, the iframe is same origin (no thrown exception)
+ if(document.title != docTitle){ //sync title of main window with title of iframe.
+ docTitle = this.iframe.document.title = document.title;
+ }
+ }catch(e){
+ //permission denied - server cannot be reached.
+ ifrOffline = true;
+ console.error("dojo.hash: Error adding history entry. Server unreachable.");
+ }
+ }
+ var hash = _getHash();
+ if(transitioning && _recentHash === hash){
+ // we're in an iframe transition (s4 or s5)
+ if(ifrOffline || iframeSearch === expectedIFrameQuery){
+ // s5 (iframe caught up to main window or iframe offline), transition back to s1
+ resetState();
+ _dispatchEvent();
+ }else{
+ // s4 (waiting for iframe to catch up to main window)
+ setTimeout(dojo.hitch(this,this.pollLocation),0);
+ return;
+ }
+ }else if(_recentHash === hash && (ifrOffline || recentIframeQuery === iframeSearch)){
+ // we're in stable state (s1, iframe query == main window hash), do nothing
+ }else{
+ // the user has initiated a URL change somehow.
+ // sync iframe query <-> main window hash
+ if(_recentHash !== hash){
+ // s2 (main window location changed), set iframe url and transition to s4
+ _recentHash = hash;
+ transitioning = true;
+ expectedIFrameQuery = hash;
+ ifr.src = ifrSrc + "?" + expectedIFrameQuery;
+ ifrOffline = false; //we're updating the iframe src - set offline to false so we can check again on next poll.
+ setTimeout(dojo.hitch(this,this.pollLocation),0); //yielded transition to s4 while iframe reloads.
+ return;
+ }else if(!ifrOffline){
+ // s3 (iframe location changed via back/forward button), set main window url and transition to s1.
+ location.href = "#" + iframeLoc.search.substring(1);
+ resetState();
+ _dispatchEvent();
+ }
+ }
+ setTimeout(dojo.hitch(this,this.pollLocation), _pollFrequency);
+ }
+ resetState(); // initialize state (transition to s1)
+ setTimeout(dojo.hitch(this,this.pollLocation), _pollFrequency);
+ }
+ dojo.addOnLoad(function(){
+ if("onhashchange" in dojo.global && (!dojo.isIE || (dojo.isIE >= 8 && document.compatMode != "BackCompat"))){ //need this IE browser test because "onhashchange" exists in IE8 in IE7 mode
+ dojo.connect(dojo.global,"onhashchange",_dispatchEvent);
+ }else{
+ if(document.addEventListener){ // Non-IE
+ _recentHash = _getHash();
+ setInterval(_pollLocation, _pollFrequency); //Poll the window location for changes
+ }else if(document.attachEvent){ // IE7-
+ //Use hidden iframe in versions of IE that don't have onhashchange event
+ _ieUriMonitor = new IEUriMonitor();
+ }
+ // else non-supported browser, do nothing.
+ }
+ });
})();
+
}