Events sortEnd & updateComplete fire on empty tables. Fixes #532

Sort events now only fire while sorting, previously they fired when
updating an unsorted table
Updated pager to fire appropriately when using ajax
Added unit tests (non-ajax) events
This commit is contained in:
Mottie 2014-03-06 20:28:47 -06:00
parent c4d02b312a
commit 474e446ef8
5 changed files with 104 additions and 38 deletions

View File

@ -124,7 +124,7 @@
} }
}, },
updatePageDisplay = function(table, p, flag) { updatePageDisplay = function(table, p, completed) {
var i, pg, s, out, var i, pg, s, out,
c = table.config, c = table.config,
f = c.$table.hasClass('hasFilters') && !p.ajaxUrl, f = c.$table.hasClass('hasFilters') && !p.ajaxUrl,
@ -168,7 +168,7 @@
} }
} }
pagerArrows(p); pagerArrows(p);
if (p.initialized && flag !== false) { if (p.initialized && completed !== false) {
c.$table.trigger('pagerComplete', p); c.$table.trigger('pagerComplete', p);
// save pager info to storage // save pager info to storage
if (p.savePages && ts.storage) { if (p.savePages && ts.storage) {
@ -331,7 +331,8 @@
} }
// make sure last pager settings are saved, prevents multiple server side calls with // make sure last pager settings are saved, prevents multiple server side calls with
// the same parameters // the same parameters
p.last.totalPages = p.totalPages = Math.ceil( p.totalRows / ( p.size || 10 ) ); p.totalPages = Math.ceil( p.totalRows / ( p.size || 10 ) );
p.last.totalRows = p.totalRows;
p.last.currentFilters = p.currentFilters; p.last.currentFilters = p.currentFilters;
p.last.sortList = (c.sortList || []).join(','); p.last.sortList = (c.sortList || []).join(',');
updatePageDisplay(table, p); updatePageDisplay(table, p);
@ -339,8 +340,9 @@
$t.trigger('updateCache', [function(){ $t.trigger('updateCache', [function(){
if (p.initialized) { if (p.initialized) {
// apply widgets after table has rendered // apply widgets after table has rendered
$t.trigger('applyWidgets'); $t
$t.trigger('pagerChange', p); .trigger('applyWidgets')
.trigger('pagerChange', p);
} }
}]); }]);
} }
@ -429,6 +431,8 @@
renderTable = function(table, rows, p) { renderTable = function(table, rows, p) {
var i, $tb, var i, $tb,
$t = $(table),
c = table.config,
l = rows && rows.length || 0, // rows may be undefined l = rows && rows.length || 0, // rows may be undefined
s = ( p.page * p.size ), s = ( p.page * p.size ),
e = ( s + p.size ); e = ( s + p.size );
@ -438,7 +442,7 @@
moveToLastPage(table, p); moveToLastPage(table, p);
} }
p.isDisabled = false; // needed because sorting will change the page and re-enable the pager p.isDisabled = false; // needed because sorting will change the page and re-enable the pager
if (p.initialized) { $(table).trigger('pagerChange', p); } if (p.initialized) { $t.trigger('pagerChange', p); }
if ( !p.removeRows ) { if ( !p.removeRows ) {
hideRows(table, p); hideRows(table, p);
@ -447,7 +451,7 @@
e = rows.length; e = rows.length;
} }
ts.clearTableBody(table); ts.clearTableBody(table);
$tb = ts.processTbody(table, table.config.$tbodies.eq(0), true); $tb = ts.processTbody(table, c.$tbodies.eq(0), true);
for ( i = s; i < e; i++ ) { for ( i = s; i < e; i++ ) {
$tb.append(rows[i]); $tb.append(rows[i]);
} }
@ -456,7 +460,10 @@
updatePageDisplay(table, p); updatePageDisplay(table, p);
if ( !p.isDisabled ) { fixHeight(table, p); } if ( !p.isDisabled ) { fixHeight(table, p); }
$(table).trigger('applyWidgets'); $t.trigger('applyWidgets');
if (table.isUpdating) {
$t.trigger("updateComplete", table);
}
}, },
showAllRows = function(table, p){ showAllRows = function(table, p){
@ -484,9 +491,10 @@
}); });
}, },
moveToPage = function(table, p, flag) { moveToPage = function(table, p, pageMoved) {
if ( p.isDisabled ) { return; } if ( p.isDisabled ) { return; }
var c = table.config, var c = table.config,
$t = $(table),
l = p.last, l = p.last,
pg = Math.min( p.totalPages, p.filteredPages ); pg = Math.min( p.totalPages, p.filteredPages );
if ( p.page < 0 ) { p.page = 0; } if ( p.page < 0 ) { p.page = 0; }
@ -513,12 +521,16 @@
if (p.ajax) { if (p.ajax) {
getAjax(table, p); getAjax(table, p);
} else if (!p.ajax) { } else if (!p.ajax) {
renderTable(table, table.config.rowsCopy, p); renderTable(table, c.rowsCopy, p);
} }
$.data(table, 'pagerLastPage', p.page); $.data(table, 'pagerLastPage', p.page);
if (p.initialized && flag !== false) { if (p.initialized && pageMoved !== false) {
c.$table.trigger('pageMoved', p); $t
c.$table.trigger('applyWidgets'); .trigger('pageMoved', p)
.trigger('applyWidgets');
if (table.isUpdating) {
$t.trigger('updateComplete');
}
} }
}, },

