]>
git.wh0rd.org - tt-rss.git/blob - lib/dojo/hash.js
e7f6a400ff71c1b868757cb8cb14b434545367f2
2 Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved.
3 Available via Academic Free License >= 2.1 OR the modified BSD license.
4 see: http://dojotoolkit.org/license for details
8 if (! dojo
. _hasResource
[ "dojo.hash" ]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
9 dojo
. _hasResource
[ "dojo.hash" ] = true ;
10 dojo
. provide ( "dojo.hash" );
13 //TODOC: where does this go?
15 // Methods for monitoring and updating the hash in the browser URL.
18 // dojo.subscribe("/dojo/hashchange", context, callback);
20 // function callback (hashValue){
21 // // do something based on the hash value.
25 dojo
. hash = function ( /* String? */ hash
, /* Boolean? */ replace
){
27 // Gets or sets the hash string.
29 // Handles getting and setting of location.hash.
30 // - If no arguments are passed, acts as a getter.
31 // - If a string is passed, acts as a setter.
33 // the hash is set - #string.
35 // If true, updates the hash value in the current history
36 // state instead of creating a new history state.
38 // when used as a getter, returns the current hash string.
39 // when used as a setter, returns the new hash string.
42 if (! arguments
. length
){
46 if ( hash
. charAt ( 0 ) == "#" ){
47 hash
= hash
. substring ( 1 );
52 location
. href
= "#" + hash
;
54 return hash
; // String
58 var _recentHash
, _ieUriMonitor
, _connect
,
59 _pollFrequency
= dojo
. config
. hashPollFrequency
|| 100 ;
62 function _getSegment ( str
, delimiter
){
63 var i
= str
. indexOf ( delimiter
);
64 return ( i
>= 0 ) ? str
. substring ( i
+ 1 ) : "" ;
68 return _getSegment ( location
. href
, "#" );
71 function _dispatchEvent (){
72 dojo
. publish ( "/dojo/hashchange" , [ _getHash ()]);
75 function _pollLocation (){
76 if ( _getHash () === _recentHash
){
79 _recentHash
= _getHash ();
83 function _replace ( hash
){
85 if ( _ieUriMonitor
. isTransitioning ()){
86 setTimeout ( dojo
. hitch ( null , _replace
, hash
), _pollFrequency
);
89 var href
= _ieUriMonitor
. iframe
. location
. href
;
90 var index
= href
. indexOf ( '?' );
91 // main frame will detect and update itself
92 _ieUriMonitor
. iframe
. location
. replace ( href
. substring ( 0 , index
) + "?" + hash
);
95 location
. replace ( "#" + hash
);
96 ! _connect
&& _pollLocation ();
99 function IEUriMonitor (){
101 // Determine if the browser's URI has changed or if the user has pressed the
102 // back or forward button. If so, call _dispatchEvent.
105 // IE doesn't add changes to the URI's hash into the history unless the hash
106 // value corresponds to an actual named anchor in the document. To get around
107 // this IE difference, we use a background IFrame to maintain a back-forward
108 // history, by updating the IFrame's query string to correspond to the
109 // value of the main browser location's hash value.
111 // E.g. if the value of the browser window's location changes to
113 // #action=someAction
115 // ... then we'd update the IFrame's source to:
117 // ?action=someAction
119 // This design leads to a somewhat complex state machine, which is
122 // s1: Stable state - neither the window's location has changed nor
123 // has the IFrame's location. Note that this is the 99.9% case, so
124 // we optimize for it.
125 // Transitions: s1, s2, s3
126 // s2: Window's location changed - when a user clicks a hyperlink or
127 // code programmatically changes the window's URI.
129 // s3: Iframe's location changed as a result of user pressing back or
130 // forward - when the user presses back or forward, the location of
131 // the background's iframe changes to the previous or next value in
134 // s4: IEUriMonitor has programmatically changed the location of the
135 // background iframe, but it's location hasn't yet changed. In this
136 // case we do nothing because we need to wait for the iframe's
137 // location to reflect its actual state.
138 // Transitions: s4, s5
139 // s5: IEUriMonitor has programmatically changed the location of the
140 // background iframe, and the iframe's location has caught up with
141 // reality. In this case we need to transition to s1.
144 // The hashchange event is always dispatched on the transition back to s1.
147 // create and append iframe
148 var ifr
= document
. createElement ( "iframe" ),
149 IFRAME_ID
= "dojo-hash-iframe" ,
150 ifrSrc
= dojo
. config
. dojoBlankHtmlUrl
|| dojo
. moduleUrl ( "dojo" , "resources/blank.html" );
152 if ( dojo
. config
. useXDomain
&& ! dojo
. config
. dojoBlankHtmlUrl
){
153 console
. warn ( "dojo.hash: When using cross-domain Dojo builds,"
154 + " please save dojo/resources/blank.html to your domain and set djConfig.dojoBlankHtmlUrl"
155 + " to the path on your domain to blank.html" );
159 ifr
. src
= ifrSrc
+ "?" + _getHash ();
160 ifr
. style
. display
= "none" ;
161 document
. body
. appendChild ( ifr
);
163 this . iframe
= dojo
. global
[ IFRAME_ID
];
164 var recentIframeQuery
, transitioning
, expectedIFrameQuery
, docTitle
, ifrOffline
,
165 iframeLoc
= this . iframe
. location
;
167 function resetState (){
168 _recentHash
= _getHash ();
169 recentIframeQuery
= ifrOffline
? _recentHash
: _getSegment ( iframeLoc
. href
, "?" );
170 transitioning
= false ;
171 expectedIFrameQuery
= null ;
174 this . isTransitioning = function (){
175 return transitioning
;
178 this . pollLocation = function (){
181 //see if we can access the iframe's location without a permission denied error
182 var iframeSearch
= _getSegment ( iframeLoc
. href
, "?" );
183 //good, the iframe is same origin (no thrown exception)
184 if ( document
. title
!= docTitle
){ //sync title of main window with title of iframe.
185 docTitle
= this . iframe
. document
. title
= document
. title
;
188 //permission denied - server cannot be reached.
190 console
. error ( "dojo.hash: Error adding history entry. Server unreachable." );
193 var hash
= _getHash ();
194 if ( transitioning
&& _recentHash
=== hash
){
195 // we're in an iframe transition (s4 or s5)
196 if ( ifrOffline
|| iframeSearch
=== expectedIFrameQuery
){
197 // s5 (iframe caught up to main window or iframe offline), transition back to s1
201 // s4 (waiting for iframe to catch up to main window)
202 setTimeout ( dojo
. hitch ( this , this . pollLocation
), 0 );
205 } else if ( _recentHash
=== hash
&& ( ifrOffline
|| recentIframeQuery
=== iframeSearch
)){
206 // we're in stable state (s1, iframe query == main window hash), do nothing
208 // the user has initiated a URL change somehow.
209 // sync iframe query <-> main window hash
210 if ( _recentHash
!== hash
){
211 // s2 (main window location changed), set iframe url and transition to s4
213 transitioning
= true ;
214 expectedIFrameQuery
= hash
;
215 ifr
. src
= ifrSrc
+ "?" + expectedIFrameQuery
;
216 ifrOffline
= false ; //we're updating the iframe src - set offline to false so we can check again on next poll.
217 setTimeout ( dojo
. hitch ( this , this . pollLocation
), 0 ); //yielded transition to s4 while iframe reloads.
219 } else if (! ifrOffline
){
220 // s3 (iframe location changed via back/forward button), set main window url and transition to s1.
221 location
. href
= "#" + iframeLoc
. search
. substring ( 1 );
226 setTimeout ( dojo
. hitch ( this , this . pollLocation
), _pollFrequency
);
228 resetState (); // initialize state (transition to s1)
229 setTimeout ( dojo
. hitch ( this , this . pollLocation
), _pollFrequency
);
231 dojo
. addOnLoad ( function (){
232 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
233 _connect
= dojo
. connect ( dojo
. global
, "onhashchange" , _dispatchEvent
);
235 if ( document
. addEventListener
){ // Non-IE
236 _recentHash
= _getHash ();
237 setInterval ( _pollLocation
, _pollFrequency
); //Poll the window location for changes
238 } else if ( document
. attachEvent
){ // IE7-
239 //Use hidden iframe in versions of IE that don't have onhashchange event
240 _ieUriMonitor
= new IEUriMonitor ();
242 // else non-supported browser, do nothing.