mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-12-22 23:00:56 +01:00
Implemented grid column sorting
This commit is contained in:
parent
5ea2945538
commit
a231863ed3
@ -203,9 +203,60 @@ egwGrid.prototype.sortColsClick = function(_columnIdx)
|
|||||||
{
|
{
|
||||||
if (this.sortColsCallback)
|
if (this.sortColsCallback)
|
||||||
{
|
{
|
||||||
this.sortColsCallback.call(this.context, this.columns.columns[_columnIdx].id);
|
this.sortColsCallback.call(this.context, col.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var dir = EGW_COL_SORTMODE_ASC;
|
||||||
|
|
||||||
|
if (col.sortmode == EGW_COL_SORTMODE_ASC)
|
||||||
|
{
|
||||||
|
dir = EGW_COL_SORTMODE_DESC
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sortData(col.id, dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
egwGrid.prototype.sortData = function(_columnId, _dir)
|
||||||
|
{
|
||||||
|
var col = this.columns.getColumnById(_columnId);
|
||||||
|
|
||||||
|
if (col && col.sortable != EGW_COL_SORTABLE_NONE && col.sortable != EGW_COL_SORTABLE_EXTERNAL)
|
||||||
|
{
|
||||||
|
this.dataRoot.sortChildren(col.id, _dir, col.sortable, function() {
|
||||||
|
// Set the new sort direction
|
||||||
|
col.set_sortmode(_dir);
|
||||||
|
|
||||||
|
this.displaySortMode();
|
||||||
|
|
||||||
|
// Rebuild the inner grid
|
||||||
|
this.reload();
|
||||||
|
}, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
egwGrid.prototype.displaySortMode = function()
|
||||||
|
{
|
||||||
|
// Update the column data of the grid
|
||||||
|
this.gridOuter.updateColumns(this.columns.getColumnData());
|
||||||
|
|
||||||
|
// Update the column header
|
||||||
|
for (var i = 0; i < this.columns.columns.length; i++)
|
||||||
|
{
|
||||||
|
this.gridOuter.updateColSortmode(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
egwGrid.prototype.resetSort = function()
|
||||||
|
{
|
||||||
|
for (var i = 0; i < this.columns.columns.length; i++)
|
||||||
|
{
|
||||||
|
fileGrid.columns.columns[i].set_sortmode(EGW_COL_SORTMODE_NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.displaySortMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -192,7 +192,7 @@ egwGridColumn.prototype.set_sortmode = function(_value)
|
|||||||
{
|
{
|
||||||
if (_value != this.sortmode)
|
if (_value != this.sortmode)
|
||||||
{
|
{
|
||||||
if (this.sortmode)
|
if (this.sortmodeChangeCallback)
|
||||||
{
|
{
|
||||||
this.sortmodeChangeCallback.call(this.context, this);
|
this.sortmodeChangeCallback.call(this.context, this);
|
||||||
}
|
}
|
||||||
|
@ -752,7 +752,232 @@ egwGridDataElement.prototype.getParentList = function(_lst)
|
|||||||
return _lst;
|
return _lst;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requires a certain column to have all data localy - if this isn't the case,
|
||||||
|
* the data is fetched from the server.
|
||||||
|
*
|
||||||
|
* @param string _colId specifies the column which should be loaded
|
||||||
|
* @param function _callback is the function which should be called once all data
|
||||||
|
* has been fetched.
|
||||||
|
* @param object _context is the context in which the callback should be executed
|
||||||
|
* @param object _loadIds is used internally to accumulate the object ids which
|
||||||
|
* should be loaded.
|
||||||
|
*/
|
||||||
|
egwGridDataElement.prototype.requireColumn = function(_colId, _callback, _context,
|
||||||
|
_loadElems)
|
||||||
|
{
|
||||||
|
var outerCall = false;
|
||||||
|
if (typeof _loadElems == "undefined")
|
||||||
|
{
|
||||||
|
_loadElems = {
|
||||||
|
"elems": []
|
||||||
|
}
|
||||||
|
outerCall = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!outerCall && !this.hasColumn(_colId, false))
|
||||||
|
{
|
||||||
|
_loadElems.elems.push(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < this.children.length; i++)
|
||||||
|
{
|
||||||
|
this.children[i].requireColumn(_colId, null, null, _loadElems);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outerCall)
|
||||||
|
{
|
||||||
|
if (_loadElems.elems.length > 0)
|
||||||
|
{
|
||||||
|
this.readQueue.queueCall(_loadElems.elems, [_colId], function() {
|
||||||
|
_callback.call(_context);
|
||||||
|
}, null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If all elements had been loaded, postpone calling the callback function
|
||||||
|
window.setTimeout(function() {
|
||||||
|
_callback.call(_context);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sorts the data element by the given column, the given sort direction and the
|
||||||
|
* given sort mode - if the tree doesn't have all the column data loaded which is
|
||||||
|
* needed for sorting, it first queries it from the server.
|
||||||
|
*
|
||||||
|
* @param string _colId is the id of the column
|
||||||
|
* @param int _dir is one of EGW_COL_SORTMODE_*
|
||||||
|
* @param int _mode is one of EGW_COL_SORTABLE_*
|
||||||
|
* @param function _callback is a callback function which is called once the
|
||||||
|
* sorting is done
|
||||||
|
* @param
|
||||||
|
* @param boolean _outerCall is used internally, do not specify it
|
||||||
|
*/
|
||||||
|
egwGridDataElement.prototype.sortChildren = function(_colId, _dir, _mode, _callback,
|
||||||
|
_context, _outerCall)
|
||||||
|
{
|
||||||
|
if (typeof _outerCall == "undefined")
|
||||||
|
{
|
||||||
|
_outerCall = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is the outer call of the function, we first have to make sure
|
||||||
|
// that all data for the given column id is available
|
||||||
|
if (_outerCall)
|
||||||
|
{
|
||||||
|
this.requireColumn(_colId, function() {
|
||||||
|
// Call the actual sort part of this function by explicitly passing "false"
|
||||||
|
// to the _outerCall parameter
|
||||||
|
this.sortChildren(_colId, _dir, _mode, _callback, _context, false);
|
||||||
|
|
||||||
|
_callback.call(_context);
|
||||||
|
}, this);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Select the sort function
|
||||||
|
var sortFunc = null;
|
||||||
|
switch (_mode) {
|
||||||
|
case EGW_COL_SORTABLE_ALPHABETIC:
|
||||||
|
sortFunc = egwGridData_sortAlphabetic;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EGW_COL_SORTABLE_NATURAL:
|
||||||
|
sortFunc = egwGridData_sortNatural;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EGW_COL_SORTABLE_NUMERICAL:
|
||||||
|
sortFunc = egwGridData_sortNumerical;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var col = this.columns.getColumnById(_colId);
|
||||||
|
|
||||||
|
// Determine the mode multiplier which is used to sort the list in the
|
||||||
|
// given direction.
|
||||||
|
var dirMul = (_dir == EGW_COL_SORTMODE_ASC) ? 1 : -1;
|
||||||
|
this.children.sort(function (a, b) {
|
||||||
|
// Fetch the sortData or the regular data from the a and b element
|
||||||
|
// and pass it to the sort function
|
||||||
|
var aData = "";
|
||||||
|
var bData = "";
|
||||||
|
|
||||||
|
switch (col.type)
|
||||||
|
{
|
||||||
|
case EGW_COL_TYPE_DEFAULT:
|
||||||
|
aData = a.data[_colId].sortData === null ? a.data[_colId].data :
|
||||||
|
a.data[_colId].sortData;
|
||||||
|
bData = b.data[_colId].sortData === null ? b.data[_colId].data :
|
||||||
|
b.data[_colId].sortData;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EGW_COL_TYPE_NAME_ICON_FIXED:
|
||||||
|
aData = a.caption;
|
||||||
|
bData = b.caption;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EGW_COL_TYPE_CHECKBOX:
|
||||||
|
aData = a.actionObject.getSelected() ? 1 : 0;
|
||||||
|
bData = b.actionObject.getSelected() ? 1 : 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sortFunc(aData, bData) * dirMul;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sort all children
|
||||||
|
for (var i = 0; i < this.children.length; i++)
|
||||||
|
{
|
||||||
|
this.children[i].sortChildren(_colId, _dir, _mode, null, null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sorting is done - call the callback function
|
||||||
|
if (_callback)
|
||||||
|
{
|
||||||
|
_callback.call(_context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function egwGridData_sortAlphabetic(a, b)
|
||||||
|
{
|
||||||
|
return (a > b) ? 1 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function egwGridData_sortNumerical(a, b)
|
||||||
|
{
|
||||||
|
aa = parseFloat(a);
|
||||||
|
bb = parseFloat(b);
|
||||||
|
|
||||||
|
return (aa > bb) ? 1 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See http://my.opera.com/GreyWyvern/blog/show.dml/1671288
|
||||||
|
*/
|
||||||
|
function egwGridData_sortNatural(a, b)
|
||||||
|
{
|
||||||
|
function chunkify(t)
|
||||||
|
{
|
||||||
|
var tz = [], x = 0, y = -1, n = 0, i, j;
|
||||||
|
|
||||||
|
while (i = (j = t.charAt(x++)).charCodeAt(0))
|
||||||
|
{
|
||||||
|
var m = (i == 46 || (i >=48 && i <= 57));
|
||||||
|
if (m !== n)
|
||||||
|
{
|
||||||
|
tz[++y] = "";
|
||||||
|
n = m;
|
||||||
|
}
|
||||||
|
tz[y] += j;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tz;
|
||||||
|
}
|
||||||
|
|
||||||
|
var aa = chunkify(a);
|
||||||
|
var bb = chunkify(b);
|
||||||
|
|
||||||
|
for (x = 0; aa[x] && bb[x]; x++)
|
||||||
|
{
|
||||||
|
if (aa[x] !== bb[x])
|
||||||
|
{
|
||||||
|
var c = Number(aa[x]), d = Number(bb[x]);
|
||||||
|
if (c == aa[x] && d == bb[x])
|
||||||
|
{
|
||||||
|
return c - d;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return (aa[x] > bb[x]) ? 1 : -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return aa.length - bb.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the outermost parent of a set of elements - assume the following tree
|
||||||
|
* where the elements marked with "x" are passed as array to this function:
|
||||||
|
*
|
||||||
|
* ROOT
|
||||||
|
* |- CHILD 1
|
||||||
|
* |- SUB_CHILD 1
|
||||||
|
* |- SUB_CHILD 2 (x)
|
||||||
|
* |- CHILD 2
|
||||||
|
* |- SUB_CHILD 1
|
||||||
|
* |- SUB_SUB_CHILD 1 (x)
|
||||||
|
* The function should now return the "ROOT" elements as this is the outermost
|
||||||
|
* parent the elements have in common.
|
||||||
|
*
|
||||||
|
* TODO: If I think about this, the function actually doesn't work like I'd like
|
||||||
|
* it to behave...
|
||||||
|
*/
|
||||||
function egwGridData_getOutermostParent(_elements)
|
function egwGridData_getOutermostParent(_elements)
|
||||||
{
|
{
|
||||||
var minElem = null;
|
var minElem = null;
|
||||||
@ -772,6 +997,7 @@ function egwGridData_getOutermostParent(_elements)
|
|||||||
return minElem ? (minElem.parent ? minElem.parent : minElem) : null;
|
return minElem ? (minElem.parent ? minElem.parent : minElem) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** - egwGridDataReadQueue -- **/
|
/** - egwGridDataReadQueue -- **/
|
||||||
|
|
||||||
// Some internally used constants
|
// Some internally used constants
|
||||||
@ -809,14 +1035,14 @@ egwGridDataQueue.prototype.setDataRoot = function(_dataRoot)
|
|||||||
* than the one specified in EGW_DATA_QUEUE_MAX_ELEM_COUNT. If this is the case,
|
* than the one specified in EGW_DATA_QUEUE_MAX_ELEM_COUNT. If this is the case,
|
||||||
* the queue is flushed and false is returned, otherwise true.
|
* the queue is flushed and false is returned, otherwise true.
|
||||||
*/
|
*/
|
||||||
egwGridDataQueue.prototype._queue = function(_obj)
|
egwGridDataQueue.prototype._queue = function(_obj, _last)
|
||||||
{
|
{
|
||||||
this.timeoutId++;
|
this.timeoutId++;
|
||||||
|
|
||||||
// Push the queue object onto the queue
|
// Push the queue object onto the queue
|
||||||
this.queue.push(_obj);
|
this.queue.push(_obj);
|
||||||
|
|
||||||
if (this.queue.length > EGW_DATA_QUEUE_MAX_ELEM_COUNT)
|
if (_last && this.queue.length > EGW_DATA_QUEUE_MAX_ELEM_COUNT)
|
||||||
{
|
{
|
||||||
this.flushQueue(false);
|
this.flushQueue(false);
|
||||||
return false;
|
return false;
|
||||||
@ -838,6 +1064,8 @@ egwGridDataQueue.prototype._queue = function(_obj)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set the flush queue timeout
|
// Set the flush queue timeout
|
||||||
|
if (_last)
|
||||||
|
{
|
||||||
var tid = this.timeoutId;
|
var tid = this.timeoutId;
|
||||||
var self = this;
|
var self = this;
|
||||||
window.setTimeout(function() {
|
window.setTimeout(function() {
|
||||||
@ -847,6 +1075,7 @@ egwGridDataQueue.prototype._queue = function(_obj)
|
|||||||
}
|
}
|
||||||
}, EGW_DATA_QUEUE_FLUSH_TIMEOUT);
|
}, EGW_DATA_QUEUE_FLUSH_TIMEOUT);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -869,7 +1098,8 @@ egwGridDataQueue.prototype._accumulateQueueColumns = function(_columns)
|
|||||||
/**
|
/**
|
||||||
* Queues the given element in the fetch-data queue.
|
* Queues the given element in the fetch-data queue.
|
||||||
*
|
*
|
||||||
* @param object _elem is the element whose data will be fetched
|
* @param object/array _elems is a single element or an array of elements -
|
||||||
|
* their data will be fetched
|
||||||
* @param array _columns is an array of column ids which should be fetched. Those
|
* @param array _columns is an array of column ids which should be fetched. Those
|
||||||
* columns will be accumulated over the queue calls. _columns may also take
|
* columns will be accumulated over the queue calls. _columns may also take
|
||||||
* the value EGW_DATA_QUEUE_CHILDREN in which case a request for the children
|
* the value EGW_DATA_QUEUE_CHILDREN in which case a request for the children
|
||||||
@ -879,7 +1109,7 @@ egwGridDataQueue.prototype._accumulateQueueColumns = function(_columns)
|
|||||||
* @param object _context is the context in which the callback function will
|
* @param object _context is the context in which the callback function will
|
||||||
* be executed.
|
* be executed.
|
||||||
*/
|
*/
|
||||||
egwGridDataQueue.prototype.queueCall = function(_elem, _columns, _callback, _context)
|
egwGridDataQueue.prototype.queueCall = function(_elems, _columns, _callback, _context)
|
||||||
{
|
{
|
||||||
if (typeof _callback == "undefined")
|
if (typeof _callback == "undefined")
|
||||||
{
|
{
|
||||||
@ -890,14 +1120,22 @@ egwGridDataQueue.prototype.queueCall = function(_elem, _columns, _callback, _con
|
|||||||
_context = null;
|
_context = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!(_elems instanceof Array))
|
||||||
|
{
|
||||||
|
_elems = [_elems];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < _elems.length; i++)
|
||||||
|
{
|
||||||
|
var last = i == _elems.length - 1;
|
||||||
if (_columns === EGW_DATA_QUEUE_CHILDREN)
|
if (_columns === EGW_DATA_QUEUE_CHILDREN)
|
||||||
{
|
{
|
||||||
this._queue({
|
this._queue({
|
||||||
"elem": _elem,
|
"elem": _elems[i],
|
||||||
"type": EGW_DATA_QUEUE_CHILDREN,
|
"type": EGW_DATA_QUEUE_CHILDREN,
|
||||||
"callback": _callback,
|
"callback": last ? _callback : null,
|
||||||
"context": _context
|
"context": _context
|
||||||
});
|
}, last);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -907,11 +1145,12 @@ egwGridDataQueue.prototype.queueCall = function(_elem, _columns, _callback, _con
|
|||||||
// Queue the element and search in the elements around the given one for
|
// Queue the element and search in the elements around the given one for
|
||||||
// elements whose data isn't loaded yet.
|
// elements whose data isn't loaded yet.
|
||||||
this._queue({
|
this._queue({
|
||||||
"elem": _elem,
|
"elem": _elems[i],
|
||||||
"type": EGW_DATA_QUEUE_ELEM,
|
"type": EGW_DATA_QUEUE_ELEM,
|
||||||
"callback": _callback,
|
"callback": last ? _callback : null,
|
||||||
"context": _context
|
"context": _context
|
||||||
});
|
}, last);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -808,4 +808,21 @@ td.lettersearch {
|
|||||||
border-bottom: 0 none silver !important;
|
border-bottom: 0 none silver !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.egwGridView_outer span.sort {
|
||||||
|
display: inline-block;
|
||||||
|
width: 7px;
|
||||||
|
height: 7px;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
margin: 2px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.egwGridView_outer span.sort.asc {
|
||||||
|
background-image: url(../images/up.png);
|
||||||
|
}
|
||||||
|
|
||||||
|
.egwGridView_outer span.sort.desc {
|
||||||
|
background-image: url(../images/down.png);
|
||||||
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user