mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-12-26 16:48:49 +01:00
Allow for long-term client side caching of dataFetch responses.
This commit is contained in:
parent
8a70df4df8
commit
df76846b37
@ -26,6 +26,15 @@ egw.extend("data", egw.MODULE_APP_LOCAL, function (_app, _wnd) {
|
|||||||
|
|
||||||
var lastModification = null;
|
var lastModification = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* cacheCallback stores callbacks that determine if data is placed
|
||||||
|
* into cacheStorage, or simply kept temporarily. It is indexed
|
||||||
|
* by prefix.
|
||||||
|
*
|
||||||
|
* @type Array
|
||||||
|
*/
|
||||||
|
var cacheCallback = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The uid function generates a session-unique id for the current
|
* The uid function generates a session-unique id for the current
|
||||||
* application by appending the application name to the given uid.
|
* application by appending the application name to the given uid.
|
||||||
@ -40,6 +49,35 @@ egw.extend("data", egw.MODULE_APP_LOCAL, function (_app, _wnd) {
|
|||||||
return _prefix + "::" + _uid;
|
return _prefix + "::" + _uid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Looks like too much data is cached. Forget some.
|
||||||
|
*
|
||||||
|
* Tries to free up localStorage by removing cached data for the given
|
||||||
|
* prefix, but if none is found it will remove all cached data.
|
||||||
|
*
|
||||||
|
* @param {string} _prefix UID / application prefix
|
||||||
|
* @returns {Number} Number of cached recordsets removed
|
||||||
|
*/
|
||||||
|
function _clearCache(_prefix)
|
||||||
|
{
|
||||||
|
// Find cached items for the prefix, we prefer to expire just within the app
|
||||||
|
var indexes = [];
|
||||||
|
for(var i = 0; i < window.localStorage.length; i++)
|
||||||
|
{
|
||||||
|
if(window.localStorage.key(i).indexOf('cache_'+_prefix) == 0)
|
||||||
|
{
|
||||||
|
indexes.push(i);
|
||||||
|
window.localStorage.removeItem(window.localStorage.key(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Nothing for that prefix? Clear all cached data.
|
||||||
|
if(_prefix && indexes.length == 0)
|
||||||
|
{
|
||||||
|
return _clearCache('');
|
||||||
|
}
|
||||||
|
return indexes.length;
|
||||||
|
}
|
||||||
|
|
||||||
function parseServerResponse(_result, _callback, _context)
|
function parseServerResponse(_result, _callback, _context)
|
||||||
{
|
{
|
||||||
// Check whether the result is valid
|
// Check whether the result is valid
|
||||||
@ -61,9 +99,12 @@ egw.extend("data", egw.MODULE_APP_LOCAL, function (_app, _wnd) {
|
|||||||
if (_result.order && _result.data)
|
if (_result.order && _result.data)
|
||||||
{
|
{
|
||||||
// Assemble the correct order uids
|
// Assemble the correct order uids
|
||||||
for (var i = 0; i < _result.order.length; i++)
|
if(!(_result.order.length && _result.order[0] && _result.order[0].indexOf(_context.prefix) == 0))
|
||||||
{
|
{
|
||||||
_result.order[i] = UID(_result.order[i], _context.prefix);
|
for (var i = 0; i < _result.order.length; i++)
|
||||||
|
{
|
||||||
|
_result.order[i] = UID(_result.order[i], _context.prefix);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load all data entries that have been sent or delete them
|
// Load all data entries that have been sent or delete them
|
||||||
@ -95,6 +136,37 @@ egw.extend("data", egw.MODULE_APP_LOCAL, function (_app, _wnd) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check to see if we need long-term caching of the query and its results
|
||||||
|
if(window.localStorage && _context.prefix && cacheCallback[_context.prefix])
|
||||||
|
{
|
||||||
|
// Ask registered callbacks if we should cache this
|
||||||
|
for(var i = 0; i < cacheCallback[_context.prefix].length; i++)
|
||||||
|
{
|
||||||
|
var cc = cacheCallback[_context.prefix][i];
|
||||||
|
var cache_key = false
|
||||||
|
if(cache_key = cc.callback.call(cc.context, _context))
|
||||||
|
{
|
||||||
|
cache_key = 'cache_' + _context.prefix + '::' + cache_key;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
window.localStorage.setItem(cache_key,JSON.stringify(_result));
|
||||||
|
}
|
||||||
|
catch (e)
|
||||||
|
{
|
||||||
|
egw.debug('warning', 'Tried to cache some data', cache_key, e);
|
||||||
|
|
||||||
|
// Maybe ran out of space? Free some up...
|
||||||
|
if(e.name == 'QuotaExceededError' // storage quota is exceeded, remove cached data
|
||||||
|
|| 'NS_ERROR_DOM_QUOTA_REACHED') // FF-name
|
||||||
|
{
|
||||||
|
var count = _clearCache(_context.prefix);
|
||||||
|
egw.debug('info', 'localStorage full, removed ' + count + ' stored datasets');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Call the callback function and pass the calculated "order" array
|
// Call the callback function and pass the calculated "order" array
|
||||||
// as well as the "total" count and the "timestamp" to the listener.
|
// as well as the "total" count and the "timestamp" to the listener.
|
||||||
if (_callback)
|
if (_callback)
|
||||||
@ -204,6 +276,29 @@ egw.extend("data", egw.MODULE_APP_LOCAL, function (_app, _wnd) {
|
|||||||
knownUids.slice(typeof _queriedRange.start != "undefined" ? _queriedRange.start:0,KNOWN_UID_LIMIT);
|
knownUids.slice(typeof _queriedRange.start != "undefined" ? _queriedRange.start:0,KNOWN_UID_LIMIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check to see if we have long-term caching of the query and its results
|
||||||
|
if(window.localStorage && _context.prefix && cacheCallback[_context.prefix])
|
||||||
|
{
|
||||||
|
// Ask registered callbacks if we should cache this
|
||||||
|
for(var i = 0; i < cacheCallback[_context.prefix].length; i++)
|
||||||
|
{
|
||||||
|
var cc = cacheCallback[_context.prefix][i];
|
||||||
|
var cache_key = false
|
||||||
|
if(cache_key = cc.callback.call(cc.context, _context))
|
||||||
|
{
|
||||||
|
cache_key = 'cache_' + _context.prefix + '::' + cache_key;
|
||||||
|
|
||||||
|
var cached = window.localStorage.getItem(cache_key);
|
||||||
|
if(cached)
|
||||||
|
{
|
||||||
|
egw.debug('log', 'Data cached query: ' + cache_key + "\nprocessing...");
|
||||||
|
// Call right away with cached data. We'll still ask the server
|
||||||
|
// though.
|
||||||
|
parseServerResponse(JSON.parse(cached), _callback, _context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
var request = egw.json(
|
var request = egw.json(
|
||||||
_app+".etemplate_widget_nextmatch.ajax_get_rows.etemplate",
|
_app+".etemplate_widget_nextmatch.ajax_get_rows.etemplate",
|
||||||
[
|
[
|
||||||
@ -221,8 +316,59 @@ egw.extend("data", egw.MODULE_APP_LOCAL, function (_app, _wnd) {
|
|||||||
true
|
true
|
||||||
);
|
);
|
||||||
request.sendRequest();
|
request.sendRequest();
|
||||||
}
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turn on long-term client side cache of a particular request
|
||||||
|
* (cache the nextmatch query results) for fast, immediate response
|
||||||
|
* with old data.
|
||||||
|
*
|
||||||
|
* The request is still sent to the server, and the cache is updated
|
||||||
|
* with fresh data, and any needed callbacks are called again with
|
||||||
|
* the fresh data.
|
||||||
|
*
|
||||||
|
* @param {string} prefix UID / Application prefix should match the
|
||||||
|
* individual record prefix
|
||||||
|
* @param {function} callback A function that will analize the provided fetch
|
||||||
|
* parameters and return a reproducable cache key, or false to not cache
|
||||||
|
* the request.
|
||||||
|
* @param {object} context Context for callback function.
|
||||||
|
*/
|
||||||
|
dataCacheRegister: function(prefix, callback, context)
|
||||||
|
{
|
||||||
|
if(typeof cacheCallback[prefix] == 'undefined')
|
||||||
|
{
|
||||||
|
cacheCallback[prefix] = [];
|
||||||
|
}
|
||||||
|
cacheCallback[prefix].push({
|
||||||
|
callback: callback,
|
||||||
|
context: context
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister a previously registered cache callback
|
||||||
|
* @param {string} prefix UID / Application prefix should match the
|
||||||
|
* individual record prefix
|
||||||
|
* @param {function} [callback] Callback function to un-register. If
|
||||||
|
* omitted, all functions for the prefix will be removed.
|
||||||
|
*/
|
||||||
|
dataCacheUnregister: function(prefix, callback)
|
||||||
|
{
|
||||||
|
if(typeof callback != 'undefined')
|
||||||
|
{
|
||||||
|
for(var i = 0; i < cacheCallback[prefix].length; i++)
|
||||||
|
{
|
||||||
|
if(cacheCallback[prefix][i].callback == callback)
|
||||||
|
{
|
||||||
|
cacheCallback[prefix].splice(i,1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Callback not provided or not found, reset by prefix
|
||||||
|
cacheCallback[prefix] = [];
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user