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