470 lines
15 KiB
JavaScript
470 lines
15 KiB
JavaScript
/*! Widget: Build Table - updated 2018-03-26 (v2.30.2) *//*
|
|
* for tableSorter v2.16.0+
|
|
* 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 build options to defaults to prevent warnings
|
|
$.extend(true, ts.defaults.widgetOptions, bt.defaults);
|
|
// add table if one doesn't exist
|
|
var $tbl = tar.nodeName === '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,
|
|
debug = ts.debug(c, 'build'),
|
|
|
|
// determine type: html, json, array, csv, object
|
|
runType = function(d) {
|
|
var t = $.type(d),
|
|
jq = d instanceof $;
|
|
// 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 || 'null');
|
|
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 (debug) {
|
|
console.error('Build >> ERROR: Aborting build table widget, incorrect build type');
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if ( d instanceof $ ) {
|
|
// 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) {
|
|
if (debug) {
|
|
console.error('Build >> ERROR: Aborting build table widget, failed ajax load');
|
|
}
|
|
$tbl.html('<tr><td class="error">' + jqXHR.status + ' ' + textStatus + '</td></tr>');
|
|
});
|
|
} else {
|
|
runType(d);
|
|
}
|
|
};
|
|
|
|
// add data to defaults for validator; value must be falsy!
|
|
ts.defaults.data = '';
|
|
|
|
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).triggerHandler(wo.build_complete);
|
|
if (table.config && ts.debug(table.config, 'build')) {
|
|
console.log('Build >> Table 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 $ ) {
|
|
$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 (ts.debug(c, 'build')) {
|
|
console.error('Build >> ERROR: 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);
|