2 /*jshint onevar: false */
5 wgMonthNames: ['', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
6 wgMonthNamesShort: ['', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
7 wgDefaultDateFormat: 'dmy',
8 wgSeparatorTransformTable: ['', ''],
9 wgDigitTransformTable: ['', ''],
10 wgContentLanguage: 'en'
13 QUnit.module( 'jquery.tablesorter', QUnit.newMwEnvironment( { config: config } ) );
16 * Create an HTML table from an array of row arrays containing text strings.
17 * First row will be header row. No fancy rowspan/colspan stuff.
19 * @param {String[]} header
20 * @param {String[][]} data
23 function tableCreate( header, data ) {
25 $table = $( '<table class="sortable"><thead></thead><tbody></tbody></table>' ),
26 $thead = $table.find( 'thead' ),
27 $tbody = $table.find( 'tbody' ),
30 $.each( header, function ( i, str ) {
31 var $th = $( '<th>' );
32 $th.text( str ).appendTo( $tr );
34 $tr.appendTo( $thead );
36 for ( i = 0; i < data.length; i++ ) {
37 /*jshint loopfunc: true */
39 $.each( data[i], function ( j, str ) {
40 var $td = $( '<td>' );
41 $td.text( str ).appendTo( $tr );
43 $tr.appendTo( $tbody );
49 * Extract text from table.
51 * @param {jQuery} $table
54 function tableExtract( $table ) {
57 $table.find( 'tbody' ).find( 'tr' ).each( function ( i, tr ) {
59 $( tr ).find( 'td,th' ).each( function ( i, td ) {
60 row.push( $( td ).text() );
68 * Run a table test by building a table with the given data,
69 * running some callback on it, then checking the results.
71 * @param {String} msg text to pass on to qunit for the comparison
72 * @param {String[]} header cols to make the table
73 * @param {String[][]} data rows/cols to make the table
74 * @param {String[][]} expected rows/cols to compare against at end
75 * @param {function($table)} callback something to do with the table before we compare
77 function tableTest( msg, header, data, expected, callback ) {
78 QUnit.test( msg, 1, function ( assert ) {
79 var $table = tableCreate( header, data );
81 // Give caller a chance to set up sorting and manipulate the table.
84 // Table sorting is done synchronously; if it ever needs to change back
85 // to asynchronous, we'll need a timeout or a callback here.
86 var extracted = tableExtract( $table );
87 assert.deepEqual( extracted, expected, msg );
92 * Run a table test by building a table with the given HTML,
93 * running some callback on it, then checking the results.
95 * @param {String} msg text to pass on to qunit for the comparison
96 * @param {String} HTML to make the table
97 * @param {String[][]} expected rows/cols to compare against at end
98 * @param {function($table)} callback something to do with the table before we compare
100 function tableTestHTML( msg, html, expected, callback ) {
101 QUnit.test( msg, 1, function ( assert ) {
102 var $table = $( html );
104 // Give caller a chance to set up sorting and manipulate the table.
108 $table.tablesorter();
109 $table.find( '#sortme' ).click();
112 // Table sorting is done synchronously; if it ever needs to change back
113 // to asynchronous, we'll need a timeout or a callback here.
114 var extracted = tableExtract( $table );
115 assert.deepEqual( extracted, expected, msg );
119 function reversed( arr ) {
121 var arr2 = arr.slice( 0 );
128 // Sample data set using planets named and their radius
129 var header = [ 'Planet' , 'Radius (km)'],
130 mercury = [ 'Mercury', '2439.7' ],
131 venus = [ 'Venus' , '6051.8' ],
132 earth = [ 'Earth' , '6371.0' ],
133 mars = [ 'Mars' , '3390.0' ],
134 jupiter = [ 'Jupiter', '69911' ],
135 saturn = [ 'Saturn' , '58232' ];
138 var planets = [mercury, venus, earth, mars, jupiter, saturn];
139 var ascendingName = [earth, jupiter, mars, mercury, saturn, venus];
140 var ascendingRadius = [mercury, mars, venus, earth, saturn, jupiter];
143 'Basic planet table: sorting initially - ascending by name',
147 function ( $table ) {
148 $table.tablesorter( { sortList: [
154 'Basic planet table: sorting initially - descending by radius',
157 reversed( ascendingRadius ),
158 function ( $table ) {
159 $table.tablesorter( { sortList: [
165 'Basic planet table: ascending by name',
169 function ( $table ) {
170 $table.tablesorter();
171 $table.find( '.headerSort:eq(0)' ).click();
175 'Basic planet table: ascending by name a second time',
179 function ( $table ) {
180 $table.tablesorter();
181 $table.find( '.headerSort:eq(0)' ).click();
185 'Basic planet table: descending by name',
188 reversed( ascendingName ),
189 function ( $table ) {
190 $table.tablesorter();
191 $table.find( '.headerSort:eq(0)' ).click().click();
195 'Basic planet table: ascending radius',
199 function ( $table ) {
200 $table.tablesorter();
201 $table.find( '.headerSort:eq(1)' ).click();
205 'Basic planet table: descending radius',
208 reversed( ascendingRadius ),
209 function ( $table ) {
210 $table.tablesorter();
211 $table.find( '.headerSort:eq(1)' ).click().click();
215 // Sample data set to test multiple column sorting
216 header = [ 'column1' , 'column2'];
224 var initial = [a2, b3, a1, a3, b2, b1];
225 var asc = [a1, a2, a3, b1, b2, b3];
226 var descasc = [b1, b2, b3, a1, a2, a3];
229 'Sorting multiple columns by passing sort list',
233 function ( $table ) {
243 'Sorting multiple columns by programmatically triggering sort()',
247 function ( $table ) {
248 $table.tablesorter();
249 $table.data( 'tablesorter' ).sort(
258 'Reset to initial sorting by triggering sort() without any parameters',
262 function ( $table ) {
269 $table.data( 'tablesorter' ).sort(
275 $table.data( 'tablesorter' ).sort();
278 QUnit.test( 'Reset sorting making table appear unsorted', 3, function ( assert ) {
279 var $table = tableCreate( header, initial );
286 $table.data( 'tablesorter' ).sort( [] );
289 $table.find( 'th.headerSortUp' ).length + $table.find( 'th.headerSortDown' ).length,
291 'No sort specific sort classes addign to header cells'
295 $table.find( 'th' ).first().attr( 'title' ),
296 mw.msg( 'sort-ascending' ),
297 'First header cell has default title'
301 $table.find( 'th' ).first().attr( 'title' ),
302 $table.find( 'th' ).last().attr( 'title' ),
303 'Both header cells\' titles match'
307 // Sorting with colspans
308 header = [ 'column1a' , 'column1b', 'column1c', 'column2' ];
310 aaa1 = [ 'A', 'A', 'A', '1' ],
311 aab5 = [ 'A', 'A', 'B', '5' ],
312 abc3 = [ 'A', 'B', 'C', '3' ],
313 bbc2 = [ 'B', 'B', 'C', '2' ],
314 caa4 = [ 'C', 'A', 'A', '4' ];
315 // initial is already declared above
316 initial = [ aab5, aaa1, abc3, bbc2, caa4 ];
317 tableTest( 'Sorting with colspanned headers: spanned column',
320 [ aaa1, aab5, abc3, bbc2, caa4 ],
321 function ( $table ) {
322 // Make colspanned header for test
323 $table.find( 'tr:eq(0) th:eq(1), tr:eq(0) th:eq(2)' ).remove();
324 $table.find( 'tr:eq(0) th:eq(0)' ).prop( 'colspan', '3' );
326 $table.tablesorter();
327 $table.find( '.headerSort:eq(0)' ).click();
330 tableTest( 'Sorting with colspanned headers: subsequent column',
333 [ aaa1, bbc2, abc3, caa4, aab5 ],
334 function ( $table ) {
335 // Make colspanned header for test
336 $table.find( 'tr:eq(0) th:eq(1), tr:eq(0) th:eq(2)' ).remove();
337 $table.find( 'tr:eq(0) th:eq(0)' ).prop( 'colspan', '3' );
339 $table.tablesorter();
340 $table.find( '.headerSort:eq(1)' ).click();
346 'Bug 28775: German-style (dmy) short numeric dates',
349 // German-style dates are day-month-year
357 // Sorted by ascending date
364 function ( $table ) {
365 mw.config.set( 'wgDefaultDateFormat', 'dmy' );
366 mw.config.set( 'wgContentLanguage', 'de' );
368 $table.tablesorter();
369 $table.find( '.headerSort:eq(0)' ).click();
374 'Bug 28775: American-style (mdy) short numeric dates',
377 // American-style dates are month-day-year
385 // Sorted by ascending date
392 function ( $table ) {
393 mw.config.set( 'wgDefaultDateFormat', 'mdy' );
395 $table.tablesorter();
396 $table.find( '.headerSort:eq(0)' ).click();
401 // Some randomly generated fake IPs
412 // Sort order should go octet by octet
424 'Bug 17141: IPv4 address sorting',
428 function ( $table ) {
429 $table.tablesorter();
430 $table.find( '.headerSort:eq(0)' ).click();
434 'Bug 17141: IPv4 address sorting (reverse)',
437 reversed( ipv4Sorted ),
438 function ( $table ) {
439 $table.tablesorter();
440 $table.find( '.headerSort:eq(0)' ).click().click();
445 // Some words with Umlauts
456 var umlautWordsSorted = [
457 // Some words with Umlauts
469 'Accented Characters with custom collation',
473 function ( $table ) {
474 mw.config.set( 'tableSorterCollation', {
481 $table.tablesorter();
482 $table.find( '.headerSort:eq(0)' ).click();
486 QUnit.test( 'Rowspan not exploded on init', 1, function ( assert ) {
487 var $table = tableCreate( header, planets );
489 // Modify the table to have a multiple-row-spanning cell:
490 // - Remove 2nd cell of 4th row, and, 2nd cell or 5th row.
491 $table.find( 'tr:eq(3) td:eq(1), tr:eq(4) td:eq(1)' ).remove();
492 // - Set rowspan for 2nd cell of 3rd row to 3.
493 // This covers the removed cell in the 4th and 5th row.
494 $table.find( 'tr:eq(2) td:eq(1)' ).prop( 'rowspan', '3' );
496 $table.tablesorter();
499 $table.find( 'tr:eq(2) td:eq(1)' ).prop( 'rowspan' ),
501 'Rowspan not exploded'
505 var planetsRowspan = [
506 [ 'Earth', '6051.8' ],
508 [ 'Mars', '6051.8' ],
513 var planetsRowspanII = [ jupiter, mercury, saturn, venus, [ 'Venus', '6371.0' ], [ 'Venus', '3390.0' ] ];
516 'Basic planet table: same value for multiple rows via rowspan',
520 function ( $table ) {
521 // Modify the table to have a multiple-row-spanning cell:
522 // - Remove 2nd cell of 4th row, and, 2nd cell or 5th row.
523 $table.find( 'tr:eq(3) td:eq(1), tr:eq(4) td:eq(1)' ).remove();
524 // - Set rowspan for 2nd cell of 3rd row to 3.
525 // This covers the removed cell in the 4th and 5th row.
526 $table.find( 'tr:eq(2) td:eq(1)' ).prop( 'rowspan', '3' );
528 $table.tablesorter();
529 $table.find( '.headerSort:eq(0)' ).click();
533 'Basic planet table: same value for multiple rows via rowspan (sorting initially)',
537 function ( $table ) {
538 // Modify the table to have a multiple-row-spanning cell:
539 // - Remove 2nd cell of 4th row, and, 2nd cell or 5th row.
540 $table.find( 'tr:eq(3) td:eq(1), tr:eq(4) td:eq(1)' ).remove();
541 // - Set rowspan for 2nd cell of 3rd row to 3.
542 // This covers the removed cell in the 4th and 5th row.
543 $table.find( 'tr:eq(2) td:eq(1)' ).prop( 'rowspan', '3' );
545 $table.tablesorter( { sortList: [
551 'Basic planet table: Same value for multiple rows via rowspan II',
555 function ( $table ) {
556 // Modify the table to have a multiple-row-spanning cell:
557 // - Remove 1st cell of 4th row, and, 1st cell or 5th row.
558 $table.find( 'tr:eq(3) td:eq(0), tr:eq(4) td:eq(0)' ).remove();
559 // - Set rowspan for 1st cell of 3rd row to 3.
560 // This covers the removed cell in the 4th and 5th row.
561 $table.find( 'tr:eq(2) td:eq(0)' ).prop( 'rowspan', '3' );
563 $table.tablesorter();
564 $table.find( '.headerSort:eq(0)' ).click();
568 var complexMDYDates = [
569 // Some words with Umlauts
570 ['January, 19 2010'],
577 var complexMDYSorted = [
581 ['January, 19 2010'],
586 'Complex date parsing I',
590 function ( $table ) {
591 mw.config.set( 'wgDefaultDateFormat', 'mdy' );
593 $table.tablesorter();
594 $table.find( '.headerSort:eq(0)' ).click();
598 var currencyUnsorted = [
608 var currencySorted = [
615 // Comma's sort after dots
616 // Not intentional but test to detect changes
621 'Currency parsing I',
625 function ( $table ) {
626 $table.tablesorter();
627 $table.find( '.headerSort:eq(0)' ).click();
631 var ascendingNameLegacy = ascendingName.slice( 0 );
632 ascendingNameLegacy[4] = ascendingNameLegacy[5];
633 ascendingNameLegacy.pop();
636 'Legacy compat with .sortbottom',
640 function ( $table ) {
641 $table.find( 'tr:last' ).addClass( 'sortbottom' );
642 $table.tablesorter();
643 $table.find( '.headerSort:eq(0)' ).click();
647 QUnit.test( 'Test detection routine', 1, function ( assert ) {
650 '<table class="sortable">' +
651 '<caption>CAPTION</caption>' +
652 '<tr><th>THEAD</th></tr>' +
653 '<tr><td>1</td></tr>' +
654 '<tr class="sortbottom"><td>text</td></tr>' +
657 $table.tablesorter();
658 $table.find( '.headerSort:eq(0)' ).click();
661 $table.data( 'tablesorter' ).config.parsers[0].id,
663 'Correctly detected column content skipping sortbottom'
667 /** FIXME: the diff output is not very readeable. */
668 QUnit.test( 'bug 32047 - caption must be before thead', 1, function ( assert ) {
671 '<table class="sortable">' +
672 '<caption>CAPTION</caption>' +
673 '<tr><th>THEAD</th></tr>' +
674 '<tr><td>A</td></tr>' +
675 '<tr><td>B</td></tr>' +
676 '<tr class="sortbottom"><td>TFOOT</td></tr>' +
679 $table.tablesorter();
682 $table.children().get( 0 ).nodeName,
684 'First element after <thead> must be <caption> (bug 32047)'
688 QUnit.test( 'data-sort-value attribute, when available, should override sorting position', 3, function ( assert ) {
691 // Example 1: All cells except one cell without data-sort-value,
692 // which should be sorted at it's text content value.
694 '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
696 '<tr><td>Cheetah</td></tr>' +
697 '<tr><td data-sort-value="Apple">Bird</td></tr>' +
698 '<tr><td data-sort-value="Bananna">Ferret</td></tr>' +
699 '<tr><td data-sort-value="Drupe">Elephant</td></tr>' +
700 '<tr><td data-sort-value="Cherry">Dolphin</td></tr>' +
703 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
706 $table.find( 'tbody > tr' ).each( function ( i, tr ) {
707 $( tr ).find( 'td' ).each( function ( i, td ) {
709 data: $( td ).data( 'sortValue' ),
715 assert.deepEqual( data, [
736 ], 'Order matches expected order (based on data-sort-value attribute values)' );
740 '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
742 '<tr><td>D</td></tr>' +
743 '<tr><td data-sort-value="E">A</td></tr>' +
744 '<tr><td>B</td></tr>' +
745 '<tr><td>G</td></tr>' +
746 '<tr><td data-sort-value="F">C</td></tr>' +
749 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
752 $table.find( 'tbody > tr' ).each( function ( i, tr ) {
753 $( tr ).find( 'td' ).each( function ( i, td ) {
755 data: $( td ).data( 'sortValue' ),
761 assert.deepEqual( data, [
782 ], 'Order matches expected order (based on data-sort-value attribute values)' );
784 // Example 3: Test that live changes are used from data-sort-value,
785 // even if they change after the tablesorter is constructed (bug 38152).
787 '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
789 '<tr><td>D</td></tr>' +
790 '<tr><td data-sort-value="1">A</td></tr>' +
791 '<tr><td>B</td></tr>' +
792 '<tr><td data-sort-value="2">G</td></tr>' +
793 '<tr><td>C</td></tr>' +
796 // initialize table sorter and sort once
799 .find( '.headerSort:eq(0)' ).click();
801 // Change the sortValue data properties (bug 38152)
803 $table.find( 'td:contains(A)' ).data( 'sortValue', 3 );
805 $table.find( 'td:contains(B)' ).data( 'sortValue', 1 );
806 // - remove data, bring back attribute: 2
807 $table.find( 'td:contains(G)' ).removeData( 'sortValue' );
809 // Now sort again (twice, so it is back at Ascending)
810 $table.find( '.headerSort:eq(0)' ).click();
811 $table.find( '.headerSort:eq(0)' ).click();
814 $table.find( 'tbody > tr' ).each( function ( i, tr ) {
815 $( tr ).find( 'td' ).each( function ( i, td ) {
817 data: $( td ).data( 'sortValue' ),
823 assert.deepEqual( data, [
844 ], 'Order matches expected order, using the current sortValue in $.data()' );
865 tableTest( 'bug 8115: sort numbers with commas (ascending)',
866 ['Numbers'], numbers, numbersAsc,
867 function ( $table ) {
868 $table.tablesorter();
869 $table.find( '.headerSort:eq(0)' ).click();
873 tableTest( 'bug 8115: sort numbers with commas (descending)',
874 ['Numbers'], numbers, reversed( numbersAsc ),
875 function ( $table ) {
876 $table.tablesorter();
877 $table.find( '.headerSort:eq(0)' ).click().click();
880 // TODO add numbers sorting tests for bug 8115 with a different language
882 QUnit.test( 'bug 32888 - Tables inside a tableheader cell', 2, function ( assert ) {
885 '<table class="sortable" id="mw-bug-32888">' +
886 '<tr><th>header<table id="mw-bug-32888-2">' +
887 '<tr><th>1</th><th>2</th></tr>' +
888 '</table></th></tr>' +
889 '<tr><td>A</td></tr>' +
890 '<tr><td>B</td></tr>' +
893 $table.tablesorter();
896 $table.find( '> thead:eq(0) > tr > th.headerSort' ).length,
898 'Child tables inside a headercell should not interfere with sortable headers (bug 32888)'
901 $( '#mw-bug-32888-2' ).find( 'th.headerSort' ).length,
903 'The headers of child tables inside a headercell should not be sortable themselves (bug 32888)'
908 var correctDateSorting1 = [
910 ['05 February 2010'],
914 var correctDateSortingSorted1 = [
921 'Correct date sorting I',
924 correctDateSortingSorted1,
925 function ( $table ) {
926 mw.config.set( 'wgDefaultDateFormat', 'mdy' );
928 $table.tablesorter();
929 $table.find( '.headerSort:eq(0)' ).click();
933 var correctDateSorting2 = [
935 ['February 05 2010'],
939 var correctDateSortingSorted2 = [
946 'Correct date sorting II',
949 correctDateSortingSorted2,
950 function ( $table ) {
951 mw.config.set( 'wgDefaultDateFormat', 'dmy' );
953 $table.tablesorter();
954 $table.find( '.headerSort:eq(0)' ).click();
958 QUnit.test( 'Sorting images using alt text', 1, function ( assert ) {
960 '<table class="sortable">' +
961 '<tr><th>THEAD</th></tr>' +
962 '<tr><td><img alt="2"/></td></tr>' +
963 '<tr><td>1</td></tr>' +
966 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
969 $table.find( 'td' ).first().text(),
971 'Applied correct sorting order'
975 QUnit.test( 'Sorting images using alt text (complex)', 1, function ( assert ) {
977 '<table class="sortable">' +
978 '<tr><th>THEAD</th></tr>' +
979 '<tr><td><img alt="D" />A</td></tr>' +
980 '<tr><td>CC</td></tr>' +
981 '<tr><td><a><img alt="A" /></a>F</tr>' +
982 '<tr><td><img alt="A" /><strong>E</strong></tr>' +
983 '<tr><td><strong><img alt="A" />D</strong></tr>' +
984 '<tr><td><img alt="A" />C</tr>' +
987 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
990 $table.find( 'td' ).text(),
992 'Applied correct sorting order'
996 QUnit.test( 'Sorting images using alt text (with format autodetection)', 1, function ( assert ) {
998 '<table class="sortable">' +
999 '<tr><th>THEAD</th></tr>' +
1000 '<tr><td><img alt="1" />7</td></tr>' +
1001 '<tr><td>1<img alt="6" /></td></tr>' +
1002 '<tr><td>5</td></tr>' +
1003 '<tr><td>4</td></tr>' +
1006 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
1009 $table.find( 'td' ).text(),
1011 'Applied correct sorting order'
1015 // bug 41889 - exploding rowspans in more complex cases
1017 'Rowspan exploding with row headers',
1018 '<table class="sortable">' +
1019 '<thead><tr><th id="sortme">n</th><th>foo</th><th>bar</th><th>baz</th></tr></thead>' +
1021 '<tr><td>1</td><th rowspan="2">foo</th><td rowspan="2">bar</td><td>baz</td></tr>' +
1022 '<tr><td>2</td><td>baz</td></tr>' +
1025 [ '1', 'foo', 'bar', 'baz' ],
1026 [ '2', 'foo', 'bar', 'baz' ]
1031 'Rowspan exploding with colspanned cells',
1032 '<table class="sortable">' +
1033 '<thead><tr><th id="sortme">n</th><th>foo</th><th>bar</th><th>baz</th></tr></thead>' +
1035 '<tr><td>1</td><td>foo</td><td>bar</td><td rowspan="2">baz</td></tr>' +
1036 '<tr><td>2</td><td colspan="2">foobar</td></tr>' +
1039 [ '1', 'foo', 'bar', 'baz' ],
1040 [ '2', 'foobar', 'baz' ]
1045 'Rowspan exploding with colspanned cells (2)',
1046 '<table class="sortable">' +
1047 '<thead><tr><th id="sortme">n</th><th>foo</th><th>bar</th><th>baz</th><th>quux</th></tr></thead>' +
1049 '<tr><td>1</td><td>foo</td><td>bar</td><td rowspan="2">baz</td><td>quux</td></tr>' +
1050 '<tr><td>2</td><td colspan="2">foobar</td><td>quux</td></tr>' +
1053 [ '1', 'foo', 'bar', 'baz', 'quux' ],
1054 [ '2', 'foobar', 'baz', 'quux' ]
1059 'Rowspan exploding with rightmost rows spanning most',
1060 '<table class="sortable">' +
1061 '<thead><tr><th id="sortme">n</th><th>foo</th><th>bar</th></tr></thead>' +
1063 '<tr><td>1</td><td rowspan="2">foo</td><td rowspan="4">bar</td></tr>' +
1064 '<tr><td>2</td></tr>' +
1065 '<tr><td>3</td><td rowspan="2">foo</td></tr>' +
1066 '<tr><td>4</td></tr>' +
1069 [ '1', 'foo', 'bar' ],
1070 [ '2', 'foo', 'bar' ],
1071 [ '3', 'foo', 'bar' ],
1072 [ '4', 'foo', 'bar' ]
1077 'Rowspan exploding with rightmost rows spanning most (2)',
1078 '<table class="sortable">' +
1079 '<thead><tr><th id="sortme">n</th><th>foo</th><th>bar</th><th>baz</th></tr></thead>' +
1081 '<tr><td>1</td><td rowspan="2">foo</td><td rowspan="4">bar</td><td>baz</td></tr>' +
1082 '<tr><td>2</td><td>baz</td></tr>' +
1083 '<tr><td>3</td><td rowspan="2">foo</td><td>baz</td></tr>' +
1084 '<tr><td>4</td><td>baz</td></tr>' +
1087 [ '1', 'foo', 'bar', 'baz' ],
1088 [ '2', 'foo', 'bar', 'baz' ],
1089 [ '3', 'foo', 'bar', 'baz' ],
1090 [ '4', 'foo', 'bar', 'baz' ]
1095 'Rowspan exploding with row-and-colspanned cells',
1096 '<table class="sortable">' +
1097 '<thead><tr><th id="sortme">n</th><th>foo1</th><th>foo2</th><th>bar</th><th>baz</th></tr></thead>' +
1099 '<tr><td>1</td><td rowspan="2">foo1</td><td rowspan="2">foo2</td><td rowspan="4">bar</td><td>baz</td></tr>' +
1100 '<tr><td>2</td><td>baz</td></tr>' +
1101 '<tr><td>3</td><td colspan="2" rowspan="2">foo</td><td>baz</td></tr>' +
1102 '<tr><td>4</td><td>baz</td></tr>' +
1105 [ '1', 'foo1', 'foo2', 'bar', 'baz' ],
1106 [ '2', 'foo1', 'foo2', 'bar', 'baz' ],
1107 [ '3', 'foo', 'bar', 'baz' ],
1108 [ '4', 'foo', 'bar', 'baz' ]
1113 'Rowspan exploding with uneven rowspan layout',
1114 '<table class="sortable">' +
1115 '<thead><tr><th id="sortme">n</th><th>foo1</th><th>foo2</th><th>foo3</th><th>bar</th><th>baz</th></tr></thead>' +
1117 '<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>' +
1118 '<tr><td>2</td><td rowspan="3">bar</td><td>baz</td></tr>' +
1119 '<tr><td>3</td><td rowspan="2">foo1</td><td rowspan="2">foo2</td><td rowspan="2">foo3</td><td>baz</td></tr>' +
1120 '<tr><td>4</td><td>baz</td></tr>' +
1123 [ '1', 'foo1', 'foo2', 'foo3', 'bar', 'baz' ],
1124 [ '2', 'foo1', 'foo2', 'foo3', 'bar', 'baz' ],
1125 [ '3', 'foo1', 'foo2', 'foo3', 'bar', 'baz' ],
1126 [ '4', 'foo1', 'foo2', 'foo3', 'bar', 'baz' ]
1130 }( jQuery, mediaWiki ) );