PageSourceSearch

https://noltrix.com/lib/dataTables/datatables.js?v=ycNxJOP5J1px6nQ4yu9iWNo8X50CNLorNUpzt_EAgIA

js noltrix.com collected 2026-10-09 23:05:53 UTC 1,390,597 bytes, 41,895 lines download raw bytes

vendor: 6,850 bytes, lines 1-141
1/*
2 * This combined file was created by the DataTables downloader builder:
3 *   https://datatables.net/download
4 *
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.13.4/af-2.5.3/b-2.3.6/b-colvis-2.3.6/b-html5-2.3.6/b-print-2.3.6/cr-1.6.2/fc-4.2.2/fh-3.3.2/kt-2.9.0/r-2.4.1/rg-1.3.1/rr-1.3.3/sc-2.1.1/sb-1.4.2/sp-2.1.2/sl-1.6.2/sr-1.2.2
8 *
9 * Included libraries:
10 *   DataTables 1.13.4, AutoFill 2.5.3, Buttons 2.3.6, Column visibility 2.3.6, HTML5 export 2.3.6, Print view 2.3.6, ColReorder 1.6.2, FixedColumns 4.2.2, FixedHeader 3.3.2, KeyTable 2.9.0, Responsive 2.4.1, RowGroup 1.3.1, RowReorder 1.3.3, Scroller 2.1.1, SearchBuilder 1.4.2, SearchPanes 2.1.2, Select 1.6.2, StateRestore 1.2.2
11 */
12
13/*! DataTables 1.13.4
14 * ©2008-2023 SpryMedia Ltd - datatables.net/license
15 */
16
17/**
18 * @summary     DataTables
19 * @description Paginate, search and order HTML tables
20 * @version     1.13.4
21 * @author      SpryMedia Ltd
22 * @contact     www.datatables.net
23 * @copyright   SpryMedia Ltd.
24 *
25 * This source file is free software, available under the following license:
26 *   MIT license - http://datatables.net/license
27 *
28 * This source file is distributed in the hope that it will be useful, but
29 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
30 * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
31 *
32 * For details please refer to: http://www.datatables.net
33 */
34
35/*jslint evil: true, undef: true, browser: true */
36/*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*/
37
38(function( factory ) {
39	"use strict";
40
41	if ( typeof define === 'function' && define.amd ) {
42		// AMD
43		define( ['jquery'], function ( $ ) {
44			return factory( $, window, document );
45		} );
46	}
47	else if ( typeof exports === 'object' ) {
48		// CommonJS
49		// jQuery's factory checks for a global window - if it isn't present then it
50		// returns a factory function that expects the window object
51		var jq = require('jquery');
52
53		if (typeof window !== 'undefined') {
54			module.exports = function (root, $) {
55				if ( ! root ) {
56					// CommonJS environments without a window global must pass a
57					// root. This will give an error otherwise
58					root = window;
59				}
60
61				if ( ! $ ) {
62					$ = jq( root );
63				}
64
65				return factory( $, root, root.document );
66			};
67		}
68		else {
69			return factory( jq, window, window.document );
70		}
71	}
72	else {
73		// Browser
74		window.DataTable = factory( jQuery, window, document );
75	}
76}
77(function( $, window, document, undefined ) {
78	"use strict";
79
80	
81	var DataTable = function ( selector, options )
82	{
83		// Check if called with a window or jQuery object for DOM less applications
84		// This is for backwards compatibility
85		if (DataTable.factory(selector, options)) {
86			return DataTable;
87		}
88	
89		// When creating with `new`, create a new DataTable, returning the API instance
90		if (this instanceof DataTable) {
91			return $(selector).DataTable(options);
92		}
93		else {
94			// Argument switching
95			options = selector;
96		}
97	
98		/**
99		 * Perform a jQuery selector action on the table's TR elements (from the tbody) and
100		 * return the resulting jQuery object.
101		 *  @param {string|node|jQuery} sSelector jQuery selector or node collection to act on
102		 *  @param {object} [oOpts] Optional parameters for modifying the rows to be included
103		 *  @param {string} [oOpts.filter=none] Select TR elements that meet the current filter
104		 *    criterion ("applied") or all TR elements (i.e. no filter).
105		 *  @param {string} [oOpts.order=current] Order of the TR elements in the processed array.
106		 *    Can be either 'current', whereby the current sorting of the table is used, or
107		 *    'original' whereby the original order the data was read into the table is used.
108		 *  @param {string} [oOpts.page=all] Limit the selection to the currently displayed page
109		 *    ("current") or not ("all"). If 'current' is given, then order is assumed to be
110		 *    'current' and filter is 'applied', regardless of what they might be given as.
111		 *  @returns {object} jQuery object, filtered by the given selector.
112		 *  @dtopt API
113		 *  @deprecated Since v1.10
114		 *
115		 *  @example
116		 *    $(document).ready(function() {
117		 *      var oTable = $('#example').dataTable();
118		 *
119		 *      // Highlight every second row
120		 *      oTable.$('tr:odd').css('backgroundColor', 'blue');
121		 *    } );
122		 *
123		 *  @example
124		 *    $(document).ready(function() {
125		 *      var oTable = $('#example').dataTable();
126		 *
127		 *      // Filter to rows with 'Webkit' in them, add a background colour and then
128		 *      // remove the filter, thus highlighting the 'Webkit' rows only.
129		 *      oTable.fnFilter('Webkit');
130		 *      oTable.$('tr', {"search": "applied"}).css('backgroundColor', 'blue');
131		 *      oTable.fnFilter('');
132		 *    } );
133		 */
134		this.$ = function ( sSelector, oOpts )
135		{
136			return this.api(true).$( sSelector, oOpts );
137		};
138		
139		
140		/**
141		 * Almost identical to $ in operation, but in this case returns the data for the matched
142		 * rows - as such, the jQuery selector used should match TR row nodes or TD/TH cell nodes
143		 * rather than any descendants, so the data can be obtained for the row/cell. If matching
144		 * rows are found, the data returned is the original data array/object that was used to
145		 * create the row (or a generated array if from a DOM source).
146		 *
147		 * This method is often useful in-combination with $ where both functions are given the
148		 * same parameters and the array indexes will match identically.
149		 *  @param {string|node|jQuery} sSelector jQuery selector or node collection to act on
150		 *  @param {object} [oOpts] Optional parameters for modifying the rows to be included
151		 *  @param {string} [oOpts.filter=none] Select elements that meet the current filter
152		 *    criterion ("applied") or all elements (i.e. no filter).
153		 *  @param {string} [oOpts.order=current] Order of the data in the processed array.
154		 *    Can be either 'current', whereby the current sorting of the table is used, or
155		 *    'original' whereby the original order the data was read into the table is used.
156		 *  @param {string} [oOpts.page=all] Limit the selection to the currently displayed page
157		 *    ("current") or not ("all"). If 'current' is given, then order is assumed to be
158		 *    'current' and filter is 'applied', regardless of what they might be given as.
159		 *  @returns {array} Data for the matched elements. If any elements, as a result of the
160		 *    selector, were not TR, TD or TH elements in the DataTable, they will have a null
161		 *    entry in the array.
162		 *  @dtopt API
163		 *  @deprecated Since v1.10
164		 *
165		 *  @example
166		 *    $(document).ready(function() {
167		 *      var oTable = $('#example').dataTable();
168		 *
169		 *      // Get the data from the first row in the table
170		 *      var data = oTable._('tr:first');
171		 *
172		 *      // Do something useful with the data
173		 *      alert( "First cell is: "+data[0] );
174		 *    } );
175		 *
176		 *  @example
177		 *    $(document).ready(function() {
178		 *      var oTable = $('#example').dataTable();
179		 *
180		 *      // Filter to 'Webkit' and get all data for
181		 *      oTable.fnFilter('Webkit');
182		 *      var data = oTable._('tr', {"search": "applied"});
183		 *
184		 *      // Do something with the data
185		 *      alert( data.length+" rows matched the search" );
186		 *    } );
187		 */
188		this._ = function ( sSelector, oOpts )
189		{
190			return this.api(true).rows( sSelector, oOpts ).data();
191		};
192		
193		
194		/**
195		 * Create a DataTables Api instance, with the currently selected tables for
196		 * the Api's context.
197		 * @param {boolean} [traditional=false] Set the API instance's context to be
198		 *   only the table referred to by the `DataTable.ext.iApiIndex` option, as was
199		 *   used in the API presented by DataTables 1.9- (i.e. the traditional mode),
200		 *   or if all tables captured in the jQuery object should be used.
201		 * @return {DataTables.Api}
202		 */
203		this.api = function ( traditional )
204		{
205			return traditional ?
206				new _Api(
207					_fnSettingsFromNode( this[ _ext.iApiIndex ] )
208				) :
209				new _Api( this );
210		};
211		
212		
213		/**
214		 * Add a single new row or multiple rows of data to the table. Please note
215		 * that this is suitable for client-side processing only - if you are using
216		 * server-side processing (i.e. "bServerSide": true), then to add data, you
217		 * must add it to the data source, i.e. the server-side, through an Ajax call.
218		 *  @param {array|object} data The data to be added to the table. This can be:
219		 *    <ul>
220		 *      <li>1D array of data - add a single row with the data provided</li>
221		 *      <li>2D array of arrays - add multiple rows in a single call</li>
222		 *      <li>object - data object when using <i>mData</i></li>
223		 *      <li>array of objects - multiple data objects when using <i>mData</i></li>
224		 *    </ul>
225		 *  @param {bool} [redraw=true] redraw the table or not
226		 *  @returns {array} An array of integers, representing the list of indexes in
227		 *    <i>aoData</i> ({@link DataTable.models.oSettings}) that have been added to
228		 *    the table.
229		 *  @dtopt API
230		 *  @deprecated Since v1.10
231		 *
232		 *  @example
233		 *    // Global var for counter
234		 *    var giCount = 2;
235		 *
236		 *    $(document).ready(function() {
237		 *      $('#example').dataTable();
238		 *    } );
239		 *
240		 *    function fnClickAddRow() {
241		 *      $('#example').dataTable().fnAddData( [
242		 *        giCount+".1",
243		 *        giCount+".2",
244		 *        giCount+".3",
245		 *        giCount+".4" ]
246		 *      );
247		 *
248		 *      giCount++;
249		 *    }
250		 */
251		this.fnAddData = function( data, redraw )
252		{
253			var api = this.api( true );
254		
255			/* Check if we want to add multiple rows or not */
256			var rows = Array.isArray(data) && ( Array.isArray(data[0]) || $.isPlainObject(data[0]) ) ?
257				api.rows.add( data ) :
258				api.row.add( data );
259		
260			if ( redraw === undefined || redraw ) {
261				api.draw();
262			}
263		
264			return rows.flatten().toArray();
265		};
266		
267		
268		/**
269		 * This function will make DataTables recalculate the column sizes, based on the data
270		 * contained in the table and the sizes applied to the columns (in the DOM, CSS or
271		 * through the sWidth parameter). This can be useful when the width of the table's
vendor: 200,873 bytes, lines 272-7019
272		 * parent element changes (for example a window resize).
273		 *  @param {boolean} [bRedraw=true] Redraw the table or not, you will typically want to
274		 *  @dtopt API
275		 *  @deprecated Since v1.10
276		 *
277		 *  @example
278		 *    $(document).ready(function() {
279		 *      var oTable = $('#example').dataTable( {
280		 *        "sScrollY": "200px",
281		 *        "bPaginate": false
282		 *      } );
283		 *
284		 *      $(window).on('resize', function () {
285		 *        oTable.fnAdjustColumnSizing();
286		 *      } );
287		 *    } );
288		 */
289		this.fnAdjustColumnSizing = function ( bRedraw )
290		{
291			var api = this.api( true ).columns.adjust();
292			var settings = api.settings()[0];
293			var scroll = settings.oScroll;
294		
295			if ( bRedraw === undefined || bRedraw ) {
296				api.draw( false );
297			}
298			else if ( scroll.sX !== "" || scroll.sY !== "" ) {
299				/* If not redrawing, but scrolling, we want to apply the new column sizes anyway */
300				_fnScrollDraw( settings );
301			}
302		};
303		
304		
305		/**
306		 * Quickly and simply clear a table
307		 *  @param {bool} [bRedraw=true] redraw the table or not
308		 *  @dtopt API
309		 *  @deprecated Since v1.10
310		 *
311		 *  @example
312		 *    $(document).ready(function() {
313		 *      var oTable = $('#example').dataTable();
314		 *
315		 *      // Immediately 'nuke' the current rows (perhaps waiting for an Ajax callback...)
316		 *      oTable.fnClearTable();
317		 *    } );
318		 */
319		this.fnClearTable = function( bRedraw )
320		{
321			var api = this.api( true ).clear();
322		
323			if ( bRedraw === undefined || bRedraw ) {
324				api.draw();
325			}
326		};
327		
328		
329		/**
330		 * The exact opposite of 'opening' a row, this function will close any rows which
331		 * are currently 'open'.
332		 *  @param {node} nTr the table row to 'close'
333		 *  @returns {int} 0 on success, or 1 if failed (can't find the row)
334		 *  @dtopt API
335		 *  @deprecated Since v1.10
336		 *
337		 *  @example
338		 *    $(document).ready(function() {
339		 *      var oTable;
340		 *
341		 *      // 'open' an information row when a row is clicked on
342		 *      $('#example tbody tr').click( function () {
343		 *        if ( oTable.fnIsOpen(this) ) {
344		 *          oTable.fnClose( this );
345		 *        } else {
346		 *          oTable.fnOpen( this, "Temporary row opened", "info_row" );
347		 *        }
348		 *      } );
349		 *
350		 *      oTable = $('#example').dataTable();
351		 *    } );
352		 */
353		this.fnClose = function( nTr )
354		{
355			this.api( true ).row( nTr ).child.hide();
356		};
357		
358		
359		/**
360		 * Remove a row for the table
361		 *  @param {mixed} target The index of the row from aoData to be deleted, or
362		 *    the TR element you want to delete
363		 *  @param {function|null} [callBack] Callback function
364		 *  @param {bool} [redraw=true] Redraw the table or not
365		 *  @returns {array} The row that was deleted
366		 *  @dtopt API
367		 *  @deprecated Since v1.10
368		 *
369		 *  @example
370		 *    $(document).ready(function() {
371		 *      var oTable = $('#example').dataTable();
372		 *
373		 *      // Immediately remove the first row
374		 *      oTable.fnDeleteRow( 0 );
375		 *    } );
376		 */
377		this.fnDeleteRow = function( target, callback, redraw )
378		{
379			var api = this.api( true );
380			var rows = api.rows( target );
381			var settings = rows.settings()[0];
382			var data = settings.aoData[ rows[0][0] ];
383		
384			rows.remove();
385		
386			if ( callback ) {
387				callback.call( this, settings, data );
388			}
389		
390			if ( redraw === undefined || redraw ) {
391				api.draw();
392			}
393		
394			return data;
395		};
396		
397		
398		/**
399		 * Restore the table to it's original state in the DOM by removing all of DataTables
400		 * enhancements, alterations to the DOM structure of the table and event listeners.
401		 *  @param {boolean} [remove=false] Completely remove the table from the DOM
402		 *  @dtopt API
403		 *  @deprecated Since v1.10
404		 *
405		 *  @example
406		 *    $(document).ready(function() {
407		 *      // This example is fairly pointless in reality, but shows how fnDestroy can be used
408		 *      var oTable = $('#example').dataTable();
409		 *      oTable.fnDestroy();
410		 *    } );
411		 */
412		this.fnDestroy = function ( remove )
413		{
414			this.api( true ).destroy( remove );
415		};
416		
417		
418		/**
419		 * Redraw the table
420		 *  @param {bool} [complete=true] Re-filter and resort (if enabled) the table before the draw.
421		 *  @dtopt API
422		 *  @deprecated Since v1.10
423		 *
424		 *  @example
425		 *    $(document).ready(function() {
426		 *      var oTable = $('#example').dataTable();
427		 *
428		 *      // Re-draw the table - you wouldn't want to do it here, but it's an example :-)
429		 *      oTable.fnDraw();
430		 *    } );
431		 */
432		this.fnDraw = function( complete )
433		{
434			// Note that this isn't an exact match to the old call to _fnDraw - it takes
435			// into account the new data, but can hold position.
436			this.api( true ).draw( complete );
437		};
438		
439		
440		/**
441		 * Filter the input based on data
442		 *  @param {string} sInput String to filter the table on
443		 *  @param {int|null} [iColumn] Column to limit filtering to
444		 *  @param {bool} [bRegex=false] Treat as regular expression or not
445		 *  @param {bool} [bSmart=true] Perform smart filtering or not
446		 *  @param {bool} [bShowGlobal=true] Show the input global filter in it's input box(es)
447		 *  @param {bool} [bCaseInsensitive=true] Do case-insensitive matching (true) or not (false)
448		 *  @dtopt API
449		 *  @deprecated Since v1.10
450		 *
451		 *  @example
452		 *    $(document).ready(function() {
453		 *      var oTable = $('#example').dataTable();
454		 *
455		 *      // Sometime later - filter...
456		 *      oTable.fnFilter( 'test string' );
457		 *    } );
458		 */
459		this.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal, bCaseInsensitive )
460		{
461			var api = this.api( true );
462		
463			if ( iColumn === null || iColumn === undefined ) {
464				api.search( sInput, bRegex, bSmart, bCaseInsensitive );
465			}
466			else {
467				api.column( iColumn ).search( sInput, bRegex, bSmart, bCaseInsensitive );
468			}
469		
470			api.draw();
471		};
472		
473		
474		/**
475		 * Get the data for the whole table, an individual row or an individual cell based on the
476		 * provided parameters.
477		 *  @param {int|node} [src] A TR row node, TD/TH cell node or an integer. If given as
478		 *    a TR node then the data source for the whole row will be returned. If given as a
479		 *    TD/TH cell node then iCol will be automatically calculated and the data for the
480		 *    cell returned. If given as an integer, then this is treated as the aoData internal
481		 *    data index for the row (see fnGetPosition) and the data for that row used.
482		 *  @param {int} [col] Optional column index that you want the data of.
483		 *  @returns {array|object|string} If mRow is undefined, then the data for all rows is
484		 *    returned. If mRow is defined, just data for that row, and is iCol is
485		 *    defined, only data for the designated cell is returned.
486		 *  @dtopt API
487		 *  @deprecated Since v1.10
488		 *
489		 *  @example
490		 *    // Row data
491		 *    $(document).ready(function() {
492		 *      oTable = $('#example').dataTable();
493		 *
494		 *      oTable.$('tr').click( function () {
495		 *        var data = oTable.fnGetData( this );
496		 *        // ... do something with the array / object of data for the row
497		 *      } );
498		 *    } );
499		 *
500		 *  @example
501		 *    // Individual cell data
502		 *    $(document).ready(function() {
503		 *      oTable = $('#example').dataTable();
504		 *
505		 *      oTable.$('td').click( function () {
506		 *        var sData = oTable.fnGetData( this );
507		 *        alert( 'The cell clicked on had the value of '+sData );
508		 *      } );
509		 *    } );
510		 */
511		this.fnGetData = function( src, col )
512		{
513			var api = this.api( true );
514		
515			if ( src !== undefined ) {
516				var type = src.nodeName ? src.nodeName.toLowerCase() : '';
517		
518				return col !== undefined || type == 'td' || type == 'th' ?
519					api.cell( src, col ).data() :
520					api.row( src ).data() || null;
521			}
522		
523			return api.data().toArray();
524		};
525		
526		
527		/**
528		 * Get an array of the TR nodes that are used in the table's body. Note that you will
529		 * typically want to use the '$' API method in preference to this as it is more
530		 * flexible.
531		 *  @param {int} [iRow] Optional row index for the TR element you want
532		 *  @returns {array|node} If iRow is undefined, returns an array of all TR elements
533		 *    in the table's body, or iRow is defined, just the TR element requested.
534		 *  @dtopt API
535		 *  @deprecated Since v1.10
536		 *
537		 *  @example
538		 *    $(document).ready(function() {
539		 *      var oTable = $('#example').dataTable();
540		 *
541		 *      // Get the nodes from the table
542		 *      var nNodes = oTable.fnGetNodes( );
543		 *    } );
544		 */
545		this.fnGetNodes = function( iRow )
546		{
547			var api = this.api( true );
548		
549			return iRow !== undefined ?
550				api.row( iRow ).node() :
551				api.rows().nodes().flatten().toArray();
552		};
553		
554		
555		/**
556		 * Get the array indexes of a particular cell from it's DOM element
557		 * and column index including hidden columns
558		 *  @param {node} node this can either be a TR, TD or TH in the table's body
559		 *  @returns {int} If nNode is given as a TR, then a single index is returned, or
560		 *    if given as a cell, an array of [row index, column index (visible),
561		 *    column index (all)] is given.
562		 *  @dtopt API
563		 *  @deprecated Since v1.10
564		 *
565		 *  @example
566		 *    $(document).ready(function() {
567		 *      $('#example tbody td').click( function () {
568		 *        // Get the position of the current data from the node
569		 *        var aPos = oTable.fnGetPosition( this );
570		 *
571		 *        // Get the data array for this row
572		 *        var aData = oTable.fnGetData( aPos[0] );
573		 *
574		 *        // Update the data array and return the value
575		 *        aData[ aPos[1] ] = 'clicked';
576		 *        this.innerHTML = 'clicked';
577		 *      } );
578		 *
579		 *      // Init DataTables
580		 *      oTable = $('#example').dataTable();
581		 *    } );
582		 */
583		this.fnGetPosition = function( node )
584		{
585			var api = this.api( true );
586			var nodeName = node.nodeName.toUpperCase();
587		
588			if ( nodeName == 'TR' ) {
589				return api.row( node ).index();
590			}
591			else if ( nodeName == 'TD' || nodeName == 'TH' ) {
592				var cell = api.cell( node ).index();
593		
594				return [
595					cell.row,
596					cell.columnVisible,
597					cell.column
598				];
599			}
600			return null;
601		};
602		
603		
604		/**
605		 * Check to see if a row is 'open' or not.
606		 *  @param {node} nTr the table row to check
607		 *  @returns {boolean} true if the row is currently open, false otherwise
608		 *  @dtopt API
609		 *  @deprecated Since v1.10
610		 *
611		 *  @example
612		 *    $(document).ready(function() {
613		 *      var oTable;
614		 *
615		 *      // 'open' an information row when a row is clicked on
616		 *      $('#example tbody tr').click( function () {
617		 *        if ( oTable.fnIsOpen(this) ) {
618		 *          oTable.fnClose( this );
619		 *        } else {
620		 *          oTable.fnOpen( this, "Temporary row opened", "info_row" );
621		 *        }
622		 *      } );
623		 *
624		 *      oTable = $('#example').dataTable();
625		 *    } );
626		 */
627		this.fnIsOpen = function( nTr )
628		{
629			return this.api( true ).row( nTr ).child.isShown();
630		};
631		
632		
633		/**
634		 * This function will place a new row directly after a row which is currently
635		 * on display on the page, with the HTML contents that is passed into the
636		 * function. This can be used, for example, to ask for confirmation that a
637		 * particular record should be deleted.
638		 *  @param {node} nTr The table row to 'open'
639		 *  @param {string|node|jQuery} mHtml The HTML to put into the row
640		 *  @param {string} sClass Class to give the new TD cell
641		 *  @returns {node} The row opened. Note that if the table row passed in as the
642		 *    first parameter, is not found in the table, this method will silently
643		 *    return.
644		 *  @dtopt API
645		 *  @deprecated Since v1.10
646		 *
647		 *  @example
648		 *    $(document).ready(function() {
649		 *      var oTable;
650		 *
651		 *      // 'open' an information row when a row is clicked on
652		 *      $('#example tbody tr').click( function () {
653		 *        if ( oTable.fnIsOpen(this) ) {
654		 *          oTable.fnClose( this );
655		 *        } else {
656		 *          oTable.fnOpen( this, "Temporary row opened", "info_row" );
657		 *        }
658		 *      } );
659		 *
660		 *      oTable = $('#example').dataTable();
661		 *    } );
662		 */
663		this.fnOpen = function( nTr, mHtml, sClass )
664		{
665			return this.api( true )
666				.row( nTr )
667				.child( mHtml, sClass )
668				.show()
669				.child()[0];
670		};
671		
672		
673		/**
674		 * Change the pagination - provides the internal logic for pagination in a simple API
675		 * function. With this function you can have a DataTables table go to the next,
676		 * previous, first or last pages.
677		 *  @param {string|int} mAction Paging action to take: "first", "previous", "next" or "last"
678		 *    or page number to jump to (integer), note that page 0 is the first page.
679		 *  @param {bool} [bRedraw=true] Redraw the table or not
680		 *  @dtopt API
681		 *  @deprecated Since v1.10
682		 *
683		 *  @example
684		 *    $(document).ready(function() {
685		 *      var oTable = $('#example').dataTable();
686		 *      oTable.fnPageChange( 'next' );
687		 *    } );
688		 */
689		this.fnPageChange = function ( mAction, bRedraw )
690		{
691			var api = this.api( true ).page( mAction );
692		
693			if ( bRedraw === undefined || bRedraw ) {
694				api.draw(false);
695			}
696		};
697		
698		
699		/**
700		 * Show a particular column
701		 *  @param {int} iCol The column whose display should be changed
702		 *  @param {bool} bShow Show (true) or hide (false) the column
703		 *  @param {bool} [bRedraw=true] Redraw the table or not
704		 *  @dtopt API
705		 *  @deprecated Since v1.10
706		 *
707		 *  @example
708		 *    $(document).ready(function() {
709		 *      var oTable = $('#example').dataTable();
710		 *
711		 *      // Hide the second column after initialisation
712		 *      oTable.fnSetColumnVis( 1, false );
713		 *    } );
714		 */
715		this.fnSetColumnVis = function ( iCol, bShow, bRedraw )
716		{
717			var api = this.api( true ).column( iCol ).visible( bShow );
718		
719			if ( bRedraw === undefined || bRedraw ) {
720				api.columns.adjust().draw();
721			}
722		};
723		
724		
725		/**
726		 * Get the settings for a particular table for external manipulation
727		 *  @returns {object} DataTables settings object. See
728		 *    {@link DataTable.models.oSettings}
729		 *  @dtopt API
730		 *  @deprecated Since v1.10
731		 *
732		 *  @example
733		 *    $(document).ready(function() {
734		 *      var oTable = $('#example').dataTable();
735		 *      var oSettings = oTable.fnSettings();
736		 *
737		 *      // Show an example parameter from the settings
738		 *      alert( oSettings._iDisplayStart );
739		 *    } );
740		 */
741		this.fnSettings = function()
742		{
743			return _fnSettingsFromNode( this[_ext.iApiIndex] );
744		};
745		
746		
747		/**
748		 * Sort the table by a particular column
749		 *  @param {int} iCol the data index to sort on. Note that this will not match the
750		 *    'display index' if you have hidden data entries
751		 *  @dtopt API
752		 *  @deprecated Since v1.10
753		 *
754		 *  @example
755		 *    $(document).ready(function() {
756		 *      var oTable = $('#example').dataTable();
757		 *
758		 *      // Sort immediately with columns 0 and 1
759		 *      oTable.fnSort( [ [0,'asc'], [1,'asc'] ] );
760		 *    } );
761		 */
762		this.fnSort = function( aaSort )
763		{
764			this.api( true ).order( aaSort ).draw();
765		};
766		
767		
768		/**
769		 * Attach a sort listener to an element for a given column
770		 *  @param {node} nNode the element to attach the sort listener to
771		 *  @param {int} iColumn the column that a click on this node will sort on
772		 *  @param {function} [fnCallback] callback function when sort is run
773		 *  @dtopt API
774		 *  @deprecated Since v1.10
775		 *
776		 *  @example
777		 *    $(document).ready(function() {
778		 *      var oTable = $('#example').dataTable();
779		 *
780		 *      // Sort on column 1, when 'sorter' is clicked on
781		 *      oTable.fnSortListener( document.getElementById('sorter'), 1 );
782		 *    } );
783		 */
784		this.fnSortListener = function( nNode, iColumn, fnCallback )
785		{
786			this.api( true ).order.listener( nNode, iColumn, fnCallback );
787		};
788		
789		
790		/**
791		 * Update a table cell or row - this method will accept either a single value to
792		 * update the cell with, an array of values with one element for each column or
793		 * an object in the same format as the original data source. The function is
794		 * self-referencing in order to make the multi column updates easier.
795		 *  @param {object|array|string} mData Data to update the cell/row with
796		 *  @param {node|int} mRow TR element you want to update or the aoData index
797		 *  @param {int} [iColumn] The column to update, give as null or undefined to
798		 *    update a whole row.
799		 *  @param {bool} [bRedraw=true] Redraw the table or not
800		 *  @param {bool} [bAction=true] Perform pre-draw actions or not
801		 *  @returns {int} 0 on success, 1 on error
802		 *  @dtopt API
803		 *  @deprecated Since v1.10
804		 *
805		 *  @example
806		 *    $(document).ready(function() {
807		 *      var oTable = $('#example').dataTable();
808		 *      oTable.fnUpdate( 'Example update', 0, 0 ); // Single cell
809		 *      oTable.fnUpdate( ['a', 'b', 'c', 'd', 'e'], $('tbody tr')[0] ); // Row
810		 *    } );
811		 */
812		this.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction )
813		{
814			var api = this.api( true );
815		
816			if ( iColumn === undefined || iColumn === null ) {
817				api.row( mRow ).data( mData );
818			}
819			else {
820				api.cell( mRow, iColumn ).data( mData );
821			}
822		
823			if ( bAction === undefined || bAction ) {
824				api.columns.adjust();
825			}
826		
827			if ( bRedraw === undefined || bRedraw ) {
828				api.draw();
829			}
830			return 0;
831		};
832		
833		
834		/**
835		 * Provide a common method for plug-ins to check the version of DataTables being used, in order
836		 * to ensure compatibility.
837		 *  @param {string} sVersion Version string to check for, in the format "X.Y.Z". Note that the
838		 *    formats "X" and "X.Y" are also acceptable.
839		 *  @returns {boolean} true if this version of DataTables is greater or equal to the required
840		 *    version, or false if this version of DataTales is not suitable
841		 *  @method
842		 *  @dtopt API
843		 *  @deprecated Since v1.10
844		 *
845		 *  @example
846		 *    $(document).ready(function() {
847		 *      var oTable = $('#example').dataTable();
848		 *      alert( oTable.fnVersionCheck( '1.9.0' ) );
849		 *    } );
850		 */
851		this.fnVersionCheck = _ext.fnVersionCheck;
852		
853	
854		var _that = this;
855		var emptyInit = options === undefined;
856		var len = this.length;
857	
858		if ( emptyInit ) {
859			options = {};
860		}
861	
862		this.oApi = this.internal = _ext.internal;
863	
864		// Extend with old style plug-in API methods
865		for ( var fn in DataTable.ext.internal ) {
866			if ( fn ) {
867				this[fn] = _fnExternApiFunc(fn);
868			}
869		}
870	
871		this.each(function() {
872			// For each initialisation we want to give it a clean initialisation
873			// object that can be bashed around
874			var o = {};
875			var oInit = len > 1 ? // optimisation for single table case
876				_fnExtend( o, options, true ) :
877				options;
878	
879			/*global oInit,_that,emptyInit*/
880			var i=0, iLen, j, jLen, k, kLen;
881			var sId = this.getAttribute( 'id' );
882			var bInitHandedOff = false;
883			var defaults = DataTable.defaults;
884			var $this = $(this);
885			
886			
887			/* Sanity check */
888			if ( this.nodeName.toLowerCase() != 'table' )
889			{
890				_fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 );
891				return;
892			}
893			
894			/* Backwards compatibility for the defaults */
895			_fnCompatOpts( defaults );
896			_fnCompatCols( defaults.column );
897			
898			/* Convert the camel-case defaults to Hungarian */
899			_fnCamelToHungarian( defaults, defaults, true );
900			_fnCamelToHungarian( defaults.column, defaults.column, true );
901			
902			/* Setting up the initialisation object */
903			_fnCamelToHungarian( defaults, $.extend( oInit, $this.data() ), true );
904			
905			
906			
907			/* Check to see if we are re-initialising a table */
908			var allSettings = DataTable.settings;
909			for ( i=0, iLen=allSettings.length ; i<iLen ; i++ )
910			{
911				var s = allSettings[i];
912			
913				/* Base check on table node */
914				if (
915					s.nTable == this ||
916					(s.nTHead && s.nTHead.parentNode == this) ||
917					(s.nTFoot && s.nTFoot.parentNode == this)
918				) {
919					var bRetrieve = oInit.bRetrieve !== undefined ? oInit.bRetrieve : defaults.bRetrieve;
920					var bDestroy = oInit.bDestroy !== undefined ? oInit.bDestroy : defaults.bDestroy;
921			
922					if ( emptyInit || bRetrieve )
923					{
924						return s.oInstance;
925					}
926					else if ( bDestroy )
927					{
928						s.oInstance.fnDestroy();
929						break;
930					}
931					else
932					{
933						_fnLog( s, 0, 'Cannot reinitialise DataTable', 3 );
934						return;
935					}
936				}
937			
938				/* If the element we are initialising has the same ID as a table which was previously
939				 * initialised, but the table nodes don't match (from before) then we destroy the old
940				 * instance by simply deleting it. This is under the assumption that the table has been
941				 * destroyed by other methods. Anyone using non-id selectors will need to do this manually
942				 */
943				if ( s.sTableId == this.id )
944				{
945					allSettings.splice( i, 1 );
946					break;
947				}
948			}
949			
950			/* Ensure the table has an ID - required for accessibility */
951			if ( sId === null || sId === "" )
952			{
953				sId = "DataTables_Table_"+(DataTable.ext._unique++);
954				this.id = sId;
955			}
956			
957			/* Create the settings object for this table and set some of the default parameters */
958			var oSettings = $.extend( true, {}, DataTable.models.oSettings, {
959				"sDestroyWidth": $this[0].style.width,
960				"sInstance":     sId,
961				"sTableId":      sId
962			} );
963			oSettings.nTable = this;
964			oSettings.oApi   = _that.internal;
965			oSettings.oInit  = oInit;
966			
967			allSettings.push( oSettings );
968			
969			// Need to add the instance after the instance after the settings object has been added
970			// to the settings array, so we can self reference the table instance if more than one
971			oSettings.oInstance = (_that.length===1) ? _that : $this.dataTable();
972			
973			// Backwards compatibility, before we apply all the defaults
974			_fnCompatOpts( oInit );
975			_fnLanguageCompat( oInit.oLanguage );
976			
977			// If the length menu is given, but the init display length is not, use the length menu
978			if ( oInit.aLengthMenu && ! oInit.iDisplayLength )
979			{
980				oInit.iDisplayLength = Array.isArray( oInit.aLengthMenu[0] ) ?
981					oInit.aLengthMenu[0][0] : oInit.aLengthMenu[0];
982			}
983			
984			// Apply the defaults and init options to make a single init object will all
985			// options defined from defaults and instance options.
986			oInit = _fnExtend( $.extend( true, {}, defaults ), oInit );
987			
988			
989			// Map the initialisation options onto the settings object
990			_fnMap( oSettings.oFeatures, oInit, [
991				"bPaginate",
992				"bLengthChange",
993				"bFilter",
994				"bSort",
995				"bSortMulti",
996				"bInfo",
997				"bProcessing",
998				"bAutoWidth",
999				"bSortClasses",
1000				"bServerSide",
1001				"bDeferRender"
1002			] );
1003			_fnMap( oSettings, oInit, [
1004				"asStripeClasses",
1005				"ajax",
1006				"fnServerData",
1007				"fnFormatNumber",
1008				"sServerMethod",
1009				"aaSorting",
1010				"aaSortingFixed",
1011				"aLengthMenu",
1012				"sPaginationType",
1013				"sAjaxSource",
1014				"sAjaxDataProp",
1015				"iStateDuration",
1016				"sDom",
1017				"bSortCellsTop",
1018				"iTabIndex",
1019				"fnStateLoadCallback",
1020				"fnStateSaveCallback",
1021				"renderer",
1022				"searchDelay",
1023				"rowId",
1024				[ "iCookieDuration", "iStateDuration" ], // backwards compat
1025				[ "oSearch", "oPreviousSearch" ],
1026				[ "aoSearchCols", "aoPreSearchCols" ],
1027				[ "iDisplayLength", "_iDisplayLength" ]
1028			] );
1029			_fnMap( oSettings.oScroll, oInit, [
1030				[ "sScrollX", "sX" ],
1031				[ "sScrollXInner", "sXInner" ],
1032				[ "sScrollY", "sY" ],
1033				[ "bScrollCollapse", "bCollapse" ]
1034			] );
1035			_fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" );
1036			
1037			/* Callback functions which are array driven */
1038			_fnCallbackReg( oSettings, 'aoDrawCallback',       oInit.fnDrawCallback,      'user' );
1039			_fnCallbackReg( oSettings, 'aoServerParams',       oInit.fnServerParams,      'user' );
1040			_fnCallbackReg( oSettings, 'aoStateSaveParams',    oInit.fnStateSaveParams,   'user' );
1041			_fnCallbackReg( oSettings, 'aoStateLoadParams',    oInit.fnStateLoadParams,   'user' );
1042			_fnCallbackReg( oSettings, 'aoStateLoaded',        oInit.fnStateLoaded,       'user' );
1043			_fnCallbackReg( oSettings, 'aoRowCallback',        oInit.fnRowCallback,       'user' );
1044			_fnCallbackReg( oSettings, 'aoRowCreatedCallback', oInit.fnCreatedRow,        'user' );
1045			_fnCallbackReg( oSettings, 'aoHeaderCallback',     oInit.fnHeaderCallback,    'user' );
1046			_fnCallbackReg( oSettings, 'aoFooterCallback',     oInit.fnFooterCallback,    'user' );
1047			_fnCallbackReg( oSettings, 'aoInitComplete',       oInit.fnInitComplete,      'user' );
1048			_fnCallbackReg( oSettings, 'aoPreDrawCallback',    oInit.fnPreDrawCallback,   'user' );
1049			
1050			oSettings.rowIdFn = _fnGetObjectDataFn( oInit.rowId );
1051			
1052			/* Browser support detection */
1053			_fnBrowserDetect( oSettings );
1054			
1055			var oClasses = oSettings.oClasses;
1056			
1057			$.extend( oClasses, DataTable.ext.classes, oInit.oClasses );
1058			$this.addClass( oClasses.sTable );
1059			
1060			
1061			if ( oSettings.iInitDisplayStart === undefined )
1062			{
1063				/* Display start point, taking into account the save saving */
1064				oSettings.iInitDisplayStart = oInit.iDisplayStart;
1065				oSettings._iDisplayStart = oInit.iDisplayStart;
1066			}
1067			
1068			if ( oInit.iDeferLoading !== null )
1069			{
1070				oSettings.bDeferLoading = true;
1071				var tmp = Array.isArray( oInit.iDeferLoading );
1072				oSettings._iRecordsDisplay = tmp ? oInit.iDeferLoading[0] : oInit.iDeferLoading;
1073				oSettings._iRecordsTotal = tmp ? oInit.iDeferLoading[1] : oInit.iDeferLoading;
1074			}
1075			
1076			/* Language definitions */
1077			var oLanguage = oSettings.oLanguage;
1078			$.extend( true, oLanguage, oInit.oLanguage );
1079			
1080			if ( oLanguage.sUrl )
1081			{
1082				/* Get the language definitions from a file - because this Ajax call makes the language
1083				 * get async to the remainder of this function we use bInitHandedOff to indicate that
1084				 * _fnInitialise will be fired by the returned Ajax handler, rather than the constructor
1085				 */
1086				$.ajax( {
1087					dataType: 'json',
1088					url: oLanguage.sUrl,
1089					success: function ( json ) {
1090						_fnCamelToHungarian( defaults.oLanguage, json );
1091						_fnLanguageCompat( json );
1092						$.extend( true, oLanguage, json, oSettings.oInit.oLanguage );
1093			
1094						_fnCallbackFire( oSettings, null, 'i18n', [oSettings]);
1095						_fnInitialise( oSettings );
1096					},
1097					error: function () {
1098						// Error occurred loading language file, continue on as best we can
1099						_fnInitialise( oSettings );
1100					}
1101				} );
1102				bInitHandedOff = true;
1103			}
1104			else {
1105				_fnCallbackFire( oSettings, null, 'i18n', [oSettings]);
1106			}
1107			
1108			/*
1109			 * Stripes
1110			 */
1111			if ( oInit.asStripeClasses === null )
1112			{
1113				oSettings.asStripeClasses =[
1114					oClasses.sStripeOdd,
1115					oClasses.sStripeEven
1116				];
1117			}
1118			
1119			/* Remove row stripe classes if they are already on the table row */
1120			var stripeClasses = oSettings.asStripeClasses;
1121			var rowOne = $this.children('tbody').find('tr').eq(0);
1122			if ( $.inArray( true, $.map( stripeClasses, function(el, i) {
1123				return rowOne.hasClass(el);
1124			} ) ) !== -1 ) {
1125				$('tbody tr', this).removeClass( stripeClasses.join(' ') );
1126				oSettings.asDestroyStripes = stripeClasses.slice();
1127			}
1128			
1129			/*
1130			 * Columns
1131			 * See if we should load columns automatically or use defined ones
1132			 */
1133			var anThs = [];
1134			var aoColumnsInit;
1135			var nThead = this.getElementsByTagName('thead');
1136			if ( nThead.length !== 0 )
1137			{
1138				_fnDetectHeader( oSettings.aoHeader, nThead[0] );
1139				anThs = _fnGetUniqueThs( oSettings );
1140			}
1141			
1142			/* If not given a column array, generate one with nulls */
1143			if ( oInit.aoColumns === null )
1144			{
1145				aoColumnsInit = [];
1146				for ( i=0, iLen=anThs.length ; i<iLen ; i++ )
1147				{
1148					aoColumnsInit.push( null );
1149				}
1150			}
1151			else
1152			{
1153				aoColumnsInit = oInit.aoColumns;
1154			}
1155			
1156			/* Add the columns */
1157			for ( i=0, iLen=aoColumnsInit.length ; i<iLen ; i++ )
1158			{
1159				_fnAddColumn( oSettings, anThs ? anThs[i] : null );
1160			}
1161			
1162			/* Apply the column definitions */
1163			_fnApplyColumnDefs( oSettings, oInit.aoColumnDefs, aoColumnsInit, function (iCol, oDef) {
1164				_fnColumnOptions( oSettings, iCol, oDef );
1165			} );
1166			
1167			/* HTML5 attribute detection - build an mData object automatically if the
1168			 * attributes are found
1169			 */
1170			if ( rowOne.length ) {
1171				var a = function ( cell, name ) {
1172					return cell.getAttribute( 'data-'+name ) !== null ? name : null;
1173				};
1174			
1175				$( rowOne[0] ).children('th, td').each( function (i, cell) {
1176					var col = oSettings.aoColumns[i];
1177			
1178					if (! col) {
1179						_fnLog( oSettings, 0, 'Incorrect column count', 18 );
1180					}
1181			
1182					if ( col.mData === i ) {
1183						var sort = a( cell, 'sort' ) || a( cell, 'order' );
1184						var filter = a( cell, 'filter' ) || a( cell, 'search' );
1185			
1186						if ( sort !== null || filter !== null ) {
1187							col.mData = {
1188								_:      i+'.display',
1189								sort:   sort !== null   ? i+'.@data-'+sort   : undefined,
1190								type:   sort !== null   ? i+'.@data-'+sort   : undefined,
1191								filter: filter !== null ? i+'.@data-'+filter : undefined
1192							};
1193							col._isArrayHost = true;
1194			
1195							_fnColumnOptions( oSettings, i );
1196						}
1197					}
1198				} );
1199			}
1200			
1201			var features = oSettings.oFeatures;
1202			var loadedInit = function () {
1203				/*
1204				 * Sorting
1205				 * @todo For modularisation (1.11) this needs to do into a sort start up handler
1206				 */
1207			
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];
1214					}
1215				}
1216			
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
1219				 */
1220				_fnSortingClasses( oSettings );
1221			
1222				if ( features.bSort ) {
1223					_fnCallbackReg( oSettings, 'aoDrawCallback', function () {
1224						if ( oSettings.bSorted ) {
1225							var aSort = _fnSortFlatten( oSettings );
1226							var sortedColumns = {};
1227			
1228							$.each( aSort, function (i, val) {
1229								sortedColumns[ val.src ] = val.dir;
1230							} );
1231			
1232							_fnCallbackFire( oSettings, null, 'order', [oSettings, aSort, sortedColumns] );
1233							_fnSortAria( oSettings );
1234						}
1235					} );
1236				}
1237			
1238				_fnCallbackReg( oSettings, 'aoDrawCallback', function () {
1239					if ( oSettings.bSorted || _fnDataSource( oSettings ) === 'ssp' || features.bDeferRender ) {
1240						_fnSortingClasses( oSettings );
1241					}
1242				}, 'sc' );
1243			
1244			
1245				/*
1246				 * Final init
1247				 * Cache the header, body and footer as required, creating them if needed
1248				 */
1249			
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');
1253				} );
1254			
1255				var thead = $this.children('thead');
1256				if ( thead.length === 0 ) {
1257					thead = $('<thead/>').appendTo($this);
1258				}
1259				oSettings.nTHead = thead[0];
1260			
1261				var tbody = $this.children('tbody');
1262				if ( tbody.length === 0 ) {
1263					tbody = $('<tbody/>').insertAfter(thead);
1264				}
1265				oSettings.nTBody = tbody[0];
1266			
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);
1272				}
1273			
1274				if ( tfoot.length === 0 || tfoot.children().length === 0 ) {
1275					$this.addClass( oClasses.sNoFooter );
1276				}
1277				else if ( tfoot.length > 0 ) {
1278					oSettings.nTFoot = tfoot[0];
1279					_fnDetectHeader( oSettings.aoFooter, oSettings.nTFoot );
1280				}
1281			
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 ] );
1286					}
1287				}
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
1292					 */
1293					_fnAddTr( oSettings, $(oSettings.nTBody).children('tr') );
1294				}
1295			
1296				/* Copy the data index array */
1297				oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
1298			
1299				/* Initialisation complete - table can be drawn */
1300				oSettings.bInitialised = true;
1301			
1302				/* Check if we need to initialise the table (it might not have been handed off to the
1303				 * language processor)
1304				 */
1305				if ( bInitHandedOff === false ) {
1306					_fnInitialise( oSettings );
1307				}
1308			};
1309			
1310			/* Must be done after everything which can be overridden by the state saving! */
1311			_fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState, 'state_save' );
1312			
1313			if ( oInit.bStateSave )
1314			{
1315				features.bStateSave = true;
1316				_fnLoadState( oSettings, oInit, loadedInit );
1317			}
1318			else {
1319				loadedInit();
1320			}
1321			
1322		} );
1323		_that = null;
1324		return this;
1325	};
1326	
1327	
1328	/*
1329	 * It is useful to have variables which are scoped locally so only the
1330	 * DataTables functions can access them and they don't leak into global space.
1331	 * At the same time these functions are often useful over multiple files in the
1332	 * core and API, so we list, or at least document, all variables which are used
1333	 * by DataTables as private variables here. This also ensures that there is no
1334	 * clashing of variable names and that they can easily referenced for reuse.
1335	 */
1336	
1337	
1338	// Defined else where
1339	//  _selector_run
1340	//  _selector_opts
1341	//  _selector_first
1342	//  _selector_row_indexes
1343	
1344	var _ext; // DataTable.ext
1345	var _Api; // DataTable.Api
1346	var _api_register; // DataTable.Api.register
1347	var _api_registerPlural; // DataTable.Api.registerPlural
1348	
1349	var _re_dic = {};
1350	var _re_new_lines = /[\r\n\u2028]/g;
1351	var _re_html = /<.*?>/g;
1352	
1353	// This is not strict ISO8601 - Date.parse() is quite lax, although
1354	// implementations differ between browsers.
1355	var _re_date = /^\d{2,4}[\.\/\-]\d{1,2}[\.\/\-]\d{1,2}([T ]{1}\d{1,2}[:\.]\d{2}([\.:]\d{2})?)?$/;
1356	
1357	// Escape regular expression special characters
1358	var _re_escape_regex = new RegExp( '(\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ].join('|\\') + ')', 'g' );
1359	
1360	// http://en.wikipedia.org/wiki/Foreign_exchange_market
1361	// - \u20BD - Russian ruble.
1362	// - \u20a9 - South Korean Won
1363	// - \u20BA - Turkish Lira
1364	// - \u20B9 - Indian Rupee
1365	// - R - Brazil (R$) and South Africa
1366	// - fr - Swiss Franc
1367	// - kr - Swedish krona, Norwegian krone and Danish krone
1368	// - \u2009 is thin space and \u202F is narrow no-break space, both used in many
1369	// - Ƀ - Bitcoin
1370	// - Ξ - Ethereum
1371	//   standards as thousands separators.
1372	var _re_formatted_numeric = /['\u00A0,$£€¥%\u2009\u202F\u20BD\u20a9\u20BArfkɃΞ]/gi;
1373	
1374	
1375	var _empty = function ( d ) {
1376		return !d || d === true || d === '-' ? true : false;
1377	};
1378	
1379	
1380	var _intVal = function ( s ) {
1381		var integer = parseInt( s, 10 );
1382		return !isNaN(integer) && isFinite(s) ? integer : null;
1383	};
1384	
1385	// Convert from a formatted number with characters other than `.` as the
1386	// decimal place, to a Javascript number
1387	var _numToDecimal = function ( num, decimalPoint ) {
1388		// Cache created regular expressions for speed as this function is called often
1389		if ( ! _re_dic[ decimalPoint ] ) {
1390			_re_dic[ decimalPoint ] = new RegExp( _fnEscapeRegex( decimalPoint ), 'g' );
1391		}
1392		return typeof num === 'string' && decimalPoint !== '.' ?
1393			num.replace( /\./g, '' ).replace( _re_dic[ decimalPoint ], '.' ) :
1394			num;
1395	};
1396	
1397	
1398	var _isNumber = function ( d, decimalPoint, formatted ) {
1399		let type = typeof d;
1400		var strType = type === 'string';
1401	
1402		if ( type === 'number' || type === 'bigint') {
1403			return true;
1404		}
1405	
1406		// If empty return immediately so there must be a number if it is a
1407		// formatted string (this stops the string "k", or "kr", etc being detected
1408		// as a formatted number for currency
1409		if ( _empty( d ) ) {
1410			return true;
1411		}
1412	
1413		if ( decimalPoint && strType ) {
1414			d = _numToDecimal( d, decimalPoint );
1415		}
1416	
1417		if ( formatted && strType ) {
1418			d = d.replace( _re_formatted_numeric, '' );
1419		}
1420	
1421		return !isNaN( parseFloat(d) ) && isFinite( d );
1422	};
1423	
1424	
1425	// A string without HTML in it can be considered to be HTML still
1426	var _isHtml = function ( d ) {
1427		return _empty( d ) || typeof d === 'string';
1428	};
1429	
1430	
1431	var _htmlNumeric = function ( d, decimalPoint, formatted ) {
1432		if ( _empty( d ) ) {
1433			return true;
1434		}
1435	
1436		var html = _isHtml( d );
1437		return ! html ?
1438			null :
1439			_isNumber( _stripHtml( d ), decimalPoint, formatted ) ?
1440				true :
1441				null;
1442	};
1443	
1444	
1445	var _pluck = function ( a, prop, prop2 ) {
1446		var out = [];
1447		var i=0, ien=a.length;
1448	
1449		// Could have the test in the loop for slightly smaller code, but speed
1450		// is essential here
1451		if ( prop2 !== undefined ) {
1452			for ( ; i<ien ; i++ ) {
1453				if ( a[i] && a[i][ prop ] ) {
1454					out.push( a[i][ prop ][ prop2 ] );
1455				}
1456			}
1457		}
1458		else {
1459			for ( ; i<ien ; i++ ) {
1460				if ( a[i] ) {
1461					out.push( a[i][ prop ] );
1462				}
1463			}
1464		}
1465	
1466		return out;
1467	};
1468	
1469	
1470	// Basically the same as _pluck, but rather than looping over `a` we use `order`
1471	// as the indexes to pick from `a`
1472	var _pluck_order = function ( a, order, prop, prop2 )
1473	{
1474		var out = [];
1475		var i=0, ien=order.length;
1476	
1477		// Could have the test in the loop for slightly smaller code, but speed
1478		// is essential here
1479		if ( prop2 !== undefined ) {
1480			for ( ; i<ien ; i++ ) {
1481				if ( a[ order[i] ][ prop ] ) {
1482					out.push( a[ order[i] ][ prop ][ prop2 ] );
1483				}
1484			}
1485		}
1486		else {
1487			for ( ; i<ien ; i++ ) {
1488				out.push( a[ order[i] ][ prop ] );
1489			}
1490		}
1491	
1492		return out;
1493	};
1494	
1495	
1496	var _range = function ( len, start )
1497	{
1498		var out = [];
1499		var end;
1500	
1501		if ( start === undefined ) {
1502			start = 0;
1503			end = len;
1504		}
1505		else {
1506			end = start;
1507			start = len;
1508		}
1509	
1510		for ( var i=start ; i<end ; i++ ) {
1511			out.push( i );
1512		}
1513	
1514		return out;
1515	};
1516	
1517	
1518	var _removeEmpty = function ( a )
1519	{
1520		var out = [];
1521	
1522		for ( var i=0, ien=a.length ; i<ien ; i++ ) {
1523			if ( a[i] ) { // careful - will remove all falsy values!
1524				out.push( a[i] );
1525			}
1526		}
1527	
1528		return out;
1529	};
1530	
1531	
1532	var _stripHtml = function ( d ) {
1533		return d.replace( _re_html, '' );
1534	};
1535	
1536	
1537	/**
1538	 * Determine if all values in the array are unique. This means we can short
1539	 * cut the _unique method at the cost of a single loop. A sorted array is used
1540	 * to easily check the values.
1541	 *
1542	 * @param  {array} src Source array
1543	 * @return {boolean} true if all unique, false otherwise
1544	 * @ignore
1545	 */
1546	var _areAllUnique = function ( src ) {
1547		if ( src.length < 2 ) {
1548			return true;
1549		}
1550	
1551		var sorted = src.slice().sort();
1552		var last = sorted[0];
1553	
1554		for ( var i=1, ien=sorted.length ; i<ien ; i++ ) {
1555			if ( sorted[i] === last ) {
1556				return false;
1557			}
1558	
1559			last = sorted[i];
1560		}
1561	
1562		return true;
1563	};
1564	
1565	
1566	/**
1567	 * Find the unique elements in a source array.
1568	 *
1569	 * @param  {array} src Source array
1570	 * @return {array} Array of unique items
1571	 * @ignore
1572	 */
1573	var _unique = function ( src )
1574	{
1575		if ( _areAllUnique( src ) ) {
1576			return src.slice();
1577		}
1578	
1579		// A faster unique method is to use object keys to identify used values,
1580		// but this doesn't work with arrays or objects, which we must also
1581		// consider. See jsperf.com/compare-array-unique-versions/4 for more
1582		// information.
1583		var
1584			out = [],
1585			val,
1586			i, ien=src.length,
1587			j, k=0;
1588	
1589		again: for ( i=0 ; i<ien ; i++ ) {
1590			val = src[i];
1591	
1592			for ( j=0 ; j<k ; j++ ) {
1593				if ( out[j] === val ) {
1594					continue again;
1595				}
1596			}
1597	
1598			out.push( val );
1599			k++;
1600		}
1601	
1602		return out;
1603	};
1604	
1605	// Surprisingly this is faster than [].concat.apply
1606	// https://jsperf.com/flatten-an-array-loop-vs-reduce/2
1607	var _flatten = function (out, val) {
1608		if (Array.isArray(val)) {
1609			for (var i=0 ; i<val.length ; i++) {
1610				_flatten(out, val[i]);
1611			}
1612		}
1613		else {
1614			out.push(val);
1615		}
1616	  
1617		return out;
1618	}
1619	
1620	var _includes = function (search, start) {
1621		if (start === undefined) {
1622			start = 0;
1623		}
1624	
1625		return this.indexOf(search, start) !== -1;	
1626	};
1627	
1628	// Array.isArray polyfill.
1629	// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
1630	if (! Array.isArray) {
1631	    Array.isArray = function(arg) {
1632	        return Object.prototype.toString.call(arg) === '[object Array]';
1633	    };
1634	}
1635	
1636	if (! Array.prototype.includes) {
1637		Array.prototype.includes = _includes;
1638	}
1639	
1640	// .trim() polyfill
1641	// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trim
1642	if (!String.prototype.trim) {
1643	  String.prototype.trim = function () {
1644	    return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
1645	  };
1646	}
1647	
1648	if (! String.prototype.includes) {
1649		String.prototype.includes = _includes;
1650	}
1651	
1652	/**
1653	 * DataTables utility methods
1654	 * 
1655	 * This namespace provides helper methods that DataTables uses internally to
1656	 * create a DataTable, but which are not exclusively used only for DataTables.
1657	 * These methods can be used by extension authors to save the duplication of
1658	 * code.
1659	 *
1660	 *  @namespace
1661	 */
1662	DataTable.util = {
1663		/**
1664		 * Throttle the calls to a function. Arguments and context are maintained
1665		 * for the throttled function.
1666		 *
1667		 * @param {function} fn Function to be called
1668		 * @param {integer} freq Call frequency in mS
1669		 * @return {function} Wrapped function
1670		 */
1671		throttle: function ( fn, freq ) {
1672			var
1673				frequency = freq !== undefined ? freq : 200,
1674				last,
1675				timer;
1676	
1677			return function () {
1678				var
1679					that = this,
1680					now  = +new Date(),
1681					args = arguments;
1682	
1683				if ( last && now < last + frequency ) {
1684					clearTimeout( timer );
1685	
1686					timer = setTimeout( function () {
1687						last = undefined;
1688						fn.apply( that, args );
1689					}, frequency );
1690				}
1691				else {
1692					last = now;
1693					fn.apply( that, args );
1694				}
1695			};
1696		},
1697	
1698	
1699		/**
1700		 * Escape a string such that it can be used in a regular expression
1701		 *
1702		 *  @param {string} val string to escape
1703		 *  @returns {string} escaped string
1704		 */
1705		escapeRegex: function ( val ) {
1706			return val.replace( _re_escape_regex, '\\$1' );
1707		},
1708	
1709		/**
1710		 * Create a function that will write to a nested object or array
1711		 * @param {*} source JSON notation string
1712		 * @returns Write function
1713		 */
1714		set: function ( source ) {
1715			if ( $.isPlainObject( source ) ) {
1716				/* Unlike get, only the underscore (global) option is used for for
1717				 * setting data since we don't know the type here. This is why an object
1718				 * option is not documented for `mData` (which is read/write), but it is
1719				 * for `mRender` which is read only.
1720				 */
1721				return DataTable.util.set( source._ );
1722			}
1723			else if ( source === null ) {
1724				// Nothing to do when the data source is null
1725				return function () {};
1726			}
1727			else if ( typeof source === 'function' ) {
1728				return function (data, val, meta) {
1729					source( data, 'set', val, meta );
1730				};
1731			}
1732			else if ( typeof source === 'string' && (source.indexOf('.') !== -1 ||
1733					  source.indexOf('[') !== -1 || source.indexOf('(') !== -1) )
1734			{
1735				// Like the get, we need to get data from a nested object
1736				var setData = function (data, val, src) {
1737					var a = _fnSplitObjNotation( src ), b;
1738					var aLast = a[a.length-1];
1739					var arrayNotation, funcNotation, o, innerSrc;
1740		
1741					for ( var i=0, iLen=a.length-1 ; i<iLen ; i++ ) {
1742						// Protect against prototype pollution
1743						if (a[i] === '__proto__' || a[i] === 'constructor') {
1744							throw new Error('Cannot set prototype values');
1745						}
1746		
1747						// Check if we are dealing with an array notation request
1748						arrayNotation = a[i].match(__reArray);
1749						funcNotation = a[i].match(__reFn);
1750		
1751						if ( arrayNotation ) {
1752							a[i] = a[i].replace(__reArray, '');
1753							data[ a[i] ] = [];
1754		
1755							// Get the remainder of the nested object to set so we can recurse
1756							b = a.slice();
1757							b.splice( 0, i+1 );
1758							innerSrc = b.join('.');
1759		
1760							// Traverse each entry in the array setting the properties requested
1761							if ( Array.isArray( val ) ) {
1762								for ( var j=0, jLen=val.length ; j<jLen ; j++ ) {
1763									o = {};
1764									setData( o, val[j], innerSrc );
1765									data[ a[i] ].push( o );
1766								}
1767							}
1768							else {
1769								// We've been asked to save data to an array, but it
1770								// isn't array data to be saved. Best that can be done
1771								// is to just save the value.
1772								data[ a[i] ] = val;
1773							}
1774		
1775							// The inner call to setData has already traversed through the remainder
1776							// of the source and has set the data, thus we can exit here
1777							return;
1778						}
1779						else if ( funcNotation ) {
1780							// Function call
1781							a[i] = a[i].replace(__reFn, '');
1782							data = data[ a[i] ]( val );
1783						}
1784		
1785						// If the nested object doesn't currently exist - since we are
1786						// trying to set the value - create it
1787						if ( data[ a[i] ] === null || data[ a[i] ] === undefined ) {
1788							data[ a[i] ] = {};
1789						}
1790						data = data[ a[i] ];
1791					}
1792		
1793					// Last item in the input - i.e, the actual set
1794					if ( aLast.match(__reFn ) ) {
1795						// Function call
1796						data = data[ aLast.replace(__reFn, '') ]( val );
1797					}
1798					else {
1799						// If array notation is used, we just want to strip it and use the property name
1800						// and assign the value. If it isn't used, then we get the result we want anyway
1801						data[ aLast.replace(__reArray, '') ] = val;
1802					}
1803				};
1804		
1805				return function (data, val) { // meta is also passed in, but not used
1806					return setData( data, val, source );
1807				};
1808			}
1809			else {
1810				// Array or flat object mapping
1811				return function (data, val) { // meta is also passed in, but not used
1812					data[source] = val;
1813				};
1814			}
1815		},
1816	
1817		/**
1818		 * Create a function that will read nested objects from arrays, based on JSON notation
1819		 * @param {*} source JSON notation string
1820		 * @returns Value read
1821		 */
1822		get: function ( source ) {
1823			if ( $.isPlainObject( source ) ) {
1824				// Build an object of get functions, and wrap them in a single call
1825				var o = {};
1826				$.each( source, function (key, val) {
1827					if ( val ) {
1828						o[key] = DataTable.util.get( val );
1829					}
1830				} );
1831		
1832				return function (data, type, row, meta) {
1833					var t = o[type] || o._;
1834					return t !== undefined ?
1835						t(data, type, row, meta) :
1836						data;
1837				};
1838			}
1839			else if ( source === null ) {
1840				// Give an empty string for rendering / sorting etc
1841				return function (data) { // type, row and meta also passed, but not used
1842					return data;
1843				};
1844			}
1845			else if ( typeof source === 'function' ) {
1846				return function (data, type, row, meta) {
1847					return source( data, type, row, meta );
1848				};
1849			}
1850			else if ( typeof source === 'string' && (source.indexOf('.') !== -1 ||
1851					  source.indexOf('[') !== -1 || source.indexOf('(') !== -1) )
1852			{
1853				/* If there is a . in the source string then the data source is in a
1854				 * nested object so we loop over the data for each level to get the next
1855				 * level down. On each loop we test for undefined, and if found immediately
1856				 * return. This allows entire objects to be missing and sDefaultContent to
1857				 * be used if defined, rather than throwing an error
1858				 */
1859				var fetchData = function (data, type, src) {
1860					var arrayNotation, funcNotation, out, innerSrc;
1861		
1862					if ( src !== "" ) {
1863						var a = _fnSplitObjNotation( src );
1864		
1865						for ( var i=0, iLen=a.length ; i<iLen ; i++ ) {
1866							// Check if we are dealing with special notation
1867							arrayNotation = a[i].match(__reArray);
1868							funcNotation = a[i].match(__reFn);
1869		
1870							if ( arrayNotation ) {
1871								// Array notation
1872								a[i] = a[i].replace(__reArray, '');
1873		
1874								// Condition allows simply [] to be passed in
1875								if ( a[i] !== "" ) {
1876									data = data[ a[i] ];
1877								}
1878								out = [];
1879		
1880								// Get the remainder of the nested object to get
1881								a.splice( 0, i+1 );
1882								innerSrc = a.join('.');
1883		
1884								// Traverse each entry in the array getting the properties requested
1885								if ( Array.isArray( data ) ) {
1886									for ( var j=0, jLen=data.length ; j<jLen ; j++ ) {
1887										out.push( fetchData( data[j], type, innerSrc ) );
1888									}
1889								}
1890		
1891								// If a string is given in between the array notation indicators, that
1892								// is used to join the strings together, otherwise an array is returned
1893								var join = arrayNotation[0].substring(1, arrayNotation[0].length-1);
1894								data = (join==="") ? out : out.join(join);
1895		
1896								// The inner call to fetchData has already traversed through the remainder
1897								// of the source requested, so we exit from the loop
1898								break;
1899							}
1900							else if ( funcNotation ) {
1901								// Function call
1902								a[i] = a[i].replace(__reFn, '');
1903								data = data[ a[i] ]();
1904								continue;
1905							}
1906		
1907							if ( data === null || data[ a[i] ] === undefined ) {
1908								return undefined;
1909							}
1910	
1911							data = data[ a[i] ];
1912						}
1913					}
1914		
1915					return data;
1916				};
1917		
1918				return function (data, type) { // row and meta also passed, but not used
1919					return fetchData( data, type, source );
1920				};
1921			}
1922			else {
1923				// Array or flat object mapping
1924				return function (data, type) { // row and meta also passed, but not used
1925					return data[source];
1926				};
1927			}
1928		}
1929	};
1930	
1931	
1932	
1933	/**
1934	 * Create a mapping object that allows camel case parameters to be looked up
1935	 * for their Hungarian counterparts. The mapping is stored in a private
1936	 * parameter called `_hungarianMap` which can be accessed on the source object.
1937	 *  @param {object} o
1938	 *  @memberof DataTable#oApi
1939	 */
1940	function _fnHungarianMap ( o )
1941	{
1942		var
1943			hungarian = 'a aa ai ao as b fn i m o s ',
1944			match,
1945			newKey,
1946			map = {};
1947	
1948		$.each( o, function (key, val) {
1949			match = key.match(/^([^A-Z]+?)([A-Z])/);
1950	
1951			if ( match && hungarian.indexOf(match[1]+' ') !== -1 )
1952			{
1953				newKey = key.replace( match[0], match[2].toLowerCase() );
1954				map[ newKey ] = key;
1955	
1956				if ( match[1] === 'o' )
1957				{
1958					_fnHungarianMap( o[key] );
1959				}
1960			}
1961		} );
1962	
1963		o._hungarianMap = map;
1964	}
1965	
1966	
1967	/**
1968	 * Convert from camel case parameters to Hungarian, based on a Hungarian map
1969	 * created by _fnHungarianMap.
1970	 *  @param {object} src The model object which holds all parameters that can be
1971	 *    mapped.
1972	 *  @param {object} user The object to convert from camel case to Hungarian.
1973	 *  @param {boolean} force When set to `true`, properties which already have a
1974	 *    Hungarian value in the `user` object will be overwritten. Otherwise they
1975	 *    won't be.
1976	 *  @memberof DataTable#oApi
1977	 */
1978	function _fnCamelToHungarian ( src, user, force )
1979	{
1980		if ( ! src._hungarianMap ) {
1981			_fnHungarianMap( src );
1982		}
1983	
1984		var hungarianKey;
1985	
1986		$.each( user, function (key, val) {
1987			hungarianKey = src._hungarianMap[ key ];
1988	
1989			if ( hungarianKey !== undefined && (force || user[hungarianKey] === undefined) )
1990			{
1991				// For objects, we need to buzz down into the object to copy parameters
1992				if ( hungarianKey.charAt(0) === 'o' )
1993				{
1994					// Copy the camelCase options over to the hungarian
1995					if ( ! user[ hungarianKey ] ) {
1996						user[ hungarianKey ] = {};
1997					}
1998					$.extend( true, user[hungarianKey], user[key] );
1999	
2000					_fnCamelToHungarian( src[hungarianKey], user[hungarianKey], force );
2001				}
2002				else {
2003					user[hungarianKey] = user[ key ];
2004				}
2005			}
2006		} );
2007	}
2008	
2009	
2010	/**
2011	 * Language compatibility - when certain options are given, and others aren't, we
2012	 * need to duplicate the values over, in order to provide backwards compatibility
2013	 * with older language files.
2014	 *  @param {object} oSettings dataTables settings object
2015	 *  @memberof DataTable#oApi
2016	 */
2017	function _fnLanguageCompat( lang )
2018	{
2019		// Note the use of the Hungarian notation for the parameters in this method as
2020		// this is called after the mapping of camelCase to Hungarian
2021		var defaults = DataTable.defaults.oLanguage;
2022	
2023		// Default mapping
2024		var defaultDecimal = defaults.sDecimal;
2025		if ( defaultDecimal ) {
2026			_addNumericSort( defaultDecimal );
2027		}
2028	
2029		if ( lang ) {
2030			var zeroRecords = lang.sZeroRecords;
2031	
2032			// Backwards compatibility - if there is no sEmptyTable given, then use the same as
2033			// sZeroRecords - assuming that is given.
2034			if ( ! lang.sEmptyTable && zeroRecords &&
2035				defaults.sEmptyTable === "No data available in table" )
2036			{
2037				_fnMap( lang, lang, 'sZeroRecords', 'sEmptyTable' );
2038			}
2039	
2040			// Likewise with loading records
2041			if ( ! lang.sLoadingRecords && zeroRecords &&
2042				defaults.sLoadingRecords === "Loading..." )
2043			{
2044				_fnMap( lang, lang, 'sZeroRecords', 'sLoadingRecords' );
2045			}
2046	
2047			// Old parameter name of the thousands separator mapped onto the new
2048			if ( lang.sInfoThousands ) {
2049				lang.sThousands = lang.sInfoThousands;
2050			}
2051	
2052			var decimal = lang.sDecimal;
2053			if ( decimal && defaultDecimal !== decimal ) {
2054				_addNumericSort( decimal );
2055			}
2056		}
2057	}
2058	
2059	
2060	/**
2061	 * Map one parameter onto another
2062	 *  @param {object} o Object to map
2063	 *  @param {*} knew The new parameter name
2064	 *  @param {*} old The old parameter name
2065	 */
2066	var _fnCompatMap = function ( o, knew, old ) {
2067		if ( o[ knew ] !== undefined ) {
2068			o[ old ] = o[ knew ];
2069		}
2070	};
2071	
2072	
2073	/**
2074	 * Provide backwards compatibility for the main DT options. Note that the new
2075	 * options are mapped onto the old parameters, so this is an external interface
2076	 * change only.
2077	 *  @param {object} init Object to map
2078	 */
2079	function _fnCompatOpts ( init )
2080	{
2081		_fnCompatMap( init, 'ordering',      'bSort' );
2082		_fnCompatMap( init, 'orderMulti',    'bSortMulti' );
2083		_fnCompatMap( init, 'orderClasses',  'bSortClasses' );
2084		_fnCompatMap( init, 'orderCellsTop', 'bSortCellsTop' );
2085		_fnCompatMap( init, 'order',         'aaSorting' );
2086		_fnCompatMap( init, 'orderFixed',    'aaSortingFixed' );
2087		_fnCompatMap( init, 'paging',        'bPaginate' );
2088		_fnCompatMap( init, 'pagingType',    'sPaginationType' );
2089		_fnCompatMap( init, 'pageLength',    'iDisplayLength' );
2090		_fnCompatMap( init, 'searching',     'bFilter' );
2091	
2092		// Boolean initialisation of x-scrolling
2093		if ( typeof init.sScrollX === 'boolean' ) {
2094			init.sScrollX = init.sScrollX ? '100%' : '';
2095		}
2096		if ( typeof init.scrollX === 'boolean' ) {
2097			init.scrollX = init.scrollX ? '100%' : '';
2098		}
2099	
2100		// Column search objects are in an array, so it needs to be converted
2101		// element by element
2102		var searchCols = init.aoSearchCols;
2103	
2104		if ( searchCols ) {
2105			for ( var i=0, ien=searchCols.length ; i<ien ; i++ ) {
2106				if ( searchCols[i] ) {
2107					_fnCamelToHungarian( DataTable.models.oSearch, searchCols[i] );
2108				}
2109			}
2110		}
2111	}
2112	
2113	
2114	/**
2115	 * Provide backwards compatibility for column options. Note that the new options
2116	 * are mapped onto the old parameters, so this is an external interface change
2117	 * only.
2118	 *  @param {object} init Object to map
2119	 */
2120	function _fnCompatCols ( init )
2121	{
2122		_fnCompatMap( init, 'orderable',     'bSortable' );
2123		_fnCompatMap( init, 'orderData',     'aDataSort' );
2124		_fnCompatMap( init, 'orderSequence', 'asSorting' );
2125		_fnCompatMap( init, 'orderDataType', 'sortDataType' );
2126	
2127		// orderData can be given as an integer
2128		var dataSort = init.aDataSort;
2129		if ( typeof dataSort === 'number' && ! Array.isArray( dataSort ) ) {
2130			init.aDataSort = [ dataSort ];
2131		}
2132	}
2133	
2134	
2135	/**
2136	 * Browser feature detection for capabilities, quirks
2137	 *  @param {object} settings dataTables settings object
2138	 *  @memberof DataTable#oApi
2139	 */
2140	function _fnBrowserDetect( settings )
2141	{
2142		// We don't need to do this every time DataTables is constructed, the values
2143		// calculated are specific to the browser and OS configuration which we
2144		// don't expect to change between initialisations
2145		if ( ! DataTable.__browser ) {
2146			var browser = {};
2147			DataTable.__browser = browser;
2148	
2149			// Scrolling feature / quirks detection
2150			var n = $('<div/>')
2151				.css( {
2152					position: 'fixed',
2153					top: 0,
2154					left: $(window).scrollLeft()*-1, // allow for scrolling
2155					height: 1,
2156					width: 1,
2157					overflow: 'hidden'
2158				} )
2159				.append(
2160					$('<div/>')
2161						.css( {
2162							position: 'absolute',
2163							top: 1,
2164							left: 1,
2165							width: 100,
2166							overflow: 'scroll'
2167						} )
2168						.append(
2169							$('<div/>')
2170								.css( {
2171									width: '100%',
2172									height: 10
2173								} )
2174						)
2175				)
2176				.appendTo( 'body' );
2177	
2178			var outer = n.children();
2179			var inner = outer.children();
2180	
2181			// Numbers below, in order, are:
2182			// inner.offsetWidth, inner.clientWidth, outer.offsetWidth, outer.clientWidth
2183			//
2184			// IE6 XP:                           100 100 100  83
2185			// IE7 Vista:                        100 100 100  83
2186			// IE 8+ Windows:                     83  83 100  83
2187			// Evergreen Windows:                 83  83 100  83
2188			// Evergreen Mac with scrollbars:     85  85 100  85
2189			// Evergreen Mac without scrollbars: 100 100 100 100
2190	
2191			// Get scrollbar width
2192			browser.barWidth = outer[0].offsetWidth - outer[0].clientWidth;
2193	
2194			// IE6/7 will oversize a width 100% element inside a scrolling element, to
2195			// include the width of the scrollbar, while other browsers ensure the inner
2196			// element is contained without forcing scrolling
2197			browser.bScrollOversize = inner[0].offsetWidth === 100 && outer[0].clientWidth !== 100;
2198	
2199			// In rtl text layout, some browsers (most, but not all) will place the
2200			// scrollbar on the left, rather than the right.
2201			browser.bScrollbarLeft = Math.round( inner.offset().left ) !== 1;
2202	
2203			// IE8- don't provide height and width for getBoundingClientRect
2204			browser.bBounding = n[0].getBoundingClientRect().width ? true : false;
2205	
2206			n.remove();
2207		}
2208	
2209		$.extend( settings.oBrowser, DataTable.__browser );
2210		settings.oScroll.iBarWidth = DataTable.__browser.barWidth;
2211	}
2212	
2213	
2214	/**
2215	 * Array.prototype reduce[Right] method, used for browsers which don't support
2216	 * JS 1.6. Done this way to reduce code size, since we iterate either way
2217	 *  @param {object} settings dataTables settings object
2218	 *  @memberof DataTable#oApi
2219	 */
2220	function _fnReduce ( that, fn, init, start, end, inc )
2221	{
2222		var
2223			i = start,
2224			value,
2225			isSet = false;
2226	
2227		if ( init !== undefined ) {
2228			value = init;
2229			isSet = true;
2230		}
2231	
2232		while ( i !== end ) {
2233			if ( ! that.hasOwnProperty(i) ) {
2234				continue;
2235			}
2236	
2237			value = isSet ?
2238				fn( value, that[i], i, that ) :
2239				that[i];
2240	
2241			isSet = true;
2242			i += inc;
2243		}
2244	
2245		return value;
2246	}
2247	
2248	/**
2249	 * Add a column to the list used for the table with default values
2250	 *  @param {object} oSettings dataTables settings object
2251	 *  @param {node} nTh The th element for this column
2252	 *  @memberof DataTable#oApi
2253	 */
2254	function _fnAddColumn( oSettings, nTh )
2255	{
2256		// Add column to aoColumns array
2257		var oDefaults = DataTable.defaults.column;
2258		var iCol = oSettings.aoColumns.length;
2259		var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, {
2260			"nTh": nTh ? nTh : document.createElement('th'),
2261			"sTitle":    oDefaults.sTitle    ? oDefaults.sTitle    : nTh ? nTh.innerHTML : '',
2262			"aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol],
2263			"mData": oDefaults.mData ? oDefaults.mData : iCol,
2264			idx: iCol
2265		} );
2266		oSettings.aoColumns.push( oCol );
2267	
2268		// Add search object for column specific search. Note that the `searchCols[ iCol ]`
2269		// passed into extend can be undefined. This allows the user to give a default
2270		// with only some of the parameters defined, and also not give a default
2271		var searchCols = oSettings.aoPreSearchCols;
2272		searchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch, searchCols[ iCol ] );
2273	
2274		// Use the default column options function to initialise classes etc
2275		_fnColumnOptions( oSettings, iCol, $(nTh).data() );
2276	}
2277	
2278	
2279	/**
2280	 * Apply options for a column
2281	 *  @param {object} oSettings dataTables settings object
2282	 *  @param {int} iCol column index to consider
2283	 *  @param {object} oOptions object with sType, bVisible and bSearchable etc
2284	 *  @memberof DataTable#oApi
2285	 */
2286	function _fnColumnOptions( oSettings, iCol, oOptions )
2287	{
2288		var oCol = oSettings.aoColumns[ iCol ];
2289		var oClasses = oSettings.oClasses;
2290		var th = $(oCol.nTh);
2291	
2292		// Try to get width information from the DOM. We can't get it from CSS
2293		// as we'd need to parse the CSS stylesheet. `width` option can override
2294		if ( ! oCol.sWidthOrig ) {
2295			// Width attribute
2296			oCol.sWidthOrig = th.attr('width') || null;
2297	
2298			// Style attribute
2299			var t = (th.attr('style') || '').match(/width:\s*(\d+[pxem%]+)/);
2300			if ( t ) {
2301				oCol.sWidthOrig = t[1];
2302			}
2303		}
2304	
2305		/* User specified column options */
2306		if ( oOptions !== undefined && oOptions !== null )
2307		{
2308			// Backwards compatibility
2309			_fnCompatCols( oOptions );
2310	
2311			// Map camel case parameters to their Hungarian counterparts
2312			_fnCamelToHungarian( DataTable.defaults.column, oOptions, true );
2313	
2314			/* Backwards compatibility for mDataProp */
2315			if ( oOptions.mDataProp !== undefined && !oOptions.mData )
2316			{
2317				oOptions.mData = oOptions.mDataProp;
2318			}
2319	
2320			if ( oOptions.sType )
2321			{
2322				oCol._sManualType = oOptions.sType;
2323			}
2324	
2325			// `class` is a reserved word in Javascript, so we need to provide
2326			// the ability to use a valid name for the camel case input
2327			if ( oOptions.className && ! oOptions.sClass )
2328			{
2329				oOptions.sClass = oOptions.className;
2330			}
2331			if ( oOptions.sClass ) {
2332				th.addClass( oOptions.sClass );
2333			}
2334	
2335			var origClass = oCol.sClass;
2336	
2337			$.extend( oCol, oOptions );
2338			_fnMap( oCol, oOptions, "sWidth", "sWidthOrig" );
2339	
2340			// Merge class from previously defined classes with this one, rather than just
2341			// overwriting it in the extend above
2342			if (origClass !== oCol.sClass) {
2343				oCol.sClass = origClass + ' ' + oCol.sClass;
2344			}
2345	
2346			/* iDataSort to be applied (backwards compatibility), but aDataSort will take
2347			 * priority if defined
2348			 */
2349			if ( oOptions.iDataSort !== undefined )
2350			{
2351				oCol.aDataSort = [ oOptions.iDataSort ];
2352			}
2353			_fnMap( oCol, oOptions, "aDataSort" );
2354		}
2355	
2356		/* Cache the data get and set functions for speed */
2357		var mDataSrc = oCol.mData;
2358		var mData = _fnGetObjectDataFn( mDataSrc );
2359		var mRender = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null;
2360	
2361		var attrTest = function( src ) {
2362			return typeof src === 'string' && src.indexOf('@') !== -1;
2363		};
2364		oCol._bAttrSrc = $.isPlainObject( mDataSrc ) && (
2365			attrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter)
2366		);
2367		oCol._setter = null;
2368	
2369		oCol.fnGetData = function (rowData, type, meta) {
2370			var innerData = mData( rowData, type, undefined, meta );
2371	
2372			return mRender && type ?
2373				mRender( innerData, type, rowData, meta ) :
2374				innerData;
2375		};
2376		oCol.fnSetData = function ( rowData, val, meta ) {
2377			return _fnSetObjectDataFn( mDataSrc )( rowData, val, meta );
2378		};
2379	
2380		// Indicate if DataTables should read DOM data as an object or array
2381		// Used in _fnGetRowElements
2382		if ( typeof mDataSrc !== 'number' && ! oCol._isArrayHost ) {
2383			oSettings._rowReadObject = true;
2384		}
2385	
2386		/* Feature sorting overrides column specific when off */
2387		if ( !oSettings.oFeatures.bSort )
2388		{
2389			oCol.bSortable = false;
2390			th.addClass( oClasses.sSortableNone ); // Have to add class here as order event isn't called
2391		}
2392	
2393		/* Check that the class assignment is correct for sorting */
2394		var bAsc = $.inArray('asc', oCol.asSorting) !== -1;
2395		var bDesc = $.inArray('desc', oCol.asSorting) !== -1;
2396		if ( !oCol.bSortable || (!bAsc && !bDesc) )
2397		{
2398			oCol.sSortingClass = oClasses.sSortableNone;
2399			oCol.sSortingClassJUI = "";
2400		}
2401		else if ( bAsc && !bDesc )
2402		{
2403			oCol.sSortingClass = oClasses.sSortableAsc;
2404			oCol.sSortingClassJUI = oClasses.sSortJUIAscAllowed;
2405		}
2406		else if ( !bAsc && bDesc )
2407		{
2408			oCol.sSortingClass = oClasses.sSortableDesc;
2409			oCol.sSortingClassJUI = oClasses.sSortJUIDescAllowed;
2410		}
2411		else
2412		{
2413			oCol.sSortingClass = oClasses.sSortable;
2414			oCol.sSortingClassJUI = oClasses.sSortJUI;
2415		}
2416	}
2417	
2418	
2419	/**
2420	 * Adjust the table column widths for new data. Note: you would probably want to
2421	 * do a redraw after calling this function!
2422	 *  @param {object} settings dataTables settings object
2423	 *  @memberof DataTable#oApi
2424	 */
2425	function _fnAdjustColumnSizing ( settings )
2426	{
2427		/* Not interested in doing column width calculation if auto-width is disabled */
2428		if ( settings.oFeatures.bAutoWidth !== false )
2429		{
2430			var columns = settings.aoColumns;
2431	
2432			_fnCalculateColumnWidths( settings );
2433			for ( var i=0 , iLen=columns.length ; i<iLen ; i++ )
2434			{
2435				columns[i].nTh.style.width = columns[i].sWidth;
2436			}
2437		}
2438	
2439		var scroll = settings.oScroll;
2440		if ( scroll.sY !== '' || scroll.sX !== '')
2441		{
2442			_fnScrollDraw( settings );
2443		}
2444	
2445		_fnCallbackFire( settings, null, 'column-sizing', [settings] );
2446	}
2447	
2448	
2449	/**
2450	 * Convert the index of a visible column to the index in the data array (take account
2451	 * of hidden columns)
2452	 *  @param {object} oSettings dataTables settings object
2453	 *  @param {int} iMatch Visible column index to lookup
2454	 *  @returns {int} i the data index
2455	 *  @memberof DataTable#oApi
2456	 */
2457	function _fnVisibleToColumnIndex( oSettings, iMatch )
2458	{
2459		var aiVis = _fnGetColumns( oSettings, 'bVisible' );
2460	
2461		return typeof aiVis[iMatch] === 'number' ?
2462			aiVis[iMatch] :
2463			null;
2464	}
2465	
2466	
2467	/**
2468	 * Convert the index of an index in the data array and convert it to the visible
2469	 *   column index (take account of hidden columns)
2470	 *  @param {int} iMatch Column index to lookup
2471	 *  @param {object} oSettings dataTables settings object
2472	 *  @returns {int} i the data index
2473	 *  @memberof DataTable#oApi
2474	 */
2475	function _fnColumnIndexToVisible( oSettings, iMatch )
2476	{
2477		var aiVis = _fnGetColumns( oSettings, 'bVisible' );
2478		var iPos = $.inArray( iMatch, aiVis );
2479	
2480		return iPos !== -1 ? iPos : null;
2481	}
2482	
2483	
2484	/**
2485	 * Get the number of visible columns
2486	 *  @param {object} oSettings dataTables settings object
2487	 *  @returns {int} i the number of visible columns
2488	 *  @memberof DataTable#oApi
2489	 */
2490	function _fnVisbleColumns( oSettings )
2491	{
2492		var vis = 0;
2493	
2494		// No reduce in IE8, use a loop for now
2495		$.each( oSettings.aoColumns, function ( i, col ) {
2496			if ( col.bVisible && $(col.nTh).css('display') !== 'none' ) {
2497				vis++;
2498			}
2499		} );
2500	
2501		return vis;
2502	}
2503	
2504	
2505	/**
2506	 * Get an array of column indexes that match a given property
2507	 *  @param {object} oSettings dataTables settings object
2508	 *  @param {string} sParam Parameter in aoColumns to look for - typically
2509	 *    bVisible or bSearchable
2510	 *  @returns {array} Array of indexes with matched properties
2511	 *  @memberof DataTable#oApi
2512	 */
2513	function _fnGetColumns( oSettings, sParam )
2514	{
2515		var a = [];
2516	
2517		$.map( oSettings.aoColumns, function(val, i) {
2518			if ( val[sParam] ) {
2519				a.push( i );
2520			}
2521		} );
2522	
2523		return a;
2524	}
2525	
2526	
2527	/**
2528	 * Calculate the 'type' of a column
2529	 *  @param {object} settings dataTables settings object
2530	 *  @memberof DataTable#oApi
2531	 */
2532	function _fnColumnTypes ( settings )
2533	{
2534		var columns = settings.aoColumns;
2535		var data = settings.aoData;
2536		var types = DataTable.ext.type.detect;
2537		var i, ien, j, jen, k, ken;
2538		var col, cell, detectedType, cache;
2539	
2540		// For each column, spin over the 
2541		for ( i=0, ien=columns.length ; i<ien ; i++ ) {
2542			col = columns[i];
2543			cache = [];
2544	
2545			if ( ! col.sType && col._sManualType ) {
2546				col.sType = col._sManualType;
2547			}
2548			else if ( ! col.sType ) {
2549				for ( j=0, jen=types.length ; j<jen ; j++ ) {
2550					for ( k=0, ken=data.length ; k<ken ; k++ ) {
2551						// Use a cache array so we only need to get the type data
2552						// from the formatter once (when using multiple detectors)
2553						if ( cache[k] === undefined ) {
2554							cache[k] = _fnGetCellData( settings, k, i, 'type' );
2555						}
2556	
2557						detectedType = types[j]( cache[k], settings );
2558	
2559						// If null, then this type can't apply to this column, so
2560						// rather than testing all cells, break out. There is an
2561						// exception for the last type which is `html`. We need to
2562						// scan all rows since it is possible to mix string and HTML
2563						// types
2564						if ( ! detectedType && j !== types.length-1 ) {
2565							break;
2566						}
2567	
2568						// Only a single match is needed for html type since it is
2569						// bottom of the pile and very similar to string - but it
2570						// must not be empty
2571						if ( detectedType === 'html' && ! _empty(cache[k]) ) {
2572							break;
2573						}
2574					}
2575	
2576					// Type is valid for all data points in the column - use this
2577					// type
2578					if ( detectedType ) {
2579						col.sType = detectedType;
2580						break;
2581					}
2582				}
2583	
2584				// Fall back - if no type was detected, always use string
2585				if ( ! col.sType ) {
2586					col.sType = 'string';
2587				}
2588			}
2589		}
2590	}
2591	
2592	
2593	/**
2594	 * Take the column definitions and static columns arrays and calculate how
2595	 * they relate to column indexes. The callback function will then apply the
2596	 * definition found for a column to a suitable configuration object.
2597	 *  @param {object} oSettings dataTables settings object
2598	 *  @param {array} aoColDefs The aoColumnDefs array that is to be applied
2599	 *  @param {array} aoCols The aoColumns array that defines columns individually
2600	 *  @param {function} fn Callback function - takes two parameters, the calculated
2601	 *    column index and the definition for that column.
2602	 *  @memberof DataTable#oApi
2603	 */
2604	function _fnApplyColumnDefs( oSettings, aoColDefs, aoCols, fn )
2605	{
2606		var i, iLen, j, jLen, k, kLen, def;
2607		var columns = oSettings.aoColumns;
2608	
2609		// Column definitions with aTargets
2610		if ( aoColDefs )
2611		{
2612			/* Loop over the definitions array - loop in reverse so first instance has priority */
2613			for ( i=aoColDefs.length-1 ; i>=0 ; i-- )
2614			{
2615				def = aoColDefs[i];
2616	
2617				/* Each definition can target multiple columns, as it is an array */
2618				var aTargets = def.target !== undefined
2619					? def.target
2620					: def.targets !== undefined
2621						? def.targets
2622						: def.aTargets;
2623	
2624				if ( ! Array.isArray( aTargets ) )
2625				{
2626					aTargets = [ aTargets ];
2627				}
2628	
2629				for ( j=0, jLen=aTargets.length ; j<jLen ; j++ )
2630				{
2631					if ( typeof aTargets[j] === 'number' && aTargets[j] >= 0 )
2632					{
2633						/* Add columns that we don't yet know about */
2634						while( columns.length <= aTargets[j] )
2635						{
2636							_fnAddColumn( oSettings );
2637						}
2638	
2639						/* Integer, basic index */
2640						fn( aTargets[j], def );
2641					}
2642					else if ( typeof aTargets[j] === 'number' && aTargets[j] < 0 )
2643					{
2644						/* Negative integer, right to left column counting */
2645						fn( columns.length+aTargets[j], def );
2646					}
2647					else if ( typeof aTargets[j] === 'string' )
2648					{
2649						/* Class name matching on TH element */
2650						for ( k=0, kLen=columns.length ; k<kLen ; k++ )
2651						{
2652							if ( aTargets[j] == "_all" ||
2653							     $(columns[k].nTh).hasClass( aTargets[j] ) )
2654							{
2655								fn( k, def );
2656							}
2657						}
2658					}
2659				}
2660			}
2661		}
2662	
2663		// Statically defined columns array
2664		if ( aoCols )
2665		{
2666			for ( i=0, iLen=aoCols.length ; i<iLen ; i++ )
2667			{
2668				fn( i, aoCols[i] );
2669			}
2670		}
2671	}
2672	
2673	/**
2674	 * Add a data array to the table, creating DOM node etc. This is the parallel to
2675	 * _fnGatherData, but for adding rows from a Javascript source, rather than a
2676	 * DOM source.
2677	 *  @param {object} oSettings dataTables settings object
2678	 *  @param {array} aData data array to be added
2679	 *  @param {node} [nTr] TR element to add to the table - optional. If not given,
2680	 *    DataTables will create a row automatically
2681	 *  @param {array} [anTds] Array of TD|TH elements for the row - must be given
2682	 *    if nTr is.
2683	 *  @returns {int} >=0 if successful (index of new aoData entry), -1 if failed
2684	 *  @memberof DataTable#oApi
2685	 */
2686	function _fnAddData ( oSettings, aDataIn, nTr, anTds )
2687	{
2688		/* Create the object for storing information about this new row */
2689		var iRow = oSettings.aoData.length;
2690		var oData = $.extend( true, {}, DataTable.models.oRow, {
2691			src: nTr ? 'dom' : 'data',
2692			idx: iRow
2693		} );
2694	
2695		oData._aData = aDataIn;
2696		oSettings.aoData.push( oData );
2697	
2698		/* Create the cells */
2699		var nTd, sThisType;
2700		var columns = oSettings.aoColumns;
2701	
2702		// Invalidate the column types as the new data needs to be revalidated
2703		for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
2704		{
2705			columns[i].sType = null;
2706		}
2707	
2708		/* Add to the display array */
2709		oSettings.aiDisplayMaster.push( iRow );
2710	
2711		var id = oSettings.rowIdFn( aDataIn );
2712		if ( id !== undefined ) {
2713			oSettings.aIds[ id ] = oData;
2714		}
2715	
2716		/* Create the DOM information, or register it if already present */
2717		if ( nTr || ! oSettings.oFeatures.bDeferRender )
2718		{
2719			_fnCreateTr( oSettings, iRow, nTr, anTds );
2720		}
2721	
2722		return iRow;
2723	}
2724	
2725	
2726	/**
2727	 * Add one or more TR elements to the table. Generally we'd expect to
2728	 * use this for reading data from a DOM sourced table, but it could be
2729	 * used for an TR element. Note that if a TR is given, it is used (i.e.
2730	 * it is not cloned).
2731	 *  @param {object} settings dataTables settings object
2732	 *  @param {array|node|jQuery} trs The TR element(s) to add to the table
2733	 *  @returns {array} Array of indexes for the added rows
2734	 *  @memberof DataTable#oApi
2735	 */
2736	function _fnAddTr( settings, trs )
2737	{
2738		var row;
2739	
2740		// Allow an individual node to be passed in
2741		if ( ! (trs instanceof $) ) {
2742			trs = $(trs);
2743		}
2744	
2745		return trs.map( function (i, el) {
2746			row = _fnGetRowElements( settings, el );
2747			return _fnAddData( settings, row.data, el, row.cells );
2748		} );
2749	}
2750	
2751	
2752	/**
2753	 * Take a TR element and convert it to an index in aoData
2754	 *  @param {object} oSettings dataTables settings object
2755	 *  @param {node} n the TR element to find
2756	 *  @returns {int} index if the node is found, null if not
2757	 *  @memberof DataTable#oApi
2758	 */
2759	function _fnNodeToDataIndex( oSettings, n )
2760	{
2761		return (n._DT_RowIndex!==undefined) ? n._DT_RowIndex : null;
2762	}
2763	
2764	
2765	/**
2766	 * Take a TD element and convert it into a column data index (not the visible index)
2767	 *  @param {object} oSettings dataTables settings object
2768	 *  @param {int} iRow The row number the TD/TH can be found in
2769	 *  @param {node} n The TD/TH element to find
2770	 *  @returns {int} index if the node is found, -1 if not
2771	 *  @memberof DataTable#oApi
2772	 */
2773	function _fnNodeToColumnIndex( oSettings, iRow, n )
2774	{
2775		return $.inArray( n, oSettings.aoData[ iRow ].anCells );
2776	}
2777	
2778	
2779	/**
2780	 * Get the data for a given cell from the internal cache, taking into account data mapping
2781	 *  @param {object} settings dataTables settings object
2782	 *  @param {int} rowIdx aoData row id
2783	 *  @param {int} colIdx Column index
2784	 *  @param {string} type data get type ('display', 'type' 'filter|search' 'sort|order')
2785	 *  @returns {*} Cell data
2786	 *  @memberof DataTable#oApi
2787	 */
2788	function _fnGetCellData( settings, rowIdx, colIdx, type )
2789	{
2790		if (type === 'search') {
2791			type = 'filter';
2792		}
2793		else if (type === 'order') {
2794			type = 'sort';
2795		}
2796	
2797		var draw           = settings.iDraw;
2798		var col            = settings.aoColumns[colIdx];
2799		var rowData        = settings.aoData[rowIdx]._aData;
2800		var defaultContent = col.sDefaultContent;
2801		var cellData       = col.fnGetData( rowData, type, {
2802			settings: settings,
2803			row:      rowIdx,
2804			col:      colIdx
2805		} );
2806	
2807		if ( cellData === undefined ) {
2808			if ( settings.iDrawError != draw && defaultContent === null ) {
2809				_fnLog( settings, 0, "Requested unknown parameter "+
2810					(typeof col.mData=='function' ? '{function}' : "'"+col.mData+"'")+
2811					" for row "+rowIdx+", column "+colIdx, 4 );
2812				settings.iDrawError = draw;
2813			}
2814			return defaultContent;
2815		}
2816	
2817		// When the data source is null and a specific data type is requested (i.e.
2818		// not the original data), we can use default column data
2819		if ( (cellData === rowData || cellData === null) && defaultContent !== null && type !== undefined ) {
2820			cellData = defaultContent;
2821		}
2822		else if ( typeof cellData === 'function' ) {
2823			// If the data source is a function, then we run it and use the return,
2824			// executing in the scope of the data object (for instances)
2825			return cellData.call( rowData );
2826		}
2827	
2828		if ( cellData === null && type === 'display' ) {
2829			return '';
2830		}
2831	
2832		if ( type === 'filter' ) {
2833			var fomatters = DataTable.ext.type.search;
2834	
2835			if ( fomatters[ col.sType ] ) {
2836				cellData = fomatters[ col.sType ]( cellData );
2837			}
2838		}
2839	
2840		return cellData;
2841	}
2842	
2843	
2844	/**
2845	 * Set the value for a specific cell, into the internal data cache
2846	 *  @param {object} settings dataTables settings object
2847	 *  @param {int} rowIdx aoData row id
2848	 *  @param {int} colIdx Column index
2849	 *  @param {*} val Value to set
2850	 *  @memberof DataTable#oApi
2851	 */
2852	function _fnSetCellData( settings, rowIdx, colIdx, val )
2853	{
2854		var col     = settings.aoColumns[colIdx];
2855		var rowData = settings.aoData[rowIdx]._aData;
2856	
2857		col.fnSetData( rowData, val, {
2858			settings: settings,
2859			row:      rowIdx,
2860			col:      colIdx
2861		}  );
2862	}
2863	
2864	
2865	// Private variable that is used to match action syntax in the data property object
2866	var __reArray = /\[.*?\]$/;
2867	var __reFn = /\(\)$/;
2868	
2869	/**
2870	 * Split string on periods, taking into account escaped periods
2871	 * @param  {string} str String to split
2872	 * @return {array} Split string
2873	 */
2874	function _fnSplitObjNotation( str )
2875	{
2876		return $.map( str.match(/(\\.|[^\.])+/g) || [''], function ( s ) {
2877			return s.replace(/\\\./g, '.');
2878		} );
2879	}
2880	
2881	
2882	/**
2883	 * Return a function that can be used to get data from a source object, taking
2884	 * into account the ability to use nested objects as a source
2885	 *  @param {string|int|function} mSource The data source for the object
2886	 *  @returns {function} Data get function
2887	 *  @memberof DataTable#oApi
2888	 */
2889	var _fnGetObjectDataFn = DataTable.util.get;
2890	
2891	
2892	/**
2893	 * Return a function that can be used to set data from a source object, taking
2894	 * into account the ability to use nested objects as a source
2895	 *  @param {string|int|function} mSource The data source for the object
2896	 *  @returns {function} Data set function
2897	 *  @memberof DataTable#oApi
2898	 */
2899	var _fnSetObjectDataFn = DataTable.util.set;
2900	
2901	
2902	/**
2903	 * Return an array with the full table data
2904	 *  @param {object} oSettings dataTables settings object
2905	 *  @returns array {array} aData Master data array
2906	 *  @memberof DataTable#oApi
2907	 */
2908	function _fnGetDataMaster ( settings )
2909	{
2910		return _pluck( settings.aoData, '_aData' );
2911	}
2912	
2913	
2914	/**
2915	 * Nuke the table
2916	 *  @param {object} oSettings dataTables settings object
2917	 *  @memberof DataTable#oApi
2918	 */
2919	function _fnClearTable( settings )
2920	{
2921		settings.aoData.length = 0;
2922		settings.aiDisplayMaster.length = 0;
2923		settings.aiDisplay.length = 0;
2924		settings.aIds = {};
2925	}
2926	
2927	
2928	 /**
2929	 * Take an array of integers (index array) and remove a target integer (value - not
2930	 * the key!)
2931	 *  @param {array} a Index array to target
2932	 *  @param {int} iTarget value to find
2933	 *  @memberof DataTable#oApi
2934	 */
2935	function _fnDeleteIndex( a, iTarget, splice )
2936	{
2937		var iTargetIndex = -1;
2938	
2939		for ( var i=0, iLen=a.length ; i<iLen ; i++ )
2940		{
2941			if ( a[i] == iTarget )
2942			{
2943				iTargetIndex = i;
2944			}
2945			else if ( a[i] > iTarget )
2946			{
2947				a[i]--;
2948			}
2949		}
2950	
2951		if ( iTargetIndex != -1 && splice === undefined )
2952		{
2953			a.splice( iTargetIndex, 1 );
2954		}
2955	}
2956	
2957	
2958	/**
2959	 * Mark cached data as invalid such that a re-read of the data will occur when
2960	 * the cached data is next requested. Also update from the data source object.
2961	 *
2962	 * @param {object} settings DataTables settings object
2963	 * @param {int}    rowIdx   Row index to invalidate
2964	 * @param {string} [src]    Source to invalidate from: undefined, 'auto', 'dom'
2965	 *     or 'data'
2966	 * @param {int}    [colIdx] Column index to invalidate. If undefined the whole
2967	 *     row will be invalidated
2968	 * @memberof DataTable#oApi
2969	 *
2970	 * @todo For the modularisation of v1.11 this will need to become a callback, so
2971	 *   the sort and filter methods can subscribe to it. That will required
2972	 *   initialisation options for sorting, which is why it is not already baked in
2973	 */
2974	function _fnInvalidate( settings, rowIdx, src, colIdx )
2975	{
2976		var row = settings.aoData[ rowIdx ];
2977		var i, ien;
2978		var cellWrite = function ( cell, col ) {
2979			// This is very frustrating, but in IE if you just write directly
2980			// to innerHTML, and elements that are overwritten are GC'ed,
2981			// even if there is a reference to them elsewhere
2982			while ( cell.childNodes.length ) {
2983				cell.removeChild( cell.firstChild );
2984			}
2985	
2986			cell.innerHTML = _fnGetCellData( settings, rowIdx, col, 'display' );
2987		};
2988	
2989		// Are we reading last data from DOM or the data object?
2990		if ( src === 'dom' || ((! src || src === 'auto') && row.src === 'dom') ) {
2991			// Read the data from the DOM
2992			row._aData = _fnGetRowElements(
2993					settings, row, colIdx, colIdx === undefined ? undefined : row._aData
2994				)
2995				.data;
2996		}
2997		else {
2998			// Reading from data object, update the DOM
2999			var cells = row.anCells;
3000	
3001			if ( cells ) {
3002				if ( colIdx !== undefined ) {
3003					cellWrite( cells[colIdx], colIdx );
3004				}
3005				else {
3006					for ( i=0, ien=cells.length ; i<ien ; i++ ) {
3007						cellWrite( cells[i], i );
3008					}
3009				}
3010			}
3011		}
3012	
3013		// For both row and cell invalidation, the cached data for sorting and
3014		// filtering is nulled out
3015		row._aSortData = null;
3016		row._aFilterData = null;
3017	
3018		// Invalidate the type for a specific column (if given) or all columns since
3019		// the data might have changed
3020		var cols = settings.aoColumns;
3021		if ( colIdx !== undefined ) {
3022			cols[ colIdx ].sType = null;
3023		}
3024		else {
3025			for ( i=0, ien=cols.length ; i<ien ; i++ ) {
3026				cols[i].sType = null;
3027			}
3028	
3029			// Update DataTables special `DT_*` attributes for the row
3030			_fnRowAttributes( settings, row );
3031		}
3032	}
3033	
3034	
3035	/**
3036	 * Build a data source object from an HTML row, reading the contents of the
3037	 * cells that are in the row.
3038	 *
3039	 * @param {object} settings DataTables settings object
3040	 * @param {node|object} TR element from which to read data or existing row
3041	 *   object from which to re-read the data from the cells
3042	 * @param {int} [colIdx] Optional column index
3043	 * @param {array|object} [d] Data source object. If `colIdx` is given then this
3044	 *   parameter should also be given and will be used to write the data into.
3045	 *   Only the column in question will be written
3046	 * @returns {object} Object with two parameters: `data` the data read, in
3047	 *   document order, and `cells` and array of nodes (they can be useful to the
3048	 *   caller, so rather than needing a second traversal to get them, just return
3049	 *   them from here).
3050	 * @memberof DataTable#oApi
3051	 */
3052	function _fnGetRowElements( settings, row, colIdx, d )
3053	{
3054		var
3055			tds = [],
3056			td = row.firstChild,
3057			name, col, o, i=0, contents,
3058			columns = settings.aoColumns,
3059			objectRead = settings._rowReadObject;
3060	
3061		// Allow the data object to be passed in, or construct
3062		d = d !== undefined ?
3063			d :
3064			objectRead ?
3065				{} :
3066				[];
3067	
3068		var attr = function ( str, td  ) {
3069			if ( typeof str === 'string' ) {
3070				var idx = str.indexOf('@');
3071	
3072				if ( idx !== -1 ) {
3073					var attr = str.substring( idx+1 );
3074					var setter = _fnSetObjectDataFn( str );
3075					setter( d, td.getAttribute( attr ) );
3076				}
3077			}
3078		};
3079	
3080		// Read data from a cell and store into the data object
3081		var cellProcess = function ( cell ) {
3082			if ( colIdx === undefined || colIdx === i ) {
3083				col = columns[i];
3084				contents = (cell.innerHTML).trim();
3085	
3086				if ( col && col._bAttrSrc ) {
3087					var setter = _fnSetObjectDataFn( col.mData._ );
3088					setter( d, contents );
3089	
3090					attr( col.mData.sort, cell );
3091					attr( col.mData.type, cell );
3092					attr( col.mData.filter, cell );
3093				}
3094				else {
3095					// Depending on the `data` option for the columns the data can
3096					// be read to either an object or an array.
3097					if ( objectRead ) {
3098						if ( ! col._setter ) {
3099							// Cache the setter function
3100							col._setter = _fnSetObjectDataFn( col.mData );
3101						}
3102						col._setter( d, contents );
3103					}
3104					else {
3105						d[i] = contents;
3106					}
3107				}
3108			}
3109	
3110			i++;
3111		};
3112	
3113		if ( td ) {
3114			// `tr` element was passed in
3115			while ( td ) {
3116				name = td.nodeName.toUpperCase();
3117	
3118				if ( name == "TD" || name == "TH" ) {
3119					cellProcess( td );
3120					tds.push( td );
3121				}
3122	
3123				td = td.nextSibling;
3124			}
3125		}
3126		else {
3127			// Existing row object passed in
3128			tds = row.anCells;
3129	
3130			for ( var j=0, jen=tds.length ; j<jen ; j++ ) {
3131				cellProcess( tds[j] );
3132			}
3133		}
3134	
3135		// Read the ID from the DOM if present
3136		var rowNode = row.firstChild ? row : row.nTr;
3137	
3138		if ( rowNode ) {
3139			var id = rowNode.getAttribute( 'id' );
3140	
3141			if ( id ) {
3142				_fnSetObjectDataFn( settings.rowId )( d, id );
3143			}
3144		}
3145	
3146		return {
3147			data: d,
3148			cells: tds
3149		};
3150	}
3151	/**
3152	 * Create a new TR element (and it's TD children) for a row
3153	 *  @param {object} oSettings dataTables settings object
3154	 *  @param {int} iRow Row to consider
3155	 *  @param {node} [nTrIn] TR element to add to the table - optional. If not given,
3156	 *    DataTables will create a row automatically
3157	 *  @param {array} [anTds] Array of TD|TH elements for the row - must be given
3158	 *    if nTr is.
3159	 *  @memberof DataTable#oApi
3160	 */
3161	function _fnCreateTr ( oSettings, iRow, nTrIn, anTds )
3162	{
3163		var
3164			row = oSettings.aoData[iRow],
3165			rowData = row._aData,
3166			cells = [],
3167			nTr, nTd, oCol,
3168			i, iLen, create;
3169	
3170		if ( row.nTr === null )
3171		{
3172			nTr = nTrIn || document.createElement('tr');
3173	
3174			row.nTr = nTr;
3175			row.anCells = cells;
3176	
3177			/* Use a private property on the node to allow reserve mapping from the node
3178			 * to the aoData array for fast look up
3179			 */
3180			nTr._DT_RowIndex = iRow;
3181	
3182			/* Special parameters can be given by the data source to be used on the row */
3183			_fnRowAttributes( oSettings, row );
3184	
3185			/* Process each column */
3186			for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
3187			{
3188				oCol = oSettings.aoColumns[i];
3189				create = nTrIn ? false : true;
3190	
3191				nTd = create ? document.createElement( oCol.sCellType ) : anTds[i];
3192	
3193				if (! nTd) {
3194					_fnLog( oSettings, 0, 'Incorrect column count', 18 );
3195				}
3196	
3197				nTd._DT_CellIndex = {
3198					row: iRow,
3199					column: i
3200				};
3201				
3202				cells.push( nTd );
3203	
3204				// Need to create the HTML if new, or if a rendering function is defined
3205				if ( create || ((oCol.mRender || oCol.mData !== i) &&
3206					 (!$.isPlainObject(oCol.mData) || oCol.mData._ !== i+'.display')
3207				)) {
3208					nTd.innerHTML = _fnGetCellData( oSettings, iRow, i, 'display' );
3209				}
3210	
3211				/* Add user defined class */
3212				if ( oCol.sClass )
3213				{
3214					nTd.className += ' '+oCol.sClass;
3215				}
3216	
3217				// Visibility - add or remove as required
3218				if ( oCol.bVisible && ! nTrIn )
3219				{
3220					nTr.appendChild( nTd );
3221				}
3222				else if ( ! oCol.bVisible && nTrIn )
3223				{
3224					nTd.parentNode.removeChild( nTd );
3225				}
3226	
3227				if ( oCol.fnCreatedCell )
3228				{
3229					oCol.fnCreatedCell.call( oSettings.oInstance,
3230						nTd, _fnGetCellData( oSettings, iRow, i ), rowData, iRow, i
3231					);
3232				}
3233			}
3234	
3235			_fnCallbackFire( oSettings, 'aoRowCreatedCallback', null, [nTr, rowData, iRow, cells] );
3236		}
3237	}
3238	
3239	
3240	/**
3241	 * Add attributes to a row based on the special `DT_*` parameters in a data
3242	 * source object.
3243	 *  @param {object} settings DataTables settings object
3244	 *  @param {object} DataTables row object for the row to be modified
3245	 *  @memberof DataTable#oApi
3246	 */
3247	function _fnRowAttributes( settings, row )
3248	{
3249		var tr = row.nTr;
3250		var data = row._aData;
3251	
3252		if ( tr ) {
3253			var id = settings.rowIdFn( data );
3254	
3255			if ( id ) {
3256				tr.id = id;
3257			}
3258	
3259			if ( data.DT_RowClass ) {
3260				// Remove any classes added by DT_RowClass before
3261				var a = data.DT_RowClass.split(' ');
3262				row.__rowc = row.__rowc ?
3263					_unique( row.__rowc.concat( a ) ) :
3264					a;
3265	
3266				$(tr)
3267					.removeClass( row.__rowc.join(' ') )
3268					.addClass( data.DT_RowClass );
3269			}
3270	
3271			if ( data.DT_RowAttr ) {
3272				$(tr).attr( data.DT_RowAttr );
3273			}
3274	
3275			if ( data.DT_RowData ) {
3276				$(tr).data( data.DT_RowData );
3277			}
3278		}
3279	}
3280	
3281	
3282	/**
3283	 * Create the HTML header for the table
3284	 *  @param {object} oSettings dataTables settings object
3285	 *  @memberof DataTable#oApi
3286	 */
3287	function _fnBuildHead( oSettings )
3288	{
3289		var i, ien, cell, row, column;
3290		var thead = oSettings.nTHead;
3291		var tfoot = oSettings.nTFoot;
3292		var createHeader = $('th, td', thead).length === 0;
3293		var classes = oSettings.oClasses;
3294		var columns = oSettings.aoColumns;
3295	
3296		if ( createHeader ) {
3297			row = $('<tr/>').appendTo( thead );
3298		}
3299	
3300		for ( i=0, ien=columns.length ; i<ien ; i++ ) {
3301			column = columns[i];
3302			cell = $( column.nTh ).addClass( column.sClass );
3303	
3304			if ( createHeader ) {
3305				cell.appendTo( row );
3306			}
3307	
3308			// 1.11 move into sorting
3309			if ( oSettings.oFeatures.bSort ) {
3310				cell.addClass( column.sSortingClass );
3311	
3312				if ( column.bSortable !== false ) {
3313					cell
3314						.attr( 'tabindex', oSettings.iTabIndex )
3315						.attr( 'aria-controls', oSettings.sTableId );
3316	
3317					_fnSortAttachListener( oSettings, column.nTh, i );
3318				}
3319			}
3320	
3321			if ( column.sTitle != cell[0].innerHTML ) {
3322				cell.html( column.sTitle );
3323			}
3324	
3325			_fnRenderer( oSettings, 'header' )(
3326				oSettings, cell, column, classes
3327			);
3328		}
3329	
3330		if ( createHeader ) {
3331			_fnDetectHeader( oSettings.aoHeader, thead );
3332		}
3333	
3334		/* Deal with the footer - add classes if required */
3335		$(thead).children('tr').children('th, td').addClass( classes.sHeaderTH );
3336		$(tfoot).children('tr').children('th, td').addClass( classes.sFooterTH );
3337	
3338		// Cache the footer cells. Note that we only take the cells from the first
3339		// row in the footer. If there is more than one row the user wants to
3340		// interact with, they need to use the table().foot() method. Note also this
3341		// allows cells to be used for multiple columns using colspan
3342		if ( tfoot !== null ) {
3343			var cells = oSettings.aoFooter[0];
3344	
3345			for ( i=0, ien=cells.length ; i<ien ; i++ ) {
3346				column = columns[i];
3347	
3348				if (column) {
3349					column.nTf = cells[i].cell;
3350		
3351					if ( column.sClass ) {
3352						$(column.nTf).addClass( column.sClass );
3353					}
3354				}
3355				else {
3356					_fnLog( oSettings, 0, 'Incorrect column count', 18 );
3357				}
3358			}
3359		}
3360	}
3361	
3362	
3363	/**
3364	 * Draw the header (or footer) element based on the column visibility states. The
3365	 * methodology here is to use the layout array from _fnDetectHeader, modified for
3366	 * the instantaneous column visibility, to construct the new layout. The grid is
3367	 * traversed over cell at a time in a rows x columns grid fashion, although each
3368	 * cell insert can cover multiple elements in the grid - which is tracks using the
3369	 * aApplied array. Cell inserts in the grid will only occur where there isn't
3370	 * already a cell in that position.
3371	 *  @param {object} oSettings dataTables settings object
3372	 *  @param array {objects} aoSource Layout array from _fnDetectHeader
3373	 *  @param {boolean} [bIncludeHidden=false] If true then include the hidden columns in the calc,
3374	 *  @memberof DataTable#oApi
3375	 */
3376	function _fnDrawHead( oSettings, aoSource, bIncludeHidden )
3377	{
3378		var i, iLen, j, jLen, k, kLen, n, nLocalTr;
3379		var aoLocal = [];
3380		var aApplied = [];
3381		var iColumns = oSettings.aoColumns.length;
3382		var iRowspan, iColspan;
3383	
3384		if ( ! aoSource )
3385		{
3386			return;
3387		}
3388	
3389		if (  bIncludeHidden === undefined )
3390		{
3391			bIncludeHidden = false;
3392		}
3393	
3394		/* Make a copy of the master layout array, but without the visible columns in it */
3395		for ( i=0, iLen=aoSource.length ; i<iLen ; i++ )
3396		{
3397			aoLocal[i] = aoSource[i].slice();
3398			aoLocal[i].nTr = aoSource[i].nTr;
3399	
3400			/* Remove any columns which are currently hidden */
3401			for ( j=iColumns-1 ; j>=0 ; j-- )
3402			{
3403				if ( !oSettings.aoColumns[j].bVisible && !bIncludeHidden )
3404				{
3405					aoLocal[i].splice( j, 1 );
3406				}
3407			}
3408	
3409			/* Prep the applied array - it needs an element for each row */
3410			aApplied.push( [] );
3411		}
3412	
3413		for ( i=0, iLen=aoLocal.length ; i<iLen ; i++ )
3414		{
3415			nLocalTr = aoLocal[i].nTr;
3416	
3417			/* All cells are going to be replaced, so empty out the row */
3418			if ( nLocalTr )
3419			{
3420				while( (n = nLocalTr.firstChild) )
3421				{
3422					nLocalTr.removeChild( n );
3423				}
3424			}
3425	
3426			for ( j=0, jLen=aoLocal[i].length ; j<jLen ; j++ )
3427			{
3428				iRowspan = 1;
3429				iColspan = 1;
3430	
3431				/* Check to see if there is already a cell (row/colspan) covering our target
3432				 * insert point. If there is, then there is nothing to do.
3433				 */
3434				if ( aApplied[i][j] === undefined )
3435				{
3436					nLocalTr.appendChild( aoLocal[i][j].cell );
3437					aApplied[i][j] = 1;
3438	
3439					/* Expand the cell to cover as many rows as needed */
3440					while ( aoLocal[i+iRowspan] !== undefined &&
3441					        aoLocal[i][j].cell == aoLocal[i+iRowspan][j].cell )
3442					{
3443						aApplied[i+iRowspan][j] = 1;
3444						iRowspan++;
3445					}
3446	
3447					/* Expand the cell to cover as many columns as needed */
3448					while ( aoLocal[i][j+iColspan] !== undefined &&
3449					        aoLocal[i][j].cell == aoLocal[i][j+iColspan].cell )
3450					{
3451						/* Must update the applied array over the rows for the columns */
3452						for ( k=0 ; k<iRowspan ; k++ )
3453						{
3454							aApplied[i+k][j+iColspan] = 1;
3455						}
3456						iColspan++;
3457					}
3458	
3459					/* Do the actual expansion in the DOM */
3460					$(aoLocal[i][j].cell)
3461						.attr('rowspan', iRowspan)
3462						.attr('colspan', iColspan);
3463				}
3464			}
3465		}
3466	}
3467	
3468	
3469	/**
3470	 * Insert the required TR nodes into the table for display
3471	 *  @param {object} oSettings dataTables settings object
3472	 *  @param ajaxComplete true after ajax call to complete rendering
3473	 *  @memberof DataTable#oApi
3474	 */
3475	function _fnDraw( oSettings, ajaxComplete )
3476	{
3477		// Allow for state saving and a custom start position
3478		_fnStart( oSettings );
3479	
3480		/* Provide a pre-callback function which can be used to cancel the draw is false is returned */
3481		var aPreDraw = _fnCallbackFire( oSettings, 'aoPreDrawCallback', 'preDraw', [oSettings] );
3482		if ( $.inArray( false, aPreDraw ) !== -1 )
3483		{
3484			_fnProcessingDisplay( oSettings, false );
3485			return;
3486		}
3487	
3488		var anRows = [];
3489		var iRowCount = 0;
3490		var asStripeClasses = oSettings.asStripeClasses;
3491		var iStripes = asStripeClasses.length;
3492		var oLang = oSettings.oLanguage;
3493		var bServerSide = _fnDataSource( oSettings ) == 'ssp';
3494		var aiDisplay = oSettings.aiDisplay;
3495		var iDisplayStart = oSettings._iDisplayStart;
3496		var iDisplayEnd = oSettings.fnDisplayEnd();
3497	
3498		oSettings.bDrawing = true;
3499	
3500		/* Server-side processing draw intercept */
3501		if ( oSettings.bDeferLoading )
3502		{
3503			oSettings.bDeferLoading = false;
3504			oSettings.iDraw++;
3505			_fnProcessingDisplay( oSettings, false );
3506		}
3507		else if ( !bServerSide )
3508		{
3509			oSettings.iDraw++;
3510		}
3511		else if ( !oSettings.bDestroying && !ajaxComplete)
3512		{
3513			_fnAjaxUpdate( oSettings );
3514			return;
3515		}
3516	
3517		if ( aiDisplay.length !== 0 )
3518		{
3519			var iStart = bServerSide ? 0 : iDisplayStart;
3520			var iEnd = bServerSide ? oSettings.aoData.length : iDisplayEnd;
3521	
3522			for ( var j=iStart ; j<iEnd ; j++ )
3523			{
3524				var iDataIndex = aiDisplay[j];
3525				var aoData = oSettings.aoData[ iDataIndex ];
3526				if ( aoData.nTr === null )
3527				{
3528					_fnCreateTr( oSettings, iDataIndex );
3529				}
3530	
3531				var nRow = aoData.nTr;
3532	
3533				/* Remove the old striping classes and then add the new one */
3534				if ( iStripes !== 0 )
3535				{
3536					var sStripe = asStripeClasses[ iRowCount % iStripes ];
3537					if ( aoData._sRowStripe != sStripe )
3538					{
3539						$(nRow).removeClass( aoData._sRowStripe ).addClass( sStripe );
3540						aoData._sRowStripe = sStripe;
3541					}
3542				}
3543	
3544				// Row callback functions - might want to manipulate the row
3545				// iRowCount and j are not currently documented. Are they at all
3546				// useful?
3547				_fnCallbackFire( oSettings, 'aoRowCallback', null,
3548					[nRow, aoData._aData, iRowCount, j, iDataIndex] );
3549	
3550				anRows.push( nRow );
3551				iRowCount++;
3552			}
3553		}
3554		else
3555		{
3556			/* Table is empty - create a row with an empty message in it */
3557			var sZero = oLang.sZeroRecords;
3558			if ( oSettings.iDraw == 1 &&  _fnDataSource( oSettings ) == 'ajax' )
3559			{
3560				sZero = oLang.sLoadingRecords;
3561			}
3562			else if ( oLang.sEmptyTable && oSettings.fnRecordsTotal() === 0 )
3563			{
3564				sZero = oLang.sEmptyTable;
3565			}
3566	
3567			anRows[ 0 ] = $( '<tr/>', { 'class': iStripes ? asStripeClasses[0] : '' } )
3568				.append( $('<td />', {
3569					'valign':  'top',
3570					'colSpan': _fnVisbleColumns( oSettings ),
3571					'class':   oSettings.oClasses.sRowEmpty
3572				} ).html( sZero ) )[0];
3573		}
3574	
3575		/* Header and footer callbacks */
3576		_fnCallbackFire( oSettings, 'aoHeaderCallback', 'header', [ $(oSettings.nTHead).children('tr')[0],
3577			_fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );
3578	
3579		_fnCallbackFire( oSettings, 'aoFooterCallback', 'footer', [ $(oSettings.nTFoot).children('tr')[0],
3580			_fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );
3581	
3582		var body = $(oSettings.nTBody);
3583	
3584		body.children().detach();
3585		body.append( $(anRows) );
3586	
3587		/* Call all required callback functions for the end of a draw */
3588		_fnCallbackFire( oSettings, 'aoDrawCallback', 'draw', [oSettings] );
3589	
3590		/* Draw is complete, sorting and filtering must be as well */
3591		oSettings.bSorted = false;
3592		oSettings.bFiltered = false;
3593		oSettings.bDrawing = false;
3594	}
3595	
3596	
3597	/**
3598	 * Redraw the table - taking account of the various features which are enabled
3599	 *  @param {object} oSettings dataTables settings object
3600	 *  @param {boolean} [holdPosition] Keep the current paging position. By default
3601	 *    the paging is reset to the first page
3602	 *  @memberof DataTable#oApi
3603	 */
3604	function _fnReDraw( settings, holdPosition )
3605	{
3606		var
3607			features = settings.oFeatures,
3608			sort     = features.bSort,
3609			filter   = features.bFilter;
3610	
3611		if ( sort ) {
3612			_fnSort( settings );
3613		}
3614	
3615		if ( filter ) {
3616			_fnFilterComplete( settings, settings.oPreviousSearch );
3617		}
3618		else {
3619			// No filtering, so we want to just use the display master
3620			settings.aiDisplay = settings.aiDisplayMaster.slice();
3621		}
3622	
3623		if ( holdPosition !== true ) {
3624			settings._iDisplayStart = 0;
3625		}
3626	
3627		// Let any modules know about the draw hold position state (used by
3628		// scrolling internally)
3629		settings._drawHold = holdPosition;
3630	
3631		_fnDraw( settings );
3632	
3633		settings._drawHold = false;
3634	}
3635	
3636	
3637	/**
3638	 * Add the options to the page HTML for the table
3639	 *  @param {object} oSettings dataTables settings object
3640	 *  @memberof DataTable#oApi
3641	 */
3642	function _fnAddOptionsHtml ( oSettings )
3643	{
3644		var classes = oSettings.oClasses;
3645		var table = $(oSettings.nTable);
3646		var holding = $('<div/>').insertBefore( table ); // Holding element for speed
3647		var features = oSettings.oFeatures;
3648	
3649		// All DataTables are wrapped in a div
3650		var insert = $('<div/>', {
3651			id:      oSettings.sTableId+'_wrapper',
3652			'class': classes.sWrapper + (oSettings.nTFoot ? '' : ' '+classes.sNoFooter)
3653		} );
3654	
3655		oSettings.nHolding = holding[0];
3656		oSettings.nTableWrapper = insert[0];
3657		oSettings.nTableReinsertBefore = oSettings.nTable.nextSibling;
3658	
3659		/* Loop over the user set positioning and place the elements as needed */
3660		var aDom = oSettings.sDom.split('');
3661		var featureNode, cOption, nNewNode, cNext, sAttr, j;
3662		for ( var i=0 ; i<aDom.length ; i++ )
3663		{
3664			featureNode = null;
3665			cOption = aDom[i];
3666	
3667			if ( cOption == '<' )
3668			{
3669				/* New container div */
3670				nNewNode = $('<div/>')[0];
3671	
3672				/* Check to see if we should append an id and/or a class name to the container */
3673				cNext = aDom[i+1];
3674				if ( cNext == "'" || cNext == '"' )
3675				{
3676					sAttr = "";
3677					j = 2;
3678					while ( aDom[i+j] != cNext )
3679					{
3680						sAttr += aDom[i+j];
3681						j++;
3682					}
3683	
3684					/* Replace jQuery UI constants @todo depreciated */
3685					if ( sAttr == "H" )
3686					{
3687						sAttr = classes.sJUIHeader;
3688					}
3689					else if ( sAttr == "F" )
3690					{
3691						sAttr = classes.sJUIFooter;
3692					}
3693	
3694					/* The attribute can be in the format of "#id.class", "#id" or "class" This logic
3695					 * breaks the string into parts and applies them as needed
3696					 */
3697					if ( sAttr.indexOf('.') != -1 )
3698					{
3699						var aSplit = sAttr.split('.');
3700						nNewNode.id = aSplit[0].substr(1, aSplit[0].length-1);
3701						nNewNode.className = aSplit[1];
3702					}
3703					else if ( sAttr.charAt(0) == "#" )
3704					{
3705						nNewNode.id = sAttr.substr(1, sAttr.length-1);
3706					}
3707					else
3708					{
3709						nNewNode.className = sAttr;
3710					}
3711	
3712					i += j; /* Move along the position array */
3713				}
3714	
3715				insert.append( nNewNode );
3716				insert = $(nNewNode);
3717			}
3718			else if ( cOption == '>' )
3719			{
3720				/* End container div */
3721				insert = insert.parent();
3722			}
3723			// @todo Move options into their own plugins?
3724			else if ( cOption == 'l' && features.bPaginate && features.bLengthChange )
3725			{
3726				/* Length */
3727				featureNode = _fnFeatureHtmlLength( oSettings );
3728			}
3729			else if ( cOption == 'f' && features.bFilter )
3730			{
3731				/* Filter */
3732				featureNode = _fnFeatureHtmlFilter( oSettings );
3733			}
3734			else if ( cOption == 'r' && features.bProcessing )
3735			{
3736				/* pRocessing */
3737				featureNode = _fnFeatureHtmlProcessing( oSettings );
3738			}
3739			else if ( cOption == 't' )
3740			{
3741				/* Table */
3742				featureNode = _fnFeatureHtmlTable( oSettings );
3743			}
3744			else if ( cOption ==  'i' && features.bInfo )
3745			{
3746				/* Info */
3747				featureNode = _fnFeatureHtmlInfo( oSettings );
3748			}
3749			else if ( cOption == 'p' && features.bPaginate )
3750			{
3751				/* Pagination */
3752				featureNode = _fnFeatureHtmlPaginate( oSettings );
3753			}
3754			else if ( DataTable.ext.feature.length !== 0 )
3755			{
3756				/* Plug-in features */
3757				var aoFeatures = DataTable.ext.feature;
3758				for ( var k=0, kLen=aoFeatures.length ; k<kLen ; k++ )
3759				{
3760					if ( cOption == aoFeatures[k].cFeature )
3761					{
3762						featureNode = aoFeatures[k].fnInit( oSettings );
3763						break;
3764					}
3765				}
3766			}
3767	
3768			/* Add to the 2D features array */
3769			if ( featureNode )
3770			{
3771				var aanFeatures = oSettings.aanFeatures;
3772	
3773				if ( ! aanFeatures[cOption] )
3774				{
3775					aanFeatures[cOption] = [];
3776				}
3777	
3778				aanFeatures[cOption].push( featureNode );
3779				insert.append( featureNode );
3780			}
3781		}
3782	
3783		/* Built our DOM structure - replace the holding div with what we want */
3784		holding.replaceWith( insert );
3785		oSettings.nHolding = null;
3786	}
3787	
3788	
3789	/**
3790	 * Use the DOM source to create up an array of header cells. The idea here is to
3791	 * create a layout grid (array) of rows x columns, which contains a reference
3792	 * to the cell that that point in the grid (regardless of col/rowspan), such that
3793	 * any column / row could be removed and the new grid constructed
3794	 *  @param array {object} aLayout Array to store the calculated layout in
3795	 *  @param {node} nThead The header/footer element for the table
3796	 *  @memberof DataTable#oApi
3797	 */
3798	function _fnDetectHeader ( aLayout, nThead )
3799	{
3800		var nTrs = $(nThead).children('tr');
3801		var nTr, nCell;
3802		var i, k, l, iLen, jLen, iColShifted, iColumn, iColspan, iRowspan;
3803		var bUnique;
3804		var fnShiftCol = function ( a, i, j ) {
3805			var k = a[i];
3806	                while ( k[j] ) {
3807				j++;
3808			}
3809			return j;
3810		};
3811	
3812		aLayout.splice( 0, aLayout.length );
3813	
3814		/* We know how many rows there are in the layout - so prep it */
3815		for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
3816		{
3817			aLayout.push( [] );
3818		}
3819	
3820		/* Calculate a layout array */
3821		for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
3822		{
3823			nTr = nTrs[i];
3824			iColumn = 0;
3825	
3826			/* For every cell in the row... */
3827			nCell = nTr.firstChild;
3828			while ( nCell ) {
3829				if ( nCell.nodeName.toUpperCase() == "TD" ||
3830				     nCell.nodeName.toUpperCase() == "TH" )
3831				{
3832					/* Get the col and rowspan attributes from the DOM and sanitise them */
3833					iColspan = nCell.getAttribute('colspan') * 1;
3834					iRowspan = nCell.getAttribute('rowspan') * 1;
3835					iColspan = (!iColspan || iColspan===0 || iColspan===1) ? 1 : iColspan;
3836					iRowspan = (!iRowspan || iRowspan===0 || iRowspan===1) ? 1 : iRowspan;
3837	
3838					/* There might be colspan cells already in this row, so shift our target
3839					 * accordingly
3840					 */
3841					iColShifted = fnShiftCol( aLayout, i, iColumn );
3842	
3843					/* Cache calculation for unique columns */
3844					bUnique = iColspan === 1 ? true : false;
3845	
3846					/* If there is col / rowspan, copy the information into the layout grid */
3847					for ( l=0 ; l<iColspan ; l++ )
3848					{
3849						for ( k=0 ; k<iRowspan ; k++ )
3850						{
3851							aLayout[i+k][iColShifted+l] = {
3852								"cell": nCell,
3853								"unique": bUnique
3854							};
3855							aLayout[i+k].nTr = nTr;
3856						}
3857					}
3858				}
3859				nCell = nCell.nextSibling;
3860			}
3861		}
3862	}
3863	
3864	
3865	/**
3866	 * Get an array of unique th elements, one for each column
3867	 *  @param {object} oSettings dataTables settings object
3868	 *  @param {node} nHeader automatically detect the layout from this node - optional
3869	 *  @param {array} aLayout thead/tfoot layout from _fnDetectHeader - optional
3870	 *  @returns array {node} aReturn list of unique th's
3871	 *  @memberof DataTable#oApi
3872	 */
3873	function _fnGetUniqueThs ( oSettings, nHeader, aLayout )
3874	{
3875		var aReturn = [];
3876		if ( !aLayout )
3877		{
3878			aLayout = oSettings.aoHeader;
3879			if ( nHeader )
3880			{
3881				aLayout = [];
3882				_fnDetectHeader( aLayout, nHeader );
3883			}
3884		}
3885	
3886		for ( var i=0, iLen=aLayout.length ; i<iLen ; i++ )
3887		{
3888			for ( var j=0, jLen=aLayout[i].length ; j<jLen ; j++ )
3889			{
3890				if ( aLayout[i][j].unique &&
3891					 (!aReturn[j] || !oSettings.bSortCellsTop) )
3892				{
3893					aReturn[j] = aLayout[i][j].cell;
3894				}
3895			}
3896		}
3897	
3898		return aReturn;
3899	}
3900	
3901	/**
3902	 * Set the start position for draw
3903	 *  @param {object} oSettings dataTables settings object
3904	 */
3905	function _fnStart( oSettings )
3906	{
3907		var bServerSide = _fnDataSource( oSettings ) == 'ssp';
3908		var iInitDisplayStart = oSettings.iInitDisplayStart;
3909	
3910		// Check and see if we have an initial draw position from state saving
3911		if ( iInitDisplayStart !== undefined && iInitDisplayStart !== -1 )
3912		{
3913			oSettings._iDisplayStart = bServerSide ?
3914				iInitDisplayStart :
3915				iInitDisplayStart >= oSettings.fnRecordsDisplay() ?
3916					0 :
3917					iInitDisplayStart;
3918	
3919			oSettings.iInitDisplayStart = -1;
3920		}
3921	}
3922	
3923	/**
3924	 * Create an Ajax call based on the table's settings, taking into account that
3925	 * parameters can have multiple forms, and backwards compatibility.
3926	 *
3927	 * @param {object} oSettings dataTables settings object
3928	 * @param {array} data Data to send to the server, required by
3929	 *     DataTables - may be augmented by developer callbacks
3930	 * @param {function} fn Callback function to run when data is obtained
3931	 */
3932	function _fnBuildAjax( oSettings, data, fn )
3933	{
3934		// Compatibility with 1.9-, allow fnServerData and event to manipulate
3935		_fnCallbackFire( oSettings, 'aoServerParams', 'serverParams', [data] );
3936	
3937		// Convert to object based for 1.10+ if using the old array scheme which can
3938		// come from server-side processing or serverParams
3939		if ( data && Array.isArray(data) ) {
3940			var tmp = {};
3941			var rbracket = /(.*?)\[\]$/;
3942	
3943			$.each( data, function (key, val) {
3944				var match = val.name.match(rbracket);
3945	
3946				if ( match ) {
3947					// Support for arrays
3948					var name = match[0];
3949	
3950					if ( ! tmp[ name ] ) {
3951						tmp[ name ] = [];
3952					}
3953					tmp[ name ].push( val.value );
3954				}
3955				else {
3956					tmp[val.name] = val.value;
3957				}
3958			} );
3959			data = tmp;
3960		}
3961	
3962		var ajaxData;
3963		var ajax = oSettings.ajax;
3964		var instance = oSettings.oInstance;
3965		var callback = function ( json ) {
3966			var status = oSettings.jqXHR
3967				? oSettings.jqXHR.status
3968				: null;
3969	
3970			if ( json === null || (typeof status === 'number' && status == 204 ) ) {
3971				json = {};
3972				_fnAjaxDataSrc( oSettings, json, [] );
3973			}
3974	
3975			var error = json.error || json.sError;
3976			if ( error ) {
3977				_fnLog( oSettings, 0, error );
3978			}
3979	
3980			oSettings.json = json;
3981	
3982			_fnCallbackFire( oSettings, null, 'xhr', [oSettings, json, oSettings.jqXHR] );
3983			fn( json );
3984		};
3985	
3986		if ( $.isPlainObject( ajax ) && ajax.data )
3987		{
3988			ajaxData = ajax.data;
3989	
3990			var newData = typeof ajaxData === 'function' ?
3991				ajaxData( data, oSettings ) :  // fn can manipulate data or return
3992				ajaxData;                      // an object object or array to merge
3993	
3994			// If the function returned something, use that alone
3995			data = typeof ajaxData === 'function' && newData ?
3996				newData :
3997				$.extend( true, data, newData );
3998	
3999			// Remove the data property as we've resolved it already and don't want
4000			// jQuery to do it again (it is restored at the end of the function)
4001			delete ajax.data;
4002		}
4003	
4004		var baseAjax = {
4005			"data": data,
4006			"success": callback,
4007			"dataType": "json",
4008			"cache": false,
4009			"type": oSettings.sServerMethod,
4010			"error": function (xhr, error, thrown) {
4011				var ret = _fnCallbackFire( oSettings, null, 'xhr', [oSettings, null, oSettings.jqXHR] );
4012	
4013				if ( $.inArray( true, ret ) === -1 ) {
4014					if ( error == "parsererror" ) {
4015						_fnLog( oSettings, 0, 'Invalid JSON response', 1 );
4016					}
4017					else if ( xhr.readyState === 4 ) {
4018						_fnLog( oSettings, 0, 'Ajax error', 7 );
4019					}
4020				}
4021	
4022				_fnProcessingDisplay( oSettings, false );
4023			}
4024		};
4025	
4026		// Store the data submitted for the API
4027		oSettings.oAjaxData = data;
4028	
4029		// Allow plug-ins and external processes to modify the data
4030		_fnCallbackFire( oSettings, null, 'preXhr', [oSettings, data] );
4031	
4032		if ( oSettings.fnServerData )
4033		{
4034			// DataTables 1.9- compatibility
4035			oSettings.fnServerData.call( instance,
4036				oSettings.sAjaxSource,
4037				$.map( data, function (val, key) { // Need to convert back to 1.9 trad format
4038					return { name: key, value: val };
4039				} ),
4040				callback,
4041				oSettings
4042			);
4043		}
4044		else if ( oSettings.sAjaxSource || typeof ajax === 'string' )
4045		{
4046			// DataTables 1.9- compatibility
4047			oSettings.jqXHR = $.ajax( $.extend( baseAjax, {
4048				url: ajax || oSettings.sAjaxSource
4049			} ) );
4050		}
4051		else if ( typeof ajax === 'function' )
4052		{
4053			// Is a function - let the caller define what needs to be done
4054			oSettings.jqXHR = ajax.call( instance, data, callback, oSettings );
4055		}
4056		else
4057		{
4058			// Object to extend the base settings
4059			oSettings.jqXHR = $.ajax( $.extend( baseAjax, ajax ) );
4060	
4061			// Restore for next time around
4062			ajax.data = ajaxData;
4063		}
4064	}
4065	
4066	
4067	/**
4068	 * Update the table using an Ajax call
4069	 *  @param {object} settings dataTables settings object
4070	 *  @returns {boolean} Block the table drawing or not
4071	 *  @memberof DataTable#oApi
4072	 */
4073	function _fnAjaxUpdate( settings )
4074	{
4075		settings.iDraw++;
4076		_fnProcessingDisplay( settings, true );
4077	
4078		_fnBuildAjax(
4079			settings,
4080			_fnAjaxParameters( settings ),
4081			function(json) {
4082				_fnAjaxUpdateDraw( settings, json );
4083			}
4084		);
4085	}
4086	
4087	
4088	/**
4089	 * Build up the parameters in an object needed for a server-side processing
4090	 * request. Note that this is basically done twice, is different ways - a modern
4091	 * method which is used by default in DataTables 1.10 which uses objects and
4092	 * arrays, or the 1.9- method with is name / value pairs. 1.9 method is used if
4093	 * the sAjaxSource option is used in the initialisation, or the legacyAjax
4094	 * option is set.
4095	 *  @param {object} oSettings dataTables settings object
4096	 *  @returns {bool} block the table drawing or not
4097	 *  @memberof DataTable#oApi
4098	 */
4099	function _fnAjaxParameters( settings )
4100	{
4101		var
4102			columns = settings.aoColumns,
4103			columnCount = columns.length,
4104			features = settings.oFeatures,
4105			preSearch = settings.oPreviousSearch,
4106			preColSearch = settings.aoPreSearchCols,
4107			i, data = [], dataProp, column, columnSearch,
4108			sort = _fnSortFlatten( settings ),
4109			displayStart = settings._iDisplayStart,
4110			displayLength = features.bPaginate !== false ?
4111				settings._iDisplayLength :
4112				-1;
4113	
4114		var param = function ( name, value ) {
4115			data.push( { 'name': name, 'value': value } );
4116		};
4117	
4118		// DataTables 1.9- compatible method
4119		param( 'sEcho',          settings.iDraw );
4120		param( 'iColumns',       columnCount );
4121		param( 'sColumns',       _pluck( columns, 'sName' ).join(',') );
4122		param( 'iDisplayStart',  displayStart );
4123		param( 'iDisplayLength', displayLength );
4124	
4125		// DataTables 1.10+ method
4126		var d = {
4127			draw:    settings.iDraw,
4128			columns: [],
4129			order:   [],
4130			start:   displayStart,
4131			length:  displayLength,
4132			search:  {
4133				value: preSearch.sSearch,
4134				regex: preSearch.bRegex
4135			}
4136		};
4137	
4138		for ( i=0 ; i<columnCount ; i++ ) {
4139			column = columns[i];
4140			columnSearch = preColSearch[i];
4141			dataProp = typeof column.mData=="function" ? 'function' : column.mData ;
4142	
4143			d.columns.push( {
4144				data:       dataProp,
4145				name:       column.sName,
4146				searchable: column.bSearchable,
4147				orderable:  column.bSortable,
4148				search:     {
4149					value: columnSearch.sSearch,
4150					regex: columnSearch.bRegex
4151				}
4152			} );
4153	
4154			param( "mDataProp_"+i, dataProp );
4155	
4156			if ( features.bFilter ) {
4157				param( 'sSearch_'+i,     columnSearch.sSearch );
4158				param( 'bRegex_'+i,      columnSearch.bRegex );
4159				param( 'bSearchable_'+i, column.bSearchable );
4160			}
4161	
4162			if ( features.bSort ) {
4163				param( 'bSortable_'+i, column.bSortable );
4164			}
4165		}
4166	
4167		if ( features.bFilter ) {
4168			param( 'sSearch', preSearch.sSearch );
4169			param( 'bRegex', preSearch.bRegex );
4170		}
4171	
4172		if ( features.bSort ) {
4173			$.each( sort, function ( i, val ) {
4174				d.order.push( { column: val.col, dir: val.dir } );
4175	
4176				param( 'iSortCol_'+i, val.col );
4177				param( 'sSortDir_'+i, val.dir );
4178			} );
4179	
4180			param( 'iSortingCols', sort.length );
4181		}
4182	
4183		// If the legacy.ajax parameter is null, then we automatically decide which
4184		// form to use, based on sAjaxSource
4185		var legacy = DataTable.ext.legacy.ajax;
4186		if ( legacy === null ) {
4187			return settings.sAjaxSource ? data : d;
4188		}
4189	
4190		// Otherwise, if legacy has been specified then we use that to decide on the
4191		// form
4192		return legacy ? data : d;
4193	}
4194	
4195	
4196	/**
4197	 * Data the data from the server (nuking the old) and redraw the table
4198	 *  @param {object} oSettings dataTables settings object
4199	 *  @param {object} json json data return from the server.
4200	 *  @param {string} json.sEcho Tracking flag for DataTables to match requests
4201	 *  @param {int} json.iTotalRecords Number of records in the data set, not accounting for filtering
4202	 *  @param {int} json.iTotalDisplayRecords Number of records in the data set, accounting for filtering
4203	 *  @param {array} json.aaData The data to display on this page
4204	 *  @param {string} [json.sColumns] Column ordering (sName, comma separated)
4205	 *  @memberof DataTable#oApi
4206	 */
4207	function _fnAjaxUpdateDraw ( settings, json )
4208	{
4209		// v1.10 uses camelCase variables, while 1.9 uses Hungarian notation.
4210		// Support both
4211		var compat = function ( old, modern ) {
4212			return json[old] !== undefined ? json[old] : json[modern];
4213		};
4214	
4215		var data = _fnAjaxDataSrc( settings, json );
4216		var draw            = compat( 'sEcho',                'draw' );
4217		var recordsTotal    = compat( 'iTotalRecords',        'recordsTotal' );
4218		var recordsFiltered = compat( 'iTotalDisplayRecords', 'recordsFiltered' );
4219	
4220		if ( draw !== undefined ) {
4221			// Protect against out of sequence returns
4222			if ( draw*1 < settings.iDraw ) {
4223				return;
4224			}
4225			settings.iDraw = draw * 1;
4226		}
4227	
4228		// No data in returned object, so rather than an array, we show an empty table
4229		if ( ! data ) {
4230			data = [];
4231		}
4232	
4233		_fnClearTable( settings );
4234		settings._iRecordsTotal   = parseInt(recordsTotal, 10);
4235		settings._iRecordsDisplay = parseInt(recordsFiltered, 10);
4236	
4237		for ( var i=0, ien=data.length ; i<ien ; i++ ) {
4238			_fnAddData( settings, data[i] );
4239		}
4240		settings.aiDisplay = settings.aiDisplayMaster.slice();
4241	
4242		_fnDraw( settings, true );
4243	
4244		if ( ! settings._bInitComplete ) {
4245			_fnInitComplete( settings, json );
4246		}
4247	
4248		_fnProcessingDisplay( settings, false );
4249	}
4250	
4251	
4252	/**
4253	 * Get the data from the JSON data source to use for drawing a table. Using
4254	 * `_fnGetObjectDataFn` allows the data to be sourced from a property of the
4255	 * source object, or from a processing function.
4256	 *  @param {object} oSettings dataTables settings object
4257	 *  @param  {object} json Data source object / array from the server
4258	 *  @return {array} Array of data to use
4259	 */
4260	 function _fnAjaxDataSrc ( oSettings, json, write )
4261	 {
4262		var dataSrc = $.isPlainObject( oSettings.ajax ) && oSettings.ajax.dataSrc !== undefined ?
4263			oSettings.ajax.dataSrc :
4264			oSettings.sAjaxDataProp; // Compatibility with 1.9-.
4265	
4266		if ( ! write ) {
4267			if ( dataSrc === 'data' ) {
4268				// If the default, then we still want to support the old style, and safely ignore
4269				// it if possible
4270				return json.aaData || json[dataSrc];
4271			}
4272	
4273			return dataSrc !== "" ?
4274				_fnGetObjectDataFn( dataSrc )( json ) :
4275				json;
4276		}
4277	
4278		// set
4279		_fnSetObjectDataFn( dataSrc )( json, write );
4280	}
4281	
4282	/**
4283	 * Generate the node required for filtering text
4284	 *  @returns {node} Filter control element
4285	 *  @param {object} oSettings dataTables settings object
4286	 *  @memberof DataTable#oApi
4287	 */
4288	function _fnFeatureHtmlFilter ( settings )
4289	{
4290		var classes = settings.oClasses;
4291		var tableId = settings.sTableId;
4292		var language = settings.oLanguage;
4293		var previousSearch = settings.oPreviousSearch;
4294		var features = settings.aanFeatures;
4295		var input = '<input type="search" class="'+classes.sFilterInput+'"/>';
4296	
4297		var str = language.sSearch;
4298		str = str.match(/_INPUT_/) ?
4299			str.replace('_INPUT_', input) :
4300			str+input;
4301	
4302		var filter = $('<div/>', {
4303				'id': ! features.f ? tableId+'_filter' : null,
4304				'class': classes.sFilter
4305			} )
4306			.append( $('<label/>' ).append( str ) );
4307	
4308		var searchFn = function(event) {
4309			/* Update all other filter input elements for the new display */
4310			var n = features.f;
4311			var val = !this.value ? "" : this.value; // mental IE8 fix :-(
4312			if(previousSearch.return && event.key !== "Enter") {
4313				return;
4314			}
4315			/* Now do the filter */
4316			if ( val != previousSearch.sSearch ) {
4317				_fnFilterComplete( settings, {
4318					"sSearch": val,
4319					"bRegex": previousSearch.bRegex,
4320					"bSmart": previousSearch.bSmart ,
4321					"bCaseInsensitive": previousSearch.bCaseInsensitive,
4322					"return": previousSearch.return
4323				} );
4324	
4325				// Need to redraw, without resorting
4326				settings._iDisplayStart = 0;
4327				_fnDraw( settings );
4328			}
4329		};
4330	
4331		var searchDelay = settings.searchDelay !== null ?
4332			settings.searchDelay :
4333			_fnDataSource( settings ) === 'ssp' ?
4334				400 :
4335				0;
4336	
4337		var jqFilter = $('input', filter)
4338			.val( previousSearch.sSearch )
4339			.attr( 'placeholder', language.sSearchPlaceholder )
4340			.on(
4341				'keyup.DT search.DT input.DT paste.DT cut.DT',
4342				searchDelay ?
4343					_fnThrottle( searchFn, searchDelay ) :
4344					searchFn
4345			)
4346			.on( 'mouseup', function(e) {
4347				// Edge fix! Edge 17 does not trigger anything other than mouse events when clicking
4348				// on the clear icon (Edge bug 17584515). This is safe in other browsers as `searchFn`
4349				// checks the value to see if it has changed. In other browsers it won't have.
4350				setTimeout( function () {
4351					searchFn.call(jqFilter[0], e);
4352				}, 10);
4353			} )
4354			.on( 'keypress.DT', function(e) {
4355				/* Prevent form submission */
4356				if ( e.keyCode == 13 ) {
4357					return false;
4358				}
4359			} )
4360			.attr('aria-controls', tableId);
4361	
4362		// Update the input elements whenever the table is filtered
4363		$(settings.nTable).on( 'search.dt.DT', function ( ev, s ) {
4364			if ( settings === s ) {
4365				// IE9 throws an 'unknown error' if document.activeElement is used
4366				// inside an iframe or frame...
4367				try {
4368					if ( jqFilter[0] !== document.activeElement ) {
4369						jqFilter.val( previousSearch.sSearch );
4370					}
4371				}
4372				catch ( e ) {}
4373			}
4374		} );
4375	
4376		return filter[0];
4377	}
4378	
4379	
4380	/**
4381	 * Filter the table using both the global filter and column based filtering
4382	 *  @param {object} oSettings dataTables settings object
4383	 *  @param {object} oSearch search information
4384	 *  @param {int} [iForce] force a research of the master array (1) or not (undefined or 0)
4385	 *  @memberof DataTable#oApi
4386	 */
4387	function _fnFilterComplete ( oSettings, oInput, iForce )
4388	{
4389		var oPrevSearch = oSettings.oPreviousSearch;
4390		var aoPrevSearch = oSettings.aoPreSearchCols;
4391		var fnSaveFilter = function ( oFilter ) {
4392			/* Save the filtering values */
4393			oPrevSearch.sSearch = oFilter.sSearch;
4394			oPrevSearch.bRegex = oFilter.bRegex;
4395			oPrevSearch.bSmart = oFilter.bSmart;
4396			oPrevSearch.bCaseInsensitive = oFilter.bCaseInsensitive;
4397			oPrevSearch.return = oFilter.return;
4398		};
4399		var fnRegex = function ( o ) {
4400			// Backwards compatibility with the bEscapeRegex option
4401			return o.bEscapeRegex !== undefined ? !o.bEscapeRegex : o.bRegex;
4402		};
4403	
4404		// Resolve any column types that are unknown due to addition or invalidation
4405		// @todo As per sort - can this be moved into an event handler?
4406		_fnColumnTypes( oSettings );
4407	
4408		/* In server-side processing all filtering is done by the server, so no point hanging around here */
4409		if ( _fnDataSource( oSettings ) != 'ssp' )
4410		{
4411			/* Global filter */
4412			_fnFilter( oSettings, oInput.sSearch, iForce, fnRegex(oInput), oInput.bSmart, oInput.bCaseInsensitive, oInput.return );
4413			fnSaveFilter( oInput );
4414	
4415			/* Now do the individual column filter */
4416			for ( var i=0 ; i<aoPrevSearch.length ; i++ )
4417			{
4418				_fnFilterColumn( oSettings, aoPrevSearch[i].sSearch, i, fnRegex(aoPrevSearch[i]),
4419					aoPrevSearch[i].bSmart, aoPrevSearch[i].bCaseInsensitive );
4420			}
4421	
4422			/* Custom filtering */
4423			_fnFilterCustom( oSettings );
4424		}
4425		else
4426		{
4427			fnSaveFilter( oInput );
4428		}
4429	
4430		/* Tell the draw function we have been filtering */
4431		oSettings.bFiltered = true;
4432		_fnCallbackFire( oSettings, null, 'search', [oSettings] );
4433	}
4434	
4435	
4436	/**
4437	 * Apply custom filtering functions
4438	 *  @param {object} oSettings dataTables settings object
4439	 *  @memberof DataTable#oApi
4440	 */
4441	function _fnFilterCustom( settings )
4442	{
4443		var filters = DataTable.ext.search;
4444		var displayRows = settings.aiDisplay;
4445		var row, rowIdx;
4446	
4447		for ( var i=0, ien=filters.length ; i<ien ; i++ ) {
4448			var rows = [];
4449	
4450			// Loop over each row and see if it should be included
4451			for ( var j=0, jen=displayRows.length ; j<jen ; j++ ) {
4452				rowIdx = displayRows[ j ];
4453				row = settings.aoData[ rowIdx ];
4454	
4455				if ( filters[i]( settings, row._aFilterData, rowIdx, row._aData, j ) ) {
4456					rows.push( rowIdx );
4457				}
4458			}
4459	
4460			// So the array reference doesn't break set the results into the
4461			// existing array
4462			displayRows.length = 0;
4463			$.merge( displayRows, rows );
4464		}
4465	}
4466	
4467	
4468	/**
4469	 * Filter the table on a per-column basis
4470	 *  @param {object} oSettings dataTables settings object
4471	 *  @param {string} sInput string to filter on
4472	 *  @param {int} iColumn column to filter
4473	 *  @param {bool} bRegex treat search string as a regular expression or not
4474	 *  @param {bool} bSmart use smart filtering or not
4475	 *  @param {bool} bCaseInsensitive Do case insensitive matching or not
4476	 *  @memberof DataTable#oApi
4477	 */
4478	function _fnFilterColumn ( settings, searchStr, colIdx, regex, smart, caseInsensitive )
4479	{
4480		if ( searchStr === '' ) {
4481			return;
4482		}
4483	
4484		var data;
4485		var out = [];
4486		var display = settings.aiDisplay;
4487		var rpSearch = _fnFilterCreateSearch( searchStr, regex, smart, caseInsensitive );
4488	
4489		for ( var i=0 ; i<display.length ; i++ ) {
4490			data = settings.aoData[ display[i] ]._aFilterData[ colIdx ];
4491	
4492			if ( rpSearch.test( data ) ) {
4493				out.push( display[i] );
4494			}
4495		}
4496	
4497		settings.aiDisplay = out;
4498	}
4499	
4500	
4501	/**
4502	 * Filter the data table based on user input and draw the table
4503	 *  @param {object} settings dataTables settings object
4504	 *  @param {string} input string to filter on
4505	 *  @param {int} force optional - force a research of the master array (1) or not (undefined or 0)
4506	 *  @param {bool} regex treat as a regular expression or not
4507	 *  @param {bool} smart perform smart filtering or not
4508	 *  @param {bool} caseInsensitive Do case insensitive matching or not
4509	 *  @memberof DataTable#oApi
4510	 */
4511	function _fnFilter( settings, input, force, regex, smart, caseInsensitive )
4512	{
4513		var rpSearch = _fnFilterCreateSearch( input, regex, smart, caseInsensitive );
4514		var prevSearch = settings.oPreviousSearch.sSearch;
4515		var displayMaster = settings.aiDisplayMaster;
4516		var display, invalidated, i;
4517		var filtered = [];
4518	
4519		// Need to take account of custom filtering functions - always filter
4520		if ( DataTable.ext.search.length !== 0 ) {
4521			force = true;
4522		}
4523	
4524		// Check if any of the rows were invalidated
4525		invalidated = _fnFilterData( settings );
4526	
4527		// If the input is blank - we just want the full data set
4528		if ( input.length <= 0 ) {
4529			settings.aiDisplay = displayMaster.slice();
4530		}
4531		else {
4532			// New search - start from the master array
4533			if ( invalidated ||
4534				 force ||
4535				 regex ||
4536				 prevSearch.length > input.length ||
4537				 input.indexOf(prevSearch) !== 0 ||
4538				 settings.bSorted // On resort, the display master needs to be
4539				                  // re-filtered since indexes will have changed
4540			) {
4541				settings.aiDisplay = displayMaster.slice();
4542			}
4543	
4544			// Search the display array
4545			display = settings.aiDisplay;
4546	
4547			for ( i=0 ; i<display.length ; i++ ) {
4548				if ( rpSearch.test( settings.aoData[ display[i] ]._sFilterRow ) ) {
4549					filtered.push( display[i] );
4550				}
4551			}
4552	
4553			settings.aiDisplay = filtered;
4554		}
4555	}
4556	
4557	
4558	/**
4559	 * Build a regular expression object suitable for searching a table
4560	 *  @param {string} sSearch string to search for
4561	 *  @param {bool} bRegex treat as a regular expression or not
4562	 *  @param {bool} bSmart perform smart filtering or not
4563	 *  @param {bool} bCaseInsensitive Do case insensitive matching or not
4564	 *  @returns {RegExp} constructed object
4565	 *  @memberof DataTable#oApi
4566	 */
4567	function _fnFilterCreateSearch( search, regex, smart, caseInsensitive )
4568	{
4569		search = regex ?
4570			search :
4571			_fnEscapeRegex( search );
4572		
4573		if ( smart ) {
4574			/* For smart filtering we want to allow the search to work regardless of
4575			 * word order. We also want double quoted text to be preserved, so word
4576			 * order is important - a la google. So this is what we want to
4577			 * generate:
4578			 * 
4579			 * ^(?=.*?\bone\b)(?=.*?\btwo three\b)(?=.*?\bfour\b).*$
4580			 */
4581			var a = $.map( search.match( /"[^"]+"|[^ ]+/g ) || [''], function ( word ) {
4582				if ( word.charAt(0) === '"' ) {
4583					var m = word.match( /^"(.*)"$/ );
4584					word = m ? m[1] : word;
4585				}
4586	
4587				return word.replace('"', '');
4588			} );
4589	
4590			search = '^(?=.*?'+a.join( ')(?=.*?' )+').*$';
4591		}
4592	
4593		return new RegExp( search, caseInsensitive ? 'i' : '' );
4594	}
4595	
4596	
4597	/**
4598	 * Escape a string such that it can be used in a regular expression
4599	 *  @param {string} sVal string to escape
4600	 *  @returns {string} escaped string
4601	 *  @memberof DataTable#oApi
4602	 */
4603	var _fnEscapeRegex = DataTable.util.escapeRegex;
4604	
4605	var __filter_div = $('<div>')[0];
4606	var __filter_div_textContent = __filter_div.textContent !== undefined;
4607	
4608	// Update the filtering data for each row if needed (by invalidation or first run)
4609	function _fnFilterData ( settings )
4610	{
4611		var columns = settings.aoColumns;
4612		var column;
4613		var i, j, ien, jen, filterData, cellData, row;
4614		var wasInvalidated = false;
4615	
4616		for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
4617			row = settings.aoData[i];
4618	
4619			if ( ! row._aFilterData ) {
4620				filterData = [];
4621	
4622				for ( j=0, jen=columns.length ; j<jen ; j++ ) {
4623					column = columns[j];
4624	
4625					if ( column.bSearchable ) {
4626						cellData = _fnGetCellData( settings, i, j, 'filter' );
4627	
4628						// Search in DataTables 1.10 is string based. In 1.11 this
4629						// should be altered to also allow strict type checking.
4630						if ( cellData === null ) {
4631							cellData = '';
4632						}
4633	
4634						if ( typeof cellData !== 'string' && cellData.toString ) {
4635							cellData = cellData.toString();
4636						}
4637					}
4638					else {
4639						cellData = '';
4640					}
4641	
4642					// If it looks like there is an HTML entity in the string,
4643					// attempt to decode it so sorting works as expected. Note that
4644					// we could use a single line of jQuery to do this, but the DOM
4645					// method used here is much faster http://jsperf.com/html-decode
4646					if ( cellData.indexOf && cellData.indexOf('&') !== -1 ) {
4647						__filter_div.innerHTML = cellData;
4648						cellData = __filter_div_textContent ?
4649							__filter_div.textContent :
4650							__filter_div.innerText;
4651					}
4652	
4653					if ( cellData.replace ) {
4654						cellData = cellData.replace(/[\r\n\u2028]/g, '');
4655					}
4656	
4657					filterData.push( cellData );
4658				}
4659	
4660				row._aFilterData = filterData;
4661				row._sFilterRow = filterData.join('  ');
4662				wasInvalidated = true;
4663			}
4664		}
4665	
4666		return wasInvalidated;
4667	}
4668	
4669	
4670	/**
4671	 * Convert from the internal Hungarian notation to camelCase for external
4672	 * interaction
4673	 *  @param {object} obj Object to convert
4674	 *  @returns {object} Inverted object
4675	 *  @memberof DataTable#oApi
4676	 */
4677	function _fnSearchToCamel ( obj )
4678	{
4679		return {
4680			search:          obj.sSearch,
4681			smart:           obj.bSmart,
4682			regex:           obj.bRegex,
4683			caseInsensitive: obj.bCaseInsensitive
4684		};
4685	}
4686	
4687	
4688	
4689	/**
4690	 * Convert from camelCase notation to the internal Hungarian. We could use the
4691	 * Hungarian convert function here, but this is cleaner
4692	 *  @param {object} obj Object to convert
4693	 *  @returns {object} Inverted object
4694	 *  @memberof DataTable#oApi
4695	 */
4696	function _fnSearchToHung ( obj )
4697	{
4698		return {
4699			sSearch:          obj.search,
4700			bSmart:           obj.smart,
4701			bRegex:           obj.regex,
4702			bCaseInsensitive: obj.caseInsensitive
4703		};
4704	}
4705	
4706	/**
4707	 * Generate the node required for the info display
4708	 *  @param {object} oSettings dataTables settings object
4709	 *  @returns {node} Information element
4710	 *  @memberof DataTable#oApi
4711	 */
4712	function _fnFeatureHtmlInfo ( settings )
4713	{
4714		var
4715			tid = settings.sTableId,
4716			nodes = settings.aanFeatures.i,
4717			n = $('<div/>', {
4718				'class': settings.oClasses.sInfo,
4719				'id': ! nodes ? tid+'_info' : null
4720			} );
4721	
4722		if ( ! nodes ) {
4723			// Update display on each draw
4724			settings.aoDrawCallback.push( {
4725				"fn": _fnUpdateInfo,
4726				"sName": "information"
4727			} );
4728	
4729			n
4730				.attr( 'role', 'status' )
4731				.attr( 'aria-live', 'polite' );
4732	
4733			// Table is described by our info div
4734			$(settings.nTable).attr( 'aria-describedby', tid+'_info' );
4735		}
4736	
4737		return n[0];
4738	}
4739	
4740	
4741	/**
4742	 * Update the information elements in the display
4743	 *  @param {object} settings dataTables settings object
4744	 *  @memberof DataTable#oApi
4745	 */
4746	function _fnUpdateInfo ( settings )
4747	{
4748		/* Show information about the table */
4749		var nodes = settings.aanFeatures.i;
4750		if ( nodes.length === 0 ) {
4751			return;
4752		}
4753	
4754		var
4755			lang  = settings.oLanguage,
4756			start = settings._iDisplayStart+1,
4757			end   = settings.fnDisplayEnd(),
4758			max   = settings.fnRecordsTotal(),
4759			total = settings.fnRecordsDisplay(),
4760			out   = total ?
4761				lang.sInfo :
4762				lang.sInfoEmpty;
4763	
4764		if ( total !== max ) {
4765			/* Record set after filtering */
4766			out += ' ' + lang.sInfoFiltered;
4767		}
4768	
4769		// Convert the macros
4770		out += lang.sInfoPostFix;
4771		out = _fnInfoMacros( settings, out );
4772	
4773		var callback = lang.fnInfoCallback;
4774		if ( callback !== null ) {
4775			out = callback.call( settings.oInstance,
4776				settings, start, end, max, total, out
4777			);
4778		}
4779	
4780		$(nodes).html( out );
4781	}
4782	
4783	
4784	function _fnInfoMacros ( settings, str )
4785	{
4786		// When infinite scrolling, we are always starting at 1. _iDisplayStart is used only
4787		// internally
4788		var
4789			formatter  = settings.fnFormatNumber,
4790			start      = settings._iDisplayStart+1,
4791			len        = settings._iDisplayLength,
4792			vis        = settings.fnRecordsDisplay(),
4793			all        = len === -1;
4794	
4795		return str.
4796			replace(/_START_/g, formatter.call( settings, start ) ).
4797			replace(/_END_/g,   formatter.call( settings, settings.fnDisplayEnd() ) ).
4798			replace(/_MAX_/g,   formatter.call( settings, settings.fnRecordsTotal() ) ).
4799			replace(/_TOTAL_/g, formatter.call( settings, vis ) ).
4800			replace(/_PAGE_/g,  formatter.call( settings, all ? 1 : Math.ceil( start / len ) ) ).
4801			replace(/_PAGES_/g, formatter.call( settings, all ? 1 : Math.ceil( vis / len ) ) );
4802	}
4803	
4804	
4805	
4806	/**
4807	 * Draw the table for the first time, adding all required features
4808	 *  @param {object} settings dataTables settings object
4809	 *  @memberof DataTable#oApi
4810	 */
4811	function _fnInitialise ( settings )
4812	{
4813		var i, iLen, iAjaxStart=settings.iInitDisplayStart;
4814		var columns = settings.aoColumns, column;
4815		var features = settings.oFeatures;
4816		var deferLoading = settings.bDeferLoading; // value modified by the draw
4817	
4818		/* Ensure that the table data is fully initialised */
4819		if ( ! settings.bInitialised ) {
4820			setTimeout( function(){ _fnInitialise( settings ); }, 200 );
4821			return;
4822		}
4823	
4824		/* Show the display HTML options */
4825		_fnAddOptionsHtml( settings );
4826	
4827		/* Build and draw the header / footer for the table */
4828		_fnBuildHead( settings );
4829		_fnDrawHead( settings, settings.aoHeader );
4830		_fnDrawHead( settings, settings.aoFooter );
4831	
4832		/* Okay to show that something is going on now */
4833		_fnProcessingDisplay( settings, true );
4834	
4835		/* Calculate sizes for columns */
4836		if ( features.bAutoWidth ) {
4837			_fnCalculateColumnWidths( settings );
4838		}
4839	
4840		for ( i=0, iLen=columns.length ; i<iLen ; i++ ) {
4841			column = columns[i];
4842	
4843			if ( column.sWidth ) {
4844				column.nTh.style.width = _fnStringToCss( column.sWidth );
4845			}
4846		}
4847	
4848		_fnCallbackFire( settings, null, 'preInit', [settings] );
4849	
4850		// If there is default sorting required - let's do it. The sort function
4851		// will do the drawing for us. Otherwise we draw the table regardless of the
4852		// Ajax source - this allows the table to look initialised for Ajax sourcing
4853		// data (show 'loading' message possibly)
4854		_fnReDraw( settings );
4855	
4856		// Server-side processing init complete is done by _fnAjaxUpdateDraw
4857		var dataSrc = _fnDataSource( settings );
4858		if ( dataSrc != 'ssp' || deferLoading ) {
4859			// if there is an ajax source load the data
4860			if ( dataSrc == 'ajax' ) {
4861				_fnBuildAjax( settings, [], function(json) {
4862					var aData = _fnAjaxDataSrc( settings, json );
4863	
4864					// Got the data - add it to the table
4865					for ( i=0 ; i<aData.length ; i++ ) {
4866						_fnAddData( settings, aData[i] );
4867					}
4868	
4869					// Reset the init display for cookie saving. We've already done
4870					// a filter, and therefore cleared it before. So we need to make
4871					// it appear 'fresh'
4872					settings.iInitDisplayStart = iAjaxStart;
4873	
4874					_fnReDraw( settings );
4875	
4876					_fnProcessingDisplay( settings, false );
4877					_fnInitComplete( settings, json );
4878				}, settings );
4879			}
4880			else {
4881				_fnProcessingDisplay( settings, false );
4882				_fnInitComplete( settings );
4883			}
4884		}
4885	}
4886	
4887	
4888	/**
4889	 * Draw the table for the first time, adding all required features
4890	 *  @param {object} oSettings dataTables settings object
4891	 *  @param {object} [json] JSON from the server that completed the table, if using Ajax source
4892	 *    with client-side processing (optional)
4893	 *  @memberof DataTable#oApi
4894	 */
4895	function _fnInitComplete ( settings, json )
4896	{
4897		settings._bInitComplete = true;
4898	
4899		// When data was added after the initialisation (data or Ajax) we need to
4900		// calculate the column sizing
4901		if ( json || settings.oInit.aaData ) {
4902			_fnAdjustColumnSizing( settings );
4903		}
4904	
4905		_fnCallbackFire( settings, null, 'plugin-init', [settings, json] );
4906		_fnCallbackFire( settings, 'aoInitComplete', 'init', [settings, json] );
4907	}
4908	
4909	
4910	function _fnLengthChange ( settings, val )
4911	{
4912		var len = parseInt( val, 10 );
4913		settings._iDisplayLength = len;
4914	
4915		_fnLengthOverflow( settings );
4916	
4917		// Fire length change event
4918		_fnCallbackFire( settings, null, 'length', [settings, len] );
4919	}
4920	
4921	
4922	/**
4923	 * Generate the node required for user display length changing
4924	 *  @param {object} settings dataTables settings object
4925	 *  @returns {node} Display length feature node
4926	 *  @memberof DataTable#oApi
4927	 */
4928	function _fnFeatureHtmlLength ( settings )
4929	{
4930		var
4931			classes  = settings.oClasses,
4932			tableId  = settings.sTableId,
4933			menu     = settings.aLengthMenu,
4934			d2       = Array.isArray( menu[0] ),
4935			lengths  = d2 ? menu[0] : menu,
4936			language = d2 ? menu[1] : menu;
4937	
4938		var select = $('<select/>', {
4939			'name':          tableId+'_length',
4940			'aria-controls': tableId,
4941			'class':         classes.sLengthSelect
4942		} );
4943	
4944		for ( var i=0, ien=lengths.length ; i<ien ; i++ ) {
4945			select[0][ i ] = new Option(
4946				typeof language[i] === 'number' ?
4947					settings.fnFormatNumber( language[i] ) :
4948					language[i],
4949				lengths[i]
4950			);
4951		}
4952	
4953		var div = $('<div><label/></div>').addClass( classes.sLength );
4954		if ( ! settings.aanFeatures.l ) {
4955			div[0].id = tableId+'_length';
4956		}
4957	
4958		div.children().append(
4959			settings.oLanguage.sLengthMenu.replace( '_MENU_', select[0].outerHTML )
4960		);
4961	
4962		// Can't use `select` variable as user might provide their own and the
4963		// reference is broken by the use of outerHTML
4964		$('select', div)
4965			.val( settings._iDisplayLength )
4966			.on( 'change.DT', function(e) {
4967				_fnLengthChange( settings, $(this).val() );
4968				_fnDraw( settings );
4969			} );
4970	
4971		// Update node value whenever anything changes the table's length
4972		$(settings.nTable).on( 'length.dt.DT', function (e, s, len) {
4973			if ( settings === s ) {
4974				$('select', div).val( len );
4975			}
4976		} );
4977	
4978		return div[0];
4979	}
4980	
4981	
4982	
4983	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
4984	 * Note that most of the paging logic is done in
4985	 * DataTable.ext.pager
4986	 */
4987	
4988	/**
4989	 * Generate the node required for default pagination
4990	 *  @param {object} oSettings dataTables settings object
4991	 *  @returns {node} Pagination feature node
4992	 *  @memberof DataTable#oApi
4993	 */
4994	function _fnFeatureHtmlPaginate ( settings )
4995	{
4996		var
4997			type   = settings.sPaginationType,
4998			plugin = DataTable.ext.pager[ type ],
4999			modern = typeof plugin === 'function',
5000			redraw = function( settings ) {
5001				_fnDraw( settings );
5002			},
5003			node = $('<div/>').addClass( settings.oClasses.sPaging + type )[0],
5004			features = settings.aanFeatures;
5005	
5006		if ( ! modern ) {
5007			plugin.fnInit( settings, node, redraw );
5008		}
5009	
5010		/* Add a draw callback for the pagination on first instance, to update the paging display */
5011		if ( ! features.p )
5012		{
5013			node.id = settings.sTableId+'_paginate';
5014	
5015			settings.aoDrawCallback.push( {
5016				"fn": function( settings ) {
5017					if ( modern ) {
5018						var
5019							start      = settings._iDisplayStart,
5020							len        = settings._iDisplayLength,
5021							visRecords = settings.fnRecordsDisplay(),
5022							all        = len === -1,
5023							page = all ? 0 : Math.ceil( start / len ),
5024							pages = all ? 1 : Math.ceil( visRecords / len ),
5025							buttons = plugin(page, pages),
5026							i, ien;
5027	
5028						for ( i=0, ien=features.p.length ; i<ien ; i++ ) {
5029							_fnRenderer( settings, 'pageButton' )(
5030								settings, features.p[i], i, buttons, page, pages
5031							);
5032						}
5033					}
5034					else {
5035						plugin.fnUpdate( settings, redraw );
5036					}
5037				},
5038				"sName": "pagination"
5039			} );
5040		}
5041	
5042		return node;
5043	}
5044	
5045	
5046	/**
5047	 * Alter the display settings to change the page
5048	 *  @param {object} settings DataTables settings object
5049	 *  @param {string|int} action Paging action to take: "first", "previous",
5050	 *    "next" or "last" or page number to jump to (integer)
5051	 *  @param [bool] redraw Automatically draw the update or not
5052	 *  @returns {bool} true page has changed, false - no change
5053	 *  @memberof DataTable#oApi
5054	 */
5055	function _fnPageChange ( settings, action, redraw )
5056	{
5057		var
5058			start     = settings._iDisplayStart,
5059			len       = settings._iDisplayLength,
5060			records   = settings.fnRecordsDisplay();
5061	
5062		if ( records === 0 || len === -1 )
5063		{
5064			start = 0;
5065		}
5066		else if ( typeof action === "number" )
5067		{
5068			start = action * len;
5069	
5070			if ( start > records )
5071			{
5072				start = 0;
5073			}
5074		}
5075		else if ( action == "first" )
5076		{
5077			start = 0;
5078		}
5079		else if ( action == "previous" )
5080		{
5081			start = len >= 0 ?
5082				start - len :
5083				0;
5084	
5085			if ( start < 0 )
5086			{
5087			  start = 0;
5088			}
5089		}
5090		else if ( action == "next" )
5091		{
5092			if ( start + len < records )
5093			{
5094				start += len;
5095			}
5096		}
5097		else if ( action == "last" )
5098		{
5099			start = Math.floor( (records-1) / len) * len;
5100		}
5101		else
5102		{
5103			_fnLog( settings, 0, "Unknown paging action: "+action, 5 );
5104		}
5105	
5106		var changed = settings._iDisplayStart !== start;
5107		settings._iDisplayStart = start;
5108	
5109		if ( changed ) {
5110			_fnCallbackFire( settings, null, 'page', [settings] );
5111	
5112			if ( redraw ) {
5113				_fnDraw( settings );
5114			}
5115		}
5116		else {
5117			// No change event - paging was called, but no change
5118			_fnCallbackFire( settings, null, 'page-nc', [settings] );
5119		}
5120	
5121		return changed;
5122	}
5123	
5124	
5125	
5126	/**
5127	 * Generate the node required for the processing node
5128	 *  @param {object} settings dataTables settings object
5129	 *  @returns {node} Processing element
5130	 *  @memberof DataTable#oApi
5131	 */
5132	function _fnFeatureHtmlProcessing ( settings )
5133	{
5134		return $('<div/>', {
5135				'id': ! settings.aanFeatures.r ? settings.sTableId+'_processing' : null,
5136				'class': settings.oClasses.sProcessing,
5137				'role': 'status'
5138			} )
5139			.html( settings.oLanguage.sProcessing )
5140			.append('<div><div></div><div></div><div></div><div></div></div>')
5141			.insertBefore( settings.nTable )[0];
5142	}
5143	
5144	
5145	/**
5146	 * Display or hide the processing indicator
5147	 *  @param {object} settings dataTables settings object
5148	 *  @param {bool} show Show the processing indicator (true) or not (false)
5149	 *  @memberof DataTable#oApi
5150	 */
5151	function _fnProcessingDisplay ( settings, show )
5152	{
5153		if ( settings.oFeatures.bProcessing ) {
5154			$(settings.aanFeatures.r).css( 'display', show ? 'block' : 'none' );
5155		}
5156	
5157		_fnCallbackFire( settings, null, 'processing', [settings, show] );
5158	}
5159	
5160	/**
5161	 * Add any control elements for the table - specifically scrolling
5162	 *  @param {object} settings dataTables settings object
5163	 *  @returns {node} Node to add to the DOM
5164	 *  @memberof DataTable#oApi
5165	 */
5166	function _fnFeatureHtmlTable ( settings )
5167	{
5168		var table = $(settings.nTable);
5169	
5170		// Scrolling from here on in
5171		var scroll = settings.oScroll;
5172	
5173		if ( scroll.sX === '' && scroll.sY === '' ) {
5174			return settings.nTable;
5175		}
5176	
5177		var scrollX = scroll.sX;
5178		var scrollY = scroll.sY;
5179		var classes = settings.oClasses;
5180		var caption = table.children('caption');
5181		var captionSide = caption.length ? caption[0]._captionSide : null;
5182		var headerClone = $( table[0].cloneNode(false) );
5183		var footerClone = $( table[0].cloneNode(false) );
5184		var footer = table.children('tfoot');
5185		var _div = '<div/>';
5186		var size = function ( s ) {
5187			return !s ? null : _fnStringToCss( s );
5188		};
5189	
5190		if ( ! footer.length ) {
5191			footer = null;
5192		}
5193	
5194		/*
5195		 * The HTML structure that we want to generate in this function is:
5196		 *  div - scroller
5197		 *    div - scroll head
5198		 *      div - scroll head inner
5199		 *        table - scroll head table
5200		 *          thead - thead
5201		 *    div - scroll body
5202		 *      table - table (master table)
5203		 *        thead - thead clone for sizing
5204		 *        tbody - tbody
5205		 *    div - scroll foot
5206		 *      div - scroll foot inner
5207		 *        table - scroll foot table
5208		 *          tfoot - tfoot
5209		 */
5210		var scroller = $( _div, { 'class': classes.sScrollWrapper } )
5211			.append(
5212				$(_div, { 'class': classes.sScrollHead } )
5213					.css( {
5214						overflow: 'hidden',
5215						position: 'relative',
5216						border: 0,
5217						width: scrollX ? size(scrollX) : '100%'
5218					} )
5219					.append(
5220						$(_div, { 'class': classes.sScrollHeadInner } )
5221							.css( {
5222								'box-sizing': 'content-box',
5223								width: scroll.sXInner || '100%'
5224							} )
5225							.append(
5226								headerClone
5227									.removeAttr('id')
5228									.css( 'margin-left', 0 )
5229									.append( captionSide === 'top' ? caption : null )
5230									.append(
5231										table.children('thead')
5232									)
5233							)
5234					)
5235			)
5236			.append(
5237				$(_div, { 'class': classes.sScrollBody } )
5238					.css( {
5239						position: 'relative',
5240						overflow: 'auto',
5241						width: size( scrollX )
5242					} )
5243					.append( table )
5244			);
5245	
5246		if ( footer ) {
5247			scroller.append(
5248				$(_div, { 'class': classes.sScrollFoot } )
5249					.css( {
5250						overflow: 'hidden',
5251						border: 0,
5252						width: scrollX ? size(scrollX) : '100%'
5253					} )
5254					.append(
5255						$(_div, { 'class': classes.sScrollFootInner } )
5256							.append(
5257								footerClone
5258									.removeAttr('id')
5259									.css( 'margin-left', 0 )
5260									.append( captionSide === 'bottom' ? caption : null )
5261									.append(
5262										table.children('tfoot')
5263									)
5264							)
5265					)
5266			);
5267		}
5268	
5269		var children = scroller.children();
5270		var scrollHead = children[0];
5271		var scrollBody = children[1];
5272		var scrollFoot = footer ? children[2] : null;
5273	
5274		// When the body is scrolled, then we also want to scroll the headers
5275		if ( scrollX ) {
5276			$(scrollBody).on( 'scroll.DT', function (e) {
5277				var scrollLeft = this.scrollLeft;
5278	
5279				scrollHead.scrollLeft = scrollLeft;
5280	
5281				if ( footer ) {
5282					scrollFoot.scrollLeft = scrollLeft;
5283				}
5284			} );
5285		}
5286	
5287		$(scrollBody).css('max-height', scrollY);
5288		if (! scroll.bCollapse) {
5289			$(scrollBody).css('height', scrollY);
5290		}
5291	
5292		settings.nScrollHead = scrollHead;
5293		settings.nScrollBody = scrollBody;
5294		settings.nScrollFoot = scrollFoot;
5295	
5296		// On redraw - align columns
5297		settings.aoDrawCallback.push( {
5298			"fn": _fnScrollDraw,
5299			"sName": "scrolling"
5300		} );
5301	
5302		return scroller[0];
5303	}
5304	
5305	
5306	
5307	/**
5308	 * Update the header, footer and body tables for resizing - i.e. column
5309	 * alignment.
5310	 *
5311	 * Welcome to the most horrible function DataTables. The process that this
5312	 * function follows is basically:
5313	 *   1. Re-create the table inside the scrolling div
5314	 *   2. Take live measurements from the DOM
5315	 *   3. Apply the measurements to align the columns
5316	 *   4. Clean up
5317	 *
5318	 *  @param {object} settings dataTables settings object
5319	 *  @memberof DataTable#oApi
5320	 */
5321	function _fnScrollDraw ( settings )
5322	{
5323		// Given that this is such a monster function, a lot of variables are use
5324		// to try and keep the minimised size as small as possible
5325		var
5326			scroll         = settings.oScroll,
5327			scrollX        = scroll.sX,
5328			scrollXInner   = scroll.sXInner,
5329			scrollY        = scroll.sY,
5330			barWidth       = scroll.iBarWidth,
5331			divHeader      = $(settings.nScrollHead),
5332			divHeaderStyle = divHeader[0].style,
5333			divHeaderInner = divHeader.children('div'),
5334			divHeaderInnerStyle = divHeaderInner[0].style,
5335			divHeaderTable = divHeaderInner.children('table'),
5336			divBodyEl      = settings.nScrollBody,
5337			divBody        = $(divBodyEl),
5338			divBodyStyle   = divBodyEl.style,
5339			divFooter      = $(settings.nScrollFoot),
5340			divFooterInner = divFooter.children('div'),
5341			divFooterTable = divFooterInner.children('table'),
5342			header         = $(settings.nTHead),
5343			table          = $(settings.nTable),
5344			tableEl        = table[0],
5345			tableStyle     = tableEl.style,
5346			footer         = settings.nTFoot ? $(settings.nTFoot) : null,
5347			browser        = settings.oBrowser,
5348			ie67           = browser.bScrollOversize,
5349			dtHeaderCells  = _pluck( settings.aoColumns, 'nTh' ),
5350			headerTrgEls, footerTrgEls,
5351			headerSrcEls, footerSrcEls,
5352			headerCopy, footerCopy,
5353			headerWidths=[], footerWidths=[],
5354			headerContent=[], footerContent=[],
5355			idx, correction, sanityWidth,
5356			zeroOut = function(nSizer) {
5357				var style = nSizer.style;
5358				style.paddingTop = "0";
5359				style.paddingBottom = "0";
5360				style.borderTopWidth = "0";
5361				style.borderBottomWidth = "0";
5362				style.height = 0;
5363			};
5364	
5365		// If the scrollbar visibility has changed from the last draw, we need to
5366		// adjust the column sizes as the table width will have changed to account
5367		// for the scrollbar
5368		var scrollBarVis = divBodyEl.scrollHeight > divBodyEl.clientHeight;
5369		
5370		if ( settings.scrollBarVis !== scrollBarVis && settings.scrollBarVis !== undefined ) {
5371			settings.scrollBarVis = scrollBarVis;
5372			_fnAdjustColumnSizing( settings );
5373			return; // adjust column sizing will call this function again
5374		}
5375		else {
5376			settings.scrollBarVis = scrollBarVis;
5377		}
5378	
5379		/*
5380		 * 1. Re-create the table inside the scrolling div
5381		 */
5382	
5383		// Remove the old minimised thead and tfoot elements in the inner table
5384		table.children('thead, tfoot').remove();
5385	
5386		if ( footer ) {
5387			footerCopy = footer.clone().prependTo( table );
5388			footerTrgEls = footer.find('tr'); // the original tfoot is in its own table and must be sized
5389			footerSrcEls = footerCopy.find('tr');
5390			footerCopy.find('[id]').removeAttr('id');
5391		}
5392	
5393		// Clone the current header and footer elements and then place it into the inner table
5394		headerCopy = header.clone().prependTo( table );
5395		headerTrgEls = header.find('tr'); // original header is in its own table
5396		headerSrcEls = headerCopy.find('tr');
5397		headerCopy.find('th, td').removeAttr('tabindex');
5398		headerCopy.find('[id]').removeAttr('id');
5399	
5400	
5401		/*
5402		 * 2. Take live measurements from the DOM - do not alter the DOM itself!
5403		 */
5404	
5405		// Remove old sizing and apply the calculated column widths
5406		// Get the unique column headers in the newly created (cloned) header. We want to apply the
5407		// calculated sizes to this header
5408		if ( ! scrollX )
5409		{
5410			divBodyStyle.width = '100%';
5411			divHeader[0].style.width = '100%';
5412		}
5413	
5414		$.each( _fnGetUniqueThs( settings, headerCopy ), function ( i, el ) {
5415			idx = _fnVisibleToColumnIndex( settings, i );
5416			el.style.width = settings.aoColumns[idx].sWidth;
5417		} );
5418	
5419		if ( footer ) {
5420			_fnApplyToChildren( function(n) {
5421				n.style.width = "";
5422			}, footerSrcEls );
5423		}
5424	
5425		// Size the table as a whole
5426		sanityWidth = table.outerWidth();
5427		if ( scrollX === "" ) {
5428			// No x scrolling
5429			tableStyle.width = "100%";
5430	
5431			// IE7 will make the width of the table when 100% include the scrollbar
5432			// - which is shouldn't. When there is a scrollbar we need to take this
5433			// into account.
5434			if ( ie67 && (table.find('tbody').height() > divBodyEl.offsetHeight ||
5435				divBody.css('overflow-y') == "scroll")
5436			) {
5437				tableStyle.width = _fnStringToCss( table.outerWidth() - barWidth);
5438			}
5439	
5440			// Recalculate the sanity width
5441			sanityWidth = table.outerWidth();
5442		}
5443		else if ( scrollXInner !== "" ) {
5444			// legacy x scroll inner has been given - use it
5445			tableStyle.width = _fnStringToCss(scrollXInner);
5446	
5447			// Recalculate the sanity width
5448			sanityWidth = table.outerWidth();
5449		}
5450	
5451		// Hidden header should have zero height, so remove padding and borders. Then
5452		// set the width based on the real headers
5453	
5454		// Apply all styles in one pass
5455		_fnApplyToChildren( zeroOut, headerSrcEls );
5456	
5457		// Read all widths in next pass
5458		_fnApplyToChildren( function(nSizer) {
5459			var style = window.getComputedStyle ?
5460				window.getComputedStyle(nSizer).width :
5461				_fnStringToCss( $(nSizer).width() );
5462	
5463			headerContent.push( nSizer.innerHTML );
5464			headerWidths.push( style );
5465		}, headerSrcEls );
5466	
5467		// Apply all widths in final pass
5468		_fnApplyToChildren( function(nToSize, i) {
5469			nToSize.style.width = headerWidths[i];
5470		}, headerTrgEls );
5471	
5472		$(headerSrcEls).css('height', 0);
5473	
5474		/* Same again with the footer if we have one */
5475		if ( footer )
5476		{
5477			_fnApplyToChildren( zeroOut, footerSrcEls );
5478	
5479			_fnApplyToChildren( function(nSizer) {
5480				footerContent.push( nSizer.innerHTML );
5481				footerWidths.push( _fnStringToCss( $(nSizer).css('width') ) );
5482			}, footerSrcEls );
5483	
5484			_fnApplyToChildren( function(nToSize, i) {
5485				nToSize.style.width = footerWidths[i];
5486			}, footerTrgEls );
5487	
5488			$(footerSrcEls).height(0);
5489		}
5490	
5491	
5492		/*
5493		 * 3. Apply the measurements
5494		 */
5495	
5496		// "Hide" the header and footer that we used for the sizing. We need to keep
5497		// the content of the cell so that the width applied to the header and body
5498		// both match, but we want to hide it completely. We want to also fix their
5499		// width to what they currently are
5500		_fnApplyToChildren( function(nSizer, i) {
5501			nSizer.innerHTML = '<div class="dataTables_sizing">'+headerContent[i]+'</div>';
5502			nSizer.childNodes[0].style.height = "0";
5503			nSizer.childNodes[0].style.overflow = "hidden";
5504			nSizer.style.width = headerWidths[i];
5505		}, headerSrcEls );
5506	
5507		if ( footer )
5508		{
5509			_fnApplyToChildren( function(nSizer, i) {
5510				nSizer.innerHTML = '<div class="dataTables_sizing">'+footerContent[i]+'</div>';
5511				nSizer.childNodes[0].style.height = "0";
5512				nSizer.childNodes[0].style.overflow = "hidden";
5513				nSizer.style.width = footerWidths[i];
5514			}, footerSrcEls );
5515		}
5516	
5517		// Sanity check that the table is of a sensible width. If not then we are going to get
5518		// misalignment - try to prevent this by not allowing the table to shrink below its min width
5519		if ( Math.round(table.outerWidth()) < Math.round(sanityWidth) )
5520		{
5521			// The min width depends upon if we have a vertical scrollbar visible or not */
5522			correction = ((divBodyEl.scrollHeight > divBodyEl.offsetHeight ||
5523				divBody.css('overflow-y') == "scroll")) ?
5524					sanityWidth+barWidth :
5525					sanityWidth;
5526	
5527			// IE6/7 are a law unto themselves...
5528			if ( ie67 && (divBodyEl.scrollHeight >
5529				divBodyEl.offsetHeight || divBody.css('overflow-y') == "scroll")
5530			) {
5531				tableStyle.width = _fnStringToCss( correction-barWidth );
5532			}
5533	
5534			// And give the user a warning that we've stopped the table getting too small
5535			if ( scrollX === "" || scrollXInner !== "" ) {
5536				_fnLog( settings, 1, 'Possible column misalignment', 6 );
5537			}
5538		}
5539		else
5540		{
5541			correction = '100%';
5542		}
5543	
5544		// Apply to the container elements
5545		divBodyStyle.width = _fnStringToCss( correction );
5546		divHeaderStyle.width = _fnStringToCss( correction );
5547	
5548		if ( footer ) {
5549			settings.nScrollFoot.style.width = _fnStringToCss( correction );
5550		}
5551	
5552	
5553		/*
5554		 * 4. Clean up
5555		 */
5556		if ( ! scrollY ) {
5557			/* IE7< puts a vertical scrollbar in place (when it shouldn't be) due to subtracting
5558			 * the scrollbar height from the visible display, rather than adding it on. We need to
5559			 * set the height in order to sort this. Don't want to do it in any other browsers.
5560			 */
5561			if ( ie67 ) {
5562				divBodyStyle.height = _fnStringToCss( tableEl.offsetHeight+barWidth );
5563			}
5564		}
5565	
5566		/* Finally set the width's of the header and footer tables */
5567		var iOuterWidth = table.outerWidth();
5568		divHeaderTable[0].style.width = _fnStringToCss( iOuterWidth );
5569		divHeaderInnerStyle.width = _fnStringToCss( iOuterWidth );
5570	
5571		// Figure out if there are scrollbar present - if so then we need a the header and footer to
5572		// provide a bit more space to allow "overflow" scrolling (i.e. past the scrollbar)
5573		var bScrolling = table.height() > divBodyEl.clientHeight || divBody.css('overflow-y') == "scroll";
5574		var padding = 'padding' + (browser.bScrollbarLeft ? 'Left' : 'Right' );
5575		divHeaderInnerStyle[ padding ] = bScrolling ? barWidth+"px" : "0px";
5576	
5577		if ( footer ) {
5578			divFooterTable[0].style.width = _fnStringToCss( iOuterWidth );
5579			divFooterInner[0].style.width = _fnStringToCss( iOuterWidth );
5580			divFooterInner[0].style[padding] = bScrolling ? barWidth+"px" : "0px";
5581		}
5582	
5583		// Correct DOM ordering for colgroup - comes before the thead
5584		table.children('colgroup').insertBefore( table.children('thead') );
5585	
5586		/* Adjust the position of the header in case we loose the y-scrollbar */
5587		divBody.trigger('scroll');
5588	
5589		// If sorting or filtering has occurred, jump the scrolling back to the top
5590		// only if we aren't holding the position
5591		if ( (settings.bSorted || settings.bFiltered) && ! settings._drawHold ) {
5592			divBodyEl.scrollTop = 0;
5593		}
5594	}
5595	
5596	
5597	
5598	/**
5599	 * Apply a given function to the display child nodes of an element array (typically
5600	 * TD children of TR rows
5601	 *  @param {function} fn Method to apply to the objects
5602	 *  @param array {nodes} an1 List of elements to look through for display children
5603	 *  @param array {nodes} an2 Another list (identical structure to the first) - optional
5604	 *  @memberof DataTable#oApi
5605	 */
5606	function _fnApplyToChildren( fn, an1, an2 )
5607	{
5608		var index=0, i=0, iLen=an1.length;
5609		var nNode1, nNode2;
5610	
5611		while ( i < iLen ) {
5612			nNode1 = an1[i].firstChild;
5613			nNode2 = an2 ? an2[i].firstChild : null;
5614	
5615			while ( nNode1 ) {
5616				if ( nNode1.nodeType === 1 ) {
5617					if ( an2 ) {
5618						fn( nNode1, nNode2, index );
5619					}
5620					else {
5621						fn( nNode1, index );
5622					}
5623	
5624					index++;
5625				}
5626	
5627				nNode1 = nNode1.nextSibling;
5628				nNode2 = an2 ? nNode2.nextSibling : null;
5629			}
5630	
5631			i++;
5632		}
5633	}
5634	
5635	
5636	
5637	var __re_html_remove = /<.*?>/g;
5638	
5639	
5640	/**
5641	 * Calculate the width of columns for the table
5642	 *  @param {object} oSettings dataTables settings object
5643	 *  @memberof DataTable#oApi
5644	 */
5645	function _fnCalculateColumnWidths ( oSettings )
5646	{
5647		var
5648			table = oSettings.nTable,
5649			columns = oSettings.aoColumns,
5650			scroll = oSettings.oScroll,
5651			scrollY = scroll.sY,
5652			scrollX = scroll.sX,
5653			scrollXInner = scroll.sXInner,
5654			columnCount = columns.length,
5655			visibleColumns = _fnGetColumns( oSettings, 'bVisible' ),
5656			headerCells = $('th', oSettings.nTHead),
5657			tableWidthAttr = table.getAttribute('width'), // from DOM element
5658			tableContainer = table.parentNode,
5659			userInputs = false,
5660			i, column, columnIdx, width, outerWidth,
5661			browser = oSettings.oBrowser,
5662			ie67 = browser.bScrollOversize;
5663	
5664		var styleWidth = table.style.width;
5665		if ( styleWidth && styleWidth.indexOf('%') !== -1 ) {
5666			tableWidthAttr = styleWidth;
5667		}
5668	
5669		/* Convert any user input sizes into pixel sizes */
5670		for ( i=0 ; i<visibleColumns.length ; i++ ) {
5671			column = columns[ visibleColumns[i] ];
5672	
5673			if ( column.sWidth !== null ) {
5674				column.sWidth = _fnConvertToWidth( column.sWidthOrig, tableContainer );
5675	
5676				userInputs = true;
5677			}
5678		}
5679	
5680		/* If the number of columns in the DOM equals the number that we have to
5681		 * process in DataTables, then we can use the offsets that are created by
5682		 * the web- browser. No custom sizes can be set in order for this to happen,
5683		 * nor scrolling used
5684		 */
5685		if ( ie67 || ! userInputs && ! scrollX && ! scrollY &&
5686		     columnCount == _fnVisbleColumns( oSettings ) &&
5687		     columnCount == headerCells.length
5688		) {
5689			for ( i=0 ; i<columnCount ; i++ ) {
5690				var colIdx = _fnVisibleToColumnIndex( oSettings, i );
5691	
5692				if ( colIdx !== null ) {
5693					columns[ colIdx ].sWidth = _fnStringToCss( headerCells.eq(i).width() );
5694				}
5695			}
5696		}
5697		else
5698		{
5699			// Otherwise construct a single row, worst case, table with the widest
5700			// node in the data, assign any user defined widths, then insert it into
5701			// the DOM and allow the browser to do all the hard work of calculating
5702			// table widths
5703			var tmpTable = $(table).clone() // don't use cloneNode - IE8 will remove events on the main table
5704				.css( 'visibility', 'hidden' )
5705				.removeAttr( 'id' );
5706	
5707			// Clean up the table body
5708			tmpTable.find('tbody tr').remove();
5709			var tr = $('<tr/>').appendTo( tmpTable.find('tbody') );
5710	
5711			// Clone the table header and footer - we can't use the header / footer
5712			// from the cloned table, since if scrolling is active, the table's
5713			// real header and footer are contained in different table tags
5714			tmpTable.find('thead, tfoot').remove();
5715			tmpTable
5716				.append( $(oSettings.nTHead).clone() )
5717				.append( $(oSettings.nTFoot).clone() );
5718	
5719			// Remove any assigned widths from the footer (from scrolling)
5720			tmpTable.find('tfoot th, tfoot td').css('width', '');
5721	
5722			// Apply custom sizing to the cloned header
5723			headerCells = _fnGetUniqueThs( oSettings, tmpTable.find('thead')[0] );
5724	
5725			for ( i=0 ; i<visibleColumns.length ; i++ ) {
5726				column = columns[ visibleColumns[i] ];
5727	
5728				headerCells[i].style.width = column.sWidthOrig !== null && column.sWidthOrig !== '' ?
5729					_fnStringToCss( column.sWidthOrig ) :
5730					'';
5731	
5732				// For scrollX we need to force the column width otherwise the
5733				// browser will collapse it. If this width is smaller than the
5734				// width the column requires, then it will have no effect
5735				if ( column.sWidthOrig && scrollX ) {
5736					$( headerCells[i] ).append( $('<div/>').css( {
5737						width: column.sWidthOrig,
5738						margin: 0,
5739						padding: 0,
5740						border: 0,
5741						height: 1
5742					} ) );
5743				}
5744			}
5745	
5746			// Find the widest cell for each column and put it into the table
5747			if ( oSettings.aoData.length ) {
5748				for ( i=0 ; i<visibleColumns.length ; i++ ) {
5749					columnIdx = visibleColumns[i];
5750					column = columns[ columnIdx ];
5751	
5752					$( _fnGetWidestNode( oSettings, columnIdx ) )
5753						.clone( false )
5754						.append( column.sContentPadding )
5755						.appendTo( tr );
5756				}
5757			}
5758	
5759			// Tidy the temporary table - remove name attributes so there aren't
5760			// duplicated in the dom (radio elements for example)
5761			$('[name]', tmpTable).removeAttr('name');
5762	
5763			// Table has been built, attach to the document so we can work with it.
5764			// A holding element is used, positioned at the top of the container
5765			// with minimal height, so it has no effect on if the container scrolls
5766			// or not. Otherwise it might trigger scrolling when it actually isn't
5767			// needed
5768			var holder = $('<div/>').css( scrollX || scrollY ?
5769					{
5770						position: 'absolute',
5771						top: 0,
5772						left: 0,
5773						height: 1,
5774						right: 0,
5775						overflow: 'hidden'
5776					} :
5777					{}
5778				)
5779				.append( tmpTable )
5780				.appendTo( tableContainer );
5781	
5782			// When scrolling (X or Y) we want to set the width of the table as 
5783			// appropriate. However, when not scrolling leave the table width as it
5784			// is. This results in slightly different, but I think correct behaviour
5785			if ( scrollX && scrollXInner ) {
5786				tmpTable.width( scrollXInner );
5787			}
5788			else if ( scrollX ) {
5789				tmpTable.css( 'width', 'auto' );
5790				tmpTable.removeAttr('width');
5791	
5792				// If there is no width attribute or style, then allow the table to
5793				// collapse
5794				if ( tmpTable.width() < tableContainer.clientWidth && tableWidthAttr ) {
5795					tmpTable.width( tableContainer.clientWidth );
5796				}
5797			}
5798			else if ( scrollY ) {
5799				tmpTable.width( tableContainer.clientWidth );
5800			}
5801			else if ( tableWidthAttr ) {
5802				tmpTable.width( tableWidthAttr );
5803			}
5804	
5805			// Get the width of each column in the constructed table - we need to
5806			// know the inner width (so it can be assigned to the other table's
5807			// cells) and the outer width so we can calculate the full width of the
5808			// table. This is safe since DataTables requires a unique cell for each
5809			// column, but if ever a header can span multiple columns, this will
5810			// need to be modified.
5811			var total = 0;
5812			for ( i=0 ; i<visibleColumns.length ; i++ ) {
5813				var cell = $(headerCells[i]);
5814				var border = cell.outerWidth() - cell.width();
5815	
5816				// Use getBounding... where possible (not IE8-) because it can give
5817				// sub-pixel accuracy, which we then want to round up!
5818				var bounding = browser.bBounding ?
5819					Math.ceil( headerCells[i].getBoundingClientRect().width ) :
5820					cell.outerWidth();
5821	
5822				// Total is tracked to remove any sub-pixel errors as the outerWidth
5823				// of the table might not equal the total given here (IE!).
5824				total += bounding;
5825	
5826				// Width for each column to use
5827				columns[ visibleColumns[i] ].sWidth = _fnStringToCss( bounding - border );
5828			}
5829	
5830			table.style.width = _fnStringToCss( total );
5831	
5832			// Finished with the table - ditch it
5833			holder.remove();
5834		}
5835	
5836		// If there is a width attr, we want to attach an event listener which
5837		// allows the table sizing to automatically adjust when the window is
5838		// resized. Use the width attr rather than CSS, since we can't know if the
5839		// CSS is a relative value or absolute - DOM read is always px.
5840		if ( tableWidthAttr ) {
5841			table.style.width = _fnStringToCss( tableWidthAttr );
5842		}
5843	
5844		if ( (tableWidthAttr || scrollX) && ! oSettings._reszEvt ) {
5845			var bindResize = function () {
5846				$(window).on('resize.DT-'+oSettings.sInstance, _fnThrottle( function () {
5847					_fnAdjustColumnSizing( oSettings );
5848				} ) );
5849			};
5850	
5851			// IE6/7 will crash if we bind a resize event handler on page load.
5852			// To be removed in 1.11 which drops IE6/7 support
5853			if ( ie67 ) {
5854				setTimeout( bindResize, 1000 );
5855			}
5856			else {
5857				bindResize();
5858			}
5859	
5860			oSettings._reszEvt = true;
5861		}
5862	}
5863	
5864	
5865	/**
5866	 * Throttle the calls to a function. Arguments and context are maintained for
5867	 * the throttled function
5868	 *  @param {function} fn Function to be called
5869	 *  @param {int} [freq=200] call frequency in mS
5870	 *  @returns {function} wrapped function
5871	 *  @memberof DataTable#oApi
5872	 */
5873	var _fnThrottle = DataTable.util.throttle;
5874	
5875	
5876	/**
5877	 * Convert a CSS unit width to pixels (e.g. 2em)
5878	 *  @param {string} width width to be converted
5879	 *  @param {node} parent parent to get the with for (required for relative widths) - optional
5880	 *  @returns {int} width in pixels
5881	 *  @memberof DataTable#oApi
5882	 */
5883	function _fnConvertToWidth ( width, parent )
5884	{
5885		if ( ! width ) {
5886			return 0;
5887		}
5888	
5889		var n = $('<div/>')
5890			.css( 'width', _fnStringToCss( width ) )
5891			.appendTo( parent || document.body );
5892	
5893		var val = n[0].offsetWidth;
5894		n.remove();
5895	
5896		return val;
5897	}
5898	
5899	
5900	/**
5901	 * Get the widest node
5902	 *  @param {object} settings dataTables settings object
5903	 *  @param {int} colIdx column of interest
5904	 *  @returns {node} widest table node
5905	 *  @memberof DataTable#oApi
5906	 */
5907	function _fnGetWidestNode( settings, colIdx )
5908	{
5909		var idx = _fnGetMaxLenString( settings, colIdx );
5910		if ( idx < 0 ) {
5911			return null;
5912		}
5913	
5914		var data = settings.aoData[ idx ];
5915		return ! data.nTr ? // Might not have been created when deferred rendering
5916			$('<td/>').html( _fnGetCellData( settings, idx, colIdx, 'display' ) )[0] :
5917			data.anCells[ colIdx ];
5918	}
5919	
5920	
5921	/**
5922	 * Get the maximum strlen for each data column
5923	 *  @param {object} settings dataTables settings object
5924	 *  @param {int} colIdx column of interest
5925	 *  @returns {string} max string length for each column
5926	 *  @memberof DataTable#oApi
5927	 */
5928	function _fnGetMaxLenString( settings, colIdx )
5929	{
5930		var s, max=-1, maxIdx = -1;
5931	
5932		for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
5933			s = _fnGetCellData( settings, i, colIdx, 'display' )+'';
5934			s = s.replace( __re_html_remove, '' );
5935			s = s.replace( /&nbsp;/g, ' ' );
5936	
5937			if ( s.length > max ) {
5938				max = s.length;
5939				maxIdx = i;
5940			}
5941		}
5942	
5943		return maxIdx;
5944	}
5945	
5946	
5947	/**
5948	 * Append a CSS unit (only if required) to a string
5949	 *  @param {string} value to css-ify
5950	 *  @returns {string} value with css unit
5951	 *  @memberof DataTable#oApi
5952	 */
5953	function _fnStringToCss( s )
5954	{
5955		if ( s === null ) {
5956			return '0px';
5957		}
5958	
5959		if ( typeof s == 'number' ) {
5960			return s < 0 ?
5961				'0px' :
5962				s+'px';
5963		}
5964	
5965		// Check it has a unit character already
5966		return s.match(/\d$/) ?
5967			s+'px' :
5968			s;
5969	}
5970	
5971	
5972	
5973	function _fnSortFlatten ( settings )
5974	{
5975		var
5976			i, iLen, k, kLen,
5977			aSort = [],
5978			aiOrig = [],
5979			aoColumns = settings.aoColumns,
5980			aDataSort, iCol, sType, srcCol,
5981			fixed = settings.aaSortingFixed,
5982			fixedObj = $.isPlainObject( fixed ),
5983			nestedSort = [],
5984			add = function ( a ) {
5985				if ( a.length && ! Array.isArray( a[0] ) ) {
5986					// 1D array
5987					nestedSort.push( a );
5988				}
5989				else {
5990					// 2D array
5991					$.merge( nestedSort, a );
5992				}
5993			};
5994	
5995		// Build the sort array, with pre-fix and post-fix options if they have been
5996		// specified
5997		if ( Array.isArray( fixed ) ) {
5998			add( fixed );
5999		}
6000	
6001		if ( fixedObj && fixed.pre ) {
6002			add( fixed.pre );
6003		}
6004	
6005		add( settings.aaSorting );
6006	
6007		if (fixedObj && fixed.post ) {
6008			add( fixed.post );
6009		}
6010	
6011		for ( i=0 ; i<nestedSort.length ; i++ )
6012		{
6013			srcCol = nestedSort[i][0];
6014			aDataSort = aoColumns[ srcCol ].aDataSort;
6015	
6016			for ( k=0, kLen=aDataSort.length ; k<kLen ; k++ )
6017			{
6018				iCol = aDataSort[k];
6019				sType = aoColumns[ iCol ].sType || 'string';
6020	
6021				if ( nestedSort[i]._idx === undefined ) {
6022					nestedSort[i]._idx = $.inArray( nestedSort[i][1], aoColumns[iCol].asSorting );
6023				}
6024	
6025				aSort.push( {
6026					src:       srcCol,
6027					col:       iCol,
6028					dir:       nestedSort[i][1],
6029					index:     nestedSort[i]._idx,
6030					type:      sType,
6031					formatter: DataTable.ext.type.order[ sType+"-pre" ]
6032				} );
6033			}
6034		}
6035	
6036		return aSort;
6037	}
6038	
6039	/**
6040	 * Change the order of the table
6041	 *  @param {object} oSettings dataTables settings object
6042	 *  @memberof DataTable#oApi
6043	 *  @todo This really needs split up!
6044	 */
6045	function _fnSort ( oSettings )
6046	{
6047		var
6048			i, ien, iLen, j, jLen, k, kLen,
6049			sDataType, nTh,
6050			aiOrig = [],
6051			oExtSort = DataTable.ext.type.order,
6052			aoData = oSettings.aoData,
6053			aoColumns = oSettings.aoColumns,
6054			aDataSort, data, iCol, sType, oSort,
6055			formatters = 0,
6056			sortCol,
6057			displayMaster = oSettings.aiDisplayMaster,
6058			aSort;
6059	
6060		// Resolve any column types that are unknown due to addition or invalidation
6061		// @todo Can this be moved into a 'data-ready' handler which is called when
6062		//   data is going to be used in the table?
6063		_fnColumnTypes( oSettings );
6064	
6065		aSort = _fnSortFlatten( oSettings );
6066	
6067		for ( i=0, ien=aSort.length ; i<ien ; i++ ) {
6068			sortCol = aSort[i];
6069	
6070			// Track if we can use the fast sort algorithm
6071			if ( sortCol.formatter ) {
6072				formatters++;
6073			}
6074	
6075			// Load the data needed for the sort, for each cell
6076			_fnSortData( oSettings, sortCol.col );
6077		}
6078	
6079		/* No sorting required if server-side or no sorting array */
6080		if ( _fnDataSource( oSettings ) != 'ssp' && aSort.length !== 0 )
6081		{
6082			// Create a value - key array of the current row positions such that we can use their
6083			// current position during the sort, if values match, in order to perform stable sorting
6084			for ( i=0, iLen=displayMaster.length ; i<iLen ; i++ ) {
6085				aiOrig[ displayMaster[i] ] = i;
6086			}
6087	
6088			/* Do the sort - here we want multi-column sorting based on a given data source (column)
6089			 * and sorting function (from oSort) in a certain direction. It's reasonably complex to
6090			 * follow on it's own, but this is what we want (example two column sorting):
6091			 *  fnLocalSorting = function(a,b){
6092			 *    var iTest;
6093			 *    iTest = oSort['string-asc']('data11', 'data12');
6094			 *      if (iTest !== 0)
6095			 *        return iTest;
6096			 *    iTest = oSort['numeric-desc']('data21', 'data22');
6097			 *    if (iTest !== 0)
6098			 *      return iTest;
6099			 *    return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
6100			 *  }
6101			 * Basically we have a test for each sorting column, if the data in that column is equal,
6102			 * test the next column. If all columns match, then we use a numeric sort on the row
6103			 * positions in the original data array to provide a stable sort.
6104			 *
6105			 * Note - I know it seems excessive to have two sorting methods, but the first is around
6106			 * 15% faster, so the second is only maintained for backwards compatibility with sorting
6107			 * methods which do not have a pre-sort formatting function.
6108			 */
6109			if ( formatters === aSort.length ) {
6110				// All sort types have formatting functions
6111				displayMaster.sort( function ( a, b ) {
6112					var
6113						x, y, k, test, sort,
6114						len=aSort.length,
6115						dataA = aoData[a]._aSortData,
6116						dataB = aoData[b]._aSortData;
6117	
6118					for ( k=0 ; k<len ; k++ ) {
6119						sort = aSort[k];
6120	
6121						x = dataA[ sort.col ];
6122						y = dataB[ sort.col ];
6123	
6124						test = x<y ? -1 : x>y ? 1 : 0;
6125						if ( test !== 0 ) {
6126							return sort.dir === 'asc' ? test : -test;
6127						}
6128					}
6129	
6130					x = aiOrig[a];
6131					y = aiOrig[b];
6132					return x<y ? -1 : x>y ? 1 : 0;
6133				} );
6134			}
6135			else {
6136				// Depreciated - remove in 1.11 (providing a plug-in option)
6137				// Not all sort types have formatting methods, so we have to call their sorting
6138				// methods.
6139				displayMaster.sort( function ( a, b ) {
6140					var
6141						x, y, k, l, test, sort, fn,
6142						len=aSort.length,
6143						dataA = aoData[a]._aSortData,
6144						dataB = aoData[b]._aSortData;
6145	
6146					for ( k=0 ; k<len ; k++ ) {
6147						sort = aSort[k];
6148	
6149						x = dataA[ sort.col ];
6150						y = dataB[ sort.col ];
6151	
6152						fn = oExtSort[ sort.type+"-"+sort.dir ] || oExtSort[ "string-"+sort.dir ];
6153						test = fn( x, y );
6154						if ( test !== 0 ) {
6155							return test;
6156						}
6157					}
6158	
6159					x = aiOrig[a];
6160					y = aiOrig[b];
6161					return x<y ? -1 : x>y ? 1 : 0;
6162				} );
6163			}
6164		}
6165	
6166		/* Tell the draw function that we have sorted the data */
6167		oSettings.bSorted = true;
6168	}
6169	
6170	
6171	function _fnSortAria ( settings )
6172	{
6173		var label;
6174		var nextSort;
6175		var columns = settings.aoColumns;
6176		var aSort = _fnSortFlatten( settings );
6177		var oAria = settings.oLanguage.oAria;
6178	
6179		// ARIA attributes - need to loop all columns, to update all (removing old
6180		// attributes as needed)
6181		for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
6182		{
6183			var col = columns[i];
6184			var asSorting = col.asSorting;
6185			var sTitle = col.ariaTitle || col.sTitle.replace( /<.*?>/g, "" );
6186			var th = col.nTh;
6187	
6188			// IE7 is throwing an error when setting these properties with jQuery's
6189			// attr() and removeAttr() methods...
6190			th.removeAttribute('aria-sort');
6191	
6192			/* In ARIA only the first sorting column can be marked as sorting - no multi-sort option */
6193			if ( col.bSortable ) {
6194				if ( aSort.length > 0 && aSort[0].col == i ) {
6195					th.setAttribute('aria-sort', aSort[0].dir=="asc" ? "ascending" : "descending" );
6196					nextSort = asSorting[ aSort[0].index+1 ] || asSorting[0];
6197				}
6198				else {
6199					nextSort = asSorting[0];
6200				}
6201	
6202				label = sTitle + ( nextSort === "asc" ?
6203					oAria.sSortAscending :
6204					oAria.sSortDescending
6205				);
6206			}
6207			else {
6208				label = sTitle;
6209			}
6210	
6211			th.setAttribute('aria-label', label);
6212		}
6213	}
6214	
6215	
6216	/**
6217	 * Function to run on user sort request
6218	 *  @param {object} settings dataTables settings object
6219	 *  @param {node} attachTo node to attach the handler to
6220	 *  @param {int} colIdx column sorting index
6221	 *  @param {boolean} [append=false] Append the requested sort to the existing
6222	 *    sort if true (i.e. multi-column sort)
6223	 *  @param {function} [callback] callback function
6224	 *  @memberof DataTable#oApi
6225	 */
6226	function _fnSortListener ( settings, colIdx, append, callback )
6227	{
6228		var col = settings.aoColumns[ colIdx ];
6229		var sorting = settings.aaSorting;
6230		var asSorting = col.asSorting;
6231		var nextSortIdx;
6232		var next = function ( a, overflow ) {
6233			var idx = a._idx;
6234			if ( idx === undefined ) {
6235				idx = $.inArray( a[1], asSorting );
6236			}
6237	
6238			return idx+1 < asSorting.length ?
6239				idx+1 :
6240				overflow ?
6241					null :
6242					0;
6243		};
6244	
6245		// Convert to 2D array if needed
6246		if ( typeof sorting[0] === 'number' ) {
6247			sorting = settings.aaSorting = [ sorting ];
6248		}
6249	
6250		// If appending the sort then we are multi-column sorting
6251		if ( append && settings.oFeatures.bSortMulti ) {
6252			// Are we already doing some kind of sort on this column?
6253			var sortIdx = $.inArray( colIdx, _pluck(sorting, '0') );
6254	
6255			if ( sortIdx !== -1 ) {
6256				// Yes, modify the sort
6257				nextSortIdx = next( sorting[sortIdx], true );
6258	
6259				if ( nextSortIdx === null && sorting.length === 1 ) {
6260					nextSortIdx = 0; // can't remove sorting completely
6261				}
6262	
6263				if ( nextSortIdx === null ) {
6264					sorting.splice( sortIdx, 1 );
6265				}
6266				else {
6267					sorting[sortIdx][1] = asSorting[ nextSortIdx ];
6268					sorting[sortIdx]._idx = nextSortIdx;
6269				}
6270			}
6271			else {
6272				// No sort on this column yet
6273				sorting.push( [ colIdx, asSorting[0], 0 ] );
6274				sorting[sorting.length-1]._idx = 0;
6275			}
6276		}
6277		else if ( sorting.length && sorting[0][0] == colIdx ) {
6278			// Single column - already sorting on this column, modify the sort
6279			nextSortIdx = next( sorting[0] );
6280	
6281			sorting.length = 1;
6282			sorting[0][1] = asSorting[ nextSortIdx ];
6283			sorting[0]._idx = nextSortIdx;
6284		}
6285		else {
6286			// Single column - sort only on this column
6287			sorting.length = 0;
6288			sorting.push( [ colIdx, asSorting[0] ] );
6289			sorting[0]._idx = 0;
6290		}
6291	
6292		// Run the sort by calling a full redraw
6293		_fnReDraw( settings );
6294	
6295		// callback used for async user interaction
6296		if ( typeof callback == 'function' ) {
6297			callback( settings );
6298		}
6299	}
6300	
6301	
6302	/**
6303	 * Attach a sort handler (click) to a node
6304	 *  @param {object} settings dataTables settings object
6305	 *  @param {node} attachTo node to attach the handler to
6306	 *  @param {int} colIdx column sorting index
6307	 *  @param {function} [callback] callback function
6308	 *  @memberof DataTable#oApi
6309	 */
6310	function _fnSortAttachListener ( settings, attachTo, colIdx, callback )
6311	{
6312		var col = settings.aoColumns[ colIdx ];
6313	
6314		_fnBindAction( attachTo, {}, function (e) {
6315			/* If the column is not sortable - don't to anything */
6316			if ( col.bSortable === false ) {
6317				return;
6318			}
6319	
6320			// If processing is enabled use a timeout to allow the processing
6321			// display to be shown - otherwise to it synchronously
6322			if ( settings.oFeatures.bProcessing ) {
6323				_fnProcessingDisplay( settings, true );
6324	
6325				setTimeout( function() {
6326					_fnSortListener( settings, colIdx, e.shiftKey, callback );
6327	
6328					// In server-side processing, the draw callback will remove the
6329					// processing display
6330					if ( _fnDataSource( settings ) !== 'ssp' ) {
6331						_fnProcessingDisplay( settings, false );
6332					}
6333				}, 0 );
6334			}
6335			else {
6336				_fnSortListener( settings, colIdx, e.shiftKey, callback );
6337			}
6338		} );
6339	}
6340	
6341	
6342	/**
6343	 * Set the sorting classes on table's body, Note: it is safe to call this function
6344	 * when bSort and bSortClasses are false
6345	 *  @param {object} oSettings dataTables settings object
6346	 *  @memberof DataTable#oApi
6347	 */
6348	function _fnSortingClasses( settings )
6349	{
6350		var oldSort = settings.aLastSort;
6351		var sortClass = settings.oClasses.sSortColumn;
6352		var sort = _fnSortFlatten( settings );
6353		var features = settings.oFeatures;
6354		var i, ien, colIdx;
6355	
6356		if ( features.bSort && features.bSortClasses ) {
6357			// Remove old sorting classes
6358			for ( i=0, ien=oldSort.length ; i<ien ; i++ ) {
6359				colIdx = oldSort[i].src;
6360	
6361				// Remove column sorting
6362				$( _pluck( settings.aoData, 'anCells', colIdx ) )
6363					.removeClass( sortClass + (i<2 ? i+1 : 3) );
6364			}
6365	
6366			// Add new column sorting
6367			for ( i=0, ien=sort.length ; i<ien ; i++ ) {
6368				colIdx = sort[i].src;
6369	
6370				$( _pluck( settings.aoData, 'anCells', colIdx ) )
6371					.addClass( sortClass + (i<2 ? i+1 : 3) );
6372			}
6373		}
6374	
6375		settings.aLastSort = sort;
6376	}
6377	
6378	
6379	// Get the data to sort a column, be it from cache, fresh (populating the
6380	// cache), or from a sort formatter
6381	function _fnSortData( settings, idx )
6382	{
6383		// Custom sorting function - provided by the sort data type
6384		var column = settings.aoColumns[ idx ];
6385		var customSort = DataTable.ext.order[ column.sSortDataType ];
6386		var customData;
6387	
6388		if ( customSort ) {
6389			customData = customSort.call( settings.oInstance, settings, idx,
6390				_fnColumnIndexToVisible( settings, idx )
6391			);
6392		}
6393	
6394		// Use / populate cache
6395		var row, cellData;
6396		var formatter = DataTable.ext.type.order[ column.sType+"-pre" ];
6397	
6398		for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
6399			row = settings.aoData[i];
6400	
6401			if ( ! row._aSortData ) {
6402				row._aSortData = [];
6403			}
6404	
6405			if ( ! row._aSortData[idx] || customSort ) {
6406				cellData = customSort ?
6407					customData[i] : // If there was a custom sort function, use data from there
6408					_fnGetCellData( settings, i, idx, 'sort' );
6409	
6410				row._aSortData[ idx ] = formatter ?
6411					formatter( cellData ) :
6412					cellData;
6413			}
6414		}
6415	}
6416	
6417	
6418	
6419	/**
6420	 * Save the state of a table
6421	 *  @param {object} oSettings dataTables settings object
6422	 *  @memberof DataTable#oApi
6423	 */
6424	function _fnSaveState ( settings )
6425	{
6426		if (settings._bLoadingState) {
6427			return;
6428		}
6429	
6430		/* Store the interesting variables */
6431		var state = {
6432			time:    +new Date(),
6433			start:   settings._iDisplayStart,
6434			length:  settings._iDisplayLength,
6435			order:   $.extend( true, [], settings.aaSorting ),
6436			search:  _fnSearchToCamel( settings.oPreviousSearch ),
6437			columns: $.map( settings.aoColumns, function ( col, i ) {
6438				return {
6439					visible: col.bVisible,
6440					search: _fnSearchToCamel( settings.aoPreSearchCols[i] )
6441				};
6442			} )
6443		};
6444	
6445		settings.oSavedState = state;
6446		_fnCallbackFire( settings, "aoStateSaveParams", 'stateSaveParams', [settings, state] );
6447		
6448		if ( settings.oFeatures.bStateSave && !settings.bDestroying )
6449		{
6450			settings.fnStateSaveCallback.call( settings.oInstance, settings, state );
6451		}	
6452	}
6453	
6454	
6455	/**
6456	 * Attempt to load a saved table state
6457	 *  @param {object} oSettings dataTables settings object
6458	 *  @param {object} oInit DataTables init object so we can override settings
6459	 *  @param {function} callback Callback to execute when the state has been loaded
6460	 *  @memberof DataTable#oApi
6461	 */
6462	function _fnLoadState ( settings, oInit, callback )
6463	{
6464		if ( ! settings.oFeatures.bStateSave ) {
6465			callback();
6466			return;
6467		}
6468	
6469		var loaded = function(state) {
6470			_fnImplementState(settings, state, callback);
6471		}
6472	
6473		var state = settings.fnStateLoadCallback.call( settings.oInstance, settings, loaded );
6474	
6475		if ( state !== undefined ) {
6476			_fnImplementState( settings, state, callback );
6477		}
6478		// otherwise, wait for the loaded callback to be executed
6479	
6480		return true;
6481	}
6482	
6483	function _fnImplementState ( settings, s, callback) {
6484		var i, ien;
6485		var columns = settings.aoColumns;
6486		settings._bLoadingState = true;
6487	
6488		// When StateRestore was introduced the state could now be implemented at any time
6489		// Not just initialisation. To do this an api instance is required in some places
6490		var api = settings._bInitComplete ? new DataTable.Api(settings) : null;
6491	
6492		if ( ! s || ! s.time ) {
6493			settings._bLoadingState = false;
6494			callback();
6495			return;
6496		}
6497	
6498		// Allow custom and plug-in manipulation functions to alter the saved data set and
6499		// cancelling of loading by returning false
6500		var abStateLoad = _fnCallbackFire( settings, 'aoStateLoadParams', 'stateLoadParams', [settings, s] );
6501		if ( $.inArray( false, abStateLoad ) !== -1 ) {
6502			settings._bLoadingState = false;
6503			callback();
6504			return;
6505		}
6506	
6507		// Reject old data
6508		var duration = settings.iStateDuration;
6509		if ( duration > 0 && s.time < +new Date() - (duration*1000) ) {
6510			settings._bLoadingState = false;
6511			callback();
6512			return;
6513		}
6514	
6515		// Number of columns have changed - all bets are off, no restore of settings
6516		if ( s.columns && columns.length !== s.columns.length ) {
6517			settings._bLoadingState = false;
6518			callback();
6519			return;
6520		}
6521	
6522		// Store the saved state so it might be accessed at any time
6523		settings.oLoadedState = $.extend( true, {}, s );
6524	
6525		// Page Length
6526		if ( s.length !== undefined ) {
6527			// If already initialised just set the value directly so that the select element is also updated
6528			if (api) {
6529				api.page.len(s.length)
6530			}
6531			else {
6532				settings._iDisplayLength   = s.length;
6533			}
6534		}
6535	
6536		// Restore key features - todo - for 1.11 this needs to be done by
6537		// subscribed events
6538		if ( s.start !== undefined ) {
6539			if(api === null) {
6540				settings._iDisplayStart    = s.start;
6541				settings.iInitDisplayStart = s.start;
6542			}
6543			else {
6544				_fnPageChange(settings, s.start/settings._iDisplayLength);
6545			}
6546		}
6547	
6548		// Order
6549		if ( s.order !== undefined ) {
6550			settings.aaSorting = [];
6551			$.each( s.order, function ( i, col ) {
6552				settings.aaSorting.push( col[0] >= columns.length ?
6553					[ 0, col[1] ] :
6554					col
6555				);
6556			} );
6557		}
6558	
6559		// Search
6560		if ( s.search !== undefined ) {
6561			$.extend( settings.oPreviousSearch, _fnSearchToHung( s.search ) );
6562		}
6563	
6564		// Columns
6565		if ( s.columns ) {
6566			for ( i=0, ien=s.columns.length ; i<ien ; i++ ) {
6567				var col = s.columns[i];
6568	
6569				// Visibility
6570				if ( col.visible !== undefined ) {
6571					// If the api is defined, the table has been initialised so we need to use it rather than internal settings
6572					if (api) {
6573						// Don't redraw the columns on every iteration of this loop, we will do this at the end instead
6574						api.column(i).visible(col.visible, false);
6575					}
6576					else {
6577						columns[i].bVisible = col.visible;
6578					}
6579				}
6580	
6581				// Search
6582				if ( col.search !== undefined ) {
6583					$.extend( settings.aoPreSearchCols[i], _fnSearchToHung( col.search ) );
6584				}
6585			}
6586			
6587			// If the api is defined then we need to adjust the columns once the visibility has been changed
6588			if (api) {
6589				api.columns.adjust();
6590			}
6591		}
6592	
6593		settings._bLoadingState = false;
6594		_fnCallbackFire( settings, 'aoStateLoaded', 'stateLoaded', [settings, s] );
6595		callback();
6596	};
6597	
6598	
6599	/**
6600	 * Return the settings object for a particular table
6601	 *  @param {node} table table we are using as a dataTable
6602	 *  @returns {object} Settings object - or null if not found
6603	 *  @memberof DataTable#oApi
6604	 */
6605	function _fnSettingsFromNode ( table )
6606	{
6607		var settings = DataTable.settings;
6608		var idx = $.inArray( table, _pluck( settings, 'nTable' ) );
6609	
6610		return idx !== -1 ?
6611			settings[ idx ] :
6612			null;
6613	}
6614	
6615	
6616	/**
6617	 * Log an error message
6618	 *  @param {object} settings dataTables settings object
6619	 *  @param {int} level log error messages, or display them to the user
6620	 *  @param {string} msg error message
6621	 *  @param {int} tn Technical note id to get more information about the error.
6622	 *  @memberof DataTable#oApi
6623	 */
6624	function _fnLog( settings, level, msg, tn )
6625	{
6626		msg = 'DataTables warning: '+
6627			(settings ? 'table id='+settings.sTableId+' - ' : '')+msg;
6628	
6629		if ( tn ) {
6630			msg += '. For more information about this error, please see '+
6631			'http://datatables.net/tn/'+tn;
6632		}
6633	
6634		if ( ! level  ) {
6635			// Backwards compatibility pre 1.10
6636			var ext = DataTable.ext;
6637			var type = ext.sErrMode || ext.errMode;
6638	
6639			if ( settings ) {
6640				_fnCallbackFire( settings, null, 'error', [ settings, tn, msg ] );
6641			}
6642	
6643			if ( type == 'alert' ) {
6644				alert( msg );
6645			}
6646			else if ( type == 'throw' ) {
6647				throw new Error(msg);
6648			}
6649			else if ( typeof type == 'function' ) {
6650				type( settings, tn, msg );
6651			}
6652		}
6653		else if ( window.console && console.log ) {
6654			console.log( msg );
6655		}
6656	}
6657	
6658	
6659	/**
6660	 * See if a property is defined on one object, if so assign it to the other object
6661	 *  @param {object} ret target object
6662	 *  @param {object} src source object
6663	 *  @param {string} name property
6664	 *  @param {string} [mappedName] name to map too - optional, name used if not given
6665	 *  @memberof DataTable#oApi
6666	 */
6667	function _fnMap( ret, src, name, mappedName )
6668	{
6669		if ( Array.isArray( name ) ) {
6670			$.each( name, function (i, val) {
6671				if ( Array.isArray( val ) ) {
6672					_fnMap( ret, src, val[0], val[1] );
6673				}
6674				else {
6675					_fnMap( ret, src, val );
6676				}
6677			} );
6678	
6679			return;
6680		}
6681	
6682		if ( mappedName === undefined ) {
6683			mappedName = name;
6684		}
6685	
6686		if ( src[name] !== undefined ) {
6687			ret[mappedName] = src[name];
6688		}
6689	}
6690	
6691	
6692	/**
6693	 * Extend objects - very similar to jQuery.extend, but deep copy objects, and
6694	 * shallow copy arrays. The reason we need to do this, is that we don't want to
6695	 * deep copy array init values (such as aaSorting) since the dev wouldn't be
6696	 * able to override them, but we do want to deep copy arrays.
6697	 *  @param {object} out Object to extend
6698	 *  @param {object} extender Object from which the properties will be applied to
6699	 *      out
6700	 *  @param {boolean} breakRefs If true, then arrays will be sliced to take an
6701	 *      independent copy with the exception of the `data` or `aaData` parameters
6702	 *      if they are present. This is so you can pass in a collection to
6703	 *      DataTables and have that used as your data source without breaking the
6704	 *      references
6705	 *  @returns {object} out Reference, just for convenience - out === the return.
6706	 *  @memberof DataTable#oApi
6707	 *  @todo This doesn't take account of arrays inside the deep copied objects.
6708	 */
6709	function _fnExtend( out, extender, breakRefs )
6710	{
6711		var val;
6712	
6713		for ( var prop in extender ) {
6714			if ( extender.hasOwnProperty(prop) ) {
6715				val = extender[prop];
6716	
6717				if ( $.isPlainObject( val ) ) {
6718					if ( ! $.isPlainObject( out[prop] ) ) {
6719						out[prop] = {};
6720					}
6721					$.extend( true, out[prop], val );
6722				}
6723				else if ( breakRefs && prop !== 'data' && prop !== 'aaData' && Array.isArray(val) ) {
6724					out[prop] = val.slice();
6725				}
6726				else {
6727					out[prop] = val;
6728				}
6729			}
6730		}
6731	
6732		return out;
6733	}
6734	
6735	
6736	/**
6737	 * Bind an event handers to allow a click or return key to activate the callback.
6738	 * This is good for accessibility since a return on the keyboard will have the
6739	 * same effect as a click, if the element has focus.
6740	 *  @param {element} n Element to bind the action to
6741	 *  @param {object} oData Data object to pass to the triggered function
6742	 *  @param {function} fn Callback function for when the event is triggered
6743	 *  @memberof DataTable#oApi
6744	 */
6745	function _fnBindAction( n, oData, fn )
6746	{
6747		$(n)
6748			.on( 'click.DT', oData, function (e) {
6749					$(n).trigger('blur'); // Remove focus outline for mouse users
6750					fn(e);
6751				} )
6752			.on( 'keypress.DT', oData, function (e){
6753					if ( e.which === 13 ) {
6754						e.preventDefault();
6755						fn(e);
6756					}
6757				} )
6758			.on( 'selectstart.DT', function () {
6759					/* Take the brutal approach to cancelling text selection */
6760					return false;
6761				} );
6762	}
6763	
6764	
6765	/**
6766	 * Register a callback function. Easily allows a callback function to be added to
6767	 * an array store of callback functions that can then all be called together.
6768	 *  @param {object} oSettings dataTables settings object
6769	 *  @param {string} sStore Name of the array storage for the callbacks in oSettings
6770	 *  @param {function} fn Function to be called back
6771	 *  @param {string} sName Identifying name for the callback (i.e. a label)
6772	 *  @memberof DataTable#oApi
6773	 */
6774	function _fnCallbackReg( oSettings, sStore, fn, sName )
6775	{
6776		if ( fn )
6777		{
6778			oSettings[sStore].push( {
6779				"fn": fn,
6780				"sName": sName
6781			} );
6782		}
6783	}
6784	
6785	
6786	/**
6787	 * Fire callback functions and trigger events. Note that the loop over the
6788	 * callback array store is done backwards! Further note that you do not want to
6789	 * fire off triggers in time sensitive applications (for example cell creation)
6790	 * as its slow.
6791	 *  @param {object} settings dataTables settings object
6792	 *  @param {string} callbackArr Name of the array storage for the callbacks in
6793	 *      oSettings
6794	 *  @param {string} eventName Name of the jQuery custom event to trigger. If
6795	 *      null no trigger is fired
6796	 *  @param {array} args Array of arguments to pass to the callback function /
6797	 *      trigger
6798	 *  @memberof DataTable#oApi
6799	 */
6800	function _fnCallbackFire( settings, callbackArr, eventName, args )
6801	{
6802		var ret = [];
6803	
6804		if ( callbackArr ) {
6805			ret = $.map( settings[callbackArr].slice().reverse(), function (val, i) {
6806				return val.fn.apply( settings.oInstance, args );
6807			} );
6808		}
6809	
6810		if ( eventName !== null ) {
6811			var e = $.Event( eventName+'.dt' );
6812			var table = $(settings.nTable);
6813	
6814			table.trigger( e, args );
6815	
6816			// If not yet attached to the document, trigger the event
6817			// on the body directly to sort of simulate the bubble
6818			if (table.parents('body').length === 0) {
6819				$('body').trigger( e, args );
6820			}
6821	
6822			ret.push( e.result );
6823		}
6824	
6825		return ret;
6826	}
6827	
6828	
6829	function _fnLengthOverflow ( settings )
6830	{
6831		var
6832			start = settings._iDisplayStart,
6833			end = settings.fnDisplayEnd(),
6834			len = settings._iDisplayLength;
6835	
6836		/* If we have space to show extra rows (backing up from the end point - then do so */
6837		if ( start >= end )
6838		{
6839			start = end - len;
6840		}
6841	
6842		// Keep the start record on the current page
6843		start -= (start % len);
6844	
6845		if ( len === -1 || start < 0 )
6846		{
6847			start = 0;
6848		}
6849	
6850		settings._iDisplayStart = start;
6851	}
6852	
6853	
6854	function _fnRenderer( settings, type )
6855	{
6856		var renderer = settings.renderer;
6857		var host = DataTable.ext.renderer[type];
6858	
6859		if ( $.isPlainObject( renderer ) && renderer[type] ) {
6860			// Specific renderer for this type. If available use it, otherwise use
6861			// the default.
6862			return host[renderer[type]] || host._;
6863		}
6864		else if ( typeof renderer === 'string' ) {
6865			// Common renderer - if there is one available for this type use it,
6866			// otherwise use the default
6867			return host[renderer] || host._;
6868		}
6869	
6870		// Use the default
6871		return host._;
6872	}
6873	
6874	
6875	/**
6876	 * Detect the data source being used for the table. Used to simplify the code
6877	 * a little (ajax) and to make it compress a little smaller.
6878	 *
6879	 *  @param {object} settings dataTables settings object
6880	 *  @returns {string} Data source
6881	 *  @memberof DataTable#oApi
6882	 */
6883	function _fnDataSource ( settings )
6884	{
6885		if ( settings.oFeatures.bServerSide ) {
6886			return 'ssp';
6887		}
6888		else if ( settings.ajax || settings.sAjaxSource ) {
6889			return 'ajax';
6890		}
6891		return 'dom';
6892	}
6893	
6894	
6895	
6896	
6897	/**
6898	 * Computed structure of the DataTables API, defined by the options passed to
6899	 * `DataTable.Api.register()` when building the API.
6900	 *
6901	 * The structure is built in order to speed creation and extension of the Api
6902	 * objects since the extensions are effectively pre-parsed.
6903	 *
6904	 * The array is an array of objects with the following structure, where this
6905	 * base array represents the Api prototype base:
6906	 *
6907	 *     [
6908	 *       {
6909	 *         name:      'data'                -- string   - Property name
6910	 *         val:       function () {},       -- function - Api method (or undefined if just an object
6911	 *         methodExt: [ ... ],              -- array    - Array of Api object definitions to extend the method result
6912	 *         propExt:   [ ... ]               -- array    - Array of Api object definitions to extend the property
6913	 *       },
6914	 *       {
6915	 *         name:     'row'
6916	 *         val:       {},
6917	 *         methodExt: [ ... ],
6918	 *         propExt:   [
6919	 *           {
6920	 *             name:      'data'
6921	 *             val:       function () {},
6922	 *             methodExt: [ ... ],
6923	 *             propExt:   [ ... ]
6924	 *           },
6925	 *           ...
6926	 *         ]
6927	 *       }
6928	 *     ]
6929	 *
6930	 * @type {Array}
6931	 * @ignore
6932	 */
6933	var __apiStruct = [];
6934	
6935	
6936	/**
6937	 * `Array.prototype` reference.
6938	 *
6939	 * @type object
6940	 * @ignore
6941	 */
6942	var __arrayProto = Array.prototype;
6943	
6944	
6945	/**
6946	 * Abstraction for `context` parameter of the `Api` constructor to allow it to
6947	 * take several different forms for ease of use.
6948	 *
6949	 * Each of the input parameter types will be converted to a DataTables settings
6950	 * object where possible.
6951	 *
6952	 * @param  {string|node|jQuery|object} mixed DataTable identifier. Can be one
6953	 *   of:
6954	 *
6955	 *   * `string` - jQuery selector. Any DataTables' matching the given selector
6956	 *     with be found and used.
6957	 *   * `node` - `TABLE` node which has already been formed into a DataTable.
6958	 *   * `jQuery` - A jQuery object of `TABLE` nodes.
6959	 *   * `object` - DataTables settings object
6960	 *   * `DataTables.Api` - API instance
6961	 * @return {array|null} Matching DataTables settings objects. `null` or
6962	 *   `undefined` is returned if no matching DataTable is found.
6963	 * @ignore
6964	 */
6965	var _toSettings = function ( mixed )
6966	{
6967		var idx, jq;
6968		var settings = DataTable.settings;
6969		var tables = $.map( settings, function (el, i) {
6970			return el.nTable;
6971		} );
6972	
6973		if ( ! mixed ) {
6974			return [];
6975		}
6976		else if ( mixed.nTable && mixed.oApi ) {
6977			// DataTables settings object
6978			return [ mixed ];
6979		}
6980		else if ( mixed.nodeName && mixed.nodeName.toLowerCase() === 'table' ) {
6981			// Table node
6982			idx = $.inArray( mixed, tables );
6983			return idx !== -1 ? [ settings[idx] ] : null;
6984		}
6985		else if ( mixed && typeof mixed.settings === 'function' ) {
6986			return mixed.settings().toArray();
6987		}
6988		else if ( typeof mixed === 'string' ) {
6989			// jQuery selector
6990			jq = $(mixed);
6991		}
6992		else if ( mixed instanceof $ ) {
6993			// jQuery object (also DataTables instance)
6994			jq = mixed;
6995		}
6996	
6997		if ( jq ) {
6998			return jq.map( function(i) {
6999				idx = $.inArray( this, tables );
7000				return idx !== -1 ? settings[idx] : null;
7001			} ).toArray();
7002		}
7003	};
7004	
7005	
7006	/**
7007	 * DataTables API class - used to control and interface with  one or more
7008	 * DataTables enhanced tables.
7009	 *
7010	 * The API class is heavily based on jQuery, presenting a chainable interface
7011	 * that you can use to interact with tables. Each instance of the API class has
7012	 * a "context" - i.e. the tables that it will operate on. This could be a single
7013	 * table, all tables on a page or a sub-set thereof.
7014	 *
7015	 * Additionally the API is designed to allow you to easily work with the data in
7016	 * the tables, retrieving and manipulating it as required. This is done by
7017	 * presenting the API class as an array like interface. The contents of the
7018	 * array depend upon the actions requested by each method (for example
7019	 * `rows().nodes()` will return an array of nodes, while `rows().data()` will
7020	 * return an array of objects or arrays depending upon your table's
7021	 * configuration). The API object has a number of array like methods (`push`,
7022	 * `pop`, `reverse` etc) as well as additional helper methods (`each`, `pluck`,
7023	 * `unique` etc) to assist your working with the data held in a table.
7024	 *
7025	 * Most methods (those which return an Api instance) are chainable, which means
7026	 * the return from a method call also has all of the methods available that the
7027	 * top level object had. For example, these two calls are equivalent:
7028	 *
7029	 *     // Not chained
7030	 *     api.row.add( {...} );
7031	 *     api.draw();
7032	 *
7033	 *     // Chained
7034	 *     api.row.add( {...} ).draw();
7035	 *
7036	 * @class DataTable.Api
7037	 * @param {array|object|string|jQuery} context DataTable identifier. This is
7038	 *   used to define which DataTables enhanced tables this API will operate on.
7039	 *   Can be one of:
7040	 *
7041	 *   * `string` - jQuery selector. Any DataTables' matching the given selector
7042	 *     with be found and used.
7043	 *   * `node` - `TABLE` node which has already been formed into a DataTable.
7044	 *   * `jQuery` - A jQuery object of `TABLE` nodes.
7045	 *   * `object` - DataTables settings object
7046	 * @param {array} [data] Data to initialise the Api instance with.
7047	 *
7048	 * @example
7049	 *   // Direct initialisation during DataTables construction
7050	 *   var api = $('#example').DataTable();
7051	 *
7052	 * @example
7053	 *   // Initialisation using a DataTables jQuery object
7054	 *   var api = $('#example').dataTable().api();
7055	 *
7056	 * @example
7057	 *   // Initialisation as a constructor
7058	 *   var api = new $.fn.DataTable.Api( 'table.dataTable' );
7059	 */
7060	_Api = function ( context, data )
7061	{
7062		if ( ! (this instanceof _Api) ) {
7063			return new _Api( context, data );
7064		}
7065	
7066		var settings = [];
7067		var ctxSettings = function ( o ) {
7068			var a = _toSettings( o );
7069			if ( a ) {
7070				settings.push.apply( settings, a );
7071			}
7072		};
7073	
7074		if ( Array.isArray( context ) ) {
7075			for ( var i=0, ien=context.length ; i<ien ; i++ ) {
7076				ctxSettings( context[i] );
7077			}
7078		}
7079		else {
7080			ctxSettings( context );
7081		}
7082	
7083		// Remove duplicates
7084		this.context = _unique( settings );
7085	
7086		// Initial data
7087		if ( data ) {
7088			$.merge( this, data );
7089		}
7090	
7091		// selector
7092		this.selector = {
7093			rows: null,
7094			cols: null,
7095			opts: null
7096		};
7097	
7098		_Api.extend( this, this, __apiStruct );
7099	};
7100	
7101	DataTable.Api = _Api;
7102	
7103	// Don't destroy the existing prototype, just extend it. Required for jQuery 2's
7104	// isPlainObject.
7105	$.extend( _Api.prototype, {
7106		any: function ()
7107		{
7108			return this.count() !== 0;
7109		},
7110	
7111	
7112		concat:  __arrayProto.concat,
7113	
7114	
7115		context: [], // array of table settings objects
7116	
7117	
7118		count: function ()
7119		{
7120			return this.flatten().length;
7121		},
7122	
7123	
7124		each: function ( fn )
7125		{
7126			for ( var i=0, ien=this.length ; i<ien; i++ ) {
7127				fn.call( this, this[i], i, this );
7128			}
7129	
7130			return this;
7131		},
7132	
7133	
7134		eq: function ( idx )
7135		{
7136			var ctx = this.context;
7137	
7138			return ctx.length > idx ?
7139				new _Api( ctx[idx], this[idx] ) :
7140				null;
7141		},
7142	
7143	
7144		filter: function ( fn )
7145		{
7146			var a = [];
7147	
7148			if ( __arrayProto.filter ) {
7149				a = __arrayProto.filter.call( this, fn, this );
7150			}
7151			else {
7152				// Compatibility for browsers without EMCA-252-5 (JS 1.6)
7153				for ( var i=0, ien=this.length ; i<ien ; i++ ) {
7154					if ( fn.call( this, this[i], i, this ) ) {
7155						a.push( this[i] );
7156					}
7157				}
7158			}
7159	
7160			return new _Api( this.context, a );
7161		},
7162	
7163	
7164		flatten: function ()
7165		{
7166			var a = [];
7167			return new _Api( this.context, a.concat.apply( a, this.toArray() ) );
7168		},
7169	
7170	
7171		join:    __arrayProto.join,
7172	
7173	
7174		indexOf: __arrayProto.indexOf || function (obj, start)
7175		{
7176			for ( var i=(start || 0), ien=this.length ; i<ien ; i++ ) {
7177				if ( this[i] === obj ) {
7178					return i;
7179				}
7180			}
7181			return -1;
7182		},
7183	
7184		iterator: function ( flatten, type, fn, alwaysNew ) {
7185			var
7186				a = [], ret,
7187				i, ien, j, jen,
7188				context = this.context,
7189				rows, items, item,
7190				selector = this.selector;
7191	
7192			// Argument shifting
7193			if ( typeof flatten === 'string' ) {
7194				alwaysNew = fn;
7195				fn = type;
7196				type = flatten;
7197				flatten = false;
7198			}
7199	
7200			for ( i=0, ien=context.length ; i<ien ; i++ ) {
7201				var apiInst = new _Api( context[i] );
7202	
7203				if ( type === 'table' ) {
7204					ret = fn.call( apiInst, context[i], i );
7205	
7206					if ( ret !== undefined ) {
7207						a.push( ret );
7208					}
7209				}
7210				else if ( type === 'columns' || type === 'rows' ) {
7211					// this has same length as context - one entry for each table
7212					ret = fn.call( apiInst, context[i], this[i], i );
7213	
7214					if ( ret !== undefined ) {
7215						a.push( ret );
7216					}
7217				}
7218				else if ( type === 'column' || type === 'column-rows' || type === 'row' || type === 'cell' ) {
7219					// columns and rows share the same structure.
7220					// 'this' is an array of column indexes for each context
7221					items = this[i];
7222	
7223					if ( type === 'column-rows' ) {
7224						rows = _selector_row_indexes( context[i], selector.opts );
7225					}
7226	
7227					for ( j=0, jen=items.length ; j<jen ; j++ ) {
7228						item = items[j];
vendor: 71,453 bytes, lines 7229-9858
7229	
7230						if ( type === 'cell' ) {
7231							ret = fn.call( apiInst, context[i], item.row, item.column, i, j );
7232						}
7233						else {
7234							ret = fn.call( apiInst, context[i], item, i, j, rows );
7235						}
7236	
7237						if ( ret !== undefined ) {
7238							a.push( ret );
7239						}
7240					}
7241				}
7242			}
7243	
7244			if ( a.length || alwaysNew ) {
7245				var api = new _Api( context, flatten ? a.concat.apply( [], a ) : a );
7246				var apiSelector = api.selector;
7247				apiSelector.rows = selector.rows;
7248				apiSelector.cols = selector.cols;
7249				apiSelector.opts = selector.opts;
7250				return api;
7251			}
7252			return this;
7253		},
7254	
7255	
7256		lastIndexOf: __arrayProto.lastIndexOf || function (obj, start)
7257		{
7258			// Bit cheeky...
7259			return this.indexOf.apply( this.toArray.reverse(), arguments );
7260		},
7261	
7262	
7263		length:  0,
7264	
7265	
7266		map: function ( fn )
7267		{
7268			var a = [];
7269	
7270			if ( __arrayProto.map ) {
7271				a = __arrayProto.map.call( this, fn, this );
7272			}
7273			else {
7274				// Compatibility for browsers without EMCA-252-5 (JS 1.6)
7275				for ( var i=0, ien=this.length ; i<ien ; i++ ) {
7276					a.push( fn.call( this, this[i], i ) );
7277				}
7278			}
7279	
7280			return new _Api( this.context, a );
7281		},
7282	
7283	
7284		pluck: function ( prop )
7285		{
7286			var fn = DataTable.util.get(prop);
7287	
7288			return this.map( function ( el ) {
7289				return fn(el);
7290			} );
7291		},
7292	
7293		pop:     __arrayProto.pop,
7294	
7295	
7296		push:    __arrayProto.push,
7297	
7298	
7299		// Does not return an API instance
7300		reduce: __arrayProto.reduce || function ( fn, init )
7301		{
7302			return _fnReduce( this, fn, init, 0, this.length, 1 );
7303		},
7304	
7305	
7306		reduceRight: __arrayProto.reduceRight || function ( fn, init )
7307		{
7308			return _fnReduce( this, fn, init, this.length-1, -1, -1 );
7309		},
7310	
7311	
7312		reverse: __arrayProto.reverse,
7313	
7314	
7315		// Object with rows, columns and opts
7316		selector: null,
7317	
7318	
7319		shift:   __arrayProto.shift,
7320	
7321	
7322		slice: function () {
7323			return new _Api( this.context, this );
7324		},
7325	
7326	
7327		sort:    __arrayProto.sort, // ? name - order?
7328	
7329	
7330		splice:  __arrayProto.splice,
7331	
7332	
7333		toArray: function ()
7334		{
7335			return __arrayProto.slice.call( this );
7336		},
7337	
7338	
7339		to$: function ()
7340		{
7341			return $( this );
7342		},
7343	
7344	
7345		toJQuery: function ()
7346		{
7347			return $( this );
7348		},
7349	
7350	
7351		unique: function ()
7352		{
7353			return new _Api( this.context, _unique(this) );
7354		},
7355	
7356	
7357		unshift: __arrayProto.unshift
7358	} );
7359	
7360	
7361	_Api.extend = function ( scope, obj, ext )
7362	{
7363		// Only extend API instances and static properties of the API
7364		if ( ! ext.length || ! obj || ( ! (obj instanceof _Api) && ! obj.__dt_wrapper ) ) {
7365			return;
7366		}
7367	
7368		var
7369			i, ien,
7370			struct,
7371			methodScoping = function ( scope, fn, struc ) {
7372				return function () {
7373					var ret = fn.apply( scope, arguments );
7374	
7375					// Method extension
7376					_Api.extend( ret, ret, struc.methodExt );
7377					return ret;
7378				};
7379			};
7380	
7381		for ( i=0, ien=ext.length ; i<ien ; i++ ) {
7382			struct = ext[i];
7383	
7384			// Value
7385			obj[ struct.name ] = struct.type === 'function' ?
7386				methodScoping( scope, struct.val, struct ) :
7387				struct.type === 'object' ?
7388					{} :
7389					struct.val;
7390	
7391			obj[ struct.name ].__dt_wrapper = true;
7392	
7393			// Property extension
7394			_Api.extend( scope, obj[ struct.name ], struct.propExt );
7395		}
7396	};
7397	
7398	
7399	// @todo - Is there need for an augment function?
7400	// _Api.augment = function ( inst, name )
7401	// {
7402	// 	// Find src object in the structure from the name
7403	// 	var parts = name.split('.');
7404	
7405	// 	_Api.extend( inst, obj );
7406	// };
7407	
7408	
7409	//     [
7410	//       {
7411	//         name:      'data'                -- string   - Property name
7412	//         val:       function () {},       -- function - Api method (or undefined if just an object
7413	//         methodExt: [ ... ],              -- array    - Array of Api object definitions to extend the method result
7414	//         propExt:   [ ... ]               -- array    - Array of Api object definitions to extend the property
7415	//       },
7416	//       {
7417	//         name:     'row'
7418	//         val:       {},
7419	//         methodExt: [ ... ],
7420	//         propExt:   [
7421	//           {
7422	//             name:      'data'
7423	//             val:       function () {},
7424	//             methodExt: [ ... ],
7425	//             propExt:   [ ... ]
7426	//           },
7427	//           ...
7428	//         ]
7429	//       }
7430	//     ]
7431	
7432	_Api.register = _api_register = function ( name, val )
7433	{
7434		if ( Array.isArray( name ) ) {
7435			for ( var j=0, jen=name.length ; j<jen ; j++ ) {
7436				_Api.register( name[j], val );
7437			}
7438			return;
7439		}
7440	
7441		var
7442			i, ien,
7443			heir = name.split('.'),
7444			struct = __apiStruct,
7445			key, method;
7446	
7447		var find = function ( src, name ) {
7448			for ( var i=0, ien=src.length ; i<ien ; i++ ) {
7449				if ( src[i].name === name ) {
7450					return src[i];
7451				}
7452			}
7453			return null;
7454		};
7455	
7456		for ( i=0, ien=heir.length ; i<ien ; i++ ) {
7457			method = heir[i].indexOf('()') !== -1;
7458			key = method ?
7459				heir[i].replace('()', '') :
7460				heir[i];
7461	
7462			var src = find( struct, key );
7463			if ( ! src ) {
7464				src = {
7465					name:      key,
7466					val:       {},
7467					methodExt: [],
7468					propExt:   [],
7469					type:      'object'
7470				};
7471				struct.push( src );
7472			}
7473	
7474			if ( i === ien-1 ) {
7475				src.val = val;
7476				src.type = typeof val === 'function' ?
7477					'function' :
7478					$.isPlainObject( val ) ?
7479						'object' :
7480						'other';
7481			}
7482			else {
7483				struct = method ?
7484					src.methodExt :
7485					src.propExt;
7486			}
7487		}
7488	};
7489	
7490	_Api.registerPlural = _api_registerPlural = function ( pluralName, singularName, val ) {
7491		_Api.register( pluralName, val );
7492	
7493		_Api.register( singularName, function () {
7494			var ret = val.apply( this, arguments );
7495	
7496			if ( ret === this ) {
7497				// Returned item is the API instance that was passed in, return it
7498				return this;
7499			}
7500			else if ( ret instanceof _Api ) {
7501				// New API instance returned, want the value from the first item
7502				// in the returned array for the singular result.
7503				return ret.length ?
7504					Array.isArray( ret[0] ) ?
7505						new _Api( ret.context, ret[0] ) : // Array results are 'enhanced'
7506						ret[0] :
7507					undefined;
7508			}
7509	
7510			// Non-API return - just fire it back
7511			return ret;
7512		} );
7513	};
7514	
7515	
7516	/**
7517	 * Selector for HTML tables. Apply the given selector to the give array of
7518	 * DataTables settings objects.
7519	 *
7520	 * @param {string|integer} [selector] jQuery selector string or integer
7521	 * @param  {array} Array of DataTables settings objects to be filtered
7522	 * @return {array}
7523	 * @ignore
7524	 */
7525	var __table_selector = function ( selector, a )
7526	{
7527		if ( Array.isArray(selector) ) {
7528			return $.map( selector, function (item) {
7529				return __table_selector(item, a);
7530			} );
7531		}
7532	
7533		// Integer is used to pick out a table by index
7534		if ( typeof selector === 'number' ) {
7535			return [ a[ selector ] ];
7536		}
7537	
7538		// Perform a jQuery selector on the table nodes
7539		var nodes = $.map( a, function (el, i) {
7540			return el.nTable;
7541		} );
7542	
7543		return $(nodes)
7544			.filter( selector )
7545			.map( function (i) {
7546				// Need to translate back from the table node to the settings
7547				var idx = $.inArray( this, nodes );
7548				return a[ idx ];
7549			} )
7550			.toArray();
7551	};
7552	
7553	
7554	
7555	/**
7556	 * Context selector for the API's context (i.e. the tables the API instance
7557	 * refers to.
7558	 *
7559	 * @name    DataTable.Api#tables
7560	 * @param {string|integer} [selector] Selector to pick which tables the iterator
7561	 *   should operate on. If not given, all tables in the current context are
7562	 *   used. This can be given as a jQuery selector (for example `':gt(0)'`) to
7563	 *   select multiple tables or as an integer to select a single table.
7564	 * @returns {DataTable.Api} Returns a new API instance if a selector is given.
7565	 */
7566	_api_register( 'tables()', function ( selector ) {
7567		// A new instance is created if there was a selector specified
7568		return selector !== undefined && selector !== null ?
7569			new _Api( __table_selector( selector, this.context ) ) :
7570			this;
7571	} );
7572	
7573	
7574	_api_register( 'table()', function ( selector ) {
7575		var tables = this.tables( selector );
7576		var ctx = tables.context;
7577	
7578		// Truncate to the first matched table
7579		return ctx.length ?
7580			new _Api( ctx[0] ) :
7581			tables;
7582	} );
7583	
7584	
7585	_api_registerPlural( 'tables().nodes()', 'table().node()' , function () {
7586		return this.iterator( 'table', function ( ctx ) {
7587			return ctx.nTable;
7588		}, 1 );
7589	} );
7590	
7591	
7592	_api_registerPlural( 'tables().body()', 'table().body()' , function () {
7593		return this.iterator( 'table', function ( ctx ) {
7594			return ctx.nTBody;
7595		}, 1 );
7596	} );
7597	
7598	
7599	_api_registerPlural( 'tables().header()', 'table().header()' , function () {
7600		return this.iterator( 'table', function ( ctx ) {
7601			return ctx.nTHead;
7602		}, 1 );
7603	} );
7604	
7605	
7606	_api_registerPlural( 'tables().footer()', 'table().footer()' , function () {
7607		return this.iterator( 'table', function ( ctx ) {
7608			return ctx.nTFoot;
7609		}, 1 );
7610	} );
7611	
7612	
7613	_api_registerPlural( 'tables().containers()', 'table().container()' , function () {
7614		return this.iterator( 'table', function ( ctx ) {
7615			return ctx.nTableWrapper;
7616		}, 1 );
7617	} );
7618	
7619	
7620	
7621	/**
7622	 * Redraw the tables in the current context.
7623	 */
7624	_api_register( 'draw()', function ( paging ) {
7625		return this.iterator( 'table', function ( settings ) {
7626			if ( paging === 'page' ) {
7627				_fnDraw( settings );
7628			}
7629			else {
7630				if ( typeof paging === 'string' ) {
7631					paging = paging === 'full-hold' ?
7632						false :
7633						true;
7634				}
7635	
7636				_fnReDraw( settings, paging===false );
7637			}
7638		} );
7639	} );
7640	
7641	
7642	
7643	/**
7644	 * Get the current page index.
7645	 *
7646	 * @return {integer} Current page index (zero based)
7647	 *//**
7648	 * Set the current page.
7649	 *
7650	 * Note that if you attempt to show a page which does not exist, DataTables will
7651	 * not throw an error, but rather reset the paging.
7652	 *
7653	 * @param {integer|string} action The paging action to take. This can be one of:
7654	 *  * `integer` - The page index to jump to
7655	 *  * `string` - An action to take:
7656	 *    * `first` - Jump to first page.
7657	 *    * `next` - Jump to the next page
7658	 *    * `previous` - Jump to previous page
7659	 *    * `last` - Jump to the last page.
7660	 * @returns {DataTables.Api} this
7661	 */
7662	_api_register( 'page()', function ( action ) {
7663		if ( action === undefined ) {
7664			return this.page.info().page; // not an expensive call
7665		}
7666	
7667		// else, have an action to take on all tables
7668		return this.iterator( 'table', function ( settings ) {
7669			_fnPageChange( settings, action );
7670		} );
7671	} );
7672	
7673	
7674	/**
7675	 * Paging information for the first table in the current context.
7676	 *
7677	 * If you require paging information for another table, use the `table()` method
7678	 * with a suitable selector.
7679	 *
7680	 * @return {object} Object with the following properties set:
7681	 *  * `page` - Current page index (zero based - i.e. the first page is `0`)
7682	 *  * `pages` - Total number of pages
7683	 *  * `start` - Display index for the first record shown on the current page
7684	 *  * `end` - Display index for the last record shown on the current page
7685	 *  * `length` - Display length (number of records). Note that generally `start
7686	 *    + length = end`, but this is not always true, for example if there are
7687	 *    only 2 records to show on the final page, with a length of 10.
7688	 *  * `recordsTotal` - Full data set length
7689	 *  * `recordsDisplay` - Data set length once the current filtering criterion
7690	 *    are applied.
7691	 */
7692	_api_register( 'page.info()', function ( action ) {
7693		if ( this.context.length === 0 ) {
7694			return undefined;
7695		}
7696	
7697		var
7698			settings   = this.context[0],
7699			start      = settings._iDisplayStart,
7700			len        = settings.oFeatures.bPaginate ? settings._iDisplayLength : -1,
7701			visRecords = settings.fnRecordsDisplay(),
7702			all        = len === -1;
7703	
7704		return {
7705			"page":           all ? 0 : Math.floor( start / len ),
7706			"pages":          all ? 1 : Math.ceil( visRecords / len ),
7707			"start":          start,
7708			"end":            settings.fnDisplayEnd(),
7709			"length":         len,
7710			"recordsTotal":   settings.fnRecordsTotal(),
7711			"recordsDisplay": visRecords,
7712			"serverSide":     _fnDataSource( settings ) === 'ssp'
7713		};
7714	} );
7715	
7716	
7717	/**
7718	 * Get the current page length.
7719	 *
7720	 * @return {integer} Current page length. Note `-1` indicates that all records
7721	 *   are to be shown.
7722	 *//**
7723	 * Set the current page length.
7724	 *
7725	 * @param {integer} Page length to set. Use `-1` to show all records.
7726	 * @returns {DataTables.Api} this
7727	 */
7728	_api_register( 'page.len()', function ( len ) {
7729		// Note that we can't call this function 'length()' because `length`
7730		// is a Javascript property of functions which defines how many arguments
7731		// the function expects.
7732		if ( len === undefined ) {
7733			return this.context.length !== 0 ?
7734				this.context[0]._iDisplayLength :
7735				undefined;
7736		}
7737	
7738		// else, set the page length
7739		return this.iterator( 'table', function ( settings ) {
7740			_fnLengthChange( settings, len );
7741		} );
7742	} );
7743	
7744	
7745	
7746	var __reload = function ( settings, holdPosition, callback ) {
7747		// Use the draw event to trigger a callback
7748		if ( callback ) {
7749			var api = new _Api( settings );
7750	
7751			api.one( 'draw', function () {
7752				callback( api.ajax.json() );
7753			} );
7754		}
7755	
7756		if ( _fnDataSource( settings ) == 'ssp' ) {
7757			_fnReDraw( settings, holdPosition );
7758		}
7759		else {
7760			_fnProcessingDisplay( settings, true );
7761	
7762			// Cancel an existing request
7763			var xhr = settings.jqXHR;
7764			if ( xhr && xhr.readyState !== 4 ) {
7765				xhr.abort();
7766			}
7767	
7768			// Trigger xhr
7769			_fnBuildAjax( settings, [], function( json ) {
7770				_fnClearTable( settings );
7771	
7772				var data = _fnAjaxDataSrc( settings, json );
7773				for ( var i=0, ien=data.length ; i<ien ; i++ ) {
7774					_fnAddData( settings, data[i] );
7775				}
7776	
7777				_fnReDraw( settings, holdPosition );
7778				_fnProcessingDisplay( settings, false );
7779			} );
7780		}
7781	};
7782	
7783	
7784	/**
7785	 * Get the JSON response from the last Ajax request that DataTables made to the
7786	 * server. Note that this returns the JSON from the first table in the current
7787	 * context.
7788	 *
7789	 * @return {object} JSON received from the server.
7790	 */
7791	_api_register( 'ajax.json()', function () {
7792		var ctx = this.context;
7793	
7794		if ( ctx.length > 0 ) {
7795			return ctx[0].json;
7796		}
7797	
7798		// else return undefined;
7799	} );
7800	
7801	
7802	/**
7803	 * Get the data submitted in the last Ajax request
7804	 */
7805	_api_register( 'ajax.params()', function () {
7806		var ctx = this.context;
7807	
7808		if ( ctx.length > 0 ) {
7809			return ctx[0].oAjaxData;
7810		}
7811	
7812		// else return undefined;
7813	} );
7814	
7815	
7816	/**
7817	 * Reload tables from the Ajax data source. Note that this function will
7818	 * automatically re-draw the table when the remote data has been loaded.
7819	 *
7820	 * @param {boolean} [reset=true] Reset (default) or hold the current paging
7821	 *   position. A full re-sort and re-filter is performed when this method is
7822	 *   called, which is why the pagination reset is the default action.
7823	 * @returns {DataTables.Api} this
7824	 */
7825	_api_register( 'ajax.reload()', function ( callback, resetPaging ) {
7826		return this.iterator( 'table', function (settings) {
7827			__reload( settings, resetPaging===false, callback );
7828		} );
7829	} );
7830	
7831	
7832	/**
7833	 * Get the current Ajax URL. Note that this returns the URL from the first
7834	 * table in the current context.
7835	 *
7836	 * @return {string} Current Ajax source URL
7837	 *//**
7838	 * Set the Ajax URL. Note that this will set the URL for all tables in the
7839	 * current context.
7840	 *
7841	 * @param {string} url URL to set.
7842	 * @returns {DataTables.Api} this
7843	 */
7844	_api_register( 'ajax.url()', function ( url ) {
7845		var ctx = this.context;
7846	
7847		if ( url === undefined ) {
7848			// get
7849			if ( ctx.length === 0 ) {
7850				return undefined;
7851			}
7852			ctx = ctx[0];
7853	
7854			return ctx.ajax ?
7855				$.isPlainObject( ctx.ajax ) ?
7856					ctx.ajax.url :
7857					ctx.ajax :
7858				ctx.sAjaxSource;
7859		}
7860	
7861		// set
7862		return this.iterator( 'table', function ( settings ) {
7863			if ( $.isPlainObject( settings.ajax ) ) {
7864				settings.ajax.url = url;
7865			}
7866			else {
7867				settings.ajax = url;
7868			}
7869			// No need to consider sAjaxSource here since DataTables gives priority
7870			// to `ajax` over `sAjaxSource`. So setting `ajax` here, renders any
7871			// value of `sAjaxSource` redundant.
7872		} );
7873	} );
7874	
7875	
7876	/**
7877	 * Load data from the newly set Ajax URL. Note that this method is only
7878	 * available when `ajax.url()` is used to set a URL. Additionally, this method
7879	 * has the same effect as calling `ajax.reload()` but is provided for
7880	 * convenience when setting a new URL. Like `ajax.reload()` it will
7881	 * automatically redraw the table once the remote data has been loaded.
7882	 *
7883	 * @returns {DataTables.Api} this
7884	 */
7885	_api_register( 'ajax.url().load()', function ( callback, resetPaging ) {
7886		// Same as a reload, but makes sense to present it for easy access after a
7887		// url change
7888		return this.iterator( 'table', function ( ctx ) {
7889			__reload( ctx, resetPaging===false, callback );
7890		} );
7891	} );
7892	
7893	
7894	
7895	
7896	var _selector_run = function ( type, selector, selectFn, settings, opts )
7897	{
7898		var
7899			out = [], res,
7900			a, i, ien, j, jen,
7901			selectorType = typeof selector;
7902	
7903		// Can't just check for isArray here, as an API or jQuery instance might be
7904		// given with their array like look
7905		if ( ! selector || selectorType === 'string' || selectorType === 'function' || selector.length === undefined ) {
7906			selector = [ selector ];
7907		}
7908	
7909		for ( i=0, ien=selector.length ; i<ien ; i++ ) {
7910			// Only split on simple strings - complex expressions will be jQuery selectors
7911			a = selector[i] && selector[i].split && ! selector[i].match(/[\[\(:]/) ?
7912				selector[i].split(',') :
7913				[ selector[i] ];
7914	
7915			for ( j=0, jen=a.length ; j<jen ; j++ ) {
7916				res = selectFn( typeof a[j] === 'string' ? (a[j]).trim() : a[j] );
7917	
7918				if ( res && res.length ) {
7919					out = out.concat( res );
7920				}
7921			}
7922		}
7923	
7924		// selector extensions
7925		var ext = _ext.selector[ type ];
7926		if ( ext.length ) {
7927			for ( i=0, ien=ext.length ; i<ien ; i++ ) {
7928				out = ext[i]( settings, opts, out );
7929			}
7930		}
7931	
7932		return _unique( out );
7933	};
7934	
7935	
7936	var _selector_opts = function ( opts )
7937	{
7938		if ( ! opts ) {
7939			opts = {};
7940		}
7941	
7942		// Backwards compatibility for 1.9- which used the terminology filter rather
7943		// than search
7944		if ( opts.filter && opts.search === undefined ) {
7945			opts.search = opts.filter;
7946		}
7947	
7948		return $.extend( {
7949			search: 'none',
7950			order: 'current',
7951			page: 'all'
7952		}, opts );
7953	};
7954	
7955	
7956	var _selector_first = function ( inst )
7957	{
7958		// Reduce the API instance to the first item found
7959		for ( var i=0, ien=inst.length ; i<ien ; i++ ) {
7960			if ( inst[i].length > 0 ) {
7961				// Assign the first element to the first item in the instance
7962				// and truncate the instance and context
7963				inst[0] = inst[i];
7964				inst[0].length = 1;
7965				inst.length = 1;
7966				inst.context = [ inst.context[i] ];
7967	
7968				return inst;
7969			}
7970		}
7971	
7972		// Not found - return an empty instance
7973		inst.length = 0;
7974		return inst;
7975	};
7976	
7977	
7978	var _selector_row_indexes = function ( settings, opts )
7979	{
7980		var
7981			i, ien, tmp, a=[],
7982			displayFiltered = settings.aiDisplay,
7983			displayMaster = settings.aiDisplayMaster;
7984	
7985		var
7986			search = opts.search,  // none, applied, removed
7987			order  = opts.order,   // applied, current, index (original - compatibility with 1.9)
7988			page   = opts.page;    // all, current
7989	
7990		if ( _fnDataSource( settings ) == 'ssp' ) {
7991			// In server-side processing mode, most options are irrelevant since
7992			// rows not shown don't exist and the index order is the applied order
7993			// Removed is a special case - for consistency just return an empty
7994			// array
7995			return search === 'removed' ?
7996				[] :
7997				_range( 0, displayMaster.length );
7998		}
7999		else if ( page == 'current' ) {
8000			// Current page implies that order=current and filter=applied, since it is
8001			// fairly senseless otherwise, regardless of what order and search actually
8002			// are
8003			for ( i=settings._iDisplayStart, ien=settings.fnDisplayEnd() ; i<ien ; i++ ) {
8004				a.push( displayFiltered[i] );
8005			}
8006		}
8007		else if ( order == 'current' || order == 'applied' ) {
8008			if ( search == 'none') {
8009				a = displayMaster.slice();
8010			}
8011			else if ( search == 'applied' ) {
8012				a = displayFiltered.slice();
8013			}
8014			else if ( search == 'removed' ) {
8015				// O(n+m) solution by creating a hash map
8016				var displayFilteredMap = {};
8017	
8018				for ( var i=0, ien=displayFiltered.length ; i<ien ; i++ ) {
8019					displayFilteredMap[displayFiltered[i]] = null;
8020				}
8021	
8022				a = $.map( displayMaster, function (el) {
8023					return ! displayFilteredMap.hasOwnProperty(el) ?
8024						el :
8025						null;
8026				} );
8027			}
8028		}
8029		else if ( order == 'index' || order == 'original' ) {
8030			for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
8031				if ( search == 'none' ) {
8032					a.push( i );
8033				}
8034				else { // applied | removed
8035					tmp = $.inArray( i, displayFiltered );
8036	
8037					if ((tmp === -1 && search == 'removed') ||
8038						(tmp >= 0   && search == 'applied') )
8039					{
8040						a.push( i );
8041					}
8042				}
8043			}
8044		}
8045	
8046		return a;
8047	};
8048	
8049	
8050	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8051	 * Rows
8052	 *
8053	 * {}          - no selector - use all available rows
8054	 * {integer}   - row aoData index
8055	 * {node}      - TR node
8056	 * {string}    - jQuery selector to apply to the TR elements
8057	 * {array}     - jQuery array of nodes, or simply an array of TR nodes
8058	 *
8059	 */
8060	var __row_selector = function ( settings, selector, opts )
8061	{
8062		var rows;
8063		var run = function ( sel ) {
8064			var selInt = _intVal( sel );
8065			var i, ien;
8066			var aoData = settings.aoData;
8067	
8068			// Short cut - selector is a number and no options provided (default is
8069			// all records, so no need to check if the index is in there, since it
8070			// must be - dev error if the index doesn't exist).
8071			if ( selInt !== null && ! opts ) {
8072				return [ selInt ];
8073			}
8074	
8075			if ( ! rows ) {
8076				rows = _selector_row_indexes( settings, opts );
8077			}
8078	
8079			if ( selInt !== null && $.inArray( selInt, rows ) !== -1 ) {
8080				// Selector - integer
8081				return [ selInt ];
8082			}
8083			else if ( sel === null || sel === undefined || sel === '' ) {
8084				// Selector - none
8085				return rows;
8086			}
8087	
8088			// Selector - function
8089			if ( typeof sel === 'function' ) {
8090				return $.map( rows, function (idx) {
8091					var row = aoData[ idx ];
8092					return sel( idx, row._aData, row.nTr ) ? idx : null;
8093				} );
8094			}
8095	
8096			// Selector - node
8097			if ( sel.nodeName ) {
8098				var rowIdx = sel._DT_RowIndex;  // Property added by DT for fast lookup
8099				var cellIdx = sel._DT_CellIndex;
8100	
8101				if ( rowIdx !== undefined ) {
8102					// Make sure that the row is actually still present in the table
8103					return aoData[ rowIdx ] && aoData[ rowIdx ].nTr === sel ?
8104						[ rowIdx ] :
8105						[];
8106				}
8107				else if ( cellIdx ) {
8108					return aoData[ cellIdx.row ] && aoData[ cellIdx.row ].nTr === sel.parentNode ?
8109						[ cellIdx.row ] :
8110						[];
8111				}
8112				else {
8113					var host = $(sel).closest('*[data-dt-row]');
8114					return host.length ?
8115						[ host.data('dt-row') ] :
8116						[];
8117				}
8118			}
8119	
8120			// ID selector. Want to always be able to select rows by id, regardless
8121			// of if the tr element has been created or not, so can't rely upon
8122			// jQuery here - hence a custom implementation. This does not match
8123			// Sizzle's fast selector or HTML4 - in HTML5 the ID can be anything,
8124			// but to select it using a CSS selector engine (like Sizzle or
8125			// querySelect) it would need to need to be escaped for some characters.
8126			// DataTables simplifies this for row selectors since you can select
8127			// only a row. A # indicates an id any anything that follows is the id -
8128			// unescaped.
8129			if ( typeof sel === 'string' && sel.charAt(0) === '#' ) {
8130				// get row index from id
8131				var rowObj = settings.aIds[ sel.replace( /^#/, '' ) ];
8132				if ( rowObj !== undefined ) {
8133					return [ rowObj.idx ];
8134				}
8135	
8136				// need to fall through to jQuery in case there is DOM id that
8137				// matches
8138			}
8139			
8140			// Get nodes in the order from the `rows` array with null values removed
8141			var nodes = _removeEmpty(
8142				_pluck_order( settings.aoData, rows, 'nTr' )
8143			);
8144	
8145			// Selector - jQuery selector string, array of nodes or jQuery object/
8146			// As jQuery's .filter() allows jQuery objects to be passed in filter,
8147			// it also allows arrays, so this will cope with all three options
8148			return $(nodes)
8149				.filter( sel )
8150				.map( function () {
8151					return this._DT_RowIndex;
8152				} )
8153				.toArray();
8154		};
8155	
8156		return _selector_run( 'row', selector, run, settings, opts );
8157	};
8158	
8159	
8160	_api_register( 'rows()', function ( selector, opts ) {
8161		// argument shifting
8162		if ( selector === undefined ) {
8163			selector = '';
8164		}
8165		else if ( $.isPlainObject( selector ) ) {
8166			opts = selector;
8167			selector = '';
8168		}
8169	
8170		opts = _selector_opts( opts );
8171	
8172		var inst = this.iterator( 'table', function ( settings ) {
8173			return __row_selector( settings, selector, opts );
8174		}, 1 );
8175	
8176		// Want argument shifting here and in __row_selector?
8177		inst.selector.rows = selector;
8178		inst.selector.opts = opts;
8179	
8180		return inst;
8181	} );
8182	
8183	_api_register( 'rows().nodes()', function () {
8184		return this.iterator( 'row', function ( settings, row ) {
8185			return settings.aoData[ row ].nTr || undefined;
8186		}, 1 );
8187	} );
8188	
8189	_api_register( 'rows().data()', function () {
8190		return this.iterator( true, 'rows', function ( settings, rows ) {
8191			return _pluck_order( settings.aoData, rows, '_aData' );
8192		}, 1 );
8193	} );
8194	
8195	_api_registerPlural( 'rows().cache()', 'row().cache()', function ( type ) {
8196		return this.iterator( 'row', function ( settings, row ) {
8197			var r = settings.aoData[ row ];
8198			return type === 'search' ? r._aFilterData : r._aSortData;
8199		}, 1 );
8200	} );
8201	
8202	_api_registerPlural( 'rows().invalidate()', 'row().invalidate()', function ( src ) {
8203		return this.iterator( 'row', function ( settings, row ) {
8204			_fnInvalidate( settings, row, src );
8205		} );
8206	} );
8207	
8208	_api_registerPlural( 'rows().indexes()', 'row().index()', function () {
8209		return this.iterator( 'row', function ( settings, row ) {
8210			return row;
8211		}, 1 );
8212	} );
8213	
8214	_api_registerPlural( 'rows().ids()', 'row().id()', function ( hash ) {
8215		var a = [];
8216		var context = this.context;
8217	
8218		// `iterator` will drop undefined values, but in this case we want them
8219		for ( var i=0, ien=context.length ; i<ien ; i++ ) {
8220			for ( var j=0, jen=this[i].length ; j<jen ; j++ ) {
8221				var id = context[i].rowIdFn( context[i].aoData[ this[i][j] ]._aData );
8222				a.push( (hash === true ? '#' : '' )+ id );
8223			}
8224		}
8225	
8226		return new _Api( context, a );
8227	} );
8228	
8229	_api_registerPlural( 'rows().remove()', 'row().remove()', function () {
8230		var that = this;
8231	
8232		this.iterator( 'row', function ( settings, row, thatIdx ) {
8233			var data = settings.aoData;
8234			var rowData = data[ row ];
8235			var i, ien, j, jen;
8236			var loopRow, loopCells;
8237	
8238			data.splice( row, 1 );
8239	
8240			// Update the cached indexes
8241			for ( i=0, ien=data.length ; i<ien ; i++ ) {
8242				loopRow = data[i];
8243				loopCells = loopRow.anCells;
8244	
8245				// Rows
8246				if ( loopRow.nTr !== null ) {
8247					loopRow.nTr._DT_RowIndex = i;
8248				}
8249	
8250				// Cells
8251				if ( loopCells !== null ) {
8252					for ( j=0, jen=loopCells.length ; j<jen ; j++ ) {
8253						loopCells[j]._DT_CellIndex.row = i;
8254					}
8255				}
8256			}
8257	
8258			// Delete from the display arrays
8259			_fnDeleteIndex( settings.aiDisplayMaster, row );
8260			_fnDeleteIndex( settings.aiDisplay, row );
8261			_fnDeleteIndex( that[ thatIdx ], row, false ); // maintain local indexes
8262	
8263			// For server-side processing tables - subtract the deleted row from the count
8264			if ( settings._iRecordsDisplay > 0 ) {
8265				settings._iRecordsDisplay--;
8266			}
8267	
8268			// Check for an 'overflow' they case for displaying the table
8269			_fnLengthOverflow( settings );
8270	
8271			// Remove the row's ID reference if there is one
8272			var id = settings.rowIdFn( rowData._aData );
8273			if ( id !== undefined ) {
8274				delete settings.aIds[ id ];
8275			}
8276		} );
8277	
8278		this.iterator( 'table', function ( settings ) {
8279			for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
8280				settings.aoData[i].idx = i;
8281			}
8282		} );
8283	
8284		return this;
8285	} );
8286	
8287	
8288	_api_register( 'rows.add()', function ( rows ) {
8289		var newRows = this.iterator( 'table', function ( settings ) {
8290				var row, i, ien;
8291				var out = [];
8292	
8293				for ( i=0, ien=rows.length ; i<ien ; i++ ) {
8294					row = rows[i];
8295	
8296					if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {
8297						out.push( _fnAddTr( settings, row )[0] );
8298					}
8299					else {
8300						out.push( _fnAddData( settings, row ) );
8301					}
8302				}
8303	
8304				return out;
8305			}, 1 );
8306	
8307		// Return an Api.rows() extended instance, so rows().nodes() etc can be used
8308		var modRows = this.rows( -1 );
8309		modRows.pop();
8310		$.merge( modRows, newRows );
8311	
8312		return modRows;
8313	} );
8314	
8315	
8316	
8317	
8318	
8319	/**
8320	 *
8321	 */
8322	_api_register( 'row()', function ( selector, opts ) {
8323		return _selector_first( this.rows( selector, opts ) );
8324	} );
8325	
8326	
8327	_api_register( 'row().data()', function ( data ) {
8328		var ctx = this.context;
8329	
8330		if ( data === undefined ) {
8331			// Get
8332			return ctx.length && this.length ?
8333				ctx[0].aoData[ this[0] ]._aData :
8334				undefined;
8335		}
8336	
8337		// Set
8338		var row = ctx[0].aoData[ this[0] ];
8339		row._aData = data;
8340	
8341		// If the DOM has an id, and the data source is an array
8342		if ( Array.isArray( data ) && row.nTr && row.nTr.id ) {
8343			_fnSetObjectDataFn( ctx[0].rowId )( data, row.nTr.id );
8344		}
8345	
8346		// Automatically invalidate
8347		_fnInvalidate( ctx[0], this[0], 'data' );
8348	
8349		return this;
8350	} );
8351	
8352	
8353	_api_register( 'row().node()', function () {
8354		var ctx = this.context;
8355	
8356		return ctx.length && this.length ?
8357			ctx[0].aoData[ this[0] ].nTr || null :
8358			null;
8359	} );
8360	
8361	
8362	_api_register( 'row.add()', function ( row ) {
8363		// Allow a jQuery object to be passed in - only a single row is added from
8364		// it though - the first element in the set
8365		if ( row instanceof $ && row.length ) {
8366			row = row[0];
8367		}
8368	
8369		var rows = this.iterator( 'table', function ( settings ) {
8370			if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {
8371				return _fnAddTr( settings, row )[0];
8372			}
8373			return _fnAddData( settings, row );
8374		} );
8375	
8376		// Return an Api.rows() extended instance, with the newly added row selected
8377		return this.row( rows[0] );
8378	} );
8379	
8380	
8381	$(document).on('plugin-init.dt', function (e, context) {
8382		var api = new _Api( context );
8383		var namespace = 'on-plugin-init';
8384		var stateSaveParamsEvent = 'stateSaveParams.' + namespace;
8385		var destroyEvent = 'destroy. ' + namespace;
8386	
8387		api.on( stateSaveParamsEvent, function ( e, settings, d ) {
8388			// This could be more compact with the API, but it is a lot faster as a simple
8389			// internal loop
8390			var idFn = settings.rowIdFn;
8391			var data = settings.aoData;
8392			var ids = [];
8393	
8394			for (var i=0 ; i<data.length ; i++) {
8395				if (data[i]._detailsShow) {
8396					ids.push( '#' + idFn(data[i]._aData) );
8397				}
8398			}
8399	
8400			d.childRows = ids;
8401		});
8402	
8403		api.on( destroyEvent, function () {
8404			api.off(stateSaveParamsEvent + ' ' + destroyEvent);
8405		});
8406	
8407		var loaded = api.state.loaded();
8408	
8409		if ( loaded && loaded.childRows ) {
8410			api
8411				.rows( $.map(loaded.childRows, function (id){
8412					return id.replace(/:/g, '\\:')
8413				}) )
8414				.every( function () {
8415					_fnCallbackFire( context, null, 'requestChild', [ this ] )
8416				});
8417		}
8418	});
8419	
8420	var __details_add = function ( ctx, row, data, klass )
8421	{
8422		// Convert to array of TR elements
8423		var rows = [];
8424		var addRow = function ( r, k ) {
8425			// Recursion to allow for arrays of jQuery objects
8426			if ( Array.isArray( r ) || r instanceof $ ) {
8427				for ( var i=0, ien=r.length ; i<ien ; i++ ) {
8428					addRow( r[i], k );
8429				}
8430				return;
8431			}
8432	
8433			// If we get a TR element, then just add it directly - up to the dev
8434			// to add the correct number of columns etc
8435			if ( r.nodeName && r.nodeName.toLowerCase() === 'tr' ) {
8436				rows.push( r );
8437			}
8438			else {
8439				// Otherwise create a row with a wrapper
8440				var created = $('<tr><td></td></tr>').addClass( k );
8441				$('td', created)
8442					.addClass( k )
8443					.html( r )
8444					[0].colSpan = _fnVisbleColumns( ctx );
8445	
8446				rows.push( created[0] );
8447			}
8448		};
8449	
8450		addRow( data, klass );
8451	
8452		if ( row._details ) {
8453			row._details.detach();
8454		}
8455	
8456		row._details = $(rows);
8457	
8458		// If the children were already shown, that state should be retained
8459		if ( row._detailsShow ) {
8460			row._details.insertAfter( row.nTr );
8461		}
8462	};
8463	
8464	
8465	// Make state saving of child row details async to allow them to be batch processed
8466	var __details_state = DataTable.util.throttle(
8467		function (ctx) {
8468			_fnSaveState( ctx[0] )
8469		},
8470		500
8471	);
8472	
8473	
8474	var __details_remove = function ( api, idx )
8475	{
8476		var ctx = api.context;
8477	
8478		if ( ctx.length ) {
8479			var row = ctx[0].aoData[ idx !== undefined ? idx : api[0] ];
8480	
8481			if ( row && row._details ) {
8482				row._details.remove();
8483	
8484				row._detailsShow = undefined;
8485				row._details = undefined;
8486				$( row.nTr ).removeClass( 'dt-hasChild' );
8487				__details_state( ctx );
8488			}
8489		}
8490	};
8491	
8492	
8493	var __details_display = function ( api, show ) {
8494		var ctx = api.context;
8495	
8496		if ( ctx.length && api.length ) {
8497			var row = ctx[0].aoData[ api[0] ];
8498	
8499			if ( row._details ) {
8500				row._detailsShow = show;
8501	
8502				if ( show ) {
8503					row._details.insertAfter( row.nTr );
8504					$( row.nTr ).addClass( 'dt-hasChild' );
8505				}
8506				else {
8507					row._details.detach();
8508					$( row.nTr ).removeClass( 'dt-hasChild' );
8509				}
8510	
8511				_fnCallbackFire( ctx[0], null, 'childRow', [ show, api.row( api[0] ) ] )
8512	
8513				__details_events( ctx[0] );
8514				__details_state( ctx );
8515			}
8516		}
8517	};
8518	
8519	
8520	var __details_events = function ( settings )
8521	{
8522		var api = new _Api( settings );
8523		var namespace = '.dt.DT_details';
8524		var drawEvent = 'draw'+namespace;
8525		var colvisEvent = 'column-sizing'+namespace;
8526		var destroyEvent = 'destroy'+namespace;
8527		var data = settings.aoData;
8528	
8529		api.off( drawEvent +' '+ colvisEvent +' '+ destroyEvent );
8530	
8531		if ( _pluck( data, '_details' ).length > 0 ) {
8532			// On each draw, insert the required elements into the document
8533			api.on( drawEvent, function ( e, ctx ) {
8534				if ( settings !== ctx ) {
8535					return;
8536				}
8537	
8538				api.rows( {page:'current'} ).eq(0).each( function (idx) {
8539					// Internal data grab
8540					var row = data[ idx ];
8541	
8542					if ( row._detailsShow ) {
8543						row._details.insertAfter( row.nTr );
8544					}
8545				} );
8546			} );
8547	
8548			// Column visibility change - update the colspan
8549			api.on( colvisEvent, function ( e, ctx, idx, vis ) {
8550				if ( settings !== ctx ) {
8551					return;
8552				}
8553	
8554				// Update the colspan for the details rows (note, only if it already has
8555				// a colspan)
8556				var row, visible = _fnVisbleColumns( ctx );
8557	
8558				for ( var i=0, ien=data.length ; i<ien ; i++ ) {
8559					row = data[i];
8560	
8561					if ( row._details ) {
8562						row._details.children('td[colspan]').attr('colspan', visible );
8563					}
8564				}
8565			} );
8566	
8567			// Table destroyed - nuke any child rows
8568			api.on( destroyEvent, function ( e, ctx ) {
8569				if ( settings !== ctx ) {
8570					return;
8571				}
8572	
8573				for ( var i=0, ien=data.length ; i<ien ; i++ ) {
8574					if ( data[i]._details ) {
8575						__details_remove( api, i );
8576					}
8577				}
8578			} );
8579		}
8580	};
8581	
8582	// Strings for the method names to help minification
8583	var _emp = '';
8584	var _child_obj = _emp+'row().child';
8585	var _child_mth = _child_obj+'()';
8586	
8587	// data can be:
8588	//  tr
8589	//  string
8590	//  jQuery or array of any of the above
8591	_api_register( _child_mth, function ( data, klass ) {
8592		var ctx = this.context;
8593	
8594		if ( data === undefined ) {
8595			// get
8596			return ctx.length && this.length ?
8597				ctx[0].aoData[ this[0] ]._details :
8598				undefined;
8599		}
8600		else if ( data === true ) {
8601			// show
8602			this.child.show();
8603		}
8604		else if ( data === false ) {
8605			// remove
8606			__details_remove( this );
8607		}
8608		else if ( ctx.length && this.length ) {
8609			// set
8610			__details_add( ctx[0], ctx[0].aoData[ this[0] ], data, klass );
8611		}
8612	
8613		return this;
8614	} );
8615	
8616	
8617	_api_register( [
8618		_child_obj+'.show()',
8619		_child_mth+'.show()' // only when `child()` was called with parameters (without
8620	], function ( show ) {   // it returns an object and this method is not executed)
8621		__details_display( this, true );
8622		return this;
8623	} );
8624	
8625	
8626	_api_register( [
8627		_child_obj+'.hide()',
8628		_child_mth+'.hide()' // only when `child()` was called with parameters (without
8629	], function () {         // it returns an object and this method is not executed)
8630		__details_display( this, false );
8631		return this;
8632	} );
8633	
8634	
8635	_api_register( [
8636		_child_obj+'.remove()',
8637		_child_mth+'.remove()' // only when `child()` was called with parameters (without
8638	], function () {           // it returns an object and this method is not executed)
8639		__details_remove( this );
8640		return this;
8641	} );
8642	
8643	
8644	_api_register( _child_obj+'.isShown()', function () {
8645		var ctx = this.context;
8646	
8647		if ( ctx.length && this.length ) {
8648			// _detailsShown as false or undefined will fall through to return false
8649			return ctx[0].aoData[ this[0] ]._detailsShow || false;
8650		}
8651		return false;
8652	} );
8653	
8654	
8655	
8656	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8657	 * Columns
8658	 *
8659	 * {integer}           - column index (>=0 count from left, <0 count from right)
8660	 * "{integer}:visIdx"  - visible column index (i.e. translate to column index)  (>=0 count from left, <0 count from right)
8661	 * "{integer}:visible" - alias for {integer}:visIdx  (>=0 count from left, <0 count from right)
8662	 * "{string}:name"     - column name
8663	 * "{string}"          - jQuery selector on column header nodes
8664	 *
8665	 */
8666	
8667	// can be an array of these items, comma separated list, or an array of comma
8668	// separated lists
8669	
8670	var __re_column_selector = /^([^:]+):(name|visIdx|visible)$/;
8671	
8672	
8673	// r1 and r2 are redundant - but it means that the parameters match for the
8674	// iterator callback in columns().data()
8675	var __columnData = function ( settings, column, r1, r2, rows ) {
8676		var a = [];
8677		for ( var row=0, ien=rows.length ; row<ien ; row++ ) {
8678			a.push( _fnGetCellData( settings, rows[row], column ) );
8679		}
8680		return a;
8681	};
8682	
8683	
8684	var __column_selector = function ( settings, selector, opts )
8685	{
8686		var
8687			columns = settings.aoColumns,
8688			names = _pluck( columns, 'sName' ),
8689			nodes = _pluck( columns, 'nTh' );
8690	
8691		var run = function ( s ) {
8692			var selInt = _intVal( s );
8693	
8694			// Selector - all
8695			if ( s === '' ) {
8696				return _range( columns.length );
8697			}
8698	
8699			// Selector - index
8700			if ( selInt !== null ) {
8701				return [ selInt >= 0 ?
8702					selInt : // Count from left
8703					columns.length + selInt // Count from right (+ because its a negative value)
8704				];
8705			}
8706	
8707			// Selector = function
8708			if ( typeof s === 'function' ) {
8709				var rows = _selector_row_indexes( settings, opts );
8710	
8711				return $.map( columns, function (col, idx) {
8712					return s(
8713							idx,
8714							__columnData( settings, idx, 0, 0, rows ),
8715							nodes[ idx ]
8716						) ? idx : null;
8717				} );
8718			}
8719	
8720			// jQuery or string selector
8721			var match = typeof s === 'string' ?
8722				s.match( __re_column_selector ) :
8723				'';
8724	
8725			if ( match ) {
8726				switch( match[2] ) {
8727					case 'visIdx':
8728					case 'visible':
8729						var idx = parseInt( match[1], 10 );
8730						// Visible index given, convert to column index
8731						if ( idx < 0 ) {
8732							// Counting from the right
8733							var visColumns = $.map( columns, function (col,i) {
8734								return col.bVisible ? i : null;
8735							} );
8736							return [ visColumns[ visColumns.length + idx ] ];
8737						}
8738						// Counting from the left
8739						return [ _fnVisibleToColumnIndex( settings, idx ) ];
8740	
8741					case 'name':
8742						// match by name. `names` is column index complete and in order
8743						return $.map( names, function (name, i) {
8744							return name === match[1] ? i : null;
8745						} );
8746	
8747					default:
8748						return [];
8749				}
8750			}
8751	
8752			// Cell in the table body
8753			if ( s.nodeName && s._DT_CellIndex ) {
8754				return [ s._DT_CellIndex.column ];
8755			}
8756	
8757			// jQuery selector on the TH elements for the columns
8758			var jqResult = $( nodes )
8759				.filter( s )
8760				.map( function () {
8761					return $.inArray( this, nodes ); // `nodes` is column index complete and in order
8762				} )
8763				.toArray();
8764	
8765			if ( jqResult.length || ! s.nodeName ) {
8766				return jqResult;
8767			}
8768	
8769			// Otherwise a node which might have a `dt-column` data attribute, or be
8770			// a child or such an element
8771			var host = $(s).closest('*[data-dt-column]');
8772			return host.length ?
8773				[ host.data('dt-column') ] :
8774				[];
8775		};
8776	
8777		return _selector_run( 'column', selector, run, settings, opts );
8778	};
8779	
8780	
8781	var __setColumnVis = function ( settings, column, vis ) {
8782		var
8783			cols = settings.aoColumns,
8784			col  = cols[ column ],
8785			data = settings.aoData,
8786			row, cells, i, ien, tr;
8787	
8788		// Get
8789		if ( vis === undefined ) {
8790			return col.bVisible;
8791		}
8792	
8793		// Set
8794		// No change
8795		if ( col.bVisible === vis ) {
8796			return;
8797		}
8798	
8799		if ( vis ) {
8800			// Insert column
8801			// Need to decide if we should use appendChild or insertBefore
8802			var insertBefore = $.inArray( true, _pluck(cols, 'bVisible'), column+1 );
8803	
8804			for ( i=0, ien=data.length ; i<ien ; i++ ) {
8805				tr = data[i].nTr;
8806				cells = data[i].anCells;
8807	
8808				if ( tr ) {
8809					// insertBefore can act like appendChild if 2nd arg is null
8810					tr.insertBefore( cells[ column ], cells[ insertBefore ] || null );
8811				}
8812			}
8813		}
8814		else {
8815			// Remove column
8816			$( _pluck( settings.aoData, 'anCells', column ) ).detach();
8817		}
8818	
8819		// Common actions
8820		col.bVisible = vis;
8821	};
8822	
8823	
8824	_api_register( 'columns()', function ( selector, opts ) {
8825		// argument shifting
8826		if ( selector === undefined ) {
8827			selector = '';
8828		}
8829		else if ( $.isPlainObject( selector ) ) {
8830			opts = selector;
8831			selector = '';
8832		}
8833	
8834		opts = _selector_opts( opts );
8835	
8836		var inst = this.iterator( 'table', function ( settings ) {
8837			return __column_selector( settings, selector, opts );
8838		}, 1 );
8839	
8840		// Want argument shifting here and in _row_selector?
8841		inst.selector.cols = selector;
8842		inst.selector.opts = opts;
8843	
8844		return inst;
8845	} );
8846	
8847	_api_registerPlural( 'columns().header()', 'column().header()', function ( selector, opts ) {
8848		return this.iterator( 'column', function ( settings, column ) {
8849			return settings.aoColumns[column].nTh;
8850		}, 1 );
8851	} );
8852	
8853	_api_registerPlural( 'columns().footer()', 'column().footer()', function ( selector, opts ) {
8854		return this.iterator( 'column', function ( settings, column ) {
8855			return settings.aoColumns[column].nTf;
8856		}, 1 );
8857	} );
8858	
8859	_api_registerPlural( 'columns().data()', 'column().data()', function () {
8860		return this.iterator( 'column-rows', __columnData, 1 );
8861	} );
8862	
8863	_api_registerPlural( 'columns().dataSrc()', 'column().dataSrc()', function () {
8864		return this.iterator( 'column', function ( settings, column ) {
8865			return settings.aoColumns[column].mData;
8866		}, 1 );
8867	} );
8868	
8869	_api_registerPlural( 'columns().cache()', 'column().cache()', function ( type ) {
8870		return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
8871			return _pluck_order( settings.aoData, rows,
8872				type === 'search' ? '_aFilterData' : '_aSortData', column
8873			);
8874		}, 1 );
8875	} );
8876	
8877	_api_registerPlural( 'columns().nodes()', 'column().nodes()', function () {
8878		return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
8879			return _pluck_order( settings.aoData, rows, 'anCells', column ) ;
8880		}, 1 );
8881	} );
8882	
8883	_api_registerPlural( 'columns().visible()', 'column().visible()', function ( vis, calc ) {
8884		var that = this;
8885		var ret = this.iterator( 'column', function ( settings, column ) {
8886			if ( vis === undefined ) {
8887				return settings.aoColumns[ column ].bVisible;
8888			} // else
8889			__setColumnVis( settings, column, vis );
8890		} );
8891	
8892		// Group the column visibility changes
8893		if ( vis !== undefined ) {
8894			this.iterator( 'table', function ( settings ) {
8895				// Redraw the header after changes
8896				_fnDrawHead( settings, settings.aoHeader );
8897				_fnDrawHead( settings, settings.aoFooter );
8898		
8899				// Update colspan for no records display. Child rows and extensions will use their own
8900				// listeners to do this - only need to update the empty table item here
8901				if ( ! settings.aiDisplay.length ) {
8902					$(settings.nTBody).find('td[colspan]').attr('colspan', _fnVisbleColumns(settings));
8903				}
8904		
8905				_fnSaveState( settings );
8906	
8907				// Second loop once the first is done for events
8908				that.iterator( 'column', function ( settings, column ) {
8909					_fnCallbackFire( settings, null, 'column-visibility', [settings, column, vis, calc] );
8910				} );
8911	
8912				if ( calc === undefined || calc ) {
8913					that.columns.adjust();
8914				}
8915			});
8916		}
8917	
8918		return ret;
8919	} );
8920	
8921	_api_registerPlural( 'columns().indexes()', 'column().index()', function ( type ) {
8922		return this.iterator( 'column', function ( settings, column ) {
8923			return type === 'visible' ?
8924				_fnColumnIndexToVisible( settings, column ) :
8925				column;
8926		}, 1 );
8927	} );
8928	
8929	_api_register( 'columns.adjust()', function () {
8930		return this.iterator( 'table', function ( settings ) {
8931			_fnAdjustColumnSizing( settings );
8932		}, 1 );
8933	} );
8934	
8935	_api_register( 'column.index()', function ( type, idx ) {
8936		if ( this.context.length !== 0 ) {
8937			var ctx = this.context[0];
8938	
8939			if ( type === 'fromVisible' || type === 'toData' ) {
8940				return _fnVisibleToColumnIndex( ctx, idx );
8941			}
8942			else if ( type === 'fromData' || type === 'toVisible' ) {
8943				return _fnColumnIndexToVisible( ctx, idx );
8944			}
8945		}
8946	} );
8947	
8948	_api_register( 'column()', function ( selector, opts ) {
8949		return _selector_first( this.columns( selector, opts ) );
8950	} );
8951	
8952	var __cell_selector = function ( settings, selector, opts )
8953	{
8954		var data = settings.aoData;
8955		var rows = _selector_row_indexes( settings, opts );
8956		var cells = _removeEmpty( _pluck_order( data, rows, 'anCells' ) );
8957		var allCells = $(_flatten( [], cells ));
8958		var row;
8959		var columns = settings.aoColumns.length;
8960		var a, i, ien, j, o, host;
8961	
8962		var run = function ( s ) {
8963			var fnSelector = typeof s === 'function';
8964	
8965			if ( s === null || s === undefined || fnSelector ) {
8966				// All cells and function selectors
8967				a = [];
8968	
8969				for ( i=0, ien=rows.length ; i<ien ; i++ ) {
8970					row = rows[i];
8971	
8972					for ( j=0 ; j<columns ; j++ ) {
8973						o = {
8974							row: row,
8975							column: j
8976						};
8977	
8978						if ( fnSelector ) {
8979							// Selector - function
8980							host = data[ row ];
8981	
8982							if ( s( o, _fnGetCellData(settings, row, j), host.anCells ? host.anCells[j] : null ) ) {
8983								a.push( o );
8984							}
8985						}
8986						else {
8987							// Selector - all
8988							a.push( o );
8989						}
8990					}
8991				}
8992	
8993				return a;
8994			}
8995			
8996			// Selector - index
8997			if ( $.isPlainObject( s ) ) {
8998				// Valid cell index and its in the array of selectable rows
8999				return s.column !== undefined && s.row !== undefined && $.inArray( s.row, rows ) !== -1 ?
9000					[s] :
9001					[];
9002			}
9003	
9004			// Selector - jQuery filtered cells
9005			var jqResult = allCells
9006				.filter( s )
9007				.map( function (i, el) {
9008					return { // use a new object, in case someone changes the values
9009						row:    el._DT_CellIndex.row,
9010						column: el._DT_CellIndex.column
9011	 				};
9012				} )
9013				.toArray();
9014	
9015			if ( jqResult.length || ! s.nodeName ) {
9016				return jqResult;
9017			}
9018	
9019			// Otherwise the selector is a node, and there is one last option - the
9020			// element might be a child of an element which has dt-row and dt-column
9021			// data attributes
9022			host = $(s).closest('*[data-dt-row]');
9023			return host.length ?
9024				[ {
9025					row: host.data('dt-row'),
9026					column: host.data('dt-column')
9027				} ] :
9028				[];
9029		};
9030	
9031		return _selector_run( 'cell', selector, run, settings, opts );
9032	};
9033	
9034	
9035	
9036	
9037	_api_register( 'cells()', function ( rowSelector, columnSelector, opts ) {
9038		// Argument shifting
9039		if ( $.isPlainObject( rowSelector ) ) {
9040			// Indexes
9041			if ( rowSelector.row === undefined ) {
9042				// Selector options in first parameter
9043				opts = rowSelector;
9044				rowSelector = null;
9045			}
9046			else {
9047				// Cell index objects in first parameter
9048				opts = columnSelector;
9049				columnSelector = null;
9050			}
9051		}
9052		if ( $.isPlainObject( columnSelector ) ) {
9053			opts = columnSelector;
9054			columnSelector = null;
9055		}
9056	
9057		// Cell selector
9058		if ( columnSelector === null || columnSelector === undefined ) {
9059			return this.iterator( 'table', function ( settings ) {
9060				return __cell_selector( settings, rowSelector, _selector_opts( opts ) );
9061			} );
9062		}
9063	
9064		// The default built in options need to apply to row and columns
9065		var internalOpts = opts ? {
9066			page: opts.page,
9067			order: opts.order,
9068			search: opts.search
9069		} : {};
9070	
9071		// Row + column selector
9072		var columns = this.columns( columnSelector, internalOpts );
9073		var rows = this.rows( rowSelector, internalOpts );
9074		var i, ien, j, jen;
9075	
9076		var cellsNoOpts = this.iterator( 'table', function ( settings, idx ) {
9077			var a = [];
9078	
9079			for ( i=0, ien=rows[idx].length ; i<ien ; i++ ) {
9080				for ( j=0, jen=columns[idx].length ; j<jen ; j++ ) {
9081					a.push( {
9082						row:    rows[idx][i],
9083						column: columns[idx][j]
9084					} );
9085				}
9086			}
9087	
9088			return a;
9089		}, 1 );
9090	
9091		// There is currently only one extension which uses a cell selector extension
9092		// It is a _major_ performance drag to run this if it isn't needed, so this is
9093		// an extension specific check at the moment
9094		var cells = opts && opts.selected ?
9095			this.cells( cellsNoOpts, opts ) :
9096			cellsNoOpts;
9097	
9098		$.extend( cells.selector, {
9099			cols: columnSelector,
9100			rows: rowSelector,
9101			opts: opts
9102		} );
9103	
9104		return cells;
9105	} );
9106	
9107	
9108	_api_registerPlural( 'cells().nodes()', 'cell().node()', function () {
9109		return this.iterator( 'cell', function ( settings, row, column ) {
9110			var data = settings.aoData[ row ];
9111	
9112			return data && data.anCells ?
9113				data.anCells[ column ] :
9114				undefined;
9115		}, 1 );
9116	} );
9117	
9118	
9119	_api_register( 'cells().data()', function () {
9120		return this.iterator( 'cell', function ( settings, row, column ) {
9121			return _fnGetCellData( settings, row, column );
9122		}, 1 );
9123	} );
9124	
9125	
9126	_api_registerPlural( 'cells().cache()', 'cell().cache()', function ( type ) {
9127		type = type === 'search' ? '_aFilterData' : '_aSortData';
9128	
9129		return this.iterator( 'cell', function ( settings, row, column ) {
9130			return settings.aoData[ row ][ type ][ column ];
9131		}, 1 );
9132	} );
9133	
9134	
9135	_api_registerPlural( 'cells().render()', 'cell().render()', function ( type ) {
9136		return this.iterator( 'cell', function ( settings, row, column ) {
9137			return _fnGetCellData( settings, row, column, type );
9138		}, 1 );
9139	} );
9140	
9141	
9142	_api_registerPlural( 'cells().indexes()', 'cell().index()', function () {
9143		return this.iterator( 'cell', function ( settings, row, column ) {
9144			return {
9145				row: row,
9146				column: column,
9147				columnVisible: _fnColumnIndexToVisible( settings, column )
9148			};
9149		}, 1 );
9150	} );
9151	
9152	
9153	_api_registerPlural( 'cells().invalidate()', 'cell().invalidate()', function ( src ) {
9154		return this.iterator( 'cell', function ( settings, row, column ) {
9155			_fnInvalidate( settings, row, src, column );
9156		} );
9157	} );
9158	
9159	
9160	
9161	_api_register( 'cell()', function ( rowSelector, columnSelector, opts ) {
9162		return _selector_first( this.cells( rowSelector, columnSelector, opts ) );
9163	} );
9164	
9165	
9166	_api_register( 'cell().data()', function ( data ) {
9167		var ctx = this.context;
9168		var cell = this[0];
9169	
9170		if ( data === undefined ) {
9171			// Get
9172			return ctx.length && cell.length ?
9173				_fnGetCellData( ctx[0], cell[0].row, cell[0].column ) :
9174				undefined;
9175		}
9176	
9177		// Set
9178		_fnSetCellData( ctx[0], cell[0].row, cell[0].column, data );
9179		_fnInvalidate( ctx[0], cell[0].row, 'data', cell[0].column );
9180	
9181		return this;
9182	} );
9183	
9184	
9185	
9186	/**
9187	 * Get current ordering (sorting) that has been applied to the table.
9188	 *
9189	 * @returns {array} 2D array containing the sorting information for the first
9190	 *   table in the current context. Each element in the parent array represents
9191	 *   a column being sorted upon (i.e. multi-sorting with two columns would have
9192	 *   2 inner arrays). The inner arrays may have 2 or 3 elements. The first is
9193	 *   the column index that the sorting condition applies to, the second is the
9194	 *   direction of the sort (`desc` or `asc`) and, optionally, the third is the
9195	 *   index of the sorting order from the `column.sorting` initialisation array.
9196	 *//**
9197	 * Set the ordering for the table.
9198	 *
9199	 * @param {integer} order Column index to sort upon.
9200	 * @param {string} direction Direction of the sort to be applied (`asc` or `desc`)
9201	 * @returns {DataTables.Api} this
9202	 *//**
9203	 * Set the ordering for the table.
9204	 *
9205	 * @param {array} order 1D array of sorting information to be applied.
9206	 * @param {array} [...] Optional additional sorting conditions
9207	 * @returns {DataTables.Api} this
9208	 *//**
9209	 * Set the ordering for the table.
9210	 *
9211	 * @param {array} order 2D array of sorting information to be applied.
9212	 * @returns {DataTables.Api} this
9213	 */
9214	_api_register( 'order()', function ( order, dir ) {
9215		var ctx = this.context;
9216	
9217		if ( order === undefined ) {
9218			// get
9219			return ctx.length !== 0 ?
9220				ctx[0].aaSorting :
9221				undefined;
9222		}
9223	
9224		// set
9225		if ( typeof order === 'number' ) {
9226			// Simple column / direction passed in
9227			order = [ [ order, dir ] ];
9228		}
9229		else if ( order.length && ! Array.isArray( order[0] ) ) {
9230			// Arguments passed in (list of 1D arrays)
9231			order = Array.prototype.slice.call( arguments );
9232		}
9233		// otherwise a 2D array was passed in
9234	
9235		return this.iterator( 'table', function ( settings ) {
9236			settings.aaSorting = order.slice();
9237		} );
9238	} );
9239	
9240	
9241	/**
9242	 * Attach a sort listener to an element for a given column
9243	 *
9244	 * @param {node|jQuery|string} node Identifier for the element(s) to attach the
9245	 *   listener to. This can take the form of a single DOM node, a jQuery
9246	 *   collection of nodes or a jQuery selector which will identify the node(s).
9247	 * @param {integer} column the column that a click on this node will sort on
9248	 * @param {function} [callback] callback function when sort is run
9249	 * @returns {DataTables.Api} this
9250	 */
9251	_api_register( 'order.listener()', function ( node, column, callback ) {
9252		return this.iterator( 'table', function ( settings ) {
9253			_fnSortAttachListener( settings, node, column, callback );
9254		} );
9255	} );
9256	
9257	
9258	_api_register( 'order.fixed()', function ( set ) {
9259		if ( ! set ) {
9260			var ctx = this.context;
9261			var fixed = ctx.length ?
9262				ctx[0].aaSortingFixed :
9263				undefined;
9264	
9265			return Array.isArray( fixed ) ?
9266				{ pre: fixed } :
9267				fixed;
9268		}
9269	
9270		return this.iterator( 'table', function ( settings ) {
9271			settings.aaSortingFixed = $.extend( true, {}, set );
9272		} );
9273	} );
9274	
9275	
9276	// Order by the selected column(s)
9277	_api_register( [
9278		'columns().order()',
9279		'column().order()'
9280	], function ( dir ) {
9281		var that = this;
9282	
9283		return this.iterator( 'table', function ( settings, i ) {
9284			var sort = [];
9285	
9286			$.each( that[i], function (j, col) {
9287				sort.push( [ col, dir ] );
9288			} );
9289	
9290			settings.aaSorting = sort;
9291		} );
9292	} );
9293	
9294	
9295	
9296	_api_register( 'search()', function ( input, regex, smart, caseInsen ) {
9297		var ctx = this.context;
9298	
9299		if ( input === undefined ) {
9300			// get
9301			return ctx.length !== 0 ?
9302				ctx[0].oPreviousSearch.sSearch :
9303				undefined;
9304		}
9305	
9306		// set
9307		return this.iterator( 'table', function ( settings ) {
9308			if ( ! settings.oFeatures.bFilter ) {
9309				return;
9310			}
9311	
9312			_fnFilterComplete( settings, $.extend( {}, settings.oPreviousSearch, {
9313				"sSearch": input+"",
9314				"bRegex":  regex === null ? false : regex,
9315				"bSmart":  smart === null ? true  : smart,
9316				"bCaseInsensitive": caseInsen === null ? true : caseInsen
9317			} ), 1 );
9318		} );
9319	} );
9320	
9321	
9322	_api_registerPlural(
9323		'columns().search()',
9324		'column().search()',
9325		function ( input, regex, smart, caseInsen ) {
9326			return this.iterator( 'column', function ( settings, column ) {
9327				var preSearch = settings.aoPreSearchCols;
9328	
9329				if ( input === undefined ) {
9330					// get
9331					return preSearch[ column ].sSearch;
9332				}
9333	
9334				// set
9335				if ( ! settings.oFeatures.bFilter ) {
9336					return;
9337				}
9338	
9339				$.extend( preSearch[ column ], {
9340					"sSearch": input+"",
9341					"bRegex":  regex === null ? false : regex,
9342					"bSmart":  smart === null ? true  : smart,
9343					"bCaseInsensitive": caseInsen === null ? true : caseInsen
9344				} );
9345	
9346				_fnFilterComplete( settings, settings.oPreviousSearch, 1 );
9347			} );
9348		}
9349	);
9350	
9351	/*
9352	 * State API methods
9353	 */
9354	
9355	_api_register( 'state()', function () {
9356		return this.context.length ?
9357			this.context[0].oSavedState :
9358			null;
9359	} );
9360	
9361	
9362	_api_register( 'state.clear()', function () {
9363		return this.iterator( 'table', function ( settings ) {
9364			// Save an empty object
9365			settings.fnStateSaveCallback.call( settings.oInstance, settings, {} );
9366		} );
9367	} );
9368	
9369	
9370	_api_register( 'state.loaded()', function () {
9371		return this.context.length ?
9372			this.context[0].oLoadedState :
9373			null;
9374	} );
9375	
9376	
9377	_api_register( 'state.save()', function () {
9378		return this.iterator( 'table', function ( settings ) {
9379			_fnSaveState( settings );
9380		} );
9381	} );
9382	
9383	
9384	
9385	/**
9386	 * Set the jQuery or window object to be used by DataTables
9387	 *
9388	 * @param {*} module Library / container object
9389	 * @param {string} type Library or container type `lib` or `win`.
9390	 */
9391	DataTable.use = function (module, type) {
9392		if (type === 'lib' || module.fn) {
9393			$ = module;
9394		}
9395		else if (type == 'win' || module.document) {
9396			window = module;
9397			document = module.document;
9398		}
9399	}
9400	
9401	/**
9402	 * CommonJS factory function pass through. This will check if the arguments
9403	 * given are a window object or a jQuery object. If so they are set
9404	 * accordingly.
9405	 * @param {*} root Window
9406	 * @param {*} jq jQUery
9407	 * @returns {boolean} Indicator
9408	 */
9409	DataTable.factory = function (root, jq) {
9410		var is = false;
9411	
9412		// Test if the first parameter is a window object
9413		if (root && root.document) {
9414			window = root;
9415			document = root.document;
9416		}
9417	
9418		// Test if the second parameter is a jQuery object
9419		if (jq && jq.fn && jq.fn.jquery) {
9420			$ = jq;
9421			is = true;
9422		}
9423	
9424		return is;
9425	}
9426	
9427	/**
9428	 * Provide a common method for plug-ins to check the version of DataTables being
9429	 * used, in order to ensure compatibility.
9430	 *
9431	 *  @param {string} version Version string to check for, in the format "X.Y.Z".
9432	 *    Note that the formats "X" and "X.Y" are also acceptable.
9433	 *  @returns {boolean} true if this version of DataTables is greater or equal to
9434	 *    the required version, or false if this version of DataTales is not
9435	 *    suitable
9436	 *  @static
9437	 *  @dtopt API-Static
9438	 *
9439	 *  @example
9440	 *    alert( $.fn.dataTable.versionCheck( '1.9.0' ) );
9441	 */
9442	DataTable.versionCheck = DataTable.fnVersionCheck = function( version )
9443	{
9444		var aThis = DataTable.version.split('.');
9445		var aThat = version.split('.');
9446		var iThis, iThat;
9447	
9448		for ( var i=0, iLen=aThat.length ; i<iLen ; i++ ) {
9449			iThis = parseInt( aThis[i], 10 ) || 0;
9450			iThat = parseInt( aThat[i], 10 ) || 0;
9451	
9452			// Parts are the same, keep comparing
9453			if (iThis === iThat) {
9454				continue;
9455			}
9456	
9457			// Parts are different, return immediately
9458			return iThis > iThat;
9459		}
9460	
9461		return true;
9462	};
9463	
9464	
9465	/**
9466	 * Check if a `<table>` node is a DataTable table already or not.
9467	 *
9468	 *  @param {node|jquery|string} table Table node, jQuery object or jQuery
9469	 *      selector for the table to test. Note that if more than more than one
9470	 *      table is passed on, only the first will be checked
9471	 *  @returns {boolean} true the table given is a DataTable, or false otherwise
9472	 *  @static
9473	 *  @dtopt API-Static
9474	 *
9475	 *  @example
9476	 *    if ( ! $.fn.DataTable.isDataTable( '#example' ) ) {
9477	 *      $('#example').dataTable();
9478	 *    }
9479	 */
9480	DataTable.isDataTable = DataTable.fnIsDataTable = function ( table )
9481	{
9482		var t = $(table).get(0);
9483		var is = false;
9484	
9485		if ( table instanceof DataTable.Api ) {
9486			return true;
9487		}
9488	
9489		$.each( DataTable.settings, function (i, o) {
9490			var head = o.nScrollHead ? $('table', o.nScrollHead)[0] : null;
9491			var foot = o.nScrollFoot ? $('table', o.nScrollFoot)[0] : null;
9492	
9493			if ( o.nTable === t || head === t || foot === t ) {
9494				is = true;
9495			}
9496		} );
9497	
9498		return is;
9499	};
9500	
9501	
9502	/**
9503	 * Get all DataTable tables that have been initialised - optionally you can
9504	 * select to get only currently visible tables.
9505	 *
9506	 *  @param {boolean} [visible=false] Flag to indicate if you want all (default)
9507	 *    or visible tables only.
9508	 *  @returns {array} Array of `table` nodes (not DataTable instances) which are
9509	 *    DataTables
9510	 *  @static
9511	 *  @dtopt API-Static
9512	 *
9513	 *  @example
9514	 *    $.each( $.fn.dataTable.tables(true), function () {
9515	 *      $(table).DataTable().columns.adjust();
9516	 *    } );
9517	 */
9518	DataTable.tables = DataTable.fnTables = function ( visible )
9519	{
9520		var api = false;
9521	
9522		if ( $.isPlainObject( visible ) ) {
9523			api = visible.api;
9524			visible = visible.visible;
9525		}
9526	
9527		var a = $.map( DataTable.settings, function (o) {
9528			if ( !visible || (visible && $(o.nTable).is(':visible')) ) {
9529				return o.nTable;
9530			}
9531		} );
9532	
9533		return api ?
9534			new _Api( a ) :
9535			a;
9536	};
9537	
9538	
9539	/**
9540	 * Convert from camel case parameters to Hungarian notation. This is made public
9541	 * for the extensions to provide the same ability as DataTables core to accept
9542	 * either the 1.9 style Hungarian notation, or the 1.10+ style camelCase
9543	 * parameters.
9544	 *
9545	 *  @param {object} src The model object which holds all parameters that can be
9546	 *    mapped.
9547	 *  @param {object} user The object to convert from camel case to Hungarian.
9548	 *  @param {boolean} force When set to `true`, properties which already have a
9549	 *    Hungarian value in the `user` object will be overwritten. Otherwise they
9550	 *    won't be.
9551	 */
9552	DataTable.camelToHungarian = _fnCamelToHungarian;
9553	
9554	
9555	
9556	/**
9557	 *
9558	 */
9559	_api_register( '$()', function ( selector, opts ) {
9560		var
9561			rows   = this.rows( opts ).nodes(), // Get all rows
9562			jqRows = $(rows);
9563	
9564		return $( [].concat(
9565			jqRows.filter( selector ).toArray(),
9566			jqRows.find( selector ).toArray()
9567		) );
9568	} );
9569	
9570	
9571	// jQuery functions to operate on the tables
9572	$.each( [ 'on', 'one', 'off' ], function (i, key) {
9573		_api_register( key+'()', function ( /* event, handler */ ) {
9574			var args = Array.prototype.slice.call(arguments);
9575	
9576			// Add the `dt` namespace automatically if it isn't already present
9577			args[0] = $.map( args[0].split( /\s/ ), function ( e ) {
9578				return ! e.match(/\.dt\b/) ?
9579					e+'.dt' :
9580					e;
9581				} ).join( ' ' );
9582	
9583			var inst = $( this.tables().nodes() );
9584			inst[key].apply( inst, args );
9585			return this;
9586		} );
9587	} );
9588	
9589	
9590	_api_register( 'clear()', function () {
9591		return this.iterator( 'table', function ( settings ) {
9592			_fnClearTable( settings );
9593		} );
9594	} );
9595	
9596	
9597	_api_register( 'settings()', function () {
9598		return new _Api( this.context, this.context );
9599	} );
9600	
9601	
9602	_api_register( 'init()', function () {
9603		var ctx = this.context;
9604		return ctx.length ? ctx[0].oInit : null;
9605	} );
9606	
9607	
9608	_api_register( 'data()', function () {
9609		return this.iterator( 'table', function ( settings ) {
9610			return _pluck( settings.aoData, '_aData' );
9611		} ).flatten();
9612	} );
9613	
9614	
9615	_api_register( 'destroy()', function ( remove ) {
9616		remove = remove || false;
9617	
9618		return this.iterator( 'table', function ( settings ) {
9619			var classes   = settings.oClasses;
9620			var table     = settings.nTable;
9621			var tbody     = settings.nTBody;
9622			var thead     = settings.nTHead;
9623			var tfoot     = settings.nTFoot;
9624			var jqTable   = $(table);
9625			var jqTbody   = $(tbody);
9626			var jqWrapper = $(settings.nTableWrapper);
9627			var rows      = $.map( settings.aoData, function (r) { return r.nTr; } );
9628			var i, ien;
9629	
9630			// Flag to note that the table is currently being destroyed - no action
9631			// should be taken
9632			settings.bDestroying = true;
9633	
9634			// Fire off the destroy callbacks for plug-ins etc
9635			_fnCallbackFire( settings, "aoDestroyCallback", "destroy", [settings] );
9636	
9637			// If not being removed from the document, make all columns visible
9638			if ( ! remove ) {
9639				new _Api( settings ).columns().visible( true );
9640			}
9641	
9642			// Blitz all `DT` namespaced events (these are internal events, the
9643			// lowercase, `dt` events are user subscribed and they are responsible
9644			// for removing them
9645			jqWrapper.off('.DT').find(':not(tbody *)').off('.DT');
9646			$(window).off('.DT-'+settings.sInstance);
9647	
9648			// When scrolling we had to break the table up - restore it
9649			if ( table != thead.parentNode ) {
9650				jqTable.children('thead').detach();
9651				jqTable.append( thead );
9652			}
9653	
9654			if ( tfoot && table != tfoot.parentNode ) {
9655				jqTable.children('tfoot').detach();
9656				jqTable.append( tfoot );
9657			}
9658	
9659			settings.aaSorting = [];
9660			settings.aaSortingFixed = [];
9661			_fnSortingClasses( settings );
9662	
9663			$( rows ).removeClass( settings.asStripeClasses.join(' ') );
9664	
9665			$('th, td', thead).removeClass( classes.sSortable+' '+
9666				classes.sSortableAsc+' '+classes.sSortableDesc+' '+classes.sSortableNone
9667			);
9668	
9669			// Add the TR elements back into the table in their original order
9670			jqTbody.children().detach();
9671			jqTbody.append( rows );
9672	
9673			var orig = settings.nTableWrapper.parentNode;
9674	
9675			// Remove the DataTables generated nodes, events and classes
9676			var removedMethod = remove ? 'remove' : 'detach';
9677			jqTable[ removedMethod ]();
9678			jqWrapper[ removedMethod ]();
9679	
9680			// If we need to reattach the table to the document
9681			if ( ! remove && orig ) {
9682				// insertBefore acts like appendChild if !arg[1]
9683				orig.insertBefore( table, settings.nTableReinsertBefore );
9684	
9685				// Restore the width of the original table - was read from the style property,
9686				// so we can restore directly to that
9687				jqTable
9688					.css( 'width', settings.sDestroyWidth )
9689					.removeClass( classes.sTable );
9690	
9691				// If the were originally stripe classes - then we add them back here.
9692				// Note this is not fool proof (for example if not all rows had stripe
9693				// classes - but it's a good effort without getting carried away
9694				ien = settings.asDestroyStripes.length;
9695	
9696				if ( ien ) {
9697					jqTbody.children().each( function (i) {
9698						$(this).addClass( settings.asDestroyStripes[i % ien] );
9699					} );
9700				}
9701			}
9702	
9703			/* Remove the settings object from the settings array */
9704			var idx = $.inArray( settings, DataTable.settings );
9705			if ( idx !== -1 ) {
9706				DataTable.settings.splice( idx, 1 );
9707			}
9708		} );
9709	} );
9710	
9711	
9712	// Add the `every()` method for rows, columns and cells in a compact form
9713	$.each( [ 'column', 'row', 'cell' ], function ( i, type ) {
9714		_api_register( type+'s().every()', function ( fn ) {
9715			var opts = this.selector.opts;
9716			var api = this;
9717	
9718			return this.iterator( type, function ( settings, arg1, arg2, arg3, arg4 ) {
9719				// Rows and columns:
9720				//  arg1 - index
9721				//  arg2 - table counter
9722				//  arg3 - loop counter
9723				//  arg4 - undefined
9724				// Cells:
9725				//  arg1 - row index
9726				//  arg2 - column index
9727				//  arg3 - table counter
9728				//  arg4 - loop counter
9729				fn.call(
9730					api[ type ](
9731						arg1,
9732						type==='cell' ? arg2 : opts,
9733						type==='cell' ? opts : undefined
9734					),
9735					arg1, arg2, arg3, arg4
9736				);
9737			} );
9738		} );
9739	} );
9740	
9741	
9742	// i18n method for extensions to be able to use the language object from the
9743	// DataTable
9744	_api_register( 'i18n()', function ( token, def, plural ) {
9745		var ctx = this.context[0];
9746		var resolved = _fnGetObjectDataFn( token )( ctx.oLanguage );
9747	
9748		if ( resolved === undefined ) {
9749			resolved = def;
9750		}
9751	
9752		if ( plural !== undefined && $.isPlainObject( resolved ) ) {
9753			resolved = resolved[ plural ] !== undefined ?
9754				resolved[ plural ] :
9755				resolved._;
9756		}
9757	
9758		return resolved.replace( '%d', plural ); // nb: plural might be undefined,
9759	} );	
9760	/**
9761	 * Version string for plug-ins to check compatibility. Allowed format is
9762	 * `a.b.c-d` where: a:int, b:int, c:int, d:string(dev|beta|alpha). `d` is used
9763	 * only for non-release builds. See http://semver.org/ for more information.
9764	 *  @member
9765	 *  @type string
9766	 *  @default Version number
9767	 */
9768	DataTable.version = "1.13.4";
9769	
9770	/**
9771	 * Private data store, containing all of the settings objects that are
9772	 * created for the tables on a given page.
9773	 *
9774	 * Note that the `DataTable.settings` object is aliased to
9775	 * `jQuery.fn.dataTableExt` through which it may be accessed and
9776	 * manipulated, or `jQuery.fn.dataTable.settings`.
9777	 *  @member
9778	 *  @type array
9779	 *  @default []
9780	 *  @private
9781	 */
9782	DataTable.settings = [];
9783	
9784	/**
9785	 * Object models container, for the various models that DataTables has
9786	 * available to it. These models define the objects that are used to hold
9787	 * the active state and configuration of the table.
9788	 *  @namespace
9789	 */
9790	DataTable.models = {};
9791	
9792	
9793	
9794	/**
9795	 * Template object for the way in which DataTables holds information about
9796	 * search information for the global filter and individual column filters.
9797	 *  @namespace
9798	 */
9799	DataTable.models.oSearch = {
9800		/**
9801		 * Flag to indicate if the filtering should be case insensitive or not
9802		 *  @type boolean
9803		 *  @default true
9804		 */
9805		"bCaseInsensitive": true,
9806	
9807		/**
9808		 * Applied search term
9809		 *  @type string
9810		 *  @default <i>Empty string</i>
9811		 */
9812		"sSearch": "",
9813	
9814		/**
9815		 * Flag to indicate if the search term should be interpreted as a
9816		 * regular expression (true) or not (false) and therefore and special
9817		 * regex characters escaped.
9818		 *  @type boolean
9819		 *  @default false
9820		 */
9821		"bRegex": false,
9822	
9823		/**
9824		 * Flag to indicate if DataTables is to use its smart filtering or not.
9825		 *  @type boolean
9826		 *  @default true
9827		 */
9828		"bSmart": true,
9829	
9830		/**
9831		 * Flag to indicate if DataTables should only trigger a search when
9832		 * the return key is pressed.
9833		 *  @type boolean
9834		 *  @default false
9835		 */
9836		"return": false
9837	};
9838	
9839	
9840	
9841	
9842	/**
9843	 * Template object for the way in which DataTables holds information about
9844	 * each individual row. This is the object format used for the settings
9845	 * aoData array.
9846	 *  @namespace
9847	 */
9848	DataTable.models.oRow = {
9849		/**
9850		 * TR element for the row
9851		 *  @type node
9852		 *  @default null
9853		 */
9854		"nTr": null,
9855	
9856		/**
9857		 * Array of TD elements for each row. This is null until the row has been
9858		 * created.
9859		 *  @type array nodes
9860		 *  @default []
9861		 */
9862		"anCells": null,
9863	
9864		/**
9865		 * Data object from the original data source for the row. This is either
9866		 * an array if using the traditional form of DataTables, or an object if
9867		 * using mData options. The exact type will depend on the passed in
9868		 * data from the data source, or will be an array if using DOM a data
9869		 * source.
9870		 *  @type array|object
9871		 *  @default []
9872		 */
9873		"_aData": [],
9874	
9875		/**
9876		 * Sorting data cache - this array is ostensibly the same length as the
9877		 * number of columns (although each index is generated only as it is
9878		 * needed), and holds the data that is used for sorting each column in the
9879		 * row. We do this cache generation at the start of the sort in order that
9880		 * the formatting of the sort data need be done only once for each cell
9881		 * per sort. This array should not be read from or written to by anything
9882		 * other than the master sorting methods.
9883		 *  @type array
9884		 *  @default null
9885		 *  @private
9886		 */
9887		"_aSortData": null,
9888	
9889		/**
9890		 * Per cell filtering data cache. As per the sort data cache, used to
9891		 * increase the performance of the filtering in DataTables
9892		 *  @type array
9893		 *  @default null
9894		 *  @private
9895		 */
9896		"_aFilterData": null,
9897	
9898		/**
9899		 * Filtering data cache. This is the same as the cell filtering cache, but
9900		 * in this case a string rather than an array. This is easily computed with
9901		 * a join on `_aFilterData`, but is provided as a cache so the join isn't
9902		 * needed on every search (memory traded for performance)
9903		 *  @type array
9904		 *  @default null
9905		 *  @private
9906		 */
9907		"_sFilterRow": null,
9908	
9909		/**
9910		 * Cache of the class name that DataTables has applied to the row, so we
9911		 * can quickly look at this variable rather than needing to do a DOM check
9912		 * on className for the nTr property.
9913		 *  @type string
9914		 *  @default <i>Empty string</i>
9915		 *  @private
9916		 */
9917		"_sRowStripe": "",
9918	
9919		/**
9920		 * Denote if the original data source was from the DOM, or the data source
9921		 * object. This is used for invalidating data, so DataTables can
9922		 * automatically read data from the original source, unless uninstructed
9923		 * otherwise.
9924		 *  @type string
9925		 *  @default null
9926		 *  @private
9927		 */
9928		"src": null,
9929	
9930		/**
9931		 * Index in the aoData array. This saves an indexOf lookup when we have the
9932		 * object, but want to know the index
9933		 *  @type integer
9934		 *  @default -1
9935		 *  @private
9936		 */
9937		"idx": -1
9938	};
9939	
9940	
9941	/**
9942	 * Template object for the column information object in DataTables. This object
9943	 * is held in the settings aoColumns array and contains all the information that
9944	 * DataTables needs about each individual column.
9945	 *
9946	 * Note that this object is related to {@link DataTable.defaults.column}
9947	 * but this one is the internal data store for DataTables's cache of columns.
9948	 * It should NOT be manipulated outside of DataTables. Any configuration should
9949	 * be done through the initialisation options.
9950	 *  @namespace
9951	 */
9952	DataTable.models.oColumn = {
9953		/**
9954		 * Column index. This could be worked out on-the-fly with $.inArray, but it
9955		 * is faster to just hold it as a variable
9956		 *  @type integer
9957		 *  @default null
9958		 */
9959		"idx": null,
9960	
9961		/**
9962		 * A list of the columns that sorting should occur on when this column
9963		 * is sorted. That this property is an array allows multi-column sorting
9964		 * to be defined for a column (for example first name / last name columns
9965		 * would benefit from this). The values are integers pointing to the
9966		 * columns to be sorted on (typically it will be a single integer pointing
9967		 * at itself, but that doesn't need to be the case).
9968		 *  @type array
9969		 */
9970		"aDataSort": null,
9971	
9972		/**
9973		 * Define the sorting directions that are applied to the column, in sequence
9974		 * as the column is repeatedly sorted upon - i.e. the first value is used
9975		 * as the sorting direction when the column if first sorted (clicked on).
9976		 * Sort it again (click again) and it will move on to the next index.
9977		 * Repeat until loop.
9978		 *  @type array
9979		 */
9980		"asSorting": null,
9981	
9982		/**
9983		 * Flag to indicate if the column is searchable, and thus should be included
9984		 * in the filtering or not.
9985		 *  @type boolean
9986		 */
9987		"bSearchable": null,
9988	
9989		/**
9990		 * Flag to indicate if the column is sortable or not.
9991		 *  @type boolean
9992		 */
9993		"bSortable": null,
9994	
9995		/**
9996		 * Flag to indicate if the column is currently visible in the table or not
9997		 *  @type boolean
9998		 */
9999		"bVisible": null,
10000	
10001		/**
10002		 * Store for manual type assignment using the `column.type` option. This
10003		 * is held in store so we can manipulate the column's `sType` property.
10004		 *  @type string
10005		 *  @default null
10006		 *  @private
10007		 */
10008		"_sManualType": null,
10009	
10010		/**
10011		 * Flag to indicate if HTML5 data attributes should be used as the data
10012		 * source for filtering or sorting. True is either are.
10013		 *  @type boolean
10014		 *  @default false
10015		 *  @private
10016		 */
10017		"_bAttrSrc": false,
10018	
10019		/**
10020		 * Developer definable function that is called whenever a cell is created (Ajax source,
10021		 * etc) or processed for input (DOM source). This can be used as a compliment to mRender
10022		 * allowing you to modify the DOM element (add background colour for example) when the
10023		 * element is available.
10024		 *  @type function
10025		 *  @param {element} nTd The TD node that has been created
10026		 *  @param {*} sData The Data for the cell
10027		 *  @param {array|object} oData The data for the whole row
10028		 *  @param {int} iRow The row index for the aoData data store
10029		 *  @default null
10030		 */
10031		"fnCreatedCell": null,
10032	
10033		/**
10034		 * Function to get data from a cell in a column. You should <b>never</b>
10035		 * access data directly through _aData internally in DataTables - always use
10036		 * the method attached to this property. It allows mData to function as
10037		 * required. This function is automatically assigned by the column
10038		 * initialisation method
10039		 *  @type function
10040		 *  @param {array|object} oData The data array/object for the array
10041		 *    (i.e. aoData[]._aData)
10042		 *  @param {string} sSpecific The specific data type you want to get -
10043		 *    'display', 'type' 'filter' 'sort'
10044		 *  @returns {*} The data for the cell from the given row's data
10045		 *  @default null
10046		 */
10047		"fnGetData": null,
10048	
10049		/**
10050		 * Function to set data for a cell in the column. You should <b>never</b>
10051		 * set the data directly to _aData internally in DataTables - always use
10052		 * this method. It allows mData to function as required. This function
10053		 * is automatically assigned by the column initialisation method
10054		 *  @type function
10055		 *  @param {array|object} oData The data array/object for the array
10056		 *    (i.e. aoData[]._aData)
10057		 *  @param {*} sValue Value to set
10058		 *  @default null
10059		 */
10060		"fnSetData": null,
10061	
10062		/**
10063		 * Property to read the value for the cells in the column from the data
10064		 * source array / object. If null, then the default content is used, if a
10065		 * function is given then the return from the function is used.
10066		 *  @type function|int|string|null
10067		 *  @default null
10068		 */
10069		"mData": null,
10070	
10071		/**
10072		 * Partner property to mData which is used (only when defined) to get
10073		 * the data - i.e. it is basically the same as mData, but without the
10074		 * 'set' option, and also the data fed to it is the result from mData.
10075		 * This is the rendering method to match the data method of mData.
10076		 *  @type function|int|string|null
10077		 *  @default null
10078		 */
10079		"mRender": null,
10080	
10081		/**
10082		 * Unique header TH/TD element for this column - this is what the sorting
10083		 * listener is attached to (if sorting is enabled.)
10084		 *  @type node
10085		 *  @default null
10086		 */
10087		"nTh": null,
10088	
10089		/**
10090		 * Unique footer TH/TD element for this column (if there is one). Not used
10091		 * in DataTables as such, but can be used for plug-ins to reference the
10092		 * footer for each column.
10093		 *  @type node
10094		 *  @default null
10095		 */
10096		"nTf": null,
10097	
10098		/**
10099		 * The class to apply to all TD elements in the table's TBODY for the column
10100		 *  @type string
10101		 *  @default null
10102		 */
10103		"sClass": null,
10104	
10105		/**
10106		 * When DataTables calculates the column widths to assign to each column,
10107		 * it finds the longest string in each column and then constructs a
10108		 * temporary table and reads the widths from that. The problem with this
10109		 * is that "mmm" is much wider then "iiii", but the latter is a longer
10110		 * string - thus the calculation can go wrong (doing it properly and putting
10111		 * it into an DOM object and measuring that is horribly(!) slow). Thus as
10112		 * a "work around" we provide this option. It will append its value to the
10113		 * text that is found to be the longest string for the column - i.e. padding.
10114		 *  @type string
10115		 */
10116		"sContentPadding": null,
10117	
10118		/**
10119		 * Allows a default value to be given for a column's data, and will be used
10120		 * whenever a null data source is encountered (this can be because mData
10121		 * is set to null, or because the data source itself is null).
10122		 *  @type string
10123		 *  @default null
10124		 */
10125		"sDefaultContent": null,
10126	
10127		/**
10128		 * Name for the column, allowing reference to the column by name as well as
10129		 * by index (needs a lookup to work by name).
10130		 *  @type string
10131		 */
10132		"sName": null,
10133	
10134		/**
10135		 * Custom sorting data type - defines which of the available plug-ins in
10136		 * afnSortData the custom sorting will use - if any is defined.
10137		 *  @type string
10138		 *  @default std
10139		 */
10140		"sSortDataType": 'std',
10141	
10142		/**
10143		 * Class to be applied to the header element when sorting on this column
10144		 *  @type string
10145		 *  @default null
10146		 */
10147		"sSortingClass": null,
10148	
10149		/**
10150		 * Class to be applied to the header element when sorting on this column -
10151		 * when jQuery UI theming is used.
10152		 *  @type string
10153		 *  @default null
10154		 */
10155		"sSortingClassJUI": null,
10156	
10157		/**
10158		 * Title of the column - what is seen in the TH element (nTh).
10159		 *  @type string
10160		 */
10161		"sTitle": null,
10162	
10163		/**
10164		 * Column sorting and filtering type
10165		 *  @type string
10166		 *  @default null
10167		 */
10168		"sType": null,
10169	
10170		/**
10171		 * Width of the column
10172		 *  @type string
10173		 *  @default null
10174		 */
10175		"sWidth": null,
10176	
10177		/**
10178		 * Width of the column when it was first "encountered"
10179		 *  @type string
10180		 *  @default null
10181		 */
10182		"sWidthOrig": null
10183	};
10184	
10185	
10186	/*
10187	 * Developer note: The properties of the object below are given in Hungarian
10188	 * notation, that was used as the interface for DataTables prior to v1.10, however
10189	 * from v1.10 onwards the primary interface is camel case. In order to avoid
10190	 * breaking backwards compatibility utterly with this change, the Hungarian
10191	 * version is still, internally the primary interface, but is is not documented
10192	 * - hence the @name tags in each doc comment. This allows a Javascript function
10193	 * to create a map from Hungarian notation to camel case (going the other direction
10194	 * would require each property to be listed, which would add around 3K to the size
10195	 * of DataTables, while this method is about a 0.5K hit).
10196	 *
10197	 * Ultimately this does pave the way for Hungarian notation to be dropped
10198	 * completely, but that is a massive amount of work and will break current
10199	 * installs (therefore is on-hold until v2).
10200	 */
10201	
10202	/**
10203	 * Initialisation options that can be given to DataTables at initialisation
10204	 * time.
10205	 *  @namespace
10206	 */
10207	DataTable.defaults = {
10208		/**
10209		 * An array of data to use for the table, passed in at initialisation which
10210		 * will be used in preference to any data which is already in the DOM. This is
10211		 * particularly useful for constructing tables purely in Javascript, for
10212		 * example with a custom Ajax call.
10213		 *  @type array
10214		 *  @default null
10215		 *
10216		 *  @dtopt Option
10217		 *  @name DataTable.defaults.data
10218		 *
10219		 *  @example
10220		 *    // Using a 2D array data source
10221		 *    $(document).ready( function () {
10222		 *      $('#example').dataTable( {
10223		 *        "data": [
10224		 *          ['Trident', 'Internet Explorer 4.0', 'Win 95+', 4, 'X'],
10225		 *          ['Trident', 'Internet Explorer 5.0', 'Win 95+', 5, 'C'],
10226		 *        ],
10227		 *        "columns": [
10228		 *          { "title": "Engine" },
10229		 *          { "title": "Browser" },
10230		 *          { "title": "Platform" },
10231		 *          { "title": "Version" },
10232		 *          { "title": "Grade" }
10233		 *        ]
10234		 *      } );
10235		 *    } );
10236		 *
10237		 *  @example
10238		 *    // Using an array of objects as a data source (`data`)
10239		 *    $(document).ready( function () {
10240		 *      $('#example').dataTable( {
10241		 *        "data": [
10242		 *          {
10243		 *            "engine":   "Trident",
10244		 *            "browser":  "Internet Explorer 4.0",
10245		 *            "platform": "Win 95+",
10246		 *            "version":  4,
10247		 *            "grade":    "X"
10248		 *          },
10249		 *          {
10250		 *            "engine":   "Trident",
10251		 *            "browser":  "Internet Explorer 5.0",
10252		 *            "platform": "Win 95+",
10253		 *            "version":  5,
10254		 *            "grade":    "C"
10255		 *          }
10256		 *        ],
10257		 *        "columns": [
10258		 *          { "title": "Engine",   "data": "engine" },
10259		 *          { "title": "Browser",  "data": "browser" },
10260		 *          { "title": "Platform", "data": "platform" },
10261		 *          { "title": "Version",  "data": "version" },
10262		 *          { "title": "Grade",    "data": "grade" }
10263		 *        ]
10264		 *      } );
10265		 *    } );
10266		 */
10267		"aaData": null,
10268	
10269	
10270		/**
10271		 * If ordering is enabled, then DataTables will perform a first pass sort on
10272		 * initialisation. You can define which column(s) the sort is performed
10273		 * upon, and the sorting direction, with this variable. The `sorting` array
10274		 * should contain an array for each column to be sorted initially containing
10275		 * the column's index and a direction string ('asc' or 'desc').
10276		 *  @type array
10277		 *  @default [[0,'asc']]
10278		 *
10279		 *  @dtopt Option
10280		 *  @name DataTable.defaults.order
10281		 *
10282		 *  @example
10283		 *    // Sort by 3rd column first, and then 4th column
10284		 *    $(document).ready( function() {
10285		 *      $('#example').dataTable( {
10286		 *        "order": [[2,'asc'], [3,'desc']]
10287		 *      } );
10288		 *    } );
10289		 *
10290		 *    // No initial sorting
10291		 *    $(document).ready( function() {
10292		 *      $('#example').dataTable( {
10293		 *        "order": []
10294		 *      } );
10295		 *    } );
10296		 */
10297		"aaSorting": [[0,'asc']],
10298	
10299	
10300		/**
10301		 * This parameter is basically identical to the `sorting` parameter, but
10302		 * cannot be overridden by user interaction with the table. What this means
10303		 * is that you could have a column (visible or hidden) which the sorting
10304		 * will always be forced on first - any sorting after that (from the user)
10305		 * will then be performed as required. This can be useful for grouping rows
10306		 * together.
10307		 *  @type array
10308		 *  @default null
10309		 *
10310		 *  @dtopt Option
10311		 *  @name DataTable.defaults.orderFixed
10312		 *
10313		 *  @example
10314		 *    $(document).ready( function() {
10315		 *      $('#example').dataTable( {
10316		 *        "orderFixed": [[0,'asc']]
10317		 *      } );
10318		 *    } )
10319		 */
10320		"aaSortingFixed": [],
10321	
10322	
10323		/**
10324		 * DataTables can be instructed to load data to display in the table from a
10325		 * Ajax source. This option defines how that Ajax call is made and where to.
10326		 *
10327		 * The `ajax` property has three different modes of operation, depending on
10328		 * how it is defined. These are:
10329		 *
10330		 * * `string` - Set the URL from where the data should be loaded from.
10331		 * * `object` - Define properties for `jQuery.ajax`.
10332		 * * `function` - Custom data get function
10333		 *
10334		 * `string`
10335		 * --------
10336		 *
10337		 * As a string, the `ajax` property simply defines the URL from which
10338		 * DataTables will load data.
10339		 *
10340		 * `object`
10341		 * --------
10342		 *
10343		 * As an object, the parameters in the object are passed to
10344		 * [jQuery.ajax](http://api.jquery.com/jQuery.ajax/) allowing fine control
10345		 * of the Ajax request. DataTables has a number of default parameters which
10346		 * you can override using this option. Please refer to the jQuery
10347		 * documentation for a full description of the options available, although
10348		 * the following parameters provide additional options in DataTables or
10349		 * require special consideration:
10350		 *
10351		 * * `data` - As with jQuery, `data` can be provided as an object, but it
10352		 *   can also be used as a function to manipulate the data DataTables sends
10353		 *   to the server. The function takes a single parameter, an object of
10354		 *   parameters with the values that DataTables has readied for sending. An
10355		 *   object may be returned which will be merged into the DataTables
10356		 *   defaults, or you can add the items to the object that was passed in and
10357		 *   not return anything from the function. This supersedes `fnServerParams`
10358		 *   from DataTables 1.9-.
10359		 *
10360		 * * `dataSrc` - By default DataTables will look for the property `data` (or
10361		 *   `aaData` for compatibility with DataTables 1.9-) when obtaining data
10362		 *   from an Ajax source or for server-side processing - this parameter
10363		 *   allows that property to be changed. You can use Javascript dotted
10364		 *   object notation to get a data source for multiple levels of nesting, or
10365		 *   it my be used as a function. As a function it takes a single parameter,
10366		 *   the JSON returned from the server, which can be manipulated as
10367		 *   required, with the returned value being that used by DataTables as the
10368		 *   data source for the table. This supersedes `sAjaxDataProp` from
10369		 *   DataTables 1.9-.
10370		 *
10371		 * * `success` - Should not be overridden it is used internally in
10372		 *   DataTables. To manipulate / transform the data returned by the server
10373		 *   use `ajax.dataSrc`, or use `ajax` as a function (see below).
10374		 *
10375		 * `function`
10376		 * ----------
10377		 *
10378		 * As a function, making the Ajax call is left up to yourself allowing
10379		 * complete control of the Ajax request. Indeed, if desired, a method other
10380		 * than Ajax could be used to obtain the required data, such as Web storage
10381		 * or an AIR database.
10382		 *
10383		 * The function is given four parameters and no return is required. The
10384		 * parameters are:
10385		 *
10386		 * 1. _object_ - Data to send to the server
10387		 * 2. _function_ - Callback function that must be executed when the required
10388		 *    data has been obtained. That data should be passed into the callback
10389		 *    as the only parameter
10390		 * 3. _object_ - DataTables settings object for the table
10391		 *
10392		 * Note that this supersedes `fnServerData` from DataTables 1.9-.
10393		 *
10394		 *  @type string|object|function
10395		 *  @default null
10396		 *
10397		 *  @dtopt Option
10398		 *  @name DataTable.defaults.ajax
10399		 *  @since 1.10.0
10400		 *
10401		 * @example
10402		 *   // Get JSON data from a file via Ajax.
10403		 *   // Note DataTables expects data in the form `{ data: [ ...data... ] }` by default).
10404		 *   $('#example').dataTable( {
10405		 *     "ajax": "data.json"
10406		 *   } );
10407		 *
10408		 * @example
10409		 *   // Get JSON data from a file via Ajax, using `dataSrc` to change
10410		 *   // `data` to `tableData` (i.e. `{ tableData: [ ...data... ] }`)
10411		 *   $('#example').dataTable( {
10412		 *     "ajax": {
10413		 *       "url": "data.json",
10414		 *       "dataSrc": "tableData"
10415		 *     }
10416		 *   } );
10417		 *
10418		 * @example
10419		 *   // Get JSON data from a file via Ajax, using `dataSrc` to read data
10420		 *   // from a plain array rather than an array in an object
10421		 *   $('#example').dataTable( {
10422		 *     "ajax": {
10423		 *       "url": "data.json",
10424		 *       "dataSrc": ""
10425		 *     }
10426		 *   } );
10427		 *
10428		 * @example
10429		 *   // Manipulate the data returned from the server - add a link to data
10430		 *   // (note this can, should, be done using `render` for the column - this
10431		 *   // is just a simple example of how the data can be manipulated).
10432		 *   $('#example').dataTable( {
10433		 *     "ajax": {
10434		 *       "url": "data.json",
10435		 *       "dataSrc": function ( json ) {
10436		 *         for ( var i=0, ien=json.length ; i<ien ; i++ ) {
10437		 *           json[i][0] = '<a href="/message/'+json[i][0]+'>View message</a>';
10438		 *         }
10439		 *         return json;
10440		 *       }
10441		 *     }
10442		 *   } );
10443		 *
10444		 * @example
10445		 *   // Add data to the request
10446		 *   $('#example').dataTable( {
10447		 *     "ajax": {
10448		 *       "url": "data.json",
10449		 *       "data": function ( d ) {
10450		 *         return {
10451		 *           "extra_search": $('#extra').val()
10452		 *         };
10453		 *       }
10454		 *     }
10455		 *   } );
10456		 *
10457		 * @example
10458		 *   // Send request as POST
10459		 *   $('#example').dataTable( {
10460		 *     "ajax": {
10461		 *       "url": "data.json",
10462		 *       "type": "POST"
10463		 *     }
10464		 *   } );
10465		 *
10466		 * @example
10467		 *   // Get the data from localStorage (could interface with a form for
10468		 *   // adding, editing and removing rows).
10469		 *   $('#example').dataTable( {
10470		 *     "ajax": function (data, callback, settings) {
10471		 *       callback(
10472		 *         JSON.parse( localStorage.getItem('dataTablesData') )
10473		 *       );
10474		 *     }
10475		 *   } );
10476		 */
10477		"ajax": null,
10478	
10479	
10480		/**
10481		 * This parameter allows you to readily specify the entries in the length drop
10482		 * down menu that DataTables shows when pagination is enabled. It can be
10483		 * either a 1D array of options which will be used for both the displayed
10484		 * option and the value, or a 2D array which will use the array in the first
10485		 * position as the value, and the array in the second position as the
10486		 * displayed options (useful for language strings such as 'All').
10487		 *
10488		 * Note that the `pageLength` property will be automatically set to the
10489		 * first value given in this array, unless `pageLength` is also provided.
10490		 *  @type array
10491		 *  @default [ 10, 25, 50, 100 ]
10492		 *
10493		 *  @dtopt Option
10494		 *  @name DataTable.defaults.lengthMenu
10495		 *
10496		 *  @example
10497		 *    $(document).ready( function() {
10498		 *      $('#example').dataTable( {
10499		 *        "lengthMenu": [[10, 25, 50, -1], [10, 25, 50, "All"]]
10500		 *      } );
10501		 *    } );
10502		 */
10503		"aLengthMenu": [ 10, 25, 50, 100 ],
10504	
10505	
10506		/**
10507		 * The `columns` option in the initialisation parameter allows you to define
10508		 * details about the way individual columns behave. For a full list of
10509		 * column options that can be set, please see
10510		 * {@link DataTable.defaults.column}. Note that if you use `columns` to
10511		 * define your columns, you must have an entry in the array for every single
10512		 * column that you have in your table (these can be null if you don't which
10513		 * to specify any options).
10514		 *  @member
10515		 *
10516		 *  @name DataTable.defaults.column
10517		 */
10518		"aoColumns": null,
10519	
10520		/**
10521		 * Very similar to `columns`, `columnDefs` allows you to target a specific
10522		 * column, multiple columns, or all columns, using the `targets` property of
10523		 * each object in the array. This allows great flexibility when creating
10524		 * tables, as the `columnDefs` arrays can be of any length, targeting the
10525		 * columns you specifically want. `columnDefs` may use any of the column
10526		 * options available: {@link DataTable.defaults.column}, but it _must_
10527		 * have `targets` defined in each object in the array. Values in the `targets`
10528		 * array may be:
10529		 *   <ul>
10530		 *     <li>
10530a string - class name will be matched on the TH for the column</li>
10531		 *     <li>0 or a positive integer - column index counting from the left</li>
10532		 *     <li>a negative integer - column index counting from the right</li>
10533		 *     <li>the string "_all" - all columns (i.e. assign a default)</li>
10534		 *   </ul>
10535		 *  @member
10536		 *
10537		 *  @name DataTable.defaults.columnDefs
10538		 */
10539		"aoColumnDefs": null,
10540	
10541	
10542		/**
10543		 * Basically the same as `search`, this parameter defines the individual column
10544		 * filtering state at initialisation time. The array must be of the same size
10545		 * as the number of columns, and each element be an object with the parameters
10546		 * `search` and `escapeRegex` (the latter is optional). 'null' is also
10547		 * accepted and the default will be used.
10548		 *  @type array
10549		 *  @default []
10550		 *
10551		 *  @dtopt Option
10552		 *  @name DataTable.defaults.searchCols
10553		 *
10554		 *  @example
10555		 *    $(document).ready( function() {
10556		 *      $('#example').dataTable( {
10557		 *        "searchCols": [
10558		 *          null,
10559		 *          { "search": "My filter" },
10560		 *          null,
10561		 *          { "search": "^[0-9]", "escapeRegex": false }
10562		 *        ]
10563		 *      } );
10564		 *    } )
10565		 */
10566		"aoSearchCols": [],
10567	
10568	
10569		/**
10570		 * An array of CSS classes that should be applied to displayed rows. This
10571		 * array may be of any length, and DataTables will apply each class
10572		 * sequentially, looping when required.
10573		 *  @type array
10574		 *  @default null <i>
10574Will take the values determined by the `oClasses.stripe*`
10575		 *    options</i>
10576		 *
10577		 *  @dtopt Option
10578		 *  @name DataTable.defaults.stripeClasses
10579		 *
10580		 *  @example
10581		 *    $(document).ready( function() {
10582		 *      $('#example').dataTable( {
10583		 *        "stripeClasses": [ 'strip1', 'strip2', 'strip3' ]
10584		 *      } );
10585		 *    } )
10586		 */
10587		"asStripeClasses": null,
10588	
10589	
10590		/**
10591		 * Enable or disable automatic column width calculation. This can be disabled
10592		 * as an optimisation (it takes some time to calculate the widths) if the
10593		 * tables widths are passed in using `columns`.
10594		 *  @type boolean
10595		 *  @default true
10596		 *
10597		 *  @dtopt Features
10598		 *  @name DataTable.defaults.autoWidth
10599		 *
10600		 *  @example
10601		 *    $(document).ready( function () {
10602		 *      $('#example').dataTable( {
10603		 *        "autoWidth": false
10604		 *      } );
10605		 *    } );
10606		 */
10607		"bAutoWidth": true,
10608	
10609	
10610		/**
10611		 * Deferred rendering can provide DataTables with a huge speed boost when you
10612		 * are using an Ajax or JS data source for the table. This option, when set to
10613		 * true, will cause DataTables to defer the creation of the table elements for
10614		 * each row until they are needed for a draw - saving a significant amount of
10615		 * time.
10616		 *  @type boolean
10617		 *  @default false
10618		 *
10619		 *  @dtopt Features
10620		 *  @name DataTable.defaults.deferRender
10621		 *
10622		 *  @example
10623		 *    $(document).ready( function() {
10624		 *      $('#example').dataTable( {
10625		 *        "ajax": "sources/arrays.txt",
10626		 *        "deferRender": true
10627		 *      } );
10628		 *    } );
10629		 */
10630		"bDeferRender": false,
10631	
10632	
10633		/**
10634		 * Replace a DataTable which matches the given selector and replace it with
10635		 * one which has the properties of the new initialisation object passed. If no
10636		 * table matches the selector, then the new DataTable will be constructed as
10637		 * per normal.
10638		 *  @type boolean
10639		 *  @default false
10640		 *
10641		 *  @dtopt Options
10642		 *  @name DataTable.defaults.destroy
10643		 *
10644		 *  @example
10645		 *    $(document).ready( function() {
10646		 *      $('#example').dataTable( {
10647		 *        "srollY": "200px",
10648		 *        "paginate": false
10649		 *      } );
10650		 *
10651		 *      // Some time later....
10652		 *      $('#example').dataTable( {
10653		 *        "filter": false,
10654		 *        "destroy": true
10655		 *      } );
10656		 *    } );
10657		 */
10658		"bDestroy": false,
10659	
10660	
10661		/**
10662		 * Enable or disable filtering of data. Filtering in DataTables is "smart" in
10663		 * that it allows the end user to input multiple words (space separated) and
10664		 * will match a row containing those words, even if not in the order that was
10665		 * specified (this allow matching across multiple columns). Note that if you
10666		 * wish to use filtering in DataTables this must remain 'true' - to remove the
10667		 * default filtering input box and retain filtering abilities, please use
10668		 * {@link DataTable.defaults.dom}.
10669		 *  @type boolean
10670		 *  @default true
10671		 *
10672		 *  @dtopt Features
10673		 *  @name DataTable.defaults.searching
10674		 *
10675		 *  @example
10676		 *    $(document).ready( function () {
10677		 *      $('#example').dataTable( {
10678		 *        "searching": false
10679		 *      } );
10680		 *    } );
10681		 */
10682		"bFilter": true,
10683	
10684	
10685		/**
10686		 * Enable or disable the table information display. This shows information
10687		 * about the data that is currently visible on the page, including information
10688		 * about filtered data if that action is being performed.
10689		 *  @type boolean
10690		 *  @default true
10691		 *
10692		 *  @dtopt Features
10693		 *  @name DataTable.defaults.info
10694		 *
10695		 *  @example
10696		 *    $(document).ready( function () {
10697		 *      $('#example').dataTable( {
10698		 *        "info": false
10699		 *      } );
10700		 *    } );
10701		 */
10702		"bInfo": true,
10703	
10704	
10705		/**
10706		 * Allows the end user to select the size of a formatted page from a select
10707		 * menu (sizes are 10, 25, 50 and 100). Requires pagination (`paginate`).
10708		 *  @type boolean
10709		 *  @default true
10710		 *
10711		 *  @dtopt Features
10712		 *  @name DataTable.defaults.lengthChange
10713		 *
10714		 *  @example
10715		 *    $(document).ready( function () {
10716		 *      $('#example').dataTable( {
10717		 *        "lengthChange": false
10718		 *      } );
10719		 *    } );
10720		 */
10721		"bLengthChange": true,
10722	
10723	
10724		/**
10725		 * Enable or disable pagination.
10726		 *  @type boolean
10727		 *  @default true
10728		 *
10729		 *  @dtopt Features
10730		 *  @name DataTable.defaults.paging
10731		 *
10732		 *  @example
10733		 *    $(document).ready( function () {
10734		 *      $('#example').dataTable( {
10735		 *        "paging": false
10736		 *      } );
10737		 *    } );
10738		 */
10739		"bPaginate": true,
10740	
10741	
10742		/**
10743		 * Enable or disable the display of a 'processing' indicator when the table is
10744		 * being processed (e.g. a sort). This is particularly useful for tables with
10745		 * large amounts of data where it can take a noticeable amount of time to sort
10746		 * the entries.
10747		 *  @type boolean
10748		 *  @default false
10749		 *
10750		 *  @dtopt Features
10751		 *  @name DataTable.defaults.processing
10752		 *
10753		 *  @example
10754		 *    $(document).ready( function () {
10755		 *      $('#example').dataTable( {
10756		 *        "processing": true
10757		 *      } );
10758		 *    } );
10759		 */
10760		"bProcessing": false,
10761	
10762	
10763		/**
10764		 * Retrieve the DataTables object for the given selector. Note that if the
10765		 * table has already been initialised, this parameter will cause DataTables
10766		 * to simply return the object that has already been set up - it will not take
10767		 * account of any changes you might have made to the initialisation object
10768		 * passed to DataTables (setting this parameter to true is an acknowledgement
10769		 * that you understand this). `destroy` can be used to reinitialise a table if
10770		 * you need.
10771		 *  @type boolean
10772		 *  @default false
10773		 *
10774		 *  @dtopt Options
10775		 *  @name DataTable.defaults.retrieve
10776		 *
10777		 *  @example
10778		 *    $(document).ready( function() {
10779		 *      initTable();
10780		 *      tableActions();
10781		 *    } );
10782		 *
10783		 *    function initTable ()
10784		 *    {
10785		 *      return $('#example').dataTable( {
10786		 *        "scrollY": "200px",
10787		 *        "paginate": false,
10788		 *        "retrieve": true
10789		 *      } );
10790		 *    }
10791		 *
10792		 *    function tableActions ()
10793		 *    {
10794		 *      var table = initTable();
10795		 *      // perform API operations with oTable
10796		 *    }
10797		 */
10798		"bRetrieve": false,
10799	
10800	
10801		/**
10802		 * When vertical (y) scrolling is enabled, DataTables will force the height of
10803		 * the table's viewport to the given height at all times (useful for layout).
10804		 * However, this can look odd when filtering data down to a small data set,
10805		 * and the footer is left "floating" further down. This parameter (when
10806		 * enabled) will cause DataTables to collapse the table's viewport down when
10807		 * the result set will fit within the given Y height.
10808		 *  @type boolean
10809		 *  @default false
10810		 *
10811		 *  @dtopt Options
10812		 *  @name DataTable.defaults.scrollCollapse
10813		 *
10814		 *  @example
10815		 *    $(document).ready( function() {
10816		 *      $('#example').dataTable( {
10817		 *        "scrollY": "200",
10818		 *        "scrollCollapse": true
10819		 *      } );
10820		 *    } );
10821		 */
10822		"bScrollCollapse": false,
10823	
10824	
10825		/**
10826		 * Configure DataTables to use server-side processing. Note that the
10827		 * `ajax` parameter must also be given in order to give DataTables a
10828		 * source to obtain the required data for each draw.
10829		 *  @type boolean
10830		 *  @default false
10831		 *
10832		 *  @dtopt Features
10833		 *  @dtopt Server-side
10834		 *  @name DataTable.defaults.serverSide
10835		 *
10836		 *  @example
10837		 *    $(document).ready( function () {
10838		 *      $('#example').dataTable( {
10839		 *        "serverSide": true,
10840		 *        "ajax": "xhr.php"
10841		 *      } );
10842		 *    } );
10843		 */
10844		"bServerSide": false,
10845	
10846	
10847		/**
10848		 * Enable or disable sorting of columns. Sorting of individual columns can be
10849		 * disabled by the `sortable` option for each column.
10850		 *  @type boolean
10851		 *  @default true
10852		 *
10853		 *  @dtopt Features
10854		 *  @name DataTable.defaults.ordering
10855		 *
10856		 *  @example
10857		 *    $(document).ready( function () {
10858		 *      $('#example').dataTable( {
10859		 *        "ordering": false
10860		 *      } );
10861		 *    } );
10862		 */
10863		"bSort": true,
10864	
10865	
10866		/**
10867		 * Enable or display DataTables' ability to sort multiple columns at the
10868		 * same time (activated by shift-click by the user).
10869		 *  @type boolean
10870		 *  @default true
10871		 *
10872		 *  @dtopt Options
10873		 *  @name DataTable.defaults.orderMulti
10874		 *
10875		 *  @example
10876		 *    // Disable multiple column sorting ability
10877		 *    $(document).ready( function () {
10878		 *      $('#example').dataTable( {
10879		 *        "orderMulti": false
10880		 *      } );
10881		 *    } );
10882		 */
10883		"bSortMulti": true,
10884	
10885	
10886		/**
10887		 * Allows control over whether DataTables should use the top (true) unique
10888		 * cell that is found for a single column, or the bottom (false - default).
10889		 * This is useful when using complex headers.
10890		 *  @type boolean
10891		 *  @default false
10892		 *
10893		 *  @dtopt Options
10894		 *  @name DataTable.defaults.orderCellsTop
10895		 *
10896		 *  @example
10897		 *    $(document).ready( function() {
10898		 *      $('#example').dataTable( {
10899		 *        "orderCellsTop": true
10900		 *      } );
10901		 *    } );
10902		 */
10903		"bSortCellsTop": false,
10904	
10905	
10906		/**
10907		 * Enable or disable the addition of the classes `sorting\_1`, `sorting\_2` and
10908		 * `sorting\_3` to the columns which are currently being sorted on. This is
10909		 * presented as a feature switch as it can increase processing time (while
10910		 * classes are removed and added) so for large data sets you might want to
10911		 * turn this off.
10912		 *  @type boolean
10913		 *  @default true
10914		 *
10915		 *  @dtopt Features
10916		 *  @name DataTable.defaults.orderClasses
10917		 *
10918		 *  @example
10919		 *    $(document).ready( function () {
10920		 *      $('#example').dataTable( {
10921		 *        "orderClasses": false
10922		 *      } );
10923		 *    } );
10924		 */
10925		"bSortClasses": true,
10926	
10927	
10928		/**
10929		 * Enable or disable state saving. When enabled HTML5 `localStorage` will be
10930		 * used to save table display information such as pagination information,
10931		 * display length, filtering and sorting. As such when the end user reloads
10932		 * the page the display display will match what thy had previously set up.
10933		 *
10934		 * Due to the use of `localStorage` the default state saving is not supported
10935		 * in IE6 or 7. If state saving is required in those browsers, use
10936		 * `stateSaveCallback` to provide a storage solution such as cookies.
10937		 *  @type boolean
10938		 *  @default false
10939		 *
10940		 *  @dtopt Features
10941		 *  @name DataTable.defaults.stateSave
10942		 *
10943		 *  @example
10944		 *    $(document).ready( function () {
10945		 *      $('#example').dataTable( {
10946		 *        "stateSave": true
10947		 *      } );
10948		 *    } );
10949		 */
10950		"bStateSave": false,
10951	
10952	
10953		/**
10954		 * This function is called when a TR element is created (and all TD child
10955		 * elements have been inserted), or registered if using a DOM source, allowing
10956		 * manipulation of the TR element (adding classes etc).
10957		 *  @type function
10958		 *  @param {node} row "TR" element for the current row
10959		 *  @param {array} data Raw data array for this row
10960		 *  @param {int} dataIndex The index of this row in the internal aoData array
10961		 *
10962		 *  @dtopt Callbacks
10963		 *  @name DataTable.defaults.createdRow
10964		 *
10965		 *  @example
10966		 *    $(document).ready( function() {
10967		 *      $('#example').dataTable( {
10968		 *        "createdRow": function( row, data, dataIndex ) {
10969		 *          // Bold the grade for all 'A' grade browsers
10970		 *          if ( data[4] == "A" )
10971		 *          {
10972		 *            $('td:eq(4)', row).html( '<b>A</b>' );
10973		 *          }
10974		 *        }
10975		 *      } );
10976		 *    } );
10977		 */
10978		"fnCreatedRow": null,
10979	
10980	
10981		/**
10982		 * This function is called on every 'draw' event, and allows you to
10983		 * dynamically modify any aspect you want about the created DOM.
10984		 *  @type function
10985		 *  @param {object} settings DataTables settings object
10986		 *
10987		 *  @dtopt Callbacks
10988		 *  @name DataTable.defaults.drawCallback
10989		 *
10990		 *  @example
10991		 *    $(document).ready( function() {
10992		 *      $('#example').dataTable( {
10993		 *        "drawCallback": function( settings ) {
10994		 *          alert( 'DataTables has redrawn the table' );
10995		 *        }
10996		 *      } );
10997		 *    } );
10998		 */
10999		"fnDrawCallback": null,
11000	
11001	
11002		/**
11003		 * Identical to fnHeaderCallback() but for the table footer this function
11004		 * allows you to modify the table footer on every 'draw' event.
11005		 *  @type function
11006		 *  @param {node} foot "TR" element for the footer
11007		 *  @param {array} data Full table data (as derived from the original HTML)
11008		 *  @param {int} start Index for the current display starting point in the
11009		 *    display array
11010		 *  @param {int} end Index for the current display ending point in the
11011		 *    display array
11012		 *  @param {array int} display Index array to translate the visual position
11013		 *    to the full data array
11014		 *
11015		 *  @dtopt Callbacks
11016		 *  @name DataTable.defaults.footerCallback
11017		 *
11018		 *  @example
11019		 *    $(document).ready( function() {
11020		 *      $('#example').dataTable( {
11021		 *        "footerCallback": function( tfoot, data, start, end, display ) {
11022		 *          tfoot.getElementsByTagName('th')[0].innerHTML = "Starting index is "+start;
11023		 *        }
11024		 *      } );
11025		 *    } )
11026		 */
11027		"fnFooterCallback": null,
11028	
11029	
11030		/**
11031		 * When rendering large numbers in the information element for the table
11032		 * (i.e. "Showing 1 to 10 of 57 entries") DataTables will render large numbers
11033		 * to have a comma separator for the 'thousands' units (e.g. 1 million is
11034		 * rendered as "1,000,000") to help readability for the end user. This
11035		 * function will override the default method DataTables uses.
11036		 *  @type function
11037		 *  @member
11038		 *  @param {int} toFormat number to be formatted
11039		 *  @returns {string} formatted string for DataTables to show the number
11040		 *
11041		 *  @dtopt Callbacks
11042		 *  @name DataTable.defaults.formatNumber
11043		 *
11044		 *  @example
11045		 *    // Format a number using a single quote for the separator (note that
11046		 *    // this can also be done with the language.thousands option)
11047		 *    $(document).ready( function() {
11048		 *      $('#example').dataTable( {
11049		 *        "formatNumber": function ( toFormat ) {
11050		 *          return toFormat.toString().replace(
11051		 *            /\B(?=(\d{3})+(?!\d))/g, "'"
11052		 *          );
11053		 *        };
11054		 *      } );
11055		 *    } );
11056		 */
11057		"fnFormatNumber": function ( toFormat ) {
11058			return toFormat.toString().replace(
11059				/\B(?=(\d{3})+(?!\d))/g,
11060				this.oLanguage.sThousands
11061			);
11062		},
11063	
11064	
11065		/**
11066		 * This function is called on every 'draw' event, and allows you to
11067		 * dynamically modify the header row. This can be used to calculate and
11068		 * display useful information about the table.
11069		 *  @type function
11070		 *  @param {node} head "TR" element for the header
11071		 *  @param {array} data Full table data (as derived from the original HTML)
11072		 *  @param {int} start Index for the current display starting point in the
11073		 *    display array
11074		 *  @param {int} end Index for the current display ending point in the
11075		 *    display array
11076		 *  @param {array int} display Index array to translate the visual position
11077		 *    to the full data array
11078		 *
11079		 *  @dtopt Callbacks
11080		 *  @name DataTable.defaults.headerCallback
11081		 *
11082		 *  @example
11083		 *    $(document).ready( function() {
11084		 *      $('#example').dataTable( {
11085		 *        "fheaderCallback": function( head, data, start, end, display ) {
11086		 *          head.getElementsByTagName('th')[0].innerHTML = "Displaying "+(end-start)+" records";
11087		 *        }
11088		 *      } );
11089		 *    } )
11090		 */
11091		"fnHeaderCallback": null,
11092	
11093	
11094		/**
11095		 * The information element can be used to convey information about the current
11096		 * state of the table. Although the internationalisation options presented by
11097		 * DataTables are quite capable of dealing with most customisations, there may
11098		 * be times where you wish to customise the string further. This callback
11099		 * allows you to do exactly that.
11100		 *  @type function
11101		 *  @param {object} oSettings DataTables settings object
11102		 *  @param {int} start Starting position in data for the draw
11103		 *  @param {int} end End position in data for the draw
11104		 *  @param {int} max Total number of rows in the table (regardless of
11105		 *    filtering)
11106		 *  @param {int} total Total number of rows in the data set, after filtering
11107		 *  @param {string} pre The string that DataTables has formatted using it's
11108		 *    own rules
11109		 *  @returns {string} The string to be displayed in the information element.
11110		 *
11111		 *  @dtopt Callbacks
11112		 *  @name DataTable.defaults.infoCallback
11113		 *
11114		 *  @example
11115		 *    $('#example').dataTable( {
11116		 *      "infoCallback": function( settings, start, end, max, total, pre ) {
11117		 *        return start +" to "+ end;
11118		 *      }
11119		 *    } );
11120		 */
11121		"fnInfoCallback": null,
11122	
11123	
11124		/**
11125		 * Called when the table has been initialised. Normally DataTables will
11126		 * initialise sequentially and there will be no need for this function,
11127		 * however, this does not hold true when using external language information
11128		 * since that is obtained using an async XHR call.
11129		 *  @type function
11130		 *  @param {object} settings DataTables settings object
11131		 *  @param {object} json The JSON object request from the server - only
11132		 *    present if client-side Ajax sourced data is used
11133		 *
11134		 *  @dtopt Callbacks
11135		 *  @name DataTable.defaults.initComplete
11136		 *
11137		 *  @example
11138		 *    $(document).ready( function() {
11139		 *      $('#example').dataTable( {
11140		 *        "initComplete": function(settings, json) {
11141		 *          alert( 'DataTables has finished its initialisation.' );
11142		 *        }
11143		 *      } );
11144		 *    } )
11145		 */
11146		"fnInitComplete": null,
11147	
11148	
11149		/**
11150		 * Called at the very start of each table draw and can be used to cancel the
11151		 * draw by returning false, any other return (including undefined) results in
11152		 * the full draw occurring).
11153		 *  @type function
11154		 *  @param {object} settings DataTables settings object
11155		 *  @returns {boolean} False will cancel the draw, anything else (including no
11156		 *    return) will allow it to complete.
11157		 *
11158		 *  @dtopt Callbacks
11159		 *  @name DataTable.defaults.preDrawCallback
11160		 *
11161		 *  @example
11162		 *    $(document).ready( function() {
11163		 *      $('#example').dataTable( {
11164		 *        "preDrawCallback": function( settings ) {
11165		 *          if ( $('#test').val() == 1 ) {
11166		 *            return false;
11167		 *          }
11168		 *        }
11169		 *      } );
11170		 *    } );
11171		 */
11172		"fnPreDrawCallback": null,
11173	
11174	
11175		/**
11176		 * This function allows you to 'post process' each row after it have been
11177		 * generated for each table draw, but before it is rendered on screen. This
11178		 * function might be used for setting the row class name etc.
11179		 *  @type function
11180		 *  @param {node} row "TR" element for the current row
11181		 *  @param {array} data Raw data array for this row
11182		 *  @param {int} displayIndex The display index for the current table draw
11183		 *  @param {int} displayIndexFull The index of the data in the full list of
11184		 *    rows (after filtering)
11185		 *
11186		 *  @dtopt Callbacks
11187		 *  @name DataTable.defaults.rowCallback
11188		 *
11189		 *  @example
11190		 *    $(document).ready( function() {
11191		 *      $('#example').dataTable( {
11192		 *        "rowCallback": function( row, data, displayIndex, displayIndexFull ) {
11193		 *          // Bold the grade for all 'A' grade browsers
11194		 *          if ( data[4] == "A" ) {
11195		 *            $('td:eq(4)', row).html( '<b>A</b>' );
11196		 *          }
11197		 *        }
11198		 *      } );
11199		 *    } );
11200		 */
11201		"fnRowCallback": null,
11202	
11203	
11204		/**
11205		 * __Deprecated__ The functionality provided by this parameter has now been
11206		 * superseded by that provided through `ajax`, which should be used instead.
11207		 *
11208		 * This parameter allows you to override the default function which obtains
11209		 * the data from the server so something more suitable for your application.
11210		 * For example you could use POST data, or pull information from a Gears or
11211		 * AIR database.
11212		 *  @type function
11213		 *  @member
11214		 *  @param {string} source HTTP source to obtain the data from (`ajax`)
11215		 *  @param {array}
11215 data A key/value pair object containing the data to send
11216		 *    to the server
11217		 *  @param {function} callback to be called on completion of the data get
11218		 *    process that will draw the data on the page.
11219		 *  @param {object} settings DataTables settings object
11220		 *
11221		 *  @dtopt Callbacks
11222		 *  @dtopt Server-side
11223		 *  @name DataTable.defaults.serverData
11224		 *
11225		 *  @deprecated 1.10. Please use `ajax` for this functionality now.
11226		 */
11227		"fnServerData": null,
11228	
11229	
11230		/**
11231		 * __Deprecated__ The functionality provided by this parameter has now been
11232		 * superseded by that provided through `ajax`, which should be used instead.
11233		 *
11234		 *  It is often useful to send extra data to the server when making an Ajax
11235		 * request - for example custom filtering information, and this callback
11236		 * function makes it trivial to send extra information to the server. The
11237		 * passed in parameter is the data set that has been constructed by
11238		 * DataTables, and you can add to this or modify it as you require.
11239		 *  @type function
11240		 *  @param {array} data Data array (array of objects which are name/value
11241		 *    pairs) that has been constructed by DataTables and will be sent to the
11242		 *    server. In the case of Ajax sourced data with server-side processing
11243		 *    this will be an empty array, for server-side processing there will be a
11244		 *    significant number of parameters!
11245		 *  @returns {undefined} Ensure that you modify the data array passed in,
11246		 *    as this is passed by reference.
11247		 *
11248		 *  @dtopt Callbacks
11249		 *  @dtopt Server-side
11250		 *  @name DataTable.defaults.serverParams
11251		 *
11252		 *  @deprecated 1.10. Please use `ajax` for this functionality now.
11253		 */
11254		"fnServerParams": null,
11255	
11256	
11257		/**
11258		 * Load the table state. With this function you can define from where, and how, the
11259		 * state of a table is loaded. By default DataTables will load from `localStorage`
11260		 * but you might wish to use a server-side database or cookies.
11261		 *  @type function
11262		 *  @member
11263		 *  @param {object} settings DataTables settings object
11264		 *  @param {object} callback Callback that can be executed when done. It
11265		 *    should be passed the loaded state object.
11266		 *  @return {object} The DataTables state object to be loaded
11267		 *
11268		 *  @dtopt Callbacks
11269		 *  @name DataTable.defaults.stateLoadCallback
11270		 *
11271		 *  @example
11272		 *    $(document).ready( function() {
11273		 *      $('#example').dataTable( {
11274		 *        "stateSave": true,
11275		 *        "stateLoadCallback": function (settings, callback) {
11276		 *          $.ajax( {
11277		 *            "url": "/state_load",
11278		 *            "dataType": "json",
11279		 *            "success": function (json) {
11280		 *              callback( json );
11281		 *            }
11282		 *          } );
11283		 *        }
11284		 *      } );
11285		 *    } );
11286		 */
11287		"fnStateLoadCallback": function ( settings ) {
11288			try {
11289				return JSON.parse(
11290					(settings.iStateDuration === -1 ? sessionStorage : localStorage).getItem(
11291						'DataTables_'+settings.sInstance+'_'+location.pathname
11292					)
11293				);
11294			} catch (e) {
11295				return {};
11296			}
11297		},
11298	
11299	
11300		/**
11301		 * Callback which allows modification of the saved state prior to loading that state.
11302		 * This callback is called when the table is loading state from the stored data, but
11303		 * prior to the settings object being modified by the saved state. Note that for
11304		 * plug-in authors, you should use the `stateLoadParams` event to load parameters for
11305		 * a plug-in.
11306		 *  @type function
11307		 *  @param {object} settings DataTables settings object
11308		 *  @param {object} data The state object that is to be loaded
11309		 *
11310		 *  @dtopt Callbacks
11311		 *  @name DataTable.defaults.stateLoadParams
11312		 *
11313		 *  @example
11314		 *    // Remove a saved filter, so filtering is never loaded
11315		 *    $(document).ready( function() {
11316		 *      $('#example').dataTable( {
11317		 *        "stateSave": true,
11318		 *        "stateLoadParams": function (settings, data) {
11319		 *          data.oSearch.sSearch = "";
11320		 *        }
11321		 *      } );
11322		 *    } );
11323		 *
11324		 *  @example
11325		 *    // Disallow state loading by returning false
11326		 *    $(document).ready( function() {
11327		 *      $('#example').dataTable( {
11328		 *        "stateSave": true,
11329		 *        "stateLoadParams": function (settings, data) {
11330		 *          return false;
11331		 *        }
11332		 *      } );
11333		 *    } );
11334		 */
11335		"fnStateLoadParams": null,
11336	
11337	
11338		/**
11339		 * Callback that is called when the state has been loaded from the state saving method
11340		 * and the DataTables settings object has been modified as a result of the loaded state.
11341		 *  @type function
11342		 *  @param {object} settings DataTables settings object
11343		 *  @param {object} data The state object that was loaded
11344		 *
11345		 *  @dtopt Callbacks
11346		 *  @name DataTable.defaults.stateLoaded
11347		 *
11348		 *  @example
11349		 *    // Show an alert with the filtering value that was saved
11350		 *    $(document).ready( function() {
11351		 *      $('#example').dataTable( {
11352		 *        "stateSave": true,
11353		 *        "stateLoaded": function (settings, data) {
11354		 *          alert( 'Saved filter was: '+data.oSearch.sSearch );
11355		 *        }
11356		 *      } );
11357		 *    } );
11358		 */
11359		"fnStateLoaded": null,
11360	
11361	
11362		/**
11363		 * Save the table state. This function allows you to define where and how the state
11364		 * information for the table is stored By default DataTables will use `localStorage`
11365		 * but you might wish to use a server-side database or cookies.
11366		 *  @type function
11367		 *  @member
11368		 *  @param {object} settings DataTables settings object
11369		 *  @param {object} data The state object to be saved
11370		 *
11371		 *  @dtopt Callbacks
11372		 *  @name DataTable.defaults.stateSaveCallback
11373		 *
11374		 *  @example
11375		 *    $(document).ready( function() {
11376		 *      $('#example').dataTable( {
11377		 *        "stateSave": true,
11378		 *        "stateSaveCallback": function (settings, data) {
11379		 *          // Send an Ajax request to the server with the state object
11380		 *          $.ajax( {
11381		 *            "url": "/state_save",
11382		 *            "data": data,
11383		 *            "dataType": "json",
11384		 *            "method": "POST"
11385		 *            "success": function () {}
11386		 *          } );
11387		 *        }
11388		 *      } );
11389		 *    } );
11390		 */
11391		"fnStateSaveCallback": function ( settings, data ) {
11392			try {
11393				(settings.iStateDuration === -1 ? sessionStorage : localStorage).setItem(
11394					'DataTables_'+settings.sInstance+'_'+location.pathname,
11395					JSON.stringify( data )
11396				);
11397			} catch (e) {}
11398		},
11399	
11400	
11401		/**
11402		 * Callback which allows modification of the state to be saved. Called when the table
11403		 * has changed state a new state save is required. This method allows modification of
11404		 * the state saving object prior to actually doing the save, including addition or
11405		 * other state properties or modification. Note that for plug-in authors, you should
11406		 * use the `stateSaveParams` event to save parameters for a plug-in.
11407		 *  @type function
11408		 *  @param {object} settings DataTables settings object
11409		 *  @param {object} data The state object to be saved
11410		 *
11411		 *  @dtopt Callbacks
11412		 *  @name DataTable.defaults.stateSaveParams
11413		 *
11414		 *  @example
11415		 *    // Remove a saved filter, so filtering is never saved
11416		 *    $(document).ready( function() {
11417		 *      $('#example').dataTable( {
11418		 *        "stateSave": true,
11419		 *        "stateSaveParams": function (settings, data) {
11420		 *          data.oSearch.sSearch = "";
11421		 *        }
11422		 *      } );
11423		 *    } );
11424		 */
11425		"fnStateSaveParams": null,
11426	
11427	
11428		/**
11429		 * Duration for which the saved state information is considered valid. After this period
11430		 * has elapsed the state will be returned to the default.
11431		 * Value is given in seconds.
11432		 *  @type int
11433		 *  @default 7200 <i>(2 hours)</i>
11434		 *
11435		 *  @dtopt Options
11436		 *  @name DataTable.defaults.stateDuration
11437		 *
11438		 *  @example
11439		 *    $(document).ready( function() {
11440		 *      $('#example').dataTable( {
11441		 *        "stateDuration": 60*60*24; // 1 day
11442		 *      } );
11443		 *    } )
11444		 */
11445		"iStateDuration": 7200,
11446	
11447	
11448		/**
11449		 * When enabled DataTables will not make a request to the server for the first
11450		 * page draw - rather it will use the data already on the page (no sorting etc
11451		 * will be applied to it), thus saving on an XHR at load time. `deferLoading`
11452		 * is used to indicate that deferred loading is required, but it is also used
11453		 * to tell DataTables how many records there are in the full table (allowing
11454		 * the information element and pagination to be displayed correctly). In the case
11455		 * where a filtering is applied to the table on initial load, this can be
11456		 * indicated by giving the parameter as an array, where the first element is
11457		 * the number of records available after filtering and the second element is the
11458		 * number of records without filtering (allowing the table information element
11459		 * to be shown correctly).
11460		 *  @type int | array
11461		 *  @default null
11462		 *
11463		 *  @dtopt Options
11464		 *  @name DataTable.defaults.deferLoading
11465		 *
11466		 *  @example
11467		 *    // 57 records available in the table, no filtering applied
11468		 *    $(document).ready( function() {
11469		 *      $('#example').dataTable( {
11470		 *        "serverSide": true,
11471		 *        "ajax": "scripts/server_processing.php",
11472		 *        "deferLoading": 57
11473		 *      } );
11474		 *    } );
11475		 *
11476		 *  @example
11477		 *    // 57 records after filtering, 100 without filtering (an initial filter applied)
11478		 *    $(document).ready( function() {
11479		 *      $('#example').dataTable( {
11480		 *        "serverSide": true,
11481		 *        "ajax": "scripts/server_processing.php",
11482		 *        "deferLoading": [ 57, 100 ],
11483		 *        "search": {
11484		 *          "search": "my_filter"
11485		 *        }
11486		 *      } );
11487		 *    } );
11488		 */
11489		"iDeferLoading": null,
11490	
11491	
11492		/**
11493		 * Number of rows to display on a single page when using pagination. If
11494		 * feature enabled (`lengthChange`) then the end user will be able to override
11495		 * this to a custom setting using a pop-up menu.
11496		 *  @type int
11497		 *  @default 10
11498		 *
11499		 *  @dtopt Options
11500		 *  @name DataTable.defaults.pageLength
11501		 *
11502		 *  @example
11503		 *    $(document).ready( function() {
11504		 *      $('#example').dataTable( {
11505		 *        "pageLength": 50
11506		 *      } );
11507		 *    } )
11508		 */
11509		"iDisplayLength": 10,
11510	
11511	
11512		/**
11513		 * Define the starting point for data display when using DataTables with
11514		 * pagination. Note that this parameter is the number of records, rather than
11515		 * the page number, so if you have 10 records per page and want to start on
11516		 * the third page, it should be "20".
11517		 *  @type int
11518		 *  @default 0
11519		 *
11520		 *  @dtopt Options
11521		 *  @name DataTable.defaults.displayStart
11522		 *
11523		 *  @example
11524		 *    $(document).ready( function() {
11525		 *      $('#example').dataTable( {
11526		 *        "displayStart": 20
11527		 *      } );
11528		 *    } )
11529		 */
11530		"iDisplayStart": 0,
11531	
11532	
11533		/**
11534		 * By default DataTables allows keyboard navigation of the table (sorting, paging,
11535		 * and filtering) by adding a `tabindex` attribute to the required elements. This
11536		 * allows you to tab through the controls and press the enter key to activate them.
11537		 * The tabindex is default 0, meaning that the tab follows the flow of the document.
11538		 * You can overrule this using this parameter if you wish. Use a value of -1 to
11539		 * disable built-in keyboard navigation.
11540		 *  @type int
11541		 *  @default 0
11542		 *
11543		 *  @dtopt Options
11544		 *  @name DataTable.defaults.tabIndex
11545		 *
11546		 *  @example
11547		 *    $(document).ready( function() {
11548		 *      $('#example').dataTable( {
11549		 *        "tabIndex": 1
11550		 *      } );
11551		 *    } );
11552		 */
11553		"iTabIndex": 0,
11554	
11555	
11556		/**
11557		 * Classes that DataTables assigns to the various components and features
11558		 * that it adds to the HTML table. This allows classes to be configured
11559		 * during initialisation in addition to through the static
11560		 * {@link DataTable.ext.oStdClasses} object).
11561		 *  @namespace
11562		 *  @name DataTable.defaults.classes
11563		 */
11564		"oClasses": {},
11565	
11566	
11567		/**
11568		 * All strings that DataTables uses in the user interface that it creates
11569		 * are defined in this object, allowing you to modified them individually or
11570		 * completely replace them all as required.
11571		 *  @namespace
11572		 *  @name DataTable.defaults.language
11573		 */
11574		"oLanguage": {
11575			/**
11576			 * Strings that are used for WAI-ARIA labels and controls only (these are not
11577			 * actually visible on the page, but will be read by screenreaders, and thus
11578			 * must be internationalised as well).
11579			 *  @namespace
11580			 *  @name DataTable.defaults.language.aria
11581			 */
11582			"oAria": {
11583				/**
11584				 * ARIA label that is added to the table headers when the column may be
11585				 * sorted ascending by activing the column (click or return when focused).
11586				 * Note that the column header is prefixed to this string.
11587				 *  @type string
11588				 *  @default : activate to sort column ascending
11589				 *
11590				 *  @dtopt Language
11591				 *  @name DataTable.defaults.language.aria.sortAscending
11592				 *
11593				 *  @example
11594				 *    $(document).ready( function() {
11595				 *      $('#example').dataTable( {
11596				 *        "language": {
11597				 *          "aria": {
11598				 *            "sortAscending": " - click/return to sort ascending"
11599				 *          }
11600				 *        }
11601				 *      } );
11602				 *    } );
11603				 */
11604				"sSortAscending": ": activate to sort column ascending",
11605	
11606				/**
11607				 * ARIA label that is added to the table headers when the column may be
11608				 * sorted descending by activing the column (click or return when focused).
11609				 * Note that the column header is prefixed to this string.
11610				 *  @type string
11611				 *  @default : activate to sort column ascending
11612				 *
11613				 *  @dtopt Language
11614				 *  @name DataTable.defaults.language.aria.sortDescending
11615				 *
11616				 *  @example
11617				 *    $(document).ready( function() {
11618				 *      $('#example').dataTable( {
11619				 *        "language": {
11620				 *          "aria": {
11621				 *            "sortDescending": " - click/return to sort descending"
11622				 *          }
11623				 *        }
11624				 *      } );
11625				 *    } );
11626				 */
11627				"sSortDescending": ": activate to sort column descending"
11628			},
11629	
11630			/**
11631			 * Pagination string used by DataTables for the built-in pagination
11632			 * control types.
11633			 *  @namespace
11634			 *  @name DataTable.defaults.language.paginate
11635			 */
11636			"oPaginate": {
11637				/**
11638				 * Text to use when using the 'full_numbers' type of pagination for the
11639				 * button to take the user to the first page.
11640				 *  @type string
11641				 *  @default First
11642				 *
11643				 *  @dtopt Language
11644				 *  @name DataTable.defaults.language.paginate.first
11645				 *
11646				 *  @example
11647				 *    $(document).ready( function() {
11648				 *      $('#example').dataTable( {
11649				 *        "language": {
11650				 *          "paginate": {
11651				 *            "first": "First page"
11652				 *          }
11653				 *        }
11654				 *      } );
11655				 *    } );
11656				 */
11657				"sFirst": "First",
11658	
11659	
11660				/**
11661				 * Text to use when using the 'full_numbers' type of pagination for the
11662				 * button to take the user to the last page.
11663				 *  @type string
11664				 *  @default Last
11665				 *
11666				 *  @dtopt Language
11667				 *  @name DataTable.defaults.language.paginate.last
11668				 *
11669				 *  @example
11670				 *    $(document).ready( function() {
11671				 *      $('#example').dataTable( {
11672				 *        "language": {
11673				 *          "paginate": {
11674				 *            "last": "Last page"
11675				 *          }
11676				 *        }
11677				 *      } );
11678				 *    } );
11679				 */
11680				"sLast": "Last",
11681	
11682	
11683				/**
11684				 * Text to use for the 'next' pagination button (to take the user to the
11685				 * next page).
11686				 *  @type string
11687				 *  @default Next
11688				 *
11689				 *  @dtopt Language
11690				 *  @name DataTable.defaults.language.paginate.next
11691				 *
11692				 *  @example
11693				 *    $(document).ready( function() {
11694				 *      $('#example').dataTable( {
11695				 *        "language": {
11696				 *          "paginate": {
11697				 *            "next": "Next page"
11698				 *          }
11699				 *        }
11700				 *      } );
11701				 *    } );
11702				 */
11703				"sNext": "Next",
11704	
11705	
11706				/**
11707				 * Text to use for the 'previous' pagination button (to take the user to
11708				 * the previous page).
11709				 *  @type string
11710				 *  @default Previous
11711				 *
11712				 *  @dtopt Language
11713				 *  @name DataTable.defaults.language.paginate.previous
11714				 *
11715				 *  @example
11716				 *    $(document).ready( function() {
11717				 *      $('#example').dataTable( {
11718				 *        "language": {
11719				 *          "paginate": {
11720				 *            "previous": "Previous page"
11721				 *          }
11722				 *        }
11723				 *      } );
11724				 *    } );
11725				 */
11726				"sPrevious": "Previous"
11727			},
11728	
11729			/**
11730			 * This string is shown in preference to `zeroRecords` when the table is
11731			 * empty of data (regardless of filtering). Note that this is an optional
11732			 * parameter - if it is not given, the value of `zeroRecords` will be used
11733			 * instead (either the default or given value).
11734			 *  @type string
11735			 *  @default No data available in table
11736			 *
11737			 *  @dtopt Language
11738			 *  @name DataTable.defaults.language.emptyTable
11739			 *
11740			 *  @example
11741			 *    $(document).ready( function() {
11742			 *      $('#example').dataTable( {
11743			 *        "language": {
11744			 *          "emptyTable": "No data available in table"
11745			 *        }
11746			 *      } );
11747			 *    } );
11748			 */
11749			"sEmptyTable": "No data available in table",
11750	
11751	
11752			/**
11753			 * This string gives information to the end user about the information
11754			 * that is current on display on the page. The following tokens can be
11755			 * used in the string and will be dynamically replaced as the table
11756			 * display updates. This tokens can be placed anywhere in the string, or
11757			 * removed as needed by the language requires:
11758			 *
11759			 * * `\_START\_` - Display index of the first record on the current page
11760			 * * `\_END\_` - Display index of the last record on the current page
11761			 * * `\_TOTAL\_` - Number of records in the table after filtering
11762			 * * `\_MAX\_` - Number of records in the table without filtering
11763			 * * `\_PAGE\_` - Current page number
11764			 * * `\_PAGES\_` - Total number of pages of data in the table
11765			 *
11766			 *  @type string
11767			 *  @default Showing _START_ to _END_ of _TOTAL_ entries
11768			 *
11769			 *  @dtopt Language
11770			 *  @name DataTable.defaults.language.info
11771			 *
11772			 *  @example
11773			 *    $(document).ready( function() {
11774			 *      $('#example').dataTable( {
11775			 *        "language": {
11776			 *          "info": "Showing page _PAGE_ of _PAGES_"
11777			 *        }
11778			 *      } );
11779			 *    } );
11780			 */
11781			"sInfo": "Showing _START_ to _END_ of _TOTAL_ entries",
11782	
11783	
11784			/**
11785			 * Display information string for when the table is empty. Typically the
11786			 * format of this string should match `info`.
11787			 *  @type string
11788			 *  @default Showing 0 to 0 of 0 entries
11789			 *
11790			 *  @dtopt Language
11791			 *  @name DataTable.defaults.language.infoEmpty
11792			 *
11793			 *  @example
11794			 *    $(document).ready( function() {
11795			 *      $('#example').dataTable( {
11796			 *        "language": {
11797			 *          "infoEmpty": "No entries to show"
11798			 *        }
11799			 *      } );
11800			 *    } );
11801			 */
11802			"sInfoEmpty": "Showing 0 to 0 of 0 entries",
11803	
11804	
11805			/**
11806			 * When a user filters the information in a table, this string is appended
11807			 * to the information (`info`) to give an idea of how strong the filtering
11808			 * is. The variable _MAX_ is dynamically updated.
11809			 *  @type string
11810			 *  @default (filtered from _MAX_ total entries)
11811			 *
11812			 *  @dtopt Language
11813			 *  @name DataTable.defaults.language.infoFiltered
11814			 *
11815			 *  @example
11816			 *    $(document).ready( function() {
11817			 *      $('#example').dataTable( {
11818			 *        "language": {
11819			 *          "infoFiltered": " - filtering from _MAX_ records"
11820			 *        }
11821			 *      } );
11822			 *    } );
11823			 */
11824			"sInfoFiltered": "(filtered from _MAX_ total entries)",
11825	
11826	
11827			/**
11828			 * If can be useful to append extra information to the info string at times,
11829			 * and this variable does exactly that. This information will be appended to
11830			 * the `info` (`infoEmpty` and `infoFiltered` in whatever combination they are
11831			 * being used) at all times.
11832			 *  @type string
11833			 *  @default <i>Empty string</i>
11834			 *
11835			 *  @dtopt Language
11836			 *  @name DataTable.defaults.language.infoPostFix
11837			 *
11838			 *  @example
11839			 *    $(document).ready( function() {
11840			 *      $('#example').dataTable( {
11841			 *        "language": {
11842			 *          "infoPostFix": "All records shown are derived from real information."
11843			 *        }
11844			 *      } );
11845			 *    } );
11846			 */
11847			"sInfoPostFix": "",
11848	
11849	
11850			/**
11851			 * This decimal place operator is a little different from the other
11852			 * language options since DataTables doesn't output floating point
11853			 * numbers, so it won't ever use this for display of a number. Rather,
11854			 * what this parameter does is modify the sort methods of the table so
11855			 * that numbers which are in a format which has a character other than
11856			 * a period (`.`) as a decimal place will be sorted numerically.
11857			 *
11858			 * Note that numbers with different decimal places cannot be shown in
11859			 * the same table and still be sortable, the table must be consistent.
11860			 * However, multiple different tables on the page can use different
11861			 * decimal place characters.
11862			 *  @type string
11863			 *  @default 
11864			 *
11865			 *  @dtopt Language
11866			 *  @name DataTable.defaults.language.decimal
11867			 *
11868			 *  @example
11869			 *    $(document).ready( function() {
11870			 *      $('#example').dataTable( {
11871			 *        "language": {
11872			 *          "decimal": ","
11873			 *          "thousands": "."
11874			 *        }
11875			 *      } );
11876			 *    } );
11877			 */
11878			"sDecimal": "",
11879	
11880	
11881			/**
11882			 * DataTables has a build in number formatter (`formatNumber`) which is
11883			 * used to format large numbers that are used in the table information.
11884			 * By default a comma is used, but this can be trivially changed to any
11885			 * character you wish with this parameter.
11886			 *  @type string
11887			 *  @default ,
11888			 *
11889			 *  @dtopt Language
11890			 *  @name DataTable.defaults.language.thousands
11891			 *
11892			 *  @example
11893			 *    $(document).ready( function() {
11894			 *      $('#example').dataTable( {
11895			 *        "language": {
11896			 *          "thousands": "'"
11897			 *        }
11898			 *      } );
11899			 *    } );
11900			 */
11901			"sThousands": ",",
11902	
11903	
11904			/**
11905			 * Detail the action that will be taken when the drop down menu for the
11906			 * pagination length option is changed. The '_MENU_' variable is replaced
11907			 * with a default select list of 10, 25, 50 and 100, and can be replaced
11908			 * with a custom select box if required.
11909			 *  @type string
11910			 *  @default Show _MENU_ entries
11911			 *
11912			 *  @dtopt Language
11913			 *  @name DataTable.defaults.language.lengthMenu
11914			 *
11915			 *  @example
11916			 *    // Language change only
11917			 *    $(document).ready( function() {
11918			 *      $('#example').dataTable( {
11919			 *        "language": {
11920			 *          "lengthMenu": "Display _MENU_ records"
11921			 *        }
11922			 *      } );
11923			 *    } );
11924			 *
11925			 *  @example
11926			 *    // Language and options change
11927			 *    $(document).ready( function() {
11928			 *      $('#example').dataTable( {
11929			 *        "language": {
11930			 *          "lengthMenu": 'Display <select>'+
11931			 *            '<option value="10">10</option>'+
11932			 *            '<option value="20">20</option>'+
11933			 *            '<option value="30">30</option>'+
11934			 *            '<option value="40">40</option>'+
11935			 *            '<option value="50">50</option>'+
11936			 *            '<option value="-1">All</option>'+
11937			 *            '</select> records'
11938			 *        }
11939			 *      } );
11940			 *    } );
11941			 */
11942			"sLengthMenu": "Show _MENU_ entries",
11943	
11944	
11945			/**
11946			 * When using Ajax sourced data and during the first draw when DataTables is
11947			 * gathering the data, this message is shown in an empty row in the table to
11948			 * indicate to the end user the the data is being loaded. Note that this
11949			 * parameter is not used when loading data by server-side processing, just
11950			 * Ajax sourced data with client-side processing.
11951			 *  @type string
11952			 *  @default Loading...
11953			 *
11954			 *  @dtopt Language
11955			 *  @name DataTable.defaults.language.loadingRecords
11956			 *
11957			 *  @example
11958			 *    $(document).ready( function() {
11959			 *      $('#example').dataTable( {
11960			 *        "language": {
11961			 *          "loadingRecords": "Please wait - loading..."
11962			 *        }
11963			 *      } );
11964			 *    } );
11965			 */
11966			"sLoadingRecords": "Loading...",
11967	
11968	
11969			/**
11970			 * Text which is displayed when the table is processing a user action
11971			 * (usually a sort command or similar).
11972			 *  @type string
11973			 *
11974			 *  @dtopt Language
11975			 *  @name DataTable.defaults.language.processing
11976			 *
11977			 *  @example
11978			 *    $(document).ready( function() {
11979			 *      $('#example').dataTable( {
11980			 *        "language": {
11981			 *          "processing": "DataTables is currently busy"
11982			 *        }
11983			 *      } );
11984			 *    } );
11985			 */
11986			"sProcessing": "",
11987	
11988	
11989			/**
11990			 * Details the actions that will be taken when the user types into the
11991			 * filtering input text box. The variable "_INPUT_", if used in the string,
11992			 * is replaced with the HTML text box for the filtering input allowing
11993			 * control over where it appears in the string. If "_INPUT_" is not given
11994			 * then the input box is appended to the string automatically.
11995			 *  @type string
11996			 *  @default Search:
11997			 *
11998			 *  @dtopt Language
11999			 *  @name DataTable.defaults.language.search
12000			 *
12001			 *  @example
12002			 *    // Input text box will be appended at the end automatically
12003			 *    $(document).ready( function() {
12004			 *      $('#example').dataTable( {
12005			 *        "language": {
12006			 *          "search": "Filter records:"
12007			 *        }
12008			 *      } );
12009			 *    } );
12010			 *
12011			 *  @example
12012			 *    // Specify where the filter should appear
12013			 *    $(document).ready( function() {
12014			 *      $('#example').dataTable( {
12015			 *        "language": {
12016			 *          "search": "Apply filter _INPUT_ to table"
12017			 *        }
12018			 *      } );
12019			 *    } );
12020			 */
12021			"sSearch": "Search:",
12022	
12023	
12024			/**
12025			 * Assign a `placeholder` attribute to the search `input` element
12026			 *  @type string
12027			 *  @default 
12028			 *
12029			 *  @dtopt Language
12030			 *  @name DataTable.defaults.language.searchPlaceholder
12031			 */
12032			"sSearchPlaceholder": "",
12033	
12034	
12035			/**
12036			 * All of the language information can be stored in a file on the
12037			 * server-side, which DataTables will look up if this parameter is passed.
12038			 * It must store the URL of the language file, which is in a JSON format,
12039			 * and the object has the same properties as the oLanguage object in the
12040			 * initialiser object (i.e. the above parameters). Please refer to one of
12041			 * the example language files to see how this works in action.
12042			 *  @type string
12043			 *  @default <i>Empty string - i.e. disabled</i>
12044			 *
12045			 *  @dtopt Language
12046			 *  @name DataTable.defaults.language.url
12047			 *
12048			 *  @example
12049			 *    $(document).ready( function() {
12050			 *      $('#example').dataTable( {
12051			 *        "language": {
12052			 *          "url": "http://www.sprymedia.co.uk/dataTables/lang.txt"
12053			 *        }
12054			 *      } );
12055			 *    } );
12056			 */
12057			"sUrl": "",
12058	
12059	
12060			/**
12061			 * Text shown inside the table records when the is no information to be
12062			 * displayed after filtering. `emptyTable` is shown when there is simply no
12063			 * information in the table at all (regardless of filtering).
12064			 *  @type string
12065			 *  @default No matching records found
12066			 *
12067			 *  @dtopt Language
12068			 *  @name DataTable.defaults.language.zeroRecords
12069			 *
12070			 *  @example
12071			 *    $(document).ready( function() {
12072			 *      $('#example').dataTable( {
12073			 *        "language": {
12074			 *          "zeroRecords": "No records to display"
12075			 *        }
12076			 *      } );
12077			 *    } );
12078			 */
12079			"sZeroRecords": "No matching records found"
12080		},
12081	
12082	
12083		/**
12084		 * This parameter allows you to have define the global filtering state at
12085		 * initialisation time. As an object the `search` parameter must be
12086		 * defined, but all other parameters are optional. When `regex` is true,
12087		 * the search string will be treated as a regular expression, when false
12088		 * (default) it will be treated as a straight string. When `smart`
12089		 * DataTables will use it's smart filtering methods (to word match at
12090		 * any point in the data), when false this will not be done.
12091		 *  @namespace
12092		 *  @extends DataTable.models.oSearch
12093		 *
12094		 *  @dtopt Options
12095		 *  @name DataTable.defaults.search
12096		 *
12097		 *  @example
12098		 *    $(document).ready( function() {
12099		 *      $('#example').dataTable( {
12100		 *        "search": {"search": "Initial search"}
12101		 *      } );
12102		 *    } )
12103		 */
12104		"oSearch": $.extend( {}, DataTable.models.oSearch ),
12105	
12106	
12107		/**
12108		 * __Deprecated__ The functionality provided by this parameter has now been
12109		 * superseded by that provided through `ajax`, which should be used instead.
12110		 *
12111		 * By default DataTables will look for the property `data` (or `aaData` for
12112		 * compatibility with DataTables 1.9-) when obtaining data from an Ajax
12113		 * source or for server-side processing - this parameter allows that
12114		 * property to be changed. You can use Javascript dotted object notation to
12115		 * get a data source for multiple levels of nesting.
12116		 *  @type string
12117		 *  @default data
12118		 *
12119		 *  @dtopt Options
12120		 *  @dtopt Server-side
12121		 *  @name DataTable.defaults.ajaxDataProp
12122		 *
12123		 *  @deprecated 1.10. Please use `ajax` for this functionality now.
12124		 */
12125		"sAjaxDataProp": "data",
12126	
12127	
12128		/**
12129		 * __Deprecated__ The functionality provided by this parameter has now been
12130		 * superseded by that provided through `ajax`, which should be used instead.
12131		 *
12132		 * You can instruct DataTables to load data from an external
12133		 * source using this parameter (use aData if you want to pass data in you
12134		 * already have). Simply provide a url a JSON object can be obtained from.
12135		 *  @type string
12136		 *  @default null
12137		 *
12138		 *  @dtopt Options
12139		 *  @dtopt Server-side
12140		 *  @name DataTable.defaults.ajaxSource
12141		 *
12142		 *  @deprecated 1.10. Please use `ajax` for this functionality now.
12143		 */
12144		"sAjaxSource": null,
12145	
12146	
12147		/**
12148		 * This initialisation variable allows you to specify exactly where in the
12149		 * DOM you want DataTables to inject the various controls it adds to the page
12150		 * (for example you might want the pagination controls at the top of the
12151		 * table). DIV elements (with or without a custom class) can also be added to
12152		 * aid styling. The follow syntax is used:
12153		 *   <ul>
12154		 *     <li>The following options are allowed:
12155		 *       <ul>
12156		 *         <li>'l' - Length changing</li>
12157		 *         <li>'f' - Filtering input</li>
12158		 *         <li>'t' - The table!</li>
12159		 *         <li>'i' - Information</li>
12160		 *         <li>'p' - Pagination</li>
12161		 *         <li>'r' - pRocessing</li>
12162		 *       </ul>
12163		 *     </li>
12164		 *     <li>The following constants are allowed:
12165		 *       <ul>
12166		 *         <li>'H' - jQueryUI theme "header" classes ('fg-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix')</li>
12167		 *         <li>'F' - jQueryUI theme "footer" classes ('fg-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix')</li>
12168		 *       </ul>
12169		 *     </li>
12170		 *     <li>The following syntax is expected:
12171		 *       <ul>
12172		 *         <li>'&lt;' and '&gt;' - div elements</li>
12173		 *         <li>'&lt;"class" and '&gt;' - div with a class</li>
12174		 *         <li>'&lt;"#id" and '&gt;' - div with an ID</li>
12175		 *       </ul>
12176		 *     </li>
12177		 *     <li>Examples:
12178		 *       <ul>
12179		 *         <li>'&lt;"wrapper"flipt&gt;'</li>
12180		 *         <li>'&lt;lf&lt;t&gt;ip&gt;'</li>
12181		 *       </ul>
12182		 *     </li>
12183		 *   </ul>
12184		 *  @type string
12185		 *  @default lfrtip <i>(when `jQueryUI` is false)</i> <b>or</b>
12186		 *    <"H"lfr>t<"F"ip> <i>(when `jQueryUI` is true)</i>
12187		 *
12188		 *  @dtopt Options
12189		 *  @name DataTable.defaults.dom
12190		 *
12191		 *  @example
12192		 *    $(document).ready( function() {
12193		 *      $('#example').dataTable( {
12194		 *        "dom": '&lt;"top"i&gt;rt&lt;"bottom"flp&gt;&lt;"clear"&gt;'
12195		 *      } );
12196		 *    } );
12197		 */
12198		"sDom": "lfrtip",
12199	
12200	
12201		/**
12202		 * Search delay option. This will throttle full table searches that use the
12203		 * DataTables provided search input element (it does not effect calls to
12204		 * `dt-api search()`, providing a delay before the search is made.
12205		 *  @type integer
12206		 *  @default 0
12207		 *
12208		 *  @dtopt Options
12209		 *  @name DataTable.defaults.searchDelay
12210		 *
12211		 *  @example
12212		 *    $(document).ready( function() {
12213		 *      $('#example').dataTable( {
12214		 *        "searchDelay": 200
12215		 *      } );
12216		 *    } )
12217		 */
12218		"searchDelay": null,
12219	
12220	
12221		/**
12222		 * DataTables features six different built-in options for the buttons to
12223		 * display for pagination control:
12224		 *
12225		 * * `numbers` - Page number buttons only
12226		 * * `simple` - 'Previous' and 'Next' buttons only
12227		 * * 'simple_numbers` - 'Previous' and 'Next' buttons, plus page numbers
12228		 * * `full` - 'First', 'Previous', 'Next' and 'Last' buttons
12229		 * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus page numbers
12230		 * * `first_last_numbers` - 'First' and 'Last' buttons, plus page numbers
12231		 *  
12232		 * Further methods can be added using {@link DataTable.ext.oPagination}.
12233		 *  @type string
12234		 *  @default simple_numbers
12235		 *
12236		 *  @dtopt Options
12237		 *  @name DataTable.defaults.pagingType
12238		 *
12239		 *  @example
12240		 *    $(document).ready( function() {
12241		 *      $('#example').dataTable( {
12242		 *        "pagingType": "full_numbers"
12243		 *      } );
12244		 *    } )
12245		 */
12246		"sPaginationType": "simple_numbers",
12247	
12248	
12249		/**
12250		 * Enable horizontal scrolling. When a table is too wide to fit into a
12251		 * certain layout, or you have a large number of columns in the table, you
12252		 * can enable x-scrolling to show the table in a viewport, which can be
12253		 * scrolled. This property can be `true` which will allow the table to
12254		 * scroll horizontally when needed, or any CSS unit, or a number (in which
12255		 * case it will be treated as a pixel measurement). Setting as simply `true`
12256		 * is recommended.
12257		 *  @type boolean|string
12258		 *  @default <i>blank string - i.e. disabled</i>
12259		 *
12260		 *  @dtopt Features
12261		 *  @name DataTable.defaults.scrollX
12262		 *
12263		 *  @example
12264		 *    $(document).ready( function() {
12265		 *      $('#example').dataTable( {
12266		 *        "scrollX": true,
12267		 *        "scrollCollapse": true
12268		 *      } );
12269		 *    } );
12270		 */
12271		"sScrollX": "",
12272	
12273	
12274		/**
12275		 * This property can be used to force a DataTable to use more width than it
12276		 * might otherwise do when x-scrolling is enabled. For example if you have a
12277		 * table which requires to be well spaced, this parameter is useful for
12278		 * "over-sizing" the table, and thus forcing scrolling. This property can by
12279		 * any CSS unit, or a number (in which case it will be treated as a pixel
12280		 * measurement).
12281		 *  @type string
12282		 *  @default <i>blank string - i.e. disabled</i>
12283		 *
12284		 *  @dtopt Options
12285		 *  @name DataTable.defaults.scrollXInner
12286		 *
12287		 *  @example
12288		 *    $(document).ready( function() {
12289		 *      $('#example').dataTable( {
12290		 *        "scrollX": "100%",
12291		 *        "scrollXInner": "110%"
12292		 *      } );
12293		 *    } );
12294		 */
12295		"sScrollXInner": "",
12296	
12297	
12298		/**
12299		 * Enable vertical scrolling. Vertical scrolling will constrain the DataTable
12300		 * to the given height, and enable scrolling for any data which overflows the
12301		 * current viewport. This can be used as an alternative to paging to display
12302		 * a lot of data in a small area (although paging and scrolling can both be
12303		 * enabled at the same time). This property can be any CSS unit, or a number
12304		 * (in which case it will be treated as a pixel measurement).
12305		 *  @type string
12306		 *  @default <i>blank string - i.e. disabled</i>
12307		 *
12308		 *  @dtopt Features
12309		 *  @name DataTable.defaults.scrollY
12310		 *
12311		 *  @example
12312		 *    $(document).ready( function() {
12313		 *      $('#example').dataTable( {
12314		 *        "scrollY": "200px",
12315		 *        "paginate": false
12316		 *      } );
12317		 *    } );
12318		 */
12319		"sScrollY": "",
12320	
12321	
12322		/**
12323		 * __Deprecated__ The functionality provided by this parameter has now been
12324		 * superseded by that provided through `ajax`, which should be used instead.
12325		 *
12326		 * Set the HTTP method that is used to make the Ajax call for server-side
12327		 * processing or Ajax sourced data.
12328		 *  @type string
12329		 *  @default GET
12330		 *
12331		 *  @dtopt Options
12332		 *  @dtopt Server-side
12333		 *  @name DataTable.defaults.serverMethod
12334		 *
12335		 *  @deprecated 1.10. Please use `ajax` for this functionality now.
12336		 */
12337		"sServerMethod": "GET",
12338	
12339	
12340		/**
12341		 * DataTables makes use of renderers when displaying HTML elements for
12342		 * a table. These renderers can be added or modified by plug-ins to
12343		 * generate suitable mark-up for a site. For example the Bootstrap
12344		 * integration plug-in for DataTables uses a paging button renderer to
12345		 * display pagination buttons in the mark-up required by Bootstrap.
12346		 *
12347		 * For further information about the renderers available see
12348		 * DataTable.ext.renderer
12349		 *  @type string|object
12350		 *  @default null
12351		 *
12352		 *  @name DataTable.defaults.renderer
12353		 *
12354		 */
12355		"renderer": null,
12356	
12357	
12358		/**
12359		 * Set the data property name that DataTables should use to get a row's id
12360		 * to set as the `id` property in the node.
12361		 *  @type string
12362		 *  @default DT_RowId
12363		 *
12364		 *  @name DataTable.defaults.rowId
12365		 */
12366		"rowId": "DT_RowId"
12367	};
12368	
12369	_fnHungarianMap( DataTable.defaults );
12370	
12371	
12372	
12373	/*
12374	 * Developer note - See note in model.defaults.js about the use of Hungarian
12375	 * notation and camel case.
12376	 */
12377	
12378	/**
12379	 * Column options that can be given to DataTables at initialisation time.
12380	 *  @namespace
12381	 */
12382	DataTable.defaults.column = {
12383		/**
12384		 * Define which column(s) an order will occur on for this column. This
12385		 * allows a column's ordering to take multiple columns into account when
12386		 * doing a sort or use the data from a different column. For example first
12387		 * name / last name columns make sense to do a multi-column sort over the
12388		 * two columns.
12389		 *  @type array|int
12390		 *  @default null <i>Takes the value of the column index automatically</i>
12391		 *
12392		 *  @name DataTable.defaults.column.orderData
12393		 *  @dtopt Columns
12394		 *
12395		 *  @example
12396		 *    // Using `columnDefs`
12397		 *    $(document).ready( function() {
12398		 *      $('#example').dataTable( {
12399		 *        "columnDefs": [
12400		 *          { "orderData": [ 0, 1 ], "targets": [ 0 ] },
12401		 *          { "orderData": [ 1, 0 ], "targets": [ 1 ] },
12402		 *          { "orderData": 2, "targets": [ 2 ] }
12403		 *        ]
12404		 *      } );
12405		 *    } );
12406		 *
12407		 *  @example
12408		 *    // Using `columns`
12409		 *    $(document).ready( function() {
12410		 *      $('#example').dataTable( {
12411		 *        "columns": [
12412		 *          { "orderData": [ 0, 1 ] },
12413		 *          { "orderData": [ 1, 0 ] },
12414		 *          { "orderData": 2 },
12415		 *          null,
12416		 *          null
12417		 *        ]
12418		 *      } );
12419		 *    } );
12420		 */
12421		"aDataSort": null,
12422		"iDataSort": -1,
12423	
12424	
12425		/**
12426		 * You can control the default ordering direction, and even alter the
12427		 * behaviour of the sort handler (i.e. only allow ascending ordering etc)
12428		 * using this parameter.
12429		 *  @type array
12430		 *  @default [ 'asc', 'desc' ]
12431		 *
12432		 *  @name DataTable.defaults.column.orderSequence
12433		 *  @dtopt Columns
12434		 *
12435		 *  @example
12436		 *    // Using `columnDefs`
12437		 *    $(document).ready( function() {
12438		 *      $('#example').dataTable( {
12439		 *        "columnDefs": [
12440		 *          { "orderSequence": [ "asc" ], "targets": [ 1 ] },
12441		 *          { "orderSequence": [ "desc", "asc", "asc" ], "targets": [ 2 ] },
12442		 *          { "orderSequence": [ "desc" ], "targets": [ 3 ] }
12443		 *        ]
12444		 *      } );
12445		 *    } );
12446		 *
12447		 *  @example
12448		 *    // Using `columns`
12449		 *    $(document).ready( function() {
12450		 *      $('#example').dataTable( {
12451		 *        "columns": [
12452		 *          null,
12453		 *          { "orderSequence": [ "asc" ] },
12454		 *          { "orderSequence": [ "desc", "asc", "asc" ] },
12455		 *          { "orderSequence": [ "desc" ] },
12456		 *          null
12457		 *        ]
12458		 *      } );
12459		 *    } );
12460		 */
12461		"asSorting": [ 'asc', 'desc' ],
12462	
12463	
12464		/**
12465		 * Enable or disable filtering on the data in this column.
12466		 *  @type boolean
12467		 *  @default true
12468		 *
12469		 *  @name DataTable.defaults.column.searchable
12470		 *  @dtopt Columns
12471		 *
12472		 *  @example
12473		 *    // Using `columnDefs`
12474		 *    $(document).ready( function() {
12475		 *      $('#example').dataTable( {
12476		 *        "columnDefs": [
12477		 *          { "searchable": false, "targets": [ 0 ] }
12478		 *        ] } );
12479		 *    } );
12480		 *
12481		 *  @example
12482		 *    // Using `columns`
12483		 *    $(document).ready( function() {
12484		 *      $('#example').dataTable( {
12485		 *        "columns": [
12486		 *          { "searchable": false },
12487		 *          null,
12488		 *          null,
12489		 *          null,
12490		 *          null
12491		 *        ] } );
12492		 *    } );
12493		 */
12494		"bSearchable": true,
12495	
12496	
12497		/**
12498		 * Enable or disable ordering on this column.
12499		 *  @type boolean
12500		 *  @default true
12501		 *
12502		 *  @name DataTable.defaults.column.orderable
12503		 *  @dtopt Columns
12504		 *
12505		 *  @example
12506		 *    // Using `columnDefs`
12507		 *    $(document).ready( function() {
12508		 *      $('#example').dataTable( {
12509		 *        "columnDefs": [
12510		 *          { "orderable": false, "targets": [ 0 ] }
12511		 *        ] } );
12512		 *    } );
12513		 *
12514		 *  @example
12515		 *    // Using `columns`
12516		 *    $(document).ready( function() {
12517		 *      $('#example').dataTable( {
12518		 *        "columns": [
12519		 *          { "orderable": false },
12520		 *          null,
12521		 *          null,
12522		 *          null,
12523		 *          null
12524		 *        ] } );
12525		 *    } );
12526		 */
12527		"bSortable": true,
12528	
12529	
12530		/**
12531		 * Enable or disable the display of this column.
12532		 *  @type boolean
12533		 *  @default true
12534		 *
12535		 *  @name DataTable.defaults.column.visible
12536		 *  @dtopt Columns
12537		 *
12538		 *  @example
12539		 *    // Using `columnDefs`
12540		 *    $(document).ready( function() {
12541		 *      $('#example').dataTable( {
12542		 *        "columnDefs": [
12543		 *          { "visible": false, "targets": [ 0 ] }
12544		 *        ] } );
12545		 *    } );
12546		 *
12547		 *  @example
12548		 *    // Using `columns`
12549		 *    $(document).ready( function() {
12550		 *      $('#example').dataTable( {
12551		 *        "columns": [
12552		 *          { "visible": false },
12553		 *          null,
12554		 *          null,
12555		 *          null,
12556		 *          null
12557		 *        ] } );
12558		 *    } );
12559		 */
12560		"bVisible": true,
12561	
12562	
12563		/**
12564		 * Developer definable function that is called whenever a cell is created (Ajax source,
12565		 * etc) or processed for input (DOM source). This can be used as a compliment to mRender
12566		 * allowing you to modify the DOM element (add background colour for example) when the
12567		 * element is available.
12568		 *  @type function
12569		 *  @param {element} td The TD node that has been created
12570		 *  @param {*} cellData The Data for the cell
12571		 *  @param {array|object} rowData The data for the whole row
12572		 *  @param {int} row The row index for the aoData data store
12573		 *  @param {int} col The column index for aoColumns
12574		 *
12575		 *  @name DataTable.defaults.column.createdCell
12576		 *  @dtopt Columns
12577		 *
12578		 *  @example
12579		 *    $(document).ready( function() {
12580		 *      $('#example').dataTable( {
12581		 *        "columnDefs": [ {
12582		 *          "targets": [3],
12583		 *          "createdCell": function (td, cellData, rowData, row, c
12583ol) {
12584		 *            if ( cellData == "1.7" ) {
12585		 *              $(td).css('color', 'blue')
12586		 *            }
12587		 *          }
12588		 *        } ]
12589		 *      });
12590		 *    } );
12591		 */
12592		"fnCreatedCell": null,
12593	
12594	
12595		/**
12596		 * This parameter has been replaced by `data` in DataTables to ensure naming
12597		 * consistency. `dataProp` can still be used, as there is backwards
12598		 * compatibility in DataTables for this option, but it is strongly
12599		 * recommended that you use `data` in preference to `dataProp`.
12600		 *  @name DataTable.defaults.column.dataProp
12601		 */
12602	
12603	
12604		/**
12605		 * This property can be used to read data from any data source property,
12606		 * including deeply nested objects / properties. `data` can be given in a
12607		 * number of different ways which effect its behaviour:
12608		 *
12609		 * * `integer` - treated as an array index for the data source. This is the
12610		 *   default that DataTables uses (incrementally increased for each column).
12611		 * * `string` - read an object property from the data source. There are
12612		 *   three 'special' options that can be used in the string to alter how
12613		 *   DataTables reads the data from the source object:
12614		 *    * `.` - Dotted Javascript notation. Just as you use a `.` in
12615		 *      Javascript to read from nested objects, so to can the options
12616		 *      specified in `data`. For example: `browser.version` or
12617		 *      `browser.name`. If your object parameter name contains a period, use
12618		 *      `\\` to escape it - i.e. `first\\.name`.
12619		 *    * `[]` - Array notation. DataTables can automatically combine data
12620		 *      from and array source, joining the data with the characters provided
12621		 *      between the two brackets. For example: `name[, ]` would provide a
12622		 *      comma-space separated list from the source array. If no characters
12623		 *      are provided between the brackets, the original array source is
12624		 *      returned.
12625		 *    * `()` - Function notation. Adding `()` to the end of a parameter will
12626		 *      execute a function of the name given. For example: `browser()` for a
12627		 *      simple function on the data source, `browser.version()` for a
12628		 *      function in a nested property or even `browser().version` to get an
12629		 *      object property if the function called returns an object. Note that
12630		 *      function notation is recommended for use in `render` rather than
12631		 *      `data` as it is much simpler to use as a renderer.
12632		 * * `null` - use the original data source for the row rather than plucking
12633		 *   data directly from it. This action has effects on two other
12634		 *   initialisation options:
12635		 *    * `defaultContent` - When null is given as the `data` option and
12636		 *      `defaultContent` is specified for the column, the value defined by
12637		 *      `defaultContent` will be used for the cell.
12638		 *    * `render` - When null is used for the `data` option and the `render`
12639		 *      option is specified for the column, the whole data source for the
12640		 *      row is used for the renderer.
12641		 * * `function` - the function given will be executed whenever DataTables
12642		 *   needs to set or get the data for a cell in the column. The function
12643		 *   takes three parameters:
12644		 *    * Parameters:
12645		 *      * `{array|object}` The data source for the row
12646		 *      * `{string}` The type call data requested - this will be 'set' when
12647		 *        setting data or 'filter', 'display', 'type', 'sort' or undefined
12648		 *        when gathering data. Note that when `undefined` is given for the
12649		 *        type DataTables expects to get the raw data for the object back<
12650		 *      * `{*}` Data to set when the second parameter is 'set'.
12651		 *    * Return:
12652		 *      * The return value from the function is not required when 'set' is
12653		 *        the type of call, but otherwise the return is what will be used
12654		 *        for the data requested.
12655		 *
12656		 * Note that `data` is a getter and setter option. If you just require
12657		 * formatting of data for output, you will likely want to use `render` which
12658		 * is simply a getter and thus simpler to use.
12659		 *
12660		 * Note that prior to DataTables 1.9.2 `data` was called `mDataProp`. The
12661		 * name change reflects the flexibility of this property and is consistent
12662		 * with the naming of mRender. If 'mDataProp' is given, then it will still
12663		 * be used by DataTables, as it automatically maps the old name to the new
12664		 * if required.
12665		 *
12666		 *  @type string|int|function|null
12667		 *  @default null <i>Use automatically calculated column index</i>
12668		 *
12669		 *  @name DataTable.defaults.column.data
12670		 *  @dtopt Columns
12671		 *
12672		 *  @example
12673		 *    // Read table data from objects
12674		 *    // JSON structure for each row:
12675		 *    //   {
12676		 *    //      "engine": {value},
12677		 *    //      "browser": {value},
12678		 *    //      "platform": {value},
12679		 *    //      "version": {value},
12680		 *    //      "grade": {value}
12681		 *    //   }
12682		 *    $(document).ready( function() {
12683		 *      $('#example').dataTable( {
12684		 *        "ajaxSource": "sources/objects.txt",
12685		 *        "columns": [
12686		 *          { "data": "engine" },
12687		 *          { "data": "browser" },
12688		 *          { "data": "platform" },
12689		 *          { "data": "version" },
12690		 *          { "data": "grade" }
12691		 *        ]
12692		 *      } );
12693		 *    } );
12694		 *
12695		 *  @example
12696		 *    // Read information from deeply nested objects
12697		 *    // JSON structure for each row:
12698		 *    //   {
12699		 *    //      "engine": {value},
12700		 *    //      "browser": {value},
12701		 *    //      "platform": {
12702		 *    //         "inner": {value}
12703		 *    //      },
12704		 *    //      "details": [
12705		 *    //         {value}, {value}
12706		 *    //      ]
12707		 *    //   }
12708		 *    $(document).ready( function() {
12709		 *      $('#example').dataTable( {
12710		 *        "ajaxSource": "sources/deep.txt",
12711		 *        "columns": [
12712		 *          { "data": "engine" },
12713		 *          { "data": "browser" },
12714		 *          { "data": "platform.inner" },
12715		 *          { "data": "details.0" },
12716		 *          { "data": "details.1" }
12717		 *        ]
12718		 *      } );
12719		 *    } );
12720		 *
12721		 *  @example
12722		 *    // Using `data` as a function to provide different information for
12723		 *    // sorting, filtering and display. In this case, currency (price)
12724		 *    $(document).ready( function() {
12725		 *      $('#example').dataTable( {
12726		 *        "columnDefs": [ {
12727		 *          "targets": [ 0 ],
12728		 *          "data": function ( source, type, val ) {
12729		 *            if (type === 'set') {
12730		 *              source.price = val;
12731		 *              // Store the computed display and filter values for efficiency
12732		 *              source.price_display = val=="" ? "" : "$"+numberFormat(val);
12733		 *              source.price_filter  = val=="" ? "" : "$"+numberFormat(val)+" "+val;
12734		 *              return;
12735		 *            }
12736		 *            else if (type === 'display') {
12737		 *              return source.price_display;
12738		 *            }
12739		 *            else if (type === 'filter') {
12740		 *              return source.price_filter;
12741		 *            }
12742		 *            // 'sort', 'type' and undefined all just use the integer
12743		 *            return source.price;
12744		 *          }
12745		 *        } ]
12746		 *      } );
12747		 *    } );
12748		 *
12749		 *  @example
12750		 *    // Using default content
12751		 *    $(document).ready( function() {
12752		 *      $('#example').dataTable( {
12753		 *        "columnDefs": [ {
12754		 *          "targets": [ 0 ],
12755		 *          "data": null,
12756		 *          "defaultContent": "Click to edit"
12757		 *        } ]
12758		 *      } );
12759		 *    } );
12760		 *
12761		 *  @example
12762		 *    // Using array notation - outputting a list from an array
12763		 *    $(document).ready( function() {
12764		 *      $('#example').dataTable( {
12765		 *        "columnDefs": [ {
12766		 *          "targets": [ 0 ],
12767		 *          "data": "name[, ]"
12768		 *        } ]
12769		 *      } );
12770		 *    } );
12771		 *
12772		 */
12773		"mData": null,
12774	
12775	
12776		/**
12777		 * This property is the rendering partner to `data` and it is suggested that
12778		 * when you want to manipulate data for display (including filtering,
12779		 * sorting etc) without altering the underlying data for the table, use this
12780		 * property. `render` can be considered to be the the read only companion to
12781		 * `data` which is read / write (then as such more complex). Like `data`
12782		 * this option can be given in a number of different ways to effect its
12783		 * behaviour:
12784		 *
12785		 * * `integer` - treated as an array index for the data source. This is the
12786		 *   default that DataTables uses (incrementally increased for each column).
12787		 * * `string` - read an object property from the data source. There are
12788		 *   three 'special' options that can be used in the string to alter how
12789		 *   DataTables reads the data from the source object:
12790		 *    * `.` - Dotted Javascript notation. Just as you use a `.` in
12791		 *      Javascript to read from nested objects, so to can the options
12792		 *      specified in `data`. For example: `browser.version` or
12793		 *      `browser.name`. If your object parameter name contains a period, use
12794		 *      `\\` to escape it - i.e. `first\\.name`.
12795		 *    * `[]` - Array notation. DataTables can automatically combine data
12796		 *      from and array source, joining the data with the characters provided
12797		 *      between the two brackets. For example: `name[, ]` would provide a
12798		 *      comma-space separated list from the source array. If no characters
12799		 *      are provided between the brackets, the original array source is
12800		 *      returned.
12801		 *    * `()` - Function notation. Adding `()` to the end of a parameter will
12802		 *      execute a function of the name given. For example: `browser()` for a
12803		 *      simple function on the data source, `browser.version()` for a
12804		 *      function in a nested property or even `browser().version` to get an
12805		 *      object property if the function called returns an object.
12806		 * * `object` - use different data for the different data types requested by
12807		 *   DataTables ('filter', 'display', 'type' or 'sort'). The property names
12808		 *   of the object is the data type the property refers to and the value can
12809		 *   defined using an integer, string or function using the same rules as
12810		 *   `render` normally does. Note that an `_` option _must_ be specified.
12811		 *   This is the default value to use if you haven't specified a value for
12812		 *   the data type requested by DataTables.
12813		 * * `function` - the function given will be executed whenever DataTables
12814		 *   needs to set or get the data for a cell in the column. The function
12815		 *   takes three parameters:
12816		 *    * Parameters:
12817		 *      * {array|object} The data source for the row (based on `data`)
12818		 *      * {string} The type call data requested - this will be 'filter',
12819		 *        'display', 'type' or 'sort'.
12820		 *      * {array|object} The full data source for the row (not based on
12821		 *        `data`)
12822		 *    * Return:
12823		 *      * The return value from the function is what will be used for the
12824		 *        data requested.
12825		 *
12826		 *  @type string|int|function|object|null
12827		 *  @default null Use the data source value.
12828		 *
12829		 *  @name DataTable.defaults.column.render
12830		 *  @dtopt Columns
12831		 *
12832		 *  @example
12833		 *    // Create a comma separated list from an array of objects
12834		 *    $(document).ready( function() {
12835		 *      $('#example').dataTable( {
12836		 *        "ajaxSource": "sources/deep.txt",
12837		 *        "columns": [
12838		 *          { "data": "engine" },
12839		 *          { "data": "browser" },
12840		 *          {
12841		 *            "data": "platform",
12842		 *            "render": "[, ].name"
12843		 *          }
12844		 *        ]
12845		 *      } );
12846		 *    } );
12847		 *
12848		 *  @example
12849		 *    // Execute a function to obtain data
12850		 *    $(document).ready( function() {
12851		 *      $('#example').dataTable( {
12852		 *        "columnDefs": [ {
12853		 *          "targets": [ 0 ],
12854		 *          "data": null, // Use the full data source object for the renderer's source
12855		 *          "render": "browserName()"
12856		 *        } ]
12857		 *      } );
12858		 *    } );
12859		 *
12860		 *  @example
12861		 *    // As an object, extracting different data for the different types
12862		 *    // This would be used with a data source such as:
12863		 *    //   { "phone": 5552368, "phone_filter": "5552368 555-2368", "phone_display": "555-2368" }
12864		 *    // Here the `phone` integer is used for sorting and type detection, while `phone_filter`
12865		 *    // (which has both forms) is used for filtering for if a user inputs either format, while
12866		 *    // the formatted phone number is the one that is shown in the table.
12867		 *    $(document).ready( function() {
12868		 *      $('#example').dataTable( {
12869		 *        "columnDefs": [ {
12870		 *          "targets": [ 0 ],
12871		 *          "data": null, // Use the full data source object for the renderer's source
12872		 *          "render": {
12873		 *            "_": "phone",
12874		 *            "filter": "phone_filter",
12875		 *            "display": "phone_display"
12876		 *          }
12877		 *        } ]
12878		 *      } );
12879		 *    } );
12880		 *
12881		 *  @example
12882		 *    // Use as a function to create a link from the data source
12883		 *    $(document).ready( function() {
12884		 *      $('#example').dataTable( {
12885		 *        "columnDefs": [ {
12886		 *          "targets": [ 0 ],
12887		 *          "data": "download_link",
12888		 *          "render": function ( data, type, full ) {
12889		 *            return '<a href="'+data+'">Download</a>';
12890		 *          }
12891		 *        } ]
12892		 *      } );
12893		 *    } );
12894		 */
12895		"mRender": null,
12896	
12897	
12898		/**
12899		 * Change the cell type created for the column - either TD cells or TH cells. This
12900		 * can be useful as TH cells have semantic meaning in the table body, allowing them
12901		 * to act as a header for a row (you may wish to add scope='row' to the TH elements).
12902		 *  @type string
12903		 *  @default td
12904		 *
12905		 *  @name DataTable.defaults.column.cellType
12906		 *  @dtopt Columns
12907		 *
12908		 *  @example
12909		 *    // Make the first column use TH cells
12910		 *    $(document).ready( function() {
12911		 *      $('#example').dataTable( {
12912		 *        "columnDefs": [ {
12913		 *          "targets": [ 0 ],
12914		 *          "cellType": "th"
12915		 *        } ]
12916		 *      } );
12917		 *    } );
12918		 */
12919		"sCellType": "td",
12920	
12921	
12922		/**
12923		 * Class to give to each cell in this column.
12924		 *  @type string
12925		 *  @default <i>Empty string</i>
12926		 *
12927		 *  @name DataTable.defaults.column.class
12928		 *  @dtopt Columns
12929		 *
12930		 *  @example
12931		 *    // Using `columnDefs`
12932		 *    $(document).ready( function() {
12933		 *      $('#example').dataTable( {
12934		 *        "columnDefs": [
12935		 *          { "class": "my_class", "targets": [ 0 ] }
12936		 *        ]
12937		 *      } );
12938		 *    } );
12939		 *
12940		 *  @example
12941		 *    // Using `columns`
12942		 *    $(document).ready( function() {
12943		 *      $('#example').dataTable( {
12944		 *        "columns": [
12945		 *          { "class": "my_class" },
12946		 *          null,
12947		 *          null,
12948		 *          null,
12949		 *          null
12950		 *        ]
12951		 *      } );
12952		 *    } );
12953		 */
12954		"sClass": "",
12955	
12956		/**
12957		 * When DataTables calculates the column widths to assign to each column,
12958		 * it finds the longest string in each column and then constructs a
12959		 * temporary table and reads the widths from that. The problem with this
12960		 * is that "mmm" is much wider then "iiii", but the latter is a longer
12961		 * string - thus the calculation can go wrong (doing it properly and putting
12962		 * it into an DOM object and measuring that is horribly(!) slow). Thus as
12963		 * a "work around" we provide this option. It will append its value to the
12964		 * text that is found to be the longest string for the column - i.e. padding.
12965		 * Generally you shouldn't need this!
12966		 *  @type string
12967		 *  @default <i>Empty string<i>
12968		 *
12969		 *  @name DataTable.defaults.column.contentPadding
12970		 *  @dtopt Columns
12971		 *
12972		 *  @example
12973		 *    // Using `columns`
12974		 *    $(document).ready( function() {
12975		 *      $('#example').dataTable( {
12976		 *        "columns": [
12977		 *          null,
12978		 *          null,
12979		 *          null,
12980		 *          {
12981		 *            "contentPadding": "mmm"
12982		 *          }
12983		 *        ]
12984		 *      } );
12985		 *    } );
12986		 */
12987		"sContentPadding": "",
12988	
12989	
12990		/**
12991		 * Allows a default value to be given for a column's data, and will be used
12992		 * whenever a null data source is encountered (this can be because `data`
12993		 * is set to null, or because the data source itself is null).
12994		 *  @type string
12995		 *  @default null
12996		 *
12997		 *  @name DataTable.defaults.column.defaultContent
12998		 *  @dtopt Columns
12999		 *
13000		 *  @example
13001		 *    // Using `columnDefs`
13002		 *    $(document).ready( function() {
13003		 *      $('#example').dataTable( {
13004		 *        "columnDefs": [
13005		 *          {
13006		 *            "data": null,
13007		 *            "defaultContent": "Edit",
13008		 *            "targets": [ -1 ]
13009		 *          }
13010		 *        ]
13011		 *      } );
13012		 *    } );
13013		 *
13014		 *  @example
13015		 *    // Using `columns`
13016		 *    $(document).ready( function() {
13017		 *      $('#example').dataTable( {
13018		 *        "columns": [
13019		 *          null,
13020		 *          null,
13021		 *          null,
13022		 *          {
13023		 *            "data": null,
13024		 *            "defaultContent": "Edit"
13025		 *          }
13026		 *        ]
13027		 *      } );
13028		 *    } );
13029		 */
13030		"sDefaultContent": null,
13031	
13032	
13033		/**
13034		 * This parameter is only used in DataTables' server-side processing. It can
13035		 * be exceptionally useful to know what columns are being displayed on the
13036		 * client side, and to map these to database fields. When defined, the names
13037		 * also allow DataTables to reorder information from the server if it comes
13038		 * back in an unexpected order (i.e. if you switch your columns around on the
13039		 * client-side, your server-side code does not also need updating).
13040		 *  @type string
13041		 *  @default <i>Empty string</i>
13042		 *
13043		 *  @name DataTable.defaults.column.name
13044		 *  @dtopt Columns
13045		 *
13046		 *  @example
13047		 *    // Using `columnDefs`
13048		 *    $(document).ready( function() {
13049		 *      $('#example').dataTable( {
13050		 *        "columnDefs": [
13051		 *          { "name": "engine", "targets": [ 0 ] },
13052		 *          { "name": "browser", "targets": [ 1 ] },
13053		 *          { "name": "platform", "targets": [ 2 ] },
13054		 *          { "name": "version", "targets": [ 3 ] },
13055		 *          { "name": "grade", "targets": [ 4 ] }
13056		 *        ]
13057		 *      } );
13058		 *    } );
13059		 *
13060		 *  @example
13061		 *    // Using `columns`
13062		 *    $(document).ready( function() {
13063		 *      $('#example').dataTable( {
13064		 *        "columns": [
13065		 *          { "name": "engine" },
13066		 *          { "name": "browser" },
13067		 *          { "name": "platform" },
13068		 *          { "name": "version" },
13069		 *          { "name": "grade" }
13070		 *        ]
13071		 *      } );
13072		 *    } );
13073		 */
13074		"sName": "",
13075	
13076	
13077		/**
13078		 * Defines a data source type for the ordering which can be used to read
13079		 * real-time information from the table (updating the internally cached
13080		 * version) prior to ordering. This allows ordering to occur on user
13081		 * editable elements such as form inputs.
13082		 *  @type string
13083		 *  @default std
13084		 *
13085		 *  @name DataTable.defaults.column.orderDataType
13086		 *  @dtopt Columns
13087		 *
13088		 *  @example
13089		 *    // Using `columnDefs`
13090		 *    $(document).ready( function() {
13091		 *      $('#example').dataTable( {
13092		 *        "columnDefs": [
13093		 *          { "orderDataType": "dom-text", "targets": [ 2, 3 ] },
13094		 *          { "type": "numeric", "targets": [ 3 ] },
13095		 *          { "orderDataType": "dom-select", "targets": [ 4 ] },
13096		 *          { "orderDataType": "dom-checkbox", "targets": [ 5 ] }
13097		 *        ]
13098		 *      } );
13099		 *    } );
13100		 *
13101		 *  @example
13102		 *    // Using `columns`
13103		 *    $(document).ready( function() {
13104		 *      $('#example').dataTable( {
13105		 *        "columns": [
13106		 *          null,
13107		 *          null,
13108		 *          { "orderDataType": "dom-text" },
13109		 *          { "orderDataType": "dom-text", "type": "numeric" },
13110		 *          { "orderDataType": "dom-select" },
13111		 *          { "orderDataType": "dom-checkbox" }
13112		 *        ]
13113		 *      } );
13114		 *    } );
13115		 */
13116		"sSortDataType": "std",
13117	
13118	
13119		/**
13120		 * The title of this column.
13121		 *  @type string
13122		 *  @default null <i>Derived from the 'TH' value for this column in the
13123		 *    original HTML table.</i>
13124		 *
13125		 *  @name DataTable.defaults.column.title
13126		 *  @dtopt Columns
13127		 *
13128		 *  @example
13129		 *    // Using `columnDefs`
13130		 *    $(document).ready( function() {
13131		 *      $('#example').dataTable( {
13132		 *        "columnDefs": [
13133		 *          { "title": "My column title", "targets": [ 0 ] }
13134		 *        ]
13135		 *      } );
13136		 *    } );
13137		 *
13138		 *  @example
13139		 *    // Using `columns`
13140		 *    $(document).ready( function() {
13141		 *      $('#example').dataTable( {
13142		 *        "columns": [
13143		 *          { "title": "My column title" },
13144		 *          null,
13145		 *          null,
13146		 *          null,
13147		 *          null
13148		 *        ]
13149		 *      } );
13150		 *    } );
13151		 */
13152		"sTitle": null,
13153	
13154	
13155		/**
13156		 * The type allows you to specify how the data for this column will be
13157		 * ordered. Four types (string, numeric, date and html (which will strip
13158		 * HTML tags before ordering)) are currently available. Note that only date
13159		 * formats understood by Javascript's Date() object will be accepted as type
13160		 * date. For example: "Mar 26, 2008 5:03 PM". May take the values: 'string',
13161		 * 'numeric', 'date' or 'html' (by default). Further types can be adding
13162		 * through plug-ins.
13163		 *  @type string
13164		 *  @default null <i>Auto-detected from raw data</i>
13165		 *
13166		 *  @name DataTable.defaults.column.type
13167		 *  @dtopt Columns
13168		 *
13169		 *  @example
13170		 *    // Using `columnDefs`
13171		 *    $(document).ready( function() {
13172		 *      $('#example').dataTable( {
13173		 *        "columnDefs": [
13174		 *          { "type": "html", "targets": [ 0 ] }
13175		 *        ]
13176		 *      } );
13177		 *    } );
13178		 *
13179		 *  @example
13180		 *    // Using `columns`
13181		 *    $(document).ready( function() {
13182		 *      $('#example').dataTable( {
13183		 *        "columns": [
13184		 *          { "type": "html" },
13185		 *          null,
13186		 *          null,
13187		 *          null,
13188		 *          null
13189		 *        ]
13190		 *      } );
13191		 *    } );
13192		 */
13193		"sType": null,
13194	
13195	
13196		/**
13197		 * Defining the width of the column, this parameter may take any CSS value
13198		 * (3em, 20px etc). DataTables applies 'smart' widths to columns which have not
13199		 * been given a specific width through this interface ensuring that the table
13200		 * remains readable.
13201		 *  @type string
13202		 *  @default null <i>Automatic</i>
13203		 *
13204		 *  @name DataTable.defaults.column.width
13205		 *  @dtopt Columns
13206		 *
13207		 *  @example
13208		 *    // Using `columnDefs`
13209		 *    $(document).ready( function() {
13210		 *      $('#example').dataTable( {
13211		 *        "columnDefs": [
13212		 *          { "width": "20%", "targets": [ 0 ] }
13213		 *        ]
13214		 *      } );
13215		 *    } );
13216		 *
13217		 *  @example
13218		 *    // Using `columns`
13219		 *    $(document).ready( function() {
13220		 *      $('#example').dataTable( {
13221		 *        "columns": [
13222		 *          { "width": "20%" },
13223		 *          null,
13224		 *          null,
13225		 *          null,
13226		 *          null
13227		 *        ]
13228		 *      } );
13229		 *    } );
13230		 */
13231		"sWidth": null
13232	};
13233	
13234	_fnHungarianMap( DataTable.defaults.column );
13235	
13236	
13237	
13238	/**
13239	 * DataTables settings object - this holds all the information needed for a
13240	 * given table, including configuration, data and current application of the
13241	 * table options. DataTables does not have a single instance for each DataTable
13242	 * with the settings attached to that instance, but rather instances of the
13243	 * DataTable "class" are created on-the-fly as needed (typically by a
13244	 * $().dataTable() call) and the settings object is then applied to that
13245	 * instance.
13246	 *
13247	 * Note that this object is related to {@link DataTable.defaults} but this
13248	 * one is the internal data store for DataTables's cache of columns. It should
13249	 * NOT be manipulated outside of DataTables. Any configuration should be done
13250	 * through the initialisation options.
13251	 *  @namespace
13252	 *  @todo Really should attach the settings object to individual instances so we
13253	 *    don't need to create new instances on each $().dataTable() call (if the
13254	 *    table already exists). It would also save passing oSettings around and
13255	 *    into every single function. However, this is a very significant
13256	 *    architecture change for DataTables and will almost certainly break
13257	 *    backwards compatibility with older installations. This is something that
13258	 *    will be done in 2.0.
13259	 */
13260	DataTable.models.oSettings = {
13261		/**
13262		 * Primary features of DataTables and their enablement state.
13263		 *  @namespace
13264		 */
13265		"oFeatures": {
13266	
13267			/**
13268			 * Flag to say if DataTables should automatically try to calculate the
13269			 * optimum table and columns widths (true) or not (false).
13270			 * Note that this parameter will be set by the initialisation routine. To
13271			 * set a default use {@link DataTable.defaults}.
13272			 *  @type boolean
13273			 */
13274			"bAutoWidth": null,
13275	
13276			/**
13277			 * Delay the creation of TR and TD elements until they are actually
13278			 * needed by a driven page draw. This can give a significant speed
13279			 * increase for Ajax source and Javascript source data, but makes no
13280			 * difference at all for DOM and server-side processing tables.
13281			 * Note that this parameter will be set by the initialisation routine. To
13282			 * set a default use {@link DataTable.defaults}.
13283			 *  @type boolean
13284			 */
13285			"bDeferRender": null,
13286	
13287			/**
13288			 * Enable filtering on the table or not. Note that if this is disabled
13289			 * then there is no filtering at all on the table, including fnFilter.
13290			 * To just remove the filtering input use sDom and remove the 'f' option.
13291			 * Note that this parameter will be set by the initialisation routine. To
13292			 * set a default use {@link DataTable.defaults}.
13293			 *  @type boolean
13294			 */
13295			"bFilter": null,
13296	
13297			/**
13298			 * Table information element (the 'Showing x of y records' div) enable
13299			 * flag.
13300			 * Note that this parameter will be set by the initialisation routine. To
13301			 * set a default use {@link DataTable.defaults}.
13302			 *  @type boolean
13303			 */
13304			"bInfo": null,
13305	
13306			/**
13307			 * Present a user control allowing the end user to change the page size
13308			 * when pagination is enabled.
13309			 * Note that this parameter will be set by the initialisation routine. To
13310			 * set a default use {@link DataTable.defaults}.
13311			 *  @type boolean
13312			 */
13313			"bLengthChange": null,
13314	
13315			/**
13316			 * Pagination enabled or not. Note that if this is disabled then length
13317			 * changing must also be disabled.
13318			 * Note that this parameter will be set by the initialisation routine. To
13319			 * set a default use {@link DataTable.defaults}.
13320			 *  @type boolean
13321			 */
13322			"bPaginate": null,
13323	
13324			/**
13325			 * Processing indicator enable flag whenever DataTables is enacting a
13326			 * user request - typically an Ajax request for server-side processing.
13327			 * Note that this parameter will be set by the initialisation routine. To
13328			 * set a default use {@link DataTable.defaults}.
13329			 *  @type boolean
13330			 */
13331			"bProcessing": null,
13332	
13333			/**
13334			 * Server-side processing enabled flag - when enabled DataTables will
13335			 * get all data from the server for every draw - there is no filtering,
13336			 * sorting or paging done on the client-side.
13337			 * Note that this parameter will be set by the initialisation routine. To
13338			 * set a default use {@link DataTable.defaults}.
13339			 *  @type boolean
13340			 */
13341			"bServerSide": null,
13342	
13343			/**
13344			 * Sorting enablement flag.
13345			 * Note that this parameter will be set by the initialisation routine. To
13346			 * set a default use {@link DataTable.defaults}.
13347			 *  @type boolean
13348			 */
13349			"bSort": null,
13350	
13351			/**
vendor: 10,013 bytes, lines 13352-13760
13352			 * Multi-column sorting
13353			 * Note that this parameter will be set by the initialisation routine. To
13354			 * set a default use {@link DataTable.defaults}.
13355			 *  @type boolean
13356			 */
13357			"bSortMulti": null,
13358	
13359			/**
13360			 * Apply a class to the columns which are being sorted to provide a
13361			 * visual highlight or not. This can slow things down when enabled since
13362			 * there is a lot of DOM interaction.
13363			 * Note that this parameter will be set by the initialisation routine. To
13364			 * set a default use {@link DataTable.defaults}.
13365			 *  @type boolean
13366			 */
13367			"bSortClasses": null,
13368	
13369			/**
13370			 * State saving enablement flag.
13371			 * Note that this parameter will be set by the initialisation routine. To
13372			 * set a default use {@link DataTable.defaults}.
13373			 *  @type boolean
13374			 */
13375			"bStateSave": null
13376		},
13377	
13378	
13379		/**
13380		 * Scrolling settings for a table.
13381		 *  @namespace
13382		 */
13383		"oScroll": {
13384			/**
13385			 * When the table is shorter in height than sScrollY, collapse the
13386			 * table container down to the height of the table (when true).
13387			 * Note that this parameter will be set by the initialisation routine. To
13388			 * set a default use {@link DataTable.defaults}.
13389			 *  @type boolean
13390			 */
13391			"bCollapse": null,
13392	
13393			/**
13394			 * Width of the scrollbar for the web-browser's platform. Calculated
13395			 * during table initialisation.
13396			 *  @type int
13397			 *  @default 0
13398			 */
13399			"iBarWidth": 0,
13400	
13401			/**
13402			 * Viewport width for horizontal scrolling. Horizontal scrolling is
13403			 * disabled if an empty string.
13404			 * Note that this parameter will be set by the initialisation routine. To
13405			 * set a default use {@link DataTable.defaults}.
13406			 *  @type string
13407			 */
13408			"sX": null,
13409	
13410			/**
13411			 * Width to expand the table to when using x-scrolling. Typically you
13412			 * should not need to use this.
13413			 * Note that this parameter will be set by the initialisation routine. To
13414			 * set a default use {@link DataTable.defaults}.
13415			 *  @type string
13416			 *  @deprecated
13417			 */
13418			"sXInner": null,
13419	
13420			/**
13421			 * Viewport height for vertical scrolling. Vertical scrolling is disabled
13422			 * if an empty string.
13423			 * Note that this parameter will be set by the initialisation routine. To
13424			 * set a default use {@link DataTable.defaults}.
13425			 *  @type string
13426			 */
13427			"sY": null
13428		},
13429	
13430		/**
13431		 * Language information for the table.
13432		 *  @namespace
13433		 *  @extends DataTable.defaults.oLanguage
13434		 */
13435		"oLanguage": {
13436			/**
13437			 * Information callback function. See
13438			 * {@link DataTable.defaults.fnInfoCallback}
13439			 *  @type function
13440			 *  @default null
13441			 */
13442			"fnInfoCallback": null
13443		},
13444	
13445		/**
13446		 * Browser support parameters
13447		 *  @namespace
13448		 */
13449		"oBrowser": {
13450			/**
13451			 * Indicate if the browser incorrectly calculates width:100% inside a
13452			 * scrolling element (IE6/7)
13453			 *  @type boolean
13454			 *  @default false
13455			 */
13456			"bScrollOversize": false,
13457	
13458			/**
13459			 * Determine if the vertical scrollbar is on the right or left of the
13460			 * scrolling container - needed for rtl language layout, although not
13461			 * all browsers move the scrollbar (Safari).
13462			 *  @type boolean
13463			 *  @default false
13464			 */
13465			"bScrollbarLeft": false,
13466	
13467			/**
13468			 * Flag for if `getBoundingClientRect` is fully supported or not
13469			 *  @type boolean
13470			 *  @default false
13471			 */
13472			"bBounding": false,
13473	
13474			/**
13475			 * Browser scrollbar width
13476			 *  @type integer
13477			 *  @default 0
13478			 */
13479			"barWidth": 0
13480		},
13481	
13482	
13483		"ajax": null,
13484	
13485	
13486		/**
13487		 * Array referencing the nodes which are used for the features. The
13488		 * parameters of this object match what is allowed by sDom - i.e.
13489		 *   <ul>
13490		 *     <li>'l' - Length changing</li>
13491		 *     <li>'f' - Filtering input</li>
13492		 *     <li>'t' - The table!</li>
13493		 *     <li>'i' - Information</li>
13494		 *     <li>'p' - Pagination</li>
13495		 *     <li>'r' - pRocessing</li>
13496		 *   </ul>
13497		 *  @type array
13498		 *  @default []
13499		 */
13500		"aanFeatures": [],
13501	
13502		/**
13503		 * Store data information - see {@link DataTable.models.oRow} for detailed
13504		 * information.
13505		 *  @type array
13506		 *  @default []
13507		 */
13508		"aoData": [],
13509	
13510		/**
13511		 * Array of indexes which are in the current display (after filtering etc)
13512		 *  @type array
13513		 *  @default []
13514		 */
13515		"aiDisplay": [],
13516	
13517		/**
13518		 * Array of indexes for display - no filtering
13519		 *  @type array
13520		 *  @default []
13521		 */
13522		"aiDisplayMaster": [],
13523	
13524		/**
13525		 * Map of row ids to data indexes
13526		 *  @type object
13527		 *  @default {}
13528		 */
13529		"aIds": {},
13530	
13531		/**
13532		 * Store information about each column that is in use
13533		 *  @type array
13534		 *  @default []
13535		 */
13536		"aoColumns": [],
13537	
13538		/**
13539		 * Store information about the table's header
13540		 *  @type array
13541		 *  @default []
13542		 */
13543		"aoHeader": [],
13544	
13545		/**
13546		 * Store information about the table's footer
13547		 *  @type array
13548		 *  @default []
13549		 */
13550		"aoFooter": [],
13551	
13552		/**
13553		 * Store the applied global search information in case we want to force a
13554		 * research or compare the old search to a new one.
13555		 * Note that this parameter will be set by the initialisation routine. To
13556		 * set a default use {@link DataTable.defaults}.
13557		 *  @namespace
13558		 *  @extends DataTable.models.oSearch
13559		 */
13560		"oPreviousSearch": {},
13561	
13562		/**
13563		 * Store the applied search for each column - see
13564		 * {@link DataTable.models.oSearch} for the format that is used for the
13565		 * filtering information for each column.
13566		 *  @type array
13567		 *  @default []
13568		 */
13569		"aoPreSearchCols": [],
13570	
13571		/**
13572		 * Sorting that is applied to the table. Note that the inner arrays are
13573		 * used in the following manner:
13574		 * <ul>
13575		 *   <li>Index 0 - column number</li>
13576		 *   <li>Index 1 - current sorting direction</li>
13577		 * </ul>
13578		 * Note that this parameter will be set by the initialisation routine. To
13579		 * set a default use {@link DataTable.defaults}.
13580		 *  @type array
13581		 *  @todo These inner arrays should really be objects
13582		 */
13583		"aaSorting": null,
13584	
13585		/**
13586		 * Sorting that is always applied to the table (i.e. prefixed in front of
13587		 * aaSorting).
13588		 * Note that this parameter will be set by the initialisation routine. To
13589		 * set a default use {@link DataTable.defaults}.
13590		 *  @type array
13591		 *  @default []
13592		 */
13593		"aaSortingFixed": [],
13594	
13595		/**
13596		 * Classes to use for the striping of a table.
13597		 * Note that this parameter will be set by the initialisation routine. To
13598		 * set a default use {@link DataTable.defaults}.
13599		 *  @type array
13600		 *  @default []
13601		 */
13602		"asStripeClasses": null,
13603	
13604		/**
13605		 * If restoring a table - we should restore its striping classes as well
13606		 *  @type array
13607		 *  @default []
13608		 */
13609		"asDestroyStripes": [],
13610	
13611		/**
13612		 * If restoring a table - we should restore its width
13613		 *  @type int
13614		 *  @default 0
13615		 */
13616		"sDestroyWidth": 0,
13617	
13618		/**
13619		 * Callback functions array for every time a row is inserted (i.e. on a draw).
13620		 *  @type array
13621		 *  @default []
13622		 */
13623		"aoRowCallback": [],
13624	
13625		/**
13626		 * Callback functions for the header on each draw.
13627		 *  @type array
13628		 *  @default []
13629		 */
13630		"aoHeaderCallback": [],
13631	
13632		/**
13633		 * Callback function for the footer on each draw.
13634		 *  @type array
13635		 *  @default []
13636		 */
13637		"aoFooterCallback": [],
13638	
13639		/**
13640		 * Array of callback functions for draw callback functions
13641		 *  @type array
13642		 *  @default []
13643		 */
13644		"aoDrawCallback": [],
13645	
13646		/**
13647		 * Array of callback functions for row created function
13648		 *  @type array
13649		 *  @default []
13650		 */
13651		"aoRowCreatedCallback": [],
13652	
13653		/**
13654		 * Callback functions for just before the table is redrawn. A return of
13655		 * false will be used to cancel the draw.
13656		 *  @type array
13657		 *  @default []
13658		 */
13659		"aoPreDrawCallback": [],
13660	
13661		/**
13662		 * Callback functions for when the table has been initialised.
13663		 *  @type array
13664		 *  @default []
13665		 */
13666		"aoInitComplete": [],
13667	
13668	
13669		/**
13670		 * Callbacks for modifying the settings to be stored for state saving, prior to
13671		 * saving state.
13672		 *  @type array
13673		 *  @default []
13674		 */
13675		"aoStateSaveParams": [],
13676	
13677		/**
13678		 * Callbacks for modifying the settings that have been stored for state saving
13679		 * prior to using the stored values to restore the state.
13680		 *  @type array
13681		 *  @default []
13682		 */
13683		"aoStateLoadParams": [],
13684	
13685		/**
13686		 * Callbacks for operating on the settings object once the saved state has been
13687		 * loaded
13688		 *  @type array
13689		 *  @default []
13690		 */
13691		"aoStateLoaded": [],
13692	
13693		/**
13694		 * Cache the table ID for quick access
13695		 *  @type string
13696		 *  @default <i>Empty string</i>
13697		 */
13698		"sTableId": "",
13699	
13700		/**
13701		 * The TABLE node for the main table
13702		 *  @type node
13703		 *  @default null
13704		 */
13705		"nTable": null,
13706	
13707		/**
13708		 * Permanent ref to the thead element
13709		 *  @type node
13710		 *  @default null
13711		 */
13712		"nTHead": null,
13713	
13714		/**
13715		 * Permanent ref to the tfoot element - if it exists
13716		 *  @type node
13717		 *  @default null
13718		 */
13719		"nTFoot": null,
13720	
13721		/**
13722		 * Permanent ref to the tbody element
13723		 *  @type node
13724		 *  @default null
13725		 */
13726		"nTBody": null,
13727	
13728		/**
13729		 * Cache the wrapper node (contains all DataTables controlled elements)
13730		 *  @type node
13731		 *  @default null
13732		 */
13733		"nTableWrapper": null,
13734	
13735		/**
13736		 * Indicate if when using server-side processing the loading of data
13737		 * should be deferred until the second draw.
13738		 * Note that this parameter will be set by the initialisation routine. To
13739		 * set a default use {@link DataTable.defaults}.
13740		 *  @type boolean
13741		 *  @default false
13742		 */
13743		"bDeferLoading": false,
13744	
13745		/**
13746		 * Indicate if all required information has been read in
13747		 *  @type boolean
13748		 *  @default false
13749		 */
13750		"bInitialised": false,
13751	
13752		/**
13753		 * Information about open rows. Each object in the array has the parameters
13754		 * 'nTr' and 'nParent'
13755		 *  @type array
13756		 *  @default []
13757		 */
13758		"aoOpenRows": [],
13759	
13760		/**
13761		 * Dictate the positioning of DataTables' control elements - see
13762		 * {@link DataTable.model.oInit.sDom}.
13763		 * Note that this parameter will be set by the initialisation routine. To
13764		 * set a default use {@link DataTable.defaults}.
13765		 *  @type string
13766		 *  @default null
13767		 */
13768		"sDom": null,
13769	
13770		/**
13771		 * Search delay (in mS)
13772		 *  @type integer
13773		 *  @default null
13774		 */
13775		"searchDelay": null,
13776	
13777		/**
13778		 * Which type of pagination should be used.
13779		 * Note that this parameter will be set by the initialisation routine. To
13780		 * set a default use {@link DataTable.defaults}.
13781		 *  @type string
13782		 *  @default two_button
13783		 */
13784		"sPaginationType": "two_button",
13785	
13786		/**
13787		 * The state duration (for `stateSave`) in seconds.
13788		 * Note that this parameter will be set by the initialisation routine. To
13789		 * set a default use {@link DataTable.defaults}.
13790		 *  @type int
13791		 *  @default 0
13792		 */
13793		"iStateDuration": 0,
13794	
13795		/**
13796		 * Array of callback functions for state saving. Each array element is an
13797		 * object with the following parameters:
13798		 *   <ul>
13799		 *     <li>function:fn - function to call. Takes two parameters, oSettings
13800		 *       and the JSON string to save that has been thus far created. Returns
13801		 *       a JSON string to be inserted into a json object
13802		 *       (i.e. '"param": [ 0, 1, 2]')</li>
13803		 *     <li>string:sName - name of callback</li>
13804		 *   </ul>
13805		 *  @type array
13806		 *  @default []
13807		 */
13808		"aoStateSave": [],
13809	
13810		/**
13811		 * Array of callback functions for state loading. Each array element is an
13812		 * object with the following parameters:
13813		 *   <ul>
13814		 *     <li>function:fn - function to call. Takes two parameters, oSettings
13815		 *       and the object stored. May return false to cancel state loading</li>
13816		 *     <li>string:sName - name of callback</li>
13817		 *   </ul>
13818		 *  @type array
13819		 *  @default []
13820		 */
13821		"aoStateLoad": [],
13822	
13823		/**
13824		 * State that was saved. Useful for back reference
13825		 *  @type object
13826		 *  @default null
13827		 */
13828		"oSavedState": null,
13829	
13830		/**
13831		 * State that was loaded. Useful for back reference
13832		 *  @type object
13833		 *  @default null
13834		 */
13835		"oLoadedState": null,
13836	
13837		/**
13838		 * Source url for AJAX data for the table.
13839		 * Note that this parameter will be set by the initialisation routine. To
13840		 * set a default use {@link DataTable.defaults}.
13841		 *  @type string
13842		 *  @default null
13843		 */
13844		"sAjaxSource": null,
13845	
13846		/**
13847		 * Property from a given object from which to read the table data from. This
13848		 * can be an empty string (when not server-side processing), in which case
13849		 * it is  assumed an an array is given directly.
13850		 * Note that this parameter will be set by the initialisation routine. To
13851		 * set a default use {@link DataTable.defaults}.
13852		 *  @type string
13853		 */
13854		"sAjaxDataProp": null,
13855	
13856		/**
13857		 * The last jQuery XHR object that was used for server-side data gathering.
13858		 * This can be used for working with the XHR information in one of the
13859		 * callbacks
13860		 *  @type object
13861		 *  @default null
13862		 */
13863		"jqXHR": null,
13864	
13865		/**
13866		 * JSON returned from the server in the last Ajax request
13867		 *  @type object
13868		 *  @default undefined
13869		 */
13870		"json": undefined,
13871	
13872		/**
13873		 * Data submitted as part of the last Ajax request
13874		 *  @type object
13875		 *  @default undefined
13876		 */
13877		"oAjaxData": undefined,
13878	
13879		/**
13880		 * Function to get the server-side data.
13881		 * Note that this parameter will be set by the initialisation routine. To
13882		 * set a default use {@link DataTable.defaults}.
13883		 *  @type function
13884		 */
13885		"fnServerData": null,
13886	
13887		/**
13888		 * Functions which are called prior to sending an Ajax request so extra
13889		 * parameters can easily be sent to the server
13890		 *  @type array
13891		 *  @default []
13892		 */
13893		"aoServerParams": [],
13894	
13895		/**
13896		 * Send the XHR HTTP method - GET or POST (could be PUT or DELETE if
13897		 * required).
13898		 * Note that this parameter will be set by the initialisation routine. To
13899		 * set a default use {@link DataTable.defaults}.
13900		 *  @type string
13901		 */
13902		"sServerMethod": null,
13903	
13904		/**
13905		 * Format numbers for display.
13906		 * Note that this parameter will be set by the initialisation routine. To
13907		 * set a default use {@link DataTable.defaults}.
13908		 *  @type function
13909		 */
13910		"fnFormatNumber": null,
13911	
13912		/**
13913		 * List of options that can be used for the user selectable length menu.
13914		 * Note that this parameter will be set by the initialisation routine. To
13915		 * set a default use {@link DataTable.defaults}.
13916		 *  @type array
13917		 *  @default []
13918		 */
13919		"aLengthMenu": null,
13920	
13921		/**
13922		 * Counter for the draws that the table does. Also used as a tracker for
13923		 * server-side processing
13924		 *  @type int
13925		 *  @default 0
13926		 */
13927		"iDraw": 0,
13928	
13929		/**
13930		 * Indicate if a redraw is being done - useful for Ajax
13931		 *  @type boolean
13932		 *  @default false
13933		 */
13934		"bDrawing": false,
13935	
13936		/**
13937		 * Draw index (iDraw) of the last error when parsing the returned data
13938		 *  @type int
13939		 *  @default -1
13940		 */
13941		"iDrawError": -1,
13942	
13943		/**
13944		 * Paging display length
13945		 *  @type int
13946		 *  @default 10
13947		 */
13948		"_iDisplayLength": 10,
13949	
13950		/**
13951		 * Paging start point - aiDisplay index
13952		 *  @type int
13953		 *  @default 0
13954		 */
13955		"_iDisplayStart": 0,
13956	
13957		/**
13958		 * Server-side processing - number of records in the result set
13959		 * (i.e. before filtering), Use fnRecordsTotal rather than
13960		 * this property to get the value of the number of records, regardless of
13961		 * the server-side processing setting.
13962		 *  @type int
13963		 *  @default 0
13964		 *  @private
13965		 */
13966		"_iRecordsTotal": 0,
13967	
13968		/**
13969		 * Server-side processing - number of records in the current display set
13970		 * (i.e. after filtering). Use fnRecordsDisplay rather than
13971		 * this property to get the value of the number of records, regardless of
13972		 * the server-side processing setting.
13973		 *  @type boolean
13974		 *  @default 0
13975		 *  @private
13976		 */
13977		"_iRecordsDisplay": 0,
13978	
13979		/**
13980		 * The classes to use for the table
13981		 *  @type object
13982		 *  @default {}
13983		 */
vendor: 4,121 bytes, lines 13984-14155
13984		"oClasses": {},
13985	
13986		/**
13987		 * Flag attached to the settings object so you can check in the draw
13988		 * callback if filtering has been done in the draw. Deprecated in favour of
13989		 * events.
13990		 *  @type boolean
13991		 *  @default false
13992		 *  @deprecated
13993		 */
13994		"bFiltered": false,
13995	
13996		/**
13997		 * Flag attached to the settings object so you can check in the draw
13998		 * callback if sorting has been done in the draw. Deprecated in favour of
13999		 * events.
14000		 *  @type boolean
14001		 *  @default false
14002		 *  @deprecated
14003		 */
14004		"bSorted": false,
14005	
14006		/**
14007		 * Indicate that if multiple rows are in the header and there is more than
14008		 * one unique cell per column, if the top one (true) or bottom one (false)
14009		 * should be used for sorting / title by DataTables.
14010		 * Note that this parameter will be set by the initialisation routine. To
14011		 * set a default use {@link DataTable.defaults}.
14012		 *  @type boolean
14013		 */
14014		"bSortCellsTop": null,
14015	
14016		/**
14017		 * Initialisation object that is used for the table
14018		 *  @type object
14019		 *  @default null
14020		 */
14021		"oInit": null,
14022	
14023		/**
14024		 * Destroy callback functions - for plug-ins to attach themselves to the
14025		 * destroy so they can clean up markup and events.
14026		 *  @type array
14027		 *  @default []
14028		 */
14029		"aoDestroyCallback": [],
14030	
14031	
14032		/**
14033		 * Get the number of records in the current record set, before filtering
14034		 *  @type function
14035		 */
14036		"fnRecordsTotal": function ()
14037		{
14038			return _fnDataSource( this ) == 'ssp' ?
14039				this._iRecordsTotal * 1 :
14040				this.aiDisplayMaster.length;
14041		},
14042	
14043		/**
14044		 * Get the number of records in the current record set, after filtering
14045		 *  @type function
14046		 */
14047		"fnRecordsDisplay": function ()
14048		{
14049			return _fnDataSource( this ) == 'ssp' ?
14050				this._iRecordsDisplay * 1 :
14051				this.aiDisplay.length;
14052		},
14053	
14054		/**
14055		 * Get the display end point - aiDisplay index
14056		 *  @type function
14057		 */
14058		"fnDisplayEnd": function ()
14059		{
14060			var
14061				len      = this._iDisplayLength,
14062				start    = this._iDisplayStart,
14063				calc     = start + len,
14064				records  = this.aiDisplay.length,
14065				features = this.oFeatures,
14066				paginate = features.bPaginate;
14067	
14068			if ( features.bServerSide ) {
14069				return paginate === false || len === -1 ?
14070					start + records :
14071					Math.min( start+len, this._iRecordsDisplay );
14072			}
14073			else {
14074				return ! paginate || calc>records || len===-1 ?
14075					records :
14076					calc;
14077			}
14078		},
14079	
14080		/**
14081		 * The DataTables object for this table
14082		 *  @type object
14083		 *  @default null
14084		 */
14085		"oInstance": null,
14086	
14087		/**
14088		 * Unique identifier for each instance of the DataTables object. If there
14089		 * is an ID on the table node, then it takes that value, otherwise an
14090		 * incrementing internal counter is used.
14091		 *  @type string
14092		 *  @default null
14093		 */
14094		"sInstance": null,
14095	
14096		/**
14097		 * tabindex attribute value that is added to DataTables control elements, allowing
14098		 * keyboard navigation of the table and its controls.
14099		 */
14100		"iTabIndex": 0,
14101	
14102		/**
14103		 * DIV container for the footer scrolling table if scrolling
14104		 */
14105		"nScrollHead": null,
14106	
14107		/**
14108		 * DIV container for the footer scrolling table if scrolling
14109		 */
14110		"nScrollFoot": null,
14111	
14112		/**
14113		 * Last applied sort
14114		 *  @type array
14115		 *  @default []
14116		 */
14117		"aLastSort": [],
14118	
14119		/**
14120		 * Stored plug-in instances
14121		 *  @type object
14122		 *  @default {}
14123		 */
14124		"oPlugins": {},
14125	
14126		/**
14127		 * Function used to get a row's id from the row's data
14128		 *  @type function
14129		 *  @default null
14130		 */
14131		"rowIdFn": null,
14132	
14133		/**
14134		 * Data location where to store a row's id
14135		 *  @type string
14136		 *  @default null
14137		 */
14138		"rowId": null
14139	};
14140	
14141	/**
14142	 * Extension object for DataTables that is used to provide all extension
14143	 * options.
14144	 *
14145	 * Note that the `DataTable.ext` object is available through
14146	 * `jQuery.fn.dataTable.ext` where it may be accessed and manipulated. It is
14147	 * also aliased to `jQuery.fn.dataTableExt` for historic reasons.
14148	 *  @namespace
14149	 *  @extends DataTable.models.ext
14150	 */
14151	
14152	
14153	/**
14154	 * DataTables extensions
14155	 * 
14156	 * This namespace acts as a collection area for plug-ins that can be used to
14157	 * extend DataTables capabilities. Indeed many of the build in methods
14158	 * use this method to provide their own capabilities (sorting methods for
14159	 * example).
14160	 *
14161	 * Note that this namespace is aliased to `jQuery.fn.dataTableExt` for legacy
14162	 * reasons
14163	 *
14164	 *  @namespace
14165	 */
14166	DataTable.ext = _ext = {
14167		/**
14168		 * Buttons. For use with the Buttons extension for DataTables. This is
14169		 * defined here so other extensions can define buttons regardless of load
14170		 * order. It is _not_ used by DataTables core.
14171		 *
14172		 *  @type object
14173		 *  @default {}
14174		 */
14175		buttons: {},
14176	
14177	
14178		/**
14179		 * Element class names
14180		 *
14181		 *  @type object
14182		 *  @default {}
14183		 */
14184		classes: {},
14185	
14186	
14187		/**
14188		 * DataTables build type (expanded by the download builder)
14189		 *
14190		 *  @type string
14191		 */
14192		build:"dt/dt-1.13.4/af-2.5.3/b-2.3.6/b-colvis-2.3.6/b-html5-2.3.6/b-print-2.3.6/cr-1.6.2/fc-4.2.2/fh-3.3.2/kt-2.9.0/r-2.4.1/rg-1.3.1/rr-1.3.3/sc-2.1.1/sb-1.4.2/sp-2.1.2/sl-1.6.2/sr-1.2.2",
14193	
14194	
14195		/**
14196		 * Error reporting.
14197		 * 
14198		 * How should DataTables report an error. Can take the value 'alert',
14199		 * 'throw', 'none' or a function.
14200		 *
14201		 *  @type string|function
14202		 *  @default alert
14203		 */
14204		errMode: "alert",
14205	
14206	
14207		/**
14208		 * Feature plug-ins.
14209		 * 
14210		 * This is an array of objects which describe the feature plug-ins that are
14211		 * available to DataTables. These feature plug-ins are then available for
14212		 * use through the `dom` initialisation option.
14213		 * 
14214		 * Each feature plug-in is described by an object which must have the
14215		 * following properties:
14216		 * 
14217		 * * `fnInit` - function that is used to initialise the plug-in,
14218		 * * `cFeature` - a character so the feature can be enabled by the `dom`
14219		 *   instillation option. This is case sensitive.
14220		 *
14221		 * The `fnInit` function has the following input parameters:
14222		 *
14223		 * 1. `{object}` DataTables settings object: see
14224		 *    {@link DataTable.models.oSettings}
14225		 *
14226		 * And the following return is expected:
14227		 * 
14228		 * * {node|null} The element which contains your feature. Note that the
14229		 *   return may also be void if your plug-in does not require to inject any
14230		 *   DOM elements into DataTables control (`dom`) - for example this might
14231		 *   be useful when developing a plug-in which allows table control via
14232		 *   keyboard entry
14233		 *
14234		 *  @type array
14235		 *
14236		 *  @example
14237		 *    $.fn.dataTable.ext.features.push( {
14238		 *      "fnInit": function( oSettings ) {
14239		 *        return new TableTools( { "oDTSettings": oSettings } );
14240		 *      },
14241		 *      "cFeature": "T"
14242		 *    } );
14243		 */
14244		feature: [],
14245	
14246	
14247		/**
14248		 * Row searching.
14249		 * 
14250		 * This method of searching is complimentary to the default type based
14251		 * searching, and a lot more comprehensive as it allows you complete control
14252		 * over the searching logic. Each element in this array is a function
14253		 * (parameters described below) that is called for every row in the table,
14254		 * and your logic decides if it should be included in the searching data set
14255		 * or not.
14256		 *
14257		 * Searching functions have the following input parameters:
14258		 *
14259		 * 1. `{object}` DataTables settings object: see
14260		 *    {@link DataTable.models.oSettings}
14261		 * 2. `{array|object}` Data for the row to be processed (same as the
14262		 *    original format that was passed in as the data source, or an array
14263		 *    from a DOM data source
14264		 * 3. `{int}` Row index ({@link DataTable.models.oSettings.aoData}), which
14265		 *    can be useful to retrieve the `TR` element if you need DOM interaction.
14266		 *
14267		 * And the following return is expected:
14268		 *
14269		 * * {boolean} Include the row in the searched result set (true) or not
14270		 *   (false)
14271		 *
14272		 * Note that as with the main search ability in DataTables, technically this
14273		 * is "filtering", since it is subtractive. However, for consistency in
14274		 * naming we call it searching here.
14275		 *
14276		 *  @type array
14277		 *  @default []
14278		 *
14279		 *  @example
14280		 *    // The following example shows custom search being applied to the
14281		 *    // fourth column (i.e. the data[3] index) based on two input values
14282		 *    // from the end-user, matching the data in a certain range.
14283		 *    $.fn.dataTable.ext.search.push(
14284		 *      function( settings, data, dataIndex ) {
14285		 *        var min = document.getElementById('min').value * 1;
14286		 *        var max = document.getElementById('max').value * 1;
14287		 *        var version = data[3] == "-" ? 0 : data[3]*1;
14288		 *
14289		 *        if ( min == "" && max == "" ) {
14290		 *          return true;
14291		 *        }
14292		 *        else if ( min == "" && version < max ) {
14293		 *          return true;
14294		 *        }
14295		 *        else if ( min < version && "" == max ) {
14296		 *          return true;
14297		 *        }
14298		 *        else if ( min < version && version < max ) {
14299		 *          return true;
14300		 *        }
14301		 *        return false;
14302		 *      }
14303		 *    );
14304		 */
14305		search: [],
14306	
14307	
14308		/**
14309		 * Selector extensions
14310		 *
14311		 * The `selector` option can be used to extend the options available for the
14312		 * selector modifier options (`selector-modifier` object data type) that
14313		 * each of the three built in selector types offer (row, column and cell +
14314		 * their plural counterparts). For example the Select extension uses this
14315		 * mechanism to provide an option to select only rows, columns and cells
14316		 * that have been marked as selected by the end user (`{selected: true}`),
14317		 * which can be used in conjunction with the existing built in selector
14318		 * options.
14319		 *
14320		 * Each property is an array to which functions can be pushed. The functions
14321		 * take three attributes:
14322		 *
14323		 * * Settings object for the host table
14324		 * * Options object (`selector-modifier` object type)
14325		 * * Array of selected item indexes
14326		 *
14327		 * The return is an array of the resulting item indexes after the custom
14328		 * selector has been applied.
14329		 *
14330		 *  @type object
14331		 */
14332		selector: {
14333			cell: [],
14334			column: [],
14335			row: []
14336		},
14337	
14338	
14339		/**
14340		 * Internal functions, exposed for used in plug-ins.
14341		 * 
14342		 * Please note that you should not need to use the internal methods for
14343		 * anything other than a plug-in (and even then, try to avoid if possible).
14344		 * The internal function may change between releases.
14345		 *
14346		 *  @type object
14347		 *  @default {}
14348		 */
14349		internal: {},
14350	
14351	
14352		/**
14353		 * Legacy configuration options. Enable and disable legacy options that
14354		 * are available in DataTables.
14355		 *
14356		 *  @type object
14357		 */
14358		legacy: {
14359			/**
14360			 * Enable / disable DataTables 1.9 compatible server-side processing
14361			 * requests
14362			 *
14363			 *  @type boolean
14364			 *  @default null
14365			 */
14366			ajax: null
14367		},
14368	
14369	
14370		/**
14371		 * Pagination plug-in methods.
14372		 * 
14373		 * Each entry in this object is a function and defines which buttons should
14374		 * be shown by the pagination rendering method that is used for the table:
14375		 * {@link DataTable.ext.renderer.pageButton}. The renderer addresses how the
14376		 * buttons are displayed in the document, while the functions here tell it
14377		 * what buttons to display. This is done by returning an array of button
14378		 * descriptions (what each button will do).
14379		 *
14380		 * Pagination types (the four built in options and any additional plug-in
14381		 * options defined here) can be used through the `paginationType`
14382		 * initialisation parameter.
14383		 *
14384		 * The functions defined take two parameters:
14385		 *
14386		 * 1. `{int} page` The current page index
14387		 * 2. `{int} pages` The number of pages in the table
14388		 *
14389		 * Each function is expected to return an array where each element of the
14390		 * array can be one of:
14391		 *
14392		 * * `first` - Jump to first page when activated
14393		 * * `last` - Jump to last page when activated
14394		 * * `previous` - Show previous page when activated
14395		 * * `next` - Show next page when activated
14396		 * * `{int}` - Show page of the index given
14397		 * * `{array}` - A nested array containing the above elements to add a
14398		 *   containing 'DIV' element (might be useful for styling).
14399		 *
14400		 * Note that DataTables v1.9- used this object slightly differently whereby
14401		 * an object with two functions would be defined for each plug-in. That
14402		 * ability is still supported by DataTables 1.10+ to provide backwards
14403		 * compatibility, but this option of use is now decremented and no longer
14404		 * documented in DataTables 1.10+.
14405		 *
14406		 *  @type object
14407		 *  @default {}
14408		 *
14409		 *  @example
14410		 *    // Show previous, next and current page buttons only
14411		 *    $.fn.dataTableExt.oPagination.current = function ( page, pages ) {
14412		 *      return [ 'previous', page, 'next' ];
14413		 *    };
14414		 */
14415		pager: {},
14416	
14417	
14418		renderer: {
14419			pageButton: {},
14420			header: {}
14421		},
14422	
14423	
14424		/**
14425		 * Ordering plug-ins - custom data source
14426		 * 
14427		 * The extension options for ordering of data available here is complimentary
14428		 * to the default type based ordering that DataTables typically uses. It
14429		 * allows much greater control over the the data that is being used to
14430		 * order a column, but is necessarily therefore more complex.
14431		 * 
14432		 * This type of ordering is useful if you want to do ordering based on data
14433		 * live from the DOM (for example the contents of an 'input' element) rather
14434		 * than just the static string that DataTables knows of.
14435		 * 
14436		 * The way these plug-ins work is that you create an array of the values you
14437		 * wish to be ordering for the column in question and then return that
14438		 * array. The data in the array much be in the index order of the rows in
14439		 * the table (not the currently ordering order!). Which order data gathering
14440		 * function is run here depends on the `dt-init columns.orderDataType`
14441		 * parameter that is used for the column (if any).
14442		 *
14443		 * The functions defined take two parameters:
14444		 *
14445		 * 1. `{object}` DataTables settings object: see
14446		 *    {@link DataTable.models.oSettings}
14447		 * 2. `{int}` Target column index
14448		 *
14449		 * Each function is expected to return an array:
14450		 *
14451		 * * `{array}` Data for the column to be ordering upon
14452		 *
14453		 *  @type array
14454		 *
14455		 *  @example
14456		 *    // Ordering using `input` node values
14457		 *    $.fn.dataTable.ext.order['dom-text'] = function  ( settings, col )
14458		 *    {
14459		 *      return this.api().column( col, {order:'index'} ).nodes().map( function ( td, i ) {
14460		 *        return $('input', td).val();
14461		 *      } );
14462		 *    }
14463		 */
14464		order: {},
14465	
14466	
14467		/**
14468		 * Type based plug-ins.
14469		 *
14470		 * Each column in DataTables has a type assigned to it, either by automatic
14471		 * detection or by direct assignment using the `type` option for the column.
14472		 * The type of a column will effect how it is ordering and search (plug-ins
14473		 * can also make use of the column type if required).
14474		 *
14475		 * @namespace
14476		 */
14477		type: {
14478			/**
14479			 * Type detection functions.
14480			 *
14481			 * The functions defined in this object are used to automatically detect
14482			 * a column's type, making initialisation of DataTables super easy, even
14483			 * when complex data is in the table.
14484			 *
14485			 * The functions defined take two parameters:
14486			 *
14487		     *  1. `{*}` Data from the column cell to be analysed
14488		     *  2. `{settings}` DataTables settings object. This can be used to
14489		     *     perform context specific type detection - for example detection
14490		     *     based on language settings such as using a comma for a decimal
14491		     *     place. Generally speaking the options from the settings will not
14492		     *     be required
14493			 *
14494			 * Each function is expected to return:
14495			 *
14496			 * * `{string|null}` Data type detected, or null if unknown (and thus
14497			 *   pass it on to the other type detection functions.
14498			 *
14499			 *  @type array
14500			 *
14501			 *  @example
14502			 *    // Currency type detection plug-in:
14503			 *    $.fn.dataTable.ext.type.detect.push(
14504			 *      function ( data, settings ) {
14505			 *        // Check the numeric part
14506			 *        if ( ! data.substring(1).match(/[0-9]/) ) {
14507			 *          return null;
14508			 *        }
14509			 *
14510			 *        // Check prefixed by currency
14511			 *        if ( data.charAt(0) == '$' || data.charAt(0) == '&pound;' ) {
14512			 *          return 'currency';
14513			 *        }
14514			 *        return null;
14515			 *      }
14516			 *    );
14517			 */
14518			detect: [],
14519	
14520	
14521			/**
14522			 * Type based search formatting.
14523			 *
14524			 * The type based searching functions can be used to pre-format the
14525			 * data to be search on. For example, it can be used to strip HTML
14526			 * tags or to de-format telephone numbers for numeric only searching.
14527			 *
14528			 * Note that is a search is not defined for a column of a given type,
14529			 * no search formatting will be performed.
14530			 * 
14531			 * Pre-processing of searching data plug-ins - When you assign the sType
14532			 * for a column (or have it automatically detected for you by DataTables
14533			 * or a type detection plug-in), you will typically be using this for
14534			 * custom sorting, but it can also be used to provide custom searching
14535			 * by allowing you to pre-processing the data and returning the data in
14536			 * the format that should be searched upon. This is done by adding
14537			 * functions this object with a parameter name which matches the sType
14538			 * for that target column. This is the corollary of <i>afnSortData</i>
14539			 * for searching data.
14540			 *
14541			 * The functions defined take a single parameter:
14542			 *
14543		     *  1. `{*}` Data from the column cell to be prepared for searching
14544			 *
14545			 * Each function is expected to return:
14546			 *
14547			 * * `{string|null}` Formatted string that will be used for the searching.
14548			 *
14549			 *  @type object
14550			 *  @default {}
14551			 *
14552			 *  @example
14553			 *    $.fn.dataTable.ext.type.search['title-numeric'] = function ( d ) {
14554			 *      return d.replace(/\n/g," ").replace( /<.*?>/g, "" );
14555			 *    }
14556			 */
14557			search: {},
14558	
14559	
14560			/**
14561			 * Type based ordering.
14562			 *
14563			 * The column type tells DataTables what ordering to apply to the table
14564			 * when a column is sorted upon. The order for each type that is defined,
14565			 * is defined by the functions available in this object.
14566			 *
14567			 * Each ordering option can be described by three properties added to
14568			 * this object:
14569			 *
14570			 * * `{type}-pre` - Pre-formatting function
14571			 * * `{type}-asc` - Ascending order function
14572			 * * `{type}-desc` - Descending order function
14573			 *
14574			 * All three can be used together, only `{type}-pre` or only
14575			 * `{type}-asc` and `{type}-desc` together. It is generally recommended
14576			 * that only `{type}-pre` is used, as this provides the optimal
14577			 * implementation in terms of speed, although the others are provided
14578			 * for compatibility with existing Javascript sort functions.
14579			 *
14580			 * `{type}-pre`: Functions defined take a single parameter:
14581			 *
14582		     *  1. `{*}` Data from the column cell to be prepared for ordering
14583			 *
14584			 * And return:
14585			 *
14586			 * * `{*}` Data to be sorted upon
14587			 *
14588			 * `{type}-asc` and `{type}-desc`: Functions are typical Javascript sort
14589			 * functions, taking two parameters:
14590			 *
14591		     *  1. `{*}` Data to compare to the second parameter
14592		     *  2. `{*}` Data to compare to the first parameter
14593			 *
14594			 * And returning:
14595			 *
14596			 * * `{*}` Ordering match: <0 if first parameter should be sorted lower
14597			 *   than the second parameter, ===0 if the two parameters are equal and
14598			 *   >0 if the first parameter should be sorted height than the second
14599			 *   parameter.
14600			 * 
14601			 *  @type object
14602			 *  @default {}
14603			 *
14604			 *  @example
14605			 *    // Numeric ordering of formatted numbers with a pre-formatter
14606			 *    $.extend( $.fn.dataTable.ext.type.order, {
14607			 *      "string-pre": function(x) {
14608			 *        a = (a === "-" || a === "") ? 0 : a.replace( /[^\d\-\.]/g, "" );
14609			 *        return parseFloat( a );
14610			 *      }
14611			 *    } );
14612			 *
14613			 *  @example
14614			 *    // Case-sensitive string ordering, with no pre-formatting method
14615			 *    $.extend( $.fn.dataTable.ext.order, {
14616			 *      "string-case-asc": function(x,y) {
14617			 *        return ((x < y) ? -1 : ((x > y) ? 1 : 0));
14618			 *      },
14619			 *      "string-case-desc": function(x,y) {
14620			 *        return ((x < y) ? 1 : ((x > y) ? -1 : 0));
14621			 *      }
14622			 *    } );
14623			 */
14624			order: {}
14625		},
14626	
14627		/**
14628		 * Unique DataTables instance counter
14629		 *
14630		 * @type int
14631		 * @private
14632		 */
14633		_unique: 0,
14634	
14635	
14636		//
14637		// Depreciated
14638		// The following properties are retained for backwards compatibility only.
14639		// The should not be used in new projects and will be removed in a future
14640		// version
14641		//
14642	
14643		/**
14644		 * Version check function.
14645		 *  @type function
14646		 *  @depreciated Since 1.10
14647		 */
14648		fnVersionCheck: DataTable.fnVersionCheck,
14649	
14650	
14651		/**
14652		 * Index for what 'this' index API functions should use
14653		 *  @type int
14654		 *  @deprecated Since v1.10
14655		 */
14656		iApiIndex: 0,
14657	
14658	
14659		/**
14660		 * jQuery UI class container
14661		 *  @type object
14662		 *  @deprecated Since v1.10
14663		 */
14664		oJUIClasses: {},
14665	
14666	
14667		/**
14668		 * Software version
14669		 *  @type string
14670		 *  @deprecated Since v1.10
14671		 */
vendor: 31,389 bytes, lines 14672-15816
14672		sVersion: DataTable.version
14673	};
14674	
14675	
14676	//
14677	// Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts
14678	//
14679	$.extend( _ext, {
14680		afnFiltering: _ext.search,
14681		aTypes:       _ext.type.detect,
14682		ofnSearch:    _ext.type.search,
14683		oSort:        _ext.type.order,
14684		afnSortData:  _ext.order,
14685		aoFeatures:   _ext.feature,
14686		oApi:         _ext.internal,
14687		oStdClasses:  _ext.classes,
14688		oPagination:  _ext.pager
14689	} );
14690	
14691	
14692	$.extend( DataTable.ext.classes, {
14693		"sTable": "dataTable",
14694		"sNoFooter": "no-footer",
14695	
14696		/* Paging buttons */
14697		"sPageButton": "paginate_button",
14698		"sPageButtonActive": "current",
14699		"sPageButtonDisabled": "disabled",
14700	
14701		/* Striping classes */
14702		"sStripeOdd": "odd",
14703		"sStripeEven": "even",
14704	
14705		/* Empty row */
14706		"sRowEmpty": "dataTables_empty",
14707	
14708		/* Features */
14709		"sWrapper": "dataTables_wrapper",
14710		"sFilter": "dataTables_filter",
14711		"sInfo": "dataTables_info",
14712		"sPaging": "dataTables_paginate paging_", /* Note that the type is postfixed */
14713		"sLength": "dataTables_length",
14714		"sProcessing": "dataTables_processing",
14715	
14716		/* Sorting */
14717		"sSortAsc": "sorting_asc",
14718		"sSortDesc": "sorting_desc",
14719		"sSortable": "sorting", /* Sortable in both directions */
14720		"sSortableAsc": "sorting_desc_disabled",
14721		"sSortableDesc": "sorting_asc_disabled",
14722		"sSortableNone": "sorting_disabled",
14723		"sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */
14724	
14725		/* Filtering */
14726		"sFilterInput": "",
14727	
14728		/* Page length */
14729		"sLengthSelect": "",
14730	
14731		/* Scrolling */
14732		"sScrollWrapper": "dataTables_scroll",
14733		"sScrollHead": "dataTables_scrollHead",
14734		"sScrollHeadInner": "dataTables_scrollHeadInner",
14735		"sScrollBody": "dataTables_scrollBody",
14736		"sScrollFoot": "dataTables_scrollFoot",
14737		"sScrollFootInner": "dataTables_scrollFootInner",
14738	
14739		/* Misc */
14740		"sHeaderTH": "",
14741		"sFooterTH": "",
14742	
14743		// Deprecated
14744		"sSortJUIAsc": "",
14745		"sSortJUIDesc": "",
14746		"sSortJUI": "",
14747		"sSortJUIAscAllowed": "",
14748		"sSortJUIDescAllowed": "",
14749		"sSortJUIWrapper": "",
14750		"sSortIcon": "",
14751		"sJUIHeader": "",
14752		"sJUIFooter": ""
14753	} );
14754	
14755	
14756	var extPagination = DataTable.ext.pager;
14757	
14758	function _numbers ( page, pages ) {
14759		var
14760			numbers = [],
14761			buttons = extPagination.numbers_length,
14762			half = Math.floor( buttons / 2 ),
14763			i = 1;
14764	
14765		if ( pages <= buttons ) {
14766			numbers = _range( 0, pages );
14767		}
14768		else if ( page <= half ) {
14769			numbers = _range( 0, buttons-2 );
14770			numbers.push( 'ellipsis' );
14771			numbers.push( pages-1 );
14772		}
14773		else if ( page >= pages - 1 - half ) {
14774			numbers = _range( pages-(buttons-2), pages );
14775			numbers.splice( 0, 0, 'ellipsis' ); // no unshift in ie6
14776			numbers.splice( 0, 0, 0 );
14777		}
14778		else {
14779			numbers = _range( page-half+2, page+half-1 );
14780			numbers.push( 'ellipsis' );
14781			numbers.push( pages-1 );
14782			numbers.splice( 0, 0, 'ellipsis' );
14783			numbers.splice( 0, 0, 0 );
14784		}
14785	
14786		numbers.DT_el = 'span';
14787		return numbers;
14788	}
14789	
14790	
14791	$.extend( extPagination, {
14792		simple: function ( page, pages ) {
14793			return [ 'previous', 'next' ];
14794		},
14795	
14796		full: function ( page, pages ) {
14797			return [  'first', 'previous', 'next', 'last' ];
14798		},
14799	
14800		numbers: function ( page, pages ) {
14801			return [ _numbers(page, pages) ];
14802		},
14803	
14804		simple_numbers: function ( page, pages ) {
14805			return [ 'previous', _numbers(page, pages), 'next' ];
14806		},
14807	
14808		full_numbers: function ( page, pages ) {
14809			return [ 'first', 'previous', _numbers(page, pages), 'next', 'last' ];
14810		},
14811		
14812		first_last_numbers: function (page, pages) {
14813	 		return ['first', _numbers(page, pages), 'last'];
14814	 	},
14815	
14816		// For testing and plug-ins to use
14817		_numbers: _numbers,
14818	
14819		// Number of number buttons (including ellipsis) to show. _Must be odd!_
14820		numbers_length: 7
14821	} );
14822	
14823	
14824	$.extend( true, DataTable.ext.renderer, {
14825		pageButton: {
14826			_: function ( settings, host, idx, buttons, page, pages ) {
14827				var classes = settings.oClasses;
14828				var lang = settings.oLanguage.oPaginate;
14829				var aria = settings.oLanguage.oAria.paginate || {};
14830				var btnDisplay, btnClass;
14831	
14832				var attach = function( container, buttons ) {
14833					var i, ien, node, button, tabIndex;
14834					var disabledClass = classes.sPageButtonDisabled;
14835					var clickHandler = function ( e ) {
14836						_fnPageChange( settings, e.data.action, true );
14837					};
14838	
14839					for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
14840						button = buttons[i];
14841	
14842						if ( Array.isArray( button ) ) {
14843							var inner = $( '<'+(button.DT_el || 'div')+'/>' )
14844								.appendTo( container );
14845							attach( inner, button );
14846						}
14847						else {
14848							btnDisplay = null;
14849							btnClass = button;
14850							tabIndex = settings.iTabIndex;
14851	
14852							switch ( button ) {
14853								case 'ellipsis':
14854									container.append('<span class="ellipsis">&#x2026;</span>');
14855									break;
14856	
14857								case 'first':
14858									btnDisplay = lang.sFirst;
14859	
14860									if ( page === 0 ) {
14861										tabIndex = -1;
14862										btnClass += ' ' + disabledClass;
14863									}
14864									break;
14865	
14866								case 'previous':
14867									btnDisplay = lang.sPrevious;
14868	
14869									if ( page === 0 ) {
14870										tabIndex = -1;
14871										btnClass += ' ' + disabledClass;
14872									}
14873									break;
14874	
14875								case 'next':
14876									btnDisplay = lang.sNext;
14877	
14878									if ( pages === 0 || page === pages-1 ) {
14879										tabIndex = -1;
14880										btnClass += ' ' + disabledClass;
14881									}
14882									break;
14883	
14884								case 'last':
14885									btnDisplay = lang.sLast;
14886	
14887									if ( pages === 0 || page === pages-1 ) {
14888										tabIndex = -1;
14889										btnClass += ' ' + disabledClass;
14890									}
14891									break;
14892	
14893								default:
14894									btnDisplay = settings.fnFormatNumber( button + 1 );
14895									btnClass = page === button ?
14896										classes.sPageButtonActive : '';
14897									break;
14898							}
14899	
14900							if ( btnDisplay !== null ) {
14901								var tag = settings.oInit.pagingTag || 'a';
14902								var disabled = btnClass.indexOf(disabledClass) !== -1;
14903			
14904	
14905								node = $('<'+tag+'>', {
14906										'class': classes.sPageButton+' '+btnClass,
14907										'aria-controls': settings.sTableId,
14908										'aria-disabled': disabled ? 'true' : null,
14909										'aria-label': aria[ button ],
14910										'aria-role': 'link',
14911										'aria-current': btnClass === classes.sPageButtonActive ? 'page' : null,
14912										'data-dt-idx': button,
14913										'tabindex': tabIndex,
14914										'id': idx === 0 && typeof button === 'string' ?
14915											settings.sTableId +'_'+ button :
14916											null
14917									} )
14918									.html( btnDisplay )
14919									.appendTo( container );
14920	
14921								_fnBindAction(
14922									node, {action: button}, clickHandler
14923								);
14924							}
14925						}
14926					}
14927				};
14928	
14929				// IE9 throws an 'unknown error' if document.activeElement is used
14930				// inside an iframe or frame. Try / catch the error. Not good for
14931				// accessibility, but neither are frames.
14932				var activeEl;
14933	
14934				try {
14935					// Because this approach is destroying and recreating the paging
14936					// elements, focus is lost on the select button which is bad for
14937					// accessibility. So we want to restore focus once the draw has
14938					// completed
14939					activeEl = $(host).find(document.activeElement).data('dt-idx');
14940				}
14941				catch (e) {}
14942	
14943				attach( $(host).empty(), buttons );
14944	
14945				if ( activeEl !== undefined ) {
14946					$(host).find( '[data-dt-idx='+activeEl+']' ).trigger('focus');
14947				}
14948			}
14949		}
14950	} );
14951	
14952	
14953	
14954	// Built in type detection. See model.ext.aTypes for information about
14955	// what is required from this methods.
14956	$.extend( DataTable.ext.type.detect, [
14957		// Plain numbers - first since V8 detects some plain numbers as dates
14958		// e.g. Date.parse('55') (but not all, e.g. Date.parse('22')...).
14959		function ( d, settings )
14960		{
14961			var decimal = settings.oLanguage.sDecimal;
14962			return _isNumber( d, decimal ) ? 'num'+decimal : null;
14963		},
14964	
14965		// Dates (only those recognised by the browser's Date.parse)
14966		function ( d, settings )
14967		{
14968			// V8 tries _very_ hard to make a string passed into `Date.parse()`
14969			// valid, so we need to use a regex to restrict date formats. Use a
14970			// plug-in for anything other than ISO8601 style strings
14971			if ( d && !(d instanceof Date) && ! _re_date.test(d) ) {
14972				return null;
14973			}
14974			var parsed = Date.parse(d);
14975			return (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null;
14976		},
14977	
14978		// Formatted numbers
14979		function ( d, settings )
14980		{
14981			var decimal = settings.oLanguage.sDecimal;
14982			return _isNumber( d, decimal, true ) ? 'num-fmt'+decimal : null;
14983		},
14984	
14985		// HTML numeric
14986		function ( d, settings )
14987		{
14988			var decimal = settings.oLanguage.sDecimal;
14989			return _htmlNumeric( d, decimal ) ? 'html-num'+decimal : null;
14990		},
14991	
14992		// HTML numeric, formatted
14993		function ( d, settings )
14994		{
14995			var decimal = settings.oLanguage.sDecimal;
14996			return _htmlNumeric( d, decimal, true ) ? 'html-num-fmt'+decimal : null;
14997		},
14998	
14999		// HTML (this is strict checking - there must be html)
15000		function ( d, settings )
15001		{
15002			return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1) ?
15003				'html' : null;
15004		}
15005	] );
15006	
15007	
15008	
15009	// Filter formatting functions. See model.ext.ofnSearch for information about
15010	// what is required from these methods.
15011	// 
15012	// Note that additional search methods are added for the html numbers and
15013	// html formatted numbers by `_addNumericSort()` when we know what the decimal
15014	// place is
15015	
15016	
15017	$.extend( DataTable.ext.type.search, {
15018		html: function ( data ) {
15019			return _empty(data) ?
15020				data :
15021				typeof data === 'string' ?
15022					data
15023						.replace( _re_new_lines, " " )
15024						.replace( _re_html, "" ) :
15025					'';
15026		},
15027	
15028		string: function ( data ) {
15029			return _empty(data) ?
15030				data :
15031				typeof data === 'string' ?
15032					data.replace( _re_new_lines, " " ) :
15033					data;
15034		}
15035	} );
15036	
15037	
15038	
15039	var __numericReplace = function ( d, decimalPlace, re1, re2 ) {
15040		if ( d !== 0 && (!d || d === '-') ) {
15041			return -Infinity;
15042		}
15043		
15044		let type = typeof d;
15045	
15046		if (type === 'number' || type === 'bigint') {
15047			return d;
15048		}
15049	
15050		// If a decimal place other than `.` is used, it needs to be given to the
15051		// function so we can detect it and replace with a `.` which is the only
15052		// decimal place Javascript recognises - it is not locale aware.
15053		if ( decimalPlace ) {
15054			d = _numToDecimal( d, decimalPlace );
15055		}
15056	
15057		if ( d.replace ) {
15058			if ( re1 ) {
15059				d = d.replace( re1, '' );
15060			}
15061	
15062			if ( re2 ) {
15063				d = d.replace( re2, '' );
15064			}
15065		}
15066	
15067		return d * 1;
15068	};
15069	
15070	
15071	// Add the numeric 'deformatting' functions for sorting and search. This is done
15072	// in a function to provide an easy ability for the language options to add
15073	// additional methods if a non-period decimal place is used.
15074	function _addNumericSort ( decimalPlace ) {
15075		$.each(
15076			{
15077				// Plain numbers
15078				"num": function ( d ) {
15079					return __numericReplace( d, decimalPlace );
15080				},
15081	
15082				// Formatted numbers
15083				"num-fmt": function ( d ) {
15084					return __numericReplace( d, decimalPlace, _re_formatted_numeric );
15085				},
15086	
15087				// HTML numeric
15088				"html-num": function ( d ) {
15089					return __numericReplace( d, decimalPlace, _re_html );
15090				},
15091	
15092				// HTML numeric, formatted
15093				"html-num-fmt": function ( d ) {
15094					return __numericReplace( d, decimalPlace, _re_html, _re_formatted_numeric );
15095				}
15096			},
15097			function ( key, fn ) {
15098				// Add the ordering method
15099				_ext.type.order[ key+decimalPlace+'-pre' ] = fn;
15100	
15101				// For HTML types add a search formatter that will strip the HTML
15102				if ( key.match(/^html\-/) ) {
15103					_ext.type.search[ key+decimalPlace ] = _ext.type.search.html;
15104				}
15105			}
15106		);
15107	}
15108	
15109	
15110	// Default sort methods
15111	$.extend( _ext.type.order, {
15112		// Dates
15113		"date-pre": function ( d ) {
15114			var ts = Date.parse( d );
15115			return isNaN(ts) ? -Infinity : ts;
15116		},
15117	
15118		// html
15119		"html-pre": function ( a ) {
15120			return _empty(a) ?
15121				'' :
15122				a.replace ?
15123					a.replace( /<.*?>/g, "" ).toLowerCase() :
15124					a+'';
15125		},
15126	
15127		// string
15128		"string-pre": function ( a ) {
15129			// This is a little complex, but faster than always calling toString,
15130			// http://jsperf.com/tostring-v-check
15131			return _empty(a) ?
15132				'' :
15133				typeof a === 'string' ?
15134					a.toLowerCase() :
15135					! a.toString ?
15136						'' :
15137						a.toString();
15138		},
15139	
15140		// string-asc and -desc are retained only for compatibility with the old
15141		// sort methods
15142		"string-asc": function ( x, y ) {
15143			return ((x < y) ? -1 : ((x > y) ? 1 : 0));
15144		},
15145	
15146		"string-desc": function ( x, y ) {
15147			return ((x < y) ? 1 : ((x > y) ? -1 : 0));
15148		}
15149	} );
15150	
15151	
15152	// Numeric sorting types - order doesn't matter here
15153	_addNumericSort( '' );
15154	
15155	
15156	$.extend( true, DataTable.ext.renderer, {
15157		header: {
15158			_: function ( settings, cell, column, classes ) {
15159				// No additional mark-up required
15160				// Attach a sort listener to update on sort - note that using the
15161				// `DT` namespace will allow the event to be removed automatically
15162				// on destroy, while the `dt` namespaced event is the one we are
15163				// listening for
15164				$(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {
15165					if ( settings !== ctx ) { // need to check this this is the host
15166						return;               // table, not a nested one
15167					}
15168	
15169					var colIdx = column.idx;
15170	
15171					cell
15172						.removeClass(
15173							classes.sSortAsc +' '+
15174							classes.sSortDesc
15175						)
15176						.addClass( columns[ colIdx ] == 'asc' ?
15177							classes.sSortAsc : columns[ colIdx ] == 'desc' ?
15178								classes.sSortDesc :
15179								column.sSortingClass
15180						);
15181				} );
15182			},
15183	
15184			jqueryui: function ( settings, cell, column, classes ) {
15185				$('<div/>')
15186					.addClass( classes.sSortJUIWrapper )
15187					.append( cell.contents() )
15188					.append( $('<span/>')
15189						.addClass( classes.sSortIcon+' '+column.sSortingClassJUI )
15190					)
15191					.appendTo( cell );
15192	
15193				// Attach a sort listener to update on sort
15194				$(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {
15195					if ( settings !== ctx ) {
15196						return;
15197					}
15198	
15199					var colIdx = column.idx;
15200	
15201					cell
15202						.removeClass( classes.sSortAsc +" "+classes.sSortDesc )
15203						.addClass( columns[ colIdx ] == 'asc' ?
15204							classes.sSortAsc : columns[ colIdx ] == 'desc' ?
15205								classes.sSortDesc :
15206								column.sSortingClass
15207						);
15208	
15209					cell
15210						.find( 'span.'+classes.sSortIcon )
15211						.removeClass(
15212							classes.sSortJUIAsc +" "+
15213							classes.sSortJUIDesc +" "+
15214							classes.sSortJUI +" "+
15215							classes.sSortJUIAscAllowed +" "+
15216							classes.sSortJUIDescAllowed
15217						)
15218						.addClass( columns[ colIdx ] == 'asc' ?
15219							classes.sSortJUIAsc : columns[ colIdx ] == 'desc' ?
15220								classes.sSortJUIDesc :
15221								column.sSortingClassJUI
15222						);
15223				} );
15224			}
15225		}
15226	} );
15227	
15228	/*
15229	 * Public helper functions. These aren't used internally by DataTables, or
15230	 * called by any of the options passed into DataTables, but they can be used
15231	 * externally by developers working with DataTables. They are helper functions
15232	 * to make working with DataTables a little bit easier.
15233	 */
15234	
15235	var __htmlEscapeEntities = function ( d ) {
15236		if (Array.isArray(d)) {
15237			d = d.join(',');
15238		}
15239	
15240		return typeof d === 'string' ?
15241			d
15242				.replace(/&/g, '&amp;')
15243				.replace(/</g, '&lt;')
15244				.replace(/>/g, '&gt;')
15245				.replace(/"/g, '&quot;') :
15246			d;
15247	};
15248	
15249	// Common logic for moment, luxon or a date action
15250	function __mld( dt, momentFn, luxonFn, dateFn, arg1 ) {
15251		if (window.moment) {
15252			return dt[momentFn]( arg1 );
15253		}
15254		else if (window.luxon) {
15255			return dt[luxonFn]( arg1 );
15256		}
15257		
15258		return dateFn ? dt[dateFn]( arg1 ) : dt;
15259	}
15260	
15261	
15262	var __mlWarning = false;
15263	function __mldObj (d, format, locale) {
15264		var dt;
15265	
15266		if (window.moment) {
15267			dt = window.moment.utc( d, format, locale, true );
15268	
15269			if (! dt.isValid()) {
15270				return null;
15271			}
15272		}
15273		else if (window.luxon) {
15274			dt = format && typeof d === 'string'
15275				? window.luxon.DateTime.fromFormat( d, format )
15276				: window.luxon.DateTime.fromISO( d );
15277	
15278			if (! dt.isValid) {
15279				return null;
15280			}
15281	
15282			dt.setLocale(locale);
15283		}
15284		else if (! format) {
15285			// No format given, must be ISO
15286			dt = new Date(d);
15287		}
15288		else {
15289			if (! __mlWarning) {
15290				alert('DataTables warning: Formatted date without Moment.js or Luxon - https://datatables.net/tn/17');
15291			}
15292	
15293			__mlWarning = true;
15294		}
15295	
15296		return dt;
15297	}
15298	
15299	// Wrapper for date, datetime and time which all operate the same way with the exception of
15300	// the output string for auto locale support
15301	function __mlHelper (localeString) {
15302		return function ( from, to, locale, def ) {
15303			// Luxon and Moment support
15304			// Argument shifting
15305			if ( arguments.length === 0 ) {
15306				locale = 'en';
15307				to = null; // means toLocaleString
15308				from = null; // means iso8601
15309			}
15310			else if ( arguments.length === 1 ) {
15311				locale = 'en';
15312				to = from;
15313				from = null;
15314			}
15315			else if ( arguments.length === 2 ) {
15316				locale = to;
15317				to = from;
15318				from = null;
15319			}
15320	
15321			var typeName = 'datetime-' + to;
15322	
15323			// Add type detection and sorting specific to this date format - we need to be able to identify
15324			// date type columns as such, rather than as numbers in extensions. Hence the need for this.
15325			if (! DataTable.ext.type.order[typeName]) {
15326				// The renderer will give the value to type detect as the type!
15327				DataTable.ext.type.detect.unshift(function (d) {
15328					return d === typeName ? typeName : false;
15329				});
15330	
15331				// The renderer gives us Moment, Luxon or Date obects for the sorting, all of which have a
15332				// `valueOf` which gives milliseconds epoch
15333				DataTable.ext.type.order[typeName + '-asc'] = function (a, b) {
15334					var x = a.valueOf();
15335					var y = b.valueOf();
15336	
15337					return x === y
15338						? 0
15339						: x < y
15340							? -1
15341							: 1;
15342				}
15343	
15344				DataTable.ext.type.order[typeName + '-desc'] = function (a, b) {
15345					var x = a.valueOf();
15346					var y = b.valueOf();
15347	
15348					return x === y
15349						? 0
15350						: x > y
15351							? -1
15352							: 1;
15353				}
15354			}
15355		
15356			return function ( d, type ) {
15357				// Allow for a default value
15358				if (d === null || d === undefined) {
15359					if (def === '--now') {
15360						// We treat everything as UTC further down, so no changes are
15361						// made, as such need to get the local date / time as if it were
15362						// UTC
15363						var local = new Date();
15364						d = new Date( Date.UTC(
15365							local.getFullYear(), local.getMonth(), local.getDate(),
15366							local.getHours(), local.getMinutes(), local.getSeconds()
15367						) );
15368					}
15369					else {
15370						d = '';
15371					}
15372				}
15373	
15374				if (type === 'type') {
15375					// Typing uses the type name for fast matching
15376					return typeName;
15377				}
15378	
15379				if (d === '') {
15380					return type !== 'sort'
15381						? ''
15382						: __mldObj('0000-01-01 00:00:00', null, locale);
15383				}
15384	
15385				// Shortcut. If `from` and `to` are the same, we are using the renderer to
15386				// format for ordering, not display - its already in the display format.
15387				if ( to !== null && from === to && type !== 'sort' && type !== 'type' && ! (d instanceof Date) ) {
15388					return d;
15389				}
15390	
15391				var dt = __mldObj(d, from, locale);
15392	
15393				if (dt === null) {
15394					return d;
15395				}
15396	
15397				if (type === 'sort') {
15398					return dt;
15399				}
15400				
15401				var formatted = to === null
15402					? __mld(dt, 'toDate', 'toJSDate', '')[localeString]()
15403					: __mld(dt, 'format', 'toFormat', 'toISOString', to);
15404	
15405				// XSS protection
15406				return type === 'display' ?
15407					__htmlEscapeEntities( formatted ) :
15408					formatted;
15409			};
15410		}
15411	}
15412	
15413	// Based on locale, determine standard number formatting
15414	// Fallback for legacy browsers is US English
15415	var __thousands = ',';
15416	var __decimal = '.';
15417	
15418	if (Intl) {
15419		try {
15420			var num = new Intl.NumberFormat().formatToParts(100000.1);
15421		
15422			for (var i=0 ; i<num.length ; i++) {
15423				if (num[i].type === 'group') {
15424					__thousands = num[i].value;
15425				}
15426				else if (num[i].type === 'decimal') {
15427					__decimal = num[i].value;
15428				}
15429			}
15430		}
15431		catch (e) {
15432			// noop
15433		}
15434	}
15435	
15436	// Formatted date time detection - use by declaring the formats you are going to use
15437	DataTable.datetime = function ( format, locale ) {
15438		var typeName = 'datetime-detect-' + format;
15439	
15440		if (! locale) {
15441			locale = 'en';
15442		}
15443	
15444		if (! DataTable.ext.type.order[typeName]) {
15445			DataTable.ext.type.detect.unshift(function (d) {
15446				var dt = __mldObj(d, format, locale);
15447				return d === '' || dt ? typeName : false;
15448			});
15449	
15450			DataTable.ext.type.order[typeName + '-pre'] = function (d) {
15451				return __mldObj(d, format, locale) || 0;
15452			}
15453		}
15454	}
15455	
15456	/**
15457	 * Helpers for `columns.render`.
15458	 *
15459	 * The options defined here can be used with the `columns.render` initialisation
15460	 * option to provide a display renderer. The following functions are defined:
15461	 *
15462	 * * `number` - Will format numeric data (defined by `columns.data`) for
15463	 *   display, retaining the original unformatted data for sorting and filtering.
15464	 *   It takes 5 parameters:
15465	 *   * `string` - Thousands grouping separator
15466	 *   * `string` - Decimal point indicator
15467	 *   * `integer` - Number of decimal points to show
15468	 *   * `string` (optional) - Prefix.
15469	 *   * `string` (optional) - Postfix (/suffix).
15470	 * * `text` - Escape HTML to help prevent XSS attacks. It has no optional
15471	 *   parameters.
15472	 *
15473	 * @example
15474	 *   // Column definition using the number renderer
15475	 *   {
15476	 *     data: "salary",
15477	 *     render: $.fn.dataTable.render.number( '\'', '.', 0, '$' )
15478	 *   }
15479	 *
15480	 * @namespace
15481	 */
15482	DataTable.render = {
15483		date: __mlHelper('toLocaleDateString'),
15484		datetime: __mlHelper('toLocaleString'),
15485		time: __mlHelper('toLocaleTimeString'),
15486		number: function ( thousands, decimal, precision, prefix, postfix ) {
15487			// Auto locale detection
15488			if (thousands === null || thousands === undefined) {
15489				thousands = __thousands;
15490			}
15491	
15492			if (decimal === null || decimal === undefined) {
15493				decimal = __decimal;
15494			}
15495	
15496			return {
15497				display: function ( d ) {
15498					if ( typeof d !== 'number' && typeof d !== 'string' ) {
15499						return d;
15500					}
15501	
15502					if (d === '' || d === null) {
15503						return d;
15504					}
15505	
15506					var negative = d < 0 ? '-' : '';
15507					var flo = parseFloat( d );
15508	
15509					// If NaN then there isn't much formatting that we can do - just
15510					// return immediately, escaping any HTML (this was supposed to
15511					// be a number after all)
15512					if ( isNaN( flo ) ) {
15513						return __htmlEscapeEntities( d );
15514					}
15515	
15516					flo = flo.toFixed( precision );
15517					d = Math.abs( flo );
15518	
15519					var intPart = parseInt( d, 10 );
15520					var floatPart = precision ?
15521						decimal+(d - intPart).toFixed( precision ).substring( 2 ):
15522						'';
15523	
15524					// If zero, then can't have a negative prefix
15525					if (intPart === 0 && parseFloat(floatPart) === 0) {
15526						negative = '';
15527					}
15528	
15529					return negative + (prefix||'') +
15530						intPart.toString().replace(
15531							/\B(?=(\d{3})+(?!\d))/g, thousands
15532						) +
15533						floatPart +
15534						(postfix||'');
15535				}
15536			};
15537		},
15538	
15539		text: function () {
15540			return {
15541				display: __htmlEscapeEntities,
15542				filter: __htmlEscapeEntities
15543			};
15544		}
15545	};
15546	
15547	
15548	/*
15549	 * This is really a good bit rubbish this method of exposing the internal methods
15550	 * publicly... - To be fixed in 2.0 using methods on the prototype
15551	 */
15552	
15553	
15554	/**
15555	 * Create a wrapper function for exporting an internal functions to an external API.
15556	 *  @param {string} fn API function name
15557	 *  @returns {function} wrapped function
15558	 *  @memberof DataTable#internal
15559	 */
15560	function _fnExternApiFunc (fn)
15561	{
15562		return function() {
15563			var args = [_fnSettingsFromNode( this[DataTable.ext.iApiIndex] )].concat(
15564				Array.prototype.slice.call(arguments)
15565			);
15566			return DataTable.ext.internal[fn].apply( this, args );
15567		};
15568	}
15569	
15570	
15571	/**
15572	 * Reference to internal functions for use by plug-in developers. Note that
15573	 * these methods are references to internal functions and are considered to be
15574	 * private. If you use these methods, be aware that they are liable to change
15575	 * between versions.
15576	 *  @namespace
15577	 */
15578	$.extend( DataTable.ext.internal, {
15579		_fnExternApiFunc: _fnExternApiFunc,
15580		_fnBuildAjax: _fnBuildAjax,
15581		_fnAjaxUpdate: _fnAjaxUpdate,
15582		_fnAjaxParameters: _fnAjaxParameters,
15583		_fnAjaxUpdateDraw: _fnAjaxUpdateDraw,
15584		_fnAjaxDataSrc: _fnAjaxDataSrc,
15585		_fnAddColumn: _fnAddColumn,
15586		_fnColumnOptions: _fnColumnOptions,
15587		_fnAdjustColumnSizing: _fnAdjustColumnSizing,
15588		_fnVisibleToColumnIndex: _fnVisibleToColumnIndex,
15589		_fnColumnIndexToVisible: _fnColumnIndexToVisible,
15590		_fnVisbleColumns: _fnVisbleColumns,
15591		_fnGetColumns: _fnGetColumns,
15592		_fnColumnTypes: _fnColumnTypes,
15593		_fnApplyColumnDefs: _fnApplyColumnDefs,
15594		_fnHungarianMap: _fnHungarianMap,
15595		_fnCamelToHungarian: _fnCamelToHungarian,
15596		_fnLanguageCompat: _fnLanguageCompat,
15597		_fnBrowserDetect: _fnBrowserDetect,
15598		_fnAddData: _fnAddData,
15599		_fnAddTr: _fnAddTr,
15600		_fnNodeToDataIndex: _fnNodeToDataIndex,
15601		_fnNodeToColumnIndex: _fnNodeToColumnIndex,
15602		_fnGetCellData: _fnGetCellData,
15603		_fnSetCellData: _fnSetCellData,
15604		_fnSplitObjNotation: _fnSplitObjNotation,
15605		_fnGetObjectDataFn: _fnGetObjectDataFn,
15606		_fnSetObjectDataFn: _fnSetObjectDataFn,
15607		_fnGetDataMaster: _fnGetDataMaster,
15608		_fnClearTable: _fnClearTable,
15609		_fnDeleteIndex: _fnDeleteIndex,
15610		_fnInvalidate: _fnInvalidate,
15611		_fnGetRowElements: _fnGetRowElements,
15612		_fnCreateTr: _fnCreateTr,
15613		_fnBuildHead: _fnBuildHead,
15614		_fnDrawHead: _fnDrawHead,
15615		_fnDraw: _fnDraw,
15616		_fnReDraw: _fnReDraw,
15617		_fnAddOptionsHtml: _fnAddOptionsHtml,
15618		_fnDetectHeader: _fnDetectHeader,
15619		_fnGetUniqueThs: _fnGetUniqueThs,
15620		_fnFeatureHtmlFilter: _fnFeatureHtmlFilter,
15621		_fnFilterComplete: _fnFilterComplete,
15622		_fnFilterCustom: _fnFilterCustom,
15623		_fnFilterColumn: _fnFilterColumn,
15624		_fnFilter: _fnFilter,
15625		_fnFilterCreateSearch: _fnFilterCreateSearch,
15626		_fnEscapeRegex: _fnEscapeRegex,
15627		_fnFilterData: _fnFilterData,
15628		_fnFeatureHtmlInfo: _fnFeatureHtmlInfo,
15629		_fnUpdateInfo: _fnUpdateInfo,
15630		_fnInfoMacros: _fnInfoMacros,
15631		_fnInitialise: _fnInitialise,
15632		_fnInitComplete: _fnInitComplete,
15633		_fnLengthChange: _fnLengthChange,
15634		_fnFeatureHtmlLength: _fnFeatureHtmlLength,
15635		_fnFeatureHtmlPaginate: _fnFeatureHtmlPaginate,
15636		_fnPageChange: _fnPageChange,
15637		_fnFeatureHtmlProcessing: _fnFeatureHtmlProcessing,
15638		_fnProcessingDisplay: _fnProcessingDisplay,
15639		_fnFeatureHtmlTable: _fnFeatureHtmlTable,
15640		_fnScrollDraw: _fnScrollDraw,
15641		_fnApplyToChildren: _fnApplyToChildren,
15642		_fnCalculateColumnWidths: _fnCalculateColumnWidths,
15643		_fnThrottle: _fnThrottle,
15644		_fnConvertToWidth: _fnConvertToWidth,
15645		_fnGetWidestNode: _fnGetWidestNode,
15646		_fnGetMaxLenString: _fnGetMaxLenString,
15647		_fnStringToCss: _fnStringToCss,
15648		_fnSortFlatten: _fnSortFlatten,
15649		_fnSort: _fnSort,
15650		_fnSortAria: _fnSortAria,
15651		_fnSortListener: _fnSortListener,
15652		_fnSortAttachListener: _fnSortAttachListener,
15653		_fnSortingClasses: _fnSortingClasses,
15654		_fnSortData: _fnSortData,
15655		_fnSaveState: _fnSaveState,
15656		_fnLoadState: _fnLoadState,
15657		_fnImplementState: _fnImplementState,
15658		_fnSettingsFromNode: _fnSettingsFromNode,
15659		_fnLog: _fnLog,
15660		_fnMap: _fnMap,
15661		_fnBindAction: _fnBindAction,
15662		_fnCallbackReg: _fnCallbackReg,
15663		_fnCallbackFire: _fnCallbackFire,
15664		_fnLengthOverflow: _fnLengthOverflow,
15665		_fnRenderer: _fnRenderer,
15666		_fnDataSource: _fnDataSource,
15667		_fnRowAttributes: _fnRowAttributes,
15668		_fnExtend: _fnExtend,
15669		_fnCalculateEnd: function () {} // Used by a lot of plug-ins, but redundant
15670		                                // in 1.10, so this dead-end function is
15671		                                // added to prevent errors
15672	} );
15673	
15674	
15675	// jQuery access
15676	$.fn.dataTable = DataTable;
15677	
15678	// Provide access to the host jQuery object (circular reference)
15679	DataTable.$ = $;
15680	
15681	// Legacy aliases
15682	$.fn.dataTableSettings = DataTable.settings;
15683	$.fn.dataTableExt = DataTable.ext;
15684	
15685	// With a capital `D` we return a DataTables API instance rather than a
15686	// jQuery object
15687	$.fn.DataTable = function ( opts ) {
15688		return $(this).dataTable( opts ).api();
15689	};
15690	
15691	// All properties that are available to $.fn.dataTable should also be
15692	// available on $.fn.DataTable
15693	$.each( DataTable, function ( prop, val ) {
15694		$.fn.DataTable[ prop ] = val;
15695	} );
15696
15697	return DataTable;
15698}));
15699
15700
15701/*! DataTables styling integration
15702 * ©2018 SpryMedia Ltd - datatables.net/license
15703 */
15704
15705(function( factory ){
15706	if ( typeof define === 'function' && define.amd ) {
15707		// AMD
15708		define( ['jquery', 'datatables.net'], function ( $ ) {
15709			return factory( $, window, document );
15710		} );
15711	}
15712	else if ( typeof exports === 'object' ) {
15713		// CommonJS
15714		var jq = require('jquery');
15715		var cjsRequires = function (root, $) {
15716			if ( ! $.fn.dataTable ) {
15717				require('datatables.net')(root, $);
15718			}
15719		};
15720
15721		if (typeof window !== 'undefined') {
15722			module.exports = function (root, $) {
15723				if ( ! root ) {
15724					// CommonJS environments without a window global must pass a
15725					// root. This will give an error otherwise
15726					root = window;
15727				}
15728
15729				if ( ! $ ) {
15730					$ = jq( root );
15731				}
15732
15733				cjsRequires( root, $ );
15734				return factory( $, root, root.document );
15735			};
15736		}
15737		else {
15738			cjsRequires( window, jq );
15739			module.exports = factory( jq, window, window.document );
15740		}
15741	}
15742	else {
15743		// Browser
15744		factory( jQuery, window, document );
15745	}
15746}(function( $, window, document, undefined ) {
15747'use strict';
15748var DataTable = $.fn.dataTable;
15749
15750
15751
15752
15753
15754return DataTable;
15755}));
15756
15757
15758/*! AutoFill 2.5.3
15759 * ©2008-2023 SpryMedia Ltd - datatables.net/license
15760 */
15761
15762(function( factory ){
15763	if ( typeof define === 'function' && define.amd ) {
15764		// AMD
15765		define( ['jquery', 'datatables.net'], function ( $ ) {
15766			return factory( $, window, document );
15767		} );
15768	}
15769	else if ( typeof exports === 'object' ) {
15770		// CommonJS
15771		var jq = require('jquery');
15772		var cjsRequires = function (root, $) {
15773			if ( ! $.fn.dataTable ) {
15774				require('datatables.net')(root, $);
15775			}
15776		};
15777
15778		if (typeof window !== 'undefined') {
15779			module.exports = function (root, $) {
15780				if ( ! root ) {
15781					// CommonJS environments without a window global must pass a
15782					// root. This will give an error otherwise
15783					root = window;
15784				}
15785
15786				if ( ! $ ) {
15787					$ = jq( root );
15788				}
15789
15790				cjsRequires( root, $ );
15791				return factory( $, root, root.document );
15792			};
15793		}
15794		else {
15795			cjsRequires( window, jq );
15796			module.exports = factory( jq, window, window.document );
15797		}
15798	}
15799	else {
15800		// Browser
15801		factory( jQuery, window, document );
15802	}
15803}(function( $, window, document, undefined ) {
15804'use strict';
15805var DataTable = $.fn.dataTable;
15806
15807
15808
15809/**
15810 * @summary     AutoFill
15811 * @description Add Excel like click and drag auto-fill options to DataTables
15812 * @version     2.5.3
15813 * @author      SpryMedia Ltd (www.sprymedia.co.uk)
15814 * @copyright   SpryMedia Ltd.
15815 *
15816 * This source file is free software, available under the following license:
15817 *   MIT license - http://datatables.net/license/mit
15818 *
15819 * This source file is distributed in the hope that it will be useful, but
15820 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15821 * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
15822 *
15823 * For details please refer to: http://www.datatables.net
15824 */
15825
15826var _instance = 0;
15827
15828/** 
15829 * AutoFill provides Excel like auto-fill features for a DataTable
15830 *
15831 * @class AutoFill
15832 * @constructor
15833 * @param {object} oTD DataTables settings object
15834 * @param {object} oConfig Configuration object for AutoFill
15835 */
15836var AutoFill = function( dt, opts )
15837{
15838	if ( ! DataTable.versionCheck || ! DataTable.versionCheck( '1.10.8' ) ) {
15839		throw( "Warning: AutoFill requires DataTables 1.10.8 or greater");
15840	}
15841
15842	// User and defaults configuration object
15843	this.c = $.extend( true, {},
15844		DataTable.defaults.autoFill,
15845		AutoFill.defaults,
15846		opts
15847	);
15848
15849	/**
15850	 * @namespace Settings object which contains customisable information for AutoFill instance
15851	 */
15852	this.s = {
15853		/** @type {DataTable.Api} DataTables' API instance */
15854		dt: new DataTable.Api( dt ),
15855
15856		/** @type {String} Unique namespace for events attached to the document */
15857		namespace: '.autoFill'+(_instance++),
15858
15859		/** @type {Object} Cached dimension information for use in the mouse move event handler */
15860		scroll: {},
15861
15862		/** @type {integer} Interval object used for smooth scrolling */
15863		scrollInterval: null,
15864
15865		handle: {
15866			height: 0,
15867			width: 0
15868		},
15869
15870		/**
15871		 * Enabled setting
15872		 * @type {Boolean}
15873		 */
15874		enabled: false
15875	};
15876
15877
15878	/**
15879	 * @namespace Common and useful DOM elements for the class instance
15880	 */
15881	this.dom = {
15882		closeButton: $('<div class="dtaf-popover-close">x</div>'),
15883
15884		/** @type {jQuery} AutoFill handle */
15885		handle: $('<div class="dt-autofill-handle"/>'),
15886
15887		/**
15888		 * @type {Object} Selected cells outline - Need to use 4 elements,
15889		 *   otherwise the mouse over if you back into the selected rectangle
15890		 *   will be over that element, rather than the cells!
15891		 */
15892		select: {
15893			top:    $('<div class="dt-autofill-select top"/>'),
15894			right:  $('<div class="dt-autofill-select right"/>'),
15895			bottom: $('<div class="dt-autofill-select bottom"/>'),
15896			left:   $('<div class="dt-autofill-select left"/>')
15897		},
15898
15899		/** @type {jQuery} Fill type chooser background */
15900		background: $('<div class="dt-autofill-background"/>'),
15901
15902		/** @type {jQuery} Fill type chooser */
15903		list: $('<div class="dt-autofill-list">'+this.s.dt.i18n('autoFill.info', '')+'<ul/></div>'),
15904
15905		/** @type {jQuery} DataTables scrolling container */
15906		dtScroll: null,
15907
15908		/** @type {jQuery} Offset parent element */
15909		offsetParent: null
15910	};
15911
15912
15913	/* Constructor logic */
15914	this._constructor();
15915};
15916
15917
15918
15919$.extend( AutoFill.prototype, {
15920	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15921	 * Public methods (exposed via the DataTables API below)
15922	 */
15923	enabled: function ()
15924	{
15925		return this.s.enabled;
15926	},
15927
15928
15929	enable: function ( flag )
15930	{
15931		var that = this;
15932
15933		if ( flag === false ) {
15934			return this.disable();
15935		}
15936
15937		this.s.enabled = true;
15938
15939		this._focusListener();
15940
15941		this.dom.handle.on( 'mousedown touchstart', function (e) {
15942			that._mousedown( e );
15943			return false;
15944		} );
15945
15946		$(window).on('resize', function() {
15947			var handle = $('div.dt-autofill-handle');
15948			if(handle.length > 0 && that.dom.attachedTo !== undefined) {
15949				that._attach(that.dom.attachedTo)
15950			}
15951		})
15952
15953		let orientationReset = function() {
15954			that.s.handle = {
15955				height: false,
15956				width: false
15957			};
15958			$(that.dom.handle).css({
15959				'height': '',
15960				'width': ''
15961			})
15962			if(that.dom.attachedTo !== undefined) {
15963				that._attach(that.dom.attachedTo)
15964			}
15965		}
15966
15967		$(window)
15968			.on('orientationchange', function() {
15969				setTimeout(function() {
15970					orientationReset();
15971					setTimeout(orientationReset, 150);
15972				}, 50);
15973			});
15974
15975		return this;
15976	},
15977
15978	disable: function ()
15979	{
15980		this.s.enabled = false;
15981
15982		this._focusListenerRemove();
15983
15984		return this;
15985	},
15986
15987
15988	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15989	 * Constructor
15990	 */
15991
15992	/**
15993	 * Initialise the RowReorder instance
15994	 *
15995	 * @private
15996	 */
15997	_constructor: function ()
15998	{
15999		var that = this;
16000		var dt = this.s.dt;
16001		var dtScroll = $('div.dataTables_scrollBody', this.s.dt.table().container());
16002
16003		// Make the instance accessible to the API
16004		dt.settings()[0].autoFill = this;
16005
16006		if ( dtScroll.length ) {
16007			this.dom.dtScroll = dtScroll;
16008
16009			// Need to scroll container to be the offset parent
16010			if ( dtScroll.css('position') === 'static' ) {
16011				dtScroll.css( 'position', 'relative' );
16012			}
16013		}
16014
16015		if ( this.c.enable !== false ) {
16016			this.enable();
16017		}
16018
16019		dt.on( 'destroy.autoFill', function () {
16020			that._focusListenerRemove();
16021		} );
16022	},
16023
16024
16025	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16026	 * Private methods
16027	 */
16028
16029	/**
16030	 * Display the AutoFill drag handle by appending it to a table cell. This
16031	 * is the opposite of the _detach method.
16032	 *
16033	 * @param  {node} node TD/TH cell to insert the handle into
16034	 * @private
16035	 */
16036	_attach: function ( node )
16037	{
16038		var dt = this.s.dt;
16039		var idx = dt.cell( node ).index();
16040		var handle = this.dom.handle;
16041		var handleDim = this.s.handle;
16042
16043		if ( ! idx || dt.columns( this.c.columns ).indexes().indexOf( idx.column ) === -1 ) {
16044			this._detach();
16045			return;
16046		}
16047
16048		if ( ! this.dom.offsetParent ) {
16049			// We attach to the table's offset parent
16050			this.dom.offsetParent = $( dt.table().node() ).offsetParent();
16051		}
16052
16053		if ( ! handleDim.height || ! handleDim.width ) {
16054			// Append to document so we can get its size. Not expecting it to
16055			// change during the life time of the page
16056			handle.appendTo( 'body' );
16057			handleDim.height = handle.outerHeight();
16058			handleDim.width = handle.outerWidth();
16059		}
16060
16061		// Might need to go through multiple offset parents
16062		var offset = this._getPosition( node, this.dom.offsetParent );
16063		
16064		this.dom.attachedTo = node;
16065		handle
16066			.css( {
16067				top: offset.top + node.offsetHeight - handleDim.height,
16068				left: offset.left + node.offsetWidth - handleDim.width
16069			} )
16070			.appendTo( this.dom.offsetParent );
16071	},
16072
16073
16074	/**
16075	 * Determine can the fill type should be. This can be automatic, or ask the
16076	 * end user.
16077	 *
16078	 * @param {array} cells Information about the selected cells from the key
16079	 *     up function
16080	 * @private
16081	 */
16082	_actionSelector: function ( cells )
16083	{
16084		var that = this;
16085		var dt = this.s.dt;
16086		var actions = AutoFill.actions;
16087		var available = [];
16088
16089		// "Ask" each plug-in if it wants to handle this data
16090		$.each( actions, function ( key, action ) {
16091			if ( action.available( dt, cells ) ) {
16092				available.push( key );
16093			}
16094		} );
16095
16096		if ( available.length === 1 && this.c.alwaysAsk === false ) {
16097			// Only one action available - enact it immediately
16098			var result = actions[ available[0] ].execute( dt, cells );
16099			this._update( result, cells );
16100		}
16101		else if ( available.length > 1 ) {
16102			// Multiple actions available - ask the end user what they want to do
16103			var list = this.dom.list.children('ul').empty();
16104
16105			// Add a cancel option
16106			available.push( 'cancel' );
16107
16108			$.each( available, function ( i, name ) {
16109				list.append( $('<li/>')
16110					.append(
16111						'<div class="dt-autofill-question">'+
16112							actions[ name ].option( dt, cells )+
16113						'<div>'
16114					)
16115					.append( $('<div class="dt-autofill-button">' ).append( $('<button class="'+AutoFill.classes.btn+'">'+dt.i18n('autoFill.button', '&gt;')+'</button>')))
16116					.on( 'click', function () {
16117						var result = actions[ name ].execute(
16118							dt, cells, $(this).closest('li')
16119						);
16120						that._update( result, cells );
16121
16122						that.dom.background.remove();
16123						that.dom.list.remove();
16124					} )
16125				);
16126			} );
16127
16128			this.dom.background.appendTo( 'body' );
16129			this.dom.background.one('click', function() {
16130				that.dom.background.remove();
16131				that.dom.list.remove();
16132			})
16133			this.dom.list.appendTo( 'body' );
16134
16135			if (this.c.closeButton) {
16136				this.dom.list.prepend(this.dom.closeButton).addClass(AutoFill.classes.closeable)
16137				this.dom.closeButton.on('click', function() {
16138					return that.dom.background.click()
16139				});
16140			}
16141
16142			this.dom.list.css( 'margin-top', this.dom.list.outerHeight()/2 * -1 );
16143		}
16144	},
16145
16146
16147	/**
16148	 * Remove the AutoFill handle from the document
16149	 *
16150	 * @private
16151	 */
16152	_detach: function ()
16153	{
16154		this.dom.attachedTo = null;
16155		this.dom.handle.detach();
16156	},
16157
16158
16159	/**
16160	 * Draw the selection outline by calculating the range between the start
16161	 * and end cells, then placing the highlighting elements to draw a rectangle
16162	 *
16163	 * @param  {node}   target End cell
16164	 * @param  {object} e      Originating event
16165	 * @private
16166	 */
16167	_drawSelection: function ( target, e )
16168	{
16169		// Calculate boundary for start cell to this one
16170		var dt = this.s.dt;
16171		var start = this.s.start;
16172		var startCell = $(this.dom.start);
16173		var end = {
16174			row: this.c.vertical ?
16175				dt.rows( { page: 'current' } ).nodes().indexOf( target.parentNode ) :
16176				start.row,
16177			column: this.c.horizontal ?
16178				$(target).index() :
16179				start.column
16180		};
16181		var colIndx = dt.column.index( 'toData', end.column );
16182		var endRow =  dt.row( ':eq('+end.row+')', { page: 'current' } ); // Workaround for M581
16183		var endCell = $( dt.cell( endRow.index(), colIndx ).node() );
16184
16185		// Be sure that is a DataTables controlled cell
16186		if ( ! dt.cell( endCell ).any() ) {
16187			return;
16188		}
16189
16190		// if target is not in the columns available - do nothing
16191		if ( dt.columns( this.c.columns ).indexes().indexOf( colIndx ) === -1 || end.row 
16191=== -1) {
16192			return;
16193		}
16194
16195		this.s.end = end;
16196
16197		var top, bottom, left, right, height, width;
16198
16199		top    = start.row    < end.row    ? startCell : endCell;
16200		bottom = start.row    < end.row    ? endCell   : startCell;
16201		left   = start.column < end.column ? startCell : endCell;
16202		right  = start.column < end.column ? endCell   : startCell;
16203
16204		top    = this._getPosition( top.get(0) ).top;
16205		left   = this._getPosition( left.get(0) ).left;
16206		height = this._getPosition( bottom.get(0) ).top + bottom.outerHeight() - top;
16207		width  = this._getPosition( right.get(0) ).left + right.outerWidth() - left;
16208
16209		var select = this.dom.select;
16210		select.top.css( {
16211			top: top,
16212			left: left,
16213			width: width
16214		} );
16215
16216		select.left.css( {
16217			top: top,
16218			left: left,
16219			height: height
16220		} );
16221
16222		select.bottom.css( {
16223			top: top + height,
16224			left: left,
16225			width: width
16226		} );
16227
16228		select.right.css( {
16229			top: top,
16230			left: left + width,
16231			height: height
16232		} );
16233	},
16234
16235
16236	/**
16237	 * Use the Editor API to perform an update based on the new data for the
16238	 * cells
16239	 *
16240	 * @param {array} cells Information about the selected cells from the key
16241	 *     up function
16242	 * @private
16243	 */
16244	_editor: function ( cells )
16245	{
16246		var dt = this.s.dt;
16247		var editor = this.c.editor;
16248
16249		if ( ! editor ) {
16250			return;
16251		}
16252
16253		// Build the object structure for Editor's multi-row editing
16254		var idValues = {};
16255		var nodes = [];
16256		var fields = editor.fields();
16257
16258		for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
16259			for ( var j=0, jen=cells[i].length ; j<jen ; j++ ) {
16260				var cell = cells[i][j];
16261
16262				// Determine the field name for the cell being edited
16263				var col = dt.settings()[0].aoColumns[ cell.index.column ];
16264				var fieldName = col.editField;
16265
16266				if ( fieldName === undefined ) {
16267					var dataSrc = col.mData;
16268
16269					// dataSrc is the `field.data` property, but we need to set
16270					// using the field name, so we need to translate from the
16271					// data to the name
16272					for ( var k=0, ken=fields.length ; k<ken ; k++ ) {
16273						var field = editor.field( fields[k] );
16274
16275						if ( field.dataSrc() === dataSrc ) {
16276							fieldName = field.name();
16277							break;
16278						}
16279					}
16280				}
16281
16282				if ( ! fieldName ) {
16283					throw 'Could not automatically determine field data. '+
16284						'Please see https://datatables.net/tn/11';
16285				}
16286
16287				if ( ! idValues[ fieldName ] ) {
16288					idValues[ fieldName ] = {};
16289				}
16290
16291				var id = dt.row( cell.index.row ).id();
16292				idValues[ fieldName ][ id ] = cell.set;
16293
16294				// Keep a list of cells so we can activate the bubble editing
16295				// with them
16296				nodes.push( cell.index );
16297			}
16298		}
16299
16300		// Perform the edit using bubble editing as it allows us to specify
16301		// the cells to be edited, rather than using full rows
16302		editor
16303			.bubble( nodes, false )
16304			.multiSet( idValues )
16305			.submit();
16306	},
16307
16308
16309	/**
16310	 * Emit an event on the DataTable for listeners
16311	 *
16312	 * @param  {string} name Event name
16313	 * @param  {array} args Event arguments
16314	 * @private
16315	 */
16316	_emitEvent: function ( name, args )
16317	{
16318		this.s.dt.iterator( 'table', function ( ctx, i ) {
16319			$(ctx.nTable).triggerHandler( name+'.dt', args );
16320		} );
16321	},
16322
16323
16324	/**
16325	 * Attach suitable listeners (based on the configuration) that will attach
16326	 * and detach the AutoFill handle in the document.
16327	 *
16328	 * @private
16329	 */
16330	_focusListener: function ()
16331	{
16332		var that = this;
16333		var dt = this.s.dt;
16334		var namespace = this.s.namespace;
16335		var focus = this.c.focus !== null ?
16336			this.c.focus :
16337			dt.init().keys || dt.settings()[0].keytable ?
16338				'focus' :
16339				'hover';
16340
16341		// All event listeners attached here are removed in the `destroy`
16342		// callback in the constructor
16343		if ( focus === 'focus' ) {
16344			dt
16345				.on( 'key-focus.autoFill', function ( e, dt, cell ) {
16346					that._attach( cell.node() );
16347				} )
16348				.on( 'key-blur.autoFill', function ( e, dt, cell ) {
16349					that._detach();
16350				} );
16351		}
16352		else if ( focus === 'click' ) {
16353			$(dt.table().body()).on( 'click'+namespace, 'td, th', function (e) {
16354				that._attach( this );
16355			} );
16356
16357			$(document.body).on( 'click'+namespace, function (e) {
16358				if ( ! $(e.target).parents().filter( dt.table().body() ).length ) {
16359					that._detach();
16360				}
16361			} );
16362		}
16363		else {
16364			$(dt.table().body())
16365				.on( 'mouseenter'+namespace+' touchstart'+namespace, 'td, th', function (e) {
16366					that._attach( this );
16367				} )
16368				.on( 'mouseleave'+namespace+'touchend'+namespace, function (e) {
16369					if ( $(e.relatedTarget).hasClass('dt-autofill-handle') ) {
16370						return;
16371					}
16372
16373					that._detach();
16374				} );
16375
16376			
16377		}
16378	},
16379
16380
16381	_focusListenerRemove: function ()
16382	{
16383		var dt = this.s.dt;
16384
16385		dt.off( '.autoFill' );
16386		$(dt.table().body()).off( this.s.namespace );
16387		$(document.body).off( this.s.namespace );
16388	},
16389
16390
16391	/**
16392	 * Get the position of a node, relative to another, including any scrolling
16393	 * offsets.
16394	 * @param  {Node}   node         Node to get the position of
16395	 * @param  {jQuery} targetParent Node to use as the parent
16396	 * @return {object}              Offset calculation
16397	 * @private
16398	 */
16399	_getPosition: function ( node, targetParent )
16400	{
16401		var
16402			currNode = node,
16403			currOffsetParent,
16404			top = 0,
16405			left = 0;
16406
16407		if ( ! targetParent ) {
16408			targetParent = $( $( this.s.dt.table().node() )[0].offsetParent );
16409		}
16410
16411		do {
16412			// Don't use jQuery().position() the behaviour changes between 1.x and 3.x for
16413			// tables
16414			var positionTop = currNode.offsetTop;
16415			var positionLeft = currNode.offsetLeft;
16416
16417			// jQuery doesn't give a `table` as the offset parent oddly, so use DOM directly
16418			currOffsetParent = $( currNode.offsetParent );
16419
16420			top += positionTop + parseInt( currOffsetParent.css('border-top-width') || 0 ) * 1;
16421			left += positionLeft + parseInt( currOffsetParent.css('border-left-width') || 0 ) * 1;
16422
16423			// Emergency fall back. Shouldn't happen, but just in case!
16424			if ( currNode.nodeName.toLowerCase() === 'body' ) {
16425				break;
16426			}
16427
16428			currNode = currOffsetParent.get(0); // for next loop
16429		}
16430		while ( currOffsetParent.get(0) !== targetParent.get(0) )
16431
16432		return {
16433			top: top,
16434			left: left
16435		};
16436	},
16437
16438
16439	/**
16440	 * Start mouse drag - selects the start cell
16441	 *
16442	 * @param  {object} e Mouse down event
16443	 * @private
16444	 */
16445	_mousedown: function ( e )
16446	{
16447		var that = this;
16448		var dt = this.s.dt;
16449
16450		this.dom.start = this.dom.attachedTo;
16451		this.s.start = {
16452			row: dt.rows( { page: 'current' } ).nodes().indexOf( $(this.dom.start).parent()[0] ),
16453			column: $(this.dom.start).index()
16454		};
16455
16456		$(document.body)
16457			.on( 'mousemove.autoFill touchmove.autoFill', function (e) {
16458				that._mousemove( e );
16459				// If it is a touch event then when the touch ends we need to remove the handle
16460				if(e.type === 'touchmove') {
16461					$(document.body).one('touchend.autoFill', function() {
16462						that._detach();
16463					})
16464				}
16465			} )
16466			.on( 'mouseup.autoFill touchend.autoFill', function (e) {
16467				that._mouseup( e );
16468			} );
16469
16470		var select = this.dom.select;
16471		var offsetParent = $( dt.table().node() ).offsetParent();
16472		select.top.appendTo( offsetParent );
16473		select.left.appendTo( offsetParent );
16474		select.right.appendTo( offsetParent );
16475		select.bottom.appendTo( offsetParent );
16476
16477		this._drawSelection( this.dom.start, e );
16478
16479		this.dom.handle.css( 'display', 'none' );
16480
16481		// Cache scrolling information so mouse move doesn't need to read.
16482		// This assumes that the window and DT scroller will not change size
16483		// during an AutoFill drag, which I think is a fair assumption
16484		var scrollWrapper = this.dom.dtScroll;
16485		this.s.scroll = {
16486			windowHeight: $(window).height(),
16487			windowWidth:  $(window).width(),
16488			dtTop:        scrollWrapper ? scrollWrapper.offset().top : null,
16489			dtLeft:       scrollWrapper ? scrollWrapper.offset().left : null,
16490			dtHeight:     scrollWrapper ? scrollWrapper.outerHeight() : null,
16491			dtWidth:      scrollWrapper ? scrollWrapper.outerWidth() : null
16492		};
16493	},
16494
16495
16496	/**
16497	 * Mouse drag - selects the end cell and update the selection display for
16498	 * the end user
16499	 *
16500	 * @param  {object} e Mouse move event
16501	 * @private
16502	 */
16503	_mousemove: function ( e )
16504	{
16505		var target = e.touches && e.touches.length
16506			? document.elementFromPoint(e.touches[0].clientX, e.touches[0].clientY)
16507			: e.target;
16508		var name = target.nodeName.toLowerCase();
16509
16510		if ( name !== 'td' && name !== 'th' ) {
16511			return;
16512		}
16513
16514		this._drawSelection( target, e );
16515		this._shiftScroll( e );
16516	},
16517
16518
16519	/**
16520	 * End mouse drag - perform the update actions
16521	 *
16522	 * @param  {object} e Mouse up event
16523	 * @private
16524	 */
16525	_mouseup: function ( e )
16526	{
16527		$(document.body).off( '.autoFill' );
16528
16529		var that = this;
16530		var dt = this.s.dt;
16531		var select = this.dom.select;
16532		select.top.remove();
16533		select.left.remove();
16534		select.right.remove();
16535		select.bottom.remove();
16536
16537		this.dom.handle.css( 'display', 'block' );
16538
16539		// Display complete - now do something useful with the selection!
16540		var start = this.s.start;
16541		var end = this.s.end;
16542
16543		// Haven't selected multiple cells, so nothing to do
16544		if ( start.row === end.row && start.column === end.column ) {
16545			return;
16546		}
16547
16548		var startDt = dt.cell( ':eq('+start.row+')', start.column+':visible', {page:'current'} );
16549
16550		// If Editor is active inside this cell (inline editing) we need to wait for Editor to
16551		// submit and then we can loop back and trigger the fill.
16552		if ( $('div.DTE', startDt.node()).length ) {
16553			var editor = dt.editor();
16554
16555			editor
16556				.on( 'submitSuccess.dtaf close.dtaf', function () {
16557					editor.off( '.dtaf');
16558
16559					setTimeout( function () {
16560						that._mouseup( e );
16561					}, 100 );
16562				} )
16563				.on( 'submitComplete.dtaf preSubmitCancelled.dtaf close.dtaf', function () {
16564					editor.off( '.dtaf');
16565				} );
16566
16567			// Make the current input submit
16568			editor.submit();
16569
16570			return;
16571		}
16572
16573		// Build a matrix representation of the selected rows
16574		var rows       = this._range( start.row, end.row );
16575		var columns    = this._range( start.column, end.column );
16576		var selected   = [];
16577		var dtSettings = dt.settings()[0];
16578		var dtColumns  = dtSettings.aoColumns;
16579		var enabledColumns = dt.columns( this.c.columns ).indexes();
16580
16581		// Can't use Array.prototype.map as IE8 doesn't support it
16582		// Can't use $.map as jQuery flattens 2D arrays
16583		// Need to use a good old fashioned for loop
16584		for ( var rowIdx=0 ; rowIdx<rows.length ; rowIdx++ ) {
16585			selected.push(
16586				$.map( columns, function (column) {
16587					var row = dt.row( ':eq('+rows[rowIdx]+')', {page:'current'} ); // Workaround for M581
16588					var cell = dt.cell( row.index(), column+':visible' );
16589					var data = cell.data();
16590					var cellIndex = cell.index();
16591					var editField = dtColumns[ cellIndex.column ].editField;
16592
16593					if ( editField !== undefined ) {
16594						data = dtSettings.oApi._fnGetObjectDataFn( editField )( dt.row( cellIndex.row ).data() );
16595					}
16596
16597					if ( enabledColumns.indexOf(cellIndex.column) === -1 ) {
16598						return;
16599					}
16600
16601					return {
16602						cell:  cell,
16603						data:  data,
16604						label: cell.data(),
16605						index: cellIndex
16606					};
16607				} )
16608			);
16609		}
16610
16611		this._actionSelector( selected );
16612		
16613		// Stop shiftScroll
16614		clearInterval( this.s.scrollInterval );
16615		this.s.scrollInterval = null;
16616	},
16617
16618
16619	/**
16620	 * Create an array with a range of numbers defined by the start and end
16621	 * parameters passed in (inclusive!).
16622	 * 
16623	 * @param  {integer} start Start
16624	 * @param  {integer} end   End
16625	 * @private
16626	 */
16627	_range: function ( start, end )
16628	{
16629		var out = [];
16630		var i;
16631
16632		if ( start <= end ) {
16633			for ( i=start ; i<=end ; i++ ) {
16634				out.push( i );
16635			}
16636		}
16637		else {
16638			for ( i=start ; i>=end ; i-- ) {
16639				out.push( i );
16640			}
16641		}
16642
16643		return out;
16644	},
16645
16646
16647	/**
16648	 * Move the window and DataTables scrolling during a drag to scroll new
16649	 * content into view. This is done by proximity to the edge of the scrolling
16650	 * container of the mouse - for example near the top edge of the window
16651	 * should scroll up. This is a little complicated as there are two elements
16652	 * that can be scrolled - the window and the DataTables scrolling view port
16653	 * (if scrollX and / or scrollY is enabled).
16654	 *
16655	 * @param  {object} e Mouse move event object
16656	 * @private
16657	 */
16658	_shiftScroll: function ( e )
16659	{
16660		var that = this;
16661		var dt = this.s.dt;
16662		var scroll = this.s.scroll;
16663		var runInterval = false;
16664		var scrollSpeed = 5;
16665		var buffer = 65;
16666
16667		// Different values if using a touchscreen
16668		var pageX = !e.type.includes('touch') ? e.pageX - window.scrollX :e.touches[0].clientX;
16669		var pageY = !e.type.includes('touch') ? e.pageY - window.scrollY :e.touches[0].clientY;
16670		var
16671			windowY = pageY,
16672			windowX = pageX,
16673			windowVert, windowHoriz,
16674			dtVert, dtHoriz;
16675
16676		// Window calculations - based on the mouse position in the window,
16677		// regardless of scrolling
16678		if ( windowY < buffer ) {
16679			windowVert = scrollSpeed * -1;
16680		}
16681		else if ( windowY > scroll.windowHeight - buffer ) {
16682			windowVert = scrollSpeed;
16683		}
16684
16685		if ( windowX < buffer ) {
16686			windowHoriz = scrollSpeed * -1;
16687		}
16688		else if ( windowX > scroll.windowWidth - buffer ) {
16689			windowHoriz = scrollSpeed;
16690		}
16691
16692		// DataTables scrolling calculations - based on the table's position in
16693		// the document and the mouse position on the page
16694		if ( scroll.dtTop !== null && pageY < scroll.dtTop + buffer ) {
16695			dtVert = scrollSpeed * -1;
16696		}
16697		else if ( scroll.dtTop !== null && pageY > scroll.dtTop + scroll.dtHeight - buffer ) {
16698			dtVert = scrollSpeed;
16699		}
16700
16701		if ( scroll.dtLeft !== null && pageX < scroll.dtLeft + buffer ) {
16702			dtHoriz = scrollSpeed * -1;
16703		}
16704		else if ( scroll.dtLeft !== null && pageX > scroll.dtLeft + scroll.dtWidth - buffer ) {
16705			dtHoriz = scrollSpeed;
16706		}
16707
16708		// This is where it gets interesting. We want to continue scrolling
16709		// without requiring a mouse move, so we need an interval to be
16710		// triggered. The interval should continue until it is no longer needed,
16711		// but it must also use the latest scroll commands (for example consider
16712		// that the mouse might move from scrolling up to scrolling left, all
16713		// with the same interval running. We use the `scroll` object to "pass"
16714		// this information to the interval. Can't use local variables as they
16715		// wouldn't be the ones that are used by an already existing interval!
16716		if ( windowVert || windowHoriz || dtVert || dtHoriz ) {
16717			scroll.windowVert = windowVert;
16718			scroll.windowHoriz = windowHoriz;
16719			scroll.dtVert = dtVert;
16720			scroll.dtHoriz = dtHoriz;
16721			runInterval = true;
16722		}
16723		else if ( this.s.scrollInterval ) {
16724			// Don't need to scroll - remove any existing timer
16725			clearInterval( this.s.scrollInterval );
16726			this.s.scrollInterval = null;
16727		}
16728
16729		// If we need to run the interval to scroll and there is no existing
16730		// interval (if there is an existing one, it will continue to run)
16731		if ( ! this.s.scrollInterval && runInterval ) {
16732			this.s.scrollInterval = setInterval( function () {
16733				// Don't need to worry about setting scroll <0 or beyond the
16734				// scroll bound as the browser will just reject that.
16735				window.scrollTo(window.scrollX + (scroll.windowHoriz ? scroll.windowHoriz : 0), window.scrollY + (scroll.windowVert ? scroll.windowVert : 0))
16736
16737				// DataTables scrolling
16738				if ( scroll.dtVert || scroll.dtHoriz ) {
16739					var scroller = that.dom.dtScroll[0];
16740
16741					if ( scroll.dtVert ) {
16742						scroller.scrollTop += scroll.dtVert;
16743					}
16744					if ( scroll.dtHoriz ) {
16745						scroller.scrollLeft += scroll.dtHoriz;
16746					}
16747				}
16748			}, 20 );
16749		}
16750	},
16751
16752
16753	/**
16754	 * Update the DataTable after the user has selected what they want to do
16755	 *
16756	 * @param  {false|undefined} result Return from the `execute` method - can
16757	 *   be false internally to do nothing. This is not documented for plug
16757-ins
16758	 *   and is used only by the cancel option.
16759	 * @param {array} cells Information about the selected cells from the key
16760	 *     up function, argumented with the set values
16761	 * @private
16762	 */
16763	_update: function ( result, cells )
16764	{
16765		// Do nothing on `false` return from an execute function
16766		if ( result === false ) {
16767			return;
16768		}
16769
16770		var dt = this.s.dt;
16771		var cell;
16772		var columns = dt.columns( this.c.columns ).indexes();
16773
16774		// Potentially allow modifications to the cells matrix
16775		this._emitEvent( 'preAutoFill', [ dt, cells ] );
16776
16777		this._editor( cells );
16778
16779		// Automatic updates are not performed if `update` is null and the
16780		// `editor` parameter is passed in - the reason being that Editor will
16781		// update the data once submitted
16782		var update = this.c.update !== null ?
16783			this.c.update :
16784			this.c.editor ?
16785				false :
16786				true;
16787
16788		if ( update ) {
16789			for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
16790				for ( var j=0, jen=cells[i].length ; j<jen ; j++ ) {
16791					cell = cells[i][j];
16792
16793					if ( columns.indexOf(cell.index.column) !== -1 ) {
16794						cell.cell.data( cell.set );
16795					}
16796				}
16797			}
16798
16799			dt.draw(false);
16800		}
16801
16802		this._emitEvent( 'autoFill', [ dt, cells ] );
16803	}
16804} );
16805
16806
16807/**
16808 * AutoFill actions. The options here determine how AutoFill will fill the data
16809 * in the table when the user has selected a range of cells. Please see the
16810 * documentation on the DataTables site for full details on how to create plug-
16811 * ins.
16812 *
16813 * @type {Object}
16814 */
16815AutoFill.actions = {
16816	increment: {
16817		available: function ( dt, cells ) {
16818			var d = cells[0][0].label;
16819
16820			// is numeric test based on jQuery's old `isNumeric` function
16821			return !isNaN( d - parseFloat( d ) );
16822		},
16823
16824		option: function ( dt, cells ) {
16825			return dt.i18n(
16826				'autoFill.increment',
16827				'Increment / decrement each cell by: <input type="number" value="1">'
16828			);
16829		},
16830
16831		execute: function ( dt, cells, node ) {
16832			var value = cells[0][0].data * 1;
16833			var increment = $('input', node).val() * 1;
16834
16835			for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
16836				for ( var j=0, jen=cells[i].length ; j<jen ; j++ ) {
16837					cells[i][j].set = value;
16838
16839					value += increment;
16840				}
16841			}
16842		}
16843	},
16844
16845	fill: {
16846		available: function ( dt, cells ) {
16847			return true;
16848		},
16849
16850		option: function ( dt, cells ) {
16851			return dt.i18n('autoFill.fill', 'Fill all cells with <i>%d</i>', cells[0][0].label );
16852		},
16853
16854		execute: function ( dt, cells, node ) {
16855			var value = cells[0][0].data;
16856
16857			for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
16858				for ( var j=0, jen=cells[i].length ; j<jen ; j++ ) {
16859					cells[i][j].set = value;
16860				}
16861			}
16862		}
16863	},
16864
16865	fillHorizontal: {
16866		available: function ( dt, cells ) {
16867			return cells.length > 1 && cells[0].length > 1;
16868		},
16869
16870		option: function ( dt, cells ) {
16871			return dt.i18n('autoFill.fillHorizontal', 'Fill cells horizontally' );
16872		},
16873
16874		execute: function ( dt, cells, node ) {
16875			for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
16876				for ( var j=0, jen=cells[i].length ; j<jen ; j++ ) {
16877					cells[i][j].set = cells[i][0].data;
16878				}
16879			}
16880		}
16881	},
16882
16883	fillVertical: {
16884		available: function ( dt, cells ) {
16885			return cells.length > 1 && cells[0].length > 1;
16886		},
16887
16888		option: function ( dt, cells ) {
16889			return dt.i18n('autoFill.fillVertical', 'Fill cells vertically' );
16890		},
16891
16892		execute: function ( dt, cells, node ) {
16893			for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
16894				for ( var j=0, jen=cells[i].length ; j<jen ; j++ ) {
16895					cells[i][j].set = cells[0][j].data;
16896				}
16897			}
16898		}
16899	},
16900
16901	// Special type that does not make itself available, but is added
16902	// automatically by AutoFill if a multi-choice list is shown. This allows
16903	// sensible code reuse
16904	cancel: {
16905		available: function () {
16906			return false;
16907		},
16908
16909		option: function ( dt ) {
16910			return dt.i18n('autoFill.cancel', 'Cancel' );
16911		},
16912
16913		execute: function () {
16914			return false;
16915		}
16916	}
16917};
16918
16919
16920/**
16921 * AutoFill version
16922 * 
16923 * @static
16924 * @type      String
16925 */
16926AutoFill.version = '2.5.3';
16927
16928
16929/**
16930 * AutoFill defaults
16931 * 
16932 * @namespace
16933 */
16934AutoFill.defaults = {
16935	/** @type {Boolean} Ask user what they want to do, even for a single option */
16936	alwaysAsk: false,
16937
16938	closeButton: true,
16939
16940	/** @type {string|null} What will trigger a focus */
16941	focus: null, // focus, click, hover
16942
16943	/** @type {column-selector} Columns to provide auto fill for */
16944	columns: '', // all
16945
16946	/** @type {Boolean} Enable AutoFill on load */
16947	enable: true,
16948
16949	/** @type {boolean|null} Update the cells after a drag */
16950	update: null, // false is editor given, true otherwise
16951
16952	/** @type {DataTable.Editor} Editor instance for automatic submission */
16953	editor: null,
16954
16955	/** @type {boolean} Enable vertical fill */
16956	vertical: true,
16957
16958	/** @type {boolean} Enable horizontal fill */
16959	horizontal: true
16960};
16961
16962
16963/**
16964 * Classes used by AutoFill that are configurable
16965 * 
16966 * @namespace
16967 */
16968AutoFill.classes = {
16969	/** @type {String} Class used by the selection button */
16970	btn: 'btn',
16971
16972	closeable: 'dtaf-popover-closeable'
16973};
16974
16975
16976/*
16977 * API
16978 */
16979var Api = $.fn.dataTable.Api;
16980
16981// Doesn't do anything - Not documented
16982Api.register( 'autoFill()', function () {
16983	return this;
16984} );
16985
16986Api.register( 'autoFill().enabled()', function () {
16987	var ctx = this.context[0];
16988
16989	return ctx.autoFill ?
16990		ctx.autoFill.enabled() :
16991		false;
16992} );
16993
16994Api.register( 'autoFill().enable()', function ( flag ) {
16995	return this.iterator( 'table', function ( ctx ) {
16996		if ( ctx.autoFill ) {
16997			ctx.autoFill.enable( flag );
16998		}
16999	} );
17000} );
17001
17002Api.register( 'autoFill().disable()', function () {
17003	return this.iterator( 'table', function ( ctx ) {
17004		if ( ctx.autoFill ) {
17005			ctx.autoFill.disable();
17006		}
17007	} );
17008} );
17009
17010
17011// Attach a listener to the document which listens for DataTables initialisation
17012// events so we can automatically initialise
17013$(document).on( 'preInit.dt.autofill', function (e, settings, json) {
17014	if ( e.namespace !== 'dt' ) {
17015		return;
17016	}
17017
17018	var init = settings.oInit.autoFill;
17019	var defaults = DataTable.defaults.autoFill;
17020
17021	if ( init || defaults ) {
17022		var opts = $.extend( {}, init, defaults );
17023
17024		if ( init !== false ) {
17025			new AutoFill( settings, opts  );
17026		}
17027	}
17028} );
17029
17030
17031// Alias for access
17032DataTable.AutoFill = AutoFill;
17033$.fn.DataTable.AutoFill = AutoFill;
17034
17035
17036return DataTable;
17037}));
17038
17039
17040/*! DataTables styling wrapper for AutoFill
17041 * ©2018 SpryMedia Ltd - datatables.net/license
17042 */
17043
17044(function( factory ){
17045	if ( typeof define === 'function' && define.amd ) {
17046		// AMD
17047		define( ['jquery', 'datatables.net-dt', 'datatables.net-autofill'], function ( $ ) {
17048			return factory( $, window, document );
17049		} );
17050	}
17051	else if ( typeof exports === 'object' ) {
17052		// CommonJS
17053		var jq = require('jquery');
17054		var cjsRequires = function (root, $) {
17055			if ( ! $.fn.dataTable ) {
17056				require('datatables.net-dt')(root, $);
17057			}
17058
17059			if ( ! $.fn.dataTable.AutoFill ) {
17060				require('datatables.net-autofill')(root, $);
17061			}
17062		};
17063
17064		if (typeof window !== 'undefined') {
17065			module.exports = function (root, $) {
17066				if ( ! root ) {
17067					// CommonJS environments without a window global must pass a
17068					// root. This will give an error otherwise
17069					root = window;
17070				}
17071
17072				if ( ! $ ) {
17073					$ = jq( root );
17074				}
17075
17076				cjsRequires( root, $ );
17077				return factory( $, root, root.document );
17078			};
17079		}
17080		else {
17081			cjsRequires( window, jq );
17082			module.exports = factory( jq, window, window.document );
17083		}
17084	}
17085	else {
17086		// Browser
17087		factory( jQuery, window, document );
17088	}
17089}(function( $, window, document, undefined ) {
17090'use strict';
17091var DataTable = $.fn.dataTable;
17092
17093
17094
17095
17096return DataTable;
17097}));
17098
17099
17100/*! Buttons for DataTables 2.3.6
17101 * ©2016-2023 SpryMedia Ltd - datatables.net/license
17102 */
17103
17104(function( factory ){
17105	if ( typeof define === 'function' && define.amd ) {
17106		// AMD
17107		define( ['jquery', 'datatables.net'], function ( $ ) {
17108			return factory( $, window, document );
17109		} );
17110	}
17111	else if ( typeof exports === 'object' ) {
17112		// CommonJS
17113		var jq = require('jquery');
17114		var cjsRequires = function (root, $) {
17115			if ( ! $.fn.dataTable ) {
17116				require('datatables.net')(root, $);
17117			}
17118		};
17119
17120		if (typeof window !== 'undefined') {
17121			module.exports = function (root, $) {
17122				if ( ! root ) {
17123					// CommonJS environments without a window global must pass a
17124					// root. This will give an error otherwise
17125					root = window;
17126				}
17127
17128				if ( ! $ ) {
17129					$ = jq( root );
17130				}
17131
17132				cjsRequires( root, $ );
17133				return factory( $, root, root.document );
17134			};
17135		}
17136		else {
17137			cjsRequires( window, jq );
17138			module.exports = factory( jq, window, window.document );
17139		}
17140	}
17141	else {
17142		// Browser
17143		factory( jQuery, window, document );
17144	}
17145}(function( $, window, document, undefined ) {
17146'use strict';
17147var DataTable = $.fn.dataTable;
17148
17149
17150
17151// Used for namespacing events added to the document by each instance, so they
17152// can be removed on destroy
17153var _instCounter = 0;
17154
17155// Button namespacing counter for namespacing events on individual buttons
17156var _buttonCounter = 0;
17157
17158var _dtButtons = DataTable.ext.buttons;
17159
17160// Allow for jQuery slim
17161function _fadeIn(el, duration, fn) {
17162	if ($.fn.animate) {
17163		el
17164			.stop()
17165			.fadeIn( duration, fn );
17166
17167	}
17168	else {
17169		el.css('display', 'block');
17170
17171		if (fn) {
17172			fn.call(el);
17173		}
17174	}
17175}
17176
17177function _fadeOut(el, duration, fn) {
17178	if ($.fn.animate) {
17179		el
17180			.stop()
17181			.fadeOut( duration, fn );
17182	}
17183	else {
17184		el.css('display', 'none');
17185		
17186		if (fn) {
17187			fn.call(el);
17188		}
17189	}
17190}
17191
17192/**
17193 * [Buttons description]
17194 * @param {[type]}
17195 * @param {[type]}
17196 */
17197var Buttons = function( dt, config )
17198{
17199	// If not created with a `new` keyword then we return a wrapper function that
17200	// will take the settings object for a DT. This allows easy use of new instances
17201	// with the `layout` option - e.g. `topLeft: $.fn.dataTable.Buttons( ... )`.
17202	if ( !(this instanceof Buttons) ) {
17203		return function (settings) {
17204			return new Buttons( settings, dt ).container();
17205		};
17206	}
17207
17208	// If there is no config set it to an empty object
17209	if ( typeof( config ) === 'undefined' ) {
17210		config = {};	
17211	}
17212	
17213	// Allow a boolean true for defaults
17214	if ( config === true ) {
17215		config = {};
17216	}
17217
17218	// For easy configuration of buttons an array can be given
17219	if ( Array.isArray( config ) ) {
17220		config = { buttons: config };
17221	}
17222
17223	this.c = $.extend( true, {}, Buttons.defaults, config );
17224
17225	// Don't want a deep copy for the buttons
17226	if ( config.buttons ) {
17227		this.c.buttons = config.buttons;
17228	}
17229
17230	this.s = {
17231		dt: new DataTable.Api( dt ),
17232		buttons: [],
17233		listenKeys: '',
17234		namespace: 'dtb'+(_instCounter++)
17235	};
17236
17237	this.dom = {
17238		container: $('<'+this.c.dom.container.tag+'/>')
17239			.addClass( this.c.dom.container.className )
17240	};
17241
17242	this._constructor();
17243};
17244
17245
17246$.extend( Buttons.prototype, {
17247	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
17248	 * Public methods
17249	 */
17250
17251	/**
17252	 * Get the action of a button
17253	 * @param  {int|string} Button index
17254	 * @return {function}
17255	 *//**
17256	 * Set the action of a button
17257	 * @param  {node} node Button element
17258	 * @param  {function} action Function to set
17259	 * @return {Buttons} Self for chaining
17260	 */
17261	action: function ( node, action )
17262	{
17263		var button = this._nodeToButton( node );
17264
17265		if ( action === undefined ) {
17266			return button.conf.action;
17267		}
17268
17269		button.conf.action = action;
17270
17271		return this;
17272	},
17273
17274	/**
17275	 * Add an active class to the button to make to look active or get current
17276	 * active state.
17277	 * @param  {node} node Button element
17278	 * @param  {boolean} [flag] Enable / disable flag
17279	 * @return {Buttons} Self for chaining or boolean for getter
17280	 */
17281	active: function ( node, flag ) {
17282		var button = this._nodeToButton( node );
17283		var klass = this.c.dom.button.active;
17284		var jqNode = $(button.node);
17285
17286		if ( flag === undefined ) {
17287			return jqNode.hasClass( klass );
17288		}
17289
17290		jqNode.toggleClass( klass, flag === undefined ? true : flag );
17291
17292		return this;
17293	},
17294
17295	/**
17296	 * Add a new button
17297	 * @param {object} config Button configuration object, base string name or function
17298	 * @param {int|string} [idx] Button index for where to insert the button
17299	 * @param {boolean} [draw=true] Trigger a draw. Set a false when adding
17300	 *   lots of buttons, until the last button.
17301	 * @return {Buttons} Self for chaining
17302	 */
17303	add: function ( config, idx, draw )
17304	{
17305		var buttons = this.s.buttons;
17306
17307		if ( typeof idx === 'string' ) {
17308			var split = idx.split('-');
17309			var base = this.s;
17310
17311			for ( var i=0, ien=split.length-1 ; i<ien ; i++ ) {
17312				base = base.buttons[ split[i]*1 ];
17313			}
17314
17315			buttons = base.buttons;
17316			idx = split[ split.length-1 ]*1;
17317		}
17318
17319		this._expandButton(
17320			buttons,
17321			config,
17322			config !== undefined ? config.split : undefined,
17323			(config === undefined || config.split === undefined || config.split.length === 0) && base !== undefined,
17324			false,
17325			idx
17326		);
17327
17328		if (draw === undefined || draw === true) {
17329			this._draw();
17330		}
17331	
17332		return this;
17333	},
17334
17335	/**
17336	 * Clear buttons from a collection and then insert new buttons
17337	 */
17338	collectionRebuild: function ( node, newButtons )
17339	{
17340		var button = this._nodeToButton( node );
17341		
17342		if(newButtons !== undefined) {
17343			var i;
17344			// Need to reverse the array
17345			for (i=button.buttons.length-1; i>=0; i--) {
17346				this.remove(button.buttons[i].node);
17347			}
17348
17349			// If the collection has prefix and / or postfix buttons we need to add them in
17350			if (button.conf.prefixButtons) {
17351				newButtons.unshift.apply(newButtons, button.conf.prefixButtons);
17352			}
17353
17354			if (button.conf.postfixButtons) {
17355				newButtons.push.apply(newButtons, button.conf.postfixButtons);
17356			}
17357
17358			for (i=0; i<newButtons.length; i++) {
17359				var newBtn = newButtons[i];
17360
17361				this._expandButton(
17362					button.buttons,
17363					newBtn,
17364					newBtn !== undefined && newBtn.config !== undefined && newBtn.config.split !== undefined,
17365					true,
17366					newBtn.parentConf !== undefined && newBtn.parentConf.split !== undefined,
17367					null,
17368					newBtn.parentConf
17369				);
17370			}
17371		}
17372
17373		this._draw(button.collection, button.buttons);
17374	},
17375
17376	/**
17377	 * Get the container node for the buttons
17378	 * @return {jQuery} Buttons node
17379	 */
17380	container: function ()
17381	{
17382		return this.dom.container;
17383	},
17384
17385	/**
17386	 * Disable a button
17387	 * @param  {node} node Button node
17388	 * @return {Buttons} Self for chaining
17389	 */
17390	disable: function ( node ) {
17391		var button = this._nodeToButton( node );
17392
17393		$(button.node)
17394			.addClass( this.c.dom.button.disabled )
17395			.prop('disabled', true);
17396
17397		return this;
17398	},
17399
17400	/**
17401	 * Destroy the instance, cleaning up event handlers and removing DOM
17402	 * elements
17403	 * @return {Buttons} Self for chaining
17404	 */
17405	destroy: function ()
17406	{
17407		// Key event listener
17408		$('body').off( 'keyup.'+this.s.namespace );
17409
17410		// Individual button destroy (so they can remove their own events if
17411		// needed). Take a copy as the array is modified by `remove`
17412		var buttons = this.s.buttons.slice();
17413		var i, ien;
17414		
17415		for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
17416			this.remove( buttons[i].node );
17417		}
17418
17419		// Container
17420		this.dom.container.remove();
17421
17422		// Remove from the settings object collection
17423		var buttonInsts = this.s.dt.settings()[0];
17424
17425		for ( i=0, ien=buttonInsts.length ; i<ien ; i++ ) {
17426			if ( buttonInsts.inst === this ) {
17427				buttonInsts.splice( i, 1 );
17428				break;
17429			}
17430		}
17431
17432		return this;
17433	},
17434
17435	/**
17436	 * Enable / disable a button
17437	 * @param  {node} node Button node
17438	 * @param  {boolean} [flag=true] Enable / disable flag
17439	 * @return {Buttons} Self for chaining
17440	 */
17441	enable: function ( node, flag )
17442	{
17443		if ( flag === false ) {
17444			return this.disable( node );
17445		}
17446
17447		var button = this._nodeToButton( node );
17448		$(button.node)
17449			.removeClass( this.c.dom.button.disabled )
17450			.prop('disabled', false);
17451
17452		return this;
17453	},
17454
17455	/**
17456	 * Get a button's index
17457	 * 
17458	 * This is internally recursive
17459	 * @param {element} node Button to get the index of
17460	 * @return {string} Button index
17461	 */
17462	index: function ( node, nested, buttons )
17463	{
17464		if ( ! nested ) {
17465			nested = '';
17466			buttons = this.s.buttons;
17467		}
17468
17469		for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
17470			var inner = buttons[i].buttons;
17471
17472			if (buttons[i].node === node) {
17473				return nested + i;
17474			}
17475
17476			if ( inner && inner.length ) {
17477				var match = this.index(node, i + '-', inner);
17478
17479				if (match !== null) {
17480					return match;
17481				}
17482			}
17483		}
17484
17485		return null;
17486	},
17487
17488
17489	/**
17490	 * Get the instance name for the button set selector
17491	 * @return {string} Instance name
17492	 */
17493	name: function ()
17494	{
17495		return this.c.name;
17496	},
17497
17498	/**
17499	 * Get a button's node of the buttons container if no button is given
17500	 * @param  {node} [node] Button node
17501	 * @return {jQuery} Button element, or container
17502	 */
17503	node: function ( node )
17504	{
17505		if ( ! node ) {
17506			return this.dom.container;
17507		}
17508
17509		var button = this._nodeToButton( node );
17510		return $(button.node);
17511	},
17512
17513	/**
17514	 * Set / get a processing class on the selected button
17515	 * @param {element} node Triggering button node
17516	 * @param  {boolean} flag true to add, false to remove, undefined to get
17517	 * @return {boolean|Buttons} Getter value or this if a setter.
17518	 */
17519	processing: function ( node, flag )
17520	{
17521		var dt = this.s.dt;
17522		var button = this._nodeToButton( node );
17523
17524		if ( flag === undefined ) {
17525			return $(button.node).hasClass( 'processing' );
17526		}
17527
17528		$(button.node).toggleClass( 'processing', flag );
17529
17530		$(dt.table().node()).triggerHandler( 'buttons-processing.dt', [
17531			flag, dt.button( node ), dt, $(node), button.conf
17532		] );
17533
17534		return this;
17535	},
17536
17537	/**
17538	 * Remove a button.
17539	 * @param  {node} node Button node
17540	 * @return {Buttons} Self for chaining
17541	 */
17542	remove: function ( node )
17543	{
17544		var button = this._nodeToButton( node );
17545		var host = this._nodeToHost( node );
17546		var dt = this.s.dt;
17547
17548		// Remove any child buttons first
17549		if ( button.buttons.length ) {
17550			for ( var i=button.buttons.length-1 ; i>=0 ; i-- ) {
17551				this.remove( button.buttons[i].node );
17552			}
17553		}
17554
17555		button.conf.destroying = true;
17556
17557		// Allow the button to remove event handlers, etc
17558		if ( button.conf.destroy ) {
17559			button.conf.destroy.call( dt.button(node), dt, $(node), button.conf );
17560		}
17561
17562		this._removeKey( button.conf );
17563
17564		$(button.node).remove();
17565
17566		var idx = $.inArray( button, host );
17567		host.splice( idx, 1 );
17568
17569		return this;
17570	},
17571
17572	/**
17573	 * Get the text for a button
17574	 * @param  {int|string}
17574 node Button index
17575	 * @return {string} Button text
17576	 *//**
17577	 * Set the text for a button
17578	 * @param  {int|string|function} node Button index
17579	 * @param  {string} label Text
17580	 * @return {Buttons} Self for chaining
17581	 */
17582	text: function ( node, label )
17583	{
17584		var button = this._nodeToButton( node );
17585		var buttonLiner = this.c.dom.collection.buttonLiner;
17586		var linerTag = button.inCollection && buttonLiner && buttonLiner.tag ?
17587			buttonLiner.tag :
17588			this.c.dom.buttonLiner.tag;
17589		var dt = this.s.dt;
17590		var jqNode = $(button.node);
17591		var text = function ( opt ) {
17592			return typeof opt === 'function' ?
17593				opt( dt, jqNode, button.conf ) :
17594				opt;
17595		};
17596
17597		if ( label === undefined ) {
17598			return text( button.conf.text );
17599		}
17600
17601		button.conf.text = label;
17602
17603		if ( linerTag ) {
17604			jqNode
17605				.children( linerTag )
17606				.eq(0)
17607				.filter(':not(.dt-down-arrow)')
17608				.html( text(label) );
17609		}
17610		else {
17611			jqNode.html( text(label) );
17612		}
17613
17614		return this;
17615	},
17616
17617
17618	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
17619	 * Constructor
17620	 */
17621
17622	/**
17623	 * Buttons constructor
17624	 * @private
17625	 */
17626	_constructor: function ()
17627	{
17628		var that = this;
17629		var dt = this.s.dt;
17630		var dtSettings = dt.settings()[0];
17631		var buttons =  this.c.buttons;
17632
17633		if ( ! dtSettings._buttons ) {
17634			dtSettings._buttons = [];
17635		}
17636
17637		dtSettings._buttons.push( {
17638			inst: this,
17639			name: this.c.name
17640		} );
17641
17642		for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
17643			this.add( buttons[i] );
17644		}
17645
17646		dt.on( 'destroy', function ( e, settings ) {
17647			if ( settings === dtSettings ) {
17648				that.destroy();
17649			}
17650		} );
17651
17652		// Global key event binding to listen for button keys
17653		$('body').on( 'keyup.'+this.s.namespace, function ( e ) {
17654			if ( ! document.activeElement || document.activeElement === document.body ) {
17655				// SUse a string of characters for fast lookup of if we need to
17656				// handle this
17657				var character = String.fromCharCode(e.keyCode).toLowerCase();
17658
17659				if ( that.s.listenKeys.toLowerCase().indexOf( character ) !== -1 ) {
17660					that._keypress( character, e );
17661				}
17662			}
17663		} );
17664	},
17665
17666
17667	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
17668	 * Private methods
17669	 */
17670
17671	/**
17672	 * Add a new button to the key press listener
17673	 * @param {object} conf Resolved button configuration object
17674	 * @private
17675	 */
17676	_addKey: function ( conf )
17677	{
17678		if ( conf.key ) {
17679			this.s.listenKeys += $.isPlainObject( conf.key ) ?
17680				conf.key.key :
17681				conf.key;
17682		}
17683	},
17684
17685	/**
17686	 * Insert the buttons into the container. Call without parameters!
17687	 * @param  {node} [container] Recursive only - Insert point
17688	 * @param  {array} [buttons] Recursive only - Buttons array
17689	 * @private
17690	 */
17691	_draw: function ( container, buttons )
17692	{
17693		if ( ! container ) {
17694			container = this.dom.container;
17695			buttons = this.s.buttons;
17696		}
17697
17698		container.children().detach();
17699
17700		for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
17701			container.append( buttons[i].inserter );
17702			container.append( ' ' );
17703
17704			if ( buttons[i].buttons && buttons[i].buttons.length ) {
17705				this._draw( buttons[i].collection, buttons[i].buttons );
17706			}
17707		}
17708	},
17709
17710	/**
17711	 * Create buttons from an array of buttons
17712	 * @param  {array} attachTo Buttons array to attach to
17713	 * @param  {object} button Button definition
17714	 * @param  {boolean} inCollection true if the button is in a collection
17715	 * @private
17716	 */
17717	_expandButton: function ( attachTo, button, split, inCollection, inSplit, attachPoint, parentConf )
17718	{
17719		var dt = this.s.dt;
17720		var buttonCounter = 0;
17721		var isSplit = false;
17722		var buttons = ! Array.isArray( button ) ?
17723			[ button ] :
17724			button;
17725		
17726		if(button === undefined ) {
17727			buttons = !Array.isArray(split) ?
17728				[ split ] :
17729				split;
17730		}
17731
17732		if (button !== undefined && button.split !== undefined) {
17733			isSplit = true;
17734		}
17735			
17736		for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
17737			var conf = this._resolveExtends( buttons[i] );
17738
17739			if ( ! conf ) {
17740				continue;
17741			}
17742
17743			if( conf.config !== undefined && conf.config.split) {
17744				isSplit = true;
17745			}
17746			else {
17747				isSplit = false;
17748			}
17749			
17750			// If the configuration is an array, then expand the buttons at this
17751			// point
17752			if ( Array.isArray( conf ) ) {
17753				this._expandButton( attachTo, conf, built !== undefined && built.conf !== undefined ? built.conf.split : undefined, inCollection, parentConf !== undefined && parentConf.split !== undefined, attachPoint, parentConf );
17754				continue;
17755			}
17756
17757			var built = this._buildButton( conf, inCollection, conf.split !== undefined || (conf.config !== undefined && conf.config.split !== undefined), inSplit );
17758			if ( ! built ) {
17759				continue;
17760			}
17761
17762			if ( attachPoint !== undefined && attachPoint !== null ) {
17763				attachTo.splice( attachPoint, 0, built );
17764				attachPoint++;
17765			}
17766			else {
17767				attachTo.push( built );
17768			}
17769
17770			
17771			if ( built.conf.buttons || built.conf.split ) {
17772				built.collection = $('<'+(isSplit ? this.c.dom.splitCollection.tag : this.c.dom.collection.tag)+'/>');
17773
17774				built.conf._collection = built.collection;
17775
17776				if(built.conf.split) {
17777					for(var j = 0; j < built.conf.split.length; j++) {
17778						if(typeof built.conf.split[j] === "object") {
17779							built.conf.split[j].parent = parentConf;
17780							if(built.conf.split[j].collectionLayout === undefined) {
17781								built.conf.split[j].collectionLayout = built.conf.collectionLayout;
17782							}
17783							if(built.conf.split[j].dropup === undefined) {
17784								built.conf.split[j].dropup = built.conf.dropup;
17785							}
17786							if(built.conf.split[j].fade === undefined) {
17787								built.conf.split[j].fade = built.conf.fade;
17788							}
17789						}
17790					}
17791				}
17792				else {
17793					$(built.node).append($('<span class="dt-down-arrow">'+this.c.dom.splitDropdown.text+'</span>'))
17794				}
17795
17796				this._expandButton( built.buttons, built.conf.buttons, built.conf.split, !isSplit, isSplit, attachPoint, built.conf );
17797			}
17798			built.conf.parent = parentConf;
17799
17800			// init call is made here, rather than buildButton as it needs to
17801			// be selectable, and for that it needs to be in the buttons array
17802			if ( conf.init ) {
17803				conf.init.call( dt.button( built.node ), dt, $(built.node), conf );
17804			}
17805
17806			buttonCounter++;
17807		}
17808	},
17809
17810	/**
17811	 * Create an individual button
17812	 * @param  {object} config            Resolved button configuration
17813	 * @param  {boolean} inCollection `true` if a collection button
17814	 * @return {jQuery} Created button node (jQuery)
17815	 * @private
17816	 */
17817	_buildButton: function ( config, inCollection, isSplit, inSplit )
17818	{
17819		var buttonDom = this.c.dom.button;
17820		var linerDom = this.c.dom.buttonLiner;
17821		var collectionDom = this.c.dom.collection;
17822		var splitDom = this.c.dom.split;
17823		var splitCollectionDom = this.c.dom.splitCollection;
17824		var splitDropdownButton = this.c.dom.splitDropdownButton;
17825		var dt = this.s.dt;
17826		var text = function ( opt ) {
17827			return typeof opt === 'function' ?
17828				opt( dt, button, config ) :
17829				opt;
17830		};
17831
17832		// Spacers don't do much other than insert an element into the DOM
17833		if (config.spacer) {
17834			var spacer = $('<span></span>')
17835				.addClass('dt-button-spacer ' + config.style + ' ' + buttonDom.spacerClass)
17836				.html(text(config.text));
17837
17838			return {
17839				conf:         config,
17840				node:         spacer,
17841				inserter:     spacer,
17842				buttons:      [],
17843				inCollection: inCollection,
17844				isSplit:	  isSplit,
17845				inSplit:	  inSplit,
17846				collection:   null
17847			};
17848		}
17849
17850		if ( !isSplit && inSplit && splitCollectionDom ) {
17851			buttonDom = splitDropdownButton;
17852		}
17853		else if ( !isSplit && inCollection && collectionDom.button ) {
17854			buttonDom = collectionDom.button;
17855		} 
17856
17857		if ( !isSplit && inSplit && splitCollectionDom.buttonLiner ) {
17858			linerDom = splitCollectionDom.buttonLiner
17859		}
17860		else if ( !isSplit && inCollection && collectionDom.buttonLiner ) {
17861			linerDom = collectionDom.buttonLiner;
17862		}
17863
17864		// Make sure that the button is available based on whatever requirements
17865		// it has. For example, PDF button require pdfmake
17866		if ( config.available && ! config.available( dt, config ) && !config.hasOwnProperty('html') ) {
17867			return false;
17868		}
17869
17870		var button;
17871		if(!config.hasOwnProperty('html')) {
17872			var action = function ( e, dt, button, config ) {
17873				config.action.call( dt.button( button ), e, dt, button, config );
17874	
17875				$(dt.table().node()).triggerHandler( 'buttons-action.dt', [
17876					dt.button( button ), dt, button, config 
17877				] );
17878			};
17879
17880			var tag = config.tag || buttonDom.tag;
17881			var clickBlurs = config.clickBlurs === undefined
17882				? true :
17883				config.clickBlurs;
17884
17885			button = $('<'+tag+'/>')
17886				.addClass( buttonDom.className )
17887				.addClass( inSplit ? this.c.dom.splitDropdownButton.className : '')
17888				.attr( 'tabindex', this.s.dt.settings()[0].iTabIndex )
17889				.attr( 'aria-controls', this.s.dt.table().node().id )
17890				.on( 'click.dtb', function (e) {
17891					e.preventDefault();
17892	
17893					if ( ! button.hasClass( buttonDom.disabled ) && config.action ) {
17894						action( e, dt, button, config );
17895					}
17896					if( clickBlurs ) {
17897						button.trigger('blur');
17898					}
17899				} )
17900				.on( 'keypress.dtb', function (e) {
17901					if ( e.keyCode === 13 ) {
17902						e.preventDefault();
17903
17904						if ( ! button.hasClass( buttonDom.disabled ) && config.action ) {
17905							action( e, dt, button, config );
17906						}
17907					}
17908				} );
17909	
17910			// Make `a` tags act like a link
17911			if ( tag.toLowerCase() === 'a' ) {
17912				button.attr( 'href', '#' );
17913			}
17914	
17915			// Button tags should have `type=button` so they don't have any default behaviour
17916			if ( tag.toLowerCase() === 'button' ) {
17917				button.attr( 'type', 'button' );
17918			}
17919	
17920			if ( linerDom.tag ) {
17921				var liner = $('<'+linerDom.tag+'/>')
17922					.html( text( config.text ) )
17923					.addClass( linerDom.className );
17924	
17925				if ( linerDom.tag.toLowerCase() === 'a' ) {
17926					liner.attr( 'href', '#' );
17927				}
17928	
17929				button.append( liner );
17930			}
17931			else {
17932				button.html( text( config.text ) );
17933			}
17934	
17935			if ( config.enabled === false ) {
17936				button.addClass( buttonDom.disabled );
17937			}
17938	
17939			if ( config.className ) {
17940				button.addClass( config.className );
17941			}
17942	
17943			if ( config.titleAttr ) {
17944				button.attr( 'title', text( config.titleAttr ) );
17945			}
17946	
17947			if ( config.attr ) {
17948				button.attr( config.attr );
17949			}
17950	
17951			if ( ! config.namespace ) {
17952				config.namespace = '.dt-button-'+(_buttonCounter++);
17953			}
17954
17955			if  ( config.config !== undefined && config.config.split ) {
17956				config.split = config.config.split;
17957			}
17958		}
17959		else {
17960			button = $(config.html)
17961		}
17962	
17963		var buttonContainer = this.c.dom.buttonContainer;
17964		var inserter;
17965		if ( buttonContainer && buttonContainer.tag ) {
17966			inserter = $('<'+buttonContainer.tag+'/>')
17967				.addClass( buttonContainer.className )
17968				.append( button );
17969		}
17970		else {
17971			inserter = button;
17972		}
17973
17974		this._addKey( config );
17975
17976		// Style integration callback for DOM manipulation
17977		// Note that this is _not_ documented. It is currently
17978		// for style integration only
17979		if( this.c.buttonCreated ) {
17980			inserter = this.c.buttonCreated( config, inserter );
17981		}
17982
17983		var splitDiv;
17984		if(isSplit) {
17985			splitDiv = $('<div/>').addClass(this.c.dom.splitWrapper.className)
17986			splitDiv.append(button);
17987			var dropButtonConfig = $.extend(config, {
17988				text: this.c.dom.splitDropdown.text,
17989				className: this.c.dom.splitDropdown.className,
17990				closeButton: false,
17991				attr: {
17992					'aria-haspopup': 'dialog',
17993					'aria-expanded': false
17994				},
17995				align: this.c.dom.splitDropdown.align,
17996				splitAlignClass: this.c.dom.splitDropdown.splitAlignClass
17997				
17998			})
17999
18000			this._addKey(dropButtonConfig);
18001
18002			var splitAction = function ( e, dt, button, config ) {
18003				_dtButtons.split.action.call( dt.button(splitDiv), e, dt, button, config );
18004	
18005				$(dt.table().node()).triggerHandler( 'buttons-action.dt', [
18006					dt.button( button ), dt, button, config 
18007				] );
18008				button.attr('aria-expanded', true)
18009			};
18010			
18011			var dropButton = $('<button class="' + this.c.dom.splitDropdown.className + ' dt-button"><span class="dt-btn-split-drop-arrow">'+this.c.dom.splitDropdown.text+'</span></button>')
18012				.on( 'click.dtb', function (e) {
18013					e.preventDefault();
18014					e.stopPropagation();
18015
18016					if ( ! dropButton.hasClass( buttonDom.disabled )) {
18017						splitAction( e, dt, dropButton, dropButtonConfig );
18018					}
18019					if ( clickBlurs ) {
18020						dropButton.trigger('blur');
18021					}
18022				} )
18023				.on( 'keypress.dtb', function (e) {
18024					if ( e.keyCode === 13 ) {
18025						e.preventDefault();
18026
18027						if ( ! dropButton.hasClass( buttonDom.disabled ) ) {
18028							splitAction( e, dt, dropButton, dropButtonConfig );
18029						}
18030					}
18031				} );
18032
18033			if(config.split.length === 0) {
18034				dropButton.addClass('dtb-hide-drop');
18035			}
18036
18037			splitDiv.append(dropButton).attr(dropButtonConfig.attr);
18038		}
18039
18040		return {
18041			conf:         config,
18042			node:         isSplit ? splitDiv.get(0) : button.get(0),
18043			inserter:     isSplit ? splitDiv : inserter,
18044			buttons:      [],
18045			inCollection: inCollection,
18046			isSplit:	  isSplit,
18047			inSplit:	  inSplit,
18048			collection:   null
18049		};
18050	},
18051
18052	/**
18053	 * Get the button object from a node (recursive)
18054	 * @param  {node} node Button node
18055	 * @param  {array} [buttons] Button array, uses base if not defined
18056	 * @return {object} Button object
18057	 * @private
18058	 */
18059	_nodeToButton: function ( node, buttons )
18060	{
18061		if ( ! buttons ) {
18062			buttons = this.s.buttons;
18063		}
18064
18065		for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
18066			if ( buttons[i].node === node ) {
18067				return buttons[i];
18068			}
18069
18070			if ( buttons[i].buttons.length ) {
18071				var ret = this._nodeToButton( node, buttons[i].buttons );
18072
18073				if ( ret ) {
18074					return ret;
18075				}
18076			}
18077		}
18078	},
18079
18080	/**
18081	 * Get container array for a button from a button node (recursive)
18082	 * @param  {node} node Button node
18083	 * @param  {array} [buttons] Button array, uses base if not defined
18084	 * @return {array} Button's host array
18085	 * @private
18086	 */
18087	_nodeToHost: function ( node, buttons )
18088	{
18089		if ( ! buttons ) {
18090			buttons = this.s.buttons;
18091		}
18092
18093		for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
18094			if ( buttons[i].node === node ) {
18095				return buttons;
18096			}
18097
18098			if ( buttons[i].buttons.length ) {
18099				var ret = this._nodeToHost( node, buttons[i].buttons );
18100
18101				if ( ret ) {
18102					return ret;
18103				}
18104			}
18105		}
18106	},
18107
18108	/**
18109	 * Handle a key press - determine if any button's key configured matches
18110	 * what was typed and trigger the action if so.
18111	 * @param  {string} character The character pressed
18112	 * @param  {object} e Key event that triggered this call
18113	 * @private
18114	 */
18115	_keypress: function ( character, e )
18116	{
18117		// Check if this button press already activated on another instance of Buttons
18118		if ( e._buttonsHandled ) {
18119			return;
18120		}
18121
18122		var run = function ( conf, node ) {
18123			if ( ! conf.key ) {
18124				return;
18125			}
18126
18127			if ( conf.key === character ) {
18128				e._buttonsHandled = true;
18129				$(node).click();
18130			}
18131			else if ( $.isPlainObject( conf.key ) ) {
18132				if ( conf.key.key !== character ) {
18133					return;
18134				}
18135
18136				if ( conf.key.shiftKey && ! e.shiftKey ) {
18137					return;
18138				}
18139
18140				if ( conf.key.altKey && ! e.altKey ) {
18141					return;
18142				}
18143
18144				if ( conf.key.ctrlKey && ! e.ctrlKey ) {
18145					return;
18146				}
18147
18148				if ( conf.key.metaKey && ! e.metaKey ) {
18149					return;
18150				}
18151
18152				// Made it this far - it is good
18153				e._buttonsHandled = true;
18154				$(node).click();
18155			}
18156		};
18157
18158		var recurse = function ( a ) {
18159			for ( var i=0, ien=a.length ; i<ien ; i++ ) {
18160				run( a[i].conf, a[i].node );
18161
18162				if ( a[i].buttons.length ) {
18163					recurse( a[i].buttons );
18164				}
18165			}
18166		};
18167
18168		recurse( this.s.buttons );
18169	},
18170
18171	/**
18172	 * Remove a key from the key listener for this instance (to be used when a
18173	 * button is removed)
18174	 * @param  {object} conf Button configuration
18175	 * @private
18176	 */
18177	_removeKey: function ( conf )
18178	{
18179		if ( conf.key ) {
18180			var character = $.isPlainObject( conf.key ) ?
18181				conf.key.key :
18182				conf.key;
18183
18184			// Remove only one character, as multiple buttons could have the
18185			// same listening key
18186			var a = this.s.listenKeys.split('');
18187			var idx = $.inArray( character, a );
18188			a.splice( idx, 1 );
18189			this.s.listenKeys = a.join('');
18190		}
18191	},
18192
18193	/**
18194	 * Resolve a button configuration
18195	 * @param  {string|function|object} conf Button config to resolve
18196	 * @return {object} Button configuration
18197	 * @private
18198	 */
18199	_resolveExtends: function ( conf )
18200	{
18201		var that = this;
18202		var dt = this.s.dt;
18203		var i, ien;
18204		var toConfObject = function ( base ) {
18205			var loop = 0;
18206
18207			// Loop until we have resolved to a button configuration, or an
18208			// array of button configurations (which will be iterated
18209			// separately)
18210			while ( ! $.isPlainObject(base) && ! Array.isArray(base) ) {
18211				if ( base === undefined ) {
18212					return;
18213				}
18214
18215				if ( typeof base === 'function' ) {
18216					base = base.call( that, dt, conf );
18217
18218					if ( ! base ) {
18219						return false;
18220					}
18221				}
18222				else if ( typeof base === 'string' ) {
18223					if ( ! _dtButtons[ base ] ) {
18224						return {html: base}
18225					}
18226
18227					base = _dtButtons[ base ];
18228				}
18229
18230				loop++;
18231				if ( loop > 30 ) {
18232					// Protect against misconfiguration killing the browser
18233					throw 'Buttons: Too many iterations';
18234				}
18235			}
18236
18237			return Array.isArray( base ) ?
18238				base :
18239				$.extend( {}, base );
18240		};
18241
18242		conf = toConfObject( conf );
18243
18244		while ( conf && conf.extend ) {
18245			// Use `toConfObject` in case the button definition being extended
18246			// is itself a string or a function
18247			if ( ! _dtButtons[ conf.extend ] ) {
18248				throw 'Cannot extend unknown button type: '+conf.extend;
18249			}
18250
18251			var objArray = toConfObject( _dtButtons[ conf.extend ] );
18252			if ( Array.isArray( objArray ) ) {
18253				return objArray;
18254			}
18255			else if ( ! objArray ) {
18256				// This is a little brutal as it might be possible to have a
18257				// valid button without the extend, but if there is no extend
18258				// then the host button would be acting in an undefined state
18259				return false;
18260			}
18261
18262			// Stash the current class name
18263			var originalClassName = objArray.className;
18264
18265			if (conf.config !== undefined && objArray.config !== undefined) {
18266				conf.config = $.extend({}, objArray.config, conf.config)
18267			}
18268
18269			conf = $.extend( {}, objArray, conf );
18270
18271			// The extend will have overwritten the original class name if the
18272			// `conf` object also assigned a class, but we want to concatenate
18273			// them so they are list that is combined from all extended buttons
18274			if ( originalClassName && conf.className !== originalClassName ) {
18275				conf.className = originalClassName+' '+conf.className;
18276			}
18277
18278			// Although we want the `conf` object to overwrite almost all of
18279			// the properties of the object being extended, the `extend`
18280			// property should come from the object being extended
18281			conf.extend = objArray.extend;
18282		}
18283
18284		// Buttons to be added to a collection  -gives the ability to define
18285		// if buttons should be added to the start or end of a collection
18286		var postfixButtons = conf.postfixButtons;
18287		if ( postfixButtons ) {
18288			if ( ! conf.buttons ) {
18289				conf.buttons = [];
18290			}
18291
18292			for ( i=0, ien=postfixButtons.length ; i<ien ; i++ ) {
18293				conf.buttons.push( postfixButtons[i] );
18294			}
18295		}
18296
18297		var prefixButtons = conf.prefixButtons;
18298		if ( prefixButtons ) {
18299			if ( ! conf.buttons ) {
18300				conf.buttons = [];
18301			}
18302
18303			for ( i=0, ien=prefixButtons.length ; i<ien ; i++ ) {
18304				conf.buttons.splice( i, 0, prefixButtons[i] );
18305			}
18306		}
18307
18308		return conf;
18309	},
18310
18311	/**
18312	 * Display (and replace if there is an existing one) a popover attached to a button
18313	 * @param {string|node} content Content to show
18314	 * @param {DataTable.Api} hostButton DT API instance of the button
18315	 * @param {object} inOpts Options (see object below for all options)
18316	 */
18317	_popover: function ( content, hostButton, inOpts, e ) {
18318		var dt = hostButton;
18319		var buttonsSettings = this.c;
18320		var closed = false;
18321		var options = $.extend( {
18322			align: 'button-left', // button-right, dt-container, split-left, split-right
18323			autoClose: false,
18324			background: true,
18325			backgroundClassName: 'dt-button-background',
18326			closeButton: true,
18327			contentClassName: buttonsSettings.dom.collection.className,
18328			collectionLayout: '',
18329			collectionTitle: '',
18330			dropup: false,
18331			fade: 400,
18332			popoverTitle: '',
18333			rightAlignClassName: 'dt-button-right',
18334			tag: buttonsSettings.dom.collection.tag
18335		}, inOpts );
18336
18337		var hostNode = hostButton.node();
18338
18339		var close = function () {
18340			closed = true;
18341
18342			_fadeOut(
18343				$('.dt-button-collection'),
18344				options.fade,
18345				function () {
18346					$(this).detach();
18347				}
18348			);
18349
18350			$(dt.buttons( '[aria-haspopup="dialog"][aria-expanded="true"]' ).nodes())
18351				.attr('aria-expanded', 'false');
18352
18353			$('div.dt-button-background').off( 'click.dtb-collection' );
18354			Buttons.background( false, options.backgroundClassName, options.fade, hostNode );
18355
18356			$(window).off('resize.resize.dtb-collection');
18357			$('body').off( '.dtb-collection' );
18358			dt.off( 'buttons-action.b-internal' );
18359			dt.off( 'destroy' );
18360		};
18361
18362		if (content === false) {
18363			close();
18364			return;
18365		}
18366
18367		var existingExpanded = $(dt.buttons( '[aria-haspopup="dialog"][aria-expanded="true"]' ).nodes());
18368		if ( existingExpanded.length ) {
18369			// Reuse the current position if the button that was triggered is inside an existing collection
18370			if (hostNode.closest('div.dt-button-collection').length) {
18371				hostNode = existingExpanded.eq(0);
18372			}
18373
18374			close();
18375		}
18376
18377		// Try to be smart about the layout
18378		var cnt = $('.dt-button', content).length;
18379		var mod = '';
18380
18381		if (cnt === 3) {
18382			mod = 'dtb-b3';
18383		}
18384		else if (cnt === 2) {
18385			mod = 'dtb-b2';
18386		}
18387		else if (cnt === 1) {
18388			mod = 'dtb-b1';
18389		}
18390
18391		var display = $('<div/>')
18392			.addClass('dt-button-collection')
18393			.addClass(options.collectionLayout)
18394			.addClass(options.splitAlignClass)
18395			.addClass(mod)
18396			.css('display', 'none')
18397			.attr({
18398				'aria-modal': true,
18399				role: 'dialog'
18400			});
18401
18402		content = $(content)
18403			.addClass(options.contentClassName)
18404			.attr('role', 'menu')
18405			.appendTo(display);
18406
18407		hostNode.attr( 'aria-expanded', 'true' );
18408
18409		if ( hostNode.parents('body')[0] !== document.body ) {
18410			hostNode = document.body.lastChild;
18411		}
18412
18413		if ( options.popoverTitle ) {
18414			display.prepend('<div class="dt-button-collection-title">'+options.popoverTitle+'</div>');
18415		}
18416		else if ( options.collectionTitle ) {
18417			display.prepend('<div class="dt-button-collection-title">'+options.collectionTitle+'</div>');
18418		}
18419
18420		if (options.closeButton) {
18421			display.prepend('<div class="dtb-popover-close">x</div>').addClass('dtb-collection-closeable')
18422		}
18423
18424		_fadeIn( display.insertAfter( hostNode ), options.fade );
18425
18426		var tableContainer = $( hostButton.table().container() );
18427		var position = display.css( 'position' );
18428
18429		if ( options.span === 'container' || options.align === 'dt-container' ) {
18430			hostNode = hostNode.parent();
18431			display.css('width', tableContainer.width());
18432		}
18433
18434		// Align the popover relative to the DataTables container
18435		// Useful for wide popovers such as SearchPanes
18436		if (position === 'absolute') {
18437			// Align relative to the host button
18438			var offsetParent = $(hostNode[0].offsetParent);
18439			var buttonPosition = hostNode.position();
18440			var buttonOffset = hostNode.offset();
18441			var tableSizes = offsetParent.offset();
18442			var containerPosition = offsetParent.position();
18443			var computed = window.getComputedStyle(offsetParent[0]);
18444
18445			tableSizes.height = offsetParent.outerHeight();
18446			tableSizes.width = offsetParent.width() + parseFloat(computed.paddingLeft);
18447			tableSizes.right = tableSizes.left + tableSizes.width;
18448			tableSizes.bottom = tableSizes.top + tableSizes.height;
18449
18450			// Set the initial position so we can read height / width
18451			var top = buttonPosition.top + hostNode.outerHeight();
18452			var left = buttonPosition.left;
18453
18454			display.css( {
18455				top: top,
18456				left: left
18457			} );
18458
18459			// Get the popover position
18460			computed = window.getComputedStyle(display[0]);
18461			var popoverSizes = display.offset();
18462
18463			popoverSizes.height = display.outerHeight();
18464			popoverSizes.width = display.outerWidth();
18465			popoverSizes.right = popoverSizes.left + popoverSizes.width;
18466			popoverSizes.bottom = popoverSizes.top + popoverSizes.height;
18467			popoverSizes.marginTop = parseFloat(computed.marginTop);
18468			popoverSizes.marginBottom = parseFloat(computed.marginBottom);
18469
18470			// First position per the class requirements - pop up and right align
18471			if (options.dropup) {
18472				top = buttonPosition.top - popoverSizes.height - popoverSizes.marginTop - popoverSizes.marginBottom;
18473			}
18474
18475			if (options.align === 'button-right' || display.hasClass( options.rightAlignClassName )) {
18476				left = buttonPosition.left - popoverSizes.width + hostNode.outerWidth(); 
18477			}
18478
18479			// Container alignment - make sure it doesn't overflow the table container
18480			if (options.align === 'dt-container' || options.align === 'container') {
18481				if (left < buttonPosition.left) {
18482					left = -buttonPosition.left;
18483				}
18484
18485				if (left + popoverSizes.width > tableSizes.width) {
18486					left = tableSizes.width - popoverSizes.width;
18487				}
18488			}
18489
18490			// Window adjustment
18491			if (containerPosition.left + left + popoverSizes.width > $(window).width()) {
18492				// Overflowing the document to the right
18493				left = $(window).width() - popoverSizes.width - containerPosition.left;
18494			}
18495
18496			if (buttonOffset.left + left < 0) {
18497				// Off to the left of the document
18498				left = -buttonOffset.left;
18499			}
18500
18501			if (containerPosition.top + top + popoverSizes.height > $(window).height() + $(window).scrollTop()) {
18502				// Pop up if otherwise we'd need the user to scroll down
18503				top = buttonPosition.top - popoverSizes.height - popoverSizes.marginTop - popoverSizes.marginBottom;
18504			}
18505
18506			if (containerPosition.top + top < $(window).scrollTop()) {
18507				// Correction for when the top is beyond the top of the page
18508				top = buttonPosition.top + hostNode.outerHeight();
18509			}
18510
18511			// Calculations all done - now set it
18512			display.css( {
18513				top: top,
18514				left: left
18515			} );
18516		}
18517		else {
18518			// Fix position - centre on screen
18519			var position = function () {
18520				var half = $(window).height() / 2;
18521
18522				var top = display.height() / 2;
18523				if ( top > half ) {
18524					top = half;
18525				}
18526
18527				display.css( 'marginTop', top*-1 );
18528			};
18529
18530			position();
18531
18532			$(window).on('resize.dtb-collection', function () {
18533				position();
18534			});
18535		}
18536
18537		if ( options.background ) {
18538			Buttons.background(
18539				true,
18540				options.backgroundClassName,
18541				options.fade,
18542				options.backgroundHost || hostNode
18543			);
18544		}
18545
18546		// This is bonkers, but if we don't have a click listener on the
18547		// background element, iOS Safari will ignore the body click
18548		// listener below. An empty function here is all that is
18549		// required to make it work...
18550		$('div.dt-button-background').on( 'click.dtb-collection', function () {} );
18551
18552		if ( options.autoClose ) {
18553			setTimeout( function () {
18554				dt.on( 'buttons-action.b-internal', function (e, btn, dt, node) {
18555					if ( node[0] === hostNode[0] ) {
18556						return;
18557					}
18558					close();
18559				} );
18560			}, 0);
18561		}
18562		
18563		$(display).trigger('buttons-popover.dt');
18564
18565
18566		dt.on('destroy', close);
18567
18568		setTimeout(function() {
18569			closed = false;
18570			$('body')
18571				.on( 'click.dtb-collection', function (e) {
18572					if (closed) {
18573						return;
18574					}
18575
18576					// andSelf is deprecated in jQ1.8, but we want 1.7 compat
18577					var back = $.fn.addBack ? 'addBack' : 'andSelf';
18578					var parent = $(e.target).parent()[0];
18579	
18580					if (( ! $(e.target).parents()[back]().filter( content ).length  && !$(parent).hasClass('dt-buttons')) || $(e.target).hasClass('dt-button-background')) {
18581						close();
18582					}
18583				} )
18584				.on( 'keyup.dtb-collection', function (e) {
18585					if ( e.keyCode === 27 ) {
18586						close();
18587					}
18588				} )
18589				.on( 'keydown.dtb-collection', function (e) {
18590					// Focus trap for tab key
18591					var elements = $('a, button', content);
18592					var active = document.activeElement;
18593
18594					if (e.keyCode !== 9) { // tab
18595						return;
18596					}
18597
18598					if (elements.index(active) === -1) {
18599						// If current focus is not inside the popover
18600						elements.first().focus();
18601						e.preventDefault();
18602					}
18603					else if (e.shiftKey) {
18604						// Reverse tabbing order when shift key is pressed
18605						if (active === elements[0]) {
18606							elements.last().focus();
18607							e.preventDefault();
18608						}
18609					}
18610					else {
18611						if (active === elements.last()[0]) {
18612							elements.first().focus();
18613							e.preventDefault();
18614						}
18615					}
18616				} );
18617		}, 0);
18618	}
18619} );
18620
18621
18622
18623/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
18624 * Statics
18625 */
18626
18627/**
18628 * Show / hide a background layer behind a collection
18629 * @param  {boolean} Flag to indicate if the background should be shown or
18630 *   hidden 
18631 * @param  {string} Class to assign to the background
18632 * @static
18633 */
18634Buttons.background = function ( show, className, fade, insertPoint ) {
18635	if ( fade === undefined ) {
18636		fade = 400;
18637	}
18638	if ( ! insertPoint ) {
18639		insertPoint = document.body;
18640	}
18641
18642	if ( show ) {
18643		_fadeIn(
18644			$('<div/>')
18645				.addClass( className )
18646				.css( 'display', 'none' )
18647				.insertAfter( insertPoint ),
18648			fade
18649		);
18650	}
18651	else {
18652		_fadeOut(
18653			$('div.'+className),
18654			fade,
18655			function () {
18656				$(this)
18657					.removeClass( className )
18658					.remove();
18659			}
18660		);
18661	}
18662};
18663
18664/**
18665 * Instance selector - select Buttons instances based on an instance selector
18666 * value from the buttons assigned to a DataTable. This is only useful if
18667 * multiple instances are attached to a DataTable.
18668 * @param  {string|int|array} Instance selector - see `instance-selector`
18669 *   documentation on the DataTables site
18670 * @param  {array} Button instance array that was attached to the DataTables
18671 *   settings object
18672 * @return {array} Buttons instances
18673 * @static
18674 */
18675Buttons.instanceSelector = function ( group, buttons )
18676{
18677	if ( group === undefined || group === null ) {
18678		return $.map( buttons, function ( v ) {
18679			return v.inst;
18680		} );
18681	}
18682
18683	var ret = [];
18684	var names = $.map( buttons, function ( v ) {
18685		return v.name;
18686	} );
18687
18688	// Flatten the group selector into an array of single options
18689	var process = function ( input ) {
18690		if ( Array.isArray( input ) ) {
18691			for ( var i=0, ien=input.length ; i<ien ; i++ ) {
18692				process( input[i] );
18693			}
18694			return;
18695		}
18696
18697		if ( typeof input === 'string' ) {
18698			if ( input.indexOf( ',' ) !== -1 ) {
18699				// String selector, list of names
18700				process( input.split(',') );
18701			}
18702			else {
18703				// String selector individual name
18704				var idx = $.inArray( input.trim(), names );
18705
18706				if ( idx !== -1 ) {
18707					ret.push( buttons[ idx ].inst );
18708				}
18709			}
18710		}
18711		else if ( typeof input === 'number' ) {
18712			// Index selector
18713			ret.push( buttons[ input ].inst );
18714		}
18715		else if ( typeof input === 'object' ) {
18716			// Actual instance selector
18717			ret.push( input );
18718		}
18719	};
18720	
18721	process( group );
18722
18723	return ret;
18724};
18725
18726/**
18727 * Button selector - select one or more buttons from a selector input so some
18728 * operation can be performed on them.
18729 * @param  {array} Button instances array that the selector should operate on
18730 * @param  {string|int|node|jQuery|array} Button selector - see
18731 *   `button-selector` documentation on the DataTables site
18732 * @return {array} Array of objects containing `inst` and `idx` properties of
18733 *   the selected buttons so you know which instance each button belongs to.
18734 * @static
18735 */
18736Buttons.buttonSelector = function ( insts, selector )
18737{
18738	var ret = [];
18739	var nodeBuilder = function ( a, buttons, baseIdx ) {
18740		var button;
18741		var idx;
18742
18743		for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
18744			button = buttons[i];
18745
18746			if ( button ) {
18747				idx = baseIdx !== undefined ?
18748					baseIdx+i :
18749					i+'';
18750
18751				a.push( {
18752					node: button.node,
18753					name: button.conf.name,
18754					idx:  idx
18755				} );
18756
18757				if ( button.buttons ) {
18758					nodeBuilder( a, button.buttons, idx+'-' );
18759				}
18760			}
18761		}
18762	};
18763
18764	var run = function ( selector, inst ) {
18765		var i, ien;
18766		var buttons = [];
18767		nodeBuilder( buttons, inst.s.buttons );
18768
18769		var nodes = $.map( buttons, function (v) {
18770			return v.node;
18771		} );
18772
18773		if ( Array.isArray( selector ) || selector instanceof $ ) {
18774			for ( i=0, ien=selector.length ; i<ien ; i++ ) {
18775				run( selector[i], inst );
18776			}
18777			return;
18778		}
18779
18780		if ( selector === null || selector === undefined || selector === '*' ) {
18781			// Select all
18782			for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
18783				ret.push( {
18784					inst: inst,
18785					node: buttons[i].node
18786				} );
18787			}
18788		}
18789		else if ( typeof selector === 'number' ) {
18790			// Main button index selector
18791			if (inst.s.buttons[ selector ]) {
18792				ret.push( {
18793					inst: inst,
18794					node: inst.s.buttons[ selector ].node
18795				} );
18796			}
18797		}
18798		else if ( typeof selector === 'string' ) {
18799			if ( selector.indexOf( ',' ) !== -1 ) {
18800				// Split
18801				var a = selector.split(',');
18802
18803				for ( i=0, ien=a.length ; i<ien ; i++ ) {
18804					run( a[i].trim(), inst );
18805				}
18806			}
18807			else if ( selector.match( /^\d+(\-\d+)*$/ ) ) {
18808				// Sub-button index selector
18809				var indexes = $.map( buttons, function (v) {
18810					return v.idx;
18811				} );
18812
18813				ret.push( {
18814					inst: inst,
18815					node: buttons[ $.inArray( selector, indexes ) ].node
18816				} );
18817			}
18818			else if ( selector.indexOf( ':name' ) !== -1 ) {
18819				// Button name selector
18820				var name = selector.replace( ':name', '' );
18821
18822				for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
18823					if ( buttons[i].name === name ) {
18824						ret.push( {
18825							inst: inst,
18826							node: buttons[i].node
18827						} );
18828					}
18829				}
18830			}
18831			else {
18832				// jQuery selector on the nodes
18833				$( nodes ).filter( selector ).each( function () {
18834					ret.push( {
18835						inst: inst,
18836						node: this
18837					} );
18838				} );
18839			}
18840		}
18841		else if ( typeof selector === 'object' && selector.nodeName ) {
18842			// Node selector
18843			var idx = $.inArray( selector, nodes );
18844
18845			if ( idx !== -1 ) {
18846				ret.push( {
18847					inst: inst,
18848					node: nodes[ idx ]
18849				} );
18850			}
18851		}
18852	};
18853
18854
18855	for ( var i=0, ien=insts.length ; i<ien ; i++ ) {
18856		var inst = insts[i];
18857
18858		run( selector, inst );
18859	}
18860
18861	return ret;
18862};
18863
18864/**
18865 * Default function used for formatting output data.
18866 * @param {*} str Data to strip
18867 */
18868Buttons.stripData = function ( str, config ) {
18869	if ( typeof str !== 'string' ) {
18870		return str;
18871	}
18872
18873	// Always remove script tags
18874	str = str.replace( /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '' );
18875
18876	// Always remove comments
18877	str = str.replace( /<!\-\-.*?\-\->/g, '' );
18878
18879	if ( ! config || config.stripHtml ) {
18880		str = str.replace( /<[^>]*>/g, '' );
18881	}
18882
18883	if ( ! config || config.trim ) {
18884		str = str.replace( /^\s+|\s+$/g, '' );
18885	}
18886
18887	if ( ! config || config.stripNewlines ) {
18888		str = str.replace( /\n/g, ' ' );
18889	}
18890
18891	if ( ! config || config.decodeEntities ) {
18892		_exportTextarea.innerHTML = str;
18893		str = _exportTextarea.value;
18894	}
18895
18896	return str;
18897};
18898
18899
18900/**
18901 * Buttons defaults. For full documentation, please refer to the docs/option
18902 * directory or the DataTables site.
18903 * @type {Object}
18904 * @static
18905 */
18906Buttons.defaults = {
18907	buttons: [ 'copy', 'excel', 'csv', 'pdf', 'print' ],
18908	name: 'main',
18909	tabIndex: 0,
18910	dom: {
18911		container: {
18912			tag: 'div',
18913			className: 'dt-buttons'
18914		},
18915		collection: {
18916			tag: 'div',
18917			className: ''
18918		},
18919		button: {
18920			tag: 'button',
18921			className: 'dt-button',
18922			active: 'active',
18923			disabled: 'disabled',
18924			spacerClass: ''
18925		},
18926		buttonLiner: {
18927			tag: 'span',
18928			className: ''
18929		},
18930		split: {
18931			tag: 'div',
18932			className: 'dt-button-split',
18933		},
18934		splitWrapper: {
18935			tag: 'div',
18936			className: 'dt-btn-split-wrapper',
18937		},
18938		splitDropdown: {
18939			tag: 'button',
18940			text: '&#x25BC;',
18941			className: 'dt-btn-split-drop',
18942			align: 'split-right',
18943			splitAlignClass: 'dt-button-split-left'
18944		},
18945		splitDropdownButton: {
18946			tag: 'button',
18947			className: 'dt-btn-split-drop-button dt-button',
18948		},
18949		splitCollection: {
18950			tag: 'div',
18951			className: 'dt-button-split-collection',
18952		}
18953	}
18954};
18955
18956/**
18957 * Version information
18958 * @type {string}
18959 * @static
18960 */
18961Buttons.version = '2.3.6';
18962
18963
18964$.extend( _dtButtons, {
18965	collection: {
18966		text: function ( dt ) {
18967			return dt.i18n( 'buttons.collection', 'Collection' );
18968		},
18969		className: 'buttons-collection',
18970		closeButton: false,
18971		init: function ( dt, button, config ) {
18972			button.attr( 'aria-expanded', false );
18973		},
18974		action: function ( e, dt, button, config ) {
18975			if ( config._collection.parents('body').length ) {
18976				this.popover(false, config);
18977			}
18978			else {
18979				this.popover(config._collection, config);
18980			}
18981
18982			// When activated using a key - auto focus on the
18983			// first item in the popover
18984			if (e.type === 'keypress') {
18985				$('a, button', config._collection).eq(0).focus();
18986			}
18987		},
18988		attr: {
18989			'aria-haspopup': 'dialog'
18990		}
18991		// Also the popover options, defined in Buttons.popover
18992	},
18993	split: {
18994		text: function ( dt ) {
18995			return dt.i18n( 'buttons.split', 'Split' );
18996		},
18997		className: 'buttons-split',
18998		closeButton: false,
18999		init: function ( dt, button, config ) {
19000			return button.attr( 'aria-expanded', false );
19001		},
19002		action: function ( e, dt, button, config ) {
19003			this.popover(config._collection, config);
19004		},
19005		attr: {
19006			'aria-haspopup': 'dialog'
19007		}
19008		// Also the popover options, defined in Buttons.popover
19009	},
19010	copy: function ( dt, conf ) {
19011		if ( _dtButtons.copyHtml5 ) {
19012			return 'copyHtml5';
19013		}
19014	},
19015	csv: function ( dt, conf ) {
19016		if ( _dtButtons.csvHtml5 && _dtButtons.csvHtml5.available( dt, conf ) ) {
19017			return 'csvHtml5';
19018		}
19019	},
19020	excel: function ( dt, conf ) {
19021		if ( _dtButtons.excelHtml5 && _dtButtons.excelHtml5.available( dt, conf ) ) {
19022			return 'excelHtml5';
19023		}
19024	},
19025	pdf: function ( dt, conf ) {
19026		if ( _dtButtons.pdfHtml5 && _dtButtons.pdfHtml5.available( dt, conf ) ) {
19027			return 'pdfHtml5';
19028		}
19029	},
19030	pageLength: function ( dt ) {
19031		var lengthMenu = dt.settings()[0].aLengthMenu;
19032		var vals = [];
19033		var lang = [];
19034		var text = function ( dt ) {
19035			return dt.i18n( 'buttons.pageLength', {
19036				"-1": 'Show all rows',
19037				_:    'Show %d rows'
19038			}, dt.page.len() );
19039		};
19040
19041		// Support for DataTables 1.x 2D array
19042		if (Array.isArray( lengthMenu[0] )) {
19043			vals = lengthMenu[0];
19044			lang = lengthMenu[1];
19045		}
19046		else {
19047			for (var i=0 ; i<lengthMenu.length ; i++) {
19048				var option = lengthMenu[i];
19049
19050				// Support for DataTables 2 object in the array
19051				if ($.isPlainObject(option)) {
19052					vals.push(option.value);
19053					lang.push(option.label);
19054				}
19055				else {
19056					vals.push(option);
19057					lang.push(option);
19058				}
19059			}
19060		}
19061
19062		return {
19063			extend: 'collection',
19064			text: text,
19065			className: 'buttons-page-length',
19066			autoClose: true,
19067			buttons: $.map( vals, function ( val, i ) {
19068				return {
19069					text: lang[i],
19070					className: 'button-page-length',
19071					action: function ( e, dt ) {
19072						dt.page.len( val ).draw();
19073					},
19074					init: function ( dt, node, conf ) {
19075						var that = this;
19076						var fn = function () {
19077							that.active( dt.page.len() === val );
19078						};
19079
19080						dt.on( 'length.dt'+conf.namespace, fn );
19081						fn();
19082					},
19083					destroy: function ( dt, node, conf ) {
19084						dt.off( 'length.dt'+conf.namespace );
19085					}
19086				};
19087			} ),
19088			init: function ( dt, node, conf ) {
19089				var that = this;
19090				dt.on( 'length.dt'+conf.namespace, function () {
19091					that.text( conf.text );
19092				} );
19093			},
19094			destroy: function ( dt, node, conf ) {
19095				dt.off( 'length.dt'+conf.namespace );
19096			}
19097		};
19098	},
19099	spacer: {
19100		style: 'empty',
19101		spacer: true,
19102		text: function ( dt ) {
19103			return dt.i18n( 'buttons.spacer', '' );
19104		}
19105	}
19106} );
19107
19108
19109/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
19110 * DataTables API
19111 *
19112 * For complete documentation, please refer to the docs/api directory or the
19113 * DataTables site
19114 */
19115
19116// Buttons group and individual button selector
19117DataTable.Api.register( 'buttons()', function ( group, selector ) {
19118	// Argument shifting
19119	if ( selector === undefined ) {
19120		selector = group;
19121		group = undefined;
19122	}
19123
19124	this.selector.buttonGroup = group;
19125
19126	var res = this.iterator( true, 'table', function ( ctx ) {
19127		if ( ctx._buttons ) {
19128			return Buttons.buttonSelector(
19129				Buttons.instanceSelector( group, ctx._buttons ),
19130				selector
19131			);
19132		}
19133	}, true );
19134
19135	res._groupSelector = group;
19136	return res;
19137} );
19138
19139// Individual button selector
19140DataTable.Api.register( 'button()', function ( group, selector ) {
19141	// just run buttons() and truncate
19142	var buttons = this.buttons( group, selector );
19143
19144	if ( buttons.length > 1 ) {
19145		buttons.splice( 1, buttons.length );
19146	}
19147
19148	return buttons;
19149} );
19150
19151// Active buttons
19152DataTable.Api.registerPlural( 'buttons().active()', 'button().active()', function ( flag ) {
19153	if ( flag === undefined ) {
19154		return this.map( function ( set ) {
19155			return set.inst.active( set.node );
19156		} );
19157	}
19158
19159	return this.each( function ( set ) {
19160		set.inst.active( set.node, flag );
19161	} );
19162} );
19163
19164// Get / set button action
19165DataTable.Api.registerPlural( 'buttons().action()', 'button().action()', function ( action ) {
19166	if ( action === undefined ) {
19167		return this.map( function ( set ) {
19168			return set.inst.action( set.node );
19169		} );
19170	}
19171
19172	return this.each( function ( set ) {
19173		set.inst.action( set.node, action );
19174	} );
19175} );
19176
19177// Collection control
19178DataTable.Api.registerPlural( 'buttons().collectionRebuild()', 'button().collectionRebuild()', function ( buttons ) {
19179	return this.each( function ( set ) {
19180		for(var i = 0; i < buttons.length; i++) {
19181			if(typeof buttons[i] === 'object') {
19182				buttons[i].parentConf = set;
19183			}
19184		}
19185		set.inst.collectionRebuild( set.node, buttons );
19186	} );
19187} );
19188
19189// Enable / disable buttons
19190DataTable.Api.register( ['buttons().enable()', 'button().enable()'], function ( flag ) {
19191	return this.each( function ( set ) {
19192		set.inst.enable( set.node, flag );
19193	} );
19194} );
19195
19196// Disable buttons
19197DataTable.Api.register( ['buttons().disable()', 'button().disable()'], function () {
19198	return this.each( function ( set ) {
19199		set.inst.disable( set.node );
19200	} );
19201} );
19202
19203// Button index
19204DataTable.Api.register( 'button().index()', function () {
19205	var idx = null;
19206
19207	this.each( function ( set ) {
19208		var res = set.inst.index( set.node );
19209
19210		if (res !== null) {
19211			idx = res;
19212		}
19213	} );
19214
19215	return idx;
19216} );
19217
19218// Get button nodes
19219DataTable.Api.registerPlural( 'buttons().nodes()', 'button().node()', function () {
19220	var jq = $();
19221
19222	// jQuery will automatically reduce duplicates to a single entry
19223	$( this.each( function ( set ) {
19224		jq = jq.add( set.inst.node( set.node ) );
19225	} ) );
19226
19227	return jq;
19228} );
19229
19230// Get / set button processing state
19231DataTable.Api.registerPlural( 'buttons().processing()', 'button().processing()', function ( flag ) {
19232	if ( flag === undefined ) {
19233		return this.map( function ( set ) {
19234			return set.inst.processing( set.node );
19235		} );
19236	}
19237
19238	return this.each( function ( set ) {
19239		set.inst.processing( set.node, flag );
19240	} );
19241} );
19242
19243// Get / set button text (i.e. the button labels)
19244DataTable.Api.registerPlural( 'buttons().text()', 'button().text()', function ( label ) {
19245	if ( label === undefined ) {
19246		return this.map( function ( set ) {
19247			return set.inst.text( set.node );
19248		} );
19249	}
19250
19251	return this.each( function ( set ) {
19252		set.inst.text( set.node, label );
19253	} );
19254} );
19255
19256// Trigger a button's action
19257DataTable.Api.registerPlural( 'buttons().trigger()', 'button().trigger()', function () {
19258	return this.each( function ( set ) {
19259		set.inst.node( set.node ).trigger( 'click' );
19260	} );
19261} );
19262
19263// Button resolver to the popover
19264DataTable.Api.register( 'button().popover()', function (content, options) {
19265	return this.map( function ( set ) {
19266		return set.inst._popover( content, this.button(this[0].node), options );
19267	} );
19268} );
19269
19270// Get the container elements
19271DataTable.Api.register( 'buttons().containers()', function () {
19272	var jq = $();
19273	var groupSelector = this._groupSelector;
19274
19275	// We need to use the group selector directly, since if there are no buttons
19276	// the result set will be empty
19277	this.iterator( true, 'table', function ( ctx ) {
19278		if ( ctx._buttons ) {
19279			var insts = Buttons.instanceSelector( groupSelector, ctx._buttons );
19280
19281			for ( var i=0, ien=insts.length ; i<ien ; i++ ) {
19282				jq = jq.add( insts[i].container() );
19283			}
19284		}
19285	} );
19286
19287	return jq;
19288} );
19289
19290DataTable.Api.register( 'buttons().container()', function () {
19291	// API level of nesting is `buttons()` so we can zip into the containers method
19292	return this.containers().eq(0);
19293} );
19294
19295// Add a new button
19296DataTable.Api.register( 'button().add()', function ( idx, conf, draw ) {
19297	var ctx = this.context;
19298
19299	// Don't use `this` as it could be empty - select the instances directly
19300	if ( ctx.length ) {
19301		var inst = Buttons.instanceSelector( this._groupSelector, ctx[0]._buttons );
19302
19303		if ( inst.length ) {
19304			inst[0].add( conf, idx , draw);
19305		}
19306	}
19307
19308	return this.button( this._groupSelector, idx );
19309} );
19310
19311// Destroy the button sets selected
19312DataTable.Api.register( 'buttons().destroy()', function () {
19313	this.pluck( 'inst' ).unique().each( function ( inst ) {
19314		inst.destroy();
19315	} );
19316
19317	return this;
19318} );
19319
19320// Remove a button
19321DataTable.Api.registerPlural( 'buttons().remove()', 'buttons().remove()', function () {
19322	this.each( function ( set ) {
19323		set.inst.remove( set.node );
19324	} );
19325
19326	return this;
19327} );
19328
19329// Information box that can be used by buttons
19330var _infoTimer;
19331DataTable.Api.register( 'buttons.info()', function ( title, message, time ) {
19332	var that = this;
19333
19334	if ( title === false ) {
19335		this.off('destroy.btn-info');
19336		_fadeOut(
19337			$('#datatables_buttons_info'),
19338			400,
19339			function () {
19340				$(this).remove();
19341			}
19342		);
19343		clearTimeout( _infoTimer );
19344		_infoTimer = null;
19345
19346		return this;
19347	}
19348
19349	if ( _infoTimer ) {
19350		clearTimeout( _infoTimer );
19351	}
19352
19353	if ( $('#datatables_buttons_info').length ) {
19354		$('#datatables_buttons_info').remove();
19355	}
19356
19357	title = title ? '<h2>'+title+'</h2>' : '';
19358
19359	_fadeIn(
19360		$('<div id="datatables_buttons_info" class="dt-button-info"/>')
19361			.html( title )
19362			.append( $('<div/>')[ typeof message === 'string' ? 'html' : 'append' ]( message ) )
19363			.css( 'display', 'none' )
19364			.appendTo( 'body' )
19365	);
19366
19367	if ( time !== undefined && time !== 0 ) {
19368		_infoTimer = setTimeout( function () {
19369			that.buttons.info( false );
19370		}, time );
19371	}
19372
19373	this.on('destroy.btn-info', function () {
19374		that.buttons.info(false);
19375	});
19376
19377	return this;
19378} );
19379
19380// Get data from the table for export - this is common to a number of plug-in
19381// buttons so it is included in the Buttons core library
19382DataTable.Api.register( 'buttons.exportData()', function ( options ) {
19383	if ( this.context.length ) {
19384		return _exportData( new DataTable.Api( this.context[0] ), options );
19385	}
19386} );
19387
19388// Get information about the export that is common to many of the export data
19389// types (DRY)
19390DataTable.Api.register( 'buttons.exportInfo()', function ( conf ) {
19391	if ( ! conf ) {
19392		conf = {};
19393	}
19394
19395	return {
19396		filename: _filename( conf ),
19397		title: _title( conf ),
19398		messageTop: _message(this, conf.message || conf.messageTop, 'top'),
19399		messageBottom: _message(this, conf.messageBottom, 'bottom')
19400	};
19401} );
19402
19403
19404
19405/**
19406 * Get the file name for an exported file.
19407 *
19408 * @param {object}	config Button configuration
19409 * @param {boolean} incExtension Include the file name extension
19410 */
19411var _filename = function ( config )
19412{
19413	// Backwards compatibility
19414	var filename = config.filename === '*' && config.title !== '*' && config.title !== undefined && config.title !== null && config.title !== '' ?
19415		config.title :
19416		config.filename;
19417
19418	if ( typeof filename === 'function' ) {
19419		filename = filename();
19420	}
19421
19422	if ( filename === undefined || filename === null ) {
19423		return null;
19424	}
19425
19426	if ( filename.indexOf( '*' ) !== -1 ) {
19427		filename = filename.replace( '*', $('head > title').text() ).trim();
19428	}
19429
19430	// Strip characters which the OS will object to
19431	filename = filename.replace(/[^a-zA-Z0-9_\u00A1-\uFFFF\.,\-_ !\(\)]/g, "");
19432
19433	var extension = _stringOrFunction( config.extension );
19434	if ( ! extension ) {
19435		extension = '';
19436	}
19437
19438	return filename + extension;
19439};
19440
19441/**
19442 * Simply utility method to allow parameters to be given as a function
19443 *
19444 * @param {undefined|string|function} option Option
19445 * @return {null|string} Resolved value
19446 */
19447var _stringOrFunction = function ( option )
19448{
19449	if ( option === null || option === undefined ) {
19450		return null;
19451	}
19452	else if ( typeof option === 'function' ) {
19453		return option();
19454	}
19455	return option;
19456};
19457
19458/**
19459 * Get the title for an exported file.
19460 *
19461 * @param {object} config	Button configuration
19462 */
19463var _title = function ( config )
19464{
19465	var title = _stringOrFunction( config.title );
19466
19467	return title === null ?
19468		null : title.indexOf( '*' ) !== -1 ?
19469			title.replace( '*', $('head > title').text() || 'Exported data' ) :
19470			title;
19471};
19472
19473var _message = function ( dt, option, position )
19474{
19475	var message = _stringOrFunction( option );
19476	if ( message === null ) {
19477		return null;
19478	}
19479
19480	var caption = $('caption', dt.table().container()).eq(0);
19481	if ( message === '*' ) {
19482		var side = caption.css( 'caption-side' );
19483		if ( side !== position ) {
19484			return null;
19485		}
19486
19487		return caption.length ?
19488			caption.text() :
19489			'';
19490	}
19491
19492	return message;
19493};
19494
19495
19496
19497
19498var _exportTextarea = $('<textarea/>')[0];
19499var _exportData = function ( dt, inOpts )
19500{
19501	var config = $.extend( true, {}, {
19502		rows:           null,
19503		columns:        '',
19504		modifier:       {
19505			search: 'applied',
19506			order:  'applied'
19507		},
19508		orthogonal:     'display',
19509		stripHtml:      true,
19510		stripNewlines:  true,
19511		decodeEntities: true,
19512		trim:           true,
19513		format:         {
19514			header: function ( d ) {
19515				return Buttons.stripData( d, config );
19516			},
19517			footer: function ( d ) {
19518				return Buttons.stripData( d, config );
19519			},
19520			body: function ( d ) {
19521				return Buttons.stripData( d, config );
19522			}
19523		},
19524		customizeData: null
19525	}, inOpts );
19526
19527	var header = dt.columns( config.columns ).indexes().map( function (idx) {
19528		var el = dt.column( idx ).header();
19529		return config.format.header( el.innerHTML, idx, el );
19530	} ).toArray();
19531
19532	var footer = dt.table().footer() ?
19533		dt.columns( config.columns ).indexes().map( function (idx) {
19534			var el = dt.column( idx ).footer();
19535			return config.format.footer( el ? el.innerHTML : '', idx, el );
19536		} ).toArray() :
19537		null;
19538	
19539	// If Select is available on this table, and any rows are selected, limit the export
19540	// to the selected rows. If no rows are selected, all rows will be exported. Specify
19541	// a `selected` modifier to control directly.
19542	var modifier = $.extend( {}, config.modifier );
19543	if ( dt.select && typeof dt.select.info === 'function' && modifier.selected === undefined ) {
19544		if ( dt.rows( config.rows, $.extend( { selected: true }, modifier ) ).any() ) {
19545			$.extend( modifier, { selected: true } )
19546		}
19547	}
19548
19549	var rowIndexes = dt.rows( config.rows, modifier ).indexes().toArray();
19550	var selectedCells = dt.cells( rowIndexes, config.columns );
19551	var cells = selectedCells
19552		.render( config.orthogonal )
19553		.toArray();
19554	var cellNodes = selectedCells
19555		.nodes()
19556		.toArray();
19557
19558	var columns = header.length;
19559	var rows = columns > 0 ? cells.length / columns : 0;
19560	var body = [];
19561	var cellCounter = 0;
19562
19563	for ( var i=0, ien=rows ; i<ien ; i++ ) {
19564		var row = [ columns ];
19565
19566		for ( var j=0 ; j<columns ; j++ ) {
19567			row[j] = config.format.body( cells[ cellCounter ], i, j, cellNodes[ cellCounter ] );
19568			cellCounter++;
19569		}
19570
19571		body[i] = row;
19572	}
19573
19574	var data = {
19575		header: header,
19576		footer: footer,
19577		body:   body
19578	};
19579
19580	if ( config.customizeData ) {
19581		config.customizeData( data );
19582	}
19583
19584	return data;
19585};
19586
19587
19588/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
19589 * DataTables interface
19590 */
19591
19592// Attach to DataTables objects for global access
19593$.fn.dataTable.Buttons = Buttons;
19594$.fn.DataTable.Buttons = Buttons;
19595
19596
19597
19598// DataTables creation - check if the buttons have been defined for this table,
19599// they will have been if the `B` option was used in `dom`, otherwise we should
19600// create the buttons instance here so they can be inserted into the document
19601// using the API. Listen for `init` for compatibility with pre 1.10.10, but to
19602// be removed in future.
19603$(document).on( 'init.dt plugin-init.dt', function (e, settings) {
19604	if ( e.namespace !== 'dt' ) {
19605		return;
19606	}
19607
19608	var opts = settings.oInit.buttons || DataTable.defaults.buttons;
19609
19610	if ( opts && ! settings._buttons ) {
19611		new Buttons( settings, opts ).container();
19612	}
19613} );
19614
19615function _init ( settings, options ) {
19616	var api = new DataTable.Api( settings );
19617	var opts = options
19618		? options
19619		: api.init().buttons || DataTable.defaults.buttons;
19620
19621	return new Buttons( api, opts ).container();
19622}
19623
19624// DataTables `dom` feature option
19625DataTable.ext.feature.push( {
19626	fnInit: _init,
19627	cFeature: "B"
19628} );
19629
19630// DataTables 2 layout feature
19631if ( DataTable.ext.features ) {
19632	DataTable.ext.features.register( 'buttons', _init );
19633}
19634
19635
19636return DataTable;
19637}));
19638
19639
19640/*! DataTables styling wrapper for Buttons
19641 * ©2018 SpryMedia Ltd - datatables.net/license
19642 */
19643
19644(function( factory ){
19645	if ( typeof define === 'function' && define.amd ) {
19646		// AMD
19647		define( ['jquery', 'datatables.net-dt', 'datatables.net-buttons'], function ( $ ) {
19648			return factory( $, window, document );
19649		} );
19650	}
19651	else if ( typeof exports === 'object' ) {
19652		// CommonJS
19653		var jq = require('jquery');
19654		var cjsRequires = function (root, $) {
19655			if ( ! $.fn.dataTable ) {
19656				require('datatables.net-dt')(root, $);
19657			}
19658
19659			if ( ! $.fn.dataTable.Buttons ) {
19660				require('datatables.net-buttons')(root, $);
19661			}
19662		};
19663
19664		if (typeof window !== 'undefined') {
19665			module.exports = function (root, $) {
19666				if ( ! root ) {
19667					// CommonJS environments without a window global must pass a
19668					// root. This will give an error otherwise
19669					root = window;
19670				}
19671
19672				if ( ! $ ) {
19673					$ = jq( root );
19674				}
19675
19676				cjsRequires( root, $ );
19677				return factory( $, root, root.document );
19678			};
19679		}
19680		else {
19681			cjsRequires( window, jq );
19682			module.exports = factory( jq, window, window.document );
19683		}
19684	}
19685	else {
19686		// Browser
19687		factory( jQuery, window, document );
19688	}
19689}(function( $, window, document, undefined ) {
19690'use strict';
19691var DataTable = $.fn.dataTable;
19692
19693
19694
19695(function( factory ){
19696	if ( typeof define === 'function' && define.amd ) {
19697		// AMD
19698		define( ['jquery', 'datatables.net-dt', 'datatables.net-buttons'], function ( $ ) {
19699			return factory( $, window, document );
19700		} );
19701	}
19702	else if ( typeof exports === 'object' ) {
19703		// CommonJS
19704		module.exports = function (root, $) {
19705			if ( ! root ) {
19706				root = window;
19707			}
19708
19709			if ( ! $ || ! $.fn.dataTable ) {
19710				$ = require('datatables.net-dt')(root, $).$;
19711			}
19712
19713			if ( ! $.fn.dataTable.Buttons ) {
19714				require('datatables.net-buttons')(root, $);
19715			}
19716
19717			return factory( $, root, root.document );
19718		};
19719	}
19720	else {
19721		// Browser
19722		factory( jQuery, window, document );
19723	}
19724}(function( $, window, document, undefined ) {
19725
19726return $.fn.dataTable;
19727
19728}));
19729
19730return DataTable;
19731}));
19732
19733
19734/*!
19735 * Column visibility buttons for Buttons and DataTables.
19736 * 2016 SpryMedia Ltd - datatables.net/license
19737 */
19738
19739(function( factory ){
19740	if ( typeof define === 'function' && define.amd ) {
19741		// AMD
19742		define( ['jquery', 'datatables.net', 'datatables.net-buttons'], function ( $ ) {
19743			return factory( $, window, document );
19744		} );
19745	}
19746	else if ( typeof exports === 'object' ) {
19747		// CommonJS
19748		var jq = require('jquery');
19749		var cjsRequires = function (root, $) {
19750			if ( ! $.fn.dataTable ) {
19751				require('datatables.net')(root, $);
19752			}
19753
19754			if ( ! $.fn.dataTable.Buttons ) {
19755				require('datatables.net-buttons')(root, $);
19756			}
19757		};
19758
19759		if (typeof window !== 'undefined') {
19760			module.exports = function (root, $) {
19761				if ( ! root ) {
19762					// CommonJS environments without a window global must pass a
19763					// root. This will give an error otherwise
19764					root = window;
19765				}
19766
19767				if ( ! $ ) {
19768					$ = jq( root );
19769				}
19770
19771				cjsRequires( root, $ );
19772				return factory( $, root, root.document );
19773			};
19774		}
19775		else {
19776			cjsRequires( window, jq );
19777			module.exports = factory( jq, window, window.document );
19778		}
19779	}
19780	else {
19781		// Browser
19782		factory( jQuery, window, document );
19783	}
19784}(function( $, window, document, undefined ) {
19785'use strict';
19786var DataTable = $.fn.dataTable;
19787
19788
19789
19790$.extend( DataTable.ext.buttons, {
19791	// A collection of column visibility buttons
19792	colvis: function ( dt, conf ) {
19793		var node = null;
19794		var buttonConf = {
19795			extend: 'collection',
19796			init: function ( dt, n ) {
19797				node = n;
19798			},
19799			text: function ( dt ) {
19800				return dt.i18n( 'buttons.colvis', 'Column visibility' );
19801			},
19802			className: 'buttons-colvis',
19803			closeButton: false,
19804			buttons: [ {
19805				extend: 'columnsToggle',
19806				columns: conf.columns,
19807				columnText: conf.columnText
19808			} ]
19809		};
19810
19811		// Rebuild the collection with the new column structure if columns are reordered
19812		dt.on( 'column-reorder.dt'+conf.namespace, function (e, settings, details) {
19813			dt.button(null, dt.button(null, node).node()).collectionRebuild([{
19814				extend: 'columnsToggle',
19815				columns: conf.columns,
19816				columnText: conf.columnText
19817			}]);
19818		});
19819
19820		return buttonConf;
19821	},
19822
19823	// Selected columns with individual buttons - toggle column visibility
19824	columnsToggle: function ( dt, conf ) {
19825		var columns = dt.columns( conf.columns ).indexes().map( function ( idx ) {
19826			return {
19827				extend: 'columnToggle',
19828				columns: idx,
19829				columnText: conf.columnText
19830			};
19831		} ).toArray();
19832
19833		return columns;
19834	},
19835
19836	// Single button to toggle column visibility
19837	columnToggle: function ( dt, conf ) {
19838		return {
19839			extend: 'columnVisibility',
19840			columns: conf.columns,
19841			columnText: conf.columnText
19842		};
19843	},
19844
19845	// Selected columns with individual buttons - set column visibility
19846	columnsVisibility: function ( dt, conf ) {
19847		var columns = dt.columns( conf.columns ).indexes().map( function ( idx ) {
19848			return {
19849				extend: 'columnVisibility',
19850				columns: idx,
19851				visibility: conf.visibility,
19852				columnText: conf.columnText
19853			};
19854		} ).toArray();
19855
19856		return columns;
19857	},
19858
19859	// Single button to set column visibility
19860	columnVisibility: {
19861		columns: undefined, // column selector
19862		text: function ( dt, button, conf ) {
19863			return conf._columnText( dt, conf );
19864		},
19865		className: 'buttons-columnVisibility',
19866		action: function ( e, dt, button, conf ) {
19867			var col = dt.columns( conf.columns );
19868			var curr = col.visible();
19869
19870			col.visible( conf.visibility !== undefined ?
19871				conf.visibility :
19872				! (curr.length ? curr[0] : false )
19873			);
19874		},
19875		init: function ( dt, button, conf ) {
19876			var that = this;
19877			button.attr( 'data-cv-idx', conf.columns );
19878
19879			dt
19880				.on( 'column-visibility.dt'+conf.namespace, function (e, settings) {
19881					if ( ! settings.bDestroying && settings.nTable == dt.settings()[0].nTable ) {
19882						that.active( dt.column( conf.columns ).visible() );
19883					}
19884				} )
19885				.on( 'column-reorder.dt'+conf.namespace, function (e, settings, details) {
19886					// Button has been removed from the DOM
19887					if ( conf.destroying ) {
19888						return;
19889					}
19890
19891					if ( dt.columns( conf.columns ).count() !== 1 ) {
19892						return;
19893					}
19894
19895					// This button controls the same column index but the text for the column has
19896					// changed
19897					that.text( conf._columnText( dt, conf ) );
19898
19899					// Since its a different column, we need to check its visibility
19900					that.active( dt.column( conf.columns ).visible() );
19901				} );
19902
19903			this.active( dt.column( conf.columns ).visible() );
19904		},
19905		destroy: function ( dt, button, conf ) {
19906			dt
19907				.off( 'column-visibility.dt'+conf.namespace )
19908				.off( 'column-reorder.dt'+conf.namespace );
19909		},
19910
19911		_columnText: function ( dt, conf ) {
19912			// Use DataTables' internal data structure until this is presented
19913			// is a public API. The other option is to use
19914			// `$( column(col).node() ).text()` but the node might not have been
19915			// populated when Buttons is constructed.
19916			var idx = dt.column( conf.columns ).index();
19917			var title = dt.settings()[0].aoColumns[ idx ].sTitle;
19918
19919			if (! title) {
19920				title = dt.column(idx).header().innerHTML;
19921			}
19922
19923			title = title
19924				.replace(/\n/g," ")        // remove new lines
19925				.replace(/<br\s*\/?>/gi, " ")  // replace line breaks with spaces
19926				.replace(/<select(.*?)<\/select>/g, "") // remove select tags, including options text
19927				.replace(/<!\-\-.*?\-\->/g, "") // strip HTML comments
19928				.replace(/<.*?>/g, "")   // strip HTML
19929				.replace(/^\s+|\s+$/g,""); // trim
19930
19931			return conf.columnText ?
19932				conf.columnText( dt, idx, title ) :
19933				title;
19934		}
19935	},
19936
19937
19938	colvisRestore: {
19939		className: 'buttons-colvisRestore',
19940
19941		text: function ( dt ) {
19942			return dt.i18n( 'buttons.colvisRestore', 'Restore visibility' );
19943		},
19944
19945		init: function ( dt, button, conf ) {
19946			conf._visOriginal = dt.columns().indexes().map( function ( idx ) {
19947				return dt.column( idx ).visible();
19948			} ).toArray();
19949		},
19950
19951		action: function ( e, dt, button, conf ) {
19952			dt.columns().every( function ( i ) {
19953				// Take into account that ColReorder might have disrupted our
19954				// indexes
19955				var idx = dt.colReorder && dt.colReorder.transpose ?
19956					dt.colReorder.transpose( i, 'toOriginal' ) :
19957					i;
19958
19959				this.visible( conf._visOriginal[ idx ] );
19960			} );
19961		}
19962	},
19963
19964
19965	colvisGroup: {
19966		className: 'buttons-colvisGroup',
19967
19968		action: function ( e, dt, button, conf ) {
19969			dt.columns( conf.show ).visible( true, false );
19970			dt.columns( conf.hide ).visible( false, false );
19971
19972			dt.columns.adjust();
19973		},
19974
19975		show: [],
19976
19977		hide: []
19978	}
19979} );
19980
19981
19982return DataTable;
19983}));
19984
19985
19986/*!
19987 * HTML5 export buttons for Buttons and DataTables.
19988 * 2016 SpryMedia Ltd - datatables.net/license
19989 *
19990 * FileSaver.js (1.3.3) - MIT license
19991 * Copyright © 2016 Eli Grey - http://eligrey.com
19992 */
19993
19994(function( factory ){
19995	if ( typeof define === 'function' && define.amd ) {
19996		// AMD
19997		define( ['jquery', 'datatables.net', 'datatables.net-buttons'], function ( $ ) {
19998			return factory( $, window, document );
19999		} );
20000	}
20001	else if ( typeof exports === 'object' ) {
20002		// CommonJS
20003		var jq = require('jquery');
20004		var cjsRequires = function (root, $) {
20005			if ( ! $.fn.dataTable ) {
20006				require('datatables.net')(root, $);
20007			}
20008
20009			if ( ! $.fn.dataTable.Buttons ) {
20010				require('datatables.net-buttons')(root, $);
20011			}
20012		};
20013
20014		if (typeof window !== 'undefined') {
20015			module.exports = function (root, $, jszip, pdfmake) {
20016				if ( ! root ) {
20017					// CommonJS environments without a window global must pass a
20018					// root. This will give an error otherwise
20019					root = window;
20020				}
20021
20022				if ( ! $ ) {
20023					$ = jq( root );
20024				}
20025
20026				cjsRequires( root, $ );
20027				return factory( $, root, root.document, jszip, pdfmake );
20028			};
20029		}
20030		else {
20031			cjsRequires( window, jq );
20032			module.exports = factory( jq, window, window.document );
20033		}
20034	}
20035	else {
20036		// Browser
20037		factory( jQuery, window, document );
20038	}
20039}(function( $, window, document, jszip, pdfmake, undefined ) {
20040'use strict';
20041var DataTable = $.fn.dataTable;
20042
20043
20044
20045// Allow the constructor to pass in JSZip and PDFMake from external requires.
20046// Otherwise, use globally defined variables, if they are available.
20047var useJszip;
20048var usePdfmake;
20049
20050function _jsZip () {
20051	return useJszip || window.JSZip;
20052}
20053function _pdfMake () {
20054	return usePdfmake || window.pdfMake;
20055}
20056
20057DataTable.Buttons.pdfMake = function (_) {
20058	if ( ! _ ) {
20059		return _pdfMake();
20060	}
20061	usePdfmake = _;
20062}
20063
20064DataTable.Buttons.jszip = function (_) {
20065	if ( ! _ ) {
20066		return _jsZip();
20067	}
20068	useJszip = _;
20069}
20070
20071
20072/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
20073 * FileSaver.js dependency
20074 */
20075
20076/*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */
20077
20078var _saveAs = (function(view) {
20079	"use strict";
20080	// IE <10 is explicitly unsupported
20081	if (typeof view === "undefined" || typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) {
20082		return;
20083	}
20084	var
20085		  doc = view.document
20086		  // only get URL when necessary in case Blob.js hasn't overridden it yet
20087		, get_URL = function() {
20088			return view.URL || view.webkitURL || view;
20089		}
20090		, save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a")
20091		, can_use_save_link = "download" in save_link
20092		, click = function(node) {
20093			var event = new MouseEvent("click");
20094			node.dispatchEvent(event);
20095		}
20096		, is_safari = /constructor/i.test(view.HTMLElement) || view.safari
20097		, is_chrome_ios =/CriOS\/[\d]+/.test(navigator.userAgent)
20098		, throw_outside = function(ex) {
20099			(view.setImmediate || view.setTimeout)(function() {
20100				throw ex;
20101			}, 0);
20102		}
20103		, force_saveable_type = "application/octet-stream"
20104		// the Blob API is fundamentally broken as there is no "downloadfinished" event to subscribe to
20105		, arbitrary_revoke_timeout = 1000 * 40 // in ms
20106		, revoke = function(file) {
20107			var revoker = function() {
20108				if (typeof file === "string") { // file is an object URL
20109					get_URL().revokeObjectURL(file);
20110				} else { // file is a File
20111					file.remove();
20112				}
20113			};
20114			setTimeout(revoker, arbitrary_revoke_timeout);
20115		}
20116		, dispatch = function(filesaver, event_types, event) {
20117			event_types = [].concat(event_types);
20118			var i = event_types.length;
20119			while (i--) {
20120				var listener = filesaver["on" + event_types[i]];
20121				if (typeof listener === "function") {
20122					try {
20123						listener.call(filesaver, event || filesaver);
20124					} catch (ex) {
20125						throw_outside(ex);
20126					}
20127				}
20128			}
20129		}
20130		, auto_bom = function(blob) {
20131			// prepend BOM for UTF-8 XML and text/* types (including HTML)
20132			// note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
20133			if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
20134				return new Blob([String.fromCharCode(0xFEFF), blob], {type: blob.type});
20135			}
20136			return blob;
20137		}
20138		, FileSaver = function(blob, name, no_auto_bom) {
20139			if (!no_auto_bom) {
20140				blob = auto_bom(blob);
20141			}
20142			// First try a.download, then web filesystem, then object URLs
20143			var
20144				  filesaver = this
20145				, type = blob.type
20146				, force = type === force_saveable_type
20147				, object_url
20148				, dispatch_all = function() {
20149					dispatch(filesaver, "writestart progress write writeend".split(" "));
20150				}
20151				// on any filesys errors revert to saving with object URLs
20152				, fs_error = function() {
20153					if ((is_chrome_ios || (force && is_safari)) && view.FileReader) {
20154						// Safari doesn't allow downloading of blob urls
20155						var reader = new FileReader();
20156						reader.onloadend = function() {
20157							var url = is_chrome_ios ? reader.result : reader.result.replace(/^data:[^;]*;/, 'data:attachment/file;');
20158							var popup = view.open(url, '_blank');
20159							if(!popup) view.location.href = url;
20160							url=undefined; // release reference before dispatching
20161							filesaver.readyState = filesaver.DONE;
20162							dispatch_all();
20163						};
20164						reader.readAsDataURL(blob);
20165						filesaver.readyState = filesaver.INIT;
20166						return;
20167					}
20168					// don't create more object URLs than needed
20169					if (!object_url) {
20170						object_url = get_URL().createObjectURL(blob);
20171					}
20172					if (force) {
20173						view.location.href = object_url;
20174					} else {
20175						var opened = view.open(object_url, "_blank");
20176						if (!opened) {
20177							// Apple does not allow window.open, see https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/WorkingwithWindowsandTabs/WorkingwithWindowsandTabs.html
20178							view.location.href = object_url;
20179						}
20180					}
20181					filesaver.readyState = filesaver.DONE;
20182					dispatch_all();
20183					revoke(object_url);
20184				}
20185			;
20186			filesaver.readyState = filesaver.INIT;
20187
20188			if (can_use_save_link) {
20189				object_url = get_URL().createObjectURL(blob);
20190				setTimeout(function() {
20191					save_link.href = object_url;
20192					save_link.download = name;
20193					click(save_link);
20194					dispatch_all();
20195					revoke(object_url);
20196					filesaver.readyState = filesaver.DONE;
20197				});
20198				return;
20199			}
20200
20201			fs_error();
20202		}
20203		, FS_proto = FileSaver.prototype
20204		, saveAs = function(blob, name, no_auto_bom) {
20205			return new FileSaver(blob, name || blob.name || "download", no_auto_bom);
20206		}
20207	;
20208	// IE 10+ (native saveAs)
20209	if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) {
20210		return function(blob, name, no_auto_bom) {
20211			name = name || blob.name || "download";
20212
20213			if (!no_auto_bom) {
20214				blob = auto_bom(blob);
20215			}
20216			return navigator.msSaveOrOpenBlob(blob, name);
20217		};
20218	}
20219
20220	FS_proto.abort = function(){};
20221	FS_proto.readyState = FS_proto.INIT = 0;
20222	FS_proto.WRITING = 1;
20223	FS_proto.DONE = 2;
20224
20225	FS_proto.error =
20226	FS_proto.onwritestart =
20227	FS_proto.onprogress =
20228	FS_proto.onwrite =
20229	FS_proto.onabort =
20230	FS_proto.onerror =
20231	FS_proto.onwriteend =
20232		null;
20233
20234	return saveAs;
20235}(
20236	   typeof self !== "undefined" && self
20237	|| typeof window !== "undefined" && window
20238	|| this.content
20239));
20240
20241
20242// Expose file saver on the DataTables API. Can't attach to `DataTables.Buttons`
20243// since this file can be loaded before Button's core!
20244DataTable.fileSave = _saveAs;
20245
20246
20247/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
20248 * Local (private) functions
20249 */
20250
20251/**
20252 * Get the sheet name for Excel exports.
20253 *
20254 * @param {object}	config Button configuration
20255 */
20256var _sheetname = function ( config )
20257{
20258	var sheetName = 'Sheet1';
20259
20260	if ( config.sheetName ) {
20261		sheetName = config.sheetName.replace(/[\[\]\*\/\\\?\:]/g, '');
20262	}
20263
20264	return sheetName;
20265};
20266
20267/**
20268 * Get the newline character(s)
20269 *
20270 * @param {object}	config Button configuration
20271 * @return {string}				Newline character
20272 */
20273var _newLine = function ( config )
20274{
20275	return config.newline ?
20276		config.newline :
20277		navigator.userAgent.match(/Windows/) ?
20278			'\r\n' :
20279			'\n';
20280};
20281
20282/**
20283 * Combine the data from the `buttons.exportData` method into a string that
20284 * will be used in the export file.
20285 *
20286 * @param	{DataTable.Api} dt		 DataTables API instance
20287 * @param	{object}				config Button configuration
20288 * @return {object}							 The data to export
20289 */
20290var _exportData = function ( dt, config )
20291{
20292	var newLine = _newLine( config );
20293	var data = dt.buttons.exportData( config.exportOptions );
20294	var boundary = config.fieldBoundary;
20295	var separator = config.fieldSeparator;
20296	var reBoundary = new RegExp( boundary, 'g' );
20297	var escapeChar = config.escapeChar !== undefined ?
20298		config.escapeChar :
20299		'\\';
20300	var join = function ( a ) {
20301		var s = '';
20302
20303		// If there is a field boundary, then we might need to escape it in
20304		// the source data
20305		for ( var i=0, ien=a.length ; i<ien ; i++ ) {
20306			if ( i > 0 ) {
20307				s += separator;
20308			}
20309
20310			s += boundary ?
20311				boundary + ('' + a[i]).replace( reBoundary, escapeChar+boundary ) + boundary :
20312				a[i];
20313		}
20314
20315		return s;
20316	};
20317
20318	var header = config.header ? join( data.header )+newLine : '';
20319	var footer = config.footer && data.footer ? newLine+join( data.footer ) : '';
20320	var body = [];
20321
20322	for ( var i=0, ien=data.body.length ; i<ien ; i++ ) {
20323		body.push( join( data.body[i] ) );
20324	}
20325
20326	return {
20327		str: header + body.join( newLine ) + footer,
20328		rows: body.length
20329	};
20330};
20331
20332/**
20333 * Older versions of Safari (prior to tech preview 18) don't support the
20334 * download option required.
20335 *
20336 * @return {Boolean} `true` if old Safari
20337 */
20338var _isDuffSafari = function ()
20339{
20340	var safari = navigator.userAgent.indexOf('Safari') !== -1 &&
20341		navigator.userAgent.indexOf('Chrome') === -1 &&
20342		navigator.userAgent.indexOf('Opera') === -1;
20343
20344	if ( ! safari ) {
20345		return false;
20346	}
20347
20348	var version = navigator.userAgent.match( /AppleWebKit\/(\d+\.\d+)/ );
20349	if ( version && version.length > 1 && version[1]*1 < 603.1 ) {
20350		return true;
20351	}
20352
20353	return false;
20354};
20355
20356/**
20357 * Convert from numeric position to letter for column names in Excel
20358 * @param  {int} n Column number
20359 * @return {string} Column letter(s) name
20360 */
20361function createCellPos( n ){
20362	var ordA = 'A'.charCodeAt(0);
20363	var ordZ = 'Z'.charCodeAt(0);
20364	var len = ordZ - ordA + 1;
20365	var s = "";
20366
20367	while( n >= 0 ) {
20368		s = String.fromCharCode(n % len + ordA) + s;
20369		n = Math.floor(n / len) - 1;
20370	}
20371
20372	return s;
20373}
20374
20375try {
20376	var _serialiser = new XMLSerializer();
20377	var _ieExcel;
20378}
20379catch (t) {}
20380
20381/**
20382 * Recursively add XML files from an object's structure to a ZIP file. This
20383 * allows the XSLX file to be easily defined with an object's structure matching
20384 * the files structure.
20385 *
20386 * @param {JSZip} zip ZIP package
20387 * @param {object} obj Object to add (recursive)
20388 */
20389function _addToZip( zip, obj ) {
20390	if ( _ieExcel === undefined ) {
20391		// Detect if we are dealing with IE's _awful_ serialiser by seeing if it
20392		// drop attributes
20393		_ieExcel = _serialiser
20394			.serializeToString(
20395				( new window.DOMParser() ).parseFromString( excelStrings['xl/worksheets/sheet1.xml'], 'text/xml' )
20396			)
20397			.indexOf( 'xmlns:r' ) === -1;
20398	}
20399
20400	$.each( obj, function ( name, val ) {
20401		if ( $.isPlainObject( val ) ) {
20402			var newDir = zip.folder( name );
20403			_addToZip( newDir, val );
20404		}
20405		else {
20406			if ( _ieExcel ) {
20407				// IE's XML serialiser will drop some name space attributes from
20408				// from the root node, so we need to save them. Do this by
20409				// replacing the namespace nodes with a regular attribute that
20410				// we convert back when serialised. Edge does not have this
20411				// issue
20412				var worksheet = val.childNodes[0];
20413				var i, ien;
20414				var attrs = [];
20415
20416				for ( i=worksheet.attributes.length-1 ; i>=0 ; i-- ) {
20417					var attrName = worksheet.attributes[i].nodeName;
20418					var attrValue = worksheet.attributes[i].nodeValue;
20419
20420					if ( attrName.indexOf( ':' ) !== -1 ) {
20421						attrs.push( { name: attrName, value: attrValue } );
20422
20423						worksheet.removeAttribute( attrName );
20424					}
20425				}
20426
20427				for ( i=0, ien=attrs.length ; i<ien ; i++ ) {
20428					var attr = val.createAttribute( attrs[i].name.replace( ':', '_dt_b_namespace_token_' ) );
20429					attr.value = attrs[i].value;
20430					worksheet.setAttributeNode( attr );
20431				}
20432			}
20433
20434			var str = _serialiser.serializeToString(val);
20435
20436			// Fix IE's XML
20437			if ( _ieExcel ) {
20438				// IE doesn't include the XML declaration
20439				if ( str.indexOf( '<?xml' ) === -1 ) {
20440					str = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'+str;
20441				}
20442
20443				// Return namespace attributes to being as such
20444				str = str.replace( /_dt_b_namespace_token_/g, ':' );
20445
20446				// Remove testing name space that IE puts into the space preserve attr
20447				str = str.replace( /xmlns:NS[\d]+="" NS[\d]+:/g, '' );
20448			}
20449
20450			// Safari, IE and Edge will put empty name space attributes onto
20451			// various elements making them useless. This strips them out
20452			str = str.replace( /<([^<>]*?) xmlns=""([^<>]*?)>/g, '<$1 $2>' );
20453
20454			zip.file( name, str );
20455		}
20456	} );
20457}
20458
20459/**
20460 * Create an XML node and add any children, attributes, etc without needing to
20461 * be verbose in the DOM.
20462 *
20463 * @param  {object} doc      XML document
20464 * @param  {string} nodeName Node name
20465 * @param  {object} opts     Options - can be `attr` (attributes), `children`
20466 *   (child nodes) and `text` (text content)
20467 * @return {node}            Created node
20468 */
20469function _createNode( doc, nodeName, opts ) {
20470	var tempNode = doc.createElement( nodeName );
20471
20472	if ( opts ) {
20473		if ( opts.attr ) {
20474			$(tempNode).attr( opts.attr );
20475		}
20476
20477		if ( opts.children ) {
20478			$.each( opts.children, function ( key, value ) {
20479				tempNode.appendChild( value );
20480			} );
20481		}
20482
20483		if ( opts.text !== null && opts.text !== undefined ) {
20484			tempNode.appendChild( doc.createTextNode( opts.text ) );
20485		}
20486	}
20487
20488	return tempNode;
20489}
20490
20491/**
20492 * Get the width for an Excel column based on the contents of that column
20493 * @param  {object} data Data for export
20494 * @param  {int}    col  Column index
20495 * @return {int}         Column width
20496 */
20497function _excelColWidth( data, col ) {
20498	var max = data.header[col].length;
20499	var len, lineSplit, str;
20500
20501	if ( data.footer && data.footer[col].length > max ) {
20502		max = data.footer[col].length;
20503	}
20504
20505	for ( var i=0, ien=data.body.length ; i<ien ; i++ ) {
20506		var point = data.body[i][col];
20507		str = point !== null && point !== undefined ?
20508			point.toString() :
20509			'';
20510
20511		// If there is a newline character, workout the width of the column
20512		// based on the longest line in the string
20513		if ( str.indexOf('\n') !== -1 ) {
20514			lineSplit = str.split('\n');
20515			lineSplit.sort( function (a, b) {
20516				return b.length - a.length;
20517			} );
20518
20519			len = lineSplit[0].length;
20520		}
20521		else {
20522			len = str.length;
20523		}
20524
20525		if ( len > max ) {
20526			max = len;
20527		}
20528
20529		// Max width rather than having potentially massive column widths
20530		if ( max > 40 ) {
20531			return 54; // 40 * 1.35
20532		}
20533	}
20534
20535	max *= 1.35;
20536
20537	// And a min width
20538	return max > 6 ? max : 6;
20539}
20540
20541// Excel - Pre-defined strings to build a basic XLSX file
20542var excelStrings = {
20543	"_rels/.rels":
20544		'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'+
20545		'<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'+
20546			'<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>'+
20547		'</Relationships>',
20548
20549	"xl/_rels/workbook.xml.rels":
20550		'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'+
20551		'<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'+
20552			'<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/>'+
20553			'<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>'+
20554		'</Relationships>',
20555
20556	"[Content_Types].xml":
20557		'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'+
20558		'<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">'+
20559			'<Default Extension="xml" ContentType="application/xml" />'+
20560			'<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml" />'+
20561			'<Default Extension="jpeg" ContentType="image/jpeg" />'+
20562			'<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml" />'+
20563			'<Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml" />'+
20564			'<Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml" />'+
20565		'</Types>',
20566
20567	"xl/workbook.xml":
20568		'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'+
20569		'<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">'+
20570			'<fileVersion appName="xl" lastEdited="5" lowestEdited="5" rupBuild="24816"/>'+
20571			'<workbookPr showInkAnnotation="0" autoCompressPictures="0"/>'+
20572			'<bookViews>'+
20573				'<workbookView xWindow="0" yWindow="0" windowWidth="25600" windowHeight="19020" tabRatio="500"/>'+
20574			'</bookViews>'+
20575			'<sheets>'+
20576				'<sheet name="Sheet1" sheetId="1" r:id="rId1"/>'+
20577			'</sheets>'+
20578			'<definedNames/>'+
20579		'</workbook>',
20580
20581	"xl/worksheets/sheet1.xml":
20582		'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'+
20583		'<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac">'+
20584			'<sheetData/>'+
20585			'<mergeCells count="0"/>'+
20586		'</worksheet>',
20587
20588	"xl/styles.xml":
20589		'<?xml version="1.0" encoding="UTF-8"?>'+
20590		'<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac">'+
20591			'<numFmts count="6">'+
20592				'<numFmt numFmtId="164" formatCode="#,##0.00_-\ [$$-45C]"/>'+
20593				'<numFmt numFmtId="165" formatCode="&quot;£&quot;#,##0.00"/>'+
20594				'<numFmt numFmtId="166" formatCode="[$€-2]\ #,##0.00"/>'+
20595				'<numFmt numFmtId="167" formatCode="0.0%"/>'+
20596				'<numFmt numFmtId="168" formatCode="#,##0;(#,##0)"/>'+
20597				'<numFmt numFmtId="169" formatCode="#,##0.00;(#,##0.00)"/>'+
20598			'</numFmts>'+
20599			'<fonts count="5" x14ac:knownFonts="1">'+
20600				'<font>'+
20601					'<sz val="11" />'+
20602					'<name val="Calibri" />'+
20603				'</font>'+
20604				'<font>'+
20605					'<sz val="11" />'+
20606					'<name val="Calibri" />'+
20607					'<color rgb="FFFFFFFF" />'+
20608				'</font>'+
20609				'<font>'+
20610					'<sz val="11" />'+
20611					'<name val="Calibri" />'+
20612					'<b />'+
20613				'</font>'+
20614				'<font>'+
20615					'<sz val="11" />'+
20616					'<name val="Calibri" />'+
20617					'<i />'+
20618				'</font>'+
20619				'<font>'+
20620					'<sz val="11" />'+
20621					'<name val="Calibri" />'+
20622					'<u />'+
20623				'</font>'+
20624			'</fonts>'+
20625			'<fills count="6">'+
20626				'<fill>'+
20627					'<patternFill patternType="none" />'+
20628				'</fill>'+
20629				'<fill>'+ // Excel appears to use this as a dotted background regardless of values but
20630					'<patternFill patternType="none" />'+ // to be valid to the schema, use a patternFill
20631				'</fill>'+
20632				'<fill>'+
20633					'<patternFill patternType="solid">'+
20634						'<fgColor rgb="FFD9D9D9" />'+
20635						'<bgColor indexed="64" />'+
20636					'</patternFill>'+
20637				'</fill>'+
20638				'<fill>'+
20639					'<patternFill patternType="solid">'+
20640						'<fgColor rgb="FFD99795" />'+
20641						'<bgColor indexed="64" />'+
20642					'</patternFill>'+
20643				'</fill>'+
20644				'<fill>'+
20645					'<patternFill patternType="solid">'+
20646						'<fgColor rgb="ffc6efce" />'+
20647						'<bgColor indexed="64" />'+
20648					'</patternFill>'+
20649				'</fill>'+
20650				'<fill>'+
20651					'<patternFill patternType="solid">'+
20652						'<fgColor rgb="ffc6cfef" />'+
20653						'<bgColor indexed="64" />'+
20654					'</patternFill>'+
20655				'</fill>'+
20656			'</fills>'+
20657			'<borders count="2">'+
20658				'<border>'+
20659					'<left />'+
20660					'<right />'+
20661					'<top />'+
20662					'<bottom />'+
20663					'<diagonal />'+
20664				'</border>'+
20665				'<border diagonalUp="false" diagonalDown="false">'+
20666					'<left style="thin">'+
20667						'<color auto="1" />'+
20668					'</left>'+
20669					'<right style="thin">'+
20670						'<color auto="1" />'+
20671					'</right>'+
20672					'<top style="thin">'+
20673						'<color auto="1" />'+
20674					'</top>'+
20675					'<bottom style="thin">'+
20676						'<color auto="1" />'+
20677					'</bottom>'+
20678					'<diagonal />'+
20679				'</border>'+
20680			'</borders>'+
20681			'<cellStyleXfs count="1">'+
20682				'<xf numFmtId="0" fontId="0" fillId="0" borderId="0" />'+
20683			'</cellStyleXfs>'+
20684			'<cellXfs count="68">'+
20685				'<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
20686				'<xf numFmtId="0" fontId="1" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
20687				'<xf numFmtId="0" fontId="2" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
20688				'<xf numFmtId="0" fontId="3" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
20689				'<xf numFmtId="0" fontId="4" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
20690				'<xf numFmtId="0" fontId="0" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
20691				'<xf numFmtId="0" fontId="1" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
20692				'<xf numFmtId="0" fontId="2" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
20693				'<xf numFmtId="0" fontId="3" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
20694				'<xf numFmtId="0" fontId="4" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
20695				'<xf numFmtId="0" fontId="0" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
20696				'<xf numFmtId="0" fontId="1" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
20697				'<xf numFmtId="0" fontId="2" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
20698				'<xf numFmtId="0" fontId="3" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
20699				'<xf numFmtId="0" fontId="4" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
20700				'<xf numFmtId="0" fontId="0" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
20701				'<xf numFmtId="0" fontId="1" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
20702				'<xf numFmtId="0" fontId="2" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
20703				'<xf numFmtId="0" fontId="3" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
20704				'<xf numFmtId="0" fontId="4" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
20705				'<xf numFmtId="0" fontId="0" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
20706				'<xf numFmtId="0" fontId="1" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
20707				'<xf numFmtId="0" fontId="2" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
20708				'<xf numFmtId="0" fontId="3" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
20709				'<xf numFmtId="0" fontId="4" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
20710				'<xf numFmtId="0" fontId="0" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
20711				'<xf numFmtId="0" fontId="1" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
20712				'<xf numFmtId="0" fontId="2" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
20713				'<xf numFmtId="0" fontId="3" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
20714				'<xf numFmtId="0" fontId="4" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
20715				'<xf numFmtId="0" fontId="0" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
20716				'<xf numFmtId="0" fontId="1" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
20717				'<xf numFmtId="0" fontId="2" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
20718				'<xf numFmtId="0" fontId="3" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
20719				'<xf numFmtId="0" fontId="4" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
20720				'<xf numFmtId="0" fontId="0" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
20721				'<xf numFmtId="0" fontId="1" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
20722				'<xf numFmtId="0" fontId="2" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
20723				'<xf numFmtId="0" fontId="3" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
20724				'<xf numFmtId="0" fontId="4" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
20725				'<xf numFmtId="0" fontId="0" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
20726				'<xf numFmtId="0" fontId="1" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
20727				'<xf numFmtId="0" fontId="2" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
20728				'<xf numFmtId="0" fontId="3" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
20729				'<xf numFmtId="0" fontId="4" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
20730				'<xf numFmtId="0" fontId="0" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
20731				'<xf numFmtId="0" fontId="1" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
20732				'<xf numFmtId="0" fontId="2" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
20733				'<xf numFmtId="0" fontId="3" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
20734				'<xf numFmtId="0" fontId="4" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
20735				'<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">'+
20736					'<alignment horizontal="left"/>'+
20737				'</xf>'+
20738				'<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">'+
20739					'<alignment horizontal="center"/>'+
20740				'</xf>'+
20741				'<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">'+
20742					'<alignment horizontal="right"/>'+
20743				'</xf>'+
20744				'<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">'+
20745					'<alignment horizontal="fill"/>'+
20746				'</xf>'+
20747				'<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">'+
20748					'<alignment textRotation="90"/>'+
20749				'</xf>'+
20750				'<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">'+
20751					'<alignment wrapText="1"/>'+
20752				'</xf>'+
20753				'<xf numFmtId="9"   fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
20754				'<xf numFmtId="164" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
20755				'<xf numFmtId="165" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
20756				'<xf numFmtId="166" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
20757				'<xf numFmtId="167" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
20758				'<xf numFmtId="168" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
20759				'<xf numFmtId="169" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
20760				'<xf numFmtId="3" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
20761				'<xf numFmtId="4" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
20762				'<xf numFmtId="1" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
20763				'<xf numFmtId="2" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
20764				'<xf numFmtId="14" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
20765			'</cellXfs>'+
20766			'<cellStyles count="1">'+
20767				'<cellStyle name="Normal" xfId="0" builtinId="0" />'+
20768			'</cellStyles>'+
20769			'<dxfs count="0" />'+
20770			'<tableStyles count="0" defaultTableStyle="TableStyleMedium9" defaultPivotStyle="PivotStyleMedium4" />'+
20771		'</styleSheet>'
20772};
20773// Note we could use 3 `for` loops for the styles, but when gzipped there is
20774// virtually no difference in size, since the above can be easily compressed
20775
20776// Pattern matching for special number formats. Perhaps this should be exposed
20777// via an API in future?
20778// Ref: section 3.8.30 - built in formatters in open spreadsheet
20779//   https://www.ecma-international.org/news/TC45_current_work/Office%20Open%20XML%20Part%204%20-%20Markup%20Language%20Reference.pdf
20780var _excelSpecials = [
20781	{ match: /^\-?\d+\.\d%$/,               style: 60, fmt: function (d) { return d/100; } }, // Percent with d.p.
20782	{ match: /^\-?\d+\.?\d*%$/,             style: 56, fmt: function (d) { return d/100; } }, // Percent
20783	{ match: /^\-?\$[\d,]+.?\d*$/,          style: 57 }, // Dollars
20784	{ match: /^\-?£[\d,]+.?\d*$/,           style: 58 }, // Pounds
20785	{ match: /^\-?€[\d,]+.?\d*$/,           style: 59 }, // Euros
20786	{ match: /^\-?\d+$/,                    style: 65 }, // Numbers without thousand separators
20787	{ match: /^\-?\d+\.\d{2}$/,             style: 66 }, // Numbers 2 d.p. without thousands separators
20788	{ match: /^\([\d,]+\)$/,                style: 61, fmt: function (d) { return -1 * d.replace(/[\(\)]/g, ''); } },  // Negative numbers indicated by brackets
20789	{ match: /^\([\d,]+\.\d{2}\)$/,         style: 62, fmt: function (d) { return -1 * d.replace(/[\(\)]/g, ''); } },  // Negative numbers indicated by brackets - 2d.p.
20790	{ match: /^\-?[\d,]+$/,                 style: 63 }, // Numbers with thousand separators
20791	{ match: /^\-?[\d,]+\.\d{2}$/,          style: 64 },
20792	{ match: /^[\d]{4}\-[01][\d]\-[0123][\d]$/, style: 67, fmt: function (d) {return Math.round(25569 + (Date.parse(d) / (86400 * 1000)));}} //Date yyyy-mm-dd
20793];
20794
20795
20796
20797/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
20798 * Buttons
20799 */
20800
20801//
20802// Copy to clipboard
20803//
20804DataTable.ext.buttons.copyHtml5 = {
20805	className: 'buttons-copy buttons-html5',
20806
20807	text: function ( dt ) {
20808		return dt.i18n( 'buttons.copy', 'Copy' );
20809	},
20810
20811	action: function ( e, dt, button, config ) {
20812		this.processing( true );
20813
20814		var that = this;
20815		var exportData = _exportData( dt, config );
20816		var info = dt.buttons.exportInfo( config );
20817		var newline = _newLine(config);
20818		var output = exportData.str;
20819		var hiddenDiv = $('<div/>')
20820			.css( {
20821				height: 1,
20822				width: 1,
20823				overflow: 'hidden',
20824				position: 'fixed',
20825				top: 0,
20826				left: 0
20827			} );
20828
20829		if ( info.title ) {
20830			output = info.title + newline + newline + output;
20831		}
20832
20833		if ( info.messageTop ) {
20834			output = info.messageTop + newline + newline + output;
20835		}
20836
20837		if ( info.messageBottom ) {
20838			output = output + newline + newline + info.messageBottom;
20839		}
20840
20841		if ( config.customize ) {
20842			output = config.customize( output, config, dt );
20843		}
20844
20845		var textarea = $('<textarea readonly/>')
20846			.val( output )
20847			.appendTo( hiddenDiv );
20848
20849		// For browsers that support the copy execCommand, try to use it
20850		if ( document.queryCommandSupported('copy') ) {
20851			hiddenDiv.appendTo( dt.table().container() );
20852			textarea[0].focus();
20853			textarea[0].select();
20854
20855			try {
20856				var successful = document.execCommand( 'copy' );
20857				hiddenDiv.remove();
20858
20859				if (successful) {
20860					dt.buttons.info(
20861						dt.i18n( 'buttons.copyTitle', 'Copy to clipboard' ),
20862						dt.i18n( 'buttons.copySuccess', {
20863							1: 'Copied one row to clipboard',
20864							_: 'Copied %d rows to clipboard'
20865						}, exportData.rows ),
20866						2000
20867					);
20868
20869					this.processing( false );
20870					return;
20871				}
20872			}
20873			catch (t) {}
20874		}
20875
20876		// Otherwise we show the text box and instruct the user to use it
20877		var message = $('<span>'+dt.i18n( 'buttons.copyKeys',
20878				'Press <i>ctrl</i> or <i>\u2318</i> + <i>C</i> to copy the table data<br>to your system clipboard.<br><br>'+
20879				'To cancel, click this message or press escape.' )+'</span>'
20880			)
20881			.append( hiddenDiv );
20882
20883		dt.buttons.info( dt.i18n( 'buttons.copyTitle', 'Copy to clipboard' ), message, 0 );
20884
20885		// Select the text so when the user activates their system clipboard
20886		// it will copy that text
20887		textarea[0].focus();
20888		textarea[0].select();
20889
20890		// Event to hide the message when the user is done
20891		var container = $(message).closest('.dt-button-info');
20892		var close = function () {
20893			container.off( 'click.buttons-copy' );
20894			$(document).off( '.buttons-copy' );
20895			dt.buttons.info( false );
20896		};
20897
20898		container.on( 'click.buttons-copy', close );
20899		$(document)
20900			.on( 'keydown.buttons-copy', function (e) {
20901				if ( e.keyCode === 27 ) { // esc
20902					close();
20903					that.processing( false );
20904				}
20905			} )
20906			.on( 'copy.buttons-copy cut.buttons-copy', function () {
20907				close();
20908				that.processing( false );
20909			} );
20910	},
20911
20912	exportOptions: {},
20913
20914	fieldSeparator: '\t',
20915
20916	fieldBoundary: '',
20917
20918	header: true,
20919
20920	footer: false,
20921
20922	title: '*',
20923
20924	messageTop: '*',
20925
20926	messageBottom: '*'
20927};
20928
20929//
20930// CSV export
20931//
20932DataTable.ext.buttons.csvHtml5 = {
20933	bom: false,
20934
20935	className: 'buttons-csv buttons-html5',
20936
20937	available: function () {
20938		return window.FileReader !== undefined && window.Blob;
20939	},
20940
20941	text: function ( dt ) {
20942		return dt.i18n( 'buttons.csv', 'CSV' );
20943	},
20944
20945	action: function ( e, dt, button, config ) {
20946		this.processing( true );
20947
20948		// Set the text
20949		var output = _exportData( dt, config ).str;
20950		var info = dt.buttons.exportInfo(config);
20951		var charset = config.charset;
20952
20953		if ( config.customize ) {
20954			output = config.customize( output, config, dt );
20955		}
20956
20957		if ( charset !== false ) {
20958			if ( ! charset ) {
20959				charset = document.characterSet || document.charset;
20960			}
20961
20962			if ( charset ) {
20963				charset = ';charset='+charset;
20964			}
20965		}
20966		else {
20967			charset = '';
20968		}
20969
20970		if ( config.bom ) {
20971			output = String.fromCharCode(0xFEFF) + output;
20972		}
20973
20974		_saveAs(
20975			new Blob( [output], {type: 'text/csv'+charset} ),
20976			info.filename,
20977			true
20978		);
20979
20980		this.processing( false );
20981	},
20982
20983	filename: '*',
20984
20985	extension: '.csv',
20986
20987	exportOptions: {},
20988
20989	fieldSeparator: ',',
20990
20991	fieldBoundary: '"',
20992
20993	escapeChar: '"',
20994
20995	charset: null,
20996
20997	header: true,
20998
20999	footer: false
21000};
21001
21002//
21003// Excel (xlsx) export
21004//
21005DataTable.ext.buttons.excelHtml5 = {
21006	className: 'buttons-excel buttons-html5',
21007
21008	available: function () {
21009		return window.FileReader !== undefined && _jsZip() !== undefined && ! _isDuffSafari() && _serialiser;
21010	},
21011
21012	text: function ( dt ) {
21013		return dt.i18n( 'buttons.excel', 'Excel' );
21014	},
21015
21016	action: function ( e, dt, button, config ) {
21017		this.processing( true );
21018
21019		var that = this;
21020		var rowPos = 0;
21021		var dataStartRow, dataEndRow;
21022		var getXml = function ( type ) {
21023			var str = excelStrings[ type ];
21024
21025			//str = str.replace( /xmlns:/g, 'xmlns_' ).replace( /mc:/g, 'mc_' );
21026
21027			return $.parseXML( str );
21028		};
21029		var rels = getXml('xl/worksheets/sheet1.xml');
21030		var relsGet = rels.getElementsByTagName( "sheetData" )[0];
21031
21032		var xlsx = {
21033			_rels: {
21034				".rels": getXml('_rels/.rels')
21035			},
21036			xl: {
21037				_rels: {
21038					"workbook.xml.rels": getXml('xl/_rels/workbook.xml.rels')
21039				},
21040				"workbook.xml": getXml('xl/workbook.xml'),
21041				"styles.xml": getXml('xl/styles.xml'),
21042				"worksheets": {
21043					"sheet1.xml": rels
21044				}
21045
21046			},
21047			"[Content_Types].xml": getXml('[Content_Types].xml')
21048		};
21049
21050		var data = dt.buttons.exportData( config.exportOptions );
21051		var currentRow, rowNode;
21052		var addRow = function ( row ) {
21053			currentRow = rowPos+1;
21054			rowNode = _createNode( rels, "row", { attr: {r:currentRow} } );
21055
21056			for ( var i=0, ien=row.length ; i<ien ; i++ ) {
21057				// Concat both the Cell Columns as a letter and the Row of the cell.
21058				var cellId = createCellPos(i) + '' + currentRow;
21059				var cell = null;
21060
21061				// For null, undefined of blank cell, continue so it doesn't create the _createNode
21062				if ( row[i] === null || row[i] === undefined || row[i] === '' ) {
21063					if ( config.createEmptyCells === true ) {
21064						row[i] = '';
21065					}
21066					else {
21067						continue;
21068					}
21069				}
21070
21071				var originalContent = row[i];
21072				row[i] = typeof row[i].trim === 'function'
21073					? row[i].trim()
21074					: row[i];
21075
21076				// Special number formatting options
21077				for ( var j=0, jen=_excelSpecials.length ; j<jen ; j++ ) {
21078					var special = _excelSpecials[j];
21079
21080					// TODO Need to provide the ability for the specials to say
21081					// if they are returning a string, since at the moment it is
21082					// assumed to be a number
21083					if ( row[i].match && ! row[i].match(/^0\d+/) && row[i].match( special.match ) ) {
21084						var val = row[i].replace(/[^\d\.\-]/g, '');
21085
21086						if ( special.fmt ) {
21087							val = special.fmt( val );
21088						}
21089
21090						cell = _createNode( rels, 'c', {
21091							attr: {
21092								r: cellId,
21093								s: special.style
21094							},
21095							children: [
21096								_createNode( rels, 'v', { text: val } )
21097							]
21098						} );
21099
21100						break;
21101					}
21102				}
21103
21104				if ( ! cell ) {
21105					if ( typeof row[i] === 'number' || (
21106						row[i].match &&
21107						row[i].match(/^-?\d+(\.\d+)?([eE]\-?\d+)?$/) && // Includes exponential format
21108						! row[i].match(/^0\d+/) )
21109					) {
21110						// Detect numbers - don't match numbers with leading zeros
21111						// or a negative anywhere but the start
21112						cell = _createNode( rels, 'c', {
21113							attr: {
21114								t: 'n',
21115								r: cellId
21116							},
21117							children: [
21118								_createNode( rels, 'v', { text: row[i] } )
21119							]
21120						} );
21121					}
21122					else {
21123						// String output - replace non standard characters for text output
21124						var text = ! originalContent.replace ?
21125							originalContent :
21126							originalContent.replace(/[\x00-\x09\x0B\x0C\x0E-\x1F\x7F-\x9F]/g, '');
21127
21128						cell = _createNode( rels, 'c', {
21129							attr: {
21130								t: 'inlineStr',
21131								r: cellId
21132							},
21133							children:{
21134								row: _createNode( rels, 'is', {
21135									children: {
21136										row: _createNode( rels, 't', {
21137											text: text,
21138											attr: {
21139												'xml:space': 'preserve'
21140											}
21141										} )
21142									}
21143								} )
21144							}
21145						} );
21146					}
21147				}
21148
21149				rowNode.appendChild( cell );
21150			}
21151
21152			relsGet.appendChild(rowNode);
21153			rowPos++;
21154		};
21155
21156		if ( config.customizeData ) {
21157			config.customizeData( data );
21158		}
21159
21160		var mergeCells = function ( row, colspan ) {
21161			var mergeCells = $('mergeCells', rels);
21162
21163			mergeCells[0].appendChild( _createNode( rels, 'mergeCell', {
21164				attr: {
21165					ref: 'A'+row+':'+createCellPos(colspan)+row
21166				}
21167			} ) );
21168			mergeCells.attr( 'count', parseFloat(mergeCells.attr( 'count' ))+1 );
21169			$('row:eq('+(row-1)+') c', rels).attr( 's', '51' ); // centre
21170		};
21171
21172		// Title and top messages
21173		var exportInfo = dt.buttons.exportInfo( config );
21174		if ( exportInfo.title ) {
21175			addRow( [exportInfo.title], rowPos );
21176			mergeCells( rowPos, data.header.length-1 );
21177		}
21178
21179		if ( exportInfo.messageTop ) {
21180			addRow( [exportInfo.messageTop], rowPos );
21181			mergeCells( rowPos, data.header.length-1 );
21182		}
21183
21184
21185		// Table itself
21186		if ( config.header ) {
21187			addRow( data.header, rowPos );
21188			$('row:last c', rels).attr( 's', '2' ); // bold
21189		}
21190	
21191		dataStartRow = rowPos;
21192
21193		for ( var n=0, ie=data.body.length ; n<ie ; n++ ) {
21194			addRow( data.body[n], rowPos );
21195		}
21196	
21197		dataEndRow = rowPos;
21198
21199		if ( config.footer && data.footer ) {
21200			addRow( data.footer, rowPos);
21201			$('row:last c', rels).attr( 's', '2' ); // bold
21202		}
21203
21204		// Below the table
21205		if ( exportInfo.messageBottom ) {
21206			addRow( [exportInfo.messageBottom], rowPos );
21207			mergeCells( rowPos, data.header.length-1 );
21208		}
21209
21210		// Set column widths
21211		var cols = _createNode( rels, 'cols' );
21212		$('worksheet', rels).prepend( cols );
21213
21214		for ( var i=0, ien=data.header.length ; i<ien ; i++ ) {
21215			cols.appendChild( _createNode( rels, 'col', {
21216				attr: {
21217					min: i+1,
21218					max: i+1,
21219					width: _excelColWidth( data, i ),
21220					customWidth: 1
21221				}
21222			} ) );
21223		}
21224
21225		// Workbook modifications
21226		var workbook = xlsx.xl['workbook.xml'];
21227
21228		$( 'sheets sheet', workbook ).attr( 'name', _sheetname( config ) );
21229
21230		// Auto filter for columns
21231		if ( config.autoFilter ) {
21232			$('mergeCells', rels).before( _createNode( rels, 'autoFilter', {
21233				attr: {
21234					ref: 'A'+dataStartRow+':'+createCellPos(data.header.length-1)+dataEndRow
21235				}
21236			} ) );
21237
21238			$('definedNames', workbook).append( _createNode( workbook, 'definedName', {
21239				attr: {
21240					name: '_xlnm._FilterDatabase',
21241					localSheetId: '0',
21242					hidden: 1
21243				},
21244				text: _sheetname(config)+'!$A$'+dataStartRow+':'+createCellPos(data.header.length-1)+dataEndRow
21245			} ) );
21246		}
21247
21248		// Let the developer customise the document if they want to
21249		if ( config.customize ) {
21250			config.customize( xlsx, config, dt );
21251		}
21252
21253		// Excel doesn't like an empty mergeCells tag
21254		if ( $('mergeCells', rels).children().length === 0 ) {
21255			$('mergeCells', rels).remove();
21256		}
21257
21258		var jszip = _jsZip();
21259		var zip = new jszip();
21260		var zipConfig = {
21261			compression: "DEFLATE",
21262			type: 'blob',
21263			mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
21264		};
21265
21266		_addToZip( zip, xlsx );
21267
21268		// Modern Excel has a 218 character limit on the file name + path of the file (why!?)
21269		// https://support.microsoft.com/en-us/office/excel-specifications-and-limits-1672b34d-7043-467e-8e27-269d656771c3
21270		// So we truncate to allow for this.
21271		var filename = exportInfo.filename;
21272
21273		if (filename > 175) {
21274			filename = filename.substr(0, 175);
21275		}
21276
21277		if ( zip.generateAsync ) {
21278			// JSZip 3+
21279			zip
21280				.generateAsync( zipConfig )
21281				.then( function ( blob ) {
21282					_saveAs( blob, filename );
21283					that.processing( false );
21284				} );
21285		}
21286		else {
21287			// JSZip 2.5
21288			_saveAs(
21289				zip.generate( zipConfig ),
21290				filename
21291			);
21292			this.processing( false );
21293		}
21294	},
21295
21296	filename: '*',
21297
21298	extension: '.xlsx',
21299
21300	exportOptions: {},
21301
21302	header: true,
21303
21304	footer: false,
21305
21306	title: '*',
21307
21308	messageTop: '*',
21309
21310	messageBottom: '*',
21311
21312	createEmptyCells: false,
21313
21314	autoFilter: false,
21315
21316	sheetName: ''
21317};
21318
21319//
21320// PDF export - using pdfMake - http://pdfmake.org
21321//
21322DataTable.ext.buttons.pdfHtml5 = {
21323	className: 'buttons-pdf buttons-html5',
21324
21325	available: function () {
21326		return window.FileReader !== undefined && _pdfMake();
21327	},
21328
21329	text: function ( dt ) {
21330		return dt.i18n( 'buttons.pdf', 'PDF' );
21331	},
21332
21333	action: function ( e, dt, button, config ) {
21334		this.processing( true );
21335
21336		var that = this;
21337		var data = dt.buttons.exportData( config.exportOptions );
21338		var info = dt.buttons.exportInfo( config );
21339		var rows = [];
21340
21341		if ( config.header ) {
21342			rows.push( $.map( data.header, function ( d ) {
21343				return {
21344					text: typeof d === 'string' ? d : d+'',
21345					style: 'tableHeader'
21346				};
21347			} ) );
21348		}
21349
21350		for ( var i=0, ien=data.body.length ; i<ien ; i++ ) {
21351			rows.push( $.map( data.body[i], function ( d ) {
21352				if ( d === null || d === undefined ) {
21353					d = '';
21354				}
21355				return {
21356					text: typeof d === 'string' ? d : d+'',
21357					style: i % 2 ? 'tableBodyEven' : 'tableBodyOdd'
21358				};
21359			} ) );
21360		}
21361
21362		if ( config.footer && data.footer) {
21363			rows.push( $.map( data.footer, function ( d ) {
21364				return {
21365					text: typeof d === 'string' ? d : d+'',
21366					style: 'tableFooter'
21367				};
21368			} ) );
21369		}
21370
21371		var doc = {
21372			pageSize: config.pageSize,
21373			pageOrientation: config.orientation,
21374			content: [
21375				{
21376					table: {
21377						headerRows: 1,
21378						body: rows
21379					},
21380					layout: 'noBorders'
21381				}
21382			],
21383			styles: {
21384				tableHeader: {
21385					bold: true,
21386					fontSize: 11,
21387					color: 'white',
21388					fillColor: '#2d4154',
21389					alignment: 'center'
21390				},
21391				tableBodyEven: {},
21392				tableBodyOdd: {
21393					fillColor: '#f3f3f3'
21394				},
21395				tableFooter: {
21396					bold: true,
21397					fontSize: 11,
21398					color: 'white',
21399					fillColor: '#2d4154'
21400				},
21401				title: {
21402					alignment: 'center',
21403					fontSize: 15
21404				},
21405				message: {}
21406			},
21407			defaultStyle: {
21408				fontSize: 10
21409			}
21410		};
21411
21412		if ( info.messageTop ) {
21413			doc.content.unshift( {
21414				text: info.messageTop,
21415				style: 'message',
21416				margin: [ 0, 0, 0, 12 ]
21417			} );
21418		}
21419
21420		if ( info.messageBottom ) {
21421			doc.content.push( {
21422				text: info.messageBottom,
21423				style: 'message',
21424				margin: [ 0, 0, 0, 12 ]
21425			} );
21426		}
21427
21428		if ( info.title ) {
21429			doc.content.unshift( {
21430				text: info.title,
21431				style: 'title',
21432				margin: [ 0, 0, 0, 12 ]
21433			} );
21434		}
21435
21436		if ( config.customize ) {
21437			config.customize( doc, config, dt );
21438		}
21439
21440		var pdf = _pdfMake().createPdf( doc );
21441
21442		if ( config.download === 'open' && ! _isDuffSafari() ) {
21443			pdf.open();
21444		}
21445		else {
21446			pdf.download( info.filename );
21447		}
21448
21449		this.processing( false );
21450	},
21451
21452	title: '*',
21453
21454	filename: '*',
21455
21456	extension: '.pdf',
21457
21458	exportOptions: {},
21459
21460	orientation: 'portrait',
21461
21462	pageSize: 'A4',
21463
21464	header: true,
21465
21466	footer: false,
21467
21468	messageTop: '*',
21469
21470	messageBottom: '*',
21471
21472	customize: null,
21473
21474	download: 'download'
21475};
21476
21477
21478return DataTable;
21479}));
21480
21481
21482/*!
21483 * Print button for Buttons and DataTables.
21484 * 2016 SpryMedia Ltd - datatables.net/license
21485 */
21486
21487(function( factory ){
21488	if ( typeof define === 'function' && define.amd ) {
21489		// AMD
21490		define( ['jquery', 'datatables.net', 'datatables.net-buttons'], function ( $ ) {
21491			return factory( $, window, document );
21492		} );
21493	}
21494	else if ( typeof exports === 'object' ) {
21495		// CommonJS
21496		var jq = require('jquery');
21497		var cjsRequires = function (root, $) {
21498			if ( ! $.fn.dataTable ) {
21499				require('datatables.net')(root, $);
21500			}
21501
21502			if ( ! $.fn.dataTable.Buttons ) {
21503				require('datatables.net-buttons')(root, $);
21504			}
21505		};
21506
21507		if (typeof window !== 'undefined') {
21508			module.exports = function (root, $) {
21509				if ( ! root ) {
21510					// CommonJS environments without a window global must pass a
21511					// root. This will give an error otherwise
21512					root = window;
21513				}
21514
21515				if ( ! $ ) {
21516					$ = jq( root );
21517				}
21518
21519				cjsRequires( root, $ );
21520				return factory( $, root, root.document );
21521			};
21522		}
21523		else {
21524			cjsRequires( window, jq );
21525			module.exports = factory( jq, window, window.document );
21526		}
21527	}
21528	else {
21529		// Browser
21530		factory( jQuery, window, document );
21531	}
21532}(function( $, window, document, undefined ) {
21533'use strict';
21534var DataTable = $.fn.dataTable;
21535
21536
21537
21538var _link = document.createElement( 'a' );
21539
21540/**
21541 * Clone link and style tags, taking into account the need to change the source
21542 * path.
21543 *
21544 * @param  {node}     el Element to convert
21545 */
21546var _styleToAbs = function( el ) {
21547	var url;
21548	var clone = $(el).clone()[0];
21549	var linkHost;
21550
21551	if ( clone.nodeName.toLowerCase() === 'link' ) {
21552		clone.href = _relToAbs( clone.href );
21553	}
21554
21555	return clone.outerHTML;
21556};
21557
21558/**
21559 * Convert a URL from a relative to an absolute address so it will work
21560 * correctly in the popup window which has no base URL.
21561 *
21562 * @param  {string} href URL
21563 */
21564var _relToAbs = function( href ) {
21565	// Assign to a link on the original page so the browser will do all the
21566	// hard work of figuring out where the file actually is
21567	_link.href = href;
21568	var linkHost = _link.host;
21569
21570	// IE doesn't have a trailing slash on the host
21571	// Chrome has it on the pathname
21572	if ( linkHost.indexOf('/') === -1 && _link.pathname.indexOf('/') !== 0) {
21573		linkHost += '/';
21574	}
21575
21576	return _link.protocol+"//"+linkHost+_link.pathname+_link.search;
21577};
21578
21579
21580DataTable.ext.buttons.print = {
21581	className: 'buttons-print',
21582
21583	text: function ( dt ) {
21584		return dt.i18n( 'buttons.print', 'Print' );
21585	},
21586
21587	action: function ( e, dt, button, config ) {
21588		var data = dt.buttons.exportData(
21589			$.extend( {decodeEntities: false}, config.exportOptions ) // XSS protection
21590		);
21591		var exportInfo = dt.buttons.exportInfo( config );
21592		var columnClasses = dt
21593			.columns( config.exportOptions.columns )
21594			.flatten()
21595			.map( function (idx) {
21596				return dt.settings()[0].aoColumns[dt.column(idx).index()].sClass;
21597			} )
21598			.toArray();
21599
21600		var addRow = function ( d, tag ) {
21601			var str = '<tr>';
21602
21603			for ( var i=0, ien=d.length ; i<ien ; i++ ) {
21604				// null and undefined aren't useful in the print output
21605				var dataOut = d[i] === null || d[i] === undefined ?
21606					'' :
21607					d[i];
21608				var classAttr = columnClasses[i] ?
21609					'class="'+columnClasses[i]+'"' :
21610					'';
21611
21612				str += '<'+tag+' '+classAttr+'>'+dataOut+'</'+tag+'>';
21613			}
21614
21615			return str + '</tr>';
21616		};
21617
21618		// Construct a table for printing
21619		var html = '<table class="'+dt.table().node().className+'">';
21620
21621		if ( config.header ) {
21622			html += '<thead>'+ addRow( data.header, 'th' ) +'</thead>';
21623		}
21624
21625		html += '<tbody>';
21626		for ( var i=0, ien=data.body.length ; i<ien ; i++ ) {
21627			html += addRow( data.body[i], 'td' );
21628		}
21629		html += '</tbody>';
21630
21631		if ( config.footer && data.footer ) {
21632			html += '<tfoot>'+ addRow( data.footer, 'th' ) +'</tfoot>';
21633		}
21634		html += '</table>';
21635
21636		// Open a new window for the printable table
21637		var win = window.open( '', '' );
21638
21639		if (! win) {
21640			dt.buttons.info(
21641				dt.i18n( 'buttons.printErrorTitle', 'Unable to open print view' ),
21642				dt.i18n( 'buttons.printErrorMsg', 'Please allow popups in your browser for this site to be able to view the print view.' ),
21643				5000
21644			);
21645
21646			return;
21647		}
21648
21649		win.document.close();
21650
21651		// Inject the title and also a copy of the style and link tags from this
21652		// document so the table can retain its base styling. Note that we have
21653		// to use string manipulation as IE won't allow elements to be created
21654		// in the host document and then appended to the new window.
21655		var head = '<title>'+exportInfo.title+'</title>';
21656		$('style, link').each( function () {
21657			head += _styleToAbs( this );
21658		} );
21659
21660		try {
21661			win.document.head.innerHTML = head; // Work around for Edge
21662		}
21663		catch (e) {
21664			$(win.document.head).html( head ); // Old IE
21665		}
21666
21667		// Inject the table and other surrounding information
21668		win.document.body.innerHTML =
21669			'<h1>'+exportInfo.title+'</h1>'+
21670			'<div>'+(exportInfo.messageTop || '')+'</div>'+
21671			html+
21672			'<div>'+(exportInfo.messageBottom || '')+'</div>';
21673
21674		$(win.document.body).addClass('dt-print-view');
21675
21676		$('img', win.document.body).each( function ( i, img ) {
21677			img.setAttribute( 'src', _relToAbs( img.getAttribute('src') ) );
21678		} );
21679
21680		if ( config.customize ) {
21681			config.customize( win, config, dt );
21682		}
21683
21684		// Allow stylesheets time to load
21685		var autoPrint = function () {
21686			if ( config.autoPrint ) {
21687				win.print(); // blocking - so close will not
21688				win.close(); // execute until this is done
21689			}
21690		};
21691
21692		if ( navigator.userAgent.match(/Trident\/\d.\d/) ) { // IE needs to call this without a setTimeout
21693			autoPrint();
21694		}
21695		else {
21696			win.setTimeout( autoPrint, 1000 );
21697		}
21698	},
21699
21700	title: '*',
21701
21702	messageTop: '*',
21703
21704	messageBottom: '*',
21705
21706	exportOptions: {},
21707
21708	header: true,
21709
21710	footer: false,
21711
21712	autoPrint: true,
21713
21714	customize: null
21715};
21716
21717
21718return DataTable;
21719}));
21720
21721
21722/*! ColReorder 1.6.2
21723 * © SpryMedia Ltd - datatables.net/license
21724 */
21725
21726(function( factory ){
21727	if ( typeof define === 'function' && define.amd ) {
21728		// AMD
21729		define( ['jquery', 'datatables.net'], function ( $ ) {
21730			return factory( $, window, document );
21731		} );
21732	}
21733	else if ( typeof exports === 'object' ) {
21734		// CommonJS
21735		var jq = require('jquery');
21736		var cjsRequires = function (root, $) {
21737			if ( ! $.fn.dataTable ) {
21738				require('datatables.net')(root, $);
21739			}
21740		};
21741
21742		if (typeof window !== 'undefined') {
21743			module.exports = function (root, $) {
21744				if ( ! root ) {
21745					// CommonJS environments without a window global must pass a
21746					// root. This will give an error otherwise
21747					root = window;
21748				}
21749
21750				if ( ! $ ) {
21751					$ = jq( root );
21752				}
21753
21754				cjsRequires( root, $ );
21755				return factory( $, root, root.document );
21756			};
21757		}
21758		else {
21759			cjsRequires( window, jq );
21760			module.exports = factory( jq, window, window.document );
21761		}
21762	}
21763	else {
21764		// Browser
21765		factory( jQuery, window, document );
21766	}
21767}(function( $, window, document, undefined ) {
21768'use strict';
21769var DataTable = $.fn.dataTable;
21770
21771
21772
21773/**
21774 * @summary     ColReorder
21775 * @description Provide the ability to reorder columns in a DataTable
21776 * @version     1.6.2
21777 * @author      SpryMedia Ltd
21778 * @contact     datatables.net
21779 * @copyright   SpryMedia Ltd.
21780 *
21781 * This source file is free software, available under the following license:
21782 *   MIT license - http://datatables.net/license/mit
21783 *
21784 * This source file is distributed in the hope that it will be useful, but
21785 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
21786 * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
21787 *
21788 * For details please refer to: http://www.datatables.net
21789 */
21790
21791/**
21792 * Switch the key value pairing of an index array to be value key (i.e. the old value is now the
21793 * key). For example consider [ 2, 0, 1 ] this would be returned as [ 1, 2, 0 ].
21794 *  @method  fnInvertKeyValues
21795 *  @param   array aIn Array to switch around
21796 *  @returns array
21797 */
21798function fnInvertKeyValues( aIn )
21799{
21800	var aRet=[];
21801	for ( var i=0, iLen=aIn.length ; i<iLen ; i++ )
21802	{
21803		aRet[ aIn[i] ] = i;
21804	}
21805	return aRet;
21806}
21807
21808
21809/**
21810 * Modify an array by switching the position of two elements
21811 *  @method  fnArraySwitch
21812 *  @param   array aArray Array to consider, will be modified by reference (i.e. no return)
21813 *  @param   int iFrom From point
21814 *  @param   int iTo Insert point
21815 *  @returns void
21816 */
21817function fnArraySwitch( aArray, iFrom, iTo )
21818{
21819	var mStore = aArray.splice( iFrom, 1 )[0];
21820	aArray.splice( iTo, 0, mStore );
21821}
21822
21823
21824/**
21825 * Switch the positions of nodes in a parent node (note this is specifically designed for
21826 * table rows). Note this function considers all element nodes under the parent!
21827 *  @method  fnDomSwitch
21828 *  @param   string sTag Tag to consider
21829 *  @param   int iFrom Element to move
21830 *  @param   int Point to element the element to (before this point), can be null for append
21831 *  @returns void
21832 */
21833function fnDomSwitch( nParent, iFrom, iTo )
21834{
21835	var anTags = [];
21836	for ( var i=0, iLen=nParent.childNodes.length ; i<iLen ; i++ )
21837	{
21838		if ( nParent.childNodes[i].nodeType == 1 )
21839		{
21840			anTags.push( nParent.childNodes[i] );
21841		}
21842	}
21843	var nStore = anTags[ iFrom ];
21844
21845	if ( iTo !== null )
21846	{
21847		nParent.insertBefore( nStore, anTags[iTo] );
21848	}
21849	else
21850	{
21851		nParent.appendChild( nStore );
21852	}
21853}
21854
21855
21856/**
21857 * Plug-in for DataTables which will reorder the internal column structure by taking the column
21858 * from one position (iFrom) and insert it into a given point (iTo).
21859 *  @method  $.fn.dataTableExt.oApi.fnColReorder
21860 *  @param   object oSettings DataTables settings object - automatically added by DataTables!
21861 *  @param   int iFrom Take the column to be repositioned from this point
21862 *  @param   int iTo and insert it into this point
21863 *  @param   bool drop Indicate if the reorder is the final one (i.e. a drop)
21864 *    not a live reorder
21865 *  @param   bool invalidateRows speeds up processing if false passed
21866 *  @returns void
21867 */
21868$.fn.dataTableExt.oApi.fnColReorder = function ( oSettings, iFrom, iTo, drop, invalidateRows )
21869{
21870	var i, iLen, j, jLen, jen, iCols=oSettings.aoColumns.length, nTrs, oCol;
21871	var attrMap = function ( obj, prop, mapping ) {
21872		if ( ! obj[ prop ] || typeof obj[ prop ] === 'function' ) {
21873			return;
21874		}
21875
21876		var a = obj[ prop ].split('.');
21877		var num = a.shift();
21878
21879		if ( isNaN( num*1 ) ) {
21880			return;
21881		}
21882
21883		obj[ prop ] = mapping[ num*1 ]+'.'+a.join('.');
21884	};
21885
21886	/* Sanity check in the input */
21887	if ( iFrom == iTo )
21888	{
21889		/* Pointless reorder */
21890		return;
21891	}
21892
21893	if ( iFrom < 0 || iFrom >= iCols )
21894	{
21895		this.oApi._fnLog( oSettings, 1, "ColReorder 'from' index is out of bounds: "+iFrom );
21896		return;
21897	}
21898
21899	if ( iTo < 0 || iTo >= iCols )
21900	{
21901		this.oApi._fnLog( oSettings, 1, "ColReorder 'to' index is out of bounds: "+iTo );
21902		return;
21903	}
21904
21905	/*
21906	 * Calculate the new column array index, so we have a mapping between the old and new
21907	 */
21908	var aiMapping = [];
21909	for ( i=0, iLen=iCols ; i<iLen ; i++ )
21910	{
21911		aiMapping[i] = i;
21912	}
21913	fnArraySwitch( aiMapping, iFrom, iTo );
21914	var aiInvertMapping = fnInvertKeyValues( aiMapping );
21915
21916
21917	/*
21918	 * Convert all internal indexing to the new column order indexes
21919	 */
21920	/* Sorting */
21921	for ( i=0, iLen=oSettings.aaSorting.length ; i<iLen ; i++ )
21922	{
21923		oSettings.aaSorting[i][0] = aiInvertMapping[ oSettings.aaSorting[i][0] ];
21924	}
21925
21926	/* Fixed sorting */
21927	if ( oSettings.aaSortingFixed !== null )
21928	{
21929		for ( i=0, iLen=oSettings.aaSortingFixed.length ; i<iLen ; i++ )
21930		{
21931			oSettings.aaSortingFixed[i][0] = aiInvertMapping[ oSettings.aaSortingFixed[i][0] ];
21932		}
21933	}
21934
21935	/* Data column sorting (the column which the sort for a given column should take place on) */
21936	for ( i=0, iLen=iCols ; i<iLen ; i++ )
21937	{
21938		oCol = oSettings.aoColumns[i];
21939		for ( j=0, jLen=oCol.aDataSort.length ; j<jLen ; j++ )
21940		{
21941			oCol.aDataSort[j] = aiInvertMapping[ oCol.aDataSort[j] ];
21942		}
21943
21944		// Update the column indexes
21945		oCol.idx = aiInvertMapping[ oCol.idx ];
21946	}
21947
21948	// Update 1.10 optimised sort class removal variable
21949	$.each( oSettings.aLastSort, function (i, val) {
21950		oSettings.aLastSort[i].src = aiInvertMapping[ val.src ];
21951	} );
21952
21953	/* Update the Get and Set functions for each column */
21954	for ( i=0, iLen=iCols ; i<iLen ; i++ )
21955	{
21956		oCol = oSettings.aoColumns[i];
21957
21958		if ( typeof oCol.mData == 'number' ) {
21959			oCol.mData = aiInvertMapping[ oCol.mData ];
21960		}
21961		else if ( $.isPlainObject( oCol.mData ) ) {
21962			// HTML5 data sourced
21963			attrMap( oCol.mData, '_',      aiInvertMapping );
21964			attrMap( oCol.mData, 'filter', aiInvertMapping );
21965			attrMap( oCol.mData, 'sort',   aiInvertMapping );
21966			attrMap( oCol.mData, 'type',   aiInvertMapping );
21967		}
21968	}
21969
21970	/*
21971	 * Move the DOM elements
21972	 */
21973	if ( oSettings.aoColumns[iFrom].bVisible )
21974	{
21975		/* Calculate the current visible index and the point to insert the node before. The insert
21976		 * before needs to take into account that there might not be an element to insert before,
21977		 * in which case it will be null, and an appendChild should be used
21978		 */
21979		var iVisibleIndex = this.oApi._fnColumnIndexToVisible( oSettings, iFrom );
21980		var iInsertBeforeIndex = null;
21981
21982		i = iTo < iFrom ? iTo : iTo + 1;
21983		while ( iInsertBeforeIndex === null && i < iCols )
21984		{
21985			iInsertBeforeIndex = this.oApi._fnColumnIndexToVisible( oSettings, i );
21986			i++;
21987		}
21988
21989		/* Header */
21990		nTrs = oSettings.nTHead.getElementsByTagName('tr');
21991		for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
21992		{
21993			fnDomSwitch( nTrs[i], iVisibleIndex, iInsertBeforeIndex );
21994		}
21995
21996		/* Footer */
21997		if ( oSettings.nTFoot !== null )
21998		{
21999			nTrs = oSettings.nTFoot.getElementsByTagName('tr');
22000			for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
22001			{
22002				fnDomSwitch( nTrs[i], iVisibleIndex, iInsertBeforeIndex );
22003			}
22004		}
22005
22006		/* Body */
22007		for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
22008		{
22009			if ( oSettings.aoData[i].nTr !== null )
22010			{
22011				fnDomSwitch( oSettings.aoData[i].nTr, iVisibleIndex, iInsertBeforeIndex );
22012			}
22013		}
22014	}
22015
22016	/*
22017	 * Move the internal array elements
22018	 */
22019	/* Columns */
22020	fnArraySwitch( oSettings.aoColumns, iFrom, iTo );
22021
22022	// regenerate the get / set functions
22023	for ( i=0, iLen=iCols ; i<iLen ; i++ ) {
22024		oSettings.oApi._fnColumnOptions( oSettings, i, {} );
22025	}
22026
22027	/* Search columns */
22028	fnArraySwitch( oSettings.aoPreSearchCols, iFrom, iTo );
22029
22030	/* Array array - internal data anodes cache */
22031	for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
22032	{
22033		var data = oSettings.aoData[i];
22034		var cells = data.anCells;
22035
22036		if ( cells ) {
22037			fnArraySwitch( cells, iFrom, iTo );
22038
22039			// Longer term, should this be moved into the DataTables' invalidate
22040			// methods?
22041			for ( j=0, jen=cells.length ; j<jen ; j++ ) {
22042				if ( cells[j] && cells[j]._DT_CellIndex ) {
22043					cells[j]._DT_CellIndex.column = j;
22044				}
22045			}
22046		}
22047
22048		// Swap around array sourced data (object based is left as is)
22049		if ( Array.isArray( data._aData ) ) {
22050			fnArraySwitch( data._aData, iFrom, iTo );
22051		}
22052	}
22053
22054	/* Reposition the header elements in the header layout array */
22055	for ( i=0, iLen=oSettings.aoHeader.length ; i<iLen ; i++ )
22056	{
22057		fnArraySwitch( oSettings.aoHeader[i], iFrom, iTo );
22058	}
22059
22060	if ( oSettings.aoFooter !== null )
22061	{
22062		for ( i=0, iLen=oSettings.aoFooter.length ; i<iLen ; i++ )
22063		{
22064			fnArraySwitch( oSettings.aoFooter[i], iFrom, iTo );
22065		}
22066	}
22067
22068	if ( invalidateRows || invalidateRows === undefined )
22069	{
22070		// Always read from the data object rather than reading back from the DOM
22071		// since it could have been changed by a renderer
22072		$.fn.dataTable.Api( oSettings ).rows().invalidate('data');
22073	}
22074
22075	/*
22076	 * Update DataTables' event handlers
22077	 */
22078
22079	/* Sort listener */
22080	for ( i=0, iLen=iCols ; i<iLen ; i++ )
22081	{
22082		$(oSettings.aoColumns[i].nTh).off('.DT');
22083		this.oApi._fnSortAttachListener( oSettings, oSettings.aoColumns[i].nTh, i );
22084	}
22085
22086
22087	/* Fire an event so other plug-ins can update */
22088	$(oSettings.oInstance).trigger( 'column-reorder.dt', [ oSettings, {
22089		from: iFrom,
22090		to: iTo,
22091		mapping: aiInvertMapping,
22092		drop: drop,
22093
22094		// Old style parameters for compatibility
22095		iFrom: iFrom,
22096		iTo: iTo,
22097		aiInvertMapping: aiInvertMapping
22098	} ] );
22099};
22100
22101/**
22102 * ColReorder provides column visibility control for DataTables
22103 * @class ColReorder
22104 * @constructor
22105 * @param {object} dt DataTables settings object
22106 * @param {object} opts ColReorder options
22107 */
22108var ColReorder = function( dt, opts )
22109{
22110	var settings = new $.fn.dataTable.Api( dt ).settings()[0];
22111
22112	// Ensure that we can't initialise on the same table twice
22113	if ( settings._colReorder ) {
22114		return settings._colReorder;
22115	}
22116
22117	// Allow the options to be a boolean for defaults
22118	if ( opts === true ) {
22119		opts = {};
22120	}
22121
22122	// Convert from camelCase to Hungarian, just as DataTables does
22123	var camelToHungarian = $.fn.dataTable.camelToHungarian;
22124	if ( camelToHungarian ) {
22125		camelToHungarian( ColReorder.defaults, ColReorder.defaults, true );
22126		camelToHungarian( ColReorder.defaults, opts || {} );
22127	}
22128
22129
22130	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
22131	 * Public class variables
22132	 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
22133
22134	/**
22135	 * @namespace Settings object which contains customisable information for ColReorder instance
22136	 */
22137	this.s = {
22138		/**
22139		 * DataTables settings object
22140		 *  @property dt
22141		 *  @type     Object
22142		 *  @default  null
22143		 */
22144		"dt": null,
22145
22146		/**
22147		 * Enable flag
22148		 *  @property dt
22149		 *  @type     Object
22150		 *  @default  null
22151		 */
22152		"enable": null,
22153
22154		/**
22155		 * Initialisation object used for this instance
22156		 *  @property init
22157		 *  @type     object
22158		 *  @default  {}
22159		 */
22160		"init": $.extend( true, {}, ColReorder.defaults, opts ),
22161
22162		/**
22163		 * Number of columns to fix (not allow to be reordered)
22164		 *  @property fixed
22165		 *  @type     int
22166		 *  @default  0
22167		 */
22168		"fixed": 0,
22169
22170		/**
22171		 * Number of columns to fix counting from right (not allow to be reordered)
22172		 *  @property fixedRight
22173		 *  @type     int
22174		 *  @default  0
22175		 */
22176		"fixedRight": 0,
22177
22178		/**
22179		 * Callback function for once the reorder has been done
22180		 *  @property reorderCallback
22181		 *  @type     function
22182		 *  @default  null
22183		 */
22184		"reorderCallback": null,
22185
22186		/**
22187		 * @namespace Information used for the mouse drag
22188		 */
22189		"mouse": {
22190			"startX": -1,
22191			"startY": -1,
22192			"offsetX": -1,
22193			"offsetY": -1,
22194			"target": -1,
22195			"targetIndex": -1,
22196			"fromIndex": -1
22197		},
22198
22199		/**
22200		 * Information which is used for positioning the insert cusor and knowing where to do the
22201		 * insert. Array of objects with the properties:
22202		 *   x: x-axis position
22203		 *   to: insert point
22204		 *  @property aoTargets
22205		 *  @type     array
22206		 *  @default  []
22207		 */
22208		"aoTargets": []
22209	};
22210
22211
22212	/**
22213	 * @namespace Common and useful DOM elements for the class instance
22214	 */
22215	this.dom = {
22216		/**
22217		 * Dragging element (the one the mouse is moving)
22218		 *  @property drag
22219		 *  @type     element
22220		 *  @default  null
22221		 */
22222		"drag": null,
22223
22224		/**
22225		 * The insert cursor
22226		 *  @property pointer
22227		 *  @type     element
22228		 *  @default  null
22229		 */
22230		"pointer": null
22231	};
22232
22233	/* Constructor logic */
22234	this.s.enable = this.s.init.bEnable;
22235	this.s.dt = settings;
22236	this.s.dt._colReorder = this;
22237	this._fnConstruct();
22238
22239	return this;
22240};
22241
22242
22243
22244$.extend( ColReorder.prototype, {
22245	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
22246	 * Public methods
22247	 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
22248
22249	/**
22250	 * Enable / disable end user interaction
22251	 */
22252	fnEnable: function ( flag )
22253	{
22254		if ( flag === false ) {
22255			return this.fnDisable();
22256		}
22257
22258		this.s.enable = true;
22259	},
22260
22261	/**
22262	 * Disable end user interaction
22263	 */
22264	fnDisable: function ()
22265	{
22266		this.s.enable = false;
22267	},
22268
22269	/**
22270	 * Reset the column ordering to the original ordering that was detected on
22271	 * start up.
22272	 *  @return {this} Returns `this` for chaining.
22273	 *
22274	 *  @example
22275	 *    // DataTables initialisation with ColReorder
22276	 *    var table = $('#example').dataTable( {
22277	 *        "sDom": 'Rlfrtip'
22278	 *    } );
22279	 *
22280	 *    // Add click event to a button to reset the ordering
22281	 *    $('#resetOrdering').click( function (e) {
22282	 *        e.preventDefault();
22283	 *        $.fn.dataTable.ColReorder( table ).fnReset();
22284	 *    } );
22285	 */
22286	"fnReset": function ()
22287	{
22288		this._fnOrderColumns( this.fnOrder() );
22289
22290		return this;
22291	},
22292
22293	/**
22294	 * `Deprecated` - Get the current order of the columns, as an array.
22295	 *  @return {array} Array of column identifiers
22296	 *  @deprecated `fnOrder` should be used in preference to this method.
22297	 *      `fnOrder` acts as a getter/setter.
22298	 */
22299	"fnGetCurrentOrder": function ()
22300	{
22301		return this.fnOrder();
22302	},
22303
22304	/**
22305	 * Get the current order of the columns, as an array. Note that the values
22306	 * given in the array are unique identifiers for each column. Currently
22307	 * these are the original ordering of the columns that was detected on
22308	 * start up, but this could potentially change in future.
22309	 *  @return {array} Array of column identifiers
22310	 *
22311	 *  @example
22312	 *    // Get column ordering for the table
22313	 *    var order = $.fn.dataTable.ColReorder( dataTable ).fnOrder();
22314	 *//**
22315	 * Set the order of the columns, from the positions identified in the
22316	 * ordering array given. Note that ColReorder takes a brute force approach
22317	 * to reordering, so it is possible multiple reordering events will occur
22318	 * before the final order is settled upon.
22319	 *  @param {array} [set] Array of column identifiers in the new order. Note
22320	 *    that every column must be included, uniquely, in this array.
22321	 *  @return {this} Returns `this` for chaining.
22322	 *
22323	 *  @example
22324	 *    // Swap the first and second columns
22325	 *    $.fn.dataTable.ColReorder( dataTable ).fnOrder( [1, 0, 2, 3, 4] );
22326	 *
22327	 *  @example
22328	 *    // Move the first column to the end for the table `#example`
22329	 *    var curr = $.fn.dataTable.ColReorder( '#example' ).fnOrder();
22330	 *    var first = curr.shift();
22331	 *    curr.push( first );
22332	 *    $.fn.dataTable.ColReorder( '#example' ).fnOrder( curr );
22333	 *
22334	 *  @example
22335	 *    // Reverse the table's order
22336	 *    $.fn.dataTable.ColReorder( '#example' ).fnOrder(
22337	 *      $.fn.dataTable.ColReorder( '#example' ).fnOrder().reverse()
22338	 *    );
22339	 */
22340	"fnOrder": function ( set, original )
22341	{
22342		var a = [], i, ien, j, jen;
22343		var columns = this.s.dt.aoColumns;
22344
22345		if ( set === undefined ){
22346			for ( i=0, ien=columns.length ; i<ien ; i++ ) {
22347				a.push( columns[i]._ColReorder_iOrigCol );
22348			}
22349
22350			return a;
22351		}
22352
22353		// The order given is based on the original indexes, rather than the
22354		// existing ones, so we need to translate from the original to current
22355		// before then doing the order
22356		if ( original ) {
22357			var order = this.fnOrder();
22358
22359			for ( i=0, ien=set.length ; i<ien ; i++ ) {
22360				a.push( $.inArray( set[i], order ) );
22361			}
22362
22363			set = a;
22364		}
22365
22366		this._fnOrderColumns( fnInvertKeyValues( set ) );
22367
22368		return this;
22369	},
22370
22371
22372	/**
22373	 * Convert from the original column index, to the original
22374	 *
22375	 * @param  {int|array} idx Index(es) to convert
22376	 * @param  {string} dir Transpose direction - `fromOriginal` / `toCurrent`
22377	 *   or `'toOriginal` / `fromCurrent`
22378	 * @return {int|array}     Converted values
22379	 */
22380	fnTranspose: function ( idx, dir )
22381	{
22382		if ( ! dir ) {
22383			dir = 'toCurrent';
22384		}
22385
22386		var order = this.fnOrder();
22387		var columns = this.s.dt.aoColumns;
22388
22389		if ( dir === 'toCurrent' ) {
22390			// Given an original index, want the current
22391			return ! Array.isArray( idx ) ?
22392				$.inArray( idx, order ) :
22393				$.map( idx, function ( index ) {
22394					return $.inArray( index, order );
22395				} );
22396		}
22397		else {
22398			// Given a current index, want the original
22399			return ! Array.isArray( idx ) ?
22400				columns[idx]._ColReorder_iOrigCol :
22401				$.map( idx, function ( index ) {
22402					return columns[index]._ColReorder_iOrigCol;
22403				} );
22404		}
22405	},
22406
22407
22408	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
22409	 * Private methods (they are of course public in JS, but recommended as private)
22410	 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
22411
22412	/**
22413	 * Constructor logic
22414	 *  @method  _fnConstruct
22415	 *  @returns void
22416	 *  @private
22417	 */
22418	"_fnConstruct": function ()
22419	{
22420		var that = this;
22421		var iLen = this.s.dt.aoColumns.length;
22422		var table = this.s.dt.nTable;
22423		var i;
22424
22425		/* Columns discounted from reordering - counting left to right */
22426		if ( this.s.init.iFixedColumns )
22427		{
22428			this.s.fixed = this.s.init.iFixedColumns;
22429		}
22430
22431		if ( this.s.init.iFixedColumnsLeft )
22432		{
22433			this.s.fixed = this.s.init.iFixedColumnsLeft;
22434		}
22435
22436		/* Columns discounted from reordering - counting right to left */
22437		this.s.fixedRight = this.s.init.iFixedColumnsRight ?
22438			this.s.init.iFixedColumnsRight :
22439			0;
22440
22441		/* Drop callback initialisation option */
22442		if ( this.s.init.fnReorderCallback )
22443		{
22444			this.s.reorderCallback = this.s.init.fnReorderCallback;
22445		}
22446
22447		/* Add event handlers for the drag and drop, and also mark the original column order */
22448		for ( i = 0; i < iLen; i++ )
22449		{
22450			if ( i > this.s.fixed-1 && i < iLen - this.s.fixedRight )
22451			{
22452				this._fnMouseListener( i, this.s.dt.aoColumns[i].nTh );
22453			}
22454
22455			/* Mark the original column order for later reference */
22456			this.s.dt.aoColumns[i]._ColReorder_iOrigCol = i;
22457		}
22458
22459		/* State saving */
22460		this.s.dt.oApi._fnCallbackReg( this.s.dt, 'aoStateSaveParams', function (oS, oData) {
22461			that._fnStateSave.call( that, oData );
22462		}, "ColReorder_State" );
22463
22464		this.s.dt.oApi._fnCallbackReg(this.s.dt, 'aoStateLoadParams', function(oS, oData) {
22465			that.s.dt._colReorder.fnOrder(oData.ColReorder, true);
22466		})
22467
22468		/* An initial column order has been specified */
22469		var aiOrder = null;
22470		if ( this.s.init.aiOrder )
22471		{
22472			aiOrder = this.s.init.aiOrder.slice();
22473		}
22474
22475		/* State loading, overrides the column order given */
22476		if ( this.s.dt.oLoadedState && typeof this.s.dt.oLoadedState.ColReorder != 'undefined' &&
22477		  this.s.dt.oLoadedState.ColReorder.length == this.s.dt.aoColumns.length )
22478		{
22479			aiOrder = this.s.dt.oLoadedState.ColReorder;
22480		}
22481
22482		/* If we have an order to apply - do so */
22483		if ( aiOrder )
22484		{
22485			/* We might be called during or after the DataTables initialisation. If before, then we need
22486			 * to wait until the draw is done, if after, then do what we need to do right away
22487			 */
22488			if ( !that.s.dt._bInitComplete )
22489			{
22490				var bDone = false;
22491				$(table).on( 'draw.dt.colReorder', function () {
22492					if ( !that.s.dt._bInitComplete && !bDone )
22493					{
22494						bDone = true;
22495						var resort = fnInvertKeyValues( aiOrder );
22496						that._fnOrderColumns.call( that, resort );
22497					}
22498				} );
22499			}
22500			else
22501			{
22502				var resort = fnInvertKeyValues( aiOrder );
22503				that._fnOrderColumns.call( that, resort );
22504			}
22505		}
22506		else {
22507			this._fnSetColumnIndexes();
22508		}
22509
22510		// Destroy clean up
22511		$(table).on( 'destroy.dt.colReorder', function () {
22512			// Restore table to original order from when it was loaded
22513			that.fnReset();
22514
22515			$(table).off( 'destroy.dt.colReorder draw.dt.colReorder' );
22516
22517			$.each( that.s.dt.aoColumns, function (i, column) {
22518				$(column.nTh).off('.ColReorder');
22519				$(column.nTh).removeAttr('data-column-index');
22520			} );
22521
22522			that.s.dt._colReorder = null;
22523			that.s = null;
22524		} );
22525	},
22526
22527
22528	/**
22529	 * Set the column order from an array
22530	 *  @method  _fnOrderColumns
22531	 *  @param   array a An array of integers which dictate the column order that should be applied
22532	 *  @returns void
22533	 *  @private
22534	 */
22535	"_fnOrderColumns": function ( a )
22536	{
22537		var changed = false;
22538
22539		if ( a.length != this.s.dt.aoColumns.length )
22540		{
22541			this.s.dt.oInstance.oApi._fnLog( this.s.dt, 1, "ColReorder - array reorder does not "+
22542				"match known number of columns. Skipping." );
22543			return;
22544		}
22545
22546		for ( var i=0, iLen=a.length ; i<iLen ; i++ )
22547		{
22548			var currIndex = $.inArray( i, a );
22549			if ( i != currIndex )
22550			{
22551				/* Reorder our switching array */
22552				fnArraySwitch( a, currIndex, i );
22553
22554				/* Do the column reorder in the table */
22555				this.s.dt.oInstance.fnColReorder( currIndex, i, true, false );
22556
22557				changed = true;
22558			}
22559		}
22560
22561		this._fnSetColumnIndexes();
22562
22563		// Has anything actually changed? If not, then nothing else to do
22564		if ( ! changed ) {
22565			return;
22566		}
22567
22568		$.fn.dataTable.Api( this.s.dt ).rows().invalidate('data');
22569
22570		/* When scrolling we need to recalculate the column sizes to allow for the shift */
22571		if ( this.s.dt.oScroll.sX !== "" || this.s.dt.oScroll.sY !== "" )
22572		{
22573			this.s.dt.oInstance.fnAdjustColumnSizing( false );
22574		}
22575
22576		/* Save the state */
22577		this.s.dt.oInstance.oApi._fnSaveState( this.s.dt );
22578
22579		if ( this.s.reorderCallback !== null )
22580		{
22581			this.s.reorderCallback.call( this );
22582		}
22583	},
22584
22585
22586	/**
22587	 * Because we change the indexes of columns in the table, relative to their starting point
22588	 * we need to reorder the state columns to what they are at the starting point so we can
22589	 * then rearrange them again on state load!
22590	 *  @method  _fnStateSave
22591	 *  @param   object oState DataTables state
22592	 *  @returns string JSON encoded cookie string for DataTables
22593	 *  @private
22594	 */
22595	"_fnStateSave": function ( oState )
22596	{
22597		if(this.s === null) {
22598			return;
22599		}
22600		var i, iLen, aCopy, iOrigColumn;
22601		var oSettings = this.s.dt;
22602		var columns = oSettings.aoColumns;
22603
22604		oState.ColReorder = [];
22605
22606		/* Sorting */
22607		if ( oState.aaSorting ) {
22608			// 1.10.0-
22609			for ( i=0 ; i<oState.aaSorting.length ; i++ ) {
22610				oState.aaSorting[i][0] = columns[ oState.aaSorting[i][0] ]._ColReorder_iOrigCol;
22611			}
22612
22613			var aSearchCopy = $.extend( true, [], oState.aoSearchCols );
22614
22615			for ( i=0, iLen=columns.length ; i<iLen ; i++ )
22616			{
22617				iOrigColumn = columns[i]._ColReorder_iOrigCol;
22618
22619				/* Column filter */
22620				oState.aoSearchCols[ iOrigColumn ] = aSearchCopy[i];
22621
22622				/* Visibility */
22623				oState.abVisCols[ iOrigColumn ] = columns[i].bVisible;
22624
22625				/* Column reordering */
22626				oState.ColReorder.push( iOrigColumn );
22627			}
22628		}
22629		else if ( oState.order ) {
22630			// 1.10.1+
22631			for ( i=0 ; i<oState.order.length ; i++ ) {
22632				oState.order[i][0] = columns[ oState.order[i][0] ]._ColReorder_iOrigCol;
22633			}
22634
22635			var stateColumnsCopy = $.extend( true, [], oState.columns );
22636
22637			for ( i=0, iLen=columns.length ; i<iLen ; i++ )
22638			{
22639				iOrigColumn = columns[i]._ColReorder_iOrigCol;
22640
22641				/* Columns */
22642				oState.columns[ iOrigColumn ] = stateColumnsCopy[i];
22643
22644				/* Column reordering */
22645				oState.ColReorder.push( iOrigColumn );
22646			}
22647		}
22648	},
22649
22650
22651	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
22652	 * Mouse drop and drag
22653	 */
22654
22655	/**
22656	 * Add a mouse down listener to a particluar TH element
22657	 *  @method  _fnMouseListener
22658	 *  @param   int i Column index
22659	 *  @param   element nTh TH element clicked on
22660	 *  @returns void
22661	 *  @private
22662	 */
22663	"_fnMouseListener": function ( i, nTh )
22664	{
22665		var that = this;
22666		$(nTh)
22667			.on( 'mousedown.ColReorder', function (e) {
22668				if ( that.s.enable && e.which === 1 ) {
22669					that._fnMouseDown.call( that, e, nTh );
22670				}
22671			} )
22672			.on( 'touchstart.ColReorder', function (e) {
22673				if ( that.s.enable ) {
22674					that._fnMouseDown.call( that, e, nTh );
22675				}
22676			} );
22677	},
22678
22679
22680	/**
22681	 * Mouse down on a TH element in the table header
22682	 *  @method  _fnMouseDown
22683	 *  @param   event e Mouse event
22684	 *  @param   element nTh TH element to be dragged
22685	 *  @returns void
22686	 *  @private
22687	 */
22688	"_fnMouseDown": function ( e, nTh )
22689	{
22690		var that = this;
22691
22692		/* Store information about the mouse position */
22693		var target = $(e.target).closest('th, td');
22694		var offset = target.offset();
22695		var idx = parseInt( $(nTh).attr('data-column-index'), 10 );
22696
22697		if ( idx === undefined ) {
22698			return;
22699		}
22700
22701		this.s.mouse.startX = this._fnCursorPosition( e, 'pageX' );
22702		this.s.mouse.startY = this._fnCursorPosition( e, 'pageY' );
22703		this.s.mouse.offsetX = this._fnCursorPosition( e, 'pageX' ) - offset.left;
22704		this.s.mouse.offsetY = this._fnCursorPosition( e, 'pageY' ) - offset.top;
22705		this.s.mouse.target = this.s.dt.aoColumns[ idx ].nTh;//target[0];
22706		this.s.mouse.targetIndex = idx;
22707		this.s.mouse.fromIndex = idx;
22708
22709		this._fnRegions();
22710
22711		/* Add event handlers to the document */
22712		$(document)
22713			.on( 'mousemove.ColReorder touchmove.ColReorder', function (e) {
22714				that._fnMouseMove.call( that, e );
22715			} )
22716			.on( 'mouseup.ColReorder touchend.ColReorder', function (e) {
22717				that._fnMouseUp.call( that, e );
22718			} );
22719	},
22720
22721
22722	/**
22723	 * Deal with a mouse move event while dragging a node
22724	 *  @method  _fnMouseMove
22725	 *  @param   event e Mouse event
22726	 *  @returns void
22727	 *  @private
22728	 */
22729	"_fnMouseMove": function ( e )
22730	{
22731		var that = this;
22732
22733		if ( this.dom.drag === null )
22734		{
22735			/* Only create the drag element if the mouse has moved a specific distance from the start
22736			 * point - this allows the user to make small mouse movements when sorting and not have a
22737			 * possibly confusing drag element showing up
22738			 */
22739			if ( Math.pow(
22740				Math.pow(this._fnCursorPosition( e, 'pageX') - this.s.mouse.startX, 2) +
22741				Math.pow(this._fnCursorPosition( e, 'pageY') - this.s.mouse.startY, 2), 0.5 ) < 5 )
22742			{
22743				return;
22744			}
22745			this._fnCreateDragNode();
22746		}
22747
22748		/* Position the element - we respect where in the element the click occured */
22749		this.dom.drag.css( {
22750			left: this._fnCursorPosition( e, 'pageX' ) - this.s.mouse.offsetX,
22751			top: this._fnCursorPosition( e, 'pageY' ) - this.s.mouse.offsetY
22752		} );
22753
22754		/* Based on the current mouse position, calculate where the insert should go */
22755		var target;
22756		var lastToIndex = this.s.mouse.toIndex;
22757		var cursorXPosiotion = this._fnCursorPosition(e, 'pageX');
22758		var targetsPrev = function (i) {
22759			while (i >= 0) {
22760				i--;
22761
22762				if (i <= 0) {
22763					return null;
22764				}
22765
22766				if (that.s.aoTargets[i+1].x !== that.s.aoTargets[i].x) {
22767					return that.s.aoTargets[i];
22768				}
22769			}
22770		};
22771		var firstNotHidden = function () {
22772			for (var i=0 ; i<that.s.aoTargets.length-1 ; i++) {
22773				if (that.s.aoTargets[i].x !== that.s.aoTargets[i+1].x) {
22774					return that.s.aoTargets[i];
22775				}
22776			}
22777		};
22778		var lastNotHidden = function () {
22779			for (var i=that.s.aoTargets.length-1 ; i>0 ; i--) {
22780				if (that.s.aoTargets[i].x !== that.s.aoTargets[i-1].x) {
22781					return that.s.aoTargets[i];
22782				}
22783			}
22784		};
22785
22786        for (var i = 1; i < this.s.aoTargets.length; i++) {
22787			var prevTarget = targetsPrev(i);
22788			if (! prevTarget) {
22789				prevTarget = firstNotHidden();
22790			}
22791
22792			var prevTargetMiddle = prevTarget.x + (this.s.aoTargets[i].x - prevTarget.x) / 2;
22793
22794            if (this._fnIsLtr()) {
22795                if (cursorXPosiotion < prevTargetMiddle ) {
22796                    target = prevTarget;
22797                    break;
22798                }
22799            }
22800            else {
22801                if (cursorXPosiotion > prevTargetMiddle) {
22802                    target = prevTarget;
22803                    break;
22804                }
22805            }
22806		}
22807
22808        if (target) {
22809            this.dom.pointer.css('left', target.x);
22810            this.s.mouse.toIndex = target.to;
22811        }
22812        else {
22813			// The insert element wasn't positioned in the array (less than
22814			// operator), so we put it at the end
22815			this.dom.pointer.css( 'left', lastNotHidden().x );
22816			this.s.mouse.toIndex = lastNotHidden().to;
22817		}
22818
22819		// Perform reordering if realtime updating is on and the column has moved
22820		if ( this.s.init.bRealtime && lastToIndex !== this.s.mouse.toIndex ) {
22821			this.s.dt.oInstance.fnColReorder( this.s.mouse.fromIndex, this.s.mouse.toIndex );
22822			this.s.mouse.fromIndex = this.s.mouse.toIndex;
22823
22824			// Not great for performance, but required to keep everything in alignment
22825			if ( this.s.dt.oScroll.sX !== "" || this.s.dt.oScroll.sY !== "" )
22826			{
22827				this.s.dt.oInstance.fnAdjustColumnSizing( false );
22828			}
22829
22830			this._fnRegions();
22831		}
22832	},
22833
22834
22835	/**
22836	 * Finish off the mouse drag and insert the column where needed
22837	 *  @method  _fnMouseUp
22838	 *  @param   event e Mouse event
22839	 *  @returns void
22840	 *  @private
22841	 */
22842	"_fnMouseUp": function ( e )
22843	{
22844		var that = this;
22845
22846		$(document).off( '.ColReorder' );
22847
22848		if ( this.dom.drag !== null )
22849		{
22850			/* Remove the guide elements */
22851			this.dom.drag.remove();
22852			this.dom.pointer.remove();
22853			this.dom.drag = null;
22854			this.dom.pointer = null;
22855
22856			/* Actually do the reorder */
22857			this.s.dt.oInstance.fnColReorder( this.s.mouse.fromIndex, this.s.mouse.toIndex, true );
22858			this._fnSetColumnIndexes();
22859
22860			/* When scrolling we need to recalculate the column sizes to allow for the shift */
22861			if ( this.s.dt.oScroll.sX !== "" || this.s.dt.oScroll.sY !== "" )
22862			{
22863				this.s.dt.oInstance.fnAdjustColumnSizing( false );
22864			}
22865
22866			/* Save the state */
22867			this.s.dt.oInstance.oApi._fnSaveState( this.s.dt );
22868
22869			if ( this.s.reorderCallback !== null )
22870			{
22871				this.s.reorderCallback.call( this );
22872			}
22873		}
22874	},
22875
22876
22877	/**
22878	 * Calculate a cached array with the points of the column inserts, and the
22879	 * 'to' points
22880	 *  @method  _fnRegions
22881	 *  @returns void
22882	 *  @private
22883	 */
22884	"_fnRegions": function ()
22885	{
22886		var aoColumns = this.s.dt.aoColumns;
22887        var isLTR = this._fnIsLtr();
22888		this.s.aoTargets.splice(0, this.s.aoTargets.length);
22889		var lastBound = $(this.s.dt.nTable).offset().left;
22890
22891        var aoColumnBounds = [];
22892        $.each(aoColumns, function (i, column) {
22893            if (column.bVisible && column.nTh.style.display !== 'none') {
22894                var nth = $(column.nTh);
22895				var bound = nth.offset().left;
22896
22897                if (isLTR) {
22898                    bound += nth.outerWidth();
22899                }
22900
22901                aoColumnBounds.push({
22902                    index: i,
22903                    bound: bound
22904				});
22905
22906				lastBound = bound;
22907			}
22908			else {
22909                aoColumnBounds.push({
22910					index: i,
22911					bound: lastBound
22912                });
22913			}
22914		});
22915
22916        var firstColumn = aoColumnBounds[0];
22917		var firstColumnWidth = $(aoColumns[firstColumn.index].nTh).outerWidth();
22918
22919        this.s.aoTargets.push({
22920            to: 0,
22921			x: firstColumn.bound - firstColumnWidth
22922        });
22923
22924        for (var i = 0; i < aoColumnBounds.length; i++) {
22925            var columnBound = aoColumnBounds[i];
22926            var iToPoint = columnBound.index;
22927
22928            /* For the column / header in question, we want it's position to remain the same if the
22929            * position is just to it's immediate left or right, so we only increment the counter for
22930            * other columns
22931            */
22932            if (columnBound.index < this.s.mouse.fromIndex) {
22933                iToPoint++;
22934            }
22935
22936            this.s.aoTargets.push({
22937				to: iToPoint,
22938                x: columnBound.bound
22939            });
22940        }
22941
22942		/* Disallow columns for being reordered by drag and drop, counting right to left */
22943		if ( this.s.fixedRight !== 0 )
22944		{
22945			this.s.aoTargets.splice( this.s.aoTargets.length - this.s.fixedRight );
22946		}
22947
22948		/* Disallow columns for being reordered by drag and drop, counting left to right */
22949		if ( this.s.fixed !== 0 )
22950		{
22951			this.s.aoTargets.splice( 0, this.s.fixed );
22952		}
22953	},
22954
22955
22956	/**
22957	 * Copy the TH element that is being drags so the user has the idea that they are actually
22958	 * moving it around the page.
22959	 *  @method  _fnCreateDragNode
22960	 *  @returns void
22961	 *  @private
22962	 */
22963	"_fnCreateDragNode": function ()
22964	{
22965		var scrolling = this.s.dt.oScroll.sX !== "" || this.s.dt.oScroll.sY !== "";
22966
22967		var origCell = this.s.dt.aoColumns[ this.s.mouse.targetIndex ].nTh;
22968		var origTr = origCell.parentNode;
22969		var origThead = origTr.parentNode;
22970		var origTable = origThead.parentNode;
22971		var cloneCell = $(origCell).clone();
22972
22973		// This is a slightly odd combination of jQuery and DOM, but it is the
22974		// fastest and least resource intensive way I could think of cloning
22975		// the table with just a single header cell in it.
22976		this.dom.drag = $(origTable.cloneNode(false))
22977			.addClass( 'DTCR_clonedTable' )
22978			.append(
22979				$(origThead.cloneNode(false)).append(
22980					$(origTr.cloneNode(false)).append(
22981						cloneCell[0]
22982					)
22983				)
22984			)
22985			.css( {
22986				position: 'absolute',
22987				top: 0,
22988				left: 0,
22989				width: $(origCell).outerWidth(),
22990				height: $(origCell).outerHeight()
22991			} )
22992			.appendTo( 'body' );
22993
22994		this.dom.pointer = $('<div></div>')
22995			.addClass( 'DTCR_pointer' )
22996			.css( {
22997				position: 'absolute',
22998				top: scrolling ?
22999					$($(this.s.dt.nScrollBody).parent()).offset().top :
23000					$(this.s.dt.nTable).offset().top,
23001				height : scrolling ?
23002					$($(this.s.dt.nScrollBody).parent()).height() :
23003					$(this.s.dt.nTable).height()
23004			} )
23005			.appendTo( 'body' );
23006	},
23007
23008
23009	/**
23010	 * Add a data attribute to the column headers, so we know the index of
23011	 * the row to be reordered. This allows fast detection of the index, and
23012	 * for this plug-in to work with FixedHeader which clones the nodes.
23013	 *  @private
23014	 */
23015	"_fnSetColumnIndexes": function ()
23016	{
23017		$.each( this.s.dt.aoColumns, function (i, column) {
23018			$(column.nTh).attr('data-column-index', i);
23019		} );
23020	},
23021
23022
23023	/**
23024	 * Get cursor position regardless of mouse or touch input
23025	 * @param  {Event}  e    jQuery Event
23026	 * @param  {string} prop Property to get
23027	 * @return {number}      Value
23028	 */
23029	_fnCursorPosition: function ( e, prop ) {
23030		if ( e.type.indexOf('touch') !== -1 ) {
23031			return e.originalEvent.touches[0][ prop ];
23032		}
23033		return e[ prop ];
23034    },
23035
23036    _fnIsLtr: function () {
23037        return $(this.s.dt.nTable).css('direction') !== "rtl";
23038    }
23039} );
23040
23041
23042
23043
23044
23045/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23046 * Static parameters
23047 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
23048
23049
23050/**
23051 * ColReorder default settings for initialisation
23052 *  @namespace
23053 *  @static
23054 */
23055ColReorder.defaults = {
23056	/**
23057	 * Predefined ordering for the columns that will be applied automatically
23058	 * on initialisation. If not specified then the order that the columns are
23059	 * found to be in the HTML is the order used.
23060	 *  @type array
23061	 *  @default null
23062	 *  @static
23063	 */
23064	aiOrder: null,
23065
23066	/**
23067	 * ColReorder enable on initialisation
23068	 *  @type boolean
23069	 *  @default true
23070	 *  @static
23071	 */
23072	bEnable: true,
23073
23074	/**
23075	 * Redraw the table's column ordering as the end user draws the column
23076	 * (`true`) or wait until the mouse is released (`false` - default). Note
23077	 * that this will perform a redraw on each reordering, which involves an
23078	 * Ajax request each time if you are using server-side processing in
23079	 * DataTables.
23080	 *  @type boolean
23081	 *  @default false
23082	 *  @static
23083	 */
23084	bRealtime: true,
23085
23086	/**
23087	 * Indicate how many columns should be fixed in position (counting from the
23088	 * left). This will typically be 1 if used, but can be as high as you like.
23089	 *  @type int
23090	 *  @default 0
23091	 *  @static
23092	 */
23093	iFixedColumnsLeft: 0,
23094
23095	/**
23096	 * As `iFixedColumnsRight` but counting from the right.
23097	 *  @type int
23098	 *  @default 0
23099	 *  @static
23100	 */
23101	iFixedColumnsRight: 0,
23102
23103	/**
23104	 * Callback function that is fired when columns are reordered. The `column-
23105	 * reorder` event is preferred over this callback
23106	 *  @type function():void
23107	 *  @default null
23108	 *  @static
23109	 */
23110	fnReorderCallback: null
23111};
23112
23113
23114
23115/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23116 * Constants
23117 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
23118
23119/**
23120 * ColReorder version
23121 *  @constant  version
23122 *  @type      String
23123 *  @default   As code
23124 */
23125ColReorder.version = "1.6.2";
23126
23127
23128
23129/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
23130 * DataTables interfaces
23131 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
23132
23133// Expose
23134$.fn.dataTable.ColReorder = ColReorder;
23135$.fn.DataTable.ColReorder = ColReorder;
23136
23137
23138// Register a new feature with DataTables
23139if ( typeof $.fn.dataTable == "function" &&
23140     typeof $.fn.dataTableExt.fnVersionCheck == "function" &&
23141     $.fn.dataTableExt.fnVersionCheck('1.10.8') )
23142{
23143	$.fn.dataTableExt.aoFeatures.push( {
23144		"fnInit": function( settings ) {
23145			var table = settings.oInstance;
23146
23147			if ( ! settings._colReorder ) {
23148				var dtInit = settings.oInit;
23149				var opts = dtInit.colReorder || dtInit.oColReorder || {};
23150
23151				new ColReorder( settings, opts );
23152			}
23153			else {
23154				table.oApi._fnLog( settings, 1, "ColReorder attempted to initialise twice. Ignoring second" );
23155			}
23156
23157			return null; /* No node for DataTables to insert */
23158		},
23159		"cFeature": "R",
23160		"sFeature": "ColReorder"
23161	} );
23162}
23163else {
23164	alert( "Warning: ColReorder requires DataTables 1.10.8 or greater - www.datatables.net/download");
23165}
23166
23167
23168// Attach a listener to the document which listens for DataTables initialisation
23169// events so we can automatically initialise
23170$(document).on( 'preInit.dt.colReorder', function (e, settings) {
23171	if ( e.namespace !== 'dt' ) {
23172		return;
23173	}
23174
23175	var init = settings.oInit.colReorder;
23176	var defaults = DataTable.defaults.colReorder;
23177
23178	if ( init || defaults ) {
23179		var opts = $.extend( {}, init, defaults );
23180
23181		if ( init !== false ) {
23182			new ColReorder( settings, opts  );
23183		}
23184	}
23185} );
23186
23187
23188// API augmentation
23189$.fn.dataTable.Api.register( 'colReorder.reset()', function () {
23190	return this.iterator( 'table', function ( ctx ) {
23191		ctx._colReorder.fnReset();
23192	} );
23193} );
23194
23195$.fn.dataTable.Api.register( 'colReorder.order()', function ( set, original ) {
23196	if ( set ) {
23197		return this.iterator( 'table', function ( ctx ) {
23198			ctx._colReorder.fnOrder( set, original );
23199		} );
23200	}
23201
23202	return this.context.length ?
23203		this.context[0]._colReorder.fnOrder() :
23204		null;
23205} );
23206
23207$.fn.dataTable.Api.register( 'colReorder.transpose()', function ( idx, dir ) {
23208	return this.context.length && this.context[0]._colReorder ?
23209		this.context[0]._colReorder.fnTranspose( idx, dir ) :
23210		idx;
23211} );
23212
23213$.fn.dataTable.Api.register( 'colReorder.move()', function( from, to, drop, invalidateRows ) {
23214	if (this.context.length) {
23215		this.context[0]._colReorder.s.dt.oInstance.fnColReorder( from, to, drop, invalidateRows );
23216		this.context[0]._colReorder._fnSetColumnIndexes();
23217	}
23218	return this;
23219} );
23220
23221$.fn.dataTable.Api.register( 'colReorder.enable()', function( flag ) {
23222	return this.iterator( 'table', function ( ctx ) {
23223		if ( ctx._colReorder ) {
23224			ctx._colReorder.fnEnable( flag );
23225		}
23226	} );
23227} );
23228
23229$.fn.dataTable.Api.register( 'colReorder.disable()', function() {
23230	return this.iterator( 'table', function ( ctx ) {
23231		if ( ctx._colReorder ) {
23232			ctx._colReorder.fnDisable();
23233		}
23234	} );
23235} );
23236
23237
23238return DataTable;
23239}));
23240
23241
23242/*! FixedColumns 4.2.2
23243 * © SpryMedia Ltd - datatables.net/license
23244 */
23245
23246(function( factory ){
23247	if ( typeof define === 'function' && define.amd ) {
23248		// AMD
23249		define( ['jquery', 'datatables.net'], function ( $ ) {
23250			return factory( $, window, document );
23251		} );
23252	}
23253	else if ( typeof exports === 'object' ) {
23254		// CommonJS
23255		var jq = require('jquery');
23256		var cjsRequires = function (root, $) {
23257			if ( ! $.fn.dataTable ) {
23258				require('datatables.net')(root, $);
23259			}
23260		};
23261
23262		if (typeof window !== 'undefined') {
23263			module.exports = function (root, $) {
23264				if ( ! root ) {
23265					// CommonJS environments without a window global must pass a
23266					// root. This will give an error otherwise
23267					root = window;
23268				}
23269
23270				if ( ! $ ) {
23271					$ = jq( root );
23272				}
23273
23274				cjsRequires( root, $ );
23275				return factory( $, root, root.document );
23276			};
23277		}
23278		else {
23279			cjsRequires( window, jq );
23280			module.exports = factory( jq, window, window.document );
23281		}
23282	}
23283	else {
23284		// Browser
23285		factory( jQuery, window, document );
23286	}
23287}(function( $, window, document, undefined ) {
23288'use strict';
23289var DataTable = $.fn.dataTable;
23290
23291
23292(function () {
23293    'use strict';
23294
23295    var $$1;
23296    var dataTable;
23297    function setJQuery(jq) {
23298        $$1 = jq;
23299        dataTable = $$1.fn.dataTable;
23300    }
23301    var FixedColumns = /** @class */ (function () {
23302        function FixedColumns(settings, opts) {
23303            var _this = this;
23304            // Check that the required version of DataTables is included
23305            if (!dataTable || !dataTable.versionCheck || !dataTable.versionCheck('1.10.0')) {
23306                throw new Error('StateRestore requires DataTables 1.10 or newer');
23307            }
23308            var table = new dataTable.Api(settings);
23309            this.classes = $$1.extend(true, {}, FixedColumns.classes);
23310            // Get options from user
23311            this.c = $$1.extend(true, {}, FixedColumns.defaults, opts);
23312            // Backwards compatibility for deprecated leftColumns
23313            if ((!opts || opts.left === undefined) && this.c.leftColumns !== undefined) {
23314                this.c.left = this.c.leftColumns;
23315            }
23316            // Backwards compatibility for deprecated rightColumns
23317            if ((!opts || opts.right === undefined) && this.c.rightColumns !== undefined) {
23318                this.c.right = this.c.rightColumns;
23319            }
23320            this.s = {
23321                barWidth: 0,
23322                dt: table,
23323                rtl: $$1('body').css('direction') === 'rtl'
23324            };
23325            // Common CSS for all blockers
23326            var blockerCSS = {
23327                'bottom': '0px',
23328                'display': 'block',
23329                'position': 'absolute',
23330                'width': this.s.barWidth + 1 + 'px'
23331            };
23332            this.dom = {
23333                leftBottomBlocker: $$1('<div>')
23334                    .css(blockerCSS)
23335                    .css('left', 0)
23336                    .addClass(this.classes.leftBottomBlocker),
23337                leftTopBlocker: $$1('<div>')
23338                    .css(blockerCSS)
23339                    .css({
23340                    left: 0,
23341                    top: 0
23342                })
23343                    .addClass(this.classes.leftTopBlocker),
23344                rightBottomBlocker: $$1('<div>')
23345                    .css(blockerCSS)
23346                    .css('right', 0)
23347                    .addClass(this.classes.rightBottomBlocker),
23348                rightTopBlocker: $$1('<div>')
23349                    .css(blockerCSS)
23350                    .css({
23351                    right: 0,
23352                    top: 0
23353                })
23354                    .addClass(this.classes.rightTopBlocker)
23355            };
23356            if (this.s.dt.settings()[0]._bInitComplete) {
23357                // Fixed Columns Initialisation
23358                this._addStyles();
23359                this._setKeyTableListener();
23360            }
23361            else {
23362                table.one('init.dt', function () {
23363                    // Fixed Columns Initialisation
23364                    _this._addStyles();
23365                    _this._setKeyTableListener();
23366                });
23367            }
23368            table.on('column-sizing.dt', function () { return _this._addStyles(); });
23369            // Make class available through dt object
23370            table.settings()[0]._fixedColumns = this;
23371            return this;
23372        }
23373        /**
23374         * Getter/Setter for the `fixedColumns.left` property
23375         *
23376         * @param newVal Optional. If present this will be the new value for the number of left fixed columns
23377         * @returns The number of left fixed columns
23378         */
23379        FixedColumns.prototype.left = function (newVal) {
23380            // If the value is to change
23381            if (newVal !== undefined) {
23382                // Set the new values and redraw the columns
23383                this.c.left = newVal;
23384                this._addStyles();
23385            }
23386            return this.c.left;
23387        };
23388        /**
23389         * Getter/Setter for the `fixedColumns.left` property
23390         *
23391         * @param newVal Optional. If present this will be the new value for the number of right fixed columns
23392         * @returns The number of right fixed columns
23393         */
23394        FixedColumns.prototype.right = function (newVal) {
23395            // If the value is to change
23396            if (newVal !== undefined) {
23397                // Set the new values and redraw the columns
23398                this.c.right = newVal;
23399                this._addStyles();
23400            }
23401            return this.c.right;
23402        };
23403        /**
23404         * Iterates over the columns, fixing the appropriate ones to the left and right
23405         */
23406        FixedColumns.prototype._addStyles = function () {
23407            // Set the bar width if vertical scrolling is enabled
23408            if (this.s.dt.settings()[0].oScroll.sY) {
23409                var scroll_1 = $$1(this.s.dt.table().node()).closest('div.dataTables_scrollBody')[0];
23410                var barWidth = this.s.dt.settings()[0].oBrowser.barWidth;
23411                if (scroll_1.offsetWidth - scroll_1.clientWidth >= barWidth) {
23412                    this.s.barWidth = barWidth;
23413                }
23414                else {
23415                    this.s.barWidth = 0;
23416                }
23417                this.dom.rightTopBlocker.css('width', this.s.barWidth + 1);
23418                this.dom.leftTopBlocker.css('width', this.s.barWidth + 1);
23419                this.dom.rightBottomBlocker.css('width', this.s.barWidth + 1);
23420                this.dom.leftBottomBlocker.css('width', this.s.barWidth + 1);
23421            }
23422            var parentDiv = null;
23423            // Get the header and it's height
23424            var header = this.s.dt.column(0).header();
23425            var headerHeight = null;
23426            if (header !== null) {
23427                header = $$1(header);
23428                headerHeight = header.outerHeight() + 1;
23429                parentDiv = $$1(header.closest('div.dataTables_scroll')).css('position', 'relative');
23430            }
23431            // Get the footer and it's height
23432            var footer = this.s.dt.column(0).footer();
23433            var footerHeight = null;
23434            if (footer !== null) {
23435                footer = $$1(footer);
23436                footerHeight = footer.outerHeight();
23437                // Only attempt to retrieve the parentDiv if it has not been retrieved already
23438                if (parentDiv === null) {
23439                    parentDiv = $$1(footer.closest('div.dataTables_scroll')).css('position', 'relative');
23440                }
23441            }
23442            // Get the number of columns in the table - this is used often so better to only make 1 api call
23443            var numCols = this.s.dt.columns().data().toArray().length;
23444            // Tracker for the number of pixels should be left to the left of the table
23445            var distLeft = 0;
23446            // Sometimes the headers have slightly different widths so need to track them individually
23447            var headLeft = 0;
23448            // Get all of the row elements in the table
23449            var rows = $$1(this.s.dt.table().node()).children('tbody').children('tr');
23450            var invisibles = 0;
23451            // When working from right to left we need to know how many are invisible before a point,
23452            // without including those that are invisible after
23453            var prevInvisible = new Map();
23454            // Iterate over all of the columns
23455            for (var i = 0; i < numCols; i++) {
23456                var column = this.s.dt.column(i);
23457                // Set the map for the previous column
23458                if (i > 0) {
23459                    prevInvisible.set(i - 1, invisibles);
23460                }
23461                if (!column.visible()) {
23462                    invisibles++;
23463                    continue;
23464                }
23465                // Get the columns header and footer element
23466                var colHeader = $$1(column.header());
23467                var colFooter = $$1(column.footer());
23468                // If i is less than the value of left then this column should be fixed left
23469                if (i - invisibles < this.c.left) {
23470                    $$1(this.s.dt.table().node()).addClass(this.classes.tableFixedLeft);
23471                    parentDiv.addClass(this.classes.tableFixedLeft);
23472                    // Add the width of the previous node - only if we are on atleast the second column
23473                    if (i - invisibles > 0) {
23474                        var prevIdx = i;
23475                        // Simply using the number of hidden columns doesn't work here,
23476                        // if the first is hidden then this would be thrown off
23477                        while (prevIdx + 1 < numCols) {
23478                            var prevCol = this.s.dt.column(prevIdx - 1, { page: 'current' });
23479                            if (prevCol.visible()) {
23480                                distLeft += $$1(prevCol.nodes()[0]).outerWidth();
23481                                headLeft += prevCol.header() ?
23482                                    $$1(prevCol.header()).outerWidth() :
23483                                    prevCol.footer() ?
23484                                        $$1(prevCol.header()).outerWidth() :
23485                                        0;
23486                                break;
23487                            }
23488                            prevIdx--;
23489                        }
23490                    }
23491                    // Iterate over all of the rows, fixing the cell to the left
23492                    for (var _i = 0, rows_1 = rows; _i < rows_1.length; _i++) {
23493                        var row = rows_1[_i];
23494                        $$1($$1(row).children()[i - invisibles])
23495                            .css(this._getCellCSS(false, distLeft, 'left'))
23496                            .addClass(this.classes.fixedLeft);
23497                    }
23498                    // Add the css for the header and the footer
23499                    colHeader
23500                        .css(this._getCellCSS(true, headLeft, 'left'))
23501                        .addClass(this.classes.fixedLeft);
23502                    colFooter
23503                        .css(this._getCellCSS(true, headLeft, 'left'))
23504                        .addClass(this.classes.fixedLeft);
23505                }
23506                else {
23507                    // Iteriate through all of the rows, making sure they aren't currently trying to fix left
23508                    for (var _a = 0, rows_2 = rows; _a < rows_2.length; _a++) {
23509                        var row = rows_2[_a];
23510                        var cell = $$1($$1(row).children()[i - invisibles]);
23511                        // If the cell is trying to fix to the left, remove the class and the css
23512                        if (cell.hasClass(this.classes.fixedLeft)) {
23513                            cell
23514                                .css(this._clearCellCSS('left'))
23515                                .removeClass(this.classes.fixedLeft);
23516                        }
23517                    }
23518                    // Make sure the header for this column isn't fixed left
23519                    if (colHeader.hasClass(this.classes.fixedLeft)) {
23520                        colHeader
23521                            .css(this._clearCellCSS('left'))
23522                            .removeClass(this.classes.fixedLeft);
23523                    }
23524                    // Make sure the footer for this column isn't fixed left
23525                    if (colFooter.hasClass(this.classes.fixedLeft)) {
23526                        colFooter
23527                            .css(this._clearCellCSS('left'))
23528                            .removeClass(this.classes.fixedLeft);
23529                    }
23530                }
23531            }
23532            var distRight = 0;
23533            var headRight = 0;
23534            // Counter for the number of invisible columns so far
23535            var rightInvisibles = 0;
23536            for (var i = numCols - 1; i >= 0; i--) {
23537                var column = this.s.dt.column(i);
23538                // If a column is invisible just skip it
23539                if (!column.visible()) {
23540                    rightInvisibles++;
23541                    continue;
23542                }
23543                // Get the columns header and footer element
23544                var colHeader = $$1(column.header());
23545                var colFooter = $$1(column.footer());
23546                // Get the number of visible columns that came before this one
23547                var prev = prevInvisible.get(i);
23548                if (prev === undefined) {
23549                    // If it wasn't set then it was the last column so just use the final value
23550                    prev = invisibles;
23551                }
23552                if (i + rightInvisibles >= numCols - this.c.right) {
23553                    $$1(this.s.dt.table().node()).addClass(this.classes.tableFixedRight);
23554                    parentDiv.addClass(this.classes.tableFixedRight);
23555                    // Add the widht of the previous node, only if we are on atleast the second column
23556                    if (i + 1 + rightInvisibles < numCols) {
23557                        var prevIdx = i;
23558                        // Simply using the number of hidden columns doesn't work here,
23559                        // if the first is hidden then this would be thrown off
23560                        while (prevIdx + 1 < numCols) {
23561                            var prevCol = this.s.dt.column(prevIdx + 1, { page: 'current' });
23562                            if (prevCol.visible()) {
23563                                distRight += $$1(prevCol.nodes()[0]).outerWidth();
23564                                headRight += prevCol.header() ?
23565                                    $$1(prevCol.header()).outerWidth() :
23566                                    prevCol.footer() ?
23567                                        $$1(prevCol.header()).outerWidth() :
23568                                        0;
23569                                break;
23570                            }
23571                            prevIdx++;
23572                        }
23573                    }
23574                    // Iterate over all of the rows, fixing the cell to the right
23575                    for (var _b = 0, rows_3 = rows; _b < rows_3.length; _b++) {
23576                        var row = rows_3[_b];
23577                        $$1($$1(row).children()[i - prev])
23578                            .css(this._getCellCSS(false, distRight, 'right'))
23579                            .addClass(this.classes.fixedRight);
23580                    }
23581                    // Add the css for the header and the footer
23582                    colHeader
23583                        .css(this._getCellCSS(true, headRight, 'right'))
23584                        .addClass(this.classes.fixedRight);
23585                    colFooter
23586                        .css(this._getCellCSS(true, headRight, 'right'))
23587                        .addClass(this.classes.fixedRight);
23588                }
23589                else {
23590                    // Iteriate through all of the rows, making sure they aren't currently trying to fix right
23591                    for (var _c = 0, rows_4 = rows; _c < rows_4.length; _c++) {
23592                        var row = rows_4[_c];
23593                        var cell = $$1($$1(row).children()[i - prev]);
23594                        // If the cell is trying to fix to the right, remove the class and the css
23595                        if (cell.hasClass(this.classes.fixedRight)) {
23596                            cell
23597                                .css(this._clearCellCSS('right'))
23598                                .removeClass(this.classes.fixedRight);
23599                        }
23600                    }
23601                    // Make sure the header for this column isn't fixed right
23602                    if (colHeader.hasClass(this.classes.fixedRight)) {
23603                        colHeader
23604                            .css(this._clearCellCSS('right'))
23605                            .removeClass(this.classes.fixedRight);
23606                    }
23607                    // Make sure the footer for this column isn't fixed right
23608                    if (colFooter.hasClass(this.classes.fixedRight)) {
23609                        colFooter
23610                            .css(this._clearCellCSS('right'))
23611                            .removeClass(this.classes.fixedRight);
23612                    }
23613                }
23614            }
23615            // If there is a header with the index class and reading rtl then add right top blocker
23616            if (header) {
23617                if (!this.s.rtl) {
23618                    this.dom.rightTopBlocker.outerHeight(headerHeight);
23619                    parentDiv.append(this.dom.rightTopBlocker);
23620                }
23621                else {
23622                    this.dom.leftTopBlocker.outerHeight(headerHeight);
23623                    parentDiv.append(this.dom.leftTopBlocker);
23624                }
23625            }
23626            // If there is a footer with the index class and reading rtl then add right bottom blocker
23627            if (footer) {
23628                if (!this.s.rtl) {
23629                    this.dom.rightBottomBlocker.outerHeight(footerHeight);
23630                    parentDiv.append(this.dom.rightBottomBlocker);
23631                }
23632                else {
23633                    this.dom.leftBottomBlocker.outerHeight(footerHeight);
23634                    parentDiv.append(this.dom.leftBottomBlocker);
23635                }
23636            }
23637        };
23638        /**
23639         * Gets the correct CSS for the cell, header or footer based on options provided
23640         *
23641         * @param header Whether this cell is a header or a footer
23642         * @param dist The distance that the cell should be moved away from the edge
23643         * @param lr Indicator of fixing to the left or the right
23644         * @returns An object containing the correct css
23645         */
23646        FixedColumns.prototype._getCellCSS = function (header, dist, lr) {
23647            if (lr === 'left') {
23648                return this.s.rtl
23649                    ? {
23650                        position: 'sticky',
23651                        right: dist + 'px'
23652                    }
23653                    : {
23654                        left: dist + 'px',
23655                        position: 'sticky'
23656                    };
23657            }
23658            else {
23659                return this.s.rtl
23660                    ? {
23661                        left: dist + (header ? this.s.barWidth : 0) + 'px',
23662                        position: 'sticky'
23663                    }
23664                    : {
23665                        position: 'sticky',
23666                        right: dist + (header ? this.s.barWidth : 0) + 'px'
23667                    };
23668            }
23669        };
23670        /**
23671         * Gets the css that is required to clear the fixing to a side
23672         *
23673         * @param lr Indicator of fixing to the left or the right
23674         * @returns An object containing the correct css
23675         */
23676        FixedColumns.prototype._clearCellCSS = function (lr) {
23677            if (lr === 'left') {
23678                return !this.s.rtl ?
23679                    {
23680                        left: '',
23681                        position: ''
23682                    } :
23683                    {
23684                        position: '',
23685                        right: ''
23686                    };
23687            }
23688            else {
23689                return !this.s.rtl ?
23690                    {
23691                        position: '',
23692                        right: ''
23693                    } :
23694                    {
23695                        left: '',
23696                        position: ''
23697                    };
23698            }
23699        };
23700        FixedColumns.prototype._setKeyTableListener = function () {
23701            var _this = this;
23702            this.s.dt.on('key-focus', function (e, dt, cell) {
23703                var cellPos = $$1(cell.node()).offset();
23704                var scroll = $$1($$1(_this.s.dt.table().node()).closest('div.dataTables_scrollBody'));
23705                // If there are fixed columns to the left
23706                if (_this.c.left > 0) {
23707                    // Get the rightmost left fixed column header, it's position and it's width
23708                    var rightMost = $$1(_this.s.dt.column(_this.c.left - 1).header());
23709                    var rightMostPos = rightMost.offset();
23710                    var rightMostWidth = rightMost.outerWidth();
23711                    // If the current highlighted cell is left of the rightmost cell on the screen
23712                    if (cellPos.left < rightMostPos.left + rightMostWidth) {
23713                        // Scroll it into view
23714                        var currScroll = scroll.scrollLeft();
23715                        scroll.scrollLeft(currScroll - (rightMostPos.left + rightMostWidth - cellPos.left));
23716                    }
23717                }
23718                // If there are fixed columns to the right
23719                if (_this.c.right > 0) {
23720                    // Get the number of columns and the width of the cell as doing right side calc
23721                    var numCols = _this.s.dt.columns().data().toArray().length;
23722                    var cellWidth = $$1(cell.node()).outerWidth();
23723                    // Get the leftmost right fixed column header and it's position
23724                    var leftMost = $$1(_this.s.dt.column(numCols - _this.c.right).header());
23725                    var leftMostPos = leftMost.offset();
23726                    // If the current highlighted cell is right of the leftmost cell on the screen
23727                    if (cellPos.left + cellWidth > leftMostPos.left) {
23728                        // Scroll it into view
23729                        var currScroll = scroll.scrollLeft();
23730                        scroll.scrollLeft(currScroll - (leftMostPos.left - (cellPos.left + cellWidth)));
23731                    }
23732                }
23733            });
23734            // Whenever a draw occurs there is potential for the data to have changed and therefore also the column widths
23735            // Therefore it is necessary to recalculate the values for the fixed columns
23736            this.s.dt.on('draw', function () {
23737                _this._addStyles();
23738            });
23739            this.s.dt.on('column-reorder', function () {
23740                _this._addStyles();
23741            });
23742            this.s.dt.on('column-visibility', function (e, settings, column, state, recalc) {
23743                if (recalc && !settings.bDestroying) {
23744                    setTimeout(function () {
23745                        _this._addStyles();
23746                    }, 50);
23747                }
23748            });
23749        };
23750        FixedColumns.version = '4.2.2';
23751        FixedColumns.classes = {
23752            fixedLeft: 'dtfc-fixed-left',
23753            fixedRight: 'dtfc-fixed-right',
23754            leftBottomBlocker: 'dtfc-left-bottom-blocker',
23755            leftTopBlocker: 'dtfc-left-top-blocker',
23756            rightBottomBlocker: 'dtfc-right-bottom-blocker',
23757            rightTopBlocker: 'dtfc-right-top-blocker',
23758            tableFixedLeft: 'dtfc-has-left',
23759            tableFixedRight: 'dtfc-has-right'
23760        };
23761        FixedColumns.defaults = {
23762            i18n: {
23763                button: 'FixedColumns'
23764            },
23765            left: 1,
23766            right: 0
23767        };
23768        return FixedColumns;
23769    }());
23770
23771    /*! FixedColumns 4.2.2
23772     * © SpryMedia Ltd - datatables.net/license
23773     */
23774    setJQuery($);
23775    $.fn.dataTable.FixedColumns = FixedColumns;
23776    $.fn.DataTable.FixedColumns = FixedColumns;
23777    var apiRegister = DataTable.Api.register;
23778    apiRegister('fixedColumns()', function () {
23779        return this;
23780    });
23781    apiRegister('fixedColumns().left()', function (newVal) {
23782        var ctx = this.context[0];
23783        if (newVal !== undefined) {
23784            ctx._fixedColumns.left(newVal);
23785            return this;
23786        }
23787        else {
23788            return ctx._fixedColumns.left();
23789        }
23790    });
23791    apiRegister('fixedColumns().right()', function (newVal) {
23792        var ctx = this.context[0];
23793        if (newVal !== undefined) {
23794            ctx._fixedColumns.right(newVal);
23795            return this;
23796        }
23797        else {
23798            return ctx._fixedColumns.right();
23799        }
23800    });
23801    DataTable.ext.buttons.fixedColumns = {
23802        action: function (e, dt, node, config) {
23803            if ($(node).attr('active')) {
23804                $(node).removeAttr('active').removeClass('active');
23805                dt.fixedColumns().left(0);
23806                dt.fixedColumns().right(0);
23807            }
23808            else {
23809                $(node).attr('active', 'true').addClass('active');
23810                dt.fixedColumns().left(config.config.left);
23811                dt.fixedColumns().right(config.config.right);
23812            }
23813        },
23814        config: {
23815            left: 1,
23816            right: 0
23817        },
23818        init: function (dt, node, config) {
23819            if (dt.settings()[0]._fixedColumns === undefined) {
23820                _init(dt.settings(), config);
23821            }
23822            $(node).attr('active', 'true').addClass('active');
23823            dt.button(node).text(config.text || dt.i18n('buttons.fixedColumns', dt.settings()[0]._fixedColumns.c.i18n.button));
23824        },
23825        text: null
23826    };
23827    function _init(settings, options) {
23828        if (options === void 0) { options = null; }
23829        var api = new DataTable.Api(settings);
23830        var opts = options
23831            ? options
23832            : api.init().fixedColumns || DataTable.defaults.fixedColumns;
23833        var fixedColumns = new FixedColumns(api, opts);
23834        return fixedColumns;
23835    }
23836    // Attach a listener to the document which listens for DataTables initialisation
23837    // events so we can automatically initialise
23838    $(document).on('plugin-init.dt', function (e, settings) {
23839        if (e.namespace !== 'dt') {
23840            return;
23841        }
23842        if (settings.oInit.fixedColumns ||
23843            DataTable.defaults.fixedColumns) {
23844            if (!settings._fixedColumns) {
23845                _init(settings, null);
23846            }
23847        }
23848    });
23849
23850})();
23851
23852
23853return DataTable;
23854}));
23855
23856
23857/*! FixedHeader 3.3.2
23858 * © SpryMedia Ltd - datatables.net/license
23859 */
23860
23861(function( factory ){
23862	if ( typeof define === 'function' && define.amd ) {
23863		// AMD
23864		define( ['jquery', 'datatables.net'], function ( $ ) {
23865			return factory( $, window, document );
23866		} );
23867	}
23868	else if ( typeof exports === 'object' ) {
23869		// CommonJS
23870		var jq = require('jquery');
23871		var cjsRequires = function (root, $) {
23872			if ( ! $.fn.dataTable ) {
23873				require('datatables.net')(root, $);
23874			}
23875		};
23876
23877		if (typeof window !== 'undefined') {
23878			module.exports = function (root, $) {
23879				if ( ! root ) {
23880					// CommonJS environments without a window global must pass a
23881					// root. This will give an error otherwise
23882					root = window;
23883				}
23884
23885				if ( ! $ ) {
23886					$ = jq( root );
23887				}
23888
23889				cjsRequires( root, $ );
23890				return factory( $, root, root.document );
23891			};
23892		}
23893		else {
23894			cjsRequires( window, jq );
23895			module.exports = factory( jq, window, window.document );
23896		}
23897	}
23898	else {
23899		// Browser
23900		factory( jQuery, window, document );
23901	}
23902}(function( $, window, document, undefined ) {
23903'use strict';
23904var DataTable = $.fn.dataTable;
23905
23906
23907
23908/**
23909 * @summary     FixedHeader
23910 * @description Fix a table's header or footer, so it is always visible while
23911 *              scrolling
23912 * @version     3.3.2
23913 * @author      SpryMedia Ltd (www.sprymedia.co.uk)
23914 * @contact     www.sprymedia.co.uk
23915 * @copyright   SpryMedia Ltd.
23916 *
23917 * This source file is free software, available under the following license:
23918 *   MIT license - http://datatables.net/license/mit
23919 *
23920 * This source file is distributed in the hope that it will be useful, but
23921 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
23922 * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
23923 *
23924 * For details please refer to: http://www.datatables.net
23925 */
23926
23927var _instCounter = 0;
23928
23929var FixedHeader = function ( dt, config ) {
23930	// Sanity check - you just know it will happen
23931	if ( ! (this instanceof FixedHeader) ) {
23932		throw "FixedHeader must be initialised with the 'new' keyword.";
23933	}
23934
23935	// Allow a boolean true for defaults
23936	if ( config === true ) {
23937		config = {};
23938	}
23939
23940	dt = new DataTable.Api( dt );
23941
23942	this.c = $.extend( true, {}, FixedHeader.defaults, config );
23943
23944	this.s = {
23945		dt: dt,
23946		position: {
23947			theadTop: 0,
23948			tbodyTop: 0,
23949			tfootTop: 0,
23950			tfootBottom: 0,
23951			width: 0,
23952			left: 0,
23953			tfootHeight: 0,
23954			theadHeight: 0,
23955			windowHeight: $(window).height(),
23956			visible: true
23957		},
23958		headerMode: null,
23959		footerMode: null,
23960		autoWidth: dt.settings()[0].oFeatures.bAutoWidth,
23961		namespace: '.dtfc'+(_instCounter++),
23962		scrollLeft: {
23963			header: -1,
23964			footer: -1
23965		},
23966		enable: true
23967	};
23968
23969	this.dom = {
23970		floatingHeader: null,
23971		thead: $(dt.table().header()),
23972		tbody: $(dt.table().body()),
23973		tfoot: $(dt.table().footer()),
23974		header: {
23975			host: null,
23976			floating: null,
23977			floatingParent: $('<div class="dtfh-floatingparent">'),
23978			placeholder: null
23979		},
23980		footer: {
23981			host: null,
23982			floating: null,
23983			floatingParent: $('<div class="dtfh-floatingparent">'),
23984			placeholder: null
23985		}
23986	};
23987
23988	this.dom.header.host = this.dom.thead.parent();
23989	this.dom.footer.host = this.dom.tfoot.parent();
23990
23991	var dtSettings = dt.settings()[0];
23992	if ( dtSettings._fixedHeader ) {
23993		throw "FixedHeader already initialised on table "+dtSettings.nTable.id;
23994	}
23995
23996	dtSettings._fixedHeader = this;
23997
23998	this._constructor();
23999};
24000
24001
24002/*
24003 * Variable: FixedHeader
24004 * Purpose:  Prototype for FixedHeader
24005 * Scope:    global
24006 */
24007$.extend( FixedHeader.prototype, {
24008	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
24009	 * API methods
24010	 */
24011
24012	/**
24013	 * Kill off FH and any events
24014	 */
24015	destroy: function () {
24016		var dom = this.dom;
24017
24018		this.s.dt.off( '.dtfc' );
24019		$(window).off( this.s.namespace );
24020
24021		// Remove clones of FC blockers
24022		if (dom.header.rightBlocker) {
24023			dom.header.rightBlocker.remove();
24024		}
24025		if (dom.header.leftBlocker) {
24026			dom.header.leftBlocker.remove();
24027		}
24028		if (dom.footer.rightBlocker) {
24029			dom.footer.rightBlocker.remove();
24030		}
24031		if (dom.footer.leftBlocker) {
24032			dom.footer.leftBlocker.remove();
24033		}
24034
24035		if ( this.c.header ) {
24036			this._modeChange( 'in-place', 'header', true );
24037		}
24038
24039		if ( this.c.footer && dom.tfoot.length ) {
24040			this._modeChange( 'in-place', 'footer', true );
24041		}
24042	},
24043
24044	/**
24045	 * Enable / disable the fixed elements
24046	 *
24047	 * @param  {boolean} enable `true` to enable, `false` to disable
24048	 */
24049	enable: function ( enable, update )
24050	{
24051		this.s.enable = enable;
24052
24053		if ( update || update === undefined ) {
24054			this._positions();
24055			this._scroll( true );
24056		}
24057	},
24058
24059	/**
24060	 * Get enabled status
24061	 */
24062	enabled: function ()
24063	{
24064		return this.s.enable;
24065	},
24066	
24067	/**
24068	 * Set header offset 
24069	 *
24070	 * @param  {int} new value for headerOffset
24071	 */
24072	headerOffset: function ( offset )
24073	{
24074		if ( offset !== undefined ) {
24075			this.c.headerOffset = offset;
24076			this.update();
24077		}
24078
24079		return this.c.headerOffset;
24080	},
24081	
24082	/**
24083	 * Set footer offset
24084	 *
24085	 * @param  {int} new value for footerOffset
24086	 */
24087	footerOffset: function ( offset )
24088	{
24089		if ( offset !== undefined ) {
24090			this.c.footerOffset = offset;
24091			this.update();
24092		}
24093
24094		return this.c.footerOffset;
24095	},
24096
24097	
24098	/**
24099	 * Recalculate the position of the fixed elements and force them into place
24100	 */
24101	update: function (force)
24102	{
24103		if (! this.s.enable) {
24104			return;
24105		}
24106
24107		var table = this.s.dt.table().node();
24108
24109		if ( $(table).is(':visible') ) {
24110			this.enable( true, false );
24111		}
24112		else {
24113			this.enable( false, false );
24114		}
24115
24116		// Don't update if header is not in the document atm (due to
24117		// async events)
24118		if ($(table).children('thead').length === 0) {
24119			return;
24120		}
24121
24122		this._positions();
24123		this._scroll( force !== undefined ? force : true );
24124	},
24125
24126
24127	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
24128	 * Constructor
24129	 */
24130	
24131	/**
24132	 * FixedHeader constructor - adding the required event listeners and
24133	 * simple initialisation
24134	 *
24135	 * @private
24136	 */
24137	_constructor: function ()
24138	{
24139		var that = this;
24140		var dt = this.s.dt;
24141
24142		$(window)
24143			.on( 'scroll'+this.s.namespace, function () {
24144				that._scroll();
24145			} )
24146			.on( 'resize'+this.s.namespace, DataTable.util.throttle( function () {
24147				that.s.position.windowHeight = $(window).height();
24148				that.update();
24149			}, 50 ) );
24150
24151		var autoHeader = $('.fh-fixedHeader');
24152		if ( ! this.c.headerOffset && autoHeader.length ) {
24153			this.c.headerOffset = autoHeader.outerHeight();
24154		}
24155
24156		var autoFooter = $('.fh-fixedFooter');
24157		if ( ! this.c.footerOffset && autoFooter.length ) {
24158			this.c.footerOffset = autoFooter.outerHeight();
24159		}
24160
24161		dt
24162			.on( 'column-reorder.dt.dtfc column-visibility.dt.dtfc column-sizing.dt.dtfc responsive-display.dt.dtfc', function (e, ctx) {
24163				that.update();
24164			} )
24165			.on( 'draw.dt.dtfc', function (e, ctx) {
24166				// For updates from our own table, don't reclone, but for all others, do
24167				that.update(ctx === dt.settings()[0] ? false : true);
24168			} );
24169
24170		dt.on( 'destroy.dtfc', function () {
24171			that.destroy();
24172		} );
24173
24174		this._positions();
24175		this._scroll();
24176	},
24177
24178
24179	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
24180	 * Private methods
24181	 */
24182
24183	/**
24184	 * Clone a fixed item to act as a place holder for the original element
24185	 * which is moved into a clone of the table element, and moved around the
24186	 * document to give the fixed effect.
24187	 *
24188	 * @param  {string}  item  'header' or 'footer'
24189	 * @param  {boolean} force Force the clone to happen, or allow automatic
24190	 *   decision (reuse existing if available)
24191	 * @private
24192	 */
24193	_clone: function ( item, force )
24194	{
24195		var that = this;
24196		var dt = this.s.dt;
24197		var itemDom = this.dom[ item ];
24198		var itemElement = item === 'header' ?
24199			this.dom.thead :
24200			this.dom.tfoot;
24201
24202		// If footer and scrolling is enabled then we don't clone
24203		// Instead the table's height is decreased accordingly - see `_scroll()`
24204		if (item === 'footer' && this._scrollEnabled()) {
24205			return;
24206		}	
24207
24208		if ( ! force && itemDom.floating ) {
24209			// existing floating element - reuse it
24210			itemDom.floating.removeClass( 'fixedHeader-floating fixedHeader-locked' );
24211		}
24212		else {
24213			var docScrollLeft = $(document).scrollLeft();
24214			var docScrollTop = $(document).scrollTop();
24215
24216			if ( itemDom.floating ) {
24217				if(itemDom.placeholder !== null) {
24218					itemDom.placeholder.remove();
24219				}
24220				this._unsize( item );
24221				itemDom.floating.children().detach();
24222				itemDom.floating.remove();
24223			}
24224
24225			var tableNode = $(dt.table().node()); 
24226			var scrollBody = $(tableNode.parent());
24227			var scrollEnabled = this._scrollEnabled();
24228
24229			itemDom.floating = $( dt.table().node().cloneNode( false ) )
24230				.attr( 'aria-hidden', 'true' )
24231				.css({
24232					'table-layout': 'fixed',
24233					top: 0,
24234					left: 0
24235				})
24236				.removeAttr( 'id' )
24237				.append( itemElement );
24238
24239			itemDom.floatingParent
24240				.css({
24241					width: scrollBody.width(),
24242					overflow: 'hidden',
24243					height: 'fit-content',
24244					position: 'fixed',
24245					left: scrollEnabled ? tableNode.offset().left + scrollBody.scrollLeft() : 0
24246				})
24247				.css(
24248					item === 'header' ?
24249						{
24250							top: this.c.headerOffset,
24251							bottom: ''
24252						} :
24253						{
24254							top: '',
24255							bottom: this.c.footerOffset
24256						}
24257				)
24258				.addClass(item === 'footer' ? 'dtfh-floatingparentfoot' : 'dtfh-floatingparenthead')
24259				.append(itemDom.floating)
24260				.appendTo( 'body' );
24261
24262			this._stickyPosition(itemDom.floating, '-');
24263
24264			var scrollLeftUpdate = function () {
24265				var scrollLeft = scrollBody.scrollLeft()
24266				that.s.scrollLeft = {footer: scrollLeft, header: scrollLeft};
24267				itemDom.floatingParent.scrollLeft(that.s.scrollLeft.header);
24268			}
24269
24270			scrollLeftUpdate();
24271			scrollBody
24272				.off('scroll.dtfh')
24273				.on('scroll.dtfh', scrollLeftUpdate);
24274
24275			// Insert a fake thead/tfoot into the DataTable to stop it jumping around
24276			itemDom.placeholder = itemElement.clone( false );
24277			itemDom.placeholder
24278				.find( '*[id]' )
24279				.removeAttr( 'id' );
24280
24281			itemDom.host.prepend( itemDom.placeholder );
24282
24283			// Clone widths
24284			this._matchWidths( itemDom.placeholder, itemDom.floating );
24285
24286			// The above action will remove the table header, potentially causing the table to
24287			// collapse to a smaller size, before it is then re-inserted (append). The result
24288			// can be that the document, if scrolling, can "jump".
24289			$(document)
24290				.scrollTop(docScrollTop)
24291				.scrollLeft(docScrollLeft);
24292		}
24293	},
24294
24295	/**
24296	 * This method sets the sticky position of the header elements to match fixed columns
24297	 * @param {JQuery<HTMLElement>} el 
24298	 * @param {string} sign 
24299	 */
24300	_stickyPosition: function(el, sign) {
24301		if (this._scrollEnabled()) {
24302			var that = this
24303			var rtl = $(that.s.dt.table().node()).css('direction') === 'rtl';
24304
24305			el.find('th').each(function() {
24306				// Find out if fixed header has previously set this column
24307				if ($(this).css('position') === 'sticky') {
24308					var right = $(this).css('right');
24309					var left = $(this).css('left');
24310					if (right !== 'auto' && !rtl) {
24311						// New position either adds or dismisses the barWidth
24312						var potential = +right.replace(/px/g, '') + (sign === '-' ? -1 : 1) * that.s.dt.settings()[0].oBrowser.barWidth;
24313						$(this).css('right', potential > 0 ? potential : 0);
24314					}
24315					else if(left !== 'auto' && rtl) {
24316						var potential = +left.replace(/px/g, '') + (sign === '-' ? -1 : 1) * that.s.dt.settings()[0].oBrowser.barWidth;
24317						$(this).css('left', potential > 0 ? potential : 0);
24318					}
24319				}
24320			});
24321		}
24322	},
24323
24324	/**
24325	 * Copy widths from the cells in one element to another. This is required
24326	 * for the footer as the footer in the main table takes its sizes from the
24327	 * header columns. That isn't present in the footer so to have it still
24328	 * align correctly, the sizes need to be copied over. It is also required
24329	 * for the header when auto width is not enabled
24330	 *
24331	 * @param  {jQuery} from Copy widths from
24332	 * @param  {jQuery} to   Copy widths to
24333	 * @private
24334	 */
24335	_matchWidths: function ( from, to ) {
24336		var get = function ( name ) {
24337			return $(name, from)
24338				.map( function () {
24339					return $(this).css('width').replace(/[^\d\.]/g, '') * 1;
24340				} ).toArray();
24341		};
24342
24343		var set = function ( name, toWidths ) {
24344			$(name, to).each( function ( i ) {
24345				$(this).css( {
24346					width: toWidths[i],
24347					minWidth: toWidths[i]
24348				} );
24349			} );
24350		};
24351
24352		var thWidths = get( 'th' );
24353		var tdWidths = get( 'td' );
24354
24355		set( 'th', thWidths );
24356		set( 'td', tdWidths );
24357	},
24358
24359	/**
24360	 * Remove assigned widths from the cells in an element. This is required
24361	 * when inserting the footer back into the main table so the size is defined
24362	 * by the header columns and also when auto width is disabled in the
24363	 * DataTable.
24364	 *
24365	 * @param  {string} item The `header` or `footer`
24366	 * @private
24367	 */
24368	_unsize: function ( item ) {
24369		var el = this.dom[ item ].floating;
24370
24371		if ( el && (item === 'footer' || (item === 'header' && ! this.s.autoWidth)) ) {
24372			$('th, td', el).css( {
24373				width: '',
24374				minWidth: ''
24375			} );
24376		}
24377		else if ( el && item === 'header' ) {
24378			$('th, td', el).css( 'min-width', '' );
24379		}
24380	},
24381
24382	/**
24383	 * Reposition the floating elements to take account of horizontal page
24384	 * scroll
24385	 *
24386	 * @param  {string} item       The `header` or `footer`
24387	 * @param  {int}    scrollLeft Document scrollLeft
24388	 * @private
24389	 */
24390	_horizontal: function ( item, scrollLeft )
24391	{
24392		var itemDom = this.dom[ item ];
24393		var position = this.s.position;
24394		var lastScrollLeft = this.s.scrollLeft;
24395
24396		if ( itemDom.floating && lastScrollLeft[ item ] !== scrollLeft ) {
24397			// If scrolling is enabled we need to match the floating header to the body
24398			if (this._scrollEnabled()) {
24399				var newScrollLeft = $($(this.s.dt.table().node()).parent()).scrollLeft()
24400				itemDom.floating.scrollLeft(newScrollLeft);
24401				itemDom.floatingParent.scrollLeft(newScrollLeft);
24402			}
24403
24404			lastScrollLeft[ item ] = scrollLeft;
24405		}
24406	},
24407
24408	/**
24409	 * Change from one display mode to another. Each fixed item can be in one
24410	 * of:
24411	 *
24412	 * * `in-place` - In the main DataTable
24413	 * * `in` - Floating over the DataTable
24414	 * * `below` - (Header only) Fixed to the bottom of the table body
24415	 * * `above` - (Footer only) Fixed to the top of the table body
24416	 * 
24417	 * @param  {string}  mode        Mode that the item should be shown in
24418	 * @param  {string}  item        'header' or 'footer'
24419	 * @param  {boolean} forceChange Force a redraw of the mode, even if already
24420	 *     in that mode.
24421	 * @private
24422	 */
24423	_modeChange: function ( mode, item, forceChange )
24424	{
24425		var dt = this.s.dt;
24426		var itemDom = this.dom[ item ];
24427		var position = this.s.position;
24428
24429		// Just determine if scroll is enabled once
24430		var scrollEnabled = this._scrollEnabled();
24431
24432		// If footer and scrolling is enabled then we don't clone
24433		// Instead the table's height is decreased accordingly - see `_scroll()`
24434		if (item === 'footer' && scrollEnabled) {
24435			return;
24436		}		
24437
24438		// It isn't trivial to add a !important css attribute...
24439		var importantWidth = function (w) {
24440			itemDom.floating.attr('style', function(i,s) {
24441				return (s || '') + 'width: '+w+'px !important;';
24442			});
24443
24444			// If not scrolling also have to update the floatingParent
24445			if (!scrollEnabled) {
24446				itemDom.floatingParent.attr('style', function(i,s) {
24447					return (s || '') + 'width: '+w+'px !important;';
24448				});
24449			}
24450		};
24451
24452		// Record focus. Browser's will cause input elements to loose focus if
24453		// they are inserted else where in the doc
24454		var tablePart = this.dom[ item==='footer' ? 'tfoot' : 'thead' ];
24455		var focus = $.contains( tablePart[0], document.activeElement ) ?
24456			document.activeElement :
24457			null;
24458		var scrollBody = $($(this.s.dt.table().node()).parent());
24459
24460		if ( mode === 'in-place' ) {
24461			// Insert the header back into the table's real header
24462			if ( itemDom.placeholder ) {
24463				itemDom.placeholder.remove();
24464				itemDom.placeholder = null;
24465			}
24466
24467			this._unsize( item );
24468
24469			if ( item === 'header' ) {
24470				itemDom.host.prepend( tablePart );
24471			}
24472			else {
24473				itemDom.host.append( tablePart );
24474			}
24475
24476			if ( itemDom.floating ) {
24477				itemDom.floating.remove();
24478				itemDom.floating = null;
24479				this._stickyPosition(itemDom.host, '+');
24480			}
24481
24482			if ( itemDom.floatingParent ) {
24483				itemDom.floatingParent.remove();
24484			}
24485
24486			$($(itemDom.host.parent()).parent()).scrollLeft(scrollBody.scrollLeft())
24487		}
24488		else if ( mode === 'in' ) {
24489			// Remove the header from the read header and insert into a fixed
24490			// positioned floating table clone
24491			this._clone( item, forceChange );
24492
24493			// Get useful position values
24494			var scrollOffset = scrollBody.offset();
24495			var windowTop = $(document).scrollTop();
24496			var windowHeight = $(window).height();
24497			var windowBottom = windowTop + windowHeight;
24498			var bodyTop = scrollEnabled ? scrollOffset.top : position.tbodyTop;
24499			var bodyBottom = scrollEnabled ? scrollOffset.top + scrollBody.outerHeight() : position.tfootTop
24500
24501			// Calculate the amount that the footer or header needs to be shuffled
24502			var shuffle = item === 'footer' ?
24503				// footer and top of body isn't on screen
24504				bodyTop > windowBottom ?
24505					// Yes - push the footer below
24506					position.tfootHeight :
24507					// No - bottom set to the gap between the top of the body and the bottom of the window
24508					bodyTop + position.tfootHeight - windowBottom :
24509				// Otherwise must be a header so get the difference from the bottom of the
24510				//  desired floating header and the bottom of the table body
24511				windowTop + this.c.headerOffset + position.theadHeight - bodyBottom
24512				
24513			// Set the top or bottom based off of the offset and the shuffle value
24514			var prop = item === 'header' ? 'top' : 'bottom';
24515			var val = this.c[item+'Offset'] - (shuffle > 0 ? shuffle : 0);
24516
24517			itemDom.floating.addClass( 'fixedHeader-floating' );
24518			itemDom.floatingParent
24519				.css(prop, val)
24520				.css( {
24521					'left': position.left,
24522					'height': item === 'header' ? position.theadHeight : position.tfootHeight,
24523					'z-index': 2
24524				})
24525				.append(itemDom.floating);
24526
24527			importantWidth(position.width);
24528
24529			if ( item === 'footer' ) {
24530				itemDom.floating.css( 'top', '' );
24531			}
24532		}
24533		else if ( mode === 'below' ) { // only used for the header
24534			// Fix the position of the floating header at base of the table body
24535			this._clone( item, forceChange );
24536
24537			itemDom.floating.addClass( 'fixedHeader-locked' );
24538			itemDom.floatingParent.css({
24539				position: 'absolute',
24540				top: position.tfootTop - position.theadHeight,
24541				left: position.left+'px'
24542			});
24543
24544			importantWidth(position.width);
24545		}
24546		else if ( mode === 'above' ) { // only used for the footer
24547			// Fix the position of the floating footer at top of the table body
24548			this._clone( item, forceChange );
24549
24550			itemDom.floating.addClass( 'fixedHeader-locked' );
24551			itemDom.floatingParent.css({
24552				position: 'absolute',
24553				top: position.tbodyTop,
24554				left: position.left+'px'
24555			});
24556
24557			importantWidth(position.width);
24558		}
24559
24560		// Restore focus if it was lost
24561		if ( focus && focus !== document.activeElement ) {
24562			setTimeout( function () {
24563				focus.focus();
24564			}, 10 );
24565		}
24566
24567		this.s.scrollLeft.header = -1;
24568		this.s.scrollLeft.footer = -1;
24569		this.s[item+'Mode'] = mode;
24570	},
24571
24572	/**
24573	 * Cache the positional information that is required for the mode
24574	 * calculations that FixedHeader performs.
24575	 *
24576	 * @private
24577	 */
24578	_positions: function ()
24579	{
24580		var dt = this.s.dt;
24581		var table = dt.table();
24582		var position = this.s.position;
24583		var dom = this.dom;
24584		var tableNode = $(table.node());
24585		var scrollEnabled = this._scrollEnabled();
24586
24587		// Need to use the header and footer that are in the main table,
24588		// regardless of if they are clones, since they hold the positions we
24589		// want to measure from
24590		var thead = $(dt.table().header());
24591		var tfoot = $(dt.table().footer());
24592		var tbody = dom.tbody;
24593		var scrollBody = tableNode.parent();
24594
24595		position.visible = tableNode.is(':visible');
24596		position.width = tableNode.outerWidth();
24597		position.left = tableNode.offset().left;
24598		position.theadTop = thead.offset().top;
24599		position.tbodyTop = scrollEnabled ? scrollBody.offset().top : tbody.offset().top;
24600		position.tbodyHeight = scrollEnabled ? scrollBody.outerHeight() : tbody.outerHeight();
24601		position.theadHeight = thead.outerHeight();
24602		position.theadBottom = position.theadTop + position.theadHeight;
24603
24604		if ( tfoot.length ) {
24605			position.tfootTop = position.tbodyTop + position.tbodyHeight; //tfoot.offset().top;
24606			position.tfootBottom = position.tfootTop + tfoot.outerHeight();
24607			position.tfootHeight = tfoot.outerHeight();
24608		}
24609		else {
24610			position.tfootTop = position.tbodyTop + tbody.outerHeight();
24611			position.tfootBottom = position.tfootTop;
24612			position.tfootHeight = position.tfootTop;
24613		}
24614	},
24615
24616
24617	/**
24618	 * Mode calculation - determine what mode the fixed items should be placed
24619	 * into.
24620	 *
24621	 * @param  {boolean} forceChange Force a redraw of the mode, even if already
24622	 *     in that mode.
24623	 * @private
24624	 */
24625	_scroll: function ( forceChange )
24626	{
24627		if (this.s.dt.settings()[0].bDestroying) {
24628			return;
24629		}
24630
24631		// ScrollBody details
24632		var scrollEnabled = this._scrollEnabled();
24633		var scrollBody = $(this.s.dt.table().node()).parent();
24634		var scrollOffset =  scrollBody.offset();
24635		var scrollHeight =  scrollBody.outerHeight();
24636
24637		// Window details
24638		var windowLeft = $(document).scrollLeft();
24639		var windowTop = $(document).scrollTop();
24640		var windowHeight = $(window).height();
24641		var windowBottom = windowHeight + windowTop
24642
24643
24644		var position = this.s.position;
24645		var headerMode, footerMode;
24646
24647		// Body Details
24648		var bodyTop = (scrollEnabled ? scrollOffset.top : position.tbodyTop);
24649		var bodyLeft = (scrollEnabled ? scrollOffset.left : position.left);
24650		var bodyBottom = (scrollEnabled ? scrollOffset.top + scrollHeight : position.tfootTop);
24651		var bodyWidth = (scrollEnabled ? scrollBody.outerWidth() : position.tbodyWidth);
24652
24653		var windowBottom = windowTop + windowHeight;
24654
24655		if ( this.c.header ) {
24656			if ( ! this.s.enable ) {
24657				headerMode = 'in-place';
24658			}
24659			// The header is in it's normal place if the body top is lower than
24660			//  the scroll of the window plus the headerOffset and the height of the header
24661			else if ( ! position.visible || windowTop + this.c.headerOffset + position.theadHeight <= bodyTop) {
24662				headerMode = 'in-place';
24663			}
24664			// The header should be floated if
24665			else if (
24666				// The scrolling plus the header offset plus the height of the header is lower than the top of the body
24667				windowTop + this.c.headerOffset + position.theadHeight > bodyTop &&
24668				// And the scrolling at the top plus the header offset is above the bottom of the body
24669				windowTop + this.c.headerOffset + position.theadHeight < bodyBottom
24670			) {
24671				headerMode = 'in';
24672				var scrollBody = $($(this.s.dt.table().node()).parent());
24673
24674				// Further to the above, If the scrolling plus the header offset plus the header height is lower
24675				// than the bottom of the table a shuffle is required so have to force the calculation
24676				if(windowTop + this.c.headerOffset + position.theadHeight > bodyBottom || this.dom.header.floatingParent === undefined){
24677					forceChange = true;
24678				}
24679				else {
24680					this.dom.header.floatingParent
24681						.css({
24682							'top': this.c.headerOffset,
24683							'position': 'fixed'
24684						})
24685						.append(this.dom.header.floating);
24686				}
24687			}
24688			// Anything else and the view is below the table
24689			else {
24690				headerMode = 'below';
24691			}
24692
24693			if ( forceChange || headerMode !== this.s.headerMode ) {
24694				this._modeChange( headerMode, 'header', forceChange );
24695			}
24696
24697			this._horizontal( 'header', windowLeft );
24698		}
24699
24700		var header = {
24701			offset: {top: 0, left: 0},
24702			height: 0
24703		}
24704		var footer = {
24705			offset: {top: 0, left: 0},
24706			height: 0
24707		}
24708
24709		if ( this.c.footer && this.dom.tfoot.length ) {
24710			if ( ! this.s.enable ) {
24711				footerMode = 'in-place';
24712			}
24713			else if ( ! position.visible || position.tfootBottom + this.c.footerOffset <= windowBottom ) {
24714				footerMode = 'in-place';
24715			}
24716			else if (
24717				bodyBottom + position.tfootHeight + this.c.footerOffset > windowBottom &&
24718				bodyTop + this.c.footerOffset < windowBottom
24719			) {
24720				footerMode = 'in';
24721				forceChange = true;
24722			}
24723			else {
24724				footerMode = 'above';
24725			}
24726			
24727			if ( forceChange || footerMode !== this.s.footerMode ) {
24728				this._modeChange( footerMode, 'footer', forceChange );
24729			}
24730
24731			this._horizontal( 'footer', windowLeft );
24732			
24733			var getOffsetHeight = function (el) {
24734				return {
24735					offset: el.offset(),
24736					height: el.outerHeight()
24737				};
24738			};
24739		
24740			header = this.dom.header.floating ? getOffsetHeight(this.dom.header.floating) : getOffsetHeight(this.dom.thead);
24741			footer = this.dom.footer.floating ? getOffsetHeight(this.dom.footer.floating) : getOffsetHeight(this.dom.tfoot);
24742
24743			// If scrolling is enabled and the footer is off the screen
24744			if (scrollEnabled && footer.offset.top > windowTop){// && footer.offset.top >= windowBottom) {
24745				// Calculate the gap between the top of the scrollBody and the top of the window
24746				var overlap = windowTop - scrollOffset.top;
24747				// The new height is the bottom of the window
24748				var newHeight = windowBottom +
24749					// If the gap between the top of the scrollbody and the window is more than
24750					//  the height of the header then the top of the table is still visible so add that gap
24751					// Doing this has effectively calculated the height from the top of the table to the bottom of the current page
24752					(overlap > -header.height ? overlap : 0) -
24753					// Take from that
24754					(
24755						// The top of the header plus
24756						header.offset.top +
24757						// The header height if the standard header is present
24758						(overlap < -header.height ? header.height : 0) +
24759						// And the height of the footer
24760						footer.height
24761					)
24762
24763					// Don't want a negative height
24764				if (newHeight < 0) {
24765					newHeight = 0;
24766				}
24767
24768				// At the end of the above calculation the space between the header (top of the page if floating)
24769				// and the point just above the footer should be the new value for the height of the table.
24770				scrollBody.outerHeight(newHeight);
24771				
24772				// Need some rounding here as sometimes very small decimal places are encountered
24773				// If the actual height is bigger or equal to the height we just applied then the footer is "Floating"
24774				if(Math.round(scrollBody.outerHeight()) >= Math.round(newHeight)) {
24775					$(this.dom.tfoot.parent()).addClass("fixedHeader-floating");
24776				}
24777				// Otherwise max-width has kicked in so it is not floating
24778				else {
24779					$(this.dom.tfoot.parent()).removeClass("fixedHeader-floating");
24780				}
24781			}
24782		}
24783
24784		if(this.dom.header.floating){
24785			this.dom.header.floatingParent.css('left', bodyLeft-windowLeft);
24786		}
24787		if(this.dom.footer.floating){
24788			this.dom.footer.floatingParent.css('left', bodyLeft-windowLeft);
24789		}
24790
24791		// If fixed columns is being used on this table then the blockers need to be copied across
24792		// Cloning these is cleaner than creating as our own as it will keep consistency with fixedColumns automatically
24793		// ASSUMING that the class remains the same
24794		if (this.s.dt.settings()[0]._fixedColumns !== undefined) {
24795			var adjustBlocker = function (side, end, el) {
24796				if (el === undefined) {
24797					var blocker = $('div.dtfc-'+side+'-'+end+'-blocker');
24798
24799					el = blocker.length === 0 ?
24800						null :
24801						blocker.clone().css('z-index', 1);
24802				}
24803
24804				if(el !== null) {
24805					if (headerMode === 'in' || headerMode === 'below') {
24806						el
24807							.appendTo('body')
24808							.css({
24809								top: end === 'top' ? header.offset.top : footer.offset.top,
24810								left: side === 'right' ? bodyLeft + bodyWidth - el.width() : bodyLeft
24811							});
24812					}
24813					else {
24814						el.detach();
24815					}
24816				}
24817
24818				return el;
24819			}
24820
24821			// Adjust all blockers
24822			this.dom.header.rightBlocker = adjustBlocker('right', 'top', this.dom.header.rightBlocker);
24823			this.dom.header.leftBlocker = adjustBlocker('left', 'top', this.dom.header.leftBlocker);
24824			this.dom.footer.rightBlocker = adjustBlocker('right', 'bottom', this.dom.footer.rightBlocker);
24825			this.dom.footer.leftBlocker = adjustBlocker('left', 'bottom', this.dom.footer.leftBlocker);
24826		}
24827	},
24828
24829	/**
24830	 * Function to check if scrolling is enabled on the table or not
24831	 * @returns Boolean value indicating if scrolling on the table is enabled or not
24832	 */
24833	_scrollEnabled: function() {
24834		var oScroll = this.s.dt.settings()[0].oScroll;
24835		if(oScroll.sY !== "" || oScroll.sX !== "") {
24836			return true;
24837		}
24838		return false
24839	}
24840} );
24841
24842
24843/**
24844 * Version
24845 * @type {String}
24846 * @static
24847 */
24848FixedHeader.version = "3.3.2";
24849
24850/**
24851 * Defaults
24852 * @type {Object}
24853 * @static
24854 */
24855FixedHeader.defaults = {
24856	header: true,
24857	footer: false,
24858	headerOffset: 0,
24859	footerOffset: 0
24860};
24861
24862
24863/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
24864 * DataTables interfaces
24865 */
24866
24867// Attach for constructor access
24868$.fn.dataTable.FixedHeader = FixedHeader;
24869$.fn.DataTable.FixedHeader = FixedHeader;
24870
24871
24872// DataTables creation - check if the FixedHeader option has been defined on the
24873// table and if so, initialise
24874$(document).on( 'init.dt.dtfh', function (e, settings, json) {
24875	if ( e.namespace !== 'dt' ) {
24876		return;
24877	}
24878
24879	var init = settings.oInit.fixedHeader;
24880	var defaults = DataTable.defaults.fixedHeader;
24881
24882	if ( (init || defaults) && ! settings._fixedHeader ) {
24883		var opts = $.extend( {}, defaults, init );
24884
24885		if ( init !== false ) {
24886			new FixedHeader( settings, opts );
24887		}
24888	}
24889} );
24890
24891// DataTables API methods
24892DataTable.Api.register( 'fixedHeader()', function () {} );
24893
24894DataTable.Api.register( 'fixedHeader.adjust()', function () {
24895	return this.iterator( 'table', function ( ctx ) {
24896		var fh = ctx._fixedHeader;
24897
24898		if ( fh ) {
24899			fh.update();
24900		}
24901	} );
24902} );
24903
24904DataTable.Api.register( 'fixedHeader.enable()', function ( flag ) {
24905	return this.iterator( 'table', function ( ctx ) {
24906		var fh = ctx._fixedHeader;
24907
24908		flag = ( flag !== undefined ? flag : true );
24909		if ( fh && flag !== fh.enabled() ) {
24910			fh.enable( flag );
24911		}
24912	} );
24913} );
24914
24915DataTable.Api.register( 'fixedHeader.enabled()', function () {
24916	if ( this.context.length ) {
24917		var fh = this.context[0]._fixedHeader;
24918
24919		if ( fh ) {
24920			return fh.enabled();
24921		}
24922	}
24923
24924	return false;
24925} );
24926
24927DataTable.Api.register( 'fixedHeader.disable()', function ( ) {
24928	return this.iterator( 'table', function ( ctx ) {
24929		var fh = ctx._fixedHeader;
24930
24931		if ( fh && fh.enabled() ) {
24932			fh.enable( false );
24933		}
24934	} );
24935} );
24936
24937$.each( ['header', 'footer'], function ( i, el ) {
24938	DataTable.Api.register( 'fixedHeader.'+el+'Offset()', function ( offset ) {
24939		var ctx = this.context;
24940
24941		if ( offset === undefined ) {
24942			return ctx.length && ctx[0]._fixedHeader ?
24943				ctx[0]._fixedHeader[el +'Offset']() :
24944				undefined;
24945		}
24946
24947		return this.iterator( 'table', function ( ctx ) {
24948			var fh = ctx._fixedHeader;
24949
24950			if ( fh ) {
24951				fh[ el +'Offset' ]( offset );
24952			}
24953		} );
24954	} );
24955} );
24956
24957
24958return DataTable;
24959}));
24960
24961
24962/*! KeyTable 2.9.0
24963 * © SpryMedia Ltd - datatables.net/license
24964 */
24965
24966(function( factory ){
24967	if ( typeof define === 'function' && define.amd ) {
24968		// AMD
24969		define( ['jquery', 'datatables.net'], function ( $ ) {
24970			return factory( $, window, document );
24971		} );
24972	}
24973	else if ( typeof exports === 'object' ) {
24974		// CommonJS
24975		var jq = require('jquery');
24976		var cjsRequires = function (root, $) {
24977			if ( ! $.fn.dataTable ) {
24978				require('datatables.net')(root, $);
24979			}
24980		};
24981
24982		if (typeof window === 'undefined') {
24983			module.exports = function (root, $) {
24984				if ( ! root ) {
24985					// CommonJS environments without a window global must pass a
24986					// root. This will give an error otherwise
24987					root = window;
24988				}
24989
24990				if ( ! $ ) {
24991					$ = jq( root );
24992				}
24993
24994				cjsRequires( root, $ );
24995				return factory( $, root, root.document );
24996			};
24997		}
24998		else {
24999			cjsRequires( window, jq );
25000			module.exports = factory( jq, window, window.document );
25001		}
25002	}
25003	else {
25004		// Browser
25005		factory( jQuery, window, document );
25006	}
25007}(function( $, window, document, undefined ) {
25008'use strict';
25009var DataTable = $.fn.dataTable;
25010
25011
25012
25013/**
25014 * @summary     KeyTable
25015 * @description Spreadsheet like keyboard navigation for DataTables
25016 * @version     2.9.0
25017 * @file        dataTables.keyTable.js
25018 * @author      SpryMedia Ltd
25019 * @contact     datatables.net
25020 * @copyright   Copyright SpryMedia Ltd.
25021 *
25022 * This source file is free software, available under the following license:
25023 *   MIT license - http://datatables.net/license/mit
25024 *
25025 * This source file is distributed in the hope that it will be useful, but
25026 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
25027 * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
25028 *
25029 * For details please refer to: http://www.datatables.net
25030 */
25031
25032var namespaceCounter = 0;
25033var editorNamespaceCounter = 0;
25034
25035
25036var KeyTable = function ( dt, opts ) {
25037	// Sanity check that we are using DataTables 1.10 or newer
25038	if ( ! DataTable.versionCheck || ! DataTable.versionCheck( '1.10.8' ) ) {
25039		throw 'KeyTable requires DataTables 1.10.8 or newer';
25040	}
25041
25042	// User and defaults configuration object
25043	this.c = $.extend( true, {},
25044		DataTable.defaults.keyTable,
25045		KeyTable.defaults,
25046		opts
25047	);
25048
25049	// Internal settings
25050	this.s = {
25051		/** @type {DataTable.Api} DataTables' API instance */
25052		dt: new DataTable.Api( dt ),
25053
25054		enable: true,
25055
25056		/** @type {bool} Flag for if a draw is triggered by focus */
25057		focusDraw: false,
25058
25059		/** @type {bool} Flag to indicate when waiting for a draw to happen.
25060		  *   Will ignore key presses at this point
25061		  */
25062		waitingForDraw: false,
25063
25064		/** @type {object} Information about the last cell that was focused */
25065		lastFocus: null,
25066
25067		/** @type {string} Unique namespace per instance */
25068		namespace: '.keyTable-'+(namespaceCounter++),
25069
25070		/** @type {Node} Input element for tabbing into the table */
25071		tabInput: null
25072	};
25073
25074	// DOM items
25075	this.dom = {
25076
25077	};
25078
25079	// Check if row reorder has already been initialised on this table
25080	var settings = this.s.dt.settings()[0];
25081	var exisiting = settings.keytable;
25082	if ( exisiting ) {
25083		return exisiting;
25084	}
25085
25086	settings.keytable = this;
25087	this._constructor();
25088};
25089
25090
25091$.extend( KeyTable.prototype, {
25092	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
25093	 * API methods for DataTables API interface
25094	 */
25095
25096	/**
25097	 * Blur the table's cell focus
25098	 */
25099	blur: function ()
25100	{
25101		this._blur();
25102	},
25103
25104	/**
25105	 * Enable cell focus for the table
25106	 *
25107	 * @param  {string} state Can be `true`, `false` or `-string navigation-only`
25108	 */
25109	enable: function ( state )
25110	{
25111		this.s.enable = state;
25112	},
25113
25114	/**
25115	 * Get enable status
25116	 */
25117	enabled: function () {
25118		return this.s.enable;
25119	},
25120
25121	/**
25122	 * Focus on a cell
25123	 * @param  {integer} row    Row index
25124	 * @param  {integer} column Column index
25125	 */
25126	focus: function ( row, column )
25127	{
25128		this._focus( this.s.dt.cell( row, column ) );
25129	},
25130
25131	/**
25132	 * Is the cell focused
25133	 * @param  {object} cell Cell index to check
25134	 * @returns {boolean} true if focused, false otherwise
25135	 */
25136	focused: function ( cell )
25137	{
25138		var lastFocus = this.s.lastFocus;
25139
25140		if ( ! lastFocus ) {
25141			return false;
25142		}
25143
25144		var lastIdx = this.s.lastFocus.cell.index();
25145		return cell.row === lastIdx.row && cell.column === lastIdx.column;
25146	},
25147
25148
25149	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
25150	 * Constructor
25151	 */
25152
25153	/**
25154	 * Initialise the KeyTable instance
25155	 *
25156	 * @private
25157	 */
25158	_constructor: function ()
25159	{
25160		this._tabInput();
25161
25162		var that = this;
25163		var dt = this.s.dt;
25164		var table = $( dt.table().node() );
25165		var namespace = this.s.namespace;
25166		var editorBlock = false;
25167
25168		// Need to be able to calculate the cell positions relative to the table
25169		if ( table.css('position') === 'static' ) {
25170			table.css( 'position', 'relative' );
25171		}
25172
25173		// Click to focus
25174		$( dt.table().body() ).on( 'click'+namespace, 'th, td', function (e) {
25175			if ( that.s.enable === false ) {
25176				return;
25177			}
25178
25179			var cell = dt.cell( this );
25180
25181			if ( ! cell.any() ) {
25182				return;
25183			}
25184
25185			that._focus( cell, null, false, e );
25186		} );
25187
25188		// Key events
25189		$( document ).on( 'keydown'+namespace, function (e) {
25190			if ( ! editorBlock ) {
25191				that._key( e );
25192			}
25193		} );
25194
25195		// Click blur
25196		if ( this.c.blurable ) {
25197			$( document ).on( 'mousedown'+namespace, function ( e ) {
25198				// Click on the search input will blur focus
25199				if ( $(e.target).parents( '.dataTables_filter' ).length ) {
25200					that._blur();
25201				}
25202
25203				// If the click was inside the DataTables container, don't blur
25204				if ( $(e.target).parents().filter( dt.table().container() ).length ) {
25205					return;
25206				}
25207
25208				// Don't blur in Editor form
25209				if ( $(e.target).parents('div.DTE').length ) {
25210					return;
25211				}
25212
25213				// Or an Editor date input
25214				if (
25215					$(e.target).parents('div.editor-datetime').length ||
25216					$(e.target).parents('div.dt-datetime').length 
25217				) {
25218					return;
25219				}
25220
25221				//If the click was inside the fixed columns container, don't blur
25222				if ( $(e.target).parents().filter('.DTFC_Cloned').length ) {
25223					return;
25224				}
25225
25226				that._blur();
25227			} );
25228		}
25229
25230		if ( this.c.editor ) {
25231			var editor = this.c.editor;
25232
25233			// Need to disable KeyTable when the main editor is shown
25234			editor.on( 'open.keyTableMain', function (e, mode, action) {
25235				if ( mode !== 'inline' && that.s.enable ) {
25236					that.enable( false );
25237
25238					editor.one( 'close'+namespace, function () {
25239						that.enable( true );
25240					} );
25241				}
25242			} );
25243
25244			if ( this.c.editOnFocus ) {
25245				dt.on( 'key-focus'+namespace+' key-refocus'+namespace, function ( e, dt, cell, orig ) {
25246					that._editor( null, orig, true );
25247				} );
25248			}
25249
25250			// Activate Editor when a key is pressed (will be ignored, if
25251			// already active).
25252			dt.on( 'key'+namespace, function ( e, dt, key, cell, orig ) {
25253				that._editor( key, orig, false );
25254			} );
25255
25256			// Active editing on double click - it will already have focus from
25257			// the click event handler above
25258			$( dt.table().body() ).on( 'dblclick'+namespace, 'th, td', function (e) {
25259				if ( that.s.enable === false ) {
25260					return;
25261				}
25262
25263				var cell = dt.cell( this );
25264
25265				if ( ! cell.any() ) {
25266					return;
25267				}
25268
25269				if ( that.s.lastFocus && this !== that.s.lastFocus.cell.node() ) {
25270					return;
25271				}
25272
25273				that._editor( null, e, true );
25274			} );
25275
25276			// While Editor is busy processing, we don't want to process any key events
25277			editor
25278				.on('preSubmit', function () {
25279					editorBlock = true;
25280				} )
25281				.on('preSubmitCancelled', function () {
25282					editorBlock = false;
25283				} )
25284				.on('submitComplete', function () {
25285					editorBlock = false;
25286				} );
25287		}
25288
25289		// Stave saving
25290		// if ( dt.settings()[0].oFeatures.bStateSave ) {
25291			dt.on( 'stateSaveParams'+namespace, function (e, s, d) {
25292				d.keyTable = that.s.lastFocus ?
25293					that.s.lastFocus.cell.index() :
25294					null;
25295			} );
25296		// }
25297
25298		dt.on( 'column-visibility'+namespace, function (e) {
25299			that._tabInput();
25300		} );
25301
25302		dt.on( 'column-reorder'+namespace, function (e, s, d) {
25303			// Need to update the last focus cell's index
25304			var lastFocus = that.s.lastFocus;
25305
25306			if (lastFocus && lastFocus.cell) {
25307				var curr = lastFocus.relative.column;
25308				
25309				// Manipulate the API instance to correct the column index
25310				lastFocus.cell[0][0].column = d.mapping.indexOf(curr);
25311				lastFocus.relative.column = d.mapping.indexOf(curr);
25312			}
25313		} );
25314
25315		// Redraw - retain focus on the current cell
25316		dt.on( 'draw'+namespace, function (e) {
25317			that._tabInput();
25318
25319			if ( that.s.focusDraw ) {
25320				return;
25321			}
25322
25323			var lastFocus = that.s.lastFocus;
25324
25325			if ( lastFocus ) {
25326				var relative = that.s.lastFocus.relative;
25327				var info = dt.page.info();
25328				var row = relative.row + info.start;
25329
25330				if ( info.recordsDisplay === 0 ) {
25331					return;
25332				}
25333
25334				// Reverse if needed
25335				if ( row >= info.recordsDisplay ) {
25336					row = info.recordsDisplay - 1;
25337				}
25338
25339				that._focus( row, relative.column, true, e );
25340			}
25341		} );
25342
25343		// Clipboard support
25344		if ( this.c.clipboard ) {
25345			this._clipboard();
25346		}
25347
25348		dt.on( 'destroy'+namespace, function () {
25349			that._blur( true );
25350
25351			// Event tidy up
25352			dt.off( namespace );
25353
25354			$( dt.table().body() )
25355				.off( 'click'+namespace, 'th, td' )
25356				.off( 'dblclick'+namespace, 'th, td' );
25357
25358			$( document )
25359				.off( 'mousedown'+namespace )
25360				.off( 'keydown'+namespace )
25361				.off( 'copy'+namespace )
25362				.off( 'paste'+namespace );
25363		} );
25364
25365		// Initial focus comes from state or options
25366		var state = dt.state.loaded();
25367
25368		if ( state && state.keyTable ) {
25369			// Wait until init is done
25370			dt.one( 'init', function () {
25371				var cell = dt.cell( state.keyTable );
25372
25373				// Ensure that the saved cell still exists
25374				if ( cell.any() ) {
25375					cell.focus();
25376				}
25377			} );
25378		}
25379		else if ( this.c.focus ) {
25380			dt.cell( this.c.focus ).focus();
25381		}
25382	},
25383
25384
25385	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
25386	 * Private methods
25387	 */
25388
25389	/**
25390	 * Blur the control
25391	 *
25392	 * @param {boolean} [noEvents=false] Don't trigger updates / events (for destroying)
25393	 * @private
25394	 */
25395	_blur: function (noEvents)
25396	{
25397		if ( ! this.s.enable || ! this.s.lastFocus ) {
25398			return;
25399		}
25400
25401		var cell = this.s.lastFocus.cell;
25402
25403		$( cell.node() ).removeClass( this.c.className );
25404		this.s.lastFocus = null;
25405
25406		if ( ! noEvents ) {
25407			this._updateFixedColumns(cell.index().column);
25408
25409			this._emitEvent( 'key-blur', [ this.s.dt, cell ] );
25410		}
25411	},
25412
25413
25414	/**
25415	 * Clipboard interaction handlers
25416	 *
25417	 * @private
25418	 */
25419	_clipboard: function () {
25420		var dt = this.s.dt;
25421		var that = this;
25422		var namespace = this.s.namespace;
25423
25424		// IE8 doesn't support getting selected text
25425		if ( ! window.getSelection ) {
25426			return;
25427		}
25428
25429		$(document).on( 'copy'+namespace, function (ejq) {
25430			var e = ejq.originalEvent;
25431			var selection = window.getSelection().toString();
25432			var focused = that.s.lastFocus;
25433
25434			// Only copy cell text to clipboard if there is no other selection
25435			// and there is a focused cell
25436			if ( ! selection && focused ) {
25437				e.clipboardData.setData(
25438					'text/plain',
25439					focused.cell.render( that.c.clipboardOrthogonal )
25440				);
25441				e.preventDefault();
25442			}
25443		} );
25444
25445		$(document).on( 'paste'+namespace, function (ejq) {
25446			var e = ejq.originalEvent;
25447			var focused = that.s.lastFocus;
25448			var activeEl = document.activeElement;
25449			var editor = that.c.editor;
25450			var pastedText;
25451
25452			if ( focused && (! activeEl || activeEl.nodeName.toLowerCase() === 'body') ) {
25453				e.preventDefault();
25454
25455				if ( window.clipboardData && window.clipboardData.getData ) {
25456					// IE
25457					pastedText = window.clipboardData.getData('Text');
25458				}
25459				else if ( e.clipboardData && e.clipboardData.getData ) {
25460					// Everything else
25461					pastedText = e.clipboardData.getData('text/plain');
25462				}
25463
25464				if ( editor ) {
25465					// Got Editor - need to activate inline editing,
25466					// set the value and submit
25467					var options = that._inlineOptions(focused.cell.index());
25468
25469					editor
25470						.inline(options.cell, options.field, options.options)
25471						.set( editor.displayed()[0], pastedText )
25472						.submit();
25473				}
25474				else {
25475					// No editor, so just dump the data in
25476					focused.cell.data( pastedText );
25477					dt.draw(false);
25478				}
25479			}
25480		} );
25481	},
25482
25483
25484	/**
25485	 * Get an array of the column indexes that KeyTable can operate on. This
25486	 * is a merge of the user supplied columns and the visible columns.
25487	 *
25488	 * @private
25489	 */
25490	_columns: function ()
25491	{
25492		var dt = this.s.dt;
25493		var user = dt.columns( this.c.columns ).indexes();
25494		var out = [];
25495
25496		dt.columns( ':visible' ).every( function (i) {
25497			if ( user.indexOf( i ) !== -1 ) {
25498				out.push( i );
25499			}
25500		} );
25501
25502		return out;
25503	},
25504
25505
25506	/**
25507	 * Perform excel like navigation for Editor by triggering an edit on key
25508	 * press
25509	 *
25510	 * @param  {integer} key Key code for the pressed key
25511	 * @param  {object} orig Original event
25512	 * @private
25513	 */
25514	_editor: function ( key, orig, hardEdit )
25515	{
25516		// If nothing focused, we can't take any action
25517		if (! this.s.lastFocus) {
25518			return;	
25519		}
25520
25521		// DataTables draw event
25522		if (orig && orig.type === 'draw') {
25523			return;
25524		}
25525
25526		var that = this;
25527		var dt = this.s.dt;
25528		var editor = this.c.editor;
25529		var editCell = this.s.lastFocus.cell;
25530		var namespace = this.s.namespace + 'e' + editorNamespaceCounter++;
25531
25532		// Do nothing if there is already an inline edit in this cell
25533		if ( $('div.DTE', editCell.node()).length ) {
25534			return;
25535		}
25536
25537		// Don't activate Editor on control key presses
25538		if ( key !== null && (
25539			(key >= 0x00 && key <= 0x09) ||
25540			key === 0x0b ||
25541			key === 0x0c ||
25542			(key >= 0x0e && key <= 0x1f) ||
25543			(key >= 0x70 && key <= 0x7b) ||
25544			(key >= 0x7f && key <= 0x9f)
25545		) ) {
25546			return;
25547		}
25548
25549		if ( orig ) {
25550			orig.stopPropagation();
25551
25552			// Return key should do nothing - for textareas it would empty the
25553			// contents
25554			if ( key === 13 ) {
25555				orig.preventDefault();
25556			}
25557		}
25558
25559		var editInline = function () {
25560			var options = that._inlineOptions(editCell.index());
25561
25562			editor
25563				.one( 'open'+namespace, function () {
25564					// Remove cancel open
25565					editor.off( 'cancelOpen'+namespace );
25566
25567					// Excel style - select all text
25568					if ( ! hardEdit ) {
25569						$('div.DTE_Field_InputControl input, div.DTE_Field_InputControl textarea').select();
25570					}
25571
25572					// Reduce the keys the Keys listens for
25573					dt.keys.enable( hardEdit ? 'tab-only' : 'navigation-only' );
25574
25575					// On blur of the navigation submit
25576					dt.on( 'key-blur.editor', function (e, dt, cell) {
25577						// When Editor has its own blur enabled - do nothing here
25578						if (editor.s.editOpts.onBlur === 'submit') {
25579							return;
25580						}
25581
25582						if ( editor.displayed() && cell.node() === editCell.node() ) {
25583							editor.submit();
25584						}
25585					} );
25586
25587					// Highlight the cell a different colour on full edit
25588					if ( hardEdit ) {
25589						$( dt.table().container() ).addClass('dtk-focus-alt');
25590					}
25591
25592					// If the dev cancels the submit, we need to return focus
25593					editor.on( 'preSubmitCancelled'+namespace, function () {
25594						setTimeout( function () {
25595							that._focus( editCell, null, false );
25596						}, 50 );
25597					} );
25598
25599					editor.on( 'submitUnsuccessful'+namespace, function () {
25600						that._focus( editCell, null, false );
25601					} );
25602
25603					// Restore full key navigation on close
25604					editor.one( 'close'+namespace, function () {
25605						dt.keys.enable( true );
25606						dt.off( 'key-blur.editor' );
25607						editor.off( namespace );
25608						$( dt.table().container() ).removeClass('dtk-focus-alt');
25609
25610						if (that.s.returnSubmit) {
25611							that.s.returnSubmit = false;
25612							that._emitEvent( 'key-return-submit', [dt, editCell] );
25613						}
25614					} );
25615				} )
25616				.one( 'cancelOpen'+namespace, function () {
25617					// `preOpen` can cancel the display of the form, so it
25618					// might be that the open event handler isn't needed
25619					editor.off( namespace );
25620				} )
25621				.inline(options.cell, options.field, options.options);
25622		};
25623
25624		// Editor 1.7 listens for `return` on keyup, so if return is the trigger
25625		// key, we need to wait for `keyup` otherwise Editor would just submit
25626		// the content triggered by this keypress.
25627		if ( key === 13 ) {
25628			hardEdit = true;
25629
25630			$(document).one( 'keyup', function () { // immediately removed
25631				editInline();
25632			} );
25633		}
25634		else {
25635			editInline();
25636		}
25637	},
25638
25639
25640	_inlineOptions: function (cellIdx)
25641	{
25642		if (this.c.editorOptions) {
25643			return this.c.editorOptions(cellIdx);
25644		}
25645
25646		return {
25647			cell: cellIdx,
25648			field: undefined,
25649			options: undefined
25650		};
25651	},
25652
25653
25654	/**
25655	 * Emit an event on the DataTable for listeners
25656	 *
25657	 * @param  {string} name Event name
25658	 * @param  {array} args Event arguments
25659	 * @private
25660	 */
25661	_emitEvent: function ( name, args )
25662	{
25663		return this.s.dt.iterator( 'table', function ( ctx, i ) {
25664			return $(ctx.nTable).triggerHandler( name, args );
25665		} );
25666	},
25667
25668
25669	/**
25670	 * Focus on a particular cell, shifting the table's paging if required
25671	 *
25672	 * @param  {DataTables.Api|integer} row Can be given as an API instance that
25673	 *   contains the cell to focus or as an integer. As the latter it is the
25674	 *   visible row index (from the whole data set) - NOT the data index
25675	 * @param  {integer} [column] Not required if a cell is given as the first
25676	 *   parameter. Otherwise this is the column data index for the cell to
25677	 *   focus on
25678	 * @param {boolean} [shift=true] Should the viewport be moved to show cell
25679	 * @private
25680	 */
25681	_focus: function ( row, column, shift, originalEvent )
25682	{
25683		var that = this;
25684		var dt = this.s.dt;
25685		var pageInfo = dt.page.info();
25686		var lastFocus = this.s.lastFocus;
25687
25688		if ( ! originalEvent) {
25689			originalEvent = null;
25690		}
25691
25692		if ( ! this.s.enable ) {
25693			return;
25694		}
25695
25696		if ( typeof row !== 'number' ) {
25697			// Its an API instance - check that there is actually a row
25698			if ( ! row.any() ) {
25699				return;
25700			}
25701
25702			// Convert the cell to a row and column
25703			var index = row.index();
25704			column = index.column;
25705			row = dt
25706				.rows( { filter: 'applied', order: 'applied' } )
25707				.indexes()
25708				.indexOf( index.row );
25709			
25710			// Don't focus rows that were filtered out.
25711			if ( row < 0 ) {
25712				return;
25713			}
25714
25715			// For server-side processing normalise the row by adding the start
25716			// point, since `rows().indexes()` includes only rows that are
25717			// available at the client-side
25718			if ( pageInfo.serverSide ) {
25719				row += pageInfo.start;
25720			}
25721		}
25722
25723		// Is the row on the current page? If not, we need to redraw to show the
25724		// page
25725		if ( pageInfo.length !== -1 && (row < pageInfo.start || row >= pageInfo.start+pageInfo.length) ) {
25726			this.s.focusDraw = true;
25727			this.s.waitingForDraw = true;
25728
25729			dt
25730				.one( 'draw', function () {
25731					that.s.focusDraw = false;
25732					that.s.waitingForDraw = false;
25733					that._focus( row, column, undefined, originalEvent );
25734				} )
25735				.page( Math.floor( row / pageInfo.length ) )
25736				.draw( false );
25737
25738			return;
25739		}
25740
25741		// In the available columns?
25742		if ( $.inArray( column, this._columns() ) === -1 ) {
25743			return;
25744		}
25745
25746		// De-normalise the server-side processing row, so we select the row
25747		// in its displayed position
25748		if ( pageInfo.serverSide ) {
25749			row -= pageInfo.start;
25750		}
25751
25752		// Get the cell from the current position - ignoring any cells which might
25753		// not have been rendered (therefore can't use `:eq()` selector).
25754		var cells = dt.cells( null, column, {search: 'applied', order: 'applied'} ).flatten();
25755		var cell = dt.cell( cells[ row ] );
25756
25757		// Prefocus check - this event allows a focus action to be disallowed. 
25758		var preFocus = this._emitEvent( 'key-prefocus', [ this.s.dt, cell, originalEvent || null ] );
25759		if (preFocus.indexOf(false) !== -1) {
25760			return;
25761		}
25762
25763		if ( lastFocus ) {
25764			// Don't trigger a refocus on the same cell
25765			if ( lastFocus.node === cell.node() ) {
25766				this._emitEvent( 'key-refocus', [ this.s.dt, cell, originalEvent || null ] );
25767				return;
25768			}
25769
25770			// Otherwise blur the old focus
25771			this._blur();
25772		}
25773
25774		// Clear focus from other tables
25775		this._removeOtherFocus();
25776
25777		var node = $( cell.node() );
25778		node.addClass( this.c.className );
25779
25780		this._updateFixedColumns(column);
25781
25782		// Shift viewpoint and page to make cell visible
25783		if ( shift === undefined || shift === true ) {
25784			this._scroll( $(window), $(document.body), node, 'offset' );
25785
25786			var bodyParent = dt.table().body().parentNode;
25787			if ( bodyParent !== dt.table().header().parentNode ) {
25788				var parent = $(bodyParent.parentNode);
25789
25790				this._scroll( parent, parent, node, 'position' );
25791			}
25792		}
25793
25794		// Event and finish
25795		this.s.lastFocus = {
25796			cell: cell,
25797			node: cell.node(),
25798			relative: {
25799				row: dt.rows( { page: 'current' } ).indexes().indexOf( cell.index().row ),
25800				column: cell.index().column
25801			}
25802		};
25803
25804		this._emitEvent( 'key-focus', [ this.s.dt, cell, originalEvent || null ] );
25805		dt.state.save();
25806	},
25807
25808	/**
25809	 * Handle key press
25810	 *
25811	 * @param  {object} e Event
25812	 * @private
25813	 */
25814	_key: function ( e )
25815	{
25816		// If we are waiting for a draw to happen from another key event, then
25817		// do nothing for this new key press.
25818		if ( this.s.waitingForDraw ) {
25819			e.preventDefault();
25820			return;
25821		}
25822
25823		var enable = this.s.enable;
25824		this.s.returnSubmit = (enable === 'navigation-only' || enable === 'tab-only') && e.keyCode === 13
25825			? true
25826			: false;
25827
25828		var navEnable = enable === true || enable === 'navigation-only';
25829		if ( ! enable ) {
25830			return;
25831		}
25832
25833		if ( (e.keyCode === 0 || e.ctrlKey || e.metaKey || e.altKey) && !(e.ctrlKey && e.altKey) ) {
25834			return;
25835		}
25836
25837		// If not focused, then there is no key action to take
25838		var lastFocus = this.s.lastFocus;
25839		if ( ! lastFocus ) {
25840			return;
25841		}
25842
25843		// And the last focus still exists!
25844		if ( ! this.s.dt.cell(lastFocus.node).any() ) {
25845			this.s.lastFocus = null;
25846			return;
25847		}
25848
25849		var that = this;
25850		var dt = this.s.dt;
25851		var scrolling = this.s.dt.settings()[0].oScroll.sY ? true : false;
25852
25853		// If we are not listening for this key, do nothing
25854		if ( this.c.keys && $.inArray( e.keyCode, this.c.keys ) === -1 ) {
25855			return;
25856		}
25857
25858		switch( e.keyCode ) {
25859			case 9: // tab
25860				// `enable` can be tab-only
25861				e.preventDefault();
25862
25863				this._keyAction( function () {
25864					that._shift( e, e.shiftKey ? 'left' : 'right', true );
25865				} );
25866				break;
25867
25868			case 27: // esc
25869				if ( this.c.blurable && enable === true ) {
25870					this._blur();
25871				}
25872				break;
25873
25874			case 33: // page up (previous page)
25875			case 34: // page down (next page)
25876				if ( navEnable && !scrolling ) {
25877					e.preventDefault();
25878
25879					this._keyAction( function () {
25880						dt
25881							.page( e.keyCode === 33 ? 'previous' : 'next' )
25882							.draw( false );
25883					} );
25884				}
25885				break;
25886
25887			case 35: // end (end of current page)
25888			case 36: // home (start of current page)
25889				if ( navEnable ) {
25890					e.preventDefault();
25891
25892					this._keyAction( function () {
25893						var indexes = dt.cells( {page: 'current'} ).indexes();
25894						var colIndexes = that._columns();
25895
25896						that._focus( dt.cell(
25897							indexes[ e.keyCode === 35 ? indexes.length-1 : colIndexes[0] ]
25898						), null, true, e );
25899					} );
25900				}
25901				break;
25902
25903			case 37: // left arrow
25904				if ( navEnable ) {
25905					this._keyAction( function () {
25906						that._shift( e, 'left' );
25907					} );
25908				}
25909				break;
25910
25911			case 38: // up arrow
25912				if ( navEnable ) {
25913					this._keyAction( function () {
25914						that._shift( e, 'up' );
25915					} );
25916				}
25917				break;
25918
25919			case 39: // right arrow
25920				if ( navEnable ) {
25921					this._keyAction( function () {
25922						that._shift( e, 'right' );
25923					} );
25924				}
25925				break;
25926
25927			case 40: // down arrow
25928				if ( navEnable ) {
25929					this._keyAction( function () {
25930						that._shift( e, 'down' );
25931					} );
25932				}
25933				break;
25934
25935			case 113: // F2 - Excel like hard edit
25936				if ( this.c.editor ) {
25937					this._editor(null, e, true);
25938					break;
25939				}
25940				// else fallthrough
25941
25942			default:
25943				// Everything else - pass through only when fully enabled
25944				if ( enable === true ) {
25945					this._emitEvent( 'key', [ dt, e.keyCode, this.s.lastFocus.cell, e ] );
25946				}
25947				break;
25948		}
25949	},
25950
25951	/**
25952	 * Whether we perform a key shift action immediately or not depends
25953	 * upon if Editor is being used. If it is, then we wait until it
25954	 * completes its action
25955	 * @param {*} action Function to trigger when ready
25956	 */
25957	_keyAction: function (action) {
25958		var editor = this.c.editor;
25959
25960		if (editor && editor.mode()) {
25961			editor.submit(action);
25962		}
25963		else {
25964			action();
25965		}
25966	},
25967
25968	/**
25969	 * Remove focus from all tables other than this one
25970	 */
25971	_removeOtherFocus: function ()
25972	{
25973		var thisTable = this.s.dt.table().node();
25974
25975		$.fn.dataTable.tables({api:true}).iterator('table', function (settings) {
25976			if (this.table().node() !== thisTable) {
25977				this.cell.blur();
25978			}
25979		});
25980	},
25981
25982	/**
25983	 * Scroll a container to make a cell visible in it. This can be used for
25984	 * both DataTables scrolling and native window scrolling.
25985	 *
25986	 * @param  {jQuery} container Scrolling container
25987	 * @param  {jQuery} scroller  Item being scrolled
25988	 * @param  {jQuery} cell      Cell in the scroller
25989	 * @param  {string} posOff    `position` or `offset` - which to use for the
25990	 *   calculation. `offset` for the document, otherwise `position`
25991	 * @private
25992	 */
25993	_scroll: function ( container, scroller, cell, posOff )
25994	{
25995		var offset = cell[posOff]();
25996		var height = cell.outerHeight();
25997		var width = cell.outerWidth();
25998
25999		var scrollTop = scroller.scrollTop();
26000		var scrollLeft = scroller.scrollLeft();
26001		var containerHeight = container.height();
26002		var containerWidth = container.width();
26003
26004		// If Scroller is being used, the table can be `position: absolute` and that
26005		// needs to be taken account of in the offset. If no Scroller, this will be 0
26006		if ( posOff === 'position' ) {
26007			offset.top += parseInt( cell.closest('table').css('top'), 10 );
26008		}
26009
26010		// Top correction (partially in view)
26011		if ( offset.top < scrollTop && offset.top + height > scrollTop - 5 ) {
26012			scroller.scrollTop( offset.top );
26013		}
26014
26015		// Left correction
26016		if ( offset.left < scrollLeft ) {
26017			scroller.scrollLeft( offset.left );
26018		}
26019
26020		// Bottom correction plus in view correction. Note that the magic 5 is to allow
26021		// for the edge just passing the bottom of the view
26022		if (
26023			offset.top + height > scrollTop + containerHeight &&
26024			offset.top < scrollTop + containerHeight + 5 &&
26025			height < containerHeight
26026		) {
26027			scroller.scrollTop( offset.top + height - containerHeight );
26028		}
26029
26030		// Right correction
26031		if ( offset.left + width > scrollLeft + containerWidth && width < containerWidth ) {
26032			scroller.scrollLeft( offset.left + width - containerWidth );
26033		}
26034	},
26035
26036
26037	/**
26038	 * Calculate a single offset movement in the table - up, down, left and
26039	 * right and then perform the focus if possible
26040	 *
26041	 * @param  {object}  e           Event object
26042	 * @param  {string}  direction   Movement direction
26043	 * @param  {boolean} keyBlurable `true` if the key press can result in the
26044	 *   table being blurred. This is so arrow keys won't blur the table, but
26045	 *   tab will.
26046	 * @private
26047	 */
26048	_shift: function ( e, direction, keyBlurable )
26049	{
26050		var that      = this;
26051		var dt        = this.s.dt;
26052		var pageInfo  = dt.page.info();
26053		var rows      = pageInfo.recordsDisplay;
26054		var columns   = this._columns();
26055		var last      = this.s.lastFocus;
26056		if ( ! last ) {
26057			return;
26058		}
26059	
26060		var currentCell = last.cell;
26061		if ( ! currentCell ) {
26062			return;
26063		}
26064
26065		var currRow = dt
26066			.rows( { filter: 'applied', order: 'applied' } )
26067			.indexes()
26068			.indexOf( currentCell.index().row );
26069
26070		// When server-side processing, `rows().indexes()` only gives the rows
26071		// that are available at the client-side, so we need to normalise the
26072		// row's current position by the display start point
26073		if ( pageInfo.serverSide ) {
26074			currRow += pageInfo.start;
26075		}
26076
26077		var currCol = dt
26078			.columns( columns )
26079			.indexes()
26080			.indexOf( currentCell.index().column );
26081
26082		var
26083			row = currRow,
26084			column = columns[ currCol ]; // row is the display, column is an index
26085
26086		// If the direction is rtl then the logic needs to be inverted from this point forwards
26087		if($(dt.table().node()).css('direction') === 'rtl') {
26088			if(direction === 'right') {
26089				direction = 'left';
26090			}
26091			else if(direction === 'left'){
26092				direction = 'right';
26093			}
26094		}
26095
26096		if ( direction === 'right' ) {
26097			if ( currCol >= columns.length - 1 ) {
26098				row++;
26099				column = columns[0];
26100			}
26101			else {
26102				column = columns[ currCol+1 ];
26103			}
26104		}
26105		else if ( direction === 'left' ) {
26106			if ( currCol === 0 ) {
26107				row--;
26108				column = columns[ columns.length - 1 ];
26109			}
26110			else {
26111				column = columns[ currCol-1 ];
26112			}
26113		}
26114		else if ( direction === 'up' ) {
26115			row--;
26116		}
26117		else if ( direction === 'down' ) {
26118			row++;
26119		}
26120
26121		if ( row >= 0 && row < rows && $.inArray( column, columns ) !== -1 ) {
26122			if (e) {
26123				e.preventDefault();
26124			}
26125
26126			this._focus( row, column, true, e );
26127		}
26128		else if ( ! keyBlurable || ! this.c.blurable ) {
26129			// No new focus, but if the table isn't blurable, then don't loose
26130			// focus
26131			if (e) {
26132				e.preventDefault();
26133			}
26134		}
26135		else {
26136			this._blur();
26137		}
26138	},
26139
26140
26141	/**
26142	 * Create and insert a hidden input element that can receive focus on behalf
26143	 * of the table
26144	 *
26145	 * @private
26146	 */
26147	_tabInput: function ()
26148	{
26149		var that = this;
26150		var dt = this.s.dt;
26151		var tabIndex = this.c.tabIndex !== null ?
26152			this.c.tabIndex :
26153			dt.settings()[0].iTabIndex;
26154
26155		if ( tabIndex == -1 ) {
26156			return;
26157		}
26158
26159		// Only create the input element once on first class
26160		if (! this.s.tabInput) {
26161			var div = $('<div><input type="text" tabindex="'+tabIndex+'"/></div>')
26162				.css( {
26163					position: 'absolute',
26164					height: 1,
26165					width: 0,
26166					overflow: 'hidden'
26167				} );
26168
26169			div.children().on( 'focus', function (e) {
26170				var cell = dt.cell(':eq(0)', that._columns(), {page: 'current'});
26171	
26172				if ( cell.any() ) {
26173					that._focus( cell, null, true, e );
26174				}
26175			} );
26176
26177			this.s.tabInput = div;
26178		}
26179
26180		// Insert the input element into the first cell in the table's body
26181		var cell = this.s.dt.cell(':eq(0)', '0:visible', {page: 'current', order: 'current'}).node();
26182		if (cell) {
26183			$(cell).prepend(this.s.tabInput);
26184		}
26185	},
26186
26187	/**
26188	 * Update fixed columns if they are enabled and if the cell we are
26189	 * focusing is inside a fixed column
26190	 * @param  {integer} column Index of the column being changed
26191	 * @private
26192	 */
26193	_updateFixedColumns: function( column )
26194	{
26195		var dt = this.s.dt;
26196		var settings = dt.settings()[0];
26197
26198		if ( settings._oFixedColumns ) {
26199			var leftCols = settings._oFixedColumns.s.iLeftColumns;
26200			var rightCols = settings.aoColumns.length - settings._oFixedColumns.s.iRightColumns;
26201
26202			if (column < leftCols || column >= rightCols) {
26203				dt.fixedColumns().update();
26204			}
26205		}
26206	}
26207} );
26208
26209
26210/**
26211 * KeyTable default settings for initialisation
26212 *
26213 * @namespace
26214 * @name KeyTable.defaults
26215 * @static
26216 */
26217KeyTable.defaults = {
26218	/**
26219	 * Can focus be removed from the table
26220	 * @type {Boolean}
26221	 */
26222	blurable: true,
26223
26224	/**
26225	 * Class to give to the focused cell
26226	 * @type {String}
26227	 */
26228	className: 'focus',
26229
26230	/**
26231	 * Enable or disable clipboard support
26232	 * @type {Boolean}
26233	 */
26234	clipboard: true,
26235
26236	/**
26237	 * Orthogonal data that should be copied to clipboard
26238	 * @type {string}
26239	 */
26240	clipboardOrthogonal: 'display',
26241
26242	/**
26243	 * Columns that can be focused. This is automatically merged with the
26244	 * visible columns as only visible columns can gain focus.
26245	 * @type {String}
26246	 */
26247	columns: '', // all
26248
26249	/**
26250	 * Editor instance to automatically perform Excel like navigation
26251	 * @type {Editor}
26252	 */
26253	editor: null,
26254
26255	/**
26256	 * Trigger editing immediately on focus
26257	 * @type {boolean}
26258	 */
26259	editOnFocus: false,
26260
26261	/**
26262	 * Options to pass to Editor's inline method
26263	 * @type {function}
26264	 */
26265	editorOptions: null,
26266
26267	/**
26268	 * Select a cell to automatically select on start up. `null` for no
26269	 * automatic selection
26270	 * @type {cell-selector}
26271	 */
26272	focus: null,
26273
26274	/**
26275	 * Array of keys to listen for
26276	 * @type {null|array}
26277	 */
26278	keys: null,
26279
26280	/**
26281	 * Tab index for where the table should sit in the document's tab flow
26282	 * @type {integer|null}
26283	 */
26284	tabIndex: null
26285};
26286
26287
26288
26289KeyTable.version = "2.9.0";
26290
26291
26292$.fn.dataTable.KeyTable = KeyTable;
26293$.fn.DataTable.KeyTable = KeyTable;
26294
26295
26296DataTable.Api.register( 'cell.blur()', function () {
26297	return this.iterator( 'table', function (ctx) {
26298		if ( ctx.keytable ) {
26299			ctx.keytable.blur();
26300		}
26301	} );
26302} );
26303
26304DataTable.Api.register( 'cell().focus()', function () {
26305	return this.iterator( 'cell', function (ctx, row, column) {
26306		if ( ctx.keytable ) {
26307			ctx.keytable.focus( row, column );
26308		}
26309	} );
26310} );
26311
26312DataTable.Api.register( 'keys.disable()', function () {
26313	return this.iterator( 'table', function (ctx) {
26314		if ( ctx.keytable ) {
26315			ctx.keytable.enable( false );
26316		}
26317	} );
26318} );
26319
26320DataTable.Api.register( 'keys.enable()', function ( opts ) {
26321	return this.iterator( 'table', function (ctx) {
26322		if ( ctx.keytable ) {
26323			ctx.keytable.enable( opts === undefined ? true : opts );
26324		}
26325	} );
26326} );
26327
26328DataTable.Api.register( 'keys.enabled()', function ( opts ) {
26329	var ctx = this.context;
26330
26331	if (ctx.length) {
26332		return ctx[0].keytable
26333			? ctx[0].keytable.enabled()
26334			: false;
26335	}
26336
26337	return false;
26338} );
26339
26340DataTable.Api.register( 'keys.move()', function ( dir ) {
26341	return this.iterator( 'table', function (ctx) {
26342		if ( ctx.keytable ) {
26343			ctx.keytable._shift( null, dir, false );
26344		}
26345	} );
26346} );
26347
26348// Cell selector
26349DataTable.ext.selector.cell.push( function ( settings, opts, cells ) {
26350	var focused = opts.focused;
26351	var kt = settings.keytable;
26352	var out = [];
26353
26354	if ( ! kt || focused === undefined ) {
26355		return cells;
26356	}
26357
26358	for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
26359		if ( (focused === true &&  kt.focused( cells[i] ) ) ||
26360			 (focused === false && ! kt.focused( cells[i] ) )
26361		) {
26362			out.push( cells[i] );
26363		}
26364	}
26365
26366	return out;
26367} );
26368
26369
26370// Attach a listener to the document which listens for DataTables initialisation
26371// events so we can automatically initialise
26372$(document).on( 'preInit.dt.dtk', function (e, settings, json) {
26373	if ( e.namespace !== 'dt' ) {
26374		return;
26375	}
26376
26377	var init = settings.oInit.keys;
26378	var defaults = DataTable.defaults.keys;
26379
26380	if ( init || defaults ) {
26381		var opts = $.extend( {}, defaults, init );
26382
26383		if ( init !== false ) {
26384			new KeyTable( settings, opts  );
26385		}
26386	}
26387} );
26388
26389
26390return DataTable;
26391}));
26392
26393
26394/*! Responsive 2.4.1
26395 * © SpryMedia Ltd - datatables.net/license
26396 */
26397
26398(function( factory ){
26399	if ( typeof define === 'function' && define.amd ) {
26400		// AMD
26401		define( ['jquery', 'datatables.net'], function ( $ ) {
26402			return factory( $, window, document );
26403		} );
26404	}
26405	else if ( typeof exports === 'object' ) {
26406		// CommonJS
26407		var jq = require('jquery');
26408		var cjsRequires = function (root, $) {
26409			if ( ! $.fn.dataTable ) {
26410				require('datatables.net')(root, $);
26411			}
26412		};
26413
26414		if (typeof window !== 'undefined') {
26415			module.exports = function (root, $) {
26416				if ( ! root ) {
26417					// CommonJS environments without a window global must pass a
26418					// root. This will give an error otherwise
26419					root = window;
26420				}
26421
26422				if ( ! $ ) {
26423					$ = jq( root );
26424				}
26425
26426				cjsRequires( root, $ );
26427				return factory( $, root, root.document );
26428			};
26429		}
26430		else {
26431			cjsRequires( window, jq );
26432			module.exports = factory( jq, window, window.document );
26433		}
26434	}
26435	else {
26436		// Browser
26437		factory( jQuery, window, document );
26438	}
26439}(function( $, window, document, undefined ) {
26440'use strict';
26441var DataTable = $.fn.dataTable;
26442
26443
26444
26445/**
26446 * @summary     Responsive
26447 * @description Responsive tables plug-in for DataTables
26448 * @version     2.4.1
26449 * @author      SpryMedia Ltd (www.sprymedia.co.uk)
26450 * @contact     www.sprymedia.co.uk/contact
26451 * @copyright   SpryMedia Ltd.
26452 *
26453 * This source file is free software, available under the following license:
26454 *   MIT license - http://datatables.net/license/mit
26455 *
26456 * This source file is distributed in the hope that it will be useful, but
26457 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
26458 * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
26459 *
26460 * For details please refer to: http://www.datatables.net
26461 */
26462
26463/**
26464 * Responsive is a plug-in for the DataTables library that makes use of
26465 * DataTables' ability to change the visibility of columns, changing the
26466 * visibility of columns so the displayed columns fit into the table container.
26467 * The end result is that complex tables will be dynamically adjusted to fit
26468 * into the viewport, be it on a desktop, tablet or mobile browser.
26469 *
26470 * Responsive for DataTables has two modes of operation, which can used
26471 * individually or combined:
26472 *
26473 * * Class name based control - columns assigned class names that match the
26474 *   breakpoint logic can be shown / hidden as required for each breakpoint.
26475 * * Automatic control - columns are automatically hidden when there is no
26476 *   room left to display them. Columns removed from the right.
26477 *
26478 * In additional to column visibility control, Responsive also has built into
26479 * options to use DataTables' child row display to show / hide the information
26480 * from the table that has been hidden. There are also two modes of operation
26481 * for this child row display:
26482 *
26483 * * Inline - when the control element that the user can use to show / hide
26484 *   child rows is displayed inside the first column of the table.
26485 * * Column - where a whole column is dedicated to be the show / hide control.
26486 *
26487 * Initialisation of Responsive is performed by:
26488 *
26489 * * Adding the class `responsive` or `dt-responsive` to the table. In this case
26490 *   Responsive will automatically be initialised with the default configuration
26491 *   options when the DataTable is created.
26492 * * Using the `responsive` option in the DataTables configuration options. This
26493 *   can also be used to specify the configuration options, or simply set to
26494 *   `true` to use the defaults.
26495 *
26496 *  @class
26497 *  @param {object} settings DataTables settings object for the host table
26498 *  @param {object} [opts] Configuration options
26499 *  @requires jQuery 1.7+
26500 *  @requires DataTables 1.10.3+
26501 *
26502 *  @example
26503 *      $('#example').DataTable( {
26504 *        responsive: true
26505 *      } );
26506 *    } );
26507 */
26508var Responsive = function ( settings, opts ) {
26509	// Sanity check that we are using DataTables 1.10 or newer
26510	if ( ! DataTable.versionCheck || ! DataTable.versionCheck( '1.10.10' ) ) {
26511		throw 'DataTables Responsive requires DataTables 1.10.10 or newer';
26512	}
26513
26514	this.s = {
26515		childNodeStore: {},
26516		columns: [],
26517		current: [],
26518		dt: new DataTable.Api( settings )
26519	};
26520
26521	// Check if responsive has already been initialised on this table
26522	if ( this.s.dt.settings()[0].responsive ) {
26523		return;
26524	}
26525
26526	// details is an object, but for simplicity the user can give it as a string
26527	// or a boolean
26528	if ( opts && typeof opts.details === 'string' ) {
26529		opts.details = { type: opts.details };
26530	}
26531	else if ( opts && opts.details === false ) {
26532		opts.details = { type: false };
26533	}
26534	else if ( opts && opts.details === true ) {
26535		opts.details = { type: 'inline' };
26536	}
26537
26538	this.c = $.extend( true, {}, Responsive.defaults, DataTable.defaults.responsive, opts );
26539	settings.responsive = this;
26540	this._constructor();
26541};
26542
26543$.extend( Responsive.prototype, {
26544	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
26545	 * Constructor
26546	 */
26547
26548	/**
26549	 * Initialise the Responsive instance
26550	 *
26551	 * @private
26552	 */
26553	_constructor: function ()
26554	{
26555		var that = this;
26556		var dt = this.s.dt;
26557		var dtPrivateSettings = dt.settings()[0];
26558		var oldWindowWidth = $(window).innerWidth();
26559
26560		dt.settings()[0]._responsive = this;
26561
26562		// Use DataTables' throttle function to avoid processor thrashing on
26563		// resize
26564		$(window).on( 'resize.dtr orientationchange.dtr', DataTable.util.throttle( function () {
26565			// iOS has a bug whereby resize can fire when only scrolling
26566			// See: http://stackoverflow.com/questions/8898412
26567			var width = $(window).innerWidth();
26568
26569			if ( width !== oldWindowWidth ) {
26570				that._resize();
26571				oldWindowWidth = width;
26572			}
26573		} ) );
26574
26575		// DataTables doesn't currently trigger an event when a row is added, so
26576		// we need to hook into its private API to enforce the hidden rows when
26577		// new data is added
26578		dtPrivateSettings.oApi._fnCallbackReg( dtPrivateSettings, 'aoRowCreatedCallback', function (tr, data, idx) {
26579			if ( $.inArray( false, that.s.current ) !== -1 ) {
26580				$('>td, >th', tr).each( function ( i ) {
26581					var idx = dt.column.index( 'toData', i );
26582
26583					if ( that.s.current[idx] === false ) {
26584						$(this).css('display', 'none');
26585					}
26586				} );
26587			}
26588		} );
26589
26590		// Destroy event handler
26591		dt.on( 'destroy.dtr', function () {
26592			dt.off( '.dtr' );
26593			$( dt.table().body() ).off( '.dtr' );
26594			$(window).off( 'resize.dtr orientationchange.dtr' );
26595			dt.cells('.dtr-control').nodes().to$().removeClass('dtr-control');
26596
26597			// Restore the columns that we've hidden
26598			$.each( that.s.current, function ( i, val ) {
26599				if ( val === false ) {
26600					that._setColumnVis( i, true );
26601				}
26602			} );
26603		} );
26604
26605		// Reorder the breakpoints array here in case they have been added out
26606		// of order
26607		this.c.breakpoints.sort( function (a, b) {
26608			return a.width < b.width ? 1 :
26609				a.width > b.width ? -1 : 0;
26610		} );
26611
26612		this._classLogic();
26613		this._resizeAuto();
26614
26615		// Details handler
26616		var details = this.c.details;
26617
26618		if ( details.type !== false ) {
26619			that._detailsInit();
26620
26621			// DataTables will trigger this event on every column it shows and
26622			// hides individually
26623			dt.on( 'column-visibility.dtr', function () {
26624				// Use a small debounce to allow multiple columns to be set together
26625				if ( that._timer ) {
26626					clearTimeout( that._timer );
26627				}
26628
26629				that._timer = setTimeout( function () {
26630					that._timer = null;
26631
26632					that._classLogic();
26633					that._resizeAuto();
26634					that._resize(true);
26635
26636					that._redrawChildren();
26637				}, 100 );
26638			} );
26639
26640			// Redraw the details box on each draw which will happen if the data
26641			// has changed. This is used until DataTables implements a native
26642			// `updated` event for rows
26643			dt.on( 'draw.dtr', function () {
26644				that._redrawChildren();
26645			} );
26646
26647			$(dt.table().node()).addClass( 'dtr-'+details.type );
26648		}
26649
26650		dt.on( 'column-reorder.dtr', function (e, settings, details) {
26651			that._classLogic();
26652			that._resizeAuto();
26653			that._resize(true);
26654		} );
26655
26656		// Change in column sizes means we need to calc
26657		dt.on( 'column-sizing.dtr', function () {
26658			that._resizeAuto();
26659			that._resize();
26660		});
26661
26662		// DT2 let's us tell it if we are hiding columns
26663		dt.on( 'column-calc.dt', function (e, d) {
26664			var curr = that.s.current;
26665
26666			for (var i=0 ; i<curr.length ; i++) {
26667				var idx = d.visible.indexOf(i);
26668
26669				if (curr[i] === false && idx >= 0) {
26670					d.visible.splice(idx, 1);
26671				}
26672			}
26673		} );
26674
26675		// On Ajax reload we want to reopen any child rows which are displayed
26676		// by responsive
26677		dt.on( 'preXhr.dtr', function () {
26678			var rowIds = [];
26679			dt.rows().every( function () {
26680				if ( this.child.isShown() ) {
26681					rowIds.push( this.id(true) );
26682				}
26683			} );
26684
26685			dt.one( 'draw.dtr', function () {
26686				that._resizeAuto();
26687				that._resize();
26688
26689				dt.rows( rowIds ).every( function () {
26690					that._detailsDisplay( this, false );
26691				} );
26692			} );
26693		});
26694
26695		dt
26696			.on( 'draw.dtr', function () {
26697				that._controlClass();
26698			})
26699			.on( 'init.dtr', function (e, settings, details) {
26700				if ( e.namespace !== 'dt' ) {
26701					return;
26702				}
26703
26704				that._resizeAuto();
26705				that._resize();
26706
26707				// If columns were hidden, then DataTables needs to adjust the
26708				// column sizing
26709				if ( $.inArray( false, that.s.current ) ) {
26710					dt.columns.adjust();
26711				}
26712			} );
26713
26714		// First pass - draw the table for the current viewport size
26715		this._resize();
26716	},
26717
26718
26719	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
26720	 * Private methods
26721	 */
26722
26723	/**
26724	 * Get and store nodes from a cell - use for node moving renderers
26725	 *
26726	 * @param {*} dt DT instance
26727	 * @param {*} row Row index
26728	 * @param {*} col Column index
26729	 */
26730	_childNodes: function( dt, row, col ) {
26731		var name = row+'-'+col;
26732
26733		if ( this.s.childNodeStore[ name ] ) {
26734			return this.s.childNodeStore[ name ];
26735		}
26736
26737		// https://jsperf.com/childnodes-array-slice-vs-loop
26738		var nodes = [];
26739		var children = dt.cell( row, col ).node().childNodes;
26740		for ( var i=0, ien=children.length ; i<ien ; i++ ) {
26741			nodes.push( children[i] );
26742		}
26743
26744		this.s.childNodeStore[ name ] = nodes;
26745
26746		return nodes;
26747	},
26748
26749	/**
26750	 * Restore nodes from the cache to a table cell
26751	 *
26752	 * @param {*} dt DT instance
26753	 * @param {*} row Row index
26754	 * @param {*} col Column index
26755	 */
26756	_childNodesRestore: function( dt, row, col ) {
26757		var name = row+'-'+col;
26758
26759		if ( ! this.s.childNodeStore[ name ] ) {
26760			return;
26761		}
26762
26763		var node = dt.cell( row, col ).node();
26764		var store = this.s.childNodeStore[ name ];
26765		var parent = store[0].parentNode;
26766		var parentChildren = parent.childNodes;
26767		var a = [];
26768
26769		for ( var i=0, ien=parentChildren.length ; i<ien ; i++ ) {
26770			a.push( parentChildren[i] );
26771		}
26772
26773		for ( var j=0, jen=a.length ; j<jen ; j++ ) {
26774			node.appendChild( a[j] );
26775		}
26776
26777		this.s.childNodeStore[ name ] = undefined;
26778	},
26779
26780	/**
26781	 * Calculate the visibility for the columns in a table for a given
26782	 * breakpoint. The result is pre-determined based on the class logic if
26783	 * class names are used to control all columns, but the width of the table
26784	 * is also used if there are columns which are to be automatically shown
26785	 * and hidden.
26786	 *
26787	 * @param  {string} breakpoint Breakpoint name to use for the calculation
26788	 * @return {array} Array of boolean values initiating the visibility of each
26789	 *   column.
26790	 *  @private
26791	 */
26792	_columnsVisiblity: function ( breakpoint )
26793	{
26794		var dt = this.s.dt;
26795		var columns = this.s.columns;
26796		var i, ien;
26797
26798		// Create an array that defines the column ordering based first on the
26799		// column's priority, and secondly the column index. This allows the
26800		// columns to be removed from the right if the priority matches
26801		var order = columns
26802			.map( function ( col, idx ) {
26803				return {
26804					columnIdx: idx,
26805					priority: col.priority
26806				};
26807			} )
26808			.sort( function ( a, b ) {
26809				if ( a.priority !== b.priority ) {
26810					return a.priority - b.priority;
26811				}
26812				return a.columnIdx - b.columnIdx;
26813			} );
26814
26815		// Class logic - determine which columns are in this breakpoint based
26816		// on the classes. If no class control (i.e. `auto`) then `-` is used
26817		// to indicate this to the rest of the function
26818		var display = $.map( columns, function ( col, i ) {
26819			if ( dt.column(i).visible() === false ) {
26820				return 'not-visible';
26821			}
26822			return col.auto && col.minWidth === null ?
26823				false :
26824				col.auto === true ?
26825					'-' :
26826					$.inArray( breakpoint, col.includeIn ) !== -1;
26827		} );
26828
26829		// Auto column control - first pass: how much width is taken by the
26830		// ones that must be included from the non-auto columns
26831		var requiredWidth = 0;
26832		for ( i=0, ien=display.length ; i<ien ; i++ ) {
26833			if ( display[i] === true ) {
26834				requiredWidth += columns[i].minWidth;
26835			}
26836		}
26837
26838		// Second pass, use up any remaining width for other columns. For
26839		// scrolling tables we need to subtract the width of the scrollbar. It
26840		// may not be requires which makes this sub-optimal, but it would
26841		// require another full redraw to make complete use of those extra few
26842		// pixels
26843		var scrolling = dt.settings()[0].oScroll;
26844		var bar = scrolling.sY || scrolling.sX ? scrolling.iBarWidth : 0;
26845		var widthAvailable = dt.table().container().offsetWidth - bar;
26846		var usedWidth = widthAvailable - requiredWidth;
26847
26848		// Control column needs to always be included. This makes it sub-
26849		// optimal in terms of using the available with, but to stop layout
26850		// thrashing or overflow. Also we need to account for the control column
26851		// width first so we know how much width is available for the other
26852		// columns, since the control column might not be the first one shown
26853		for ( i=0, ien=display.length ; i<ien ; i++ ) {
26854			if ( columns[i].control ) {
26855				usedWidth -= columns[i].minWidth;
26856			}
26857		}
26858
26859		// Allow columns to be shown (counting by priority and then right to
26860		// left) until we run out of room
26861		var empty = false;
26862		for ( i=0, ien=order.length ; i<ien ; i++ ) {
26863			var colIdx = order[i].columnIdx;
26864
26865			if ( display[colIdx] === '-' && ! columns[colIdx].control && columns[colIdx].minWidth ) {
26866				// Once we've found a column that won't fit we don't let any
26867				// others display either, or columns might disappear in the
26868				// middle of the table
26869				if ( empty || usedWidth - columns[colIdx].minWidth < 0 ) {
26870					empty = true;
26871					display[colIdx] = false;
26872				}
26873				else {
26874					display[colIdx] = true;
26875				}
26876
26877				usedWidth -= columns[colIdx].minWidth;
26878			}
26879		}
26880
26881		// Determine if the 'control' column should be shown (if there is one).
26882		// This is the case when there is a hidden column (that is not the
26883		// control column). The two loops look inefficient here, but they are
26884		// trivial and will fly through. We need to know the outcome from the
26885		// first , before the action in the second can be taken
26886		var showControl = false;
26887
26888		for ( i=0, ien=columns.length ; i<ien ; i++ ) {
26889			if ( ! columns[i].control && ! columns[i].never && display[i] === false ) {
26890				showControl = true;
26891				break;
26892			}
26893		}
26894
26895		for ( i=0, ien=columns.length ; i<ien ; i++ ) {
26896			if ( columns[i].control ) {
26897				display[i] = showControl;
26898			}
26899
26900			// Replace not visible string with false from the control column detection above
26901			if ( display[i] === 'not-visible' ) {
26902				display[i] = false;
26903			}
26904		}
26905
26906		// Finally we need to make sure that there is at least one column that
26907		// is visible
26908		if ( $.inArray( true, display ) === -1 ) {
26909			display[0] = true;
26910		}
26911
26912		return display;
26913	},
26914
26915
26916	/**
26917	 * Create the internal `columns` array with information about the columns
26918	 * for the table. This includes determining which breakpoints the column
26919	 * will appear in, based upon class names in the column, which makes up the
26920	 * vast majority of this method.
26921	 *
26922	 * @private
26923	 */
26924	_classLogic: function ()
26925	{
26926		var that = this;
26927		var calc = {};
26928		var breakpoints = this.c.breakpoints;
26929		var dt = this.s.dt;
26930		var columns = dt.columns().eq(0).map( function (i) {
26931			var column = this.column(i);
26932			var className = column.header().className;
26933			var priority = dt.settings()[0].aoColumns[i].responsivePriority;
26934			var dataPriority = column.header().getAttribute('data-priority');
26935
26936			if ( priority === undefined ) {
26937				priority = dataPriority === undefined || dataPriority === null?
26938					10000 :
26939					dataPriority * 1;
26940			}
26941
26942			return {
26943				className: className,
26944				includeIn: [],
26945				auto:      false,
26946				control:   false,
26947				never:     className.match(/\b(dtr\-)?never\b/) ? true : false,
26948				priority:  priority
26949			};
26950		} );
26951
26952		// Simply add a breakpoint to `includeIn` array, ensuring that there are
26953		// no duplicates
26954		var add = function ( colIdx, name ) {
26955			var includeIn = columns[ colIdx ].includeIn;
26956
26957			if ( $.inArray( name, includeIn ) === -1 ) {
26958				includeIn.push( name );
26959			}
26960		};
26961
26962		var column = function ( colIdx, name, operator, matched ) {
26963			var size, i, ien;
26964
26965			if ( ! operator ) {
26966				columns[ colIdx ].includeIn.push( name );
26967			}
26968			else if ( operator === 'max-' ) {
26969				// Add this breakpoint and all smaller
26970				size = that._find( name ).width;
26971
26972				for ( i=0, ien=breakpoints.length ; i<ien ; i++ ) {
26973					if ( breakpoints[i].width <= size ) {
26974						add( colIdx, breakpoints[i].name );
26975					}
26976				}
26977			}
26978			else if ( operator === 'min-' ) {
26979				// Add this breakpoint and all larger
26980				size = that._find( name ).width;
26981
26982				for ( i=0, ien=breakpoints.length ; i<ien ; i++ ) {
26983					if ( breakpoints[i].width >= size ) {
26984						add( colIdx, breakpoints[i].name );
26985					}
26986				}
26987			}
26988			else if ( operator === 'not-' ) {
26989				// Add all but this breakpoint
26990				for ( i=0, ien=breakpoints.length ; i<ien ; i++ ) {
26991					if ( breakpoints[i].name.indexOf( matched ) === -1 ) {
26992						add( colIdx, breakpoints[i].name );
26993					}
26994				}
26995			}
26996		};
26997
26998		// Loop over each column and determine if it has a responsive control
26999		// class
27000		columns.each( function ( col, i ) {
27001			var classNames = col.className.split(' ');
27002			var hasClass = false;
27003
27004			// Split the class name up so multiple rules can be applied if needed
27005			for ( var k=0, ken=classNames.length ; k<ken ; k++ ) {
27006				var className = classNames[k].trim();
27007
27008				if ( className === 'all' || className === 'dtr-all' ) {
27009					// Include in all
27010					hasClass = true;
27011					col.includeIn = $.map( breakpoints, function (a) {
27012						return a.name;
27013					} );
27014					return;
27015				}
27016				else if ( className === 'none' || className === 'dtr-none' || col.never ) {
27017					// Include in none (default) and no auto
27018					hasClass = true;
27019					return;
27020				}
27021				else if ( className === 'control' || className === 'dtr-control' ) {
27022					// Special column that is only visible, when one of the other
27023					// columns is hidden. This is used for the details control
27024					hasClass = true;
27025					col.control = true;
27026					return;
27027				}
27028
27029				$.each( breakpoints, function ( j, breakpoint ) {
27030					// Does this column have a class that matches this breakpoint?
27031					var brokenPoint = breakpoint.name.split('-');
27032					var re = new RegExp( '(min\\-|max\\-|not\\-)?('+brokenPoint[0]+')(\\-[_a-zA-Z0-9])?' );
27033					var match = className.match( re );
27034
27035					if ( match ) {
27036						hasClass = true;
27037
27038						if ( match[2] === brokenPoint[0] && match[3] === '-'+brokenPoint[1] ) {
27039							// Class name matches breakpoint name fully
27040							column( i, breakpoint.name, match[1], match[2]+match[3] );
27041						}
27042						else if ( match[2] === brokenPoint[0] && ! match[3] ) {
27043							// Class name matched primary breakpoint name with no qualifier
27044							column( i, breakpoint.name, match[1], match[2] );
27045						}
27046					}
27047				} );
27048			}
27049
27050			// If there was no control class, then automatic sizing is used
27051			if ( ! hasClass ) {
27052				col.auto = true;
27053			}
27054		} );
27055
27056		this.s.columns = columns;
27057	},
27058
27059	/**
27060	 * Update the cells to show the correct control class / button
27061	 * @private
27062	 */
27063	_controlClass: function ()
27064	{
27065		if ( this.c.details.type === 'inline' ) {
27066			var dt = this.s.dt;
27067			var columnsVis = this.s.current;
27068			var firstVisible = $.inArray(true, columnsVis);
27069
27070			// Remove from any cells which shouldn't have it
27071			dt.cells(
27072				null,
27073				function(idx) {
27074					return idx !== firstVisible;
27075				},
27076				{page: 'current'}
27077			)
27078				.nodes()
27079				.to$()
27080				.filter('.dtr-control')
27081				.removeClass('dtr-control');
27082
27083			dt.cells(null, firstVisible, {page: 'current'})
27084				.nodes()
27085				.to$()
27086				.addClass('dtr-control');
27087		}
27088	},
27089
27090	/**
27091	 * Show the details for the child row
27092	 *
27093	 * @param  {DataTables.Api} row    API instance for the row
27094	 * @param  {boolean}        update Update flag
27095	 * @private
27096	 */
27097	_detailsDisplay: function ( row, update )
27098	{
27099		var that = this;
27100		var dt = this.s.dt;
27101		var details = this.c.details;
27102
27103		if ( details && details.type !== false ) {
27104			var renderer = typeof details.renderer === 'string'
27105				? Responsive.renderer[details.renderer]()
27106				: details.renderer;
27107
27108			var res = details.display( row, update, function () {
27109				return renderer.call(
27110					that, dt, row[0], that._detailsObj(row[0])
27111				);
27112			} );
27113
27114			if ( res === true || res === false ) {
27115				$(dt.table().node()).triggerHandler( 'responsive-display.dt', [dt, row, res, update] );
27116			}
27117		}
27118	},
27119
27120
27121	/**
27122	 * Initialisation for the details handler
27123	 *
27124	 * @private
27125	 */
27126	_detailsInit: function ()
27127	{
27128		var that    = this;
27129		var dt      = this.s.dt;
27130		var details = this.c.details;
27131
27132		// The inline type always uses the first child as the target
27133		if ( details.type === 'inline' ) {
27134			details.target = 'td.dtr-control, th.dtr-control';
27135		}
27136
27137		// Keyboard accessibility
27138		dt.on( 'draw.dtr', function () {
27139			that._tabIndexes();
27140		} );
27141		that._tabIndexes(); // Initial draw has already happened
27142
27143		$( dt.table().body() ).on( 'keyup.dtr', 'td, th', function (e) {
27144			if ( e.keyCode === 13 && $(this).data('dtr-keyboard') ) {
27145				$(this).click();
27146			}
27147		} );
27148
27149		// type.target can be a string jQuery selector or a column index
27150		var target   = details.target;
27151		var selector = typeof target === 'string' ? target : 'td, th';
27152
27153		if ( target !== undefined || target !== null ) {
27154			// Click handler to show / hide the details rows when they are available
27155			$( dt.table().body() )
27156				.on( 'click.dtr mousedown.dtr mouseup.dtr', selector, function (e) {
27157					// If the table is not collapsed (i.e. there is no hidden columns)
27158					// then take no action
27159					if ( ! $(dt.table().node()).hasClass('collapsed' ) ) {
27160						return;
27161					}
27162
27163					// Check that the row is actually a DataTable's controlled node
27164					if ( $.inArray( $(this).closest('tr').get(0), dt.rows().nodes().toArray() ) === -1 ) {
27165						return;
27166					}
27167
27168					// For column index, we determine if we should act or not in the
27169					// handler - otherwise it is already okay
27170					if ( typeof target === 'number' ) {
27171						var targetIdx = target < 0 ?
27172							dt.columns().eq(0).length + target :
27173							target;
27174
27175						if ( dt.cell( this ).index().column !== targetIdx ) {
27176							return;
27177						}
27178					}
27179
27180					// $().closest() includes itself in its check
27181					var row = dt.row( $(this).closest('tr') );
27182
27183					// Check event type to do an action
27184					if ( e.type === 'click' ) {
27185						// The renderer is given as a function so the caller can execute it
27186						// only when they need (i.e. if hiding there is no point is running
27187						// the renderer)
27188						that._detailsDisplay( row, false );
27189					}
27190					else if ( e.type === 'mousedown' ) {
27191						// For mouse users, prevent the focus ring from showing
27192						$(this).css('outline', 'none');
27193					}
27194					else if ( e.type === 'mouseup' ) {
27195						// And then re-allow at the end of the click
27196						$(this).trigger('blur').css('outline', '');
27197					}
27198				} );
27199		}
27200	},
27201
27202
27203	/**
27204	 * Get the details to pass to a renderer for a row
27205	 * @param  {int} rowIdx Row index
27206	 * @private
27207	 */
27208	_detailsObj: function ( rowIdx )
27209	{
27210		var that = this;
27211		var dt = this.s.dt;
27212
27213		return $.map( this.s.columns, function( col, i ) {
27214			// Never and control columns should not be passed to the renderer
27215			if ( col.never || col.control ) {
27216				return;
27217			}
27218
27219			var dtCol = dt.settings()[0].aoColumns[ i ];
27220
27221			return {
27222				className:   dtCol.sClass,
27223				columnIndex: i,
27224				data:        dt.cell( rowIdx, i ).render( that.c.orthogonal ),
27225				hidden:      dt.column( i ).visible() && !that.s.current[ i ],
27226				rowIndex:    rowIdx,
27227				title:       dtCol.sTitle !== null ?
27228					dtCol.sTitle :
27229					$(dt.column(i).header()).text()
27230			};
27231		} );
27232	},
27233
27234
27235	/**
27236	 * Find a breakpoint object from a name
27237	 *
27238	 * @param  {string} name Breakpoint name to find
27239	 * @return {object}      Breakpoint description object
27240	 * @private
27241	 */
27242	_find: function ( name )
27243	{
27244		var breakpoints = this.c.breakpoints;
27245
27246		for ( var i=0, ien=breakpoints.length ; i<ien ; i++ ) {
27247			if ( breakpoints[i].name === name ) {
27248				return breakpoints[i];
27249			}
27250		}
27251	},
27252
27253
27254	/**
27255	 * Re-create the contents of the child rows as the display has changed in
27256	 * some way.
27257	 *
27258	 * @private
27259	 */
27260	_redrawChildren: function ()
27261	{
27262		var that = this;
27263		var dt = this.s.dt;
27264
27265		dt.rows( {page: 'current'} ).iterator( 'row', function ( settings, idx ) {
27266			var row = dt.row( idx );
27267
27268			that._detailsDisplay( dt.row( idx ), true );
27269		} );
27270	},
27271
27272
27273	/**
27274	 * Alter the table display for a resized viewport. This involves first
27275	 * determining what breakpoint the window currently is in, getting the
27276	 * column visibilities to apply and then setting them.
27277	 *
27278	 * @param  {boolean} forceRedraw Force a redraw
27279	 * @private
27280	 */
27281	_resize: function (forceRedraw)
27282	{
27283		var that = this;
27284		var dt = this.s.dt;
27285		var width = $(window).innerWidth();
27286		var breakpoints = this.c.breakpoints;
27287		var breakpoint = breakpoints[0].name;
27288		var columns = this.s.columns;
27289		var i, ien;
27290		var oldVis = this.s.current.slice();
27291
27292		// Determine what breakpoint we are currently at
27293		for ( i=breakpoints.length-1 ; i>=0 ; i-- ) {
27294			if ( width <= breakpoints[i].width ) {
27295				breakpoint = breakpoints[i].name;
27296				break;
27297			}
27298		}
27299		
27300		// Show the columns for that break point
27301		var columnsVis = this._columnsVisiblity( breakpoint );
27302		this.s.current = columnsVis;
27303
27304		// Set the class before the column visibility is changed so event
27305		// listeners know what the state is. Need to determine if there are
27306		// any columns that are not visible but can be shown
27307		var collapsedClass = false;
27308	
27309		for ( i=0, ien=columns.length ; i<ien ; i++ ) {
27310			if ( columnsVis[i] === false && ! columns[i].never && ! columns[i].control && ! dt.column(i).visible() === false ) {
27311				collapsedClass = true;
27312				break;
27313			}
27314		}
27315
27316		$( dt.table().node() ).toggleClass( 'collapsed', collapsedClass );
27317
27318		var changed = false;
27319		var visible = 0;
27320
27321		dt.columns().eq(0).each( function ( colIdx, i ) {
27322			if ( columnsVis[i] === true ) {
27323				visible++;
27324			}
27325
27326			if ( forceRedraw || columnsVis[i] !== oldVis[i] ) {
27327				changed = true;
27328				that._setColumnVis( colIdx, columnsVis[i] );
27329			}
27330		} );
27331
27332		// Always need to update the display, regardless of if it has changed or not, so nodes
27333		// can be re-inserted for listHiddenNodes
27334		this._redrawChildren();
27335
27336		if ( changed ) {
27337			// Inform listeners of the change
27338			$(dt.table().node()).trigger( 'responsive-resize.dt', [dt, this.s.current] );
27339
27340			// If no records, update the "No records" display element
27341			if ( dt.page.info().recordsDisplay === 0 ) {
27342				$('td', dt.table().body()).eq(0).attr('colspan', visible);
27343			}
27344		}
27345
27346		that._controlClass();
27347	},
27348
27349
27350	/**
27351	 * Determine the width of each column in the table so the auto column hiding
27352	 * has that information to work with. This method is never going to be 100%
27353	 * perfect since column widths can change slightly per page, but without
27354	 * seriously compromising performance this is quite effective.
27355	 *
27356	 * @private
27357	 */
27358	_resizeAuto: function ()
27359	{
27360		var dt = this.s.dt;
27361		var columns = this.s.columns;
27362		var that = this;
27363
27364		// Are we allowed to do auto sizing?
27365		if ( ! this.c.auto ) {
27366			return;
27367		}
27368
27369		// Are there any columns that actually need auto-sizing, or do they all
27370		// have classes defined
27371		if ( $.inArray( true, $.map( columns, function (c) { return c.auto; } ) ) === -1 ) {
27372			return;
27373		}
27374
27375		// Need to restore all children. They will be reinstated by a re-render
27376		if ( ! $.isEmptyObject( this.s.childNodeStore ) ) {
27377			$.each( this.s.childNodeStore, function ( key ) {
27378				var idx = key.split('-');
27379
27380				that._childNodesRestore( dt, idx[0]*1, idx[1]*1 );
27381			} );
27382		}
27383
27384		// Clone the table with the current data in it
27385		var tableWidth   = dt.table().node().offsetWidth;
27386		var columnWidths = dt.columns;
27387		var clonedTable  = dt.table().node().cloneNode( false );
27388		var clonedHeader = $( dt.table().header().cloneNode( false ) ).appendTo( clonedTable );
27389		var clonedBody   = $( dt.table().body() ).clone( false, false ).empty().appendTo( clonedTable ); // use jQuery because of IE8
27390
27391		clonedTable.style.width = 'auto';
27392
27393		// Header
27394		var headerCells = dt.columns()
27395			.header()
27396			.filter( function (idx) {
27397				return dt.column(idx).visible();
27398			} )
27399			.to$()
27400			.clone( false )
27401			.css( 'display', 'table-cell' )
27402			.css( 'width', 'auto' )
27403			.css( 'min-width', 0 );
27404
27405		// Body rows - we don't need to take account of DataTables' column
27406		// visibility since we implement our own here (hence the `display` set)
27407		$(clonedBody)
27408			.append( $(dt.rows( { page: 'current' } ).nodes()).clone( false ) )
27409			.find( 'th, td' ).css( 'display', '' );
27410
27411		// Footer
27412		var footer = dt.table().footer();
27413		if ( footer ) {
27414			var clonedFooter = $( footer.cloneNode( false ) ).appendTo( clonedTable );
27415			var footerCells = dt.columns()
27416				.footer()
27417				.filter( function (idx) {
27418					return dt.column(idx).visible();
27419				} )
27420				.to$()
27421				.clone( false )
27422				.css( 'display', 'table-cell' );
27423
27424			$('<tr/>')
27425				.append( footerCells )
27426				.appendTo( clonedFooter );
27427		}
27428
27429		$('<tr/>')
27430			.append( headerCells )
27431			.appendTo( clonedHeader );
27432
27433		// In the inline case extra padding is applied to the first column to
27434		// give space for the show / hide icon. We need to use this in the
27435		// calculation
27436		if ( this.c.details.type === 'inline' ) {
27437			$(clonedTable).addClass( 'dtr-inline collapsed' );
27438		}
27439		
27440		// It is unsafe to insert elements with the same name into the DOM
27441		// multiple times. For example, cloning and inserting a checked radio
27442		// clears the chcecked state of the original radio.
27443		$( clonedTable ).find( '[name]' ).removeAttr( 'name' );
27444
27445		// A position absolute table would take the table out of the flow of
27446		// our container element, bypassing the height and width (Scroller)
27447		$( clonedTable ).css( 'position', 'relative' )
27448		
27449		var inserted = $('<div/>')
27450			.css( {
27451				width: 1,
27452				height: 1,
27453				overflow: 'hidden',
27454				clear: 'both'
27455			} )
27456			.append( clonedTable );
27457
27458		inserted.insertBefore( dt.table().node() );
27459
27460		// The cloned header now contains the smallest that each column can be
27461		headerCells.each( function (i) {
27462			var idx = dt.column.index( 'fromVisible', i );
27463			columns[ idx ].minWidth =  this.offsetWidth || 0;
27464		} );
27465
27466		inserted.remove();
27467	},
27468
27469	/**
27470	 * Get the state of the current hidden columns - controlled by Responsive only
27471	 */
27472	_responsiveOnlyHidden: function ()
27473	{
27474		var dt = this.s.dt;
27475
27476		return $.map( this.s.current, function (v, i) {
27477			// If the column is hidden by DataTables then it can't be hidden by
27478			// Responsive!
27479			if ( dt.column(i).visible() === false ) {
27480				return true;
27481			}
27482			return v;
27483		} );
27484	},
27485
27486	/**
27487	 * Set a column's visibility.
27488	 *
27489	 * We don't use DataTables' column visibility controls in order to ensure
27490	 * that column visibility can Responsive can no-exist. Since only IE8+ is
27491	 * supported (and all evergreen browsers of course) the control of the
27492	 * display attribute works well.
27493	 *
27494	 * @param {integer} col      Column index
27495	 * @param {boolean} showHide Show or hide (true or false)
27496	 * @private
27497	 */
27498	_setColumnVis: function ( col, showHide )
27499	{
27500		var that = this;
27501		var dt = this.s.dt;
27502		var display = showHide ? '' : 'none'; // empty string will remove the attr
27503
27504		$( dt.column( col ).header() )
27505			.css( 'display', display )
27506			.toggleClass('dtr-hidden', !showHide);
27507
27508		$( dt.column( col ).footer() )
27509			.css( 'display', display )
27510			.toggleClass('dtr-hidden', !showHide);
27511
27512		dt.column( col ).nodes().to$()
27513			.css( 'display', display )
27514			.toggleClass('dtr-hidden', !showHide);
27515
27516		// If the are child nodes stored, we might need to reinsert them
27517		if ( ! $.isEmptyObject( this.s.childNodeStore ) ) {
27518			dt.cells( null, col ).indexes().each( function (idx) {
27519				that._childNodesRestore( dt, idx.row, idx.column );
27520			} );
27521		}
27522	},
27523
27524
27525	/**
27526	 * Update the cell tab indexes for keyboard accessibility. This is called on
27527	 * every table draw - that is potentially inefficient, but also the least
27528	 * complex option given that column visibility can change on the fly. Its a
27529	 * shame user-focus was removed from CSS 3 UI, as it would have solved this
27530	 * issue with a single CSS statement.
27531	 *
27532	 * @private
27533	 */
27534	_tabIndexes: function ()
27535	{
27536		var dt = this.s.dt;
27537		var cells = dt.cells( { page: 'current' } ).nodes().to$();
27538		var ctx = dt.settings()[0];
27539		var target = this.c.details.target;
27540
27541		cells.filter( '[data-dtr-keyboard]' ).removeData( '[data-dtr-keyboard]' );
27542
27543		if ( typeof target === 'number' ) {
27544			dt.cells( null, target, { page: 'current' } ).nodes().to$()
27545				.attr( 'tabIndex', ctx.iTabIndex )
27546				.data( 'dtr-keyboard', 1 );
27547		}
27548		else {
27549			// This is a bit of a hack - we need to limit the selected nodes to just
27550			// those of this table
27551			if ( target === 'td:first-child, th:first-child' ) {
27552				target = '>td:first-child, >th:first-child';
27553			}
27554
27555			$( target, dt.rows( { page: 'current' } ).nodes() )
27556				.attr( 'tabIndex', ctx.iTabIndex )
27557				.data( 'dtr-keyboard', 1 );
27558		}
27559	}
27560} );
27561
27562
27563/**
27564 * List of default breakpoints. Each item in the array is an object with two
27565 * properties:
27566 *
27567 * * `name` - the breakpoint name.
27568 * * `width` - the breakpoint width
27569 *
27570 * @name Responsive.breakpoints
27571 * @static
27572 */
27573Responsive.breakpoints = [
27574	{ name: 'desktop',  width: Infinity },
27575	{ name: 'tablet-l', width: 1024 },
27576	{ name: 'tablet-p', width: 768 },
27577	{ name: 'mobile-l', width: 480 },
27578	{ name: 'mobile-p', width: 320 }
27579];
27580
27581
27582/**
27583 * Display methods - functions which define how the hidden data should be shown
27584 * in the table.
27585 *
27586 * @namespace
27587 * @name Responsive.defaults
27588 * @static
27589 */
27590Responsive.display = {
27591	childRow: function ( row, update, render ) {
27592		if ( update ) {
27593			if ( $(row.node()).hasClass('parent') ) {
27594				row.child( render(), 'child' ).show();
27595
27596				return true;
27597			}
27598		}
27599		else {
27600			if ( ! row.child.isShown()  ) {
27601				row.child( render(), 'child' ).show();
27602				$( row.node() ).addClass( 'parent' );
27603
27604				return true;
27605			}
27606			else {
27607				row.child( false );
27608				$( row.node() ).removeClass( 'parent' );
27609
27610				return false;
27611			}
27612		}
27613	},
27614
27615	childRowImmediate: function ( row, update, render ) {
27616		if ( (! update && row.child.isShown()) || ! row.responsive.hasHidden() ) {
27617			// User interaction and the row is show, or nothing to show
27618			row.child( false );
27619			$( row.node() ).removeClass( 'parent' );
27620
27621			return false;
27622		}
27623		else {
27624			// Display
27625			row.child( render(), 'child' ).show();
27626			$( row.node() ).addClass( 'parent' );
27627
27628			return true;
27629		}
27630	},
27631
27632	// This is a wrapper so the modal options for Bootstrap and jQuery UI can
27633	// have options passed into them. This specific one doesn't need to be a
27634	// function but it is for consistency in the `modal` name
27635	modal: function ( options ) {
27636		return function ( row, update, render ) {
27637			if ( ! update ) {
27638				// Show a modal
27639				var close = function () {
27640					modal.remove(); // will tidy events for us
27641					$(document).off( 'keypress.dtr' );
27642				};
27643
27644				var modal = $('<div class="dtr-modal"/>')
27645					.append( $('<div class="dtr-modal-display"/>')
27646						.append( $('<div class="dtr-modal-content"/>')
27647							.append( render() )
27648						)
27649						.append( $('<div class="dtr-modal-close">&times;</div>' )
27650							.click( function () {
27651								close();
27652							} )
27653						)
27654					)
27655					.append( $('<div class="dtr-modal-background"/>')
27656						.click( function () {
27657							close();
27658						} )
27659					)
27660					.appendTo( 'body' );
27661
27662				$(document).on( 'keyup.dtr', function (e) {
27663					if ( e.keyCode === 27 ) {
27664						e.stopPropagation();
27665
27666						close();
27667					}
27668				} );
27669			}
27670			else {
27671				$('div.dtr-modal-content')
27672					.empty()
27673					.append( render() );
27674			}
27675
27676			if ( options && options.header ) {
27677				$('div.dtr-modal-content').prepend(
27678					'<h2>'+options.header( row )+'</h2>'
27679				);
27680			}
27681		};
27682	}
27683};
27684
27685
27686/**
27687 * Display methods - functions which define how the hidden data should be shown
27688 * in the table.
27689 *
27690 * @namespace
27691 * @name Responsive.defaults
27692 * @static
27693 */
27694Responsive.renderer = {
27695	listHiddenNodes: function () {
27696		return function ( api, rowIdx, columns ) {
27697			var that = this;
27698			var ul = $('<ul data-dtr-index="'+rowIdx+'" class="dtr-details"/>');
27699			var found = false;
27700
27701			var data = $.each( columns, function ( i, col ) {
27702				if ( col.hidden ) {
27703					var klass = col.className ?
27704						'class="'+ col.className +'"' :
27705						'';
27706	
27707					$(
27708						'<li '+klass+' data-dtr-index="'+col.columnIndex+'" data-dt-row="'+col.rowIndex+'" data-dt-column="'+col.columnIndex+'">'+
27709							'<span class="dtr-title">'+
27710								col.title+
27711							'</span> '+
27712						'</li>'
27713					)
27714						.append( $('<span class="dtr-data"/>').append( that._childNodes( api, col.rowIndex, col.columnIndex ) ) )// api.cell( col.rowIndex, col.columnIndex ).node().childNodes ) )
27715						.appendTo( ul );
27716
27717					found = true;
27718				}
27719			} );
27720
27721			return found ?
27722				ul :
27723				false;
27724		};
27725	},
27726
27727	listHidden: function () {
27728		return function ( api, rowIdx, columns ) {
27729			var data = $.map( columns, function ( col ) {
27730				var klass = col.className ?
27731					'class="'+ col.className +'"' :
27732					'';
27733
27734				return col.hidden ?
27735					'<li '+klass+' data-dtr-index="'+col.columnIndex+'" data-dt-row="'+col.rowIndex+'" data-dt-column="'+col.columnIndex+'">'+
27736						'<span class="dtr-title">'+
27737							col.title+
27738						'</span> '+
27739						'<span class="dtr-data">'+
27740							col.data+
27741						'</span>'+
27742					'</li>' :
27743					'';
27744			} ).join('');
27745
27746			return data ?
27747				$('<ul data-dtr-index="'+rowIdx+'" class="dtr-details"/>').append( data ) :
27748				false;
27749		}
27750	},
27751
27752	tableAll: function ( options ) {
27753		options = $.extend( {
27754			tableClass: ''
27755		}, options );
27756
27757		return function ( api, rowIdx, columns ) {
27758			var data = $.map( columns, function ( col ) {
27759				var klass = col.className ?
27760					'class="'+ col.className +'"' :
27761					'';
27762
27763				return '<tr '+klass+' data-dt-row="'+col.rowIndex+'" data-dt-column="'+col.columnIndex+'">'+
27764						'<td>'+col.title+':'+'</td> '+
27765						'<td>'+col.data+'</td>'+
27766					'</tr>';
27767			} ).join('');
27768
27769			return $('<table class="'+options.tableClass+' dtr-details" width="100%"/>').append( data );
27770		}
27771	}
27772};
27773
27774/**
27775 * Responsive default settings for initialisation
27776 *
27777 * @namespace
27778 * @name Responsive.defaults
27779 * @static
27780 */
27781Responsive.defaults = {
27782	/**
27783	 * List of breakpoints for the instance. Note that this means that each
27784	 * instance can have its own breakpoints. Additionally, the breakpoints
27785	 * cannot be changed once an instance has been creased.
27786	 *
27787	 * @type {Array}
27788	 * @default Takes the value of `Responsive.breakpoints`
27789	 */
27790	breakpoints: Responsive.breakpoints,
27791
27792	/**
27793	 * Enable / disable auto hiding calculations. It can help to increase
27794	 * performance slightly if you disable this option, but all columns would
27795	 * need to have breakpoint classes assigned to them
27796	 *
27797	 * @type {Boolean}
27798	 * @default  `true`
27799	 */
27800	auto: true,
27801
27802	/**
27803	 * Details control. If given as a string value, the `type` property of the
27804	 * default object is set to that value, and the defaults used for the rest
27805	 * of the object - this is for ease of implementation.
27806	 *
27807	 * The object consists of the following properties:
27808	 *
27809	 * * `display` - A function that is used to show and hide the hidden details
27810	 * * `renderer` - function that is called for display of the child row data.
27811	 *   The default function will show the data from the hidden columns
27812	 * * `target` - Used as the selector for what objects to attach the child
27813	 *   open / close to
27814	 * * `type` - `false` to disable the details display, `inline` or `column`
27815	 *   for the two control types
27816	 *
27817	 * @type {Object|string}
27818	 */
27819	details: {
27820		display: Responsive.display.childRow,
27821
27822		renderer: Responsive.renderer.listHidden(),
27823
27824		target: 0,
27825
27826		type: 'inline'
27827	},
27828
27829	/**
27830	 * Orthogonal data request option. This is used to define the data type
27831	 * requested when Responsive gets the data to show in the child row.
27832	 *
27833	 * @type {String}
27834	 */
27835	orthogonal: 'display'
27836};
27837
27838
27839/*
27840 * API
27841 */
27842var Api = $.fn.dataTable.Api;
27843
27844// Doesn't do anything - work around for a bug in DT... Not documented
27845Api.register( 'responsive()', function () {
27846	return this;
27847} );
27848
27849Api.register( 'responsive.index()', function ( li ) {
27850	li = $(li);
27851
27852	return {
27853		column: li.data('dtr-index'),
27854		row:    li.parent().data('dtr-index')
27855	};
27856} );
27857
27858Api.register( 'responsive.rebuild()', function () {
27859	return this.iterator( 'table', function ( ctx ) {
27860		if ( ctx._responsive ) {
27861			ctx._responsive._classLogic();
27862		}
27863	} );
27864} );
27865
27866Api.register( 'responsive.recalc()', function () {
27867	return this.iterator( 'table', function ( ctx ) {
27868		if ( ctx._responsive ) {
27869			ctx._responsive._resizeAuto();
27870			ctx._responsive._resize();
27871		}
27872	} );
27873} );
27874
27875Api.register( 'responsive.hasHidden()', function () {
27876	var ctx = this.context[0];
27877
27878	return ctx._responsive ?
27879		$.inArray( false, ctx._responsive._responsiveOnlyHidden() ) !== -1 :
27880		false;
27881} );
27882
27883Api.registerPlural( 'columns().responsiveHidden()', 'column().responsiveHidden()', function () {
27884	return this.iterator( 'column', function ( settings, column ) {
27885		return settings._responsive ?
27886			settings._responsive._responsiveOnlyHidden()[ column ] :
27887			false;
27888	}, 1 );
27889} );
27890
27891
27892/**
27893 * Version information
27894 *
27895 * @name Responsive.version
27896 * @static
27897 */
27898Responsive.version = '2.4.1';
27899
27900
27901$.fn.dataTable.Responsive = Responsive;
27902$.fn.DataTable.Responsive = Responsive;
27903
27904// Attach a listener to the document which listens for DataTables initialisation
27905// events so we can automatically initialise
27906$(document).on( 'preInit.dt.dtr', function (e, settings, json) {
27907	if ( e.namespace !== 'dt' ) {
27908		return;
27909	}
27910
27911	if ( $(settings.nTable).hasClass( 'responsive' ) ||
27912		 $(settings.nTable).hasClass( 'dt-responsive' ) ||
27913		 settings.oInit.responsive ||
27914		 DataTable.defaults.responsive
27915	) {
27916		var init = settings.oInit.responsive;
27917
27918		if ( init !== false ) {
27919			new Responsive( settings, $.isPlainObject( init ) ? init : {}  );
27920		}
27921	}
27922} );
27923
27924
27925return DataTable;
27926}));
27927
27928
27929/*! DataTables styling wrapper for Responsive
27930 * © SpryMedia Ltd - datatables.net/license
27931 */
27932
27933(function( factory ){
27934	if ( typeof define === 'function' && define.amd ) {
27935		// AMD
27936		define( ['jquery', 'datatables.net-dt', 'datatables.net-responsive'], function ( $ ) {
27937			return factory( $, window, document );
27938		} );
27939	}
27940	else if ( typeof exports === 'object' ) {
27941		// CommonJS
27942		var jq = require('jquery');
27943		var cjsRequires = function (root, $) {
27944			if ( ! $.fn.dataTable ) {
27945				require('datatables.net-dt')(root, $);
27946			}
27947
27948			if ( ! $.fn.dataTable.Responsive ) {
27949				require('datatables.net-responsive')(root, $);
27950			}
27951		};
27952
27953		if (typeof window !== 'undefined') {
27954			module.exports = function (root, $) {
27955				if ( ! root ) {
27956					// CommonJS environments without a window global must pass a
27957					// root. This will give an error otherwise
27958					root = window;
27959				}
27960
27961				if ( ! $ ) {
27962					$ = jq( root );
27963				}
27964
27965				cjsRequires( root, $ );
27966				return factory( $, root, root.document );
27967			};
27968		}
27969		else {
27970			cjsRequires( window, jq );
27971			module.exports = factory( jq, window, window.document );
27972		}
27973	}
27974	else {
27975		// Browser
27976		factory( jQuery, window, document );
27977	}
27978}(function( $, window, document, undefined ) {
27979'use strict';
27980var DataTable = $.fn.dataTable;
27981
27982
27983
27984
27985return DataTable;
27986}));
27987
27988
27989/*! RowGroup 1.3.1
27990 * © SpryMedia Ltd - datatables.net/license
27991 */
27992
27993(function( factory ){
27994	if ( typeof define === 'function' && define.amd ) {
27995		// AMD
27996		define( ['jquery', 'datatables.net'], function ( $ ) {
27997			return factory( $, window, document );
27998		} );
27999	}
28000	else if ( typeof exports === 'object' ) {
28001		// CommonJS
28002		var jq = require('jquery');
28003		var cjsRequires = function (root, $) {
28004			if ( ! $.fn.dataTable ) {
28005				require('datatables.net')(root, $);
28006			}
28007		};
28008
28009		if (typeof window !== 'undefined') {
28010			module.exports = function (root, $) {
28011				if ( ! root ) {
28012					// CommonJS environments without a window global must pass a
28013					// root. This will give an error otherwise
28014					root = window;
28015				}
28016
28017				if ( ! $ ) {
28018					$ = jq( root );
28019				}
28020
28021				cjsRequires( root, $ );
28022				return factory( $, root, root.document );
28023			};
28024		}
28025		else {
28026			cjsRequires( window, jq );
28027			module.exports = factory( jq, window, window.document );
28028		}
28029	}
28030	else {
28031		// Browser
28032		factory( jQuery, window, document );
28033	}
28034}(function( $, window, document, undefined ) {
28035'use strict';
28036var DataTable = $.fn.dataTable;
28037
28038
28039
28040/**
28041 * @summary     RowGroup
28042 * @description RowGrouping for DataTables
28043 * @version     1.3.1
28044 * @author      SpryMedia Ltd (www.sprymedia.co.uk)
28045 * @contact     datatables.net
28046 * @copyright   SpryMedia Ltd.
28047 *
28048 * This source file is free software, available under the following license:
28049 *   MIT license - http://datatables.net/license/mit
28050 *
28051 * This source file is distributed in the hope that it will be useful, but
28052 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
28053 * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
28054 *
28055 * For details please refer to: http://www.datatables.net
28056 */
28057
28058var RowGroup = function ( dt, opts ) {
28059	// Sanity check that we are using DataTables 1.10 or newer
28060	if ( ! DataTable.versionCheck || ! DataTable.versionCheck( '1.10.8' ) ) {
28061		throw 'RowGroup requires DataTables 1.10.8 or newer';
28062	}
28063
28064	// User and defaults configuration object
28065	this.c = $.extend( true, {},
28066		DataTable.defaults.rowGroup,
28067		RowGroup.defaults,
28068		opts
28069	);
28070
28071	// Internal settings
28072	this.s = {
28073		dt: new DataTable.Api( dt )
28074	};
28075
28076	// DOM items
28077	this.dom = {
28078
28079	};
28080
28081	// Check if row grouping has already been initialised on this table
28082	var settings = this.s.dt.settings()[0];
28083	var existing = settings.rowGroup;
28084	if ( existing ) {
28085		return existing;
28086	}
28087
28088	settings.rowGroup = this;
28089	this._constructor();
28090};
28091
28092
28093$.extend( RowGroup.prototype, {
28094	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
28095	 * API methods for DataTables API interface
28096	 */
28097
28098	/**
28099	 * Get/set the grouping data source - need to call draw after this is
28100	 * executed as a setter
28101	 * @returns string~RowGroup
28102	 */
28103	dataSrc: function ( val )
28104	{
28105		if ( val === undefined ) {
28106			return this.c.dataSrc;
28107		}
28108
28109		var dt = this.s.dt;
28110
28111		this.c.dataSrc = val;
28112
28113		$(dt.table().node()).triggerHandler( 'rowgroup-datasrc.dt', [ dt, val ] );
28114
28115		return this;
28116	},
28117
28118	/**
28119	 * Disable - need to call draw after this is executed
28120	 * @returns RowGroup
28121	 */
28122	disable: function ()
28123	{
28124		this.c.enable = false;
28125		return this;
28126	},
28127
28128	/**
28129	 * Enable - need to call draw after this is executed
28130	 * @returns RowGroup
28131	 */
28132	enable: function ( flag )
28133	{
28134		if ( flag === false ) {
28135			return this.disable();
28136		}
28137
28138		this.c.enable = true;
28139		return this;
28140	},
28141
28142	/**
28143	 * Get enabled flag
28144	 * @returns boolean
28145	 */
28146	enabled: function ()
28147	{
28148		return this.c.enable;
28149	},
28150
28151
28152	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
28153	 * Constructor
28154	 */
28155	_constructor: function ()
28156	{
28157		var that = this;
28158		var dt = this.s.dt;
28159		var hostSettings = dt.settings()[0];
28160
28161		dt.on( 'draw.dtrg', function (e, s) {
28162			if ( that.c.enable && hostSettings === s ) {
28163				that._draw();
28164			}
28165		} );
28166
28167		dt.on( 'column-visibility.dt.dtrg responsive-resize.dt.dtrg', function () {
28168			that._adjustColspan();
28169		} );
28170
28171		dt.on( 'destroy', function () {
28172			dt.off( '.dtrg' );
28173		} );
28174	},
28175
28176
28177	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
28178	 * Private methods
28179	 */
28180
28181	/**
28182	 * Adjust column span when column visibility changes
28183	 * @private
28184	 */
28185	_adjustColspan: function ()
28186	{
28187		$( 'tr.'+this.c.className, this.s.dt.table().body() ).find('td:visible')
28188			.attr( 'colspan', this._colspan() );
28189	},
28190
28191	/**
28192	 * Get the number of columns that a grouping row should span
28193	 * @private
28194	 */
28195	_colspan: function ()
28196	{
28197		return this.s.dt.columns().visible().reduce( function (a, b) {
28198			return a + b;
28199		}, 0 );
28200	},
28201
28202
28203	/**
28204	 * Update function that is called whenever we need to draw the grouping rows.
28205	 * This is basically a bootstrap for the self iterative _group and _groupDisplay
28206	 * methods
28207	 * @private
28208	 */
28209	_draw: function ()
28210	{
28211		var dt = this.s.dt;
28212		var groupedRows = this._group( 0, dt.rows( { page: 'current' } ).indexes() );
28213
28214		this._groupDisplay( 0, groupedRows );
28215	},
28216
28217	/**
28218	 * Get the grouping information from a data set (index) of rows
28219	 * @param {number} level Nesting level
28220	 * @param {DataTables.Api} rows API of the rows to consider for this group
28221	 * @returns {object[]} Nested grouping information - it is structured like this:
28222	 *	{
28223	 *		dataPoint: 'Edinburgh',
28224	 *		rows: [ 1,2,3,4,5,6,7 ],
28225	 *		children: [ {
28226	 *			dataPoint: 'developer'
28227	 *			rows: [ 1, 2, 3 ]
28228	 *		},
28229	 *		{
28230	 *			dataPoint: 'support',
28231	 *			rows: [ 4, 5, 6, 7 ]
28232	 *		} ]
28233	 *	}
28234	 * @private
28235	 */
28236	_group: function ( level, rows ) {
28237		var fns = Array.isArray( this.c.dataSrc ) ? this.c.dataSrc : [ this.c.dataSrc ];
28238		var fn = DataTable.ext.oApi._fnGetObjectDataFn( fns[ level ] );
28239		var dt = this.s.dt;
28240		var group, last;
28241		var data = [];
28242		var that = this;
28243
28244		for ( var i=0, ien=rows.length ; i<ien ; i++ ) {
28245			var rowIndex = rows[i];
28246			var rowData = dt.row( rowIndex ).data();
28247			var group = fn( rowData );
28248
28249			if ( group === null || group === undefined ) {
28250				group = that.c.emptyDataGroup;
28251			}
28252			
28253			if ( last === undefined || group !== last ) {
28254				data.push( {
28255					dataPoint: group,
28256					rows: []
28257				} );
28258
28259				last = group;
28260			}
28261
28262			data[ data.length-1 ].rows.push( rowIndex );
28263		}
28264
28265		if ( fns[ level+1 ] !== undefined ) {
28266			for ( var i=0, ien=data.length ; i<ien ; i++ ) {
28267				data[i].children = this._group( level+1, data[i].rows );
28268			}
28269		}
28270
28271		return data;
28272	},
28273
28274	/**
28275	 * Row group display - insert the rows into the document
28276	 * @param {number} level Nesting level
28277	 * @param {object[]} groups Takes the nested array from `_group`
28278	 * @private
28279	 */
28280	_groupDisplay: function ( level, groups )
28281	{
28282		var dt = this.s.dt;
28283		var display;
28284	
28285		for ( var i=0, ien=groups.length ; i<ien ; i++ ) {
28286			var group = groups[i];
28287			var groupName = group.dataPoint;
28288			var row;
28289			var rows = group.rows;
28290
28291			if ( this.c.startRender ) {
28292				display = this.c.startRender.call( this, dt.rows(rows), groupName, level );
28293				row = this._rowWrap( display, this.c.startClassName, level );
28294
28295				if ( row ) {
28296					row.insertBefore( dt.row( rows[0] ).node() );
28297				}
28298			}
28299
28300			if ( this.c.endRender ) {
28301				display = this.c.endRender.call( this, dt.rows(rows), groupName, level );
28302				row = this._rowWrap( display, this.c.endClassName, level );
28303
28304				if ( row ) {
28305					row.insertAfter( dt.row( rows[ rows.length-1 ] ).node() );
28306				}
28307			}
28308
28309			if ( group.children ) {
28310				this._groupDisplay( level+1, group.children );
28311			}
28312		}
28313	},
28314
28315	/**
28316	 * Take a rendered value from an end user and make it suitable for display
28317	 * as a row, by wrapping it in a row, or detecting that it is a row.
28318	 * @param {node|jQuery|string} display Display value
28319	 * @param {string} className Class to add to the row
28320	 * @param {array} group
28321	 * @param {number} group level
28322	 * @private
28323	 */
28324	_rowWrap: function ( display, className, level )
28325	{
28326		var row;
28327		
28328		if ( display === null || display === '' ) {
28329			display = this.c.emptyDataGroup;
28330		}
28331
28332		if ( display === undefined || display === null ) {
28333			return null;
28334		}
28335		
28336		if ( typeof display === 'object' && display.nodeName && display.nodeName.toLowerCase() === 'tr') {
28337			row = $(display);
28338		}
28339		else if (display instanceof $ && display.length && display[0].nodeName.toLowerCase() === 'tr') {
28340			row = display;
28341		}
28342		else {
28343			row = $('<tr/>')
28344				.append(
28345					$('<th/>')
28346						.attr( 'colspan', this._colspan() )
28347						.attr( 'scope', 'row' )
28348						.append( display  )
28349				);
28350		}
28351
28352		return row
28353			.addClass( this.c.className )
28354			.addClass( className )
28355			.addClass( 'dtrg-level-'+level );
28356	}
28357} );
28358
28359
28360/**
28361 * RowGroup default settings for initialisation
28362 *
28363 * @namespace
28364 * @name RowGroup.defaults
28365 * @static
28366 */
28367RowGroup.defaults = {
28368	/**
28369	 * Class to apply to grouping rows - applied to both the start and
28370	 * end grouping rows.
28371	 * @type string
28372	 */
28373	className: 'dtrg-group',
28374
28375	/**
28376	 * Data property from which to read the grouping information
28377	 * @type string|integer|array
28378	 */
28379	dataSrc: 0,
28380
28381	/**
28382	 * Text to show if no data is found for a group
28383	 * @type string
28384	 */
28385	emptyDataGroup: 'No group',
28386
28387	/**
28388	 * Initial enablement state
28389	 * @boolean
28390	 */
28391	enable: true,
28392
28393	/**
28394	 * Class name to give to the end grouping row
28395	 * @type string
28396	 */
28397	endClassName: 'dtrg-end',
28398
28399	/**
28400	 * End grouping label function
28401	 * @function
28402	 */
28403	endRender: null,
28404
28405	/**
28406	 * Class name to give to the start grouping row
28407	 * @type string
28408	 */
28409	startClassName: 'dtrg-start',
28410
28411	/**
28412	 * Start grouping label function
28413	 * @function
28414	 */
28415	startRender: function ( rows, group ) {
28416		return group;
28417	}
28418};
28419
28420
28421RowGroup.version = "1.3.1";
28422
28423
28424$.fn.dataTable.RowGroup = RowGroup;
28425$.fn.DataTable.RowGroup = RowGroup;
28426
28427
28428DataTable.Api.register( 'rowGroup()', function () {
28429	return this;
28430} );
28431
28432DataTable.Api.register( 'rowGroup().disable()', function () {
28433	return this.iterator( 'table', function (ctx) {
28434		if ( ctx.rowGroup ) {
28435			ctx.rowGroup.enable( false );
28436		}
28437	} );
28438} );
28439
28440DataTable.Api.register( 'rowGroup().enable()', function ( opts ) {
28441	return this.iterator( 'table', function (ctx) {
28442		if ( ctx.rowGroup ) {
28443			ctx.rowGroup.enable( opts === undefined ? true : opts );
28444		}
28445	} );
28446} );
28447
28448DataTable.Api.register( 'rowGroup().enabled()', function () {
28449	var ctx = this.context;
28450
28451	return ctx.length && ctx[0].rowGroup ?
28452		ctx[0].rowGroup.enabled() :
28453		false;
28454} );
28455
28456DataTable.Api.register( 'rowGroup().dataSrc()', function ( val ) {
28457	if ( val === undefined ) {
28458		return this.context[0].rowGroup.dataSrc();
28459	}
28460
28461	return this.iterator( 'table', function (ctx) {
28462		if ( ctx.rowGroup ) {
28463			ctx.rowGroup.dataSrc( val );
28464		}
28465	} );
28466} );
28467
28468
28469// Attach a listener to the document which listens for DataTables initialisation
28470// events so we can automatically initialise
28471$(document).on( 'preInit.dt.dtrg', function (e, settings, json) {
28472	if ( e.namespace !== 'dt' ) {
28473		return;
28474	}
28475
28476	var init = settings.oInit.rowGroup;
28477	var defaults = DataTable.defaults.rowGroup;
28478
28479	if ( init || defaults ) {
28480		var opts = $.extend( {}, defaults, init );
28481
28482		if ( init !== false ) {
28483			new RowGroup( settings, opts  );
28484		}
28485	}
28486} );
28487
28488
28489return DataTable;
28490}));
28491
28492
28493/*! RowReorder 1.3.3
28494 * © SpryMedia Ltd - datatables.net/license
28495 */
28496
28497(function( factory ){
28498	if ( typeof define === 'function' && define.amd ) {
28499		// AMD
28500		define( ['jquery', 'datatables.net'], function ( $ ) {
28501			return factory( $, window, document );
28502		} );
28503	}
28504	else if ( typeof exports === 'object' ) {
28505		// CommonJS
28506		var jq = require('jquery');
28507		var cjsRequires = function (root, $) {
28508			if ( ! $.fn.dataTable ) {
28509				require('datatables.net')(root, $);
28510			}
28511		};
28512
28513		if (typeof window !== 'undefined') {
28514			module.exports = function (root, $) {
28515				if ( ! root ) {
28516					// CommonJS environments without a window global must pass a
28517					// root. This will give an error otherwise
28518					root = window;
28519				}
28520
28521				if ( ! $ ) {
28522					$ = jq( root );
28523				}
28524
28525				cjsRequires( root, $ );
28526				return factory( $, root, root.document );
28527			};
28528		}
28529		else {
28530			cjsRequires( window, jq );
28531			module.exports = factory( jq, window, window.document );
28532		}
28533	}
28534	else {
28535		// Browser
28536		factory( jQuery, window, document );
28537	}
28538}(function( $, window, document, undefined ) {
28539'use strict';
28540var DataTable = $.fn.dataTable;
28541
28542
28543
28544/**
28545 * @summary     RowReorder
28546 * @description Row reordering extension for DataTables
28547 * @version     1.3.3
28548 * @file        dataTables.rowReorder.js
28549 * @author      SpryMedia Ltd
28550 * @contact     datatables.net
28551 * @copyright   Copyright 2015-2023 SpryMedia Ltd.
28552 *
28553 * This source file is free software, available under the following license:
28554 *   MIT license - http://datatables.net/license/mit
28555 *
28556 * This source file is distributed in the hope that it will be useful, but
28557 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
28558 * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
28559 *
28560 * For details please refer to: http://www.datatables.net
28561 */
28562
28563/**
28564 * RowReorder provides the ability in DataTables to click and drag rows to
28565 * reorder them. When a row is dropped the data for the rows effected will be
28566 * updated to reflect the change. Normally this data point should also be the
28567 * column being sorted upon in the DataTable but this does not need to be the
28568 * case. RowReorder implements a "data swap" method - so the rows being
28569 * reordered take the value of the data point from the row that used to occupy
28570 * the row's new position.
28571 *
28572 * Initialisation is done by either:
28573 *
28574 * * `rowReorder` parameter in the DataTable initialisation object
28575 * * `new $.fn.dataTable.RowReorder( table, opts )` after DataTables
28576 *   initialisation.
28577 * 
28578 *  @class
28579 *  @param {object} settings DataTables settings object for the host table
28580 *  @param {object} [opts] Configuration options
28581 *  @requires jQuery 1.7+
28582 *  @requires DataTables 1.10.7+
28583 */
28584var RowReorder = function ( dt, opts ) {
28585	// Sanity check that we are using DataTables 1.10 or newer
28586	if ( ! DataTable.versionCheck || ! DataTable.versionCheck( '1.10.8' ) ) {
28587		throw 'DataTables RowReorder requires DataTables 1.10.8 or newer';
28588	}
28589
28590	// User and defaults configuration object
28591	this.c = $.extend( true, {},
28592		DataTable.defaults.rowReorder,
28593		RowReorder.defaults,
28594		opts
28595	);
28596
28597	// Internal settings
28598	this.s = {
28599		/** @type {integer}
28599 Scroll body top cache */
28600		bodyTop: null,
28601
28602		/** @type {DataTable.Api} DataTables' API instance */
28603		dt: new DataTable.Api( dt ),
28604
28605		/** @type {function} Data fetch function */
28606		getDataFn: DataTable.ext.oApi._fnGetObjectDataFn( this.c.dataSrc ),
28607
28608		/** @type {array} Pixel positions for row insertion calculation */
28609		middles: null,
28610
28611		/** @type {Object} Cached dimension information for use in the mouse move event handler */
28612		scroll: {},
28613
28614		/** @type {integer} Interval object used for smooth scrolling */
28615		scrollInterval: null,
28616
28617		/** @type {function} Data set function */
28618		setDataFn: DataTable.ext.oApi._fnSetObjectDataFn( this.c.dataSrc ),
28619
28620		/** @type {Object} Mouse down information */
28621		start: {
28622			top: 0,
28623			left: 0,
28624			offsetTop: 0,
28625			offsetLeft: 0,
28626			nodes: []
28627		},
28628
28629		/** @type {integer} Window height cached value */
28630		windowHeight: 0,
28631
28632		/** @type {integer} Document outer height cached value */
28633		documentOuterHeight: 0,
28634
28635		/** @type {integer} DOM clone outer height cached value */
28636		domCloneOuterHeight: 0
28637	};
28638
28639	// DOM items
28640	this.dom = {
28641		/** @type {jQuery} Cloned row being moved around */
28642		clone: null,
28643		cloneParent: null,
28644
28645		/** @type {jQuery} DataTables scrolling container */
28646		dtScroll: $('div.dataTables_scrollBody', this.s.dt.table().container())
28647	};
28648
28649	// Check if row reorder has already been initialised on this table
28650	var settings = this.s.dt.settings()[0];
28651	var exisiting = settings.rowreorder;
28652
28653	if ( exisiting ) {
28654		return exisiting;
28655	}
28656
28657	if ( !this.dom.dtScroll.length ) {
28658		this.dom.dtScroll = $(this.s.dt.table().container(), 'tbody')
28659	}
28660
28661	settings.rowreorder = this;
28662	this._constructor();
28663};
28664
28665
28666$.extend( RowReorder.prototype, {
28667	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
28668	 * Constructor
28669	 */
28670
28671	/**
28672	 * Initialise the RowReorder instance
28673	 *
28674	 * @private
28675	 */
28676	_constructor: function ()
28677	{
28678		var that = this;
28679		var dt = this.s.dt;
28680		var table = $( dt.table().node() );
28681
28682		// Need to be able to calculate the row positions relative to the table
28683		if ( table.css('position') === 'static' ) {
28684			table.css( 'position', 'relative' );
28685		}
28686
28687		// listen for mouse down on the target column - we have to implement
28688		// this rather than using HTML5 drag and drop as drag and drop doesn't
28689		// appear to work on table rows at this time. Also mobile browsers are
28690		// not supported.
28691		// Use `table().container()` rather than just the table node for IE8 -
28692		// otherwise it only works once...
28693		$(dt.table().container()).on( 'mousedown.rowReorder touchstart.rowReorder', this.c.selector, function (e) {
28694			if ( ! that.c.enable ) {
28695				return;
28696			}
28697
28698			// Ignore excluded children of the selector
28699			if ( $(e.target).is(that.c.excludedChildren) ) {
28700				return true;
28701			}
28702
28703			var tr = $(this).closest('tr');
28704			var row = dt.row( tr );
28705
28706			// Double check that it is a DataTable row
28707			if ( row.any() ) {
28708				that._emitEvent( 'pre-row-reorder', {
28709					node: row.node(),
28710					index: row.index()
28711				} );
28712
28713				that._mouseDown( e, tr );
28714				return false;
28715			}
28716		} );
28717
28718		dt.on( 'destroy.rowReorder', function () {
28719			$(dt.table().container()).off( '.rowReorder' );
28720			dt.off( '.rowReorder' );
28721		} );
28722	},
28723
28724
28725	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
28726	 * Private methods
28727	 */
28728	
28729	/**
28730	 * Cache the measurements that RowReorder needs in the mouse move handler
28731	 * to attempt to speed things up, rather than reading from the DOM.
28732	 *
28733	 * @private
28734	 */
28735	_cachePositions: function ()
28736	{
28737		var dt = this.s.dt;
28738
28739		// Frustratingly, if we add `position:relative` to the tbody, the
28740		// position is still relatively to the parent. So we need to adjust
28741		// for that
28742		var headerHeight = $( dt.table().node() ).find('thead').outerHeight();
28743
28744		// Need to pass the nodes through jQuery to get them in document order,
28745		// not what DataTables thinks it is, since we have been altering the
28746		// order
28747		var nodes = $.unique( dt.rows( { page: 'current' } ).nodes().toArray() );
28748		var middles = $.map( nodes, function ( node, i ) {
28749			var top = $(node).position().top - headerHeight;
28750
28751			return (top + top + $(node).outerHeight() ) / 2;
28752		} );
28753
28754		this.s.middles = middles;
28755		this.s.bodyTop = $( dt.table().body() ).offset().top;
28756		this.s.windowHeight = $(window).height();
28757		this.s.documentOuterHeight = $(document).outerHeight();
28758	},
28759
28760
28761	/**
28762	 * Clone a row so it can be floated around the screen
28763	 *
28764	 * @param  {jQuery} target Node to be cloned
28765	 * @private
28766	 */
28767	_clone: function ( target )
28768	{
28769		var dt = this.s.dt;
28770		var clone = $( dt.table().node().cloneNode(false) )
28771			.addClass( 'dt-rowReorder-float' )
28772			.append('<tbody/>')
28773			.append( target.clone( false ) );
28774
28775		// Match the table and column widths - read all sizes before setting
28776		// to reduce reflows
28777		var tableWidth = target.outerWidth();
28778		var tableHeight = target.outerHeight();
28779		var scrollBody = $($(this.s.dt.table().node()).parent());
28780		var scrollWidth = scrollBody.width();
28781		var scrollLeft = scrollBody.scrollLeft();
28782		var sizes = target.children().map( function () {
28783			return $(this).width();
28784		} );
28785
28786		clone
28787			.width( tableWidth )
28788			.height( tableHeight )
28789			.find('tr').children().each( function (i) {
28790				this.style.width = sizes[i]+'px';
28791			} );
28792
28793		var cloneParent = $('<div>')
28794			.addClass('dt-rowReorder-float-parent')
28795			.width(scrollWidth)
28796			.append(clone)
28797			.appendTo( 'body' )
28798			.scrollLeft(scrollLeft);
28799
28800		// Insert into the document to have it floating around
28801
28802		this.dom.clone = clone;
28803		this.dom.cloneParent = cloneParent;
28804		this.s.domCloneOuterHeight = clone.outerHeight();
28805	},
28806
28807
28808	/**
28809	 * Update the cloned item's position in the document
28810	 *
28811	 * @param  {object} e Event giving the mouse's position
28812	 * @private
28813	 */
28814	_clonePosition: function ( e )
28815	{
28816		var start = this.s.start;
28817		var topDiff = this._eventToPage( e, 'Y' ) - start.top;
28818		var leftDiff = this._eventToPage( e, 'X' ) - start.left;
28819		var snap = this.c.snapX;
28820		var left;
28821		var top = topDiff + start.offsetTop;
28822
28823		if ( snap === true ) {
28824			left = start.offsetLeft;
28825		}
28826		else if ( typeof snap === 'number' ) {
28827			left = start.offsetLeft + snap;
28828		}
28829		else {
28830			left = leftDiff + start.offsetLeft + this.dom.cloneParent.scrollLeft();
28831		}
28832
28833		if(top < 0) {
28834			top = 0
28835		}
28836		else if(top + this.s.domCloneOuterHeight > this.s.documentOuterHeight) {
28837			top = this.s.documentOuterHeight - this.s.domCloneOuterHeight;
28838		}
28839
28840		this.dom.cloneParent.css( {
28841			top: top,
28842			left: left
28843		} );
28844	},
28845
28846
28847	/**
28848	 * Emit an event on the DataTable for listeners
28849	 *
28850	 * @param  {string} name Event name
28851	 * @param  {array} args Event arguments
28852	 * @private
28853	 */
28854	_emitEvent: function ( name, args )
28855	{
28856		this.s.dt.iterator( 'table', function ( ctx, i ) {
28857			$(ctx.nTable).triggerHandler( name+'.dt', args );
28858		} );
28859	},
28860
28861
28862	/**
28863	 * Get pageX/Y position from an event, regardless of if it is a mouse or
28864	 * touch event.
28865	 *
28866	 * @param  {object} e Event
28867	 * @param  {string} pos X or Y (must be a capital)
28868	 * @private
28869	 */
28870	_eventToPage: function ( e, pos )
28871	{
28872		if ( e.type.indexOf( 'touch' ) !== -1 ) {
28873			return e.originalEvent.touches[0][ 'page'+pos ];
28874		}
28875
28876		return e[ 'page'+pos ];
28877	},
28878
28879
28880	/**
28881	 * Mouse down event handler. Read initial positions and add event handlers
28882	 * for the move.
28883	 *
28884	 * @param  {object} e      Mouse event
28885	 * @param  {jQuery} target TR element that is to be moved
28886	 * @private
28887	 */
28888	_mouseDown: function ( e, target )
28889	{
28890		var that = this;
28891		var dt = this.s.dt;
28892		var start = this.s.start;
28893
28894		var offset = target.offset();
28895		start.top = this._eventToPage( e, 'Y' );
28896		start.left = this._eventToPage( e, 'X' );
28897		start.offsetTop = offset.top;
28898		start.offsetLeft = offset.left;
28899		start.nodes = $.unique( dt.rows( { page: 'current' } ).nodes().toArray() );
28900
28901		this._cachePositions();
28902		this._clone( target );
28903		this._clonePosition( e );
28904
28905		this.dom.target = target;
28906		target.addClass( 'dt-rowReorder-moving' );
28907
28908		$( document )
28909			.on( 'mouseup.rowReorder touchend.rowReorder', function (e) {
28910				that._mouseUp(e);
28911			} )
28912			.on( 'mousemove.rowReorder touchmove.rowReorder', function (e) {
28913				that._mouseMove(e);
28914			} );
28915
28916		// Check if window is x-scrolling - if not, disable it for the duration
28917		// of the drag
28918		if ( $(window).width() === $(document).width() ) {
28919			$(document.body).addClass( 'dt-rowReorder-noOverflow' );
28920		}
28921
28922		// Cache scrolling information so mouse move doesn't need to read.
28923		// This assumes that the window and DT scroller will not change size
28924		// during an row drag, which I think is a fair assumption
28925		var scrollWrapper = this.dom.dtScroll;
28926		this.s.scroll = {
28927			windowHeight: $(window).height(),
28928			windowWidth:  $(window).width(),
28929			dtTop:        scrollWrapper.length ? scrollWrapper.offset().top : null,
28930			dtLeft:       scrollWrapper.length ? scrollWrapper.offset().left : null,
28931			dtHeight:     scrollWrapper.length ? scrollWrapper.outerHeight() : null,
28932			dtWidth:      scrollWrapper.length ? scrollWrapper.outerWidth() : null
28933		};
28934	},
28935
28936
28937	/**
28938	 * Mouse move event handler - move the cloned row and shuffle the table's
28939	 * rows if required.
28940	 *
28941	 * @param  {object} e Mouse event
28942	 * @private
28943	 */
28944	_mouseMove: function ( e )
28945	{
28946		this._clonePosition( e );
28947
28948		// Transform the mouse position into a position in the table's body
28949		var bodyY = this._eventToPage( e, 'Y' ) - this.s.bodyTop;
28950		var middles = this.s.middles;
28951		var insertPoint = null;
28952		var dt = this.s.dt;
28953
28954		// Determine where the row should be inserted based on the mouse
28955		// position
28956		for ( var i=0, ien=middles.length ; i<ien ; i++ ) {
28957			if ( bodyY < middles[i] ) {
28958				insertPoint = i;
28959				break;
28960			}
28961		}
28962
28963		if ( insertPoint === null ) {
28964			insertPoint = middles.length;
28965		}
28966
28967		// Perform the DOM shuffle if it has changed from last time
28968		if ( this.s.lastInsert === null || this.s.lastInsert !== insertPoint ) {
28969			var nodes = $.unique( dt.rows( { page: 'current' } ).nodes().toArray() );
28970
28971			if ( insertPoint > this.s.lastInsert ) {
28972				this.dom.target.insertAfter( nodes[ insertPoint-1 ] );
28973			}
28974			else {
28975				this.dom.target.insertBefore( nodes[ insertPoint ] );
28976			}
28977
28978			this._cachePositions();
28979
28980			this.s.lastInsert = insertPoint;
28981		}
28982
28983		this._shiftScroll( e );
28984	},
28985
28986
28987	/**
28988	 * Mouse up event handler - release the event handlers and perform the
28989	 * table updates
28990	 *
28991	 * @param  {object} e Mouse event
28992	 * @private
28993	 */
28994	_mouseUp: function ( e )
28995	{
28996		var that = this;
28997		var dt = this.s.dt;
28998		var i, ien;
28999		var dataSrc = this.c.dataSrc;
29000
29001		this.dom.clone.remove();
29002		this.dom.cloneParent.remove();
29003		this.dom.clone = null;
29004		this.dom.cloneParent = null;
29005
29006		this.dom.target.removeClass( 'dt-rowReorder-moving' );
29007		//this.dom.target = null;
29008
29009		$(document).off( '.rowReorder' );
29010		$(document.body).removeClass( 'dt-rowReorder-noOverflow' );
29011
29012		clearInterval( this.s.scrollInterval );
29013		this.s.scrollInterval = null;
29014
29015		// Calculate the difference
29016		var startNodes = this.s.start.nodes;
29017		var endNodes = $.unique( dt.rows( { page: 'current' } ).nodes().toArray() );
29018		var idDiff = {};
29019		var fullDiff = [];
29020		var diffNodes = [];
29021		var getDataFn = this.s.getDataFn;
29022		var setDataFn = this.s.setDataFn;
29023
29024		for ( i=0, ien=startNodes.length ; i<ien ; i++ ) {
29025			if ( startNodes[i] !== endNodes[i] ) {
29026				var id = dt.row( endNodes[i] ).id();
29027				var endRowData = dt.row( endNodes[i] ).data();
29028				var startRowData = dt.row( startNodes[i] ).data();
29029
29030				if ( id ) {
29031					idDiff[ id ] = getDataFn( startRowData );
29032				}
29033
29034				fullDiff.push( {
29035					node: endNodes[i],
29036					oldData: getDataFn( endRowData ),
29037					newData: getDataFn( startRowData ),
29038					newPosition: i,
29039					oldPosition: $.inArray( endNodes[i], startNodes )
29040				} );
29041
29042				diffNodes.push( endNodes[i] );
29043			}
29044		}
29045		
29046		// Create event args
29047		var eventArgs = [ fullDiff, {
29048			dataSrc:       dataSrc,
29049			nodes:         diffNodes,
29050			values:        idDiff,
29051			triggerRow:    dt.row( this.dom.target ),
29052			originalEvent: e
29053		} ];
29054		
29055		// Emit event
29056		this._emitEvent( 'row-reorder', eventArgs );
29057
29058		var update = function () {
29059			if ( that.c.update ) {
29060				for ( i=0, ien=fullDiff.length ; i<ien ; i++ ) {
29061					var row = dt.row( fullDiff[i].node );
29062					var rowData = row.data();
29063
29064					setDataFn( rowData, fullDiff[i].newData );
29065
29066					// Invalidate the cell that has the same data source as the dataSrc
29067					dt.columns().every( function () {
29068						if ( this.dataSrc() === dataSrc ) {
29069							dt.cell( fullDiff[i].node, this.index() ).invalidate( 'data' );
29070						}
29071					} );
29072				}
29073
29074				// Trigger row reordered event
29075				that._emitEvent( 'row-reordered', eventArgs );
29076
29077				dt.draw( false );
29078			}
29079		};
29080
29081		// Editor interface
29082		if ( this.c.editor ) {
29083			// Disable user interaction while Editor is submitting
29084			this.c.enable = false;
29085
29086			this.c.editor
29087				.edit(
29088					diffNodes,
29089					false,
29090					$.extend( {submit: 'changed'}, this.c.formOptions )
29091				)
29092				.multiSet( dataSrc, idDiff )
29093				.one( 'preSubmitCancelled.rowReorder', function () {
29094					that.c.enable = true;
29095					that.c.editor.off( '.rowReorder' );
29096					dt.draw( false );
29097				} )
29098				.one( 'submitUnsuccessful.rowReorder', function () {
29099					dt.draw( false );
29100				} )
29101				.one( 'submitSuccess.rowReorder', function () {
29102					update();
29103				} )
29104				.one( 'submitComplete', function () {
29105					that.c.enable = true;
29106					that.c.editor.off( '.rowReorder' );
29107				} )
29108				.submit();
29109		}
29110		else {
29111			update();
29112		}
29113	},
29114
29115
29116	/**
29117	 * Move the window and DataTables scrolling during a drag to scroll new
29118	 * content into view.
29119	 *
29120	 * This matches the `_shiftScroll` method used in AutoFill, but only
29121	 * horizontal scrolling is considered here.
29122	 *
29123	 * @param  {object} e Mouse move event object
29124	 * @private
29125	 */
29126	_shiftScroll: function ( e )
29127	{
29128		var that = this;
29129		var dt = this.s.dt;
29130		var scroll = this.s.scroll;
29131		var runInterval = false;
29132		var scrollSpeed = 5;
29133		var buffer = 65;
29134		var
29135			windowY = e.pageY - document.body.scrollTop,
29136			windowVert,
29137			dtVert;
29138
29139		// Window calculations - based on the mouse position in the window,
29140		// regardless of scrolling
29141		if ( windowY < $(window).scrollTop() + buffer ) {
29142			windowVert = scrollSpeed * -1;
29143		}
29144		else if ( windowY > scroll.windowHeight + $(window).scrollTop() - buffer ) {
29145			windowVert = scrollSpeed;
29146		}
29147
29148		// DataTables scrolling calculations - based on the table's position in
29149		// the document and the mouse position on the page
29150		if ( scroll.dtTop !== null && e.pageY < scroll.dtTop + buffer ) {
29151			dtVert = scrollSpeed * -1;
29152		}
29153		else if ( scroll.dtTop !== null && e.pageY > scroll.dtTop + scroll.dtHeight - buffer ) {
29154			dtVert = scrollSpeed;
29155		}
29156
29157		// This is where it gets interesting. We want to continue scrolling
29158		// without requiring a mouse move, so we need an interval to be
29159		// triggered. The interval should continue until it is no longer needed,
29160		// but it must also use the latest scroll commands (for example consider
29161		// that the mouse might move from scrolling up to scrolling left, all
29162		// with the same interval running. We use the `scroll` object to "pass"
29163		// this information to the interval. Can't use local variables as they
29164		// wouldn't be the ones that are used by an already existing interval!
29165		if ( windowVert || dtVert ) {
29166			scroll.windowVert = windowVert;
29167			scroll.dtVert = dtVert;
29168			runInterval = true;
29169		}
29170		else if ( this.s.scrollInterval ) {
29171			// Don't need to scroll - remove any existing timer
29172			clearInterval( this.s.scrollInterval );
29173			this.s.scrollInterval = null;
29174		}
29175
29176		// If we need to run the interval to scroll and there is no existing
29177		// interval (if there is an existing one, it will continue to run)
29178		if ( ! this.s.scrollInterval && runInterval ) {
29179			this.s.scrollInterval = setInterval( function () {
29180				// Don't need to worry about setting scroll <0 or beyond the
29181				// scroll bound as the browser will just reject that.
29182				if ( scroll.windowVert ) {
29183					var top = $(document).scrollTop();
29184					$(document).scrollTop(top + scroll.windowVert);
29185
29186					if ( top !== $(document).scrollTop() ) {
29187						var move = parseFloat(that.dom.cloneParent.css("top"));
29188						that.dom.cloneParent.css("top", move + scroll.windowVert);					
29189					}
29190				}
29191
29192				// DataTables scrolling
29193				if ( scroll.dtVert ) {
29194					var scroller = that.dom.dtScroll[0];
29195
29196					if ( scroll.dtVert ) {
29197						scroller.scrollTop += scroll.dtVert;
29198					}
29199				}
29200			}, 20 );
29201		}
29202	}
29203} );
29204
29205
29206
29207/**
29208 * RowReorder default settings for initialisation
29209 *
29210 * @namespace
29211 * @name RowReorder.defaults
29212 * @static
29213 */
29214RowReorder.defaults = {
29215	/**
29216	 * Data point in the host row's data source object for where to get and set
29217	 * the data to reorder. This will normally also be the sorting column.
29218	 *
29219	 * @type {Number}
29220	 */
29221	dataSrc: 0,
29222
29223	/**
29224	 * Editor instance that will be used to perform the update
29225	 *
29226	 * @type {DataTable.Editor}
29227	 */
29228	editor: null,
29229
29230	/**
29231	 * Enable / disable RowReorder's user interaction
29232	 * @type {Boolean}
29233	 */
29234	enable: true,
29235
29236	/**
29237	 * Form options to pass to Editor when submitting a change in the row order.
29238	 * See the Editor `from-options` object for details of the options
29239	 * available.
29240	 * @type {Object}
29241	 */
29242	formOptions: {},
29243
29244	/**
29245	 * Drag handle selector. This defines the element that when dragged will
29246	 * reorder a row.
29247	 *
29248	 * @type {String}
29249	 */
29250	selector: 'td:first-child',
29251
29252	/**
29253	 * Optionally lock the dragged row's x-position. This can be `true` to
29254	 * fix the position match the host table's, `false` to allow free movement
29255	 * of the row, or a number to define an offset from the host table.
29256	 *
29257	 * @type {Boolean|number}
29258	 */
29259	snapX: false,
29260
29261	/**
29262	 * Update the table's data on drop
29263	 *
29264	 * @type {Boolean}
29265	 */
29266	update: true,
29267
29268	/**
29269	 * Selector for children of the drag handle selector that mouseDown events
29270	 * will be passed through to and drag will not activate
29271	 *
29272	 * @type {String}
29273	 */
29274	excludedChildren: 'a'
29275};
29276
29277
29278/*
29279 * API
29280 */
29281var Api = $.fn.dataTable.Api;
29282
29283// Doesn't do anything - work around for a bug in DT... Not documented
29284Api.register( 'rowReorder()', function () {
29285	return this;
29286} );
29287
29288Api.register( 'rowReorder.enable()', function ( toggle ) {
29289	if ( toggle === undefined ) {
29290		toggle = true;
29291	}
29292
29293	return this.iterator( 'table', function ( ctx ) {
29294		if ( ctx.rowreorder ) {
29295			ctx.rowreorder.c.enable = toggle;
29296		}
29297	} );
29298} );
29299
29300Api.register( 'rowReorder.disable()', function () {
29301	return this.iterator( 'table', function ( ctx ) {
29302		if ( ctx.rowreorder ) {
29303			ctx.rowreorder.c.enable = false;
29304		}
29305	} );
29306} );
29307
29308
29309/**
29310 * Version information
29311 *
29312 * @name RowReorder.version
29313 * @static
29314 */
29315RowReorder.version = '1.3.3';
29316
29317
29318$.fn.dataTable.RowReorder = RowReorder;
29319$.fn.DataTable.RowReorder = RowReorder;
29320
29321// Attach a listener to the document which listens for DataTables initialisation
29322// events so we can automatically initialise
29323$(document).on( 'init.dt.dtr', function (e, settings, json) {
29324	if ( e.namespace !== 'dt' ) {
29325		return;
29326	}
29327
29328	var init = settings.oInit.rowReorder;
29329	var defaults = DataTable.defaults.rowReorder;
29330
29331	if ( init || defaults ) {
29332		var opts = $.extend( {}, init, defaults );
29333
29334		if ( init !== false ) {
29335			new RowReorder( settings, opts  );
29336		}
29337	}
29338} );
29339
29340
29341return DataTable;
29342}));
29343
29344
29345/*! Scroller 2.1.1
29346 * © SpryMedia Ltd - datatables.net/license
29347 */
29348
29349(function( factory ){
29350	if ( typeof define === 'function' && define.amd ) {
29351		// AMD
29352		define( ['jquery', 'datatables.net'], function ( $ ) {
29353			return factory( $, window, document );
29354		} );
29355	}
29356	else if ( typeof exports === 'object' ) {
29357		// CommonJS
29358		var jq = require('jquery');
29359		var cjsRequires = function (root, $) {
29360			if ( ! $.fn.dataTable ) {
29361				require('datatables.net')(root, $);
29362			}
29363		};
29364
29365		if (typeof window !== 'undefined') {
29366			module.exports = function (root, $) {
29367				if ( ! root ) {
29368					// CommonJS environments without a window global must pass a
29369					// root. This will give an error otherwise
29370					root = window;
29371				}
29372
29373				if ( ! $ ) {
29374					$ = jq( root );
29375				}
29376
29377				cjsRequires( root, $ );
29378				return factory( $, root, root.document );
29379			};
29380		}
29381		else {
29382			cjsRequires( window, jq );
29383			module.exports = factory( jq, window, window.document );
29384		}
29385	}
29386	else {
29387		// Browser
29388		factory( jQuery, window, document );
29389	}
29390}(function( $, window, document, undefined ) {
29391'use strict';
29392var DataTable = $.fn.dataTable;
29393
29394
29395
29396/**
29397 * @summary     Scroller
29398 * @description Virtual rendering for DataTables
29399 * @version     2.1.1
29400 * @author      SpryMedia Ltd (www.sprymedia.co.uk)
29401 * @contact     www.sprymedia.co.uk/contact
29402 * @copyright   SpryMedia Ltd.
29403 *
29404 * This source file is free software, available under the following license:
29405 *   MIT license - http://datatables.net/license/mit
29406 *
29407 * This source file is distributed in the hope that it will be useful, but
29408 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
29409 * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
29410 *
29411 * For details please refer to: http://www.datatables.net
29412 */
29413
29414
29415/**
29416 * Scroller is a virtual rendering plug-in for DataTables which allows large
29417 * datasets to be drawn on screen every quickly. What the virtual rendering means
29418 * is that only the visible portion of the table (and a bit to either side to make
29419 * the scrolling smooth) is drawn, while the scrolling container gives the
29420 * visual impression that the whole table is visible. This is done by making use
29421 * of the pagination abilities of DataTables and moving the table around in the
29422 * scrolling container DataTables adds to the page. The scrolling container is
29423 * forced to the height it would be for the full table display using an extra
29424 * element.
29425 *
29426 * Note that rows in the table MUST all be the same height. Information in a cell
29427 * which expands on to multiple lines will cause some odd behaviour in the scrolling.
29428 *
29429 * Scroller is initialised by simply including the letter 'S' in the sDom for the
29430 * table you want to have this feature enabled on. Note that the 'S' must come
29431 * AFTER the 't' parameter in `dom`.
29432 *
29433 * Key features include:
29434 *   <ul class="limit_length">
29435 *     <li>Speed! The aim of Scroller for DataTables is to make rendering large data sets fast</li>
29436 *     <li>Full compatibility with deferred rendering in DataTables for maximum speed</li>
29437 *     <li>Display millions of rows</li>
29438 *     <li>Integration with state saving in DataTables (scrolling position is saved)</li>
29439 *     <li>Easy to use</li>
29440 *   </ul>
29441 *
29442 *  @class
29443 *  @constructor
29444 *  @global
29445 *  @param {object} dt DataTables settings object or API instance
29446 *  @param {object} [opts={}] Configuration object for Scroller. Options 
29447 *    are defined by {@link Scroller.defaults}
29448 *
29449 *  @requires jQuery 1.7+
29450 *  @requires DataTables 1.10.0+
29451 *
29452 *  @example
29453 *    $(document).ready(function() {
29454 *        $('#example').DataTable( {
29455 *            "scrollY": "200px",
29456 *            "ajax": "media/dataset/large.txt",
29457 *            "scroller": true,
29458 *            "deferRender": true
29459 *        } );
29460 *    } );
29461 */
29462var Scroller = function ( dt, opts ) {
29463	/* Sanity check - you just know it will happen */
29464	if ( ! (this instanceof Scroller) ) {
29465		alert( "Scroller warning: Scroller must be initialised with the 'new' keyword." );
29466		return;
29467	}
29468
29469	if ( opts === undefined ) {
29470		opts = {};
29471	}
29472
29473	var dtApi = $.fn.dataTable.Api( dt );
29474
29475	/**
29476	 * Settings object which contains customisable information for the Scroller instance
29477	 * @namespace
29478	 * @private
29479	 * @extends Scroller.defaults
29480	 */
29481	this.s = {
29482		/**
29483		 * DataTables settings object
29484		 *  @type     object
29485		 *  @default  Passed in as first parameter to constructor
29486		 */
29487		dt: dtApi.settings()[0],
29488
29489		/**
29490		 * DataTables API instance
29491		 *  @type     DataTable.Api
29492		 */
29493		dtApi: dtApi,
29494
29495		/**
29496		 * Pixel location of the top of the drawn table in the viewport
29497		 *  @type     int
29498		 *  @default  0
29499		 */
29500		tableTop: 0,
29501
29502		/**
29503		 * Pixel location of the bottom of the drawn table in the viewport
29504		 *  @type     int
29505		 *  @default  0
29506		 */
29507		tableBottom: 0,
29508
29509		/**
29510		 * Pixel location of the boundary for when the next data set should be loaded and drawn
29511		 * when scrolling up the way.
29512		 *  @type     int
29513		 *  @default  0
29514		 *  @private
29515		 */
29516		redrawTop: 0,
29517
29518		/**
29519		 * Pixel location of the boundary for when the next data set should be loaded and drawn
29520		 * when scrolling down the way. Note that this is actually calculated as the offset from
29521		 * the top.
29522		 *  @type     int
29523		 *  @default  0
29524		 *  @private
29525		 */
29526		redrawBottom: 0,
29527
29528		/**
29529		 * Auto row height or not indicator
29530		 *  @type     bool
29531		 *  @default  0
29532		 */
29533		autoHeight: true,
29534
29535		/**
29536		 * Number of rows calculated as visible in the visible viewport
29537		 *  @type     int
29538		 *  @default  0
29539		 */
29540		viewportRows: 0,
29541
29542		/**
29543		 * setTimeout reference for state saving, used when state saving is enabled in the DataTable
29544		 * and when the user scrolls the viewport in order to stop the cookie set taking too much
29545		 * CPU!
29546		 *  @type     int
29547		 *  @default  0
29548		 */
29549		stateTO: null,
29550
29551		stateSaveThrottle: function () {},
29552
29553		/**
29554		 * setTimeout reference for the redraw, used when server-side processing is enabled in the
29555		 * DataTables in order to prevent DoSing the server
29556		 *  @type     int
29557		 *  @default  null
29558		 */
29559		drawTO: null,
29560
29561		heights: {
29562			jump: null,
29563			page: null,
29564			virtual: null,
29565			scroll: null,
29566
29567			/**
29568			 * Height of rows in the table
29569			 *  @type     int
29570			 *  @default  0
29571			 */
29572			row: null,
29573
29574			/**
29575			 * Pixel height of the viewport
29576			 *  @type     int
29577			 *  @default  0
29578			 */
29579			viewport: null,
29580			labelHeight: 0,
29581			xbar: 0
29582		},
29583
29584		topRowFloat: 0,
29585		scrollDrawDiff: null,
29586		loaderVisible: false,
29587		forceReposition: false,
29588		baseRowTop: 0,
29589		baseScrollTop: 0,
29590		mousedown: false,
29591		lastScrollTop: 0
29592	};
29593
29594	// @todo The defaults should extend a `c` property and the internal settings
29595	// only held in the `s` property. At the moment they are mixed
29596	this.s = $.extend( this.s, Scroller.oDefaults, opts );
29597
29598	// Workaround for row height being read from height object (see above comment)
29599	this.s.heights.row = this.s.rowHeight;
29600
29601	/**
29602	 * DOM elements used by the class instance
29603	 * @private
29604	 * @namespace
29605	 *
29606	 */
29607	this.dom = {
29608		"force":    document.createElement('div'),
29609		"label":    $('<div class="dts_label">0</div>'),
29610		"scroller": null,
29611		"table":    null,
29612		"loader":   null
29613	};
29614
29615	// Attach the instance to the DataTables instance so it can be accessed in
29616	// future. Don't initialise Scroller twice on the same table
29617	if ( this.s.dt.oScroller ) {
29618		return;
29619	}
29620
29621	this.s.dt.oScroller = this;
29622
29623	/* Let's do it */
29624	this.construct();
29625};
29626
29627
29628
29629$.extend( Scroller.prototype, {
29630	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
29631	 * Public methods - to be exposed via the DataTables API
29632	 */
29633
29634	/**
29635	 * Calculate and store information about how many rows are to be displayed
29636	 * in the scrolling viewport, based on current dimensions in the browser's
29637	 * rendering. This can be particularly useful if the table is initially
29638	 * drawn in a hidden element - for example in a tab.
29639	 *  @param {bool} [redraw=true] Redraw the table automatically after the recalculation, with
29640	 *    the new dimensions forming the basis for the draw.
29641	 *  @returns {void}
29642	 */
29643	measure: function ( redraw )
29644	{
29645		if ( this.s.autoHeight )
29646		{
29647			this._calcRowHeight();
29648		}
29649
29650		var heights = this.s.heights;
29651
29652		if ( heights.row ) {
29653			heights.viewport = this._parseHeight($(this.dom.scroller).css('max-height'));
29654
29655			this.s.viewportRows = parseInt( heights.viewport / heights.row, 10 )+1;
29656			this.s.dt._iDisplayLength = this.s.viewportRows * this.s.displayBuffer;
29657		}
29658
29659		var label = this.dom.label.outerHeight();
29660		
29661		heights.xbar = this.dom.scroller.offsetHeight - this.dom.scroller.clientHeight;
29662		heights.labelHeight = label;
29663
29664		if ( redraw === undefined || redraw )
29665		{
29666			this.s.dt.oInstance.fnDraw( false );
29667		}
29668	},
29669
29670	/**
29671	 * Get information about current displayed record range. This corresponds to
29672	 * the information usually displayed in the "Info" block of the table.
29673	 *
29674	 * @returns {object} info as an object:
29675	 *  {
29676	 *      start: {int}, // the 0-indexed record at the top of the viewport
29677	 *      end:   {int}, // the 0-indexed record at the bottom of the viewport
29678	 *  }
29679	*/
29680	pageInfo: function()
29681	{
29682		var 
29683			dt = this.s.dt,
29684			iScrollTop = this.dom.scroller.scrollTop,
29685			iTotal = dt.fnRecordsDisplay(),
29686			iPossibleEnd = Math.ceil(this.pixelsToRow(iScrollTop + this.s.heights.viewport, false, this.s.ani));
29687
29688		return {
29689			start: Math.floor(this.pixelsToRow(iScrollTop, false, this.s.ani)),
29690			end: iTotal < iPossibleEnd ? iTotal-1 : iPossibleEnd-1
29691		};
29692	},
29693
29694	/**
29695	 * Calculate the row number that will be found at the given pixel position
29696	 * (y-scroll).
29697	 *
29698	 * Please note that when the height of the full table exceeds 1 million
29699	 * pixels, Scroller switches into a non-linear mode for the scrollbar to fit
29700	 * all of the records into a finite area, but this function returns a linear
29701	 * value (relative to the last non-linear positioning).
29702	 *  @param {int} pixels Offset from top to calculate the row number of
29703	 *  @param {int} [intParse=true] If an integer value should be returned
29704	 *  @param {int} [virtual=false] Perform the calculations in the virtual domain
29705	 *  @returns {int} Row index
29706	 */
29707	pixelsToRow: function ( pixels, intParse, virtual )
29708	{
29709		var diff = pixels - this.s.baseScrollTop;
29710		var row = virtual ?
29711			(this._domain( 'physicalToVirtual', this.s.baseScrollTop ) + diff) / this.s.heights.row :
29712			( diff / this.s.heights.row ) + this.s.baseRowTop;
29713
29714		return intParse || intParse === undefined ?
29715			parseInt( row, 10 ) :
29716			row;
29717	},
29718
29719	/**
29720	 * Calculate the pixel position from the top of the scrolling container for
29721	 * a given row
29722	 *  @param {int} iRow Row number to calculate the position of
29723	 *  @returns {int} Pixels
29724	 */
29725	rowToPixels: function ( rowIdx, intParse, virtual )
29726	{
29727		var pixels;
29728		var diff = rowIdx - this.s.baseRowTop;
29729
29730		if ( virtual ) {
29731			pixels = this._domain( 'virtualToPhysical', this.s.baseScrollTop );
29732			pixels += diff * this.s.heights.row;
29733		}
29734		else {
29735			pixels = this.s.baseScrollTop;
29736			pixels += diff * this.s.heights.row;
29737		}
29738
29739		return intParse || intParse === undefined ?
29740			parseInt( pixels, 10 ) :
29741			pixels;
29742	},
29743
29744
29745	/**
29746	 * Calculate the row number that will be found at the given pixel position (y-scroll)
29747	 *  @param {int} row Row index to scroll to
29748	 *  @param {bool} [animate=true] Animate the transition or not
29749	 *  @returns {void}
29750	 */
29751	scrollToRow: function ( row, animate )
29752	{
29753		var that = this;
29754		var ani = false;
29755		var px = this.rowToPixels( row );
29756
29757		// We need to know if the table will redraw or not before doing the
29758		// scroll. If it will not redraw, then we need to use the currently
29759		// displayed table, and scroll with the physical pixels. Otherwise, we
29760		// need to calculate the table's new position from the virtual
29761		// transform.
29762		var preRows = ((this.s.displayBuffer-1)/2) * this.s.viewportRows;
29763		var drawRow = row - preRows;
29764		if ( drawRow < 0 ) {
29765			drawRow = 0;
29766		}
29767
29768		if ( (px > this.s.redrawBottom || px < this.s.redrawTop) && this.s.dt._iDisplayStart !== drawRow ) {
29769			ani = true;
29770			px = this._domain( 'virtualToPhysical', row * this.s.heights.row );
29771
29772			// If we need records outside the current draw region, but the new
29773			// scrolling position is inside that (due to the non-linear nature
29774			// for larger numbers of records), we need to force position update.
29775			if ( this.s.redrawTop < px && px < this.s.redrawBottom ) {
29776				this.s.forceReposition = true;
29777				animate = false;
29778			}
29779		}
29780
29781		if ( animate === undefined || animate )
29782		{
29783			this.s.ani = ani;
29784			$(this.dom.scroller).animate( {
29785				"scrollTop": px
29786			}, function () {
29787				// This needs to happen after the animation has completed and
29788				// the final scroll event fired
29789				setTimeout( function () {
29790					that.s.ani = false;
29791				}, 250 );
29792			} );
29793		}
29794		else
29795		{
29796			$(this.dom.scroller).scrollTop( px );
29797		}
29798	},
29799
29800
29801	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
29802	 * Constructor
29803	 */
29804
29805	/**
29806	 * Initialisation for Scroller
29807	 *  @returns {void}
29808	 *  @private
29809	 */
29810	construct: function ()
29811	{
29812		var that = this;
29813		var dt = this.s.dtApi;
29814
29815		/* Sanity check */
29816		if ( !this.s.dt.oFeatures.bPaginate ) {
29817			this.s.dt.oApi._fnLog( this.s.dt, 0, 'Pagination must be enabled for Scroller' );
29818			return;
29819		}
29820
29821		/* Insert a div element that we can use to force the DT scrolling container to
29822		 * the height that would be required if the whole table was being displayed
29823		 */
29824		this.dom.force.style.position = "relative";
29825		this.dom.force.style.top = "0px";
29826		this.dom.force.style.left = "0px";
29827		this.dom.force.style.width = "1px";
29828
29829		this.dom.scroller = $('div.'+this.s.dt.oClasses.sScrollBody, this.s.dt.nTableWrapper)[0];
29830		this.dom.scroller.appendChild( this.dom.force );
29831		this.dom.scroller.style.position = "relative";
29832
29833		this.dom.table = $('>table', this.dom.scroller)[0];
29834		this.dom.table.style.position = "absolute";
29835		this.dom.table.style.top = "0px";
29836		this.dom.table.style.left = "0px";
29837
29838		// Add class to 'announce' that we are a Scroller table
29839		$(dt.table().container()).addClass('dts DTS');
29840
29841		// Add a 'loading' indicator
29842		if ( this.s.loadingIndicator )
29843		{
29844			this.dom.loader = $('<div class="dataTables_processing dts_loading">'+this.s.dt.oLanguage.sLoadingRecords+'</div>')
29845				.css('display', 'none');
29846
29847			$(this.dom.scroller.parentNode)
29848				.css('position', 'relative')
29849				.append( this.dom.loader );
29850		}
29851
29852		this.dom.label.appendTo(this.dom.scroller);
29853
29854		/* Initial size calculations */
29855		if ( this.s.heights.row && this.s.heights.row != 'auto' )
29856		{
29857			this.s.autoHeight = false;
29858		}
29859
29860		// Scrolling callback to see if a page change is needed
29861		this.s.ingnoreScroll = true;
29862		$(this.dom.scroller).on( 'scroll.dt-scroller', function (e) {
29863			that._scroll.call( that );
29864		} );
29865
29866		// In iOS we catch the touchstart event in case the user tries to scroll
29867		// while the display is already scrolling
29868		$(this.dom.scroller).on('touchstart.dt-scroller', function () {
29869			that._scroll.call( that );
29870		} );
29871
29872		$(this.dom.scroller)
29873			.on('mousedown.dt-scroller', function () {
29874				that.s.mousedown = true;
29875			})
29876			.on('mouseup.dt-scroller', function () {
29877				that.s.labelVisible = false;
29878				that.s.mousedown = false;
29879				that.dom.label.css('display', 'none');
29880			});
29881
29882		// On resize, update the information element, since the number of rows shown might change
29883		$(window).on( 'resize.dt-scroller', function () {
29884			that.measure( false );
29885			that._info();
29886		} );
29887
29888		// Add a state saving parameter to the DT state saving so we can restore the exact
29889		// position of the scrolling.
29890		var initialStateSave = true;
29891		var loadedState = dt.state.loaded();
29892
29893		dt.on( 'stateSaveParams.scroller', function ( e, settings, data ) {
29894			if ( initialStateSave && loadedState ) {
29895				data.scroller = loadedState.scroller;
29896				initialStateSave = false;
29897
29898				if (data.scroller) {
29899					that.s.lastScrollTop = data.scroller.scrollTop;
29900				}
29901			}
29902			else {
29903				// Need to used the saved position on init
29904				data.scroller = {
29905					topRow: that.s.topRowFloat,
29906					baseScrollTop: that.s.baseScrollTop,
29907					baseRowTop: that.s.baseRowTop,
29908					scrollTop: that.s.lastScrollTop
29909				};
29910			}
29911		} );
29912
29913		dt.on( 'stateLoadParams.scroller', function( e, settings, data ) {
29914			if (data.scroller !== undefined) {
29915				that.scrollToRow(data.scroller.topRow);
29916			}
29917		});
29918
29919		if ( loadedState && loadedState.scroller ) {
29920			this.s.topRowFloat = loadedState.scroller.topRow;
29921			this.s.baseScrollTop = loadedState.scroller.baseScrollTop;
29922			this.s.baseRowTop = loadedState.scroller.baseRowTop;
29923		}
29924
29925		this.measure( false );
29926	
29927		that.s.stateSaveThrottle = that.s.dt.oApi._fnThrottle( function () {
29928			that.s.dtApi.state.save();
29929		}, 500 );
29930
29931		dt.on( 'init.scroller', function () {
29932			that.measure( false );
29933
29934			// Setting to `jump` will instruct _draw to calculate the scroll top
29935			// position
29936			that.s.scrollType = 'jump';
29937			that._draw();
29938
29939			// Update the scroller when the DataTable is redrawn
29940			dt.on( 'draw.scroller', function () {
29941				that._draw();
29942			});
29943		} );
29944
29945		// Set height before the draw happens, allowing everything else to update
29946		// on draw complete without worry for roder.
29947		dt.on( 'preDraw.dt.scroller', function () {
29948			that._scrollForce();
29949		} );
29950
29951		// Destructor
29952		dt.on( 'destroy.scroller', function () {
29953			$(window).off( 'resize.dt-scroller' );
29954			$(that.dom.scroller).off('.dt-scroller');
29955			$(that.s.dt.nTable).off( '.scroller' );
29956
29957			$(that.s.dt.nTableWrapper).removeClass('DTS');
29958			$('div.DTS_Loading', that.dom.scroller.parentNode).remove();
29959
29960			that.dom.table.style.position = "";
29961			that.dom.table.style.top = "";
29962			that.dom.table.style.left = "";
29963		} );
29964	},
29965
29966
29967	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
29968	 * Private methods
29969	 */
29970
29971	/**
29972	 * Automatic calculation of table row height. This is just a little tricky here as using
29973	 * initialisation DataTables has tale the table out of the document, so we need to create
29974	 * a new table and insert it into the document, calculate the row height and then whip the
29975	 * table out.
29976	 *  @returns {void}
29977	 *  @private
29978	 */
29979	_calcRowHeight: function ()
29980	{
29981		var dt = this.s.dt;
29982		var origTable = dt.nTable;
29983		var nTable = origTable.cloneNode( false );
29984		var tbody = $('<tbody/>').appendTo( nTable );
29985		var container = $(
29986			'<div class="'+dt.oClasses.sWrapper+' DTS">'+
29987				'<div class="'+dt.oClasses.sScrollWrapper+'">'+
29988					'<div class="'+dt.oClasses.sScrollBody+'"></div>'+
29989				'</div>'+
29990			'</div>'
29991		);
29992
29993		// Want 3 rows in the sizing table so :first-child and :last-child
29994		// CSS styles don't come into play - take the size of the middle row
29995		$('tbody tr:lt(4)', origTable).clone().appendTo( tbody );
29996        var rowsCount = $('tr', tbody).length;
29997
29998        if ( rowsCount === 1 ) {
29999            tbody.prepend('<tr><td>&#160;</td></tr>');
30000            tbody.append('<tr><td>&#160;</td></tr>');
30001		}
30002		else {
30003            for (; rowsCount < 3; rowsCount++) {
30004                tbody.append('<tr><td>&#160;</td></tr>');
30005            }
30006		}
30007	
30008		$('div.'+dt.oClasses.sScrollBody, container).append( nTable );
30009
30010		// If initialised using `dom`, use the holding element as the insert point
30011		var insertEl = this.s.dt.nHolding || origTable.parentNode;
30012
30013		if ( ! $(insertEl).is(':visible') ) {
30014			insertEl = 'body';
30015		}
30016
30017		// Remove form element links as they might select over others (particularly radio and checkboxes)
30018		container.find("input").removeAttr("name");
30019
30020		container.appendTo( insertEl );
30021		this.s.heights.row = $('tr', tbody).eq(1).outerHeight();
30022
30023		container.remove();
30024	},
30025
30026	/**
30027	 * Draw callback function which is fired when the DataTable is redrawn. The main function of
30028	 * this method is to position the drawn table correctly the scrolling container for the rows
30029	 * that is displays as a result of the scrolling position.
30030	 *  @returns {void}
30031	 *  @private
30032	 */
30033	_draw: function ()
30034	{
30035		var
30036			that = this,
30037			heights = this.s.heights,
30038			iScrollTop = this.dom.scroller.scrollTop,
30039			iTableHeight = $(this.s.dt.nTable).height(),
30040			displayStart = this.s.dt._iDisplayStart,
30041			displayLen = this.s.dt._iDisplayLength,
30042			displayEnd = this.s.dt.fnRecordsDisplay(),
30043			viewportEndY = iScrollTop + heights.viewport;
30044
30045		// Disable the scroll event listener while we are updating the DOM
30046		this.s.skip = true;
30047
30048		// If paging is reset
30049		if ( (this.s.dt.bSorted || this.s.dt.bFiltered) && displayStart === 0 && !this.s.dt._drawHold ) {
30050			this.s.topRowFloat = 0;
30051		}
30052
30053		iScrollTop = this.s.scrollType === 'jump' ?
30054			this._domain( 'virtualToPhysical', this.s.topRowFloat * heights.row ) :
30055			iScrollTop;
30056
30057		// Store positional information so positional calculations can be based
30058		// upon the current table draw position
30059		this.s.baseScrollTop = iScrollTop;
30060		this.s.baseRowTop = this.s.topRowFloat;
30061
30062		// Position the table in the virtual scroller
30063		var tableTop = iScrollTop - ((this.s.topRowFloat - displayStart) * heights.row);
30064		if ( displayStart === 0 ) {
30065			tableTop = 0;
30066		}
30067		else if ( displayStart + displayLen >= displayEnd ) {
30068			tableTop = heights.scroll - iTableHeight;
30069		}
30070		else {
30071			var iTableBottomY = tableTop + iTableHeight;
30072			if (iTableBottomY < viewportEndY) {
30073				// The last row of the data is above the end of the viewport.
30074				// This means the background is visible, which is not what the user expects.
30075				var newTableTop = viewportEndY - iTableHeight;
30076				var diffPx = newTableTop - tableTop;
30077				this.s.baseScrollTop += diffPx + 1; // Update start row number in footer.
30078				tableTop = newTableTop; // Move table so last line of data is at the bottom of the viewport.
30079			}
30080		}
30081
30082		this.dom.table.style.top = tableTop+'px';
30083
30084		/* Cache some information for the scroller */
30085		this.s.tableTop = tableTop;
30086		this.s.tableBottom = iTableHeight + this.s.tableTop;
30087
30088		// Calculate the boundaries for where a redraw will be triggered by the
30089		// scroll event listener
30090		var boundaryPx = (iScrollTop - this.s.tableTop) * this.s.boundaryScale;
30091		this.s.redrawTop = iScrollTop - boundaryPx;
30092		this.s.redrawBottom = iScrollTop + boundaryPx > heights.scroll - heights.viewport - heights.row ?
30093			heights.scroll - heights.viewport - heights.row :
30094			iScrollTop + boundaryPx;
30095
30096		this.s.skip = false;
30097
30098		if(that.s.ingnoreScroll) {
30099			// Restore the scrolling position that was saved by DataTable's state
30100			// saving Note that this is done on the second draw when data is Ajax
30101			// sourced, and the first draw when DOM soured
30102			if ( this.s.dt.oFeatures.bStateSave && this.s.dt.oLoadedState !== null &&
30103				 typeof this.s.dt.oLoadedState.scroller != 'undefined' )
30104			{
30105				// A quirk of DataTables is that the draw callback will occur on an
30106				// empty set if Ajax sourced, but not if server-side processing.
30107				var ajaxSourced = (this.s.dt.sAjaxSource || that.s.dt.ajax) && ! this.s.dt.oFeatures.bServerSide ?
30108					true :
30109					false;
30110	
30111				if ( ( ajaxSourced && this.s.dt.iDraw >= 2) ||
30112					 (!ajaxSourced && this.s.dt.iDraw >= 1) )
30113				{
30114					setTimeout( function () {
30115						$(that.dom.scroller).scrollTop( that.s.dt.oLoadedState.scroller.scrollTop );
30116	
30117						// In order to prevent layout thrashing we need another
30118						// small delay
30119						setTimeout( function () {
30120							that.s.ingnoreScroll = false;
30121						}, 0 );
30122					}, 0 );
30123				}
30124			}
30125			else {
30126				that.s.ingnoreScroll = false;
30127			}
30128		}
30129
30130
30131		// Because of the order of the DT callbacks, the info update will
30132		// take precedence over the one we want here. So a 'thread' break is
30133		// needed.  Only add the thread break if bInfo is set
30134		if ( this.s.dt.oFeatures.bInfo ) {
30135			setTimeout( function () {
30136				that._info.call( that );
30137			}, 0 );
30138		}
30139
30140		$(this.s.dt.nTable).triggerHandler('position.dts.dt', tableTop);
30141
30142		// Hide the loading indicator
30143		if ( this.dom.loader && this.s.loaderVisible ) {
30144			this.dom.loader.css( 'display', 'none' );
30145			this.s.loaderVisible = false;
30146		}
30147	},
30148
30149	/**
30150	 * Convert from one domain to another. The physical domain is the actual
30151	 * pixel count on the screen, while the virtual is if we had browsers which
30152	 * had scrolling containers of infinite height (i.e. the absolute value)
30153	 *
30154	 *  @param {string} dir Domain transform direction, `virtualToPhysical` or
30155	 *    `physicalToVirtual` 
30156	 *  @returns {number} Calculated transform
30157	 *  @private
30158	 */
30159	_domain: function ( dir, val )
30160	{
30161		var heights = this.s.heights;
30162		var diff;
30163		var magic = 10000; // the point at which the non-linear calculations start to happen
30164
30165		// If the virtual and physical height match, then we use a linear
30166		// transform between the two, allowing the scrollbar to be linear
30167		if ( heights.virtual === heights.scroll ) {
30168			return val;
30169		}
30170
30171		// In the first 10k pixels and the last 10k pixels, we want the scrolling
30172		// to be linear. After that it can be non-linear. It would be unusual for
30173		// anyone to mouse wheel through that much.
30174		if ( val < magic ) {
30175			return val;
30176		}
30177		else if ( dir === 'virtualToPhysical' && val >= heights.virtual - magic ) {
30178			diff = heights.virtual - val;
30179			return heights.scroll - diff;
30180		}
30181		else if ( dir === 'physicalToVirtual' && val >= heights.scroll - magic ) {
30182			diff = heights.scroll - val;
30183			return heights.virtual - diff;
30184		}
30185
30186		// Otherwise, we want a non-linear scrollbar to take account of the
30187		// redrawing regions at the start and end of the table, otherwise these
30188		// can stutter badly - on large tables 30px (for example) scroll might
30189		// be hundreds of rows, so the table would be redrawing every few px at
30190		// the start and end. Use a simple linear eq. to stop this, effectively
30191		// causing a kink in the scrolling ratio. It does mean the scrollbar is
30192		// non-linear, but with such massive data sets, the scrollbar is going
30193		// to be a best guess anyway
30194		var m = (heights.virtual - magic - magic) / (heights.scroll - magic - magic);
30195		var c = magic - (m*magic);
30196
30197		return dir === 'virtualToPhysical' ?
30198			(val-c) / m :
30199			(m*val) + c;
30200	},
30201
30202	/**
30203	 * Update any information elements that are controlled by the DataTable based on the scrolling
30204	 * viewport and what rows are visible in it. This function basically acts in the same way as
30205	 * _fnUpdateInfo in DataTables, and effectively replaces that function.
30206	 *  @returns {void}
30207	 *  @private
30208	 */
30209	_info: function ()
30210	{
30211		if ( !this.s.dt.oFeatures.bInfo )
30212		{
30213			return;
30214		}
30215
30216		var
30217			dt = this.s.dt,
30218			language = dt.oLanguage,
30219			iScrollTop = this.dom.scroller.scrollTop,
30220			iStart = Math.floor( this.pixelsToRow(iScrollTop, false, this.s.ani)+1 ),
30221			iMax = dt.fnRecordsTotal(),
30222			iTotal = dt.fnRecordsDisplay(),
30223			iPossibleEnd = Math.ceil( this.pixelsToRow(iScrollTop+this.s.heights.viewport, false, this.s.ani) ),
30224			iEnd = iTotal < iPossibleEnd ? iTotal : iPossibleEnd,
30225			sStart = dt.fnFormatNumber( iStart ),
30226			sEnd = dt.fnFormatNumber( iEnd ),
30227			sMax = dt.fnFormatNumber( iMax ),
30228			sTotal = dt.fnFormatNumber( iTotal ),
30229			sOut;
30230
30231		if ( dt.fnRecordsDisplay() === 0 &&
30232			   dt.fnRecordsDisplay() == dt.fnRecordsTotal() )
30233		{
30234			/* Empty record set */
30235			sOut = language.sInfoEmpty+ language.sInfoPostFix;
30236		}
30237		else if ( dt.fnRecordsDisplay() === 0 )
30238		{
30239			/* Empty record set after filtering */
30240			sOut = language.sInfoEmpty +' '+
30241				language.sInfoFiltered.replace('_MAX_', sMax)+
30242					language.sInfoPostFix;
30243		}
30244		else if ( dt.fnRecordsDisplay() == dt.fnRecordsTotal() )
30245		{
30246			/* Normal record set */
30247			sOut = language.sInfo.
30248					replace('_START_', sStart).
30249					replace('_END_',   sEnd).
30250					replace('_MAX_',   sMax).
30251					replace('_TOTAL_', sTotal)+
30252				language.sInfoPostFix;
30253		}
30254		else
30255		{
30256			/* Record set after filtering */
30257			sOut = language.sInfo.
30258					replace('_START_', sStart).
30259					replace('_END_',   sEnd).
30260					replace('_MAX_',   sMax).
30261					replace('_TOTAL_', sTotal) +' '+
30262				language.sInfoFiltered.replace(
30263					'_MAX_',
30264					dt.fnFormatNumber(dt.fnRecordsTotal())
30265				)+
30266				language.sInfoPostFix;
30267		}
30268
30269		var callback = language.fnInfoCallback;
30270		if ( callback ) {
30271			sOut = callback.call( dt.oInstance,
30272				dt, iStart, iEnd, iMax, iTotal, sOut
30273			);
30274		}
30275
30276		var n = dt.aanFeatures.i;
30277		if ( typeof n != 'undefined' )
30278		{
30279			for ( var i=0, iLen=n.length ; i<iLen ; i++ )
30280			{
30281				$(n[i]).html( sOut );
30282			}
30283		}
30284
30285		// DT doesn't actually (yet) trigger this event, but it will in future
30286		$(dt.nTable).triggerHandler( 'info.dt' );
30287	},
30288
30289	/**
30290	 * Parse CSS height property string as number
30291	 *
30292	 * An attempt is made to parse the string as a number. Currently supported units are 'px',
30293	 * 'vh', and 'rem'. 'em' is partially supported; it works as long as the parent element's
30294	 * font size matches the body element. Zero is returned for unrecognized strings.
30295	 *  @param {string} cssHeight CSS height property string
30296	 *  @returns {number} height
30297	 *  @private
30298	 */
30299	_parseHeight: function(cssHeight) {
30300		var height;
30301		var matches = /^([+-]?(?:\d+(?:\.\d+)?|\.\d+))(px|em|rem|vh)$/.exec(cssHeight);
30302
30303		if (matches === null) {
30304			return 0;
30305		}
30306
30307		var value = parseFloat(matches[1]);
30308		var unit = matches[2];
30309
30310		if ( unit === 'px' ) {
30311			height = value;
30312		}
30313		else if ( unit === 'vh' ) {
30314			height = ( value / 100 ) * $(window).height();
30315		}
30316		else if ( unit === 'rem' ) {
30317			height = value * parseFloat($(':root').css('font-size'));
30318		}
30319		else if ( unit === 'em' ) {
30320			height = value * parseFloat($('body').css('font-size'));
30321		}
30322
30323		return height ?
30324			height :
30325			0;
30326	},
30327
30328	/**
30329	 * Scrolling function - fired whenever the scrolling position is changed.
30330	 * This method needs to use the stored values to see if the table should be
30331	 * redrawn as we are moving towards the end of the information that is
30332	 * currently drawn or not. If needed, then it will redraw the table based on
30333	 * the new position.
30334	 *  @returns {void}
30335	 *  @private
30336	 */
30337	_scroll: function ()
30338	{
30339		var
30340			that = this,
30341			heights = this.s.heights,
30342			iScrollTop = this.dom.scroller.scrollTop,
30343			iTopRow;
30344
30345		if ( this.s.skip ) {
30346			return;
30347		}
30348
30349		if ( this.s.ingnoreScroll ) {
30350			return;
30351		}
30352
30353		if ( iScrollTop === this.s.lastScrollTop ) {
30354			return;
30355		}
30356
30357		/* If the table has been sorted or filtered, then we use the redraw that
30358		 * DataTables as done, rather than performing our own
30359		 */
30360		if ( this.s.dt.bFiltered || this.s.dt.bSorted ) {
30361			this.s.lastScrollTop = 0;
30362			return;
30363		}
30364
30365		/* Update the table's information display for what is now in the viewport */
30366		this._info();
30367
30368		/* We don't want to state save on every scroll event - that's heavy
30369		 * handed, so use a timeout to update the state saving only when the
30370		 * scrolling has finished
30371		 */
30372		clearTimeout( this.s.stateTO );
30373		this.s.stateTO = setTimeout( function () {
30374			that.s.dtApi.state.save();
30375		}, 250 );
30376
30377		this.s.scrollType = Math.abs(iScrollTop - this.s.lastScrollTop) > heights.viewport ?
30378			'jump' :
30379			'cont';
30380
30381		this.s.topRowFloat = this.s.scrollType === 'cont' ?
30382			this.pixelsToRow( iScrollTop, false, false ) :
30383			this._domain( 'physicalToVirtual', iScrollTop ) / heights.row;
30384
30385		if ( this.s.topRowFloat < 0 ) {
30386			this.s.topRowFloat = 0;
30387		}
30388
30389		/* Check if the scroll point is outside the trigger boundary which would required
30390		 * a DataTables redraw
30391		 */
30392		if ( this.s.forceReposition || iScrollTop < this.s.redrawTop || iScrollTop > this.s.redrawBottom ) {
30393			var preRows = Math.ceil( ((this.s.displayBuffer-1)/2) * this.s.viewportRows );
30394
30395			iTopRow = parseInt(this.s.topRowFloat, 10) - preRows;
30396			this.s.forceReposition = false;
30397
30398			if ( iTopRow <= 0 ) {
30399				/* At the start of the table */
30400				iTopRow = 0;
30401			}
30402			else if ( iTopRow + this.s.dt._iDisplayLength > this.s.dt.fnRecordsDisplay() ) {
30403				/* At the end of the table */
30404				iTopRow = this.s.dt.fnRecordsDisplay() - this.s.dt._iDisplayLength;
30405				if ( iTopRow < 0 ) {
30406					iTopRow = 0;
30407				}
30408			}
30409			else if ( iTopRow % 2 !== 0 ) {
30410				// For the row-striping classes (odd/even) we want only to start
30411				// on evens otherwise the stripes will change between draws and
30412				// look rubbish
30413				iTopRow++;
30414			}
30415
30416			// Store calcuated value, in case the following condition is not met, but so
30417			// that the draw function will still use it.
30418			this.s.targetTop = iTopRow;
30419
30420			if ( iTopRow != this.s.dt._iDisplayStart ) {
30421				/* Cache the new table position for quick lookups */
30422				this.s.tableTop = $(this.s.dt.nTable).offset().top;
30423				this.s.tableBottom = $(this.s.dt.nTable).height() + this.s.tableTop;
30424
30425				var draw = function () {
30426					that.s.dt._iDisplayStart = that.s.targetTop;
30427					that.s.dt.oApi._fnDraw( that.s.dt );
30428				};
30429
30430				/* Do the DataTables redraw based on the calculated start point - note that when
30431				 * using server-side processing we introduce a small delay to not DoS the server...
30432				 */
30433				if ( this.s.dt.oFeatures.bServerSide ) {
30434					this.s.forceReposition = true;
30435
30436					clearTimeout( this.s.drawTO );
30437					this.s.drawTO = setTimeout( draw, this.s.serverWait );
30438				}
30439				else {
30440					draw();
30441				}
30442
30443				if ( this.dom.loader && ! this.s.loaderVisible ) {
30444					this.dom.loader.css( 'display', 'block' );
30445					this.s.loaderVisible = true;
30446				}
30447			}
30448		}
30449		else {
30450			this.s.topRowFloat = this.pixelsToRow( iScrollTop, false, true );
30451		}
30452
30453		this.s.lastScrollTop = iScrollTop;
30454		this.s.stateSaveThrottle();
30455
30456		if ( this.s.scrollType === 'jump' && this.s.mousedown ) {
30457			this.s.labelVisible = true;
30458		}
30459		if (this.s.labelVisible) {
30460			var labelFactor = (heights.viewport-heights.labelHeight - heights.xbar) / heights.scroll;
30461
30462			this.dom.label
30463				.html( this.s.dt.fnFormatNumber( parseInt( this.s.topRowFloat, 10 )+1 ) )
30464				.css( 'top', iScrollTop + (iScrollTop * labelFactor) )
30465				.css( 'right', 10 - this.dom.scroller.scrollLeft)
30466				.css( 'display', 'block' );
30467		}
30468	},
30469
30470	/**
30471	 * Force the scrolling container to have height beyond that of just the
30472	 * table that has been drawn so the user can scroll the whole data set.
30473	 *
30474	 * Note that if the calculated required scrolling height exceeds a maximum
30475	 * value (1 million pixels - hard-coded) the forcing element will be set
30476	 * only to that maximum value and virtual / physical domain transforms will
30477	 * be used to allow Scroller to display tables of any number of records.
30478	 *  @returns {void}
30479	 *  @private
30480	 */
30481	_scrollForce: function ()
30482	{
30483		var heights = this.s.heights;
30484		var max = 1000000;
30485
30486		heights.virtual = heights.row * this.s.dt.fnRecordsDisplay();
30487		heights.scroll = heights.virtual;
30488
30489		if ( heights.scroll > max ) {
30490			heights.scroll = max;
30491		}
30492
30493		// Minimum height so there is always a row visible (the 'no rows found'
30494		// if reduced to zero filtering)
30495		this.dom.force.style.height = heights.scroll > this.s.heights.row ?
30496			heights.scroll+'px' :
30497			this.s.heights.row+'px';
30498	}
30499} );
30500
30501
30502
30503/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
30504 * Statics
30505 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
30506
30507
30508/**
30509 * Scroller default settings for initialisation
30510 *  @namespace
30511 *  @name Scroller.defaults
30512 *  @static
30513 */
30514Scroller.defaults = {
30515	/**
30516	 * Scroller uses the boundary scaling factor to decide when to redraw the table - which it
30517	 * typically does before you reach the end of the currently loaded data set (in order to
30518	 * allow the data to look continuous to a user scrolling through the data). If given as 0
30519	 * then the table will be redrawn whenever the viewport is scrolled, while 1 would not
30520	 * redraw the table until the currently loaded data has all been shown. You will want
30521	 * something in the middle - the default factor of 0.5 is usually suitable.
30522	 *  @type     float
30523	 *  @default  0.5
30524	 *  @static
30525	 */
30526	boundaryScale: 0.5,
30527
30528	/**
30529	 * The display buffer is what Scroller uses to calculate how many rows it should pre-fetch
30530	 * for scrolling. Scroller automatically adjusts DataTables' display length to pre-fetch
30531	 * rows that will be shown in "near scrolling" (i.e. just beyond the current display area).
30532	 * The value is based upon the number of rows that can be displayed in the viewport (i.e.
30533	 * a value of 1), and will apply the display range to records before before and after the
30534	 * current viewport - i.e. a factor of 3 will allow Scroller to pre-fetch 1 viewport's worth
30535	 * of rows before the current viewport, the current viewport's rows and 1 viewport's worth
30536	 * of rows after the current viewport. Adjusting this value can be useful for ensuring
30537	 * smooth scrolling based on your data set.
30538	 *  @type     int
30539	 *  @default  7
30540	 *  @static
30541	 */
30542	displayBuffer: 9,
30543
30544	/**
30545	 * Show (or not) the loading element in the background of the table. Note that you should
30546	 * include the dataTables.scroller.css file for this to be displayed correctly.
30547	 *  @type     boolean
30548	 *  @default  false
30549	 *  @static
30550	 */
30551	loadingIndicator: false,
30552
30553	/**
30554	 * Scroller will attempt to automatically calculate the height of rows for it's internal
30555	 * calculations. However the height that is used can be overridden using this parameter.
30556	 *  @type     int|string
30557	 *  @default  auto
30558	 *  @static
30559	 */
30560	rowHeight: "auto",
30561
30562	/**
30563	 * When using server-side processing, Scroller will wait a small amount of time to allow
30564	 * the scrolling to finish before requesting more data from the server. This prevents
30565	 * you from DoSing your own server! The wait time can be configured by this parameter.
30566	 *  @type     int
30567	 *  @default  200
30568	 *  @static
30569	 */
30570	serverWait: 200
30571};
30572
30573Scroller.oDefaults = Scroller.defaults;
30574
30575
30576
30577/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
30578 * Constants
30579 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
30580
30581/**
30582 * Scroller version
30583 *  @type      String
30584 *  @default   See code
30585 *  @name      Scroller.version
30586 *  @static
30587 */
30588Scroller.version = "2.1.1";
30589
30590
30591
30592/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
30593 * Initialisation
30594 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
30595
30596// Attach a listener to the document which listens for DataTables initialisation
30597// events so we can automatically initialise
30598$(document).on( 'preInit.dt.dtscroller', function (e, settings) {
30599	if ( e.namespace !== 'dt' ) {
30600		return;
30601	}
30602
30603	var init = settings.oInit.scroller;
30604	var defaults = DataTable.defaults.scroller;
30605
30606	if ( init || defaults ) {
30607		var opts = $.extend( {}, init, defaults );
30608
30609		if ( init !== false ) {
30610			new Scroller( settings, opts  );
30611		}
30612	}
30613} );
30614
30615
30616// Attach Scroller to DataTables so it can be accessed as an 'extra'
30617$.fn.dataTable.Scroller = Scroller;
30618$.fn.DataTable.Scroller = Scroller;
30619
30620
30621// DataTables 1.10 API method aliases
30622var Api = $.fn.dataTable.Api;
30623
30624Api.register( 'scroller()', function () {
30625	return this;
30626} );
30627
30628// Undocumented and deprecated - is it actually useful at all?
30629Api.register( 'scroller().rowToPixels()', function ( rowIdx, intParse, virtual ) {
30630	var ctx = this.context;
30631
30632	if ( ctx.length && ctx[0].oScroller ) {
30633		return ctx[0].oScroller.rowToPixels( rowIdx, intParse, virtual );
30634	}
30635	// undefined
30636} );
30637
30638// Undocumented and deprecated - is it actually useful at all?
30639Api.register( 'scroller().pixelsToRow()', function ( pixels, intParse, virtual ) {
30640	var ctx = this.context;
30641
30642	if ( ctx.length && ctx[0].oScroller ) {
30643		return ctx[0].oScroller.pixelsToRow( pixels, intParse, virtual );
30644	}
30645	// undefined
30646} );
30647
30648// `scroller().scrollToRow()` is undocumented and deprecated. Use `scroller.toPosition()
30649Api.register( ['scroller().scrollToRow()', 'scroller.toPosition()'], function ( idx, ani ) {
30650	this.iterator( 'table', function ( ctx ) {
30651		if ( ctx.oScroller ) {
30652			ctx.oScroller.scrollToRow( idx, ani );
30653		}
30654	} );
30655
30656	return this;
30657} );
30658
30659Api.register( 'row().scrollTo()', function ( ani ) {
30660	var that = this;
30661
30662	this.iterator( 'row', function ( ctx, rowIdx ) {
30663		if ( ctx.oScroller ) {
30664			var displayIdx = that
30665				.rows( { order: 'applied', search: 'applied' } )
30666				.indexes()
30667				.indexOf( rowIdx );
30668
30669			ctx.oScroller.scrollToRow( displayIdx, ani );
30670		}
30671	} );
30672
30673	return this;
30674} );
30675
30676Api.register( 'scroller.measure()', function ( redraw ) {
30677	this.iterator( 'table', function ( ctx ) {
30678		if ( ctx.oScroller ) {
30679			ctx.oScroller.measure( redraw );
30680		}
30681	} );
30682
30683	return this;
30684} );
30685
30686Api.register( 'scroller.page()', function() {
30687	var ctx = this.context;
30688
30689	if ( ctx.length && ctx[0].oScroller ) {
30690		return ctx[0].oScroller.pageInfo();
30691	}
30692	// undefined
30693} );
30694
30695
30696return DataTable;
30697}));
30698
30699
30700/*! SearchBuilder 1.4.2
30701 * ©SpryMedia Ltd - datatables.net/license/mit
30702 */
30703
30704(function( factory ){
30705	if ( typeof define === 'function' && define.amd ) {
30706		// AMD
30707		define( ['jquery', 'datatables.net'], function ( $ ) {
30708			return factory( $, window, document );
30709		} );
30710	}
30711	else if ( typeof exports === 'object' ) {
30712		// CommonJS
30713		var jq = require('jquery');
30714		var cjsRequires = function (root, $) {
30715			if ( ! $.fn.dataTable ) {
30716				require('datatables.net')(root, $);
30717			}
30718		};
30719
30720		if (typeof window !== 'undefined') {
30721			module.exports = function (root, $) {
30722				if ( ! root ) {
30723					// CommonJS environments without a window global must pass a
30724					// root. This will give an error otherwise
30725					root = window;
30726				}
30727
30728				if ( ! $ ) {
30729					$ = jq( root );
30730				}
30731
30732				cjsRequires( root, $ );
30733				return factory( $, root, root.document );
30734			};
30735		}
30736		else {
30737			cjsRequires( window, jq );
30738			module.exports = factory( jq, window, window.document );
30739		}
30740	}
30741	else {
30742		// Browser
30743		factory( jQuery, window, document );
30744	}
30745}(function( $, window, document, undefined ) {
30746'use strict';
30747var DataTable = $.fn.dataTable;
30748
30749
30750(function () {
30751    'use strict';
30752
30753    var $$3;
30754    var dataTable$3;
30755    function moment() {
30756        return window.moment;
30757    }
30758    function luxon() {
30759        return window.luxon;
30760    }
30761    /**
30762     * Sets the value of jQuery for use in the file
30763     *
30764     * @param jq the instance of jQuery to be set
30765     */
30766    function setJQuery$2(jq) {
30767        $$3 = jq;
30768        dataTable$3 = jq.fn.dataTable;
30769    }
30770    /**
30771     * The Criteria class is used within SearchBuilder to represent a search criteria
30772     */
30773    var Criteria = /** @class */ (function () {
30774        function Criteria(table, opts, topGroup, index, depth, serverData) {
30775            if (index === void 0) { index = 0; }
30776            if (depth === void 0) { depth = 1; }
30777            if (serverData === void 0) { serverData = undefined; }
30778            var _this = this;
30779            // Check that the required version of DataTables is included
30780            if (!dataTable$3 || !dataTable$3.versionCheck || !dataTable$3.versionCheck('1.10.0')) {
30781                throw new Error('SearchPane requires DataTables 1.10 or newer');
30782            }
30783            this.classes = $$3.extend(true, {}, Criteria.classes);
30784            // Get options from user and any extra conditions/column types defined by plug-ins
30785            this.c = $$3.extend(true, {}, Criteria.defaults, $$3.fn.dataTable.ext.searchBuilder, opts);
30786            var i18n = this.c.i18n;
30787            this.s = {
30788                condition: undefined,
30789                conditions: {},
30790                data: undefined,
30791                dataIdx: -1,
30792                dataPoints: [],
30793                dateFormat: false,
30794                depth: depth,
30795                dt: table,
30796                filled: false,
30797                index: index,
30798                origData: undefined,
30799                preventRedraw: false,
30800                serverData: serverData,
30801                topGroup: topGroup,
30802                type: '',
30803                value: []
30804            };
30805            this.dom = {
30806                buttons: $$3('<div/>')
30807                    .addClass(this.classes.buttonContainer),
30808                condition: $$3('<select disabled/>')
30809                    .addClass(this.classes.condition)
30810                    .addClass(this.classes.dropDown)
30811                    .addClass(this.classes.italic)
30812                    .attr('autocomplete', 'hacking'),
30813                conditionTitle: $$3('<option value="" disabled selected hidden/>')
30814                    .html(this.s.dt.i18n('searchBuilder.condition', i18n.condition)),
30815                container: $$3('<div/>')
30816                    .addClass(this.classes.container),
30817                data: $$3('<select/>')
30818                    .addClass(this.classes.data)
30819                    .addClass(this.classes.dropDown)
30820                    .addClass(this.classes.italic),
30821                dataTitle: $$3('<option value="" disabled selected hidden/>')
30822                    .html(this.s.dt.i18n('searchBuilder.data', i18n.data)),
30823                defaultValue: $$3('<select disabled/>')
30824                    .addClass(this.classes.value)
30825                    .addClass(this.classes.dropDown)
30826                    .addClass(this.classes.select)
30827                    .addClass(this.classes.italic),
30828                "delete": $$3('<button/>')
30829                    .html(this.s.dt.i18n('searchBuilder.delete', i18n["delete"]))
30830                    .addClass(this.classes["delete"])
30831                    .addClass(this.classes.button)
30832                    .attr('title', this.s.dt.i18n('searchBuilder.deleteTitle', i18n.deleteTitle))
30833                    .attr('type', 'button'),
30834                inputCont: $$3('<div/>')
30835                    .addClass(this.classes.inputCont),
30836                // eslint-disable-next-line no-useless-escape
30837                left: $$3('<button/>')
30838                    .html(this.s.dt.i18n('searchBuilder.left', i18n.left))
30839                    .addClass(this.classes.left)
30840                    .addClass(this.classes.button)
30841                    .attr('title', this.s.dt.i18n('searchBuilder.leftTitle', i18n.leftTitle))
30842                    .attr('type', 'button'),
30843                // eslint-disable-next-line no-useless-escape
30844                right: $$3('<button/>')
30845                    .html(this.s.dt.i18n('searchBuilder.right', i18n.right))
30846                    .addClass(this.classes.right)
30847                    .addClass(this.classes.button)
30848                    .attr('title', this.s.dt.i18n('searchBuilder.rightTitle', i18n.rightTitle))
30849                    .attr('type', 'button'),
30850                value: [
30851                    $$3('<select disabled/>')
30852                        .addClass(this.classes.value)
30853                        .addClass(this.classes.dropDown)
30854                        .addClass(this.classes.italic)
30855                        .addClass(this.classes.select)
30856                ],
30857                valueTitle: $$3('<option value="--valueTitle--" disabled selected hidden/>')
30858                    .html(this.s.dt.i18n('searchBuilder.value', i18n.value))
30859            };
30860            // If the greyscale option is selected then add the class to add the grey colour to SearchBuilder
30861            if (this.c.greyscale) {
30862                this.dom.data.addClass(this.classes.greyscale);
30863                this.dom.condition.addClass(this.classes.greyscale);
30864                this.dom.defaultValue.addClass(this.classes.greyscale);
30865                for (var _i = 0, _a = this.dom.value; _i < _a.length; _i++) {
30866                    var val = _a[_i];
30867                    val.addClass(this.classes.greyscale);
30868                }
30869            }
30870            $$3(window).on('resize.dtsb', dataTable$3.util.throttle(function () {
30871                _this.s.topGroup.trigger('dtsb-redrawLogic');
30872            }));
30873            this._buildCriteria();
30874            return this;
30875        }
30876        /**
30877         * Escape html characters within a string
30878         *
30879         * @param txt the string to be escaped
30880         * @returns the escaped string
30881         */
30882        Criteria._escapeHTML = function (txt) {
30883            return txt
30884                .toString()
30885                .replace(/&amp;/g, '&')
30886                .replace(/&lt;/g, '<')
30887                .replace(/&gt;/g, '>')
30888                .replace(/&quot;/g, '"');
30889        };
30890        /**
30891         * Parses formatted numbers down to a form where they can be compared
30892         *
30893         * @param val the value to convert
30894         * @returns the converted value
30895         */
30896        Criteria.parseNumFmt = function (val) {
30897            return +val.replace(/(?!^-)[^0-9.]/g, '');
30898        };
30899        /**
30900         * Adds the left button to the criteria
30901         */
30902        Criteria.prototype.updateArrows = function (hasSiblings) {
30903            if (hasSiblings === void 0) { hasSiblings = false; }
30904            // Empty the container and append all of the elements in the correct order
30905            this.dom.container.children().detach();
30906            this.dom.container
30907                .append(this.dom.data)
30908                .append(this.dom.condition)
30909                .append(this.dom.inputCont);
30910            this.setListeners();
30911            // Trigger the inserted events for the value elements as they are inserted
30912            if (this.dom.value[0] !== undefined) {
30913                this.dom.value[0].trigger('dtsb-inserted');
30914            }
30915            for (var i = 1; i < this.dom.value.length; i++) {
30916                this.dom.inputCont.append(this.dom.value[i]);
30917                this.dom.value[i].trigger('dtsb-inserted');
30918            }
30919            // If this is a top level criteria then don't let it move left
30920            if (this.s.depth > 1) {
30921                this.dom.buttons.append(this.dom.left);
30922            }
30923            // If the depthLimit of the query has been hit then don't add the right button
30924            if ((this.c.depthLimit === false || this.s.depth < this.c.depthLimit) && hasSiblings) {
30925                this.dom.buttons.append(this.dom.right);
30926            }
30927            else {
30928                this.dom.right.remove();
30929            }
30930            this.dom.buttons.append(this.dom["delete"]);
30931            this.dom.container.append(this.dom.buttons);
30932        };
30933        /**
30934         * Destroys the criteria, removing listeners and container from the dom
30935         */
30936        Criteria.prototype.destroy = function () {
30937            // Turn off listeners
30938            this.dom.data.off('.dtsb');
30939            this.dom.condition.off('.dtsb');
30940            this.dom["delete"].off('.dtsb');
30941            for (var _i = 0, _a = this.dom.value; _i < _a.length; _i++) {
30942                var val = _a[_i];
30943                val.off('.dtsb');
30944            }
30945            // Remove container from the dom
30946            this.dom.container.remove();
30947        };
30948        /**
30949         * Passes in the data for the row and compares it against this single criteria
30950         *
30951         * @param rowData The data for the row to be compared
30952         * @returns boolean Whether the criteria has passed
30953         */
30954        Criteria.prototype.search = function (rowData, rowIdx) {
30955            var condition = this.s.conditions[this.s.condition];
30956            if (this.s.condition !== undefined && condition !== undefined) {
30957                var filter = rowData[this.s.dataIdx];
30958                // This check is in place for if a custom decimal character is in place
30959                if (this.s.type.includes('num') &&
30960                    (this.s.dt.settings()[0].oLanguage.sDecimal !== '' ||
30961                        this.s.dt.settings()[0].oLanguage.sThousands !== '')) {
30962                    var splitRD = [rowData[this.s.dataIdx]];
30963                    if (this.s.dt.settings()[0].oLanguage.sDecimal !== '') {
30964                        splitRD = rowData[this.s.dataIdx].split(this.s.dt.settings()[0].oLanguage.sDecimal);
30965                    }
30966                    if (this.s.dt.settings()[0].oLanguage.sThousands !== '') {
30967                        for (var i = 0; i < splitRD.length; i++) {
30968                            splitRD[i] = splitRD[i].replace(this.s.dt.settings()[0].oLanguage.sThousands, ',');
30969                        }
30970                    }
30971                    filter = splitRD.join('.');
30972                }
30973                // If orthogonal data is in place we need to get it's values for searching
30974                if (this.c.orthogonal.search !== 'filter') {
30975                    var settings = this.s.dt.settings()[0];
30976                    filter = settings.oApi._fnGetCellData(settings, rowIdx, this.s.dataIdx, typeof this.c.orthogonal === 'string' ?
30977                        this.c.orthogonal :
30978                        this.c.orthogonal.search);
30979                }
30980                if (this.s.type === 'array') {
30981                    // Make sure we are working with an array
30982                    if (!Array.isArray(filter)) {
30983                        filter = [filter];
30984                    }
30985                    filter.sort();
30986                    for (var _i = 0, filter_1 = filter; _i < filter_1.length; _i++) {
30987                        var filt = filter_1[_i];
30988                        if (filt && typeof filt === 'string') {
30989                            filt = filt.replace(/[\r\n\u2028]/g, ' ');
30990                        }
30991                    }
30992                }
30993                else if (filter !== null && typeof filter === 'string') {
30994                    filter = filter.replace(/[\r\n\u2028]/g, ' ');
30995                }
30996                if (this.s.type.includes('html') && typeof filter === 'string') {
30997                    filter = filter.replace(/(<([^>]+)>)/ig, '');
30998                }
30999                // Not ideal, but jqueries .val() returns an empty string even
31000                // when the value set is null, so we shall assume the two are equal
31001                if (filter === null) {
31002                    filter = '';
31003                }
31004                return condition.search(filter, this.s.value, this);
31005            }
31006        };
31007        /**
31008         * Gets the details required to rebuild the criteria
31009         */
31010        Criteria.prototype.getDetails = function (deFormatDates) {
31011            if (deFormatDates === void 0) { deFormatDates = false; }
31012            // This check is in place for if a custom decimal character is in place
31013            if (this.s.type !== null &&
31014                this.s.type.includes('num') &&
31015                (this.s.dt.settings()[0].oLanguage.sDecimal !== '' || this.s.dt.settings()[0].oLanguage.sThousands !== '')) {
31016                for (var i = 0; i < this.s.value.length; i++) {
31017                    var splitRD = [this.s.value[i].toString()];
31018                    if (this.s.dt.settings()[0].oLanguage.sDecimal !== '') {
31019                        splitRD = this.s.value[i].split(this.s.dt.settings()[0].oLanguage.sDecimal);
31020                    }
31021                    if (this.s.dt.settings()[0].oLanguage.sThousands !== '') {
31022                        for (var j = 0; j < splitRD.length; j++) {
31023                            splitRD[j] = splitRD[j].replace(this.s.dt.settings()[0].oLanguage.sThousands, ',');
31024                        }
31025                    }
31026                    this.s.value[i] = splitRD.join('.');
31027                }
31028            }
31029            else if (this.s.type !== null && deFormatDates) {
31030                if (this.s.type.includes('date') ||
31031                    this.s.type.includes('time')) {
31032                    for (var i = 0; i < this.s.value.length; i++) {
31033                        if (this.s.value[i].match(/^\d{4}-([0]\d|1[0-2])-([0-2]\d|3[01])$/g) === null) {
31034                            this.s.value[i] = '';
31035                        }
31036                    }
31037                }
31038                else if (this.s.type.includes('moment')) {
31039                    for (var i = 0; i < this.s.value.length; i++) {
31040                        if (this.s.value[i] &&
31041                            this.s.value[i].length > 0 &&
31042                            moment()(this.s.value[i], this.s.dateFormat, true).isValid()) {
31043                            this.s.value[i] = moment()(this.s.value[i], this.s.dateFormat).format('YYYY-MM-DD HH:mm:ss');
31044                        }
31045                    }
31046                }
31047                else if (this.s.type.includes('luxon')) {
31048                    for (var i = 0; i < this.s.value.length; i++) {
31049                        if (this.s.value[i] &&
31050                            this.s.value[i].length > 0 &&
31051                            luxon().DateTime.fromFormat(this.s.value[i], this.s.dateFormat).invalid === null) {
31052                            this.s.value[i] = luxon().DateTime.fromFormat(this.s.value[i], this.s.dateFormat).toFormat('yyyy-MM-dd HH:mm:ss');
31053                        }
31054                    }
31055                }
31056            }
31057            if (this.s.type.includes('num') && this.s.dt.page.info().serverSide) {
31058                for (var i = 0; i < this.s.value.length; i++) {
31059                    this.s.value[i] = this.s.value[i].replace(/[^0-9.\-]/g, '');
31060                }
31061            }
31062            return {
31063                condition: this.s.condition,
31064                data: this.s.data,
31065                origData: this.s.origData,
31066                type: this.s.type,
31067                value: this.s.value.map(function (a) { return a !== null && a !== undefined ? a.toString() : a; })
31068            };
31069        };
31070        /**
31071         * Getter for the node for the container of the criteria
31072         *
31073         * @returns JQuery<HTMLElement> the node for the container
31074         */
31075        Criteria.prototype.getNode = function () {
31076            return this.dom.container;
31077        };
31078        /**
31079         * Populates the criteria data, condition and value(s) as far as has been selected
31080         */
31081        Criteria.prototype.populate = function () {
31082            this._populateData();
31083            // If the column index has been found attempt to select a condition
31084            if (this.s.dataIdx !== -1) {
31085                this._populateCondition();
31086                // If the condittion has been found attempt to select the values
31087                if (this.s.condition !== undefined) {
31088                    this._populateValue();
31089                }
31090            }
31091        };
31092        /**
31093         * Rebuilds the criteria based upon the details passed in
31094         *
31095         * @param loadedCriteria the details required to rebuild the criteria
31096         */
31097        Criteria.prototype.rebuild = function (loadedCriteria) {
31098            // Check to see if the previously selected data exists, if so select it
31099            var foundData = false;
31100            var dataIdx;
31101            this._populateData();
31102            // If a data selection has previously been made attempt to find and select it
31103            if (loadedCriteria.data !== undefined) {
31104                var italic_1 = this.classes.italic;
31105                var data_1 = this.dom.data;
31106                this.dom.data.children('option').each(function () {
31107                    if (!foundData &&
31108                        ($$3(this).text() === loadedCriteria.data ||
31109                            loadedCriteria.origData && $$3(this).prop('origData') === loadedCriteria.origData)) {
31110                        $$3(this).prop('selected', true);
31111                        data_1.removeClass(italic_1);
31112                        foundData = true;
31113                        dataIdx = $$3(this).val();
31114                    }
31115                    else {
31116                        $$3(this).removeProp('selected');
31117                    }
31118                });
31119            }
31120            // If the data has been found and selected then the condition can be populated and searched
31121            if (foundData) {
31122                this.s.data = loadedCriteria.data;
31123                this.s.origData = loadedCriteria.origData;
31124                this.s.dataIdx = dataIdx;
31125                this.c.orthogonal = this._getOptions().orthogonal;
31126                this.dom.dataTitle.remove();
31127                this._populateCondition();
31128                this.dom.conditionTitle.remove();
31129                var condition = void 0;
31130                // Check to see if the previously selected condition exists, if so select it
31131                var options = this.dom.condition.children('option');
31132                // eslint-disable-next-line @typescript-eslint/prefer-for-of
31133                for (var i = 0; i < options.length; i++) {
31134                    var option = $$3(options[i]);
31135                    if (loadedCriteria.condition !== undefined &&
31136                        option.val() === loadedCriteria.condition &&
31137                        typeof loadedCriteria.condition === 'string') {
31138                        option.prop('selected', true);
31139                        condition = option.val();
31140                    }
31141                    else {
31142                        option.removeProp('selected');
31143                    }
31144                }
31145                this.s.condition = condition;
31146                // If the condition has been found and selected then the value can be populated and searched
31147                if (this.s.condition !== undefined) {
31148                    this.dom.conditionTitle.removeProp('selected');
31149                    this.dom.conditionTitle.remove();
31150                    this.dom.condition.removeClass(this.classes.italic);
31151                    // eslint-disable-next-line @typescript-eslint/prefer-for-of
31152                    for (var i = 0; i < options.length; i++) {
31153                        var option = $$3(options[i]);
31154                        if (option.val() !== this.s.condition) {
31155                            option.removeProp('selected');
31156                        }
31157                    }
31158                    this._populateValue(loadedCriteria);
31159                }
31160                else {
31161                    this.dom.conditionTitle.prependTo(this.dom.condition).prop('selected', true);
31162                }
31163            }
31164        };
31165        /**
31166         * Sets the listeners for the criteria
31167         */
31168        Criteria.prototype.setListeners = function () {
31169            var _this = this;
31170            this.dom.data
31171                .unbind('change')
31172                .on('change.dtsb', function () {
31173                _this.dom.dataTitle.removeProp('selected');
31174                // Need to go over every option to identify the correct selection
31175                var options = _this.dom.data.children('option.' + _this.classes.option);
31176                // eslint-disable-next-line @typescript-eslint/prefer-for-of
31177                for (var i = 0; i < options.length; i++) {
31178                    var option = $$3(options[i]);
31179                    if (option.val() === _this.dom.data.val()) {
31180                        _this.dom.data.removeClass(_this.classes.italic);
31181                        option.prop('selected', true);
31182                        _this.s.dataIdx = +option.val();
31183                        _this.s.data = option.text();
31184                        _this.s.origData = option.prop('origData');
31185                        _this.c.orthogonal = _this._getOptions().orthogonal;
31186                        // When the data is changed, the values in condition and
31187                        // value may also change so need to renew them
31188                        _this._clearCondition();
31189                        _this._clearValue();
31190                        _this._populateCondition();
31191                        // If this criteria was previously active in the search then
31192                        // remove it from the search and trigger a new search
31193                        if (_this.s.filled) {
31194                            _this.s.filled = false;
31195                            _this.s.dt.draw();
31196                            _this.setListeners();
31197                        }
31198                        _this.s.dt.state.save();
31199                    }
31200                    else {
31201                        option.removeProp('selected');
31202                    }
31203                }
31204            });
31205            this.dom.condition
31206                .unbind('change')
31207                .on('change.dtsb', function () {
31208                _this.dom.conditionTitle.removeProp('selected');
31209                // Need to go over every option to identify the correct selection
31210                var options = _this.dom.condition.children('option.' + _this.classes.option);
31211                // eslint-disable-next-line @typescript-eslint/prefer-for-of
31212                for (var i = 0; i < options.length; i++) {
31213                    var option = $$3(options[i]);
31214                    if (option.val() === _this.dom.condition.val()) {
31215                        _this.dom.condition.removeClass(_this.classes.italic);
31216                        option.prop('selected', true);
31217                        var condDisp = option.val();
31218                        // Find the condition that has been selected and store it internally
31219                        for (var _i = 0, _a = Object.keys(_this.s.conditions); _i < _a.length; _i++) {
31220                            var cond = _a[_i];
31221                            if (cond === condDisp) {
31222                                _this.s.condition = condDisp;
31223                                break;
31224                            }
31225                        }
31226                        // When the condition is changed, the value selector may switch between
31227                        // a select element and an input element
31228                        _this._clearValue();
31229                        _this._populateValue();
31230                        for (var _b = 0, _c = _this.dom.value; _b < _c.length; _b++) {
31231                            var val = _c[_b];
31232                            // If this criteria was previously active in the search then remove
31233                            // it from the search and trigger a new search
31234                            if (_this.s.filled && val !== undefined && _this.dom.inputCont.has(val[0]).length !== 0) {
31235                                _this.s.filled = false;
31236                                _this.s.dt.draw();
31237                                _this.setListeners();
31238                            }
31239                        }
31240                        if (_this.dom.value.length === 0 ||
31241                            _this.dom.value.length === 1 && _this.dom.value[0] === undefined) {
31242                            _this.s.dt.draw();
31243                        }
31244                    }
31245                    else {
31246                        option.removeProp('selected');
31247                    }
31248                }
31249            });
31250        };
31251        Criteria.prototype.setupButtons = function () {
31252            if (window.innerWidth > 550) {
31253                this.dom.container.removeClass(this.classes.vertical);
31254                this.dom.buttons.css('left', null);
31255                this.dom.buttons.css('top', null);
31256                return;
31257            }
31258            this.dom.container.addClass(this.classes.vertical);
31259            this.dom.buttons.css('left', this.dom.data.innerWidth());
31260            this.dom.buttons.css('top', this.dom.data.position().top);
31261        };
31262        /**
31263         * Builds the elements of the dom together
31264         */
31265        Criteria.prototype._buildCriteria = function () {
31266            // Append Titles for select elements
31267            this.dom.data.append(this.dom.dataTitle);
31268            this.dom.condition.append(this.dom.conditionTitle);
31269            // Add elements to container
31270            this.dom.container
31271                .append(this.dom.data)
31272                .append(this.dom.condition);
31273            this.dom.inputCont.empty();
31274            for (var _i = 0, _a = this.dom.value; _i < _a.length; _i++) {
31275                var val = _a[_i];
31276                val.append(this.dom.valueTitle);
31277                this.dom.inputCont.append(val);
31278            }
31279            // Add buttons to container
31280            this.dom.buttons
31281                .append(this.dom["delete"])
31282                .append(this.dom.right);
31283            this.dom.container.append(this.dom.inputCont).append(this.dom.buttons);
31284            this.setListeners();
31285        };
31286        /**
31287         * Clears the condition select element
31288         */
31289        Criteria.prototype._clearCondition = function () {
31290            this.dom.condition.empty();
31291            this.dom.conditionTitle.prop('selected', true).attr('disabled', 'true');
31292            this.dom.condition.prepend(this.dom.conditionTitle).prop('selectedIndex', 0);
31293            this.s.conditions = {};
31294            this.s.condition = undefined;
31295        };
31296        /**
31297         * Clears the value elements
31298         */
31299        Criteria.prototype._clearValue = function () {
31300            if (this.s.condition !== undefined) {
31301                if (this.dom.value.length > 0 && this.dom.value[0] !== undefined) {
31302                    var _loop_1 = function (val) {
31303                        if (val !== undefined) {
31304                            // Timeout is annoying but because of IOS
31305                            setTimeout(function () {
31306                                val.remove();
31307                            }, 50);
31308                        }
31309                    };
31310                    // Remove all of the value elements
31311                    for (var _i = 0, _a = this.dom.value; _i < _a.length; _i++) {
31312                        var val = _a[_i];
31313                        _loop_1(val);
31314                    }
31315                }
31316                // Call the init function to get the value elements for this condition
31317                this.dom.value = [].concat(this.s.conditions[this.s.condition].init(this, Criteria.updateListener));
31318                if (this.dom.value.length > 0 && this.dom.value[0] !== undefined) {
31319                    this.dom.inputCont
31320                        .empty()
31321                        .append(this.dom.value[0])
31322                        .insertAfter(this.dom.condition);
31323                    this.dom.value[0].trigger('dtsb-inserted');
31324                    // Insert all of the value elements
31325                    for (var i = 1; i < this.dom.value.length; i++) {
31326                        this.dom.inputCont.append(this.dom.value[i]);
31327                        this.dom.value[i].trigger('dtsb-inserted');
31328                    }
31329                }
31330            }
31331            else {
31332                var _loop_2 = function (val) {
31333                    if (val !== undefined) {
31334                        // Timeout is annoying but because of IOS
31335                        setTimeout(function () {
31336                            val.remove();
31337                        }, 50);
31338                    }
31339                };
31340                // Remove all of the value elements
31341                for (var _b = 0, _c = this.dom.value; _b < _c.length; _b++) {
31342                    var val = _c[_b];
31343                    _loop_2(val);
31344                }
31345                // Append the default valueTitle to the default select element
31346                this.dom.valueTitle
31347                    .prop('selected', true);
31348                this.dom.defaultValue
31349                    .append(this.dom.valueTitle)
31350                    .insertAfter(this.dom.condition);
31351            }
31352            this.s.value = [];
31353            this.dom.value = [
31354                $$3('<select disabled/>')
31355                    .addClass(this.classes.value)
31356                    .addClass(this.classes.dropDown)
31357                    .addClass(this.classes.italic)
31358                    .addClass(this.classes.select)
31359                    .append(this.dom.valueTitle.clone())
31360            ];
31361        };
31362        /**
31363         * Gets the options for the column
31364         *
31365         * @returns {object} The options for the column
31366         */
31367        Criteria.prototype._getOptions = function () {
31368            var table = this.s.dt;
31369            return $$3.extend(true, {}, Criteria.defaults, table.settings()[0].aoColumns[this.s.dataIdx].searchBuilder);
31370        };
31371        /**
31372         * Populates the condition dropdown
31373         */
31374        Criteria.prototype._populateCondition = function () {
31375            var conditionOpts = [];
31376            var conditionsLength = Object.keys(this.s.conditions).length;
31377            var colInits = this.s.dt.settings()[0].aoColumns;
31378            var column = +this.dom.data.children('option:selected').val();
31379            // If there are no conditions stored then we need to get them from the appropriate type
31380            if (conditionsLength === 0) {
31381                this.s.type = this.s.dt.columns().type().toArray()[column];
31382                if (colInits !== undefined) {
31383                    var colInit = colInits[column];
31384                    if (colInit.searchBuilderType !== undefined && colInit.searchBuilderType !== null) {
31385                        this.s.type = colInit.searchBuilderType;
31386                    }
31387                    else if (this.s.type === undefined || this.s.type === null) {
31388                        this.s.type = colInit.sType;
31389                    }
31390                }
31391                // If the column type is still unknown, call a draw to try reading it again
31392                if (this.s.type === null || this.s.type === undefined) {
31393                    $$3.fn.dataTable.ext.oApi._fnColumnTypes(this.s.dt.settings()[0]);
31394                    this.s.type = this.s.dt.columns().type().toArray()[column];
31395                }
31396                // Enable the condition element
31397                this.dom.condition
31398                    .removeAttr('disabled')
31399                    .empty()
31400                    .append(this.dom.conditionTitle)
31401                    .addClass(this.classes.italic);
31402                this.dom.conditionTitle
31403                    .prop('selected', true);
31404                var decimal = this.s.dt.settings()[0].oLanguage.sDecimal;
31405                // This check is in place for if a custom decimal character is in place
31406                if (decimal !== '' && this.s.type.indexOf(decimal) === this.s.type.length - decimal.length) {
31407                    if (this.s.type.includes('num-fmt')) {
31408                        this.s.type = this.s.type.replace(decimal, '');
31409                    }
31410                    else if (this.s.type.includes('num')) {
31411                        this.s.type = this.s.type.replace(decimal, '');
31412                    }
31413                }
31414                // Select which conditions are going to be used based on the column type
31415                var conditionObj = this.c.conditions[this.s.type] !== undefined ?
31416                    this.c.conditions[this.s.type] :
31417                    this.s.type.includes('moment') ?
31418                        this.c.conditions.moment :
31419                        this.s.type.includes('luxon') ?
31420                            this.c.conditions.luxon :
31421                            this.c.conditions.string;
31422                // If it is a moment format then extract the date format
31423                if (this.s.type.includes('moment')) {
31424                    this.s.dateFormat = this.s.type.replace(/moment-/g, '');
31425                }
31426                else if (this.s.type.includes('luxon')) {
31427                    this.s.dateFormat = this.s.type.replace(/luxon-/g, '');
31428                }
31429                // Add all of the conditions to the select element
31430                for (var _i = 0, _a = Object.keys(conditionObj); _i < _a.length; _i++) {
31431                    var condition = _a[_i];
31432                    if (conditionObj[condition] !== null) {
31433                        // Serverside processing does not supply the options for the select elements
31434                        // Instead input elements need to be used for these instead
31435                        if (this.s.dt.page.info().serverSide && conditionObj[condition].init === Criteria.initSelect) {
31436                            var col = colInits[column];
31437                            if (this.s.serverData && this.s.serverData[col.data]) {
31438                                conditionObj[condition].init = Criteria.initSelectSSP;
31439                                conditionObj[condition].inputValue = Criteria.inputValueSelect;
31440                                conditionObj[condition].isInputValid = Criteria.isInputValidSelect;
31441                            }
31442                            else {
31443                                conditionObj[condition].init = Criteria.initInput;
31444                                conditionObj[condition].inputValue = Criteria.inputValueInput;
31445                                conditionObj[condition].isInputValid = Criteria.isInputValidInput;
31446                            }
31447                        }
31448                        this.s.conditions[condition] = conditionObj[condition];
31449                        var condName = conditionObj[condition].conditionName;
31450                        if (typeof condName === 'function') {
31451                            condName = condName(this.s.dt, this.c.i18n);
31452                        }
31453                        conditionOpts.push($$3('<option>', {
31454                            text: condName,
31455                            value: condition
31456                        })
31457                            .addClass(this.classes.option)
31458                            .addClass(this.classes.notItalic));
31459                    }
31460                }
31461            }
31462            // Otherwise we can just load them in
31463            else if (conditionsLength > 0) {
31464                this.dom.condition.empty().removeAttr('disabled').addClass(this.classes.italic);
31465                for (var _b = 0, _c = Object.keys(this.s.conditions); _b < _c.length; _b++) {
31466                    var condition = _c[_b];
31467                    var condName = this.s.conditions[condition].conditionName;
31468                    if (typeof condName === 'function') {
31469                        condName = condName(this.s.dt, this.c.i18n);
31470                    }
31471                    var newOpt = $$3('<option>', {
31472                        text: condName,
31473                        value: condition
31474                    })
31475                        .addClass(this.classes.option)
31476                        .addClass(this.classes.notItalic);
31477                    if (this.s.condition !== undefined && this.s.condition === condName) {
31478                        newOpt.prop('selected', true);
31479                        this.dom.condition.removeClass(this.classes.italic);
31480                    }
31481                    conditionOpts.push(newOpt);
31482                }
31483            }
31484            else {
31485                this.dom.condition
31486                    .attr('disabled', 'true')
31487   
31487                 .addClass(this.classes.italic);
31488                return;
31489            }
31490            for (var _d = 0, conditionOpts_1 = conditionOpts; _d < conditionOpts_1.length; _d++) {
31491                var opt = conditionOpts_1[_d];
31492                this.dom.condition.append(opt);
31493            }
31494            // Selecting a default condition if one is set
31495            if (colInits[column].searchBuilder && colInits[column].searchBuilder.defaultCondition) {
31496                var defaultCondition = colInits[column].searchBuilder.defaultCondition;
31497                // If it is a number just use it as an index
31498                if (typeof defaultCondition === 'number') {
31499                    this.dom.condition.prop('selectedIndex', defaultCondition);
31500                    this.dom.condition.trigger('change');
31501                }
31502                // If it is a string then things get slightly more tricly
31503                else if (typeof defaultCondition === 'string') {
31504                    // We need to check each condition option to see if any will match
31505                    for (var i = 0; i < conditionOpts.length; i++) {
31506                        // Need to check against the stored conditions so we can match the token "cond" to the option
31507                        for (var _e = 0, _f = Object.keys(this.s.conditions); _e < _f.length; _e++) {
31508                            var cond = _f[_e];
31509                            var condName = this.s.conditions[cond].conditionName;
31510                            if (
31511                            // If the conditionName matches the text of the option
31512                            (typeof condName === 'string' ? condName : condName(this.s.dt, this.c.i18n)) ===
31513                                conditionOpts[i].text() &&
31514                                // and the tokens match
31515                                cond === defaultCondition) {
31516                                // Select that option
31517                                this.dom.condition
31518                                    .prop('selectedIndex', this.dom.condition.children().toArray().indexOf(conditionOpts[i][0]))
31519                                    .removeClass(this.classes.italic);
31520                                this.dom.condition.trigger('change');
31521                                i = conditionOpts.length;
31522                                break;
31523                            }
31524                        }
31525                    }
31526                }
31527            }
31528            // If not default set then default to 0, the title
31529            else {
31530                this.dom.condition.prop('selectedIndex', 0);
31531            }
31532        };
31533        /**
31534         * Populates the data / column select element
31535         */
31536        Criteria.prototype._populateData = function () {
31537            var columns = this.s.dt.settings()[0].aoColumns;
31538            var includeColumns = this.s.dt.columns(this.c.columns).indexes().toArray();
31539            this.dom.data.empty().append(this.dom.dataTitle);
31540            for (var index = 0; index < columns.length; index++) {
31541                // Need to check that the column can be filtered on before adding it
31542                if (this.c.columns === true || includeColumns.includes(index)) {
31543                    var col = columns[index];
31544                    var opt = {
31545                        index: index,
31546                        origData: col.data,
31547                        text: (col.searchBuilderTitle || col.sTitle)
31548                            .replace(/(<([^>]+)>)/ig, '')
31549                    };
31550                    this.dom.data.append($$3('<option>', {
31551                        text: opt.text,
31552                        value: opt.index
31553                    })
31554                        .addClass(this.classes.option)
31555                        .addClass(this.classes.notItalic)
31556                        .prop('origData', col.data)
31557                        .prop('selected', this.s.dataIdx === opt.index ? true : false));
31558                    if (this.s.dataIdx === opt.index) {
31559                        this.dom.dataTitle.removeProp('selected');
31560                    }
31561                }
31562            }
31563        };
31564        /**
31565         * Populates the Value select element
31566         *
31567         * @param loadedCriteria optional, used to reload criteria from predefined filters
31568         */
31569        Criteria.prototype._populateValue = function (loadedCriteria) {
31570            var _this = this;
31571            var prevFilled = this.s.filled;
31572            this.s.filled = false;
31573            // Remove any previous value elements
31574            // Timeout is annoying but because of IOS
31575            setTimeout(function () {
31576                _this.dom.defaultValue.remove();
31577            }, 50);
31578            var _loop_3 = function (val) {
31579                // Timeout is annoying but because of IOS
31580                setTimeout(function () {
31581                    if (val !== undefined) {
31582                        val.remove();
31583                    }
31584                }, 50);
31585            };
31586            for (var _i = 0, _a = this.dom.value; _i < _a.length; _i++) {
31587                var val = _a[_i];
31588                _loop_3(val);
31589            }
31590            var children = this.dom.inputCont.children();
31591            if (children.length > 1) {
31592                // eslint-disable-next-line @typescript-eslint/prefer-for-of
31593                for (var i = 0; i < children.length; i++) {
31594                    $$3(children[i]).remove();
31595                }
31596            }
31597            // Find the column with the title matching the data for the criteria and take note of the index
31598            if (loadedCriteria !== undefined) {
31599                this.s.dt.columns().every(function (index) {
31600                    if (_this.s.dt.settings()[0].aoColumns[index].sTitle === loadedCriteria.data) {
31601                        _this.s.dataIdx = index;
31602                    }
31603                });
31604            }
31605            // Initialise the value elements based on the condition
31606            this.dom.value = [].concat(this.s.conditions[this.s.condition].init(this, Criteria.updateListener, loadedCriteria !== undefined ? loadedCriteria.value : undefined));
31607            if (loadedCriteria !== undefined && loadedCriteria.value !== undefined) {
31608                this.s.value = loadedCriteria.value;
31609            }
31610            this.dom.inputCont.empty();
31611            // Insert value elements and trigger the inserted event
31612            if (this.dom.value[0] !== undefined) {
31613                this.dom.value[0]
31614                    .appendTo(this.dom.inputCont)
31615                    .trigger('dtsb-inserted');
31616            }
31617            for (var i = 1; i < this.dom.value.length; i++) {
31618                this.dom.value[i]
31619                    .insertAfter(this.dom.value[i - 1])
31620                    .trigger('dtsb-inserted');
31621            }
31622            // Check if the criteria can be used in a search
31623            this.s.filled = this.s.conditions[this.s.condition].isInputValid(this.dom.value, this);
31624            this.setListeners();
31625            // If it can and this is different to before then trigger a draw
31626            if (!this.s.preventRedraw && prevFilled !== this.s.filled) {
31627                // If using SSP we want to restrict the amount of server calls that take place
31628                //  and this will already have taken place
31629                if (!this.s.dt.page.info().serverSide) {
31630                    this.s.dt.draw();
31631                }
31632                this.setListeners();
31633            }
31634        };
31635        /**
31636         * Provides throttling capabilities to SearchBuilder without having to use dt's _fnThrottle function
31637         * This is because that function is not quite suitable for our needs as it runs initially rather than waiting
31638         *
31639         * @param args arguments supplied to the throttle function
31640         * @returns Function that is to be run that implements the throttling
31641         */
31642        Criteria.prototype._throttle = function (fn, frequency) {
31643            if (frequency === void 0) { frequency = 200; }
31644            var last = null;
31645            var timer = null;
31646            var that = this;
31647            if (frequency === null) {
31648                frequency = 200;
31649            }
31650            return function () {
31651                var args = [];
31652                for (var _i = 0; _i < arguments.length; _i++) {
31653                    args[_i] = arguments[_i];
31654                }
31655                var now = +new Date();
31656                if (last !== null && now < last + frequency) {
31657                    clearTimeout(timer);
31658                }
31659                else {
31660                    last = now;
31661                }
31662                timer = setTimeout(function () {
31663                    last = null;
31664                    fn.apply(that, args);
31665                }, frequency);
31666            };
31667        };
31668        Criteria.version = '1.1.0';
31669        Criteria.classes = {
31670            button: 'dtsb-button',
31671            buttonContainer: 'dtsb-buttonContainer',
31672            condition: 'dtsb-condition',
31673            container: 'dtsb-criteria',
31674            data: 'dtsb-data',
31675            "delete": 'dtsb-delete',
31676            dropDown: 'dtsb-dropDown',
31677            greyscale: 'dtsb-greyscale',
31678            input: 'dtsb-input',
31679            inputCont: 'dtsb-inputCont',
31680            italic: 'dtsb-italic',
31681            joiner: 'dtsp-joiner',
31682            left: 'dtsb-left',
31683            notItalic: 'dtsb-notItalic',
31684            option: 'dtsb-option',
31685            right: 'dtsb-right',
31686            select: 'dtsb-select',
31687            value: 'dtsb-value',
31688            vertical: 'dtsb-vertical'
31689        };
31690        /**
31691         * Default initialisation function for select conditions
31692         */
31693        Criteria.initSelect = function (that, fn, preDefined, array) {
31694            if (preDefined === void 0) { preDefined = null; }
31695            if (array === void 0) { array = false; }
31696            var column = that.dom.data.children('option:selected').val();
31697            var indexArray = that.s.dt.rows().indexes().toArray();
31698            var settings = that.s.dt.settings()[0];
31699            that.dom.valueTitle.prop('selected', true);
31700            // Declare select element to be used with all of the default classes and listeners.
31701            var el = $$3('<select/>')
31702                .addClass(Criteria.classes.value)
31703                .addClass(Criteria.classes.dropDown)
31704                .addClass(Criteria.classes.italic)
31705                .addClass(Criteria.classes.select)
31706                .append(that.dom.valueTitle)
31707                .on('change.dtsb', function () {
31708                $$3(this).removeClass(Criteria.classes.italic);
31709                fn(that, this);
31710            });
31711            if (that.c.greyscale) {
31712                el.addClass(Criteria.classes.greyscale);
31713            }
31714            var added = [];
31715            var options = [];
31716            // Add all of the options from the table to the select element.
31717            // Only add one option for each possible value
31718            for (var _i = 0, indexArray_1 = indexArray; _i < indexArray_1.length; _i++) {
31719                var index = indexArray_1[_i];
31720                var filter = settings.oApi._fnGetCellData(settings, index, column, typeof that.c.orthogonal === 'string' ?
31721                    that.c.orthogonal :
31722                    that.c.orthogonal.search);
31723                var value = {
31724                    filter: typeof filter === 'string' ?
31725                        filter.replace(/[\r\n\u2028]/g, ' ') : // Need to replace certain characters to match search values
31726                        filter,
31727                    index: index,
31728                    text: settings.oApi._fnGetCellData(settings, index, column, typeof that.c.orthogonal === 'string' ?
31729                        that.c.orthogonal :
31730                        that.c.orthogonal.display)
31731                };
31732                // If we are dealing with an array type, either make sure we are working with arrays, or sort them
31733                if (that.s.type === 'array') {
31734                    value.filter = !Array.isArray(value.filter) ? [value.filter] : value.filter;
31735                    value.text = !Array.isArray(value.text) ? [value.text] : value.text;
31736                }
31737                // Function to add an option to the select element
31738                var addOption = function (filt, text) {
31739                    if (that.s.type.includes('html') && filt !== null && typeof filt === 'string') {
31740                        filt.replace(/(<([^>]+)>)/ig, '');
31741                    }
31742                    // Add text and value, stripping out any html if that is the column type
31743                    var opt = $$3('<option>', {
31744                        type: Array.isArray(filt) ? 'Array' : 'String',
31745                        value: filt
31746                    })
31747                        .data('sbv', filt)
31748                        .addClass(that.classes.option)
31749                        .addClass(that.classes.notItalic)
31750                        // Have to add the text this way so that special html characters are not escaped - &amp; etc.
31751                        .html(typeof text === 'string' ?
31752                        text.replace(/(<([^>]+)>)/ig, '') :
31753                        text);
31754                    var val = opt.val();
31755                    // Check that this value has not already been added
31756                    if (added.indexOf(val) === -1) {
31757                        added.push(val);
31758                        options.push(opt);
31759                        if (preDefined !== null && Array.isArray(preDefined[0])) {
31760                            preDefined[0] = preDefined[0].sort().join(',');
31761                        }
31762                        // If this value was previously selected as indicated by preDefined, then select it again
31763                        if (preDefined !== null && opt.val() === preDefined[0]) {
31764                            opt.prop('selected', true);
31765                            el.removeClass(Criteria.classes.italic);
31766                            that.dom.valueTitle.removeProp('selected');
31767                        }
31768                    }
31769                };
31770                // If this is to add the individual values within the array we need to loop over the array
31771                if (array) {
31772                    for (var i = 0; i < value.filter.length; i++) {
31773                        addOption(value.filter[i], value.text[i]);
31774                    }
31775                }
31776                // Otherwise the value that is in the cell is to be added
31777                else {
31778                    addOption(value.filter, Array.isArray(value.text) ? value.text.join(', ') : value.text);
31779                }
31780            }
31781            options.sort(function (a, b) {
31782                if (that.s.type === 'array' ||
31783                    that.s.type === 'string' ||
31784                    that.s.type === 'html') {
31785                    if (a.val() < b.val()) {
31786                        return -1;
31787                    }
31788                    else if (a.val() > b.val()) {
31789                        return 1;
31790                    }
31791                    else {
31792                        return 0;
31793                    }
31794                }
31795                else if (that.s.type === 'num' ||
31796                    that.s.type === 'html-num') {
31797                    if (+a.val().replace(/(<([^>]+)>)/ig, '') < +b.val().replace(/(<([^>]+)>)/ig, '')) {
31798                        return -1;
31799                    }
31800                    else if (+a.val().replace(/(<([^>]+)>)/ig, '') > +b.val().replace(/(<([^>]+)>)/ig, '')) {
31801                        return 1;
31802                    }
31803                    else {
31804                        return 0;
31805                    }
31806                }
31807                else if (that.s.type === 'num-fmt' || that.s.type === 'html-num-fmt') {
31808                    if (+a.val().replace(/[^0-9.]/g, '') < +b.val().replace(/[^0-9.]/g, '')) {
31809                        return -1;
31810                    }
31811                    else if (+a.val().replace(/[^0-9.]/g, '') > +b.val().replace(/[^0-9.]/g, '')) {
31812                        return 1;
31813                    }
31814                    else {
31815                        return 0;
31816                    }
31817                }
31818            });
31819            for (var _a = 0, options_1 = options; _a < options_1.length; _a++) {
31820                var opt = options_1[_a];
31821                el.append(opt);
31822            }
31823            return el;
31824        };
31825        /**
31826         * Default initialisation function for select conditions
31827         */
31828        Criteria.initSelectSSP = function (that, fn, preDefined) {
31829            if (preDefined === void 0) { preDefined = null; }
31830            that.dom.valueTitle.prop('selected', true);
31831            // Declare select element to be used with all of the default classes and listeners.
31832            var el = $$3('<select/>')
31833                .addClass(Criteria.classes.value)
31834                .addClass(Criteria.classes.dropDown)
31835                .addClass(Criteria.classes.italic)
31836                .addClass(Criteria.classes.select)
31837                .append(that.dom.valueTitle)
31838                .on('change.dtsb', function () {
31839                $$3(this).removeClass(Criteria.classes.italic);
31840                fn(that, this);
31841            });
31842            if (that.c.greyscale) {
31843                el.addClass(Criteria.classes.greyscale);
31844            }
31845            var options = [];
31846            for (var _i = 0, _a = that.s.serverData[that.s.origData]; _i < _a.length; _i++) {
31847                var option = _a[_i];
31848                var value = option.value;
31849                var label = option.label;
31850                // Function to add an option to the select element
31851                var addOption = function (filt, text) {
31852                    if (that.s.type.includes('html') && filt !== null && typeof filt === 'string') {
31853                        filt.replace(/(<([^>]+)>)/ig, '');
31854                    }
31855                    // Add text and value, stripping out any html if that is the column type
31856                    var opt = $$3('<option>', {
31857                        type: Array.isArray(filt) ? 'Array' : 'String',
31858                        value: filt
31859                    })
31860                        .data('sbv', filt)
31861                        .addClass(that.classes.option)
31862                        .addClass(that.classes.notItalic)
31863                        // Have to add the text this way so that special html characters are not escaped - &amp; etc.
31864                        .html(typeof text === 'string' ?
31865                        text.replace(/(<([^>]+)>)/ig, '') :
31866                        text);
31867                    options.push(opt);
31868                    // If this value was previously selected as indicated by preDefined, then select it again
31869                    if (preDefined !== null && opt.val() === preDefined[0]) {
31870                        opt.prop('selected', true);
31871                        el.removeClass(Criteria.classes.italic);
31872                        that.dom.valueTitle.removeProp('selected');
31873                    }
31874                };
31875                addOption(value, label);
31876            }
31877            for (var _b = 0, options_2 = options; _b < options_2.length; _b++) {
31878                var opt = options_2[_b];
31879                el.append(opt);
31880            }
31881            return el;
31882        };
31883        /**
31884         * Default initialisation function for select array conditions
31885         *
31886         * This exists because there needs to be different select functionality for contains/without and equals/not
31887         */
31888        Criteria.initSelectArray = function (that, fn, preDefined) {
31889            if (preDefined === void 0) { preDefined = null; }
31890            return Criteria.initSelect(that, fn, preDefined, true);
31891        };
31892        /**
31893         * Default initialisation function for input conditions
31894         */
31895        Criteria.initInput = function (that, fn, preDefined) {
31896            if (preDefined === void 0) { preDefined = null; }
31897            // Declare the input element
31898            var searchDelay = that.s.dt.settings()[0].searchDelay;
31899            var el = $$3('<input/>')
31900                .addClass(Criteria.classes.value)
31901                .addClass(Criteria.classes.input)
31902                .on('input.dtsb keypress.dtsb', that._throttle(function (e) {
31903                var code = e.keyCode || e.which;
31904                return fn(that, this, code);
31905            }, searchDelay === null ? 100 : searchDelay));
31906            if (that.c.greyscale) {
31907                el.addClass(Criteria.classes.greyscale);
31908            }
31909            // If there is a preDefined value then add it
31910            if (preDefined !== null) {
31911                el.val(preDefined[0]);
31912            }
31913            // This is add responsive functionality to the logic button without redrawing everything else
31914            that.s.dt.one('draw.dtsb', function () {
31915                that.s.topGroup.trigger('dtsb-redrawLogic');
31916            });
31917            return el;
31918        };
31919        /**
31920         * Default initialisation function for conditions requiring 2 inputs
31921         */
31922        Criteria.init2Input = function (that, fn, preDefined) {
31923            if (preDefined === void 0) { preDefined = null; }
31924            // Declare all of the necessary jQuery elements
31925            var searchDelay = that.s.dt.settings()[0].searchDelay;
31926            var els = [
31927                $$3('<input/>')
31928                    .addClass(Criteria.classes.value)
31929                    .addClass(Criteria.classes.input)
31930                    .on('input.dtsb keypress.dtsb', that._throttle(function (e) {
31931                    var code = e.keyCode || e.which;
31932                    return fn(that, this, code);
31933                }, searchDelay === null ? 100 : searchDelay)),
31934                $$3('<span>')
31935                    .addClass(that.classes.joiner)
31936                    .html(that.s.dt.i18n('searchBuilder.valueJoiner', that.c.i18n.valueJoiner)),
31937                $$3('<input/>')
31938                    .addClass(Criteria.classes.value)
31939                    .addClass(Criteria.classes.input)
31940                    .on('input.dtsb keypress.dtsb', that._throttle(function (e) {
31941                    var code = e.keyCode || e.which;
31942                    return fn(that, this, code);
31943                }, searchDelay === null ? 100 : searchDelay))
31944            ];
31945            if (that.c.greyscale) {
31946                els[0].addClass(Criteria.classes.greyscale);
31947                els[2].addClass(Criteria.classes.greyscale);
31948            }
31949            // If there is a preDefined value then add it
31950            if (preDefined !== null) {
31951                els[0].val(preDefined[0]);
31952                els[2].val(preDefined[1]);
31953            }
31954            // This is add responsive functionality to the logic button without redrawing everything else
31955            that.s.dt.one('draw.dtsb', function () {
31956                that.s.topGroup.trigger('dtsb-redrawLogic');
31957            });
31958            return els;
31959        };
31960        /**
31961         * Default initialisation function for date conditions
31962         */
31963        Criteria.initDate = function (that, fn, preDefined) {
31964            if (preDefined === void 0) { preDefined = null; }
31965            var searchDelay = that.s.dt.settings()[0].searchDelay;
31966            // Declare date element using DataTables dateTime plugin
31967            var el = $$3('<input/>')
31968                .addClass(Criteria.classes.value)
31969                .addClass(Criteria.classes.input)
31970                .dtDateTime({
31971                attachTo: 'input',
31972                format: that.s.dateFormat ? that.s.dateFormat : undefined
31973            })
31974                .on('change.dtsb', that._throttle(function () {
31975                return fn(that, this);
31976            }, searchDelay === null ? 100 : searchDelay))
31977                .on('input.dtsb keypress.dtsb', function (e) {
31978                that._throttle(function () {
31979                    var code = e.keyCode || e.which;
31980                    return fn(that, this, code);
31981                }, searchDelay === null ? 100 : searchDelay);
31982            });
31983            if (that.c.greyscale) {
31984                el.addClass(Criteria.classes.greyscale);
31985            }
31986            // If there is a preDefined value then add it
31987            if (preDefined !== null) {
31988                el.val(preDefined[0]);
31989            }
31990            // This is add responsive functionality to the logic button without redrawing everything else
31991            that.s.dt.one('draw.dtsb', function () {
31992                that.s.topGroup.trigger('dtsb-redrawLogic');
31993            });
31994            return el;
31995        };
31996        Criteria.initNoValue = function (that) {
31997            // This is add responsive functionality to the logic button without redrawing everything else
31998            that.s.dt.one('draw.dtsb', function () {
31999                that.s.topGroup.trigger('dtsb-redrawLogic');
32000            });
32001            return [];
32002        };
32003        Criteria.init2Date = function (that, fn, preDefined) {
32004            var _this = this;
32005            if (preDefined === void 0) { preDefined = null; }
32006            var searchDelay = that.s.dt.settings()[0].searchDelay;
32007            // Declare all of the date elements that are required using DataTables dateTime plugin
32008            var els = [
32009                $$3('<input/>')
32010                    .addClass(Criteria.classes.value)
32011                    .addClass(Criteria.classes.input)
32012                    .dt
32012DateTime({
32013                    attachTo: 'input',
32014                    format: that.s.dateFormat ? that.s.dateFormat : undefined
32015                })
32016                    .on('change.dtsb', searchDelay !== null ?
32017                    that.s.dt.settings()[0].oApi._fnThrottle(function () {
32018                        return fn(that, this);
32019                    }, searchDelay) :
32020                    function () {
32021                        fn(that, _this);
32022                    })
32023                    .on('input.dtsb keypress.dtsb', function (e) {
32024                    that.s.dt.settings()[0].oApi._fnThrottle(function () {
32025                        var code = e.keyCode || e.which;
32026                        return fn(that, this, code);
32027                    }, searchDelay === null ? 0 : searchDelay);
32028                }),
32029                $$3('<span>')
32030                    .addClass(that.classes.joiner)
32031                    .html(that.s.dt.i18n('searchBuilder.valueJoiner', that.c.i18n.valueJoiner)),
32032                $$3('<input/>')
32033                    .addClass(Criteria.classes.value)
32034                    .addClass(Criteria.classes.input)
32035                    .dtDateTime({
32036                    attachTo: 'input',
32037                    format: that.s.dateFormat ? that.s.dateFormat : undefined
32038                })
32039                    .on('change.dtsb', searchDelay !== null ?
32040                    that.s.dt.settings()[0].oApi._fnThrottle(function () {
32041                        return fn(that, this);
32042                    }, searchDelay) :
32043                    function () {
32044                        fn(that, _this);
32045                    })
32046                    .on('input.dtsb keypress.dtsb', !that.c.enterSearch &&
32047                    !(that.s.dt.settings()[0].oInit.search !== undefined &&
32048                        that.s.dt.settings()[0].oInit.search["return"]) &&
32049                    searchDelay !== null ?
32050                    that.s.dt.settings()[0].oApi._fnThrottle(function () {
32051                        return fn(that, this);
32052                    }, searchDelay) :
32053                    function (e) {
32054                        var code = e.keyCode || e.which;
32055                        fn(that, _this, code);
32056                    })
32057            ];
32058            if (that.c.greyscale) {
32059                els[0].addClass(Criteria.classes.greyscale);
32060                els[2].addClass(Criteria.classes.greyscale);
32061            }
32062            // If there are and preDefined values then add them
32063            if (preDefined !== null && preDefined.length > 0) {
32064                els[0].val(preDefined[0]);
32065                els[2].val(preDefined[1]);
32066            }
32067            // This is add responsive functionality to the logic button without redrawing everything else
32068            that.s.dt.one('draw.dtsb', function () {
32069                that.s.topGroup.trigger('dtsb-redrawLogic');
32070            });
32071            return els;
32072        };
32073        /**
32074         * Default function for select elements to validate condition
32075         */
32076        Criteria.isInputValidSelect = function (el) {
32077            var allFilled = true;
32078            // Check each element to make sure that the selections are valid
32079            for (var _i = 0, el_1 = el; _i < el_1.length; _i++) {
32080                var element = el_1[_i];
32081                if (element.children('option:selected').length ===
32082                    element.children('option').length -
32083                        element.children('option.' + Criteria.classes.notItalic).length &&
32084                    element.children('option:selected').length === 1 &&
32085                    element.children('option:selected')[0] === element.children('option')[0]) {
32086                    allFilled = false;
32087                }
32088            }
32089            return allFilled;
32090        };
32091        /**
32092         * Default function for input and date elements to validate condition
32093         */
32094        Criteria.isInputValidInput = function (el) {
32095            var allFilled = true;
32096            // Check each element to make sure that the inputs are valid
32097            for (var _i = 0, el_2 = el; _i < el_2.length; _i++) {
32098                var element = el_2[_i];
32099                if (element.is('input') && element.val().length === 0) {
32100                    allFilled = false;
32101                }
32102            }
32103            return allFilled;
32104        };
32105        /**
32106         * Default function for getting select conditions
32107         */
32108        Criteria.inputValueSelect = function (el) {
32109            var values = [];
32110            // Go through the select elements and push each selected option to the return array
32111            for (var _i = 0, el_3 = el; _i < el_3.length; _i++) {
32112                var element = el_3[_i];
32113                if (element.is('select')) {
32114                    values.push(Criteria._escapeHTML(element.children('option:selected').data('sbv')));
32115                }
32116            }
32117            return values;
32118        };
32119        /**
32120         * Default function for getting input conditions
32121         */
32122        Criteria.inputValueInput = function (el) {
32123            var values = [];
32124            // Go through the input elements and push each value to the return array
32125            for (var _i = 0, el_4 = el; _i < el_4.length; _i++) {
32126                var element = el_4[_i];
32127                if (element.is('input')) {
32128                    values.push(Criteria._escapeHTML(element.val()));
32129                }
32130            }
32131            return values;
32132        };
32133        /**
32134         * Function that is run on each element as a call back when a search should be triggered
32135         */
32136        Criteria.updateListener = function (that, el, code) {
32137            // When the value is changed the criteria is now complete so can be included in searches
32138            // Get the condition from the map based on the key that has been selected for the condition
32139            var condition = that.s.conditions[that.s.condition];
32140            that.s.filled = condition.isInputValid(that.dom.value, that);
32141            that.s.value = condition.inputValue(that.dom.value, that);
32142            if (!that.s.filled) {
32143                if (!that.c.enterSearch &&
32144                    !(that.s.dt.settings()[0].oInit.search !== undefined &&
32145                        that.s.dt.settings()[0].oInit.search["return"]) ||
32146                    code === 13) {
32147                    that.s.dt.draw();
32148                }
32149                return;
32150            }
32151            if (!Array.isArray(that.s.value)) {
32152                that.s.value = [that.s.value];
32153            }
32154            for (var i = 0; i < that.s.value.length; i++) {
32155                // If the value is an array we need to sort it
32156                if (Array.isArray(that.s.value[i])) {
32157                    that.s.value[i].sort();
32158                }
32159                // Otherwise replace the decimal place character for i18n
32160                else if (that.s.type.includes('num') &&
32161                    (that.s.dt.settings()[0].oLanguage.sDecimal !== '' ||
32162                        that.s.dt.settings()[0].oLanguage.sThousands !== '')) {
32163                    var splitRD = [that.s.value[i].toString()];
32164                    if (that.s.dt.settings()[0].oLanguage.sDecimal !== '') {
32165                        splitRD = that.s.value[i].split(that.s.dt.settings()[0].oLanguage.sDecimal);
32166                    }
32167                    if (that.s.dt.settings()[0].oLanguage.sThousands !== '') {
32168                        for (var j = 0; j < splitRD.length; j++) {
32169                            splitRD[j] = splitRD[j].replace(that.s.dt.settings()[0].oLanguage.sThousands, ',');
32170                        }
32171                    }
32172                    that.s.value[i] = splitRD.join('.');
32173                }
32174            }
32175            // Take note of the cursor position so that we can refocus there later
32176            var idx = null;
32177            var cursorPos = null;
32178            for (var i = 0; i < that.dom.value.length; i++) {
32179                if (el === that.dom.value[i][0]) {
32180                    idx = i;
32181                    if (el.selectionStart !== undefined) {
32182                        cursorPos = el.selectionStart;
32183                    }
32184                }
32185            }
32186            if (!that.c.enterSearch &&
32187                !(that.s.dt.settings()[0].oInit.search !== undefined &&
32188                    that.s.dt.settings()[0].oInit.search["return"]) ||
32189                code === 13) {
32190                // Trigger a search
32191                that.s.dt.draw();
32192            }
32193            // Refocus the element and set the correct cursor position
32194            if (idx !== null) {
32195                that.dom.value[idx].removeClass(that.classes.italic);
32196                that.dom.value[idx].focus();
32197                if (cursorPos !== null) {
32198                    that.dom.value[idx][0].setSelectionRange(cursorPos, cursorPos);
32199                }
32200            }
32201        };
32202        // The order of the conditions will make eslint sad :(
32203        // Has to be in this order so that they are displayed correctly in select elements
32204        // Also have to disable member ordering for this as the private methods used are not yet declared otherwise
32205        // eslint-disable-next-line @typescript-eslint/member-ordering
32206        Criteria.dateConditions = {
32207            '=': {
32208                conditionName: function (dt, i18n) {
32209                    return dt.i18n('searchBuilder.conditions.date.equals', i18n.conditions.date.equals);
32210                },
32211                init: Criteria.initDate,
32212                inputValue: Criteria.inputValueInput,
32213                isInputValid: Criteria.isInputValidInput,
32214                search: function (value, comparison) {
32215                    value = value.replace(/(\/|-|,)/g, '-');
32216                    return value === comparison[0];
32217                }
32218            },
32219            // eslint-disable-next-line sort-keys
32220            '!=': {
32221                conditionName: function (dt, i18n) {
32222                    return dt.i18n('searchBuilder.conditions.date.not', i18n.conditions.date.not);
32223                },
32224                init: Criteria.initDate,
32225                inputValue: Criteria.inputValueInput,
32226                isInputValid: Criteria.isInputValidInput,
32227                search: function (value, comparison) {
32228                    value = value.replace(/(\/|-|,)/g, '-');
32229                    return value !== comparison[0];
32230                }
32231            },
32232            '<': {
32233                conditionName: function (dt, i18n) {
32234                    return dt.i18n('searchBuilder.conditions.date.before', i18n.conditions.date.before);
32235                },
32236                init: Criteria.initDate,
32237                inputValue: Criteria.inputValueInput,
32238                isInputValid: Criteria.isInputValidInput,
32239                search: function (value, comparison) {
32240                    value = value.replace(/(\/|-|,)/g, '-');
32241                    return value < comparison[0];
32242                }
32243            },
32244            '>': {
32245                conditionName: function (dt, i18n) {
32246                    return dt.i18n('searchBuilder.conditions.date.after', i18n.conditions.date.after);
32247                },
32248                init: Criteria.initDate,
32249                inputValue: Criteria.inputValueInput,
32250                isInputValid: Criteria.isInputValidInput,
32251                search: function (value, comparison) {
32252                    value = value.replace(/(\/|-|,)/g, '-');
32253                    return value > comparison[0];
32254                }
32255            },
32256            'between': {
32257                conditionName: function (dt, i18n) {
32258                    return dt.i18n('searchBuilder.conditions.date.between', i18n.conditions.date.between);
32259                },
32260                init: Criteria.init2Date,
32261                inputValue: Criteria.inputValueInput,
32262                isInputValid: Criteria.isInputValidInput,
32263                search: function (value, comparison) {
32264                    value = value.replace(/(\/|-|,)/g, '-');
32265                    if (comparison[0] < comparison[1]) {
32266                        return comparison[0] <= value && value <= comparison[1];
32267                    }
32268                    else {
32269                        return comparison[1] <= value && value <= comparison[0];
32270                    }
32271                }
32272            },
32273            // eslint-disable-next-line sort-keys
32274            '!between': {
32275                conditionName: function (dt, i18n) {
32276                    return dt.i18n('searchBuilder.conditions.date.notBetween', i18n.conditions.date.notBetween);
32277                },
32278                init: Criteria.init2Date,
32279                inputValue: Criteria.inputValueInput,
32280                isInputValid: Criteria.isInputValidInput,
32281                search: function (value, comparison) {
32282                    value = value.replace(/(\/|-|,)/g, '-');
32283                    if (comparison[0] < comparison[1]) {
32284                        return !(comparison[0] <= value && value <= comparison[1]);
32285                    }
32286                    else {
32287                        return !(comparison[1] <= value && value <= comparison[0]);
32288                    }
32289                }
32290            },
32291            'null': {
32292                conditionName: function (dt, i18n) {
32293                    return dt.i18n('searchBuilder.conditions.date.empty', i18n.conditions.date.empty);
32294                },
32295                init: Criteria.initNoValue,
32296                inputValue: function () {
32297                    return;
32298                },
32299                isInputValid: function () {
32300                    return true;
32301                },
32302                search: function (value) {
32303                    return value === null || value === undefined || value.length === 0;
32304                }
32305            },
32306            // eslint-disable-next-line sort-keys
32307            '!null': {
32308                conditionName: function (dt, i18n) {
32309                    return dt.i18n('searchBuilder.conditions.date.notEmpty', i18n.conditions.date.notEmpty);
32310                },
32311                init: Criteria.initNoValue,
32312                inputValue: function () {
32313                    return;
32314                },
32315                isInputValid: function () {
32316                    return true;
32317                },
32318                search: function (value) {
32319                    return !(value === null || value === undefined || value.length === 0);
32320                }
32321            }
32322        };
32323        // The order of the conditions will make eslint sad :(
32324        // Has to be in this order so that they are displayed correctly in select elements
32325        // Also have to disable member ordering for this as the private methods used are not yet declared otherwise
32326        // eslint-disable-next-line @typescript-eslint/member-ordering
32327        Criteria.momentDateConditions = {
32328            '=': {
32329                conditionName: function (dt, i18n) {
32330                    return dt.i18n('searchBuilder.conditions.date.equals', i18n.conditions.date.equals);
32331                },
32332                init: Criteria.initDate,
32333                inputValue: Criteria.inputValueInput,
32334                isInputValid: Criteria.isInputValidInput,
32335                search: function (value, comparison, that) {
32336                    return moment()(value, that.s.dateFormat).valueOf() ===
32337                        moment()(comparison[0], that.s.dateFormat).valueOf();
32338                }
32339            },
32340            // eslint-disable-next-line sort-keys
32341            '!=': {
32342                conditionName: function (dt, i18n) {
32343                    return dt.i18n('searchBuilder.conditions.date.not', i18n.conditions.date.not);
32344                },
32345                init: Criteria.initDate,
32346                inputValue: Criteria.inputValueInput,
32347                isInputValid: Criteria.isInputValidInput,
32348                search: function (value, comparison, that) {
32349                    return moment()(value, that.s.dateFormat).valueOf() !==
32350                        moment()(comparison[0], that.s.dateFormat).valueOf();
32351                }
32352            },
32353            '<': {
32354                conditionName: function (dt, i18n) {
32355                    return dt.i18n('searchBuilder.conditions.date.before', i18n.conditions.date.before);
32356                },
32357                init: Criteria.initDate,
32358                inputValue: Criteria.inputValueInput,
32359                isInputValid: Criteria.isInputValidInput,
32360                search: function (value, comparison, that) {
32361                    return moment()(value, that.s.dateFormat).valueOf() < moment()(comparison[0], that.s.dateFormat).valueOf();
32362                }
32363            },
32364            '>': {
32365                conditionName: function (dt, i18n) {
32366                    return dt.i18n('searchBuilder.conditions.date.after', i18n.conditions.date.after);
32367                },
32368                init: Criteria.initDate,
32369                inputValue: Criteria.inputValueInput,
32370                isInputValid: Criteria.isInputValidInput,
32371                search: function (value, comparison, that) {
32372                    return moment()(value, that.s.dateFormat).valueOf() > moment()(comparison[0], that.s.dateFormat).valueOf();
32373                }
32374            },
32375            'between': {
32376                conditionName: function (dt, i18n) {
32377                    return dt.i18n('searchBuilder.conditions.date.between', i18n.conditions.date.between);
32378                },
32379                init: Criteria.init2Date,
32380                inputValue: Criteria.inputValueInput,
32381                isInputValid: Criteria.isInputValidInput,
32382                search: function (value, comparison, that) {
32383                    var val = moment()(value, that.s.dateFormat).valueOf();
32384                    var comp0 = moment()(comparison[0], that.s.dateFormat).valueOf();
32385                    var comp1 = moment()(comparison[1], that.s.dateFormat).valueOf();
32386                    if (comp0 < comp1) {
32387                        return comp0 <= val && val <= comp1;
32388                    }
32389                    else {
32390                        return comp1 <= val && val <= comp0;
32391                    }
32392                }
32393            },
32394            // eslint-disable-next-line sort-keys
32395            '!between': {
32396                conditionName: function (dt, i18n) {
32397                    return dt.i18n('searchBuilder.conditions.date.notBetween', i18n.conditions.date.notBetween);
32398                },
32399                init: Criteria.init2Date,
32400                inputValue: Criteria.inputValueInput,
32401                isInputValid: Criteria.isInputValidInput,
32402                search: function (value, comparison, that) {
32403                    var val = moment()(value, that.s.dateFormat).valueOf();
32404                    var comp0 = moment()(comparison[0], that.s.dateFormat).valueOf();
32405                    var comp1 = moment()(comparison[1], that.s.dateFormat).valueOf();
32406                    if (comp0 < comp1) {
32407                        return !(+comp0 <= +val && +val <= +comp1);
32408                    }
32409                    else {
32410                        return !(+comp1 <= +val && +val <= +comp0);
32411                    }
32412                }
32413            },
32414            'null': {
32415                conditionName: function (dt, i18n) {
32416                    return dt.i18n('searchBuilder.conditions.date.empty', i18n.conditions.date.empty);
32417                },
32418                init: Criteria.initNoValue,
32419                inputValue: function () {
32420                    return;
32421                },
32422                isInputValid: function () {
32423                    return true;
32424                },
32425                search: function (value) {
32426                    return value === null || value === undefined || value.length === 0;
32427                }
32428            },
32429            // eslint-disable-next-line sort-keys
32430            '!null': {
32431                conditionName: function (dt, i18n) {
32432                    return dt.i18n('searchBuilder.conditions.date.notEmpty', i18n.conditions.date.notEmpty);
32433                },
32434                init: Criteria.initNoValue,
32435                inputValue: function () {
32436                    return;
32437                },
32438                isInputValid: function () {
32439                    return true;
32440                },
32441                search: function (value) {
32442                    return !(value === null || value === undefined || value.length === 0);
32443                }
32444            }
32445        };
32446        // The order of the conditions will make eslint sad :(
32447        // Has to be in this order so that they are displayed correctly in select elements
32448        // Also have to disable member ordering for this as the private methods used are not yet declared otherwise
32449        // eslint-disable-next-line @typescript-eslint/member-ordering
32450        Criteria.luxonDateConditions = {
32451            '=': {
32452                conditionName: function (dt, i18n) {
32453                    return dt.i18n('searchBuilder.conditions.date.equals', i18n.conditions.date.equals);
32454                },
32455                init: Criteria.initDate,
32456                inputValue: Criteria.inputValueInput,
32457                isInputValid: Criteria.isInputValidInput,
32458                search: function (value, comparison, that) {
32459                    return luxon().DateTime.fromFormat(value, that.s.dateFormat).ts
32460                        === luxon().DateTime.fromFormat(comparison[0], that.s.dateFormat).ts;
32461                }
32462            },
32463            // eslint-disable-next-line sort-keys
32464            '!=': {
32465                conditionName: function (dt, i18n) {
32466                    return dt.i18n('searchBuilder.conditions.date.not', i18n.conditions.date.not);
32467                },
32468                init: Criteria.initDate,
32469                inputValue: Criteria.inputValueInput,
32470                isInputValid: Criteria.isInputValidInput,
32471                search: function (value, comparison, that) {
32472                    return luxon().DateTime.fromFormat(value, that.s.dateFormat).ts
32473                        !== luxon().DateTime.fromFormat(comparison[0], that.s.dateFormat).ts;
32474                }
32475            },
32476            '<': {
32477                conditionName: function (dt, i18n) {
32478                    return dt.i18n('searchBuilder.conditions.date.before', i18n.conditions.date.before);
32479                },
32480                init: Criteria.initDate,
32481                inputValue: Criteria.inputValueInput,
32482                isInputValid: Criteria.isInputValidInput,
32483                search: function (value, comparison, that) {
32484                    return luxon().DateTime.fromFormat(value, that.s.dateFormat).ts
32485                        < luxon().DateTime.fromFormat(comparison[0], that.s.dateFormat).ts;
32486                }
32487            },
32488            '>': {
32489                conditionName: function (dt, i18n) {
32490                    return dt.i18n('searchBuilder.conditions.date.after', i18n.conditions.date.after);
32491                },
32492                init: Criteria.initDate,
32493                inputValue: Criteria.inputValueInput,
32494                isInputValid: Criteria.isInputValidInput,
32495                search: function (value, comparison, that) {
32496                    return luxon().DateTime.fromFormat(value, that.s.dateFormat).ts
32496
32497                        > luxon().DateTime.fromFormat(comparison[0], that.s.dateFormat).ts;
32498                }
32499            },
32500            'between': {
32501                conditionName: function (dt, i18n) {
32502                    return dt.i18n('searchBuilder.conditions.date.between', i18n.conditions.date.between);
32503                },
32504                init: Criteria.init2Date,
32505                inputValue: Criteria.inputValueInput,
32506                isInputValid: Criteria.isInputValidInput,
32507                search: function (value, comparison, that) {
32508                    var val = luxon().DateTime.fromFormat(value, that.s.dateFormat).ts;
32509                    var comp0 = luxon().DateTime.fromFormat(comparison[0], that.s.dateFormat).ts;
32510                    var comp1 = luxon().DateTime.fromFormat(comparison[1], that.s.dateFormat).ts;
32511                    if (comp0 < comp1) {
32512                        return comp0 <= val && val <= comp1;
32513                    }
32514                    else {
32515                        return comp1 <= val && val <= comp0;
32516                    }
32517                }
32518            },
32519            // eslint-disable-next-line sort-keys
32520            '!between': {
32521                conditionName: function (dt, i18n) {
32522                    return dt.i18n('searchBuilder.conditions.date.notBetween', i18n.conditions.date.notBetween);
32523                },
32524                init: Criteria.init2Date,
32525                inputValue: Criteria.inputValueInput,
32526                isInputValid: Criteria.isInputValidInput,
32527                search: function (value, comparison, that) {
32528                    var val = luxon().DateTime.fromFormat(value, that.s.dateFormat).ts;
32529                    var comp0 = luxon().DateTime.fromFormat(comparison[0], that.s.dateFormat).ts;
32530                    var comp1 = luxon().DateTime.fromFormat(comparison[1], that.s.dateFormat).ts;
32531                    if (comp0 < comp1) {
32532                        return !(+comp0 <= +val && +val <= +comp1);
32533                    }
32534                    else {
32535                        return !(+comp1 <= +val && +val <= +comp0);
32536                    }
32537                }
32538            },
32539            'null': {
32540                conditionName: function (dt, i18n) {
32541                    return dt.i18n('searchBuilder.conditions.date.empty', i18n.conditions.date.empty);
32542                },
32543                init: Criteria.initNoValue,
32544                inputValue: function () {
32545                    return;
32546                },
32547                isInputValid: function () {
32548                    return true;
32549                },
32550                search: function (value) {
32551                    return value === null || value === undefined || value.length === 0;
32552                }
32553            },
32554            // eslint-disable-next-line sort-keys
32555            '!null': {
32556                conditionName: function (dt, i18n) {
32557                    return dt.i18n('searchBuilder.conditions.date.notEmpty', i18n.conditions.date.notEmpty);
32558                },
32559                init: Criteria.initNoValue,
32560                inputValue: function () {
32561                    return;
32562                },
32563                isInputValid: function () {
32564                    return true;
32565                },
32566                search: function (value) {
32567                    return !(value === null || value === undefined || value.length === 0);
32568                }
32569            }
32570        };
32571        // The order of the conditions will make eslint sad :(
32572        // Has to be in this order so that they are displayed correctly in select elements
32573        // Also have to disable member ordering for this as the private methods used are not yet declared otherwise
32574        // eslint-disable-next-line @typescript-eslint/member-ordering
32575        Criteria.numConditions = {
32576            '=': {
32577                conditionName: function (dt, i18n) {
32578                    return dt.i18n('searchBuilder.conditions.number.equals', i18n.conditions.number.equals);
32579                },
32580                init: Criteria.initSelect,
32581                inputValue: Criteria.inputValueSelect,
32582                isInputValid: Criteria.isInputValidSelect,
32583                search: function (value, comparison) {
32584                    return +value === +comparison[0];
32585                }
32586            },
32587            // eslint-disable-next-line sort-keys
32588            '!=': {
32589                conditionName: function (dt, i18n) {
32590                    return dt.i18n('searchBuilder.conditions.number.not', i18n.conditions.number.not);
32591                },
32592                init: Criteria.initSelect,
32593                inputValue: Criteria.inputValueSelect,
32594                isInputValid: Criteria.isInputValidSelect,
32595                search: function (value, comparison) {
32596                    return +value !== +comparison[0];
32597                }
32598            },
32599            '<': {
32600                conditionName: function (dt, i18n) {
32601                    return dt.i18n('searchBuilder.conditions.number.lt', i18n.conditions.number.lt);
32602                },
32603                init: Criteria.initInput,
32604                inputValue: Criteria.inputValueInput,
32605                isInputValid: Criteria.isInputValidInput,
32606                search: function (value, comparison) {
32607                    return +value < +comparison[0];
32608                }
32609            },
32610            '<=': {
32611                conditionName: function (dt, i18n) {
32612                    return dt.i18n('searchBuilder.conditions.number.lte', i18n.conditions.number.lte);
32613                },
32614                init: Criteria.initInput,
32615                inputValue: Criteria.inputValueInput,
32616                isInputValid: Criteria.isInputValidInput,
32617                search: function (value, comparison) {
32618                    return +value <= +comparison[0];
32619                }
32620            },
32621            '>=': {
32622                conditionName: function (dt, i18n) {
32623                    return dt.i18n('searchBuilder.conditions.number.gte', i18n.conditions.number.gte);
32624                },
32625                init: Criteria.initInput,
32626                inputValue: Criteria.inputValueInput,
32627                isInputValid: Criteria.isInputValidInput,
32628                search: function (value, comparison) {
32629                    return +value >= +comparison[0];
32630                }
32631            },
32632            // eslint-disable-next-line sort-keys
32633            '>': {
32634                conditionName: function (dt, i18n) {
32635                    return dt.i18n('searchBuilder.conditions.number.gt', i18n.conditions.number.gt);
32636                },
32637                init: Criteria.initInput,
32638                inputValue: Criteria.inputValueInput,
32639                isInputValid: Criteria.isInputValidInput,
32640                search: function (value, comparison) {
32641                    return +value > +comparison[0];
32642                }
32643            },
32644            'between': {
32645                conditionName: function (dt, i18n) {
32646                    return dt.i18n('searchBuilder.conditions.number.between', i18n.conditions.number.between);
32647                },
32648                init: Criteria.init2Input,
32649                inputValue: Criteria.inputValueInput,
32650                isInputValid: Criteria.isInputValidInput,
32651                search: function (value, comparison) {
32652                    if (+comparison[0] < +comparison[1]) {
32653                        return +comparison[0] <= +value && +value <= +comparison[1];
32654                    }
32655                    else {
32656                        return +comparison[1] <= +value && +value <= +comparison[0];
32657                    }
32658                }
32659            },
32660            // eslint-disable-next-line sort-keys
32661            '!between': {
32662                conditionName: function (dt, i18n) {
32663                    return dt.i18n('searchBuilder.conditions.number.notBetween', i18n.conditions.number.notBetween);
32664                },
32665                init: Criteria.init2Input,
32666                inputValue: Criteria.inputValueInput,
32667                isInputValid: Criteria.isInputValidInput,
32668                search: function (value, comparison) {
32669                    if (+comparison[0] < +comparison[1]) {
32670                        return !(+comparison[0] <= +value && +value <= +comparison[1]);
32671                    }
32672                    else {
32673                        return !(+comparison[1] <= +value && +value <= +comparison[0]);
32674                    }
32675                }
32676            },
32677            'null': {
32678                conditionName: function (dt, i18n) {
32679                    return dt.i18n('searchBuilder.conditions.number.empty', i18n.conditions.number.empty);
32680                },
32681                init: Criteria.initNoValue,
32682                inputValue: function () {
32683                    return;
32684                },
32685                isInputValid: function () {
32686                    return true;
32687                },
32688                search: function (value) {
32689                    return value === null || value === undefined || value.length === 0;
32690                }
32691            },
32692            // eslint-disable-next-line sort-keys
32693            '!null': {
32694                conditionName: function (dt, i18n) {
32695                    return dt.i18n('searchBuilder.conditions.number.notEmpty', i18n.conditions.number.notEmpty);
32696                },
32697                init: Criteria.initNoValue,
32698                inputValue: function () {
32699                    return;
32700                },
32701                isInputValid: function () {
32702                    return true;
32703                },
32704                search: function (value) {
32705                    return !(value === null || value === undefined || value.length === 0);
32706                }
32707            }
32708        };
32709        // The order of the conditions will make eslint sad :(
32710        // Has to be in this order so that they are displayed correctly in select elements
32711        // Also have to disable member ordering for this as the private methods used are not yet declared otherwise
32712        // eslint-disable-next-line @typescript-eslint/member-ordering
32713        Criteria.numFmtConditions = {
32714            '=': {
32715                conditionName: function (dt, i18n) {
32716                    return dt.i18n('searchBuilder.conditions.number.equals', i18n.conditions.number.equals);
32717                },
32718                init: Criteria.initSelect,
32719                inputValue: Criteria.inputValueSelect,
32720                isInputValid: Criteria.isInputValidSelect,
32721                search: function (value, comparison) {
32722                    return Criteria.parseNumFmt(value) === Criteria.parseNumFmt(comparison[0]);
32723                }
32724            },
32725            // eslint-disable-next-line sort-keys
32726            '!=': {
32727                conditionName: function (dt, i18n) {
32728                    return dt.i18n('searchBuilder.conditions.number.not', i18n.conditions.number.not);
32729                },
32730                init: Criteria.initSelect,
32731                inputValue: Criteria.inputValueSelect,
32732                isInputValid: Criteria.isInputValidSelect,
32733                search: function (value, comparison) {
32734                    return Criteria.parseNumFmt(value) !== Criteria.parseNumFmt(comparison[0]);
32735                }
32736            },
32737            '<': {
32738                conditionName: function (dt, i18n) {
32739                    return dt.i18n('searchBuilder.conditions.number.lt', i18n.conditions.number.lt);
32740                },
32741                init: Criteria.initInput,
32742                inputValue: Criteria.inputValueInput,
32743                isInputValid: Criteria.isInputValidInput,
32744                search: function (value, comparison) {
32745                    return Criteria.parseNumFmt(value) < Criteria.parseNumFmt(comparison[0]);
32746                }
32747            },
32748            '<=': {
32749                conditionName: function (dt, i18n) {
32750                    return dt.i18n('searchBuilder.conditions.number.lte', i18n.conditions.number.lte);
32751                },
32752                init: Criteria.initInput,
32753                inputValue: Criteria.inputValueInput,
32754                isInputValid: Criteria.isInputValidInput,
32755                search: function (value, comparison) {
32756                    return Criteria.parseNumFmt(value) <= Criteria.parseNumFmt(comparison[0]);
32757                }
32758            },
32759            '>=': {
32760                conditionName: function (dt, i18n) {
32761                    return dt.i18n('searchBuilder.conditions.number.gte', i18n.conditions.number.gte);
32762                },
32763                init: Criteria.initInput,
32764                inputValue: Criteria.inputValueInput,
32765                isInputValid: Criteria.isInputValidInput,
32766                search: function (value, comparison) {
32767                    return Criteria.parseNumFmt(value) >= Criteria.parseNumFmt(comparison[0]);
32768                }
32769            },
32770            // eslint-disable-next-line sort-keys
32771            '>': {
32772                conditionName: function (dt, i18n) {
32773                    return dt.i18n('searchBuilder.conditions.number.gt', i18n.conditions.number.gt);
32774                },
32775                init: Criteria.initInput,
32776                inputValue: Criteria.inputValueInput,
32777                isInputValid: Criteria.isInputValidInput,
32778                search: function (value, comparison) {
32779                    return Criteria.parseNumFmt(value) > Criteria.parseNumFmt(comparison[0]);
32780                }
32781            },
32782            'between': {
32783                conditionName: function (dt, i18n) {
32784                    return dt.i18n('searchBuilder.conditions.number.between', i18n.conditions.number.between);
32785                },
32786                init: Criteria.init2Input,
32787                inputValue: Criteria.inputValueInput,
32788                isInputValid: Criteria.isInputValidInput,
32789                search: function (value, comparison) {
32790                    var val = Criteria.parseNumFmt(value);
32791                    var comp0 = Criteria.parseNumFmt(comparison[0]);
32792                    var comp1 = Criteria.parseNumFmt(comparison[1]);
32793                    if (+comp0 < +comp1) {
32794                        return +comp0 <= +val && +val <= +comp1;
32795                    }
32796                    else {
32797                        return +comp1 <= +val && +val <= +comp0;
32798                    }
32799                }
32800            },
32801            // eslint-disable-next-line sort-keys
32802            '!between': {
32803                conditionName: function (dt, i18n) {
32804                    return dt.i18n('searchBuilder.conditions.number.notBetween', i18n.conditions.number.notBetween);
32805                },
32806                init: Criteria.init2Input,
32807                inputValue: Criteria.inputValueInput,
32808                isInputValid: Criteria.isInputValidInput,
32809                search: function (value, comparison) {
32810                    var val = Criteria.parseNumFmt(value);
32811                    var comp0 = Criteria.parseNumFmt(comparison[0]);
32812                    var comp1 = Criteria.parseNumFmt(comparison[1]);
32813                    if (+comp0 < +comp1) {
32814                        return !(+comp0 <= +val && +val <= +comp1);
32815                    }
32816                    else {
32817                        return !(+comp1 <= +val && +val <= +comp0);
32818                    }
32819                }
32820            },
32821            'null': {
32822                conditionName: function (dt, i18n) {
32823                    return dt.i18n('searchBuilder.conditions.number.empty', i18n.conditions.number.empty);
32824                },
32825                init: Criteria.initNoValue,
32826                inputValue: function () {
32827                    return;
32828                },
32829                isInputValid: function () {
32830                    return true;
32831                },
32832                search: function (value) {
32833                    return value === null || value === undefined || value.length === 0;
32834                }
32835            },
32836            // eslint-disable-next-line sort-keys
32837            '!null': {
32838                conditionName: function (dt, i18n) {
32839                    return dt.i18n('searchBuilder.conditions.number.notEmpty', i18n.conditions.number.notEmpty);
32840                },
32841                init: Criteria.in
32841itNoValue,
32842                inputValue: function () {
32843                    return;
32844                },
32845                isInputValid: function () {
32846                    return true;
32847                },
32848                search: function (value) {
32849                    return !(value === null || value === undefined || value.length === 0);
32850                }
32851            }
32852        };
32853        // The order of the conditions will make eslint sad :(
32854        // Has to be in this order so that they are displayed correctly in select elements
32855        // Also have to disable member ordering for this as the private methods used are not yet declared otherwise
32856        // eslint-disable-next-line @typescript-eslint/member-ordering
32857        Criteria.stringConditions = {
32858            '=': {
32859                conditionName: function (dt, i18n) {
32860                    return dt.i18n('searchBuilder.conditions.string.equals', i18n.conditions.string.equals);
32861                },
32862                init: Criteria.initSelect,
32863                inputValue: Criteria.inputValueSelect,
32864                isInputValid: Criteria.isInputValidSelect,
32865                search: function (value, comparison) {
32866                    return value === comparison[0];
32867                }
32868            },
32869            // eslint-disable-next-line sort-keys
32870            '!=': {
32871                conditionName: function (dt, i18n) {
32872                    return dt.i18n('searchBuilder.conditions.string.not', i18n.conditions.string.not);
32873                },
32874                init: Criteria.initSelect,
32875                inputValue: Criteria.inputValueSelect,
32876                isInputValid: Criteria.isInputValidInput,
32877                search: function (value, comparison) {
32878                    return value !== comparison[0];
32879                }
32880            },
32881            'starts': {
32882                conditionName: function (dt, i18n) {
32883                    return dt.i18n('searchBuilder.conditions.string.startsWith', i18n.conditions.string.startsWith);
32884                },
32885                init: Criteria.initInput,
32886                inputValue: Criteria.inputValueInput,
32887                isInputValid: Criteria.isInputValidInput,
32888                search: function (value, comparison) {
32889                    return value.toLowerCase().indexOf(comparison[0].toLowerCase()) === 0;
32890                }
32891            },
32892            // eslint-disable-next-line sort-keys
32893            '!starts': {
32894                conditionName: function (dt, i18n) {
32895                    return dt.i18n('searchBuilder.conditions.string.notStartsWith', i18n.conditions.string.notStartsWith);
32896                },
32897                init: Criteria.initInput,
32898                inputValue: Criteria.inputValueInput,
32899                isInputValid: Criteria.isInputValidInput,
32900                search: function (value, comparison) {
32901                    return value.toLowerCase().indexOf(comparison[0].toLowerCase()) !== 0;
32902                }
32903            },
32904            // eslint-disable-next-line sort-keys
32905            'contains': {
32906                conditionName: function (dt, i18n) {
32907                    return dt.i18n('searchBuilder.conditions.string.contains', i18n.conditions.string.contains);
32908                },
32909                init: Criteria.initInput,
32910                inputValue: Criteria.inputValueInput,
32911                isInputValid: Criteria.isInputValidInput,
32912                search: function (value, comparison) {
32913                    return value.toLowerCase().includes(comparison[0].toLowerCase());
32914                }
32915            },
32916            // eslint-disable-next-line sort-keys
32917            '!contains': {
32918                conditionName: function (dt, i18n) {
32919                    return dt.i18n('searchBuilder.conditions.string.notContains', i18n.conditions.string.notContains);
32920                },
32921                init: Criteria.initInput,
32922                inputValue: Criteria.inputValueInput,
32923                isInputValid: Criteria.isInputValidInput,
32924                search: function (value, comparison) {
32925                    return !value.toLowerCase().includes(comparison[0].toLowerCase());
32926                }
32927            },
32928            'ends': {
32929                conditionName: function (dt, i18n) {
32930                    return dt.i18n('searchBuilder.conditions.string.endsWith', i18n.conditions.string.endsWith);
32931                },
32932                init: Criteria.initInput,
32933                inputValue: Criteria.inputValueInput,
32934                isInputValid: Criteria.isInputValidInput,
32935                search: function (value, comparison) {
32936                    return value.toLowerCase().endsWith(comparison[0].toLowerCase());
32937                }
32938            },
32939            // eslint-disable-next-line sort-keys
32940            '!ends': {
32941                conditionName: function (dt, i18n) {
32942                    return dt.i18n('searchBuilder.conditions.string.notEndsWith', i18n.conditions.string.notEndsWith);
32943                },
32944                init: Criteria.initInput,
32945                inputValue: Criteria.inputValueInput,
32946                isInputValid: Criteria.isInputValidInput,
32947                search: function (value, comparison) {
32948                    return !value.toLowerCase().endsWith(comparison[0].toLowerCase());
32949                }
32950            },
32951            'null': {
32952                conditionName: function (dt, i18n) {
32953                    return dt.i18n('searchBuilder.conditions.string.empty', i18n.conditions.string.empty);
32954                },
32955                init: Criteria.initNoValue,
32956                inputValue: function () {
32957                    return;
32958                },
32959                isInputValid: function () {
32960                    return true;
32961                },
32962                search: function (value) {
32963                    return value === null || value === undefined || value.length === 0;
32964                }
32965            },
32966            // eslint-disable-next-line sort-keys
32967            '!null': {
32968                conditionName: function (dt, i18n) {
32969                    return dt.i18n('searchBuilder.conditions.string.notEmpty', i18n.conditions.string.notEmpty);
32970                },
32971                init: Criteria.initNoValue,
32972                inputValue: function () {
32973                    return;
32974                },
32975                isInputValid: function () {
32976                    return true;
32977                },
32978                search: function (value) {
32979                    return !(value === null || value === undefined || value.length === 0);
32980                }
32981            }
32982        };
32983        // The order of the conditions will make eslint sad :(
32984        // Also have to disable member ordering for this as the private methods used are not yet declared otherwise
32985        // eslint-disable-next-line @typescript-eslint/member-ordering
32986        Criteria.arrayConditions = {
32987            'contains': {
32988                conditionName: function (dt, i18n) {
32989                    return dt.i18n('searchBuilder.conditions.array.contains', i18n.conditions.array.contains);
32990                },
32991                init: Criteria.initSelectArray,
32992                inputValue: Criteria.inputValueSelect,
32993                isInputValid: Criteria.isInputValidSelect,
32994                search: function (value, comparison) {
32995                    return value.includes(comparison[0]);
32996                }
32997            },
32998            'without': {
32999                conditionName: function (dt, i18n) {
33000                    return dt.i18n('searchBuilder.conditions.array.without', i18n.conditions.array.without);
33001                },
33002                init: Criteria.initSelectArray,
33003                inputValue: Criteria.inputValueSelect,
33004                isInputValid: Criteria.isInputValidSelect,
33005                search: function (value, comparison) {
33006                    return value.indexOf(comparison[0]) === -1;
33007                }
33008            },
33009            // eslint-disable-next-line sort-keys
33010            '=': {
33011                conditionName: function (dt, i18n) {
33012                    return dt.i18n('searchBuilder.conditions.array.equals', i18n.conditions.array.equals);
33013                },
33014                init: Criteria.initSelect,
33015                inputValue: Criteria.inputValueSelect,
33016                isInputValid: Criteria.isInputValidSelect,
33017                search: function (value, comparison) {
33018                    if (value.length === comparison[0].length) {
33019                        for (var i = 0; i < value.length; i++) {
33020                            if (value[i] !== comparison[0][i]) {
33021                                return false;
33022                            }
33023                        }
33024                        return true;
33025                    }
33026                    return false;
33027                }
33028            },
33029            // eslint-disable-next-line sort-keys
33030            '!=': {
33031                conditionName: function (dt, i18n) {
33032                    return dt.i18n('searchBuilder.conditions.array.not', i18n.conditions.array.not);
33033                },
33034                init: Criteria.initSelect,
33035                inputValue: Criteria.inputValueSelect,
33036                isInputValid: Criteria.isInputValidSelect,
33037                search: function (value, comparison) {
33038                    if (value.length === comparison[0].length) {
33039                        for (var i = 0; i < value.length; i++) {
33040                            if (value[i] !== comparison[0][i]) {
33041                                return true;
33042                            }
33043                        }
33044                        return false;
33045                    }
33046                    return true;
33047                }
33048            },
33049            'null': {
33050                conditionName: function (dt, i18n) {
33051                    return dt.i18n('searchBuilder.conditions.array.empty', i18n.conditions.array.empty);
33052                },
33053                init: Criteria.initNoValue,
33054                inputValue: function () {
33055                    return;
33056                },
33057                isInputValid: function () {
33058                    return true;
33059                },
33060                search: function (value) {
33061                    return value === null || value === undefined || value.length === 0;
33062                }
33063            },
33064            // eslint-disable-next-line sort-keys
33065            '!null': {
33066                conditionName: function (dt, i18n) {
33067                    return dt.i18n('searchBuilder.conditions.array.notEmpty', i18n.conditions.array.notEmpty);
33068                },
33069                init: Criteria.initNoValue,
33070                inputValue: function () {
33071                    return;
33072                },
33073                isInputValid: function () {
33074                    return true;
33075                },
33076                search: function (value) {
33077                    return value !== null && value !== undefined && value.length !== 0;
33078                }
33079            }
33080        };
33081        // eslint will be sad because we have to disable member ordering for this as the
33082        // private static properties used are not yet declared otherwise
33083        // eslint-disable-next-line @typescript-eslint/member-ordering
33084        Criteria.defaults = {
33085            columns: true,
33086            conditions: {
33087                'array': Criteria.arrayConditions,
33088                'date': Criteria.dateConditions,
33089                'html': Criteria.stringConditions,
33090                'html-num': Criteria.numConditions,
33091                'html-num-fmt': Criteria.numFmtConditions,
33092                'luxon': Criteria.luxonDateConditions,
33093                'moment': Criteria.momentDateConditions,
33094                'num': Criteria.numConditions,
33095                'num-fmt': Criteria.numFmtConditions,
33096                'string': Criteria.stringConditions
33097            },
33098            depthLimit: false,
33099            enterSearch: false,
33100            filterChanged: undefined,
33101            greyscale: false,
33102            i18n: {
33103                add: 'Add Condition',
33104                button: {
33105                    0: 'Search Builder',
33106                    _: 'Search Builder (%d)'
33107                },
33108                clearAll: 'Clear All',
33109                condition: 'Condition',
33110                data: 'Data',
33111                "delete": '&times',
33112                deleteTitle: 'Delete filtering rule',
33113                left: '<',
33114                leftTitle: 'Outdent criteria',
33115                logicAnd: 'And',
33116                logicOr: 'Or',
33117                right: '>',
33118                rightTitle: 'Indent criteria',
33119                title: {
33120                    0: 'Custom Search Builder',
33121                    _: 'Custom Search Builder (%d)'
33122                },
33123                value: 'Value',
33124                valueJoiner: 'and'
33125            },
33126            logic: 'AND',
33127            orthogonal: {
33128                display: 'display',
33129                search: 'filter'
33130            },
33131            preDefined: false
33132        };
33133        return Criteria;
33134    }());
33135
33136    var $$2;
33137    var dataTable$2;
33138    /**
33139     * Sets the value of jQuery for use in the file
33140     *
33141     * @param jq the instance of jQuery to be set
33142     */
33143    function setJQuery$1(jq) {
33144        $$2 = jq;
33145        dataTable$2 = jq.fn.dataTable;
33146    }
33147    /**
33148     * The Group class is used within SearchBuilder to represent a group of criteria
33149     */
33150    var Group = /** @class */ (function () {
33151        function Group(table, opts, topGroup, index, isChild, depth, serverData) {
33152            if (index === void 0) { index = 0; }
33153            if (isChild === void 0) { isChild = false; }
33154            if (depth === void 0) { depth = 1; }
33155            if (serverData === void 0) { serverData = undefined; }
33156            // Check that the required version of DataTables is included
33157            if (!dataTable$2 || !dataTable$2.versionCheck || !dataTable$2.versionCheck('1.10.0')) {
33158                throw new Error('SearchBuilder requires DataTables 1.10 or newer');
33159            }
33160            this.classes = $$2.extend(true, {}, Group.classes);
33161            // Get options from user
33162            this.c = $$2.extend(true, {}, Group.defaults, opts);
33163            this.s = {
33164                criteria: [],
33165                depth: depth,
33166                dt: table,
33167                index: index,
33168                isChild: isChild,
33169                logic: undefined,
33170                opts: opts,
33171                preventRedraw: false,
33172                serverData: serverData,
33173                toDrop: undefined,
33174                topGroup: topGroup
33175            };
33176            this.dom = {
33177                add: $$2('<button/>')
33178                    .addClass(this.classes.add)
33179                    .addClass(this.classes.button)
33180                    .attr('type', 'button'),
33181                clear: $$2('<button>&times</button>')
33182                    .addClass(this.classes.button)
33183                    .addClass(this.classes.clearGroup)
33184                    .attr('type', 'button'),
33185                container: $$2('<div/>')
33186                    .addClass(this.classes.group),
33187                logic: $$2('<button><div/></button>')
33188                    .addClass(this.classes.logic)
33189                    .addClass(this.classes.button)
33190                    .attr('type', 'button'),
33191                logicContainer: $$2('<div/>')
33192                    .addClass(this.classes.logicContainer)
33193            };
33194            // A reference to the top level group is maintained throughout any subgroups and criteria that may be created
33195            if (this.s.topGroup === undefined) {
33196                this.s.topGroup = this.dom.container;
33197            }
33198            this._setup();
33199            return this;
33200        }
33201        /**
33202         * Destroys the groups buttons, clears the internal criteria and removes it from the dom
33203         */
33204        Group.prototype.destroy = function () {
33205            // Turn off listeners
33206            this.dom.add.off('.dtsb');
33207            this.dom.logic.off('.dtsb');
33208            // Trigger event for groups at a higher level to pick up on
33209            this.dom.container
33210                .trigger('dtsb-destroy')
33211                .remove();
33212            this.s.criteria = [];
33213        };
33214        /**
33215         * Gets the details required to rebuild the group
33216         */
33217        // Eslint upset at empty object but needs to be done
33218        // eslint-disable-next-line @typescript-eslint/ban-types
33219        Group.prototype.getDetails = function (deFormatDates) {
33220            if (deFormatDates === void 0) { deFormatDates = false; }
33221            if (this.s.criteria.length === 0) {
33222                return {};
33223            }
33224            var details = {
33225                criteria: [],
33226                logic: this.s.logic
33227            };
33228            // NOTE here crit could be either a subgroup or a criteria
33229            for (var _i = 0, _a = this.s.criteria; _i < _a.length; _i++) {
33230                var crit = _a[_i];
33231                details.criteria.push(crit.criteria.getDetails(deFormatDates));
33232            }
33233            return details;
33234        };
33235        /**
33236         * Getter for the node for the container of the group
33237         *
33238         * @returns Node for the container of the group
33239         */
33240        Group.prototype.getNode = function () {
33241            return this.dom.container;
33242        };
33243        /**
33244         * Rebuilds the group based upon the details passed in
33245         *
33246         * @param loadedDetails the details required to rebuild the group
33247         */
33248        Group.prototype.rebuild = function (loadedDetails) {
33249            // If no criteria are stored then just return
33250            if (loadedDetails.criteria === undefined ||
33251                loadedDetails.criteria === null ||
33252                Array.isArray(loadedDetails.criteria) && loadedDetails.criteria.length === 0) {
33253                return;
33254            }
33255            this.s.logic = loadedDetails.logic;
33256            this.dom.logic.children().first().html(this.s.logic === 'OR'
33257                ? this.s.dt.i18n('searchBuilder.logicOr', this.c.i18n.logicOr)
33258                : this.s.dt.i18n('searchBuilder.logicAnd', this.c.i18n.logicAnd));
33259            // Add all of the criteria, be it a sub group or a criteria
33260            if (Array.isArray(loadedDetails.criteria)) {
33261                for (var _i = 0, _a = loadedDetails.criteria; _i < _a.length; _i++) {
33262                    var crit = _a[_i];
33263                    if (crit.logic !== undefined) {
33264                        this._addPrevGroup(crit);
33265                    }
33266                    else if (crit.logic === undefined) {
33267                        this._addPrevCriteria(crit);
33268                    }
33269                }
33270            }
33271            // For all of the criteria children, update the arrows incase they require changing and set the listeners
33272            for (var _b = 0, _c = this.s.criteria; _b < _c.length; _b++) {
33273                var crit = _c[_b];
33274                if (crit.criteria instanceof Criteria) {
33275                    crit.criteria.updateArrows(this.s.criteria.length > 1);
33276                    this._setCriteriaListeners(crit.criteria);
33277                }
33278            }
33279        };
33280        /**
33281         * Redraws the Contents of the searchBuilder Groups and Criteria
33282         */
33283        Group.prototype.redrawContents = function () {
33284            if (this.s.preventRedraw) {
33285                return;
33286            }
33287            // Clear the container out and add the basic elements
33288            this.dom.container.children().detach();
33289            this.dom.container
33290                .append(this.dom.logicContainer)
33291                .append(this.dom.add);
33292            // Sort the criteria by index so that they appear in the correct order
33293            this.s.criteria.sort(function (a, b) {
33294                if (a.criteria.s.index < b.criteria.s.index) {
33295                    return -1;
33296                }
33297                else if (a.criteria.s.index > b.criteria.s.index) {
33298                    return 1;
33299                }
33300                return 0;
33301            });
33302            this.setListeners();
33303            for (var i = 0; i < this.s.criteria.length; i++) {
33304                var crit = this.s.criteria[i].criteria;
33305                if (crit instanceof Criteria) {
33306                    // Reset the index to the new value
33307                    this.s.criteria[i].index = i;
33308                    this.s.criteria[i].criteria.s.index = i;
33309                    // Add to the group
33310                    this.s.criteria[i].criteria.dom.container.insertBefore(this.dom.add);
33311                    // Set listeners for various points
33312                    this._setCriteriaListeners(crit);
33313                    this.s.criteria[i].criteria.s.preventRedraw = this.s.preventRedraw;
33314                    this.s.criteria[i].criteria.rebuild(this.s.criteria[i].criteria.getDetails());
33315                    this.s.criteria[i].criteria.s.preventRedraw = false;
33316                }
33317                else if (crit instanceof Group && crit.s.criteria.length > 0) {
33318                    // Reset the index to the new value
33319                    this.s.criteria[i].index = i;
33320                    this.s.criteria[i].criteria.s.index = i;
33321                    // Add the sub group to the group
33322                    this.s.criteria[i].criteria.dom.container.insertBefore(this.dom.add);
33323                    // Redraw the contents of the group
33324                    crit.s.preventRedraw = this.s.preventRedraw;
33325                    crit.redrawContents();
33326                    crit.s.preventRedraw = false;
33327                    this._setGroupListeners(crit);
33328                }
33329                else {
33330                    // The group is empty so remove it
33331                    this.s.criteria.splice(i, 1);
33332                    i--;
33333                }
33334            }
33335            this.setupLogic();
33336        };
33337        /**
33338         * Resizes the logic button only rather than the entire dom.
33339         */
33340        Group.prototype.redrawLogic = function () {
33341            for (var _i = 0, _a = this.s.criteria; _i < _a.length; _i++) {
33342                var crit = _a[_i];
33343                if (crit.criteria instanceof Group) {
33344                    crit.criteria.redrawLogic();
33345                }
33346            }
33347            this.setupLogic();
33348        };
33349        /**
33350         * Search method, checking the row data against the criteria in the group
33351         *
33352         * @param rowData The row data to be compared
33353         * @returns boolean The result of the search
33354         */
33355        Group.prototype.search = function (rowData, rowIdx) {
33356            if (this.s.logic === 'AND') {
33357                return this._andSearch(rowData, rowIdx);
33358            }
33359            else if (this.s.logic === 'OR') {
33360                return this._orSearch(rowData, rowIdx);
33361            }
33362            return true;
33363        };
33364        /**
33365         * Locates the groups logic button to the correct location on the page
33366         */
33367        Group.prototype.setupLogic = function () {
33368            // Remove logic button
33369            this.dom.logicContainer.remove();
33370            this.dom.clear.remove();
33371            // If there are no criteria in the group then keep the logic removed and return
33372            if (this.s.criteria.length < 1) {
33373                if (!this.s.isChild) {
33374                    this.dom.container.trigger('dtsb-destroy');
33375                    // Set criteria left margin
33376                    this.dom.container.css('margin-left', 0);
33377                }
33378                return;
33379            }
33380            this.dom.clear.height('0px');
33381            this.dom.logicContainer.append(this.dom.clear);
33382            // Prepend logic button
33383            this.dom.container.prepend(this.dom.logicContainer);
33384            for (var _i = 0, _a = this.s.criteria; _i < _a.length; _i++) {
33385                var crit = _a[_i];
33386                if (crit.criteria instanceof Criteria) {
33387                    crit.criteria.setupButtons();
33388                }
33389            }
33390            // Set width, take 2 for the border
33391            var height = this.dom.container.outerHeight() - 1;
33392            this.dom.logicContainer.width(height);
33393            this._setLogicListener();
33394            // Set criteria left margin
33395            this.dom.container.css('margin-left', this.dom.logicContainer.outerHeight(true));
33396            var logicOffset = this.dom.logicContainer.offset();
33397            // Set horizontal alignment
33398            var currentLeft = logicOffset.left;
33399            var groupLeft = this.dom.container.offset().left;
33400            var shuffleLeft = currentLeft - groupLeft;
33401            var newPos = currentLeft - shuffleLeft - this.dom.logicContainer.outerHeight(true);
33402            this.dom.logicContainer.offset({ left: newPos });
33403            // Set vertical alignment
33404            var firstCrit = this.dom.logicContainer.next();
33405            var currentTop = logicOffset.top;
33406            var firstTop = $$2(firstCrit).offset().top;
33407            var shuffleTop = currentTop - firstTop;
33408            var newTop = currentTop - shuffleTop;
33409            this.dom.logicContainer.offset({ top: newTop });
33410            this.dom.clear.outerHeight(this.dom.logicContainer.height());
33411            this._setClearListener();
33412        };
33413        /**
33414         * Sets listeners on the groups elements
33415         */
33416        Group.prototype.setListeners = function () {
33417            var _this = this;
33418            this.dom.add.unbind('click');
33419            this.dom.add.on('click.dtsb', function () {
33420                // If this is the parent group then the logic button has not been added yet
33421                if (!_this.s.isChild) {
33422                    _this.dom.container.prepend(_this.dom.logicContainer);
33423                }
33424                _this.addCriteria();
33425                _this.dom.container.trigger('dtsb-add');
33426                _this.s.dt.state.save();
33427                return false;
33428            });
33429            for (var _i = 0, _a = this.s.criteria; _i < _a.length; _i++) {
33430                var crit = _a[_i];
33431                crit.criteria.setListeners();
33432            }
33433            this._setClearListener();
33434            this._setLogicListener();
33435        };
33436        /**
33437         * Adds a criteria to the group
33438         *
33439         * @param crit Instance of Criteria to be added to the group
33440         */
33441        Group.prototype.addCriteria = function (crit) {
33442            if (crit === void 0) { crit = null; }
33443            var index = crit === null ? this.s.criteria.length : crit.s.index;
33444            var criteria = new Criteria(this.s.dt, this.s.opts, this.s.topGroup, index, this.s.depth, this.s.serverData);
33445            // If a Criteria has been passed in then set the values to continue that
33446            if (crit !== null) {
33447                criteria.c = crit.c;
33448                criteria.s = crit.s;
33449                criteria.s.depth = this.s.depth;
33450                criteria.classes = crit.classes;
33451            }
33452            criteria.populate();
33453            var inserted = false;
33454            for (var i = 0; i < this.s.criteria.length; i++) {
33455                if (i === 0 && this.s.criteria[i].criteria.s.index > criteria.s.index) {
33456                    // Add the node for the criteria at the start of the group
33457                    criteria.getNode().insertBefore(this.s.criteria[i].criteria.dom.container);
33458                    inserted = true;
33459                }
33460                else if (i < this.s.criteria.length - 1 &&
33461                    this.s.criteria[i].criteria.s.index < criteria.s.index &&
33462                    this.s.criteria[i + 1].criteria.s.index > criteria.s.index) {
33463                    // Add the node for the criteria in the correct location
33464                    criteria.getNode().insertAfter(this.s.criteria[i].criteria.dom.container);
33465                    inserted = true;
33466                }
33467            }
33468            if (!inserted) {
33469                criteria.getNode().insertBefore(this.dom.add);
33470            }
33471            // Add the details for this criteria to the array
33472            this.s.criteria.push({
33473                criteria: criteria,
33474                index: index
33475            });
33476            this.s.criteria = this.s.criteria.sort(function (a, b) { return a.criteria.s.index - b.criteria.s.index; });
33477            for (var _i = 0, _a = this.s.criteria; _i < _a.length; _i++) {
33478                var opt = _a[_i];
33479                if (opt.criteria instanceof Criteria) {
33480                    opt.criteria.updateArrows(this.s.criteria.length > 1);
33481                }
33482         
33482   }
33483            this._setCriteriaListeners(criteria);
33484            criteria.setListeners();
33485            this.setupLogic();
33486        };
33487        /**
33488         * Checks the group to see if it has any filled criteria
33489         */
33490        Group.prototype.checkFilled = function () {
33491            for (var _i = 0, _a = this.s.criteria; _i < _a.length; _i++) {
33492                var crit = _a[_i];
33493                if (crit.criteria instanceof Criteria && crit.criteria.s.filled ||
33494                    crit.criteria instanceof Group && crit.criteria.checkFilled()) {
33495                    return true;
33496                }
33497            }
33498            return false;
33499        };
33500        /**
33501         * Gets the count for the number of criteria in this group and any sub groups
33502         */
33503        Group.prototype.count = function () {
33504            var count = 0;
33505            for (var _i = 0, _a = this.s.criteria; _i < _a.length; _i++) {
33506                var crit = _a[_i];
33507                if (crit.criteria instanceof Group) {
33508                    count += crit.criteria.count();
33509                }
33510                else {
33511                    count++;
33512                }
33513            }
33514            return count;
33515        };
33516        /**
33517         * Rebuilds a sub group that previously existed
33518         *
33519         * @param loadedGroup The details of a group within this group
33520         */
33521        Group.prototype._addPrevGroup = function (loadedGroup) {
33522            var idx = this.s.criteria.length;
33523            var group = new Group(this.s.dt, this.c, this.s.topGroup, idx, true, this.s.depth + 1, this.s.serverData);
33524            // Add the new group to the criteria array
33525            this.s.criteria.push({
33526                criteria: group,
33527                index: idx,
33528                logic: group.s.logic
33529            });
33530            // Rebuild it with the previous conditions for that group
33531            group.rebuild(loadedGroup);
33532            this.s.criteria[idx].criteria = group;
33533            this.s.topGroup.trigger('dtsb-redrawContents');
33534            this._setGroupListeners(group);
33535        };
33536        /**
33537         * Rebuilds a criteria of this group that previously existed
33538         *
33539         * @param loadedCriteria The details of a criteria within the group
33540         */
33541        Group.prototype._addPrevCriteria = function (loadedCriteria) {
33542            var idx = this.s.criteria.length;
33543            var criteria = new Criteria(this.s.dt, this.s.opts, this.s.topGroup, idx, this.s.depth, this.s.serverData);
33544            criteria.populate();
33545            // Add the new criteria to the criteria array
33546            this.s.criteria.push({
33547                criteria: criteria,
33548                index: idx
33549            });
33550            // Rebuild it with the previous conditions for that criteria
33551            criteria.s.preventRedraw = this.s.preventRedraw;
33552            criteria.rebuild(loadedCriteria);
33553            criteria.s.preventRedraw = false;
33554            this.s.criteria[idx].criteria = criteria;
33555            if (!this.s.preventRedraw) {
33556                this.s.topGroup.trigger('dtsb-redrawContents');
33557            }
33558        };
33559        /**
33560         * Checks And the criteria using AND logic
33561         *
33562         * @param rowData The row data to be checked against the search criteria
33563         * @returns boolean The result of the AND search
33564         */
33565        Group.prototype._andSearch = function (rowData, rowIdx) {
33566            // If there are no criteria then return true for this group
33567            if (this.s.criteria.length === 0) {
33568                return true;
33569            }
33570            for (var _i = 0, _a = this.s.criteria; _i < _a.length; _i++) {
33571                var crit = _a[_i];
33572                // If the criteria is not complete then skip it
33573                if (crit.criteria instanceof Criteria && !crit.criteria.s.filled) {
33574                    continue;
33575                }
33576                // Otherwise if a single one fails return false
33577                else if (!crit.criteria.search(rowData, rowIdx)) {
33578                    return false;
33579                }
33580            }
33581            // If we get to here then everything has passed, so return true for the group
33582            return true;
33583        };
33584        /**
33585         * Checks And the criteria using OR logic
33586         *
33587         * @param rowData The row data to be checked against the search criteria
33588         * @returns boolean The result of the OR search
33589         */
33590        Group.prototype._orSearch = function (rowData, rowIdx) {
33591            // If there are no criteria in the group then return true
33592            if (this.s.criteria.length === 0) {
33593                return true;
33594            }
33595            // This will check to make sure that at least one criteria in the group is complete
33596            var filledfound = false;
33597            for (var _i = 0, _a = this.s.criteria; _i < _a.length; _i++) {
33598                var crit = _a[_i];
33599                if (crit.criteria instanceof Criteria && crit.criteria.s.filled) {
33600                    // A completed criteria has been found so set the flag
33601                    filledfound = true;
33602                    // If the search passes then return true
33603                    if (crit.criteria.search(rowData, rowIdx)) {
33604                        return true;
33605                    }
33606                }
33607                else if (crit.criteria instanceof Group && crit.criteria.checkFilled()) {
33608                    filledfound = true;
33609                    if (crit.criteria.search(rowData, rowIdx)) {
33610                        return true;
33611                    }
33612                }
33613            }
33614            // If we get here we need to return the inverse of filledfound,
33615            //  as if any have been found and we are here then none have passed
33616            return !filledfound;
33617        };
33618        /**
33619         * Removes a criteria from the group
33620         *
33621         * @param criteria The criteria instance to be removed
33622         */
33623        Group.prototype._removeCriteria = function (criteria, group) {
33624            if (group === void 0) { group = false; }
33625            // If removing a criteria and there is only then then just destroy the group
33626            if (this.s.criteria.length <= 1 && this.s.isChild) {
33627                this.destroy();
33628            }
33629            else {
33630                // Otherwise splice the given criteria out and redo the indexes
33631                var last = void 0;
33632                for (var i = 0; i < this.s.criteria.length; i++) {
33633                    if (this.s.criteria[i].index === criteria.s.index &&
33634                        (!group || this.s.criteria[i].criteria instanceof Group)) {
33635                        last = i;
33636                    }
33637                }
33638                // We want to remove the last element with the desired index, as its replacement will be inserted before it
33639                if (last !== undefined) {
33640                    this.s.criteria.splice(last, 1);
33641                }
33642                for (var i = 0; i < this.s.criteria.length; i++) {
33643                    this.s.criteria[i].index = i;
33644                    this.s.criteria[i].criteria.s.index = i;
33645                }
33646            }
33647        };
33648        /**
33649         * Sets the listeners in group for a criteria
33650         *
33651         * @param criteria The criteria for the listeners to be set on
33652         */
33653        Group.prototype._setCriteriaListeners = function (criteria) {
33654            var _this = this;
33655            criteria.dom["delete"]
33656                .unbind('click')
33657                .on('click.dtsb', function () {
33658                _this._removeCriteria(criteria);
33659                criteria.dom.container.remove();
33660                for (var _i = 0, _a = _this.s.criteria; _i < _a.length; _i++) {
33661                    var crit = _a[_i];
33662                    if (crit.criteria instanceof Criteria) {
33663                        crit.criteria.updateArrows(_this.s.criteria.length > 1);
33664                    }
33665                }
33666                criteria.destroy();
33667                _this.s.dt.draw();
33668                _this.s.topGroup.trigger('dtsb-redrawContents');
33669                return false;
33670            });
33671            criteria.dom.right
33672                .unbind('click')
33673                .on('click.dtsb', function () {
33674                var idx = criteria.s.index;
33675                var group = new Group(_this.s.dt, _this.s.opts, _this.s.topGroup, criteria.s.index, true, _this.s.depth + 1, _this.s.serverData);
33676                // Add the criteria that is to be moved to the new group
33677                group.addCriteria(criteria);
33678                // Update the details in the current groups criteria array
33679                _this.s.criteria[idx].criteria = group;
33680                _this.s.criteria[idx].logic = 'AND';
33681                _this.s.topGroup.trigger('dtsb-redrawContents');
33682                _this._setGroupListeners(group);
33683                return false;
33684            });
33685            criteria.dom.left
33686                .unbind('click')
33687                .on('click.dtsb', function () {
33688                _this.s.toDrop = new Criteria(_this.s.dt, _this.s.opts, _this.s.topGroup, criteria.s.index, undefined, _this.s.serverData);
33689                _this.s.toDrop.s = criteria.s;
33690                _this.s.toDrop.c = criteria.c;
33691                _this.s.toDrop.classes = criteria.classes;
33692                _this.s.toDrop.populate();
33693                // The dropCriteria event mutates the reference to the index so need to store it
33694                var index = _this.s.toDrop.s.index;
33695                _this.dom.container.trigger('dtsb-dropCriteria');
33696                criteria.s.index = index;
33697                _this._removeCriteria(criteria);
33698                // By tracking the top level group we can directly trigger a redraw on it,
33699                //  bubbling is also possible, but that is slow with deep levelled groups
33700                _this.s.topGroup.trigger('dtsb-redrawContents');
33701                _this.s.dt.draw();
33702                return false;
33703            });
33704        };
33705        /**
33706         * Set's the listeners for the group clear button
33707         */
33708        Group.prototype._setClearListener = function () {
33709            var _this = this;
33710            this.dom.clear
33711                .unbind('click')
33712                .on('click.dtsb', function () {
33713                if (!_this.s.isChild) {
33714                    _this.dom.container.trigger('dtsb-clearContents');
33715                    return false;
33716                }
33717                _this.destroy();
33718                _this.s.topGroup.trigger('dtsb-redrawContents');
33719                return false;
33720            });
33721        };
33722        /**
33723         * Sets listeners for sub groups of this group
33724         *
33725         * @param group The sub group that the listeners are to be set on
33726         */
33727        Group.prototype._setGroupListeners = function (group) {
33728            var _this = this;
33729            // Set listeners for the new group
33730            group.dom.add
33731                .unbind('click')
33732                .on('click.dtsb', function () {
33733                _this.setupLogic();
33734                _this.dom.container.trigger('dtsb-add');
33735                return false;
33736            });
33737            group.dom.container
33738                .unbind('dtsb-add')
33739                .on('dtsb-add.dtsb', function () {
33740                _this.setupLogic();
33741                _this.dom.container.trigger('dtsb-add');
33742                return false;
33743            });
33744            group.dom.container
33745                .unbind('dtsb-destroy')
33746                .on('dtsb-destroy.dtsb', function () {
33747                _this._removeCriteria(group, true);
33748                group.dom.container.remove();
33749                _this.setupLogic();
33750                return false;
33751            });
33752            group.dom.container
33753                .unbind('dtsb-dropCriteria')
33754                .on('dtsb-dropCriteria.dtsb', function () {
33755                var toDrop = group.s.toDrop;
33756                toDrop.s.index = group.s.index;
33757                toDrop.updateArrows(_this.s.criteria.length > 1);
33758                _this.addCriteria(toDrop);
33759                return false;
33760            });
33761            group.setListeners();
33762        };
33763        /**
33764         * Sets up the Group instance, setting listeners and appending elements
33765         */
33766        Group.prototype._setup = function () {
33767            this.setListeners();
33768            this.dom.add.html(this.s.dt.i18n('searchBuilder.add', this.c.i18n.add));
33769            this.dom.logic.children().first().html(this.c.logic === 'OR'
33770                ? this.s.dt.i18n('searchBuilder.logicOr', this.c.i18n.logicOr)
33771                : this.s.dt.i18n('searchBuilder.logicAnd', this.c.i18n.logicAnd));
33772            this.s.logic = this.c.logic === 'OR' ? 'OR' : 'AND';
33773            if (this.c.greyscale) {
33774                this.dom.logic.addClass(this.classes.greyscale);
33775            }
33776            this.dom.logicContainer.append(this.dom.logic).append(this.dom.clear);
33777            // Only append the logic button immediately if this is a sub group,
33778            //  otherwise it will be prepended later when adding a criteria
33779            if (this.s.isChild) {
33780                this.dom.container.append(this.dom.logicContainer);
33781            }
33782            this.dom.container.append(this.dom.add);
33783        };
33784        /**
33785         * Sets the listener for the logic button
33786         */
33787        Group.prototype._setLogicListener = function () {
33788            var _this = this;
33789            this.dom.logic
33790                .unbind('click')
33791                .on('click.dtsb', function () {
33792                _this._toggleLogic();
33793                _this.s.dt.draw();
33794                for (var _i = 0, _a = _this.s.criteria; _i < _a.length; _i++) {
33795                    var crit = _a[_i];
33796                    crit.criteria.setListeners();
33797                }
33798            });
33799        };
33800        /**
33801         * Toggles the logic for the group
33802         */
33803        Group.prototype._toggleLogic = function () {
33804            if (this.s.logic === 'OR') {
33805                this.s.logic = 'AND';
33806                this.dom.logic.children().first().html(this.s.dt.i18n('searchBuilder.logicAnd', this.c.i18n.logicAnd));
33807            }
33808            else if (this.s.logic === 'AND') {
33809                this.s.logic = 'OR';
33810                this.dom.logic.children().first().html(this.s.dt.i18n('searchBuilder.logicOr', this.c.i18n.logicOr));
33811            }
33812        };
33813        Group.version = '1.1.0';
33814        Group.classes = {
33815            add: 'dtsb-add',
33816            button: 'dtsb-button',
33817            clearGroup: 'dtsb-clearGroup',
33818            greyscale: 'dtsb-greyscale',
33819            group: 'dtsb-group',
33820            inputButton: 'dtsb-iptbtn',
33821            logic: 'dtsb-logic',
33822            logicContainer: 'dtsb-logicContainer'
33823        };
33824        Group.defaults = {
33825            columns: true,
33826            conditions: {
33827                'date': Criteria.dateConditions,
33828                'html': Criteria.stringConditions,
33829                'html-num': Criteria.numConditions,
33830                'html-num-fmt': Criteria.numFmtConditions,
33831                'luxon': Criteria.luxonDateConditions,
33832                'moment': Criteria.momentDateConditions,
33833                'num': Criteria.numConditions,
33834                'num-fmt': Criteria.numFmtConditions,
33835                'string': Criteria.stringConditions
33836            },
33837            depthLimit: false,
33838            enterSearch: false,
33839            filterChanged: undefined,
33840            greyscale: false,
33841            i18n: {
33842                add: 'Add Condition',
33843                button: {
33844                    0: 'Search Builder',
33845                    _: 'Search Builder (%d)'
33846                },
33847                clearAll: 'Clear All',
33848                condition: 'Condition',
33849                data: 'Data',
33850                "delete": '&times',
33851                deleteTitle: 'Delete filtering rule',
33852                left: '<',
33853                leftTitle: 'Outdent criteria',
33854                logicAnd: 'And',
33855                logicOr: 'Or',
33856                right: '>',
33857                rightTitle: 'Indent criteria',
33858                title: {
33859                    0: 'Custom Search Builder',
33860                    _: 'Custom Search Builder (%d)'
33861                },
33862                value: 'Value',
33863                valueJoiner: 'and'
33864            },
33865            logic: 'AND',
33866            orthogonal: {
33867                display: 'display',
33868                search: 'filter'
33869            },
33870            preDefined: false
33871        };
33872        return Group;
33873    }());
33874
33875    var $$1;
33876    var dataTable$1;
33877    /**
33878     * Sets the value of jQuery for use in the file
33879     *
33880     * @param jq the instance of jQuery to be set
33881     */
33882    function setJQuery(jq) {
33883        $$1 = jq;
33884        dataTable$1 = jq.fn.DataTable;
33885    }
33886    /**
33887     * SearchBuilder class for DataTables.
33888     * Allows for complex search queries to be constructed and implemented on a DataTable
33889     */
33890    var SearchBuilder = /** @class */ (function () {
33891        function SearchBuilder(builderSettings, opts) {
33892            var _this = this;
33893            // Check that the required version of DataTables is included
33894            if (!dataTable$1 || !dataTable$1.versionCheck || !dataTable$1.versionCheck('1.10.0')) {
33895                throw new Error('SearchBuilder requires DataTables 1.10 or newer');
33896            }
33897            var table = new dataTable$1.Api(builderSettings);
33898            this.classes = $$1.extend(true, {}, SearchBuilder.classes);
33899            // Get options from user
33900            this.c = $$1.extend(true, {}, SearchBuilder.defaults, opts);
33901            this.dom = {
33902                clearAll: $$1('<button type="button">' + table.i18n('searchBuilder.clearAll', this.c.i18n.clearAll) + '</button>')
33903                    .addClass(this.classes.clearAll)
33904                    .addClass(this.classes.button)
33905                    .attr('type', 'button'),
33906                container: $$1('<div/>')
33907                    .addClass(this.classes.container),
33908                title: $$1('<div/>')
33909                    .addClass(this.classes.title),
33910                titleRow: $$1('<div/>')
33911                    .addClass(this.classes.titleRow),
33912                topGroup: undefined
33913            };
33914            this.s = {
33915                dt: table,
33916                opts: opts,
33917                search: undefined,
33918                serverData: undefined,
33919                topGroup: undefined
33920            };
33921            // If searchbuilder is already defined for this table then return
33922            if (table.settings()[0]._searchBuilder !== undefined) {
33923                return;
33924            }
33925            table.settings()[0]._searchBuilder = this;
33926            // If using SSP we want to include the previous state in the very first server call
33927            if (this.s.dt.page.info().serverSide) {
33928                this.s.dt.on('preXhr.dtsb', function (e, settings, data) {
33929                    var loadedState = _this.s.dt.state.loaded();
33930                    if (loadedState && loadedState.searchBuilder) {
33931                        data.searchBuilder = _this._collapseArray(loadedState.searchBuilder);
33932                    }
33933                });
33934                this.s.dt.on('xhr.dtsb', function (e, settings, json) {
33935                    if (json && json.searchBuilder && json.searchBuilder.options) {
33936                        _this.s.serverData = json.searchBuilder.options;
33937                    }
33938                });
33939            }
33940            // Run the remaining setup when the table is initialised
33941            if (this.s.dt.settings()[0]._bInitComplete) {
33942                this._setUp();
33943            }
33944            else {
33945                table.one('init.dt', function () {
33946                    _this._setUp();
33947                });
33948            }
33949            return this;
33950        }
33951        /**
33952         * Gets the details required to rebuild the SearchBuilder as it currently is
33953         */
33954        // eslint upset at empty object but that is what it is
33955        // eslint-disable-next-line @typescript-eslint/ban-types
33956        SearchBuilder.prototype.getDetails = function (deFormatDates) {
33957            if (deFormatDates === void 0) { deFormatDates = false; }
33958            return this.s.topGroup.getDetails(deFormatDates);
33959        };
33960        /**
33961         * Getter for the node of the container for the searchBuilder
33962         *
33963         * @returns JQuery<HTMLElement> the node of the container
33964         */
33965        SearchBuilder.prototype.getNode = function () {
33966            return this.dom.container;
33967        };
33968        /**
33969         * Rebuilds the SearchBuilder to a state that is provided
33970         *
33971         * @param details The details required to perform a rebuild
33972         */
33973        SearchBuilder.prototype.rebuild = function (details) {
33974            this.dom.clearAll.click();
33975            // If there are no details to rebuild then return
33976            if (details === undefined || details === null) {
33977                return this;
33978            }
33979            this.s.topGroup.s.preventRedraw = true;
33980            this.s.topGroup.rebuild(details);
33981            this.s.topGroup.s.preventRedraw = false;
33982            this._checkClear();
33983            this._updateTitle(this.s.topGroup.count());
33984            this.s.topGroup.redrawContents();
33985            this.s.dt.draw(false);
33986            this.s.topGroup.setListeners();
33987            return this;
33988        };
33989        /**
33990         * Applies the defaults to preDefined criteria
33991         *
33992         * @param preDef the array of criteria to be processed.
33993         */
33994        SearchBuilder.prototype._applyPreDefDefaults = function (preDef) {
33995            var _this = this;
33996            if (preDef.criteria !== undefined && preDef.logic === undefined) {
33997                preDef.logic = 'AND';
33998            }
33999            var _loop_1 = function (crit) {
34000                // Apply the defaults to any further criteria
34001                if (crit.criteria !== undefined) {
34002                    crit = this_1._applyPreDefDefaults(crit);
34003                }
34004                else {
34005                    this_1.s.dt.columns().every(function (index) {
34006                        if (_this.s.dt.settings()[0].aoColumns[index].sTitle === crit.data) {
34007                            crit.dataIdx = index;
34008                        }
34009                    });
34010                }
34011            };
34012            var this_1 = this;
34013            for (var _i = 0, _a = preDef.criteria; _i < _a.length; _i++) {
34014                var crit = _a[_i];
34015                _loop_1(crit);
34016            }
34017            return preDef;
34018        };
34019        /**
34020         * Set's up the SearchBuilder
34021         */
34022        SearchBuilder.prototype._setUp = function (loadState) {
34023            var _this = this;
34024            if (loadState === void 0) { loadState = true; }
34025            // Register an Api method for getting the column type
34026            $$1.fn.DataTable.Api.registerPlural('columns().type()', 'column().type()', function () {
34027                return this.iterator('column', function (settings, column) {
34028                    return settings.aoColumns[column].sType;
34029                }, 1);
34030            });
34031            // Check that DateTime is included, If not need to check if it could be used
34032            // eslint-disable-next-line no-extra-parens
34033            if (!dataTable$1.DateTime) {
34034                var types = this.s.dt.columns().type().toArray();
34035                if (types === undefined || types.includes(undefined) || types.includes(null)) {
34036                    types = [];
34037                    for (var _i = 0, _a = this.s.dt.settings()[0].aoColumns; _i < _a.length; _i++) {
34038                        var colInit = _a[_i];
34039                        types.push(colInit.searchBuilderType !== undefined ? colInit.searchBuilderType : colInit.sType);
34040                    }
34041                }
34042                var columnIdxs = this.s.dt.columns().toArray();
34043                // If the types are not yet set then draw to see if they can be retrieved then
34044                if (types === undefined || types.includes(undefined) || types.includes(null)) {
34045                    $$1.fn.dataTable.ext.oApi._fnColumnTypes(this.s.dt.settings()[0]);
34046                    types = this.s.dt.columns().type().toArray();
34047                }
34048                for (var i = 0; i < columnIdxs[0].length; i++) {
34049                    var column = columnIdxs[0][i];
34050                    var type = types[column];
34051                    if (
34052                    // Check if this column can be filtered
34053                    (this.c.columns === true ||
34054                        Array.isArray(this.c.columns) &&
34055                            this.c.columns.includes(i)) &&
34056                        // Check if the type is one of the restricted types
34057                        (type.includes('date') ||
34058                            type.includes('moment') ||
34059                            type.includes('luxon'))) {
34060                        alert('SearchBuilder Requires DateTime when used with dates.');
34061                        throw new Error('SearchBuilder requires DateTime');
34062                    }
34063                }
34064            }
34065            this.s.topGroup = new Group(this.s.dt, this.c, undefined, undefined, undefined, undefined, this.s.serverData);
34066            this._setClearListener();
34067            this.s.dt.on('stateSaveParams.dtsb', function (e, settings, data) {
34068                data.searchBuilder = _this.getDetails();
34069                if (!data.scroller) {
34070                    data.page = _this.s.dt.page();
34071                }
34072                else {
34073                    data.start = _this.s.dt.state().start;
34074                }
34075            });
34076            this.s.dt.on('stateLoadParams.dtsb', function (e, settings, data) {
34077                _this.rebuild(data.searchBuilder);
34078            });
34079            this._build();
34080            this.s.dt.on('preXhr.dtsb', function (e, settings, data) {
34081                if (_this.s.dt.page.info().serverSide) {
34082                    data.searchBuilder = _this._collapseArray(_this.getDetails(true));
34083                }
34084            });
34085            this.s.dt.on('column-reorder', function () {
34086                _this.rebuild(_this.getDetails());
34087            });
34088            if (loadState) {
34089                var loadedState = this.s.dt.state.loaded();
34090                // If the loaded State is not null rebuild based on it for statesave
34091                if (loadedState !== null && loadedState.searchBuilder !== undefined) {
34092                    this.s.topGroup.rebuild(loadedState.searchBuilder);
34093                    this.s.topGroup.dom.container.trigger('dtsb-redrawContents');
34094                    // If using SSP we want to restrict the amount of server calls that take place
34095                    //  and this information will already have been processed
34096                    if (!this.s.dt.page.info().serverSide) {
34097                        if (loadedState.page) {
34098                            this.s.dt.page(loadedState.page).draw('page');
34099                        }
34100                        else if (this.s.dt.scroller && loadedState.scroller) {
34101                            this.s.dt.scroller().scrollToRow(loadedState.scroller.topRow);
34102                        }
34103                    }
34104                    this.s.topGroup.setListeners();
34105                }
34106                // Otherwise load any predefined options
34107                else if (this.c.preDefined !== false) {
34108                    this.c.preDefined = this._applyPreDefDefaults(this.c.preDefined);
34109                    this.rebuild(this.c.preDefined);
34110                }
34111            }
34112            this._setEmptyListener();
34113            this.s.dt.state.save();
34114        };
34115        SearchBuilder.prototype._collapseArray = function (criteria) {
34116            if (criteria.logic === undefined) {
34117                if (criteria.value !== undefined) {
34118                    criteria.value.sort(function (a, b) {
34119                        if (!isNaN(+a)) {
34120                            a = +a;
34121                            b = +b;
34122                        }
34123                        if (a < b) {
34124                            return -1;
34125                        }
34126                        else if (b < a) {
34127                            return 1;
34128                        }
34129                        else {
34130                            return 0;
34131                        }
34132                    });
34133                    criteria.value1 = criteria.value[0];
34134                    criteria.value2 = criteria.value[1];
34135                }
34136            }
34137            else {
34138                for (var i = 0; i < criteria.criteria.length; i++) {
34139                    criteria.criteria[i] = this._collapseArray(criteria.criteria[i]);
34140                }
34141            }
34142            return criteria;
34143        };
34144        /**
34145         * Updates the title of the SearchBuilder
34146         *
34147         * @param count the number of filters in the SearchBuilder
34148         */
34149        SearchBuilder.prototype._updateTitle = function (count) {
34150            this.dom.title.html(this.s.dt.i18n('searchBuilder.title', this.c.i18n.title, count));
34151        };
34152        /**
34153         * Builds all of the dom elements together
34154         */
34155        SearchBuilder.prototype._build = function () {
34156            var _this = this;
34157            // Empty and setup the container
34158            this.dom.clearAll.remove();
34159            this.dom.container.empty();
34160            var count = this.s.topGroup.count();
34161            this._updateTitle(count);
34162            this.dom.titleRow.append(this.dom.title);
34163            this.dom.container.append(this.dom.titleRow);
34164            this.dom.topGroup = this.s.topGroup.getNode();
34165            this.dom.container.append(this.dom.topGroup);
34166            this._setRedrawListener();
34167            var tableNode = this.s.dt.table(0).node();
34168            if (!$$1.fn.dataTable.ext.search.includes(this.s.search)) {
34169                // Custom search function for SearchBuilder
34170                this.s.search = function (settings, searchData, dataIndex) {
34171                    if (settings.nTable !== tableNode) {
34172                        return true;
34173                    }
34174                    return _this.s.topGroup.search(searchData, dataIndex);
34175                };
34176                // Add SearchBuilder search function to the dataTables search array
34177                $$1.fn.dataTable.ext.search.push(this.s.search);
34178            }
34179            this.s.dt.on('destroy.dtsb', function () {
34180                _this.dom.container.remove();
34181                _this.dom.clearAll.remove();
34182                var searchIdx = $$1.fn.dataTable.ext.search.indexOf(_this.s.search);
34183                while (searchIdx !== -1) {
34184                    $$1.fn.dataTable.ext.search.splice(searchIdx, 1);
34185                    searchIdx = $$1.fn.dataTable.ext.search.indexOf(_this.s.search);
34186                }
34187                _this.s.dt.off('.dtsb');
34188                $$1(_this.s.dt.table().node()).off('.dtsb');
34189            });
34190        };
34191        /**
34192         * Checks if the clearAll button should be added or not
34193         */
34194        SearchBuilder.prototype._checkClear = function () {
34195            if (this.s.topGroup.s.criteria.length > 0) {
34196                this.dom.clearAll.insertAfter(this.dom.title);
34197                this._setClearListener();
34198            }
34199            else {
34200                this.dom.clearAll.remove();
34201            }
34202        };
34203        /**
34204         * Update the count in the title/button
34205         *
34206         * @param count Number of filters applied
34207         */
34208        SearchBuilder.prototype._filterChanged = function (count) {
34209            var fn = this.c.filterChanged;
34210            if (typeof fn === 'function') {
34211                fn(count, this.s.dt.i18n('searchBuilder.button', this.c.i18n.button, count));
34212            }
34213        };
34214        /**
34215         * Set the listener for the clear button
34216         */
34217        SearchBuilder.prototype._setClearListener = function () {
34218            var _this = this;
34219            this.dom.clearAll.unbind('click');
34220           
34220 this.dom.clearAll.on('click.dtsb', function () {
34221                _this.s.topGroup = new Group(_this.s.dt, _this.c, undefined, undefined, undefined, undefined, _this.s.serverData);
34222                _this._build();
34223                _this.s.dt.draw();
34224                _this.s.topGroup.setListeners();
34225                _this.dom.clearAll.remove();
34226                _this._setEmptyListener();
34227                _this._filterChanged(0);
34228                return false;
34229            });
34230        };
34231        /**
34232         * Set the listener for the Redraw event
34233         */
34234        SearchBuilder.prototype._setRedrawListener = function () {
34235            var _this = this;
34236            this.s.topGroup.dom.container.unbind('dtsb-redrawContents');
34237            this.s.topGroup.dom.container.on('dtsb-redrawContents.dtsb', function () {
34238                _this._checkClear();
34239                _this.s.topGroup.redrawContents();
34240                _this.s.topGroup.setupLogic();
34241                _this._setEmptyListener();
34242                var count = _this.s.topGroup.count();
34243                _this._updateTitle(count);
34244                _this._filterChanged(count);
34245                // If using SSP we want to restrict the amount of server calls that take place
34246                //  and this information will already have been processed
34247                if (!_this.s.dt.page.info().serverSide) {
34248                    _this.s.dt.draw();
34249                }
34250                _this.s.dt.state.save();
34251            });
34252            this.s.topGroup.dom.container.unbind('dtsb-redrawContents-noDraw');
34253            this.s.topGroup.dom.container.on('dtsb-redrawContents-noDraw.dtsb', function () {
34254                _this._checkClear();
34255                _this.s.topGroup.s.preventRedraw = true;
34256                _this.s.topGroup.redrawContents();
34257                _this.s.topGroup.s.preventRedraw = false;
34258                _this.s.topGroup.setupLogic();
34259                _this._setEmptyListener();
34260                var count = _this.s.topGroup.count();
34261                _this._updateTitle(count);
34262                _this._filterChanged(count);
34263            });
34264            this.s.topGroup.dom.container.unbind('dtsb-redrawLogic');
34265            this.s.topGroup.dom.container.on('dtsb-redrawLogic.dtsb', function () {
34266                _this.s.topGroup.redrawLogic();
34267                var count = _this.s.topGroup.count();
34268                _this._updateTitle(count);
34269                _this._filterChanged(count);
34270            });
34271            this.s.topGroup.dom.container.unbind('dtsb-add');
34272            this.s.topGroup.dom.container.on('dtsb-add.dtsb', function () {
34273                var count = _this.s.topGroup.count();
34274                _this._updateTitle(count);
34275                _this._filterChanged(count);
34276            });
34277            this.s.dt.on('postEdit.dtsb postCreate.dtsb postRemove.dtsb', function () {
34278                _this.s.topGroup.redrawContents();
34279            });
34280            this.s.topGroup.dom.container.unbind('dtsb-clearContents');
34281            this.s.topGroup.dom.container.on('dtsb-clearContents.dtsb', function () {
34282                _this._setUp(false);
34283                _this._filterChanged(0);
34284                _this.s.dt.draw();
34285            });
34286        };
34287        /**
34288         * Sets listeners to check whether clearAll should be added or removed
34289         */
34290        SearchBuilder.prototype._setEmptyListener = function () {
34291            var _this = this;
34292            this.s.topGroup.dom.add.on('click.dtsb', function () {
34293                _this._checkClear();
34294            });
34295            this.s.topGroup.dom.container.on('dtsb-destroy.dtsb', function () {
34296                _this.dom.clearAll.remove();
34297            });
34298        };
34299        SearchBuilder.version = '1.4.2';
34300        SearchBuilder.classes = {
34301            button: 'dtsb-button',
34302            clearAll: 'dtsb-clearAll',
34303            container: 'dtsb-searchBuilder',
34304            inputButton: 'dtsb-iptbtn',
34305            title: 'dtsb-title',
34306            titleRow: 'dtsb-titleRow'
34307        };
34308        SearchBuilder.defaults = {
34309            columns: true,
34310            conditions: {
34311                'date': Criteria.dateConditions,
34312                'html': Criteria.stringConditions,
34313                'html-num': Criteria.numConditions,
34314                'html-num-fmt': Criteria.numFmtConditions,
34315                'luxon': Criteria.luxonDateConditions,
34316                'moment': Criteria.momentDateConditions,
34317                'num': Criteria.numConditions,
34318                'num-fmt': Criteria.numFmtConditions,
34319                'string': Criteria.stringConditions
34320            },
34321            depthLimit: false,
34322            enterSearch: false,
34323            filterChanged: undefined,
34324            greyscale: false,
34325            i18n: {
34326                add: 'Add Condition',
34327                button: {
34328                    0: 'Search Builder',
34329                    _: 'Search Builder (%d)'
34330                },
34331                clearAll: 'Clear All',
34332                condition: 'Condition',
34333                conditions: {
34334                    array: {
34335                        contains: 'Contains',
34336                        empty: 'Empty',
34337                        equals: 'Equals',
34338                        not: 'Not',
34339                        notEmpty: 'Not Empty',
34340                        without: 'Without'
34341                    },
34342                    date: {
34343                        after: 'After',
34344                        before: 'Before',
34345                        between: 'Between',
34346                        empty: 'Empty',
34347                        equals: 'Equals',
34348                        not: 'Not',
34349                        notBetween: 'Not Between',
34350                        notEmpty: 'Not Empty'
34351                    },
34352                    // eslint-disable-next-line id-blacklist
34353                    number: {
34354                        between: 'Between',
34355                        empty: 'Empty',
34356                        equals: 'Equals',
34357                        gt: 'Greater Than',
34358                        gte: 'Greater Than Equal To',
34359                        lt: 'Less Than',
34360                        lte: 'Less Than Equal To',
34361                        not: 'Not',
34362                        notBetween: 'Not Between',
34363                        notEmpty: 'Not Empty'
34364                    },
34365                    // eslint-disable-next-line id-blacklist
34366                    string: {
34367                        contains: 'Contains',
34368                        empty: 'Empty',
34369                        endsWith: 'Ends With',
34370                        equals: 'Equals',
34371                        not: 'Not',
34372                        notContains: 'Does Not Contain',
34373                        notEmpty: 'Not Empty',
34374                        notEndsWith: 'Does Not End With',
34375                        notStartsWith: 'Does Not Start With',
34376                        startsWith: 'Starts With'
34377                    }
34378                },
34379                data: 'Data',
34380                "delete": '&times',
34381                deleteTitle: 'Delete filtering rule',
34382                left: '<',
34383                leftTitle: 'Outdent criteria',
34384                logicAnd: 'And',
34385                logicOr: 'Or',
34386                right: '>',
34387                rightTitle: 'Indent criteria',
34388                title: {
34389                    0: 'Custom Search Builder',
34390                    _: 'Custom Search Builder (%d)'
34391                },
34392                value: 'Value',
34393                valueJoiner: 'and'
34394            },
34395            logic: 'AND',
34396            orthogonal: {
34397                display: 'display',
34398                search: 'filter'
34399            },
34400            preDefined: false
34401        };
34402        return SearchBuilder;
34403    }());
34404
34405    /*! SearchBuilder 1.4.2
34406     * ©SpryMedia Ltd - datatables.net/license/mit
34407     */
34408    setJQuery($);
34409    setJQuery$1($);
34410    setJQuery$2($);
34411    var dataTable = $.fn.dataTable;
34412    // eslint-disable-next-line no-extra-parens
34413    DataTable.SearchBuilder = SearchBuilder;
34414    // eslint-disable-next-line no-extra-parens
34415    dataTable.SearchBuilder = SearchBuilder;
34416    // eslint-disable-next-line no-extra-parens
34417    DataTable.Group = Group;
34418    // eslint-disable-next-line no-extra-parens
34419    dataTable.Group = Group;
34420    // eslint-disable-next-line no-extra-parens
34421    DataTable.Criteria = Criteria;
34422    // eslint-disable-next-line no-extra-parens
34423    dataTable.Criteria = Criteria;
34424    // eslint-disable-next-line no-extra-parens
34425    var apiRegister = DataTable.Api.register;
34426    // Set up object for plugins
34427    DataTable.ext.searchBuilder = {
34428        conditions: {}
34429    };
34430    DataTable.ext.buttons.searchBuilder = {
34431        action: function (e, dt, node, config) {
34432            this.popover(config._searchBuilder.getNode(), {
34433                align: 'container',
34434                span: 'container'
34435            });
34436            var topGroup = config._searchBuilder.s.topGroup;
34437            // Need to redraw the contents to calculate the correct positions for the elements
34438            if (topGroup !== undefined) {
34439                topGroup.dom.container.trigger('dtsb-redrawContents-noDraw');
34440            }
34441            if (topGroup.s.criteria.length === 0) {
34442                $('.' + $.fn.dataTable.Group.classes.add.replace(/ /g, '.')).click();
34443            }
34444        },
34445        config: {},
34446        init: function (dt, node, config) {
34447            var sb = new DataTable.SearchBuilder(dt, $.extend({
34448                filterChanged: function (count, text) {
34449                    dt.button(node).text(text);
34450                }
34451            }, config.config));
34452            dt.button(node).text(config.text || dt.i18n('searchBuilder.button', sb.c.i18n.button, 0));
34453            config._searchBuilder = sb;
34454        },
34455        text: null
34456    };
34457    apiRegister('searchBuilder.getDetails()', function (deFormatDates) {
34458        if (deFormatDates === void 0) { deFormatDates = false; }
34459        var ctx = this.context[0];
34460        // If SearchBuilder has not been initialised on this instance then return
34461        return ctx._searchBuilder ?
34462            ctx._searchBuilder.getDetails(deFormatDates) :
34463            null;
34464    });
34465    apiRegister('searchBuilder.rebuild()', function (details) {
34466        var ctx = this.context[0];
34467        // If SearchBuilder has not been initialised on this instance then return
34468        if (ctx._searchBuilder === undefined) {
34469            return null;
34470        }
34471        ctx._searchBuilder.rebuild(details);
34472        return this;
34473    });
34474    apiRegister('searchBuilder.container()', function () {
34475        var ctx = this.context[0];
34476        // If SearchBuilder has not been initialised on this instance then return
34477        return ctx._searchBuilder ?
34478            ctx._searchBuilder.getNode() :
34479            null;
34480    });
34481    /**
34482     * Init function for SearchBuilder
34483     *
34484     * @param settings the settings to be applied
34485     * @param options the options for SearchBuilder
34486     * @returns JQUERY<HTMLElement> Returns the node of the SearchBuilder
34487     */
34488    function _init(settings, options) {
34489        var api = new DataTable.Api(settings);
34490        var opts = options
34491            ? options
34492            : api.init().searchBuilder || DataTable.defaults.searchBuilder;
34493        var searchBuilder = new SearchBuilder(api, opts);
34494        var node = searchBuilder.getNode();
34495        return node;
34496    }
34497    // Attach a listener to the document which listens for DataTables initialisation
34498    // events so we can automatically initialise
34499    $(document).on('preInit.dt.dtsp', function (e, settings) {
34500        if (e.namespace !== 'dt') {
34501            return;
34502        }
34503        if (settings.oInit.searchBuilder ||
34504            DataTable.defaults.searchBuilder) {
34505            if (!settings._searchBuilder) {
34506                _init(settings);
34507            }
34508        }
34509    });
34510    // DataTables `dom` feature option
34511    DataTable.ext.feature.push({
34512        cFeature: 'Q',
34513        fnInit: _init
34514    });
34515    // DataTables 2 layout feature
34516    if (DataTable.ext.features) {
34517        DataTable.ext.features.register('searchBuilder', _init);
34518    }
34519
34520})();
34521
34522
34523return DataTable;
34524}));
34525
34526
34527/*! DataTables integration for DataTables' SearchBuilder
34528 * © SpryMedia Ltd - datatables.net/license
34529 */
34530
34531(function( factory ){
34532	if ( typeof define === 'function' && define.amd ) {
34533		// AMD
34534		define( ['jquery', 'datatables.net-dt', 'datatables.net-searchbuilder'], function ( $ ) {
34535			return factory( $, window, document );
34536		} );
34537	}
34538	else if ( typeof exports === 'object' ) {
34539		// CommonJS
34540		var jq = require('jquery');
34541		var cjsRequires = function (root, $) {
34542			if ( ! $.fn.dataTable ) {
34543				require('datatables.net-dt')(root, $);
34544			}
34545
34546			if ( ! $.fn.dataTable.SearchBuilder ) {
34547				require('datatables.net-searchbuilder')(root, $);
34548			}
34549		};
34550
34551		if (typeof window !== 'undefined') {
34552			module.exports = function (root, $) {
34553				if ( ! root ) {
34554					// CommonJS environments without a window global must pass a
34555					// root. This will give an error otherwise
34556					root = window;
34557				}
34558
34559				if ( ! $ ) {
34560					$ = jq( root );
34561				}
34562
34563				cjsRequires( root, $ );
34564				return factory( $, root, root.document );
34565			};
34566		}
34567		else {
34568			cjsRequires( window, jq );
34569			module.exports = factory( jq, window, window.document );
34570		}
34571	}
34572	else {
34573		// Browser
34574		factory( jQuery, window, document );
34575	}
34576}(function( $, window, document, undefined ) {
34577'use strict';
34578var DataTable = $.fn.dataTable;
34579
34580
34581
34582
34583return DataTable;
34584}));
34585
34586
34587/*! SearchPanes 2.1.2
34588 * © SpryMedia Ltd - datatables.net/license
34589 */
34590
34591(function( factory ){
34592	if ( typeof define === 'function' && define.amd ) {
34593		// AMD
34594		define( ['jquery', 'datatables.net'], function ( $ ) {
34595			return factory( $, window, document );
34596		} );
34597	}
34598	else if ( typeof exports === 'object' ) {
34599		// CommonJS
34600		var jq = require('jquery');
34601		var cjsRequires = function (root, $) {
34602			if ( ! $.fn.dataTable ) {
34603				require('datatables.net')(root, $);
34604			}
34605		};
34606
34607		if (typeof window !== 'undefined') {
34608			module.exports = function (root, $) {
34609				if ( ! root ) {
34610					// CommonJS environments without a window global must pass a
34611					// root. This will give an error otherwise
34612					root = window;
34613				}
34614
34615				if ( ! $ ) {
34616					$ = jq( root );
34617				}
34618
34619				cjsRequires( root, $ );
34620				return factory( $, root, root.document );
34621			};
34622		}
34623		else {
34624			cjsRequires( window, jq );
34625			module.exports = factory( jq, window, window.document );
34626		}
34627	}
34628	else {
34629		// Browser
34630		factory( jQuery, window, document );
34631	}
34632}(function( $, window, document, undefined ) {
34633'use strict';
34634var DataTable = $.fn.dataTable;
34635
34636
34637(function () {
34638    'use strict';
34639
34640    var $$5;
34641    var dataTable$2;
34642    function setJQuery$4(jq) {
34643        $$5 = jq;
34644        dataTable$2 = jq.fn.dataTable;
34645    }
34646    var SearchPane = /** @class */ (function () {
34647        /**
34648         * Creates the panes, sets up the search function
34649         *
34650         * @param paneSettings The settings for the searchPanes
34651         * @param opts The options for the default features
34652         * @param index the index of the column for this pane
34653         * @param panesContainer The overall container for SearchPanes that this pane will be attached to
34654         * @param panes The custom pane settings if this is a custom pane
34655         * @returns {object} the pane that has been created, including the table and the index of the pane
34656         */
34657        function SearchPane(paneSettings, opts, index, panesContainer, panes) {
34658            var _this = this;
34659            if (panes === void 0) { panes = null; }
34660            // Check that the required version of DataTables is included
34661            if (!dataTable$2 || !dataTable$2.versionCheck || !dataTable$2.versionCheck('1.10.0')) {
34662                throw new Error('SearchPane requires DataTables 1.10 or newer');
34663            }
34664            // Check that Select is included
34665            // eslint-disable-next-line no-extra-parens
34666            if (!dataTable$2.select) {
34667                throw new Error('SearchPane requires Select');
34668            }
34669            var table = new dataTable$2.Api(paneSettings);
34670            this.classes = $$5.extend(true, {}, SearchPane.classes);
34671            // Get options from user
34672            this.c = $$5.extend(true, {}, SearchPane.defaults, opts, panes);
34673            if (opts && opts.hideCount && opts.viewCount === undefined) {
34674                this.c.viewCount = !this.c.hideCount;
34675            }
34676            var rowLength = table.columns().eq(0).toArray().length;
34677            this.s = {
34678                colExists: index < rowLength,
34679                colOpts: undefined,
34680                customPaneSettings: panes,
34681                displayed: false,
34682                dt: table,
34683                dtPane: undefined,
34684                firstSet: true,
34685                index: index,
34686                indexes: [],
34687                listSet: false,
34688                name: undefined,
34689                rowData: {
34690                    arrayFilter: [],
34691                    arrayOriginal: [],
34692                    bins: {},
34693                    binsOriginal: {},
34694                    filterMap: new Map(),
34695                    totalOptions: 0
34696                },
34697                scrollTop: 0,
34698                searchFunction: undefined,
34699                selections: [],
34700                serverSelect: [],
34701                serverSelecting: false,
34702                tableLength: null,
34703                updating: false
34704            };
34705            this.s.colOpts = this.s.colExists ? this._getOptions() : this._getBonusOptions();
34706            this.dom = {
34707                buttonGroup: $$5('<div/>').addClass(this.classes.buttonGroup),
34708                clear: $$5('<button type="button">&#215;</button>')
34709                    .attr('disabled', 'true')
34710                    .addClass(this.classes.disabledButton)
34711                    .addClass(this.classes.paneButton)
34712                    .addClass(this.classes.clearButton)
34713                    .html(this.s.dt.i18n('searchPanes.clearPane', this.c.i18n.clearPane)),
34714                collapseButton: $$5('<button type="button"><span class="' + this.classes.caret + '">&#x5e;</span></button>')
34715                    .addClass(this.classes.paneButton)
34716                    .addClass(this.classes.collapseButton),
34717                container: $$5('<div/>')
34718                    .addClass(this.classes.container)
34719                    .addClass(this.s.colOpts.className)
34720                    .addClass(this.classes.layout +
34721                    (parseInt(this.c.layout.split('-')[1], 10) < 10 ?
34722                        this.c.layout :
34723                        this.c.layout.split('-')[0] + '-9'))
34724                    .addClass(this.s.customPaneSettings && this.s.customPaneSettings.className
34725                    ? this.s.customPaneSettings.className
34726                    : ''),
34727                countButton: $$5('<button type="button"></button>')
34728                    .addClass(this.classes.paneButton)
34729                    .addClass(this.classes.countButton),
34730                dtP: $$5('<table><thead><tr><th>' +
34731                    (this.s.colExists
34732                        ? $$5(this.s.dt.column(this.s.index).header()).text()
34733                        : this.s.customPaneSettings.header || 'Custom Pane') + '</th><th/></tr></thead></table>'),
34734                lower: $$5('<div/>').addClass(this.classes.subRow2).addClass(this.classes.narrowButton),
34735                nameButton: $$5('<button type="button"></button>')
34736                    .addClass(this.classes.paneButton)
34737                    .addClass(this.classes.nameButton),
34738                panesContainer: panesContainer,
34739                searchBox: $$5('<input/>').addClass(this.classes.paneInputButton).addClass(this.classes.search),
34740                searchButton: $$5('<button type = "button"/>')
34741                    .addClass(this.classes.searchIcon)
34742                    .addClass(this.classes.paneButton),
34743                searchCont: $$5('<div/>').addClass(this.classes.searchCont),
34744                searchLabelCont: $$5('<div/>').addClass(this.classes.searchLabelCont),
34745                topRow: $$5('<div/>').addClass(this.classes.topRow),
34746                upper: $$5('<div/>').addClass(this.classes.subRow1).addClass(this.classes.narrowSearch)
34747            };
34748            // Set the value of name incase ordering is desired
34749            if (this.s.colOpts.name) {
34750                this.s.name = this.s.colOpts.name;
34751            }
34752            else if (this.s.customPaneSettings && this.s.customPaneSettings.name) {
34753                this.s.name = this.s.customPaneSettings.name;
34754            }
34755            else {
34756                this.s.name = this.s.colExists ?
34757                    $$5(this.s.dt.column(this.s.index).header()).text() :
34758                    this.s.customPaneSettings.header || 'Custom Pane';
34759            }
34760            var tableNode = this.s.dt.table(0).node();
34761            // Custom search function for table
34762            this.s.searchFunction = function (settings, searchData, dataIndex) {
34763                // If no data has been selected then show all
34764                if (_this.s.selections.length === 0) {
34765                    return true;
34766                }
34767                if (settings.nTable !== tableNode) {
34768                    return true;
34769                }
34770                var filter = null;
34771                if (_this.s.colExists) {
34772                    // Get the current filtered data
34773                    filter = searchData[_this.s.index];
34774                    if (_this.s.colOpts.orthogonal.filter !== 'filter') {
34775                        // get the filter value from the map
34776                        filter = _this.s.rowData.filterMap.get(dataIndex);
34777                        if (filter instanceof $$5.fn.dataTable.Api) {
34778                            // eslint-disable-next-line no-extra-parens
34779                            filter = filter.toArray();
34780                        }
34781                    }
34782                }
34783                return _this._search(filter, dataIndex);
34784            };
34785            $$5.fn.dataTable.ext.search.push(this.s.searchFunction);
34786            // If the clear button for this pane is clicked clear the selections
34787            if (this.c.clear) {
34788                this.dom.clear.on('click.dtsp', function () {
34789                    var searches = _this.dom.container.find('.' + _this.classes.search.replace(/\s+/g, '.'));
34790                    searches.each(function () {
34791                        $$5(this).val('').trigger('input');
34792                    });
34793                    _this.clearPane();
34794                });
34795            }
34796            // Sometimes the top row of the panes containing the search box and ordering buttons appears
34797            //  weird if the width of the panes is lower than expected, this fixes the design.
34798            // Equally this may occur when the table is resized.
34799            this.s.dt.on('draw.dtsp', function () { return _this.adjustTopRow(); });
34800            this.s.dt.on('buttons-action.dtsp', function () { return _this.adjustTopRow(); });
34801            // When column-reorder is present and the columns are moved, it is necessary to
34802            //  reassign all of the panes indexes to the new index of the column.
34803            this.s.dt.on('column-reorder.dtsp', function (e, settings, details) {
34804                _this.s.index = details.mapping[_this.s.index];
34805            });
34806            return this;
34807        }
34808        /**
34809         * Adds a row to the panes table
34810         *
34811         * @param display the value to be displayed to the user
34812         * @param filter the value to be filtered on when searchpanes is implemented
34813         * @param shown the number of rows in the table that are currently visible matching this criteria
34814         * @param total the total number of rows in the table that match this criteria
34815         * @param sort the value to be sorted in the pane table
34816         * @param type the value of which the type is to be derived from
34817         */
34818        SearchPane.prototype.addRow = function (display, filter, sort, type, className, total, shown) {
34819            if (!total) {
34820                total = this.s.rowData.bins[filter] ?
34821                    this.s.rowData.bins[filter] :
34822                    0;
34823            }
34824            if (!shown) {
34825                shown = this._getShown(filter);
34826            }
34827            var index;
34828            for (var _i = 0, _a = this.s.indexes; _i < _a.length; _i++) {
34829                var entry = _a[_i];
34830                if (entry.filter === filter) {
34831                    index = entry.index;
34832                }
34833            }
34834            if (index === undefined) {
34835                index = this.s.indexes.length;
34836                this.s.indexes.push({ filter: filter, index: index });
34837            }
34838            return this.s.dtPane.row.add({
34839                className: className,
34840                display: display !== '' ?
34841                    display :
34842                    this.emptyMessage(),
34843                filter: filter,
34844                index: index,
34845                shown: shown,
34846                sort: sort,
34847                total: total,
34848                type: type
34849            });
34850        };
34851        /**
34852         * Adjusts the layout of the top row when the screen is resized
34853         */
34854        SearchPane.prototype.adjustTopRow = function () {
34855            var subContainers = this.dom.container.find('.' + this.classes.subRowsContainer.replace(/\s+/g, '.'));
34856            var subRow1 = this.dom.container.find('.' + this.classes.subRow1.replace(/\s+/g, '.'));
34857            var subRow2 = this.dom.container.find('.' + this.classes.subRow2.replace(/\s+/g, '.'));
34858            var topRow = this.dom.container.find('.' + this.classes.topRow.replace(/\s+/g, '.'));
34859            // If the width is 0 then it is safe to assume that the pane has not yet been displayed.
34860            //  Even if it has, if the width is 0 it won't make a difference if it has the narrow class or not
34861            if (($$5(subContainers[0]).width() < 252 || $$5(topRow[0]).width() < 252) && $$5(subContainers[0]).width() !== 0) {
34862                $$5(subContainers[0]).addClass(this.classes.narrow);
34863                $$5(subRow1[0]).addClass(this.classes.narrowSub).removeClass(this.classes.narrowSearch);
34864                $$5(subRow2[0]).addClass(this.classes.narrowSub).removeClass(this.classes.narrowButton);
34865            }
34866            else {
34867                $$5(subContainers[0]).removeClass(this.classes.narrow);
34868                $$5(subRow1[0]).removeClass(this.classes.narrowSub).addClass(this.classes.narrowSearch);
34869                $$5(subRow2[0]).removeClass(this.classes.narrowSub).addClass(this.classes.narrowButton);
34870            }
34871        };
34872        /**
34873         * In the case of a rebuild there is potential for new data to have been included or removed
34874         * so all of the rowData must be reset as a precaution.
34875         */
34876        SearchPane.prototype.clearData = function () {
34877            this.s.rowData = {
34878                arrayFilter: [],
34879                arrayOriginal: [],
34880                bins: {},
34881                binsOriginal: {},
34882                filterMap: new Map(),
34883                totalOptions: 0
34884            };
34885        };
34886        /**
34887         * Clear the selections in the pane
34888         */
34889        SearchPane.prototype.clearPane = function () {
34890            // Deselect all rows which are selected and update the table and filter count.
34891            this.s.dtPane.rows({ selected: true }).deselect();
34892            this.updateTable();
34893            return this;
34894        };
34895        /**
34896         * Collapses the pane so that only the header is displayed
34897         */
34898        SearchPane.prototype.collapse = function () {
34899            var _this = this;
34900            if (!this.s.displayed ||
34901                (
34902                // If collapsing is disabled globally, and not enabled specifically for this column
34903                !this.c.collapse && this.s.colOpts.collapse !== true ||
34904                    // OR, collapsing could be enabled globally and this column specifically
34905                    // is not to be collapsed.
34906                    // We can't just take !this.s.colOpts.collapse here as if it is undefined
34907                    // then the global should be used
34908                    this.s.colOpts.collapse === false)) {
34909                return;
34910            }
34911            $$5(this.s.dtPane.table().container()).addClass(this.classes.hidden);
34912            this.dom.topRow.addClass(this.classes.bordered);
34913            this.dom.nameButton.addClass(this.classes.disabledButton);
34914            this.dom.countButton.addClass(this.classes.disabledButton);
34915            this.dom.searchButton.addClass(this.classes.disabledButton);
34916            this.dom.collapseButton.addClass(this.classes.rotated);
34917            this.dom.topRow.one('click.dtsp', function () { return _this.show(); });
34918            this.dom.topRow.trigger('collapse.dtsps');
34919        };
34920        /**
34921         * Strips all of the SearchPanes elements from the document and turns all of the listeners for the buttons off
34922         */
34923        SearchPane.prototype.destroy = function () {
34924            if (this.s.dtPane) {
34925                this.s.dtPane.off('.dtsp');
34926            }
34927            this.s.dt.off('.dtsp');
34928            this.dom.clear.off('.dtsp');
34929            this.dom.nameButton.off('.dtsp');
34930            this.dom.countButton.off('.dtsp');
34931            this.dom.searchButton.off('.dtsp');
34932            this.dom.collapseButton.off('.dtsp');
34933            $$5(this.s.dt.table().node()).off('.dtsp');
34934            this.dom.container.detach();
34935            var searchIdx = $$5.fn.dataTable.ext.search.indexOf(this.s.searchFunction);
34936            while (searchIdx !== -1) {
34937                $$5.fn.dataTable.ext.search.splice(searchIdx, 1);
34938                searchIdx = $$5.fn.dataTable.ext.search.indexOf(this.s.searchFunction);
34939            }
34940            // If the datatables have been defined for the panes then also destroy these
34941            if (this.s.dtPane) {
34942                this.s.dtPane.destroy();
34943            }
34944            this.s.listSet = false;
34945        };
34946        /**
34947         * Getting the legacy message is a little complex due a legacy parameter
34948         */
34949        SearchPane.prototype.emptyMessage = function () {
34950            var def = this.c.i18n.emptyMessage;
34951            // Legacy parameter support
34952            if (this.c.emptyMessage) {
34953                def = this.c.emptyMessage;
34954            }
34955            // Override per column
34956            if (this.s.colOpts.emptyMessage !== false && this.s.colOpts.emptyMessage !== null) {
34957                def = this.s.colOpts.emptyMessage;
34958            }
34959            return this.s.dt.i18n('searchPanes.emptyMessage', def);
34960        };
34961        /**
34962         * Updates the number of filters that have been applied in the title
34963         */
34964        SearchPane.prototype.getPaneCount = function () {
34965            return this.s.dtPane ?
34966                this.s.dtPane.rows({ selected: true }).data().toArray().length :
34967                0;
34968        };
34969        /**
34970         * Rebuilds the panes from the start having deleted the old ones
34971         *
34972         * @param? dataIn data to be used in buildPane
34973         * @param? maintainSelection Whether the current selections are to be maintained over rebuild
34974         */
34975        SearchPane.prototype.rebuildPane = function (dataIn, maintainSelection) {
34976            if (dataIn === void 0) { dataIn = null; }
34977            if (maintainSelection === void 0) { maintainSelection = false; }
34978            this.clearData();
34979            var selectedRows = [];
34980            this.s.serverSelect = [];
34981            var prevEl = null;
34982            // When rebuilding strip all of the HTML Elements out of the container and start from scratch
34983            if (this.s.dtPane) {
34984                if (maintainSelection) {
34985                    if (!this.s.dt.page.info().serverSide) {
34986                        selectedRows = this.s.dtPane.rows({ selected: true }).data().toArray();
34987                    }
34988                    else {
34989                        this.s.serverSelect = this.s.dtPane.rows({ selected: true }).data().toArray();
34990                    }
34991                }
34992                this.s.dtPane.clear().destroy();
34993                prevEl = this.dom.container.prev();
34994                this.destroy();
34995                this.s.dtPane = undefined;
34996                $$5.fn.dataTable.ext.search.push(this.s.searchFunction);
34997            }
34998            this.dom.container.removeClass(this.classes.hidden);
34999            this.s.displayed = false;
35000            this._buildPane(!this.s.dt.page.info().serverSide ?
35001                selectedRows :
35002                this.s.serverSelect, dataIn, prevEl);
35003            return this;
35004        };
35005        /**
35006         * Resizes the pane based on the layout that is passed in
35007         *
35008         * @param layout the layout to be applied to this pane
35009         */
35010        SearchPane.prototype.resize = function (layout) {
35011            this.c.layout = layout;
35012            this.dom.container
35013                .removeClass()
35014                .addClass(this.classes.show)
35015                .addClass(this.classes.container)
35016                .addClass(this.s.colOpts.className)
35017                .addClass(this.classes.layout +
35018                (parseInt(layout.split('-')[1], 10) < 10 ?
35019                    layout :
35020                    layout.split('-')[0] + '-9'))
35021                .addClass(this.s.customPaneSettings !== null && this.s.customPaneSettings.className
35022                ? this.s.customPaneSettings.className
35023                : '');
35024            this.adjustTopRow();
35025        };
35026        /**
35027         * Sets the listeners for the pane.
35028         *
35029         * Having it in it's own function makes it easier to only set them once
35030         */
35031        SearchPane.prototype.setListeners = function () {
35032            var _this = this;
35033            if (!this.s.dtPane) {
35034                return;
35035            }
35036            // When an item is selected on the pane, add these to the array which holds selected items.
35037            // Custom search will perform.
35038            this.s.dtPane.off('select.dtsp').on('select.dtsp', function () {
35039                clearTimeout(_this.s.deselectTimeout);
35040                _this._updateSelection(!_this.s.updating);
35041                _this.dom.clear.removeClass(_this.classes.disabledButton).removeAttr('disabled');
35042            });
35043            // When an item is deselected on the pane, re add the currently selected items to the array
35044            // which holds selected items. Custom search will be performed.
35045            this.s.dtPane.off('deselect.dtsp').on('deselect.dtsp', function () {
35046                _this.s.deselectTimeout = setTimeout(function () {
35047                    _this._updateSelection(true);
35048                    if (_this.s.dtPane.rows({ selected: true }).data().toArray().length === 0) {
35049                        _this.dom.clear.addClass(_this.classes.disabledButton).attr('disabled', 'true');
35050                    }
35051                }, 50);
35052            });
35053            // If we attempty to turn off this event then it will ruin behaviour in other panes
35054            //  so need to make sure that it is only done once
35055            if (this.s.firstSet) {
35056                this.s.firstSet = false;
35057                // When saving the state store all of the selected rows for preselection next time around
35058                this.s.dt.on('stateSaveParams.dtsp', function (e, settings, data) {
35059                    // If the data being passed in is empty then state clear must have occured
35060                    // so clear the panes state as well
35061                    if ($$5.isEmptyObject(data)) {
35062                        _this.s.dtPane.state.clear();
35063                        return;
35064                    }
35065                    var bins;
35066                    var order;
35067                    var selected = [];
35068                    var collapsed;
35069                    var searchTerm;
35070                    var arrayFilter;
35071                    // Get all of the data needed for the state save from the pane
35072                    if (_this.s.dtPane) {
35073                        selected = _this.s.dtPane
35074                            .rows({ selected: true })
35075                            .data()
35076                            .map(function (item) { return item.filter.toString(); })
35077                            .toArray();
35078                        searchTerm = _this.dom.searchBox.val();
35079                        order = _this.s.dtPane.order();
35080                        bins = _this.s.rowData.binsOriginal;
35081                        arrayFilter = _this.s.rowData.arrayOriginal;
35082                        collapsed = _this.dom.collapseButton.hasClass(_this.classes.rotated);
35083                    }
35084                    if (data.searchPanes === undefined) {
35085                        data.searchPanes = {};
35086                    }
35087                    if (data.searchPanes.panes === undefined) {
35088                        data.searchPanes.panes = [];
35089                    }
35090                    for (var i = 0; i < data.searchPanes.panes.length; i++) {
35091                        if (data.searchPanes.panes[i].id === _this.s.index) {
35092                            data.searchPanes.panes.splice(i, 1);
35093                            i--;
35094                        }
35095                    }
35096                    // Add the panes data to the state object
35097                    data.searchPanes.panes.push({
35098                        arrayFilter: arrayFilter,
35099                        bins: bins,
35100                        collapsed: collapsed,
35101                        id: _this.s.index,
35102                        order: order,
35103                        searchTerm: searchTerm,
35104                        selected: selected
35105                    });
35106                });
35107            }
35108            this.s.dtPane.off('user-select.dtsp').on('user-select.dtsp', function (e, _dt, type, cell, originalEvent) {
35109                originalEvent.stopPropagation();
35110            });
35111            this.s.dtPane.off('draw.dtsp').on('draw.dtsp', function () { return _this.adjustTopRow(); });
35112            // When the button to order by the name of the options is clicked then
35113            //  change the ordering to whatever it isn't currently
35114            this.dom.nameButton.off('click.dtsp').on('click.dtsp', function () {
35115                var currentOrder = _this.s.dtPane.order()[0][1];
35116                _this.s.dtPane.order([0, currentOrder === 'asc' ? 'desc' : 'asc']).draw();
35117                // This state save is required so that the ordering of the panes is maintained
35118                _this.s.dt.state.save();
35119            });
35120            // When the button to order by the number of entries in the column is clicked then
35121            //  change the ordering to whatever it isn't currently
35122            this.dom.countButton.off('click.dtsp').on('click.dtsp', function () {
35123                var currentOrder = _this.s.dtPane.order()[0][1];
35124                _this.s.dtPane.order([1, currentOrder === 'asc' ? 'desc' : 'asc']).draw();
35125                // This state save is required so that the ordering of the panes is maintained
35126                _this.s.dt.state.save();
35127            });
35128            // When the button to order by the number of entries in the column is clicked then
35129            //  change the ordering to whatever it isn't currently
35130            this.dom.collapseButton.off('click.dtsp').on('click.dtsp', function (e) {
35131                e.stopPropagation();
35132                var container = $$5(_this.s.dtPane.table().container());
35133                // Toggle the classes
35134                container.toggleClass(_this.classes.hidden);
35135                _this.dom.topRow.toggleClass(_this.classes.bordered);
35136                _this.dom.nameButton.toggleClass(_this.classes.disabledButton);
35137                _this.dom.countButton.toggleClass(_this.classes.disabledButton);
35138                _this.dom.searchButton.toggleClass(_this.classes.disabledButton);
35139                _this.dom.collapseButton.toggleClass(_this.classes.rotated);
35140                if (container.hasClass(_this.classes.hidden)) {
35141                    _this.dom.topRow.on('click.dtsp', function () { return _this.dom.collapseButton.click(); });
35142                }
35143                else {
35144                    _this.dom.topRow.off('click.dtsp');
35145                }
35146                _this.s.dt.state.save();
35147                _this.dom.topRow.trigger('collapse.dtsps');
35148            });
35149            // When the clear button is clicked reset the pane
35150            this.dom.clear.off('click.dtsp').on('click.dtsp', function () {
35151                var searches = _this.dom.container.find('.' + _this.classes.search.replace(/ /g, '.'));
35152                searches.each(function () {
35153                    // set the value of the search box to be an empty string and then search on that, effectively reseting
35154                    $$5(this).val('').trigger('input');
35155                });
35156                _this.clearPane();
35157            });
35158            // When the search button is clicked then draw focus to the search box
35159            this.dom.searchButton.off('click.dtsp').on('click.dtsp', function () { return _this.dom.searchBox.focus(); });
35160            // When a character is inputted into the searchbox search the pane for matching values.
35161            // Doing it this way means that no button has to be clicked to trigger a search, it is done asynchronously
35162            this.dom.searchBox.off('click.dtsp').on('input.dtsp', function () {
35163                var searchval = _this.dom.searchBox.val();
35164                _this.s.dtPane.search(searchval).draw();
35165                if (typeof searchval === 'string' &&
35166                    (searchval.length > 0 ||
35167                        searchval.length === 0 && _this.s.dtPane.rows({ selected: true }).data().toArray().length > 0)) {
35168                    _this.dom.clear.removeClass(_this.classes.disabledButton).removeAttr('disabled');
35169                }
35170                else {
35171                    _this.dom.clear.addClass(_this.classes.disabledButton).attr('disabled', 'true');
35172                }
35173                // This state save is required so that the searching on the panes is maintained
35174                _this.s.dt.state.save();
35175            });
35176            this.s.dtPane.select.style(this.s.colOpts.dtOpts && this.s.colOpts.dtOpts.select && this.s.colOpts.dtOpts.select.style
35177                ? this.s.colOpts.dtOpts.select.style
35178                : this.c.dtOpts && this.c.dtOpts.select && this.c.dtOpts.select.style
35179                    ? this.c.dtOpts.select.style
35180                    : 'os');
35181        };
35182        /**
35183         * Populates the SearchPane based off of the data that has been recieved from the server
35184         *
35185         * This method is overriden by SearchPaneST
35186         *
35187         * @param dataIn The data that has been sent from the server
35188         */
35189        SearchPane.prototype._serverPopulate = function (dataIn) {
35190            if (dataIn.tableLength) {
35191                this.s.tableLength = dataIn.tableLength;
35192                this.s.rowData.totalOptions = this.s.tableLength;
35193            }
35194            else if (this.s.tableLength === null || this.s.dt.rows()[0].length > this.s.tableLength) {
35195                this.s.tableLength = this.s.dt.rows()[0].length;
35196                this.s.rowData.totalOptions = this.s.tableLength;
35197            }
35198            var colTitle = this.s.dt.column(this.s.index).dataSrc();
35199            // If there is SP data for this column add it to the data array and bin
35200            if (dataIn.searchPanes.options[colTitle]) {
35201                for (var _i = 0, _a = dataIn.searchPanes.options[colTitle]; _i < _a.length; _i++) {
35202                    var dataPoint = _a[_i];
35203                    this.s.rowData.arrayFilter.push({
35204                        display: dataPoint.label,
35205                        filter: dataPoint.value,
35206                        sort: dataPoint.label,
35207                        type: dataPoint.label
35208                    });
35209                    this.s.rowData.bins[dataPoint.value] = dataPoint.total;
35210                }
35211            }
35212            var binLength = Object.keys(this.s.rowData.bins).length;
35213            var uniqueRatio = this._uniqueRatio(binLength, this.s.tableLength);
35214            // Don't show the pane if there isnt enough variance in the data, or there is only 1 entry for that pane
35215            if (this.s.displayed === false &&
35216                ((this.s.colOpts.show === undefined && this.s.colOpts.threshold === null ?
35217                    uniqueRatio > this.c.threshold :
35218                    uniqueRatio > this.s.colOpts.threshold) ||
35219                    this.s.colOpts.show !== true && binLength <= 1)) {
35220                this.dom.container.addClass(this.classes.hidden);
35221                this.s.displayed = false;
35222                return;
35223            }
35224            // Store the original data
35225            this.s.rowData.arrayOriginal = this.s.rowData.arrayFilter;
35226            this.s.rowData.binsOriginal = this.s.rowData.bins;
35227            // Flag this pane as being displayed
35228            this.s.displayed = true;
35229        };
35230        /**
35231         * Expands the pane from the collapsed state
35232         */
35233        SearchPane.prototype.show = function () {
35234            if (!this.s.displayed) {
35235                return;
35236            }
35237            this.dom.topRow.removeClass(this.classes.bordered);
35238            this.dom.nameButton.removeClass(this.classes.disabledButton);
35239            this.dom.countButton.removeClass(this.classes.disabledButton);
35240            this.dom.searchButton.removeClass(this.classes.disabledButton);
35241            this.dom.collapseButton.removeClass(this.classes.rotated);
35242            $$5(this.s.dtPane.table().container()).removeClass(this.classes.hidden);
35243            this.dom.topRow.trigger('collapse.dtsps');
35244        };
35245        /**
35246         * Finds the ratio of the number of different options in the table to the number of rows
35247         *
35248         * @param bins the number of different options in the table
35249         * @param rowCount the total number of rows in the table
35250         * @returns {number} returns the ratio
35251         */
35252        SearchPane.prototype._uniqueRatio = function (bins, rowCount) {
35253            if (rowCount > 0 &&
35254                (this.s.rowData.totalOptions > 0 && !this.s.dt.page.info().serverSide ||
35255                    this.s.dt.page.info().serverSide && this.s.tableLength > 0)) {
35256                return bins / this.s.rowData.totalOptions;
35257            }
35258            return 1;
35259        };
35260        /**
35261         * Updates the panes if one of the options to do so has been set to true
35262         * rather than the filtered message when using viewTotal.
35263         */
35264        SearchPane.prototype.updateTable = function () {
35265            var selectedRows = this.s.dtPane.rows({ selected: true }).data().toArray().map(function (el) { return el.filter; });
35266            this.s.selections = selectedRows;
35267            this._searchExtras();
35268        };
35269        /**
35270         * Adds the custom options to the pane
35271         *
35272         * @returns {Array} Returns the array of rows which have been added to the pane
35273         */
35274        SearchPane.prototype._getComparisonRows = function () {
35275            // Find the appropriate options depending on whether this is a pane for a specific column or a custom pane
35276            var options = this.s.colOpts.options
35277                ? this.s.colOpts.options
35278                : this.s.customPaneSettings && this.s.customPaneSettings.options
35279                    ? this.s.customPaneSettings.options
35280                    : undefined;
35281            if (options === undefined) {
35282                return;
35283            }
35284            var allRows = this.s.dt.rows();
35285            var tableValsTotal = allRows.data().toArray();
35286            var rows = [];
35287            // Clear all of the other rows from the pane, only custom options are to be displayed when they are defined
35288            this.s.dtPane.clear();
35289            this.s.indexes = [];
35290            for (var _i = 0, options_1 = options; _i < options_1.length; _i++) {
35291                var comp = options_1[_i];
35292                // Initialise the object which is to be placed in the row
35293                var insert = comp.label !== '' ?
35294                    comp.label :
35295                    this.emptyMessage();
35296                var comparisonObj = {
35297                    className: comp.className,
35298                    display: insert,
35299                    filter: typeof comp.value === 'function' ? comp.value : [],
35300                    sort: insert,
35301                    total: 0,
35302                    type: insert
35303                };
35304                // If a custom function is in place
35305                if (typeof comp.value === 'function') {
35306                    // Count the number of times the function evaluates to true for the original data in the Table
35307                    for (var i = 0; i < tableValsTotal.length; i++) {
35308                        if (comp.value.call(this.s.dt, tableValsTotal[i], allRows[0][i])) {
35309                            comparisonObj.total++;
35310                        }
35311                    }
35312                    // Update the comparisonObj
35313                    if (typeof comparisonObj.filter !== 'function') {
35314                        comparisonObj.filter.push(comp.filter);
35315                    }
35316                }
35317                rows.push(this.addRow(comparisonObj.display, comparisonObj.filter, comparisonObj.sort, comparisonObj.type, comparisonObj.className, comparisonObj.total));
35318            }
35319            return rows;
35320        };
35321        SearchPane.prototype._getMessage = function (row) {
35322            return this.s.dt.i18n('searchPanes.count', this.c.i18n.count).replace(/{total}/g, row.total);
35323        };
35324        /**
35325         * Overridden in SearchPaneViewTotal and SearchPaneCascade to get the number of times a specific value is shown
35326         *
35327         * Here it is blanked so that it takes no action
35328         *
35329         * @param filter The filter value
35330         * @returns undefined
35331         */
35332        // eslint-disable-next-line @typescript-eslint/no-unused-vars
35333        SearchPane.prototype._getShown = function (filter) {
35334            return undefined;
35335        };
35336        /**
35337         * Get's the pane config appropriate to this class
35338         *
35339         * @returns The config needed to create a pane of this type
35340         */
35341        SearchPane.prototype._getPaneConfig = function () {
35342            var _this = this;
35343            // eslint-disable-next-line no-extra-parens
35344            var haveScroller = dataTable$2.Scroller;
35345            var langOpts = this.s.dt.settings()[0].oLanguage;
35346            langOpts.url = undefined;
35347            langOpts.sUrl = undefined;
35348            return {
35349                columnDefs: [
35350                    {
35351                        className: 'dtsp-nameColumn',
35352                        data: 'display',
35353                        render: function (data, type, row) {
35354                            if (type === 'sort') {
35355                                return row.sort;
35356                            }
35357                            else if (type === 'type') {
35358                                return row.type;
35359                            }
35360                            var message = _this._getMessage(row);
35361                            // We are displaying the count in the same columne as the name of the search option.
35362                            // This is so that there is not need to call columns.adjust()
35363                            //  which in turn speeds up the code
35364                            var pill = '<span class="' + _this.classes.pill + '">' + message + '</span>';
35365                            if (!_this.c.viewCount || !_this.s.colOpts.viewCount) {
35366                                pill = '';
35367                            }
35368                            if (type === 'filter') {
35369                                return typeof data === 'string' && data.match(/<[^>]*>/) !== null ?
35370                                    data.replace(/<[^>]*>
35370/g, '') :
35371                                    data;
35372                            }
35373                            return '<div class="' + _this.classes.nameCont + '"><span title="' +
35374                                (typeof data === 'string' && data.match(/<[^>]*>/) !== null ?
35375                                    data.replace(/<[^>]*>/g, '') :
35376                                    data) +
35377                                '" class="' + _this.classes.name + '">' +
35378                                data + '</span>' +
35379                                pill + '</div>';
35380                        },
35381                        targets: 0,
35382                        // Accessing the private datatables property to set type based on the original table.
35383                        // This is null if not defined by the user, meaning that automatic type detection
35384                        //  would take place
35385                        type: this.s.dt.settings()[0].aoColumns[this.s.index] ?
35386                            this.s.dt.settings()[0].aoColumns[this.s.index]._sManualType :
35387                            null
35388                    },
35389                    {
35390                        className: 'dtsp-countColumn ' + this.classes.badgePill,
35391                        data: 'total',
35392                        searchable: false,
35393                        targets: 1,
35394                        visible: false
35395                    }
35396                ],
35397                deferRender: true,
35398                dom: 't',
35399                info: false,
35400                language: langOpts,
35401                paging: haveScroller ? true : false,
35402                scrollX: false,
35403                scrollY: '200px',
35404                scroller: haveScroller ? true : false,
35405                select: true,
35406                stateSave: this.s.dt.settings()[0].oFeatures.bStateSave ? true : false
35407            };
35408        };
35409        /**
35410         * This method allows for changes to the panes and table to be made when a selection or a deselection occurs
35411         */
35412        SearchPane.prototype._makeSelection = function () {
35413            this.updateTable();
35414            this.s.updating = true;
35415            this.s.dt.draw(false);
35416            this.s.updating = false;
35417        };
35418        /**
35419         * Populates an array with all of the data for the table
35420         *
35421         * @param rowIdx The current row index to be compared
35422         * @param arrayFilter The array that is to be populated with row Details
35423         * @param settings The DataTable settings object
35424         * @param bins The bins object that is to be populated with the row counts
35425         */
35426        SearchPane.prototype._populatePaneArray = function (rowIdx, arrayFilter, settings, bins) {
35427            if (bins === void 0) { bins = this.s.rowData.bins; }
35428            // Retrieve the rendered data from the cell using the fnGetCellData function
35429            // rather than the cell().render API method for optimisation
35430            if (typeof this.s.colOpts.orthogonal === 'string') {
35431                var rendered = settings.oApi._fnGetCellData(settings, rowIdx, this.s.index, this.s.colOpts.orthogonal);
35432                this.s.rowData.filterMap.set(rowIdx, rendered);
35433                this._addOption(rendered, rendered, rendered, rendered, arrayFilter, bins);
35434                this.s.rowData.totalOptions++;
35435            }
35436            else {
35437                var filter = settings.oApi._fnGetCellData(settings, rowIdx, this.s.index, this.s.colOpts.orthogonal.search);
35438                // Null and empty string are to be considered the same value
35439                if (filter === null) {
35440                    filter = '';
35441                }
35442                if (typeof filter === 'string') {
35443                    filter = filter.replace(/<[^>]*>/g, '');
35444                }
35445                this.s.rowData.filterMap.set(rowIdx, filter);
35446                if (!bins[filter]) {
35447                    bins[filter] = 1;
35448                    this._addOption(filter, settings.oApi._fnGetCellData(settings, rowIdx, this.s.index, this.s.colOpts.orthogonal.display), settings.oApi._fnGetCellData(settings, rowIdx, this.s.index, this.s.colOpts.orthogonal.sort), settings.oApi._fnGetCellData(settings, rowIdx, this.s.index, this.s.colOpts.orthogonal.type), arrayFilter, bins);
35449                    this.s.rowData.totalOptions++;
35450                }
35451                else {
35452                    bins[filter]++;
35453                    this.s.rowData.totalOptions++;
35454                }
35455            }
35456        };
35457        /**
35458         * Reloads all of the previous selects into the panes
35459         *
35460         * @param loadedFilter The loaded filters from a previous state
35461         */
35462        SearchPane.prototype._reloadSelect = function (loadedFilter) {
35463            // If the state was not saved don't selected any
35464            if (loadedFilter === undefined) {
35465                return;
35466            }
35467            var idx;
35468            // For each pane, check that the loadedFilter list exists and is not null,
35469            // find the id of each search item and set it to be selected.
35470            for (var i = 0; i < loadedFilter.searchPanes.panes.length; i++) {
35471                if (loadedFilter.searchPanes.panes[i].id === this.s.index) {
35472                    idx = i;
35473                    break;
35474                }
35475            }
35476            if (idx) {
35477                var table = this.s.dtPane;
35478                var rows = table.rows({ order: 'index' }).data().map(function (item) { return item.filter !== null ?
35479                    item.filter.toString() :
35480                    null; }).toArray();
35481                for (var _i = 0, _a = loadedFilter.searchPanes.panes[idx].selected; _i < _a.length; _i++) {
35482                    var filter = _a[_i];
35483                    var id = -1;
35484                    if (filter !== null) {
35485                        id = rows.indexOf(filter.toString());
35486                    }
35487                    if (id > -1) {
35488                        this.s.serverSelecting = true;
35489                        table.row(id).select();
35490                        this.s.serverSelecting = false;
35491                    }
35492                }
35493            }
35494        };
35495        /**
35496         * Notes the rows that have been selected within this pane and stores them internally
35497         *
35498         * @param notUpdating Whether the panes are updating themselves or not
35499         */
35500        SearchPane.prototype._updateSelection = function (notUpdating) {
35501            var _this = this;
35502            var settings = this.s.dt.settings()[0];
35503            var oApi = settings.oApi;
35504            var run = function () {
35505                _this.s.scrollTop = $$5(_this.s.dtPane.table().node()).parent()[0].scrollTop;
35506                if (_this.s.dt.page.info().serverSide && !_this.s.updating) {
35507                    if (!_this.s.serverSelecting) {
35508                        _this.s.serverSelect = _this.s.dtPane.rows({ selected: true }).data().toArray();
35509                        _this.s.dt.draw(false);
35510                    }
35511                }
35512                else if (notUpdating) {
35513                    _this._makeSelection();
35514                }
35515                oApi._fnProcessingDisplay(settings, false);
35516            };
35517            // If the processing display is enabled, we need to allow the browser
35518            // to draw it before performing our calculations
35519            if (settings.oFeatures.bProcessing) {
35520                oApi._fnProcessingDisplay(settings, true);
35521                setTimeout(run, 1);
35522            }
35523            else {
35524                run();
35525            }
35526        };
35527        /**
35528         * Takes in potentially undetected rows and adds them to the array if they are not yet featured
35529         *
35530         * @param filter the filter value of the potential row
35531         * @param display the display value of the potential row
35532         * @param sort the sort value of the potential row
35533         * @param type the type value of the potential row
35534         * @param arrayFilter the array to be populated
35535         * @param bins the bins to be populated
35536         */
35537        SearchPane.prototype._addOption = function (filter, display, sort, type, arrayFilter, bins) {
35538            // If the filter is an array then take a note of this, and add the elements to the arrayFilter array
35539            if (Array.isArray(filter) || filter instanceof dataTable$2.Api) {
35540                // Convert to an array so that we can work with it
35541                if (filter instanceof dataTable$2.Api) {
35542                    filter = filter.toArray();
35543                    display = display.toArray();
35544                }
35545                if (filter.length === display.length) {
35546                    for (var i = 0; i < filter.length; i++) {
35547                        // If we haven't seen this row before add it
35548                        if (!bins[filter[i]]) {
35549                            bins[filter[i]] = 1;
35550                            arrayFilter.push({
35551                                display: display[i],
35552                                filter: filter[i],
35553                                sort: sort[i],
35554                                type: type[i]
35555                            });
35556                        }
35557                        // Otherwise just increment the count
35558                        else {
35559                            bins[filter[i]]++;
35560                        }
35561                        this.s.rowData.totalOptions++;
35562                    }
35563                    return;
35564                }
35565                throw new Error('display and filter not the same length');
35566            }
35567            // If the values were affected by othogonal data and are not an array then check if it is already present
35568            else if (typeof this.s.colOpts.orthogonal === 'string') {
35569                if (!bins[filter]) {
35570                    bins[filter] = 1;
35571                    arrayFilter.push({
35572                        display: display,
35573                        filter: filter,
35574                        sort: sort,
35575                        type: type
35576                    });
35577                    this.s.rowData.totalOptions++;
35578                }
35579                else {
35580                    bins[filter]++;
35581                    this.s.rowData.totalOptions++;
35582                }
35583            }
35584            // Otherwise we must just be adding an option
35585            else {
35586                arrayFilter.push({
35587                    display: display,
35588                    filter: filter,
35589                    sort: sort,
35590                    type: type
35591                });
35592            }
35593        };
35594        /**
35595         * Method to construct the actual pane.
35596         *
35597         * @param selectedRows previously selected Rows to be reselected
35598         * @param dataIn Data that should be used to populate this pane
35599         * @param prevEl Reference to the previous element, used to ensure insert is in the correct location
35600         * @returns boolean to indicate whether this pane was the last one to have a selection made
35601         */
35602        SearchPane.prototype._buildPane = function (selectedRows, dataIn, prevEl) {
35603            var _this = this;
35604            if (selectedRows === void 0) { selectedRows = []; }
35605            if (dataIn === void 0) { dataIn = null; }
35606            if (prevEl === void 0) { prevEl = null; }
35607            // Aliases
35608            this.s.selections = [];
35609            // Other Variables
35610            var loadedFilter = this.s.dt.state.loaded();
35611            // If the listeners have not been set yet then using the latest state may result in funny errors
35612            if (this.s.listSet) {
35613                loadedFilter = this.s.dt.state();
35614            }
35615            // If it is not a custom pane in place
35616            if (this.s.colExists) {
35617                var idx = -1;
35618                if (loadedFilter && loadedFilter.searchPanes && loadedFilter.searchPanes.panes) {
35619                    for (var i = 0; i < loadedFilter.searchPanes.panes.length; i++) {
35620                        if (loadedFilter.searchPanes.panes[i].id === this.s.index) {
35621                            idx = i;
35622                            break;
35623                        }
35624                    }
35625                }
35626                // Perform checks that do not require populate pane to run
35627                if ((this.s.colOpts.show === false ||
35628                    this.s.colOpts.show !== undefined && this.s.colOpts.show !== true) &&
35629                    idx === -1) {
35630                    this.dom.container.addClass(this.classes.hidden);
35631                    this.s.displayed = false;
35632                    return false;
35633                }
35634                else if (this.s.colOpts.show === true || idx !== -1) {
35635                    this.s.displayed = true;
35636                }
35637                if (!this.s.dt.page.info().serverSide &&
35638                    (!dataIn ||
35639                        !dataIn.searchPanes ||
35640                        !dataIn.searchPanes.options)) {
35641                    // Only run populatePane if the data has not been collected yet
35642                    if (this.s.rowData.arrayFilter.length === 0) {
35643                        this.s.rowData.totalOptions = 0;
35644                        this._populatePane();
35645                        this.s.rowData.arrayOriginal = this.s.rowData.arrayFilter;
35646                        this.s.rowData.binsOriginal = this.s.rowData.bins;
35647                    }
35648                    var binLength = Object.keys(this.s.rowData.binsOriginal).length;
35649                    var uniqueRatio = this._uniqueRatio(binLength, this.s.dt.rows()[0].length);
35650                    // Don't show the pane if there isn't enough variance in the data, or there is only 1 entry
35651                    //  for that pane
35652                    if (this.s.displayed === false &&
35653                        ((this.s.colOpts.show === undefined && this.s.colOpts.threshold === null ?
35654                            uniqueRatio > this.c.threshold :
35655                            uniqueRatio > this.s.colOpts.threshold) ||
35656                            this.s.colOpts.show !== true && binLength <= 1)) {
35657                        this.dom.container.addClass(this.classes.hidden);
35658                        this.s.displayed = false;
35659                        return;
35660                    }
35661                    this.dom.container.addClass(this.classes.show);
35662                    this.s.displayed = true;
35663                }
35664                else if (dataIn && dataIn.searchPanes && dataIn.searchPanes.options) {
35665                    this._serverPopulate(dataIn);
35666                }
35667            }
35668            else {
35669                this.s.displayed = true;
35670            }
35671            // If the variance is accceptable then display the search pane
35672            this._displayPane();
35673            if (!this.s.listSet) {
35674                // Here, when the state is loaded if the data object on the original table is empty,
35675                //  then a state.clear() must have occurred, so delete all of the panes tables state objects too.
35676                this.dom.dtP.on('stateLoadParams.dtsp', function (e, settings, data) {
35677                    if ($$5.isEmptyObject(_this.s.dt.state.loaded())) {
35678                        $$5.each(data, function (index) {
35679                            delete data[index];
35680                        });
35681                    }
35682                });
35683            }
35684            // Add the container to the document in its original location
35685            if (prevEl !== null && this.dom.panesContainer.has(prevEl).length > 0) {
35686                this.dom.container.insertAfter(prevEl);
35687            }
35688            else {
35689                this.dom.panesContainer.prepend(this.dom.container);
35690            }
35691            // Declare the datatable for the pane
35692            var errMode = $$5.fn.dataTable.ext.errMode;
35693            $$5.fn.dataTable.ext.errMode = 'none';
35694            // eslint-disable-next-line no-extra-parens
35695            this.s.dtPane = this.dom.dtP.DataTable($$5.extend(true, this._getPaneConfig(), this.c.dtOpts, this.s.colOpts ? this.s.colOpts.dtOpts : {}, this.s.colOpts.options || !this.s.colExists ?
35696                {
35697                    createdRow: function (row, data) {
35698                        $$5(row).addClass(data.className);
35699                    }
35700                } :
35701                undefined, this.s.customPaneSettings !== null && this.s.customPaneSettings.dtOpts ?
35702                this.s.customPaneSettings.dtOpts :
35703                {}, $$5.fn.dataTable.versionCheck('2')
35704                ? {
35705                    layout: {
35706                        bottomLeft: null,
35707                        bottomRight: null,
35708                        topLeft: null,
35709                        topRight: null
35710                    }
35711                }
35712                : {}));
35713            this.dom.dtP.addClass(this.classes.table);
35714            // Getting column titles is a little messy
35715            var headerText = 'Custom Pane';
35716            if (this.s.customPaneSettings && this.s.customPaneSettings.header) {
35717                headerText = this.s.customPaneSettings.header;
35718            }
35719            else if (this.s.colOpts.header) {
35720                headerText = this.s.colOpts.header;
35721            }
35722            else if (this.s.colExists) {
35723                headerText = $$5.fn.dataTable.versionCheck('2')
35724                    ? this.s.dt.column(this.s.index).title()
35725                    : this.s.dt.settings()[0].aoColumns[this.s.index].sTitle;
35726            }
35727            headerText = this._escapeHTML(headerText);
35728            this.dom.searchBox.attr('placeholder', headerText);
35729            // As the pane table is not in the document yet we must initialise select ourselves
35730            // eslint-disable-next-line no-extra-parens
35731            $$5.fn.dataTable.select.init(this.s.dtPane);
35732            $$5.fn.dataTable.ext.errMode = errMode;
35733            // If it is not a custom pane
35734            if (this.s.colExists) {
35735                // Add all of the search options to the pane
35736                for (var i = 0, ien = this.s.rowData.arrayFilter.length; i < ien; i++) {
35737                    if (this.s.dt.page.info().serverSide) {
35738                        var row = this.addRow(this.s.rowData.arrayFilter[i].display, this.s.rowData.arrayFilter[i].filter, this.s.rowData.arrayFilter[i].sort, this.s.rowData.arrayFilter[i].type);
35739                        for (var _i = 0, _a = this.s.serverSelect; _i < _a.length; _i++) {
35740                            var option = _a[_i];
35741                            if (option.filter === this.s.rowData.arrayFilter[i].filter) {
35742                                this.s.serverSelecting = true;
35743                                row.select();
35744                                this.s.serverSelecting = false;
35745                            }
35746                        }
35747                    }
35748                    else if (!this.s.dt.page.info().serverSide && this.s.rowData.arrayFilter[i]) {
35749                        this.addRow(this.s.rowData.arrayFilter[i].display, this.s.rowData.arrayFilter[i].filter, this.s.rowData.arrayFilter[i].sort, this.s.rowData.arrayFilter[i].type);
35750                    }
35751                    else if (!this.s.dt.page.info().serverSide) {
35752                        // Just pass an empty string as the message will be calculated based on that in addRow()
35753                        this.addRow('', '', '', '');
35754                    }
35755                }
35756            }
35757            // eslint-disable-next-line no-extra-parens
35758            dataTable$2.select.init(this.s.dtPane);
35759            // If there are custom options set or it is a custom pane then get them
35760            if (this.s.colOpts.options ||
35761                this.s.customPaneSettings && this.s.customPaneSettings.options) {
35762                this._getComparisonRows();
35763            }
35764            // Display the pane
35765            this.s.dtPane.draw();
35766            this.s.dtPane.table().node().parentNode.scrollTop = this.s.scrollTop;
35767            this.adjustTopRow();
35768            this.setListeners();
35769            this.s.listSet = true;
35770            for (var _b = 0, selectedRows_1 = selectedRows; _b < selectedRows_1.length; _b++) {
35771                var selection = selectedRows_1[_b];
35772                if (selection) {
35773                    for (var _c = 0, _d = this.s.dtPane.rows().indexes().toArray(); _c < _d.length; _c++) {
35774                        var row = _d[_c];
35775                        if (this.s.dtPane.row(row).data() &&
35776                            selection.filter === this.s.dtPane.row(row).data().filter) {
35777                            // If this is happening when serverSide processing is happening then
35778                            //  different behaviour is needed
35779                            if (this.s.dt.page.info().serverSide) {
35780                                this.s.serverSelecting = true;
35781                                this.s.dtPane.row(row).select();
35782                                this.s.serverSelecting = false;
35783                            }
35784                            else {
35785                                this.s.dtPane.row(row).select();
35786                            }
35787                        }
35788                    }
35789                }
35790            }
35791            //  If SSP and the table is ready, apply the search for the pane
35792            if (this.s.dt.page.info().serverSide) {
35793                this.s.dtPane.search(this.dom.searchBox.val()).draw();
35794            }
35795            if ((this.c.initCollapsed && this.s.colOpts.initCollapsed !== false ||
35796                this.s.colOpts.initCollapsed) &&
35797                (this.c.collapse && this.s.colOpts.collapse !== false ||
35798                    this.s.colOpts.collapse)) {
35799                // If the pane has not initialised yet then we need to wait for it to do so before collapsing
35800                // Otherwise the container that the class is added to does not exist
35801                if (this.s.dtPane.settings()[0]._bInitComplete) {
35802                    this.collapse();
35803                }
35804                else {
35805                    this.s.dtPane.one('init', function () { return _this.collapse(); });
35806                }
35807            }
35808            // Reload the selection, searchbox entry and ordering from the previous state
35809            // Need to check here if SSP that this is the first draw, otherwise it will infinite loop
35810            if (loadedFilter &&
35811                loadedFilter.searchPanes &&
35812                loadedFilter.searchPanes.panes &&
35813                (!dataIn ||
35814                    dataIn.draw === 1)) {
35815                this._reloadSelect(loadedFilter);
35816                for (var _e = 0, _f = loadedFilter.searchPanes.panes; _e < _f.length; _e++) {
35817                    var pane = _f[_e];
35818                    if (pane.id === this.s.index) {
35819                        // Save some time by only triggering an input if there is a value
35820                        if (pane.searchTerm && pane.searchTerm.length > 0) {
35821                            this.dom.searchBox.val(pane.searchTerm).trigger('input');
35822                        }
35823                        if (pane.order) {
35824                            this.s.dtPane.order(pane.order).draw();
35825                        }
35826                        // Is the pane to be hidden or shown?
35827                        if (pane.collapsed) {
35828                            this.collapse();
35829                        }
35830                        else {
35831                            this.show();
35832                        }
35833                    }
35834                }
35835            }
35836            return true;
35837        };
35838        /**
35839         * Appends all of the HTML elements to their relevant parent Elements
35840         */
35841        SearchPane.prototype._displayPane = function () {
35842            // Empty everything to start again
35843            this.dom.dtP.empty();
35844            this.dom.topRow.empty().addClass(this.classes.topRow);
35845            // If there are more than 3 columns defined then make there be a smaller gap between the panes
35846            if (parseInt(this.c.layout.split('-')[1], 10) > 3) {
35847                this.dom.container.addClass(this.classes.smallGap);
35848            }
35849            this.dom.topRow
35850                .addClass(this.classes.subRowsContainer)
35851                .append(this.dom.upper.append(this.dom.searchCont))
35852                .append(this.dom.lower.append(this.dom.buttonGroup));
35853            // If no selections have been made in the pane then disable the clear button
35854            if (this.c.dtOpts.searching === false ||
35855                this.s.colOpts.dtOpts && this.s.colOpts.dtOpts.searching === false ||
35856                (!this.c.controls || !this.s.colOpts.controls) ||
35857                this.s.customPaneSettings &&
35858                    this.s.customPaneSettings.dtOpts &&
35859                    this.s.customPaneSettings.dtOpts.searching !== undefined &&
35860                    !this.s.customPaneSettings.dtOpts.searching) {
35861                this.dom.searchBox
35862                    .removeClass(this.classes.paneInputButton)
35863                    .addClass(this.classes.disabledButton)
35864                    .attr('disabled', 'true');
35865            }
35866            this.dom.searchBox.appendTo(this.dom.searchCont);
35867            // Create the contents of the searchCont div. Worth noting that this function will change when using semantic ui
35868            this._searchContSetup();
35869            // If the clear button is allowed to show then display it
35870            if (this.c.clear && this.c.controls && this.s.colOpts.controls) {
35871                this.dom.clear.appendTo(this.dom.buttonGroup);
35872            }
35873            if (this.c.orderable && this.s.colOpts.orderable && this.c.controls && this.s.colOpts.controls) {
35874                this.dom.nameButton.appendTo(this.dom.buttonGroup);
35875            }
35876            // If the count column is hidden then don't display the ordering button for it
35877            if (this.c.viewCount &&
35878                this.s.colOpts.viewCount &&
35879                this.c.orderable &&
35880                this.s.colOpts.orderable &&
35881                this.c.controls &&
35882                this.s.colOpts.controls) {
35883                this.dom.countButton.appendTo(this.dom.buttonGroup);
35884            }
35885            if ((this.c.collapse && this.s.colOpts.collapse !== false ||
35886                this.s.colOpts.collapse) &&
35887                this.c.controls && this.s.colOpts.controls) {
35888                this.dom.collapseButton.appendTo(this.dom.buttonGroup);
35889            }
35890            this.dom.container.prepend(this.dom.topRow).append(this.dom.dtP).show();
35891        };
35892        /**
35893         * Escape html characters within a string
35894         *
35895         * @param txt the string to be escaped
35896         * @returns the escaped string
35897         */
35898        SearchPane.prototype._escapeHTML = function (txt) {
35899            return txt
35900                .toString()
35901                .replace(/&amp;/g, '&')
35902                .replace(/&lt;/g, '<')
35903                .replace(/&gt;/g, '>')
35904                .replace(/&quot;/g, '"');
35905        };
35906        /**
35907         * Gets the options for the row for the customPanes
35908         *
35909         * @returns {object} The options for the row extended to include the options from the user.
35910         */
35911        SearchPane.prototype._getBonusOptions = function () {
35912            // We need to reset the thresholds as if they have a value in colOpts then that value will be used
35913            var defaultMutator = {
35914                threshold: null
35915            };
35916            return $$5.extend(true, {}, SearchPane.defaults, defaultMutator, this.c ? this.c : {});
35917        };
35918        /**
35919         * Gets the options for the row for the customPanes
35920         *
35921         * @returns {object} The options for the row extended to include the options from the user.
35922         */
35923        SearchPane.prototype._getOptions = function () {
35924            var table = this.s.dt;
35925            // We need to reset the thresholds as if they have a value in colOpts then that value will be used
35926            var defaultMutator = {
35927                collapse: null,
35928                emptyMessage: false,
35929                initCollapsed: null,
35930                threshold: null
35931            };
35932            var columnOptions = table.settings()[0].aoColumns[this.s.index].searchPanes;
35933            var colOpts = $$5.extend(true, {}, SearchPane.defaults, defaultMutator, columnOptions);
35934            if (columnOptions && columnOptions.hideCount && columnOptions.viewCount === undefined) {
35935                colOpts.viewCount = !columnOptions.hideCount;
35936            }
35937            return colOpts;
35938        };
35939        /**
35940         * Fill the array with the values that are currently being displayed in the table
35941         */
35942        SearchPane.prototype._populatePane = function () {
35943            this.s.rowData.arrayFilter = [];
35944            this.s.rowData.bins = {};
35945            var settings = this.s.dt.settings()[0];
35946            if (!this.s.dt.page.info().serverSide) {
35947                for (var _i = 0, _a = this.s.dt.rows().indexes().toArray(); _i < _a.length; _i++) {
35948                    var index = _a[_i];
35949                    this._populatePaneArray(index, this.s.rowData.arrayFilter, settings);
35950                }
35951            }
35952        };
35953        /**
35954         * This method decides whether a row should contribute to the pane or not
35955         *
35956         * @param filter the value that the row is to be filtered on
35957         * @param dataIndex the row index
35958         */
35959        SearchPane.prototype._search = function (filter, dataIndex) {
35960            var colOpts = this.s.colOpts;
35961            var table = this.s.dt;
35962            // For each item selected in the pane, check if it is available in the cell
35963            for (var _i = 0, _a = this.s.selections; _i < _a.length; _i++) {
35964                var colSelect = _a[_i];
35965                if (typeof colSelect === 'string' && typeof filter === 'string') {
35966                    // The filter value will not have the &amp; in place but a &,
35967                    // so we need to do a replace to make sure that they will match
35968                    colSelect = this._escapeHTML(colSelect);
35969                }
35970                // if the filter is an array then is the column present in it
35971                if (Array.isArray(filter)) {
35972                    if (colOpts.combiner === 'and') {
35973                        if (!filter.includes(colSelect)) {
35974                            return false;
35975                        }
35976                    }
35977                    else if (filter.includes(colSelect)) {
35978                        return true;
35979                    }
35980                }
35981                // if the filter is a function then does it meet the criteria of that function or not
35982                else if (typeof colSelect === 'function') {
35983                    if (colSelect.call(table, table.row(dataIndex).data(), dataIndex)) {
35984                        if (colOpts.combiner === 'or') {
35985                            return true;
35986                        }
35987                    }
35988                    // If the combiner is an "and" then we need to check against all possible selections
35989                    // so if it fails here then the and is not met and return false
35990                    else if (colOpts.combiner === 'and') {
35991                        return false;
35992                    }
35993                }
35994                // otherwise if the two filter values are equal then return true
35995                else if (filter === colSelect ||
35996                    // Loose type checking incase number type in column comparing to a string
35997                    // eslint-disable-next-line eqeqeq
35998                    !(typeof filter === 'string' && filter.length === 0) && filter == colSelect ||
35999                    colSelect === null && typeof filter === 'string' && filter === '') {
36000                    return true;
36001                }
36002            }
36003            // If the combiner is an and then we need to check against all possible selections
36004            // so return true here if so because it would have returned false earlier if it had failed
36005            if (colOpts.combiner === 'and') {
36006                return true;
36007            }
36008            // Otherwise it hasn't matched with anything by this point so it must be false
36009            return false;
36010        };
36011        /**
36012         * Creates the contents of the searchCont div
36013         *
36014         * NOTE This is overridden when semantic ui styling in order to integrate the search button into the text box.
36015         */
36016        SearchPane.prototype._searchContSetup = function () {
36017            if (this.c.controls && this.s.colOpts.controls) {
36018                this.dom.searchButton.appendTo(this.dom.searchLabelCont);
36019            }
36020            if (!(this.c.dtOpts.searching === false ||
36021                this.s.colOpts.dtOpts.searching === false ||
36022                this.s.customPaneSettings &&
36023                    this.s.customPaneSettings.dtOpts &&
36024                    this.s.customPaneSettings.dtOpts.searching !== undefined &&
36025                    !this.s.customPaneSettings.dtOpts.searching)) {
36026                this.dom.searchLabelCont.appendTo(this.dom.searchCont);
36027            }
36028        };
36029        /**
36030         * Adds outline to the pane when a selection has been made
36031         */
36032        SearchPane.prototype._searchExtras = function () {
36033            var updating = this.s.updating;
36034            this.s.updating = true;
36035            var filters = this.s.dtPane.rows({ selected: true }
36035).data().pluck('filter').toArray();
36036            var nullIndex = filters.indexOf(this.emptyMessage());
36037            var container = $$5(this.s.dtPane.table().container());
36038            // If null index is found then search for empty cells as a filter.
36039            if (nullIndex > -1) {
36040                filters[nullIndex] = '';
36041            }
36042            // If a filter has been applied then outline the respective pane, remove it when it no longer is.
36043            if (filters.length > 0) {
36044                container.addClass(this.classes.selected);
36045            }
36046            else if (filters.length === 0) {
36047                container.removeClass(this.classes.selected);
36048            }
36049            this.s.updating = updating;
36050        };
36051        SearchPane.version = '2.1.2';
36052        SearchPane.classes = {
36053            bordered: 'dtsp-bordered',
36054            buttonGroup: 'dtsp-buttonGroup',
36055            buttonSub: 'dtsp-buttonSub',
36056            caret: 'dtsp-caret',
36057            clear: 'dtsp-clear',
36058            clearAll: 'dtsp-clearAll',
36059            clearButton: 'clearButton',
36060            collapseAll: 'dtsp-collapseAll',
36061            collapseButton: 'dtsp-collapseButton',
36062            container: 'dtsp-searchPane',
36063            countButton: 'dtsp-countButton',
36064            disabledButton: 'dtsp-disabledButton',
36065            hidden: 'dtsp-hidden',
36066            hide: 'dtsp-hide',
36067            layout: 'dtsp-',
36068            name: 'dtsp-name',
36069            nameButton: 'dtsp-nameButton',
36070            nameCont: 'dtsp-nameCont',
36071            narrow: 'dtsp-narrow',
36072            paneButton: 'dtsp-paneButton',
36073            paneInputButton: 'dtsp-paneInputButton',
36074            pill: 'dtsp-pill',
36075            rotated: 'dtsp-rotated',
36076            search: 'dtsp-search',
36077            searchCont: 'dtsp-searchCont',
36078            searchIcon: 'dtsp-searchIcon',
36079            searchLabelCont: 'dtsp-searchButtonCont',
36080            selected: 'dtsp-selected',
36081            smallGap: 'dtsp-smallGap',
36082            subRow1: 'dtsp-subRow1',
36083            subRow2: 'dtsp-subRow2',
36084            subRowsContainer: 'dtsp-subRowsContainer',
36085            title: 'dtsp-title',
36086            topRow: 'dtsp-topRow'
36087        };
36088        // Define SearchPanes default options
36089        SearchPane.defaults = {
36090            clear: true,
36091            collapse: true,
36092            combiner: 'or',
36093            container: function (dt) {
36094                return dt.table().container();
36095            },
36096            controls: true,
36097            dtOpts: {},
36098            emptyMessage: null,
36099            hideCount: false,
36100            i18n: {
36101                clearPane: '&times;',
36102                count: '{total}',
36103                emptyMessage: '<em>No data</em>'
36104            },
36105            initCollapsed: false,
36106            layout: 'auto',
36107            name: undefined,
36108            orderable: true,
36109            orthogonal: {
36110                display: 'display',
36111                filter: 'filter',
36112                hideCount: false,
36113                search: 'filter',
36114                show: undefined,
36115                sort: 'sort',
36116                threshold: 0.6,
36117                type: 'type',
36118                viewCount: true
36119            },
36120            preSelect: [],
36121            threshold: 0.6,
36122            viewCount: true
36123        };
36124        return SearchPane;
36125    }());
36126
36127    var __extends$4 = (window && window.__extends) || (function () {
36128        var extendStatics = function (d, b) {
36129            extendStatics = Object.setPrototypeOf ||
36130                ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
36131                function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
36132            return extendStatics(d, b);
36133        };
36134        return function (d, b) {
36135            extendStatics(d, b);
36136            function __() { this.constructor = d; }
36137            d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
36138        };
36139    })();
36140    var SearchPaneST = /** @class */ (function (_super) {
36141        __extends$4(SearchPaneST, _super);
36142        function SearchPaneST(paneSettings, opts, index, panesContainer, panes) {
36143            return _super.call(this, paneSettings, opts, index, panesContainer, panes) || this;
36144        }
36145        /**
36146         * Populates the SearchPane based off of the data that has been recieved from the server
36147         *
36148         * This method overrides SearchPane's _serverPopulate() method
36149         *
36150         * @param dataIn The data that has been sent from the server
36151         */
36152        SearchPaneST.prototype._serverPopulate = function (dataIn) {
36153            this.s.rowData.binsShown = {};
36154            this.s.rowData.arrayFilter = [];
36155            if (dataIn.tableLength !== undefined) {
36156                this.s.tableLength = dataIn.tableLength;
36157                this.s.rowData.totalOptions = this.s.tableLength;
36158            }
36159            else if (this.s.tableLength === null || this.s.dt.rows()[0].length > this.s.tableLength) {
36160                this.s.tableLength = this.s.dt.rows()[0].length;
36161                this.s.rowData.totalOptions = this.s.tableLength;
36162            }
36163            var colTitle = this.s.dt.column(this.s.index).dataSrc();
36164            // If there is SP data for this column add it to the data array and bin
36165            if (dataIn.searchPanes.options[colTitle] !== undefined) {
36166                for (var _i = 0, _a = dataIn.searchPanes.options[colTitle]; _i < _a.length; _i++) {
36167                    var dataPoint = _a[_i];
36168                    this.s.rowData.arrayFilter.push({
36169                        display: dataPoint.label,
36170                        filter: dataPoint.value,
36171                        shown: +dataPoint.count,
36172                        sort: dataPoint.label,
36173                        total: +dataPoint.total,
36174                        type: dataPoint.label
36175                    });
36176                    this.s.rowData.binsShown[dataPoint.value] = +dataPoint.count;
36177                    this.s.rowData.bins[dataPoint.value] = +dataPoint.total;
36178                }
36179            }
36180            var binLength = Object.keys(this.s.rowData.bins).length;
36181            var uniqueRatio = this._uniqueRatio(binLength, this.s.tableLength);
36182            // Don't show the pane if there isnt enough variance in the data, or there is only 1 entry for that pane
36183            if (!this.s.colOpts.show &&
36184                this.s.displayed === false &&
36185                ((this.s.colOpts.show === undefined && this.s.colOpts.threshold === null ?
36186                    uniqueRatio > this.c.threshold :
36187                    uniqueRatio > this.s.colOpts.threshold) ||
36188                    this.s.colOpts.show !== true && binLength <= 1)) {
36189                this.dom.container.addClass(this.classes.hidden);
36190                this.s.displayed = false;
36191                return;
36192            }
36193            // Store the original data
36194            this.s.rowData.arrayOriginal = this.s.rowData.arrayFilter;
36195            this.s.rowData.binsOriginal = this.s.rowData.bins;
36196            // Flag this pane as being displayed
36197            this.s.displayed = true;
36198            // If the pane exists
36199            if (this.s.dtPane) {
36200                // Not the selections that have been made and remove all of the rows
36201                var selected = this.s.serverSelect;
36202                this.s.dtPane.rows().remove();
36203                // Add the rows that are to be shown into the pane
36204                for (var _b = 0, _c = this.s.rowData.arrayFilter; _b < _c.length; _b++) {
36205                    var data = _c[_b];
36206                    if (this._shouldAddRow(data)) {
36207                        var row = this.addRow(data.display, data.filter, data.sort, data.type);
36208                        // Select the row if it was selected before
36209                        for (var i = 0; i < selected.length; i++) {
36210                            var selection = selected[i];
36211                            if (selection.filter === data.filter) {
36212                                // This flag stops another request being made to the server
36213                                this.s.serverSelecting = true;
36214                                row.select();
36215                                this.s.serverSelecting = false;
36216                                // Remove the selection from the to select list and add it to the selected list
36217                                selected.splice(i, 1);
36218                                this.s.selections.push(data.filter);
36219                                break;
36220                            }
36221                        }
36222                    }
36223                }
36224                // Remake any selections that are no longer present
36225                for (var _d = 0, selected_1 = selected; _d < selected_1.length; _d++) {
36226                    var selection = selected_1[_d];
36227                    for (var _e = 0, _f = this.s.rowData.arrayOriginal; _e < _f.length; _e++) {
36228                        var data = _f[_e];
36229                        if (data.filter === selection.filter) {
36230                            var row = this.addRow(data.display, data.filter, data.sort, data.type);
36231                            this.s.serverSelecting = true;
36232                            row.select();
36233                            this.s.serverSelecting = false;
36234                            this.s.selections.push(data.filter);
36235                        }
36236                    }
36237                }
36238                // Store the selected rows
36239                this.s.serverSelect = this.s.dtPane.rows({ selected: true }).data().toArray();
36240                // Update the pane
36241                this.s.dtPane.draw();
36242            }
36243        };
36244        /**
36245         * This method updates the rows and their data within the SearchPanes
36246         *
36247         * SearchPaneCascade overrides this method
36248         */
36249        SearchPaneST.prototype.updateRows = function () {
36250            if (!this.s.dt.page.info().serverSide) {
36251                // Get the latest count values from the table
36252                this.s.rowData.binsShown = {};
36253                for (var _i = 0, _a = this.s.dt.rows({ search: 'applied' }).indexes().toArray(); _i < _a.length; _i++) {
36254                    var index = _a[_i];
36255                    this._updateShown(index, this.s.dt.settings()[0], this.s.rowData.binsShown);
36256                }
36257            }
36258            // Update the rows data to show the current counts
36259            for (var _b = 0, _c = this.s.dtPane.rows().data().toArray(); _b < _c.length; _b++) {
36260                var row = _c[_b];
36261                row.shown = typeof this.s.rowData.binsShown[row.filter] === 'number' ?
36262                    this.s.rowData.binsShown[row.filter] :
36263                    0;
36264                this.s.dtPane.row(row.index).data(row);
36265            }
36266            // Show updates in the pane
36267            this.s.dtPane.draw();
36268            this.s.dtPane.table().node().parentNode.scrollTop = this.s.scrollTop;
36269        };
36270        /**
36271         * Remove functionality from makeSelection - needs to be more advanced when tracking selections
36272         */
36273        SearchPaneST.prototype._makeSelection = function () {
36274            return;
36275        };
36276        /**
36277         * Blank method to remove reloading of selected rows - needs to be more advanced when tracking selections
36278         */
36279        SearchPaneST.prototype._reloadSelect = function () {
36280            return;
36281        };
36282        /**
36283         * Decides if a row should be added when being added from the server
36284         *
36285         * Overridden by SearchPaneCascade
36286         *
36287         * @param data the row data
36288         * @returns boolean indicating if the row should be added or not
36289         */
36290        // eslint-disable-next-line @typescript-eslint/no-unused-vars
36291        SearchPaneST.prototype._shouldAddRow = function (data) {
36292            return true;
36293        };
36294        /**
36295         * Updates the server selection list where appropriate
36296         */
36297        SearchPaneST.prototype._updateSelection = function () {
36298            if (this.s.dt.page.info().serverSide && !this.s.updating && !this.s.serverSelecting) {
36299                this.s.serverSelect = this.s.dtPane.rows({ selected: true }).data().toArray();
36300            }
36301        };
36302        /**
36303         * Used when binning the data for a column
36304         *
36305         * @param rowIdx The current row that is to be added to the bins
36306         * @param settings The datatables settings object
36307         * @param bins The bins object that is to be incremented
36308         */
36309        SearchPaneST.prototype._updateShown = function (rowIdx, settings, bins) {
36310            if (bins === void 0) { bins = this.s.rowData.binsShown; }
36311            var orth = typeof this.s.colOpts.orthogonal === 'string'
36312                ? this.s.colOpts.orthogonal
36313                : this.s.colOpts.orthogonal.search;
36314            var filter = settings.oApi._fnGetCellData(settings, rowIdx, this.s.index, orth);
36315            var add = function (f) {
36316                if (!bins[f]) {
36317                    bins[f] = 1;
36318                }
36319                else {
36320                    bins[f]++;
36321                }
36322            };
36323            if (Array.isArray(filter)) {
36324                for (var _i = 0, filter_1 = filter; _i < filter_1.length; _i++) {
36325                    var f = filter_1[_i];
36326                    add(f);
36327                }
36328            }
36329            else {
36330                add(filter);
36331            }
36332        };
36333        return SearchPaneST;
36334    }(SearchPane));
36335
36336    var __extends$3 = (window && window.__extends) || (function () {
36337        var extendStatics = function (d, b) {
36338            extendStatics = Object.setPrototypeOf ||
36339                ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
36340                function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
36341            return extendStatics(d, b);
36342        };
36343        return function (d, b) {
36344            extendStatics(d, b);
36345            function __() { this.constructor = d; }
36346            d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
36347        };
36348    })();
36349    var $$4;
36350    function setJQuery$3(jq) {
36351        $$4 = jq;
36352    }
36353    var SearchPaneViewTotal = /** @class */ (function (_super) {
36354        __extends$3(SearchPaneViewTotal, _super);
36355        function SearchPaneViewTotal(paneSettings, opts, index, panesContainer, panes) {
36356            var _this = this;
36357            var override = {
36358                i18n: {
36359                    countFiltered: '{shown} ({total})'
36360                }
36361            };
36362            _this = _super.call(this, paneSettings, $$4.extend(override, opts), index, panesContainer, panes) || this;
36363            return _this;
36364        }
36365        /**
36366         * Gets the message that is to be used to indicate the count for each SearchPane row
36367         *
36368         * This method overrides _getMessage() in SearchPane and is overridden by SearchPaneCascadeViewTotal
36369         *
36370         * @param row The row object that is being processed
36371         * @returns string - the message that is to be shown for the count of each entry
36372         */
36373        SearchPaneViewTotal.prototype._getMessage = function (row) {
36374            var countMessage = this.s.dt.i18n('searchPanes.count', this.c.i18n.count);
36375            var filteredMessage = this.s.dt.i18n('searchPanes.countFiltered', this.c.i18n.countFiltered);
36376            return (this.s.filteringActive ? filteredMessage : countMessage)
36377                .replace(/{total}/g, row.total)
36378                .replace(/{shown}/g, row.shown);
36379        };
36380        /**
36381         * Overrides the blank method in SearchPane to return the number of times a given value is currently being displayed
36382         *
36383         * @param filter The filter value
36384         * @returns number - The number of times the value is shown
36385         */
36386        SearchPaneViewTotal.prototype._getShown = function (filter) {
36387            return this.s.rowData.binsShown && this.s.rowData.binsShown[filter] ?
36388                this.s.rowData.binsShown[filter] :
36389                0;
36390        };
36391        return SearchPaneViewTotal;
36392    }(SearchPaneST));
36393
36394    var __extends$2 = (window && window.__extends) || (function () {
36395        var extendStatics = function (d, b) {
36396            extendStatics = Object.setPrototypeOf ||
36397                ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
36398                function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
36399            return extendStatics(d, b);
36400        };
36401        return function (d, b) {
36402            extendStatics(d, b);
36403            function __() { this.constructor = d; }
36404            d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
36405        };
36406    })();
36407    var $$3;
36408    function setJQuery$2(jq) {
36409        $$3 = jq;
36410    }
36411    var SearchPaneCascade = /** @class */ (function (_super) {
36412        __extends$2(SearchPaneCascade, _super);
36413        function SearchPaneCascade(paneSettings, opts, index, panesContainer, panes) {
36414            var _this = this;
36415            var override = {
36416                i18n: {
36417                    count: '{shown}'
36418                }
36419            };
36420            _this = _super.call(this, paneSettings, $$3.extend(override, opts), index, panesContainer, panes) || this;
36421            return _this;
36422        }
36423        /**
36424         * This method updates the rows and their data within the SearchPanes
36425         *
36426         * This overrides the method in SearchPane
36427         */
36428        SearchPaneCascade.prototype.updateRows = function () {
36429            // Note the currently selected values in the pane and remove all of the rows
36430            var selected = this.s.dtPane.rows({ selected: true }).data().toArray();
36431            if (this.s.colOpts.options ||
36432                this.s.customPaneSettings && this.s.customPaneSettings.options) {
36433                // If there are custom options set or it is a custom pane then get them
36434                this._getComparisonRows();
36435                var rows = this.s.dtPane.rows().toArray()[0];
36436                for (var i = 0; i < rows.length; i++) {
36437                    var row = this.s.dtPane.row(rows[i]);
36438                    var rowData = row.data();
36439                    if (rowData === undefined) {
36440                        continue;
36441                    }
36442                    if (rowData.shown === 0) {
36443                        row.remove();
36444                        rows = this.s.dtPane.rows().toArray()[0];
36445                        i--;
36446                        continue;
36447                    }
36448                    for (var _i = 0, selected_1 = selected; _i < selected_1.length; _i++) {
36449                        var selection = selected_1[_i];
36450                        if (rowData.filter === selection.filter) {
36451                            row.select();
36452                            selected.splice(i, 1);
36453                            this.s.selections.push(rowData.filter);
36454                            break;
36455                        }
36456                    }
36457                }
36458            }
36459            else {
36460                if (!this.s.dt.page.info().serverSide) {
36461                    // Get the latest count values from the table
36462                    this._activePopulatePane();
36463                    this.s.rowData.binsShown = {};
36464                    for (var _a = 0, _b = this.s.dt.rows({ search: 'applied' }).indexes().toArray(); _a < _b.length; _a++) {
36465                        var index = _b[_a];
36466                        this._updateShown(index, this.s.dt.settings()[0], this.s.rowData.binsShown);
36467                    }
36468                }
36469                this.s.dtPane.rows().remove();
36470                // Go over all of the rows that could be displayed
36471                for (var _c = 0, _d = this.s.rowData.arrayFilter; _c < _d.length; _c++) {
36472                    var data = _d[_c];
36473                    // Cascade - If there are no rows present in the table don't show the option
36474                    if (data.shown === 0) {
36475                        continue;
36476                    }
36477                    // Add the row to the pane
36478                    var row = this.addRow(data.display, data.filter, data.sort, data.type, undefined);
36479                    // Check if this row was selected
36480                    for (var i = 0; i < selected.length; i++) {
36481                        var selection = selected[i];
36482                        if (selection.filter === data.filter) {
36483                            row.select();
36484                            // Remove the row from the to find list and then add it to the selections list
36485                            selected.splice(i, 1);
36486                            this.s.selections.push(data.filter);
36487                            break;
36488                        }
36489                    }
36490                }
36491                // Add all of the rows that were previously selected but aren't any more
36492                for (var _e = 0, selected_2 = selected; _e < selected_2.length; _e++) {
36493                    var selection = selected_2[_e];
36494                    for (var _f = 0, _g = this.s.rowData.arrayOriginal; _f < _g.length; _f++) {
36495                        var data = _g[_f];
36496                        if (data.filter === selection.filter) {
36497                            var row = this.addRow(data.display, data.filter, data.sort, data.type, undefined);
36498                            row.select();
36499                            this.s.selections.push(data.filter);
36500                        }
36501                    }
36502                }
36503            }
36504            // Show updates in the pane
36505            this.s.dtPane.draw();
36506            this.s.dtPane.table().node().parentNode.scrollTop = this.s.scrollTop;
36507            // If client side updated the tables results
36508            if (!this.s.dt.page.info().serverSide) {
36509                this.s.dt.draw(false);
36510            }
36511        };
36512        /**
36513         * Fill the array with the values that are currently being displayed in the table
36514         */
36515        SearchPaneCascade.prototype._activePopulatePane = function () {
36516            this.s.rowData.arrayFilter = [];
36517            this.s.rowData.bins = {};
36518            var settings = this.s.dt.settings()[0];
36519            if (!this.s.dt.page.info().serverSide) {
36520                for (var _i = 0, _a = this.s.dt.rows({ search: 'applied' }).indexes().toArray(); _i < _a.length; _i++) {
36521                    var index = _a[_i];
36522                    this._populatePaneArray(index, this.s.rowData.arrayFilter, settings);
36523                }
36524            }
36525        };
36526        SearchPaneCascade.prototype._getComparisonRows = function () {
36527            // Find the appropriate options depending on whether this is a pane for a specific column or a custom pane
36528            var options = this.s.colOpts.options
36529                ? this.s.colOpts.options
36530                : this.s.customPaneSettings && this.s.customPaneSettings.options
36531                    ? this.s.customPaneSettings.options
36532                    : undefined;
36533            if (options === undefined) {
36534                return;
36535            }
36536            var allRows = this.s.dt.rows();
36537            var shownRows = this.s.dt.rows({ search: 'applied' });
36538            var tableValsTotal = allRows.data().toArray();
36539            var tableValsShown = shownRows.data().toArray();
36540            var rows = [];
36541            // Clear all of the other rows from the pane, only custom options are to be displayed when they are defined
36542            this.s.dtPane.clear();
36543            this.s.indexes = [];
36544            for (var _i = 0, options_1 = options; _i < options_1.length; _i++) {
36545                var comp = options_1[_i];
36546                // Initialise the object which is to be placed in the row
36547                var insert = comp.label !== '' ?
36548                    comp.label :
36549                    this.emptyMessage();
36550                var comparisonObj = {
36551                    className: comp.className,
36552                    display: insert,
36553                    filter: typeof comp.value === 'function' ? comp.value : [],
36554                    shown: 0,
36555                    sort: insert,
36556                    total: 0,
36557                    type: insert
36558                };
36559                // If a custom function is in place
36560                if (typeof comp.value === 'function') {
36561                    // Count the number of times the function evaluates to true for the original data in the Table
36562                    for (var i = 0; i < tableValsTotal.length; i++) {
36563                        if (comp.value.call(this.s.dt, tableValsTotal[i], allRows[0][i])) {
36564                            comparisonObj.total++;
36565                        }
36566                    }
36567                    for (var i = 0; i < tableValsShown.length; i++) {
36568                        if (comp.value.call(this.s.dt, tableValsShown[i], shownRows[0][i])) {
36569                            comparisonObj.shown++;
36570                        }
36571                    }
36572                    // Update the comparisonObj
36573                    if (typeof comparisonObj.filter !== 'function') {
36574                        comparisonObj.filter.push(comp.filter);
36575                    }
36576                }
36577                rows.push(this.addRow(comparisonObj.display, comparisonObj.filter, comparisonObj.sort, comparisonObj.type, comparisonObj.className, comparisonObj.total, comparisonObj.shown));
36578            }
36579            return rows;
36580        };
36581        /**
36582         * Gets the message that is to be used to indicate the count for each SearchPane row
36583         *
36584         * This method overrides _getMessage() in SearchPane and is overridden by SearchPaneCascadeViewTotal
36585         *
36586         * @param row The row object that is being processed
36587         * @returns string - the message that is to be shown for the count of each entry
36588         */
36589        SearchPaneCascade.prototype._getMessage = function (row) {
36590            return this.s.dt.i18n('searchPanes.count', this.c.i18n.count)
36591                .replace(/{total}/g, row.total)
36592                .replace(/{shown}/g, row.shown);
36593        };
36594        /**
36595         * Overrides the blank method in SearchPane to return the number of times a given value is currently being displayed
36596         *
36597         * @param filter The filter value
36598         * @returns number - The number of times the value is shown
36599         */
36600        SearchPaneCascade.prototype._getShown = function (filter) {
36601            return this.s.rowData.binsShown && this.s.rowData.binsShown[filter] ?
36602                this.s.rowData.binsShown[filter] :
36603                0;
36604        };
36605        /**
36606         * Decides if a row should be added when being added from the server
36607         *
36608         * Overrides method in by SearchPaneST
36609         *
36610         * @param data the row data
36611         * @returns boolean indicating if the row should be added or not
36612         */
36613        SearchPaneCascade.prototype._shouldAddRow = function (data) {
36614            return data.shown > 0;
36615        };
36616        return SearchPaneCascade;
36617    }(SearchPaneST));
36618
36619    var __extends$1 = (window && window.__extends) || (function () {
36620        var extendStatics = function (d, b) {
36621            extendStatics = Object.setPrototypeOf ||
36622                ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
36623                function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
36624            return extendStatics(d, b);
36625        };
36626        return function (d, b) {
36627            extendStatics(d, b);
36628            function __() { this.constructor = d; }
36629            d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
36630        };
36631    })();
36632    var $$2;
36633    function setJQuery$1(jq) {
36634        $$2 = jq;
36635    }
36636    var SearchPaneCascadeViewTotal = /** @class */ (function (_super) {
36637        __extends$1(SearchPaneCascadeViewTotal, _super);
36638        function SearchPaneCascadeViewTotal(paneSettings, opts, index, panesContainer, panes) {
36639            var _this = this;
36640            var override = {
36641                i18n: {
36642                    count: '{total}',
36643                    countFiltered: '{shown} ({total})'
36644                }
36645            };
36646            _this = _super.call(this, paneSettings, $$2.extend(override, opts), index, panesContainer, panes) || this;
36647            return _this;
36648        }
36649        /**
36650         * Fill the array with the values that are currently being displayed in the table
36651         *
36652         * This method overrides _activePopulatePane() in SearchPaneCascade
36653         */
36654        SearchPaneCascadeViewTotal.prototype._activePopulatePane = function () {
36655            this.s.rowData.arrayFilter = [];
36656            this.s.rowData.binsShown = {};
36657            var settings = this.s.dt.settings()[0];
36658            if (!this.s.dt.page.info().serverSide) {
36659                for (var _i = 0, _a = this.s.dt.rows({ search: 'applied' }).indexes().toArray(); _i < _a.length; _i++) {
36660                    var index = _a[_i];
36661                    this._populatePaneArray(index, this.s.rowData.arrayFilter, settings, this.s.rowData.binsShown);
36662                }
36663            }
36664        };
36665        /**
36666         * Gets the message that is to be used to indicate the count for each SearchPane row
36667         *
36668         * This method overrides _getMessage() in SearchPaneCascade
36669         *
36670         * @param row The row object that is being processed
36671         * @returns string - the message that is to be shown for the count of each entry
36672         */
36673        SearchPaneCascadeViewTotal.prototype._getMessage = function (row) {
36674            var countMessage = this.s.dt.i18n('searchPanes.count', this.c.i18n.count);
36675            var filteredMessage = this.s.dt.i18n('searchPanes.countFiltered', this.c.i18n.countFiltered);
36676            return (this.s.filteringActive ? filteredMessage : countMessage)
36677                .replace(/{total}/g, row.total)
36678                .replace(/{shown}/g, row.shown);
36679        };
36680        return SearchPaneCascadeViewTotal;
36681    }(SearchPaneCascade));
36682
36683    var $$1;
36684    var dataTable$1;
36685    function setJQuery(jq) {
36686        $$1 = jq;
36687        dataTable$1 = jq.fn.dataTable;
36688    }
36689    var SearchPanes = /** @class */ (function () {
36690        function SearchPanes(paneSettings, opts, fromPreInit, paneClass) {
36691            var _this = this;
36692            if (fromPreInit === void 0) { fromPreInit = false; }
36693            if (paneClass === void 0) { paneClass = SearchPane; }
36694            // Check that the required version of DataTables is included
36695            if (!dataTable$1 || !dataTable$1.versionCheck || !dataTable$1.versionCheck('1.10.0')) {
36696                throw new Error('SearchPane requires DataTables 1.10 or newer');
36697            }
36698            // Check that Select is included
36699            // eslint-disable-next-line no-extra-parens
36700            if (!dataTable$1.select) {
36701                throw new Error('SearchPane requires Select');
36702            }
36703            var table = new dataTable$1.Api(paneSettings);
36704            this.classes = $$1.extend(true, {}, SearchPanes.classes);
36705            // Get options from user
36706            this.c = $$1.extend(true, {}, SearchPanes.defaults, opts);
36707            // Add extra elements to DOM object including clear
36708            this.dom = {
36709                clearAll: $$1('<button type="button"/>')
36710                    .addClass(this.classes.clearAll)
36711                    .html(table.i18n('searchPanes.clearMessage', this.c.i18n.clearMessage)),
36712                collapseAll: $$1('<button type="button"/>')
36713                    .addClass(this.classes.collapseAll)
36714                    .html(table.i18n('searchPanes.collapseMessage', this.c.i18n.collapseMessage)),
36715                container: $$1('<div/>').addClass(this.classes.panes).html(table.i18n('searchPanes.loadMessage', this.c.i18n.loadMessage)),
36716                emptyMessage: $$1('<div/>').addClass(this.classes.emptyMessage),
36717                panes: $$1('<div/>').addClass(this.classes.container),
36718                showAll: $$1('<button type="button"/>')
36719                    .addClass(this.classes.showAll)
36720                    .addClass(this.classes.disabledButton)
36721                    .attr('disabled', 'true')
36722                    .html(table.i18n('searchPanes.showMessage', this.c.i18n.showMessage)),
36723                title: $$1('<div/>').addClass(this.classes.title),
36724                titleRow: $$1('<div/>').addClass(this.classes.titleRow)
36725            };
36726            this.s = {
36727                colOpts: [],
36728                dt: table,
36729                filterCount: 0,
36730                minPaneWidth: 260.0,
36731                page: 0,
36732                paging: false,
36733                pagingST: false,
36734                paneClass: paneClass,
36735                panes: [],
36736                selectionList: [],
36737                serverData: {},
36738                stateRead: false,
36739                updating: false
36740            };
36741            // Do not reinitialise if already initialised on table
36742            if (table.settings()[0]._searchPanes) {
36743                return;
36744            }
36745            this._getState();
36746            if (this.s.dt.page.info().serverSide) {
36747                var hostSettings = this.s.dt.settings()[0];
36748                // Listener to get the data into the server request before it is made
36749                this.s.dt.on('preXhr.dtsps', function (e, settings, data) {
36750                    if (hostSettings !== settings) {
36751                        return;
36752                    }
36753                    if (data.searchPanes === undefined) {
36754                        data.searchPanes = {};
36755                    }
36756                    if (data.searchPanes_null === undefined) {
36757                        data.searchPanes_null = {};
36758                    }
36759                    var src;
36760                    for (var _i = 0, _a = _this.s.selectionList; _i < _a.length; _i++) {
36761                        var selection = _a[_i];
36762                        src = _this.s.dt.column(selection.column).dataSrc();
36763                        if (data.searchPanes[src] === undefined) {
36764                            data.searchPanes[src] = {};
36765                        }
36766                        if (data.searchPanes_null[src] === undefined) {
36767                            data.searchPanes_null[src] = {};
36768                        }
36769                        for (var i = 0; i < selection.rows.length; i++) {
36770                            data.searchPanes[src][i] = selection.rows[i];
36771                            if (data.searchPanes[src][i] === null) {
36772                                data.searchPanes_null[src][i] = true;
36773                            }
36774                        }
36775                    }
36776                    if (_this.s.selectionList.length > 0) {
36777                        data.searchPanesLast = src;
36778                    }
36779                    // Config options that will change how the querying is done
36780                    data.searchPanes_options = {
36781                        cascade: _this.c.cascadePanes,
36782                        viewCount: _this.c.viewCount,
36783                        viewTotal: _this.c.viewTotal
36784                    };
36785                });
36786            }
36787            this._setXHR();
36788            table.settings()[0]._searchPanes = this;
36789            if (this.s.dt.settings()[0]._bInitComplete || fromPreInit) {
36790                this._paneDeclare(table, paneSettings, opts);
36791            }
36792            else {
36793                table.one('preInit.dtsps', function () {
36794                    _this._paneDeclare(table, paneSettings, opts);
36795                });
36796            }
36797            return this;
36798        }
36799        /**
36800         * Clear the selections of all of the panes
36801         */
36802        SearchPanes.prototype.clearSelections = function () {
36803            for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
36804                var pane = _a[_i];
36805                if (pane.s.dtPane) {
36806                    pane.s.scrollTop = pane.s.dtPane.table().node().parentNode.scrollTop;
36807                }
36808            }
36809            // Load in all of the searchBoxes in the documents
36810            var searches = this.dom.container.find('.' + this.classes.search.replace(/\s+/g, '.'));
36811            // For each searchBox set the input text to be empty and then trigger
36812            // an input on them so that they no longer filter the panes
36813            searches.each(function () {
36814                $$1(this).val('').trigger('input');
36815            });
36816            // Clear the selectionList
36817            this.s.selectionList = [];
36818            var returnArray = [];
36819            for (var _b = 0, _c = this.s.panes; _b < _c.length; _b++) {
36820                var pane = _c[_b];
36821                if (pane.s.dtPane) {
36822                    returnArray.push(pane.clearPane());
36823                }
36824            }
36825            return returnArray;
36826        };
36827        /**
36828         * returns the container node for the searchPanes
36829         */
36830        SearchPanes.prototype.getNode = function () {
36831            return this.dom.container;
36832        };
36833        /**
36834         * rebuilds all of the panes
36835         */
36836        SearchPanes.prototype.rebuild = function (targetIdx, maintainSelection) {
36837            if (targetIdx === void 0) { targetIdx = false; }
36838            if (maintainSelection === void 0) { maintainSelection = false; }
36839            this.dom.emptyMessage.detach();
36840            // As a rebuild from scratch is required, empty the searchpanes container.
36841            if (targetIdx === false) {
36842                this.dom.panes.empty();
36843            }
36844            // Rebuild each pane individually, if a specific pane has been selected then only rebuild that one
36845            var returnArray = [];
36846            for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
36847                var pane = _a[_i];
36848                if (targetIdx === false || pane.s.index === targetIdx) {
36849                    pane.clearData();
36850                    pane.rebuildPane(this.s.dt.page.info().serverSide ?
36851                        this.s.serverData :
36852                        undefined, maintainSelection);
36853                    this.dom.panes.append(pane.dom.container);
36854                    returnArray.push(pane);
36855                }
36856            }
36857            this._updateSelection();
36858            // Attach panes, clear buttons, and title bar to the document
36859            this._updateFilterCount();
36860            this._attachPaneContainer();
36861            this._initSelectionListeners(false);
36862            // If the selections are to be maintained, then it is safe to assume that paging is also to be maintained
36863            // Otherwise, the paging should be reset
36864            this.s.dt.draw(!maintainSelection);
36865            // Resize the panes incase there has been a change
36866            this.resizePanes();
36867            // If a single pane has been rebuilt then return only that pane
36868            return returnArray.length === 1 ? returnArray[0] : returnArray;
36869        };
36870        /**
36871         * Resizes all of the panes
36872         */
36873        SearchPanes.prototype.resizePanes = function () {
36874            if (this.c.layout === 'auto') {
36875                var contWidth = $$1(this.s.dt.searchPanes.container()).width();
36876                var target = Math.floor(contWidth / this.s.minPaneWidth); // The neatest number of panes per row
36877                var highest_1 = 1;
36878                var highestmod_1 = 0;
36879                // Get the indexes of all of the displayed panes
36880                var dispIndex = [];
36881                for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
36882                    var pane = _a[_i];
36883                    if (pane.s.displayed) {
36884                        dispIndex.push(pane.s.index);
36885                    }
36886                }
36887                var displayCount = dispIndex.length;
36888                // If the neatest number is the number we have then use this.
36889                if (target === displayCount) {
36890                    highest_1 = target;
36891                }
36892                else {
36893                    // Go from the target down and find the value with the most panes left over, this will be the best fit
36894                    for (var ppr = target; ppr > 1; ppr--) {
36895                        var rem = displayCount % ppr;
36896                        if (rem === 0) {
36897                            highest_1 = ppr;
36898                            highestmod_1 = 0;
36899                            break;
36900                        }
36901                        // If there are more left over at this amount of panes per row (ppr)
36902                        // then it fits better so new values
36903                        else if (rem > highestmod_1) {
36904                            highest_1 = ppr;
36905                            highestmod_1 = rem;
36906                        }
36907                    }
36908                }
36909                // If there is a perfect fit then none are to be wider
36910                var widerIndexes_1 = highestmod_1 !== 0 ? dispIndex.slice(dispIndex.length - highestmod_1, dispIndex.length) : [];
36911                this.s.panes.forEach(function (pane) {
36912                    // Resize the pane with the new layout
36913                    if (pane.s.displayed) {
36914                        pane.resize('columns-' + (!widerIndexes_1.includes(pane.s.index) ? highest_1 : highestmod_1));
36915                    }
36916                });
36917            }
36918            else {
36919                for (var _b = 0, _c = this.s.panes; _b < _c.length; _b++) {
36920                    var pane = _c[_b];
36921                    pane.adjustTopRow();
36922                }
36923            }
36924            return this;
36925        };
36926        /**
36927         * Holder method that is userd in SearchPanesST to set listeners that have an effect on other panes
36928         *
36929         * @param isPreselect boolean to indicate if the preselect array is to override the current selection list.
36930         */
36931        SearchPanes.prototype._initSelectionListeners = function (isPreselect) {
36932            return;
36933        };
36934        /**
36935         * Blank method that is overridden in SearchPanesST to retrieve the totals from the server data
36936         */
36937        SearchPanes.prototype._serverTotals = function () {
36938            return;
36939        };
36940        /**
36941         * Set's the xhr listener so that SP can extact appropriate data from the response
36942         */
36943        SearchPanes.prototype._setXHR = function () {
36944            var _this = this;
36945            var hostSettings = this.s.dt.settings()[0];
36946            var run = function (json) {
36947                if (json && json.searchPanes && json.searchPanes.options) {
36948                    _this.s.serverData = json;
36949                    _this.s.serverData.tableLength = json.recordsTotal;
36950                    _this._serverTotals();
36951                }
36952            };
36953            // We are using the xhr event to rebuild the panes if required due to viewTotal being enabled
36954            // If viewTotal is not enabled then we simply update the data from the server
36955            this.s.dt.on('xhr.dtsps', function (e, settings, json) {
36956                if (hostSettings === settings) {
36957                    run(json);
36958                }
36959            });
36960            // Account for the initial JSON fetch having already completed
36961            run(this.s.dt.ajax.json());
36962        };
36963        /**
36964         * Set's the function that is to be performed when a state is loaded
36965         *
36966         * Overridden by the method in SearchPanesST
36967         */
36968        SearchPanes.prototype._stateLoadListener = function () {
36969            var _this = this;
36970            var hostSettings = this.s.dt.settings()[0];
36971            this.s.dt.on('stateLoadParams.dtsps', function (e, settings, data) {
36972                if (data.searchPanes === undefined || settings !== hostSettings) {
36973                    return;
36974                }
36975                _this.clearSelections();
36976                // Set the selection list for the panes so that the correct
36977                // rows can be reselected and in the right order
36978                _this.s.selectionList =
36979                    data.searchPanes.selectionList ?
36980                        data.searchPanes.selectionList :
36981                        [];
36982                // Find the panes that match from the state and the actual instance
36983                if (data.searchPanes.panes) {
36984                    for (var _i = 0, _a = data.searchPanes.panes; _i < _a.length; _i++) {
36985                        var loadedPane = _a[_i];
36986                        for (var _b = 0, _c = _this.s.panes; _b < _c.length; _b++) {
36987                            var pane = _c[_b];
36988                            if (loadedPane.id === pane.s.index && pane.s.dtPane) {
36989                                // Set the value of the searchbox
36990                                pane.dom.searchBox.val(loadedPane.searchTerm);
36991                                // Set the value of the order
36992                                pane.s.dtPane.order(loadedPane.order);
36993                            }
36994                        }
36995                    }
36996                }
36997                _this._makeSelections(_this.s.selectionList);
36998            });
36999        };
37000        /**
37001         * Updates the selectionList when cascade is not in place
37002         *
37003         * Overridden in SearchPanesST
37004         */
37005        SearchPanes.prototype._updateSelection = function () {
37006            this.s.selectionList = [];
37007            for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
37008                var pane = _a[_i];
37009                if (pane.s.dtPane) {
37010                    var rows = pane.s.dtPane.rows({ selected: true }).data().toArray().map(function (el) { return el.filter; });
37011                    if (rows.length) {
37012                        this.s.selectionList.push({
37013                            column: pane.s.index,
37014                            rows: rows
37015                        });
37016                    }
37017                }
37018            }
37019        };
37020        /**
37021         * Attach the panes, buttons and title to the document
37022         */
37023        SearchPanes.prototype._attach = function () {
37024            var _this = this;
37025            this.dom.titleRow
37026                .removeClass(this.classes.hide)
37027                .detach()
37028                .append(this.dom.title);
37029            // If the clear button is permitted attach it
37030            if (this.c.clear) {
37031                this.dom.clearAll
37032                    .appendTo(this.dom.titleRow)
37033                    .on('click.dtsps', function () { return _this.clearSelections(); });
37034            }
37035            if (this.c.collapse) {
37036                this.dom.showAll.appendTo(this.dom.titleRow);
37037                this.dom.collapseAll.appendTo(this.dom.titleRow);
37038                this._setCollapseListener();
37039            }
37040            // Attach the container for each individual pane to the overall container
37041            for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
37042                var pane = _a[_i];
37043                this.dom.panes.append(pane.dom.container);
37044            }
37045            // Attach everything to the document
37046            this.dom.container
37047                .text('')
37048                .removeClass(this.classes.hide)
37049                .append(this.dom.titleRow)
37050                .append(this.dom.panes);
37051            // WORKAROUND
37052            this.s.panes.forEach(function (pane) { return pane.setListeners(); });
37053            if ($$1('div.' + this.classes.container).length === 0) {
37054                this.dom.container.prependTo(this.s.dt);
37055            }
37056        };
37057        /**
37058         * If there are no panes to display then this method is called to either
37059         * display a message in their place or hide them completely.
37060         */
37061        SearchPanes.prototype._attachMessage = function () {
37062            // Create a message to display on the screen
37063            var message;
37064            try {
37065                message = this.s.dt.i18n('searchPanes.emptyPanes', this.c.i18n.emptyPanes);
37066            }
37067            catch (error) {
37068                message = null;
37069            }
37070            // If the message is an empty string then searchPanes.emptyPanes is undefined,
37071            // therefore the pane container should be removed from the display
37072            if (message === null) {
37073                this.dom.container.addClass(this.classes.hide);
37074                this.dom.titleRow.removeClass(this.classes.hide);
37075                return;
37076            }
37077            // Otherwise display the message
37078            this.dom.container.removeClass(this.classes.hide);
37079            this.dom.titleRow.addClass(this.classes.hide);
37080            this.dom.emptyMessage.html(message).appendTo(this.dom.container);
37081        };
37082        /**
37083         * Attaches the panes to the document and displays a message or hides if there are none
37084         */
37085        SearchPanes.prototype._attachPaneContainer = function () {
37086            // If a pane is to be displayed then attach the normal pane output
37087            for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
37088                var pane = _a[_i];
37089                if (pane.s.displayed === true) {
37090                    this._attach();
37091                    return;
37092                }
37093            }
37094            // Otherwise attach the custom message or remove the container from the display
37095            this._attachMessage();
37096        };
37097        /**
37098         * Checks which panes are collapsed and then performs relevant actions to the collapse/show all buttons
37099         */
37100        SearchPanes.prototype._checkCollapse = function () {
37101            var disableClose = true;
37102            var disableShow = true;
37103            for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
37104                var pane = _a[_i];
37105                if (pane.s.displayed) {
37106                    // If the pane is not collapsed
37107                    if (!pane.dom.collapseButton.hasClass(pane.classes.rotated)) {
37108                        // Enable the collapse all button
37109                        this.dom.collapseAll.removeClass(this.classes.disabledButton).removeAttr('disabled');
37110                        disableClose = false;
37111                    }
37112                    else {
37113                        // Otherwise enable the show all button
37114                        this.dom.showAll.removeClass(this.classes.disabledButton).removeAttr('disabled');
37115                        disableShow = false;
37116                    }
37117                }
37118            }
37119            // If this flag is still true, no panes are open so the close button should be disabled
37120            if (disableClose) {
37121                this.dom.collapseAll.addClass(this.classes.disabledButton).attr('disabled', 'true');
37122            }
37123            // If this flag is still true, no panes are closed so the show button should be disabled
37124            if (disableShow) {
37125                this.dom.showAll.addClass(this.classes.disabledButton).attr('disabled', 'true');
37126            }
37127        };
37128        /**
37129         * Attaches the message to the document but does not add any panes
37130         */
37131        SearchPanes.prototype._checkMessage = function () {
37132            // If a pane is to be displayed then attach the normal pane output
37133            for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
37134                var pane = _a[_i];
37135                if (pane.s.displayed === true) {
37136                    // Ensure that the empty message is removed if a pane is displayed
37137                    this.dom.emptyMessage.detach();
37138                    this.dom.titleRow.removeClass(this.classes.hide);
37139                    return;
37140                }
37141            }
37142            // Otherwise attach the custom message or remove the container from the display
37143            this._attachMessage();
37144        };
37145        /**
37146         * Collapses all of the panes
37147         */
37148        SearchPanes.prototype._collapseAll = function () {
37149            for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
37150                var pane = _a[_i];
37151                pane.collapse();
37152            }
37153        };
37154        /**
37155         * Finds a pane based upon the name of that pane
37156         *
37157         * @param name string representing the name of the pane
37158         * @returns SearchPane The pane which has that name
37159         */
37160        SearchPanes.prototype._findPane = function (name) {
37161            for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
37162                var pane = _a[_i];
37163                if (name === pane.s.name) {
37164                    return pane;
37165                }
37166            }
37167        };
37168        /**
37169         * Gets the selection list from the previous state and stores it in the selectionList Property
37170         */
37171        SearchPanes.prototype._getState = function () {
37172            var loadedFilter = this.s.dt.state.loaded();
37173            if (loadedFilter && loadedFilter.searchPanes && loadedFilter.searchPanes.selectionList) {
37174                this.s.selectionList = loadedFilter.searchPanes.selectionList;
37175            }
37176        };
37177        SearchPanes.prototype._makeSelections = function (selectList) {
37178            for (var _i = 0, selectList_1 = selectList; _i < selectList_1.length; _i++) {
37179                var selection = selectList_1[_i];
37180                var pane = void 0;
37181                for (var _a = 0, _b = this.s.panes; _a < _b.length; _a++) {
37182                    var p = _b[_a];
37183                    if (p.s.index === selection.column) {
37184                        pane = p;
37185                        break;
37186                    }
37187                }
37188                if (pane && pane.s.dtPane) {
37189                    for (var j = 0; j < pane.s.dtPane.rows().data().toArray().length; j++) {
37190                        if (selection.rows.includes(typeof pane.s.dtPane.row(j).data().filter === 'function' ?
37191                            pane.s.dtPane.cell(j, 0).data() :
37192                            pane.s.dtPane.row(j).data().filter)) {
37193                            pane.s.dtPane.row(j).select();
37194                        }
37195                    }
37196                    pane.updateTable();
37197                }
37198            }
37199        };
37200        /**
37201         * Declares the instances of individual searchpanes depen
37201dant on the number of columns.
37202         * It is necessary to run this once preInit has completed otherwise no panes will be
37203         * created as the column count will be 0.
37204         *
37205         * @param table the DataTable api for the parent table
37206         * @param paneSettings the settings passed into the constructor
37207         * @param opts the options passed into the constructor
37208         */
37209        SearchPanes.prototype._paneDeclare = function (table, paneSettings, opts) {
37210            var _this = this;
37211            // Create Panes
37212            table
37213                .columns(this.c.columns.length > 0 ? this.c.columns : undefined)
37214                .eq(0)
37215                .each(function (idx) {
37216                _this.s.panes.push(new _this.s.paneClass(paneSettings, opts, idx, _this.dom.panes));
37217            });
37218            // If there is any extra custom panes defined then create panes for them too
37219            var colCount = table.columns().eq(0).toArray().length;
37220            for (var i = 0; i < this.c.panes.length; i++) {
37221                var id = colCount + i;
37222                this.s.panes.push(new this.s.paneClass(paneSettings, opts, id, this.dom.panes, this.c.panes[i]));
37223            }
37224            // If a custom ordering is being used
37225            if (this.c.order.length > 0) {
37226                // Make a new Array of panes based upon the order
37227                this.s.panes = this.c.order.map(function (name) { return _this._findPane(name); });
37228            }
37229            // If this internal property is true then the DataTable has been initialised already
37230            if (this.s.dt.settings()[0]._bInitComplete) {
37231                this._startup(table);
37232            }
37233            else {
37234                // Otherwise add the paneStartup function to the list of functions
37235                // that are to be run when the table is initialised. This will garauntee that the
37236                // panes are initialised before the init event and init Complete callback is fired
37237                this.s.dt.settings()[0].aoInitComplete.push({
37238                    fn: function () { return _this._startup(table); }
37239                });
37240            }
37241        };
37242        /**
37243         * Sets the listeners for the collapse and show all buttons
37244         * Also sets and performs checks on current panes to see if they are collapsed
37245         */
37246        SearchPanes.prototype._setCollapseListener = function () {
37247            var _this = this;
37248            this.dom.collapseAll.on('click.dtsps', function () {
37249                _this._collapseAll();
37250                _this.dom.collapseAll.addClass(_this.classes.disabledButton).attr('disabled', 'true');
37251                _this.dom.showAll.removeClass(_this.classes.disabledButton).removeAttr('disabled');
37252                _this.s.dt.state.save();
37253            });
37254            this.dom.showAll.on('click.dtsps', function () {
37255                _this._showAll();
37256                _this.dom.showAll.addClass(_this.classes.disabledButton).attr('disabled', 'true');
37257                _this.dom.collapseAll.removeClass(_this.classes.disabledButton).removeAttr('disabled');
37258                _this.s.dt.state.save();
37259            });
37260            for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
37261                var pane = _a[_i];
37262                // We want to make the same check whenever there is a collapse/expand
37263                pane.dom.topRow.on('collapse.dtsps', function () { return _this._checkCollapse(); });
37264            }
37265            this._checkCollapse();
37266        };
37267        /**
37268         * Shows all of the panes
37269         */
37270        SearchPanes.prototype._showAll = function () {
37271            for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
37272                var pane = _a[_i];
37273                pane.show();
37274            }
37275        };
37276        /**
37277         * Initialises the tables previous/preset selections and initialises callbacks for events
37278         *
37279         * @param table the parent table for which the searchPanes are being created
37280         */
37281        SearchPanes.prototype._startup = function (table) {
37282            var _this = this;
37283            // Attach clear button and title bar to the document
37284            this._attach();
37285            this.dom.panes.empty();
37286            var hostSettings = this.s.dt.settings()[0];
37287            for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
37288                var pane = _a[_i];
37289                pane.rebuildPane(Object.keys(this.s.serverData).length > 0 ? this.s.serverData : undefined);
37290                this.dom.panes.append(pane.dom.container);
37291            }
37292            // If the layout is set to auto then the panes need to be resized to their best fit
37293            if (this.c.layout === 'auto') {
37294                this.resizePanes();
37295            }
37296            var loadedFilter = this.s.dt.state.loaded();
37297            // Reset the paging if that has been saved in the state
37298            if (!this.s.stateRead && loadedFilter) {
37299                this.s.dt
37300                    .page(loadedFilter.start / this.s.dt.page.len())
37301                    .draw('page');
37302            }
37303            this.s.stateRead = true;
37304            this._checkMessage();
37305            // When a draw is called on the DataTable, update all of the panes incase the data in the DataTable has changed
37306            table.on('preDraw.dtsps', function () {
37307                // Check that the panes are not updating to avoid infinite loops
37308                // Also check that this draw is not due to paging
37309                if (!_this.s.updating && !_this.s.paging) {
37310                    _this._updateFilterCount();
37311                    _this._updateSelection();
37312                }
37313                // Paging flag reset - we only need to dodge the draw once
37314                _this.s.paging = false;
37315            });
37316            $$1(window).on('resize.dtsps', dataTable$1.util.throttle(function () { return _this.resizePanes(); }));
37317            // Whenever a state save occurs store the selection list in the state object
37318            this.s.dt.on('stateSaveParams.dtsps', function (e, settings, data) {
37319                if (settings !== hostSettings) {
37320                    return;
37321                }
37322                if (data.searchPanes === undefined) {
37323                    data.searchPanes = {};
37324                }
37325                data.searchPanes.selectionList = _this.s.selectionList;
37326            });
37327            this._stateLoadListener();
37328            // Listener for paging on main table
37329            table.off('page.dtsps page-nc.dtsps').on('page.dtsps page-nc.dtsps', function (e, s) {
37330                _this.s.paging = true;
37331                // This is an indicator to any selection tracking classes that paging has occured
37332                // It has to happen here so that we don't stack event listeners unnecessarily
37333                // The value is only ever set back to false in the SearchPanesST class
37334                // Equally it is never read in this class
37335                _this.s.pagingST = true;
37336                _this.s.page = _this.s.dt.page();
37337            });
37338            if (this.s.dt.page.info().serverSide) {
37339                table.off('preXhr.dtsps').on('preXhr.dtsps', function (e, settings, data) {
37340                    if (settings !== hostSettings) {
37341                        return;
37342                    }
37343                    if (!data.searchPanes) {
37344                        data.searchPanes = {};
37345                    }
37346                    if (!data.searchPanes_null) {
37347                        data.searchPanes_null = {};
37348                    }
37349                    // Count how many filters are being applied
37350                    var filterCount = 0;
37351                    for (var _i = 0, _a = _this.s.panes; _i < _a.length; _i++) {
37352                        var pane = _a[_i];
37353                        var src = _this.s.dt.column(pane.s.index).dataSrc();
37354                        if (!data.searchPanes[src]) {
37355                            data.searchPanes[src] = {};
37356                        }
37357                        if (!data.searchPanes_null[src]) {
37358                            data.searchPanes_null[src] = {};
37359                        }
37360                        if (pane.s.dtPane) {
37361                            var rowData = pane.s.dtPane.rows({ selected: true }).data().toArray();
37362                            for (var i = 0; i < rowData.length; i++) {
37363                                data.searchPanes[src][i] = rowData[i].filter;
37364                                if (!data.searchPanes[src][i]) {
37365                                    data.searchPanes_null[src][i] = true;
37366                                }
37367                                filterCount++;
37368                            }
37369                        }
37370                    }
37371                    // If there is a filter to be applied, then we need to read from the start of the result set
37372                    // and set the paging to 0. This matches the behaviour of client side processing
37373                    if (filterCount > 0) {
37374                        // If the number of filters has changed we need to read from the start of the
37375                        // result set and reset the paging
37376                        if (filterCount !== _this.s.filterCount) {
37377                            data.start = 0;
37378                            _this.s.page = 0;
37379                        }
37380                        // Otherwise it is a paging request and we need to read from whatever the paging has been set to
37381                        else {
37382                            data.start = _this.s.page * _this.s.dt.page.len();
37383                        }
37384                        _this.s.dt.page(_this.s.page);
37385                        _this.s.filterCount = filterCount;
37386                    }
37387                    if (_this.s.selectionList.length > 0) {
37388                        data.searchPanesLast = _this.s.dt
37389                            .column(_this.s.selectionList[_this.s.selectionList.length - 1].column)
37390                            .dataSrc();
37391                    }
37392                    // Config options that will change how the querying is done
37393                    data.searchPanes_options = {
37394                        cascade: _this.c.cascadePanes,
37395                        viewCount: _this.c.viewCount,
37396                        viewTotal: _this.c.viewTotal
37397                    };
37398                });
37399            }
37400            else {
37401                table.on('preXhr.dtsps', function () { return _this.s.panes.forEach(function (pane) { return pane.clearData(); }); });
37402            }
37403            // If the data is reloaded from the server then it is possible that it has changed completely,
37404            // so we need to rebuild the panes
37405            this.s.dt.on('xhr.dtsps', function (e, settings) {
37406                if (settings.nTable !== _this.s.dt.table().node()) {
37407                    return;
37408                }
37409                if (!_this.s.dt.page.info().serverSide) {
37410                    var processing_1 = false;
37411                    _this.s.dt.one('preDraw.dtsps', function () {
37412                        if (processing_1) {
37413                            return;
37414                        }
37415                        var page = _this.s.dt.page();
37416                        processing_1 = true;
37417                        _this.s.updating = true;
37418                        _this.dom.panes.empty();
37419                        for (var _i = 0, _a = _this.s.panes; _i < _a.length; _i++) {
37420                            var pane = _a[_i];
37421                            pane.clearData(); // Clears all of the bins and will mean that the data has to be re-read
37422                            // Pass a boolean to say whether this is the last choice made for maintaining selections
37423                            // when rebuilding
37424                            pane.rebuildPane(undefined, true);
37425                            _this.dom.panes.append(pane.dom.container);
37426                        }
37427                        if (!_this.s.dt.page.info().serverSide) {
37428                            _this.s.dt.draw();
37429                        }
37430                        _this.s.updating = false;
37431                        _this._updateSelection();
37432                        _this._checkMessage();
37433                        _this.s.dt.one('draw.dtsps', function () {
37434                            _this.s.updating = true;
37435                            _this.s.dt.page(page).draw(false);
37436                            _this.s.updating = false;
37437                        });
37438                    });
37439                }
37440            });
37441            // PreSelect any selections which have been defined using the preSelect option
37442            var selectList = this.c.preSelect;
37443            if (loadedFilter && loadedFilter.searchPanes && loadedFilter.searchPanes.selectionList) {
37444                selectList = loadedFilter.searchPanes.selectionList;
37445            }
37446            this._makeSelections(selectList);
37447            // Update the title bar to show how many filters have been selected
37448            this._updateFilterCount();
37449            // If the table is destroyed and restarted then clear the selections so that they do not persist.
37450            table.on('destroy.dtsps', function (e, settings) {
37451                if (settings !== hostSettings) {
37452                    return;
37453                }
37454                for (var _i = 0, _a = _this.s.panes; _i < _a.length; _i++) {
37455                    var pane = _a[_i];
37456                    pane.destroy();
37457                }
37458                table.off('.dtsps');
37459                _this.dom.showAll.off('.dtsps');
37460                _this.dom.clearAll.off('.dtsps');
37461                _this.dom.collapseAll.off('.dtsps');
37462                $$1(table.table().node()).off('.dtsps');
37463                _this.dom.container.detach();
37464                _this.clearSelections();
37465            });
37466            if (this.c.collapse) {
37467                this._setCollapseListener();
37468            }
37469            // When the clear All button has been pressed clear all of the selections in the panes
37470            if (this.c.clear) {
37471                this.dom.clearAll.on('click.dtsps', function () { return _this.clearSelections(); });
37472            }
37473            hostSettings._searchPanes = this;
37474            // This state save is required so that state is maintained over multiple refreshes if no actions are made
37475            this.s.dt.state.save();
37476        };
37477        /**
37478         * Updates the number of filters that have been applied in the title
37479         */
37480        SearchPanes.prototype._updateFilterCount = function () {
37481            var filterCount = 0;
37482            // Add the number of all of the filters throughout the panes
37483            for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
37484                var pane = _a[_i];
37485                if (pane.s.dtPane) {
37486                    filterCount += pane.getPaneCount();
37487                }
37488            }
37489            // Run the message through the internationalisation method to improve readability
37490            this.dom.title.html(this.s.dt.i18n('searchPanes.title', this.c.i18n.title, filterCount));
37491            if (this.c.filterChanged && typeof this.c.filterChanged === 'function') {
37492                this.c.filterChanged.call(this.s.dt, filterCount);
37493            }
37494            if (filterCount === 0) {
37495                this.dom.clearAll.addClass(this.classes.disabledButton).attr('disabled', 'true');
37496            }
37497            else {
37498                this.dom.clearAll.removeClass(this.classes.disabledButton).removeAttr('disabled');
37499            }
37500        };
37501        SearchPanes.version = '2.1.2';
37502        SearchPanes.classes = {
37503            clear: 'dtsp-clear',
37504            clearAll: 'dtsp-clearAll',
37505            collapseAll: 'dtsp-collapseAll',
37506            container: 'dtsp-searchPanes',
37507            disabledButton: 'dtsp-disabledButton',
37508            emptyMessage: 'dtsp-emptyMessage',
37509            hide: 'dtsp-hidden',
37510            panes: 'dtsp-panesContainer',
37511            search: 'dtsp-search',
37512            showAll: 'dtsp-showAll',
37513            title: 'dtsp-title',
37514            titleRow: 'dtsp-titleRow'
37515        };
37516        // Define SearchPanes default options
37517        SearchPanes.defaults = {
37518            cascadePanes: false,
37519            clear: true,
37520            collapse: true,
37521            columns: [],
37522            container: function (dt) {
37523                return dt.table().container();
37524            },
37525            filterChanged: undefined,
37526            i18n: {
37527                clearMessage: 'Clear All',
37528                clearPane: '&times;',
37529                collapse: {
37530                    0: 'SearchPanes',
37531                    _: 'SearchPanes (%d)'
37532                },
37533                collapseMessage: 'Collapse All',
37534                count: '{total}',
37535                emptyMessage: '<em>No data</em>',
37536                emptyPanes: 'No SearchPanes',
37537                loadMessage: 'Loading Search Panes...',
37538                showMessage: 'Show All',
37539                title: 'Filters Active - %d'
37540            },
37541            layout: 'auto',
37542            order: [],
37543            panes: [],
37544            preSelect: [],
37545            viewCount: true,
37546            viewTotal: false
37547        };
37548        return SearchPanes;
37549    }());
37550
37551    var __extends = (window && window.__extends) || (function () {
37552        var extendStatics = function (d, b) {
37553            extendStatics = Object.setPrototypeOf ||
37554                ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
37555                function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
37556            return extendStatics(d, b);
37557        };
37558        return function (d, b) {
37559            extendStatics(d, b);
37560            function __() { this.constructor = d; }
37561            d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
37562        };
37563    })();
37564    var SearchPanesST = /** @class */ (function (_super) {
37565        __extends(SearchPanesST, _super);
37566        function SearchPanesST(paneSettings, opts, fromPreInit) {
37567            if (fromPreInit === void 0) { fromPreInit = false; }
37568            var _this = this;
37569            var paneClass;
37570            if (opts.cascadePanes && opts.viewTotal) {
37571                paneClass = SearchPaneCascadeViewTotal;
37572            }
37573            else if (opts.cascadePanes) {
37574                paneClass = SearchPaneCascade;
37575            }
37576            else if (opts.viewTotal) {
37577                paneClass = SearchPaneViewTotal;
37578            }
37579            _this = _super.call(this, paneSettings, opts, fromPreInit, paneClass) || this;
37580            var dt = _this.s.dt;
37581            var loadedFilter = dt.state.loaded();
37582            var loadFn = function () { return _this._initSelectionListeners(true, loadedFilter && loadedFilter.searchPanes && loadedFilter.searchPanes.selectionList ?
37583                loadedFilter.searchPanes.selectionList :
37584                _this.c.preSelect); };
37585            if (dt.settings()[0]._bInitComplete) {
37586                loadFn();
37587            }
37588            else {
37589                dt.off('init.dtsps').on('init.dtsps', loadFn);
37590            }
37591            return _this;
37592        }
37593        /**
37594         * Ensures that the correct selection listeners are set for selection tracking
37595         *
37596         * @param preSelect Any values that are to be preselected
37597         */
37598        SearchPanesST.prototype._initSelectionListeners = function (isPreselect, preSelect) {
37599            if (isPreselect === void 0) { isPreselect = true; }
37600            if (preSelect === void 0) { preSelect = []; }
37601            if (isPreselect) {
37602                this.s.selectionList = preSelect;
37603            }
37604            // Set selection listeners for each pane
37605            for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
37606                var pane = _a[_i];
37607                if (pane.s.displayed) {
37608                    pane.s.dtPane
37609                        .off('select.dtsp')
37610                        .on('select.dtsp', this._update(pane))
37611                        .off('deselect.dtsp')
37612                        .on('deselect.dtsp', this._updateTimeout(pane));
37613                }
37614            }
37615            // Update on every draw
37616            this.s.dt.off('draw.dtsps').on('draw.dtsps', this._update());
37617            // Also update right now as table has just initialised
37618            this._updateSelectionList();
37619        };
37620        /**
37621         * Retrieve the total values from the server data
37622         */
37623        SearchPanesST.prototype._serverTotals = function () {
37624            for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
37625                var pane = _a[_i];
37626                if (pane.s.colOpts.show) {
37627                    var colTitle = this.s.dt.column(pane.s.index).dataSrc();
37628                    var blockVT = true;
37629                    // If any of the counts are not equal to the totals filtering must be active
37630                    if (this.s.serverData.searchPanes.options[colTitle]) {
37631                        for (var _b = 0, _c = this.s.serverData.searchPanes.options[colTitle]; _b < _c.length; _b++) {
37632                            var data = _c[_b];
37633                            if (data.total !== data.count) {
37634                                blockVT = false;
37635                                break;
37636                            }
37637                        }
37638                    }
37639                    // Set if filtering is present on the pane and populate the data arrays
37640                    pane.s.filteringActive = !blockVT;
37641                    pane._serverPopulate(this.s.serverData);
37642                }
37643            }
37644        };
37645        /**
37646         * Set's the function that is to be performed when a state is loaded
37647         *
37648         * Overrides the method in SearchPanes
37649         */
37650        SearchPanesST.prototype._stateLoadListener = function () {
37651            var _this = this;
37652            var stateLoadFunction = function (e, settings, data) {
37653                if (data.searchPanes === undefined) {
37654                    return;
37655                }
37656                // Set the selection list for the panes so that the correct
37657                // rows can be reselected and in the right order
37658                _this.s.selectionList =
37659                    data.searchPanes.selectionList ?
37660                        data.searchPanes.selectionList :
37661                        [];
37662                // Find the panes that match from the state and the actual instance
37663                if (data.searchPanes.panes) {
37664                    for (var _i = 0, _a = data.searchPanes.panes; _i < _a.length; _i++) {
37665                        var loadedPane = _a[_i];
37666                        for (var _b = 0, _c = _this.s.panes; _b < _c.length; _b++) {
37667                            var pane = _c[_b];
37668                            if (loadedPane.id === pane.s.index && pane.s.dtPane) {
37669                                // Set the value of the searchbox
37670                                pane.dom.searchBox.val(loadedPane.searchTerm);
37671                                // Set the value of the order
37672                                pane.s.dtPane.order(loadedPane.order);
37673                            }
37674                        }
37675                    }
37676                }
37677                _this._updateSelectionList();
37678            };
37679            this.s.dt.off('stateLoadParams.dtsps', stateLoadFunction).on('stateLoadParams.dtsps', stateLoadFunction);
37680        };
37681        /**
37682         * Remove the function's actions when using cascade
37683         *
37684         * Overrides the method in SearchPanes
37685         */
37686        SearchPanesST.prototype._updateSelection = function () {
37687            return;
37688        };
37689        /**
37690         * Returns a function that updates the selection list based on a specific pane
37691         * Also clears the timeout to stop the deselect from running
37692         *
37693         * @param pane the pane that is to have it's selections loaded
37694         */
37695        SearchPanesST.prototype._update = function (pane) {
37696            var _this = this;
37697            if (pane === void 0) { pane = undefined; }
37698            return function () {
37699                if (pane) {
37700                    clearTimeout(pane.s.deselectTimeout);
37701                }
37702                _this._updateSelectionList(pane);
37703            };
37704        };
37705        /**
37706         * Returns a function that updates the selection list based on a specific pane
37707         * Also sets a timeout incase a select is about to be made
37708         *
37709         * @param pane the pane that is to have it's selections loaded
37710         */
37711        SearchPanesST.prototype._updateTimeout = function (pane) {
37712            var _this = this;
37713            if (pane === void 0) { pane = undefined; }
37714            return function () { return pane ?
37715                pane.s.deselectTimeout = setTimeout(function () { return _this._updateSelectionList(pane); }, 50) :
37716                _this._updateSelectionList(); };
37717        };
37718        /**
37719         * Updates the selection list to include the latest selections for a given pane
37720         *
37721         * @param index The index of the pane that is to be updated
37722         * @param selected Which rows are selected within the pane
37723         */
37724        SearchPanesST.prototype._updateSelectionList = function (paneIn) {
37725            if (paneIn === void 0) { paneIn = undefined; }
37726            // Bail if any of these flags are set
37727            if (this.s.pagingST) {
37728                // Reset pagingST flag
37729                this.s.pagingST = false;
37730                return;
37731            }
37732            else if (this.s.updating || paneIn && paneIn.s.serverSelecting) {
37733                return;
37734            }
37735            if (paneIn !== undefined) {
37736                if (this.s.dt.page.info().serverSide) {
37737                    paneIn._updateSelection();
37738                }
37739                // Get filter values for all of the rows and the selections
37740                var rows = paneIn.s.dtPane.rows({ selected: true }).data().toArray().map(function (el) { return el.filter; });
37741                this.s.selectionList = this.s.selectionList.filter(function (selection) { return selection.column !== paneIn.s.index; });
37742                if (rows.length > 0) {
37743                    this.s.selectionList.push({
37744                        column: paneIn.s.index,
37745                        rows: rows
37746                    });
37747                    paneIn.dom.clear.removeClass(this.classes.disabledButton).removeAttr('disabled');
37748                }
37749                else {
37750                    paneIn.dom.clear.addClass(this.classes.disabledButton).attr('disabled', 'true');
37751                }
37752                if (this.s.dt.page.info().serverSide) {
37753                    this.s.dt.draw(false);
37754                }
37755            }
37756            this._remakeSelections();
37757            this._updateFilterCount();
37758        };
37759        /**
37760         * Remake the selections that were present before new data or calculations have occured
37761         */
37762        SearchPanesST.prototype._remakeSelections = function () {
37763            this.s.updating = true;
37764            if (!this.s.dt.page.info().serverSide) {
37765                var tmpSL = this.s.selectionList;
37766                var anotherFilter = false;
37767                this.clearSelections();
37768                this.s.dt.draw(false);
37769                // When there are no selections present if the length of the data does not match the searched data
37770                // then another filter is present
37771                if (this.s.dt.rows().toArray()[0].length > this.s.dt.rows({ search: 'applied' }).toArray()[0].length) {
37772                    anotherFilter = true;
37773                }
37774                this.s.selectionList = tmpSL;
37775                // Update the rows in each pane
37776                for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
37777                    var pane = _a[_i];
37778                    if (pane.s.displayed) {
37779                        pane.s.filteringActive = anotherFilter;
37780                        pane.updateRows();
37781                    }
37782                }
37783                for (var _b = 0, _c = this.s.selectionList; _b < _c.length; _b++) {
37784                    var selection = _c[_b];
37785                    var pane = void 0;
37786                    for (var _d = 0, _e = this.s.panes; _d < _e.length; _d++) {
37787                        var paneCheck = _e[_d];
37788                        if (paneCheck.s.index === selection.column) {
37789                            pane = paneCheck;
37790                            break;
37791                        }
37792                    }
37793                    if (!pane.s.dtPane) {
37794                        continue;
37795                    }
37796                    var ids = pane.s.dtPane.rows().indexes().toArray();
37797                    // Select the rows that are present in the selection list
37798                    for (var i = 0; i < selection.rows.length; i++) {
37799                        var rowFound = false;
37800                        for (var _f = 0, ids_1 = ids; _f < ids_1.length; _f++) {
37801                            var id = ids_1[_f];
37802                            var currRow = pane.s.dtPane.row(id);
37803                            var data = currRow.data();
37804                            if (selection.rows[i] === data.filter) {
37805                                currRow.select();
37806                                rowFound = true;
37807                            }
37808                        }
37809                        if (!rowFound) {
37810                            selection.rows.splice(i, 1);
37811                            i--;
37812                        }
37813                    }
37814                    pane.s.selections = selection.rows;
37815                    // If there are no rows selected then don't bother continuing past here
37816                    // Will just increase processing time and skew the rows that are shown in the table
37817                    if (selection.rows.length === 0) {
37818                        continue;
37819                    }
37820                    // Update the table to display the current results
37821                    this.s.dt.draw(false);
37822                    var filteringActive = false;
37823                    var filterCount = 0;
37824                    var prevSelectedPanes = 0;
37825                    var selectedPanes = 0;
37826                    // Add the number of all of the filters throughout the panes
37827                    for (var _g = 0, _h = this.s.panes; _g < _h.length; _g++) {
37828                        var currPane = _h[_g];
37829                        if (currPane.s.dtPane) {
37830                            filterCount += currPane.getPaneCount();
37831                            if (filterCount > prevSelectedPanes) {
37832                                selectedPanes++;
37833                                prevSelectedPanes = filterCount;
37834                            }
37835                        }
37836                    }
37837                    filteringActive = filterCount > 0;
37838                    for (var _j = 0, _k = this.s.panes; _j < _k.length; _j++) {
37839                        var currPane = _k[_j];
37840                        if (currPane.s.displayed) {
37841                            // Set the filtering active flag
37842                            if (anotherFilter || pane.s.index !== currPane.s.index || !filteringActive) {
37843                                currPane.s.filteringActive = filteringActive || anotherFilter;
37844                            }
37845                            else if (selectedPanes === 1) {
37846                                currPane.s.filteringActive = false;
37847                            }
37848                            // Update the rows to show correct counts
37849                            if (currPane.s.index !== pane.s.index) {
37850                                currPane.updateRows();
37851                            }
37852                        }
37853                    }
37854                }
37855                // Update table to show final search results
37856                this.s.dt.draw(false);
37857            }
37858            else {
37859                // Identify the last pane to have a change in selection
37860                var pane = void 0;
37861                if (this.s.selectionList.length > 0) {
37862                    pane = this.s.panes[this.s.selectionList[this.s.selectionList.length - 1].column];
37863                }
37864                // Update the rows of all of the other panes
37865                for (var _l = 0, _m = this.s.panes; _l < _m.length; _l++) {
37866                    var currPane = _m[_l];
37867                    if (currPane.s.displayed && (!pane || currPane.s.index !== pane.s.index)) {
37868                        currPane.updateRows();
37869                    }
37870                }
37871            }
37872            this.s.updating = false;
37873        };
37874        return SearchPanesST;
37875    }(SearchPanes));
37876
37877    /*! SearchPanes 2.1.2
37878     * © SpryMedia Ltd - datatables.net/license
37879     */
37880    setJQuery$4($);
37881    setJQuery($);
37882    setJQuery$3($);
37883    setJQuery$2($);
37884    setJQuery$1($);
37885    var dataTable = $.fn.dataTable;
37886    // eslint-disable-next-line no-extra-parens
37887    dataTable.SearchPanes = SearchPanes;
37888    // eslint-disable-next-line no-extra-parens
37889    DataTable.SearchPanes = SearchPanes;
37890    // eslint-disable-next-line no-extra-parens
37891    dataTable.SearchPanesST = SearchPanesST;
37892    // eslint-disable-next-line no-extra-parens
37893    DataTable.SearchPanesST = SearchPanesST;
37894    // eslint-disable-next-line no-extra-parens
37895    dataTable.SearchPane = SearchPane;
37896    // eslint-disable-next-line no-extra-parens
37897    DataTable.SearchPane = SearchPane;
37898    // eslint-disable-next-line no-extra-parens
37899    dataTable.SearchPaneViewTotal = SearchPaneViewTotal;
37900    // eslint-disable-next-line no-extra-parens
37901    DataTable.SearchPaneViewTotal = SearchPaneViewTotal;
37902    // eslint-disable-next-line no-extra-parens
37903    dataTable.SearchPaneCascade = SearchPaneCascade;
37904    // eslint-disable-next-line no-extra-parens
37905    DataTable.SearchPaneCascade = SearchPaneCascade;
37906    // eslint-disable-next-line no-extra-parens
37907    dataTable.SearchPaneCascadeViewTotal = SearchPaneCascadeViewTotal;
37908    // eslint-disable-next-line no-extra-parens
37909    DataTable.SearchPaneCascadeViewTotal = SearchPaneCascadeViewTotal;
37910    // eslint-disable-next-line no-extra-parens
37911    var apiRegister = $.fn.dataTable.Api.register;
37912    apiRegister('searchPanes()', function () {
37913        return this;
37914    });
37915    apiRegister('searchPanes.clearSelections()', function () {
37916        return this.iterator('table', function (ctx) {
37917            if (ctx._searchPanes) {
37918                ctx._searchPanes.clearSelections();
37919            }
37920        });
37921    });
37922    apiRegister('searchPanes.rebuildPane()', function (targetIdx, maintainSelections) {
37923        return this.iterator('table', function (ctx) {
37924            if (ctx._searchPanes) {
37925                ctx._searchPanes.rebuild(targetIdx, maintainSelections);
37926            }
37927        });
37928    });
37929    apiRegister('searchPanes.resizePanes()', function () {
37930        var ctx = this.context[0];
37931        return ctx._searchPanes ?
37932            ctx._searchPanes.resizePanes() :
37933            null;
37934    });
37935    apiRegister('searchPanes.container()', function () {
37936        var ctx = this.context[0];
37937        return ctx._searchPanes
37938            ? ctx._searchPanes.getNode()
37939            : null;
37940    });
37941    DataTable.ext.buttons.searchPanesClear = {
37942        action: function (e, dt) {
37943            dt.searchPanes.clearSelections();
37944        },
37945        text: 'Clear Panes'
37946    };
37947    DataTable.ext.buttons.searchPanes = {
37948        action: function (e, dt, node, config) {
37949            var _this = this;
37950            if (!config._panes) {
37951                // No SearchPanes on this button yet - initialise and show
37952                this.processing(true);
37953                setTimeout(function () {
37954                    _buttonSourced(dt, node, config);
37955                    _this.popover(config._panes.getNode(), {
37956                        align: 'container',
37957                        span: 'container'
37958                    });
37959                    config._panes.rebuild(undefined, true);
37960                    _this.processing(false);
37961                }, 10);
37962            }
37963            else {
37964                // Already got SP - show it
37965                this.popover(config._panes.getNode(), {
37966                    align: 'container',
37967                    span: 'container'
37968                });
37969                config._panes.rebuild(undefined, true);
37970            }
37971        },
37972        init: function (dt, node, config) {
37973            dt.button(node).text(config.text || dt.i18n('searchPanes.collapse', 'SearchPanes', 0));
37974            // If state save is enabled, we need to initialise SP immediately
37975            // to allow any saved state to be restored. Otherwise we can delay
37976            // the init until needed by button press
37977            if (dt.init().stateSave) {
37978                _buttonSourced(dt, node, config);
37979            }
37980        },
37981        config: {},
37982        text: ''
37983    };
37984    function _buttonSourced(dt, node, config) {
37985        var buttonOpts = $.extend({
37986            filterChanged: function (count) {
37987                dt.button(node).text(dt.i18n('searchPanes.collapse', dt.context[0].oLanguage.searchPanes !== undefined ?
37988                    dt.context[0].oLanguage.searchPanes.collapse :
37989                    dt.context[0]._searchPanes.c.i18n.collapse, count));
37990            }
37991        }, config.config);
37992        var panes = buttonOpts && (buttonOpts.cascadePanes || buttonOpts.viewTotal) ?
37993            new DataTable.SearchPanesST(dt, buttonOpts) :
37994            new DataTable.SearchPanes(dt, buttonOpts);
37995        dt.button(node).text(config.text || dt.i18n('searchPanes.collapse', panes.c.i18n.collapse, 0));
37996        config._panes = panes;
37997    }
37998    function _init(settings, options, fromPre) {
37999        if (options === void 0) { options = null; }
38000        if (fromPre === void 0) { fromPre = false; }
38001        var api = new dataTable.Api(settings);
38002        var opts = options
38003            ? options
38004            : api.init().searchPanes || dataTable.defaults.searchPanes;
38005        var searchPanes = opts && (opts.cascadePanes || opts.viewTotal) ?
38006            new SearchPanesST(api, opts, fromPre) :
38007            new SearchPanes(api, opts, fromPre);
38008        var node = searchPanes.getNode();
38009        return node;
38010    }
38011    // Attach a listener to the document which listens for DataTables initialisation
38012    // events so we can automatically initialise
38013    $(document).on('preInit.dt.dtsp', function (e, settings) {
38014        if (e.namespace !== 'dt') {
38015            return;
38016        }
38017        if (settings.oInit.searchPanes ||
38018            DataTable.defaults.searchPanes) {
38019            if (!settings._searchPanes) {
38020                _init(settings, null, true);
38021            }
38022        }
38023    });
38024    // DataTables `dom` feature option
38025    DataTable.ext.feature.push({
38026        cFeature: 'P',
38027        fnInit: _init
38028    });
38029    // DataTables 2 layout feature
38030    if (DataTable.ext.features) {
38031        DataTable.ext.features.register('searchPanes', _init);
38032    }
38033
38034})();
38035
38036
38037return DataTable;
38038}));
38039
38040
38041/*! Bootstrap integration for DataTables' SearchPanes
38042 * © SpryMedia Ltd - datatables.net/license
38043 */
38044
38045(function( factory ){
38046	if ( typeof define === 'function' && define.amd ) {
38047		// AMD
38048		define( ['jquery', 'datatables.net-dt', 'datatables.net-searchpanes'], function ( $ ) {
38049			return factory( $, window, document );
38050		} );
38051	}
38052	else if ( typeof exports === 'object' ) {
38053		// CommonJS
38054		var jq = require('jquery');
38055		var cjsRequires = function (root, $) {
38056			if ( ! $.fn.dataTable ) {
38057				require('datatables.net-dt')(root, $);
38058			}
38059
38060			if ( ! $.fn.dataTable.SearchPanes ) {
38061				require('datatables.net-searchpanes')(root, $);
38062			}
38063		};
38064
38065		if (typeof window !== 'undefined') {
38066			module.exports = function (root, $) {
38067				if ( ! root ) {
38068					// CommonJS environments without a window global must pass a
38069					// root. This will give an error otherwise
38070					root = window;
38071				}
38072
38073				if ( ! $ ) {
38074					$ = jq( root );
38075				}
38076
38077				cjsRequires( root, $ );
38078				return factory( $, root, root.document );
38079			};
38080		}
38081		else {
38082			cjsRequires( window, jq );
38083			module.exports = factory( jq, window, window.document );
38084		}
38085	}
38086	else {
38087		// Browser
38088		factory( jQuery, window, document );
38089	}
38090}(function( $, window, document, undefined ) {
38091'use strict';
38092var DataTable = $.fn.dataTable;
38093
38094
38095
38096
38097return DataTable;
38098}));
38099
38100
38101/*! Select for DataTables 1.6.2
38102 * © SpryMedia Ltd - datatables.net/license/mit
38103 */
38104
38105(function( factory ){
38106	if ( typeof define === 'function' && define.amd ) {
38107		// AMD
38108		define( ['jquery', 'datatables.net'], function ( $ ) {
38109			return factory( $, window, document );
38110		} );
38111	}
38112	else if ( typeof exports === 'object' ) {
38113		// CommonJS
38114		var jq = require('jquery');
38115		var cjsRequires = function (root, $) {
38116			if ( ! $.fn.dataTable ) {
38117				require('datatables.net')(root, $);
38118			}
38119		};
38120
38121		if (typeof window !== 'undefined') {
38122			module.exports = function (root, $) {
38123				if ( ! root ) {
38124					// CommonJS environments without a window global must pass a
38125					// root. This will give an error otherwise
38126					root = window;
38127				}
38128
38129				if ( ! $ ) {
38130					$ = jq( root );
38131				}
38132
38133				cjsRequires( root, $ );
38134				return factory( $, root, root.document );
38135			};
38136		}
38137		else {
38138			cjsRequires( window, jq );
38139			module.exports = factory( jq, window, window.document );
38140		}
38141	}
38142	else {
38143		// Browser
38144		factory( jQuery, window, document );
38145	}
38146}(function( $, window, document, undefined ) {
38147'use strict';
38148var DataTable = $.fn.dataTable;
38149
38150
38151
38152// Version information for debugger
38153DataTable.select = {};
38154
38155DataTable.select.version = '1.6.2';
38156
38157DataTable.select.init = function ( dt ) {
38158	var ctx = dt.settings()[0];
38159
38160	if (ctx._select) {
38161		return;
38162	}
38163
38164	var savedSelected = dt.state.loaded();
38165
38166	var selectAndSave = function(e, settings, data) {
38167		if(data === null || data.select === undefined) {
38168			return;
38169		}
38170
38171		// Clear any currently selected rows, before restoring state
38172		// None will be selected on first initialisation
38173		if (dt.rows({selected: true}).any()) {
38174			dt.rows().deselect();
38175		}
38176		if (data.select.rows !== undefined) {
38177			dt.rows(data.select.rows).select();
38178		}
38179
38180		if (dt.columns({selected: true}).any()) {
38181			dt.columns().deselect();
38182		}
38183		if (data.select.columns !== undefined) {
38184			dt.columns(data.select.columns).select();
38185		}
38186
38187		if (dt.cells({selected: true}).any()) {
38188			dt.cells().deselect();
38189		}
38190		if (data.select.cells !== undefined) {
38191			for(var i = 0; i < data.select.cells.length; i++) {
38192				dt.cell(data.select.cells[i].row, data.select.cells[i].column).select();
38193			}
38194		}
38195
38196		dt.state.save();
38197	}
38198	
38199	dt
38200		.on('stateSaveParams', function(e, settings, data) {
38201			data.select = {};
38202			data.select.rows = dt.rows({selected:true}).ids(true).toArray();
38203			data.select.columns = dt.columns({selected:true})[0];
38204			data.select.cells = dt.cells({selected:true})[0].map(function(coords) {
38205				return {row: dt.row(coords.row).id(true), column: coords.column}
38206			});
38207		})
38208		.on('stateLoadParams', selectAndSave)
38209		.one('init', function() {
38210			selectAndSave(undefined, undefined, savedSelected);
38211		});
38212
38213	var init = ctx.oInit.select;
38214	var defaults = DataTable.defaults.select;
38215	var opts = init === undefined ?
38216		defaults :
38217		init;
38218
38219	// Set defaults
38220	var items = 'row';
38221	var style = 'api';
38222	var blurable = false;
38223	var toggleable = true;
38224	var info = true;
38225	var selector = 'td, th';
38226	var className = 'selected';
38227	var setStyle = false;
38228
38229	ctx._select = {};
38230
38231	// Initialisation customisations
38232	if ( opts === true ) {
38233		style = 'os';
38234		setStyle = true;
38235	}
38236	else if ( typeof opts === 'string' ) {
38237		style = opts;
38238		setStyle = true;
38239	}
38240	else if ( $.isPlainObject( opts ) ) {
38241		if ( opts.blurable !== undefined ) {
38242			blurable = opts.blurable;
38243		}
38244		
38245		if ( opts.toggleable !== undefined ) {
38246			toggleable = opts.toggleable;
38247		}
38248
38249		if ( opts.info !== undefined ) {
38250			info = opts.info;
38251		}
38252
38253		if ( opts.items !== undefined ) {
38254			items = opts.items;
38255		}
38256
38257		if ( opts.style !== undefined ) {
38258			style = opts.style;
38259			setStyle = true;
38260		}
38261		else {
38262			style = 'os';
38263			setStyle = true;
38264		}
38265
38266		if ( opts.selector !== undefined ) {
38267			selector = opts.selector;
38268		}
38269
38270		if ( opts.className !== undefined ) {
38271			className = opts.className;
38272		}
38273	}
38274
38275	dt.select.selector( selector );
38276	dt.select.items( items );
38277	dt.select.style( style );
38278	dt.select.blurable( blurable );
38279	dt.select.toggleable( toggleable );
38280	dt.select.info( info );
38281	ctx._select.className = className;
38282
38283
38284	// Sort table based on selected rows. Requires Select Datatables extension
38285	$.fn.dataTable.ext.order['select-checkbox'] = function ( settings, col ) {
38286		return this.api().column( col, {order: 'index'} ).nodes().map( function ( td ) {
38287			if ( settings._select.items === 'row' ) {
38288				return $( td ).parent().hasClass( settings._select.className );
38289			} else if ( settings._select.items === 'cell' ) {
38290				return $( td ).hasClass( settings._select.className );
38291			}
38292			return false;
38293		});
38294	};
38295
38296	// If the init options haven't enabled select, but there is a selectable
38297	// class name, then enable
38298	if ( ! setStyle && $( dt.table().node() ).hasClass( 'selectable' ) ) {
38299		dt.select.style( 'os' );
38300	}
38301};
38302
38303/*
38304
38305Select is a collection of API methods, event handlers, event emitters and
38306buttons (for the `Buttons` extension) for DataTables. It provides the following
38307features, with an overview of how they are implemented:
38308
38309## Selection of rows, columns and cells. Whether an item is selected or not is
38310   stored in:
38311
38312* rows: a `_select_selected` property which contains a boolean value of the
38313  DataTables' `aoData` object for each row
38314* columns: a `_select_selected` property which contains a boolean value of the
38315  DataTables' `aoColumns` object for each column
38316* cells: a `_selected_cells` property which contains an array of boolean values
38317  of the `aoData` object for each row. The array is the same length as the
38318  columns array, with each element of it representing a cell.
38319
38320This method of using boolean flags allows Select to operate when nodes have not
38321been created for rows / cells (DataTables' defer rendering feature).
38322
38323## API methods
38324
38325A range of API methods are available for triggering selection and de-selection
38326of rows. Methods are also available to configure the selection events that can
38327be triggered by an end user (such as which items are to be selected). To a large
38328extent, these of API methods *is* Select. It is basically a collection of helper
38329functions that can be used to select items in a DataTable.
38330
38331Configuration of select is held in the object `_select` which is attached to the
38332DataTables settings object on initialisation. Select being available on a table
38333is not optional when Select is loaded, but its default is for selection only to
38334be available via the API - so the end user wouldn't be able to select rows
38335without additional configuration.
38336
38337The `_select` object contains the following properties:
38338
38339```
38340{
38341	items:string       - Can be `rows`, `columns` or `cells`. Defines what item 
38342	                     will be selected if the user is allowed to activate row
38343	                     selection using the mouse.
38344	style:string       - Can be `none`, `single`, `multi` or `os`. Defines the
38345	                     interaction style when selecting items
38346	blurable:boolean   - If row selection can be cleared by clicking outside of
38347	                     the table
38348	toggleable:boolean - If row selection can be cancelled by repeated clicking
38349	                     on the row
38350	info:boolean       - If the selection summary should be shown in the table
38351	                     information elements
38352}
38353```
38354
38355In addition to the API methods, Select also extends the DataTables selector
38356options for rows, columns and cells adding a `selected` option to the selector
38357options object, allowing the developer to select only selected items or
38358unselected items.
38359
38360## Mouse selection of items
38361
38362Clicking on items can be used to select items. This is done by a simple event
38363handler that will select the items using the API methods.
38364
38365 */
38366
38367
38368/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
38369 * Local functions
38370 */
38371
38372/**
38373 * Add one or more cells to the selection when shift clicking in OS selection
38374 * style cell selection.
38375 *
38376 * Cell range is more complicated than row and column as we want to select
38377 * in the visible grid rather than by index in sequence. For example, if you
38378 * click first in cell 1-1 and then shift click in 2-2 - cells 1-2 and 2-1
38379 * should also be selected (and not 1-3, 1-4. etc)
38380 * 
38381 * @param  {DataTable.Api} dt   DataTable
38382 * @param  {object}        idx  Cell index to select to
38383 * @param  {object}        last Cell index to select from
38384 * @private
38385 */
38386function cellRange( dt, idx, last )
38387{
38388	var indexes;
38389	var columnIndexes;
38390	var rowIndexes;
38391	var selectColumns = function ( start, end ) {
38392		if ( start > end ) {
38393			var tmp = end;
38394			end = start;
38395			start = tmp;
38396		}
38397		
38398		var record = false;
38399		return dt.columns( ':visible' ).indexes().filter( function (i) {
38400			if ( i === start ) {
38401				record = true;
38402			}
38403			
38404			if ( i === end ) { // not else if, as start might === end
38405				record = false;
38406				return true;
38407			}
38408
38409			return record;
38410		} );
38411	};
38412
38413	var selectRows = function ( start, end ) {
38414		var indexes = dt.rows( { search: 'applied' } ).indexes();
38415
38416		// Which comes first - might need to swap
38417		if ( indexes.indexOf( start ) > indexes.indexOf( end ) ) {
38418			var tmp = end;
38419			end = start;
38420			start = tmp;
38421		}
38422
38423		var record = false;
38424		return indexes.filter( function (i) {
38425			if ( i === start ) {
38426				record = true;
38427			}
38428			
38429			if ( i === end ) {
38430				record = false;
38431				return true;
38432			}
38433
38434			return record;
38435		} );
38436	};
38437
38438	if ( ! dt.cells( { selected: true } ).any() && ! last ) {
38439		// select from the top left cell to this one
38440		columnIndexes = selectColumns( 0, idx.column );
38441		rowIndexes = selectRows( 0 , idx.row );
38442	}
38443	else {
38444		// Get column indexes between old and new
38445		columnIndexes = selectColumns( last.column, idx.column );
38446		rowIndexes = selectRows( last.row , idx.row );
38447	}
38448
38449	indexes = dt.cells( rowIndexes, columnIndexes ).flatten();
38450
38451	if ( ! dt.cells( idx, { selected: true } ).any() ) {
38452		// Select range
38453		dt.cells( indexes ).select();
38454	}
38455	else {
38456		// Deselect range
38457		dt.cells( indexes ).deselect();
38458	}
38459}
38460
38461/**
38462 * Disable mouse selection by removing the selectors
38463 *
38464 * @param {DataTable.Api} dt DataTable to remove events from
38465 * @private
38466 */
38467function disableMouseSelection( dt )
38468{
38469	var ctx = dt.settings()[0];
38470	var selector = ctx._select.selector;
38471
38472	$( dt.table().container() )
38473		.off( 'mousedown.dtSelect', selector )
38474		.off( 'mouseup.dtSelect', selector )
38475		.off( 'click.dtSelect', selector );
38476
38477	$('body').off( 'click.dtSelect' + _safeId(dt.table().node()) );
38478}
38479
38480/**
38481 * Attach mouse listeners to the table to allow mouse selection of items
38482 *
38483 * @param {DataTable.Api} dt DataTable to remove events from
38484 * @private
38485 */
38486function enableMouseSelection ( dt )
38487{
38488	var container = $( dt.table().container() );
38489	var ctx = dt.settings()[0];
38490	var selector = ctx._select.selector;
38491	var matchSelection;
38492
38493	container
38494		.on( 'mousedown.dtSelect', selector, function(e) {
38495			// Disallow text selection for shift clicking on the table so multi
38496			// element selection doesn't look terrible!
38497			if ( e.shiftKey || e.metaKey || e.ctrlKey ) {
38498				container
38499					.css( '-moz-user-select', 'none' )
38500					.one('selectstart.dtSelect', selector, function () {
38501						return false;
38502					} );
38503			}
38504
38505			if ( window.getSelection ) {
38506				matchSelection = window.getSelection();
38507			}
38508		} )
38509		.on( 'mouseup.dtSelect', selector, function() {
38510			// Allow text selection to occur again, Mozilla style (tested in FF
38511			// 35.0.1 - still required)
38512			container.css( '-moz-user-select', '' );
38513		} )
38514		.on( 'click.dtSelect', selector, function ( e ) {
38515			var items = dt.select.items();
38516			var idx;
38517
38518			// If text was selected (click and drag), then we shouldn't change
38519			// the row's selected state
38520			if ( matchSelection ) {
38521				var selection = window.getSelection();
38522
38523				// If the element that contains the selection is not in the table, we can ignore it
38524				// This can happen if the developer selects text from the click event
38525				if ( ! selection.anchorNode || $(selection.anchorNode).closest('table')[0] === dt.table().node() ) {
38526					if ( selection !== matchSelection ) {
38527						return;
38528					}
38529				}
38530			}
38531
38532			var ctx = dt.settings()[0];
38533			var wrapperClass = dt.settings()[0].oClasses.sWrapper.trim().replace(/ +/g, '.');
38534
38535			// Ignore clicks inside a sub-table
38536			if ( $(e.target).closest('div.'+wrapperClass)[0] != dt.table().container() ) {
38537				return;
38538			}
38539
38540			var cell = dt.cell( $(e.target).closest('td, th') );
38541
38542			// Check the cell actually belongs to the host DataTable (so child
38543			// rows, etc, are ignored)
38544			if ( ! cell.any() ) {
38545				return;
38546			}
38547
38548			var event = $.Event('user-select.dt');
38549			eventTrigger( dt, event, [ items, cell, e ] );
38550
38551			if ( event.isDefaultPrevented() ) {
38552				return;
38553			}
38554
38555			var cellIndex = cell.index();
38556			if ( items === 'row' ) {
38557				idx = cellIndex.row;
38558				typeSelect( e, dt, ctx, 'row', idx );
38559			}
38560			else if ( items === 'column' ) {
38561				idx = cell.index().column;
38562				typeSelect( e, dt, ctx, 'column', idx );
38563			}
38564			else if ( items === 'cell' ) {
38565				idx = cell.index();
38566				typeSelect( e, dt, ctx, 'cell', idx );
38567			}
38568
38569			ctx._select_lastCell = cellIndex;
38570		} );
38571
38572	// Blurable
38573	$('body').on( 'click.dtSelect' + _safeId(dt.table().node()), function ( e ) {
38574		if ( ctx._select.blurable ) {
38575			// If the click was inside the DataTables container, don't blur
38576			if ( $(e.target).parents().filter( dt.table().container() ).length ) {
38577				return;
38578			}
38579
38580			// Ignore elements which have been removed from the DOM (i.e. paging
38581			// buttons)
38582			if ( $(e.target).parents('html').length === 0 ) {
38583			 	return;
38584			}
38585
38586			// Don't blur in Editor form
38587			if ( $(e.target).parents('div.DTE').length ) {
38588				return;
38589			}
38590
38591			var event = $.Event('select-blur.dt');
38592			eventTrigger( dt, event, [ e.target, e ] );
38593
38594			if ( event.isDefaultPrevented() ) {
38595				return;
38596			}
38597
38598			clear( ctx, true );
38599		}
38600	} );
38601}
38602
38603/**
38604 * Trigger an event on a DataTable
38605 *
38606 * @param {DataTable.Api} api      DataTable to trigger events on
38607 * @param  {boolean}      selected true if selected, false if deselected
38608 * @param  {string}       type     Item type acting on
38609 * @param  {boolean}      any      Require that there are values before
38610 *     triggering
38611 * @private
38612 */
38613function eventTrigger ( api, type, args, any )
38614{
38615	if ( any && ! api.flatten().length ) {
38616		return;
38617	}
38618
38619	if ( typeof type === 'string' ) {
38620		type = type +'.dt';
38621	}
38622
38623	args.unshift( api );
38624
38625	$(api.table().node()).trigger( type, args );
38626}
38627
38628/**
38629 * Update the information element of the DataTable showing information about the
38630 * items selected. This is done by adding tags to the existing text
38631 * 
38632 * @param {DataTable.Api} api DataTable to update
38633 * @private
38634 */
38635function info ( api )
38636{
38637	var ctx = api.settings()[0];
38638
38639	if ( ! ctx._select.info || ! ctx.aanFeatures.i ) {
38640		return;
38641	}
38642
38643	if ( api.select.style() === 'api' ) {
38644		return;
38645	}
38646
38647	var rows    = api.rows( { selected: true } ).flatten().length;
38648	var columns = api.columns( { selected: true } ).flatten().length;
38649	var cells   = api.cells( { selected: true } ).flatten().length;
38650
38651	var add = function ( el, name, num ) {
38652		el.append( $('<span class="select-item"/>').append( api.i18n(
38653			'select.'+name+'s',
38654			{ _: '%d '+name+'s selected', 0: '', 1: '1 '+name+' selected' },
38655			num
38656		) ) );
38657	};
38658
38659	// Internal knowledge of DataTables to loop over all information elements
38660	$.each( ctx.aanFeatures.i, function ( i, el ) {
38661		el = $(el);
38662
38663		var output  = $('<span class="select-info"/>');
38664		add( output, 'row', rows );
38665		add( output, 'column', columns );
38666		add( output, 'cell', cells  );
38667
38668		var exisiting = el.children('span.select-info');
38669		if ( exisiting.length ) {
38670			exisiting.remove();
38671		}
38672
38673		if ( output.text() !== '' ) {
38674			el.append( output );
38675		}
38676	} );
38677}
38678
38679/**
38680 * Initialisation of a new table. Attach event handlers and callbacks to allow
38681 * Select to operate correctly.
38682 *
38683 * This will occur _after_ the initial DataTables initialisation, although
38684 * before Ajax data is rendered, if there is ajax data
38685 *
38686 * @param  {DataTable.settings} ctx Settings object to operate on
38687 * @private
38688 */
38689function init ( ctx ) {
38690	var api = new DataTable.Api( ctx );
38691	ctx._select_init = true;
38692
38693	// Row callback so that classes can be added to rows and cells if the item
38694	// was selected before the element was created. This will happen with the
38695	// `deferRender` option enabled.
38696	// 
38697	// This method of attaching to `aoRowCreatedCallback` is a hack until
38698	// DataTables has proper events for row manipulation If you are reviewing
38699	// this code to create your own plug-ins, please do not do this!
38700	ctx.aoRowCreatedCallback.push( {
38701		fn: function ( row, data, index ) {
38702			var i, ien;
38703			var d = ctx.aoData[ index ];
38704
38705			// Row
38706			if ( d._select_selected ) {
38707				$( row ).addClass( ctx._select.className );
38708			}
38709
38710			// Cells and columns - if separated out, we would need to do two
38711			// loops, so it makes sense to combine them into a single one
38712			for ( i=0, ien=ctx.aoColumns.length ; i<ien ; i++ ) {
38713				if ( ctx.aoColumns[i]._select_selected || (d._selected_cells && d._selected_cells[i]) ) {
38714					$(d.anCells[i]).addClass( ctx._select.className );
38715				}
38716			}
38717		},
38718		sName: 'select-deferRender'
38719	} );
38720
38721	// On Ajax reload we want to reselect all rows which are currently selected,
38722	// if there is an rowId (i.e. a unique value to identify each row with)
38723	api.on( 'preXhr.dt.dtSelect', function (e, settings) {
38724		if (settings !== api.settings()[0]) {
38725			// Not triggered by our DataTable!
38726			return;
38727		}
38728
38729		// note that column selection doesn't need to be cached and then
38730		// reselected, as they are already selected
38731		var rows = api.rows( { selected: true } ).ids( true ).filter( function ( d ) {
38732			return d !== undefined;
38733		} );
38734
38735		var cells = api.cells( { selected: true } ).eq(0).map( function ( cellIdx ) {
38736			var id = api.row( cellIdx.row ).id( true );
38737			return id ?
38738				{ row: id, column: cellIdx.column } :
38739				undefined;
38740		} ).filter( function ( d ) {
38741			return d !== undefined;
38742		} );
38743
38744		// On the next draw, reselect the currently selected items
38745		api.one( 'draw.dt.dtSelect', function () {
38746			api.rows( rows ).select();
38747
38748			// `cells` is not a cell index selector, so it needs a loop
38749			if ( cells.any() ) {
38750				cells.each( function ( id ) {
38751					api.cells( id.row, id.column ).select();
38752				} );
38753			}
38754		} );
38755	} );
38756
38757	// Update the table information element with selected item summary
38758	api.on( 'draw.dtSelect.dt select.dtSelect.dt deselect.dtSelect.dt info.dt', function () {
38759		info( api );
38760		api.state.save();
38761	} );
38762
38763	// Clean up and release
38764	api.on( 'destroy.dtSelect', function () {
38765		// Remove class directly rather than calling deselect - which would trigger events
38766		$(api.rows({selected: true}).nodes()).removeClass(api.settings()[0]._select.className);
38767
38768		disableMouseSelection( api );
38769		api.off( '.dtSelect' );
38770		$('body').off('.dtSelect' + _safeId(api.table().node()));
38771	} );
38772}
38773
38774/**
38775 * Add one or more items (rows or columns) to the selection when shift clicking
38776 * in OS selection style
38777 *
38778 * @param  {DataTable.Api} dt   DataTable
38779 * @param  {string}        type Row or column range selector
38780 * @param  {object}        idx  Item index to select to
38781 * @param  {object}        last Item index to select from
38782 * @private
38783 */
38784function rowColumnRange( dt, type, idx, last )
38785{
38786	// Add a range of rows from the last selected row to this one
38787	var indexes = dt[type+'s']( { search: 'applied' } ).indexes();
38788	var idx1 = $.inArray( last, indexes );
38789	var idx2 = $.inArray( idx, indexes );
38790
38791	if ( ! dt[type+'s']( { selected: true } ).any() && idx1 === -1 ) {
38792		// select from top to here - slightly odd, but both Windows and Mac OS
38793		// do this
38794		indexes.splice( $.inArray( idx, indexes )+1, indexes.length );
38795	}
38796	else {
38797		// reverse so we can shift click 'up' as well as down
38798		if ( idx1 > idx2 ) {
38799			var tmp = idx2;
38800			idx2 = idx1;
38801			idx1 = tmp;
38802		}
38803
38804		indexes.splice( idx2+1, indexes.length );
38805		indexes.splice( 0, idx1 );
38806	}
38807
38808	if ( ! dt[type]( idx, { selected: true } ).any() ) {
38809		// Select range
38810		dt[type+'s']( indexes ).select();
38811	}
38812	else {
38813		// Deselect range - need to keep the clicked on row selected
38814		indexes.splice( $.inArray( idx, indexes ), 1 );
38815		dt[type+'s']( indexes ).deselect();
38816	}
38817}
38818
38819/**
38820 * Clear all selected items
38821 *
38822 * @param  {DataTable.settings} ctx Settings object of the host DataTable
38823 * @param  {boolean} [force=false] Force the de-selection to happen, regardless
38824 *     of selection style
38825 * @private
38826 */
38827function clear( ctx, force )
38828{
38829	if ( force || ctx._select.style === 'single' ) {
38830		var api = new DataTable.Api( ctx );
38831		
38832		api.rows( { selected: true } ).deselect();
38833		api.columns( { selected: true } ).deselect();
38834		api.cells( { selected: true } ).deselect();
38835	}
38836}
38837
38838/**
38839 * Select items based on the current configuration for style and items.
38840 *
38841 * @param  {object}             e    Mouse event object
38842 * @param  {DataTables.Api}     dt   DataTable
38843 * @param  {DataTable.settings} ctx  Settings object of the host DataTable
38844 * @param  {string}             type Items to select
38845 * @param  {int|object}         idx  Index of the item to select
38846 * @private
38847 */
38848function typeSelect ( e, dt, ctx, type, idx )
38849{
38850	var style = dt.select.style();
38851	var toggleable = dt.select.toggleable();
38852	var isSelected = dt[type]( idx, { selected: true } ).any();
38853	
38854	if ( isSelected && ! toggleable ) {
38855		return;
38856	}
38857
38858	if ( style === 'os' ) {
38859		if ( e.ctrlKey || e.metaKey ) {
38860			// Add or remove from the selection
38861			dt[type]( idx ).select( ! isSelected );
38862		}
38863		else if ( e.shiftKey ) {
38864			if ( type === 'cell' ) {
38865				cellRange( dt, idx, ctx._select_lastCell || null );
38866			}
38867			else {
38868				rowColumnRange( dt, type, idx, ctx._select_lastCell ?
38869					ctx._select_lastCell[type] :
38870					null
38871				);
38872			}
38873		}
38874		else {
38875			// No cmd or shift click - deselect if selected, or select
38876			// this row only
38877			var selected = dt[type+'s']( { selected: true } );
38878
38879			if ( isSelected && selected.flatten().length === 1 ) {
38880				dt[type]( idx ).deselect();
38881			}
38882			else {
38883				selected.deselect();
38884				dt[type]( idx ).select();
38885			}
38886		}
38887	} else if ( style == 'multi+shift' ) {
38888		if ( e.shiftKey ) {
38889			if ( type === 'cell' ) {
38890				cellRange( dt, idx, ctx._select_lastCell || null );
38891			}
38892			else {
38893				rowColumnRange( dt, type, idx, ctx._select_lastCell ?
38894					ctx._select_lastCell[type] :
38895					null
38896				);
38897			}
38898		}
38899		else {
38900			dt[ type ]( idx ).select( ! isSelected );
38901		}
38902	}
38903	else {
38904		dt[ type ]( idx ).select( ! isSelected );
38905	}
38906}
38907
38908function _safeId( node ) {
38909	return node.id.replace(/[^a-zA-Z0-9\-\_]/g, '-');
38910}
38911
38912
38913
38914/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
38915 * DataTables selectors
38916 */
38917
38918// row and column are basically identical just assigned to different properties
38919// and checking a different array, so we can dynamically create the functions to
38920// reduce the code size
38921$.each( [
38922	{ type: 'row', prop: 'aoData' },
38923	{ type: 'column', prop: 'aoColumns' }
38924], function ( i, o ) {
38925	DataTable.ext.selector[ o.type ].push( function ( settings, opts, indexes ) {
38926		var selected = opts.selected;
38927		var data;
38928		var out = [];
38929
38930		if ( selected !== true && selected !== false ) {
38931			return indexes;
38932		}
38933
38934		for ( var i=0, ien=indexes.length ; i<ien ; i++ ) {
38935			data = settings[ o.prop ][ indexes[i] ];
38936
38937			if ( (selected === true && data._select_selected === true) ||
38938			     (selected === false && ! data._select_selected )
38939			) {
38940				out.push( indexes[i] );
38941			}
38942		}
38943
38944		return out;
38945	} );
38946} );
38947
38948DataTable.ext.selector.cell.push( function ( settings, opts, cells ) {
38949	var selected = opts.selected;
38950	var rowData;
38951	var out = [];
38952
38953	if ( selected === undefined ) {
38954		return cells;
38955	}
38956
38957	for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
38958		rowData = settings.aoData[ cells[i].row ];
38959
38960		if ( (selected === true && rowData._selected_cells && rowData._selected_cells[ cells[i].column ] === true) ||
38961		     (selected === false && ( ! rowData._selected_cells || ! rowData._selected_cells[ cells[i].column ] ) )
38962		) {
38963			out.push( cells[i] );
38964		}
38965	}
38966
38967	return out;
38968} );
38969
38970
38971
38972/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
38973 * DataTables API
38974 *
38975 * For complete documentation, please refer to the docs/api directory or the
38976 * DataTables site
38977 */
38978
38979// Local variables to improve compression
38980var apiRegister = DataTable.Api.register;
38981var apiRegisterPlural = DataTable.Api.registerPlural;
38982
38983apiRegister( 'select()', function () {
38984	return this.iterator( 'table', function ( ctx ) {
38985		DataTable.select.init( new DataTable.Api( ctx ) );
38986	} );
38987} );
38988
38989apiRegister( 'select.blurable()', function ( flag ) {
38990	if ( flag === undefined ) {
38991		return this.context[0]._select.blurable;
38992	}
38993
38994	return this.iterator( 'table', function ( ctx ) {
38995		ctx._select.blurable = flag;
38996	} );
38997} );
38998
38999apiRegister( 'select.toggleable()', function ( flag ) {
39000	if ( flag === undefined ) {
39001		return this.context[0]._select.toggleable;
39002	}
39003
39004	return this.iterator( 'table', function ( ctx ) {
39005		ctx._select.toggleable = flag;
39006	} );
39007} );
39008
39009apiRegister( 'select.info()', function ( flag ) {
39010	if ( flag === undefined ) {
39011		return this.context[0]._select.info;
39012	}
39013
39014	return this.iterator( 'table', function ( ctx ) {
39015		ctx._select.info = flag;
39016	} );
39017} );
39018
39019apiRegister( 'select.items()', function ( items ) {
39020	if ( items === undefined ) {
39021		return this.context[0]._select.items;
39022	}
39023
39024	return this.iterator( 'table', function ( ctx ) {
39025		ctx._select.items = items;
39026
39027		eventTrigger( new DataTable.Api( ctx ), 'selectItems', [ items ] );
39028	} );
39029} );
39030
39031// Takes effect from the _next_ selection. None disables future selection, but
39032// does not clear the current selection. Use the `deselect` methods for that
39033apiRegister( 'select.style()', function ( style ) {
39034	if ( style === undefined ) {
39035		return this.context[0]._select.style;
39036	}
39037
39038	return this.iterator( 'table', function ( ctx ) {
39039		if ( ! ctx._select ) {
39040			DataTable.select.init( new DataTable.Api(ctx) );
39041		}
39042
39043		if ( ! ctx._select_init ) {
39044			init(ctx);
39045		}
39046
39047		ctx._select.style = style;
39048
39049		// Add / remove mouse event handlers. They aren't required when only
39050		// API selection is available
39051		var dt = new DataTable.Api( ctx );
39052		disableMouseSelection( dt );
39053		
39054		if ( style !== 'api' ) {
39055			enableMouseSelection( dt );
39056		}
39057
39058		eventTrigger( new DataTable.Api( ctx ), 'selectStyle', [ style ] );
39059	} );
39060} );
39061
39062apiRegister( 'select.selector()', function ( selector ) {
39063	if ( selector === undefined ) {
39064		return this.context[0]._select.selector;
39065	}
39066
39067	return this.iterator( 'table', function ( ctx ) {
39068		disableMouseSelection( new DataTable.Api( ctx ) );
39069
39070		ctx._select.selector = selector;
39071
39072		if ( ctx._select.style !== 'api' ) {
39073			enableMouseSelection( new DataTable.Api( ctx ) );
39074		}
39075	} );
39076} );
39077
39078
39079
39080apiRegisterPlural( 'rows().select()', 'row().select()', function ( select ) {
39081	var api = this;
39082
39083	if ( select === false ) {
39084		return this.deselect();
39085	}
39086
39087	this.iterator( 'row', function ( ctx, idx ) {
39088		clear( ctx );
39089
39090		ctx.aoData[ idx ]._select_selected = true;
39091		$( ctx.aoData[ idx ].nTr ).addClass( ctx._select.className );
39092	} );
39093
39094	this.iterator( 'table', function ( ctx, i ) {
39095		eventTrigger( api, 'select', [ 'row', api[i] ], true );
39096	} );
39097
39098	return this;
39099} );
39100
39101apiRegister( 'row().selected()', function () {
39102	var ctx = this.context[0];
39103
39104	if (
39105		ctx &&
39106		this.length &&
39107		ctx.aoData[this[0]] &&
39108		ctx.aoData[this[0]]._select_selected
39109	) {
39110		return true;
39111	}
39112
39113	return false;
39114} );
39115
39116apiRegisterPlural( 'columns().select()', 'column().select()', function ( select ) {
39117	var api = this;
39118
39119	if ( select === false ) {
39120		return this.deselect();
39121	}
39122
39123	this.iterator( 'column', function ( ctx, idx ) {
39124		clear( ctx );
39125
39126		ctx.aoColumns[ idx ]._select_selected = true;
39127
39128		var column = new DataTable.Api( ctx ).column( idx );
39129
39130		$( column.header() ).addClass( ctx._select.className );
39131		$( column.footer() ).addClass( ctx._select.className );
39132
39133		column.nodes().to$().addClass( ctx._select.className );
39134	} );
39135
39136	this.iterator( 'table', function ( ctx, i ) {
39137		eventTrigger( api, 'select', [ 'column', api[i] ], true );
39138	} );
39139
39140	return this;
39141} );
39142
39143apiRegister( 'column().selected()', function () {
39144	var ctx = this.context[0];
39145
39146	if (
39147		ctx &&
39148		this.length &&
39149		ctx.aoColumns[this[0]] &&
39150		ctx.aoColumns[this[0]]._select_selected
39151	) {
39152		return true;
39153	}
39154
39155	return false;
39156} );
39157
39158apiRegisterPlural( 'cells().select()', 'cell().select()', function ( select ) {
39159	var api = this;
39160
39161	if ( select === false ) {
39162		return this.deselect();
39163	}
39164
39165	this.iterator( 'cell', function ( ctx, rowIdx, colIdx ) {
39166		clear( ctx );
39167
39168		var data = ctx.aoData[ rowIdx ];
39169
39170		if ( data._selected_cells === undefined ) {
39171			data._selected_cells = [];
39172		}
39173
39174		data._selected_cells[ colIdx ] = true;
39175
39176		if ( data.anCells ) {
39177			$( data.anCells[ colIdx ] ).addClass( ctx._select.className );
39178		}
39179	} );
39180
39181	this.iterator( 'table', function ( ctx, i ) {
39182		eventTrigger( api, 'select', [ 'cell', api.cells(api[i]).indexes().toArray() ], true );
39183	} );
39184
39185	return this;
39186} );
39187
39188apiRegister( 'cell().selected()', function () {
39189	var ctx = this.context[0];
39190
39191	if (ctx && this.length) {
39192		var row = ctx.aoData[this[0][0].row];
39193
39194		if (row && row._selected_cells && row._selected_cells[this[0][0].column]) {
39195			return true;
39196		}
39197	}
39198
39199	return false;
39200} );
39201
39202
39203apiRegisterPlural( 'rows().deselect()', 'row().deselect()', function () {
39204	var api = this;
39205
39206	this.iterator( 'row', function ( ctx, idx ) {
39207		ctx.aoData[ idx ]._select_selected = false;
39208		ctx._select_lastCell = null;
39209		$( ctx.aoData[ idx ].nTr ).removeClass( ctx._select.className );
39210	} );
39211
39212	this.iterator( 'table', function ( ctx, i ) {
39213		eventTrigger( api, 'deselect', [ 'row', api[i] ], true );
39214	} );
39215
39216	return this;
39217} );
39218
39219apiRegisterPlural( 'columns().deselect()', 'column().deselect()', function () {
39220	var api = this;
39221
39222	this.iterator( 'column', function ( ctx, idx ) {
39223		ctx.aoColumns[ idx ]._select_selected = false;
39224
39225		var api = new DataTable.Api( ctx );
39226		var column = api.column( idx );
39227
39228		$( column.header() ).removeClass( ctx._select.className );
39229		$( column.footer() ).removeClass( ctx._select.className );
39230
39231		// Need to loop over each cell, rather than just using
39232		// `column().nodes()` as cells which are individually selected should
39233		// not have the `selected` class removed from them
39234		api.cells( null, idx ).indexes().each( function (cellIdx) {
39235			var data = ctx.aoData[ cellIdx.row ];
39236			var cellSelected = data._selected_cells;
39237
39238			if ( data.anCells && (! cellSelected || ! cellSelected[ cellIdx.column ]) ) {
39239				$( data.anCells[ cellIdx.column  ] ).removeClass( ctx._select.className );
39240			}
39241		} );
39242	} );
39243
39244	this.iterator( 'table', function ( ctx, i ) {
39245		eventTrigger( api, 'deselect', [ 'column', api[i] ], true );
39246	} );
39247
39248	return this;
39249} );
39250
39251apiRegisterPlural( 'cells().deselect()', 'cell().deselect()', function () {
39252	var api = this;
39253
39254	this.iterator( 'cell', function ( ctx, rowIdx, colIdx ) {
39255		var data = ctx.aoData[ rowIdx ];
39256
39257		if(data._selected_cells !== undefined) {
39258			data._selected_cells[ colIdx ] = false;
39259		}
39260
39261		// Remove class only if the cells exist, and the cell is not column
39262		// selected, in which case the class should remain (since it is selected
39263		// in the column)
39264		if ( data.anCells && ! ctx.aoColumns[ colIdx ]._select_selected ) {
39265			$( data.anCells[ colIdx ] ).removeClass( ctx._select.className );
39266		}
39267	} );
39268
39269	this.iterator( 'table', function ( ctx, i ) {
39270		eventTrigger( api, 'deselect', [ 'cell', api[i] ], true );
39271	} );
39272
39273	return this;
39274} );
39275
39276
39277
39278/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
39279 * Buttons
39280 */
39281function i18n( label, def ) {
39282	return function (dt) {
39283		return dt.i18n( 'buttons.'+label, def );
39284	};
39285}
39286
39287// Common events with suitable namespaces
39288function namespacedEvents ( config ) {
39289	var unique = config._eventNamespace;
39290
39291	return 'draw.dt.DT'+unique+' select.dt.DT'+unique+' deselect.dt.DT'+unique;
39292}
39293
39294function enabled ( dt, config ) {
39295	if ( $.inArray( 'rows', config.limitTo ) !== -1 && dt.rows( { selected: true } ).any() ) {
39296		return true;
39297	}
39298
39299	if ( $.inArray( 'columns', config.limitTo ) !== -1 && dt.columns( { selected: true } ).any() ) {
39300		return true;
39301	}
39302
39303	if ( $.inArray( 'cells', config.limitTo ) !== -1 && dt.cells( { selected: true } ).any() ) {
39304		return true;
39305	}
39306
39307	return false;
39308}
39309
39310var _buttonNamespace = 0;
39311
39312$.extend( DataTable.ext.buttons, {
39313	selected: {
39314		text: i18n( 'selected', 'Selected' ),
39315		className: 'buttons-selected',
39316		limitTo: [ 'rows', 'columns', 'cells' ],
39317		init: function ( dt, node, config ) {
39318			var that = this;
39319			config._eventNamespace = '.select'+(_buttonNamespace++);
39320
39321			// .DT namespace listeners are removed by DataTables automatically
39322			// on table destroy
39323			dt.on( namespacedEvents(config), function () {
39324				that.enable( enabled(dt, config) );
39325			} );
39326
39327			this.disable();
39328		},
39329		destroy: function ( dt, node, config ) {
39330			dt.off( config._eventNamespace );
39331		}
39332	},
39333	selectedSingle: {
39334		text: i18n( 'selectedSingle', 'Selected single' ),
39335		className: 'buttons-selected-single',
39336		init: function ( dt, node, config ) {
39337			var that = this;
39338			config._eventNamespace = '.select'+(_buttonNamespace++);
39339
39340			dt.on( namespacedEvents(config), function () {
39341				var count = dt.rows( { selected: true } ).flatten().length +
39342				            dt.columns( { selected: true } ).flatten().length +
39343				            dt.cells( { selected: true } ).flatten().length;
39344
39345				that.enable( count === 1 );
39346			} );
39347
39348			this.disable();
39349		},
39350		destroy: function ( dt, node, config ) {
39351			dt.off( config._eventNamespace );
39352		}
39353	},
39354	selectAll: {
39355		text: i18n( 'selectAll', 'Select all' ),
39356		className: 'buttons-select-all',
39357		action: function () {
39358			var items = this.select.items();
39359			this[ items+'s' ]().select();
39360		}
39361	},
39362	selectNone: {
39363		text: i18n( 'selectNone', 'Deselect all' ),
39364		className: 'buttons-select-none',
39365		action: function () {
39366			clear( this.settings()[0], true );
39367		},
39368		init: function ( dt, node, config ) {
39369			var that = this;
39370			config._eventNamespace = '.select'+(_buttonNamespace++);
39371
39372			dt.on( namespacedEvents(config), function () {
39373				var count = dt.rows( { selected: true } ).flatten().length +
39374				            dt.columns( { selected: true } ).flatten().length +
39375				            dt.cells( { selected: true } ).flatten().length;
39376
39377				that.enable( count > 0 );
39378			} );
39379
39380			this.disable();
39381		},
39382		destroy: function ( dt, node, config ) {
39383			dt.off( config._eventNamespace );
39384		}
39385	},
39386	showSelected: {
39387		text: i18n( 'showSelected', 'Show only selected' ),
39388		className: 'buttons-show-selected',
39389		action: function (e, dt, node, conf) {
39390			// Works by having a filtering function which will reduce to the selected
39391			// items only. So we can re-reference the function it gets stored in the
39392			// `conf` object
39393			if (conf._filter) {
39394				var idx = DataTable.ext.search.indexOf(conf._filter);
39395
39396				if (idx !== -1) {
39397					DataTable.ext.search.splice(idx, 1);
39398					conf._filter = null;
39399				}
39400
39401				this.active(false);
39402			}
39403			else {
39404				var fn = function (s, data, idx) {
39405					// Need to be sure we are operating on our table!
39406					if (s !== dt.settings()[0]) {
39407						return true;
39408					}
39409
39410					let row = s.aoData[idx];
39411
39412					return row._select_selected;
39413				}
39414
39415				conf._filter = fn;
39416				DataTable.ext.search.push(fn);
39417
39418				this.active(true);
39419			}
39420
39421			dt.draw();
39422		}
39423	}
39424} );
39425
39426$.each( [ 'Row', 'Column', 'Cell' ], function ( i, item ) {
39427	var lc = item.toLowerCase();
39428
39429	DataTable.ext.buttons[ 'select'+item+'s' ] = {
39430		text: i18n( 'select'+item+'s', 'Select '+lc+'s' ),
39431		className: 'buttons-select-'+lc+'s',
39432		action: function () {
39433			this.select.items( lc );
39434		},
39435		init: function ( dt ) {
39436			var that = this;
39437
39438			dt.on( 'selectItems.dt.DT', function ( e, ctx, items ) {
39439				that.active( items === lc );
39440			} );
39441		}
39442	};
39443} );
39444
39445
39446$.fn.DataTable.select = DataTable.select;
39447
39448/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
39449 * Initialisation
39450 */
39451
39452// DataTables creation - check if select has been defined in the options. Note
39453// this required that the table be in the document! If it isn't then something
39454// needs to trigger this method unfortunately. The next major release of
39455// DataTables will rework the events and address this.
39456$(document).on( 'preInit.dt.dtSelect', function (e, ctx) {
39457	if ( e.namespace !== 'dt' ) {
39458		return;
39459	}
39460
39461	DataTable.select.init( new DataTable.Api( ctx ) );
39462} );
39463
39464
39465return DataTable;
39466}));
39467
39468
39469/*! StateRestore 1.2.2
39470 * © SpryMedia Ltd - datatables.net/license
39471 */
39472
39473(function( factory ){
39474	if ( typeof define === 'function' && define.amd ) {
39475		// AMD
39476		define( ['jquery', 'datatables.net'], function ( $ ) {
39477			return factory( $, window, document );
39478		} );
39479	}
39480	else if ( typeof exports === 'object' ) {
39481		// CommonJS
39482		var jq = require('jquery');
39483		var cjsRequires = function (root, $) {
39484			if ( ! $.fn.dataTable ) {
39485				require('datatables.net')(root, $);
39486			}
39487		};
39488
39489		if (typeof window !== 'undefined') {
39490			module.exports = function (root, $) {
39491				if ( ! root ) {
39492					// CommonJS environments without a window global must pass a
39493					// root. This will give an error otherwise
39494					root = window;
39495				}
39496
39497				if ( ! $ ) {
39498					$ = jq( root );
39499				}
39500
39501				cjsRequires( root, $ );
39502				return factory( $, root, root.document );
39503			};
39504		}
39505		else {
39506			cjsRequires( window, jq );
39507			module.exports = factory( jq, window, window.document );
39508		}
39509	}
39510	else {
39511		// Browser
39512		factory( jQuery, window, document );
39513	}
39514}(function( $, window, document, undefined ) {
39515'use strict';
39516var DataTable = $.fn.dataTable;
39517
39518
39519(function () {
39520    'use strict';
39521
39522    var $$2;
39523    var dataTable$1;
39524    function setJQuery$1(jq) {
39525        $$2 = jq;
39526        dataTable$1 = jq.fn.dataTable;
39527    }
39528    var StateRestore = /** @class */ (function () {
39529        function StateRestore(settings, opts, identifier, state, isPreDefined, successCallback) {
39530            if (state === void 0) { state = undefined; }
39531            if (isPreDefined === void 0) { isPreDefined = false; }
39532            if (successCallback === void 0) { successCallback = function () { return null; }; }
39533            // Check that the required version of DataTables is included
39534            if (!dataTable$1 || !dataTable$1.versionCheck || !dataTable$1.versionCheck('1.10.0')) {
39535                throw new Error('StateRestore requires DataTables 1.10 or newer');
39536            }
39537            // Check that Select is included
39538            // eslint-disable-next-line no-extra-parens
39539            if (!dataTable$1.Buttons) {
39540                throw new Error('StateRestore requires Buttons');
39541            }
39542            var table = new dataTable$1.Api(settings);
39543            this.classes = $$2.extend(true, {}, StateRestore.classes);
39544            // Get options from user
39545            this.c = $$2.extend(true, {}, StateRestore.defaults, opts);
39546            this.s = {
39547                dt: table,
39548                identifier: identifier,
39549                isPreDefined: isPreDefined,
39550                savedState: null,
39551                tableId: state && state.stateRestore ? state.stateRestore.tableId : undefined
39552            };
39553            this.dom = {
39554                background: $$2('<div class="' + this.classes.background + '"/>'),
39555                closeButton: $$2('<div class="' + this.classes.closeButton + '">x</div>'),
39556                confirmation: $$2('<div class="' + this.classes.confirmation + '"/>'),
39557                confirmationButton: $$2('<button class="' + this.classes.confirmationButton + ' ' + this.classes.dtButton + '">'),
39558                confirmationTitleRow: $$2('<div class="' + this.classes.confirmationTitleRow + '"></div>'),
39559                dtContainer: $$2(this.s.dt.table().container()),
39560                duplicateError: $$2('<span class="' + this.classes.modalError + '">' +
39561                    this.s.dt.i18n('stateRestore.duplicateError', this.c.i18n.duplicateError) +
39562                    '</span>'),
39563                emptyError: $$2('<span class="' + this.classes.modalError + '">' +
39564                    this.s.dt.i18n('stateRestore.emptyError', this.c.i18n.emptyError) +
39565                    '</span>'),
39566                removeContents: $$2('<div class="' + this.classes.confirmationText + '"><span>' +
39567                    this.s.dt
39568                        .i18n('stateRestore.removeConfirm', this.c.i18n.removeConfirm)
39569                        .replace(/%s/g, this.s.identifier) +
39570                    '</span></div>'),
39571                removeError: $$2('<span class="' + this.classes.modalError + '">' +
39572                    this.s.dt.i18n('stateRestore.removeError', this.c.i18n.removeError) +
39573                    '</span>'),
39574                removeTitle: $$2('<h2 class="' + this.classes.confirmationTitle + '">' +
39575                    this.s.dt.i18n('stateRestore.removeTitle', this.c.i18n.removeTitle) +
39576                    '</h2>'),
39577                renameContents: $$2('<div class="' + this.classes.confirmationText + ' ' + this.classes.renameModal + '">' +
39578                    '<label class="' + this.classes.confirmationMessage + '">' +
39579                    this.s.dt
39580                        .i18n('stateRestore.renameLabel', this.c.i18n.renameLabel)
39581                        .replace(/%s/g, this.s.identifier) +
39582                    '</label>' +
39583                    '</div>'),
39584                renameInput: $$2('<input class="' + this.classes.input + '" type="text"></input>'),
39585                renameTitle: $$2('<h2 class="' + this.classes.confirmationTitle + '">' +
39586                    this.s.dt.i18n('stateRestore.renameTitle', this.c.i18n.renameTitle) +
39587                    '</h2>')
39588            };
39589            // When a StateRestore instance is created the current state of the table should also be saved.
39590            this.save(state, successCallback);
39591        }
39592        /**
39593         * Removes a state from storage and then triggers the dtsr-remove event
39594         * so that the StateRestoreCollection class can remove it's references as well.
39595         *
39596         * @param skipModal Flag to indicate if the modal should be skipped or not
39597         */
39598        StateRestore.prototype.remove = function (skipModal) {
39599            var _a;
39600            var _this = this;
39601            if (skipModal === void 0) { skipModal = false; }
39602            // Check if removal of states is allowed
39603            if (!this.c.remove) {
39604                return false;
39605            }
39606            var removeFunction;
39607            var ajaxData = {
39608                action: 'remove',
39609                stateRestore: (_a = {},
39610                    _a[this.s.identifier] = this.s.savedState,
39611                    _a)
39612            };
39613            var successCallback = function () {
39614                _this.dom.confirmation.trigger('dtsr-remove');
39615                $$2(_this.s.dt.table().node()).trigger('stateRestore-change');
39616                _this.dom.background.click();
39617                _this.dom.confirmation.remove();
39618                $$2(document).unbind('keyup', function (e) { return _this._keyupFunction(e); });
39619                _this.dom.confirmationButton.off('click');
39620            };
39621            // If the remove is not happening over ajax remove it from local storage and then trigger the event
39622            if (!this.c.ajax) {
39623                removeFunction = function () {
39624                    try {
39625                        localStorage.removeItem('DataTables_stateRestore_' + _this.s.identifier + '_' + location.pathname +
39626                            (_this.s.tableId ? '_' + _this.s.tableId : ''));
39627                        successCallback();
39628                    }
39629                    catch (e) {
39630                        _this.dom.confirmation.children('.' + _this.classes.modalError).remove();
39631                        _this.dom.confirmation.append(_this.dom.removeError);
39632                        return 'remove';
39633                    }
39634                    return true;
39635                };
39636            }
39637            // Ajax property has to be a string, not just true
39638            // Also only want to save if the table has been initialised and the states have been loaded in
39639            else if (typeof this.c.ajax === 'string' && this.s.dt.settings()[0]._bInitComplete) {
39640                removeFunction = function () {
39641                    $$2.ajax({
39642                        data: ajaxData,
39643                        success: successCallback,
39644                        type: 'POST',
39645                        url: _this.c.ajax
39646                    });
39647                    return true;
39648                };
39649            }
39650            else if (typeof this.c.ajax === 'function') {
39651                removeFunction = function () {
39652                    if (typeof _this.c.ajax === 'function') {
39653                        _this.c.ajax.call(_this.s.dt, ajaxData, successCallback);
39654                    }
39655                    return true;
39656                };
39657            }
39658            // If the modal is to be skipped then remove straight away
39659            if (skipModal) {
39660                this.dom.confirmation.appendTo(this.dom.dtContainer);
39661                $$2(this.s.dt.table().node()).trigger('dtsr-modal-inserted');
39662                removeFunction();
39663                this.dom.confirmation.remove();
39664            }
39665            // Otherwise display the modal
39666            else {
39667                this._newModal(this.dom.removeTitle, this.s.dt.i18n('stateRestore.removeSubmit', this.c.i18n.removeSubmit), removeFunction, this.dom.removeContents);
39668            }
39669            return true;
39670        };
39671        /**
39672         * Compares the state held within this instance with a state that is passed in
39673         *
39674         * @param state The state that is to be compared against
39675         * @returns boolean indicating if the states match
39676         */
39677        StateRestore.prototype.compare = function (state) {
39678            // Order
39679            if (!this.c.saveState.order) {
39680                state.order = undefined;
39681            }
39682            // Search
39683            if (!this.c.saveState.search) {
39684                state.search = undefined;
39685            }
39686            // Columns
39687            if (this.c.saveState.columns && state.columns) {
39688                for (var i = 0, ien = state.columns.length; i < ien; i++) {
39689                    // Visibility
39690                    if (typeof this.c.saveState.columns !== 'boolean' && !this.c.saveState.columns.visible) {
39691                        state.columns[i].visible = undefined;
39692                    }
39693                    // Search
39694                    if (typeof this.c.saveState.columns !== 'boolean' && !this.c.saveState.columns.search) {
39695                        state.columns[i].search = undefined;
39696                    }
39697                }
39698            }
39699            else if (!this.c.saveState.columns) {
39700                state.columns = undefined;
39701            }
39702            // Paging
39703            if (!this.c.saveState.paging) {
39704                state.page = undefined;
39705            }
39706            // SearchBuilder
39707            if (!this.c.saveState.searchBuilder) {
39708                state.searchBuilder = undefined;
39709            }
39710            // SearchPanes
39711            if (!this.c.saveState.searchPanes) {
39712                state.searchPanes = undefined;
39713            }
39714            // Select
39715            if (!this.c.saveState.select) {
39716                state.select = undefined;
39717            }
39718            // ColReorder
39719            if (!this.c.saveState.colReorder) {
39720                state.ColReorder = undefined;
39721            }
39722            // Scroller
39723            if (!this.c.saveState.scroller) {
39724                state.scroller = undefined;
39725                if (dataTable$1.Scroller !== undefined) {
39726                    state.start = 0;
39727                }
39728            }
39729            // Paging
39730            if (!this.c.saveState.paging) {
39731                state.start = 0;
39732            }
39733            // Page Length
39734            if (!this.c.saveState.length) {
39735                state.length = undefined;
39736            }
39737            // Need to delete properties that we do not want to compare
39738            delete state.time;
39739            var copyState = this.s.savedState;
39740            delete copyState.time;
39741            delete copyState.c;
39742            delete copyState.stateRestore;
39743            // Perform a deep compare of the two state objects
39744            return this._deepCompare(state, copyState);
39745        };
39746        /**
39747         * Removes all of the dom elements from the document
39748         */
39749        StateRestore.prototype.destroy = function () {
39750            Object.values(this.dom).forEach(function (node) { return node.off().remove(); });
39751        };
39752        /**
39753         * Loads the state referenced by the identifier from storage
39754         *
39755         * @param state The identifier of the state that should be loaded
39756         * @returns the state that has been loaded
39757         */
39758        StateRestore.prototype.load = function () {
39759            var _this = this;
39760            var loadedState = this.s.savedState;
39761            var settings = this.s.dt.settings()[0];
39762            // Always want the states stored here to be loaded in - regardless of when they were created
39763            loadedState.time = +new Date();
39764            settings.oLoadedState = $$2.extend(true, {}, loadedState);
39765            // Click on a background if there is one to shut the collection
39766            $$2('div.dt-button-background').click();
39767            // Call the internal datatables function to implement the state on the table
39768            $$2.fn.dataTable.ext.oApi._fnImplementState(settings, loadedState, function () {
39769                var correctPaging = function (e, preSettings) {
39770                    setTimeout(function () {
39771                        var currpage = preSettings._iDisplayStart / preSettings._iDisplayLength;
39772                        var intendedPage = loadedState.start / loadedState.length;
39773                        // If the paging is incorrect then we have to set it again so that it is correct
39774                        // This happens when a searchpanes filter is removed
39775                        // This has to happen in a timeout because searchpanes only deselects after a timeout
39776                        if (currpage >= 0 && intendedPage >= 0 && currpage !== intendedPage) {
39777                            _this.s.dt.page(intendedPage).draw(false);
39778                        }
39779                    }, 50);
39780                };
39781                _this.s.dt.one('preDraw', correctPaging);
39782                _this.s.dt.draw(false);
39783            });
39784            return loadedState;
39785        };
39786        /**
39787         * Shows a modal that allows a state to be renamed
39788         *
39789         * @param newIdentifier Optional. The new identifier for this state
39790         */
39791        StateRestore.prototype.rename = function (newIdentifier, currentIdentifiers) {
39792            var _this = this;
39793            if (newIdentifier === void 0) { newIdentifier = null; }
39794            // Check if renaming of states is allowed
39795            if (!this.c.rename) {
39796                return;
39797            }
39798            var renameFunction = function () {
39799                var _a;
39800                if (newIdentifier === null) {
39801                    var tempIdentifier = $$2('input.' + _this.classes.input.replace(/ /g, '.')).val();
39802                    if (tempIdentifier.length === 0) {
39803                        _this.dom.confirmation.children('.' + _this.classes.modalError).remove();
39804                        _this.dom.confirmation.append(_this.dom.emptyError);
39805                        return 'empty';
39806                    }
39807                    else if (currentIdentifiers.includes(tempIdentifier)) {
39808                        _this.dom.confirmation.children('.' + _this.classes.modalError).remove();
39809                        _this.dom.confirmation.append(_this.dom.duplicateError);
39810                        return 'duplicate';
39811                    }
39812                    else {
39813                        newIdentifier = tempIdentifier;
39814                    }
39815                }
39816                var ajaxData = {
39817                    action: 'rename',
39818                    stateRestore: (_a = {},
39819                        _a[_this.s.identifier] = newIdentifier,
39820                        _a)
39821                };
39822                var successCallback = function () {
39823                    _this.s.identifier = newIdentifier;
39824                    _this.save(_this.s.savedState, function () { return null; }, false);
39825                    _this.dom.removeContents = $$2('<div class="' + _this.classes.confirmationText 
39825+ '"><span>' +
39826                        _this.s.dt
39827                            .i18n('stateRestore.removeConfirm', _this.c.i18n.removeConfirm)
39828                            .replace(/%s/g, _this.s.identifier) +
39829                        '</span></div>');
39830                    _this.dom.confirmation.trigger('dtsr-rename');
39831                    _this.dom.background.click();
39832                    _this.dom.confirmation.remove();
39833                    $$2(document).unbind('keyup', function (e) { return _this._keyupFunction(e); });
39834                    _this.dom.confirmationButton.off('click');
39835                };
39836                if (!_this.c.ajax) {
39837                    try {
39838                        localStorage.removeItem('DataTables_stateRestore_' + _this.s.identifier + '_' + location.pathname +
39839                            (_this.s.tableId ? '_' + _this.s.tableId : ''));
39840                        successCallback();
39841                    }
39842                    catch (e) {
39843                        _this.dom.confirmation.children('.' + _this.classes.modalError).remove();
39844                        _this.dom.confirmation.append(_this.dom.removeError);
39845                        return false;
39846                    }
39847                }
39848                else if (typeof _this.c.ajax === 'string' && _this.s.dt.settings()[0]._bInitComplete) {
39849                    $$2.ajax({
39850                        data: ajaxData,
39851                        success: successCallback,
39852                        type: 'POST',
39853                        url: _this.c.ajax
39854                    });
39855                }
39856                else if (typeof _this.c.ajax === 'function') {
39857                    _this.c.ajax.call(_this.s.dt, ajaxData, successCallback);
39858                }
39859                return true;
39860            };
39861            // Check if a new identifier has been provided, if so no need for a modal
39862            if (newIdentifier !== null) {
39863                if (currentIdentifiers.includes(newIdentifier)) {
39864                    throw new Error(this.s.dt.i18n('stateRestore.duplicateError', this.c.i18n.duplicateError));
39865                }
39866                else if (newIdentifier.length === 0) {
39867                    throw new Error(this.s.dt.i18n('stateRestore.emptyError', this.c.i18n.emptyError));
39868                }
39869                else {
39870                    this.dom.confirmation.appendTo(this.dom.dtContainer);
39871                    $$2(this.s.dt.table().node()).trigger('dtsr-modal-inserted');
39872                    renameFunction();
39873                    this.dom.confirmation.remove();
39874                }
39875            }
39876            else {
39877                this.dom.renameInput.val(this.s.identifier);
39878                this.dom.renameContents.append(this.dom.renameInput);
39879                this._newModal(this.dom.renameTitle, this.s.dt.i18n('stateRestore.renameButton', this.c.i18n.renameButton), renameFunction, this.dom.renameContents);
39880            }
39881        };
39882        /**
39883         * Saves the tables current state using the identifier that is passed in.
39884         *
39885         * @param state Optional. If provided this is the state that will be saved rather than using the current state
39886         */
39887        StateRestore.prototype.save = function (state, passedSuccessCallback, callAjax) {
39888            var _a;
39889            var _this = this;
39890            if (callAjax === void 0) { callAjax = true; }
39891            // Check if saving states is allowed
39892            if (!this.c.save) {
39893                if (passedSuccessCallback) {
39894                    passedSuccessCallback.call(this);
39895                }
39896                return;
39897            }
39898            // this.s.dt.state.save();
39899            var savedState;
39900            // If no state has been provided then create a new one from the current state
39901            this.s.dt.state.save();
39902            if (state === undefined) {
39903                savedState = this.s.dt.state();
39904            }
39905            else if (typeof state !== 'object') {
39906                return;
39907            }
39908            else {
39909                savedState = state;
39910            }
39911            if (savedState.stateRestore) {
39912                savedState.stateRestore.isPreDefined = this.s.isPreDefined;
39913                savedState.stateRestore.state = this.s.identifier;
39914                savedState.stateRestore.tableId = this.s.tableId;
39915            }
39916            else {
39917                savedState.stateRestore = {
39918                    isPreDefined: this.s.isPreDefined,
39919                    state: this.s.identifier,
39920                    tableId: this.s.tableId
39921                };
39922            }
39923            this.s.savedState = savedState;
39924            // Order
39925            if (!this.c.saveState.order) {
39926                this.s.savedState.order = undefined;
39927            }
39928            // Search
39929            if (!this.c.saveState.search) {
39930                this.s.savedState.search = undefined;
39931            }
39932            // Columns
39933            if (this.c.saveState.columns && this.s.savedState.columns) {
39934                for (var i = 0, ien = this.s.savedState.columns.length; i < ien; i++) {
39935                    // Visibility
39936                    if (typeof this.c.saveState.columns !== 'boolean' && !this.c.saveState.columns.visible) {
39937                        this.s.savedState.columns[i].visible = undefined;
39938                    }
39939                    // Search
39940                    if (typeof this.c.saveState.columns !== 'boolean' && !this.c.saveState.columns.search) {
39941                        this.s.savedState.columns[i].search = undefined;
39942                    }
39943                }
39944            }
39945            else if (!this.c.saveState.columns) {
39946                this.s.savedState.columns = undefined;
39947            }
39948            // SearchBuilder
39949            if (!this.c.saveState.searchBuilder) {
39950                this.s.savedState.searchBuilder = undefined;
39951            }
39952            // SearchPanes
39953            if (!this.c.saveState.searchPanes) {
39954                this.s.savedState.searchPanes = undefined;
39955            }
39956            // Select
39957            if (!this.c.saveState.select) {
39958                this.s.savedState.select = undefined;
39959            }
39960            // ColReorder
39961            if (!this.c.saveState.colReorder) {
39962                this.s.savedState.ColReorder = undefined;
39963            }
39964            // Scroller
39965            if (!this.c.saveState.scroller) {
39966                this.s.savedState.scroller = undefined;
39967                if (dataTable$1.Scroller !== undefined) {
39968                    this.s.savedState.start = 0;
39969                }
39970            }
39971            // Paging
39972            if (!this.c.saveState.paging) {
39973                this.s.savedState.start = 0;
39974            }
39975            // Page Length
39976            if (!this.c.saveState.length) {
39977                this.s.savedState.length = undefined;
39978            }
39979            this.s.savedState.c = this.c;
39980            // Need to remove the parent reference before we save the state
39981            // Its not needed to rebuild, but it does cause a circular reference when convert
39981ing to JSON
39982            if (this.s.savedState.c.splitSecondaries.length) {
39983                for (var _i = 0, _b = this.s.savedState.c.splitSecondaries; _i < _b.length; _i++) {
39984                    var secondary = _b[_i];
39985                    if (secondary.parent) {
39986                        secondary.parent = undefined;
39987                    }
39988                }
39989            }
39990            // If the state is predefined there is no need to save it over ajax or to local storage
39991            if (this.s.isPreDefined) {
39992                if (passedSuccessCallback) {
39993                    passedSuccessCallback.call(this);
39994                }
39995                return;
39996            }
39997            var ajaxData = {
39998                action: 'save',
39999                stateRestore: (_a = {},
40000                    _a[this.s.identifier] = this.s.savedState,
40001                    _a)
40002            };
40003            var successCallback = function () {
40004                if (passedSuccessCallback) {
40005                    passedSuccessCallback.call(_this);
40006                }
40007                _this.dom.confirmation.trigger('dtsr-save');
40008                $$2(_this.s.dt.table().node()).trigger('stateRestore-change');
40009            };
40010            if (!this.c.ajax) {
40011                localStorage.setItem('DataTables_stateRestore_' + this.s.identifier + '_' + location.pathname +
40012                    (this.s.tableId ? '_' + this.s.tableId : ''), JSON.stringify(this.s.savedState));
40013                successCallback();
40014            }
40015            else if (typeof this.c.ajax === 'string' && callAjax) {
40016                if (this.s.dt.settings()[0]._bInitComplete) {
40017                    $$2.ajax({
40018                        data: ajaxData,
40019                        success: successCallback,
40020                        type: 'POST',
40021                        url: this.c.ajax
40022                    });
40023                }
40024                else {
40025                    this.s.dt.one('init', function () {
40026                        $$2.ajax({
40027                            data: ajaxData,
40028                            success: successCallback,
40029                            type: 'POST',
40030                            url: _this.c.ajax
40031                        });
40032                    });
40033                }
40034            }
40035            else if (typeof this.c.ajax === 'function' && callAjax) {
40036                this.c.ajax.call(this.s.dt, ajaxData, successCallback);
40037            }
40038        };
40039        /**
40040         * Performs a deep compare of two state objects, returning true if they match
40041         *
40042         * @param state1 The first object to compare
40043         * @param state2 The second object to compare
40044         * @returns boolean indicating if the objects match
40045         */
40046        StateRestore.prototype._deepCompare = function (state1, state2) {
40047            // Put keys and states into arrays as this makes the later code easier to work
40048            var states = [state1, state2];
40049            var keys = [Object.keys(state1).sort(), Object.keys(state2).sort()];
40050            // If scroller is included then we need to remove the start value
40051            //  as it can be different but yield the same results
40052            if (keys[0].includes('scroller')) {
40053                var startIdx = keys[0].indexOf('start');
40054                if (startIdx) {
40055                    keys[0].splice(startIdx, 1);
40056                }
40057            }
40058            if (keys[1].includes('scroller')) {
40059                var startIdx = keys[1].indexOf('start');
40060                if (startIdx) {
40061                    keys[1].splice(startIdx, 1);
40062                }
40063            }
40064            // We want to remove any private properties within the states
40065            for (var i = 0; i < keys[0].length; i++) {
40066                if (keys[0][i].indexOf('_') === 0) {
40067                    keys[0].splice(i, 1);
40068                    i--;
40069                    continue;
40070                }
40071                // If scroller is included then we need to remove the following values
40072                //  as they can be different but yield the same results
40073                if (keys[0][i] === 'baseRowTop' ||
40074                    keys[0][i] === 'baseScrollTop' ||
40075                    keys[0][i] === 'scrollTop' ||
40076                    (!this.c.saveState.paging && keys[0][i] === 'page')) {
40077                    keys[0].splice(i, 1);
40078                    i--;
40079                    continue;
40080                }
40081            }
40082            for (var i = 0; i < keys[1].length; i++) {
40083                if (keys[1][i].indexOf('_') === 0) {
40084                    keys[1].splice(i, 1);
40085                    i--;
40086                    continue;
40087                }
40088                if (keys[1][i] === 'baseRowTop' ||
40089                    keys[1][i] === 'baseScrollTop' ||
40090                    keys[1][i] === 'scrollTop' ||
40091                    (!this.c.saveState.paging && keys[0][i] === 'page')) {
40092                    keys[1].splice(i, 1);
40093                    i--;
40094                    continue;
40095                }
40096            }
40097            if (keys[0].length === 0 && keys[1].length > 0 ||
40098                keys[1].length === 0 && keys[0].length > 0) {
40099                return false;
40100            }
40101            // We are only going to compare the keys that are common between both states
40102            for (var i = 0; i < keys[0].length; i++) {
40103                if (!keys[1].includes(keys[0][i])) {
40104                    keys[0].splice(i, 1);
40105                    i--;
40106                }
40107            }
40108            for (var i = 0; i < keys[1].length; i++) {
40109                if (!keys[0].includes(keys[1][i])) {
40110                    keys[1].splice(i, 1);
40111                    i--;
40112                }
40113            }
40114            // Then each key and value has to be checked against each other
40115            for (var i = 0; i < keys[0].length; i++) {
40116                // If the keys dont equal, or their corresponding types are different we can return false
40117                if (keys[0][i] !== keys[1][i] || typeof states[0][keys[0][i]] !== typeof states[1][keys[1][i]]) {
40118                    return false;
40119                }
40120                // If the type is an object then further deep comparisons are required
40121                if (typeof states[0][keys[0][i]] === 'object') {
40122                    if (!this._deepCompare(states[0][keys[0][i]], states[1][keys[1][i]])) {
40123                        return false;
40124                    }
40125                }
40126                else if (typeof states[0][keys[0][i]] === 'number' && typeof states[1][keys[1][i]] === 'number') {
40127                    if (Math.round(states[0][keys[0][i]]) !== Math.round(states[1][keys[1][i]])) {
40128                        return false;
40129                    }
40130                }
40131                // Otherwise we can just check the value
40132                else if (states[0][keys[0][i]] !== states[1][keys[1][i]]) {
40133                    return false;
40134                }
40135            }
40136            // If we get all the way to here there are no differences so return true for this object
40137            return true;
40138        };
40139        StateRestore.prototype._keyupFunction = function (e) {
40140            // If enter same action as pressing the button
40141            if (e.key === 'Enter') {
40142                this.dom.confirmationButton.click();
40143            }
40144            // If escape close modal
40145            else if (e.key === 'Escape') {
40146                $$2('div.' + this.classes.background.replace(/ /g, '.')).click();
40147            }
40148        };
40149        /**
40150         * Creates a new confirmation modal for the user to approve an action
40151         *
40152         * @param title The title that is to be displayed at the top of the modal
40153         * @param buttonText The text that is to be displayed in the confirmation button of the modal
40154         * @param buttonAction The action that should be taken when the confirmation button is pressed
40155         * @param modalContents The contents for the main body of the modal
40156         */
40157        StateRestore.prototype._newModal = function (title, buttonText, buttonAction, modalContents) {
40158            var _this = this;
40159            this.dom.background.appendTo(this.dom.dtContainer);
40160            this.dom.confirmationTitleRow.empty().append(title);
40161            this.dom.confirmationButton.html(buttonText);
40162            this.dom.confirmation
40163                .empty()
40164                .append(this.dom.confirmationTitleRow)
40165                .append(modalContents)
40166                .append($$2('<div class="' + this.classes.confirmationButtons + '"></div>')
40167                .append(this.dom.confirmationButton))
40168                .appendTo(this.dom.dtContainer);
40169            $$2(this.s.dt.table().node()).trigger('dtsr-modal-inserted');
40170            var inputs = modalContents.children('input');
40171            // If there is an input focus on that
40172            if (inputs.length > 0) {
40173                $$2(inputs[0]).focus();
40174            }
40175            // Otherwise focus on the confirmation button
40176            else {
40177                this.dom.confirmationButton.focus();
40178            }
40179            var background = $$2('div.' + this.classes.background.replace(/ /g, '.'));
40180            if (this.c.modalCloseButton) {
40181                this.dom.confirmation.append(this.dom.closeButton);
40182                this.dom.closeButton.on('click', function () { return background.click(); });
40183            }
40184            // When the button is clicked, call the appropriate action,
40185            // remove the background and modal from the screen and unbind the keyup event.
40186            this.dom.confirmationButton.on('click', function () { return buttonAction(); });
40187            this.dom.confirmation.on('click', function (e) {
40188                e.stopPropagation();
40189            });
40190            // When the button is clicked, remove the background and modal from the screen and unbind the keyup event.
40191            background.one('click', function () {
40192                _this.dom.background.remove();
40193                _this.dom.confirmation.remove();
40194                $$2(document).unbind('keyup', function (e) { return _this._keyupFunction(e); });
40195            });
40196            $$2(document).on('keyup', function (e) { return _this._keyupFunction(e); });
40197        };
40198        /**
40199         * Convert from camelCase notation to the internal Hungarian.
40200         * We could use the Hungarian convert function here, but this is cleaner
40201         *
40202         * @param {object} obj Object to convert
40203         * @returns {object} Inverted object
40204         * @memberof DataTable#oApi
40205         */
40206        StateRestore.prototype._searchToHung = function (obj) {
40207            return {
40208                bCaseInsensitive: obj.caseInsensitive,
40209                bRegex: obj.regex,
40210                bSmart: obj.smart,
40211                sSearch: obj.search
40212            };
40213        };
40214        StateRestore.version = '1.2.2';
40215        StateRestore.classes = {
40216            background: 'dtsr-background',
40217            closeButton: 'dtsr-popover-close',
40218            confirmation: 'dtsr-confirmation',
40219            confirmationButton: 'dtsr-confirmation-button',
40220            confirmationButtons: 'dtsr-confirmation-buttons',
40221            confirmationMessage: 'dtsr-confirmation-message dtsr-name-label',
40222            confirmationText: 'dtsr-confirmation-text',
40223            confirmationTitle: 'dtsr-confirmation-title',
40224            confirmationTitleRow: 'dtsr-confirmation-title-row',
40225            dtButton: 'dt-button',
40226            input: 'dtsr-input',
40227            modalError: 'dtsr-modal-error',
40228            renameModal: 'dtsr-rename-modal'
40229        };
40230        StateRestore.defaults = {
40231            _createInSaved: false,
40232            ajax: false,
40233            create: true,
40234            creationModal: false,
40235            i18n: {
40236                creationModal: {
40237                    button: 'Create',
40238                    colReorder: 'Column Order:',
40239                    columns: {
40240                        search: 'Column Search:',
40241                        visible: 'Column Visibility:'
40242                    },
40243                    length: 'Page Length:',
40244                    name: 'Name:',
40245                    order: 'Sorting:',
40246                    paging: 'Paging:',
40247                    scroller: 'Scroll Position:',
40248                    search: 'Search:',
40249                    searchBuilder: 'SearchBuilder:',
40250                    searchPanes: 'SearchPanes:',
40251                    select: 'Select:',
40252                    title: 'Create New State',
40253                    toggleLabel: 'Includes:'
40254                },
40255                duplicateError: 'A state with this name already exists.',
40256                emptyError: 'Name cannot be empty.',
40257                emptyStates: 'No saved states',
40258                removeConfirm: 'Are you sure you want to remove %s?',
40259                removeError: 'Failed to remove state.',
40260                removeJoiner: ' and ',
40261                removeSubmit: 'Remove',
40262                removeTitle: 'Remove State',
40263                renameButton: 'Rename',
40264                renameLabel: 'New Name for %s:',
40265                renameTitle: 'Rename State'
40266            },
40267            modalCloseButton: true,
40268            remove: true,
40269            rename: true,
40270            save: true,
40271            saveState: {
40272                colReorder: true,
40273                columns: {
40274                    search: true,
40275                    visible: true
40276                },
40277                length: true,
40278                order: true,
40279                paging: true,
40280                scroller: true,
40281                search: true,
40282                searchBuilder: true,
40283                searchPanes: true,
40284                select: true
40285            },
40286            splitSecondaries: [
40287                'updateState',
40288                'renameState',
40289                'removeState'
40290            ],
40291            toggle: {
40292                colReorder: false,
40293                columns: {
40294                    search: false,
40295                    visible: false
40296                },
40297                length: false,
40298                order: false,
40299                paging: false,
40300                scroller: false,
40301                search: false,
40302                searchBuilder: false,
40303                searchPanes: false,
40304                select: false
40305            }
40306        };
40307        return StateRestore;
40308    }());
40309
40310    var $$1;
40311    var dataTable;
40312    function setJQuery(jq) {
40313        $$1 = jq;
40314        dataTable = jq.fn.dataTable;
40315    }
40316    var StateRestoreCollection = /** @class */ (function () {
40317        function StateRestoreCollection(settings, opts) {
40318            var _this = this;
40319            // Check that the required version of DataTables is included
40320            if (!dataTable || !dataTable.versionCheck || !dataTable.versionCheck('1.10.0')) {
40321                throw new Error('StateRestore requires DataTables 1.10 or newer');
40322            }
40323            // Check that Select is included
40324            // eslint-disable-next-line no-extra-parens
40325            if (!dataTable.Buttons) {
40326                throw new Error('StateRestore requires Buttons');
40327            }
40328            var table = new dataTable.Api(settings);
40329            this.classes = $$1.extend(true, {}, StateRestoreCollection.classes);
40330            if (table.settings()[0]._stateRestore !== undefined) {
40331                return;
40332            }
40333            // Get options from user
40334            this.c = $$1.extend(true, {}, StateRestoreCollection.defaults, opts);
40335            this.s = {
40336                dt: table,
40337                hasColReorder: dataTable.ColReorder !== undefined,
40338                hasScroller: dataTable.Scroller !== undefined,
40339                hasSearchBuilder: dataTable.SearchBuilder !== undefined,
40340                hasSearchPanes: dataTable.SearchPanes !== undefined,
40341                hasSelect: dataTable.select !== undefined,
40342                states: []
40343            };
40344            this.s.dt.on('xhr', function (e, xhrsettings, json) {
40345                // Has staterestore been used before? Is there anything to load?
40346                if (json && json.stateRestore) {
40347                    _this._addPreDefined(json.stateRestore);
40348                }
40349            });
40350            this.dom = {
40351                background: $$1('<div class="' + this.classes.background + '"/>'),
40352                closeButton: $$1('<div class="' + this.classes.closeButton + '">x</div>'),
40353                colReorderToggle: $$1('<div class="' + this.classes.formRow + ' ' + this.classes.checkRow + '">' +
40354                    '<input type="checkbox" class="' +
40355                    this.classes.colReorderToggle + ' ' +
40356                    this.classes.checkBox +
40357                    '" checked>' +
40358                    '<label class="' + this.classes.checkLabel + '">' +
40359                    this.s.dt.i18n('stateRestore.creationModal.colReorder', this.c.i18n.creationModal.colReorder) +
40360                    '</label>' +
40361                    '</div>'),
40362                columnsSearchToggle: $$1('<div class="' + this.classes.formRow + ' ' + this.classes.checkRow + '">' +
40363                    '<input type="checkbox" class="' +
40364                    this.classes.columnsSearchToggle + ' ' +
40365                    this.classes.checkBox +
40366                    '" checked>' +
40367                    '<label class="' + this.classes.checkLabel + '">' +
40368                    this.s.dt.i18n('stateRestore.creationModal.columns.search', this.c.i18n.creationModal.columns.search) +
40369                    '</label>' +
40370                    '</div>'),
40371                columnsVisibleToggle: $$1('<div class="' + this.classes.formRow + ' ' + this.classes.checkRow + ' ' + '">' +
40372                    '<input type="checkbox" class="' +
40373                    this.classes.columnsVisibleToggle + ' ' +
40374                    this.classes.checkBox +
40375                    '" checked>' +
40376                    '<label class="' + this.classes.checkLabel + '">' +
40377                    this.s.dt.i18n('stateRestore.creationModal.columns.visible', this.c.i18n.creationModal.columns.visible) +
40378                    '</label>' +
40379                    '</div>'),
40380                confirmation: $$1('<div class="' + this.classes.confirmation + '"/>'),
40381                confirmationTitleRow: $$1('<div class="' + this.classes.confirmationTitleRow + '"></div>'),
40382                createButtonRow: $$1('<div class="' + this.classes.formRow + ' ' + this.classes.modalFoot + '">' +
40383                    '<button class="' + this.classes.creationButton + ' ' + this.classes.dtButton + '">' +
40384                    this.s.dt.i18n('stateRestore.creationModal.button', this.c.i18n.creationModal.button) +
40385                    '</button>' +
40386                    '</div>'),
40387                creation: $$1('<div class="' + this.classes.creation + '"/>'),
40388                creationForm: $$1('<div class="' + this.classes.creationForm + '"/>'),
40389                creationTitle: $$1('<div class="' + this.classes.creationText 
40389+ '">' +
40390                    '<h2 class="' + this.classes.creationTitle + '">' +
40391                    this.s.dt.i18n('stateRestore.creationModal.title', this.c.i18n.creationModal.title) +
40392                    '</h2>' +
40393                    '</div>'),
40394                dtContainer: $$1(this.s.dt.table().container()),
40395                duplicateError: $$1('<span class="' + this.classes.modalError + '">' +
40396                    this.s.dt.i18n('stateRestore.duplicateError', this.c.i18n.duplicateError) +
40397                    '</span>'),
40398                emptyError: $$1('<span class="' + this.classes.modalError + '">' +
40399                    this.s.dt.i18n('stateRestore.emptyError', this.c.i18n.emptyError) +
40400                    '</span>'),
40401                lengthToggle: $$1('<div class="' + this.classes.formRow + ' ' + this.classes.checkRow + '">' +
40402                    '<input type="checkbox" class="' +
40403                    this.classes.lengthToggle + ' ' +
40404                    this.classes.checkBox +
40405                    '" checked>' +
40406                    '<label class="' + this.classes.checkLabel + '">' +
40407                    this.s.dt.i18n('stateRestore.creationModal.length', this.c.i18n.creationModal.length) +
40408                    '</label>' +
40409                    '</div>'),
40410                nameInputRow: $$1('<div class="' + this.classes.formRow + '">' +
40411                    '<label class="' + this.classes.nameLabel + '">' +
40412                    this.s.dt.i18n('stateRestore.creationModal.name', this.c.i18n.creationModal.name) +
40413                    '</label>' +
40414                    '<input class="' + this.classes.nameInput + '" type="text">' +
40415                    '</div>'),
40416                orderToggle: $$1('<div class="' + this.classes.formRow + ' ' + this.classes.checkRow + '">' +
40417                    '<input type="checkbox" class="' +
40418                    this.classes.orderToggle + ' ' +
40419                    this.classes.checkBox +
40420                    '" checked>' +
40421                    '<label class="' + this.classes.checkLabel + '">' +
40422                    this.s.dt.i18n('stateRestore.creationModal.order', this.c.i18n.creationModal.order) +
40423                    '</label>' +
40424                    '</div>'),
40425                pagingToggle: $$1('<div class="' + this.classes.formRow + ' ' + this.classes.checkRow + '">' +
40426                    '<input type="checkbox" class="' +
40427                    this.classes.pagingToggle + ' ' +
40428                    this.classes.checkBox +
40429                    '" checked>' +
40430                    '<label class="' + this.classes.checkLabel + '">' +
40431                    this.s.dt.i18n('stateRestore.creationModal.paging', this.c.i18n.creationModal.paging) +
40432                    '</label>' +
40433                    '</div>'),
40434                removeContents: $$1('<div class="' + this.classes.confirmationText + '"><span></span></div>'),
40435                removeTitle: $$1('<div class="' + this.classes.creationText + '">' +
40436                    '<h2 class="' + this.classes.creationTitle + '">' +
40437                    this.s.dt.i18n('stateRestore.removeTitle', this.c.i18n.removeTitle) +
40438                    '</h2>' +
40439                    '</div>'),
40440                scrollerToggle: $$1('<div class="' + this.classes.formRow + ' ' + this.classes.checkRow + '">' +
40441                    '<input type="checkbox" class="' +
40442                    this.classes.scrollerToggle + ' ' +
40443                    this.classes.checkBox +
40444                    '" checked>' +
40445                    '<label class="' + this.classes.checkLabel + '">' +
40446                    this.s.dt.i18n('stateRestore.creationModal.scroller', this.c.i18n.creationModal.scroller) +
40447                    '</label>' +
40448                    '</div>'),
40449                searchBuilderToggle: $$1('<div class="' + this.classes.formRow + ' ' + this.classes.checkRow + '">' +
40450                    '<input type="checkbox" class="' +
40451                    this.classes.searchBuilderToggle + ' ' +
40452                    this.classes.checkBox +
40453                    '" checked>' +
40454                    '<label class="' + this.classes.checkLabel + '">' +
40455                    this.s.dt.i18n('stateRestore.creationModal.searchBuilder', this.c.i18n.creationModal.searchBuilder) +
40456                    '</label>' +
40457                    '</div>'),
40458                searchPanesToggle: $$1('<div class="' + this.classes.formRow + ' ' + this.classes.checkRow + '">' +
40459                    '<input type="checkbox" class="' +
40460                    this.classes.searchPanesToggle + ' ' +
40461                    this.classes.checkBox +
40462                    '" checked>' +
40463                    '<label class="' + this.classes.checkLabel + '">' +
40464                    this.s.dt.i18n('stateRestore.creationModal.searchPanes', this.c.i18n.creationModal.searchPanes) +
40465                    '</label>' +
40466                    '</div>'),
40467                searchToggle: $$1('<div class="' + this.classes.formRow + ' ' + this.classes.checkRow + '">' +
40468                    '<input type="checkbox" class="' +
40469                    this.classes.searchToggle + ' ' +
40470                    this.classes.checkBox +
40471                    '" checked>' +
40472                    '<label class="' + this.classes.checkLabel + '">' +
40473                    this.s.dt.i18n('stateRestore.creationModal.search', this.c.i18n.creationModal.search) +
40474                    '</label>' +
40475                    '</div>'),
40476                selectToggle: $$1('<div class="' + this.classes.formRow + ' ' + this.classes.checkRow + '">' +
40477                    '<input type="checkbox" class="' +
40478                    this.classes.selectToggle + ' ' +
40479                    this.classes.checkBox +
40480                    '" checked>' +
40481                    '<label class="' + this.classes.checkLabel + '">' +
40482                    this.s.dt.i18n('stateRestore.creationModal.select', this.c.i18n.creationModal.select) +
40483                    '</label>' +
40484                    '</div>'),
40485                toggleLabel: $$1('<label class="' + this.classes.nameLabel + ' ' + this.classes.toggleLabel + '">' +
40486                    this.s.dt.i18n('stateRestore.creationModal.toggleLabel', this.c.i18n.creationModal.toggleLabel) +
40487                    '</label>')
40488            };
40489            table.settings()[0]._stateRestore = this;
40490            this._searchForStates();
40491            // Has staterestore been used before? Is there anything to load?
40492            this._addPreDefined(this.c.preDefined);
40493            var ajaxFunction;
40494            var ajaxData = {
40495                action: 'load'
40496            };
40497            if (typeof this.c.ajax === 'function') {
40498                ajaxFunction = function () {
40499                    if (typeof _this.c.ajax === 'function') {
40500                        _this.c.ajax.call(_this.s.dt, ajaxData, function (s) { return _this._addPreDefined(s); });
40501                    }
40502                };
40503            }
40504            else if (typeof this.c.ajax === 'string') {
40505                ajaxFunction = function () {
40506                    $$1.ajax({
40507                        data: ajaxData,
40508                        success: function (data) {
40509                            _this._addPreDefined(data);
40510                        },
40511                        type: 'POST',
40512                        url: _this.c.ajax
40513                    });
40514                };
40515            }
40516            if (typeof ajaxFunction === 'function') {
40517                if (this.s.dt.settings()[0]._bInitComplete) {
40518                    ajaxFunction();
40519                }
40520                else {
40521                    this.s.dt.one('preInit.dtsr', function () {
40522                        ajaxFunction();
40523                    });
40524                }
40525            }
40526            this.s.dt.on('destroy.dtsr', function () {
40527                _this.destroy();
40528            });
40529            this.s.dt.on('draw.dtsr buttons-action.dtsr', function () { return _this.findActive(); });
40530            return this;
40531        }
40532        /**
40533         * Adds a new StateRestore instance to the collection based on the current properties of the table
40534         *
40535         * @param identifier The value that is used to identify a state.
40536         * @returns The state that has been created
40537         */
40538        StateRestoreCollection.prototype.addState = function (identifier, currentIdentifiers, options) {
40539            var _this = this;
40540            // If creation/saving is not allowed then return
40541            if (!this.c.create || !this.c.save) {
40542                return;
40543            }
40544            // Check if the state exists before creating a new ones
40545            var state = this.getState(identifier);
40546            var createFunction = function (id, toggles) {
40547                if (id.length === 0) {
40548                    return 'empty';
40549                }
40550                else if (currentIdentifiers.includes(id)) {
40551                    return 'duplicate';
40552                }
40553                _this.s.dt.state.save();
40554                var that = _this;
40555                var successCallback = function () {
40556                    that.s.states.push(this);
40557                    that._collectionRebuild();
40558                };
40559                var currState = _this.s.dt.state();
40560                currState.stateRestore = {
40561                    isPredefined: false,
40562                    state: id,
40563                    tableId: _this.s.dt.table().node().id
40564                };
40565                if (toggles.saveState) {
40566                    var opts = _this.c.saveState;
40567                    // We don't want to extend, but instead AND all properties of the saveState option
40568                    for (var _i = 0, _a = Object.keys(toggles.saveState); _i < _a.length; _i++) {
40569                        var key = _a[_i];
40570                        if (!toggles.saveState[key]) {
40571                            opts[key] = false;
40572                        }
40573                    }
40574                    _this.c.saveState = opts;
40575                }
40576                var newState = new StateRestore(_this.s.dt.settings()[0], $$1.extend(true, {}, _this.c, options), id, currState, false, successCallback);
40577                $$1(_this.s.dt.table().node()).on('dtsr-modal-inserted', function () {
40578                    newState.dom.confirmation.one('dtsr-remove', function () { return _this._removeCallback(newState.s.identifier); });
40579                    newState.dom.confirmation.one('dtsr-rename', function () { return _this._collectionRebuild(); });
40580                    newState.dom.confirmation.one('dtsr-save', function () { return _this._collectionRebuild(); });
40581                });
40582                return true;
40583            };
40584            // If there isn't already a state with this identifier
40585            if (state === null) {
40586                if (this.c.creationModal || options !== undefined && options.creationModal) {
40587                    this._creationModal(createFunction, identifier, options);
40588                }
40589                else {
40590                    var success = createFunction(identifier, {});
40591                    if (success === 'empty') {
40592                        throw new Error(this.s.dt.i18n('stateRestore.emptyError', this.c.i18n.emptyError));
40593                    }
40594                    else if (success === 'duplicate') {
40595                        throw new Error(this.s.dt.i18n('stateRestore.duplicateError', this.c.i18n.duplicateError));
40596                    }
40597                }
40598            }
40599            else {
40600                throw new Error(this.s.dt.i18n('stateRestore.duplicateError', this.c.i18n.duplicateError));
40601            }
40602        };
40603        /**
40604         * Removes all of the states, showing a modal to the user for confirmation
40605         *
40606         * @param removeFunction The action to be taken when the action is confirmed
40607         */
40608        StateRestoreCollection.prototype.removeAll = function (removeFunction) {
40609            // There are no states to remove so just return
40610            if (this.s.states.length === 0) {
40611                return;
40612            }
40613            var ids = this.s.states.map(function (state) { return state.s.identifier; });
40614            var replacementString = ids[0];
40615            if (ids.length > 1) {
40616                replacementString = ids.slice(0, -1).join(', ') +
40617                    this.s.dt.i18n('stateRestore.removeJoiner', this.c.i18n.removeJoiner) +
40618                    ids.slice(-1);
40619            }
40620            $$1(this.dom.removeContents.children('span')).html(this.s.dt
40621                .i18n('stateRestore.removeConfirm', this.c.i18n.removeConfirm)
40622                .replace(/%s/g, replacementString));
40623            this._newModal(this.dom.removeTitle, this.s.dt.i18n('stateRestore.removeSubmit', this.c.i18n.removeSubmit), removeFunction, this.dom.removeContents);
40624        };
40625        /**
40626         * Removes all of the dom elements from the document for the collection and the stored states
40627         */
40628        StateRestoreCollection.prototype.destroy = function () {
40629            for (var _i = 0, _a = this.s.states; _i < _a.length; _i++) {
40630                var state = _a[_i];
40631                state.destroy();
40632            }
40633            Object.values(this.dom).forEach(function (node) {
40634                node.off();
40635                node.remove();
40636            });
40637            this.s.states = [];
40638            this.s.dt.off('.dtsr');
40639            $$1(this.s.dt.table().node()).off('.dtsr');
40640        };
40641        /**
40642         * Identifies active states and updates their button to reflect this.
40643         *
40644         * @returns An array containing objects with the details of currently active states
40645         */
40646        StateRestoreCollection.prototype.findActive = function () {
40647            // Make sure that the state is up to date
40648            this.s.dt.state.save();
40649            var currState = this.s.dt.state();
40650            // Make all of the buttons inactive so that only any that match will be marked as active
40651            var buttons = $$1('button.' + $$1.fn.DataTable.Buttons.defaults.dom.button.className.replace(/ /g, '.'));
40652            // Some of the styling libraries use a tags instead of buttons
40653            if (buttons.length === 0) {
40654                buttons = $$1('a.' + $$1.fn.DataTable.Buttons.defaults.dom.button.className.replace(/ /g, '.'));
40655            }
40656            for (var _i = 0, buttons_1 = buttons; _i < buttons_1.length; _i++) {
40657                var button = buttons_1[_i];
40658                this.s.dt.button($$1(button).parent()[0]).active(false);
40659            }
40660            var results = [];
40661            // Go through all of the states comparing if their state is the same to the current one
40662            for (var _a = 0, _b = this.s.states; _a < _b.length; _a++) {
40663                var state = _b[_a];
40664                if (state.compare(currState)) {
40665                    results.push({
40666                        data: state.s.savedState,
40667                        name: state.s.identifier
40668                    });
40669                    // If so, find the corresponding button and mark it as active
40670                    for (var _c = 0, buttons_2 = buttons; _c < buttons_2.length; _c++) {
40671                        var button = buttons_2[_c];
40672                        if ($$1(button).text() === state.s.identifier) {
40673                            this.s.dt.button($$1(button).parent()[0]).active(true);
40674                            break;
40675                        }
40676                    }
40677                }
40678            }
40679            return results;
40680        };
40681        /**
40682         * Gets a single state that has the identifier matching that which is passed in
40683         *
40684         * @param identifier The value that is used to identify a state
40685         * @returns The state that has been identified or null if no states have been identified
40686         */
40687        StateRestoreCollection.prototype.getState = function (identifier) {
40688            for (var _i = 0, _a = this.s.states; _i < _a.length; _i++) {
40689                var state = _a[_i];
40690                if (state.s.identifier === identifier) {
40691                    return state;
40692                }
40693            }
40694            return null;
40695        };
40696        /**
40697         * Gets an array of all of the states
40698         *
40699         * @returns Any states that have been identified
40700         */
40701        StateRestoreCollection.prototype.getStates = function (ids) {
40702            if (ids === undefined) {
40703                return this.s.states;
40704            }
40705            else {
40706                var states = [];
40707                for (var _i = 0, ids_1 = ids; _i < ids_1.length; _i++) {
40708                    var id = ids_1[_i];
40709                    var found = false;
40710                    for (var _a = 0, _b = this.s.states; _a < _b.length; _a++) {
40711                        var state = _b[_a];
40712                        if (id === state.s.identifier) {
40713                            states.push(state);
40714                            found = true;
40715                            break;
40716                        }
40717                    }
40718                    if (!found) {
40719                        states.push(undefined);
40720                    }
40721                }
40722                return states;
40723            }
40724        };
40725        /**
40726         * Reloads states that are set via datatables config or over ajax
40727         *
40728         * @param preDefined Object containing the predefined states that are to be reintroduced
40729         */
40730        StateRestoreCollection.prototype._addPreDefined = function (preDefined) {
40731            var _this = this;
40732            // There is a potential issue here if sorting where the string parts of the name are the same,
40733            // only the number differs and there are many states - but this wouldn't be usfeul naming so
40734            // more of a priority to sort alphabetically
40735            var states = Object.keys(preDefined).sort(function (a, b) { return a > b ? 1 : a < b ? -1 : 0; });
40736            var _loop_1 = function (state) {
40737                for (var i = 0; i < this_1.s.states.length; i++) {
40738                    if (this_1.s.states[i].s.identifier === state) {
40739                        this_1.s.states.splice(i, 1);
40740                    }
40741                }
40742                var that = this_1;
40743                var successCallback = function () {
40744                    that.s.states.push(this);
40745                    that._collectionRebuild();
40746                };
40747                var loadedState = preDefined[state];
40748                var newState = new StateRestore(this_1.s.dt, $$1.extend(true, {}, this_1.c, loadedState.c !== undefined ?
40749                    { saveState: loadedState.c.saveState } :
40750                    undefined, true), state, loadedState, true, successCallback);
40751                newState.s.savedState = loadedState;
40752                $$1(this_1.s.dt.table().node()).on('dtsr-modal-inserted', function () {
40753                    newState.dom.confirmation.one('dtsr-remove', function () { return _this._removeCallback(newState.s.identifier); });
40754                    newState.dom.confirmation.one('dtsr-rename', function () { return _this._collectionRebuild(); });
40755                    newState.dom.confirmation.one('dtsr-save', function () { return _this._collectionRebuild(); });
40756                });
40757            };
40758            var this_1 = this;
40759            for (var _i = 0, states_1 = states; _i < states_1.length; _i++) {
40760                var state = states_1[_i];
40761                _loop_1(state);
40762            }
40763        };
40764        /**
40765         * Rebuilds all of the buttons in the collection of states to make sure that states and text is up to date
40766         */
40767        StateRestoreCollection.prototype._collectionRebuild = function () {
40768            var button = this.s.dt.button('SaveStateRestore:name');
40769            var stateButtons = [];
40770            // Need to get the original configuration object, so we can rebuild it
40771            // It might be nested, so need to traverse down the tree
40772            if (button[0]) {
40773                var idxs = button.index().split('-');
40774                stateButtons = button[0].inst.c.buttons;
40775                for (var i = 0; i < idxs.length; i++) {
40776                    if (stateButtons[idxs[i]].buttons) {
40777                        stateButtons = stateButtons[idxs[i]].buttons;
40778                    }
40779                    else {
40780                        stateButtons = [];
40781                        break;
40782                    }
40783                }
40784            }
40785            // remove any states from the previous rebuild - if they are still there they will be added later
40786            for (var i = 0; i < stateButtons.length; i++) {
40787                if (stateButtons[i].extend === 'stateRestore') {
40788                    stateButtons.splice(i, 1);
40789                    i--;
40790                }
40791            }
40792            if (this.c._createInSaved) {
40793                stateButtons.push('createState');
40794            }
40795            var emptyText = '<span class="' + this.classes.emptyStates + '">' +
40796                this.s.dt.i18n('stateRestore.emptyStates', this.c.i18n.emptyStates) +
40797                '</span>';
40798            // If there are no states display an empty message
40799            if (this.s.states.length === 0) {
40800                // Don't want the empty text included more than twice
40801                if (!stateButtons.includes(emptyText)) {
40802                    stateButtons.push(emptyText);
40803                }
40804            }
40805            else {
40806                // There are states to add so there shouldn't be any empty text left!
40807                while (stateButtons.includes(emptyText)) {
40808                    stateButtons.splice(stateButtons.indexOf(emptyText), 1);
40809                }
40810                // There is a potential issue here if sorting where the string parts of the name are the same,
40811                // only the number differs and there are many states - but this wouldn't be usfeul naming so
40812                // more of a priority to sort alphabetically
40813                this.s.states = this.s.states.sort(function (a, b) {
40814                    var aId = a.s.identifier;
40815                    var bId = b.s.identifier;
40816                    return aId > bId ?
40817                        1 :
40818                        aId < bId ?
40819                            -1 :
40820                            0;
40821                });
40822                // Construct the split property of each button
40823                for (var _i = 0, _a = this.s.states; _i < _a.length; _i++) {
40824                    var state = _a[_i];
40825                    var split = Object.assign([], this.c.splitSecondaries);
40826                    if (split.includes('updateState') && (!this.c.save || !state.c.save)) {
40827                        split.splice(split.indexOf('updateState'), 1);
40828                    }
40829                    if (split.includes('renameState') &&
40830                        (!this.c.save || !state.c.save || !this.c.rename || !state.c.rename)) {
40831                        split.splice(split.indexOf('renameState'), 1);
40832                    }
40833                    if (split.includes('removeState') && (!this.c.remove || !state.c.remove)) {
40834                        split.splice(split.indexOf('removeState'), 1);
40835                    }
40836                    if (split.length > 0 &&
40837                        !split.includes('<h3>' + state.s.identifier + '</h3>')) {
40838                        split.unshift('<h3>' + state.s.identifier + '</h3>');
40839                    }
40840                    stateButtons.push({
40841                        _stateRestore: state,
40842                        attr: {
40843                            title: state.s.identifier
40844                        },
40845                        config: {
40846                            split: split
40847                        },
40848                        extend: 'stateRestore',
40849                        text: state.s.identifier
40850                    });
40851                }
40852            }
40853            button.collectionRebuild(stateButtons);
40854            // Need to disable the removeAllStates button if there are no states and it is present
40855            var buttons = this.s.dt.buttons();
40856            for (var _b = 0, buttons_3 = buttons; _b < buttons_3.length; _b++) {
40857                var butt = buttons_3[_b];
40858                if ($$1(b
40858utt.node).hasClass('dtsr-removeAllStates')) {
40859                    if (this.s.states.length === 0) {
40860                        this.s.dt.button(butt.node).disable();
40861                    }
40862                    else {
40863                        this.s.dt.button(butt.node).enable();
40864                    }
40865                }
40866            }
40867        };
40868        /**
40869         * Displays a modal that is used to get information from the user to create a new state.
40870         *
40871         * @param buttonAction The action that should be taken when the button is pressed
40872         * @param identifier The default identifier for the next new state
40873         */
40874        StateRestoreCollection.prototype._creationModal = function (buttonAction, identifier, options) {
40875            var _this = this;
40876            this.dom.creation.empty();
40877            this.dom.creationForm.empty();
40878            this.dom.nameInputRow.children('input').val(identifier);
40879            this.dom.creationForm.append(this.dom.nameInputRow);
40880            var tableConfig = this.s.dt.settings()[0].oInit;
40881            var togglesToInsert = [];
40882            var toggleDefined = options !== undefined && options.toggle !== undefined;
40883            // Order toggle - check toggle and saving enabled
40884            if (((!toggleDefined || options.toggle.order === undefined) && this.c.toggle.order ||
40885                toggleDefined && options.toggle.order) &&
40886                this.c.saveState.order &&
40887                (tableConfig.ordering === undefined || tableConfig.ordering)) {
40888                togglesToInsert.push(this.dom.orderToggle);
40889            }
40890            // Search toggle - check toggle and saving enabled
40891            if (((!toggleDefined || options.toggle.search === undefined) && this.c.toggle.search ||
40892                toggleDefined && options.toggle.search) &&
40893                this.c.saveState.search &&
40894                (tableConfig.searching === undefined || tableConfig.searching)) {
40895                togglesToInsert.push(this.dom.searchToggle);
40896            }
40897            // Paging toggle - check toggle and saving enabled
40898            if (((!toggleDefined || options.toggle.paging === undefined) && this.c.toggle.paging ||
40899                toggleDefined && options.toggle.paging) &&
40900                this.c.saveState.paging &&
40901                (tableConfig.paging === undefined || tableConfig.paging)) {
40902                togglesToInsert.push(this.dom.pagingToggle);
40903            }
40904            // Page Length toggle - check toggle and saving enabled
40905            if (((!toggleDefined || options.toggle.length === undefined) && this.c.toggle.length ||
40906                toggleDefined && options.toggle.length) &&
40907                this.c.saveState.length &&
40908                (tableConfig.length === undefined || tableConfig.length)) {
40909                togglesToInsert.push(this.dom.lengthToggle);
40910            }
40911            // ColReorder toggle - check toggle and saving enabled
40912            if (this.s.hasColReorder &&
40913                ((!toggleDefined || options.toggle.colReorder === undefined) && this.c.toggle.colReorder ||
40914                    toggleDefined && options.toggle.colReorder) &&
40915                this.c.saveState.colReorder) {
40916                togglesToInsert.push(this.dom.colReorderToggle);
40917            }
40918            // Scroller toggle - check toggle and saving enabled
40919            if (this.s.hasScroller &&
40920                ((!toggleDefined || options.toggle.scroller === undefined) && this.c.toggle.scroller ||
40921                    toggleDefined && options.toggle.scroller) &&
40922                this.c.saveState.scroller) {
40923                togglesToInsert.push(this.dom.scrollerToggle);
40924            }
40925            // SearchBuilder toggle - check toggle and saving enabled
40926            if (this.s.hasSearchBuilder &&
40927                ((!toggleDefined || options.toggle.searchBuilder === undefined) && this.c.toggle.searchBuilder ||
40928                    toggleDefined && options.toggle.searchBuilder) &&
40929                this.c.saveState.searchBuilder) {
40930                togglesToInsert.push(this.dom.searchBuilderToggle);
40931            }
40932            // SearchPanes toggle - check toggle and saving enabled
40933            if (this.s.hasSearchPanes &&
40934                ((!toggleDefined || options.toggle.searchPanes === undefined) && this.c.toggle.searchPanes ||
40935                    toggleDefined && options.toggle.searchPanes) &&
40936                this.c.saveState.searchPanes) {
40937                togglesToInsert.push(this.dom.searchPanesToggle);
40938            }
40939            // Select toggle - check toggle and saving enabled
40940            if (this.s.hasSelect &&
40941                ((!toggleDefined || options.toggle.select === undefined) && this.c.toggle.select ||
40942                    toggleDefined && options.toggle.select) &&
40943                this.c.saveState.select) {
40944                togglesToInsert.push(this.dom.selectToggle);
40945            }
40946            // Columns toggle - check toggle and saving enabled
40947            if (typeof this.c.toggle.columns === 'boolean' &&
40948                ((!toggleDefined || options.toggle.order === undefined) && this.c.toggle.columns ||
40949                    toggleDefined && options.toggle.order) &&
40950                this.c.saveState.columns) {
40951                togglesToInsert.push(this.dom.columnsSearchToggle);
40952                togglesToInsert.push(this.dom.columnsVisibleToggle);
40953            }
40954            else if ((!toggleDefined || options.toggle.columns === undefined) && typeof this.c.toggle.columns !== 'boolean' ||
40955                typeof options.toggle.order !== 'boolean') {
40956                if (typeof this.c.saveState.columns !== 'boolean' && this.c.saveState.columns) {
40957                    // Column search toggle - check toggle and saving enabled
40958                    if ((
40959                    // columns.search is defined when passed in
40960                    toggleDefined &&
40961                        options.toggle.columns !== undefined &&
40962                        typeof options.toggle.columns !== 'boolean' &&
40963                        options.toggle.columns.search ||
40964                        // Columns search is not defined when passed in but is 
40964in defaults
40965                        (!toggleDefined ||
40966                            options.toggle.columns === undefined ||
40967                            typeof options.toggle.columns !== 'boolean' && options.toggle.columns.search === undefined) &&
40968                            typeof this.c.toggle.columns !== 'boolean' &&
40969                            this.c.toggle.columns.search) &&
40970                        this.c.saveState.columns.search) {
40971                        togglesToInsert.push(this.dom.columnsSearchToggle);
40972                    }
40973                    // Column visiblity toggle - check toggle and saving enabled
40974                    if ((
40975                    // columns.visible is defined when passed in
40976                    toggleDefined &&
40977                        options.toggle.columns !== undefined &&
40978                        typeof options.toggle.columns !== 'boolean' &&
40979                        options.toggle.columns.visible ||
40980                        // Columns visible is not defined when passed in but is in defaults
40981                        (!toggleDefined ||
40982                            options.toggle.columns === undefined ||
40983                            typeof options.toggle.columns !== 'boolean' && options.toggle.columns.visible === undefined) &&
40984                            typeof this.c.toggle.columns !== 'boolean' &&
40985                            this.c.toggle.columns.visible) &&
40986                        this.c.saveState.columns.visible) {
40987                        togglesToInsert.push(this.dom.columnsVisibleToggle);
40988                    }
40989                }
40990                else if (this.c.saveState.columns) {
40991                    togglesToInsert.push(this.dom.columnsSearchToggle);
40992                    togglesToInsert.push(this.dom.columnsVisibleToggle);
40993                }
40994            }
40995            // Make sure that the toggles are displayed alphabetically
40996            togglesToInsert.sort(function (a, b) {
40997                var aVal = a.children('label.dtsr-check-label')[0].innerHTML;
40998                var bVal = b.children('label.dtsr-check-label')[0].innerHTML;
40999                if (aVal < bVal) {
41000                    return -1;
41001                }
41002                else if (aVal > bVal) {
41003                    return 1;
41004                }
41005                else {
41006                    return 0;
41007                }
41008            });
41009            // Append all of the toggles that are to be inserted
41010            for (var _i = 0, togglesToInsert_1 = togglesToInsert; _i < togglesToInsert_1.length; _i++) {
41011                var toggle = togglesToInsert_1[_i];
41012                this.dom.creationForm.append(toggle);
41013            }
41014            // Insert the toggle label next to the first check box
41015            $$1(this.dom.creationForm.children('div.' + this.classes.checkRow)[0]).prepend(this.dom.toggleLabel);
41016            // Insert the creation modal and the background
41017            this.dom.background.appendTo(this.dom.dtContainer);
41018            this.dom.creation
41019                .append(this.dom.creationTitle)
41020                .append(this.dom.creationForm)
41021                .append(this.dom.createButtonRow)
41022                .appendTo(this.dom.dtContainer);
41023            $$1(this.s.dt.table().node()).trigger('dtsr-modal-inserted');
41024            var _loop_2 = function (toggle) {
41025                $$1(toggle.children('label:last-child')).on('click', function () {
41026                    toggle.children('input').prop('checked', !toggle.children('input').prop('checked'));
41027                });
41028            };
41029            // Allow the label to be clicked to toggle the checkbox
41030            for (var _a = 0, togglesToInsert_2 = togglesToInsert; _a < togglesToInsert_2.length; _a++) {
41031                var toggle = togglesToInsert_2[_a];
41032                _loop_2(toggle);
41033            }
41034            var creationButton = $$1('button.' + this.classes.creationButton.replace(/ /g, '.'));
41035            var inputs = this.dom.creationForm.find('input');
41036            // If there is an input focus on that
41037            if (inputs.length > 0) {
41038                $$1(inputs[0]).focus();
41039            }
41040            // Otherwise focus on the confirmation button
41041            else {
41042                creationButton.focus();
41043            }
41044            var background = $$1('div.' + this.classes.background.replace(/ /g, '.'));
41045            var keyupFunction = function (e) {
41046                if (e.key === 'Enter') {
41047                    creationButton.click();
41048                }
41049                else if (e.key === 'Escape') {
41050                    background.click();
41051                }
41052            };
41053            if (this.c.modalCloseButton) {
41054                this.dom.creation.append(this.dom.closeButton);
41055                this.dom.closeButton.on('click', function () { return background.click(); });
41056            }
41057            creationButton.on('click', function () {
41058                // Get the values of the checkBoxes
41059                var saveState = {
41060                    colReorder: _this.dom.colReorderToggle.children('input').is(':checked'),
41061                    columns: {
41062                        search: _this.dom.columnsSearchToggle.children('input').is(':checked'),
41063                        visible: _this.dom.columnsVisibleToggle.children('input').is(':checked')
41064                    },
41065                    length: _this.dom.lengthToggle.children('input').is(':checked'),
41066                    order: _this.dom.orderToggle.children('input').is(':checked'),
41067                    paging: _this.dom.pagingToggle.children('input').is(':checked'),
41068                    scroller: _this.dom.scrollerToggle.children('input').is(':checked'),
41069                    search: _this.dom.searchToggle.children('input').is(':checked'),
41070                    searchBuilder: _this.dom.searchBuilderToggle.children('input').is(':checked'),
41071                    searchPanes: _this.dom.searchPanesToggle.children('input').is(':checked'),
41072                    select: _this.dom.selectToggle.children('input').is(':checked')
41073                };
41074                // Call the buttons functionality passing in the identifier and what should be saved
41075                var success = buttonAction($$1('input.' + _this.classes.nameInput.replace(/ /g, '.')).val(), { saveState: saveState });
41076                if (success === true) {
41077                    // Remove the dom elements as operation has completed
41078                    _this.dom.background.remove();
41079                    _this.dom.creation.remove();
41080                    // Unbind the keyup function  - don't want it to run unnecessarily on every keypress that occurs
41081                    $$1(document).unbind('keyup', keyupFunction);
41082                }
41083                else {
41084                    _this.dom.creation.children('.' + _this.classes.modalError).remove();
41085                    _this.dom.creation.append(_this.dom[success + 'Error']);
41086                }
41087            });
41088            background.one('click', function () {
41089                // Remove the dome elements as operation has been cancelled
41090                _this.dom.background.remove();
41091                _this.dom.creation.remove();
41092                // Unbind the keyup function - don't want it to run unnecessarily on every keypress that occurs
41093                $$1(document).unbind('keyup', keyupFunction);
41094                // Rebuild the collection to ensure that the latest changes are present
41095                _this._collectionRebuild();
41096            });
41097            // Have to listen to the keyup event as `escape` doesn't trigger keypress
41098            $$1(document).on('keyup', keyupFunction);
41099            // Need to save the state before the focus is lost when the modal is interacted with
41100            this.s.dt.state.save();
41101        };
41102        /**
41103         * This callback is called when a state is removed.
41104         * This removes the state from storage and also strips it's button from the container
41105         *
41106         * @param identifier The value that is used to identify a state
41107         */
41108        StateRestoreCollection.prototype._removeCallback = function (identifier) {
41109            for (var i = 0; i < this.s.states.length; i++) {
41110                if (this.s.states[i].s.identifier === identifier) {
41111                    this.s.states.splice(i, 1);
41112                    i--;
41113                }
41114            }
41115            this._collectionRebuild();
41116            return true;
41117        };
41118        /**
41119         * Creates a new confirmation modal for the user to approve an action
41120         *
41121         * @param title The title that is to be displayed at the top of the modal
41122         * @param buttonText The text that is to be displayed in the confirmation button of the modal
41123         * @param buttonAction The action that should be taken when the confirmation button is pressed
41124         * @param modalContents The contents for the main body of the modal
41125         */
41126        StateRestoreCollection.prototype._newModal = function (title, buttonText, buttonAction, modalContents) {
41127            var _this = this;
41128            this.dom.background.appendTo(this.dom.dtContainer);
41129            this.dom.confirmationTitleRow.empty().append(title);
41130            var confirmationButton = $$1('<button class="' + this.classes.confirmationButton + ' ' + this.classes.dtButton + '">' +
41131                buttonText +
41132                '</button>');
41133            this.dom.confirmation
41134                .empty()
41135                .append(this.dom.confirmationTitleRow)
41136                .append(modalContents)
41137                .append($$1('<div class="' + this.classes.confirmationButtons + '"></div>')
41138                .append(confirmationButton))
41139                .appendTo(this.dom.dtContainer);
41140            $$1(this.s.dt.table().node()).trigger('dtsr-modal-inserted');
41141            var inputs = modalContents.children('input');
41142            // If there is an input focus on that
41143            if (inputs.length > 0) {
41144                $$1(inputs[0]).focus();
41145            }
41146            // Otherwise focus on the confirmation button
41147            else {
41148                confirmationButton.focus();
41149            }
41150            var background = $$1('div.' + this.classes.background.replace(/ /g, '.'));
41151            var keyupFunction = function (e) {
41152                // If enter same action as pressing the button
41153                if (e.key === 'Enter') {
41154                    confirmationButton.click();
41155                }
41156                // If escape close modal
41157                else if (e.key === 'Escape') {
41158                    background.click();
41159                }
41160            };
41161            // When the button is clicked, call the appropriate action,
41162            // remove the background and modal from the screen and unbind the keyup event.
41163            confirmationButton.on('click', function () {
41164                var success = buttonAction(true);
41165                if (success === true) {
41166                    _this.dom.background.remove();
41167                    _this.dom.confirmation.remove();
41168                    $$1(document).unbind('keyup', keyupFunction);
41169                    confirmationButton.off('click');
41170                }
41171                else {
41172                    _this.dom.confirmation.children('.' + _this.classes.modalError).remove();
41173                    _this.dom.confirmation.append(_this.dom[success + 'Error']);
41174                }
41175            });
41176            this.dom.confirmation.on('click', function (e) {
41177                e.stopPropagation();
41178            });
41179            // When the button is clicked, remove the background and modal from the screen and unbind the keyup event.
41180            background.one('click', function () {
41181                _this.dom.background.remove();
41182                _this.dom.confirmation.remove();
41183                $$1(document).unbind('keyup', keyupFunction);
41184            });
41185            $$1(document).on('keyup', keyupFunction);
41186        };
41187        /**
41188         * Private method that checks for previously created states on initialisation
41189         */
41190        StateRestoreCollection.prototype._searchForStates = function () {
41191            var _this = this;
41192            var keys = Object.keys(localStorage);
41193            var _loop_3 = function (key) {
41194                // eslint-disable-next-line no-useless-escape
41195                if (key.match(new RegExp('^DataTables_stateRestore_.*_' + location.pathname.replace(/\//g, '/') + '$')) ||
41196                    key.match(new RegExp('^DataTables_stateRestore_.*_' + location.pathname.replace(/\//g, '/') +
41197                        '_' + this_2.s.dt.table().node().id + '$'))) {
41198                    var loadedState_1 = JSON.parse(localStorage.getItem(key));
41199                    if (loadedState_1.stateRestore.isPreDefined ||
41200                        (loadedState_1.stateRestore.tableId &&
41201                            loadedState_1.stateRestore.tableId !== this_2.s.dt.table().node().id)) {
41202                        return "continue";
41203                    }
41204                    var that_1 = this_2;
41205                    var successCallback = function () {
41206                        this.s.savedState = loadedState_1;
41207                        that_1.s.states.push(this);
41208                        that_1._collectionRebuild();
41209                    };
41210                    var newState_1 = new StateRestore(this_2.s.dt, $$1.extend(true, {}, this_2.c, { saveState: loadedState_1.c.saveState }), loadedState_1.stateRestore.state, loadedState_1, false, successCallback);
41211                    $$1(this_2.s.dt.table().node()).on('dtsr-modal-inserted', function () {
41212                        newState_1.dom.confirmation.one('dtsr-remove', function () { return _this._removeCallback(newState_1.s.identifier); });
41213                        newState_1.dom.confirmation.one('dtsr-rename', function () { return _this._collectionRebuild(); });
41214                        newState_1.dom.confirmation.one('dtsr-save', function () { return _this._collectionRebuild(); });
41215                    });
41216                }
41217            };
41218            var this_2 = this;
41219            for (var _i = 0, keys_1 = keys; _i < keys_1.length; _i++) {
41220                var key = keys_1[_i];
41221                _loop_3(key);
41222            }
41223        };
41224        StateRestoreCollection.version = '1.0.0';
41225        StateRestoreCollection.classes = {
41226            background: 'dtsr-background',
41227            checkBox: 'dtsr-check-box',
41228            checkLabel: 'dtsr-check-label',
41229            checkRow: 'dtsr-check-row',
41230            closeButton: 'dtsr-popover-close',
41231            colReorderToggle: 'dtsr-colReorder-toggle',
41232            columnsSearchToggle: 'dtsr-columns-search-toggle',
41233            columnsVisibleToggle: 'dtsr-columns-visible-toggle',
41234            confirmation: 'dtsr-confirmation',
41235            confirmationButton: 'dtsr-confirmation-button',
41236            confirmationButtons: 'dtsr-confirmation-buttons',
41237            confirmationMessage: 'dtsr-confirmation-message dtsr-name-label',
41238            confirmationText: 'dtsr-confirmation-text',
41239            confirmationTitle: 'dtsr-confirmation-title',
41240            confirmationTitleRow: 'dtsr-confirmation-title-row',
41241            creation: 'dtsr-creation',
41242            creationButton: 'dtsr-creation-button',
41243            creationForm: 'dtsr-creation-form',
41244            creationText: 'dtsr-creation-text',
41245            creationTitle: 'dtsr-creation-title',
41246            dtButton: 'dt-button',
41247            emptyStates: 'dtsr-emptyStates',
41248            formRow: 'dtsr-form-row',
41249            leftSide: 'dtsr-left',
41250            lengthToggle: 'dtsr-length-toggle',
41251            modalError: 'dtsr-modal-error',
41252            modalFoot: 'dtsr-modal-foot',
41253            nameInput: 'dtsr-name-input',
41254            nameLabel: 'dtsr-name-label',
41255            orderToggle: 'dtsr-order-toggle',
41256            pagingToggle: 'dtsr-paging-toggle',
41257            rightSide: 'dtsr-right',
41258            scrollerToggle: 'dtsr-scroller-toggle',
41259            searchBuilderToggle: 'dtsr-searchBuilder-toggle',
41260            searchPanesToggle: 'dtsr-searchPanes-toggle',
41261            searchToggle: 'dtsr-search-toggle',
41262            selectToggle: 'dtsr-select-toggle',
41263            toggleLabel: 'dtsr-toggle-title'
41264        };
41265        StateRestoreCollection.defaults = {
41266            _createInSaved: false,
41267            ajax: false,
41268            create: true,
41269            creationModal: false,
41270            i18n: {
41271                creationModal: {
41272                    button: 'Create',
41273                    colReorder: 'Column Order',
41274                    columns: {
41275                        search: 'Column Search',
41276                        visible: 'Column Visibility'
41277                    },
41278                    length: 'Page Length',
41279                    name: 'Name:',
41280                    order: 'Sorting',
41281                    paging: 'Paging',
41282                    scroller: 'Scroll Position',
41283                    search: 'Search',
41284                    searchBuilder: 'SearchBuilder',
41285                    searchPanes: 'SearchPanes',
41286                    select: 'Select',
41287                    title: 'Create New State',
41288                    toggleLabel: 'Includes:'
41289                },
41290                duplicateError: 'A state with this name already exists.',
41291                emptyError: 'Name cannot be empty.',
41292                emptyStates: 'No saved states',
41293                removeConfirm: 'Are you sure you want to remove %s?',
41294                removeError: 'Failed to remove state.',
41295                removeJoiner: ' and ',
41296                removeSubmit: 'Remove',
41297                removeTitle: 'Remove State',
41298                renameButton: 'Rename',
41299                renameLabel: 'New Name for %s:',
41300                renameTitle: 'Rename State'
41301            },
41302            modalCloseButton: true,
41303            preDefined: {},
41304            remove: true,
41305            rename: true,
41306            save: true,
41307            saveState: {
41308                colReorder: true,
41309                columns: {
41310                    search: true,
41311                    visible: true
41312                },
41313                length: true,
41314                order: true,
41315                paging: true,
41316                scroller: true,
41317                search: true,
41318                searchBuilder: true,
41319                searchPanes: true,
41320                select: true
41321            },
41322            splitSecondaries: [
41323                'updateState',
41324                'renameState',
41325                'removeState'
41326            ],
41327            toggle: {
41328                colReorder: false,
41329                columns: {
41330                    search: false,
41331                    visible: false
41332                },
41333                length: false,
41334                order: false,
41335                paging: false,
41336                scroller: false,
41337                search: false,
41338                searchBuilder: false,
41339                searchPanes: false,
41340                select: false
41341            }
41342        };
41343        return StateRestoreCollection;
41344    }());
41345
41346    /*! StateRestore 1.2.2
41347     * © SpryMedia Ltd - datatables.net/license
41348     */
41349    setJQuery$1($);
41350    setJQuery($);
41351    $.fn.dataTable.StateRestore = StateRestore;
41352    $.fn.DataTable.StateRestore = StateRestore;
41353    $.fn.dataTable.StateRestoreCollection = StateRestoreCollection;
41354    $.fn.DataTable.StateRestoreCollection = StateRestoreCollection;
41355    var apiRegister = DataTable.Api.register;
41356    apiRegister('stateRestore()', function () {
41357        return this;
41358    });
41359    apiRegister('stateRestore.state()', function (identifier) {
41360        var ctx = this.context[0];
41361        if (!ctx._stateRestore) {
41362            var api = DataTable.Api(ctx);
41363            var src = new DataTable.StateRestoreCollection(api, {});
41364            _stateRegen(api, src);
41365        }
41366        this[0] = ctx._stateRestore.getState(identifier);
41367        return this;
41368    });
41369    apiRegister('stateRestore.state.add()', function (identifier, options) {
41370        var ctx = this.context[0];
41371        if (!ctx._stateRestore) {
41372            var api = DataTable.Api(ctx);
41373            var src = new DataTable.StateRestoreCollection(api, {});
41374            _stateRegen(api, src);
41375        }
41376        if (!ctx._stateRestore.c.create) {
41377            return this;
41378        }
41379        if (ctx._stateRestore.addState) {
41380            var states = ctx._stateRestore.s.states;
41381            var ids = [];
41382            for (var _i = 0, states_1 = states; _i < states_1.length; _i++) {
41383                var intState = states_1[_i];
41384                ids.push(intState.s.identifier);
41385            }
41386            ctx._stateRestore.addState(identifier, ids, options);
41387            return this;
41388        }
41389    });
41390    apiRegister('stateRestore.states()', function (ids) {
41391        var ctx = this.context[0];
41392        if (!ctx._stateRestore) {
41393            var api = DataTable.Api(ctx);
41394            var src = new DataTable.StateRestoreCollection(api, {});
41395            _stateRegen(api, src);
41396        }
41397        this.length = 0;
41398        this.push.apply(this, ctx._stateRestore.getStates(ids));
41399        return this;
41400    });
41401    apiRegister('stateRestore.state().save()', function () {
41402        var ctx = this[0];
41403        // Check if saving states is allowed
41404        if (ctx.c.save) {
41405            ctx.save();
41406        }
41407        return this;
41408    });
41409    apiRegister('stateRestore.state().rename()', function (newIdentifier) {
41410        var ctx = this.context[0];
41411        var state = this[0];
41412        // Check if renaming states is allowed
41413        if (state.c.save) {
41414            var states = ctx._stateRestore.s.states;
41415            var ids = [];
41416            for (var _i = 0, states_2 = states; _i < states_2.length; _i++) {
41417                var intState = states_2[_i];
41418                ids.push(intState.s.identifier);
41419            }
41420            state.rename(newIdentifier, ids);
41421        }
41422        return this;
41423    });
41424    apiRegister('stateRestore.state().load()', function () {
41425        var ctx = this[0];
41426        ctx.load();
41427        return this;
41428    });
41429    apiRegister('stateRestore.state().remove()', function (skipModal) {
41430        var ctx = this[0];
41431        // Check if removal of states is allowed
41432        if (ctx.c.remove) {
41433            ctx.remove(skipModal);
41434        }
41435        return this;
41436    });
41437    apiRegister('stateRestore.states().remove()', function (skipModal) {
41438        var _this = this;
41439        var removeAllCallBack = function (skipModalIn) {
41440            var success = true;
41441            var that = _this.toArray();
41442            while (that.length > 0) {
41443                var set = that[0];
41444                if (set !== undefined && set.c.remove) {
41445                    var tempSuccess = set.remove(skipModalIn);
41446                    if (tempSuccess !== true) {
41447                        success = tempSuccess;
41448                    }
41449                    else {
41450                        that.splice(0, 1);
41451                    }
41452                }
41453                else {
41454                    break;
41455                }
41456            }
41457            return success;
41458        };
41459        if (this.context[0]._stateRestore && this.context[0]._stateRestore.c.remove) {
41460            if (skipModal) {
41461                removeAllCallBack(skipModal);
41462            }
41463            else {
41464                this.context[0]._stateRestore.removeAll(removeAllCallBack);
41465            }
41466        }
41467        return this;
41468    });
41469    apiRegister('stateRestore.activeStates()', function () {
41470        var ctx = this.context[0];
41471        this.length = 0;
41472        if (!ctx._stateRestore) {
41473            var api = DataTable.Api(ctx);
41474            var src = new DataTable.StateRestoreCollection(api, {});
41475            _stateRegen(api, src);
41476        }
41477        if (ctx._stateRestore) {
41478            this.push.apply(this, ctx._stateRestore.findActive());
41479        }
41480        return this;
41481    });
41482    DataTable.ext.buttons.stateRestore = {
41483        action: function (e, dt, node, config) {
41484            config._stateRestore.load();
41485            node.blur();
41486        },
41487        config: {
41488            split: ['updateState', 'renameState', 'removeState']
41489        },
41490        text: function (dt) {
41491            return dt.i18n('buttons.stateRestore', 'State %d', dt.stateRestore.states()[0].length + 1);
41492        }
41493    };
41494    DataTable.ext.buttons.updateState = {
41495        action: function (e, dt, node, config) {
41496            $('div.dt-button-background').click();
41497            config.parent._stateRestore.save();
41498        },
41499        text: function (dt) {
41500            return dt.i18n('buttons.updateState', 'Update');
41501        }
41502    };
41503    DataTable.ext.buttons.savedStates = {
41504        buttons: [],
41505        extend: 'collection',
41506        init: function (dt, node, config) {
41507            dt.on('stateRestore-change', function () {
41508                dt.button(node).text(dt.i18n('buttons.savedStates', 'Saved States', dt.stateRestore.states().length));
41509            });
41510            if (dt.settings()[0]._stateRestore === undefined) {
41511                _buttonInit(dt, config);
41512            }
41513        },
41514        name: 'SaveStateRestore',
41515        text: function (dt) {
41516            return dt.i18n('buttons.savedStates', 'Saved States', 0);
41517        }
41518    };
41519    DataTable.ext.buttons.savedStatesCreate = {
41520        buttons: [],
41521        extend: 'collection',
41522        init: function (dt, node, config) {
41523            dt.on('stateRestore-change', function () {
41524                dt.button(node).text(dt.i18n('buttons.savedStates', 'Saved States', dt.stateRestore.states().length));
41525            });
41526            if (dt.settings()[0]._stateRestore === undefined) {
41527                if (config.config === undefined) {
41528                    config.config = {};
41529                }
41530                config.config._createInSaved = true;
41531                _buttonInit(dt, config);
41532            }
41533        },
41534        name: 'SaveStateRestore',
41535        text: function (dt) {
41536            return dt.i18n('buttons.savedStates', 'Saved States', 0);
41537        }
41538    };
41539    DataTable.ext.buttons.createState = {
41540        action: function (e, dt, node, config) {
41541            e.stopPropagation();
41542            var stateRestoreOpts = dt.settings()[0]._stateRestore.c;
41543            var language = dt.settings()[0].oLanguage;
41544            // If creation/saving is not allowed then return
41545            if (!stateRestoreOpts.create || !stateRestoreOpts.save) {
41546                return;
41547            }
41548            var prevStates = dt.stateRestore.states().toArray();
41549            // Create a replacement regex based on the i18n values
41550            var defaultString = language.buttons !== undefined && language.buttons.stateRestore !== undefined ?
41551                language.buttons.stateRestore :
41552                'State ';
41553            var replaceRegex;
41554            if (defaultString.indexOf('%d') === defaultString.length - 3) {
41555                replaceRegex = new RegExp(defaultString.replace(/%d/g, ''));
41556            }
41557            else {
41558                var splitString = defaultString.split('%d');
41559                replaceRegex = [];
41560                for (var _i = 0, splitString_1 = splitString; _i < splitString_1.length; _i++) {
41561                    var split = splitString_1[_i];
41562                    replaceRegex.push(new RegExp(split));
41563                }
41564            }
41565            var getId = function (identifier) {
41566                var id;
41567                if (Array.isArray(replaceRegex)) {
41568                    id = identifier;
41569                    for (var _i = 0, replaceRegex_1 = replaceRegex; _i < replaceRegex_1.length; _i++) {
41570                        var reg = replaceRegex_1[_i];
41571                        id = id.replace(reg, '');
41572                    }
41573                }
41574                else {
41575                    id = identifier.replace(replaceRegex, '');
41576                }
41577                // If the id after replacement is not a number, or the length is the same as before,
41578                //  it has been customised so return 0
41579                if (isNaN(+id) || id.length === identifier) {
41580                    return 0;
41581                }
41582                // Otherwise return the number that has been assigned previously
41583                else {
41584                    return +id;
41585                }
41586            };
41587            // Extract the numbers from the identifiers that use the standard naming convention
41588            var identifiers = prevStates
41589                .map(function (state) { return getId(state.s.identifier); })
41590                .sort(function (a, b) { return +a < +b ?
41591                1 :
41592                +a > +b ?
41593                    -1 :
41594                    0; });
41595            var lastNumber = identifiers[0];
41596            dt.stateRestore.state.add(dt.i18n('buttons.stateRestore', 'State %d', lastNumber !== undefined ? lastNumber + 1 : 1), config.config);
41597            var states = dt.stateRestore.states().sort(function (a, b) {
41598                var aId = +getId(a.s.identifier);
41599                var bId = +getId(b.s.identifier);
41600                return aId > bId ?
41601                    1 :
41602                    aId < bId ?
41603                        -1 :
41604                        0;
41605            });
41606            var button = dt.button('SaveStateRestore:name');
41607            var stateButtons = button[0] !== undefined && button[0].inst.c.buttons[0].buttons !== undefined ?
41608                button[0].inst.c.buttons[0].buttons :
41609                [];
41610            // remove any states from the previous rebuild - if they are still there they will be added later
41611            for (var i = 0; i < stateButtons.length; i++) {
41612                if (stateButtons[i].extend === 'stateRestore') {
41613                    stateButtons.splice(i, 1);
41614                    i--;
41615                }
41616            }
41617            if (stateRestoreOpts._createInSaved) {
41618                stateButtons.push('createState');
41619                stateButtons.push('');
41620            }
41621            for (var _a = 0, states_3 = states; _a < states_3.length; _a++) {
41622                var state = states_3[_a];
41623                var split = Object.assign([], stateRestoreOpts.splitSecondaries);
41624                if (split.includes('updateState') && !stateRestoreOpts.save) {
41625                    split.splice(split.indexOf('updateState'), 1);
41626                }
41627                if (split.includes('renameState') &&
41628                    (!stateRestoreOpts.save || !stateRestoreOpts.rename)) {
41629                    split.splice(split.indexOf('renameState'), 1);
41630                }
41631                if (split.includes('removeState') && !stateRestoreOpts.remove) {
41632                    split.splice(split.indexOf('removeState'), 1);
41633                }
41634                if (split.length > 0 &&
41635                    !split.includes('<h3>' + state.s.identifier + '</h3>')) {
41636                    split.unshift('<h3>' + state.s.identifier + '</h3>');
41637                }
41638                stateButtons.push({
41639                    _stateRestore: state,
41640                    attr: {
41641                        title: state.s.identifier
41642                    },
41643                    config: {
41644                        split: split
41645                    },
41646                    extend: 'stateRestore',
41647                    text: state.s.identifier
41648                });
41649            }
41650            dt.button('SaveStateRestore:name').collectionRebuild(stateButtons);
41651            node.blur();
41652            // Need to disable the removeAllStates button if there are no states and it is present
41653            var buttons = dt.buttons();
41654            for (var _b = 0, buttons_1 = buttons; _b < buttons_1.length; _b++) {
41655                var butt = buttons_1[_b];
41656                if ($(butt.node).hasClass('dtsr-removeAllStates')) {
41657                    if (states.length === 0) {
41658                        dt.button(butt.node).disable();
41659                    }
41660                    else {
41661                        dt.button(butt.node).enable();
41662                    }
41663                }
41664            }
41665        },
41666        init: function (dt, node, config) {
41667            if (dt.settings()[0]._stateRestore === undefined && dt.button('SaveStateRestore:name').length > 1) {
41668                _buttonInit(dt, config);
41669            }
41670        },
41671        text: function (dt) {
41672            return dt.i18n('buttons.createState', 'Create State');
41673        }
41674    };
41675    DataTable.ext.buttons.removeState = {
41676        action: function (e, dt, node, config) {
41677            config.parent._stateRestore.remove();
41678            node.blur();
41679        },
41680        text: function (dt) {
41681            return dt.i18n('buttons.removeState', 'Remove');
41682        }
41683    };
41684    DataTable.ext.buttons.removeAllStates = {
41685        action: function (e, dt, node) {
41686            dt.stateRestore.states().remove(true);
41687            node.blur();
41688        },
41689        className: 'dt-button dtsr-removeAllStates',
41690        init: function (dt, node) {
41691            if (!dt.settings()[0]._stateRestore || dt.stateRestore.states().length === 0) {
41692                $(node).addClass('disabled');
41693            }
41694        },
41695        text: function (dt) {
41696            return dt.i18n('buttons.removeAllStates', 'Remove All States');
41697        }
41698    };
41699    DataTable.ext.buttons.renameState = {
41700        action: function (e, dt, node, config) {
41701            var states = dt.settings()[0]._stateRestore.s.states;
41702            var ids = [];
41703            for (var _i = 0, states_4 = states; _i < states_4.length; _i++) {
41704                var state = states_4[_i];
41705                ids.push(state.s.identifier);
41706            }
41707            config.parent._stateRestore.rename(undefined, ids);
41708            node.blur();
41709        },
41710        text: function (dt) {
41711            return dt.i18n('buttons.renameState', 'Rename');
41712        }
41713    };
41714    function _init(settings, options) {
41715        if (options === void 0) { options = null; }
41716        var api = new DataTable.Api(settings);
41717        var opts = options
41718            ? options
41719            : api.init().stateRestore || DataTable.defaults.stateRestore;
41720        var stateRestore = new StateRestoreCollection(api, opts);
41721        _stateRegen(api, stateRestore);
41722        return stateRestore;
41723    }
41724    /**
41725     * Initialisation function if initialising using a button
41726     *
41727     * @param dt The datatables instance
41728     * @param config the config for the button
41729     */
41730    function _buttonInit(dt, config) {
41731        var SRC = new DataTable.StateRestoreCollection(dt, config.config);
41732        _stateRegen(dt, SRC);
41733    }
41734    function _stateRegen(dt, src) {
41735        var states = dt.stateRestore.states();
41736        var button = dt.button('SaveStateRestore:name');
41737        var stateButtons = [];
41738        // Need to get the original configuration object, so we can rebuild it
41739        // It might be nested, so need to traverse down the tree
41740        if (button[0]) {
41741            var idxs = button.index().split('-');
41742            stateButtons = button[0].inst.c.buttons;
41743            for (var i = 0; i < idxs.length; i++) {
41744                if (stateButtons[idxs[i]].buttons) {
41745                    stateButtons = stateButtons[idxs[i]].buttons;
41746                }
41747                else {
41748                    stateButtons = [];
41749                    break;
41750                }
41751            }
41752        }
41753        var stateRestoreOpts = dt.settings()[0]._stateRestore.c;
41754        // remove any states from the previous rebuild - if they are still there they will be added later
41755        for (var i = 0; i < stateButtons.length; i++) {
41756            if (stateButtons[i].extend === 'stateRestore') {
41757                stateButtons.splice(i, 1);
41758                i--;
41759            }
41760        }
41761        if (stateRestoreOpts._createInSaved) {
41762            stateButtons.push('createState');
41763        }
41764        if (states === undefined || states.length === 0) {
41765            stateButtons.push('<span class="' + src.classes.emptyStates + '">' +
41766                dt.i18n('stateRestore.emptyStates', src.c.i18n.emptyStates) +
41767                '</span>');
41768        }
41769        else {
41770            for (var _i = 0, states_5 = states; _i < states_5.length; _i++) {
41771                var state = states_5[_i];
41772                var split = Object.assign([], stateRestoreOpts.splitSecondaries);
41773                if (split.includes('updateState') && !stateRestoreOpts.save) {
41774                    split.splice(split.indexOf('updateState'), 1);
41775                }
41776                if (split.includes('renameState') &&
41777                    (!stateRestoreOpts.save || !stateRestoreOpts.rename)) {
41778                    split.splice(split.indexOf('renameState'), 1);
41779                }
41780                if (split.includes('removeState') && !stateRestoreOpts.remove) {
41781                    split.splice(split.indexOf('removeState'), 1);
41782                }
41783                if (split.length > 0 &&
41784                    !split.includes('<h3>' + state.s.identifier + '</h3>')) {
41785                    split.unshift('<h3>' + state.s.identifier + '</h3>');
41786                }
41787                stateButtons.push({
41788                    _stateRestore: state,
41789                    attr: {
41790                        title: state.s.identifier
41791                    },
41792                    config: {
41793                        split: split
41794                    },
41795                    extend: 'stateRestore',
41796                    text: state.s.identifier
41797                });
41798            }
41799        }
41800        dt.button('SaveStateRestore:name').collectionRebuild(stateButtons);
41801        // Need to disable the removeAllStates button if there are no states and it is present
41802        var buttons = dt.buttons();
41803        for (var _a = 0, buttons_2 = buttons; _a < buttons_2.length; _a++) {
41804            var butt = buttons_2[_a];
41805            if ($(butt.node).hasClass('dtsr-removeAllStates')) {
41806                if (states.length === 0) {
41807                    dt.button(butt.node).disable();
41808                }
41809                else {
41810                    dt.button(butt.node).enable();
41811                }
41812            }
41813        }
41814    }
41815    // Attach a listener to the document which listens for DataTables initialisation
41816    // events so we can automatically initialise
41817    $(document).on('preInit.dt.dtsr', function (e, settings) {
41818        if (e.namespace !== 'dt') {
41819            return;
41820        }
41821        if (settings.oInit.stateRestore ||
41822            DataTable.defaults.stateRestore) {
41823            if (!settings._stateRestore) {
41824                _init(settings, null);
41825            }
41826        }
41827    });
41828
41829})();
41830
41831
41832return DataTable;
41833}));
41834
41835
41836/*! Bootstrap integration for DataTables' StateRestore
41837 * © SpryMedia Ltd - datatables.net/license
41838 */
41839
41840(function( factory ){
41841	if ( typeof define === 'function' && define.amd ) {
41842		// AMD
41843		define( ['jquery', 'datatables.net-dt', 'datatables.net-staterestore'], function ( $ ) {
41844			return factory( $, window, document );
41845		} );
41846	}
41847	else if ( typeof exports === 'object' ) {
41848		// CommonJS
41849		var jq = require('jquery');
41850		var cjsRequires = function (root, $) {
41851			if ( ! $.fn.dataTable ) {
41852				require('datatables.net-dt')(root, $);
41853			}
41854
41855			if ( ! $.fn.dataTable.StateRestore ) {
41856				require('datatables.net-staterestore')(root, $);
41857			}
41858		};
41859
41860		if (typeof window !== 'undefined') {
41861			module.exports = function (root, $) {
41862				if ( ! root ) {
41863					// CommonJS environments without a window global must pass a
41864					// root. This will give an error otherwise
41865					root = window;
41866				}
41867
41868				if ( ! $ ) {
41869					$ = jq( root );
41870				}
41871
41872				cjsRequires( root, $ );
41873				return factory( $, root, root.document );
41874			};
41875		}
41876		else {
41877			cjsRequires( window, jq );
41878			module.exports = factory( jq, window, window.document );
41879		}
41880	}
41881	else {
41882		// Browser
41883		factory( jQuery, window, document );
41884	}
41885}(function( $, window, document, undefined ) {
41886'use strict';
41887var DataTable = $.fn.dataTable;
41888
41889
41890
41891
41892return DataTable;
41893}));
41894
41895

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.