Show More
Commit Description:
select2, ace, moment now work....
Commit Description:
select2, ace, moment now work.
converting navbar
References:
File last commit:
Show/Diff file:
Action:
vendor/javascript/datatables/Select-1.4.0/js/dataTables.select.js
| 1327 lines
| 34.5 KiB
| application/javascript
| JavascriptLexer
|
r866 | /*! Select for DataTables 1.4.0 | |||
* 2015-2021 SpryMedia Ltd - datatables.net/license/mit | ||||
*/ | ||||
/** | ||||
* @summary Select for DataTables | ||||
* @description A collection of API methods, events and buttons for DataTables | ||||
* that provides selection options of the items in a DataTable | ||||
* @version 1.4.0 | ||||
* @file dataTables.select.js | ||||
* @author SpryMedia Ltd (www.sprymedia.co.uk) | ||||
* @contact datatables.net/forums | ||||
* @copyright Copyright 2015-2021 SpryMedia Ltd. | ||||
* | ||||
* This source file is free software, available under the following license: | ||||
* MIT license - http://datatables.net/license/mit | ||||
* | ||||
* This source file is distributed in the hope that it will be useful, but | ||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details. | ||||
* | ||||
* For details please refer to: http://www.datatables.net/extensions/select | ||||
*/ | ||||
(function( factory ){ | ||||
if ( typeof define === 'function' && define.amd ) { | ||||
// AMD | ||||
define( ['jquery', 'datatables.net'], function ( $ ) { | ||||
return factory( $, window, document ); | ||||
} ); | ||||
} | ||||
else if ( typeof exports === 'object' ) { | ||||
// CommonJS | ||||
module.exports = function (root, $) { | ||||
if ( ! root ) { | ||||
root = window; | ||||
} | ||||
if ( ! $ || ! $.fn.dataTable ) { | ||||
$ = require('datatables.net')(root, $).$; | ||||
} | ||||
return factory( $, root, root.document ); | ||||
}; | ||||
} | ||||
else { | ||||
// Browser | ||||
factory( jQuery, window, document ); | ||||
} | ||||
}(function( $, window, document, undefined ) { | ||||
'use strict'; | ||||
var DataTable = $.fn.dataTable; | ||||
// Version information for debugger | ||||
DataTable.select = {}; | ||||
DataTable.select.version = '1.4.0'; | ||||
DataTable.select.init = function ( dt ) { | ||||
var ctx = dt.settings()[0]; | ||||
if (ctx._select) { | ||||
return; | ||||
} | ||||
var savedSelected = dt.state.loaded(); | ||||
var selectAndSave = function(e, settings, data) { | ||||
if(data === null || data.select === undefined) { | ||||
return; | ||||
} | ||||
// Clear any currently selected rows, before restoring state | ||||
// None will be selected on first initialisation | ||||
if (dt.rows({selected: true}).any()) { | ||||
dt.rows().deselect(); | ||||
} | ||||
if (data.select.rows !== undefined) { | ||||
dt.rows(data.select.rows).select(); | ||||
} | ||||
if (dt.columns({selected: true}).any()) { | ||||
dt.columns().deselect(); | ||||
} | ||||
if (data.select.columns !== undefined) { | ||||
dt.columns(data.select.columns).select(); | ||||
} | ||||
if (dt.cells({selected: true}).any()) { | ||||
dt.cells().deselect(); | ||||
} | ||||
if (data.select.cells !== undefined) { | ||||
for(var i = 0; i < data.select.cells.length; i++) { | ||||
dt.cell(data.select.cells[i].row, data.select.cells[i].column).select(); | ||||
} | ||||
} | ||||
dt.state.save(); | ||||
} | ||||
dt.one('init', function() { | ||||
dt.on('stateSaveParams', function(e, settings, data) { | ||||
data.select = {}; | ||||
data.select.rows = dt.rows({selected:true}).ids(true).toArray(); | ||||
data.select.columns = dt.columns({selected:true})[0]; | ||||
data.select.cells = dt.cells({selected:true})[0].map(function(coords) { | ||||
return {row: dt.row(coords.row).id(true), column: coords.column} | ||||
}); | ||||
}) | ||||
selectAndSave(undefined, undefined, savedSelected) | ||||
dt.on('stateLoaded stateLoadParams', selectAndSave) | ||||
}) | ||||
var init = ctx.oInit.select; | ||||
var defaults = DataTable.defaults.select; | ||||
var opts = init === undefined ? | ||||
defaults : | ||||
init; | ||||
// Set defaults | ||||
var items = 'row'; | ||||
var style = 'api'; | ||||
var blurable = false; | ||||
var toggleable = true; | ||||
var info = true; | ||||
var selector = 'td, th'; | ||||
var className = 'selected'; | ||||
var setStyle = false; | ||||
ctx._select = {}; | ||||
// Initialisation customisations | ||||
if ( opts === true ) { | ||||
style = 'os'; | ||||
setStyle = true; | ||||
} | ||||
else if ( typeof opts === 'string' ) { | ||||
style = opts; | ||||
setStyle = true; | ||||
} | ||||
else if ( $.isPlainObject( opts ) ) { | ||||
if ( opts.blurable !== undefined ) { | ||||
blurable = opts.blurable; | ||||
} | ||||
if ( opts.toggleable !== undefined ) { | ||||
toggleable = opts.toggleable; | ||||
} | ||||
if ( opts.info !== undefined ) { | ||||
info = opts.info; | ||||
} | ||||
if ( opts.items !== undefined ) { | ||||
items = opts.items; | ||||
} | ||||
if ( opts.style !== undefined ) { | ||||
style = opts.style; | ||||
setStyle = true; | ||||
} | ||||
else { | ||||
style = 'os'; | ||||
setStyle = true; | ||||
} | ||||
if ( opts.selector !== undefined ) { | ||||
selector = opts.selector; | ||||
} | ||||
if ( opts.className !== undefined ) { | ||||
className = opts.className; | ||||
} | ||||
} | ||||
dt.select.selector( selector ); | ||||
dt.select.items( items ); | ||||
dt.select.style( style ); | ||||
dt.select.blurable( blurable ); | ||||
dt.select.toggleable( toggleable ); | ||||
dt.select.info( info ); | ||||
ctx._select.className = className; | ||||
// Sort table based on selected rows. Requires Select Datatables extension | ||||
$.fn.dataTable.ext.order['select-checkbox'] = function ( settings, col ) { | ||||
return this.api().column( col, {order: 'index'} ).nodes().map( function ( td ) { | ||||
if ( settings._select.items === 'row' ) { | ||||
return $( td ).parent().hasClass( settings._select.className ); | ||||
} else if ( settings._select.items === 'cell' ) { | ||||
return $( td ).hasClass( settings._select.className ); | ||||
} | ||||
return false; | ||||
}); | ||||
}; | ||||
// If the init options haven't enabled select, but there is a selectable | ||||
// class name, then enable | ||||
if ( ! setStyle && $( dt.table().node() ).hasClass( 'selectable' ) ) { | ||||
dt.select.style( 'os' ); | ||||
} | ||||
}; | ||||
/* | ||||
Select is a collection of API methods, event handlers, event emitters and | ||||
buttons (for the `Buttons` extension) for DataTables. It provides the following | ||||
features, with an overview of how they are implemented: | ||||
## Selection of rows, columns and cells. Whether an item is selected or not is | ||||
stored in: | ||||
* rows: a `_select_selected` property which contains a boolean value of the | ||||
DataTables' `aoData` object for each row | ||||
* columns: a `_select_selected` property which contains a boolean value of the | ||||
DataTables' `aoColumns` object for each column | ||||
* cells: a `_selected_cells` property which contains an array of boolean values | ||||
of the `aoData` object for each row. The array is the same length as the | ||||
columns array, with each element of it representing a cell. | ||||
This method of using boolean flags allows Select to operate when nodes have not | ||||
been created for rows / cells (DataTables' defer rendering feature). | ||||
## API methods | ||||
A range of API methods are available for triggering selection and de-selection | ||||
of rows. Methods are also available to configure the selection events that can | ||||
be triggered by an end user (such as which items are to be selected). To a large | ||||
extent, these of API methods *is* Select. It is basically a collection of helper | ||||
functions that can be used to select items in a DataTable. | ||||
Configuration of select is held in the object `_select` which is attached to the | ||||
DataTables settings object on initialisation. Select being available on a table | ||||
is not optional when Select is loaded, but its default is for selection only to | ||||
be available via the API - so the end user wouldn't be able to select rows | ||||
without additional configuration. | ||||
The `_select` object contains the following properties: | ||||
``` | ||||
{ | ||||
items:string - Can be `rows`, `columns` or `cells`. Defines what item | ||||
will be selected if the user is allowed to activate row | ||||
selection using the mouse. | ||||
style:string - Can be `none`, `single`, `multi` or `os`. Defines the | ||||
interaction style when selecting items | ||||
blurable:boolean - If row selection can be cleared by clicking outside of | ||||
the table | ||||
toggleable:boolean - If row selection can be cancelled by repeated clicking | ||||
on the row | ||||
info:boolean - If the selection summary should be shown in the table | ||||
information elements | ||||
} | ||||
``` | ||||
In addition to the API methods, Select also extends the DataTables selector | ||||
options for rows, columns and cells adding a `selected` option to the selector | ||||
options object, allowing the developer to select only selected items or | ||||
unselected items. | ||||
## Mouse selection of items | ||||
Clicking on items can be used to select items. This is done by a simple event | ||||
handler that will select the items using the API methods. | ||||
*/ | ||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | ||||
* Local functions | ||||
*/ | ||||
/** | ||||
* Add one or more cells to the selection when shift clicking in OS selection | ||||
* style cell selection. | ||||
* | ||||
* Cell range is more complicated than row and column as we want to select | ||||
* in the visible grid rather than by index in sequence. For example, if you | ||||
* click first in cell 1-1 and then shift click in 2-2 - cells 1-2 and 2-1 | ||||
* should also be selected (and not 1-3, 1-4. etc) | ||||
* | ||||
* @param {DataTable.Api} dt DataTable | ||||
* @param {object} idx Cell index to select to | ||||
* @param {object} last Cell index to select from | ||||
* @private | ||||
*/ | ||||
function cellRange( dt, idx, last ) | ||||
{ | ||||
var indexes; | ||||
var columnIndexes; | ||||
var rowIndexes; | ||||
var selectColumns = function ( start, end ) { | ||||
if ( start > end ) { | ||||
var tmp = end; | ||||
end = start; | ||||
start = tmp; | ||||
} | ||||
var record = false; | ||||
return dt.columns( ':visible' ).indexes().filter( function (i) { | ||||
if ( i === start ) { | ||||
record = true; | ||||
} | ||||
if ( i === end ) { // not else if, as start might === end | ||||
record = false; | ||||
return true; | ||||
} | ||||
return record; | ||||
} ); | ||||
}; | ||||
var selectRows = function ( start, end ) { | ||||
var indexes = dt.rows( { search: 'applied' } ).indexes(); | ||||
// Which comes first - might need to swap | ||||
if ( indexes.indexOf( start ) > indexes.indexOf( end ) ) { | ||||
var tmp = end; | ||||
end = start; | ||||
start = tmp; | ||||
} | ||||
var record = false; | ||||
return indexes.filter( function (i) { | ||||
if ( i === start ) { | ||||
record = true; | ||||
} | ||||
if ( i === end ) { | ||||
record = false; | ||||
return true; | ||||
} | ||||
return record; | ||||
} ); | ||||
}; | ||||
if ( ! dt.cells( { selected: true } ).any() && ! last ) { | ||||
// select from the top left cell to this one | ||||
columnIndexes = selectColumns( 0, idx.column ); | ||||
rowIndexes = selectRows( 0 , idx.row ); | ||||
} | ||||
else { | ||||
// Get column indexes between old and new | ||||
columnIndexes = selectColumns( last.column, idx.column ); | ||||
rowIndexes = selectRows( last.row , idx.row ); | ||||
} | ||||
indexes = dt.cells( rowIndexes, columnIndexes ).flatten(); | ||||
if ( ! dt.cells( idx, { selected: true } ).any() ) { | ||||
// Select range | ||||
dt.cells( indexes ).select(); | ||||
} | ||||
else { | ||||
// Deselect range | ||||
dt.cells( indexes ).deselect(); | ||||
} | ||||
} | ||||
/** | ||||
* Disable mouse selection by removing the selectors | ||||
* | ||||
* @param {DataTable.Api} dt DataTable to remove events from | ||||
* @private | ||||
*/ | ||||
function disableMouseSelection( dt ) | ||||
{ | ||||
var ctx = dt.settings()[0]; | ||||
var selector = ctx._select.selector; | ||||
$( dt.table().container() ) | ||||
.off( 'mousedown.dtSelect', selector ) | ||||
.off( 'mouseup.dtSelect', selector ) | ||||
.off( 'click.dtSelect', selector ); | ||||
$('body').off( 'click.dtSelect' + _safeId(dt.table().node()) ); | ||||
} | ||||
/** | ||||
* Attach mouse listeners to the table to allow mouse selection of items | ||||
* | ||||
* @param {DataTable.Api} dt DataTable to remove events from | ||||
* @private | ||||
*/ | ||||
function enableMouseSelection ( dt ) | ||||
{ | ||||
var container = $( dt.table().container() ); | ||||
var ctx = dt.settings()[0]; | ||||
var selector = ctx._select.selector; | ||||
var matchSelection; | ||||
container | ||||
.on( 'mousedown.dtSelect', selector, function(e) { | ||||
// Disallow text selection for shift clicking on the table so multi | ||||
// element selection doesn't look terrible! | ||||
if ( e.shiftKey || e.metaKey || e.ctrlKey ) { | ||||
container | ||||
.css( '-moz-user-select', 'none' ) | ||||
.one('selectstart.dtSelect', selector, function () { | ||||
return false; | ||||
} ); | ||||
} | ||||
if ( window.getSelection ) { | ||||
matchSelection = window.getSelection(); | ||||
} | ||||
} ) | ||||
.on( 'mouseup.dtSelect', selector, function() { | ||||
// Allow text selection to occur again, Mozilla style (tested in FF | ||||
// 35.0.1 - still required) | ||||
container.css( '-moz-user-select', '' ); | ||||
} ) | ||||
.on( 'click.dtSelect', selector, function ( e ) { | ||||
var items = dt.select.items(); | ||||
var idx; | ||||
// If text was selected (click and drag), then we shouldn't change | ||||
// the row's selected state | ||||
if ( matchSelection ) { | ||||
var selection = window.getSelection(); | ||||
// If the element that contains the selection is not in the table, we can ignore it | ||||
// This can happen if the developer selects text from the click event | ||||
if ( ! selection.anchorNode || $(selection.anchorNode).closest('table')[0] === dt.table().node() ) { | ||||
if ( selection !== matchSelection ) { | ||||
return; | ||||
} | ||||
} | ||||
} | ||||
var ctx = dt.settings()[0]; | ||||
var wrapperClass = dt.settings()[0].oClasses.sWrapper.trim().replace(/ +/g, '.'); | ||||
// Ignore clicks inside a sub-table | ||||
if ( $(e.target).closest('div.'+wrapperClass)[0] != dt.table().container() ) { | ||||
return; | ||||
} | ||||
var cell = dt.cell( $(e.target).closest('td, th') ); | ||||
// Check the cell actually belongs to the host DataTable (so child | ||||
// rows, etc, are ignored) | ||||
if ( ! cell.any() ) { | ||||
return; | ||||
} | ||||
var event = $.Event('user-select.dt'); | ||||
eventTrigger( dt, event, [ items, cell, e ] ); | ||||
if ( event.isDefaultPrevented() ) { | ||||
return; | ||||
} | ||||
var cellIndex = cell.index(); | ||||
if ( items === 'row' ) { | ||||
idx = cellIndex.row; | ||||
typeSelect( e, dt, ctx, 'row', idx ); | ||||
} | ||||
else if ( items === 'column' ) { | ||||
idx = cell.index().column; | ||||
typeSelect( e, dt, ctx, 'column', idx ); | ||||
} | ||||
else if ( items === 'cell' ) { | ||||
idx = cell.index(); | ||||
typeSelect( e, dt, ctx, 'cell', idx ); | ||||
} | ||||
ctx._select_lastCell = cellIndex; | ||||
} ); | ||||
// Blurable | ||||
$('body').on( 'click.dtSelect' + _safeId(dt.table().node()), function ( e ) { | ||||
if ( ctx._select.blurable ) { | ||||
// If the click was inside the DataTables container, don't blur | ||||
if ( $(e.target).parents().filter( dt.table().container() ).length ) { | ||||
return; | ||||
} | ||||
// Ignore elements which have been removed from the DOM (i.e. paging | ||||
// buttons) | ||||
if ( $(e.target).parents('html').length === 0 ) { | ||||
return; | ||||
} | ||||
// Don't blur in Editor form | ||||
if ( $(e.target).parents('div.DTE').length ) { | ||||
return; | ||||
} | ||||
var event = $.Event('select-blur.dt'); | ||||
eventTrigger( dt, event, [ e.target, e ] ); | ||||
if ( event.isDefaultPrevented() ) { | ||||
return; | ||||
} | ||||
clear( ctx, true ); | ||||
} | ||||
} ); | ||||
} | ||||
/** | ||||
* Trigger an event on a DataTable | ||||
* | ||||
* @param {DataTable.Api} api DataTable to trigger events on | ||||
* @param {boolean} selected true if selected, false if deselected | ||||
* @param {string} type Item type acting on | ||||
* @param {boolean} any Require that there are values before | ||||
* triggering | ||||
* @private | ||||
*/ | ||||
function eventTrigger ( api, type, args, any ) | ||||
{ | ||||
if ( any && ! api.flatten().length ) { | ||||
return; | ||||
} | ||||
if ( typeof type === 'string' ) { | ||||
type = type +'.dt'; | ||||
} | ||||
args.unshift( api ); | ||||
$(api.table().node()).trigger( type, args ); | ||||
} | ||||
/** | ||||
* Update the information element of the DataTable showing information about the | ||||
* items selected. This is done by adding tags to the existing text | ||||
* | ||||
* @param {DataTable.Api} api DataTable to update | ||||
* @private | ||||
*/ | ||||
function info ( api ) | ||||
{ | ||||
var ctx = api.settings()[0]; | ||||
if ( ! ctx._select.info || ! ctx.aanFeatures.i ) { | ||||
return; | ||||
} | ||||
if ( api.select.style() === 'api' ) { | ||||
return; | ||||
} | ||||
var rows = api.rows( { selected: true } ).flatten().length; | ||||
var columns = api.columns( { selected: true } ).flatten().length; | ||||
var cells = api.cells( { selected: true } ).flatten().length; | ||||
var add = function ( el, name, num ) { | ||||
el.append( $('<span class="select-item"/>').append( api.i18n( | ||||
'select.'+name+'s', | ||||
{ _: '%d '+name+'s selected', 0: '', 1: '1 '+name+' selected' }, | ||||
num | ||||
) ) ); | ||||
}; | ||||
// Internal knowledge of DataTables to loop over all information elements | ||||
$.each( ctx.aanFeatures.i, function ( i, el ) { | ||||
el = $(el); | ||||
var output = $('<span class="select-info"/>'); | ||||
add( output, 'row', rows ); | ||||
add( output, 'column', columns ); | ||||
add( output, 'cell', cells ); | ||||
var exisiting = el.children('span.select-info'); | ||||
if ( exisiting.length ) { | ||||
exisiting.remove(); | ||||
} | ||||
if ( output.text() !== '' ) { | ||||
el.append( output ); | ||||
} | ||||
} ); | ||||
} | ||||
/** | ||||
* Initialisation of a new table. Attach event handlers and callbacks to allow | ||||
* Select to operate correctly. | ||||
* | ||||
* This will occur _after_ the initial DataTables initialisation, although | ||||
* before Ajax data is rendered, if there is ajax data | ||||
* | ||||
* @param {DataTable.settings} ctx Settings object to operate on | ||||
* @private | ||||
*/ | ||||
function init ( ctx ) { | ||||
var api = new DataTable.Api( ctx ); | ||||
ctx._select_init = true; | ||||
// Row callback so that classes can be added to rows and cells if the item | ||||
// was selected before the element was created. This will happen with the | ||||
// `deferRender` option enabled. | ||||
// | ||||
// This method of attaching to `aoRowCreatedCallback` is a hack until | ||||
// DataTables has proper events for row manipulation If you are reviewing | ||||
// this code to create your own plug-ins, please do not do this! | ||||
ctx.aoRowCreatedCallback.push( { | ||||
fn: function ( row, data, index ) { | ||||
var i, ien; | ||||
var d = ctx.aoData[ index ]; | ||||
// Row | ||||
if ( d._select_selected ) { | ||||
$( row ).addClass( ctx._select.className ); | ||||
} | ||||
// Cells and columns - if separated out, we would need to do two | ||||
// loops, so it makes sense to combine them into a single one | ||||
for ( i=0, ien=ctx.aoColumns.length ; i<ien ; i++ ) { | ||||
if ( ctx.aoColumns[i]._select_selected || (d._selected_cells && d._selected_cells[i]) ) { | ||||
$(d.anCells[i]).addClass( ctx._select.className ); | ||||
} | ||||
} | ||||
}, | ||||
sName: 'select-deferRender' | ||||
} ); | ||||
// On Ajax reload we want to reselect all rows which are currently selected, | ||||
// if there is an rowId (i.e. a unique value to identify each row with) | ||||
api.on( 'preXhr.dt.dtSelect', function (e, settings) { | ||||
if (settings !== api.settings()[0]) { | ||||
// Not triggered by our DataTable! | ||||
return; | ||||
} | ||||
// note that column selection doesn't need to be cached and then | ||||
// reselected, as they are already selected | ||||
var rows = api.rows( { selected: true } ).ids( true ).filter( function ( d ) { | ||||
return d !== undefined; | ||||
} ); | ||||
var cells = api.cells( { selected: true } ).eq(0).map( function ( cellIdx ) { | ||||
var id = api.row( cellIdx.row ).id( true ); | ||||
return id ? | ||||
{ row: id, column: cellIdx.column } : | ||||
undefined; | ||||
} ).filter( function ( d ) { | ||||
return d !== undefined; | ||||
} ); | ||||
// On the next draw, reselect the currently selected items | ||||
api.one( 'draw.dt.dtSelect', function () { | ||||
api.rows( rows ).select(); | ||||
// `cells` is not a cell index selector, so it needs a loop | ||||
if ( cells.any() ) { | ||||
cells.each( function ( id ) { | ||||
api.cells( id.row, id.column ).select(); | ||||
} ); | ||||
} | ||||
} ); | ||||
} ); | ||||
// Update the table information element with selected item summary | ||||
api.on( 'draw.dtSelect.dt select.dtSelect.dt deselect.dtSelect.dt info.dt', function () { | ||||
info( api ); | ||||
api.state.save(); | ||||
} ); | ||||
// Clean up and release | ||||
api.on( 'destroy.dtSelect', function () { | ||||
api.rows({selected: true}).deselect(); | ||||
disableMouseSelection( api ); | ||||
api.off( '.dtSelect' ); | ||||
$('body').off('.dtSelect' + _safeId(api.table().node())); | ||||
} ); | ||||
} | ||||
/** | ||||
* Add one or more items (rows or columns) to the selection when shift clicking | ||||
* in OS selection style | ||||
* | ||||
* @param {DataTable.Api} dt DataTable | ||||
* @param {string} type Row or column range selector | ||||
* @param {object} idx Item index to select to | ||||
* @param {object} last Item index to select from | ||||
* @private | ||||
*/ | ||||
function rowColumnRange( dt, type, idx, last ) | ||||
{ | ||||
// Add a range of rows from the last selected row to this one | ||||
var indexes = dt[type+'s']( { search: 'applied' } ).indexes(); | ||||
var idx1 = $.inArray( last, indexes ); | ||||
var idx2 = $.inArray( idx, indexes ); | ||||
if ( ! dt[type+'s']( { selected: true } ).any() && idx1 === -1 ) { | ||||
// select from top to here - slightly odd, but both Windows and Mac OS | ||||
// do this | ||||
indexes.splice( $.inArray( idx, indexes )+1, indexes.length ); | ||||
} | ||||
else { | ||||
// reverse so we can shift click 'up' as well as down | ||||
if ( idx1 > idx2 ) { | ||||
var tmp = idx2; | ||||
idx2 = idx1; | ||||
idx1 = tmp; | ||||
} | ||||
indexes.splice( idx2+1, indexes.length ); | ||||
indexes.splice( 0, idx1 ); | ||||
} | ||||
if ( ! dt[type]( idx, { selected: true } ).any() ) { | ||||
// Select range | ||||
dt[type+'s']( indexes ).select(); | ||||
} | ||||
else { | ||||
// Deselect range - need to keep the clicked on row selected | ||||
indexes.splice( $.inArray( idx, indexes ), 1 ); | ||||
dt[type+'s']( indexes ).deselect(); | ||||
} | ||||
} | ||||
/** | ||||
* Clear all selected items | ||||
* | ||||
* @param {DataTable.settings} ctx Settings object of the host DataTable | ||||
* @param {boolean} [force=false] Force the de-selection to happen, regardless | ||||
* of selection style | ||||
* @private | ||||
*/ | ||||
function clear( ctx, force ) | ||||
{ | ||||
if ( force || ctx._select.style === 'single' ) { | ||||
var api = new DataTable.Api( ctx ); | ||||
api.rows( { selected: true } ).deselect(); | ||||
api.columns( { selected: true } ).deselect(); | ||||
api.cells( { selected: true } ).deselect(); | ||||
} | ||||
} | ||||
/** | ||||
* Select items based on the current configuration for style and items. | ||||
* | ||||
* @param {object} e Mouse event object | ||||
* @param {DataTables.Api} dt DataTable | ||||
* @param {DataTable.settings} ctx Settings object of the host DataTable | ||||
* @param {string} type Items to select | ||||
* @param {int|object} idx Index of the item to select | ||||
* @private | ||||
*/ | ||||
function typeSelect ( e, dt, ctx, type, idx ) | ||||
{ | ||||
var style = dt.select.style(); | ||||
var toggleable = dt.select.toggleable(); | ||||
var isSelected = dt[type]( idx, { selected: true } ).any(); | ||||
if ( isSelected && ! toggleable ) { | ||||
return; | ||||
} | ||||
if ( style === 'os' ) { | ||||
if ( e.ctrlKey || e.metaKey ) { | ||||
// Add or remove from the selection | ||||
dt[type]( idx ).select( ! isSelected ); | ||||
} | ||||
else if ( e.shiftKey ) { | ||||
if ( type === 'cell' ) { | ||||
cellRange( dt, idx, ctx._select_lastCell || null ); | ||||
} | ||||
else { | ||||
rowColumnRange( dt, type, idx, ctx._select_lastCell ? | ||||
ctx._select_lastCell[type] : | ||||
null | ||||
); | ||||
} | ||||
} | ||||
else { | ||||
// No cmd or shift click - deselect if selected, or select | ||||
// this row only | ||||
var selected = dt[type+'s']( { selected: true } ); | ||||
if ( isSelected && selected.flatten().length === 1 ) { | ||||
dt[type]( idx ).deselect(); | ||||
} | ||||
else { | ||||
selected.deselect(); | ||||
dt[type]( idx ).select(); | ||||
} | ||||
} | ||||
} else if ( style == 'multi+shift' ) { | ||||
if ( e.shiftKey ) { | ||||
if ( type === 'cell' ) { | ||||
cellRange( dt, idx, ctx._select_lastCell || null ); | ||||
} | ||||
else { | ||||
rowColumnRange( dt, type, idx, ctx._select_lastCell ? | ||||
ctx._select_lastCell[type] : | ||||
null | ||||
); | ||||
} | ||||
} | ||||
else { | ||||
dt[ type ]( idx ).select( ! isSelected ); | ||||
} | ||||
} | ||||
else { | ||||
dt[ type ]( idx ).select( ! isSelected ); | ||||
} | ||||
} | ||||
function _safeId( node ) { | ||||
return node.id.replace(/[^a-zA-Z0-9\-\_]/g, '-'); | ||||
} | ||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | ||||
* DataTables selectors | ||||
*/ | ||||
// row and column are basically identical just assigned to different properties | ||||
// and checking a different array, so we can dynamically create the functions to | ||||
// reduce the code size | ||||
$.each( [ | ||||
{ type: 'row', prop: 'aoData' }, | ||||
{ type: 'column', prop: 'aoColumns' } | ||||
], function ( i, o ) { | ||||
DataTable.ext.selector[ o.type ].push( function ( settings, opts, indexes ) { | ||||
var selected = opts.selected; | ||||
var data; | ||||
var out = []; | ||||
if ( selected !== true && selected !== false ) { | ||||
return indexes; | ||||
} | ||||
for ( var i=0, ien=indexes.length ; i<ien ; i++ ) { | ||||
data = settings[ o.prop ][ indexes[i] ]; | ||||
if ( (selected === true && data._select_selected === true) || | ||||
(selected === false && ! data._select_selected ) | ||||
) { | ||||
out.push( indexes[i] ); | ||||
} | ||||
} | ||||
return out; | ||||
} ); | ||||
} ); | ||||
DataTable.ext.selector.cell.push( function ( settings, opts, cells ) { | ||||
var selected = opts.selected; | ||||
var rowData; | ||||
var out = []; | ||||
if ( selected === undefined ) { | ||||
return cells; | ||||
} | ||||
for ( var i=0, ien=cells.length ; i<ien ; i++ ) { | ||||
rowData = settings.aoData[ cells[i].row ]; | ||||
if ( (selected === true && rowData._selected_cells && rowData._selected_cells[ cells[i].column ] === true) || | ||||
(selected === false && ( ! rowData._selected_cells || ! rowData._selected_cells[ cells[i].column ] ) ) | ||||
) { | ||||
out.push( cells[i] ); | ||||
} | ||||
} | ||||
return out; | ||||
} ); | ||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | ||||
* DataTables API | ||||
* | ||||
* For complete documentation, please refer to the docs/api directory or the | ||||
* DataTables site | ||||
*/ | ||||
// Local variables to improve compression | ||||
var apiRegister = DataTable.Api.register; | ||||
var apiRegisterPlural = DataTable.Api.registerPlural; | ||||
apiRegister( 'select()', function () { | ||||
return this.iterator( 'table', function ( ctx ) { | ||||
DataTable.select.init( new DataTable.Api( ctx ) ); | ||||
} ); | ||||
} ); | ||||
apiRegister( 'select.blurable()', function ( flag ) { | ||||
if ( flag === undefined ) { | ||||
return this.context[0]._select.blurable; | ||||
} | ||||
return this.iterator( 'table', function ( ctx ) { | ||||
ctx._select.blurable = flag; | ||||
} ); | ||||
} ); | ||||
apiRegister( 'select.toggleable()', function ( flag ) { | ||||
if ( flag === undefined ) { | ||||
return this.context[0]._select.toggleable; | ||||
} | ||||
return this.iterator( 'table', function ( ctx ) { | ||||
ctx._select.toggleable = flag; | ||||
} ); | ||||
} ); | ||||
apiRegister( 'select.info()', function ( flag ) { | ||||
if ( flag === undefined ) { | ||||
return this.context[0]._select.info; | ||||
} | ||||
return this.iterator( 'table', function ( ctx ) { | ||||
ctx._select.info = flag; | ||||
} ); | ||||
} ); | ||||
apiRegister( 'select.items()', function ( items ) { | ||||
if ( items === undefined ) { | ||||
return this.context[0]._select.items; | ||||
} | ||||
return this.iterator( 'table', function ( ctx ) { | ||||
ctx._select.items = items; | ||||
eventTrigger( new DataTable.Api( ctx ), 'selectItems', [ items ] ); | ||||
} ); | ||||
} ); | ||||
// Takes effect from the _next_ selection. None disables future selection, but | ||||
// does not clear the current selection. Use the `deselect` methods for that | ||||
apiRegister( 'select.style()', function ( style ) { | ||||
if ( style === undefined ) { | ||||
return this.context[0]._select.style; | ||||
} | ||||
return this.iterator( 'table', function ( ctx ) { | ||||
if ( ! ctx._select ) { | ||||
DataTable.select.init( new DataTable.Api(ctx) ); | ||||
} | ||||
if ( ! ctx._select_init ) { | ||||
init(ctx); | ||||
} | ||||
ctx._select.style = style; | ||||
// Add / remove mouse event handlers. They aren't required when only | ||||
// API selection is available | ||||
var dt = new DataTable.Api( ctx ); | ||||
disableMouseSelection( dt ); | ||||
if ( style !== 'api' ) { | ||||
enableMouseSelection( dt ); | ||||
} | ||||
eventTrigger( new DataTable.Api( ctx ), 'selectStyle', [ style ] ); | ||||
} ); | ||||
} ); | ||||
apiRegister( 'select.selector()', function ( selector ) { | ||||
if ( selector === undefined ) { | ||||
return this.context[0]._select.selector; | ||||
} | ||||
return this.iterator( 'table', function ( ctx ) { | ||||
disableMouseSelection( new DataTable.Api( ctx ) ); | ||||
ctx._select.selector = selector; | ||||
if ( ctx._select.style !== 'api' ) { | ||||
enableMouseSelection( new DataTable.Api( ctx ) ); | ||||
} | ||||
} ); | ||||
} ); | ||||
apiRegisterPlural( 'rows().select()', 'row().select()', function ( select ) { | ||||
var api = this; | ||||
if ( select === false ) { | ||||
return this.deselect(); | ||||
} | ||||
this.iterator( 'row', function ( ctx, idx ) { | ||||
clear( ctx ); | ||||
ctx.aoData[ idx ]._select_selected = true; | ||||
$( ctx.aoData[ idx ].nTr ).addClass( ctx._select.className ); | ||||
} ); | ||||
this.iterator( 'table', function ( ctx, i ) { | ||||
eventTrigger( api, 'select', [ 'row', api[i] ], true ); | ||||
} ); | ||||
return this; | ||||
} ); | ||||
apiRegister( 'row().selected()', function () { | ||||
var ctx = this.context[0]; | ||||
if ( | ||||
ctx && | ||||
this.length && | ||||
ctx.aoData[this[0]] && | ||||
ctx.aoData[this[0]]._select_selected | ||||
) { | ||||
return true; | ||||
} | ||||
return false; | ||||
} ); | ||||
apiRegisterPlural( 'columns().select()', 'column().select()', function ( select ) { | ||||
var api = this; | ||||
if ( select === false ) { | ||||
return this.deselect(); | ||||
} | ||||
this.iterator( 'column', function ( ctx, idx ) { | ||||
clear( ctx ); | ||||
ctx.aoColumns[ idx ]._select_selected = true; | ||||
var column = new DataTable.Api( ctx ).column( idx ); | ||||
$( column.header() ).addClass( ctx._select.className ); | ||||
$( column.footer() ).addClass( ctx._select.className ); | ||||
column.nodes().to$().addClass( ctx._select.className ); | ||||
} ); | ||||
this.iterator( 'table', function ( ctx, i ) { | ||||
eventTrigger( api, 'select', [ 'column', api[i] ], true ); | ||||
} ); | ||||
return this; | ||||
} ); | ||||
apiRegister( 'column().selected()', function () { | ||||
var ctx = this.context[0]; | ||||
if ( | ||||
ctx && | ||||
this.length && | ||||
ctx.aoColumns[this[0]] && | ||||
ctx.aoColumns[this[0]]._select_selected | ||||
) { | ||||
return true; | ||||
} | ||||
return false; | ||||
} ); | ||||
apiRegisterPlural( 'cells().select()', 'cell().select()', function ( select ) { | ||||
var api = this; | ||||
if ( select === false ) { | ||||
return this.deselect(); | ||||
} | ||||
this.iterator( 'cell', function ( ctx, rowIdx, colIdx ) { | ||||
clear( ctx ); | ||||
var data = ctx.aoData[ rowIdx ]; | ||||
if ( data._selected_cells === undefined ) { | ||||
data._selected_cells = []; | ||||
} | ||||
data._selected_cells[ colIdx ] = true; | ||||
if ( data.anCells ) { | ||||
$( data.anCells[ colIdx ] ).addClass( ctx._select.className ); | ||||
} | ||||
} ); | ||||
this.iterator( 'table', function ( ctx, i ) { | ||||
eventTrigger( api, 'select', [ 'cell', api.cells(api[i]).indexes().toArray() ], true ); | ||||
} ); | ||||
return this; | ||||
} ); | ||||
apiRegister( 'cell().selected()', function () { | ||||
var ctx = this.context[0]; | ||||
if (ctx && this.length) { | ||||
var row = ctx.aoData[this[0][0].row]; | ||||
if (row && row._selected_cells && row._selected_cells[this[0][0].column]) { | ||||
return true; | ||||
} | ||||
} | ||||
return false; | ||||
} ); | ||||
apiRegisterPlural( 'rows().deselect()', 'row().deselect()', function () { | ||||
var api = this; | ||||
this.iterator( 'row', function ( ctx, idx ) { | ||||
ctx.aoData[ idx ]._select_selected = false; | ||||
ctx._select_lastCell = null; | ||||
$( ctx.aoData[ idx ].nTr ).removeClass( ctx._select.className ); | ||||
} ); | ||||
this.iterator( 'table', function ( ctx, i ) { | ||||
eventTrigger( api, 'deselect', [ 'row', api[i] ], true ); | ||||
} ); | ||||
return this; | ||||
} ); | ||||
apiRegisterPlural( 'columns().deselect()', 'column().deselect()', function () { | ||||
var api = this; | ||||
this.iterator( 'column', function ( ctx, idx ) { | ||||
ctx.aoColumns[ idx ]._select_selected = false; | ||||
var api = new DataTable.Api( ctx ); | ||||
var column = api.column( idx ); | ||||
$( column.header() ).removeClass( ctx._select.className ); | ||||
$( column.footer() ).removeClass( ctx._select.className ); | ||||
// Need to loop over each cell, rather than just using | ||||
// `column().nodes()` as cells which are individually selected should | ||||
// not have the `selected` class removed from them | ||||
api.cells( null, idx ).indexes().each( function (cellIdx) { | ||||
var data = ctx.aoData[ cellIdx.row ]; | ||||
var cellSelected = data._selected_cells; | ||||
if ( data.anCells && (! cellSelected || ! cellSelected[ cellIdx.column ]) ) { | ||||
$( data.anCells[ cellIdx.column ] ).removeClass( ctx._select.className ); | ||||
} | ||||
} ); | ||||
} ); | ||||
this.iterator( 'table', function ( ctx, i ) { | ||||
eventTrigger( api, 'deselect', [ 'column', api[i] ], true ); | ||||
} ); | ||||
return this; | ||||
} ); | ||||
apiRegisterPlural( 'cells().deselect()', 'cell().deselect()', function () { | ||||
var api = this; | ||||
this.iterator( 'cell', function ( ctx, rowIdx, colIdx ) { | ||||
var data = ctx.aoData[ rowIdx ]; | ||||
if(data._selected_cells !== undefined) { | ||||
data._selected_cells[ colIdx ] = false; | ||||
} | ||||
// Remove class only if the cells exist, and the cell is not column | ||||
// selected, in which case the class should remain (since it is selected | ||||
// in the column) | ||||
if ( data.anCells && ! ctx.aoColumns[ colIdx ]._select_selected ) { | ||||
$( data.anCells[ colIdx ] ).removeClass( ctx._select.className ); | ||||
} | ||||
} ); | ||||
this.iterator( 'table', function ( ctx, i ) { | ||||
eventTrigger( api, 'deselect', [ 'cell', api[i] ], true ); | ||||
} ); | ||||
return this; | ||||
} ); | ||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | ||||
* Buttons | ||||
*/ | ||||
function i18n( label, def ) { | ||||
return function (dt) { | ||||
return dt.i18n( 'buttons.'+label, def ); | ||||
}; | ||||
} | ||||
// Common events with suitable namespaces | ||||
function namespacedEvents ( config ) { | ||||
var unique = config._eventNamespace; | ||||
return 'draw.dt.DT'+unique+' select.dt.DT'+unique+' deselect.dt.DT'+unique; | ||||
} | ||||
function enabled ( dt, config ) { | ||||
if ( $.inArray( 'rows', config.limitTo ) !== -1 && dt.rows( { selected: true } ).any() ) { | ||||
return true; | ||||
} | ||||
if ( $.inArray( 'columns', config.limitTo ) !== -1 && dt.columns( { selected: true } ).any() ) { | ||||
return true; | ||||
} | ||||
if ( $.inArray( 'cells', config.limitTo ) !== -1 && dt.cells( { selected: true } ).any() ) { | ||||
return true; | ||||
} | ||||
return false; | ||||
} | ||||
var _buttonNamespace = 0; | ||||
$.extend( DataTable.ext.buttons, { | ||||
selected: { | ||||
text: i18n( 'selected', 'Selected' ), | ||||
className: 'buttons-selected', | ||||
limitTo: [ 'rows', 'columns', 'cells' ], | ||||
init: function ( dt, node, config ) { | ||||
var that = this; | ||||
config._eventNamespace = '.select'+(_buttonNamespace++); | ||||
// .DT namespace listeners are removed by DataTables automatically | ||||
// on table destroy | ||||
dt.on( namespacedEvents(config), function () { | ||||
that.enable( enabled(dt, config) ); | ||||
} ); | ||||
this.disable(); | ||||
}, | ||||
destroy: function ( dt, node, config ) { | ||||
dt.off( config._eventNamespace ); | ||||
} | ||||
}, | ||||
selectedSingle: { | ||||
text: i18n( 'selectedSingle', 'Selected single' ), | ||||
className: 'buttons-selected-single', | ||||
init: function ( dt, node, config ) { | ||||
var that = this; | ||||
config._eventNamespace = '.select'+(_buttonNamespace++); | ||||
dt.on( namespacedEvents(config), function () { | ||||
var count = dt.rows( { selected: true } ).flatten().length + | ||||
dt.columns( { selected: true } ).flatten().length + | ||||
dt.cells( { selected: true } ).flatten().length; | ||||
that.enable( count === 1 ); | ||||
} ); | ||||
this.disable(); | ||||
}, | ||||
destroy: function ( dt, node, config ) { | ||||
dt.off( config._eventNamespace ); | ||||
} | ||||
}, | ||||
selectAll: { | ||||
text: i18n( 'selectAll', 'Select all' ), | ||||
className: 'buttons-select-all', | ||||
action: function () { | ||||
var items = this.select.items(); | ||||
this[ items+'s' ]().select(); | ||||
} | ||||
}, | ||||
selectNone: { | ||||
text: i18n( 'selectNone', 'Deselect all' ), | ||||
className: 'buttons-select-none', | ||||
action: function () { | ||||
clear( this.settings()[0], true ); | ||||
}, | ||||
init: function ( dt, node, config ) { | ||||
var that = this; | ||||
config._eventNamespace = '.select'+(_buttonNamespace++); | ||||
dt.on( namespacedEvents(config), function () { | ||||
var count = dt.rows( { selected: true } ).flatten().length + | ||||
dt.columns( { selected: true } ).flatten().length + | ||||
dt.cells( { selected: true } ).flatten().length; | ||||
that.enable( count > 0 ); | ||||
} ); | ||||
this.disable(); | ||||
}, | ||||
destroy: function ( dt, node, config ) { | ||||
dt.off( config._eventNamespace ); | ||||
} | ||||
} | ||||
} ); | ||||
$.each( [ 'Row', 'Column', 'Cell' ], function ( i, item ) { | ||||
var lc = item.toLowerCase(); | ||||
DataTable.ext.buttons[ 'select'+item+'s' ] = { | ||||
text: i18n( 'select'+item+'s', 'Select '+lc+'s' ), | ||||
className: 'buttons-select-'+lc+'s', | ||||
action: function () { | ||||
this.select.items( lc ); | ||||
}, | ||||
init: function ( dt ) { | ||||
var that = this; | ||||
dt.on( 'selectItems.dt.DT', function ( e, ctx, items ) { | ||||
that.active( items === lc ); | ||||
} ); | ||||
} | ||||
}; | ||||
} ); | ||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | ||||
* Initialisation | ||||
*/ | ||||
// DataTables creation - check if select has been defined in the options. Note | ||||
// this required that the table be in the document! If it isn't then something | ||||
// needs to trigger this method unfortunately. The next major release of | ||||
// DataTables will rework the events and address this. | ||||
$(document).on( 'preInit.dt.dtSelect', function (e, ctx) { | ||||
if ( e.namespace !== 'dt' ) { | ||||
return; | ||||
} | ||||
DataTable.select.init( new DataTable.Api( ctx ) ); | ||||
} ); | ||||
return DataTable.select; | ||||
})); | ||||