Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / src / state / Provider.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 /**
16  * @class Ext.state.Provider
17  * <p>Abstract base class for state provider implementations. The provider is responsible
18  * for setting values  and extracting values to/from the underlying storage source. The 
19  * storage source can vary and the details should be implemented in a subclass. For example
20  * a provider could use a server side database or the browser localstorage where supported.</p>
21  *
22  * <p>This class provides methods for encoding and decoding <b>typed</b> variables including 
23  * dates and defines the Provider interface. By default these methods put the value and the
24  * type information into a delimited string that can be stored. These should be overridden in 
25  * a subclass if you want to change the format of the encoded value and subsequent decoding.</p>
26  */
27 Ext.define('Ext.state.Provider', {
28     mixins: {
29         observable: 'Ext.util.Observable'
30     },
31     
32     /**
33      * @cfg {String} prefix A string to prefix to items stored in the underlying state store. 
34      * Defaults to <tt>'ext-'</tt>
35      */
36     prefix: 'ext-',
37     
38     constructor : function(config){
39         config = config || {};
40         var me = this;
41         Ext.apply(me, config);
42         /**
43          * @event statechange
44          * Fires when a state change occurs.
45          * @param {Ext.state.Provider} this This state provider
46          * @param {String} key The state key which was changed
47          * @param {String} value The encoded value for the state
48          */
49         me.addEvents("statechange");
50         me.state = {};
51         me.mixins.observable.constructor.call(me);
52     },
53     
54     /**
55      * Returns the current value for a key
56      * @param {String} name The key name
57      * @param {Object} defaultValue A default value to return if the key's value is not found
58      * @return {Object} The state data
59      */
60     get : function(name, defaultValue){
61         return typeof this.state[name] == "undefined" ?
62             defaultValue : this.state[name];
63     },
64
65     /**
66      * Clears a value from the state
67      * @param {String} name The key name
68      */
69     clear : function(name){
70         var me = this;
71         delete me.state[name];
72         me.fireEvent("statechange", me, name, null);
73     },
74
75     /**
76      * Sets the value for a key
77      * @param {String} name The key name
78      * @param {Object} value The value to set
79      */
80     set : function(name, value){
81         var me = this;
82         me.state[name] = value;
83         me.fireEvent("statechange", me, name, value);
84     },
85
86     /**
87      * Decodes a string previously encoded with {@link #encodeValue}.
88      * @param {String} value The value to decode
89      * @return {Object} The decoded value
90      */
91     decodeValue : function(value){
92
93         // a -> Array
94         // n -> Number
95         // d -> Date
96         // b -> Boolean
97         // s -> String
98         // o -> Object
99         // -> Empty (null)
100
101         var me = this,
102             re = /^(a|n|d|b|s|o|e)\:(.*)$/,
103             matches = re.exec(unescape(value)),
104             all,
105             type,
106             value,
107             keyValue;
108             
109         if(!matches || !matches[1]){
110             return; // non state
111         }
112         
113         type = matches[1];
114         value = matches[2];
115         switch (type) {
116             case 'e':
117                 return null;
118             case 'n':
119                 return parseFloat(value);
120             case 'd':
121                 return new Date(Date.parse(value));
122             case 'b':
123                 return (value == '1');
124             case 'a':
125                 all = [];
126                 if(value != ''){
127                     Ext.each(value.split('^'), function(val){
128                         all.push(me.decodeValue(val));
129                     }, me);
130                 }
131                 return all;
132            case 'o':
133                 all = {};
134                 if(value != ''){
135                     Ext.each(value.split('^'), function(val){
136                         keyValue = val.split('=');
137                         all[keyValue[0]] = me.decodeValue(keyValue[1]);
138                     }, me);
139                 }
140                 return all;
141            default:
142                 return value;
143         }
144     },
145
146     /**
147      * Encodes a value including type information.  Decode with {@link #decodeValue}.
148      * @param {Object} value The value to encode
149      * @return {String} The encoded value
150      */
151     encodeValue : function(value){
152         var flat = '',
153             i = 0,
154             enc,
155             len,
156             key;
157             
158         if (value == null) {
159             return 'e:1';    
160         } else if(typeof value == 'number') {
161             enc = 'n:' + value;
162         } else if(typeof value == 'boolean') {
163             enc = 'b:' + (value ? '1' : '0');
164         } else if(Ext.isDate(value)) {
165             enc = 'd:' + value.toGMTString();
166         } else if(Ext.isArray(value)) {
167             for (len = value.length; i < len; i++) {
168                 flat += this.encodeValue(value[i]);
169                 if (i != len - 1) {
170                     flat += '^';
171                 }
172             }
173             enc = 'a:' + flat;
174         } else if (typeof value == 'object') {
175             for (key in value) {
176                 if (typeof value[key] != 'function' && value[key] !== undefined) {
177                     flat += key + '=' + this.encodeValue(value[key]) + '^';
178                 }
179             }
180             enc = 'o:' + flat.substring(0, flat.length-1);
181         } else {
182             enc = 's:' + value;
183         }
184         return escape(enc);
185     }
186 });