Files
server/usr/share/psa-horde/js/tables.js
2026-01-07 20:52:11 +01:00

258 lines
6.8 KiB
JavaScript

/**
* Javascript code for finding all tables with classname "striped" and
* dynamically striping their row colors, and finding all tables with
* classname "sortable" and making them dynamically sortable.
*
* @copyright 2014-2015 Horde LLC
* @license LGPL-2.1 (http://www.horde.org/licenses/lgpl21)
*/
var SORT_COLUMN_INDEX;
function table_stripe(table)
{
var classes = [ 'rowEven', 'rowOdd' ];
// Tables can have more than one tbody element; get all child
// tbody tags and interate through them.
table.select('tbody tr').each(function(c) {
c.removeClassName(classes[1]);
c.addClassName(classes[0]);
classes.reverse(true);
});
}
function table_makeSortable(table)
{
var i = 0;
// We have a first row: assume it's the header, and make its
// contents clickable links.
table.down('tr').childElements().each(function(cell) {
if (cell.hasClassName('nosort')) {
++i;
return;
}
cell.setAttribute('columnIndex', i++);
cell.setStyle({ cursor: 'pointer' });
cell.observe('click', function(e) {
var c = e.findElement('th'),
a = c.down('a');
e.stop();
if (a && !a.hasClassName('sortlink')) {
return true;
}
table_resortTable(c);
return false;
});
});
}
function table_getSortValue(el)
{
var str = '';
if (Object.isString(el) || Object.isUndefined(el)) {
return el;
}
// Use "sortval" if defined.
el = $(el);
if (el.readAttribute('sortval')) {
return el.readAttribute('sortval');
}
if (el.innerText) {
// Not needed but it is faster.
return el.innerText;
}
$A(el.childNodes).each(function(e) {
switch (e.nodeType) {
case 1:
// ELEMENT_NODE
str += table_getSortValue(e);
break;
case 3:
// TEXT_NODE
str += e.nodeValue;
break;
}
});
return str.trim();
}
function table_resortTable(th)
{
var table = th.up('table'),
th_siblings = th.up().childElements(),
itm, sortfn,
sortDown = 0;
th_siblings.each(function(e) {
if (th == e) {
if (e.hasClassName('sortup')) {
e.removeClassName('sortup');
e.addClassName('sortdown');
} else if (e.hasClassName('sortdown')) {
e.removeClassName('sortdown');
e.addClassName('sortup');
sortDown = 1;
} else {
e.addClassName('sortdown');
}
} else {
e.removeClassName('sortup');
e.removeClassName('sortdown');
}
});
// Work out a type for the column
if (th_siblings.size() <= 1) {
return;
}
SORT_COLUMN_INDEX = th.readAttribute('columnIndex');
itm = table_getSortValue(table.down('tbody > tr').cells[SORT_COLUMN_INDEX]);
if (itm.match(/^\d\d[\/\-]\d\d[\/\-]\d\d\d\d$/) ||
itm.match(/^\d\d[\/\-]\d\d[\/\-]\d\d$/)) {
sortfn = table_sort_date;
} else if (itm.match(/^[£$€¥]/)) {
sortfn = table_sort_currency;
} else if (itm.match(/^[\d\.]+$/)) {
sortfn = table_sort_numeric;
} else {
sortfn = table_sort_caseinsensitive;
}
// Don't mix up seperate tbodies; sort each in turn.
$A(table.getElementsByTagName('tbody')).each(function(tbody) {
var bottomRows = [ ],
newRows = $A(tbody.getElementsByTagName('tr'));
newRows.sort(sortfn);
if (sortDown) {
newRows.reverse();
}
// We appendChild rows that already exist to the tbody, so it
// moves them rather than creating new ones. Don't do
// sortbottom rows.
newRows.each(function(r) {
if (r.hasClassName('sortbottom')) {
bottomRows.push(r);
} else {
tbody.appendChild(r);
}
});
// Do sortbottom rows only.
bottomRows.each(function(r) {
tbody.appendChild(r);
});
});
// If we just resorted a striped table, re-stripe it.
if (table.hasClassName('striped')) {
table_stripe(table);
}
// Finally, see if we have a callback function to trigger.
if (typeof table_sortCallback != 'undefined' && Object.isFunction(table_sortCallback)) {
table_sortCallback(table.id, th.id, sortDown);
}
}
function table_sort_date(a, b)
{
// Two digit years less than 50 are treated as 20XX, greater than
// 50 are treated as 19XX.
var aa = table_getSortValue(a.cells[SORT_COLUMN_INDEX]),
bb = table_getSortValue(b.cells[SORT_COLUMN_INDEX]),
dt1, dt2, yr;
if (aa.length == 10) {
dt1 = aa.substr(6, 4) + aa.substr(3, 2) + aa.substr(0, 2);
} else {
yr = aa.substr(6, 2);
if (parseInt(yr, 10) < 50) {
yr = '20' + yr;
} else {
yr = '19' + yr;
}
dt1 = yr + aa.substr(3, 2) + aa.substr(0, 2);
}
if (bb.length == 10) {
dt2 = bb.substr(6, 4) + bb.substr(3, 2) + bb.substr(0, 2);
} else {
yr = bb.substr(6, 2);
if (parseInt(yr, 10) < 50) {
yr = '20' + yr;
} else {
yr = '19' + yr;
}
dt2 = yr + bb.substr(3, 2) + bb.substr(0, 2);
}
if (dt1 == dt2) {
return 0;
} else if (dt1 < dt2) {
return -1;
}
return 1;
}
function table_sort_currency(a, b)
{
var aa = table_getSortValue(a.cells[SORT_COLUMN_INDEX]).replace(/[^0-9.]/g, ''),
bb = table_getSortValue(b.cells[SORT_COLUMN_INDEX]).replace(/[^0-9.]/g, '');
return parseFloat(aa) - parseFloat(bb);
}
function table_sort_numeric(a, b)
{
var aa = parseFloat(table_getSortValue(a.cells[SORT_COLUMN_INDEX])),
bb = parseFloat(table_getSortValue(b.cells[SORT_COLUMN_INDEX]));
if (isNaN(aa)) {
aa = 0;
}
if (isNaN(bb)) {
bb = 0;
}
return aa - bb;
}
function table_sort_caseinsensitive(a, b)
{
var aa = table_getSortValue(a.cells[SORT_COLUMN_INDEX]).toLowerCase(),
bb = table_getSortValue(b.cells[SORT_COLUMN_INDEX]).toLowerCase();
if (aa == bb) {
return 0;
} else if (aa < bb) {
return -1;
}
return 1;
}
function table_sort_default(a, b)
{
var aa = table_getSortValue(a.cells[SORT_COLUMN_INDEX]),
bb = table_getSortValue(b.cells[SORT_COLUMN_INDEX]);
if (aa == bb) {
return 0;
} else if (aa < bb) {
return -1;
}
return 1;
}
/* We do everything onload so that the entire document is present
* before we start searching it for tables. */
document.observe('dom:loaded', function() {
$$('table.striped').each(table_stripe);
$$('table.sortable').each(table_makeSortable);
});