Merge "Minor CSS cleanup for Vector and Monobook skins"
[mediawiki.git] / tests / qunit / suites / resources / jquery / jquery.tablesorter.test.js
blob1a380a57618340756bfa57162abd735f6d447137
1 ( function ( $, mw ) {
2         /*jshint onevar: false */
4         var config = {
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                 wgContentLanguage: 'en'
9         };
11         QUnit.module( 'jquery.tablesorter', QUnit.newMwEnvironment( { config: config } ) );
13         /**
14          * Create an HTML table from an array of row arrays containing text strings.
15          * First row will be header row. No fancy rowspan/colspan stuff.
16          *
17          * @param {String[]} header
18          * @param {String[][]} data
19          * @return jQuery
20          */
21         function tableCreate( header, data ) {
22                 var i,
23                         $table = $( '<table class="sortable"><thead></thead><tbody></tbody></table>' ),
24                         $thead = $table.find( 'thead' ),
25                         $tbody = $table.find( 'tbody' ),
26                         $tr = $( '<tr>' );
28                 $.each( header, function ( i, str ) {
29                         var $th = $( '<th>' );
30                         $th.text( str ).appendTo( $tr );
31                 } );
32                 $tr.appendTo( $thead );
34                 for ( i = 0; i < data.length; i++ ) {
35                         /*jshint loopfunc: true */
36                         $tr = $( '<tr>' );
37                         $.each( data[i], function ( j, str ) {
38                                 var $td = $( '<td>' );
39                                 $td.text( str ).appendTo( $tr );
40                         } );
41                         $tr.appendTo( $tbody );
42                 }
43                 return $table;
44         }
46         /**
47          * Extract text from table.
48          *
49          * @param {jQuery} $table
50          * @return String[][]
51          */
52         function tableExtract( $table ) {
53                 var data = [];
55                 $table.find( 'tbody' ).find( 'tr' ).each( function ( i, tr ) {
56                         var row = [];
57                         $( tr ).find( 'td,th' ).each( function ( i, td ) {
58                                 row.push( $( td ).text() );
59                         } );
60                         data.push( row );
61                 } );
62                 return data;
63         }
65         /**
66          * Run a table test by building a table with the given data,
67          * running some callback on it, then checking the results.
68          *
69          * @param {String} msg text to pass on to qunit for the comparison
70          * @param {String[]} header cols to make the table
71          * @param {String[][]} data rows/cols to make the table
72          * @param {String[][]} expected rows/cols to compare against at end
73          * @param {function($table)} callback something to do with the table before we compare
74          */
75         function tableTest( msg, header, data, expected, callback ) {
76                 QUnit.test( msg, 1, function ( assert ) {
77                         var $table = tableCreate( header, data );
79                         // Give caller a chance to set up sorting and manipulate the table.
80                         callback( $table );
82                         // Table sorting is done synchronously; if it ever needs to change back
83                         // to asynchronous, we'll need a timeout or a callback here.
84                         var extracted = tableExtract( $table );
85                         assert.deepEqual( extracted, expected, msg );
86                 } );
87         }
89         /**
90          * Run a table test by building a table with the given HTML,
91          * running some callback on it, then checking the results.
92          *
93          * @param {String} msg text to pass on to qunit for the comparison
94          * @param {String} HTML to make the table
95          * @param {String[][]} expected rows/cols to compare against at end
96          * @param {function($table)} callback something to do with the table before we compare
97          */
98         function tableTestHTML( msg, html, expected, callback ) {
99                 QUnit.test( msg, 1, function ( assert ) {
100                         var $table = $( html );
102                         // Give caller a chance to set up sorting and manipulate the table.
103                         if ( callback ) {
104                                 callback( $table );
105                         } else {
106                                 $table.tablesorter();
107                                 $table.find( '#sortme' ).click();
108                         }
110                         // Table sorting is done synchronously; if it ever needs to change back
111                         // to asynchronous, we'll need a timeout or a callback here.
112                         var extracted = tableExtract( $table );
113                         assert.deepEqual( extracted, expected, msg );
114                 } );
115         }
117         function reversed( arr ) {
118                 // Clone array
119                 var arr2 = arr.slice( 0 );
121                 arr2.reverse();
123                 return arr2;
124         }
126         // Sample data set using planets named and their radius
127         var header = [ 'Planet' , 'Radius (km)'],
128                 mercury = [ 'Mercury', '2439.7' ],
129                 venus = [ 'Venus'  , '6051.8' ],
130                 earth = [ 'Earth'  , '6371.0' ],
131                 mars = [ 'Mars'   , '3390.0' ],
132                 jupiter = [ 'Jupiter', '69911' ],
133                 saturn = [ 'Saturn' , '58232' ];
135         // Initial data set
136         var planets = [mercury, venus, earth, mars, jupiter, saturn];
137         var ascendingName = [earth, jupiter, mars, mercury, saturn, venus];
138         var ascendingRadius = [mercury, mars, venus, earth, saturn, jupiter];
140         tableTest(
141                 'Basic planet table: sorting initially - ascending by name',
142                 header,
143                 planets,
144                 ascendingName,
145                 function ( $table ) {
146                         $table.tablesorter( { sortList: [
147                                 { 0: 'asc' }
148                         ] } );
149                 }
150         );
151         tableTest(
152                 'Basic planet table: sorting initially - descending by radius',
153                 header,
154                 planets,
155                 reversed( ascendingRadius ),
156                 function ( $table ) {
157                         $table.tablesorter( { sortList: [
158                                 { 1: 'desc' }
159                         ] } );
160                 }
161         );
162         tableTest(
163                 'Basic planet table: ascending by name',
164                 header,
165                 planets,
166                 ascendingName,
167                 function ( $table ) {
168                         $table.tablesorter();
169                         $table.find( '.headerSort:eq(0)' ).click();
170                 }
171         );
172         tableTest(
173                 'Basic planet table: ascending by name a second time',
174                 header,
175                 planets,
176                 ascendingName,
177                 function ( $table ) {
178                         $table.tablesorter();
179                         $table.find( '.headerSort:eq(0)' ).click();
180                 }
181         );
182         tableTest(
183                 'Basic planet table: descending by name',
184                 header,
185                 planets,
186                 reversed( ascendingName ),
187                 function ( $table ) {
188                         $table.tablesorter();
189                         $table.find( '.headerSort:eq(0)' ).click().click();
190                 }
191         );
192         tableTest(
193                 'Basic planet table: ascending radius',
194                 header,
195                 planets,
196                 ascendingRadius,
197                 function ( $table ) {
198                         $table.tablesorter();
199                         $table.find( '.headerSort:eq(1)' ).click();
200                 }
201         );
202         tableTest(
203                 'Basic planet table: descending radius',
204                 header,
205                 planets,
206                 reversed( ascendingRadius ),
207                 function ( $table ) {
208                         $table.tablesorter();
209                         $table.find( '.headerSort:eq(1)' ).click().click();
210                 }
211         );
213         // Sample data set to test multiple column sorting
214         header = [ 'column1' , 'column2'];
215         var
216                 a1 = [ 'A', '1' ],
217                 a2 = [ 'A', '2' ],
218                 a3 = [ 'A', '3' ],
219                 b1 = [ 'B', '1' ],
220                 b2 = [ 'B', '2' ],
221                 b3 = [ 'B', '3' ];
222         var initial = [a2, b3, a1, a3, b2, b1];
223         var asc = [a1, a2, a3, b1, b2, b3];
224         var descasc = [b1, b2, b3, a1, a2, a3];
226         tableTest(
227                 'Sorting multiple columns by passing sort list',
228                 header,
229                 initial,
230                 asc,
231                 function ( $table ) {
232                         $table.tablesorter(
233                                 { sortList: [
234                                         { 0: 'asc' },
235                                         { 1: 'asc' }
236                                 ] }
237                         );
238                 }
239         );
240         tableTest(
241                 'Sorting multiple columns by programmatically triggering sort()',
242                 header,
243                 initial,
244                 descasc,
245                 function ( $table ) {
246                         $table.tablesorter();
247                         $table.data( 'tablesorter' ).sort(
248                                 [
249                                         { 0: 'desc' },
250                                         { 1: 'asc' }
251                                 ]
252                         );
253                 }
254         );
255         tableTest(
256                 'Reset to initial sorting by triggering sort() without any parameters',
257                 header,
258                 initial,
259                 asc,
260                 function ( $table ) {
261                         $table.tablesorter(
262                                 { sortList: [
263                                         { 0: 'asc' },
264                                         { 1: 'asc' }
265                                 ] }
266                         );
267                         $table.data( 'tablesorter' ).sort(
268                                 [
269                                         { 0: 'desc' },
270                                         { 1: 'asc' }
271                                 ]
272                         );
273                         $table.data( 'tablesorter' ).sort();
274                 }
275         );
276         QUnit.test( 'Reset sorting making table appear unsorted', 3, function ( assert ) {
277                 var $table = tableCreate( header, initial );
278                 $table.tablesorter(
279                         { sortList: [
280                                 { 0: 'desc' },
281                                 { 1: 'asc' }
282                         ] }
283                 );
284                 $table.data( 'tablesorter' ).sort( [] );
286                 assert.equal(
287                         $table.find( 'th.headerSortUp' ).length + $table.find( 'th.headerSortDown' ).length,
288                         0,
289                         'No sort specific sort classes addign to header cells'
290                 );
292                 assert.equal(
293                         $table.find( 'th' ).first().attr( 'title' ),
294                         mw.msg( 'sort-ascending' ),
295                         'First header cell has default title'
296                 );
298                 assert.equal(
299                         $table.find( 'th' ).first().attr( 'title' ),
300                         $table.find( 'th' ).last().attr( 'title' ),
301                         'Both header cells\' titles match'
302                 );
303         } );
305         // Sorting with colspans
306         header = [ 'column1a' , 'column1b', 'column1c', 'column2' ];
307         var
308                 aaa1 = [ 'A', 'A', 'A', '1' ],
309                 aab5 = [ 'A', 'A', 'B', '5' ],
310                 abc3 = [ 'A', 'B', 'C', '3' ],
311                 bbc2 = [ 'B', 'B', 'C', '2' ],
312                 caa4 = [ 'C', 'A', 'A', '4' ];
313         // initial is already declared above
314         initial = [ aab5, aaa1, abc3, bbc2, caa4 ];
315         tableTest( 'Sorting with colspanned headers: spanned column',
316                 header,
317                 initial,
318                 [ aaa1, aab5, abc3, bbc2, caa4 ],
319                 function ( $table ) {
320                         // Make colspanned header for test
321                         $table.find( 'tr:eq(0) th:eq(1), tr:eq(0) th:eq(2)' ).remove();
322                         $table.find( 'tr:eq(0) th:eq(0)' ).prop( 'colspan', '3' );
324                         $table.tablesorter();
325                         $table.find( '.headerSort:eq(0)' ).click();
326                 }
327         );
328         tableTest( 'Sorting with colspanned headers: subsequent column',
329                 header,
330                 initial,
331                 [ aaa1, bbc2, abc3, caa4, aab5 ],
332                 function ( $table ) {
333                         // Make colspanned header for test
334                         $table.find( 'tr:eq(0) th:eq(1), tr:eq(0) th:eq(2)' ).remove();
335                         $table.find( 'tr:eq(0) th:eq(0)' ).prop( 'colspan', '3' );
337                         $table.tablesorter();
338                         $table.find( '.headerSort:eq(1)' ).click();
339                 }
340         );
342         // Regression tests!
343         tableTest(
344                 'Bug 28775: German-style (dmy) short numeric dates',
345                 ['Date'],
346                 [
347                         // German-style dates are day-month-year
348                         ['11.11.2011'],
349                         ['01.11.2011'],
350                         ['02.10.2011'],
351                         ['03.08.2011'],
352                         ['09.11.2011']
353                 ],
354                 [
355                         // Sorted by ascending date
356                         ['03.08.2011'],
357                         ['02.10.2011'],
358                         ['01.11.2011'],
359                         ['09.11.2011'],
360                         ['11.11.2011']
361                 ],
362                 function ( $table ) {
363                         mw.config.set( 'wgDefaultDateFormat', 'dmy' );
364                         mw.config.set( 'wgContentLanguage', 'de' );
366                         $table.tablesorter();
367                         $table.find( '.headerSort:eq(0)' ).click();
368                 }
369         );
371         tableTest(
372                 'Bug 28775: American-style (mdy) short numeric dates',
373                 ['Date'],
374                 [
375                         // American-style dates are month-day-year
376                         ['11.11.2011'],
377                         ['01.11.2011'],
378                         ['02.10.2011'],
379                         ['03.08.2011'],
380                         ['09.11.2011']
381                 ],
382                 [
383                         // Sorted by ascending date
384                         ['01.11.2011'],
385                         ['02.10.2011'],
386                         ['03.08.2011'],
387                         ['09.11.2011'],
388                         ['11.11.2011']
389                 ],
390                 function ( $table ) {
391                         mw.config.set( 'wgDefaultDateFormat', 'mdy' );
393                         $table.tablesorter();
394                         $table.find( '.headerSort:eq(0)' ).click();
395                 }
396         );
398         var ipv4 = [
399                 // Some randomly generated fake IPs
400                 ['45.238.27.109'],
401                 ['44.172.9.22'],
402                 ['247.240.82.209'],
403                 ['204.204.132.158'],
404                 ['170.38.91.162'],
405                 ['197.219.164.9'],
406                 ['45.68.154.72'],
407                 ['182.195.149.80']
408         ];
409         var ipv4Sorted = [
410                 // Sort order should go octet by octet
411                 ['44.172.9.22'],
412                 ['45.68.154.72'],
413                 ['45.238.27.109'],
414                 ['170.38.91.162'],
415                 ['182.195.149.80'],
416                 ['197.219.164.9'],
417                 ['204.204.132.158'],
418                 ['247.240.82.209']
419         ];
421         tableTest(
422                 'Bug 17141: IPv4 address sorting',
423                 ['IP'],
424                 ipv4,
425                 ipv4Sorted,
426                 function ( $table ) {
427                         $table.tablesorter();
428                         $table.find( '.headerSort:eq(0)' ).click();
429                 }
430         );
431         tableTest(
432                 'Bug 17141: IPv4 address sorting (reverse)',
433                 ['IP'],
434                 ipv4,
435                 reversed( ipv4Sorted ),
436                 function ( $table ) {
437                         $table.tablesorter();
438                         $table.find( '.headerSort:eq(0)' ).click().click();
439                 }
440         );
442         var umlautWords = [
443                 // Some words with Umlauts
444                 ['Günther'],
445                 ['Peter'],
446                 ['Björn'],
447                 ['Bjorn'],
448                 ['Apfel'],
449                 ['Äpfel'],
450                 ['Strasse'],
451                 ['Sträßschen']
452         ];
454         var umlautWordsSorted = [
455                 // Some words with Umlauts
456                 ['Äpfel'],
457                 ['Apfel'],
458                 ['Björn'],
459                 ['Bjorn'],
460                 ['Günther'],
461                 ['Peter'],
462                 ['Sträßschen'],
463                 ['Strasse']
464         ];
466         tableTest(
467                 'Accented Characters with custom collation',
468                 ['Name'],
469                 umlautWords,
470                 umlautWordsSorted,
471                 function ( $table ) {
472                         mw.config.set( 'tableSorterCollation', {
473                                 'ä': 'ae',
474                                 'ö': 'oe',
475                                 'ß': 'ss',
476                                 'ü': 'ue'
477                         } );
479                         $table.tablesorter();
480                         $table.find( '.headerSort:eq(0)' ).click();
481                 }
482         );
484         QUnit.test( 'Rowspan not exploded on init', 1, function ( assert ) {
485                 var $table = tableCreate( header, planets );
487                 // Modify the table to have a multiple-row-spanning cell:
488                 // - Remove 2nd cell of 4th row, and, 2nd cell or 5th row.
489                 $table.find( 'tr:eq(3) td:eq(1), tr:eq(4) td:eq(1)' ).remove();
490                 // - Set rowspan for 2nd cell of 3rd row to 3.
491                 //   This covers the removed cell in the 4th and 5th row.
492                 $table.find( 'tr:eq(2) td:eq(1)' ).prop( 'rowspan', '3' );
494                 $table.tablesorter();
496                 assert.equal(
497                         $table.find( 'tr:eq(2) td:eq(1)' ).prop( 'rowspan' ),
498                         3,
499                         'Rowspan not exploded'
500                 );
501         } );
503         var planetsRowspan = [
504                 [ 'Earth', '6051.8' ],
505                 jupiter,
506                 [ 'Mars', '6051.8' ],
507                 mercury,
508                 saturn,
509                 venus
510         ];
511         var planetsRowspanII = [ jupiter, mercury, saturn, venus, [ 'Venus', '6371.0' ], [ 'Venus', '3390.0' ] ];
513         tableTest(
514                 'Basic planet table: same value for multiple rows via rowspan',
515                 header,
516                 planets,
517                 planetsRowspan,
518                 function ( $table ) {
519                         // Modify the table to have a multiple-row-spanning cell:
520                         // - Remove 2nd cell of 4th row, and, 2nd cell or 5th row.
521                         $table.find( 'tr:eq(3) td:eq(1), tr:eq(4) td:eq(1)' ).remove();
522                         // - Set rowspan for 2nd cell of 3rd row to 3.
523                         //   This covers the removed cell in the 4th and 5th row.
524                         $table.find( 'tr:eq(2) td:eq(1)' ).prop( 'rowspan', '3' );
526                         $table.tablesorter();
527                         $table.find( '.headerSort:eq(0)' ).click();
528                 }
529         );
530         tableTest(
531                 'Basic planet table: same value for multiple rows via rowspan (sorting initially)',
532                 header,
533                 planets,
534                 planetsRowspan,
535                 function ( $table ) {
536                         // Modify the table to have a multiple-row-spanning cell:
537                         // - Remove 2nd cell of 4th row, and, 2nd cell or 5th row.
538                         $table.find( 'tr:eq(3) td:eq(1), tr:eq(4) td:eq(1)' ).remove();
539                         // - Set rowspan for 2nd cell of 3rd row to 3.
540                         //   This covers the removed cell in the 4th and 5th row.
541                         $table.find( 'tr:eq(2) td:eq(1)' ).prop( 'rowspan', '3' );
543                         $table.tablesorter( { sortList: [
544                                 { 0: 'asc' }
545                         ] } );
546                 }
547         );
548         tableTest(
549                 'Basic planet table: Same value for multiple rows via rowspan II',
550                 header,
551                 planets,
552                 planetsRowspanII,
553                 function ( $table ) {
554                         // Modify the table to have a multiple-row-spanning cell:
555                         // - Remove 1st cell of 4th row, and, 1st cell or 5th row.
556                         $table.find( 'tr:eq(3) td:eq(0), tr:eq(4) td:eq(0)' ).remove();
557                         // - Set rowspan for 1st cell of 3rd row to 3.
558                         //   This covers the removed cell in the 4th and 5th row.
559                         $table.find( 'tr:eq(2) td:eq(0)' ).prop( 'rowspan', '3' );
561                         $table.tablesorter();
562                         $table.find( '.headerSort:eq(0)' ).click();
563                 }
564         );
566         var complexMDYDates = [
567                 // Some words with Umlauts
568                 ['January, 19 2010'],
569                 ['April 21 1991'],
570                 ['04 22 1991'],
571                 ['5.12.1990'],
572                 ['December 12 \'10']
573         ];
575         var complexMDYSorted = [
576                 ['5.12.1990'],
577                 ['April 21 1991'],
578                 ['04 22 1991'],
579                 ['January, 19 2010'],
580                 ['December 12 \'10']
581         ];
583         tableTest(
584                 'Complex date parsing I',
585                 ['date'],
586                 complexMDYDates,
587                 complexMDYSorted,
588                 function ( $table ) {
589                         mw.config.set( 'wgDefaultDateFormat', 'mdy' );
591                         $table.tablesorter();
592                         $table.find( '.headerSort:eq(0)' ).click();
593                 }
594         );
596         var currencyUnsorted = [
597                 ['1.02 $'],
598                 ['$ 3.00'],
599                 ['€ 2,99'],
600                 ['$ 1.00'],
601                 ['$3.50'],
602                 ['$ 1.50'],
603                 ['€ 0.99']
604         ];
606         var currencySorted = [
607                 ['€ 0.99'],
608                 ['$ 1.00'],
609                 ['1.02 $'],
610                 ['$ 1.50'],
611                 ['$ 3.00'],
612                 ['$3.50'],
613                 // Comma's sort after dots
614                 // Not intentional but test to detect changes
615                 ['€ 2,99']
616         ];
618         tableTest(
619                 'Currency parsing I',
620                 ['currency'],
621                 currencyUnsorted,
622                 currencySorted,
623                 function ( $table ) {
624                         $table.tablesorter();
625                         $table.find( '.headerSort:eq(0)' ).click();
626                 }
627         );
629         var ascendingNameLegacy = ascendingName.slice( 0 );
630         ascendingNameLegacy[4] = ascendingNameLegacy[5];
631         ascendingNameLegacy.pop();
633         tableTest(
634                 'Legacy compat with .sortbottom',
635                 header,
636                 planets,
637                 ascendingNameLegacy,
638                 function ( $table ) {
639                         $table.find( 'tr:last' ).addClass( 'sortbottom' );
640                         $table.tablesorter();
641                         $table.find( '.headerSort:eq(0)' ).click();
642                 }
643         );
645         QUnit.test( 'Test detection routine', 1, function ( assert ) {
646                 var $table;
647                 $table = $(
648                         '<table class="sortable">' +
649                                 '<caption>CAPTION</caption>' +
650                                 '<tr><th>THEAD</th></tr>' +
651                                 '<tr><td>1</td></tr>' +
652                                 '<tr class="sortbottom"><td>text</td></tr>' +
653                                 '</table>'
654                 );
655                 $table.tablesorter();
656                 $table.find( '.headerSort:eq(0)' ).click();
658                 assert.equal(
659                         $table.data( 'tablesorter' ).config.parsers[0].id,
660                         'number',
661                         'Correctly detected column content skipping sortbottom'
662                 );
663         } );
665         /** FIXME: the diff output is not very readeable. */
666         QUnit.test( 'bug 32047 - caption must be before thead', 1, function ( assert ) {
667                 var $table;
668                 $table = $(
669                         '<table class="sortable">' +
670                                 '<caption>CAPTION</caption>' +
671                                 '<tr><th>THEAD</th></tr>' +
672                                 '<tr><td>A</td></tr>' +
673                                 '<tr><td>B</td></tr>' +
674                                 '<tr class="sortbottom"><td>TFOOT</td></tr>' +
675                                 '</table>'
676                 );
677                 $table.tablesorter();
679                 assert.equal(
680                         $table.children().get( 0 ).nodeName,
681                         'CAPTION',
682                         'First element after <thead> must be <caption> (bug 32047)'
683                 );
684         } );
686         QUnit.test( 'data-sort-value attribute, when available, should override sorting position', 3, function ( assert ) {
687                 var $table, data;
689                 // Example 1: All cells except one cell without data-sort-value,
690                 // which should be sorted at it's text content value.
691                 $table = $(
692                         '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
693                                 '<tbody>' +
694                                 '<tr><td>Cheetah</td></tr>' +
695                                 '<tr><td data-sort-value="Apple">Bird</td></tr>' +
696                                 '<tr><td data-sort-value="Bananna">Ferret</td></tr>' +
697                                 '<tr><td data-sort-value="Drupe">Elephant</td></tr>' +
698                                 '<tr><td data-sort-value="Cherry">Dolphin</td></tr>' +
699                                 '</tbody></table>'
700                 );
701                 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
703                 data = [];
704                 $table.find( 'tbody > tr' ).each( function ( i, tr ) {
705                         $( tr ).find( 'td' ).each( function ( i, td ) {
706                                 data.push( {
707                                         data: $( td ).data( 'sortValue' ),
708                                         text: $( td ).text()
709                                 } );
710                         } );
711                 } );
713                 assert.deepEqual( data, [
714                         {
715                                 data: 'Apple',
716                                 text: 'Bird'
717                         },
718                         {
719                                 data: 'Bananna',
720                                 text: 'Ferret'
721                         },
722                         {
723                                 data: undefined,
724                                 text: 'Cheetah'
725                         },
726                         {
727                                 data: 'Cherry',
728                                 text: 'Dolphin'
729                         },
730                         {
731                                 data: 'Drupe',
732                                 text: 'Elephant'
733                         }
734                 ], 'Order matches expected order (based on data-sort-value attribute values)' );
736                 // Example 2
737                 $table = $(
738                         '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
739                                 '<tbody>' +
740                                 '<tr><td>D</td></tr>' +
741                                 '<tr><td data-sort-value="E">A</td></tr>' +
742                                 '<tr><td>B</td></tr>' +
743                                 '<tr><td>G</td></tr>' +
744                                 '<tr><td data-sort-value="F">C</td></tr>' +
745                                 '</tbody></table>'
746                 );
747                 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
749                 data = [];
750                 $table.find( 'tbody > tr' ).each( function ( i, tr ) {
751                         $( tr ).find( 'td' ).each( function ( i, td ) {
752                                 data.push( {
753                                         data: $( td ).data( 'sortValue' ),
754                                         text: $( td ).text()
755                                 } );
756                         } );
757                 } );
759                 assert.deepEqual( data, [
760                         {
761                                 data: undefined,
762                                 text: 'B'
763                         },
764                         {
765                                 data: undefined,
766                                 text: 'D'
767                         },
768                         {
769                                 data: 'E',
770                                 text: 'A'
771                         },
772                         {
773                                 data: 'F',
774                                 text: 'C'
775                         },
776                         {
777                                 data: undefined,
778                                 text: 'G'
779                         }
780                 ], 'Order matches expected order (based on data-sort-value attribute values)' );
782                 // Example 3: Test that live changes are used from data-sort-value,
783                 // even if they change after the tablesorter is constructed (bug 38152).
784                 $table = $(
785                         '<table class="sortable"><thead><tr><th>Data</th></tr></thead>' +
786                                 '<tbody>' +
787                                 '<tr><td>D</td></tr>' +
788                                 '<tr><td data-sort-value="1">A</td></tr>' +
789                                 '<tr><td>B</td></tr>' +
790                                 '<tr><td data-sort-value="2">G</td></tr>' +
791                                 '<tr><td>C</td></tr>' +
792                                 '</tbody></table>'
793                 );
794                 // initialize table sorter and sort once
795                 $table
796                         .tablesorter()
797                         .find( '.headerSort:eq(0)' ).click();
799                 // Change the sortValue data properties (bug 38152)
800                 // - change data
801                 $table.find( 'td:contains(A)' ).data( 'sortValue', 3 );
802                 // - add data
803                 $table.find( 'td:contains(B)' ).data( 'sortValue', 1 );
804                 // - remove data, bring back attribute: 2
805                 $table.find( 'td:contains(G)' ).removeData( 'sortValue' );
807                 // Now sort again (twice, so it is back at Ascending)
808                 $table.find( '.headerSort:eq(0)' ).click();
809                 $table.find( '.headerSort:eq(0)' ).click();
811                 data = [];
812                 $table.find( 'tbody > tr' ).each( function ( i, tr ) {
813                         $( tr ).find( 'td' ).each( function ( i, td ) {
814                                 data.push( {
815                                         data: $( td ).data( 'sortValue' ),
816                                         text: $( td ).text()
817                                 } );
818                         } );
819                 } );
821                 assert.deepEqual( data, [
822                         {
823                                 data: 1,
824                                 text: 'B'
825                         },
826                         {
827                                 data: 2,
828                                 text: 'G'
829                         },
830                         {
831                                 data: 3,
832                                 text: 'A'
833                         },
834                         {
835                                 data: undefined,
836                                 text: 'C'
837                         },
838                         {
839                                 data: undefined,
840                                 text: 'D'
841                         }
842                 ], 'Order matches expected order, using the current sortValue in $.data()' );
844         } );
846         var numbers = [
847                 [ '12'    ],
848                 [  '7'    ],
849                 [ '13,000'],
850                 [  '9'    ],
851                 [ '14'    ],
852                 [  '8.0'  ]
853         ];
854         var numbersAsc = [
855                 [  '7'    ],
856                 [  '8.0'  ],
857                 [  '9'    ],
858                 [ '12'    ],
859                 [ '14'    ],
860                 [ '13,000']
861         ];
863         tableTest( 'bug 8115: sort numbers with commas (ascending)',
864                 ['Numbers'], numbers, numbersAsc,
865                 function ( $table ) {
866                         $table.tablesorter();
867                         $table.find( '.headerSort:eq(0)' ).click();
868                 }
869         );
871         tableTest( 'bug 8115: sort numbers with commas (descending)',
872                 ['Numbers'], numbers, reversed( numbersAsc ),
873                 function ( $table ) {
874                         $table.tablesorter();
875                         $table.find( '.headerSort:eq(0)' ).click().click();
876                 }
877         );
878         // TODO add numbers sorting tests for bug 8115 with a different language
880         QUnit.test( 'bug 32888 - Tables inside a tableheader cell', 2, function ( assert ) {
881                 var $table;
882                 $table = $(
883                         '<table class="sortable" id="mw-bug-32888">' +
884                                 '<tr><th>header<table id="mw-bug-32888-2">' +
885                                 '<tr><th>1</th><th>2</th></tr>' +
886                                 '</table></th></tr>' +
887                                 '<tr><td>A</td></tr>' +
888                                 '<tr><td>B</td></tr>' +
889                                 '</table>'
890                 );
891                 $table.tablesorter();
893                 assert.equal(
894                         $table.find( '> thead:eq(0) > tr > th.headerSort' ).length,
895                         1,
896                         'Child tables inside a headercell should not interfere with sortable headers (bug 32888)'
897                 );
898                 assert.equal(
899                         $( '#mw-bug-32888-2' ).find( 'th.headerSort' ).length,
900                         0,
901                         'The headers of child tables inside a headercell should not be sortable themselves (bug 32888)'
902                 );
903         } );
906         var correctDateSorting1 = [
907                 ['01 January 2010'],
908                 ['05 February 2010'],
909                 ['16 January 2010']
910         ];
912         var correctDateSortingSorted1 = [
913                 ['01 January 2010'],
914                 ['16 January 2010'],
915                 ['05 February 2010']
916         ];
918         tableTest(
919                 'Correct date sorting I',
920                 ['date'],
921                 correctDateSorting1,
922                 correctDateSortingSorted1,
923                 function ( $table ) {
924                         mw.config.set( 'wgDefaultDateFormat', 'mdy' );
926                         $table.tablesorter();
927                         $table.find( '.headerSort:eq(0)' ).click();
928                 }
929         );
931         var correctDateSorting2 = [
932                 ['January 01 2010'],
933                 ['February 05 2010'],
934                 ['January 16 2010']
935         ];
937         var correctDateSortingSorted2 = [
938                 ['January 01 2010'],
939                 ['January 16 2010'],
940                 ['February 05 2010']
941         ];
943         tableTest(
944                 'Correct date sorting II',
945                 ['date'],
946                 correctDateSorting2,
947                 correctDateSortingSorted2,
948                 function ( $table ) {
949                         mw.config.set( 'wgDefaultDateFormat', 'dmy' );
951                         $table.tablesorter();
952                         $table.find( '.headerSort:eq(0)' ).click();
953                 }
954         );
956         QUnit.test( 'Sorting images using alt text', 1, function ( assert ) {
957                 var $table = $(
958                         '<table class="sortable">' +
959                                 '<tr><th>THEAD</th></tr>' +
960                                 '<tr><td><img alt="2"/></td></tr>' +
961                                 '<tr><td>1</td></tr>' +
962                                 '</table>'
963                 );
964                 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
966                 assert.equal(
967                         $table.find( 'td' ).first().text(),
968                         '1',
969                         'Applied correct sorting order'
970                 );
971         } );
973         QUnit.test( 'Sorting images using alt text (complex)', 1, function ( assert ) {
974                 var $table = $(
975                         '<table class="sortable">' +
976                                 '<tr><th>THEAD</th></tr>' +
977                                 '<tr><td><img alt="D" />A</td></tr>' +
978                                 '<tr><td>CC</td></tr>' +
979                                 '<tr><td><a><img alt="A" /></a>F</tr>' +
980                                 '<tr><td><img alt="A" /><strong>E</strong></tr>' +
981                                 '<tr><td><strong><img alt="A" />D</strong></tr>' +
982                                 '<tr><td><img alt="A" />C</tr>' +
983                                 '</table>'
984                 );
985                 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
987                 assert.equal(
988                         $table.find( 'td' ).text(),
989                         'CDEFCCA',
990                         'Applied correct sorting order'
991                 );
992         } );
994         QUnit.test( 'Sorting images using alt text (with format autodetection)', 1, function ( assert ) {
995                 var $table = $(
996                         '<table class="sortable">' +
997                                 '<tr><th>THEAD</th></tr>' +
998                                 '<tr><td><img alt="1" />7</td></tr>' +
999                                 '<tr><td>1<img alt="6" /></td></tr>' +
1000                                 '<tr><td>5</td></tr>' +
1001                                 '<tr><td>4</td></tr>' +
1002                                 '</table>'
1003                 );
1004                 $table.tablesorter().find( '.headerSort:eq(0)' ).click();
1006                 assert.equal(
1007                         $table.find( 'td' ).text(),
1008                         '4517',
1009                         'Applied correct sorting order'
1010                 );
1011         } );
1013         // bug 41889 - exploding rowspans in more complex cases
1014         tableTestHTML(
1015                 'Rowspan exploding with row headers',
1016                 '<table class="sortable">' +
1017                         '<thead><tr><th id="sortme">n</th><th>foo</th><th>bar</th><th>baz</th></tr></thead>' +
1018                         '<tbody>' +
1019                         '<tr><td>1</td><th rowspan="2">foo</th><td rowspan="2">bar</td><td>baz</td></tr>' +
1020                         '<tr><td>2</td><td>baz</td></tr>' +
1021                         '</tbody></table>',
1022                 [
1023                         [ '1', 'foo', 'bar', 'baz' ],
1024                         [ '2', 'foo', 'bar', 'baz' ]
1025                 ]
1026         );
1028         tableTestHTML(
1029                 'Rowspan exploding with colspanned cells',
1030                 '<table class="sortable">' +
1031                         '<thead><tr><th id="sortme">n</th><th>foo</th><th>bar</th><th>baz</th></tr></thead>' +
1032                         '<tbody>' +
1033                         '<tr><td>1</td><td>foo</td><td>bar</td><td rowspan="2">baz</td></tr>' +
1034                         '<tr><td>2</td><td colspan="2">foobar</td></tr>' +
1035                         '</tbody></table>',
1036                 [
1037                         [ '1', 'foo', 'bar', 'baz' ],
1038                         [ '2', 'foobar', 'baz' ]
1039                 ]
1040         );
1042         tableTestHTML(
1043                 'Rowspan exploding with colspanned cells (2)',
1044                 '<table class="sortable">' +
1045                         '<thead><tr><th id="sortme">n</th><th>foo</th><th>bar</th><th>baz</th><th>quux</th></tr></thead>' +
1046                         '<tbody>' +
1047                         '<tr><td>1</td><td>foo</td><td>bar</td><td rowspan="2">baz</td><td>quux</td></tr>' +
1048                         '<tr><td>2</td><td colspan="2">foobar</td><td>quux</td></tr>' +
1049                         '</tbody></table>',
1050                 [
1051                         [ '1', 'foo', 'bar', 'baz', 'quux' ],
1052                         [ '2', 'foobar', 'baz', 'quux' ]
1053                 ]
1054         );
1056         tableTestHTML(
1057                 'Rowspan exploding with rightmost rows spanning most',
1058                 '<table class="sortable">' +
1059                         '<thead><tr><th id="sortme">n</th><th>foo</th><th>bar</th></tr></thead>' +
1060                         '<tbody>' +
1061                         '<tr><td>1</td><td rowspan="2">foo</td><td rowspan="4">bar</td></tr>' +
1062                         '<tr><td>2</td></tr>' +
1063                         '<tr><td>3</td><td rowspan="2">foo</td></tr>' +
1064                         '<tr><td>4</td></tr>' +
1065                         '</tbody></table>',
1066                 [
1067                         [ '1', 'foo', 'bar' ],
1068                         [ '2', 'foo', 'bar' ],
1069                         [ '3', 'foo', 'bar' ],
1070                         [ '4', 'foo', 'bar' ]
1071                 ]
1072         );
1074         tableTestHTML(
1075                 'Rowspan exploding with rightmost rows spanning most (2)',
1076                 '<table class="sortable">' +
1077                         '<thead><tr><th id="sortme">n</th><th>foo</th><th>bar</th><th>baz</th></tr></thead>' +
1078                         '<tbody>' +
1079                         '<tr><td>1</td><td rowspan="2">foo</td><td rowspan="4">bar</td><td>baz</td></tr>' +
1080                         '<tr><td>2</td><td>baz</td></tr>' +
1081                         '<tr><td>3</td><td rowspan="2">foo</td><td>baz</td></tr>' +
1082                         '<tr><td>4</td><td>baz</td></tr>' +
1083                         '</tbody></table>',
1084                 [
1085                         [ '1', 'foo', 'bar', 'baz' ],
1086                         [ '2', 'foo', 'bar', 'baz' ],
1087                         [ '3', 'foo', 'bar', 'baz' ],
1088                         [ '4', 'foo', 'bar', 'baz' ]
1089                 ]
1090         );
1092         tableTestHTML(
1093                 'Rowspan exploding with row-and-colspanned cells',
1094                 '<table class="sortable">' +
1095                         '<thead><tr><th id="sortme">n</th><th>foo1</th><th>foo2</th><th>bar</th><th>baz</th></tr></thead>' +
1096                         '<tbody>' +
1097                         '<tr><td>1</td><td rowspan="2">foo1</td><td rowspan="2">foo2</td><td rowspan="4">bar</td><td>baz</td></tr>' +
1098                         '<tr><td>2</td><td>baz</td></tr>' +
1099                         '<tr><td>3</td><td colspan="2" rowspan="2">foo</td><td>baz</td></tr>' +
1100                         '<tr><td>4</td><td>baz</td></tr>' +
1101                         '</tbody></table>',
1102                 [
1103                         [ '1', 'foo1', 'foo2', 'bar', 'baz' ],
1104                         [ '2', 'foo1', 'foo2', 'bar', 'baz' ],
1105                         [ '3', 'foo', 'bar', 'baz' ],
1106                         [ '4', 'foo', 'bar', 'baz' ]
1107                 ]
1108         );
1110         tableTestHTML(
1111                 'Rowspan exploding with uneven rowspan layout',
1112                 '<table class="sortable">' +
1113                         '<thead><tr><th id="sortme">n</th><th>foo1</th><th>foo2</th><th>foo3</th><th>bar</th><th>baz</th></tr></thead>' +
1114                         '<tbody>' +
1115                         '<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>' +
1116                         '<tr><td>2</td><td rowspan="3">bar</td><td>baz</td></tr>' +
1117                         '<tr><td>3</td><td rowspan="2">foo1</td><td rowspan="2">foo2</td><td rowspan="2">foo3</td><td>baz</td></tr>' +
1118                         '<tr><td>4</td><td>baz</td></tr>' +
1119                         '</tbody></table>',
1120                 [
1121                         [ '1', 'foo1', 'foo2', 'foo3', 'bar', 'baz' ],
1122                         [ '2', 'foo1', 'foo2', 'foo3', 'bar', 'baz' ],
1123                         [ '3', 'foo1', 'foo2', 'foo3', 'bar', 'baz' ],
1124                         [ '4', 'foo1', 'foo2', 'foo3', 'bar', 'baz' ]
1125                 ]
1126         );
1128 }( jQuery, mediaWiki ) );