PDA

View Full Version : Ext.ux.state.provider.OmniProvider w/ Direct



willf1976
29 Jul 2009, 1:42 PM
Hi all


I have made a new provider for state management that offers a much more robust feature set than anything I was able to find out there already.


Key Features:

Allows states to be saved with names so you can allow users to save and load presets for how they want their components to be configured.


Allows states to be saved, loaded and copied between: cookies, ajax and ExtDirect (allows for remotely or locally saved states, or for any sort of hybrid of the above you would prefer to use).


Allows batch saving and loading of states and direct control over when a state is saved or applied opening up more flexibility in how you interact with your state.

I have attached some sample files to this for you to look over and see how the class works:

omniProviderAjaxAndCookieHybred.html – this file demonstrates saving and loading named states via ajax while the currently displayed state is saved locally in a cookie


omniProviderAllDirect.html – this file demonstrates using ExtDirect as your state provider


To learn how to use the class please look over the documentation in the class – please read over the comments relating to events, public properties, and public functions.

Let me know any bugs you find or additional features you would like to see.

Note: I have tested all the features in this class except for the "components" array that can be passed as an argument to certain functions. I haven't had need of this feature in any of my code yet. If you try out this feature and find any bugs please let me know.

Below is the code for class:



/*
author: Will Ferrer
date: 08/03/09
history:
07/27/09 -- posted to ExtJS forums
08/03/09 -- fixed a minor typo in the code documentation, changed accidental references to Ext.state.CookieProvider to Ext.ux.state.provider.OmniProvider
*/
/**
* @class Ext.ux.state.provider.OmniProvider
* @extends Ext.state.Provider
* A class that greatly extends the capabilities of Ext.state.Provider to allow it to handle multiple named states, multiple types of state sources and a great many other enhancements to the class.
* @constructor
* @param {Object} config The config object
* @xtype ux-state-provider-omniprovider
*/

Ext.namespace("Ext.ux.state.provider");

