egroupware_official/etemplate/js/et2_dataview_model_dataProvider.js

289 lines
5.8 KiB
JavaScript

/**
* eGroupWare eTemplate2 - Class which contains a the data model
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage dataview
* @link http://www.egroupware.org
* @author Andreas Stöckel
* @copyright Stylite 2011
* @version $Id$
*/
"use strict"
/*egw:uses
egw_action.egw_action;
et2_core_inheritance;
et2_core_common;
et2_dataview_interfaces;
*/
var et2_dataview_dataProvider = Class.extend(et2_IDataProvider, {
/**
* Creates this instance of the data provider.
*/
init: function(_source, _total, _actionMgr) {
this._source = _source;
this._total = _total;
this._registeredRows = {};
this._data = {};
this._dataCount = 0;
this._queue = {};
this._queueSize = 0;
this._stepSize = 25; // Count of elements which is loaded at once
this._maxCount = 1000; // Maximum count before the elements are cleaned up
// Create an action object manager
this.actionObjectManager = new egwActionObjectManager("", _actionMgr);
var self = this;
this._cleanupInterval = window.setInterval(function() {self._cleanup()},
10 * 1000);
this._queueFlushTimeout = null;
},
destroy: function() {
// Destroy the cleanup timer callback
window.clearInterval(this._cleanupInterval);
// Destroy the _queueFlushTimeout
this._stopFlushTimer();
// Destroy the actionObject manager
this.actionObjectManager.clear();
},
/**
* Resets the stored data and aborts any queue request
*/
clear: function() {
this._data = {};
this._dataCount = 0;
this._queue = {};
this._queueSize = 0;
this._stopFlushTimer();
// Clear all elements in the action object manager
this.actionObjectManager.clear();
},
/**
* Data is an object containing an "rows" and "readonlys" array.
*/
loadData: function(_data) {
this._receiveData(_data);
},
/**
* Returns the total count
*/
getCount: function() {
return this._total;
},
registerDataRow: function(_dataRow, _idx) {
// Make sure _idx is a int
_idx = parseInt(_idx);
if (typeof this._registeredRows[_idx] != "undefined")
{
this.egw().debug("warn", "Overriding data row for index " + _idx);
}
// Associate the given data row with that index
this._registeredRows[_idx] = _dataRow;
// Check whether an entry exists in the data array - if yes, call the
// request immediately
if (typeof this._data[_idx] != "undefined")
{
if (this._data[_idx].data == false)
{
egw().debug("warn", "App provides blank first row, which causes problems");
return;
}
this._callUpdateData(_idx);
}
else
{
this._queueIndex(_idx);
}
},
unregisterDataRow: function(_idx) {
// Make sure _idx is a int
_idx = parseInt(_idx);
delete(this._registeredRows[_idx]);
},
getActionObjectManager: function() {
return this.actionObjectManager;
},
/* ---- PRIVATE FUNCTIONS ---- */
_queueIndex: function(_idx) {
// Mark the index as queued
if (typeof this._queue[_idx] == "undefined")
{
this._queue[_idx] = true;
this._queueSize++;
}
if (this._queueSize > this._stepSize)
{
this._flushQueue();
}
else
{
// (Re)start the queue flush timer
var self = this;
this._stopFlushTimer();
this._queueFlushTimeout = window.setTimeout(function() {
self._queueFlushTimeout = null;
self._flushQueue();
}, 50);
}
},
_flushQueue: function() {
// Stop the flush timer if it is still active
this._stopFlushTimer();
// Mark all elements in a radius of this._stepSize / 2
var marked = {};
var r = Math.floor(this._stepSize / 2);
for (var key in this._queue)
{
key = parseInt(key);
var b = Math.max(0, key - r);
var t = Math.min(key + this._stepSize, this._total - 1);
var c = 0;
for (var i = b; i <= t && c < this._stepSize; i ++)
{
if (typeof this._data[i] == "undefined")
{
marked[i] = true;
c++;
}
}
}
// Reset the queue
this._queue = {};
this._queueSize = 0;
// Create a list with start indices and counts
var fetchList = [];
var entry = null;
var last = 0;
// Get the int keys and sort the array numeric
var arr = et2_arrayIntKeys(marked).sort(function(a,b){return a > b ? 1 : (a == b ? 0 : -1)});
for (var i = 0; i < arr.length; i++)
{
if (i == 0 || arr[i] - last > 1)
{
if (entry)
{
fetchList.push(entry);
}
entry = {
"startIdx": arr[i],
"count": 1
};
}
else
{
entry.count++;
}
last = arr[i];
}
if (entry)
{
fetchList.push(entry);
}
// Call the "getRows" callback
this._source.getRows(fetchList, this._receiveData, this);
},
_receiveData: function(_data) {
var time = (new Date).getTime();
for (var key in _data.rows)
{
if (!isNaN(key))
{
// Make sure the key is a int
key = parseInt(key);
// Copy the data for the given index
this._data[key] = {
"data": _data.rows[key],
"timestamp": time
};
// Update the row associated to the index
this._callUpdateData(key);
}
}
},
_stopFlushTimer: function() {
// Stop the queue flush timer
if (this._queueFlushTimeout !== null)
{
window.clearTimeout(this._queueFlushTimeout);
}
},
_callUpdateData: function(_idx) {
if (typeof this._registeredRows[_idx] != "undefined")
{
this._registeredRows[_idx].updateData({
"content": this._data[_idx].data
});
}
},
_cleanup: function() {
// Delete all data rows which have not been accessed for more than
// "delta" ms (5 minutes) - this method does not ensure that _dataCount
// gets below _maxCount!
var delta = 5 * 60 * 1000;
var now = (new Date).getTime();
if (this._dataCount > this._maxCount)
{
for (var key in this._data)
{
var entry = this._data[key];
if (now - entry.timestamp > delta)
{
delete(this._data[key]);
this._dataCount--;
}
}
}
}
});