Upgrade to ExtJS 4.0.1 - Released 05/18/2011
[extjs.git] / docs / source / History.html
1 <!DOCTYPE html>
2 <html>
3 <head>
4   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5   <title>The source code</title>
6   <link href="../prettify/prettify.css" type="text/css" rel="stylesheet" />
7   <script type="text/javascript" src="../prettify/prettify.js"></script>
8   <style type="text/css">
9     .highlight { display: block; background-color: #ddd; }
10   </style>
11   <script type="text/javascript">
12     function highlight() {
13       document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
14     }
15   </script>
16 </head>
17 <body onload="prettyPrint(); highlight();">
18   <pre class="prettyprint lang-js"><span id='Ext-util-History'>/**
19 </span> * @class Ext.util.History
20
21 History management component that allows you to register arbitrary tokens that signify application
22 history state on navigation actions.  You can then handle the history {@link #change} event in order
23 to reset your application UI to the appropriate state when the user navigates forward or backward through
24 the browser history stack.
25
26 __Initializing__
27 The {@link #init} method of the History object must be called before using History. This sets up the internal
28 state and must be the first thing called before using History.
29
30 __Setup__
31 The History objects requires elements on the page to keep track of the browser history. For older versions of IE,
32 an IFrame is required to do the tracking. For other browsers, a hidden field can be used. The history objects expects
33 these to be on the page before the {@link #init} method is called. The following markup is suggested in order
34 to support all browsers:
35
36     &lt;form id=&quot;history-form&quot; class=&quot;x-hide-display&quot;&gt;
37         &lt;input type=&quot;hidden&quot; id=&quot;x-history-field&quot; /&gt;
38         &lt;iframe id=&quot;x-history-frame&quot;&gt;&lt;/iframe&gt;
39     &lt;/form&gt;
40
41  * @markdown
42  * @singleton
43  */
44 Ext.define('Ext.util.History', {
45     singleton: true,
46     alternateClassName: 'Ext.History',
47     mixins: {
48         observable: 'Ext.util.Observable'
49     },
50     
51     constructor: function() {
52         var me = this;
53         me.oldIEMode = Ext.isIE6 || Ext.isIE7 || !Ext.isStrict &amp;&amp; Ext.isIE8;
54         me.iframe = null;
55         me.hiddenField = null;
56         me.ready = false;
57         me.currentToken = null;
58     },
59     
60     getHash: function() {
61         var href = window.location.href,
62             i = href.indexOf(&quot;#&quot;);
63             
64         return i &gt;= 0 ? href.substr(i + 1) : null;
65     },
66
67     doSave: function() {
68         this.hiddenField.value = this.currentToken;
69     },
70     
71
72     handleStateChange: function(token) {
73         this.currentToken = token;
74         this.fireEvent('change', token);
75     },
76
77     updateIFrame: function(token) {
78         var html = '&lt;html&gt;&lt;body&gt;&lt;div id=&quot;state&quot;&gt;' + 
79                     Ext.util.Format.htmlEncode(token) + 
80                     '&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;';
81
82         try {
83             var doc = this.iframe.contentWindow.document;
84             doc.open();
85             doc.write(html);
86             doc.close();
87             return true;
88         } catch (e) {
89             return false;
90         }
91     },
92
93     checkIFrame: function () {
94         var me = this,
95             contentWindow = me.iframe.contentWindow;
96             
97         if (!contentWindow || !contentWindow.document) {
98             Ext.Function.defer(this.checkIFrame, 10, this);
99             return;
100         }
101        
102         var doc = contentWindow.document,
103             elem = doc.getElementById(&quot;state&quot;),
104             oldToken = elem ? elem.innerText : null,
105             oldHash = me.getHash();
106            
107         Ext.TaskManager.start({
108             run: function () {
109                 var doc = contentWindow.document,
110                     elem = doc.getElementById(&quot;state&quot;),
111                     newToken = elem ? elem.innerText : null,
112                     newHash = me.getHash();
113
114                 if (newToken !== oldToken) {
115                     oldToken = newToken;
116                     me.handleStateChange(newToken);
117                     window.top.location.hash = newToken;
118                     oldHash = newToken;
119                     me.doSave();
120                 } else if (newHash !== oldHash) {
121                     oldHash = newHash;
122                     me.updateIFrame(newHash);
123                 }
124             }, 
125             interval: 50,
126             scope: me
127         });
128         me.ready = true;
129         me.fireEvent('ready', me);            
130     },
131
132     startUp: function () {
133         var me = this;
134         
135         me.currentToken = me.hiddenField.value || this.getHash();
136
137         if (me.oldIEMode) {
138             me.checkIFrame();
139         } else {
140             var hash = me.getHash();
141             Ext.TaskManager.start({
142                 run: function () {
143                     var newHash = me.getHash();
144                     if (newHash !== hash) {
145                         hash = newHash;
146                         me.handleStateChange(hash);
147                         me.doSave();
148                     }
149                 },
150                 interval: 50,
151                 scope: me
152             });
153             me.ready = true;
154             me.fireEvent('ready', me);
155         }
156         
157     },
158
159 <span id='Ext-util-History-property-fieldId'>    /**
160 </span>     * The id of the hidden field required for storing the current history token.
161      * @type String
162      * @property
163      */
164     fieldId: Ext.baseCSSPrefix + 'history-field',
165 <span id='Ext-util-History-property-iframeId'>    /**
166 </span>     * The id of the iframe required by IE to manage the history stack.
167      * @type String
168      * @property
169      */
170     iframeId: Ext.baseCSSPrefix + 'history-frame',
171
172 <span id='Ext-util-History-method-init'>    /**
173 </span>     * Initialize the global History instance.
174      * @param {Boolean} onReady (optional) A callback function that will be called once the history
175      * component is fully initialized.
176      * @param {Object} scope (optional) The scope (&lt;code&gt;this&lt;/code&gt; reference) in which the callback is executed. Defaults to the browser window.
177      */
178     init: function (onReady, scope) {
179         var me = this;
180         
181         if (me.ready) {
182             Ext.callback(onReady, scope, [me]);
183             return;
184         }
185         
186         if (!Ext.isReady) {
187             Ext.onReady(function() {
188                 me.init(onReady, scope);
189             });
190             return;
191         }
192         
193         me.hiddenField = Ext.getDom(me.fieldId);
194         
195         if (me.oldIEMode) {
196             me.iframe = Ext.getDom(me.iframeId);
197         }
198         
199         me.addEvents(
200 <span id='Ext-util-History-event-ready'>            /**
201 </span>             * @event ready
202              * Fires when the Ext.util.History singleton has been initialized and is ready for use.
203              * @param {Ext.util.History} The Ext.util.History singleton.
204              */
205             'ready',
206 <span id='Ext-util-History-event-change'>            /**
207 </span>             * @event change
208              * Fires when navigation back or forwards within the local page's history occurs.
209              * @param {String} token An identifier associated with the page state at that point in its history.
210              */
211             'change'
212         );
213         
214         if (onReady) {
215             me.on('ready', onReady, scope, {single: true});
216         }
217         me.startUp();
218     },
219
220 <span id='Ext-util-History-method-add'>    /**
221 </span>     * Add a new token to the history stack. This can be any arbitrary value, although it would
222      * commonly be the concatenation of a component id and another id marking the specifc history
223      * state of that component.  Example usage:
224      * &lt;pre&gt;&lt;code&gt;
225 // Handle tab changes on a TabPanel
226 tabPanel.on('tabchange', function(tabPanel, tab){
227 Ext.History.add(tabPanel.id + ':' + tab.id);
228 });
229 &lt;/code&gt;&lt;/pre&gt;
230      * @param {String} token The value that defines a particular application-specific history state
231      * @param {Boolean} preventDuplicates When true, if the passed token matches the current token
232      * it will not save a new history step. Set to false if the same state can be saved more than once
233      * at the same history stack location (defaults to true).
234      */
235     add: function (token, preventDup) {
236         var me = this;
237         
238         if (preventDup !== false) {
239             if (me.getToken() === token) {
240                 return true;
241             }
242         }
243         
244         if (me.oldIEMode) {
245             return me.updateIFrame(token);
246         } else {
247             window.top.location.hash = token;
248             return true;
249         }
250     },
251
252 <span id='Ext-util-History-method-back'>    /**
253 </span>     * Programmatically steps back one step in browser history (equivalent to the user pressing the Back button).
254      */
255     back: function() {
256         window.history.go(-1);
257     },
258
259 <span id='Ext-util-History-method-forward'>    /**
260 </span>     * Programmatically steps forward one step in browser history (equivalent to the user pressing the Forward button).
261      */
262     forward: function(){
263         window.history.go(1);
264     },
265
266 <span id='Ext-util-History-method-getToken'>    /**
267 </span>     * Retrieves the currently-active history token.
268      * @return {String} The token
269      */
270     getToken: function() {
271         return this.ready ? this.currentToken : this.getHash();
272     }
273 });</pre>
274 </body>
275 </html>