return false;
}
- function setHeadersCss( table, $headers, list, css, msg ) {
+ function setHeadersCss( table, $headers, list, css, msg, columnToHeader ) {
// Remove all header information and reset titles to default message
$headers.removeClass( css[0] ).removeClass( css[1] ).attr( 'title', msg[1] );
- var h = [];
- $headers.each( function () {
- if ( !this.sortDisabled ) {
- h[this.column] = $( this );
- }
- } );
-
- var l = list.length;
- for ( var i = 0; i < l; i++ ) {
- h[ list[i][0] ].addClass( css[ list[i][1] ] ).attr( 'title', msg[ list[i][1] ] );
+ for ( var i = 0; i < list.length; i++ ) {
+ $headers.eq( columnToHeader[ list[i][0] ] )
+ .addClass( css[ list[i][1] ] )
+ .attr( 'title', msg[ list[i][1] ] );
}
}
return $tables.each( function ( i, table ) {
// Declare and cache.
var $headers, cache, config,
+ headerToColumns, columnToHeader, colspanOffset,
$table = $( table ),
firstTime = true;
table.config.parsers = buildParserCache( table, $headers );
}
+ // as each header can span over multiple columns (using colspan=N),
+ // we have to bidirectionally map headers to their columns and columns to their headers
+ headerToColumns = [];
+ columnToHeader = [];
+ colspanOffset = 0;
+ $headers.each( function ( headerIndex ) {
+ var columns = [];
+ for ( var i = 0; i < this.colSpan; i++ ) {
+ columnToHeader[ colspanOffset + i ] = headerIndex;
+ columns.push( colspanOffset + i );
+ }
+
+ headerToColumns[ headerIndex ] = columns;
+ colspanOffset += this.colSpan;
+ } );
+
// Apply event handling to headers
// this is too big, perhaps break it out?
$headers.filter( ':not(.unsortable)' ).click( function ( e ) {
var totalRows = ( $table[0].tBodies[0] && $table[0].tBodies[0].rows.length ) || 0;
if ( !table.sortDisabled && totalRows > 0 ) {
-
- // Get current column index
- var i = this.column;
-
// Get current column sort order
this.order = this.count % 2;
this.count++;
- // User only wants to sort on one column
- if ( !e[config.sortMultiSortKey] ) {
- // Flush the sort list
- config.sortList = [];
- // Add column to sort list
- config.sortList.push( [i, this.order] );
+ var cell = this;
+ // Get current column index
+ var columns = headerToColumns[this.column];
+ var newSortList = $.map( columns, function (c) {
+ // jQuery "helpfully" flattens the arrays...
+ return [[c, cell.order]];
+ });
+ // Index of first column belonging to this header
+ var i = columns[0];
- // Multi column sorting
+ if ( !e[config.sortMultiSortKey] ) {
+ // User only wants to sort on one column set
+ // Flush the sort list and add new columns
+ config.sortList = newSortList;
} else {
- // The user has clicked on an already sorted column.
+ // Multi column sorting
+ // It is not possible for one column to belong to multiple headers,
+ // so this is okay - we don't need to check for every value in the columns array
if ( isValueInArray( i, config.sortList ) ) {
+ // The user has clicked on an already sorted column.
// Reverse the sorting direction for all tables.
for ( var j = 0; j < config.sortList.length; j++ ) {
var s = config.sortList[j],
o = config.headerList[s[0]];
- if ( s[0] === i ) {
+ if ( isValueInArray( s[0], newSortList ) ) {
o.count = s[1];
o.count++;
s[1] = o.count % 2;
}
}
} else {
- // Add column to sort list array
- config.sortList.push( [i, this.order] );
+ // Add columns to sort list array
+ config.sortList = config.sortList.concat( newSortList );
}
}
// Set CSS for headers
- setHeadersCss( $table[0], $headers, config.sortList, sortCSS, sortMsg );
+ setHeadersCss( $table[0], $headers, config.sortList, sortCSS, sortMsg, columnToHeader );
appendToTable(
$table[0], multisort( $table[0], config.sortList, cache )
);
cache = buildCache( table );
// set css for headers
- setHeadersCss( table, $headers, sortList, sortCSS, sortMsg );
+ setHeadersCss( table, $headers, sortList, sortCSS, sortMsg, columnToHeader );
// sort the table and append it to the dom
appendToTable( table, multisort( table, sortList, cache ) );
);
} );
+ // Sorting with colspans
+ header = [ 'column1a' , 'column1b', 'column1c', 'column2' ];
+ var
+ aaa1 = [ 'A', 'A', 'A', '1' ],
+ aab5 = [ 'A', 'A', 'B', '5' ],
+ abc3 = [ 'A', 'B', 'C', '3' ],
+ bbc2 = [ 'B', 'B', 'C', '2' ],
+ caa4 = [ 'C', 'A', 'A', '4' ];
+ // initial is already declared above
+ initial = [ aab5, aaa1, abc3, bbc2, caa4 ];
+ tableTest( 'Sorting with colspanned headers: spanned column',
+ header,
+ initial,
+ [ aaa1, aab5, abc3, bbc2, caa4 ],
+ function ( $table ) {
+ // Make colspanned header for test
+ $table.find( 'tr:eq(0) th:eq(1), tr:eq(0) th:eq(2)' ).remove();
+ $table.find( 'tr:eq(0) th:eq(0)' ).prop( 'colspan', '3' );
+
+ $table.tablesorter();
+ $table.find( '.headerSort:eq(0)' ).click();
+ }
+ );
+ tableTest( 'Sorting with colspanned headers: subsequent column',
+ header,
+ initial,
+ [ aaa1, bbc2, abc3, caa4, aab5 ],
+ function ( $table ) {
+ // Make colspanned header for test
+ $table.find( 'tr:eq(0) th:eq(1), tr:eq(0) th:eq(2)' ).remove();
+ $table.find( 'tr:eq(0) th:eq(0)' ).prop( 'colspan', '3' );
+
+ $table.tablesorter();
+ $table.find( '.headerSort:eq(1)' ).click();
+ }
+ );
+
// Regression tests!
tableTest(
'Bug 28775: German-style (dmy) short numeric dates',