Subversion Repositories Applications.papyrus

Rev

Details | Last modification | View Log | RSS feed

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