2 * This combined file was created by the DataTables downloader builder:
3 * https://datatables.net/download
5 * To rebuild or modify this file with the latest versions of the included
6 * software please visit:
7 * https://datatables.net/download/#dt/dt-1.10.21/fh-3.1.7/sp-1.1.1/sl-1.3.1
10 * DataTables 1.10.21, FixedHeader 3.1.7, SearchPanes 1.1.1, Select 1.3.1
13 /*! DataTables 1.10.21
14 * ©2008-2020 SpryMedia Ltd - datatables.net/license
19 * @description Paginate, search and order HTML tables
21 * @file jquery.dataTables.js
22 * @author SpryMedia Ltd
23 * @contact www.datatables.net
24 * @copyright Copyright 2008-2020 SpryMedia Ltd.
26 * This source file is free software, available under the following license:
27 * MIT license - http://datatables.net/license
29 * This source file is distributed in the hope that it will be useful, but
30 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
31 * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
33 * For details please refer to: http://www.datatables.net
36 /*jslint evil: true, undef: true, browser: true */
37 /*globals $,require,jQuery,define,_selector_run,_selector_opts,_selector_first,_selector_row_indexes,_ext,_Api,_api_register,_api_registerPlural,_re_new_lines,_re_html,_re_formatted_numeric,_re_escape_regex,_empty,_intVal,_numToDecimal,_isNumber,_isHtml,_htmlNumeric,_pluck,_pluck_order,_range,_stripHtml,_unique,_fnBuildAjax,_fnAjaxUpdate,_fnAjaxParameters,_fnAjaxUpdateDraw,_fnAjaxDataSrc,_fnAddColumn,_fnColumnOptions,_fnAdjustColumnSizing,_fnVisibleToColumnIndex,_fnColumnIndexToVisible,_fnVisbleColumns,_fnGetColumns,_fnColumnTypes,_fnApplyColumnDefs,_fnHungarianMap,_fnCamelToHungarian,_fnLanguageCompat,_fnBrowserDetect,_fnAddData,_fnAddTr,_fnNodeToDataIndex,_fnNodeToColumnIndex,_fnGetCellData,_fnSetCellData,_fnSplitObjNotation,_fnGetObjectDataFn,_fnSetObjectDataFn,_fnGetDataMaster,_fnClearTable,_fnDeleteIndex,_fnInvalidate,_fnGetRowElements,_fnCreateTr,_fnBuildHead,_fnDrawHead,_fnDraw,_fnReDraw,_fnAddOptionsHtml,_fnDetectHeader,_fnGetUniqueThs,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterColumn,_fnFilter,_fnFilterCreateSearch,_fnEscapeRegex,_fnFilterData,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnInfoMacros,_fnInitialise,_fnInitComplete,_fnLengthChange,_fnFeatureHtmlLength,_fnFeatureHtmlPaginate,_fnPageChange,_fnFeatureHtmlProcessing,_fnProcessingDisplay,_fnFeatureHtmlTable,_fnScrollDraw,_fnApplyToChildren,_fnCalculateColumnWidths,_fnThrottle,_fnConvertToWidth,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnSortFlatten,_fnSort,_fnSortAria,_fnSortListener,_fnSortAttachListener,_fnSortingClasses,_fnSortData,_fnSaveState,_fnLoadState,_fnSettingsFromNode,_fnLog,_fnMap,_fnBindAction,_fnCallbackReg,_fnCallbackFire,_fnLengthOverflow,_fnRenderer,_fnDataSource,_fnRowAttributes*/
39 (function( factory ) {
42 if ( typeof define === 'function' && define.amd ) {
44 define( ['jquery'], function ( $ ) {
45 return factory( $, window, document );
48 else if ( typeof exports === 'object' ) {
50 module.exports = function (root, $) {
52 // CommonJS environments without a window global must pass a
53 // root. This will give an error otherwise
58 $ = typeof window !== 'undefined' ? // jQuery's factory checks for a global window
60 require('jquery')( root );
63 return factory( $, root, root.document );
68 factory( jQuery, window, document );
71 (function( $, window, document, undefined ) {
75 * DataTables is a plug-in for the jQuery Javascript library. It is a highly
76 * flexible tool, based upon the foundations of progressive enhancement,
77 * which will add advanced interaction controls to any HTML table. For a
78 * full list of features please refer to
79 * [DataTables.net](href="http://datatables.net).
81 * Note that the `DataTable` object is not a global variable but is aliased
82 * to `jQuery.fn.DataTable` and `jQuery.fn.dataTable` through which it may
86 * @param {object} [init={}] Configuration object for DataTables. Options
87 * are defined by {@link DataTable.defaults}
88 * @requires jQuery 1.7+
91 * // Basic initialisation
92 * $(document).ready( function {
93 * $('#example').dataTable();
97 * // Initialisation with configuration options - in this case, disable
98 * // pagination and sorting.
99 * $(document).ready( function {
100 * $('#example').dataTable( {
106 var DataTable = function ( options )
109 * Perform a jQuery selector action on the table's TR elements (from the tbody) and
110 * return the resulting jQuery object.
111 * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on
112 * @param {object} [oOpts] Optional parameters for modifying the rows to be included
113 * @param {string} [oOpts.filter=none] Select TR elements that meet the current filter
114 * criterion ("applied") or all TR elements (i.e. no filter).
115 * @param {string} [oOpts.order=current] Order of the TR elements in the processed array.
116 * Can be either 'current', whereby the current sorting of the table is used, or
117 * 'original' whereby the original order the data was read into the table is used.
118 * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page
119 * ("current") or not ("all"). If 'current' is given, then order is assumed to be
120 * 'current' and filter is 'applied', regardless of what they might be given as.
121 * @returns {object} jQuery object, filtered by the given selector.
123 * @deprecated Since v1.10
126 * $(document).ready(function() {
127 * var oTable = $('#example').dataTable();
129 * // Highlight every second row
130 * oTable.$('tr:odd').css('backgroundColor', 'blue');
134 * $(document).ready(function() {
135 * var oTable = $('#example').dataTable();
137 * // Filter to rows with 'Webkit' in them, add a background colour and then
138 * // remove the filter, thus highlighting the 'Webkit' rows only.
139 * oTable.fnFilter('Webkit');
140 * oTable.$('tr', {"search": "applied"}).css('backgroundColor', 'blue');
141 * oTable.fnFilter('');
144 this.$ = function ( sSelector, oOpts )
146 return this.api(true).$( sSelector, oOpts );
151 * Almost identical to $ in operation, but in this case returns the data for the matched
152 * rows - as such, the jQuery selector used should match TR row nodes or TD/TH cell nodes
153 * rather than any descendants, so the data can be obtained for the row/cell. If matching
154 * rows are found, the data returned is the original data array/object that was used to
155 * create the row (or a generated array if from a DOM source).
157 * This method is often useful in-combination with $ where both functions are given the
158 * same parameters and the array indexes will match identically.
159 * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on
160 * @param {object} [oOpts] Optional parameters for modifying the rows to be included
161 * @param {string} [oOpts.filter=none] Select elements that meet the current filter
162 * criterion ("applied") or all elements (i.e. no filter).
163 * @param {string} [oOpts.order=current] Order of the data in the processed array.
164 * Can be either 'current', whereby the current sorting of the table is used, or
165 * 'original' whereby the original order the data was read into the table is used.
166 * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page
167 * ("current") or not ("all"). If 'current' is given, then order is assumed to be
168 * 'current' and filter is 'applied', regardless of what they might be given as.
169 * @returns {array} Data for the matched elements. If any elements, as a result of the
170 * selector, were not TR, TD or TH elements in the DataTable, they will have a null
171 * entry in the array.
173 * @deprecated Since v1.10
176 * $(document).ready(function() {
177 * var oTable = $('#example').dataTable();
179 * // Get the data from the first row in the table
180 * var data = oTable._('tr:first');
182 * // Do something useful with the data
183 * alert( "First cell is: "+data[0] );
187 * $(document).ready(function() {
188 * var oTable = $('#example').dataTable();
190 * // Filter to 'Webkit' and get all data for
191 * oTable.fnFilter('Webkit');
192 * var data = oTable._('tr', {"search": "applied"});
194 * // Do something with the data
195 * alert( data.length+" rows matched the search" );
198 this._ = function ( sSelector, oOpts )
200 return this.api(true).rows( sSelector, oOpts ).data();
205 * Create a DataTables Api instance, with the currently selected tables for
207 * @param {boolean} [traditional=false] Set the API instance's context to be
208 * only the table referred to by the `DataTable.ext.iApiIndex` option, as was
209 * used in the API presented by DataTables 1.9- (i.e. the traditional mode),
210 * or if all tables captured in the jQuery object should be used.
211 * @return {DataTables.Api}
213 this.api = function ( traditional )
217 _fnSettingsFromNode( this[ _ext.iApiIndex ] )
224 * Add a single new row or multiple rows of data to the table. Please note
225 * that this is suitable for client-side processing only - if you are using
226 * server-side processing (i.e. "bServerSide": true), then to add data, you
227 * must add it to the data source, i.e. the server-side, through an Ajax call.
228 * @param {array|object} data The data to be added to the table. This can be:
230 * <li>1D array of data - add a single row with the data provided</li>
231 * <li>2D array of arrays - add multiple rows in a single call</li>
232 * <li>object - data object when using <i>mData</i></li>
233 * <li>array of objects - multiple data objects when using <i>mData</i></li>
235 * @param {bool} [redraw=true] redraw the table or not
236 * @returns {array} An array of integers, representing the list of indexes in
237 * <i>aoData</i> ({@link DataTable.models.oSettings}) that have been added to
240 * @deprecated Since v1.10
243 * // Global var for counter
246 * $(document).ready(function() {
247 * $('#example').dataTable();
250 * function fnClickAddRow() {
251 * $('#example').dataTable().fnAddData( [
261 this.fnAddData = function( data, redraw )
263 var api = this.api( true );
265 /* Check if we want to add multiple rows or not */
266 var rows = $.isArray(data) && ( $.isArray(data[0]) || $.isPlainObject(data[0]) ) ?
267 api.rows.add( data ) :
270 if ( redraw === undefined || redraw ) {
274 return rows.flatten().toArray();
279 * This function will make DataTables recalculate the column sizes, based on the data
280 * contained in the table and the sizes applied to the columns (in the DOM, CSS or
281 * through the sWidth parameter). This can be useful when the width of the table's
282 * parent element changes (for example a window resize).
283 * @param {boolean} [bRedraw=true] Redraw the table or not, you will typically want to
285 * @deprecated Since v1.10
288 * $(document).ready(function() {
289 * var oTable = $('#example').dataTable( {
290 * "sScrollY": "200px",
294 * $(window).on('resize', function () {
295 * oTable.fnAdjustColumnSizing();
299 this.fnAdjustColumnSizing = function ( bRedraw )
301 var api = this.api( true ).columns.adjust();
302 var settings = api.settings()[0];
303 var scroll = settings.oScroll;
305 if ( bRedraw === undefined || bRedraw ) {
308 else if ( scroll.sX !== "" || scroll.sY !== "" ) {
309 /* If not redrawing, but scrolling, we want to apply the new column sizes anyway */
310 _fnScrollDraw( settings );
316 * Quickly and simply clear a table
317 * @param {bool} [bRedraw=true] redraw the table or not
319 * @deprecated Since v1.10
322 * $(document).ready(function() {
323 * var oTable = $('#example').dataTable();
325 * // Immediately 'nuke' the current rows (perhaps waiting for an Ajax callback...)
326 * oTable.fnClearTable();
329 this.fnClearTable = function( bRedraw )
331 var api = this.api( true ).clear();
333 if ( bRedraw === undefined || bRedraw ) {
340 * The exact opposite of 'opening' a row, this function will close any rows which
341 * are currently 'open'.
342 * @param {node} nTr the table row to 'close'
343 * @returns {int} 0 on success, or 1 if failed (can't find the row)
345 * @deprecated Since v1.10
348 * $(document).ready(function() {
351 * // 'open' an information row when a row is clicked on
352 * $('#example tbody tr').click( function () {
353 * if ( oTable.fnIsOpen(this) ) {
354 * oTable.fnClose( this );
356 * oTable.fnOpen( this, "Temporary row opened", "info_row" );
360 * oTable = $('#example').dataTable();
363 this.fnClose = function( nTr )
365 this.api( true ).row( nTr ).child.hide();
370 * Remove a row for the table
371 * @param {mixed} target The index of the row from aoData to be deleted, or
372 * the TR element you want to delete
373 * @param {function|null} [callBack] Callback function
374 * @param {bool} [redraw=true] Redraw the table or not
375 * @returns {array} The row that was deleted
377 * @deprecated Since v1.10
380 * $(document).ready(function() {
381 * var oTable = $('#example').dataTable();
383 * // Immediately remove the first row
384 * oTable.fnDeleteRow( 0 );
387 this.fnDeleteRow = function( target, callback, redraw )
389 var api = this.api( true );
390 var rows = api.rows( target );
391 var settings = rows.settings()[0];
392 var data = settings.aoData[ rows[0][0] ];
397 callback.call( this, settings, data );
400 if ( redraw === undefined || redraw ) {
409 * Restore the table to it's original state in the DOM by removing all of DataTables
410 * enhancements, alterations to the DOM structure of the table and event listeners.
411 * @param {boolean} [remove=false] Completely remove the table from the DOM
413 * @deprecated Since v1.10
416 * $(document).ready(function() {
417 * // This example is fairly pointless in reality, but shows how fnDestroy can be used
418 * var oTable = $('#example').dataTable();
419 * oTable.fnDestroy();
422 this.fnDestroy = function ( remove )
424 this.api( true ).destroy( remove );
430 * @param {bool} [complete=true] Re-filter and resort (if enabled) the table before the draw.
432 * @deprecated Since v1.10
435 * $(document).ready(function() {
436 * var oTable = $('#example').dataTable();
438 * // Re-draw the table - you wouldn't want to do it here, but it's an example :-)
442 this.fnDraw = function( complete )
444 // Note that this isn't an exact match to the old call to _fnDraw - it takes
445 // into account the new data, but can hold position.
446 this.api( true ).draw( complete );
451 * Filter the input based on data
452 * @param {string} sInput String to filter the table on
453 * @param {int|null} [iColumn] Column to limit filtering to
454 * @param {bool} [bRegex=false] Treat as regular expression or not
455 * @param {bool} [bSmart=true] Perform smart filtering or not
456 * @param {bool} [bShowGlobal=true] Show the input global filter in it's input box(es)
457 * @param {bool} [bCaseInsensitive=true] Do case-insensitive matching (true) or not (false)
459 * @deprecated Since v1.10
462 * $(document).ready(function() {
463 * var oTable = $('#example').dataTable();
465 * // Sometime later - filter...
466 * oTable.fnFilter( 'test string' );
469 this.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal, bCaseInsensitive )
471 var api = this.api( true );
473 if ( iColumn === null || iColumn === undefined ) {
474 api.search( sInput, bRegex, bSmart, bCaseInsensitive );
477 api.column( iColumn ).search( sInput, bRegex, bSmart, bCaseInsensitive );
485 * Get the data for the whole table, an individual row or an individual cell based on the
486 * provided parameters.
487 * @param {int|node} [src] A TR row node, TD/TH cell node or an integer. If given as
488 * a TR node then the data source for the whole row will be returned. If given as a
489 * TD/TH cell node then iCol will be automatically calculated and the data for the
490 * cell returned. If given as an integer, then this is treated as the aoData internal
491 * data index for the row (see fnGetPosition) and the data for that row used.
492 * @param {int} [col] Optional column index that you want the data of.
493 * @returns {array|object|string} If mRow is undefined, then the data for all rows is
494 * returned. If mRow is defined, just data for that row, and is iCol is
495 * defined, only data for the designated cell is returned.
497 * @deprecated Since v1.10
501 * $(document).ready(function() {
502 * oTable = $('#example').dataTable();
504 * oTable.$('tr').click( function () {
505 * var data = oTable.fnGetData( this );
506 * // ... do something with the array / object of data for the row
511 * // Individual cell data
512 * $(document).ready(function() {
513 * oTable = $('#example').dataTable();
515 * oTable.$('td').click( function () {
516 * var sData = oTable.fnGetData( this );
517 * alert( 'The cell clicked on had the value of '+sData );
521 this.fnGetData = function( src, col )
523 var api = this.api( true );
525 if ( src !== undefined ) {
526 var type = src.nodeName ? src.nodeName.toLowerCase() : '';
528 return col !== undefined || type == 'td' || type == 'th' ?
529 api.cell( src, col ).data() :
530 api.row( src ).data() || null;
533 return api.data().toArray();
538 * Get an array of the TR nodes that are used in the table's body. Note that you will
539 * typically want to use the '$' API method in preference to this as it is more
541 * @param {int} [iRow] Optional row index for the TR element you want
542 * @returns {array|node} If iRow is undefined, returns an array of all TR elements
543 * in the table's body, or iRow is defined, just the TR element requested.
545 * @deprecated Since v1.10
548 * $(document).ready(function() {
549 * var oTable = $('#example').dataTable();
551 * // Get the nodes from the table
552 * var nNodes = oTable.fnGetNodes( );
555 this.fnGetNodes = function( iRow )
557 var api = this.api( true );
559 return iRow !== undefined ?
560 api.row( iRow ).node() :
561 api.rows().nodes().flatten().toArray();
566 * Get the array indexes of a particular cell from it's DOM element
567 * and column index including hidden columns
568 * @param {node} node this can either be a TR, TD or TH in the table's body
569 * @returns {int} If nNode is given as a TR, then a single index is returned, or
570 * if given as a cell, an array of [row index, column index (visible),
571 * column index (all)] is given.
573 * @deprecated Since v1.10
576 * $(document).ready(function() {
577 * $('#example tbody td').click( function () {
578 * // Get the position of the current data from the node
579 * var aPos = oTable.fnGetPosition( this );
581 * // Get the data array for this row
582 * var aData = oTable.fnGetData( aPos[0] );
584 * // Update the data array and return the value
585 * aData[ aPos[1] ] = 'clicked';
586 * this.innerHTML = 'clicked';
590 * oTable = $('#example').dataTable();
593 this.fnGetPosition = function( node )
595 var api = this.api( true );
596 var nodeName = node.nodeName.toUpperCase();
598 if ( nodeName == 'TR' ) {
599 return api.row( node ).index();
601 else if ( nodeName == 'TD' || nodeName == 'TH' ) {
602 var cell = api.cell( node ).index();
615 * Check to see if a row is 'open' or not.
616 * @param {node} nTr the table row to check
617 * @returns {boolean} true if the row is currently open, false otherwise
619 * @deprecated Since v1.10
622 * $(document).ready(function() {
625 * // 'open' an information row when a row is clicked on
626 * $('#example tbody tr').click( function () {
627 * if ( oTable.fnIsOpen(this) ) {
628 * oTable.fnClose( this );
630 * oTable.fnOpen( this, "Temporary row opened", "info_row" );
634 * oTable = $('#example').dataTable();
637 this.fnIsOpen = function( nTr )
639 return this.api( true ).row( nTr ).child.isShown();
644 * This function will place a new row directly after a row which is currently
645 * on display on the page, with the HTML contents that is passed into the
646 * function. This can be used, for example, to ask for confirmation that a
647 * particular record should be deleted.
648 * @param {node} nTr The table row to 'open'
649 * @param {string|node|jQuery} mHtml The HTML to put into the row
650 * @param {string} sClass Class to give the new TD cell
651 * @returns {node} The row opened. Note that if the table row passed in as the
652 * first parameter, is not found in the table, this method will silently
655 * @deprecated Since v1.10
658 * $(document).ready(function() {
661 * // 'open' an information row when a row is clicked on
662 * $('#example tbody tr').click( function () {
663 * if ( oTable.fnIsOpen(this) ) {
664 * oTable.fnClose( this );
666 * oTable.fnOpen( this, "Temporary row opened", "info_row" );
670 * oTable = $('#example').dataTable();
673 this.fnOpen = function( nTr, mHtml, sClass )
675 return this.api( true )
677 .child( mHtml, sClass )
684 * Change the pagination - provides the internal logic for pagination in a simple API
685 * function. With this function you can have a DataTables table go to the next,
686 * previous, first or last pages.
687 * @param {string|int} mAction Paging action to take: "first", "previous", "next" or "last"
688 * or page number to jump to (integer), note that page 0 is the first page.
689 * @param {bool} [bRedraw=true] Redraw the table or not
691 * @deprecated Since v1.10
694 * $(document).ready(function() {
695 * var oTable = $('#example').dataTable();
696 * oTable.fnPageChange( 'next' );
699 this.fnPageChange = function ( mAction, bRedraw )
701 var api = this.api( true ).page( mAction );
703 if ( bRedraw === undefined || bRedraw ) {
710 * Show a particular column
711 * @param {int} iCol The column whose display should be changed
712 * @param {bool} bShow Show (true) or hide (false) the column
713 * @param {bool} [bRedraw=true] Redraw the table or not
715 * @deprecated Since v1.10
718 * $(document).ready(function() {
719 * var oTable = $('#example').dataTable();
721 * // Hide the second column after initialisation
722 * oTable.fnSetColumnVis( 1, false );
725 this.fnSetColumnVis = function ( iCol, bShow, bRedraw )
727 var api = this.api( true ).column( iCol ).visible( bShow );
729 if ( bRedraw === undefined || bRedraw ) {
730 api.columns.adjust().draw();
736 * Get the settings for a particular table for external manipulation
737 * @returns {object} DataTables settings object. See
738 * {@link DataTable.models.oSettings}
740 * @deprecated Since v1.10
743 * $(document).ready(function() {
744 * var oTable = $('#example').dataTable();
745 * var oSettings = oTable.fnSettings();
747 * // Show an example parameter from the settings
748 * alert( oSettings._iDisplayStart );
751 this.fnSettings = function()
753 return _fnSettingsFromNode( this[_ext.iApiIndex] );
758 * Sort the table by a particular column
759 * @param {int} iCol the data index to sort on. Note that this will not match the
760 * 'display index' if you have hidden data entries
762 * @deprecated Since v1.10
765 * $(document).ready(function() {
766 * var oTable = $('#example').dataTable();
768 * // Sort immediately with columns 0 and 1
769 * oTable.fnSort( [ [0,'asc'], [1,'asc'] ] );
772 this.fnSort = function( aaSort )
774 this.api( true ).order( aaSort ).draw();
779 * Attach a sort listener to an element for a given column
780 * @param {node} nNode the element to attach the sort listener to
781 * @param {int} iColumn the column that a click on this node will sort on
782 * @param {function} [fnCallback] callback function when sort is run
784 * @deprecated Since v1.10
787 * $(document).ready(function() {
788 * var oTable = $('#example').dataTable();
790 * // Sort on column 1, when 'sorter' is clicked on
791 * oTable.fnSortListener( document.getElementById('sorter'), 1 );
794 this.fnSortListener = function( nNode, iColumn, fnCallback )
796 this.api( true ).order.listener( nNode, iColumn, fnCallback );
801 * Update a table cell or row - this method will accept either a single value to
802 * update the cell with, an array of values with one element for each column or
803 * an object in the same format as the original data source. The function is
804 * self-referencing in order to make the multi column updates easier.
805 * @param {object|array|string} mData Data to update the cell/row with
806 * @param {node|int} mRow TR element you want to update or the aoData index
807 * @param {int} [iColumn] The column to update, give as null or undefined to
808 * update a whole row.
809 * @param {bool} [bRedraw=true] Redraw the table or not
810 * @param {bool} [bAction=true] Perform pre-draw actions or not
811 * @returns {int} 0 on success, 1 on error
813 * @deprecated Since v1.10
816 * $(document).ready(function() {
817 * var oTable = $('#example').dataTable();
818 * oTable.fnUpdate( 'Example update', 0, 0 ); // Single cell
819 * oTable.fnUpdate( ['a', 'b', 'c', 'd', 'e'], $('tbody tr')[0] ); // Row
822 this.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction )
824 var api = this.api( true );
826 if ( iColumn === undefined || iColumn === null ) {
827 api.row( mRow ).data( mData );
830 api.cell( mRow, iColumn ).data( mData );
833 if ( bAction === undefined || bAction ) {
834 api.columns.adjust();
837 if ( bRedraw === undefined || bRedraw ) {
845 * Provide a common method for plug-ins to check the version of DataTables being used, in order
846 * to ensure compatibility.
847 * @param {string} sVersion Version string to check for, in the format "X.Y.Z". Note that the
848 * formats "X" and "X.Y" are also acceptable.
849 * @returns {boolean} true if this version of DataTables is greater or equal to the required
850 * version, or false if this version of DataTales is not suitable
853 * @deprecated Since v1.10
856 * $(document).ready(function() {
857 * var oTable = $('#example').dataTable();
858 * alert( oTable.fnVersionCheck( '1.9.0' ) );
861 this.fnVersionCheck = _ext.fnVersionCheck;
865 var emptyInit = options === undefined;
866 var len = this.length;
872 this.oApi = this.internal = _ext.internal;
874 // Extend with old style plug-in API methods
875 for ( var fn in DataTable.ext.internal ) {
877 this[fn] = _fnExternApiFunc(fn);
881 this.each(function() {
882 // For each initialisation we want to give it a clean initialisation
883 // object that can be bashed around
885 var oInit = len > 1 ? // optimisation for single table case
886 _fnExtend( o, options, true ) :
889 /*global oInit,_that,emptyInit*/
890 var i=0, iLen, j, jLen, k, kLen;
891 var sId = this.getAttribute( 'id' );
892 var bInitHandedOff = false;
893 var defaults = DataTable.defaults;
898 if ( this.nodeName.toLowerCase() != 'table' )
900 _fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 );
904 /* Backwards compatibility for the defaults */
905 _fnCompatOpts( defaults );
906 _fnCompatCols( defaults.column );
908 /* Convert the camel-case defaults to Hungarian */
909 _fnCamelToHungarian( defaults, defaults, true );
910 _fnCamelToHungarian( defaults.column, defaults.column, true );
912 /* Setting up the initialisation object */
913 _fnCamelToHungarian( defaults, $.extend( oInit, $this.data() ), true );
917 /* Check to see if we are re-initialising a table */
918 var allSettings = DataTable.settings;
919 for ( i=0, iLen=allSettings.length ; i<iLen ; i++ )
921 var s = allSettings[i];
923 /* Base check on table node */
926 (s.nTHead && s.nTHead.parentNode == this) ||
927 (s.nTFoot && s.nTFoot.parentNode == this)
929 var bRetrieve = oInit.bRetrieve !== undefined ? oInit.bRetrieve : defaults.bRetrieve;
930 var bDestroy = oInit.bDestroy !== undefined ? oInit.bDestroy : defaults.bDestroy;
932 if ( emptyInit || bRetrieve )
938 s.oInstance.fnDestroy();
943 _fnLog( s, 0, 'Cannot reinitialise DataTable', 3 );
948 /* If the element we are initialising has the same ID as a table which was previously
949 * initialised, but the table nodes don't match (from before) then we destroy the old
950 * instance by simply deleting it. This is under the assumption that the table has been
951 * destroyed by other methods. Anyone using non-id selectors will need to do this manually
953 if ( s.sTableId == this.id )
955 allSettings.splice( i, 1 );
960 /* Ensure the table has an ID - required for accessibility */
961 if ( sId === null || sId === "" )
963 sId = "DataTables_Table_"+(DataTable.ext._unique++);
967 /* Create the settings object for this table and set some of the default parameters */
968 var oSettings = $.extend( true, {}, DataTable.models.oSettings, {
969 "sDestroyWidth": $this[0].style.width,
973 oSettings.nTable = this;
974 oSettings.oApi = _that.internal;
975 oSettings.oInit = oInit;
977 allSettings.push( oSettings );
979 // Need to add the instance after the instance after the settings object has been added
980 // to the settings array, so we can self reference the table instance if more than one
981 oSettings.oInstance = (_that.length===1) ? _that : $this.dataTable();
983 // Backwards compatibility, before we apply all the defaults
984 _fnCompatOpts( oInit );
985 _fnLanguageCompat( oInit.oLanguage );
987 // If the length menu is given, but the init display length is not, use the length menu
988 if ( oInit.aLengthMenu && ! oInit.iDisplayLength )
990 oInit.iDisplayLength = $.isArray( oInit.aLengthMenu[0] ) ?
991 oInit.aLengthMenu[0][0] : oInit.aLengthMenu[0];
994 // Apply the defaults and init options to make a single init object will all
995 // options defined from defaults and instance options.
996 oInit = _fnExtend( $.extend( true, {}, defaults ), oInit );
999 // Map the initialisation options onto the settings object
1000 _fnMap( oSettings.oFeatures, oInit, [
1013 _fnMap( oSettings, oInit, [
1029 "fnStateLoadCallback",
1030 "fnStateSaveCallback",
1034 [ "iCookieDuration", "iStateDuration" ], // backwards compat
1035 [ "oSearch", "oPreviousSearch" ],
1036 [ "aoSearchCols", "aoPreSearchCols" ],
1037 [ "iDisplayLength", "_iDisplayLength" ]
1039 _fnMap( oSettings.oScroll, oInit, [
1040 [ "sScrollX", "sX" ],
1041 [ "sScrollXInner", "sXInner" ],
1042 [ "sScrollY", "sY" ],
1043 [ "bScrollCollapse", "bCollapse" ]
1045 _fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" );
1047 /* Callback functions which are array driven */
1048 _fnCallbackReg( oSettings, 'aoDrawCallback', oInit.fnDrawCallback, 'user' );
1049 _fnCallbackReg( oSettings, 'aoServerParams', oInit.fnServerParams, 'user' );
1050 _fnCallbackReg( oSettings, 'aoStateSaveParams', oInit.fnStateSaveParams, 'user' );
1051 _fnCallbackReg( oSettings, 'aoStateLoadParams', oInit.fnStateLoadParams, 'user' );
1052 _fnCallbackReg( oSettings, 'aoStateLoaded', oInit.fnStateLoaded, 'user' );
1053 _fnCallbackReg( oSettings, 'aoRowCallback', oInit.fnRowCallback, 'user' );
1054 _fnCallbackReg( oSettings, 'aoRowCreatedCallback', oInit.fnCreatedRow, 'user' );
1055 _fnCallbackReg( oSettings, 'aoHeaderCallback', oInit.fnHeaderCallback, 'user' );
1056 _fnCallbackReg( oSettings, 'aoFooterCallback', oInit.fnFooterCallback, 'user' );
1057 _fnCallbackReg( oSettings, 'aoInitComplete', oInit.fnInitComplete, 'user' );
1058 _fnCallbackReg( oSettings, 'aoPreDrawCallback', oInit.fnPreDrawCallback, 'user' );
1060 oSettings.rowIdFn = _fnGetObjectDataFn( oInit.rowId );
1062 /* Browser support detection */
1063 _fnBrowserDetect( oSettings );
1065 var oClasses = oSettings.oClasses;
1067 $.extend( oClasses, DataTable.ext.classes, oInit.oClasses );
1068 $this.addClass( oClasses.sTable );
1071 if ( oSettings.iInitDisplayStart === undefined )
1073 /* Display start point, taking into account the save saving */
1074 oSettings.iInitDisplayStart = oInit.iDisplayStart;
1075 oSettings._iDisplayStart = oInit.iDisplayStart;
1078 if ( oInit.iDeferLoading !== null )
1080 oSettings.bDeferLoading = true;
1081 var tmp = $.isArray( oInit.iDeferLoading );
1082 oSettings._iRecordsDisplay = tmp ? oInit.iDeferLoading[0] : oInit.iDeferLoading;
1083 oSettings._iRecordsTotal = tmp ? oInit.iDeferLoading[1] : oInit.iDeferLoading;
1086 /* Language definitions */
1087 var oLanguage = oSettings.oLanguage;
1088 $.extend( true, oLanguage, oInit.oLanguage );
1090 if ( oLanguage.sUrl )
1092 /* Get the language definitions from a file - because this Ajax call makes the language
1093 * get async to the remainder of this function we use bInitHandedOff to indicate that
1094 * _fnInitialise will be fired by the returned Ajax handler, rather than the constructor
1098 url: oLanguage.sUrl,
1099 success: function ( json ) {
1100 _fnLanguageCompat( json );
1101 _fnCamelToHungarian( defaults.oLanguage, json );
1102 $.extend( true, oLanguage, json );
1103 _fnInitialise( oSettings );
1105 error: function () {
1106 // Error occurred loading language file, continue on as best we can
1107 _fnInitialise( oSettings );
1110 bInitHandedOff = true;
1116 if ( oInit.asStripeClasses === null )
1118 oSettings.asStripeClasses =[
1119 oClasses.sStripeOdd,
1120 oClasses.sStripeEven
1124 /* Remove row stripe classes if they are already on the table row */
1125 var stripeClasses = oSettings.asStripeClasses;
1126 var rowOne = $this.children('tbody').find('tr').eq(0);
1127 if ( $.inArray( true, $.map( stripeClasses, function(el, i) {
1128 return rowOne.hasClass(el);
1130 $('tbody tr', this).removeClass( stripeClasses.join(' ') );
1131 oSettings.asDestroyStripes = stripeClasses.slice();
1136 * See if we should load columns automatically or use defined ones
1140 var nThead = this.getElementsByTagName('thead');
1141 if ( nThead.length !== 0 )
1143 _fnDetectHeader( oSettings.aoHeader, nThead[0] );
1144 anThs = _fnGetUniqueThs( oSettings );
1147 /* If not given a column array, generate one with nulls */
1148 if ( oInit.aoColumns === null )
1151 for ( i=0, iLen=anThs.length ; i<iLen ; i++ )
1153 aoColumnsInit.push( null );
1158 aoColumnsInit = oInit.aoColumns;
1161 /* Add the columns */
1162 for ( i=0, iLen=aoColumnsInit.length ; i<iLen ; i++ )
1164 _fnAddColumn( oSettings, anThs ? anThs[i] : null );
1167 /* Apply the column definitions */
1168 _fnApplyColumnDefs( oSettings, oInit.aoColumnDefs, aoColumnsInit, function (iCol, oDef) {
1169 _fnColumnOptions( oSettings, iCol, oDef );
1172 /* HTML5 attribute detection - build an mData object automatically if the
1173 * attributes are found
1175 if ( rowOne.length ) {
1176 var a = function ( cell, name ) {
1177 return cell.getAttribute( 'data-'+name ) !== null ? name : null;
1180 $( rowOne[0] ).children('th, td').each( function (i, cell) {
1181 var col = oSettings.aoColumns[i];
1183 if ( col.mData === i ) {
1184 var sort = a( cell, 'sort' ) || a( cell, 'order' );
1185 var filter = a( cell, 'filter' ) || a( cell, 'search' );
1187 if ( sort !== null || filter !== null ) {
1190 sort: sort !== null ? i+'.@data-'+sort : undefined,
1191 type: sort !== null ? i+'.@data-'+sort : undefined,
1192 filter: filter !== null ? i+'.@data-'+filter : undefined
1195 _fnColumnOptions( oSettings, i );
1201 var features = oSettings.oFeatures;
1202 var loadedInit = function () {
1205 * @todo For modularisation (1.11) this needs to do into a sort start up handler
1208 // If aaSorting is not defined, then we use the first indicator in asSorting
1209 // in case that has been altered, so the default sort reflects that option
1210 if ( oInit.aaSorting === undefined ) {
1211 var sorting = oSettings.aaSorting;
1212 for ( i=0, iLen=sorting.length ; i<iLen ; i++ ) {
1213 sorting[i][1] = oSettings.aoColumns[ i ].asSorting[0];
1217 /* Do a first pass on the sorting classes (allows any size changes to be taken into
1218 * account, and also will apply sorting disabled classes if disabled
1220 _fnSortingClasses( oSettings );
1222 if ( features.bSort ) {
1223 _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
1224 if ( oSettings.bSorted ) {
1225 var aSort = _fnSortFlatten( oSettings );
1226 var sortedColumns = {};
1228 $.each( aSort, function (i, val) {
1229 sortedColumns[ val.src ] = val.dir;
1232 _fnCallbackFire( oSettings, null, 'order', [oSettings, aSort, sortedColumns] );
1233 _fnSortAria( oSettings );
1238 _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
1239 if ( oSettings.bSorted || _fnDataSource( oSettings ) === 'ssp' || features.bDeferRender ) {
1240 _fnSortingClasses( oSettings );
1247 * Cache the header, body and footer as required, creating them if needed
1250 // Work around for Webkit bug 83867 - store the caption-side before removing from doc
1251 var captions = $this.children('caption').each( function () {
1252 this._captionSide = $(this).css('caption-side');
1255 var thead = $this.children('thead');
1256 if ( thead.length === 0 ) {
1257 thead = $('<thead/>').appendTo($this);
1259 oSettings.nTHead = thead[0];
1261 var tbody = $this.children('tbody');
1262 if ( tbody.length === 0 ) {
1263 tbody = $('<tbody/>').appendTo($this);
1265 oSettings.nTBody = tbody[0];
1267 var tfoot = $this.children('tfoot');
1268 if ( tfoot.length === 0 && captions.length > 0 && (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") ) {
1269 // If we are a scrolling table, and no footer has been given, then we need to create
1270 // a tfoot element for the caption element to be appended to
1271 tfoot = $('<tfoot/>').appendTo($this);
1274 if ( tfoot.length === 0 || tfoot.children().length === 0 ) {
1275 $this.addClass( oClasses.sNoFooter );
1277 else if ( tfoot.length > 0 ) {
1278 oSettings.nTFoot = tfoot[0];
1279 _fnDetectHeader( oSettings.aoFooter, oSettings.nTFoot );
1282 /* Check if there is data passing into the constructor */
1283 if ( oInit.aaData ) {
1284 for ( i=0 ; i<oInit.aaData.length ; i++ ) {
1285 _fnAddData( oSettings, oInit.aaData[ i ] );
1288 else if ( oSettings.bDeferLoading || _fnDataSource( oSettings ) == 'dom' ) {
1289 /* Grab the data from the page - only do this when deferred loading or no Ajax
1290 * source since there is no point in reading the DOM data if we are then going
1291 * to replace it with Ajax data
1293 _fnAddTr( oSettings, $(oSettings.nTBody).children('tr') );
1296 /* Copy the data index array */
1297 oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
1299 /* Initialisation complete - table can be drawn */
1300 oSettings.bInitialised = true;
1302 /* Check if we need to initialise the table (it might not have been handed off to the
1303 * language processor)
1305 if ( bInitHandedOff === false ) {
1306 _fnInitialise( oSettings );
1310 /* Must be done after everything which can be overridden by the state saving! */
1311 if ( oInit.bStateSave )
1313 features.bStateSave = true;
1314 _fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState, 'state_save' );
1315 _fnLoadState( oSettings, oInit, loadedInit );
1328 * It is useful to have variables which are scoped locally so only the
1329 * DataTables functions can access them and they don't leak into global space.
1330 * At the same time these functions are often useful over multiple files in the
1331 * core and API, so we list, or at least document, all variables which are used
1332 * by DataTables as private variables here. This also ensures that there is no
1333 * clashing of variable names and that they can easily referenced for reuse.
1337 // Defined else where
1341 // _selector_row_indexes
1343 var _ext; // DataTable.ext
1344 var _Api; // DataTable.Api
1345 var _api_register; // DataTable.Api.register
1346 var _api_registerPlural; // DataTable.Api.registerPlural
1349 var _re_new_lines = /[\r\n\u2028]/g;
1350 var _re_html = /<.*?>/g;
1352 // This is not strict ISO8601 - Date.parse() is quite lax, although
1353 // implementations differ between browsers.
1354 var _re_date = /^\d{2,4}[\.\/\-]\d{1,2}[\.\/\-]\d{1,2}([T ]{1}\d{1,2}[:\.]\d{2}([\.:]\d{2})?)?$/;
1356 // Escape regular expression special characters
1357 var _re_escape_regex = new RegExp( '(\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ].join('|\\') + ')', 'g' );
1359 // http://en.wikipedia.org/wiki/Foreign_exchange_market
1360 // - \u20BD - Russian ruble.
1361 // - \u20a9 - South Korean Won
1362 // - \u20BA - Turkish Lira
1363 // - \u20B9 - Indian Rupee
1364 // - R - Brazil (R$) and South Africa
1365 // - fr - Swiss Franc
1366 // - kr - Swedish krona, Norwegian krone and Danish krone
1367 // - \u2009 is thin space and \u202F is narrow no-break space, both used in many
1370 // standards as thousands separators.
1371 var _re_formatted_numeric = /[',$£€¥%\u2009\u202F\u20BD\u20a9\u20BArfkɃΞ]/gi;
1374 var _empty = function ( d ) {
1375 return !d || d === true || d === '-' ? true : false;
1379 var _intVal = function ( s ) {
1380 var integer = parseInt( s, 10 );
1381 return !isNaN(integer) && isFinite(s) ? integer : null;
1384 // Convert from a formatted number with characters other than `.` as the
1385 // decimal place, to a Javascript number
1386 var _numToDecimal = function ( num, decimalPoint ) {
1387 // Cache created regular expressions for speed as this function is called often
1388 if ( ! _re_dic[ decimalPoint ] ) {
1389 _re_dic[ decimalPoint ] = new RegExp( _fnEscapeRegex( decimalPoint ), 'g' );
1391 return typeof num === 'string' && decimalPoint !== '.' ?
1392 num.replace( /\./g, '' ).replace( _re_dic[ decimalPoint ], '.' ) :
1397 var _isNumber = function ( d, decimalPoint, formatted ) {
1398 var strType = typeof d === 'string';
1400 // If empty return immediately so there must be a number if it is a
1401 // formatted string (this stops the string "k", or "kr", etc being detected
1402 // as a formatted number for currency
1403 if ( _empty( d ) ) {
1407 if ( decimalPoint && strType ) {
1408 d = _numToDecimal( d, decimalPoint );
1411 if ( formatted && strType ) {
1412 d = d.replace( _re_formatted_numeric, '' );
1415 return !isNaN( parseFloat(d) ) && isFinite( d );
1419 // A string without HTML in it can be considered to be HTML still
1420 var _isHtml = function ( d ) {
1421 return _empty( d ) || typeof d === 'string';
1425 var _htmlNumeric = function ( d, decimalPoint, formatted ) {
1426 if ( _empty( d ) ) {
1430 var html = _isHtml( d );
1433 _isNumber( _stripHtml( d ), decimalPoint, formatted ) ?
1439 var _pluck = function ( a, prop, prop2 ) {
1441 var i=0, ien=a.length;
1443 // Could have the test in the loop for slightly smaller code, but speed
1444 // is essential here
1445 if ( prop2 !== undefined ) {
1446 for ( ; i<ien ; i++ ) {
1447 if ( a[i] && a[i][ prop ] ) {
1448 out.push( a[i][ prop ][ prop2 ] );
1453 for ( ; i<ien ; i++ ) {
1455 out.push( a[i][ prop ] );
1464 // Basically the same as _pluck, but rather than looping over `a` we use `order`
1465 // as the indexes to pick from `a`
1466 var _pluck_order = function ( a, order, prop, prop2 )
1469 var i=0, ien=order.length;
1471 // Could have the test in the loop for slightly smaller code, but speed
1472 // is essential here
1473 if ( prop2 !== undefined ) {
1474 for ( ; i<ien ; i++ ) {
1475 if ( a[ order[i] ][ prop ] ) {
1476 out.push( a[ order[i] ][ prop ][ prop2 ] );
1481 for ( ; i<ien ; i++ ) {
1482 out.push( a[ order[i] ][ prop ] );
1490 var _range = function ( len, start )
1495 if ( start === undefined ) {
1504 for ( var i=start ; i<end ; i++ ) {
1512 var _removeEmpty = function ( a )
1516 for ( var i=0, ien=a.length ; i<ien ; i++ ) {
1517 if ( a[i] ) { // careful - will remove all falsy values!
1526 var _stripHtml = function ( d ) {
1527 return d.replace( _re_html, '' );
1532 * Determine if all values in the array are unique. This means we can short
1533 * cut the _unique method at the cost of a single loop. A sorted array is used
1534 * to easily check the values.
1536 * @param {array} src Source array
1537 * @return {boolean} true if all unique, false otherwise
1540 var _areAllUnique = function ( src ) {
1541 if ( src.length < 2 ) {
1545 var sorted = src.slice().sort();
1546 var last = sorted[0];
1548 for ( var i=1, ien=sorted.length ; i<ien ; i++ ) {
1549 if ( sorted[i] === last ) {
1561 * Find the unique elements in a source array.
1563 * @param {array} src Source array
1564 * @return {array} Array of unique items
1567 var _unique = function ( src )
1569 if ( _areAllUnique( src ) ) {
1573 // A faster unique method is to use object keys to identify used values,
1574 // but this doesn't work with arrays or objects, which we must also
1575 // consider. See jsperf.com/compare-array-unique-versions/4 for more
1583 again: for ( i=0 ; i<ien ; i++ ) {
1586 for ( j=0 ; j<k ; j++ ) {
1587 if ( out[j] === val ) {
1601 * DataTables utility methods
1603 * This namespace provides helper methods that DataTables uses internally to
1604 * create a DataTable, but which are not exclusively used only for DataTables.
1605 * These methods can be used by extension authors to save the duplication of
1612 * Throttle the calls to a function. Arguments and context are maintained
1613 * for the throttled function.
1615 * @param {function} fn Function to be called
1616 * @param {integer} freq Call frequency in mS
1617 * @return {function} Wrapped function
1619 throttle: function ( fn, freq ) {
1621 frequency = freq !== undefined ? freq : 200,
1625 return function () {
1631 if ( last && now < last + frequency ) {
1632 clearTimeout( timer );
1634 timer = setTimeout( function () {
1636 fn.apply( that, args );
1641 fn.apply( that, args );
1648 * Escape a string such that it can be used in a regular expression
1650 * @param {string} val string to escape
1651 * @returns {string} escaped string
1653 escapeRegex: function ( val ) {
1654 return val.replace( _re_escape_regex, '\\$1' );
1661 * Create a mapping object that allows camel case parameters to be looked up
1662 * for their Hungarian counterparts. The mapping is stored in a private
1663 * parameter called `_hungarianMap` which can be accessed on the source object.
1665 * @memberof DataTable#oApi
1667 function _fnHungarianMap ( o )
1670 hungarian = 'a aa ai ao as b fn i m o s ',
1675 $.each( o, function (key, val) {
1676 match = key.match(/^([^A-Z]+?)([A-Z])/);
1678 if ( match && hungarian.indexOf(match[1]+' ') !== -1 )
1680 newKey = key.replace( match[0], match[2].toLowerCase() );
1681 map[ newKey ] = key;
1683 if ( match[1] === 'o' )
1685 _fnHungarianMap( o[key] );
1690 o._hungarianMap = map;
1695 * Convert from camel case parameters to Hungarian, based on a Hungarian map
1696 * created by _fnHungarianMap.
1697 * @param {object} src The model object which holds all parameters that can be
1699 * @param {object} user The object to convert from camel case to Hungarian.
1700 * @param {boolean} force When set to `true`, properties which already have a
1701 * Hungarian value in the `user` object will be overwritten. Otherwise they
1703 * @memberof DataTable#oApi
1705 function _fnCamelToHungarian ( src, user, force )
1707 if ( ! src._hungarianMap ) {
1708 _fnHungarianMap( src );
1713 $.each( user, function (key, val) {
1714 hungarianKey = src._hungarianMap[ key ];
1716 if ( hungarianKey !== undefined && (force || user[hungarianKey] === undefined) )
1718 // For objects, we need to buzz down into the object to copy parameters
1719 if ( hungarianKey.charAt(0) === 'o' )
1721 // Copy the camelCase options over to the hungarian
1722 if ( ! user[ hungarianKey ] ) {
1723 user[ hungarianKey ] = {};
1725 $.extend( true, user[hungarianKey], user[key] );
1727 _fnCamelToHungarian( src[hungarianKey], user[hungarianKey], force );
1730 user[hungarianKey] = user[ key ];
1738 * Language compatibility - when certain options are given, and others aren't, we
1739 * need to duplicate the values over, in order to provide backwards compatibility
1740 * with older language files.
1741 * @param {object} oSettings dataTables settings object
1742 * @memberof DataTable#oApi
1744 function _fnLanguageCompat( lang )
1746 // Note the use of the Hungarian notation for the parameters in this method as
1747 // this is called after the mapping of camelCase to Hungarian
1748 var defaults = DataTable.defaults.oLanguage;
1751 var defaultDecimal = defaults.sDecimal;
1752 if ( defaultDecimal ) {
1753 _addNumericSort( defaultDecimal );
1757 var zeroRecords = lang.sZeroRecords;
1759 // Backwards compatibility - if there is no sEmptyTable given, then use the same as
1760 // sZeroRecords - assuming that is given.
1761 if ( ! lang.sEmptyTable && zeroRecords &&
1762 defaults.sEmptyTable === "No data available in table" )
1764 _fnMap( lang, lang, 'sZeroRecords', 'sEmptyTable' );
1767 // Likewise with loading records
1768 if ( ! lang.sLoadingRecords && zeroRecords &&
1769 defaults.sLoadingRecords === "Loading..." )
1771 _fnMap( lang, lang, 'sZeroRecords', 'sLoadingRecords' );
1774 // Old parameter name of the thousands separator mapped onto the new
1775 if ( lang.sInfoThousands ) {
1776 lang.sThousands = lang.sInfoThousands;
1779 var decimal = lang.sDecimal;
1780 if ( decimal && defaultDecimal !== decimal ) {
1781 _addNumericSort( decimal );
1788 * Map one parameter onto another
1789 * @param {object} o Object to map
1790 * @param {*} knew The new parameter name
1791 * @param {*} old The old parameter name
1793 var _fnCompatMap = function ( o, knew, old ) {
1794 if ( o[ knew ] !== undefined ) {
1795 o[ old ] = o[ knew ];
1801 * Provide backwards compatibility for the main DT options. Note that the new
1802 * options are mapped onto the old parameters, so this is an external interface
1804 * @param {object} init Object to map
1806 function _fnCompatOpts ( init )
1808 _fnCompatMap( init, 'ordering', 'bSort' );
1809 _fnCompatMap( init, 'orderMulti', 'bSortMulti' );
1810 _fnCompatMap( init, 'orderClasses', 'bSortClasses' );
1811 _fnCompatMap( init, 'orderCellsTop', 'bSortCellsTop' );
1812 _fnCompatMap( init, 'order', 'aaSorting' );
1813 _fnCompatMap( init, 'orderFixed', 'aaSortingFixed' );
1814 _fnCompatMap( init, 'paging', 'bPaginate' );
1815 _fnCompatMap( init, 'pagingType', 'sPaginationType' );
1816 _fnCompatMap( init, 'pageLength', 'iDisplayLength' );
1817 _fnCompatMap( init, 'searching', 'bFilter' );
1819 // Boolean initialisation of x-scrolling
1820 if ( typeof init.sScrollX === 'boolean' ) {
1821 init.sScrollX = init.sScrollX ? '100%' : '';
1823 if ( typeof init.scrollX === 'boolean' ) {
1824 init.scrollX = init.scrollX ? '100%' : '';
1827 // Column search objects are in an array, so it needs to be converted
1828 // element by element
1829 var searchCols = init.aoSearchCols;
1832 for ( var i=0, ien=searchCols.length ; i<ien ; i++ ) {
1833 if ( searchCols[i] ) {
1834 _fnCamelToHungarian( DataTable.models.oSearch, searchCols[i] );
1842 * Provide backwards compatibility for column options. Note that the new options
1843 * are mapped onto the old parameters, so this is an external interface change
1845 * @param {object} init Object to map
1847 function _fnCompatCols ( init )
1849 _fnCompatMap( init, 'orderable', 'bSortable' );
1850 _fnCompatMap( init, 'orderData', 'aDataSort' );
1851 _fnCompatMap( init, 'orderSequence', 'asSorting' );
1852 _fnCompatMap( init, 'orderDataType', 'sortDataType' );
1854 // orderData can be given as an integer
1855 var dataSort = init.aDataSort;
1856 if ( typeof dataSort === 'number' && ! $.isArray( dataSort ) ) {
1857 init.aDataSort = [ dataSort ];
1863 * Browser feature detection for capabilities, quirks
1864 * @param {object} settings dataTables settings object
1865 * @memberof DataTable#oApi
1867 function _fnBrowserDetect( settings )
1869 // We don't need to do this every time DataTables is constructed, the values
1870 // calculated are specific to the browser and OS configuration which we
1871 // don't expect to change between initialisations
1872 if ( ! DataTable.__browser ) {
1874 DataTable.__browser = browser;
1876 // Scrolling feature / quirks detection
1881 left: $(window).scrollLeft()*-1, // allow for scrolling
1889 position: 'absolute',
1903 .appendTo( 'body' );
1905 var outer = n.children();
1906 var inner = outer.children();
1908 // Numbers below, in order, are:
1909 // inner.offsetWidth, inner.clientWidth, outer.offsetWidth, outer.clientWidth
1911 // IE6 XP: 100 100 100 83
1912 // IE7 Vista: 100 100 100 83
1913 // IE 8+ Windows: 83 83 100 83
1914 // Evergreen Windows: 83 83 100 83
1915 // Evergreen Mac with scrollbars: 85 85 100 85
1916 // Evergreen Mac without scrollbars: 100 100 100 100
1918 // Get scrollbar width
1919 browser.barWidth = outer[0].offsetWidth - outer[0].clientWidth;
1921 // IE6/7 will oversize a width 100% element inside a scrolling element, to
1922 // include the width of the scrollbar, while other browsers ensure the inner
1923 // element is contained without forcing scrolling
1924 browser.bScrollOversize = inner[0].offsetWidth === 100 && outer[0].clientWidth !== 100;
1926 // In rtl text layout, some browsers (most, but not all) will place the
1927 // scrollbar on the left, rather than the right.
1928 browser.bScrollbarLeft = Math.round( inner.offset().left ) !== 1;
1930 // IE8- don't provide height and width for getBoundingClientRect
1931 browser.bBounding = n[0].getBoundingClientRect().width ? true : false;
1936 $.extend( settings.oBrowser, DataTable.__browser );
1937 settings.oScroll.iBarWidth = DataTable.__browser.barWidth;
1942 * Array.prototype reduce[Right] method, used for browsers which don't support
1943 * JS 1.6. Done this way to reduce code size, since we iterate either way
1944 * @param {object} settings dataTables settings object
1945 * @memberof DataTable#oApi
1947 function _fnReduce ( that, fn, init, start, end, inc )
1954 if ( init !== undefined ) {
1959 while ( i !== end ) {
1960 if ( ! that.hasOwnProperty(i) ) {
1965 fn( value, that[i], i, that ) :
1976 * Add a column to the list used for the table with default values
1977 * @param {object} oSettings dataTables settings object
1978 * @param {node} nTh The th element for this column
1979 * @memberof DataTable#oApi
1981 function _fnAddColumn( oSettings, nTh )
1983 // Add column to aoColumns array
1984 var oDefaults = DataTable.defaults.column;
1985 var iCol = oSettings.aoColumns.length;
1986 var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, {
1987 "nTh": nTh ? nTh : document.createElement('th'),
1988 "sTitle": oDefaults.sTitle ? oDefaults.sTitle : nTh ? nTh.innerHTML : '',
1989 "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol],
1990 "mData": oDefaults.mData ? oDefaults.mData : iCol,
1993 oSettings.aoColumns.push( oCol );
1995 // Add search object for column specific search. Note that the `searchCols[ iCol ]`
1996 // passed into extend can be undefined. This allows the user to give a default
1997 // with only some of the parameters defined, and also not give a default
1998 var searchCols = oSettings.aoPreSearchCols;
1999 searchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch, searchCols[ iCol ] );
2001 // Use the default column options function to initialise classes etc
2002 _fnColumnOptions( oSettings, iCol, $(nTh).data() );
2007 * Apply options for a column
2008 * @param {object} oSettings dataTables settings object
2009 * @param {int} iCol column index to consider
2010 * @param {object} oOptions object with sType, bVisible and bSearchable etc
2011 * @memberof DataTable#oApi
2013 function _fnColumnOptions( oSettings, iCol, oOptions )
2015 var oCol = oSettings.aoColumns[ iCol ];
2016 var oClasses = oSettings.oClasses;
2017 var th = $(oCol.nTh);
2019 // Try to get width information from the DOM. We can't get it from CSS
2020 // as we'd need to parse the CSS stylesheet. `width` option can override
2021 if ( ! oCol.sWidthOrig ) {
2023 oCol.sWidthOrig = th.attr('width') || null;
2026 var t = (th.attr('style') || '').match(/width:\s*(\d+[pxem%]+)/);
2028 oCol.sWidthOrig = t[1];
2032 /* User specified column options */
2033 if ( oOptions !== undefined && oOptions !== null )
2035 // Backwards compatibility
2036 _fnCompatCols( oOptions );
2038 // Map camel case parameters to their Hungarian counterparts
2039 _fnCamelToHungarian( DataTable.defaults.column, oOptions, true );
2041 /* Backwards compatibility for mDataProp */
2042 if ( oOptions.mDataProp !== undefined && !oOptions.mData )
2044 oOptions.mData = oOptions.mDataProp;
2047 if ( oOptions.sType )
2049 oCol._sManualType = oOptions.sType;
2052 // `class` is a reserved word in Javascript, so we need to provide
2053 // the ability to use a valid name for the camel case input
2054 if ( oOptions.className && ! oOptions.sClass )
2056 oOptions.sClass = oOptions.className;
2058 if ( oOptions.sClass ) {
2059 th.addClass( oOptions.sClass );
2062 $.extend( oCol, oOptions );
2063 _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" );
2065 /* iDataSort to be applied (backwards compatibility), but aDataSort will take
2066 * priority if defined
2068 if ( oOptions.iDataSort !== undefined )
2070 oCol.aDataSort = [ oOptions.iDataSort ];
2072 _fnMap( oCol, oOptions, "aDataSort" );
2075 /* Cache the data get and set functions for speed */
2076 var mDataSrc = oCol.mData;
2077 var mData = _fnGetObjectDataFn( mDataSrc );
2078 var mRender = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null;
2080 var attrTest = function( src ) {
2081 return typeof src === 'string' && src.indexOf('@') !== -1;
2083 oCol._bAttrSrc = $.isPlainObject( mDataSrc ) && (
2084 attrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter)
2086 oCol._setter = null;
2088 oCol.fnGetData = function (rowData, type, meta) {
2089 var innerData = mData( rowData, type, undefined, meta );
2091 return mRender && type ?
2092 mRender( innerData, type, rowData, meta ) :
2095 oCol.fnSetData = function ( rowData, val, meta ) {
2096 return _fnSetObjectDataFn( mDataSrc )( rowData, val, meta );
2099 // Indicate if DataTables should read DOM data as an object or array
2100 // Used in _fnGetRowElements
2101 if ( typeof mDataSrc !== 'number' ) {
2102 oSettings._rowReadObject = true;
2105 /* Feature sorting overrides column specific when off */
2106 if ( !oSettings.oFeatures.bSort )
2108 oCol.bSortable = false;
2109 th.addClass( oClasses.sSortableNone ); // Have to add class here as order event isn't called
2112 /* Check that the class assignment is correct for sorting */
2113 var bAsc = $.inArray('asc', oCol.asSorting) !== -1;
2114 var bDesc = $.inArray('desc', oCol.asSorting) !== -1;
2115 if ( !oCol.bSortable || (!bAsc && !bDesc) )
2117 oCol.sSortingClass = oClasses.sSortableNone;
2118 oCol.sSortingClassJUI = "";
2120 else if ( bAsc && !bDesc )
2122 oCol.sSortingClass = oClasses.sSortableAsc;
2123 oCol.sSortingClassJUI = oClasses.sSortJUIAscAllowed;
2125 else if ( !bAsc && bDesc )
2127 oCol.sSortingClass = oClasses.sSortableDesc;
2128 oCol.sSortingClassJUI = oClasses.sSortJUIDescAllowed;
2132 oCol.sSortingClass = oClasses.sSortable;
2133 oCol.sSortingClassJUI = oClasses.sSortJUI;
2139 * Adjust the table column widths for new data. Note: you would probably want to
2140 * do a redraw after calling this function!
2141 * @param {object} settings dataTables settings object
2142 * @memberof DataTable#oApi
2144 function _fnAdjustColumnSizing ( settings )
2146 /* Not interested in doing column width calculation if auto-width is disabled */
2147 if ( settings.oFeatures.bAutoWidth !== false )
2149 var columns = settings.aoColumns;
2151 _fnCalculateColumnWidths( settings );
2152 for ( var i=0 , iLen=columns.length ; i<iLen ; i++ )
2154 columns[i].nTh.style.width = columns[i].sWidth;
2158 var scroll = settings.oScroll;
2159 if ( scroll.sY !== '' || scroll.sX !== '')
2161 _fnScrollDraw( settings );
2164 _fnCallbackFire( settings, null, 'column-sizing', [settings] );
2169 * Covert the index of a visible column to the index in the data array (take account
2170 * of hidden columns)
2171 * @param {object} oSettings dataTables settings object
2172 * @param {int} iMatch Visible column index to lookup
2173 * @returns {int} i the data index
2174 * @memberof DataTable#oApi
2176 function _fnVisibleToColumnIndex( oSettings, iMatch )
2178 var aiVis = _fnGetColumns( oSettings, 'bVisible' );
2180 return typeof aiVis[iMatch] === 'number' ?
2187 * Covert the index of an index in the data array and convert it to the visible
2188 * column index (take account of hidden columns)
2189 * @param {int} iMatch Column index to lookup
2190 * @param {object} oSettings dataTables settings object
2191 * @returns {int} i the data index
2192 * @memberof DataTable#oApi
2194 function _fnColumnIndexToVisible( oSettings, iMatch )
2196 var aiVis = _fnGetColumns( oSettings, 'bVisible' );
2197 var iPos = $.inArray( iMatch, aiVis );
2199 return iPos !== -1 ? iPos : null;
2204 * Get the number of visible columns
2205 * @param {object} oSettings dataTables settings object
2206 * @returns {int} i the number of visible columns
2207 * @memberof DataTable#oApi
2209 function _fnVisbleColumns( oSettings )
2213 // No reduce in IE8, use a loop for now
2214 $.each( oSettings.aoColumns, function ( i, col ) {
2215 if ( col.bVisible && $(col.nTh).css('display') !== 'none' ) {
2225 * Get an array of column indexes that match a given property
2226 * @param {object} oSettings dataTables settings object
2227 * @param {string} sParam Parameter in aoColumns to look for - typically
2228 * bVisible or bSearchable
2229 * @returns {array} Array of indexes with matched properties
2230 * @memberof DataTable#oApi
2232 function _fnGetColumns( oSettings, sParam )
2236 $.map( oSettings.aoColumns, function(val, i) {
2237 if ( val[sParam] ) {
2247 * Calculate the 'type' of a column
2248 * @param {object} settings dataTables settings object
2249 * @memberof DataTable#oApi
2251 function _fnColumnTypes ( settings )
2253 var columns = settings.aoColumns;
2254 var data = settings.aoData;
2255 var types = DataTable.ext.type.detect;
2256 var i, ien, j, jen, k, ken;
2257 var col, cell, detectedType, cache;
2259 // For each column, spin over the
2260 for ( i=0, ien=columns.length ; i<ien ; i++ ) {
2264 if ( ! col.sType && col._sManualType ) {
2265 col.sType = col._sManualType;
2267 else if ( ! col.sType ) {
2268 for ( j=0, jen=types.length ; j<jen ; j++ ) {
2269 for ( k=0, ken=data.length ; k<ken ; k++ ) {
2270 // Use a cache array so we only need to get the type data
2271 // from the formatter once (when using multiple detectors)
2272 if ( cache[k] === undefined ) {
2273 cache[k] = _fnGetCellData( settings, k, i, 'type' );
2276 detectedType = types[j]( cache[k], settings );
2278 // If null, then this type can't apply to this column, so
2279 // rather than testing all cells, break out. There is an
2280 // exception for the last type which is `html`. We need to
2281 // scan all rows since it is possible to mix string and HTML
2283 if ( ! detectedType && j !== types.length-1 ) {
2287 // Only a single match is needed for html type since it is
2288 // bottom of the pile and very similar to string
2289 if ( detectedType === 'html' ) {
2294 // Type is valid for all data points in the column - use this
2296 if ( detectedType ) {
2297 col.sType = detectedType;
2302 // Fall back - if no type was detected, always use string
2303 if ( ! col.sType ) {
2304 col.sType = 'string';
2312 * Take the column definitions and static columns arrays and calculate how
2313 * they relate to column indexes. The callback function will then apply the
2314 * definition found for a column to a suitable configuration object.
2315 * @param {object} oSettings dataTables settings object
2316 * @param {array} aoColDefs The aoColumnDefs array that is to be applied
2317 * @param {array} aoCols The aoColumns array that defines columns individually
2318 * @param {function} fn Callback function - takes two parameters, the calculated
2319 * column index and the definition for that column.
2320 * @memberof DataTable#oApi
2322 function _fnApplyColumnDefs( oSettings, aoColDefs, aoCols, fn )
2324 var i, iLen, j, jLen, k, kLen, def;
2325 var columns = oSettings.aoColumns;
2327 // Column definitions with aTargets
2330 /* Loop over the definitions array - loop in reverse so first instance has priority */
2331 for ( i=aoColDefs.length-1 ; i>=0 ; i-- )
2335 /* Each definition can target multiple columns, as it is an array */
2336 var aTargets = def.targets !== undefined ?
2340 if ( ! $.isArray( aTargets ) )
2342 aTargets = [ aTargets ];
2345 for ( j=0, jLen=aTargets.length ; j<jLen ; j++ )
2347 if ( typeof aTargets[j] === 'number' && aTargets[j] >= 0 )
2349 /* Add columns that we don't yet know about */
2350 while( columns.length <= aTargets[j] )
2352 _fnAddColumn( oSettings );
2355 /* Integer, basic index */
2356 fn( aTargets[j], def );
2358 else if ( typeof aTargets[j] === 'number' && aTargets[j] < 0 )
2360 /* Negative integer, right to left column counting */
2361 fn( columns.length+aTargets[j], def );
2363 else if ( typeof aTargets[j] === 'string' )
2365 /* Class name matching on TH element */
2366 for ( k=0, kLen=columns.length ; k<kLen ; k++ )
2368 if ( aTargets[j] == "_all" ||
2369 $(columns[k].nTh).hasClass( aTargets[j] ) )
2379 // Statically defined columns array
2382 for ( i=0, iLen=aoCols.length ; i<iLen ; i++ )
2390 * Add a data array to the table, creating DOM node etc. This is the parallel to
2391 * _fnGatherData, but for adding rows from a Javascript source, rather than a
2393 * @param {object} oSettings dataTables settings object
2394 * @param {array} aData data array to be added
2395 * @param {node} [nTr] TR element to add to the table - optional. If not given,
2396 * DataTables will create a row automatically
2397 * @param {array} [anTds] Array of TD|TH elements for the row - must be given
2399 * @returns {int} >=0 if successful (index of new aoData entry), -1 if failed
2400 * @memberof DataTable#oApi
2402 function _fnAddData ( oSettings, aDataIn, nTr, anTds )
2404 /* Create the object for storing information about this new row */
2405 var iRow = oSettings.aoData.length;
2406 var oData = $.extend( true, {}, DataTable.models.oRow, {
2407 src: nTr ? 'dom' : 'data',
2411 oData._aData = aDataIn;
2412 oSettings.aoData.push( oData );
2414 /* Create the cells */
2416 var columns = oSettings.aoColumns;
2418 // Invalidate the column types as the new data needs to be revalidated
2419 for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
2421 columns[i].sType = null;
2424 /* Add to the display array */
2425 oSettings.aiDisplayMaster.push( iRow );
2427 var id = oSettings.rowIdFn( aDataIn );
2428 if ( id !== undefined ) {
2429 oSettings.aIds[ id ] = oData;
2432 /* Create the DOM information, or register it if already present */
2433 if ( nTr || ! oSettings.oFeatures.bDeferRender )
2435 _fnCreateTr( oSettings, iRow, nTr, anTds );
2443 * Add one or more TR elements to the table. Generally we'd expect to
2444 * use this for reading data from a DOM sourced table, but it could be
2445 * used for an TR element. Note that if a TR is given, it is used (i.e.
2446 * it is not cloned).
2447 * @param {object} settings dataTables settings object
2448 * @param {array|node|jQuery} trs The TR element(s) to add to the table
2449 * @returns {array} Array of indexes for the added rows
2450 * @memberof DataTable#oApi
2452 function _fnAddTr( settings, trs )
2456 // Allow an individual node to be passed in
2457 if ( ! (trs instanceof $) ) {
2461 return trs.map( function (i, el) {
2462 row = _fnGetRowElements( settings, el );
2463 return _fnAddData( settings, row.data, el, row.cells );
2469 * Take a TR element and convert it to an index in aoData
2470 * @param {object} oSettings dataTables settings object
2471 * @param {node} n the TR element to find
2472 * @returns {int} index if the node is found, null if not
2473 * @memberof DataTable#oApi
2475 function _fnNodeToDataIndex( oSettings, n )
2477 return (n._DT_RowIndex!==undefined) ? n._DT_RowIndex : null;
2482 * Take a TD element and convert it into a column data index (not the visible index)
2483 * @param {object} oSettings dataTables settings object
2484 * @param {int} iRow The row number the TD/TH can be found in
2485 * @param {node} n The TD/TH element to find
2486 * @returns {int} index if the node is found, -1 if not
2487 * @memberof DataTable#oApi
2489 function _fnNodeToColumnIndex( oSettings, iRow, n )
2491 return $.inArray( n, oSettings.aoData[ iRow ].anCells );
2496 * Get the data for a given cell from the internal cache, taking into account data mapping
2497 * @param {object} settings dataTables settings object
2498 * @param {int} rowIdx aoData row id
2499 * @param {int} colIdx Column index
2500 * @param {string} type data get type ('display', 'type' 'filter' 'sort')
2501 * @returns {*} Cell data
2502 * @memberof DataTable#oApi
2504 function _fnGetCellData( settings, rowIdx, colIdx, type )
2506 var draw = settings.iDraw;
2507 var col = settings.aoColumns[colIdx];
2508 var rowData = settings.aoData[rowIdx]._aData;
2509 var defaultContent = col.sDefaultContent;
2510 var cellData = col.fnGetData( rowData, type, {
2516 if ( cellData === undefined ) {
2517 if ( settings.iDrawError != draw && defaultContent === null ) {
2518 _fnLog( settings, 0, "Requested unknown parameter "+
2519 (typeof col.mData=='function' ? '{function}' : "'"+col.mData+"'")+
2520 " for row "+rowIdx+", column "+colIdx, 4 );
2521 settings.iDrawError = draw;
2523 return defaultContent;
2526 // When the data source is null and a specific data type is requested (i.e.
2527 // not the original data), we can use default column data
2528 if ( (cellData === rowData || cellData === null) && defaultContent !== null && type !== undefined ) {
2529 cellData = defaultContent;
2531 else if ( typeof cellData === 'function' ) {
2532 // If the data source is a function, then we run it and use the return,
2533 // executing in the scope of the data object (for instances)
2534 return cellData.call( rowData );
2537 if ( cellData === null && type == 'display' ) {
2545 * Set the value for a specific cell, into the internal data cache
2546 * @param {object} settings dataTables settings object
2547 * @param {int} rowIdx aoData row id
2548 * @param {int} colIdx Column index
2549 * @param {*} val Value to set
2550 * @memberof DataTable#oApi
2552 function _fnSetCellData( settings, rowIdx, colIdx, val )
2554 var col = settings.aoColumns[colIdx];
2555 var rowData = settings.aoData[rowIdx]._aData;
2557 col.fnSetData( rowData, val, {
2565 // Private variable that is used to match action syntax in the data property object
2566 var __reArray = /\[.*?\]$/;
2567 var __reFn = /\(\)$/;
2570 * Split string on periods, taking into account escaped periods
2571 * @param {string} str String to split
2572 * @return {array} Split string
2574 function _fnSplitObjNotation( str )
2576 return $.map( str.match(/(\\.|[^\.])+/g) || [''], function ( s ) {
2577 return s.replace(/\\\./g, '.');
2583 * Return a function that can be used to get data from a source object, taking
2584 * into account the ability to use nested objects as a source
2585 * @param {string|int|function} mSource The data source for the object
2586 * @returns {function} Data get function
2587 * @memberof DataTable#oApi
2589 function _fnGetObjectDataFn( mSource )
2591 if ( $.isPlainObject( mSource ) )
2593 /* Build an object of get functions, and wrap them in a single call */
2595 $.each( mSource, function (key, val) {
2597 o[key] = _fnGetObjectDataFn( val );
2601 return function (data, type, row, meta) {
2602 var t = o[type] || o._;
2603 return t !== undefined ?
2604 t(data, type, row, meta) :
2608 else if ( mSource === null )
2610 /* Give an empty string for rendering / sorting etc */
2611 return function (data) { // type, row and meta also passed, but not used
2615 else if ( typeof mSource === 'function' )
2617 return function (data, type, row, meta) {
2618 return mSource( data, type, row, meta );
2621 else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 ||
2622 mSource.indexOf('[') !== -1 || mSource.indexOf('(') !== -1) )
2624 /* If there is a . in the source string then the data source is in a
2625 * nested object so we loop over the data for each level to get the next
2626 * level down. On each loop we test for undefined, and if found immediately
2627 * return. This allows entire objects to be missing and sDefaultContent to
2628 * be used if defined, rather than throwing an error
2630 var fetchData = function (data, type, src) {
2631 var arrayNotation, funcNotation, out, innerSrc;
2635 var a = _fnSplitObjNotation( src );
2637 for ( var i=0, iLen=a.length ; i<iLen ; i++ )
2639 // Check if we are dealing with special notation
2640 arrayNotation = a[i].match(__reArray);
2641 funcNotation = a[i].match(__reFn);
2643 if ( arrayNotation )
2646 a[i] = a[i].replace(__reArray, '');
2648 // Condition allows simply [] to be passed in
2649 if ( a[i] !== "" ) {
2650 data = data[ a[i] ];
2654 // Get the remainder of the nested object to get
2656 innerSrc = a.join('.');
2658 // Traverse each entry in the array getting the properties requested
2659 if ( $.isArray( data ) ) {
2660 for ( var j=0, jLen=data.length ; j<jLen ; j++ ) {
2661 out.push( fetchData( data[j], type, innerSrc ) );
2665 // If a string is given in between the array notation indicators, that
2666 // is used to join the strings together, otherwise an array is returned
2667 var join = arrayNotation[0].substring(1, arrayNotation[0].length-1);
2668 data = (join==="") ? out : out.join(join);
2670 // The inner call to fetchData has already traversed through the remainder
2671 // of the source requested, so we exit from the loop
2674 else if ( funcNotation )
2677 a[i] = a[i].replace(__reFn, '');
2678 data = data[ a[i] ]();
2682 if ( data === null || data[ a[i] ] === undefined )
2686 data = data[ a[i] ];
2693 return function (data, type) { // row and meta also passed, but not used
2694 return fetchData( data, type, mSource );
2699 /* Array or flat object mapping */
2700 return function (data, type) { // row and meta also passed, but not used
2701 return data[mSource];
2708 * Return a function that can be used to set data from a source object, taking
2709 * into account the ability to use nested objects as a source
2710 * @param {string|int|function} mSource The data source for the object
2711 * @returns {function} Data set function
2712 * @memberof DataTable#oApi
2714 function _fnSetObjectDataFn( mSource )
2716 if ( $.isPlainObject( mSource ) )
2718 /* Unlike get, only the underscore (global) option is used for for
2719 * setting data since we don't know the type here. This is why an object
2720 * option is not documented for `mData` (which is read/write), but it is
2721 * for `mRender` which is read only.
2723 return _fnSetObjectDataFn( mSource._ );
2725 else if ( mSource === null )
2727 /* Nothing to do when the data source is null */
2728 return function () {};
2730 else if ( typeof mSource === 'function' )
2732 return function (data, val, meta) {
2733 mSource( data, 'set', val, meta );
2736 else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 ||
2737 mSource.indexOf('[') !== -1 || mSource.indexOf('(') !== -1) )
2739 /* Like the get, we need to get data from a nested object */
2740 var setData = function (data, val, src) {
2741 var a = _fnSplitObjNotation( src ), b;
2742 var aLast = a[a.length-1];
2743 var arrayNotation, funcNotation, o, innerSrc;
2745 for ( var i=0, iLen=a.length-1 ; i<iLen ; i++ )
2747 // Check if we are dealing with an array notation request
2748 arrayNotation = a[i].match(__reArray);
2749 funcNotation = a[i].match(__reFn);
2751 if ( arrayNotation )
2753 a[i] = a[i].replace(__reArray, '');
2756 // Get the remainder of the nested object to set so we can recurse
2759 innerSrc = b.join('.');
2761 // Traverse each entry in the array setting the properties requested
2762 if ( $.isArray( val ) )
2764 for ( var j=0, jLen=val.length ; j<jLen ; j++ )
2767 setData( o, val[j], innerSrc );
2768 data[ a[i] ].push( o );
2773 // We've been asked to save data to an array, but it
2774 // isn't array data to be saved. Best that can be done
2775 // is to just save the value.
2779 // The inner call to setData has already traversed through the remainder
2780 // of the source and has set the data, thus we can exit here
2783 else if ( funcNotation )
2786 a[i] = a[i].replace(__reFn, '');
2787 data = data[ a[i] ]( val );
2790 // If the nested object doesn't currently exist - since we are
2791 // trying to set the value - create it
2792 if ( data[ a[i] ] === null || data[ a[i] ] === undefined )
2796 data = data[ a[i] ];
2799 // Last item in the input - i.e, the actual set
2800 if ( aLast.match(__reFn ) )
2803 data = data[ aLast.replace(__reFn, '') ]( val );
2807 // If array notation is used, we just want to strip it and use the property name
2808 // and assign the value. If it isn't used, then we get the result we want anyway
2809 data[ aLast.replace(__reArray, '') ] = val;
2813 return function (data, val) { // meta is also passed in, but not used
2814 return setData( data, val, mSource );
2819 /* Array or flat object mapping */
2820 return function (data, val) { // meta is also passed in, but not used
2821 data[mSource] = val;
2828 * Return an array with the full table data
2829 * @param {object} oSettings dataTables settings object
2830 * @returns array {array} aData Master data array
2831 * @memberof DataTable#oApi
2833 function _fnGetDataMaster ( settings )
2835 return _pluck( settings.aoData, '_aData' );
2841 * @param {object} oSettings dataTables settings object
2842 * @memberof DataTable#oApi
2844 function _fnClearTable( settings )
2846 settings.aoData.length = 0;
2847 settings.aiDisplayMaster.length = 0;
2848 settings.aiDisplay.length = 0;
2854 * Take an array of integers (index array) and remove a target integer (value - not
2856 * @param {array} a Index array to target
2857 * @param {int} iTarget value to find
2858 * @memberof DataTable#oApi
2860 function _fnDeleteIndex( a, iTarget, splice )
2862 var iTargetIndex = -1;
2864 for ( var i=0, iLen=a.length ; i<iLen ; i++ )
2866 if ( a[i] == iTarget )
2870 else if ( a[i] > iTarget )
2876 if ( iTargetIndex != -1 && splice === undefined )
2878 a.splice( iTargetIndex, 1 );
2884 * Mark cached data as invalid such that a re-read of the data will occur when
2885 * the cached data is next requested. Also update from the data source object.
2887 * @param {object} settings DataTables settings object
2888 * @param {int} rowIdx Row index to invalidate
2889 * @param {string} [src] Source to invalidate from: undefined, 'auto', 'dom'
2891 * @param {int} [colIdx] Column index to invalidate. If undefined the whole
2892 * row will be invalidated
2893 * @memberof DataTable#oApi
2895 * @todo For the modularisation of v1.11 this will need to become a callback, so
2896 * the sort and filter methods can subscribe to it. That will required
2897 * initialisation options for sorting, which is why it is not already baked in
2899 function _fnInvalidate( settings, rowIdx, src, colIdx )
2901 var row = settings.aoData[ rowIdx ];
2903 var cellWrite = function ( cell, col ) {
2904 // This is very frustrating, but in IE if you just write directly
2905 // to innerHTML, and elements that are overwritten are GC'ed,
2906 // even if there is a reference to them elsewhere
2907 while ( cell.childNodes.length ) {
2908 cell.removeChild( cell.firstChild );
2911 cell.innerHTML = _fnGetCellData( settings, rowIdx, col, 'display' );
2914 // Are we reading last data from DOM or the data object?
2915 if ( src === 'dom' || ((! src || src === 'auto') && row.src === 'dom') ) {
2916 // Read the data from the DOM
2917 row._aData = _fnGetRowElements(
2918 settings, row, colIdx, colIdx === undefined ? undefined : row._aData
2923 // Reading from data object, update the DOM
2924 var cells = row.anCells;
2927 if ( colIdx !== undefined ) {
2928 cellWrite( cells[colIdx], colIdx );
2931 for ( i=0, ien=cells.length ; i<ien ; i++ ) {
2932 cellWrite( cells[i], i );
2938 // For both row and cell invalidation, the cached data for sorting and
2939 // filtering is nulled out
2940 row._aSortData = null;
2941 row._aFilterData = null;
2943 // Invalidate the type for a specific column (if given) or all columns since
2944 // the data might have changed
2945 var cols = settings.aoColumns;
2946 if ( colIdx !== undefined ) {
2947 cols[ colIdx ].sType = null;
2950 for ( i=0, ien=cols.length ; i<ien ; i++ ) {
2951 cols[i].sType = null;
2954 // Update DataTables special `DT_*` attributes for the row
2955 _fnRowAttributes( settings, row );
2961 * Build a data source object from an HTML row, reading the contents of the
2962 * cells that are in the row.
2964 * @param {object} settings DataTables settings object
2965 * @param {node|object} TR element from which to read data or existing row
2966 * object from which to re-read the data from the cells
2967 * @param {int} [colIdx] Optional column index
2968 * @param {array|object} [d] Data source object. If `colIdx` is given then this
2969 * parameter should also be given and will be used to write the data into.
2970 * Only the column in question will be written
2971 * @returns {object} Object with two parameters: `data` the data read, in
2972 * document order, and `cells` and array of nodes (they can be useful to the
2973 * caller, so rather than needing a second traversal to get them, just return
2975 * @memberof DataTable#oApi
2977 function _fnGetRowElements( settings, row, colIdx, d )
2981 td = row.firstChild,
2982 name, col, o, i=0, contents,
2983 columns = settings.aoColumns,
2984 objectRead = settings._rowReadObject;
2986 // Allow the data object to be passed in, or construct
2987 d = d !== undefined ?
2993 var attr = function ( str, td ) {
2994 if ( typeof str === 'string' ) {
2995 var idx = str.indexOf('@');
2998 var attr = str.substring( idx+1 );
2999 var setter = _fnSetObjectDataFn( str );
3000 setter( d, td.getAttribute( attr ) );
3005 // Read data from a cell and store into the data object
3006 var cellProcess = function ( cell ) {
3007 if ( colIdx === undefined || colIdx === i ) {
3009 contents = $.trim(cell.innerHTML);
3011 if ( col && col._bAttrSrc ) {
3012 var setter = _fnSetObjectDataFn( col.mData._ );
3013 setter( d, contents );
3015 attr( col.mData.sort, cell );
3016 attr( col.mData.type, cell );
3017 attr( col.mData.filter, cell );
3020 // Depending on the `data` option for the columns the data can
3021 // be read to either an object or an array.
3023 if ( ! col._setter ) {
3024 // Cache the setter function
3025 col._setter = _fnSetObjectDataFn( col.mData );
3027 col._setter( d, contents );
3039 // `tr` element was passed in
3041 name = td.nodeName.toUpperCase();
3043 if ( name == "TD" || name == "TH" ) {
3048 td = td.nextSibling;
3052 // Existing row object passed in
3055 for ( var j=0, jen=tds.length ; j<jen ; j++ ) {
3056 cellProcess( tds[j] );
3060 // Read the ID from the DOM if present
3061 var rowNode = row.firstChild ? row : row.nTr;
3064 var id = rowNode.getAttribute( 'id' );
3067 _fnSetObjectDataFn( settings.rowId )( d, id );
3077 * Create a new TR element (and it's TD children) for a row
3078 * @param {object} oSettings dataTables settings object
3079 * @param {int} iRow Row to consider
3080 * @param {node} [nTrIn] TR element to add to the table - optional. If not given,
3081 * DataTables will create a row automatically
3082 * @param {array} [anTds] Array of TD|TH elements for the row - must be given
3084 * @memberof DataTable#oApi
3086 function _fnCreateTr ( oSettings, iRow, nTrIn, anTds )
3089 row = oSettings.aoData[iRow],
3090 rowData = row._aData,
3095 if ( row.nTr === null )
3097 nTr = nTrIn || document.createElement('tr');
3100 row.anCells = cells;
3102 /* Use a private property on the node to allow reserve mapping from the node
3103 * to the aoData array for fast look up
3105 nTr._DT_RowIndex = iRow;
3107 /* Special parameters can be given by the data source to be used on the row */
3108 _fnRowAttributes( oSettings, row );
3110 /* Process each column */
3111 for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
3113 oCol = oSettings.aoColumns[i];
3114 create = nTrIn ? false : true;
3116 nTd = create ? document.createElement( oCol.sCellType ) : anTds[i];
3117 nTd._DT_CellIndex = {
3124 // Need to create the HTML if new, or if a rendering function is defined
3125 if ( create || ((!nTrIn || oCol.mRender || oCol.mData !== i) &&
3126 (!$.isPlainObject(oCol.mData) || oCol.mData._ !== i+'.display')
3128 nTd.innerHTML = _fnGetCellData( oSettings, iRow, i, 'display' );
3131 /* Add user defined class */
3134 nTd.className += ' '+oCol.sClass;
3137 // Visibility - add or remove as required
3138 if ( oCol.bVisible && ! nTrIn )
3140 nTr.appendChild( nTd );
3142 else if ( ! oCol.bVisible && nTrIn )
3144 nTd.parentNode.removeChild( nTd );
3147 if ( oCol.fnCreatedCell )
3149 oCol.fnCreatedCell.call( oSettings.oInstance,
3150 nTd, _fnGetCellData( oSettings, iRow, i ), rowData, iRow, i
3155 _fnCallbackFire( oSettings, 'aoRowCreatedCallback', null, [nTr, rowData, iRow, cells] );
3158 // Remove once webkit bug 131819 and Chromium bug 365619 have been resolved
3160 row.nTr.setAttribute( 'role', 'row' );
3165 * Add attributes to a row based on the special `DT_*` parameters in a data
3167 * @param {object} settings DataTables settings object
3168 * @param {object} DataTables row object for the row to be modified
3169 * @memberof DataTable#oApi
3171 function _fnRowAttributes( settings, row )
3174 var data = row._aData;
3177 var id = settings.rowIdFn( data );
3183 if ( data.DT_RowClass ) {
3184 // Remove any classes added by DT_RowClass before
3185 var a = data.DT_RowClass.split(' ');
3186 row.__rowc = row.__rowc ?
3187 _unique( row.__rowc.concat( a ) ) :
3191 .removeClass( row.__rowc.join(' ') )
3192 .addClass( data.DT_RowClass );
3195 if ( data.DT_RowAttr ) {
3196 $(tr).attr( data.DT_RowAttr );
3199 if ( data.DT_RowData ) {
3200 $(tr).data( data.DT_RowData );
3207 * Create the HTML header for the table
3208 * @param {object} oSettings dataTables settings object
3209 * @memberof DataTable#oApi
3211 function _fnBuildHead( oSettings )
3213 var i, ien, cell, row, column;
3214 var thead = oSettings.nTHead;
3215 var tfoot = oSettings.nTFoot;
3216 var createHeader = $('th, td', thead).length === 0;
3217 var classes = oSettings.oClasses;
3218 var columns = oSettings.aoColumns;
3220 if ( createHeader ) {
3221 row = $('<tr/>').appendTo( thead );
3224 for ( i=0, ien=columns.length ; i<ien ; i++ ) {
3225 column = columns[i];
3226 cell = $( column.nTh ).addClass( column.sClass );
3228 if ( createHeader ) {
3229 cell.appendTo( row );
3232 // 1.11 move into sorting
3233 if ( oSettings.oFeatures.bSort ) {
3234 cell.addClass( column.sSortingClass );
3236 if ( column.bSortable !== false ) {
3238 .attr( 'tabindex', oSettings.iTabIndex )
3239 .attr( 'aria-controls', oSettings.sTableId );
3241 _fnSortAttachListener( oSettings, column.nTh, i );
3245 if ( column.sTitle != cell[0].innerHTML ) {
3246 cell.html( column.sTitle );
3249 _fnRenderer( oSettings, 'header' )(
3250 oSettings, cell, column, classes
3254 if ( createHeader ) {
3255 _fnDetectHeader( oSettings.aoHeader, thead );
3258 /* ARIA role for the rows */
3259 $(thead).find('>tr').attr('role', 'row');
3261 /* Deal with the footer - add classes if required */
3262 $(thead).find('>tr>th, >tr>td').addClass( classes.sHeaderTH );
3263 $(tfoot).find('>tr>th, >tr>td').addClass( classes.sFooterTH );
3265 // Cache the footer cells. Note that we only take the cells from the first
3266 // row in the footer. If there is more than one row the user wants to
3267 // interact with, they need to use the table().foot() method. Note also this
3268 // allows cells to be used for multiple columns using colspan
3269 if ( tfoot !== null ) {
3270 var cells = oSettings.aoFooter[0];
3272 for ( i=0, ien=cells.length ; i<ien ; i++ ) {
3273 column = columns[i];
3274 column.nTf = cells[i].cell;
3276 if ( column.sClass ) {
3277 $(column.nTf).addClass( column.sClass );
3285 * Draw the header (or footer) element based on the column visibility states. The
3286 * methodology here is to use the layout array from _fnDetectHeader, modified for
3287 * the instantaneous column visibility, to construct the new layout. The grid is
3288 * traversed over cell at a time in a rows x columns grid fashion, although each
3289 * cell insert can cover multiple elements in the grid - which is tracks using the
3290 * aApplied array. Cell inserts in the grid will only occur where there isn't
3291 * already a cell in that position.
3292 * @param {object} oSettings dataTables settings object
3293 * @param array {objects} aoSource Layout array from _fnDetectHeader
3294 * @param {boolean} [bIncludeHidden=false] If true then include the hidden columns in the calc,
3295 * @memberof DataTable#oApi
3297 function _fnDrawHead( oSettings, aoSource, bIncludeHidden )
3299 var i, iLen, j, jLen, k, kLen, n, nLocalTr;
3302 var iColumns = oSettings.aoColumns.length;
3303 var iRowspan, iColspan;
3310 if ( bIncludeHidden === undefined )
3312 bIncludeHidden = false;
3315 /* Make a copy of the master layout array, but without the visible columns in it */
3316 for ( i=0, iLen=aoSource.length ; i<iLen ; i++ )
3318 aoLocal[i] = aoSource[i].slice();
3319 aoLocal[i].nTr = aoSource[i].nTr;
3321 /* Remove any columns which are currently hidden */
3322 for ( j=iColumns-1 ; j>=0 ; j-- )
3324 if ( !oSettings.aoColumns[j].bVisible && !bIncludeHidden )
3326 aoLocal[i].splice( j, 1 );
3330 /* Prep the applied array - it needs an element for each row */
3331 aApplied.push( [] );
3334 for ( i=0, iLen=aoLocal.length ; i<iLen ; i++ )
3336 nLocalTr = aoLocal[i].nTr;
3338 /* All cells are going to be replaced, so empty out the row */
3341 while( (n = nLocalTr.firstChild) )
3343 nLocalTr.removeChild( n );
3347 for ( j=0, jLen=aoLocal[i].length ; j<jLen ; j++ )
3352 /* Check to see if there is already a cell (row/colspan) covering our target
3353 * insert point. If there is, then there is nothing to do.
3355 if ( aApplied[i][j] === undefined )
3357 nLocalTr.appendChild( aoLocal[i][j].cell );
3360 /* Expand the cell to cover as many rows as needed */
3361 while ( aoLocal[i+iRowspan] !== undefined &&
3362 aoLocal[i][j].cell == aoLocal[i+iRowspan][j].cell )
3364 aApplied[i+iRowspan][j] = 1;
3368 /* Expand the cell to cover as many columns as needed */
3369 while ( aoLocal[i][j+iColspan] !== undefined &&
3370 aoLocal[i][j].cell == aoLocal[i][j+iColspan].cell )
3372 /* Must update the applied array over the rows for the columns */
3373 for ( k=0 ; k<iRowspan ; k++ )
3375 aApplied[i+k][j+iColspan] = 1;
3380 /* Do the actual expansion in the DOM */
3381 $(aoLocal[i][j].cell)
3382 .attr('rowspan', iRowspan)
3383 .attr('colspan', iColspan);
3391 * Insert the required TR nodes into the table for display
3392 * @param {object} oSettings dataTables settings object
3393 * @memberof DataTable#oApi
3395 function _fnDraw( oSettings )
3397 /* Provide a pre-callback function which can be used to cancel the draw is false is returned */
3398 var aPreDraw = _fnCallbackFire( oSettings, 'aoPreDrawCallback', 'preDraw', [oSettings] );
3399 if ( $.inArray( false, aPreDraw ) !== -1 )
3401 _fnProcessingDisplay( oSettings, false );
3408 var asStripeClasses = oSettings.asStripeClasses;
3409 var iStripes = asStripeClasses.length;
3410 var iOpenRows = oSettings.aoOpenRows.length;
3411 var oLang = oSettings.oLanguage;
3412 var iInitDisplayStart = oSettings.iInitDisplayStart;
3413 var bServerSide = _fnDataSource( oSettings ) == 'ssp';
3414 var aiDisplay = oSettings.aiDisplay;
3416 oSettings.bDrawing = true;
3418 /* Check and see if we have an initial draw position from state saving */
3419 if ( iInitDisplayStart !== undefined && iInitDisplayStart !== -1 )
3421 oSettings._iDisplayStart = bServerSide ?
3423 iInitDisplayStart >= oSettings.fnRecordsDisplay() ?
3427 oSettings.iInitDisplayStart = -1;
3430 var iDisplayStart = oSettings._iDisplayStart;
3431 var iDisplayEnd = oSettings.fnDisplayEnd();
3433 /* Server-side processing draw intercept */
3434 if ( oSettings.bDeferLoading )
3436 oSettings.bDeferLoading = false;
3438 _fnProcessingDisplay( oSettings, false );
3440 else if ( !bServerSide )
3444 else if ( !oSettings.bDestroying && !_fnAjaxUpdate( oSettings ) )
3449 if ( aiDisplay.length !== 0 )
3451 var iStart = bServerSide ? 0 : iDisplayStart;
3452 var iEnd = bServerSide ? oSettings.aoData.length : iDisplayEnd;
3454 for ( var j=iStart ; j<iEnd ; j++ )
3456 var iDataIndex = aiDisplay[j];
3457 var aoData = oSettings.aoData[ iDataIndex ];
3458 if ( aoData.nTr === null )
3460 _fnCreateTr( oSettings, iDataIndex );
3463 var nRow = aoData.nTr;
3465 /* Remove the old striping classes and then add the new one */
3466 if ( iStripes !== 0 )
3468 var sStripe = asStripeClasses[ iRowCount % iStripes ];
3469 if ( aoData._sRowStripe != sStripe )
3471 $(nRow).removeClass( aoData._sRowStripe ).addClass( sStripe );
3472 aoData._sRowStripe = sStripe;
3476 // Row callback functions - might want to manipulate the row
3477 // iRowCount and j are not currently documented. Are they at all
3479 _fnCallbackFire( oSettings, 'aoRowCallback', null,
3480 [nRow, aoData._aData, iRowCount, j, iDataIndex] );
3482 anRows.push( nRow );
3488 /* Table is empty - create a row with an empty message in it */
3489 var sZero = oLang.sZeroRecords;
3490 if ( oSettings.iDraw == 1 && _fnDataSource( oSettings ) == 'ajax' )
3492 sZero = oLang.sLoadingRecords;
3494 else if ( oLang.sEmptyTable && oSettings.fnRecordsTotal() === 0 )
3496 sZero = oLang.sEmptyTable;
3499 anRows[ 0 ] = $( '<tr/>', { 'class': iStripes ? asStripeClasses[0] : '' } )
3500 .append( $('<td />', {
3502 'colSpan': _fnVisbleColumns( oSettings ),
3503 'class': oSettings.oClasses.sRowEmpty
3504 } ).html( sZero ) )[0];
3507 /* Header and footer callbacks */
3508 _fnCallbackFire( oSettings, 'aoHeaderCallback', 'header', [ $(oSettings.nTHead).children('tr')[0],
3509 _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );
3511 _fnCallbackFire( oSettings, 'aoFooterCallback', 'footer', [ $(oSettings.nTFoot).children('tr')[0],
3512 _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );
3514 var body = $(oSettings.nTBody);
3516 body.children().detach();
3517 body.append( $(anRows) );
3519 /* Call all required callback functions for the end of a draw */
3520 _fnCallbackFire( oSettings, 'aoDrawCallback', 'draw', [oSettings] );
3522 /* Draw is complete, sorting and filtering must be as well */
3523 oSettings.bSorted = false;
3524 oSettings.bFiltered = false;
3525 oSettings.bDrawing = false;
3530 * Redraw the table - taking account of the various features which are enabled
3531 * @param {object} oSettings dataTables settings object
3532 * @param {boolean} [holdPosition] Keep the current paging position. By default
3533 * the paging is reset to the first page
3534 * @memberof DataTable#oApi
3536 function _fnReDraw( settings, holdPosition )
3539 features = settings.oFeatures,
3540 sort = features.bSort,
3541 filter = features.bFilter;
3544 _fnSort( settings );
3548 _fnFilterComplete( settings, settings.oPreviousSearch );
3551 // No filtering, so we want to just use the display master
3552 settings.aiDisplay = settings.aiDisplayMaster.slice();
3555 if ( holdPosition !== true ) {
3556 settings._iDisplayStart = 0;
3559 // Let any modules know about the draw hold position state (used by
3560 // scrolling internally)
3561 settings._drawHold = holdPosition;
3563 _fnDraw( settings );
3565 settings._drawHold = false;
3570 * Add the options to the page HTML for the table
3571 * @param {object} oSettings dataTables settings object
3572 * @memberof DataTable#oApi
3574 function _fnAddOptionsHtml ( oSettings )
3576 var classes = oSettings.oClasses;
3577 var table = $(oSettings.nTable);
3578 var holding = $('<div/>').insertBefore( table ); // Holding element for speed
3579 var features = oSettings.oFeatures;
3581 // All DataTables are wrapped in a div
3582 var insert = $('<div/>', {
3583 id: oSettings.sTableId+'_wrapper',
3584 'class': classes.sWrapper + (oSettings.nTFoot ? '' : ' '+classes.sNoFooter)
3587 oSettings.nHolding = holding[0];
3588 oSettings.nTableWrapper = insert[0];
3589 oSettings.nTableReinsertBefore = oSettings.nTable.nextSibling;
3591 /* Loop over the user set positioning and place the elements as needed */
3592 var aDom = oSettings.sDom.split('');
3593 var featureNode, cOption, nNewNode, cNext, sAttr, j;
3594 for ( var i=0 ; i<aDom.length ; i++ )
3599 if ( cOption == '<' )
3601 /* New container div */
3602 nNewNode = $('<div/>')[0];
3604 /* Check to see if we should append an id and/or a class name to the container */
3606 if ( cNext == "'" || cNext == '"' )
3610 while ( aDom[i+j] != cNext )
3616 /* Replace jQuery UI constants @todo depreciated */
3619 sAttr = classes.sJUIHeader;
3621 else if ( sAttr == "F" )
3623 sAttr = classes.sJUIFooter;
3626 /* The attribute can be in the format of "#id.class", "#id" or "class" This logic
3627 * breaks the string into parts and applies them as needed
3629 if ( sAttr.indexOf('.') != -1 )
3631 var aSplit = sAttr.split('.');
3632 nNewNode.id = aSplit[0].substr(1, aSplit[0].length-1);
3633 nNewNode.className = aSplit[1];
3635 else if ( sAttr.charAt(0) == "#" )
3637 nNewNode.id = sAttr.substr(1, sAttr.length-1);
3641 nNewNode.className = sAttr;
3644 i += j; /* Move along the position array */
3647 insert.append( nNewNode );
3648 insert = $(nNewNode);
3650 else if ( cOption == '>' )
3652 /* End container div */
3653 insert = insert.parent();
3655 // @todo Move options into their own plugins?
3656 else if ( cOption == 'l' && features.bPaginate && features.bLengthChange )
3659 featureNode = _fnFeatureHtmlLength( oSettings );
3661 else if ( cOption == 'f' && features.bFilter )
3664 featureNode = _fnFeatureHtmlFilter( oSettings );
3666 else if ( cOption == 'r' && features.bProcessing )
3669 featureNode = _fnFeatureHtmlProcessing( oSettings );
3671 else if ( cOption == 't' )
3674 featureNode = _fnFeatureHtmlTable( oSettings );
3676 else if ( cOption == 'i' && features.bInfo )
3679 featureNode = _fnFeatureHtmlInfo( oSettings );
3681 else if ( cOption == 'p' && features.bPaginate )
3684 featureNode = _fnFeatureHtmlPaginate( oSettings );
3686 else if ( DataTable.ext.feature.length !== 0 )
3688 /* Plug-in features */
3689 var aoFeatures = DataTable.ext.feature;
3690 for ( var k=0, kLen=aoFeatures.length ; k<kLen ; k++ )
3692 if ( cOption == aoFeatures[k].cFeature )
3694 featureNode = aoFeatures[k].fnInit( oSettings );
3700 /* Add to the 2D features array */
3703 var aanFeatures = oSettings.aanFeatures;
3705 if ( ! aanFeatures[cOption] )
3707 aanFeatures[cOption] = [];
3710 aanFeatures[cOption].push( featureNode );
3711 insert.append( featureNode );
3715 /* Built our DOM structure - replace the holding div with what we want */
3716 holding.replaceWith( insert );
3717 oSettings.nHolding = null;
3722 * Use the DOM source to create up an array of header cells. The idea here is to
3723 * create a layout grid (array) of rows x columns, which contains a reference
3724 * to the cell that that point in the grid (regardless of col/rowspan), such that
3725 * any column / row could be removed and the new grid constructed
3726 * @param array {object} aLayout Array to store the calculated layout in
3727 * @param {node} nThead The header/footer element for the table
3728 * @memberof DataTable#oApi
3730 function _fnDetectHeader ( aLayout, nThead )
3732 var nTrs = $(nThead).children('tr');
3734 var i, k, l, iLen, jLen, iColShifted, iColumn, iColspan, iRowspan;
3736 var fnShiftCol = function ( a, i, j ) {
3744 aLayout.splice( 0, aLayout.length );
3746 /* We know how many rows there are in the layout - so prep it */
3747 for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
3752 /* Calculate a layout array */
3753 for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
3758 /* For every cell in the row... */
3759 nCell = nTr.firstChild;
3761 if ( nCell.nodeName.toUpperCase() == "TD" ||
3762 nCell.nodeName.toUpperCase() == "TH" )
3764 /* Get the col and rowspan attributes from the DOM and sanitise them */
3765 iColspan = nCell.getAttribute('colspan') * 1;
3766 iRowspan = nCell.getAttribute('rowspan') * 1;
3767 iColspan = (!iColspan || iColspan===0 || iColspan===1) ? 1 : iColspan;
3768 iRowspan = (!iRowspan || iRowspan===0 || iRowspan===1) ? 1 : iRowspan;
3770 /* There might be colspan cells already in this row, so shift our target
3773 iColShifted = fnShiftCol( aLayout, i, iColumn );
3775 /* Cache calculation for unique columns */
3776 bUnique = iColspan === 1 ? true : false;
3778 /* If there is col / rowspan, copy the information into the layout grid */
3779 for ( l=0 ; l<iColspan ; l++ )
3781 for ( k=0 ; k<iRowspan ; k++ )
3783 aLayout[i+k][iColShifted+l] = {
3787 aLayout[i+k].nTr = nTr;
3791 nCell = nCell.nextSibling;
3798 * Get an array of unique th elements, one for each column
3799 * @param {object} oSettings dataTables settings object
3800 * @param {node} nHeader automatically detect the layout from this node - optional
3801 * @param {array} aLayout thead/tfoot layout from _fnDetectHeader - optional
3802 * @returns array {node} aReturn list of unique th's
3803 * @memberof DataTable#oApi
3805 function _fnGetUniqueThs ( oSettings, nHeader, aLayout )
3810 aLayout = oSettings.aoHeader;
3814 _fnDetectHeader( aLayout, nHeader );
3818 for ( var i=0, iLen=aLayout.length ; i<iLen ; i++ )
3820 for ( var j=0, jLen=aLayout[i].length ; j<jLen ; j++ )
3822 if ( aLayout[i][j].unique &&
3823 (!aReturn[j] || !oSettings.bSortCellsTop) )
3825 aReturn[j] = aLayout[i][j].cell;
3834 * Create an Ajax call based on the table's settings, taking into account that
3835 * parameters can have multiple forms, and backwards compatibility.
3837 * @param {object} oSettings dataTables settings object
3838 * @param {array} data Data to send to the server, required by
3839 * DataTables - may be augmented by developer callbacks
3840 * @param {function} fn Callback function to run when data is obtained
3842 function _fnBuildAjax( oSettings, data, fn )
3844 // Compatibility with 1.9-, allow fnServerData and event to manipulate
3845 _fnCallbackFire( oSettings, 'aoServerParams', 'serverParams', [data] );
3847 // Convert to object based for 1.10+ if using the old array scheme which can
3848 // come from server-side processing or serverParams
3849 if ( data && $.isArray(data) ) {
3851 var rbracket = /(.*?)\[\]$/;
3853 $.each( data, function (key, val) {
3854 var match = val.name.match(rbracket);
3857 // Support for arrays
3858 var name = match[0];
3860 if ( ! tmp[ name ] ) {
3863 tmp[ name ].push( val.value );
3866 tmp[val.name] = val.value;
3873 var ajax = oSettings.ajax;
3874 var instance = oSettings.oInstance;
3875 var callback = function ( json ) {
3876 _fnCallbackFire( oSettings, null, 'xhr', [oSettings, json, oSettings.jqXHR] );
3880 if ( $.isPlainObject( ajax ) && ajax.data )
3882 ajaxData = ajax.data;
3884 var newData = typeof ajaxData === 'function' ?
3885 ajaxData( data, oSettings ) : // fn can manipulate data or return
3886 ajaxData; // an object object or array to merge
3888 // If the function returned something, use that alone
3889 data = typeof ajaxData === 'function' && newData ?
3891 $.extend( true, data, newData );
3893 // Remove the data property as we've resolved it already and don't want
3894 // jQuery to do it again (it is restored at the end of the function)
3900 "success": function (json) {
3901 var error = json.error || json.sError;
3903 _fnLog( oSettings, 0, error );
3906 oSettings.json = json;
3911 "type": oSettings.sServerMethod,
3912 "error": function (xhr, error, thrown) {
3913 var ret = _fnCallbackFire( oSettings, null, 'xhr', [oSettings, null, oSettings.jqXHR] );
3915 if ( $.inArray( true, ret ) === -1 ) {
3916 if ( error == "parsererror" ) {
3917 _fnLog( oSettings, 0, 'Invalid JSON response', 1 );
3919 else if ( xhr.readyState === 4 ) {
3920 _fnLog( oSettings, 0, 'Ajax error', 7 );
3924 _fnProcessingDisplay( oSettings, false );
3928 // Store the data submitted for the API
3929 oSettings.oAjaxData = data;
3931 // Allow plug-ins and external processes to modify the data
3932 _fnCallbackFire( oSettings, null, 'preXhr', [oSettings, data] );
3934 if ( oSettings.fnServerData )
3936 // DataTables 1.9- compatibility
3937 oSettings.fnServerData.call( instance,
3938 oSettings.sAjaxSource,
3939 $.map( data, function (val, key) { // Need to convert back to 1.9 trad format
3940 return { name: key, value: val };
3946 else if ( oSettings.sAjaxSource || typeof ajax === 'string' )
3948 // DataTables 1.9- compatibility
3949 oSettings.jqXHR = $.ajax( $.extend( baseAjax, {
3950 url: ajax || oSettings.sAjaxSource
3953 else if ( typeof ajax === 'function' )
3955 // Is a function - let the caller define what needs to be done
3956 oSettings.jqXHR = ajax.call( instance, data, callback, oSettings );
3960 // Object to extend the base settings
3961 oSettings.jqXHR = $.ajax( $.extend( baseAjax, ajax ) );
3963 // Restore for next time around
3964 ajax.data = ajaxData;
3970 * Update the table using an Ajax call
3971 * @param {object} settings dataTables settings object
3972 * @returns {boolean} Block the table drawing or not
3973 * @memberof DataTable#oApi
3975 function _fnAjaxUpdate( settings )
3977 if ( settings.bAjaxDataGet ) {
3979 _fnProcessingDisplay( settings, true );
3983 _fnAjaxParameters( settings ),
3985 _fnAjaxUpdateDraw( settings, json );
3996 * Build up the parameters in an object needed for a server-side processing
3997 * request. Note that this is basically done twice, is different ways - a modern
3998 * method which is used by default in DataTables 1.10 which uses objects and
3999 * arrays, or the 1.9- method with is name / value pairs. 1.9 method is used if
4000 * the sAjaxSource option is used in the initialisation, or the legacyAjax
4002 * @param {object} oSettings dataTables settings object
4003 * @returns {bool} block the table drawing or not
4004 * @memberof DataTable#oApi
4006 function _fnAjaxParameters( settings )
4009 columns = settings.aoColumns,
4010 columnCount = columns.length,
4011 features = settings.oFeatures,
4012 preSearch = settings.oPreviousSearch,
4013 preColSearch = settings.aoPreSearchCols,
4014 i, data = [], dataProp, column, columnSearch,
4015 sort = _fnSortFlatten( settings ),
4016 displayStart = settings._iDisplayStart,
4017 displayLength = features.bPaginate !== false ?
4018 settings._iDisplayLength :
4021 var param = function ( name, value ) {
4022 data.push( { 'name': name, 'value': value } );
4025 // DataTables 1.9- compatible method
4026 param( 'sEcho', settings.iDraw );
4027 param( 'iColumns', columnCount );
4028 param( 'sColumns', _pluck( columns, 'sName' ).join(',') );
4029 param( 'iDisplayStart', displayStart );
4030 param( 'iDisplayLength', displayLength );
4032 // DataTables 1.10+ method
4034 draw: settings.iDraw,
4037 start: displayStart,
4038 length: displayLength,
4040 value: preSearch.sSearch,
4041 regex: preSearch.bRegex
4045 for ( i=0 ; i<columnCount ; i++ ) {
4046 column = columns[i];
4047 columnSearch = preColSearch[i];
4048 dataProp = typeof column.mData=="function" ? 'function' : column.mData ;
4053 searchable: column.bSearchable,
4054 orderable: column.bSortable,
4056 value: columnSearch.sSearch,
4057 regex: columnSearch.bRegex
4061 param( "mDataProp_"+i, dataProp );
4063 if ( features.bFilter ) {
4064 param( 'sSearch_'+i, columnSearch.sSearch );
4065 param( 'bRegex_'+i, columnSearch.bRegex );
4066 param( 'bSearchable_'+i, column.bSearchable );
4069 if ( features.bSort ) {
4070 param( 'bSortable_'+i, column.bSortable );
4074 if ( features.bFilter ) {
4075 param( 'sSearch', preSearch.sSearch );
4076 param( 'bRegex', preSearch.bRegex );
4079 if ( features.bSort ) {
4080 $.each( sort, function ( i, val ) {
4081 d.order.push( { column: val.col, dir: val.dir } );
4083 param( 'iSortCol_'+i, val.col );
4084 param( 'sSortDir_'+i, val.dir );
4087 param( 'iSortingCols', sort.length );
4090 // If the legacy.ajax parameter is null, then we automatically decide which
4091 // form to use, based on sAjaxSource
4092 var legacy = DataTable.ext.legacy.ajax;
4093 if ( legacy === null ) {
4094 return settings.sAjaxSource ? data : d;
4097 // Otherwise, if legacy has been specified then we use that to decide on the
4099 return legacy ? data : d;
4104 * Data the data from the server (nuking the old) and redraw the table
4105 * @param {object} oSettings dataTables settings object
4106 * @param {object} json json data return from the server.
4107 * @param {string} json.sEcho Tracking flag for DataTables to match requests
4108 * @param {int} json.iTotalRecords Number of records in the data set, not accounting for filtering
4109 * @param {int} json.iTotalDisplayRecords Number of records in the data set, accounting for filtering
4110 * @param {array} json.aaData The data to display on this page
4111 * @param {string} [json.sColumns] Column ordering (sName, comma separated)
4112 * @memberof DataTable#oApi
4114 function _fnAjaxUpdateDraw ( settings, json )
4116 // v1.10 uses camelCase variables, while 1.9 uses Hungarian notation.
4118 var compat = function ( old, modern ) {
4119 return json[old] !== undefined ? json[old] : json[modern];
4122 var data = _fnAjaxDataSrc( settings, json );
4123 var draw = compat( 'sEcho', 'draw' );
4124 var recordsTotal = compat( 'iTotalRecords', 'recordsTotal' );
4125 var recordsFiltered = compat( 'iTotalDisplayRecords', 'recordsFiltered' );
4127 if ( draw !== undefined ) {
4128 // Protect against out of sequence returns
4129 if ( draw*1 < settings.iDraw ) {
4132 settings.iDraw = draw * 1;
4135 _fnClearTable( settings );
4136 settings._iRecordsTotal = parseInt(recordsTotal, 10);
4137 settings._iRecordsDisplay = parseInt(recordsFiltered, 10);
4139 for ( var i=0, ien=data.length ; i<ien ; i++ ) {
4140 _fnAddData( settings, data[i] );
4142 settings.aiDisplay = settings.aiDisplayMaster.slice();
4144 settings.bAjaxDataGet = false;
4145 _fnDraw( settings );
4147 if ( ! settings._bInitComplete ) {
4148 _fnInitComplete( settings, json );
4151 settings.bAjaxDataGet = true;
4152 _fnProcessingDisplay( settings, false );
4157 * Get the data from the JSON data source to use for drawing a table. Using
4158 * `_fnGetObjectDataFn` allows the data to be sourced from a property of the
4159 * source object, or from a processing function.
4160 * @param {object} oSettings dataTables settings object
4161 * @param {object} json Data source object / array from the server
4162 * @return {array} Array of data to use
4164 function _fnAjaxDataSrc ( oSettings, json )
4166 var dataSrc = $.isPlainObject( oSettings.ajax ) && oSettings.ajax.dataSrc !== undefined ?
4167 oSettings.ajax.dataSrc :
4168 oSettings.sAjaxDataProp; // Compatibility with 1.9-.
4170 // Compatibility with 1.9-. In order to read from aaData, check if the
4171 // default has been changed, if not, check for aaData
4172 if ( dataSrc === 'data' ) {
4173 return json.aaData || json[dataSrc];
4176 return dataSrc !== "" ?
4177 _fnGetObjectDataFn( dataSrc )( json ) :
4182 * Generate the node required for filtering text
4183 * @returns {node} Filter control element
4184 * @param {object} oSettings dataTables settings object
4185 * @memberof DataTable#oApi
4187 function _fnFeatureHtmlFilter ( settings )
4189 var classes = settings.oClasses;
4190 var tableId = settings.sTableId;
4191 var language = settings.oLanguage;
4192 var previousSearch = settings.oPreviousSearch;
4193 var features = settings.aanFeatures;
4194 var input = '<input type="search" class="'+classes.sFilterInput+'"/>';
4196 var str = language.sSearch;
4197 str = str.match(/_INPUT_/) ?
4198 str.replace('_INPUT_', input) :
4201 var filter = $('<div/>', {
4202 'id': ! features.f ? tableId+'_filter' : null,
4203 'class': classes.sFilter
4205 .append( $('<label/>' ).append( str ) );
4207 var searchFn = function() {
4208 /* Update all other filter input elements for the new display */
4210 var val = !this.value ? "" : this.value; // mental IE8 fix :-(
4212 /* Now do the filter */
4213 if ( val != previousSearch.sSearch ) {
4214 _fnFilterComplete( settings, {
4216 "bRegex": previousSearch.bRegex,
4217 "bSmart": previousSearch.bSmart ,
4218 "bCaseInsensitive": previousSearch.bCaseInsensitive
4221 // Need to redraw, without resorting
4222 settings._iDisplayStart = 0;
4223 _fnDraw( settings );
4227 var searchDelay = settings.searchDelay !== null ?
4228 settings.searchDelay :
4229 _fnDataSource( settings ) === 'ssp' ?
4233 var jqFilter = $('input', filter)
4234 .val( previousSearch.sSearch )
4235 .attr( 'placeholder', language.sSearchPlaceholder )
4237 'keyup.DT search.DT input.DT paste.DT cut.DT',
4239 _fnThrottle( searchFn, searchDelay ) :
4242 .on( 'mouseup', function(e) {
4243 // Edge fix! Edge 17 does not trigger anything other than mouse events when clicking
4244 // on the clear icon (Edge bug 17584515). This is safe in other browsers as `searchFn`
4245 // checks the value to see if it has changed. In other browsers it won't have.
4246 setTimeout( function () {
4247 searchFn.call(jqFilter[0]);
4250 .on( 'keypress.DT', function(e) {
4251 /* Prevent form submission */
4252 if ( e.keyCode == 13 ) {
4256 .attr('aria-controls', tableId);
4258 // Update the input elements whenever the table is filtered
4259 $(settings.nTable).on( 'search.dt.DT', function ( ev, s ) {
4260 if ( settings === s ) {
4261 // IE9 throws an 'unknown error' if document.activeElement is used
4262 // inside an iframe or frame...
4264 if ( jqFilter[0] !== document.activeElement ) {
4265 jqFilter.val( previousSearch.sSearch );
4277 * Filter the table using both the global filter and column based filtering
4278 * @param {object} oSettings dataTables settings object
4279 * @param {object} oSearch search information
4280 * @param {int} [iForce] force a research of the master array (1) or not (undefined or 0)
4281 * @memberof DataTable#oApi
4283 function _fnFilterComplete ( oSettings, oInput, iForce )
4285 var oPrevSearch = oSettings.oPreviousSearch;
4286 var aoPrevSearch = oSettings.aoPreSearchCols;
4287 var fnSaveFilter = function ( oFilter ) {
4288 /* Save the filtering values */
4289 oPrevSearch.sSearch = oFilter.sSearch;
4290 oPrevSearch.bRegex = oFilter.bRegex;
4291 oPrevSearch.bSmart = oFilter.bSmart;
4292 oPrevSearch.bCaseInsensitive = oFilter.bCaseInsensitive;
4294 var fnRegex = function ( o ) {
4295 // Backwards compatibility with the bEscapeRegex option
4296 return o.bEscapeRegex !== undefined ? !o.bEscapeRegex : o.bRegex;
4299 // Resolve any column types that are unknown due to addition or invalidation
4300 // @todo As per sort - can this be moved into an event handler?
4301 _fnColumnTypes( oSettings );
4303 /* In server-side processing all filtering is done by the server, so no point hanging around here */
4304 if ( _fnDataSource( oSettings ) != 'ssp' )
4307 _fnFilter( oSettings, oInput.sSearch, iForce, fnRegex(oInput), oInput.bSmart, oInput.bCaseInsensitive );
4308 fnSaveFilter( oInput );
4310 /* Now do the individual column filter */
4311 for ( var i=0 ; i<aoPrevSearch.length ; i++ )
4313 _fnFilterColumn( oSettings, aoPrevSearch[i].sSearch, i, fnRegex(aoPrevSearch[i]),
4314 aoPrevSearch[i].bSmart, aoPrevSearch[i].bCaseInsensitive );
4317 /* Custom filtering */
4318 _fnFilterCustom( oSettings );
4322 fnSaveFilter( oInput );
4325 /* Tell the draw function we have been filtering */
4326 oSettings.bFiltered = true;
4327 _fnCallbackFire( oSettings, null, 'search', [oSettings] );
4332 * Apply custom filtering functions
4333 * @param {object} oSettings dataTables settings object
4334 * @memberof DataTable#oApi
4336 function _fnFilterCustom( settings )
4338 var filters = DataTable.ext.search;
4339 var displayRows = settings.aiDisplay;
4342 for ( var i=0, ien=filters.length ; i<ien ; i++ ) {
4345 // Loop over each row and see if it should be included
4346 for ( var j=0, jen=displayRows.length ; j<jen ; j++ ) {
4347 rowIdx = displayRows[ j ];
4348 row = settings.aoData[ rowIdx ];
4350 if ( filters[i]( settings, row._aFilterData, rowIdx, row._aData, j ) ) {
4351 rows.push( rowIdx );
4355 // So the array reference doesn't break set the results into the
4357 displayRows.length = 0;
4358 $.merge( displayRows, rows );
4364 * Filter the table on a per-column basis
4365 * @param {object} oSettings dataTables settings object
4366 * @param {string} sInput string to filter on
4367 * @param {int} iColumn column to filter
4368 * @param {bool} bRegex treat search string as a regular expression or not
4369 * @param {bool} bSmart use smart filtering or not
4370 * @param {bool} bCaseInsensitive Do case insenstive matching or not
4371 * @memberof DataTable#oApi
4373 function _fnFilterColumn ( settings, searchStr, colIdx, regex, smart, caseInsensitive )
4375 if ( searchStr === '' ) {
4381 var display = settings.aiDisplay;
4382 var rpSearch = _fnFilterCreateSearch( searchStr, regex, smart, caseInsensitive );
4384 for ( var i=0 ; i<display.length ; i++ ) {
4385 data = settings.aoData[ display[i] ]._aFilterData[ colIdx ];
4387 if ( rpSearch.test( data ) ) {
4388 out.push( display[i] );
4392 settings.aiDisplay = out;
4397 * Filter the data table based on user input and draw the table
4398 * @param {object} settings dataTables settings object
4399 * @param {string} input string to filter on
4400 * @param {int} force optional - force a research of the master array (1) or not (undefined or 0)
4401 * @param {bool} regex treat as a regular expression or not
4402 * @param {bool} smart perform smart filtering or not
4403 * @param {bool} caseInsensitive Do case insenstive matching or not
4404 * @memberof DataTable#oApi
4406 function _fnFilter( settings, input, force, regex, smart, caseInsensitive )
4408 var rpSearch = _fnFilterCreateSearch( input, regex, smart, caseInsensitive );
4409 var prevSearch = settings.oPreviousSearch.sSearch;
4410 var displayMaster = settings.aiDisplayMaster;
4411 var display, invalidated, i;
4414 // Need to take account of custom filtering functions - always filter
4415 if ( DataTable.ext.search.length !== 0 ) {
4419 // Check if any of the rows were invalidated
4420 invalidated = _fnFilterData( settings );
4422 // If the input is blank - we just want the full data set
4423 if ( input.length <= 0 ) {
4424 settings.aiDisplay = displayMaster.slice();
4427 // New search - start from the master array
4431 prevSearch.length > input.length ||
4432 input.indexOf(prevSearch) !== 0 ||
4433 settings.bSorted // On resort, the display master needs to be
4434 // re-filtered since indexes will have changed
4436 settings.aiDisplay = displayMaster.slice();
4439 // Search the display array
4440 display = settings.aiDisplay;
4442 for ( i=0 ; i<display.length ; i++ ) {
4443 if ( rpSearch.test( settings.aoData[ display[i] ]._sFilterRow ) ) {
4444 filtered.push( display[i] );
4448 settings.aiDisplay = filtered;
4454 * Build a regular expression object suitable for searching a table
4455 * @param {string} sSearch string to search for
4456 * @param {bool} bRegex treat as a regular expression or not
4457 * @param {bool} bSmart perform smart filtering or not
4458 * @param {bool} bCaseInsensitive Do case insensitive matching or not
4459 * @returns {RegExp} constructed object
4460 * @memberof DataTable#oApi
4462 function _fnFilterCreateSearch( search, regex, smart, caseInsensitive )
4466 _fnEscapeRegex( search );
4469 /* For smart filtering we want to allow the search to work regardless of
4470 * word order. We also want double quoted text to be preserved, so word
4471 * order is important - a la google. So this is what we want to
4474 * ^(?=.*?\bone\b)(?=.*?\btwo three\b)(?=.*?\bfour\b).*$
4476 var a = $.map( search.match( /"[^"]+"|[^ ]+/g ) || [''], function ( word ) {
4477 if ( word.charAt(0) === '"' ) {
4478 var m = word.match( /^"(.*)"$/ );
4479 word = m ? m[1] : word;
4482 return word.replace('"', '');
4485 search = '^(?=.*?'+a.join( ')(?=.*?' )+').*$';
4488 return new RegExp( search, caseInsensitive ? 'i' : '' );
4493 * Escape a string such that it can be used in a regular expression
4494 * @param {string} sVal string to escape
4495 * @returns {string} escaped string
4496 * @memberof DataTable#oApi
4498 var _fnEscapeRegex = DataTable.util.escapeRegex;
4500 var __filter_div = $('<div>')[0];
4501 var __filter_div_textContent = __filter_div.textContent !== undefined;
4503 // Update the filtering data for each row if needed (by invalidation or first run)
4504 function _fnFilterData ( settings )
4506 var columns = settings.aoColumns;
4508 var i, j, ien, jen, filterData, cellData, row;
4509 var fomatters = DataTable.ext.type.search;
4510 var wasInvalidated = false;
4512 for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
4513 row = settings.aoData[i];
4515 if ( ! row._aFilterData ) {
4518 for ( j=0, jen=columns.length ; j<jen ; j++ ) {
4519 column = columns[j];
4521 if ( column.bSearchable ) {
4522 cellData = _fnGetCellData( settings, i, j, 'filter' );
4524 if ( fomatters[ column.sType ] ) {
4525 cellData = fomatters[ column.sType ]( cellData );
4528 // Search in DataTables 1.10 is string based. In 1.11 this
4529 // should be altered to also allow strict type checking.
4530 if ( cellData === null ) {
4534 if ( typeof cellData !== 'string' && cellData.toString ) {
4535 cellData = cellData.toString();
4542 // If it looks like there is an HTML entity in the string,
4543 // attempt to decode it so sorting works as expected. Note that
4544 // we could use a single line of jQuery to do this, but the DOM
4545 // method used here is much faster http://jsperf.com/html-decode
4546 if ( cellData.indexOf && cellData.indexOf('&') !== -1 ) {
4547 __filter_div.innerHTML = cellData;
4548 cellData = __filter_div_textContent ?
4549 __filter_div.textContent :
4550 __filter_div.innerText;
4553 if ( cellData.replace ) {
4554 cellData = cellData.replace(/[\r\n\u2028]/g, '');
4557 filterData.push( cellData );
4560 row._aFilterData = filterData;
4561 row._sFilterRow = filterData.join(' ');
4562 wasInvalidated = true;
4566 return wasInvalidated;
4571 * Convert from the internal Hungarian notation to camelCase for external
4573 * @param {object} obj Object to convert
4574 * @returns {object} Inverted object
4575 * @memberof DataTable#oApi
4577 function _fnSearchToCamel ( obj )
4580 search: obj.sSearch,
4583 caseInsensitive: obj.bCaseInsensitive
4590 * Convert from camelCase notation to the internal Hungarian. We could use the
4591 * Hungarian convert function here, but this is cleaner
4592 * @param {object} obj Object to convert
4593 * @returns {object} Inverted object
4594 * @memberof DataTable#oApi
4596 function _fnSearchToHung ( obj )
4599 sSearch: obj.search,
4602 bCaseInsensitive: obj.caseInsensitive
4607 * Generate the node required for the info display
4608 * @param {object} oSettings dataTables settings object
4609 * @returns {node} Information element
4610 * @memberof DataTable#oApi
4612 function _fnFeatureHtmlInfo ( settings )
4615 tid = settings.sTableId,
4616 nodes = settings.aanFeatures.i,
4618 'class': settings.oClasses.sInfo,
4619 'id': ! nodes ? tid+'_info' : null
4623 // Update display on each draw
4624 settings.aoDrawCallback.push( {
4625 "fn": _fnUpdateInfo,
4626 "sName": "information"
4630 .attr( 'role', 'status' )
4631 .attr( 'aria-live', 'polite' );
4633 // Table is described by our info div
4634 $(settings.nTable).attr( 'aria-describedby', tid+'_info' );
4642 * Update the information elements in the display
4643 * @param {object} settings dataTables settings object
4644 * @memberof DataTable#oApi
4646 function _fnUpdateInfo ( settings )
4648 /* Show information about the table */
4649 var nodes = settings.aanFeatures.i;
4650 if ( nodes.length === 0 ) {
4655 lang = settings.oLanguage,
4656 start = settings._iDisplayStart+1,
4657 end = settings.fnDisplayEnd(),
4658 max = settings.fnRecordsTotal(),
4659 total = settings.fnRecordsDisplay(),
4664 if ( total !== max ) {
4665 /* Record set after filtering */
4666 out += ' ' + lang.sInfoFiltered;
4669 // Convert the macros
4670 out += lang.sInfoPostFix;
4671 out = _fnInfoMacros( settings, out );
4673 var callback = lang.fnInfoCallback;
4674 if ( callback !== null ) {
4675 out = callback.call( settings.oInstance,
4676 settings, start, end, max, total, out
4680 $(nodes).html( out );
4684 function _fnInfoMacros ( settings, str )
4686 // When infinite scrolling, we are always starting at 1. _iDisplayStart is used only
4689 formatter = settings.fnFormatNumber,
4690 start = settings._iDisplayStart+1,
4691 len = settings._iDisplayLength,
4692 vis = settings.fnRecordsDisplay(),
4696 replace(/_START_/g, formatter.call( settings, start ) ).
4697 replace(/_END_/g, formatter.call( settings, settings.fnDisplayEnd() ) ).
4698 replace(/_MAX_/g, formatter.call( settings, settings.fnRecordsTotal() ) ).
4699 replace(/_TOTAL_/g, formatter.call( settings, vis ) ).
4700 replace(/_PAGE_/g, formatter.call( settings, all ? 1 : Math.ceil( start / len ) ) ).
4701 replace(/_PAGES_/g, formatter.call( settings, all ? 1 : Math.ceil( vis / len ) ) );
4707 * Draw the table for the first time, adding all required features
4708 * @param {object} settings dataTables settings object
4709 * @memberof DataTable#oApi
4711 function _fnInitialise ( settings )
4713 var i, iLen, iAjaxStart=settings.iInitDisplayStart;
4714 var columns = settings.aoColumns, column;
4715 var features = settings.oFeatures;
4716 var deferLoading = settings.bDeferLoading; // value modified by the draw
4718 /* Ensure that the table data is fully initialised */
4719 if ( ! settings.bInitialised ) {
4720 setTimeout( function(){ _fnInitialise( settings ); }, 200 );
4724 /* Show the display HTML options */
4725 _fnAddOptionsHtml( settings );
4727 /* Build and draw the header / footer for the table */
4728 _fnBuildHead( settings );
4729 _fnDrawHead( settings, settings.aoHeader );
4730 _fnDrawHead( settings, settings.aoFooter );
4732 /* Okay to show that something is going on now */
4733 _fnProcessingDisplay( settings, true );
4735 /* Calculate sizes for columns */
4736 if ( features.bAutoWidth ) {
4737 _fnCalculateColumnWidths( settings );
4740 for ( i=0, iLen=columns.length ; i<iLen ; i++ ) {
4741 column = columns[i];
4743 if ( column.sWidth ) {
4744 column.nTh.style.width = _fnStringToCss( column.sWidth );
4748 _fnCallbackFire( settings, null, 'preInit', [settings] );
4750 // If there is default sorting required - let's do it. The sort function
4751 // will do the drawing for us. Otherwise we draw the table regardless of the
4752 // Ajax source - this allows the table to look initialised for Ajax sourcing
4753 // data (show 'loading' message possibly)
4754 _fnReDraw( settings );
4756 // Server-side processing init complete is done by _fnAjaxUpdateDraw
4757 var dataSrc = _fnDataSource( settings );
4758 if ( dataSrc != 'ssp' || deferLoading ) {
4759 // if there is an ajax source load the data
4760 if ( dataSrc == 'ajax' ) {
4761 _fnBuildAjax( settings, [], function(json) {
4762 var aData = _fnAjaxDataSrc( settings, json );
4764 // Got the data - add it to the table
4765 for ( i=0 ; i<aData.length ; i++ ) {
4766 _fnAddData( settings, aData[i] );
4769 // Reset the init display for cookie saving. We've already done
4770 // a filter, and therefore cleared it before. So we need to make
4771 // it appear 'fresh'
4772 settings.iInitDisplayStart = iAjaxStart;
4774 _fnReDraw( settings );
4776 _fnProcessingDisplay( settings, false );
4777 _fnInitComplete( settings, json );
4781 _fnProcessingDisplay( settings, false );
4782 _fnInitComplete( settings );
4789 * Draw the table for the first time, adding all required features
4790 * @param {object} oSettings dataTables settings object
4791 * @param {object} [json] JSON from the server that completed the table, if using Ajax source
4792 * with client-side processing (optional)
4793 * @memberof DataTable#oApi
4795 function _fnInitComplete ( settings, json )
4797 settings._bInitComplete = true;
4799 // When data was added after the initialisation (data or Ajax) we need to
4800 // calculate the column sizing
4801 if ( json || settings.oInit.aaData ) {
4802 _fnAdjustColumnSizing( settings );
4805 _fnCallbackFire( settings, null, 'plugin-init', [settings, json] );
4806 _fnCallbackFire( settings, 'aoInitComplete', 'init', [settings, json] );
4810 function _fnLengthChange ( settings, val )
4812 var len = parseInt( val, 10 );
4813 settings._iDisplayLength = len;
4815 _fnLengthOverflow( settings );
4817 // Fire length change event
4818 _fnCallbackFire( settings, null, 'length', [settings, len] );
4823 * Generate the node required for user display length changing
4824 * @param {object} settings dataTables settings object
4825 * @returns {node} Display length feature node
4826 * @memberof DataTable#oApi
4828 function _fnFeatureHtmlLength ( settings )
4831 classes = settings.oClasses,
4832 tableId = settings.sTableId,
4833 menu = settings.aLengthMenu,
4834 d2 = $.isArray( menu[0] ),
4835 lengths = d2 ? menu[0] : menu,
4836 language = d2 ? menu[1] : menu;
4838 var select = $('<select/>', {
4839 'name': tableId+'_length',
4840 'aria-controls': tableId,
4841 'class': classes.sLengthSelect
4844 for ( var i=0, ien=lengths.length ; i<ien ; i++ ) {
4845 select[0][ i ] = new Option(
4846 typeof language[i] === 'number' ?
4847 settings.fnFormatNumber( language[i] ) :
4853 var div = $('<div><label/></div>').addClass( classes.sLength );
4854 if ( ! settings.aanFeatures.l ) {
4855 div[0].id = tableId+'_length';
4858 div.children().append(
4859 settings.oLanguage.sLengthMenu.replace( '_MENU_', select[0].outerHTML )
4862 // Can't use `select` variable as user might provide their own and the
4863 // reference is broken by the use of outerHTML
4865 .val( settings._iDisplayLength )
4866 .on( 'change.DT', function(e) {
4867 _fnLengthChange( settings, $(this).val() );
4868 _fnDraw( settings );
4871 // Update node value whenever anything changes the table's length
4872 $(settings.nTable).on( 'length.dt.DT', function (e, s, len) {
4873 if ( settings === s ) {
4874 $('select', div).val( len );
4883 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
4884 * Note that most of the paging logic is done in
4885 * DataTable.ext.pager
4889 * Generate the node required for default pagination
4890 * @param {object} oSettings dataTables settings object
4891 * @returns {node} Pagination feature node
4892 * @memberof DataTable#oApi
4894 function _fnFeatureHtmlPaginate ( settings )
4897 type = settings.sPaginationType,
4898 plugin = DataTable.ext.pager[ type ],
4899 modern = typeof plugin === 'function',
4900 redraw = function( settings ) {
4901 _fnDraw( settings );
4903 node = $('<div/>').addClass( settings.oClasses.sPaging + type )[0],
4904 features = settings.aanFeatures;
4907 plugin.fnInit( settings, node, redraw );
4910 /* Add a draw callback for the pagination on first instance, to update the paging display */
4913 node.id = settings.sTableId+'_paginate';
4915 settings.aoDrawCallback.push( {
4916 "fn": function( settings ) {
4919 start = settings._iDisplayStart,
4920 len = settings._iDisplayLength,
4921 visRecords = settings.fnRecordsDisplay(),
4923 page = all ? 0 : Math.ceil( start / len ),
4924 pages = all ? 1 : Math.ceil( visRecords / len ),
4925 buttons = plugin(page, pages),
4928 for ( i=0, ien=features.p.length ; i<ien ; i++ ) {
4929 _fnRenderer( settings, 'pageButton' )(
4930 settings, features.p[i], i, buttons, page, pages
4935 plugin.fnUpdate( settings, redraw );
4938 "sName": "pagination"
4947 * Alter the display settings to change the page
4948 * @param {object} settings DataTables settings object
4949 * @param {string|int} action Paging action to take: "first", "previous",
4950 * "next" or "last" or page number to jump to (integer)
4951 * @param [bool] redraw Automatically draw the update or not
4952 * @returns {bool} true page has changed, false - no change
4953 * @memberof DataTable#oApi
4955 function _fnPageChange ( settings, action, redraw )
4958 start = settings._iDisplayStart,
4959 len = settings._iDisplayLength,
4960 records = settings.fnRecordsDisplay();
4962 if ( records === 0 || len === -1 )
4966 else if ( typeof action === "number" )
4968 start = action * len;
4970 if ( start > records )
4975 else if ( action == "first" )
4979 else if ( action == "previous" )
4990 else if ( action == "next" )
4992 if ( start + len < records )
4997 else if ( action == "last" )
4999 start = Math.floor( (records-1) / len) * len;
5003 _fnLog( settings, 0, "Unknown paging action: "+action, 5 );
5006 var changed = settings._iDisplayStart !== start;
5007 settings._iDisplayStart = start;
5010 _fnCallbackFire( settings, null, 'page', [settings] );
5013 _fnDraw( settings );
5023 * Generate the node required for the processing node
5024 * @param {object} settings dataTables settings object
5025 * @returns {node} Processing element
5026 * @memberof DataTable#oApi
5028 function _fnFeatureHtmlProcessing ( settings )
5030 return $('<div/>', {
5031 'id': ! settings.aanFeatures.r ? settings.sTableId+'_processing' : null,
5032 'class': settings.oClasses.sProcessing
5034 .html( settings.oLanguage.sProcessing )
5035 .insertBefore( settings.nTable )[0];
5040 * Display or hide the processing indicator
5041 * @param {object} settings dataTables settings object
5042 * @param {bool} show Show the processing indicator (true) or not (false)
5043 * @memberof DataTable#oApi
5045 function _fnProcessingDisplay ( settings, show )
5047 if ( settings.oFeatures.bProcessing ) {
5048 $(settings.aanFeatures.r).css( 'display', show ? 'block' : 'none' );
5051 _fnCallbackFire( settings, null, 'processing', [settings, show] );
5055 * Add any control elements for the table - specifically scrolling
5056 * @param {object} settings dataTables settings object
5057 * @returns {node} Node to add to the DOM
5058 * @memberof DataTable#oApi
5060 function _fnFeatureHtmlTable ( settings )
5062 var table = $(settings.nTable);
5064 // Add the ARIA grid role to the table
5065 table.attr( 'role', 'grid' );
5067 // Scrolling from here on in
5068 var scroll = settings.oScroll;
5070 if ( scroll.sX === '' && scroll.sY === '' ) {
5071 return settings.nTable;
5074 var scrollX = scroll.sX;
5075 var scrollY = scroll.sY;
5076 var classes = settings.oClasses;
5077 var caption = table.children('caption');
5078 var captionSide = caption.length ? caption[0]._captionSide : null;
5079 var headerClone = $( table[0].cloneNode(false) );
5080 var footerClone = $( table[0].cloneNode(false) );
5081 var footer = table.children('tfoot');
5082 var _div = '<div/>';
5083 var size = function ( s ) {
5084 return !s ? null : _fnStringToCss( s );
5087 if ( ! footer.length ) {
5092 * The HTML structure that we want to generate in this function is:
5095 * div - scroll head inner
5096 * table - scroll head table
5099 * table - table (master table)
5100 * thead - thead clone for sizing
5103 * div - scroll foot inner
5104 * table - scroll foot table
5107 var scroller = $( _div, { 'class': classes.sScrollWrapper } )
5109 $(_div, { 'class': classes.sScrollHead } )
5112 position: 'relative',
5114 width: scrollX ? size(scrollX) : '100%'
5117 $(_div, { 'class': classes.sScrollHeadInner } )
5119 'box-sizing': 'content-box',
5120 width: scroll.sXInner || '100%'
5125 .css( 'margin-left', 0 )
5126 .append( captionSide === 'top' ? caption : null )
5128 table.children('thead')
5134 $(_div, { 'class': classes.sScrollBody } )
5136 position: 'relative',
5138 width: size( scrollX )
5145 $(_div, { 'class': classes.sScrollFoot } )
5149 width: scrollX ? size(scrollX) : '100%'
5152 $(_div, { 'class': classes.sScrollFootInner } )
5156 .css( 'margin-left', 0 )
5157 .append( captionSide === 'bottom' ? caption : null )
5159 table.children('tfoot')
5166 var children = scroller.children();
5167 var scrollHead = children[0];
5168 var scrollBody = children[1];
5169 var scrollFoot = footer ? children[2] : null;
5171 // When the body is scrolled, then we also want to scroll the headers
5173 $(scrollBody).on( 'scroll.DT', function (e) {
5174 var scrollLeft = this.scrollLeft;
5176 scrollHead.scrollLeft = scrollLeft;
5179 scrollFoot.scrollLeft = scrollLeft;
5184 $(scrollBody).css('max-height', scrollY);
5185 if (! scroll.bCollapse) {
5186 $(scrollBody).css('height', scrollY);
5189 settings.nScrollHead = scrollHead;
5190 settings.nScrollBody = scrollBody;
5191 settings.nScrollFoot = scrollFoot;
5193 // On redraw - align columns
5194 settings.aoDrawCallback.push( {
5195 "fn": _fnScrollDraw,
5196 "sName": "scrolling"
5205 * Update the header, footer and body tables for resizing - i.e. column
5208 * Welcome to the most horrible function DataTables. The process that this
5209 * function follows is basically:
5210 * 1. Re-create the table inside the scrolling div
5211 * 2. Take live measurements from the DOM
5212 * 3. Apply the measurements to align the columns
5215 * @param {object} settings dataTables settings object
5216 * @memberof DataTable#oApi
5218 function _fnScrollDraw ( settings )
5220 // Given that this is such a monster function, a lot of variables are use
5221 // to try and keep the minimised size as small as possible
5223 scroll = settings.oScroll,
5224 scrollX = scroll.sX,
5225 scrollXInner = scroll.sXInner,
5226 scrollY = scroll.sY,
5227 barWidth = scroll.iBarWidth,
5228 divHeader = $(settings.nScrollHead),
5229 divHeaderStyle = divHeader[0].style,
5230 divHeaderInner = divHeader.children('div'),
5231 divHeaderInnerStyle = divHeaderInner[0].style,
5232 divHeaderTable = divHeaderInner.children('table'),
5233 divBodyEl = settings.nScrollBody,
5234 divBody = $(divBodyEl),
5235 divBodyStyle = divBodyEl.style,
5236 divFooter = $(settings.nScrollFoot),
5237 divFooterInner = divFooter.children('div'),
5238 divFooterTable = divFooterInner.children('table'),
5239 header = $(settings.nTHead),
5240 table = $(settings.nTable),
5242 tableStyle = tableEl.style,
5243 footer = settings.nTFoot ? $(settings.nTFoot) : null,
5244 browser = settings.oBrowser,
5245 ie67 = browser.bScrollOversize,
5246 dtHeaderCells = _pluck( settings.aoColumns, 'nTh' ),
5247 headerTrgEls, footerTrgEls,
5248 headerSrcEls, footerSrcEls,
5249 headerCopy, footerCopy,
5250 headerWidths=[], footerWidths=[],
5251 headerContent=[], footerContent=[],
5252 idx, correction, sanityWidth,
5253 zeroOut = function(nSizer) {
5254 var style = nSizer.style;
5255 style.paddingTop = "0";
5256 style.paddingBottom = "0";
5257 style.borderTopWidth = "0";
5258 style.borderBottomWidth = "0";
5262 // If the scrollbar visibility has changed from the last draw, we need to
5263 // adjust the column sizes as the table width will have changed to account
5264 // for the scrollbar
5265 var scrollBarVis = divBodyEl.scrollHeight > divBodyEl.clientHeight;
5267 if ( settings.scrollBarVis !== scrollBarVis && settings.scrollBarVis !== undefined ) {
5268 settings.scrollBarVis = scrollBarVis;
5269 _fnAdjustColumnSizing( settings );
5270 return; // adjust column sizing will call this function again
5273 settings.scrollBarVis = scrollBarVis;
5277 * 1. Re-create the table inside the scrolling div
5280 // Remove the old minimised thead and tfoot elements in the inner table
5281 table.children('thead, tfoot').remove();
5284 footerCopy = footer.clone().prependTo( table );
5285 footerTrgEls = footer.find('tr'); // the original tfoot is in its own table and must be sized
5286 footerSrcEls = footerCopy.find('tr');
5289 // Clone the current header and footer elements and then place it into the inner table
5290 headerCopy = header.clone().prependTo( table );
5291 headerTrgEls = header.find('tr'); // original header is in its own table
5292 headerSrcEls = headerCopy.find('tr');
5293 headerCopy.find('th, td').removeAttr('tabindex');
5297 * 2. Take live measurements from the DOM - do not alter the DOM itself!
5300 // Remove old sizing and apply the calculated column widths
5301 // Get the unique column headers in the newly created (cloned) header. We want to apply the
5302 // calculated sizes to this header
5305 divBodyStyle.width = '100%';
5306 divHeader[0].style.width = '100%';
5309 $.each( _fnGetUniqueThs( settings, headerCopy ), function ( i, el ) {
5310 idx = _fnVisibleToColumnIndex( settings, i );
5311 el.style.width = settings.aoColumns[idx].sWidth;
5315 _fnApplyToChildren( function(n) {
5320 // Size the table as a whole
5321 sanityWidth = table.outerWidth();
5322 if ( scrollX === "" ) {
5324 tableStyle.width = "100%";
5326 // IE7 will make the width of the table when 100% include the scrollbar
5327 // - which is shouldn't. When there is a scrollbar we need to take this
5329 if ( ie67 && (table.find('tbody').height() > divBodyEl.offsetHeight ||
5330 divBody.css('overflow-y') == "scroll")
5332 tableStyle.width = _fnStringToCss( table.outerWidth() - barWidth);
5335 // Recalculate the sanity width
5336 sanityWidth = table.outerWidth();
5338 else if ( scrollXInner !== "" ) {
5339 // legacy x scroll inner has been given - use it
5340 tableStyle.width = _fnStringToCss(scrollXInner);
5342 // Recalculate the sanity width
5343 sanityWidth = table.outerWidth();
5346 // Hidden header should have zero height, so remove padding and borders. Then
5347 // set the width based on the real headers
5349 // Apply all styles in one pass
5350 _fnApplyToChildren( zeroOut, headerSrcEls );
5352 // Read all widths in next pass
5353 _fnApplyToChildren( function(nSizer) {
5354 headerContent.push( nSizer.innerHTML );
5355 headerWidths.push( _fnStringToCss( $(nSizer).css('width') ) );
5358 // Apply all widths in final pass
5359 _fnApplyToChildren( function(nToSize, i) {
5360 // Only apply widths to the DataTables detected header cells - this
5361 // prevents complex headers from having contradictory sizes applied
5362 if ( $.inArray( nToSize, dtHeaderCells ) !== -1 ) {
5363 nToSize.style.width = headerWidths[i];
5367 $(headerSrcEls).height(0);
5369 /* Same again with the footer if we have one */
5372 _fnApplyToChildren( zeroOut, footerSrcEls );
5374 _fnApplyToChildren( function(nSizer) {
5375 footerContent.push( nSizer.innerHTML );
5376 footerWidths.push( _fnStringToCss( $(nSizer).css('width') ) );
5379 _fnApplyToChildren( function(nToSize, i) {
5380 nToSize.style.width = footerWidths[i];
5383 $(footerSrcEls).height(0);
5388 * 3. Apply the measurements
5391 // "Hide" the header and footer that we used for the sizing. We need to keep
5392 // the content of the cell so that the width applied to the header and body
5393 // both match, but we want to hide it completely. We want to also fix their
5394 // width to what they currently are
5395 _fnApplyToChildren( function(nSizer, i) {
5396 nSizer.innerHTML = '<div class="dataTables_sizing">'+headerContent[i]+'</div>';
5397 nSizer.childNodes[0].style.height = "0";
5398 nSizer.childNodes[0].style.overflow = "hidden";
5399 nSizer.style.width = headerWidths[i];
5404 _fnApplyToChildren( function(nSizer, i) {
5405 nSizer.innerHTML = '<div class="dataTables_sizing">'+footerContent[i]+'</div>';
5406 nSizer.childNodes[0].style.height = "0";
5407 nSizer.childNodes[0].style.overflow = "hidden";
5408 nSizer.style.width = footerWidths[i];
5412 // Sanity check that the table is of a sensible width. If not then we are going to get
5413 // misalignment - try to prevent this by not allowing the table to shrink below its min width
5414 if ( table.outerWidth() < sanityWidth )
5416 // The min width depends upon if we have a vertical scrollbar visible or not */
5417 correction = ((divBodyEl.scrollHeight > divBodyEl.offsetHeight ||
5418 divBody.css('overflow-y') == "scroll")) ?
5419 sanityWidth+barWidth :
5422 // IE6/7 are a law unto themselves...
5423 if ( ie67 && (divBodyEl.scrollHeight >
5424 divBodyEl.offsetHeight || divBody.css('overflow-y') == "scroll")
5426 tableStyle.width = _fnStringToCss( correction-barWidth );
5429 // And give the user a warning that we've stopped the table getting too small
5430 if ( scrollX === "" || scrollXInner !== "" ) {
5431 _fnLog( settings, 1, 'Possible column misalignment', 6 );
5436 correction = '100%';
5439 // Apply to the container elements
5440 divBodyStyle.width = _fnStringToCss( correction );
5441 divHeaderStyle.width = _fnStringToCss( correction );
5444 settings.nScrollFoot.style.width = _fnStringToCss( correction );
5452 /* IE7< puts a vertical scrollbar in place (when it shouldn't be) due to subtracting
5453 * the scrollbar height from the visible display, rather than adding it on. We need to
5454 * set the height in order to sort this. Don't want to do it in any other browsers.
5457 divBodyStyle.height = _fnStringToCss( tableEl.offsetHeight+barWidth );
5461 /* Finally set the width's of the header and footer tables */
5462 var iOuterWidth = table.outerWidth();
5463 divHeaderTable[0].style.width = _fnStringToCss( iOuterWidth );
5464 divHeaderInnerStyle.width = _fnStringToCss( iOuterWidth );
5466 // Figure out if there are scrollbar present - if so then we need a the header and footer to
5467 // provide a bit more space to allow "overflow" scrolling (i.e. past the scrollbar)
5468 var bScrolling = table.height() > divBodyEl.clientHeight || divBody.css('overflow-y') == "scroll";
5469 var padding = 'padding' + (browser.bScrollbarLeft ? 'Left' : 'Right' );
5470 divHeaderInnerStyle[ padding ] = bScrolling ? barWidth+"px" : "0px";
5473 divFooterTable[0].style.width = _fnStringToCss( iOuterWidth );
5474 divFooterInner[0].style.width = _fnStringToCss( iOuterWidth );
5475 divFooterInner[0].style[padding] = bScrolling ? barWidth+"px" : "0px";
5478 // Correct DOM ordering for colgroup - comes before the thead
5479 table.children('colgroup').insertBefore( table.children('thead') );
5481 /* Adjust the position of the header in case we loose the y-scrollbar */
5482 divBody.trigger('scroll');
5484 // If sorting or filtering has occurred, jump the scrolling back to the top
5485 // only if we aren't holding the position
5486 if ( (settings.bSorted || settings.bFiltered) && ! settings._drawHold ) {
5487 divBodyEl.scrollTop = 0;
5494 * Apply a given function to the display child nodes of an element array (typically
5495 * TD children of TR rows
5496 * @param {function} fn Method to apply to the objects
5497 * @param array {nodes} an1 List of elements to look through for display children
5498 * @param array {nodes} an2 Another list (identical structure to the first) - optional
5499 * @memberof DataTable#oApi
5501 function _fnApplyToChildren( fn, an1, an2 )
5503 var index=0, i=0, iLen=an1.length;
5506 while ( i < iLen ) {
5507 nNode1 = an1[i].firstChild;
5508 nNode2 = an2 ? an2[i].firstChild : null;
5511 if ( nNode1.nodeType === 1 ) {
5513 fn( nNode1, nNode2, index );
5516 fn( nNode1, index );
5522 nNode1 = nNode1.nextSibling;
5523 nNode2 = an2 ? nNode2.nextSibling : null;
5532 var __re_html_remove = /<.*?>/g;
5536 * Calculate the width of columns for the table
5537 * @param {object} oSettings dataTables settings object
5538 * @memberof DataTable#oApi
5540 function _fnCalculateColumnWidths ( oSettings )
5543 table = oSettings.nTable,
5544 columns = oSettings.aoColumns,
5545 scroll = oSettings.oScroll,
5546 scrollY = scroll.sY,
5547 scrollX = scroll.sX,
5548 scrollXInner = scroll.sXInner,
5549 columnCount = columns.length,
5550 visibleColumns = _fnGetColumns( oSettings, 'bVisible' ),
5551 headerCells = $('th', oSettings.nTHead),
5552 tableWidthAttr = table.getAttribute('width'), // from DOM element
5553 tableContainer = table.parentNode,
5555 i, column, columnIdx, width, outerWidth,
5556 browser = oSettings.oBrowser,
5557 ie67 = browser.bScrollOversize;
5559 var styleWidth = table.style.width;
5560 if ( styleWidth && styleWidth.indexOf('%') !== -1 ) {
5561 tableWidthAttr = styleWidth;
5564 /* Convert any user input sizes into pixel sizes */
5565 for ( i=0 ; i<visibleColumns.length ; i++ ) {
5566 column = columns[ visibleColumns[i] ];
5568 if ( column.sWidth !== null ) {
5569 column.sWidth = _fnConvertToWidth( column.sWidthOrig, tableContainer );
5575 /* If the number of columns in the DOM equals the number that we have to
5576 * process in DataTables, then we can use the offsets that are created by
5577 * the web- browser. No custom sizes can be set in order for this to happen,
5578 * nor scrolling used
5580 if ( ie67 || ! userInputs && ! scrollX && ! scrollY &&
5581 columnCount == _fnVisbleColumns( oSettings ) &&
5582 columnCount == headerCells.length
5584 for ( i=0 ; i<columnCount ; i++ ) {
5585 var colIdx = _fnVisibleToColumnIndex( oSettings, i );
5587 if ( colIdx !== null ) {
5588 columns[ colIdx ].sWidth = _fnStringToCss( headerCells.eq(i).width() );
5594 // Otherwise construct a single row, worst case, table with the widest
5595 // node in the data, assign any user defined widths, then insert it into
5596 // the DOM and allow the browser to do all the hard work of calculating
5598 var tmpTable = $(table).clone() // don't use cloneNode - IE8 will remove events on the main table
5599 .css( 'visibility', 'hidden' )
5600 .removeAttr( 'id' );
5602 // Clean up the table body
5603 tmpTable.find('tbody tr').remove();
5604 var tr = $('<tr/>').appendTo( tmpTable.find('tbody') );
5606 // Clone the table header and footer - we can't use the header / footer
5607 // from the cloned table, since if scrolling is active, the table's
5608 // real header and footer are contained in different table tags
5609 tmpTable.find('thead, tfoot').remove();
5611 .append( $(oSettings.nTHead).clone() )
5612 .append( $(oSettings.nTFoot).clone() );
5614 // Remove any assigned widths from the footer (from scrolling)
5615 tmpTable.find('tfoot th, tfoot td').css('width', '');
5617 // Apply custom sizing to the cloned header
5618 headerCells = _fnGetUniqueThs( oSettings, tmpTable.find('thead')[0] );
5620 for ( i=0 ; i<visibleColumns.length ; i++ ) {
5621 column = columns[ visibleColumns[i] ];
5623 headerCells[i].style.width = column.sWidthOrig !== null && column.sWidthOrig !== '' ?
5624 _fnStringToCss( column.sWidthOrig ) :
5627 // For scrollX we need to force the column width otherwise the
5628 // browser will collapse it. If this width is smaller than the
5629 // width the column requires, then it will have no effect
5630 if ( column.sWidthOrig && scrollX ) {
5631 $( headerCells[i] ).append( $('<div/>').css( {
5632 width: column.sWidthOrig,
5641 // Find the widest cell for each column and put it into the table
5642 if ( oSettings.aoData.length ) {
5643 for ( i=0 ; i<visibleColumns.length ; i++ ) {
5644 columnIdx = visibleColumns[i];
5645 column = columns[ columnIdx ];
5647 $( _fnGetWidestNode( oSettings, columnIdx ) )
5649 .append( column.sContentPadding )
5654 // Tidy the temporary table - remove name attributes so there aren't
5655 // duplicated in the dom (radio elements for example)
5656 $('[name]', tmpTable).removeAttr('name');
5658 // Table has been built, attach to the document so we can work with it.
5659 // A holding element is used, positioned at the top of the container
5660 // with minimal height, so it has no effect on if the container scrolls
5661 // or not. Otherwise it might trigger scrolling when it actually isn't
5663 var holder = $('<div/>').css( scrollX || scrollY ?
5665 position: 'absolute',
5675 .appendTo( tableContainer );
5677 // When scrolling (X or Y) we want to set the width of the table as
5678 // appropriate. However, when not scrolling leave the table width as it
5679 // is. This results in slightly different, but I think correct behaviour
5680 if ( scrollX && scrollXInner ) {
5681 tmpTable.width( scrollXInner );
5683 else if ( scrollX ) {
5684 tmpTable.css( 'width', 'auto' );
5685 tmpTable.removeAttr('width');
5687 // If there is no width attribute or style, then allow the table to
5689 if ( tmpTable.width() < tableContainer.clientWidth && tableWidthAttr ) {
5690 tmpTable.width( tableContainer.clientWidth );
5693 else if ( scrollY ) {
5694 tmpTable.width( tableContainer.clientWidth );
5696 else if ( tableWidthAttr ) {
5697 tmpTable.width( tableWidthAttr );
5700 // Get the width of each column in the constructed table - we need to
5701 // know the inner width (so it can be assigned to the other table's
5702 // cells) and the outer width so we can calculate the full width of the
5703 // table. This is safe since DataTables requires a unique cell for each
5704 // column, but if ever a header can span multiple columns, this will
5705 // need to be modified.
5707 for ( i=0 ; i<visibleColumns.length ; i++ ) {
5708 var cell = $(headerCells[i]);
5709 var border = cell.outerWidth() - cell.width();
5711 // Use getBounding... where possible (not IE8-) because it can give
5712 // sub-pixel accuracy, which we then want to round up!
5713 var bounding = browser.bBounding ?
5714 Math.ceil( headerCells[i].getBoundingClientRect().width ) :
5717 // Total is tracked to remove any sub-pixel errors as the outerWidth
5718 // of the table might not equal the total given here (IE!).
5721 // Width for each column to use
5722 columns[ visibleColumns[i] ].sWidth = _fnStringToCss( bounding - border );
5725 table.style.width = _fnStringToCss( total );
5727 // Finished with the table - ditch it
5731 // If there is a width attr, we want to attach an event listener which
5732 // allows the table sizing to automatically adjust when the window is
5733 // resized. Use the width attr rather than CSS, since we can't know if the
5734 // CSS is a relative value or absolute - DOM read is always px.
5735 if ( tableWidthAttr ) {
5736 table.style.width = _fnStringToCss( tableWidthAttr );
5739 if ( (tableWidthAttr || scrollX) && ! oSettings._reszEvt ) {
5740 var bindResize = function () {
5741 $(window).on('resize.DT-'+oSettings.sInstance, _fnThrottle( function () {
5742 _fnAdjustColumnSizing( oSettings );
5746 // IE6/7 will crash if we bind a resize event handler on page load.
5747 // To be removed in 1.11 which drops IE6/7 support
5749 setTimeout( bindResize, 1000 );
5755 oSettings._reszEvt = true;
5761 * Throttle the calls to a function. Arguments and context are maintained for
5762 * the throttled function
5763 * @param {function} fn Function to be called
5764 * @param {int} [freq=200] call frequency in mS
5765 * @returns {function} wrapped function
5766 * @memberof DataTable#oApi
5768 var _fnThrottle = DataTable.util.throttle;
5772 * Convert a CSS unit width to pixels (e.g. 2em)
5773 * @param {string} width width to be converted
5774 * @param {node} parent parent to get the with for (required for relative widths) - optional
5775 * @returns {int} width in pixels
5776 * @memberof DataTable#oApi
5778 function _fnConvertToWidth ( width, parent )
5785 .css( 'width', _fnStringToCss( width ) )
5786 .appendTo( parent || document.body );
5788 var val = n[0].offsetWidth;
5796 * Get the widest node
5797 * @param {object} settings dataTables settings object
5798 * @param {int} colIdx column of interest
5799 * @returns {node} widest table node
5800 * @memberof DataTable#oApi
5802 function _fnGetWidestNode( settings, colIdx )
5804 var idx = _fnGetMaxLenString( settings, colIdx );
5809 var data = settings.aoData[ idx ];
5810 return ! data.nTr ? // Might not have been created when deferred rendering
5811 $('<td/>').html( _fnGetCellData( settings, idx, colIdx, 'display' ) )[0] :
5812 data.anCells[ colIdx ];
5817 * Get the maximum strlen for each data column
5818 * @param {object} settings dataTables settings object
5819 * @param {int} colIdx column of interest
5820 * @returns {string} max string length for each column
5821 * @memberof DataTable#oApi
5823 function _fnGetMaxLenString( settings, colIdx )
5825 var s, max=-1, maxIdx = -1;
5827 for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
5828 s = _fnGetCellData( settings, i, colIdx, 'display' )+'';
5829 s = s.replace( __re_html_remove, '' );
5830 s = s.replace( / /g, ' ' );
5832 if ( s.length > max ) {
5843 * Append a CSS unit (only if required) to a string
5844 * @param {string} value to css-ify
5845 * @returns {string} value with css unit
5846 * @memberof DataTable#oApi
5848 function _fnStringToCss( s )
5854 if ( typeof s == 'number' ) {
5860 // Check it has a unit character already
5861 return s.match(/\d$/) ?
5868 function _fnSortFlatten ( settings )
5874 aoColumns = settings.aoColumns,
5875 aDataSort, iCol, sType, srcCol,
5876 fixed = settings.aaSortingFixed,
5877 fixedObj = $.isPlainObject( fixed ),
5879 add = function ( a ) {
5880 if ( a.length && ! $.isArray( a[0] ) ) {
5882 nestedSort.push( a );
5886 $.merge( nestedSort, a );
5890 // Build the sort array, with pre-fix and post-fix options if they have been
5892 if ( $.isArray( fixed ) ) {
5896 if ( fixedObj && fixed.pre ) {
5900 add( settings.aaSorting );
5902 if (fixedObj && fixed.post ) {
5906 for ( i=0 ; i<nestedSort.length ; i++ )
5908 srcCol = nestedSort[i][0];
5909 aDataSort = aoColumns[ srcCol ].aDataSort;
5911 for ( k=0, kLen=aDataSort.length ; k<kLen ; k++ )
5913 iCol = aDataSort[k];
5914 sType = aoColumns[ iCol ].sType || 'string';
5916 if ( nestedSort[i]._idx === undefined ) {
5917 nestedSort[i]._idx = $.inArray( nestedSort[i][1], aoColumns[iCol].asSorting );
5923 dir: nestedSort[i][1],
5924 index: nestedSort[i]._idx,
5926 formatter: DataTable.ext.type.order[ sType+"-pre" ]
5935 * Change the order of the table
5936 * @param {object} oSettings dataTables settings object
5937 * @memberof DataTable#oApi
5938 * @todo This really needs split up!
5940 function _fnSort ( oSettings )
5943 i, ien, iLen, j, jLen, k, kLen,
5946 oExtSort = DataTable.ext.type.order,
5947 aoData = oSettings.aoData,
5948 aoColumns = oSettings.aoColumns,
5949 aDataSort, data, iCol, sType, oSort,
5952 displayMaster = oSettings.aiDisplayMaster,
5955 // Resolve any column types that are unknown due to addition or invalidation
5956 // @todo Can this be moved into a 'data-ready' handler which is called when
5957 // data is going to be used in the table?
5958 _fnColumnTypes( oSettings );
5960 aSort = _fnSortFlatten( oSettings );
5962 for ( i=0, ien=aSort.length ; i<ien ; i++ ) {
5965 // Track if we can use the fast sort algorithm
5966 if ( sortCol.formatter ) {
5970 // Load the data needed for the sort, for each cell
5971 _fnSortData( oSettings, sortCol.col );
5974 /* No sorting required if server-side or no sorting array */
5975 if ( _fnDataSource( oSettings ) != 'ssp' && aSort.length !== 0 )
5977 // Create a value - key array of the current row positions such that we can use their
5978 // current position during the sort, if values match, in order to perform stable sorting
5979 for ( i=0, iLen=displayMaster.length ; i<iLen ; i++ ) {
5980 aiOrig[ displayMaster[i] ] = i;
5983 /* Do the sort - here we want multi-column sorting based on a given data source (column)
5984 * and sorting function (from oSort) in a certain direction. It's reasonably complex to
5985 * follow on it's own, but this is what we want (example two column sorting):
5986 * fnLocalSorting = function(a,b){
5988 * iTest = oSort['string-asc']('data11', 'data12');
5991 * iTest = oSort['numeric-desc']('data21', 'data22');
5994 * return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
5996 * Basically we have a test for each sorting column, if the data in that column is equal,
5997 * test the next column. If all columns match, then we use a numeric sort on the row
5998 * positions in the original data array to provide a stable sort.
6000 * Note - I know it seems excessive to have two sorting methods, but the first is around
6001 * 15% faster, so the second is only maintained for backwards compatibility with sorting
6002 * methods which do not have a pre-sort formatting function.
6004 if ( formatters === aSort.length ) {
6005 // All sort types have formatting functions
6006 displayMaster.sort( function ( a, b ) {
6008 x, y, k, test, sort,
6010 dataA = aoData[a]._aSortData,
6011 dataB = aoData[b]._aSortData;
6013 for ( k=0 ; k<len ; k++ ) {
6016 x = dataA[ sort.col ];
6017 y = dataB[ sort.col ];
6019 test = x<y ? -1 : x>y ? 1 : 0;
6021 return sort.dir === 'asc' ? test : -test;
6027 return x<y ? -1 : x>y ? 1 : 0;
6031 // Depreciated - remove in 1.11 (providing a plug-in option)
6032 // Not all sort types have formatting methods, so we have to call their sorting
6034 displayMaster.sort( function ( a, b ) {
6036 x, y, k, l, test, sort, fn,
6038 dataA = aoData[a]._aSortData,
6039 dataB = aoData[b]._aSortData;
6041 for ( k=0 ; k<len ; k++ ) {
6044 x = dataA[ sort.col ];
6045 y = dataB[ sort.col ];
6047 fn = oExtSort[ sort.type+"-"+sort.dir ] || oExtSort[ "string-"+sort.dir ];
6056 return x<y ? -1 : x>y ? 1 : 0;
6061 /* Tell the draw function that we have sorted the data */
6062 oSettings.bSorted = true;
6066 function _fnSortAria ( settings )
6070 var columns = settings.aoColumns;
6071 var aSort = _fnSortFlatten( settings );
6072 var oAria = settings.oLanguage.oAria;
6074 // ARIA attributes - need to loop all columns, to update all (removing old
6075 // attributes as needed)
6076 for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
6078 var col = columns[i];
6079 var asSorting = col.asSorting;
6080 var sTitle = col.sTitle.replace( /<.*?>/g, "" );
6083 // IE7 is throwing an error when setting these properties with jQuery's
6084 // attr() and removeAttr() methods...
6085 th.removeAttribute('aria-sort');
6087 /* In ARIA only the first sorting column can be marked as sorting - no multi-sort option */
6088 if ( col.bSortable ) {
6089 if ( aSort.length > 0 && aSort[0].col == i ) {
6090 th.setAttribute('aria-sort', aSort[0].dir=="asc" ? "ascending" : "descending" );
6091 nextSort = asSorting[ aSort[0].index+1 ] || asSorting[0];
6094 nextSort = asSorting[0];
6097 label = sTitle + ( nextSort === "asc" ?
6098 oAria.sSortAscending :
6099 oAria.sSortDescending
6106 th.setAttribute('aria-label', label);
6112 * Function to run on user sort request
6113 * @param {object} settings dataTables settings object
6114 * @param {node} attachTo node to attach the handler to
6115 * @param {int} colIdx column sorting index
6116 * @param {boolean} [append=false] Append the requested sort to the existing
6117 * sort if true (i.e. multi-column sort)
6118 * @param {function} [callback] callback function
6119 * @memberof DataTable#oApi
6121 function _fnSortListener ( settings, colIdx, append, callback )
6123 var col = settings.aoColumns[ colIdx ];
6124 var sorting = settings.aaSorting;
6125 var asSorting = col.asSorting;
6127 var next = function ( a, overflow ) {
6129 if ( idx === undefined ) {
6130 idx = $.inArray( a[1], asSorting );
6133 return idx+1 < asSorting.length ?
6140 // Convert to 2D array if needed
6141 if ( typeof sorting[0] === 'number' ) {
6142 sorting = settings.aaSorting = [ sorting ];
6145 // If appending the sort then we are multi-column sorting
6146 if ( append && settings.oFeatures.bSortMulti ) {
6147 // Are we already doing some kind of sort on this column?
6148 var sortIdx = $.inArray( colIdx, _pluck(sorting, '0') );
6150 if ( sortIdx !== -1 ) {
6151 // Yes, modify the sort
6152 nextSortIdx = next( sorting[sortIdx], true );
6154 if ( nextSortIdx === null && sorting.length === 1 ) {
6155 nextSortIdx = 0; // can't remove sorting completely
6158 if ( nextSortIdx === null ) {
6159 sorting.splice( sortIdx, 1 );
6162 sorting[sortIdx][1] = asSorting[ nextSortIdx ];
6163 sorting[sortIdx]._idx = nextSortIdx;
6167 // No sort on this column yet
6168 sorting.push( [ colIdx, asSorting[0], 0 ] );
6169 sorting[sorting.length-1]._idx = 0;
6172 else if ( sorting.length && sorting[0][0] == colIdx ) {
6173 // Single column - already sorting on this column, modify the sort
6174 nextSortIdx = next( sorting[0] );
6177 sorting[0][1] = asSorting[ nextSortIdx ];
6178 sorting[0]._idx = nextSortIdx;
6181 // Single column - sort only on this column
6183 sorting.push( [ colIdx, asSorting[0] ] );
6184 sorting[0]._idx = 0;
6187 // Run the sort by calling a full redraw
6188 _fnReDraw( settings );
6190 // callback used for async user interaction
6191 if ( typeof callback == 'function' ) {
6192 callback( settings );
6198 * Attach a sort handler (click) to a node
6199 * @param {object} settings dataTables settings object
6200 * @param {node} attachTo node to attach the handler to
6201 * @param {int} colIdx column sorting index
6202 * @param {function} [callback] callback function
6203 * @memberof DataTable#oApi
6205 function _fnSortAttachListener ( settings, attachTo, colIdx, callback )
6207 var col = settings.aoColumns[ colIdx ];
6209 _fnBindAction( attachTo, {}, function (e) {
6210 /* If the column is not sortable - don't to anything */
6211 if ( col.bSortable === false ) {
6215 // If processing is enabled use a timeout to allow the processing
6216 // display to be shown - otherwise to it synchronously
6217 if ( settings.oFeatures.bProcessing ) {
6218 _fnProcessingDisplay( settings, true );
6220 setTimeout( function() {
6221 _fnSortListener( settings, colIdx, e.shiftKey, callback );
6223 // In server-side processing, the draw callback will remove the
6224 // processing display
6225 if ( _fnDataSource( settings ) !== 'ssp' ) {
6226 _fnProcessingDisplay( settings, false );
6231 _fnSortListener( settings, colIdx, e.shiftKey, callback );
6238 * Set the sorting classes on table's body, Note: it is safe to call this function
6239 * when bSort and bSortClasses are false
6240 * @param {object} oSettings dataTables settings object
6241 * @memberof DataTable#oApi
6243 function _fnSortingClasses( settings )
6245 var oldSort = settings.aLastSort;
6246 var sortClass = settings.oClasses.sSortColumn;
6247 var sort = _fnSortFlatten( settings );
6248 var features = settings.oFeatures;
6251 if ( features.bSort && features.bSortClasses ) {
6252 // Remove old sorting classes
6253 for ( i=0, ien=oldSort.length ; i<ien ; i++ ) {
6254 colIdx = oldSort[i].src;
6256 // Remove column sorting
6257 $( _pluck( settings.aoData, 'anCells', colIdx ) )
6258 .removeClass( sortClass + (i<2 ? i+1 : 3) );
6261 // Add new column sorting
6262 for ( i=0, ien=sort.length ; i<ien ; i++ ) {
6263 colIdx = sort[i].src;
6265 $( _pluck( settings.aoData, 'anCells', colIdx ) )
6266 .addClass( sortClass + (i<2 ? i+1 : 3) );
6270 settings.aLastSort = sort;
6274 // Get the data to sort a column, be it from cache, fresh (populating the
6275 // cache), or from a sort formatter
6276 function _fnSortData( settings, idx )
6278 // Custom sorting function - provided by the sort data type
6279 var column = settings.aoColumns[ idx ];
6280 var customSort = DataTable.ext.order[ column.sSortDataType ];
6284 customData = customSort.call( settings.oInstance, settings, idx,
6285 _fnColumnIndexToVisible( settings, idx )
6289 // Use / populate cache
6291 var formatter = DataTable.ext.type.order[ column.sType+"-pre" ];
6293 for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
6294 row = settings.aoData[i];
6296 if ( ! row._aSortData ) {
6297 row._aSortData = [];
6300 if ( ! row._aSortData[idx] || customSort ) {
6301 cellData = customSort ?
6302 customData[i] : // If there was a custom sort function, use data from there
6303 _fnGetCellData( settings, i, idx, 'sort' );
6305 row._aSortData[ idx ] = formatter ?
6306 formatter( cellData ) :
6315 * Save the state of a table
6316 * @param {object} oSettings dataTables settings object
6317 * @memberof DataTable#oApi
6319 function _fnSaveState ( settings )
6321 if ( !settings.oFeatures.bStateSave || settings.bDestroying )
6326 /* Store the interesting variables */
6329 start: settings._iDisplayStart,
6330 length: settings._iDisplayLength,
6331 order: $.extend( true, [], settings.aaSorting ),
6332 search: _fnSearchToCamel( settings.oPreviousSearch ),
6333 columns: $.map( settings.aoColumns, function ( col, i ) {
6335 visible: col.bVisible,
6336 search: _fnSearchToCamel( settings.aoPreSearchCols[i] )
6341 _fnCallbackFire( settings, "aoStateSaveParams", 'stateSaveParams', [settings, state] );
6343 settings.oSavedState = state;
6344 settings.fnStateSaveCallback.call( settings.oInstance, settings, state );
6349 * Attempt to load a saved table state
6350 * @param {object} oSettings dataTables settings object
6351 * @param {object} oInit DataTables init object so we can override settings
6352 * @param {function} callback Callback to execute when the state has been loaded
6353 * @memberof DataTable#oApi
6355 function _fnLoadState ( settings, oInit, callback )
6358 var columns = settings.aoColumns;
6359 var loaded = function ( s ) {
6360 if ( ! s || ! s.time ) {
6365 // Allow custom and plug-in manipulation functions to alter the saved data set and
6366 // cancelling of loading by returning false
6367 var abStateLoad = _fnCallbackFire( settings, 'aoStateLoadParams', 'stateLoadParams', [settings, s] );
6368 if ( $.inArray( false, abStateLoad ) !== -1 ) {
6374 var duration = settings.iStateDuration;
6375 if ( duration > 0 && s.time < +new Date() - (duration*1000) ) {
6380 // Number of columns have changed - all bets are off, no restore of settings
6381 if ( s.columns && columns.length !== s.columns.length ) {
6386 // Store the saved state so it might be accessed at any time
6387 settings.oLoadedState = $.extend( true, {}, s );
6389 // Restore key features - todo - for 1.11 this needs to be done by
6390 // subscribed events
6391 if ( s.start !== undefined ) {
6392 settings._iDisplayStart = s.start;
6393 settings.iInitDisplayStart = s.start;
6395 if ( s.length !== undefined ) {
6396 settings._iDisplayLength = s.length;
6400 if ( s.order !== undefined ) {
6401 settings.aaSorting = [];
6402 $.each( s.order, function ( i, col ) {
6403 settings.aaSorting.push( col[0] >= columns.length ?
6411 if ( s.search !== undefined ) {
6412 $.extend( settings.oPreviousSearch, _fnSearchToHung( s.search ) );
6418 for ( i=0, ien=s.columns.length ; i<ien ; i++ ) {
6419 var col = s.columns[i];
6422 if ( col.visible !== undefined ) {
6423 columns[i].bVisible = col.visible;
6427 if ( col.search !== undefined ) {
6428 $.extend( settings.aoPreSearchCols[i], _fnSearchToHung( col.search ) );
6433 _fnCallbackFire( settings, 'aoStateLoaded', 'stateLoaded', [settings, s] );
6437 if ( ! settings.oFeatures.bStateSave ) {
6442 var state = settings.fnStateLoadCallback.call( settings.oInstance, settings, loaded );
6444 if ( state !== undefined ) {
6447 // otherwise, wait for the loaded callback to be executed
6452 * Return the settings object for a particular table
6453 * @param {node} table table we are using as a dataTable
6454 * @returns {object} Settings object - or null if not found
6455 * @memberof DataTable#oApi
6457 function _fnSettingsFromNode ( table )
6459 var settings = DataTable.settings;
6460 var idx = $.inArray( table, _pluck( settings, 'nTable' ) );
6469 * Log an error message
6470 * @param {object} settings dataTables settings object
6471 * @param {int} level log error messages, or display them to the user
6472 * @param {string} msg error message
6473 * @param {int} tn Technical note id to get more information about the error.
6474 * @memberof DataTable#oApi
6476 function _fnLog( settings, level, msg, tn )
6478 msg = 'DataTables warning: '+
6479 (settings ? 'table id='+settings.sTableId+' - ' : '')+msg;
6482 msg += '. For more information about this error, please see '+
6483 'http://datatables.net/tn/'+tn;
6487 // Backwards compatibility pre 1.10
6488 var ext = DataTable.ext;
6489 var type = ext.sErrMode || ext.errMode;
6492 _fnCallbackFire( settings, null, 'error', [ settings, tn, msg ] );
6495 if ( type == 'alert' ) {
6498 else if ( type == 'throw' ) {
6499 throw new Error(msg);
6501 else if ( typeof type == 'function' ) {
6502 type( settings, tn, msg );
6505 else if ( window.console && console.log ) {
6512 * See if a property is defined on one object, if so assign it to the other object
6513 * @param {object} ret target object
6514 * @param {object} src source object
6515 * @param {string} name property
6516 * @param {string} [mappedName] name to map too - optional, name used if not given
6517 * @memberof DataTable#oApi
6519 function _fnMap( ret, src, name, mappedName )
6521 if ( $.isArray( name ) ) {
6522 $.each( name, function (i, val) {
6523 if ( $.isArray( val ) ) {
6524 _fnMap( ret, src, val[0], val[1] );
6527 _fnMap( ret, src, val );
6534 if ( mappedName === undefined ) {
6538 if ( src[name] !== undefined ) {
6539 ret[mappedName] = src[name];
6545 * Extend objects - very similar to jQuery.extend, but deep copy objects, and
6546 * shallow copy arrays. The reason we need to do this, is that we don't want to
6547 * deep copy array init values (such as aaSorting) since the dev wouldn't be
6548 * able to override them, but we do want to deep copy arrays.
6549 * @param {object} out Object to extend
6550 * @param {object} extender Object from which the properties will be applied to
6552 * @param {boolean} breakRefs If true, then arrays will be sliced to take an
6553 * independent copy with the exception of the `data` or `aaData` parameters
6554 * if they are present. This is so you can pass in a collection to
6555 * DataTables and have that used as your data source without breaking the
6557 * @returns {object} out Reference, just for convenience - out === the return.
6558 * @memberof DataTable#oApi
6559 * @todo This doesn't take account of arrays inside the deep copied objects.
6561 function _fnExtend( out, extender, breakRefs )
6565 for ( var prop in extender ) {
6566 if ( extender.hasOwnProperty(prop) ) {
6567 val = extender[prop];
6569 if ( $.isPlainObject( val ) ) {
6570 if ( ! $.isPlainObject( out[prop] ) ) {
6573 $.extend( true, out[prop], val );
6575 else if ( breakRefs && prop !== 'data' && prop !== 'aaData' && $.isArray(val) ) {
6576 out[prop] = val.slice();
6589 * Bind an event handers to allow a click or return key to activate the callback.
6590 * This is good for accessibility since a return on the keyboard will have the
6591 * same effect as a click, if the element has focus.
6592 * @param {element} n Element to bind the action to
6593 * @param {object} oData Data object to pass to the triggered function
6594 * @param {function} fn Callback function for when the event is triggered
6595 * @memberof DataTable#oApi
6597 function _fnBindAction( n, oData, fn )
6600 .on( 'click.DT', oData, function (e) {
6601 $(n).trigger('blur'); // Remove focus outline for mouse users
6604 .on( 'keypress.DT', oData, function (e){
6605 if ( e.which === 13 ) {
6610 .on( 'selectstart.DT', function () {
6611 /* Take the brutal approach to cancelling text selection */
6618 * Register a callback function. Easily allows a callback function to be added to
6619 * an array store of callback functions that can then all be called together.
6620 * @param {object} oSettings dataTables settings object
6621 * @param {string} sStore Name of the array storage for the callbacks in oSettings
6622 * @param {function} fn Function to be called back
6623 * @param {string} sName Identifying name for the callback (i.e. a label)
6624 * @memberof DataTable#oApi
6626 function _fnCallbackReg( oSettings, sStore, fn, sName )
6630 oSettings[sStore].push( {
6639 * Fire callback functions and trigger events. Note that the loop over the
6640 * callback array store is done backwards! Further note that you do not want to
6641 * fire off triggers in time sensitive applications (for example cell creation)
6643 * @param {object} settings dataTables settings object
6644 * @param {string} callbackArr Name of the array storage for the callbacks in
6646 * @param {string} eventName Name of the jQuery custom event to trigger. If
6647 * null no trigger is fired
6648 * @param {array} args Array of arguments to pass to the callback function /
6650 * @memberof DataTable#oApi
6652 function _fnCallbackFire( settings, callbackArr, eventName, args )
6656 if ( callbackArr ) {
6657 ret = $.map( settings[callbackArr].slice().reverse(), function (val, i) {
6658 return val.fn.apply( settings.oInstance, args );
6662 if ( eventName !== null ) {
6663 var e = $.Event( eventName+'.dt' );
6665 $(settings.nTable).trigger( e, args );
6667 ret.push( e.result );
6674 function _fnLengthOverflow ( settings )
6677 start = settings._iDisplayStart,
6678 end = settings.fnDisplayEnd(),
6679 len = settings._iDisplayLength;
6681 /* If we have space to show extra rows (backing up from the end point - then do so */
6687 // Keep the start record on the current page
6688 start -= (start % len);
6690 if ( len === -1 || start < 0 )
6695 settings._iDisplayStart = start;
6699 function _fnRenderer( settings, type )
6701 var renderer = settings.renderer;
6702 var host = DataTable.ext.renderer[type];
6704 if ( $.isPlainObject( renderer ) && renderer[type] ) {
6705 // Specific renderer for this type. If available use it, otherwise use
6707 return host[renderer[type]] || host._;
6709 else if ( typeof renderer === 'string' ) {
6710 // Common renderer - if there is one available for this type use it,
6711 // otherwise use the default
6712 return host[renderer] || host._;
6721 * Detect the data source being used for the table. Used to simplify the code
6722 * a little (ajax) and to make it compress a little smaller.
6724 * @param {object} settings dataTables settings object
6725 * @returns {string} Data source
6726 * @memberof DataTable#oApi
6728 function _fnDataSource ( settings )
6730 if ( settings.oFeatures.bServerSide ) {
6733 else if ( settings.ajax || settings.sAjaxSource ) {
6743 * Computed structure of the DataTables API, defined by the options passed to
6744 * `DataTable.Api.register()` when building the API.
6746 * The structure is built in order to speed creation and extension of the Api
6747 * objects since the extensions are effectively pre-parsed.
6749 * The array is an array of objects with the following structure, where this
6750 * base array represents the Api prototype base:
6754 * name: 'data' -- string - Property name
6755 * val: function () {}, -- function - Api method (or undefined if just an object
6756 * methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result
6757 * propExt: [ ... ] -- array - Array of Api object definitions to extend the property
6762 * methodExt: [ ... ],
6766 * val: function () {},
6767 * methodExt: [ ... ],
6778 var __apiStruct = [];
6782 * `Array.prototype` reference.
6787 var __arrayProto = Array.prototype;
6791 * Abstraction for `context` parameter of the `Api` constructor to allow it to
6792 * take several different forms for ease of use.
6794 * Each of the input parameter types will be converted to a DataTables settings
6795 * object where possible.
6797 * @param {string|node|jQuery|object} mixed DataTable identifier. Can be one
6800 * * `string` - jQuery selector. Any DataTables' matching the given selector
6801 * with be found and used.
6802 * * `node` - `TABLE` node which has already been formed into a DataTable.
6803 * * `jQuery` - A jQuery object of `TABLE` nodes.
6804 * * `object` - DataTables settings object
6805 * * `DataTables.Api` - API instance
6806 * @return {array|null} Matching DataTables settings objects. `null` or
6807 * `undefined` is returned if no matching DataTable is found.
6810 var _toSettings = function ( mixed )
6813 var settings = DataTable.settings;
6814 var tables = $.map( settings, function (el, i) {
6821 else if ( mixed.nTable && mixed.oApi ) {
6822 // DataTables settings object
6825 else if ( mixed.nodeName && mixed.nodeName.toLowerCase() === 'table' ) {
6827 idx = $.inArray( mixed, tables );
6828 return idx !== -1 ? [ settings[idx] ] : null;
6830 else if ( mixed && typeof mixed.settings === 'function' ) {
6831 return mixed.settings().toArray();
6833 else if ( typeof mixed === 'string' ) {
6837 else if ( mixed instanceof $ ) {
6838 // jQuery object (also DataTables instance)
6843 return jq.map( function(i) {
6844 idx = $.inArray( this, tables );
6845 return idx !== -1 ? settings[idx] : null;
6852 * DataTables API class - used to control and interface with one or more
6853 * DataTables enhanced tables.
6855 * The API class is heavily based on jQuery, presenting a chainable interface
6856 * that you can use to interact with tables. Each instance of the API class has
6857 * a "context" - i.e. the tables that it will operate on. This could be a single
6858 * table, all tables on a page or a sub-set thereof.
6860 * Additionally the API is designed to allow you to easily work with the data in
6861 * the tables, retrieving and manipulating it as required. This is done by
6862 * presenting the API class as an array like interface. The contents of the
6863 * array depend upon the actions requested by each method (for example
6864 * `rows().nodes()` will return an array of nodes, while `rows().data()` will
6865 * return an array of objects or arrays depending upon your table's
6866 * configuration). The API object has a number of array like methods (`push`,
6867 * `pop`, `reverse` etc) as well as additional helper methods (`each`, `pluck`,
6868 * `unique` etc) to assist your working with the data held in a table.
6870 * Most methods (those which return an Api instance) are chainable, which means
6871 * the return from a method call also has all of the methods available that the
6872 * top level object had. For example, these two calls are equivalent:
6875 * api.row.add( {...} );
6879 * api.row.add( {...} ).draw();
6881 * @class DataTable.Api
6882 * @param {array|object|string|jQuery} context DataTable identifier. This is
6883 * used to define which DataTables enhanced tables this API will operate on.
6886 * * `string` - jQuery selector. Any DataTables' matching the given selector
6887 * with be found and used.
6888 * * `node` - `TABLE` node which has already been formed into a DataTable.
6889 * * `jQuery` - A jQuery object of `TABLE` nodes.
6890 * * `object` - DataTables settings object
6891 * @param {array} [data] Data to initialise the Api instance with.
6894 * // Direct initialisation during DataTables construction
6895 * var api = $('#example').DataTable();
6898 * // Initialisation using a DataTables jQuery object
6899 * var api = $('#example').dataTable().api();
6902 * // Initialisation as a constructor
6903 * var api = new $.fn.DataTable.Api( 'table.dataTable' );
6905 _Api = function ( context, data )
6907 if ( ! (this instanceof _Api) ) {
6908 return new _Api( context, data );
6912 var ctxSettings = function ( o ) {
6913 var a = _toSettings( o );
6915 settings.push.apply( settings, a );
6919 if ( $.isArray( context ) ) {
6920 for ( var i=0, ien=context.length ; i<ien ; i++ ) {
6921 ctxSettings( context[i] );
6925 ctxSettings( context );
6928 // Remove duplicates
6929 this.context = _unique( settings );
6933 $.merge( this, data );
6943 _Api.extend( this, this, __apiStruct );
6946 DataTable.Api = _Api;
6948 // Don't destroy the existing prototype, just extend it. Required for jQuery 2's
6950 $.extend( _Api.prototype, {
6953 return this.count() !== 0;
6957 concat: __arrayProto.concat,
6960 context: [], // array of table settings objects
6965 return this.flatten().length;
6969 each: function ( fn )
6971 for ( var i=0, ien=this.length ; i<ien; i++ ) {
6972 fn.call( this, this[i], i, this );
6979 eq: function ( idx )
6981 var ctx = this.context;
6983 return ctx.length > idx ?
6984 new _Api( ctx[idx], this[idx] ) :
6989 filter: function ( fn )
6993 if ( __arrayProto.filter ) {
6994 a = __arrayProto.filter.call( this, fn, this );
6997 // Compatibility for browsers without EMCA-252-5 (JS 1.6)
6998 for ( var i=0, ien=this.length ; i<ien ; i++ ) {
6999 if ( fn.call( this, this[i], i, this ) ) {
7005 return new _Api( this.context, a );
7009 flatten: function ()
7012 return new _Api( this.context, a.concat.apply( a, this.toArray() ) );
7016 join: __arrayProto.join,
7019 indexOf: __arrayProto.indexOf || function (obj, start)
7021 for ( var i=(start || 0), ien=this.length ; i<ien ; i++ ) {
7022 if ( this[i] === obj ) {
7029 iterator: function ( flatten, type, fn, alwaysNew ) {
7033 context = this.context,
7035 selector = this.selector;
7037 // Argument shifting
7038 if ( typeof flatten === 'string' ) {
7045 for ( i=0, ien=context.length ; i<ien ; i++ ) {
7046 var apiInst = new _Api( context[i] );
7048 if ( type === 'table' ) {
7049 ret = fn.call( apiInst, context[i], i );
7051 if ( ret !== undefined ) {
7055 else if ( type === 'columns' || type === 'rows' ) {
7056 // this has same length as context - one entry for each table
7057 ret = fn.call( apiInst, context[i], this[i], i );
7059 if ( ret !== undefined ) {
7063 else if ( type === 'column' || type === 'column-rows' || type === 'row' || type === 'cell' ) {
7064 // columns and rows share the same structure.
7065 // 'this' is an array of column indexes for each context
7068 if ( type === 'column-rows' ) {
7069 rows = _selector_row_indexes( context[i], selector.opts );
7072 for ( j=0, jen=items.length ; j<jen ; j++ ) {
7075 if ( type === 'cell' ) {
7076 ret = fn.call( apiInst, context[i], item.row, item.column, i, j );
7079 ret = fn.call( apiInst, context[i], item, i, j, rows );
7082 if ( ret !== undefined ) {
7089 if ( a.length || alwaysNew ) {
7090 var api = new _Api( context, flatten ? a.concat.apply( [], a ) : a );
7091 var apiSelector = api.selector;
7092 apiSelector.rows = selector.rows;
7093 apiSelector.cols = selector.cols;
7094 apiSelector.opts = selector.opts;
7101 lastIndexOf: __arrayProto.lastIndexOf || function (obj, start)
7104 return this.indexOf.apply( this.toArray.reverse(), arguments );
7111 map: function ( fn )
7115 if ( __arrayProto.map ) {
7116 a = __arrayProto.map.call( this, fn, this );
7119 // Compatibility for browsers without EMCA-252-5 (JS 1.6)
7120 for ( var i=0, ien=this.length ; i<ien ; i++ ) {
7121 a.push( fn.call( this, this[i], i ) );
7125 return new _Api( this.context, a );
7129 pluck: function ( prop )
7131 return this.map( function ( el ) {
7136 pop: __arrayProto.pop,
7139 push: __arrayProto.push,
7142 // Does not return an API instance
7143 reduce: __arrayProto.reduce || function ( fn, init )
7145 return _fnReduce( this, fn, init, 0, this.length, 1 );
7149 reduceRight: __arrayProto.reduceRight || function ( fn, init )
7151 return _fnReduce( this, fn, init, this.length-1, -1, -1 );
7155 reverse: __arrayProto.reverse,
7158 // Object with rows, columns and opts
7162 shift: __arrayProto.shift,
7165 slice: function () {
7166 return new _Api( this.context, this );
7170 sort: __arrayProto.sort, // ? name - order?
7173 splice: __arrayProto.splice,
7176 toArray: function ()
7178 return __arrayProto.slice.call( this );
7188 toJQuery: function ()
7196 return new _Api( this.context, _unique(this) );
7200 unshift: __arrayProto.unshift
7204 _Api.extend = function ( scope, obj, ext )
7206 // Only extend API instances and static properties of the API
7207 if ( ! ext.length || ! obj || ( ! (obj instanceof _Api) && ! obj.__dt_wrapper ) ) {
7214 methodScoping = function ( scope, fn, struc ) {
7215 return function () {
7216 var ret = fn.apply( scope, arguments );
7219 _Api.extend( ret, ret, struc.methodExt );
7224 for ( i=0, ien=ext.length ; i<ien ; i++ ) {
7228 obj[ struct.name ] = struct.type === 'function' ?
7229 methodScoping( scope, struct.val, struct ) :
7230 struct.type === 'object' ?
7234 obj[ struct.name ].__dt_wrapper = true;
7236 // Property extension
7237 _Api.extend( scope, obj[ struct.name ], struct.propExt );
7242 // @todo - Is there need for an augment function?
7243 // _Api.augment = function ( inst, name )
7245 // // Find src object in the structure from the name
7246 // var parts = name.split('.');
7248 // _Api.extend( inst, obj );
7254 // name: 'data' -- string - Property name
7255 // val: function () {}, -- function - Api method (or undefined if just an object
7256 // methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result
7257 // propExt: [ ... ] -- array - Array of Api object definitions to extend the property
7262 // methodExt: [ ... ],
7266 // val: function () {},
7267 // methodExt: [ ... ],
7275 _Api.register = _api_register = function ( name, val )
7277 if ( $.isArray( name ) ) {
7278 for ( var j=0, jen=name.length ; j<jen ; j++ ) {
7279 _Api.register( name[j], val );
7286 heir = name.split('.'),
7287 struct = __apiStruct,
7290 var find = function ( src, name ) {
7291 for ( var i=0, ien=src.length ; i<ien ; i++ ) {
7292 if ( src[i].name === name ) {
7299 for ( i=0, ien=heir.length ; i<ien ; i++ ) {
7300 method = heir[i].indexOf('()') !== -1;
7302 heir[i].replace('()', '') :
7305 var src = find( struct, key );
7317 if ( i === ien-1 ) {
7319 src.type = typeof val === 'function' ?
7321 $.isPlainObject( val ) ?
7333 _Api.registerPlural = _api_registerPlural = function ( pluralName, singularName, val ) {
7334 _Api.register( pluralName, val );
7336 _Api.register( singularName, function () {
7337 var ret = val.apply( this, arguments );
7339 if ( ret === this ) {
7340 // Returned item is the API instance that was passed in, return it
7343 else if ( ret instanceof _Api ) {
7344 // New API instance returned, want the value from the first item
7345 // in the returned array for the singular result.
7347 $.isArray( ret[0] ) ?
7348 new _Api( ret.context, ret[0] ) : // Array results are 'enhanced'
7353 // Non-API return - just fire it back
7360 * Selector for HTML tables. Apply the given selector to the give array of
7361 * DataTables settings objects.
7363 * @param {string|integer} [selector] jQuery selector string or integer
7364 * @param {array} Array of DataTables settings objects to be filtered
7368 var __table_selector = function ( selector, a )
7370 if ( $.isArray(selector) ) {
7371 return $.map( selector, function (item) {
7372 return __table_selector(item, a);
7376 // Integer is used to pick out a table by index
7377 if ( typeof selector === 'number' ) {
7378 return [ a[ selector ] ];
7381 // Perform a jQuery selector on the table nodes
7382 var nodes = $.map( a, function (el, i) {
7388 .map( function (i) {
7389 // Need to translate back from the table node to the settings
7390 var idx = $.inArray( this, nodes );
7399 * Context selector for the API's context (i.e. the tables the API instance
7402 * @name DataTable.Api#tables
7403 * @param {string|integer} [selector] Selector to pick which tables the iterator
7404 * should operate on. If not given, all tables in the current context are
7405 * used. This can be given as a jQuery selector (for example `':gt(0)'`) to
7406 * select multiple tables or as an integer to select a single table.
7407 * @returns {DataTable.Api} Returns a new API instance if a selector is given.
7409 _api_register( 'tables()', function ( selector ) {
7410 // A new instance is created if there was a selector specified
7411 return selector !== undefined && selector !== null ?
7412 new _Api( __table_selector( selector, this.context ) ) :
7417 _api_register( 'table()', function ( selector ) {
7418 var tables = this.tables( selector );
7419 var ctx = tables.context;
7421 // Truncate to the first matched table
7423 new _Api( ctx[0] ) :
7428 _api_registerPlural( 'tables().nodes()', 'table().node()' , function () {
7429 return this.iterator( 'table', function ( ctx ) {
7435 _api_registerPlural( 'tables().body()', 'table().body()' , function () {
7436 return this.iterator( 'table', function ( ctx ) {
7442 _api_registerPlural( 'tables().header()', 'table().header()' , function () {
7443 return this.iterator( 'table', function ( ctx ) {
7449 _api_registerPlural( 'tables().footer()', 'table().footer()' , function () {
7450 return this.iterator( 'table', function ( ctx ) {
7456 _api_registerPlural( 'tables().containers()', 'table().container()' , function () {
7457 return this.iterator( 'table', function ( ctx ) {
7458 return ctx.nTableWrapper;
7465 * Redraw the tables in the current context.
7467 _api_register( 'draw()', function ( paging ) {
7468 return this.iterator( 'table', function ( settings ) {
7469 if ( paging === 'page' ) {
7470 _fnDraw( settings );
7473 if ( typeof paging === 'string' ) {
7474 paging = paging === 'full-hold' ?
7479 _fnReDraw( settings, paging===false );
7487 * Get the current page index.
7489 * @return {integer} Current page index (zero based)
7491 * Set the current page.
7493 * Note that if you attempt to show a page which does not exist, DataTables will
7494 * not throw an error, but rather reset the paging.
7496 * @param {integer|string} action The paging action to take. This can be one of:
7497 * * `integer` - The page index to jump to
7498 * * `string` - An action to take:
7499 * * `first` - Jump to first page.
7500 * * `next` - Jump to the next page
7501 * * `previous` - Jump to previous page
7502 * * `last` - Jump to the last page.
7503 * @returns {DataTables.Api} this
7505 _api_register( 'page()', function ( action ) {
7506 if ( action === undefined ) {
7507 return this.page.info().page; // not an expensive call
7510 // else, have an action to take on all tables
7511 return this.iterator( 'table', function ( settings ) {
7512 _fnPageChange( settings, action );
7518 * Paging information for the first table in the current context.
7520 * If you require paging information for another table, use the `table()` method
7521 * with a suitable selector.
7523 * @return {object} Object with the following properties set:
7524 * * `page` - Current page index (zero based - i.e. the first page is `0`)
7525 * * `pages` - Total number of pages
7526 * * `start` - Display index for the first record shown on the current page
7527 * * `end` - Display index for the last record shown on the current page
7528 * * `length` - Display length (number of records). Note that generally `start
7529 * + length = end`, but this is not always true, for example if there are
7530 * only 2 records to show on the final page, with a length of 10.
7531 * * `recordsTotal` - Full data set length
7532 * * `recordsDisplay` - Data set length once the current filtering criterion
7535 _api_register( 'page.info()', function ( action ) {
7536 if ( this.context.length === 0 ) {
7541 settings = this.context[0],
7542 start = settings._iDisplayStart,
7543 len = settings.oFeatures.bPaginate ? settings._iDisplayLength : -1,
7544 visRecords = settings.fnRecordsDisplay(),
7548 "page": all ? 0 : Math.floor( start / len ),
7549 "pages": all ? 1 : Math.ceil( visRecords / len ),
7551 "end": settings.fnDisplayEnd(),
7553 "recordsTotal": settings.fnRecordsTotal(),
7554 "recordsDisplay": visRecords,
7555 "serverSide": _fnDataSource( settings ) === 'ssp'
7561 * Get the current page length.
7563 * @return {integer} Current page length. Note `-1` indicates that all records
7566 * Set the current page length.
7568 * @param {integer} Page length to set. Use `-1` to show all records.
7569 * @returns {DataTables.Api} this
7571 _api_register( 'page.len()', function ( len ) {
7572 // Note that we can't call this function 'length()' because `length`
7573 // is a Javascript property of functions which defines how many arguments
7574 // the function expects.
7575 if ( len === undefined ) {
7576 return this.context.length !== 0 ?
7577 this.context[0]._iDisplayLength :
7581 // else, set the page length
7582 return this.iterator( 'table', function ( settings ) {
7583 _fnLengthChange( settings, len );
7589 var __reload = function ( settings, holdPosition, callback ) {
7590 // Use the draw event to trigger a callback
7592 var api = new _Api( settings );
7594 api.one( 'draw', function () {
7595 callback( api.ajax.json() );
7599 if ( _fnDataSource( settings ) == 'ssp' ) {
7600 _fnReDraw( settings, holdPosition );
7603 _fnProcessingDisplay( settings, true );
7605 // Cancel an existing request
7606 var xhr = settings.jqXHR;
7607 if ( xhr && xhr.readyState !== 4 ) {
7612 _fnBuildAjax( settings, [], function( json ) {
7613 _fnClearTable( settings );
7615 var data = _fnAjaxDataSrc( settings, json );
7616 for ( var i=0, ien=data.length ; i<ien ; i++ ) {
7617 _fnAddData( settings, data[i] );
7620 _fnReDraw( settings, holdPosition );
7621 _fnProcessingDisplay( settings, false );
7628 * Get the JSON response from the last Ajax request that DataTables made to the
7629 * server. Note that this returns the JSON from the first table in the current
7632 * @return {object} JSON received from the server.
7634 _api_register( 'ajax.json()', function () {
7635 var ctx = this.context;
7637 if ( ctx.length > 0 ) {
7641 // else return undefined;
7646 * Get the data submitted in the last Ajax request
7648 _api_register( 'ajax.params()', function () {
7649 var ctx = this.context;
7651 if ( ctx.length > 0 ) {
7652 return ctx[0].oAjaxData;
7655 // else return undefined;
7660 * Reload tables from the Ajax data source. Note that this function will
7661 * automatically re-draw the table when the remote data has been loaded.
7663 * @param {boolean} [reset=true] Reset (default) or hold the current paging
7664 * position. A full re-sort and re-filter is performed when this method is
7665 * called, which is why the pagination reset is the default action.
7666 * @returns {DataTables.Api} this
7668 _api_register( 'ajax.reload()', function ( callback, resetPaging ) {
7669 return this.iterator( 'table', function (settings) {
7670 __reload( settings, resetPaging===false, callback );
7676 * Get the current Ajax URL. Note that this returns the URL from the first
7677 * table in the current context.
7679 * @return {string} Current Ajax source URL
7681 * Set the Ajax URL. Note that this will set the URL for all tables in the
7684 * @param {string} url URL to set.
7685 * @returns {DataTables.Api} this
7687 _api_register( 'ajax.url()', function ( url ) {
7688 var ctx = this.context;
7690 if ( url === undefined ) {
7692 if ( ctx.length === 0 ) {
7698 $.isPlainObject( ctx.ajax ) ?
7705 return this.iterator( 'table', function ( settings ) {
7706 if ( $.isPlainObject( settings.ajax ) ) {
7707 settings.ajax.url = url;
7710 settings.ajax = url;
7712 // No need to consider sAjaxSource here since DataTables gives priority
7713 // to `ajax` over `sAjaxSource`. So setting `ajax` here, renders any
7714 // value of `sAjaxSource` redundant.
7720 * Load data from the newly set Ajax URL. Note that this method is only
7721 * available when `ajax.url()` is used to set a URL. Additionally, this method
7722 * has the same effect as calling `ajax.reload()` but is provided for
7723 * convenience when setting a new URL. Like `ajax.reload()` it will
7724 * automatically redraw the table once the remote data has been loaded.
7726 * @returns {DataTables.Api} this
7728 _api_register( 'ajax.url().load()', function ( callback, resetPaging ) {
7729 // Same as a reload, but makes sense to present it for easy access after a
7731 return this.iterator( 'table', function ( ctx ) {
7732 __reload( ctx, resetPaging===false, callback );
7739 var _selector_run = function ( type, selector, selectFn, settings, opts )
7744 selectorType = typeof selector;
7746 // Can't just check for isArray here, as an API or jQuery instance might be
7747 // given with their array like look
7748 if ( ! selector || selectorType === 'string' || selectorType === 'function' || selector.length === undefined ) {
7749 selector = [ selector ];
7752 for ( i=0, ien=selector.length ; i<ien ; i++ ) {
7753 // Only split on simple strings - complex expressions will be jQuery selectors
7754 a = selector[i] && selector[i].split && ! selector[i].match(/[\[\(:]/) ?
7755 selector[i].split(',') :
7758 for ( j=0, jen=a.length ; j<jen ; j++ ) {
7759 res = selectFn( typeof a[j] === 'string' ? $.trim(a[j]) : a[j] );
7761 if ( res && res.length ) {
7762 out = out.concat( res );
7767 // selector extensions
7768 var ext = _ext.selector[ type ];
7770 for ( i=0, ien=ext.length ; i<ien ; i++ ) {
7771 out = ext[i]( settings, opts, out );
7775 return _unique( out );
7779 var _selector_opts = function ( opts )
7785 // Backwards compatibility for 1.9- which used the terminology filter rather
7787 if ( opts.filter && opts.search === undefined ) {
7788 opts.search = opts.filter;
7799 var _selector_first = function ( inst )
7801 // Reduce the API instance to the first item found
7802 for ( var i=0, ien=inst.length ; i<ien ; i++ ) {
7803 if ( inst[i].length > 0 ) {
7804 // Assign the first element to the first item in the instance
7805 // and truncate the instance and context
7809 inst.context = [ inst.context[i] ];
7815 // Not found - return an empty instance
7821 var _selector_row_indexes = function ( settings, opts )
7825 displayFiltered = settings.aiDisplay,
7826 displayMaster = settings.aiDisplayMaster;
7829 search = opts.search, // none, applied, removed
7830 order = opts.order, // applied, current, index (original - compatibility with 1.9)
7831 page = opts.page; // all, current
7833 if ( _fnDataSource( settings ) == 'ssp' ) {
7834 // In server-side processing mode, most options are irrelevant since
7835 // rows not shown don't exist and the index order is the applied order
7836 // Removed is a special case - for consistency just return an empty
7838 return search === 'removed' ?
7840 _range( 0, displayMaster.length );
7842 else if ( page == 'current' ) {
7843 // Current page implies that order=current and fitler=applied, since it is
7844 // fairly senseless otherwise, regardless of what order and search actually
7846 for ( i=settings._iDisplayStart, ien=settings.fnDisplayEnd() ; i<ien ; i++ ) {
7847 a.push( displayFiltered[i] );
7850 else if ( order == 'current' || order == 'applied' ) {
7851 if ( search == 'none') {
7852 a = displayMaster.slice();
7854 else if ( search == 'applied' ) {
7855 a = displayFiltered.slice();
7857 else if ( search == 'removed' ) {
7858 // O(n+m) solution by creating a hash map
7859 var displayFilteredMap = {};
7861 for ( var i=0, ien=displayFiltered.length ; i<ien ; i++ ) {
7862 displayFilteredMap[displayFiltered[i]] = null;
7865 a = $.map( displayMaster, function (el) {
7866 return ! displayFilteredMap.hasOwnProperty(el) ?
7872 else if ( order == 'index' || order == 'original' ) {
7873 for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
7874 if ( search == 'none' ) {
7877 else { // applied | removed
7878 tmp = $.inArray( i, displayFiltered );
7880 if ((tmp === -1 && search == 'removed') ||
7881 (tmp >= 0 && search == 'applied') )
7893 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7896 * {} - no selector - use all available rows
7897 * {integer} - row aoData index
7899 * {string} - jQuery selector to apply to the TR elements
7900 * {array} - jQuery array of nodes, or simply an array of TR nodes
7903 var __row_selector = function ( settings, selector, opts )
7906 var run = function ( sel ) {
7907 var selInt = _intVal( sel );
7909 var aoData = settings.aoData;
7911 // Short cut - selector is a number and no options provided (default is
7912 // all records, so no need to check if the index is in there, since it
7913 // must be - dev error if the index doesn't exist).
7914 if ( selInt !== null && ! opts ) {
7919 rows = _selector_row_indexes( settings, opts );
7922 if ( selInt !== null && $.inArray( selInt, rows ) !== -1 ) {
7923 // Selector - integer
7926 else if ( sel === null || sel === undefined || sel === '' ) {
7931 // Selector - function
7932 if ( typeof sel === 'function' ) {
7933 return $.map( rows, function (idx) {
7934 var row = aoData[ idx ];
7935 return sel( idx, row._aData, row.nTr ) ? idx : null;
7940 if ( sel.nodeName ) {
7941 var rowIdx = sel._DT_RowIndex; // Property added by DT for fast lookup
7942 var cellIdx = sel._DT_CellIndex;
7944 if ( rowIdx !== undefined ) {
7945 // Make sure that the row is actually still present in the table
7946 return aoData[ rowIdx ] && aoData[ rowIdx ].nTr === sel ?
7950 else if ( cellIdx ) {
7951 return aoData[ cellIdx.row ] && aoData[ cellIdx.row ].nTr === sel.parentNode ?
7956 var host = $(sel).closest('*[data-dt-row]');
7957 return host.length ?
7958 [ host.data('dt-row') ] :
7963 // ID selector. Want to always be able to select rows by id, regardless
7964 // of if the tr element has been created or not, so can't rely upon
7965 // jQuery here - hence a custom implementation. This does not match
7966 // Sizzle's fast selector or HTML4 - in HTML5 the ID can be anything,
7967 // but to select it using a CSS selector engine (like Sizzle or
7968 // querySelect) it would need to need to be escaped for some characters.
7969 // DataTables simplifies this for row selectors since you can select
7970 // only a row. A # indicates an id any anything that follows is the id -
7972 if ( typeof sel === 'string' && sel.charAt(0) === '#' ) {
7973 // get row index from id
7974 var rowObj = settings.aIds[ sel.replace( /^#/, '' ) ];
7975 if ( rowObj !== undefined ) {
7976 return [ rowObj.idx ];
7979 // need to fall through to jQuery in case there is DOM id that
7983 // Get nodes in the order from the `rows` array with null values removed
7984 var nodes = _removeEmpty(
7985 _pluck_order( settings.aoData, rows, 'nTr' )
7988 // Selector - jQuery selector string, array of nodes or jQuery object/
7989 // As jQuery's .filter() allows jQuery objects to be passed in filter,
7990 // it also allows arrays, so this will cope with all three options
7994 return this._DT_RowIndex;
7999 return _selector_run( 'row', selector, run, settings, opts );
8003 _api_register( 'rows()', function ( selector, opts ) {
8004 // argument shifting
8005 if ( selector === undefined ) {
8008 else if ( $.isPlainObject( selector ) ) {
8013 opts = _selector_opts( opts );
8015 var inst = this.iterator( 'table', function ( settings ) {
8016 return __row_selector( settings, selector, opts );
8019 // Want argument shifting here and in __row_selector?
8020 inst.selector.rows = selector;
8021 inst.selector.opts = opts;
8026 _api_register( 'rows().nodes()', function () {
8027 return this.iterator( 'row', function ( settings, row ) {
8028 return settings.aoData[ row ].nTr || undefined;
8032 _api_register( 'rows().data()', function () {
8033 return this.iterator( true, 'rows', function ( settings, rows ) {
8034 return _pluck_order( settings.aoData, rows, '_aData' );
8038 _api_registerPlural( 'rows().cache()', 'row().cache()', function ( type ) {
8039 return this.iterator( 'row', function ( settings, row ) {
8040 var r = settings.aoData[ row ];
8041 return type === 'search' ? r._aFilterData : r._aSortData;
8045 _api_registerPlural( 'rows().invalidate()', 'row().invalidate()', function ( src ) {
8046 return this.iterator( 'row', function ( settings, row ) {
8047 _fnInvalidate( settings, row, src );
8051 _api_registerPlural( 'rows().indexes()', 'row().index()', function () {
8052 return this.iterator( 'row', function ( settings, row ) {
8057 _api_registerPlural( 'rows().ids()', 'row().id()', function ( hash ) {
8059 var context = this.context;
8061 // `iterator` will drop undefined values, but in this case we want them
8062 for ( var i=0, ien=context.length ; i<ien ; i++ ) {
8063 for ( var j=0, jen=this[i].length ; j<jen ; j++ ) {
8064 var id = context[i].rowIdFn( context[i].aoData[ this[i][j] ]._aData );
8065 a.push( (hash === true ? '#' : '' )+ id );
8069 return new _Api( context, a );
8072 _api_registerPlural( 'rows().remove()', 'row().remove()', function () {
8075 this.iterator( 'row', function ( settings, row, thatIdx ) {
8076 var data = settings.aoData;
8077 var rowData = data[ row ];
8079 var loopRow, loopCells;
8081 data.splice( row, 1 );
8083 // Update the cached indexes
8084 for ( i=0, ien=data.length ; i<ien ; i++ ) {
8086 loopCells = loopRow.anCells;
8089 if ( loopRow.nTr !== null ) {
8090 loopRow.nTr._DT_RowIndex = i;
8094 if ( loopCells !== null ) {
8095 for ( j=0, jen=loopCells.length ; j<jen ; j++ ) {
8096 loopCells[j]._DT_CellIndex.row = i;
8101 // Delete from the display arrays
8102 _fnDeleteIndex( settings.aiDisplayMaster, row );
8103 _fnDeleteIndex( settings.aiDisplay, row );
8104 _fnDeleteIndex( that[ thatIdx ], row, false ); // maintain local indexes
8106 // For server-side processing tables - subtract the deleted row from the count
8107 if ( settings._iRecordsDisplay > 0 ) {
8108 settings._iRecordsDisplay--;
8111 // Check for an 'overflow' they case for displaying the table
8112 _fnLengthOverflow( settings );
8114 // Remove the row's ID reference if there is one
8115 var id = settings.rowIdFn( rowData._aData );
8116 if ( id !== undefined ) {
8117 delete settings.aIds[ id ];
8121 this.iterator( 'table', function ( settings ) {
8122 for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
8123 settings.aoData[i].idx = i;
8131 _api_register( 'rows.add()', function ( rows ) {
8132 var newRows = this.iterator( 'table', function ( settings ) {
8136 for ( i=0, ien=rows.length ; i<ien ; i++ ) {
8139 if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {
8140 out.push( _fnAddTr( settings, row )[0] );
8143 out.push( _fnAddData( settings, row ) );
8150 // Return an Api.rows() extended instance, so rows().nodes() etc can be used
8151 var modRows = this.rows( -1 );
8153 $.merge( modRows, newRows );
8165 _api_register( 'row()', function ( selector, opts ) {
8166 return _selector_first( this.rows( selector, opts ) );
8170 _api_register( 'row().data()', function ( data ) {
8171 var ctx = this.context;
8173 if ( data === undefined ) {
8175 return ctx.length && this.length ?
8176 ctx[0].aoData[ this[0] ]._aData :
8181 var row = ctx[0].aoData[ this[0] ];
8184 // If the DOM has an id, and the data source is an array
8185 if ( $.isArray( data ) && row.nTr && row.nTr.id ) {
8186 _fnSetObjectDataFn( ctx[0].rowId )( data, row.nTr.id );
8189 // Automatically invalidate
8190 _fnInvalidate( ctx[0], this[0], 'data' );
8196 _api_register( 'row().node()', function () {
8197 var ctx = this.context;
8199 return ctx.length && this.length ?
8200 ctx[0].aoData[ this[0] ].nTr || null :
8205 _api_register( 'row.add()', function ( row ) {
8206 // Allow a jQuery object to be passed in - only a single row is added from
8207 // it though - the first element in the set
8208 if ( row instanceof $ && row.length ) {
8212 var rows = this.iterator( 'table', function ( settings ) {
8213 if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {
8214 return _fnAddTr( settings, row )[0];
8216 return _fnAddData( settings, row );
8219 // Return an Api.rows() extended instance, with the newly added row selected
8220 return this.row( rows[0] );
8225 var __details_add = function ( ctx, row, data, klass )
8227 // Convert to array of TR elements
8229 var addRow = function ( r, k ) {
8230 // Recursion to allow for arrays of jQuery objects
8231 if ( $.isArray( r ) || r instanceof $ ) {
8232 for ( var i=0, ien=r.length ; i<ien ; i++ ) {
8238 // If we get a TR element, then just add it directly - up to the dev
8239 // to add the correct number of columns etc
8240 if ( r.nodeName && r.nodeName.toLowerCase() === 'tr' ) {
8244 // Otherwise create a row with a wrapper
8245 var created = $('<tr><td/></tr>').addClass( k );
8249 [0].colSpan = _fnVisbleColumns( ctx );
8251 rows.push( created[0] );
8255 addRow( data, klass );
8257 if ( row._details ) {
8258 row._details.detach();
8261 row._details = $(rows);
8263 // If the children were already shown, that state should be retained
8264 if ( row._detailsShow ) {
8265 row._details.insertAfter( row.nTr );
8270 var __details_remove = function ( api, idx )
8272 var ctx = api.context;
8275 var row = ctx[0].aoData[ idx !== undefined ? idx : api[0] ];
8277 if ( row && row._details ) {
8278 row._details.remove();
8280 row._detailsShow = undefined;
8281 row._details = undefined;
8287 var __details_display = function ( api, show ) {
8288 var ctx = api.context;
8290 if ( ctx.length && api.length ) {
8291 var row = ctx[0].aoData[ api[0] ];
8293 if ( row._details ) {
8294 row._detailsShow = show;
8297 row._details.insertAfter( row.nTr );
8300 row._details.detach();
8303 __details_events( ctx[0] );
8309 var __details_events = function ( settings )
8311 var api = new _Api( settings );
8312 var namespace = '.dt.DT_details';
8313 var drawEvent = 'draw'+namespace;
8314 var colvisEvent = 'column-visibility'+namespace;
8315 var destroyEvent = 'destroy'+namespace;
8316 var data = settings.aoData;
8318 api.off( drawEvent +' '+ colvisEvent +' '+ destroyEvent );
8320 if ( _pluck( data, '_details' ).length > 0 ) {
8321 // On each draw, insert the required elements into the document
8322 api.on( drawEvent, function ( e, ctx ) {
8323 if ( settings !== ctx ) {
8327 api.rows( {page:'current'} ).eq(0).each( function (idx) {
8328 // Internal data grab
8329 var row = data[ idx ];
8331 if ( row._detailsShow ) {
8332 row._details.insertAfter( row.nTr );
8337 // Column visibility change - update the colspan
8338 api.on( colvisEvent, function ( e, ctx, idx, vis ) {
8339 if ( settings !== ctx ) {
8343 // Update the colspan for the details rows (note, only if it already has
8345 var row, visible = _fnVisbleColumns( ctx );
8347 for ( var i=0, ien=data.length ; i<ien ; i++ ) {
8350 if ( row._details ) {
8351 row._details.children('td[colspan]').attr('colspan', visible );
8356 // Table destroyed - nuke any child rows
8357 api.on( destroyEvent, function ( e, ctx ) {
8358 if ( settings !== ctx ) {
8362 for ( var i=0, ien=data.length ; i<ien ; i++ ) {
8363 if ( data[i]._details ) {
8364 __details_remove( api, i );
8371 // Strings for the method names to help minification
8373 var _child_obj = _emp+'row().child';
8374 var _child_mth = _child_obj+'()';
8379 // jQuery or array of any of the above
8380 _api_register( _child_mth, function ( data, klass ) {
8381 var ctx = this.context;
8383 if ( data === undefined ) {
8385 return ctx.length && this.length ?
8386 ctx[0].aoData[ this[0] ]._details :
8389 else if ( data === true ) {
8393 else if ( data === false ) {
8395 __details_remove( this );
8397 else if ( ctx.length && this.length ) {
8399 __details_add( ctx[0], ctx[0].aoData[ this[0] ], data, klass );
8407 _child_obj+'.show()',
8408 _child_mth+'.show()' // only when `child()` was called with parameters (without
8409 ], function ( show ) { // it returns an object and this method is not executed)
8410 __details_display( this, true );
8416 _child_obj+'.hide()',
8417 _child_mth+'.hide()' // only when `child()` was called with parameters (without
8418 ], function () { // it returns an object and this method is not executed)
8419 __details_display( this, false );
8425 _child_obj+'.remove()',
8426 _child_mth+'.remove()' // only when `child()` was called with parameters (without
8427 ], function () { // it returns an object and this method is not executed)
8428 __details_remove( this );
8433 _api_register( _child_obj+'.isShown()', function () {
8434 var ctx = this.context;
8436 if ( ctx.length && this.length ) {
8437 // _detailsShown as false or undefined will fall through to return false
8438 return ctx[0].aoData[ this[0] ]._detailsShow || false;
8445 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8448 * {integer} - column index (>=0 count from left, <0 count from right)
8449 * "{integer}:visIdx" - visible column index (i.e. translate to column index) (>=0 count from left, <0 count from right)
8450 * "{integer}:visible" - alias for {integer}:visIdx (>=0 count from left, <0 count from right)
8451 * "{string}:name" - column name
8452 * "{string}" - jQuery selector on column header nodes
8456 // can be an array of these items, comma separated list, or an array of comma
8459 var __re_column_selector = /^([^:]+):(name|visIdx|visible)$/;
8462 // r1 and r2 are redundant - but it means that the parameters match for the
8463 // iterator callback in columns().data()
8464 var __columnData = function ( settings, column, r1, r2, rows ) {
8466 for ( var row=0, ien=rows.length ; row<ien ; row++ ) {
8467 a.push( _fnGetCellData( settings, rows[row], column ) );
8473 var __column_selector = function ( settings, selector, opts )
8476 columns = settings.aoColumns,
8477 names = _pluck( columns, 'sName' ),
8478 nodes = _pluck( columns, 'nTh' );
8480 var run = function ( s ) {
8481 var selInt = _intVal( s );
8485 return _range( columns.length );
8489 if ( selInt !== null ) {
8490 return [ selInt >= 0 ?
8491 selInt : // Count from left
8492 columns.length + selInt // Count from right (+ because its a negative value)
8496 // Selector = function
8497 if ( typeof s === 'function' ) {
8498 var rows = _selector_row_indexes( settings, opts );
8500 return $.map( columns, function (col, idx) {
8503 __columnData( settings, idx, 0, 0, rows ),
8509 // jQuery or string selector
8510 var match = typeof s === 'string' ?
8511 s.match( __re_column_selector ) :
8515 switch( match[2] ) {
8518 var idx = parseInt( match[1], 10 );
8519 // Visible index given, convert to column index
8521 // Counting from the right
8522 var visColumns = $.map( columns, function (col,i) {
8523 return col.bVisible ? i : null;
8525 return [ visColumns[ visColumns.length + idx ] ];
8527 // Counting from the left
8528 return [ _fnVisibleToColumnIndex( settings, idx ) ];
8531 // match by name. `names` is column index complete and in order
8532 return $.map( names, function (name, i) {
8533 return name === match[1] ? i : null;
8541 // Cell in the table body
8542 if ( s.nodeName && s._DT_CellIndex ) {
8543 return [ s._DT_CellIndex.column ];
8546 // jQuery selector on the TH elements for the columns
8547 var jqResult = $( nodes )
8550 return $.inArray( this, nodes ); // `nodes` is column index complete and in order
8554 if ( jqResult.length || ! s.nodeName ) {
8558 // Otherwise a node which might have a `dt-column` data attribute, or be
8559 // a child or such an element
8560 var host = $(s).closest('*[data-dt-column]');
8561 return host.length ?
8562 [ host.data('dt-column') ] :
8566 return _selector_run( 'column', selector, run, settings, opts );
8570 var __setColumnVis = function ( settings, column, vis ) {
8572 cols = settings.aoColumns,
8573 col = cols[ column ],
8574 data = settings.aoData,
8575 row, cells, i, ien, tr;
8578 if ( vis === undefined ) {
8579 return col.bVisible;
8584 if ( col.bVisible === vis ) {
8590 // Need to decide if we should use appendChild or insertBefore
8591 var insertBefore = $.inArray( true, _pluck(cols, 'bVisible'), column+1 );
8593 for ( i=0, ien=data.length ; i<ien ; i++ ) {
8595 cells = data[i].anCells;
8598 // insertBefore can act like appendChild if 2nd arg is null
8599 tr.insertBefore( cells[ column ], cells[ insertBefore ] || null );
8605 $( _pluck( settings.aoData, 'anCells', column ) ).detach();
8613 _api_register( 'columns()', function ( selector, opts ) {
8614 // argument shifting
8615 if ( selector === undefined ) {
8618 else if ( $.isPlainObject( selector ) ) {
8623 opts = _selector_opts( opts );
8625 var inst = this.iterator( 'table', function ( settings ) {
8626 return __column_selector( settings, selector, opts );
8629 // Want argument shifting here and in _row_selector?
8630 inst.selector.cols = selector;
8631 inst.selector.opts = opts;
8636 _api_registerPlural( 'columns().header()', 'column().header()', function ( selector, opts ) {
8637 return this.iterator( 'column', function ( settings, column ) {
8638 return settings.aoColumns[column].nTh;
8642 _api_registerPlural( 'columns().footer()', 'column().footer()', function ( selector, opts ) {
8643 return this.iterator( 'column', function ( settings, column ) {
8644 return settings.aoColumns[column].nTf;
8648 _api_registerPlural( 'columns().data()', 'column().data()', function () {
8649 return this.iterator( 'column-rows', __columnData, 1 );
8652 _api_registerPlural( 'columns().dataSrc()', 'column().dataSrc()', function () {
8653 return this.iterator( 'column', function ( settings, column ) {
8654 return settings.aoColumns[column].mData;
8658 _api_registerPlural( 'columns().cache()', 'column().cache()', function ( type ) {
8659 return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
8660 return _pluck_order( settings.aoData, rows,
8661 type === 'search' ? '_aFilterData' : '_aSortData', column
8666 _api_registerPlural( 'columns().nodes()', 'column().nodes()', function () {
8667 return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
8668 return _pluck_order( settings.aoData, rows, 'anCells', column ) ;
8672 _api_registerPlural( 'columns().visible()', 'column().visible()', function ( vis, calc ) {
8674 var ret = this.iterator( 'column', function ( settings, column ) {
8675 if ( vis === undefined ) {
8676 return settings.aoColumns[ column ].bVisible;
8678 __setColumnVis( settings, column, vis );
8681 // Group the column visibility changes
8682 if ( vis !== undefined ) {
8683 this.iterator( 'table', function ( settings ) {
8684 // Redraw the header after changes
8685 _fnDrawHead( settings, settings.aoHeader );
8686 _fnDrawHead( settings, settings.aoFooter );
8688 // Update colspan for no records display. Child rows and extensions will use their own
8689 // listeners to do this - only need to update the empty table item here
8690 if ( ! settings.aiDisplay.length ) {
8691 $(settings.nTBody).find('td[colspan]').attr('colspan', _fnVisbleColumns(settings));
8694 _fnSaveState( settings );
8696 // Second loop once the first is done for events
8697 that.iterator( 'column', function ( settings, column ) {
8698 _fnCallbackFire( settings, null, 'column-visibility', [settings, column, vis, calc] );
8701 if ( calc === undefined || calc ) {
8702 that.columns.adjust();
8710 _api_registerPlural( 'columns().indexes()', 'column().index()', function ( type ) {
8711 return this.iterator( 'column', function ( settings, column ) {
8712 return type === 'visible' ?
8713 _fnColumnIndexToVisible( settings, column ) :
8718 _api_register( 'columns.adjust()', function () {
8719 return this.iterator( 'table', function ( settings ) {
8720 _fnAdjustColumnSizing( settings );
8724 _api_register( 'column.index()', function ( type, idx ) {
8725 if ( this.context.length !== 0 ) {
8726 var ctx = this.context[0];
8728 if ( type === 'fromVisible' || type === 'toData' ) {
8729 return _fnVisibleToColumnIndex( ctx, idx );
8731 else if ( type === 'fromData' || type === 'toVisible' ) {
8732 return _fnColumnIndexToVisible( ctx, idx );
8737 _api_register( 'column()', function ( selector, opts ) {
8738 return _selector_first( this.columns( selector, opts ) );
8743 var __cell_selector = function ( settings, selector, opts )
8745 var data = settings.aoData;
8746 var rows = _selector_row_indexes( settings, opts );
8747 var cells = _removeEmpty( _pluck_order( data, rows, 'anCells' ) );
8748 var allCells = $( [].concat.apply([], cells) );
8750 var columns = settings.aoColumns.length;
8751 var a, i, ien, j, o, host;
8753 var run = function ( s ) {
8754 var fnSelector = typeof s === 'function';
8756 if ( s === null || s === undefined || fnSelector ) {
8757 // All cells and function selectors
8760 for ( i=0, ien=rows.length ; i<ien ; i++ ) {
8763 for ( j=0 ; j<columns ; j++ ) {
8770 // Selector - function
8773 if ( s( o, _fnGetCellData(settings, row, j), host.anCells ? host.anCells[j] : null ) ) {
8788 if ( $.isPlainObject( s ) ) {
8789 // Valid cell index and its in the array of selectable rows
8790 return s.column !== undefined && s.row !== undefined && $.inArray( s.row, rows ) !== -1 ?
8795 // Selector - jQuery filtered cells
8796 var jqResult = allCells
8798 .map( function (i, el) {
8799 return { // use a new object, in case someone changes the values
8800 row: el._DT_CellIndex.row,
8801 column: el._DT_CellIndex.column
8806 if ( jqResult.length || ! s.nodeName ) {
8810 // Otherwise the selector is a node, and there is one last option - the
8811 // element might be a child of an element which has dt-row and dt-column
8813 host = $(s).closest('*[data-dt-row]');
8814 return host.length ?
8816 row: host.data('dt-row'),
8817 column: host.data('dt-column')
8822 return _selector_run( 'cell', selector, run, settings, opts );
8828 _api_register( 'cells()', function ( rowSelector, columnSelector, opts ) {
8829 // Argument shifting
8830 if ( $.isPlainObject( rowSelector ) ) {
8832 if ( rowSelector.row === undefined ) {
8833 // Selector options in first parameter
8838 // Cell index objects in first parameter
8839 opts = columnSelector;
8840 columnSelector = null;
8843 if ( $.isPlainObject( columnSelector ) ) {
8844 opts = columnSelector;
8845 columnSelector = null;
8849 if ( columnSelector === null || columnSelector === undefined ) {
8850 return this.iterator( 'table', function ( settings ) {
8851 return __cell_selector( settings, rowSelector, _selector_opts( opts ) );
8855 // The default built in options need to apply to row and columns
8856 var internalOpts = opts ? {
8862 // Row + column selector
8863 var columns = this.columns( columnSelector, internalOpts );
8864 var rows = this.rows( rowSelector, internalOpts );
8867 var cellsNoOpts = this.iterator( 'table', function ( settings, idx ) {
8870 for ( i=0, ien=rows[idx].length ; i<ien ; i++ ) {
8871 for ( j=0, jen=columns[idx].length ; j<jen ; j++ ) {
8874 column: columns[idx][j]
8882 // There is currently only one extension which uses a cell selector extension
8883 // It is a _major_ performance drag to run this if it isn't needed, so this is
8884 // an extension specific check at the moment
8885 var cells = opts && opts.selected ?
8886 this.cells( cellsNoOpts, opts ) :
8889 $.extend( cells.selector, {
8890 cols: columnSelector,
8899 _api_registerPlural( 'cells().nodes()', 'cell().node()', function () {
8900 return this.iterator( 'cell', function ( settings, row, column ) {
8901 var data = settings.aoData[ row ];
8903 return data && data.anCells ?
8904 data.anCells[ column ] :
8910 _api_register( 'cells().data()', function () {
8911 return this.iterator( 'cell', function ( settings, row, column ) {
8912 return _fnGetCellData( settings, row, column );
8917 _api_registerPlural( 'cells().cache()', 'cell().cache()', function ( type ) {
8918 type = type === 'search' ? '_aFilterData' : '_aSortData';
8920 return this.iterator( 'cell', function ( settings, row, column ) {
8921 return settings.aoData[ row ][ type ][ column ];
8926 _api_registerPlural( 'cells().render()', 'cell().render()', function ( type ) {
8927 return this.iterator( 'cell', function ( settings, row, column ) {
8928 return _fnGetCellData( settings, row, column, type );
8933 _api_registerPlural( 'cells().indexes()', 'cell().index()', function () {
8934 return this.iterator( 'cell', function ( settings, row, column ) {
8938 columnVisible: _fnColumnIndexToVisible( settings, column )
8944 _api_registerPlural( 'cells().invalidate()', 'cell().invalidate()', function ( src ) {
8945 return this.iterator( 'cell', function ( settings, row, column ) {
8946 _fnInvalidate( settings, row, src, column );
8952 _api_register( 'cell()', function ( rowSelector, columnSelector, opts ) {
8953 return _selector_first( this.cells( rowSelector, columnSelector, opts ) );
8957 _api_register( 'cell().data()', function ( data ) {
8958 var ctx = this.context;
8961 if ( data === undefined ) {
8963 return ctx.length && cell.length ?
8964 _fnGetCellData( ctx[0], cell[0].row, cell[0].column ) :
8969 _fnSetCellData( ctx[0], cell[0].row, cell[0].column, data );
8970 _fnInvalidate( ctx[0], cell[0].row, 'data', cell[0].column );
8978 * Get current ordering (sorting) that has been applied to the table.
8980 * @returns {array} 2D array containing the sorting information for the first
8981 * table in the current context. Each element in the parent array represents
8982 * a column being sorted upon (i.e. multi-sorting with two columns would have
8983 * 2 inner arrays). The inner arrays may have 2 or 3 elements. The first is
8984 * the column index that the sorting condition applies to, the second is the
8985 * direction of the sort (`desc` or `asc`) and, optionally, the third is the
8986 * index of the sorting order from the `column.sorting` initialisation array.
8988 * Set the ordering for the table.
8990 * @param {integer} order Column index to sort upon.
8991 * @param {string} direction Direction of the sort to be applied (`asc` or `desc`)
8992 * @returns {DataTables.Api} this
8994 * Set the ordering for the table.
8996 * @param {array} order 1D array of sorting information to be applied.
8997 * @param {array} [...] Optional additional sorting conditions
8998 * @returns {DataTables.Api} this
9000 * Set the ordering for the table.
9002 * @param {array} order 2D array of sorting information to be applied.
9003 * @returns {DataTables.Api} this
9005 _api_register( 'order()', function ( order, dir ) {
9006 var ctx = this.context;
9008 if ( order === undefined ) {
9010 return ctx.length !== 0 ?
9016 if ( typeof order === 'number' ) {
9017 // Simple column / direction passed in
9018 order = [ [ order, dir ] ];
9020 else if ( order.length && ! $.isArray( order[0] ) ) {
9021 // Arguments passed in (list of 1D arrays)
9022 order = Array.prototype.slice.call( arguments );
9024 // otherwise a 2D array was passed in
9026 return this.iterator( 'table', function ( settings ) {
9027 settings.aaSorting = order.slice();
9033 * Attach a sort listener to an element for a given column
9035 * @param {node|jQuery|string} node Identifier for the element(s) to attach the
9036 * listener to. This can take the form of a single DOM node, a jQuery
9037 * collection of nodes or a jQuery selector which will identify the node(s).
9038 * @param {integer} column the column that a click on this node will sort on
9039 * @param {function} [callback] callback function when sort is run
9040 * @returns {DataTables.Api} this
9042 _api_register( 'order.listener()', function ( node, column, callback ) {
9043 return this.iterator( 'table', function ( settings ) {
9044 _fnSortAttachListener( settings, node, column, callback );
9049 _api_register( 'order.fixed()', function ( set ) {
9051 var ctx = this.context;
9052 var fixed = ctx.length ?
9053 ctx[0].aaSortingFixed :
9056 return $.isArray( fixed ) ?
9061 return this.iterator( 'table', function ( settings ) {
9062 settings.aaSortingFixed = $.extend( true, {}, set );
9067 // Order by the selected column(s)
9069 'columns().order()',
9071 ], function ( dir ) {
9074 return this.iterator( 'table', function ( settings, i ) {
9077 $.each( that[i], function (j, col) {
9078 sort.push( [ col, dir ] );
9081 settings.aaSorting = sort;
9087 _api_register( 'search()', function ( input, regex, smart, caseInsen ) {
9088 var ctx = this.context;
9090 if ( input === undefined ) {
9092 return ctx.length !== 0 ?
9093 ctx[0].oPreviousSearch.sSearch :
9098 return this.iterator( 'table', function ( settings ) {
9099 if ( ! settings.oFeatures.bFilter ) {
9103 _fnFilterComplete( settings, $.extend( {}, settings.oPreviousSearch, {
9104 "sSearch": input+"",
9105 "bRegex": regex === null ? false : regex,
9106 "bSmart": smart === null ? true : smart,
9107 "bCaseInsensitive": caseInsen === null ? true : caseInsen
9113 _api_registerPlural(
9114 'columns().search()',
9115 'column().search()',
9116 function ( input, regex, smart, caseInsen ) {
9117 return this.iterator( 'column', function ( settings, column ) {
9118 var preSearch = settings.aoPreSearchCols;
9120 if ( input === undefined ) {
9122 return preSearch[ column ].sSearch;
9126 if ( ! settings.oFeatures.bFilter ) {
9130 $.extend( preSearch[ column ], {
9131 "sSearch": input+"",
9132 "bRegex": regex === null ? false : regex,
9133 "bSmart": smart === null ? true : smart,
9134 "bCaseInsensitive": caseInsen === null ? true : caseInsen
9137 _fnFilterComplete( settings, settings.oPreviousSearch, 1 );
9146 _api_register( 'state()', function () {
9147 return this.context.length ?
9148 this.context[0].oSavedState :
9153 _api_register( 'state.clear()', function () {
9154 return this.iterator( 'table', function ( settings ) {
9155 // Save an empty object
9156 settings.fnStateSaveCallback.call( settings.oInstance, settings, {} );
9161 _api_register( 'state.loaded()', function () {
9162 return this.context.length ?
9163 this.context[0].oLoadedState :
9168 _api_register( 'state.save()', function () {
9169 return this.iterator( 'table', function ( settings ) {
9170 _fnSaveState( settings );
9177 * Provide a common method for plug-ins to check the version of DataTables being
9178 * used, in order to ensure compatibility.
9180 * @param {string} version Version string to check for, in the format "X.Y.Z".
9181 * Note that the formats "X" and "X.Y" are also acceptable.
9182 * @returns {boolean} true if this version of DataTables is greater or equal to
9183 * the required version, or false if this version of DataTales is not
9189 * alert( $.fn.dataTable.versionCheck( '1.9.0' ) );
9191 DataTable.versionCheck = DataTable.fnVersionCheck = function( version )
9193 var aThis = DataTable.version.split('.');
9194 var aThat = version.split('.');
9197 for ( var i=0, iLen=aThat.length ; i<iLen ; i++ ) {
9198 iThis = parseInt( aThis[i], 10 ) || 0;
9199 iThat = parseInt( aThat[i], 10 ) || 0;
9201 // Parts are the same, keep comparing
9202 if (iThis === iThat) {
9206 // Parts are different, return immediately
9207 return iThis > iThat;
9215 * Check if a `<table>` node is a DataTable table already or not.
9217 * @param {node|jquery|string} table Table node, jQuery object or jQuery
9218 * selector for the table to test. Note that if more than more than one
9219 * table is passed on, only the first will be checked
9220 * @returns {boolean} true the table given is a DataTable, or false otherwise
9225 * if ( ! $.fn.DataTable.isDataTable( '#example' ) ) {
9226 * $('#example').dataTable();
9229 DataTable.isDataTable = DataTable.fnIsDataTable = function ( table )
9231 var t = $(table).get(0);
9234 if ( table instanceof DataTable.Api ) {
9238 $.each( DataTable.settings, function (i, o) {
9239 var head = o.nScrollHead ? $('table', o.nScrollHead)[0] : null;
9240 var foot = o.nScrollFoot ? $('table', o.nScrollFoot)[0] : null;
9242 if ( o.nTable === t || head === t || foot === t ) {
9252 * Get all DataTable tables that have been initialised - optionally you can
9253 * select to get only currently visible tables.
9255 * @param {boolean} [visible=false] Flag to indicate if you want all (default)
9256 * or visible tables only.
9257 * @returns {array} Array of `table` nodes (not DataTable instances) which are
9263 * $.each( $.fn.dataTable.tables(true), function () {
9264 * $(table).DataTable().columns.adjust();
9267 DataTable.tables = DataTable.fnTables = function ( visible )
9271 if ( $.isPlainObject( visible ) ) {
9273 visible = visible.visible;
9276 var a = $.map( DataTable.settings, function (o) {
9277 if ( !visible || (visible && $(o.nTable).is(':visible')) ) {
9289 * Convert from camel case parameters to Hungarian notation. This is made public
9290 * for the extensions to provide the same ability as DataTables core to accept
9291 * either the 1.9 style Hungarian notation, or the 1.10+ style camelCase
9294 * @param {object} src The model object which holds all parameters that can be
9296 * @param {object} user The object to convert from camel case to Hungarian.
9297 * @param {boolean} force When set to `true`, properties which already have a
9298 * Hungarian value in the `user` object will be overwritten. Otherwise they
9301 DataTable.camelToHungarian = _fnCamelToHungarian;
9308 _api_register( '$()', function ( selector, opts ) {
9310 rows = this.rows( opts ).nodes(), // Get all rows
9313 return $( [].concat(
9314 jqRows.filter( selector ).toArray(),
9315 jqRows.find( selector ).toArray()
9320 // jQuery functions to operate on the tables
9321 $.each( [ 'on', 'one', 'off' ], function (i, key) {
9322 _api_register( key+'()', function ( /* event, handler */ ) {
9323 var args = Array.prototype.slice.call(arguments);
9325 // Add the `dt` namespace automatically if it isn't already present
9326 args[0] = $.map( args[0].split( /\s/ ), function ( e ) {
9327 return ! e.match(/\.dt\b/) ?
9332 var inst = $( this.tables().nodes() );
9333 inst[key].apply( inst, args );
9339 _api_register( 'clear()', function () {
9340 return this.iterator( 'table', function ( settings ) {
9341 _fnClearTable( settings );
9346 _api_register( 'settings()', function () {
9347 return new _Api( this.context, this.context );
9351 _api_register( 'init()', function () {
9352 var ctx = this.context;
9353 return ctx.length ? ctx[0].oInit : null;
9357 _api_register( 'data()', function () {
9358 return this.iterator( 'table', function ( settings ) {
9359 return _pluck( settings.aoData, '_aData' );
9364 _api_register( 'destroy()', function ( remove ) {
9365 remove = remove || false;
9367 return this.iterator( 'table', function ( settings ) {
9368 var orig = settings.nTableWrapper.parentNode;
9369 var classes = settings.oClasses;
9370 var table = settings.nTable;
9371 var tbody = settings.nTBody;
9372 var thead = settings.nTHead;
9373 var tfoot = settings.nTFoot;
9374 var jqTable = $(table);
9375 var jqTbody = $(tbody);
9376 var jqWrapper = $(settings.nTableWrapper);
9377 var rows = $.map( settings.aoData, function (r) { return r.nTr; } );
9380 // Flag to note that the table is currently being destroyed - no action
9382 settings.bDestroying = true;
9384 // Fire off the destroy callbacks for plug-ins etc
9385 _fnCallbackFire( settings, "aoDestroyCallback", "destroy", [settings] );
9387 // If not being removed from the document, make all columns visible
9389 new _Api( settings ).columns().visible( true );
9392 // Blitz all `DT` namespaced events (these are internal events, the
9393 // lowercase, `dt` events are user subscribed and they are responsible
9394 // for removing them
9395 jqWrapper.off('.DT').find(':not(tbody *)').off('.DT');
9396 $(window).off('.DT-'+settings.sInstance);
9398 // When scrolling we had to break the table up - restore it
9399 if ( table != thead.parentNode ) {
9400 jqTable.children('thead').detach();
9401 jqTable.append( thead );
9404 if ( tfoot && table != tfoot.parentNode ) {
9405 jqTable.children('tfoot').detach();
9406 jqTable.append( tfoot );
9409 settings.aaSorting = [];
9410 settings.aaSortingFixed = [];
9411 _fnSortingClasses( settings );
9413 $( rows ).removeClass( settings.asStripeClasses.join(' ') );
9415 $('th, td', thead).removeClass( classes.sSortable+' '+
9416 classes.sSortableAsc+' '+classes.sSortableDesc+' '+classes.sSortableNone
9419 // Add the TR elements back into the table in their original order
9420 jqTbody.children().detach();
9421 jqTbody.append( rows );
9423 // Remove the DataTables generated nodes, events and classes
9424 var removedMethod = remove ? 'remove' : 'detach';
9425 jqTable[ removedMethod ]();
9426 jqWrapper[ removedMethod ]();
9428 // If we need to reattach the table to the document
9429 if ( ! remove && orig ) {
9430 // insertBefore acts like appendChild if !arg[1]
9431 orig.insertBefore( table, settings.nTableReinsertBefore );
9433 // Restore the width of the original table - was read from the style property,
9434 // so we can restore directly to that
9436 .css( 'width', settings.sDestroyWidth )
9437 .removeClass( classes.sTable );
9439 // If the were originally stripe classes - then we add them back here.
9440 // Note this is not fool proof (for example if not all rows had stripe
9441 // classes - but it's a good effort without getting carried away
9442 ien = settings.asDestroyStripes.length;
9445 jqTbody.children().each( function (i) {
9446 $(this).addClass( settings.asDestroyStripes[i % ien] );
9451 /* Remove the settings object from the settings array */
9452 var idx = $.inArray( settings, DataTable.settings );
9454 DataTable.settings.splice( idx, 1 );
9460 // Add the `every()` method for rows, columns and cells in a compact form
9461 $.each( [ 'column', 'row', 'cell' ], function ( i, type ) {
9462 _api_register( type+'s().every()', function ( fn ) {
9463 var opts = this.selector.opts;
9466 return this.iterator( type, function ( settings, arg1, arg2, arg3, arg4 ) {
9467 // Rows and columns:
9469 // arg2 - table counter
9470 // arg3 - loop counter
9474 // arg2 - column index
9475 // arg3 - table counter
9476 // arg4 - loop counter
9480 type==='cell' ? arg2 : opts,
9481 type==='cell' ? opts : undefined
9483 arg1, arg2, arg3, arg4
9490 // i18n method for extensions to be able to use the language object from the
9492 _api_register( 'i18n()', function ( token, def, plural ) {
9493 var ctx = this.context[0];
9494 var resolved = _fnGetObjectDataFn( token )( ctx.oLanguage );
9496 if ( resolved === undefined ) {
9500 if ( plural !== undefined && $.isPlainObject( resolved ) ) {
9501 resolved = resolved[ plural ] !== undefined ?
9502 resolved[ plural ] :
9506 return resolved.replace( '%d', plural ); // nb: plural might be undefined,
9509 * Version string for plug-ins to check compatibility. Allowed format is
9510 * `a.b.c-d` where: a:int, b:int, c:int, d:string(dev|beta|alpha). `d` is used
9511 * only for non-release builds. See http://semver.org/ for more information.
9514 * @default Version number
9516 DataTable.version = "1.10.21";
9519 * Private data store, containing all of the settings objects that are
9520 * created for the tables on a given page.
9522 * Note that the `DataTable.settings` object is aliased to
9523 * `jQuery.fn.dataTableExt` through which it may be accessed and
9524 * manipulated, or `jQuery.fn.dataTable.settings`.
9530 DataTable.settings = [];
9533 * Object models container, for the various models that DataTables has
9534 * available to it. These models define the objects that are used to hold
9535 * the active state and configuration of the table.
9538 DataTable.models = {};
9543 * Template object for the way in which DataTables holds information about
9544 * search information for the global filter and individual column filters.
9547 DataTable.models.oSearch = {
9549 * Flag to indicate if the filtering should be case insensitive or not
9553 "bCaseInsensitive": true,
9556 * Applied search term
9558 * @default <i>Empty string</i>
9563 * Flag to indicate if the search term should be interpreted as a
9564 * regular expression (true) or not (false) and therefore and special
9565 * regex characters escaped.
9572 * Flag to indicate if DataTables is to use its smart filtering or not.
9583 * Template object for the way in which DataTables holds information about
9584 * each individual row. This is the object format used for the settings
9588 DataTable.models.oRow = {
9590 * TR element for the row
9597 * Array of TD elements for each row. This is null until the row has been
9605 * Data object from the original data source for the row. This is either
9606 * an array if using the traditional form of DataTables, or an object if
9607 * using mData options. The exact type will depend on the passed in
9608 * data from the data source, or will be an array if using DOM a data
9610 * @type array|object
9616 * Sorting data cache - this array is ostensibly the same length as the
9617 * number of columns (although each index is generated only as it is
9618 * needed), and holds the data that is used for sorting each column in the
9619 * row. We do this cache generation at the start of the sort in order that
9620 * the formatting of the sort data need be done only once for each cell
9621 * per sort. This array should not be read from or written to by anything
9622 * other than the master sorting methods.
9630 * Per cell filtering data cache. As per the sort data cache, used to
9631 * increase the performance of the filtering in DataTables
9636 "_aFilterData": null,
9639 * Filtering data cache. This is the same as the cell filtering cache, but
9640 * in this case a string rather than an array. This is easily computed with
9641 * a join on `_aFilterData`, but is provided as a cache so the join isn't
9642 * needed on every search (memory traded for performance)
9647 "_sFilterRow": null,
9650 * Cache of the class name that DataTables has applied to the row, so we
9651 * can quickly look at this variable rather than needing to do a DOM check
9652 * on className for the nTr property.
9654 * @default <i>Empty string</i>
9660 * Denote if the original data source was from the DOM, or the data source
9661 * object. This is used for invalidating data, so DataTables can
9662 * automatically read data from the original source, unless uninstructed
9671 * Index in the aoData array. This saves an indexOf lookup when we have the
9672 * object, but want to know the index
9682 * Template object for the column information object in DataTables. This object
9683 * is held in the settings aoColumns array and contains all the information that
9684 * DataTables needs about each individual column.
9686 * Note that this object is related to {@link DataTable.defaults.column}
9687 * but this one is the internal data store for DataTables's cache of columns.
9688 * It should NOT be manipulated outside of DataTables. Any configuration should
9689 * be done through the initialisation options.
9692 DataTable.models.oColumn = {
9694 * Column index. This could be worked out on-the-fly with $.inArray, but it
9695 * is faster to just hold it as a variable
9702 * A list of the columns that sorting should occur on when this column
9703 * is sorted. That this property is an array allows multi-column sorting
9704 * to be defined for a column (for example first name / last name columns
9705 * would benefit from this). The values are integers pointing to the
9706 * columns to be sorted on (typically it will be a single integer pointing
9707 * at itself, but that doesn't need to be the case).
9713 * Define the sorting directions that are applied to the column, in sequence
9714 * as the column is repeatedly sorted upon - i.e. the first value is used
9715 * as the sorting direction when the column if first sorted (clicked on).
9716 * Sort it again (click again) and it will move on to the next index.
9717 * Repeat until loop.
9723 * Flag to indicate if the column is searchable, and thus should be included
9724 * in the filtering or not.
9727 "bSearchable": null,
9730 * Flag to indicate if the column is sortable or not.
9736 * Flag to indicate if the column is currently visible in the table or not
9742 * Store for manual type assignment using the `column.type` option. This
9743 * is held in store so we can manipulate the column's `sType` property.
9748 "_sManualType": null,
9751 * Flag to indicate if HTML5 data attributes should be used as the data
9752 * source for filtering or sorting. True is either are.
9760 * Developer definable function that is called whenever a cell is created (Ajax source,
9761 * etc) or processed for input (DOM source). This can be used as a compliment to mRender
9762 * allowing you to modify the DOM element (add background colour for example) when the
9763 * element is available.
9765 * @param {element} nTd The TD node that has been created
9766 * @param {*} sData The Data for the cell
9767 * @param {array|object} oData The data for the whole row
9768 * @param {int} iRow The row index for the aoData data store
9771 "fnCreatedCell": null,
9774 * Function to get data from a cell in a column. You should <b>never</b>
9775 * access data directly through _aData internally in DataTables - always use
9776 * the method attached to this property. It allows mData to function as
9777 * required. This function is automatically assigned by the column
9778 * initialisation method
9780 * @param {array|object} oData The data array/object for the array
9781 * (i.e. aoData[]._aData)
9782 * @param {string} sSpecific The specific data type you want to get -
9783 * 'display', 'type' 'filter' 'sort'
9784 * @returns {*} The data for the cell from the given row's data
9790 * Function to set data for a cell in the column. You should <b>never</b>
9791 * set the data directly to _aData internally in DataTables - always use
9792 * this method. It allows mData to function as required. This function
9793 * is automatically assigned by the column initialisation method
9795 * @param {array|object} oData The data array/object for the array
9796 * (i.e. aoData[]._aData)
9797 * @param {*} sValue Value to set
9803 * Property to read the value for the cells in the column from the data
9804 * source array / object. If null, then the default content is used, if a
9805 * function is given then the return from the function is used.
9806 * @type function|int|string|null
9812 * Partner property to mData which is used (only when defined) to get
9813 * the data - i.e. it is basically the same as mData, but without the
9814 * 'set' option, and also the data fed to it is the result from mData.
9815 * This is the rendering method to match the data method of mData.
9816 * @type function|int|string|null
9822 * Unique header TH/TD element for this column - this is what the sorting
9823 * listener is attached to (if sorting is enabled.)
9830 * Unique footer TH/TD element for this column (if there is one). Not used
9831 * in DataTables as such, but can be used for plug-ins to reference the
9832 * footer for each column.
9839 * The class to apply to all TD elements in the table's TBODY for the column
9846 * When DataTables calculates the column widths to assign to each column,
9847 * it finds the longest string in each column and then constructs a
9848 * temporary table and reads the widths from that. The problem with this
9849 * is that "mmm" is much wider then "iiii", but the latter is a longer
9850 * string - thus the calculation can go wrong (doing it properly and putting
9851 * it into an DOM object and measuring that is horribly(!) slow). Thus as
9852 * a "work around" we provide this option. It will append its value to the
9853 * text that is found to be the longest string for the column - i.e. padding.
9856 "sContentPadding": null,
9859 * Allows a default value to be given for a column's data, and will be used
9860 * whenever a null data source is encountered (this can be because mData
9861 * is set to null, or because the data source itself is null).
9865 "sDefaultContent": null,
9868 * Name for the column, allowing reference to the column by name as well as
9869 * by index (needs a lookup to work by name).
9875 * Custom sorting data type - defines which of the available plug-ins in
9876 * afnSortData the custom sorting will use - if any is defined.
9880 "sSortDataType": 'std',
9883 * Class to be applied to the header element when sorting on this column
9887 "sSortingClass": null,
9890 * Class to be applied to the header element when sorting on this column -
9891 * when jQuery UI theming is used.
9895 "sSortingClassJUI": null,
9898 * Title of the column - what is seen in the TH element (nTh).
9904 * Column sorting and filtering type
9911 * Width of the column
9918 * Width of the column when it was first "encountered"
9927 * Developer note: The properties of the object below are given in Hungarian
9928 * notation, that was used as the interface for DataTables prior to v1.10, however
9929 * from v1.10 onwards the primary interface is camel case. In order to avoid
9930 * breaking backwards compatibility utterly with this change, the Hungarian
9931 * version is still, internally the primary interface, but is is not documented
9932 * - hence the @name tags in each doc comment. This allows a Javascript function
9933 * to create a map from Hungarian notation to camel case (going the other direction
9934 * would require each property to be listed, which would at around 3K to the size
9935 * of DataTables, while this method is about a 0.5K hit.
9937 * Ultimately this does pave the way for Hungarian notation to be dropped
9938 * completely, but that is a massive amount of work and will break current
9939 * installs (therefore is on-hold until v2).
9943 * Initialisation options that can be given to DataTables at initialisation
9947 DataTable.defaults = {
9949 * An array of data to use for the table, passed in at initialisation which
9950 * will be used in preference to any data which is already in the DOM. This is
9951 * particularly useful for constructing tables purely in Javascript, for
9952 * example with a custom Ajax call.
9957 * @name DataTable.defaults.data
9960 * // Using a 2D array data source
9961 * $(document).ready( function () {
9962 * $('#example').dataTable( {
9964 * ['Trident', 'Internet Explorer 4.0', 'Win 95+', 4, 'X'],
9965 * ['Trident', 'Internet Explorer 5.0', 'Win 95+', 5, 'C'],
9968 * { "title": "Engine" },
9969 * { "title": "Browser" },
9970 * { "title": "Platform" },
9971 * { "title": "Version" },
9972 * { "title": "Grade" }
9978 * // Using an array of objects as a data source (`data`)
9979 * $(document).ready( function () {
9980 * $('#example').dataTable( {
9983 * "engine": "Trident",
9984 * "browser": "Internet Explorer 4.0",
9985 * "platform": "Win 95+",
9990 * "engine": "Trident",
9991 * "browser": "Internet Explorer 5.0",
9992 * "platform": "Win 95+",
9998 * { "title": "Engine", "data": "engine" },
9999 * { "title": "Browser", "data": "browser" },
10000 * { "title": "Platform", "data": "platform" },
10001 * { "title": "Version", "data": "version" },
10002 * { "title": "Grade", "data": "grade" }
10011 * If ordering is enabled, then DataTables will perform a first pass sort on
10012 * initialisation. You can define which column(s) the sort is performed
10013 * upon, and the sorting direction, with this variable. The `sorting` array
10014 * should contain an array for each column to be sorted initially containing
10015 * the column's index and a direction string ('asc' or 'desc').
10017 * @default [[0,'asc']]
10020 * @name DataTable.defaults.order
10023 * // Sort by 3rd column first, and then 4th column
10024 * $(document).ready( function() {
10025 * $('#example').dataTable( {
10026 * "order": [[2,'asc'], [3,'desc']]
10030 * // No initial sorting
10031 * $(document).ready( function() {
10032 * $('#example').dataTable( {
10037 "aaSorting": [[0,'asc']],
10041 * This parameter is basically identical to the `sorting` parameter, but
10042 * cannot be overridden by user interaction with the table. What this means
10043 * is that you could have a column (visible or hidden) which the sorting
10044 * will always be forced on first - any sorting after that (from the user)
10045 * will then be performed as required. This can be useful for grouping rows
10051 * @name DataTable.defaults.orderFixed
10054 * $(document).ready( function() {
10055 * $('#example').dataTable( {
10056 * "orderFixed": [[0,'asc']]
10060 "aaSortingFixed": [],
10064 * DataTables can be instructed to load data to display in the table from a
10065 * Ajax source. This option defines how that Ajax call is made and where to.
10067 * The `ajax` property has three different modes of operation, depending on
10068 * how it is defined. These are:
10070 * * `string` - Set the URL from where the data should be loaded from.
10071 * * `object` - Define properties for `jQuery.ajax`.
10072 * * `function` - Custom data get function
10077 * As a string, the `ajax` property simply defines the URL from which
10078 * DataTables will load data.
10083 * As an object, the parameters in the object are passed to
10084 * [jQuery.ajax](http://api.jquery.com/jQuery.ajax/) allowing fine control
10085 * of the Ajax request. DataTables has a number of default parameters which
10086 * you can override using this option. Please refer to the jQuery
10087 * documentation for a full description of the options available, although
10088 * the following parameters provide additional options in DataTables or
10089 * require special consideration:
10091 * * `data` - As with jQuery, `data` can be provided as an object, but it
10092 * can also be used as a function to manipulate the data DataTables sends
10093 * to the server. The function takes a single parameter, an object of
10094 * parameters with the values that DataTables has readied for sending. An
10095 * object may be returned which will be merged into the DataTables
10096 * defaults, or you can add the items to the object that was passed in and
10097 * not return anything from the function. This supersedes `fnServerParams`
10098 * from DataTables 1.9-.
10100 * * `dataSrc` - By default DataTables will look for the property `data` (or
10101 * `aaData` for compatibility with DataTables 1.9-) when obtaining data
10102 * from an Ajax source or for server-side processing - this parameter
10103 * allows that property to be changed. You can use Javascript dotted
10104 * object notation to get a data source for multiple levels of nesting, or
10105 * it my be used as a function. As a function it takes a single parameter,
10106 * the JSON returned from the server, which can be manipulated as
10107 * required, with the returned value being that used by DataTables as the
10108 * data source for the table. This supersedes `sAjaxDataProp` from
10111 * * `success` - Should not be overridden it is used internally in
10112 * DataTables. To manipulate / transform the data returned by the server
10113 * use `ajax.dataSrc`, or use `ajax` as a function (see below).
10118 * As a function, making the Ajax call is left up to yourself allowing
10119 * complete control of the Ajax request. Indeed, if desired, a method other
10120 * than Ajax could be used to obtain the required data, such as Web storage
10121 * or an AIR database.
10123 * The function is given four parameters and no return is required. The
10126 * 1. _object_ - Data to send to the server
10127 * 2. _function_ - Callback function that must be executed when the required
10128 * data has been obtained. That data should be passed into the callback
10129 * as the only parameter
10130 * 3. _object_ - DataTables settings object for the table
10132 * Note that this supersedes `fnServerData` from DataTables 1.9-.
10134 * @type string|object|function
10138 * @name DataTable.defaults.ajax
10142 * // Get JSON data from a file via Ajax.
10143 * // Note DataTables expects data in the form `{ data: [ ...data... ] }` by default).
10144 * $('#example').dataTable( {
10145 * "ajax": "data.json"
10149 * // Get JSON data from a file via Ajax, using `dataSrc` to change
10150 * // `data` to `tableData` (i.e. `{ tableData: [ ...data... ] }`)
10151 * $('#example').dataTable( {
10153 * "url": "data.json",
10154 * "dataSrc": "tableData"
10159 * // Get JSON data from a file via Ajax, using `dataSrc` to read data
10160 * // from a plain array rather than an array in an object
10161 * $('#example').dataTable( {
10163 * "url": "data.json",
10169 * // Manipulate the data returned from the server - add a link to data
10170 * // (note this can, should, be done using `render` for the column - this
10171 * // is just a simple example of how the data can be manipulated).
10172 * $('#example').dataTable( {
10174 * "url": "data.json",
10175 * "dataSrc": function ( json ) {
10176 * for ( var i=0, ien=json.length ; i<ien ; i++ ) {
10177 * json[i][0] = '<a href="/message/'+json[i][0]+'>View message</a>';
10185 * // Add data to the request
10186 * $('#example').dataTable( {
10188 * "url": "data.json",
10189 * "data": function ( d ) {
10191 * "extra_search": $('#extra').val()
10198 * // Send request as POST
10199 * $('#example').dataTable( {
10201 * "url": "data.json",
10207 * // Get the data from localStorage (could interface with a form for
10208 * // adding, editing and removing rows).
10209 * $('#example').dataTable( {
10210 * "ajax": function (data, callback, settings) {
10212 * JSON.parse( localStorage.getItem('dataTablesData') )
10221 * This parameter allows you to readily specify the entries in the length drop
10222 * down menu that DataTables shows when pagination is enabled. It can be
10223 * either a 1D array of options which will be used for both the displayed
10224 * option and the value, or a 2D array which will use the array in the first
10225 * position as the value, and the array in the second position as the
10226 * displayed options (useful for language strings such as 'All').
10228 * Note that the `pageLength` property will be automatically set to the
10229 * first value given in this array, unless `pageLength` is also provided.
10231 * @default [ 10, 25, 50, 100 ]
10234 * @name DataTable.defaults.lengthMenu
10237 * $(document).ready( function() {
10238 * $('#example').dataTable( {
10239 * "lengthMenu": [[10, 25, 50, -1], [10, 25, 50, "All"]]
10243 "aLengthMenu": [ 10, 25, 50, 100 ],
10247 * The `columns` option in the initialisation parameter allows you to define
10248 * details about the way individual columns behave. For a full list of
10249 * column options that can be set, please see
10250 * {@link DataTable.defaults.column}. Note that if you use `columns` to
10251 * define your columns, you must have an entry in the array for every single
10252 * column that you have in your table (these can be null if you don't which
10253 * to specify any options).
10256 * @name DataTable.defaults.column
10261 * Very similar to `columns`, `columnDefs` allows you to target a specific
10262 * column, multiple columns, or all columns, using the `targets` property of
10263 * each object in the array. This allows great flexibility when creating
10264 * tables, as the `columnDefs` arrays can be of any length, targeting the
10265 * columns you specifically want. `columnDefs` may use any of the column
10266 * options available: {@link DataTable.defaults.column}, but it _must_
10267 * have `targets` defined in each object in the array. Values in the `targets`
10270 * <li>a string - class name will be matched on the TH for the column</li>
10271 * <li>0 or a positive integer - column index counting from the left</li>
10272 * <li>a negative integer - column index counting from the right</li>
10273 * <li>the string "_all" - all columns (i.e. assign a default)</li>
10277 * @name DataTable.defaults.columnDefs
10279 "aoColumnDefs": null,
10283 * Basically the same as `search`, this parameter defines the individual column
10284 * filtering state at initialisation time. The array must be of the same size
10285 * as the number of columns, and each element be an object with the parameters
10286 * `search` and `escapeRegex` (the latter is optional). 'null' is also
10287 * accepted and the default will be used.
10292 * @name DataTable.defaults.searchCols
10295 * $(document).ready( function() {
10296 * $('#example').dataTable( {
10299 * { "search": "My filter" },
10301 * { "search": "^[0-9]", "escapeRegex": false }
10306 "aoSearchCols": [],
10310 * An array of CSS classes that should be applied to displayed rows. This
10311 * array may be of any length, and DataTables will apply each class
10312 * sequentially, looping when required.
10314 * @default null <i>Will take the values determined by the `oClasses.stripe*`
10318 * @name DataTable.defaults.stripeClasses
10321 * $(document).ready( function() {
10322 * $('#example').dataTable( {
10323 * "stripeClasses": [ 'strip1', 'strip2', 'strip3' ]
10327 "asStripeClasses": null,
10331 * Enable or disable automatic column width calculation. This can be disabled
10332 * as an optimisation (it takes some time to calculate the widths) if the
10333 * tables widths are passed in using `columns`.
10338 * @name DataTable.defaults.autoWidth
10341 * $(document).ready( function () {
10342 * $('#example').dataTable( {
10343 * "autoWidth": false
10347 "bAutoWidth": true,
10351 * Deferred rendering can provide DataTables with a huge speed boost when you
10352 * are using an Ajax or JS data source for the table. This option, when set to
10353 * true, will cause DataTables to defer the creation of the table elements for
10354 * each row until they are needed for a draw - saving a significant amount of
10360 * @name DataTable.defaults.deferRender
10363 * $(document).ready( function() {
10364 * $('#example').dataTable( {
10365 * "ajax": "sources/arrays.txt",
10366 * "deferRender": true
10370 "bDeferRender": false,
10374 * Replace a DataTable which matches the given selector and replace it with
10375 * one which has the properties of the new initialisation object passed. If no
10376 * table matches the selector, then the new DataTable will be constructed as
10382 * @name DataTable.defaults.destroy
10385 * $(document).ready( function() {
10386 * $('#example').dataTable( {
10387 * "srollY": "200px",
10388 * "paginate": false
10391 * // Some time later....
10392 * $('#example').dataTable( {
10402 * Enable or disable filtering of data. Filtering in DataTables is "smart" in
10403 * that it allows the end user to input multiple words (space separated) and
10404 * will match a row containing those words, even if not in the order that was
10405 * specified (this allow matching across multiple columns). Note that if you
10406 * wish to use filtering in DataTables this must remain 'true' - to remove the
10407 * default filtering input box and retain filtering abilities, please use
10408 * {@link DataTable.defaults.dom}.
10413 * @name DataTable.defaults.searching
10416 * $(document).ready( function () {
10417 * $('#example').dataTable( {
10418 * "searching": false
10426 * Enable or disable the table information display. This shows information
10427 * about the data that is currently visible on the page, including information
10428 * about filtered data if that action is being performed.
10433 * @name DataTable.defaults.info
10436 * $(document).ready( function () {
10437 * $('#example').dataTable( {
10446 * Allows the end user to select the size of a formatted page from a select
10447 * menu (sizes are 10, 25, 50 and 100). Requires pagination (`paginate`).
10452 * @name DataTable.defaults.lengthChange
10455 * $(document).ready( function () {
10456 * $('#example').dataTable( {
10457 * "lengthChange": false
10461 "bLengthChange": true,
10465 * Enable or disable pagination.
10470 * @name DataTable.defaults.paging
10473 * $(document).ready( function () {
10474 * $('#example').dataTable( {
10483 * Enable or disable the display of a 'processing' indicator when the table is
10484 * being processed (e.g. a sort). This is particularly useful for tables with
10485 * large amounts of data where it can take a noticeable amount of time to sort
10491 * @name DataTable.defaults.processing
10494 * $(document).ready( function () {
10495 * $('#example').dataTable( {
10496 * "processing": true
10500 "bProcessing": false,
10504 * Retrieve the DataTables object for the given selector. Note that if the
10505 * table has already been initialised, this parameter will cause DataTables
10506 * to simply return the object that has already been set up - it will not take
10507 * account of any changes you might have made to the initialisation object
10508 * passed to DataTables (setting this parameter to true is an acknowledgement
10509 * that you understand this). `destroy` can be used to reinitialise a table if
10515 * @name DataTable.defaults.retrieve
10518 * $(document).ready( function() {
10523 * function initTable ()
10525 * return $('#example').dataTable( {
10526 * "scrollY": "200px",
10527 * "paginate": false,
10532 * function tableActions ()
10534 * var table = initTable();
10535 * // perform API operations with oTable
10538 "bRetrieve": false,
10542 * When vertical (y) scrolling is enabled, DataTables will force the height of
10543 * the table's viewport to the given height at all times (useful for layout).
10544 * However, this can look odd when filtering data down to a small data set,
10545 * and the footer is left "floating" further down. This parameter (when
10546 * enabled) will cause DataTables to collapse the table's viewport down when
10547 * the result set will fit within the given Y height.
10552 * @name DataTable.defaults.scrollCollapse
10555 * $(document).ready( function() {
10556 * $('#example').dataTable( {
10557 * "scrollY": "200",
10558 * "scrollCollapse": true
10562 "bScrollCollapse": false,
10566 * Configure DataTables to use server-side processing. Note that the
10567 * `ajax` parameter must also be given in order to give DataTables a
10568 * source to obtain the required data for each draw.
10573 * @dtopt Server-side
10574 * @name DataTable.defaults.serverSide
10577 * $(document).ready( function () {
10578 * $('#example').dataTable( {
10579 * "serverSide": true,
10580 * "ajax": "xhr.php"
10584 "bServerSide": false,
10588 * Enable or disable sorting of columns. Sorting of individual columns can be
10589 * disabled by the `sortable` option for each column.
10594 * @name DataTable.defaults.ordering
10597 * $(document).ready( function () {
10598 * $('#example').dataTable( {
10599 * "ordering": false
10607 * Enable or display DataTables' ability to sort multiple columns at the
10608 * same time (activated by shift-click by the user).
10613 * @name DataTable.defaults.orderMulti
10616 * // Disable multiple column sorting ability
10617 * $(document).ready( function () {
10618 * $('#example').dataTable( {
10619 * "orderMulti": false
10623 "bSortMulti": true,
10627 * Allows control over whether DataTables should use the top (true) unique
10628 * cell that is found for a single column, or the bottom (false - default).
10629 * This is useful when using complex headers.
10634 * @name DataTable.defaults.orderCellsTop
10637 * $(document).ready( function() {
10638 * $('#example').dataTable( {
10639 * "orderCellsTop": true
10643 "bSortCellsTop": false,
10647 * Enable or disable the addition of the classes `sorting\_1`, `sorting\_2` and
10648 * `sorting\_3` to the columns which are currently being sorted on. This is
10649 * presented as a feature switch as it can increase processing time (while
10650 * classes are removed and added) so for large data sets you might want to
10656 * @name DataTable.defaults.orderClasses
10659 * $(document).ready( function () {
10660 * $('#example').dataTable( {
10661 * "orderClasses": false
10665 "bSortClasses": true,
10669 * Enable or disable state saving. When enabled HTML5 `localStorage` will be
10670 * used to save table display information such as pagination information,
10671 * display length, filtering and sorting. As such when the end user reloads
10672 * the page the display display will match what thy had previously set up.
10674 * Due to the use of `localStorage` the default state saving is not supported
10675 * in IE6 or 7. If state saving is required in those browsers, use
10676 * `stateSaveCallback` to provide a storage solution such as cookies.
10681 * @name DataTable.defaults.stateSave
10684 * $(document).ready( function () {
10685 * $('#example').dataTable( {
10686 * "stateSave": true
10690 "bStateSave": false,
10694 * This function is called when a TR element is created (and all TD child
10695 * elements have been inserted), or registered if using a DOM source, allowing
10696 * manipulation of the TR element (adding classes etc).
10698 * @param {node} row "TR" element for the current row
10699 * @param {array} data Raw data array for this row
10700 * @param {int} dataIndex The index of this row in the internal aoData array
10703 * @name DataTable.defaults.createdRow
10706 * $(document).ready( function() {
10707 * $('#example').dataTable( {
10708 * "createdRow": function( row, data, dataIndex ) {
10709 * // Bold the grade for all 'A' grade browsers
10710 * if ( data[4] == "A" )
10712 * $('td:eq(4)', row).html( '<b>A</b>' );
10718 "fnCreatedRow": null,
10722 * This function is called on every 'draw' event, and allows you to
10723 * dynamically modify any aspect you want about the created DOM.
10725 * @param {object} settings DataTables settings object
10728 * @name DataTable.defaults.drawCallback
10731 * $(document).ready( function() {
10732 * $('#example').dataTable( {
10733 * "drawCallback": function( settings ) {
10734 * alert( 'DataTables has redrawn the table' );
10739 "fnDrawCallback": null,
10743 * Identical to fnHeaderCallback() but for the table footer this function
10744 * allows you to modify the table footer on every 'draw' event.
10746 * @param {node} foot "TR" element for the footer
10747 * @param {array} data Full table data (as derived from the original HTML)
10748 * @param {int} start Index for the current display starting point in the
10750 * @param {int} end Index for the current display ending point in the
10752 * @param {array int} display Index array to translate the visual position
10753 * to the full data array
10756 * @name DataTable.defaults.footerCallback
10759 * $(document).ready( function() {
10760 * $('#example').dataTable( {
10761 * "footerCallback": function( tfoot, data, start, end, display ) {
10762 * tfoot.getElementsByTagName('th')[0].innerHTML = "Starting index is "+start;
10767 "fnFooterCallback": null,
10771 * When rendering large numbers in the information element for the table
10772 * (i.e. "Showing 1 to 10 of 57 entries") DataTables will render large numbers
10773 * to have a comma separator for the 'thousands' units (e.g. 1 million is
10774 * rendered as "1,000,000") to help readability for the end user. This
10775 * function will override the default method DataTables uses.
10778 * @param {int} toFormat number to be formatted
10779 * @returns {string} formatted string for DataTables to show the number
10782 * @name DataTable.defaults.formatNumber
10785 * // Format a number using a single quote for the separator (note that
10786 * // this can also be done with the language.thousands option)
10787 * $(document).ready( function() {
10788 * $('#example').dataTable( {
10789 * "formatNumber": function ( toFormat ) {
10790 * return toFormat.toString().replace(
10791 * /\B(?=(\d{3})+(?!\d))/g, "'"
10797 "fnFormatNumber": function ( toFormat ) {
10798 return toFormat.toString().replace(
10799 /\B(?=(\d{3})+(?!\d))/g,
10800 this.oLanguage.sThousands
10806 * This function is called on every 'draw' event, and allows you to
10807 * dynamically modify the header row. This can be used to calculate and
10808 * display useful information about the table.
10810 * @param {node} head "TR" element for the header
10811 * @param {array} data Full table data (as derived from the original HTML)
10812 * @param {int} start Index for the current display starting point in the
10814 * @param {int} end Index for the current display ending point in the
10816 * @param {array int} display Index array to translate the visual position
10817 * to the full data array
10820 * @name DataTable.defaults.headerCallback
10823 * $(document).ready( function() {
10824 * $('#example').dataTable( {
10825 * "fheaderCallback": function( head, data, start, end, display ) {
10826 * head.getElementsByTagName('th')[0].innerHTML = "Displaying "+(end-start)+" records";
10831 "fnHeaderCallback": null,
10835 * The information element can be used to convey information about the current
10836 * state of the table. Although the internationalisation options presented by
10837 * DataTables are quite capable of dealing with most customisations, there may
10838 * be times where you wish to customise the string further. This callback
10839 * allows you to do exactly that.
10841 * @param {object} oSettings DataTables settings object
10842 * @param {int} start Starting position in data for the draw
10843 * @param {int} end End position in data for the draw
10844 * @param {int} max Total number of rows in the table (regardless of
10846 * @param {int} total Total number of rows in the data set, after filtering
10847 * @param {string} pre The string that DataTables has formatted using it's
10849 * @returns {string} The string to be displayed in the information element.
10852 * @name DataTable.defaults.infoCallback
10855 * $('#example').dataTable( {
10856 * "infoCallback": function( settings, start, end, max, total, pre ) {
10857 * return start +" to "+ end;
10861 "fnInfoCallback": null,
10865 * Called when the table has been initialised. Normally DataTables will
10866 * initialise sequentially and there will be no need for this function,
10867 * however, this does not hold true when using external language information
10868 * since that is obtained using an async XHR call.
10870 * @param {object} settings DataTables settings object
10871 * @param {object} json The JSON object request from the server - only
10872 * present if client-side Ajax sourced data is used
10875 * @name DataTable.defaults.initComplete
10878 * $(document).ready( function() {
10879 * $('#example').dataTable( {
10880 * "initComplete": function(settings, json) {
10881 * alert( 'DataTables has finished its initialisation.' );
10886 "fnInitComplete": null,
10890 * Called at the very start of each table draw and can be used to cancel the
10891 * draw by returning false, any other return (including undefined) results in
10892 * the full draw occurring).
10894 * @param {object} settings DataTables settings object
10895 * @returns {boolean} False will cancel the draw, anything else (including no
10896 * return) will allow it to complete.
10899 * @name DataTable.defaults.preDrawCallback
10902 * $(document).ready( function() {
10903 * $('#example').dataTable( {
10904 * "preDrawCallback": function( settings ) {
10905 * if ( $('#test').val() == 1 ) {
10912 "fnPreDrawCallback": null,
10916 * This function allows you to 'post process' each row after it have been
10917 * generated for each table draw, but before it is rendered on screen. This
10918 * function might be used for setting the row class name etc.
10920 * @param {node} row "TR" element for the current row
10921 * @param {array} data Raw data array for this row
10922 * @param {int} displayIndex The display index for the current table draw
10923 * @param {int} displayIndexFull The index of the data in the full list of
10924 * rows (after filtering)
10927 * @name DataTable.defaults.rowCallback
10930 * $(document).ready( function() {
10931 * $('#example').dataTable( {
10932 * "rowCallback": function( row, data, displayIndex, displayIndexFull ) {
10933 * // Bold the grade for all 'A' grade browsers
10934 * if ( data[4] == "A" ) {
10935 * $('td:eq(4)', row).html( '<b>A</b>' );
10941 "fnRowCallback": null,
10945 * __Deprecated__ The functionality provided by this parameter has now been
10946 * superseded by that provided through `ajax`, which should be used instead.
10948 * This parameter allows you to override the default function which obtains
10949 * the data from the server so something more suitable for your application.
10950 * For example you could use POST data, or pull information from a Gears or
10954 * @param {string} source HTTP source to obtain the data from (`ajax`)
10955 * @param {array} data A key/value pair object containing the data to send
10957 * @param {function} callback to be called on completion of the data get
10958 * process that will draw the data on the page.
10959 * @param {object} settings DataTables settings object
10962 * @dtopt Server-side
10963 * @name DataTable.defaults.serverData
10965 * @deprecated 1.10. Please use `ajax` for this functionality now.
10967 "fnServerData": null,
10971 * __Deprecated__ The functionality provided by this parameter has now been
10972 * superseded by that provided through `ajax`, which should be used instead.
10974 * It is often useful to send extra data to the server when making an Ajax
10975 * request - for example custom filtering information, and this callback
10976 * function makes it trivial to send extra information to the server. The
10977 * passed in parameter is the data set that has been constructed by
10978 * DataTables, and you can add to this or modify it as you require.
10980 * @param {array} data Data array (array of objects which are name/value
10981 * pairs) that has been constructed by DataTables and will be sent to the
10982 * server. In the case of Ajax sourced data with server-side processing
10983 * this will be an empty array, for server-side processing there will be a
10984 * significant number of parameters!
10985 * @returns {undefined} Ensure that you modify the data array passed in,
10986 * as this is passed by reference.
10989 * @dtopt Server-side
10990 * @name DataTable.defaults.serverParams
10992 * @deprecated 1.10. Please use `ajax` for this functionality now.
10994 "fnServerParams": null,
10998 * Load the table state. With this function you can define from where, and how, the
10999 * state of a table is loaded. By default DataTables will load from `localStorage`
11000 * but you might wish to use a server-side database or cookies.
11003 * @param {object} settings DataTables settings object
11004 * @param {object} callback Callback that can be executed when done. It
11005 * should be passed the loaded state object.
11006 * @return {object} The DataTables state object to be loaded
11009 * @name DataTable.defaults.stateLoadCallback
11012 * $(document).ready( function() {
11013 * $('#example').dataTable( {
11014 * "stateSave": true,
11015 * "stateLoadCallback": function (settings, callback) {
11017 * "url": "/state_load",
11018 * "dataType": "json",
11019 * "success": function (json) {
11020 * callback( json );
11027 "fnStateLoadCallback": function ( settings ) {
11030 (settings.iStateDuration === -1 ? sessionStorage : localStorage).getItem(
11031 'DataTables_'+settings.sInstance+'_'+location.pathname
11041 * Callback which allows modification of the saved state prior to loading that state.
11042 * This callback is called when the table is loading state from the stored data, but
11043 * prior to the settings object being modified by the saved state. Note that for
11044 * plug-in authors, you should use the `stateLoadParams` event to load parameters for
11047 * @param {object} settings DataTables settings object
11048 * @param {object} data The state object that is to be loaded
11051 * @name DataTable.defaults.stateLoadParams
11054 * // Remove a saved filter, so filtering is never loaded
11055 * $(document).ready( function() {
11056 * $('#example').dataTable( {
11057 * "stateSave": true,
11058 * "stateLoadParams": function (settings, data) {
11059 * data.oSearch.sSearch = "";
11065 * // Disallow state loading by returning false
11066 * $(document).ready( function() {
11067 * $('#example').dataTable( {
11068 * "stateSave": true,
11069 * "stateLoadParams": function (settings, data) {
11075 "fnStateLoadParams": null,
11079 * Callback that is called when the state has been loaded from the state saving method
11080 * and the DataTables settings object has been modified as a result of the loaded state.
11082 * @param {object} settings DataTables settings object
11083 * @param {object} data The state object that was loaded
11086 * @name DataTable.defaults.stateLoaded
11089 * // Show an alert with the filtering value that was saved
11090 * $(document).ready( function() {
11091 * $('#example').dataTable( {
11092 * "stateSave": true,
11093 * "stateLoaded": function (settings, data) {
11094 * alert( 'Saved filter was: '+data.oSearch.sSearch );
11099 "fnStateLoaded": null,
11103 * Save the table state. This function allows you to define where and how the state
11104 * information for the table is stored By default DataTables will use `localStorage`
11105 * but you might wish to use a server-side database or cookies.
11108 * @param {object} settings DataTables settings object
11109 * @param {object} data The state object to be saved
11112 * @name DataTable.defaults.stateSaveCallback
11115 * $(document).ready( function() {
11116 * $('#example').dataTable( {
11117 * "stateSave": true,
11118 * "stateSaveCallback": function (settings, data) {
11119 * // Send an Ajax request to the server with the state object
11121 * "url": "/state_save",
11123 * "dataType": "json",
11125 * "success": function () {}
11131 "fnStateSaveCallback": function ( settings, data ) {
11133 (settings.iStateDuration === -1 ? sessionStorage : localStorage).setItem(
11134 'DataTables_'+settings.sInstance+'_'+location.pathname,
11135 JSON.stringify( data )
11142 * Callback which allows modification of the state to be saved. Called when the table
11143 * has changed state a new state save is required. This method allows modification of
11144 * the state saving object prior to actually doing the save, including addition or
11145 * other state properties or modification. Note that for plug-in authors, you should
11146 * use the `stateSaveParams` event to save parameters for a plug-in.
11148 * @param {object} settings DataTables settings object
11149 * @param {object} data The state object to be saved
11152 * @name DataTable.defaults.stateSaveParams
11155 * // Remove a saved filter, so filtering is never saved
11156 * $(document).ready( function() {
11157 * $('#example').dataTable( {
11158 * "stateSave": true,
11159 * "stateSaveParams": function (settings, data) {
11160 * data.oSearch.sSearch = "";
11165 "fnStateSaveParams": null,
11169 * Duration for which the saved state information is considered valid. After this period
11170 * has elapsed the state will be returned to the default.
11171 * Value is given in seconds.
11173 * @default 7200 <i>(2 hours)</i>
11176 * @name DataTable.defaults.stateDuration
11179 * $(document).ready( function() {
11180 * $('#example').dataTable( {
11181 * "stateDuration": 60*60*24; // 1 day
11185 "iStateDuration": 7200,
11189 * When enabled DataTables will not make a request to the server for the first
11190 * page draw - rather it will use the data already on the page (no sorting etc
11191 * will be applied to it), thus saving on an XHR at load time. `deferLoading`
11192 * is used to indicate that deferred loading is required, but it is also used
11193 * to tell DataTables how many records there are in the full table (allowing
11194 * the information element and pagination to be displayed correctly). In the case
11195 * where a filtering is applied to the table on initial load, this can be
11196 * indicated by giving the parameter as an array, where the first element is
11197 * the number of records available after filtering and the second element is the
11198 * number of records without filtering (allowing the table information element
11199 * to be shown correctly).
11200 * @type int | array
11204 * @name DataTable.defaults.deferLoading
11207 * // 57 records available in the table, no filtering applied
11208 * $(document).ready( function() {
11209 * $('#example').dataTable( {
11210 * "serverSide": true,
11211 * "ajax": "scripts/server_processing.php",
11212 * "deferLoading": 57
11217 * // 57 records after filtering, 100 without filtering (an initial filter applied)
11218 * $(document).ready( function() {
11219 * $('#example').dataTable( {
11220 * "serverSide": true,
11221 * "ajax": "scripts/server_processing.php",
11222 * "deferLoading": [ 57, 100 ],
11224 * "search": "my_filter"
11229 "iDeferLoading": null,
11233 * Number of rows to display on a single page when using pagination. If
11234 * feature enabled (`lengthChange`) then the end user will be able to override
11235 * this to a custom setting using a pop-up menu.
11240 * @name DataTable.defaults.pageLength
11243 * $(document).ready( function() {
11244 * $('#example').dataTable( {
11249 "iDisplayLength": 10,
11253 * Define the starting point for data display when using DataTables with
11254 * pagination. Note that this parameter is the number of records, rather than
11255 * the page number, so if you have 10 records per page and want to start on
11256 * the third page, it should be "20".
11261 * @name DataTable.defaults.displayStart
11264 * $(document).ready( function() {
11265 * $('#example').dataTable( {
11266 * "displayStart": 20
11270 "iDisplayStart": 0,
11274 * By default DataTables allows keyboard navigation of the table (sorting, paging,
11275 * and filtering) by adding a `tabindex` attribute to the required elements. This
11276 * allows you to tab through the controls and press the enter key to activate them.
11277 * The tabindex is default 0, meaning that the tab follows the flow of the document.
11278 * You can overrule this using this parameter if you wish. Use a value of -1 to
11279 * disable built-in keyboard navigation.
11284 * @name DataTable.defaults.tabIndex
11287 * $(document).ready( function() {
11288 * $('#example').dataTable( {
11297 * Classes that DataTables assigns to the various components and features
11298 * that it adds to the HTML table. This allows classes to be configured
11299 * during initialisation in addition to through the static
11300 * {@link DataTable.ext.oStdClasses} object).
11302 * @name DataTable.defaults.classes
11308 * All strings that DataTables uses in the user interface that it creates
11309 * are defined in this object, allowing you to modified them individually or
11310 * completely replace them all as required.
11312 * @name DataTable.defaults.language
11316 * Strings that are used for WAI-ARIA labels and controls only (these are not
11317 * actually visible on the page, but will be read by screenreaders, and thus
11318 * must be internationalised as well).
11320 * @name DataTable.defaults.language.aria
11324 * ARIA label that is added to the table headers when the column may be
11325 * sorted ascending by activing the column (click or return when focused).
11326 * Note that the column header is prefixed to this string.
11328 * @default : activate to sort column ascending
11331 * @name DataTable.defaults.language.aria.sortAscending
11334 * $(document).ready( function() {
11335 * $('#example').dataTable( {
11338 * "sortAscending": " - click/return to sort ascending"
11344 "sSortAscending": ": activate to sort column ascending",
11347 * ARIA label that is added to the table headers when the column may be
11348 * sorted descending by activing the column (click or return when focused).
11349 * Note that the column header is prefixed to this string.
11351 * @default : activate to sort column ascending
11354 * @name DataTable.defaults.language.aria.sortDescending
11357 * $(document).ready( function() {
11358 * $('#example').dataTable( {
11361 * "sortDescending": " - click/return to sort descending"
11367 "sSortDescending": ": activate to sort column descending"
11371 * Pagination string used by DataTables for the built-in pagination
11374 * @name DataTable.defaults.language.paginate
11378 * Text to use when using the 'full_numbers' type of pagination for the
11379 * button to take the user to the first page.
11384 * @name DataTable.defaults.language.paginate.first
11387 * $(document).ready( function() {
11388 * $('#example').dataTable( {
11391 * "first": "First page"
11401 * Text to use when using the 'full_numbers' type of pagination for the
11402 * button to take the user to the last page.
11407 * @name DataTable.defaults.language.paginate.last
11410 * $(document).ready( function() {
11411 * $('#example').dataTable( {
11414 * "last": "Last page"
11424 * Text to use for the 'next' pagination button (to take the user to the
11430 * @name DataTable.defaults.language.paginate.next
11433 * $(document).ready( function() {
11434 * $('#example').dataTable( {
11437 * "next": "Next page"
11447 * Text to use for the 'previous' pagination button (to take the user to
11448 * the previous page).
11450 * @default Previous
11453 * @name DataTable.defaults.language.paginate.previous
11456 * $(document).ready( function() {
11457 * $('#example').dataTable( {
11460 * "previous": "Previous page"
11466 "sPrevious": "Previous"
11470 * This string is shown in preference to `zeroRecords` when the table is
11471 * empty of data (regardless of filtering). Note that this is an optional
11472 * parameter - if it is not given, the value of `zeroRecords` will be used
11473 * instead (either the default or given value).
11475 * @default No data available in table
11478 * @name DataTable.defaults.language.emptyTable
11481 * $(document).ready( function() {
11482 * $('#example').dataTable( {
11484 * "emptyTable": "No data available in table"
11489 "sEmptyTable": "No data available in table",
11493 * This string gives information to the end user about the information
11494 * that is current on display on the page. The following tokens can be
11495 * used in the string and will be dynamically replaced as the table
11496 * display updates. This tokens can be placed anywhere in the string, or
11497 * removed as needed by the language requires:
11499 * * `\_START\_` - Display index of the first record on the current page
11500 * * `\_END\_` - Display index of the last record on the current page
11501 * * `\_TOTAL\_` - Number of records in the table after filtering
11502 * * `\_MAX\_` - Number of records in the table without filtering
11503 * * `\_PAGE\_` - Current page number
11504 * * `\_PAGES\_` - Total number of pages of data in the table
11507 * @default Showing _START_ to _END_ of _TOTAL_ entries
11510 * @name DataTable.defaults.language.info
11513 * $(document).ready( function() {
11514 * $('#example').dataTable( {
11516 * "info": "Showing page _PAGE_ of _PAGES_"
11521 "sInfo": "Showing _START_ to _END_ of _TOTAL_ entries",
11525 * Display information string for when the table is empty. Typically the
11526 * format of this string should match `info`.
11528 * @default Showing 0 to 0 of 0 entries
11531 * @name DataTable.defaults.language.infoEmpty
11534 * $(document).ready( function() {
11535 * $('#example').dataTable( {
11537 * "infoEmpty": "No entries to show"
11542 "sInfoEmpty": "Showing 0 to 0 of 0 entries",
11546 * When a user filters the information in a table, this string is appended
11547 * to the information (`info`) to give an idea of how strong the filtering
11548 * is. The variable _MAX_ is dynamically updated.
11550 * @default (filtered from _MAX_ total entries)
11553 * @name DataTable.defaults.language.infoFiltered
11556 * $(document).ready( function() {
11557 * $('#example').dataTable( {
11559 * "infoFiltered": " - filtering from _MAX_ records"
11564 "sInfoFiltered": "(filtered from _MAX_ total entries)",
11568 * If can be useful to append extra information to the info string at times,
11569 * and this variable does exactly that. This information will be appended to
11570 * the `info` (`infoEmpty` and `infoFiltered` in whatever combination they are
11571 * being used) at all times.
11573 * @default <i>Empty string</i>
11576 * @name DataTable.defaults.language.infoPostFix
11579 * $(document).ready( function() {
11580 * $('#example').dataTable( {
11582 * "infoPostFix": "All records shown are derived from real information."
11587 "sInfoPostFix": "",
11591 * This decimal place operator is a little different from the other
11592 * language options since DataTables doesn't output floating point
11593 * numbers, so it won't ever use this for display of a number. Rather,
11594 * what this parameter does is modify the sort methods of the table so
11595 * that numbers which are in a format which has a character other than
11596 * a period (`.`) as a decimal place will be sorted numerically.
11598 * Note that numbers with different decimal places cannot be shown in
11599 * the same table and still be sortable, the table must be consistent.
11600 * However, multiple different tables on the page can use different
11601 * decimal place characters.
11606 * @name DataTable.defaults.language.decimal
11609 * $(document).ready( function() {
11610 * $('#example').dataTable( {
11622 * DataTables has a build in number formatter (`formatNumber`) which is
11623 * used to format large numbers that are used in the table information.
11624 * By default a comma is used, but this can be trivially changed to any
11625 * character you wish with this parameter.
11630 * @name DataTable.defaults.language.thousands
11633 * $(document).ready( function() {
11634 * $('#example').dataTable( {
11645 * Detail the action that will be taken when the drop down menu for the
11646 * pagination length option is changed. The '_MENU_' variable is replaced
11647 * with a default select list of 10, 25, 50 and 100, and can be replaced
11648 * with a custom select box if required.
11650 * @default Show _MENU_ entries
11653 * @name DataTable.defaults.language.lengthMenu
11656 * // Language change only
11657 * $(document).ready( function() {
11658 * $('#example').dataTable( {
11660 * "lengthMenu": "Display _MENU_ records"
11666 * // Language and options change
11667 * $(document).ready( function() {
11668 * $('#example').dataTable( {
11670 * "lengthMenu": 'Display <select>'+
11671 * '<option value="10">10</option>'+
11672 * '<option value="20">20</option>'+
11673 * '<option value="30">30</option>'+
11674 * '<option value="40">40</option>'+
11675 * '<option value="50">50</option>'+
11676 * '<option value="-1">All</option>'+
11677 * '</select> records'
11682 "sLengthMenu": "Show _MENU_ entries",
11686 * When using Ajax sourced data and during the first draw when DataTables is
11687 * gathering the data, this message is shown in an empty row in the table to
11688 * indicate to the end user the the data is being loaded. Note that this
11689 * parameter is not used when loading data by server-side processing, just
11690 * Ajax sourced data with client-side processing.
11692 * @default Loading...
11695 * @name DataTable.defaults.language.loadingRecords
11698 * $(document).ready( function() {
11699 * $('#example').dataTable( {
11701 * "loadingRecords": "Please wait - loading..."
11706 "sLoadingRecords": "Loading...",
11710 * Text which is displayed when the table is processing a user action
11711 * (usually a sort command or similar).
11713 * @default Processing...
11716 * @name DataTable.defaults.language.processing
11719 * $(document).ready( function() {
11720 * $('#example').dataTable( {
11722 * "processing": "DataTables is currently busy"
11727 "sProcessing": "Processing...",
11731 * Details the actions that will be taken when the user types into the
11732 * filtering input text box. The variable "_INPUT_", if used in the string,
11733 * is replaced with the HTML text box for the filtering input allowing
11734 * control over where it appears in the string. If "_INPUT_" is not given
11735 * then the input box is appended to the string automatically.
11740 * @name DataTable.defaults.language.search
11743 * // Input text box will be appended at the end automatically
11744 * $(document).ready( function() {
11745 * $('#example').dataTable( {
11747 * "search": "Filter records:"
11753 * // Specify where the filter should appear
11754 * $(document).ready( function() {
11755 * $('#example').dataTable( {
11757 * "search": "Apply filter _INPUT_ to table"
11762 "sSearch": "Search:",
11766 * Assign a `placeholder` attribute to the search `input` element
11771 * @name DataTable.defaults.language.searchPlaceholder
11773 "sSearchPlaceholder": "",
11777 * All of the language information can be stored in a file on the
11778 * server-side, which DataTables will look up if this parameter is passed.
11779 * It must store the URL of the language file, which is in a JSON format,
11780 * and the object has the same properties as the oLanguage object in the
11781 * initialiser object (i.e. the above parameters). Please refer to one of
11782 * the example language files to see how this works in action.
11784 * @default <i>Empty string - i.e. disabled</i>
11787 * @name DataTable.defaults.language.url
11790 * $(document).ready( function() {
11791 * $('#example').dataTable( {
11793 * "url": "http://www.sprymedia.co.uk/dataTables/lang.txt"
11802 * Text shown inside the table records when the is no information to be
11803 * displayed after filtering. `emptyTable` is shown when there is simply no
11804 * information in the table at all (regardless of filtering).
11806 * @default No matching records found
11809 * @name DataTable.defaults.language.zeroRecords
11812 * $(document).ready( function() {
11813 * $('#example').dataTable( {
11815 * "zeroRecords": "No records to display"
11820 "sZeroRecords": "No matching records found"
11825 * This parameter allows you to have define the global filtering state at
11826 * initialisation time. As an object the `search` parameter must be
11827 * defined, but all other parameters are optional. When `regex` is true,
11828 * the search string will be treated as a regular expression, when false
11829 * (default) it will be treated as a straight string. When `smart`
11830 * DataTables will use it's smart filtering methods (to word match at
11831 * any point in the data), when false this will not be done.
11833 * @extends DataTable.models.oSearch
11836 * @name DataTable.defaults.search
11839 * $(document).ready( function() {
11840 * $('#example').dataTable( {
11841 * "search": {"search": "Initial search"}
11845 "oSearch": $.extend( {}, DataTable.models.oSearch ),
11849 * __Deprecated__ The functionality provided by this parameter has now been
11850 * superseded by that provided through `ajax`, which should be used instead.
11852 * By default DataTables will look for the property `data` (or `aaData` for
11853 * compatibility with DataTables 1.9-) when obtaining data from an Ajax
11854 * source or for server-side processing - this parameter allows that
11855 * property to be changed. You can use Javascript dotted object notation to
11856 * get a data source for multiple levels of nesting.
11861 * @dtopt Server-side
11862 * @name DataTable.defaults.ajaxDataProp
11864 * @deprecated 1.10. Please use `ajax` for this functionality now.
11866 "sAjaxDataProp": "data",
11870 * __Deprecated__ The functionality provided by this parameter has now been
11871 * superseded by that provided through `ajax`, which should be used instead.
11873 * You can instruct DataTables to load data from an external
11874 * source using this parameter (use aData if you want to pass data in you
11875 * already have). Simply provide a url a JSON object can be obtained from.
11880 * @dtopt Server-side
11881 * @name DataTable.defaults.ajaxSource
11883 * @deprecated 1.10. Please use `ajax` for this functionality now.
11885 "sAjaxSource": null,
11889 * This initialisation variable allows you to specify exactly where in the
11890 * DOM you want DataTables to inject the various controls it adds to the page
11891 * (for example you might want the pagination controls at the top of the
11892 * table). DIV elements (with or without a custom class) can also be added to
11893 * aid styling. The follow syntax is used:
11895 * <li>The following options are allowed:
11897 * <li>'l' - Length changing</li>
11898 * <li>'f' - Filtering input</li>
11899 * <li>'t' - The table!</li>
11900 * <li>'i' - Information</li>
11901 * <li>'p' - Pagination</li>
11902 * <li>'r' - pRocessing</li>
11905 * <li>The following constants are allowed:
11907 * <li>'H' - jQueryUI theme "header" classes ('fg-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix')</li>
11908 * <li>'F' - jQueryUI theme "footer" classes ('fg-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix')</li>
11911 * <li>The following syntax is expected:
11913 * <li>'<' and '>' - div elements</li>
11914 * <li>'<"class" and '>' - div with a class</li>
11915 * <li>'<"#id" and '>' - div with an ID</li>
11920 * <li>'<"wrapper"flipt>'</li>
11921 * <li>'<lf<t>ip>'</li>
11926 * @default lfrtip <i>(when `jQueryUI` is false)</i> <b>or</b>
11927 * <"H"lfr>t<"F"ip> <i>(when `jQueryUI` is true)</i>
11930 * @name DataTable.defaults.dom
11933 * $(document).ready( function() {
11934 * $('#example').dataTable( {
11935 * "dom": '<"top"i>rt<"bottom"flp><"clear">'
11943 * Search delay option. This will throttle full table searches that use the
11944 * DataTables provided search input element (it does not effect calls to
11945 * `dt-api search()`, providing a delay before the search is made.
11950 * @name DataTable.defaults.searchDelay
11953 * $(document).ready( function() {
11954 * $('#example').dataTable( {
11955 * "searchDelay": 200
11959 "searchDelay": null,
11963 * DataTables features six different built-in options for the buttons to
11964 * display for pagination control:
11966 * * `numbers` - Page number buttons only
11967 * * `simple` - 'Previous' and 'Next' buttons only
11968 * * 'simple_numbers` - 'Previous' and 'Next' buttons, plus page numbers
11969 * * `full` - 'First', 'Previous', 'Next' and 'Last' buttons
11970 * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus page numbers
11971 * * `first_last_numbers` - 'First' and 'Last' buttons, plus page numbers
11973 * Further methods can be added using {@link DataTable.ext.oPagination}.
11975 * @default simple_numbers
11978 * @name DataTable.defaults.pagingType
11981 * $(document).ready( function() {
11982 * $('#example').dataTable( {
11983 * "pagingType": "full_numbers"
11987 "sPaginationType": "simple_numbers",
11991 * Enable horizontal scrolling. When a table is too wide to fit into a
11992 * certain layout, or you have a large number of columns in the table, you
11993 * can enable x-scrolling to show the table in a viewport, which can be
11994 * scrolled. This property can be `true` which will allow the table to
11995 * scroll horizontally when needed, or any CSS unit, or a number (in which
11996 * case it will be treated as a pixel measurement). Setting as simply `true`
11998 * @type boolean|string
11999 * @default <i>blank string - i.e. disabled</i>
12002 * @name DataTable.defaults.scrollX
12005 * $(document).ready( function() {
12006 * $('#example').dataTable( {
12008 * "scrollCollapse": true
12016 * This property can be used to force a DataTable to use more width than it
12017 * might otherwise do when x-scrolling is enabled. For example if you have a
12018 * table which requires to be well spaced, this parameter is useful for
12019 * "over-sizing" the table, and thus forcing scrolling. This property can by
12020 * any CSS unit, or a number (in which case it will be treated as a pixel
12023 * @default <i>blank string - i.e. disabled</i>
12026 * @name DataTable.defaults.scrollXInner
12029 * $(document).ready( function() {
12030 * $('#example').dataTable( {
12031 * "scrollX": "100%",
12032 * "scrollXInner": "110%"
12036 "sScrollXInner": "",
12040 * Enable vertical scrolling. Vertical scrolling will constrain the DataTable
12041 * to the given height, and enable scrolling for any data which overflows the
12042 * current viewport. This can be used as an alternative to paging to display
12043 * a lot of data in a small area (although paging and scrolling can both be
12044 * enabled at the same time). This property can be any CSS unit, or a number
12045 * (in which case it will be treated as a pixel measurement).
12047 * @default <i>blank string - i.e. disabled</i>
12050 * @name DataTable.defaults.scrollY
12053 * $(document).ready( function() {
12054 * $('#example').dataTable( {
12055 * "scrollY": "200px",
12056 * "paginate": false
12064 * __Deprecated__ The functionality provided by this parameter has now been
12065 * superseded by that provided through `ajax`, which should be used instead.
12067 * Set the HTTP method that is used to make the Ajax call for server-side
12068 * processing or Ajax sourced data.
12073 * @dtopt Server-side
12074 * @name DataTable.defaults.serverMethod
12076 * @deprecated 1.10. Please use `ajax` for this functionality now.
12078 "sServerMethod": "GET",
12082 * DataTables makes use of renderers when displaying HTML elements for
12083 * a table. These renderers can be added or modified by plug-ins to
12084 * generate suitable mark-up for a site. For example the Bootstrap
12085 * integration plug-in for DataTables uses a paging button renderer to
12086 * display pagination buttons in the mark-up required by Bootstrap.
12088 * For further information about the renderers available see
12089 * DataTable.ext.renderer
12090 * @type string|object
12093 * @name DataTable.defaults.renderer
12100 * Set the data property name that DataTables should use to get a row's id
12101 * to set as the `id` property in the node.
12103 * @default DT_RowId
12105 * @name DataTable.defaults.rowId
12107 "rowId": "DT_RowId"
12110 _fnHungarianMap( DataTable.defaults );
12115 * Developer note - See note in model.defaults.js about the use of Hungarian
12116 * notation and camel case.
12120 * Column options that can be given to DataTables at initialisation time.
12123 DataTable.defaults.column = {
12125 * Define which column(s) an order will occur on for this column. This
12126 * allows a column's ordering to take multiple columns into account when
12127 * doing a sort or use the data from a different column. For example first
12128 * name / last name columns make sense to do a multi-column sort over the
12131 * @default null <i>Takes the value of the column index automatically</i>
12133 * @name DataTable.defaults.column.orderData
12137 * // Using `columnDefs`
12138 * $(document).ready( function() {
12139 * $('#example').dataTable( {
12141 * { "orderData": [ 0, 1 ], "targets": [ 0 ] },
12142 * { "orderData": [ 1, 0 ], "targets": [ 1 ] },
12143 * { "orderData": 2, "targets": [ 2 ] }
12149 * // Using `columns`
12150 * $(document).ready( function() {
12151 * $('#example').dataTable( {
12153 * { "orderData": [ 0, 1 ] },
12154 * { "orderData": [ 1, 0 ] },
12155 * { "orderData": 2 },
12167 * You can control the default ordering direction, and even alter the
12168 * behaviour of the sort handler (i.e. only allow ascending ordering etc)
12169 * using this parameter.
12171 * @default [ 'asc', 'desc' ]
12173 * @name DataTable.defaults.column.orderSequence
12177 * // Using `columnDefs`
12178 * $(document).ready( function() {
12179 * $('#example').dataTable( {
12181 * { "orderSequence": [ "asc" ], "targets": [ 1 ] },
12182 * { "orderSequence": [ "desc", "asc", "asc" ], "targets": [ 2 ] },
12183 * { "orderSequence": [ "desc" ], "targets": [ 3 ] }
12189 * // Using `columns`
12190 * $(document).ready( function() {
12191 * $('#example').dataTable( {
12194 * { "orderSequence": [ "asc" ] },
12195 * { "orderSequence": [ "desc", "asc", "asc" ] },
12196 * { "orderSequence": [ "desc" ] },
12202 "asSorting": [ 'asc', 'desc' ],
12206 * Enable or disable filtering on the data in this column.
12210 * @name DataTable.defaults.column.searchable
12214 * // Using `columnDefs`
12215 * $(document).ready( function() {
12216 * $('#example').dataTable( {
12218 * { "searchable": false, "targets": [ 0 ] }
12223 * // Using `columns`
12224 * $(document).ready( function() {
12225 * $('#example').dataTable( {
12227 * { "searchable": false },
12235 "bSearchable": true,
12239 * Enable or disable ordering on this column.
12243 * @name DataTable.defaults.column.orderable
12247 * // Using `columnDefs`
12248 * $(document).ready( function() {
12249 * $('#example').dataTable( {
12251 * { "orderable": false, "targets": [ 0 ] }
12256 * // Using `columns`
12257 * $(document).ready( function() {
12258 * $('#example').dataTable( {
12260 * { "orderable": false },
12272 * Enable or disable the display of this column.
12276 * @name DataTable.defaults.column.visible
12280 * // Using `columnDefs`
12281 * $(document).ready( function() {
12282 * $('#example').dataTable( {
12284 * { "visible": false, "targets": [ 0 ] }
12289 * // Using `columns`
12290 * $(document).ready( function() {
12291 * $('#example').dataTable( {
12293 * { "visible": false },
12305 * Developer definable function that is called whenever a cell is created (Ajax source,
12306 * etc) or processed for input (DOM source). This can be used as a compliment to mRender
12307 * allowing you to modify the DOM element (add background colour for example) when the
12308 * element is available.
12310 * @param {element} td The TD node that has been created
12311 * @param {*} cellData The Data for the cell
12312 * @param {array|object} rowData The data for the whole row
12313 * @param {int} row The row index for the aoData data store
12314 * @param {int} col The column index for aoColumns
12316 * @name DataTable.defaults.column.createdCell
12320 * $(document).ready( function() {
12321 * $('#example').dataTable( {
12322 * "columnDefs": [ {
12324 * "createdCell": function (td, cellData, rowData, row, col) {
12325 * if ( cellData == "1.7" ) {
12326 * $(td).css('color', 'blue')
12333 "fnCreatedCell": null,
12337 * This parameter has been replaced by `data` in DataTables to ensure naming
12338 * consistency. `dataProp` can still be used, as there is backwards
12339 * compatibility in DataTables for this option, but it is strongly
12340 * recommended that you use `data` in preference to `dataProp`.
12341 * @name DataTable.defaults.column.dataProp
12346 * This property can be used to read data from any data source property,
12347 * including deeply nested objects / properties. `data` can be given in a
12348 * number of different ways which effect its behaviour:
12350 * * `integer` - treated as an array index for the data source. This is the
12351 * default that DataTables uses (incrementally increased for each column).
12352 * * `string` - read an object property from the data source. There are
12353 * three 'special' options that can be used in the string to alter how
12354 * DataTables reads the data from the source object:
12355 * * `.` - Dotted Javascript notation. Just as you use a `.` in
12356 * Javascript to read from nested objects, so to can the options
12357 * specified in `data`. For example: `browser.version` or
12358 * `browser.name`. If your object parameter name contains a period, use
12359 * `\\` to escape it - i.e. `first\\.name`.
12360 * * `[]` - Array notation. DataTables can automatically combine data
12361 * from and array source, joining the data with the characters provided
12362 * between the two brackets. For example: `name[, ]` would provide a
12363 * comma-space separated list from the source array. If no characters
12364 * are provided between the brackets, the original array source is
12366 * * `()` - Function notation. Adding `()` to the end of a parameter will
12367 * execute a function of the name given. For example: `browser()` for a
12368 * simple function on the data source, `browser.version()` for a
12369 * function in a nested property or even `browser().version` to get an
12370 * object property if the function called returns an object. Note that
12371 * function notation is recommended for use in `render` rather than
12372 * `data` as it is much simpler to use as a renderer.
12373 * * `null` - use the original data source for the row rather than plucking
12374 * data directly from it. This action has effects on two other
12375 * initialisation options:
12376 * * `defaultContent` - When null is given as the `data` option and
12377 * `defaultContent` is specified for the column, the value defined by
12378 * `defaultContent` will be used for the cell.
12379 * * `render` - When null is used for the `data` option and the `render`
12380 * option is specified for the column, the whole data source for the
12381 * row is used for the renderer.
12382 * * `function` - the function given will be executed whenever DataTables
12383 * needs to set or get the data for a cell in the column. The function
12384 * takes three parameters:
12386 * * `{array|object}` The data source for the row
12387 * * `{string}` The type call data requested - this will be 'set' when
12388 * setting data or 'filter', 'display', 'type', 'sort' or undefined
12389 * when gathering data. Note that when `undefined` is given for the
12390 * type DataTables expects to get the raw data for the object back<
12391 * * `{*}` Data to set when the second parameter is 'set'.
12393 * * The return value from the function is not required when 'set' is
12394 * the type of call, but otherwise the return is what will be used
12395 * for the data requested.
12397 * Note that `data` is a getter and setter option. If you just require
12398 * formatting of data for output, you will likely want to use `render` which
12399 * is simply a getter and thus simpler to use.
12401 * Note that prior to DataTables 1.9.2 `data` was called `mDataProp`. The
12402 * name change reflects the flexibility of this property and is consistent
12403 * with the naming of mRender. If 'mDataProp' is given, then it will still
12404 * be used by DataTables, as it automatically maps the old name to the new
12407 * @type string|int|function|null
12408 * @default null <i>Use automatically calculated column index</i>
12410 * @name DataTable.defaults.column.data
12414 * // Read table data from objects
12415 * // JSON structure for each row:
12417 * // "engine": {value},
12418 * // "browser": {value},
12419 * // "platform": {value},
12420 * // "version": {value},
12421 * // "grade": {value}
12423 * $(document).ready( function() {
12424 * $('#example').dataTable( {
12425 * "ajaxSource": "sources/objects.txt",
12427 * { "data": "engine" },
12428 * { "data": "browser" },
12429 * { "data": "platform" },
12430 * { "data": "version" },
12431 * { "data": "grade" }
12437 * // Read information from deeply nested objects
12438 * // JSON structure for each row:
12440 * // "engine": {value},
12441 * // "browser": {value},
12443 * // "inner": {value}
12446 * // {value}, {value}
12449 * $(document).ready( function() {
12450 * $('#example').dataTable( {
12451 * "ajaxSource": "sources/deep.txt",
12453 * { "data": "engine" },
12454 * { "data": "browser" },
12455 * { "data": "platform.inner" },
12456 * { "data": "details.0" },
12457 * { "data": "details.1" }
12463 * // Using `data` as a function to provide different information for
12464 * // sorting, filtering and display. In this case, currency (price)
12465 * $(document).ready( function() {
12466 * $('#example').dataTable( {
12467 * "columnDefs": [ {
12468 * "targets": [ 0 ],
12469 * "data": function ( source, type, val ) {
12470 * if (type === 'set') {
12471 * source.price = val;
12472 * // Store the computed dislay and filter values for efficiency
12473 * source.price_display = val=="" ? "" : "$"+numberFormat(val);
12474 * source.price_filter = val=="" ? "" : "$"+numberFormat(val)+" "+val;
12477 * else if (type === 'display') {
12478 * return source.price_display;
12480 * else if (type === 'filter') {
12481 * return source.price_filter;
12483 * // 'sort', 'type' and undefined all just use the integer
12484 * return source.price;
12491 * // Using default content
12492 * $(document).ready( function() {
12493 * $('#example').dataTable( {
12494 * "columnDefs": [ {
12495 * "targets": [ 0 ],
12497 * "defaultContent": "Click to edit"
12503 * // Using array notation - outputting a list from an array
12504 * $(document).ready( function() {
12505 * $('#example').dataTable( {
12506 * "columnDefs": [ {
12507 * "targets": [ 0 ],
12508 * "data": "name[, ]"
12518 * This property is the rendering partner to `data` and it is suggested that
12519 * when you want to manipulate data for display (including filtering,
12520 * sorting etc) without altering the underlying data for the table, use this
12521 * property. `render` can be considered to be the the read only companion to
12522 * `data` which is read / write (then as such more complex). Like `data`
12523 * this option can be given in a number of different ways to effect its
12526 * * `integer` - treated as an array index for the data source. This is the
12527 * default that DataTables uses (incrementally increased for each column).
12528 * * `string` - read an object property from the data source. There are
12529 * three 'special' options that can be used in the string to alter how
12530 * DataTables reads the data from the source object:
12531 * * `.` - Dotted Javascript notation. Just as you use a `.` in
12532 * Javascript to read from nested objects, so to can the options
12533 * specified in `data`. For example: `browser.version` or
12534 * `browser.name`. If your object parameter name contains a period, use
12535 * `\\` to escape it - i.e. `first\\.name`.
12536 * * `[]` - Array notation. DataTables can automatically combine data
12537 * from and array source, joining the data with the characters provided
12538 * between the two brackets. For example: `name[, ]` would provide a
12539 * comma-space separated list from the source array. If no characters
12540 * are provided between the brackets, the original array source is
12542 * * `()` - Function notation. Adding `()` to the end of a parameter will
12543 * execute a function of the name given. For example: `browser()` for a
12544 * simple function on the data source, `browser.version()` for a
12545 * function in a nested property or even `browser().version` to get an
12546 * object property if the function called returns an object.
12547 * * `object` - use different data for the different data types requested by
12548 * DataTables ('filter', 'display', 'type' or 'sort'). The property names
12549 * of the object is the data type the property refers to and the value can
12550 * defined using an integer, string or function using the same rules as
12551 * `render` normally does. Note that an `_` option _must_ be specified.
12552 * This is the default value to use if you haven't specified a value for
12553 * the data type requested by DataTables.
12554 * * `function` - the function given will be executed whenever DataTables
12555 * needs to set or get the data for a cell in the column. The function
12556 * takes three parameters:
12558 * * {array|object} The data source for the row (based on `data`)
12559 * * {string} The type call data requested - this will be 'filter',
12560 * 'display', 'type' or 'sort'.
12561 * * {array|object} The full data source for the row (not based on
12564 * * The return value from the function is what will be used for the
12567 * @type string|int|function|object|null
12568 * @default null Use the data source value.
12570 * @name DataTable.defaults.column.render
12574 * // Create a comma separated list from an array of objects
12575 * $(document).ready( function() {
12576 * $('#example').dataTable( {
12577 * "ajaxSource": "sources/deep.txt",
12579 * { "data": "engine" },
12580 * { "data": "browser" },
12582 * "data": "platform",
12583 * "render": "[, ].name"
12590 * // Execute a function to obtain data
12591 * $(document).ready( function() {
12592 * $('#example').dataTable( {
12593 * "columnDefs": [ {
12594 * "targets": [ 0 ],
12595 * "data": null, // Use the full data source object for the renderer's source
12596 * "render": "browserName()"
12602 * // As an object, extracting different data for the different types
12603 * // This would be used with a data source such as:
12604 * // { "phone": 5552368, "phone_filter": "5552368 555-2368", "phone_display": "555-2368" }
12605 * // Here the `phone` integer is used for sorting and type detection, while `phone_filter`
12606 * // (which has both forms) is used for filtering for if a user inputs either format, while
12607 * // the formatted phone number is the one that is shown in the table.
12608 * $(document).ready( function() {
12609 * $('#example').dataTable( {
12610 * "columnDefs": [ {
12611 * "targets": [ 0 ],
12612 * "data": null, // Use the full data source object for the renderer's source
12615 * "filter": "phone_filter",
12616 * "display": "phone_display"
12623 * // Use as a function to create a link from the data source
12624 * $(document).ready( function() {
12625 * $('#example').dataTable( {
12626 * "columnDefs": [ {
12627 * "targets": [ 0 ],
12628 * "data": "download_link",
12629 * "render": function ( data, type, full ) {
12630 * return '<a href="'+data+'">Download</a>';
12640 * Change the cell type created for the column - either TD cells or TH cells. This
12641 * can be useful as TH cells have semantic meaning in the table body, allowing them
12642 * to act as a header for a row (you may wish to add scope='row' to the TH elements).
12646 * @name DataTable.defaults.column.cellType
12650 * // Make the first column use TH cells
12651 * $(document).ready( function() {
12652 * $('#example').dataTable( {
12653 * "columnDefs": [ {
12654 * "targets": [ 0 ],
12664 * Class to give to each cell in this column.
12666 * @default <i>Empty string</i>
12668 * @name DataTable.defaults.column.class
12672 * // Using `columnDefs`
12673 * $(document).ready( function() {
12674 * $('#example').dataTable( {
12676 * { "class": "my_class", "targets": [ 0 ] }
12682 * // Using `columns`
12683 * $(document).ready( function() {
12684 * $('#example').dataTable( {
12686 * { "class": "my_class" },
12698 * When DataTables calculates the column widths to assign to each column,
12699 * it finds the longest string in each column and then constructs a
12700 * temporary table and reads the widths from that. The problem with this
12701 * is that "mmm" is much wider then "iiii", but the latter is a longer
12702 * string - thus the calculation can go wrong (doing it properly and putting
12703 * it into an DOM object and measuring that is horribly(!) slow). Thus as
12704 * a "work around" we provide this option. It will append its value to the
12705 * text that is found to be the longest string for the column - i.e. padding.
12706 * Generally you shouldn't need this!
12708 * @default <i>Empty string<i>
12710 * @name DataTable.defaults.column.contentPadding
12714 * // Using `columns`
12715 * $(document).ready( function() {
12716 * $('#example').dataTable( {
12722 * "contentPadding": "mmm"
12728 "sContentPadding": "",
12732 * Allows a default value to be given for a column's data, and will be used
12733 * whenever a null data source is encountered (this can be because `data`
12734 * is set to null, or because the data source itself is null).
12738 * @name DataTable.defaults.column.defaultContent
12742 * // Using `columnDefs`
12743 * $(document).ready( function() {
12744 * $('#example').dataTable( {
12748 * "defaultContent": "Edit",
12749 * "targets": [ -1 ]
12756 * // Using `columns`
12757 * $(document).ready( function() {
12758 * $('#example').dataTable( {
12765 * "defaultContent": "Edit"
12771 "sDefaultContent": null,
12775 * This parameter is only used in DataTables' server-side processing. It can
12776 * be exceptionally useful to know what columns are being displayed on the
12777 * client side, and to map these to database fields. When defined, the names
12778 * also allow DataTables to reorder information from the server if it comes
12779 * back in an unexpected order (i.e. if you switch your columns around on the
12780 * client-side, your server-side code does not also need updating).
12782 * @default <i>Empty string</i>
12784 * @name DataTable.defaults.column.name
12788 * // Using `columnDefs`
12789 * $(document).ready( function() {
12790 * $('#example').dataTable( {
12792 * { "name": "engine", "targets": [ 0 ] },
12793 * { "name": "browser", "targets": [ 1 ] },
12794 * { "name": "platform", "targets": [ 2 ] },
12795 * { "name": "version", "targets": [ 3 ] },
12796 * { "name": "grade", "targets": [ 4 ] }
12802 * // Using `columns`
12803 * $(document).ready( function() {
12804 * $('#example').dataTable( {
12806 * { "name": "engine" },
12807 * { "name": "browser" },
12808 * { "name": "platform" },
12809 * { "name": "version" },
12810 * { "name": "grade" }
12819 * Defines a data source type for the ordering which can be used to read
12820 * real-time information from the table (updating the internally cached
12821 * version) prior to ordering. This allows ordering to occur on user
12822 * editable elements such as form inputs.
12826 * @name DataTable.defaults.column.orderDataType
12830 * // Using `columnDefs`
12831 * $(document).ready( function() {
12832 * $('#example').dataTable( {
12834 * { "orderDataType": "dom-text", "targets": [ 2, 3 ] },
12835 * { "type": "numeric", "targets": [ 3 ] },
12836 * { "orderDataType": "dom-select", "targets": [ 4 ] },
12837 * { "orderDataType": "dom-checkbox", "targets": [ 5 ] }
12843 * // Using `columns`
12844 * $(document).ready( function() {
12845 * $('#example').dataTable( {
12849 * { "orderDataType": "dom-text" },
12850 * { "orderDataType": "dom-text", "type": "numeric" },
12851 * { "orderDataType": "dom-select" },
12852 * { "orderDataType": "dom-checkbox" }
12857 "sSortDataType": "std",
12861 * The title of this column.
12863 * @default null <i>Derived from the 'TH' value for this column in the
12864 * original HTML table.</i>
12866 * @name DataTable.defaults.column.title
12870 * // Using `columnDefs`
12871 * $(document).ready( function() {
12872 * $('#example').dataTable( {
12874 * { "title": "My column title", "targets": [ 0 ] }
12880 * // Using `columns`
12881 * $(document).ready( function() {
12882 * $('#example').dataTable( {
12884 * { "title": "My column title" },
12897 * The type allows you to specify how the data for this column will be
12898 * ordered. Four types (string, numeric, date and html (which will strip
12899 * HTML tags before ordering)) are currently available. Note that only date
12900 * formats understood by Javascript's Date() object will be accepted as type
12901 * date. For example: "Mar 26, 2008 5:03 PM". May take the values: 'string',
12902 * 'numeric', 'date' or 'html' (by default). Further types can be adding
12903 * through plug-ins.
12905 * @default null <i>Auto-detected from raw data</i>
12907 * @name DataTable.defaults.column.type
12911 * // Using `columnDefs`
12912 * $(document).ready( function() {
12913 * $('#example').dataTable( {
12915 * { "type": "html", "targets": [ 0 ] }
12921 * // Using `columns`
12922 * $(document).ready( function() {
12923 * $('#example').dataTable( {
12925 * { "type": "html" },
12938 * Defining the width of the column, this parameter may take any CSS value
12939 * (3em, 20px etc). DataTables applies 'smart' widths to columns which have not
12940 * been given a specific width through this interface ensuring that the table
12941 * remains readable.
12943 * @default null <i>Automatic</i>
12945 * @name DataTable.defaults.column.width
12949 * // Using `columnDefs`
12950 * $(document).ready( function() {
12951 * $('#example').dataTable( {
12953 * { "width": "20%", "targets": [ 0 ] }
12959 * // Using `columns`
12960 * $(document).ready( function() {
12961 * $('#example').dataTable( {
12963 * { "width": "20%" },
12975 _fnHungarianMap( DataTable.defaults.column );
12980 * DataTables settings object - this holds all the information needed for a
12981 * given table, including configuration, data and current application of the
12982 * table options. DataTables does not have a single instance for each DataTable
12983 * with the settings attached to that instance, but rather instances of the
12984 * DataTable "class" are created on-the-fly as needed (typically by a
12985 * $().dataTable() call) and the settings object is then applied to that
12988 * Note that this object is related to {@link DataTable.defaults} but this
12989 * one is the internal data store for DataTables's cache of columns. It should
12990 * NOT be manipulated outside of DataTables. Any configuration should be done
12991 * through the initialisation options.
12993 * @todo Really should attach the settings object to individual instances so we
12994 * don't need to create new instances on each $().dataTable() call (if the
12995 * table already exists). It would also save passing oSettings around and
12996 * into every single function. However, this is a very significant
12997 * architecture change for DataTables and will almost certainly break
12998 * backwards compatibility with older installations. This is something that
12999 * will be done in 2.0.
13001 DataTable.models.oSettings = {
13003 * Primary features of DataTables and their enablement state.
13009 * Flag to say if DataTables should automatically try to calculate the
13010 * optimum table and columns widths (true) or not (false).
13011 * Note that this parameter will be set by the initialisation routine. To
13012 * set a default use {@link DataTable.defaults}.
13015 "bAutoWidth": null,
13018 * Delay the creation of TR and TD elements until they are actually
13019 * needed by a driven page draw. This can give a significant speed
13020 * increase for Ajax source and Javascript source data, but makes no
13021 * difference at all fro DOM and server-side processing tables.
13022 * Note that this parameter will be set by the initialisation routine. To
13023 * set a default use {@link DataTable.defaults}.
13026 "bDeferRender": null,
13029 * Enable filtering on the table or not. Note that if this is disabled
13030 * then there is no filtering at all on the table, including fnFilter.
13031 * To just remove the filtering input use sDom and remove the 'f' option.
13032 * Note that this parameter will be set by the initialisation routine. To
13033 * set a default use {@link DataTable.defaults}.
13039 * Table information element (the 'Showing x of y records' div) enable
13041 * Note that this parameter will be set by the initialisation routine. To
13042 * set a default use {@link DataTable.defaults}.
13048 * Present a user control allowing the end user to change the page size
13049 * when pagination is enabled.
13050 * Note that this parameter will be set by the initialisation routine. To
13051 * set a default use {@link DataTable.defaults}.
13054 "bLengthChange": null,
13057 * Pagination enabled or not. Note that if this is disabled then length
13058 * changing must also be disabled.
13059 * Note that this parameter will be set by the initialisation routine. To
13060 * set a default use {@link DataTable.defaults}.
13066 * Processing indicator enable flag whenever DataTables is enacting a
13067 * user request - typically an Ajax request for server-side processing.
13068 * Note that this parameter will be set by the initialisation routine. To
13069 * set a default use {@link DataTable.defaults}.
13072 "bProcessing": null,
13075 * Server-side processing enabled flag - when enabled DataTables will
13076 * get all data from the server for every draw - there is no filtering,
13077 * sorting or paging done on the client-side.
13078 * Note that this parameter will be set by the initialisation routine. To
13079 * set a default use {@link DataTable.defaults}.
13082 "bServerSide": null,
13085 * Sorting enablement flag.
13086 * Note that this parameter will be set by the initialisation routine. To
13087 * set a default use {@link DataTable.defaults}.
13093 * Multi-column sorting
13094 * Note that this parameter will be set by the initialisation routine. To
13095 * set a default use {@link DataTable.defaults}.
13098 "bSortMulti": null,
13101 * Apply a class to the columns which are being sorted to provide a
13102 * visual highlight or not. This can slow things down when enabled since
13103 * there is a lot of DOM interaction.
13104 * Note that this parameter will be set by the initialisation routine. To
13105 * set a default use {@link DataTable.defaults}.
13108 "bSortClasses": null,
13111 * State saving enablement flag.
13112 * Note that this parameter will be set by the initialisation routine. To
13113 * set a default use {@link DataTable.defaults}.
13121 * Scrolling settings for a table.
13126 * When the table is shorter in height than sScrollY, collapse the
13127 * table container down to the height of the table (when true).
13128 * Note that this parameter will be set by the initialisation routine. To
13129 * set a default use {@link DataTable.defaults}.
13135 * Width of the scrollbar for the web-browser's platform. Calculated
13136 * during table initialisation.
13143 * Viewport width for horizontal scrolling. Horizontal scrolling is
13144 * disabled if an empty string.
13145 * Note that this parameter will be set by the initialisation routine. To
13146 * set a default use {@link DataTable.defaults}.
13152 * Width to expand the table to when using x-scrolling. Typically you
13153 * should not need to use this.
13154 * Note that this parameter will be set by the initialisation routine. To
13155 * set a default use {@link DataTable.defaults}.
13162 * Viewport height for vertical scrolling. Vertical scrolling is disabled
13163 * if an empty string.
13164 * Note that this parameter will be set by the initialisation routine. To
13165 * set a default use {@link DataTable.defaults}.
13172 * Language information for the table.
13174 * @extends DataTable.defaults.oLanguage
13178 * Information callback function. See
13179 * {@link DataTable.defaults.fnInfoCallback}
13183 "fnInfoCallback": null
13187 * Browser support parameters
13192 * Indicate if the browser incorrectly calculates width:100% inside a
13193 * scrolling element (IE6/7)
13197 "bScrollOversize": false,
13200 * Determine if the vertical scrollbar is on the right or left of the
13201 * scrolling container - needed for rtl language layout, although not
13202 * all browsers move the scrollbar (Safari).
13206 "bScrollbarLeft": false,
13209 * Flag for if `getBoundingClientRect` is fully supported or not
13213 "bBounding": false,
13216 * Browser scrollbar width
13228 * Array referencing the nodes which are used for the features. The
13229 * parameters of this object match what is allowed by sDom - i.e.
13231 * <li>'l' - Length changing</li>
13232 * <li>'f' - Filtering input</li>
13233 * <li>'t' - The table!</li>
13234 * <li>'i' - Information</li>
13235 * <li>'p' - Pagination</li>
13236 * <li>'r' - pRocessing</li>
13244 * Store data information - see {@link DataTable.models.oRow} for detailed
13252 * Array of indexes which are in the current display (after filtering etc)
13259 * Array of indexes for display - no filtering
13263 "aiDisplayMaster": [],
13266 * Map of row ids to data indexes
13273 * Store information about each column that is in use
13280 * Store information about the table's header
13287 * Store information about the table's footer
13294 * Store the applied global search information in case we want to force a
13295 * research or compare the old search to a new one.
13296 * Note that this parameter will be set by the initialisation routine. To
13297 * set a default use {@link DataTable.defaults}.
13299 * @extends DataTable.models.oSearch
13301 "oPreviousSearch": {},
13304 * Store the applied search for each column - see
13305 * {@link DataTable.models.oSearch} for the format that is used for the
13306 * filtering information for each column.
13310 "aoPreSearchCols": [],
13313 * Sorting that is applied to the table. Note that the inner arrays are
13314 * used in the following manner:
13316 * <li>Index 0 - column number</li>
13317 * <li>Index 1 - current sorting direction</li>
13319 * Note that this parameter will be set by the initialisation routine. To
13320 * set a default use {@link DataTable.defaults}.
13322 * @todo These inner arrays should really be objects
13327 * Sorting that is always applied to the table (i.e. prefixed in front of
13329 * Note that this parameter will be set by the initialisation routine. To
13330 * set a default use {@link DataTable.defaults}.
13334 "aaSortingFixed": [],
13337 * Classes to use for the striping of a table.
13338 * Note that this parameter will be set by the initialisation routine. To
13339 * set a default use {@link DataTable.defaults}.
13343 "asStripeClasses": null,
13346 * If restoring a table - we should restore its striping classes as well
13350 "asDestroyStripes": [],
13353 * If restoring a table - we should restore its width
13357 "sDestroyWidth": 0,
13360 * Callback functions array for every time a row is inserted (i.e. on a draw).
13364 "aoRowCallback": [],
13367 * Callback functions for the header on each draw.
13371 "aoHeaderCallback": [],
13374 * Callback function for the footer on each draw.
13378 "aoFooterCallback": [],
13381 * Array of callback functions for draw callback functions
13385 "aoDrawCallback": [],
13388 * Array of callback functions for row created function
13392 "aoRowCreatedCallback": [],
13395 * Callback functions for just before the table is redrawn. A return of
13396 * false will be used to cancel the draw.
13400 "aoPreDrawCallback": [],
13403 * Callback functions for when the table has been initialised.
13407 "aoInitComplete": [],
13411 * Callbacks for modifying the settings to be stored for state saving, prior to
13416 "aoStateSaveParams": [],
13419 * Callbacks for modifying the settings that have been stored for state saving
13420 * prior to using the stored values to restore the state.
13424 "aoStateLoadParams": [],
13427 * Callbacks for operating on the settings object once the saved state has been
13432 "aoStateLoaded": [],
13435 * Cache the table ID for quick access
13437 * @default <i>Empty string</i>
13442 * The TABLE node for the main table
13449 * Permanent ref to the thead element
13456 * Permanent ref to the tfoot element - if it exists
13463 * Permanent ref to the tbody element
13470 * Cache the wrapper node (contains all DataTables controlled elements)
13474 "nTableWrapper": null,
13477 * Indicate if when using server-side processing the loading of data
13478 * should be deferred until the second draw.
13479 * Note that this parameter will be set by the initialisation routine. To
13480 * set a default use {@link DataTable.defaults}.
13484 "bDeferLoading": false,
13487 * Indicate if all required information has been read in
13491 "bInitialised": false,
13494 * Information about open rows. Each object in the array has the parameters
13495 * 'nTr' and 'nParent'
13502 * Dictate the positioning of DataTables' control elements - see
13503 * {@link DataTable.model.oInit.sDom}.
13504 * Note that this parameter will be set by the initialisation routine. To
13505 * set a default use {@link DataTable.defaults}.
13512 * Search delay (in mS)
13516 "searchDelay": null,
13519 * Which type of pagination should be used.
13520 * Note that this parameter will be set by the initialisation routine. To
13521 * set a default use {@link DataTable.defaults}.
13523 * @default two_button
13525 "sPaginationType": "two_button",
13528 * The state duration (for `stateSave`) in seconds.
13529 * Note that this parameter will be set by the initialisation routine. To
13530 * set a default use {@link DataTable.defaults}.
13534 "iStateDuration": 0,
13537 * Array of callback functions for state saving. Each array element is an
13538 * object with the following parameters:
13540 * <li>function:fn - function to call. Takes two parameters, oSettings
13541 * and the JSON string to save that has been thus far created. Returns
13542 * a JSON string to be inserted into a json object
13543 * (i.e. '"param": [ 0, 1, 2]')</li>
13544 * <li>string:sName - name of callback</li>
13552 * Array of callback functions for state loading. Each array element is an
13553 * object with the following parameters:
13555 * <li>function:fn - function to call. Takes two parameters, oSettings
13556 * and the object stored. May return false to cancel state loading</li>
13557 * <li>string:sName - name of callback</li>
13565 * State that was saved. Useful for back reference
13569 "oSavedState": null,
13572 * State that was loaded. Useful for back reference
13576 "oLoadedState": null,
13579 * Source url for AJAX data for the table.
13580 * Note that this parameter will be set by the initialisation routine. To
13581 * set a default use {@link DataTable.defaults}.
13585 "sAjaxSource": null,
13588 * Property from a given object from which to read the table data from. This
13589 * can be an empty string (when not server-side processing), in which case
13590 * it is assumed an an array is given directly.
13591 * Note that this parameter will be set by the initialisation routine. To
13592 * set a default use {@link DataTable.defaults}.
13595 "sAjaxDataProp": null,
13598 * Note if draw should be blocked while getting data
13602 "bAjaxDataGet": true,
13605 * The last jQuery XHR object that was used for server-side data gathering.
13606 * This can be used for working with the XHR information in one of the
13614 * JSON returned from the server in the last Ajax request
13616 * @default undefined
13621 * Data submitted as part of the last Ajax request
13623 * @default undefined
13625 "oAjaxData": undefined,
13628 * Function to get the server-side data.
13629 * Note that this parameter will be set by the initialisation routine. To
13630 * set a default use {@link DataTable.defaults}.
13633 "fnServerData": null,
13636 * Functions which are called prior to sending an Ajax request so extra
13637 * parameters can easily be sent to the server
13641 "aoServerParams": [],
13644 * Send the XHR HTTP method - GET or POST (could be PUT or DELETE if
13646 * Note that this parameter will be set by the initialisation routine. To
13647 * set a default use {@link DataTable.defaults}.
13650 "sServerMethod": null,
13653 * Format numbers for display.
13654 * Note that this parameter will be set by the initialisation routine. To
13655 * set a default use {@link DataTable.defaults}.
13658 "fnFormatNumber": null,
13661 * List of options that can be used for the user selectable length menu.
13662 * Note that this parameter will be set by the initialisation routine. To
13663 * set a default use {@link DataTable.defaults}.
13667 "aLengthMenu": null,
13670 * Counter for the draws that the table does. Also used as a tracker for
13671 * server-side processing
13678 * Indicate if a redraw is being done - useful for Ajax
13685 * Draw index (iDraw) of the last error when parsing the returned data
13692 * Paging display length
13696 "_iDisplayLength": 10,
13699 * Paging start point - aiDisplay index
13703 "_iDisplayStart": 0,
13706 * Server-side processing - number of records in the result set
13707 * (i.e. before filtering), Use fnRecordsTotal rather than
13708 * this property to get the value of the number of records, regardless of
13709 * the server-side processing setting.
13714 "_iRecordsTotal": 0,
13717 * Server-side processing - number of records in the current display set
13718 * (i.e. after filtering). Use fnRecordsDisplay rather than
13719 * this property to get the value of the number of records, regardless of
13720 * the server-side processing setting.
13725 "_iRecordsDisplay": 0,
13728 * The classes to use for the table
13735 * Flag attached to the settings object so you can check in the draw
13736 * callback if filtering has been done in the draw. Deprecated in favour of
13742 "bFiltered": false,
13745 * Flag attached to the settings object so you can check in the draw
13746 * callback if sorting has been done in the draw. Deprecated in favour of
13755 * Indicate that if multiple rows are in the header and there is more than
13756 * one unique cell per column, if the top one (true) or bottom one (false)
13757 * should be used for sorting / title by DataTables.
13758 * Note that this parameter will be set by the initialisation routine. To
13759 * set a default use {@link DataTable.defaults}.
13762 "bSortCellsTop": null,
13765 * Initialisation object that is used for the table
13772 * Destroy callback functions - for plug-ins to attach themselves to the
13773 * destroy so they can clean up markup and events.
13777 "aoDestroyCallback": [],
13781 * Get the number of records in the current record set, before filtering
13784 "fnRecordsTotal": function ()
13786 return _fnDataSource( this ) == 'ssp' ?
13787 this._iRecordsTotal * 1 :
13788 this.aiDisplayMaster.length;
13792 * Get the number of records in the current record set, after filtering
13795 "fnRecordsDisplay": function ()
13797 return _fnDataSource( this ) == 'ssp' ?
13798 this._iRecordsDisplay * 1 :
13799 this.aiDisplay.length;
13803 * Get the display end point - aiDisplay index
13806 "fnDisplayEnd": function ()
13809 len = this._iDisplayLength,
13810 start = this._iDisplayStart,
13811 calc = start + len,
13812 records = this.aiDisplay.length,
13813 features = this.oFeatures,
13814 paginate = features.bPaginate;
13816 if ( features.bServerSide ) {
13817 return paginate === false || len === -1 ?
13819 Math.min( start+len, this._iRecordsDisplay );
13822 return ! paginate || calc>records || len===-1 ?
13829 * The DataTables object for this table
13836 * Unique identifier for each instance of the DataTables object. If there
13837 * is an ID on the table node, then it takes that value, otherwise an
13838 * incrementing internal counter is used.
13845 * tabindex attribute value that is added to DataTables control elements, allowing
13846 * keyboard navigation of the table and its controls.
13851 * DIV container for the footer scrolling table if scrolling
13853 "nScrollHead": null,
13856 * DIV container for the footer scrolling table if scrolling
13858 "nScrollFoot": null,
13861 * Last applied sort
13868 * Stored plug-in instances
13875 * Function used to get a row's id from the row's data
13882 * Data location where to store a row's id
13890 * Extension object for DataTables that is used to provide all extension
13893 * Note that the `DataTable.ext` object is available through
13894 * `jQuery.fn.dataTable.ext` where it may be accessed and manipulated. It is
13895 * also aliased to `jQuery.fn.dataTableExt` for historic reasons.
13897 * @extends DataTable.models.ext
13902 * DataTables extensions
13904 * This namespace acts as a collection area for plug-ins that can be used to
13905 * extend DataTables capabilities. Indeed many of the build in methods
13906 * use this method to provide their own capabilities (sorting methods for
13909 * Note that this namespace is aliased to `jQuery.fn.dataTableExt` for legacy
13914 DataTable.ext = _ext = {
13916 * Buttons. For use with the Buttons extension for DataTables. This is
13917 * defined here so other extensions can define buttons regardless of load
13918 * order. It is _not_ used by DataTables core.
13927 * Element class names
13936 * DataTables build type (expanded by the download builder)
13940 build:"dt/dt-1.10.21/fh-3.1.7/sp-1.1.1/sl-1.3.1",
13946 * How should DataTables report an error. Can take the value 'alert',
13947 * 'throw', 'none' or a function.
13949 * @type string|function
13956 * Feature plug-ins.
13958 * This is an array of objects which describe the feature plug-ins that are
13959 * available to DataTables. These feature plug-ins are then available for
13960 * use through the `dom` initialisation option.
13962 * Each feature plug-in is described by an object which must have the
13963 * following properties:
13965 * * `fnInit` - function that is used to initialise the plug-in,
13966 * * `cFeature` - a character so the feature can be enabled by the `dom`
13967 * instillation option. This is case sensitive.
13969 * The `fnInit` function has the following input parameters:
13971 * 1. `{object}` DataTables settings object: see
13972 * {@link DataTable.models.oSettings}
13974 * And the following return is expected:
13976 * * {node|null} The element which contains your feature. Note that the
13977 * return may also be void if your plug-in does not require to inject any
13978 * DOM elements into DataTables control (`dom`) - for example this might
13979 * be useful when developing a plug-in which allows table control via
13985 * $.fn.dataTable.ext.features.push( {
13986 * "fnInit": function( oSettings ) {
13987 * return new TableTools( { "oDTSettings": oSettings } );
13998 * This method of searching is complimentary to the default type based
13999 * searching, and a lot more comprehensive as it allows you complete control
14000 * over the searching logic. Each element in this array is a function
14001 * (parameters described below) that is called for every row in the table,
14002 * and your logic decides if it should be included in the searching data set
14005 * Searching functions have the following input parameters:
14007 * 1. `{object}` DataTables settings object: see
14008 * {@link DataTable.models.oSettings}
14009 * 2. `{array|object}` Data for the row to be processed (same as the
14010 * original format that was passed in as the data source, or an array
14011 * from a DOM data source
14012 * 3. `{int}` Row index ({@link DataTable.models.oSettings.aoData}), which
14013 * can be useful to retrieve the `TR` element if you need DOM interaction.
14015 * And the following return is expected:
14017 * * {boolean} Include the row in the searched result set (true) or not
14020 * Note that as with the main search ability in DataTables, technically this
14021 * is "filtering", since it is subtractive. However, for consistency in
14022 * naming we call it searching here.
14028 * // The following example shows custom search being applied to the
14029 * // fourth column (i.e. the data[3] index) based on two input values
14030 * // from the end-user, matching the data in a certain range.
14031 * $.fn.dataTable.ext.search.push(
14032 * function( settings, data, dataIndex ) {
14033 * var min = document.getElementById('min').value * 1;
14034 * var max = document.getElementById('max').value * 1;
14035 * var version = data[3] == "-" ? 0 : data[3]*1;
14037 * if ( min == "" && max == "" ) {
14040 * else if ( min == "" && version < max ) {
14043 * else if ( min < version && "" == max ) {
14046 * else if ( min < version && version < max ) {
14057 * Selector extensions
14059 * The `selector` option can be used to extend the options available for the
14060 * selector modifier options (`selector-modifier` object data type) that
14061 * each of the three built in selector types offer (row, column and cell +
14062 * their plural counterparts). For example the Select extension uses this
14063 * mechanism to provide an option to select only rows, columns and cells
14064 * that have been marked as selected by the end user (`{selected: true}`),
14065 * which can be used in conjunction with the existing built in selector
14068 * Each property is an array to which functions can be pushed. The functions
14069 * take three attributes:
14071 * * Settings object for the host table
14072 * * Options object (`selector-modifier` object type)
14073 * * Array of selected item indexes
14075 * The return is an array of the resulting item indexes after the custom
14076 * selector has been applied.
14088 * Internal functions, exposed for used in plug-ins.
14090 * Please note that you should not need to use the internal methods for
14091 * anything other than a plug-in (and even then, try to avoid if possible).
14092 * The internal function may change between releases.
14101 * Legacy configuration options. Enable and disable legacy options that
14102 * are available in DataTables.
14108 * Enable / disable DataTables 1.9 compatible server-side processing
14119 * Pagination plug-in methods.
14121 * Each entry in this object is a function and defines which buttons should
14122 * be shown by the pagination rendering method that is used for the table:
14123 * {@link DataTable.ext.renderer.pageButton}. The renderer addresses how the
14124 * buttons are displayed in the document, while the functions here tell it
14125 * what buttons to display. This is done by returning an array of button
14126 * descriptions (what each button will do).
14128 * Pagination types (the four built in options and any additional plug-in
14129 * options defined here) can be used through the `paginationType`
14130 * initialisation parameter.
14132 * The functions defined take two parameters:
14134 * 1. `{int} page` The current page index
14135 * 2. `{int} pages` The number of pages in the table
14137 * Each function is expected to return an array where each element of the
14138 * array can be one of:
14140 * * `first` - Jump to first page when activated
14141 * * `last` - Jump to last page when activated
14142 * * `previous` - Show previous page when activated
14143 * * `next` - Show next page when activated
14144 * * `{int}` - Show page of the index given
14145 * * `{array}` - A nested array containing the above elements to add a
14146 * containing 'DIV' element (might be useful for styling).
14148 * Note that DataTables v1.9- used this object slightly differently whereby
14149 * an object with two functions would be defined for each plug-in. That
14150 * ability is still supported by DataTables 1.10+ to provide backwards
14151 * compatibility, but this option of use is now decremented and no longer
14152 * documented in DataTables 1.10+.
14158 * // Show previous, next and current page buttons only
14159 * $.fn.dataTableExt.oPagination.current = function ( page, pages ) {
14160 * return [ 'previous', page, 'next' ];
14173 * Ordering plug-ins - custom data source
14175 * The extension options for ordering of data available here is complimentary
14176 * to the default type based ordering that DataTables typically uses. It
14177 * allows much greater control over the the data that is being used to
14178 * order a column, but is necessarily therefore more complex.
14180 * This type of ordering is useful if you want to do ordering based on data
14181 * live from the DOM (for example the contents of an 'input' element) rather
14182 * than just the static string that DataTables knows of.
14184 * The way these plug-ins work is that you create an array of the values you
14185 * wish to be ordering for the column in question and then return that
14186 * array. The data in the array much be in the index order of the rows in
14187 * the table (not the currently ordering order!). Which order data gathering
14188 * function is run here depends on the `dt-init columns.orderDataType`
14189 * parameter that is used for the column (if any).
14191 * The functions defined take two parameters:
14193 * 1. `{object}` DataTables settings object: see
14194 * {@link DataTable.models.oSettings}
14195 * 2. `{int}` Target column index
14197 * Each function is expected to return an array:
14199 * * `{array}` Data for the column to be ordering upon
14204 * // Ordering using `input` node values
14205 * $.fn.dataTable.ext.order['dom-text'] = function ( settings, col )
14207 * return this.api().column( col, {order:'index'} ).nodes().map( function ( td, i ) {
14208 * return $('input', td).val();
14216 * Type based plug-ins.
14218 * Each column in DataTables has a type assigned to it, either by automatic
14219 * detection or by direct assignment using the `type` option for the column.
14220 * The type of a column will effect how it is ordering and search (plug-ins
14221 * can also make use of the column type if required).
14227 * Type detection functions.
14229 * The functions defined in this object are used to automatically detect
14230 * a column's type, making initialisation of DataTables super easy, even
14231 * when complex data is in the table.
14233 * The functions defined take two parameters:
14235 * 1. `{*}` Data from the column cell to be analysed
14236 * 2. `{settings}` DataTables settings object. This can be used to
14237 * perform context specific type detection - for example detection
14238 * based on language settings such as using a comma for a decimal
14239 * place. Generally speaking the options from the settings will not
14242 * Each function is expected to return:
14244 * * `{string|null}` Data type detected, or null if unknown (and thus
14245 * pass it on to the other type detection functions.
14250 * // Currency type detection plug-in:
14251 * $.fn.dataTable.ext.type.detect.push(
14252 * function ( data, settings ) {
14253 * // Check the numeric part
14254 * if ( ! data.substring(1).match(/[0-9]/) ) {
14258 * // Check prefixed by currency
14259 * if ( data.charAt(0) == '$' || data.charAt(0) == '£' ) {
14260 * return 'currency';
14270 * Type based search formatting.
14272 * The type based searching functions can be used to pre-format the
14273 * data to be search on. For example, it can be used to strip HTML
14274 * tags or to de-format telephone numbers for numeric only searching.
14276 * Note that is a search is not defined for a column of a given type,
14277 * no search formatting will be performed.
14279 * Pre-processing of searching data plug-ins - When you assign the sType
14280 * for a column (or have it automatically detected for you by DataTables
14281 * or a type detection plug-in), you will typically be using this for
14282 * custom sorting, but it can also be used to provide custom searching
14283 * by allowing you to pre-processing the data and returning the data in
14284 * the format that should be searched upon. This is done by adding
14285 * functions this object with a parameter name which matches the sType
14286 * for that target column. This is the corollary of <i>afnSortData</i>
14287 * for searching data.
14289 * The functions defined take a single parameter:
14291 * 1. `{*}` Data from the column cell to be prepared for searching
14293 * Each function is expected to return:
14295 * * `{string|null}` Formatted string that will be used for the searching.
14301 * $.fn.dataTable.ext.type.search['title-numeric'] = function ( d ) {
14302 * return d.replace(/\n/g," ").replace( /<.*?>/g, "" );
14309 * Type based ordering.
14311 * The column type tells DataTables what ordering to apply to the table
14312 * when a column is sorted upon. The order for each type that is defined,
14313 * is defined by the functions available in this object.
14315 * Each ordering option can be described by three properties added to
14318 * * `{type}-pre` - Pre-formatting function
14319 * * `{type}-asc` - Ascending order function
14320 * * `{type}-desc` - Descending order function
14322 * All three can be used together, only `{type}-pre` or only
14323 * `{type}-asc` and `{type}-desc` together. It is generally recommended
14324 * that only `{type}-pre` is used, as this provides the optimal
14325 * implementation in terms of speed, although the others are provided
14326 * for compatibility with existing Javascript sort functions.
14328 * `{type}-pre`: Functions defined take a single parameter:
14330 * 1. `{*}` Data from the column cell to be prepared for ordering
14334 * * `{*}` Data to be sorted upon
14336 * `{type}-asc` and `{type}-desc`: Functions are typical Javascript sort
14337 * functions, taking two parameters:
14339 * 1. `{*}` Data to compare to the second parameter
14340 * 2. `{*}` Data to compare to the first parameter
14344 * * `{*}` Ordering match: <0 if first parameter should be sorted lower
14345 * than the second parameter, ===0 if the two parameters are equal and
14346 * >0 if the first parameter should be sorted height than the second
14353 * // Numeric ordering of formatted numbers with a pre-formatter
14354 * $.extend( $.fn.dataTable.ext.type.order, {
14355 * "string-pre": function(x) {
14356 * a = (a === "-" || a === "") ? 0 : a.replace( /[^\d\-\.]/g, "" );
14357 * return parseFloat( a );
14362 * // Case-sensitive string ordering, with no pre-formatting method
14363 * $.extend( $.fn.dataTable.ext.order, {
14364 * "string-case-asc": function(x,y) {
14365 * return ((x < y) ? -1 : ((x > y) ? 1 : 0));
14367 * "string-case-desc": function(x,y) {
14368 * return ((x < y) ? 1 : ((x > y) ? -1 : 0));
14376 * Unique DataTables instance counter
14386 // The following properties are retained for backwards compatiblity only.
14387 // The should not be used in new projects and will be removed in a future
14392 * Version check function.
14394 * @depreciated Since 1.10
14396 fnVersionCheck: DataTable.fnVersionCheck,
14400 * Index for what 'this' index API functions should use
14402 * @deprecated Since v1.10
14408 * jQuery UI class container
14410 * @deprecated Since v1.10
14418 * @deprecated Since v1.10
14420 sVersion: DataTable.version
14425 // Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts
14428 afnFiltering: _ext.search,
14429 aTypes: _ext.type.detect,
14430 ofnSearch: _ext.type.search,
14431 oSort: _ext.type.order,
14432 afnSortData: _ext.order,
14433 aoFeatures: _ext.feature,
14434 oApi: _ext.internal,
14435 oStdClasses: _ext.classes,
14436 oPagination: _ext.pager
14440 $.extend( DataTable.ext.classes, {
14441 "sTable": "dataTable",
14442 "sNoFooter": "no-footer",
14444 /* Paging buttons */
14445 "sPageButton": "paginate_button",
14446 "sPageButtonActive": "current",
14447 "sPageButtonDisabled": "disabled",
14449 /* Striping classes */
14450 "sStripeOdd": "odd",
14451 "sStripeEven": "even",
14454 "sRowEmpty": "dataTables_empty",
14457 "sWrapper": "dataTables_wrapper",
14458 "sFilter": "dataTables_filter",
14459 "sInfo": "dataTables_info",
14460 "sPaging": "dataTables_paginate paging_", /* Note that the type is postfixed */
14461 "sLength": "dataTables_length",
14462 "sProcessing": "dataTables_processing",
14465 "sSortAsc": "sorting_asc",
14466 "sSortDesc": "sorting_desc",
14467 "sSortable": "sorting", /* Sortable in both directions */
14468 "sSortableAsc": "sorting_asc_disabled",
14469 "sSortableDesc": "sorting_desc_disabled",
14470 "sSortableNone": "sorting_disabled",
14471 "sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */
14474 "sFilterInput": "",
14477 "sLengthSelect": "",
14480 "sScrollWrapper": "dataTables_scroll",
14481 "sScrollHead": "dataTables_scrollHead",
14482 "sScrollHeadInner": "dataTables_scrollHeadInner",
14483 "sScrollBody": "dataTables_scrollBody",
14484 "sScrollFoot": "dataTables_scrollFoot",
14485 "sScrollFootInner": "dataTables_scrollFootInner",
14493 "sSortJUIDesc": "",
14495 "sSortJUIAscAllowed": "",
14496 "sSortJUIDescAllowed": "",
14497 "sSortJUIWrapper": "",
14504 var extPagination = DataTable.ext.pager;
14506 function _numbers ( page, pages ) {
14509 buttons = extPagination.numbers_length,
14510 half = Math.floor( buttons / 2 ),
14513 if ( pages <= buttons ) {
14514 numbers = _range( 0, pages );
14516 else if ( page <= half ) {
14517 numbers = _range( 0, buttons-2 );
14518 numbers.push( 'ellipsis' );
14519 numbers.push( pages-1 );
14521 else if ( page >= pages - 1 - half ) {
14522 numbers = _range( pages-(buttons-2), pages );
14523 numbers.splice( 0, 0, 'ellipsis' ); // no unshift in ie6
14524 numbers.splice( 0, 0, 0 );
14527 numbers = _range( page-half+2, page+half-1 );
14528 numbers.push( 'ellipsis' );
14529 numbers.push( pages-1 );
14530 numbers.splice( 0, 0, 'ellipsis' );
14531 numbers.splice( 0, 0, 0 );
14534 numbers.DT_el = 'span';
14539 $.extend( extPagination, {
14540 simple: function ( page, pages ) {
14541 return [ 'previous', 'next' ];
14544 full: function ( page, pages ) {
14545 return [ 'first', 'previous', 'next', 'last' ];
14548 numbers: function ( page, pages ) {
14549 return [ _numbers(page, pages) ];
14552 simple_numbers: function ( page, pages ) {
14553 return [ 'previous', _numbers(page, pages), 'next' ];
14556 full_numbers: function ( page, pages ) {
14557 return [ 'first', 'previous', _numbers(page, pages), 'next', 'last' ];
14560 first_last_numbers: function (page, pages) {
14561 return ['first', _numbers(page, pages), 'last'];
14564 // For testing and plug-ins to use
14565 _numbers: _numbers,
14567 // Number of number buttons (including ellipsis) to show. _Must be odd!_
14572 $.extend( true, DataTable.ext.renderer, {
14574 _: function ( settings, host, idx, buttons, page, pages ) {
14575 var classes = settings.oClasses;
14576 var lang = settings.oLanguage.oPaginate;
14577 var aria = settings.oLanguage.oAria.paginate || {};
14578 var btnDisplay, btnClass, counter=0;
14580 var attach = function( container, buttons ) {
14581 var i, ien, node, button, tabIndex;
14582 var disabledClass = classes.sPageButtonDisabled;
14583 var clickHandler = function ( e ) {
14584 _fnPageChange( settings, e.data.action, true );
14587 for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
14588 button = buttons[i];
14590 if ( $.isArray( button ) ) {
14591 var inner = $( '<'+(button.DT_el || 'div')+'/>' )
14592 .appendTo( container );
14593 attach( inner, button );
14598 tabIndex = settings.iTabIndex;
14600 switch ( button ) {
14602 container.append('<span class="ellipsis">…</span>');
14606 btnDisplay = lang.sFirst;
14608 if ( page === 0 ) {
14610 btnClass += ' ' + disabledClass;
14615 btnDisplay = lang.sPrevious;
14617 if ( page === 0 ) {
14619 btnClass += ' ' + disabledClass;
14624 btnDisplay = lang.sNext;
14626 if ( pages === 0 || page === pages-1 ) {
14628 btnClass += ' ' + disabledClass;
14633 btnDisplay = lang.sLast;
14635 if ( page === pages-1 ) {
14637 btnClass += ' ' + disabledClass;
14642 btnDisplay = button + 1;
14643 btnClass = page === button ?
14644 classes.sPageButtonActive : '';
14648 if ( btnDisplay !== null ) {
14650 'class': classes.sPageButton+' '+btnClass,
14651 'aria-controls': settings.sTableId,
14652 'aria-label': aria[ button ],
14653 'data-dt-idx': counter,
14654 'tabindex': tabIndex,
14655 'id': idx === 0 && typeof button === 'string' ?
14656 settings.sTableId +'_'+ button :
14659 .html( btnDisplay )
14660 .appendTo( container );
14663 node, {action: button}, clickHandler
14672 // IE9 throws an 'unknown error' if document.activeElement is used
14673 // inside an iframe or frame. Try / catch the error. Not good for
14674 // accessibility, but neither are frames.
14678 // Because this approach is destroying and recreating the paging
14679 // elements, focus is lost on the select button which is bad for
14680 // accessibility. So we want to restore focus once the draw has
14682 activeEl = $(host).find(document.activeElement).data('dt-idx');
14686 attach( $(host).empty(), buttons );
14688 if ( activeEl !== undefined ) {
14689 $(host).find( '[data-dt-idx='+activeEl+']' ).trigger('focus');
14697 // Built in type detection. See model.ext.aTypes for information about
14698 // what is required from this methods.
14699 $.extend( DataTable.ext.type.detect, [
14700 // Plain numbers - first since V8 detects some plain numbers as dates
14701 // e.g. Date.parse('55') (but not all, e.g. Date.parse('22')...).
14702 function ( d, settings )
14704 var decimal = settings.oLanguage.sDecimal;
14705 return _isNumber( d, decimal ) ? 'num'+decimal : null;
14708 // Dates (only those recognised by the browser's Date.parse)
14709 function ( d, settings )
14711 // V8 tries _very_ hard to make a string passed into `Date.parse()`
14712 // valid, so we need to use a regex to restrict date formats. Use a
14713 // plug-in for anything other than ISO8601 style strings
14714 if ( d && !(d instanceof Date) && ! _re_date.test(d) ) {
14717 var parsed = Date.parse(d);
14718 return (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null;
14721 // Formatted numbers
14722 function ( d, settings )
14724 var decimal = settings.oLanguage.sDecimal;
14725 return _isNumber( d, decimal, true ) ? 'num-fmt'+decimal : null;
14729 function ( d, settings )
14731 var decimal = settings.oLanguage.sDecimal;
14732 return _htmlNumeric( d, decimal ) ? 'html-num'+decimal : null;
14735 // HTML numeric, formatted
14736 function ( d, settings )
14738 var decimal = settings.oLanguage.sDecimal;
14739 return _htmlNumeric( d, decimal, true ) ? 'html-num-fmt'+decimal : null;
14742 // HTML (this is strict checking - there must be html)
14743 function ( d, settings )
14745 return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1) ?
14752 // Filter formatting functions. See model.ext.ofnSearch for information about
14753 // what is required from these methods.
14755 // Note that additional search methods are added for the html numbers and
14756 // html formatted numbers by `_addNumericSort()` when we know what the decimal
14760 $.extend( DataTable.ext.type.search, {
14761 html: function ( data ) {
14762 return _empty(data) ?
14764 typeof data === 'string' ?
14766 .replace( _re_new_lines, " " )
14767 .replace( _re_html, "" ) :
14771 string: function ( data ) {
14772 return _empty(data) ?
14774 typeof data === 'string' ?
14775 data.replace( _re_new_lines, " " ) :
14782 var __numericReplace = function ( d, decimalPlace, re1, re2 ) {
14783 if ( d !== 0 && (!d || d === '-') ) {
14787 // If a decimal place other than `.` is used, it needs to be given to the
14788 // function so we can detect it and replace with a `.` which is the only
14789 // decimal place Javascript recognises - it is not locale aware.
14790 if ( decimalPlace ) {
14791 d = _numToDecimal( d, decimalPlace );
14796 d = d.replace( re1, '' );
14800 d = d.replace( re2, '' );
14808 // Add the numeric 'deformatting' functions for sorting and search. This is done
14809 // in a function to provide an easy ability for the language options to add
14810 // additional methods if a non-period decimal place is used.
14811 function _addNumericSort ( decimalPlace ) {
14815 "num": function ( d ) {
14816 return __numericReplace( d, decimalPlace );
14819 // Formatted numbers
14820 "num-fmt": function ( d ) {
14821 return __numericReplace( d, decimalPlace, _re_formatted_numeric );
14825 "html-num": function ( d ) {
14826 return __numericReplace( d, decimalPlace, _re_html );
14829 // HTML numeric, formatted
14830 "html-num-fmt": function ( d ) {
14831 return __numericReplace( d, decimalPlace, _re_html, _re_formatted_numeric );
14834 function ( key, fn ) {
14835 // Add the ordering method
14836 _ext.type.order[ key+decimalPlace+'-pre' ] = fn;
14838 // For HTML types add a search formatter that will strip the HTML
14839 if ( key.match(/^html\-/) ) {
14840 _ext.type.search[ key+decimalPlace ] = _ext.type.search.html;
14847 // Default sort methods
14848 $.extend( _ext.type.order, {
14850 "date-pre": function ( d ) {
14851 var ts = Date.parse( d );
14852 return isNaN(ts) ? -Infinity : ts;
14856 "html-pre": function ( a ) {
14860 a.replace( /<.*?>/g, "" ).toLowerCase() :
14865 "string-pre": function ( a ) {
14866 // This is a little complex, but faster than always calling toString,
14867 // http://jsperf.com/tostring-v-check
14870 typeof a === 'string' ?
14877 // string-asc and -desc are retained only for compatibility with the old
14879 "string-asc": function ( x, y ) {
14880 return ((x < y) ? -1 : ((x > y) ? 1 : 0));
14883 "string-desc": function ( x, y ) {
14884 return ((x < y) ? 1 : ((x > y) ? -1 : 0));
14889 // Numeric sorting types - order doesn't matter here
14890 _addNumericSort( '' );
14893 $.extend( true, DataTable.ext.renderer, {
14895 _: function ( settings, cell, column, classes ) {
14896 // No additional mark-up required
14897 // Attach a sort listener to update on sort - note that using the
14898 // `DT` namespace will allow the event to be removed automatically
14899 // on destroy, while the `dt` namespaced event is the one we are
14901 $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {
14902 if ( settings !== ctx ) { // need to check this this is the host
14903 return; // table, not a nested one
14906 var colIdx = column.idx;
14910 column.sSortingClass +' '+
14911 classes.sSortAsc +' '+
14914 .addClass( columns[ colIdx ] == 'asc' ?
14915 classes.sSortAsc : columns[ colIdx ] == 'desc' ?
14916 classes.sSortDesc :
14917 column.sSortingClass
14922 jqueryui: function ( settings, cell, column, classes ) {
14924 .addClass( classes.sSortJUIWrapper )
14925 .append( cell.contents() )
14926 .append( $('<span/>')
14927 .addClass( classes.sSortIcon+' '+column.sSortingClassJUI )
14931 // Attach a sort listener to update on sort
14932 $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {
14933 if ( settings !== ctx ) {
14937 var colIdx = column.idx;
14940 .removeClass( classes.sSortAsc +" "+classes.sSortDesc )
14941 .addClass( columns[ colIdx ] == 'asc' ?
14942 classes.sSortAsc : columns[ colIdx ] == 'desc' ?
14943 classes.sSortDesc :
14944 column.sSortingClass
14948 .find( 'span.'+classes.sSortIcon )
14950 classes.sSortJUIAsc +" "+
14951 classes.sSortJUIDesc +" "+
14952 classes.sSortJUI +" "+
14953 classes.sSortJUIAscAllowed +" "+
14954 classes.sSortJUIDescAllowed
14956 .addClass( columns[ colIdx ] == 'asc' ?
14957 classes.sSortJUIAsc : columns[ colIdx ] == 'desc' ?
14958 classes.sSortJUIDesc :
14959 column.sSortingClassJUI
14967 * Public helper functions. These aren't used internally by DataTables, or
14968 * called by any of the options passed into DataTables, but they can be used
14969 * externally by developers working with DataTables. They are helper functions
14970 * to make working with DataTables a little bit easier.
14973 var __htmlEscapeEntities = function ( d ) {
14974 return typeof d === 'string' ?
14976 .replace(/&/g, '&')
14977 .replace(/</g, '<')
14978 .replace(/>/g, '>')
14979 .replace(/"/g, '"') :
14984 * Helpers for `columns.render`.
14986 * The options defined here can be used with the `columns.render` initialisation
14987 * option to provide a display renderer. The following functions are defined:
14989 * * `number` - Will format numeric data (defined by `columns.data`) for
14990 * display, retaining the original unformatted data for sorting and filtering.
14991 * It takes 5 parameters:
14992 * * `string` - Thousands grouping separator
14993 * * `string` - Decimal point indicator
14994 * * `integer` - Number of decimal points to show
14995 * * `string` (optional) - Prefix.
14996 * * `string` (optional) - Postfix (/suffix).
14997 * * `text` - Escape HTML to help prevent XSS attacks. It has no optional
15001 * // Column definition using the number renderer
15004 * render: $.fn.dataTable.render.number( '\'', '.', 0, '$' )
15009 DataTable.render = {
15010 number: function ( thousands, decimal, precision, prefix, postfix ) {
15012 display: function ( d ) {
15013 if ( typeof d !== 'number' && typeof d !== 'string' ) {
15017 var negative = d < 0 ? '-' : '';
15018 var flo = parseFloat( d );
15020 // If NaN then there isn't much formatting that we can do - just
15021 // return immediately, escaping any HTML (this was supposed to
15022 // be a number after all)
15023 if ( isNaN( flo ) ) {
15024 return __htmlEscapeEntities( d );
15027 flo = flo.toFixed( precision );
15028 d = Math.abs( flo );
15030 var intPart = parseInt( d, 10 );
15031 var floatPart = precision ?
15032 decimal+(d - intPart).toFixed( precision ).substring( 2 ):
15035 return negative + (prefix||'') +
15036 intPart.toString().replace(
15037 /\B(?=(\d{3})+(?!\d))/g, thousands
15045 text: function () {
15047 display: __htmlEscapeEntities,
15048 filter: __htmlEscapeEntities
15055 * This is really a good bit rubbish this method of exposing the internal methods
15056 * publicly... - To be fixed in 2.0 using methods on the prototype
15061 * Create a wrapper function for exporting an internal functions to an external API.
15062 * @param {string} fn API function name
15063 * @returns {function} wrapped function
15064 * @memberof DataTable#internal
15066 function _fnExternApiFunc (fn)
15068 return function() {
15069 var args = [_fnSettingsFromNode( this[DataTable.ext.iApiIndex] )].concat(
15070 Array.prototype.slice.call(arguments)
15072 return DataTable.ext.internal[fn].apply( this, args );
15078 * Reference to internal functions for use by plug-in developers. Note that
15079 * these methods are references to internal functions and are considered to be
15080 * private. If you use these methods, be aware that they are liable to change
15081 * between versions.
15084 $.extend( DataTable.ext.internal, {
15085 _fnExternApiFunc: _fnExternApiFunc,
15086 _fnBuildAjax: _fnBuildAjax,
15087 _fnAjaxUpdate: _fnAjaxUpdate,
15088 _fnAjaxParameters: _fnAjaxParameters,
15089 _fnAjaxUpdateDraw: _fnAjaxUpdateDraw,
15090 _fnAjaxDataSrc: _fnAjaxDataSrc,
15091 _fnAddColumn: _fnAddColumn,
15092 _fnColumnOptions: _fnColumnOptions,
15093 _fnAdjustColumnSizing: _fnAdjustColumnSizing,
15094 _fnVisibleToColumnIndex: _fnVisibleToColumnIndex,
15095 _fnColumnIndexToVisible: _fnColumnIndexToVisible,
15096 _fnVisbleColumns: _fnVisbleColumns,
15097 _fnGetColumns: _fnGetColumns,
15098 _fnColumnTypes: _fnColumnTypes,
15099 _fnApplyColumnDefs: _fnApplyColumnDefs,
15100 _fnHungarianMap: _fnHungarianMap,
15101 _fnCamelToHungarian: _fnCamelToHungarian,
15102 _fnLanguageCompat: _fnLanguageCompat,
15103 _fnBrowserDetect: _fnBrowserDetect,
15104 _fnAddData: _fnAddData,
15105 _fnAddTr: _fnAddTr,
15106 _fnNodeToDataIndex: _fnNodeToDataIndex,
15107 _fnNodeToColumnIndex: _fnNodeToColumnIndex,
15108 _fnGetCellData: _fnGetCellData,
15109 _fnSetCellData: _fnSetCellData,
15110 _fnSplitObjNotation: _fnSplitObjNotation,
15111 _fnGetObjectDataFn: _fnGetObjectDataFn,
15112 _fnSetObjectDataFn: _fnSetObjectDataFn,
15113 _fnGetDataMaster: _fnGetDataMaster,
15114 _fnClearTable: _fnClearTable,
15115 _fnDeleteIndex: _fnDeleteIndex,
15116 _fnInvalidate: _fnInvalidate,
15117 _fnGetRowElements: _fnGetRowElements,
15118 _fnCreateTr: _fnCreateTr,
15119 _fnBuildHead: _fnBuildHead,
15120 _fnDrawHead: _fnDrawHead,
15122 _fnReDraw: _fnReDraw,
15123 _fnAddOptionsHtml: _fnAddOptionsHtml,
15124 _fnDetectHeader: _fnDetectHeader,
15125 _fnGetUniqueThs: _fnGetUniqueThs,
15126 _fnFeatureHtmlFilter: _fnFeatureHtmlFilter,
15127 _fnFilterComplete: _fnFilterComplete,
15128 _fnFilterCustom: _fnFilterCustom,
15129 _fnFilterColumn: _fnFilterColumn,
15130 _fnFilter: _fnFilter,
15131 _fnFilterCreateSearch: _fnFilterCreateSearch,
15132 _fnEscapeRegex: _fnEscapeRegex,
15133 _fnFilterData: _fnFilterData,
15134 _fnFeatureHtmlInfo: _fnFeatureHtmlInfo,
15135 _fnUpdateInfo: _fnUpdateInfo,
15136 _fnInfoMacros: _fnInfoMacros,
15137 _fnInitialise: _fnInitialise,
15138 _fnInitComplete: _fnInitComplete,
15139 _fnLengthChange: _fnLengthChange,
15140 _fnFeatureHtmlLength: _fnFeatureHtmlLength,
15141 _fnFeatureHtmlPaginate: _fnFeatureHtmlPaginate,
15142 _fnPageChange: _fnPageChange,
15143 _fnFeatureHtmlProcessing: _fnFeatureHtmlProcessing,
15144 _fnProcessingDisplay: _fnProcessingDisplay,
15145 _fnFeatureHtmlTable: _fnFeatureHtmlTable,
15146 _fnScrollDraw: _fnScrollDraw,
15147 _fnApplyToChildren: _fnApplyToChildren,
15148 _fnCalculateColumnWidths: _fnCalculateColumnWidths,
15149 _fnThrottle: _fnThrottle,
15150 _fnConvertToWidth: _fnConvertToWidth,
15151 _fnGetWidestNode: _fnGetWidestNode,
15152 _fnGetMaxLenString: _fnGetMaxLenString,
15153 _fnStringToCss: _fnStringToCss,
15154 _fnSortFlatten: _fnSortFlatten,
15156 _fnSortAria: _fnSortAria,
15157 _fnSortListener: _fnSortListener,
15158 _fnSortAttachListener: _fnSortAttachListener,
15159 _fnSortingClasses: _fnSortingClasses,
15160 _fnSortData: _fnSortData,
15161 _fnSaveState: _fnSaveState,
15162 _fnLoadState: _fnLoadState,
15163 _fnSettingsFromNode: _fnSettingsFromNode,
15166 _fnBindAction: _fnBindAction,
15167 _fnCallbackReg: _fnCallbackReg,
15168 _fnCallbackFire: _fnCallbackFire,
15169 _fnLengthOverflow: _fnLengthOverflow,
15170 _fnRenderer: _fnRenderer,
15171 _fnDataSource: _fnDataSource,
15172 _fnRowAttributes: _fnRowAttributes,
15173 _fnExtend: _fnExtend,
15174 _fnCalculateEnd: function () {} // Used by a lot of plug-ins, but redundant
15175 // in 1.10, so this dead-end function is
15176 // added to prevent errors
15181 $.fn.dataTable = DataTable;
15183 // Provide access to the host jQuery object (circular reference)
15187 $.fn.dataTableSettings = DataTable.settings;
15188 $.fn.dataTableExt = DataTable.ext;
15190 // With a capital `D` we return a DataTables API instance rather than a
15192 $.fn.DataTable = function ( opts ) {
15193 return $(this).dataTable( opts ).api();
15196 // All properties that are available to $.fn.dataTable should also be
15197 // available on $.fn.DataTable
15198 $.each( DataTable, function ( prop, val ) {
15199 $.fn.DataTable[ prop ] = val;
15203 // Information about events fired by DataTables - for documentation.
15205 * Draw event, fired whenever the table is redrawn on the page, at the same
15206 * point as fnDrawCallback. This may be useful for binding events or
15207 * performing calculations when the table is altered at all.
15208 * @name DataTable#draw.dt
15210 * @param {event} e jQuery event object
15211 * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
15215 * Search event, fired when the searching applied to the table (using the
15216 * built-in global search, or column filters) is altered.
15217 * @name DataTable#search.dt
15219 * @param {event} e jQuery event object
15220 * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
15224 * Page change event, fired when the paging of the table is altered.
15225 * @name DataTable#page.dt
15227 * @param {event} e jQuery event object
15228 * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
15232 * Order event, fired when the ordering applied to the table is altered.
15233 * @name DataTable#order.dt
15235 * @param {event} e jQuery event object
15236 * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
15240 * DataTables initialisation complete event, fired when the table is fully
15241 * drawn, including Ajax data loaded, if Ajax data is required.
15242 * @name DataTable#init.dt
15244 * @param {event} e jQuery event object
15245 * @param {object} oSettings DataTables settings object
15246 * @param {object} json The JSON object request from the server - only
15247 * present if client-side Ajax sourced data is used</li></ol>
15251 * State save event, fired when the table has changed state a new state save
15252 * is required. This event allows modification of the state saving object
15253 * prior to actually doing the save, including addition or other state
15254 * properties (for plug-ins) or modification of a DataTables core property.
15255 * @name DataTable#stateSaveParams.dt
15257 * @param {event} e jQuery event object
15258 * @param {object} oSettings DataTables settings object
15259 * @param {object} json The state information to be saved
15263 * State load event, fired when the table is loading state from the stored
15264 * data, but prior to the settings object being modified by the saved state
15265 * - allowing modification of the saved state is required or loading of
15266 * state for a plug-in.
15267 * @name DataTable#stateLoadParams.dt
15269 * @param {event} e jQuery event object
15270 * @param {object} oSettings DataTables settings object
15271 * @param {object} json The saved state information
15275 * State loaded event, fired when state has been loaded from stored data and
15276 * the settings object has been modified by the loaded data.
15277 * @name DataTable#stateLoaded.dt
15279 * @param {event} e jQuery event object
15280 * @param {object} oSettings DataTables settings object
15281 * @param {object} json The saved state information
15285 * Processing event, fired when DataTables is doing some kind of processing
15286 * (be it, order, search or anything else). It can be used to indicate to
15287 * the end user that there is something happening, or that something has
15289 * @name DataTable#processing.dt
15291 * @param {event} e jQuery event object
15292 * @param {object} oSettings DataTables settings object
15293 * @param {boolean} bShow Flag for if DataTables is doing processing or not
15297 * Ajax (XHR) event, fired whenever an Ajax request is completed from a
15298 * request to made to the server for new data. This event is called before
15299 * DataTables processed the returned data, so it can also be used to pre-
15300 * process the data returned from the server, if needed.
15302 * Note that this trigger is called in `fnServerData`, if you override
15303 * `fnServerData` and which to use this event, you need to trigger it in you
15304 * success function.
15305 * @name DataTable#xhr.dt
15307 * @param {event} e jQuery event object
15308 * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
15309 * @param {object} json JSON returned from the server
15312 * // Use a custom property returned from the server in another DOM element
15313 * $('#table').dataTable().on('xhr.dt', function (e, settings, json) {
15314 * $('#status').html( json.status );
15318 * // Pre-process the data returned from the server
15319 * $('#table').dataTable().on('xhr.dt', function (e, settings, json) {
15320 * for ( var i=0, ien=json.aaData.length ; i<ien ; i++ ) {
15321 * json.aaData[i].sum = json.aaData[i].one + json.aaData[i].two;
15323 * // Note no return - manipulate the data directly in the JSON object.
15328 * Destroy event, fired when the DataTable is destroyed by calling fnDestroy
15329 * or passing the bDestroy:true parameter in the initialisation object. This
15330 * can be used to remove bound events, added DOM nodes, etc.
15331 * @name DataTable#destroy.dt
15333 * @param {event} e jQuery event object
15334 * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
15338 * Page length change event, fired when number of records to show on each
15339 * page (the length) is changed.
15340 * @name DataTable#length.dt
15342 * @param {event} e jQuery event object
15343 * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
15344 * @param {integer} len New length
15348 * Column sizing has changed.
15349 * @name DataTable#column-sizing.dt
15351 * @param {event} e jQuery event object
15352 * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
15356 * Column visibility has changed.
15357 * @name DataTable#column-visibility.dt
15359 * @param {event} e jQuery event object
15360 * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
15361 * @param {int} column Column index
15362 * @param {bool} vis `false` if column now hidden, or `true` if visible
15365 return $.fn.dataTable;
15369 /*! FixedHeader 3.1.7
15370 * ©2009-2020 SpryMedia Ltd - datatables.net/license
15374 * @summary FixedHeader
15375 * @description Fix a table's header or footer, so it is always visible while
15378 * @file dataTables.fixedHeader.js
15379 * @author SpryMedia Ltd (www.sprymedia.co.uk)
15380 * @contact www.sprymedia.co.uk/contact
15381 * @copyright Copyright 2009-2020 SpryMedia Ltd.
15383 * This source file is free software, available under the following license:
15384 * MIT license - http://datatables.net/license/mit
15386 * This source file is distributed in the hope that it will be useful, but
15387 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15388 * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
15390 * For details please refer to: http://www.datatables.net
15393 (function( factory ){
15394 if ( typeof define === 'function' && define.amd ) {
15396 define( ['jquery', 'datatables.net'], function ( $ ) {
15397 return factory( $, window, document );
15400 else if ( typeof exports === 'object' ) {
15402 module.exports = function (root, $) {
15407 if ( ! $ || ! $.fn.dataTable ) {
15408 $ = require('datatables.net')(root, $).$;
15411 return factory( $, root, root.document );
15416 factory( jQuery, window, document );
15418 }(function( $, window, document, undefined ) {
15420 var DataTable = $.fn.dataTable;
15423 var _instCounter = 0;
15425 var FixedHeader = function ( dt, config ) {
15426 // Sanity check - you just know it will happen
15427 if ( ! (this instanceof FixedHeader) ) {
15428 throw "FixedHeader must be initialised with the 'new' keyword.";
15431 // Allow a boolean true for defaults
15432 if ( config === true ) {
15436 dt = new DataTable.Api( dt );
15438 this.c = $.extend( true, {}, FixedHeader.defaults, config );
15451 windowHeight: $(window).height(),
15456 autoWidth: dt.settings()[0].oFeatures.bAutoWidth,
15457 namespace: '.dtfc'+(_instCounter++),
15466 floatingHeader: null,
15467 thead: $(dt.table().header()),
15468 tbody: $(dt.table().body()),
15469 tfoot: $(dt.table().footer()),
15482 this.dom.header.host = this.dom.thead.parent();
15483 this.dom.footer.host = this.dom.tfoot.parent();
15485 var dtSettings = dt.settings()[0];
15486 if ( dtSettings._fixedHeader ) {
15487 throw "FixedHeader already initialised on table "+dtSettings.nTable.id;
15490 dtSettings._fixedHeader = this;
15492 this._constructor();
15497 * Variable: FixedHeader
15498 * Purpose: Prototype for FixedHeader
15501 $.extend( FixedHeader.prototype, {
15502 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15507 * Kill off FH and any events
15509 destroy: function () {
15510 this.s.dt.off( '.dtfc' );
15511 $(window).off( this.s.namespace );
15513 if ( this.c.header ) {
15514 this._modeChange( 'in-place', 'header', true );
15517 if ( this.c.footer && this.dom.tfoot.length ) {
15518 this._modeChange( 'in-place', 'footer', true );
15523 * Enable / disable the fixed elements
15525 * @param {boolean} enable `true` to enable, `false` to disable
15527 enable: function ( enable, update )
15529 this.s.enable = enable;
15531 if ( update || update === undefined ) {
15533 this._scroll( true );
15538 * Get enabled status
15540 enabled: function ()
15542 return this.s.enable;
15546 * Set header offset
15548 * @param {int} new value for headerOffset
15550 headerOffset: function ( offset )
15552 if ( offset !== undefined ) {
15553 this.c.headerOffset = offset;
15557 return this.c.headerOffset;
15561 * Set footer offset
15563 * @param {int} new value for footerOffset
15565 footerOffset: function ( offset )
15567 if ( offset !== undefined ) {
15568 this.c.footerOffset = offset;
15572 return this.c.footerOffset;
15577 * Recalculate the position of the fixed elements and force them into place
15579 update: function ()
15581 var table = this.s.dt.table().node();
15583 if ( $(table).is(':visible') ) {
15584 this.enable( true, false );
15587 this.enable( false, false );
15591 this._scroll( true );
15595 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15600 * FixedHeader constructor - adding the required event listeners and
15601 * simple initialisation
15605 _constructor: function ()
15608 var dt = this.s.dt;
15611 .on( 'scroll'+this.s.namespace, function () {
15614 .on( 'resize'+this.s.namespace, DataTable.util.throttle( function () {
15615 that.s.position.windowHeight = $(window).height();
15619 var autoHeader = $('.fh-fixedHeader');
15620 if ( ! this.c.headerOffset && autoHeader.length ) {
15621 this.c.headerOffset = autoHeader.outerHeight();
15624 var autoFooter = $('.fh-fixedFooter');
15625 if ( ! this.c.footerOffset && autoFooter.length ) {
15626 this.c.footerOffset = autoFooter.outerHeight();
15629 dt.on( 'column-reorder.dt.dtfc column-visibility.dt.dtfc draw.dt.dtfc column-sizing.dt.dtfc responsive-display.dt.dtfc', function () {
15633 dt.on( 'destroy.dtfc', function () {
15642 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15647 * Clone a fixed item to act as a place holder for the original element
15648 * which is moved into a clone of the table element, and moved around the
15649 * document to give the fixed effect.
15651 * @param {string} item 'header' or 'footer'
15652 * @param {boolean} force Force the clone to happen, or allow automatic
15653 * decision (reuse existing if available)
15656 _clone: function ( item, force )
15658 var dt = this.s.dt;
15659 var itemDom = this.dom[ item ];
15660 var itemElement = item === 'header' ?
15664 if ( ! force && itemDom.floating ) {
15665 // existing floating element - reuse it
15666 itemDom.floating.removeClass( 'fixedHeader-floating fixedHeader-locked' );
15669 if ( itemDom.floating ) {
15670 itemDom.placeholder.remove();
15671 this._unsize( item );
15672 itemDom.floating.children().detach();
15673 itemDom.floating.remove();
15676 itemDom.floating = $( dt.table().node().cloneNode( false ) )
15677 .css( 'table-layout', 'fixed' )
15678 .attr( 'aria-hidden', 'true' )
15679 .removeAttr( 'id' )
15680 .append( itemElement )
15681 .appendTo( 'body' );
15683 // Insert a fake thead/tfoot into the DataTable to stop it jumping around
15684 itemDom.placeholder = itemElement.clone( false );
15685 itemDom.placeholder
15687 .removeAttr( 'id' );
15689 itemDom.host.prepend( itemDom.placeholder );
15692 this._matchWidths( itemDom.placeholder, itemDom.floating );
15697 * Copy widths from the cells in one element to another. This is required
15698 * for the footer as the footer in the main table takes its sizes from the
15699 * header columns. That isn't present in the footer so to have it still
15700 * align correctly, the sizes need to be copied over. It is also required
15701 * for the header when auto width is not enabled
15703 * @param {jQuery} from Copy widths from
15704 * @param {jQuery} to Copy widths to
15707 _matchWidths: function ( from, to ) {
15708 var get = function ( name ) {
15709 return $(name, from)
15710 .map( function () {
15711 return $(this).width();
15715 var set = function ( name, toWidths ) {
15716 $(name, to).each( function ( i ) {
15718 width: toWidths[i],
15719 minWidth: toWidths[i]
15724 var thWidths = get( 'th' );
15725 var tdWidths = get( 'td' );
15727 set( 'th', thWidths );
15728 set( 'td', tdWidths );
15732 * Remove assigned widths from the cells in an element. This is required
15733 * when inserting the footer back into the main table so the size is defined
15734 * by the header columns and also when auto width is disabled in the
15737 * @param {string} item The `header` or `footer`
15740 _unsize: function ( item ) {
15741 var el = this.dom[ item ].floating;
15743 if ( el && (item === 'footer' || (item === 'header' && ! this.s.autoWidth)) ) {
15744 $('th, td', el).css( {
15749 else if ( el && item === 'header' ) {
15750 $('th, td', el).css( 'min-width', '' );
15755 * Reposition the floating elements to take account of horizontal page
15758 * @param {string} item The `header` or `footer`
15759 * @param {int} scrollLeft Document scrollLeft
15762 _horizontal: function ( item, scrollLeft )
15764 var itemDom = this.dom[ item ];
15765 var position = this.s.position;
15766 var lastScrollLeft = this.s.scrollLeft;
15768 if ( itemDom.floating && lastScrollLeft[ item ] !== scrollLeft ) {
15769 itemDom.floating.css( 'left', position.left - scrollLeft );
15771 lastScrollLeft[ item ] = scrollLeft;
15776 * Change from one display mode to another. Each fixed item can be in one
15779 * * `in-place` - In the main DataTable
15780 * * `in` - Floating over the DataTable
15781 * * `below` - (Header only) Fixed to the bottom of the table body
15782 * * `above` - (Footer only) Fixed to the top of the table body
15784 * @param {string} mode Mode that the item should be shown in
15785 * @param {string} item 'header' or 'footer'
15786 * @param {boolean} forceChange Force a redraw of the mode, even if already
15790 _modeChange: function ( mode, item, forceChange )
15792 var dt = this.s.dt;
15793 var itemDom = this.dom[ item ];
15794 var position = this.s.position;
15796 // It isn't trivial to add a !important css attribute...
15797 var importantWidth = function (w) {
15798 itemDom.floating.attr('style', function(i,s) {
15799 return (s || '') + 'width: '+w+'px !important;';
15803 // Record focus. Browser's will cause input elements to loose focus if
15804 // they are inserted else where in the doc
15805 var tablePart = this.dom[ item==='footer' ? 'tfoot' : 'thead' ];
15806 var focus = $.contains( tablePart[0], document.activeElement ) ?
15807 document.activeElement :
15814 if ( mode === 'in-place' ) {
15815 // Insert the header back into the table's real header
15816 if ( itemDom.placeholder ) {
15817 itemDom.placeholder.remove();
15818 itemDom.placeholder = null;
15821 this._unsize( item );
15823 if ( item === 'header' ) {
15824 itemDom.host.prepend( tablePart );
15827 itemDom.host.append( tablePart );
15830 if ( itemDom.floating ) {
15831 itemDom.floating.remove();
15832 itemDom.floating = null;
15835 else if ( mode === 'in' ) {
15836 // Remove the header from the read header and insert into a fixed
15837 // positioned floating table clone
15838 this._clone( item, forceChange );
15841 .addClass( 'fixedHeader-floating' )
15842 .css( item === 'header' ? 'top' : 'bottom', this.c[item+'Offset'] )
15843 .css( 'left', position.left+'px' );
15845 importantWidth(position.width);
15847 if ( item === 'footer' ) {
15848 itemDom.floating.css( 'top', '' );
15851 else if ( mode === 'below' ) { // only used for the header
15852 // Fix the position of the floating header at base of the table body
15853 this._clone( item, forceChange );
15856 .addClass( 'fixedHeader-locked' )
15857 .css( 'top', position.tfootTop - position.theadHeight )
15858 .css( 'left', position.left+'px' );
15860 importantWidth(position.width);
15862 else if ( mode === 'above' ) { // only used for the footer
15863 // Fix the position of the floating footer at top of the table body
15864 this._clone( item, forceChange );
15867 .addClass( 'fixedHeader-locked' )
15868 .css( 'top', position.tbodyTop )
15869 .css( 'left', position.left+'px' );
15871 importantWidth(position.width);
15874 // Restore focus if it was lost
15875 if ( focus && focus !== document.activeElement ) {
15876 setTimeout( function () {
15881 this.s.scrollLeft.header = -1;
15882 this.s.scrollLeft.footer = -1;
15883 this.s[item+'Mode'] = mode;
15887 * Cache the positional information that is required for the mode
15888 * calculations that FixedHeader performs.
15892 _positions: function ()
15894 var dt = this.s.dt;
15895 var table = dt.table();
15896 var position = this.s.position;
15897 var dom = this.dom;
15898 var tableNode = $(table.node());
15900 // Need to use the header and footer that are in the main table,
15901 // regardless of if they are clones, since they hold the positions we
15902 // want to measure from
15903 var thead = tableNode.children('thead');
15904 var tfoot = tableNode.children('tfoot');
15905 var tbody = dom.tbody;
15907 position.visible = tableNode.is(':visible');
15908 position.width = tableNode.outerWidth();
15909 position.left = tableNode.offset().left;
15910 position.theadTop = thead.offset().top;
15911 position.tbodyTop = tbody.offset().top;
15912 position.tbodyHeight = tbody.outerHeight();
15913 position.theadHeight = position.tbodyTop - position.theadTop;
15915 if ( tfoot.length ) {
15916 position.tfootTop = tfoot.offset().top;
15917 position.tfootBottom = position.tfootTop + tfoot.outerHeight();
15918 position.tfootHeight = position.tfootBottom - position.tfootTop;
15921 position.tfootTop = position.tbodyTop + tbody.outerHeight();
15922 position.tfootBottom = position.tfootTop;
15923 position.tfootHeight = position.tfootTop;
15929 * Mode calculation - determine what mode the fixed items should be placed
15932 * @param {boolean} forceChange Force a redraw of the mode, even if already
15936 _scroll: function ( forceChange )
15938 var windowTop = $(document).scrollTop();
15939 var windowLeft = $(document).scrollLeft();
15940 var position = this.s.position;
15941 var headerMode, footerMode;
15943 if ( this.c.header ) {
15944 if ( ! this.s.enable ) {
15945 headerMode = 'in-place';
15947 else if ( ! position.visible || windowTop <= position.theadTop - this.c.headerOffset ) {
15948 headerMode = 'in-place';
15950 else if ( windowTop <= position.tfootTop - position.theadHeight - this.c.headerOffset ) {
15954 headerMode = 'below';
15957 if ( forceChange || headerMode !== this.s.headerMode ) {
15958 this._modeChange( headerMode, 'header', forceChange );
15961 this._horizontal( 'header', windowLeft );
15964 if ( this.c.footer && this.dom.tfoot.length ) {
15965 if ( ! this.s.enable ) {
15966 footerMode = 'in-place';
15968 else if ( ! position.visible || windowTop + position.windowHeight >= position.tfootBottom + this.c.footerOffset ) {
15969 footerMode = 'in-place';
15971 else if ( position.windowHeight + windowTop > position.tbodyTop + position.tfootHeight + this.c.footerOffset ) {
15975 footerMode = 'above';
15978 if ( forceChange || footerMode !== this.s.footerMode ) {
15979 this._modeChange( footerMode, 'footer', forceChange );
15982 this._horizontal( 'footer', windowLeft );
15993 FixedHeader.version = "3.1.7";
16000 FixedHeader.defaults = {
16008 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16009 * DataTables interfaces
16012 // Attach for constructor access
16013 $.fn.dataTable.FixedHeader = FixedHeader;
16014 $.fn.DataTable.FixedHeader = FixedHeader;
16017 // DataTables creation - check if the FixedHeader option has been defined on the
16018 // table and if so, initialise
16019 $(document).on( 'init.dt.dtfh', function (e, settings, json) {
16020 if ( e.namespace !== 'dt' ) {
16024 var init = settings.oInit.fixedHeader;
16025 var defaults = DataTable.defaults.fixedHeader;
16027 if ( (init || defaults) && ! settings._fixedHeader ) {
16028 var opts = $.extend( {}, defaults, init );
16030 if ( init !== false ) {
16031 new FixedHeader( settings, opts );
16036 // DataTables API methods
16037 DataTable.Api.register( 'fixedHeader()', function () {} );
16039 DataTable.Api.register( 'fixedHeader.adjust()', function () {
16040 return this.iterator( 'table', function ( ctx ) {
16041 var fh = ctx._fixedHeader;
16049 DataTable.Api.register( 'fixedHeader.enable()', function ( flag ) {
16050 return this.iterator( 'table', function ( ctx ) {
16051 var fh = ctx._fixedHeader;
16053 flag = ( flag !== undefined ? flag : true );
16054 if ( fh && flag !== fh.enabled() ) {
16060 DataTable.Api.register( 'fixedHeader.enabled()', function () {
16061 if ( this.context.length ) {
16062 var fh = this.content[0]._fixedHeader;
16065 return fh.enabled();
16072 DataTable.Api.register( 'fixedHeader.disable()', function ( ) {
16073 return this.iterator( 'table', function ( ctx ) {
16074 var fh = ctx._fixedHeader;
16076 if ( fh && fh.enabled() ) {
16077 fh.enable( false );
16082 $.each( ['header', 'footer'], function ( i, el ) {
16083 DataTable.Api.register( 'fixedHeader.'+el+'Offset()', function ( offset ) {
16084 var ctx = this.context;
16086 if ( offset === undefined ) {
16087 return ctx.length && ctx[0]._fixedHeader ?
16088 ctx[0]._fixedHeader[el +'Offset']() :
16092 return this.iterator( 'table', function ( ctx ) {
16093 var fh = ctx._fixedHeader;
16096 fh[ el +'Offset' ]( offset );
16103 return FixedHeader;
16107 /*! SearchPanes 1.1.1
16108 * 2019-2020 SpryMedia Ltd - datatables.net/license
16115 function setJQuery(jq) {
16117 DataTable = jq.fn.dataTable;
16119 var SearchPane = /** @class */ (function () {
16121 * Creates the panes, sets up the search function
16122 * @param paneSettings The settings for the searchPanes
16123 * @param opts The options for the default features
16124 * @param idx the index of the column for this pane
16125 * @returns {object} the pane that has been created, including the table and the index of the pane
16127 function SearchPane(paneSettings, opts, idx, layout, panesContainer, panes) {
16129 if (panes === void 0) { panes = null; }
16130 // Check that the required version of DataTables is included
16131 if (!DataTable || !DataTable.versionCheck || !DataTable.versionCheck('1.10.0')) {
16132 throw new Error('SearchPane requires DataTables 1.10 or newer');
16134 // Check that Select is included
16135 if (!DataTable.select) {
16136 throw new Error('SearchPane requires Select');
16138 var table = new DataTable.Api(paneSettings);
16139 this.classes = $.extend(true, {}, SearchPane.classes);
16140 // Get options from user
16141 this.c = $.extend(true, {}, SearchPane.defaults, opts);
16142 this.customPaneSettings = panes;
16144 cascadeRegen: false,
16151 filteringActive: false,
16154 lastCascade: false,
16166 filterMap: new Map(),
16170 searchFunction: undefined,
16171 selectPresent: false,
16173 serverSelecting: false,
16174 showFiltered: false,
16178 var rowLength = table.columns().eq(0).toArray().length;
16179 this.colExists = this.s.index < rowLength;
16180 // Add extra elements to DOM object including clear and hide buttons
16181 this.c.layout = layout;
16182 var layVal = parseInt(layout.split('-')[1], 10);
16184 buttonGroup: $('<div/>').addClass(this.classes.buttonGroup),
16185 clear: $('<button type="button">×</button>')
16186 .addClass(this.classes.dull)
16187 .addClass(this.classes.paneButton)
16188 .addClass(this.classes.clearButton),
16189 container: $('<div/>').addClass(this.classes.container).addClass(this.classes.layout +
16190 (layVal < 10 ? layout : layout.split('-')[0] + '-9')),
16191 countButton: $('<button type="button"></button>')
16192 .addClass(this.classes.paneButton)
16193 .addClass(this.classes.countButton),
16194 dtP: $('<table><thead><tr><th>' +
16196 ? $(table.column(this.colExists ? this.s.index : 0).header()).text()
16197 : this.customPaneSettings.header || 'Custom Pane') + '</th><th/></tr></thead></table>'),
16198 lower: $('<div/>').addClass(this.classes.subRow2).addClass(this.classes.narrowButton),
16199 nameButton: $('<button type="button"></button>').addClass(this.classes.paneButton).addClass(this.classes.nameButton),
16200 panesContainer: panesContainer,
16201 searchBox: $('<input/>').addClass(this.classes.paneInputButton).addClass(this.classes.search),
16202 searchButton: $('<button type = "button" class="' + this.classes.searchIcon + '"></button>')
16203 .addClass(this.classes.paneButton),
16204 searchCont: $('<div/>').addClass(this.classes.searchCont),
16205 searchLabelCont: $('<div/>').addClass(this.classes.searchLabelCont),
16206 topRow: $('<div/>').addClass(this.classes.topRow),
16207 upper: $('<div/>').addClass(this.classes.subRow1).addClass(this.classes.narrowSearch)
16209 this.s.displayed = false;
16211 this.selections = [];
16212 this.s.colOpts = this.colExists ? this._getOptions() : this._getBonusOptions();
16213 var colOpts = this.s.colOpts;
16214 var clear = $('<button type="button">X</button>').addClass(this.classes.paneButton);
16215 $(clear).text(table.i18n('searchPanes.clearPane', 'X'));
16216 this.dom.container.addClass(colOpts.className);
16217 this.dom.container.addClass((this.customPaneSettings !== null && this.customPaneSettings.className !== undefined)
16218 ? this.customPaneSettings.className
16220 // Set the value of name incase ordering is desired
16221 if (this.s.colOpts.name !== undefined) {
16222 this.s.name = this.s.colOpts.name;
16224 else if (this.customPaneSettings !== null && this.customPaneSettings.name !== undefined) {
16225 this.s.name = this.customPaneSettings.name;
16228 this.s.name = this.colExists ?
16229 $(table.column(this.s.index).header()).text() :
16230 this.customPaneSettings.header || 'Custom Pane';
16232 $(panesContainer).append(this.dom.container);
16233 var tableNode = table.table(0).node();
16234 // Custom search function for table
16235 this.s.searchFunction = function (settings, searchData, dataIndex, origData) {
16236 // If no data has been selected then show all
16237 if (_this.selections.length === 0) {
16240 if (settings.nTable !== tableNode) {
16244 if (_this.colExists) {
16245 // Get the current filtered data
16246 filter = searchData[_this.s.index];
16247 if (colOpts.orthogonal.filter !== 'filter') {
16248 // get the filter value from the map
16249 filter = _this.s.rowData.filterMap.get(dataIndex);
16250 if (filter instanceof $.fn.dataTable.Api) {
16251 filter = filter.toArray();
16255 return _this._search(filter, dataIndex);
16257 $.fn.dataTable.ext.search.push(this.s.searchFunction);
16258 // If the clear button for this pane is clicked clear the selections
16259 if (this.c.clear) {
16260 $(clear).on('click', function () {
16261 var searches = _this.dom.container.find(_this.classes.search);
16262 searches.each(function () {
16264 $(this).trigger('input');
16269 // Sometimes the top row of the panes containing the search box and ordering buttons appears
16270 // weird if the width of the panes is lower than expected, this fixes the design.
16271 // Equally this may occur when the table is resized.
16272 table.on('draw.dtsp', function () {
16273 _this._adjustTopRow();
16275 table.on('buttons-action', function () {
16276 _this._adjustTopRow();
16278 $(window).on('resize.dtsp', DataTable.util.throttle(function () {
16279 _this._adjustTopRow();
16281 // When column-reorder is present and the columns are moved, it is necessary to
16282 // reassign all of the panes indexes to the new index of the column.
16283 table.on('column-reorder.dtsp', function (e, settings, details) {
16284 _this.s.index = details.mapping[_this.s.index];
16289 * In the case of a rebuild there is potential for new data to have been included or removed
16290 * so all of the rowData must be reset as a precaution.
16292 SearchPane.prototype.clearData = function () {
16300 filterMap: new Map(),
16305 * Clear the selections in the pane
16307 SearchPane.prototype.clearPane = function () {
16308 // Deselect all rows which are selected and update the table and filter count.
16309 this.s.dtPane.rows({ selected: true }).deselect();
16310 this.updateTable();
16314 * Strips all of the SearchPanes elements from the document and turns all of the listeners for the buttons off
16316 SearchPane.prototype.destroy = function () {
16317 $(this.s.dtPane).off('.dtsp');
16318 $(this.s.dt).off('.dtsp');
16319 $(this.dom.nameButton).off('.dtsp');
16320 $(this.dom.countButton).off('.dtsp');
16321 $(this.dom.clear).off('.dtsp');
16322 $(this.dom.searchButton).off('.dtsp');
16323 $(this.dom.container).remove();
16324 var searchIdx = $.fn.dataTable.ext.search.indexOf(this.s.searchFunction);
16325 while (searchIdx !== -1) {
16326 $.fn.dataTable.ext.search.splice(searchIdx, 1);
16327 searchIdx = $.fn.dataTable.ext.search.indexOf(this.s.searchFunction);
16329 // If the datatables have been defined for the panes then also destroy these
16330 if (this.s.dtPane !== undefined) {
16331 this.s.dtPane.destroy();
16333 this.s.listSet = false;
16336 * Updates the number of filters that have been applied in the title
16338 SearchPane.prototype.getPaneCount = function () {
16339 return this.s.dtPane !== undefined ?
16340 this.s.dtPane.rows({ selected: true }).data().toArray().length :
16344 * Rebuilds the panes from the start having deleted the old ones
16345 * @param? last boolean to indicate if this is the last pane a selection was made in
16346 * @param? dataIn data to be used in buildPane
16347 * @param? init Whether this is the initial draw or not
16348 * @param? maintainSelection Whether the current selections are to be maintained over rebuild
16350 SearchPane.prototype.rebuildPane = function (last, dataIn, init, maintainSelection) {
16351 if (last === void 0) { last = false; }
16352 if (dataIn === void 0) { dataIn = null; }
16353 if (init === void 0) { init = null; }
16354 if (maintainSelection === void 0) { maintainSelection = false; }
16356 var selectedRows = [];
16357 this.s.serverSelect = [];
16359 // When rebuilding strip all of the HTML Elements out of the container and start from scratch
16360 if (this.s.dtPane !== undefined) {
16361 if (maintainSelection) {
16362 if (!this.s.dt.page.info().serverSide) {
16363 selectedRows = this.s.dtPane.rows({ selected: true }).data().toArray();
16366 this.s.serverSelect = this.s.dtPane.rows({ selected: true }).data().toArray();
16369 this.s.dtPane.clear().destroy();
16370 prevEl = $(this.dom.container).prev();
16372 this.s.dtPane = undefined;
16373 $.fn.dataTable.ext.search.push(this.s.searchFunction);
16375 this.dom.container.removeClass(this.classes.hidden);
16376 this.s.displayed = false;
16377 this._buildPane(!this.s.dt.page.info().serverSide ?
16379 this.s.serverSelect, last, dataIn, init, prevEl);
16383 * removes the pane from the page and sets the displayed property to false.
16385 SearchPane.prototype.removePane = function () {
16386 this.s.displayed = false;
16387 $(this.dom.container).hide();
16390 * Sets the cascadeRegen property of the pane. Accessible from above because as SearchPanes.ts deals with the rebuilds.
16391 * @param val the boolean value that the cascadeRegen property is to be set to
16393 SearchPane.prototype.setCascadeRegen = function (val) {
16394 this.s.cascadeRegen = val;
16397 * This function allows the clearing property to be assigned. This is used when implementing cascadePane.
16398 * In setting this to true for the clearing of the panes selection on the deselects it forces the pane to
16399 * repopulate from the entire dataset not just the displayed values.
16400 * @param val the boolean value which the clearing property is to be assigned
16402 SearchPane.prototype.setClear = function (val) {
16403 this.s.clearing = val;
16406 * Updates the values of all of the panes
16407 * @param draw whether this has been triggered by a draw event or not
16409 SearchPane.prototype.updatePane = function (draw) {
16410 if (draw === void 0) { draw = false; }
16411 this.s.updating = true;
16412 this._updateCommon(draw);
16413 this.s.updating = false;
16416 * Updates the panes if one of the options to do so has been set to true
16417 * rather than the filtered message when using viewTotal.
16419 SearchPane.prototype.updateTable = function () {
16420 var selectedRows = this.s.dtPane.rows({ selected: true }).data().toArray();
16421 this.selections = selectedRows;
16422 this._searchExtras();
16423 // If either of the options that effect how the panes are displayed are selected then update the Panes
16424 if (this.c.cascadePanes || this.c.viewTotal) {
16429 * Sets the listeners for the pane.
16431 * Having it in it's own function makes it easier to only set them once
16433 SearchPane.prototype._setListeners = function () {
16435 var rowData = this.s.rowData;
16437 // When an item is selected on the pane, add these to the array which holds selected items.
16438 // Custom search will perform.
16439 this.s.dtPane.on('select.dtsp', function () {
16440 if (_this.s.dt.page.info().serverSide && !_this.s.updating) {
16441 if (!_this.s.serverSelecting) {
16442 _this.s.serverSelect = _this.s.dtPane.rows({ selected: true }).data().toArray();
16443 _this.s.scrollTop = $(_this.s.dtPane.table().node()).parent()[0].scrollTop;
16444 _this.s.selectPresent = true;
16445 _this.s.dt.draw(false);
16450 $(_this.dom.clear).removeClass(_this.classes.dull);
16451 _this.s.selectPresent = true;
16452 if (!_this.s.updating) {
16453 _this._makeSelection();
16455 _this.s.selectPresent = false;
16458 // When an item is deselected on the pane, re add the currently selected items to the array
16459 // which holds selected items. Custom search will be performed.
16460 this.s.dtPane.on('deselect.dtsp', function () {
16461 t0 = setTimeout(function () {
16462 if (_this.s.dt.page.info().serverSide && !_this.s.updating) {
16463 if (!_this.s.serverSelecting) {
16464 _this.s.serverSelect = _this.s.dtPane.rows({ selected: true }).data().toArray();
16465 _this.s.deselect = true;
16466 _this.s.dt.draw(false);
16470 _this.s.deselect = true;
16471 if (_this.s.dtPane.rows({ selected: true }).data().toArray().length === 0) {
16472 $(_this.dom.clear).addClass(_this.classes.dull);
16474 _this._makeSelection();
16475 _this.s.deselect = false;
16476 _this.s.dt.state.save();
16480 // When saving the state store all of the selected rows for preselection next time around
16481 this.s.dt.on('stateSaveParams.dtsp', function (e, settings, data) {
16482 // If the data being passed in is empty then a state clear must have occured so clear the panes state as well
16483 if ($.isEmptyObject(data)) {
16484 _this.s.dtPane.state.clear();
16492 // Get all of the data needed for the state save from the pane
16493 if (_this.s.dtPane !== undefined) {
16494 selected = _this.s.dtPane.rows({ selected: true }).data().map(function (item) { return item.filter.toString(); }).toArray();
16495 searchTerm = $(_this.dom.searchBox).val();
16496 order = _this.s.dtPane.order();
16497 bins = rowData.binsOriginal;
16498 arrayFilter = rowData.arrayOriginal;
16500 if (data.searchPanes === undefined) {
16501 data.searchPanes = {};
16503 if (data.searchPanes.panes === undefined) {
16504 data.searchPanes.panes = [];
16506 // Add the panes data to the state object
16507 data.searchPanes.panes.push({
16508 arrayFilter: arrayFilter,
16512 searchTerm: searchTerm,
16516 this.s.dtPane.on('user-select.dtsp', function (e, _dt, type, cell, originalEvent) {
16517 originalEvent.stopPropagation();
16519 this.s.dtPane.on('draw.dtsp', function () {
16520 _this._adjustTopRow();
16522 // When the button to order by the name of the options is clicked then
16523 // change the ordering to whatever it isn't currently
16524 $(this.dom.nameButton).on('click.dtsp', function () {
16525 var currentOrder = _this.s.dtPane.order()[0][1];
16526 _this.s.dtPane.order([0, currentOrder === 'asc' ? 'desc' : 'asc']).draw();
16527 _this.s.dt.state.save();
16529 // When the button to order by the number of entries in the column is clicked then
16530 // change the ordering to whatever it isn't currently
16531 $(this.dom.countButton).on('click.dtsp', function () {
16532 var currentOrder = _this.s.dtPane.order()[0][1];
16533 _this.s.dtPane.order([1, currentOrder === 'asc' ? 'desc' : 'asc']).draw();
16534 _this.s.dt.state.save();
16536 // When the clear button is clicked reset the pane
16537 $(this.dom.clear).on('click.dtsp', function () {
16538 var searches = _this.dom.container.find('.' + _this.classes.search);
16539 searches.each(function () {
16540 // set the value of the search box to be an empty string and then search on that, effectively reseting
16542 $(this).trigger('input');
16546 // When the search button is clicked then draw focus to the search box
16547 $(this.dom.searchButton).on('click.dtsp', function () {
16548 $(_this.dom.searchBox).focus();
16550 // When a character is inputted into the searchbox search the pane for matching values.
16551 // Doing it this way means that no button has to be clicked to trigger a search, it is done asynchronously
16552 $(this.dom.searchBox).on('input.dtsp', function () {
16553 _this.s.dtPane.search($(_this.dom.searchBox).val()).draw();
16554 _this.s.dt.state.save();
16556 // Make sure to save the state once the pane has been built
16557 this.s.dt.state.save();
16561 * Takes in potentially undetected rows and adds them to the array if they are not yet featured
16562 * @param filter the filter value of the potential row
16563 * @param display the display value of the potential row
16564 * @param sort the sort value of the potential row
16565 * @param type the type value of the potential row
16566 * @param arrayFilter the array to be populated
16567 * @param bins the bins to be populated
16569 SearchPane.prototype._addOption = function (filter, display, sort, type, arrayFilter, bins) {
16570 // If the filter is an array then take a note of this, and add the elements to the arrayFilter array
16571 if (Array.isArray(filter) || filter instanceof DataTable.Api) {
16572 // Convert to an array so that we can work with it
16573 if (filter instanceof DataTable.Api) {
16574 filter = filter.toArray();
16575 display = display.toArray();
16577 if (filter.length === display.length) {
16578 for (var i = 0; i < filter.length; i++) {
16579 // If we haven't seen this row before add it
16580 if (!bins[filter[i]]) {
16581 bins[filter[i]] = 1;
16583 display: display[i],
16589 // Otherwise just increment the count
16593 this.s.rowData.totalOptions++;
16598 throw new Error('display and filter not the same length');
16601 // If the values were affected by othogonal data and are not an array then check if it is already present
16602 else if (typeof this.s.colOpts.orthogonal === 'string') {
16603 if (!bins[filter]) {
16611 this.s.rowData.totalOptions++;
16615 this.s.rowData.totalOptions++;
16619 // Otherwise we must just be adding an option
16630 * Adds a row to the panes table
16631 * @param display the value to be displayed to the user
16632 * @param filter the value to be filtered on when searchpanes is implemented
16633 * @param shown the number of rows in the table that are currently visible matching this criteria
16634 * @param total the total number of rows in the table that match this criteria
16635 * @param sort the value to be sorted in the pane table
16636 * @param type the value of which the type is to be derived from
16638 SearchPane.prototype._addRow = function (display, filter, shown, total, sort, type) {
16640 for (var _i = 0, _a = this.s.indexes; _i < _a.length; _i++) {
16641 var entry = _a[_i];
16642 if (entry.filter === filter) {
16643 index = entry.index;
16646 if (index === undefined) {
16647 index = this.s.indexes.length;
16648 this.s.indexes.push({ filter: filter, index: index });
16650 return this.s.dtPane.row.add({
16651 display: display !== '' ? display : this.c.emptyMessage,
16655 sort: sort !== '' ? sort : this.c.emptyMessage,
16661 * Adjusts the layout of the top row when the screen is resized
16663 SearchPane.prototype._adjustTopRow = function () {
16664 var subContainers = this.dom.container.find('.' + this.classes.subRowsContainer);
16665 var subRow1 = this.dom.container.find('.dtsp-subRow1');
16666 var subRow2 = this.dom.container.find('.dtsp-subRow2');
16667 var topRow = this.dom.container.find('.' + this.classes.topRow);
16668 // If the width is 0 then it is safe to assume that the pane has not yet been displayed.
16669 // Even if it has, if the width is 0 it won't make a difference if it has the narrow class or not
16670 if (($(subContainers[0]).width() < 252 || $(topRow[0]).width() < 252) && $(subContainers[0]).width() !== 0) {
16671 $(subContainers[0]).addClass(this.classes.narrow);
16672 $(subRow1[0]).addClass(this.classes.narrowSub).removeClass(this.classes.narrowSearch);
16673 $(subRow2[0]).addClass(this.classes.narrowSub).removeClass(this.classes.narrowButton);
16676 $(subContainers[0]).removeClass(this.classes.narrow);
16677 $(subRow1[0]).removeClass(this.classes.narrowSub).addClass(this.classes.narrowSearch);
16678 $(subRow2[0]).removeClass(this.classes.narrowSub).addClass(this.classes.narrowButton);
16682 * Method to construct the actual pane.
16683 * @param selectedRows previously selected Rows to be reselected
16684 * @last boolean to indicate whether this pane was the last one to have a selection made
16686 SearchPane.prototype._buildPane = function (selectedRows, last, dataIn, init, prevEl) {
16688 if (selectedRows === void 0) { selectedRows = []; }
16689 if (last === void 0) { last = false; }
16690 if (dataIn === void 0) { dataIn = null; }
16691 if (init === void 0) { init = null; }
16692 if (prevEl === void 0) { prevEl = null; }
16694 this.selections = [];
16695 var table = this.s.dt;
16696 var column = table.column(this.colExists ? this.s.index : 0);
16697 var colOpts = this.s.colOpts;
16698 var rowData = this.s.rowData;
16700 var countMessage = table.i18n('searchPanes.count', '{total}');
16701 var filteredMessage = table.i18n('searchPanes.countFiltered', '{shown} ({total})');
16702 var loadedFilter = table.state.loaded();
16703 // If the listeners have not been set yet then using the latest state may result in funny errors
16704 if (this.s.listSet) {
16705 loadedFilter = table.state();
16707 // If it is not a custom pane in place
16708 if (this.colExists) {
16710 if (loadedFilter && loadedFilter.searchPanes && loadedFilter.searchPanes.panes) {
16711 for (var i = 0; i < loadedFilter.searchPanes.panes.length; i++) {
16712 if (loadedFilter.searchPanes.panes[i].id === this.s.index) {
16718 // Perform checks that do not require populate pane to run
16719 if ((colOpts.show === false
16720 || (colOpts.show !== undefined && colOpts.show !== true)) &&
16722 this.dom.container.addClass(this.classes.hidden);
16723 this.s.displayed = false;
16726 else if (colOpts.show === true || idx !== -1) {
16727 this.s.displayed = true;
16729 if (!this.s.dt.page.info().serverSide) {
16730 // Only run populatePane if the data has not been collected yet
16731 if (rowData.arrayFilter.length === 0) {
16732 this._populatePane(last);
16733 this.s.rowData.totalOptions = 0;
16734 this._detailsPane();
16735 if (loadedFilter && loadedFilter.searchPanes && loadedFilter.searchPanes.panes) {
16736 // If the index is not found then no data has been added to the state for this pane,
16737 // which will only occur if it has previously failed to meet the criteria to be
16738 // displayed, therefore we can just hide it again here
16740 rowData.binsOriginal = loadedFilter.searchPanes.panes[idx].bins;
16741 rowData.arrayOriginal = loadedFilter.searchPanes.panes[idx].arrayFilter;
16744 this.dom.container.addClass(this.classes.hidden);
16745 this.s.displayed = false;
16750 rowData.arrayOriginal = rowData.arrayTotals;
16751 rowData.binsOriginal = rowData.binsTotal;
16754 var binLength = Object.keys(rowData.binsOriginal).length;
16755 var uniqueRatio = this._uniqueRatio(binLength, table.rows()[0].length);
16756 // Don't show the pane if there isn't enough variance in the data, or there is only 1 entry for that pane
16757 if (this.s.displayed === false && ((colOpts.show === undefined && colOpts.threshold === null ?
16758 uniqueRatio > this.c.threshold :
16759 uniqueRatio > colOpts.threshold)
16760 || (colOpts.show !== true && binLength <= 1))) {
16761 this.dom.container.addClass(this.classes.hidden);
16762 this.s.displayed = false;
16765 // If the option viewTotal is true then find
16766 // the total count for the whole table to display alongside the displayed count
16767 if (this.c.viewTotal && rowData.arrayTotals.length === 0) {
16768 this.s.rowData.totalOptions = 0;
16769 this._detailsPane();
16772 rowData.binsTotal = rowData.bins;
16774 this.dom.container.addClass(this.classes.show);
16775 this.s.displayed = true;
16777 else if (dataIn !== null) {
16778 if (dataIn.tableLength !== undefined) {
16779 this.s.tableLength = dataIn.tableLength;
16780 this.s.rowData.totalOptions = this.s.tableLength;
16782 else if (this.s.tableLength === null || table.rows()[0].length > this.s.tableLength) {
16783 this.s.tableLength = table.rows()[0].length;
16784 this.s.rowData.totalOptions = this.s.tableLength;
16786 var colTitle = table.column(this.s.index).dataSrc();
16787 if (dataIn[colTitle] !== undefined) {
16788 for (var _i = 0, _a = dataIn[colTitle]; _i < _a.length; _i++) {
16789 var dataPoint = _a[_i];
16790 this.s.rowData.arrayFilter.push({
16791 display: dataPoint.label,
16792 filter: dataPoint.value,
16793 sort: dataPoint.label,
16794 type: dataPoint.label
16796 this.s.rowData.bins[dataPoint.value] = this.c.viewTotal || this.c.cascadePanes ?
16799 this.s.rowData.binsTotal[dataPoint.value] = dataPoint.total;
16802 var binLength = Object.keys(rowData.binsTotal).length;
16803 var uniqueRatio = this._uniqueRatio(binLength, this.s.tableLength);
16804 // Don't show the pane if there isn't enough variance in the data, or there is only 1 entry for that pane
16805 if (this.s.displayed === false && ((colOpts.show === undefined && colOpts.threshold === null ?
16806 uniqueRatio > this.c.threshold :
16807 uniqueRatio > colOpts.threshold)
16808 || (colOpts.show !== true && binLength <= 1))) {
16809 this.dom.container.addClass(this.classes.hidden);
16810 this.s.displayed = false;
16813 this.s.displayed = true;
16817 this.s.displayed = true;
16819 // If the variance is accceptable then display the search pane
16820 this._displayPane();
16821 if (!this.s.listSet) {
16822 // Here, when the state is loaded if the data object on the original table is empty,
16823 // then a state.clear() must have occurred, so delete all of the panes tables state objects too.
16824 this.dom.dtP.on('stateLoadParams.dt', function (e, settings, data) {
16825 if ($.isEmptyObject(table.state.loaded())) {
16826 $.each(data, function (index, value) {
16827 delete data[index];
16832 // Add the container to the document in its original location
16833 if (prevEl !== null && $(this.dom.panesContainer).has(prevEl).length > 0) {
16834 $(this.dom.panesContainer).insertAfter(prevEl);
16837 $(this.dom.panesContainer).prepend(this.dom.container);
16839 // Declare the datatable for the pane
16840 var errMode = $.fn.dataTable.ext.errMode;
16841 $.fn.dataTable.ext.errMode = 'none';
16842 var haveScroller = DataTable.Scroller;
16843 this.s.dtPane = $(this.dom.dtP).DataTable($.extend(true, {
16846 className: 'dtsp-nameColumn',
16848 render: function (data, type, row) {
16849 if (type === 'sort') {
16852 else if (type === 'type') {
16856 (_this.s.filteringActive || _this.s.showFiltered) && _this.c.viewTotal
16857 ? message = filteredMessage.replace(/{total}/, row.total)
16858 : message = countMessage.replace(/{total}/, row.total);
16859 message = message.replace(/{shown}/, row.shown);
16860 while (message.indexOf('{total}') !== -1) {
16861 message = message.replace(/{total}/, row.total);
16863 while (message.indexOf('{shown}') !== -1) {
16864 message = message.replace(/{shown}/, row.shown);
16866 // We are displaying the count in the same columne as the name of the search option.
16867 // This is so that there is not need to call columns.adjust(), which in turn speeds up the code
16868 var displayMessage = '';
16869 var pill = '<span class="' + _this.classes.pill + '">' + message + '</span>';
16870 if (_this.c.hideCount || colOpts.hideCount) {
16873 if (!_this.c.dataLength) {
16874 displayMessage = '<span class="' + _this.classes.name + '">' + data + '</span>' + pill;
16876 else if (data !== null && data.length > _this.c.dataLength) {
16877 displayMessage = '<span title="' + data + '" class="' + _this.classes.name + '">'
16878 + data.substr(0, _this.c.dataLength) + '...'
16883 displayMessage = '<span class="' + _this.classes.name + '">' + data + '</span>' + pill;
16885 return displayMessage;
16888 // Accessing the private datatables property to set type based on the original table.
16889 // This is null if not defined by the user, meaning that automatic type detection would take place
16890 type: table.settings()[0].aoColumns[this.s.index] !== undefined ?
16891 table.settings()[0].aoColumns[this.s.index]._sManualType :
16895 className: 'dtsp-countColumn ' + this.classes.badgePill,
16904 paging: haveScroller ? true : false,
16906 scroller: haveScroller ? true : false,
16908 stateSave: table.settings()[0].oFeatures.bStateSave ? true : false
16909 }, this.c.dtOpts, colOpts !== undefined ? colOpts.dtOpts : {}, (this.customPaneSettings !== null && this.customPaneSettings.dtOpts !== undefined)
16910 ? this.customPaneSettings.dtOpts
16912 $(this.dom.dtP).addClass(this.classes.table);
16913 // This is hacky but necessary for when datatables is generating the column titles automatically
16914 $(this.dom.searchBox).attr('placeholder', colOpts.header !== undefined
16917 ? table.settings()[0].aoColumns[this.s.index].sTitle
16918 : this.customPaneSettings.header || 'Custom Pane');
16919 // As the pane table is not in the document yet we must initialise select ourselves
16920 $.fn.dataTable.select.init(this.s.dtPane);
16921 $.fn.dataTable.ext.errMode = errMode;
16922 // If it is not a custom pane
16923 if (this.colExists) {
16924 // On initialisation, do we need to set a filtering value from a
16925 // saved state or init option?
16926 var search = column.search();
16927 search = search ? search.substr(1, search.length - 2).split('|') : [];
16928 // Count the number of empty cells
16930 rowData.arrayFilter.forEach(function (element) {
16931 if (element.filter === '') {
16935 // Add all of the search options to the pane
16936 for (var i = 0, ien = rowData.arrayFilter.length; i < ien; i++) {
16937 var selected = false;
16938 for (var _b = 0, _c = this.s.serverSelect; _b < _c.length; _b++) {
16939 var option = _c[_b];
16940 if (option.filter === rowData.arrayFilter[i].filter) {
16944 if (this.s.dt.page.info().serverSide &&
16945 (!this.c.cascadePanes ||
16946 (this.c.cascadePanes && rowData.bins[rowData.arrayFilter[i].filter] !== 0) ||
16947 (this.c.cascadePanes && init !== null) ||
16949 var row = this._addRow(rowData.arrayFilter[i].display, rowData.arrayFilter[i].filter, init ?
16950 rowData.binsTotal[rowData.arrayFilter[i].filter] :
16951 rowData.bins[rowData.arrayFilter[i].filter], this.c.viewTotal || init
16952 ? String(rowData.binsTotal[rowData.arrayFilter[i].filter])
16953 : rowData.bins[rowData.arrayFilter[i].filter], rowData.arrayFilter[i].sort, rowData.arrayFilter[i].type);
16954 if (colOpts.preSelect !== undefined && colOpts.preSelect.indexOf(rowData.arrayFilter[i].filter) !== -1) {
16957 for (var _d = 0, _e = this.s.serverSelect; _d < _e.length; _d++) {
16958 var option = _e[_d];
16959 if (option.filter === rowData.arrayFilter[i].filter) {
16960 this.s.serverSelecting = true;
16962 this.s.serverSelecting = false;
16966 else if (!this.s.dt.page.info().serverSide &&
16967 rowData.arrayFilter[i] &&
16968 (rowData.bins[rowData.arrayFilter[i].filter] !== undefined || !this.c.cascadePanes)) {
16969 var row = this._addRow(rowData.arrayFilter[i].display, rowData.arrayFilter[i].filter, rowData.bins[rowData.arrayFilter[i].filter], rowData.binsTotal[rowData.arrayFilter[i].filter], rowData.arrayFilter[i].sort, rowData.arrayFilter[i].type);
16970 if (colOpts.preSelect !== undefined && colOpts.preSelect.indexOf(rowData.arrayFilter[i].filter) !== -1) {
16974 else if (!this.s.dt.page.info().serverSide) {
16975 this._addRow(this.c.emptyMessage, count_1, count_1, this.c.emptyMessage, this.c.emptyMessage, this.c.emptyMessage);
16979 // If there are custom options set or it is a custom pane then get them
16980 if (colOpts.options !== undefined ||
16981 (this.customPaneSettings !== null && this.customPaneSettings.options !== undefined)) {
16982 this._getComparisonRows();
16984 DataTable.select.init(this.s.dtPane);
16985 // Display the pane
16986 this.s.dtPane.draw();
16987 this._adjustTopRow();
16988 if (!this.s.listSet) {
16989 this._setListeners();
16990 this.s.listSet = true;
16992 for (var _f = 0, selectedRows_1 = selectedRows; _f < selectedRows_1.length; _f++) {
16993 var selection = selectedRows_1[_f];
16994 if (selection !== undefined) {
16995 for (var _g = 0, _h = this.s.dtPane.rows().indexes().toArray(); _g < _h.length; _g++) {
16997 if (this.s.dtPane.row(row).data() !== undefined && selection.filter === this.s.dtPane.row(row).data().filter) {
16998 // If this is happening when serverSide processing is happening then different behaviour is needed
16999 if (this.s.dt.page.info().serverSide) {
17000 this.s.serverSelecting = true;
17001 this.s.dtPane.row(row).select();
17002 this.s.serverSelecting = false;
17005 this.s.dtPane.row(row).select();
17012 // Reload the selection, searchbox entry and ordering from the previous state
17013 if (loadedFilter && loadedFilter.searchPanes && loadedFilter.searchPanes.panes) {
17014 if (!this.c.cascadePanes) {
17015 this._reloadSelect(loadedFilter);
17017 for (var _j = 0, _k = loadedFilter.searchPanes.panes; _j < _k.length; _j++) {
17019 if (pane.id === this.s.index) {
17020 $(this.dom.searchBox).val(pane.searchTerm);
17021 $(this.dom.searchBox).trigger('input');
17022 this.s.dtPane.order(pane.order).draw();
17026 // Make sure to save the state once the pane has been built
17027 this.s.dt.state.save();
17031 * Update the array which holds the display and filter values for the table
17033 SearchPane.prototype._detailsPane = function () {
17035 var table = this.s.dt;
17036 this.s.rowData.arrayTotals = [];
17037 this.s.rowData.binsTotal = {};
17038 var settings = this.s.dt.settings()[0];
17039 table.rows().every(function (rowIdx) {
17040 _this._populatePaneArray(rowIdx, _this.s.rowData.arrayTotals, settings, _this.s.rowData.binsTotal);
17044 * Appends all of the HTML elements to their relevant parent Elements
17046 SearchPane.prototype._displayPane = function () {
17047 var container = this.dom.container;
17048 var colOpts = this.s.colOpts;
17049 var layVal = parseInt(this.c.layout.split('-')[1], 10);
17050 // Empty everything to start again
17051 $(this.dom.topRow).empty();
17052 $(this.dom.dtP).empty();
17053 $(this.dom.topRow).addClass(this.classes.topRow);
17054 // If there are more than 3 columns defined then make there be a smaller gap between the panes
17056 $(this.dom.container).addClass(this.classes.smallGap);
17058 $(this.dom.topRow).addClass(this.classes.subRowsContainer);
17059 $(this.dom.upper).appendTo(this.dom.topRow);
17060 $(this.dom.lower).appendTo(this.dom.topRow);
17061 $(this.dom.searchCont).appendTo(this.dom.upper);
17062 $(this.dom.buttonGroup).appendTo(this.dom.lower);
17063 // If no selections have been made in the pane then disable the clear button
17064 if (this.c.dtOpts.searching === false ||
17065 (colOpts.dtOpts !== undefined &&
17066 colOpts.dtOpts.searching === false) ||
17067 (!this.c.controls || !colOpts.controls) ||
17068 (this.customPaneSettings !== null &&
17069 this.customPaneSettings.dtOpts !== undefined &&
17070 this.customPaneSettings.dtOpts.searching !== undefined &&
17071 !this.customPaneSettings.dtOpts.searching)) {
17072 $(this.dom.searchBox).attr('disabled', 'disabled')
17073 .removeClass(this.classes.paneInputButton)
17074 .addClass(this.classes.disabledButton);
17076 $(this.dom.searchBox).appendTo(this.dom.searchCont);
17077 // Create the contents of the searchCont div. Worth noting that this function will change when using semantic ui
17078 this._searchContSetup();
17079 // If the clear button is allowed to show then display it
17080 if (this.c.clear && this.c.controls && colOpts.controls) {
17081 $(this.dom.clear).appendTo(this.dom.buttonGroup);
17083 if (this.c.orderable && colOpts.orderable && this.c.controls && colOpts.controls) {
17084 $(this.dom.nameButton).appendTo(this.dom.buttonGroup);
17086 // If the count column is hidden then don't display the ordering button for it
17087 if (!this.c.hideCount &&
17088 !colOpts.hideCount &&
17089 this.c.orderable &&
17090 colOpts.orderable &&
17092 colOpts.controls) {
17093 $(this.dom.countButton).appendTo(this.dom.buttonGroup);
17095 $(this.dom.topRow).prependTo(this.dom.container);
17096 $(container).append(this.dom.dtP);
17097 $(container).show();
17100 * Gets the options for the row for the customPanes
17101 * @returns {object} The options for the row extended to include the options from the user.
17103 SearchPane.prototype._getBonusOptions = function () {
17104 // We need to reset the thresholds as if they have a value in colOpts then that value will be used
17105 var defaultMutator = {
17111 return $.extend(true, {}, SearchPane.defaults, defaultMutator, this.c !== undefined ? this.c : {});
17114 * Adds the custom options to the pane
17115 * @returns {Array} Returns the array of rows which have been added to the pane
17117 SearchPane.prototype._getComparisonRows = function () {
17118 var colOpts = this.s.colOpts;
17119 // Find the appropriate options depending on whether this is a pane for a specific column or a custom pane
17120 var options = colOpts.options !== undefined
17122 : this.customPaneSettings !== null && this.customPaneSettings.options !== undefined
17123 ? this.customPaneSettings.options
17125 if (options === undefined) {
17128 var tableVals = this.s.dt.rows({ search: 'applied' }).data().toArray();
17129 var appRows = this.s.dt.rows({ search: 'applied' });
17130 var tableValsTotal = this.s.dt.rows().data().toArray();
17131 var allRows = this.s.dt.rows();
17133 // Clear all of the other rows from the pane, only custom options are to be displayed when they are defined
17134 this.s.dtPane.clear();
17135 for (var _i = 0, options_1 = options; _i < options_1.length; _i++) {
17136 var comp = options_1[_i];
17137 // Initialise the object which is to be placed in the row
17138 var insert = comp.label !== '' ? comp.label : this.c.emptyMessage;
17139 var comparisonObj = {
17141 filter: typeof comp.value === 'function' ? comp.value : [],
17147 // If a custom function is in place
17148 if (typeof comp.value === 'function') {
17149 // Count the number of times the function evaluates to true for the data currently being displayed
17150 for (var tVal = 0; tVal < tableVals.length; tVal++) {
17151 if (comp.value.call(this.s.dt, tableVals[tVal], appRows[0][tVal])) {
17152 comparisonObj.shown++;
17155 // Count the number of times the function evaluates to true for the original data in the Table
17156 for (var i = 0; i < tableValsTotal.length; i++) {
17157 if (comp.value.call(this.s.dt, tableValsTotal[i], allRows[0][i])) {
17158 comparisonObj.total++;
17161 // Update the comparisonObj
17162 if (typeof comparisonObj.filter !== 'function') {
17163 comparisonObj.filter.push(comp.filter);
17166 // If cascadePanes is not active or if it is and the comparisonObj should be shown then add it to the pane
17167 if (!this.c.cascadePanes || (this.c.cascadePanes && comparisonObj.shown !== 0)) {
17168 rows.push(this._addRow(comparisonObj.display, comparisonObj.filter, comparisonObj.shown, comparisonObj.total, comparisonObj.sort, comparisonObj.type));
17174 * Gets the options for the row for the customPanes
17175 * @returns {object} The options for the row extended to include the options from the user.
17177 SearchPane.prototype._getOptions = function () {
17178 var table = this.s.dt;
17179 // We need to reset the thresholds as if they have a value in colOpts then that value will be used
17180 var defaultMutator = {
17186 return $.extend(true, {}, SearchPane.defaults, defaultMutator, table.settings()[0].aoColumns[this.s.index].searchPanes);
17189 * This method allows for changes to the panes and table to be made when a selection or a deselection occurs
17190 * @param select Denotes whether a selection has been made or not
17192 SearchPane.prototype._makeSelection = function () {
17193 this.updateTable();
17194 this.s.updating = true;
17196 this.s.updating = false;
17199 * Fill the array with the values that are currently being displayed in the table
17200 * @param last boolean to indicate whether this was the last pane a selection was made in
17202 SearchPane.prototype._populatePane = function (last) {
17203 if (last === void 0) { last = false; }
17204 var table = this.s.dt;
17205 this.s.rowData.arrayFilter = [];
17206 this.s.rowData.bins = {};
17207 var settings = this.s.dt.settings()[0];
17208 // If cascadePanes or viewTotal are active it is necessary to get the data which is currently
17209 // being displayed for their functionality. Also make sure that this was not the last pane to have a selection made
17210 if (!this.s.dt.page.info().serverSide) {
17211 var indexArray = (this.c.cascadePanes || this.c.viewTotal) && (!this.s.clearing && !last) ?
17212 table.rows({ search: 'applied' }).indexes() :
17213 table.rows().indexes();
17214 for (var _i = 0, _a = indexArray.toArray(); _i < _a.length; _i++) {
17215 var index = _a[_i];
17216 this._populatePaneArray(index, this.s.rowData.arrayFilter, settings);
17221 * Populates an array with all of the data for the table
17222 * @param rowIdx The current row index to be compared
17223 * @param arrayFilter The array that is to be populated with row Details
17224 * @param bins The bins object that is to be populated with the row counts
17226 SearchPane.prototype._populatePaneArray = function (rowIdx, arrayFilter, settings, bins) {
17227 if (bins === void 0) { bins = this.s.rowData.bins; }
17228 var colOpts = this.s.colOpts;
17229 // Retrieve the rendered data from the cell using the fnGetCellData function
17230 // rather than the cell().render API method for optimisation
17231 if (typeof colOpts.orthogonal === 'string') {
17232 var rendered = settings.oApi._fnGetCellData(settings, rowIdx, this.s.index, colOpts.orthogonal);
17233 this.s.rowData.filterMap.set(rowIdx, rendered);
17234 this._addOption(rendered, rendered, rendered, rendered, arrayFilter, bins);
17237 var filter = settings.oApi._fnGetCellData(settings, rowIdx, this.s.index, colOpts.orthogonal.search);
17238 this.s.rowData.filterMap.set(rowIdx, filter);
17239 if (!bins[filter]) {
17241 this._addOption(filter, settings.oApi._fnGetCellData(settings, rowIdx, this.s.index, colOpts.orthogonal.display), settings.oApi._fnGetCellData(settings, rowIdx, this.s.index, colOpts.orthogonal.sort), settings.oApi._fnGetCellData(settings, rowIdx, this.s.index, colOpts.orthogonal.type), arrayFilter, bins);
17242 this.s.rowData.totalOptions++;
17246 this.s.rowData.totalOptions++;
17252 * Reloads all of the previous selects into the panes
17253 * @param loadedFilter The loaded filters from a previous state
17255 SearchPane.prototype._reloadSelect = function (loadedFilter) {
17256 // If the state was not saved don't selected any
17257 if (loadedFilter === undefined) {
17261 // For each pane, check that the loadedFilter list exists and is not null,
17262 // find the id of each search item and set it to be selected.
17263 for (var i = 0; i < loadedFilter.searchPanes.panes.length; i++) {
17264 if (loadedFilter.searchPanes.panes[i].id === this.s.index) {
17269 if (idx !== undefined) {
17270 var table = this.s.dtPane;
17271 var rows = table.rows({ order: 'index' }).data().map(function (item) { return item.filter !== null ?
17272 item.filter.toString() :
17273 null; }).toArray();
17274 for (var _i = 0, _a = loadedFilter.searchPanes.panes[idx].selected; _i < _a.length; _i++) {
17275 var filter = _a[_i];
17277 if (filter !== null) {
17278 id = rows.indexOf(filter.toString());
17281 table.row(id).select();
17282 this.s.dt.state.save();
17288 * This method decides whether a row should contribute to the pane or not
17289 * @param filter the value that the row is to be filtered on
17290 * @param dataIndex the row index
17292 SearchPane.prototype._search = function (filter, dataIndex) {
17293 var colOpts = this.s.colOpts;
17294 var table = this.s.dt;
17295 // For each item selected in the pane, check if it is available in the cell
17296 for (var _i = 0, _a = this.selections; _i < _a.length; _i++) {
17297 var colSelect = _a[_i];
17298 // if the filter is an array then is the column present in it
17299 if (Array.isArray(filter)) {
17300 if (filter.indexOf(colSelect.filter) !== -1) {
17304 // if the filter is a function then does it meet the criteria of that function or not
17305 else if (typeof colSelect.filter === 'function') {
17306 if (colSelect.filter.call(table, table.row(dataIndex).data(), dataIndex)) {
17307 if (colOpts.combiner === 'or') {
17311 // If the combiner is an "and" then we need to check against all possible selections
17312 // so if it fails here then the and is not met and return false
17313 else if (colOpts.combiner === 'and') {
17317 // otherwise if the two filter values are equal then return true
17318 else if (filter === colSelect.filter) {
17322 // If the combiner is an and then we need to check against all possible selections
17323 // so return true here if so because it would have returned false earlier if it had failed
17324 if (colOpts.combiner === 'and') {
17327 // Otherwise it hasn't matched with anything by this point so it must be false
17333 * Creates the contents of the searchCont div
17335 * NOTE This is overridden when semantic ui styling in order to integrate the search button into the text box.
17337 SearchPane.prototype._searchContSetup = function () {
17338 if (this.c.controls && this.s.colOpts.controls) {
17339 $(this.dom.searchButton).appendTo(this.dom.searchLabelCont);
17341 if (!(this.c.dtOpts.searching === false ||
17342 this.s.colOpts.dtOpts.searching === false ||
17343 (this.customPaneSettings !== null &&
17344 this.customPaneSettings.dtOpts !== undefined &&
17345 this.customPaneSettings.dtOpts.searching !== undefined &&
17346 !this.customPaneSettings.dtOpts.searching))) {
17347 $(this.dom.searchLabelCont).appendTo(this.dom.searchCont);
17351 * Adds outline to the pane when a selection has been made
17353 SearchPane.prototype._searchExtras = function () {
17354 var updating = this.s.updating;
17355 this.s.updating = true;
17356 var filters = this.s.dtPane.rows({ selected: true }).data().pluck('filter').toArray();
17357 var nullIndex = filters.indexOf(this.c.emptyMessage);
17358 var container = $(this.s.dtPane.table().container());
17359 // If null index is found then search for empty cells as a filter.
17360 if (nullIndex > -1) {
17361 filters[nullIndex] = '';
17363 // If a filter has been applied then outline the respective pane, remove it when it no longer is.
17364 if (filters.length > 0) {
17365 container.addClass(this.classes.selected);
17367 else if (filters.length === 0) {
17368 container.removeClass(this.classes.selected);
17370 this.s.updating = updating;
17373 * Finds the ratio of the number of different options in the table to the number of rows
17374 * @param bins the number of different options in the table
17375 * @param rowCount the total number of rows in the table
17376 * @returns {number} returns the ratio
17378 SearchPane.prototype._uniqueRatio = function (bins, rowCount) {
17379 if (rowCount > 0 &&
17380 ((this.s.rowData.totalOptions > 0 && !this.s.dt.page.info().serverSide) ||
17381 (this.s.dt.page.info().serverSide && this.s.tableLength > 0))) {
17382 return bins / this.s.rowData.totalOptions;
17389 * updates the options within the pane
17390 * @param draw a flag to define whether this has been called due to a draw event or not
17392 SearchPane.prototype._updateCommon = function (draw) {
17393 if (draw === void 0) { draw = false; }
17394 // Update the panes if doing a deselect. if doing a select then
17395 // update all of the panes except for the one causing the change
17396 if (!this.s.dt.page.info().serverSide &&
17397 this.s.dtPane !== undefined &&
17398 (!this.s.filteringActive || this.c.cascadePanes || draw === true) &&
17399 (this.c.cascadePanes !== true || this.s.selectPresent !== true) && (!this.s.lastSelect || !this.s.lastCascade)) {
17400 var colOpts = this.s.colOpts;
17401 var selected = this.s.dtPane.rows({ selected: true }).data().toArray();
17402 var scrollTop = $(this.s.dtPane.table().node()).parent()[0].scrollTop;
17403 var rowData = this.s.rowData;
17404 // Clear the pane in preparation for adding the updated search options
17405 this.s.dtPane.clear();
17406 // If it is not a custom pane
17407 if (this.colExists) {
17408 // Only run populatePane if the data has not been collected yet
17409 if (rowData.arrayFilter.length === 0) {
17410 this._populatePane();
17412 // If cascadePanes is active and the table has returned to its default state then
17413 // there is a need to update certain parts ofthe rowData.
17414 else if (this.c.cascadePanes
17415 && this.s.dt.rows().data().toArray().length === this.s.dt.rows({ search: 'applied' }).data().toArray().length) {
17416 rowData.arrayFilter = rowData.arrayOriginal;
17417 rowData.bins = rowData.binsOriginal;
17419 // Otherwise if viewTotal or cascadePanes is active then the data from the table must be read.
17420 else if (this.c.viewTotal || this.c.cascadePanes) {
17421 this._populatePane();
17423 // If the viewTotal option is selected then find the totals for the table
17424 if (this.c.viewTotal) {
17425 this._detailsPane();
17428 rowData.binsTotal = rowData.bins;
17430 if (this.c.viewTotal && !this.c.cascadePanes) {
17431 rowData.arrayFilter = rowData.arrayTotals;
17433 var _loop_1 = function (dataP) {
17434 // If both view Total and cascadePanes have been selected and the count of the row is not 0 then add it to pane
17435 // Do this also if the viewTotal option has been selected and cascadePanes has not
17436 if (dataP && ((rowData.bins[dataP.filter] !== undefined && rowData.bins[dataP.filter] !== 0 && this_1.c.cascadePanes)
17437 || !this_1.c.cascadePanes
17438 || this_1.s.clearing)) {
17439 var row = this_1._addRow(dataP.display, dataP.filter, !this_1.c.viewTotal
17440 ? rowData.bins[dataP.filter]
17441 : rowData.bins[dataP.filter] !== undefined
17442 ? rowData.bins[dataP.filter]
17443 : 0, this_1.c.viewTotal
17444 ? String(rowData.binsTotal[dataP.filter])
17445 : rowData.bins[dataP.filter], dataP.sort, dataP.type);
17446 // Find out if the filter was selected in the previous search, if so select it and remove from array.
17447 var selectIndex = selected.findIndex(function (element) {
17448 return element.filter === dataP.filter;
17450 if (selectIndex !== -1) {
17452 selected.splice(selectIndex, 1);
17457 for (var _i = 0, _a = rowData.arrayFilter; _i < _a.length; _i++) {
17458 var dataP = _a[_i];
17462 if ((colOpts.searchPanes !== undefined && colOpts.searchPanes.options !== undefined) ||
17463 colOpts.options !== undefined ||
17464 (this.customPaneSettings !== null && this.customPaneSettings.options !== undefined)) {
17465 var rows = this._getComparisonRows();
17466 var _loop_2 = function (row) {
17467 var selectIndex = selected.findIndex(function (element) {
17468 if (element.display === row.data().display) {
17472 if (selectIndex !== -1) {
17474 selected.splice(selectIndex, 1);
17477 for (var _b = 0, rows_1 = rows; _b < rows_1.length; _b++) {
17478 var row = rows_1[_b];
17482 // Add search options which were previously selected but whos results are no
17483 // longer present in the resulting data set.
17484 for (var _c = 0, selected_1 = selected; _c < selected_1.length; _c++) {
17485 var selectedEl = selected_1[_c];
17486 var row = this._addRow(selectedEl.display, selectedEl.filter, 0, this.c.viewTotal
17488 : 0, selectedEl.filter, selectedEl.filter);
17489 this.s.updating = true;
17491 this.s.updating = false;
17493 this.s.dtPane.draw();
17494 this.s.dtPane.table().node().parentNode.scrollTop = scrollTop;
17497 SearchPane.version = '1.1.0';
17498 SearchPane.classes = {
17499 buttonGroup: 'dtsp-buttonGroup',
17500 buttonSub: 'dtsp-buttonSub',
17501 clear: 'dtsp-clear',
17502 clearAll: 'dtsp-clearAll',
17503 clearButton: 'clearButton',
17504 container: 'dtsp-searchPane',
17505 countButton: 'dtsp-countButton',
17506 disabledButton: 'dtsp-disabledButton',
17508 hidden: 'dtsp-hidden',
17512 nameButton: 'dtsp-nameButton',
17513 narrow: 'dtsp-narrow',
17514 paneButton: 'dtsp-paneButton',
17515 paneInputButton: 'dtsp-paneInputButton',
17517 search: 'dtsp-search',
17518 searchCont: 'dtsp-searchCont',
17519 searchIcon: 'dtsp-searchIcon',
17520 searchLabelCont: 'dtsp-searchButtonCont',
17521 selected: 'dtsp-selected',
17522 smallGap: 'dtsp-smallGap',
17523 subRow1: 'dtsp-subRow1',
17524 subRow2: 'dtsp-subRow2',
17525 subRowsContainer: 'dtsp-subRowsContainer',
17526 title: 'dtsp-title',
17527 topRow: 'dtsp-topRow'
17529 // Define SearchPanes default options
17530 SearchPane.defaults = {
17531 cascadePanes: false,
17535 container: function (dt) {
17536 return dt.table().container();
17540 emptyMessage: '<i>No Data</i>',
17542 layout: 'columns-3',
17546 display: 'display',
17563 function setJQuery$1(jq) {
17565 DataTable$1 = jq.fn.dataTable;
17567 var SearchPanes = /** @class */ (function () {
17568 function SearchPanes(paneSettings, opts, fromInit) {
17570 if (fromInit === void 0) { fromInit = false; }
17571 this.regenerating = false;
17572 // Check that the required version of DataTables is included
17573 if (!DataTable$1 || !DataTable$1.versionCheck || !DataTable$1.versionCheck('1.10.0')) {
17574 throw new Error('SearchPane requires DataTables 1.10 or newer');
17576 // Check that Select is included
17577 if (!DataTable$1.select) {
17578 throw new Error('SearchPane requires Select');
17580 var table = new DataTable$1.Api(paneSettings);
17581 this.classes = $$1.extend(true, {}, SearchPanes.classes);
17582 // Get options from user
17583 this.c = $$1.extend(true, {}, SearchPanes.defaults, opts);
17584 // Add extra elements to DOM object including clear
17586 clearAll: $$1('<button type="button">Clear All</button>').addClass(this.classes.clearAll),
17587 container: $$1('<div/>').addClass(this.classes.panes).text(table.i18n('searchPanes.loadMessage', 'Loading Search Panes...')),
17588 emptyMessage: $$1('<div/>').addClass(this.classes.emptyMessage),
17589 options: $$1('<div/>').addClass(this.classes.container),
17590 panes: $$1('<div/>').addClass(this.classes.container),
17591 title: $$1('<div/>').addClass(this.classes.title),
17592 titleRow: $$1('<div/>').addClass(this.classes.titleRow),
17593 wrapper: $$1('<div/>')
17604 if (table.settings()[0]._searchPanes !== undefined) {
17607 // We are using the xhr event to rebuild the panes if required due to viewTotal being enabled
17608 // If viewTotal is not enabled then we simply update the data from the server
17609 table.on('xhr', function (e, settings, json, xhr) {
17610 if (json.searchPanes && json.searchPanes.options) {
17611 _this.s.serverData = json.searchPanes.options;
17612 _this.s.serverData.tableLength = json.recordsTotal;
17613 if (_this.c.viewTotal || _this.c.cascadePanes) {
17614 _this._serverTotals();
17618 table.settings()[0]._searchPanes = this;
17619 this.dom.clearAll.text(table.i18n('searchPanes.clearMessage', 'Clear All'));
17621 if (this.s.dt.settings()[0]._bInitComplete || fromInit) {
17622 this._paneDeclare(table, paneSettings, opts);
17625 table.one('preInit.dt', function (settings) {
17626 _this._paneDeclare(table, paneSettings, opts);
17631 * Clear the selections of all of the panes
17633 SearchPanes.prototype.clearSelections = function () {
17634 // Load in all of the searchBoxes in the documents
17635 var searches = this.dom.container.find(this.classes.search);
17636 // For each searchBox set the input text to be empty and then trigger
17637 // an input on them so that they no longer filter the panes
17638 searches.each(function () {
17640 $$1(this).trigger('input');
17642 var returnArray = [];
17643 // For every pane, clear the selections in the pane
17644 for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
17646 if (pane.s.dtPane !== undefined) {
17647 returnArray.push(pane.clearPane());
17651 return returnArray;
17654 * returns the container node for the searchPanes
17656 SearchPanes.prototype.getNode = function () {
17657 return this.dom.container;
17660 * rebuilds all of the panes
17662 SearchPanes.prototype.rebuild = function (targetIdx, maintainSelection) {
17663 if (targetIdx === void 0) { targetIdx = false; }
17664 if (maintainSelection === void 0) { maintainSelection = false; }
17665 $$1(this.dom.emptyMessage).remove();
17666 // As a rebuild from scratch is required, empty the searchpanes container.
17667 var returnArray = [];
17668 // Rebuild each pane individually, if a specific pane has been selected then only rebuild that one
17669 $$1(this.dom.panes).empty();
17670 for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
17672 if (targetIdx !== false && pane.s.index !== targetIdx) {
17673 $$1(this.dom.panes).append(pane.dom.container);
17678 // Pass a boolean to say whether this is the last choice made for maintaining selections when rebuilding
17679 pane.rebuildPane(this.s.selectionList[this.s.selectionList.length - 1] !== undefined ?
17680 pane.s.index === this.s.selectionList[this.s.selectionList.length - 1].index :
17681 false, this.s.dt.page.info().serverSide ?
17682 this.s.serverData :
17683 undefined, null, maintainSelection));
17684 $$1(this.dom.panes).append(pane.dom.container);
17686 if (this.c.cascadePanes || this.c.viewTotal) {
17687 this.redrawPanes(true);
17690 this._updateSelection();
17692 // Attach panes, clear buttons, and title bar to the document
17693 this._updateFilterCount();
17694 this._attachPaneContainer();
17696 // If a single pane has been rebuilt then return only that pane
17697 if (returnArray.length === 1) {
17698 return returnArray[0];
17700 // Otherwise return all of the panes that have been rebuilt
17702 return returnArray;
17706 * Redraws all of the panes
17708 SearchPanes.prototype.redrawPanes = function (rebuild) {
17709 if (rebuild === void 0) { rebuild = false; }
17710 var table = this.s.dt;
17711 // Only do this if the redraw isn't being triggered by the panes updating themselves
17712 if (!this.s.updating && !this.s.dt.page.info().serverSide) {
17713 var filterActive = true;
17714 var filterPane = this.s.filterPane;
17715 // If the number of rows currently visible is equal to the number of rows in the table
17716 // then there can't be any filtering taking place
17717 if (table.rows({ search: 'applied' }).data().toArray().length === table.rows().data().toArray().length) {
17718 filterActive = false;
17720 // Otherwise if viewTotal is active then it is necessary to determine which panes a select is present in.
17721 // If there is only one pane with a selection present then it should not show the filtered message as
17722 // more selections may be made in that pane.
17723 else if (this.c.viewTotal) {
17724 for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
17726 if (pane.s.dtPane !== undefined) {
17727 var selectLength = pane.s.dtPane.rows({ selected: true }).data().toArray().length;
17728 if (selectLength === 0) {
17729 for (var _b = 0, _c = this.s.selectionList; _b < _c.length; _b++) {
17730 var selection = _c[_b];
17731 if (selection.index === pane.s.index && selection.rows.length !== 0) {
17732 selectLength = selection.rows.length;
17736 // If filterPane === -1 then a pane with a selection has not been found yet, so set filterPane to that panes index
17737 if (selectLength > 0 && filterPane === -1) {
17738 filterPane = pane.s.index;
17740 // Then if another pane is found with a selection then set filterPane to null to
17741 // show that multiple panes have selections present
17742 else if (selectLength > 0) {
17748 var deselectIdx = void 0;
17749 var newSelectionList = [];
17750 // Don't run this if it is due to the panes regenerating
17751 if (!this.regenerating) {
17752 for (var _d = 0, _e = this.s.panes; _d < _e.length; _d++) {
17754 // Identify the pane where a selection or deselection has been made and add it to the list.
17755 if (pane.s.selectPresent) {
17756 this.s.selectionList.push({ index: pane.s.index, rows: pane.s.dtPane.rows({ selected: true }).data().toArray(), protect: false });
17757 table.state.save();
17760 else if (pane.s.deselect) {
17761 deselectIdx = pane.s.index;
17762 var selectedData = pane.s.dtPane.rows({ selected: true }).data().toArray();
17763 if (selectedData.length > 0) {
17764 this.s.selectionList.push({ index: pane.s.index, rows: selectedData, protect: true });
17768 if (this.s.selectionList.length > 0) {
17769 var last = this.s.selectionList[this.s.selectionList.length - 1].index;
17770 for (var _f = 0, _g = this.s.panes; _f < _g.length; _f++) {
17772 pane.s.lastSelect = (pane.s.index === last);
17775 // Remove selections from the list from the pane where a deselect has taken place
17776 for (var i = 0; i < this.s.selectionList.length; i++) {
17777 if (this.s.selectionList[i].index !== deselectIdx || this.s.selectionList[i].protect === true) {
17778 var further = false;
17779 // Find out if this selection is the last one in the list for that pane
17780 for (var j = i + 1; j < this.s.selectionList.length; j++) {
17781 if (this.s.selectionList[j].index === this.s.selectionList[i].index) {
17785 // If there are no selections for this pane in the list then just push this one
17787 newSelectionList.push(this.s.selectionList[i]);
17788 this.s.selectionList[i].protect = false;
17793 if (newSelectionList.length === 1) {
17794 solePane = newSelectionList[0].index;
17796 // Update all of the panes to reflect the current state of the filters
17797 for (var _h = 0, _j = this.s.panes; _h < _j.length; _h++) {
17799 if (pane.s.dtPane !== undefined) {
17800 var tempFilter = true;
17801 pane.s.filteringActive = true;
17802 if ((filterPane !== -1 && filterPane !== null && filterPane === pane.s.index) ||
17803 filterActive === false ||
17804 pane.s.index === solePane) {
17805 tempFilter = false;
17806 pane.s.filteringActive = false;
17808 pane.updatePane(!tempFilter ? false : filterActive);
17811 // Update the label that shows how many filters are in place
17812 this._updateFilterCount();
17813 // If the length of the selections are different then some of them have been removed and a deselect has occured
17814 if (newSelectionList.length > 0 && (newSelectionList.length < this.s.selectionList.length || rebuild)) {
17815 this._cascadeRegen(newSelectionList);
17816 var last = newSelectionList[newSelectionList.length - 1].index;
17817 for (var _k = 0, _l = this.s.panes; _k < _l.length; _k++) {
17819 pane.s.lastSelect = (pane.s.index === last);
17822 else if (newSelectionList.length > 0) {
17823 // Update all of the other panes as you would just making a normal selection
17824 for (var _m = 0, _o = this.s.panes; _m < _o.length; _m++) {
17825 var paneUpdate = _o[_m];
17826 if (paneUpdate.s.dtPane !== undefined) {
17827 var tempFilter = true;
17828 paneUpdate.s.filteringActive = true;
17829 if ((filterPane !== -1 && filterPane !== null && filterPane === paneUpdate.s.index) || filterActive === false) {
17830 tempFilter = false;
17831 paneUpdate.s.filteringActive = false;
17833 paneUpdate.updatePane(!tempFilter ? tempFilter : filterActive);
17840 if (newSelectionList.length === 1) {
17841 solePane = newSelectionList[0].index;
17843 for (var _p = 0, _q = this.s.panes; _p < _q.length; _p++) {
17845 if (pane.s.dtPane !== undefined) {
17846 var tempFilter = true;
17847 pane.s.filteringActive = true;
17848 if ((filterPane !== -1 && filterPane !== null && filterPane === pane.s.index) ||
17849 filterActive === false ||
17850 pane.s.index === solePane) {
17851 tempFilter = false;
17852 pane.s.filteringActive = false;
17854 pane.updatePane(!tempFilter ? tempFilter : filterActive);
17857 // Update the label that shows how many filters are in place
17858 this._updateFilterCount();
17860 if (!filterActive) {
17861 this.s.selectionList = [];
17866 * Attach the panes, buttons and title to the document
17868 SearchPanes.prototype._attach = function () {
17870 $$1(this.dom.container).removeClass(this.classes.hide);
17871 $$1(this.dom.titleRow).removeClass(this.classes.hide);
17872 $$1(this.dom.titleRow).remove();
17873 $$1(this.dom.title).appendTo(this.dom.titleRow);
17874 // If the clear button is permitted attach it
17875 if (this.c.clear) {
17876 $$1(this.dom.clearAll).appendTo(this.dom.titleRow);
17877 $$1(this.dom.clearAll).on('click.dtsps', function () {
17878 _this.clearSelections();
17881 $$1(this.dom.titleRow).appendTo(this.dom.container);
17882 // Attach the container for each individual pane to the overall container
17883 for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
17885 $$1(pane.dom.container).appendTo(this.dom.panes);
17887 // Attach everything to the document
17888 $$1(this.dom.panes).appendTo(this.dom.container);
17889 if ($$1('div.' + this.classes.container).length === 0) {
17890 $$1(this.dom.container).prependTo(this.s.dt);
17892 return this.dom.container;
17895 * Attach the top row containing the filter count and clear all button
17897 SearchPanes.prototype._attachExtras = function () {
17898 $$1(this.dom.container).removeClass(this.classes.hide);
17899 $$1(this.dom.titleRow).removeClass(this.classes.hide);
17900 $$1(this.dom.titleRow).remove();
17901 $$1(this.dom.title).appendTo(this.dom.titleRow);
17902 // If the clear button is permitted attach it
17903 if (this.c.clear) {
17904 $$1(this.dom.clearAll).appendTo(this.dom.titleRow);
17906 $$1(this.dom.titleRow).appendTo(this.dom.container);
17907 return this.dom.container;
17910 * If there are no panes to display then this method is called to either
17911 * display a message in their place or hide them completely.
17913 SearchPanes.prototype._attachMessage = function () {
17914 // Create a message to display on the screen
17917 message = this.s.dt.i18n('searchPanes.emptyPanes', 'No SearchPanes');
17922 // If the message is an empty string then searchPanes.emptyPanes is undefined,
17923 // therefore the pane container should be removed from the display
17924 if (message === null) {
17925 $$1(this.dom.container).addClass(this.classes.hide);
17926 $$1(this.dom.titleRow).removeClass(this.classes.hide);
17930 $$1(this.dom.container).removeClass(this.classes.hide);
17931 $$1(this.dom.titleRow).addClass(this.classes.hide);
17933 // Otherwise display the message
17934 $$1(this.dom.emptyMessage).text(message);
17935 this.dom.emptyMessage.appendTo(this.dom.container);
17936 return this.dom.container;
17939 * Attaches the panes to the document and displays a message or hides if there are none
17941 SearchPanes.prototype._attachPaneContainer = function () {
17942 // If a pane is to be displayed then attach the normal pane output
17943 for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
17945 if (pane.s.displayed === true) {
17946 return this._attach();
17949 // Otherwise attach the custom message or remove the container from the display
17950 return this._attachMessage();
17953 * Prepares the panes for selections to be made when cascade is active and a deselect has occured
17954 * @param newSelectionList the list of selections which are to be made
17956 SearchPanes.prototype._cascadeRegen = function (newSelectionList) {
17957 // Set this to true so that the actions taken do not cause this to run until it is finished
17958 this.regenerating = true;
17959 // If only one pane has been selected then take note of its index
17961 if (newSelectionList.length === 1) {
17962 solePane = newSelectionList[0].index;
17964 // Let the pane know that a cascadeRegen is taking place to avoid unexpected behaviour
17965 // and clear all of the previous selections in the pane
17966 for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
17968 pane.setCascadeRegen(true);
17969 pane.setClear(true);
17970 // If this is the same as the pane with the only selection then pass it as a parameter into clearPane
17971 if ((pane.s.dtPane !== undefined && pane.s.index === solePane) || pane.s.dtPane !== undefined) {
17974 pane.setClear(false);
17976 // Remake Selections
17977 this._makeCascadeSelections(newSelectionList);
17978 // Set the selection list property to be the list without the selections from the deselect pane
17979 this.s.selectionList = newSelectionList;
17980 // The regeneration of selections is over so set it back to false
17981 for (var _b = 0, _c = this.s.panes; _b < _c.length; _b++) {
17983 pane.setCascadeRegen(false);
17985 this.regenerating = false;
17988 * Attaches the message to the document but does not add any panes
17990 SearchPanes.prototype._checkMessage = function () {
17991 // If a pane is to be displayed then attach the normal pane output
17992 for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
17994 if (pane.s.displayed === true) {
17998 // Otherwise attach the custom message or remove the container from the display
17999 return this._attachMessage();
18002 * Gets the selection list from the previous state and stores it in the selectionList Property
18004 SearchPanes.prototype._getState = function () {
18005 var loadedFilter = this.s.dt.state.loaded();
18006 if (loadedFilter && loadedFilter.searchPanes && loadedFilter.searchPanes.selectionList !== undefined) {
18007 this.s.selectionList = loadedFilter.searchPanes.selectionList;
18011 * Makes all of the selections when cascade is active
18012 * @param newSelectionList the list of selections to be made, in the order they were originally selected
18014 SearchPanes.prototype._makeCascadeSelections = function (newSelectionList) {
18015 // make selections in the order they were made previously, excluding those from the pane where a deselect was made
18016 for (var i = 0; i < newSelectionList.length; i++) {
18017 var _loop_1 = function (pane) {
18018 if (pane.s.index === newSelectionList[i].index && pane.s.dtPane !== undefined) {
18019 // When regenerating the cascade selections we need this flag so that the panes are only ignored if it
18020 // is the last selection and the pane for that selection
18021 if (i === newSelectionList.length - 1) {
18022 pane.s.lastCascade = true;
18024 // if there are any selections currently in the pane then deselect them as we are about to make our new selections
18025 if (pane.s.dtPane.rows({ selected: true }).data().toArray().length > 0 && pane.s.dtPane !== undefined) {
18026 pane.setClear(true);
18028 pane.setClear(false);
18030 var _loop_2 = function (row) {
18031 pane.s.dtPane.rows().every(function (rowIdx) {
18032 if (pane.s.dtPane.row(rowIdx).data() !== undefined &&
18033 row !== undefined &&
18034 pane.s.dtPane.row(rowIdx).data().filter === row.filter) {
18035 pane.s.dtPane.row(rowIdx).select();
18039 // select every row in the pane that was selected previously
18040 for (var _i = 0, _a = newSelectionList[i].rows; _i < _a.length; _i++) {
18044 // Update the label that shows how many filters are in place
18045 this_1._updateFilterCount();
18046 pane.s.lastCascade = false;
18050 // As the selections may have been made across the panes in a different order to the pane index we must identify
18051 // which pane has the index of the selection. This is also important for colreorder etc
18052 for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
18057 // Make sure that the state is saved after all of these selections
18058 this.s.dt.state.save();
18061 * Declares the instances of individual searchpanes dependant on the number of columns.
18062 * It is necessary to run this once preInit has completed otherwise no panes will be
18063 * created as the column count will be 0.
18064 * @param table the DataTable api for the parent table
18065 * @param paneSettings the settings passed into the constructor
18066 * @param opts the options passed into the constructor
18068 SearchPanes.prototype._paneDeclare = function (table, paneSettings, opts) {
18072 .columns(this.c.columns.length > 0 ? this.c.columns : undefined)
18074 .each(function (idx) {
18075 _this.s.panes.push(new SearchPane(paneSettings, opts, idx, _this.c.layout, _this.dom.panes));
18077 // If there is any extra custom panes defined then create panes for them too
18078 var rowLength = table.columns().eq(0).toArray().length;
18079 var paneLength = this.c.panes.length;
18080 for (var i = 0; i < paneLength; i++) {
18081 var id = rowLength + i;
18082 this.s.panes.push(new SearchPane(paneSettings, opts, id, this.c.layout, this.dom.panes, this.c.panes[i]));
18084 // If a custom ordering is being used
18085 if (this.c.order.length > 0) {
18086 // Make a new Array of panes based upon the order
18087 var newPanes = this.c.order.map(function (name, index, values) {
18088 return _this._findPane(name);
18090 // Remove the old panes from the dom
18091 this.dom.panes.empty();
18092 this.s.panes = newPanes;
18093 // Append the panes in the correct order
18094 for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
18096 this.dom.panes.append(pane.dom.container);
18099 // If this internal property is true then the DataTable has been initialised already
18100 if (this.s.dt.settings()[0]._bInitComplete) {
18101 this._paneStartup(table);
18104 // Otherwise add the paneStartup function to the list of functions that are to be run when the table is initialised
18105 // This will garauntee that the panes are initialised before the init event and init Complete callback is fired
18106 this.s.dt.settings()[0].aoInitComplete.push({ fn: function () {
18107 _this._paneStartup(table);
18112 * Finds a pane based upon the name of that pane
18113 * @param name string representing the name of the pane
18114 * @returns SearchPane The pane which has that name
18116 SearchPanes.prototype._findPane = function (name) {
18117 for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
18119 if (name === pane.s.name) {
18125 * Runs the start up functions for the panes to enable listeners and populate panes
18126 * @param table the DataTable api for the parent Table
18128 SearchPanes.prototype._paneStartup = function (table) {
18130 // Magic number of 500 is a guess at what will be fast
18131 if (this.s.dt.page.info().recordsTotal <= 500) {
18132 this._startup(table);
18135 setTimeout(function () {
18136 _this._startup(table);
18141 * Works out which panes to update when data is recieved from the server and viewTotal is active
18143 SearchPanes.prototype._serverTotals = function () {
18144 var selectPresent = false;
18145 var deselectPresent = false;
18146 var table = this.s.dt;
18147 for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
18149 // Identify the pane where a selection or deselection has been made and add it to the list.
18150 if (pane.s.selectPresent) {
18151 this.s.selectionList.push({ index: pane.s.index, rows: pane.s.dtPane.rows({ selected: true }).data().toArray(), protect: false });
18152 table.state.save();
18153 pane.s.selectPresent = false;
18154 selectPresent = true;
18157 else if (pane.s.deselect) {
18158 var selectedData = pane.s.dtPane.rows({ selected: true }).data().toArray();
18159 if (selectedData.length > 0) {
18160 this.s.selectionList.push({ index: pane.s.index, rows: selectedData, protect: true });
18162 selectPresent = true;
18163 deselectPresent = true;
18166 // Build an updated list based on any selections or deselections added
18167 if (!selectPresent) {
18168 this.s.selectionList = [];
18171 var newSelectionList = [];
18172 for (var i = 0; i < this.s.selectionList.length; i++) {
18173 var further = false;
18174 // Find out if this selection is the last one in the list for that pane
18175 for (var j = i + 1; j < this.s.selectionList.length; j++) {
18176 if (this.s.selectionList[j].index === this.s.selectionList[i].index) {
18180 // If there are no selections for this pane in the list then just push this one
18182 this.s.panes[this.s.selectionList[i].index].s.dtPane.rows({ selected: true }).data().toArray().length > 0) {
18183 newSelectionList.push(this.s.selectionList[i]);
18186 this.s.selectionList = newSelectionList;
18189 // If there has been a deselect and only one pane has a selection then update everything
18190 if (deselectPresent && this.s.selectionList.length === 1) {
18191 for (var _b = 0, _c = this.s.panes; _b < _c.length; _b++) {
18193 pane.s.lastSelect = false;
18194 pane.s.deselect = false;
18195 if (pane.s.dtPane !== undefined && pane.s.dtPane.rows({ selected: true }).data().toArray().length > 0) {
18196 initIdx = pane.s.index;
18200 // Otherwise if there are more 1 selections then find the last one and set it to not update that pane
18201 else if (this.s.selectionList.length > 0) {
18202 var last = this.s.selectionList[this.s.selectionList.length - 1].index;
18203 for (var _d = 0, _e = this.s.panes; _d < _e.length; _d++) {
18205 pane.s.lastSelect = (pane.s.index === last);
18206 pane.s.deselect = false;
18209 // Otherwise if there are no selections then find where that took place and do not update to maintain scrolling
18210 else if (this.s.selectionList.length === 0) {
18211 for (var _f = 0, _g = this.s.panes; _f < _g.length; _f++) {
18213 // pane.s.lastSelect = (pane.s.deselect === true);
18214 pane.s.lastSelect = false;
18215 pane.s.deselect = false;
18218 $$1(this.dom.panes).empty();
18219 // Rebuild the desired panes
18220 for (var _h = 0, _j = this.s.panes; _h < _j.length; _h++) {
18222 if (!pane.s.lastSelect) {
18223 pane.rebuildPane(undefined, this.s.dt.page.info().serverSide ? this.s.serverData : undefined, pane.s.index === initIdx ? true : null, true);
18226 pane._setListeners();
18228 // append all of the panes and enable select
18229 $$1(this.dom.panes).append(pane.dom.container);
18230 if (pane.s.dtPane !== undefined) {
18231 $$1(pane.s.dtPane.table().node()).parent()[0].scrollTop = pane.s.scrollTop;
18232 $$1.fn.dataTable.select.init(pane.s.dtPane);
18237 * Initialises the tables previous/preset selections and initialises callbacks for events
18238 * @param table the parent table for which the searchPanes are being created
18240 SearchPanes.prototype._startup = function (table) {
18242 $$1(this.dom.container).text('');
18243 // Attach clear button and title bar to the document
18244 this._attachExtras();
18245 $$1(this.dom.container).append(this.dom.panes);
18246 $$1(this.dom.panes).empty();
18247 if (this.c.viewTotal && !this.c.cascadePanes) {
18248 var loadedFilter = this.s.dt.state.loaded();
18249 if (loadedFilter !== null &&
18250 loadedFilter !== undefined &&
18251 loadedFilter.searchPanes !== undefined &&
18252 loadedFilter.searchPanes.panes !== undefined) {
18253 var filterActive = false;
18254 for (var _i = 0, _a = loadedFilter.searchPanes.panes; _i < _a.length; _i++) {
18256 if (pane.selected.length > 0) {
18257 filterActive = true;
18261 if (filterActive) {
18262 for (var _b = 0, _c = this.s.panes; _b < _c.length; _b++) {
18264 pane.s.showFiltered = true;
18269 for (var _d = 0, _e = this.s.panes; _d < _e.length; _d++) {
18271 pane.rebuildPane(undefined, this.s.dt.page.info().serverSide ? this.s.serverData : undefined);
18272 $$1(this.dom.panes).append(pane.dom.container);
18274 if (this.c.viewTotal && !this.c.cascadePanes) {
18275 for (var _f = 0, _g = this.s.panes; _f < _g.length; _f++) {
18280 this._updateFilterCount();
18281 this._checkMessage();
18282 // When a draw is called on the DataTable, update all of the panes incase the data in the DataTable has changed
18283 table.on('draw.dtsps', function () {
18284 _this._updateFilterCount();
18285 if ((_this.c.cascadePanes || _this.c.viewTotal) && !_this.s.dt.page.info().serverSide) {
18286 _this.redrawPanes();
18289 _this._updateSelection();
18291 _this.s.filterPane = -1;
18293 // Whenever a state save occurs store the selection list in the state object
18294 this.s.dt.on('stateSaveParams.dtsp', function (e, settings, data) {
18295 if (data.searchPanes === undefined) {
18296 data.searchPanes = {};
18298 data.searchPanes.selectionList = _this.s.selectionList;
18300 // If the data is reloaded from the server then it is possible that it has changed completely,
18301 // so we need to rebuild the panes
18302 this.s.dt.on('xhr', function () {
18303 var processing = false;
18304 if (!_this.s.dt.page.info().serverSide) {
18305 _this.s.dt.one('draw', function () {
18310 $$1(_this.dom.panes).empty();
18311 for (var _i = 0, _a = _this.s.panes; _i < _a.length; _i++) {
18313 pane.clearData(); // Clears all of the bins and will mean that the data has to be re-read
18314 // Pass a boolean to say whether this is the last choice made for maintaining selections when rebuilding
18315 pane.rebuildPane(_this.s.selectionList[_this.s.selectionList.length - 1] !== undefined ?
18316 pane.s.index === _this.s.selectionList[_this.s.selectionList.length - 1].index :
18317 false, undefined, undefined, true);
18318 $$1(_this.dom.panes).append(pane.dom.container);
18320 if (_this.c.cascadePanes || _this.c.viewTotal) {
18321 _this.redrawPanes(_this.c.cascadePanes);
18324 _this._updateSelection();
18326 _this._checkMessage();
18330 if (this.s.selectionList !== undefined && this.s.selectionList.length > 0) {
18331 var last = this.s.selectionList[this.s.selectionList.length - 1].index;
18332 for (var _h = 0, _j = this.s.panes; _h < _j.length; _h++) {
18334 pane.s.lastSelect = (pane.s.index === last);
18337 // If cascadePanes is active then make the previous selections in the order they were previously
18338 if (this.s.selectionList.length > 0 && this.c.cascadePanes) {
18339 this._cascadeRegen(this.s.selectionList);
18341 // PreSelect any selections which have been defined using the preSelect option
18343 .columns(this.c.columns.length > 0 ? this.c.columns : undefined)
18345 .each(function (idx) {
18346 if (_this.s.panes[idx] !== undefined &&
18347 _this.s.panes[idx].s.dtPane !== undefined &&
18348 _this.s.panes[idx].s.colOpts.preSelect !== undefined) {
18349 var tableLength = _this.s.panes[idx].s.dtPane.rows().data().toArray().length;
18350 for (var i = 0; i < tableLength; i++) {
18351 if (_this.s.panes[idx].s.colOpts.preSelect.indexOf(_this.s.panes[idx].s.dtPane.cell(i, 0).data()) !== -1) {
18352 _this.s.panes[idx].s.dtPane.row(i).select();
18353 _this.s.panes[idx].updateTable();
18358 // Update the title bar to show how many filters have been selected
18359 this._updateFilterCount();
18360 // If the table is destroyed and restarted then clear the selections so that they do not persist.
18361 table.on('destroy.dtsps', function () {
18362 for (var _i = 0, _a = _this.s.panes; _i < _a.length; _i++) {
18366 table.off('.dtsps');
18367 $$1(_this.dom.clearAll).off('.dtsps');
18368 $$1(_this.dom.container).remove();
18369 _this.clearSelections();
18371 // When the clear All button has been pressed clear all of the selections in the panes
18372 if (this.c.clear) {
18373 $$1(this.dom.clearAll).on('click.dtsps', function () {
18374 _this.clearSelections();
18377 if (this.s.dt.page.info().serverSide) {
18378 table.on('preXhr.dt', function (e, settings, data) {
18379 if (data.searchPanes === undefined) {
18380 data.searchPanes = {};
18382 for (var _i = 0, _a = _this.s.panes; _i < _a.length; _i++) {
18384 var src = _this.s.dt.column(pane.s.index).dataSrc();
18385 if (data.searchPanes[src] === undefined) {
18386 data.searchPanes[src] = {};
18388 if (pane.s.dtPane !== undefined) {
18389 var rowData = pane.s.dtPane.rows({ selected: true }).data().toArray();
18390 for (var i = 0; i < rowData.length; i++) {
18391 data.searchPanes[src][i] = rowData[i].display;
18395 if (_this.c.viewTotal) {
18396 _this._prepViewTotal();
18401 table.on('preXhr.dt', function (e, settings, data) {
18402 for (var _i = 0, _a = _this.s.panes; _i < _a.length; _i++) {
18408 table.settings()[0]._searchPanes = this;
18410 SearchPanes.prototype._prepViewTotal = function () {
18411 var filterPane = this.s.filterPane;
18412 var filterActive = false;
18413 for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
18415 if (pane.s.dtPane !== undefined) {
18416 var selectLength = pane.s.dtPane.rows({ selected: true }).data().toArray().length;
18417 // If filterPane === -1 then a pane with a selection has not been found yet, so set filterPane to that panes index
18418 if (selectLength > 0 && filterPane === -1) {
18419 filterPane = pane.s.index;
18420 filterActive = true;
18422 // Then if another pane is found with a selection then set filterPane to null to
18423 // show that multiple panes have selections present
18424 else if (selectLength > 0) {
18429 // Update all of the panes to reflect the current state of the filters
18430 for (var _b = 0, _c = this.s.panes; _b < _c.length; _b++) {
18432 if (pane.s.dtPane !== undefined) {
18433 pane.s.filteringActive = true;
18434 if ((filterPane !== -1 && filterPane !== null && filterPane === pane.s.index) || filterActive === false) {
18435 pane.s.filteringActive = false;
18441 * Updates the number of filters that have been applied in the title
18443 SearchPanes.prototype._updateFilterCount = function () {
18444 var filterCount = 0;
18445 // Add the number of all of the filters throughout the panes
18446 for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
18448 if (pane.s.dtPane !== undefined) {
18449 filterCount += pane.getPaneCount();
18452 // Run the message through the internationalisation method to improve readability
18453 var message = this.s.dt.i18n('searchPanes.title', 'Filters Active - %d', filterCount);
18454 $$1(this.dom.title).text(message);
18455 if (this.c.filterChanged !== undefined && typeof this.c.filterChanged === 'function') {
18456 this.c.filterChanged(filterCount);
18460 * Updates the selectionList when cascade is not in place
18462 SearchPanes.prototype._updateSelection = function () {
18463 this.s.selectionList = [];
18464 for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
18466 if (pane.s.dtPane !== undefined) {
18467 this.s.selectionList.push({ index: pane.s.index, rows: pane.s.dtPane.rows({ selected: true }).data().toArray(), protect: false });
18470 this.s.dt.state.save();
18472 SearchPanes.version = '1.1.1';
18473 SearchPanes.classes = {
18474 clear: 'dtsp-clear',
18475 clearAll: 'dtsp-clearAll',
18476 container: 'dtsp-searchPanes',
18477 emptyMessage: 'dtsp-emptyMessage',
18478 hide: 'dtsp-hidden',
18479 panes: 'dtsp-panesContainer',
18480 search: 'dtsp-search',
18481 title: 'dtsp-title',
18482 titleRow: 'dtsp-titleRow'
18484 // Define SearchPanes default options
18485 SearchPanes.defaults = {
18486 cascadePanes: false,
18488 container: function (dt) {
18489 return dt.table().container();
18492 filterChanged: undefined,
18493 layout: 'columns-3',
18498 return SearchPanes;
18501 /*! SearchPanes 1.1.1
18502 * 2019-2020 SpryMedia Ltd - datatables.net/license
18504 // DataTables extensions common UMD. Note that this allows for AMD, CommonJS
18505 // (with window and jQuery being allowed as parameters to the returned
18506 // function) or just default browser loading.
18507 (function (factory) {
18508 if (typeof define === 'function' && define.amd) {
18510 define(['jquery', 'datatables.net'], function ($) {
18511 return factory($, window, document);
18514 else if (typeof exports === 'object') {
18516 module.exports = function (root, $) {
18520 if (!$ || !$.fn.dataTable) {
18521 $ = require('datatables.net')(root, $).$;
18523 return factory($, root, root.document);
18527 // Browser - assume jQuery has already been loaded
18528 factory(window.jQuery, window, document);
18530 }(function ($, window, document) {
18533 var DataTable = $.fn.dataTable;
18534 $.fn.dataTable.SearchPanes = SearchPanes;
18535 $.fn.DataTable.SearchPanes = SearchPanes;
18536 $.fn.dataTable.SearchPane = SearchPane;
18537 $.fn.DataTable.SearchPane = SearchPane;
18538 DataTable.Api.register('searchPanes.rebuild()', function () {
18539 return this.iterator('table', function () {
18540 if (this.searchPanes) {
18541 this.searchPanes.rebuild();
18545 DataTable.Api.register('column().paneOptions()', function (options) {
18546 return this.iterator('column', function (idx) {
18547 var col = this.aoColumns[idx];
18548 if (!col.searchPanes) {
18549 col.searchPanes = {};
18551 col.searchPanes.values = options;
18552 if (this.searchPanes) {
18553 this.searchPanes.rebuild();
18557 var apiRegister = $.fn.dataTable.Api.register;
18558 apiRegister('searchPanes()', function () {
18561 apiRegister('searchPanes.clearSelections()', function () {
18562 var ctx = this.context[0];
18563 ctx._searchPanes.clearSelections();
18566 apiRegister('searchPanes.rebuildPane()', function (targetIdx, maintainSelections) {
18567 var ctx = this.context[0];
18568 ctx._searchPanes.rebuild(targetIdx, maintainSelections);
18571 apiRegister('searchPanes.container()', function () {
18572 var ctx = this.context[0];
18573 return ctx._searchPanes.getNode();
18575 $.fn.dataTable.ext.buttons.searchPanesClear = {
18576 text: 'Clear Panes',
18577 action: function (e, dt, node, config) {
18578 dt.searchPanes.clearSelections();
18581 $.fn.dataTable.ext.buttons.searchPanes = {
18582 action: function (e, dt, node, config) {
18583 e.stopPropagation();
18584 this.popover(config._panes.getNode(), {
18585 align: 'dt-container'
18589 init: function (dt, node, config) {
18590 var panes = new $.fn.dataTable.SearchPanes(dt, $.extend({
18591 filterChanged: function (count) {
18592 dt.button(node).text(dt.i18n('searchPanes.collapse', { 0: 'SearchPanes', _: 'SearchPanes (%d)' }, count));
18594 }, config.config));
18595 var message = dt.i18n('searchPanes.collapse', 'SearchPanes', 0);
18596 dt.button(node).text(message);
18597 config._panes = panes;
18599 text: 'Search Panes'
18601 function _init(settings, fromPre) {
18602 if (fromPre === void 0) { fromPre = false; }
18603 var api = new DataTable.Api(settings);
18604 var opts = api.init().searchPanes || DataTable.defaults.searchPanes;
18605 var searchPanes = new SearchPanes(api, opts, fromPre);
18606 var node = searchPanes.getNode();
18609 // Attach a listener to the document which listens for DataTables initialisation
18610 // events so we can automatically initialise
18611 $(document).on('preInit.dt.dtsp', function (e, settings, json) {
18612 if (e.namespace !== 'dt') {
18615 if (settings.oInit.searchPanes ||
18616 DataTable.defaults.searchPanes) {
18617 if (!settings._searchPanes) {
18618 _init(settings, true);
18622 // DataTables `dom` feature option
18623 DataTable.ext.feature.push({
18627 // DataTables 2 layout feature
18628 if (DataTable.ext.features) {
18629 DataTable.ext.features.register('searchPanes', _init);
18636 (function (factory) {
18637 if (typeof define === 'function' && define.amd) {
18639 define(['jquery', 'datatables.net-dt', 'datatables.net-searchPanes'], function ($) {
18640 return factory($, window, document);
18643 else if (typeof exports === 'object') {
18645 module.exports = function (root, $) {
18649 if (!$ || !$.fn.dataTable) {
18650 $ = require('datatables.net-dt')(root, $).$;
18652 if (!$.fn.dataTable.searchPanes) {
18653 require('datatables.net-searchpanes')(root, $);
18655 return factory($, root, root.document);
18660 factory(jQuery, window, document);
18662 }(function ($, window, document) {
18664 var DataTable = $.fn.dataTable;
18665 return DataTable.searchPanes;
18669 /*! Select for DataTables 1.3.1
18670 * 2015-2019 SpryMedia Ltd - datatables.net/license/mit
18674 * @summary Select for DataTables
18675 * @description A collection of API methods, events and buttons for DataTables
18676 * that provides selection options of the items in a DataTable
18678 * @file dataTables.select.js
18679 * @author SpryMedia Ltd (www.sprymedia.co.uk)
18680 * @contact datatables.net/forums
18681 * @copyright Copyright 2015-2019 SpryMedia Ltd.
18683 * This source file is free software, available under the following license:
18684 * MIT license - http://datatables.net/license/mit
18686 * This source file is distributed in the hope that it will be useful, but
18687 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18688 * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
18690 * For details please refer to: http://www.datatables.net/extensions/select
18692 (function( factory ){
18693 if ( typeof define === 'function' && define.amd ) {
18695 define( ['jquery', 'datatables.net'], function ( $ ) {
18696 return factory( $, window, document );
18699 else if ( typeof exports === 'object' ) {
18701 module.exports = function (root, $) {
18706 if ( ! $ || ! $.fn.dataTable ) {
18707 $ = require('datatables.net')(root, $).$;
18710 return factory( $, root, root.document );
18715 factory( jQuery, window, document );
18717 }(function( $, window, document, undefined ) {
18719 var DataTable = $.fn.dataTable;
18722 // Version information for debugger
18723 DataTable.select = {};
18725 DataTable.select.version = '1.3.1';
18727 DataTable.select.init = function ( dt ) {
18728 var ctx = dt.settings()[0];
18729 var init = ctx.oInit.select;
18730 var defaults = DataTable.defaults.select;
18731 var opts = init === undefined ?
18738 var blurable = false;
18739 var toggleable = true;
18741 var selector = 'td, th';
18742 var className = 'selected';
18743 var setStyle = false;
18747 // Initialisation customisations
18748 if ( opts === true ) {
18752 else if ( typeof opts === 'string' ) {
18756 else if ( $.isPlainObject( opts ) ) {
18757 if ( opts.blurable !== undefined ) {
18758 blurable = opts.blurable;
18761 if ( opts.toggleable !== undefined ) {
18762 toggleable = opts.toggleable;
18765 if ( opts.info !== undefined ) {
18769 if ( opts.items !== undefined ) {
18770 items = opts.items;
18773 if ( opts.style !== undefined ) {
18774 style = opts.style;
18782 if ( opts.selector !== undefined ) {
18783 selector = opts.selector;
18786 if ( opts.className !== undefined ) {
18787 className = opts.className;
18791 dt.select.selector( selector );
18792 dt.select.items( items );
18793 dt.select.style( style );
18794 dt.select.blurable( blurable );
18795 dt.select.toggleable( toggleable );
18796 dt.select.info( info );
18797 ctx._select.className = className;
18800 // Sort table based on selected rows. Requires Select Datatables extension
18801 $.fn.dataTable.ext.order['select-checkbox'] = function ( settings, col ) {
18802 return this.api().column( col, {order: 'index'} ).nodes().map( function ( td ) {
18803 if ( settings._select.items === 'row' ) {
18804 return $( td ).parent().hasClass( settings._select.className );
18805 } else if ( settings._select.items === 'cell' ) {
18806 return $( td ).hasClass( settings._select.className );
18812 // If the init options haven't enabled select, but there is a selectable
18813 // class name, then enable
18814 if ( ! setStyle && $( dt.table().node() ).hasClass( 'selectable' ) ) {
18815 dt.select.style( 'os' );
18821 Select is a collection of API methods, event handlers, event emitters and
18822 buttons (for the `Buttons` extension) for DataTables. It provides the following
18823 features, with an overview of how they are implemented:
18825 ## Selection of rows, columns and cells. Whether an item is selected or not is
18828 * rows: a `_select_selected` property which contains a boolean value of the
18829 DataTables' `aoData` object for each row
18830 * columns: a `_select_selected` property which contains a boolean value of the
18831 DataTables' `aoColumns` object for each column
18832 * cells: a `_selected_cells` property which contains an array of boolean values
18833 of the `aoData` object for each row. The array is the same length as the
18834 columns array, with each element of it representing a cell.
18836 This method of using boolean flags allows Select to operate when nodes have not
18837 been created for rows / cells (DataTables' defer rendering feature).
18841 A range of API methods are available for triggering selection and de-selection
18842 of rows. Methods are also available to configure the selection events that can
18843 be triggered by an end user (such as which items are to be selected). To a large
18844 extent, these of API methods *is* Select. It is basically a collection of helper
18845 functions that can be used to select items in a DataTable.
18847 Configuration of select is held in the object `_select` which is attached to the
18848 DataTables settings object on initialisation. Select being available on a table
18849 is not optional when Select is loaded, but its default is for selection only to
18850 be available via the API - so the end user wouldn't be able to select rows
18851 without additional configuration.
18853 The `_select` object contains the following properties:
18857 items:string - Can be `rows`, `columns` or `cells`. Defines what item
18858 will be selected if the user is allowed to activate row
18859 selection using the mouse.
18860 style:string - Can be `none`, `single`, `multi` or `os`. Defines the
18861 interaction style when selecting items
18862 blurable:boolean - If row selection can be cleared by clicking outside of
18864 toggleable:boolean - If row selection can be cancelled by repeated clicking
18866 info:boolean - If the selection summary should be shown in the table
18867 information elements
18871 In addition to the API methods, Select also extends the DataTables selector
18872 options for rows, columns and cells adding a `selected` option to the selector
18873 options object, allowing the developer to select only selected items or
18876 ## Mouse selection of items
18878 Clicking on items can be used to select items. This is done by a simple event
18879 handler that will select the items using the API methods.
18884 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
18889 * Add one or more cells to the selection when shift clicking in OS selection
18890 * style cell selection.
18892 * Cell range is more complicated than row and column as we want to select
18893 * in the visible grid rather than by index in sequence. For example, if you
18894 * click first in cell 1-1 and then shift click in 2-2 - cells 1-2 and 2-1
18895 * should also be selected (and not 1-3, 1-4. etc)
18897 * @param {DataTable.Api} dt DataTable
18898 * @param {object} idx Cell index to select to
18899 * @param {object} last Cell index to select from
18902 function cellRange( dt, idx, last )
18907 var selectColumns = function ( start, end ) {
18908 if ( start > end ) {
18914 var record = false;
18915 return dt.columns( ':visible' ).indexes().filter( function (i) {
18916 if ( i === start ) {
18920 if ( i === end ) { // not else if, as start might === end
18929 var selectRows = function ( start, end ) {
18930 var indexes = dt.rows( { search: 'applied' } ).indexes();
18932 // Which comes first - might need to swap
18933 if ( indexes.indexOf( start ) > indexes.indexOf( end ) ) {
18939 var record = false;
18940 return indexes.filter( function (i) {
18941 if ( i === start ) {
18954 if ( ! dt.cells( { selected: true } ).any() && ! last ) {
18955 // select from the top left cell to this one
18956 columnIndexes = selectColumns( 0, idx.column );
18957 rowIndexes = selectRows( 0 , idx.row );
18960 // Get column indexes between old and new
18961 columnIndexes = selectColumns( last.column, idx.column );
18962 rowIndexes = selectRows( last.row , idx.row );
18965 indexes = dt.cells( rowIndexes, columnIndexes ).flatten();
18967 if ( ! dt.cells( idx, { selected: true } ).any() ) {
18969 dt.cells( indexes ).select();
18973 dt.cells( indexes ).deselect();
18978 * Disable mouse selection by removing the selectors
18980 * @param {DataTable.Api} dt DataTable to remove events from
18983 function disableMouseSelection( dt )
18985 var ctx = dt.settings()[0];
18986 var selector = ctx._select.selector;
18988 $( dt.table().container() )
18989 .off( 'mousedown.dtSelect', selector )
18990 .off( 'mouseup.dtSelect', selector )
18991 .off( 'click.dtSelect', selector );
18993 $('body').off( 'click.dtSelect' + _safeId(dt.table().node()) );
18997 * Attach mouse listeners to the table to allow mouse selection of items
18999 * @param {DataTable.Api} dt DataTable to remove events from
19002 function enableMouseSelection ( dt )
19004 var container = $( dt.table().container() );
19005 var ctx = dt.settings()[0];
19006 var selector = ctx._select.selector;
19007 var matchSelection;
19010 .on( 'mousedown.dtSelect', selector, function(e) {
19011 // Disallow text selection for shift clicking on the table so multi
19012 // element selection doesn't look terrible!
19013 if ( e.shiftKey || e.metaKey || e.ctrlKey ) {
19015 .css( '-moz-user-select', 'none' )
19016 .one('selectstart.dtSelect', selector, function () {
19021 if ( window.getSelection ) {
19022 matchSelection = window.getSelection();
19025 .on( 'mouseup.dtSelect', selector, function() {
19026 // Allow text selection to occur again, Mozilla style (tested in FF
19027 // 35.0.1 - still required)
19028 container.css( '-moz-user-select', '' );
19030 .on( 'click.dtSelect', selector, function ( e ) {
19031 var items = dt.select.items();
19034 // If text was selected (click and drag), then we shouldn't change
19035 // the row's selected state
19036 if ( matchSelection ) {
19037 var selection = window.getSelection();
19039 // If the element that contains the selection is not in the table, we can ignore it
19040 // This can happen if the developer selects text from the click event
19041 if ( ! selection.anchorNode || $(selection.anchorNode).closest('table')[0] === dt.table().node() ) {
19042 if ( selection !== matchSelection ) {
19048 var ctx = dt.settings()[0];
19049 var wrapperClass = $.trim(dt.settings()[0].oClasses.sWrapper).replace(/ +/g, '.');
19051 // Ignore clicks inside a sub-table
19052 if ( $(e.target).closest('div.'+wrapperClass)[0] != dt.table().container() ) {
19056 var cell = dt.cell( $(e.target).closest('td, th') );
19058 // Check the cell actually belongs to the host DataTable (so child
19059 // rows, etc, are ignored)
19060 if ( ! cell.any() ) {
19064 var event = $.Event('user-select.dt');
19065 eventTrigger( dt, event, [ items, cell, e ] );
19067 if ( event.isDefaultPrevented() ) {
19071 var cellIndex = cell.index();
19072 if ( items === 'row' ) {
19073 idx = cellIndex.row;
19074 typeSelect( e, dt, ctx, 'row', idx );
19076 else if ( items === 'column' ) {
19077 idx = cell.index().column;
19078 typeSelect( e, dt, ctx, 'column', idx );
19080 else if ( items === 'cell' ) {
19081 idx = cell.index();
19082 typeSelect( e, dt, ctx, 'cell', idx );
19085 ctx._select_lastCell = cellIndex;
19089 $('body').on( 'click.dtSelect' + _safeId(dt.table().node()), function ( e ) {
19090 if ( ctx._select.blurable ) {
19091 // If the click was inside the DataTables container, don't blur
19092 if ( $(e.target).parents().filter( dt.table().container() ).length ) {
19096 // Ignore elements which have been removed from the DOM (i.e. paging
19098 if ( $(e.target).parents('html').length === 0 ) {
19102 // Don't blur in Editor form
19103 if ( $(e.target).parents('div.DTE').length ) {
19107 clear( ctx, true );
19113 * Trigger an event on a DataTable
19115 * @param {DataTable.Api} api DataTable to trigger events on
19116 * @param {boolean} selected true if selected, false if deselected
19117 * @param {string} type Item type acting on
19118 * @param {boolean} any Require that there are values before
19122 function eventTrigger ( api, type, args, any )
19124 if ( any && ! api.flatten().length ) {
19128 if ( typeof type === 'string' ) {
19129 type = type +'.dt';
19132 args.unshift( api );
19134 $(api.table().node()).trigger( type, args );
19138 * Update the information element of the DataTable showing information about the
19139 * items selected. This is done by adding tags to the existing text
19141 * @param {DataTable.Api} api DataTable to update
19144 function info ( api )
19146 var ctx = api.settings()[0];
19148 if ( ! ctx._select.info || ! ctx.aanFeatures.i ) {
19152 if ( api.select.style() === 'api' ) {
19156 var rows = api.rows( { selected: true } ).flatten().length;
19157 var columns = api.columns( { selected: true } ).flatten().length;
19158 var cells = api.cells( { selected: true } ).flatten().length;
19160 var add = function ( el, name, num ) {
19161 el.append( $('<span class="select-item"/>').append( api.i18n(
19162 'select.'+name+'s',
19163 { _: '%d '+name+'s selected', 0: '', 1: '1 '+name+' selected' },
19168 // Internal knowledge of DataTables to loop over all information elements
19169 $.each( ctx.aanFeatures.i, function ( i, el ) {
19172 var output = $('<span class="select-info"/>');
19173 add( output, 'row', rows );
19174 add( output, 'column', columns );
19175 add( output, 'cell', cells );
19177 var exisiting = el.children('span.select-info');
19178 if ( exisiting.length ) {
19179 exisiting.remove();
19182 if ( output.text() !== '' ) {
19183 el.append( output );
19189 * Initialisation of a new table. Attach event handlers and callbacks to allow
19190 * Select to operate correctly.
19192 * This will occur _after_ the initial DataTables initialisation, although
19193 * before Ajax data is rendered, if there is ajax data
19195 * @param {DataTable.settings} ctx Settings object to operate on
19198 function init ( ctx ) {
19199 var api = new DataTable.Api( ctx );
19201 // Row callback so that classes can be added to rows and cells if the item
19202 // was selected before the element was created. This will happen with the
19203 // `deferRender` option enabled.
19205 // This method of attaching to `aoRowCreatedCallback` is a hack until
19206 // DataTables has proper events for row manipulation If you are reviewing
19207 // this code to create your own plug-ins, please do not do this!
19208 ctx.aoRowCreatedCallback.push( {
19209 fn: function ( row, data, index ) {
19211 var d = ctx.aoData[ index ];
19214 if ( d._select_selected ) {
19215 $( row ).addClass( ctx._select.className );
19218 // Cells and columns - if separated out, we would need to do two
19219 // loops, so it makes sense to combine them into a single one
19220 for ( i=0, ien=ctx.aoColumns.length ; i<ien ; i++ ) {
19221 if ( ctx.aoColumns[i]._select_selected || (d._selected_cells && d._selected_cells[i]) ) {
19222 $(d.anCells[i]).addClass( ctx._select.className );
19226 sName: 'select-deferRender'
19229 // On Ajax reload we want to reselect all rows which are currently selected,
19230 // if there is an rowId (i.e. a unique value to identify each row with)
19231 api.on( 'preXhr.dt.dtSelect', function () {
19232 // note that column selection doesn't need to be cached and then
19233 // reselected, as they are already selected
19234 var rows = api.rows( { selected: true } ).ids( true ).filter( function ( d ) {
19235 return d !== undefined;
19238 var cells = api.cells( { selected: true } ).eq(0).map( function ( cellIdx ) {
19239 var id = api.row( cellIdx.row ).id( true );
19241 { row: id, column: cellIdx.column } :
19243 } ).filter( function ( d ) {
19244 return d !== undefined;
19247 // On the next draw, reselect the currently selected items
19248 api.one( 'draw.dt.dtSelect', function () {
19249 api.rows( rows ).select();
19251 // `cells` is not a cell index selector, so it needs a loop
19252 if ( cells.any() ) {
19253 cells.each( function ( id ) {
19254 api.cells( id.row, id.column ).select();
19260 // Update the table information element with selected item summary
19261 api.on( 'draw.dtSelect.dt select.dtSelect.dt deselect.dtSelect.dt info.dt', function () {
19265 // Clean up and release
19266 api.on( 'destroy.dtSelect', function () {
19267 disableMouseSelection( api );
19268 api.off( '.dtSelect' );
19273 * Add one or more items (rows or columns) to the selection when shift clicking
19274 * in OS selection style
19276 * @param {DataTable.Api} dt DataTable
19277 * @param {string} type Row or column range selector
19278 * @param {object} idx Item index to select to
19279 * @param {object} last Item index to select from
19282 function rowColumnRange( dt, type, idx, last )
19284 // Add a range of rows from the last selected row to this one
19285 var indexes = dt[type+'s']( { search: 'applied' } ).indexes();
19286 var idx1 = $.inArray( last, indexes );
19287 var idx2 = $.inArray( idx, indexes );
19289 if ( ! dt[type+'s']( { selected: true } ).any() && idx1 === -1 ) {
19290 // select from top to here - slightly odd, but both Windows and Mac OS
19292 indexes.splice( $.inArray( idx, indexes )+1, indexes.length );
19295 // reverse so we can shift click 'up' as well as down
19296 if ( idx1 > idx2 ) {
19302 indexes.splice( idx2+1, indexes.length );
19303 indexes.splice( 0, idx1 );
19306 if ( ! dt[type]( idx, { selected: true } ).any() ) {
19308 dt[type+'s']( indexes ).select();
19311 // Deselect range - need to keep the clicked on row selected
19312 indexes.splice( $.inArray( idx, indexes ), 1 );
19313 dt[type+'s']( indexes ).deselect();
19318 * Clear all selected items
19320 * @param {DataTable.settings} ctx Settings object of the host DataTable
19321 * @param {boolean} [force=false] Force the de-selection to happen, regardless
19322 * of selection style
19325 function clear( ctx, force )
19327 if ( force || ctx._select.style === 'single' ) {
19328 var api = new DataTable.Api( ctx );
19330 api.rows( { selected: true } ).deselect();
19331 api.columns( { selected: true } ).deselect();
19332 api.cells( { selected: true } ).deselect();
19337 * Select items based on the current configuration for style and items.
19339 * @param {object} e Mouse event object
19340 * @param {DataTables.Api} dt DataTable
19341 * @param {DataTable.settings} ctx Settings object of the host DataTable
19342 * @param {string} type Items to select
19343 * @param {int|object} idx Index of the item to select
19346 function typeSelect ( e, dt, ctx, type, idx )
19348 var style = dt.select.style();
19349 var toggleable = dt.select.toggleable();
19350 var isSelected = dt[type]( idx, { selected: true } ).any();
19352 if ( isSelected && ! toggleable ) {
19356 if ( style === 'os' ) {
19357 if ( e.ctrlKey || e.metaKey ) {
19358 // Add or remove from the selection
19359 dt[type]( idx ).select( ! isSelected );
19361 else if ( e.shiftKey ) {
19362 if ( type === 'cell' ) {
19363 cellRange( dt, idx, ctx._select_lastCell || null );
19366 rowColumnRange( dt, type, idx, ctx._select_lastCell ?
19367 ctx._select_lastCell[type] :
19373 // No cmd or shift click - deselect if selected, or select
19375 var selected = dt[type+'s']( { selected: true } );
19377 if ( isSelected && selected.flatten().length === 1 ) {
19378 dt[type]( idx ).deselect();
19381 selected.deselect();
19382 dt[type]( idx ).select();
19385 } else if ( style == 'multi+shift' ) {
19386 if ( e.shiftKey ) {
19387 if ( type === 'cell' ) {
19388 cellRange( dt, idx, ctx._select_lastCell || null );
19391 rowColumnRange( dt, type, idx, ctx._select_lastCell ?
19392 ctx._select_lastCell[type] :
19398 dt[ type ]( idx ).select( ! isSelected );
19402 dt[ type ]( idx ).select( ! isSelected );
19406 function _safeId( node ) {
19407 return node.id.replace(/[^a-zA-Z0-9\-\_]/g, '-');
19412 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
19413 * DataTables selectors
19416 // row and column are basically identical just assigned to different properties
19417 // and checking a different array, so we can dynamically create the functions to
19418 // reduce the code size
19420 { type: 'row', prop: 'aoData' },
19421 { type: 'column', prop: 'aoColumns' }
19422 ], function ( i, o ) {
19423 DataTable.ext.selector[ o.type ].push( function ( settings, opts, indexes ) {
19424 var selected = opts.selected;
19428 if ( selected !== true && selected !== false ) {
19432 for ( var i=0, ien=indexes.length ; i<ien ; i++ ) {
19433 data = settings[ o.prop ][ indexes[i] ];
19435 if ( (selected === true && data._select_selected === true) ||
19436 (selected === false && ! data._select_selected )
19438 out.push( indexes[i] );
19446 DataTable.ext.selector.cell.push( function ( settings, opts, cells ) {
19447 var selected = opts.selected;
19451 if ( selected === undefined ) {
19455 for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
19456 rowData = settings.aoData[ cells[i].row ];
19458 if ( (selected === true && rowData._selected_cells && rowData._selected_cells[ cells[i].column ] === true) ||
19459 (selected === false && ( ! rowData._selected_cells || ! rowData._selected_cells[ cells[i].column ] ) )
19461 out.push( cells[i] );
19470 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
19473 * For complete documentation, please refer to the docs/api directory or the
19477 // Local variables to improve compression
19478 var apiRegister = DataTable.Api.register;
19479 var apiRegisterPlural = DataTable.Api.registerPlural;
19481 apiRegister( 'select()', function () {
19482 return this.iterator( 'table', function ( ctx ) {
19483 DataTable.select.init( new DataTable.Api( ctx ) );
19487 apiRegister( 'select.blurable()', function ( flag ) {
19488 if ( flag === undefined ) {
19489 return this.context[0]._select.blurable;
19492 return this.iterator( 'table', function ( ctx ) {
19493 ctx._select.blurable = flag;
19497 apiRegister( 'select.toggleable()', function ( flag ) {
19498 if ( flag === undefined ) {
19499 return this.context[0]._select.toggleable;
19502 return this.iterator( 'table', function ( ctx ) {
19503 ctx._select.toggleable = flag;
19507 apiRegister( 'select.info()', function ( flag ) {
19508 if ( info === undefined ) {
19509 return this.context[0]._select.info;
19512 return this.iterator( 'table', function ( ctx ) {
19513 ctx._select.info = flag;
19517 apiRegister( 'select.items()', function ( items ) {
19518 if ( items === undefined ) {
19519 return this.context[0]._select.items;
19522 return this.iterator( 'table', function ( ctx ) {
19523 ctx._select.items = items;
19525 eventTrigger( new DataTable.Api( ctx ), 'selectItems', [ items ] );
19529 // Takes effect from the _next_ selection. None disables future selection, but
19530 // does not clear the current selection. Use the `deselect` methods for that
19531 apiRegister( 'select.style()', function ( style ) {
19532 if ( style === undefined ) {
19533 return this.context[0]._select.style;
19536 return this.iterator( 'table', function ( ctx ) {
19537 ctx._select.style = style;
19539 if ( ! ctx._select_init ) {
19543 // Add / remove mouse event handlers. They aren't required when only
19544 // API selection is available
19545 var dt = new DataTable.Api( ctx );
19546 disableMouseSelection( dt );
19548 if ( style !== 'api' ) {
19549 enableMouseSelection( dt );
19552 eventTrigger( new DataTable.Api( ctx ), 'selectStyle', [ style ] );
19556 apiRegister( 'select.selector()', function ( selector ) {
19557 if ( selector === undefined ) {
19558 return this.context[0]._select.selector;
19561 return this.iterator( 'table', function ( ctx ) {
19562 disableMouseSelection( new DataTable.Api( ctx ) );
19564 ctx._select.selector = selector;
19566 if ( ctx._select.style !== 'api' ) {
19567 enableMouseSelection( new DataTable.Api( ctx ) );
19574 apiRegisterPlural( 'rows().select()', 'row().select()', function ( select ) {
19577 if ( select === false ) {
19578 return this.deselect();
19581 this.iterator( 'row', function ( ctx, idx ) {
19584 ctx.aoData[ idx ]._select_selected = true;
19585 $( ctx.aoData[ idx ].nTr ).addClass( ctx._select.className );
19588 this.iterator( 'table', function ( ctx, i ) {
19589 eventTrigger( api, 'select', [ 'row', api[i] ], true );
19595 apiRegisterPlural( 'columns().select()', 'column().select()', function ( select ) {
19598 if ( select === false ) {
19599 return this.deselect();
19602 this.iterator( 'column', function ( ctx, idx ) {
19605 ctx.aoColumns[ idx ]._select_selected = true;
19607 var column = new DataTable.Api( ctx ).column( idx );
19609 $( column.header() ).addClass( ctx._select.className );
19610 $( column.footer() ).addClass( ctx._select.className );
19612 column.nodes().to$().addClass( ctx._select.className );
19615 this.iterator( 'table', function ( ctx, i ) {
19616 eventTrigger( api, 'select', [ 'column', api[i] ], true );
19622 apiRegisterPlural( 'cells().select()', 'cell().select()', function ( select ) {
19625 if ( select === false ) {
19626 return this.deselect();
19629 this.iterator( 'cell', function ( ctx, rowIdx, colIdx ) {
19632 var data = ctx.aoData[ rowIdx ];
19634 if ( data._selected_cells === undefined ) {
19635 data._selected_cells = [];
19638 data._selected_cells[ colIdx ] = true;
19640 if ( data.anCells ) {
19641 $( data.anCells[ colIdx ] ).addClass( ctx._select.className );
19645 this.iterator( 'table', function ( ctx, i ) {
19646 eventTrigger( api, 'select', [ 'cell', api[i] ], true );
19653 apiRegisterPlural( 'rows().deselect()', 'row().deselect()', function () {
19656 this.iterator( 'row', function ( ctx, idx ) {
19657 ctx.aoData[ idx ]._select_selected = false;
19658 $( ctx.aoData[ idx ].nTr ).removeClass( ctx._select.className );
19661 this.iterator( 'table', function ( ctx, i ) {
19662 eventTrigger( api, 'deselect', [ 'row', api[i] ], true );
19668 apiRegisterPlural( 'columns().deselect()', 'column().deselect()', function () {
19671 this.iterator( 'column', function ( ctx, idx ) {
19672 ctx.aoColumns[ idx ]._select_selected = false;
19674 var api = new DataTable.Api( ctx );
19675 var column = api.column( idx );
19677 $( column.header() ).removeClass( ctx._select.className );
19678 $( column.footer() ).removeClass( ctx._select.className );
19680 // Need to loop over each cell, rather than just using
19681 // `column().nodes()` as cells which are individually selected should
19682 // not have the `selected` class removed from them
19683 api.cells( null, idx ).indexes().each( function (cellIdx) {
19684 var data = ctx.aoData[ cellIdx.row ];
19685 var cellSelected = data._selected_cells;
19687 if ( data.anCells && (! cellSelected || ! cellSelected[ cellIdx.column ]) ) {
19688 $( data.anCells[ cellIdx.column ] ).removeClass( ctx._select.className );
19693 this.iterator( 'table', function ( ctx, i ) {
19694 eventTrigger( api, 'deselect', [ 'column', api[i] ], true );
19700 apiRegisterPlural( 'cells().deselect()', 'cell().deselect()', function () {
19703 this.iterator( 'cell', function ( ctx, rowIdx, colIdx ) {
19704 var data = ctx.aoData[ rowIdx ];
19706 data._selected_cells[ colIdx ] = false;
19708 // Remove class only if the cells exist, and the cell is not column
19709 // selected, in which case the class should remain (since it is selected
19711 if ( data.anCells && ! ctx.aoColumns[ colIdx ]._select_selected ) {
19712 $( data.anCells[ colIdx ] ).removeClass( ctx._select.className );
19716 this.iterator( 'table', function ( ctx, i ) {
19717 eventTrigger( api, 'deselect', [ 'cell', api[i] ], true );
19725 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
19728 function i18n( label, def ) {
19729 return function (dt) {
19730 return dt.i18n( 'buttons.'+label, def );
19734 // Common events with suitable namespaces
19735 function namespacedEvents ( config ) {
19736 var unique = config._eventNamespace;
19738 return 'draw.dt.DT'+unique+' select.dt.DT'+unique+' deselect.dt.DT'+unique;
19741 function enabled ( dt, config ) {
19742 if ( $.inArray( 'rows', config.limitTo ) !== -1 && dt.rows( { selected: true } ).any() ) {
19746 if ( $.inArray( 'columns', config.limitTo ) !== -1 && dt.columns( { selected: true } ).any() ) {
19750 if ( $.inArray( 'cells', config.limitTo ) !== -1 && dt.cells( { selected: true } ).any() ) {
19757 var _buttonNamespace = 0;
19759 $.extend( DataTable.ext.buttons, {
19761 text: i18n( 'selected', 'Selected' ),
19762 className: 'buttons-selected',
19763 limitTo: [ 'rows', 'columns', 'cells' ],
19764 init: function ( dt, node, config ) {
19766 config._eventNamespace = '.select'+(_buttonNamespace++);
19768 // .DT namespace listeners are removed by DataTables automatically
19769 // on table destroy
19770 dt.on( namespacedEvents(config), function () {
19771 that.enable( enabled(dt, config) );
19776 destroy: function ( dt, node, config ) {
19777 dt.off( config._eventNamespace );
19781 text: i18n( 'selectedSingle', 'Selected single' ),
19782 className: 'buttons-selected-single',
19783 init: function ( dt, node, config ) {
19785 config._eventNamespace = '.select'+(_buttonNamespace++);
19787 dt.on( namespacedEvents(config), function () {
19788 var count = dt.rows( { selected: true } ).flatten().length +
19789 dt.columns( { selected: true } ).flatten().length +
19790 dt.cells( { selected: true } ).flatten().length;
19792 that.enable( count === 1 );
19797 destroy: function ( dt, node, config ) {
19798 dt.off( config._eventNamespace );
19802 text: i18n( 'selectAll', 'Select all' ),
19803 className: 'buttons-select-all',
19804 action: function () {
19805 var items = this.select.items();
19806 this[ items+'s' ]().select();
19810 text: i18n( 'selectNone', 'Deselect all' ),
19811 className: 'buttons-select-none',
19812 action: function () {
19813 clear( this.settings()[0], true );
19815 init: function ( dt, node, config ) {
19817 config._eventNamespace = '.select'+(_buttonNamespace++);
19819 dt.on( namespacedEvents(config), function () {
19820 var count = dt.rows( { selected: true } ).flatten().length +
19821 dt.columns( { selected: true } ).flatten().length +
19822 dt.cells( { selected: true } ).flatten().length;
19824 that.enable( count > 0 );
19829 destroy: function ( dt, node, config ) {
19830 dt.off( config._eventNamespace );
19835 $.each( [ 'Row', 'Column', 'Cell' ], function ( i, item ) {
19836 var lc = item.toLowerCase();
19838 DataTable.ext.buttons[ 'select'+item+'s' ] = {
19839 text: i18n( 'select'+item+'s', 'Select '+lc+'s' ),
19840 className: 'buttons-select-'+lc+'s',
19841 action: function () {
19842 this.select.items( lc );
19844 init: function ( dt ) {
19847 dt.on( 'selectItems.dt.DT', function ( e, ctx, items ) {
19848 that.active( items === lc );
19856 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
19860 // DataTables creation - check if select has been defined in the options. Note
19861 // this required that the table be in the document! If it isn't then something
19862 // needs to trigger this method unfortunately. The next major release of
19863 // DataTables will rework the events and address this.
19864 $(document).on( 'preInit.dt.dtSelect', function (e, ctx) {
19865 if ( e.namespace !== 'dt' ) {
19869 DataTable.select.init( new DataTable.Api( ctx ) );
19873 return DataTable.select;