Core & Filter: Add duplicateSpan option

Core:
- Added `duplicateSpan` option (default is `true`).
- Renamed `$.tablesorter.formatSortingOrder` to `$.tablesorter.getOrder`.
- Include `table` in console.error if an issue is encountered during
  initialization.
- Clean up warning when no parser is found for given data.
- Fix `config.sortVars` js error for non-existent header cells.
- Added unit tests.
- Added "example-colspan.html" demo.

Filter:
- Filters that span multiple columns now have the correct data-column set.
- Consolidated code that parsed data-column ranges into `findRange` function.
- Added unit tests
This commit is contained in:
Rob Garrison 2015-12-09 12:34:48 -06:00
parent 36a8b5a28e
commit 60282f0787
7 changed files with 363 additions and 38 deletions

183
docs/example-colspan.html Normal file
View File

@ -0,0 +1,183 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>jQuery plugin: Tablesorter 2.0 - Sorting &amp; Filtering with Colspans</title>
<!-- jQuery -->
<script src="js/jquery-latest.min.js"></script>
<!-- Demo stuff -->
<link rel="stylesheet" href="css/jq.css">
<link href="css/prettify.css" rel="stylesheet">
<script src="js/prettify.js"></script>
<script src="js/docs.js"></script>
<!-- Tablesorter: required -->
<link rel="stylesheet" href="../css/theme.blue.css">
<style id="css">.tablesorter-blue td[colspan] { color: red; } /* for demo purposes */</style>
<script src="../js/jquery.tablesorter.js"></script>
<script src="../js/widgets/widget-filter.js"></script>
<script>
$(function(){
var dupe = true;
$( '#dupe' ).click( function() {
dupe = !dupe;
$( this ).text( '' + dupe );
$( 'table' )[0].config.duplicateSpan = dupe;
$( 'table' ).trigger( 'update' );
});
$('table').on('filterEnd', function( event, c ) {
$( '#show-filter' ).html( '[ "' + c.lastSearch.join('", "') + '" ]' );
});
$('.search').click(function(){
var $this = $(this),
filter = [],
col = $this.attr( 'data-column' );
if ( col === 'all' ) {
col = $('table')[0].config.columns;
}
filter[ col ] = $this.text();
$.tablesorter.setFilters( $('table'), filter );
});
});
</script>
<script id="js">$( function() {
$( 'table' ).tablesorter({
theme : 'blue',
duplicateSpan : true, // default setting
widthFixed: true,
widgets : [ 'zebra', 'filter' ],
widgetOptions : {
filter_external: 'input.search',
filter_reset: '.reset'
}
});
$('.sort').click(function() {
// it is still possible to use 'a', 'd', 'n', 's' or 'o' on the second column
// see http://mottie.github.io/tablesorter/docs/#sorton
$('table').trigger('sorton', [ [[ $(this).text(), 'n' ]] ]);
});
});</script>
</head>
<body>
<div id="banner">
<h1>table<em>sorter</em></h1>
<h2>Sorting &amp; Filtering with Colspans</h2>
<h3>Flexible client-side table sorting</h3>
<a href="index.html">Back to documentation</a>
</div>
<div id="main">
<p class="tip">
<em>NOTE!</em>
<ul>
<li>Having a <code>colspan</code> in the tbody is not fully supported by all widgets, and there are still some minor issues to work out.</li>
<li><span class="label alert">Alert</span> Cells with a <code>rowspan</code> are <em>not</em> currently supported.</li>
<li><span class="label warning">Warning</span> Cells with a <code>colspan</code> will attempt to use the parser set for that column and <em>will not</em> use the parser for another column while sorting or filtering - try this <button class="search" data-column="3" type="button">&gt;10</button> - the "17 Koala" cell is not parsed as a numeric value and is thus considered a string.</li>
<li>The <code>duplicateSpan</code> option (storing of cache data) is a preliminary step in providing <code>colspan</code> support, it is by no means complete.</li>
<li>This demo requires tablesorter v2.24.7+, as well the corresponding version of the filter widget.<br><br></li>
<li>Follow the demo steps to hopefully get the full understanding of how to use <code>colspan</code>s in the tbody.</li>
<li>Ultimately, with the issues noted below, I would not recommend including an entire column in the tbody that does not have a corresponding header cell - <em>don't do what I did in this demo for the first two colums</em>.</li>
</ul>
<p>
<h1>Demo</h1>
<div id="demo"><ul>
<li>Sort Column <button class="sort" type="button">0</button> <button class="sort" type="button">1</button>
(toggle sort direction) - There is no method to use the UI to sort the second column because it has no header; use "sorton" instead.
</li>
<li>Search:
<button class="search" type="button" data-column="2">zyx</button>
<button class="search" type="button" data-column="3">7</button>
<button class="search" type="button" data-column="4">Koala</button>
<button class="search" type="button" data-column="5">edu</button>,
then toggle <code>duplicateSpan</code> : <button id="dupe" type="button">true</button>.
</li>
<li>Searching the first two columns <sup class="results xsmall">&dagger;</sup>:
<ul>
<li>Search using column <code>0</code> (zero):<br>
<button class="search" type="button" data-column="0">4</button> (nothing visible in column filter)<br>
<button class="search" type="button" data-column="1">&gt;4</button> (search second column, nothing visible in filter)
</li>
<li>Search using column <code>6</code> (used by "all" filter):<br>
<button class="search" type="button" data-column="6">4</button> (search both index columns)<sup class="results xsmall">&Dagger;</sup><br>
<button class="search" type="button" data-column="6">1:4</button> (only search "Group" column)<br>
<button class="search" type="button" data-column="6">2:&gt;4</button> (search second column)
</li>
</ul>
</li>
</ul>
Search:
<input type="search" class="search" data-column="all" placeholder="Search all columns"><sup class="results xsmall">&Dagger;</sup>
<button class="reset">Reset</button>
<code id="show-filter"></code>
<p class="xsmall"><span class="results">&dagger;</span> The reason for this issue is that the filter input in the index column has this setting:
<code>data-column="0-1"</code>, and it has not yet been worked out how to properly target that input.<br>
<span class="results">&Dagger;</span> It is still being investigated as to why the search using the button targeting column 6 and the "all" input have different results (Enter "4" in the input and 4 rows will appear in the result, then click on the "4" to search both index columns - one less row).
</p>
<table class="tablesorter">
<thead>
<tr>
<th rowspan="2" colspan="2">Index (colspan 2)</th>
<th colspan="4">Products</th>
</tr>
<tr>
<th>Product ID</th>
<th>Numeric</th>
<th>Animals</th>
<th >Url</th>
</tr>
</thead>
<tfoot>
<tr>
<th colspan="2">Index</th>
<th>Product ID</th>
<th>Numeric</th>
<th id="test">Animals</th>
<th >Url</th>
</tr>
</tfoot>
<tbody>
<tr><td>Group 1</td><td style="width:100px">6</td><td>abc 9</td><td>155</td><td>Lion</td><td>http://www.nytimes.com/</td></tr>
<tr><td>Group 4</td><td>1</td><td>abc 1</td><td>237</td><td colspan="2">Ox http://www.yahoo.com</td></tr>
<tr><td>Group 1</td><td>2</td><td colspan="4">zyx 1 957 Koala http://www.mit.edu/</td></tr>
<tr><td>Group 0</td><td>5</td><td>abc 2</td><td>56</td><td>Elephant</td><td>http://www.wikipedia.org/</td></tr>
<tr><td>Group 3</td><td>0</td><td>abc 123</td><td colspan="2">17 Koala</td><td>http://www.google.com</td></tr>
<tr><td>Group 2</td><td>8</td><td>zyx 9</td><td>10</td><td>Girafee</td><td>http://www.facebook.com</td></tr>
<tr><td>Group 1</td><td>3</td><td colspan="2">zyx 4 767</td><td>Bison</td><td>http://www.whitehouse.gov/</td></tr>
<tr><td>Group 2</td><td>4</td><td>abc 11</td><td>3</td><td>Chimp</td><td>http://www.ucla.edu/</td></tr>
<tr><td>Group 4</td><td>7</td><td colspan="2">ABC 10 87</td><td>Zebra</td><td>http://www.google.com</td></tr>
<tr><td>Group 3</td><td>9</td><td>zyx 12</td><td>0</td><td>Koala</td><td>http://www.nasa.gov/</td></tr>
</tbody>
</table></div>
<h1>Javascript</h1>
<div id="javascript">
<pre class="prettyprint lang-javascript"></pre>
</div>
<h1>CSS</h1>
<div id="css">
<pre class="prettyprint lang-css"></pre>
</div>
<h1>HTML</h1>
<div id="html">
<pre class="prettyprint lang-html"></pre>
</div>
</div>
</body>
</html>

