Upgrade to ExtJS 3.2.2 - Released 06/02/2010
[extjs.git] / pkgs / state-debug.js
1 /*!
2  * Ext JS Library 3.2.2
3  * Copyright(c) 2006-2010 Ext JS, Inc.
4  * licensing@extjs.com
5  * http://www.extjs.com/license
6  */
7 /**
8  * @class Ext.state.Provider
9  * Abstract base class for state provider implementations. This class provides methods
10  * for encoding and decoding <b>typed</b> variables including dates and defines the
11  * Provider interface.
12  */
13 Ext.state.Provider = Ext.extend(Ext.util.Observable, {
14     
15     constructor : function(){
16         /**
17          * @event statechange
18          * Fires when a state change occurs.
19          * @param {Provider} this This state provider
20          * @param {String} key The state key which was changed
21          * @param {String} value The encoded value for the state
22          */
23         this.addEvents("statechange");
24         this.state = {};
25         Ext.state.Provider.superclass.constructor.call(this);
26     },
27     
28     /**
29      * Returns the current value for a key
30      * @param {String} name The key name
31      * @param {Mixed} defaultValue A default value to return if the key's value is not found
32      * @return {Mixed} The state data
33      */
34     get : function(name, defaultValue){
35         return typeof this.state[name] == "undefined" ?
36             defaultValue : this.state[name];
37     },
38
39     /**
40      * Clears a value from the state
41      * @param {String} name The key name
42      */
43     clear : function(name){
44         delete this.state[name];
45         this.fireEvent("statechange", this, name, null);
46     },
47
48     /**
49      * Sets the value for a key
50      * @param {String} name The key name
51      * @param {Mixed} value The value to set
52      */
53     set : function(name, value){
54         this.state[name] = value;
55         this.fireEvent("statechange", this, name, value);
56     },
57
58     /**
59      * Decodes a string previously encoded with {@link #encodeValue}.
60      * @param {String} value The value to decode
61      * @return {Mixed} The decoded value
62      */
63     decodeValue : function(cookie){
64         /**
65          * a -> Array
66          * n -> Number
67          * d -> Date
68          * b -> Boolean
69          * s -> String
70          * o -> Object
71          * -> Empty (null)
72          */
73         var re = /^(a|n|d|b|s|o|e)\:(.*)$/,
74             matches = re.exec(unescape(cookie)),
75             all,
76             type,
77             v,
78             kv;
79         if(!matches || !matches[1]){
80             return; // non state cookie
81         }
82         type = matches[1];
83         v = matches[2];
84         switch(type){
85             case 'e':
86                 return null;
87             case 'n':
88                 return parseFloat(v);
89             case 'd':
90                 return new Date(Date.parse(v));
91             case 'b':
92                 return (v == '1');
93             case 'a':
94                 all = [];
95                 if(v != ''){
96                     Ext.each(v.split('^'), function(val){
97                         all.push(this.decodeValue(val));
98                     }, this);
99                 }
100                 return all;
101            case 'o':
102                 all = {};
103                 if(v != ''){
104                     Ext.each(v.split('^'), function(val){
105                         kv = val.split('=');
106                         all[kv[0]] = this.decodeValue(kv[1]);
107                     }, this);
108                 }
109                 return all;
110            default:
111                 return v;
112         }
113     },
114
115     /**
116      * Encodes a value including type information.  Decode with {@link #decodeValue}.
117      * @param {Mixed} value The value to encode
118      * @return {String} The encoded value
119      */
120     encodeValue : function(v){
121         var enc,
122             flat = '',
123             i = 0,
124             len,
125             key;
126         if(v == null){
127             return 'e:1';    
128         }else if(typeof v == 'number'){
129             enc = 'n:' + v;
130         }else if(typeof v == 'boolean'){
131             enc = 'b:' + (v ? '1' : '0');
132         }else if(Ext.isDate(v)){
133             enc = 'd:' + v.toGMTString();
134         }else if(Ext.isArray(v)){
135             for(len = v.length; i < len; i++){
136                 flat += this.encodeValue(v[i]);
137                 if(i != len - 1){
138                     flat += '^';
139                 }
140             }
141             enc = 'a:' + flat;
142         }else if(typeof v == 'object'){
143             for(key in v){
144                 if(typeof v[key] != 'function' && v[key] !== undefined){
145                     flat += key + '=' + this.encodeValue(v[key]) + '^';
146                 }
147             }
148             enc = 'o:' + flat.substring(0, flat.length-1);
149         }else{
150             enc = 's:' + v;
151         }
152         return escape(enc);
153     }
154 });
155 /**
156  * @class Ext.state.Manager
157  * This is the global state manager. By default all components that are "state aware" check this class
158  * for state information if you don't pass them a custom state provider. In order for this class
159  * to be useful, it must be initialized with a provider when your application initializes. Example usage:
160  <pre><code>
161 // in your initialization function
162 init : function(){
163    Ext.state.Manager.setProvider(new Ext.state.CookieProvider());
164    var win = new Window(...);
165    win.restoreState();
166 }
167  </code></pre>
168  * @singleton
169  */
170 Ext.state.Manager = function(){
171     var provider = new Ext.state.Provider();
172
173     return {
174         /**
175          * Configures the default state provider for your application
176          * @param {Provider} stateProvider The state provider to set
177          */
178         setProvider : function(stateProvider){
179             provider = stateProvider;
180         },
181
182         /**
183          * Returns the current value for a key
184          * @param {String} name The key name
185          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
186          * @return {Mixed} The state data
187          */
188         get : function(key, defaultValue){
189             return provider.get(key, defaultValue);
190         },
191
192         /**
193          * Sets the value for a key
194          * @param {String} name The key name
195          * @param {Mixed} value The state data
196          */
197          set : function(key, value){
198             provider.set(key, value);
199         },
200
201         /**
202          * Clears a value from the state
203          * @param {String} name The key name
204          */
205         clear : function(key){
206             provider.clear(key);
207         },
208
209         /**
210          * Gets the currently configured state provider
211          * @return {Provider} The state provider
212          */
213         getProvider : function(){
214             return provider;
215         }
216     };
217 }();
218 /**
219  * @class Ext.state.CookieProvider
220  * @extends Ext.state.Provider
221  * The default Provider implementation which saves state via cookies.
222  * <br />Usage:
223  <pre><code>
224    var cp = new Ext.state.CookieProvider({
225        path: "/cgi-bin/",
226        expires: new Date(new Date().getTime()+(1000*60*60*24*30)), //30 days
227        domain: "extjs.com"
228    });
229    Ext.state.Manager.setProvider(cp);
230  </code></pre>
231  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
232  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
233  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
234  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'extjs.com' to include
235  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
236  * domain the page is running on including the 'www' like 'www.extjs.com')
237  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
238  * @constructor
239  * Create a new CookieProvider
240  * @param {Object} config The configuration object
241  */
242 Ext.state.CookieProvider = Ext.extend(Ext.state.Provider, {
243     
244     constructor : function(config){
245         Ext.state.CookieProvider.superclass.constructor.call(this);
246         this.path = "/";
247         this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
248         this.domain = null;
249         this.secure = false;
250         Ext.apply(this, config);
251         this.state = this.readCookies();
252     },
253     
254     // private
255     set : function(name, value){
256         if(typeof value == "undefined" || value === null){
257             this.clear(name);
258             return;
259         }
260         this.setCookie(name, value);
261         Ext.state.CookieProvider.superclass.set.call(this, name, value);
262     },
263
264     // private
265     clear : function(name){
266         this.clearCookie(name);
267         Ext.state.CookieProvider.superclass.clear.call(this, name);
268     },
269
270     // private
271     readCookies : function(){
272         var cookies = {},
273             c = document.cookie + ";",
274             re = /\s?(.*?)=(.*?);/g,
275             matches,
276             name,
277             value;
278         while((matches = re.exec(c)) != null){
279             name = matches[1];
280             value = matches[2];
281             if(name && name.substring(0,3) == "ys-"){
282                 cookies[name.substr(3)] = this.decodeValue(value);
283             }
284         }
285         return cookies;
286     },
287
288     // private
289     setCookie : function(name, value){
290         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
291            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
292            ((this.path == null) ? "" : ("; path=" + this.path)) +
293            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
294            ((this.secure == true) ? "; secure" : "");
295     },
296
297     // private
298     clearCookie : function(name){
299         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
300            ((this.path == null) ? "" : ("; path=" + this.path)) +
301            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
302            ((this.secure == true) ? "; secure" : "");
303     }
304 });