Add JS files

This commit is contained in:
2025-05-12 15:45:17 +00:00
parent 7ddd15c4fa
commit 967007b0c7
3239 changed files with 1157078 additions and 0 deletions

View File

@@ -0,0 +1,145 @@
/*! tablesorter Align Character widget - updated 3/12/2014 (core v2.15.8)
* 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.alignChar = {
init : function(table, c, wo) {
c.$headers.filter('[' + wo.alignChar_charAttrib + ']').each(function(){
var $this = $(this),
vars = {
column : this.column,
align : $this.attr(wo.alignChar_charAttrib),
alignIndex : parseInt( $this.attr(wo.alignChar_indexAttrib) || 0, 10),
adjust : parseFloat($this.attr(wo.alignChar_adjustAttrib)) || 0,
};
vars.regex = new RegExp('\\' + vars.align, 'g');
if (typeof vars.align !== 'undefined') {
wo.alignChar_savedVars[this.column] = vars;
ts.alignChar.setup(table, c, wo, vars);
}
});
},
setup: function(table, c, wo, v){
// do nothing for empty tables
if ($.isEmptyObject(c.cache)) { return; }
var tbodyIndex, rowIndex, start, end, last, index, rows, val, count,
len, wLeft, wRight, alignChar, $row,
left = [],
right = [];
for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++){
rows = c.cache[tbodyIndex];
len = rows.normalized.length;
for (rowIndex = 0; rowIndex < len; rowIndex++) {
// set up to work with modified cache v2.16.0+
$row = rows.row ? rows.row[rowIndex] : rows.normalized[rowIndex][c.columns].$row;
val = $row.find('td').eq(v.column).text().replace(/[ ]/g, "\u00a0");
// count how many "align" characters are in the string
count = (val.match( v.regex ) || []).length;
// set alignment @ alignIndex (one-based index)
if (count > 0 && v.alignIndex > 0) {
end = Math.min(v.alignIndex, count);
start = 0;
index = 0;
last = 0;
// find index of nth align character based on alignIndex (data-align-index)
while (start++ < end) {
last = val.indexOf(v.align, last + 1);
index = last < 0 ? index : last;
}
} else {
index = val.indexOf(v.align);
}
if ( index >= 0 ) {
left.push( val.substring(0, index) || '' );
right.push( val.substring(index, val.length) || '' );
} else {
// no align character found!
// put val in right or left based on the align index
left.push( (count >= 1 && v.alignIndex >= count) ? '' : val || '' );
right.push( (count >= 1 && v.alignIndex >= count) ? val || '' : '' );
}
}
}
// find widest segments
wLeft = ($.extend([], left)).sort(function(a,b){ return b.length - a.length; })[0];
wRight = ($.extend([], right)).sort(function(a,b){ return b.length - a.length; })[0];
// calculate percentage widths
v.width = v.width || ( Math.floor(wLeft.length / (wLeft.length + wRight.length) * 100) + v.adjust );
wLeft = 'min-width:' + v.width + '%';
wRight = 'min-width:' + (100 - v.width) + '%';
for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++){
rows = c.cache[tbodyIndex];
len = rows.normalized.length;
for (rowIndex = 0; rowIndex < len; rowIndex++) {
alignChar = $(wo.alignChar_wrap).length ? $(wo.alignChar_wrap).html(v.align)[0].outerHTML : v.align;
$row = rows.row ? rows.row[rowIndex] : rows.normalized[rowIndex][c.columns].$row;
$row.find('td').eq(v.column).html(
'<span class="ts-align-wrap"><span class="ts-align-left" style="' + wLeft + '">' + left[rowIndex] + '</span>' +
'<span class="ts-align-right" style="' + wRight + '">' + alignChar +
right[rowIndex].slice(v.align.length) + '</span></span>'
);
}
}
wo.alignChar_initialized = true;
},
remove: function(table, c, column){
if ($.isEmptyObject(c.cache)) { return; }
var tbodyIndex, rowIndex, len, rows, $row, $cell;
for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++){
rows = c.cache[tbodyIndex];
len = rows.normalized.length;
for (rowIndex = 0; rowIndex < len; rowIndex++) {
$row = rows.row ? rows.row[rowIndex] : rows.normalized[rowIndex][c.columns].$row;
$cell = $row.find('td').eq(column);
$cell.html( $cell.text().replace(/\s/g, ' ') );
}
}
}
};
ts.addWidget({
id: 'alignChar',
priority: 100,
options: {
alignChar_wrap : '',
alignChar_charAttrib : 'data-align-char',
alignChar_indexAttrib : 'data-align-index',
alignChar_adjustAttrib : 'data-align-adjust' // percentage width adjustments
},
init: function(table, thisWidget, c, wo){
wo.alignChar_initialized = false;
wo.alignChar_savedVars = [];
ts.alignChar.init(table, c, wo);
c.$table.on('pagerEnd refreshAlign', function(){
c.$headers.filter('[' + wo.alignChar_charAttrib + ']').each(function(){
ts.alignChar.remove(table, c, this.column);
});
ts.alignChar.init(table, c, wo);
});
},
format : function(table, c, wo){
// reinitialize in case table is empty when first initialized
if (!wo.alignChar_initialized) {
c.$table.trigger('refreshAlign');
}
},
remove : function(table, c, wo){
c.$headers.filter('[' + wo.alignChar_charAttrib + ']').each(function(){
ts.alignChar.remove(table, c, this.column);
});
wo.alignChar_initialized = false;
}
});
})(jQuery);

View File

@@ -0,0 +1,453 @@
/*! Build Table widget for tableSorter v2.16.0 (4/23/2014)
* 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.buildTable = 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.buildTable.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.build = {
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) {
if (/<\s*\/t(d|h)\s*>/.test(h)) {
t += h;
} else {
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) {
// test if HTML is already included; look for closing </td>
if (/<\s*\/t(d|h)\s*>/.test(item)) {
t += item;
} else {
t += '<' + (ftr ? h + (c && c[i] ? ' class="' + c[i] + '"' : '') : h) + '>' +
(ftr && txt && txt.length && txt[i] ? txt[i] : item) + '</' + h + '>';
}
});
return t + '</tr>';
}
};
bt.buildComplete = 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.build.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.build.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.build.rows(items, wo.build_footers.text, c, wo, printedLines, infooter);
}
}
});
tableHTML += (f > 0 ? '' : '</tbody>');
if (error) {
$t.html(error);
} else {
$t.html(tableHTML);
bt.buildComplete(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.buildComplete(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.build.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){
var j;
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.build.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.build.cell(d[j], wo, 'th', j);
if ($tb[0] && $tb[0].length) { $tb[0].appendTo( $tr ); } // add cell
}
});
}
}
$(table).html( $t.html() );
bt.buildComplete(table, wo);
};
bt.ajax = bt.json = function(table, data, wo) {
return bt.object(table, data, wo);
};
})(jQuery);

View File

@@ -0,0 +1,317 @@
/* Column Selector/Responsive table widget (beta) for TableSorter 5/22/2014 (v2.17.0)
* Requires tablesorter v2.8+ and jQuery 1.7+
* by Justin Hallett & Rob Garrison
*/
/*jshint browser:true, jquery:true, unused:false */
/*global jQuery: false */
;(function($){
"use strict";
var ts = $.tablesorter,
namespace = '.tscolsel',
tsColSel = ts.columnSelector = {
queryAll : '@media only all { [columns] { display: none; } }',
queryBreak : '@media all and (min-width: [size]) { [columns] { display: table-cell; } }',
init: function(table, c, wo) {
var $t, colSel;
// abort if no input is contained within the layout
$t = $(wo.columnSelector_layout);
if (!$t.find('input').add( $t.filter('input') ).length) {
if (c.debug) {
ts.log('*** ERROR: Column Selector aborting, no input found in the layout! ***');
}
return;
}
// unique table class name
c.tableId = 'tablesorter' + new Date().getTime();
c.$table.addClass( c.tableId );
// build column selector/state array
colSel = c.selector = { $container : $(wo.columnSelector_container || '<div>') };
colSel.$style = $('<style></style>').prop('disabled', true).appendTo('head');
colSel.$breakpoints = $('<style></style>').prop('disabled', true).appendTo('head');
colSel.isInitializing = true;
tsColSel.setupSelector(table, c, wo);
if (wo.columnSelector_mediaquery) {
tsColSel.setupBreakpoints(c, wo);
}
colSel.isInitializing = false;
if (colSel.$container.length) {
tsColSel.updateCols(c, wo);
}
c.$table
.off('refreshColumnSelector' + namespace)
.on('refreshColumnSelector' + namespace, function(){
// make sure we're using current config settings
var c = this.config;
tsColSel.updateBreakpoints(c, c.widgetOptions);
tsColSel.updateCols(c, c.widgetOptions);
});
},
setupSelector: function(table, c, wo) {
var name,
colSel = c.selector,
$container = colSel.$container,
useStorage = wo.columnSelector_saveColumns && ts.storage,
// get stored column states
saved = useStorage ? ts.storage( table, 'tablesorter-columnSelector' ) : [],
state = useStorage ? ts.storage( table, 'tablesorter-columnSelector-auto') : {};
// initial states
colSel.auto = $.isEmptyObject(state) || $.type(state.auto) !== "boolean" ? wo.columnSelector_mediaqueryState : state.auto;
colSel.states = [];
colSel.$column = [];
colSel.$wrapper = [];
colSel.$checkbox = [];
// populate the selector container
c.$table.children('thead').find('tr:first th', table).each(function() {
var $this = $(this),
// if no data-priority is assigned, default to 1, but don't remove it from the selector list
priority = $this.attr(wo.columnSelector_priority) || 1,
colId = $this.attr('data-column'),
state = ts.getData(this, c.headers[colId], 'columnSelector');
// if this column not hidable at all
// include getData check (includes "columnSelector-false" class, data attribute, etc)
if ( isNaN(priority) && priority.length > 0 || state === 'disable' ||
( wo.columnSelector_columns[colId] && wo.columnSelector_columns[colId] === 'disable') ) {
return true; // goto next
}
// set default state; storage takes priority
colSel.states[colId] = saved && typeof(saved[colId]) !== 'undefined' ?
saved[colId] : typeof(wo.columnSelector_columns[colId]) !== 'undefined' ?
wo.columnSelector_columns[colId] : (state === 'true' || !(state === 'false'));
colSel.$column[colId] = $(this);
// set default col title
name = $this.attr(wo.columnSelector_name) || $this.text();
if ($container.length) {
colSel.$wrapper[colId] = $(wo.columnSelector_layout.replace(/\{name\}/g, name)).appendTo($container);
colSel.$checkbox[colId] = colSel.$wrapper[colId]
// input may not be wrapped within the layout template
.find('input').add( colSel.$wrapper[colId].filter('input') )
.attr('data-column', colId)
.prop('checked', colSel.states[colId])
.on('change', function(){
colSel.states[colId] = this.checked;
tsColSel.updateCols(c, wo);
}).change();
}
});
},
setupBreakpoints: function(c, wo){
var colSel = c.selector;
// add responsive breakpoints
if (wo.columnSelector_mediaquery) {
// used by window resize function
colSel.lastIndex = -1;
wo.columnSelector_breakpoints.sort();
tsColSel.updateBreakpoints(c, wo);
c.$table
.off('updateAll' + namespace)
.on('updateAll' + namespace, function(){
tsColSel.updateBreakpoints(c, wo);
tsColSel.updateCols(c, wo);
});
}
if (colSel.$container.length) {
// Add media queries toggle
if (wo.columnSelector_mediaquery) {
colSel.$auto = $( wo.columnSelector_layout.replace(/\{name\}/g, wo.columnSelector_mediaqueryName) ).prependTo(colSel.$container);
colSel.$auto
// needed in case the input in the layout is not wrapped
.find('input').add( colSel.$auto.filter('input') )
.attr('data-column', 'auto')
.prop('checked', colSel.auto)
.on('change', function(){
colSel.auto = this.checked;
$.each( colSel.$checkbox, function(i, $cb){
if ($cb) {
$cb[0].disabled = colSel.auto;
colSel.$wrapper[i].toggleClass('disabled', colSel.auto);
}
});
if (wo.columnSelector_mediaquery) {
tsColSel.updateBreakpoints(c, wo);
}
tsColSel.updateCols(c, wo);
// copy the column selector to a popup/tooltip
if (c.selector.$popup) {
c.selector.$popup.find('.tablesorter-column-selector')
.html( colSel.$container.html() )
.find('input').each(function(){
var indx = $(this).attr('data-column');
$(this).prop( 'checked', indx === 'auto' ? colSel.auto : colSel.states[indx] );
});
}
if (wo.columnSelector_saveColumns && ts.storage) {
ts.storage( c.$table[0], 'tablesorter-columnSelector-auto', { auto : colSel.auto } );
}
}).change();
}
// Add a bind on update to re-run col setup
c.$table.off('update' + namespace).on('update' + namespace, function() {
tsColSel.updateCols(c, wo);
});
}
},
updateBreakpoints: function(c, wo) {
var priority, column, breaks,
colSel = c.selector,
prefix = '.' + c.tableId,
mediaAll = [],
breakpts = '';
if (wo.columnSelector_mediaquery && !colSel.auto) {
colSel.$breakpoints.prop('disabled', true);
colSel.$style.prop('disabled', false);
return;
}
// only 6 breakpoints (same as jQuery Mobile)
for (priority = 0; priority < 6; priority++){
/*jshint loopfunc:true */
breaks = [];
c.$headers.filter('[' + wo.columnSelector_priority + '=' + (priority + 1) + ']').each(function(){
column = parseInt($(this).attr('data-column'), 10) + 1;
breaks.push(prefix + ' tr th:nth-child(' + column + ')');
breaks.push(prefix + ' tr td:nth-child(' + column + ')');
});
if (breaks.length) {
mediaAll = mediaAll.concat( breaks );
breakpts += tsColSel.queryBreak
.replace(/\[size\]/g, wo.columnSelector_breakpoints[priority])
.replace(/\[columns\]/g, breaks.join(','));
}
}
if (colSel.$style) {
colSel.$style.prop('disabled', true);
}
colSel.$breakpoints
.prop('disabled', false)
.html( tsColSel.queryAll.replace(/\[columns\]/g, mediaAll.join(',')) + breakpts );
},
updateCols: function(c, wo) {
if (wo.columnSelector_mediaquery && c.selector.auto || c.selector.isInitializing) {
return;
}
var column,
colSel = c.selector,
styles = [],
prefix = '.' + c.tableId;
colSel.$container.find('input[data-column]').filter('[data-column!="auto"]').each(function(){
if (!this.checked) {
column = parseInt( $(this).attr('data-column'), 10 ) + 1;
styles.push(prefix + ' tr th:nth-child(' + column + ')');
styles.push(prefix + ' tr td:nth-child(' + column + ')');
}
});
if (wo.columnSelector_mediaquery){
colSel.$breakpoints.prop('disabled', true);
}
if (colSel.$style) {
colSel.$style.prop('disabled', false).html( styles.length ? styles.join(',') + ' { display: none; }' : '' );
}
if (wo.columnSelector_saveColumns && ts.storage) {
ts.storage( c.$table[0], 'tablesorter-columnSelector', colSel.states );
}
},
attachTo : function(table, elm) {
table = $(table)[0];
var colSel, wo, indx,
c = table.config,
$popup = $(elm);
if ($popup.length && c) {
if (!$popup.find('.tablesorter-column-selector').length) {
// add a wrapper to add the selector into, in case the popup has other content
$popup.append('<span class="tablesorter-column-selector"></span>');
}
colSel = c.selector;
wo = c.widgetOptions;
$popup.find('.tablesorter-column-selector')
.html( colSel.$container.html() )
.find('input').each(function(){
var indx = $(this).attr('data-column');
$(this).prop( 'checked', indx === 'auto' ? colSel.auto : colSel.states[indx] );
});
colSel.$popup = $popup.on('change', 'input', function(){
// data input
indx = $(this).attr('data-column');
// update original popup
colSel.$container.find('input[data-column="' + indx + '"]')
.prop('checked', this.checked)
.trigger('change');
});
}
}
};
ts.addWidget({
id: "columnSelector",
priority: 10,
options: {
// target the column selector markup
columnSelector_container : null,
// column status, true = display, false = hide
// disable = do not display on list
columnSelector_columns : {},
// remember selected columns
columnSelector_saveColumns: true,
// container layout
columnSelector_layout : '<label><input type="checkbox">{name}</label>',
// data attribute containing column name to use in the selector container
columnSelector_name : 'data-selector-name',
/* Responsive Media Query settings */
// enable/disable mediaquery breakpoints
columnSelector_mediaquery: true,
// toggle checkbox name
columnSelector_mediaqueryName: 'Auto: ',
// breakpoints checkbox initial setting
columnSelector_mediaqueryState: true,
// responsive table hides columns with priority 1-6 at these breakpoints
// see http://view.jquerymobile.com/1.3.2/dist/demos/widgets/table-column-toggle/#Applyingapresetbreakpoint
// *** set to false to disable ***
columnSelector_breakpoints : [ '20em', '30em', '40em', '50em', '60em', '70em' ],
// data attribute containing column priority
// duplicates how jQuery mobile uses priorities:
// http://view.jquerymobile.com/1.3.2/dist/demos/widgets/table-column-toggle/
columnSelector_priority : 'data-priority'
},
init: function(table, thisWidget, c, wo) {
tsColSel.init(table, c, wo);
},
remove: function(table, c){
var csel = c.selector;
csel.$container.empty();
if (csel.$popup) { csel.$popup.empty(); }
csel.$style.remove();
csel.$breakpoints.remove();
c.$table.off('updateAll' + namespace + ' update' + namespace);
}
});
})(jQuery);

