tablesorter/js/widgets/widget-sortTbodies.js

229 lines
7.9 KiB
JavaScript

/*! tablesorter tbody sorting widget (BETA) - 6/10/2015 (core v2.22.2)
* Requires tablesorter v2.22.2+ and jQuery 1.4+
* by Rob Garrison
*/
/*jshint browser:true, jquery:true, unused:false */
/*global jQuery: false */
;( function( $ ) {
'use strict';
var ts = $.tablesorter;
ts.sortTbodies = {
init: function( c, wo ) {
var index, rows, txt, max, $rows,
namespace = c.namespace + 'sortTbody',
$tbodies = c.$table.children( 'tbody' ),
len = $tbodies.length;
// save serverSideSorting value; use to toggle internal row sorting
wo.sortTbody_original_serverSideSorting = c.serverSideSorting;
// include info-only tbodies - we need parsed data from *all* tbodies
wo.sortTbody_original_cssInfoBlock = c.cssInfoBlock;
c.cssInfoBlock = wo.sortTbody_noSort;
ts.sortTbodies.setTbodies( c, wo );
// add original order index for stable sort
for ( index = 0; index < len; index++ ) {
$tbodies.eq( index ).attr( 'data-ts-original-order', index );
}
c.$table
.unbind( 'sortBegin updateComplete '.split( ' ' ).join( namespace + ' ' ) )
.bind( 'sortBegin' + namespace, function() {
ts.sortTbodies.sorter( c );
})
.bind( 'updateComplete' + namespace, function() {
// find parsers for each column
ts.sortTbodies.setTbodies( c, wo );
ts.updateCache( c, null, c.$tbodies );
});
// detect parsers - in case the table contains only info-only tbodies
if ( $.isEmptyObject( c.parsers ) || c.$tbodies.length !== $tbodies.length ) {
ts.sortTbodies.setTbodies( c, wo );
ts.updateCache( c, null, c.$tbodies );
}
// find colMax; this only matter for numeric columns
$rows = $tbodies.children( 'tr' );
len = $rows.length;
for ( index = 0; index < c.columns; index++ ) {
max = 0;
if ( c.parsers[ index ].type === 'numeric' ) {
for ( rows = 0; rows < len; rows++ ) {
// update column max value (ignore sign)
txt = ts.getParsedText( c, $rows.eq( rows ).children()[ index ], index );
max = Math.max( Math.abs( txt ) || 0, max );
}
}
c.$headerIndexed[ index ].attr( 'data-ts-col-max-value', max );
}
},
// make sure c.$tbodies is up-to-date (init & after updates)
setTbodies: function( c, wo ) {
c.$tbodies = c.$table.children( 'tbody' ).not( '.' + wo.sortTbody_noSort );
},
sorter: function( c ) {
var $table = c.$table,
wo = c.widgetOptions;
// prevent multiple calls while processing
if ( wo.sortTbody_busy !== true ) {
wo.sortTbody_busy = true;
var $tbodies = $table.children( 'tbody' ).not( '.' + wo.sortTbody_noSort ),
primary = wo.sortTbody_primaryRow || 'tr:eq(0)',
sortList = c.sortList || [],
len = sortList.length;
if ( len ) {
// toggle internal row sorting
c.serverSideSorting = !wo.sortTbody_sortRows;
$tbodies.sort( function( a, b ) {
var sortListIndex, txt, dir, num, colMax, sort, col, order, colA, colB, x, y,
table = c.table,
parsers = c.parsers,
cts = c.textSorter || '',
$tbodyA = $( a ),
$tbodyB = $( b ),
$a = $tbodyA.find( primary ).children( 'td, th' ),
$b = $tbodyB.find( primary ).children( 'td, th' );
for ( sortListIndex = 0; sortListIndex < len; sortListIndex++ ) {
col = sortList[ sortListIndex ][0];
order = sortList[ sortListIndex ][1];
// sort direction, true = asc, false = desc
dir = order === 0;
// column txt - tbody A
txt = ts.getElementText( c, $a.eq( col ), col );
colA = parsers[ col ].format( txt, table, $a[ col ], col );
// column txt - tbody B
txt = ts.getElementText( c, $b.eq( col ), col );
colB = parsers[ col ].format( txt, table, $b[ col ], col );
if (c.sortStable && colA === colB && len === 1) {
return $tbodyA.attr( 'data-ts-original-order' ) - $tbodyB.attr( 'data-ts-original-order' );
}
// fallback to natural sort since it is more robust
num = /n/i.test( parsers && parsers[ col ] ? parsers[ col ].type || '' : '' );
if ( num && c.strings[ col ] ) {
colMax = c.$headerIndexed[ col ].attr( 'data-ts-col-max-value' ) ||
1.79E+308; // close to Number.MAX_VALUE
// sort strings in numerical columns
if ( typeof ( ts.string[ c.strings[ col ] ] ) === 'boolean' ) {
num = ( dir ? 1 : -1 ) * ( ts.string[ c.strings[ col ] ] ? -1 : 1 );
} else {
num = ( c.strings[ col ] ) ? ts.string[ c.strings[ col ] ] || 0 : 0;
}
// fall back to built-in numeric sort
// var sort = $.tablesorter['sort' + s](a, b, dir, colMax, table);
sort = c.numberSorter ? c.numberSorter( colA, colB, dir, colMax, table ) :
ts[ 'sortNumeric' + ( dir ? 'Asc' : 'Desc' ) ]( colA, colB, num, colMax, col, table );
} else {
// set a & b depending on sort direction
x = dir ? colA : colB;
y = dir ? colB : colA;
// text sort function
if ( typeof ( cts ) === 'function' ) {
// custom OVERALL text sorter
sort = cts( x, y, dir, col, table );
} else if ( typeof ( cts ) === 'object' && cts.hasOwnProperty( col ) ) {
// custom text sorter for a SPECIFIC COLUMN
sort = cts[ col ]( x, y, dir, col, table );
} else {
// fall back to natural sort
sort = ts[ 'sortNatural' + ( dir ? 'Asc' : 'Desc' ) ]( colA, colB, col, c );
}
}
if ( sort ) { return sort; }
}
return $tbodyA.attr( 'data-ts-original-order' ) - $tbodyB.attr( 'data-ts-original-order' );
});
ts.sortTbodies.restoreTbodies( c, wo, $tbodies );
wo.sortTbody_busy = false;
}
}
},
restoreTbodies : function ( c, wo, $sortedTbodies ) {
var $nosort, $tbodies, $thisTbody, tbLen, nsLen, index, targetIndex,
$table = c.$table,
hasShuffled = true,
indx = 0;
// hide entire table to improve sort performance
$table.hide();
$sortedTbodies.appendTo( $table );
// reposition no-sort tbodies
$tbodies = $table.children( 'tbody' );
tbLen = $tbodies.length;
$nosort = $tbodies.filter( '.' + wo.sortTbody_noSort ).appendTo( $table );
nsLen = $nosort.length;
if ( nsLen ) {
// don't allow the while loop to cycle more times than the set number of no-sort tbodies
while ( hasShuffled && indx < nsLen ) {
hasShuffled = false;
for ( index = 0; index < nsLen; index++ ) {
targetIndex = parseInt( $nosort.eq( index ).attr( 'data-ts-original-order' ), 10 );
// if target index > number of tbodies, make it last
targetIndex = targetIndex >= tbLen ? tbLen : targetIndex < 0 ? 0 : targetIndex;
if ( targetIndex !== $nosort.eq( index ).index() ) {
hasShuffled = true;
$thisTbody = $nosort.eq( index ).detach();
if ( targetIndex >= tbLen ) {
// Are we trying to be the last tbody?
$thisTbody.appendTo( $table );
} else if ( targetIndex === 0 ) {
// Are we trying to be the first tbody?
$thisTbody.prependTo( $table );
} else {
// No, we want to be somewhere in the middle!
$thisTbody.insertBefore( $table.children( 'tbody:eq(' + targetIndex + ')' ) );
}
}
}
indx++;
}
}
$table.show();
}
};
ts.addWidget({
id: 'sortTbody',
// priority < 50 (filter widget), so c.$tbodies has the correct elements
priority: 40,
options: {
// point to a row within the tbody that matches the number of header columns
sortTbody_primaryRow : null,
// sort tbody internal rows
sortTbody_sortRows : false,
// static tbodies (like static rows)
sortTbody_noSort : 'tablesorter-no-sort-tbody'
},
init : function( table, thisWidget, c, wo ) {
ts.sortTbodies.init( c, wo );
},
remove : function( table, c, wo, refreshing ) {
c.$table.unbind( 'sortBegin updateComplete '.split( ' ' ).join( c.namespace + 'sortTbody ' ) );
c.serverSideSorting = wo.sortTbody_original_serverSideSorting;
c.cssInfoBlock = wo.sortTbody_original_cssInfoBlock;
}
});
})( jQuery );