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