META-INF.resources.bower_components.datatables.net-responsive.js.dataTables.responsive.js Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jwebmp-data-tables Show documentation
Show all versions of jwebmp-data-tables Show documentation
The JWebSwing implementation for Data Tables
The newest version!
/*! Responsive 2.2.3
* 2014-2018 SpryMedia Ltd - datatables.net/license
*/
/**
* @summary Responsive
* @description Responsive tables plug-in for DataTables
* @version 2.2.3
* @file dataTables.responsive.js
* @author SpryMedia Ltd (www.sprymedia.co.uk)
* @contact www.sprymedia.co.uk/contact
* @copyright Copyright 2014-2018 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
*/
(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;
/**
* Responsive is a plug-in for the DataTables library that makes use of
* DataTables' ability to change the visibility of columns, changing the
* visibility of columns so the displayed columns fit into the table container.
* The end result is that complex tables will be dynamically adjusted to fit
* into the viewport, be it on a desktop, tablet or mobile browser.
*
* Responsive for DataTables has two modes of operation, which can used
* individually or combined:
*
* * Class name based control - columns assigned class names that match the
* breakpoint logic can be shown / hidden as required for each breakpoint.
* * Automatic control - columns are automatically hidden when there is no
* room left to display them. Columns removed from the right.
*
* In additional to column visibility control, Responsive also has built into
* options to use DataTables' child row display to show / hide the information
* from the table that has been hidden. There are also two modes of operation
* for this child row display:
*
* * Inline - when the control element that the user can use to show / hide
* child rows is displayed inside the first column of the table.
* * Column - where a whole column is dedicated to be the show / hide control.
*
* Initialisation of Responsive is performed by:
*
* * Adding the class `responsive` or `dt-responsive` to the table. In this case
* Responsive will automatically be initialised with the default configuration
* options when the DataTable is created.
* * Using the `responsive` option in the DataTables configuration options. This
* can also be used to specify the configuration options, or simply set to
* `true` to use the defaults.
*
* @class
* @param {object} settings DataTables settings object for the host table
* @param {object} [opts] Configuration options
* @requires jQuery 1.7+
* @requires DataTables 1.10.3+
*
* @example
* $('#example').DataTable( {
* responsive: true
* } );
* } );
*/
var Responsive = function (settings, opts) {
// Sanity check that we are using DataTables 1.10 or newer
if (!DataTable.versionCheck || !DataTable.versionCheck('1.10.10')) {
throw 'DataTables Responsive requires DataTables 1.10.10 or newer';
}
this.s = {
dt: new DataTable.Api(settings),
columns: [],
current: []
};
// Check if responsive has already been initialised on this table
if (this.s.dt.settings()[0].responsive) {
return;
}
// details is an object, but for simplicity the user can give it as a string
// or a boolean
if (opts && typeof opts.details === 'string') {
opts.details = {type: opts.details};
}
else if (opts && opts.details === false) {
opts.details = {type: false};
}
else if (opts && opts.details === true) {
opts.details = {type: 'inline'};
}
this.c = $.extend(true, {}, Responsive.defaults, DataTable.defaults.responsive, opts);
settings.responsive = this;
this._constructor();
};
$.extend(Responsive.prototype, {
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Constructor
*/
/**
* Initialise the Responsive instance
*
* @private
*/
_constructor: function () {
var that = this;
var dt = this.s.dt;
var dtPrivateSettings = dt.settings()[0];
var oldWindowWidth = $(window).width();
dt.settings()[0]._responsive = this;
// Use DataTables' throttle function to avoid processor thrashing on
// resize
$(window).on('resize.dtr orientationchange.dtr', DataTable.util.throttle(function () {
// iOS has a bug whereby resize can fire when only scrolling
// See: http://stackoverflow.com/questions/8898412
var width = $(window).width();
if (width !== oldWindowWidth) {
that._resize();
oldWindowWidth = width;
}
}));
// DataTables doesn't currently trigger an event when a row is added, so
// we need to hook into its private API to enforce the hidden rows when
// new data is added
dtPrivateSettings.oApi._fnCallbackReg(dtPrivateSettings, 'aoRowCreatedCallback', function (tr, data, idx) {
if ($.inArray(false, that.s.current) !== -1) {
$('>td, >th', tr).each(function (i) {
var idx = dt.column.index('toData', i);
if (that.s.current[idx] === false) {
$(this).css('display', 'none');
}
});
}
});
// Destroy event handler
dt.on('destroy.dtr', function () {
dt.off('.dtr');
$(dt.table().body()).off('.dtr');
$(window).off('resize.dtr orientationchange.dtr');
// Restore the columns that we've hidden
$.each(that.s.current, function (i, val) {
if (val === false) {
that._setColumnVis(i, true);
}
});
});
// Reorder the breakpoints array here in case they have been added out
// of order
this.c.breakpoints.sort(function (a, b) {
return a.width < b.width ? 1 :
a.width > b.width ? -1 : 0;
});
this._classLogic();
this._resizeAuto();
// Details handler
var details = this.c.details;
if (details.type !== false) {
that._detailsInit();
// DataTables will trigger this event on every column it shows and
// hides individually
dt.on('column-visibility.dtr', function () {
// Use a small debounce to allow multiple columns to be set together
if (that._timer) {
clearTimeout(that._timer);
}
that._timer = setTimeout(function () {
that._timer = null;
that._classLogic();
that._resizeAuto();
that._resize();
that._redrawChildren();
}, 100);
});
// Redraw the details box on each draw which will happen if the data
// has changed. This is used until DataTables implements a native
// `updated` event for rows
dt.on('draw.dtr', function () {
that._redrawChildren();
});
$(dt.table().node()).addClass('dtr-' + details.type);
}
dt.on('column-reorder.dtr', function (e, settings, details) {
that._classLogic();
that._resizeAuto();
that._resize();
});
// Change in column sizes means we need to calc
dt.on('column-sizing.dtr', function () {
that._resizeAuto();
that._resize();
});
// On Ajax reload we want to reopen any child rows which are displayed
// by responsive
dt.on('preXhr.dtr', function () {
var rowIds = [];
dt.rows().every(function () {
if (this.child.isShown()) {
rowIds.push(this.id(true));
}
});
dt.one('draw.dtr', function () {
that._resizeAuto();
that._resize();
dt.rows(rowIds).every(function () {
that._detailsDisplay(this, false);
});
});
});
dt.on('init.dtr', function (e, settings, details) {
that._resizeAuto();
that._resize();
// If columns were hidden, then DataTables needs to adjust the
// column sizing
if ($.inArray(false, that.s.current)) {
dt.columns.adjust();
}
});
// First pass - draw the table for the current viewport size
this._resize();
},
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Private methods
*/
/**
* Calculate the visibility for the columns in a table for a given
* breakpoint. The result is pre-determined based on the class logic if
* class names are used to control all columns, but the width of the table
* is also used if there are columns which are to be automatically shown
* and hidden.
*
* @param {string} breakpoint Breakpoint name to use for the calculation
* @return {array} Array of boolean values initiating the visibility of each
* column.
* @private
*/
_columnsVisiblity: function (breakpoint) {
var dt = this.s.dt;
var columns = this.s.columns;
var i, ien;
// Create an array that defines the column ordering based first on the
// column's priority, and secondly the column index. This allows the
// columns to be removed from the right if the priority matches
var order = columns
.map(function (col, idx) {
return {
columnIdx: idx,
priority: col.priority
};
})
.sort(function (a, b) {
if (a.priority !== b.priority) {
return a.priority - b.priority;
}
return a.columnIdx - b.columnIdx;
});
// Class logic - determine which columns are in this breakpoint based
// on the classes. If no class control (i.e. `auto`) then `-` is used
// to indicate this to the rest of the function
var display = $.map(columns, function (col, i) {
if (dt.column(i).visible() === false) {
return 'not-visible';
}
return col.auto && col.minWidth === null ?
false :
col.auto === true ?
'-' :
$.inArray(breakpoint, col.includeIn) !== -1;
});
// Auto column control - first pass: how much width is taken by the
// ones that must be included from the non-auto columns
var requiredWidth = 0;
for (i = 0, ien = display.length; i < ien; i++) {
if (display[i] === true) {
requiredWidth += columns[i].minWidth;
}
}
// Second pass, use up any remaining width for other columns. For
// scrolling tables we need to subtract the width of the scrollbar. It
// may not be requires which makes this sub-optimal, but it would
// require another full redraw to make complete use of those extra few
// pixels
var scrolling = dt.settings()[0].oScroll;
var bar = scrolling.sY || scrolling.sX ? scrolling.iBarWidth : 0;
var widthAvailable = dt.table().container().offsetWidth - bar;
var usedWidth = widthAvailable - requiredWidth;
// Control column needs to always be included. This makes it sub-
// optimal in terms of using the available with, but to stop layout
// thrashing or overflow. Also we need to account for the control column
// width first so we know how much width is available for the other
// columns, since the control column might not be the first one shown
for (i = 0, ien = display.length; i < ien; i++) {
if (columns[i].control) {
usedWidth -= columns[i].minWidth;
}
}
// Allow columns to be shown (counting by priority and then right to
// left) until we run out of room
var empty = false;
for (i = 0, ien = order.length; i < ien; i++) {
var colIdx = order[i].columnIdx;
if (display[colIdx] === '-' && !columns[colIdx].control && columns[colIdx].minWidth) {
// Once we've found a column that won't fit we don't let any
// others display either, or columns might disappear in the
// middle of the table
if (empty || usedWidth - columns[colIdx].minWidth < 0) {
empty = true;
display[colIdx] = false;
}
else {
display[colIdx] = true;
}
usedWidth -= columns[colIdx].minWidth;
}
}
// Determine if the 'control' column should be shown (if there is one).
// This is the case when there is a hidden column (that is not the
// control column). The two loops look inefficient here, but they are
// trivial and will fly through. We need to know the outcome from the
// first , before the action in the second can be taken
var showControl = false;
for (i = 0, ien = columns.length; i < ien; i++) {
if (!columns[i].control && !columns[i].never && display[i] === false) {
showControl = true;
break;
}
}
for (i = 0, ien = columns.length; i < ien; i++) {
if (columns[i].control) {
display[i] = showControl;
}
// Replace not visible string with false from the control column detection above
if (display[i] === 'not-visible') {
display[i] = false;
}
}
// Finally we need to make sure that there is at least one column that
// is visible
if ($.inArray(true, display) === -1) {
display[0] = true;
}
return display;
},
/**
* Create the internal `columns` array with information about the columns
* for the table. This includes determining which breakpoints the column
* will appear in, based upon class names in the column, which makes up the
* vast majority of this method.
*
* @private
*/
_classLogic: function () {
var that = this;
var calc = {};
var breakpoints = this.c.breakpoints;
var dt = this.s.dt;
var columns = dt.columns().eq(0).map(function (i) {
var column = this.column(i);
var className = column.header().className;
var priority = dt.settings()[0].aoColumns[i].responsivePriority;
if (priority === undefined) {
var dataPriority = $(column.header()).data('priority');
priority = dataPriority !== undefined ?
dataPriority * 1 :
10000;
}
return {
className: className,
includeIn: [],
auto: false,
control: false,
never: className.match(/\bnever\b/) ? true : false,
priority: priority
};
});
// Simply add a breakpoint to `includeIn` array, ensuring that there are
// no duplicates
var add = function (colIdx, name) {
var includeIn = columns[colIdx].includeIn;
if ($.inArray(name, includeIn) === -1) {
includeIn.push(name);
}
};
var column = function (colIdx, name, operator, matched) {
var size, i, ien;
if (!operator) {
columns[colIdx].includeIn.push(name);
}
else if (operator === 'max-') {
// Add this breakpoint and all smaller
size = that._find(name).width;
for (i = 0, ien = breakpoints.length; i < ien; i++) {
if (breakpoints[i].width <= size) {
add(colIdx, breakpoints[i].name);
}
}
}
else if (operator === 'min-') {
// Add this breakpoint and all larger
size = that._find(name).width;
for (i = 0, ien = breakpoints.length; i < ien; i++) {
if (breakpoints[i].width >= size) {
add(colIdx, breakpoints[i].name);
}
}
}
else if (operator === 'not-') {
// Add all but this breakpoint
for (i = 0, ien = breakpoints.length; i < ien; i++) {
if (breakpoints[i].name.indexOf(matched) === -1) {
add(colIdx, breakpoints[i].name);
}
}
}
};
// Loop over each column and determine if it has a responsive control
// class
columns.each(function (col, i) {
var classNames = col.className.split(' ');
var hasClass = false;
// Split the class name up so multiple rules can be applied if needed
for (var k = 0, ken = classNames.length; k < ken; k++) {
var className = $.trim(classNames[k]);
if (className === 'all') {
// Include in all
hasClass = true;
col.includeIn = $.map(breakpoints, function (a) {
return a.name;
});
return;
}
else if (className === 'none' || col.never) {
// Include in none (default) and no auto
hasClass = true;
return;
}
else if (className === 'control') {
// Special column that is only visible, when one of the other
// columns is hidden. This is used for the details control
hasClass = true;
col.control = true;
return;
}
$.each(breakpoints, function (j, breakpoint) {
// Does this column have a class that matches this breakpoint?
var brokenPoint = breakpoint.name.split('-');
var re = new RegExp('(min\\-|max\\-|not\\-)?(' + brokenPoint[0] + ')(\\-[_a-zA-Z0-9])?');
var match = className.match(re);
if (match) {
hasClass = true;
if (match[2] === brokenPoint[0] && match[3] === '-' + brokenPoint[1]) {
// Class name matches breakpoint name fully
column(i, breakpoint.name, match[1], match[2] + match[3]);
}
else if (match[2] === brokenPoint[0] && !match[3]) {
// Class name matched primary breakpoint name with no qualifier
column(i, breakpoint.name, match[1], match[2]);
}
}
});
}
// If there was no control class, then automatic sizing is used
if (!hasClass) {
col.auto = true;
}
});
this.s.columns = columns;
},
/**
* Show the details for the child row
*
* @param {DataTables.Api} row API instance for the row
* @param {boolean} update Update flag
* @private
*/
_detailsDisplay: function (row, update) {
var that = this;
var dt = this.s.dt;
var details = this.c.details;
if (details && details.type !== false) {
var res = details.display(row, update, function () {
return details.renderer(
dt, row[0], that._detailsObj(row[0])
);
});
if (res === true || res === false) {
$(dt.table().node()).triggerHandler('responsive-display.dt', [dt, row, res, update]);
}
}
},
/**
* Initialisation for the details handler
*
* @private
*/
_detailsInit: function () {
var that = this;
var dt = this.s.dt;
var details = this.c.details;
// The inline type always uses the first child as the target
if (details.type === 'inline') {
details.target = 'td:first-child, th:first-child';
}
// Keyboard accessibility
dt.on('draw.dtr', function () {
that._tabIndexes();
});
that._tabIndexes(); // Initial draw has already happened
$(dt.table().body()).on('keyup.dtr', 'td, th', function (e) {
if (e.keyCode === 13 && $(this).data('dtr-keyboard')) {
$(this).click();
}
});
// type.target can be a string jQuery selector or a column index
var target = details.target;
var selector = typeof target === 'string' ? target : 'td, th';
// Click handler to show / hide the details rows when they are available
$(dt.table().body())
.on('click.dtr mousedown.dtr mouseup.dtr', selector, function (e) {
// If the table is not collapsed (i.e. there is no hidden columns)
// then take no action
if (!$(dt.table().node()).hasClass('collapsed')) {
return;
}
// Check that the row is actually a DataTable's controlled node
if ($.inArray($(this).closest('tr').get(0), dt.rows().nodes().toArray()) === -1) {
return;
}
// For column index, we determine if we should act or not in the
// handler - otherwise it is already okay
if (typeof target === 'number') {
var targetIdx = target < 0 ?
dt.columns().eq(0).length + target :
target;
if (dt.cell(this).index().column !== targetIdx) {
return;
}
}
// $().closest() includes itself in its check
var row = dt.row($(this).closest('tr'));
// Check event type to do an action
if (e.type === 'click') {
// The renderer is given as a function so the caller can execute it
// only when they need (i.e. if hiding there is no point is running
// the renderer)
that._detailsDisplay(row, false);
}
else if (e.type === 'mousedown') {
// For mouse users, prevent the focus ring from showing
$(this).css('outline', 'none');
}
else if (e.type === 'mouseup') {
// And then re-allow at the end of the click
$(this).blur().css('outline', '');
}
});
},
/**
* Get the details to pass to a renderer for a row
* @param {int} rowIdx Row index
* @private
*/
_detailsObj: function (rowIdx) {
var that = this;
var dt = this.s.dt;
return $.map(this.s.columns, function (col, i) {
// Never and control columns should not be passed to the renderer
if (col.never || col.control) {
return;
}
return {
title: dt.settings()[0].aoColumns[i].sTitle,
data: dt.cell(rowIdx, i).render(that.c.orthogonal),
hidden: dt.column(i).visible() && !that.s.current[i],
columnIndex: i,
rowIndex: rowIdx
};
});
},
/**
* Find a breakpoint object from a name
*
* @param {string} name Breakpoint name to find
* @return {object} Breakpoint description object
* @private
*/
_find: function (name) {
var breakpoints = this.c.breakpoints;
for (var i = 0, ien = breakpoints.length; i < ien; i++) {
if (breakpoints[i].name === name) {
return breakpoints[i];
}
}
},
/**
* Re-create the contents of the child rows as the display has changed in
* some way.
*
* @private
*/
_redrawChildren: function () {
var that = this;
var dt = this.s.dt;
dt.rows({page: 'current'}).iterator('row', function (settings, idx) {
var row = dt.row(idx);
that._detailsDisplay(dt.row(idx), true);
});
},
/**
* Alter the table display for a resized viewport. This involves first
* determining what breakpoint the window currently is in, getting the
* column visibilities to apply and then setting them.
*
* @private
*/
_resize: function () {
var that = this;
var dt = this.s.dt;
var width = $(window).width();
var breakpoints = this.c.breakpoints;
var breakpoint = breakpoints[0].name;
var columns = this.s.columns;
var i, ien;
var oldVis = this.s.current.slice();
// Determine what breakpoint we are currently at
for (i = breakpoints.length - 1; i >= 0; i--) {
if (width <= breakpoints[i].width) {
breakpoint = breakpoints[i].name;
break;
}
}
// Show the columns for that break point
var columnsVis = this._columnsVisiblity(breakpoint);
this.s.current = columnsVis;
// Set the class before the column visibility is changed so event
// listeners know what the state is. Need to determine if there are
// any columns that are not visible but can be shown
var collapsedClass = false;
for (i = 0, ien = columns.length; i < ien; i++) {
if (columnsVis[i] === false && !columns[i].never && !columns[i].control && !dt.column(i).visible() === false) {
collapsedClass = true;
break;
}
}
$(dt.table().node()).toggleClass('collapsed', collapsedClass);
var changed = false;
var visible = 0;
dt.columns().eq(0).each(function (colIdx, i) {
if (columnsVis[i] === true) {
visible++;
}
if (columnsVis[i] !== oldVis[i]) {
changed = true;
that._setColumnVis(colIdx, columnsVis[i]);
}
});
if (changed) {
this._redrawChildren();
// Inform listeners of the change
$(dt.table().node()).trigger('responsive-resize.dt', [dt, this.s.current]);
// If no records, update the "No records" display element
if (dt.page.info().recordsDisplay === 0) {
$('td', dt.table().body()).eq(0).attr('colspan', visible);
}
}
},
/**
* Determine the width of each column in the table so the auto column hiding
* has that information to work with. This method is never going to be 100%
* perfect since column widths can change slightly per page, but without
* seriously compromising performance this is quite effective.
*
* @private
*/
_resizeAuto: function () {
var dt = this.s.dt;
var columns = this.s.columns;
// Are we allowed to do auto sizing?
if (!this.c.auto) {
return;
}
// Are there any columns that actually need auto-sizing, or do they all
// have classes defined
if ($.inArray(true, $.map(columns, function (c) {
return c.auto;
})) === -1) {
return;
}
// Need to restore all children. They will be reinstated by a re-render
if (!$.isEmptyObject(_childNodeStore)) {
$.each(_childNodeStore, function (key) {
var idx = key.split('-');
_childNodesRestore(dt, idx[0] * 1, idx[1] * 1);
});
}
// Clone the table with the current data in it
var tableWidth = dt.table().node().offsetWidth;
var columnWidths = dt.columns;
var clonedTable = dt.table().node().cloneNode(false);
var clonedHeader = $(dt.table().header().cloneNode(false)).appendTo(clonedTable);
var clonedBody = $(dt.table().body()).clone(false, false).empty().appendTo(clonedTable); // use jQuery because of IE8
// Header
var headerCells = dt.columns()
.header()
.filter(function (idx) {
return dt.column(idx).visible();
})
.to$()
.clone(false)
.css('display', 'table-cell')
.css('min-width', 0);
// Body rows - we don't need to take account of DataTables' column
// visibility since we implement our own here (hence the `display` set)
$(clonedBody)
.append($(dt.rows({page: 'current'}).nodes()).clone(false))
.find('th, td').css('display', '');
// Footer
var footer = dt.table().footer();
if (footer) {
var clonedFooter = $(footer.cloneNode(false)).appendTo(clonedTable);
var footerCells = dt.columns()
.footer()
.filter(function (idx) {
return dt.column(idx).visible();
})
.to$()
.clone(false)
.css('display', 'table-cell');
$(' ')
.append(footerCells)
.appendTo(clonedFooter);
}
$(' ')
.append(headerCells)
.appendTo(clonedHeader);
// In the inline case extra padding is applied to the first column to
// give space for the show / hide icon. We need to use this in the
// calculation
if (this.c.details.type === 'inline') {
$(clonedTable).addClass('dtr-inline collapsed');
}
// It is unsafe to insert elements with the same name into the DOM
// multiple times. For example, cloning and inserting a checked radio
// clears the chcecked state of the original radio.
$(clonedTable).find('[name]').removeAttr('name');
// A position absolute table would take the table out of the flow of
// our container element, bypassing the height and width (Scroller)
$(clonedTable).css('position', 'relative')
var inserted = $('')
.css({
width: 1,
height: 1,
overflow: 'hidden',
clear: 'both'
})
.append(clonedTable);
inserted.insertBefore(dt.table().node());
// The cloned header now contains the smallest that each column can be
headerCells.each(function (i) {
var idx = dt.column.index('fromVisible', i);
columns[idx].minWidth = this.offsetWidth || 0;
});
inserted.remove();
},
/**
* Set a column's visibility.
*
* We don't use DataTables' column visibility controls in order to ensure
* that column visibility can Responsive can no-exist. Since only IE8+ is
* supported (and all evergreen browsers of course) the control of the
* display attribute works well.
*
* @param {integer} col Column index
* @param {boolean} showHide Show or hide (true or false)
* @private
*/
_setColumnVis: function (col, showHide) {
var dt = this.s.dt;
var display = showHide ? '' : 'none'; // empty string will remove the attr
$(dt.column(col).header()).css('display', display);
$(dt.column(col).footer()).css('display', display);
dt.column(col).nodes().to$().css('display', display);
// If the are child nodes stored, we might need to reinsert them
if (!$.isEmptyObject(_childNodeStore)) {
dt.cells(null, col).indexes().each(function (idx) {
_childNodesRestore(dt, idx.row, idx.column);
});
}
},
/**
* Update the cell tab indexes for keyboard accessibility. This is called on
* every table draw - that is potentially inefficient, but also the least
* complex option given that column visibility can change on the fly. Its a
* shame user-focus was removed from CSS 3 UI, as it would have solved this
* issue with a single CSS statement.
*
* @private
*/
_tabIndexes: function () {
var dt = this.s.dt;
var cells = dt.cells({page: 'current'}).nodes().to$();
var ctx = dt.settings()[0];
var target = this.c.details.target;
cells.filter('[data-dtr-keyboard]').removeData('[data-dtr-keyboard]');
if (typeof target === 'number') {
dt.cells(null, target, {page: 'current'}).nodes().to$()
.attr('tabIndex', ctx.iTabIndex)
.data('dtr-keyboard', 1);
}
else {
// This is a bit of a hack - we need to limit the selected nodes to just
// those of this table
if (target === 'td:first-child, th:first-child') {
target = '>td:first-child, >th:first-child';
}
$(target, dt.rows({page: 'current'}).nodes())
.attr('tabIndex', ctx.iTabIndex)
.data('dtr-keyboard', 1);
}
}
});
/**
* List of default breakpoints. Each item in the array is an object with two
* properties:
*
* * `name` - the breakpoint name.
* * `width` - the breakpoint width
*
* @name Responsive.breakpoints
* @static
*/
Responsive.breakpoints = [
{name: 'desktop', width: Infinity},
{name: 'tablet-l', width: 1024},
{name: 'tablet-p', width: 768},
{name: 'mobile-l', width: 480},
{name: 'mobile-p', width: 320}
];
/**
* Display methods - functions which define how the hidden data should be shown
* in the table.
*
* @namespace
* @name Responsive.defaults
* @static
*/
Responsive.display = {
childRow: function (row, update, render) {
if (update) {
if ($(row.node()).hasClass('parent')) {
row.child(render(), 'child').show();
return true;
}
}
else {
if (!row.child.isShown()) {
row.child(render(), 'child').show();
$(row.node()).addClass('parent');
return true;
}
else {
row.child(false);
$(row.node()).removeClass('parent');
return false;
}
}
},
childRowImmediate: function (row, update, render) {
if ((!update && row.child.isShown()) || !row.responsive.hasHidden()) {
// User interaction and the row is show, or nothing to show
row.child(false);
$(row.node()).removeClass('parent');
return false;
}
else {
// Display
row.child(render(), 'child').show();
$(row.node()).addClass('parent');
return true;
}
},
// This is a wrapper so the modal options for Bootstrap and jQuery UI can
// have options passed into them. This specific one doesn't need to be a
// function but it is for consistency in the `modal` name
modal: function (options) {
return function (row, update, render) {
if (!update) {
// Show a modal
var close = function () {
modal.remove(); // will tidy events for us
$(document).off('keypress.dtr');
};
var modal = $('')
.append($('')
.append($('')
.append(render())
)
.append($('×')
.click(function () {
close();
})
)
)
.append($('')
.click(function () {
close();
})
)
.appendTo('body');
$(document).on('keyup.dtr', function (e) {
if (e.keyCode === 27) {
e.stopPropagation();
close();
}
});
}
else {
$('div.dtr-modal-content')
.empty()
.append(render());
}
if (options && options.header) {
$('div.dtr-modal-content').prepend(
'' + options.header(row) + '
'
);
}
};
}
};
var _childNodeStore = {};
function _childNodes(dt, row, col) {
var name = row + '-' + col;
if (_childNodeStore[name]) {
return _childNodeStore[name];
}
// https://jsperf.com/childnodes-array-slice-vs-loop
var nodes = [];
var children = dt.cell(row, col).node().childNodes;
for (var i = 0, ien = children.length; i < ien; i++) {
nodes.push(children[i]);
}
_childNodeStore[name] = nodes;
return nodes;
}
function _childNodesRestore(dt, row, col) {
var name = row + '-' + col;
if (!_childNodeStore[name]) {
return;
}
var node = dt.cell(row, col).node();
var store = _childNodeStore[name];
var parent = store[0].parentNode;
var parentChildren = parent.childNodes;
var a = [];
for (var i = 0, ien = parentChildren.length; i < ien; i++) {
a.push(parentChildren[i]);
}
for (var j = 0, jen = a.length; j < jen; j++) {
node.appendChild(a[j]);
}
_childNodeStore[name] = undefined;
}
/**
* Display methods - functions which define how the hidden data should be shown
* in the table.
*
* @namespace
* @name Responsive.defaults
* @static
*/
Responsive.renderer = {
listHiddenNodes: function () {
return function (api, rowIdx, columns) {
var ul = $('
');
var found = false;
var data = $.each(columns, function (i, col) {
if (col.hidden) {
$(
'' +
'' +
col.title +
' ' +
' '
)
.append($('').append(_childNodes(api, col.rowIndex, col.columnIndex)))// api.cell( col.rowIndex, col.columnIndex ).node().childNodes ) )
.appendTo(ul);
found = true;
}
});
return found ?
ul :
false;
};
},
listHidden: function () {
return function (api, rowIdx, columns) {
var data = $.map(columns, function (col) {
return col.hidden ?
'' +
'' +
col.title +
' ' +
'' +
col.data +
'' +
' ' :
'';
}).join('');
return data ?
$('
').append(data) :
false;
}
},
tableAll: function (options) {
options = $.extend({
tableClass: ''
}, options);
return function (api, rowIdx, columns) {
var data = $.map(columns, function (col) {
return '' +
'' + col.title + ':' + ' ' +
'' + col.data + ' ' +
' ';
}).join('');
return $('
').append(data);
}
}
};
/**
* Responsive default settings for initialisation
*
* @namespace
* @name Responsive.defaults
* @static
*/
Responsive.defaults = {
/**
* List of breakpoints for the instance. Note that this means that each
* instance can have its own breakpoints. Additionally, the breakpoints
* cannot be changed once an instance has been creased.
*
* @type {Array}
* @default Takes the value of `Responsive.breakpoints`
*/
breakpoints: Responsive.breakpoints,
/**
* Enable / disable auto hiding calculations. It can help to increase
* performance slightly if you disable this option, but all columns would
* need to have breakpoint classes assigned to them
*
* @type {Boolean}
* @default `true`
*/
auto: true,
/**
* Details control. If given as a string value, the `type` property of the
* default object is set to that value, and the defaults used for the rest
* of the object - this is for ease of implementation.
*
* The object consists of the following properties:
*
* * `display` - A function that is used to show and hide the hidden details
* * `renderer` - function that is called for display of the child row data.
* The default function will show the data from the hidden columns
* * `target` - Used as the selector for what objects to attach the child
* open / close to
* * `type` - `false` to disable the details display, `inline` or `column`
* for the two control types
*
* @type {Object|string}
*/
details: {
display: Responsive.display.childRow,
renderer: Responsive.renderer.listHidden(),
target: 0,
type: 'inline'
},
/**
* Orthogonal data request option. This is used to define the data type
* requested when Responsive gets the data to show in the child row.
*
* @type {String}
*/
orthogonal: 'display'
};
/*
* API
*/
var Api = $.fn.dataTable.Api;
// Doesn't do anything - work around for a bug in DT... Not documented
Api.register('responsive()', function () {
return this;
});
Api.register('responsive.index()', function (li) {
li = $(li);
return {
column: li.data('dtr-index'),
row: li.parent().data('dtr-index')
};
});
Api.register('responsive.rebuild()', function () {
return this.iterator('table', function (ctx) {
if (ctx._responsive) {
ctx._responsive._classLogic();
}
});
});
Api.register('responsive.recalc()', function () {
return this.iterator('table', function (ctx) {
if (ctx._responsive) {
ctx._responsive._resizeAuto();
ctx._responsive._resize();
}
});
});
Api.register('responsive.hasHidden()', function () {
var ctx = this.context[0];
return ctx._responsive ?
$.inArray(false, ctx._responsive.s.current) !== -1 :
false;
});
Api.registerPlural('columns().responsiveHidden()', 'column().responsiveHidden()', function () {
return this.iterator('column', function (settings, column) {
return settings._responsive ?
settings._responsive.s.current[column] :
false;
}, 1);
});
/**
* Version information
*
* @name Responsive.version
* @static
*/
Responsive.version = '2.2.3';
$.fn.dataTable.Responsive = Responsive;
$.fn.DataTable.Responsive = Responsive;
// Attach a listener to the document which listens for DataTables initialisation
// events so we can automatically initialise
$(document).on('preInit.dt.dtr', function (e, settings, json) {
if (e.namespace !== 'dt') {
return;
}
if ($(settings.nTable).hasClass('responsive') ||
$(settings.nTable).hasClass('dt-responsive') ||
settings.oInit.responsive ||
DataTable.defaults.responsive
) {
var init = settings.oInit.responsive;
if (init !== false) {
new Responsive(settings, $.isPlainObject(init) ? init : {});
}
}
});
return Responsive;
}));
© 2015 - 2025 Weber Informatics LLC | Privacy Policy