]>
git.wh0rd.org - tt-rss.git/blob - lib/dojo/back.js.uncompressed.js
1 define("dojo/back", ["./_base/config", "./_base/lang", "./sniff", "./dom", "./dom-construct", "./_base/window", "require"],
2 function(config
, lang
, has
, dom
, domConstruct
, baseWindow
, require
){
8 // Browser history management resources
10 1 && lang
.setObject("dojo.back", back
);
12 // everyone deals with encoding the hash slightly differently
14 var getHash
= back
.getHash = function(){
15 var h
= window
.location
.hash
;
16 if(h
.charAt(0) == "#"){ h
= h
.substring(1); }
17 return has("mozilla") ? h
: decodeURIComponent(h
);
20 setHash
= back
.setHash = function(h
){
22 window
.location
.hash
= encodeURIComponent(h
);
23 historyCounter
= history
.length
;
26 var initialHref
= (typeof(window
) !== "undefined") ? window
.location
.href
: "";
27 var initialHash
= (typeof(window
) !== "undefined") ? getHash() : "";
28 var initialState
= null;
30 var locationTimer
= null;
31 var bookmarkAnchor
= null;
32 var historyIframe
= null;
33 var forwardStack
= [];
34 var historyStack
= [];
35 var moveForward
= false;
36 var changingUrl
= false;
39 function handleBackButton(){
41 // private method. Do not call this directly.
43 //The "current" page is always at the top of the history stack.
44 var current
= historyStack
.pop();
45 if(!current
){ return; }
46 var last
= historyStack
[historyStack
.length
-1];
47 if(!last
&& historyStack
.length
== 0){
51 if(last
.kwArgs
["back"]){
52 last
.kwArgs
["back"]();
53 }else if(last
.kwArgs
["backButton"]){
54 last
.kwArgs
["backButton"]();
55 }else if(last
.kwArgs
["handle"]){
56 last
.kwArgs
.handle("back");
59 forwardStack
.push(current
);
62 back
.goBack
= handleBackButton
;
64 function handleForwardButton(){
66 // private method. Do not call this directly.
67 var last
= forwardStack
.pop();
69 if(last
.kwArgs
["forward"]){
70 last
.kwArgs
.forward();
71 }else if(last
.kwArgs
["forwardButton"]){
72 last
.kwArgs
.forwardButton();
73 }else if(last
.kwArgs
["handle"]){
74 last
.kwArgs
.handle("forward");
76 historyStack
.push(last
);
79 back
.goForward
= handleForwardButton
;
81 function createState(url
, args
, hash
){
83 // private method. Do not call this directly.
84 return {"url": url
, "kwArgs": args
, "urlHash": hash
}; //Object
87 function getUrlQuery(url
){
89 // private method. Do not call this directly.
90 var segments
= url
.split("?");
91 if(segments
.length
< 2){
95 return segments
[1]; //String
99 function loadIframeHistory(){
101 // private method. Do not call this directly.
102 var url
= (config
["dojoIframeHistoryUrl"] || require
.toUrl("./resources/iframe_history.html")) + "?" + (new Date()).getTime();
105 has("webkit") ? historyIframe
.location
= url
: window
.frames
[historyIframe
.name
].location
= url
;
107 //console.warn("dojo/back: Not initialised. You need to call back.init() from a <script> block that lives inside the <body> tag.");
112 function checkLocation(){
114 var hsl
= historyStack
.length
;
116 var hash
= getHash();
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);
126 // first check to see if we could have gone forward. We always halt on
128 if(forwardStack
.length
> 0){
129 if(forwardStack
[forwardStack
.length
-1].urlHash
=== hash
){
130 handleForwardButton();
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
){
144 back
.init = function(){
146 // Initializes the undo stack. This must be called from a <script>
147 // block that lives inside the `<body>` tag to prevent bugs on IE.
149 // Only call this method before the page's DOM is finished loading. Otherwise
150 // it will not work. Be careful with xdomain loading or djConfig.debugAtAllCosts scenarios,
151 // in order for this method to work, dojo/back will need to be part of a build layer.
154 if(dom
.byId("dj_history")){ return; }
156 var src
= config
["dojoIframeHistoryUrl"] || require
.toUrl("./resources/iframe_history.html");
157 if (config
.afterOnLoad
){
158 console
.error("dojo/back::init() must be called before the DOM has loaded. "
159 + "Include dojo/back in a build layer.");
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>');
165 back
.setInitialState = function(/*Object*/args
){
167 // Sets the state object and back callback for the very first page
170 // It is recommended that you call this method as part of an event
171 // listener that is registered via dojo/ready.
173 // See the addToHistory() function for the list of valid args properties.
174 initialState
= createState(initialHref
, args
, initialHash
);
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.
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 // analogous 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
199 back
.addToHistory = function(args
){
201 // adds a state object (args) to the history list.
203 // The state object that will be added to the history list.
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.
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.
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
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+).
229 // | back.addToHistory({
230 // | back: function(){ console.log('back pressed'); },
231 // | forward: function(){ console.log('forward pressed'); },
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.
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
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.
256 // same behavior as IE 5.5 SP2
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.
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.
270 if(config
["useXDomain"] && !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");
275 historyIframe
= window
.frames
["dj_history"];
278 bookmarkAnchor
= domConstruct
.create("a", {style
: {display
: "none"}}, baseWindow
.body());
280 if(args
["changeUrl"]){
281 hash
= ""+ ((args
["changeUrl"]!==true) ? args
["changeUrl"] : (new Date()).getTime());
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
);
293 }else if(historyStack
.length
> 0 && historyStack
[historyStack
.length
- 1].urlHash
== hash
){
294 historyStack
[historyStack
.length
- 1] = createState(url
, args
, hash
);
299 setTimeout(function(){
303 bookmarkAnchor
.href
= hash
;
306 url
= loadIframeHistory();
308 var oldCB
= args
["back"]||args
["backButton"]||args
["handle"];
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
){
315 setTimeout(function(){ setHash(hash
); }, 1);
317 //Use apply to set "this" to args, and to try to avoid memory leaks.
318 oldCB
.apply(this, [handleName
]);
321 //Set interceptor function in the right place.
324 }else if(args
["backButton"]){
325 args
.backButton
= tcb
;
326 }else if(args
["handle"]){
330 var oldFW
= args
["forward"]||args
["forwardButton"]||args
["handle"];
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
){
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
]);
345 //Set interceptor function in the right place.
348 }else if(args
["forwardButton"]){
349 args
.forwardButton
= tfw
;
350 }else if(args
["handle"]){
354 }else if(!has("ie")){
357 locationTimer
= setInterval(checkLocation
, 200);
362 url
= loadIframeHistory();
365 historyStack
.push(createState(url
, args
, hash
));
368 back
._iframeLoaded = function(evt
, ifrLoc
){
370 // private method. Do not call this directly.
371 var query
= getUrlQuery(ifrLoc
.href
);
373 // alert("iframeLoaded");
374 // we hit the end of the history, so we should go back
375 if(historyStack
.length
== 1){
381 // we were expecting it, so it's not either a forward or backward movement
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
)){
390 }else if(forwardStack
.length
> 0 && query
== getUrlQuery(forwardStack
[forwardStack
.length
-1].url
)){
391 handleForwardButton();