]> git.wh0rd.org - tt-rss.git/blame - lib/dojo/hash.js.uncompressed.js
make precache_headlines_idle() start slower
[tt-rss.git] / lib / dojo / hash.js.uncompressed.js
CommitLineData
1354d172
AD
1define("dojo/hash", ["./_base/kernel", "require", "./_base/connect", "./_base/lang", "./ready", "./_base/sniff"],
2 function(dojo, require, connect, lang, ready, has) {
3 // module:
4 // dojo/hash
5 // summary:
6 // TODOC
7
8
9//TODOC: where does this go?
10// summary:
11// Methods for monitoring and updating the hash in the browser URL.
12//
13// example:
14// dojo.subscribe("/dojo/hashchange", context, callback);
15//
16// function callback (hashValue){
17// // do something based on the hash value.
18// }
19
20 dojo.hash = function(/* String? */ hash, /* Boolean? */ replace){
21 // summary:
22 // Gets or sets the hash string.
23 // description:
24 // Handles getting and setting of location.hash.
25 // - If no arguments are passed, acts as a getter.
26 // - If a string is passed, acts as a setter.
27 // hash:
28 // the hash is set - #string.
29 // replace:
30 // If true, updates the hash value in the current history
31 // state instead of creating a new history state.
32 // returns:
33 // when used as a getter, returns the current hash string.
34 // when used as a setter, returns the new hash string.
35
36 // getter
37 if(!arguments.length){
38 return _getHash();
39 }
40 // setter
41 if(hash.charAt(0) == "#"){
42 hash = hash.substring(1);
43 }
44 if(replace){
45 _replace(hash);
46 }else{
47 location.href = "#" + hash;
48 }
49 return hash; // String
50 };
51
52 // Global vars
53 var _recentHash, _ieUriMonitor, _connect,
54 _pollFrequency = dojo.config.hashPollFrequency || 100;
55
56 //Internal functions
57 function _getSegment(str, delimiter){
58 var i = str.indexOf(delimiter);
59 return (i >= 0) ? str.substring(i+1) : "";
60 }
61
62 function _getHash(){
63 return _getSegment(location.href, "#");
64 }
65
66 function _dispatchEvent(){
67 connect.publish("/dojo/hashchange", [_getHash()]);
68 }
69
70 function _pollLocation(){
71 if(_getHash() === _recentHash){
72 return;
73 }
74 _recentHash = _getHash();
75 _dispatchEvent();
76 }
77
78 function _replace(hash){
79 if(_ieUriMonitor){
80 if(_ieUriMonitor.isTransitioning()){
81 setTimeout(lang.hitch(null,_replace,hash), _pollFrequency);
82 return;
83 }
84 var href = _ieUriMonitor.iframe.location.href;
85 var index = href.indexOf('?');
86 // main frame will detect and update itself
87 _ieUriMonitor.iframe.location.replace(href.substring(0, index) + "?" + hash);
88 return;
89 }
90 location.replace("#"+hash);
91 !_connect && _pollLocation();
92 }
93
94 function IEUriMonitor(){
95 // summary:
96 // Determine if the browser's URI has changed or if the user has pressed the
97 // back or forward button. If so, call _dispatchEvent.
98 //
99 // description:
100 // IE doesn't add changes to the URI's hash into the history unless the hash
101 // value corresponds to an actual named anchor in the document. To get around
102 // this IE difference, we use a background IFrame to maintain a back-forward
103 // history, by updating the IFrame's query string to correspond to the
104 // value of the main browser location's hash value.
105 //
106 // E.g. if the value of the browser window's location changes to
107 //
108 // #action=someAction
109 //
110 // ... then we'd update the IFrame's source to:
111 //
112 // ?action=someAction
113 //
114 // This design leads to a somewhat complex state machine, which is
115 // described below:
116 //
117 // s1: Stable state - neither the window's location has changed nor
118 // has the IFrame's location. Note that this is the 99.9% case, so
119 // we optimize for it.
120 // Transitions: s1, s2, s3
121 // s2: Window's location changed - when a user clicks a hyperlink or
122 // code programmatically changes the window's URI.
123 // Transitions: s4
124 // s3: Iframe's location changed as a result of user pressing back or
125 // forward - when the user presses back or forward, the location of
126 // the background's iframe changes to the previous or next value in
127 // its history.
128 // Transitions: s1
129 // s4: IEUriMonitor has programmatically changed the location of the
130 // background iframe, but it's location hasn't yet changed. In this
131 // case we do nothing because we need to wait for the iframe's
132 // location to reflect its actual state.
133 // Transitions: s4, s5
134 // s5: IEUriMonitor has programmatically changed the location of the
135 // background iframe, and the iframe's location has caught up with
136 // reality. In this case we need to transition to s1.
137 // Transitions: s1
138 //
139 // The hashchange event is always dispatched on the transition back to s1.
140 //
141
142 // create and append iframe
143 var ifr = document.createElement("iframe"),
144 IFRAME_ID = "dojo-hash-iframe",
145 ifrSrc = dojo.config.dojoBlankHtmlUrl || require.toUrl("./resources/blank.html");
146
147 if(dojo.config.useXDomain && !dojo.config.dojoBlankHtmlUrl){
148 console.warn("dojo.hash: When using cross-domain Dojo builds,"
149 + " please save dojo/resources/blank.html to your domain and set djConfig.dojoBlankHtmlUrl"
150 + " to the path on your domain to blank.html");
151 }
152
153 ifr.id = IFRAME_ID;
154 ifr.src = ifrSrc + "?" + _getHash();
155 ifr.style.display = "none";
156 document.body.appendChild(ifr);
157
158 this.iframe = dojo.global[IFRAME_ID];
159 var recentIframeQuery, transitioning, expectedIFrameQuery, docTitle, ifrOffline,
160 iframeLoc = this.iframe.location;
161
162 function resetState(){
163 _recentHash = _getHash();
164 recentIframeQuery = ifrOffline ? _recentHash : _getSegment(iframeLoc.href, "?");
165 transitioning = false;
166 expectedIFrameQuery = null;
167 }
168
169 this.isTransitioning = function(){
170 return transitioning;
171 };
172
173 this.pollLocation = function(){
174 if(!ifrOffline) {
175 try{
176 //see if we can access the iframe's location without a permission denied error
177 var iframeSearch = _getSegment(iframeLoc.href, "?");
178 //good, the iframe is same origin (no thrown exception)
179 if(document.title != docTitle){ //sync title of main window with title of iframe.
180 docTitle = this.iframe.document.title = document.title;
181 }
182 }catch(e){
183 //permission denied - server cannot be reached.
184 ifrOffline = true;
185 console.error("dojo.hash: Error adding history entry. Server unreachable.");
186 }
187 }
188 var hash = _getHash();
189 if(transitioning && _recentHash === hash){
190 // we're in an iframe transition (s4 or s5)
191 if(ifrOffline || iframeSearch === expectedIFrameQuery){
192 // s5 (iframe caught up to main window or iframe offline), transition back to s1
193 resetState();
194 _dispatchEvent();
195 }else{
196 // s4 (waiting for iframe to catch up to main window)
197 setTimeout(lang.hitch(this,this.pollLocation),0);
198 return;
199 }
200 }else if(_recentHash === hash && (ifrOffline || recentIframeQuery === iframeSearch)){
201 // we're in stable state (s1, iframe query == main window hash), do nothing
202 }else{
203 // the user has initiated a URL change somehow.
204 // sync iframe query <-> main window hash
205 if(_recentHash !== hash){
206 // s2 (main window location changed), set iframe url and transition to s4
207 _recentHash = hash;
208 transitioning = true;
209 expectedIFrameQuery = hash;
210 ifr.src = ifrSrc + "?" + expectedIFrameQuery;
211 ifrOffline = false; //we're updating the iframe src - set offline to false so we can check again on next poll.
212 setTimeout(lang.hitch(this,this.pollLocation),0); //yielded transition to s4 while iframe reloads.
213 return;
214 }else if(!ifrOffline){
215 // s3 (iframe location changed via back/forward button), set main window url and transition to s1.
216 location.href = "#" + iframeLoc.search.substring(1);
217 resetState();
218 _dispatchEvent();
219 }
220 }
221 setTimeout(lang.hitch(this,this.pollLocation), _pollFrequency);
222 };
223 resetState(); // initialize state (transition to s1)
224 setTimeout(lang.hitch(this,this.pollLocation), _pollFrequency);
225 }
226 ready(function(){
227 if("onhashchange" in dojo.global && (!has("ie") || (has("ie") >= 8 && document.compatMode != "BackCompat"))){ //need this IE browser test because "onhashchange" exists in IE8 in IE7 mode
228 _connect = connect.connect(dojo.global,"onhashchange",_dispatchEvent);
229 }else{
230 if(document.addEventListener){ // Non-IE
231 _recentHash = _getHash();
232 setInterval(_pollLocation, _pollFrequency); //Poll the window location for changes
233 }else if(document.attachEvent){ // IE7-
234 //Use hidden iframe in versions of IE that don't have onhashchange event
235 _ieUriMonitor = new IEUriMonitor();
236 }
237 // else non-supported browser, do nothing.
238 }
239 });
240
241 return dojo.hash;
242
243});