View File

@@ -0,0 +1,70 @@
/*! tablesorter CSS Sticky Headers widget - updated 5/5/2014 (v2.16.4)
* Requires a modern browser, tablesorter v2.8+
*/
/*jshint jquery:true, unused:false */
;(function($){
"use strict";
$.tablesorter.addWidget({
id: "cssStickyHeaders",
priority: 10,
options: {
cssStickyHeaders_offset : 0,
cssStickyHeaders_addCaption : false,
cssStickyHeaders_attachTo : null,
cssStickyHeaders_filteredToTop : true,
cssStickyHeaders_zIndex : 10
},
init : function(table, thisWidget, c, wo) {
var $attach = $(wo.cssStickyHeaders_attachTo),
namespace = '.cssstickyheader',
$thead = c.$table.children('thead'),
$caption = c.$table.find('caption'),
$win = $attach.length ? $attach : $(window);
$win.bind('scroll resize '.split(' ').join(namespace + ' '), function() {
var top = $attach.length ? $attach.offset().top : $win.scrollTop(),
// add caption height; include table padding top & border-spacing or text may be above the fold (jQuery UI themes)
// border-spacing needed in Firefox, but not webkit... not sure if I should account for that
captionTop = wo.cssStickyHeaders_addCaption ? $caption.outerHeight(true) +
(parseInt(c.$table.css('padding-top'), 10) || 0) + (parseInt(c.$table.css('border-spacing'), 10) || 0) : 0,
bottom = c.$table.height() - $thead.height() - (c.$table.find('tfoot').height() || 0) - captionTop,
deltaY = top - $thead.offset().top + (parseInt(c.$table.css('border-top-width'), 10) || 0) +
(wo.cssStickyHeaders_offset || 0) + captionTop,
finalY = (deltaY > 0 && deltaY <= bottom ? deltaY : 0),
// IE can only transform header cells - fixes #447 thanks to @gakreol!
$cells = $thead.children().children();
if (wo.cssStickyHeaders_addCaption) {
$cells = $cells.add($caption);
}
$cells.css({
"position" : "relative",
"z-index" : wo.cssStickyHeaders_zIndex,
"transform" : finalY === 0 ? "" : "translate(0px," + finalY + "px)",
"-ms-transform" : finalY === 0 ? "" : "translate(0px," + finalY + "px)",
"-webkit-transform" : finalY === 0 ? "" : "translate(0px," + finalY + "px)"
});
});
c.$table.bind('filterEnd', function() {
if (wo.cssStickyHeaders_filteredToTop) {
// scroll top of table into view
window.scrollTo(0, c.$table.position().top);
}
});
},
remove: function(table, c, wo){
var namespace = '.cssstickyheader';
$(window).unbind('scroll resize '.split(' ').join(namespace + ' '));
c.$table
.unbind('update updateAll '.split(' ').join(namespace + ' '))
.children('thead, caption').css({
"position" : "",
"z-index" : "",
"transform" : "",
"-ms-transform" : "",
"-webkit-transform" : ""
});
}
});
})(jQuery);

View File

