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

View File

@ -331,7 +331,8 @@
// empty table - fixes #206/#346
if (isEmptyObject(c2)) {
// 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) {
appendTime = new Date();
@ -368,9 +369,9 @@
}
// apply table widgets; but not before ajax completes
if (!init && !c.appender) { ts.applyWidget(table); }
// trigger sortend
$(table).trigger("sortEnd", table);
$(table).trigger("updateComplete", table);
if (table.isUpdating) {
c.$table.trigger("updateComplete", table);
}
}
// computeTableHeaderCellIndexes from:
@ -682,6 +683,7 @@
setHeadersCss(table);
multisort(table);
appendToTable(table);
$table.trigger("sortEnd", table);
}, 1);
}
@ -754,8 +756,9 @@
}
function resortComplete($table, callback){
var c = $table[0].config;
if (c.pager && !c.pager.ajax) {
var table = $table[0],
c = table.config;
if (table.isUpdating) {
$table.trigger('updateComplete');
}
if (typeof callback === "function") {
@ -764,12 +767,13 @@
}
function checkResort($table, flag, callback) {
var sl = $table[0].config.sortList;
// don't try to resort if the table is still processing
// this will catch spamming of the updateCell method
if (flag !== false && !$table[0].isProcessing) {
$table.trigger("sorton", [$table[0].config.sortList, function(){
if (flag !== false && !$table[0].isProcessing && sl.length) {
$table.trigger("sorton", [sl, function(){
resortComplete($table, callback);
}]);
}, true]);
} else {
resortComplete($table, callback);
}
@ -780,7 +784,7 @@
$table = c.$table;
// apply easy methods that trigger bound events
$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){
e.stopPropagation();
c.sortList = [];
@ -790,6 +794,7 @@
})
.bind("updateAll.tablesorter", function(e, resort, callback){
e.stopPropagation();
table.isUpdating = true;
ts.refreshWidgets(table, true, true);
ts.restoreHeaders(table);
buildHeaders(table);
@ -799,12 +804,14 @@
})
.bind("update.tablesorter updateRows.tablesorter", function(e, resort, callback) {
e.stopPropagation();
table.isUpdating = true;
// update sorting (if enabled/disabled)
updateHeader(table);
commonUpdate(table, resort, callback);
})
.bind("updateCell.tablesorter", function(e, cell, resort, callback) {
e.stopPropagation();
table.isUpdating = true;
$table.find(c.selectorRemove).remove();
// get position from the dom
var l, row, icell,
@ -826,6 +833,7 @@
})
.bind("addRows.tablesorter", function(e, $row, resort, callback) {
e.stopPropagation();
table.isUpdating = true;
if (isEmptyObject(c.cache)) {
// empty table, do an update instead - fixes #450
updateHeader(table);
@ -856,6 +864,9 @@
checkResort($table, resort, callback);
}
})
.bind("updateComplete.tablesorter", function(){
table.isUpdating = false;
})
.bind("sorton.tablesorter", function(e, list, callback, init) {
var c = table.config;
e.stopPropagation();
@ -870,6 +881,7 @@
// sort the table and append it to the dom
multisort(table);
appendToTable(table, init);
$table.trigger("sortEnd", this);
if (typeof callback === "function") {
callback(table);
}
@ -994,7 +1006,7 @@
ts.applyWidget(table, true);
// if user has supplied a sort list to constructor
if (c.sortList.length > 0) {
$table.trigger("sorton", [c.sortList, {}, !c.initWidgets]);
$table.trigger("sorton", [c.sortList, {}, !c.initWidgets, true]);
} else {
setHeadersCss(table);
if (c.initWidgets) {
@ -1135,7 +1147,7 @@
// disable tablesorter
$t
.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)
.removeClass( [ts.css.header, c.cssHeader, c.cssAsc, c.cssDesc, ts.css.sortAsc, ts.css.sortDesc, ts.css.sortNone].join(' ') )
.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,
wo = c.widgetOptions,
p = c.pager,
@ -361,7 +361,7 @@ tsp = ts.pager = {
}
}
tsp.pagerArrows(c);
if (p.initialized && flag !== false) {
if (p.initialized && completed !== false) {
c.$table.trigger('pagerComplete', c);
// save pager info to 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
// 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.sortList = (c.sortList || []).join(',');
tsp.updatePageDisplay(table, c);
@ -650,6 +651,9 @@ tsp = ts.pager = {
wo.pager_startPage = p.page;
wo.pager_size = p.size;
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; }
var c = table.config,
l = p.last,
@ -714,8 +718,11 @@ tsp = ts.pager = {
tsp.renderTable(table, c.rowsCopy);
}
$.data(table, 'pagerLastPage', p.page);
if (p.initialized && flag !== false) {
if (p.initialized && pageMoved !== false) {
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);
// update display here in case all rows are removed
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/jquery-1.8.3.min.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/jquery.metadata.js"></script>
<script src="testing/testing.js"></script>
<script src="testing/testing-ipv6.js"></script>
@ -46,12 +46,15 @@
*/
$(function(){
// keep stuff in order; yeah I know every test needs to be atomic - bleh
QUnit.config.reorder = false;
var ts = $.tablesorter,
$table1 = $('.tester:eq(0)'),
$table2 = $('.tester:eq(1)'),
$table3 = $('.tester:eq(2)'),
$table4 = $('.tester:eq(3)'),
$table5 = $('.tester:eq(4)'), // empty table
table1 = $table1[0],
table2 = $table2[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
************************************************/
@ -235,8 +252,8 @@
parserTests = 85,
// skipping metadata parser
sample1 = {
'text' : { 'test': 'test', 'TesT': 'test', '\u00e1 test': 'á test' },
'currency' : { '£1': 1, '($2.23)': -2.23, '5€': 5, '(11¤)': -11, '500¥': 500, '25¢': 25, '$1,000.50': 1000.5 },
'text' : { 'test': 'test', 'TesT': 'test', '\u00e1 test': '\u00e1 test' },
'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 },
'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') },
@ -249,7 +266,7 @@
// switch ignoreCase, sortLocalCompare & shortDate "ddmmyyyy"
sample2 = {
'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 to "yyyymmdd"
@ -363,8 +380,8 @@
test( "sort Events", function(){
expect(1);
// table1 sorted twice in the above test; sortIndx = 6 (3 events x 2)
equal( sortIndx, 6, 'sortStart, sortBegin & sortComplete fired in order x2' );
// table1 sorted twice in the above test; sortIndx = 9 then empty table5 x1 (total = 3 events x 3)
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' );
}]);
// update empty table
$table5.trigger('update', [false, function(){
updateCallback++;
}]);
});
test( "UpdateComplete Event", function(){
expect(1);
// table1 updated 4x in the above test
// table5 updated 1x
equal( updateIndx, updateCallback, 'updatedComplete and update callback functions working properly' );
});
@ -615,5 +638,14 @@
</tbody>
</table>
<!-- empty table -->
<table class="tester">
<thead>
<tr><th>1</th></tr>
</thead>
<tbody>
</tbody>
</table>
</body>
</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){
if (result) {
// ok( $.tablesorter.regex.ipv6Validate.test(str), "valid: " + str );
@ -6,7 +7,7 @@ ipv6test = function(result, str, expect){
var t = ipv6parser(str, true);
equal( t, expect, 'valid: ' + t + ' \u2190 "' + str + '"' );
} else {
ok( !$.tablesorter.regex.ipv6Validate.test(str), 'invalid: "' + str + '"' );
ok( !ipv6regex.test(str), 'invalid: "' + str + '"' );
}
},
ipv6tests = function(){