4 wgMonthNames
: ['', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
5 wgMonthNamesShort
: ['', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
6 wgDefaultDateFormat
: 'dmy',
7 wgContentLanguage
: 'en'
10 QUnit
.module( 'jquery.tablesorter', QUnit
.newMwEnvironment({ config
: config
}) );
13 * Create an HTML table from an array of row arrays containing text strings.
14 * First row will be header row. No fancy rowspan/colspan stuff.
16 * @param {String[]} header
17 * @param {String[][]} data
20 function tableCreate( header
, data
) {
22 $table
= $( '<table class="sortable"><thead></thead><tbody></tbody></table>' ),
23 $thead
= $table
.find( 'thead' ),
24 $tbody
= $table
.find( 'tbody' ),
27 $.each( header
, function ( i
, str
) {
28 var $th
= $( '<th>' );
29 $th
.text( str
).appendTo( $tr
);
31 $tr
.appendTo( $thead
);
33 for ( i
= 0; i
< data
.length
; i
++ ) {
35 $.each( data
[i
], function ( j
, str
) {
36 var $td
= $( '<td>' );
37 $td
.text( str
).appendTo( $tr
);
39 $tr
.appendTo( $tbody
);
45 * Extract text from table.
47 * @param {jQuery} $table
50 function tableExtract( $table
) {
53 $table
.find( 'tbody' ).find( 'tr' ).each( function( i
, tr
) {
55 $( tr
).find( 'td,th' ).each( function( i
, td
) {
56 row
.push( $( td
).text() );
64 * Run a table test by building a table with the given data,
65 * running some callback on it, then checking the results.
67 * @param {String} msg text to pass on to qunit for the comparison
68 * @param {String[]} header cols to make the table
69 * @param {String[][]} data rows/cols to make the table
70 * @param {String[][]} expected rows/cols to compare against at end
71 * @param {function($table)} callback something to do with the table before we compare
73 function tableTest( msg
, header
, data
, expected
, callback
) {
74 QUnit
.test( msg
, 1, function ( assert
) {
75 var $table
= tableCreate( header
, data
);
77 // Give caller a chance to set up sorting and manipulate the table.
80 // Table sorting is done synchronously; if it ever needs to change back
81 // to asynchronous, we'll need a timeout or a callback here.
82 var extracted
= tableExtract( $table
);
83 assert
.deepEqual( extracted
, expected
, msg
);
87 function reversed(arr
) {
89 var arr2
= arr
.slice(0);
96 // Sample data set using planets named and their radius
97 var header
= [ 'Planet' , 'Radius (km)'],
98 mercury
= [ 'Mercury', '2439.7' ],
99 venus
= [ 'Venus' , '6051.8' ],
100 earth
= [ 'Earth' , '6371.0' ],
101 mars
= [ 'Mars' , '3390.0' ],
102 jupiter
= [ 'Jupiter', '69911' ],
103 saturn
= [ 'Saturn' , '58232' ];
106 var planets
= [mercury
, venus
, earth
, mars
, jupiter
, saturn
];
107 var ascendingName
= [earth
, jupiter
, mars
, mercury
, saturn
, venus
];
108 var ascendingRadius
= [mercury
, mars
, venus
, earth
, saturn
, jupiter
];
111 'Basic planet table: ascending by name',
115 function ( $table
) {
116 $table
.tablesorter();
117 $table
.find( '.headerSort:eq(0)' ).click();
121 'Basic planet table: ascending by name a second time',
125 function ( $table
) {
126 $table
.tablesorter();
127 $table
.find( '.headerSort:eq(0)' ).click();
131 'Basic planet table: descending by name',
134 reversed(ascendingName
),
135 function ( $table
) {
136 $table
.tablesorter();
137 $table
.find( '.headerSort:eq(0)' ).click().click();
141 'Basic planet table: ascending radius',
145 function ( $table
) {
146 $table
.tablesorter();
147 $table
.find( '.headerSort:eq(1)' ).click();
151 'Basic planet table: descending radius',
154 reversed(ascendingRadius
),
155 function ( $table
) {
156 $table
.tablesorter();
157 $table
.find( '.headerSort:eq(1)' ).click().click();
164 'Bug 28775: German-style (dmy) short numeric dates',
166 [ // German-style dates are day-month-year
173 [ // Sorted by ascending date
180 function ( $table
) {
181 mw
.config
.set( 'wgDefaultDateFormat', 'dmy' );
182 mw
.config
.set( 'wgContentLanguage', 'de' );
184 $table
.tablesorter();
185 $table
.find( '.headerSort:eq(0)' ).click();
190 'Bug 28775: American-style (mdy) short numeric dates',
192 [ // American-style dates are month-day-year
199 [ // Sorted by ascending date
206 function ( $table
) {
207 mw
.config
.set( 'wgDefaultDateFormat', 'mdy' );
209 $table
.tablesorter();
210 $table
.find( '.headerSort:eq(0)' ).click();
215 // Some randomly generated fake IPs
226 // Sort order should go octet by octet
238 'Bug 17141: IPv4 address sorting',
242 function ( $table
) {
243 $table
.tablesorter();
244 $table
.find( '.headerSort:eq(0)' ).click();
248 'Bug 17141: IPv4 address sorting (reverse)',
251 reversed(ipv4Sorted
),
252 function ( $table
) {
253 $table
.tablesorter();
254 $table
.find( '.headerSort:eq(0)' ).click().click();
259 // Some words with Umlauts
270 var umlautWordsSorted
= [
271 // Some words with Umlauts
283 'Accented Characters with custom collation',
287 function ( $table
) {
288 mw
.config
.set( 'tableSorterCollation', {
295 $table
.tablesorter();
296 $table
.find( '.headerSort:eq(0)' ).click();
300 var planetsRowspan
= [["Earth","6051.8"], jupiter
, ["Mars","6051.8"], mercury
, saturn
, venus
];
301 var planetsRowspanII
= [jupiter
, mercury
, saturn
, venus
, ['Venus', '6371.0'], ['Venus', '3390.0']];
304 'Basic planet table: same value for multiple rows via rowspan',
308 function ( $table
) {
309 // Modify the table to have a multiuple-row-spanning cell:
310 // - Remove 2nd cell of 4th row, and, 2nd cell or 5th row.
311 $table
.find( 'tr:eq(3) td:eq(1), tr:eq(4) td:eq(1)' ).remove();
312 // - Set rowspan for 2nd cell of 3rd row to 3.
313 // This covers the removed cell in the 4th and 5th row.
314 $table
.find( 'tr:eq(2) td:eq(1)' ).prop( 'rowspan', '3' );
316 $table
.tablesorter();
317 $table
.find( '.headerSort:eq(0)' ).click();
321 'Basic planet table: Same value for multiple rows via rowspan II',
325 function ( $table
) {
326 // Modify the table to have a multiuple-row-spanning cell:
327 // - Remove 1st cell of 4th row, and, 1st cell or 5th row.
328 $table
.find( 'tr:eq(3) td:eq(0), tr:eq(4) td:eq(0)' ).remove();
329 // - Set rowspan for 1st cell of 3rd row to 3.
330 // This covers the removed cell in the 4th and 5th row.
331 $table
.find( 'tr:eq(2) td:eq(0)' ).prop( 'rowspan', '3' );
333 $table
.tablesorter();
334 $table
.find( '.headerSort:eq(0)' ).click();
338 var complexMDYDates
= [
339 // Some words with Umlauts
340 ['January, 19 2010'],
347 var complexMDYSorted
= [
351 ['January, 19 2010'],
356 'Complex date parsing I',
360 function ( $table
) {
361 mw
.config
.set( 'wgDefaultDateFormat', 'mdy' );
363 $table
.tablesorter();
364 $table
.find( '.headerSort:eq(0)' ).click();
368 var currencyUnsorted
= [
378 var currencySorted
= [
385 // Comma's sort after dots
386 // Not intentional but test to detect changes
391 'Currency parsing I',
395 function ( $table
) {
396 $table
.tablesorter();
397 $table
.find( '.headerSort:eq(0)' ).click();
401 var ascendingNameLegacy
= ascendingName
.slice(0);
402 ascendingNameLegacy
[4] = ascendingNameLegacy
[5];
403 ascendingNameLegacy
.pop();
406 'Legacy compat with .sortbottom',
411 $table
.find( 'tr:last' ).addClass( 'sortbottom' );
412 $table
.tablesorter();
413 $table
.find( '.headerSort:eq(0)' ).click();
418 /** FIXME: the diff output is not very readeable. */
419 QUnit
.test( 'bug 32047 - caption must be before thead', function ( assert
) {
422 '<table class="sortable">' +
423 '<caption>CAPTION</caption>' +
424 '<tr><th>THEAD</th></tr>' +
425 '<tr><td>A</td></tr>' +
426 '<tr><td>B</td></tr>' +
427 '<tr class="sortbottom"><td>TFOOT</td></tr>' +
430 $table
.tablesorter();
433 $table
.children( ).get( 0 ).nodeName
,
435 'First element after <thead> must be <caption> (bug 32047)'
439 QUnit
.test( 'data-sort-value attribute, when available, should override sorting position', function ( assert
) {
442 // Example 1: All cells except one cell without data-sort-value,
443 // which should be sorted at it's text content value.
445 '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
447 '<tr><td>Cheetah</td></tr>' +
448 '<tr><td data-sort-value="Apple">Bird</td></tr>' +
449 '<tr><td data-sort-value="Bananna">Ferret</td></tr>' +
450 '<tr><td data-sort-value="Drupe">Elephant</td></tr>' +
451 '<tr><td data-sort-value="Cherry">Dolphin</td></tr>' +
454 $table
.tablesorter().find( '.headerSort:eq(0)' ).click();
457 $table
.find( 'tbody > tr' ).each( function( i
, tr
) {
458 $( tr
).find( 'td' ).each( function( i
, td
) {
460 data
: $( td
).data( 'sortValue' ),
466 assert
.deepEqual( data
, [
483 ], 'Order matches expected order (based on data-sort-value attribute values)' );
487 '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
489 '<tr><td>D</td></tr>' +
490 '<tr><td data-sort-value="E">A</td></tr>' +
491 '<tr><td>B</td></tr>' +
492 '<tr><td>G</td></tr>' +
493 '<tr><td data-sort-value="F">C</td></tr>' +
496 $table
.tablesorter().find( '.headerSort:eq(0)' ).click();
499 $table
.find( 'tbody > tr' ).each( function ( i
, tr
) {
500 $( tr
).find( 'td' ).each( function ( i
, td
) {
502 data
: $( td
).data( 'sortValue' ),
508 assert
.deepEqual( data
, [
525 ], 'Order matches expected order (based on data-sort-value attribute values)' );
527 // Example 3: Test that live changes are used from data-sort-value,
528 // even if they change after the tablesorter is constructed (bug 38152).
530 '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
532 '<tr><td>D</td></tr>' +
533 '<tr><td data-sort-value="1">A</td></tr>' +
534 '<tr><td>B</td></tr>' +
535 '<tr><td data-sort-value="2">G</td></tr>' +
536 '<tr><td>C</td></tr>' +
539 // initialize table sorter and sort once
542 .find( '.headerSort:eq(0)' ).click();
544 // Change the sortValue data properties (bug 38152)
546 $table
.find( 'td:contains(A)' ).data( 'sortValue', 3 );
548 $table
.find( 'td:contains(B)' ).data( 'sortValue', 1 );
549 // - remove data, bring back attribute: 2
550 $table
.find( 'td:contains(G)' ).removeData( 'sortValue' );
552 // Now sort again (twice, so it is back at Ascending)
553 $table
.find( '.headerSort:eq(0)' ).click();
554 $table
.find( '.headerSort:eq(0)' ).click();
557 $table
.find( 'tbody > tr' ).each( function( i
, tr
) {
558 $( tr
).find( 'td' ).each( function( i
, td
) {
560 data
: $( td
).data( 'sortValue' ),
566 assert
.deepEqual( data
, [
583 ], 'Order matches expected order, using the current sortValue in $.data()' );
604 tableTest( 'bug 8115: sort numbers with commas (ascending)',
605 ['Numbers'], numbers
, numbersAsc
,
607 $table
.tablesorter();
608 $table
.find( '.headerSort:eq(0)' ).click();
612 tableTest( 'bug 8115: sort numbers with commas (descending)',
613 ['Numbers'], numbers
, reversed(numbersAsc
),
615 $table
.tablesorter();
616 $table
.find( '.headerSort:eq(0)' ).click().click();
619 // TODO add numbers sorting tests for bug 8115 with a different language
621 QUnit
.test( 'bug 32888 - Tables inside a tableheader cell', 2, function ( assert
) {
624 '<table class="sortable" id="mw-bug-32888">' +
625 '<tr><th>header<table id="mw-bug-32888-2">'+
626 '<tr><th>1</th><th>2</th></tr>' +
627 '</table></th></tr>' +
628 '<tr><td>A</td></tr>' +
629 '<tr><td>B</td></tr>' +
632 $table
.tablesorter();
635 $table
.find('> thead:eq(0) > tr > th.headerSort').length
,
637 'Child tables inside a headercell should not interfere with sortable headers (bug 32888)'
640 $( '#mw-bug-32888-2' ).find('th.headerSort').length
,
642 'The headers of child tables inside a headercell should not be sortable themselves (bug 32888)'
647 var correctDateSorting1
= [
649 ['05 February 2010'],
653 var correctDateSortingSorted1
= [
660 'Correct date sorting I',
663 correctDateSortingSorted1
,
664 function ( $table
) {
665 mw
.config
.set( 'wgDefaultDateFormat', 'mdy' );
667 $table
.tablesorter();
668 $table
.find( '.headerSort:eq(0)' ).click();
672 var correctDateSorting2
= [
674 ['February 05 2010'],
678 var correctDateSortingSorted2
= [
685 'Correct date sorting II',
688 correctDateSortingSorted2
,
689 function ( $table
) {
690 mw
.config
.set( 'wgDefaultDateFormat', 'dmy' );
692 $table
.tablesorter();
693 $table
.find( '.headerSort:eq(0)' ).click();
697 }( jQuery
, mediaWiki
) );