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' ],
85 [ 'January, 19 2010' ],
89 [ 'December 12 \'10' ]
95 [ 'January, 19 2010' ],
96 [ 'December 12 \'10' ]
115 // Comma's sort after dots
116 // Not intentional but test to detect changes
137 correctDateSorting1 = [
138 [ '01 January 2010' ],
139 [ '05 February 2010' ],
140 [ '16 January 2010' ]
142 correctDateSortingSorted1 = [
143 [ '01 January 2010' ],
144 [ '16 January 2010' ],
145 [ '05 February 2010' ]
148 correctDateSorting2 = [
149 [ 'January 01 2010' ],
150 [ 'February 05 2010' ],
151 [ 'January 16 2010' ]
153 correctDateSortingSorted2 = [
154 [ 'January 01 2010' ],
155 [ 'January 16 2010' ],
156 [ 'February 05 2010' ]
160 [ '2009-12-25T12:30:45.001Z' ],
163 [ '2009-12-25T12:30:45' ],
164 [ '2009-12-25T12:30:45.111' ],
165 [ '2009-12-25T12:30:45+01:00' ]
167 isoDateSortingSorted = [
169 [ '2009-12-25T12:30:45' ],
170 [ '2009-12-25T12:30:45+01:00' ],
171 [ '2009-12-25T12:30:45.001Z' ],
172 [ '2009-12-25T12:30:45.111' ],
177 QUnit.module( 'jquery.tablesorter', QUnit.newMwEnvironment( {
179 this.liveMonths = mw.language.months;
180 mw.language.months = {
182 names: [ 'january', 'february', 'march', 'april', 'may_long', 'june',
183 'july', 'august', 'september', 'october', 'november', 'december' ],
184 genitive: [ 'january-gen', 'february-gen', 'march-gen', 'april-gen', 'may-gen', 'june-gen',
185 'july-gen', 'august-gen', 'september-gen', 'october-gen', 'november-gen', 'december-gen' ],
186 abbrev: [ 'jan', 'feb', 'mar', 'apr', 'may', 'jun',
187 'jul', 'aug', 'sep', 'oct', 'nov', 'dec' ]
189 names: [ 'January', 'February', 'March', 'April', 'May', 'June',
190 'July', 'August', 'September', 'October', 'November', 'December' ],
191 genitive: [ 'January', 'February', 'March', 'April', 'May', 'June',
192 'July', 'August', 'September', 'October', 'November', 'December' ],
193 abbrev: [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
194 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ]
197 teardown: function () {
198 mw.language.months = this.liveMonths;
201 wgDefaultDateFormat: 'dmy',
202 wgSeparatorTransformTable: [ '', '' ],
203 wgDigitTransformTable: [ '', '' ],
204 wgPageContentLanguage: 'en'
209 * Create an HTML table from an array of row arrays containing text strings.
210 * First row will be header row. No fancy rowspan/colspan stuff.
212 * @param {string[]} header
213 * @param {string[][]} data
216 function tableCreate( header, data ) {
218 $table = $( '<table class="sortable"><thead></thead><tbody></tbody></table>' ),
219 $thead = $table.find( 'thead' ),
220 $tbody = $table.find( 'tbody' ),
223 $.each( header, function ( i, str ) {
224 var $th = $( '<th>' );
225 $th.text( str ).appendTo( $tr );
227 $tr.appendTo( $thead );
229 for ( i = 0; i < data.length; i++ ) {
230 /*jshint loopfunc: true */
232 $.each( data[ i ], function ( j, str ) {
233 var $td = $( '<td>' );
234 $td.text( str ).appendTo( $tr );
236 $tr.appendTo( $tbody );
242 * Extract text from table.
244 * @param {jQuery} $table
245 * @return {string[][]}
247 function tableExtract( $table ) {
250 $table.find( 'tbody' ).find( 'tr' ).each( function ( i, tr ) {
252 $( tr ).find( 'td,th' ).each( function ( i, td ) {
253 row.push( $( td ).text() );
261 * Run a table test by building a table with the given data,
262 * running some callback on it, then checking the results.
264 * @param {string} msg text to pass on to qunit for the comparison
265 * @param {string[]} header cols to make the table
266 * @param {string[][]} data rows/cols to make the table
267 * @param {string[][]} expected rows/cols to compare against at end
268 * @param {function($table)} callback something to do with the table before we compare
270 function tableTest( msg, header, data, expected, callback ) {
271 QUnit.test( msg, 1, function ( assert ) {
273 $table = tableCreate( header, data );
275 // Give caller a chance to set up sorting and manipulate the table.
278 // Table sorting is done synchronously; if it ever needs to change back
279 // to asynchronous, we'll need a timeout or a callback here.
280 extracted = tableExtract( $table );
281 assert.deepEqual( extracted, expected, msg );
286 * Run a table test by building a table with the given HTML,
287 * running some callback on it, then checking the results.
289 * @param {string} msg text to pass on to qunit for the comparison
290 * @param {string} html HTML to make the table
291 * @param {string[][]} expected Rows/cols to compare against at end
292 * @param {function($table)} callback Something to do with the table before we compare
294 function tableTestHTML( msg, html, expected, callback ) {
295 QUnit.test( msg, 1, function ( assert ) {
299 // Give caller a chance to set up sorting and manipulate the table.
303 $table.tablesorter();
304 $table.find( '#sortme' ).click();
307 // Table sorting is done synchronously; if it ever needs to change back
308 // to asynchronous, we'll need a timeout or a callback here.
309 extracted = tableExtract( $table );
310 assert.deepEqual( extracted, expected, msg );
314 function reversed( arr ) {
316 var arr2 = arr.slice( 0 );
323 // Sample data set using planets named and their radius
326 'Basic planet table: sorting initially - ascending by name',
330 function ( $table ) {
331 $table.tablesorter( { sortList: [
337 'Basic planet table: sorting initially - descending by radius',
340 reversed( planetsAscRadius ),
341 function ( $table ) {
342 $table.tablesorter( { sortList: [
348 'Basic planet table: ascending by name',
352 function ( $table ) {
353 $table.tablesorter();
354 $table.find( '.headerSort:eq(0)' ).click();
358 'Basic planet table: ascending by name a second time',
362 function ( $table ) {
363 $table.tablesorter();
364 $table.find( '.headerSort:eq(0)' ).click();
368 'Basic planet table: ascending by name (multiple clicks)',
372 function ( $table ) {
373 $table.tablesorter();
374 $table.find( '.headerSort:eq(0)' ).click();
375 $table.find( '.headerSort:eq(1)' ).click();
376 $table.find( '.headerSort:eq(0)' ).click();
380 'Basic planet table: descending by name',
383 reversed( planetsAscName ),
384 function ( $table ) {
385 $table.tablesorter();
386 $table.find( '.headerSort:eq(0)' ).click().click();
390 'Basic planet table: ascending radius',
394 function ( $table ) {
395 $table.tablesorter();
396 $table.find( '.headerSort:eq(1)' ).click();
400 'Basic planet table: descending radius',
403 reversed( planetsAscRadius ),
404 function ( $table ) {
405 $table.tablesorter();
406 $table.find( '.headerSort:eq(1)' ).click().click();
410 'Sorting multiple columns by passing sort list',
414 function ( $table ) {
424 'Sorting multiple columns by programmatically triggering sort()',
428 function ( $table ) {
429 $table.tablesorter();
430 $table.data( 'tablesorter' ).sort(
439 'Reset to initial sorting by triggering sort() without any parameters',
443 function ( $table ) {
450 $table.data( 'tablesorter' ).sort(
456 $table.data( 'tablesorter' ).sort();
460 'Sort via click event after having initialized the tablesorter with initial sorting',
464 function ( $table ) {
466 { sortList: [ { 0: 'asc' }, { 1: 'asc' } ] }
468 $table.find( '.headerSort:eq(0)' ).click();
472 'Multi-sort via click event after having initialized the tablesorter with initial sorting',
476 function ( $table ) {
478 { sortList: [ { 0: 'desc' }, { 1: 'desc' } ] }
480 $table.find( '.headerSort:eq(0)' ).click();
482 // Pretend to click while pressing the multi-sort key
483 var event = $.Event( 'click' );
484 event[ $table.data( 'tablesorter' ).config.sortMultiSortKey ] = true;
485 $table.find( '.headerSort:eq(1)' ).trigger( event );
488 QUnit.test( 'Reset sorting making table appear unsorted', 3, function ( assert ) {
489 var $table = tableCreate( header, simple );
496 $table.data( 'tablesorter' ).sort( [] );
499 $table.find( 'th.headerSortUp' ).length + $table.find( 'th.headerSortDown' ).length,
501 'No sort specific sort classes addign to header cells'
505 $table.find( 'th' ).first().attr( 'title' ),
506 mw.msg( 'sort-ascending' ),
507 'First header cell has default title'
511 $table.find( 'th' ).first().attr( 'title' ),
512 $table.find( 'th' ).last().attr( 'title' ),
513 'Both header cells\' titles match'
517 // Sorting with colspans
519 tableTest( 'Sorting with colspanned headers: spanned column',
522 [ aaa1, aab5, abc3, bbc2, caa4 ],
523 function ( $table ) {
524 // Make colspanned header for test
525 $table.find( 'tr:eq(0) th:eq(1), tr:eq(0) th:eq(2)' ).remove();
526 $table.find( 'tr:eq(0) th:eq(0)' ).attr( 'colspan', '3' );
528 $table.tablesorter();
529 $table.find( '.headerSort:eq(0)' ).click();
532 tableTest( 'Sorting with colspanned headers: sort spanned column twice',
535 [ caa4, bbc2, abc3, aab5, aaa1 ],
536 function ( $table ) {
537 // Make colspanned header for test
538 $table.find( 'tr:eq(0) th:eq(1), tr:eq(0) th:eq(2)' ).remove();
539 $table.find( 'tr:eq(0) th:eq(0)' ).attr( 'colspan', '3' );
541 $table.tablesorter();
542 $table.find( '.headerSort:eq(0)' ).click();
543 $table.find( '.headerSort:eq(0)' ).click();
546 tableTest( 'Sorting with colspanned headers: subsequent column',
549 [ aaa1, bbc2, abc3, caa4, aab5 ],
550 function ( $table ) {
551 // Make colspanned header for test
552 $table.find( 'tr:eq(0) th:eq(1), tr:eq(0) th:eq(2)' ).remove();
553 $table.find( 'tr:eq(0) th:eq(0)' ).attr( 'colspan', '3' );
555 $table.tablesorter();
556 $table.find( '.headerSort:eq(1)' ).click();
559 tableTest( 'Sorting with colspanned headers: sort subsequent column twice',
562 [ aab5, caa4, abc3, bbc2, aaa1 ],
563 function ( $table ) {
564 // Make colspanned header for test
565 $table.find( 'tr:eq(0) th:eq(1), tr:eq(0) th:eq(2)' ).remove();
566 $table.find( 'tr:eq(0) th:eq(0)' ).attr( 'colspan', '3' );
568 $table.tablesorter();
569 $table.find( '.headerSort:eq(1)' ).click();
570 $table.find( '.headerSort:eq(1)' ).click();
574 QUnit.test( 'Basic planet table: one unsortable column', 3, function ( assert ) {
575 var $table = tableCreate( header, planets ),
577 $table.find( 'tr:eq(0) > th:eq(0)' ).addClass( 'unsortable' );
579 $table.tablesorter();
580 $table.find( 'tr:eq(0) > th:eq(0)' ).click();
583 tableExtract( $table ),
588 $cell = $table.find( 'tr:eq(0) > th:eq(0)' );
589 $table.find( 'tr:eq(0) > th:eq(1)' ).click();
592 $cell.hasClass( 'headerSortUp' ) || $cell.hasClass( 'headerSortDown' ),
594 'after sort: no class headerSortUp or headerSortDown'
598 $cell.attr( 'title' ),
600 'after sort: no title tag added'
607 'Bug 28775: German-style (dmy) short numeric dates',
610 // German-style dates are day-month-year
618 // Sorted by ascending date
625 function ( $table ) {
626 mw.config.set( 'wgDefaultDateFormat', 'dmy' );
627 mw.config.set( 'wgPageContentLanguage', 'de' );
629 $table.tablesorter();
630 $table.find( '.headerSort:eq(0)' ).click();
635 'Bug 28775: American-style (mdy) short numeric dates',
638 // American-style dates are month-day-year
646 // Sorted by ascending date
653 function ( $table ) {
654 mw.config.set( 'wgDefaultDateFormat', 'mdy' );
656 $table.tablesorter();
657 $table.find( '.headerSort:eq(0)' ).click();
662 'Bug 17141: IPv4 address sorting',
666 function ( $table ) {
667 $table.tablesorter();
668 $table.find( '.headerSort:eq(0)' ).click();
672 'Bug 17141: IPv4 address sorting (reverse)',
675 reversed( ipv4Sorted ),
676 function ( $table ) {
677 $table.tablesorter();
678 $table.find( '.headerSort:eq(0)' ).click().click();
683 'Accented Characters with custom collation',
687 function ( $table ) {
688 mw.config.set( 'tableSorterCollation', {
695 $table.tablesorter();
696 $table.find( '.headerSort:eq(0)' ).click();
700 QUnit.test( 'Rowspan not exploded on init', 1, function ( assert ) {
701 var $table = tableCreate( header, planets );
703 // Modify the table to have a multiple-row-spanning cell:
704 // - Remove 2nd cell of 4th row, and, 2nd cell or 5th row.
705 $table.find( 'tr:eq(3) td:eq(1), tr:eq(4) td:eq(1)' ).remove();
706 // - Set rowspan for 2nd cell of 3rd row to 3.
707 // This covers the removed cell in the 4th and 5th row.
708 $table.find( 'tr:eq(2) td:eq(1)' ).attr( 'rowspan', '3' );
710 $table.tablesorter();
713 $table.find( 'tr:eq(2) td:eq(1)' ).prop( 'rowSpan' ),
715 'Rowspan not exploded'
720 [ 'Earth', '6051.8' ],
722 [ 'Mars', '6051.8' ],
727 planetsRowspanII = [ jupiter, mercury, saturn, venus, [ 'Venus', '6371.0' ], [ 'Venus', '3390.0' ] ];
730 'Basic planet table: same value for multiple rows via rowspan',
734 function ( $table ) {
735 // Modify the table to have a multiple-row-spanning cell:
736 // - Remove 2nd cell of 4th row, and, 2nd cell or 5th row.
737 $table.find( 'tr:eq(3) td:eq(1), tr:eq(4) td:eq(1)' ).remove();
738 // - Set rowspan for 2nd cell of 3rd row to 3.
739 // This covers the removed cell in the 4th and 5th row.
740 $table.find( 'tr:eq(2) td:eq(1)' ).attr( 'rowspan', '3' );
742 $table.tablesorter();
743 $table.find( '.headerSort:eq(0)' ).click();
747 'Basic planet table: same value for multiple rows via rowspan (sorting initially)',
751 function ( $table ) {
752 // Modify the table to have a multiple-row-spanning cell:
753 // - Remove 2nd cell of 4th row, and, 2nd cell or 5th row.
754 $table.find( 'tr:eq(3) td:eq(1), tr:eq(4) td:eq(1)' ).remove();
755 // - Set rowspan for 2nd cell of 3rd row to 3.
756 // This covers the removed cell in the 4th and 5th row.
757 $table.find( 'tr:eq(2) td:eq(1)' ).attr( 'rowspan', '3' );
759 $table.tablesorter( { sortList: [
765 'Basic planet table: Same value for multiple rows via rowspan II',
769 function ( $table ) {
770 // Modify the table to have a multiple-row-spanning cell:
771 // - Remove 1st cell of 4th row, and, 1st cell or 5th row.
772 $table.find( 'tr:eq(3) td:eq(0), tr:eq(4) td:eq(0)' ).remove();
773 // - Set rowspan for 1st cell of 3rd row to 3.
774 // This covers the removed cell in the 4th and 5th row.
775 $table.find( 'tr:eq(2) td:eq(0)' ).attr( 'rowspan', '3' );
777 $table.tablesorter();
778 $table.find( '.headerSort:eq(0)' ).click();
783 'Complex date parsing I',
787 function ( $table ) {
788 mw.config.set( 'wgDefaultDateFormat', 'mdy' );
790 $table.tablesorter();
791 $table.find( '.headerSort:eq(0)' ).click();
796 'Currency parsing I',
800 function ( $table ) {
801 $table.tablesorter();
802 $table.find( '.headerSort:eq(0)' ).click();
806 planetsAscNameLegacy = planetsAscName.slice( 0 );
807 planetsAscNameLegacy[ 4 ] = planetsAscNameLegacy[ 5 ];
808 planetsAscNameLegacy.pop();
811 'Legacy compat with .sortbottom',
814 planetsAscNameLegacy,
815 function ( $table ) {
816 $table.find( 'tr:last' ).addClass( 'sortbottom' );
817 $table.tablesorter();
818 $table.find( '.headerSort:eq(0)' ).click();
822 QUnit.test( 'Test detection routine', 1, function ( assert ) {
825 '<table class="sortable">' +
826 '<caption>CAPTION</caption>' +
827 '<tr><th>THEAD</th></tr>' +
828 '<tr><td>1</td></tr>' +
829 '<tr class="sortbottom"><td>text</td></tr>' +
832 $table.tablesorter();
833 $table.find( '.headerSort:eq(0)' ).click();
836 $table.data( 'tablesorter' ).config.parsers[ 0 ].id,
838 'Correctly detected column content skipping sortbottom'
842 /** FIXME: the diff output is not very readeable. */
843 QUnit.test( 'bug 32047 - caption must be before thead', 1, function ( assert ) {
846 '<table class="sortable">' +
847 '<caption>CAPTION</caption>' +
848 '<tr><th>THEAD</th></tr>' +
849 '<tr><td>A</td></tr>' +
850 '<tr><td>B</td></tr>' +
851 '<tr class="sortbottom"><td>TFOOT</td></tr>' +
854 $table.tablesorter();
857 $table.children().get( 0 ).nodeName,
859 'First element after <thead> must be <caption> (bug 32047)'
863 QUnit.test( 'data-sort-value attribute, when available, should override sorting position', 3, function ( assert ) {
866 // Example 1: All cells except one cell without data-sort-value,
867 // which should be sorted at it's text content value.
869 '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
871 '<tr><td>Cheetah</td></tr>' +
872 '<tr><td data-sort-value="Apple">Bird</td></tr>' +
873 '<tr><td data-sort-value="Bananna">Ferret</td></tr>' +
874 '<tr><td data-sort-value="Drupe">Elephant</td></tr>' +
875 '<tr><td data-sort-value="Cherry">Dolphin</td></tr>' +
878 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
881 $table.find( 'tbody > tr' ).each( function ( i, tr ) {
882 $( tr ).find( 'td' ).each( function ( i, td ) {
884 data: $( td ).data( 'sortValue' ),
890 assert.deepEqual( data, [
911 ], 'Order matches expected order (based on data-sort-value attribute values)' );
915 '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
917 '<tr><td>D</td></tr>' +
918 '<tr><td data-sort-value="E">A</td></tr>' +
919 '<tr><td>B</td></tr>' +
920 '<tr><td>G</td></tr>' +
921 '<tr><td data-sort-value="F">C</td></tr>' +
924 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
927 $table.find( 'tbody > tr' ).each( function ( i, tr ) {
928 $( tr ).find( 'td' ).each( function ( i, td ) {
930 data: $( td ).data( 'sortValue' ),
936 assert.deepEqual( data, [
957 ], 'Order matches expected order (based on data-sort-value attribute values)' );
959 // Example 3: Test that live changes are used from data-sort-value,
960 // even if they change after the tablesorter is constructed (bug 38152).
962 '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
964 '<tr><td>D</td></tr>' +
965 '<tr><td data-sort-value="1">A</td></tr>' +
966 '<tr><td>B</td></tr>' +
967 '<tr><td data-sort-value="2">G</td></tr>' +
968 '<tr><td>C</td></tr>' +
971 // initialize table sorter and sort once
974 .find( '.headerSort:eq(0)' ).click();
976 // Change the sortValue data properties (bug 38152)
978 $table.find( 'td:contains(A)' ).data( 'sortValue', 3 );
980 $table.find( 'td:contains(B)' ).data( 'sortValue', 1 );
981 // - remove data, bring back attribute: 2
982 $table.find( 'td:contains(G)' ).removeData( 'sortValue' );
984 // Now sort again (twice, so it is back at Ascending)
985 $table.find( '.headerSort:eq(0)' ).click();
986 $table.find( '.headerSort:eq(0)' ).click();
989 $table.find( 'tbody > tr' ).each( function ( i, tr ) {
990 $( tr ).find( 'td' ).each( function ( i, td ) {
992 data: $( td ).data( 'sortValue' ),
998 assert.deepEqual( data, [
1019 ], 'Order matches expected order, using the current sortValue in $.data()' );
1023 tableTest( 'bug 8115: sort numbers with commas (ascending)',
1024 [ 'Numbers' ], numbers, numbersAsc,
1025 function ( $table ) {
1026 $table.tablesorter();
1027 $table.find( '.headerSort:eq(0)' ).click();
1031 tableTest( 'bug 8115: sort numbers with commas (descending)',
1032 [ 'Numbers' ], numbers, reversed( numbersAsc ),
1033 function ( $table ) {
1034 $table.tablesorter();
1035 $table.find( '.headerSort:eq(0)' ).click().click();
1038 // TODO add numbers sorting tests for bug 8115 with a different language
1040 QUnit.test( 'bug 32888 - Tables inside a tableheader cell', 2, function ( assert ) {
1043 '<table class="sortable" id="mw-bug-32888">' +
1044 '<tr><th>header<table id="mw-bug-32888-2">' +
1045 '<tr><th>1</th><th>2</th></tr>' +
1046 '</table></th></tr>' +
1047 '<tr><td>A</td></tr>' +
1048 '<tr><td>B</td></tr>' +
1051 $table.tablesorter();
1054 $table.find( '> thead:eq(0) > tr > th.headerSort' ).length,
1056 'Child tables inside a headercell should not interfere with sortable headers (bug 32888)'
1059 $( '#mw-bug-32888-2' ).find( 'th.headerSort' ).length,
1061 'The headers of child tables inside a headercell should not be sortable themselves (bug 32888)'
1066 'Correct date sorting I',
1068 correctDateSorting1,
1069 correctDateSortingSorted1,
1070 function ( $table ) {
1071 mw.config.set( 'wgDefaultDateFormat', 'mdy' );
1073 $table.tablesorter();
1074 $table.find( '.headerSort:eq(0)' ).click();
1079 'Correct date sorting II',
1081 correctDateSorting2,
1082 correctDateSortingSorted2,
1083 function ( $table ) {
1084 mw.config.set( 'wgDefaultDateFormat', 'dmy' );
1086 $table.tablesorter();
1087 $table.find( '.headerSort:eq(0)' ).click();
1095 isoDateSortingSorted,
1096 function ( $table ) {
1097 mw.config.set( 'wgDefaultDateFormat', 'dmy' );
1099 $table.tablesorter();
1100 $table.find( '.headerSort:eq(0)' ).click();
1104 QUnit.test( 'Sorting images using alt text', 1, function ( assert ) {
1106 '<table class="sortable">' +
1107 '<tr><th>THEAD</th></tr>' +
1108 '<tr><td><img alt="2"/></td></tr>' +
1109 '<tr><td>1</td></tr>' +
1112 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
1115 $table.find( 'td' ).first().text(),
1117 'Applied correct sorting order'
1121 QUnit.test( 'Sorting images using alt text (complex)', 1, function ( assert ) {
1123 '<table class="sortable">' +
1124 '<tr><th>THEAD</th></tr>' +
1125 '<tr><td><img alt="D" />A</td></tr>' +
1126 '<tr><td>CC</td></tr>' +
1127 '<tr><td><a><img alt="A" /></a>F</tr>' +
1128 '<tr><td><img alt="A" /><strong>E</strong></tr>' +
1129 '<tr><td><strong><img alt="A" />D</strong></tr>' +
1130 '<tr><td><img alt="A" />C</tr>' +
1133 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
1136 $table.find( 'td' ).text(),
1138 'Applied correct sorting order'
1142 QUnit.test( 'Sorting images using alt text (with format autodetection)', 1, function ( assert ) {
1144 '<table class="sortable">' +
1145 '<tr><th>THEAD</th></tr>' +
1146 '<tr><td><img alt="1" />7</td></tr>' +
1147 '<tr><td>1<img alt="6" /></td></tr>' +
1148 '<tr><td>5</td></tr>' +
1149 '<tr><td>4</td></tr>' +
1152 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
1155 $table.find( 'td' ).text(),
1157 'Applied correct sorting order'
1161 QUnit.test( 'bug 38911 - The row with the largest amount of columns should receive the sort indicators', 3, function ( assert ) {
1163 '<table class="sortable">' +
1165 '<tr><th rowspan="2" id="A1">A1</th><th colspan="2">B2a</th></tr>' +
1166 '<tr><th id="B2b">B2b</th><th id="C2b">C2b</th></tr>' +
1168 '<tr><td>A</td><td>Aa</td><td>Ab</td></tr>' +
1169 '<tr><td>B</td><td>Ba</td><td>Bb</td></tr>' +
1172 $table.tablesorter();
1175 $table.find( '#A1' ).attr( 'class' ),
1177 'The first column of the first row should be sortable'
1180 $table.find( '#B2b' ).attr( 'class' ),
1182 'The th element of the 2nd row of the 2nd column should be sortable'
1185 $table.find( '#C2b' ).attr( 'class' ),
1187 'The th element of the 2nd row of the 3rd column should be sortable'
1191 QUnit.test( 'rowspans in table headers should prefer the last row when rows are equal in length', 2, function ( assert ) {
1193 '<table class="sortable">' +
1195 '<tr><th rowspan="2" id="A1">A1</th><th>B2a</th></tr>' +
1196 '<tr><th id="B2b">B2b</th></tr>' +
1198 '<tr><td>A</td><td>Aa</td></tr>' +
1199 '<tr><td>B</td><td>Ba</td></tr>' +
1202 $table.tablesorter();
1205 $table.find( '#A1' ).attr( 'class' ),
1207 'The first column of the first row should be sortable'
1210 $table.find( '#B2b' ).attr( 'class' ),
1212 'The th element of the 2nd row of the 2nd column should be sortable'
1216 QUnit.test( 'holes in the table headers should not throw JS errors', 2, function ( assert ) {
1218 '<table class="sortable">' +
1220 '<tr><th id="A1">A1</th><th>B1</th><th id="C1" rowspan="2">C1</th></tr>' +
1221 '<tr><th id="A2">A2</th></tr>' +
1223 '<tr><td>A</td><td>Aa</td><td>Aaa</td></tr>' +
1224 '<tr><td>B</td><td>Ba</td><td>Bbb</td></tr>' +
1227 $table.tablesorter();
1228 assert.equal( $table.find( '#A2' ).data( 'headerIndex' ),
1230 'A2 should not be a sort header'
1232 assert.equal( $table.find( '#C1' ).data( 'headerIndex' ),
1234 'C1 should be a sort header'
1239 QUnit.test( 'td cells in thead should not be taken into account for longest row calculation', 2, function ( assert ) {
1241 '<table class="sortable">' +
1243 '<tr><th id="A1">A1</th><th>B1</th><td id="C1">C1</td></tr>' +
1244 '<tr><th id="A2">A2</th><th>B2</th><th id="C2">C2</th></tr>' +
1248 $table.tablesorter();
1249 assert.equal( $table.find( '#C2' ).data( 'headerIndex' ),
1251 'C2 should be a sort header'
1253 assert.equal( $table.find( '#C1' ).data( 'headerIndex' ),
1255 'C1 should not be a sort header'
1259 // bug 41889 - exploding rowspans in more complex cases
1261 'Rowspan exploding with row headers',
1262 '<table class="sortable">' +
1263 '<thead><tr><th id="sortme">n</th><th>foo</th><th>bar</th><th>baz</th></tr></thead>' +
1265 '<tr><td>1</td><th rowspan="2">foo</th><td rowspan="2">bar</td><td>baz</td></tr>' +
1266 '<tr><td>2</td><td>baz</td></tr>' +
1269 [ '1', 'foo', 'bar', 'baz' ],
1270 [ '2', 'foo', 'bar', 'baz' ]
1274 // bug 53211 - exploding rowspans in more complex cases
1276 'Rowspan exploding with row headers and colspans', 1, function ( assert ) {
1277 var $table = $( '<table class="sortable">' +
1278 '<thead><tr><th rowspan="2">n</th><th colspan="2">foo</th><th rowspan="2">baz</th></tr>' +
1279 '<tr><th>foo</th><th>bar</th></tr></thead>' +
1281 '<tr><td>1</td><td>foo</td><td>bar</td><td>baz</td></tr>' +
1282 '<tr><td>2</td><td>foo</td><td>bar</td><td>baz</td></tr>' +
1283 '</tbody></table>' );
1285 $table.tablesorter();
1286 assert.equal( $table.find( 'tr:eq(1) th:eq(1)' ).data( 'headerIndex' ),
1288 'Incorrect index of sort header'
1294 'Rowspan exploding with colspanned cells',
1295 '<table class="sortable">' +
1296 '<thead><tr><th id="sortme">n</th><th>foo</th><th>bar</th><th>baz</th></tr></thead>' +
1298 '<tr><td>1</td><td>foo</td><td>bar</td><td rowspan="2">baz</td></tr>' +
1299 '<tr><td>2</td><td colspan="2">foobar</td></tr>' +
1302 [ '1', 'foo', 'bar', 'baz' ],
1303 [ '2', 'foobar', 'baz' ]
1308 'Rowspan exploding with colspanned cells (2)',
1309 '<table class="sortable">' +
1310 '<thead><tr><th>n</th><th>foo</th><th>bar</th><th>baz</th><th id="sortme">n2</th></tr></thead>' +
1312 '<tr><td>1</td><td>foo</td><td>bar</td><td rowspan="2">baz</td><td>2</td></tr>' +
1313 '<tr><td>2</td><td colspan="2">foobar</td><td>1</td></tr>' +
1316 [ '2', 'foobar', 'baz', '1' ],
1317 [ '1', 'foo', 'bar', 'baz', '2' ]
1322 'Rowspan exploding with rightmost rows spanning most',
1323 '<table class="sortable">' +
1324 '<thead><tr><th id="sortme">n</th><th>foo</th><th>bar</th></tr></thead>' +
1326 '<tr><td>1</td><td rowspan="2">foo</td><td rowspan="4">bar</td></tr>' +
1327 '<tr><td>2</td></tr>' +
1328 '<tr><td>3</td><td rowspan="2">foo</td></tr>' +
1329 '<tr><td>4</td></tr>' +
1332 [ '1', 'foo', 'bar' ],
1333 [ '2', 'foo', 'bar' ],
1334 [ '3', 'foo', 'bar' ],
1335 [ '4', 'foo', 'bar' ]
1340 'Rowspan exploding with rightmost rows spanning most (2)',
1341 '<table class="sortable">' +
1342 '<thead><tr><th id="sortme">n</th><th>foo</th><th>bar</th><th>baz</th></tr></thead>' +
1344 '<tr><td>1</td><td rowspan="2">foo</td><td rowspan="4">bar</td><td>baz</td></tr>' +
1345 '<tr><td>2</td><td>baz</td></tr>' +
1346 '<tr><td>3</td><td rowspan="2">foo</td><td>baz</td></tr>' +
1347 '<tr><td>4</td><td>baz</td></tr>' +
1350 [ '1', 'foo', 'bar', 'baz' ],
1351 [ '2', 'foo', 'bar', 'baz' ],
1352 [ '3', 'foo', 'bar', 'baz' ],
1353 [ '4', 'foo', 'bar', 'baz' ]
1358 'Rowspan exploding with row-and-colspanned cells',
1359 '<table class="sortable">' +
1360 '<thead><tr><th id="sortme">n</th><th>foo1</th><th>foo2</th><th>bar</th><th>baz</th></tr></thead>' +
1362 '<tr><td>1</td><td rowspan="2">foo1</td><td rowspan="2">foo2</td><td rowspan="4">bar</td><td>baz</td></tr>' +
1363 '<tr><td>2</td><td>baz</td></tr>' +
1364 '<tr><td>3</td><td colspan="2" rowspan="2">foo</td><td>baz</td></tr>' +
1365 '<tr><td>4</td><td>baz</td></tr>' +
1368 [ '1', 'foo1', 'foo2', 'bar', 'baz' ],
1369 [ '2', 'foo1', 'foo2', 'bar', 'baz' ],
1370 [ '3', 'foo', 'bar', 'baz' ],
1371 [ '4', 'foo', 'bar', 'baz' ]
1376 'Rowspan exploding with uneven rowspan layout',
1377 '<table class="sortable">' +
1378 '<thead><tr><th id="sortme">n</th><th>foo1</th><th>foo2</th><th>foo3</th><th>bar</th><th>baz</th></tr></thead>' +
1380 '<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>' +
1381 '<tr><td>2</td><td rowspan="3">bar</td><td>baz</td></tr>' +
1382 '<tr><td>3</td><td rowspan="2">foo1</td><td rowspan="2">foo2</td><td rowspan="2">foo3</td><td>baz</td></tr>' +
1383 '<tr><td>4</td><td>baz</td></tr>' +
1386 [ '1', 'foo1', 'foo2', 'foo3', 'bar', 'baz' ],
1387 [ '2', 'foo1', 'foo2', 'foo3', 'bar', 'baz' ],
1388 [ '3', 'foo1', 'foo2', 'foo3', 'bar', 'baz' ],
1389 [ '4', 'foo1', 'foo2', 'foo3', 'bar', 'baz' ]
1393 QUnit.test( 'bug 105731 - incomplete rows in table body', 3, function ( assert ) {
1394 var $table, parsers;
1396 '<table class="sortable">' +
1397 '<tr><th>A</th><th>B</th></tr>' +
1398 '<tr><td>3</td></tr>' +
1399 '<tr><td>1</td><td>2</td></tr>' +
1402 $table.tablesorter();
1403 $table.find( '.headerSort:eq(0)' ).click();
1404 // now the first row have 2 columns
1405 $table.find( '.headerSort:eq(1)' ).click();
1407 parsers = $table.data( 'tablesorter' ).config.parsers;
1412 'detectParserForColumn() detect 2 parsers'
1418 'detectParserForColumn() detect parser.id "number" for second column'
1422 parsers[ 1 ].format( $table.find( 'tbody > tr > td:eq(1)' ).text() ),
1424 'empty cell is sorted as number 0'
1428 QUnit.test( 'bug T114721 - use of expand-child class', 2, function ( assert ) {
1429 var $table, parsers;
1431 '<table class="sortable">' +
1432 '<tr><th>A</th><th>B</th></tr>' +
1433 '<tr><td>b</td><td>4</td></tr>' +
1434 '<tr class="expand-child"><td colspan="2">some text follow b</td></tr>' +
1435 '<tr><td>a</td><td>2</td></tr>' +
1436 '<tr class="expand-child"><td colspan="2">some text follow a</td></tr>' +
1437 '<tr class="expand-child"><td colspan="2">more text</td></tr>' +
1440 $table.tablesorter();
1441 $table.find( '.headerSort:eq(0)' ).click();
1444 tableExtract( $table ),
1447 [ 'some text follow a' ],
1450 [ 'some text follow b' ]
1452 'row with expand-child class follow above row'
1455 parsers = $table.data( 'tablesorter' ).config.parsers;
1459 'detectParserForColumn() detect parser.id "number" for second column'
1463 }( jQuery, mediaWiki ) );