From 8c88df4ecf027785abfb7bc31baa6b71f02c5aaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Sun, 21 Jun 2026 14:41:56 +0200 Subject: [PATCH] Use upstream version of dataTables.select as the fix was merged --- .../datatables/datatables_controller.js | 4 +- assets/js/lib/dataTables.select.mjs | 1538 ----------------- 2 files changed, 1 insertion(+), 1541 deletions(-) delete mode 100644 assets/js/lib/dataTables.select.mjs diff --git a/assets/controllers/elements/datatables/datatables_controller.js b/assets/controllers/elements/datatables/datatables_controller.js index d945004b..4b84a834 100644 --- a/assets/controllers/elements/datatables/datatables_controller.js +++ b/assets/controllers/elements/datatables/datatables_controller.js @@ -38,9 +38,7 @@ import 'datatables.net-colreorder-bs5'; import 'datatables.net-responsive-bs5'; import '../../../js/lib/datatables'; -//import 'datatables.net-select-bs5'; -//Use the local version containing the fix for the select extension -import '../../../js/lib/dataTables.select.mjs'; +import 'datatables.net-select-bs5'; const EVENT_DT_LOADED = 'dt:loaded'; diff --git a/assets/js/lib/dataTables.select.mjs b/assets/js/lib/dataTables.select.mjs deleted file mode 100644 index bba97692..00000000 --- a/assets/js/lib/dataTables.select.mjs +++ /dev/null @@ -1,1538 +0,0 @@ -/********************* - * This is the fixed version of the select extension for DataTables with the fix for the issue with the select extension - * (https://github.com/DataTables/Select/issues/51) - * We use this instead of the yarn version until the PR (https://github.com/DataTables/Select/pull/52) is merged and released - * /*******************/ - - -/*! Select for DataTables 2.0.0 - * © SpryMedia Ltd - datatables.net/license/mit - */ - -import jQuery from 'jquery'; -import DataTable from 'datatables.net'; - -// Allow reassignment of the $ variable -let $ = jQuery; - - -// Version information for debugger -DataTable.select = {}; - -DataTable.select.version = '2.0.0'; - -DataTable.select.init = function (dt) { - var ctx = dt.settings()[0]; - - if (!DataTable.versionCheck('2')) { - throw 'Warning: Select requires DataTables 2 or newer'; - } - - 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.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 }; - }); - }) - .on('stateLoadParams', selectAndSave) - .one('init', function () { - selectAndSave(undefined, undefined, savedSelected); - }); - - 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 headerCheckbox = true; - var setStyle = false; - - ctx._select = { - infoEls: [] - }; - - // 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; - } - - if (opts.headerCheckbox !== undefined) { - headerCheckbox = opts.headerCheckbox; - } - } - - 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; - - // 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'); - } - - // Insert a checkbox into the header if needed - might need to wait - // for init complete, or it might already be done - if (headerCheckbox) { - initCheckboxHeader(dt); - - dt.on('init', function () { - initCheckboxHeader(dt); - }); - } -}; - -/* - -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 - infoEls:element[] - List of HTML elements with info elements for a table -} -``` - -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 container = dt.table().container(); - - // Ignore clicks inside a sub-table - if ($(e.target).closest('div.dt-container')[0] != 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, node) { - if (api.select.style() === 'api' || api.select.info() === false) { - 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( - $('').append( - api.i18n( - 'select.' + name + 's', - { _: '%d ' + name + 's selected', 0: '', 1: '1 ' + name + ' selected' }, - num - ) - ) - ); - }; - - var el = $(node); - var output = $(''); - - add(output, 'row', rows); - add(output, 'column', columns); - add(output, 'cell', cells); - - var existing = el.children('span.select-info'); - - if (existing.length) { - existing.remove(); - } - - if (output.text() !== '') { - el.append(output); - } -} - -/** - * Add a checkbox to the header for checkbox columns, allowing all rows to - * be selected, deselected or just to show the state. - * - * @param {*} dt API - */ -function initCheckboxHeader( dt ) { - // Find any checkbox column(s) - dt.columns('.dt-select').every(function () { - var header = this.header(); - - if (! $('input', header).length) { - // If no checkbox yet, insert one - var input = $('') - .attr({ - class: 'dt-select-checkbox', - type: 'checkbox', - 'aria-label': dt.i18n('select.aria.headerCheckbox') || 'Select all rows' - }) - .appendTo(header) - .on('change', function () { - if (this.checked) { - dt.rows({search: 'applied'}).select(); - } - else { - dt.rows({selected: true}).deselect(); - } - }) - .on('click', function (e) { - e.stopPropagation(); - }); - - // Update the header checkbox's state when the selection in the - // table changes - dt.on('draw select deselect', function (e, pass, type) { - if (type === 'row' || ! type) { - var count = dt.rows({selected: true}).count(); - var search = dt.rows({search: 'applied', selected: true}).count(); - var available = dt.rows({search: 'applied'}).count(); - - if (search && search <= count && search === available) { - input - .prop('checked', true) - .prop('indeterminate', false); - } - else if (search === 0 && count === 0) { - input - .prop('checked', false) - .prop('indeterminate', false); - } - else { - input - .prop('checked', false) - .prop('indeterminate', true); - } - } - }); - } - }); -} - -/** - * 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(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); - } - } - } - ); - - // 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('info.dt', function (e, ctx, node) { - // Store the info node for updating on select / deselect - if (!ctx._select.infoEls.includes(node)) { - ctx._select.infoEls.push(node); - } - - info(api, node); - }); - - api.on('select.dtSelect.dt deselect.dtSelect.dt', function () { - ctx._select.infoEls.forEach(function (el) { - info(api, el); - }); - - api.state.save(); - }); - - // Clean up and release - api.on('destroy.dtSelect', function () { - // Remove class directly rather than calling deselect - which would trigger events - $(api.rows({ selected: true }).nodes()).removeClass(api.settings()[0]._select.className); - - 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 = indexes.indexOf(last); - var idx2 = indexes.indexOf(idx); - - 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(indexes.indexOf(idx) + 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(indexes.indexOf(idx), 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 ( - data && ( - (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 ( - rowData && ( - (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)); - } - }); -}); - -apiRegister('select.last()', function (set) { - let ctx = this.context[0]; - - if (set) { - ctx._select_lastCell = set; - return this; - } - - return ctx._select_lastCell; -}); - -apiRegisterPlural('rows().select()', 'row().select()', function (select) { - var api = this; - - if (select === false) { - return this.deselect(); - } - - this.iterator('row', function (ctx, idx) { - clear(ctx); - - // There is a good amount of knowledge of DataTables internals in - // this function. It _could_ be done without that, but it would hurt - // performance (or DT would need new APIs for this work) - var dtData = ctx.aoData[idx]; - var dtColumns = ctx.aoColumns; - - $(dtData.nTr).addClass(ctx._select.className); - dtData._select_selected = true; - - for (var i=0 ; i 0); - }); - - this.disable(); - }, - destroy: function (dt, node, config) { - dt.off(config._eventNamespace); - } - }, - showSelected: { - text: i18n('showSelected', 'Show only selected'), - className: 'buttons-show-selected', - action: function (e, dt) { - if (dt.search.fixed('dt-select')) { - // Remove existing function - dt.search.fixed('dt-select', null); - - this.active(false); - } - else { - // Use a fixed filtering function to match on selected rows - // This needs to reference the internal aoData since that is - // where Select stores its reference for the selected state - var dataSrc = dt.settings()[0].aoData; - - dt.search.fixed('dt-select', function (text, data, idx) { - // _select_selected is set by Select on the data object for the row - return dataSrc[idx]._select_selected; - }); - - this.active(true); - } - - dt.draw(); - } - } -}); - -$.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); - }); - } - }; -}); - -DataTable.type('select-checkbox', { - className: 'dt-select', - detect: function (data) { - // Rendering function will tell us if it is a checkbox type - return data === 'select-checkbox' ? data : false; - }, - order: { - pre: function (d) { - return d === 'X' ? -1 : 0; - } - } -}); - -$.extend(true, DataTable.defaults.oLanguage, { - select: { - aria: { - rowCheckbox: 'Select row' - } - } -}); - -DataTable.render.select = function (valueProp, nameProp) { - var valueFn = valueProp ? DataTable.util.get(valueProp) : null; - var nameFn = nameProp ? DataTable.util.get(nameProp) : null; - - return function (data, type, row, meta) { - var dtRow = meta.settings.aoData[meta.row]; - var selected = dtRow._select_selected; - var ariaLabel = meta.settings.oLanguage.select.aria.rowCheckbox; - - if (type === 'display') { - return $('') - .attr({ - 'aria-label': ariaLabel, - class: 'dt-select-checkbox', - name: nameFn ? nameFn(row) : null, - type: 'checkbox', - value: valueFn ? valueFn(row) : null, - checked: selected - })[0]; - } - else if (type === 'type') { - return 'select-checkbox'; - } - else if (type === 'filter') { - return ''; - } - - return selected ? 'X' : ''; - } -} - -// Legacy checkbox ordering -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; - }); -}; - -$.fn.DataTable.select = DataTable.select; - -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * 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)); -}); - - -export default DataTable;