/** * @class Ext.state.Provider * <p>Abstract base class for state provider implementations. The provider is responsible * for setting values and extracting values to/from the underlying storage source. The * storage source can vary and the details should be implemented in a subclass. For example * a provider could use a server side database or the browser localstorage where supported.</p> * * <p>This class provides methods for encoding and decoding <b>typed</b> variables including * dates and defines the Provider interface. By default these methods put the value and the * type information into a delimited string that can be stored. These should be overridden in * a subclass if you want to change the format of the encoded value and subsequent decoding.</p> */ Ext.define('Ext.state.Provider', { mixins: { observable: 'Ext.util.Observable' }, /** * @cfg {String} prefix A string to prefix to items stored in the underlying state store. * Defaults to <tt>'ext-'</tt> */ prefix: 'ext-', constructor : function(config){ config = config || {}; var me = this; Ext.apply(me, config); /** * @event statechange * Fires when a state change occurs. * @param {Provider} this This state provider * @param {String} key The state key which was changed * @param {String} value The encoded value for the state */ me.addEvents("statechange"); me.state = {}; me.mixins.observable.constructor.call(me); }, /** * Returns the current value for a key * @param {String} name The key name * @param {Mixed} defaultValue A default value to return if the key's value is not found * @return {Mixed} The state data */ get : function(name, defaultValue){ return typeof this.state[name] == "undefined" ? defaultValue : this.state[name]; }, /** * Clears a value from the state * @param {String} name The key name */ clear : function(name){ var me = this; delete me.state[name]; me.fireEvent("statechange", me, name, null); }, /** * Sets the value for a key * @param {String} name The key name * @param {Mixed} value The value to set */ set : function(name, value){ var me = this; me.state[name] = value; me.fireEvent("statechange", me, name, value); }, /** * Decodes a string previously encoded with {@link #encodeValue}. * @param {String} value The value to decode * @return {Mixed} The decoded value */ decodeValue : function(value){ // a -> Array // n -> Number // d -> Date // b -> Boolean // s -> String // o -> Object // -> Empty (null) var me = this, re = /^(a|n|d|b|s|o|e)\:(.*)$/, matches = re.exec(unescape(value)), all, type, value, keyValue; if(!matches || !matches[1]){ return; // non state } type = matches[1]; value = matches[2]; switch (type) { case 'e': return null; case 'n': return parseFloat(value); case 'd': return new Date(Date.parse(value)); case 'b': return (value == '1'); case 'a': all = []; if(value != ''){ Ext.each(value.split('^'), function(val){ all.push(me.decodeValue(val)); }, me); } return all; case 'o': all = {}; if(value != ''){ Ext.each(value.split('^'), function(val){ keyValue = val.split('='); all[keyValue[0]] = me.decodeValue(keyValue[1]); }, me); } return all; default: return value; } }, /** * Encodes a value including type information. Decode with {@link #decodeValue}. * @param {Mixed} value The value to encode * @return {String} The encoded value */ encodeValue : function(value){ var flat = '', i = 0, enc, len, key; if (value == null) { return 'e:1'; } else if(typeof value == 'number') { enc = 'n:' + value; } else if(typeof value == 'boolean') { enc = 'b:' + (value ? '1' : '0'); } else if(Ext.isDate(value)) { enc = 'd:' + value.toGMTString(); } else if(Ext.isArray(value)) { for (len = value.length; i < len; i++) { flat += this.encodeValue(value[i]); if (i != len - 1) { flat += '^'; } } enc = 'a:' + flat; } else if (typeof value == 'object') { for (key in value) { if (typeof value[key] != 'function' && value[key] !== undefined) { flat += key + '=' + this.encodeValue(value[key]) + '^'; } } enc = 'o:' + flat.substring(0, flat.length-1); } else { enc = 's:' + value; } return escape(enc); } });