dataTables.checkboxes.js 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178
  1. /*! Checkboxes 1.2.10
  2. * Copyright (c) Gyrocode (www.gyrocode.com)
  3. * License: MIT License
  4. */
  5. /**
  6. * @summary Checkboxes
  7. * @description Checkboxes extension for jQuery DataTables
  8. * @version 1.2.10
  9. * @file dataTables.checkboxes.js
  10. * @author Gyrocode (http://www.gyrocode.com/projects/jquery-datatables-checkboxes/)
  11. * @contact http://www.gyrocode.com/contacts
  12. * @copyright Copyright (c) Gyrocode
  13. * @license MIT License
  14. */
  15. (function( factory ){
  16. /* eslint-disable */
  17. if ( typeof define === 'function' && define.amd ) {
  18. // AMD
  19. define( ['jquery', 'datatables.net'], function ( $ ) {
  20. return factory( $, window, document );
  21. } );
  22. }
  23. else if ( typeof exports === 'object' ) {
  24. // CommonJS
  25. module.exports = function (root, $) {
  26. if ( ! root ) {
  27. root = window;
  28. }
  29. if ( ! $ || ! $.fn.dataTable ) {
  30. $ = require('datatables.net')(root, $).$;
  31. }
  32. return factory( $, root, root.document );
  33. };
  34. }
  35. else {
  36. // Browser
  37. factory( jQuery, window, document );
  38. }
  39. /* eslint-enable */
  40. }(function( $, window, document ) {
  41. 'use strict';
  42. var DataTable = $.fn.dataTable;
  43. /**
  44. * Checkboxes is an extension for the jQuery DataTables library that provides
  45. * universal solution for working with checkboxes in a table.
  46. *
  47. * @class
  48. * @param {object} settings DataTables settings object for the host table
  49. * @requires jQuery 1.7+
  50. * @requires DataTables 1.10.8+
  51. *
  52. * @example
  53. * $('#example').DataTable({
  54. * 'columnDefs': [
  55. * {
  56. * 'targets': 0,
  57. * 'checkboxes': true
  58. * }
  59. * ]
  60. * });
  61. */
  62. var Checkboxes = function ( settings ) {
  63. // Sanity check that we are using DataTables 1.10.8 or newer
  64. if ( ! DataTable.versionCheck || ! DataTable.versionCheck( '1.10.8' ) ) {
  65. throw 'DataTables Checkboxes requires DataTables 1.10.8 or newer';
  66. }
  67. this.s = {
  68. dt: new DataTable.Api( settings ),
  69. columns: [],
  70. data: [],
  71. dataDisabled: [],
  72. ignoreSelect: false
  73. };
  74. // Get settings object
  75. this.s.ctx = this.s.dt.settings()[0];
  76. // Check if checkboxes have already been initialised on this table
  77. if ( this.s.ctx.checkboxes ) {
  78. return;
  79. }
  80. settings.checkboxes = this;
  81. this._constructor();
  82. };
  83. Checkboxes.prototype = {
  84. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  85. * Constructor
  86. */
  87. /**
  88. * Initialise the Checkboxes instance
  89. *
  90. * @private
  91. */
  92. _constructor: function ()
  93. {
  94. var self = this;
  95. var dt = self.s.dt;
  96. var ctx = self.s.ctx;
  97. var hasCheckboxes = false;
  98. var hasCheckboxesSelectRow = false;
  99. // Retrieve stored state
  100. var state = dt.state.loaded();
  101. for(var i = 0; i < ctx.aoColumns.length; i++){
  102. if (ctx.aoColumns[i].checkboxes){
  103. var $colHeader = $(dt.column(i).header());
  104. //
  105. // INITIALIZATION
  106. //
  107. hasCheckboxes = true;
  108. if(!$.isPlainObject(ctx.aoColumns[i].checkboxes)){
  109. ctx.aoColumns[i].checkboxes = {};
  110. }
  111. ctx.aoColumns[i].checkboxes = $.extend(
  112. {}, Checkboxes.defaults, ctx.aoColumns[i].checkboxes
  113. );
  114. //
  115. // OPTIONS
  116. //
  117. var colOptions = {
  118. 'searchable': false,
  119. 'orderable': false
  120. };
  121. if(ctx.aoColumns[i].sClass === ''){
  122. colOptions['className'] = 'dt-checkboxes-cell';
  123. } else {
  124. colOptions['className'] = ctx.aoColumns[i].sClass + ' dt-checkboxes-cell';
  125. }
  126. if(ctx.aoColumns[i].sWidthOrig === null){
  127. colOptions['width'] = '1%';
  128. }
  129. if(ctx.aoColumns[i].mRender === null){
  130. colOptions['render'] = function(){
  131. return '<input type="checkbox" class="dt-checkboxes">';
  132. };
  133. }
  134. DataTable.ext.internal._fnColumnOptions(ctx, i, colOptions);
  135. // WORKAROUND: Remove "sorting" class
  136. $colHeader.removeClass('sorting');
  137. // WORKAROUND: Detach all event handlers for this column
  138. $colHeader.off('.dt');
  139. // If table has data source other than Ajax
  140. if(ctx.sAjaxSource === null){
  141. // WORKAROUND: Invalidate column data
  142. var cells = dt.cells('tr', i);
  143. cells.invalidate('data');
  144. // WORKAROUND: Add required class to existing cells
  145. $(cells.nodes()).addClass(colOptions['className']);
  146. }
  147. //
  148. // DATA
  149. //
  150. // Initialize object holding data for selected checkboxes
  151. self.s.data[i] = {};
  152. self.s.dataDisabled[i] = {};
  153. // If state is loaded and contains data for this column
  154. if(state && state.checkboxes && state.checkboxes.hasOwnProperty(i)){
  155. // If checkbox state saving is enabled
  156. if(ctx.aoColumns[i].checkboxes.stateSave){
  157. // Load previous state
  158. self.s.data[i] = state.checkboxes[i];
  159. }
  160. }
  161. // Store column index for easy column selection later
  162. self.s.columns.push(i);
  163. //
  164. // CLASSES
  165. //
  166. // If row selection is enabled for this column
  167. if(ctx.aoColumns[i].checkboxes.selectRow){
  168. // If Select extension is enabled
  169. if(ctx._select){
  170. hasCheckboxesSelectRow = true;
  171. // Otherwise, if Select extension is not enabled
  172. } else {
  173. // Disable row selection for this column
  174. ctx.aoColumns[i].checkboxes.selectRow = false;
  175. }
  176. }
  177. if(ctx.aoColumns[i].checkboxes.selectAll){
  178. // Save previous HTML content
  179. $colHeader.data('html', $colHeader.html());
  180. // If "Select all" control markup is provided
  181. if(ctx.aoColumns[i].checkboxes.selectAllRender !== null){
  182. var selectAllHtml = '';
  183. // If "selectAllRender" option is a function
  184. if($.isFunction(ctx.aoColumns[i].checkboxes.selectAllRender)){
  185. selectAllHtml = ctx.aoColumns[i].checkboxes.selectAllRender();
  186. // Otherwise, if "selectAllRender" option is a string
  187. } else if(typeof ctx.aoColumns[i].checkboxes.selectAllRender === 'string'){
  188. selectAllHtml = ctx.aoColumns[i].checkboxes.selectAllRender;
  189. }
  190. $colHeader
  191. .html(selectAllHtml)
  192. .addClass('dt-checkboxes-select-all')
  193. .attr('data-col', i);
  194. }
  195. }
  196. }
  197. }
  198. // If table has at least one checkbox
  199. if(hasCheckboxes){
  200. //
  201. // EVENT HANDLERS
  202. //
  203. var $table = $(dt.table().node());
  204. var $tableBody = $(dt.table().body());
  205. var $tableContainer = $(dt.table().container());
  206. // If there is at least one column that has row selection enabled
  207. if(hasCheckboxesSelectRow){
  208. $table.addClass('dt-checkboxes-select');
  209. // Handle event before row is selected/deselected
  210. $table.on('user-select.dt.dtCheckboxes', function ( e, dt, type, cell /*, originalEvent*/ ){
  211. var cellIdx = cell.index();
  212. var rowIdx = cellIdx.row;
  213. var colIdx = self.getSelectRowColIndex();
  214. var cellData = dt.cell({ row: rowIdx, column: colIdx }).data();
  215. // If checkbox in the cell cannot be checked
  216. if(!self.isCellSelectable(colIdx, cellData)){
  217. // Prevent row selection
  218. e.preventDefault();
  219. }
  220. });
  221. // Handle row select/deselect event
  222. $table.on('select.dt.dtCheckboxes deselect.dt.dtCheckboxes', function(e, api, type, indexes){
  223. self.onSelect(e, type, indexes);
  224. });
  225. // Disable Select extension information display
  226. dt.select.info(false);
  227. // Update the table information element with selected item summary
  228. //
  229. // NOTE: Needed to display correct count of selected rows
  230. // when using server-side processing mode
  231. $table.on('draw.dt.dtCheckboxes select.dt.dtCheckboxes deselect.dt.dtCheckboxes', function(){
  232. self.showInfoSelected();
  233. });
  234. }
  235. // Handle table draw event
  236. $table.on('draw.dt.dtCheckboxes', function(e){
  237. self.onDraw(e);
  238. });
  239. // Handle checkbox click event
  240. $tableBody.on('click.dtCheckboxes', 'input.dt-checkboxes', function(e){
  241. self.onClick(e, this);
  242. });
  243. // Handle click on "Select all" control
  244. $tableContainer.on('click.dtCheckboxes', 'thead th.dt-checkboxes-select-all input[type="checkbox"]', function(e){
  245. self.onClickSelectAll(e, this);
  246. });
  247. // Handle click on heading containing "Select all" control
  248. $tableContainer.on('click.dtCheckboxes', 'thead th.dt-checkboxes-select-all', function() {
  249. $('input[type="checkbox"]', this).not(':disabled').trigger('click');
  250. });
  251. // If row selection is disabled
  252. if(!hasCheckboxesSelectRow){
  253. // Handle click on cell containing checkbox
  254. $tableContainer.on('click.dtCheckboxes', 'tbody td.dt-checkboxes-cell', function() {
  255. $('input[type="checkbox"]', this).not(':disabled').trigger('click');
  256. });
  257. }
  258. // Handle click on label node in heading containing "Select all" control
  259. // and in cell containing checkbox
  260. $tableContainer.on('click.dtCheckboxes', 'thead th.dt-checkboxes-select-all label, tbody td.dt-checkboxes-cell label', function(e) {
  261. // Prevent default behavior
  262. e.preventDefault();
  263. });
  264. // Handle click on "Select all" control in floating fixed header
  265. $(document).on('click.dtCheckboxes', '.fixedHeader-floating thead th.dt-checkboxes-select-all input[type="checkbox"]', function(e){
  266. // If FixedHeader is enabled in this instance
  267. if(ctx._fixedHeader){
  268. // If header is floating in this instance
  269. if(ctx._fixedHeader.dom['header'].floating){
  270. self.onClickSelectAll(e, this);
  271. }
  272. }
  273. });
  274. // Handle click on heading containing "Select all" control in floating fixed header
  275. $(document).on('click.dtCheckboxes', '.fixedHeader-floating thead th.dt-checkboxes-select-all', function() {
  276. // If FixedHeader is enabled in this instance
  277. if(ctx._fixedHeader){
  278. // If header is floating in this instance
  279. if(ctx._fixedHeader.dom['header'].floating){
  280. $('input[type="checkbox"]', this).trigger('click');
  281. }
  282. }
  283. });
  284. // Handle table initialization event
  285. $table.on('init.dt.dtCheckboxes', function(){
  286. // If server-side processing mode is not enabled
  287. // NOTE: Needed to avoid duplicate call to updateStateCheckboxes() in onDraw()
  288. if(!ctx.oFeatures.bServerSide){
  289. // If state saving is enabled
  290. if(ctx.oFeatures.bStateSave){
  291. self.updateState();
  292. }
  293. // Handle Ajax request completion event
  294. // NOTE: Needed to update table state
  295. // if table is reloaded via ajax.reload() API method
  296. $table.on('xhr.dt.dtCheckboxes', function ( /* e, settings , json, xhr */ ) {
  297. // For every column where checkboxes are enabled
  298. $.each(self.s.columns, function(index, colIdx){
  299. // Clear data
  300. self.s.data[colIdx] = {};
  301. self.s.dataDisabled[colIdx] = {};
  302. });
  303. // If state saving is enabled
  304. if(ctx.oFeatures.bStateSave){
  305. // Retrieve stored state
  306. var state = dt.state.loaded();
  307. // For every column where checkboxes are enabled
  308. $.each(self.s.columns, function(index, colIdx){
  309. // If state is loaded and contains data for this column
  310. if(state && state.checkboxes && state.checkboxes.hasOwnProperty(colIdx)){
  311. // If checkbox state saving is enabled
  312. if(ctx.aoColumns[colIdx].checkboxes.stateSave){
  313. // Load previous state
  314. self.s.data[colIdx] = state.checkboxes[colIdx];
  315. }
  316. }
  317. });
  318. // Update table state on next redraw
  319. $table.one('draw.dt.dtCheckboxes', function(){
  320. self.updateState();
  321. });
  322. }
  323. });
  324. }
  325. // If state saving is enabled
  326. if(ctx.oFeatures.bStateSave){
  327. // Handle state saving event
  328. $table.on('stateSaveParams.dt.dtCheckboxes', function (e, settings, data){
  329. // Initialize array holding checkbox state for each column
  330. data.checkboxes = [];
  331. // For every column where checkboxes are enabled
  332. $.each(self.s.columns, function(index, colIdx){
  333. // If checkbox state saving is enabled
  334. if(ctx.aoColumns[colIdx].checkboxes.stateSave){
  335. // Store data associated with this plug-in
  336. data.checkboxes[colIdx] = self.s.data[colIdx];
  337. }
  338. });
  339. });
  340. }
  341. });
  342. // Handle table destroy event
  343. $table.one('destroy.dt.dtCheckboxes', function(){
  344. // Detach event handlers
  345. $(document).off('click.dtCheckboxes');
  346. $tableContainer.on('.dtCheckboxes');
  347. $tableBody.off('.dtCheckboxes');
  348. $table.off('.dtCheckboxes');
  349. // Clear data
  350. //
  351. // NOTE: Needed only to reduce memory footprint
  352. // in case user saves instance of DataTable object.
  353. self.s.data = {};
  354. self.s.dataDisabled = {};
  355. // Remove added elements
  356. $('.dt-checkboxes-select-all', $table).each(function(index, el){
  357. $(el)
  358. .html($(el).data('html'))
  359. .removeClass('dt-checkboxes-select-all');
  360. });
  361. });
  362. }
  363. },
  364. // Updates array holding data for selected checkboxes
  365. updateData: function(cells, colIdx, isSelected){
  366. var self = this;
  367. var dt = self.s.dt;
  368. var ctx = self.s.ctx;
  369. // If Checkboxes extension is enabled for this column
  370. if(ctx.aoColumns[colIdx].checkboxes){
  371. var cellsData = cells.data();
  372. cellsData.each(function(cellData){
  373. // If checkbox is checked
  374. if(isSelected){
  375. ctx.checkboxes.s.data[colIdx][cellData] = 1;
  376. // Otherwise, if checkbox is not checked
  377. } else {
  378. delete ctx.checkboxes.s.data[colIdx][cellData];
  379. }
  380. });
  381. // If state saving is enabled
  382. if(ctx.oFeatures.bStateSave){
  383. // If checkbox state saving is enabled
  384. if(ctx.aoColumns[colIdx].checkboxes.stateSave){
  385. // Save state
  386. dt.state.save();
  387. }
  388. }
  389. }
  390. },
  391. // Updates row selection
  392. updateSelect: function(selector, isSelected){
  393. var self = this;
  394. var dt = self.s.dt;
  395. var ctx = self.s.ctx;
  396. // If Select extension is enabled
  397. if(ctx._select){
  398. // Disable select event hanlder temporarily
  399. self.s.ignoreSelect = true;
  400. if(isSelected){
  401. dt.rows(selector).select();
  402. } else {
  403. dt.rows(selector).deselect();
  404. }
  405. // Re-enable select event handler
  406. self.s.ignoreSelect = false;
  407. }
  408. },
  409. // Updates state of single checkbox
  410. updateCheckbox: function(cells, colIdx, isSelected){
  411. var self = this;
  412. var ctx = self.s.ctx;
  413. var cellNodes = cells.nodes();
  414. if(cellNodes.length){
  415. $('input.dt-checkboxes', cellNodes).not(':disabled').prop('checked', isSelected);
  416. // If selectCallback is a function
  417. if($.isFunction(ctx.aoColumns[colIdx].checkboxes.selectCallback)){
  418. ctx.aoColumns[colIdx].checkboxes.selectCallback(cellNodes, isSelected);
  419. }
  420. }
  421. },
  422. // Update table state
  423. updateState: function(){
  424. var self = this;
  425. self.updateStateCheckboxes({ page: 'all', search: 'none' });
  426. $.each(self.s.columns, function(index, colIdx){
  427. self.updateSelectAll(colIdx);
  428. });
  429. },
  430. // Updates state of multiple checkboxes
  431. updateStateCheckboxes: function(opts){
  432. var self = this;
  433. var dt = self.s.dt;
  434. var ctx = self.s.ctx;
  435. // Enumerate all cells
  436. dt.cells('tr', self.s.columns, opts).every(function(rowIdx, colIdx){
  437. // Get cell data
  438. var cellData = this.data();
  439. // Determine if checkbox in the cell can be selected
  440. var isCellSelectable = self.isCellSelectable(colIdx, cellData);
  441. // If checkbox is checked
  442. if(ctx.checkboxes.s.data[colIdx].hasOwnProperty(cellData)){
  443. self.updateCheckbox(this, colIdx, true);
  444. // If row selection is enabled
  445. // and checkbox can be checked
  446. if(ctx.aoColumns[colIdx].checkboxes.selectRow && isCellSelectable){
  447. self.updateSelect(rowIdx, true);
  448. }
  449. }
  450. // If checkbox is disabled
  451. if(!isCellSelectable){
  452. $('input.dt-checkboxes', this.node()).prop('disabled', true);
  453. }
  454. });
  455. },
  456. // Handles checkbox click event
  457. onClick: function(e, ctrl){
  458. var self = this;
  459. var dt = self.s.dt;
  460. var ctx = self.s.ctx;
  461. var cellSelector;
  462. // Get cell
  463. var $cell = $(ctrl).closest('td');
  464. // If cell is in a fixed column using FixedColumns extension
  465. if($cell.parents('.DTFC_Cloned').length){
  466. cellSelector = dt.fixedColumns().cellIndex($cell);
  467. } else {
  468. cellSelector = $cell;
  469. }
  470. var cell = dt.cell(cellSelector);
  471. var cellIdx = cell.index();
  472. var colIdx = cellIdx.column;
  473. // If row selection is not enabled
  474. // NOTE: if row selection is enabled, checkbox selection/deselection
  475. // would be handled by onSelect event instead
  476. if(!ctx.aoColumns[colIdx].checkboxes.selectRow){
  477. cell.checkboxes.select(ctrl.checked);
  478. // Prevent click event from propagating to parent
  479. e.stopPropagation();
  480. } else {
  481. // WORKAROUND:
  482. // Select extension may keep the row selected
  483. // when checkbox is unchecked with SHIFT key.
  484. //
  485. // We need to update the state of the checkbox AFTER handling
  486. // select/deselect event from Select extension.
  487. //
  488. // Call to setTimeout is needed to let select/deselect event handler
  489. // update the data first.
  490. setTimeout(function(){
  491. // Get cell data
  492. var cellData = cell.data();
  493. // Determine whether data is in the list
  494. var hasData = self.s.data[colIdx].hasOwnProperty(cellData);
  495. // If state of the checkbox needs to be updated
  496. if(hasData !== ctrl.checked){
  497. self.updateCheckbox(cell, colIdx, hasData);
  498. self.updateSelectAll(colIdx);
  499. }
  500. }, 0);
  501. }
  502. },
  503. // Handles row select/deselect event
  504. onSelect: function(e, type, indexes){
  505. var self = this;
  506. var dt = self.s.dt;
  507. if(self.s.ignoreSelect){ return; }
  508. if(type === 'row'){
  509. // Get index of the first column that has checkbox and row selection enabled
  510. var colIdx = self.getSelectRowColIndex();
  511. if(colIdx !== null){
  512. var cells = dt.cells(indexes, colIdx);
  513. self.updateData(cells, colIdx, (e.type === 'select') ? true : false);
  514. self.updateCheckbox(cells, colIdx, (e.type === 'select') ? true : false);
  515. self.updateSelectAll(colIdx);
  516. }
  517. }
  518. },
  519. // Handles checkbox click event
  520. onClickSelectAll: function(e, ctrl){
  521. var self = this;
  522. var dt = self.s.dt;
  523. var ctx = self.s.ctx;
  524. // Calculate column index
  525. var colIdx = null;
  526. var $th = $(ctrl).closest('th');
  527. // If column is fixed using FixedColumns extension
  528. if($th.parents('.DTFC_Cloned').length){
  529. var cellIdx = dt.fixedColumns().cellIndex($th);
  530. colIdx = cellIdx.column;
  531. } else {
  532. colIdx = dt.column($th).index();
  533. }
  534. // Indicate that state of "Select all" control has been changed
  535. $(ctrl).data('is-changed', true);
  536. dt.column(colIdx, {
  537. page: (
  538. (ctx.aoColumns[colIdx].checkboxes && ctx.aoColumns[colIdx].checkboxes.selectAllPages)
  539. ? 'all'
  540. : 'current'
  541. ),
  542. search: 'applied'
  543. }).checkboxes.select(ctrl.checked);
  544. // Prevent click event from propagating to parent
  545. e.stopPropagation();
  546. },
  547. // Handles table draw event
  548. onDraw: function(){
  549. var self = this;
  550. var ctx = self.s.ctx;
  551. // If server-side processing is enabled
  552. // or deferred render is enabled
  553. //
  554. // TODO: it's not optimal to update state of checkboxes
  555. // for already created rows in deferred rendering mode
  556. if(ctx.oFeatures.bServerSide || ctx.oFeatures.bDeferRender){
  557. self.updateStateCheckboxes({ page: 'current', search: 'none' });
  558. }
  559. $.each(self.s.columns, function(index, colIdx){
  560. self.updateSelectAll(colIdx);
  561. });
  562. },
  563. // Updates state of the "Select all" controls
  564. updateSelectAll: function(colIdx){
  565. var self = this;
  566. var dt = self.s.dt;
  567. var ctx = self.s.ctx;
  568. // If Checkboxes extension is enabled for this column
  569. // and "Select all" control is enabled for this column
  570. if(ctx.aoColumns[colIdx].checkboxes && ctx.aoColumns[colIdx].checkboxes.selectAll){
  571. var cells = dt.cells('tr', colIdx, {
  572. page: (
  573. (ctx.aoColumns[colIdx].checkboxes.selectAllPages)
  574. ? 'all'
  575. : 'current'
  576. ),
  577. search: 'applied'
  578. });
  579. var $tableContainer = dt.table().container();
  580. var $checkboxesSelectAll = $('.dt-checkboxes-select-all[data-col="' + colIdx + '"] input[type="checkbox"]', $tableContainer);
  581. var countChecked = 0;
  582. var countDisabled = 0;
  583. var cellsData = cells.data();
  584. $.each(cellsData, function(index, cellData){
  585. // If checkbox is not disabled
  586. if(self.isCellSelectable(colIdx, cellData)){
  587. if(self.s.data[colIdx].hasOwnProperty(cellData)){ countChecked++; }
  588. // Otherwise, if checkbox is disabled
  589. } else {
  590. countDisabled++;
  591. }
  592. });
  593. // If FixedHeader is enabled in this instance
  594. if(ctx._fixedHeader){
  595. // If header is floating in this instance
  596. if(ctx._fixedHeader.dom['header'].floating){
  597. $checkboxesSelectAll = $('.fixedHeader-floating .dt-checkboxes-select-all[data-col="' + colIdx + '"] input[type="checkbox"]');
  598. }
  599. }
  600. var isSelected;
  601. var isIndeterminate;
  602. // If none of the checkboxes are checked
  603. if (countChecked === 0){
  604. isSelected = false;
  605. isIndeterminate = false;
  606. // If all of the checkboxes are checked
  607. } else if ((countChecked + countDisabled) === cellsData.length){
  608. isSelected = true;
  609. isIndeterminate = false;
  610. // If some of the checkboxes are checked
  611. } else {
  612. isSelected = true;
  613. isIndeterminate = true;
  614. }
  615. var isChanged = $checkboxesSelectAll.data('is-changed');
  616. var isSelectedNow = $checkboxesSelectAll.prop('checked');
  617. var isIndeterminateNow = $checkboxesSelectAll.prop('indeterminate');
  618. // If state of "Select all" control has been changed
  619. if(isChanged || isSelectedNow !== isSelected || isIndeterminateNow !== isIndeterminate){
  620. // Reset "Select all" control state flag
  621. $checkboxesSelectAll.data('is-changed', false);
  622. $checkboxesSelectAll.prop({
  623. 'checked': isSelected,
  624. 'indeterminate': isIndeterminate
  625. });
  626. // If selectAllCallback is a function
  627. if($.isFunction(ctx.aoColumns[colIdx].checkboxes.selectAllCallback)){
  628. ctx.aoColumns[colIdx].checkboxes.selectAllCallback($checkboxesSelectAll.closest('th').get(0), isSelected, isIndeterminate);
  629. }
  630. }
  631. }
  632. },
  633. // Updates the information element of the DataTable showing information about the
  634. // items selected. Based on info() method of Select extension.
  635. showInfoSelected: function(){
  636. var self = this;
  637. var dt = self.s.dt;
  638. var ctx = self.s.ctx;
  639. if ( ! ctx.aanFeatures.i ) {
  640. return;
  641. }
  642. var $output = $('<span class="select-info"/>');
  643. var add = function(name, num){
  644. $output.append( $('<span class="select-item"/>').append( dt.i18n(
  645. 'select.'+name+'s',
  646. { _: '%d '+name+'s selected', 0: '', 1: '1 '+name+' selected' },
  647. num
  648. ) ) );
  649. };
  650. // Get index of the first column that has checkbox and row selection enabled
  651. var colIdx = self.getSelectRowColIndex();
  652. // If there is a column that has checkbox and row selection enabled
  653. if(colIdx !== null){
  654. // Count number of selected rows
  655. var countRows = 0;
  656. for (var cellData in ctx.checkboxes.s.data[colIdx]){
  657. if (ctx.checkboxes.s.data[colIdx].hasOwnProperty(cellData)){
  658. countRows++;
  659. }
  660. }
  661. add('row', countRows);
  662. // Internal knowledge of DataTables to loop over all information elements
  663. $.each( ctx.aanFeatures.i, function ( i, el ) {
  664. var $el = $(el);
  665. var $existing = $el.children('span.select-info');
  666. if($existing.length){
  667. $existing.remove();
  668. }
  669. if($output.text() !== ''){
  670. $el.append($output);
  671. }
  672. });
  673. }
  674. },
  675. // Determines whether checkbox in the cell can be checked
  676. isCellSelectable: function(colIdx, cellData){
  677. var self = this;
  678. var ctx = self.s.ctx;
  679. // If data is in the list of disabled elements
  680. if(ctx.checkboxes.s.dataDisabled[colIdx].hasOwnProperty(cellData)){
  681. return false;
  682. // Otherwise, if checkbox can be selected
  683. } else {
  684. return true;
  685. }
  686. },
  687. // Gets cell index
  688. getCellIndex: function(cell){
  689. var self = this;
  690. var dt = self.s.dt;
  691. var ctx = self.s.ctx;
  692. // If FixedColumns extension is available
  693. if(ctx._oFixedColumns){
  694. return dt.fixedColumns().cellIndex(cell);
  695. } else {
  696. return dt.cell(cell).index();
  697. }
  698. },
  699. // Gets index of the first column that has checkbox and row selection enabled
  700. getSelectRowColIndex: function(){
  701. var self = this;
  702. var ctx = self.s.ctx;
  703. var colIdx = null;
  704. for(var i = 0; i < ctx.aoColumns.length; i++){
  705. // If Checkboxes extension is enabled
  706. // and row selection is enabled for this column
  707. if(ctx.aoColumns[i].checkboxes && ctx.aoColumns[i].checkboxes.selectRow){
  708. colIdx = i;
  709. break;
  710. }
  711. }
  712. return colIdx;
  713. },
  714. // Updates fixed column if FixedColumns extension is enabled
  715. // and given column is inside a fixed column
  716. updateFixedColumn: function(colIdx){
  717. var self = this;
  718. var dt = self.s.dt;
  719. var ctx = self.s.ctx;
  720. // If FixedColumns extension is enabled
  721. if(ctx._oFixedColumns){
  722. var leftCols = ctx._oFixedColumns.s.iLeftColumns;
  723. var rightCols = ctx.aoColumns.length - ctx._oFixedColumns.s.iRightColumns - 1;
  724. if (colIdx < leftCols || colIdx > rightCols){
  725. dt.fixedColumns().update();
  726. }
  727. }
  728. }
  729. };
  730. /**
  731. * Checkboxes default settings for initialisation
  732. *
  733. * @namespace
  734. * @name Checkboxes.defaults
  735. * @static
  736. */
  737. Checkboxes.defaults = {
  738. /**
  739. * Enable / disable checkbox state loading/saving if state saving is enabled globally
  740. *
  741. * @type {Boolean}
  742. * @default `true`
  743. */
  744. stateSave: true,
  745. /**
  746. * Enable / disable row selection
  747. *
  748. * @type {Boolean}
  749. * @default `false`
  750. */
  751. selectRow: false,
  752. /**
  753. * Enable / disable "select all" control in the header
  754. *
  755. * @type {Boolean}
  756. * @default `true`
  757. */
  758. selectAll: true,
  759. /**
  760. * Enable / disable ability to select checkboxes from all pages
  761. *
  762. * @type {Boolean}
  763. * @default `true`
  764. */
  765. selectAllPages: true,
  766. /**
  767. * Checkbox select/deselect callback
  768. *
  769. * @type {Function}
  770. * @default `null`
  771. */
  772. selectCallback: null,
  773. /**
  774. * "Select all" control select/deselect callback
  775. *
  776. * @type {Function}
  777. * @default `null`
  778. */
  779. selectAllCallback: null,
  780. /**
  781. * "Select all" control markup
  782. *
  783. * @type {mixed}
  784. * @default `<input type="checkbox">`
  785. */
  786. selectAllRender: '<input type="checkbox">'
  787. };
  788. /*
  789. * API
  790. */
  791. var Api = $.fn.dataTable.Api;
  792. // Doesn't do anything - work around for a bug in DT... Not documented
  793. Api.register( 'checkboxes()', function () {
  794. return this;
  795. } );
  796. Api.registerPlural( 'columns().checkboxes.select()', 'column().checkboxes.select()', function ( state ) {
  797. if(typeof state === 'undefined'){ state = true; }
  798. return this.iterator( 'column-rows', function ( ctx, colIdx, i, j, rowsIdx ) {
  799. // If Checkboxes extension is enabled for this column
  800. if(ctx.aoColumns[colIdx].checkboxes){
  801. // Prepare a list of all cells
  802. var selector = [];
  803. $.each(rowsIdx, function(index, rowIdx){
  804. selector.push({ row: rowIdx, column: colIdx });
  805. });
  806. var cells = this.cells(selector);
  807. var cellsData = cells.data();
  808. // Prepare a list of cells that contain checkboxes that can be selected
  809. var rowsSelectableIdx = [];
  810. selector = [];
  811. $.each(cellsData, function(index, cellData){
  812. // If checkbox in the cell can be selected
  813. if(ctx.checkboxes.isCellSelectable(colIdx, cellData)){
  814. selector.push({ row: rowsIdx[index], column: colIdx });
  815. rowsSelectableIdx.push(rowsIdx[index]);
  816. }
  817. });
  818. cells = this.cells(selector);
  819. ctx.checkboxes.updateData(cells, colIdx, state);
  820. ctx.checkboxes.updateCheckbox(cells, colIdx, state);
  821. // If row selection is enabled
  822. if(ctx.aoColumns[colIdx].checkboxes.selectRow){
  823. ctx.checkboxes.updateSelect(rowsSelectableIdx, state);
  824. }
  825. // If FixedColumns extension is enabled
  826. if(ctx._oFixedColumns){
  827. // Use timeout to let FixedColumns construct the header
  828. // before we update the "Select all" checkbox
  829. setTimeout(function(){ ctx.checkboxes.updateSelectAll(colIdx); }, 0);
  830. } else {
  831. ctx.checkboxes.updateSelectAll(colIdx);
  832. }
  833. ctx.checkboxes.updateFixedColumn(colIdx);
  834. }
  835. }, 1 );
  836. } );
  837. Api.registerPlural( 'cells().checkboxes.select()', 'cell().checkboxes.select()', function ( state ) {
  838. if(typeof state === 'undefined'){ state = true; }
  839. return this.iterator( 'cell', function ( ctx, rowIdx, colIdx ) {
  840. // If Checkboxes extension is enabled for this column
  841. if(ctx.aoColumns[colIdx].checkboxes){
  842. var cells = this.cells([{ row: rowIdx, column: colIdx }]);
  843. var cellData = this.cell({ row: rowIdx, column: colIdx }).data();
  844. // If checkbox in the cell can be selected
  845. if(ctx.checkboxes.isCellSelectable(colIdx, cellData)){
  846. ctx.checkboxes.updateData(cells, colIdx, state);
  847. ctx.checkboxes.updateCheckbox(cells, colIdx, state);
  848. // If row selection is enabled
  849. if(ctx.aoColumns[colIdx].checkboxes.selectRow){
  850. ctx.checkboxes.updateSelect(rowIdx, state);
  851. }
  852. // If FixedColumns extension is enabled
  853. if(ctx._oFixedColumns){
  854. // Use timeout to let FixedColumns construct the header
  855. // before we update the "Select all" checkbox
  856. setTimeout(function(){ ctx.checkboxes.updateSelectAll(colIdx); }, 0);
  857. } else {
  858. ctx.checkboxes.updateSelectAll(colIdx);
  859. }
  860. ctx.checkboxes.updateFixedColumn(colIdx);
  861. }
  862. }
  863. }, 1 );
  864. } );
  865. Api.registerPlural( 'cells().checkboxes.enable()', 'cell().checkboxes.enable()', function ( state ) {
  866. if(typeof state === 'undefined'){ state = true; }
  867. return this.iterator( 'cell', function ( ctx, rowIdx, colIdx ) {
  868. // If Checkboxes extension is enabled for this column
  869. if(ctx.aoColumns[colIdx].checkboxes){
  870. var cell = this.cell({ row: rowIdx, column: colIdx });
  871. // Get cell data
  872. var cellData = cell.data();
  873. // If checkbox should be enabled
  874. if(state){
  875. delete ctx.checkboxes.s.dataDisabled[colIdx][cellData];
  876. // Otherwise, if checkbox should be disabled
  877. } else {
  878. ctx.checkboxes.s.dataDisabled[colIdx][cellData] = 1;
  879. }
  880. // Determine if cell node is available
  881. // (deferRender is not enabled or cell has been already created)
  882. var cellNode = cell.node();
  883. if(cellNode){
  884. $('input.dt-checkboxes', cellNode).prop('disabled', !state);
  885. }
  886. // If row selection is enabled
  887. // and checkbox can be checked
  888. if(ctx.aoColumns[colIdx].checkboxes.selectRow){
  889. // If data is in the list
  890. if(ctx.checkboxes.s.data[colIdx].hasOwnProperty(cellData)){
  891. // Update selection based on current state:
  892. // if checkbox is enabled then select row;
  893. // otherwise, deselect row
  894. ctx.checkboxes.updateSelect(rowIdx, state);
  895. }
  896. }
  897. }
  898. }, 1 );
  899. } );
  900. Api.registerPlural( 'cells().checkboxes.disable()', 'cell().checkboxes.disable()', function ( state ) {
  901. if(typeof state === 'undefined'){ state = true; }
  902. return this.checkboxes.enable(!state);
  903. } );
  904. Api.registerPlural( 'columns().checkboxes.deselect()', 'column().checkboxes.deselect()', function ( state ) {
  905. if(typeof state === 'undefined'){ state = true; }
  906. return this.checkboxes.select(!state);
  907. } );
  908. Api.registerPlural( 'cells().checkboxes.deselect()', 'cell().checkboxes.deselect()', function ( state ) {
  909. if(typeof state === 'undefined'){ state = true; }
  910. return this.checkboxes.select(!state);
  911. } );
  912. Api.registerPlural( 'columns().checkboxes.deselectAll()', 'column().checkboxes.deselectAll()', function () {
  913. return this.iterator( 'column', function (ctx, colIdx){
  914. // If Checkboxes extension is enabled for this column
  915. if(ctx.aoColumns[colIdx].checkboxes){
  916. ctx.checkboxes.s.data[colIdx] = {};
  917. this.column(colIdx).checkboxes.select(false);
  918. }
  919. }, 1 );
  920. } );
  921. Api.registerPlural( 'columns().checkboxes.selected()', 'column().checkboxes.selected()', function () {
  922. return this.iterator( 'column-rows', function ( ctx, colIdx, i, j, rowsIdx ) {
  923. // If Checkboxes extension is enabled for this column
  924. if(ctx.aoColumns[colIdx].checkboxes){
  925. // Prepare a list of all cells
  926. var selector = [];
  927. $.each(rowsIdx, function(index, rowIdx){
  928. selector.push({ row: rowIdx, column: colIdx });
  929. });
  930. // Get all cells data
  931. var cells = this.cells(selector);
  932. var cellsData = cells.data();
  933. var data = [];
  934. // Enumerate all cells data
  935. $.each(cellsData, function(index, cellData){
  936. // If checkbox is checked
  937. if(ctx.checkboxes.s.data[colIdx].hasOwnProperty(cellData)){
  938. // If checkbox in the cell can be selected
  939. if(ctx.checkboxes.isCellSelectable(colIdx, cellData)){
  940. data.push(cellData);
  941. }
  942. }
  943. });
  944. return data;
  945. } else {
  946. return [];
  947. }
  948. }, 1 );
  949. } );
  950. /**
  951. * Version information
  952. *
  953. * @name Checkboxes.version
  954. * @static
  955. */
  956. Checkboxes.version = '1.2.10';
  957. $.fn.DataTable.Checkboxes = Checkboxes;
  958. $.fn.dataTable.Checkboxes = Checkboxes;
  959. // Attach a listener to the document which listens for DataTables initialisation
  960. // events so we can automatically initialise
  961. $(document).on( 'preInit.dt.dtCheckboxes', function (e, settings /*, json */ ) {
  962. if ( e.namespace !== 'dt' ) {
  963. return;
  964. }
  965. new Checkboxes( settings );
  966. } );
  967. return Checkboxes;
  968. }));