@@ -0,0 +1,200 @@
/*! tablesorter Editable Content widget - updated 9/15/2014 (core v2.17.8)
* 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.addWidget({
id: 'editable',
options : {
editable_columns : [],
editable_enterToAccept : true,
editable_autoAccept : true,
editable_autoResort : false,
editable_wrapContent : '<div>', // wrap the cell content... makes this widget work in IE, and with autocomplete
editable_trimContent : true, // trim content inside of contenteditable (remove tabs & carriage returns)
editable_validate : null, // function(text, originalText){ return text; }
editable_focused : null, // function(text, columnIndex, $element) {}
editable_blur : null, // function(text, columnIndex, $element) { }
editable_selectAll : false, // true/false or function(text, columnIndex, $element) { return true; }
editable_noEdit : 'no-edit',
editable_editComplete : 'editComplete'
},
init: function(table, thisWidget, c, wo){
if ( !wo.editable_columns.length ) { return; }
var indx, tmp, $t,
cols = [],
editComplete = function($cell, refocus){
$cell
.removeClass('tseditable-last-edited-cell')
.trigger( wo.editable_editComplete, [c] );
// restore focus last cell after updating
if (refocus) {
setTimeout(function(){
$cell.focus();
}, 50);
}
},
selectAll = function(cell){
setTimeout(function(){
// select all text in contenteditable
// see http://stackoverflow.com/a/6150060/145346
var range = document.createRange();
range.selectNodeContents(cell);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
}, 100);
};
if ( $.type(wo.editable_columns) === "string" && wo.editable_columns.indexOf('-') >= 0 ) {
// editable_columns can contain a range string (i.e. "2-4" )
tmp = wo.editable_columns.split('-');
indx = parseInt(tmp[0],10) || 0;
tmp = parseInt(tmp[1],10) || (c.columns - 1);
if ( tmp > c.columns ) { tmp = c.columns - 1; }
for ( ; indx <= tmp; indx++ ) {
cols.push('td:nth-child(' + (indx + 1) + ')');
}
} else if ( $.isArray(wo.editable_columns) ) {
$.each(wo.editable_columns, function(i, col){
if ( col < c.columns ) {
cols.push('td:nth-child(' + (col + 1) + ')');
}
});
}
tmp = $('<div>').wrapInner(wo.editable_wrapContent).children().length || $.isFunction(wo.editable_wrapContent);
// 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);
if (tmp && $t.children().length === 0) {
$t.wrapInner( wo.editable_wrapContent );
}
if ($t.children().length) {
// make all children content editable
$t.children().not('.' + wo.editable_noEdit).each(function(){
var $this = $(this);
if (wo.editable_trimContent) {
$this.text(function(i, txt){
return $.trim(txt);
});
}
$this.prop( 'contenteditable', true );
});
} else {
if (wo.editable_trimContent) {
$t.text(function(i, txt){
return $.trim(txt);
});
}
$t.prop( 'contenteditable', true );
}
});
c.$tbodies
.on('mouseleave.tseditable', function(){
if ( c.$table.data('contentFocused') ) {
// change to "true" instead of element to allow focusout to process
c.$table.data( 'contentFocused', true );
$(':focus').trigger('focusout');
}
})
.on('focus.tseditable', '[contenteditable]', function(e){
clearTimeout( $(this).data('timer') );
c.$table.data( 'contentFocused', e.target );
var $this = $(this),
selAll = wo.editable_selectAll,
column = $this.closest('td').index(),
txt = $.trim( $this.text() );
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 : txt, original: txt });
if (typeof wo.editable_focused === 'function') {
wo.editable_focused( txt, column, $this );
}
if (selAll) {
if (typeof selAll === 'function') {
if ( selAll( txt, column, $this ) ) {
selectAll($this[0]);
}
} else {
selectAll($this[0]);
}
}
})
.on('blur focusout keydown '.split(' ').join('.tseditable '), '[contenteditable]', function(e){
if ( !c.$table.data('contentFocused') ) { return; }
var t, validate,
valid = false,
$this = $(e.target),
txt = $.trim( $this.text() ),
column = $this.closest('td').index();
if ( e.which === 27 ) {
// user cancelled
$this.html( $.trim( $this.data('original') ) ).trigger('blur.tseditable');
c.$table.data( 'contentFocused', false );
return false;
}
// accept on enter (if set), alt-enter (always) or if autoAccept is set and element is blurred or unfocused
t = e.which === 13 && ( wo.editable_enterToAccept || e.altKey ) || wo.editable_autoAccept && e.type !== 'keydown';
// change if new or user hits enter (if option set)
if ( t && $this.data('before') !== txt ) {
validate = wo.editable_validate;
valid = txt;
if (typeof(validate) === "function") {
valid = validate( txt, $this.data('original'), column, $this );
} else if (typeof (validate = $.tablesorter.getColumnData( table, validate, column )) === 'function') {
valid = validate( txt, $this.data('original'), column, $this );
}
if ( t && valid !== false ) {
c.$table.find('.tseditable-last-edited-cell').removeClass('tseditable-last-edited-cell');
$this
.addClass('tseditable-last-edited-cell')
.html( $.trim( valid ) )
.data('before', valid)
.data('original', valid)
.trigger('change');
c.$table.trigger('updateCell', [ $this.closest('td'), false, function(){
if (wo.editable_autoResort) {
setTimeout(function(){
c.$table.trigger("sorton", [ c.sortList, function(){
editComplete(c.$table.find('.tseditable-last-edited-cell'), true);
}, true ]);
}, 10);
} else {
editComplete(c.$table.find('.tseditable-last-edited-cell'));
}
} ]);
return false;
}
} else if ( !valid && e.type !== 'keydown' ) {
clearTimeout( $this.data('timer') );
$this.data('timer', setTimeout(function(){
if ($.isFunction(wo.editable_blur)) {
wo.editable_blur( $.trim( $this.text() ), column, $this );
}
}, 100));
// restore original content on blur
$this.html( $.trim( $this.data('original') ) );
}
});
}
});
})(jQuery);

View File

@@ -0,0 +1,249 @@
/*! tablesorter Grouping widget - updated 3/7/2014 (core v2.15.6)
* 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 = {
types : {
number : function(c, $column, txt, num, group){
var value, word;
if (num > 1 && txt !== '') {
if ($column.hasClass(ts.css.sortAsc)) {
value = Math.floor(parseFloat(txt)/num) * num;
return value > parseFloat(group || 0) ? value : parseFloat(group || 0);
} else {
value = Math.ceil(parseFloat(txt)/num) * num;
return value < parseFloat(group || num) - value ? parseFloat(group || num) - value : value;
}
} else {
word = (txt + '').match(/\d+/g);
return word && word.length >= num ? word[num - 1] : txt || '';
}
},
separator : function(c, $column, txt, num){
var word = (txt + '').split(c.widgetOptions.group_separator);
return $.trim(word && num > 0 && word.length >= num ? word[(num || 1) - 1] : '');
},
word : function(c, $column, txt, num){
var word = (txt + ' ').match(/\w+/g);
return word && word.length >= num ? word[num - 1] : txt || '';
},
letter : function(c, $column, txt, num){
return txt ? (txt + ' ').substring(0, num) : '';
},
date : function(c, $column, txt, part, group){
var wo = c.widgetOptions,
time = new Date(txt || ''),
hours = time.getHours();
return part === 'year' ? time.getFullYear() :
part === 'month' ? wo.group_months[time.getMonth()] :
part === 'day' ? wo.group_months[time.getMonth()] + ' ' + time.getDate() :
part === 'week' ? wo.group_week[time.getDay()] :
part === 'time' ? ('00' + (hours > 12 ? hours - 12 : hours === 0 ? hours + 12 : hours)).slice(-2) + ':' +
('00' + time.getMinutes()).slice(-2) + ' ' + ('00' + wo.group_time[hours >= 12 ? 1 : 0]).slice(-2) :
wo.group_dateString(time);
}
},
update : function(table, c, wo){
if ($.isEmptyObject(c.cache)) { return; }
var rowIndex, tbodyIndex, currentGroup, $rows, groupClass, grouping, time, cache, saveName, direction,
lang = wo.grouping_language,
group = '',
savedGroup = false,
column = 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)
c.$table.data('pagerSavedHeight', 0);
}
if (column >= 0 && !c.$headers.filter('[data-column="' + column + '"]:last').hasClass('group-false')) {
if (c.debug){ time = new Date(); }
wo.group_currentGroup = ''; // save current groups
wo.group_currentGroups = {};
// group class finds "group-{word/separator/letter/number/date/false}-{optional:#/year/month/day/week/time}"
groupClass = (c.$headers.filter('[data-column="' + column + '"]:last').attr('class') || '').match(/(group-\w+(-\w+)?)/g);
// grouping = [ 'group', '{word/separator/letter/number/date/false}', '{#/year/month/day/week/time}' ]
grouping = groupClass ? groupClass[0].split('-') : ['group','letter',1]; // default to letter 1
// save current grouping
if (wo.group_collapsible && wo.group_saveGroups && ts.storage) {
wo.group_currentGroups = ts.storage( table, 'tablesorter-groups' ) || {};
// include direction when grouping numbers > 1 (reversed direction shows different range values)
direction = (grouping[1] === 'number' && grouping[2] > 1) ? 'dir' + c.sortList[0][1] : '';
// combine column, sort direction & grouping as save key
saveName = wo.group_currentGroup = '' + column + direction + grouping.join('');
if (!wo.group_currentGroups[saveName]) {
wo.group_currentGroups[saveName] = [];
} else {
savedGroup = true;
}
}
for (tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++) {
cache = c.cache[tbodyIndex].normalized;
group = ''; // clear grouping across tbodies
$rows = c.$tbodies.eq(tbodyIndex).children('tr').not('.' + c.cssChildRow);
for (rowIndex = 0; rowIndex < $rows.length; rowIndex++) {
if ( $rows.eq(rowIndex).is(':visible') ) {
// fixes #438
if (ts.grouping.types[grouping[1]]) {
currentGroup = cache[rowIndex] ?
ts.grouping.types[grouping[1]]( c, c.$headers.filter('[data-column="' + column + '"]:last'), cache[rowIndex][column], /date/.test(groupClass) ?
grouping[2] : parseInt(grouping[2] || 1, 10) || 1, group, lang ) : currentGroup;
if (group !== currentGroup) {
group = currentGroup;
// show range if number > 1
if (grouping[1] === 'number' && grouping[2] > 1 && currentGroup !== '') {
currentGroup += ' - ' + (parseInt(currentGroup, 10) +
((parseInt(grouping[2],10) - 1) * (c.$headers.filter('[data-column="' + column + '"]:last').hasClass(ts.css.sortAsc) ? 1 : -1)));
}
if ($.isFunction(wo.group_formatter)) {
currentGroup = wo.group_formatter((currentGroup || '').toString(), column, table, c, wo) || currentGroup;
}
$rows.eq(rowIndex).before('<tr class="group-header ' + c.selectorRemove.slice(1) +
'" unselectable="on"><td colspan="' +
c.columns + '">' + (wo.group_collapsible ? '<i/>' : '') + '<span class="group-name">' +
currentGroup + '</span><span class="group-count"></span></td></tr>');
if (wo.group_saveGroups && !savedGroup && wo.group_collapsed && wo.group_collapsible) {
// all groups start collapsed
wo.group_currentGroups[wo.group_currentGroup].push(currentGroup);
}
}
}
}
}
}
c.$table.find('tr.group-header')
.bind('selectstart', false)
.each(function(){
var isHidden, $label, name,
$row = $(this),
$rows = $row.nextUntil('tr.group-header').filter(':visible');
if (wo.group_count || $.isFunction(wo.group_callback)) {
$label = $row.find('.group-count');
if ($label.length) {
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, column, table);
}
}
}
if (wo.group_saveGroups && wo.group_currentGroups[wo.group_currentGroup].length) {
name = $row.find('.group-name').text().toLowerCase();
isHidden = $.inArray( name, wo.group_currentGroups[wo.group_currentGroup] ) > -1;
$row.toggleClass('collapsed', isHidden);
$rows.toggleClass('group-hidden', isHidden);
} else if (wo.group_collapsed && wo.group_collapsible) {
$row.addClass('collapsed');
$rows.addClass('group-hidden');
}
});
c.$table.trigger(wo.group_complete);
if (c.debug) {
$.tablesorter.benchmark("Applying groups widget: ", time);
}
}
},
bindEvents : function(table, c, wo){
if (wo.group_collapsible) {
wo.group_currentGroups = [];
// .on() requires jQuery 1.7+
c.$table.on('click toggleGroup', 'tr.group-header', function(event){
event.stopPropagation();
var isCollapsed, $groups, indx,
$this = $(this),
name = $this.find('.group-name').text().toLowerCase();
// use shift-click to toggle ALL groups
if (event.type === 'click' && event.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') );
// save collapsed groups
if (wo.group_saveGroups && ts.storage) {
$groups = c.$table.find('.group-header');
isCollapsed = $this.hasClass('collapsed');
if (!wo.group_currentGroups[wo.group_currentGroup]) {
wo.group_currentGroups[wo.group_currentGroup] = [];
}
if (isCollapsed && wo.group_currentGroup) {
wo.group_currentGroups[wo.group_currentGroup].push( name );
} else if (wo.group_currentGroup) {
indx = $.inArray( name, wo.group_currentGroups[wo.group_currentGroup] );
if (indx > -1) {
wo.group_currentGroups[wo.group_currentGroup].splice( indx, 1 );
}
}
ts.storage( table, 'tablesorter-groups', wo.group_currentGroups );
}
});
}
$(wo.group_saveReset).on('click', function(){
ts.grouping.clearSavedGroups(table);
});
c.$table.on('pagerChange.tsgrouping', function(){
ts.grouping.update(table, c, wo);
});
},
clearSavedGroups: function(table){
if (table && ts.storage) {
ts.storage(table, 'tablesorter-groups', '');
ts.grouping.update(table, table.config, table.config.widgetOptions);
}
}
};
ts.addWidget({
id: 'group',
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_saveGroups : true, // remember collapsed groups
group_saveReset : null, // element to clear saved collapsed groups
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, column, 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' ],
// this function is used when "group-date" is set to create the date string
// you can just return date, date.toLocaleString(), date.toLocaleDateString() or d.toLocaleTimeString()
// reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#Conversion_getter
group_dateString : function(date) { return date.toLocaleString(); }
},
init: function(table, thisWidget, c, wo){
ts.grouping.bindEvents(table, c, wo);
},
format: function(table, c, wo) {
ts.grouping.update(table, c, wo);
},
remove : function(table, c, wo){
c.$table
.off('click', 'tr.group-header')
.off('pagerChange.tsgrouping')
.find('.group-hidden').removeClass('group-hidden').end()
.find('tr.group-header').remove();
}
});
})(jQuery);

View File

@@ -0,0 +1,91 @@
/*! tablesorter headerTitles widget - updated 3/5/2014 (core v2.15.6)
* 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.addWidget({
id: 'headerTitles',
options: {
// use aria-label text
// e.g. "First Name: Ascending sort applied, activate to apply a descending sort"
headerTitle_useAria : false,
// add tooltip class
headerTitle_tooltip : '',
// custom titles [ ascending, descending, unsorted ]
headerTitle_cur_text : [ ' sort: A - Z', ' sort: Z - A', 'ly unsorted' ],
headerTitle_cur_numeric : [ ' sort: 0 - 9', ' sort: 9 - 0', 'ly unsorted' ],
headerTitle_nxt_text : [ ' sort: A - Z', ' sort: Z - A', 'remove sort' ],
headerTitle_nxt_numeric : [ ' sort: 0 - 9', ' sort: 9 - 0', 'remove sort' ],
// title display; {prefix} adds above prefix
// {type} adds the current sort order from above (text or numeric)
// {next} adds the next sort direction using the sort order above
headerTitle_output_sorted : 'current{current}; activate to {next}',
headerTitle_output_unsorted : 'current{current}; activate to {next} ',
headerTitle_output_nosort : 'No sort available',
// use this type to override the parser detection result
// e.g. use for numerically parsed columns (e.g. dates), but you
// want the user to see a text sort, e.g. [ 'text', 'numeric' ]
headerTitle_type : [],
// manipulate the title as desired
headerTitle_callback : null // function($cell, txt) { return txt; }
},
init: function(table, thisWidget, c, wo){
// force refresh
c.$table.on('refreshHeaderTitle', function(){
thisWidget.format(table, c, wo);
});
// add tooltip class
if ($.isArray(wo.headerTitle_tooltip)) {
c.$headers.each(function(){
$(this).addClass( wo.headerTitle_tooltip[this.column] || '' );
});
} else if (wo.headerTitle_tooltip !== '') {
c.$headers.addClass( wo.headerTitle_tooltip );
}
},
format: function (table, c, wo) {
var txt;
c.$headers.each(function(){
var t = this,
$this = $(this),
sortType = wo.headerTitle_type[t.column] || c.parsers[ t.column ].type || 'text',
sortDirection = $this.hasClass(ts.css.sortAsc) ? 0 : $this.hasClass(ts.css.sortDesc) ? 1 : 2,
sortNext = t.order[(t.count + 1) % (c.sortReset ? 3 : 2)];
if (wo.headerTitle_useAria) {
txt = $this.hasClass('sorter-false') ? wo.headerTitle_output_nosort : $this.attr('aria-label') || '';
} else {
txt = (wo.headerTitle_prefix || '') + // now deprecated
($this.hasClass('sorter-false') ? wo.headerTitle_output_nosort :
ts.isValueInArray( t.column, c.sortList ) >= 0 ? wo.headerTitle_output_sorted : wo.headerTitle_output_unsorted);
txt = txt.replace(/\{(current|next|name)\}/gi, function(m){
return {
'{name}' : $this.text(),
'{current}' : wo[ 'headerTitle_cur_' + sortType ][ sortDirection ] || '',
'{next}' : wo[ 'headerTitle_nxt_' + sortType ][ sortNext ] || ''
}[m.toLowerCase()];
});
}
$this.attr('title', $.isFunction(wo.headerTitle_callback) ? wo.headerTitle_callback($this, txt) : txt);
});
},
remove: function (table, c, wo) {
c.$headers.attr('title', '');
c.$table.off('refreshHeaderTitle');
// remove tooltip class
if ($.isArray(wo.headerTitle_tooltip)) {
c.$headers.each(function(){
$(this).removeClass( wo.headerTitle_tooltip[this.column] || '' );
});
} else if (wo.headerTitle_tooltip !== '') {
c.$headers.removeClass( wo.headerTitle_tooltip );
}
}
});
})(jQuery);

View File

@@ -0,0 +1,413 @@
/*! tablesorter math widget - beta - updated 5/28/2014 (v2.17.1)
* Requires tablesorter v2.16+ and jQuery 1.7+
* by Rob Garrison
*/
/*jshint browser:true, jquery:true, unused:false */
/*global jQuery: false */
;(function($){
"use strict";
var ts = $.tablesorter,
math = {
// get all of the row numerical values in an arry
getRow : function(table, wo, $el, dataAttrib) {
var $t, txt,
c = table.config,
arry = [],
$row = $el.closest('tr'),
$cells = $row.children();
if (!$row.hasClass(wo.filter_filteredRow || 'filtered')) {
if (wo.math_ignore.length) {
$cells = $cells.not('[' + dataAttrib + '=ignore]').not('[data-column=' + wo.math_ignore.join('],[data-column=') + ']');
}
arry = $cells.not($el).map(function(){
$t = $(this);
txt = $t.attr(c.textAttribute);
if (typeof txt === "undefined") {
txt = this.textContent || $t.text();
}
txt = ts.formatFloat(txt.replace(/[^\w,. \-()]/g, ""), table);
return isNaN(txt) ? 0 : txt;
}).get();
}
return arry;
},
// get all of the column numerical values in an arry
getColumn : function(table, wo, $el, type, dataAttrib){
var i, txt, $t, len, mathAbove,
arry = [],
c = table.config,
filtered = wo.filter_filteredRow || 'filtered',
cIndex = parseInt( $el.attr('data-column'), 10 ),
$rows = c.$table.children('tbody').children(),
$row = $el.closest('tr');
// make sure tfoot rows are AFTER the tbody rows
// $rows.add( c.$table.children('tfoot').children() );
if (type === 'above') {
len = $rows.index($row);
i = len;
while (i >= 0) {
$t = $rows.eq(i).children().filter('[data-column=' + cIndex + ']');
mathAbove = $t.filter('[' + dataAttrib + '^=above]').length;
// ignore filtered rows & rows with data-math="ignore" (and starting row)
if ( ( !$rows.eq(i).hasClass(filtered) && $rows.eq(i).not('[' + dataAttrib + '=ignore]').length && i !== len ) || mathAbove && i !== len ) {
// stop calculating "above", when encountering another "above"
if (mathAbove) {
i = 0;
} else if ($t.length) {
txt = $t.attr(c.textAttribute);
if (typeof txt === "undefined") {
txt = $t[0].textContent || $t.text();
}
txt = ts.formatFloat(txt.replace(/[^\w,. \-()]/g, ""), table);
arry.push(isNaN(txt) ? 0 : txt);
}
}
i--;
}
} else {
$rows.each(function(){
$t = $(this).children().filter('[data-column=' + cIndex + ']');
if (!$(this).hasClass(filtered) && $t.not('[' + dataAttrib + '^=above],[' + dataAttrib + '^=col]').length && !$t.is($el)) {
txt = $t.attr(c.textAttribute);
if (typeof txt === "undefined") {
txt = ($t[0] ? $t[0].textContent : '') || $t.text();
}
txt = ts.formatFloat(txt.replace(/[^\w,. \-()]/g, ""), table);
arry.push(isNaN(txt) ? 0 : txt);
}
});
}
return arry;
},
// get all of the column numerical values in an arry
getAll : function(table, wo, dataAttrib){
var txt, $t, col,
arry = [],
c = table.config,
filtered = wo.filter_filteredRow || 'filtered',
$rows = c.$table.children('tbody').children();
$rows.each(function(){
if (!$(this).hasClass(filtered)) {
$(this).children().each(function(){
$t = $(this);
col = parseInt( $t.attr('data-column'), 10);
if (!$t.filter('[' + dataAttrib + ']').length && $.inArray(col, wo.math_ignore) < 0) {
txt = $t.attr(c.textAttribute);
if (typeof txt === "undefined") {
txt = ($t[0] ? $t[0].textContent : '') || $t.text();
}
txt = ts.formatFloat(txt.replace(/[^\w,. \-()]/g, ""), table);
arry.push(isNaN(txt) ? 0 : txt);
}
});
}
});
return arry;
},
recalculate : function(table, c, wo, init){
if (c && (!wo.math_isUpdating || init)) {
// add data-column attributes to all table cells
if (init) {
ts.computeColumnIndex( c.$table.children('tbody').children() );
}
// data-attribute name (defaults to data-math)
var dataAttrib = 'data-' + (wo.math_data || 'math'),
// all non-info tbody cells
$mathCells = c.$tbodies.find('[' + dataAttrib + ']');
math.mathType( table, wo, $mathCells, wo.math_priority, dataAttrib );
// only info tbody cells
$mathCells = c.$table.find('.' + c.cssInfoBlock + ', tfoot').find('[' + dataAttrib + ']');
math.mathType( table, wo, $mathCells, wo.math_priority, dataAttrib );
// find the "all" total
math.mathType( table, wo, c.$table.find('[' + dataAttrib + '^=all]'), ['all'], dataAttrib );
wo.math_isUpdating = true;
c.$table.trigger('update');
}
},
mathType : function(table, wo, $cells, priority, dataAttrib) {
if ($cells.length) {
var formula, t, $t, arry, getAll,
eq = ts.equations;
if (priority[0] === 'all') {
// no need to get all cells more than once
getAll = math.getAll(table, wo, dataAttrib);
}
$.each( priority, function(i, type) {
$cells.filter('[' + dataAttrib + '^=' + type + ']').each(function(){
$t = $(this);
formula = ($t.attr(dataAttrib) || '').replace(type + '-', '');
arry = (type === "row") ? math.getRow(table, wo, $t, dataAttrib) :
(type === "all") ? getAll : math.getColumn(table, wo, $t, type, dataAttrib);
if (eq[formula]) {
t = eq[formula](arry);
if (table.config.debug && console && console.log) {
console.log($t.attr(dataAttrib), arry, '=', t);
}
math.output( $t, wo, t, arry );
}
});
});
}
},
output : function($cell, wo, value, arry) {
// get mask from cell data-attribute: data-math-mask="#,##0.00"
var result = ts.formatMask( $cell.attr('data-' + wo.math_data + '-mask') || wo.math_mask, value, wo.math_wrapPrefix, wo.math_wrapSuffix );
if ($.isFunction(wo.math_complete)) {
result = wo.math_complete($cell, wo, result, value, arry);
}
if (result !== false) {
$cell.html(result);
}
}
};
// Modified from https://code.google.com/p/javascript-number-formatter/
/**
* @preserve IntegraXor Web SCADA - JavaScript Number Formatter
* http:// www.integraxor.com/
* author: KPL, KHL
* (c)2011 ecava
* Dual licensed under the MIT or GPL Version 2 licenses.
*/
ts.formatMask = function(m, v, tmpPrefix, tmpSuffix){
if ( !m || isNaN(+v) ) {
return v; // return as it is.
}
var isNegative, result, decimal, group, posLeadZero, posTrailZero, posSeparator, part, szSep,
integer, str, offset, i, l, len, start, tmp, end, inv,
prefix = '',
suffix = '';
// find prefix/suffix
len = m.length;
start = m.search( /[0-9\-\+#]/ );
tmp = start > 0 ? m.substring(0, start) : '';
prefix = tmp;
if ( start > 0 && tmpPrefix ) {
if ( /\{content\}/.test(tmpPrefix || '') ) {
prefix = (tmpPrefix || '').replace(/\{content\}/g, tmp || '');
} else {
prefix = (tmpPrefix || '') + tmp;
}
}
// reverse string: not an ideal method if there are surrogate pairs
inv = m.split('').reverse().join('');
end = inv.search( /[0-9\-\+#]/ );
i = len - end;
i += (m.substring( i, i + 1 ) === '.') ? 1 : 0;
tmp = end > 0 ? m.substring( i, len) : '';
suffix = tmp;
if (tmp !== '' && tmpSuffix) {
if ( /\{content\}/.test(tmpSuffix || '') ) {
suffix = (tmpSuffix || '').replace(/\{content\}/g, tmp || '');
} else {
suffix = tmp + (tmpSuffix || '');
}
}
m = m.substring(start, i);
// convert any string to number according to formation sign.
v = m.charAt(0) == '-' ? -v : +v;
isNegative = v < 0 ? v = -v : 0; // process only abs(), and turn on flag.
// search for separator for grp & decimal, anything not digit, not +/- sign, not #.
result = m.match( /[^\d\-\+#]/g );
decimal = ( result && result[result.length-1] ) || '.'; // treat the right most symbol as decimal
group = ( result && result[1] && result[0] ) || ','; // treat the left most symbol as group separator
// split the decimal for the format string if any.
m = m.split( decimal );
// Fix the decimal first, toFixed will auto fill trailing zero.
v = v.toFixed( m[1] && m[1].length );
v = +(v) + ''; // convert number to string to trim off *all* trailing decimal zero(es)
// fill back any trailing zero according to format
posTrailZero = m[1] && m[1].lastIndexOf('0'); // look for last zero in format
part = v.split('.');
// integer will get !part[1]
if ( !part[1] || ( part[1] && part[1].length <= posTrailZero ) ) {
v = (+v).toFixed( posTrailZero + 1 );
}
szSep = m[0].split( group ); // look for separator
m[0] = szSep.join(''); // join back without separator for counting the pos of any leading 0.
posLeadZero = m[0] && m[0].indexOf('0');
if ( posLeadZero > -1 ) {
while ( part[0].length < ( m[0].length - posLeadZero ) ) {
part[0] = '0' + part[0];
}
} else if ( +part[0] === 0 ) {
part[0] = '';
}
v = v.split('.');
v[0] = part[0];
// process the first group separator from decimal (.) only, the rest ignore.
// get the length of the last slice of split result.
posSeparator = ( szSep[1] && szSep[ szSep.length - 1 ].length );
if ( posSeparator ) {
integer = v[0];
str = '';
offset = integer.length % posSeparator;
l = integer.length;
for ( i = 0; i < l; i++ ) {
str += integer.charAt(i); // ie6 only support charAt for sz.
// -posSeparator so that won't trail separator on full length
/*jshint -W018 */
if ( !( ( i - offset + 1 ) % posSeparator ) && i < l - posSeparator ) {
str += group;
}
}
v[0] = str;
}
v[1] = ( m[1] && v[1] ) ? decimal + v[1] : "";
// put back any negation, combine integer and fraction, and add back prefix & suffix
return prefix + ( ( isNegative ? '-' : '' ) + v[0] + v[1] ) + suffix;
};
ts.equations = {
count : function(arry) {
return arry.length;
},
sum : function(arry) {
var total = 0;
$.each( arry, function(i) {
total += arry[i];
});
return total;
},
mean : function(arry) {
var total = ts.equations.sum( arry );
return total / arry.length;
},
median : function(arry) {
// https://gist.github.com/caseyjustus/1166258
arry.sort( function(a,b){ return a - b; } );
var half = Math.floor( arry.length / 2 );
return (arry.length % 2) ? arry[half] : ( arry[half - 1] + arry[half] ) / 2.0;
},
mode : function(arry) {
// http://stackoverflow.com/a/3451640/145346
if ( arry.length === 0 ) { return 'none'; }
var i, el,
modeMap = {},
maxCount = 1,
modes = [arry[0]];
for (i = 0; i < arry.length; i++) {
el = arry[i];
modeMap[el] = modeMap[el] ? modeMap[el] + 1 : 1;
if ( modeMap[el] > maxCount ) {
modes = [el];
maxCount = modeMap[el];
} else if (modeMap[el] === maxCount) {
modes.push(el);
maxCount = modeMap[el];
}
}
// returns arry of modes if there is a tie
return modes.sort( function(a,b){ return a - b; } );
},
max : function(arry) {
return Math.max.apply( Math, arry );
},
min : function(arry) {
return Math.min.apply( Math, arry );
},
range: function(arry) {
var v = arry.sort(function(a,b){ return a - b; });
return v[ arry.length - 1 ] - v[0];
},
// common variance equation
// (not accessible via data-attribute setting)
variance: function(arry, population) {
var avg = ts.equations.mean( arry ),
v = 0,
i = arry.length;
while (i--) {
v += Math.pow( ( arry[i] - avg ), 2 );
}
v /= ( arry.length - (population ? 0 : 1) );
return v;
},
// variance (population)
varp : function(arry) {
return ts.equations.variance(arry, true);
},
// variance (sample)
vars : function(arry) {
return ts.equations.variance(arry);
},
// standard deviation (sample)
stdevs : function(arry) {
var vars = ts.equations.variance(arry);
return Math.sqrt( vars );
},
// standard deviation (population)
stdevp : function(arry){
var varp = ts.equations.variance(arry, true);
return Math.sqrt( varp );
}
};
// add new widget called repeatHeaders
// ************************************
ts.addWidget({
id: "math",
priority: 100,
options: {
math_data : 'math',
// column index to ignore
math_ignore : [],
// mask info: https://code.google.com/p/javascript-number-formatter/
math_mask : '#,##0.00',
// complete executed after each fucntion
math_complete : null, // function($cell, wo, result, value, arry){ return result; },
// order of calculation; "all" is last
math_priority : [ 'row', 'above', 'col' ],
// template for or just prepend the mask prefix & suffix with this HTML
// e.g. '<span class="red">{content}</span>'
math_prefix : '',
math_suffix : ''
},
init : function(table, thisWidget, c, wo){
c.$table
.bind('tablesorter-initialized update updateRows addRows updateCell filterReset filterEnd '.split(' ').join('.tsmath '), function(e){
var init = e.type === 'tablesorter-initialized';
if (!wo.math_isUpdating || init) {
math.recalculate( table, c, wo, init );
}
})
.bind('updateComplete.tsmath', function(){
setTimeout(function(){
wo.math_isUpdating = false;
}, 500);
});
wo.math_isUpdating = false;
},
// 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, wo){
$(table)
.unbind('tablesorter-initialized update updateRows addRows updateCell filterReset filterEnd '.split(' ').join('.tsmath '))
.find('[data-' + wo.math_data + ']').empty();
}
});
})(jQuery);

View File

@@ -0,0 +1,316 @@
/* Output widget (beta) for TableSorter 7/17/2014 (v2.17.5)
* Requires tablesorter v2.8+ and jQuery 1.7+
* Modified from:
* HTML Table to CSV: http://www.kunalbabre.com/projects/table2CSV.php (License unknown?)
* Download-File-JS: https://github.com/PixelsCommander/Download-File-JS (http://www.apache.org/licenses/LICENSE-2.0)
*/
/*jshint browser:true, jquery:true, unused:false */
/*global jQuery: false */
;(function($){
"use strict";
var ts = $.tablesorter,
output = ts.output = {
event : 'outputTable',
// wrap line breaks & tabs in quotes
regexQuote : /([\n\t]|<[^<]+>)/, // test
regexBR : /(<br([\s\/])?>|\n)/g, // replace
regexIMG : /<img[^>]+alt\s*=\s*['"]([^'"]+)['"][^>]*>/i, // match
regexHTML : /<[^<]+>/g, // replace
replaceCR : '\\n',
replaceTab : '\\t',
popupTitle : 'Output',
popupStyle : 'width:100%;height:100%;', // for textarea
message : 'Your device does not support downloading. Please try again in desktop browser.',
init : function(c) {
c.$table
.off(output.event)
.on(output.event, function(){
// explicitly use table.config.widgetOptions because we want
// the most up-to-date values; not the "wo" from initialization
output.process(c, c.widgetOptions);
});
},
processRow: function(c, $rows, isHeader, isJSON) {
var $this, row, col, rowlen, collen, txt,
wo = c.widgetOptions,
tmpRow = [],
dupe = wo.output_duplicateSpans,
addSpanIndex = isHeader && isJSON && wo.output_headerRows && $.isFunction(wo.output_callbackJSON),
cellIndex = 0;
$rows.each(function(rowIndex) {
if (!tmpRow[rowIndex]) { tmpRow[rowIndex] = []; }
cellIndex = 0;
$(this).children().each(function(){
$this = $(this);
// process rowspans
if ($this.filter('[rowspan]').length) {
rowlen = parseInt( $this.attr('rowspan'), 10) - 1;
txt = output.formatData( wo, $this.attr(wo.output_dataAttrib) || $this.html(), isHeader );
for (row = 1; row <= rowlen; row++) {
if (!tmpRow[rowIndex + row]) { tmpRow[rowIndex + row] = []; }
tmpRow[rowIndex + row][cellIndex] = isHeader ? txt : dupe ? txt : '';
}
}
// process colspans
if ($this.filter('[colspan]').length) {
collen = parseInt( $this.attr('colspan'), 10) - 1;
txt = output.formatData( wo, $this.attr(wo.output_dataAttrib) || $this.html(), isHeader );
for (col = 1; col <= collen; col++) {
// if we're processing the header & making JSON, the header names need to be unique
if ($this.filter('[rowspan]').length) {
rowlen = parseInt( $this.attr('rowspan'), 10);
for (row = 0; row < rowlen; row++) {
if (!tmpRow[rowIndex + row]) { tmpRow[rowIndex + row] = []; }
tmpRow[rowIndex + row][cellIndex + col] = addSpanIndex ?
wo.output_callbackJSON($this, txt, cellIndex + col) || txt + '(' + (cellIndex + col) + ')' : isHeader ? txt : dupe ? txt : '';
}
} else {
tmpRow[rowIndex][cellIndex + col] = addSpanIndex ?
wo.output_callbackJSON($this, txt, cellIndex + col) || txt + '(' + (cellIndex + col) + ')' : isHeader ? txt : dupe ? txt : '';
}
}
}
// don't include hidden columns
if ( $this.css('display') !== 'none' ) {
// skip column if already defined
while (typeof tmpRow[rowIndex][cellIndex] !== 'undefined') { cellIndex++; }
tmpRow[rowIndex][cellIndex] = tmpRow[rowIndex][cellIndex] ||
output.formatData( wo, $this.attr(wo.output_dataAttrib) || $this.html(), isHeader );
cellIndex++;
}
});
});
return tmpRow;
},
ignoreColumns : function(wo, data) {
// ignore columns -> remove data from built array (because we've already processed any rowspan/colspan)
$.each( data, function(indx, val){
data[indx] = $.grep(val, function(v, cellIndx){
return $.inArray(cellIndx, wo.output_ignoreColumns) < 0;
});
});
return data;
},
process : function(c, wo) {
var mydata, $this, $rows, headers, csvData, len,
hasStringify = window.JSON && JSON.hasOwnProperty('stringify'),
indx = 0,
tmpData = (wo.output_separator || ',').toLowerCase(),
outputJSON = tmpData === 'json',
outputArray = tmpData === 'array',
separator = outputJSON || outputArray ? ',' : wo.output_separator,
$el = c.$table;
// regex to look for the set separator or HTML
wo.output_regex = new RegExp('(' + (/\\/.test(separator) ? '\\' : '' ) + separator + ')' );
// get header cells
$this = $el.find('thead tr:visible').not('.' + (ts.css.filterRow || 'tablesorter-filter-row') );
headers = output.processRow(c, $this, true, outputJSON);
// all tbody rows
$rows = $el.children('tbody').children('tr');
// get (f)iltered, (v)isible or all rows (look for the first letter only)
$rows = /f/.test(wo.output_saveRows) ? $rows.not('.' + (wo.filter_filteredRow || 'filtered') ) :
/v/.test(wo.output_saveRows) ? $rows.filter(':visible') : $rows;
// process to array of arrays
csvData = output.processRow(c, $rows);
len = headers.length;
if (wo.output_ignoreColumns.length) {
headers = output.ignoreColumns(wo, headers);
csvData = output.ignoreColumns(wo, csvData);
}
if (outputJSON) {
tmpData = [];
$.each( csvData, function(indx, val){
// multiple header rows & output_headerRows = true, pick the last row...
tmpData.push( output.row2Hash( headers[ (len > 1 && wo.output_headerRows) ? indx % len : len - 1], val ) );
});
// requires JSON stringify; if it doesn't exist, the output will show [object Object],... in the output window
mydata = hasStringify ? JSON.stringify(tmpData) : tmpData;
} else {
tmpData = output.row2CSV(wo, wo.output_headerRows ? headers : [ headers[ (len > 1 && wo.output_headerRows) ? indx % len : len - 1] ], outputArray)
.concat( output.row2CSV(wo, csvData, outputArray) );
// stringify the array; if stringify doesn't exist the array will be flattened
mydata = outputArray && hasStringify ? JSON.stringify(tmpData) : tmpData.join('\n');
}
// callback; if true returned, continue processing
if ($.isFunction(wo.output_callback) && !wo.output_callback(c, mydata)) { return; }
if ( /p/.test( (wo.output_delivery || '').toLowerCase() ) ) {
output.popup(mydata, wo.output_popupStyle, outputJSON || outputArray);
} else {
output.download(wo, mydata);
}
}, // end process
row2CSV : function(wo, tmpRow, outputArray) {
var tmp, rowIndex,
csvData = [],
rowLen = tmpRow.length;
for (rowIndex = 0; rowIndex < rowLen; rowIndex++) {
// remove any blank rows
tmp = tmpRow[rowIndex].join('').replace(/\"/g,'');
if (tmpRow[rowIndex].length > 0 && tmp !== '') {
csvData[csvData.length] = outputArray ? tmpRow[rowIndex] : tmpRow[rowIndex].join(wo.output_separator);
}
}
return csvData;
},
row2Hash : function(keys, values) {
var json = {};
$.each(values, function(indx, val) {
if ( indx < keys.length ) {
json[ keys[indx] ] = val;
}
});
return json;
},
formatData : function(wo, input, isHeader) {
var txt,
quotes = (wo.output_separator || ',').toLowerCase(),
separator = quotes === 'json' || quotes === 'array',
// replace " with “ if undefined
result = input.replace(/\"/g, wo.output_replaceQuote || '\u201c');
// replace line breaks with \\n & tabs with \\t
result = result.replace(output.regexBR, output.replaceCR).replace(/\t/g, output.replaceTab);
// extract img alt text
txt = result.match(output.regexIMG);
if (!wo.output_includeHTML && txt !== null) {
result = txt[1];
}
// replace/remove html
result = wo.output_includeHTML && !isHeader ? result : result.replace(output.regexHTML, '');
result = wo.output_trimSpaces || isHeader ? $.trim(result) : result;
// JSON & array outputs don't need quotes
quotes = separator ? false : wo.output_wrapQuotes || wo.output_regex.test(result) || output.regexQuote.test(result);
return quotes ? '"' + result + '"' : result;
},
popup : function(data, style, wrap) {
var generator = window.open('', output.popupTitle, style);
generator.document.write(
'<html><head><title>' + output.popupTitle + '</title></head><body>' +
'<textarea wrap="' + (wrap ? 'on' : 'off') + '" style="' + output.popupStyle + '">' + data + '\n</textarea>' +
'</body></html>'
);
generator.document.close();
generator.focus();
// select all text and focus within the textarea in the popup
// $(generator.document).find('textarea').select().focus();
return true;
},
// modified from https://github.com/PixelsCommander/Download-File-JS
// & http://html5-demos.appspot.com/static/a.download.html
download : function (wo, data){
var e, blob, gotBlob,
nav = window.navigator,
link = document.createElement('a');
// iOS devices do not support downloading. We have to inform user about this.
if (/(iP)/g.test(nav.userAgent)) {
alert(output.message);
return false;
}
// test for blob support
try {
gotBlob = !!new Blob();
} catch (err) {
gotBlob = false;
}
// Use HTML5 Blob if browser supports it
if ( gotBlob ) {
window.URL = window.webkitURL || window.URL;
blob = new Blob([data], {type: wo.output_encoding});
if (nav.msSaveBlob) {
// IE 10+
nav.msSaveBlob(blob, wo.output_saveFileName);
} else {
// all other browsers
link.href = window.URL.createObjectURL(blob);
link.download = wo.output_saveFileName;
// Dispatching click event; using $(link).trigger() won't work
if (document.createEvent) {
e = document.createEvent('MouseEvents');
// event.initMouseEvent(type, canBubble, cancelable, view, detail, screenX, screenY, clientX, clientY, ctrlKey, altKey, shiftKey, metaKey, button, relatedTarget);
e.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
link.dispatchEvent(e);
}
}
return false;
}
// fallback to force file download (whether supported by server).
// not sure if this actually works in IE9 and older...
window.open( wo.output_encoding + encodeURIComponent(data) + '?download' , '_self');
return true;
},
remove : function(c) {
c.$table.off(output.event);
}
};
ts.addWidget({
id: "output",
options: {
output_separator : ',', // set to "json", "array" or any separator
output_ignoreColumns : [], // columns to ignore [0, 1,... ] (zero-based index)
output_dataAttrib : 'data-name', // header attrib containing modified header name
output_headerRows : false, // if true, include multiple header rows (JSON only)
output_delivery : 'popup', // popup, download
output_saveRows : 'filtered', // all, visible or filtered
output_duplicateSpans: true, // duplicate output data in tbody colspan/rowspan
output_replaceQuote : '\u201c;', // left double quote
output_includeHTML : false,
output_trimSpaces : true,
output_wrapQuotes : false,
output_popupStyle : 'width=500,height=300',
output_saveFileName : 'mytable.csv',
// callback executed when processing completes
// return true to continue download/output
// return false to stop delivery & do something else with the data
output_callback : function(config, data){ return true; },
// JSON callback executed when a colspan is encountered in the header
output_callbackJSON : function($cell, txt, cellIndex) { return txt + '(' + (cellIndex) + ')'; },
// the need to modify this for Excel no longer exists
output_encoding : 'data:application/octet-stream;charset=utf8,'
},
init: function(table, thisWidget, c) {
output.init(c);
},
remove: function(table, c){
output.remove(c);
}
});
})(jQuery);

View File

@@ -0,0 +1,975 @@
/* Pager widget for TableSorter 9/15/2014 (v2.17.8) */
/*jshint browser:true, jquery:true, unused:false */
;(function($){
"use strict";
var tsp,
ts = $.tablesorter;
ts.addWidget({
id: "pager",
priority: 55, // load pager after filter widget
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,
// reset pager after filtering; set to desired page #
// set to false to not change page at filter start
pager_pageReset: 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,
//defines custom storage key
pager_storageKey: 'tablesorter-pager',
// 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'
},
// set this to false if you want to block ajax loading on init
pager_processAjaxOnInit: true,
// 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
gotoPage : '.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){
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,
startRow: 0,
endRow: 0,
ajaxCounter: 0,
$size: null,
last: {}
}, c.pager);
// pager initializes multiple times before table has completed initialization
if (p.isInitializing) { return; }
p.isInitializing = true;
if (c.debug) {
ts.log('Pager initializing');
}
p.size = $.data(table, 'pagerLastSize') || wo.pager_size;
// 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.gotoPage); // goto is a reserved word #657
// page size selector
p.$size = p.$container.find(s.pageSize);
p.totalRows = c.$tbodies.eq(0).children('tr').not( c.widgetOptions.pager_countChildRows ? '' : '.' + c.cssChildRow ).length;
p.oldAjaxSuccess = p.oldAjaxSuccess || wo.pager_ajaxObject.success;
c.appender = tsp.appender;
if (ts.filter && $.inArray('filter', c.widgets) >= 0) {
// get any default filter settings (data-value attribute) fixes #388
p.currentFilters = c.$table.data('lastSearch') || ts.filter.setDefaults(table, c, wo) || [];
// set, but don't apply current filters
ts.setFilters(table, p.currentFilters, false);
}
if (wo.pager_savePages && ts.storage) {
t = ts.storage(table, wo.pager_storageKey) || {}; // fixes #387
p.page = isNaN(t.page) ? p.page : t.page;
p.size = ( isNaN(t.size) ? p.size : t.size ) || 10;
$.data(table, 'pagerLastSize', p.size);
}
// skipped rows
p.regexRows = new RegExp('(' + (wo.filter_filteredRow || 'filtered') + '|' + c.selectorRemove.replace(/^(\w+\.)/g,'') + '|' + c.cssChildRow + ')');
// 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);
tsp.setPageSize(table, 0, c); // page size 0 is ignored
// pager initialized
p.initialized = true;
p.isInitializing = false;
c.$table.trigger('pagerInitialized', c);
tsp.updatePageDisplay(table, c);
},
bindEvents: function(table, c){
var ctrls, fxn,
p = c.pager,
wo = c.widgetOptions,
s = wo.pager_selectors;
c.$table
.unbind('filterStart filterEnd sortEnd disable enable destroy updateComplete pageSize '.split(' ').join('.pager '))
.bind('filterStart.pager', function(e, filters) {
p.currentFilters = filters;
// don't change page is filters are the same (pager updating, etc)
if (wo.pager_pageReset !== false && (c.lastCombinedFilter || '') !== (filters || []).join('')) {
p.page = wo.pager_pageReset; // fixes #456 & #565
}
})
// update pager after filter widget completes
.bind('filterEnd.pager sortEnd.pager', function() {
if (p.initialized) {
if (c.delayInit && c.rowsCopy && c.rowsCopy.length === 0) {
// make sure we have a copy of all table rows once the cache has been built
tsp.updateCache(table);
}
// update page display first, so we update p.filteredPages
tsp.updatePageDisplay(table, c, false);
// tsp.moveToPage(table, p, false); <-- called when applyWidgets is triggered
c.pager.last.page = -1;
c.$table.trigger('applyWidgets');
tsp.fixHeight(table, c);
}
})
.bind('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('updateComplete.pager', function(e, table, triggered){
e.stopPropagation();
// table can be unintentionally undefined in tablesorter v2.17.7 and earlier
if (!table || triggered) { return; }
tsp.fixHeight(table, c);
var $rows = c.$tbodies.eq(0).children('tr').not(c.selectorRemove);
p.totalRows = $rows.length - ( c.widgetOptions.pager_countChildRows ? 0 : $rows.filter('.' + c.cssChildRow).length );
p.totalPages = Math.ceil( p.totalRows / p.size );
if ($rows.length && c.rowsCopy && c.rowsCopy.length === 0) {
// make a copy of all table rows once the cache has been built
tsp.updateCache(table);
}
tsp.updatePageDisplay(table, c);
tsp.hideRows(table, c);
// make sure widgets are applied - fixes #450
c.$table.trigger('applyWidgets');
})
.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, true);
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(','))
.attr("tabindex", 0)
.unbind('click.pager')
.bind('click.pager', function(e){
e.stopPropagation();
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;
}
}
}
});
if ( p.$goto.length ) {
p.$goto
.unbind('change')
.bind('change', function(){
p.page = $(this).val() - 1;
tsp.moveToPage(table, p, true);
tsp.updatePageDisplay(table, c, false);
});
}
if ( p.$size.length ) {
// setting an option as selected appears to cause issues with initial page size
p.$size.find('option').removeAttr('selected');
p.$size
.unbind('change.pager')
.bind('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,
first = dis || p.page === 0,
tp = Math.min( p.totalPages, p.filteredPages ),
last = dis || p.page === tp - 1 || tp === 0,
wo = c.widgetOptions,
s = wo.pager_selectors;
if ( wo.pager_updateArrows ) {
p.$container.find(s.first + ',' + s.prev).toggleClass(wo.pager_css.disabled, first).attr('aria-disabled', first);
p.$container.find(s.next + ',' + s.last).toggleClass(wo.pager_css.disabled, last).attr('aria-disabled', last);
}
},
updatePageDisplay: function(table, c, completed) {
var i, pg, s, $out, regex,
wo = c.widgetOptions,
p = c.pager,
f = c.$table.hasClass('hasFilters'),
t = [],
sz = p.size || 10; // don't allow dividing by zero
t = [ wo && wo.filter_filteredRow || 'filtered', c.selectorRemove.replace(/^(\w+\.)/g,'') ];
if (wo.pager_countChildRows) { t.push(c.cssChildRow); }
regex = new RegExp( '(' + t.join('|') + ')' );
p.$size.add(p.$goto).removeClass(wo.pager_css.disabled).removeAttr('disabled').attr('aria-disabled', 'false');
if (f && !wo.pager_ajaxUrl) {
if ($.isEmptyObject(c.cache)) {
// delayInit: true so nothing is in the cache
p.filteredRows = p.totalRows = c.$tbodies.eq(0).children('tr').not( c.widgetOptions.pager_countChildRows ? '' : '.' + c.cssChildRow ).length;
} else {
p.filteredRows = 0;
$.each(c.cache[0].normalized, function(i, el) {
p.filteredRows += p.regexRows.test(el[c.columns].$row[0].className) ? 0 : 1;
});
}
} else if (!f) {
p.filteredRows = p.totalRows;
}
p.totalPages = Math.ceil( p.totalRows / sz ); // needed for "pageSize" method
c.totalRows = p.totalRows;
c.filteredRows = p.filteredRows;
p.filteredPages = Math.ceil( p.filteredRows / sz ) || 0;
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.totalPages ? p.page + (n ? parseInt(n, 10) : 1) : 0;
})
// {totalPages}, {extra}, {extra:0} (array) or {extra : key} (object)
.replace(/\{\w+(\s*:\s*\w+)?\}/gi, function(m){
var len, indx,
str = m.replace(/[{}\s]/g,''),
extra = str.split(':'),
data = p.ajaxData,
// return zero for default page/row numbers
deflt = /(rows?|pages?)$/i.test(str) ? 0 : '';
if (/(startRow|page)/.test(extra[0]) && extra[1] === 'input') {
len = ('' + (extra[0] === 'page' ? p.totalPages : p.totalRows)).length;
indx = extra[0] === 'page' ? p.page + 1 : p.startRow;
return '<input type="text" class="ts-' + extra[0] + '" style="max-width:' + len + 'em" value="' + indx + '"/>';
}
return extra.length > 1 && data && data[extra[0]] ? data[extra[0]][extra[1]] : p[str] || (data ? data[str] : deflt) || deflt;
});
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[0].innerHTML = t;
p.$goto[0].value = p.page + 1;
}
// rebind startRow/page inputs
$out.find('.ts-startRow, .ts-page').unbind('change').bind('change', function(){
var v = $(this).val(),
pg = $(this).hasClass('ts-startRow') ? Math.floor( v/p.size ) + 1 : v;
c.$table.trigger('pageSet.pager', [ pg ]);
});
}
}
tsp.pagerArrows(c);
if (p.initialized && completed !== false) {
c.$table.trigger('pagerComplete', c);
// save pager info to storage
if (wo.pager_savePages && ts.storage) {
ts.storage(table, wo.pager_storageKey, {
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 = $.data(table, 'pagerSavedHeight');
if (h) {
d = h - $b.height();
if ( d > 5 && $.data(table, 'pagerLastSize') === p.size && $b.children('tr:visible').length < p.size ) {
$b.append('<tr class="pagerSavedHeightSpacer ' + wo.pager_selectors.remove.replace(/^(\w+\.)/g,'') + '" style="height:' + d + 'px;"></tr>');
}
}
}
},
changeHeight: function(table, c) {
var $b = c.$tbodies.eq(0);
$b.find('tr.pagerSavedHeightSpacer').remove();
$.data(table, 'pagerSavedHeight', $b.height());
tsp.fixHeight(table, c);
$.data(table, 'pagerLastSize', c.pager.size);
},
hideRows: function(table, c){
if (!c.widgetOptions.pager_ajaxUrl) {
var i,
lastIndex = 0,
p = c.pager,
wo = c.widgetOptions,
rows = c.$tbodies.eq(0).children('tr'),
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) ) {
if (j === s && rows[i].className.match(c.cssChildRow)) {
// hide child rows @ start of pager (if already visible)
rows[i].style.display = 'none';
} else {
rows[i].style.display = ( j >= s && j < e ) ? '' : 'none';
// don't count child rows
j += rows[i].className.match(c.cssChildRow + '|' + c.selectorRemove.replace(/^(\w+\.)/g,'')) && !wo.pager_countChildRows ? 0 : 1;
if ( j === e && rows[i].style.display !== 'none' && rows[i].className.match(ts.css.cssHasChild) ) {
lastIndex = i;
}
}
}
}
// add any attached child rows to last row of pager. Fixes part of issue #396
if ( lastIndex > 0 && rows[lastIndex].className.match(ts.css.cssHasChild) ) {
while ( ++lastIndex < l && rows[lastIndex].className.match(c.cssChildRow) ) {
rows[lastIndex].style.display = '';
}
}
}
},
hideRowsSetup: function(table, c){
var p = c.pager;
p.size = parseInt( p.$size.val(), 10 ) || p.size;
$.data(table, '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, rr_count,
$t = c.$table,
tds = '',
result = wo.pager_ajaxProcessing(data, table, xhr) || [ 0, [] ],
hl = $t.find('thead th').length;
// Clean up any previous error.
ts.showError(table);
if ( exception ) {
if (c.debug) {
ts.log('Ajax Error', xhr, exception);
}
ts.showError(table, exception.message + ' (' + xhr.status + ')');
c.$tbodies.eq(0).children('tr').detach();
p.totalRows = 0;
} else {
// process ajax object
if (!$.isArray(result)) {
p.ajaxData = result;
c.totalRows = p.totalRows = result.total;
c.filteredRows = p.filteredRows = typeof result.filteredRows !== 'undefined' ? result.filteredRows : 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;
// can't set filtered rows when returning an array
c.totalRows = c.filteredRows = p.filteredRows = p.totalRows;
d = p.totalRows === 0 ? [""] : result[t ? 0 : 1] || []; // row data
th = result[2]; // headers
}
l = d && d.length;
if (d instanceof jQuery) {
if (wo.pager_processAjaxOnInit) {
// append jQuery object
c.$tbodies.eq(0).children('tr').detach();
c.$tbodies.eq(0).append(d);
}
} else if (l) {
// build table from array
for ( i = 0; i < l; i++ ) {
tds += '<tr>';
for ( j = 0; j < d[i].length; j++ ) {
// build tbody cells; watch for data containing HTML markup - see #434
tds += /^\s*<td/.test(d[i][j]) ? $.trim(d[i][j]) : '<td>' + d[i][j] + '</td>';
}
tds += '</tr>';
}
// add rows to first tbody
if (wo.pager_processAjaxOnInit) {
c.$tbodies.eq(0).html( tds );
}
}
wo.pager_processAjaxOnInit = true;
// only add new header text if the length matches
if ( th && th.length === hl ) {
hsh = $t.hasClass('hasStickyHeaders');
$sh = hsh ? wo.$sticky.children('thead:first').children('tr').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
}
// make sure last pager settings are saved, prevents multiple server side calls with
// the same parameters
p.totalPages = Math.ceil( p.totalRows / ( p.size || 10 ) );
p.last.totalRows = p.totalRows;
p.last.currentFilters = p.currentFilters;
p.last.sortList = (c.sortList || []).join(',');
tsp.updatePageDisplay(table, c);
tsp.fixHeight(table, c);
$t.trigger('updateCache', [function(){
if (p.initialized) {
// apply widgets after table has rendered & after a delay to prevent
// multiple applyWidget blocking code from blocking this trigger
setTimeout(function(){
$t
.trigger('applyWidgets')
.trigger('pagerChange', p);
}, 0);
}
}]);
}
if (!p.initialized) {
c.$table.trigger('applyWidgets');
}
},
getAjax: function(table, c){
var counter,
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.unbind('ajaxError.pager');
});
counter = ++p.ajaxCounter;
wo.pager_ajaxObject.url = url; // from the ajaxUrl option and modified by customAjaxUrl
wo.pager_ajaxObject.success = function(data, status, jqxhr) {
// Refuse to process old ajax commands that were overwritten by new ones - see #443
if (counter < p.ajaxCounter){
return;
}
tsp.renderAjax(data, table, c, jqxhr);
$doc.unbind('ajaxError.pager');
if (typeof p.oldAjaxSuccess === 'function') {
p.oldAjaxSuccess(data);
}
};
if (c.debug) {
ts.log('ajax initialized', wo.pager_ajaxObject);
}
$.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 || $(table).data('lastSearch') || [],
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 );
p.currentFilters = fl;
}
if ( $.isFunction(wo.pager_customAjaxUrl) ) {
url = wo.pager_customAjaxUrl(table, url);
}
if (c.debug) {
ts.log('Pager ajax url: ' + url);
}
return url;
},
renderTable: function(table, rows) {
var $tb, index, count, added,
c = table.config,
p = c.pager,
wo = c.widgetOptions,
f = c.$table.hasClass('hasFilters'),
l = rows && rows.length || 0, // rows may be undefined
s = ( p.page * p.size ),
e = p.size;
if ( l < 1 ) {
if (c.debug) {
ts.log('Pager: no rows for pager to render');
}
// empty table, abort!
return;
}
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 {
ts.clearTableBody(table);
$tb = ts.processTbody(table, c.$tbodies.eq(0), true);
// not filtered, start from the calculated starting point (s)
// if filtered, start from zero
index = f ? 0 : s;
count = f ? 0 : s;
added = 0;
while (added < e && index < rows.length) {
if (!f || !/filtered/.test(rows[index][0].className)){
count++;
if (count > s && added <= e) {
added++;
$tb.append(rows[index]);
}
}
index++;
}
ts.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;
if (table.isUpdating) {
c.$table.trigger('updateComplete', [ table, true ]);
}
},
showAllRows: function(table, c){
var p = c.pager,
wo = c.widgetOptions;
if ( p.ajax ) {
tsp.pagerArrows(c, true);
} else {
p.isDisabled = true;
$.data(table, 'pagerLastPage', p.page);
$.data(table, 'pagerLastSize', p.size);
p.page = 0;
p.size = p.totalRows;
p.totalPages = 1;
c.$table
.addClass('pagerDisabled')
.removeAttr('aria-describedby')
.find('tr.pagerSavedHeightSpacer').remove();
tsp.renderTable(table, c.rowsCopy);
c.$table.trigger('applyWidgets');
if (c.debug) {
ts.log('pager disabled');
}
}
// disable size selector
p.$size.add(p.$goto).each(function(){
$(this).attr('aria-disabled', 'true').addClass(wo.pager_css.disabled)[0].disabled = true;
});
},
// updateCache if delayInit: true
// this is normally done by "appendToTable" function in the tablesorter core AFTER a sort
updateCache: function(table) {
var c = table.config,
p = c.pager;
c.$table.trigger('updateCache', [ function(){
if ( !$.isEmptyObject(table.config.cache) ) {
var i,
rows = [],
n = table.config.cache[0].normalized;
p.totalRows = n.length;
for (i = 0; i < p.totalRows; i++) {
rows.push(n[i][c.columns].$row);
}
c.rowsCopy = rows;
tsp.moveToPage(table, p, true);
// clear out last search to force an update
p.last.currentFilters = [' '];
}
} ]);
},
moveToPage: function(table, p, pageMoved) {
if ( p.isDisabled ) { return; }
if ( pageMoved !== false && p.initialized && $.isEmptyObject(table.config.cache)) {
return tsp.updateCache(table);
}
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; }
// fixes issue where one current filter is [] and the other is ['','',''],
// making the next if comparison think the filters as different. Fixes #202.
l.currentFilters = (l.currentFilters || []).join('') === '' ? [] : l.currentFilters;
p.currentFilters = (p.currentFilters || []).join('') === '' ? [] : p.currentFilters;
// don't allow rendering multiple times on the same page/size/totalRows/filters/sorts
if ( l.page === p.page && l.size === p.size && l.totalRows === p.totalRows &&
(l.currentFilters || []).join(',') === (p.currentFilters || []).join(',') &&
l.sortList === (c.sortList || []).join(',') ) {
return;
}
if (c.debug) {
ts.log('Pager changing to page ' + p.page);
}
p.last = {
page : p.page,
size : p.size,
// fixes #408; modify sortList otherwise it auto-updates
sortList : (c.sortList || []).join(','),
totalRows : p.totalRows,
currentFilters : p.currentFilters || []
};
if (p.ajax) {
tsp.getAjax(table, c);
} else if (!p.ajax) {
tsp.renderTable(table, c.rowsCopy);
}
$.data(table, 'pagerLastPage', p.page);
if (p.initialized && pageMoved !== false) {
c.$table
.trigger('pageMoved', c)
.trigger('applyWidgets');
if (!p.ajax && table.isUpdating) {
c.$table.trigger('updateComplete', [ table, true ]);
}
}
},
setPageSize: function(table, size, c) {
var p = c.pager;
p.size = size || p.size || 10;
p.$size.val(p.size);
$.data(table, 'pagerLastPage', p.page);
$.data(table, 'pagerLastSize', p.size);
p.totalPages = Math.ceil( p.totalRows / p.size );
p.filteredPages = Math.ceil( p.filteredRows / p.size );
tsp.moveToPage(table, p, true);
},
moveToFirstPage: function(table, p) {
p.page = 0;
tsp.moveToPage(table, p, true);
},
moveToLastPage: function(table, p) {
p.page = ( Math.min( p.totalPages, p.filteredPages ) - 1 );
tsp.moveToPage(table, p, true);
},
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, true);
},
moveToPrevPage: function(table, p) {
p.page--;
if ( p.page <= 0 ) {
p.page = 0;
}
tsp.moveToPage(table, p, true);
},
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;
delete table.config.rowsCopy;
c.$table.unbind('destroy.pager sortEnd.pager filterEnd.pager enable.pager disable.pager');
if (ts.storage) {
ts.storage(table, c.widgetOptions.pager_storageKey, '');
}
},
enablePager: function(table, c, triggered){
var info, p = c.pager;
p.isDisabled = false;
p.page = $.data(table, 'pagerLastPage') || p.page || 0;
p.size = $.data(table, 'pagerLastSize') || parseInt(p.$size.find('option[selected]').val(), 10) || p.size || 10;
p.$size.val(p.size); // set page size
p.totalPages = Math.ceil( Math.min( p.totalRows, p.filteredRows ) / p.size );
c.$table.removeClass('pagerDisabled');
// if table id exists, include page display with aria info
if ( table.id ) {
info = table.id + '_pager_info';
p.$container.find(c.widgetOptions.pager_selectors.pageDisplay).attr('id', info);
c.$table.attr('aria-describedby', info);
}
if ( triggered ) {
c.$table.trigger('updateRows');
tsp.setPageSize(table, p.size, c);
tsp.hideRowsSetup(table, c);
tsp.fixHeight(table, c);
if (c.debug) {
ts.log('pager enabled');
}
}
},
appender: function(table, rows) {
var c = table.config,
wo = c.widgetOptions,
p = c.pager;
if ( !p.ajax ) {
c.rowsCopy = rows;
p.totalRows = c.widgetOptions.pager_countChildRows ? c.$tbodies.eq(0).children('tr').length : rows.length;
p.size = $.data(table, 'pagerLastSize') || p.size || wo.pager_size || 10;
p.totalPages = Math.ceil( p.totalRows / p.size );
tsp.moveToPage(table, p);
// update display here in case all rows are removed
tsp.updatePageDisplay(table, c, false);
} else {
tsp.moveToPage(table, p, true);
}
}
};
// see #486
ts.showError = function(table, message){
$(table).each(function(){
var $row,
c = this.config,
errorRow = c.pager && c.pager.cssErrorRow || c.widgetOptions.pager_css && c.widgetOptions.pager_css.errorRow || 'tablesorter-errorRow';
if (c) {
if (typeof message === 'undefined') {
c.$table.find('thead').find(c.selectorRemove).remove();
} else {
$row = ( /tr\>/.test(message) ? $(message) : $('<tr><td colspan="' + c.columns + '">' + message + '</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( c.$table.find('thead:first') )
.addClass( errorRow + ' ' + c.selectorRemove.replace(/^(\w+\.)/g,'') )
.attr({
role : 'alert',
'aria-live' : 'assertive'
});
}
}
});
};
})(jQuery);