Ext.ux.state.provider.OmniProvider = function(config){
//event 'statechange' is already included in Ext.state.Provider and will fire on: 'set' and 'clear'
Ext.apply(this, config);
Ext.ux.state.provider.OmniProvider.superclass.constructor.call(this);
this.addEvents(
/**
* @event beforereadstate
* Fires right before the readState function is run
* @param {Ext.ux.state.provider.OmniProvider} this
* @param {String} stateGroupId
* @param {Boolean} triggerApplyAll
* @param {String} sourceType
* @param {Array} components
* @param {Object} extra
*/
'beforereadstate',
/**
* @event beforeupdatestate
* Fires right before the updateState function is run
* @param {Ext.ux.state.provider.OmniProvider} this
* @param {Object} state
* @param {String} stateGroupId
* @param {String} sourceType
* @param {Object} extra
*/
'beforeupdatestate',
/**
* @event beforedestroystate
* Fires right before the destroyState function is run
* @param {Ext.ux.state.provider.OmniProvider} this
* @param {Array} stateNames
* @param {String} stateGroupId
* @param {String} sourceType
* @param {Object} extra
*/
'beforedestroystate',
/**
* @event beforecopystate
* Fires right before the copyState function is run
* @param {Ext.ux.state.provider.OmniProvider} this
* @param {String} fromSourceType
* @param {String} toSourceType
* @param {String} stateGroupId
* @param {String} newStateGroupId
*/
'beforecopystate',
/**
* @event beforeapplyall
* Fires right before the applyAll function is run
* @param {Ext.ux.state.provider.OmniProvider} this
* @param {String} stateGroupId
* @param {Array} components
*/
'beforeapplyall',
/**
* @event beforesaveall
* Fires right before the saveAll function is run
* @param {Ext.ux.state.provider.OmniProvider} this
* @param {String} stateGroupId
* @param {Array} components
*/
'beforesaveall',
/**
* @event readstatesuccess
* Fires when a state has been read into the provider.
* @param {Ext.ux.state.provider.OmniProvider} this
* @param {String} sourceType
* @param {String} stateGroupId
* @param {Boolean} triggerApplyAll
* @param {Array} components
* @param {Object} extra
*/
'readstatesuccess',
/**
* @event updatestatesuccess
* Fires when a state has been updated.
* @param {Ext.ux.state.provider.OmniProvider} this
* @param {String} sourceType
* @param {Object} state
* @param {String} stateGroupId
* @param {Object} extra
*/
'updatestatesuccess',
/**
* @event destroystatesuccess
* Fires when a state has been destroyed.
* @param {Ext.ux.state.provider.OmniProvider} this
* @param {String} sourceType
* @param {Object} stateNames
* @param {String} stateGroupId
* @param {Object} extra
*/
'destroystatesuccess',
/**
* @event copystatereadsuccess
* Fires when a state has been read into the provider and is about to be coppied.
* @param {Ext.ux.state.provider.OmniProvider} this
* @param {Object} copyStateSettings
*/
'copystatereadsuccess',
/**
* @event copystateupdatesuccess
* Fires when the copy state process completes.
* @param {Ext.ux.state.provider.OmniProvider} this
* @param {Object} copyStateSettings
*/
'copystateupdatesuccess',
/**
* @event applyallsuccess
* Fires when applyAll function has completed.
* @param {Ext.ux.state.provider.OmniProvider} this
* @param {String} stateGroupId
* @param {Array} components
*/
'applyallsuccess',
/**
* @event saveallsuccess
* Fires when saveAll function has completed.
* @param {Ext.ux.state.provider.OmniProvider} this
* @param {String} stateGroupId
* @param {Array} components
*/
'saveallsuccess',
/**
* @event readstatefailure
* Fires when readState has failed.
* @param {Ext.ux.state.provider.OmniProvider} this
* @param {String} sourceType
* @param {Object} e
* @param {Object} Response
* @param {String} stateGroupId
* @param {Boolean} triggerApplyAll
* @param {Array} components
* @param {Object} extra
*/
'readstatefailure',
/**
* @event updatestatefailure
* Fires when updateState has failed.
* @param {Ext.ux.state.provider.OmniProvider} this
* @param {String} sourceType
* @param {Object} e
* @param {Object} Response
* @param {String} stateGroupId
* @param {Object} extra
*/
'updatestatefailure',
/**
* @event destroystatefailure
* Fires when destroyState has failed.
* @param {Ext.ux.state.provider.OmniProvider} this
* @param {String} sourceType
* @param {Object} e
* @param {Object} Response
* @param {Array} stateNames
* @param {String} stateGroupId
* @param {Object} extra
*/
'destroystatefailure',
/**
* @event copystatereadfailure
* Fires when copyState has failed during it's read operation.
* @param {Ext.ux.state.provider.OmniProvider} this
* @param {Object} copyStateSettings
*/
'copystatereadfailure',
/**
* @event copystateupdatefailure
* Fires when copyState has failed during it's update operation.
* @param {Ext.ux.state.provider.OmniProvider} this
* @param {Object} copyStateSettings
*/
'copystateupdatefailure'
);

//These listeners are used to allow the copyState function to complete it's opperation:
this.on('readstatesuccess', function () {
var extra = arguments[arguments.length-1];
var copyStateSettings = (extra && extra.copyStateSettings)?extra.copyStateSettings:null;
if (copyStateSettings) {
this.fireEvent("copystatereadsuccess", this, copyStateSettings);
var updateStateGroupId = (copyStateSettings.newStateGroupId)?copyStateSettings.newStateGroupId:this.stateGroupId;
this.updateState(this.tempState, updateStateGroupId, copyStateSettings.toSourceType, extra);
this.tempState = {};
}
}, this);

this.on('readstatefailure', function () {
var extra = arguments[arguments.length-1];
var copyStateSettings = (extra && extra.copyStateSettings)?extra.copyStateSettings:null;
if (copyStateSettings) {
this.fireEvent("copystatereadfailure", this, copyStateSettings);
this.tempState = {};
}
}, this);

this.on('updatestatesuccess', function () {
var extra = arguments[arguments.length-1];
var copyStateSettings = (extra && extra.copyStateSettings)?extra.copyStateSettings:null;
if (copyStateSettings) {
this.fireEvent("copystateupdatesuccess", this, copyStateSettings);
this.tempState = {};
}
}, this);

this.on('updatestatefailure', function () {
var extra = arguments[arguments.length-1];
var copyStateSettings = (extra && extra.copyStateSettings)?extra.copyStateSettings:null;
if (copyStateSettings) {
this.fireEvent("copystateupdatefailure", this, copyStateSettings);
this.tempState = {};
}
}, this);

if (this.autoReadStateOnConstruct) {
this.readState();
}

};
Ext.extend(Ext.ux.state.provider.OmniProvider, Ext.state.Provider, {
//Public Properties:
/**
* @cfg {Boolean} debug
* whether or not to use console.log to log failed connection attemps and other errors (defaults to true).
*
*/
debug : true,
/**
* @cfg {String} sourceType
* the source used for states -- may be 'cookie' (uses modified version of Ext.state.CookieProvider code), 'ajax' (uses ajax request with data formated as json), 'direct' (uses api for ExtDirect) (defaults to 'cookie').
*
*/
sourceType : 'cookie',
/**
* @cfg {Boolean} protectState
* protect the currently loaded state so that other functions that attempt to directly alter or delete it are defeated -- I added this feature because several components will try to modify or delete elements of the state and in some circumstances this can cause problems (defaults to false).
*
*/
protectState : false,
/**
* @cfg {Mixed} stateGroupId
* allows you to save configurations of multiple components in a 'set'. This will allow for multiple state configurations to saved and retrieved as part of a group (defaults to null).
*
*/
stateGroupId : null,
/**
* @cfg {Boolean} autoSaveState
* may be set to false in order to prevent components from saving their own state configurations -- set this to false when you want a state to be saved manually or by another function (defaults to true).
*
*/
autoSaveState : true,
/**
* @cfg {Boolean} autoGiveStateOnGet
* may be set to false in order to prevent components from applying their own state configurations -- set this to false when you want a state to be applied manually or by another function (defaults to true).
*
*/
autoGiveStateOnGet : true,
/**
* @cfg {Boolean} autoReadStateOnGet
* whether or not to repload the state each time a get request comes in from a component -- seldom used feature (defaults to false).
*
*/
autoReadStateOnGet : false,
/**
* @cfg {Boolean} autoReadStateOnConstruct
* whether or not to read the state into memory when provider is constructed (defaults to true).
*
*/
autoReadStateOnConstruct : true,
/**
* @cfg {Object} cookieConfig
* configurations used when saving and retrieving states from a cookie -- does not generaly need to be modified but may be used to overide the settings in defaultCookieConfig. Used when sourceType is set to 'cookie' (defaults to {}).
*
*/
cookieConfig : {},
/**
* @cfg {Object} api
* An object containing referances to the functions to use when making ExtDirect calls to read, update and destroy the state (generally created using Ext.Direct.addProvider). The functions must connect to remote functions that do the following:
* read($stateGroupId, $triggerApplyAll, $components, $extra) -- remote function must return: {success : [boolean], result : [state_object]}
* update($state, $stateGroupId, $extra) -- remote function must return: {success : [boolean], result : [message]}
* destroy($stateNames, $stateGroupId, $extra) -- remote function must return: {success : [boolean], result : [message]}
*/
api : {
read : null,
update : null,
destroy : null
},
/**
* @cfg {Object} ajaxConfig
* configuration for ajax calls to read, update and destroy the state. Generally you will only need to define the 'url' properties but other things may be set on this configuration as well. omniProvider will make the following calls to your ajax script:
* action='read'&components=[json_encoded_array_of_components]&stateGroupId=[stateGroupId] -- remote function must return json encoded: {success : [boolean], result : [state_object]}
* action='update'&stateGroupId=[stateGroupId]&state=[json_encoded_state_object] -- remote function must return json encoded: {success : [boolean], result : [message]}
* action='destroy'&stateGroupId=[stateGroupId]&stateNames=[json_encoded_array_of_state_names] -- remote function must return json encoded: {success : [boolean], result : [message]}
*/
ajaxConfig : {
url: null
},
// Private Properties:
/**
* @private internal config {Object} specialCaseGetState
* flag used when all components are instructed to restore their state via the applyAll function.
*
*/
specialCaseGetState : false,
/**
* @private internal config {Object} specialCaseSetState
* flag used when all components are instructed to save their state via the saveAll function.
*
*/
specialCaseSetState : false,
/**
* @private internal config {Object} tempSaveState
* used when a batch save takes place (via the saveAll function) to record all the state info to save.
*
*/
tempSaveState : {},
/**
* @private internal config {Object} tempClearStateNames
* used when a batch save takes place to record all the state info to clear.
*
*/
tempClearStateNames : [],
/**
* @private internal config {Object} tempState
* used when a copyState function is called to read the contents of a state into a property of the class with out overwriting the state property.
*
*/
tempState : {},
/**
* @private internal config {Object} state
* State when object is instantiated, is over written when readState functions are called
*
*/
state : {},
/**
* @private internal config {Object} defaultStateGroupId
* used to store what the stateGroupId is before performning batch operations.
*
*/
defaultStateGroupId : '',
/**
* @private internal config {Object} defaultCookieConfig
* default config for cookieConfig -- over ridden by any values passed in with cookieConfig.
*
*/
defaultCookieConfig : {
path : "/",
expires : new Date(new Date().getTime()+(1000*60*60*24*7)), //7 days
domain : null,
secure : false
},
/**
* @private internal config {Object} errorMessages
* error messages that will be logged through the log function if debug is set to true.
*
*/
errorMessages : {
ajaxCouldNoteDecodeJson : 'omniProviderError-Ajax: could not decode remote json data:',
ajaxImproperlyFormatedReturn : 'omniProviderError-Ajax: response from server improperly formated -- should be {success:[true_or_false], result:{[error_message_or_state_information]}}:',
ajaxServerReportedAnError : 'omniProviderError-Ajax: the server reported an error',
ajaxServerDidNotRespond : 'omniProviderError-Ajax: Did not receive a response from the server',

directRead : 'omniProviderError-Direct: error with readStateDirect return data',
directUpdated : 'omniProviderError-Direct: error with updateStateDirect return data',
directDestroy : 'omniProviderError-Direct: error with destroyStateDirect return data'
},
//Public Functions:
/** Public Function: readState
* reads state into memory from either the sourceType defined for the class or from the sourceType passed to the function. This function routes to other functions depending on the sourceType
* @param {String} stateGroupId (Optional) A stateGroupId to over ride the stateGroupId set for the class -- using different stateGroupId allows you to save and load states using different names. Defaults to this.this.stateGroupId.
* @param {Boolean} triggerApplyAll (Optional) If set to true the applyAll function will be called after the readState function completes. Defaults to false.
* @param {Mixed} sourceType (Optional) May be set to: 'cookie', 'ajax' or 'direct'. Overides the sourceType set for the class to all the function to read in the state from a different source. Defaults to null.
* @param {Array} components (Optional) An array of component ids that may optionaly be passed in to cause just the states relating to these components to be loaded. Defaults to this.sourceType.
* @param {Object} extra (Optional) Values that will be passed along with the requests to read the state -- this is used largely by the copyState functions. Defaults to null.
* @return {Object} the state that was read by the function
*/
readState : function (stateGroupId, triggerApplyAll, sourceType, components, extra) {
var stateGroupId = (typeof(stateGroupId)!='undefined')?stateGroupId:this.stateGroupId;
var triggerApplyAll = (typeof(triggerApplyAll)!='undefined')?triggerApplyAll:false;
var components = (typeof(components)!='undefined')?components:null;
var components = (typeof(components)=='string')?[components]:components;
var sourceType = (typeof(sourceType)!='undefined')?sourceType:this.sourceType;
var extra = (typeof(extra)!='undefined')?extra:null;
var stateHolder = (extra && extra.stateHolder)?extra.stateHolder:'state';

this.fireEvent("beforereadstate", this, stateGroupId, triggerApplyAll, sourceType, components, extra);

switch (sourceType) {
case 'cookie':
this.readCookies(stateGroupId, triggerApplyAll, components, extra);
break;
case 'ajax':
this.readAjax(stateGroupId, triggerApplyAll, components, extra);
break;
case 'direct':
this.readDirect(stateGroupId, triggerApplyAll, components, extra);
break;
}

return this[stateHolder];
},
/** Public Function: updateState
* saves the state passed to the function to the source of sourceType either set for the class or passed to the function. This function routes to other functions depending on the sourceType
* @param {Object} state (Required) A state object to save
* @param {String} stateGroupId (Optional) A stateGroupId to over ride the stateGroupId set for the class -- using different stateGroupId allows you to save and load states using different name. Defaults to this.stateGroupId.
* @param {String} sourceType (Optional) May be set to: 'cookie', 'ajax' or 'direct'. Overides the sourceType set for the class to all the function to read in the state from a different source. Defaults to this.sourceType.
* @param {Mixed} extra (Optional) Values that will be passed along with the requests to read the state -- this is used largely by the copyState functions. Defaults to null.
* @return {Boolean} returns true
*/
updateState : function (state, stateGroupId, sourceType, extra) {
var stateGroupId = (typeof(stateGroupId)!='undefined')?stateGroupId:this.stateGroupId;
var sourceType = (typeof(sourceType)!='undefined')?sourceType:this.sourceType;
var extra = (typeof(extra)!='undefined')?extra:null;

this.fireEvent("beforeupdatestate", this, state, stateGroupId, sourceType, extra);
switch (sourceType) {
case 'cookie':
this.updateCookies(state, stateGroupId, extra);
break;
case 'ajax':
this.updateAjax(state, stateGroupId, extra);
break;
case 'direct':
this.updateDirect(state, stateGroupId, extra);
break;
}
return true;
},
/** Public Function: destroyState
* destroys the states whose names are passed in stateNames from sourceType either set for the class or passed to the function. This function routes to other functions depending on the sourceType
* @param {Array} stateNames (Required) An array of stateNames to destroy
* @param {String} stateGroupId (Optional) A stateGroupId to over ride the stateGroupId set for the class -- using different stateGroupId allows you to save and load states using different name. Defaults to this.stateGroupId
* @param {String} sourceType (Optional) May be set to: 'cookie', 'ajax' or 'direct'. Overides the sourceType set for the class to all the function to read in the state from a different source. Defaults to this.sourceType
* @param {Mixed} extra (Optional) Values that will be passed along with the requests to read the state -- this is used largely by the copyState functions. Defaults to null.
* @return {Boolean} returns true
*/
destroyState : function (stateNames, stateGroupId, sourceType, extra) {
var stateGroupId = (typeof(stateGroupId)!='undefined')?stateGroupId:this.stateGroupId;
var sourceType = (typeof(sourceType)!='undefined')?sourceType:this.sourceType;
var extra = (typeof(extra)!='undefined')?extra:null;
var stateNames = (typeof(stateNames)!='string')?stateNames:[stateNames];
this.fireEvent("beforedestroystate", this, stateNames, stateGroupId, sourceType, extra);

switch (sourceType) {
case 'cookie':
this.destroyCookies(stateNames, stateGroupId, extra);
break;
case 'ajax':
this.destroyAjax(stateNames, stateGroupId, extra);
break;
case 'direct':
this.destroyDirect(stateNames, stateGroupId, extra);
break;
}
return true;
},
/** Public Function: copyState
* starts the process to read in a state from a source and then update it to another source.
* @param {String} fromSourceType (Required) May be set to: 'cookie', 'ajax' or 'direct'. source from which to read.
* @param {String} toSourceType (Required) May be set to: 'cookie', 'ajax' or 'direct'. sourceType to copy the state to.
* @param {String} stateGroupId (Optional) A stateGroupId to over ride the stateGroupId set for the class -- using different stateGroupId allows you to save and load states using different name. Defaults to this.stateGroupId
* @param {String} newStateGroupId (Optional) The new stateGroupId to copy the state you have read to. Defaults to this.stateGroupId
* @return {Boolean} returns true
*/
copyState : function (fromSourceType, toSourceType, stateGroupId, newStateGroupId) {
var stateGroupId = (typeof(stateGroupId)!='undefined')?stateGroupId:this.stateGroupId;
var newStateGroupId = (typeof(newStateGroupId)!='undefined')?newStateGroupId:this.stateGroupId;

this.fireEvent("beforecopystate", this, fromSourceType, toSourceType, stateGroupId, newStateGroupId);

var extra = {
copyStateSettings :
{
stateGroupId : stateGroupId,
fromSourceType : fromSourceType,
newStateGroupId : newStateGroupId,
toSourceType : toSourceType
},
stateHolder : 'tempState'
};
this.readState(stateGroupId, false, fromSourceType, null, extra);
return true;
},
/** Public Function: applyAll
* runs a batch process to call the initState function on stateful components
* @param {String} stateGroupId (Optional) A stateGroupId to over ride the stateGroupId set for the class -- using different stateGroupId allows you to save and load states using different name. Defaults to this.stateGroupId;
* @param {Mixed} components An array of component ids that may optionaly be passed in to cause just these components to run their initState function. Defaults to null.
* @return {Boolean} returns true
*/
applyAll : function (stateGroupId, components) {
var stateGroupId = (typeof(stateGroupId)!='undefined')?stateGroupId:this.stateGroupId;
var components = (typeof(components)!='undefined')?components:null;
var components = (typeof(components)=='string')?[components]:components;

this.fireEvent("beforeapplyall", this, stateGroupId, components);
this.batch(stateGroupId, components,'specialCaseGetState', 'initState');
this.fireEvent("applyallsuccess", this, stateGroupId, components);
return true;
},
/** Public Function: saveAll
* runs a batch process to call the saveState function on stateful components
* @param {String} stateGroupId (Optional) A stateGroupId to over ride the stateGroupId set for the class -- using different stateGroupId allows you to save and load states using different name. Defaults to this.stateGroupId.
* @param {Mixed} components (Optional) An array of component ids that may optionaly be passed in to cause just these components to run their saveState function. Defaults to null.
* @return {Boolean} returns true
*/
saveAll : function (stateGroupId, components) {
var stateGroupId = (typeof(stateGroupId)!='undefined')?stateGroupId:this.stateGroupId;
var components = (typeof(components)!='undefined')?components:null;
var components = (typeof(components)=='string')?[components]:components;
this.fireEvent("beforesaveall", this, stateGroupId, components);
this.batch(stateGroupId, components, 'specialCaseSetState', 'saveState');

this.updateState(this.tempSaveState);
this.destroyState(this.tempClearStateNames);

this.tempSaveState = {};
this.tempClearStateNames = [];
this.fireEvent("saveallsuccess", this, stateGroupId, components);
return true;
},
/** Public Function: get
* modified standard get function called by the Ext.state.Manager when an component requests to read it's loaded state
* @param {String} name (Required) The key name.
* @param {Mixed} defaultValue (Optional) The default value to return if the key lookup does not match. Defaults to undefined.
* @return {Mixed} The state data or null.
*/
get : function (name, defaultValue) {
if (this.autoReadStateOnGet) {
this.readState(this.stateGroupId, false, this.sourceType, [name]);
}
if (this.autoGiveStateOnGet || this.specialCaseGetState) {
if (this.protectState) {
var returnData = {};
var stateObj = typeof this.state[name] == "undefined" ?
defaultValue : this.state[name];
Ext.apply(returnData, stateObj);
return returnData;
} else {
return typeof this.state[name] == "undefined" ?
defaultValue : this.state[name];
}
} else {
return null;
}
},
/** Public Function: set
* modified standard set function called by the Ext.state.Manager when an component requests to set it's state
* @param {String} name (Required) The key name.
* @param {Mixed} value (Required) The state data.
* @return {Boolean} returns true or false
*/
set : function (name, value) {
if (this.autoSaveState || this.specialCaseSetState) {
if(typeof value == "undefined" || value === null){
this.clear(name);
return;
}
if (this.specialCaseSetState) {
this.tempSaveState[name] = value;
} else {
var tempState = [];
tempState[name] = value;
this.updateState(tempState);
Ext.ux.state.provider.OmniProvider.superclass.set.call(this, name, value);
}
return true;
} else {
return false;
}

},
/** Public Function: clear
* modified standard clear function called by the Ext.state.Manager when an component requests to clear its
* @param {String} name (Required) The key name.
* @return {Boolean} true
*/
clear : function (name) {
if (this.specialCaseSetState) {
this.tempClearStateNames.push(name);
} else {
this.destroyState([name]);
Ext.ux.state.provider.OmniProvider.superclass.clear.call(this, name);
}
return true;
},
//Private Functions:
// private
readCookies : function(stateGroupId, triggerApplyAll, components, extra){
var stateHolder = (extra && extra.stateHolder)?extra.stateHolder:'state';
var cookies = {};
var c = document.cookie + ";";
var re = /\s?(.*?)=(.*?);/g;
var matches;
var regExString = "^ys\-" + stateGroupId + "\-";
var extraRegEx = (stateGroupId) ? new RegExp(regExString) : null;

while((matches = re.exec(c)) != null){
var name = matches[1];
var value = matches[2];
var nameFirst3 = name.substring(0,3);
var trimmedName = name.replace(extraRegEx, '');
var decodedValue = this.decodeValue(value);
if(name && nameFirst3 == "ys-" && (!extraRegEx || extraRegEx.test(name)) && (!components || this.inArray(components, name))){
cookies[trimmedName] = decodedValue;
}
}
this[stateHolder] = cookies;

this.fireEvent("readstatesuccess", this, 'cookie', stateGroupId, triggerApplyAll, components, extra);
if (triggerApplyAll) {
this.applyAll(stateGroupId, components);
}
},
// private
updateCookies : function(state, stateGroupId, extra){
for (var obj in state) {
var curName = obj;
var curValue = state[obj];
this.updateCookie(stateGroupId, curName, curValue);
}
this.fireEvent("updatestatesuccess", this, 'cookie', state, stateGroupId, extra);
},
// private
updateCookie : function(stateGroupId, name, value){
curCookieConfig = this.cookieConfig;
Ext.applyIf(curCookieConfig, this.defaultCookieConfig);
var nameExtra = (stateGroupId) ? stateGroupId + "-" : '';
var cookieName = "ys-"+ nameExtra + name;
var cookieValue = this.encodeValue(value) +
((curCookieConfig.expires == null) ? "" : ("; expires=" + curCookieConfig.expires.toGMTString())) +
((curCookieConfig.path == null) ? "" : ("; path=" + curCookieConfig.path)) +
((curCookieConfig.domain == null) ? "" : ("; domain=" + curCookieConfig.domain)) +
((curCookieConfig.secure == true) ? "; secure" : "");

document.cookie = cookieName + "=" + cookieValue;
},
// private
destroyCookies : function(stateNames, stateGroupId, extra){
for (var n=0; n< stateNames.length; n++) {
var curName = stateNames[n];
this.destroyCookie(stateGroupId, curName);
}
this.fireEvent("destroystatesuccess", this, 'cookie', stateNames, stateGroupId, extra);
},
// private
destroyCookie : function(stateGroupId, name){
curCookieConfig = this.cookieConfig;
Ext.applyIf(curCookieConfig, this.defaultCookieConfig);
var nameExtra = (stateGroupId) ? stateGroupId + "-" : '';
document.cookie = "ys-" + nameExtra + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
((curCookieConfig.path == null) ? "" : ("; path=" + curCookieConfig.path)) +
((curCookieConfig.domain == null) ? "" : ("; domain=" + curCookieConfig.domain)) +
((curCookieConfig.secure == true) ? "; secure" : "");
},
//private
readAjax : function (stateGroupId, triggerApplyAll, components, extra) {
var curAjax = this.ajaxConfig;
var componentsEncoded = (components)?Ext.encode(components):null;
curAjax.params = {
action:'read',
components:componentsEncoded,
stateGroupId:stateGroupId
};
curAjax.triggerApplyAll = triggerApplyAll;
curAjax.extra = extra;
curAjax.components = components;
curAjax.stateGroupId = stateGroupId;
curAjax.success = this.onAjaxReadSuccess;
curAjax.failure = this.onAjaxReadFailure;
curAjax.scope = this;

Ext.Ajax.request(curAjax);
},
//private
onAjaxReadSuccess : function (response, options) {
var response = this.parseJSONReturn(response);

var stateGroupId = options.stateGroupId;
var triggerApplyAll = options.triggerApplyAll;
var components = options.components;
var extra = options.extra;

var success = response.success;
var stateHolder = (extra && extra.stateHolder)?extra.stateHolder:'state';

if (success) {
for (obj in response.result) {
response.result[obj] = Ext.decode(response.result[obj]);
}

this[stateHolder] = response.result;
this.fireEvent("readstatesuccess", this, 'ajax', stateGroupId, triggerApplyAll, components, extra);
if (triggerApplyAll) {
this.applyAll(stateGroupId, components);
}
} else {
this.fireEvent("readstatefailure", this, 'ajax', null, response, stateGroupId, triggerApplyAll, components, extra);
this.log(this.errorMessages.ajaxServerReportedAnError, 'onAjaxReadSuccess', this, 'ajax', response, stateGroupId, triggerApplyAll, components, extra);
}
},
//private
onAjaxReadFailure : function (e, options) {
var stateGroupId = options.stateGroupId;
var triggerApplyAll = options.triggerApplyAll;
var components = options.components;
var extra = options.extra;
this.fireEvent("readstatefailure", this, 'ajax', e, null, stateGroupId, triggerApplyAll, components, extra);
this.log(this.errorMessages.ajaxServerDidNotRespond, 'ajax', e, null, stateGroupId, triggerApplyAll, components, extra);
},
//private
updateAjax : function (state, stateGroupId, extra) {
var curAjax = this.ajaxConfig;
//The 'remove' function on the state obj screwed up the Ext.encode function so we make an object that doesn't have it:
var stateToEncode = {};
for (obj in state) {
if (obj != 'remove') {
stateToEncode[obj] = state[obj];
}
}
stateEncoded = Ext.encode(stateToEncode);

curAjax.params = {
action:'update',
state:stateEncoded,
stateGroupId:stateGroupId
};
curAjax.state = state;
curAjax.stateGroupId = stateGroupId;
curAjax.extra = extra;
curAjax.success = this.onAjaxUpdateSuccess;
curAjax.failure = this.onAjaxUpdateFailure;
curAjax.scope = this;
Ext.Ajax.request(curAjax);
},
//private
onAjaxUpdateSuccess : function (response, options) {
response = this.parseJSONReturn(response);
var success = response.success;
var state = options.state;
var stateGroupId = options.stateGroupId;
var extra = options.extra

if (success) {
this.fireEvent("updatestatesuccess", this, 'ajax', state, stateGroupId, extra);
} else {
this.fireEvent("updatestatefailure", this, 'ajax', null, response, state, stateGroupId, extra);
this.log(this.errorMessages.ajaxServerReportedAnError, 'onAjaxUpdateSuccess', this, 'ajax', null, response, state, stateGroupId, extra);
}
},
//private
onAjaxUpdateFailure : function (e, options) {
var state = options.state;
var stateGroupId = options.stateGroupId;
var extra = options.extra
this.fireEvent("updatestatefailure", this, 'ajax', e, null, state, stateGroupId, extra);
this.log(this.errorMessages.ajaxServerDidNotRespond, this, 'ajax', e, null, state, stateGroupId, extra);
},
//private
destroyAjax : function (stateNames, stateGroupId, extra) {
var curAjax = this.ajaxConfig;
var stateNamesEncoded = (stateNames)?Ext.encode(stateNames):null;
curAjax.params = {
action:'destroy',
stateNames:stateNamesEncoded,
stateGroupId:stateGroupId
};
curAjax.stateNames = stateNames;
curAjax.stateGroupId = stateGroupId;
curAjax.extra = extra;
curAjax.success = this.onAjaxDestroySuccess;
curAjax.failure = this.onAjaxDestroyFailure;
curAjax.scope = this;
Ext.Ajax.request(curAjax);
},
//private
onAjaxDestroySuccess : function (response, options) {
response = this.parseJSONReturn(response);
var stateNames = options.stateNames;
var stateGroupId = options.stateGroupId;
var extra = options.extra

var success = response.success;
if (success) {
this.fireEvent("destroystatesuccess", this, 'ajax', stateNames, stateGroupId, extra);
} else {
this.fireEvent("destroystatefailure", this, 'ajax', null, response, stateNames, stateGroupId, extra);
this.log(this.errorMessages.ajaxServerDidNotRespond, this, 'ajax', response, stateNames, stateGroupId, extra);
}
},
//private
onAjaxDestroyFailure : function (e, options) {
var stateNames = options.stateNames;
var stateGroupId = options.stateGroupId;
var extra = options.extra

this.fireEvent("destroystatefailure", this, 'ajax', e, null, stateNames, stateGroupId, extra);
this.log(this.errorMessages.ajaxServerDidNotRespond, this, 'ajax', e, stateNames, stateGroupId, extra);
},
//private
readDirect : function (stateGroupId, triggerApplyAll, components, extra) {
this.api.read(stateGroupId, triggerApplyAll, components, extra, this.onDirectReadSuccess, this);
},
//private
onDirectReadSuccess : function(response, e){
var transaction = e.getTransaction();
var stateGroupId = transaction.args[0];
var triggerApplyAll = transaction.args[1];
var components = transaction.args[2];
var extra = transaction.args[3];
var stateHolder = (extra && extra.stateHolder)?extra.stateHolder:'state';
var success = response.success;
if (success) {
this[stateHolder] = response.result;
this.fireEvent("readstatesuccess", this, 'direct', stateGroupId, triggerApplyAll, components, extra);
if (triggerApplyAll) {
this.applyAll(stateGroupId, components);
}
} else {
this.fireEvent("readstatefailure", this, 'direct', e, response, stateGroupId, triggerApplyAll, components, extra);
this.log(this.errorMessages.directRead, 'onDirectReadSuccess', this, 'direct', e, stateGroupId, triggerApplyAll, components, extra);
}
},
//private
updateDirect : function (state, stateGroupId, extra) {
var stateToSend = {};
for (obj in state) {
if (obj != 'remove') {
stateToSend[obj] = state[obj];
}
}
this.api.update(stateToSend, stateGroupId, extra, this.onDirectUpdateSuccess, this);
},
//private
onDirectUpdateSuccess : function(response, e){
var transaction = e.getTransaction();
var state = transaction.args[0];
var stateGroupId = transaction.args[1];
var extra = transaction.args[2];
var success = response.success;

if (success) {
this.fireEvent("updatestatesuccess", this, 'direct', state, stateGroupId, extra);
} else {
this.fireEvent("updatestatefailure", this, 'direct', e, response, state, stateGroupId, extra);
this.log(this.errorMessages.directUpdate, 'onDirectUpdateSuccess', this, 'direct', e, response, state, stateGroupId, extra);
}

},
//private
destroyDirect : function (stateNames, stateGroupId, extra) {
this.api.destroy(stateNames, stateGroupId, extra, this.onDirectDestroySuccess, this);
},
//private
onDirectDestroySuccess : function(response, e){
var transaction = e.getTransaction();
var stateNames = transaction.args[0];
var stateGroupId = transaction.args[1];
var extra = transaction.args[2];
var success = response.success;
if (success) {
this.fireEvent("destroystatesuccess", this, 'direct', stateNames, stateGroupId, extra);
} else {
this.fireEvent("destroystatefailure", this, 'direct', e, response, stateNames, stateGroupId, extra);
this.log(this.errorMessages.directDestroy, 'onAjaxDestroySuccess', this, 'direct', e, stateNames, stateGroupId, extra);
}
},
//private
parseJSONReturn : function (response) {
try {
var result = Ext.decode(response.responseText);
} catch(e) {
this.log(this.errorMessages.ajaxCouldNoteDecodeJson, e, response, options, this);
return false;
}
if (typeof(result.success) == 'undefined' || typeof(result.result)== 'undefined') {
this.log(this.errorMessages.ajaxImproperlyFormatedReturn, response, options, this);
return false;
}
return result;
},
//private
batch : function (stateGroupId, components, flag, callFunction) {
this.defaultStateGroupId = this.stateGroupId;
this.stateGroupId = (typeof(stateGroupId)!='undefined')?stateGroupId:this.stateGroupId;

this[flag] = true;

var curComponents = (components)?components:Ext.ComponentMgr.all.items;

for (var n=0; n<curComponents.length; n++) {
var curComp = curComponents[n];
curComp = (typeof(curComp)=='string')?Ext.getCmp(curComp):curComp;
if (typeof(curComp.stateful !== false)) {
curComp[callFunction]();
}
}

this[flag] = false;
this.stateGroupId = this.defaultStateGroupId;
},
//private
log : function() {
if (this.debug){
if(console) {
console.log.apply(console, arguments);
}
}
},
//private
inArray : function (array, value, caseSensitive) {
var i;
for (i=0; i < array.length; i++) {
// use === to check for Matches. ie., identical (===),
if(caseSensitive){ //performs match even the string is case sensitive
if (array[i].toLowerCase() == value.toLowerCase()) {
return true;
}
}else{
if (array[i] == value) {
return true;
}
}
}
return false;
}
});
Ext.reg('ux-state-provider-omniprovider', Ext.ux.state.provider.OmniProvider);

