/public/js/tablesorter/widgets/widget-build-table.js

https://gitlab.com/webster5361/UserFrosting · JavaScript · 454 lines · 309 code · 27 blank · 118 comment · 105 complexity · c409dba72c7ea052366c031ad293a049 MD5 · raw file

  1. /*! Widget: Build Table - updated 3/26/2015 (v2.21.3) *//*
  2. * for tableSorter v2.16.0+
  3. * by Rob Garrison
  4. */
  5. /*jshint browser:true, jquery:true, unused:false */
  6. /*global jQuery: false */
  7. ;(function($){
  8. 'use strict';
  9. var ts = $.tablesorter = $.tablesorter || {},
  10. // build a table from data (requires existing <table> tag)
  11. // data.header contains an array of header titles
  12. // data.rows contains an array of rows which contains an array of cells
  13. bt = ts.buildTable = function(tar, c){
  14. // add table if one doesn't exist
  15. var $tbl = tar.nodeName === 'TABLE' ? $(tar) : $('<table>').appendTo(tar),
  16. table = $tbl[0],
  17. wo = c.widgetOptions = $.extend( true, {}, bt.defaults, c.widgetOptions ),
  18. p = wo.build_processing,
  19. typ = wo.build_type,
  20. d = wo.build_source || c.data,
  21. // determine type: html, json, array, csv, object
  22. runType = function(d){
  23. var t = $.type(d),
  24. jq = d instanceof jQuery;
  25. // run any processing if set
  26. if ( typeof p === 'function' ) { d = p(d, wo); }
  27. // store processed data in table.config.data
  28. c.data = d;
  29. // String (html or unprocessed json) or jQuery object
  30. if ( jq || t === 'string' ) {
  31. // look for </tr> closing tag, then we have an HTML string
  32. if ( jq || /<\s*\/tr\s*>/.test(d) ) {
  33. return bt.html( table, d, wo );
  34. }
  35. try {
  36. d = $.parseJSON(d || 'null');
  37. if (d) {
  38. // valid JSON!
  39. return bt.object( table, d, wo );
  40. }
  41. } catch (ignore) {}
  42. // fall through in case it's a csv string
  43. }
  44. // Array
  45. if (t === 'array' || t === 'string' || typ === 'array' || typ === 'csv') {
  46. // build table using an array (csv & array combined script)
  47. return bt.csv( table, d, wo );
  48. }
  49. // if we got here, it's an object, or nothing
  50. return bt.object( table, d, wo );
  51. };
  52. // store config
  53. table.config = c;
  54. // even if wo.build_type is undefined, we can try to figure out the type
  55. if ( !ts.buildTable.hasOwnProperty(typ) && typ !== '' ) {
  56. if (c.debug) { console.error('aborting build table widget, incorrect build type'); }
  57. return false;
  58. }
  59. if ( d instanceof jQuery ) {
  60. // get data from within a jQuery object (csv)
  61. runType( $.trim( d.html() ) );
  62. } else if ( d && ( d.hasOwnProperty('url') || typ === 'json' ) ) {
  63. // load data via ajax
  64. $.ajax( wo.build_source )
  65. .done(function(data) {
  66. runType(data);
  67. })
  68. .fail(function( jqXHR, textStatus, errorThrown) {
  69. if (c.debug) { console.error('aborting build table widget, failed ajax load'); }
  70. $tbl.html('<tr><td class="error">' + jqXHR.status + ' ' + textStatus + '</td></tr>');
  71. });
  72. } else {
  73. runType(d);
  74. }
  75. };
  76. bt.defaults = {
  77. // *** build widget core ***
  78. build_type : '', // array, csv, object, json, html
  79. build_source : '', // array, object, jQuery Object or ajaxObject { url: '', dataType: 'json' },
  80. build_processing : null, // function that returns a useable build_type (e.g. string to array)
  81. build_complete : 'tablesorter-build-complete', // triggered event when build completes
  82. // *** CSV & Array ***
  83. build_headers : {
  84. rows : 1, // Number of header rows from the csv
  85. classes : [], // Header classes to apply to cells
  86. text : [], // Header cell text
  87. widths : [] // set header cell widths (set in colgroup)
  88. },
  89. build_footers : {
  90. rows : 1, // Number of header rows from the csv
  91. classes : [], // Footer classes to apply to cells
  92. text : [] // Footer cell text
  93. },
  94. build_numbers : {
  95. addColumn : false, // include row numbering column?
  96. sortable : false // make column sortable?
  97. },
  98. // *** CSV only options ***
  99. build_csvStartLine : 0, // line within the csv to start adding to table
  100. build_csvSeparator : ',', // csv separator
  101. // *** build object options ***
  102. build_objectRowKey : 'rows', // object key containing table rows
  103. build_objectCellKey : 'cells', // object key containing table cells (within the rows object)
  104. build_objectHeaderKey : 'headers', // object key containing table headers
  105. build_objectFooterKey : 'footers' // object key containing table footers
  106. };
  107. bt.build = {
  108. colgroup : function(widths) {
  109. var t = '';
  110. // add colgroup if widths set
  111. if (widths && widths.length) {
  112. t += '<colgroup>';
  113. $.each(widths, function(i, w){
  114. t += '<col' + ( w ? ' style="width:' + w + '"' : '' ) + '>';
  115. });
  116. t += '</colgroup>';
  117. }
  118. return t;
  119. },
  120. // d = cell data; typ = 'th' or 'td'; first = save widths from first header row only
  121. cell : function(d, wo, typ, col, first){
  122. var j, $td,
  123. $col = first ? $('<col>') : '',
  124. cls = wo.build_headers.classes,
  125. cw = wo.build_headers.widths;
  126. // d is just an array
  127. if (/string|number/.test(typeof d)) {
  128. // add classes from options, but not text
  129. $td = $('<' + typ + (cls && cls[col] ? ' class="' + cls[col] + '"' : '') + '>' + d + '</' + typ + '>');
  130. // get widths from options (only from first row)
  131. if (first && cw && cw[col]) {
  132. $col.width(cw[col] || '');
  133. }
  134. } else {
  135. // assume we have an object
  136. $td = $('<' + typ + '>');
  137. for (j in d) {
  138. if (d.hasOwnProperty(j)){
  139. if (j === 'text' || j === 'html') {
  140. $td[j]( d[j] );
  141. } else if (first && j === 'width') {
  142. // set column width, but only from first row
  143. $col.width(d[j] || '');
  144. } else {
  145. $td.attr(j, d[j]);
  146. }
  147. }
  148. }
  149. }
  150. return [ $td, $col ];
  151. },
  152. // h1 = header text from data
  153. header : function(h1, wo){
  154. var h2 = wo.build_headers.text,
  155. cls = wo.build_headers.classes,
  156. t = '<tr>' + (wo.build_numbers.addColumn ? '<th' + (wo.build_numbers.sortable ? '' :
  157. ' class="sorter-false"') + '>' + wo.build_numbers.addColumn + '</th>' : '');
  158. $.each(h1, function(i, h) {
  159. if (/<\s*\/t(d|h)\s*>/.test(h)) {
  160. t += h;
  161. } else {
  162. t += '<th' + (cls && cls[i] ? ' class="' + cls[i] + '"' : '') + '>' +
  163. (h2 && h2[i] ? h2[i] : h) + '</th>';
  164. }
  165. });
  166. return t + '</tr>';
  167. },
  168. rows : function(items, txt, c, wo, num, ftr){
  169. var h = (ftr ? 'th' : 'td'),
  170. t = '<tr>' + (wo.build_numbers.addColumn ? '<' + h + '>' + (ftr ? '' : num) + '</' + h + '>' : '');
  171. $.each(items, function(i, item) {
  172. // test if HTML is already included; look for closing </td>
  173. if (/<\s*\/t(d|h)\s*>/.test(item)) {
  174. t += item;
  175. } else {
  176. t += '<' + (ftr ? h + (c && c[i] ? ' class="' + c[i] + '"' : '') : h) + '>' +
  177. (ftr && txt && txt.length && txt[i] ? txt[i] : item) + '</' + h + '>';
  178. }
  179. });
  180. return t + '</tr>';
  181. }
  182. };
  183. bt.buildComplete = function(table, wo){
  184. $(table).trigger(wo.build_complete);
  185. ts.setup(table, table.config);
  186. };
  187. /* ==== Array example ====
  188. [
  189. [ "header1", "header2", ... "headerN" ],
  190. [ "row1cell1", "row1cell2", ... "row1cellN" ],
  191. [ "row2cell1", "row2cell2", ... "row2cellN" ],
  192. ...
  193. [ "rowNcell1", "rowNcell2", ... "rowNcellN" ]
  194. ]
  195. */
  196. bt.array = function(table, data, wo) {
  197. return bt.csv(table, data, wo);
  198. };
  199. /* ==== CSV example ====
  200. ID, Name, Age, Date
  201. A42b, Parker, 28, "Jul 6, 2006 8:14 AM"
  202. A255, Hood, 33, "Dec 10, 2002 5:14 AM"
  203. A33, Kent, 18, "Jan 12, 2003 11:14 AM"
  204. A1, Franklin, 45, "Jan 18, 2001 9:12 AM"
  205. A102, Evans, 22, "Jan 18, 2007 9:12 AM"
  206. A42a, Everet, 22, "Jan 18, 2007 9:12 AM"
  207. ID, Name, Age, Date
  208. */
  209. // Adapted & modified from csvToTable.js by Steve Sobel
  210. // MIT license: https://code.google.com/p/jquerycsvtotable/
  211. bt.csv = function(table, data, wo) {
  212. var c, h,
  213. csv = wo.build_type === 'csv' || typeof data === 'string',
  214. $t = $(table),
  215. lines = csv ? data.replace('\r', '').split('\n') : data,
  216. len = lines.length,
  217. printedLines = 0,
  218. infooter = false,
  219. r = wo.build_headers.rows + (csv ? wo.build_csvStartLine : 0),
  220. f = wo.build_footers.rows,
  221. headerCount = 0,
  222. error = '',
  223. items,
  224. tableHTML = bt.build.colgroup( wo.build_headers.widths ) + '<thead>';
  225. $.each(lines, function(n, line) {
  226. if ( n >= len - f ) { infooter = true; }
  227. // build header
  228. if ( (csv ? n >= wo.build_csvStartLine : true) && ( n < r ) ) {
  229. h = csv ? bt.splitCSV( line, wo.build_csvSeparator ) : line;
  230. headerCount = h.length;
  231. tableHTML += bt.build.header(h, wo);
  232. } else if ( n >= r ) {
  233. // build tbody & tfoot rows
  234. if (n === r) {
  235. tableHTML += '</thead><tbody>';
  236. }
  237. items = csv ? bt.splitCSV( line, wo.build_csvSeparator ) : line;
  238. if (infooter && f > 0) {
  239. tableHTML += (n === len - f ? '</tbody><tfoot>' : '') +
  240. (n === len ? '</tfoot>' : '');
  241. }
  242. if (items.length > 1) {
  243. printedLines++;
  244. if ( items.length !== headerCount ) {
  245. error += 'error on line ' + n + ': Item count (' + items.length +
  246. ') does not match header count (' + headerCount + ') \n';
  247. }
  248. c = infooter ? wo.build_footers.classes : '';
  249. tableHTML += bt.build.rows(items, wo.build_footers.text, c, wo, printedLines, infooter);
  250. }
  251. }
  252. });
  253. tableHTML += (f > 0 ? '' : '</tbody>');
  254. if (error) {
  255. $t.html(error);
  256. } else {
  257. $t.html(tableHTML);
  258. bt.buildComplete(table, wo);
  259. }
  260. };
  261. // CSV Parser by Brian Huisman (http://www.greywyvern.com/?post=258)
  262. bt.splitCSV = function(str, sep) {
  263. var x, tl,
  264. thisCSV = $.trim(str).split(sep = sep || ',');
  265. for ( x = thisCSV.length - 1; x >= 0; x-- ) {
  266. if ( thisCSV[x].replace(/\"\s+$/, '"').charAt(thisCSV[x].length - 1) === '"' ) {
  267. if ( (tl = thisCSV[x].replace(/^\s+\"/, '"')).length > 1 && tl.charAt(0) === '"' ) {
  268. thisCSV[x] = thisCSV[x].replace(/^\s*"|"\s*$/g, '').replace(/""/g, '"');
  269. } else if (x) {
  270. thisCSV.splice(x - 1, 2, [ thisCSV[x - 1], thisCSV[x] ].join(sep));
  271. } else {
  272. thisCSV = thisCSV.shift().split(sep).concat(thisCSV);
  273. }
  274. } else {
  275. thisCSV[x].replace(/""/g, '"');
  276. }
  277. }
  278. return thisCSV;
  279. };
  280. // data may be a jQuery object after processing
  281. bt.html = function(table, data, wo) {
  282. var $t = $(table);
  283. if ( data instanceof jQuery ) {
  284. $t.empty().append(data);
  285. } else {
  286. $t.html(data);
  287. }
  288. bt.buildComplete(table, wo);
  289. };
  290. /* ==== Object example ====
  291. data : {
  292. headers : [
  293. [
  294. { text: 'First Name', class: 'fname', width: '20%' }, // row 1 cell 1
  295. 'Last Name',
  296. { text: 'Age', class: 'age', 'data-sorter' : false },
  297. 'Total',
  298. { text: 'Discount', class : 'sorter-false' },
  299. { text: 'Date', class : 'date' } // row 1 cell 6
  300. ]
  301. ],
  302. footers : 'clone', // clone headers or assign array like headers
  303. rows : [
  304. // TBODY 1
  305. [ 'Peter', 'Parker', 28, '$9.99', '20%', 'Jul 6, 2006 8:14 AM' ], // row 1
  306. [ 'John', 'Hood', 33, '$19.99', '25%', 'Dec 10, 2002 5:14 AM' ], // row 2
  307. [ 'Clark', 'Kent', 18, '$15.89', '44%', 'Jan 12, 2003 11:14 AM' ], // row 3
  308. // TBODY 2
  309. { newTbody: true, class: 'tablesorter-infoOnly' },
  310. { cells : [ { text: 'Info Row', colSpan: 6 } ] }, // row 4
  311. // TBODY 3
  312. { newTbody: true },
  313. [ 'Bruce', 'Evans', 22, '$13.19', '11%', 'Jan 18, 2007 9:12 AM' ], // row 5
  314. [ 'Brice', 'Almighty', 45, '$153.19', '44%', 'Jan 18, 2001 9:12 AM' ], // row 6
  315. { class: 'specialRow', // row 7
  316. cells: [
  317. { text: 'Fred', class: 'fname' },
  318. { text: 'Smith', class: 'lname' },
  319. { text: 18, class: 'age', 'data-info': 'fake ID!, he is really 16' },
  320. { text: '$22.44', class: 'total' },
  321. { text: '8%', class: 'discount' },
  322. { text: 'Aug 20, 2012 10:15 AM', class: 'date' }
  323. ],
  324. 'data-info' : 'This row likes turtles'
  325. }
  326. ]
  327. }
  328. */
  329. bt.object = function(table, data, wo) {
  330. // 'rows'
  331. var j, l, t, $c, $t, $tb, $tr,
  332. c = table.config,
  333. kh = wo.build_objectHeaderKey,
  334. kr = wo.build_objectRowKey,
  335. h = data.hasOwnProperty(kh) && !$.isEmptyObject(data.kh) ? data.kh : data.hasOwnProperty('headers') ? data.headers : false,
  336. r = data.hasOwnProperty(kr) && !$.isEmptyObject(data.kr) ? data.kr : data.hasOwnProperty('rows') ? data.rows : false;
  337. if (!h || !r || h.length === 0 || r.length === 0) {
  338. if (c.debug) { console.error('aborting build table widget, missing data for object build'); }
  339. return false;
  340. }
  341. $c = $('<colgroup>');
  342. $t = $('<table><thead/></table>');
  343. // Build thead
  344. // h = [ ['headerRow1Cell1', 'headerRow1Cell2', ... 'headerRow1CellN' ], ['headerRow2Cell1', ... ] ]
  345. // or h = [ [ { text: 'firstCell', class: 'fc', width: '20%' }, ..., { text: 'last Cell' } ], [ /* second row */ ] ]
  346. $.each(h, function(i, d){
  347. $tr = $('<tr>').appendTo( $t.find('thead') );
  348. l = d.length; // header row
  349. for ( j = 0; j < l; j++ ) {
  350. // cell(cellData, widgetOptions, 'th', first row)
  351. t = bt.build.cell(d[j], wo, 'th', j, i === 0);
  352. if (t[0] && t[0].length) { t[0].appendTo( $tr ); } // add cell
  353. if (i === 0 && t[1]) { t[1].appendTo( $c ); } // add col to colgroup
  354. }
  355. });
  356. if ($c.find('col[style]').length) {
  357. // add colgroup if it contains col elements
  358. $t.prepend( $c );
  359. }
  360. $tb = $('<tbody>');
  361. // Build tbody
  362. $.each(r, function(i, d){
  363. var j;
  364. t = $.type(d) === 'object';
  365. // add new tbody
  366. if (t && d.newTbody) {
  367. $tb = $('<tbody>').appendTo( $t );
  368. for (j in d) {
  369. if (d.hasOwnProperty(j) && j !== 'newTbody'){
  370. $tb.attr(j, d[j]);
  371. }
  372. }
  373. } else {
  374. if (i === 0) {
  375. // add tbody, if the first item in the object isn't a call for a new tbody
  376. $tb.appendTo( $t );
  377. }
  378. $tr = $('<tr>').appendTo( $tb );
  379. if (t) {
  380. // row defined by object
  381. for (j in d) {
  382. if (d.hasOwnProperty(j) && j !== wo.build_objectCellKey){
  383. $tr.attr(j, d[j]);
  384. }
  385. }
  386. if (d.hasOwnProperty(wo.build_objectCellKey)) {
  387. // cells contains each cell info
  388. d = d.cells;
  389. }
  390. }
  391. l = d.length;
  392. for ( j = 0; j < l; j++ ) {
  393. // cell(cellData, widgetOptions, 'td')
  394. $c = bt.build.cell(d[j], wo, 'td', j);
  395. if ($c[0] && $c[0].length) { $c[0].appendTo( $tr ); } // add cell
  396. }
  397. }
  398. });
  399. // add footer
  400. if (data.hasOwnProperty(wo.build_objectFooterKey)) {
  401. t = data[wo.build_objectFooterKey];
  402. if (t === 'clone') {
  403. $c = $t.find('thead').html();
  404. $t.append('<tfoot>' + $c + '</tfoot>');
  405. } else {
  406. $c = $('<tfoot>').appendTo( $t );
  407. $.each(t, function(i, d) {
  408. $tr = $('<tr>').appendTo( $c );
  409. l = d.length; // footer cells
  410. for ( j = 0; j < l; j++ ) {
  411. // cell(cellData, widgetOptions, 'th')
  412. $tb = bt.build.cell(d[j], wo, 'th', j);
  413. if ($tb[0] && $tb[0].length) { $tb[0].appendTo( $tr ); } // add cell
  414. }
  415. });
  416. }
  417. }
  418. $(table).html( $t.html() );
  419. bt.buildComplete(table, wo);
  420. };
  421. bt.ajax = bt.json = function(table, data, wo) {
  422. return bt.object(table, data, wo);
  423. };
  424. })(jQuery);