View File

@ -331,7 +331,8 @@
// empty table - fixes #206/#346 // empty table - fixes #206/#346
if (isEmptyObject(c2)) { if (isEmptyObject(c2)) {
// run pager appender in case the table was just emptied // run pager appender in case the table was just emptied
return c.appender ? c.appender(table, rows) : ''; return c.appender ? c.appender(table, rows) :
table.isUpdating ? c.$table.trigger("updateComplete", table) : ''; // Fixes #532
} }
if (c.debug) { if (c.debug) {
appendTime = new Date(); appendTime = new Date();
@ -368,9 +369,9 @@
} }
// apply table widgets; but not before ajax completes // apply table widgets; but not before ajax completes
if (!init && !c.appender) { ts.applyWidget(table); } if (!init && !c.appender) { ts.applyWidget(table); }
// trigger sortend if (table.isUpdating) {
$(table).trigger("sortEnd", table); c.$table.trigger("updateComplete", table);
$(table).trigger("updateComplete", table); }
} }
// computeTableHeaderCellIndexes from: // computeTableHeaderCellIndexes from:
@ -682,6 +683,7 @@
setHeadersCss(table); setHeadersCss(table);
multisort(table); multisort(table);
appendToTable(table); appendToTable(table);
$table.trigger("sortEnd", table);
}, 1); }, 1);
} }
@ -754,8 +756,9 @@
} }
function resortComplete($table, callback){ function resortComplete($table, callback){
var c = $table[0].config; var table = $table[0],
if (c.pager && !c.pager.ajax) { c = table.config;
if (table.isUpdating) {
$table.trigger('updateComplete'); $table.trigger('updateComplete');
} }
if (typeof callback === "function") { if (typeof callback === "function") {
@ -764,12 +767,13 @@
} }
function checkResort($table, flag, callback) { function checkResort($table, flag, callback) {
var sl = $table[0].config.sortList;
// don't try to resort if the table is still processing // don't try to resort if the table is still processing
// this will catch spamming of the updateCell method // this will catch spamming of the updateCell method
if (flag !== false && !$table[0].isProcessing) { if (flag !== false && !$table[0].isProcessing && sl.length) {
$table.trigger("sorton", [$table[0].config.sortList, function(){ $table.trigger("sorton", [sl, function(){
resortComplete($table, callback); resortComplete($table, callback);
}]); }, true]);
} else { } else {
resortComplete($table, callback); resortComplete($table, callback);
} }
@ -780,7 +784,7 @@
$table = c.$table; $table = c.$table;
// apply easy methods that trigger bound events // apply easy methods that trigger bound events
$table $table
.unbind('sortReset update updateRows updateCell updateAll addRows sorton appendCache updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave '.split(' ').join('.tablesorter ')) .unbind('sortReset update updateRows updateCell updateAll addRows updateComplete sorton appendCache updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave '.split(' ').join('.tablesorter '))
.bind("sortReset.tablesorter", function(e){ .bind("sortReset.tablesorter", function(e){
e.stopPropagation(); e.stopPropagation();
c.sortList = []; c.sortList = [];
@ -790,6 +794,7 @@
}) })
.bind("updateAll.tablesorter", function(e, resort, callback){ .bind("updateAll.tablesorter", function(e, resort, callback){
e.stopPropagation(); e.stopPropagation();
table.isUpdating = true;
ts.refreshWidgets(table, true, true); ts.refreshWidgets(table, true, true);
ts.restoreHeaders(table); ts.restoreHeaders(table);
buildHeaders(table); buildHeaders(table);
@ -799,12 +804,14 @@
}) })
.bind("update.tablesorter updateRows.tablesorter", function(e, resort, callback) { .bind("update.tablesorter updateRows.tablesorter", function(e, resort, callback) {
e.stopPropagation(); e.stopPropagation();
table.isUpdating = true;
// update sorting (if enabled/disabled) // update sorting (if enabled/disabled)
updateHeader(table); updateHeader(table);
commonUpdate(table, resort, callback); commonUpdate(table, resort, callback);
}) })
.bind("updateCell.tablesorter", function(e, cell, resort, callback) { .bind("updateCell.tablesorter", function(e, cell, resort, callback) {
e.stopPropagation(); e.stopPropagation();
table.isUpdating = true;
$table.find(c.selectorRemove).remove(); $table.find(c.selectorRemove).remove();
// get position from the dom // get position from the dom
var l, row, icell, var l, row, icell,
@ -826,6 +833,7 @@
}) })
.bind("addRows.tablesorter", function(e, $row, resort, callback) { .bind("addRows.tablesorter", function(e, $row, resort, callback) {
e.stopPropagation(); e.stopPropagation();
table.isUpdating = true;
if (isEmptyObject(c.cache)) { if (isEmptyObject(c.cache)) {
// empty table, do an update instead - fixes #450 // empty table, do an update instead - fixes #450
updateHeader(table); updateHeader(table);
@ -856,6 +864,9 @@
checkResort($table, resort, callback); checkResort($table, resort, callback);
} }
}) })
.bind("updateComplete.tablesorter", function(){
table.isUpdating = false;
})
.bind("sorton.tablesorter", function(e, list, callback, init) { .bind("sorton.tablesorter", function(e, list, callback, init) {
var c = table.config; var c = table.config;
e.stopPropagation(); e.stopPropagation();
@ -870,6 +881,7 @@
// sort the table and append it to the dom // sort the table and append it to the dom
multisort(table); multisort(table);
appendToTable(table, init); appendToTable(table, init);
$table.trigger("sortEnd", this);
if (typeof callback === "function") { if (typeof callback === "function") {
callback(table); callback(table);
} }
@ -994,7 +1006,7 @@
ts.applyWidget(table, true); ts.applyWidget(table, true);
// if user has supplied a sort list to constructor // if user has supplied a sort list to constructor
if (c.sortList.length > 0) { if (c.sortList.length > 0) {
$table.trigger("sorton", [c.sortList, {}, !c.initWidgets]); $table.trigger("sorton", [c.sortList, {}, !c.initWidgets, true]);
} else { } else {
setHeadersCss(table); setHeadersCss(table);
if (c.initWidgets) { if (c.initWidgets) {
@ -1135,7 +1147,7 @@
// disable tablesorter // disable tablesorter
$t $t
.removeData('tablesorter') .removeData('tablesorter')
.unbind('sortReset update updateAll updateRows updateCell addRows sorton appendCache updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave keypress sortBegin sortEnd '.split(' ').join('.tablesorter ')); .unbind('sortReset update updateAll updateRows updateCell addRows updateComplete sorton appendCache updateCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave keypress sortBegin sortEnd '.split(' ').join('.tablesorter '));
c.$headers.add($f) c.$headers.add($f)
.removeClass( [ts.css.header, c.cssHeader, c.cssAsc, c.cssDesc, ts.css.sortAsc, ts.css.sortDesc, ts.css.sortNone].join(' ') ) .removeClass( [ts.css.header, c.cssHeader, c.cssAsc, c.cssDesc, ts.css.sortAsc, ts.css.sortDesc, ts.css.sortNone].join(' ') )
.removeAttr('data-column'); .removeAttr('data-column');

View File

@ -315,7 +315,7 @@ tsp = ts.pager = {
} }
}, },
updatePageDisplay: function(table, c, flag) { updatePageDisplay: function(table, c, completed) {
var i, pg, s, out, var i, pg, s, out,
wo = c.widgetOptions, wo = c.widgetOptions,
p = c.pager, p = c.pager,
@ -361,7 +361,7 @@ tsp = ts.pager = {
} }
} }
tsp.pagerArrows(c); tsp.pagerArrows(c);
if (p.initialized && flag !== false) { if (p.initialized && completed !== false) {
c.$table.trigger('pagerComplete', c); c.$table.trigger('pagerComplete', c);
// save pager info to storage // save pager info to storage
if (wo.pager_savePages && ts.storage) { if (wo.pager_savePages && ts.storage) {
@ -521,7 +521,8 @@ tsp = ts.pager = {
} }
// make sure last pager settings are saved, prevents multiple server side calls with // make sure last pager settings are saved, prevents multiple server side calls with
// the same parameters // the same parameters
p.last.totalPages = p.totalPages = Math.ceil( p.totalRows / ( p.size || 10 ) ); p.totalPages = Math.ceil( p.totalRows / ( p.size || 10 ) );
p.last.totalRows = p.totalRows;
p.last.currentFilters = p.currentFilters; p.last.currentFilters = p.currentFilters;
p.last.sortList = (c.sortList || []).join(','); p.last.sortList = (c.sortList || []).join(',');
tsp.updatePageDisplay(table, c); tsp.updatePageDisplay(table, c);
@ -650,6 +651,9 @@ tsp = ts.pager = {
wo.pager_startPage = p.page; wo.pager_startPage = p.page;
wo.pager_size = p.size; wo.pager_size = p.size;
c.$table.trigger('applyWidgets'); c.$table.trigger('applyWidgets');
if (table.isUpdating) {
c.$table.trigger('updateComplete');
}
}, },
@ -680,7 +684,7 @@ tsp = ts.pager = {
}); });
}, },
moveToPage: function(table, p, flag) { moveToPage: function(table, p, pageMoved) {
if ( p.isDisabled ) { return; } if ( p.isDisabled ) { return; }
var c = table.config, var c = table.config,
l = p.last, l = p.last,
@ -714,8 +718,11 @@ tsp = ts.pager = {
tsp.renderTable(table, c.rowsCopy); tsp.renderTable(table, c.rowsCopy);
} }
$.data(table, 'pagerLastPage', p.page); $.data(table, 'pagerLastPage', p.page);
if (p.initialized && flag !== false) { if (p.initialized && pageMoved !== false) {
c.$table.trigger('pageMoved', c); c.$table.trigger('pageMoved', c);
if (!p.ajax && table.isUpdating) {
c.$table.trigger('updateComplete');
}
} }
}, },
@ -804,6 +811,8 @@ tsp = ts.pager = {
tsp.moveToPage(table, p, true); tsp.moveToPage(table, p, true);
// update display here in case all rows are removed // update display here in case all rows are removed
tsp.updatePageDisplay(table, c, false); tsp.updatePageDisplay(table, c, false);
} else {
tsp.moveToPage(table, p, true);
} }
} }

View File

@ -10,8 +10,8 @@
<script src="testing/jshint-r12.js"></script> <script src="testing/jshint-r12.js"></script>
<script src="testing/jquery-1.8.3.min.js"></script> <script src="testing/jquery-1.8.3.min.js"></script>
<script src="js/jquery.tablesorter.js"></script> <script src="js/jquery.tablesorter.js"></script>
<script src="js/jquery.metadata.js"></script>
<script src="js/parsers/parser-ipv6.js"></script> <script src="js/parsers/parser-ipv6.js"></script>
<script src="js/jquery.metadata.js"></script>
<script src="testing/testing.js"></script> <script src="testing/testing.js"></script>
<script src="testing/testing-ipv6.js"></script> <script src="testing/testing-ipv6.js"></script>
@ -46,12 +46,15 @@
*/ */
$(function(){ $(function(){
// keep stuff in order; yeah I know every test needs to be atomic - bleh
QUnit.config.reorder = false;
var ts = $.tablesorter, var ts = $.tablesorter,
$table1 = $('.tester:eq(0)'), $table1 = $('.tester:eq(0)'),
$table2 = $('.tester:eq(1)'), $table2 = $('.tester:eq(1)'),
$table3 = $('.tester:eq(2)'), $table3 = $('.tester:eq(2)'),
$table4 = $('.tester:eq(3)'), $table4 = $('.tester:eq(3)'),
$table5 = $('.tester:eq(4)'), // empty table
table1 = $table1[0], table1 = $table1[0],
table2 = $table2[0], table2 = $table2[0],
table3 = $table3[0], table3 = $table3[0],
@ -114,6 +117,20 @@
} }
}); });
$table5
.bind( events.join(' '), function(e){
if (e.type === events[sortIndx%3]) {
sortIndx++;
}
})
.bind('updateComplete', function(){
updateIndx++;
})
.tablesorter();
// ensure all sort events fire on an empty table
$table5.trigger('sorton', [ [[0,0]] ]);
/************************************************ /************************************************
JSHint testing JSHint testing
************************************************/ ************************************************/
@ -235,8 +252,8 @@
parserTests = 85, parserTests = 85,
// skipping metadata parser // skipping metadata parser
sample1 = { sample1 = {
'text' : { 'test': 'test', 'TesT': 'test', '\u00e1 test': 'á test' }, 'text' : { 'test': 'test', 'TesT': 'test', '\u00e1 test': '\u00e1 test' },
'currency' : { '£1': 1, '($2.23)': -2.23, '5€': 5, '(11¤)': -11, '500¥': 500, '25¢': 25, '$1,000.50': 1000.5 }, 'currency' : { '\u00a31': 1, '($2.23)': -2.23, '5\u20ac': 5, '(11\u00a4)': -11, '500\u00a5': 500, '25\u00a2': 25, '$1,000.50': 1000.5 },
'ipAddress' : { '255.255.255.255': 255255255255, '32.32.32.32': 32032032032, '1.1.1.1': 1001001001 }, 'ipAddress' : { '255.255.255.255': 255255255255, '32.32.32.32': 32032032032, '1.1.1.1': 1001001001 },
'url' : { 'http://google.com': 'google.com', 'ftp://fred.com': 'fred.com', 'https://github.com': 'github.com' }, 'url' : { 'http://google.com': 'google.com', 'ftp://fred.com': 'fred.com', 'https://github.com': 'github.com' },
'isoDate' : { '2012/12/12': returnTime('2012/12/12'), '2012-12/12': returnTime('2012/12/12'), '2013-1-1': returnTime('2013/1/1'), '2013/1/1 12:34:56 AM': returnTime('2013/1/1 12:34:56 AM') }, 'isoDate' : { '2012/12/12': returnTime('2012/12/12'), '2012-12/12': returnTime('2012/12/12'), '2013-1-1': returnTime('2013/1/1'), '2013/1/1 12:34:56 AM': returnTime('2013/1/1 12:34:56 AM') },
@ -249,7 +266,7 @@
// switch ignoreCase, sortLocalCompare & shortDate "ddmmyyyy" // switch ignoreCase, sortLocalCompare & shortDate "ddmmyyyy"
sample2 = { sample2 = {
'text' : { 'TesT': 'TesT', '\u00e1 test': 'a test' }, 'text' : { 'TesT': 'TesT', '\u00e1 test': 'a test' },
'currency' : { '€ 123 456,78': 123456.78, '€ 123.456,78': 123456.78 }, 'currency' : { '\u20ac 123 456,78': 123456.78, '\u20ac 123.456,78': 123456.78 },
'shortDate' : { '2/1/2001': returnTime('1/2/2001'), '2-1-2001': returnTime('1/2/2001'), '2 1,2001': returnTime('1/2/2001') } 'shortDate' : { '2/1/2001': returnTime('1/2/2001'), '2-1-2001': returnTime('1/2/2001'), '2 1,2001': returnTime('1/2/2001') }
}, },
// shortdate to "yyyymmdd" // shortdate to "yyyymmdd"
@ -363,8 +380,8 @@
test( "sort Events", function(){ test( "sort Events", function(){
expect(1); expect(1);
// table1 sorted twice in the above test; sortIndx = 6 (3 events x 2) // table1 sorted twice in the above test; sortIndx = 9 then empty table5 x1 (total = 3 events x 3)
equal( sortIndx, 6, 'sortStart, sortBegin & sortComplete fired in order x2' ); equal( sortIndx, 9, 'sortStart, sortBegin & sortEnd fired in order x3; including empty table' );
}); });
/************************************************ /************************************************
@ -414,11 +431,17 @@
tester.cacheCompare( table1, 'all', [ 'test3', 1, 'test2', 2, 'test1', 3, '', '', 'testc', 4, 'testb', 5, 'testa', 6 ], 'update method' ); tester.cacheCompare( table1, 'all', [ 'test3', 1, 'test2', 2, 'test1', 3, '', '', 'testc', 4, 'testb', 5, 'testa', 6 ], 'update method' );
}]); }]);
// update empty table
$table5.trigger('update', [false, function(){
updateCallback++;
}]);
}); });
test( "UpdateComplete Event", function(){ test( "UpdateComplete Event", function(){
expect(1); expect(1);
// table1 updated 4x in the above test // table1 updated 4x in the above test
// table5 updated 1x
equal( updateIndx, updateCallback, 'updatedComplete and update callback functions working properly' ); equal( updateIndx, updateCallback, 'updatedComplete and update callback functions working properly' );
}); });
@ -615,5 +638,14 @@
</tbody> </tbody>
</table> </table>
<!-- empty table -->
<table class="tester">
<thead>
<tr><th>1</th></tr>
</thead>
<tbody>
</tbody>
</table>
</body> </body>
</html> </html>

View File

@ -1,4 +1,5 @@
var ipv6parser = $.tablesorter.getParserById('ipv6Address').format, var ipv6parser = $.tablesorter.getParserById('ipv6Address').format,
ipv6regex = $.tablesorter.regex.ipv6Validate,
ipv6test = function(result, str, expect){ ipv6test = function(result, str, expect){
if (result) { if (result) {
// ok( $.tablesorter.regex.ipv6Validate.test(str), "valid: " + str ); // ok( $.tablesorter.regex.ipv6Validate.test(str), "valid: " + str );
@ -6,7 +7,7 @@ ipv6test = function(result, str, expect){
var t = ipv6parser(str, true); var t = ipv6parser(str, true);
equal( t, expect, 'valid: ' + t + ' \u2190 "' + str + '"' ); equal( t, expect, 'valid: ' + t + ' \u2190 "' + str + '"' );
} else { } else {
ok( !$.tablesorter.regex.ipv6Validate.test(str), 'invalid: "' + str + '"' ); ok( !ipv6regex.test(str), 'invalid: "' + str + '"' );
} }
}, },
ipv6tests = function(){ ipv6tests = function(){