]> git.wh0rd.org - tt-rss.git/blame - lib/dojo/back.js.uncompressed.js
make precache_headlines_idle() start slower
[tt-rss.git] / lib / dojo / back.js.uncompressed.js
CommitLineData
1354d172
AD
1define("dojo/back", ["./_base/kernel", "./_base/lang", "./_base/sniff", "./dom", "./dom-construct", "./_base/window", "require"], function(dojo, lang, sniff, dom, domConstruct, baseWindow, require) {
2 // module:
3 // dojo/back
4 // summary:
5 // TODOC
6
7 lang.getObject("back", true, dojo);
8
9/*=====
10dojo.back = {
11 // summary: Browser history management resources
12};
13=====*/
14
15 var back = dojo.back,
16
17 // everyone deals with encoding the hash slightly differently
18
19 getHash = back.getHash = function(){
20 var h = window.location.hash;
21 if(h.charAt(0) == "#"){ h = h.substring(1); }
22 return sniff("mozilla") ? h : decodeURIComponent(h);
23 },
24
25 setHash = back.setHash = function(h){
26 if(!h){ h = ""; }
27 window.location.hash = encodeURIComponent(h);
28 historyCounter = history.length;
29 };
30
31 var initialHref = (typeof(window) !== "undefined") ? window.location.href : "";
32 var initialHash = (typeof(window) !== "undefined") ? getHash() : "";
33 var initialState = null;
34
35 var locationTimer = null;
36 var bookmarkAnchor = null;
37 var historyIframe = null;
38 var forwardStack = [];
39 var historyStack = [];
40 var moveForward = false;
41 var changingUrl = false;
42 var historyCounter;
43
44 function handleBackButton(){
45 //summary: private method. Do not call this directly.
46
47 //The "current" page is always at the top of the history stack.
48 var current = historyStack.pop();
49 if(!current){ return; }
50 var last = historyStack[historyStack.length-1];
51 if(!last && historyStack.length == 0){
52 last = initialState;
53 }
54 if(last){
55 if(last.kwArgs["back"]){
56 last.kwArgs["back"]();
57 }else if(last.kwArgs["backButton"]){
58 last.kwArgs["backButton"]();
59 }else if(last.kwArgs["handle"]){
60 last.kwArgs.handle("back");
61 }
62 }
63 forwardStack.push(current);
64 }
65
66 back.goBack = handleBackButton;
67
68 function handleForwardButton(){
69 //summary: private method. Do not call this directly.
70 var last = forwardStack.pop();
71 if(!last){ return; }
72 if(last.kwArgs["forward"]){
73 last.kwArgs.forward();
74 }else if(last.kwArgs["forwardButton"]){
75 last.kwArgs.forwardButton();
76 }else if(last.kwArgs["handle"]){
77 last.kwArgs.handle("forward");
78 }
79 historyStack.push(last);
80 }
81
82 back.goForward = handleForwardButton;
83
84 function createState(url, args, hash){
85 //summary: private method. Do not call this directly.
86 return {"url": url, "kwArgs": args, "urlHash": hash}; //Object
87 }
88
89 function getUrlQuery(url){
90 //summary: private method. Do not call this directly.
91 var segments = url.split("?");
92 if(segments.length < 2){
93 return null; //null
94 }
95 else{
96 return segments[1]; //String
97 }
98 }
99
100 function loadIframeHistory(){
101 //summary: private method. Do not call this directly.
102 var url = (dojo.config["dojoIframeHistoryUrl"] || require.toUrl("./resources/iframe_history.html")) + "?" + (new Date()).getTime();
103 moveForward = true;
104 if(historyIframe){
105 sniff("webkit") ? historyIframe.location = url : window.frames[historyIframe.name].location = url;
106 }else{
107 //console.warn("dojo.back: Not initialised. You need to call dojo.back.init() from a <script> block that lives inside the <body> tag.");
108 }
109 return url; //String
110 }
111
112 function checkLocation(){
113 if(!changingUrl){
114 var hsl = historyStack.length;
115
116 var hash = getHash();
117
118 if((hash === initialHash||window.location.href == initialHref)&&(hsl == 1)){
119 // FIXME: could this ever be a forward button?
120 // we can't clear it because we still need to check for forwards. Ugg.
121 // clearInterval(this.locationTimer);
122 handleBackButton();
123 return;
124 }
125
126 // first check to see if we could have gone forward. We always halt on
127 // a no-hash item.
128 if(forwardStack.length > 0){
129 if(forwardStack[forwardStack.length-1].urlHash === hash){
130 handleForwardButton();
131 return;
132 }
133 }
134
135 // ok, that didn't work, try someplace back in the history stack
136 if((hsl >= 2)&&(historyStack[hsl-2])){
137 if(historyStack[hsl-2].urlHash === hash){
138 handleBackButton();
139 }
140 }
141 }
142 }
143
144 back.init = function(){
145 //summary: Initializes the undo stack. This must be called from a <script>
146 // block that lives inside the <body> tag to prevent bugs on IE.
147 // description:
148 // Only call this method before the page's DOM is finished loading. Otherwise
149 // it will not work. Be careful with xdomain loading or djConfig.debugAtAllCosts scenarios,
150 // in order for this method to work, dojo.back will need to be part of a build layer.
151
152 // prevent reinit
153 if(dom.byId("dj_history")){ return; }
154
155 var src = dojo.config["dojoIframeHistoryUrl"] || require.toUrl("./resources/iframe_history.html");
156 if (dojo._postLoad) {
157 console.error("dojo.back.init() must be called before the DOM has loaded. "
158 + "If using xdomain loading or djConfig.debugAtAllCosts, include dojo.back "
159 + "in a build layer.");
160 } else {
161 document.write('<iframe style="border:0;width:1px;height:1px;position:absolute;visibility:hidden;bottom:0;right:0;" name="dj_history" id="dj_history" src="' + src + '"></iframe>');
162 }
163 };
164
165 back.setInitialState = function(/*Object*/args){
166 //summary:
167 // Sets the state object and back callback for the very first page
168 // that is loaded.
169 //description:
170 // It is recommended that you call this method as part of an event
171 // listener that is registered via dojo.addOnLoad().
172 //args: Object
173 // See the addToHistory() function for the list of valid args properties.
174 initialState = createState(initialHref, args, initialHash);
175 };
176
177 //FIXME: Make these doc comments not be awful. At least they're not wrong.
178 //FIXME: Would like to support arbitrary back/forward jumps. Have to rework iframeLoaded among other things.
179 //FIXME: is there a slight race condition in moz using change URL with the timer check and when
180 // the hash gets set? I think I have seen a back/forward call in quick succession, but not consistent.
181
182
183 /*=====
184 dojo.__backArgs = function(kwArgs){
185 // back: Function?
186 // A function to be called when this state is reached via the user
187 // clicking the back button.
188 // forward: Function?
189 // Upon return to this state from the "back, forward" combination
190 // of navigation steps, this function will be called. Somewhat
191 // analgous to the semantic of an "onRedo" event handler.
192 // changeUrl: Boolean?|String?
193 // Boolean indicating whether or not to create a unique hash for
194 // this state. If a string is passed instead, it is used as the
195 // hash.
196 }
197 =====*/
198
199 back.addToHistory = function(/*dojo.__backArgs*/ args){
200 // summary:
201 // adds a state object (args) to the history list.
202 // args: dojo.__backArgs
203 // The state object that will be added to the history list.
204 // description:
205 // To support getting back button notifications, the object
206 // argument should implement a function called either "back",
207 // "backButton", or "handle". The string "back" will be passed as
208 // the first and only argument to this callback.
209 //
210 // To support getting forward button notifications, the object
211 // argument should implement a function called either "forward",
212 // "forwardButton", or "handle". The string "forward" will be
213 // passed as the first and only argument to this callback.
214 //
215 // If you want the browser location string to change, define "changeUrl" on the object. If the
216 // value of "changeUrl" is true, then a unique number will be appended to the URL as a fragment
217 // identifier (http://some.domain.com/path#uniquenumber). If it is any other value that does
218 // not evaluate to false, that value will be used as the fragment identifier. For example,
219 // if changeUrl: 'page1', then the URL will look like: http://some.domain.com/path#page1
220 //
221 // There are problems with using dojo.back with semantically-named fragment identifiers
222 // ("hash values" on an URL). In most browsers it will be hard for dojo.back to know
223 // distinguish a back from a forward event in those cases. For back/forward support to
224 // work best, the fragment ID should always be a unique value (something using new Date().getTime()
225 // for example). If you want to detect hash changes using semantic fragment IDs, then
226 // consider using dojo.hash instead (in Dojo 1.4+).
227 //
228 // example:
229 // | dojo.back.addToHistory({
230 // | back: function(){ console.log('back pressed'); },
231 // | forward: function(){ console.log('forward pressed'); },
232 // | changeUrl: true
233 // | });
234
235 // BROWSER NOTES:
236 // Safari 1.2:
237 // back button "works" fine, however it's not possible to actually
238 // DETECT that you've moved backwards by inspecting window.location.
239 // Unless there is some other means of locating.
240 // FIXME: perhaps we can poll on history.length?
241 // Safari 2.0.3+ (and probably 1.3.2+):
242 // works fine, except when changeUrl is used. When changeUrl is used,
243 // Safari jumps all the way back to whatever page was shown before
244 // the page that uses dojo.undo.browser support.
245 // IE 5.5 SP2:
246 // back button behavior is macro. It does not move back to the
247 // previous hash value, but to the last full page load. This suggests
248 // that the iframe is the correct way to capture the back button in
249 // these cases.
250 // Don't test this page using local disk for MSIE. MSIE will not create
251 // a history list for iframe_history.html if served from a file: URL.
252 // The XML served back from the XHR tests will also not be properly
253 // created if served from local disk. Serve the test pages from a web
254 // server to test in that browser.
255 // IE 6.0:
256 // same behavior as IE 5.5 SP2
257 // Firefox 1.0+:
258 // the back button will return us to the previous hash on the same
259 // page, thereby not requiring an iframe hack, although we do then
260 // need to run a timer to detect inter-page movement.
261
262 //If addToHistory is called, then that means we prune the
263 //forward stack -- the user went back, then wanted to
264 //start a new forward path.
265 forwardStack = [];
266
267 var hash = null;
268 var url = null;
269 if(!historyIframe){
270 if(dojo.config["useXDomain"] && !dojo.config["dojoIframeHistoryUrl"]){
271 console.warn("dojo.back: When using cross-domain Dojo builds,"
272 + " please save iframe_history.html to your domain and set djConfig.dojoIframeHistoryUrl"
273 + " to the path on your domain to iframe_history.html");
274 }
275 historyIframe = window.frames["dj_history"];
276 }
277 if(!bookmarkAnchor){
278 bookmarkAnchor = domConstruct.create("a", {style: {display: "none"}}, baseWindow.body());
279 }
280 if(args["changeUrl"]){
281 hash = ""+ ((args["changeUrl"]!==true) ? args["changeUrl"] : (new Date()).getTime());
282
283 //If the current hash matches the new one, just replace the history object with
284 //this new one. It doesn't make sense to track different state objects for the same
285 //logical URL. This matches the browser behavior of only putting in one history
286 //item no matter how many times you click on the same #hash link, at least in Firefox
287 //and Safari, and there is no reliable way in those browsers to know if a #hash link
288 //has been clicked on multiple times. So making this the standard behavior in all browsers
289 //so that dojo.back's behavior is the same in all browsers.
290 if(historyStack.length == 0 && initialState.urlHash == hash){
291 initialState = createState(url, args, hash);
292 return;
293 }else if(historyStack.length > 0 && historyStack[historyStack.length - 1].urlHash == hash){
294 historyStack[historyStack.length - 1] = createState(url, args, hash);
295 return;
296 }
297
298 changingUrl = true;
299 setTimeout(function() {
300 setHash(hash);
301 changingUrl = false;
302 }, 1);
303 bookmarkAnchor.href = hash;
304
305 if(sniff("ie")){
306 url = loadIframeHistory();
307
308 var oldCB = args["back"]||args["backButton"]||args["handle"];
309
310 //The function takes handleName as a parameter, in case the
311 //callback we are overriding was "handle". In that case,
312 //we will need to pass the handle name to handle.
313 var tcb = function(handleName){
314 if(getHash() != ""){
315 setTimeout(function() { setHash(hash); }, 1);
316 }
317 //Use apply to set "this" to args, and to try to avoid memory leaks.
318 oldCB.apply(this, [handleName]);
319 };
320
321 //Set interceptor function in the right place.
322 if(args["back"]){
323 args.back = tcb;
324 }else if(args["backButton"]){
325 args.backButton = tcb;
326 }else if(args["handle"]){
327 args.handle = tcb;
328 }
329
330 var oldFW = args["forward"]||args["forwardButton"]||args["handle"];
331
332 //The function takes handleName as a parameter, in case the
333 //callback we are overriding was "handle". In that case,
334 //we will need to pass the handle name to handle.
335 var tfw = function(handleName){
336 if(getHash() != ""){
337 setHash(hash);
338 }
339 if(oldFW){ // we might not actually have one
340 //Use apply to set "this" to args, and to try to avoid memory leaks.
341 oldFW.apply(this, [handleName]);
342 }
343 };
344
345 //Set interceptor function in the right place.
346 if(args["forward"]){
347 args.forward = tfw;
348 }else if(args["forwardButton"]){
349 args.forwardButton = tfw;
350 }else if(args["handle"]){
351 args.handle = tfw;
352 }
353
354 }else if(!sniff("ie")){
355 // start the timer
356 if(!locationTimer){
357 locationTimer = setInterval(checkLocation, 200);
358 }
359
360 }
361 }else{
362 url = loadIframeHistory();
363 }
364
365 historyStack.push(createState(url, args, hash));
366 };
367
368 back._iframeLoaded = function(evt, ifrLoc){
369 //summary:
370 // private method. Do not call this directly.
371 var query = getUrlQuery(ifrLoc.href);
372 if(query == null){
373 // alert("iframeLoaded");
374 // we hit the end of the history, so we should go back
375 if(historyStack.length == 1){
376 handleBackButton();
377 }
378 return;
379 }
380 if(moveForward){
381 // we were expecting it, so it's not either a forward or backward movement
382 moveForward = false;
383 return;
384 }
385
386 //Check the back stack first, since it is more likely.
387 //Note that only one step back or forward is supported.
388 if(historyStack.length >= 2 && query == getUrlQuery(historyStack[historyStack.length-2].url)){
389 handleBackButton();
390 }else if(forwardStack.length > 0 && query == getUrlQuery(forwardStack[forwardStack.length-1].url)){
391 handleForwardButton();
392 }
393 };
394
395 return dojo.back;
396
397});