sort multiple tbodies

This commit is contained in:
Rob Garrison 2012-05-03 09:46:31 -05:00
parent 64b63632ec
commit 28e9a07d59
3 changed files with 155 additions and 129 deletions

View File

@ -235,7 +235,7 @@
if (e > rows.length ) {
e = rows.length;
}
$(table.tBodies[0]).empty();
$.tablesorter.clearTableBody(table);
for (i = s; i < e; i++) {
o = rows[i];
l = o.length;

View File

@ -26,6 +26,7 @@
cssAsc: "tablesorter-headerSortUp",
cssDesc: "tablesorter-headerSortDown",
cssChildRow: "expand-child",
cssInfoBlock: "tablesorter-infoOnly",
sortInitialOrder: "asc",
sortMultiSortKey: "shiftKey",
sortForce: null,
@ -196,42 +197,44 @@
/* utils */
function buildCache(table) {
var b = table.tBodies[0],
totalRows = (b && b.rows.length) || 0,
totalCells = (b.rows[0] && b.rows[0].cells.length) || 0,
parsers = table.config.parsers,
cache = {
row: [],
normalized: []
},
var b = table.tBodies,
tc = table.config,
totalRows,
totalCells,
parsers = tc.parsers,
t, i, j, c, cols, cacheTime;
if (table.config.debug) {
tc.cache = {};
if (tc.debug) {
cacheTime = new Date();
}
for (i = 0; i < totalRows; ++i) {
/** Add the table data to main data array */
c = $(b.rows[i]);
cols = [];
// if this is a child row, add it to the last row's children and continue to the next row
if (c.hasClass(table.config.cssChildRow)) {
cache.row[cache.row.length - 1] = cache.row[cache.row.length - 1].add(c);
// go to the next for loop
continue;
for (k = 0; k < b.length; k++) {
tc.cache[k] = { row: [], normalized: [] };
totalRows = (b[k] && b[k].rows.length) || 0;
totalCells = (b[k].rows[0] && b[k].rows[0].cells.length) || 0;
for (i = 0; i < totalRows; ++i) {
/** Add the table data to main data array */
c = $(b[k].rows[i]);
cols = [];
// if this is a child row, add it to the last row's children and continue to the next row
if (c.hasClass(tc.cssChildRow)) {
tc.cache[k].row[tc.cache[k].row.length - 1] = tc.cache[k].row[tc.cache[k].row.length - 1].add(c);
// go to the next for loop
continue;
}
tc.cache[k].row.push(c);
for (j = 0; j < totalCells; ++j) {
t = trimAndGetNodeText(tc, c[0].cells[j], j);
// don't bother parsing if the string is empty - previously parsing would change it to zero
cols.push( parsers[j].format(t, table, c[0].cells[j], j) );
}
cols.push(tc.cache[k].normalized.length); // add position for rowCache
tc.cache[k].normalized.push(cols);
}
cache.row.push(c);
for (j = 0; j < totalCells; ++j) {
t = trimAndGetNodeText(table.config, c[0].cells[j], j);
// don't bother parsing if the string is empty - previously parsing would change it to zero
cols.push( parsers[j].format(t, table, c[0].cells[j], j) );
}
cols.push(cache.normalized.length); // add position for rowCache
cache.normalized.push(cols);
}
if (table.config.debug) {
if (tc.debug) {
benchmark("Building cache for " + totalRows + " rows", cacheTime);
}
table.config.cache = cache;
return cache;
}
function getWidgetById(name) {
@ -259,30 +262,35 @@
}
}
function appendToTable(table, cache) {
function appendToTable(table) {
var c = table.config,
r = cache.row,
n = cache.normalized,
totalRows = n.length,
checkCell = totalRows ? (n[0].length - 1) : 0,
b = table.tBodies,
rows = [],
f = document.createDocumentFragment(),
i, j, l, pos, appendTime;
r, n, totalRows, checkCell,
f, i, j, l, pos, appendTime;
if (c.debug) {
appendTime = new Date();
}
for (i = 0; i < totalRows; i++) {
pos = n[i][checkCell];
rows.push(r[pos]);
// removeRows used by the pager plugin
if (!c.appender || !c.removeRows) {
l = r[pos].length;
for (j = 0; j < l; j++) {
f.appendChild(r[pos][j]);
for (k = 0; k < b.length; k++) {
f = document.createDocumentFragment();
r = c.cache[k].row;
n = c.cache[k].normalized;
totalRows = n.length;
checkCell = totalRows ? (n[0].length - 1) : 0;
for (i = 0; i < totalRows; i++) {
pos = n[i][checkCell];
rows.push(r[pos]);
// removeRows used by the pager plugin
if (!c.appender || !c.removeRows) {
l = r[pos].length;
for (j = 0; j < l; j++) {
f.appendChild(r[pos][j]);
}
}
}
table.tBodies[k].appendChild(f);
}
table.tBodies[0].appendChild(f);
if (c.appender) {
c.appender(table, rows);
}
@ -464,46 +472,50 @@
}
/* sorting methods - reverted sorting method back to version 2.0.3 */
function multisort(table,sortList,cache) {
var dynamicExp = "var sortWrapper = function(a,b) {",
col, mx = 0, dir = 0, tc = table.config, lc = cache.normalized.length,
l = sortList.length, sortTime, i, j, c, s, e, order, orgOrderCol;
function multisort(table, sortList) {
var dynamicExp, col, mx = 0, dir = 0, tc = table.config,
l = sortList.length, bl = table.tBodies.length,
sortTime, i, j, c, cache, lc, s, e, order, orgOrderCol;
if (tc.debug) { sortTime = new Date(); }
for (i=0; i < l; i++) {
c = sortList[i][0];
order = sortList[i][1];
s = getCachedSortType(tc.parsers,c) === "text" ? "Text" : "Numeric";
s += order === 0 ? "" : "Desc";
e = "e" + i;
// get max column value (ignore sign)
if (/Numeric/.test(s) && tc.strings[c]) {
for (j=0; j < lc; j++) {
col = Math.abs(parseFloat(cache.normalized[j][c]));
mx = Math.max( mx, isNaN(col) ? 0 : col );
}
// sort strings in numerical columns
if (typeof(tc.string[tc.strings[c]]) === 'boolean') {
dir = (order === 0 ? 1 : -1) * (tc.string[tc.strings[c]] ? -1 : 1);
} else {
dir = (tc.strings[c]) ? tc.string[tc.strings[c]] || 0 : 0;
for (k = 0; k < bl; k++) {
dynamicExp = "var sortWrapper = function(a,b) {";
cache = tc.cache[k];
lc = cache.normalized.length;
for (i = 0; i < l; i++) {
c = sortList[i][0];
order = sortList[i][1];
s = getCachedSortType(tc.parsers,c) === "text" ? "Text" : "Numeric";
s += order === 0 ? "" : "Desc";
e = "e" + i;
// get max column value (ignore sign)
if (/Numeric/.test(s) && tc.strings[c]) {
for (j = 0; j < lc; j++) {
col = Math.abs(parseFloat(cache.normalized[j][c]));
mx = Math.max( mx, isNaN(col) ? 0 : col );
}
// sort strings in numerical columns
if (typeof(tc.string[tc.strings[c]]) === 'boolean') {
dir = (order === 0 ? 1 : -1) * (tc.string[tc.strings[c]] ? -1 : 1);
} else {
dir = (tc.strings[c]) ? tc.string[tc.strings[c]] || 0 : 0;
}
}
dynamicExp += "var " + e + " = sort" + s + "(a[" + c + "],b[" + c + "]," + c + "," + mx + "," + dir + "); ";
dynamicExp += "if (" + e + ") { return " + e + "; } ";
dynamicExp += "else { ";
}
dynamicExp += "var " + e + " = sort" + s + "(a[" + c + "],b[" + c + "]," + c + "," + mx + "," + dir + "); ";
dynamicExp += "if (" + e + ") { return " + e + "; } ";
dynamicExp += "else { ";
}
// if value is the same keep orignal order
orgOrderCol = (cache.normalized && cache.normalized[0]) ? cache.normalized[0].length - 1 : 0;
dynamicExp += "return a[" + orgOrderCol + "]-b[" + orgOrderCol + "];";
for (i=0; i < l; i++) {
// if value is the same keep orignal order
orgOrderCol = (cache.normalized && cache.normalized[0]) ? cache.normalized[0].length - 1 : 0;
dynamicExp += "return a[" + orgOrderCol + "]-b[" + orgOrderCol + "];";
for (i=0; i < l; i++) {
dynamicExp += "}; ";
}
dynamicExp += "return 0; ";
dynamicExp += "}; ";
eval(dynamicExp);
cache.normalized.sort(sortWrapper); // sort using eval expression
}
dynamicExp += "return 0; ";
dynamicExp += "}; ";
eval(dynamicExp);
cache.normalized.sort(sortWrapper); // sort using eval expression
if (tc.debug) { benchmark("Sorting on " + sortList.toString() + " and dir " + order+ " time", sortTime); }
return cache;
}
// Natural sort modified from: http://www.webdeveloper.com/forum/showthread.php?t=107909
@ -588,7 +600,7 @@
// if no thead or tbody quit.
if (!this.tHead || this.tBodies.length === 0) { return; }
// declare
var $this, $headers, cache, config,
var $this, $headers, config,
totalRows, $cell, c, i, j, k, a, s, o;
// new blank config object
this.config = {};
@ -605,14 +617,14 @@
// try to auto detect column type, and store in tables config
c.parsers = buildParserCache(this, $headers);
// build the cache for the tbody cells
cache = buildCache(this);
buildCache(this);
// fixate columns if the users supplies the fixedWidth option
fixColumnWidth(this);
// apply event handling to headers
// this is to big, perhaps break it out?
$headers
.click(function(e) {
totalRows = ($this[0].tBodies[0] && $this[0].tBodies[0].rows.length) || 0;
// totalRows = ($this[0].tBodies[0] && $this[0].tBodies[0].rows.length) || 0;
if (!this.sortDisabled) {
// Only call sortStart if sorting is enabled.
$this.trigger("sortStart", tbl[0]);
@ -680,7 +692,7 @@
$this.trigger("sortBegin", tbl[0]);
// set css for headers
setHeadersCss($this[0], $headers, c.sortList);
appendToTable($this[0], multisort($this[0], c.sortList, cache));
appendToTable($this[0], multisort($this[0], c.sortList));
// stop normal event by returning false
return false;
}
@ -697,45 +709,44 @@
// apply easy methods that trigger binded events
$this
.bind("update", function(e, resort) {
var t = this, c = t.config;
// remove rows/elements before update
$(c.selectorRemove, t.tBodies[0]).remove();
$(c.selectorRemove, this).remove();
// rebuild parsers.
c.parsers = buildParserCache(t, $headers);
c.parsers = buildParserCache(this, $headers);
// rebuild the cache map
cache = buildCache(t);
buildCache(this);
if (resort !== false) { $this.trigger("sorton", [c.sortList]); }
})
.bind("updateCell", function(e, cell, resort) {
// get position from the dom.
var pos = [(cell.parentNode.rowIndex - 1), cell.cellIndex];
var pos = [(cell.parentNode.rowIndex - 1), cell.cellIndex],
// update cache - format: function(s, table, cell, cellIndex)
cache.normalized[pos[0]][pos[1]] = c.parsers[pos[1]].format(getElementText(c, cell, pos[1]), $this, cell, pos[1]);
c.cache = cache;
tbodyindex = $(cell).closest('tbody').index();
table.cache[tbodyindex].normalized[pos[0]][pos[1]] = c.parsers[pos[1]].format(getElementText(c, cell, pos[1]), $this, cell, pos[1]);
if (resort !== false) { $this.trigger("sorton", [c.sortList]); }
})
.bind("addRows", function(e, row, resort) {
var i, rows = row.filter('tr').length,
dat = [], l = row[0].cells.length;
.bind("addRows", function(e, $row, resort) {
var i, rows = $row.filter('tr').length,
dat = [], l = $row[0].cells.length,
tbodyindex = $row.closest('tbody').index();
// add each row
for (i = 0; i < rows; i++) {
// add each cell
for (j = 0; j < l; j++) {
dat[j] = c.parsers[j].format(getElementText(c, row[i].cells[j], j), $this, row[i].cells[j], j );
dat[j] = c.parsers[j].format(getElementText(c, $row[i].cells[j], j), $this, $row[i].cells[j], j );
}
// add the row index to the end
dat.push(cache.row.length);
dat.push(c.cache[tbodyindex].row.length);
// update cache
cache.row.push([row[i]]);
cache.normalized.push(dat);
c.cache[tbodyindex].row.push([$row[i]]);
c.cache[tbodyindex].normalized.push(dat);
dat = [];
}
c.cache = cache;
// resort using current settings
if (resort !== false) { $this.trigger("sorton", [c.sortList]); }
})
.bind("sorton", function(e, list) {
$(this).trigger("sortStart", tbl[0]);
$this.trigger("sortStart", tbl[0]);
c.sortList = list;
// update and store the sortlist
var sortList = c.sortList;
@ -744,10 +755,10 @@
// set css for headers
setHeadersCss(this, $headers, sortList);
// sort the table and append it to the dom
appendToTable(this, multisort(this, sortList, cache));
appendToTable(this, multisort(this, sortList));
})
.bind("appendCache", function () {
appendToTable(this, cache);
appendToTable(this);
})
.bind("applyWidgetId", function (e, id) {
getWidgetById(id).format(this);
@ -769,6 +780,8 @@
applyWidget(this);
}
this.hasInitialized = true;
$this.trigger('tablesorter-initialized', this);
if (typeof c.initialized === 'function') { c.initialized(this); }
});
};
this.addParser = function(parser) {
@ -807,7 +820,7 @@
return (/^[\-+(]?\d*[)]?$/).test($.trim(s.replace(/[,.'\s]/g, '')));
};
this.clearTableBody = function (table) {
$(table.tBodies[0]).empty();
$(table.tBodies).filter(':not(.' + table.config.cssInfoBlock + ')').empty();
};
}
})();
@ -970,9 +983,10 @@
ts.addWidget({
id: "zebra",
format: function(table) {
var $tr, row = 0, even, time,
var $tr, row, even, time, k,
c = table.config,
child = c.cssChildRow,
b = table.tBodies,
css = [ "even", "odd" ];
// maintain backwards compatibility
css = c.widgetZebra && c.hasOwnProperty('css') ? c.widgetZebra.css :
@ -980,16 +994,22 @@
if (table.config.debug) {
time = new Date();
}
// loop through the visible rows
$("tr:visible", table.tBodies[0]).each(function() {
$tr = $(this);
// style children rows the same way the parent row was styled
if (!$tr.hasClass(child)) { row++; }
even = (row % 2 === 0);
$tr
.removeClass(css[even ? 1 : 0])
.addClass(css[even ? 0 : 1]);
});
for (k = 0; k < b.length; k++ ) {
row = 0;
// loop through the visible rows
$tr = $(b[k]).filter(':not(' + c.cssInfoBlock + ')').find('tr:visible:not(.' + c.cssInfoBlock + ')');
if ($tr.length > 1) {
$tr.each(function() {
$tr = $(this);
// style children rows the same way the parent row was styled
if (!$tr.hasClass(child)) { row++; }
even = (row % 2 === 0);
$tr
.removeClass(css[even ? 1 : 0])
.addClass(css[even ? 0 : 1]);
});
}
}
if (table.config.debug) {
$.tablesorter.benchmark("Applying Zebra widget", time);
}

View File

@ -125,8 +125,9 @@ $.tablesorter.addWidget({
$.tablesorter.addWidget({
id: "columns",
format: function(table) {
var $td, time, i, last, rmv,
var $tr, $td, time, i, last, rmv, k,
c = table.config,
b = table.tBodies,
list = c.sortList,
len = list.length,
css = [ "primary", "secondary", "tertiary" ]; // default options
@ -140,21 +141,24 @@ $.tablesorter.addWidget({
}
// check if there is a sort (on initialization there may not be one)
if (list && list[0]) {
// loop through the visible rows
$("tr:visible", table.tBodies[0]).each(function (i) {
$td = $(this).children().removeClass(rmv);
// primary sort column class
$td.eq(list[0][0]).addClass(css[0]);
if (len > 1) {
for (i=1; i<len; i++){
// secondary, tertiary, etc sort column classes
$td.eq(list[i][0]).addClass( css[i] || css[last] );
for (k = 0; k < b.length; k++ ) {
// loop through the visible rows
$tr = $(b[k]).filter(':not(' + c.cssInfoBlock + ')').find('tr:visible:not(.' + c.cssInfoBlock + ')');
$tr.each(function (i) {
$td = $(this).children().removeClass(rmv);
// primary sort column class
$td.eq(list[0][0]).addClass(css[0]);
if (len > 1) {
for (i=1; i<len; i++){
// secondary, tertiary, etc sort column classes
$td.eq(list[i][0]).addClass( css[i] || css[last] );
}
}
}
});
});
}
} else {
// remove all column classes if sort is cleared (sortReset)
$("td", table.tBodies[0]).removeClass(rmv);
$("td", table).removeClass(rmv);
}
if (c.debug) {
$.tablesorter.benchmark("Applying Columns widget", time);
@ -192,7 +196,7 @@ $.tablesorter.addWidget({
if (v.join('') === '') {
$t.find('tr').show();
} else {
$t.find('tbody').find('tr:not(.' + c.cssChildRow + ')').each(function(){
$t.find('tbody').find('tr:not(.' + c.cssChildRow + '):not(.' + c.cssInfoBlock + ')').each(function(){
r = true;
cr = $(this).nextUntil('tr:not(.' + c.cssChildRow + ')');
// so, if "table.config.widgetOptions.filter_childRows" is true and there is
@ -238,6 +242,7 @@ $.tablesorter.addWidget({
css = wo.stickyHeaders || 'tablesorter-stickyHeader',
innr = '.tablesorter-header-inner',
firstCell = hdrCells.eq(0),
tfoot = $table.find('tfoot'),
sticky = header.find('tr.tablesorter-header:not(.sticky-false)').clone()
.removeClass('tablesorter-header')
.addClass(css)
@ -284,7 +289,8 @@ $.tablesorter.addWidget({
.scroll(function(){
var offset = firstCell.offset(),
sTop = win.scrollTop(),
vis = ((sTop > offset.top) && (sTop < offset.top + $table.find('tbody').height())) ? 'visible' : 'hidden';
tableHt = $table.height() - (firstCell.height() + (tfoot.height() || 0)),
vis = (sTop > offset.top) && (sTop < offset.top + tableHt) ? 'visible' : 'hidden';
sticky.css({
left : offset.left - win.scrollLeft(),
visibility : vis