lechenique
30 Jul 2009, 2:42 PM
I just downloaded your extension and will start playing with it soon. If it works as expected, you will save me a whole lot of time.
Thanks in advance. I will let you know what I find.

Georgioa
31 Jul 2009, 1:11 AM
This seems great !

I'll need this in a couple of weeks. I'll give you feedback as soon as I test it !

Regards

willf1976
3 Aug 2009, 5:35 PM
Hi All

Many thanks to those Georgioa and lechenique for checking out the code. Looking forward to hearing what you guys find :).

I made a very minor revision today -- this literally does not change the functionality of the code at all but I fixed a few typos I found and figured I might as well post the updated version of the code:


/*
author: Will Ferrer
date: 08/03/09
history:
07/27/09 -- posted to ExtJS forums
08/03/09 -- fixed a minor typo in the code documentation, changed accidental references to Ext.state.CookieProvider to Ext.ux.state.provider.OmniProvider
*/
/**
* @class Ext.ux.state.provider.OmniProvider
* @extends Ext.state.Provider
* A class that greatly extends the capabilities of Ext.state.Provider to allow it to handle multiple named states, multiple types of state sources and a great many other enhancements to the class.
* @constructor
* @param {Object} config The config object
* @xtype ux-state-provider-omniprovider
*/

