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