/*! DataTables 2.0.2 * © SpryMedia Ltd - datatables.net/license */ /** * @summary DataTables * @description Paginate, search and order HTML tables * @version 2.0.2 * @author SpryMedia Ltd * @contact www.datatables.net * @copyright SpryMedia Ltd. * * This source file is free software, available under the following license: * MIT license - https://datatables.net/license * * 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: https://www.datatables.net */ (function( factory ) { "use strict"; if ( typeof define === 'function' && define.amd ) { // AMD define( ['jquery'], function ( $ ) { return factory( $, window, document ); } ); } else if ( typeof exports === 'object' ) { // CommonJS // jQuery's factory checks for a global window - if it isn't present then it // returns a factory function that expects the window object var jq = require('jquery'); if (typeof window === 'undefined') { module.exports = function (root, $) { if ( ! root ) { // CommonJS environments without a window global must pass a // root. This will give an error otherwise root = window; } if ( ! $ ) { $ = jq( root ); } return factory( $, root, root.document ); }; } else { module.exports = factory( jq, window, window.document ); } } else { // Browser window.DataTable = factory( jQuery, window, document ); } }(function( $, window, document ) { "use strict"; var DataTable = function ( selector, options ) { // Check if called with a window or jQuery object for DOM less applications // This is for backwards compatibility if (DataTable.factory(selector, options)) { return DataTable; } // When creating with `new`, create a new DataTable, returning the API instance if (this instanceof DataTable) { return $(selector).DataTable(options); } else { // Argument switching options = selector; } var _that = this; var emptyInit = options === undefined; var len = this.length; if ( emptyInit ) { options = {}; } // Method to get DT API instance from jQuery object this.api = function () { return new _Api( this ); }; this.each(function() { // For each initialisation we want to give it a clean initialisation // object that can be bashed around var o = {}; var oInit = len > 1 ? // optimisation for single table case _fnExtend( o, options, true ) : options; var i=0, iLen; var sId = this.getAttribute( 'id' ); var bInitHandedOff = false; var defaults = DataTable.defaults; var $this = $(this); /* Sanity check */ if ( this.nodeName.toLowerCase() != 'table' ) { _fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 ); return; } $(this).trigger( 'options.dt', oInit ); /* Backwards compatibility for the defaults */ _fnCompatOpts( defaults ); _fnCompatCols( defaults.column ); /* Convert the camel-case defaults to Hungarian */ _fnCamelToHungarian( defaults, defaults, true ); _fnCamelToHungarian( defaults.column, defaults.column, true ); /* Setting up the initialisation object */ _fnCamelToHungarian( defaults, $.extend( oInit, $this.data() ), true ); /* Check to see if we are re-initialising a table */ var allSettings = DataTable.settings; for ( i=0, iLen=allSettings.length ; i').prependTo(this), fastData: function (row, column, type) { return _fnGetCellData(oSettings, row, column, type); } } ); oSettings.nTable = this; oSettings.oInit = oInit; allSettings.push( oSettings ); // Make a single API instance available for internal handling oSettings.api = new _Api( oSettings ); // Need to add the instance after the instance after the settings object has been added // to the settings array, so we can self reference the table instance if more than one oSettings.oInstance = (_that.length===1) ? _that : $this.dataTable(); // Backwards compatibility, before we apply all the defaults _fnCompatOpts( oInit ); // If the length menu is given, but the init display length is not, use the length menu if ( oInit.aLengthMenu && ! oInit.iDisplayLength ) { oInit.iDisplayLength = Array.isArray(oInit.aLengthMenu[0]) ? oInit.aLengthMenu[0][0] : $.isPlainObject( oInit.aLengthMenu[0] ) ? oInit.aLengthMenu[0].value : oInit.aLengthMenu[0]; } // Apply the defaults and init options to make a single init object will all // options defined from defaults and instance options. oInit = _fnExtend( $.extend( true, {}, defaults ), oInit ); // Map the initialisation options onto the settings object _fnMap( oSettings.oFeatures, oInit, [ "bPaginate", "bLengthChange", "bFilter", "bSort", "bSortMulti", "bInfo", "bProcessing", "bAutoWidth", "bSortClasses", "bServerSide", "bDeferRender" ] ); _fnMap( oSettings, oInit, [ "ajax", "fnFormatNumber", "sServerMethod", "aaSorting", "aaSortingFixed", "aLengthMenu", "sPaginationType", "iStateDuration", "bSortCellsTop", "iTabIndex", "sDom", "fnStateLoadCallback", "fnStateSaveCallback", "renderer", "searchDelay", "rowId", "caption", "layout", [ "iCookieDuration", "iStateDuration" ], // backwards compat [ "oSearch", "oPreviousSearch" ], [ "aoSearchCols", "aoPreSearchCols" ], [ "iDisplayLength", "_iDisplayLength" ] ] ); _fnMap( oSettings.oScroll, oInit, [ [ "sScrollX", "sX" ], [ "sScrollXInner", "sXInner" ], [ "sScrollY", "sY" ], [ "bScrollCollapse", "bCollapse" ] ] ); _fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" ); /* Callback functions which are array driven */ _fnCallbackReg( oSettings, 'aoDrawCallback', oInit.fnDrawCallback ); _fnCallbackReg( oSettings, 'aoStateSaveParams', oInit.fnStateSaveParams ); _fnCallbackReg( oSettings, 'aoStateLoadParams', oInit.fnStateLoadParams ); _fnCallbackReg( oSettings, 'aoStateLoaded', oInit.fnStateLoaded ); _fnCallbackReg( oSettings, 'aoRowCallback', oInit.fnRowCallback ); _fnCallbackReg( oSettings, 'aoRowCreatedCallback', oInit.fnCreatedRow ); _fnCallbackReg( oSettings, 'aoHeaderCallback', oInit.fnHeaderCallback ); _fnCallbackReg( oSettings, 'aoFooterCallback', oInit.fnFooterCallback ); _fnCallbackReg( oSettings, 'aoInitComplete', oInit.fnInitComplete ); _fnCallbackReg( oSettings, 'aoPreDrawCallback', oInit.fnPreDrawCallback ); oSettings.rowIdFn = _fnGetObjectDataFn( oInit.rowId ); /* Browser support detection */ _fnBrowserDetect( oSettings ); var oClasses = oSettings.oClasses; $.extend( oClasses, DataTable.ext.classes, oInit.oClasses ); $this.addClass( oClasses.table ); if (! oSettings.oFeatures.bPaginate) { oInit.iDisplayStart = 0; } if ( oSettings.iInitDisplayStart === undefined ) { /* Display start point, taking into account the save saving */ oSettings.iInitDisplayStart = oInit.iDisplayStart; oSettings._iDisplayStart = oInit.iDisplayStart; } /* Language definitions */ var oLanguage = oSettings.oLanguage; $.extend( true, oLanguage, oInit.oLanguage ); if ( oLanguage.sUrl ) { /* 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 ); _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]); } /* * Columns * See if we should load columns automatically or use defined ones */ var columnsInit = []; var thead = this.getElementsByTagName('thead'); var initHeaderLayout = _fnDetectHeader( oSettings, thead[0] ); // If we don't have a columns array, then generate one with nulls if ( oInit.aoColumns ) { columnsInit = oInit.aoColumns; } else if ( initHeaderLayout.length ) { for ( i=0, iLen=initHeaderLayout[0].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 ; iafnSortData * for searching data. * * The functions defined take a single parameter: * * 1. `{*}` Data from the column cell to be prepared for searching * * Each function is expected to return: * * * `{string|null}` Formatted string that will be used for the searching. * * @type object * @default {} * * @example * $.fn.dataTable.ext.type.search['title-numeric'] = function ( d ) { * return d.replace(/\n/g," ").replace( /<.*?>/g, "" ); * } */ search: {}, /** * Type based ordering. * * The column type tells DataTables what ordering to apply to the table * when a column is sorted upon. The order for each type that is defined, * is defined by the functions available in this object. * * Each ordering option can be described by three properties added to * this object: * * * `{type}-pre` - Pre-formatting function * * `{type}-asc` - Ascending order function * * `{type}-desc` - Descending order function * * All three can be used together, only `{type}-pre` or only * `{type}-asc` and `{type}-desc` together. It is generally recommended * that only `{type}-pre` is used, as this provides the optimal * implementation in terms of speed, although the others are provided * for compatibility with existing Javascript sort functions. * * `{type}-pre`: Functions defined take a single parameter: * * 1. `{*}` Data from the column cell to be prepared for ordering * * And return: * * * `{*}` Data to be sorted upon * * `{type}-asc` and `{type}-desc`: Functions are typical Javascript sort * functions, taking two parameters: * * 1. `{*}` Data to compare to the second parameter * 2. `{*}` Data to compare to the first parameter * * And returning: * * * `{*}` Ordering match: <0 if first parameter should be sorted lower * than the second parameter, ===0 if the two parameters are equal and * >0 if the first parameter should be sorted height than the second * parameter. * * @type object * @default {} * * @example * // Numeric ordering of formatted numbers with a pre-formatter * $.extend( $.fn.dataTable.ext.type.order, { * "string-pre": function(x) { * a = (a === "-" || a === "") ? 0 : a.replace( /[^\d\-\.]/g, "" ); * return parseFloat( a ); * } * } ); * * @example * // Case-sensitive string ordering, with no pre-formatting method * $.extend( $.fn.dataTable.ext.order, { * "string-case-asc": function(x,y) { * return ((x < y) ? -1 : ((x > y) ? 1 : 0)); * }, * "string-case-desc": function(x,y) { * return ((x < y) ? 1 : ((x > y) ? -1 : 0)); * } * } ); */ order: {} }, /** * Unique DataTables instance counter * * @type int * @private */ _unique: 0, // // Depreciated // The following properties are retained for backwards compatibility only. // The should not be used in new projects and will be removed in a future // version // /** * Version check function. * @type function * @depreciated Since 1.10 */ fnVersionCheck: DataTable.fnVersionCheck, /** * Index for what 'this' index API functions should use * @type int * @deprecated Since v1.10 */ iApiIndex: 0, /** * Software version * @type string * @deprecated Since v1.10 */ sVersion: DataTable.version }; // // Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts // $.extend( _ext, { afnFiltering: _ext.search, aTypes: _ext.type.detect, ofnSearch: _ext.type.search, oSort: _ext.type.order, afnSortData: _ext.order, aoFeatures: _ext.feature, oStdClasses: _ext.classes, oPagination: _ext.pager } ); $.extend( DataTable.ext.classes, { container: 'dt-container', empty: { row: 'dt-empty' }, info: { container: 'dt-info' }, length: { container: 'dt-length', select: 'dt-input' }, order: { canAsc: 'dt-orderable-asc', canDesc: 'dt-orderable-desc', isAsc: 'dt-ordering-asc', isDesc: 'dt-ordering-desc', none: 'dt-orderable-none', position: 'sorting_' }, processing: { container: 'dt-processing' }, scrolling: { body: 'dt-scroll-body', container: 'dt-scroll', footer: { self: 'dt-scroll-foot', inner: 'dt-scroll-footInner' }, header: { self: 'dt-scroll-head', inner: 'dt-scroll-headInner' } }, search: { container: 'dt-search', input: 'dt-input' }, table: 'dataTable', tbody: { cell: '', row: '' }, thead: { cell: '', row: '' }, tfoot: { cell: '', row: '' }, paging: { active: 'current', button: 'dt-paging-button', container: 'dt-paging', disabled: 'disabled' } } ); /* * It is useful to have variables which are scoped locally so only the * DataTables functions can access them and they don't leak into global space. * At the same time these functions are often useful over multiple files in the * core and API, so we list, or at least document, all variables which are used * by DataTables as private variables here. This also ensures that there is no * clashing of variable names and that they can easily referenced for reuse. */ // Defined else where // _selector_run // _selector_opts // _selector_row_indexes var _ext; // DataTable.ext var _Api; // DataTable.Api var _api_register; // DataTable.Api.register var _api_registerPlural; // DataTable.Api.registerPlural var _re_dic = {}; var _re_new_lines = /[\r\n\u2028]/g; var _re_html = /<.*?>/g; // This is not strict ISO8601 - Date.parse() is quite lax, although // implementations differ between browsers. var _re_date = /^\d{2,4}[./-]\d{1,2}[./-]\d{1,2}([T ]{1}\d{1,2}[:.]\d{2}([.:]\d{2})?)?$/; // Escape regular expression special characters var _re_escape_regex = new RegExp( '(\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ].join('|\\') + ')', 'g' ); // https://en.wikipedia.org/wiki/Foreign_exchange_market // - \u20BD - Russian ruble. // - \u20a9 - South Korean Won // - \u20BA - Turkish Lira // - \u20B9 - Indian Rupee // - R - Brazil (R$) and South Africa // - fr - Swiss Franc // - kr - Swedish krona, Norwegian krone and Danish krone // - \u2009 is thin space and \u202F is narrow no-break space, both used in many // - Ƀ - Bitcoin // - Ξ - Ethereum // standards as thousands separators. var _re_formatted_numeric = /['\u00A0,$£€¥%\u2009\u202F\u20BD\u20a9\u20BArfkɃΞ]/gi; var _empty = function ( d ) { return !d || d === true || d === '-' ? true : false; }; var _intVal = function ( s ) { var integer = parseInt( s, 10 ); return !isNaN(integer) && isFinite(s) ? integer : null; }; // Convert from a formatted number with characters other than `.` as the // decimal place, to a Javascript number var _numToDecimal = function ( num, decimalPoint ) { // Cache created regular expressions for speed as this function is called often if ( ! _re_dic[ decimalPoint ] ) { _re_dic[ decimalPoint ] = new RegExp( _fnEscapeRegex( decimalPoint ), 'g' ); } return typeof num === 'string' && decimalPoint !== '.' ? num.replace( /\./g, '' ).replace( _re_dic[ decimalPoint ], '.' ) : num; }; var _isNumber = function ( d, decimalPoint, formatted ) { var type = typeof d; var strType = type === 'string'; if ( type === 'number' || type === 'bigint') { return true; } // 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 ) ) { return true; } if ( decimalPoint && strType ) { d = _numToDecimal( d, decimalPoint ); } if ( formatted && strType ) { d = d.replace( _re_formatted_numeric, '' ); } return !isNaN( parseFloat(d) ) && isFinite( d ); }; // A string without HTML in it can be considered to be HTML still var _isHtml = function ( d ) { return _empty( d ) || typeof d === 'string'; }; // Is a string a number surrounded by HTML? var _htmlNumeric = function ( d, decimalPoint, formatted ) { if ( _empty( d ) ) { return true; } // input and select strings mean that this isn't just a number if (typeof d === 'string' && d.match(/<(input|select)/i)) { return null; } var html = _isHtml( d ); return ! html ? null : _isNumber( _stripHtml( d ), decimalPoint, formatted ) ? true : null; }; var _pluck = function ( a, prop, prop2 ) { var out = []; var i=0, ien=a.length; // Could have the test in the loop for slightly smaller code, but speed // is essential here if ( prop2 !== undefined ) { for ( ; i/g, '>') .replace(/"/g, '"') : d; }; // Remove diacritics from a string by decomposing it and then removing // non-ascii characters var _normalize = function (str, both) { if (typeof str !== 'string') { return str; } // It is faster to just run `normalize` than it is to check if // we need to with a regex! var res = str.normalize("NFD"); // Equally, here we check if a regex is needed or not return res.length !== str.length ? (both === true ? str + ' ' : '' ) + res.replace(/[\u0300-\u036f]/g, "") : res; } /** * Determine if all values in the array are unique. This means we can short * cut the _unique method at the cost of a single loop. A sorted array is used * to easily check the values. * * @param {array} src Source array * @return {boolean} true if all unique, false otherwise * @ignore */ var _areAllUnique = function ( src ) { if ( src.length < 2 ) { return true; } var sorted = src.slice().sort(); var last = sorted[0]; for ( var i=1, ien=sorted.length ; i') .css( { position: 'fixed', top: 0, left: -1 * window.pageXOffset, // allow for scrolling height: 1, width: 1, overflow: 'hidden' } ) .append( $('
') .css( { position: 'absolute', top: 1, left: 1, width: 100, overflow: 'scroll' } ) .append( $('
') .css( { width: '100%', height: 10 } ) ) ) .appendTo( 'body' ); var outer = n.children(); var inner = outer.children(); // Get scrollbar width browser.barWidth = outer[0].offsetWidth - outer[0].clientWidth; // In rtl text layout, some browsers (most, but not all) will place the // scrollbar on the left, rather than the right. browser.bScrollbarLeft = Math.round( inner.offset().left ) !== 1; n.remove(); } $.extend( settings.oBrowser, DataTable.__browser ); settings.oScroll.iBarWidth = DataTable.__browser.barWidth; } /** * Add a column to the list used for the table with default values * @param {object} oSettings dataTables settings object * @memberof DataTable#oApi */ function _fnAddColumn( oSettings ) { // Add column to aoColumns array var oDefaults = DataTable.defaults.column; var iCol = oSettings.aoColumns.length; var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, { "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol], "mData": oDefaults.mData ? oDefaults.mData : iCol, idx: iCol, searchFixed: {}, colEl: $('') } ); oSettings.aoColumns.push( oCol ); // Add search object for column specific search. Note that the `searchCols[ iCol ]` // passed into extend can be undefined. This allows the user to give a default // with only some of the parameters defined, and also not give a default var searchCols = oSettings.aoPreSearchCols; searchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch, searchCols[ iCol ] ); } /** * Apply options for a column * @param {object} oSettings dataTables settings object * @param {int} iCol column index to consider * @param {object} oOptions object with sType, bVisible and bSearchable etc * @memberof DataTable#oApi */ function _fnColumnOptions( oSettings, iCol, oOptions ) { var oCol = oSettings.aoColumns[ iCol ]; /* User specified column options */ if ( oOptions !== undefined && oOptions !== null ) { // Backwards compatibility _fnCompatCols( oOptions ); // Map camel case parameters to their Hungarian counterparts _fnCamelToHungarian( DataTable.defaults.column, oOptions, true ); /* Backwards compatibility for mDataProp */ if ( oOptions.mDataProp !== undefined && !oOptions.mData ) { oOptions.mData = oOptions.mDataProp; } if ( oOptions.sType ) { oCol._sManualType = oOptions.sType; } // `class` is a reserved word in Javascript, so we need to provide // the ability to use a valid name for the camel case input if ( oOptions.className && ! oOptions.sClass ) { oOptions.sClass = oOptions.className; } var origClass = oCol.sClass; $.extend( oCol, oOptions ); _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" ); // Merge class from previously defined classes with this one, rather than just // overwriting it in the extend above if (origClass !== oCol.sClass) { oCol.sClass = origClass + ' ' + oCol.sClass; } /* iDataSort to be applied (backwards compatibility), but aDataSort will take * priority if defined */ if ( oOptions.iDataSort !== undefined ) { oCol.aDataSort = [ oOptions.iDataSort ]; } _fnMap( oCol, oOptions, "aDataSort" ); } /* Cache the data get and set functions for speed */ var mDataSrc = oCol.mData; var mData = _fnGetObjectDataFn( mDataSrc ); // The `render` option can be given as an array to access the helper rendering methods. // The first element is the rendering method to use, the rest are the parameters to pass if ( oCol.mRender && Array.isArray( oCol.mRender ) ) { var copy = oCol.mRender.slice(); var name = copy.shift(); oCol.mRender = DataTable.render[name].apply(window, copy); } oCol._render = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null; var attrTest = function( src ) { return typeof src === 'string' && src.indexOf('@') !== -1; }; oCol._bAttrSrc = $.isPlainObject( mDataSrc ) && ( attrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter) ); oCol._setter = null; oCol.fnGetData = function (rowData, type, meta) { var innerData = mData( rowData, type, undefined, meta ); return oCol._render && type ? oCol._render( innerData, type, rowData, meta ) : innerData; }; oCol.fnSetData = function ( rowData, val, meta ) { return _fnSetObjectDataFn( mDataSrc )( rowData, val, meta ); }; // Indicate if DataTables should read DOM data as an object or array // Used in _fnGetRowElements if ( typeof mDataSrc !== 'number' && ! oCol._isArrayHost ) { oSettings._rowReadObject = true; } /* Feature sorting overrides column specific when off */ if ( !oSettings.oFeatures.bSort ) { oCol.bSortable = false; } } /** * Adjust the table column widths for new data. Note: you would probably want to * do a redraw after calling this function! * @param {object} settings dataTables settings object * @memberof DataTable#oApi */ function _fnAdjustColumnSizing ( settings ) { _fnCalculateColumnWidths( settings ); _fnColumnSizes( settings ); var scroll = settings.oScroll; if ( scroll.sY !== '' || scroll.sX !== '') { _fnScrollDraw( settings ); } _fnCallbackFire( settings, null, 'column-sizing', [settings] ); } /** * Apply column sizes * * @param {*} settings DataTables settings object */ function _fnColumnSizes ( settings ) { var cols = settings.aoColumns; for (var i=0 ; i