View File

@@ -0,0 +1,123 @@
/* Print widget (beta) for TableSorter 6/18/2014 (v2.17.2)
* Requires tablesorter v2.8+ and jQuery 1.2.6+
*/
/*jshint browser:true, jquery:true, unused:false */
/*global jQuery: false */
;(function($){
"use strict";
var ts = $.tablesorter,
printTable = ts.printTable = {
event : 'printTable',
basicStyle : 'table, tr, td, th { border : solid 1px black; border-collapse : collapse; } td, th { padding: 2px; }',
init : function(c) {
c.$table
.unbind(printTable.event)
.bind(printTable.event, function(){
// explicitly use table.config.widgetOptions because we want
// the most up-to-date values; not the "wo" from initialization
printTable.process(c, c.widgetOptions);
});
},
process : function(c, wo) {
var $this,
$table = $('<div/>').append(c.$table.clone()),
printStyle = printTable.basicStyle + 'table { width: 100% }' +
// hide filter row
'.tablesorter-filter-row { display: none }' +
// hide sort arrows
'.tablesorter-header { background-image: none !important; }';
// replace content with data-attribute content
$table.find('[' + wo.print_dataAttrib + ']').each(function(){
$this = $(this);
$this.text( $this.attr(wo.print_dataAttrib) );
});
// === rows ===
// Assume "visible" means rows hidden by the pager (rows set to "display:none")
// or hidden by a class name which is added to the wo.print_extraCSS definition
if (/a/i.test(wo.print_rows)) {
// force show of all rows
printStyle += 'tbody tr { display: table-row !important; }';
} else if (/f/i.test(wo.print_rows)) {
// add definition to show all non-filtered rows (cells hidden by the pager)
printStyle += 'tbody tr:not(.' + (wo.filter_filteredRow || 'filtered') + ') { display: table-row !important; }';
}
// === columns ===
// columnSelector -> c.selector.$style
// Assume "visible" means hidden columns have a "display:none" style, or a class name
// add the definition to the wo.print_extraCSS option
if (/s/i.test(wo.print_columns) && c.selector && c.widgets.indexOf('columnSelector') >= 0) {
// show selected (visible) columns; make a copy of the columnSelector widget css (not media queries)
printStyle += c.selector.auto ? '' : c.selector.$style.text();
} else if (/a/i.test(wo.print_columns)) {
// force show all cells
printStyle += 'td, th { display: table-cell !important; }';
}
printStyle += wo.print_extraCSS;
// callback function
if ( $.isFunction(wo.print_callback) ) {
wo.print_callback( c, $table, printStyle );
} else {
printTable.printOutput(c, $table.html(), printStyle);
}
}, // end process
printOutput : function(c, data, style) {
var wo = c.widgetOptions,
generator = window.open('', wo.print_title, 'width=500,height=300'),
t = wo.print_title || c.$table.find('caption').text() || c.$table[0].id || document.title || 'table';
generator.document.write(
'<html><head><title>' + t + '</title>' +
( wo.print_styleSheet ? '<link rel="stylesheet" href="' + wo.print_styleSheet + '">' : '' ) +
'<style>' + style + '</style>' +
'</head><body>' + data + '</body></html>'
);
generator.document.close();
generator.print();
generator.close();
return true;
},
remove : function(c) {
c.$table.off(printTable.event);
}
};
ts.addWidget({
id: 'print',
options: {
print_title : '', // this option > caption > table id > "table"
print_dataAttrib : 'data-name', // header attrib containing modified header name
print_rows : 'filtered', // (a)ll, (v)isible or (f)iltered
print_columns : 'selected', // (a)ll or (s)elected (if columnSelector widget is added)
print_extraCSS : '', // add any extra css definitions for the popup window here
print_styleSheet : '', // add the url of your print stylesheet
// callback executed when processing completes
// to continue printing, use the following function:
// function( config, $table, printStyle ) {
// // do something to the table or printStyle string
// $.tablesorter.printTable.printOutput( config, $table.html(), printStyle );
// }
print_callback : null
},
init: function(table, thisWidget, c) {
printTable.init(c);
},
remove: function(table, c){
printTable.remove(c);
}
});
})(jQuery);

