This commit is contained in:
2024-04-27 09:23:34 +02:00
commit 11e713ca6f
11884 changed files with 3263371 additions and 0 deletions

View File

@@ -0,0 +1,441 @@
/*! Build Table widget * by Rob Garrison */
/*jshint browser:true, jquery:true, unused:false */
/*global jQuery: false */
;(function($){
"use strict";
var ts = $.tablesorter = $.tablesorter || {},
// build a table from data (requires existing <table> tag)
// data.header contains an array of header titles
// data.rows contains an array of rows which contains an array of cells
bt = ts.build.table = function(tar, c){
// add table if one doesn't exist
var $tbl = tar.tagName === 'TABLE' ? $(tar) : $('<table>').appendTo(tar),
table = $tbl[0],
wo = c.widgetOptions = $.extend( true, {}, bt.defaults, c.widgetOptions ),
p = wo.build_processing,
typ = wo.build_type,
d = wo.build_source || c.data,
// determine type: html, json, array, csv, object
runType = function(d){
var t = $.type(d),
jq = d instanceof jQuery;
// run any processing if set
if ( typeof p === 'function' ) { d = p(d, wo); }
// store processed data in table.config.data
c.data = d;
// String (html or unprocessed json) or jQuery object
if ( jq || t === 'string' ) {
// look for </tr> closing tag, then we have an HTML string
if ( jq || /<\s*\/tr\s*>/.test(d) ) {
return bt.html( table, d, wo );
}
try {
d = $.parseJSON(d);
if (d) {
// valid JSON!
return bt.object( table, d, wo );
}
} catch(ignore) {}
// fall through in case it's a csv string
}
// Array
if (t === 'array' || t === 'string' || typ === 'array' || typ === 'csv') {
// build table using an array (csv & array combined script)
return bt.csv( table, d, wo );
}
// if we got here, it's an object, or nothing
return bt.object( table, d, wo );
};
// store config
table.config = c;
// even if wo.build_type is undefined, we can try to figure out the type
if ( !ts.build.table.hasOwnProperty(typ) && typ !== '' ) {
if (c.debug) { ts.log('aborting build table widget, incorrect build type'); }
return false;
}
if ( d instanceof jQuery ) {
// get data from within a jQuery object (csv)
runType( $.trim( d.html() ) );
} else if ( d && ( d.hasOwnProperty('url') || typ === 'json' ) ) {
// load data via ajax
$.ajax( wo.build_source )
.done(function(data) {
runType(data);
})
.fail(function( jqXHR, textStatus, errorThrown) {
if (c.debug) { ts.log('aborting build table widget, failed ajax load'); }
$tbl.html('<tr><td class="error">' + jqXHR.status + ' ' + textStatus + '</td></tr>');
});
} else {
runType(d);
}
};
bt.defaults = {
// *** build widget core ***
build_type : '', // array, csv, object, json, html
build_source : '', // array, object, jQuery Object or ajaxObject { url: '', dataType: 'json' },
build_processing : null, // function that returns a useable build_type (e.g. string to array)
build_complete : 'tablesorter-build-complete', // triggered event when build completes
// *** CSV & Array ***
build_headers : {
rows : 1, // Number of header rows from the csv
classes : [], // Header classes to apply to cells
text : [], // Header cell text
widths : [] // set header cell widths (set in colgroup)
},
build_footers : {
rows : 1, // Number of header rows from the csv
classes : [], // Footer classes to apply to cells
text : [] // Footer cell text
},
build_numbers : {
addColumn : false, // include row numbering column?
sortable : false // make column sortable?
},
// *** CSV only options ***
build_csvStartLine : 0, // line within the csv to start adding to table
build_csvSeparator : ",", // csv separator
// *** build object options ***
build_objectRowKey : 'rows', // object key containing table rows
build_objectCellKey : 'cells', // object key containing table cells (within the rows object)
build_objectHeaderKey : 'headers', // object key containing table headers
build_objectFooterKey : 'footers' // object key containing table footers
};
bt.create = {
colgroup : function(widths) {
var t = '';
// add colgroup if widths set
if (widths && widths.length) {
t += '<colgroup>';
$.each(widths, function(i, w){
t += '<col' + ( w ? ' style="width:' + w + '"' : '' ) + '>';
});
t += '</colgroup>';
}
return t;
},
// d = cell data; typ = 'th' or 'td'; first = save widths from first header row only
cell : function(d, wo, typ, col, first){
var j, $td,
$col = first ? $('<col>') : '',
cls = wo.build_headers.classes,
cw = wo.build_headers.widths;
// d is just an array
if (/string|number/.test(typeof d)) {
// add classes from options, but not text
$td = $('<' + typ + (cls && cls[col] ? ' class="' + cls[col] + '"' : '') + '>' + d + '</' + typ + '>');
// get widths from options (only from first row)
if (first && cw && cw[col]) {
$col.width(cw[col] || '');
}
} else {
// assume we have an object
$td = $('<' + typ + '>');
for (j in d) {
if (d.hasOwnProperty(j)){
if (j === 'text' || j === 'html') {
$td[j]( d[j] );
} else if (first && j === 'width') {
// set column width, but only from first row
$col.width(d[j] || '');
} else {
$td.attr(j, d[j]);
}
}
}
}
return [ $td, $col ];
},
// h1 = header text from data
header : function(h1, wo){
var h2 = wo.build_headers.text,
cls = wo.build_headers.classes,
t = '<tr>' + (wo.build_numbers.addColumn ? '<th' + (wo.build_numbers.sortable ? '' :
' class="sorter-false"') + '>' + wo.build_numbers.addColumn + '</th>' : '');
$.each(h1, function(i, h) {
t += '<th' + (cls && cls[i] ? ' class="' + cls[i] + '"' : '') + '>' +
(h2 && h2[i] ? h2[i] : h) + '</th>';
});
return t + '</tr>';
},
rows : function(items, txt, c, wo, num, ftr){
var h = (ftr ? 'th' : 'td'),
t = '<tr>' + (wo.build_numbers.addColumn ? '<' + h + '>' + (ftr ? '' : num) + '</' + h + '>' : '');
$.each(items, function(i, item) {
t += '<' + (ftr ? h + (c && c[i] ? ' class="' + c[i] + '"' : '') : h) + '>' +
(ftr && txt && txt.length && txt[i] ? txt[i] : item) + '</' + h + '>';
});
return t + '</tr>';
}
};
bt.complete = function(table, wo){
$(table).trigger(wo.build_complete);
ts.setup(table, table.config);
};
/* ==== Array example ====
[
[ "header1", "header2", ... "headerN" ],
[ "row1cell1", "row1cell2", ... "row1cellN" ],
[ "row2cell1", "row2cell2", ... "row2cellN" ],
...
[ "rowNcell1", "rowNcell2", ... "rowNcellN" ]
]
*/
bt.array = function(table, data, wo) {
return bt.csv(table, data, wo);
};
/* ==== CSV example ====
ID, Name, Age, Date
A42b, Parker, 28, "Jul 6, 2006 8:14 AM"
A255, Hood, 33, "Dec 10, 2002 5:14 AM"
A33, Kent, 18, "Jan 12, 2003 11:14 AM"
A1, Franklin, 45, "Jan 18, 2001 9:12 AM"
A102, Evans, 22, "Jan 18, 2007 9:12 AM"
A42a, Everet, 22, "Jan 18, 2007 9:12 AM"
ID, Name, Age, Date
*/
// Adapted & modified from csvToTable.js by Steve Sobel
// MIT license: https://code.google.com/p/jquerycsvtotable/
bt.csv = function(table, data, wo) {
var c, h,
csv = wo.build_type === 'csv' || typeof data === 'string',
$t = $(table),
lines = csv ? data.replace('\r','').split('\n') : data,
len = lines.length,
printedLines = 0,
infooter = false,
r = wo.build_headers.rows + (csv ? wo.build_csvStartLine : 0),
f = wo.build_footers.rows,
headerCount = 0,
error = '',
items,
tableHTML = bt.create.colgroup( wo.build_headers.widths ) + '<thead>';
$.each(lines, function(n, line) {
if ( n >= len - f ) { infooter = true; }
// build header
if ( (csv ? n >= wo.build_csvStartLine : true) && ( n < r ) ) {
h = csv ? bt.splitCSV( line, wo.build_csvSeparator ) : line;
headerCount = h.length;
tableHTML += bt.create.header(h, wo);
} else if ( n >= r ) {
// build tbody & tfoot rows
if (n === r) {
tableHTML += '</thead><tbody>';
}
items = csv ? bt.splitCSV( line, wo.build_csvSeparator ) : line;
if (infooter && f > 0) {
tableHTML += (n === len - f ? '</tbody><tfoot>' : '') +
(n === len ? '</tfoot>' : '');
}
if (items.length > 1) {
printedLines++;
if ( items.length !== headerCount ) {
error += 'error on line ' + n + ': Item count (' + items.length +
') does not match header count (' + headerCount + ') \n';
}
c = infooter ? wo.build_footers.classes : '';
tableHTML += bt.create.rows(items, wo.build_footers.text, c, wo, printedLines, infooter);
}
}
});
tableHTML += (f > 0 ? '' : '</tbody>');
if (error) {
$t.html(error);
} else {
$t.html(tableHTML);
bt.complete(table, wo);
}
};
// CSV Parser by Brian Huisman (http://www.greywyvern.com/?post=258)
bt.splitCSV = function(str, sep) {
var x, tl,
thisCSV = $.trim(str).split(sep = sep || ",");
for ( x = thisCSV.length - 1; x >= 0; x-- ) {
if ( thisCSV[x].replace(/\"\s+$/, '"').charAt(thisCSV[x].length - 1) === '"' ) {
if ( (tl = thisCSV[x].replace(/^\s+\"/, '"')).length > 1 && tl.charAt(0) === '"' ) {
thisCSV[x] = thisCSV[x].replace(/^\s*"|"\s*$/g, '').replace(/""/g, '"');
} else if (x) {
thisCSV.splice(x - 1, 2, [thisCSV[x - 1], thisCSV[x]].join(sep));
} else {
thisCSV = thisCSV.shift().split(sep).concat(thisCSV);
}
} else {
thisCSV[x].replace(/""/g, '"');
}
}
return thisCSV;
};
// data may be a jQuery object after processing
bt.html = function(table, data, wo) {
var $t = $(table);
if ( data instanceof jQuery ) {
$t.empty().append(data);
} else {
$t.html(data);
}
bt.complete(table, wo);
};
/* ==== Object example ====
data : {
headers : [
[
{ text: 'First Name', class: 'fname', width: '20%' }, // row 1 cell 1
'Last Name',
{ text: 'Age', class: 'age', 'data-sorter' : false },
'Total',
{ text: 'Discount', class : 'sorter-false' },
{ text: 'Date', class : 'date' } // row 1 cell 6
]
],
footers : 'clone', // clone headers or assign array like headers
rows : [
// TBODY 1
[ 'Peter', 'Parker', 28, '$9.99', '20%', 'Jul 6, 2006 8:14 AM' ], // row 1
[ 'John', 'Hood', 33, '$19.99', '25%', 'Dec 10, 2002 5:14 AM' ], // row 2
[ 'Clark', 'Kent', 18, '$15.89', '44%', 'Jan 12, 2003 11:14 AM' ], // row 3
// TBODY 2
{ newTbody: true, class: 'tablesorter-infoOnly' },
{ cells : [ { text: 'Info Row', colSpan: 6 } ] }, // row 4
// TBODY 3
{ newTbody: true },
[ 'Bruce', 'Evans', 22, '$13.19', '11%', 'Jan 18, 2007 9:12 AM' ], // row 5
[ 'Brice', 'Almighty', 45, '$153.19', '44%', 'Jan 18, 2001 9:12 AM' ], // row 6
{ class: 'specialRow', // row 7
cells: [
{ text: 'Fred', class: 'fname' },
{ text: 'Smith', class: 'lname' },
{ text: 18, class: 'age', 'data-info': 'fake ID!, he is really 16' },
{ text: '$22.44', class: 'total' },
{ text: '8%', class: 'discount' },
{ text: 'Aug 20, 2012 10:15 AM', class: 'date' }
],
'data-info' : 'This row likes turtles'
}
]
}
*/
bt.object = function(table, data, wo) {
// "rows"
var j, l, t, $c, $t, $tb, $tr,
c = table.config,
kh = wo.build_objectHeaderKey,
kr = wo.build_objectRowKey,
h = data.hasOwnProperty(kh) && !$.isEmptyObject(data.kh) ? data.kh : data.hasOwnProperty('headers') ? data.headers : false,
r = data.hasOwnProperty(kr) && !$.isEmptyObject(data.kr) ? data.kr : data.hasOwnProperty('rows') ? data.rows : false;
if (!h || !r || h.length === 0 || r.length === 0) {
if (c.debug) { ts.log('aborting build table widget, missing data for object build'); }
return false;
}
$c = $('<colgroup>');
$t = $('<table><thead/></table>');
// Build thead
// h = [ ['headerRow1Cell1', 'headerRow1Cell2', ... 'headerRow1CellN' ], ['headerRow2Cell1', ... ] ]
// or h = [ [ { text: 'firstCell', class: 'fc', width: '20%' }, ..., { text: 'last Cell' } ], [ /* second row */ ] ]
$.each(h, function(i, d){
$tr = $('<tr>').appendTo( $t.find('thead') );
l = d.length; // header row
for ( j = 0; j < l; j++ ) {
// cell(cellData, widgetOptions, 'th', first row)
t = bt.create.cell(d[j], wo, 'th', j, i === 0);
if (t[0] && t[0].length) { t[0].appendTo( $tr ); } // add cell
if (i === 0 && t[1]) { t[1].appendTo( $c ); } // add col to colgroup
}
});
if ($c.find('col[style]').length) {
// add colgroup if it contains col elements
$t.prepend( $c );
}
$tb = $('<tbody>');
// Build tbody
$.each(r, function(i, d){
t = $.type(d) === 'object';
// add new tbody
if (t && d.newTbody) {
$tb = $('<tbody>').appendTo( $t );
for (j in d) {
if (d.hasOwnProperty(j) && j !== 'newTbody'){
$tb.attr(j, d[j]);
}
}
} else {
if (i === 0) {
// add tbody, if the first item in the object isn't a call for a new tbody
$tb.appendTo( $t );
}
$tr = $('<tr>').appendTo( $tb );
if (t) {
// row defined by object
for (j in d) {
if (d.hasOwnProperty(j) && j !== wo.build_objectCellKey){
$tr.attr(j, d[j]);
}
}
if (d.hasOwnProperty(wo.build_objectCellKey)) {
// cells contains each cell info
d = d.cells;
}
}
l = d.length;
for ( j = 0; j < l; j++ ) {
// cell(cellData, widgetOptions, 'td')
$c = bt.create.cell(d[j], wo, 'td', j);
if ($c[0] && $c[0].length) { $c[0].appendTo( $tr ); } // add cell
}
}
});
// add footer
if (data.hasOwnProperty(wo.build_objectFooterKey)) {
t = data[wo.build_objectFooterKey];
if (t === 'clone') {
$c = $t.find('thead').html();
$t.append('<tfoot>' + $c + '</tfoot>');
} else {
$c = $('<tfoot>').appendTo( $t );
$.each(t, function(i, d) {
$tr = $('<tr>').appendTo( $c );
l = d.length; // footer cells
for ( j = 0; j < l; j++ ) {
// cell(cellData, widgetOptions, 'th')
$tb = bt.create.cell(d[j], wo, 'th', j);
if ($tb[0] && $tb[0].length) { $tb[0].appendTo( $tr ); } // add cell
}
});
}
}
$(table).html( $t.html() );
bt.complete(table, wo);
};
bt.ajax = bt.json = function(table, data, wo) {
return bt.object(table, data, wo);
};
})(jQuery);

View File

@@ -0,0 +1,77 @@
/*! tablesorter Editable Content widget - updated 4/12/2013
* Requires tablesorter v2.8+ and jQuery 1.7+
* by Rob Garrison
*/
/*jshint browser:true, jquery:true, unused:false */
/*global jQuery: false */
;(function($){
"use strict";
$.tablesorter.widget.add({
id: 'editable',
options : {
editable_columns : [],
editable_enterToAccept : true,
editable_autoResort : false,
editable_noEdit : 'no-edit',
editable_editComplete : 'editComplete'
},
init: function(table, thisWidget, c, wo){
if (!wo.editable_columns.length) { return; }
var $t, cols = [];
$.each(wo.editable_columns, function(i, col){
cols.push('td:nth-child(' + (col + 1) + ')');
});
// IE does not allow making TR/TH/TD cells directly editable (issue #404)
// so add a div or span inside ( it's faster than using wrapInner() )
c.$tbodies.find( cols.join(',') ).not('.' + wo.editable_noEdit).each(function(){
// test for children, if they exist, then make the children editable
$t = $(this);
( $t.children().length ? $t.children() : $t ).prop('contenteditable', true);
});
c.$tbodies
.on('mouseleave.tseditable', function(){
if (c.$table.data('contentFocused')) {
$(':focus').trigger('blur');
}
})
.on('focus.tseditable', '[contenteditable]', function(){
c.$table.data('contentFocused', true);
var $this = $(this), v = $this.html();
if (wo.editable_enterToAccept) {
// prevent enter from adding into the content
$this.on('keydown.tseditable', function(e){
if (e.which === 13) {
e.preventDefault();
}
});
}
$this.data({ before : v, original: v });
})
.on('blur focusout keyup '.split(' ').join('.tseditable '), '[contenteditable]', function(e){
if (!c.$table.data('contentFocused')) { return; }
var $this = $(e.target), t;
if (e.which === 27) {
// user cancelled
$this.html( $this.data('original') ).trigger('blur.tseditable');
c.$table.data('contentFocused', false);
return false;
}
t = e.type !== 'keyup' || (wo.editable_enterToAccept && e.which === 13);
// change if new or user hits enter (if option set)
if ($this.data('before') !== $this.html() || t) {
$this.data('before', $this.html()).trigger('change');
if (t) {
c.$table
.data('contentFocused', false)
.trigger('updateCell', [ $this.closest('td'), wo.editable_autoResort, function(table){
$this.trigger( wo.editable_editComplete );
} ]);
$this.trigger('blur.tseditable');
}
}
});
}
});
})(jQuery);

View File

@@ -0,0 +1,159 @@
/*! tablesorter Grouping widget - updated 10/18/2013
* Requires tablesorter v2.8+ and jQuery 1.7+
* by Rob Garrison
*/
/*jshint browser:true, jquery:true, unused:false */
/*global jQuery: false */
;(function($){
"use strict";
var ts = $.tablesorter;
ts.grouping = {
number : function(c, $col, txt, num, group){
var t, w;
if (num > 1 && txt !== '') {
if ($col.hasClass(ts.css.sortAsc)) {
t = Math.floor(parseFloat(txt)/num) * num;
return t > parseFloat(group || 0) ? t : parseFloat(group || 0);
} else {
t = Math.ceil(parseFloat(txt)/num) * num;
return t < parseFloat(group || num) - t ? parseFloat(group || num) - t : t;
}
} else {
w = (txt + '').match(/\d+/g);
return w && w.length >= num ? w[num - 1] : txt || '';
}
},
separator : function(c, $col, txt, num){
var wo = c.widgetOptions,
w = (txt + '').split(wo.group_separator);
return $.trim(w && num > 0 && w.length >= num ? w[(num || 1) - 1] : '');
},
word : function(c, $col, txt, num){
var w = (txt + ' ').match(/\w+/g);
return w && w.length >= num ? w[num - 1] : txt || '';
},
letter : function(c, $col, txt, num){
return txt ? (txt + ' ').substring(0, num) : '';
},
date : function(c, $col, txt, part){
var wo = c.widgetOptions,
t = new Date(txt || ''),
t2 = t.getHours();
return part === 'year' ? t.getFullYear() :
part === 'month' ? wo.group_months[t.getMonth()] :
part === 'day' ? wo.group_months[t.getMonth()] + ' ' + t.getDate() :
part === 'week' ? wo.group_week[t.getDay()] :
part === 'time' ? ('00' + (t2 > 12 ? t2 - 12 : t2 === 0 ? t2 + 12 : t2)).slice(-2) + ':' +
('00' + t.getMinutes()).slice(-2) + ' ' + ('00' + wo.group_time[t2 >= 12 ? 1 : 0]).slice(-2) :
t.toString();
}
};
ts.widget.add({
id: 'group',
// run AFTER the zebra widget, so the header rows do not get zebra striping
priority: 100,
options: {
group_collapsible : true, // make the group header clickable and collapse the rows below it.
group_collapsed : false, // start with all groups collapsed
group_count : ' ({num})', // if not false, the "{num}" string is replaced with the number of rows in the group
group_separator : '-', // group name separator; used when group-separator-# class is used.
group_formatter : null, // function(txt, col, table, c, wo) { return txt; }
group_callback : null, // function($cell, $rows, column, table){}, callback allowing modification of the group header labels
group_complete : 'groupingComplete', // event triggered on the table when the grouping widget has finished work
// change these default date names based on your language preferences
group_months : [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ],
group_week : [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ],
group_time : [ 'AM', 'PM' ]
},
init: function(table, thisWidget, c, wo){
if (wo.group_collapsible) {
// .on() requires jQuery 1.7+
c.$table.on('click toggleGroup', 'tr.group-header', function(e){
// use shift-click to toggle ALL groups
if (e.type === 'click' && e.shiftKey) {
$(this).siblings('.group-header').trigger('toggleGroup');
}
$(this).toggleClass('collapsed');
// nextUntil requires jQuery 1.4+
$(this).nextUntil('tr.group-header').toggleClass('group-hidden', $(this).hasClass('collapsed') );
e.stopPropagation();
});
}
},
format: function(table, c, wo) {
var j, k, curr, $tr, t, t2, time, n,
group = '',
col = c.sortList[0] ? c.sortList[0][0] : -1;
c.$table
.find('tr.group-hidden').removeClass('group-hidden').end()
.find('tr.group-header').remove();
if (wo.group_collapsible) {
// clear pager saved spacer height (in case the rows are collapsed)
$.data(table, 'pagerSavedHeight', 0);
}
if (col >= 0 && !c.$headers.eq(col).hasClass('group-false')) {
if (c.debug){ time = new Date(); }
for (k = 0; k < c.$tbodies.length; k++) {
n = c.cache[k].normalized;
group = ''; // clear grouping across tbodies
$tr = c.$tbodies.eq(k).children('tr');
if (wo.group_collapsed && wo.group_collapsible) {
$tr.addClass('group-hidden');
}
for (j = 0; j < $tr.length; j++) {
if ( $tr.eq(j).is(':visible') ) {
t = (c.$headers.eq(col).attr('class') || '').match(/(group-\w+(-\w+)?)/g);
// group-{type}-{number/date}
t2 = t ? t[0].split('-') : ['','letter',1]; // default to letter 1
curr = n[j] ? ts.grouping[t2[1]]( c, c.$headers.eq(col), c.cache[k].normalized[j][col], /date/.test(t) ? t2[2] : parseInt(t2[2] || 1, 10) || 1, group ) : curr;
if (group !== curr) {
group = curr;
// show range if number > 1
if (t2[1] === 'number' && t2[2] > 1 && curr !== '') {
curr += ' - ' + (parseInt(curr, 10) + ((parseInt(t2[2],10) - 1) * (c.$headers.eq(col).hasClass(ts.css.sortAsc) ? 1 : -1)));
}
if ($.isFunction(wo.group_formatter)) {
curr = wo.group_formatter((curr || '').toString(), col, table, c, wo) || curr;
}
$tr.eq(j).before('<tr class="group-header ' + c.selectorRemove.slice(1) + (wo.group_collapsed && wo.group_collapsible ? ' collapsed' : '') +
'" unselectable="on"><td colspan="' + (c.columns+1) + '">' + (wo.group_collapsible ? '<i/>' : '') + '<span class="group-name">' +
curr + '</span><span class="group-count"></span></td></tr>');
}
}
}
}
$tr = c.$table.find('tr.group-header').on('selectstart', false);
if (wo.group_count || $.isFunction(wo.group_callback)) {
$tr.each(function(){
var $rows,
$row = $(this),
$label = $row.find('.group-count');
if ($label.length) {
$rows = $(this).nextUntil('tr.group-header').filter(':visible');
if (wo.group_count) {
$label.html( wo.group_count.replace(/\{num\}/g, $rows.length) );
}
if ($.isFunction(wo.group_callback)) {
wo.group_callback($row.find('td'), $rows, col, table);
}
}
});
}
c.$table.trigger(wo.group_complete);
if (c.debug) {
$.tablesorter.utility.benchmark("Applying groups widget: ", time);
}
}
},
remove : function(table, c, wo){
c.$table
.off('click', 'tr.group-header')
.find('.group-hidden').removeClass('group-hidden').end()
.find('tr.group-header').remove();
}
});
})(jQuery);

View File

@@ -0,0 +1,748 @@
/* Pager widget (beta) for TableSorter 10/18/2013 */
/*jshint browser:true, jquery:true, unused:false */
;(function($){
"use strict";
var tsp,
ts = $.tablesorter;
ts.widget.add({
id: "pager",
priority: 5,
options : {
// output default: '{page}/{totalPages}'
// possible variables: {page}, {totalPages}, {filteredPages}, {startRow}, {endRow}, {filteredRows} and {totalRows}
pager_output: '{startRow} to {endRow} of {totalRows} rows', // '{page}/{totalPages}'
// apply disabled classname to the pager arrows when the rows at either extreme is visible
pager_updateArrows: true,
// starting page of the pager (zero based index)
pager_startPage: 0,
// Number of visible rows
pager_size: 10,
// Save pager page & size if the storage script is loaded (requires $.tablesorter.storage in jquery.tablesorter.widgets.js)
pager_savePages: true,
// if true, the table will remain the same height no matter how many records are displayed. The space is made up by an empty
// table row set to a height to compensate; default is false
pager_fixedHeight: false,
// count child rows towards the set page size? (set true if it is a visible table row within the pager)
// if true, child row(s) may not appear to be attached to its parent row, may be split across pages or
// may distort the table if rowspan or cellspans are included.
pager_countChildRows: false,
// remove rows from the table to speed up the sort of large tables.
// setting this to false, only hides the non-visible rows; needed if you plan to add/remove rows with the pager enabled.
pager_removeRows: false, // removing rows in larger tables speeds up the sort
// use this format: "http://mydatabase.com?page={page}&size={size}&{sortList:col}&{filterList:fcol}"
// where {page} is replaced by the page number, {size} is replaced by the number of records to show,
// {sortList:col} adds the sortList to the url into a "col" array, and {filterList:fcol} adds
// the filterList to the url into an "fcol" array.
// So a sortList = [[2,0],[3,0]] becomes "&col[2]=0&col[3]=0" in the url
// and a filterList = [[2,Blue],[3,13]] becomes "&fcol[2]=Blue&fcol[3]=13" in the url
pager_ajaxUrl: null,
// modify the url after all processing has been applied
pager_customAjaxUrl: function(table, url) { return url; },
// modify the $.ajax object to allow complete control over your ajax requests
pager_ajaxObject: {
dataType: 'json'
},
// process ajax so that the following information is returned:
// [ total_rows (number), rows (array of arrays), headers (array; optional) ]
// example:
// [
// 100, // total rows
// [
// [ "row1cell1", "row1cell2", ... "row1cellN" ],
// [ "row2cell1", "row2cell2", ... "row2cellN" ],
// ...
// [ "rowNcell1", "rowNcell2", ... "rowNcellN" ]
// ],
// [ "header1", "header2", ... "headerN" ] // optional
// ]
pager_ajaxProcessing: function(ajax){ return [ 0, [], null ]; },
// css class names of pager arrows
pager_css: {
container : 'tablesorter-pager',
errorRow : 'tablesorter-errorRow', // error information row (don't include period at beginning)
disabled : 'disabled' // class added to arrows @ extremes (i.e. prev/first arrows "disabled" on first page)
},
// jQuery selectors
pager_selectors: {
container : '.pager', // target the pager markup
first : '.first', // go to first page arrow
prev : '.prev', // previous page arrow
next : '.next', // next page arrow
last : '.last', // go to last page arrow
goto : '.gotoPage', // go to page selector - select dropdown that sets the current page
pageDisplay : '.pagedisplay', // location of where the "output" is displayed
pageSize : '.pagesize' // page size selector - select dropdown that sets the "size" option
}
},
init: function(table){
tsp.init(table);
},
// only update to complete sorter initialization
format: function(table, c){
if (!(c.pager && c.pager.initialized)){
return tsp.initComplete(table, c);
}
tsp.moveToPage(table, c.pager, false);
},
remove: function(table, c, wo){
tsp.destroyPager(table, c);
}
});
/* pager widget functions */
tsp = ts.pager = {
init: function(table) {
// check if tablesorter has initialized
if (table.hasInitialized && table.config.pager.initialized) { return; }
var t,
c = table.config,
wo = c.widgetOptions,
s = wo.pager_selectors,
// save pager variables
p = c.pager = $.extend({
totalPages: 0,
filteredRows: 0,
filteredPages: 0,
currentFilters: [],
page: wo.pager_startPage,
size: wo.pager_size,
startRow: 0,
endRow: 0,
$size: null,
last: {}
}, c.pager);
// added in case the pager is reinitialized after being destroyed.
p.$container = $(s.container).addClass(wo.pager_css.container).show();
// goto selector
p.$goto = p.$container.find(s.goto);
// page size selector
p.$size = p.$container.find(s.pageSize);
p.totalRows = c.$tbodies.eq(0).children().length;
p.oldAjaxSuccess = p.oldAjaxSuccess || wo.pager_ajaxObject.success;
c.appender = tsp.appender;
if (wo.pager_savePages && ts.storage) {
t = ts.storage(table, 'tablesorter-pager') || {}; // fixes #387
p.page = isNaN(t.page) ? p.page : t.page;
p.size = ( isNaN(t.size) ? p.size : t.size ) || 10;
}
// clear initialized flag
p.initialized = false;
// before initialization event
c.$table.trigger('pagerBeforeInitialized', c);
tsp.enablePager(table, c, false);
if ( typeof(wo.pager_ajaxUrl) === 'string' ) {
// ajax pager; interact with database
p.ajax = true;
// When filtering with ajax, allow only custom filtering function, disable default filtering since it will be done server side.
wo.filter_serversideFiltering = true;
c.serverSideSorting = true;
tsp.moveToPage(table, p);
} else {
p.ajax = false;
// Regular pager; all rows stored in memory
c.$table.trigger("appendCache", true);
tsp.hideRowsSetup(table, c);
}
},
initComplete: function(table, c){
var p = c.pager;
tsp.changeHeight(table, c);
tsp.bindEvents(table, c);
// pager initialized
if (!p.ajax) {
p.initialized = true;
tsp.setPageSize(table, 0, c); // page size 0 is ignored
c.$table.trigger('pagerInitialized', c);
}
},
bindEvents: function(table, c){
var ctrls, fxn,
p = c.pager,
wo = c.widgetOptions,
s = wo.pager_selectors;
c.$table
.off('filterStart filterEnd sortEnd disable enable destroy update pageSize '.split(' ').join('.pager '))
.on('filterStart.pager', function(e, filters) {
c.$table.data('pagerUpdateTriggered', false);
p.currentFilters = filters;
})
// update pager after filter widget completes
.on('filterEnd.pager sortEnd.pager', function(e) {
//Prevent infinite event loops from occuring by setting this in all moveToPage calls and catching it here.
if (c.$table.data('pagerUpdateTriggered')) {
c.$table.data('pagerUpdateTriggered', false);
return;
}
//only run the server side sorting if it has been enabled
if (e.type === "filterEnd" || (e.type === "sortEnd" && c.serverSideSorting)) {
tsp.moveToPage(table, p, false);
}
tsp.updatePageDisplay(table, c, false);
tsp.fixHeight(table, c);
})
.on('disable.pager', function(e){
e.stopPropagation();
tsp.showAllRows(table, c);
})
.on('enable.pager', function(e){
e.stopPropagation();
tsp.enablePager(table, c, true);
})
.on('destroy.pager', function(e){
e.stopPropagation();
tsp.destroyPager(table, c);
})
.on('update.pager', function(e){
e.stopPropagation();
tsp.hideRows(table, c);
})
.on('pageSize.pager', function(e,v){
e.stopPropagation();
tsp.setPageSize(table, parseInt(v, 10) || 10, c);
tsp.hideRows(table, c);
tsp.updatePageDisplay(table, c, false);
if (p.$size.length) { p.$size.val(p.size); } // twice?
})
.on('pageSet.pager', function(e,v){
e.stopPropagation();
p.page = (parseInt(v, 10) || 1) - 1;
if (p.$goto.length) { p.$goto.val(c.size); } // twice?
tsp.moveToPage(table, p);
tsp.updatePageDisplay(table, c, false);
});
// clicked controls
ctrls = [ s.first, s.prev, s.next, s.last ];
fxn = [ 'moveToFirstPage', 'moveToPrevPage', 'moveToNextPage', 'moveToLastPage' ];
p.$container.find(ctrls.join(','))
.off('click.pager')
.on('click.pager', function(){
var i,
$c = $(this),
l = ctrls.length;
if ( !$c.hasClass(wo.pager_css.disabled) ) {
for (i = 0; i < l; i++) {
if ($c.is(ctrls[i])) {
tsp[fxn[i]](table, p);
break;
}
}
}
return false;
});
if ( p.$goto.length ) {
p.$goto
.off('change')
.on('change', function(){
p.page = $(this).val() - 1;
tsp.moveToPage(table, p);
tsp.updatePageDisplay(table, c, false);
});
}
if ( p.$size.length ) {
p.$size
.off('change.pager')
.on('change.pager', function() {
p.$size.val( $(this).val() ); // in case there are more than one pagers
if ( !$(this).hasClass(wo.pager_css.disabled) ) {
tsp.setPageSize(table, parseInt( $(this).val(), 10 ), c);
tsp.changeHeight(table, c);
}
return false;
});
}
},
// hide arrows at extremes
pagerArrows: function(c, disable) {
var p = c.pager,
dis = !!disable,
wo = c.widgetOptions,
s = wo.pager_selectors,
tp = Math.min( p.totalPages, p.filteredPages );
if ( wo.pager_updateArrows ) {
p.$container.find(s.first + ',' + s.prev).toggleClass(wo.pager_css.disabled, dis || p.page === 0);
p.$container.find(s.next + ',' + s.last).toggleClass(wo.pager_css.disabled, dis || p.page === tp - 1);
}
},
updatePageDisplay: function(table, c, flag) {
var i, pg, s, t, out,
wo = c.widgetOptions,
p = c.pager,
f = c.$table.hasClass('hasFilters') && !wo.pager_ajaxUrl,
t = (c.widgetOptions && c.widgetOptions.filter_filteredRow || 'filtered') + ',' + c.selectorRemove,
sz = p.size || 10; // don't allow dividing by zero
p.$size.removeClass(wo.pager_css.disabled).removeAttr('disabled');
p.$goto.removeClass(wo.pager_css.disabled).removeAttr('disabled');
p.totalPages = Math.ceil( p.totalRows / sz ); // needed for "pageSize" method
p.filteredRows = (f) ? c.$tbodies.eq(0).children('tr').not('.' + t).length : p.totalRows;
p.filteredPages = (f) ? Math.ceil( p.filteredRows / sz ) || 1 : p.totalPages;
if ( Math.min( p.totalPages, p.filteredPages ) >= 0 ) {
t = (p.size * p.page > p.filteredRows);
p.startRow = (t) ? 1 : (p.filteredRows === 0 ? 0 : p.size * p.page + 1);
p.page = (t) ? 0 : p.page;
p.endRow = Math.min( p.filteredRows, p.totalRows, p.size * ( p.page + 1 ) );
out = p.$container.find(wo.pager_selectors.pageDisplay);
// form the output string (can now get a new output string from the server)
s = ( p.ajaxData && p.ajaxData.output ? p.ajaxData.output || wo.pager_output : wo.pager_output )
// {page} = one-based index; {page+#} = zero based index +/- value
.replace(/\{page([\-+]\d+)?\}/gi, function(m,n){
return p.page + (n ? parseInt(n, 10) : 1);
})
// {totalPages}, {extra}, {extra:0} (array) or {extra : key} (object)
.replace(/\{\w+(\s*:\s*\w+)?\}/gi, function(m){
var t = m.replace(/[{}\s]/g,''), a = t.split(':'), d = p.ajaxData;
return a.length > 1 && d && d[a[0]] ? d[a[0]][a[1]] : p[t] || (d ? d[t] : '') || '';
});
if (out.length) {
out[ (out[0].tagName === 'INPUT') ? 'val' : 'html' ](s);
if ( p.$goto.length ) {
t = '';
pg = Math.min( p.totalPages, p.filteredPages );
for ( i = 1; i <= pg; i++ ) {
t += '<option>' + i + '</option>';
}
p.$goto.html(t).val( p.page + 1 );
}
}
}
tsp.pagerArrows(c);
if (p.initialized && flag !== false) {
c.$table.trigger('pagerComplete', c);
// save pager info to storage
if (wo.pager_savePages && ts.storage) {
ts.storage(table, 'tablesorter-pager', {
page : p.page,
size : p.size
});
}
}
},
fixHeight: function(table, c) {
var d, h,
p = c.pager,
wo = c.widgetOptions,
$b = c.$tbodies.eq(0);
if (wo.pager_fixedHeight) {
$b.find('tr.pagerSavedHeightSpacer').remove();
h = c.$table.data('pagerSavedHeight');
if (h) {
d = h - $b.height();
if ( d > 5 && c.$table.data('pagerLastSize') === p.size && $b.children('tr:visible').length < p.size ) {
$b.append('<tr class="pagerSavedHeightSpacer ' + wo.pager_selectors.remove.replace(/(tr)?\./g,'') + '" style="height:' + d + 'px;"></tr>');
}
}
}
},
changeHeight: function(table, c) {
var $b = c.$tbodies.eq(0);
$b.find('tr.pagerSavedHeightSpacer').remove();
c.$table.data('pagerSavedHeight', $b.height());
tsp.fixHeight(table, c);
c.$table.data('pagerLastSize', c.pager.size);
},
hideRows: function(table, c){
if (!c.widgetOptions.pager_ajaxUrl) {
var i,
p = c.pager,
wo = c.widgetOptions,
rows = c.$tbodies.eq(0).children(),
l = rows.length,
s = ( p.page * p.size ),
e = s + p.size,
f = wo && wo.filter_filteredRow || 'filtered',
j = 0; // size counter
for ( i = 0; i < l; i++ ){
if ( !rows[i].className.match(f) ) {
rows[i].style.display = ( j >= s && j < e ) ? '' : 'none';
// don't count child rows
j += rows[i].className.match(c.cssChildRow + '|' + c.selectorRemove.slice(1)) && !wo.pager_countChildRows ? 0 : 1;
}
}
}
},
hideRowsSetup: function(table, c){
var p = c.pager;
p.size = parseInt( p.$size.val(), 10 ) || p.size;
c.$table.data('pagerLastSize', p.size);
tsp.pagerArrows(c);
if ( !c.widgetOptions.pager_removeRows ) {
tsp.hideRows(table, c);
c.$table.on('sortEnd.pager filterEnd.pager', function(){
tsp.hideRows(table, c);
});
}
},
renderAjax: function(data, table, c, xhr, exception){
var p = c.pager,
wo = c.widgetOptions;
// process data
if ( $.isFunction(wo.pager_ajaxProcessing) ) {
// ajaxProcessing result: [ total, rows, headers ]
var i, j, t, hsh, $f, $sh, th, d, l, $err, rr_count,
$t = c.$table,
tds = '',
result = wo.pager_ajaxProcessing(data, table) || [ 0, [] ],
hl = $t.find('thead th').length;
$t.find('thead tr.' + wo.pager_css.errorRow).remove(); // Clean up any previous error.
if ( exception ) {
$err = $('<tr class="' + wo.pager_css.errorRow + '"><td style="text-align:center;" colspan="' +
hl + '">' + exception.message + ' (' + xhr.status + ')</td></tr>')
.click(function(){
$(this).remove();
})
// add error row to thead instead of tbody, or clicking on the header will result in a parser error
.appendTo( $t.find('thead:first') );
c.$tbodies.eq(0).empty();
if (c.debug) { ts.log({ 'exception' : exception, 'jqxhr' : xhr }); }
} else {
// process ajax object
if (!$.isArray(result)) {
p.ajaxData = result;
p.totalRows = result.total;
th = result.headers;
d = result.rows;
} else {
// allow [ total, rows, headers ] or [ rows, total, headers ]
t = isNaN(result[0]) && !isNaN(result[1]);
//ensure a zero returned row count doesn't fail the logical ||
rr_count = result[t ? 1 : 0];
p.totalRows = isNaN(rr_count) ? p.totalRows || 0 : rr_count;
d = result[t ? 0 : 1] || []; // row data
th = result[2]; // headers
}
l = d.length;
if (d instanceof jQuery) {
// append jQuery object
c.$tbodies.eq(0).empty().append(d);
} else if (d.length) {
// build table from array
if ( l > 0 ) {
for ( i = 0; i < l; i++ ) {
tds += '<tr>';
for ( j = 0; j < d[i].length; j++ ) {
// build tbody cells
tds += '<td>' + d[i][j] + '</td>';
}
tds += '</tr>';
}
}
// add rows to first tbody
c.$tbodies.eq(0).html( tds );
}
// only add new header text if the length matches
if ( th && th.length === hl ) {
hsh = $t.hasClass('hasStickyHeaders');
$sh = hsh ? c.$sticky.children('thead:first').children().children() : '';
$f = $t.find('tfoot tr:first').children();
// don't change td headers (may contain pager)
c.$headers.filter('th').each(function(j){
var $t = $(this), icn;
// add new test within the first span it finds, or just in the header
if ( $t.find('.' + ts.css.icon).length ) {
icn = $t.find('.' + ts.css.icon).clone(true);
$t.find('.tablesorter-header-inner').html( th[j] ).append(icn);
if ( hsh && $sh.length ) {
icn = $sh.eq(j).find('.' + ts.css.icon).clone(true);
$sh.eq(j).find('.tablesorter-header-inner').html( th[j] ).append(icn);
}
} else {
$t.find('.tablesorter-header-inner').html( th[j] );
if (hsh && $sh.length) {
$sh.eq(j).find('.tablesorter-header-inner').html( th[j] );
}
}
$f.eq(j).html( th[j] );
});
}
}
if (c.showProcessing) {
ts.isProcessing(table); // remove loading icon
}
p.totalPages = Math.ceil( p.totalRows / ( p.size || 10 ) );
tsp.updatePageDisplay(table, c);
tsp.fixHeight(table, c);
if (p.initialized) {
$t.trigger('pagerChange', c);
$t.trigger('updateComplete');
} else {
$t.trigger('update');
}
}
if (!p.initialized) {
p.initialized = true;
c.$table.trigger('pagerInitialized', c);
}
},
getAjax: function(table, c){
var url = tsp.getAjaxUrl(table, c),
$doc = $(document),
wo = c.widgetOptions,
p = c.pager;
if ( url !== '' ) {
if (c.showProcessing) {
ts.isProcessing(table, true); // show loading icon
}
$doc.on('ajaxError.pager', function(e, xhr, settings, exception) {
tsp.renderAjax(null, table, c, xhr, exception);
$doc.off('ajaxError.pager');
});
wo.pager_ajaxObject.url = url; // from the ajaxUrl option and modified by customAjaxUrl
wo.pager_ajaxObject.success = function(data) {
tsp.renderAjax(data, table, c);
$doc.off('ajaxError.pager');
if (typeof p.oldAjaxSuccess === 'function') {
p.oldAjaxSuccess(data);
}
};
$.ajax(wo.pager_ajaxObject);
}
},
getAjaxUrl: function(table, c) {
var p = c.pager,
wo = c.widgetOptions,
url = (wo.pager_ajaxUrl) ? wo.pager_ajaxUrl
// allow using "{page+1}" in the url string to switch to a non-zero based index
.replace(/\{page([\-+]\d+)?\}/, function(s,n){ return p.page + (n ? parseInt(n, 10) : 0); })
.replace(/\{size\}/g, p.size) : '',
sl = c.sortList,
fl = p.currentFilters || [],
sortCol = url.match(/\{\s*sort(?:List)?\s*:\s*(\w*)\s*\}/),
filterCol = url.match(/\{\s*filter(?:List)?\s*:\s*(\w*)\s*\}/),
arry = [];
if (sortCol) {
sortCol = sortCol[1];
$.each(sl, function(i,v){
arry.push(sortCol + '[' + v[0] + ']=' + v[1]);
});
// if the arry is empty, just add the col parameter... "&{sortList:col}" becomes "&col"
url = url.replace(/\{\s*sort(?:List)?\s*:\s*(\w*)\s*\}/g, arry.length ? arry.join('&') : sortCol );
arry = [];
}
if (filterCol) {
filterCol = filterCol[1];
$.each(fl, function(i,v){
if (v) {
arry.push(filterCol + '[' + i + ']=' + encodeURIComponent(v));
}
});
// if the arry is empty, just add the fcol parameter... "&{filterList:fcol}" becomes "&fcol"
url = url.replace(/\{\s*filter(?:List)?\s*:\s*(\w*)\s*\}/g, arry.length ? arry.join('&') : filterCol );
}
if ( $.isFunction(wo.pager_customAjaxUrl) ) {
url = wo.pager_customAjaxUrl(table, url);
}
return url;
},
renderTable: function(table, rows) {
var i, $tb,
c = table.config,
p = c.pager,
wo = c.widgetOptions,
l = rows && rows.length || 0, // rows may be undefined
s = ( p.page * p.size ),
e = ( s + p.size );
if ( l < 1 ) { return; } // empty table, abort!
if ( p.page >= p.totalPages ) {
// lets not render the table more than once
return tsp.moveToLastPage(table, p);
}
p.isDisabled = false; // needed because sorting will change the page and re-enable the pager
if (p.initialized) { c.$table.trigger('pagerChange', c); }
if ( !wo.pager_removeRows ) {
tsp.hideRows(table, c);
} else {
if ( e > rows.length ) {
e = rows.length;
}
ts.clearTableBody(table);
$tb = ts.utility.processTbody(table, c.$tbodies.eq(0), true);
for ( i = s; i < e; i++ ) {
$tb.append(rows[i]);
}
ts.utility.processTbody(table, $tb, false);
}
tsp.updatePageDisplay(table, c);
if ( !p.isDisabled ) { tsp.fixHeight(table, c); }
wo.pager_startPage = p.page;
wo.pager_size = p.size;
},
showAllRows: function(table, c){
var p = c.pager,
wo = c.widgetOptions;
if ( p.ajax ) {
tsp.pagerArrows(c, true);
} else {
p.isDisabled = true;
c.$table.data('pagerLastPage', p.page);
c.$table.data('pagerLastSize', p.size);
p.page = 0;
p.size = p.totalRows;
p.totalPages = 1;
c.$table.addClass('pagerDisabled').find('tr.pagerSavedHeightSpacer').remove();
tsp.renderTable(table, c.rowsCopy);
}
// disable size selector
p.$size.add(p.$goto).each(function(){
$(this).addClass(wo.pager_css.disabled).attr('disabled', 'disabled');
});
c.$table.trigger('applyWidgets');
},
moveToPage: function(table, p, flag) {
if ( p.isDisabled ) { return; }
var c = table.config,
l = p.last,
pg = Math.min( p.totalPages, p.filteredPages );
if ( p.page < 0 ) { p.page = 0; }
if ( p.page > ( pg - 1 ) && pg !== 0 ) { p.page = pg - 1; }
// don't allow rendering multiple times on the same page/size/totalpages/filters
if (l.page === p.page && l.size === p.size && l.total === p.totalPages && l.filters === p.currentFilters ) { return; }
p.last = {
page : p.page,
size : p.size,
totalPages : p.totalPages,
currentFilters : p.currentFilters
};
if (p.ajax) {
tsp.getAjax(table, c);
} else if (!p.ajax) {
tsp.renderTable(table, c.rowsCopy);
}
c.$table.data('pagerLastPage', p.page);
c.$table.data('pagerUpdateTriggered', true);
if (p.initialized && flag !== false) {
c.$table.trigger('pageMoved', c);
c.$table.trigger('applyWidgets');
}
},
setPageSize: function(table, size, c) {
var p = c.pager;
p.size = size;
p.$size.val(size);
c.$table.data('pagerLastPage', p.page);
c.$table.data('pagerLastSize', p.size);
p.totalPages = Math.ceil( p.totalRows / ( p.size || 10 ) );
tsp.moveToPage(table, p);
},
moveToFirstPage: function(table, p) {
p.page = 0;
tsp.moveToPage(table, p);
},
moveToLastPage: function(table, p) {
p.page = ( Math.min( p.totalPages, p.filteredPages ) - 1 );
tsp.moveToPage(table, p);
},
moveToNextPage: function(table, p) {
p.page++;
if ( p.page >= ( Math.min( p.totalPages, p.filteredPages ) - 1 ) ) {
p.page = ( Math.min( p.totalPages, p.filteredPages ) - 1 );
}
tsp.moveToPage(table, p);
},
moveToPrevPage: function(table, p) {
p.page--;
if ( p.page <= 0 ) {
p.page = 0;
}
tsp.moveToPage(table, p);
},
destroyPager: function(table, c){
var p = c.pager;
tsp.showAllRows(table, c);
p.$container.hide(); // hide pager
c.appender = null; // remove pager appender function
p.initialized = false;
c.$table.off('destroy.pager sortEnd.pager filterEnd.pager enable.pager disable.pager');
if (ts.storage) {
ts.storage(table, 'tablesorter-pager', '');
}
},
enablePager: function(table, c, triggered){
var p = c.pager,
wo = c.widgetOptions;
p.isDisabled = false;
p.page = c.$table.data('pagerLastPage') || p.page || 0;
p.size = c.$table.data('pagerLastSize') || parseInt(p.$size.find('option[selected]').val(), 10) || p.size;
p.$size.val(p.size); // set page size
p.totalPages = Math.ceil( Math.min( p.totalPages, p.filteredPages ) / ( p.size || 10 ) );
c.$table.removeClass('pagerDisabled');
if ( triggered ) {
c.$table.trigger('update');
tsp.setPageSize(table, p.size, c);
tsp.hideRowsSetup(table, c);
tsp.fixHeight(table, c);
}
},
appender: function(table, rows) {
var c = table.config,
p = c.pager;
if ( !p.ajax ) {
c.rowsCopy = rows;
p.totalRows = c.widgetOptions.pager_countChildRows ? c.$tbodies.eq(0).children().length : rows.length;
p.size = c.$table.data('pagerLastSize') || p.size;
p.totalPages = Math.ceil( p.totalRows / ( p.size || 10 ) );
tsp.moveToPage(table, p);
}
}
};
})(jQuery);

View File

@@ -0,0 +1,50 @@
/*! tablesorter repeatHeaders widget - updated 4/12/2013
* Requires tablesorter v2.8+ and jQuery 1.7+
* Original by Christian Bach from the example-widgets.html demo
*/
/*global jQuery: false */
;(function($){
"use strict";
$.tablesorter.widget.add({
id: "repeatHeaders",
priority: 10,
options: {
rowsToSkip : 4
},
// format is called on init and when a sorting has finished
format: function(table, c, wo) {
var h = '', i, $tr, l, skip;
// cache and collect all TH headers
if (!wo.repeatHeaders) {
h = '<tr class="repeated-header remove-me">';
$.each(c.headerContent, function(i,t) {
h += '<th>' + t + '</th>';
});
// "remove-me" class was added in case the table needs to be updated, the "remove-me" rows will be
// removed prior to the update to prevent including the rows in the update - see "selectorRemove" option
wo.repeatHeaders = h + '</tr>';
}
// number of rows to skip
skip = wo && wo.rowsToSkip || 4;
// remove appended headers by classname
c.$table.find("tr.repeated-header").remove();
$tr = c.$tbodies.find('tr');
l = $tr.length;
// loop all tr elements and insert a copy of the "headers"
for (i = skip; i < l; i += skip) {
// insert a copy of the table head every X rows
$tr.eq(i).before(wo.repeatHeaders);
}
},
// this remove function is called when using the refreshWidgets method or when destroying the tablesorter plugin
// this function only applies to tablesorter v2.4+
remove: function(table, c){
c.$table.find("tr.repeated-header").remove();
}
});
})(jQuery);

View File

@@ -0,0 +1,241 @@
/*!
Copyright (C) 2011 T. Connell & Associates, Inc.
Dual-licensed under the MIT and GPL licenses
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Resizable scroller widget for the jQuery tablesorter plugin
Version 2.0 - modified by Rob Garrison (4/12/2013)
Requires jQuery, v1.2.3 or higher
Requires the tablesorter plugin, v2.0 or higher, available at http://mottie.github.com/tablesorter/docs/
Usage:
$(function() {
$('table.tablesorter').tablesorter({
widgets: ['zebra', 'scroller'],
widgetOptions : {
scroller_height : 300, // height of scroll window
scroller_barWidth : 17, // scroll bar width
scroller_jumpToHeader : true, // header snap to browser top when scrolling the tbody
scroller_idPrefix : 's_' // cloned thead id prefix (random number added to end)
}
});
});
Website: www.tconnell.com
*/
/*jshint browser:true, jquery:true, unused:false */
;(function($){
"use strict";
$.fn.hasScrollBar = function(){
return this.get(0).scrollHeight > this.height();
};
var ts = $.tablesorter;
ts.window_resize = function(){
if (this.resize_timer) {
clearTimeout(this.resize_timer);
}
this.resize_timer = setTimeout(function(){
$(this).trigger('resizeEnd');
}, 250);
};
// Add extra scroller css
$(function(){
var s = '<style>' +
'.tablesorter-scroller-header table.tablesorter { margin-bottom: 0; }' +
'.tablesorter-scroller-table table.tablesorter { margin-top: 0; } ' +
'.tablesorter-scroller-table .tablesorter-filter-row, .tablesorter-scroller-table tfoot { display: none; }' +
'.tablesorter-scroller-table table.tablesorter thead tr.tablesorter-headerRow * {' +
'line-height:0;height:0;border:none;background-image:none;padding-top:0;padding-bottom:0;margin-top:0;margin-bottom:0;overflow:hidden;' +
'}</style>';
$(s).appendTo('body');
});
ts.widget.add({
id: 'scroller',
priority: 60, // run after the filter widget
options: {
scroller_height : 300,
scroller_barWidth : 17,
scroller_jumpToHeader: true,
scroller_idPrefix : 's_'
},
init: function(table, thisWidget, c, wo){
var $win = $(window);
//Setup window.resizeEnd event
$win
.on('resize', ts.window_resize)
.on('resizeEnd', function(e) {
// init is run before format, so scroller_resizeWidth
// won't be defined within the "c" or "wo" parameters
if (typeof table.config.widgetOptions.scroller_resizeWidth === 'function') {
//IE calls resize when you modify content, so we have to unbind the resize event
//so we don't end up with an infinite loop. we can rebind after we're done.
$win.off('resize', ts.window_resize);
table.config.widgetOptions.scroller_resizeWidth();
$win.on('resize', ts.window_resize);
}
});
},
format: function(table, c, wo) {
var h, $hdr, id, t, resize, $cells,
$win = $(window),
$tbl = c.$table,
flag = false,
filterInputs = 'input, select';
if (!c.isScrolling) {
h = wo.scroller_height || 300;
t = $tbl.find('tbody').height();
if (t !== 0 && h > t) { h = t + 10; } // Table is less than h px
id = wo.scroller_id = wo.scroller_idPrefix + Math.floor(Math.random() * 101);
$hdr = $('<table class="' + $tbl.attr('class') + '" cellpadding=0 cellspacing=0><thead>' + $tbl.find('thead:first').html() + '</thead></table>');
$tbl
.wrap('<div id="' + id + '" class="tablesorter-scroller" style="text-align:left;" />')
.before($hdr)
.find('.tablesorter-filter-row').addClass('hideme');
$cells = $hdr
.wrap('<div class="tablesorter-scroller-header" style="width:' + $tbl.width() + ';" />')
.find('.' + ts.css.header)
.on('mousedown', function(){
this.onselectstart = function(){ return false; };
return false;
});
$tbl
.wrap('<div class="tablesorter-scroller-table" style="height:' + h + 'px;width:' + $tbl.width() + ';overflow-y:scroll;" />')
.off('sortEnd.tsScroller')
.on('sortEnd.tsScroller', function(){
c.$headers.each(function(i){
var t = $cells.eq(i);
t
.attr('class', $(this).attr('class'))
// remove processing icon
.removeClass(ts.css.processing + ' ' + c.cssProcessing);
if (ts.css.icon){
t
.find('.' + ts.css.icon)
.attr('class', $(this).find('.' + ts.css.icon).attr('class'));
}
});
});
// make scroller header sortable
c.$headers.find(c.selectorSort).add( c.$headers.filter(c.selectorSort) ).each(function(i){
var t = $(this);
$cells.eq(i)
// clicking on new header will trigger a sort
.on('mouseup', function(e){
t.trigger(e, true); // external mouseup flag (click timer is ignored)
})
// prevent header text selection
.on('mousedown', function(){
this.onselectstart = function(){ return false; };
return false;
});
});
// look for filter widget
$tbl.on('filterEnd', function(){
if (flag) { return; }
$cells.each(function(i){
$(this).find(filterInputs).val( c.$filters.find(filterInputs).eq(i).val() );
});
});
$hdr.find(filterInputs).on('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);
});
resize = function(){
var d,
//Hide other scrollers so we can resize
$div = $('div.scroller[id != "' + id + '"]').hide();
$tbl.find('thead').show();
//Reset sizes so parent can resize.
$hdr
.width(0)
.parent().width(0)
.find('th,td').width(0);
$tbl
.width(0)
.find('thead').find('th,td').width(0);
d = $tbl.parent();
d.width(0);
d.parent().trigger('resize');
// Shrink a bit to accommodate scrollbar
d.width( d.parent().innerWidth() - ( d.parent().hasScrollBar() ? wo.scroller_barWidth : 0 ) );
$tbl.width( d.innerWidth() - ( d.hasScrollBar() ? wo.scroller_barWidth : 0 ) );
$tbl.find('thead').find('th,td').filter(':visible').each(function(i, c){
var $th = $(c),
//Wrap in browser detect??
w = parseInt( $th.css('min-width').replace('auto', '0').replace(/(px|em)/, ''), 10 );
if ( $th.width() < w ) {
$th.width(w);
} else {
w = $th.width();
}
$hdr.find('th,td').eq(i).width(w);
});
$hdr.width($tbl.innerWidth());
$div.show();
};
//Expose to external calls
wo.scroller_resizeWidth = resize;
resize();
$tbl.find('thead').css('visibility', 'hidden');
c.isScrolling = true;
t = $tbl.parent().parent().height();
// The header will always jump into view if scrolling the table body
$tbl.parent().on('scroll', function(){
if (wo.scroller_jumpToHeader) {
var pos = $win.scrollTop() - $hdr.offset().top;
if ($(this).scrollTop() !== 0 && pos < t && pos > 0) {
$win.scrollTop( $hdr.offset().top );
}
}
});
}
//Sorting, so scroll to top
$tbl.parent().animate({ scrollTop: 0 }, 'fast');
},
remove : function(table, c, wo){
}
});
})(jQuery);