Upgrade to ExtJS 3.1.1 - Released 02/08/2010
[extjs.git] / pkgs / pkg-history-debug.js
1 /*!
2  * Ext JS Library 3.1.1
3  * Copyright(c) 2006-2010 Ext JS, LLC
4  * licensing@extjs.com
5  * http://www.extjs.com/license
6  */
7 /**\r
8  * @class Ext.History\r
9  * @extends Ext.util.Observable\r
10  * History management component that allows you to register arbitrary tokens that signify application\r
11  * history state on navigation actions.  You can then handle the history {@link #change} event in order\r
12  * to reset your application UI to the appropriate state when the user navigates forward or backward through\r
13  * the browser history stack.\r
14  * @singleton\r
15  */\r
16 Ext.History = (function () {\r
17     var iframe, hiddenField;\r
18     var ready = false;\r
19     var currentToken;\r
20 \r
21     function getHash() {\r
22         var href = top.location.href, i = href.indexOf("#");\r
23         return i >= 0 ? href.substr(i + 1) : null;\r
24     }\r
25 \r
26     function doSave() {\r
27         hiddenField.value = currentToken;\r
28     }\r
29 \r
30     function handleStateChange(token) {\r
31         currentToken = token;\r
32         Ext.History.fireEvent('change', token);\r
33     }\r
34 \r
35     function updateIFrame (token) {\r
36         var html = ['<html><body><div id="state">',Ext.util.Format.htmlEncode(token),'</div></body></html>'].join('');\r
37         try {\r
38             var doc = iframe.contentWindow.document;\r
39             doc.open();\r
40             doc.write(html);\r
41             doc.close();\r
42             return true;\r
43         } catch (e) {\r
44             return false;\r
45         }\r
46     }\r
47 \r
48     function checkIFrame() {\r
49         if (!iframe.contentWindow || !iframe.contentWindow.document) {\r
50             setTimeout(checkIFrame, 10);\r
51             return;\r
52         }\r
53 \r
54         var doc = iframe.contentWindow.document;\r
55         var elem = doc.getElementById("state");\r
56         var token = elem ? elem.innerText : null;\r
57 \r
58         var hash = getHash();\r
59 \r
60         setInterval(function () {\r
61 \r
62             doc = iframe.contentWindow.document;\r
63             elem = doc.getElementById("state");\r
64 \r
65             var newtoken = elem ? elem.innerText : null;\r
66 \r
67             var newHash = getHash();\r
68 \r
69             if (newtoken !== token) {\r
70                 token = newtoken;\r
71                 handleStateChange(token);\r
72                 top.location.hash = token;\r
73                 hash = token;\r
74                 doSave();\r
75             } else if (newHash !== hash) {\r
76                 hash = newHash;\r
77                 updateIFrame(newHash);\r
78             }\r
79 \r
80         }, 50);\r
81 \r
82         ready = true;\r
83 \r
84         Ext.History.fireEvent('ready', Ext.History);\r
85     }\r
86 \r
87     function startUp() {\r
88         currentToken = hiddenField.value ? hiddenField.value : getHash();\r
89 \r
90         if (Ext.isIE) {\r
91             checkIFrame();\r
92         } else {\r
93             var hash = getHash();\r
94             setInterval(function () {\r
95                 var newHash = getHash();\r
96                 if (newHash !== hash) {\r
97                     hash = newHash;\r
98                     handleStateChange(hash);\r
99                     doSave();\r
100                 }\r
101             }, 50);\r
102             ready = true;\r
103             Ext.History.fireEvent('ready', Ext.History);\r
104         }\r
105     }\r
106 \r
107     return {\r
108         /**\r
109          * The id of the hidden field required for storing the current history token.\r
110          * @type String\r
111          * @property\r
112          */\r
113         fieldId: 'x-history-field',\r
114         /**\r
115          * The id of the iframe required by IE to manage the history stack.\r
116          * @type String\r
117          * @property\r
118          */\r
119         iframeId: 'x-history-frame',\r
120 \r
121         events:{},\r
122 \r
123         /**\r
124          * Initialize the global History instance.\r
125          * @param {Boolean} onReady (optional) A callback function that will be called once the history\r
126          * component is fully initialized.\r
127          * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the callback is executed. Defaults to the browser window.\r
128          */\r
129         init: function (onReady, scope) {\r
130             if(ready) {\r
131                 Ext.callback(onReady, scope, [this]);\r
132                 return;\r
133             }\r
134             if(!Ext.isReady){\r
135                 Ext.onReady(function(){\r
136                     Ext.History.init(onReady, scope);\r
137                 });\r
138                 return;\r
139             }\r
140             hiddenField = Ext.getDom(Ext.History.fieldId);\r
141             if (Ext.isIE) {\r
142                 iframe = Ext.getDom(Ext.History.iframeId);\r
143             }\r
144             this.addEvents(\r
145                 /**\r
146                  * @event ready\r
147                  * Fires when the Ext.History singleton has been initialized and is ready for use.\r
148                  * @param {Ext.History} The Ext.History singleton.\r
149                  */\r
150                 'ready',\r
151                 /**\r
152                  * @event change\r
153                  * Fires when navigation back or forwards within the local page's history occurs.\r
154                  * @param {String} token An identifier associated with the page state at that point in its history.\r
155                  */\r
156                 'change'\r
157             );\r
158             if(onReady){\r
159                 this.on('ready', onReady, scope, {single:true});\r
160             }\r
161             startUp();\r
162         },\r
163 \r
164         /**\r
165          * Add a new token to the history stack. This can be any arbitrary value, although it would\r
166          * commonly be the concatenation of a component id and another id marking the specifc history\r
167          * state of that component.  Example usage:\r
168          * <pre><code>\r
169 // Handle tab changes on a TabPanel\r
170 tabPanel.on('tabchange', function(tabPanel, tab){\r
171     Ext.History.add(tabPanel.id + ':' + tab.id);\r
172 });\r
173 </code></pre>\r
174          * @param {String} token The value that defines a particular application-specific history state\r
175          * @param {Boolean} preventDuplicates When true, if the passed token matches the current token\r
176          * it will not save a new history step. Set to false if the same state can be saved more than once\r
177          * at the same history stack location (defaults to true).\r
178          */\r
179         add: function (token, preventDup) {\r
180             if(preventDup !== false){\r
181                 if(this.getToken() == token){\r
182                     return true;\r
183                 }\r
184             }\r
185             if (Ext.isIE) {\r
186                 return updateIFrame(token);\r
187             } else {\r
188                 top.location.hash = token;\r
189                 return true;\r
190             }\r
191         },\r
192 \r
193         /**\r
194          * Programmatically steps back one step in browser history (equivalent to the user pressing the Back button).\r
195          */\r
196         back: function(){\r
197             history.go(-1);\r
198         },\r
199 \r
200         /**\r
201          * Programmatically steps forward one step in browser history (equivalent to the user pressing the Forward button).\r
202          */\r
203         forward: function(){\r
204             history.go(1);\r
205         },\r
206 \r
207         /**\r
208          * Retrieves the currently-active history token.\r
209          * @return {String} The token\r
210          */\r
211         getToken: function() {\r
212             return ready ? currentToken : getHash();\r
213         }\r
214     };\r
215 })();\r
216 Ext.apply(Ext.History, new Ext.util.Observable());