Special case opus mime detction
[mediawiki.git] / tests / qunit / suites / resources / jquery / jquery.tablesorter.test.js
blob483a37d1be64cd09d51677b4f72fd3c48a140134
1 ( function ( $, mw ) {
2         var header = [ 'Planet', 'Radius (km)' ],
4                 // Data set "planets"
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 ],
14                 planetsRowspan,
15                 planetsRowspanII,
16                 planetsAscNameLegacy,
18                 // Data set "simple"
19                 a1 = [ 'A', '1' ],
20                 a2 = [ 'A', '2' ],
21                 a3 = [ 'A', '3' ],
22                 b1 = [ 'B', '1' ],
23                 b2 = [ 'B', '2' ],
24                 b3 = [ 'B', '3' ],
25                 simple = [ a2, b3, a1, a3, b2, b1 ],
26                 simpleAsc = [ a1, a2, a3, b1, b2, b3 ],
27                 simpleDescasc = [ b1, b2, b3, a1, a2, a3 ],
29                 // Data set "colspan"
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 ],
38                 // Data set "ipv4"
39                 ipv4 = [
40                         // Some randomly generated fake IPs
41                         [ '45.238.27.109' ],
42                         [ '44.172.9.22' ],
43                         [ '247.240.82.209' ],
44                         [ '204.204.132.158' ],
45                         [ '170.38.91.162' ],
46                         [ '197.219.164.9' ],
47                         [ '45.68.154.72' ],
48                         [ '182.195.149.80' ]
49                 ],
50                 ipv4Sorted = [
51                         // Sort order should go octet by octet
52                         [ '44.172.9.22' ],
53                         [ '45.68.154.72' ],
54                         [ '45.238.27.109' ],
55                         [ '170.38.91.162' ],
56                         [ '182.195.149.80' ],
57                         [ '197.219.164.9' ],
58                         [ '204.204.132.158' ],
59                         [ '247.240.82.209' ]
60                 ],
62                 // Data set "umlaut"
63                 umlautWords = [
64                         [ 'Günther' ],
65                         [ 'Peter' ],
66                         [ 'Björn' ],
67                         [ 'Bjorn' ],
68                         [ 'Apfel' ],
69                         [ 'Äpfel' ],
70                         [ 'Strasse' ],
71                         [ 'Sträßschen' ]
72                 ],
73                 umlautWordsSorted = [
74                         [ 'Äpfel' ],
75                         [ 'Apfel' ],
76                         [ 'Björn' ],
77                         [ 'Bjorn' ],
78                         [ 'Günther' ],
79                         [ 'Peter' ],
80                         [ 'Sträßschen' ],
81                         [ 'Strasse' ]
82                 ],
84                 // Data set "digraph"
85                 digraphWords = [
86                         [ 'London' ],
87                         [ 'Ljubljana' ],
88                         [ 'Luxembourg' ],
89                         [ 'Njivice' ],
90                         [ 'Norwich' ],
91                         [ 'New York' ]
92                 ],
93                 digraphWordsSorted = [
94                         [ 'London' ],
95                         [ 'Luxembourg' ],
96                         [ 'Ljubljana' ],
97                         [ 'New York' ],
98                         [ 'Norwich' ],
99                         [ 'Njivice' ]
100                 ],
102                 complexMDYDates = [
103                         [ 'January, 19 2010' ],
104                         [ 'April 21 1991' ],
105                         [ '04 22 1991' ],
106                         [ '5.12.1990' ],
107                         [ 'December 12 \'10' ]
108                 ],
109                 complexMDYSorted = [
110                         [ '5.12.1990' ],
111                         [ 'April 21 1991' ],
112                         [ '04 22 1991' ],
113                         [ 'January, 19 2010' ],
114                         [ 'December 12 \'10' ]
115                 ],
117                 currencyUnsorted = [
118                         [ '1.02 $' ],
119                         [ '$ 3.00' ],
120                         [ '€ 2,99' ],
121                         [ '$ 1.00' ],
122                         [ '$3.50' ],
123                         [ '$ 1.50' ],
124                         [ '€ 0.99' ]
125                 ],
126                 currencySorted = [
127                         [ '€ 0.99' ],
128                         [ '$ 1.00' ],
129                         [ '1.02 $' ],
130                         [ '$ 1.50' ],
131                         [ '$ 3.00' ],
132                         [ '$3.50' ],
133                         // Comma's sort after dots
134                         // Not intentional but test to detect changes
135                         [ '€ 2,99' ]
136                 ],
138                 numbers = [
139                         [ '12' ],
140                         [ '7' ],
141                         [ '13,000' ],
142                         [ '9' ],
143                         [ '14' ],
144                         [ '8.0' ]
145                 ],
146                 numbersAsc = [
147                         [ '7' ],
148                         [ '8.0' ],
149                         [ '9' ],
150                         [ '12' ],
151                         [ '14' ],
152                         [ '13,000' ]
153                 ],
155                 correctDateSorting1 = [
156                         [ '01 January 2010' ],
157                         [ '05 February 2010' ],
158                         [ '16 January 2010' ]
159                 ],
160                 correctDateSortingSorted1 = [
161                         [ '01 January 2010' ],
162                         [ '16 January 2010' ],
163                         [ '05 February 2010' ]
164                 ],
166                 correctDateSorting2 = [
167                         [ 'January 01 2010' ],
168                         [ 'February 05 2010' ],
169                         [ 'January 16 2010' ]
170                 ],
171                 correctDateSortingSorted2 = [
172                         [ 'January 01 2010' ],
173                         [ 'January 16 2010' ],
174                         [ 'February 05 2010' ]
175                 ],
176                 isoDateSorting = [
177                         [ '2010-02-01' ],
178                         [ '2009-12-25T12:30:45.001Z' ],
179                         [ '2010-01-31' ],
180                         [ '2009' ],
181                         [ '2009-12-25T12:30:45' ],
182                         [ '2009-12-25T12:30:45.111' ],
183                         [ '2009-12-25T12:30:45+01:00' ]
184                 ],
185                 isoDateSortingSorted = [
186                         [ '2009' ],
187                         [ '2009-12-25T12:30:45' ],
188                         [ '2009-12-25T12:30:45+01:00' ],
189                         [ '2009-12-25T12:30:45.001Z' ],
190                         [ '2009-12-25T12:30:45.111' ],
191                         [ '2010-01-31' ],
192                         [ '2010-02-01' ]
193                 ];
195         QUnit.module( 'jquery.tablesorter', QUnit.newMwEnvironment( {
196                 setup: function () {
197                         this.liveMonths = mw.language.months;
198                         mw.language.months = {
199                                 keys: {
200                                         names: [ 'january', 'february', 'march', 'april', 'may_long', 'june',
201                                                 'july', 'august', 'september', 'october', 'november', 'december' ],
202                                         genitive: [ 'january-gen', 'february-gen', 'march-gen', 'april-gen', 'may-gen', 'june-gen',
203                                                 'july-gen', 'august-gen', 'september-gen', 'october-gen', 'november-gen', 'december-gen' ],
204                                         abbrev: [ 'jan', 'feb', 'mar', 'apr', 'may', 'jun',
205                                                 'jul', 'aug', 'sep', 'oct', 'nov', 'dec' ]
206                                 },
207                                 names: [ 'January', 'February', 'March', 'April', 'May', 'June',
208                                                 'July', 'August', 'September', 'October', 'November', 'December' ],
209                                 genitive: [ 'January', 'February', 'March', 'April', 'May', 'June',
210                                                 'July', 'August', 'September', 'October', 'November', 'December' ],
211                                 abbrev: [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
212                                                 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ]
213                         };
214                 },
215                 teardown: function () {
216                         mw.language.months = this.liveMonths;
217                 },
218                 config: {
219                         wgDefaultDateFormat: 'dmy',
220                         wgSeparatorTransformTable: [ '', '' ],
221                         wgDigitTransformTable: [ '', '' ],
222                         wgPageContentLanguage: 'en'
223                 }
224         } ) );
226         /**
227          * Create an HTML table from an array of row arrays containing text strings.
228          * First row will be header row. No fancy rowspan/colspan stuff.
229          *
230          * @param {string[]} header
231          * @param {string[][]} data
232          * @return {jQuery}
233          */
234         function tableCreate( header, data ) {
235                 var i,
236                         $table = $( '<table class="sortable"><thead></thead><tbody></tbody></table>' ),
237                         $thead = $table.find( 'thead' ),
238                         $tbody = $table.find( 'tbody' ),
239                         $tr = $( '<tr>' );
241                 $.each( header, function ( i, str ) {
242                         var $th = $( '<th>' );
243                         $th.text( str ).appendTo( $tr );
244                 } );
245                 $tr.appendTo( $thead );
247                 for ( i = 0; i < data.length; i++ ) {
248                         /*jshint loopfunc: true */
249                         $tr = $( '<tr>' );
250                         $.each( data[ i ], function ( j, str ) {
251                                 var $td = $( '<td>' );
252                                 $td.text( str ).appendTo( $tr );
253                         } );
254                         $tr.appendTo( $tbody );
255                 }
256                 return $table;
257         }
259         /**
260          * Extract text from table.
261          *
262          * @param {jQuery} $table
263          * @return {string[][]}
264          */
265         function tableExtract( $table ) {
266                 var data = [];
268                 $table.find( 'tbody' ).find( 'tr' ).each( function ( i, tr ) {
269                         var row = [];
270                         $( tr ).find( 'td,th' ).each( function ( i, td ) {
271                                 row.push( $( td ).text() );
272                         } );
273                         data.push( row );
274                 } );
275                 return data;
276         }
278         /**
279          * Run a table test by building a table with the given data,
280          * running some callback on it, then checking the results.
281          *
282          * @param {string} msg text to pass on to qunit for the comparison
283          * @param {string[]} header cols to make the table
284          * @param {string[][]} data rows/cols to make the table
285          * @param {string[][]} expected rows/cols to compare against at end
286          * @param {function($table)} callback something to do with the table before we compare
287          */
288         function tableTest( msg, header, data, expected, callback ) {
289                 QUnit.test( msg, 1, function ( assert ) {
290                         var extracted,
291                                 $table = tableCreate( header, data );
293                         // Give caller a chance to set up sorting and manipulate the table.
294                         callback( $table );
296                         // Table sorting is done synchronously; if it ever needs to change back
297                         // to asynchronous, we'll need a timeout or a callback here.
298                         extracted = tableExtract( $table );
299                         assert.deepEqual( extracted, expected, msg );
300                 } );
301         }
303         /**
304          * Run a table test by building a table with the given HTML,
305          * running some callback on it, then checking the results.
306          *
307          * @param {string} msg text to pass on to qunit for the comparison
308          * @param {string} html HTML to make the table
309          * @param {string[][]} expected Rows/cols to compare against at end
310          * @param {function($table)} callback Something to do with the table before we compare
311          */
312         function tableTestHTML( msg, html, expected, callback ) {
313                 QUnit.test( msg, 1, function ( assert ) {
314                         var extracted,
315                                 $table = $( html );
317                         // Give caller a chance to set up sorting and manipulate the table.
318                         if ( callback ) {
319                                 callback( $table );
320                         } else {
321                                 $table.tablesorter();
322                                 $table.find( '#sortme' ).click();
323                         }
325                         // Table sorting is done synchronously; if it ever needs to change back
326                         // to asynchronous, we'll need a timeout or a callback here.
327                         extracted = tableExtract( $table );
328                         assert.deepEqual( extracted, expected, msg );
329                 } );
330         }
332         function reversed( arr ) {
333                 // Clone array
334                 var arr2 = arr.slice( 0 );
336                 arr2.reverse();
338                 return arr2;
339         }
341         // Sample data set using planets named and their radius
343         tableTest(
344                 'Basic planet table: sorting initially - ascending by name',
345                 header,
346                 planets,
347                 planetsAscName,
348                 function ( $table ) {
349                         $table.tablesorter( { sortList: [
350                                 { 0: 'asc' }
351                         ] } );
352                 }
353         );
354         tableTest(
355                 'Basic planet table: sorting initially - descending by radius',
356                 header,
357                 planets,
358                 reversed( planetsAscRadius ),
359                 function ( $table ) {
360                         $table.tablesorter( { sortList: [
361                                 { 1: 'desc' }
362                         ] } );
363                 }
364         );
365         tableTest(
366                 'Basic planet table: ascending by name',
367                 header,
368                 planets,
369                 planetsAscName,
370                 function ( $table ) {
371                         $table.tablesorter();
372                         $table.find( '.headerSort:eq(0)' ).click();
373                 }
374         );
375         tableTest(
376                 'Basic planet table: ascending by name a second time',
377                 header,
378                 planets,
379                 planetsAscName,
380                 function ( $table ) {
381                         $table.tablesorter();
382                         $table.find( '.headerSort:eq(0)' ).click();
383                 }
384         );
385         tableTest(
386                 'Basic planet table: ascending by name (multiple clicks)',
387                 header,
388                 planets,
389                 planetsAscName,
390                 function ( $table ) {
391                         $table.tablesorter();
392                         $table.find( '.headerSort:eq(0)' ).click();
393                         $table.find( '.headerSort:eq(1)' ).click();
394                         $table.find( '.headerSort:eq(0)' ).click();
395                 }
396         );
397         tableTest(
398                 'Basic planet table: descending by name',
399                 header,
400                 planets,
401                 reversed( planetsAscName ),
402                 function ( $table ) {
403                         $table.tablesorter();
404                         $table.find( '.headerSort:eq(0)' ).click().click();
405                 }
406         );
407         tableTest(
408                 'Basic planet table: ascending radius',
409                 header,
410                 planets,
411                 planetsAscRadius,
412                 function ( $table ) {
413                         $table.tablesorter();
414                         $table.find( '.headerSort:eq(1)' ).click();
415                 }
416         );
417         tableTest(
418                 'Basic planet table: descending radius',
419                 header,
420                 planets,
421                 reversed( planetsAscRadius ),
422                 function ( $table ) {
423                         $table.tablesorter();
424                         $table.find( '.headerSort:eq(1)' ).click().click();
425                 }
426         );
427         tableTest(
428                 'Sorting multiple columns by passing sort list',
429                 header,
430                 simple,
431                 simpleAsc,
432                 function ( $table ) {
433                         $table.tablesorter(
434                                 { sortList: [
435                                         { 0: 'asc' },
436                                         { 1: 'asc' }
437                                 ] }
438                         );
439                 }
440         );
441         tableTest(
442                 'Sorting multiple columns by programmatically triggering sort()',
443                 header,
444                 simple,
445                 simpleDescasc,
446                 function ( $table ) {
447                         $table.tablesorter();
448                         $table.data( 'tablesorter' ).sort(
449                                 [
450                                         { 0: 'desc' },
451                                         { 1: 'asc' }
452                                 ]
453                         );
454                 }
455         );
456         tableTest(
457                 'Reset to initial sorting by triggering sort() without any parameters',
458                 header,
459                 simple,
460                 simpleAsc,
461                 function ( $table ) {
462                         $table.tablesorter(
463                                 { sortList: [
464                                         { 0: 'asc' },
465                                         { 1: 'asc' }
466                                 ] }
467                         );
468                         $table.data( 'tablesorter' ).sort(
469                                 [
470                                         { 0: 'desc' },
471                                         { 1: 'asc' }
472                                 ]
473                         );
474                         $table.data( 'tablesorter' ).sort();
475                 }
476         );
477         tableTest(
478                 'Sort via click event after having initialized the tablesorter with initial sorting',
479                 header,
480                 simple,
481                 simpleDescasc,
482                 function ( $table ) {
483                         $table.tablesorter(
484                                 { sortList: [ { 0: 'asc' }, { 1: 'asc' } ] }
485                         );
486                         $table.find( '.headerSort:eq(0)' ).click();
487                 }
488         );
489         tableTest(
490                 'Multi-sort via click event after having initialized the tablesorter with initial sorting',
491                 header,
492                 simple,
493                 simpleAsc,
494                 function ( $table ) {
495                         $table.tablesorter(
496                                 { sortList: [ { 0: 'desc' }, { 1: 'desc' } ] }
497                         );
498                         $table.find( '.headerSort:eq(0)' ).click();
500                         // Pretend to click while pressing the multi-sort key
501                         var event = $.Event( 'click' );
502                         event[ $table.data( 'tablesorter' ).config.sortMultiSortKey ] = true;
503                         $table.find( '.headerSort:eq(1)' ).trigger( event );
504                 }
505         );
506         QUnit.test( 'Reset sorting making table appear unsorted', 3, function ( assert ) {
507                 var $table = tableCreate( header, simple );
508                 $table.tablesorter(
509                         { sortList: [
510                                 { 0: 'desc' },
511                                 { 1: 'asc' }
512                         ] }
513                 );
514                 $table.data( 'tablesorter' ).sort( [] );
516                 assert.equal(
517                         $table.find( 'th.headerSortUp' ).length + $table.find( 'th.headerSortDown' ).length,
518                         0,
519                         'No sort specific sort classes addign to header cells'
520                 );
522                 assert.equal(
523                         $table.find( 'th' ).first().attr( 'title' ),
524                         mw.msg( 'sort-ascending' ),
525                         'First header cell has default title'
526                 );
528                 assert.equal(
529                         $table.find( 'th' ).first().attr( 'title' ),
530                         $table.find( 'th' ).last().attr( 'title' ),
531                         'Both header cells\' titles match'
532                 );
533         } );
535         // Sorting with colspans
537         tableTest( 'Sorting with colspanned headers: spanned column',
538                 header4,
539                 colspanInitial,
540                 [ aaa1, aab5, abc3, bbc2, caa4 ],
541                 function ( $table ) {
542                         // Make colspanned header for test
543                         $table.find( 'tr:eq(0) th:eq(1), tr:eq(0) th:eq(2)' ).remove();
544                         $table.find( 'tr:eq(0) th:eq(0)' ).attr( 'colspan', '3' );
546                         $table.tablesorter();
547                         $table.find( '.headerSort:eq(0)' ).click();
548                 }
549         );
550         tableTest( 'Sorting with colspanned headers: sort spanned column twice',
551                 header4,
552                 colspanInitial,
553                 [ caa4, bbc2, abc3, aab5, aaa1 ],
554                 function ( $table ) {
555                         // Make colspanned header for test
556                         $table.find( 'tr:eq(0) th:eq(1), tr:eq(0) th:eq(2)' ).remove();
557                         $table.find( 'tr:eq(0) th:eq(0)' ).attr( 'colspan', '3' );
559                         $table.tablesorter();
560                         $table.find( '.headerSort:eq(0)' ).click();
561                         $table.find( '.headerSort:eq(0)' ).click();
562                 }
563         );
564         tableTest( 'Sorting with colspanned headers: subsequent column',
565                 header4,
566                 colspanInitial,
567                 [ aaa1, bbc2, abc3, caa4, aab5 ],
568                 function ( $table ) {
569                         // Make colspanned header for test
570                         $table.find( 'tr:eq(0) th:eq(1), tr:eq(0) th:eq(2)' ).remove();
571                         $table.find( 'tr:eq(0) th:eq(0)' ).attr( 'colspan', '3' );
573                         $table.tablesorter();
574                         $table.find( '.headerSort:eq(1)' ).click();
575                 }
576         );
577         tableTest( 'Sorting with colspanned headers: sort subsequent column twice',
578                 header4,
579                 colspanInitial,
580                 [ aab5, caa4, abc3, bbc2, aaa1 ],
581                 function ( $table ) {
582                         // Make colspanned header for test
583                         $table.find( 'tr:eq(0) th:eq(1), tr:eq(0) th:eq(2)' ).remove();
584                         $table.find( 'tr:eq(0) th:eq(0)' ).attr( 'colspan', '3' );
586                         $table.tablesorter();
587                         $table.find( '.headerSort:eq(1)' ).click();
588                         $table.find( '.headerSort:eq(1)' ).click();
589                 }
590         );
592         QUnit.test( 'Basic planet table: one unsortable column', 3, function ( assert ) {
593                 var $table = tableCreate( header, planets ),
594                         $cell;
595                 $table.find( 'tr:eq(0) > th:eq(0)' ).addClass( 'unsortable' );
597                 $table.tablesorter();
598                 $table.find( 'tr:eq(0) > th:eq(0)' ).click();
600                 assert.deepEqual(
601                         tableExtract( $table ),
602                         planets,
603                         'table not sorted'
604                 );
606                 $cell = $table.find( 'tr:eq(0) > th:eq(0)' );
607                 $table.find( 'tr:eq(0) > th:eq(1)' ).click();
609                 assert.equal(
610                         $cell.hasClass( 'headerSortUp' ) || $cell.hasClass( 'headerSortDown' ),
611                         false,
612                         'after sort: no class headerSortUp or headerSortDown'
613                 );
615                 assert.equal(
616                         $cell.attr( 'title' ),
617                         undefined,
618                         'after sort: no title tag added'
619                 );
621         } );
623         // Regression tests!
624         tableTest(
625                 'Bug 28775: German-style (dmy) short numeric dates',
626                 [ 'Date' ],
627                 [
628                         // German-style dates are day-month-year
629                         [ '11.11.2011' ],
630                         [ '01.11.2011' ],
631                         [ '02.10.2011' ],
632                         [ '03.08.2011' ],
633                         [ '09.11.2011' ]
634                 ],
635                 [
636                         // Sorted by ascending date
637                         [ '03.08.2011' ],
638                         [ '02.10.2011' ],
639                         [ '01.11.2011' ],
640                         [ '09.11.2011' ],
641                         [ '11.11.2011' ]
642                 ],
643                 function ( $table ) {
644                         mw.config.set( 'wgDefaultDateFormat', 'dmy' );
645                         mw.config.set( 'wgPageContentLanguage', 'de' );
647                         $table.tablesorter();
648                         $table.find( '.headerSort:eq(0)' ).click();
649                 }
650         );
652         tableTest(
653                 'Bug 28775: American-style (mdy) short numeric dates',
654                 [ 'Date' ],
655                 [
656                         // American-style dates are month-day-year
657                         [ '11.11.2011' ],
658                         [ '01.11.2011' ],
659                         [ '02.10.2011' ],
660                         [ '03.08.2011' ],
661                         [ '09.11.2011' ]
662                 ],
663                 [
664                         // Sorted by ascending date
665                         [ '01.11.2011' ],
666                         [ '02.10.2011' ],
667                         [ '03.08.2011' ],
668                         [ '09.11.2011' ],
669                         [ '11.11.2011' ]
670                 ],
671                 function ( $table ) {
672                         mw.config.set( 'wgDefaultDateFormat', 'mdy' );
674                         $table.tablesorter();
675                         $table.find( '.headerSort:eq(0)' ).click();
676                 }
677         );
679         tableTest(
680                 'Bug 17141: IPv4 address sorting',
681                 [ 'IP' ],
682                 ipv4,
683                 ipv4Sorted,
684                 function ( $table ) {
685                         $table.tablesorter();
686                         $table.find( '.headerSort:eq(0)' ).click();
687                 }
688         );
689         tableTest(
690                 'Bug 17141: IPv4 address sorting (reverse)',
691                 [ 'IP' ],
692                 ipv4,
693                 reversed( ipv4Sorted ),
694                 function ( $table ) {
695                         $table.tablesorter();
696                         $table.find( '.headerSort:eq(0)' ).click().click();
697                 }
698         );
700         tableTest(
701                 'Accented Characters with custom collation',
702                 [ 'Name' ],
703                 umlautWords,
704                 umlautWordsSorted,
705                 function ( $table ) {
706                         mw.config.set( 'tableSorterCollation', {
707                                 ä: 'ae',
708                                 ö: 'oe',
709                                 ß: 'ss',
710                                 ü: 'ue'
711                         } );
713                         $table.tablesorter();
714                         $table.find( '.headerSort:eq(0)' ).click();
715                 }
716         );
718         tableTest(
719                 'Digraphs with custom collation',
720                 [ 'City' ],
721                 digraphWords,
722                 digraphWordsSorted,
723                 function ( $table ) {
724                         mw.config.set( 'tableSorterCollation', {
725                                 lj: 'lzzzz',
726                                 nj: 'nzzzz'
727                         } );
729                         $table.tablesorter();
730                         $table.find( '.headerSort:eq(0)' ).click();
731                 }
732         );
734         QUnit.test( 'Rowspan not exploded on init', 1, function ( assert ) {
735                 var $table = tableCreate( header, planets );
737                 // Modify the table to have a multiple-row-spanning cell:
738                 // - Remove 2nd cell of 4th row, and, 2nd cell or 5th row.
739                 $table.find( 'tr:eq(3) td:eq(1), tr:eq(4) td:eq(1)' ).remove();
740                 // - Set rowspan for 2nd cell of 3rd row to 3.
741                 //   This covers the removed cell in the 4th and 5th row.
742                 $table.find( 'tr:eq(2) td:eq(1)' ).attr( 'rowspan', '3' );
744                 $table.tablesorter();
746                 assert.equal(
747                         $table.find( 'tr:eq(2) td:eq(1)' ).prop( 'rowSpan' ),
748                         3,
749                         'Rowspan not exploded'
750                 );
751         } );
753         planetsRowspan = [
754                 [ 'Earth', '6051.8' ],
755                 jupiter,
756                 [ 'Mars', '6051.8' ],
757                 mercury,
758                 saturn,
759                 venus
760         ];
761         planetsRowspanII = [ jupiter, mercury, saturn, venus, [ 'Venus', '6371.0' ], [ 'Venus', '3390.0' ] ];
763         tableTest(
764                 'Basic planet table: same value for multiple rows via rowspan',
765                 header,
766                 planets,
767                 planetsRowspan,
768                 function ( $table ) {
769                         // Modify the table to have a multiple-row-spanning cell:
770                         // - Remove 2nd cell of 4th row, and, 2nd cell or 5th row.
771                         $table.find( 'tr:eq(3) td:eq(1), tr:eq(4) td:eq(1)' ).remove();
772                         // - Set rowspan for 2nd cell of 3rd row to 3.
773                         //   This covers the removed cell in the 4th and 5th row.
774                         $table.find( 'tr:eq(2) td:eq(1)' ).attr( 'rowspan', '3' );
776                         $table.tablesorter();
777                         $table.find( '.headerSort:eq(0)' ).click();
778                 }
779         );
780         tableTest(
781                 'Basic planet table: same value for multiple rows via rowspan (sorting initially)',
782                 header,
783                 planets,
784                 planetsRowspan,
785                 function ( $table ) {
786                         // Modify the table to have a multiple-row-spanning cell:
787                         // - Remove 2nd cell of 4th row, and, 2nd cell or 5th row.
788                         $table.find( 'tr:eq(3) td:eq(1), tr:eq(4) td:eq(1)' ).remove();
789                         // - Set rowspan for 2nd cell of 3rd row to 3.
790                         //   This covers the removed cell in the 4th and 5th row.
791                         $table.find( 'tr:eq(2) td:eq(1)' ).attr( 'rowspan', '3' );
793                         $table.tablesorter( { sortList: [
794                                 { 0: 'asc' }
795                         ] } );
796                 }
797         );
798         tableTest(
799                 'Basic planet table: Same value for multiple rows via rowspan II',
800                 header,
801                 planets,
802                 planetsRowspanII,
803                 function ( $table ) {
804                         // Modify the table to have a multiple-row-spanning cell:
805                         // - Remove 1st cell of 4th row, and, 1st cell or 5th row.
806                         $table.find( 'tr:eq(3) td:eq(0), tr:eq(4) td:eq(0)' ).remove();
807                         // - Set rowspan for 1st cell of 3rd row to 3.
808                         //   This covers the removed cell in the 4th and 5th row.
809                         $table.find( 'tr:eq(2) td:eq(0)' ).attr( 'rowspan', '3' );
811                         $table.tablesorter();
812                         $table.find( '.headerSort:eq(0)' ).click();
813                 }
814         );
816         tableTest(
817                 'Complex date parsing I',
818                 [ 'date' ],
819                 complexMDYDates,
820                 complexMDYSorted,
821                 function ( $table ) {
822                         mw.config.set( 'wgDefaultDateFormat', 'mdy' );
824                         $table.tablesorter();
825                         $table.find( '.headerSort:eq(0)' ).click();
826                 }
827         );
829         tableTest(
830                 'Currency parsing I',
831                 [ 'currency' ],
832                 currencyUnsorted,
833                 currencySorted,
834                 function ( $table ) {
835                         $table.tablesorter();
836                         $table.find( '.headerSort:eq(0)' ).click();
837                 }
838         );
840         planetsAscNameLegacy = planetsAscName.slice( 0 );
841         planetsAscNameLegacy[ 4 ] = planetsAscNameLegacy[ 5 ];
842         planetsAscNameLegacy.pop();
844         tableTest(
845                 'Legacy compat with .sortbottom',
846                 header,
847                 planets,
848                 planetsAscNameLegacy,
849                 function ( $table ) {
850                         $table.find( 'tr:last' ).addClass( 'sortbottom' );
851                         $table.tablesorter();
852                         $table.find( '.headerSort:eq(0)' ).click();
853                 }
854         );
856         QUnit.test( 'Test detection routine', 1, function ( assert ) {
857                 var $table;
858                 $table = $(
859                         '<table class="sortable">' +
860                                 '<caption>CAPTION</caption>' +
861                                 '<tr><th>THEAD</th></tr>' +
862                                 '<tr><td>1</td></tr>' +
863                                 '<tr class="sortbottom"><td>text</td></tr>' +
864                                 '</table>'
865                 );
866                 $table.tablesorter();
867                 $table.find( '.headerSort:eq(0)' ).click();
869                 assert.equal(
870                         $table.data( 'tablesorter' ).config.parsers[ 0 ].id,
871                         'number',
872                         'Correctly detected column content skipping sortbottom'
873                 );
874         } );
876         /** FIXME: the diff output is not very readeable. */
877         QUnit.test( 'bug 32047 - caption must be before thead', 1, function ( assert ) {
878                 var $table;
879                 $table = $(
880                         '<table class="sortable">' +
881                                 '<caption>CAPTION</caption>' +
882                                 '<tr><th>THEAD</th></tr>' +
883                                 '<tr><td>A</td></tr>' +
884                                 '<tr><td>B</td></tr>' +
885                                 '<tr class="sortbottom"><td>TFOOT</td></tr>' +
886                                 '</table>'
887                 );
888                 $table.tablesorter();
890                 assert.equal(
891                         $table.children().get( 0 ).nodeName,
892                         'CAPTION',
893                         'First element after <thead> must be <caption> (bug 32047)'
894                 );
895         } );
897         QUnit.test( 'data-sort-value attribute, when available, should override sorting position', 3, function ( assert ) {
898                 var $table, data;
900                 // Example 1: All cells except one cell without data-sort-value,
901                 // which should be sorted at it's text content value.
902                 $table = $(
903                         '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
904                                 '<tbody>' +
905                                 '<tr><td>Cheetah</td></tr>' +
906                                 '<tr><td data-sort-value="Apple">Bird</td></tr>' +
907                                 '<tr><td data-sort-value="Bananna">Ferret</td></tr>' +
908                                 '<tr><td data-sort-value="Drupe">Elephant</td></tr>' +
909                                 '<tr><td data-sort-value="Cherry">Dolphin</td></tr>' +
910                                 '</tbody></table>'
911                 );
912                 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
914                 data = [];
915                 $table.find( 'tbody > tr' ).each( function ( i, tr ) {
916                         $( tr ).find( 'td' ).each( function ( i, td ) {
917                                 data.push( {
918                                         data: $( td ).data( 'sortValue' ),
919                                         text: $( td ).text()
920                                 } );
921                         } );
922                 } );
924                 assert.deepEqual( data, [
925                         {
926                                 data: 'Apple',
927                                 text: 'Bird'
928                         },
929                         {
930                                 data: 'Bananna',
931                                 text: 'Ferret'
932                         },
933                         {
934                                 data: undefined,
935                                 text: 'Cheetah'
936                         },
937                         {
938                                 data: 'Cherry',
939                                 text: 'Dolphin'
940                         },
941                         {
942                                 data: 'Drupe',
943                                 text: 'Elephant'
944                         }
945                 ], 'Order matches expected order (based on data-sort-value attribute values)' );
947                 // Example 2
948                 $table = $(
949                         '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
950                                 '<tbody>' +
951                                 '<tr><td>D</td></tr>' +
952                                 '<tr><td data-sort-value="E">A</td></tr>' +
953                                 '<tr><td>B</td></tr>' +
954                                 '<tr><td>G</td></tr>' +
955                                 '<tr><td data-sort-value="F">C</td></tr>' +
956                                 '</tbody></table>'
957                 );
958                 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
960                 data = [];
961                 $table.find( 'tbody > tr' ).each( function ( i, tr ) {
962                         $( tr ).find( 'td' ).each( function ( i, td ) {
963                                 data.push( {
964                                         data: $( td ).data( 'sortValue' ),
965                                         text: $( td ).text()
966                                 } );
967                         } );
968                 } );
970                 assert.deepEqual( data, [
971                         {
972                                 data: undefined,
973                                 text: 'B'
974                         },
975                         {
976                                 data: undefined,
977                                 text: 'D'
978                         },
979                         {
980                                 data: 'E',
981                                 text: 'A'
982                         },
983                         {
984                                 data: 'F',
985                                 text: 'C'
986                         },
987                         {
988                                 data: undefined,
989                                 text: 'G'
990                         }
991                 ], 'Order matches expected order (based on data-sort-value attribute values)' );
993                 // Example 3: Test that live changes are used from data-sort-value,
994                 // even if they change after the tablesorter is constructed (bug 38152).
995                 $table = $(
996                         '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
997                                 '<tbody>' +
998                                 '<tr><td>D</td></tr>' +
999                                 '<tr><td data-sort-value="1">A</td></tr>' +
1000                                 '<tr><td>B</td></tr>' +
1001                                 '<tr><td data-sort-value="2">G</td></tr>' +
1002                                 '<tr><td>C</td></tr>' +
1003                                 '</tbody></table>'
1004                 );
1005                 // initialize table sorter and sort once
1006                 $table
1007                         .tablesorter()
1008                         .find( '.headerSort:eq(0)' ).click();
1010                 // Change the sortValue data properties (bug 38152)
1011                 // - change data
1012                 $table.find( 'td:contains(A)' ).data( 'sortValue', 3 );
1013                 // - add data
1014                 $table.find( 'td:contains(B)' ).data( 'sortValue', 1 );
1015                 // - remove data, bring back attribute: 2
1016                 $table.find( 'td:contains(G)' ).removeData( 'sortValue' );
1018                 // Now sort again (twice, so it is back at Ascending)
1019                 $table.find( '.headerSort:eq(0)' ).click();
1020                 $table.find( '.headerSort:eq(0)' ).click();
1022                 data = [];
1023                 $table.find( 'tbody > tr' ).each( function ( i, tr ) {
1024                         $( tr ).find( 'td' ).each( function ( i, td ) {
1025                                 data.push( {
1026                                         data: $( td ).data( 'sortValue' ),
1027                                         text: $( td ).text()
1028                                 } );
1029                         } );
1030                 } );
1032                 assert.deepEqual( data, [
1033                         {
1034                                 data: 1,
1035                                 text: 'B'
1036                         },
1037                         {
1038                                 data: 2,
1039                                 text: 'G'
1040                         },
1041                         {
1042                                 data: 3,
1043                                 text: 'A'
1044                         },
1045                         {
1046                                 data: undefined,
1047                                 text: 'C'
1048                         },
1049                         {
1050                                 data: undefined,
1051                                 text: 'D'
1052                         }
1053                 ], 'Order matches expected order, using the current sortValue in $.data()' );
1055         } );
1057         tableTest( 'bug 8115: sort numbers with commas (ascending)',
1058                 [ 'Numbers' ], numbers, numbersAsc,
1059                 function ( $table ) {
1060                         $table.tablesorter();
1061                         $table.find( '.headerSort:eq(0)' ).click();
1062                 }
1063         );
1065         tableTest( 'bug 8115: sort numbers with commas (descending)',
1066                 [ 'Numbers' ], numbers, reversed( numbersAsc ),
1067                 function ( $table ) {
1068                         $table.tablesorter();
1069                         $table.find( '.headerSort:eq(0)' ).click().click();
1070                 }
1071         );
1072         // TODO add numbers sorting tests for bug 8115 with a different language
1074         QUnit.test( 'bug 32888 - Tables inside a tableheader cell', 2, function ( assert ) {
1075                 var $table;
1076                 $table = $(
1077                         '<table class="sortable" id="mw-bug-32888">' +
1078                                 '<tr><th>header<table id="mw-bug-32888-2">' +
1079                                 '<tr><th>1</th><th>2</th></tr>' +
1080                                 '</table></th></tr>' +
1081                                 '<tr><td>A</td></tr>' +
1082                                 '<tr><td>B</td></tr>' +
1083                                 '</table>'
1084                 );
1085                 $table.tablesorter();
1087                 assert.equal(
1088                         $table.find( '> thead:eq(0) > tr > th.headerSort' ).length,
1089                         1,
1090                         'Child tables inside a headercell should not interfere with sortable headers (bug 32888)'
1091                 );
1092                 assert.equal(
1093                         $( '#mw-bug-32888-2' ).find( 'th.headerSort' ).length,
1094                         0,
1095                         'The headers of child tables inside a headercell should not be sortable themselves (bug 32888)'
1096                 );
1097         } );
1099         tableTest(
1100                 'Correct date sorting I',
1101                 [ 'date' ],
1102                 correctDateSorting1,
1103                 correctDateSortingSorted1,
1104                 function ( $table ) {
1105                         mw.config.set( 'wgDefaultDateFormat', 'mdy' );
1107                         $table.tablesorter();
1108                         $table.find( '.headerSort:eq(0)' ).click();
1109                 }
1110         );
1112         tableTest(
1113                 'Correct date sorting II',
1114                 [ 'date' ],
1115                 correctDateSorting2,
1116                 correctDateSortingSorted2,
1117                 function ( $table ) {
1118                         mw.config.set( 'wgDefaultDateFormat', 'dmy' );
1120                         $table.tablesorter();
1121                         $table.find( '.headerSort:eq(0)' ).click();
1122                 }
1123         );
1125         tableTest(
1126                 'ISO date sorting',
1127                 [ 'isoDate' ],
1128                 isoDateSorting,
1129                 isoDateSortingSorted,
1130                 function ( $table ) {
1131                         mw.config.set( 'wgDefaultDateFormat', 'dmy' );
1133                         $table.tablesorter();
1134                         $table.find( '.headerSort:eq(0)' ).click();
1135                 }
1136         );
1138         QUnit.test( 'Sorting images using alt text', 1, function ( assert ) {
1139                 var $table = $(
1140                         '<table class="sortable">' +
1141                                 '<tr><th>THEAD</th></tr>' +
1142                                 '<tr><td><img alt="2"/></td></tr>' +
1143                                 '<tr><td>1</td></tr>' +
1144                                 '</table>'
1145                 );
1146                 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
1148                 assert.equal(
1149                         $table.find( 'td' ).first().text(),
1150                         '1',
1151                         'Applied correct sorting order'
1152                 );
1153         } );
1155         QUnit.test( 'Sorting images using alt text (complex)', 1, function ( assert ) {
1156                 var $table = $(
1157                         '<table class="sortable">' +
1158                                 '<tr><th>THEAD</th></tr>' +
1159                                 '<tr><td><img alt="D" />A</td></tr>' +
1160                                 '<tr><td>CC</td></tr>' +
1161                                 '<tr><td><a><img alt="A" /></a>F</tr>' +
1162                                 '<tr><td><img alt="A" /><strong>E</strong></tr>' +
1163                                 '<tr><td><strong><img alt="A" />D</strong></tr>' +
1164                                 '<tr><td><img alt="A" />C</tr>' +
1165                                 '</table>'
1166                 );
1167                 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
1169                 assert.equal(
1170                         $table.find( 'td' ).text(),
1171                         'CDEFCCA',
1172                         'Applied correct sorting order'
1173                 );
1174         } );
1176         QUnit.test( 'Sorting images using alt text (with format autodetection)', 1, function ( assert ) {
1177                 var $table = $(
1178                         '<table class="sortable">' +
1179                                 '<tr><th>THEAD</th></tr>' +
1180                                 '<tr><td><img alt="1" />7</td></tr>' +
1181                                 '<tr><td>1<img alt="6" /></td></tr>' +
1182                                 '<tr><td>5</td></tr>' +
1183                                 '<tr><td>4</td></tr>' +
1184                                 '</table>'
1185                 );
1186                 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
1188                 assert.equal(
1189                         $table.find( 'td' ).text(),
1190                         '4517',
1191                         'Applied correct sorting order'
1192                 );
1193         } );
1195         QUnit.test( 'bug 38911 - The row with the largest amount of columns should receive the sort indicators', 3, function ( assert ) {
1196                 var $table = $(
1197                         '<table class="sortable">' +
1198                                 '<thead>' +
1199                                 '<tr><th rowspan="2" id="A1">A1</th><th colspan="2">B2a</th></tr>' +
1200                                 '<tr><th id="B2b">B2b</th><th id="C2b">C2b</th></tr>' +
1201                                 '</thead>' +
1202                                 '<tr><td>A</td><td>Aa</td><td>Ab</td></tr>' +
1203                                 '<tr><td>B</td><td>Ba</td><td>Bb</td></tr>' +
1204                                 '</table>'
1205                 );
1206                 $table.tablesorter();
1208                 assert.equal(
1209                         $table.find( '#A1' ).attr( 'class' ),
1210                         'headerSort',
1211                         'The first column of the first row should be sortable'
1212                 );
1213                 assert.equal(
1214                         $table.find( '#B2b' ).attr( 'class' ),
1215                         'headerSort',
1216                         'The th element of the 2nd row of the 2nd column should be sortable'
1217                 );
1218                 assert.equal(
1219                         $table.find( '#C2b' ).attr( 'class' ),
1220                         'headerSort',
1221                         'The th element of the 2nd row of the 3rd column should be sortable'
1222                 );
1223         } );
1225         QUnit.test( 'rowspans in table headers should prefer the last row when rows are equal in length', 2, function ( assert ) {
1226                 var $table = $(
1227                         '<table class="sortable">' +
1228                                 '<thead>' +
1229                                 '<tr><th rowspan="2" id="A1">A1</th><th>B2a</th></tr>' +
1230                                 '<tr><th id="B2b">B2b</th></tr>' +
1231                                 '</thead>' +
1232                                 '<tr><td>A</td><td>Aa</td></tr>' +
1233                                 '<tr><td>B</td><td>Ba</td></tr>' +
1234                                 '</table>'
1235                 );
1236                 $table.tablesorter();
1238                 assert.equal(
1239                         $table.find( '#A1' ).attr( 'class' ),
1240                         'headerSort',
1241                         'The first column of the first row should be sortable'
1242                 );
1243                 assert.equal(
1244                         $table.find( '#B2b' ).attr( 'class' ),
1245                         'headerSort',
1246                         'The th element of the 2nd row of the 2nd column should be sortable'
1247                 );
1248         } );
1250         QUnit.test( 'holes in the table headers should not throw JS errors', 2, function ( assert ) {
1251                 var $table = $(
1252                         '<table class="sortable">' +
1253                                 '<thead>' +
1254                                 '<tr><th id="A1">A1</th><th>B1</th><th id="C1" rowspan="2">C1</th></tr>' +
1255                                 '<tr><th id="A2">A2</th></tr>' +
1256                                 '</thead>' +
1257                                 '<tr><td>A</td><td>Aa</td><td>Aaa</td></tr>' +
1258                                 '<tr><td>B</td><td>Ba</td><td>Bbb</td></tr>' +
1259                                 '</table>'
1260                 );
1261                 $table.tablesorter();
1262                 assert.equal( $table.find( '#A2' ).data( 'headerIndex' ),
1263                         undefined,
1264                         'A2 should not be a sort header'
1265                 );
1266                 assert.equal( $table.find( '#C1' ).data( 'headerIndex' ),
1267                         2,
1268                         'C1 should be a sort header'
1269                 );
1270         } );
1272         // bug 53527
1273         QUnit.test( 'td cells in thead should not be taken into account for longest row calculation', 2, function ( assert ) {
1274                 var $table = $(
1275                         '<table class="sortable">' +
1276                                 '<thead>' +
1277                                 '<tr><th id="A1">A1</th><th>B1</th><td id="C1">C1</td></tr>' +
1278                                 '<tr><th id="A2">A2</th><th>B2</th><th id="C2">C2</th></tr>' +
1279                                 '</thead>' +
1280                                 '</table>'
1281                 );
1282                 $table.tablesorter();
1283                 assert.equal( $table.find( '#C2' ).data( 'headerIndex' ),
1284                         2,
1285                         'C2 should be a sort header'
1286                 );
1287                 assert.equal( $table.find( '#C1' ).data( 'headerIndex' ),
1288                         undefined,
1289                         'C1 should not be a sort header'
1290                 );
1291         } );
1293         // bug 41889 - exploding rowspans in more complex cases
1294         tableTestHTML(
1295                 'Rowspan exploding with row headers',
1296                 '<table class="sortable">' +
1297                         '<thead><tr><th id="sortme">n</th><th>foo</th><th>bar</th><th>baz</th></tr></thead>' +
1298                         '<tbody>' +
1299                         '<tr><td>1</td><th rowspan="2">foo</th><td rowspan="2">bar</td><td>baz</td></tr>' +
1300                         '<tr><td>2</td><td>baz</td></tr>' +
1301                         '</tbody></table>',
1302                 [
1303                         [ '1', 'foo', 'bar', 'baz' ],
1304                         [ '2', 'foo', 'bar', 'baz' ]
1305                 ]
1306         );
1308         // bug 53211 - exploding rowspans in more complex cases
1309         QUnit.test(
1310                 'Rowspan exploding with row headers and colspans', 1, function ( assert ) {
1311                         var $table = $( '<table class="sortable">' +
1312                                 '<thead><tr><th rowspan="2">n</th><th colspan="2">foo</th><th rowspan="2">baz</th></tr>' +
1313                                 '<tr><th>foo</th><th>bar</th></tr></thead>' +
1314                                 '<tbody>' +
1315                                 '<tr><td>1</td><td>foo</td><td>bar</td><td>baz</td></tr>' +
1316                                 '<tr><td>2</td><td>foo</td><td>bar</td><td>baz</td></tr>' +
1317                                 '</tbody></table>' );
1319                         $table.tablesorter();
1320                         assert.equal( $table.find( 'tr:eq(1) th:eq(1)' ).data( 'headerIndex' ),
1321                                 2,
1322                                 'Incorrect index of sort header'
1323                         );
1324                 }
1325         );
1327         tableTestHTML(
1328                 'Rowspan exploding with colspanned cells',
1329                 '<table class="sortable">' +
1330                         '<thead><tr><th id="sortme">n</th><th>foo</th><th>bar</th><th>baz</th></tr></thead>' +
1331                         '<tbody>' +
1332                         '<tr><td>1</td><td>foo</td><td>bar</td><td rowspan="2">baz</td></tr>' +
1333                         '<tr><td>2</td><td colspan="2">foobar</td></tr>' +
1334                         '</tbody></table>',
1335                 [
1336                         [ '1', 'foo', 'bar', 'baz' ],
1337                         [ '2', 'foobar', 'baz' ]
1338                 ]
1339         );
1341         tableTestHTML(
1342                 'Rowspan exploding with colspanned cells (2)',
1343                 '<table class="sortable">' +
1344                         '<thead><tr><th>n</th><th>foo</th><th>bar</th><th>baz</th><th id="sortme">n2</th></tr></thead>' +
1345                         '<tbody>' +
1346                         '<tr><td>1</td><td>foo</td><td>bar</td><td rowspan="2">baz</td><td>2</td></tr>' +
1347                         '<tr><td>2</td><td colspan="2">foobar</td><td>1</td></tr>' +
1348                         '</tbody></table>',
1349                 [
1350                         [ '2', 'foobar', 'baz', '1' ],
1351                         [ '1', 'foo', 'bar', 'baz', '2' ]
1352                 ]
1353         );
1355         tableTestHTML(
1356                 'Rowspan exploding with rightmost rows spanning most',
1357                 '<table class="sortable">' +
1358                         '<thead><tr><th id="sortme">n</th><th>foo</th><th>bar</th></tr></thead>' +
1359                         '<tbody>' +
1360                         '<tr><td>1</td><td rowspan="2">foo</td><td rowspan="4">bar</td></tr>' +
1361                         '<tr><td>2</td></tr>' +
1362                         '<tr><td>3</td><td rowspan="2">foo</td></tr>' +
1363                         '<tr><td>4</td></tr>' +
1364                         '</tbody></table>',
1365                 [
1366                         [ '1', 'foo', 'bar' ],
1367                         [ '2', 'foo', 'bar' ],
1368                         [ '3', 'foo', 'bar' ],
1369                         [ '4', 'foo', 'bar' ]
1370                 ]
1371         );
1373         tableTestHTML(
1374                 'Rowspan exploding with rightmost rows spanning most (2)',
1375                 '<table class="sortable">' +
1376                         '<thead><tr><th id="sortme">n</th><th>foo</th><th>bar</th><th>baz</th></tr></thead>' +
1377                         '<tbody>' +
1378                         '<tr><td>1</td><td rowspan="2">foo</td><td rowspan="4">bar</td><td>baz</td></tr>' +
1379                         '<tr><td>2</td><td>baz</td></tr>' +
1380                         '<tr><td>3</td><td rowspan="2">foo</td><td>baz</td></tr>' +
1381                         '<tr><td>4</td><td>baz</td></tr>' +
1382                         '</tbody></table>',
1383                 [
1384                         [ '1', 'foo', 'bar', 'baz' ],
1385                         [ '2', 'foo', 'bar', 'baz' ],
1386                         [ '3', 'foo', 'bar', 'baz' ],
1387                         [ '4', 'foo', 'bar', 'baz' ]
1388                 ]
1389         );
1391         tableTestHTML(
1392                 'Rowspan exploding with row-and-colspanned cells',
1393                 '<table class="sortable">' +
1394                         '<thead><tr><th id="sortme">n</th><th>foo1</th><th>foo2</th><th>bar</th><th>baz</th></tr></thead>' +
1395                         '<tbody>' +
1396                         '<tr><td>1</td><td rowspan="2">foo1</td><td rowspan="2">foo2</td><td rowspan="4">bar</td><td>baz</td></tr>' +
1397                         '<tr><td>2</td><td>baz</td></tr>' +
1398                         '<tr><td>3</td><td colspan="2" rowspan="2">foo</td><td>baz</td></tr>' +
1399                         '<tr><td>4</td><td>baz</td></tr>' +
1400                         '</tbody></table>',
1401                 [
1402                         [ '1', 'foo1', 'foo2', 'bar', 'baz' ],
1403                         [ '2', 'foo1', 'foo2', 'bar', 'baz' ],
1404                         [ '3', 'foo', 'bar', 'baz' ],
1405                         [ '4', 'foo', 'bar', 'baz' ]
1406                 ]
1407         );
1409         tableTestHTML(
1410                 'Rowspan exploding with uneven rowspan layout',
1411                 '<table class="sortable">' +
1412                         '<thead><tr><th id="sortme">n</th><th>foo1</th><th>foo2</th><th>foo3</th><th>bar</th><th>baz</th></tr></thead>' +
1413                         '<tbody>' +
1414                         '<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>' +
1415                         '<tr><td>2</td><td rowspan="3">bar</td><td>baz</td></tr>' +
1416                         '<tr><td>3</td><td rowspan="2">foo1</td><td rowspan="2">foo2</td><td rowspan="2">foo3</td><td>baz</td></tr>' +
1417                         '<tr><td>4</td><td>baz</td></tr>' +
1418                         '</tbody></table>',
1419                 [
1420                         [ '1', 'foo1', 'foo2', 'foo3', 'bar', 'baz' ],
1421                         [ '2', 'foo1', 'foo2', 'foo3', 'bar', 'baz' ],
1422                         [ '3', 'foo1', 'foo2', 'foo3', 'bar', 'baz' ],
1423                         [ '4', 'foo1', 'foo2', 'foo3', 'bar', 'baz' ]
1424                 ]
1425         );
1427         QUnit.test( 'bug 105731 - incomplete rows in table body', 3, function ( assert ) {
1428                 var $table, parsers;
1429                 $table = $(
1430                         '<table class="sortable">' +
1431                                 '<tr><th>A</th><th>B</th></tr>' +
1432                                 '<tr><td>3</td></tr>' +
1433                                 '<tr><td>1</td><td>2</td></tr>' +
1434                                 '</table>'
1435                 );
1436                 $table.tablesorter();
1437                 $table.find( '.headerSort:eq(0)' ).click();
1438                 // now the first row have 2 columns
1439                 $table.find( '.headerSort:eq(1)' ).click();
1441                 parsers = $table.data( 'tablesorter' ).config.parsers;
1443                 assert.equal(
1444                         parsers.length,
1445                         2,
1446                         'detectParserForColumn() detect 2 parsers'
1447                 );
1449                 assert.equal(
1450                         parsers[ 1 ].id,
1451                         'number',
1452                         'detectParserForColumn() detect parser.id "number" for second column'
1453                 );
1455                 assert.equal(
1456                         parsers[ 1 ].format( $table.find( 'tbody > tr > td:eq(1)' ).text() ),
1457                         -Infinity,
1458                         'empty cell is sorted as number -Infinity'
1459                 );
1460         } );
1462         QUnit.test( 'bug T114721 - use of expand-child class', 2, function ( assert ) {
1463                 var $table, parsers;
1464                 $table = $(
1465                         '<table class="sortable">' +
1466                                 '<tr><th>A</th><th>B</th></tr>' +
1467                                 '<tr><td>b</td><td>4</td></tr>' +
1468                                 '<tr class="expand-child"><td colspan="2">some text follow b</td></tr>' +
1469                                 '<tr><td>a</td><td>2</td></tr>' +
1470                                 '<tr class="expand-child"><td colspan="2">some text follow a</td></tr>' +
1471                                 '<tr class="expand-child"><td colspan="2">more text</td></tr>' +
1472                                 '</table>'
1473                 );
1474                 $table.tablesorter();
1475                 $table.find( '.headerSort:eq(0)' ).click();
1477                 assert.deepEqual(
1478                         tableExtract( $table ),
1479                         [
1480                                 [ 'a', '2' ],
1481                                 [ 'some text follow a' ],
1482                                 [ 'more text' ],
1483                                 [ 'b', '4' ],
1484                                 [ 'some text follow b' ]
1485                         ],
1486                         'row with expand-child class follow above row'
1487                 );
1489                 parsers = $table.data( 'tablesorter' ).config.parsers;
1490                 assert.equal(
1491                         parsers[ 1 ].id,
1492                         'number',
1493                         'detectParserForColumn() detect parser.id "number" for second column'
1494                 );
1495         } );
1497 }( jQuery, mediaWiki ) );