2012-12-26 08:06:26 +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 1 2 / 2 6 / 2 0 1 2
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" ;
$ . tablesorter = $ . tablesorter || { } ;
$ . tablesorter . themes = {
"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
* /
$ . tablesorter . 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 ) {
2012-04-02 19:19:17 +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
2012-04-02 19:19:17 +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
// **************************
2011-09-11 17:51:02 +00:00
$ . tablesorter . addWidget ( {
id : "uitheme" ,
2012-08-19 02:10:01 +00:00
format : function ( table ) {
2012-12-26 07:57:03 +00:00
var time , klass , $el , $tar ,
t = $ . tablesorter . themes ,
2012-08-19 02:10:01 +00:00
$t = $ ( table ) ,
c = table . config ,
wo = c . widgetOptions ,
2012-12-26 07:57:03 +00:00
theme = c . theme !== 'default' ? c . theme : wo . uitheme || 'jui' , // default uitheme is 'jui'
o = t [ t [ theme ] ? theme : t [ wo . uitheme ] ? wo . uitheme : 'jui' ] ,
2012-08-19 02:10:01 +00:00
$h = $ ( c . headerList ) ,
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)' )
2011-09-14 14:20:15 +00:00
. hover ( function ( ) {
2012-08-19 02:10:01 +00:00
$ ( this ) . addClass ( o . hover ) ;
2011-09-14 14:20:15 +00:00
} , function ( ) {
2012-08-19 02:10:01 +00:00
$ ( this ) . 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 ) {
$ . tablesorter . 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 ) {
var $t = $ ( table ) ,
2012-09-27 19:57:19 +00:00
theme = typeof wo . uitheme === 'object' ? 'jui' : wo . uitheme || 'jui' ,
2012-08-19 02:10:01 +00:00
o = typeof wo . uitheme === 'object' ? wo . uitheme : $ . tablesorter . themes [ $ . tablesorter . themes . hasOwnProperty ( theme ) ? theme : 'jui' ] ,
$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
. unbind ( 'mouseenter mouseleave' ) // remove hover
. 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
// **************************
2011-09-11 17:51:02 +00:00
$ . tablesorter . addWidget ( {
id : "columns" ,
2012-08-19 02:10:01 +00:00
format : function ( table ) {
2012-05-28 15:01:40 +00:00
var $tb , $tr , $td , $t , time , last , rmv , i , k , l ,
2012-08-19 02:10:01 +00:00
$tbl = $ ( table ) ,
2011-09-11 17:51:02 +00:00
c = table . config ,
2012-08-19 02:10:01 +00:00
wo = c . widgetOptions ,
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
css = [ "primary" , "secondary" , "tertiary" ] ; // default options
// 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-01-04 15:15:29 +00:00
$tb = $ . tablesorter . 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
} ) ;
2012-08-19 02:10:01 +00:00
$ . tablesorter . 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 ) {
2011-09-11 17:51:02 +00:00
$ . tablesorter . benchmark ( "Applying Columns widget" , time ) ;
}
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 ,
2012-08-19 02:10:01 +00:00
rmv = ( c . widgetOptions . columns || [ "primary" , "secondary" , "tertiary" ] ) . join ( ' ' ) ;
2013-01-04 17:00:48 +00:00
c . $headers . removeClass ( rmv ) ;
$ ( 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-01-04 15:15:29 +00:00
$tb = $ . tablesorter . 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 ) ;
} ) ;
$ . tablesorter . processTbody ( table , $tb , false ) ; // restore tbody
}
2011-09-11 17:51:02 +00:00
}
2011-09-13 22:55:31 +00:00
} ) ;
2012-08-19 02:10:01 +00:00
/ * W i d g e t : f i l t e r
widgetOptions :
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 _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
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
2012-12-17 17:33:46 +00:00
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.
2012-08-19 02:10:01 +00:00
* * * * * * * * * * * * * * * * * * * * * * * * * * /
2011-09-13 22:55:31 +00:00
$ . tablesorter . addWidget ( {
id : "filter" ,
2012-08-19 02:10:01 +00:00
format : function ( table ) {
if ( table . config . parsers && ! $ ( table ) . hasClass ( 'hasFilters' ) ) {
var i , j , k , l , val , ff , x , xi , st , sel , str ,
ft , ft2 , $th , rg , s , t , dis , col ,
last = '' , // save last filter search
ts = $ . tablesorter ,
2012-05-23 17:11:30 +00:00
c = table . config ,
2012-06-12 17:01:10 +00:00
$ths = $ ( c . headerList ) ,
2012-05-23 17:11:30 +00:00
wo = c . widgetOptions ,
css = wo . filter _cssFilter || 'tablesorter-filter' ,
$t = $ ( table ) . addClass ( 'hasFilters' ) ,
2013-01-04 15:15:29 +00:00
b = c . $tbodies ,
2012-05-23 17:11:30 +00:00
cols = c . parsers . length ,
2012-08-19 02:10:01 +00:00
reg = [ // regex used in filter "check" functions
/^\/((?:\\\/|[^\/])+)\/([mig]{0,3})?$/ , // 0 = regex to test for regex
new RegExp ( c . cssChildRow ) , // 1 = child row
/undefined|number/ , // 2 = check type
/(^[\"|\'|=])|([\"|\'|=]$)/ , // 3 = exact match
/[\"\'=]/g , // 4 = replace exact match flags
/[^\w,. \-()]/g , // 5 = replace non-digits (from digit & currency parser)
/[<>=]/g // 6 = replace operators
] ,
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-05-23 17:11:30 +00:00
time , timer ,
2012-08-19 02:10:01 +00:00
// dig fer gold
checkFilters = function ( filter ) {
var arry = $ . isArray ( filter ) ,
2012-09-27 19:57:19 +00:00
$inpts = $t . find ( 'thead' ) . eq ( 0 ) . children ( 'tr' ) . find ( 'select.' + css + ', input.' + css ) ,
v = ( arry ) ? filter : $inpts . map ( function ( ) {
2012-08-19 02:10:01 +00:00
return $ ( this ) . val ( ) || '' ;
} ) . get ( ) ,
cv = ( v || [ ] ) . join ( '' ) ; // combined filter values
2012-09-27 19:57:19 +00:00
// add filter array back into inputs
if ( arry ) {
$inpts . each ( function ( i , el ) {
$ ( el ) . val ( filter [ i ] || '' ) ;
} ) ;
}
2012-08-19 02:10:01 +00:00
if ( wo . filter _hideFilters === true ) {
// 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 ) {
2012-08-19 03:20:38 +00:00
var $tb , $tr , $td , cr , r , l , ff , time , arry ;
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-01-04 15:15:29 +00:00
$tb = $ . tablesorter . 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
2012-08-19 02:10:01 +00:00
if ( reg [ 1 ] . 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
t = ( cr . length && ( wo && wo . hasOwnProperty ( 'filter_childRows' ) &&
typeof wo . filter _childRows !== 'undefined' ? wo . filter _childRows : true ) ) ? 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 ( ) ) ;
}
xi = ! reg [ 2 ] . 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
2012-08-19 02:10:01 +00:00
} else if ( reg [ 0 ] . test ( val ) ) {
rg = reg [ 0 ] . 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 ;
}
2012-08-19 02:10:01 +00:00
// Look for quotes or equals to get an exact match
} else if ( reg [ 3 ] . test ( val ) && xi === val . replace ( reg [ 4 ] , '' ) ) {
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
rg = isNaN ( xi ) ? $ . tablesorter . formatFloat ( xi . replace ( reg [ 5 ] , '' ) , table ) : $ . tablesorter . formatFloat ( xi , table ) ;
2012-08-19 02:10:01 +00:00
s = $ . tablesorter . formatFloat ( val . replace ( reg [ 5 ] , '' ) . replace ( reg [ 6 ] , '' ) , table ) ;
if ( />/ . test ( val ) ) { ff = />=/ . test ( val ) ? rg >= s : rg > s ; }
if ( /</ . test ( val ) ) { ff = /<=/ . test ( val ) ? rg <= s : rg < s ; }
2012-06-01 14:49:46 +00:00
// Look for wild card: ? = single, or * = multiple
2012-08-19 02:10:01 +00:00
} else if ( /[\?|\*]/ . test ( val ) ) {
2012-06-01 14:49:46 +00:00
ff = new RegExp ( val . replace ( /\?/g , '\\S{1}' ) . replace ( /\*/g , '\\S*' ) ) . test ( xi ) ;
// 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
}
}
2012-08-19 02:10:01 +00:00
$ . tablesorter . 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
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 ) {
return $ . inArray ( v , arry ) === k ;
} ) ;
arry = ( ts . sortText ) ? arry . sort ( function ( a , b ) { return ts . sortText ( table , a , b , i ) ; } ) : arry . sort ( true ) ;
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 ++ ) {
2012-06-01 14:49:46 +00:00
o += '<option value="' + arry [ k ] + '">' + arry [ k ] + '</option>' ;
}
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' ) ;
// look for the filter-select class, but don't build it twice.
if ( t . hasClass ( 'filter-select' ) && ! t . hasClass ( 'filter-false' ) && ! ( wo . filter _functions && wo . filter _functions [ i ] === true ) ) {
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 ( ) ;
}
2012-06-20 12:15:46 +00:00
wo . filter _ignoreCase = wo . filter _ignoreCase !== false ; // set default filter_ignoreCase to true
2012-08-19 02:10:01 +00:00
wo . filter _useParsedData = wo . filter _useParsedData === true ; // default is false
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 ) {
2012-08-19 02:10:01 +00:00
t = '<tr class="tablesorter-filter-row">' ; // build filter row
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' ) ;
t += '<td>' ;
if ( sel ) {
t += '<select data-column="' + i + '" class="' + css ;
} else {
t += '<input type="search" placeholder="' + ( $th . attr ( 'data-placeholder' ) || "" ) + '" data-column="' + i + '" class="' + css ;
}
// use header option - headers: { 1: { filter: false } } OR add class="filter-false"
if ( ts . getData ) {
dis = ts . getData ( $th [ 0 ] , c . headers [ i ] , 'filter' ) === 'false' ;
// get data from jQuery data, metadata, headers option or header class name
t += dis ? ' disabled" disabled' : '"' ;
} else {
dis = ( c . headers [ i ] && c . headers [ i ] . hasOwnProperty ( 'filter' ) && c . headers [ i ] . filter === false ) || $th . hasClass ( 'filter-false' ) ;
// only class names and header options - keep this for compatibility with tablesorter v2.0.5
t += ( dis ) ? ' disabled" disabled' : '"' ;
}
t += ( sel ? '></select>' : '>' ) + '</td>' ;
2012-05-19 20:46:14 +00:00
}
2012-08-19 02:10:01 +00:00
$t . find ( 'thead' ) . eq ( 0 ) . append ( t += '</tr>' ) ;
2011-09-13 22:55:31 +00:00
}
2012-03-07 18:06:35 +00:00
$t
2012-08-19 02:10:01 +00:00
// add .tsfilter namespace to all BUT search
. bind ( 'addRows updateCell update appendCache search' . split ( ' ' ) . join ( '.tsfilter ' ) , function ( e , filter ) {
if ( e . type !== 'search' ) {
buildDefault ( true ) ;
}
checkFilters ( e . type === 'search' ? filter : '' ) ;
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
if ( ( e . which < 32 && e . which !== 8 ) || ( e . which >= 37 && e . which <= 40 ) ) { return ; }
// skip delay
2012-08-19 02:10:01 +00:00
if ( typeof filter !== 'undefined' ) {
checkFilters ( filter ) ;
return false ;
2012-06-01 14:49:46 +00:00
}
// delay filtering
clearTimeout ( timer ) ;
timer = setTimeout ( function ( ) {
2012-08-19 02:10:01 +00:00
checkFilters ( ) ;
2012-06-01 14:49:46 +00:00
} , wo . filter _searchDelay || 300 ) ;
} ) ;
2012-08-19 02:10:01 +00:00
// reset button/link
if ( wo . filter _reset && $ ( wo . filter _reset ) . length ) {
$ ( wo . filter _reset ) . bind ( 'click' , function ( ) {
$t . find ( '.' + css ) . val ( '' ) ;
checkFilters ( ) ;
return false ;
} ) ;
}
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
}
}
2012-06-20 14:40:32 +00:00
buildDefault ( ) ;
2012-06-01 14:49:46 +00:00
2012-09-27 19:57:19 +00:00
$t . find ( 'select.' + css ) . bind ( 'change search' , function ( ) {
2012-08-19 02:10:01 +00:00
checkFilters ( ) ;
2012-06-01 14:49:46 +00:00
} ) ;
2012-08-19 02:10:01 +00:00
if ( wo . filter _hideFilters === true ) {
$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
all = $t . find ( '.' + ( wo . filter _cssFilter || 'tablesorter-filter' ) ) . map ( function ( ) {
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
if ( $t . find ( '.' + ( wo . filter _cssFilter || 'tablesorter-filter' ) ) . map ( function ( ) { return $ ( this ) . val ( ) || '' ; } ) . get ( ) . join ( '' ) === '' ) {
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 ) {
$t . bind ( 'filterStart filterEnd' , function ( e , v ) {
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' ) ;
2012-08-19 02:10:01 +00:00
}
} ,
remove : function ( table , c , wo ) {
var k , $tb ,
$t = $ ( 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
. unbind ( 'addRows updateCell update appendCache search' . split ( ' ' ) . join ( '.tsfilter' ) )
. find ( '.tablesorter-filter-row' ) . remove ( ) ;
for ( k = 0 ; k < b . length ; k ++ ) {
2013-01-04 15:15:29 +00:00
$tb = $ . tablesorter . processTbody ( table , b . eq ( k ) , true ) ; // remove tbody
2012-08-19 02:10:01 +00:00
$tb . children ( ) . removeClass ( 'filtered' ) . show ( ) ;
$ . tablesorter . processTbody ( table , $tb , false ) ; // restore tbody
2011-09-13 22:55:31 +00:00
}
2012-09-27 19:57:19 +00:00
if ( wo . filterreset ) { $ ( wo . filter _reset ) . unbind ( 'click' ) ; }
2011-09-13 22:55:31 +00:00
}
} ) ;
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
// **************************
$ . tablesorter . addWidget ( {
id : "stickyHeaders" ,
2012-08-19 02:10:01 +00:00
format : function ( table ) {
2011-12-14 17:37:55 +00:00
if ( $ ( table ) . hasClass ( 'hasStickyHeaders' ) ) { return ; }
var $table = $ ( table ) . addClass ( 'hasStickyHeaders' ) ,
2012-08-19 02:10:01 +00:00
c = table . config ,
wo = c . widgetOptions ,
2011-12-06 15:25:51 +00:00
win = $ ( window ) ,
2012-09-27 19:57:19 +00:00
header = $ ( table ) . children ( 'thead:first' ) , //.add( $(table).find('caption') ),
2012-05-08 14:00:07 +00:00
hdrCells = header . children ( 'tr:not(.sticky-false)' ) . children ( ) ,
2012-03-27 01:49:48 +00:00
css = wo . stickyHeaders || 'tablesorter-stickyHeader' ,
2012-04-21 14:40:43 +00:00
innr = '.tablesorter-header-inner' ,
2012-11-14 22:44:36 +00:00
firstRow = hdrCells . eq ( 0 ) . parent ( ) ,
2012-05-03 14:46:31 +00:00
tfoot = $table . find ( 'tfoot' ) ,
2013-01-04 17:00:48 +00:00
t2 = wo . $sticky = $table . clone ( ) , // clone table, but don't remove id... the table might be styled by css
2012-08-19 02:10:01 +00:00
// clone the entire thead - seems to work in IE8+
2012-11-14 22:44:36 +00:00
stkyHdr = t2 . children ( 'thead:first' )
2012-03-07 18:06:35 +00:00
. addClass ( css )
2011-10-11 05:48:34 +00:00
. css ( {
2012-03-27 01:49:48 +00:00
width : header . outerWidth ( true ) ,
2011-10-11 05:48:34 +00:00
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
} ) ,
2012-11-14 22:44:36 +00:00
stkyCells = stkyHdr . children ( 'tr:not(.sticky-false)' ) . children ( ) , // issue #172
2012-08-19 02:10:01 +00:00
laststate = '' ,
2012-10-13 18:58:16 +00:00
spacing = 0 ,
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
2012-11-14 22:44:36 +00:00
if ( $table . css ( 'border-collapse' ) !== 'collapse' && ! /(webkit|msie)/i . test ( bwsr ) ) {
// 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
}
stkyHdr . css ( {
left : header . offset ( ) . left - win . scrollLeft ( ) - spacing ,
width : header . outerWidth ( )
} ) ;
stkyCells
. each ( function ( i ) {
var $h = hdrCells . eq ( i ) ;
$ ( this ) . css ( {
2012-11-14 22:44:36 +00:00
width : $h . width ( ) - spacing ,
2012-08-19 02:10:01 +00:00
height : $h . height ( )
} ) ;
} )
. find ( innr ) . each ( function ( i ) {
var hi = hdrCells . eq ( i ) . find ( innr ) ,
w = hi . width ( ) ; // - ( parseInt(hi.css('padding-left'), 10) + parseInt(hi.css('padding-right'), 10) );
$ ( this ) . width ( w ) ;
} ) ;
} ;
2012-11-14 22:44:36 +00:00
// clear out cloned table, except for sticky header
t2 . find ( 'thead:gt(0),tr.sticky-false,tbody,tfoot,caption' ) . remove ( ) ;
t2 . css ( { height : 0 , width : 0 , padding : 0 , margin : 0 , border : 0 } ) ;
2012-08-19 02:10:01 +00:00
// remove rows you don't want to be sticky
stkyHdr . find ( 'tr.sticky-false' ) . remove ( ) ;
// 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
2012-08-19 02:10:01 +00:00
$table
. bind ( 'sortEnd.tsSticky' , function ( ) {
hdrCells . each ( function ( i ) {
var t = stkyCells . eq ( i ) ;
t . attr ( 'class' , $ ( this ) . attr ( 'class' ) ) ;
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
} ) ;
// set sticky header cell width and link clicks to real header
2012-09-27 19:57:19 +00:00
hdrCells . find ( '*' ) . andSelf ( ) . filter ( c . selectorSort ) . each ( function ( i ) {
2012-05-28 15:01:40 +00:00
var t = $ ( this ) ;
stkyCells . 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.
$table . after ( t2 ) ;
2011-10-11 05:48:34 +00:00
// make it sticky!
2011-10-15 14:58:44 +00:00
win
2012-08-19 02:10:01 +00:00
. bind ( 'scroll.tsSticky' , function ( ) {
2012-11-14 22:44:36 +00:00
var offset = firstRow . offset ( ) ,
2012-08-19 02:10:01 +00:00
sTop = win . scrollTop ( ) ,
2012-11-14 22:44:36 +00:00
tableHt = $table . height ( ) - ( stkyHdr . height ( ) + ( tfoot . height ( ) || 0 ) ) ,
2012-08-19 02:10:01 +00:00
vis = ( sTop > offset . top ) && ( sTop < offset . top + tableHt ) ? 'visible' : 'hidden' ;
2012-10-13 18:58:16 +00:00
stkyHdr
. css ( {
// 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
} ) ;
2012-08-19 02:10:01 +00:00
if ( vis !== laststate ) {
// make sure the column widths match
resizeHdr ( ) ;
laststate = vis ;
}
} )
. bind ( 'resize.tsSticky' , function ( ) {
resizeHdr ( ) ;
} ) ;
} ,
remove : function ( table , c , wo ) {
var $t = $ ( table ) ,
css = wo . stickyHeaders || 'tablesorter-stickyHeader' ;
$t
. removeClass ( 'hasStickyHeaders' )
. unbind ( 'sortEnd.tsSticky pagerComplete.tsSticky' )
. find ( '.' + css ) . 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
// **************************
$ . tablesorter . addWidget ( {
id : "resizable" ,
2012-08-19 02:10:01 +00:00
format : function ( table ) {
2012-03-07 18:06:35 +00:00
if ( $ ( table ) . hasClass ( 'hasResizable' ) ) { return ; }
$ ( table ) . addClass ( 'hasResizable' ) ;
2012-08-19 02:10:01 +00:00
var t , j , s , $c , $cols ,
$tbl = $ ( table ) ,
c = table . config ,
wo = c . widgetOptions ,
2012-03-07 18:06:35 +00:00
position = 0 ,
$target = null ,
2012-08-19 02:10:01 +00:00
$next = null ,
2012-03-07 18:06:35 +00:00
stopResize = function ( ) {
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
} ;
2012-09-27 19:57:19 +00:00
s = ( $ . tablesorter . storage && wo . resizable !== false ) ? $ . tablesorter . 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 ) {
if ( ! isNaN ( j ) && j < c . headerList . length ) {
2012-03-07 18:06:35 +00:00
$ ( c . headerList [ j ] ) . width ( s [ j ] ) ; // set saved resizable widths
}
2011-10-26 06:50:02 +00:00
}
2012-03-07 18:06:35 +00:00
}
2012-08-19 02:10:01 +00:00
$tbl . children ( 'thead:first' ) . find ( 'tr' ) . each ( function ( ) {
2012-09-29 13:20:33 +00:00
$c = $ ( this ) . children ( ) ;
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
}
$c = $c . slice ( 0 , - 1 ) ; // don't include the last column of the row
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 ( ) {
t = $ ( this ) ;
j = parseInt ( t . css ( 'padding-right' ) , 10 ) + 8 ; // 8 is 1/2 of the 16px wide resizer
t
2012-09-27 19:57:19 +00:00
. find ( '.tablesorter-wrapper' )
. append ( '<div class="tablesorter-resizer" style="cursor:w-resize;position:absolute;height:100%;width:16px;right:-' + j + 'px;top:0;z-index:1;"></div>' ) ;
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
var w = e . pageX - position ;
$target . width ( $target . width ( ) + w ) ;
$next . width ( $next . width ( ) - w ) ;
position = e . pageX ;
} )
. bind ( 'mouseup.tsresize' , function ( ) {
if ( $ . tablesorter . storage && $target ) {
s [ $target . index ( ) ] = $target . width ( ) ;
s [ $next . index ( ) ] = $next . width ( ) ;
2012-09-27 19:57:19 +00:00
if ( wo . resizable !== false ) {
2012-03-07 18:06:35 +00:00
$ . tablesorter . storage ( table , 'tablesorter-resizable' , s ) ;
}
2012-08-19 02:10:01 +00:00
}
stopResize ( ) ;
} )
. find ( '.tablesorter-resizer' )
. 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
$target = $ ( e . target ) . parents ( 'th:last' ) ;
2012-08-19 02:10:01 +00:00
$next = $target . next ( ) ;
position = e . pageX ;
} ) ;
$tbl . find ( 'thead:first' )
. bind ( 'mouseup.tsresize mouseleave.tsresize' , function ( e ) {
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 ( ) {
$ . tablesorter . resizableReset ( table ) ;
// $.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
} ,
remove : function ( table , c , wo ) {
$ ( table )
. removeClass ( 'hasResizable' )
. find ( 'thead' )
. unbind ( 'mouseup.tsresize mouseleave.tsresize contextmenu.tsresize' )
. find ( 'tr' ) . children ( )
. unbind ( 'mousemove.tsresize mouseup.tsresize' )
. find ( '.tablesorter-wrapper' ) . each ( function ( ) {
$ ( this ) . find ( '.tablesorter-resizer' ) . remove ( ) ;
$ ( this ) . replaceWith ( $ ( this ) . contents ( ) ) ;
} ) ;
2012-09-27 19:57:19 +00:00
$ . tablesorter . resizableReset ( table ) ;
2011-10-26 06:50:02 +00:00
}
} ) ;
2012-08-19 02:10:01 +00:00
$ . tablesorter . resizableReset = function ( table ) {
$ ( table . config . headerList ) . width ( 'auto' ) ;
$ . tablesorter . storage ( table , 'tablesorter-resizable' , { } ) ;
} ;
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
// **************************
$ . tablesorter . addWidget ( {
id : 'saveSort' ,
2012-08-19 02:10:01 +00:00
init : function ( table , thisWidget ) {
2012-02-02 00:02:19 +00:00
// run widget format before all other widgets are applied to the table
thisWidget . format ( table , true ) ;
} ,
2012-08-19 02:10:01 +00:00
format : function ( table , init ) {
var sl , time , c = table . config ,
wo = c . widgetOptions ,
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 ( ) ;
}
2012-08-19 02:10:01 +00:00
if ( $ ( table ) . hasClass ( 'hasSaveSort' ) ) {
if ( ss && table . hasInitialized && $ . tablesorter . storage ) {
2012-03-07 18:06:35 +00:00
$ . tablesorter . storage ( table , 'tablesorter-savesort' , sortList ) ;
2012-08-19 02:10:01 +00:00
if ( c . debug ) {
2012-03-07 18:06:35 +00:00
$ . tablesorter . 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
2012-03-07 18:06:35 +00:00
$ ( table ) . addClass ( 'hasSaveSort' ) ;
sortList = '' ;
2012-02-01 05:14:28 +00:00
// get data
2012-08-19 02:10:01 +00:00
if ( $ . tablesorter . storage ) {
2012-03-07 18:06:35 +00:00
sl = $ . tablesorter . storage ( table , 'tablesorter-savesort' ) ;
sortList = ( sl && sl . hasOwnProperty ( 'sortList' ) && $ . isArray ( sl . sortList ) ) ? sl . sortList : '' ;
2012-08-19 02:10:01 +00:00
if ( c . debug ) {
$ . tablesorter . benchmark ( 'saveSort: Last sort loaded: "' + sortList + '"' , time ) ;
2012-03-07 18:06:35 +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
$ ( table ) . trigger ( 'sorton' , [ sortList ] ) ;
2012-02-01 05:14:28 +00:00
}
}
2012-08-19 02:10:01 +00:00
} ,
remove : function ( table , c , wo ) {
// clear storage
$ . tablesorter . storage ( table , 'tablesorter-savesort' , '' ) ;
2012-02-01 05:14:28 +00:00
}
} ) ;
2012-05-30 04:55:28 +00:00
} ) ( jQuery ) ;