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