2 var header
= [ 'Planet', 'Radius (km)' ],
5 mercury
= [ 'Mercury', '2439.7' ],
6 venus
= [ 'Venus', '6051.8' ],
7 earth
= [ 'Earth', '6371.0' ],
8 mars
= [ 'Mars', '3390.0' ],
9 jupiter
= [ 'Jupiter', '69911' ],
10 saturn
= [ 'Saturn', '58232' ],
11 planets
= [ mercury
, venus
, earth
, mars
, jupiter
, saturn
],
12 planetsAscName
= [ earth
, jupiter
, mars
, mercury
, saturn
, venus
],
13 planetsAscRadius
= [ mercury
, mars
, venus
, earth
, saturn
, jupiter
],
25 simple
= [ a2
, b3
, a1
, a3
, b2
, b1
],
26 simpleAsc
= [ a1
, a2
, a3
, b1
, b2
, b3
],
27 simpleDescasc
= [ b1
, b2
, b3
, a1
, a2
, a3
],
30 header4
= [ 'column1a', 'column1b', 'column1c', 'column2' ],
31 aaa1
= [ 'A', 'A', 'A', '1' ],
32 aab5
= [ 'A', 'A', 'B', '5' ],
33 abc3
= [ 'A', 'B', 'C', '3' ],
34 bbc2
= [ 'B', 'B', 'C', '2' ],
35 caa4
= [ 'C', 'A', 'A', '4' ],
36 colspanInitial
= [ aab5
, aaa1
, abc3
, bbc2
, caa4
],
40 // Some randomly generated fake IPs
44 [ '204.204.132.158' ],
51 // Sort order should go octet by octet
58 [ '204.204.132.158' ],
77 umlautWordsSortedEn
= [
91 umlautWordsSortedSv
= [
102 [ 'ä' ], // ä sorts after z in Swedish
106 // Data set "digraph"
115 digraphWordsSorted
= [
125 [ 'January, 19 2010' ],
129 [ 'December 12 \'10' ]
135 [ 'January, 19 2010' ],
136 [ 'December 12 \'10' ]
155 // Commas sort after dots
156 // Not intentional but test to detect changes
177 correctDateSorting1
= [
178 [ '01 January 2010' ],
179 [ '05 February 2010' ],
180 [ '16 January 2010' ]
182 correctDateSortingSorted1
= [
183 [ '01 January 2010' ],
184 [ '16 January 2010' ],
185 [ '05 February 2010' ]
188 correctDateSorting2
= [
189 [ 'January 01 2010' ],
190 [ 'February 05 2010' ],
191 [ 'January 16 2010' ]
193 correctDateSortingSorted2
= [
194 [ 'January 01 2010' ],
195 [ 'January 16 2010' ],
196 [ 'February 05 2010' ]
200 [ '2009-12-25T12:30:45.001Z' ],
203 [ '2009-12-25T12:30:45' ],
204 [ '2009-12-25T12:30:45.111' ],
205 [ '2009-12-25T12:30:45+01:00' ]
207 isoDateSortingSorted
= [
209 [ '2009-12-25T12:30:45+01:00' ],
210 [ '2009-12-25T12:30:45' ],
211 [ '2009-12-25T12:30:45.001Z' ],
212 [ '2009-12-25T12:30:45.111' ],
217 QUnit
.module( 'jquery.tablesorter', QUnit
.newMwEnvironment( {
219 this.liveMonths
= mw
.language
.months
;
220 mw
.language
.months
= {
222 names
: [ 'january', 'february', 'march', 'april', 'may_long', 'june',
223 'july', 'august', 'september', 'october', 'november', 'december' ],
224 genitive
: [ 'january-gen', 'february-gen', 'march-gen', 'april-gen', 'may-gen', 'june-gen',
225 'july-gen', 'august-gen', 'september-gen', 'october-gen', 'november-gen', 'december-gen' ],
226 abbrev
: [ 'jan', 'feb', 'mar', 'apr', 'may', 'jun',
227 'jul', 'aug', 'sep', 'oct', 'nov', 'dec' ]
229 names
: [ 'January', 'February', 'March', 'April', 'May', 'June',
230 'July', 'August', 'September', 'October', 'November', 'December' ],
231 genitive
: [ 'January', 'February', 'March', 'April', 'May', 'June',
232 'July', 'August', 'September', 'October', 'November', 'December' ],
233 abbrev
: [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
234 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ]
237 teardown: function () {
238 mw
.language
.months
= this.liveMonths
;
241 wgDefaultDateFormat
: 'dmy',
242 wgSeparatorTransformTable
: [ '', '' ],
243 wgDigitTransformTable
: [ '', '' ],
244 wgPageContentLanguage
: 'en'
249 * Create an HTML table from an array of row arrays containing text strings.
250 * First row will be header row. No fancy rowspan/colspan stuff.
252 * @param {string[]} header
253 * @param {string[][]} data
256 function tableCreate( header
, data
) {
258 $table
= $( '<table class="sortable"><thead></thead><tbody></tbody></table>' ),
259 $thead
= $table
.find( 'thead' ),
260 $tbody
= $table
.find( 'tbody' ),
263 header
.forEach( function ( str
) {
264 var $th
= $( '<th>' );
265 $th
.text( str
).appendTo( $tr
);
267 $tr
.appendTo( $thead
);
269 for ( i
= 0; i
< data
.length
; i
++ ) {
271 // eslint-disable-next-line no-loop-func
272 data
[ i
].forEach( function ( str
) {
273 var $td
= $( '<td>' );
274 $td
.text( str
).appendTo( $tr
);
276 $tr
.appendTo( $tbody
);
282 * Extract text from table.
284 * @param {jQuery} $table
285 * @return {string[][]}
287 function tableExtract( $table
) {
290 $table
.find( 'tbody' ).find( 'tr' ).each( function ( i
, tr
) {
292 $( tr
).find( 'td,th' ).each( function ( i
, td
) {
293 row
.push( $( td
).text() );
301 * Run a table test by building a table with the given data,
302 * running some callback on it, then checking the results.
304 * @param {string} msg text to pass on to qunit for the comparison
305 * @param {string[]} header cols to make the table
306 * @param {string[][]} data rows/cols to make the table
307 * @param {string[][]} expected rows/cols to compare against at end
308 * @param {function($table)} callback something to do with the table before we compare
310 function tableTest( msg
, header
, data
, expected
, callback
) {
311 QUnit
.test( msg
, function ( assert
) {
313 $table
= tableCreate( header
, data
);
315 // Give caller a chance to set up sorting and manipulate the table.
318 // Table sorting is done synchronously; if it ever needs to change back
319 // to asynchronous, we'll need a timeout or a callback here.
320 extracted
= tableExtract( $table
);
321 assert
.deepEqual( extracted
, expected
, msg
);
326 * Run a table test by building a table with the given HTML,
327 * running some callback on it, then checking the results.
329 * @param {string} msg text to pass on to qunit for the comparison
330 * @param {string} html HTML to make the table
331 * @param {string[][]} expected Rows/cols to compare against at end
332 * @param {function($table)} callback Something to do with the table before we compare
334 function tableTestHTML( msg
, html
, expected
, callback
) {
335 QUnit
.test( msg
, function ( assert
) {
339 // Give caller a chance to set up sorting and manipulate the table.
343 $table
.tablesorter();
344 $table
.find( '#sortme' ).trigger( 'click' );
347 // Table sorting is done synchronously; if it ever needs to change back
348 // to asynchronous, we'll need a timeout or a callback here.
349 extracted
= tableExtract( $table
);
350 assert
.deepEqual( extracted
, expected
, msg
);
354 function reversed( arr
) {
356 var arr2
= arr
.slice( 0 );
363 // Sample data set using planets named and their radius
366 'Basic planet table: sorting initially - ascending by name',
370 function ( $table
) {
371 $table
.tablesorter( { sortList
: [
377 'Basic planet table: sorting initially - descending by radius',
380 reversed( planetsAscRadius
),
381 function ( $table
) {
382 $table
.tablesorter( { sortList
: [
388 'Basic planet table: ascending by name',
392 function ( $table
) {
393 $table
.tablesorter();
394 $table
.find( '.headerSort' ).eq( 0 ).trigger( 'click' );
398 'Basic planet table: ascending by name a second time',
402 function ( $table
) {
403 $table
.tablesorter();
404 $table
.find( '.headerSort' ).eq( 0 ).trigger( 'click' );
408 'Basic planet table: ascending by name (multiple clicks)',
412 function ( $table
) {
413 $table
.tablesorter();
414 $table
.find( '.headerSort' ).eq( 0 ).trigger( 'click' );
415 $table
.find( '.headerSort' ).eq( 1 ).trigger( 'click' );
416 $table
.find( '.headerSort' ).eq( 0 ).trigger( 'click' );
420 'Basic planet table: descending by name',
423 reversed( planetsAscName
),
424 function ( $table
) {
425 $table
.tablesorter();
426 $table
.find( '.headerSort' ).eq( 0 ).trigger( 'click' ).trigger( 'click' );
430 'Basic planet table: ascending radius',
434 function ( $table
) {
435 $table
.tablesorter();
436 $table
.find( '.headerSort' ).eq( 1 ).trigger( 'click' );
440 'Basic planet table: descending radius',
443 reversed( planetsAscRadius
),
444 function ( $table
) {
445 $table
.tablesorter();
446 $table
.find( '.headerSort' ).eq( 1 ).trigger( 'click' ).trigger( 'click' );
450 'Sorting multiple columns by passing sort list',
454 function ( $table
) {
464 'Sorting multiple columns by programmatically triggering sort()',
468 function ( $table
) {
469 $table
.tablesorter();
470 $table
.data( 'tablesorter' ).sort(
479 'Reset to initial sorting by triggering sort() without any parameters',
483 function ( $table
) {
490 $table
.data( 'tablesorter' ).sort(
496 $table
.data( 'tablesorter' ).sort();
500 'Sort via click event after having initialized the tablesorter with initial sorting',
504 function ( $table
) {
506 { sortList
: [ { 0: 'asc' }, { 1: 'asc' } ] }
508 $table
.find( '.headerSort' ).eq( 0 ).trigger( 'click' );
512 'Multi-sort via click event after having initialized the tablesorter with initial sorting',
516 function ( $table
) {
519 { sortList
: [ { 0: 'desc' }, { 1: 'desc' } ] }
521 $table
.find( '.headerSort' ).eq( 0 ).trigger( 'click' );
523 // Pretend to click while pressing the multi-sort key
524 event
= $.Event( 'click' );
525 event
[ $table
.data( 'tablesorter' ).config
.sortMultiSortKey
] = true;
526 $table
.find( '.headerSort' ).eq( 1 ).trigger( event
);
529 QUnit
.test( 'Reset sorting making table appear unsorted', function ( assert
) {
530 var $table
= tableCreate( header
, simple
);
537 $table
.data( 'tablesorter' ).sort( [] );
540 $table
.find( 'th.headerSortUp' ).length
+ $table
.find( 'th.headerSortDown' ).length
,
542 'No sort specific sort classes addign to header cells'
546 $table
.find( 'th' ).first().attr( 'title' ),
547 mw
.msg( 'sort-ascending' ),
548 'First header cell has default title'
552 $table
.find( 'th' ).first().attr( 'title' ),
553 $table
.find( 'th' ).last().attr( 'title' ),
554 'Both header cells\' titles match'
558 // Sorting with colspans
560 tableTest( 'Sorting with colspanned headers: spanned column',
563 [ aaa1
, aab5
, abc3
, bbc2
, caa4
],
564 function ( $table
) {
565 // Make colspanned header for test
566 $table
.find( 'tr th' ).eq( 1 ).remove();
567 $table
.find( 'tr th' ).eq( 1 ).remove();
568 $table
.find( 'tr th' ).eq( 0 ).attr( 'colspan', '3' );
570 $table
.tablesorter();
571 $table
.find( '.headerSort' ).eq( 0 ).trigger( 'click' );
574 tableTest( 'Sorting with colspanned headers: sort spanned column twice',
577 [ caa4
, bbc2
, abc3
, aab5
, aaa1
],
578 function ( $table
) {
579 // Make colspanned header for test
580 $table
.find( 'tr th' ).eq( 1 ).remove();
581 $table
.find( 'tr th' ).eq( 1 ).remove();
582 $table
.find( 'tr' ).eq( 0 ).find( 'th' ).eq( 0 ).attr( 'colspan', '3' );
584 $table
.tablesorter();
585 $table
.find( '.headerSort' ).eq( 0 ).trigger( 'click' );
586 $table
.find( '.headerSort' ).eq( 0 ).trigger( 'click' );
589 tableTest( 'Sorting with colspanned headers: subsequent column',
592 [ aaa1
, bbc2
, abc3
, caa4
, aab5
],
593 function ( $table
) {
594 // Make colspanned header for test
595 $table
.find( 'tr th' ).eq( 1 ).remove();
596 $table
.find( 'tr th' ).eq( 1 ).remove();
597 $table
.find( 'tr th' ).eq( 0 ).attr( 'colspan', '3' );
599 $table
.tablesorter();
600 $table
.find( '.headerSort' ).eq( 1 ).trigger( 'click' );
603 tableTest( 'Sorting with colspanned headers: sort subsequent column twice',
606 [ aab5
, caa4
, abc3
, bbc2
, aaa1
],
607 function ( $table
) {
608 // Make colspanned header for test
609 $table
.find( 'tr th' ).eq( 1 ).remove();
610 $table
.find( 'tr th' ).eq( 1 ).remove();
611 $table
.find( 'tr th' ).eq( 0 ).attr( 'colspan', '3' );
613 $table
.tablesorter();
614 $table
.find( '.headerSort' ).eq( 1 ).trigger( 'click' );
615 $table
.find( '.headerSort' ).eq( 1 ).trigger( 'click' );
619 QUnit
.test( 'Basic planet table: one unsortable column', function ( assert
) {
620 var $table
= tableCreate( header
, planets
),
622 $table
.find( 'tr > th' ).eq( 0 ).addClass( 'unsortable' );
624 $table
.tablesorter();
625 $table
.find( 'tr > th' ).eq( 0 ).trigger( 'click' );
628 tableExtract( $table
),
633 $cell
= $table
.find( 'tr > th' ).eq( 0 );
634 $table
.find( 'tr > th' ).eq( 1 ).trigger( 'click' );
637 // eslint-disable-next-line no-jquery/no-class-state
638 $cell
.hasClass( 'headerSortUp' ) || $cell
.hasClass( 'headerSortDown' ),
640 'after sort: no class headerSortUp or headerSortDown'
644 $cell
.attr( 'title' ),
646 'after sort: no title tag added'
653 'T30775: German-style (dmy) short numeric dates',
656 // German-style dates are day-month-year
664 // Sorted by ascending date
671 function ( $table
) {
672 mw
.config
.set( 'wgDefaultDateFormat', 'dmy' );
673 mw
.config
.set( 'wgPageContentLanguage', 'de' );
675 $table
.tablesorter();
676 $table
.find( '.headerSort' ).eq( 0 ).trigger( 'click' );
681 'T30775: American-style (mdy) short numeric dates',
684 // American-style dates are month-day-year
692 // Sorted by ascending date
699 function ( $table
) {
700 mw
.config
.set( 'wgDefaultDateFormat', 'mdy' );
702 $table
.tablesorter();
703 $table
.find( '.headerSort' ).eq( 0 ).trigger( 'click' );
708 'T19141: IPv4 address sorting',
712 function ( $table
) {
713 $table
.tablesorter();
714 $table
.find( '.headerSort' ).eq( 0 ).trigger( 'click' );
718 'T19141: IPv4 address sorting (reverse)',
721 reversed( ipv4Sorted
),
722 function ( $table
) {
723 $table
.tablesorter();
724 $table
.find( '.headerSort' ).eq( 0 ).trigger( 'click' ).trigger( 'click' );
729 'Accented Characters with custom collation',
733 function ( $table
) {
734 mw
.config
.set( 'tableSorterCollation', {
741 $table
.tablesorter();
742 $table
.find( '.headerSort' ).eq( 0 ).trigger( 'click' );
747 'Accented Characters Swedish locale',
751 function ( $table
) {
752 mw
.config
.set( 'wgPageContentLanguage', 'sv' );
754 $table
.tablesorter();
755 // eslint-disable-next-line no-jquery/no-sizzle
756 $table
.find( '.headerSort:eq(0)' ).trigger( 'click' );
761 'Digraphs with custom collation',
765 function ( $table
) {
766 mw
.config
.set( 'tableSorterCollation', {
771 $table
.tablesorter();
772 $table
.find( '.headerSort' ).eq( 0 ).trigger( 'click' );
776 QUnit
.test( 'Rowspan not exploded on init', function ( assert
) {
777 var $table
= tableCreate( header
, planets
);
779 // Modify the table to have a multiple-row-spanning cell:
780 // - Remove 2nd cell of 4th row, and, 2nd cell or 5th row.
781 $table
.find( 'tr' ).eq( 3 ).find( 'td' ).eq( 1 ).remove();
782 $table
.find( 'tr' ).eq( 4 ).find( 'td' ).eq( 1 ).remove();
783 // - Set rowspan for 2nd cell of 3rd row to 3.
784 // This covers the removed cell in the 4th and 5th row.
785 $table
.find( 'tr' ).eq( 2 ).find( 'td' ).eq( 1 ).attr( 'rowspan', '3' );
787 $table
.tablesorter();
790 $table
.find( 'tr' ).eq( 2 ).find( 'td' ).eq( 1 ).prop( 'rowSpan' ),
792 'Rowspan not exploded'
797 [ 'Earth', '6051.8' ],
799 [ 'Mars', '6051.8' ],
804 planetsRowspanII
= [ jupiter
, mercury
, saturn
, venus
, [ 'Venus', '6371.0' ], [ 'Venus', '3390.0' ] ];
807 'Basic planet table: same value for multiple rows via rowspan',
811 function ( $table
) {
812 // Modify the table to have a multiple-row-spanning cell:
813 // - Remove 2nd cell of 4th row, and, 2nd cell or 5th row.
814 $table
.find( 'tr' ).eq( 3 ).find( 'td' ).eq( 1 ).remove();
815 $table
.find( 'tr' ).eq( 4 ).find( 'td' ).eq( 1 ).remove();
816 // - Set rowspan for 2nd cell of 3rd row to 3.
817 // This covers the removed cell in the 4th and 5th row.
818 $table
.find( 'tr' ).eq( 2 ).find( 'td' ).eq( 1 ).attr( 'rowspan', '3' );
820 $table
.tablesorter();
821 $table
.find( '.headerSort' ).eq( 0 ).trigger( 'click' );
825 'Basic planet table: same value for multiple rows via rowspan (sorting initially)',
829 function ( $table
) {
830 // Modify the table to have a multiple-row-spanning cell:
831 // - Remove 2nd cell of 4th row, and, 2nd cell or 5th row.
832 $table
.find( 'tr' ).eq( 3 ).find( 'td' ).eq( 1 ).remove();
833 $table
.find( 'tr' ).eq( 4 ).find( 'td' ).eq( 1 ).remove();
834 // - Set rowspan for 2nd cell of 3rd row to 3.
835 // This covers the removed cell in the 4th and 5th row.
836 $table
.find( 'tr' ).eq( 2 ).find( 'td' ).eq( 1 ).attr( 'rowspan', '3' );
838 $table
.tablesorter( { sortList
: [
844 'Basic planet table: Same value for multiple rows via rowspan II',
848 function ( $table
) {
849 // Modify the table to have a multiple-row-spanning cell:
850 // - Remove 1st cell of 4th row, and, 1st cell or 5th row.
851 $table
.find( 'tr' ).eq( 3 ).find( 'td' ).eq( 0 ).remove();
852 $table
.find( 'tr' ).eq( 4 ).find( 'td' ).eq( 0 ).remove();
853 // - Set rowspan for 1st cell of 3rd row to 3.
854 // This covers the removed cell in the 4th and 5th row.
855 $table
.find( 'tr' ).eq( 2 ).find( 'td' ).eq( 0 ).attr( 'rowspan', '3' );
857 $table
.tablesorter();
858 $table
.find( '.headerSort' ).eq( 0 ).trigger( 'click' );
863 'Complex date parsing I',
867 function ( $table
) {
868 mw
.config
.set( 'wgDefaultDateFormat', 'mdy' );
870 $table
.tablesorter();
871 $table
.find( '.headerSort' ).eq( 0 ).trigger( 'click' );
876 'Currency parsing I',
880 function ( $table
) {
881 $table
.tablesorter();
882 $table
.find( '.headerSort' ).eq( 0 ).trigger( 'click' );
886 planetsAscNameLegacy
= planetsAscName
.slice( 0 );
887 planetsAscNameLegacy
[ 4 ] = planetsAscNameLegacy
[ 5 ];
888 planetsAscNameLegacy
.pop();
891 'Legacy compat with .sortbottom',
894 planetsAscNameLegacy
,
895 function ( $table
) {
896 $table
.find( 'tr' ).last().addClass( 'sortbottom' );
897 $table
.tablesorter();
898 $table
.find( '.headerSort' ).eq( 0 ).trigger( 'click' );
902 QUnit
.test( 'Test detection routine', function ( assert
) {
905 '<table class="sortable">' +
906 '<caption>CAPTION</caption>' +
907 '<tr><th>THEAD</th></tr>' +
908 '<tr><td>1</td></tr>' +
909 '<tr class="sortbottom"><td>text</td></tr>' +
912 $table
.tablesorter();
913 $table
.find( '.headerSort' ).eq( 0 ).trigger( 'click' );
916 $table
.data( 'tablesorter' ).config
.parsers
[ 0 ].id
,
918 'Correctly detected column content skipping sortbottom'
922 /** FIXME: the diff output is not very readeable. */
923 QUnit
.test( 'T34047 - caption must be before thead', function ( assert
) {
926 '<table class="sortable">' +
927 '<caption>CAPTION</caption>' +
928 '<tr><th>THEAD</th></tr>' +
929 '<tr><td>A</td></tr>' +
930 '<tr><td>B</td></tr>' +
931 '<tr class="sortbottom"><td>TFOOT</td></tr>' +
934 $table
.tablesorter();
937 $table
.children().get( 0 ).nodeName
,
939 'First element after <thead> must be <caption> (T34047)'
943 QUnit
.test( 'data-sort-value attribute, when available, should override sorting position', function ( assert
) {
946 // Example 1: All cells except one cell without data-sort-value,
947 // which should be sorted at it's text content value.
949 '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
951 '<tr><td>Cheetah</td></tr>' +
952 '<tr><td data-sort-value="Apple">Bird</td></tr>' +
953 '<tr><td data-sort-value="Bananna">Ferret</td></tr>' +
954 '<tr><td data-sort-value="Drupe">Elephant</td></tr>' +
955 '<tr><td data-sort-value="Cherry">Dolphin</td></tr>' +
958 $table
.tablesorter().find( '.headerSort' ).eq( 0 ).trigger( 'click' );
961 $table
.find( 'tbody > tr' ).each( function ( i
, tr
) {
962 $( tr
).find( 'td' ).each( function ( i
, td
) {
964 data
: $( td
).data( 'sortValue' ),
970 assert
.deepEqual( data
, [
991 ], 'Order matches expected order (based on data-sort-value attribute values)' );
995 '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
997 '<tr><td>D</td></tr>' +
998 '<tr><td data-sort-value="E">A</td></tr>' +
999 '<tr><td>B</td></tr>' +
1000 '<tr><td>G</td></tr>' +
1001 '<tr><td data-sort-value="F">C</td></tr>' +
1002 '<tr><td><span data-sort-value="D">H</span></td></tr>' +
1005 $table
.tablesorter().find( '.headerSort' ).eq( 0 ).trigger( 'click' );
1008 $table
.find( 'tbody > tr' ).each( function ( i
, tr
) {
1009 $( tr
).find( 'td' ).each( function ( i
, td
) {
1011 data
: $( td
).data( 'sortValue' ),
1012 text
: $( td
).text()
1017 assert
.deepEqual( data
, [
1042 ], 'Order matches expected order (based on data-sort-value attribute values)' );
1044 // Example 3: Test that live changes are used from data-sort-value,
1045 // even if they change after the tablesorter is constructed (T40152).
1047 '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
1049 '<tr><td>D</td></tr>' +
1050 '<tr><td data-sort-value="1">A</td></tr>' +
1051 '<tr><td>B</td></tr>' +
1052 '<tr><td data-sort-value="2">G</td></tr>' +
1053 '<tr><td>C</td></tr>' +
1056 // initialize table sorter and sort once
1057 $table
.tablesorter().find( '.headerSort' ).eq( 0 ).trigger( 'click' );
1059 // Change the sortValue data properties (T40152)
1061 $table
.find( 'td:contains(A)' ).data( 'sortValue', 3 );
1063 $table
.find( 'td:contains(B)' ).data( 'sortValue', 1 );
1064 // - remove data, bring back attribute: 2
1065 $table
.find( 'td:contains(G)' ).removeData( 'sortValue' );
1067 // Now sort again (twice, so it is back at Ascending)
1068 $table
.find( '.headerSort' ).eq( 0 ).trigger( 'click' );
1069 $table
.find( '.headerSort' ).eq( 0 ).trigger( 'click' );
1072 $table
.find( 'tbody > tr' ).each( function ( i
, tr
) {
1073 $( tr
).find( 'td' ).each( function ( i
, td
) {
1075 data
: $( td
).data( 'sortValue' ),
1076 text
: $( td
).text()
1081 assert
.deepEqual( data
, [
1102 ], 'Order matches expected order, using the current sortValue in $.data()' );
1106 tableTest( 'T10115: sort numbers with commas (ascending)',
1107 [ 'Numbers' ], numbers
, numbersAsc
,
1108 function ( $table
) {
1109 $table
.tablesorter();
1110 $table
.find( '.headerSort' ).eq( 0 ).trigger( 'click' );
1114 tableTest( 'T10115: sort numbers with commas (descending)',
1115 [ 'Numbers' ], numbers
, reversed( numbersAsc
),
1116 function ( $table
) {
1117 $table
.tablesorter();
1118 $table
.find( '.headerSort' ).eq( 0 ).trigger( 'click' ).trigger( 'click' );
1121 // TODO add numbers sorting tests for T10115 with a different language
1123 QUnit
.test( 'T34888 - Tables inside a tableheader cell', function ( assert
) {
1126 '<table class="sortable" id="mw-bug-32888">' +
1127 '<tr><th>header<table id="mw-bug-32888-2">' +
1128 '<tr><th>1</th><th>2</th></tr>' +
1129 '</table></th></tr>' +
1130 '<tr><td>A</td></tr>' +
1131 '<tr><td>B</td></tr>' +
1134 $table
.tablesorter();
1137 $table
.find( '> thead > tr > th.headerSort' ).length
,
1139 'Child tables inside a headercell should not interfere with sortable headers (T34888)'
1142 $( '#mw-bug-32888-2' ).find( 'th.headerSort' ).length
,
1144 'The headers of child tables inside a headercell should not be sortable themselves (T34888)'
1149 'Correct date sorting I',
1151 correctDateSorting1
,
1152 correctDateSortingSorted1
,
1153 function ( $table
) {
1154 mw
.config
.set( 'wgDefaultDateFormat', 'mdy' );
1156 $table
.tablesorter();
1157 $table
.find( '.headerSort' ).eq( 0 ).trigger( 'click' );
1162 'Correct date sorting II',
1164 correctDateSorting2
,
1165 correctDateSortingSorted2
,
1166 function ( $table
) {
1167 mw
.config
.set( 'wgDefaultDateFormat', 'dmy' );
1169 $table
.tablesorter();
1170 $table
.find( '.headerSort' ).eq( 0 ).trigger( 'click' );
1178 isoDateSortingSorted
,
1179 function ( $table
) {
1180 mw
.config
.set( 'wgDefaultDateFormat', 'dmy' );
1182 $table
.tablesorter();
1183 $table
.find( '.headerSort' ).eq( 0 ).trigger( 'click' );
1187 QUnit
.test( 'Sorting images using alt text', function ( assert
) {
1189 '<table class="sortable">' +
1190 '<tr><th>THEAD</th></tr>' +
1191 '<tr><td><img alt="2"/></td></tr>' +
1192 '<tr><td>1</td></tr>' +
1195 $table
.tablesorter().find( '.headerSort' ).eq( 0 ).trigger( 'click' );
1198 $table
.find( 'td' ).first().text(),
1200 'Applied correct sorting order'
1204 QUnit
.test( 'Sorting images using alt text (complex)', function ( assert
) {
1206 '<table class="sortable">' +
1207 '<tr><th>THEAD</th></tr>' +
1208 '<tr><td><img alt="D" />A</td></tr>' +
1209 '<tr><td>CC</td></tr>' +
1210 '<tr><td><a><img alt="A" /></a>F</tr>' +
1211 '<tr><td><img alt="A" /><strong>E</strong></tr>' +
1212 '<tr><td><strong><img alt="A" />D</strong></tr>' +
1213 '<tr><td><img alt="A" />C</tr>' +
1216 $table
.tablesorter().find( '.headerSort' ).eq( 0 ).trigger( 'click' );
1219 $table
.find( 'td' ).text(),
1221 'Applied correct sorting order'
1225 QUnit
.test( 'Sorting images using alt text (with format autodetection)', function ( assert
) {
1227 '<table class="sortable">' +
1228 '<tr><th>THEAD</th></tr>' +
1229 '<tr><td><img alt="1" />7</td></tr>' +
1230 '<tr><td>1<img alt="6" /></td></tr>' +
1231 '<tr><td>5</td></tr>' +
1232 '<tr><td>4</td></tr>' +
1235 $table
.tablesorter().find( '.headerSort' ).eq( 0 ).trigger( 'click' );
1238 $table
.find( 'td' ).text(),
1240 'Applied correct sorting order'
1244 QUnit
.test( 'T40911 - The row with the largest amount of columns should receive the sort indicators', function ( assert
) {
1246 '<table class="sortable">' +
1248 '<tr><th rowspan="2" id="A1">A1</th><th colspan="2">B2a</th></tr>' +
1249 '<tr><th id="B2b">B2b</th><th id="C2b">C2b</th></tr>' +
1251 '<tr><td>A</td><td>Aa</td><td>Ab</td></tr>' +
1252 '<tr><td>B</td><td>Ba</td><td>Bb</td></tr>' +
1255 $table
.tablesorter();
1258 $table
.find( '#A1' ).attr( 'class' ),
1260 'The first column of the first row should be sortable'
1263 $table
.find( '#B2b' ).attr( 'class' ),
1265 'The th element of the 2nd row of the 2nd column should be sortable'
1268 $table
.find( '#C2b' ).attr( 'class' ),
1270 'The th element of the 2nd row of the 3rd column should be sortable'
1274 QUnit
.test( 'rowspans in table headers should prefer the last row when rows are equal in length', function ( assert
) {
1276 '<table class="sortable">' +
1278 '<tr><th rowspan="2" id="A1">A1</th><th>B2a</th></tr>' +
1279 '<tr><th id="B2b">B2b</th></tr>' +
1281 '<tr><td>A</td><td>Aa</td></tr>' +
1282 '<tr><td>B</td><td>Ba</td></tr>' +
1285 $table
.tablesorter();
1288 $table
.find( '#A1' ).attr( 'class' ),
1290 'The first column of the first row should be sortable'
1293 $table
.find( '#B2b' ).attr( 'class' ),
1295 'The th element of the 2nd row of the 2nd column should be sortable'
1299 QUnit
.test( 'holes in the table headers should not throw JS errors', function ( assert
) {
1301 '<table class="sortable">' +
1303 '<tr><th id="A1">A1</th><th>B1</th><th id="C1" rowspan="2">C1</th></tr>' +
1304 '<tr><th id="A2">A2</th></tr>' +
1306 '<tr><td>A</td><td>Aa</td><td>Aaa</td></tr>' +
1307 '<tr><td>B</td><td>Ba</td><td>Bbb</td></tr>' +
1310 $table
.tablesorter();
1311 assert
.strictEqual( $table
.find( '#A2' ).data( 'headerIndex' ),
1313 'A2 should not be a sort header'
1315 assert
.strictEqual( $table
.find( '#C1' ).data( 'headerIndex' ),
1317 'C1 should be a sort header'
1322 QUnit
.test( 'td cells in thead should not be taken into account for longest row calculation', function ( assert
) {
1324 '<table class="sortable">' +
1326 '<tr><th id="A1">A1</th><th>B1</th><td id="C1">C1</td></tr>' +
1327 '<tr><th id="A2">A2</th><th>B2</th><th id="C2">C2</th></tr>' +
1331 $table
.tablesorter();
1332 assert
.strictEqual( $table
.find( '#C2' ).data( 'headerIndex' ),
1334 'C2 should be a sort header'
1336 assert
.strictEqual( $table
.find( '#C1' ).data( 'headerIndex' ),
1338 'C1 should not be a sort header'
1342 // T43889 - exploding rowspans in more complex cases
1344 'Rowspan exploding with row headers',
1345 '<table class="sortable">' +
1346 '<thead><tr><th id="sortme">n</th><th>foo</th><th>bar</th><th>baz</th></tr></thead>' +
1348 '<tr><td>1</td><th rowspan="2">foo</th><td rowspan="2">bar</td><td>baz</td></tr>' +
1349 '<tr><td>2</td><td>baz</td></tr>' +
1352 [ '1', 'foo', 'bar', 'baz' ],
1353 [ '2', 'foo', 'bar', 'baz' ]
1357 // T55211 - exploding rowspans in more complex cases
1359 'Rowspan exploding with row headers and colspans', function ( assert
) {
1360 var $table
= $( '<table class="sortable">' +
1361 '<thead><tr><th rowspan="2">n</th><th colspan="2">foo</th><th rowspan="2">baz</th></tr>' +
1362 '<tr><th>foo</th><th>bar</th></tr></thead>' +
1364 '<tr><td>1</td><td>foo</td><td>bar</td><td>baz</td></tr>' +
1365 '<tr><td>2</td><td>foo</td><td>bar</td><td>baz</td></tr>' +
1366 '</tbody></table>' );
1368 $table
.tablesorter();
1369 assert
.strictEqual( $table
.find( 'tr' ).eq( 1 ).find( 'th' ).eq( 1 ).data( 'headerIndex' ),
1371 'Incorrect index of sort header'
1377 'Rowspan exploding with colspanned cells',
1378 '<table class="sortable">' +
1379 '<thead><tr><th id="sortme">n</th><th>foo</th><th>bar</th><th>baz</th></tr></thead>' +
1381 '<tr><td>1</td><td>foo</td><td>bar</td><td rowspan="2">baz</td></tr>' +
1382 '<tr><td>2</td><td colspan="2">foobar</td></tr>' +
1385 [ '1', 'foo', 'bar', 'baz' ],
1386 [ '2', 'foobar', 'baz' ]
1391 'Rowspan exploding with colspanned cells (2)',
1392 '<table class="sortable">' +
1393 '<thead><tr><th>n</th><th>foo</th><th>bar</th><th>baz</th><th id="sortme">n2</th></tr></thead>' +
1395 '<tr><td>1</td><td>foo</td><td>bar</td><td rowspan="2">baz</td><td>2</td></tr>' +
1396 '<tr><td>2</td><td colspan="2">foobar</td><td>1</td></tr>' +
1399 [ '2', 'foobar', 'baz', '1' ],
1400 [ '1', 'foo', 'bar', 'baz', '2' ]
1405 'Rowspan exploding with rightmost rows spanning most',
1406 '<table class="sortable">' +
1407 '<thead><tr><th id="sortme">n</th><th>foo</th><th>bar</th></tr></thead>' +
1409 '<tr><td>1</td><td rowspan="2">foo</td><td rowspan="4">bar</td></tr>' +
1410 '<tr><td>2</td></tr>' +
1411 '<tr><td>3</td><td rowspan="2">foo</td></tr>' +
1412 '<tr><td>4</td></tr>' +
1415 [ '1', 'foo', 'bar' ],
1416 [ '2', 'foo', 'bar' ],
1417 [ '3', 'foo', 'bar' ],
1418 [ '4', 'foo', 'bar' ]
1423 'Rowspan exploding with rightmost rows spanning most (2)',
1424 '<table class="sortable">' +
1425 '<thead><tr><th id="sortme">n</th><th>foo</th><th>bar</th><th>baz</th></tr></thead>' +
1427 '<tr><td>1</td><td rowspan="2">foo</td><td rowspan="4">bar</td><td>baz</td></tr>' +
1428 '<tr><td>2</td><td>baz</td></tr>' +
1429 '<tr><td>3</td><td rowspan="2">foo</td><td>baz</td></tr>' +
1430 '<tr><td>4</td><td>baz</td></tr>' +
1433 [ '1', 'foo', 'bar', 'baz' ],
1434 [ '2', 'foo', 'bar', 'baz' ],
1435 [ '3', 'foo', 'bar', 'baz' ],
1436 [ '4', 'foo', 'bar', 'baz' ]
1441 'Rowspan exploding with row-and-colspanned cells',
1442 '<table class="sortable">' +
1443 '<thead><tr><th id="sortme">n</th><th>foo1</th><th>foo2</th><th>bar</th><th>baz</th></tr></thead>' +
1445 '<tr><td>1</td><td rowspan="2">foo1</td><td rowspan="2">foo2</td><td rowspan="4">bar</td><td>baz</td></tr>' +
1446 '<tr><td>2</td><td>baz</td></tr>' +
1447 '<tr><td>3</td><td colspan="2" rowspan="2">foo</td><td>baz</td></tr>' +
1448 '<tr><td>4</td><td>baz</td></tr>' +
1451 [ '1', 'foo1', 'foo2', 'bar', 'baz' ],
1452 [ '2', 'foo1', 'foo2', 'bar', 'baz' ],
1453 [ '3', 'foo', 'bar', 'baz' ],
1454 [ '4', 'foo', 'bar', 'baz' ]
1459 'Rowspan exploding with uneven rowspan layout',
1460 '<table class="sortable">' +
1461 '<thead><tr><th id="sortme">n</th><th>foo1</th><th>foo2</th><th>foo3</th><th>bar</th><th>baz</th></tr></thead>' +
1463 '<tr><td>1</td><td rowspan="2">foo1</td><td rowspan="2">foo2</td><td rowspan="2">foo3</td><td>bar</td><td>baz</td></tr>' +
1464 '<tr><td>2</td><td rowspan="3">bar</td><td>baz</td></tr>' +
1465 '<tr><td>3</td><td rowspan="2">foo1</td><td rowspan="2">foo2</td><td rowspan="2">foo3</td><td>baz</td></tr>' +
1466 '<tr><td>4</td><td>baz</td></tr>' +
1469 [ '1', 'foo1', 'foo2', 'foo3', 'bar', 'baz' ],
1470 [ '2', 'foo1', 'foo2', 'foo3', 'bar', 'baz' ],
1471 [ '3', 'foo1', 'foo2', 'foo3', 'bar', 'baz' ],
1472 [ '4', 'foo1', 'foo2', 'foo3', 'bar', 'baz' ]
1476 QUnit
.test( 'T105731 - incomplete rows in table body', function ( assert
) {
1477 var $table
, parsers
;
1479 '<table class="sortable">' +
1480 '<tr><th>A</th><th>B</th></tr>' +
1481 '<tr><td>3</td></tr>' +
1482 '<tr><td>1</td><td>2</td></tr>' +
1485 $table
.tablesorter();
1486 $table
.find( '.headerSort' ).eq( 0 ).trigger( 'click' );
1487 // now the first row have 2 columns
1488 $table
.find( '.headerSort' ).eq( 1 ).trigger( 'click' );
1490 parsers
= $table
.data( 'tablesorter' ).config
.parsers
;
1495 'detectParserForColumn() detect 2 parsers'
1501 'detectParserForColumn() detect parser.id "number" for second column'
1505 parsers
[ 1 ].format( $table
.find( 'tbody > tr > td' ).eq( 1 ).text() ),
1507 'empty cell is sorted as number -Infinity'
1511 QUnit
.test( 'bug T114721 - use of expand-child class', function ( assert
) {
1512 var $table
, parsers
;
1514 '<table class="sortable">' +
1515 '<tr><th>A</th><th>B</th></tr>' +
1516 '<tr><td>b</td><td>4</td></tr>' +
1517 '<tr class="expand-child"><td colspan="2">some text follow b</td></tr>' +
1518 '<tr><td>a</td><td>2</td></tr>' +
1519 '<tr class="expand-child"><td colspan="2">some text follow a</td></tr>' +
1520 '<tr class="expand-child"><td colspan="2">more text</td></tr>' +
1523 $table
.tablesorter();
1524 $table
.find( '.headerSort' ).eq( 0 ).trigger( 'click' );
1527 tableExtract( $table
),
1530 [ 'some text follow a' ],
1533 [ 'some text follow b' ]
1535 'row with expand-child class follow above row'
1538 parsers
= $table
.data( 'tablesorter' ).config
.parsers
;
1542 'detectParserForColumn() detect parser.id "number" for second column'
1545 QUnit
.test( 'T29745 - References ignored in sortkey', function ( assert
) {
1546 var $table
, parsers
;
1548 '<table class="sortable">' +
1549 '<tr><th>A</th></tr>' +
1550 '<tr><td>10</td></tr>' +
1551 '<tr><td>2<sup class="reference"><a href="#cite_note-1">[1]</a></sup></td></tr>' +
1554 $table
.tablesorter();
1555 $table
.find( '.headerSort' ).eq( 0 ).trigger( 'click' );
1558 tableExtract( $table
),
1563 'References ignored in sortkey'
1566 parsers
= $table
.data( 'tablesorter' ).config
.parsers
;
1570 'detectParserForColumn() detect parser.id "number"'