diff --git a/plugins/DataTables/datatables.css b/plugins/DataTables/datatables.css index bc32c573..2e9b2b58 100644 --- a/plugins/DataTables/datatables.css +++ b/plugins/DataTables/datatables.css @@ -4,10 +4,10 @@ * * To rebuild or modify this file with the latest versions of the included * software please visit: - * https://datatables.net/download/#bs4/dt-2.0.8 + * https://datatables.net/download/#bs4/dt-2.1.8 * * Included libraries: - * DataTables 2.0.8 + * DataTables 2.1.8 */ @charset "UTF-8"; @@ -45,15 +45,21 @@ table.dataTable tr.dt-hasChild td.dt-control:before { } html.dark table.dataTable td.dt-control:before, -:root[data-bs-theme=dark] table.dataTable td.dt-control:before { +:root[data-bs-theme=dark] table.dataTable td.dt-control:before, +:root[data-theme=dark] table.dataTable td.dt-control:before { border-left-color: rgba(255, 255, 255, 0.5); } html.dark table.dataTable tr.dt-hasChild td.dt-control:before, -:root[data-bs-theme=dark] table.dataTable tr.dt-hasChild td.dt-control:before { +:root[data-bs-theme=dark] table.dataTable tr.dt-hasChild td.dt-control:before, +:root[data-theme=dark] table.dataTable tr.dt-hasChild td.dt-control:before { border-top-color: rgba(255, 255, 255, 0.5); border-left-color: transparent; } +div.dt-scroll { + width: 100%; +} + div.dt-scroll-body thead tr, div.dt-scroll-body tfoot tr { height: 0; @@ -369,10 +375,35 @@ table.dataTable.table.table-hover > tbody > tr.selected:hover > * { box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.975); } -div.dt-container div.row { +div.dt-container div.dt-layout-start > *:not(:last-child) { + margin-right: 1em; +} +div.dt-container div.dt-layout-end > *:not(:first-child) { + margin-left: 1em; +} +div.dt-container div.dt-layout-full { + width: 100%; +} +div.dt-container div.dt-layout-full > *:only-child { + margin-left: auto; + margin-right: auto; +} +div.dt-container div.dt-layout-table > div { + display: block !important; +} + +@media screen and (max-width: 767px) { + div.dt-container div.dt-layout-start > *:not(:last-child) { + margin-right: 0; + } + div.dt-container div.dt-layout-end > *:not(:first-child) { + margin-left: 0; + } +} +div.dt-container > div.row { margin-bottom: 0.5rem; } -div.dt-container div.row:last-child { +div.dt-container > div.row:last-child { margin-bottom: 0; } div.dt-container div.dt-length label { @@ -398,7 +429,6 @@ div.dt-container div.dt-search input { width: auto; } div.dt-container div.dt-info { - padding-top: 0.85em; white-space: nowrap; } div.dt-container div.dt-paging { diff --git a/plugins/DataTables/datatables.js b/plugins/DataTables/datatables.js index a7b94fb4..0a220fb0 100644 --- a/plugins/DataTables/datatables.js +++ b/plugins/DataTables/datatables.js @@ -4,20 +4,20 @@ * * To rebuild or modify this file with the latest versions of the included * software please visit: - * https://datatables.net/download/#bs4/dt-2.0.8 + * https://datatables.net/download/#bs4/dt-2.1.8 * * Included libraries: - * DataTables 2.0.8 + * DataTables 2.1.8 */ -/*! DataTables 2.0.8 +/*! DataTables 2.1.8 * © SpryMedia Ltd - datatables.net/license */ /** * @summary DataTables * @description Paginate, search and order HTML tables - * @version 2.0.8 + * @version 2.1.8 * @author SpryMedia Ltd * @contact www.datatables.net * @copyright SpryMedia Ltd. @@ -116,7 +116,6 @@ var i=0, iLen; var sId = this.getAttribute( 'id' ); - var bInitHandedOff = false; var defaults = DataTable.defaults; var $this = $(this); @@ -266,6 +265,8 @@ "rowId", "caption", "layout", + "orderDescReverse", + "typeDetect", [ "iCookieDuration", "iStateDuration" ], // backwards compat [ "oSearch", "oPreviousSearch" ], [ "aoSearchCols", "aoPreSearchCols" ], @@ -312,38 +313,14 @@ oSettings._iDisplayStart = oInit.iDisplayStart; } - /* Language definitions */ - var oLanguage = oSettings.oLanguage; - $.extend( true, oLanguage, oInit.oLanguage ); - - if ( oLanguage.sUrl ) + var defer = oInit.iDeferLoading; + if ( defer !== null ) { - /* Get the language definitions from a file - because this Ajax call makes the language - * get async to the remainder of this function we use bInitHandedOff to indicate that - * _fnInitialise will be fired by the returned Ajax handler, rather than the constructor - */ - $.ajax( { - dataType: 'json', - url: oLanguage.sUrl, - success: function ( json ) { - _fnCamelToHungarian( defaults.oLanguage, json ); - $.extend( true, oLanguage, json, oSettings.oInit.oLanguage ); + oSettings.deferLoading = true; - _fnCallbackFire( oSettings, null, 'i18n', [oSettings], true); - _fnInitialise( oSettings ); - }, - error: function () { - // Error occurred loading language file - _fnLog( oSettings, 0, 'i18n file loading error', 21 ); - - // continue on as best we can - _fnInitialise( oSettings ); - } - } ); - bInitHandedOff = true; - } - else { - _fnCallbackFire( oSettings, null, 'i18n', [oSettings]); + var tmp = Array.isArray(defer); + oSettings._iRecordsDisplay = tmp ? defer[0] : defer; + oSettings._iRecordsTotal = tmp ? defer[1] : defer; } /* @@ -410,113 +387,112 @@ } ); } - var features = oSettings.oFeatures; - var loadedInit = function () { - /* - * Sorting - * @todo For modularisation (1.11) this needs to do into a sort start up handler - */ - - // If aaSorting is not defined, then we use the first indicator in asSorting - // in case that has been altered, so the default sort reflects that option - if ( oInit.aaSorting === undefined ) { - var sorting = oSettings.aaSorting; - for ( i=0, iLen=sorting.length ; i').appendTo( $this ); - } - - caption.html( oSettings.caption ); - } - - // Store the caption side, so we can remove the element from the document - // when creating the element - if (caption.length) { - caption[0]._captionSide = caption.css('caption-side'); - oSettings.captionNode = caption[0]; - } - - if ( thead.length === 0 ) { - thead = $('').appendTo($this); - } - oSettings.nTHead = thead[0]; - $('tr', thead).addClass(oClasses.thead.row); - - var tbody = $this.children('tbody'); - if ( tbody.length === 0 ) { - tbody = $('').insertAfter(thead); - } - oSettings.nTBody = tbody[0]; - - var tfoot = $this.children('tfoot'); - if ( tfoot.length === 0 ) { - // If we are a scrolling table, and no footer has been given, then we need to create - // a tfoot element for the caption element to be appended to - tfoot = $('').appendTo($this); - } - oSettings.nTFoot = tfoot[0]; - $('tr', tfoot).addClass(oClasses.tfoot.row); - - // Check if there is data passing into the constructor - if ( oInit.aaData ) { - for ( i=0 ; i').appendTo( $this ); + } + + caption.html( oSettings.caption ); + } + + // Store the caption side, so we can remove the element from the document + // when creating the element + if (caption.length) { + caption[0]._captionSide = caption.css('caption-side'); + oSettings.captionNode = caption[0]; + } + + if ( thead.length === 0 ) { + thead = $('').appendTo($this); + } + oSettings.nTHead = thead[0]; + $('tr', thead).addClass(oClasses.thead.row); + + var tbody = $this.children('tbody'); + if ( tbody.length === 0 ) { + tbody = $('').insertAfter(thead); + } + oSettings.nTBody = tbody[0]; + + var tfoot = $this.children('tfoot'); + if ( tfoot.length === 0 ) { + // If we are a scrolling table, and no footer has been given, then we need to create + // a tfoot element for the caption element to be appended to + tfoot = $('').appendTo($this); + } + oSettings.nTFoot = tfoot[0]; + $('tr', tfoot).addClass(oClasses.tfoot.row); + + // Copy the data index array + oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); + + // Initialisation complete - table can be drawn + oSettings.bInitialised = true; + + // Language definitions + var oLanguage = oSettings.oLanguage; + $.extend( true, oLanguage, oInit.oLanguage ); + + if ( oLanguage.sUrl ) { + // Get the language definitions from a file + $.ajax( { + dataType: 'json', + url: oLanguage.sUrl, + success: function ( json ) { + _fnCamelToHungarian( defaults.oLanguage, json ); + $.extend( true, oLanguage, json, oSettings.oInit.oLanguage ); + + _fnCallbackFire( oSettings, null, 'i18n', [oSettings], true); + _fnInitialise( oSettings ); + }, + error: function () { + // Error occurred loading language file + _fnLog( oSettings, 0, 'i18n file loading error', 21 ); + + // Continue on as best we can + _fnInitialise( oSettings ); + } + } ); + } + else { + _fnCallbackFire( oSettings, null, 'i18n', [oSettings], true); + _fnInitialise( oSettings ); + } } ); _that = null; return this; @@ -563,7 +539,7 @@ * * @type string */ - builder: "bs4/dt-2.0.8", + builder: "bs4/dt-2.1.8", /** @@ -1033,6 +1009,15 @@ info: { container: 'dt-info' }, + layout: { + row: 'dt-layout-row', + cell: 'dt-layout-cell', + tableRow: 'dt-layout-table', + tableCell: '', + start: 'dt-layout-start', + end: 'dt-layout-end', + full: 'dt-layout-full' + }, length: { container: 'dt-length', select: 'dt-input' @@ -1081,7 +1066,8 @@ active: 'current', button: 'dt-paging-button', container: 'dt-paging', - disabled: 'disabled' + disabled: 'disabled', + nav: '' } } ); @@ -1156,7 +1142,7 @@ }; - var _isNumber = function ( d, decimalPoint, formatted ) { + var _isNumber = function ( d, decimalPoint, formatted, allowEmpty ) { var type = typeof d; var strType = type === 'string'; @@ -1167,7 +1153,7 @@ // If empty return immediately so there must be a number if it is a // formatted string (this stops the string "k", or "kr", etc being detected // as a formatted number for currency - if ( _empty( d ) ) { + if ( allowEmpty && _empty( d ) ) { return true; } @@ -1189,8 +1175,8 @@ }; // Is a string a number surrounded by HTML? - var _htmlNumeric = function ( d, decimalPoint, formatted ) { - if ( _empty( d ) ) { + var _htmlNumeric = function ( d, decimalPoint, formatted, allowEmpty ) { + if ( allowEmpty && _empty( d ) ) { return true; } @@ -1202,7 +1188,7 @@ var html = _isHtml( d ); return ! html ? null : - _isNumber( _stripHtml( d ), decimalPoint, formatted ) ? + _isNumber( _stripHtml( d ), decimalPoint, formatted, allowEmpty ) ? true : null; }; @@ -1244,7 +1230,7 @@ // is essential here if ( prop2 !== undefined ) { for ( ; i _max_str_len) { throw new Error('Exceeded max str len'); @@ -1340,8 +1330,11 @@ } // It is faster to just run `normalize` than it is to check if - // we need to with a regex! - var res = str.normalize("NFD"); + // we need to with a regex! (Check as it isn't available in old + // Safari) + var res = str.normalize + ? str.normalize("NFD") + : str; // Equally, here we check if a regex is needed or not return res.length !== str.length @@ -2264,6 +2257,21 @@ return a; } + /** + * Allow the result from a type detection function to be `true` while + * translating that into a string. Old type detection functions will + * return the type name if it passes. An obect store would be better, + * but not backwards compatible. + * + * @param {*} typeDetect Object or function for type detection + * @param {*} res Result from the type detection function + * @returns Type name or false + */ + function _typeResult (typeDetect, res) { + return res === true + ? typeDetect._name + : res; + } /** * Calculate the 'type' of a column @@ -2278,7 +2286,7 @@ var i, ien, j, jen, k, ken; var col, detectedType, cache; - // For each column, spin over the + // For each column, spin over the data type detection functions, seeing if one matches for ( i=0, ien=columns.length ; i col is set to and correct if needed - for (var i=0 ; i col is set to and correct if needed + for (var i=0 ; i 0 ? idx : null; + } + + // `:visible` on its own + return idx; } ); case 'name': @@ -9215,23 +9404,60 @@ } ); /** - * Set the jQuery or window object to be used by DataTables - * - * @param {*} module Library / container object - * @param {string} [type] Library or container type `lib`, `win` or `datetime`. - * If not provided, automatic detection is attempted. + * Set the libraries that DataTables uses, or the global objects. + * Note that the arguments can be either way around (legacy support) + * and the second is optional. See docs. */ - DataTable.use = function (module, type) { - if (type === 'lib' || module.fn) { + DataTable.use = function (arg1, arg2) { + // Reverse arguments for legacy support + var module = typeof arg1 === 'string' + ? arg2 + : arg1; + var type = typeof arg2 === 'string' + ? arg2 + : arg1; + + // Getter + if (module === undefined && typeof type === 'string') { + switch (type) { + case 'lib': + case 'jq': + return $; + + case 'win': + return window; + + case 'datetime': + return DataTable.DateTime; + + case 'luxon': + return __luxon; + + case 'moment': + return __moment; + + default: + return null; + } + } + + // Setter + if (type === 'lib' || type === 'jq' || (module && module.fn && module.fn.jquery)) { $ = module; } - else if (type == 'win' || module.document) { + else if (type == 'win' || (module && module.document)) { window = module; document = module.document; } - else if (type === 'datetime' || module.type === 'DateTime') { + else if (type === 'datetime' || (module && module.type === 'DateTime')) { DataTable.DateTime = module; } + else if (type === 'luxon' || (module && module.FixedOffsetZone)) { + __luxon = module; + } + else if (type === 'moment' || (module && module.isMoment)) { + __moment = module; + } } /** @@ -9487,7 +9713,7 @@ fn.call(this); } else { - this.on('init', function () { + this.on('init.dt.DT', function () { fn.call(this); }); } @@ -9640,7 +9866,7 @@ * @type string * @default Version number */ - DataTable.version = "2.0.8"; + DataTable.version = "2.1.8"; /** * Private data store, containing all of the settings objects that are @@ -10485,7 +10711,8 @@ first: 'First', last: 'Last', next: 'Next', - previous: 'Previous' + previous: 'Previous', + number: '' } }, @@ -10665,6 +10892,10 @@ }, + /** The initial data order is reversed when `desc` ordering */ + orderDescReverse: true, + + /** * This parameter allows you to have define the global filtering state at * initialisation time. As an object the `search` parameter must be @@ -10713,7 +10944,7 @@ * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus page numbers * * `first_last_numbers` - 'First' and 'Last' buttons, plus page numbers */ - "sPaginationType": "full_numbers", + "sPaginationType": "", /** @@ -10783,7 +11014,13 @@ /** * Caption value */ - "caption": null + "caption": null, + + + /** + * For server-side processing - use the data from the DOM for the first draw + */ + iDeferLoading: null }; _fnHungarianMap( DataTable.defaults ); @@ -11726,7 +11963,13 @@ captionNode: null, - colgroup: null + colgroup: null, + + /** Delay loading of data */ + deferLoading: null, + + /** Allow auto type detection */ + typeDetect: true }; /** @@ -11750,7 +11993,7 @@ }, full: function () { - return [ 'first', 'previous', 'next', 'last' ]; + return [ 'first', 'previous', 'next', 'last' ]; }, numbers: function () { @@ -11764,11 +12007,11 @@ full_numbers: function () { return [ 'first', 'previous', 'numbers', 'next', 'last' ]; }, - + first_last: function () { return ['first', 'last']; }, - + first_last_numbers: function () { return ['first', 'numbers', 'last']; }, @@ -11850,38 +12093,56 @@ * to make working with DataTables a little bit easier. */ - function __mldFnName(name) { - return name.replace(/[\W]/g, '_') - } - - // Common logic for moment, luxon or a date action - function __mld( dt, momentFn, luxonFn, dateFn, arg1 ) { - if (window.moment) { - return dt[momentFn]( arg1 ); + /** + * Common logic for moment, luxon or a date action. + * + * Happens after __mldObj, so don't need to call `resolveWindowsLibs` again + */ + function __mld( dtLib, momentFn, luxonFn, dateFn, arg1 ) { + if (__moment) { + return dtLib[momentFn]( arg1 ); } - else if (window.luxon) { - return dt[luxonFn]( arg1 ); + else if (__luxon) { + return dtLib[luxonFn]( arg1 ); } - return dateFn ? dt[dateFn]( arg1 ) : dt; + return dateFn ? dtLib[dateFn]( arg1 ) : dtLib; } var __mlWarning = false; + var __luxon; // Can be assigned in DateTeble.use() + var __moment; // Can be assigned in DateTeble.use() + + /** + * + */ + function resolveWindowLibs() { + if (window.luxon && ! __luxon) { + __luxon = window.luxon; + } + + if (window.moment && ! __moment) { + __moment = window.moment; + } + } + function __mldObj (d, format, locale) { var dt; - if (window.moment) { - dt = window.moment.utc( d, format, locale, true ); + resolveWindowLibs(); + + if (__moment) { + dt = __moment.utc( d, format, locale, true ); if (! dt.isValid()) { return null; } } - else if (window.luxon) { + else if (__luxon) { dt = format && typeof d === 'string' - ? window.luxon.DateTime.fromFormat( d, format ) - : window.luxon.DateTime.fromISO( d ); + ? __luxon.DateTime.fromFormat( d, format ) + : __luxon.DateTime.fromISO( d ); if (! dt.isValid) { return null; @@ -11926,11 +12187,11 @@ from = null; } - var typeName = 'datetime' + (to ? '-' + __mldFnName(to) : ''); + var typeName = 'datetime' + (to ? '-' + to : ''); // Add type detection and sorting specific to this date format - we need to be able to identify // date type columns as such, rather than as numbers in extensions. Hence the need for this. - if (! DataTable.ext.type.order[typeName]) { + if (! DataTable.ext.type.order[typeName + '-pre']) { DataTable.type(typeName, { detect: function (d) { // The renderer will give the value to type detect as the type! @@ -12029,7 +12290,7 @@ // Formatted date time detection - use by declaring the formats you are going to use DataTable.datetime = function ( format, locale ) { - var typeName = 'datetime-detect-' + __mldFnName(format); + var typeName = 'datetime-' + format; if (! locale) { locale = 'en'; @@ -12169,7 +12430,7 @@ return { className: _extTypes.className[name], detect: _extTypes.detect.find(function (fn) { - return fn.name === name; + return fn._name === name; }), order: { pre: _extTypes.order[name + '-pre'], @@ -12184,27 +12445,20 @@ var setProp = function(prop, propVal) { _extTypes[prop][name] = propVal; }; - var setDetect = function (fn) { - // Wrap to allow the function to return `true` rather than - // specifying the type name. - var cb = function (d, s) { - var ret = fn(d, s); + var setDetect = function (detect) { + // `detect` can be a function or an object - we set a name + // property for either - that is used for the detection + Object.defineProperty(detect, "_name", {value: name}); - return ret === true - ? name - : ret; - }; - Object.defineProperty(cb, "name", {value: name}); - - var idx = _extTypes.detect.findIndex(function (fn) { - return fn.name === name; + var idx = _extTypes.detect.findIndex(function (item) { + return item._name === name; }); if (idx === -1) { - _extTypes.detect.unshift(cb); + _extTypes.detect.unshift(detect); } else { - _extTypes.detect.splice(idx, 1, cb); + _extTypes.detect.splice(idx, 1, detect); } }; var setOrder = function (obj) { @@ -12260,10 +12514,23 @@ // Get a list of types DataTable.types = function () { return _extTypes.detect.map(function (fn) { - return fn.name; + return fn._name; }); }; + var __diacriticSort = function (a, b) { + a = a !== null && a !== undefined ? a.toString().toLowerCase() : ''; + b = b !== null && b !== undefined ? b.toString().toLowerCase() : ''; + + // Checked for `navigator.languages` support in `oneOf` so this code can't execute in old + // Safari and thus can disable this check + // eslint-disable-next-line compat/compat + return a.localeCompare(b, navigator.languages[0] || navigator.language, { + numeric: true, + ignorePunctuation: true, + }); + } + // // Built in data types // @@ -12276,7 +12543,7 @@ pre: function ( a ) { // This is a little complex, but faster than always calling toString, // http://jsperf.com/tostring-v-check - return _empty(a) ? + return _empty(a) && typeof a !== 'boolean' ? '' : typeof a === 'string' ? a.toLowerCase() : @@ -12288,11 +12555,38 @@ search: _filterString(false, true) }); + DataTable.type('string-utf8', { + detect: { + allOf: function ( d ) { + return true; + }, + oneOf: function ( d ) { + // At least one data point must contain a non-ASCII character + // This line will also check if navigator.languages is supported or not. If not (Safari 10.0-) + // this data type won't be supported. + // eslint-disable-next-line compat/compat + return ! _empty( d ) && navigator.languages && typeof d === 'string' && d.match(/[^\x00-\x7F]/); + } + }, + order: { + asc: __diacriticSort, + desc: function (a, b) { + return __diacriticSort(a, b) * -1; + } + }, + search: _filterString(false, true) + }); + DataTable.type('html', { - detect: function ( d ) { - return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1) ? - 'html' : null; + detect: { + allOf: function ( d ) { + return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1); + }, + oneOf: function ( d ) { + // At least one data point must contain a `<` + return ! _empty( d ) && typeof d === 'string' && d.indexOf('<') !== -1; + } }, order: { pre: function ( a ) { @@ -12309,16 +12603,21 @@ DataTable.type('date', { className: 'dt-type-date', - detect: function ( d ) - { - // V8 tries _very_ hard to make a string passed into `Date.parse()` - // valid, so we need to use a regex to restrict date formats. Use a - // plug-in for anything other than ISO8601 style strings - if ( d && !(d instanceof Date) && ! _re_date.test(d) ) { - return null; + detect: { + allOf: function ( d ) { + // V8 tries _very_ hard to make a string passed into `Date.parse()` + // valid, so we need to use a regex to restrict date formats. Use a + // plug-in for anything other than ISO8601 style strings + if ( d && !(d instanceof Date) && ! _re_date.test(d) ) { + return null; + } + var parsed = Date.parse(d); + return (parsed !== null && !isNaN(parsed)) || _empty(d); + }, + oneOf: function ( d ) { + // At least one entry must be a date or a string with a date + return (d instanceof Date) || (typeof d === 'string' && _re_date.test(d)); } - var parsed = Date.parse(d); - return (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null; }, order: { pre: function ( d ) { @@ -12331,10 +12630,16 @@ DataTable.type('html-num-fmt', { className: 'dt-type-numeric', - detect: function ( d, settings ) - { - var decimal = settings.oLanguage.sDecimal; - return _htmlNumeric( d, decimal, true ) ? 'html-num-fmt' : null; + detect: { + allOf: function ( d, settings ) { + var decimal = settings.oLanguage.sDecimal; + return _htmlNumeric( d, decimal, true, false ); + }, + oneOf: function (d, settings) { + // At least one data point must contain a numeric value + var decimal = settings.oLanguage.sDecimal; + return _htmlNumeric( d, decimal, true, false ); + } }, order: { pre: function ( d, s ) { @@ -12348,10 +12653,16 @@ DataTable.type('html-num', { className: 'dt-type-numeric', - detect: function ( d, settings ) - { - var decimal = settings.oLanguage.sDecimal; - return _htmlNumeric( d, decimal ) ? 'html-num' : null; + detect: { + allOf: function ( d, settings ) { + var decimal = settings.oLanguage.sDecimal; + return _htmlNumeric( d, decimal, false, true ); + }, + oneOf: function (d, settings) { + // At least one data point must contain a numeric value + var decimal = settings.oLanguage.sDecimal; + return _htmlNumeric( d, decimal, false, false ); + } }, order: { pre: function ( d, s ) { @@ -12365,10 +12676,16 @@ DataTable.type('num-fmt', { className: 'dt-type-numeric', - detect: function ( d, settings ) - { - var decimal = settings.oLanguage.sDecimal; - return _isNumber( d, decimal, true ) ? 'num-fmt' : null; + detect: { + allOf: function ( d, settings ) { + var decimal = settings.oLanguage.sDecimal; + return _isNumber( d, decimal, true, true ); + }, + oneOf: function (d, settings) { + // At least one data point must contain a numeric value + var decimal = settings.oLanguage.sDecimal; + return _isNumber( d, decimal, true, false ); + } }, order: { pre: function ( d, s ) { @@ -12381,10 +12698,16 @@ DataTable.type('num', { className: 'dt-type-numeric', - detect: function ( d, settings ) - { - var decimal = settings.oLanguage.sDecimal; - return _isNumber( d, decimal ) ? 'num' : null; + detect: { + allOf: function ( d, settings ) { + var decimal = settings.oLanguage.sDecimal; + return _isNumber( d, decimal, false, true ); + }, + oneOf: function (d, settings) { + // At least one data point must contain a numeric value + var decimal = settings.oLanguage.sDecimal; + return _isNumber( d, decimal, false, false ); + } }, order: { pre: function (d, s) { @@ -12468,11 +12791,18 @@ // `DT` namespace will allow the event to be removed automatically // on destroy, while the `dt` namespaced event is the one we are // listening for - $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting ) { + $(settings.nTable).on( 'order.dt.DT column-visibility.dt.DT', function ( e, ctx ) { if ( settings !== ctx ) { // need to check this this is the host return; // table, not a nested one } + var sorting = ctx.sortDetails; + + if (! sorting) { + return; + } + + var i; var orderClasses = classes.order; var columns = ctx.api.columns( cell ); var col = settings.aoColumns[columns.flatten()[0]]; @@ -12480,9 +12810,7 @@ var ariaType = ''; var indexes = columns.indexes(); var sortDirs = columns.orderable(true).flatten(); - var orderedColumns = ',' + sorting.map( function (val) { - return val.col; - } ).join(',') + ','; + var orderedColumns = _pluck(sorting, 'col'); cell .removeClass( @@ -12492,10 +12820,18 @@ .toggleClass( orderClasses.none, ! orderable ) .toggleClass( orderClasses.canAsc, orderable && sortDirs.includes('asc') ) .toggleClass( orderClasses.canDesc, orderable && sortDirs.includes('desc') ); - - var sortIdx = orderedColumns.indexOf( ',' + indexes.toArray().join(',') + ',' ); - if ( sortIdx !== -1 ) { + // Determine if all of the columns that this cell covers are included in the + // current ordering + var isOrdering = true; + + for (i=0; i') - .addClass('dt-layout-row') + .attr('id', items.id || null) + .addClass(items.className || classes.row) .appendTo( container ); $.each( items, function (key, val) { - var klass = ! val.table ? - 'dt-'+key+' ' : - ''; + if (key === 'id' || key === 'className') { + return; + } + + var klass = ''; if (val.table) { - row.addClass('dt-layout-table'); + row.addClass(classes.tableRow); + klass += classes.tableCell + ' '; + } + + if (key === 'start') { + klass += classes.start; + } + else if (key === 'end') { + klass += classes.end; + } + else { + klass += classes.full; } $('
') .attr({ id: val.id || null, - "class": 'dt-layout-cell '+klass+(val.className || '') + "class": val.className + ? val.className + : classes.cell + ' ' + klass }) .append( val.contents ) .appendTo( row ); @@ -12576,6 +12941,25 @@ } }; + function _divProp(el, prop, val) { + if (val) { + el[prop] = val; + } + } + + DataTable.feature.register( 'div', function ( settings, opts ) { + var n = $('
')[0]; + + if (opts) { + _divProp(n, 'className', opts.className); + _divProp(n, 'id', opts.id); + _divProp(n, 'innerHTML', opts.html); + _divProp(n, 'textContent', opts.text); + } + + return n; + } ); + DataTable.feature.register( 'info', function ( settings, opts ) { // For compatibility with the legacy `info` top level option if (! settings.oFeatures.bInfo) { @@ -12675,6 +13059,7 @@ opts = $.extend({ placeholder: language.sSearchPlaceholder, + processing: false, text: language.sSearch }, opts); @@ -12718,13 +13103,15 @@ /* Now do the filter */ if ( val != previousSearch.search ) { - previousSearch.search = val; - - _fnFilterComplete( settings, previousSearch ); - - // Need to redraw, without resorting - settings._iDisplayStart = 0; - _fnDraw( settings ); + _fnProcessingRun(settings, opts.processing, function () { + previousSearch.search = val; + + _fnFilterComplete( settings, previousSearch ); + + // Need to redraw, without resorting + settings._iDisplayStart = 0; + _fnDraw( settings ); + }); } }; @@ -12782,17 +13169,21 @@ opts = $.extend({ buttons: DataTable.ext.pager.numbers_length, type: settings.sPaginationType, - boundaryNumbers: true + boundaryNumbers: true, + firstLast: true, + previousNext: true, + numbers: true }, opts); - // To be removed in 2.1 - if (opts.numbers) { - opts.buttons = opts.numbers; - } - - var host = $('
').addClass( settings.oClasses.paging.container + ' paging_' + opts.type ); + var host = $('
') + .addClass(settings.oClasses.paging.container + (opts.type ? ' paging_' + opts.type : '')) + .append( + $('