* @author Christian Bach/christian.bach@polyester.se
*/
-( function( $ ) {
+( function ( $, mw ) {
/* Local scope */
- var ts,
+ var ts,
parsers = [];
/* Parser utility functions */
function getElementText( node ) {
var $node = $( node ),
- data = $node.attr( 'data-sort-value' );
- if ( data !== undefined ) {
- return data;
+ // Use data-sort-value attribute.
+ // Use data() instead of attr() so that live value changes
+ // are processed as well (bug 38152).
+ data = $node.data( 'sortValue' );
+
+ if ( data !== null && data !== undefined ) {
+ // Cast any numbers or other stuff to a string, methods
+ // like charAt, toLowerCase and split are expected.
+ return String( data );
} else {
return $node.text();
}
}
function detectParserForColumn( table, rows, cellIndex ) {
- var l = parsers.length,
+ var l = parsers.length,
nodeValue,
// Start with 1 because 0 is the fallback parser
i = 1,
}
function buildParserCache( table, $headers ) {
- var rows = table.tBodies[0].rows,
+ var rows = table.tBodies[0].rows,
sortType,
parsers = [];
if ( rows[0] ) {
- var cells = rows[0].cells,
+ var cells = rows[0].cells,
len = cells.length,
i, parser;
/* Other utility functions */
function buildCache( table ) {
- var totalRows = ( table.tBodies[0] && table.tBodies[0].rows.length ) || 0,
+ var totalRows = ( table.tBodies[0] && table.tBodies[0].rows.length ) || 0,
totalCells = ( table.tBodies[0].rows[0] && table.tBodies[0].rows[0].cells.length ) || 0,
parsers = table.config.parsers,
cache = {
for ( var i = 0; i < totalRows; ++i ) {
// Add the table data to main data array
- var $row = $( table.tBodies[0].rows[i] ),
+ var $row = $( table.tBodies[0].rows[i] ),
cols = [];
// if this is a child row, add it to the last row's children and
}
function appendToTable( table, cache ) {
- var row = cache.row,
+ var row = cache.row,
normalized = cache.normalized,
totalRows = normalized.length,
checkCell = ( normalized[0].length - 1 ),
}
table.tBodies[0].appendChild( fragment );
}
-
+
/**
* Find all header rows in a thead-less table and put them in a <thead> tag.
* This only treats a row as a header row if it contains only <th>s (no <td>s)
* and if it is preceded entirely by header rows. The algorithm stops when
* it encounters the first non-header row.
- *
+ *
* After this, it will look at all rows at the bottom for footer rows
* And place these in a tfoot using similar rules.
* @param $table jQuery object for a <table>
- */
+ */
function emulateTHeadAndFoot( $table ) {
var $rows = $table.find( '> tbody > tr' );
if( !$table.get(0).tHead ) {
var $thead = $( '<thead>' );
- $rows.each( function() {
+ $rows.each( function () {
if ( $(this).children( 'td' ).length > 0 ) {
// This row contains a <td>, so it's not a header row
// Stop here
break;
}
$tfoot.prepend( $( $rows[i] ));
- }
+ }
$table.append( $tfoot );
}
}
function buildHeaders( table, msg ) {
- var maxSeen = 0,
+ var maxSeen = 0,
longest,
realCellIndex = 0,
$tableHeaders = $( 'thead:eq(0) > tr', table );
if ( $tableHeaders.length > 1 ) {
- $tableHeaders.each( function() {
+ $tableHeaders.each( function () {
if ( this.cells.length > maxSeen ) {
maxSeen = this.cells.length;
longest = this;
});
$tableHeaders = $( longest );
}
- $tableHeaders = $tableHeaders.children( 'th' ).each( function( index ) {
+ $tableHeaders = $tableHeaders.children( 'th' ).each( function ( index ) {
this.column = realCellIndex;
var colspan = this.colspan;
function isValueInArray( v, a ) {
var l = a.length;
for ( var i = 0; i < l; i++ ) {
- if ( a[i][0] == v ) {
+ if ( a[i][0] === v ) {
return true;
}
}
$headers.removeClass( css[0] ).removeClass( css[1] );
var h = [];
- $headers.each( function( offset ) {
+ $headers.each( function ( offset ) {
if ( !this.sortDisabled ) {
h[this.column] = $( this );
}
function explodeRowspans( $table ) {
// Split multi row cells into multiple cells with the same content
- $table.find( '> tbody > tr > [rowspan]' ).each(function() {
+ $table.find( '> tbody > tr > [rowspan]' ).each(function () {
var rowSpan = this.rowSpan;
this.rowSpan = 1;
var cell = $( this );
* @param $tables {jQuery}
* @param settings {Object} (optional)
*/
- construct: function( $tables, settings ) {
- return $tables.each( function( i, table ) {
+ construct: function ( $tables, settings ) {
+ return $tables.each( function ( i, table ) {
// Declare and cache.
- var $document, $headers, cache, config, sortOrder,
+ var $document, $headers, cache, config, sortOrder,
$table = $( table ),
shiftDown = 0,
firstTime = true;
// 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;
// Apply event handling to headers
// this is too big, perhaps break it out?
- $headers.click( function( e ) {
- if ( e.target.nodeName.toLowerCase() == 'a' ) {
+ $headers.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
return true;
explodeRowspans( $table );
// try to auto detect column type, and store in tables config
table.config.parsers = buildParserCache( table, $headers );
- // build the cache for the tbody cells
- cache = buildCache( table );
}
+
+ // 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 );
+
var totalRows = ( $table[0].tBodies[0] && $table[0].tBodies[0].rows.length ) || 0;
if ( !table.sortDisabled && totalRows > 0 ) {
for ( var j = 0; j < config.sortList.length; j++ ) {
var s = config.sortList[j],
o = config.headerList[s[0]];
- if ( s[0] == i ) {
+ if ( s[0] === i ) {
o.count = s[1];
o.count++;
s[1] = o.count % 2;
}
// Cancel selection
- } ).mousedown( function() {
+ } ).mousedown( function () {
if ( config.cancelSelection ) {
- this.onselectstart = function() {
+ this.onselectstart = function () {
return false;
};
return false;
} );
},
- addParser: function( parser ) {
- var l = parsers.length,
+ addParser: function ( parser ) {
+ var l = parsers.length,
a = true;
for ( var i = 0; i < l; i++ ) {
- if ( parsers[i].id.toLowerCase() == parser.id.toLowerCase() ) {
+ if ( parsers[i].id.toLowerCase() === parser.id.toLowerCase() ) {
a = false;
}
}
}
},
- formatDigit: function( s ) {
+ formatDigit: function ( s ) {
if ( ts.transformTable !== false ) {
- var out = '',
+ var out = '',
c;
for ( var p = 0; p < s.length; p++ ) {
c = s.charAt(p);
return ( isNaN(i)) ? 0 : i;
},
- formatFloat: function( s ) {
+ formatFloat: function ( s ) {
var i = parseFloat(s);
return ( isNaN(i)) ? 0 : i;
},
- formatInt: function( s ) {
+ formatInt: function ( s ) {
var i = parseInt( s, 10 );
return ( isNaN(i)) ? 0 : i;
},
- clearTableBody: function( table ) {
+ clearTableBody: function ( table ) {
if ( $.browser.msie ) {
- var empty = function( el ) {
+ var empty = function ( el ) {
while ( el.firstChild ) {
el.removeChild( el.firstChild );
}
ts = $.tablesorter;
// Register as jQuery prototype method
- $.fn.tablesorter = function( settings ) {
+ $.fn.tablesorter = function ( settings ) {
return ts.construct( this, settings );
};
// Add default parsers
ts.addParser( {
id: 'text',
- is: function( s ) {
+ is: function ( s ) {
return true;
},
- format: function( s ) {
+ format: function ( s ) {
s = $.trim( s.toLowerCase() );
if ( ts.collationRegex ) {
var tsc = ts.collationTable;
- s = s.replace( ts.collationRegex, function( match ) {
+ s = s.replace( ts.collationRegex, function ( match ) {
var r = tsc[match] ? tsc[match] : tsc[match.toUpperCase()];
return r.toLowerCase();
} );
ts.addParser( {
id: 'IPAddress',
- is: function( s ) {
+ is: function ( s ) {
return ts.rgx.IPAddress[0].test(s);
},
- format: function( s ) {
- var a = s.split( '.' ),
+ format: function ( s ) {
+ var a = s.split( '.' ),
r = '',
l = a.length;
for ( var i = 0; i < l; i++ ) {
var item = a[i];
- if ( item.length == 1 ) {
+ if ( item.length === 1 ) {
r += '00' + item;
- } else if ( item.length == 2 ) {
+ } else if ( item.length === 2 ) {
r += '0' + item;
} else {
r += item;
ts.addParser( {
id: 'currency',
- is: function( s ) {
+ is: function ( s ) {
return ts.rgx.currency[0].test(s);
},
- format: function( s ) {
+ format: function ( s ) {
return $.tablesorter.formatDigit( s.replace( ts.rgx.currency[1], '' ) );
},
type: 'numeric'
ts.addParser( {
id: 'url',
- is: function( s ) {
+ is: function ( s ) {
return ts.rgx.url[0].test(s);
},
- format: function( s ) {
+ format: function ( s ) {
return $.trim( s.replace( ts.rgx.url[1], '' ) );
},
type: 'text'
ts.addParser( {
id: 'isoDate',
- is: function( s ) {
+ is: function ( s ) {
return ts.rgx.isoDate[0].test(s);
},
- format: function( s ) {
+ format: function ( s ) {
return $.tablesorter.formatFloat((s !== '') ? new Date(s.replace(
new RegExp( /-/g), '/')).getTime() : '0' );
},
ts.addParser( {
id: 'usLongDate',
- is: function( s ) {
+ is: function ( s ) {
return ts.rgx.usLongDate[0].test(s);
},
- format: function( s ) {
+ format: function ( s ) {
return $.tablesorter.formatFloat( new Date(s).getTime() );
},
type: 'numeric'
ts.addParser( {
id: 'date',
- is: function( s ) {
+ is: function ( s ) {
return ( ts.dateRegex[0].test(s) || ts.dateRegex[1].test(s) || ts.dateRegex[2].test(s ));
},
- format: function( s, table ) {
+ format: function ( s, table ) {
+ var match;
s = $.trim( s.toLowerCase() );
- var match;
if ( ( match = s.match( ts.dateRegex[0] ) ) !== null ) {
- if ( mw.config.get( 'wgDefaultDateFormat' ) == 'mdy' || mw.config.get( 'wgContentLanguage' ) == 'en' ) {
+ if ( mw.config.get( 'wgDefaultDateFormat' ) === 'mdy' || mw.config.get( 'wgContentLanguage' ) === 'en' ) {
s = [ match[3], match[1], match[2] ];
- } else if ( mw.config.get( 'wgDefaultDateFormat' ) == 'dmy' ) {
+ } else if ( mw.config.get( 'wgDefaultDateFormat' ) === 'dmy' ) {
s = [ match[3], match[2], match[1] ];
}
} else if ( ( match = s.match( ts.dateRegex[1] ) ) !== null ) {
}
// Pad Month and Day
- if ( s[1].length == 1 ) {
+ if ( s[1].length === 1 ) {
s[1] = '0' + s[1];
}
- if ( s[2].length == 1 ) {
+ if ( s[2].length === 1 ) {
s[2] = '0' + s[2];
}
ts.addParser( {
id: 'time',
- is: function( s ) {
+ is: function ( s ) {
return ts.rgx.time[0].test(s);
},
- format: function( s ) {
+ format: function ( s ) {
return $.tablesorter.formatFloat( new Date( '2000/01/01 ' + s ).getTime() );
},
type: 'numeric'
ts.addParser( {
id: 'number',
- is: function( s, table ) {
+ is: function ( s, table ) {
return $.tablesorter.numberRegex.test( $.trim( s ));
},
- format: function( s ) {
+ format: function ( s ) {
return $.tablesorter.formatDigit(s);
},
type: 'numeric'
} );
-} )( jQuery );
+}( jQuery, mediaWiki ) );