Ext.namespace("Ext.ux.state.provider");

Ext.ux.state.provider.OmniProvider = function(config){
//event 'statechange' is already included in Ext.state.Provider and will fire on: 'set' and 'clear'
Ext.apply(this, config);
Ext.ux.state.provider.OmniProvider.superclass.constructor.call(this);
this.addEvents(
/**
* @event beforereadstate
* Fires right before the readState function is run
* @param {Ext.ux.state.provider.OmniProvider} this
* @param {String} stateGroupId
* @param {Boolean} triggerApplyAll
* @param {String} sourceType
* @param {Array} components
* @param {Object} extra
*/
'beforereadstate',
/**
* @event beforeupdatestate
* Fires right before the updateState function is run
* @param {Ext.ux.state.provider.OmniProvider} this
* @param {Object} state
* @param {String} stateGroupId
* @param {String} sourceType
* @param {Object} extra
*/
'beforeupdatestate',
/**
* @event beforedestroystate
* Fires right before the destroyState function is run
* @param {Ext.ux.state.provider.OmniProvider} this
* @param {Array} stateNames
* @param {String} stateGroupId
* @param {String} sourceType
* @param {Object} extra
*/
'beforedestroystate',
/**
* @event beforecopystate
* Fires right before the copyState function is run
* @param {Ext.ux.state.provider.OmniProvider} this
* @param {String} fromSourceType
* @param {String} toSourceType
* @param {String} stateGroupId
* @param {String} newStateGroupId
*/
'beforecopystate',
/**
* @event beforeapplyall
* Fires right before the applyAll function is run
* @param {Ext.ux.state.provider.OmniProvider} this
* @param {String} stateGroupId
* @param {Array} components
*/
'beforeapplyall',
/**
* @event beforesaveall
* Fires right before the saveAll function is run
* @param {Ext.ux.state.provider.OmniProvider} this
* @param {String} stateGroupId
* @param {Array} components
*/
'beforesaveall',

/**
* @event readstatesuccess
* Fires when a state has been read into the provider.
* @param {Ext.ux.state.provider.OmniProvider} this
* @param {String} sourceType
* @param {String} stateGroupId
* @param {Boolean} triggerApplyAll
* @param {Array} components
* @param {Object} extra
*/
'readstatesuccess',
/**
* @event updatestatesuccess
* Fires when a state has been updated.
* @param {Ext.ux.state.provider.OmniProvider} this
* @param {String} sourceType
* @param {Object} state
* @param {String} stateGroupId
* @param {Object} extra
*/
'updatestatesuccess',
/**
* @event destroystatesuccess
* Fires when a state has been destroyed.
* @param {Ext.ux.state.provider.OmniProvider} this
* @param {String} sourceType
* @param {Object} stateNames
* @param {String} stateGroupId
* @param {Object} extra
*/
'destroystatesuccess',
/**
* @event copystatereadsuccess
* Fires when a state has been read into the provider and is about to be coppied.
* @param {Ext.ux.state.provider.OmniProvider} this
* @param {Object} copyStateSettings
*/
'copystatereadsuccess',
/**
* @event copystateupdatesuccess
* Fires when the copy state process completes.
* @param {Ext.ux.state.provider.OmniProvider} this
* @param {Object} copyStateSettings
*/
'copystateupdatesuccess',
/**
* @event applyallsuccess
* Fires when applyAll function has completed.
* @param {Ext.ux.state.provider.OmniProvider} this
* @param {String} stateGroupId
* @param {Array} components
*/
'applyallsuccess',
/**
* @event saveallsuccess
* Fires when saveAll function has completed.
* @param {Ext.ux.state.provider.OmniProvider} this
* @param {String} stateGroupId
* @param {Array} components
*/
'saveallsuccess',
/**
* @event readstatefailure
* Fires when readState has failed.
* @param {Ext.ux.state.provider.OmniProvider} this
* @param {String} sourceType
* @param {Object} e
* @param {Object} Response
* @param {String} stateGroupId
* @param {Boolean} triggerApplyAll
* @param {Array} components
* @param {Object} extra
*/
'readstatefailure',
/**
* @event updatestatefailure
* Fires when updateState has failed.
* @param {Ext.ux.state.provider.OmniProvider} this
* @param {String} sourceType
* @param {Object} e
* @param {Object} Response
* @param {String} stateGroupId
* @param {Object} extra
*/
'updatestatefailure',
/**
* @event destroystatefailure
* Fires when destroyState has failed.
* @param {Ext.ux.state.provider.OmniProvider} this
* @param {String} sourceType
* @param {Object} e
* @param {Object} Response
* @param {Array} stateNames
* @param {String} stateGroupId
* @param {Object} extra
*/
'destroystatefailure',
/**
* @event copystatereadfailure
* Fires when copyState has failed during it's read operation.
* @param {Ext.ux.state.provider.OmniProvider} this
* @param {Object} copyStateSettings
*/
'copystatereadfailure',
/**
* @event copystateupdatefailure
* Fires when copyState has failed during it's update operation.
* @param {Ext.ux.state.provider.OmniProvider} this
* @param {Object} copyStateSettings
*/
'copystateupdatefailure'
);

//These listeners are used to allow the copyState function to complete it's opperation:
this.on('readstatesuccess', function () {
var extra = arguments[arguments.length-1];
var copyStateSettings = (extra && extra.copyStateSettings)?extra.copyStateSettings:null;
if (copyStateSettings) {
this.fireEvent("copystatereadsuccess", this, copyStateSettings);
var updateStateGroupId = (copyStateSettings.newStateGroupId)?copyStateSettings.newStateGroupId:this.stateGroupId;
this.updateState(this.tempState, updateStateGroupId, copyStateSettings.toSourceType, extra);
this.tempState = {};
}
}, this);

this.on('readstatefailure', function () {
var extra = arguments[arguments.length-1];
var copyStateSettings = (extra && extra.copyStateSettings)?extra.copyStateSettings:null;
if (copyStateSettings) {
this.fireEvent("copystatereadfailure", this, copyStateSettings);
this.tempState = {};
}
}, this);

this.on('updatestatesuccess', function () {
var extra = arguments[arguments.length-1];
var copyStateSettings = (extra && extra.copyStateSettings)?extra.copyStateSettings:null;
if (copyStateSettings) {
this.fireEvent("copystateupdatesuccess", this, copyStateSettings);
this.tempState = {};
}
}, this);

this.on('updatestatefailure', function () {
var extra = arguments[arguments.length-1];
var copyStateSettings = (extra && extra.copyStateSettings)?extra.copyStateSettings:null;
if (copyStateSettings) {
this.fireEvent("copystateupdatefailure", this, copyStateSettings);
this.tempState = {};
}
}, this);

if (this.autoReadStateOnConstruct) {
this.readState();
}

};
Ext.extend(Ext.ux.state.provider.OmniProvider, Ext.state.Provider, {
//Public Properties:
/**
* @cfg {Boolean} debug
* whether or not to use console.log to log failed connection attemps and other errors (defaults to true).
*
*/
debug : true,
/**
* @cfg {String} sourceType
* the source used for states -- may be 'cookie' (uses modified version of Ext.state.CookieProvider code), 'ajax' (uses ajax request with data formated as json), 'direct' (uses api for ExtDirect) (defaults to 'cookie').
*
*/
sourceType : 'cookie',
/**
* @cfg {Boolean} protectState
* protect the currently loaded state so that other functions that attempt to directly alter or delete it are defeated -- I added this feature because several components will try to modify or delete elements of the state and in some circumstances this can cause problems (defaults to false).
*
*/
protectState : false,
/**
* @cfg {Mixed} stateGroupId
* allows you to save configurations of multiple components in a 'set'. This will allow for multiple state configurations to saved and retrieved as part of a group (defaults to null).
*
*/
stateGroupId : null,
/**
* @cfg {Boolean} autoSaveState
* may be set to false in order to prevent components from saving their own state configurations -- set this to false when you want a state to be saved manually or by another function (defaults to true).
*
*/
autoSaveState : true,
/**
* @cfg {Boolean} autoGiveStateOnGet
* may be set to false in order to prevent components from applying their own state configurations -- set this to false when you want a state to be applied manually or by another function (defaults to true).
*
*/
autoGiveStateOnGet : true,
/**
* @cfg {Boolean} autoReadStateOnGet
* whether or not to repload the state each time a get request comes in from a component -- seldom used feature (defaults to false).
*
*/
autoReadStateOnGet : false,
/**
* @cfg {Boolean} autoReadStateOnConstruct
* whether or not to read the state into memory when provider is constructed (defaults to true).
*
*/
autoReadStateOnConstruct : true,
/**
* @cfg {Object} cookieConfig
* configurations used when saving and retrieving states from a cookie -- does not generaly need to be modified but may be used to overide the settings in defaultCookieConfig. Used when sourceType is set to 'cookie' (defaults to {}).
*
*/
cookieConfig : {},
/**
* @cfg {Object} api
* An object containing referances to the functions to use when making ExtDirect calls to read, update and destroy the state (generally created using Ext.Direct.addProvider). The functions must connect to remote functions that do the following:
* read($stateGroupId, $triggerApplyAll, $components, $extra) -- remote function must return: {success : [boolean], result : [state_object]}
* update($state, $stateGroupId, $extra) -- remote function must return: {success : [boolean], result : [message]}
* destroy($stateNames, $stateGroupId, $extra) -- remote function must return: {success : [boolean], result : [message]}
*/
api : {
read : null,
update : null,
destroy : null
},
/**
* @cfg {Object} ajaxConfig
* configuration for ajax calls to read, update and destroy the state. Generally you will only need to define the 'url' properties but other things may be set on this configuration as well. omniProvider will make the following calls to your ajax script:
* action='read'&components=[json_encoded_array_of_components]&stateGroupId=[stateGroupId] -- remote function must return json encoded: {success : [boolean], result : [state_object]}
* action='update'&stateGroupId=[stateGroupId]&state=[json_encoded_state_object] -- remote function must return json encoded: {success : [boolean], result : [message]}
* action='destroy'&stateGroupId=[stateGroupId]&stateNames=[json_encoded_array_of_state_names] -- remote function must return json encoded: {success : [boolean], result : [message]}
*/
ajaxConfig : {
url: null
},
// Private Properties:
/**
* @private internal config {Object} specialCaseGetState
* flag used when all components are instructed to restore their state via the applyAll function.
*
*/
specialCaseGetState : false,
/**
* @private internal config {Object} specialCaseSetState
* flag used when all components are instructed to save their state via the saveAll function.
*
*/
specialCaseSetState : false,
/**
* @private internal config {Object} tempSaveState
* used when a batch save takes place (via the saveAll function) to record all the state info to save.
*
*/
tempSaveState : {},
/**
* @private internal config {Object} tempClearStateNames
* used when a batch save takes place to record all the state info to clear.
*
*/
tempClearStateNames : [],
/**
* @private internal config {Object} tempState
* used when a copyState function is called to read the contents of a state into a property of the class with out overwriting the state property.
*
*/
tempState : {},
/**
* @private internal config {Object} state
* State when object is instantiated, is over written when readState functions are called
*
*/
state : {},
/**
* @private internal config {Object} defaultStateGroupId
* used to store what the stateGroupId is before performning batch operations.
*
*/
defaultStateGroupId : '',
/**
* @private internal config {Object} defaultCookieConfig
* default config for cookieConfig -- over ridden by any values passed in with cookieConfig.
*
*/
defaultCookieConfig : {
path : "/",
expires : new Date(new Date().getTime()+(1000*60*60*24*7)), //7 days
domain : null,
secure : false
},
/**
* @private internal config {Object} errorMessages
* error messages that will be logged through the log function if debug is set to true.
*
*/
errorMessages : {
ajaxCouldNoteDecodeJson : 'omniProviderError-Ajax: could not decode remote json data:',
ajaxImproperlyFormatedReturn : 'omniProviderError-Ajax: response from server improperly formated -- should be {success:[true_or_false], result:{[error_message_or_state_information]}}:',
ajaxServerReportedAnError : 'omniProviderError-Ajax: the server reported an error',
ajaxServerDidNotRespond : 'omniProviderError-Ajax: Did not receive a response from the server',

directRead : 'omniProviderError-Direct: error with readStateDirect return data',
directUpdated : 'omniProviderError-Direct: error with updateStateDirect return data',
directDestroy : 'omniProviderError-Direct: error with destroyStateDirect return data'
},
//Public Functions:
/** Public Function: readState
* reads state into memory from either the sourceType defined for the class or from the sourceType passed to the function. This function routes to other functions depending on the sourceType
* @param {String} stateGroupId (Optional) A stateGroupId to over ride the stateGroupId set for the class -- using different stateGroupId allows you to save and load states using different names. Defaults to this.this.stateGroupId.
* @param {Boolean} triggerApplyAll (Optional) If set to true the applyAll function will be called after the readState function completes. Defaults to false.
* @param {Mixed} sourceType (Optional) May be set to: 'cookie', 'ajax' or 'direct'. Overides the sourceType set for the class to all the function to read in the state from a different source. Defaults to null.
* @param {Array} components (Optional) An array of component ids that may optionaly be passed in to cause just the states relating to these components to be loaded. Defaults to this.sourceType.
* @param {Object} extra (Optional) Values that will be passed along with the requests to read the state -- this is used largely by the copyState functions. Defaults to null.
* @return {Object} the state that was read by the function
*/
readState : function (stateGroupId, triggerApplyAll, sourceType, components, extra) {
var stateGroupId = (typeof(stateGroupId)!='undefined')?stateGroupId:this.stateGroupId;
var triggerApplyAll = (typeof(triggerApplyAll)!='undefined')?triggerApplyAll:false;
var components = (typeof(components)!='undefined')?components:null;
var components = (typeof(components)=='string')?[components]:components;
var sourceType = (typeof(sourceType)!='undefined')?sourceType:this.sourceType;
var extra = (typeof(extra)!='undefined')?extra:null;
var stateHolder = (extra && extra.stateHolder)?extra.stateHolder:'state';

this.fireEvent("beforereadstate", this, stateGroupId, triggerApplyAll, sourceType, components, extra);

switch (sourceType) {
case 'cookie':
this.readCookies(stateGroupId, triggerApplyAll, components, extra);
break;
case 'ajax':
this.readAjax(stateGroupId, triggerApplyAll, components, extra);
break;
case 'direct':
this.readDirect(stateGroupId, triggerApplyAll, components, extra);
break;
}

return this[stateHolder];
},
/** Public Function: updateState
* saves the state passed to the function to the source of sourceType either set for the class or passed to the function. This function routes to other functions depending on the sourceType
* @param {Object} state (Required) A state object to save
* @param {String} stateGroupId (Optional) A stateGroupId to over ride the stateGroupId set for the class -- using different stateGroupId allows you to save and load states using different name. Defaults to this.stateGroupId.
* @param {String} sourceType (Optional) May be set to: 'cookie', 'ajax' or 'direct'. Overides the sourceType set for the class to all the function to read in the state from a different source. Defaults to this.sourceType.
* @param {Mixed} extra (Optional) Values that will be passed along with the requests to read the state -- this is used largely by the copyState functions. Defaults to null.
* @return {Boolean} returns true
*/
updateState : function (state, stateGroupId, sourceType, extra) {
var stateGroupId = (typeof(stateGroupId)!='undefined')?stateGroupId:this.stateGroupId;
var sourceType = (typeof(sourceType)!='undefined')?sourceType:this.sourceType;
var extra = (typeof(extra)!='undefined')?extra:null;

this.fireEvent("beforeupdatestate", this, state, stateGroupId, sourceType, extra);
switch (sourceType) {
case 'cookie':
this.updateCookies(state, stateGroupId, extra);
break;
case 'ajax':
this.updateAjax(state, stateGroupId, extra);
break;
case 'direct':
this.updateDirect(state, stateGroupId, extra);
break;
}
return true;
},
/** Public Function: destroyState
* destroys the states whose names are passed in stateNames from sourceType either set for the class or passed to the function. This function routes to other functions depending on the sourceType
* @param {Array} stateNames (Required) An array of stateNames to destroy
* @param {String} stateGroupId (Optional) A stateGroupId to over ride the stateGroupId set for the class -- using different stateGroupId allows you to save and load states using different name. Defaults to this.stateGroupId
* @param {String} sourceType (Optional) May be set to: 'cookie', 'ajax' or 'direct'. Overides the sourceType set for the class to all the function to read in the state from a different source. Defaults to this.sourceType
* @param {Mixed} extra (Optional) Values that will be passed along with the requests to read the state -- this is used largely by the copyState functions. Defaults to null.
* @return {Boolean} returns true
*/
destroyState : function (stateNames, stateGroupId, sourceType, extra) {
var stateGroupId = (typeof(stateGroupId)!='undefined')?stateGroupId:this.stateGroupId;
var sourceType = (typeof(sourceType)!='undefined')?sourceType:this.sourceType;
var extra = (typeof(extra)!='undefined')?extra:null;
var stateNames = (typeof(stateNames)!='string')?stateNames:[stateNames];
this.fireEvent("beforedestroystate", this, stateNames, stateGroupId, sourceType, extra);

switch (sourceType) {
case 'cookie':
this.destroyCookies(stateNames, stateGroupId, extra);
break;
case 'ajax':
this.destroyAjax(stateNames, stateGroupId, extra);
break;
case 'direct':
this.destroyDirect(stateNames, stateGroupId, extra);
break;
}
return true;
},
/** Public Function: copyState
* starts the process to read in a state from a source and then update it to another source.
* @param {String} fromSourceType (Required) May be set to: 'cookie', 'ajax' or 'direct'. source from which to read.
* @param {String} toSourceType (Required) May be set to: 'cookie', 'ajax' or 'direct'. sourceType to copy the state to.
* @param {String} stateGroupId (Optional) A stateGroupId to over ride the stateGroupId set for the class -- using different stateGroupId allows you to save and load states using different name. Defaults to this.stateGroupId
* @param {String} newStateGroupId (Optional) The new stateGroupId to copy the state you have read to. Defaults to this.stateGroupId
* @return {Boolean} returns true
*/
copyState : function (fromSourceType, toSourceType, stateGroupId, newStateGroupId) {
var stateGroupId = (typeof(stateGroupId)!='undefined')?stateGroupId:this.stateGroupId;
var newStateGroupId = (typeof(newStateGroupId)!='undefined')?newStateGroupId:this.stateGroupId;

this.fireEvent("beforecopystate", this, fromSourceType, toSourceType, stateGroupId, newStateGroupId);

var extra = {
copyStateSettings :
{
stateGroupId : stateGroupId,
fromSourceType : fromSourceType,
newStateGroupId : newStateGroupId,
toSourceType : toSourceType
},
stateHolder : 'tempState'
};
this.readState(stateGroupId, false, fromSourceType, null, extra);
return true;
},
/** Public Function: applyAll
* runs a batch process to call the initState function on stateful components
* @param {String} stateGroupId (Optional) A stateGroupId to over ride the stateGroupId set for the class -- using different stateGroupId allows you to save and load states using different name. Defaults to this.stateGroupId;
* @param {Mixed} components An array of component ids that may optionaly be passed in to cause just these components to run their initState function. Defaults to null.
* @return {Boolean} returns true
*/
applyAll : function (stateGroupId, components) {
var stateGroupId = (typeof(stateGroupId)!='undefined')?stateGroupId:this.stateGroupId;
var components = (typeof(components)!='undefined')?components:null;
var components = (typeof(components)=='string')?[components]:components;

this.fireEvent("beforeapplyall", this, stateGroupId, components);
this.batch(stateGroupId, components,'specialCaseGetState', 'initState');
this.fireEvent("applyallsuccess", this, stateGroupId, components);
return true;
},
/** Public Function: saveAll
* runs a batch process to call the saveState function on stateful components
* @param {String} stateGroupId (Optional) A stateGroupId to over ride the stateGroupId set for the class -- using different stateGroupId allows you to save and load states using different name. Defaults to this.stateGroupId.
* @param {Mixed} components (Optional) An array of component ids that may optionaly be passed in to cause just these components to run their saveState function. Defaults to null.
* @return {Boolean} returns true
*/
saveAll : function (stateGroupId, components) {
var stateGroupId = (typeof(stateGroupId)!='undefined')?stateGroupId:this.stateGroupId;
var components = (typeof(components)!='undefined')?components:null;
var components = (typeof(components)=='string')?[components]:components;
this.fireEvent("beforesaveall", this, stateGroupId, components);
this.batch(stateGroupId, components, 'specialCaseSetState', 'saveState');

this.updateState(this.tempSaveState);
this.destroyState(this.tempClearStateNames);

this.tempSaveState = {};
this.tempClearStateNames = [];
this.fireEvent("saveallsuccess", this, stateGroupId, components);
return true;
},
/** Public Function: get
* modified standard get function called by the Ext.state.Manager when an component requests to read it's loaded state
* @param {String} name (Required) The key name.
* @param {Mixed} defaultValue (Optional) The default value to return if the key lookup does not match. Defaults to undefined.
* @return {Mixed} The state data or null.
*/
get : function (name, defaultValue) {
if (this.autoReadStateOnGet) {
this.readState(this.stateGroupId, false, this.sourceType, [name]);
}
if (this.autoGiveStateOnGet || this.specialCaseGetState) {
if (this.protectState) {
var returnData = {};
var stateObj = typeof this.state[name] == "undefined" ?
defaultValue : this.state[name];
Ext.apply(returnData, stateObj);
return returnData;
} else {
return typeof this.state[name] == "undefined" ?
defaultValue : this.state[name];
}
} else {
return null;
}
},
/** Public Function: set
* modified standard set function called by the Ext.state.Manager when an component requests to set it's state
* @param {String} name (Required) The key name.
* @param {Mixed} value (Required) The state data.
* @return {Boolean} returns true or false
*/
set : function (name, value) {
if (this.autoSaveState || this.specialCaseSetState) {
if(typeof value == "undefined" || value === null){
this.clear(name);
return;
}
if (this.specialCaseSetState) {
this.tempSaveState[name] = value;
} else {
var tempState = [];
tempState[name] = value;
this.updateState(tempState);
Ext.ux.state.provider.OmniProvider.superclass.set.call(this, name, value);
}
return true;
} else {
return false;
}

},
/** Public Function: clear
* modified standard clear function called by the Ext.state.Manager when an component requests to clear its
* @param {String} name (Required) The key name.
* @return {Boolean} true
*/
clear : function (name) {
if (this.specialCaseSetState) {
this.tempClearStateNames.push(name);
} else {
this.destroyState([name]);
Ext.ux.state.provider.OmniProvider.superclass.clear.call(this, name);
}
return true;
},
//Private Functions:
// private
readCookies : function(stateGroupId, triggerApplyAll, components, extra){
var stateHolder = (extra && extra.stateHolder)?extra.stateHolder:'state';
var cookies = {};
var c = document.cookie + ";";
var re = /\s?(.*?)=(.*?);/g;
var matches;
var regExString = "^ys\-" + stateGroupId + "\-";
var extraRegEx = (stateGroupId) ? new RegExp(regExString) : null;

while((matches = re.exec(c)) != null){
var name = matches[1];
var value = matches[2];
var nameFirst3 = name.substring(0,3);
var trimmedName = name.replace(extraRegEx, '');
var decodedValue = this.decodeValue(value);
if(name && nameFirst3 == "ys-" && (!extraRegEx || extraRegEx.test(name)) && (!components || this.inArray(components, name))){
cookies[trimmedName] = decodedValue;
}
}
this[stateHolder] = cookies;

this.fireEvent("readstatesuccess", this, 'cookie', stateGroupId, triggerApplyAll, components, extra);
if (triggerApplyAll) {
this.applyAll(stateGroupId, components);
}
},
// private
updateCookies : function(state, stateGroupId, extra){
for (var obj in state) {
var curName = obj;
var curValue = state[obj];
this.updateCookie(stateGroupId, curName, curValue);
}
this.fireEvent("updatestatesuccess", this, 'cookie', state, stateGroupId, extra);
},
// private
updateCookie : function(stateGroupId, name, value){
curCookieConfig = this.cookieConfig;
Ext.applyIf(curCookieConfig, this.defaultCookieConfig);
var nameExtra = (stateGroupId) ? stateGroupId + "-" : '';
var cookieName = "ys-"+ nameExtra + name;
var cookieValue = this.encodeValue(value) +
((curCookieConfig.expires == null) ? "" : ("; expires=" + curCookieConfig.expires.toGMTString())) +
((curCookieConfig.path == null) ? "" : ("; path=" + curCookieConfig.path)) +
((curCookieConfig.domain == null) ? "" : ("; domain=" + curCookieConfig.domain)) +
((curCookieConfig.secure == true) ? "; secure" : "");

document.cookie = cookieName + "=" + cookieValue;
},
// private
destroyCookies : function(stateNames, stateGroupId, extra){
for (var n=0; n< stateNames.length; n++) {
var curName = stateNames[n];
this.destroyCookie(stateGroupId, curName);
}
this.fireEvent("destroystatesuccess", this, 'cookie', stateNames, stateGroupId, extra);
},
// private
destroyCookie : function(stateGroupId, name){
curCookieConfig = this.cookieConfig;
Ext.applyIf(curCookieConfig, this.defaultCookieConfig);
var nameExtra = (stateGroupId) ? stateGroupId + "-" : '';
document.cookie = "ys-" + nameExtra + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
((curCookieConfig.path == null) ? "" : ("; path=" + curCookieConfig.path)) +
((curCookieConfig.domain == null) ? "" : ("; domain=" + curCookieConfig.domain)) +
((curCookieConfig.secure == true) ? "; secure" : "");
},
//private
readAjax : function (stateGroupId, triggerApplyAll, components, extra) {
var curAjax = this.ajaxConfig;
var componentsEncoded = (components)?Ext.encode(components):null;
curAjax.params = {
action:'read',
components:componentsEncoded,
stateGroupId:stateGroupId,
};
curAjax.triggerApplyAll = triggerApplyAll;
curAjax.extra = extra;
curAjax.components = components;
curAjax.stateGroupId = stateGroupId;
curAjax.success = this.onAjaxReadSuccess;
curAjax.failure = this.onAjaxReadFailure;
curAjax.scope = this;

Ext.Ajax.request(curAjax);
},
//private
onAjaxReadSuccess : function (response, options) {
var response = this.parseJSONReturn(response);

var stateGroupId = options.stateGroupId;
var triggerApplyAll = options.triggerApplyAll;
var components = options.components;
var extra = options.extra;

var success = response.success;
var stateHolder = (extra && extra.stateHolder)?extra.stateHolder:'state';

if (success) {
for (obj in response.result) {
response.result[obj] = Ext.decode(response.result[obj]);
}

this[stateHolder] = response.result;
this.fireEvent("readstatesuccess", this, 'ajax', stateGroupId, triggerApplyAll, components, extra);
if (triggerApplyAll) {
this.applyAll(stateGroupId, components);
}
} else {
this.fireEvent("readstatefailure", this, 'ajax', null, response, stateGroupId, triggerApplyAll, components, extra);
this.log(this.errorMessages.ajaxServerReportedAnError, 'onAjaxReadSuccess', this, 'ajax', response, stateGroupId, triggerApplyAll, components, extra);
}
},
//private
onAjaxReadFailure : function (e, options) {
var stateGroupId = options.stateGroupId;
var triggerApplyAll = options.triggerApplyAll;
var components = options.components;
var extra = options.extra;
this.fireEvent("readstatefailure", this, 'ajax', e, null, stateGroupId, triggerApplyAll, components, extra);
this.log(this.errorMessages.ajaxServerDidNotRespond, 'ajax', e, null, stateGroupId, triggerApplyAll, components, extra);
},
//private
updateAjax : function (state, stateGroupId, extra) {
var curAjax = this.ajaxConfig;
//The 'remove' function on the state obj screwed up the Ext.encode function so we make an object that doesn't have it:
var stateToEncode = {};
for (obj in state) {
if (obj != 'remove') {
stateToEncode[obj] = state[obj];
}
}
stateEncoded = Ext.encode(stateToEncode);

curAjax.params = {
action:'update',
state:stateEncoded,
stateGroupId:stateGroupId
};
curAjax.state = state;
curAjax.stateGroupId = stateGroupId;
curAjax.extra = extra;
curAjax.success = this.onAjaxUpdateSuccess;
curAjax.failure = this.onAjaxUpdateFailure;
curAjax.scope = this;
Ext.Ajax.request(curAjax);
},
//private
onAjaxUpdateSuccess : function (response, options) {
response = this.parseJSONReturn(response);
var success = response.success;
var state = options.state;
var stateGroupId = options.stateGroupId;
var extra = options.extra

if (success) {
this.fireEvent("updatestatesuccess", this, 'ajax', state, stateGroupId, extra);
} else {
this.fireEvent("updatestatefailure", this, 'ajax', null, response, state, stateGroupId, extra);
this.log(this.errorMessages.ajaxServerReportedAnError, 'onAjaxUpdateSuccess', this, 'ajax', null, response, state, stateGroupId, extra);
}
},
//private
onAjaxUpdateFailure : function (e, options) {
var state = options.state;
var stateGroupId = options.stateGroupId;
var extra = options.extra
this.fireEvent("updatestatefailure", this, 'ajax', e, null, state, stateGroupId, extra);
this.log(this.errorMessages.ajaxServerDidNotRespond, this, 'ajax', e, null, state, stateGroupId, extra);
},
//private
destroyAjax : function (stateNames, stateGroupId, extra) {
var curAjax = this.ajaxConfig;
var stateNamesEncoded = (stateNames)?Ext.encode(stateNames):null;
curAjax.params = {
action:'destroy',
stateNames:stateNamesEncoded,
stateGroupId:stateGroupId
};
curAjax.stateNames = stateNames;
curAjax.stateGroupId = stateGroupId;
curAjax.extra = extra;
curAjax.success = this.onAjaxDestroySuccess;
curAjax.failure = this.onAjaxDestroyFailure;
curAjax.scope = this;
Ext.Ajax.request(curAjax);
},
//private
onAjaxDestroySuccess : function (response, options) {
response = this.parseJSONReturn(response);
var stateNames = options.stateNames;
var stateGroupId = options.stateGroupId;
var extra = options.extra

var success = response.success;
if (success) {
this.fireEvent("destroystatesuccess", this, 'ajax', stateNames, stateGroupId, extra);
} else {
this.fireEvent("destroystatefailure", this, 'ajax', null, response, stateNames, stateGroupId, extra);
this.log(this.errorMessages.ajaxServerDidNotRespond, this, 'ajax', response, stateNames, stateGroupId, extra);
}
},
//private
onAjaxDestroyFailure : function (e, options) {
var stateNames = options.stateNames;
var stateGroupId = options.stateGroupId;
var extra = options.extra

this.fireEvent("destroystatefailure", this, 'ajax', e, null, stateNames, stateGroupId, extra);
this.log(this.errorMessages.ajaxServerDidNotRespond, this, 'ajax', e, stateNames, stateGroupId, extra);
},
//private
readDirect : function (stateGroupId, triggerApplyAll, components, extra) {
this.api.read(stateGroupId, triggerApplyAll, components, extra, this.onDirectReadSuccess, this);
},
//private
onDirectReadSuccess : function(response, e){
var transaction = e.getTransaction();
var stateGroupId = transaction.args[0];
var triggerApplyAll = transaction.args[1];
var components = transaction.args[2];
var extra = transaction.args[3];
var stateHolder = (extra && extra.stateHolder)?extra.stateHolder:'state';
var success = response.success;
if (success) {
this[stateHolder] = response.result;
this.fireEvent("readstatesuccess", this, 'direct', stateGroupId, triggerApplyAll, components, extra);
if (triggerApplyAll) {
this.applyAll(stateGroupId, components);
}
} else {
this.fireEvent("readstatefailure", this, 'direct', e, response, stateGroupId, triggerApplyAll, components, extra);
this.log(this.errorMessages.directRead, 'onDirectReadSuccess', this, 'direct', e, stateGroupId, triggerApplyAll, components, extra);
}
},
//private
updateDirect : function (state, stateGroupId, extra) {
var stateToSend = {};
for (obj in state) {
if (obj != 'remove') {
stateToSend[obj] = state[obj];
}
}
this.api.update(stateToSend, stateGroupId, extra, this.onDirectUpdateSuccess, this);
},
//private
onDirectUpdateSuccess : function(response, e){
var transaction = e.getTransaction();
var state = transaction.args[0];
var stateGroupId = transaction.args[1];
var extra = transaction.args[2];
var success = response.success;

if (success) {
this.fireEvent("updatestatesuccess", this, 'direct', state, stateGroupId, extra);
} else {
this.fireEvent("updatestatefailure", this, 'direct', e, response, state, stateGroupId, extra);
this.log(this.errorMessages.directUpdate, 'onDirectUpdateSuccess', this, 'direct', e, response, state, stateGroupId, extra);
}

},
//private
destroyDirect : function (stateNames, stateGroupId, extra) {
this.api.destroy(stateNames, stateGroupId, extra, this.onDirectDestroySuccess, this);
},
//private
onDirectDestroySuccess : function(response, e){
var transaction = e.getTransaction();
var stateNames = transaction.args[0];
var stateGroupId = transaction.args[1];
var extra = transaction.args[2];
var success = response.success;
if (success) {
this.fireEvent("destroystatesuccess", this, 'direct', stateNames, stateGroupId, extra);
} else {
this.fireEvent("destroystatefailure", this, 'direct', e, response, stateNames, stateGroupId, extra);
this.log(this.errorMessages.directDestroy, 'onAjaxDestroySuccess', this, 'direct', e, stateNames, stateGroupId, extra);
}
},
//private
parseJSONReturn : function (response) {
try {
var result = Ext.decode(response.responseText);
} catch(e) {
this.log(this.errorMessages.ajaxCouldNoteDecodeJson, e, response, options, this);
return false;
}
if (typeof(result.success) == 'undefined' || typeof(result.result)== 'undefined') {
this.log(this.errorMessages.ajaxImproperlyFormatedReturn, response, options, this);
return false;
}
return result;
},
//private
batch : function (stateGroupId, components, flag, callFunction) {
this.defaultStateGroupId = this.stateGroupId;
this.stateGroupId = (typeof(stateGroupId)!='undefined')?stateGroupId:this.stateGroupId;

this[flag] = true;

var curComponents = (components)?components:Ext.ComponentMgr.all.items;

for (var n=0; n<curComponents.length; n++) {
var curComp = curComponents[n];
curComp = (typeof(curComp)=='string')?Ext.getCmp(curComp):curComp;
if (typeof(curComp.stateful !== false)) {
curComp[callFunction]();
}
}

this[flag] = false;
this.stateGroupId = this.defaultStateGroupId;
},
//private
log : function() {
if (this.debug){
if(console) {
console.log.apply(console, arguments);
}
}
},
//private
inArray : function (array, value, caseSensitive) {
var i;
for (i=0; i < array.length; i++) {
// use === to check for Matches. ie., identical (===),
if(caseSensitive){ //performs match even the string is case sensitive
if (array[i].toLowerCase() == value.toLowerCase()) {
return true;
}
}else{
if (array[i] == value) {
return true;
}
}
}
return false;
}
});
Ext.reg('ux-state-provider-omniprovider', Ext.ux.state.provider.OmniProvider);

