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