2013-03-27 23:21:09 +00:00
/ * ! t a b l e S o r t e r 2 . 4 + w i d g e t s - u p d a t e d 3 / 2 7 / 2 0 1 3
2011-10-26 06:50:02 +00:00
*
* Column Styles
2012-05-23 17:11:30 +00:00
* Column Filters
2011-10-26 06:50:02 +00:00
* Column Resizing
2012-08-19 02:10:01 +00:00
* Sticky Header
* UI Theme ( generalized )
2012-02-01 05:14:28 +00:00
* Save Sort
2012-09-27 19:57:19 +00:00
* [ "zebra" , "uitheme" , "stickyHeaders" , "filter" , "columns" ]
2011-10-26 06:50:02 +00:00
* /
2012-10-17 12:33:14 +00:00
/*jshint browser:true, jquery:true, unused:false, loopfunc:true */
2012-08-19 02:10:01 +00:00
/*global jQuery: false, localStorage: false, navigator: false */
2012-05-04 04:38:10 +00:00
; ( function ( $ ) {
2012-08-19 02:10:01 +00:00
"use strict" ;
2013-03-31 17:18:20 +00:00
var ts = $ . tablesorter = $ . tablesorter || { } ;
2012-08-19 02:10:01 +00:00
2013-03-31 17:18:20 +00:00
ts . themes = {
2012-08-19 02:10:01 +00:00
"bootstrap" : {
2012-12-18 23:49:04 +00:00
table : 'table table-bordered table-striped' ,
header : 'bootstrap-header' , // give the header a gradient background
footerRow : '' ,
footerCells : '' ,
icons : '' , // add "icon-white" to make them white; this icon class is added to the <i> in the header
sortNone : 'bootstrap-icon-unsorted' ,
sortAsc : 'icon-chevron-up' ,
sortDesc : 'icon-chevron-down' ,
active : '' , // applied when column is sorted
hover : '' , // use custom css here - bootstrap class may not override it
filterRow : '' , // filter row class
even : '' , // even row zebra striping
odd : '' // odd row zebra striping
2012-08-19 02:10:01 +00:00
} ,
"jui" : {
2012-12-18 23:49:04 +00:00
table : 'ui-widget ui-widget-content ui-corner-all' , // table classes
header : 'ui-widget-header ui-corner-all ui-state-default' , // header classes
footerRow : '' ,
footerCells : '' ,
icons : 'ui-icon' , // icon class added to the <i> in the header
sortNone : 'ui-icon-carat-2-n-s' ,
sortAsc : 'ui-icon-carat-1-n' ,
sortDesc : 'ui-icon-carat-1-s' ,
active : 'ui-state-active' , // applied when column is sorted
hover : 'ui-state-hover' , // hover class
filterRow : '' ,
even : 'ui-widget-content' , // even row zebra striping
odd : 'ui-state-default' // odd row zebra striping
2012-08-19 02:10:01 +00:00
}
} ;
2012-03-07 18:06:35 +00:00
// *** Store data in local storage, with a cookie fallback ***
/* IE7 needs JSON library for JSON.stringify - (http:/ / caniuse . com / # search = json )
if you need it , then include https : //github.com/douglascrockford/JSON-js
2012-03-27 01:49:48 +00:00
$ . parseJSON is not available is jQuery versions older than 1.4 . 1 , using older
versions will only allow storing information for one page at a time
2012-03-07 18:06:35 +00:00
// *** Save data (JSON format only) ***
// val must be valid JSON... use http://jsonlint.com/ to ensure it is valid
var val = { "mywidget" : "data1" } ; // valid JSON uses double quotes
// $.tablesorter.storage(table, key, val);
$ . tablesorter . storage ( table , 'tablesorter-mywidget' , val ) ;
// *** Get data: $.tablesorter.storage(table, key); ***
v = $ . tablesorter . storage ( table , 'tablesorter-mywidget' ) ;
// val may be empty, so also check for your data
val = ( v && v . hasOwnProperty ( 'mywidget' ) ) ? v . mywidget : '' ;
alert ( val ) ; // "data1" if saved, or "" if not
* /
2013-03-31 17:18:20 +00:00
ts . storage = function ( table , key , val ) {
2012-04-02 19:19:17 +00:00
var d , k , ls = false , v = { } ,
2012-03-07 18:06:35 +00:00
id = table . id || $ ( '.tablesorter' ) . index ( $ ( table ) ) ,
url = window . location . pathname ;
try { ls = ! ! ( localStorage . getItem ) ; } catch ( e ) { }
2012-03-27 01:49:48 +00:00
// *** get val ***
2012-08-19 02:10:01 +00:00
if ( $ . parseJSON ) {
if ( ls ) {
2013-02-24 06:22:27 +00:00
v = $ . parseJSON ( localStorage [ key ] || '{}' ) ;
2012-03-27 01:49:48 +00:00
} else {
k = document . cookie . split ( /[;\s|=]/ ) ; // cookie
d = $ . inArray ( key , k ) + 1 ; // add one to get from the key to the value
2013-02-24 06:22:27 +00:00
v = ( d !== 0 ) ? $ . parseJSON ( k [ d ] || '{}' ) : { } ;
2012-03-27 01:49:48 +00:00
}
}
2013-01-04 17:00:48 +00:00
// allow val to be an empty string to
if ( ( val || val === '' ) && window . JSON && JSON . hasOwnProperty ( 'stringify' ) ) {
2012-03-07 18:06:35 +00:00
// add unique identifiers = url pathname > table ID/index on page > data
2012-12-26 07:57:03 +00:00
if ( ! v [ url ] ) {
v [ url ] = { } ;
2012-04-02 19:19:17 +00:00
}
2012-12-26 07:57:03 +00:00
v [ url ] [ id ] = val ;
2012-03-07 18:06:35 +00:00
// *** set val ***
2012-08-19 02:10:01 +00:00
if ( ls ) {
2012-03-07 18:06:35 +00:00
localStorage [ key ] = JSON . stringify ( v ) ;
} else {
d = new Date ( ) ;
2012-08-19 02:10:01 +00:00
d . setTime ( d . getTime ( ) + ( 31536e+6 ) ) ; // 365 days
2012-03-07 18:06:35 +00:00
document . cookie = key + '=' + ( JSON . stringify ( v ) ) . replace ( /\"/g , '\"' ) + '; expires=' + d . toGMTString ( ) + '; path=/' ;
}
2012-03-27 01:49:48 +00:00
} else {
2013-01-04 17:00:48 +00:00
return v && v [ url ] ? v [ url ] [ id ] : { } ;
2012-03-07 18:06:35 +00:00
}
} ;
2012-08-19 02:10:01 +00:00
// Widget: General UI theme
2012-03-07 18:06:35 +00:00
// "uitheme" option in "widgetOptions"
2011-09-13 22:55:31 +00:00
// **************************
2013-03-31 17:18:20 +00:00
ts . addWidget ( {
2011-09-11 17:51:02 +00:00
id : "uitheme" ,
2013-03-31 17:18:20 +00:00
priority : 10 ,
options : {
uitheme : 'jui'
} ,
format : function ( table , c , wo ) {
2012-12-26 07:57:03 +00:00
var time , klass , $el , $tar ,
2013-03-31 17:18:20 +00:00
t = ts . themes ,
$t = c . $table ,
theme = c . theme !== 'default' ? c . theme : wo . uitheme || 'jui' ,
2012-12-26 07:57:03 +00:00
o = t [ t [ theme ] ? theme : t [ wo . uitheme ] ? wo . uitheme : 'jui' ] ,
2013-03-31 17:18:20 +00:00
$h = c . $headers ,
2012-08-19 02:10:01 +00:00
sh = 'tr.' + ( wo . stickyHeaders || 'tablesorter-stickyHeader' ) ,
rmv = o . sortNone + ' ' + o . sortDesc + ' ' + o . sortAsc ;
if ( c . debug ) { time = new Date ( ) ; }
if ( ! $t . hasClass ( 'tablesorter-' + theme ) || c . theme === theme || ! table . hasInitialized ) {
// update zebra stripes
if ( o . even !== '' ) { wo . zebra [ 0 ] += ' ' + o . even ; }
if ( o . odd !== '' ) { wo . zebra [ 1 ] += ' ' + o . odd ; }
// add table/footer class names
2012-12-18 23:49:04 +00:00
t = $t
2012-08-19 02:10:01 +00:00
// remove other selected themes; use widgetOptions.theme_remove
. removeClass ( c . theme === '' ? '' : 'tablesorter-' + c . theme )
2012-12-18 23:49:04 +00:00
. addClass ( 'tablesorter-' + theme + ' ' + o . table ) // add theme widget class name
. find ( 'tfoot' ) ;
if ( t . length ) {
t
. find ( 'tr' ) . addClass ( o . footerRow )
. children ( 'th, td' ) . addClass ( o . footerCells ) ;
}
2012-08-19 02:10:01 +00:00
// update header classes
$h
. addClass ( o . header )
. filter ( ':not(.sorter-false)' )
2013-02-17 19:21:05 +00:00
. bind ( 'mouseenter.tsuitheme mouseleave.tsuitheme' , function ( e ) {
// toggleClass with switch added in jQuery 1.3
$ ( this ) [ e . type === 'mouseenter' ? 'addClass' : 'removeClass' ] ( o . hover ) ;
2011-12-14 17:37:55 +00:00
} ) ;
2012-09-29 13:42:58 +00:00
if ( ! $h . find ( '.tablesorter-wrapper' ) . length ) {
// Firefox needs this inner div to position the resizer correctly
$h . wrapInner ( '<div class="tablesorter-wrapper" style="position:relative;height:100%;width:100%"></div>' ) ;
}
2012-08-19 02:10:01 +00:00
if ( c . cssIcon ) {
// if c.cssIcon is '', then no <i> is added to the header
$h . find ( '.' + c . cssIcon ) . addClass ( o . icons ) ;
}
if ( $t . hasClass ( 'hasFilters' ) ) {
$h . find ( '.tablesorter-filter-row' ) . addClass ( o . filterRow ) ;
}
2011-09-11 17:51:02 +00:00
}
2012-08-19 02:10:01 +00:00
$ . each ( $h , function ( i ) {
$el = $ ( this ) ;
$tar = ( c . cssIcon ) ? $el . find ( '.' + c . cssIcon ) : $el ;
if ( this . sortDisabled ) {
2011-09-11 17:51:02 +00:00
// no sort arrows for disabled columns!
2012-08-19 02:10:01 +00:00
$el . removeClass ( rmv ) ;
$tar . removeClass ( rmv + ' tablesorter-icon ' + o . icons ) ;
2011-09-11 17:51:02 +00:00
} else {
2012-08-19 02:10:01 +00:00
t = ( $t . hasClass ( 'hasStickyHeaders' ) ) ? $t . find ( sh ) . find ( 'th' ) . eq ( i ) . add ( $el ) : $el ;
2012-11-23 01:53:41 +00:00
klass = ( $el . hasClass ( c . cssAsc ) ) ? o . sortAsc : ( $el . hasClass ( c . cssDesc ) ) ? o . sortDesc : $el . hasClass ( c . cssHeader ) ? o . sortNone : '' ;
2012-08-19 02:10:01 +00:00
$el [ klass === o . sortNone ? 'removeClass' : 'addClass' ] ( o . active ) ;
$tar . removeClass ( rmv ) . addClass ( klass ) ;
2011-09-11 17:51:02 +00:00
}
} ) ;
2012-08-19 02:10:01 +00:00
if ( c . debug ) {
2013-03-31 17:18:20 +00:00
ts . benchmark ( "Applying " + theme + " theme" , time ) ;
2011-09-11 17:51:02 +00:00
}
2012-08-19 02:10:01 +00:00
} ,
remove : function ( table , c , wo ) {
2013-03-31 17:18:20 +00:00
var $t = c . $table ,
2012-09-27 19:57:19 +00:00
theme = typeof wo . uitheme === 'object' ? 'jui' : wo . uitheme || 'jui' ,
2013-03-31 17:18:20 +00:00
o = typeof wo . uitheme === 'object' ? wo . uitheme : ts . themes [ ts . themes . hasOwnProperty ( theme ) ? theme : 'jui' ] ,
2012-08-19 02:10:01 +00:00
$h = $t . children ( 'thead' ) . children ( ) ,
rmv = o . sortNone + ' ' + o . sortDesc + ' ' + o . sortAsc ;
$t
. removeClass ( 'tablesorter-' + theme + ' ' + o . table )
. find ( c . cssHeader ) . removeClass ( o . header ) ;
$h
2013-02-17 19:21:05 +00:00
. unbind ( 'mouseenter.tsuitheme mouseleave.tsuitheme' ) // remove hover
2012-08-19 02:10:01 +00:00
. removeClass ( o . hover + ' ' + rmv + ' ' + o . active )
. find ( '.tablesorter-filter-row' ) . removeClass ( o . filterRow ) ;
$h . find ( '.tablesorter-icon' ) . removeClass ( o . icons ) ;
2011-09-11 17:51:02 +00:00
}
} ) ;
2012-03-07 18:06:35 +00:00
// Widget: Column styles
2012-09-27 19:57:19 +00:00
// "columns", "columns_thead" (true) and
// "columns_tfoot" (true) options in "widgetOptions"
2011-09-13 22:55:31 +00:00
// **************************
2013-03-31 17:18:20 +00:00
ts . addWidget ( {
2011-09-11 17:51:02 +00:00
id : "columns" ,
2013-04-01 16:02:48 +00:00
priority : 30 ,
2013-03-31 17:18:20 +00:00
options : {
columns : [ "primary" , "secondary" , "tertiary" ]
} ,
format : function ( table , c , wo ) {
2012-05-28 15:01:40 +00:00
var $tb , $tr , $td , $t , time , last , rmv , i , k , l ,
2013-03-31 17:18:20 +00:00
$tbl = c . $table ,
2013-01-04 15:15:29 +00:00
b = c . $tbodies ,
2011-09-11 17:51:02 +00:00
list = c . sortList ,
len = list . length ,
2012-03-07 18:06:35 +00:00
// keep backwards compatibility, for now
css = ( c . widgetColumns && c . widgetColumns . hasOwnProperty ( 'css' ) ) ? c . widgetColumns . css || css :
2012-08-19 02:10:01 +00:00
( wo && wo . hasOwnProperty ( 'columns' ) ) ? wo . columns || css : css ;
2011-09-16 15:43:09 +00:00
last = css . length - 1 ;
2011-09-11 17:51:02 +00:00
rmv = css . join ( ' ' ) ;
2012-08-19 02:10:01 +00:00
if ( c . debug ) {
2011-09-11 17:51:02 +00:00
time = new Date ( ) ;
}
2011-09-13 22:55:31 +00:00
// check if there is a sort (on initialization there may not be one)
2012-08-19 02:10:01 +00:00
for ( k = 0 ; k < b . length ; k ++ ) {
2013-03-31 17:18:20 +00:00
$tb = ts . processTbody ( table , b . eq ( k ) , true ) ; // detach tbody
2012-08-19 02:10:01 +00:00
$tr = $tb . children ( 'tr' ) ;
2012-05-23 17:11:30 +00:00
l = $tr . length ;
// loop through the visible rows
2012-05-28 15:01:40 +00:00
$tr . each ( function ( ) {
$t = $ ( this ) ;
2012-08-19 02:10:01 +00:00
if ( this . style . display !== 'none' ) {
2012-05-23 17:11:30 +00:00
// remove all columns class names
2012-05-28 15:01:40 +00:00
$td = $t . children ( ) . removeClass ( rmv ) ;
2012-05-23 17:11:30 +00:00
// add appropriate column class names
2012-08-19 02:10:01 +00:00
if ( list && list [ 0 ] ) {
2012-05-23 17:11:30 +00:00
// primary sort column class
$td . eq ( list [ 0 ] [ 0 ] ) . addClass ( css [ 0 ] ) ;
2012-08-19 02:10:01 +00:00
if ( len > 1 ) {
2012-05-23 17:11:30 +00:00
for ( i = 1 ; i < len ; i ++ ) {
// secondary, tertiary, etc sort column classes
$td . eq ( list [ i ] [ 0 ] ) . addClass ( css [ i ] || css [ last ] ) ;
}
2012-05-03 14:46:31 +00:00
}
2011-09-13 22:55:31 +00:00
}
2012-05-19 20:46:14 +00:00
}
2012-05-28 15:01:40 +00:00
} ) ;
2013-03-31 17:18:20 +00:00
ts . processTbody ( table , $tb , false ) ;
2011-09-13 22:55:31 +00:00
}
2012-09-27 19:57:19 +00:00
// add classes to thead and tfoot
$tr = wo . columns _thead !== false ? 'thead tr' : '' ;
if ( wo . columns _tfoot !== false ) {
$tr += ( $tr === '' ? '' : ',' ) + 'tfoot tr' ;
}
if ( $tr . length ) {
$t = $tbl . find ( $tr ) . children ( ) . removeClass ( rmv ) ;
if ( list && list [ 0 ] ) {
// primary sort column class
$t . filter ( '[data-column="' + list [ 0 ] [ 0 ] + '"]' ) . addClass ( css [ 0 ] ) ;
if ( len > 1 ) {
for ( i = 1 ; i < len ; i ++ ) {
// secondary, tertiary, etc sort column classes
$t . filter ( '[data-column="' + list [ i ] [ 0 ] + '"]' ) . addClass ( css [ i ] || css [ last ] ) ;
}
2012-08-19 02:10:01 +00:00
}
}
}
if ( c . debug ) {
2013-03-31 17:18:20 +00:00
ts . benchmark ( "Applying Columns widget" , time ) ;
2011-09-11 17:51:02 +00:00
}
2012-08-19 02:10:01 +00:00
} ,
remove : function ( table , c , wo ) {
var k , $tb ,
2013-01-04 15:15:29 +00:00
b = c . $tbodies ,
2013-03-26 21:16:13 +00:00
rmv = ( wo . columns || [ "primary" , "secondary" , "tertiary" ] ) . join ( ' ' ) ;
2013-01-04 17:00:48 +00:00
c . $headers . removeClass ( rmv ) ;
2013-03-31 17:18:20 +00:00
c . $table . children ( 'tfoot' ) . children ( 'tr' ) . children ( 'th, td' ) . removeClass ( rmv ) ;
2012-08-19 02:10:01 +00:00
for ( k = 0 ; k < b . length ; k ++ ) {
2013-03-31 17:18:20 +00:00
$tb = ts . processTbody ( table , b . eq ( k ) , true ) ; // remove tbody
2012-08-19 02:10:01 +00:00
$tb . children ( 'tr' ) . each ( function ( ) {
$ ( this ) . children ( ) . removeClass ( rmv ) ;
} ) ;
2013-03-31 17:18:20 +00:00
ts . processTbody ( table , $tb , false ) ; // restore tbody
2012-08-19 02:10:01 +00:00
}
2011-09-11 17:51:02 +00:00
}
2011-09-13 22:55:31 +00:00
} ) ;
2013-03-31 17:18:20 +00:00
// Widget: filter
// **************************
ts . addWidget ( {
2011-09-13 22:55:31 +00:00
id : "filter" ,
2013-03-31 17:18:20 +00:00
priority : 50 ,
options : {
filter _childRows : false , // if true, filter includes child row content in the search
filter _columnFilters : true , // if true, a filter will be added to the top of each table column
filter _cssFilter : 'tablesorter-filter' , // css class name added to the filter row & each input in the row
filter _formatter : null , // add custom filter elements to the filter row
filter _functions : null , // add custom filter functions using this option
filter _hideFilters : false , // collapse filter row when mouse leaves the area
filter _ignoreCase : true , // if true, make all searches case-insensitive
2013-04-08 06:21:57 +00:00
filter _liveSearch : true , // if true, search column content while the user types (with a delay)
2013-03-31 17:18:20 +00:00
filter _reset : null , // jQuery selector string of an element used to reset the filters
filter _searchDelay : 300 , // typing delay in milliseconds before starting a search
filter _startsWith : false , // if true, filter start from the beginning of the cell contents
filter _useParsedData : false , // filter all data using parsed content
filter _serversideFiltering : false , // if true, server-side filtering should be performed because client-side filtering will be disabled, but the ui and events will still be used.
2013-04-12 22:13:22 +00:00
// regex used in filter "check" functions - not for general use and not documented
filter _regex : {
2013-03-31 17:18:20 +00:00
"regex" : /^\/((?:\\\/|[^\/])+)\/([mig]{0,3})?$/ , // regex to test for regex
"child" : /tablesorter-childRow/ , // child row class name; this gets updated in the script
"type" : /undefined|number/ , // check type
"exact" : /(^[\"|\'|=])|([\"|\'|=]$)/g , // exact match
"nondigit" : /[^\w,. \-()]/g , // replace non-digits (from digit & currency parser)
"operators" : /[<>=]/g // replace operators
}
} ,
format : function ( table , c , wo ) {
if ( c . parsers && ! c . $table . hasClass ( 'hasFilters' ) ) {
2012-08-19 02:10:01 +00:00
var i , j , k , l , val , ff , x , xi , st , sel , str ,
2013-04-01 16:02:48 +00:00
ft , ft2 , $th , rg , s , t , dis , col ,
2013-03-31 17:18:20 +00:00
fmt = ts . formatFloat ,
2012-08-19 02:10:01 +00:00
last = '' , // save last filter search
2013-03-31 17:18:20 +00:00
$ths = c . $headers ,
2013-04-01 16:02:48 +00:00
css = wo . filter _cssFilter ,
2013-03-31 17:18:20 +00:00
$t = c . $table . addClass ( 'hasFilters' ) ,
2013-03-26 21:36:44 +00:00
b = $t . find ( 'tbody' ) ,
2012-05-23 17:11:30 +00:00
cols = c . parsers . length ,
2013-02-17 19:21:05 +00:00
parsed , time , timer ,
2012-08-19 02:10:01 +00:00
// dig fer gold
checkFilters = function ( filter ) {
var arry = $ . isArray ( filter ) ,
2013-04-12 16:26:16 +00:00
v = ( arry ) ? filter : ts . getFilters ( table ) ,
2012-08-19 02:10:01 +00:00
cv = ( v || [ ] ) . join ( '' ) ; // combined filter values
2012-09-27 19:57:19 +00:00
// add filter array back into inputs
if ( arry ) {
2013-04-01 16:02:48 +00:00
c . $filters . each ( function ( i , el ) {
2012-09-27 19:57:19 +00:00
$ ( el ) . val ( filter [ i ] || '' ) ;
} ) ;
}
2013-03-31 17:18:20 +00:00
if ( wo . filter _hideFilters ) {
2012-08-19 02:10:01 +00:00
// show/hide filter row as needed
$t . find ( '.tablesorter-filter-row' ) . trigger ( cv === '' ? 'mouseleave' : 'mouseenter' ) ;
}
// return if the last search is the same; but filter === false when updating the search
// see example-widget-filter.html filter toggle buttons
if ( last === cv && filter !== false ) { return ; }
$t . trigger ( 'filterStart' , [ v ] ) ;
2012-09-27 19:57:19 +00:00
if ( c . showProcessing ) {
// give it time for the processing icon to kick in
setTimeout ( function ( ) {
findRows ( filter , v , cv ) ;
return false ;
} , 30 ) ;
} else {
2012-08-19 02:10:01 +00:00
findRows ( filter , v , cv ) ;
return false ;
2012-09-27 19:57:19 +00:00
}
2012-08-19 02:10:01 +00:00
} ,
findRows = function ( filter , v , cv ) {
2013-03-26 21:16:13 +00:00
var $tb , $tr , $td , cr , r , l , ff , time , r1 , r2 ;
2012-05-23 17:11:30 +00:00
if ( c . debug ) { time = new Date ( ) ; }
2012-08-19 02:10:01 +00:00
for ( k = 0 ; k < b . length ; k ++ ) {
2013-03-26 21:36:44 +00:00
if ( b . eq ( k ) . hasClass ( c . cssInfoBlock ) ) { continue ; } // ignore info blocks, issue #264
2013-03-31 17:18:20 +00:00
$tb = ts . processTbody ( table , b . eq ( k ) , true ) ;
2012-08-19 02:10:01 +00:00
$tr = $tb . children ( 'tr' ) ;
2012-05-23 17:11:30 +00:00
l = $tr . length ;
2012-12-17 17:33:46 +00:00
if ( cv === '' || wo . filter _serversideFiltering ) {
2012-08-19 02:10:01 +00:00
$tr . show ( ) . removeClass ( 'filtered' ) ;
} else {
// loop through the rows
for ( j = 0 ; j < l ; j ++ ) {
2012-06-20 12:15:46 +00:00
// skip child rows
2013-03-31 17:18:20 +00:00
if ( wo . filter _regex . child . test ( $tr [ j ] . className ) ) { continue ; }
2012-06-01 14:49:46 +00:00
r = true ;
cr = $tr . eq ( j ) . nextUntil ( 'tr:not(.' + c . cssChildRow + ')' ) ;
// so, if "table.config.widgetOptions.filter_childRows" is true and there is
// a match anywhere in the child row, then it will make the row visible
// checked here so the option can be changed dynamically
2013-03-31 17:18:20 +00:00
t = ( cr . length && wo . filter _childRows ) ? cr . text ( ) : '' ;
2012-06-20 12:15:46 +00:00
t = wo . filter _ignoreCase ? t . toLocaleLowerCase ( ) : t ;
2012-06-01 14:49:46 +00:00
$td = $tr . eq ( j ) . children ( 'td' ) ;
2012-08-19 02:10:01 +00:00
for ( i = 0 ; i < cols ; i ++ ) {
// ignore if filter is empty or disabled
if ( v [ i ] ) {
// check if column data should be from the cell or from parsed data
if ( wo . filter _useParsedData || parsed [ i ] ) {
x = c . cache [ k ] . normalized [ j ] [ i ] ;
} else {
// using older or original tablesorter
x = $ . trim ( $td . eq ( i ) . text ( ) ) ;
}
2013-03-31 17:18:20 +00:00
xi = ! wo . filter _regex . type . test ( typeof x ) && wo . filter _ignoreCase ? x . toLocaleLowerCase ( ) : x ;
2012-06-01 14:49:46 +00:00
ff = r ; // if r is true, show that row
// val = case insensitive, v[i] = case sensitive
val = wo . filter _ignoreCase ? v [ i ] . toLocaleLowerCase ( ) : v [ i ] ;
2012-08-19 02:10:01 +00:00
if ( wo . filter _functions && wo . filter _functions [ i ] ) {
if ( wo . filter _functions [ i ] === true ) {
2012-06-01 14:49:46 +00:00
// default selector; no "filter-select" class
2012-08-19 02:10:01 +00:00
ff = ( $ths . filter ( '[data-column="' + i + '"]:last' ) . hasClass ( 'filter-match' ) ) ? xi . search ( val ) >= 0 : v [ i ] === x ;
} else if ( typeof wo . filter _functions [ i ] === 'function' ) {
2012-06-01 14:49:46 +00:00
// filter callback( exact cell content, parser normalized content, filter input value, column index )
ff = wo . filter _functions [ i ] ( x , c . cache [ k ] . normalized [ j ] [ i ] , v [ i ] , i ) ;
} else if ( typeof wo . filter _functions [ i ] [ v [ i ] ] === 'function' ) {
// selector option function
ff = wo . filter _functions [ i ] [ v [ i ] ] ( x , c . cache [ k ] . normalized [ j ] [ i ] , v [ i ] , i ) ;
}
// Look for regex
2013-03-31 17:18:20 +00:00
} else if ( wo . filter _regex . regex . test ( val ) ) {
rg = wo . filter _regex . regex . exec ( val ) ;
2012-06-05 12:18:14 +00:00
try {
2012-08-19 02:10:01 +00:00
ff = new RegExp ( rg [ 1 ] , rg [ 2 ] ) . test ( xi ) ;
} catch ( err ) {
2012-06-05 12:18:14 +00:00
ff = false ;
}
2013-02-17 19:21:05 +00:00
// Look for quotes or equals to get an exact match; ignore type since xi could be numeric
/*jshint eqeqeq:false */
2013-03-31 17:18:20 +00:00
} else if ( val . replace ( wo . filter _regex . exact , '' ) == xi ) {
2012-06-12 17:01:10 +00:00
ff = true ;
2012-08-19 02:10:01 +00:00
// Look for a not match
} else if ( /^\!/ . test ( val ) ) {
val = val . replace ( '!' , '' ) ;
s = xi . search ( $ . trim ( val ) ) ;
ff = val === '' ? true : ! ( wo . filter _startsWith ? s === 0 : s >= 0 ) ;
// Look for operators >, >=, < or <=
} else if ( /^[<>]=?/ . test ( val ) ) {
2012-10-14 01:16:05 +00:00
// xi may be numeric - see issue #149
2013-03-31 17:18:20 +00:00
rg = isNaN ( xi ) ? fmt ( xi . replace ( wo . filter _regex . nondigit , '' ) , table ) : fmt ( xi , table ) ;
s = fmt ( val . replace ( wo . filter _regex . nondigit , '' ) . replace ( wo . filter _regex . operators , '' ) , table ) ;
2012-08-19 02:10:01 +00:00
if ( />/ . test ( val ) ) { ff = />=/ . test ( val ) ? rg >= s : rg > s ; }
if ( /</ . test ( val ) ) { ff = /<=/ . test ( val ) ? rg <= s : rg < s ; }
2013-02-17 19:21:05 +00:00
if ( s === '' ) { ff = true ; } // keep showing all rows if nothing follows the operator
// Look for an AND or && operator (logical and)
} else if ( /\s+(AND|&&)\s+/g . test ( v [ i ] ) ) {
s = val . split ( /(?:\s+(?:and|&&)\s+)/g ) ;
ff = xi . search ( $ . trim ( s [ 0 ] ) ) >= 0 ;
r1 = s . length - 1 ;
while ( ff && r1 ) {
ff = ff && xi . search ( $ . trim ( s [ r1 ] ) ) >= 0 ;
r1 -- ;
}
// Look for a range (using " to " or " - ") - see issue #166; thanks matzhu!
} else if ( /\s+(-|to)\s+/ . test ( val ) ) {
2013-03-31 17:18:20 +00:00
rg = isNaN ( xi ) ? fmt ( xi . replace ( wo . filter _regex . nondigit , '' ) , table ) : fmt ( xi , table ) ;
2013-02-17 19:21:05 +00:00
s = val . split ( /(?: - | to )/ ) ; // make sure the dash is for a range and not indicating a negative number
2013-03-31 17:18:20 +00:00
r1 = fmt ( s [ 0 ] . replace ( wo . filter _regex . nondigit , '' ) , table ) ;
r2 = fmt ( s [ 1 ] . replace ( wo . filter _regex . nondigit , '' ) , table ) ;
2013-02-17 19:21:05 +00:00
if ( r1 > r2 ) { ff = r1 ; r1 = r2 ; r2 = ff ; } // swap
ff = ( rg >= r1 && rg <= r2 ) || ( r1 === '' || r2 === '' ) ? true : false ;
// Look for wild card: ? = single, * = multiple, or | = logical OR
} else if ( /[\?|\*]/ . test ( val ) || /\s+OR\s+/ . test ( v [ i ] ) ) {
ff = new RegExp ( val . replace ( /\s+or\s+/gi , "|" ) . replace ( /\?/g , '\\S{1}' ) . replace ( /\*/g , '\\S*' ) ) . test ( xi ) ;
2012-06-01 14:49:46 +00:00
// Look for match, and add child row data for matching
} else {
x = ( xi + t ) . indexOf ( val ) ;
2012-06-12 17:01:10 +00:00
ff = ( ( ! wo . filter _startsWith && x >= 0 ) || ( wo . filter _startsWith && x === 0 ) ) ;
2012-05-23 17:11:30 +00:00
}
2012-06-01 14:49:46 +00:00
r = ( ff ) ? ( r ? true : false ) : false ;
2012-05-23 17:11:30 +00:00
}
}
2012-06-01 14:49:46 +00:00
$tr [ j ] . style . display = ( r ? '' : 'none' ) ;
2012-08-19 02:10:01 +00:00
$tr . eq ( j ) [ r ? 'removeClass' : 'addClass' ] ( 'filtered' ) ;
2012-06-01 14:49:46 +00:00
if ( cr . length ) { cr [ r ? 'show' : 'hide' ] ( ) ; }
2012-05-23 17:11:30 +00:00
}
}
2013-03-31 17:18:20 +00:00
ts . processTbody ( table , $tb , false ) ;
2012-05-23 17:11:30 +00:00
}
2012-08-19 02:10:01 +00:00
last = cv ; // save last search
2013-04-12 16:26:16 +00:00
$t . data ( 'lastSearch' , v ) ;
2012-08-19 02:10:01 +00:00
if ( c . debug ) {
ts . benchmark ( "Completed filter widget search" , time ) ;
2012-05-23 17:11:30 +00:00
}
$t . trigger ( 'applyWidgets' ) ; // make sure zebra widget is applied
2012-08-19 02:10:01 +00:00
$t . trigger ( 'filterEnd' ) ;
2012-06-01 14:49:46 +00:00
} ,
2012-06-20 14:40:32 +00:00
buildSelect = function ( i , updating ) {
2012-06-01 14:49:46 +00:00
var o , arry = [ ] ;
i = parseInt ( i , 10 ) ;
2012-06-12 17:01:10 +00:00
o = '<option value="">' + ( $ths . filter ( '[data-column="' + i + '"]:last' ) . attr ( 'data-placeholder' ) || '' ) + '</option>' ;
2012-08-19 02:10:01 +00:00
for ( k = 0 ; k < b . length ; k ++ ) {
2012-06-01 14:49:46 +00:00
l = c . cache [ k ] . row . length ;
// loop through the rows
2012-08-19 02:10:01 +00:00
for ( j = 0 ; j < l ; j ++ ) {
2012-06-01 14:49:46 +00:00
// get non-normalized cell content
2012-08-19 02:10:01 +00:00
if ( wo . filter _useParsedData ) {
arry . push ( '' + c . cache [ k ] . normalized [ j ] [ i ] ) ;
} else {
t = c . cache [ k ] . row [ j ] [ 0 ] . cells [ i ] ;
if ( t ) {
2012-10-24 03:43:11 +00:00
arry . push ( $ . trim ( c . supportsTextContent ? t . textContent : $ ( t ) . text ( ) ) ) ;
2012-08-19 02:10:01 +00:00
}
2012-06-20 14:40:32 +00:00
}
2012-06-01 14:49:46 +00:00
}
}
2012-08-19 02:10:01 +00:00
2012-06-01 14:49:46 +00:00
// get unique elements and sort the list
2012-08-19 02:10:01 +00:00
// if $.tablesorter.sortText exists (not in the original tablesorter),
// then natural sort the list otherwise use a basic sort
arry = $ . grep ( arry , function ( v , k ) {
2013-03-31 17:18:20 +00:00
return $ . inArray ( v , arry ) === k ;
2012-08-19 02:10:01 +00:00
} ) ;
2013-03-31 17:18:20 +00:00
arry = ( ts . sortText ) ? arry . sort ( function ( a , b ) { return ts . sortText ( table , a , b , i ) ; } ) : arry . sort ( true ) ;
2012-08-19 02:10:01 +00:00
2012-06-01 14:49:46 +00:00
// build option list
2012-08-19 02:10:01 +00:00
for ( k = 0 ; k < arry . length ; k ++ ) {
2013-02-21 18:08:20 +00:00
// replace quotes - fixes #242 & ignore empty strings - see http://stackoverflow.com/q/14990971/145346
o += arry [ k ] !== '' ? '<option value="' + arry [ k ] . replace ( /\"/g , """ ) + '">' + arry [ k ] + '</option>' : '' ;
2012-06-01 14:49:46 +00:00
}
2012-06-20 14:40:32 +00:00
$t . find ( 'thead' ) . find ( 'select.' + css + '[data-column="' + i + '"]' ) [ updating ? 'html' : 'append' ] ( o ) ;
} ,
buildDefault = function ( updating ) {
// build default select dropdown
2012-08-19 02:10:01 +00:00
for ( i = 0 ; i < cols ; i ++ ) {
2012-06-20 14:40:32 +00:00
t = $ths . filter ( '[data-column="' + i + '"]:last' ) ;
2013-01-09 05:21:04 +00:00
// look for the filter-select class; build/update it if found
2013-01-10 14:52:23 +00:00
if ( ( t . hasClass ( 'filter-select' ) || wo . filter _functions && wo . filter _functions [ i ] === true ) && ! t . hasClass ( 'filter-false' ) ) {
2012-08-19 02:10:01 +00:00
if ( ! wo . filter _functions ) { wo . filter _functions = { } ; }
wo . filter _functions [ i ] = true ; // make sure this select gets processed by filter_functions
2012-06-20 14:40:32 +00:00
buildSelect ( i , updating ) ;
}
}
2012-05-23 17:11:30 +00:00
} ;
2012-08-19 02:10:01 +00:00
if ( c . debug ) {
2011-09-13 22:55:31 +00:00
time = new Date ( ) ;
}
2013-03-31 17:18:20 +00:00
wo . filter _regex . child = new RegExp ( c . cssChildRow ) ;
2012-10-17 12:33:14 +00:00
// don't build filter row if columnFilters is false or all columns are set to "filter-false" - issue #156
if ( wo . filter _columnFilters !== false && $ths . filter ( '.filter-false' ) . length !== $ths . length ) {
2013-02-17 19:21:05 +00:00
// build filter row
t = '<tr class="tablesorter-filter-row">' ;
for ( i = 0 ; i < cols ; i ++ ) {
t += '<td></td>' ;
}
2013-04-01 16:02:48 +00:00
c . $filters = $ ( t += '</tr>' ) . appendTo ( $t . find ( 'thead' ) . eq ( 0 ) ) . find ( 'td' ) ;
2013-02-17 19:21:05 +00:00
// build each filter input
2012-08-19 02:10:01 +00:00
for ( i = 0 ; i < cols ; i ++ ) {
dis = false ;
$th = $ths . filter ( '[data-column="' + i + '"]:last' ) ; // assuming last cell of a column is the main column
sel = ( wo . filter _functions && wo . filter _functions [ i ] && typeof wo . filter _functions [ i ] !== 'function' ) || $th . hasClass ( 'filter-select' ) ;
// use header option - headers: { 1: { filter: false } } OR add class="filter-false"
if ( ts . getData ) {
// get data from jQuery data, metadata, headers option or header class name
2013-02-17 19:21:05 +00:00
dis = ts . getData ( $th [ 0 ] , c . headers [ i ] , 'filter' ) === 'false' ;
2012-08-19 02:10:01 +00:00
} else {
// only class names and header options - keep this for compatibility with tablesorter v2.0.5
2013-02-17 19:21:05 +00:00
dis = ( c . headers [ i ] && c . headers [ i ] . hasOwnProperty ( 'filter' ) && c . headers [ i ] . filter === false ) || $th . hasClass ( 'filter-false' ) ;
}
if ( sel ) {
2013-04-01 16:02:48 +00:00
t = $ ( '<select>' ) . appendTo ( c . $filters . eq ( i ) ) ;
2013-02-17 19:21:05 +00:00
} else {
if ( wo . filter _formatter && $ . isFunction ( wo . filter _formatter [ i ] ) ) {
2013-04-01 16:02:48 +00:00
t = wo . filter _formatter [ i ] ( c . $filters . eq ( i ) , i ) ;
2013-02-17 19:21:05 +00:00
// no element returned, so lets go find it
2013-04-01 16:02:48 +00:00
if ( t && t . length === 0 ) { t = c . $filters . eq ( i ) . children ( 'input' ) ; }
2013-02-17 19:21:05 +00:00
// element not in DOM, so lets attach it
2013-04-01 16:02:48 +00:00
if ( t && ( t . parent ( ) . length === 0 || ( t . parent ( ) . length && t . parent ( ) [ 0 ] !== c . $filters [ i ] ) ) ) {
c . $filters . eq ( i ) . append ( t ) ;
2013-02-17 19:21:05 +00:00
}
} else {
2013-04-01 16:02:48 +00:00
t = $ ( '<input type="search">' ) . appendTo ( c . $filters . eq ( i ) ) ;
2013-02-17 19:21:05 +00:00
}
if ( t ) {
t . attr ( 'placeholder' , $th . attr ( 'data-placeholder' ) || '' ) ;
}
}
if ( t ) {
t . addClass ( css ) . attr ( 'data-column' , i ) ;
if ( dis ) {
t . addClass ( 'disabled' ) [ 0 ] . disabled = true ; // disabled!
}
2012-08-19 02:10:01 +00:00
}
2012-05-19 20:46:14 +00:00
}
2011-09-13 22:55:31 +00:00
}
2012-03-07 18:06:35 +00:00
$t
2013-03-05 18:29:54 +00:00
. bind ( 'addRows updateCell update updateRows updateComplete appendCache filterReset search ' . split ( ' ' ) . join ( '.tsfilter ' ) , function ( e , filter ) {
2013-02-17 19:21:05 +00:00
if ( ! /(search|filterReset)/ . test ( e . type ) ) {
2013-03-23 14:41:33 +00:00
e . stopPropagation ( ) ;
2012-08-19 02:10:01 +00:00
buildDefault ( true ) ;
}
2013-02-17 19:21:05 +00:00
if ( e . type === 'filterReset' ) {
$t . find ( '.' + css ) . val ( '' ) ;
}
2013-02-24 06:04:46 +00:00
// send false argument to force a new search; otherwise if the filter hasn't changed, it will return
2013-04-12 16:26:16 +00:00
filter = e . type === 'search' ? filter : e . type === 'updateComplete' ? $t . data ( 'lastSearch' ) : '' ;
2013-03-05 18:29:54 +00:00
checkFilters ( filter ) ;
2012-08-19 02:10:01 +00:00
return false ;
2012-06-05 12:22:43 +00:00
} )
2012-08-19 02:10:01 +00:00
. find ( 'input.' + css ) . bind ( 'keyup search' , function ( e , filter ) {
2012-06-01 14:49:46 +00:00
// ignore arrow and meta keys; allow backspace
2013-04-08 06:21:57 +00:00
if ( e . type === 'keyup' && ( ( e . which < 32 && e . which !== 8 ) || ( e . which >= 37 && e . which <= 40 ) || ( e . which !== 13 && ! wo . filter _liveSearch ) ) ) { return ; }
2012-06-01 14:49:46 +00:00
// skip delay
2013-04-12 16:26:16 +00:00
if ( typeof filter === 'undefined' || filter === false ) {
checkFilters ( ) ;
} else {
// delay filtering
clearTimeout ( timer ) ;
timer = setTimeout ( function ( ) {
checkFilters ( filter ) ;
} , wo . filter _searchDelay ) ;
return false ;
2012-06-01 14:49:46 +00:00
}
} ) ;
2012-08-19 02:10:01 +00:00
2013-02-17 19:21:05 +00:00
// parse columns after formatter, in case the class is added at that point
parsed = $ths . map ( function ( i ) {
return ( ts . getData ) ? ts . getData ( $ths . filter ( '[data-column="' + i + '"]:last' ) , c . headers [ i ] , 'filter' ) === 'parsed' : $ ( this ) . hasClass ( 'filter-parsed' ) ;
} ) . get ( ) ;
2012-08-19 02:10:01 +00:00
// reset button/link
if ( wo . filter _reset && $ ( wo . filter _reset ) . length ) {
2013-02-17 19:21:05 +00:00
$ ( wo . filter _reset ) . bind ( 'click.tsfilter' , function ( ) {
$t . trigger ( 'filterReset' ) ;
2012-08-19 02:10:01 +00:00
} ) ;
}
if ( wo . filter _functions ) {
2012-06-01 14:49:46 +00:00
// i = column # (string)
2012-08-19 02:10:01 +00:00
for ( col in wo . filter _functions ) {
if ( wo . filter _functions . hasOwnProperty ( col ) && typeof col === 'string' ) {
t = $ths . filter ( '[data-column="' + col + '"]:last' ) ;
ff = '' ;
if ( wo . filter _functions [ col ] === true && ! t . hasClass ( 'filter-false' ) ) {
buildSelect ( col ) ;
} else if ( typeof col === 'string' && ! t . hasClass ( 'filter-false' ) ) {
// add custom drop down list
for ( str in wo . filter _functions [ col ] ) {
if ( typeof str === 'string' ) {
ff += ff === '' ? '<option value="">' + ( t . attr ( 'data-placeholder' ) || '' ) + '</option>' : '' ;
ff += '<option value="' + str + '">' + str + '</option>' ;
}
2012-06-01 14:49:46 +00:00
}
2012-08-19 02:10:01 +00:00
$t . find ( 'thead' ) . find ( 'select.' + css + '[data-column="' + col + '"]' ) . append ( ff ) ;
2012-06-01 14:49:46 +00:00
}
2011-09-13 22:55:31 +00:00
}
2012-06-01 14:49:46 +00:00
}
}
2013-01-09 05:21:04 +00:00
// not really updating, but if the column has both the "filter-select" class & filter_functions set to true,
// it would append the same options twice.
buildDefault ( true ) ;
2012-06-01 14:49:46 +00:00
2013-04-12 16:26:16 +00:00
$t . find ( 'select.' + css ) . bind ( 'change search' , function ( e , filter ) {
checkFilters ( filter ) ;
2012-06-01 14:49:46 +00:00
} ) ;
2013-03-31 17:18:20 +00:00
if ( wo . filter _hideFilters ) {
2012-08-19 02:10:01 +00:00
$t
. find ( '.tablesorter-filter-row' )
. addClass ( 'hideme' )
. bind ( 'mouseenter mouseleave' , function ( e ) {
// save event object - http://bugs.jquery.com/ticket/12140
var all , evt = e ;
ft = $ ( this ) ;
clearTimeout ( st ) ;
st = setTimeout ( function ( ) {
if ( /enter|over/ . test ( evt . type ) ) {
ft . removeClass ( 'hideme' ) ;
} else {
// don't hide if input has focus
// $(':focus') needs jQuery 1.6+
if ( $ ( document . activeElement ) . closest ( 'tr' ) [ 0 ] !== ft [ 0 ] ) {
// get all filter values
2013-03-31 17:18:20 +00:00
all = $t . find ( '.' + wo . filter _cssFilter ) . map ( function ( ) {
2012-08-19 02:10:01 +00:00
return $ ( this ) . val ( ) || '' ;
} ) . get ( ) . join ( '' ) ;
// don't hide row if any filter has a value
if ( all === '' ) {
ft . addClass ( 'hideme' ) ;
}
}
}
} , 200 ) ;
} )
. find ( 'input, select' ) . bind ( 'focus blur' , function ( e ) {
ft2 = $ ( this ) . closest ( 'tr' ) ;
clearTimeout ( st ) ;
st = setTimeout ( function ( ) {
// don't hide row if any filter has a value
2013-03-31 17:18:20 +00:00
if ( $t . find ( '.' + wo . filter _cssFilter ) . map ( function ( ) { return $ ( this ) . val ( ) || '' ; } ) . get ( ) . join ( '' ) === '' ) {
2012-08-19 02:10:01 +00:00
ft2 [ e . type === 'focus' ? 'removeClass' : 'addClass' ] ( 'hideme' ) ;
}
} , 200 ) ;
} ) ;
}
2012-09-27 19:57:19 +00:00
// show processing icon
2012-08-19 02:10:01 +00:00
if ( c . showProcessing ) {
2013-02-17 19:21:05 +00:00
$t . bind ( 'filterStart.tsfilter filterEnd.tsfilter' , function ( e , v ) {
2012-08-19 02:10:01 +00:00
var fc = ( v ) ? $t . find ( '.' + c . cssHeader ) . filter ( '[data-column]' ) . filter ( function ( ) {
2012-09-27 19:57:19 +00:00
return v [ $ ( this ) . data ( 'column' ) ] !== '' ;
2012-08-19 02:10:01 +00:00
} ) : '' ;
ts . isProcessing ( $t [ 0 ] , e . type === 'filterStart' , v ? fc : '' ) ;
} ) ;
2011-09-13 22:55:31 +00:00
}
2012-08-19 02:10:01 +00:00
if ( c . debug ) {
ts . benchmark ( "Applying Filter widget" , time ) ;
}
2012-09-27 19:57:19 +00:00
// filter widget initialized
$t . trigger ( 'filterInit' ) ;
2013-02-17 19:21:05 +00:00
checkFilters ( ) ;
2012-08-19 02:10:01 +00:00
}
} ,
remove : function ( table , c , wo ) {
var k , $tb ,
2013-03-31 17:18:20 +00:00
$t = c . $table ,
2013-01-04 15:15:29 +00:00
b = c . $tbodies ;
2012-08-19 02:10:01 +00:00
$t
. removeClass ( 'hasFilters' )
// add .tsfilter namespace to all BUT search
2013-03-05 18:29:54 +00:00
. unbind ( 'addRows updateCell update updateComplete appendCache search filterStart filterEnd ' . split ( ' ' ) . join ( '.tsfilter ' ) )
2012-08-19 02:10:01 +00:00
. find ( '.tablesorter-filter-row' ) . remove ( ) ;
for ( k = 0 ; k < b . length ; k ++ ) {
2013-03-31 17:18:20 +00:00
$tb = ts . processTbody ( table , b . eq ( k ) , true ) ; // remove tbody
2012-08-19 02:10:01 +00:00
$tb . children ( ) . removeClass ( 'filtered' ) . show ( ) ;
2013-03-31 17:18:20 +00:00
ts . processTbody ( table , $tb , false ) ; // restore tbody
2011-09-13 22:55:31 +00:00
}
2013-02-17 19:21:05 +00:00
if ( wo . filterreset ) { $ ( wo . filter _reset ) . unbind ( 'click.tsfilter' ) ; }
2011-09-13 22:55:31 +00:00
}
} ) ;
2013-04-12 16:26:16 +00:00
ts . getFilters = function ( table ) {
var c = table ? $ ( table ) [ 0 ] . config : { } ;
return c && c . $filters ? c . $filters . find ( '.' + c . widgetOptions . filter _cssFilter ) . map ( function ( i , el ) {
return $ ( el ) . val ( ) ;
} ) . get ( ) || [ ] : false ;
} ;
ts . setFilters = function ( table , filter , apply ) {
var $t = $ ( table ) ,
c = $t . length ? $t [ 0 ] . config : { } ,
valid = c && c . $filters ? c . $filters . find ( '.' + c . widgetOptions . filter _cssFilter ) . each ( function ( i , el ) {
$ ( el ) . val ( filter [ i ] || '' ) ;
} ) || false : false ;
if ( valid && apply ) { $t . trigger ( 'search' ) ; }
return ! ! valid ;
} ;
2011-09-16 15:43:09 +00:00
2012-03-07 18:06:35 +00:00
// Widget: Sticky headers
2011-10-11 05:48:34 +00:00
// based on this awesome article:
2012-08-19 02:10:01 +00:00
// http://css-tricks.com/13465-persistent-headers/
// and https://github.com/jmosbech/StickyTableHeaders by Jonas Mosbech
2011-10-11 05:48:34 +00:00
// **************************
2013-03-31 17:18:20 +00:00
ts . addWidget ( {
2011-10-11 05:48:34 +00:00
id : "stickyHeaders" ,
2013-04-01 16:02:48 +00:00
priority : 60 ,
2013-03-31 17:18:20 +00:00
options : {
stickyHeaders : 'tablesorter-stickyHeader' ,
2013-04-04 21:33:28 +00:00
stickyHeaders _cloneId : '-sticky' // added to table ID, if it exists
2013-03-31 17:18:20 +00:00
} ,
format : function ( table , c , wo ) {
if ( c . $table . hasClass ( 'hasStickyHeaders' ) ) { return ; }
2013-04-01 16:02:48 +00:00
var $t = c . $table ,
2011-12-06 15:25:51 +00:00
win = $ ( window ) ,
2013-04-01 16:02:48 +00:00
header = $t . children ( 'thead:first' ) ,
2012-05-08 14:00:07 +00:00
hdrCells = header . children ( 'tr:not(.sticky-false)' ) . children ( ) ,
2012-04-21 14:40:43 +00:00
innr = '.tablesorter-header-inner' ,
2013-04-01 16:02:48 +00:00
tfoot = $t . find ( 'tfoot' ) ,
filterInputs = 'input, select' ,
t2 = wo . $sticky = $t . clone ( )
. addClass ( 'containsStickyHeaders' )
2011-10-11 05:48:34 +00:00
. css ( {
position : 'fixed' ,
2012-03-22 14:58:43 +00:00
margin : 0 ,
2011-12-13 04:25:26 +00:00
top : 0 ,
2011-10-26 06:50:02 +00:00
visibility : 'hidden' ,
2012-08-19 02:10:01 +00:00
zIndex : 1
2011-10-11 05:48:34 +00:00
} ) ,
2013-04-01 16:02:48 +00:00
stkyHdr = t2 . children ( 'thead:first' ) . addClass ( wo . stickyHeaders ) ,
stkyCells ,
2012-08-19 02:10:01 +00:00
laststate = '' ,
2012-10-13 18:58:16 +00:00
spacing = 0 ,
2013-04-01 16:02:48 +00:00
flag = false ,
2012-08-19 02:10:01 +00:00
resizeHdr = function ( ) {
2012-10-13 18:58:16 +00:00
var bwsr = navigator . userAgent ;
spacing = 0 ;
2012-08-19 02:10:01 +00:00
// yes, I dislike browser sniffing, but it really is needed here :(
// webkit automatically compensates for border spacing
2013-04-01 16:02:48 +00:00
if ( $t . css ( 'border-collapse' ) !== 'collapse' && ! /(webkit|msie)/i . test ( bwsr ) ) {
2012-11-14 22:44:36 +00:00
// Firefox & Opera use the border-spacing
2012-08-19 02:10:01 +00:00
// update border-spacing here because of demos that switch themes
2012-11-14 22:44:36 +00:00
spacing = parseInt ( hdrCells . eq ( 0 ) . css ( 'border-left-width' ) , 10 ) * 2 ;
2012-08-19 02:10:01 +00:00
}
2013-04-01 16:02:48 +00:00
t2 . css ( {
2012-08-19 02:10:01 +00:00
left : header . offset ( ) . left - win . scrollLeft ( ) - spacing ,
2013-04-01 16:02:48 +00:00
width : $t . width ( )
2012-08-19 02:10:01 +00:00
} ) ;
2013-04-01 16:02:48 +00:00
stkyCells . each ( function ( i ) {
2012-08-19 02:10:01 +00:00
var $h = hdrCells . eq ( i ) ;
2013-04-01 16:02:48 +00:00
$ ( this )
. css ( {
width : $h . width ( ) - spacing ,
height : $h . height ( )
} )
. find ( innr ) . width ( $h . find ( innr ) . width ( ) ) ;
2012-08-19 02:10:01 +00:00
} ) ;
} ;
2013-03-31 17:18:20 +00:00
// fix clone ID, if it exists - fixes #271
if ( t2 . attr ( 'id' ) ) { t2 [ 0 ] . id += wo . stickyHeaders _cloneId ; }
2012-11-14 22:44:36 +00:00
// clear out cloned table, except for sticky header
2013-04-01 16:02:48 +00:00
// include caption & filter row (fixes #126 & #249)
t2 . find ( 'thead:gt(0), tr.sticky-false, tbody, tfoot' ) . remove ( ) ;
// issue #172 - find td/th in sticky header
stkyCells = stkyHdr . children ( ) . children ( ) ;
2012-11-14 22:44:36 +00:00
t2 . css ( { height : 0 , width : 0 , padding : 0 , margin : 0 , border : 0 } ) ;
2012-08-19 02:10:01 +00:00
// remove resizable block
stkyCells . find ( '.tablesorter-resizer' ) . remove ( ) ;
2012-03-08 15:10:38 +00:00
// update sticky header class names to match real header after sorting
2013-04-01 16:02:48 +00:00
$t
. addClass ( 'hasStickyHeaders' )
2012-08-19 02:10:01 +00:00
. bind ( 'sortEnd.tsSticky' , function ( ) {
hdrCells . each ( function ( i ) {
var t = stkyCells . eq ( i ) ;
2013-04-02 18:05:55 +00:00
t
. attr ( 'class' , $ ( this ) . attr ( 'class' ) )
// remove processing icon
. removeClass ( c . cssProcessing ) ;
2012-08-19 02:10:01 +00:00
if ( c . cssIcon ) {
t
. find ( '.' + c . cssIcon )
. attr ( 'class' , $ ( this ) . find ( '.' + c . cssIcon ) . attr ( 'class' ) ) ;
}
2011-10-11 05:48:34 +00:00
} ) ;
2012-08-19 02:10:01 +00:00
} )
. bind ( 'pagerComplete.tsSticky' , function ( ) {
resizeHdr ( ) ;
2011-10-11 05:48:34 +00:00
} ) ;
2013-04-12 16:27:46 +00:00
// http://stackoverflow.com/questions/5312849/jquery-find-self;
hdrCells . find ( c . selectorSort ) . add ( c . $headers . filter ( c . selectorSort ) ) . each ( function ( i ) {
2012-05-28 15:01:40 +00:00
var t = $ ( this ) ;
2013-04-01 16:02:48 +00:00
stkyHdr . children ( 'tr.tablesorter-headerRow' ) . children ( ) . eq ( i )
2011-10-11 05:48:34 +00:00
// clicking on sticky will trigger sort
2012-05-28 15:01:40 +00:00
. bind ( 'mouseup' , function ( e ) {
t . trigger ( e , true ) ; // external mouseup flag (click timer is ignored)
2011-10-11 05:48:34 +00:00
} )
// prevent sticky header text selection
. bind ( 'mousedown' , function ( ) {
this . onselectstart = function ( ) { return false ; } ;
return false ;
2012-08-19 02:10:01 +00:00
} ) ;
2011-10-11 05:48:34 +00:00
} ) ;
2012-11-16 00:09:50 +00:00
// add stickyheaders AFTER the table. If the table is selected by ID, the original one (first) will be returned.
2013-04-01 16:02:48 +00:00
$t . after ( t2 ) ;
2011-10-11 05:48:34 +00:00
// make it sticky!
2011-10-15 14:58:44 +00:00
win
2013-04-01 16:02:48 +00:00
. bind ( 'scroll.tsSticky resize.tsSticky' , function ( e ) {
2013-04-12 22:13:22 +00:00
if ( ! $t . is ( ':visible' ) ) { return ; } // fixes #278
2013-04-01 16:02:48 +00:00
var pre = 'tablesorter-sticky-' ,
offset = $t . offset ( ) ,
2012-08-19 02:10:01 +00:00
sTop = win . scrollTop ( ) ,
2013-04-01 16:02:48 +00:00
tableHt = $t . height ( ) - ( t2 . height ( ) + ( tfoot . height ( ) || 0 ) ) ,
2012-08-19 02:10:01 +00:00
vis = ( sTop > offset . top ) && ( sTop < offset . top + tableHt ) ? 'visible' : 'hidden' ;
2013-04-01 16:02:48 +00:00
t2
. removeClass ( pre + 'visible ' + pre + 'hidden' )
. addClass ( pre + vis )
. css ( {
2012-10-13 18:58:16 +00:00
// adjust when scrolling horizontally - fixes issue #143
left : header . offset ( ) . left - win . scrollLeft ( ) - spacing ,
2012-08-19 02:10:01 +00:00
visibility : vis
2011-10-15 14:58:44 +00:00
} ) ;
2013-04-01 16:02:48 +00:00
if ( vis !== laststate || e . type === 'resize' ) {
2012-08-19 02:10:01 +00:00
// make sure the column widths match
resizeHdr ( ) ;
laststate = vis ;
}
} ) ;
2013-04-01 16:02:48 +00:00
// look for filter widget
$t . bind ( 'filterEnd' , function ( ) {
if ( flag ) { return ; }
stkyHdr . find ( '.tablesorter-filter-row' ) . children ( ) . each ( function ( i ) {
$ ( this ) . find ( filterInputs ) . val ( c . $filters . find ( filterInputs ) . eq ( i ) . val ( ) ) ;
} ) ;
} ) ;
stkyCells . find ( filterInputs ) . bind ( 'keyup search' , function ( e ) {
// ignore arrow and meta keys; allow backspace
if ( ( e . which < 32 && e . which !== 8 ) || ( e . which >= 37 && e . which <= 40 ) ) { return ; }
flag = true ;
var $f = $ ( this ) , col = $f . attr ( 'data-column' ) ;
c . $filters . find ( filterInputs ) . eq ( col )
. val ( $f . val ( ) )
. trigger ( 'search' ) ;
setTimeout ( function ( ) {
flag = false ;
} , wo . filter _searchDelay ) ;
} ) ;
2012-08-19 02:10:01 +00:00
} ,
remove : function ( table , c , wo ) {
2013-03-31 17:18:20 +00:00
c . $table
2012-08-19 02:10:01 +00:00
. removeClass ( 'hasStickyHeaders' )
. unbind ( 'sortEnd.tsSticky pagerComplete.tsSticky' )
2013-03-31 17:18:20 +00:00
. find ( '.' + wo . stickyHeaders ) . remove ( ) ;
2013-01-04 17:00:48 +00:00
if ( wo . $sticky ) { wo . $sticky . remove ( ) ; } // remove cloned thead
2012-08-19 02:10:01 +00:00
$ ( window ) . unbind ( 'scroll.tsSticky resize.tsSticky' ) ;
2011-10-11 05:48:34 +00:00
}
} ) ;
2011-10-26 06:50:02 +00:00
// Add Column resizing widget
2012-03-07 18:06:35 +00:00
// this widget saves the column widths if
// $.tablesorter.storage function is included
2011-10-26 06:50:02 +00:00
// **************************
2013-03-31 17:18:20 +00:00
ts . addWidget ( {
2011-10-26 06:50:02 +00:00
id : "resizable" ,
2013-03-31 17:18:20 +00:00
priority : 40 ,
options : {
resizable : true ,
resizable _addLastColumn : false
} ,
format : function ( table , c , wo ) {
if ( c . $table . hasClass ( 'hasResizable' ) ) { return ; }
c . $table . addClass ( 'hasResizable' ) ;
var $t , t , i , j , s = { } , $c , $cols , w , tw ,
$tbl = c . $table ,
2012-03-07 18:06:35 +00:00
position = 0 ,
$target = null ,
2012-08-19 02:10:01 +00:00
$next = null ,
2013-01-29 22:39:06 +00:00
fullWidth = Math . abs ( $tbl . parent ( ) . width ( ) - $tbl . width ( ) ) < 20 ,
2012-03-07 18:06:35 +00:00
stopResize = function ( ) {
2013-03-31 17:18:20 +00:00
if ( ts . storage && $target ) {
2013-01-29 22:39:06 +00:00
s [ $target . index ( ) ] = $target . width ( ) ;
s [ $next . index ( ) ] = $next . width ( ) ;
$target . width ( s [ $target . index ( ) ] ) ;
$next . width ( s [ $next . index ( ) ] ) ;
if ( wo . resizable !== false ) {
2013-03-31 17:18:20 +00:00
ts . storage ( table , 'tablesorter-resizable' , s ) ;
2013-01-29 22:39:06 +00:00
}
}
2012-03-07 18:06:35 +00:00
position = 0 ;
2012-08-19 02:10:01 +00:00
$target = $next = null ;
2012-03-07 18:06:35 +00:00
$ ( window ) . trigger ( 'resize' ) ; // will update stickyHeaders, just in case
} ;
2013-03-31 17:18:20 +00:00
s = ( ts . storage && wo . resizable !== false ) ? ts . storage ( table , 'tablesorter-resizable' ) : { } ;
2012-03-07 18:06:35 +00:00
// process only if table ID or url match
2012-08-19 02:10:01 +00:00
if ( s ) {
for ( j in s ) {
2013-03-31 17:18:20 +00:00
if ( ! isNaN ( j ) && j < c . $headers . length ) {
c . $headers . eq ( j ) . width ( s [ j ] ) ; // set saved resizable widths
2012-03-07 18:06:35 +00:00
}
2011-10-26 06:50:02 +00:00
}
2012-03-07 18:06:35 +00:00
}
2013-01-29 22:39:06 +00:00
$t = $tbl . children ( 'thead:first' ) . children ( 'tr' ) ;
// add resizable-false class name to headers (across rows as needed)
$t . children ( ) . each ( function ( ) {
t = $ ( this ) ;
i = t . attr ( 'data-column' ) ;
2013-03-31 17:18:20 +00:00
j = ts . getData ( t , c . headers [ i ] , 'resizable' ) === "false" ;
2013-01-29 22:39:06 +00:00
$t . children ( ) . filter ( '[data-column="' + i + '"]' ) . toggleClass ( 'resizable-false' , j ) ;
} ) ;
// add wrapper inside each cell to allow for positioning of the resizable target block
$t . each ( function ( ) {
$c = $ ( this ) . children ( ':not(.resizable-false)' ) ;
2012-09-29 13:20:33 +00:00
if ( ! $ ( this ) . find ( '.tablesorter-wrapper' ) . length ) {
2012-09-27 19:57:19 +00:00
// Firefox needs this inner div to position the resizer correctly
2012-09-29 13:42:58 +00:00
$c . wrapInner ( '<div class="tablesorter-wrapper" style="position:relative;height:100%;width:100%"></div>' ) ;
2012-09-29 13:20:33 +00:00
}
2013-03-30 15:14:42 +00:00
// don't include the last column of the row
if ( ! wo . resizable _addLastColumn ) { $c = $c . slice ( 0 , - 1 ) ; }
2012-08-19 02:10:01 +00:00
$cols = $cols ? $cols . add ( $c ) : $c ;
} ) ;
2012-03-07 18:06:35 +00:00
$cols
2012-08-19 02:10:01 +00:00
. each ( function ( ) {
2013-01-29 22:39:06 +00:00
$t = $ ( this ) ;
j = parseInt ( $t . css ( 'padding-right' ) , 10 ) + 10 ; // 8 is 1/2 of the 16px wide resizer grip
t = '<div class="tablesorter-resizer" style="cursor:w-resize;position:absolute;z-index:1;right:-' + j +
'px;top:0;height:100%;width:20px;"></div>' ;
$t
2012-09-27 19:57:19 +00:00
. find ( '.tablesorter-wrapper' )
2013-01-29 22:39:06 +00:00
. append ( t ) ;
2012-08-19 02:10:01 +00:00
} )
. bind ( 'mousemove.tsresize' , function ( e ) {
// ignore mousemove if no mousedown
if ( position === 0 || ! $target ) { return ; }
// resize columns
2013-01-29 22:39:06 +00:00
w = e . pageX - position ;
tw = $target . width ( ) ;
$target . width ( tw + w ) ;
if ( $target . width ( ) !== tw && fullWidth ) {
$next . width ( $next . width ( ) - w ) ;
}
2012-08-19 02:10:01 +00:00
position = e . pageX ;
} )
. bind ( 'mouseup.tsresize' , function ( ) {
stopResize ( ) ;
} )
2013-01-29 22:39:06 +00:00
. find ( '.tablesorter-resizer,.tablesorter-resizer-grip' )
2012-08-19 02:10:01 +00:00
. bind ( 'mousedown' , function ( e ) {
2012-09-29 13:20:33 +00:00
// save header cell and mouse position; closest() not supported by jQuery v1.2.6
2013-01-29 22:39:06 +00:00
$target = $ ( e . target ) . closest ( 'th' ) ;
t = c . $headers . filter ( '[data-column="' + $target . attr ( 'data-column' ) + '"]' ) ;
if ( t . length > 1 ) { $target = $target . add ( t ) ; }
// if table is not as wide as it's parent, then resize the table
$next = e . shiftKey ? $target . parent ( ) . find ( 'th:not(.resizable-false)' ) . filter ( ':last' ) : $target . nextAll ( ':not(.resizable-false)' ) . eq ( 0 ) ;
2012-08-19 02:10:01 +00:00
position = e . pageX ;
} ) ;
$tbl . find ( 'thead:first' )
2013-03-26 21:16:13 +00:00
. bind ( 'mouseup.tsresize mouseleave.tsresize' , function ( ) {
2012-03-07 18:06:35 +00:00
stopResize ( ) ;
2012-08-19 02:10:01 +00:00
} )
// right click to reset columns to default widths
. bind ( 'contextmenu.tsresize' , function ( ) {
2013-03-31 17:18:20 +00:00
ts . resizableReset ( table ) ;
2012-08-19 02:10:01 +00:00
// $.isEmptyObject() needs jQuery 1.4+
2012-09-29 13:20:33 +00:00
var rtn = $ . isEmptyObject ? $ . isEmptyObject ( s ) : s === { } ; // allow right click if already reset
2012-08-19 02:10:01 +00:00
s = { } ;
return rtn ;
2012-03-07 18:06:35 +00:00
} ) ;
2012-08-19 02:10:01 +00:00
} ,
2013-03-31 17:18:20 +00:00
remove : function ( table , c , wo ) {
c . $table
2012-08-19 02:10:01 +00:00
. removeClass ( 'hasResizable' )
. find ( 'thead' )
. unbind ( 'mouseup.tsresize mouseleave.tsresize contextmenu.tsresize' )
. find ( 'tr' ) . children ( )
. unbind ( 'mousemove.tsresize mouseup.tsresize' )
2013-01-29 22:39:06 +00:00
// don't remove "tablesorter-wrapper" as uitheme uses it too
. find ( '.tablesorter-resizer,.tablesorter-resizer-grip' ) . remove ( ) ;
2013-03-31 17:18:20 +00:00
ts . resizableReset ( table ) ;
2011-10-26 06:50:02 +00:00
}
} ) ;
2013-03-31 17:18:20 +00:00
ts . resizableReset = function ( table ) {
table . config . $headers . filter ( ':not(.resizable-false)' ) . css ( 'width' , '' ) ;
if ( ts . storage ) { ts . storage ( table , 'tablesorter-resizable' , { } ) ; }
2012-08-19 02:10:01 +00:00
} ;
2011-10-26 06:50:02 +00:00
2012-02-01 05:14:28 +00:00
// Save table sort widget
2012-03-07 18:06:35 +00:00
// this widget saves the last sort only if the
2012-08-19 02:10:01 +00:00
// saveSort widget option is true AND the
2012-03-07 18:06:35 +00:00
// $.tablesorter.storage function is included
2012-02-01 05:14:28 +00:00
// **************************
2013-03-31 17:18:20 +00:00
ts . addWidget ( {
2012-02-01 05:14:28 +00:00
id : 'saveSort' ,
2013-04-01 16:02:48 +00:00
priority : 20 ,
2013-03-31 17:18:20 +00:00
options : {
saveSort : true
} ,
2013-03-05 18:02:28 +00:00
init : function ( table , thisWidget , c , wo ) {
2012-02-02 00:02:19 +00:00
// run widget format before all other widgets are applied to the table
2013-03-05 18:02:28 +00:00
thisWidget . format ( table , c , wo , true ) ;
2012-02-02 00:02:19 +00:00
} ,
2013-03-05 18:02:28 +00:00
format : function ( table , c , wo , init ) {
2013-03-31 17:18:20 +00:00
var sl , time ,
$t = c . $table ,
2012-08-19 02:10:01 +00:00
ss = wo . saveSort !== false , // make saveSort active/inactive; default to true
sortList = { "sortList" : c . sortList } ;
if ( c . debug ) {
2012-02-01 05:14:28 +00:00
time = new Date ( ) ;
}
2013-02-24 06:17:42 +00:00
if ( $t . hasClass ( 'hasSaveSort' ) ) {
2013-03-31 17:18:20 +00:00
if ( ss && table . hasInitialized && ts . storage ) {
ts . storage ( table , 'tablesorter-savesort' , sortList ) ;
2012-08-19 02:10:01 +00:00
if ( c . debug ) {
2013-03-31 17:18:20 +00:00
ts . benchmark ( 'saveSort widget: Saving last sort: ' + c . sortList , time ) ;
2012-02-21 00:14:25 +00:00
}
2012-02-01 05:14:28 +00:00
}
} else {
// set table sort on initial run of the widget
2013-02-24 06:17:42 +00:00
$t . addClass ( 'hasSaveSort' ) ;
2012-03-07 18:06:35 +00:00
sortList = '' ;
2012-02-01 05:14:28 +00:00
// get data
2013-03-31 17:18:20 +00:00
if ( ts . storage ) {
sl = ts . storage ( table , 'tablesorter-savesort' ) ;
2012-03-07 18:06:35 +00:00
sortList = ( sl && sl . hasOwnProperty ( 'sortList' ) && $ . isArray ( sl . sortList ) ) ? sl . sortList : '' ;
2012-08-19 02:10:01 +00:00
if ( c . debug ) {
2013-03-31 17:18:20 +00:00
ts . benchmark ( 'saveSort: Last sort loaded: "' + sortList + '"' , time ) ;
2012-03-07 18:06:35 +00:00
}
2013-03-23 14:41:33 +00:00
$t . bind ( 'saveSortReset' , function ( e ) {
e . stopPropagation ( ) ;
2013-03-31 17:18:20 +00:00
ts . storage ( table , 'tablesorter-savesort' , '' ) ;
2013-02-24 06:17:42 +00:00
} ) ;
2012-02-01 05:14:28 +00:00
}
2012-02-02 00:02:19 +00:00
// init is true when widget init is run, this will run this widget before all other widgets have initialized
// this method allows using this widget in the original tablesorter plugin; but then it will run all widgets twice.
2012-08-19 02:10:01 +00:00
if ( init && sortList && sortList . length > 0 ) {
2012-02-02 00:02:19 +00:00
c . sortList = sortList ;
2012-08-19 02:10:01 +00:00
} else if ( table . hasInitialized && sortList && sortList . length > 0 ) {
2012-02-02 00:02:19 +00:00
// update sort change
2013-02-24 06:17:42 +00:00
$t . trigger ( 'sorton' , [ sortList ] ) ;
2012-02-01 05:14:28 +00:00
}
}
2012-08-19 02:10:01 +00:00
} ,
2013-03-26 21:16:13 +00:00
remove : function ( table ) {
2012-08-19 02:10:01 +00:00
// clear storage
2013-03-31 17:18:20 +00:00
if ( ts . storage ) { ts . storage ( table , 'tablesorter-savesort' , '' ) ; }
2012-02-01 05:14:28 +00:00
}
} ) ;
2012-05-30 04:55:28 +00:00
} ) ( jQuery ) ;