thesilentman
6 Sep 2009, 4:07 AM
Hello Will,

your extension works nicely. I am using it with my own backend though.
I had started experimenting with the stateproviders when I stumbled upon your post.


So, for me only one little question remains...
what license is it under?

I liked it, so I wanted to include it in a commercial app I am building.

Greets,
Frank

willf1976
7 Sep 2009, 3:17 PM
Hi Frank

I am glad you like the omniProvider. I actually I am not using that back end either -- just something I through together to make a demo.

I am putting the omniProvider under the GNU license LGPL version 3 -- http://www.gnu.org/copyleft/lesser.html

Basically you can use omniProvider as a component of what ever you want to sell so long as you are not selling the omniProvider itself. Also if you are distributing omniProvider you must leave the GPL intact.

Hope you had a good weekend.

Best Regards

Will Ferrer

Dumbledore
19 Oct 2009, 11:36 PM
there ist a small typo:


readAjax : function (stateGroupId, triggerApplyAll, components, extra) {
var curAjax = this.ajaxConfig;
var componentsEncoded = (components)?Ext.encode(components):null;
curAjax.params = {
action:'read',
components:componentsEncoded,
stateGroupId:stateGroupId, <----
};

This comma is not good for the Internet-Explorer :-)

willf1976
20 Oct 2009, 9:02 PM
Hi Dumbledore