View File

@ -340,11 +340,12 @@
<li><a href="example-locale-sort.html">Sorting Accented Characters</a> (<a href="#sortlocalecompare"><code>sortLocaleCompare</code></a>; v2.24; <a href="https://github.com/Mottie/tablesorter/wiki/Language">languages</a>).</li>
<li><a href="example-trigger-sort.html">Sort table using a link outside the table</a> (external link; <span class="updated version">v2.17.0</span>).</li>
<li><a href="example-child-rows.html">Attach child rows (rows that sort with their parent row)</a> (<span class="updated version">v2.15.12</span>).</li>
<li><a href="example-child-rows-filtered.html">Use child rows + filter widget</a> (<span class="updated version">v2.22.0</span>)</li>
<li><a href="example-child-rows-filtered.html">Use child rows + filter widget</a> (<span class="updated version">v2.22.0</span>).</li>
<li><a href="example-multiple-tbodies.html">Sorting with Multiple Tbodies</a> (v2.2).</li>
<li><a href="example-header-column-span.html">Sorting Across Multiple Columns</a> (v2.3).</li>
<li><a href="example-option-show-processing.html">Show a processing icon during sorting/filtering</a> (v2.4).</li>
<li><a href="example-option-delay-init.html">Delay table initialization</a> (<a href="#delayinit"><code>delayInit</code></a>).</li>
<li><a href="example-colspan.html">Sort &amp; filter with colspans</a> (<a href="#duplicatecolspan"><code>duplicateSpan</code></a>; <span class="version">v2.24.7</span>).</li>
</ul>
</li>
</ul>
@ -462,9 +463,9 @@
<li><a href="example-widget-filter.html">basic</a> (v2.0.18; <span class="version updated">v2.23.5</span>).</li>
<li><a href="example-widget-filter-any-match.html">external option (match any column)</a> (<span class="version">v2.13.3</span>; <span class="version updated">v2.22.0</span>).</li>
<li><a href="example-widget-filter-external-inputs.html">external inputs</a> (<span class="version">v2.14</span>; <span class="version updated">v2.18.0</span>).</li>
<li><a href="example-widget-filter-custom.html">custom</a> (v2.3.6; <span class="version updated">v2.22.0</span>).</li>
<li><a href="example-widget-filter-custom-search.html">custom searches</a> (<span class="version">v2.17.5</span>; <span class="version updated">v2.22.0</span>).</li>
<li><a href="example-widget-filter-custom-search2.html">custom search (example #2)</a> (<span class="version">v2.19.1</span>; <span class="version updated">v2.24.6</span>).</li>
<li><a href="example-widget-filter-custom.html">custom filter functions</a> (v2.3.6; <span class="version updated">v2.22.0</span>).</li>
<li><a href="example-widget-filter-custom-search.html">custom search types</a> (<span class="version">v2.17.5</span>; <span class="version updated">v2.22.0</span>).</li>
<li><a href="example-widget-filter-custom-search2.html">custom search type (example #2: date range)</a> (<span class="version">v2.19.1</span>; <span class="version updated">v2.24.6</span>).</li>
<li><a href="example-widget-filter-childrows.html">child rows</a> (<span class="version">v2.23.4</span>).</li>
<li>formatter: <a href="example-widget-filter-formatter-1.html">jQuery UI widgets</a> and <a href="example-widget-filter-formatter-2.html">HTML5 Elements</a> (v2.7.7; <span class="version updated">v2.17.5</span>).</li>
<li>formatter: <a href="example-widget-filter-formatter-select2.html">select2</a> (<span class="version">v2.16.0</span>; <span class="version updated">v2.21.3</span>).</li>
@ -583,6 +584,26 @@
<td></td>
</tr>
<tr id="duplicatespan">
<td><a href="#" class="permalink">duplicateSpan</a></td>
<td>Boolean</td>
<td>true</td>
<td>Any <code>colspan</code> cells in the tbody may have its content duplicated in the cache for each spanned column (<span class="version">v2.24.7</span>).
<div class="collapsible">
<p>If <code>true</code>, the cache will contain duplicated cell contents for every column the <code>colspan</code> includes. This makes it easier to sort &amp; filter columns because a cell spanning all columns will only work with one parser. If <code>false</code>, the contents of cells that are spanned will be set to an empty string.</p>
<pre class="prettyprint lang-js">// this row: &lt;tr&gt;&lt;td colspan="3"&gt;foo&lt;/td&gt;&lt;td&gt;bar&lt;/td&gt;&lt;/tr&gt; results in this row cache:
[ 'foo', 'foo', 'foo', 'bar' ] // if duplicateSpan = true
[ 'foo', '', '', 'bar' ] // if duplicateSpan = false</pre>
<span class="label warning">*NOTE*</span>
<ul>
<li>Cells in the tbody with a <code>rowspan</code> are <em>not yet supported</em> and will not sort or filter as you would expect.</li>
<li>Having these values in the cache does not automatically guarantee that all widgets will work as expected with <code>colspan</code>s in the tbody (e.g. scroller widget with fixed columns)</li>
</ul>
</div>
</td>
<td><a href="example-widget-filter.html">Example</a></td>
</tr>
<tr id="cssasc">
<td><a href="#" class="permalink">cssAsc</a></td>
<td>String</td>

View File

@ -62,6 +62,7 @@
emptyTo : 'bottom', // sort empty cell to bottom, top, none, zero, emptyMax, emptyMin
stringTo : 'max', // sort strings in numerical column as max, min, top, bottom, zero
duplicateSpan : true, // colspan cells in the tbody will have duplicated content in the cache for each spanned column
textExtraction : 'basic', // text extraction method/function - function( node, table, cellIndex ){}
textAttribute : 'data-text',// data-attribute that contains alternate cell text (used in default textExtraction function)
textSorter : null, // choose overall or specific column sorter function( a, b, direction, table, columnIndex ) [alt: ts.sortText]
@ -209,7 +210,7 @@
if ( table.hasInitialized ) {
console.warn( 'Stopping initialization. Tablesorter has already been initialized' );
} else {
console.error( 'Stopping initialization! No table, thead or tbody' );
console.error( 'Stopping initialization! No table, thead or tbody', table );
}
}
return;
@ -561,7 +562,7 @@
// this may get updated numerous times if there are multiple rows
c.sortVars[ column ] = {
count : -1, // set to -1 because clicking on the header automatically adds one
order: ts.formatSortingOrder( tmp ) ?
order: ts.getOrder( tmp ) ?
[ 1, 0, 2 ] : // desc, asc, unsorted
[ 0, 1, 2 ], // asc, desc, unsorted
lockedOrder : false
@ -569,7 +570,7 @@
tmp = ts.getData( $elem, configHeaders, 'lockedOrder' ) || false;
if ( typeof tmp !== 'undefined' && tmp !== false ) {
c.sortVars[ column ].lockedOrder = true;
c.sortVars[ column ].order = ts.formatSortingOrder( tmp ) ? [ 1, 1, 1 ] : [ 0, 0, 0 ];
c.sortVars[ column ].order = ts.getOrder( tmp ) ? [ 1, 1, 1 ] : [ 0, 0, 0 ];
}
// add cell to headerList
c.headerList[ index ] = elem;
@ -692,6 +693,12 @@
if ( span > 0 ) {
colIndex += span;
max += span;
while ( span + 1 > 0 ) {
// set colspan columns to use the same parsers & extractors
list.parsers[ colIndex - span ] = parser;
list.extractors[ colIndex - span ] = extractor;
span--;
}
}
}
colIndex++;
@ -834,7 +841,7 @@
buildCache : function( c, callback, $tbodies ) {
var cache, val, txt, rowIndex, colIndex, tbodyIndex, $tbody, $row,
cols, $cells, cell, cacheTime, totalRows, rowData, prevRowData,
colMax, span, cacheIndex, max, len,
colMax, span, cacheIndex, hasParser, max, len, index,
table = c.table,
parsers = c.parsers;
// update tbody variable
@ -909,22 +916,31 @@
max = c.columns;
for ( colIndex = 0; colIndex < max; ++colIndex ) {
cell = $row[ 0 ].cells[ colIndex ];
if ( typeof parsers[ cacheIndex ] === 'undefined' ) {
if ( c.debug ) {
console.warn( 'No parser found for column ' + colIndex + '; cell:', cell, 'does it have a header?' );
if ( cell && cacheIndex < c.columns ) {
hasParser = typeof parsers[ cacheIndex ] !== 'undefined';
if ( !hasParser && c.debug ) {
console.warn( 'No parser found for row: ' + rowIndex + ', column: ' + colIndex +
'; cell containing: "' + $(cell).text() + '"; does it have a header?' );
}
} else if ( cell ) {
val = ts.getElementText( c, cell, cacheIndex );
rowData.raw[ cacheIndex ] = val; // save original row text
// save raw column text even if there is no parser set
txt = ts.getParsedText( c, cell, cacheIndex, val );
cols[ cacheIndex ] = txt;
if ( ( parsers[ cacheIndex ].type || '' ).toLowerCase() === 'numeric' ) {
if ( hasParser && ( parsers[ cacheIndex ].type || '' ).toLowerCase() === 'numeric' ) {
// determine column max value (ignore sign)
colMax[ cacheIndex ] = Math.max( Math.abs( txt ) || 0, colMax[ cacheIndex ] || 0 );
}
// allow colSpan in tbody
span = cell.colSpan - 1;
if ( span > 0 ) {
index = 0;
while ( index <= span ) {
// duplicate text (or not) to spanned columns
rowData.raw[ cacheIndex + index ] = c.duplicateSpan || index === 0 ? val : '';
cols[ cacheIndex + index ] = c.duplicateSpan || index === 0 ? val : '';
index++;
}
cacheIndex += span;
max += span;
}
@ -953,7 +969,7 @@
if ( !val[ 'row: ' + cacheIndex ] ) {
val[ 'row: ' + cacheIndex ] = {};
}
val[ 'row: ' + cacheIndex ][ c.headerContent[ colIndex ] ] =
val[ 'row: ' + cacheIndex ][ c.$headerIndexed[ colIndex ].text() ] =
c.cache[ 0 ].normalized[ cacheIndex ][ colIndex ];
}
}
@ -1054,7 +1070,7 @@
col = parseInt( $el.attr( 'data-column' ), 10 ),
end = col + c.$headers[ i ].colSpan;
for ( ; col < end; col++ ) {
include = include ? ts.isValueInArray( col, c.sortList ) > -1 : false;
include = include ? include || ts.isValueInArray( col, c.sortList ) > -1 : false;
}
return include;
});
@ -1159,6 +1175,14 @@
col = parseInt( val[ 0 ], 10 );
// prevents error if sorton array is wrong
if ( col < c.columns ) {
// set order if not already defined - due to colspan header without associated header cell
// adding this check prevents a javascript error
if ( !c.sortVars[ col ].order ) {
order = c.sortVars[ col ].order = ts.getOrder( c.sortInitialOrder ) ? [ 1, 0, 2 ] : [ 0, 1, 2 ];
c.sortVars[ col ].count = 0;
}
order = c.sortVars[ col ].order;
dir = ( '' + val[ 1 ] ).match( /^(1|d|s|o|n)/ );
dir = dir ? dir[ 0 ] : '';
@ -1437,6 +1461,7 @@
ts.initSort( c, cell, event );
}, 50 );
}
var arry, indx, headerIndx, dir, temp, tmp, $header,
notMultiSort = !event[ c.sortMultiSortKey ],
table = c.table,
@ -1708,7 +1733,7 @@
return ( parsers && parsers[ column ] ) ? parsers[ column ].type || '' : '';
},
formatSortingOrder : function( val ) {
getOrder : function( val ) {
// look for 'd' in 'desc' order; return true
return ( /^d/i.test( val ) || val === 1 );
},

View File

@ -208,7 +208,7 @@
table = c.table,
parsed = data.parsed[ data.index ],
query = ts.formatFloat( data.iFilter.replace( tsfRegex.operators, '' ), table ),
parser = c.parsers[ data.index ],
parser = c.parsers[ data.index ] || {},
savedSearch = query;
// parse filter value in case we're comparing numbers ( dates )
if ( parsed || parser.type === 'numeric' ) {
@ -651,11 +651,12 @@
buildFilter = '<tr role="row" class="' + tscss.filterRow + ' ' + c.cssIgnoreRow + '">';
for ( column = 0; column < columns; column++ ) {
if ( c.$headerIndexed[ column ].length ) {
buildFilter += '<td data-column="' + column + '"';
// account for entire column set with colspan. See #1047
tmp = c.$headerIndexed[ column ] && c.$headerIndexed[ column ][0].colSpan || 0;
if ( tmp > 1 ) {
buildFilter += ' colspan="' + tmp + '"';
buildFilter += '<td data-column="' + column + '-' + ( column + tmp - 1 ) + '" colspan="' + tmp + '"';
} else {
buildFilter += '<td data-column="' + column + '"';
}
if ( arry ) {
buildFilter += ( cellFilter[ column ] ? ' class="' + cellFilter[ column ] + '"' : '' );
@ -674,7 +675,8 @@
// assuming last cell of a column is the main column
$header = c.$headerIndexed[ column ];
if ( $header && $header.length ) {
$filter = c.$filters.filter( '[data-column="' + column + '"]' );
// $filter = c.$filters.filter( '[data-column="' + column + '"]' );
$filter = tsf.getColumnElm( c, c.$filters, column );
ffxn = ts.getColumnData( table, wo.filter_functions, column );
makeSelect = ( wo.filter_functions && ffxn && typeof ffxn !== 'function' ) ||
$header.hasClass( 'filter-select' );
@ -714,7 +716,8 @@
name = ( $.isArray( wo.filter_cssFilter ) ?
( typeof wo.filter_cssFilter[column] !== 'undefined' ? wo.filter_cssFilter[column] || '' : '' ) :
wo.filter_cssFilter ) || '';
buildFilter.addClass( tscss.filter + ' ' + name ).attr( 'data-column', column );
// copy data-column from table cell (it will include colspan)
buildFilter.addClass( tscss.filter + ' ' + name ).attr( 'data-column', $filter.attr( 'data-column' ) );
if ( disabled ) {
buildFilter.attr( 'placeholder', '' ).addClass( tscss.filterDisabled )[0].disabled = true;
}
@ -923,22 +926,18 @@
}
return $input || $();
},
multipleColumns: function( c, $input ) {
findRange: function( c, val, ignoreRanges ) {
// look for multiple columns '1-3,4-6,8' in data-column
var temp, ranges, range, start, end, singles, i, indx, len,
wo = c.widgetOptions,
// only target 'all' column inputs on initialization
// & don't target 'all' column inputs if they don't exist
targets = wo.filter_initialized || !$input.filter( wo.filter_anyColumnSelector ).length,
columns = [],
val = $.trim( tsf.getLatestSearch( $input ).attr( 'data-column' ) || '' );
columns = [];
if ( /^[0-9]+$/.test( val ) ) {
return parseInt( val, 10 );
// always return an array
return [ parseInt( val, 10 ) ];
}
// process column range
if ( targets && /-/.test( val ) ) {
if ( !ignoreRanges && /-/.test( val ) ) {
ranges = val.match( /(\d+)\s*-\s*(\d+)/g );
len = ranges.length;
len = ranges ? ranges.length : 0;
for ( indx = 0; indx < len; indx++ ) {
range = ranges[indx].split( /\s*-\s*/ );
start = parseInt( range[0], 10 ) || 0;
@ -957,7 +956,7 @@
}
}
// process single columns
if ( targets && /,/.test( val ) ) {
if ( !ignoreRanges && /,/.test( val ) ) {
singles = val.split( /\s*,\s*/ );
len = singles.length;
for ( i = 0; i < len; i++ ) {
@ -977,6 +976,23 @@
}
return columns;
},
getColumnElm: function( c, $elements, column ) {
// data-column may contain multiple columns '1-3,5-6,8'
// replaces: c.$filters.filter( '[data-column="' + column + '"]' );
return $elements.filter( function() {
var cols = tsf.findRange( c, $( this ).attr( 'data-column' ) );
return $.inArray( column, cols ) > -1;
});
},
multipleColumns: function( c, $input ) {
// look for multiple columns '1-3,4-6,8' in data-column
var wo = c.widgetOptions,
// only target 'all' column inputs on initialization
// & don't target 'all' column inputs if they don't exist
targets = wo.filter_initialized || !$input.filter( wo.filter_anyColumnSelector ).length,
val = $.trim( tsf.getLatestSearch( $input ).attr( 'data-column' ) || '' );
return tsf.findRange( c, val, !targets );
},
processTypes: function( c, data, vars ) {
var ffxn,
filterMatched = null,

View File

@ -127,6 +127,30 @@
</tbody>
</table>
<table id="table6" class="tester">
<thead>
<tr>
<th rowspan="2" colspan="2">Index</th>
<th colspan="4">Sort All Columns</th>
</tr>
<tr>
<th>Product ID</th>
<th>Numeric</th>
<th id="test">Animals</th>
<th >Url</th>
</tr>
</thead>
<tbody>
<tr><td>G1</td><td>6</td><td>a9</td><td>155</td><td>L</td><td>nytimes</td></tr>
<tr><td>G1</td><td>2</td><td colspan="4">z1 957 K mit</td></tr>
<tr><td>G3</td><td>0</td><td>a13</td><td colspan="2">17 K</td><td>google</td></tr>
<tr><td>G2</td><td>8</td><td>z9</td><td>10</td><td>G</td><td>facebook</td></tr>
<tr><td>G1</td><td>3</td><td colspan="2">z24 67</td><td>B</td><td>whitehouse</td></tr>
<tr><td>G4</td><td colspan="2">7 A10</td><td>87</td><td>Z</td><td>google</td></tr>
<tr><td>G3</td><td>9</td><td>z12</td><td>0</td><td colspan="2">K nasa</td></tr>
</tbody>
</table>
<div id="testblock" class="tester"></div>
<div id="testblock2" class="tester"></div>

View File

@ -141,6 +141,23 @@ jQuery(function($){
assert.cacheCompare( this.table, 3, [ 12, 18, 13, 18 ], 'starting filter value on age column', true );
});
QUnit.test( 'Filter column range', function(assert) {
expect(10);
var range = $.tablesorter.filter.findRange,
c = { columns: 10 }; // psuedo table.config
assert.deepEqual( range( c, '6' ), [ 6 ], '6' );
assert.deepEqual( range( c, '5, 6' ), [ 5,6 ], '5, 6' );
assert.deepEqual( range( c, '5 - 6' ), [ 5,6 ], '5 - 6' );
assert.deepEqual( range( c, '1-3,5-6,8' ), [ 1,2,3,5,6,8 ], '1-3,5-6,8' );
assert.deepEqual( range( c, '6- 3, 2,4' ), [ 3,4,5,6,2,4 ], '6- 3,2,4 (dupes included)' );
assert.deepEqual( range( c, '-1-3, 11' ), [ 1,2,3 ], '-1-3, 11 (negative & out of range ignored)' );
assert.deepEqual( range( c, '8-12' ), [ 8,9 ], '8-12 (not out of range)' );
assert.deepEqual( range( c, 'all' ), [ 0,1,2,3,4,5,6,7,8,9 ], 'all' );
assert.deepEqual( range( c, 'any-text' ), [ 0,1,2,3,4,5,6,7,8,9 ], 'text with dash -> all columns' );
assert.deepEqual( range( c, 'a-b-c,100' ), [ 0,1,2,3,4,5,6,7,8,9 ], 'text with dashes & commas -> all columns' );
});
QUnit.test( 'Filter searches', function(assert) {
var ts = this.ts,
c = this.c,

View File

@ -143,11 +143,13 @@ jQuery(function($){
$table3 = $('#table3'),
$table4 = $('#table4'),
$table5 = $('#table5'), // empty table
$table6 = $('#table6'), // colspan table
table1 = $table1[0],
table2 = $table2[0],
table3 = $table3[0],
table4 = $table4[0],
table5 = $table5[0],
table6 = $table6[0],
th0 = $table1.find('th')[0], // first table header cell
init = false,
sortIndx = 0,
@ -210,6 +212,7 @@ jQuery(function($){
});
$table5.tablesorter();
$table6.tablesorter();
QUnit.module('core');
/************************************************
@ -610,6 +613,42 @@ jQuery(function($){
});
QUnit.test( 'colspan parsing', function(assert) {
assert.expect(2);
t = [
'g1', '6', 'a9', 155, 'l', 'nytimes',
'g1', '2', 'z1 957 K mit', 'z1 957 K mit', 'z1 957 K mit', 'z1 957 K mit', // colspan 4
'g3', '0', 'a13', '17 K', '17 K', 'google',
'g2', '8', 'z9', 10, 'g', 'facebook',
'g1', '3', 'z24 67', 'z24 67', 'b', 'whitehouse',
'g4', '7 A10', '7 A10', 87, 'z', 'google',
'g3', '9', 'z12', 0, 'K nasa', 'K nasa'
];
assert.cacheCompare( table6,'all', t, 'colspans in tbody (duplicateSpan:true)' );
$('#testblock').html('<table class="tablesorter">' +
'<thead><tr><th>1</th><th>2</th><th>3</th><th>4</th></tr></thead>' +
'<tbody>' +
'<tr><td>1</td><td colspan="2">2</td><td>3</td></tr>' +
'<tr><td colspan="3">y</td><td>z</td></tr>' +
'<tr><td>a</td><td>b</td><td colspan="2">c</td></tr>' +
'</tbody></table>')
.find('table')
.tablesorter({
headers : { '*' : { sorter: 'text' } },
duplicateSpan: false
});
t = [
'1', '2', '', '3',
'y', '', '', 'z',
'a', 'b', 'c', ''
];
assert.cacheCompare( $('#testblock table')[0], 'all', t, 'colspans not duplicated in cache (duplicateSpan:false)' );
});
QUnit.test( 'sorton methods', function(assert) {
assert.expect(6);