]> git.proxmox.com Git - extjs.git/blame - extjs/classic/classic/src/state/Stateful.js
add extjs 6.0.1 sources
[extjs.git] / extjs / classic / classic / src / state / Stateful.js
CommitLineData
6527f429
DM
1/**\r
2 * @class Ext.state.Stateful\r
3 * A mixin for being able to save the state of an object to an underlying\r
4 * {@link Ext.state.Provider}.\r
5 */\r
6Ext.define('Ext.state.Stateful', {\r
7 mixinId: 'state',\r
8\r
9 requires: [\r
10 'Ext.state.Manager',\r
11 'Ext.util.TaskRunner'\r
12 ],\r
13\r
14 /**\r
15 * @cfg {Boolean} stateful\r
16 * A flag which causes the object to attempt to restore the state of\r
17 * internal properties from a saved state on startup. The object must have\r
18 * a {@link #stateId} for state to be managed.\r
19 *\r
20 * Auto-generated ids are not guaranteed to be stable across page loads and\r
21 * cannot be relied upon to save and restore the same state for a object.\r
22 *\r
23 * For state saving to work, the state manager's provider must have been\r
24 * set to an implementation of {@link Ext.state.Provider} which overrides the\r
25 * {@link Ext.state.Provider#set set} and {@link Ext.state.Provider#get get}\r
26 * methods to save and recall name/value pairs. A built-in implementation,\r
27 * {@link Ext.state.CookieProvider} is available.\r
28 *\r
29 * To set the state provider for the current page:\r
30 *\r
31 * Ext.state.Manager.setProvider(new Ext.state.CookieProvider({\r
32 * expires: new Date(new Date().getTime()+(1000*60*60*24*7)), //7 days from now\r
33 * }));\r
34 *\r
35 * A stateful object attempts to save state when one of the events\r
36 * listed in the {@link #stateEvents} configuration fires.\r
37 *\r
38 * To save state, a stateful object first serializes its state by\r
39 * calling *{@link #getState}*.\r
40 *\r
41 * The Component base class implements {@link #getState} to save its width and height within the state\r
42 * only if they were initially configured, and have changed from the configured value.\r
43 *\r
44 * The Panel class saves its collapsed state in addition to that.\r
45 *\r
46 * The Grid class saves its column state and store state (sorters and filters and grouper) in addition to its superclass state.\r
47 *\r
48 * If there is more application state to be save, the developer must provide an implementation which\r
49 * first calls the superclass method to inherit the above behaviour, and then injects new properties\r
50 * into the returned object.\r
51 *\r
52 * The value yielded by getState is passed to {@link Ext.state.Manager#set}\r
53 * which uses the configured {@link Ext.state.Provider} to save the object\r
54 * keyed by the {@link #stateId}.\r
55 *\r
56 * During construction, a stateful object attempts to *restore* its state by calling\r
57 * {@link Ext.state.Manager#get} passing the {@link #stateId}\r
58 *\r
59 * The resulting object is passed to {@link #applyState}*. The default implementation of\r
60 * {@link #applyState} simply copies properties into the object, but a developer may\r
61 * override this to support restoration of more complex application state.\r
62 *\r
63 * You can perform extra processing on state save and restore by attaching\r
64 * handlers to the {@link #beforestaterestore}, {@link #staterestore},\r
65 * {@link #beforestatesave} and {@link #statesave} events.\r
66 */\r
67 stateful: false,\r
68\r
69 /**\r
70 * @cfg {String} stateId\r
71 * The unique id for this object to use for state management purposes.\r
72 *\r
73 * See {@link #stateful} for an explanation of saving and restoring state.\r
74 */\r
75\r
76 /**\r
77 * @cfg {String[]} stateEvents\r
78 * An array of events that, when fired, should trigger this object to\r
79 * save its state. Defaults to none. `stateEvents` may be any type\r
80 * of event supported by this object, including browser or custom events\r
81 * (e.g., `['click', 'customerchange']`).\r
82 *\r
83 * See `{@link #stateful}` for an explanation of saving and\r
84 * restoring object state.\r
85 */\r
86\r
87 /**\r
88 * @cfg {Number} saveDelay\r
89 * A buffer to be applied if many state events are fired within a short period.\r
90 */\r
91 saveDelay: 100,\r
92\r
93 /**\r
94 * @event beforestaterestore\r
95 * Fires before the state of the object is restored. Return false from an event handler to stop the restore.\r
96 * @param {Ext.state.Stateful} this\r
97 * @param {Object} state The hash of state values returned from the StateProvider. If this\r
98 * event is not vetoed, then the state object is passed to *`applyState`*. By default,\r
99 * that simply copies property values into this object. The method maybe overriden to\r
100 * provide custom state restoration.\r
101 */\r
102\r
103 /**\r
104 * @event staterestore\r
105 * Fires after the state of the object is restored.\r
106 * @param {Ext.state.Stateful} this\r
107 * @param {Object} state The hash of state values returned from the StateProvider. This is passed\r
108 * to *`applyState`*. By default, that simply copies property values into this\r
109 * object. The method maybe overridden to provide custom state restoration.\r
110 */\r
111\r
112 /**\r
113 * @event beforestatesave\r
114 * Fires before the state of the object is saved to the configured state provider. Return false to stop the save.\r
115 * @param {Ext.state.Stateful} this\r
116 * @param {Object} state The hash of state values. This is determined by calling\r
117 * *`getState()`* on the object. This method must be provided by the\r
118 * developer to return whatever representation of state is required, by default, Ext.state.Stateful\r
119 * has a null implementation.\r
120 */\r
121\r
122 /**\r
123 * @event statesave\r
124 * Fires after the state of the object is saved to the configured state provider.\r
125 * @param {Ext.state.Stateful} this\r
126 * @param {Object} state The hash of state values. This is determined by calling\r
127 * *`getState()`* on the object. This method must be provided by the\r
128 * developer to return whatever representation of state is required, by default, Ext.state.Stateful\r
129 * has a null implementation.\r
130 */\r
131\r
132 constructor: function() {\r
133 var me = this;\r
134\r
135 if (!me.stateEvents) {\r
136 me.stateEvents = [];\r
137 }\r
138\r
139 if (me.stateful !== false) {\r
140 me.addStateEvents(me.stateEvents);\r
141 me.initState();\r
142 }\r
143 },\r
144\r
145 /**\r
146 * Add events that will trigger the state to be saved. If the first argument is an\r
147 * array, each element of that array is the name of a state event. Otherwise, each\r
148 * argument passed to this method is the name of a state event.\r
149 *\r
150 * @param {String/String[]} events The event name or an array of event names.\r
151 */\r
152 addStateEvents: function (events) {\r
153 var me = this,\r
154 i, event, stateEventsByName,\r
155 eventArray;\r
156\r
157 if (me.stateful && me.getStateId()) {\r
158 eventArray = (typeof events === 'string') ? arguments : events;\r
159\r
160 stateEventsByName = me.stateEventsByName || (me.stateEventsByName = {});\r
161\r
162 for (i = eventArray.length; i--; ) {\r
163 event = eventArray[i];\r
164\r
165 if (event && !stateEventsByName[event]) {\r
166 stateEventsByName[event] = 1;\r
167 me.on(event, me.onStateChange, me);\r
168 }\r
169 }\r
170 }\r
171 },\r
172\r
173 /**\r
174 * This method is called when any of the {@link #stateEvents} are fired.\r
175 * @private\r
176 */\r
177 onStateChange: function(){\r
178 var me = this,\r
179 delay = me.saveDelay,\r
180 statics, runner;\r
181\r
182 if (!me.stateful) {\r
183 return;\r
184 }\r
185\r
186 if (delay) {\r
187 if (!me.stateTask) {\r
188 statics = Ext.state.Stateful;\r
189 runner = statics.runner || (statics.runner = new Ext.util.TaskRunner());\r
190\r
191 me.stateTask = runner.newTask({\r
192 run: me.saveState,\r
193 scope: me,\r
194 interval: delay,\r
195 repeat: 1,\r
196 fireIdleEvent: false\r
197 });\r
198 }\r
199\r
200 me.stateTask.start();\r
201 } else {\r
202 me.saveState();\r
203 }\r
204 },\r
205\r
206 /**\r
207 * Saves the state of the object to the persistence store.\r
208 */\r
209 saveState: function() {\r
210 var me = this,\r
211 id = me.stateful && me.getStateId(),\r
212 hasListeners = me.hasListeners,\r
213 plugins,\r
214 plugin,\r
215 i, len,\r
216 state,\r
217 pluginState;\r
218\r
219 if (id) {\r
220 state = me.getState() || {}; //pass along for custom interactions\r
221\r
222 /*\r
223 * Gather state from those plugins that implement a getState method\r
224 */\r
225 plugins = me.getPlugins() || [];\r
226 for (i = 0, len = plugins.length; i < len; i++) {\r
227 plugin = plugins[i];\r
228 if (plugin && plugin.getState) {\r
229 pluginState = plugin.getState(state);\r
230 if (pluginState && !state[plugin.ptype]) { //first duplicate plugin wins\r
231 state[plugin.ptype] = pluginState;\r
232 }\r
233 }\r
234 }\r
235\r
236 if (!hasListeners.beforestatesave || me.fireEvent('beforestatesave', me, state) !== false) {\r
237 Ext.state.Manager.set(id, state);\r
238 if (hasListeners.statesave) {\r
239 me.fireEvent('statesave', me, state);\r
240 }\r
241 }\r
242 }\r
243 },\r
244\r
245 /**\r
246 * Gets the current state of the object. By default this function returns null,\r
247 * it should be overridden in subclasses to implement methods for getting the state.\r
248 * @return {Object} The current state\r
249 */\r
250 getState: function(){\r
251 return null;\r
252 },\r
253\r
254 /**\r
255 * Applies the state to the object. This should be overridden in subclasses to do\r
256 * more complex state operations. By default it applies the state properties onto\r
257 * the current object.\r
258 * @param {Object} state The state\r
259 */\r
260 applyState: function(state) {\r
261 if (state) {\r
262 Ext.apply(this, state);\r
263 }\r
264 },\r
265\r
266 /**\r
267 * Gets the state id for this object.\r
268 * @return {String} The 'stateId' or the implicit 'id' specified by component configuration.\r
269 * @private\r
270 */\r
271 getStateId: function() {\r
272 var me = this;\r
273 return me.stateId || (me.autoGenId ? null : me.id);\r
274 },\r
275\r
276 /**\r
277 * Initializes the state of the object upon construction.\r
278 * @private\r
279 */\r
280 initState: function(){\r
281 var me = this,\r
282 id = me.stateful && me.getStateId(),\r
283 hasListeners = me.hasListeners,\r
284 state,\r
285 combinedState,\r
286 i, len,\r
287 plugins,\r
288 plugin,\r
289 pluginType;\r
290\r
291 if (id) {\r
292 combinedState = Ext.state.Manager.get(id);\r
293 if (combinedState) {\r
294 state = Ext.apply({}, combinedState);\r
295 if (!hasListeners.beforestaterestore || me.fireEvent('beforestaterestore', me, combinedState) !== false) {\r
296\r
297 //Notify all plugins FIRST (if interested) in new state\r
298 plugins = me.getPlugins() || [];\r
299 for (i = 0, len = plugins.length; i < len; i++) {\r
300 plugin = plugins[i];\r
301 if (plugin) {\r
302 pluginType = plugin.ptype;\r
303 if (plugin.applyState) {\r
304 plugin.applyState(state[pluginType], combinedState);\r
305 }\r
306 delete state[pluginType]; //clean to prevent unwanted props on the component in final phase\r
307 }\r
308 }\r
309\r
310 me.applyState(state);\r
311 if (hasListeners.staterestore) {\r
312 me.fireEvent('staterestore', me, combinedState);\r
313 }\r
314 }\r
315 }\r
316 }\r
317 },\r
318\r
319 /**\r
320 * Conditionally saves a single property from this object to the given state object.\r
321 * The idea is to only save state which has changed from the initial state so that\r
322 * current software settings do not override future software settings. Only those\r
323 * values that are user-changed state should be saved.\r
324 *\r
325 * @param {String} propName The name of the property to save.\r
326 * @param {Object} state The state object in to which to save the property.\r
327 * @param {String} stateName (optional) The name to use for the property in state.\r
328 * @return {Boolean} True if the property was saved, false if not.\r
329 */\r
330 savePropToState: function (propName, state, stateName) {\r
331 var me = this,\r
332 value = me[propName],\r
333 config = me.initialConfig;\r
334\r
335 if (me.hasOwnProperty(propName)) {\r
336 if (!config || config[propName] !== value) {\r
337 if (state) {\r
338 state[stateName || propName] = value;\r
339 }\r
340 return true;\r
341 }\r
342 }\r
343 return false;\r
344 },\r
345\r
346 /**\r
347 * Gathers additional named properties of the instance and adds their current values\r
348 * to the passed state object.\r
349 * @param {String/String[]} propNames The name (or array of names) of the property to save.\r
350 * @param {Object} state The state object in to which to save the property values.\r
351 * @return {Object} state\r
352 */\r
353 savePropsToState: function (propNames, state) {\r
354 var me = this,\r
355 i, n;\r
356\r
357 if (typeof propNames === 'string') {\r
358 me.savePropToState(propNames, state);\r
359 } else {\r
360 for (i = 0, n = propNames.length; i < n; ++i) {\r
361 me.savePropToState(propNames[i], state);\r
362 }\r
363 }\r
364\r
365 return state;\r
366 },\r
367\r
368 /**\r
369 * Destroys this stateful object.\r
370 */\r
371 destroy: function(){\r
372 var me = this,\r
373 task = me.stateTask;\r
374\r
375 if (task) {\r
376 task.destroy();\r
377 me.stateTask = null;\r
378 }\r
379\r
380 me.clearListeners();\r
381 }\r
382});\r