I had made an additional post with an updated version that fixed that -- to avoid confusion I deleted the updated post and updated the original post instead.

Thanks for the catch.

Best regards

Will Ferrer


there ist a small typo:


readAjax : function (stateGroupId, triggerApplyAll, components, extra) {
var curAjax = this.ajaxConfig;
var componentsEncoded = (components)?Ext.encode(components):null;
curAjax.params = {
action:'read',
components:componentsEncoded,
stateGroupId:stateGroupId, <----
};

This comma is not good for the Internet-Explorer :-)

proceno72
6 Apr 2010, 7:47 AM
When I use destroyState function on 'ajax' sourceType (trying delete txt file on server), found error 'Options is not defined' on line 891 of your 'Ext.ux.state.provider.OmniProvider.js' (private definition of parseJSONReturn).
Function call on my page is like this:
omniProvider.destroyState('id_grid', omniProvider.stateGroupId,'ajax');

Using Extjs version 3.2.0

willf1976
12 Apr 2010, 3:35 PM
Hi Proceno

I have been very preoccupied with a deadline and haven't had a chance to look into this yet. I will try to see if I can figure it out next chance I get though. If you happen to look through the code and figure it out first please let me know and I will post your updated version of the code back into this thread.

Thanks for the catch.

Best Regards

