Benutzer:MatthiasDD/ts test.js
aus Wikipedia, der freien Enzyklopädie
< Benutzer:MatthiasDD
Dies ist die aktuelle Version dieser Seite, zuletzt bearbeitet am 13. Oktober 2020 um 22:26 Uhr durch imported>Jon (WMF)(2262197) (maintenance: more info TypeError: $nextRows[i] is undefined @User:MatthiasDD).
Hinweis: Leere nach dem Veröffentlichen den Browser-Cache, um die Änderungen sehen zu können.
- Firefox/Safari: Umschalttaste drücken und gleichzeitig Aktualisieren anklicken oder entweder Strg+F5 oder Strg+R (⌘+R auf dem Mac) drücken
- Google Chrome: Umschalttaste+Strg+R (⌘+Umschalttaste+R auf dem Mac) drücken
- Internet Explorer/Edge: Strg+F5 drücken oder Strg drücken und gleichzeitig Aktualisieren anklicken
- Opera: Strg+F5
/* Diagnosis of sortable tables with new RegExp's
*
* * After [Ctrl] + [right-click] at header use the original parsers of tablesorter,
* the backgroundcolor of cells is changed depend of data type,
* the tooltip shows the (for this cell detected) and used data type and sortvalue.
* * After [right-click] at header the new RegExp's from this file are used,
* a alert() message shows the sort type and sortkeys for this row,
* the backgroundcolor of cells is changed depend of data type,
* the tooltip shows the (for this cell detected) and used data type and sortvalue.
* This script uses code copyed from MediaWiki 1.27.0-wmf.18
* http://git.wikimedia.org/history/mediawiki%2Fcore.git/9179f661b63a3cb23b7236be5b216bbba765ce80/resources%2Fsrc%2Fjquery%2Fjquery.tablesorter.js
* last change: 2018-01-12 Formatfix Use native ES5 Array prototype methods instead of jQuery
*/
( function ( $, mw ) {
// tablesorter needs time to initialize tables at first (mediawiki.page.ready.js)
mw.loader.using( "jquery.tablesorter", function(){ setTimeout( ts_diagnosis_init, 500 ); } );
function ts_diagnosis_init() {
if( $('table.sortable').length ){
//tablesorter.construct() have to run earlier!
if( $('table.jquery-tablesorter').length ){
construct( $('table.sortable') );
}else{
console.log("ts_diagnosis.js not initialised.");
}
}
}
var ts = {
dateRegex: [],
monthNames: {}
},
newParsersActive = true, //DIAGNOSIS
newParsers = [], //DIAGNOSIS
parsers = [];
/* Parser utility functions */
function getParserById( name ) {
var i;
if ( name.toLowerCase() === 'currency' ) { //Fallback for removed parser
name = 'number';
}
for ( i = 0; i < parsers.length; i++ ) {
if ( parsers[ i ].id.toLowerCase() === name.toLowerCase() ) {
return parsers[ i ];
}
}
return false;
}
function getElementSortKey( node ) {
var $node = $( node ),
// Use data-sort-value attribute.
// Use data() instead of attr() so that live value changes
// are processed as well (T40152).
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 );
}
if ( !node ) {
return $node.text();
}
if ( node.tagName.toLowerCase() === 'img' ) {
return $node.attr( 'alt' ) || ''; // handle undefined alt
}
return $.makeArray( node.childNodes ).map( function ( elem ) {
if ( elem.nodeType === Node.ELEMENT_NODE ) {
if ( $(elem).hasClass('reference') ) {
return null;
} else {
return getElementSortKey( elem );
}
}
return $.text( elem );
} ).join( '' );
}
//DIAGNOSIS
function mark_cell( node, ParserId ) {
var Color, value;
switch (ParserId) {
case 'IPAddress': Color = '#FFCBCB'; break;
case 'url': Color = '#FFCF55'; break;
case 'isoDate': Color = '#FFEBAD'; break;
case 'usLongDate': Color = '#FFD88B'; break;
case 'asianDate': Color = '#FFEED0'; break;
case 'date': Color = '#FFFF7F'; break;
case 'time': Color = '#B9FFC5'; break;
case 'number': Color = '#98FB98'; break;
default: Color = '#FFFFFF'; //text
}
value = $( node ).attr( 'style' ) + ';background:' + Color;
$( node ).attr( 'style' , value );
}
//DIAGNOSIS: similar to detectParserForColumn()
function detectParserForCell(table, rows, rowIndex, cellIndex){
var //parsers = $.tablesorter.getParsers(),
l = parsers.length,
nodeValue,
i = 1;
if ( rows[ rowIndex ] && rows[ rowIndex ].cells[ cellIndex ] ) {
nodeValue = $.trim( getElementSortKey( rows[ rowIndex ].cells[ cellIndex ] ) );
} else {
nodeValue = '';
}
while ( i < l ){
if ( nodeValue !== '' ){
if ( parsers[i].is( nodeValue, table ) ){
mark_cell( rows[ rowIndex ].cells[ cellIndex ], parsers[i].id );//DIAGNOSIS
return parsers[i]; // Confirmed the parser, let's return it
} else {
i++; // Check next parser
}
}
else { return parsers[0]; } // Empty cell
}
mark_cell( rows[ rowIndex ].cells[ cellIndex ], parsers[0].id );//DIAGNOSIS only if different parsers used
return parsers[0];
}
//DIAGNOSIS: similar to buildCache()
function addTitleTags( table ) {
var i, j, row,
totalRows = ( table.tBodies[0] && table.tBodies[0].rows.length) || 0,
cellsInRow,
config = $( table ).data( 'tablesorter' ).config,
$node, out, data, cellParser; // extra Variable
//Show Title tag for isoDate as readable Date instead of sortkey
function formatString( parser, ElementSortKey ) {
if ( parser.id === 'isoDate' ) {
return new Date( parser.format( ElementSortKey ) ).toISOString( );
} else {
return parser.format( ElementSortKey );
}
}
for ( i = 0; i < totalRows; ++i ) {
row = table.tBodies[0].rows[i] ;
// if this is a child row, add it to the last row's children and
// continue to the next row
if ( $( row ).hasClass( config.cssChildRow ) ) {
continue;
}
cellsInRow = ( row.cells.length ) || 0; //all cells in this row
for ( j = 0; j < cellsInRow; ++j ) {
$node = $( row.cells[j] );
// if exist, show attribut data-sort-value
data = $node.attr( 'data-sort-value' );
if ( data !== undefined ) {
out = "data-sort-value = \"" +data +"\"\n";
} else {
out = "";
}
// detect Parser for every cell
cellParser = detectParserForCell( table, table.tBodies[0].rows, i, j );
// show Parser.id and sortkey
out += cellParser.id + ": " + formatString( cellParser, getElementSortKey( row.cells[j] ) );
// if used parser different show both
if ( config.parsers[j] !== cellParser ) {
out = "(" + out + ")\n" + config.parsers[j].id + ": " + formatString( config.parsers[j], getElementSortKey( row.cells[j] ) );
}
//index from explodeRowspans()
//if ( $node.data( 'tablesorter' ) ) {
// out += ", realCellIndex: " +$node.data( 'tablesorter' ).realCellIndex;
//}
// add to title
$node.attr( 'title', out );
}
}
}
//end DIAGNOSIS
function detectParserForColumn( table, rows, column ) {
var l = parsers.length,
config = $( table ).data( 'tablesorter' ).config,
cellIndex,
nodeValue,
nextRow = false,
// Start with 1 because 0 is the fallback parser
i = 1,
lastRowIndex = -1,
rowIndex = 0,
concurrent = 0,
empty = 0,
needed = ( rows.length > 4 ) ? 5 : rows.length;
while ( i < l ) {
// if this is a child row, continue to the next row (as buildCache())
if ( rows[ rowIndex ] && !$( rows[ rowIndex ] ).hasClass( config.cssChildRow ) ) {
if ( rowIndex !== lastRowIndex ) {
lastRowIndex = rowIndex;
cellIndex = $( rows[ rowIndex ] ).data( 'columnToCell' )[ column ];
nodeValue = getElementSortKey( rows[ rowIndex ].cells[ cellIndex ] ).trim();
}
} else {
nodeValue = '';
}
if ( nodeValue !== '' ) {
if ( parsers[ i ].is( nodeValue, table ) ) {
concurrent++;
nextRow = true;
if ( concurrent >= needed ) {
// Confirmed the parser for multiple cells, let's return it
return parsers[ i ];
}
} else if ( parsers[ i ].id.match( /isoDate|date/ ) && /^\D*(\d{1,4}) ?(\[.+\])?$/.test( nodeValue ) ) {
// For 1-4 digits and maybe reference(s) parser "isoDate", "date" or "number" is possible, check next row
empty++;
nextRow = true;
} else {
// Check next parser, reset rows
i++;
rowIndex = 0;
concurrent = 0;
empty = 0;
nextRow = false;
}
} else {
// Empty cell
empty++;
nextRow = true;
}
if ( nextRow ) {
nextRow = false;
rowIndex++;
if ( rowIndex >= rows.length ) {
if ( concurrent > 0 && concurrent >= rows.length - empty ) {
// Confirmed the parser for all filled cells
return parsers[ i ];
}
// Check next parser, reset rows
i++;
rowIndex = 0;
concurrent = 0;
empty = 0;
}
}
}
// 0 is always the generic parser (text)
return parsers[ 0 ];
}
function buildParserCache( table, $headers ) {
var sortType, len, j, parser,
rows = table.tBodies[ 0 ].rows,
config = $( table ).data( 'tablesorter' ).config,
parsers = [];
if ( rows[ 0 ] ) {
len = config.columns;
for ( j = 0; j < len; j++ ) {
parser = false;
sortType = $headers.eq( config.columnToHeader[ j ] ).data( 'sortType' );
if ( sortType !== undefined ) {
parser = getParserById( sortType );
}
if ( parser === false ) {
parser = detectParserForColumn( table, rows, j );
}
parsers.push( parser );
}
}
return parsers;
}
/* Other utility functions */
function buildCache( table ) {
var i, j, $row, cols,
totalRows = ( table.tBodies[ 0 ] && table.tBodies[ 0 ].rows.length ) || 0,
config = $( table ).data( 'tablesorter' ).config,
parsers = config.parsers,
len = parsers.length,
cellIndex,
cache = {
row: [],
normalized: []
};
for ( i = 0; i < totalRows; i++ ) {
// Add the table data to main data array
$row = $( table.tBodies[ 0 ].rows[ i ] );
cols = [];
// if this is a child row, add it to the last row's children and
// continue to the next row
if ( $row.hasClass( config.cssChildRow ) ) {
//if ( cache.row.length > 0 ) {
cache.row[ cache.row.length - 1 ] = cache.row[ cache.row.length - 1 ].add( $row );
//}
// go to the next for loop
continue;
}
cache.row.push( $row );
for ( j = 0; j < len; j++ ) {
cellIndex = $row.data( 'columnToCell' )[ j ];
cols.push( parsers[ j ].format( getElementSortKey( $row[ 0 ].cells[ cellIndex ] ) ) );
}
cols.push( cache.normalized.length ); // add position for rowCache
cache.normalized.push( cols );
cols = null;
}
return cache;
}
function appendToTable( table, cache ) {
var i, pos, l, j,
row = cache.row,
normalized = cache.normalized,
totalRows = normalized.length,
checkCell = ( normalized[ 0 ].length - 1 ),
fragment = document.createDocumentFragment();
for ( i = 0; i < totalRows; i++ ) {
pos = normalized[ i ][ checkCell ];
l = row[ pos ].length;
for ( j = 0; j < l; j++ ) {
fragment.appendChild( row[ pos ][ j ] );
}
}
table.tBodies[ 0 ].appendChild( fragment );
$( table ).trigger( 'sortEnd.tablesorter' );
}
/**
* 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 {jQuery} $table object for a <table>
*/
function emulateTHeadAndFoot( $table ) {
var $thead, $tfoot, i, len,
$rows = $table.find( '> tbody > tr' );
if ( !$table.get( 0 ).tHead ) {
$thead = $( '<thead>' );
$rows.each( function () {
if ( $( this ).children( 'td' ).length ) {
// This row contains a <td>, so it's not a header row
// Stop here
return false;
}
$thead.append( this );
} );
$table.find( ' > tbody:first' ).before( $thead );
}
if ( !$table.get( 0 ).tFoot ) {
$tfoot = $( '<tfoot>' );
len = $rows.length;
for ( i = len - 1; i >= 0; i-- ) {
if ( $( $rows[ i ] ).children( 'td' ).length ) {
break;
}
$tfoot.prepend( $( $rows[ i ] ) );
}
$table.append( $tfoot );
}
}
function uniqueElements( array ) {
var uniques = [];
array.forEach( function ( elem ) {
if ( elem !== undefined && uniques.indexOf( elem ) === -1 ) {
uniques.push( elem );
}
} );
return uniques;
}
function buildHeaders( table, msg ) {
var config = $( table ).data( 'tablesorter' ).config,
maxSeen = 0,
colspanOffset = 0,
columns,
k,
$cell,
rowspan,
colspan,
headerCount,
longestTR,
headerIndex,
exploded,
$tableHeaders = $( [] ),
$tableRows = $( 'thead:eq(0) > tr', table );
if ( $tableRows.length <= 1 ) {
$tableHeaders = $tableRows.children( 'th' );
} else {
exploded = [];
// Loop through all the dom cells of the thead
$tableRows.each( function ( rowIndex, row ) {
$.each( row.cells, function ( columnIndex, cell ) {
var matrixRowIndex,
matrixColumnIndex;
rowspan = Number( cell.rowSpan );
colspan = Number( cell.colSpan );
// Skip the spots in the exploded matrix that are already filled
while ( exploded[ rowIndex ] && exploded[ rowIndex ][ columnIndex ] !== undefined ) {
++columnIndex;
}
// Find the actual dimensions of the thead, by placing each cell
// in the exploded matrix rowspan times colspan times, with the proper offsets
for ( matrixColumnIndex = columnIndex; matrixColumnIndex < columnIndex + colspan; ++matrixColumnIndex ) {
for ( matrixRowIndex = rowIndex; matrixRowIndex < rowIndex + rowspan; ++matrixRowIndex ) {
if ( !exploded[ matrixRowIndex ] ) {
exploded[ matrixRowIndex ] = [];
}
exploded[ matrixRowIndex ][ matrixColumnIndex ] = cell;
}
}
} );
} );
// We want to find the row that has the most columns (ignoring colspan)
exploded.forEach( function ( cellArray, index ) {
headerCount = $( uniqueElements( cellArray ) ).filter( 'th' ).length;
if ( headerCount >= maxSeen ) {
maxSeen = headerCount;
longestTR = index;
}
} );
// We cannot use $.unique() here because it sorts into dom order, which is undesirable
$tableHeaders = $( uniqueElements( exploded[ longestTR ] ) ).filter( 'th' );
}
// 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
config.columnToHeader = [];
config.headerToColumns = [];
config.headerList = [];
headerIndex = 0;
$tableHeaders.each( function () {
$cell = $( this );
columns = [];
if ( !$cell.hasClass( config.unsortableClass ) ) {
$cell
.addClass( config.cssHeader )
.prop( 'tabIndex', 0 )
.attr( {
role: 'columnheader button',
title: msg[ 1 ]
} );
for ( k = 0; k < this.colSpan; k++ ) {
config.columnToHeader[ colspanOffset + k ] = headerIndex;
columns.push( colspanOffset + k );
}
config.headerToColumns[ headerIndex ] = columns;
$cell.data( {
headerIndex: headerIndex,
order: 0,
count: 0
} );
// add only sortable cells to headerList
config.headerList[ headerIndex ] = this;
headerIndex++;
}
colspanOffset += this.colSpan;
} );
// number of columns with extended colspan, inclusive unsortable
// parsers[j], cache[][j], columnToHeader[j], columnToCell[j] have so many elements
config.columns = colspanOffset;
return $tableHeaders.not( '.' + config.unsortableClass );
}
function isValueInArray( v, a ) {
var i;
for ( i = 0; i < a.length; i++ ) {
if ( a[ i ][ 0 ] === v ) {
return true;
}
}
return false;
}
/**
* Sets the sort count of the columns that are not affected by the sorting to have them sorted
* in default (ascending) order when their header cell is clicked the next time.
*
* @param {jQuery} $headers
* @param {Array} sortList 2D number array
* @param {Array} headerToColumns 2D number array
*/
function setHeadersOrder( $headers, sortList, headerToColumns ) {
// Loop through all headers to retrieve the indices of the columns the header spans across:
headerToColumns.forEach( function ( columns, headerIndex ) {
columns.forEach( function ( columnIndex, i ) {
var header = $headers[ headerIndex ],
$header = $( header );
if ( !isValueInArray( columnIndex, sortList ) ) {
// Column shall not be sorted: Reset header count and order.
$header.data( {
order: 0,
count: 0
} );
} else {
// Column shall be sorted: Apply designated count and order.
sortList.forEach( function ( sortColumn ) {
if ( sortColumn[ 0 ] === i ) {
$header.data( {
order: sortColumn[ 1 ],
count: sortColumn[ 1 ] + 1
} );
return false;
}
} );
}
} );
} );
}
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 ( i = 0, len = list.length; i < len; i++ ) {
$headers
.eq( columnToHeader[ list[ i ][ 0 ] ] )
.addClass( css[ list[ i ][ 1 ] ] )
.attr( 'title', msg[ list[ i ][ 1 ] ] );
}
}
function sortText( a, b ) {
return ( ( a < b ) ? -1 : ( ( a > b ) ? 1 : 0 ) );
}
function sortTextDesc( a, b ) {
return ( ( b < a ) ? -1 : ( ( b > a ) ? 1 : 0 ) );
}
function multisort( table, sortList, cache ) {
var i,
sortFn = [];
for ( i = 0; i < sortList.length; i++ ) {
sortFn[ i ] = ( sortList[ i ][ 1 ] ) ? sortTextDesc : sortText;
}
cache.normalized.sort( function ( array1, array2 ) {
var i, col, ret;
for ( i = 0; i < sortList.length; i++ ) {
col = sortList[ i ][ 0 ];
ret = sortFn[ i ].call( this, array1[ col ], array2[ col ] );
if ( ret !== 0 ) {
return ret;
}
}
// Fall back to index number column to ensure stable sort
return sortText.call( this, array1[ array1.length - 1 ], array2[ array2.length - 1 ] );
} );
return cache;
}
function buildTransformTable() {
if ( ts.numberRegex ) {
return;
}
var ascii, localised, i, tenLocal, digit, digitgroup, number, exponent,
decimalSeparator = '.',
groupSeparator = ',',
ten = '10',
digits = '0123456789'.split( '' ),
separatorTransformTable = mw.config.get( 'wgSeparatorTransformTable' ) || ['',''],
digitTransformTable = mw.config.get( 'wgDigitTransformTable' ) || ['',''];
//only for test: add some persian digits (from \core\languages\messages\MessagesFa.php)
if ( digitTransformTable[ 0 ].length ) {
digitTransformTable[ 0 ] += '\t';
digitTransformTable[ 1 ] += '\t';
}
digitTransformTable[ 0 ] += "0\t1\t2\t3\t9";
digitTransformTable[ 1 ] += "۰\t۱\t۲\t۳\t۹";
decimalSeparator = mw.RegExp.escape( decimalSeparator );
if ( separatorTransformTable === null || ( separatorTransformTable[ 0 ] === '' && digitTransformTable[ 0 ] === '' ) ) {
ts.transformTable = false;
} else {
ts.transformTable = {};
tenLocal = {};
// Unpack the transform table
ascii = separatorTransformTable[ 0 ].split( '\t' ).concat( digitTransformTable[ 0 ].split( '\t' ) );
localised = separatorTransformTable[ 1 ].split( '\t' ).concat( digitTransformTable[ 1 ].split( '\t' ) );
// Construct regexes for number identification
for ( i = 0; i < ascii.length; i++ ) {
ts.transformTable[ localised[ i ] ] = ascii[ i ];
switch ( ascii[ i ] ) {
case '.' :
decimalSeparator = mw.RegExp.escape( localised[ i ] );
break;
case ',' :
groupSeparator = mw.RegExp.escape( localised[ i ] );
break;
case '0' :
case '1' :
tenLocal[ ascii[ i ] ] = localised[ i ];
default :
digits.push( mw.RegExp.escape( localised[ i ] ) );
}
}
// Construct regex for number 10 identification
ten = '(10|' + tenLocal[ '1' ] + tenLocal[ '0' ] +')';
}
digit = digits.join( '' );
digitgroup = '[' + digit + groupSeparator + ']*';
number = '[-+\u2212]?[' + digit + '∞]' + digitgroup;
exponent = '[-+\u2212]?[' + digit + ']{1,3}';
ts.SpacesInNumber = new RegExp( /[ ']/g ) //     ' removed for number // or numbered Reference |\[\d+\]
ts.scientificRegex = new RegExp( '[×·⋅]' + ten + '\\^?' ); // ×·⋅ = \u00D7\u00B7\u22C5\ = ×·⋅
// w:en:Template:ntsh write a not displayed ^
/*ts.numberRegex = new RegExp( '^(' + '[-+\u2212]?[0-9][0-9,]*(\\.[0-9,]*)?(E[-+\u2212]?[0-9][0-9,]*)?' + // Fortran-style scientific
'|' + '[-+\u2212]?' + digitClass + '+[\\s\\xa0]*%?' + // Generic localised
')$', 'i' );*/
//Spaces defined in SpacesInNumber are removed and
//ScientificRegex replaced with 'e' before test.
ts.numberRegex = new RegExp( '^[^-+\u2212' + digit + '∞]*?(' +
number + '(' + decimalSeparator + digitgroup + ')?(e' + exponent + ')?' + // Generic localised
')', 'i' );
//All Templates that force alphabetic sorting for numbers have to change before this can remove:
//w:en:Template:dts write "000000002015-01-01-0000January 1 2015" and use parser 'text'
// Three '-' and no isoDate is not a number. ... now it's isoDate (-\d+-\d+-\d+)|
//w:en:Template:nts and w:en:Template:ntsh write "e09 1 1e9" and use parser 'text'
// No digit before 'e' is is not a number.
ts.notNumberRegex = new RegExp( /^[-+\u2212]?e/i );
}
function buildDateTable() {
var i, name, yearBC, bc,
months = [];
ts.monthNames = {};
for ( i = 0; i < 12; i++ ) {
name = mw.language.months.names[ i ].toLowerCase();
ts.monthNames[ name ] = i + 1;
months.push( mw.RegExp.escape( name ) );
name = mw.language.months.genitive[ i ].toLowerCase();
ts.monthNames[ name ] = i + 1;
months.push( mw.RegExp.escape( name ) );
name = mw.language.months.abbrev[ i ].toLowerCase().replace( '.', '' );
ts.monthNames[ name ] = i + 1;
months.push( mw.RegExp.escape( name ) );
}
// Build piped string
months = months.join( '|' );
/**
* Data yearBC - String to detect years before year 0 in parser 'date'.
* Case insensitive, a dot stay for zero or more characters of [.- ],
* after this can follow more characters.
*
* Example: "ab.C." or "AB.C" is the same and matches
* to abc, ABC, abcD, ab C, ab.-C., ab. Chr.
* but not to: bc (missing a), a b c (a and b not connected).
*
* This String is not used for asian dates with [前전] before year.
* A minus before plain year is detected without this String.
*
* @return {string}
*/
yearBC = mw.language.getData( mw.config.get("wgPageContentLanguage"), 'yearBC' );
bc = '(';
if ( yearBC ) {
yearBC = yearBC.split( /[.\- \xa0]/ ); //remove . -
for ( i = 0; i < yearBC.length; i++ ) {
if ( yearBC[ i ] ) {
bc += '\\s*' + yearBC[ i ].toLowerCase();
}
}
}
bc += ')?';
// Build RegEx
ts.dateSeparatorRegex = new RegExp( /[,.\-\/]/g ); //replaced to space before test
// Three parts of digits: mdy dmy ymd, separated with , . - / or any white space
ts.dateRegex[ 0 ] = new RegExp(
//'^\\D*(\\d{1,2})\\s+(\\d{1,2})\\s+(\\d{2,4})' + bc );
'^\\D*(\\d{1,4})\\s+(\\d{1,2})\\s+(\\d{1,4})' + bc );
// Written Month name: dm dmy ymd
ts.dateRegex[ 1 ] = new RegExp(
//'^\\D*(\\d{1,2})\\s+(' + months + ')\\b(\\s+\\d{1,4})?' + bc );
'^\\D*(\\d{1,4})\\s+(' + months + ')(\\s+\\d{1,4})?' + bc );
// Written Month name: m md my mdy
ts.dateRegex[ 2 ] = new RegExp(
// To detect a word boundary for non latin months add the parser a leading space.
//' (' + months + ') (\\s*\\d{1,4})?([\\s\']+\\d{1,4})?' + bc ); for '10 -> 2010
' (' + months + ') (\\s*\\d{1,4})?(\\s+\\d{1,4})?' + bc );
// Next 2 RegEx have no use for parser detection, because this match also to isoDate and number
// Digit Month and year: my ym
ts.dateRegex[ 3 ] = new RegExp(
//'^\\D*(\\d{1,2})\\s+(\\d{2,4})' + bc );
'^\\D*(\\d{1,4})\\s+(\\d{1,4})' + bc );
// Only year: y
ts.dateRegex[ 4 ] = new RegExp(
//bc + '[^\\d-\u2212]*([-\u2212]?\\d{1,4})(\\d*)?' + bc );
bc + '[^\\d-\u2212]*([-\u2212]?\\d+)' + bc );
}
/**
* Replace all rowspanned cells in the body with clones in each row, so sorting
* need not worry about them.
*
* @param {jQuery} $table jQuery object for a <table>
*/
function explodeRowspans( $table ) {
var spanningRealCellIndex, rowSpan, colSpan,
cell, cellData, i, $tds, $clone, $nextRows,
rowspanCells = $table.find( '> tbody > tr > [rowspan]' ).get();
//rowspanCells = $table.find( '> tbody > tr >' ).not( '[rowSpan="1"]' ).get(); //MK FF40: 1710% (650/38 ms) IE8: 4.7% (422/8859 ms)
// Short circuit
if ( !rowspanCells.length ) {
return;
}
// First, we need to make a property like cellIndex but taking into
// account colspans. We also cache the rowIndex to avoid having to take
// cell.parentNode.rowIndex in the sorting function below.
$table.find( '> tbody > tr' ).each( function () {
var i,
col = 0,
len = this.cells.length;
for ( i = 0; i < len; i++ ) {
$( this.cells[ i ] ).data( 'tablesorter', {
realCellIndex: col,
realRowIndex: this.rowIndex
} );
col += this.cells[ i ].colSpan;
}
} );
// Split multi row cells into multiple cells with the same content.
// Sort by column then row index to avoid problems with odd table structures.
// Re-sort whenever a rowspanned cell's realCellIndex is changed, because it
// might change the sort order.
function resortCells() {
var cellAData,
cellBData,
ret;
rowspanCells = rowspanCells.sort( function ( a, b ) {
cellAData = $.data( a, 'tablesorter' );
cellBData = $.data( b, 'tablesorter' );
ret = cellAData.realCellIndex - cellBData.realCellIndex;
if ( !ret ) {
ret = cellAData.realRowIndex - cellBData.realRowIndex;
}
return ret;
} );
rowspanCells.forEach( function ( cell ) {
$.data( cell, 'tablesorter' ).needResort = false;
} );
}
resortCells();
function filterfunc() {
return $.data( this, 'tablesorter' ).realCellIndex >= spanningRealCellIndex;
}
function fixTdCellIndex() {
$.data( this, 'tablesorter' ).realCellIndex += colSpan;
if ( this.rowSpan > 1 ) {
$.data( this, 'tablesorter' ).needResort = true;
}
}
while ( rowspanCells.length ) {
if ( $.data( rowspanCells[ 0 ], 'tablesorter' ).needResort ) {
resortCells();
}
cell = rowspanCells.shift();
cellData = $.data( cell, 'tablesorter' );
rowSpan = cell.rowSpan;
colSpan = cell.colSpan;
spanningRealCellIndex = cellData.realCellIndex;
cell.rowSpan = 1;
$nextRows = $( cell ).parent().nextAll();
for ( i = 0; i < rowSpan - 1; i++ ) {
$tds = $nextRows[ i ] ?
$( $nextRows[ i ].cells ).filter( filterfunc ) : false;
$clone = $( cell ).clone();
$clone.data( 'tablesorter', {
realCellIndex: spanningRealCellIndex,
realRowIndex: cellData.realRowIndex + i,
needResort: true
} );
if ( $tds.length ) {
$tds.each( fixTdCellIndex );
$tds.first().before( $clone );
} else {
$nextRows.eq( i ).append( $clone );
}
}
}
}
/**
* Build index to handle colspanned cells in the body.
* Set the cell index for each column in an array,
* so that colspaned cells set multiple in this array.
* columnToCell[collumnIndex] point at the real cell in this row.
*
* @param {jQuery} $table object for a <table>
*/
function manageColspans( $table ) {
var i, j, k, $row,
$rows = $table.find( '> tbody > tr' ),
totalRows = $rows.length || 0,
config = $table.data( 'tablesorter' ).config,
columns = config.columns,
columnToCell, cellsInRow, index;
for ( i = 0; i < totalRows; i++ ) {
$row = $rows.eq( i );
// if this is a child row, continue to the next row (as buildCache())
if ( $row.hasClass( config.cssChildRow ) ) {
// go to the next for loop
continue;
}
columnToCell = [];
cellsInRow = ( $row[ 0 ].cells.length ) || 0; // all cells in this row
index = 0; // real cell index in this row
for ( j = 0; j < columns; index++ ) {
if ( index === cellsInRow ) {
// Row with cells less than columns: add empty cell
$row.append( '<td>' );
cellsInRow++;
}
for ( k = 0; k < $row[ 0 ].cells[ index ].colSpan; k++ ) {
columnToCell[ j++ ] = index;
}
}
// Store it in $row
$row.data( 'columnToCell', columnToCell );
}
}
function buildCollationTable() {
var key, keys = [];
ts.collationTable = mw.config.get( 'tableSorterCollation' );
ts.collationRegex = null;
if ( ts.collationTable ) {
// Build array of key names
for ( key in ts.collationTable ) {
// Check hasOwn to be safe
if ( ts.collationTable.hasOwnProperty( key ) ) {
keys.push( mw.RegExp.escape( key ) );
}
}
if ( keys.length ) {
ts.collationRegex = new RegExp( keys.join( '|' ), 'ig' );
}
}
}
function cacheRegexs() {
if ( ts.rgx ) {
return;
}
ts.rgx = {
IPAddress: [
// new RegExp( /^\d{1,3}[\.]\d{1,3}[\.]\d{1,3}[\.]\d{1,3}$/ )
new RegExp( /(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})(\/\d{1,2})?/ )
],
//currency: [
// new RegExp( /(^[£$€¥]|[£$€¥]$)/ ),
// new RegExp( /[£$€¥]/g )
//],
url: [
// new RegExp( /^(https?|ftp|file):\/\/$/ ),
new RegExp( /^(https?|ftp|file):\/\// ),
new RegExp( /(https?|ftp|file):\/\// )
],
//templateDts: [
// new RegExp( /^(-?\d{12})-([01]\d)-([0-3]\d)-(\d{4})/ )
//],
isoDate: [
// new RegExp( /^([-+]?\d{1,4})-([01]\d)-([0-3]\d)([T\s]((([01]\d|2[0-3])(:?[0-5]\d)?|24:?00)?(:?([0-5]\d|60))?([.,]\d+)?)([zZ]|([\+-])([01]\d|2[0-3]):?([0-5]\d)?)?)?/ ),
new RegExp( /^[^-\d]*(-?\d{1,4})-(0\d|1[0-2])(-([0-3]\d))?([T\s]([01]\d|2[0-4]):?(([0-5]\d):?(([0-5]\d|60)([.,]\d{1,3})?)?)?([zZ]|([-+])([01]\d|2[0-3]):?([0-5]\d)?)?)?/ ),
// new RegExp( /^([-+]?\d{1,4})-([01]\d)-([0-3]\d)/ )
new RegExp( /^[^-\d]*(-?\d{1,4})-?(\d\d)?(-?(\d\d))?([T\s](\d\d):?((\d\d)?:?((\d\d)?([.,]\d{1,3})?)?)?([zZ]|([-+])(\d\d):?(\d\d)?)?)?/ )
],
usLongDate: [
// new RegExp( /^[A-Za-z]{3,10}\.? [0-9]{1,2}, ([0-9]{4}|'?[0-9]{2}) (([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(AM|PM)))$/ )
new RegExp( /([A-Za-z]{3,10}\.? [0-9]{1,2}, ([0-9]{4}|'?[0-9]{2}) (([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(AM|PM))))/ )
],
asianDate: [
new RegExp( /\d[年년月월일号号]/ ),
new RegExp( /[^前전\d]*([前전])?\s*(\d{1,4}[年년])?\s*(\d{1,2}[月월])?\s*(\d{1,2}[日일号])?/ )
],
time: [
// new RegExp( /^(([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(am|pm)))$/ )
new RegExp( /^\D*(\d{1,4}):([0-5]\d)(:[0-5]\d)?([.,]\d{1,3})?\s?(pm)?/i )
]
};
}
/**
* Converts sort objects [ { Integer: String }, ... ] to the internally used nested array
* structure [ [ Integer , Integer ], ... ]
*
* @param {Array} sortObjects List of sort objects.
* @return {Array} List of internal sort definitions.
*/
function convertSortList( sortObjects ) {
var sortList = [];
sortObjects.forEach( function ( sortObject ) {
$.each( sortObject, function ( columnIndex, order ) {
var orderIndex = ( order === 'desc' ) ? 1 : 0;
sortList.push( [ parseInt( columnIndex, 10 ), orderIndex ] );
} );
} );
return sortList;
}
/* Public scope */
$.ts_test = ts;
$.ts_test.getParser = function ( id ) {
return getParserById( id );
}
/**
* @param {jQuery} $tables
*/
function construct( $tables ) {
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;
}
//DIAGNOSIS: Read the settings from tablesorter
config = $.data( table, 'tablesorter').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() {
var $tfoot, $sortbottoms;
firstTime = false;
//only if jquery.tablesorter have not do this:
if ( !config.parsers.length ) {
// 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 ) );
}
}
explodeRowspans( $table );
manageColspans( $table );
}
// use NEW parsers from this file
// Try to auto detect column type, and store in tables config
config.parsers = buildParserCache( table, $headers );
// Remove tablesorters click event
$headers.off( 'click' );
// Apply my 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 = columns.map( function ( c ) {
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 );
}
}
// 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 )
);
// Stop normal event by returning false
return false;
}
// Cancel selection
//} ).mousedown( function () {
// if ( config.cancelSelection ) {
// this.onselectstart = function () {
// return false;
// };
// return false;
// }
} );
}; //setupForFirstSort
//DIAGNOSIS: extend 'mousedown' for sortable headers
$table.find( '.' + config.cssHeader ).on( 'mousedown', function (e) {
// Ctrl + right mousedown
if ( e.ctrlKey && e.button == 2 ) {
//
//if ( $( table ).data( 'tablesorter' ).config.parsers.length === 0 ) { //first click at this table
// $table.data( 'tablesorter' ).sort(); //run Tablesorter's setupForFirstSort()
//}
parsers = $.tablesorter.getParsers(); //old Parsers
dateSeparatorRegex = false; //do not remove elements with class 'reference'
// Grab and process locale settings.
//buildTransformTable();
//buildDateTable();
setupForFirstSort();
addTitleTags(table);
firstTime = true;
} else {
// right mousedown:
if ( e.button == 2 ) {
//set up 'config.parsers'
if ( firstTime ) {
firstTime = false;
parsers = newParsers; //from this file
newParsersActive = true;
// Grab and process locale settings.
//buildTransformTable();
//buildDateTable();
setupForFirstSort(); //->remove tablesorters sort, use this sort
addTitleTags(table);
}
//For this row show Parser.id and sort value from cache
var cell = this,
$cell = $( cell ),
j = config.headerToColumns[ $cell.data( 'headerIndex' ) ],
//j = $cell.data( 'headerIndex' ),
// Index of first column belonging to this header
//j = columns[0],
out = "", k,
//similar BuildParserCache() (here j)
parser = false,
sortType = $cell.data('sortType');
if ( sortType !== undefined ) {
out += "data-sort-type = \"" +sortType +"\"\n";
}
//after TH.'click' called setupForFirstSort() are parsers set
if ( config.parsers.length ) {
out += "Parser: " + config.parsers[ j[0] ].id;
for ( k = 1; k < j.length; k++ ) {
out += " | " + config.parsers[ j[k] ].id;
}
out += "\n\n";
cache = buildCache( table, config.parsers );
var totalRows = cache.normalized.length;
for ( i = 0; i < totalRows; i++ ) {
out += cache.normalized[ i ][ j[0] ];
for ( k = 1; k < j.length; k++ ) {
out += "\t" + cache.normalized[ i ][ j[k] ];
}
out += "\n"
}
} else {
out += 'setupForFirstSort() has config.parsers not set.';
}
alert(out);
}
}
}); //.on
});
} //end construct
function addParser( parser ) {
if ( !getParserById( parser.id ) ) {
newParsers.push( parser );
}
}
function formatDigit( s ) {
var out, c, p, i, match;
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;
}
s = s.replace( /,/g, '' ).replace( '\u2212', '-' );
if ( ( match = s.match( /([-+])?∞/ ) ) !== null ) {
return ( match[ 1 ] === '-' ) ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY;
}
i = parseFloat( s );
if ( isNaN( i ) ) {
if ( s.length === 0 ) {
return -Infinity; //ich: Number.MAX_VALUE;
} else {
//String to Number > 10000
i = 1e3;
for ( p = 0; p < s.length; p++ ) {
i = i * 10 + s.charCodeAt( p );
}
}
}
return i;
}
//ts is local
//ts = $.tablesorter;
// Add default parsers
addParser( {
id: 'text',
is: function () {
return true;
},
format: function ( s ) {
var tsc;
s = $.trim( s.toLowerCase() );
//s = s.replace( ts.rgx.numReferences , '' );
if ( ts.collationRegex ) {
tsc = ts.collationTable;
s = s.replace( ts.collationRegex, function ( match ) {
var r = tsc[ match ] ? tsc[ match ] : tsc[ match.toUpperCase() ];
return r.toLowerCase();
} );
}
return s;
},
type: 'text'
} );
addParser( {
id: 'IPAddress',
is: function ( s ) {
return ts.rgx.IPAddress[ 0 ].test( s );
},
format: function ( s ) {
var match, i, item,
r = '';
if ( ( match = s.match( ts.rgx.IPAddress[ 0 ] ) ) !== null ) {
if ( match[ 5 ] && match[ 5 ].length ) {
match[ 5 ] = match[ 5 ].substr(1); //remove '/' from CIDR
} else {
match[ 5 ] = '000';
}
for ( i = 1; i <= 5; i++ ) {
item = match[ i ];
while ( item.length < 3 ) {
item = '0' + item;
}
r = r + item;
}
return $.tablesorter.formatFloat( r );
} else {
return 0;
}
},
type: 'numeric'
} );
//Parser 'currency' is unnecessary, done with parser 'number'
/*addParser( {
id: 'currency',
is: function ( s ) {
return ts.rgx.currency[ 0 ].test( s );
},
format: function ( s ) {
return $.tablesorter.formatDigit( s.replace( ts.rgx.currency[ 1 ], '' ) );
},
type: 'numeric'
} );*/
addParser( {
id: 'url',
is: function ( s ) {
return ts.rgx.url[ 0 ].test( s );
},
format: function ( s ) {
return $.trim( s.replace( ts.rgx.url[ 1 ], '' ) );
},
type: 'text'
} );
/*addParser( {
id: 'templateDts', // Temporary parser until en:Template:Dts output isoDate
is: function ( s ) {
return ts.rgx.templateDts[ 0 ].test( s );
},
format: function ( s ) {
var match, i, isodate, ms, hOffset, mOffset;
match = s.match( ts.rgx.templateDts[ 0 ] );
if ( match ) {
if ( match[ 1 ].charAt( 0 ) === '-' ) {
match[ 1 ] = -( parseInt( match[ 1 ] ) + 1e12 );
}
ms = parseFloat( match[ 4 ] );
isodate = new Date( match[ 1 ], match[ 2 ] - 1, match[ 3 ], 0, 0, 0, ms);
//because javascript change year 70...99 to 1970...1999, we set it again
isodate.setFullYear( match[ 1 ] );
//shift local Time to UTC Time
isodate.setTime( isodate.getTime() + isodate.getTimezoneOffset() * 60000 );
return $.tablesorter.formatFloat( isodate.getTime() );
} else {
return 0;
}
},
type: 'numeric'
} );*/
addParser( {
id: 'isoDate',
is: function ( s ) {
return ts.rgx.isoDate[ 0 ].test( s );
},
format: function ( s ) {
var match, i, isodate, ms, hOffset, mOffset;
match = s.match( ts.rgx.isoDate[ 0 ] );
if ( match === null ) {
// Otherwise a signed number with 1-4 digit is parsed as isoDate
match = s.match( ts.rgx.isoDate[ 1 ] );
}
if ( !match ) {
return -Infinity;
}
// Month and day
for ( i = 2; i <= 4; i += 2 ) {
if ( !match[ i ] || match[ i ].length === 0 ) {
match[ i ] = 1;
}
}
// Time
for ( i = 6; i <= 15; i++ ) {
if ( !match[ i ] || match[ i ].length === 0 ) {
match[ i ] = '0';
}
}
ms = parseFloat( match[ 11 ].replace( /,/ , '.' ) ) * 1000;
hOffset = $.tablesorter.formatInt( match[ 13 ] + match[ 14 ] );
mOffset = $.tablesorter.formatInt( match[ 13 ] + match[ 15 ] );
isodate = new Date( 0 );
// Because Date constructor changes year 0-99 to 1900-1999, use setUTCFullYear()
isodate.setUTCFullYear( match[ 1 ], match[ 2 ] - 1, match[ 4 ] );
isodate.setUTCHours( match[ 6 ] - hOffset, match[ 8 ] - mOffset, match[ 10 ], ms );
return isodate.getTime();
},
type: 'numeric'
} );
addParser( {
id: 'usLongDate',
is: function ( s ) {
return ts.rgx.usLongDate[ 0 ].test( s );
},
format: function ( s ) {
var match = s.match( ts.rgx.usLongDate[ 0 ] );
return $.tablesorter.formatFloat( new Date( match[ 0 ] ).getTime() );
},
type: 'numeric'
} );
addParser( {
id: 'asianDate',
is: function ( s ) {
s = s.replace( ts.dateSeparatorRegex, ' ' ).toLowerCase();
return ts.rgx.asianDate[ 0 ].test( s );
},
format: function ( s ) {
var match, i, y, m, d;
s = s.replace( ts.dateSeparatorRegex, ' ' ).toLowerCase();
match = s.match( ts.rgx.asianDate[ 1 ] );
y = parseInt( match[ 2 ] || "0", 10 );
m = parseInt( match[ 3 ] || "0", 10 );
d = parseInt( match[ 4 ] || "0", 10 );
// Support for AD/BC
if ( match[ 1 ] ) {
y = -y;
}
return y * 1e4 + m * 1e2 + d;
},
type: 'numeric'
} );
addParser( {
id: 'date',
is: function ( s ) {
s = ' ' + s.replace( ts.dateSeparatorRegex, ' ' ).toLowerCase() + ' ';
return ( ts.dateRegex[ 0 ].test( s ) || ts.dateRegex[ 1 ].test( s ) || ts.dateRegex[ 2 ].test( s ) );
},
format: function ( s ) {
var match, ma2, i, bc, y,
guesstimateCenturys = false,
sl = s.toLowerCase();
s = ' ' + sl.replace( ts.dateSeparatorRegex , ' ' ) + ' ';
// Three parts of digits
if ( ( match = s.match( ts.dateRegex[ 0 ] ) ) !== null ) {
bc = match[ 4 ];
guesstimateCenturys = true;
if ( mw.config.get( 'wgDefaultDateFormat' ) === 'mdy' || mw.config.get( 'wgPageContentLanguage' ) === 'en' ) {
s = [ match[ 3 ], match[ 1 ], match[ 2 ] ];
} else if ( mw.config.get( 'wgDefaultDateFormat' ) === 'dmy' ) {
s = [ match[ 3 ], match[ 2 ], match[ 1 ] ];
} else if ( mw.config.get( 'wgDefaultDateFormat' ) === 'ymd' ) {
s = [ match[ 1 ], match[ 2 ], match[ 3 ] ];
} else {
// If we get here, we don't know which order the dd-dd-dddd
// date is in. So return something not entirely invalid.
return '99999999';
}
// Named month at second
} else if ( ( match = s.match( ts.dateRegex[ 1 ] ) ) !== null ) {
bc = match[ 4 ];
if ( mw.config.get( 'wgDefaultDateFormat' ) === 'ymd' ) { // ym ymd
s = [ match[ 1 ], String( ts.monthNames[ match[ 2 ] ] ), $.trim( match[ 3 ] ) || "0" ];
} else { // dm dmy
s = [ match[ 3 ] || "0", String( ts.monthNames[ match[ 2 ] ] ), match[ 1 ] ];
if ( match[ 3 ] ) {
guesstimateCenturys = true;
}
}
// Named month at first
} else if ( ( match = s.match( ts.dateRegex[ 2 ] ) ) !== null ) {
bc = match[ 4 ];
ma2 = $.trim( match[ 2 ] ) || "0";
if ( match[ 3 ] || ( parseInt( ma2, 10 ) < 32 && !bc ) ) {
// ma2 is a day: m md mdy
s = [ $.trim( match[ 3 ] ) || "0", String( ts.monthNames[ match[ 1 ] ] ), ma2 ];
if ( match[ 3 ] ) {
guesstimateCenturys = true;
}
} else {
// ma2 is a year: my
s = [ ma2, String( ts.monthNames[ match[ 1 ] ] ), "0" ];
}
// s[ 0 ] = s[ 0 ].replace( /'/g, "" ); for '10 -> 2010
// Digit month and year: my ym
} else if ( ( match = s.match( ts.dateRegex[ 3 ] ) ) !== null ) {
if ( mw.config.get( 'wgDefaultDateFormat' ) === 'ymd' ) {
s = [ match[ 1 ], match[ 2 ], "0" ];
} else {
s = [ match[ 2 ], match[ 1 ], "0" ];
}
// Only year: y
} else if ( ( match = sl.match( ts.dateRegex[ 4 ] ) ) !== null ) { //sl contain dateSeparators
y = match[ 2 ].replace( '\u2212', '-' ); // year possibly with sign
match = s.match( ts.dateRegex[ 4 ] ); // replaced dateSeparators
bc = match[ 1 ] || match[ 3 ];
s = [ y, "0" , "0" ];
//guesstimateCenturys = false; // use years < 100 direct
} else {
// Should never get here
return '99999999';
}
// Pad month and day
if ( s[ 1 ].length === 1 ) {
s[ 1 ] = '0' + s[ 1 ];
}
if ( s[ 2 ].length === 1 ) {
s[ 2 ] = '0' + s[ 2 ];
}
y = parseInt( s[ 0 ], 10 );
if ( bc ) {
// Support for AD/BC
y = -y;
} else if ( y < 100 && guesstimateCenturys ) {
// Guesstimate years without centuries
if ( y < 30 ) {
y = 2000 + y;
} else {
y = 1900 + y;
}
}
return y * 1e4 + parseInt( s[ 1 ] + s[ 2 ], 10 );
},
type: 'numeric'
} );
addParser( {
id: 'time',
is: function ( s ) {
return ts.rgx.time[ 0 ].test( s );
},
format: function ( s ) {
var match, date, h, sec, ms;
if ( ( match = s.match( ts.rgx.time[ 0 ] ) ) !== null ) {
sec = match[ 3 ] ? match[ 3 ].substr(1) : 0;
ms = match[ 4 ] ? parseFloat( match[ 4 ].replace( /[.,]/ , '0.' )) * 1000 : 0;
h = match[ 5 ] ? parseInt( match[ 1 ] ) + 12 : match[ 1 ]; //+12 hours for pm
date = new Date( 0 );
date.setHours( h, match[ 2 ], sec, ms);
return $.tablesorter.formatFloat( date.getTime() );
} else {
return 0;
}
},
type: 'numeric'
} );
addParser( {
id: 'number',
is: function ( s ) {
if ( ts.notNumberRegex.test( s ) ) {
return false;
}
s = s.replace( ts.SpacesInNumber , '' );
return ts.numberRegex.test( s );
},
format: function ( s ) {
var match;
s = s.replace( ts.SpacesInNumber , '' ).replace( ts.scientificRegex , 'e');
if ( ( match = s.match( ts.numberRegex ) ) !== null ) {
return formatDigit( match[ 1 ] ); //$.tablesorter.formatDigit( match[ 1 ] );
} else { //call parser 'text' format() and use only the first 10 characters to prevent long loops.
return formatDigit( parsers[ 0 ].format( s ).substr( 0, 10 ) ); //$.tablesorter.formatDigit( s )
}
},
type: 'numeric'
} );
}( jQuery, mediaWiki ) );