1 |
efrain |
1 |
YUI.add('yui2-history', function(Y) {
|
|
|
2 |
var YAHOO = Y.YUI2;
|
|
|
3 |
/*
|
|
|
4 |
Copyright (c) 2011, Yahoo! Inc. All rights reserved.
|
|
|
5 |
Code licensed under the BSD License:
|
|
|
6 |
http://developer.yahoo.com/yui/license.html
|
|
|
7 |
version: 2.9.0
|
|
|
8 |
*/
|
|
|
9 |
/**
|
|
|
10 |
* The Browser History Manager provides the ability to use the back/forward
|
|
|
11 |
* navigation buttons in a DHTML application. It also allows a DHTML
|
|
|
12 |
* application to be bookmarked in a specific state.
|
|
|
13 |
*
|
|
|
14 |
* This library requires the following static markup:
|
|
|
15 |
*
|
|
|
16 |
* <iframe id="yui-history-iframe" src="path-to-real-asset-in-same-domain"></iframe>
|
|
|
17 |
* <input id="yui-history-field" type="hidden">
|
|
|
18 |
*
|
|
|
19 |
* @module history
|
|
|
20 |
* @requires yahoo,event
|
|
|
21 |
* @namespace YAHOO.util
|
|
|
22 |
* @title Browser History Manager
|
|
|
23 |
*/
|
|
|
24 |
|
|
|
25 |
/**
|
|
|
26 |
* The History class provides the ability to use the back/forward navigation
|
|
|
27 |
* buttons in a DHTML application. It also allows a DHTML application to
|
|
|
28 |
* be bookmarked in a specific state.
|
|
|
29 |
*
|
|
|
30 |
* @class History
|
|
|
31 |
* @constructor
|
|
|
32 |
*/
|
|
|
33 |
YAHOO.util.History = (function () {
|
|
|
34 |
|
|
|
35 |
/**
|
|
|
36 |
* Our hidden IFrame used to store the browsing history.
|
|
|
37 |
*
|
|
|
38 |
* @property _histFrame
|
|
|
39 |
* @type HTMLIFrameElement
|
|
|
40 |
* @default null
|
|
|
41 |
* @private
|
|
|
42 |
*/
|
|
|
43 |
var _histFrame = null;
|
|
|
44 |
|
|
|
45 |
/**
|
|
|
46 |
* INPUT field (with type="hidden" or type="text") or TEXTAREA.
|
|
|
47 |
* This field keeps the value of the initial state, current state
|
|
|
48 |
* the list of all states across pages within a single browser session.
|
|
|
49 |
*
|
|
|
50 |
* @property _stateField
|
|
|
51 |
* @type HTMLInputElement|HTMLTextAreaElement
|
|
|
52 |
* @default null
|
|
|
53 |
* @private
|
|
|
54 |
*/
|
|
|
55 |
var _stateField = null;
|
|
|
56 |
|
|
|
57 |
/**
|
|
|
58 |
* Flag used to tell whether YAHOO.util.History.initialize has been called.
|
|
|
59 |
*
|
|
|
60 |
* @property _initialized
|
|
|
61 |
* @type boolean
|
|
|
62 |
* @default false
|
|
|
63 |
* @private
|
|
|
64 |
*/
|
|
|
65 |
var _initialized = false;
|
|
|
66 |
|
|
|
67 |
/**
|
|
|
68 |
* List of registered modules.
|
|
|
69 |
*
|
|
|
70 |
* @property _modules
|
|
|
71 |
* @type array
|
|
|
72 |
* @default []
|
|
|
73 |
* @private
|
|
|
74 |
*/
|
|
|
75 |
var _modules = [];
|
|
|
76 |
|
|
|
77 |
/**
|
|
|
78 |
* List of fully qualified states. This is used only by Safari.
|
|
|
79 |
*
|
|
|
80 |
* @property _fqstates
|
|
|
81 |
* @type array
|
|
|
82 |
* @default []
|
|
|
83 |
* @private
|
|
|
84 |
*/
|
|
|
85 |
var _fqstates = [];
|
|
|
86 |
|
|
|
87 |
/**
|
|
|
88 |
* location.hash is a bit buggy on Opera. I have seen instances where
|
|
|
89 |
* navigating the history using the back/forward buttons, and hence
|
|
|
90 |
* changing the URL, would not change location.hash. That's ok, the
|
|
|
91 |
* implementation of an equivalent is trivial.
|
|
|
92 |
*
|
|
|
93 |
* @method _getHash
|
|
|
94 |
* @return {string} The hash portion of the document's location
|
|
|
95 |
* @private
|
|
|
96 |
*/
|
|
|
97 |
function _getHash() {
|
|
|
98 |
var i, href;
|
|
|
99 |
href = self.location.href;
|
|
|
100 |
i = href.indexOf("#");
|
|
|
101 |
return i >= 0 ? href.substr(i + 1) : null;
|
|
|
102 |
}
|
|
|
103 |
|
|
|
104 |
/**
|
|
|
105 |
* Stores all the registered modules' initial state and current state.
|
|
|
106 |
* On Safari, we also store all the fully qualified states visited by
|
|
|
107 |
* the application within a single browser session. The storage takes
|
|
|
108 |
* place in the form field specified during initialization.
|
|
|
109 |
*
|
|
|
110 |
* @method _storeStates
|
|
|
111 |
* @private
|
|
|
112 |
*/
|
|
|
113 |
function _storeStates() {
|
|
|
114 |
|
|
|
115 |
var moduleName, moduleObj, initialStates = [], currentStates = [];
|
|
|
116 |
|
|
|
117 |
for (moduleName in _modules) {
|
|
|
118 |
if (YAHOO.lang.hasOwnProperty(_modules, moduleName)) {
|
|
|
119 |
moduleObj = _modules[moduleName];
|
|
|
120 |
initialStates.push(moduleName + "=" + moduleObj.initialState);
|
|
|
121 |
currentStates.push(moduleName + "=" + moduleObj.currentState);
|
|
|
122 |
}
|
|
|
123 |
}
|
|
|
124 |
|
|
|
125 |
_stateField.value = initialStates.join("&") + "|" + currentStates.join("&");
|
|
|
126 |
}
|
|
|
127 |
|
|
|
128 |
/**
|
|
|
129 |
* Sets the new currentState attribute of all modules depending on the new
|
|
|
130 |
* fully qualified state. Also notifies the modules which current state has
|
|
|
131 |
* changed.
|
|
|
132 |
*
|
|
|
133 |
* @method _handleFQStateChange
|
|
|
134 |
* @param {string} fqstate Fully qualified state
|
|
|
135 |
* @private
|
|
|
136 |
*/
|
|
|
137 |
function _handleFQStateChange(fqstate) {
|
|
|
138 |
|
|
|
139 |
var i, len, moduleName, moduleObj, modules, states, tokens, currentState;
|
|
|
140 |
|
|
|
141 |
if (!fqstate) {
|
|
|
142 |
// Notifies all modules
|
|
|
143 |
for (moduleName in _modules) {
|
|
|
144 |
if (YAHOO.lang.hasOwnProperty(_modules, moduleName)) {
|
|
|
145 |
moduleObj = _modules[moduleName];
|
|
|
146 |
moduleObj.currentState = moduleObj.initialState;
|
|
|
147 |
moduleObj.onStateChange(_decode(moduleObj.currentState));
|
|
|
148 |
}
|
|
|
149 |
}
|
|
|
150 |
return;
|
|
|
151 |
}
|
|
|
152 |
|
|
|
153 |
modules = [];
|
|
|
154 |
states = fqstate.split("&");
|
|
|
155 |
for (i = 0, len = states.length; i < len; i++) {
|
|
|
156 |
tokens = states[i].split("=");
|
|
|
157 |
if (tokens.length === 2) {
|
|
|
158 |
moduleName = tokens[0];
|
|
|
159 |
currentState = tokens[1];
|
|
|
160 |
modules[moduleName] = currentState;
|
|
|
161 |
}
|
|
|
162 |
}
|
|
|
163 |
|
|
|
164 |
for (moduleName in _modules) {
|
|
|
165 |
if (YAHOO.lang.hasOwnProperty(_modules, moduleName)) {
|
|
|
166 |
moduleObj = _modules[moduleName];
|
|
|
167 |
currentState = modules[moduleName];
|
|
|
168 |
if (!currentState || moduleObj.currentState !== currentState) {
|
|
|
169 |
moduleObj.currentState = typeof currentState === 'undefined' ? moduleObj.initialState : currentState;
|
|
|
170 |
moduleObj.onStateChange(_decode(moduleObj.currentState));
|
|
|
171 |
}
|
|
|
172 |
}
|
|
|
173 |
}
|
|
|
174 |
}
|
|
|
175 |
|
|
|
176 |
/**
|
|
|
177 |
* Update the IFrame with our new state.
|
|
|
178 |
*
|
|
|
179 |
* @method _updateIFrame
|
|
|
180 |
* @private
|
|
|
181 |
* @return {boolean} true if successful. false otherwise.
|
|
|
182 |
*/
|
|
|
183 |
function _updateIFrame (fqstate) {
|
|
|
184 |
|
|
|
185 |
var html, doc;
|
|
|
186 |
|
|
|
187 |
html = '<html><body><div id="state">' +
|
|
|
188 |
YAHOO.lang.escapeHTML(fqstate) +
|
|
|
189 |
'</div></body></html>';
|
|
|
190 |
|
|
|
191 |
try {
|
|
|
192 |
doc = _histFrame.contentWindow.document;
|
|
|
193 |
doc.open();
|
|
|
194 |
doc.write(html);
|
|
|
195 |
doc.close();
|
|
|
196 |
return true;
|
|
|
197 |
} catch (e) {
|
|
|
198 |
return false;
|
|
|
199 |
}
|
|
|
200 |
}
|
|
|
201 |
|
|
|
202 |
/**
|
|
|
203 |
* Periodically checks whether our internal IFrame is ready to be used.
|
|
|
204 |
*
|
|
|
205 |
* @method _checkIframeLoaded
|
|
|
206 |
* @private
|
|
|
207 |
*/
|
|
|
208 |
function _checkIframeLoaded() {
|
|
|
209 |
|
|
|
210 |
var doc, elem, fqstate, hash;
|
|
|
211 |
|
|
|
212 |
if (!_histFrame.contentWindow || !_histFrame.contentWindow.document) {
|
|
|
213 |
// Check again in 10 msec...
|
|
|
214 |
setTimeout(_checkIframeLoaded, 10);
|
|
|
215 |
return;
|
|
|
216 |
}
|
|
|
217 |
|
|
|
218 |
// Start the thread that will have the responsibility to
|
|
|
219 |
// periodically check whether a navigate operation has been
|
|
|
220 |
// requested on the main window. This will happen when
|
|
|
221 |
// YAHOO.util.History.navigate has been called or after
|
|
|
222 |
// the user has hit the back/forward button.
|
|
|
223 |
|
|
|
224 |
doc = _histFrame.contentWindow.document;
|
|
|
225 |
elem = doc.getElementById("state");
|
|
|
226 |
// We must use innerText, and not innerHTML because our string contains
|
|
|
227 |
// the "&" character (which would end up being escaped as "&") and
|
|
|
228 |
// the string comparison would fail...
|
|
|
229 |
fqstate = elem ? elem.innerText : null;
|
|
|
230 |
|
|
|
231 |
hash = _getHash();
|
|
|
232 |
|
|
|
233 |
setInterval(function () {
|
|
|
234 |
|
|
|
235 |
var newfqstate, states, moduleName, moduleObj, newHash, historyLength;
|
|
|
236 |
|
|
|
237 |
doc = _histFrame.contentWindow.document;
|
|
|
238 |
elem = doc.getElementById("state");
|
|
|
239 |
// See my comment above about using innerText instead of innerHTML...
|
|
|
240 |
newfqstate = elem ? elem.innerText : null;
|
|
|
241 |
|
|
|
242 |
newHash = _getHash();
|
|
|
243 |
|
|
|
244 |
if (newfqstate !== fqstate) {
|
|
|
245 |
|
|
|
246 |
fqstate = newfqstate;
|
|
|
247 |
_handleFQStateChange(fqstate);
|
|
|
248 |
|
|
|
249 |
if (!fqstate) {
|
|
|
250 |
states = [];
|
|
|
251 |
for (moduleName in _modules) {
|
|
|
252 |
if (YAHOO.lang.hasOwnProperty(_modules, moduleName)) {
|
|
|
253 |
moduleObj = _modules[moduleName];
|
|
|
254 |
states.push(moduleName + "=" + moduleObj.initialState);
|
|
|
255 |
}
|
|
|
256 |
}
|
|
|
257 |
newHash = states.join("&");
|
|
|
258 |
} else {
|
|
|
259 |
newHash = fqstate;
|
|
|
260 |
}
|
|
|
261 |
|
|
|
262 |
// Allow the state to be bookmarked by setting the top window's
|
|
|
263 |
// URL fragment identifier. Note that here, we are on IE, and
|
|
|
264 |
// IE does not touch the browser history when setting the hash
|
|
|
265 |
// (unlike all the other browsers). I used to write:
|
|
|
266 |
// self.location.replace( "#" + hash );
|
|
|
267 |
// but this had a side effect when the page was not the top frame.
|
|
|
268 |
self.location.hash = newHash;
|
|
|
269 |
hash = newHash;
|
|
|
270 |
|
|
|
271 |
_storeStates();
|
|
|
272 |
|
|
|
273 |
} else if (newHash !== hash) {
|
|
|
274 |
|
|
|
275 |
// The hash has changed. The user might have clicked on a link,
|
|
|
276 |
// or modified the URL directly, or opened the same application
|
|
|
277 |
// bookmarked in a specific state using a bookmark. However, we
|
|
|
278 |
// know the hash change was not caused by a hit on the back or
|
|
|
279 |
// forward buttons, or by a call to navigate() (because it would
|
|
|
280 |
// have been handled above) We must handle these cases, which is
|
|
|
281 |
// why we also need to keep track of hash changes on IE!
|
|
|
282 |
|
|
|
283 |
// Note that IE6 has some major issues with this kind of user
|
|
|
284 |
// interaction (the history stack gets completely messed up)
|
|
|
285 |
// but it seems to work fine on IE7.
|
|
|
286 |
|
|
|
287 |
hash = newHash;
|
|
|
288 |
|
|
|
289 |
// Now, store a new history entry. The following will cause the
|
|
|
290 |
// code above to execute, doing all the dirty work for us...
|
|
|
291 |
_updateIFrame(newHash);
|
|
|
292 |
}
|
|
|
293 |
|
|
|
294 |
}, 50);
|
|
|
295 |
|
|
|
296 |
_initialized = true;
|
|
|
297 |
YAHOO.util.History.onLoadEvent.fire();
|
|
|
298 |
}
|
|
|
299 |
|
|
|
300 |
/**
|
|
|
301 |
* Finish up the initialization of the Browser History Manager.
|
|
|
302 |
*
|
|
|
303 |
* @method _initialize
|
|
|
304 |
* @private
|
|
|
305 |
*/
|
|
|
306 |
function _initialize() {
|
|
|
307 |
|
|
|
308 |
var i, len, parts, tokens, moduleName, moduleObj, initialStates, initialState, currentStates, currentState, counter, hash;
|
|
|
309 |
|
|
|
310 |
// Decode the content of our storage field...
|
|
|
311 |
parts = _stateField.value.split("|");
|
|
|
312 |
|
|
|
313 |
if (parts.length > 1) {
|
|
|
314 |
|
|
|
315 |
initialStates = parts[0].split("&");
|
|
|
316 |
for (i = 0, len = initialStates.length; i < len; i++) {
|
|
|
317 |
tokens = initialStates[i].split("=");
|
|
|
318 |
if (tokens.length === 2) {
|
|
|
319 |
moduleName = tokens[0];
|
|
|
320 |
initialState = tokens[1];
|
|
|
321 |
|
|
|
322 |
moduleObj = YAHOO.lang.hasOwnProperty(_modules, moduleName)
|
|
|
323 |
&& _modules[moduleName];
|
|
|
324 |
|
|
|
325 |
if (moduleObj) {
|
|
|
326 |
moduleObj.initialState = initialState;
|
|
|
327 |
}
|
|
|
328 |
}
|
|
|
329 |
}
|
|
|
330 |
|
|
|
331 |
currentStates = parts[1].split("&");
|
|
|
332 |
for (i = 0, len = currentStates.length; i < len; i++) {
|
|
|
333 |
tokens = currentStates[i].split("=");
|
|
|
334 |
if (tokens.length >= 2) {
|
|
|
335 |
moduleName = tokens[0];
|
|
|
336 |
currentState = tokens[1];
|
|
|
337 |
|
|
|
338 |
moduleObj = YAHOO.lang.hasOwnProperty(_modules, moduleName)
|
|
|
339 |
&& _modules[moduleName];
|
|
|
340 |
|
|
|
341 |
if (moduleObj) {
|
|
|
342 |
moduleObj.currentState = currentState;
|
|
|
343 |
}
|
|
|
344 |
}
|
|
|
345 |
}
|
|
|
346 |
}
|
|
|
347 |
|
|
|
348 |
if (parts.length > 2) {
|
|
|
349 |
_fqstates = parts[2].split(",");
|
|
|
350 |
}
|
|
|
351 |
|
|
|
352 |
if (YAHOO.env.ua.ie) {
|
|
|
353 |
|
|
|
354 |
if (typeof document.documentMode === "undefined" || document.documentMode < 8) {
|
|
|
355 |
|
|
|
356 |
// IE < 8 or IE8 in quirks mode or IE7 standards mode
|
|
|
357 |
_checkIframeLoaded();
|
|
|
358 |
|
|
|
359 |
} else {
|
|
|
360 |
|
|
|
361 |
// IE8 in IE8 standards mode
|
|
|
362 |
YAHOO.util.Event.on(top, "hashchange",
|
|
|
363 |
function () {
|
|
|
364 |
var hash = _getHash();
|
|
|
365 |
_handleFQStateChange(hash);
|
|
|
366 |
_storeStates();
|
|
|
367 |
});
|
|
|
368 |
|
|
|
369 |
_initialized = true;
|
|
|
370 |
YAHOO.util.History.onLoadEvent.fire();
|
|
|
371 |
|
|
|
372 |
}
|
|
|
373 |
|
|
|
374 |
} else {
|
|
|
375 |
|
|
|
376 |
// Start the thread that will have the responsibility to
|
|
|
377 |
// periodically check whether a navigate operation has been
|
|
|
378 |
// requested on the main window. This will happen when
|
|
|
379 |
// YAHOO.util.History.navigate has been called or after
|
|
|
380 |
// the user has hit the back/forward button.
|
|
|
381 |
|
|
|
382 |
// On Gecko and Opera, we just need to watch the hash...
|
|
|
383 |
hash = _getHash();
|
|
|
384 |
|
|
|
385 |
setInterval(function () {
|
|
|
386 |
|
|
|
387 |
var state, newHash, newCounter;
|
|
|
388 |
|
|
|
389 |
newHash = _getHash();
|
|
|
390 |
if (newHash !== hash) {
|
|
|
391 |
hash = newHash;
|
|
|
392 |
_handleFQStateChange(hash);
|
|
|
393 |
_storeStates();
|
|
|
394 |
}
|
|
|
395 |
|
|
|
396 |
}, 50);
|
|
|
397 |
|
|
|
398 |
_initialized = true;
|
|
|
399 |
YAHOO.util.History.onLoadEvent.fire();
|
|
|
400 |
}
|
|
|
401 |
}
|
|
|
402 |
|
|
|
403 |
/**
|
|
|
404 |
* Wrapper around <code>decodeURIComponent()</code> that also converts +
|
|
|
405 |
* chars into spaces.
|
|
|
406 |
*
|
|
|
407 |
* @method _decode
|
|
|
408 |
* @param {String} string string to decode
|
|
|
409 |
* @return {String} decoded string
|
|
|
410 |
* @private
|
|
|
411 |
* @since 2.9.0
|
|
|
412 |
*/
|
|
|
413 |
function _decode(string) {
|
|
|
414 |
return decodeURIComponent(string.replace(/\+/g, ' '));
|
|
|
415 |
}
|
|
|
416 |
|
|
|
417 |
/**
|
|
|
418 |
* Wrapper around <code>encodeURIComponent()</code> that converts spaces to
|
|
|
419 |
* + chars.
|
|
|
420 |
*
|
|
|
421 |
* @method _encode
|
|
|
422 |
* @param {String} string string to encode
|
|
|
423 |
* @return {String} encoded string
|
|
|
424 |
* @private
|
|
|
425 |
* @since 2.9.0
|
|
|
426 |
*/
|
|
|
427 |
function _encode(string) {
|
|
|
428 |
return encodeURIComponent(string).replace(/%20/g, '+');
|
|
|
429 |
}
|
|
|
430 |
|
|
|
431 |
return {
|
|
|
432 |
|
|
|
433 |
/**
|
|
|
434 |
* Fired when the Browser History Manager is ready. If you subscribe to
|
|
|
435 |
* this event after the Browser History Manager has been initialized,
|
|
|
436 |
* it will not fire. Therefore, it is recommended to use the onReady
|
|
|
437 |
* method instead.
|
|
|
438 |
*
|
|
|
439 |
* @event onLoadEvent
|
|
|
440 |
* @see onReady
|
|
|
441 |
*/
|
|
|
442 |
onLoadEvent: new YAHOO.util.CustomEvent("onLoad"),
|
|
|
443 |
|
|
|
444 |
/**
|
|
|
445 |
* Executes the supplied callback when the Browser History Manager is
|
|
|
446 |
* ready. This will execute immediately if called after the Browser
|
|
|
447 |
* History Manager onLoad event has fired.
|
|
|
448 |
*
|
|
|
449 |
* @method onReady
|
|
|
450 |
* @param {function} fn what to execute when the Browser History Manager is ready.
|
|
|
451 |
* @param {object} obj an optional object to be passed back as a parameter to fn.
|
|
|
452 |
* @param {boolean|object} overrideContext If true, the obj passed in becomes fn's execution scope.
|
|
|
453 |
* @see onLoadEvent
|
|
|
454 |
*/
|
|
|
455 |
onReady: function (fn, obj, overrideContext) {
|
|
|
456 |
|
|
|
457 |
if (_initialized) {
|
|
|
458 |
|
|
|
459 |
setTimeout(function () {
|
|
|
460 |
var ctx = window;
|
|
|
461 |
if (overrideContext) {
|
|
|
462 |
if (overrideContext === true) {
|
|
|
463 |
ctx = obj;
|
|
|
464 |
} else {
|
|
|
465 |
ctx = overrideContext;
|
|
|
466 |
}
|
|
|
467 |
}
|
|
|
468 |
fn.call(ctx, "onLoad", [], obj);
|
|
|
469 |
}, 0);
|
|
|
470 |
|
|
|
471 |
} else {
|
|
|
472 |
|
|
|
473 |
YAHOO.util.History.onLoadEvent.subscribe(fn, obj, overrideContext);
|
|
|
474 |
|
|
|
475 |
}
|
|
|
476 |
},
|
|
|
477 |
|
|
|
478 |
/**
|
|
|
479 |
* Registers a new module.
|
|
|
480 |
*
|
|
|
481 |
* @method register
|
|
|
482 |
* @param {string} module Non-empty string uniquely identifying the
|
|
|
483 |
* module you wish to register.
|
|
|
484 |
* @param {string} initialState The initial state of the specified
|
|
|
485 |
* module corresponding to its earliest history entry.
|
|
|
486 |
* @param {function} onStateChange Callback called when the
|
|
|
487 |
* state of the specified module has changed.
|
|
|
488 |
* @param {object} obj An arbitrary object that will be passed as a
|
|
|
489 |
* parameter to the handler.
|
|
|
490 |
* @param {boolean} overrideContext If true, the obj passed in becomes the
|
|
|
491 |
* execution scope of the listener.
|
|
|
492 |
*/
|
|
|
493 |
register: function (module, initialState, onStateChange, obj, overrideContext) {
|
|
|
494 |
|
|
|
495 |
var scope, wrappedFn;
|
|
|
496 |
|
|
|
497 |
if (typeof module !== "string" || YAHOO.lang.trim(module) === "" ||
|
|
|
498 |
typeof initialState !== "string" ||
|
|
|
499 |
typeof onStateChange !== "function") {
|
|
|
500 |
throw new Error("Missing or invalid argument");
|
|
|
501 |
}
|
|
|
502 |
|
|
|
503 |
if (YAHOO.lang.hasOwnProperty(_modules, module)) {
|
|
|
504 |
// Here, we used to throw an exception. However, users have
|
|
|
505 |
// complained about this behavior, so we now just return.
|
|
|
506 |
return;
|
|
|
507 |
}
|
|
|
508 |
|
|
|
509 |
// Note: A module CANNOT be registered after calling
|
|
|
510 |
// YAHOO.util.History.initialize. Indeed, we set the initial state
|
|
|
511 |
// of each registered module in YAHOO.util.History.initialize.
|
|
|
512 |
// If you could register a module after initializing the Browser
|
|
|
513 |
// History Manager, you would not read the correct state using
|
|
|
514 |
// YAHOO.util.History.getCurrentState when coming back to the
|
|
|
515 |
// page using the back button.
|
|
|
516 |
if (_initialized) {
|
|
|
517 |
throw new Error("All modules must be registered before calling YAHOO.util.History.initialize");
|
|
|
518 |
}
|
|
|
519 |
|
|
|
520 |
// Make sure the strings passed in do not contain our separators "," and "|"
|
|
|
521 |
module = _encode(module);
|
|
|
522 |
initialState = _encode(initialState);
|
|
|
523 |
|
|
|
524 |
// If the user chooses to override the scope, we use the
|
|
|
525 |
// custom object passed in as the execution scope.
|
|
|
526 |
scope = null;
|
|
|
527 |
if (overrideContext === true) {
|
|
|
528 |
scope = obj;
|
|
|
529 |
} else {
|
|
|
530 |
scope = overrideContext;
|
|
|
531 |
}
|
|
|
532 |
|
|
|
533 |
wrappedFn = function (state) {
|
|
|
534 |
return onStateChange.call(scope, state, obj);
|
|
|
535 |
};
|
|
|
536 |
|
|
|
537 |
_modules[module] = {
|
|
|
538 |
name: module,
|
|
|
539 |
initialState: initialState,
|
|
|
540 |
currentState: initialState,
|
|
|
541 |
onStateChange: wrappedFn
|
|
|
542 |
};
|
|
|
543 |
},
|
|
|
544 |
|
|
|
545 |
/**
|
|
|
546 |
* Initializes the Browser History Manager. Call this method
|
|
|
547 |
* from a script block located right after the opening body tag.
|
|
|
548 |
*
|
|
|
549 |
* @method initialize
|
|
|
550 |
* @param {string|HTML Element} stateField <input type="hidden"> used
|
|
|
551 |
* to store application states. Must be in the static markup.
|
|
|
552 |
* @param {string|HTML Element} histFrame IFrame used to store
|
|
|
553 |
* the history (only required on Internet Explorer)
|
|
|
554 |
* @public
|
|
|
555 |
*/
|
|
|
556 |
initialize: function (stateField, histFrame) {
|
|
|
557 |
|
|
|
558 |
if (_initialized) {
|
|
|
559 |
// The browser history manager has already been initialized.
|
|
|
560 |
return;
|
|
|
561 |
}
|
|
|
562 |
|
|
|
563 |
if (YAHOO.env.ua.opera && typeof history.navigationMode !== "undefined") {
|
|
|
564 |
// Disable Opera's fast back/forward navigation mode and puts
|
|
|
565 |
// it in compatible mode. This makes anchor-based history
|
|
|
566 |
// navigation work after the page has been navigated away
|
|
|
567 |
// from and re-activated, at the cost of slowing down
|
|
|
568 |
// back/forward navigation to and from that page.
|
|
|
569 |
history.navigationMode = "compatible";
|
|
|
570 |
}
|
|
|
571 |
|
|
|
572 |
if (typeof stateField === "string") {
|
|
|
573 |
stateField = document.getElementById(stateField);
|
|
|
574 |
}
|
|
|
575 |
|
|
|
576 |
if (!stateField ||
|
|
|
577 |
stateField.tagName.toUpperCase() !== "TEXTAREA" &&
|
|
|
578 |
(stateField.tagName.toUpperCase() !== "INPUT" ||
|
|
|
579 |
stateField.type !== "hidden" &&
|
|
|
580 |
stateField.type !== "text")) {
|
|
|
581 |
throw new Error("Missing or invalid argument");
|
|
|
582 |
}
|
|
|
583 |
|
|
|
584 |
_stateField = stateField;
|
|
|
585 |
|
|
|
586 |
// IE < 8 or IE8 in quirks mode or IE7 standards mode
|
|
|
587 |
if (YAHOO.env.ua.ie && (typeof document.documentMode === "undefined" || document.documentMode < 8)) {
|
|
|
588 |
|
|
|
589 |
if (typeof histFrame === "string") {
|
|
|
590 |
histFrame = document.getElementById(histFrame);
|
|
|
591 |
}
|
|
|
592 |
|
|
|
593 |
if (!histFrame || histFrame.tagName.toUpperCase() !== "IFRAME") {
|
|
|
594 |
throw new Error("Missing or invalid argument");
|
|
|
595 |
}
|
|
|
596 |
|
|
|
597 |
_histFrame = histFrame;
|
|
|
598 |
}
|
|
|
599 |
|
|
|
600 |
// Note that the event utility MUST be included inline in the page.
|
|
|
601 |
// If it gets loaded later (which you may want to do to improve the
|
|
|
602 |
// loading speed of your site), the onDOMReady event never fires,
|
|
|
603 |
// and the history library never gets fully initialized.
|
|
|
604 |
YAHOO.util.Event.onDOMReady(_initialize);
|
|
|
605 |
},
|
|
|
606 |
|
|
|
607 |
/**
|
|
|
608 |
* Call this method when you want to store a new entry in the browser's history.
|
|
|
609 |
*
|
|
|
610 |
* @method navigate
|
|
|
611 |
* @param {string} module Non-empty string representing your module.
|
|
|
612 |
* @param {string} state String representing the new state of the specified module.
|
|
|
613 |
* @return {boolean} Indicates whether the new state was successfully added to the history.
|
|
|
614 |
* @public
|
|
|
615 |
*/
|
|
|
616 |
navigate: function (module, state) {
|
|
|
617 |
|
|
|
618 |
var states;
|
|
|
619 |
|
|
|
620 |
if (typeof module !== "string" || typeof state !== "string") {
|
|
|
621 |
throw new Error("Missing or invalid argument");
|
|
|
622 |
}
|
|
|
623 |
|
|
|
624 |
states = {};
|
|
|
625 |
states[module] = state;
|
|
|
626 |
|
|
|
627 |
return YAHOO.util.History.multiNavigate(states);
|
|
|
628 |
},
|
|
|
629 |
|
|
|
630 |
/**
|
|
|
631 |
* Call this method when you want to store a new entry in the browser's history.
|
|
|
632 |
*
|
|
|
633 |
* @method multiNavigate
|
|
|
634 |
* @param {object} states Associative array of module-state pairs to set simultaneously.
|
|
|
635 |
* @return {boolean} Indicates whether the new state was successfully added to the history.
|
|
|
636 |
* @public
|
|
|
637 |
*/
|
|
|
638 |
multiNavigate: function (states) {
|
|
|
639 |
|
|
|
640 |
var currentStates, moduleName, moduleObj, currentState, fqstate;
|
|
|
641 |
|
|
|
642 |
if (typeof states !== "object") {
|
|
|
643 |
throw new Error("Missing or invalid argument");
|
|
|
644 |
}
|
|
|
645 |
|
|
|
646 |
if (!_initialized) {
|
|
|
647 |
throw new Error("The Browser History Manager is not initialized");
|
|
|
648 |
}
|
|
|
649 |
|
|
|
650 |
for (moduleName in states) {
|
|
|
651 |
if (!YAHOO.lang.hasOwnProperty(_modules, _encode(moduleName))) {
|
|
|
652 |
throw new Error("The following module has not been registered: " + moduleName);
|
|
|
653 |
}
|
|
|
654 |
}
|
|
|
655 |
|
|
|
656 |
// Generate our new full state string mod1=xxx&mod2=yyy
|
|
|
657 |
currentStates = [];
|
|
|
658 |
|
|
|
659 |
for (moduleName in _modules) {
|
|
|
660 |
if (YAHOO.lang.hasOwnProperty(_modules, moduleName)) {
|
|
|
661 |
moduleObj = _modules[moduleName];
|
|
|
662 |
if (YAHOO.lang.hasOwnProperty(states, moduleName)) {
|
|
|
663 |
currentState = states[_decode(moduleName)];
|
|
|
664 |
} else {
|
|
|
665 |
currentState = _decode(moduleObj.currentState);
|
|
|
666 |
}
|
|
|
667 |
|
|
|
668 |
// Make sure the strings passed in do not contain our separators "," and "|"
|
|
|
669 |
moduleName = _encode(moduleName);
|
|
|
670 |
currentState = _encode(currentState);
|
|
|
671 |
|
|
|
672 |
currentStates.push(moduleName + "=" + currentState);
|
|
|
673 |
}
|
|
|
674 |
}
|
|
|
675 |
|
|
|
676 |
fqstate = currentStates.join("&");
|
|
|
677 |
|
|
|
678 |
if (YAHOO.env.ua.ie && (typeof document.documentMode === "undefined" || document.documentMode < 8)) {
|
|
|
679 |
|
|
|
680 |
return _updateIFrame(fqstate);
|
|
|
681 |
|
|
|
682 |
} else {
|
|
|
683 |
|
|
|
684 |
// Known bug: On Safari 1.x and 2.0, if you have tab browsing
|
|
|
685 |
// enabled, Safari will show an endless loading icon in the
|
|
|
686 |
// tab. This has apparently been fixed in recent WebKit builds.
|
|
|
687 |
// One work around found by Dav Glass is to submit a form that
|
|
|
688 |
// points to the same document. This indeed works on Safari 1.x
|
|
|
689 |
// and 2.0 but creates bigger problems on WebKit. So for now,
|
|
|
690 |
// we'll consider this an acceptable bug, and hope that Apple
|
|
|
691 |
// comes out with their next version of Safari very soon.
|
|
|
692 |
self.location.hash = fqstate;
|
|
|
693 |
|
|
|
694 |
return true;
|
|
|
695 |
}
|
|
|
696 |
},
|
|
|
697 |
|
|
|
698 |
/**
|
|
|
699 |
* Returns the current state of the specified module.
|
|
|
700 |
*
|
|
|
701 |
* @method getCurrentState
|
|
|
702 |
* @param {string} module Non-empty string representing your module.
|
|
|
703 |
* @return {string} The current state of the specified module.
|
|
|
704 |
* @public
|
|
|
705 |
*/
|
|
|
706 |
getCurrentState: function (module) {
|
|
|
707 |
|
|
|
708 |
var moduleObj;
|
|
|
709 |
|
|
|
710 |
if (typeof module !== "string") {
|
|
|
711 |
throw new Error("Missing or invalid argument");
|
|
|
712 |
}
|
|
|
713 |
|
|
|
714 |
if (!_initialized) {
|
|
|
715 |
throw new Error("The Browser History Manager is not initialized");
|
|
|
716 |
}
|
|
|
717 |
|
|
|
718 |
moduleObj = YAHOO.lang.hasOwnProperty(_modules, module)
|
|
|
719 |
&& _modules[module];
|
|
|
720 |
|
|
|
721 |
if (!moduleObj) {
|
|
|
722 |
throw new Error("No such registered module: " + module);
|
|
|
723 |
}
|
|
|
724 |
|
|
|
725 |
return _decode(moduleObj.currentState);
|
|
|
726 |
},
|
|
|
727 |
|
|
|
728 |
/**
|
|
|
729 |
* Returns the state of a module according to the URL fragment
|
|
|
730 |
* identifier. This method is useful to initialize your modules
|
|
|
731 |
* if your application was bookmarked from a particular state.
|
|
|
732 |
*
|
|
|
733 |
* @method getBookmarkedState
|
|
|
734 |
* @param {string} module Non-empty string representing your module.
|
|
|
735 |
* @return {string} The bookmarked state of the specified module.
|
|
|
736 |
* @public
|
|
|
737 |
*/
|
|
|
738 |
getBookmarkedState: function (module) {
|
|
|
739 |
|
|
|
740 |
var i, len, idx, hash, states, tokens, moduleName;
|
|
|
741 |
|
|
|
742 |
if (typeof module !== "string") {
|
|
|
743 |
throw new Error("Missing or invalid argument");
|
|
|
744 |
}
|
|
|
745 |
|
|
|
746 |
// Use location.href instead of location.hash which is already
|
|
|
747 |
// URL-decoded, which creates problems if the state value
|
|
|
748 |
// contained special characters...
|
|
|
749 |
idx = self.location.href.indexOf("#");
|
|
|
750 |
if (idx >= 0) {
|
|
|
751 |
hash = self.location.href.substr(idx + 1);
|
|
|
752 |
states = hash.split("&");
|
|
|
753 |
for (i = 0, len = states.length; i < len; i++) {
|
|
|
754 |
tokens = states[i].split("=");
|
|
|
755 |
if (tokens.length === 2) {
|
|
|
756 |
moduleName = tokens[0];
|
|
|
757 |
if (moduleName === module) {
|
|
|
758 |
return _decode(tokens[1]);
|
|
|
759 |
}
|
|
|
760 |
}
|
|
|
761 |
}
|
|
|
762 |
}
|
|
|
763 |
|
|
|
764 |
return null;
|
|
|
765 |
},
|
|
|
766 |
|
|
|
767 |
/**
|
|
|
768 |
* Returns the value of the specified query string parameter.
|
|
|
769 |
* This method is not used internally by the Browser History Manager.
|
|
|
770 |
* However, it is provided here as a helper since many applications
|
|
|
771 |
* using the Browser History Manager will want to read the value of
|
|
|
772 |
* url parameters to initialize themselves.
|
|
|
773 |
*
|
|
|
774 |
* @method getQueryStringParameter
|
|
|
775 |
* @param {string} paramName Name of the parameter we want to look up.
|
|
|
776 |
* @param {string} queryString Optional URL to look at. If not specified,
|
|
|
777 |
* this method uses the URL in the address bar.
|
|
|
778 |
* @return {string} The value of the specified parameter, or null.
|
|
|
779 |
* @public
|
|
|
780 |
*/
|
|
|
781 |
getQueryStringParameter: function (paramName, url) {
|
|
|
782 |
|
|
|
783 |
var i, len, idx, queryString, params, tokens;
|
|
|
784 |
|
|
|
785 |
url = url || self.location.href;
|
|
|
786 |
|
|
|
787 |
idx = url.indexOf("?");
|
|
|
788 |
queryString = idx >= 0 ? url.substr(idx + 1) : url;
|
|
|
789 |
|
|
|
790 |
// Remove the hash if any
|
|
|
791 |
idx = queryString.lastIndexOf("#");
|
|
|
792 |
queryString = idx >= 0 ? queryString.substr(0, idx) : queryString;
|
|
|
793 |
|
|
|
794 |
params = queryString.split("&");
|
|
|
795 |
|
|
|
796 |
for (i = 0, len = params.length; i < len; i++) {
|
|
|
797 |
tokens = params[i].split("=");
|
|
|
798 |
if (tokens.length >= 2) {
|
|
|
799 |
if (tokens[0] === paramName) {
|
|
|
800 |
return _decode(tokens[1]);
|
|
|
801 |
}
|
|
|
802 |
}
|
|
|
803 |
}
|
|
|
804 |
|
|
|
805 |
return null;
|
|
|
806 |
}
|
|
|
807 |
|
|
|
808 |
};
|
|
|
809 |
|
|
|
810 |
})();
|
|
|
811 |
YAHOO.register("history", YAHOO.util.History, {version: "2.9.0", build: "2800"});
|
|
|
812 |
|
|
|
813 |
}, '2.9.0' ,{"requires": ["yui2-yahoo", "yui2-event"]});
|