View File

@@ -0,0 +1,179 @@
/* table reflow widget (beta) for TableSorter 3/22/2014 (v2.16.0)
* Requires tablesorter v2.8+ and jQuery 1.7+
* Also, this widget requires the following default css (modify as desired)
/ * REQUIRED CSS: change your reflow breakpoint here (35em below) * /
@media ( max-width: 35em ) {
.ui-table-reflow td,
.ui-table-reflow th {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
float: right;
/ * if not using the stickyHeaders widget (not the css3 version)
* the "!important" flag, and "height: auto" can be removed * /
width: 100% !important;
height: auto !important;
}
/ * reflow widget * /
.ui-table-reflow tbody td[data-title]:before {
color: #469;
font-size: .9em;
content: attr(data-title);
float: left;
width: 50%;
white-space: pre-wrap;
text-align: bottom;
display: inline-block;
}
/ * reflow2 widget * /
table.ui-table-reflow .ui-table-cell-label.ui-table-cell-label-top {
display: block;
padding: .4em 0;
margin: .4em 0;
text-transform: uppercase;
font-size: .9em;
font-weight: 400;
}
table.ui-table-reflow .ui-table-cell-label {
padding: .4em;
min-width: 30%;
display: inline-block;
margin: -.4em 1em -.4em -.4em;
}
}
.ui-table-reflow .ui-table-cell-label {
display: none;
}
*/
/*jshint browser:true, jquery:true, unused:false */
/*global jQuery: false */
;(function($){
"use strict";
var ts = $.tablesorter,
tablereflow = {
// simple reflow
// add data-attribute to each cell which shows when media query is active
// this widget DOES NOT WORK on a table with multiple thead rows
init : function(table, c, wo) {
var $this,
title = wo.reflow_dataAttrib,
header = wo.reflow_headerAttrib,
headers = [];
c.$table
.addClass(wo.reflow_className)
.off('refresh.tsreflow updateComplete.tsreflow2')
// emulate jQuery Mobile refresh
// https://api.jquerymobile.com/table-reflow/#method-refresh
.on('refresh.tsreflow updateComplete.tsreflow2', function(){
tablereflow.init(table, c, wo);
});
c.$headers.each(function(){
$this = $(this);
headers.push( $this.attr(header) || $this.text() );
});
c.$tbodies.children().each(function(){
$(this).children().each(function(i){
$(this).attr(title, headers[i]);
});
});
},
init2: function(table, c, wo) {
var $this, $tbody, i, $hdr, txt, len,
cols = c.columns,
header = wo.reflow2_headerAttrib,
headers = [];
c.$table
.addClass(wo.reflow2_className)
.off('refresh.tsreflow2 updateComplete.tsreflow2')
// emulate jQuery Mobile refresh
// https://api.jquerymobile.com/table-reflow/#method-refresh
.on('refresh.tsreflow2 updateComplete.tsreflow2', function(){
tablereflow.init2(table, c, wo);
});
// add <b> to every table cell with thead cell contents
for (i = 0; i < cols; i++) {
$hdr = c.$headers.filter('[data-column="' + i + '"]');
if ($hdr.length > 1) {
txt = [];
$hdr.each(function(){
$this = $(this);
if (!$this.hasClass(wo.reflow2_classIgnore)) {
txt.push( $this.attr(header) || $this.text() );
}
});
} else {
txt = [ $hdr.attr(header) || $hdr.text() ];
}
headers.push( txt );
}
// include "remove-me" class so these additional elements are removed before updating
txt = '<b class="' + c.selectorRemove.slice(1) + ' ' + wo.reflow2_labelClass;
c.$tbodies.children().each(function(){
$tbody = ts.processTbody(table, $(this), true);
$tbody.children().each(function(j){
$this = $(this);
len = headers[j].length
i = len - 1;
while (i >= 0) {
$this.prepend(txt + (i === 0 && len > 1 ? ' ' + wo.reflow2_labelTop : '') + '">' + headers[j][i] + '</b>');
i--;
}
});
ts.processTbody(table, $tbody, false);
});
},
remove : function(table, c, wo) {
c.$table.removeClass(wo.reflow_className);
},
remove2 : function(table, c, wo) {
c.$table.removeClass(wo.reflow2_className);
}
};
ts.addWidget({
id: "reflow",
options: {
// class name added to make it responsive (class name within media query)
reflow_className : 'ui-table-reflow',
// header attribute containing modified header name
reflow_headerAttrib : 'data-name',
// data attribute added to each tbody cell
reflow_dataAttrib : 'data-title'
},
init: function(table, thisWidget, c, wo) {
tablereflow.init(table, c, wo);
},
remove: function(table, c, wo){
tablereflow.remove(table, c, wo);
}
});
ts.addWidget({
id: "reflow2",
options: {
// class name added to make it responsive (class name within media query)
reflow2_className : 'ui-table-reflow',
// ignore header cell content with this class name
reflow2_classIgnore : 'ui-table-reflow-ignore',
// header attribute containing modified header name
reflow2_headerAttrib : 'data-name',
// class name applied to thead labels
reflow2_labelClass : 'ui-table-cell-label',
// class name applied to first row thead label
reflow2_labelTop : 'ui-table-cell-label-top'
},
init: function(table, thisWidget, c, wo) {
tablereflow.init2(table, c, wo);
},
remove: function(table, c, wo){
tablereflow.remove2(table, c, wo);
}
});
})(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.addWidget({
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,218 @@
/*!
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; updated 6/28/2014 (v2.17.3)
Requires jQuery v1.7+
Requires the tablesorter plugin, v2.8+, 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 : 18, // 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-reset { width: auto !important; } ' +
'.tablesorter-scroller { text-align: left; overflow: hidden; }' +
'.tablesorter-scroller-header { overflow: hidden; }' +
'.tablesorter-scroller-header table.tablesorter { margin-bottom: 0; }' +
'.tablesorter-scroller-table { overflow-y: scroll; }' +
'.tablesorter-scroller-table table.tablesorter { margin-top: 0; overflow: scroll; } ' +
'.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.addWidget({
id: 'scroller',
priority: 60, // run after the filter widget
options: {
scroller_height : 300,
scroller_barWidth : 18,
scroller_jumpToHeader: true,
scroller_upAfterSort: true,
scroller_idPrefix : 's_'
},
init: function(table, thisWidget, c, wo){
var $win = $(window);
// Setup window.resizeEnd event
$win
.bind('resize', ts.window_resize)
.bind('resizeEnd', function() {
// 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.unbind('resize', ts.window_resize);
table.config.widgetOptions.scroller_resizeWidth();
$win.bind('resize', ts.window_resize);
}
});
},
format: function(table, c, wo) {
var h, $hdr, id, t, resize, $cells,
$win = $(window),
$tbl = c.$table;
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() * 1001);
$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" />')
.before($hdr)
.find('.tablesorter-filter-row').addClass('hideme');
$cells = $hdr
.wrap('<div class="tablesorter-scroller-header" style="width:' + $tbl.width() + ';" />')
.find('.' + ts.css.header);
$tbl.wrap('<div class="tablesorter-scroller-table" style="height:' + h + 'px;width:' + $tbl.width() + ';" />');
// make scroller header sortable
ts.bindEvents(table, $cells);
// look for filter widget
if ($tbl.hasClass('hasFilters')) {
ts.filter.bindSearch( $tbl, $hdr.find('.' + ts.css.filter) );
}
resize = function(){
var d, b, $h, $th, w,
// Hide other scrollers so we can resize
$div = $('div.scroller[id != "' + id + '"]').hide();
$tbl.find('thead').show();
// Reset sizes so parent can resize.
$tbl
.addClass('tablesorter-scroller-reset')
.find('thead').find('.tablesorter-header-inner').addClass('tablesorter-scroller-reset');
d = $tbl.parent();
d.addClass('tablesorter-scroller-reset');
d.parent().trigger('resize');
// Shrink a bit to accommodate scrollbar
d.width( d.parent().innerWidth() - ( d.parent().hasScrollBar() ? wo.scroller_barWidth : 0 ) );
w = d.innerWidth() - ( d.hasScrollBar() ? wo.scroller_barWidth : 0 );
$tbl.width( w );
$hdr.width( w );
$hdr.parent().width( w );
$tbl.closest('.tablesorter-scroller').find('.tablesorter-scroller-reset').removeClass('tablesorter-scroller-reset');
// include left & right border widths
b = parseInt( $tbl.css('border-left-width'), 10 ) + parseInt( $tbl.css('border-right-width'), 10 );
$h = $hdr.find('thead').children().children();
// adjust cloned header to match original table width - includes wrappers, headers, and header inner div
$tbl.find('thead').children().children().each(function(i, c){
$th = $(c).find('.tablesorter-header-inner');
if ($th.length) {
// I have no idea why this is in here anymore LOL
w = parseInt( $th.css('min-width').replace('auto', '0').replace(/(px|em)/, ''), 10 );
if ( $th.width() < w ) {
$th.width(w);
} else {
w = $th.width();
}
$h.eq(i)
.find('.tablesorter-header-inner').width(w - b)
// set inner width first
.parent()
.width( $th.parent().width() - b );
}
});
$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().bind('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 );
}
}
$hdr.parent().scrollLeft( $(this).scrollLeft() );
});
}
// Sorting, so scroll to top
if (wo.scroller_upAfterSort) {
$tbl.parent().animate({ scrollTop: 0 }, 'fast');
}
},
remove : function(table, c, wo){
var $table = c.$table;
$table.closest('.tablesorter-scroller').find('.tablesorter-scroller-header').remove();
$table
.unwrap()
.find('.tablesorter-filter-row').removeClass('hideme').end()
.find('thead').show().css('visibility', 'visible');
c.isScrolling = false;
}
});
})(jQuery);

View File

@@ -0,0 +1,124 @@
/*
* StaticRow widget for jQuery TableSorter 2.0
* Version 1.2 - modified by Rob Garrison (6/28/2014 for tablesorter v2.16.1-beta+)
* Requires:
* jQuery v1.4+
* tablesorter plugin, v2.8+, available at http://mottie.github.com/tablesorter/docs/
*
* Copyright (c) 2011 Nils Luxton
* Licensed under the MIT license:
* http://www.opensource.org/licenses/mit-license.php
*
*/
/*jshint browser:true, jquery:true, unused:false */
/*global jQuery: false */
;(function($){
"use strict";
var ts = $.tablesorter,
// events triggered on the table that update this widget
events = 'staticRowsRefresh updateComplete '.split(' ').join('.tsstaticrows '),
// add/refresh row indexes
addIndexes = function(table){
var $tr, wo, v, indx, rows,
c = table.config;
// "Index" the static rows, saving their current (starting) position in the
// table inside a data() param on the <tr> element itself for later use.
if (c) {
wo = c.widgetOptions;
c.$tbodies.each(function(){
$tr = $(this).children();
rows = $tr.length;
$tr.filter(wo.staticRow_class).each(function() {
$tr = $(this);
indx = $tr.data(wo.staticRow_index);
if (typeof indx !== "undefined") {
v = parseFloat(indx);
// percentage of total rows
indx = (/%/.test(indx)) ? Math.round(v/100 * rows) : v;
} else {
indx = $tr.index();
}
// row indexing starts over within each tbody
$tr.data( wo.staticRow_data, indx );
});
});
}
};
ts.addWidget({
// Give the new Widget an ID to be used in the tablesorter() call, as follows:
// $('#myElement').tablesorter({ widgets: ['zebra', 'staticRow'] });
id: 'staticRow',
options: {
staticRow_class : '.static',
staticRow_data : 'static-index',
staticRow_index : 'row-index'
},
init: function(table, thisWidget, c, wo){
addIndexes(table);
// refresh static rows after updates
c.$table
.unbind(events)
.bind(events, function(){
addIndexes(table);
c.$table.trigger('applyWidgets');
});
},
format: function(table, c, wo) {
// Loop thru static rows, moving them to their original "indexed" position,
// & repeat until no more re-shuffling is needed
var targetIndex, $thisRow, indx, numRows, $tbody, hasShuffled, $rows, max;
c.$tbodies.each(function(){
$tbody = $.tablesorter.processTbody(table, $(this), true); // remove tbody
hasShuffled = true;
indx = 0;
$rows = $tbody.children(wo.staticRow_class);
numRows = $tbody.children('tr').length - 1;
max = $rows.length;
// don't allow the while loop to cycle more times than the set number of static rows
while (hasShuffled && indx < max) {
hasShuffled = false;
/*jshint loopfunc:true */
$rows.each(function() {
targetIndex = $(this).data(wo.staticRow_data);
// allow setting target index >> num rows to always make a row last
targetIndex = targetIndex >= numRows ? numRows : targetIndex < 0 ? 0 : targetIndex;
if (targetIndex !== $(this).index()) {
hasShuffled = true;
$thisRow = $(this).detach();
if (targetIndex >= numRows) {
// Are we trying to be the last row?
$thisRow.appendTo( $tbody );
} else if (targetIndex === 0) {
// Are we trying to be the first row?
$thisRow.prependTo( $tbody );
} else {
// No, we want to be somewhere in the middle!
$thisRow.insertBefore( $tbody.find('tr:eq(' + targetIndex + ')') );
}
}
});
indx++;
}
$.tablesorter.processTbody(table, $tbody, false); // restore tbody
});
c.$table.trigger('staticRowsComplete', table);
},
remove : function(table, c, wo){
c.$table.unbind(events);
}
});
})(jQuery);