}
function setHeadersCss( table, $headers, list, css, msg, columnToHeader ) {
+ var i, len;
// Remove all header information and reset titles to default message
$headers.removeClass( css[ 0 ] ).removeClass( css[ 1 ] ).attr( 'title', msg[ 1 ] );
- for ( var i = 0; i < list.length; i++ ) {
+ for ( i = 0, len = list.length; i < len; i++ ) {
$headers
.eq( columnToHeader[ list[ i ][ 0 ] ] )
.addClass( css[ list[ i ][ 1 ] ] )
}
function buildCollationTable() {
+ var key, keys = [];
ts.collationTable = mw.config.get( 'tableSorterCollation' );
ts.collationRegex = null;
if ( ts.collationTable ) {
- var key,
- keys = [];
-
// Build array of key names
for ( key in ts.collationTable ) {
// Check hasOwn to be safe
/* Public scope */
$.tablesorter = {
- defaultOptions: {
- cssHeader: 'headerSort',
- cssAsc: 'headerSortUp',
- cssDesc: 'headerSortDown',
- cssChildRow: 'expand-child',
- sortMultiSortKey: 'shiftKey',
- unsortableClass: 'unsortable',
- parsers: [],
- cancelSelection: true,
- sortList: [],
- headerList: [],
- headerToColumns: [],
- columnToHeader: [],
- columns: 0
- },
-
- dateRegex: [],
- monthNames: {},
-
- /**
- * @param {jQuery} $tables
- * @param {Object} [settings]
- */
- construct: function ( $tables, settings ) {
- return $tables.each( function ( i, table ) {
- // Declare and cache.
- var $headers, cache, config, sortCSS, sortMsg,
- $table = $( table ),
- firstTime = true;
-
- // Quit if no tbody
- if ( !table.tBodies ) {
+ defaultOptions: {
+ cssHeader: 'headerSort',
+ cssAsc: 'headerSortUp',
+ cssDesc: 'headerSortDown',
+ cssChildRow: 'expand-child',
+ sortMultiSortKey: 'shiftKey',
+ unsortableClass: 'unsortable',
+ parsers: [],
+ cancelSelection: true,
+ sortList: [],
+ headerList: [],
+ headerToColumns: [],
+ columnToHeader: [],
+ columns: 0
+ },
+
+ dateRegex: [],
+ monthNames: {},
+
+ /**
+ * @param {jQuery} $tables
+ * @param {Object} [settings]
+ */
+ construct: function ( $tables, settings ) {
+ return $tables.each( function ( i, table ) {
+ // Declare and cache.
+ var $headers, cache, config, sortCSS, sortMsg,
+ $table = $( table ),
+ firstTime = true;
+
+ // Quit if no tbody
+ if ( !table.tBodies ) {
+ return;
+ }
+ if ( !table.tHead ) {
+ // No thead found. Look for rows with <th>s and
+ // move them into a <thead> tag or a <tfoot> tag
+ emulateTHeadAndFoot( $table );
+
+ // Still no thead? Then quit
+ if ( !table.tHead ) {
return;
}
- if ( !table.tHead ) {
- // No thead found. Look for rows with <th>s and
- // move them into a <thead> tag or a <tfoot> tag
- emulateTHeadAndFoot( $table );
+ }
+ $table.addClass( 'jquery-tablesorter' );
- // Still no thead? Then quit
- if ( !table.tHead ) {
- return;
- }
- }
- $table.addClass( 'jquery-tablesorter' );
-
- // Merge and extend
- config = $.extend( {}, $.tablesorter.defaultOptions, settings );
-
- // Save the settings where they read
- $.data( table, 'tablesorter', { config: config } );
-
- // Get the CSS class names, could be done elsewhere
- sortCSS = [ config.cssAsc, config.cssDesc ];
- // Messages tell the the user what the *next* state will be
- // so are in reverse order to the CSS classes.
- sortMsg = [ mw.msg( 'sort-descending' ), mw.msg( 'sort-ascending' ) ];
-
- // Build headers
- $headers = buildHeaders( table, sortMsg );
-
- // Grab and process locale settings.
- buildTransformTable();
- buildDateTable();
-
- // Precaching regexps can bring 10 fold
- // performance improvements in some browsers.
- cacheRegexs();
-
- 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 a tfoot (because that's what they actually want to be)
- // and put the <tfoot> at the end of the <table>
- var $tfoot,
- $sortbottoms = $table.find( '> tbody > tr.sortbottom' );
- if ( $sortbottoms.length ) {
- $tfoot = $table.children( 'tfoot' );
- if ( $tfoot.length ) {
- $tfoot.eq( 0 ).prepend( $sortbottoms );
- } else {
- $table.append( $( '<tfoot>' ).append( $sortbottoms ) );
- }
- }
+ // Merge and extend
+ config = $.extend( {}, $.tablesorter.defaultOptions, settings );
- explodeRowspans( $table );
- manageColspans( $table );
+ // Save the settings where they read
+ $.data( table, 'tablesorter', { config: config } );
- // Try to auto detect column type, and store in tables config
- config.parsers = buildParserCache( table, $headers );
- }
+ // Get the CSS class names, could be done elsewhere
+ sortCSS = [ config.cssAsc, config.cssDesc ];
+ // Messages tell the the user what the *next* state will be
+ // so are in reverse order to the CSS classes.
+ sortMsg = [ mw.msg( 'sort-descending' ), mw.msg( 'sort-ascending' ) ];
- // Apply event handling to headers
- // this is too big, perhaps break it out?
- $headers.on( 'keypress click', function ( e ) {
- var cell, $cell, columns, newSortList, i,
- totalRows,
- j, s, o;
-
- 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;
- }
+ // Build headers
+ $headers = buildHeaders( table, sortMsg );
- if ( e.type === 'keypress' && e.which !== 13 ) {
- // Only handle keypresses on the "Enter" key.
- return true;
- }
+ // Grab and process locale settings.
+ buildTransformTable();
+ buildDateTable();
- if ( firstTime ) {
- setupForFirstSort();
+ // Precaching regexps can bring 10 fold
+ // performance improvements in some browsers.
+ cacheRegexs();
+
+ function setupForFirstSort() {
+ var $tfoot, $sortbottoms;
+
+ 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 a tfoot (because that's what they actually want to be)
+ // and put the <tfoot> at the end of the <table>
+ $sortbottoms = $table.find( '> tbody > tr.sortbottom' );
+ if ( $sortbottoms.length ) {
+ $tfoot = $table.children( 'tfoot' );
+ if ( $tfoot.length ) {
+ $tfoot.eq( 0 ).prepend( $sortbottoms );
+ } else {
+ $table.append( $( '<tfoot>' ).append( $sortbottoms ) );
}
+ }
- // Build the cache for the tbody cells
- // to share between calculations for this sort action.
- // Re-calculated each time a sort action is performed due to possiblity
- // that sort values change. Shouldn't be too expensive, but if it becomes
- // too slow an event based system should be implemented somehow where
- // cells get event .change() and bubbles up to the <table> here
- cache = buildCache( table );
-
- totalRows = ( $table[ 0 ].tBodies[ 0 ] && $table[ 0 ].tBodies[ 0 ].rows.length ) || 0;
- if ( totalRows > 0 ) {
- cell = this;
- $cell = $( cell );
-
- // Get current column sort order
- $cell.data( {
- order: $cell.data( 'count' ) % 2,
- count: $cell.data( 'count' ) + 1
- } );
+ explodeRowspans( $table );
+ manageColspans( $table );
- cell = this;
- // Get current column index
- columns = config.headerToColumns[ $cell.data( 'headerIndex' ) ];
- newSortList = $.map( columns, function ( c ) {
- // jQuery "helpfully" flattens the arrays...
- return [ [ c, $cell.data( 'order' ) ] ];
- } );
- // Index of first column belonging to this header
- i = columns[ 0 ];
+ // Try to auto detect column type, and store in tables config
+ config.parsers = buildParserCache( table, $headers );
+ }
- 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 {
- // 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 ( j = 0; j < config.sortList.length; j++ ) {
- s = config.sortList[ j ];
- o = config.headerList[ config.columnToHeader[ s[ 0 ] ] ];
- if ( isValueInArray( s[ 0 ], newSortList ) ) {
- $( o ).data( 'count', s[ 1 ] + 1 );
- s[ 1 ] = $( o ).data( 'count' ) % 2;
- }
+ // Apply event handling to headers
+ // this is too big, perhaps break it out?
+ $headers.on( 'keypress click', function ( e ) {
+ var cell, $cell, columns, newSortList, i,
+ totalRows,
+ j, s, o;
+
+ 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;
+ }
+
+ if ( firstTime ) {
+ setupForFirstSort();
+ }
+
+ // Build the cache for the tbody cells
+ // to share between calculations for this sort action.
+ // Re-calculated each time a sort action is performed due to possiblity
+ // that sort values change. Shouldn't be too expensive, but if it becomes
+ // too slow an event based system should be implemented somehow where
+ // cells get event .change() and bubbles up to the <table> here
+ cache = buildCache( table );
+
+ totalRows = ( $table[ 0 ].tBodies[ 0 ] && $table[ 0 ].tBodies[ 0 ].rows.length ) || 0;
+ if ( totalRows > 0 ) {
+ cell = this;
+ $cell = $( cell );
+
+ // Get current column sort order
+ $cell.data( {
+ order: $cell.data( 'count' ) % 2,
+ count: $cell.data( 'count' ) + 1
+ } );
+
+ cell = this;
+ // Get current column index
+ columns = config.headerToColumns[ $cell.data( 'headerIndex' ) ];
+ newSortList = $.map( columns, function ( c ) {
+ // jQuery "helpfully" flattens the arrays...
+ return [ [ c, $cell.data( 'order' ) ] ];
+ } );
+ // Index of first column belonging to this header
+ i = columns[ 0 ];
+
+ 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 {
+ // 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 ( j = 0; j < config.sortList.length; j++ ) {
+ s = config.sortList[ j ];
+ o = config.headerList[ config.columnToHeader[ s[ 0 ] ] ];
+ if ( isValueInArray( s[ 0 ], newSortList ) ) {
+ $( o ).data( 'count', s[ 1 ] + 1 );
+ s[ 1 ] = $( o ).data( 'count' ) % 2;
}
- } else {
- // Add columns to sort list array
- config.sortList = config.sortList.concat( newSortList );
}
+ } else {
+ // Add columns to sort list array
+ config.sortList = config.sortList.concat( newSortList );
}
+ }
- // Reset order/counts of cells not affected by sorting
- setHeadersOrder( $headers, config.sortList, config.headerToColumns );
+ // Reset order/counts of cells not affected by sorting
+ setHeadersOrder( $headers, config.sortList, config.headerToColumns );
- // Set CSS for headers
- setHeadersCss( $table[ 0 ], $headers, config.sortList, sortCSS, sortMsg, config.columnToHeader );
- appendToTable(
- $table[ 0 ], multisort( $table[ 0 ], config.sortList, cache )
- );
+ // Set CSS for headers
+ setHeadersCss( $table[ 0 ], $headers, config.sortList, sortCSS, sortMsg, config.columnToHeader );
+ appendToTable(
+ $table[ 0 ], multisort( $table[ 0 ], config.sortList, cache )
+ );
- // Stop normal event by returning false
- return false;
- }
+ // Stop normal event by returning false
+ return false;
+ }
- // Cancel selection
- } ).mousedown( function () {
- if ( config.cancelSelection ) {
- this.onselectstart = function () {
- return false;
- };
+ // Cancel selection
+ } ).mousedown( function () {
+ if ( config.cancelSelection ) {
+ this.onselectstart = function () {
return false;
- }
- } );
+ };
+ return false;
+ }
+ } );
- /**
- * Sorts the table. If no sorting is specified by passing a list of sort
- * objects, the table is sorted according to the initial sorting order.
- * Passing an empty array will reset sorting (basically just reset the headers
- * making the table appear unsorted).
- *
- * @param {Array} [sortList] List of sort objects.
- */
- $table.data( 'tablesorter' ).sort = function ( sortList ) {
-
- if ( firstTime ) {
- setupForFirstSort();
- }
+ /**
+ * Sorts the table. If no sorting is specified by passing a list of sort
+ * objects, the table is sorted according to the initial sorting order.
+ * Passing an empty array will reset sorting (basically just reset the headers
+ * making the table appear unsorted).
+ *
+ * @param {Array} [sortList] List of sort objects.
+ */
+ $table.data( 'tablesorter' ).sort = function ( sortList ) {
+
+ if ( firstTime ) {
+ setupForFirstSort();
+ }
- if ( sortList === undefined ) {
- sortList = config.sortList;
- } else if ( sortList.length > 0 ) {
- sortList = convertSortList( sortList );
- }
+ if ( sortList === undefined ) {
+ sortList = config.sortList;
+ } else if ( sortList.length > 0 ) {
+ sortList = convertSortList( sortList );
+ }
- // 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, config.headerToColumns );
+ // 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, config.headerToColumns );
- // re-build the cache for the tbody cells
- cache = buildCache( table );
+ // re-build the cache for the tbody cells
+ cache = buildCache( table );
- // set css for headers
- setHeadersCss( table, $headers, sortList, sortCSS, sortMsg, config.columnToHeader );
+ // set css for headers
+ setHeadersCss( table, $headers, sortList, sortCSS, sortMsg, config.columnToHeader );
- // sort the table and append it to the dom
- appendToTable( table, multisort( table, sortList, cache ) );
- };
+ // sort the table and append it to the dom
+ appendToTable( table, multisort( table, sortList, cache ) );
+ };
- // sort initially
- if ( config.sortList.length > 0 ) {
- config.sortList = convertSortList( config.sortList );
- $table.data( 'tablesorter' ).sort();
- }
+ // sort initially
+ if ( config.sortList.length > 0 ) {
+ config.sortList = convertSortList( config.sortList );
+ $table.data( 'tablesorter' ).sort();
+ }
- } );
- },
+ } );
+ },
- addParser: function ( parser ) {
- if ( !getParserById( parser.id ) ) {
- parsers.push( parser );
- }
- },
-
- formatDigit: function ( s ) {
- var out, c, p, i;
- if ( ts.transformTable !== false ) {
- out = '';
- for ( p = 0; p < s.length; p++ ) {
- c = s.charAt( p );
- if ( c in ts.transformTable ) {
- out += ts.transformTable[ c ];
- } else {
- out += c;
- }
+ addParser: function ( parser ) {
+ if ( !getParserById( parser.id ) ) {
+ parsers.push( parser );
+ }
+ },
+
+ formatDigit: function ( s ) {
+ var out, c, p, i;
+ if ( ts.transformTable !== false ) {
+ out = '';
+ for ( p = 0; p < s.length; p++ ) {
+ c = s.charAt( p );
+ if ( c in ts.transformTable ) {
+ out += ts.transformTable[ c ];
+ } else {
+ out += c;
}
- s = out;
}
- i = parseFloat( s.replace( /[, ]/g, '' ).replace( '\u2212', '-' ) );
- return isNaN( i ) ? 0 : i;
- },
+ s = out;
+ }
+ i = parseFloat( s.replace( /[, ]/g, '' ).replace( '\u2212', '-' ) );
+ return isNaN( i ) ? 0 : i;
+ },
- formatFloat: function ( s ) {
- var i = parseFloat( s );
- return isNaN( i ) ? 0 : i;
- },
+ formatFloat: function ( s ) {
+ var i = parseFloat( s );
+ return isNaN( i ) ? 0 : i;
+ },
- formatInt: function ( s ) {
- var i = parseInt( s, 10 );
- return isNaN( i ) ? 0 : i;
- },
+ formatInt: function ( s ) {
+ var i = parseInt( s, 10 );
+ return isNaN( i ) ? 0 : i;
+ },
- clearTableBody: function ( table ) {
- $( table.tBodies[ 0 ] ).empty();
- },
+ clearTableBody: function ( table ) {
+ $( table.tBodies[ 0 ] ).empty();
+ },
- getParser: function ( id ) {
- buildTransformTable();
- buildDateTable();
- cacheRegexs();
- buildCollationTable();
+ getParser: function ( id ) {
+ buildTransformTable();
+ buildDateTable();
+ cacheRegexs();
+ buildCollationTable();
- return getParserById( id );
- },
+ return getParserById( id );
+ },
- getParsers: function () { // for table diagnosis
- return parsers;
- }
- };
+ getParsers: function () { // for table diagnosis
+ return parsers;
+ }
+ };
// Shortcut
ts = $.tablesorter;
return true;
},
format: function ( s ) {
+ var tsc;
s = $.trim( s.toLowerCase() );
if ( ts.collationRegex ) {
- var tsc = ts.collationTable;
+ tsc = ts.collationTable;
s = s.replace( ts.collationRegex, function ( match ) {
var r = tsc[ match ] ? tsc[ match ] : tsc[ match.toUpperCase() ];
return r.toLowerCase();
if ( !matches ) {
return $.tablesorter.formatFloat( 0 );
}
- isodate = new Date( matches[ 2 ] + '/' + matches[ 3 ] + '/' + matches[ 1 ] );
+ isodate = new Date( matches[ 2 ] + '/' + matches[ 3 ] + '/' + matches[ 1 ] );
} else {
matches = s.match( ts.rgx.isoDate[ 0 ] );
if ( !matches ) {