( function ( $, mw ) {
var header = [ 'Planet', 'Radius (km)' ],
// Data set "planets"
mercury = [ 'Mercury', '2439.7' ],
venus = [ 'Venus', '6051.8' ],
earth = [ 'Earth', '6371.0' ],
mars = [ 'Mars', '3390.0' ],
jupiter = [ 'Jupiter', '69911' ],
saturn = [ 'Saturn', '58232' ],
planets = [ mercury, venus, earth, mars, jupiter, saturn ],
planetsAscName = [ earth, jupiter, mars, mercury, saturn, venus ],
planetsAscRadius = [ mercury, mars, venus, earth, saturn, jupiter ],
planetsRowspan,
planetsRowspanII,
planetsAscNameLegacy,
// Data set "simple"
a1 = [ 'A', '1' ],
a2 = [ 'A', '2' ],
a3 = [ 'A', '3' ],
b1 = [ 'B', '1' ],
b2 = [ 'B', '2' ],
b3 = [ 'B', '3' ],
simple = [ a2, b3, a1, a3, b2, b1 ],
simpleAsc = [ a1, a2, a3, b1, b2, b3 ],
simpleDescasc = [ b1, b2, b3, a1, a2, a3 ],
// Data set "colspan"
header4 = [ 'column1a', 'column1b', 'column1c', 'column2' ],
aaa1 = [ 'A', 'A', 'A', '1' ],
aab5 = [ 'A', 'A', 'B', '5' ],
abc3 = [ 'A', 'B', 'C', '3' ],
bbc2 = [ 'B', 'B', 'C', '2' ],
caa4 = [ 'C', 'A', 'A', '4' ],
colspanInitial = [ aab5, aaa1, abc3, bbc2, caa4 ],
// Data set "ipv4"
ipv4 = [
// Some randomly generated fake IPs
[ '45.238.27.109' ],
[ '44.172.9.22' ],
[ '247.240.82.209' ],
[ '204.204.132.158' ],
[ '170.38.91.162' ],
[ '197.219.164.9' ],
[ '45.68.154.72' ],
[ '182.195.149.80' ]
],
ipv4Sorted = [
// Sort order should go octet by octet
[ '44.172.9.22' ],
[ '45.68.154.72' ],
[ '45.238.27.109' ],
[ '170.38.91.162' ],
[ '182.195.149.80' ],
[ '197.219.164.9' ],
[ '204.204.132.158' ],
[ '247.240.82.209' ]
],
// Data set "umlaut"
umlautWords = [
[ 'Günther' ],
[ 'Peter' ],
[ 'Björn' ],
[ 'Bjorn' ],
[ 'Apfel' ],
[ 'Äpfel' ],
[ 'Strasse' ],
[ 'Sträßschen' ]
],
umlautWordsSorted = [
[ 'Äpfel' ],
[ 'Apfel' ],
[ 'Björn' ],
[ 'Bjorn' ],
[ 'Günther' ],
[ 'Peter' ],
[ 'Sträßschen' ],
[ 'Strasse' ]
],
complexMDYDates = [
[ 'January, 19 2010' ],
[ 'April 21 1991' ],
[ '04 22 1991' ],
[ '5.12.1990' ],
[ 'December 12 \'10' ]
],
complexMDYSorted = [
[ '5.12.1990' ],
[ 'April 21 1991' ],
[ '04 22 1991' ],
[ 'January, 19 2010' ],
[ 'December 12 \'10' ]
],
currencyUnsorted = [
[ '1.02 $' ],
[ '$ 3.00' ],
[ '€ 2,99' ],
[ '$ 1.00' ],
[ '$3.50' ],
[ '$ 1.50' ],
[ '€ 0.99' ]
],
currencySorted = [
[ '€ 0.99' ],
[ '$ 1.00' ],
[ '1.02 $' ],
[ '$ 1.50' ],
[ '$ 3.00' ],
[ '$3.50' ],
// Comma's sort after dots
// Not intentional but test to detect changes
[ '€ 2,99' ]
],
numbers = [
[ '12' ],
[ '7' ],
[ '13,000' ],
[ '9' ],
[ '14' ],
[ '8.0' ]
],
numbersAsc = [
[ '7' ],
[ '8.0' ],
[ '9' ],
[ '12' ],
[ '14' ],
[ '13,000' ]
],
correctDateSorting1 = [
[ '01 January 2010' ],
[ '05 February 2010' ],
[ '16 January 2010' ]
],
correctDateSortingSorted1 = [
[ '01 January 2010' ],
[ '16 January 2010' ],
[ '05 February 2010' ]
],
correctDateSorting2 = [
[ 'January 01 2010' ],
[ 'February 05 2010' ],
[ 'January 16 2010' ]
],
correctDateSortingSorted2 = [
[ 'January 01 2010' ],
[ 'January 16 2010' ],
[ 'February 05 2010' ]
];
QUnit.module( 'jquery.tablesorter', QUnit.newMwEnvironment( {
setup: function () {
this.liveMonths = mw.language.months;
mw.language.months = {
keys: {
names: [ 'january', 'february', 'march', 'april', 'may_long', 'june',
'july', 'august', 'september', 'october', 'november', 'december' ],
genitive: [ 'january-gen', 'february-gen', 'march-gen', 'april-gen', 'may-gen', 'june-gen',
'july-gen', 'august-gen', 'september-gen', 'october-gen', 'november-gen', 'december-gen' ],
abbrev: [ 'jan', 'feb', 'mar', 'apr', 'may', 'jun',
'jul', 'aug', 'sep', 'oct', 'nov', 'dec' ]
},
names: [ 'January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December' ],
genitive: [ 'January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December' ],
abbrev: [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ]
};
},
teardown: function () {
mw.language.months = this.liveMonths;
},
config: {
wgDefaultDateFormat: 'dmy',
wgSeparatorTransformTable: [ '', '' ],
wgDigitTransformTable: [ '', '' ],
wgPageContentLanguage: 'en'
}
} ) );
/**
* Create an HTML table from an array of row arrays containing text strings.
* First row will be header row. No fancy rowspan/colspan stuff.
*
* @param {String[]} header
* @param {String[][]} data
* @return {jQuery}
*/
function tableCreate( header, data ) {
var i,
$table = $( '
' ),
$thead = $table.find( 'thead' ),
$tbody = $table.find( 'tbody' ),
$tr = $( '' );
$.each( header, function ( i, str ) {
var $th = $( '' );
$th.text( str ).appendTo( $tr );
} );
$tr.appendTo( $thead );
for ( i = 0; i < data.length; i++ ) {
/*jshint loopfunc: true */
$tr = $( ' |
' );
$.each( data[ i ], function ( j, str ) {
var $td = $( '' );
$td.text( str ).appendTo( $tr );
} );
$tr.appendTo( $tbody );
}
return $table;
}
/**
* Extract text from table.
*
* @param {jQuery} $table
* @return {String[][]}
*/
function tableExtract( $table ) {
var data = [];
$table.find( 'tbody' ).find( 'tr' ).each( function ( i, tr ) {
var row = [];
$( tr ).find( 'td,th' ).each( function ( i, td ) {
row.push( $( td ).text() );
} );
data.push( row );
} );
return data;
}
/**
* Run a table test by building a table with the given data,
* running some callback on it, then checking the results.
*
* @param {String} msg text to pass on to qunit for the comparison
* @param {String[]} header cols to make the table
* @param {String[][]} data rows/cols to make the table
* @param {String[][]} expected rows/cols to compare against at end
* @param {function($table)} callback something to do with the table before we compare
*/
function tableTest( msg, header, data, expected, callback ) {
QUnit.test( msg, 1, function ( assert ) {
var extracted,
$table = tableCreate( header, data );
// Give caller a chance to set up sorting and manipulate the table.
callback( $table );
// Table sorting is done synchronously; if it ever needs to change back
// to asynchronous, we'll need a timeout or a callback here.
extracted = tableExtract( $table );
assert.deepEqual( extracted, expected, msg );
} );
}
/**
* Run a table test by building a table with the given HTML,
* running some callback on it, then checking the results.
*
* @param {String} msg text to pass on to qunit for the comparison
* @param {String} HTML to make the table
* @param {String[][]} expected rows/cols to compare against at end
* @param {function($table)} callback something to do with the table before we compare
*/
function tableTestHTML( msg, html, expected, callback ) {
QUnit.test( msg, 1, function ( assert ) {
var extracted,
$table = $( html );
// Give caller a chance to set up sorting and manipulate the table.
if ( callback ) {
callback( $table );
} else {
$table.tablesorter();
$table.find( '#sortme' ).click();
}
// Table sorting is done synchronously; if it ever needs to change back
// to asynchronous, we'll need a timeout or a callback here.
extracted = tableExtract( $table );
assert.deepEqual( extracted, expected, msg );
} );
}
function reversed( arr ) {
// Clone array
var arr2 = arr.slice( 0 );
arr2.reverse();
return arr2;
}
// Sample data set using planets named and their radius
tableTest(
'Basic planet table: sorting initially - ascending by name',
header,
planets,
planetsAscName,
function ( $table ) {
$table.tablesorter( { sortList: [
{ 0: 'asc' }
] } );
}
);
tableTest(
'Basic planet table: sorting initially - descending by radius',
header,
planets,
reversed( planetsAscRadius ),
function ( $table ) {
$table.tablesorter( { sortList: [
{ 1: 'desc' }
] } );
}
);
tableTest(
'Basic planet table: ascending by name',
header,
planets,
planetsAscName,
function ( $table ) {
$table.tablesorter();
$table.find( '.headerSort:eq(0)' ).click();
}
);
tableTest(
'Basic planet table: ascending by name a second time',
header,
planets,
planetsAscName,
function ( $table ) {
$table.tablesorter();
$table.find( '.headerSort:eq(0)' ).click();
}
);
tableTest(
'Basic planet table: ascending by name (multiple clicks)',
header,
planets,
planetsAscName,
function ( $table ) {
$table.tablesorter();
$table.find( '.headerSort:eq(0)' ).click();
$table.find( '.headerSort:eq(1)' ).click();
$table.find( '.headerSort:eq(0)' ).click();
}
);
tableTest(
'Basic planet table: descending by name',
header,
planets,
reversed( planetsAscName ),
function ( $table ) {
$table.tablesorter();
$table.find( '.headerSort:eq(0)' ).click().click();
}
);
tableTest(
'Basic planet table: ascending radius',
header,
planets,
planetsAscRadius,
function ( $table ) {
$table.tablesorter();
$table.find( '.headerSort:eq(1)' ).click();
}
);
tableTest(
'Basic planet table: descending radius',
header,
planets,
reversed( planetsAscRadius ),
function ( $table ) {
$table.tablesorter();
$table.find( '.headerSort:eq(1)' ).click().click();
}
);
tableTest(
'Sorting multiple columns by passing sort list',
header,
simple,
simpleAsc,
function ( $table ) {
$table.tablesorter(
{ sortList: [
{ 0: 'asc' },
{ 1: 'asc' }
] }
);
}
);
tableTest(
'Sorting multiple columns by programmatically triggering sort()',
header,
simple,
simpleDescasc,
function ( $table ) {
$table.tablesorter();
$table.data( 'tablesorter' ).sort(
[
{ 0: 'desc' },
{ 1: 'asc' }
]
);
}
);
tableTest(
'Reset to initial sorting by triggering sort() without any parameters',
header,
simple,
simpleAsc,
function ( $table ) {
$table.tablesorter(
{ sortList: [
{ 0: 'asc' },
{ 1: 'asc' }
] }
);
$table.data( 'tablesorter' ).sort(
[
{ 0: 'desc' },
{ 1: 'asc' }
]
);
$table.data( 'tablesorter' ).sort();
}
);
tableTest(
'Sort via click event after having initialized the tablesorter with initial sorting',
header,
simple,
simpleDescasc,
function ( $table ) {
$table.tablesorter(
{ sortList: [ { 0: 'asc' }, { 1: 'asc' } ] }
);
$table.find( '.headerSort:eq(0)' ).click();
}
);
tableTest(
'Multi-sort via click event after having initialized the tablesorter with initial sorting',
header,
simple,
simpleAsc,
function ( $table ) {
$table.tablesorter(
{ sortList: [ { 0: 'desc' }, { 1: 'desc' } ] }
);
$table.find( '.headerSort:eq(0)' ).click();
// Pretend to click while pressing the multi-sort key
var event = $.Event( 'click' );
event[ $table.data( 'tablesorter' ).config.sortMultiSortKey ] = true;
$table.find( '.headerSort:eq(1)' ).trigger( event );
}
);
QUnit.test( 'Reset sorting making table appear unsorted', 3, function ( assert ) {
var $table = tableCreate( header, simple );
$table.tablesorter(
{ sortList: [
{ 0: 'desc' },
{ 1: 'asc' }
] }
);
$table.data( 'tablesorter' ).sort( [] );
assert.equal(
$table.find( 'th.headerSortUp' ).length + $table.find( 'th.headerSortDown' ).length,
0,
'No sort specific sort classes addign to header cells'
);
assert.equal(
$table.find( 'th' ).first().attr( 'title' ),
mw.msg( 'sort-ascending' ),
'First header cell has default title'
);
assert.equal(
$table.find( 'th' ).first().attr( 'title' ),
$table.find( 'th' ).last().attr( 'title' ),
'Both header cells\' titles match'
);
} );
// Sorting with colspans
tableTest( 'Sorting with colspanned headers: spanned column',
header4,
colspanInitial,
[ aaa1, aab5, abc3, bbc2, caa4 ],
function ( $table ) {
// Make colspanned header for test
$table.find( 'tr:eq(0) th:eq(1), tr:eq(0) th:eq(2)' ).remove();
$table.find( 'tr:eq(0) th:eq(0)' ).attr( 'colspan', '3' );
$table.tablesorter();
$table.find( '.headerSort:eq(0)' ).click();
}
);
tableTest( 'Sorting with colspanned headers: sort spanned column twice',
header4,
colspanInitial,
[ caa4, bbc2, abc3, aab5, aaa1 ],
function ( $table ) {
// Make colspanned header for test
$table.find( 'tr:eq(0) th:eq(1), tr:eq(0) th:eq(2)' ).remove();
$table.find( 'tr:eq(0) th:eq(0)' ).attr( 'colspan', '3' );
$table.tablesorter();
$table.find( '.headerSort:eq(0)' ).click();
$table.find( '.headerSort:eq(0)' ).click();
}
);
tableTest( 'Sorting with colspanned headers: subsequent column',
header4,
colspanInitial,
[ aaa1, bbc2, abc3, caa4, aab5 ],
function ( $table ) {
// Make colspanned header for test
$table.find( 'tr:eq(0) th:eq(1), tr:eq(0) th:eq(2)' ).remove();
$table.find( 'tr:eq(0) th:eq(0)' ).attr( 'colspan', '3' );
$table.tablesorter();
$table.find( '.headerSort:eq(1)' ).click();
}
);
tableTest( 'Sorting with colspanned headers: sort subsequent column twice',
header4,
colspanInitial,
[ aab5, caa4, abc3, bbc2, aaa1 ],
function ( $table ) {
// Make colspanned header for test
$table.find( 'tr:eq(0) th:eq(1), tr:eq(0) th:eq(2)' ).remove();
$table.find( 'tr:eq(0) th:eq(0)' ).attr( 'colspan', '3' );
$table.tablesorter();
$table.find( '.headerSort:eq(1)' ).click();
$table.find( '.headerSort:eq(1)' ).click();
}
);
QUnit.test( 'Basic planet table: one unsortable column', 3, function ( assert ) {
var $table = tableCreate( header, planets ),
$cell;
$table.find( 'tr:eq(0) > th:eq(0)' ).addClass( 'unsortable' );
$table.tablesorter();
$table.find( 'tr:eq(0) > th:eq(0)' ).click();
assert.deepEqual(
tableExtract( $table ),
planets,
'table not sorted'
);
$cell = $table.find( 'tr:eq(0) > th:eq(0)' );
$table.find( 'tr:eq(0) > th:eq(1)' ).click();
assert.equal(
$cell.hasClass( 'headerSortUp' ) || $cell.hasClass( 'headerSortDown' ),
false,
'after sort: no class headerSortUp or headerSortDown'
);
assert.equal(
$cell.attr( 'title' ),
undefined,
'after sort: no title tag added'
);
} );
// Regression tests!
tableTest(
'Bug 28775: German-style (dmy) short numeric dates',
[ 'Date' ],
[
// German-style dates are day-month-year
[ '11.11.2011' ],
[ '01.11.2011' ],
[ '02.10.2011' ],
[ '03.08.2011' ],
[ '09.11.2011' ]
],
[
// Sorted by ascending date
[ '03.08.2011' ],
[ '02.10.2011' ],
[ '01.11.2011' ],
[ '09.11.2011' ],
[ '11.11.2011' ]
],
function ( $table ) {
mw.config.set( 'wgDefaultDateFormat', 'dmy' );
mw.config.set( 'wgPageContentLanguage', 'de' );
$table.tablesorter();
$table.find( '.headerSort:eq(0)' ).click();
}
);
tableTest(
'Bug 28775: American-style (mdy) short numeric dates',
[ 'Date' ],
[
// American-style dates are month-day-year
[ '11.11.2011' ],
[ '01.11.2011' ],
[ '02.10.2011' ],
[ '03.08.2011' ],
[ '09.11.2011' ]
],
[
// Sorted by ascending date
[ '01.11.2011' ],
[ '02.10.2011' ],
[ '03.08.2011' ],
[ '09.11.2011' ],
[ '11.11.2011' ]
],
function ( $table ) {
mw.config.set( 'wgDefaultDateFormat', 'mdy' );
$table.tablesorter();
$table.find( '.headerSort:eq(0)' ).click();
}
);
tableTest(
'Bug 17141: IPv4 address sorting',
[ 'IP' ],
ipv4,
ipv4Sorted,
function ( $table ) {
$table.tablesorter();
$table.find( '.headerSort:eq(0)' ).click();
}
);
tableTest(
'Bug 17141: IPv4 address sorting (reverse)',
[ 'IP' ],
ipv4,
reversed( ipv4Sorted ),
function ( $table ) {
$table.tablesorter();
$table.find( '.headerSort:eq(0)' ).click().click();
}
);
tableTest(
'Accented Characters with custom collation',
[ 'Name' ],
umlautWords,
umlautWordsSorted,
function ( $table ) {
mw.config.set( 'tableSorterCollation', {
ä: 'ae',
ö: 'oe',
ß: 'ss',
ü: 'ue'
} );
$table.tablesorter();
$table.find( '.headerSort:eq(0)' ).click();
}
);
QUnit.test( 'Rowspan not exploded on init', 1, function ( assert ) {
var $table = tableCreate( header, planets );
// Modify the table to have a multiple-row-spanning cell:
// - Remove 2nd cell of 4th row, and, 2nd cell or 5th row.
$table.find( 'tr:eq(3) td:eq(1), tr:eq(4) td:eq(1)' ).remove();
// - Set rowspan for 2nd cell of 3rd row to 3.
// This covers the removed cell in the 4th and 5th row.
$table.find( 'tr:eq(2) td:eq(1)' ).attr( 'rowspan', '3' );
$table.tablesorter();
assert.equal(
$table.find( 'tr:eq(2) td:eq(1)' ).prop( 'rowSpan' ),
3,
'Rowspan not exploded'
);
} );
planetsRowspan = [
[ 'Earth', '6051.8' ],
jupiter,
[ 'Mars', '6051.8' ],
mercury,
saturn,
venus
];
planetsRowspanII = [ jupiter, mercury, saturn, venus, [ 'Venus', '6371.0' ], [ 'Venus', '3390.0' ] ];
tableTest(
'Basic planet table: same value for multiple rows via rowspan',
header,
planets,
planetsRowspan,
function ( $table ) {
// Modify the table to have a multiple-row-spanning cell:
// - Remove 2nd cell of 4th row, and, 2nd cell or 5th row.
$table.find( 'tr:eq(3) td:eq(1), tr:eq(4) td:eq(1)' ).remove();
// - Set rowspan for 2nd cell of 3rd row to 3.
// This covers the removed cell in the 4th and 5th row.
$table.find( 'tr:eq(2) td:eq(1)' ).attr( 'rowspan', '3' );
$table.tablesorter();
$table.find( '.headerSort:eq(0)' ).click();
}
);
tableTest(
'Basic planet table: same value for multiple rows via rowspan (sorting initially)',
header,
planets,
planetsRowspan,
function ( $table ) {
// Modify the table to have a multiple-row-spanning cell:
// - Remove 2nd cell of 4th row, and, 2nd cell or 5th row.
$table.find( 'tr:eq(3) td:eq(1), tr:eq(4) td:eq(1)' ).remove();
// - Set rowspan for 2nd cell of 3rd row to 3.
// This covers the removed cell in the 4th and 5th row.
$table.find( 'tr:eq(2) td:eq(1)' ).attr( 'rowspan', '3' );
$table.tablesorter( { sortList: [
{ 0: 'asc' }
] } );
}
);
tableTest(
'Basic planet table: Same value for multiple rows via rowspan II',
header,
planets,
planetsRowspanII,
function ( $table ) {
// Modify the table to have a multiple-row-spanning cell:
// - Remove 1st cell of 4th row, and, 1st cell or 5th row.
$table.find( 'tr:eq(3) td:eq(0), tr:eq(4) td:eq(0)' ).remove();
// - Set rowspan for 1st cell of 3rd row to 3.
// This covers the removed cell in the 4th and 5th row.
$table.find( 'tr:eq(2) td:eq(0)' ).attr( 'rowspan', '3' );
$table.tablesorter();
$table.find( '.headerSort:eq(0)' ).click();
}
);
tableTest(
'Complex date parsing I',
[ 'date' ],
complexMDYDates,
complexMDYSorted,
function ( $table ) {
mw.config.set( 'wgDefaultDateFormat', 'mdy' );
$table.tablesorter();
$table.find( '.headerSort:eq(0)' ).click();
}
);
tableTest(
'Currency parsing I',
[ 'currency' ],
currencyUnsorted,
currencySorted,
function ( $table ) {
$table.tablesorter();
$table.find( '.headerSort:eq(0)' ).click();
}
);
planetsAscNameLegacy = planetsAscName.slice( 0 );
planetsAscNameLegacy[ 4 ] = planetsAscNameLegacy[ 5 ];
planetsAscNameLegacy.pop();
tableTest(
'Legacy compat with .sortbottom',
header,
planets,
planetsAscNameLegacy,
function ( $table ) {
$table.find( 'tr:last' ).addClass( 'sortbottom' );
$table.tablesorter();
$table.find( '.headerSort:eq(0)' ).click();
}
);
QUnit.test( 'Test detection routine', 1, function ( assert ) {
var $table;
$table = $(
'' +
'CAPTION' +
'THEAD | ' +
'1 | ' +
'text | ' +
' '
);
$table.tablesorter();
$table.find( '.headerSort:eq(0)' ).click();
assert.equal(
$table.data( 'tablesorter' ).config.parsers[ 0 ].id,
'number',
'Correctly detected column content skipping sortbottom'
);
} );
/** FIXME: the diff output is not very readeable. */
QUnit.test( 'bug 32047 - caption must be before thead', 1, function ( assert ) {
var $table;
$table = $(
'' +
'CAPTION' +
'THEAD | ' +
'A | ' +
'B | ' +
'TFOOT | ' +
' '
);
$table.tablesorter();
assert.equal(
$table.children().get( 0 ).nodeName,
'CAPTION',
'First element after must be (bug 32047)'
);
} );
QUnit.test( 'data-sort-value attribute, when available, should override sorting position', 3, function ( assert ) {
var $table, data;
// Example 1: All cells except one cell without data-sort-value,
// which should be sorted at it's text content value.
$table = $(
'Data | ' +
'' +
'Cheetah | ' +
'Bird | ' +
'Ferret | ' +
'Elephant | ' +
'Dolphin | ' +
' '
);
$table.tablesorter().find( '.headerSort:eq(0)' ).click();
data = [];
$table.find( 'tbody > tr' ).each( function ( i, tr ) {
$( tr ).find( 'td' ).each( function ( i, td ) {
data.push( {
data: $( td ).data( 'sortValue' ),
text: $( td ).text()
} );
} );
} );
assert.deepEqual( data, [
{
data: 'Apple',
text: 'Bird'
},
{
data: 'Bananna',
text: 'Ferret'
},
{
data: undefined,
text: 'Cheetah'
},
{
data: 'Cherry',
text: 'Dolphin'
},
{
data: 'Drupe',
text: 'Elephant'
}
], 'Order matches expected order (based on data-sort-value attribute values)' );
// Example 2
$table = $(
'Data | ' +
'' +
'D | ' +
'A | ' +
'B | ' +
'G | ' +
'C | ' +
' '
);
$table.tablesorter().find( '.headerSort:eq(0)' ).click();
data = [];
$table.find( 'tbody > tr' ).each( function ( i, tr ) {
$( tr ).find( 'td' ).each( function ( i, td ) {
data.push( {
data: $( td ).data( 'sortValue' ),
text: $( td ).text()
} );
} );
} );
assert.deepEqual( data, [
{
data: undefined,
text: 'B'
},
{
data: undefined,
text: 'D'
},
{
data: 'E',
text: 'A'
},
{
data: 'F',
text: 'C'
},
{
data: undefined,
text: 'G'
}
], 'Order matches expected order (based on data-sort-value attribute values)' );
// Example 3: Test that live changes are used from data-sort-value,
// even if they change after the tablesorter is constructed (bug 38152).
$table = $(
'Data | ' +
'' +
'D | ' +
'A | ' +
'B | ' +
'G | ' +
'C | ' +
' '
);
// initialize table sorter and sort once
$table
.tablesorter()
.find( '.headerSort:eq(0)' ).click();
// Change the sortValue data properties (bug 38152)
// - change data
$table.find( 'td:contains(A)' ).data( 'sortValue', 3 );
// - add data
$table.find( 'td:contains(B)' ).data( 'sortValue', 1 );
// - remove data, bring back attribute: 2
$table.find( 'td:contains(G)' ).removeData( 'sortValue' );
// Now sort again (twice, so it is back at Ascending)
$table.find( '.headerSort:eq(0)' ).click();
$table.find( '.headerSort:eq(0)' ).click();
data = [];
$table.find( 'tbody > tr' ).each( function ( i, tr ) {
$( tr ).find( 'td' ).each( function ( i, td ) {
data.push( {
data: $( td ).data( 'sortValue' ),
text: $( td ).text()
} );
} );
} );
assert.deepEqual( data, [
{
data: 1,
text: 'B'
},
{
data: 2,
text: 'G'
},
{
data: 3,
text: 'A'
},
{
data: undefined,
text: 'C'
},
{
data: undefined,
text: 'D'
}
], 'Order matches expected order, using the current sortValue in $.data()' );
} );
tableTest( 'bug 8115: sort numbers with commas (ascending)',
[ 'Numbers' ], numbers, numbersAsc,
function ( $table ) {
$table.tablesorter();
$table.find( '.headerSort:eq(0)' ).click();
}
);
tableTest( 'bug 8115: sort numbers with commas (descending)',
[ 'Numbers' ], numbers, reversed( numbersAsc ),
function ( $table ) {
$table.tablesorter();
$table.find( '.headerSort:eq(0)' ).click().click();
}
);
// TODO add numbers sorting tests for bug 8115 with a different language
QUnit.test( 'bug 32888 - Tables inside a tableheader cell', 2, function ( assert ) {
var $table;
$table = $(
'' +
'header | ' +
'A | ' +
'B | ' +
' '
);
$table.tablesorter();
assert.equal(
$table.find( '> thead:eq(0) > tr > th.headerSort' ).length,
1,
'Child tables inside a headercell should not interfere with sortable headers (bug 32888)'
);
assert.equal(
$( '#mw-bug-32888-2' ).find( 'th.headerSort' ).length,
0,
'The headers of child tables inside a headercell should not be sortable themselves (bug 32888)'
);
} );
tableTest(
'Correct date sorting I',
[ 'date' ],
correctDateSorting1,
correctDateSortingSorted1,
function ( $table ) {
mw.config.set( 'wgDefaultDateFormat', 'mdy' );
$table.tablesorter();
$table.find( '.headerSort:eq(0)' ).click();
}
);
tableTest(
'Correct date sorting II',
[ 'date' ],
correctDateSorting2,
correctDateSortingSorted2,
function ( $table ) {
mw.config.set( 'wgDefaultDateFormat', 'dmy' );
$table.tablesorter();
$table.find( '.headerSort:eq(0)' ).click();
}
);
QUnit.test( 'Sorting images using alt text', 1, function ( assert ) {
var $table = $(
'' +
'THEAD | ' +
' | ' +
'1 | ' +
' '
);
$table.tablesorter().find( '.headerSort:eq(0)' ).click();
assert.equal(
$table.find( 'td' ).first().text(),
'1',
'Applied correct sorting order'
);
} );
QUnit.test( 'Sorting images using alt text (complex)', 1, function ( assert ) {
var $table = $(
'' +
'THEAD | ' +
'A | ' +
'CC | ' +
'F | ' +
'E | ' +
'D | ' +
'C | ' +
' '
);
$table.tablesorter().find( '.headerSort:eq(0)' ).click();
assert.equal(
$table.find( 'td' ).text(),
'CDEFCCA',
'Applied correct sorting order'
);
} );
QUnit.test( 'Sorting images using alt text (with format autodetection)', 1, function ( assert ) {
var $table = $(
'' +
'THEAD | ' +
'7 | ' +
'1 | ' +
'5 | ' +
'4 | ' +
' '
);
$table.tablesorter().find( '.headerSort:eq(0)' ).click();
assert.equal(
$table.find( 'td' ).text(),
'4517',
'Applied correct sorting order'
);
} );
QUnit.test( 'bug 38911 - The row with the largest amount of columns should receive the sort indicators', 3, function ( assert ) {
var $table = $(
'' +
'' +
'A1 | B2a | ' +
'B2b | C2b | ' +
'' +
'A | Aa | Ab | ' +
'B | Ba | Bb | ' +
' '
);
$table.tablesorter();
assert.equal(
$table.find( '#A1' ).attr( 'class' ),
'headerSort',
'The first column of the first row should be sortable'
);
assert.equal(
$table.find( '#B2b' ).attr( 'class' ),
'headerSort',
'The th element of the 2nd row of the 2nd column should be sortable'
);
assert.equal(
$table.find( '#C2b' ).attr( 'class' ),
'headerSort',
'The th element of the 2nd row of the 3rd column should be sortable'
);
} );
QUnit.test( 'rowspans in table headers should prefer the last row when rows are equal in length', 2, function ( assert ) {
var $table = $(
'' +
'' +
'A1 | B2a | ' +
'B2b | ' +
'' +
'A | Aa | ' +
'B | Ba | ' +
' '
);
$table.tablesorter();
assert.equal(
$table.find( '#A1' ).attr( 'class' ),
'headerSort',
'The first column of the first row should be sortable'
);
assert.equal(
$table.find( '#B2b' ).attr( 'class' ),
'headerSort',
'The th element of the 2nd row of the 2nd column should be sortable'
);
} );
QUnit.test( 'holes in the table headers should not throw JS errors', 2, function ( assert ) {
var $table = $(
'' +
'' +
'A1 | B1 | C1 | ' +
'A2 | ' +
'' +
'A | Aa | Aaa | ' +
'B | Ba | Bbb | ' +
' '
);
$table.tablesorter();
assert.equal( $table.find( '#A2' ).data( 'headerIndex' ),
undefined,
'A2 should not be a sort header'
);
assert.equal( $table.find( '#C1' ).data( 'headerIndex' ),
2,
'C1 should be a sort header'
);
} );
// bug 53527
QUnit.test( 'td cells in thead should not be taken into account for longest row calculation', 2, function ( assert ) {
var $table = $(
'' +
'' +
'A1 | B1 | C1 | ' +
'A2 | B2 | C2 | ' +
'' +
' '
);
$table.tablesorter();
assert.equal( $table.find( '#C2' ).data( 'headerIndex' ),
2,
'C2 should be a sort header'
);
assert.equal( $table.find( '#C1' ).data( 'headerIndex' ),
undefined,
'C1 should not be a sort header'
);
} );
// bug 41889 - exploding rowspans in more complex cases
tableTestHTML(
'Rowspan exploding with row headers',
'' +
'n | foo | bar | baz | ' +
'' +
'1 | foo | bar | baz | ' +
'2 | baz | ' +
' ',
[
[ '1', 'foo', 'bar', 'baz' ],
[ '2', 'foo', 'bar', 'baz' ]
]
);
// bug 53211 - exploding rowspans in more complex cases
QUnit.test(
'Rowspan exploding with row headers and colspans', 1, function ( assert ) {
var $table = $( '' +
'n | foo | baz | ' +
'foo | bar | ' +
'' +
'1 | foo | bar | baz | ' +
'2 | foo | bar | baz | ' +
' ' );
$table.tablesorter();
assert.equal( $table.find( 'tr:eq(1) th:eq(1)' ).data( 'headerIndex' ),
2,
'Incorrect index of sort header'
);
}
);
tableTestHTML(
'Rowspan exploding with colspanned cells',
'' +
'n | foo | bar | baz | ' +
'' +
'1 | foo | bar | baz | ' +
'2 | foobar | ' +
' ',
[
[ '1', 'foo', 'bar', 'baz' ],
[ '2', 'foobar', 'baz' ]
]
);
tableTestHTML(
'Rowspan exploding with colspanned cells (2)',
'' +
'n | foo | bar | baz | n2 | ' +
'' +
'1 | foo | bar | baz | 2 | ' +
'2 | foobar | 1 | ' +
' ',
[
[ '2', 'foobar', 'baz', '1' ],
[ '1', 'foo', 'bar', 'baz', '2' ]
]
);
tableTestHTML(
'Rowspan exploding with rightmost rows spanning most',
'' +
'n | foo | bar | ' +
'' +
'1 | foo | bar | ' +
'2 | ' +
'3 | foo | ' +
'4 | ' +
' ',
[
[ '1', 'foo', 'bar' ],
[ '2', 'foo', 'bar' ],
[ '3', 'foo', 'bar' ],
[ '4', 'foo', 'bar' ]
]
);
tableTestHTML(
'Rowspan exploding with rightmost rows spanning most (2)',
'' +
'n | foo | bar | baz | ' +
'' +
'1 | foo | bar | baz | ' +
'2 | baz | ' +
'3 | foo | baz | ' +
'4 | baz | ' +
' ',
[
[ '1', 'foo', 'bar', 'baz' ],
[ '2', 'foo', 'bar', 'baz' ],
[ '3', 'foo', 'bar', 'baz' ],
[ '4', 'foo', 'bar', 'baz' ]
]
);
tableTestHTML(
'Rowspan exploding with row-and-colspanned cells',
'' +
'n | foo1 | foo2 | bar | baz | ' +
'' +
'1 | foo1 | foo2 | bar | baz | ' +
'2 | baz | ' +
'3 | foo | baz | ' +
'4 | baz | ' +
' ',
[
[ '1', 'foo1', 'foo2', 'bar', 'baz' ],
[ '2', 'foo1', 'foo2', 'bar', 'baz' ],
[ '3', 'foo', 'bar', 'baz' ],
[ '4', 'foo', 'bar', 'baz' ]
]
);
tableTestHTML(
'Rowspan exploding with uneven rowspan layout',
'' +
'n | foo1 | foo2 | foo3 | bar | baz | ' +
'' +
'1 | foo1 | foo2 | foo3 | bar | baz | ' +
'2 | bar | baz | ' +
'3 | foo1 | foo2 | foo3 | baz | ' +
'4 | baz | ' +
' ',
[
[ '1', 'foo1', 'foo2', 'foo3', 'bar', 'baz' ],
[ '2', 'foo1', 'foo2', 'foo3', 'bar', 'baz' ],
[ '3', 'foo1', 'foo2', 'foo3', 'bar', 'baz' ],
[ '4', 'foo1', 'foo2', 'foo3', 'bar', 'baz' ]
]
);
QUnit.test( 'bug 105731 - incomplete rows in table body', 3, function ( assert ) {
var $table, parsers;
$table = $(
'' +
'A | B | ' +
'3 | ' +
'1 | 2 | ' +
' '
);
$table.tablesorter();
$table.find( '.headerSort:eq(0)' ).click();
// now the first row have 2 columns
$table.find( '.headerSort:eq(1)' ).click();
parsers = $table.data( 'tablesorter' ).config.parsers;
assert.equal(
parsers.length,
2,
'detectParserForColumn() detect 2 parsers'
);
assert.equal(
parsers[ 1 ].id,
'number',
'detectParserForColumn() detect parser.id "number" for second column'
);
assert.equal(
parsers[ 1 ].format( $table.find( 'tbody > tr > td:eq(1)' ).text() ),
0,
'empty cell is sorted as number 0'
);
} );
}( jQuery, mediaWiki ) );
|