Will Ferrer

khebs@live.com
13 Apr 2010, 12:10 AM
Hi, how do you use this class? application example?

KJedi
19 Jun 2010, 7:56 AM
This is great thing, I think exactly what I need. I understood the example, went through the code of extension, but can't figure out how to apply it to my case. I need to load and save state using ajax. I understood how to configure this state provider, it sends ajax request and loads states. I guess, when I create some stateful component,it will try to use info from the provider on creation and thus will restore it's state. But how can I save state? When to do it?
I understand, that components should automatically publish their state changes to the state provider. But how can I trigger state provider to save these changes on the server? In your example you used copying from cookie to server, but I don't need storing this in cookie at all because I'm going to have lots of info in state, cookie is not the right choice.

Looking forward for the prompt reply. Thanks beforehand!

KJedi
19 Jun 2010, 8:01 AM
Sorry, I think I found the answer - it simply saves everything on change... Is there any way of buffering changes like HttpStateProvider does?
I prefer buffering mostly because I will have lot of user interaction and I don't want so many ajax queries... Each move is a query typing a letter in the input results in a query... I need buffering :)

willf1976
19 Jun 2010, 9:01 PM
Hi Proceno

I finally got around to taking a look at this -- turns out it was just a simple error in my php. I changed the php file and uploaded it again (see attachment on original message).

willf1976
19 Jun 2010, 9:02 PM
Hi Khebs

There is some example files for this -- see the attachments on the first post. Hope that helps :)

Will

Hi, how do you use this class? application example?

willf1976
19 Jun 2010, 9:03 PM
Hi KJedi

Sadly I never got around to putting in buffering. If you use it with direct however direct itself will do buffering for you.

Hope that helps.

Will

Sorry, I think I found the answer - it simply saves everything on change... Is there any way of buffering changes like HttpStateProvider does?
I prefer buffering mostly because I will have lot of user interaction and I don't want so many ajax queries... Each move is a query typing a letter in the input results in a query... I need buffering :)