LaravelTest
1265 строк · 33.4 Кб
1/*! Select for DataTables 1.3.4-dev
2* 2015-2021 SpryMedia Ltd - datatables.net/license/mit
3*/
4
5/**
6* @summary Select for DataTables
7* @description A collection of API methods, events and buttons for DataTables
8* that provides selection options of the items in a DataTable
9* @version 1.3.4-dev
10* @file dataTables.select.js
11* @author SpryMedia Ltd (www.sprymedia.co.uk)
12* @contact datatables.net/forums
13* @copyright Copyright 2015-2021 SpryMedia Ltd.
14*
15* This source file is free software, available under the following license:
16* MIT license - http://datatables.net/license/mit
17*
18* This source file is distributed in the hope that it will be useful, but
19* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
20* or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
21*
22* For details please refer to: http://www.datatables.net/extensions/select
23*/
24(function( factory ){25if ( typeof define === 'function' && define.amd ) {26// AMD27define( ['jquery', 'datatables.net'], function ( $ ) {28return factory( $, window, document );29} );30}31else if ( typeof exports === 'object' ) {32// CommonJS33module.exports = function (root, $) {34if ( ! root ) {35root = window;36}37
38if ( ! $ || ! $.fn.dataTable ) {39$ = require('datatables.net')(root, $).$;40}41
42return factory( $, root, root.document );43};44}45else {46// Browser47factory( jQuery, window, document );48}49}(function( $, window, document, undefined ) {50'use strict';51var DataTable = $.fn.dataTable;52
53
54// Version information for debugger
55DataTable.select = {};56
57DataTable.select.version = '1.3.4-dev';58
59DataTable.select.init = function ( dt ) {60var ctx = dt.settings()[0];61
62if (ctx._select) {63return;64}65
66var savedSelected = dt.state.loaded();67
68var selectAndSave = function(e, settings, data) {69if(data === null || data.select === undefined) {70return;71}72dt.rows().deselect();73dt.columns().deselect();74dt.cells().deselect();75if (data.select.rows !== undefined) {76dt.rows(data.select.rows).select();77}78if (data.select.columns !== undefined) {79dt.columns(data.select.columns).select();80}81if (data.select.cells !== undefined) {82for(var i = 0; i < data.select.cells.length; i++) {83dt.cell(data.select.cells[i].row, data.select.cells[i].column).select();84}85}86dt.state.save();87}88
89dt.one('init', function() {90dt.on('stateSaveParams', function(e, settings, data) {91data.select = {};92data.select.rows = dt.rows({selected:true}).ids(true).toArray();93data.select.columns = dt.columns({selected:true})[0];94data.select.cells = dt.cells({selected:true})[0].map(function(coords) {95return {row: dt.row(coords.row).id(true), column: coords.column}96});97})98
99selectAndSave(undefined, undefined, savedSelected)100dt.on('stateLoaded stateLoadParams', selectAndSave)101})102
103var init = ctx.oInit.select;104var defaults = DataTable.defaults.select;105var opts = init === undefined ?106defaults :107init;108
109// Set defaults110var items = 'row';111var style = 'api';112var blurable = false;113var toggleable = true;114var info = true;115var selector = 'td, th';116var className = 'selected';117var setStyle = false;118
119ctx._select = {};120
121// Initialisation customisations122if ( opts === true ) {123style = 'os';124setStyle = true;125}126else if ( typeof opts === 'string' ) {127style = opts;128setStyle = true;129}130else if ( $.isPlainObject( opts ) ) {131if ( opts.blurable !== undefined ) {132blurable = opts.blurable;133}134
135if ( opts.toggleable !== undefined ) {136toggleable = opts.toggleable;137}138
139if ( opts.info !== undefined ) {140info = opts.info;141}142
143if ( opts.items !== undefined ) {144items = opts.items;145}146
147if ( opts.style !== undefined ) {148style = opts.style;149setStyle = true;150}151else {152style = 'os';153setStyle = true;154}155
156if ( opts.selector !== undefined ) {157selector = opts.selector;158}159
160if ( opts.className !== undefined ) {161className = opts.className;162}163}164
165dt.select.selector( selector );166dt.select.items( items );167dt.select.style( style );168dt.select.blurable( blurable );169dt.select.toggleable( toggleable );170dt.select.info( info );171ctx._select.className = className;172
173
174// Sort table based on selected rows. Requires Select Datatables extension175$.fn.dataTable.ext.order['select-checkbox'] = function ( settings, col ) {176return this.api().column( col, {order: 'index'} ).nodes().map( function ( td ) {177if ( settings._select.items === 'row' ) {178return $( td ).parent().hasClass( settings._select.className );179} else if ( settings._select.items === 'cell' ) {180return $( td ).hasClass( settings._select.className );181}182return false;183});184};185
186// If the init options haven't enabled select, but there is a selectable187// class name, then enable188if ( ! setStyle && $( dt.table().node() ).hasClass( 'selectable' ) ) {189dt.select.style( 'os' );190}191};192
193/*
194
195Select is a collection of API methods, event handlers, event emitters and
196buttons (for the `Buttons` extension) for DataTables. It provides the following
197features, with an overview of how they are implemented:
198
199## Selection of rows, columns and cells. Whether an item is selected or not is
200stored in:
201
202* rows: a `_select_selected` property which contains a boolean value of the
203DataTables' `aoData` object for each row
204* columns: a `_select_selected` property which contains a boolean value of the
205DataTables' `aoColumns` object for each column
206* cells: a `_selected_cells` property which contains an array of boolean values
207of the `aoData` object for each row. The array is the same length as the
208columns array, with each element of it representing a cell.
209
210This method of using boolean flags allows Select to operate when nodes have not
211been created for rows / cells (DataTables' defer rendering feature).
212
213## API methods
214
215A range of API methods are available for triggering selection and de-selection
216of rows. Methods are also available to configure the selection events that can
217be triggered by an end user (such as which items are to be selected). To a large
218extent, these of API methods *is* Select. It is basically a collection of helper
219functions that can be used to select items in a DataTable.
220
221Configuration of select is held in the object `_select` which is attached to the
222DataTables settings object on initialisation. Select being available on a table
223is not optional when Select is loaded, but its default is for selection only to
224be available via the API - so the end user wouldn't be able to select rows
225without additional configuration.
226
227The `_select` object contains the following properties:
228
229```
230{
231items:string - Can be `rows`, `columns` or `cells`. Defines what item
232will be selected if the user is allowed to activate row
233selection using the mouse.
234style:string - Can be `none`, `single`, `multi` or `os`. Defines the
235interaction style when selecting items
236blurable:boolean - If row selection can be cleared by clicking outside of
237the table
238toggleable:boolean - If row selection can be cancelled by repeated clicking
239on the row
240info:boolean - If the selection summary should be shown in the table
241information elements
242}
243```
244
245In addition to the API methods, Select also extends the DataTables selector
246options for rows, columns and cells adding a `selected` option to the selector
247options object, allowing the developer to select only selected items or
248unselected items.
249
250## Mouse selection of items
251
252Clicking on items can be used to select items. This is done by a simple event
253handler that will select the items using the API methods.
254
255*/
256
257
258/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
259* Local functions
260*/
261
262/**
263* Add one or more cells to the selection when shift clicking in OS selection
264* style cell selection.
265*
266* Cell range is more complicated than row and column as we want to select
267* in the visible grid rather than by index in sequence. For example, if you
268* click first in cell 1-1 and then shift click in 2-2 - cells 1-2 and 2-1
269* should also be selected (and not 1-3, 1-4. etc)
270*
271* @param {DataTable.Api} dt DataTable
272* @param {object} idx Cell index to select to
273* @param {object} last Cell index to select from
274* @private
275*/
276function cellRange( dt, idx, last )277{
278var indexes;279var columnIndexes;280var rowIndexes;281var selectColumns = function ( start, end ) {282if ( start > end ) {283var tmp = end;284end = start;285start = tmp;286}287
288var record = false;289return dt.columns( ':visible' ).indexes().filter( function (i) {290if ( i === start ) {291record = true;292}293
294if ( i === end ) { // not else if, as start might === end295record = false;296return true;297}298
299return record;300} );301};302
303var selectRows = function ( start, end ) {304var indexes = dt.rows( { search: 'applied' } ).indexes();305
306// Which comes first - might need to swap307if ( indexes.indexOf( start ) > indexes.indexOf( end ) ) {308var tmp = end;309end = start;310start = tmp;311}312
313var record = false;314return indexes.filter( function (i) {315if ( i === start ) {316record = true;317}318
319if ( i === end ) {320record = false;321return true;322}323
324return record;325} );326};327
328if ( ! dt.cells( { selected: true } ).any() && ! last ) {329// select from the top left cell to this one330columnIndexes = selectColumns( 0, idx.column );331rowIndexes = selectRows( 0 , idx.row );332}333else {334// Get column indexes between old and new335columnIndexes = selectColumns( last.column, idx.column );336rowIndexes = selectRows( last.row , idx.row );337}338
339indexes = dt.cells( rowIndexes, columnIndexes ).flatten();340
341if ( ! dt.cells( idx, { selected: true } ).any() ) {342// Select range343dt.cells( indexes ).select();344}345else {346// Deselect range347dt.cells( indexes ).deselect();348}349}
350
351/**
352* Disable mouse selection by removing the selectors
353*
354* @param {DataTable.Api} dt DataTable to remove events from
355* @private
356*/
357function disableMouseSelection( dt )358{
359var ctx = dt.settings()[0];360var selector = ctx._select.selector;361
362$( dt.table().container() )363.off( 'mousedown.dtSelect', selector )364.off( 'mouseup.dtSelect', selector )365.off( 'click.dtSelect', selector );366
367$('body').off( 'click.dtSelect' + _safeId(dt.table().node()) );368}
369
370/**
371* Attach mouse listeners to the table to allow mouse selection of items
372*
373* @param {DataTable.Api} dt DataTable to remove events from
374* @private
375*/
376function enableMouseSelection ( dt )377{
378var container = $( dt.table().container() );379var ctx = dt.settings()[0];380var selector = ctx._select.selector;381var matchSelection;382
383container
384.on( 'mousedown.dtSelect', selector, function(e) {385// Disallow text selection for shift clicking on the table so multi386// element selection doesn't look terrible!387if ( e.shiftKey || e.metaKey || e.ctrlKey ) {388container
389.css( '-moz-user-select', 'none' )390.one('selectstart.dtSelect', selector, function () {391return false;392} );393}394
395if ( window.getSelection ) {396matchSelection = window.getSelection();397}398} )399.on( 'mouseup.dtSelect', selector, function() {400// Allow text selection to occur again, Mozilla style (tested in FF401// 35.0.1 - still required)402container.css( '-moz-user-select', '' );403} )404.on( 'click.dtSelect', selector, function ( e ) {405var items = dt.select.items();406var idx;407
408// If text was selected (click and drag), then we shouldn't change409// the row's selected state410if ( matchSelection ) {411var selection = window.getSelection();412
413// If the element that contains the selection is not in the table, we can ignore it414// This can happen if the developer selects text from the click event415if ( ! selection.anchorNode || $(selection.anchorNode).closest('table')[0] === dt.table().node() ) {416if ( selection !== matchSelection ) {417return;418}419}420}421
422var ctx = dt.settings()[0];423var wrapperClass = dt.settings()[0].oClasses.sWrapper.trim().replace(/ +/g, '.');424
425// Ignore clicks inside a sub-table426if ( $(e.target).closest('div.'+wrapperClass)[0] != dt.table().container() ) {427return;428}429
430var cell = dt.cell( $(e.target).closest('td, th') );431
432// Check the cell actually belongs to the host DataTable (so child433// rows, etc, are ignored)434if ( ! cell.any() ) {435return;436}437
438var event = $.Event('user-select.dt');439eventTrigger( dt, event, [ items, cell, e ] );440
441if ( event.isDefaultPrevented() ) {442return;443}444
445var cellIndex = cell.index();446if ( items === 'row' ) {447idx = cellIndex.row;448typeSelect( e, dt, ctx, 'row', idx );449}450else if ( items === 'column' ) {451idx = cell.index().column;452typeSelect( e, dt, ctx, 'column', idx );453}454else if ( items === 'cell' ) {455idx = cell.index();456typeSelect( e, dt, ctx, 'cell', idx );457}458
459ctx._select_lastCell = cellIndex;460} );461
462// Blurable463$('body').on( 'click.dtSelect' + _safeId(dt.table().node()), function ( e ) {464if ( ctx._select.blurable ) {465// If the click was inside the DataTables container, don't blur466if ( $(e.target).parents().filter( dt.table().container() ).length ) {467return;468}469
470// Ignore elements which have been removed from the DOM (i.e. paging471// buttons)472if ( $(e.target).parents('html').length === 0 ) {473return;474}475
476// Don't blur in Editor form477if ( $(e.target).parents('div.DTE').length ) {478return;479}480
481clear( ctx, true );482}483} );484}
485
486/**
487* Trigger an event on a DataTable
488*
489* @param {DataTable.Api} api DataTable to trigger events on
490* @param {boolean} selected true if selected, false if deselected
491* @param {string} type Item type acting on
492* @param {boolean} any Require that there are values before
493* triggering
494* @private
495*/
496function eventTrigger ( api, type, args, any )497{
498if ( any && ! api.flatten().length ) {499return;500}501
502if ( typeof type === 'string' ) {503type = type +'.dt';504}505
506args.unshift( api );507
508$(api.table().node()).trigger( type, args );509}
510
511/**
512* Update the information element of the DataTable showing information about the
513* items selected. This is done by adding tags to the existing text
514*
515* @param {DataTable.Api} api DataTable to update
516* @private
517*/
518function info ( api )519{
520var ctx = api.settings()[0];521
522if ( ! ctx._select.info || ! ctx.aanFeatures.i ) {523return;524}525
526if ( api.select.style() === 'api' ) {527return;528}529
530var rows = api.rows( { selected: true } ).flatten().length;531var columns = api.columns( { selected: true } ).flatten().length;532var cells = api.cells( { selected: true } ).flatten().length;533
534var add = function ( el, name, num ) {535el.append( $('<span class="select-item"/>').append( api.i18n(536'select.'+name+'s',537{ _: '%d '+name+'s selected', 0: '', 1: '1 '+name+' selected' },538num
539) ) );540};541
542// Internal knowledge of DataTables to loop over all information elements543$.each( ctx.aanFeatures.i, function ( i, el ) {544el = $(el);545
546var output = $('<span class="select-info"/>');547add( output, 'row', rows );548add( output, 'column', columns );549add( output, 'cell', cells );550
551var exisiting = el.children('span.select-info');552if ( exisiting.length ) {553exisiting.remove();554}555
556if ( output.text() !== '' ) {557el.append( output );558}559} );560}
561
562/**
563* Initialisation of a new table. Attach event handlers and callbacks to allow
564* Select to operate correctly.
565*
566* This will occur _after_ the initial DataTables initialisation, although
567* before Ajax data is rendered, if there is ajax data
568*
569* @param {DataTable.settings} ctx Settings object to operate on
570* @private
571*/
572function init ( ctx ) {573var api = new DataTable.Api( ctx );574ctx._select_init = true;575
576// Row callback so that classes can be added to rows and cells if the item577// was selected before the element was created. This will happen with the578// `deferRender` option enabled.579//580// This method of attaching to `aoRowCreatedCallback` is a hack until581// DataTables has proper events for row manipulation If you are reviewing582// this code to create your own plug-ins, please do not do this!583ctx.aoRowCreatedCallback.push( {584fn: function ( row, data, index ) {585var i, ien;586var d = ctx.aoData[ index ];587
588// Row589if ( d._select_selected ) {590$( row ).addClass( ctx._select.className );591}592
593// Cells and columns - if separated out, we would need to do two594// loops, so it makes sense to combine them into a single one595for ( i=0, ien=ctx.aoColumns.length ; i<ien ; i++ ) {596if ( ctx.aoColumns[i]._select_selected || (d._selected_cells && d._selected_cells[i]) ) {597$(d.anCells[i]).addClass( ctx._select.className );598}599}600},601sName: 'select-deferRender'602} );603
604// On Ajax reload we want to reselect all rows which are currently selected,605// if there is an rowId (i.e. a unique value to identify each row with)606api.on( 'preXhr.dt.dtSelect', function (e, settings) {607if (settings !== api.settings()[0]) {608// Not triggered by our DataTable!609return;610}611
612// note that column selection doesn't need to be cached and then613// reselected, as they are already selected614var rows = api.rows( { selected: true } ).ids( true ).filter( function ( d ) {615return d !== undefined;616} );617
618var cells = api.cells( { selected: true } ).eq(0).map( function ( cellIdx ) {619var id = api.row( cellIdx.row ).id( true );620return id ?621{ row: id, column: cellIdx.column } :622undefined;623} ).filter( function ( d ) {624return d !== undefined;625} );626
627// On the next draw, reselect the currently selected items628api.one( 'draw.dt.dtSelect', function () {629api.rows( rows ).select();630
631// `cells` is not a cell index selector, so it needs a loop632if ( cells.any() ) {633cells.each( function ( id ) {634api.cells( id.row, id.column ).select();635} );636}637} );638} );639
640// Update the table information element with selected item summary641api.on( 'draw.dtSelect.dt select.dtSelect.dt deselect.dtSelect.dt info.dt', function () {642info( api );643api.state.save();644} );645
646// Clean up and release647api.on( 'destroy.dtSelect', function () {648api.rows({selected: true}).deselect();649
650disableMouseSelection( api );651api.off( '.dtSelect' );652$('body').off('.dtSelect' + _safeId(api.table().node()));653} );654}
655
656/**
657* Add one or more items (rows or columns) to the selection when shift clicking
658* in OS selection style
659*
660* @param {DataTable.Api} dt DataTable
661* @param {string} type Row or column range selector
662* @param {object} idx Item index to select to
663* @param {object} last Item index to select from
664* @private
665*/
666function rowColumnRange( dt, type, idx, last )667{
668// Add a range of rows from the last selected row to this one669var indexes = dt[type+'s']( { search: 'applied' } ).indexes();670var idx1 = $.inArray( last, indexes );671var idx2 = $.inArray( idx, indexes );672
673if ( ! dt[type+'s']( { selected: true } ).any() && idx1 === -1 ) {674// select from top to here - slightly odd, but both Windows and Mac OS675// do this676indexes.splice( $.inArray( idx, indexes )+1, indexes.length );677}678else {679// reverse so we can shift click 'up' as well as down680if ( idx1 > idx2 ) {681var tmp = idx2;682idx2 = idx1;683idx1 = tmp;684}685
686indexes.splice( idx2+1, indexes.length );687indexes.splice( 0, idx1 );688}689
690if ( ! dt[type]( idx, { selected: true } ).any() ) {691// Select range692dt[type+'s']( indexes ).select();693}694else {695// Deselect range - need to keep the clicked on row selected696indexes.splice( $.inArray( idx, indexes ), 1 );697dt[type+'s']( indexes ).deselect();698}699}
700
701/**
702* Clear all selected items
703*
704* @param {DataTable.settings} ctx Settings object of the host DataTable
705* @param {boolean} [force=false] Force the de-selection to happen, regardless
706* of selection style
707* @private
708*/
709function clear( ctx, force )710{
711if ( force || ctx._select.style === 'single' ) {712var api = new DataTable.Api( ctx );713
714api.rows( { selected: true } ).deselect();715api.columns( { selected: true } ).deselect();716api.cells( { selected: true } ).deselect();717}718}
719
720/**
721* Select items based on the current configuration for style and items.
722*
723* @param {object} e Mouse event object
724* @param {DataTables.Api} dt DataTable
725* @param {DataTable.settings} ctx Settings object of the host DataTable
726* @param {string} type Items to select
727* @param {int|object} idx Index of the item to select
728* @private
729*/
730function typeSelect ( e, dt, ctx, type, idx )731{
732var style = dt.select.style();733var toggleable = dt.select.toggleable();734var isSelected = dt[type]( idx, { selected: true } ).any();735
736if ( isSelected && ! toggleable ) {737return;738}739
740if ( style === 'os' ) {741if ( e.ctrlKey || e.metaKey ) {742// Add or remove from the selection743dt[type]( idx ).select( ! isSelected );744}745else if ( e.shiftKey ) {746if ( type === 'cell' ) {747cellRange( dt, idx, ctx._select_lastCell || null );748}749else {750rowColumnRange( dt, type, idx, ctx._select_lastCell ?751ctx._select_lastCell[type] :752null753);754}755}756else {757// No cmd or shift click - deselect if selected, or select758// this row only759var selected = dt[type+'s']( { selected: true } );760
761if ( isSelected && selected.flatten().length === 1 ) {762dt[type]( idx ).deselect();763}764else {765selected.deselect();766dt[type]( idx ).select();767}768}769} else if ( style == 'multi+shift' ) {770if ( e.shiftKey ) {771if ( type === 'cell' ) {772cellRange( dt, idx, ctx._select_lastCell || null );773}774else {775rowColumnRange( dt, type, idx, ctx._select_lastCell ?776ctx._select_lastCell[type] :777null778);779}780}781else {782dt[ type ]( idx ).select( ! isSelected );783}784}785else {786dt[ type ]( idx ).select( ! isSelected );787}788}
789
790function _safeId( node ) {791return node.id.replace(/[^a-zA-Z0-9\-\_]/g, '-');792}
793
794
795
796/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
797* DataTables selectors
798*/
799
800// row and column are basically identical just assigned to different properties
801// and checking a different array, so we can dynamically create the functions to
802// reduce the code size
803$.each( [804{ type: 'row', prop: 'aoData' },805{ type: 'column', prop: 'aoColumns' }806], function ( i, o ) {807DataTable.ext.selector[ o.type ].push( function ( settings, opts, indexes ) {808var selected = opts.selected;809var data;810var out = [];811
812if ( selected !== true && selected !== false ) {813return indexes;814}815
816for ( var i=0, ien=indexes.length ; i<ien ; i++ ) {817data = settings[ o.prop ][ indexes[i] ];818
819if ( (selected === true && data._select_selected === true) ||820(selected === false && ! data._select_selected )821) {822out.push( indexes[i] );823}824}825
826return out;827} );828} );829
830DataTable.ext.selector.cell.push( function ( settings, opts, cells ) {831var selected = opts.selected;832var rowData;833var out = [];834
835if ( selected === undefined ) {836return cells;837}838
839for ( var i=0, ien=cells.length ; i<ien ; i++ ) {840rowData = settings.aoData[ cells[i].row ];841
842if ( (selected === true && rowData._selected_cells && rowData._selected_cells[ cells[i].column ] === true) ||843(selected === false && ( ! rowData._selected_cells || ! rowData._selected_cells[ cells[i].column ] ) )844) {845out.push( cells[i] );846}847}848
849return out;850} );851
852
853
854/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
855* DataTables API
856*
857* For complete documentation, please refer to the docs/api directory or the
858* DataTables site
859*/
860
861// Local variables to improve compression
862var apiRegister = DataTable.Api.register;863var apiRegisterPlural = DataTable.Api.registerPlural;864
865apiRegister( 'select()', function () {866return this.iterator( 'table', function ( ctx ) {867DataTable.select.init( new DataTable.Api( ctx ) );868} );869} );870
871apiRegister( 'select.blurable()', function ( flag ) {872if ( flag === undefined ) {873return this.context[0]._select.blurable;874}875
876return this.iterator( 'table', function ( ctx ) {877ctx._select.blurable = flag;878} );879} );880
881apiRegister( 'select.toggleable()', function ( flag ) {882if ( flag === undefined ) {883return this.context[0]._select.toggleable;884}885
886return this.iterator( 'table', function ( ctx ) {887ctx._select.toggleable = flag;888} );889} );890
891apiRegister( 'select.info()', function ( flag ) {892if ( flag === undefined ) {893return this.context[0]._select.info;894}895
896return this.iterator( 'table', function ( ctx ) {897ctx._select.info = flag;898} );899} );900
901apiRegister( 'select.items()', function ( items ) {902if ( items === undefined ) {903return this.context[0]._select.items;904}905
906return this.iterator( 'table', function ( ctx ) {907ctx._select.items = items;908
909eventTrigger( new DataTable.Api( ctx ), 'selectItems', [ items ] );910} );911} );912
913// Takes effect from the _next_ selection. None disables future selection, but
914// does not clear the current selection. Use the `deselect` methods for that
915apiRegister( 'select.style()', function ( style ) {916if ( style === undefined ) {917return this.context[0]._select.style;918}919
920return this.iterator( 'table', function ( ctx ) {921if ( ! ctx._select ) {922DataTable.select.init( new DataTable.Api(ctx) );923}924
925if ( ! ctx._select_init ) {926init(ctx);927}928
929ctx._select.style = style;930
931// Add / remove mouse event handlers. They aren't required when only932// API selection is available933var dt = new DataTable.Api( ctx );934disableMouseSelection( dt );935
936if ( style !== 'api' ) {937enableMouseSelection( dt );938}939
940eventTrigger( new DataTable.Api( ctx ), 'selectStyle', [ style ] );941} );942} );943
944apiRegister( 'select.selector()', function ( selector ) {945if ( selector === undefined ) {946return this.context[0]._select.selector;947}948
949return this.iterator( 'table', function ( ctx ) {950disableMouseSelection( new DataTable.Api( ctx ) );951
952ctx._select.selector = selector;953
954if ( ctx._select.style !== 'api' ) {955enableMouseSelection( new DataTable.Api( ctx ) );956}957} );958} );959
960
961
962apiRegisterPlural( 'rows().select()', 'row().select()', function ( select ) {963var api = this;964
965if ( select === false ) {966return this.deselect();967}968
969this.iterator( 'row', function ( ctx, idx ) {970clear( ctx );971
972ctx.aoData[ idx ]._select_selected = true;973$( ctx.aoData[ idx ].nTr ).addClass( ctx._select.className );974} );975
976this.iterator( 'table', function ( ctx, i ) {977eventTrigger( api, 'select', [ 'row', api[i] ], true );978} );979
980return this;981} );982
983apiRegisterPlural( 'columns().select()', 'column().select()', function ( select ) {984var api = this;985
986if ( select === false ) {987return this.deselect();988}989
990this.iterator( 'column', function ( ctx, idx ) {991clear( ctx );992
993ctx.aoColumns[ idx ]._select_selected = true;994
995var column = new DataTable.Api( ctx ).column( idx );996
997$( column.header() ).addClass( ctx._select.className );998$( column.footer() ).addClass( ctx._select.className );999
1000column.nodes().to$().addClass( ctx._select.className );1001} );1002
1003this.iterator( 'table', function ( ctx, i ) {1004eventTrigger( api, 'select', [ 'column', api[i] ], true );1005} );1006
1007return this;1008} );1009
1010apiRegisterPlural( 'cells().select()', 'cell().select()', function ( select ) {1011var api = this;1012
1013if ( select === false ) {1014return this.deselect();1015}1016
1017this.iterator( 'cell', function ( ctx, rowIdx, colIdx ) {1018clear( ctx );1019
1020var data = ctx.aoData[ rowIdx ];1021
1022if ( data._selected_cells === undefined ) {1023data._selected_cells = [];1024}1025
1026data._selected_cells[ colIdx ] = true;1027
1028if ( data.anCells ) {1029$( data.anCells[ colIdx ] ).addClass( ctx._select.className );1030}1031} );1032
1033this.iterator( 'table', function ( ctx, i ) {1034eventTrigger( api, 'select', [ 'cell', api.cells(api[i]).indexes().toArray() ], true );1035} );1036
1037return this;1038} );1039
1040
1041apiRegisterPlural( 'rows().deselect()', 'row().deselect()', function () {1042var api = this;1043
1044this.iterator( 'row', function ( ctx, idx ) {1045ctx.aoData[ idx ]._select_selected = false;1046ctx._select_lastCell = null;1047$( ctx.aoData[ idx ].nTr ).removeClass( ctx._select.className );1048} );1049
1050this.iterator( 'table', function ( ctx, i ) {1051eventTrigger( api, 'deselect', [ 'row', api[i] ], true );1052} );1053
1054return this;1055} );1056
1057apiRegisterPlural( 'columns().deselect()', 'column().deselect()', function () {1058var api = this;1059
1060this.iterator( 'column', function ( ctx, idx ) {1061ctx.aoColumns[ idx ]._select_selected = false;1062
1063var api = new DataTable.Api( ctx );1064var column = api.column( idx );1065
1066$( column.header() ).removeClass( ctx._select.className );1067$( column.footer() ).removeClass( ctx._select.className );1068
1069// Need to loop over each cell, rather than just using1070// `column().nodes()` as cells which are individually selected should1071// not have the `selected` class removed from them1072api.cells( null, idx ).indexes().each( function (cellIdx) {1073var data = ctx.aoData[ cellIdx.row ];1074var cellSelected = data._selected_cells;1075
1076if ( data.anCells && (! cellSelected || ! cellSelected[ cellIdx.column ]) ) {1077$( data.anCells[ cellIdx.column ] ).removeClass( ctx._select.className );1078}1079} );1080} );1081
1082this.iterator( 'table', function ( ctx, i ) {1083eventTrigger( api, 'deselect', [ 'column', api[i] ], true );1084} );1085
1086return this;1087} );1088
1089apiRegisterPlural( 'cells().deselect()', 'cell().deselect()', function () {1090var api = this;1091
1092this.iterator( 'cell', function ( ctx, rowIdx, colIdx ) {1093var data = ctx.aoData[ rowIdx ];1094
1095if(data._selected_cells !== undefined) {1096data._selected_cells[ colIdx ] = false;1097}1098
1099// Remove class only if the cells exist, and the cell is not column1100// selected, in which case the class should remain (since it is selected1101// in the column)1102if ( data.anCells && ! ctx.aoColumns[ colIdx ]._select_selected ) {1103$( data.anCells[ colIdx ] ).removeClass( ctx._select.className );1104}1105} );1106
1107this.iterator( 'table', function ( ctx, i ) {1108eventTrigger( api, 'deselect', [ 'cell', api[i] ], true );1109} );1110
1111return this;1112} );1113
1114
1115
1116/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1117* Buttons
1118*/
1119function i18n( label, def ) {1120return function (dt) {1121return dt.i18n( 'buttons.'+label, def );1122};1123}
1124
1125// Common events with suitable namespaces
1126function namespacedEvents ( config ) {1127var unique = config._eventNamespace;1128
1129return 'draw.dt.DT'+unique+' select.dt.DT'+unique+' deselect.dt.DT'+unique;1130}
1131
1132function enabled ( dt, config ) {1133if ( $.inArray( 'rows', config.limitTo ) !== -1 && dt.rows( { selected: true } ).any() ) {1134return true;1135}1136
1137if ( $.inArray( 'columns', config.limitTo ) !== -1 && dt.columns( { selected: true } ).any() ) {1138return true;1139}1140
1141if ( $.inArray( 'cells', config.limitTo ) !== -1 && dt.cells( { selected: true } ).any() ) {1142return true;1143}1144
1145return false;1146}
1147
1148var _buttonNamespace = 0;1149
1150$.extend( DataTable.ext.buttons, {1151selected: {1152text: i18n( 'selected', 'Selected' ),1153className: 'buttons-selected',1154limitTo: [ 'rows', 'columns', 'cells' ],1155init: function ( dt, node, config ) {1156var that = this;1157config._eventNamespace = '.select'+(_buttonNamespace++);1158
1159// .DT namespace listeners are removed by DataTables automatically1160// on table destroy1161dt.on( namespacedEvents(config), function () {1162that.enable( enabled(dt, config) );1163} );1164
1165this.disable();1166},1167destroy: function ( dt, node, config ) {1168dt.off( config._eventNamespace );1169}1170},1171selectedSingle: {1172text: i18n( 'selectedSingle', 'Selected single' ),1173className: 'buttons-selected-single',1174init: function ( dt, node, config ) {1175var that = this;1176config._eventNamespace = '.select'+(_buttonNamespace++);1177
1178dt.on( namespacedEvents(config), function () {1179var count = dt.rows( { selected: true } ).flatten().length +1180dt.columns( { selected: true } ).flatten().length +1181dt.cells( { selected: true } ).flatten().length;1182
1183that.enable( count === 1 );1184} );1185
1186this.disable();1187},1188destroy: function ( dt, node, config ) {1189dt.off( config._eventNamespace );1190}1191},1192selectAll: {1193text: i18n( 'selectAll', 'Select all' ),1194className: 'buttons-select-all',1195action: function () {1196var items = this.select.items();1197this[ items+'s' ]().select();1198}1199},1200selectNone: {1201text: i18n( 'selectNone', 'Deselect all' ),1202className: 'buttons-select-none',1203action: function () {1204clear( this.settings()[0], true );1205},1206init: function ( dt, node, config ) {1207var that = this;1208config._eventNamespace = '.select'+(_buttonNamespace++);1209
1210dt.on( namespacedEvents(config), function () {1211var count = dt.rows( { selected: true } ).flatten().length +1212dt.columns( { selected: true } ).flatten().length +1213dt.cells( { selected: true } ).flatten().length;1214
1215that.enable( count > 0 );1216} );1217
1218this.disable();1219},1220destroy: function ( dt, node, config ) {1221dt.off( config._eventNamespace );1222}1223}1224} );1225
1226$.each( [ 'Row', 'Column', 'Cell' ], function ( i, item ) {1227var lc = item.toLowerCase();1228
1229DataTable.ext.buttons[ 'select'+item+'s' ] = {1230text: i18n( 'select'+item+'s', 'Select '+lc+'s' ),1231className: 'buttons-select-'+lc+'s',1232action: function () {1233this.select.items( lc );1234},1235init: function ( dt ) {1236var that = this;1237
1238dt.on( 'selectItems.dt.DT', function ( e, ctx, items ) {1239that.active( items === lc );1240} );1241}1242};1243} );1244
1245
1246
1247/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1248* Initialisation
1249*/
1250
1251// DataTables creation - check if select has been defined in the options. Note
1252// this required that the table be in the document! If it isn't then something
1253// needs to trigger this method unfortunately. The next major release of
1254// DataTables will rework the events and address this.
1255$(document).on( 'preInit.dt.dtSelect', function (e, ctx) {1256if ( e.namespace !== 'dt' ) {1257return;1258}1259
1260DataTable.select.init( new DataTable.Api( ctx ) );1261} );1262
1263
1264return DataTable.select;1265}));1266