GPQAPP

Форк
0
/
dataTables.select.js 
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 ){
25
	if ( typeof define === 'function' && define.amd ) {
26
		// AMD
27
		define( ['jquery', 'datatables.net'], function ( $ ) {
28
			return factory( $, window, document );
29
		} );
30
	}
31
	else if ( typeof exports === 'object' ) {
32
		// CommonJS
33
		module.exports = function (root, $) {
34
			if ( ! root ) {
35
				root = window;
36
			}
37

38
			if ( ! $ || ! $.fn.dataTable ) {
39
				$ = require('datatables.net')(root, $).$;
40
			}
41

42
			return factory( $, root, root.document );
43
		};
44
	}
45
	else {
46
		// Browser
47
		factory( jQuery, window, document );
48
	}
49
}(function( $, window, document, undefined ) {
50
'use strict';
51
var DataTable = $.fn.dataTable;
52

53

54
// Version information for debugger
55
DataTable.select = {};
56

57
DataTable.select.version = '1.3.4-dev';
58

59
DataTable.select.init = function ( dt ) {
60
	var ctx = dt.settings()[0];
61

62
	if (ctx._select) {
63
		return;
64
	}
65

66
	var savedSelected = dt.state.loaded();
67

68
	var selectAndSave = function(e, settings, data) {
69
		if(data === null || data.select === undefined) {
70
			return;
71
		}
72
		dt.rows().deselect();
73
		dt.columns().deselect();
74
		dt.cells().deselect();
75
		if (data.select.rows !== undefined) {
76
			dt.rows(data.select.rows).select();
77
		}
78
		if (data.select.columns !== undefined) {
79
			dt.columns(data.select.columns).select();
80
		}
81
		if (data.select.cells !== undefined) {
82
			for(var i = 0; i < data.select.cells.length; i++) {
83
				dt.cell(data.select.cells[i].row, data.select.cells[i].column).select();
84
			}
85
		}
86
		dt.state.save();
87
	}
88
	
89
	dt.one('init', function() {
90
		dt.on('stateSaveParams', function(e, settings, data) {
91
			data.select = {};
92
			data.select.rows = dt.rows({selected:true}).ids(true).toArray();
93
			data.select.columns = dt.columns({selected:true})[0];
94
			data.select.cells = dt.cells({selected:true})[0].map(function(coords) {
95
				return {row: dt.row(coords.row).id(true), column: coords.column}
96
			});
97
		})
98
		
99
		selectAndSave(undefined, undefined, savedSelected)
100
		dt.on('stateLoaded stateLoadParams', selectAndSave)
101
	})
102

103
	var init = ctx.oInit.select;
104
	var defaults = DataTable.defaults.select;
105
	var opts = init === undefined ?
106
		defaults :
107
		init;
108

109
	// Set defaults
110
	var items = 'row';
111
	var style = 'api';
112
	var blurable = false;
113
	var toggleable = true;
114
	var info = true;
115
	var selector = 'td, th';
116
	var className = 'selected';
117
	var setStyle = false;
118

119
	ctx._select = {};
120

121
	// Initialisation customisations
122
	if ( opts === true ) {
123
		style = 'os';
124
		setStyle = true;
125
	}
126
	else if ( typeof opts === 'string' ) {
127
		style = opts;
128
		setStyle = true;
129
	}
130
	else if ( $.isPlainObject( opts ) ) {
131
		if ( opts.blurable !== undefined ) {
132
			blurable = opts.blurable;
133
		}
134
		
135
		if ( opts.toggleable !== undefined ) {
136
			toggleable = opts.toggleable;
137
		}
138

139
		if ( opts.info !== undefined ) {
140
			info = opts.info;
141
		}
142

143
		if ( opts.items !== undefined ) {
144
			items = opts.items;
145
		}
146

147
		if ( opts.style !== undefined ) {
148
			style = opts.style;
149
			setStyle = true;
150
		}
151
		else {
152
			style = 'os';
153
			setStyle = true;
154
		}
155

156
		if ( opts.selector !== undefined ) {
157
			selector = opts.selector;
158
		}
159

160
		if ( opts.className !== undefined ) {
161
			className = opts.className;
162
		}
163
	}
164

165
	dt.select.selector( selector );
166
	dt.select.items( items );
167
	dt.select.style( style );
168
	dt.select.blurable( blurable );
169
	dt.select.toggleable( toggleable );
170
	dt.select.info( info );
171
	ctx._select.className = className;
172

173

174
	// Sort table based on selected rows. Requires Select Datatables extension
175
	$.fn.dataTable.ext.order['select-checkbox'] = function ( settings, col ) {
176
		return this.api().column( col, {order: 'index'} ).nodes().map( function ( td ) {
177
			if ( settings._select.items === 'row' ) {
178
				return $( td ).parent().hasClass( settings._select.className );
179
			} else if ( settings._select.items === 'cell' ) {
180
				return $( td ).hasClass( settings._select.className );
181
			}
182
			return false;
183
		});
184
	};
185

186
	// If the init options haven't enabled select, but there is a selectable
187
	// class name, then enable
188
	if ( ! setStyle && $( dt.table().node() ).hasClass( 'selectable' ) ) {
189
		dt.select.style( 'os' );
190
	}
191
};
192

193
/*
194

195
Select is a collection of API methods, event handlers, event emitters and
196
buttons (for the `Buttons` extension) for DataTables. It provides the following
197
features, with an overview of how they are implemented:
198

199
## Selection of rows, columns and cells. Whether an item is selected or not is
200
   stored in:
201

202
* rows: a `_select_selected` property which contains a boolean value of the
203
  DataTables' `aoData` object for each row
204
* columns: a `_select_selected` property which contains a boolean value of the
205
  DataTables' `aoColumns` object for each column
206
* cells: a `_selected_cells` property which contains an array of boolean values
207
  of the `aoData` object for each row. The array is the same length as the
208
  columns array, with each element of it representing a cell.
209

210
This method of using boolean flags allows Select to operate when nodes have not
211
been created for rows / cells (DataTables' defer rendering feature).
212

213
## API methods
214

215
A range of API methods are available for triggering selection and de-selection
216
of rows. Methods are also available to configure the selection events that can
217
be triggered by an end user (such as which items are to be selected). To a large
218
extent, these of API methods *is* Select. It is basically a collection of helper
219
functions that can be used to select items in a DataTable.
220

221
Configuration of select is held in the object `_select` which is attached to the
222
DataTables settings object on initialisation. Select being available on a table
223
is not optional when Select is loaded, but its default is for selection only to
224
be available via the API - so the end user wouldn't be able to select rows
225
without additional configuration.
226

227
The `_select` object contains the following properties:
228

229
```
230
{
231
	items:string       - Can be `rows`, `columns` or `cells`. Defines what item 
232
	                     will be selected if the user is allowed to activate row
233
	                     selection using the mouse.
234
	style:string       - Can be `none`, `single`, `multi` or `os`. Defines the
235
	                     interaction style when selecting items
236
	blurable:boolean   - If row selection can be cleared by clicking outside of
237
	                     the table
238
	toggleable:boolean - If row selection can be cancelled by repeated clicking
239
	                     on the row
240
	info:boolean       - If the selection summary should be shown in the table
241
	                     information elements
242
}
243
```
244

245
In addition to the API methods, Select also extends the DataTables selector
246
options for rows, columns and cells adding a `selected` option to the selector
247
options object, allowing the developer to select only selected items or
248
unselected items.
249

250
## Mouse selection of items
251

252
Clicking on items can be used to select items. This is done by a simple event
253
handler 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
 */
276
function cellRange( dt, idx, last )
277
{
278
	var indexes;
279
	var columnIndexes;
280
	var rowIndexes;
281
	var selectColumns = function ( start, end ) {
282
		if ( start > end ) {
283
			var tmp = end;
284
			end = start;
285
			start = tmp;
286
		}
287
		
288
		var record = false;
289
		return dt.columns( ':visible' ).indexes().filter( function (i) {
290
			if ( i === start ) {
291
				record = true;
292
			}
293
			
294
			if ( i === end ) { // not else if, as start might === end
295
				record = false;
296
				return true;
297
			}
298

299
			return record;
300
		} );
301
	};
302

303
	var selectRows = function ( start, end ) {
304
		var indexes = dt.rows( { search: 'applied' } ).indexes();
305

306
		// Which comes first - might need to swap
307
		if ( indexes.indexOf( start ) > indexes.indexOf( end ) ) {
308
			var tmp = end;
309
			end = start;
310
			start = tmp;
311
		}
312

313
		var record = false;
314
		return indexes.filter( function (i) {
315
			if ( i === start ) {
316
				record = true;
317
			}
318
			
319
			if ( i === end ) {
320
				record = false;
321
				return true;
322
			}
323

324
			return record;
325
		} );
326
	};
327

328
	if ( ! dt.cells( { selected: true } ).any() && ! last ) {
329
		// select from the top left cell to this one
330
		columnIndexes = selectColumns( 0, idx.column );
331
		rowIndexes = selectRows( 0 , idx.row );
332
	}
333
	else {
334
		// Get column indexes between old and new
335
		columnIndexes = selectColumns( last.column, idx.column );
336
		rowIndexes = selectRows( last.row , idx.row );
337
	}
338

339
	indexes = dt.cells( rowIndexes, columnIndexes ).flatten();
340

341
	if ( ! dt.cells( idx, { selected: true } ).any() ) {
342
		// Select range
343
		dt.cells( indexes ).select();
344
	}
345
	else {
346
		// Deselect range
347
		dt.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
 */
357
function disableMouseSelection( dt )
358
{
359
	var ctx = dt.settings()[0];
360
	var 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
 */
376
function enableMouseSelection ( dt )
377
{
378
	var container = $( dt.table().container() );
379
	var ctx = dt.settings()[0];
380
	var selector = ctx._select.selector;
381
	var matchSelection;
382

383
	container
384
		.on( 'mousedown.dtSelect', selector, function(e) {
385
			// Disallow text selection for shift clicking on the table so multi
386
			// element selection doesn't look terrible!
387
			if ( e.shiftKey || e.metaKey || e.ctrlKey ) {
388
				container
389
					.css( '-moz-user-select', 'none' )
390
					.one('selectstart.dtSelect', selector, function () {
391
						return false;
392
					} );
393
			}
394

395
			if ( window.getSelection ) {
396
				matchSelection = window.getSelection();
397
			}
398
		} )
399
		.on( 'mouseup.dtSelect', selector, function() {
400
			// Allow text selection to occur again, Mozilla style (tested in FF
401
			// 35.0.1 - still required)
402
			container.css( '-moz-user-select', '' );
403
		} )
404
		.on( 'click.dtSelect', selector, function ( e ) {
405
			var items = dt.select.items();
406
			var idx;
407

408
			// If text was selected (click and drag), then we shouldn't change
409
			// the row's selected state
410
			if ( matchSelection ) {
411
				var selection = window.getSelection();
412

413
				// If the element that contains the selection is not in the table, we can ignore it
414
				// This can happen if the developer selects text from the click event
415
				if ( ! selection.anchorNode || $(selection.anchorNode).closest('table')[0] === dt.table().node() ) {
416
					if ( selection !== matchSelection ) {
417
						return;
418
					}
419
				}
420
			}
421

422
			var ctx = dt.settings()[0];
423
			var wrapperClass = dt.settings()[0].oClasses.sWrapper.trim().replace(/ +/g, '.');
424

425
			// Ignore clicks inside a sub-table
426
			if ( $(e.target).closest('div.'+wrapperClass)[0] != dt.table().container() ) {
427
				return;
428
			}
429

430
			var cell = dt.cell( $(e.target).closest('td, th') );
431

432
			// Check the cell actually belongs to the host DataTable (so child
433
			// rows, etc, are ignored)
434
			if ( ! cell.any() ) {
435
				return;
436
			}
437

438
			var event = $.Event('user-select.dt');
439
			eventTrigger( dt, event, [ items, cell, e ] );
440

441
			if ( event.isDefaultPrevented() ) {
442
				return;
443
			}
444

445
			var cellIndex = cell.index();
446
			if ( items === 'row' ) {
447
				idx = cellIndex.row;
448
				typeSelect( e, dt, ctx, 'row', idx );
449
			}
450
			else if ( items === 'column' ) {
451
				idx = cell.index().column;
452
				typeSelect( e, dt, ctx, 'column', idx );
453
			}
454
			else if ( items === 'cell' ) {
455
				idx = cell.index();
456
				typeSelect( e, dt, ctx, 'cell', idx );
457
			}
458

459
			ctx._select_lastCell = cellIndex;
460
		} );
461

462
	// Blurable
463
	$('body').on( 'click.dtSelect' + _safeId(dt.table().node()), function ( e ) {
464
		if ( ctx._select.blurable ) {
465
			// If the click was inside the DataTables container, don't blur
466
			if ( $(e.target).parents().filter( dt.table().container() ).length ) {
467
				return;
468
			}
469

470
			// Ignore elements which have been removed from the DOM (i.e. paging
471
			// buttons)
472
			if ( $(e.target).parents('html').length === 0 ) {
473
			 	return;
474
			}
475

476
			// Don't blur in Editor form
477
			if ( $(e.target).parents('div.DTE').length ) {
478
				return;
479
			}
480

481
			clear( 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
 */
496
function eventTrigger ( api, type, args, any )
497
{
498
	if ( any && ! api.flatten().length ) {
499
		return;
500
	}
501

502
	if ( typeof type === 'string' ) {
503
		type = type +'.dt';
504
	}
505

506
	args.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
 */
518
function info ( api )
519
{
520
	var ctx = api.settings()[0];
521

522
	if ( ! ctx._select.info || ! ctx.aanFeatures.i ) {
523
		return;
524
	}
525

526
	if ( api.select.style() === 'api' ) {
527
		return;
528
	}
529

530
	var rows    = api.rows( { selected: true } ).flatten().length;
531
	var columns = api.columns( { selected: true } ).flatten().length;
532
	var cells   = api.cells( { selected: true } ).flatten().length;
533

534
	var add = function ( el, name, num ) {
535
		el.append( $('<span class="select-item"/>').append( api.i18n(
536
			'select.'+name+'s',
537
			{ _: '%d '+name+'s selected', 0: '', 1: '1 '+name+' selected' },
538
			num
539
		) ) );
540
	};
541

542
	// Internal knowledge of DataTables to loop over all information elements
543
	$.each( ctx.aanFeatures.i, function ( i, el ) {
544
		el = $(el);
545

546
		var output  = $('<span class="select-info"/>');
547
		add( output, 'row', rows );
548
		add( output, 'column', columns );
549
		add( output, 'cell', cells  );
550

551
		var exisiting = el.children('span.select-info');
552
		if ( exisiting.length ) {
553
			exisiting.remove();
554
		}
555

556
		if ( output.text() !== '' ) {
557
			el.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
 */
572
function init ( ctx ) {
573
	var api = new DataTable.Api( ctx );
574
	ctx._select_init = true;
575

576
	// Row callback so that classes can be added to rows and cells if the item
577
	// was selected before the element was created. This will happen with the
578
	// `deferRender` option enabled.
579
	// 
580
	// This method of attaching to `aoRowCreatedCallback` is a hack until
581
	// DataTables has proper events for row manipulation If you are reviewing
582
	// this code to create your own plug-ins, please do not do this!
583
	ctx.aoRowCreatedCallback.push( {
584
		fn: function ( row, data, index ) {
585
			var i, ien;
586
			var d = ctx.aoData[ index ];
587

588
			// Row
589
			if ( d._select_selected ) {
590
				$( row ).addClass( ctx._select.className );
591
			}
592

593
			// Cells and columns - if separated out, we would need to do two
594
			// loops, so it makes sense to combine them into a single one
595
			for ( i=0, ien=ctx.aoColumns.length ; i<ien ; i++ ) {
596
				if ( ctx.aoColumns[i]._select_selected || (d._selected_cells && d._selected_cells[i]) ) {
597
					$(d.anCells[i]).addClass( ctx._select.className );
598
				}
599
			}
600
		},
601
		sName: '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)
606
	api.on( 'preXhr.dt.dtSelect', function (e, settings) {
607
		if (settings !== api.settings()[0]) {
608
			// Not triggered by our DataTable!
609
			return;
610
		}
611

612
		// note that column selection doesn't need to be cached and then
613
		// reselected, as they are already selected
614
		var rows = api.rows( { selected: true } ).ids( true ).filter( function ( d ) {
615
			return d !== undefined;
616
		} );
617

618
		var cells = api.cells( { selected: true } ).eq(0).map( function ( cellIdx ) {
619
			var id = api.row( cellIdx.row ).id( true );
620
			return id ?
621
				{ row: id, column: cellIdx.column } :
622
				undefined;
623
		} ).filter( function ( d ) {
624
			return d !== undefined;
625
		} );
626

627
		// On the next draw, reselect the currently selected items
628
		api.one( 'draw.dt.dtSelect', function () {
629
			api.rows( rows ).select();
630

631
			// `cells` is not a cell index selector, so it needs a loop
632
			if ( cells.any() ) {
633
				cells.each( function ( id ) {
634
					api.cells( id.row, id.column ).select();
635
				} );
636
			}
637
		} );
638
	} );
639

640
	// Update the table information element with selected item summary
641
	api.on( 'draw.dtSelect.dt select.dtSelect.dt deselect.dtSelect.dt info.dt', function () {
642
		info( api );
643
		api.state.save();
644
	} );
645

646
	// Clean up and release
647
	api.on( 'destroy.dtSelect', function () {
648
		api.rows({selected: true}).deselect();
649

650
		disableMouseSelection( api );
651
		api.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
 */
666
function rowColumnRange( dt, type, idx, last )
667
{
668
	// Add a range of rows from the last selected row to this one
669
	var indexes = dt[type+'s']( { search: 'applied' } ).indexes();
670
	var idx1 = $.inArray( last, indexes );
671
	var idx2 = $.inArray( idx, indexes );
672

673
	if ( ! dt[type+'s']( { selected: true } ).any() && idx1 === -1 ) {
674
		// select from top to here - slightly odd, but both Windows and Mac OS
675
		// do this
676
		indexes.splice( $.inArray( idx, indexes )+1, indexes.length );
677
	}
678
	else {
679
		// reverse so we can shift click 'up' as well as down
680
		if ( idx1 > idx2 ) {
681
			var tmp = idx2;
682
			idx2 = idx1;
683
			idx1 = tmp;
684
		}
685

686
		indexes.splice( idx2+1, indexes.length );
687
		indexes.splice( 0, idx1 );
688
	}
689

690
	if ( ! dt[type]( idx, { selected: true } ).any() ) {
691
		// Select range
692
		dt[type+'s']( indexes ).select();
693
	}
694
	else {
695
		// Deselect range - need to keep the clicked on row selected
696
		indexes.splice( $.inArray( idx, indexes ), 1 );
697
		dt[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
 */
709
function clear( ctx, force )
710
{
711
	if ( force || ctx._select.style === 'single' ) {
712
		var api = new DataTable.Api( ctx );
713
		
714
		api.rows( { selected: true } ).deselect();
715
		api.columns( { selected: true } ).deselect();
716
		api.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
 */
730
function typeSelect ( e, dt, ctx, type, idx )
731
{
732
	var style = dt.select.style();
733
	var toggleable = dt.select.toggleable();
734
	var isSelected = dt[type]( idx, { selected: true } ).any();
735
	
736
	if ( isSelected && ! toggleable ) {
737
		return;
738
	}
739

740
	if ( style === 'os' ) {
741
		if ( e.ctrlKey || e.metaKey ) {
742
			// Add or remove from the selection
743
			dt[type]( idx ).select( ! isSelected );
744
		}
745
		else if ( e.shiftKey ) {
746
			if ( type === 'cell' ) {
747
				cellRange( dt, idx, ctx._select_lastCell || null );
748
			}
749
			else {
750
				rowColumnRange( dt, type, idx, ctx._select_lastCell ?
751
					ctx._select_lastCell[type] :
752
					null
753
				);
754
			}
755
		}
756
		else {
757
			// No cmd or shift click - deselect if selected, or select
758
			// this row only
759
			var selected = dt[type+'s']( { selected: true } );
760

761
			if ( isSelected && selected.flatten().length === 1 ) {
762
				dt[type]( idx ).deselect();
763
			}
764
			else {
765
				selected.deselect();
766
				dt[type]( idx ).select();
767
			}
768
		}
769
	} else if ( style == 'multi+shift' ) {
770
		if ( e.shiftKey ) {
771
			if ( type === 'cell' ) {
772
				cellRange( dt, idx, ctx._select_lastCell || null );
773
			}
774
			else {
775
				rowColumnRange( dt, type, idx, ctx._select_lastCell ?
776
					ctx._select_lastCell[type] :
777
					null
778
				);
779
			}
780
		}
781
		else {
782
			dt[ type ]( idx ).select( ! isSelected );
783
		}
784
	}
785
	else {
786
		dt[ type ]( idx ).select( ! isSelected );
787
	}
788
}
789

790
function _safeId( node ) {
791
	return 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 ) {
807
	DataTable.ext.selector[ o.type ].push( function ( settings, opts, indexes ) {
808
		var selected = opts.selected;
809
		var data;
810
		var out = [];
811

812
		if ( selected !== true && selected !== false ) {
813
			return indexes;
814
		}
815

816
		for ( var i=0, ien=indexes.length ; i<ien ; i++ ) {
817
			data = settings[ o.prop ][ indexes[i] ];
818

819
			if ( (selected === true && data._select_selected === true) ||
820
			     (selected === false && ! data._select_selected )
821
			) {
822
				out.push( indexes[i] );
823
			}
824
		}
825

826
		return out;
827
	} );
828
} );
829

830
DataTable.ext.selector.cell.push( function ( settings, opts, cells ) {
831
	var selected = opts.selected;
832
	var rowData;
833
	var out = [];
834

835
	if ( selected === undefined ) {
836
		return cells;
837
	}
838

839
	for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
840
		rowData = settings.aoData[ cells[i].row ];
841

842
		if ( (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
		) {
845
			out.push( cells[i] );
846
		}
847
	}
848

849
	return 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
862
var apiRegister = DataTable.Api.register;
863
var apiRegisterPlural = DataTable.Api.registerPlural;
864

865
apiRegister( 'select()', function () {
866
	return this.iterator( 'table', function ( ctx ) {
867
		DataTable.select.init( new DataTable.Api( ctx ) );
868
	} );
869
} );
870

871
apiRegister( 'select.blurable()', function ( flag ) {
872
	if ( flag === undefined ) {
873
		return this.context[0]._select.blurable;
874
	}
875

876
	return this.iterator( 'table', function ( ctx ) {
877
		ctx._select.blurable = flag;
878
	} );
879
} );
880

881
apiRegister( 'select.toggleable()', function ( flag ) {
882
	if ( flag === undefined ) {
883
		return this.context[0]._select.toggleable;
884
	}
885

886
	return this.iterator( 'table', function ( ctx ) {
887
		ctx._select.toggleable = flag;
888
	} );
889
} );
890

891
apiRegister( 'select.info()', function ( flag ) {
892
	if ( flag === undefined ) {
893
		return this.context[0]._select.info;
894
	}
895

896
	return this.iterator( 'table', function ( ctx ) {
897
		ctx._select.info = flag;
898
	} );
899
} );
900

901
apiRegister( 'select.items()', function ( items ) {
902
	if ( items === undefined ) {
903
		return this.context[0]._select.items;
904
	}
905

906
	return this.iterator( 'table', function ( ctx ) {
907
		ctx._select.items = items;
908

909
		eventTrigger( 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
915
apiRegister( 'select.style()', function ( style ) {
916
	if ( style === undefined ) {
917
		return this.context[0]._select.style;
918
	}
919

920
	return this.iterator( 'table', function ( ctx ) {
921
		if ( ! ctx._select ) {
922
			DataTable.select.init( new DataTable.Api(ctx) );
923
		}
924

925
		if ( ! ctx._select_init ) {
926
			init(ctx);
927
		}
928

929
		ctx._select.style = style;
930

931
		// Add / remove mouse event handlers. They aren't required when only
932
		// API selection is available
933
		var dt = new DataTable.Api( ctx );
934
		disableMouseSelection( dt );
935
		
936
		if ( style !== 'api' ) {
937
			enableMouseSelection( dt );
938
		}
939

940
		eventTrigger( new DataTable.Api( ctx ), 'selectStyle', [ style ] );
941
	} );
942
} );
943

944
apiRegister( 'select.selector()', function ( selector ) {
945
	if ( selector === undefined ) {
946
		return this.context[0]._select.selector;
947
	}
948

949
	return this.iterator( 'table', function ( ctx ) {
950
		disableMouseSelection( new DataTable.Api( ctx ) );
951

952
		ctx._select.selector = selector;
953

954
		if ( ctx._select.style !== 'api' ) {
955
			enableMouseSelection( new DataTable.Api( ctx ) );
956
		}
957
	} );
958
} );
959

960

961

962
apiRegisterPlural( 'rows().select()', 'row().select()', function ( select ) {
963
	var api = this;
964

965
	if ( select === false ) {
966
		return this.deselect();
967
	}
968

969
	this.iterator( 'row', function ( ctx, idx ) {
970
		clear( ctx );
971

972
		ctx.aoData[ idx ]._select_selected = true;
973
		$( ctx.aoData[ idx ].nTr ).addClass( ctx._select.className );
974
	} );
975

976
	this.iterator( 'table', function ( ctx, i ) {
977
		eventTrigger( api, 'select', [ 'row', api[i] ], true );
978
	} );
979

980
	return this;
981
} );
982

983
apiRegisterPlural( 'columns().select()', 'column().select()', function ( select ) {
984
	var api = this;
985

986
	if ( select === false ) {
987
		return this.deselect();
988
	}
989

990
	this.iterator( 'column', function ( ctx, idx ) {
991
		clear( ctx );
992

993
		ctx.aoColumns[ idx ]._select_selected = true;
994

995
		var column = new DataTable.Api( ctx ).column( idx );
996

997
		$( column.header() ).addClass( ctx._select.className );
998
		$( column.footer() ).addClass( ctx._select.className );
999

1000
		column.nodes().to$().addClass( ctx._select.className );
1001
	} );
1002

1003
	this.iterator( 'table', function ( ctx, i ) {
1004
		eventTrigger( api, 'select', [ 'column', api[i] ], true );
1005
	} );
1006

1007
	return this;
1008
} );
1009

1010
apiRegisterPlural( 'cells().select()', 'cell().select()', function ( select ) {
1011
	var api = this;
1012

1013
	if ( select === false ) {
1014
		return this.deselect();
1015
	}
1016

1017
	this.iterator( 'cell', function ( ctx, rowIdx, colIdx ) {
1018
		clear( ctx );
1019

1020
		var data = ctx.aoData[ rowIdx ];
1021

1022
		if ( data._selected_cells === undefined ) {
1023
			data._selected_cells = [];
1024
		}
1025

1026
		data._selected_cells[ colIdx ] = true;
1027

1028
		if ( data.anCells ) {
1029
			$( data.anCells[ colIdx ] ).addClass( ctx._select.className );
1030
		}
1031
	} );
1032

1033
	this.iterator( 'table', function ( ctx, i ) {
1034
		eventTrigger( api, 'select', [ 'cell', api.cells(api[i]).indexes().toArray() ], true );
1035
	} );
1036

1037
	return this;
1038
} );
1039

1040

1041
apiRegisterPlural( 'rows().deselect()', 'row().deselect()', function () {
1042
	var api = this;
1043

1044
	this.iterator( 'row', function ( ctx, idx ) {
1045
		ctx.aoData[ idx ]._select_selected = false;
1046
		ctx._select_lastCell = null;
1047
		$( ctx.aoData[ idx ].nTr ).removeClass( ctx._select.className );
1048
	} );
1049

1050
	this.iterator( 'table', function ( ctx, i ) {
1051
		eventTrigger( api, 'deselect', [ 'row', api[i] ], true );
1052
	} );
1053

1054
	return this;
1055
} );
1056

1057
apiRegisterPlural( 'columns().deselect()', 'column().deselect()', function () {
1058
	var api = this;
1059

1060
	this.iterator( 'column', function ( ctx, idx ) {
1061
		ctx.aoColumns[ idx ]._select_selected = false;
1062

1063
		var api = new DataTable.Api( ctx );
1064
		var 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 using
1070
		// `column().nodes()` as cells which are individually selected should
1071
		// not have the `selected` class removed from them
1072
		api.cells( null, idx ).indexes().each( function (cellIdx) {
1073
			var data = ctx.aoData[ cellIdx.row ];
1074
			var cellSelected = data._selected_cells;
1075

1076
			if ( data.anCells && (! cellSelected || ! cellSelected[ cellIdx.column ]) ) {
1077
				$( data.anCells[ cellIdx.column  ] ).removeClass( ctx._select.className );
1078
			}
1079
		} );
1080
	} );
1081

1082
	this.iterator( 'table', function ( ctx, i ) {
1083
		eventTrigger( api, 'deselect', [ 'column', api[i] ], true );
1084
	} );
1085

1086
	return this;
1087
} );
1088

1089
apiRegisterPlural( 'cells().deselect()', 'cell().deselect()', function () {
1090
	var api = this;
1091

1092
	this.iterator( 'cell', function ( ctx, rowIdx, colIdx ) {
1093
		var data = ctx.aoData[ rowIdx ];
1094

1095
		if(data._selected_cells !== undefined) {
1096
			data._selected_cells[ colIdx ] = false;
1097
		}
1098

1099
		// Remove class only if the cells exist, and the cell is not column
1100
		// selected, in which case the class should remain (since it is selected
1101
		// in the column)
1102
		if ( data.anCells && ! ctx.aoColumns[ colIdx ]._select_selected ) {
1103
			$( data.anCells[ colIdx ] ).removeClass( ctx._select.className );
1104
		}
1105
	} );
1106

1107
	this.iterator( 'table', function ( ctx, i ) {
1108
		eventTrigger( api, 'deselect', [ 'cell', api[i] ], true );
1109
	} );
1110

1111
	return this;
1112
} );
1113

1114

1115

1116
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1117
 * Buttons
1118
 */
1119
function i18n( label, def ) {
1120
	return function (dt) {
1121
		return dt.i18n( 'buttons.'+label, def );
1122
	};
1123
}
1124

1125
// Common events with suitable namespaces
1126
function namespacedEvents ( config ) {
1127
	var unique = config._eventNamespace;
1128

1129
	return 'draw.dt.DT'+unique+' select.dt.DT'+unique+' deselect.dt.DT'+unique;
1130
}
1131

1132
function enabled ( dt, config ) {
1133
	if ( $.inArray( 'rows', config.limitTo ) !== -1 && dt.rows( { selected: true } ).any() ) {
1134
		return true;
1135
	}
1136

1137
	if ( $.inArray( 'columns', config.limitTo ) !== -1 && dt.columns( { selected: true } ).any() ) {
1138
		return true;
1139
	}
1140

1141
	if ( $.inArray( 'cells', config.limitTo ) !== -1 && dt.cells( { selected: true } ).any() ) {
1142
		return true;
1143
	}
1144

1145
	return false;
1146
}
1147

1148
var _buttonNamespace = 0;
1149

1150
$.extend( DataTable.ext.buttons, {
1151
	selected: {
1152
		text: i18n( 'selected', 'Selected' ),
1153
		className: 'buttons-selected',
1154
		limitTo: [ 'rows', 'columns', 'cells' ],
1155
		init: function ( dt, node, config ) {
1156
			var that = this;
1157
			config._eventNamespace = '.select'+(_buttonNamespace++);
1158

1159
			// .DT namespace listeners are removed by DataTables automatically
1160
			// on table destroy
1161
			dt.on( namespacedEvents(config), function () {
1162
				that.enable( enabled(dt, config) );
1163
			} );
1164

1165
			this.disable();
1166
		},
1167
		destroy: function ( dt, node, config ) {
1168
			dt.off( config._eventNamespace );
1169
		}
1170
	},
1171
	selectedSingle: {
1172
		text: i18n( 'selectedSingle', 'Selected single' ),
1173
		className: 'buttons-selected-single',
1174
		init: function ( dt, node, config ) {
1175
			var that = this;
1176
			config._eventNamespace = '.select'+(_buttonNamespace++);
1177

1178
			dt.on( namespacedEvents(config), function () {
1179
				var count = dt.rows( { selected: true } ).flatten().length +
1180
				            dt.columns( { selected: true } ).flatten().length +
1181
				            dt.cells( { selected: true } ).flatten().length;
1182

1183
				that.enable( count === 1 );
1184
			} );
1185

1186
			this.disable();
1187
		},
1188
		destroy: function ( dt, node, config ) {
1189
			dt.off( config._eventNamespace );
1190
		}
1191
	},
1192
	selectAll: {
1193
		text: i18n( 'selectAll', 'Select all' ),
1194
		className: 'buttons-select-all',
1195
		action: function () {
1196
			var items = this.select.items();
1197
			this[ items+'s' ]().select();
1198
		}
1199
	},
1200
	selectNone: {
1201
		text: i18n( 'selectNone', 'Deselect all' ),
1202
		className: 'buttons-select-none',
1203
		action: function () {
1204
			clear( this.settings()[0], true );
1205
		},
1206
		init: function ( dt, node, config ) {
1207
			var that = this;
1208
			config._eventNamespace = '.select'+(_buttonNamespace++);
1209

1210
			dt.on( namespacedEvents(config), function () {
1211
				var count = dt.rows( { selected: true } ).flatten().length +
1212
				            dt.columns( { selected: true } ).flatten().length +
1213
				            dt.cells( { selected: true } ).flatten().length;
1214

1215
				that.enable( count > 0 );
1216
			} );
1217

1218
			this.disable();
1219
		},
1220
		destroy: function ( dt, node, config ) {
1221
			dt.off( config._eventNamespace );
1222
		}
1223
	}
1224
} );
1225

1226
$.each( [ 'Row', 'Column', 'Cell' ], function ( i, item ) {
1227
	var lc = item.toLowerCase();
1228

1229
	DataTable.ext.buttons[ 'select'+item+'s' ] = {
1230
		text: i18n( 'select'+item+'s', 'Select '+lc+'s' ),
1231
		className: 'buttons-select-'+lc+'s',
1232
		action: function () {
1233
			this.select.items( lc );
1234
		},
1235
		init: function ( dt ) {
1236
			var that = this;
1237

1238
			dt.on( 'selectItems.dt.DT', function ( e, ctx, items ) {
1239
				that.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) {
1256
	if ( e.namespace !== 'dt' ) {
1257
		return;
1258
	}
1259

1260
	DataTable.select.init( new DataTable.Api( ctx ) );
1261
} );
1262

1263

1264
return DataTable.select;
1265
}));
1266

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.