11 simple = [a2, b3, a1, a3, b2, b1],
12 simpleAsc = [a1, a2, a3, b1, b2, b3],
13 simpleDescasc = [b1, b2, b3, a1, a2, a3],
16 aaa1 = [ 'A', 'A', 'A', '1' ],
17 aab5 = [ 'A', 'A', 'B', '5' ],
18 abc3 = [ 'A', 'B', 'C', '3' ],
19 bbc2 = [ 'B', 'B', 'C', '2' ],
20 caa4 = [ 'C', 'A', 'A', '4' ],
21 colspanInitial = [ aab5, aaa1, abc3, bbc2, caa4 ],
24 mercury = [ 'Mercury', '2439.7' ],
25 venus = [ 'Venus', '6051.8' ],
26 earth = [ 'Earth', '6371.0' ],
27 mars = [ 'Mars', '3390.0' ],
28 jupiter = [ 'Jupiter', '69911' ],
29 saturn = [ 'Saturn', '58232' ],
30 planets = [mercury, venus, earth, mars, jupiter, saturn],
31 planetsAscName = [earth, jupiter, mars, mercury, saturn, venus],
32 planetsAscRadius = [mercury, mars, venus, earth, saturn, jupiter],
39 // Some randomly generated fake IPs
50 // Sort order should go octet by octet
114 // Comma's sort after dots
115 // Not intentional but test to detect changes
136 correctDateSorting1 = [
138 ['05 February 2010'],
141 correctDateSortingSorted1 = [
147 correctDateSorting2 = [
149 ['February 05 2010'],
152 correctDateSortingSorted2 = [
158 QUnit.module( 'jquery.tablesorter', QUnit.newMwEnvironment( {
160 wgMonthNames: ['', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
161 wgMonthNamesShort: ['', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
162 wgDefaultDateFormat: 'dmy',
163 wgSeparatorTransformTable: ['', ''],
164 wgDigitTransformTable: ['', ''],
165 wgContentLanguage: 'en'
170 * Create an HTML table from an array of row arrays containing text strings.
171 * First row will be header row. No fancy rowspan/colspan stuff.
173 * @param {String[]} header
174 * @param {String[][]} data
177 function tableCreate( header, data ) {
179 $table = $( '<table class="sortable"><thead></thead><tbody></tbody></table>' ),
180 $thead = $table.find( 'thead' ),
181 $tbody = $table.find( 'tbody' ),
184 $.each( header, function ( i, str ) {
185 var $th = $( '<th>' );
186 $th.text( str ).appendTo( $tr );
188 $tr.appendTo( $thead );
190 for ( i = 0; i < data.length; i++ ) {
191 /*jshint loopfunc: true */
193 $.each( data[i], function ( j, str ) {
194 var $td = $( '<td>' );
195 $td.text( str ).appendTo( $tr );
197 $tr.appendTo( $tbody );
203 * Extract text from table.
205 * @param {jQuery} $table
208 function tableExtract( $table ) {
211 $table.find( 'tbody' ).find( 'tr' ).each( function ( i, tr ) {
213 $( tr ).find( 'td,th' ).each( function ( i, td ) {
214 row.push( $( td ).text() );
222 * Run a table test by building a table with the given data,
223 * running some callback on it, then checking the results.
225 * @param {String} msg text to pass on to qunit for the comparison
226 * @param {String[]} header cols to make the table
227 * @param {String[][]} data rows/cols to make the table
228 * @param {String[][]} expected rows/cols to compare against at end
229 * @param {function($table)} callback something to do with the table before we compare
231 function tableTest( msg, header, data, expected, callback ) {
232 QUnit.test( msg, 1, function ( assert ) {
234 $table = tableCreate( header, data );
236 // Give caller a chance to set up sorting and manipulate the table.
239 // Table sorting is done synchronously; if it ever needs to change back
240 // to asynchronous, we'll need a timeout or a callback here.
241 extracted = tableExtract( $table );
242 assert.deepEqual( extracted, expected, msg );
247 * Run a table test by building a table with the given HTML,
248 * running some callback on it, then checking the results.
250 * @param {String} msg text to pass on to qunit for the comparison
251 * @param {String} HTML to make the table
252 * @param {String[][]} expected rows/cols to compare against at end
253 * @param {function($table)} callback something to do with the table before we compare
255 function tableTestHTML( msg, html, expected, callback ) {
256 QUnit.test( msg, 1, function ( assert ) {
260 // Give caller a chance to set up sorting and manipulate the table.
264 $table.tablesorter();
265 $table.find( '#sortme' ).click();
268 // Table sorting is done synchronously; if it ever needs to change back
269 // to asynchronous, we'll need a timeout or a callback here.
270 extracted = tableExtract( $table );
271 assert.deepEqual( extracted, expected, msg );
275 function reversed( arr ) {
277 var arr2 = arr.slice( 0 );
284 // Sample data set using planets named and their radius
285 header = [ 'Planet', 'Radius (km)'];
288 'Basic planet table: sorting initially - ascending by name',
292 function ( $table ) {
293 $table.tablesorter( { sortList: [
299 'Basic planet table: sorting initially - descending by radius',
302 reversed( planetsAscRadius ),
303 function ( $table ) {
304 $table.tablesorter( { sortList: [
310 'Basic planet table: ascending by name',
314 function ( $table ) {
315 $table.tablesorter();
316 $table.find( '.headerSort:eq(0)' ).click();
320 'Basic planet table: ascending by name a second time',
324 function ( $table ) {
325 $table.tablesorter();
326 $table.find( '.headerSort:eq(0)' ).click();
330 'Basic planet table: ascending by name (multiple clicks)',
334 function ( $table ) {
335 $table.tablesorter();
336 $table.find( '.headerSort:eq(0)' ).click();
337 $table.find( '.headerSort:eq(1)' ).click();
338 $table.find( '.headerSort:eq(0)' ).click();
342 'Basic planet table: descending by name',
345 reversed( planetsAscName ),
346 function ( $table ) {
347 $table.tablesorter();
348 $table.find( '.headerSort:eq(0)' ).click().click();
352 'Basic planet table: ascending radius',
356 function ( $table ) {
357 $table.tablesorter();
358 $table.find( '.headerSort:eq(1)' ).click();
362 'Basic planet table: descending radius',
365 reversed( planetsAscRadius ),
366 function ( $table ) {
367 $table.tablesorter();
368 $table.find( '.headerSort:eq(1)' ).click().click();
372 header = [ 'column1', 'column2' ];
375 'Sorting multiple columns by passing sort list',
379 function ( $table ) {
389 'Sorting multiple columns by programmatically triggering sort()',
393 function ( $table ) {
394 $table.tablesorter();
395 $table.data( 'tablesorter' ).sort(
404 'Reset to initial sorting by triggering sort() without any parameters',
408 function ( $table ) {
415 $table.data( 'tablesorter' ).sort(
421 $table.data( 'tablesorter' ).sort();
425 'Sort via click event after having initialized the tablesorter with initial sorting',
429 function ( $table ) {
431 { sortList: [ { 0: 'asc' }, { 1: 'asc' } ] }
433 $table.find( '.headerSort:eq(0)' ).click();
437 'Multi-sort via click event after having initialized the tablesorter with initial sorting',
441 function ( $table ) {
443 { sortList: [ { 0: 'desc' }, { 1: 'desc' } ] }
445 $table.find( '.headerSort:eq(0)' ).click();
447 // Pretend to click while pressing the multi-sort key
448 var event = $.Event( 'click' );
449 event[$table.data( 'tablesorter' ).config.sortMultiSortKey] = true;
450 $table.find( '.headerSort:eq(1)' ).trigger( event );
453 QUnit.test( 'Reset sorting making table appear unsorted', 3, function ( assert ) {
454 var $table = tableCreate( header, simple );
461 $table.data( 'tablesorter' ).sort( [] );
464 $table.find( 'th.headerSortUp' ).length + $table.find( 'th.headerSortDown' ).length,
466 'No sort specific sort classes addign to header cells'
470 $table.find( 'th' ).first().attr( 'title' ),
471 mw.msg( 'sort-ascending' ),
472 'First header cell has default title'
476 $table.find( 'th' ).first().attr( 'title' ),
477 $table.find( 'th' ).last().attr( 'title' ),
478 'Both header cells\' titles match'
482 // Sorting with colspans
483 header = [ 'column1a', 'column1b', 'column1c', 'column2' ];
485 tableTest( 'Sorting with colspanned headers: spanned column',
488 [ aaa1, aab5, abc3, bbc2, caa4 ],
489 function ( $table ) {
490 // Make colspanned header for test
491 $table.find( 'tr:eq(0) th:eq(1), tr:eq(0) th:eq(2)' ).remove();
492 $table.find( 'tr:eq(0) th:eq(0)' ).attr( 'colspan', '3' );
494 $table.tablesorter();
495 $table.find( '.headerSort:eq(0)' ).click();
498 tableTest( 'Sorting with colspanned headers: sort spanned column twice',
501 [ caa4, bbc2, abc3, aab5, aaa1 ],
502 function ( $table ) {
503 // Make colspanned header for test
504 $table.find( 'tr:eq(0) th:eq(1), tr:eq(0) th:eq(2)' ).remove();
505 $table.find( 'tr:eq(0) th:eq(0)' ).attr( 'colspan', '3' );
507 $table.tablesorter();
508 $table.find( '.headerSort:eq(0)' ).click();
509 $table.find( '.headerSort:eq(0)' ).click();
512 tableTest( 'Sorting with colspanned headers: subsequent column',
515 [ aaa1, bbc2, abc3, caa4, aab5 ],
516 function ( $table ) {
517 // Make colspanned header for test
518 $table.find( 'tr:eq(0) th:eq(1), tr:eq(0) th:eq(2)' ).remove();
519 $table.find( 'tr:eq(0) th:eq(0)' ).attr( 'colspan', '3' );
521 $table.tablesorter();
522 $table.find( '.headerSort:eq(1)' ).click();
525 tableTest( 'Sorting with colspanned headers: sort subsequent column twice',
528 [ aab5, caa4, abc3, bbc2, aaa1 ],
529 function ( $table ) {
530 // Make colspanned header for test
531 $table.find( 'tr:eq(0) th:eq(1), tr:eq(0) th:eq(2)' ).remove();
532 $table.find( 'tr:eq(0) th:eq(0)' ).attr( 'colspan', '3' );
534 $table.tablesorter();
535 $table.find( '.headerSort:eq(1)' ).click();
536 $table.find( '.headerSort:eq(1)' ).click();
541 'Basic planet table: one unsortable column',
545 function ( $table ) {
546 $table.find( 'tr:eq(0) > th:eq(0)' ).addClass( 'unsortable' );
548 $table.tablesorter();
549 $table.find( 'tr:eq(0) > th:eq(0)' ).click();
555 'Bug 28775: German-style (dmy) short numeric dates',
558 // German-style dates are day-month-year
566 // Sorted by ascending date
573 function ( $table ) {
574 mw.config.set( 'wgDefaultDateFormat', 'dmy' );
575 mw.config.set( 'wgContentLanguage', 'de' );
577 $table.tablesorter();
578 $table.find( '.headerSort:eq(0)' ).click();
583 'Bug 28775: American-style (mdy) short numeric dates',
586 // American-style dates are month-day-year
594 // Sorted by ascending date
601 function ( $table ) {
602 mw.config.set( 'wgDefaultDateFormat', 'mdy' );
604 $table.tablesorter();
605 $table.find( '.headerSort:eq(0)' ).click();
610 'Bug 17141: IPv4 address sorting',
614 function ( $table ) {
615 $table.tablesorter();
616 $table.find( '.headerSort:eq(0)' ).click();
620 'Bug 17141: IPv4 address sorting (reverse)',
623 reversed( ipv4Sorted ),
624 function ( $table ) {
625 $table.tablesorter();
626 $table.find( '.headerSort:eq(0)' ).click().click();
631 'Accented Characters with custom collation',
635 function ( $table ) {
636 mw.config.set( 'tableSorterCollation', {
643 $table.tablesorter();
644 $table.find( '.headerSort:eq(0)' ).click();
648 QUnit.test( 'Rowspan not exploded on init', 1, function ( assert ) {
649 var $table = tableCreate( header, planets );
651 // Modify the table to have a multiple-row-spanning cell:
652 // - Remove 2nd cell of 4th row, and, 2nd cell or 5th row.
653 $table.find( 'tr:eq(3) td:eq(1), tr:eq(4) td:eq(1)' ).remove();
654 // - Set rowspan for 2nd cell of 3rd row to 3.
655 // This covers the removed cell in the 4th and 5th row.
656 $table.find( 'tr:eq(2) td:eq(1)' ).attr( 'rowspan', '3' );
658 $table.tablesorter();
661 $table.find( 'tr:eq(2) td:eq(1)' ).prop( 'rowSpan' ),
663 'Rowspan not exploded'
668 [ 'Earth', '6051.8' ],
670 [ 'Mars', '6051.8' ],
675 planetsRowspanII = [ jupiter, mercury, saturn, venus, [ 'Venus', '6371.0' ], [ 'Venus', '3390.0' ] ];
678 'Basic planet table: same value for multiple rows via rowspan',
682 function ( $table ) {
683 // Modify the table to have a multiple-row-spanning cell:
684 // - Remove 2nd cell of 4th row, and, 2nd cell or 5th row.
685 $table.find( 'tr:eq(3) td:eq(1), tr:eq(4) td:eq(1)' ).remove();
686 // - Set rowspan for 2nd cell of 3rd row to 3.
687 // This covers the removed cell in the 4th and 5th row.
688 $table.find( 'tr:eq(2) td:eq(1)' ).attr( 'rowspan', '3' );
690 $table.tablesorter();
691 $table.find( '.headerSort:eq(0)' ).click();
695 'Basic planet table: same value for multiple rows via rowspan (sorting initially)',
699 function ( $table ) {
700 // Modify the table to have a multiple-row-spanning cell:
701 // - Remove 2nd cell of 4th row, and, 2nd cell or 5th row.
702 $table.find( 'tr:eq(3) td:eq(1), tr:eq(4) td:eq(1)' ).remove();
703 // - Set rowspan for 2nd cell of 3rd row to 3.
704 // This covers the removed cell in the 4th and 5th row.
705 $table.find( 'tr:eq(2) td:eq(1)' ).attr( 'rowspan', '3' );
707 $table.tablesorter( { sortList: [
713 'Basic planet table: Same value for multiple rows via rowspan II',
717 function ( $table ) {
718 // Modify the table to have a multiple-row-spanning cell:
719 // - Remove 1st cell of 4th row, and, 1st cell or 5th row.
720 $table.find( 'tr:eq(3) td:eq(0), tr:eq(4) td:eq(0)' ).remove();
721 // - Set rowspan for 1st cell of 3rd row to 3.
722 // This covers the removed cell in the 4th and 5th row.
723 $table.find( 'tr:eq(2) td:eq(0)' ).attr( 'rowspan', '3' );
725 $table.tablesorter();
726 $table.find( '.headerSort:eq(0)' ).click();
731 'Complex date parsing I',
735 function ( $table ) {
736 mw.config.set( 'wgDefaultDateFormat', 'mdy' );
738 $table.tablesorter();
739 $table.find( '.headerSort:eq(0)' ).click();
744 'Currency parsing I',
748 function ( $table ) {
749 $table.tablesorter();
750 $table.find( '.headerSort:eq(0)' ).click();
754 planetsAscNameLegacy = planetsAscName.slice( 0 );
755 planetsAscNameLegacy[4] = planetsAscNameLegacy[5];
756 planetsAscNameLegacy.pop();
759 'Legacy compat with .sortbottom',
762 planetsAscNameLegacy,
763 function ( $table ) {
764 $table.find( 'tr:last' ).addClass( 'sortbottom' );
765 $table.tablesorter();
766 $table.find( '.headerSort:eq(0)' ).click();
770 QUnit.test( 'Test detection routine', 1, function ( assert ) {
773 '<table class="sortable">' +
774 '<caption>CAPTION</caption>' +
775 '<tr><th>THEAD</th></tr>' +
776 '<tr><td>1</td></tr>' +
777 '<tr class="sortbottom"><td>text</td></tr>' +
780 $table.tablesorter();
781 $table.find( '.headerSort:eq(0)' ).click();
784 $table.data( 'tablesorter' ).config.parsers[0].id,
786 'Correctly detected column content skipping sortbottom'
790 /** FIXME: the diff output is not very readeable. */
791 QUnit.test( 'bug 32047 - caption must be before thead', 1, function ( assert ) {
794 '<table class="sortable">' +
795 '<caption>CAPTION</caption>' +
796 '<tr><th>THEAD</th></tr>' +
797 '<tr><td>A</td></tr>' +
798 '<tr><td>B</td></tr>' +
799 '<tr class="sortbottom"><td>TFOOT</td></tr>' +
802 $table.tablesorter();
805 $table.children().get( 0 ).nodeName,
807 'First element after <thead> must be <caption> (bug 32047)'
811 QUnit.test( 'data-sort-value attribute, when available, should override sorting position', 3, function ( assert ) {
814 // Example 1: All cells except one cell without data-sort-value,
815 // which should be sorted at it's text content value.
817 '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
819 '<tr><td>Cheetah</td></tr>' +
820 '<tr><td data-sort-value="Apple">Bird</td></tr>' +
821 '<tr><td data-sort-value="Bananna">Ferret</td></tr>' +
822 '<tr><td data-sort-value="Drupe">Elephant</td></tr>' +
823 '<tr><td data-sort-value="Cherry">Dolphin</td></tr>' +
826 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
829 $table.find( 'tbody > tr' ).each( function ( i, tr ) {
830 $( tr ).find( 'td' ).each( function ( i, td ) {
832 data: $( td ).data( 'sortValue' ),
838 assert.deepEqual( data, [
859 ], 'Order matches expected order (based on data-sort-value attribute values)' );
863 '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
865 '<tr><td>D</td></tr>' +
866 '<tr><td data-sort-value="E">A</td></tr>' +
867 '<tr><td>B</td></tr>' +
868 '<tr><td>G</td></tr>' +
869 '<tr><td data-sort-value="F">C</td></tr>' +
872 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
875 $table.find( 'tbody > tr' ).each( function ( i, tr ) {
876 $( tr ).find( 'td' ).each( function ( i, td ) {
878 data: $( td ).data( 'sortValue' ),
884 assert.deepEqual( data, [
905 ], 'Order matches expected order (based on data-sort-value attribute values)' );
907 // Example 3: Test that live changes are used from data-sort-value,
908 // even if they change after the tablesorter is constructed (bug 38152).
910 '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
912 '<tr><td>D</td></tr>' +
913 '<tr><td data-sort-value="1">A</td></tr>' +
914 '<tr><td>B</td></tr>' +
915 '<tr><td data-sort-value="2">G</td></tr>' +
916 '<tr><td>C</td></tr>' +
919 // initialize table sorter and sort once
922 .find( '.headerSort:eq(0)' ).click();
924 // Change the sortValue data properties (bug 38152)
926 $table.find( 'td:contains(A)' ).data( 'sortValue', 3 );
928 $table.find( 'td:contains(B)' ).data( 'sortValue', 1 );
929 // - remove data, bring back attribute: 2
930 $table.find( 'td:contains(G)' ).removeData( 'sortValue' );
932 // Now sort again (twice, so it is back at Ascending)
933 $table.find( '.headerSort:eq(0)' ).click();
934 $table.find( '.headerSort:eq(0)' ).click();
937 $table.find( 'tbody > tr' ).each( function ( i, tr ) {
938 $( tr ).find( 'td' ).each( function ( i, td ) {
940 data: $( td ).data( 'sortValue' ),
946 assert.deepEqual( data, [
967 ], 'Order matches expected order, using the current sortValue in $.data()' );
971 tableTest( 'bug 8115: sort numbers with commas (ascending)',
972 ['Numbers'], numbers, numbersAsc,
973 function ( $table ) {
974 $table.tablesorter();
975 $table.find( '.headerSort:eq(0)' ).click();
979 tableTest( 'bug 8115: sort numbers with commas (descending)',
980 ['Numbers'], numbers, reversed( numbersAsc ),
981 function ( $table ) {
982 $table.tablesorter();
983 $table.find( '.headerSort:eq(0)' ).click().click();
986 // TODO add numbers sorting tests for bug 8115 with a different language
988 QUnit.test( 'bug 32888 - Tables inside a tableheader cell', 2, function ( assert ) {
991 '<table class="sortable" id="mw-bug-32888">' +
992 '<tr><th>header<table id="mw-bug-32888-2">' +
993 '<tr><th>1</th><th>2</th></tr>' +
994 '</table></th></tr>' +
995 '<tr><td>A</td></tr>' +
996 '<tr><td>B</td></tr>' +
999 $table.tablesorter();
1002 $table.find( '> thead:eq(0) > tr > th.headerSort' ).length,
1004 'Child tables inside a headercell should not interfere with sortable headers (bug 32888)'
1007 $( '#mw-bug-32888-2' ).find( 'th.headerSort' ).length,
1009 'The headers of child tables inside a headercell should not be sortable themselves (bug 32888)'
1014 'Correct date sorting I',
1016 correctDateSorting1,
1017 correctDateSortingSorted1,
1018 function ( $table ) {
1019 mw.config.set( 'wgDefaultDateFormat', 'mdy' );
1021 $table.tablesorter();
1022 $table.find( '.headerSort:eq(0)' ).click();
1027 'Correct date sorting II',
1029 correctDateSorting2,
1030 correctDateSortingSorted2,
1031 function ( $table ) {
1032 mw.config.set( 'wgDefaultDateFormat', 'dmy' );
1034 $table.tablesorter();
1035 $table.find( '.headerSort:eq(0)' ).click();
1039 QUnit.test( 'Sorting images using alt text', 1, function ( assert ) {
1041 '<table class="sortable">' +
1042 '<tr><th>THEAD</th></tr>' +
1043 '<tr><td><img alt="2"/></td></tr>' +
1044 '<tr><td>1</td></tr>' +
1047 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
1050 $table.find( 'td' ).first().text(),
1052 'Applied correct sorting order'
1056 QUnit.test( 'Sorting images using alt text (complex)', 1, function ( assert ) {
1058 '<table class="sortable">' +
1059 '<tr><th>THEAD</th></tr>' +
1060 '<tr><td><img alt="D" />A</td></tr>' +
1061 '<tr><td>CC</td></tr>' +
1062 '<tr><td><a><img alt="A" /></a>F</tr>' +
1063 '<tr><td><img alt="A" /><strong>E</strong></tr>' +
1064 '<tr><td><strong><img alt="A" />D</strong></tr>' +
1065 '<tr><td><img alt="A" />C</tr>' +
1068 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
1071 $table.find( 'td' ).text(),
1073 'Applied correct sorting order'
1077 QUnit.test( 'Sorting images using alt text (with format autodetection)', 1, function ( assert ) {
1079 '<table class="sortable">' +
1080 '<tr><th>THEAD</th></tr>' +
1081 '<tr><td><img alt="1" />7</td></tr>' +
1082 '<tr><td>1<img alt="6" /></td></tr>' +
1083 '<tr><td>5</td></tr>' +
1084 '<tr><td>4</td></tr>' +
1087 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
1090 $table.find( 'td' ).text(),
1092 'Applied correct sorting order'
1096 QUnit.test( 'bug 38911 - The row with the largest amount of columns should receive the sort indicators', 3, function ( assert ) {
1098 '<table class="sortable">' +
1100 '<tr><th rowspan="2" id="A1">A1</th><th colspan="2">B2a</th></tr>' +
1101 '<tr><th id="B2b">B2b</th><th id="C2b">C2b</th></tr>' +
1103 '<tr><td>A</td><td>Aa</td><td>Ab</td></tr>' +
1104 '<tr><td>B</td><td>Ba</td><td>Bb</td></tr>' +
1107 $table.tablesorter();
1110 $table.find( '#A1' ).attr( 'class' ),
1112 'The first column of the first row should be sortable'
1115 $table.find( '#B2b' ).attr( 'class' ),
1117 'The th element of the 2nd row of the 2nd column should be sortable'
1120 $table.find( '#C2b' ).attr( 'class' ),
1122 'The th element of the 2nd row of the 3rd column should be sortable'
1126 QUnit.test( 'rowspans in table headers should prefer the last row when rows are equal in length', 2, function ( assert ) {
1128 '<table class="sortable">' +
1130 '<tr><th rowspan="2" id="A1">A1</th><th>B2a</th></tr>' +
1131 '<tr><th id="B2b">B2b</th></tr>' +
1133 '<tr><td>A</td><td>Aa</td></tr>' +
1134 '<tr><td>B</td><td>Ba</td></tr>' +
1137 $table.tablesorter();
1140 $table.find( '#A1' ).attr( 'class' ),
1142 'The first column of the first row should be sortable'
1145 $table.find( '#B2b' ).attr( 'class' ),
1147 'The th element of the 2nd row of the 2nd column should be sortable'
1151 QUnit.test( 'holes in the table headers should not throw JS errors', 2, function ( assert ) {
1153 '<table class="sortable">' +
1155 '<tr><th id="A1">A1</th><th>B1</th><th id="C1" rowspan="2">C1</th></tr>' +
1156 '<tr><th id="A2">A2</th></tr>' +
1158 '<tr><td>A</td><td>Aa</td><td>Aaa</td></tr>' +
1159 '<tr><td>B</td><td>Ba</td><td>Bbb</td></tr>' +
1162 $table.tablesorter();
1163 assert.equal( $table.find( '#A2' ).data( 'headerIndex' ),
1165 'A2 should not be a sort header'
1167 assert.equal( $table.find( '#C1' ).data( 'headerIndex' ),
1169 'C1 should be a sort header'
1174 QUnit.test( 'td cells in thead should not be taken into account for longest row calculation', 2, function ( assert ) {
1176 '<table class="sortable">' +
1178 '<tr><th id="A1">A1</th><th>B1</th><td id="C1">C1</td></tr>' +
1179 '<tr><th id="A2">A2</th><th>B2</th><th id="C2">C2</th></tr>' +
1183 $table.tablesorter();
1184 assert.equal( $table.find( '#C2' ).data( 'headerIndex' ),
1186 'C2 should be a sort header'
1188 assert.equal( $table.find( '#C1' ).data( 'headerIndex' ),
1190 'C1 should not be a sort header'
1194 // bug 41889 - exploding rowspans in more complex cases
1196 'Rowspan exploding with row headers',
1197 '<table class="sortable">' +
1198 '<thead><tr><th id="sortme">n</th><th>foo</th><th>bar</th><th>baz</th></tr></thead>' +
1200 '<tr><td>1</td><th rowspan="2">foo</th><td rowspan="2">bar</td><td>baz</td></tr>' +
1201 '<tr><td>2</td><td>baz</td></tr>' +
1204 [ '1', 'foo', 'bar', 'baz' ],
1205 [ '2', 'foo', 'bar', 'baz' ]
1209 // bug 53211 - exploding rowspans in more complex cases
1211 'Rowspan exploding with row headers and colspans', 1, function ( assert ) {
1212 var $table = $( '<table class="sortable">' +
1213 '<thead><tr><th rowspan="2">n</th><th colspan="2">foo</th><th rowspan="2">baz</th></tr>' +
1214 '<tr><th>foo</th><th>bar</th></tr></thead>' +
1216 '<tr><td>1</td><td>foo</td><td>bar</td><td>baz</td></tr>' +
1217 '<tr><td>2</td><td>foo</td><td>bar</td><td>baz</td></tr>' +
1218 '</tbody></table>' );
1220 $table.tablesorter();
1221 assert.equal( $table.find( 'tr:eq(1) th:eq(1)').data('headerIndex'),
1223 'Incorrect index of sort header'
1229 'Rowspan exploding with colspanned cells',
1230 '<table class="sortable">' +
1231 '<thead><tr><th id="sortme">n</th><th>foo</th><th>bar</th><th>baz</th></tr></thead>' +
1233 '<tr><td>1</td><td>foo</td><td>bar</td><td rowspan="2">baz</td></tr>' +
1234 '<tr><td>2</td><td colspan="2">foobar</td></tr>' +
1237 [ '1', 'foo', 'bar', 'baz' ],
1238 [ '2', 'foobar', 'baz' ]
1243 'Rowspan exploding with colspanned cells (2)',
1244 '<table class="sortable">' +
1245 '<thead><tr><th id="sortme">n</th><th>foo</th><th>bar</th><th>baz</th><th>quux</th></tr></thead>' +
1247 '<tr><td>1</td><td>foo</td><td>bar</td><td rowspan="2">baz</td><td>quux</td></tr>' +
1248 '<tr><td>2</td><td colspan="2">foobar</td><td>quux</td></tr>' +
1251 [ '1', 'foo', 'bar', 'baz', 'quux' ],
1252 [ '2', 'foobar', 'baz', 'quux' ]
1257 'Rowspan exploding with rightmost rows spanning most',
1258 '<table class="sortable">' +
1259 '<thead><tr><th id="sortme">n</th><th>foo</th><th>bar</th></tr></thead>' +
1261 '<tr><td>1</td><td rowspan="2">foo</td><td rowspan="4">bar</td></tr>' +
1262 '<tr><td>2</td></tr>' +
1263 '<tr><td>3</td><td rowspan="2">foo</td></tr>' +
1264 '<tr><td>4</td></tr>' +
1267 [ '1', 'foo', 'bar' ],
1268 [ '2', 'foo', 'bar' ],
1269 [ '3', 'foo', 'bar' ],
1270 [ '4', 'foo', 'bar' ]
1275 'Rowspan exploding with rightmost rows spanning most (2)',
1276 '<table class="sortable">' +
1277 '<thead><tr><th id="sortme">n</th><th>foo</th><th>bar</th><th>baz</th></tr></thead>' +
1279 '<tr><td>1</td><td rowspan="2">foo</td><td rowspan="4">bar</td><td>baz</td></tr>' +
1280 '<tr><td>2</td><td>baz</td></tr>' +
1281 '<tr><td>3</td><td rowspan="2">foo</td><td>baz</td></tr>' +
1282 '<tr><td>4</td><td>baz</td></tr>' +
1285 [ '1', 'foo', 'bar', 'baz' ],
1286 [ '2', 'foo', 'bar', 'baz' ],
1287 [ '3', 'foo', 'bar', 'baz' ],
1288 [ '4', 'foo', 'bar', 'baz' ]
1293 'Rowspan exploding with row-and-colspanned cells',
1294 '<table class="sortable">' +
1295 '<thead><tr><th id="sortme">n</th><th>foo1</th><th>foo2</th><th>bar</th><th>baz</th></tr></thead>' +
1297 '<tr><td>1</td><td rowspan="2">foo1</td><td rowspan="2">foo2</td><td rowspan="4">bar</td><td>baz</td></tr>' +
1298 '<tr><td>2</td><td>baz</td></tr>' +
1299 '<tr><td>3</td><td colspan="2" rowspan="2">foo</td><td>baz</td></tr>' +
1300 '<tr><td>4</td><td>baz</td></tr>' +
1303 [ '1', 'foo1', 'foo2', 'bar', 'baz' ],
1304 [ '2', 'foo1', 'foo2', 'bar', 'baz' ],
1305 [ '3', 'foo', 'bar', 'baz' ],
1306 [ '4', 'foo', 'bar', 'baz' ]
1311 'Rowspan exploding with uneven rowspan layout',
1312 '<table class="sortable">' +
1313 '<thead><tr><th id="sortme">n</th><th>foo1</th><th>foo2</th><th>foo3</th><th>bar</th><th>baz</th></tr></thead>' +
1315 '<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>' +
1316 '<tr><td>2</td><td rowspan="3">bar</td><td>baz</td></tr>' +
1317 '<tr><td>3</td><td rowspan="2">foo1</td><td rowspan="2">foo2</td><td rowspan="2">foo3</td><td>baz</td></tr>' +
1318 '<tr><td>4</td><td>baz</td></tr>' +
1321 [ '1', 'foo1', 'foo2', 'foo3', 'bar', 'baz' ],
1322 [ '2', 'foo1', 'foo2', 'foo3', 'bar', 'baz' ],
1323 [ '3', 'foo1', 'foo2', 'foo3', 'bar', 'baz' ],
1324 [ '4', 'foo1', 'foo2', 'foo3', 'bar', 'baz' ]
1328 }( jQuery, mediaWiki ) );