function buildHeaders( table, msg ) {
var maxSeen = 0,
+ colspanOffset = 0,
longest,
- realCellIndex = 0,
- $tableHeaders = $( 'thead:eq(0) > tr', table );
- if ( $tableHeaders.length > 1 ) {
- $tableHeaders.each( function () {
- if ( this.cells.length > maxSeen ) {
- maxSeen = this.cells.length;
- longest = this;
+ columns,
+ i,
+ $tableHeaders = $( [] ),
+ $tableRows = $( 'thead:eq(0) > tr', table );
+ if ( $tableRows.length <= 1 ) {
+ $tableHeaders = $tableRows.children( 'th' );
+ } else {
+ // We need to find the cells of the row containing the most columns
+ var rowspan,
+ headersIndex = [];
+ $tableRows.each( function ( rowIndex ) {
+ $.each( this.cells, function( index2, cell ) {
+ rowspan = Number( cell.rowSpan );
+ for ( i = 0; i < rowspan; i++ ) {
+ if ( headersIndex[rowIndex+i] === undefined ) {
+ headersIndex[rowIndex+i] = $( [] );
+ }
+ headersIndex[rowIndex+i].push( cell );
+ }
+ } );
+ } );
+ $.each( headersIndex, function ( index, cellArray ) {
+ if ( cellArray.length >= maxSeen ) {
+ maxSeen = cellArray.length;
+ longest = index;
}
- });
- $tableHeaders = $( longest );
+ } );
+ $tableHeaders = headersIndex[longest];
}
- $tableHeaders = $tableHeaders.children( 'th' ).each( function ( index ) {
- this.column = realCellIndex;
- var colspan = this.colspan;
- colspan = colspan ? parseInt( colspan, 10 ) : 1;
- realCellIndex += colspan;
+ // 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
+ table.headerToColumns = [];
+ table.columnToHeader = [];
+
+ $tableHeaders.each( function ( headerIndex ) {
+ columns = [];
+ for ( i = 0; i < this.colSpan; i++ ) {
+ table.columnToHeader[ colspanOffset + i ] = headerIndex;
+ columns.push( colspanOffset + i );
+ }
+
+ table.headerToColumns[ headerIndex ] = columns;
+ colspanOffset += this.colSpan;
+ this.headerIndex = headerIndex;
this.order = 0;
this.count = 0;
- if ( $( this ).is( '.unsortable' ) ) {
+ if ( $( this ).hasClass( table.config.unsortableClass ) ) {
this.sortDisabled = true;
}
if ( !this.sortDisabled ) {
- $( this ).addClass( table.config.cssHeader ).attr( 'title', msg[1] );
+ $( this )
+ .addClass( table.config.cssHeader )
+ .prop( 'tabIndex', 0 )
+ .attr( {
+ role: 'button',
+ title: msg[1]
+ } );
}
// add cell to headerList
- table.config.headerList[index] = this;
+ table.config.headerList[headerIndex] = this;
} );
return $tableHeaders;
sortInitialOrder: 'asc',
sortMultiSortKey: 'shiftKey',
sortLocaleCompare: false,
+ unsortableClass: 'unsortable',
parsers: {},
widgets: [],
headers: {},
return $tables.each( function ( i, table ) {
// Declare and cache.
var $headers, cache, config,
- headerToColumns, columnToHeader, colspanOffset,
$table = $( table ),
firstTime = true;
// Build headers
$headers = buildHeaders( table, sortMsg );
- // Grab and process locale settings
+ // Grab and process locale settings.
buildTransformTable();
buildDateTable();
- buildCollationTable();
// Precaching regexps can bring 10 fold
// performance improvements in some browsers.
function setupForFirstSort() {
firstTime = false;
+ // Defer buildCollationTable to first sort. As user and site scripts
+ // may customize tableSorterCollation but load after $.ready(), other
+ // scripts may call .tablesorter() before they have done the
+ // tableSorterCollation customizations.
+ buildCollationTable();
+
// Legacy fix of .sortbottoms
// Wrap them inside inside a tfoot (because that's what they actually want to be) &
// and put the <tfoot> at the end of the <table>
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 ) {
- if ( e.target.nodeName.toLowerCase() === 'a' ) {
- // The user clicked on a link inside a table header
- // Do nothing and let the default link click action continue
+ $headers.not( '.' + table.config.unsortableClass ).on( 'keypress click', function ( e ) {
+ if ( e.type === 'click' && e.target.nodeName.toLowerCase() === 'a' ) {
+ // The user clicked on a link inside a table header.
+ // Do nothing and let the default link click action continue.
+ return true;
+ }
+
+ if ( e.type === 'keypress' && e.which !== 13 ) {
+ // Only handle keypresses on the "Enter" key.
return true;
}
var cell = this;
// Get current column index
- var columns = headerToColumns[this.column];
+ var columns = table.headerToColumns[ this.headerIndex ];
var newSortList = $.map( columns, function (c) {
// jQuery "helpfully" flattens the arrays...
return [[c, cell.order]];
}
// Reset order/counts of cells not affected by sorting
- setHeadersOrder( $headers, config.sortList, headerToColumns );
+ setHeadersOrder( $headers, config.sortList, table.headerToColumns );
// Set CSS for headers
- setHeadersCss( $table[0], $headers, config.sortList, sortCSS, sortMsg, columnToHeader );
+ setHeadersCss( $table[0], $headers, config.sortList, sortCSS, sortMsg, table.columnToHeader );
appendToTable(
$table[0], multisort( $table[0], config.sortList, cache )
);
// Set each column's sort count to be able to determine the correct sort
// order when clicking on a header cell the next time
- setHeadersOrder( $headers, sortList, headerToColumns );
+ setHeadersOrder( $headers, sortList, table.headerToColumns );
// re-build the cache for the tbody cells
cache = buildCache( table );
// set css for headers
- setHeadersCss( table, $headers, sortList, sortCSS, sortMsg, columnToHeader );
+ setHeadersCss( table, $headers, sortList, sortCSS, sortMsg, table.columnToHeader );
// sort the table and append it to the dom
appendToTable( table, multisort( table, sortList, cache ) );