X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/0494b8d9b9bb03ab6c22b34dae81261e3cd7e3e6..7a654f8d43fdb43d78b63d90528bed6e86b608cc:/src/state/Stateful.js diff --git a/src/state/Stateful.js b/src/state/Stateful.js new file mode 100644 index 00000000..04ba3eb6 --- /dev/null +++ b/src/state/Stateful.js @@ -0,0 +1,286 @@ +/** + * @class Ext.state.Stateful + * A mixin for being able to save the state of an object to an underlying + * {@link Ext.state.Provider}. + */ +Ext.define('Ext.state.Stateful', { + + /* Begin Definitions */ + + mixins: { + observable: 'Ext.util.Observable' + }, + + requires: ['Ext.state.Manager'], + + /* End Definitions */ + + /** + * @cfg {Boolean} stateful + *

A flag which causes the object to attempt to restore the state of + * internal properties from a saved state on startup. The object must have + * a {@link #stateId} for state to be managed. + * Auto-generated ids are not guaranteed to be stable across page loads and + * cannot be relied upon to save and restore the same state for a object.

+ *

For state saving to work, the state manager's provider must have been + * set to an implementation of {@link Ext.state.Provider} which overrides the + * {@link Ext.state.Provider#set set} and {@link Ext.state.Provider#get get} + * methods to save and recall name/value pairs. A built-in implementation, + * {@link Ext.state.CookieProvider} is available.

+ *

To set the state provider for the current page:

+ *

+Ext.state.Manager.setProvider(new Ext.state.CookieProvider({
+    expires: new Date(new Date().getTime()+(1000*60*60*24*7)), //7 days from now
+}));
+     * 
+ *

A stateful object attempts to save state when one of the events + * listed in the {@link #stateEvents} configuration fires.

+ *

To save state, a stateful object first serializes its state by + * calling {@link #getState}. By default, this function does + * nothing. The developer must provide an implementation which returns an + * object hash which represents the restorable state of the object.

+ *

The value yielded by getState is passed to {@link Ext.state.Manager#set} + * which uses the configured {@link Ext.state.Provider} to save the object + * keyed by the {@link stateId}

. + *

During construction, a stateful object attempts to restore + * its state by calling {@link Ext.state.Manager#get} passing the + * {@link #stateId}

+ *

The resulting object is passed to {@link #applyState}. + * The default implementation of {@link #applyState} simply copies + * properties into the object, but a developer may override this to support + * more behaviour.

+ *

You can perform extra processing on state save and restore by attaching + * handlers to the {@link #beforestaterestore}, {@link #staterestore}, + * {@link #beforestatesave} and {@link #statesave} events.

+ */ + stateful: true, + + /** + * @cfg {String} stateId + * The unique id for this object to use for state management purposes. + *

See {@link #stateful} for an explanation of saving and restoring state.

+ */ + + /** + * @cfg {Array} stateEvents + *

An array of events that, when fired, should trigger this object to + * save its state (defaults to none). stateEvents may be any type + * of event supported by this object, including browser or custom events + * (e.g., ['click', 'customerchange']).

+ *

See {@link #stateful} for an explanation of saving and + * restoring object state.

+ */ + + /** + * @cfg {Number} saveBuffer A buffer to be applied if many state events are fired within + * a short period. Defaults to 100. + */ + saveDelay: 100, + + autoGenIdRe: /^((\w+-)|(ext-comp-))\d{4,}$/i, + + constructor: function(config) { + var me = this; + + config = config || {}; + if (Ext.isDefined(config.stateful)) { + me.stateful = config.stateful; + } + if (Ext.isDefined(config.saveDelay)) { + me.saveDelay = config.saveDelay; + } + me.stateId = config.stateId; + + if (!me.stateEvents) { + me.stateEvents = []; + } + if (config.stateEvents) { + me.stateEvents.concat(config.stateEvents); + } + this.addEvents( + /** + * @event beforestaterestore + * Fires before the state of the object is restored. Return false from an event handler to stop the restore. + * @param {Ext.state.Stateful} this + * @param {Object} state The hash of state values returned from the StateProvider. If this + * event is not vetoed, then the state object is passed to applyState. By default, + * that simply copies property values into this object. The method maybe overriden to + * provide custom state restoration. + */ + 'beforestaterestore', + + /** + * @event staterestore + * Fires after the state of the object is restored. + * @param {Ext.state.Stateful} this + * @param {Object} state The hash of state values returned from the StateProvider. This is passed + * to applyState. By default, that simply copies property values into this + * object. The method maybe overriden to provide custom state restoration. + */ + 'staterestore', + + /** + * @event beforestatesave + * Fires before the state of the object is saved to the configured state provider. Return false to stop the save. + * @param {Ext.state.Stateful} this + * @param {Object} state The hash of state values. This is determined by calling + * getState() on the object. This method must be provided by the + * developer to return whetever representation of state is required, by default, Ext.state.Stateful + * has a null implementation. + */ + 'beforestatesave', + + /** + * @event statesave + * Fires after the state of the object is saved to the configured state provider. + * @param {Ext.state.Stateful} this + * @param {Object} state The hash of state values. This is determined by calling + * getState() on the object. This method must be provided by the + * developer to return whetever representation of state is required, by default, Ext.state.Stateful + * has a null implementation. + */ + 'statesave' + ); + me.mixins.observable.constructor.call(me); + if (me.stateful !== false) { + me.initStateEvents(); + me.initState(); + } + }, + + /** + * Initializes any state events for this object. + * @private + */ + initStateEvents: function() { + this.addStateEvents(this.stateEvents); + }, + + /** + * Add events that will trigger the state to be saved. + * @param {String/Array} events The event name or an array of event names. + */ + addStateEvents: function(events){ + if (!Ext.isArray(events)) { + events = [events]; + } + + var me = this, + i = 0, + len = events.length; + + for (; i < len; ++i) { + me.on(events[i], me.onStateChange, me); + } + }, + + /** + * This method is called when any of the {@link #stateEvents} are fired. + * @private + */ + onStateChange: function(){ + var me = this, + delay = me.saveDelay; + + if (delay > 0) { + if (!me.stateTask) { + me.stateTask = Ext.create('Ext.util.DelayedTask', me.saveState, me); + } + me.stateTask.delay(me.saveDelay); + } else { + me.saveState(); + } + }, + + /** + * Saves the state of the object to the persistence store. + * @private + */ + saveState: function() { + var me = this, + id, + state; + + if (me.stateful !== false) { + id = me.getStateId(); + if (id) { + state = me.getState(); + if (me.fireEvent('beforestatesave', me, state) !== false) { + Ext.state.Manager.set(id, state); + me.fireEvent('statesave', me, state); + } + } + } + }, + + /** + * Gets the current state of the object. By default this function returns null, + * it should be overridden in subclasses to implement methods for getting the state. + * @return {Object} The current state + */ + getState: function(){ + return null; + }, + + /** + * Applies the state to the object. This should be overridden in subclasses to do + * more complex state operations. By default it applies the state properties onto + * the current object. + * @param {Object} state The state + */ + applyState: function(state) { + if (state) { + Ext.apply(this, state); + } + }, + + /** + * Gets the state id for this object. + * @return {String} The state id, null if not found. + */ + getStateId: function() { + var me = this, + id = me.stateId; + + if (!id) { + id = me.autoGenIdRe.test(String(me.id)) ? null : me.id; + } + return id; + }, + + /** + * Initializes the state of the object upon construction. + * @private + */ + initState: function(){ + var me = this, + id = me.getStateId(), + state; + + if (me.stateful !== false) { + if (id) { + state = Ext.state.Manager.get(id); + if (state) { + state = Ext.apply({}, state); + if (me.fireEvent('beforestaterestore', me, state) !== false) { + me.applyState(state); + me.fireEvent('staterestore', me, state); + } + } + } + } + }, + + /** + * Destroys this stateful object. + */ + destroy: function(){ + var task = this.stateTask; + if (task) { + task.cancel(); + } + this.clearListeners(); + + } + +});