blob: 8c791713f2ee2b6a198e1221ac7ccf0079bcc625 [file] [log] [blame]
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +01001/*
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/#bs5/dt-1.13.1/r-2.4.0/sl-1.5.0
8 *
9 * Included libraries:
10 * DataTables 1.13.1, Responsive 2.4.0, Select 1.5.0
11 */
12
13/*! DataTables 1.13.1
14 * ©2008-2022 SpryMedia Ltd - datatables.net/license
15 */
16
17/**
18 * @summary DataTables
19 * @description Paginate, search and order HTML tables
20 * @version 1.13.1
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 module.exports = function (root, $) {
50 if ( ! root ) {
51 // CommonJS environments without a window global must pass a
52 // root. This will give an error otherwise
53 root = window;
54 }
55
56 if ( ! $ ) {
57 $ = typeof window !== 'undefined' ? // jQuery's factory checks for a global window
58 require('jquery') :
59 require('jquery')( root );
60 }
61
62 return factory( $, root, root.document );
63 };
64 }
65 else {
66 // Browser
67 window.DataTable = factory( jQuery, window, document );
68 }
69}
70(function( $, window, document, undefined ) {
71 "use strict";
72
73
74 var DataTable = function ( selector, options )
75 {
76 // When creating with `new`, create a new DataTable, returning the API instance
77 if (this instanceof DataTable) {
78 return $(selector).DataTable(options);
79 }
80 else {
81 // Argument switching
82 options = selector;
83 }
84
85 /**
86 * Perform a jQuery selector action on the table's TR elements (from the tbody) and
87 * return the resulting jQuery object.
88 * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on
89 * @param {object} [oOpts] Optional parameters for modifying the rows to be included
90 * @param {string} [oOpts.filter=none] Select TR elements that meet the current filter
91 * criterion ("applied") or all TR elements (i.e. no filter).
92 * @param {string} [oOpts.order=current] Order of the TR elements in the processed array.
93 * Can be either 'current', whereby the current sorting of the table is used, or
94 * 'original' whereby the original order the data was read into the table is used.
95 * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page
96 * ("current") or not ("all"). If 'current' is given, then order is assumed to be
97 * 'current' and filter is 'applied', regardless of what they might be given as.
98 * @returns {object} jQuery object, filtered by the given selector.
99 * @dtopt API
100 * @deprecated Since v1.10
101 *
102 * @example
103 * $(document).ready(function() {
104 * var oTable = $('#example').dataTable();
105 *
106 * // Highlight every second row
107 * oTable.$('tr:odd').css('backgroundColor', 'blue');
108 * } );
109 *
110 * @example
111 * $(document).ready(function() {
112 * var oTable = $('#example').dataTable();
113 *
114 * // Filter to rows with 'Webkit' in them, add a background colour and then
115 * // remove the filter, thus highlighting the 'Webkit' rows only.
116 * oTable.fnFilter('Webkit');
117 * oTable.$('tr', {"search": "applied"}).css('backgroundColor', 'blue');
118 * oTable.fnFilter('');
119 * } );
120 */
121 this.$ = function ( sSelector, oOpts )
122 {
123 return this.api(true).$( sSelector, oOpts );
124 };
125
126
127 /**
128 * Almost identical to $ in operation, but in this case returns the data for the matched
129 * rows - as such, the jQuery selector used should match TR row nodes or TD/TH cell nodes
130 * rather than any descendants, so the data can be obtained for the row/cell. If matching
131 * rows are found, the data returned is the original data array/object that was used to
132 * create the row (or a generated array if from a DOM source).
133 *
134 * This method is often useful in-combination with $ where both functions are given the
135 * same parameters and the array indexes will match identically.
136 * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on
137 * @param {object} [oOpts] Optional parameters for modifying the rows to be included
138 * @param {string} [oOpts.filter=none] Select elements that meet the current filter
139 * criterion ("applied") or all elements (i.e. no filter).
140 * @param {string} [oOpts.order=current] Order of the data in the processed array.
141 * Can be either 'current', whereby the current sorting of the table is used, or
142 * 'original' whereby the original order the data was read into the table is used.
143 * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page
144 * ("current") or not ("all"). If 'current' is given, then order is assumed to be
145 * 'current' and filter is 'applied', regardless of what they might be given as.
146 * @returns {array} Data for the matched elements. If any elements, as a result of the
147 * selector, were not TR, TD or TH elements in the DataTable, they will have a null
148 * entry in the array.
149 * @dtopt API
150 * @deprecated Since v1.10
151 *
152 * @example
153 * $(document).ready(function() {
154 * var oTable = $('#example').dataTable();
155 *
156 * // Get the data from the first row in the table
157 * var data = oTable._('tr:first');
158 *
159 * // Do something useful with the data
160 * alert( "First cell is: "+data[0] );
161 * } );
162 *
163 * @example
164 * $(document).ready(function() {
165 * var oTable = $('#example').dataTable();
166 *
167 * // Filter to 'Webkit' and get all data for
168 * oTable.fnFilter('Webkit');
169 * var data = oTable._('tr', {"search": "applied"});
170 *
171 * // Do something with the data
172 * alert( data.length+" rows matched the search" );
173 * } );
174 */
175 this._ = function ( sSelector, oOpts )
176 {
177 return this.api(true).rows( sSelector, oOpts ).data();
178 };
179
180
181 /**
182 * Create a DataTables Api instance, with the currently selected tables for
183 * the Api's context.
184 * @param {boolean} [traditional=false] Set the API instance's context to be
185 * only the table referred to by the `DataTable.ext.iApiIndex` option, as was
186 * used in the API presented by DataTables 1.9- (i.e. the traditional mode),
187 * or if all tables captured in the jQuery object should be used.
188 * @return {DataTables.Api}
189 */
190 this.api = function ( traditional )
191 {
192 return traditional ?
193 new _Api(
194 _fnSettingsFromNode( this[ _ext.iApiIndex ] )
195 ) :
196 new _Api( this );
197 };
198
199
200 /**
201 * Add a single new row or multiple rows of data to the table. Please note
202 * that this is suitable for client-side processing only - if you are using
203 * server-side processing (i.e. "bServerSide": true), then to add data, you
204 * must add it to the data source, i.e. the server-side, through an Ajax call.
205 * @param {array|object} data The data to be added to the table. This can be:
206 * <ul>
207 * <li>1D array of data - add a single row with the data provided</li>
208 * <li>2D array of arrays - add multiple rows in a single call</li>
209 * <li>object - data object when using <i>mData</i></li>
210 * <li>array of objects - multiple data objects when using <i>mData</i></li>
211 * </ul>
212 * @param {bool} [redraw=true] redraw the table or not
213 * @returns {array} An array of integers, representing the list of indexes in
214 * <i>aoData</i> ({@link DataTable.models.oSettings}) that have been added to
215 * the table.
216 * @dtopt API
217 * @deprecated Since v1.10
218 *
219 * @example
220 * // Global var for counter
221 * var giCount = 2;
222 *
223 * $(document).ready(function() {
224 * $('#example').dataTable();
225 * } );
226 *
227 * function fnClickAddRow() {
228 * $('#example').dataTable().fnAddData( [
229 * giCount+".1",
230 * giCount+".2",
231 * giCount+".3",
232 * giCount+".4" ]
233 * );
234 *
235 * giCount++;
236 * }
237 */
238 this.fnAddData = function( data, redraw )
239 {
240 var api = this.api( true );
241
242 /* Check if we want to add multiple rows or not */
243 var rows = Array.isArray(data) && ( Array.isArray(data[0]) || $.isPlainObject(data[0]) ) ?
244 api.rows.add( data ) :
245 api.row.add( data );
246
247 if ( redraw === undefined || redraw ) {
248 api.draw();
249 }
250
251 return rows.flatten().toArray();
252 };
253
254
255 /**
256 * This function will make DataTables recalculate the column sizes, based on the data
257 * contained in the table and the sizes applied to the columns (in the DOM, CSS or
258 * through the sWidth parameter). This can be useful when the width of the table's
259 * parent element changes (for example a window resize).
260 * @param {boolean} [bRedraw=true] Redraw the table or not, you will typically want to
261 * @dtopt API
262 * @deprecated Since v1.10
263 *
264 * @example
265 * $(document).ready(function() {
266 * var oTable = $('#example').dataTable( {
267 * "sScrollY": "200px",
268 * "bPaginate": false
269 * } );
270 *
271 * $(window).on('resize', function () {
272 * oTable.fnAdjustColumnSizing();
273 * } );
274 * } );
275 */
276 this.fnAdjustColumnSizing = function ( bRedraw )
277 {
278 var api = this.api( true ).columns.adjust();
279 var settings = api.settings()[0];
280 var scroll = settings.oScroll;
281
282 if ( bRedraw === undefined || bRedraw ) {
283 api.draw( false );
284 }
285 else if ( scroll.sX !== "" || scroll.sY !== "" ) {
286 /* If not redrawing, but scrolling, we want to apply the new column sizes anyway */
287 _fnScrollDraw( settings );
288 }
289 };
290
291
292 /**
293 * Quickly and simply clear a table
294 * @param {bool} [bRedraw=true] redraw the table or not
295 * @dtopt API
296 * @deprecated Since v1.10
297 *
298 * @example
299 * $(document).ready(function() {
300 * var oTable = $('#example').dataTable();
301 *
302 * // Immediately 'nuke' the current rows (perhaps waiting for an Ajax callback...)
303 * oTable.fnClearTable();
304 * } );
305 */
306 this.fnClearTable = function( bRedraw )
307 {
308 var api = this.api( true ).clear();
309
310 if ( bRedraw === undefined || bRedraw ) {
311 api.draw();
312 }
313 };
314
315
316 /**
317 * The exact opposite of 'opening' a row, this function will close any rows which
318 * are currently 'open'.
319 * @param {node} nTr the table row to 'close'
320 * @returns {int} 0 on success, or 1 if failed (can't find the row)
321 * @dtopt API
322 * @deprecated Since v1.10
323 *
324 * @example
325 * $(document).ready(function() {
326 * var oTable;
327 *
328 * // 'open' an information row when a row is clicked on
329 * $('#example tbody tr').click( function () {
330 * if ( oTable.fnIsOpen(this) ) {
331 * oTable.fnClose( this );
332 * } else {
333 * oTable.fnOpen( this, "Temporary row opened", "info_row" );
334 * }
335 * } );
336 *
337 * oTable = $('#example').dataTable();
338 * } );
339 */
340 this.fnClose = function( nTr )
341 {
342 this.api( true ).row( nTr ).child.hide();
343 };
344
345
346 /**
347 * Remove a row for the table
348 * @param {mixed} target The index of the row from aoData to be deleted, or
349 * the TR element you want to delete
350 * @param {function|null} [callBack] Callback function
351 * @param {bool} [redraw=true] Redraw the table or not
352 * @returns {array} The row that was deleted
353 * @dtopt API
354 * @deprecated Since v1.10
355 *
356 * @example
357 * $(document).ready(function() {
358 * var oTable = $('#example').dataTable();
359 *
360 * // Immediately remove the first row
361 * oTable.fnDeleteRow( 0 );
362 * } );
363 */
364 this.fnDeleteRow = function( target, callback, redraw )
365 {
366 var api = this.api( true );
367 var rows = api.rows( target );
368 var settings = rows.settings()[0];
369 var data = settings.aoData[ rows[0][0] ];
370
371 rows.remove();
372
373 if ( callback ) {
374 callback.call( this, settings, data );
375 }
376
377 if ( redraw === undefined || redraw ) {
378 api.draw();
379 }
380
381 return data;
382 };
383
384
385 /**
386 * Restore the table to it's original state in the DOM by removing all of DataTables
387 * enhancements, alterations to the DOM structure of the table and event listeners.
388 * @param {boolean} [remove=false] Completely remove the table from the DOM
389 * @dtopt API
390 * @deprecated Since v1.10
391 *
392 * @example
393 * $(document).ready(function() {
394 * // This example is fairly pointless in reality, but shows how fnDestroy can be used
395 * var oTable = $('#example').dataTable();
396 * oTable.fnDestroy();
397 * } );
398 */
399 this.fnDestroy = function ( remove )
400 {
401 this.api( true ).destroy( remove );
402 };
403
404
405 /**
406 * Redraw the table
407 * @param {bool} [complete=true] Re-filter and resort (if enabled) the table before the draw.
408 * @dtopt API
409 * @deprecated Since v1.10
410 *
411 * @example
412 * $(document).ready(function() {
413 * var oTable = $('#example').dataTable();
414 *
415 * // Re-draw the table - you wouldn't want to do it here, but it's an example :-)
416 * oTable.fnDraw();
417 * } );
418 */
419 this.fnDraw = function( complete )
420 {
421 // Note that this isn't an exact match to the old call to _fnDraw - it takes
422 // into account the new data, but can hold position.
423 this.api( true ).draw( complete );
424 };
425
426
427 /**
428 * Filter the input based on data
429 * @param {string} sInput String to filter the table on
430 * @param {int|null} [iColumn] Column to limit filtering to
431 * @param {bool} [bRegex=false] Treat as regular expression or not
432 * @param {bool} [bSmart=true] Perform smart filtering or not
433 * @param {bool} [bShowGlobal=true] Show the input global filter in it's input box(es)
434 * @param {bool} [bCaseInsensitive=true] Do case-insensitive matching (true) or not (false)
435 * @dtopt API
436 * @deprecated Since v1.10
437 *
438 * @example
439 * $(document).ready(function() {
440 * var oTable = $('#example').dataTable();
441 *
442 * // Sometime later - filter...
443 * oTable.fnFilter( 'test string' );
444 * } );
445 */
446 this.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal, bCaseInsensitive )
447 {
448 var api = this.api( true );
449
450 if ( iColumn === null || iColumn === undefined ) {
451 api.search( sInput, bRegex, bSmart, bCaseInsensitive );
452 }
453 else {
454 api.column( iColumn ).search( sInput, bRegex, bSmart, bCaseInsensitive );
455 }
456
457 api.draw();
458 };
459
460
461 /**
462 * Get the data for the whole table, an individual row or an individual cell based on the
463 * provided parameters.
464 * @param {int|node} [src] A TR row node, TD/TH cell node or an integer. If given as
465 * a TR node then the data source for the whole row will be returned. If given as a
466 * TD/TH cell node then iCol will be automatically calculated and the data for the
467 * cell returned. If given as an integer, then this is treated as the aoData internal
468 * data index for the row (see fnGetPosition) and the data for that row used.
469 * @param {int} [col] Optional column index that you want the data of.
470 * @returns {array|object|string} If mRow is undefined, then the data for all rows is
471 * returned. If mRow is defined, just data for that row, and is iCol is
472 * defined, only data for the designated cell is returned.
473 * @dtopt API
474 * @deprecated Since v1.10
475 *
476 * @example
477 * // Row data
478 * $(document).ready(function() {
479 * oTable = $('#example').dataTable();
480 *
481 * oTable.$('tr').click( function () {
482 * var data = oTable.fnGetData( this );
483 * // ... do something with the array / object of data for the row
484 * } );
485 * } );
486 *
487 * @example
488 * // Individual cell data
489 * $(document).ready(function() {
490 * oTable = $('#example').dataTable();
491 *
492 * oTable.$('td').click( function () {
493 * var sData = oTable.fnGetData( this );
494 * alert( 'The cell clicked on had the value of '+sData );
495 * } );
496 * } );
497 */
498 this.fnGetData = function( src, col )
499 {
500 var api = this.api( true );
501
502 if ( src !== undefined ) {
503 var type = src.nodeName ? src.nodeName.toLowerCase() : '';
504
505 return col !== undefined || type == 'td' || type == 'th' ?
506 api.cell( src, col ).data() :
507 api.row( src ).data() || null;
508 }
509
510 return api.data().toArray();
511 };
512
513
514 /**
515 * Get an array of the TR nodes that are used in the table's body. Note that you will
516 * typically want to use the '$' API method in preference to this as it is more
517 * flexible.
518 * @param {int} [iRow] Optional row index for the TR element you want
519 * @returns {array|node} If iRow is undefined, returns an array of all TR elements
520 * in the table's body, or iRow is defined, just the TR element requested.
521 * @dtopt API
522 * @deprecated Since v1.10
523 *
524 * @example
525 * $(document).ready(function() {
526 * var oTable = $('#example').dataTable();
527 *
528 * // Get the nodes from the table
529 * var nNodes = oTable.fnGetNodes( );
530 * } );
531 */
532 this.fnGetNodes = function( iRow )
533 {
534 var api = this.api( true );
535
536 return iRow !== undefined ?
537 api.row( iRow ).node() :
538 api.rows().nodes().flatten().toArray();
539 };
540
541
542 /**
543 * Get the array indexes of a particular cell from it's DOM element
544 * and column index including hidden columns
545 * @param {node} node this can either be a TR, TD or TH in the table's body
546 * @returns {int} If nNode is given as a TR, then a single index is returned, or
547 * if given as a cell, an array of [row index, column index (visible),
548 * column index (all)] is given.
549 * @dtopt API
550 * @deprecated Since v1.10
551 *
552 * @example
553 * $(document).ready(function() {
554 * $('#example tbody td').click( function () {
555 * // Get the position of the current data from the node
556 * var aPos = oTable.fnGetPosition( this );
557 *
558 * // Get the data array for this row
559 * var aData = oTable.fnGetData( aPos[0] );
560 *
561 * // Update the data array and return the value
562 * aData[ aPos[1] ] = 'clicked';
563 * this.innerHTML = 'clicked';
564 * } );
565 *
566 * // Init DataTables
567 * oTable = $('#example').dataTable();
568 * } );
569 */
570 this.fnGetPosition = function( node )
571 {
572 var api = this.api( true );
573 var nodeName = node.nodeName.toUpperCase();
574
575 if ( nodeName == 'TR' ) {
576 return api.row( node ).index();
577 }
578 else if ( nodeName == 'TD' || nodeName == 'TH' ) {
579 var cell = api.cell( node ).index();
580
581 return [
582 cell.row,
583 cell.columnVisible,
584 cell.column
585 ];
586 }
587 return null;
588 };
589
590
591 /**
592 * Check to see if a row is 'open' or not.
593 * @param {node} nTr the table row to check
594 * @returns {boolean} true if the row is currently open, false otherwise
595 * @dtopt API
596 * @deprecated Since v1.10
597 *
598 * @example
599 * $(document).ready(function() {
600 * var oTable;
601 *
602 * // 'open' an information row when a row is clicked on
603 * $('#example tbody tr').click( function () {
604 * if ( oTable.fnIsOpen(this) ) {
605 * oTable.fnClose( this );
606 * } else {
607 * oTable.fnOpen( this, "Temporary row opened", "info_row" );
608 * }
609 * } );
610 *
611 * oTable = $('#example').dataTable();
612 * } );
613 */
614 this.fnIsOpen = function( nTr )
615 {
616 return this.api( true ).row( nTr ).child.isShown();
617 };
618
619
620 /**
621 * This function will place a new row directly after a row which is currently
622 * on display on the page, with the HTML contents that is passed into the
623 * function. This can be used, for example, to ask for confirmation that a
624 * particular record should be deleted.
625 * @param {node} nTr The table row to 'open'
626 * @param {string|node|jQuery} mHtml The HTML to put into the row
627 * @param {string} sClass Class to give the new TD cell
628 * @returns {node} The row opened. Note that if the table row passed in as the
629 * first parameter, is not found in the table, this method will silently
630 * return.
631 * @dtopt API
632 * @deprecated Since v1.10
633 *
634 * @example
635 * $(document).ready(function() {
636 * var oTable;
637 *
638 * // 'open' an information row when a row is clicked on
639 * $('#example tbody tr').click( function () {
640 * if ( oTable.fnIsOpen(this) ) {
641 * oTable.fnClose( this );
642 * } else {
643 * oTable.fnOpen( this, "Temporary row opened", "info_row" );
644 * }
645 * } );
646 *
647 * oTable = $('#example').dataTable();
648 * } );
649 */
650 this.fnOpen = function( nTr, mHtml, sClass )
651 {
652 return this.api( true )
653 .row( nTr )
654 .child( mHtml, sClass )
655 .show()
656 .child()[0];
657 };
658
659
660 /**
661 * Change the pagination - provides the internal logic for pagination in a simple API
662 * function. With this function you can have a DataTables table go to the next,
663 * previous, first or last pages.
664 * @param {string|int} mAction Paging action to take: "first", "previous", "next" or "last"
665 * or page number to jump to (integer), note that page 0 is the first page.
666 * @param {bool} [bRedraw=true] Redraw the table or not
667 * @dtopt API
668 * @deprecated Since v1.10
669 *
670 * @example
671 * $(document).ready(function() {
672 * var oTable = $('#example').dataTable();
673 * oTable.fnPageChange( 'next' );
674 * } );
675 */
676 this.fnPageChange = function ( mAction, bRedraw )
677 {
678 var api = this.api( true ).page( mAction );
679
680 if ( bRedraw === undefined || bRedraw ) {
681 api.draw(false);
682 }
683 };
684
685
686 /**
687 * Show a particular column
688 * @param {int} iCol The column whose display should be changed
689 * @param {bool} bShow Show (true) or hide (false) the column
690 * @param {bool} [bRedraw=true] Redraw the table or not
691 * @dtopt API
692 * @deprecated Since v1.10
693 *
694 * @example
695 * $(document).ready(function() {
696 * var oTable = $('#example').dataTable();
697 *
698 * // Hide the second column after initialisation
699 * oTable.fnSetColumnVis( 1, false );
700 * } );
701 */
702 this.fnSetColumnVis = function ( iCol, bShow, bRedraw )
703 {
704 var api = this.api( true ).column( iCol ).visible( bShow );
705
706 if ( bRedraw === undefined || bRedraw ) {
707 api.columns.adjust().draw();
708 }
709 };
710
711
712 /**
713 * Get the settings for a particular table for external manipulation
714 * @returns {object} DataTables settings object. See
715 * {@link DataTable.models.oSettings}
716 * @dtopt API
717 * @deprecated Since v1.10
718 *
719 * @example
720 * $(document).ready(function() {
721 * var oTable = $('#example').dataTable();
722 * var oSettings = oTable.fnSettings();
723 *
724 * // Show an example parameter from the settings
725 * alert( oSettings._iDisplayStart );
726 * } );
727 */
728 this.fnSettings = function()
729 {
730 return _fnSettingsFromNode( this[_ext.iApiIndex] );
731 };
732
733
734 /**
735 * Sort the table by a particular column
736 * @param {int} iCol the data index to sort on. Note that this will not match the
737 * 'display index' if you have hidden data entries
738 * @dtopt API
739 * @deprecated Since v1.10
740 *
741 * @example
742 * $(document).ready(function() {
743 * var oTable = $('#example').dataTable();
744 *
745 * // Sort immediately with columns 0 and 1
746 * oTable.fnSort( [ [0,'asc'], [1,'asc'] ] );
747 * } );
748 */
749 this.fnSort = function( aaSort )
750 {
751 this.api( true ).order( aaSort ).draw();
752 };
753
754
755 /**
756 * Attach a sort listener to an element for a given column
757 * @param {node} nNode the element to attach the sort listener to
758 * @param {int} iColumn the column that a click on this node will sort on
759 * @param {function} [fnCallback] callback function when sort is run
760 * @dtopt API
761 * @deprecated Since v1.10
762 *
763 * @example
764 * $(document).ready(function() {
765 * var oTable = $('#example').dataTable();
766 *
767 * // Sort on column 1, when 'sorter' is clicked on
768 * oTable.fnSortListener( document.getElementById('sorter'), 1 );
769 * } );
770 */
771 this.fnSortListener = function( nNode, iColumn, fnCallback )
772 {
773 this.api( true ).order.listener( nNode, iColumn, fnCallback );
774 };
775
776
777 /**
778 * Update a table cell or row - this method will accept either a single value to
779 * update the cell with, an array of values with one element for each column or
780 * an object in the same format as the original data source. The function is
781 * self-referencing in order to make the multi column updates easier.
782 * @param {object|array|string} mData Data to update the cell/row with
783 * @param {node|int} mRow TR element you want to update or the aoData index
784 * @param {int} [iColumn] The column to update, give as null or undefined to
785 * update a whole row.
786 * @param {bool} [bRedraw=true] Redraw the table or not
787 * @param {bool} [bAction=true] Perform pre-draw actions or not
788 * @returns {int} 0 on success, 1 on error
789 * @dtopt API
790 * @deprecated Since v1.10
791 *
792 * @example
793 * $(document).ready(function() {
794 * var oTable = $('#example').dataTable();
795 * oTable.fnUpdate( 'Example update', 0, 0 ); // Single cell
796 * oTable.fnUpdate( ['a', 'b', 'c', 'd', 'e'], $('tbody tr')[0] ); // Row
797 * } );
798 */
799 this.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction )
800 {
801 var api = this.api( true );
802
803 if ( iColumn === undefined || iColumn === null ) {
804 api.row( mRow ).data( mData );
805 }
806 else {
807 api.cell( mRow, iColumn ).data( mData );
808 }
809
810 if ( bAction === undefined || bAction ) {
811 api.columns.adjust();
812 }
813
814 if ( bRedraw === undefined || bRedraw ) {
815 api.draw();
816 }
817 return 0;
818 };
819
820
821 /**
822 * Provide a common method for plug-ins to check the version of DataTables being used, in order
823 * to ensure compatibility.
824 * @param {string} sVersion Version string to check for, in the format "X.Y.Z". Note that the
825 * formats "X" and "X.Y" are also acceptable.
826 * @returns {boolean} true if this version of DataTables is greater or equal to the required
827 * version, or false if this version of DataTales is not suitable
828 * @method
829 * @dtopt API
830 * @deprecated Since v1.10
831 *
832 * @example
833 * $(document).ready(function() {
834 * var oTable = $('#example').dataTable();
835 * alert( oTable.fnVersionCheck( '1.9.0' ) );
836 * } );
837 */
838 this.fnVersionCheck = _ext.fnVersionCheck;
839
840
841 var _that = this;
842 var emptyInit = options === undefined;
843 var len = this.length;
844
845 if ( emptyInit ) {
846 options = {};
847 }
848
849 this.oApi = this.internal = _ext.internal;
850
851 // Extend with old style plug-in API methods
852 for ( var fn in DataTable.ext.internal ) {
853 if ( fn ) {
854 this[fn] = _fnExternApiFunc(fn);
855 }
856 }
857
858 this.each(function() {
859 // For each initialisation we want to give it a clean initialisation
860 // object that can be bashed around
861 var o = {};
862 var oInit = len > 1 ? // optimisation for single table case
863 _fnExtend( o, options, true ) :
864 options;
865
866 /*global oInit,_that,emptyInit*/
867 var i=0, iLen, j, jLen, k, kLen;
868 var sId = this.getAttribute( 'id' );
869 var bInitHandedOff = false;
870 var defaults = DataTable.defaults;
871 var $this = $(this);
872
873
874 /* Sanity check */
875 if ( this.nodeName.toLowerCase() != 'table' )
876 {
877 _fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 );
878 return;
879 }
880
881 /* Backwards compatibility for the defaults */
882 _fnCompatOpts( defaults );
883 _fnCompatCols( defaults.column );
884
885 /* Convert the camel-case defaults to Hungarian */
886 _fnCamelToHungarian( defaults, defaults, true );
887 _fnCamelToHungarian( defaults.column, defaults.column, true );
888
889 /* Setting up the initialisation object */
890 _fnCamelToHungarian( defaults, $.extend( oInit, $this.data() ), true );
891
892
893
894 /* Check to see if we are re-initialising a table */
895 var allSettings = DataTable.settings;
896 for ( i=0, iLen=allSettings.length ; i<iLen ; i++ )
897 {
898 var s = allSettings[i];
899
900 /* Base check on table node */
901 if (
902 s.nTable == this ||
903 (s.nTHead && s.nTHead.parentNode == this) ||
904 (s.nTFoot && s.nTFoot.parentNode == this)
905 ) {
906 var bRetrieve = oInit.bRetrieve !== undefined ? oInit.bRetrieve : defaults.bRetrieve;
907 var bDestroy = oInit.bDestroy !== undefined ? oInit.bDestroy : defaults.bDestroy;
908
909 if ( emptyInit || bRetrieve )
910 {
911 return s.oInstance;
912 }
913 else if ( bDestroy )
914 {
915 s.oInstance.fnDestroy();
916 break;
917 }
918 else
919 {
920 _fnLog( s, 0, 'Cannot reinitialise DataTable', 3 );
921 return;
922 }
923 }
924
925 /* If the element we are initialising has the same ID as a table which was previously
926 * initialised, but the table nodes don't match (from before) then we destroy the old
927 * instance by simply deleting it. This is under the assumption that the table has been
928 * destroyed by other methods. Anyone using non-id selectors will need to do this manually
929 */
930 if ( s.sTableId == this.id )
931 {
932 allSettings.splice( i, 1 );
933 break;
934 }
935 }
936
937 /* Ensure the table has an ID - required for accessibility */
938 if ( sId === null || sId === "" )
939 {
940 sId = "DataTables_Table_"+(DataTable.ext._unique++);
941 this.id = sId;
942 }
943
944 /* Create the settings object for this table and set some of the default parameters */
945 var oSettings = $.extend( true, {}, DataTable.models.oSettings, {
946 "sDestroyWidth": $this[0].style.width,
947 "sInstance": sId,
948 "sTableId": sId
949 } );
950 oSettings.nTable = this;
951 oSettings.oApi = _that.internal;
952 oSettings.oInit = oInit;
953
954 allSettings.push( oSettings );
955
956 // Need to add the instance after the instance after the settings object has been added
957 // to the settings array, so we can self reference the table instance if more than one
958 oSettings.oInstance = (_that.length===1) ? _that : $this.dataTable();
959
960 // Backwards compatibility, before we apply all the defaults
961 _fnCompatOpts( oInit );
962 _fnLanguageCompat( oInit.oLanguage );
963
964 // If the length menu is given, but the init display length is not, use the length menu
965 if ( oInit.aLengthMenu && ! oInit.iDisplayLength )
966 {
967 oInit.iDisplayLength = Array.isArray( oInit.aLengthMenu[0] ) ?
968 oInit.aLengthMenu[0][0] : oInit.aLengthMenu[0];
969 }
970
971 // Apply the defaults and init options to make a single init object will all
972 // options defined from defaults and instance options.
973 oInit = _fnExtend( $.extend( true, {}, defaults ), oInit );
974
975
976 // Map the initialisation options onto the settings object
977 _fnMap( oSettings.oFeatures, oInit, [
978 "bPaginate",
979 "bLengthChange",
980 "bFilter",
981 "bSort",
982 "bSortMulti",
983 "bInfo",
984 "bProcessing",
985 "bAutoWidth",
986 "bSortClasses",
987 "bServerSide",
988 "bDeferRender"
989 ] );
990 _fnMap( oSettings, oInit, [
991 "asStripeClasses",
992 "ajax",
993 "fnServerData",
994 "fnFormatNumber",
995 "sServerMethod",
996 "aaSorting",
997 "aaSortingFixed",
998 "aLengthMenu",
999 "sPaginationType",
1000 "sAjaxSource",
1001 "sAjaxDataProp",
1002 "iStateDuration",
1003 "sDom",
1004 "bSortCellsTop",
1005 "iTabIndex",
1006 "fnStateLoadCallback",
1007 "fnStateSaveCallback",
1008 "renderer",
1009 "searchDelay",
1010 "rowId",
1011 [ "iCookieDuration", "iStateDuration" ], // backwards compat
1012 [ "oSearch", "oPreviousSearch" ],
1013 [ "aoSearchCols", "aoPreSearchCols" ],
1014 [ "iDisplayLength", "_iDisplayLength" ]
1015 ] );
1016 _fnMap( oSettings.oScroll, oInit, [
1017 [ "sScrollX", "sX" ],
1018 [ "sScrollXInner", "sXInner" ],
1019 [ "sScrollY", "sY" ],
1020 [ "bScrollCollapse", "bCollapse" ]
1021 ] );
1022 _fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" );
1023
1024 /* Callback functions which are array driven */
1025 _fnCallbackReg( oSettings, 'aoDrawCallback', oInit.fnDrawCallback, 'user' );
1026 _fnCallbackReg( oSettings, 'aoServerParams', oInit.fnServerParams, 'user' );
1027 _fnCallbackReg( oSettings, 'aoStateSaveParams', oInit.fnStateSaveParams, 'user' );
1028 _fnCallbackReg( oSettings, 'aoStateLoadParams', oInit.fnStateLoadParams, 'user' );
1029 _fnCallbackReg( oSettings, 'aoStateLoaded', oInit.fnStateLoaded, 'user' );
1030 _fnCallbackReg( oSettings, 'aoRowCallback', oInit.fnRowCallback, 'user' );
1031 _fnCallbackReg( oSettings, 'aoRowCreatedCallback', oInit.fnCreatedRow, 'user' );
1032 _fnCallbackReg( oSettings, 'aoHeaderCallback', oInit.fnHeaderCallback, 'user' );
1033 _fnCallbackReg( oSettings, 'aoFooterCallback', oInit.fnFooterCallback, 'user' );
1034 _fnCallbackReg( oSettings, 'aoInitComplete', oInit.fnInitComplete, 'user' );
1035 _fnCallbackReg( oSettings, 'aoPreDrawCallback', oInit.fnPreDrawCallback, 'user' );
1036
1037 oSettings.rowIdFn = _fnGetObjectDataFn( oInit.rowId );
1038
1039 /* Browser support detection */
1040 _fnBrowserDetect( oSettings );
1041
1042 var oClasses = oSettings.oClasses;
1043
1044 $.extend( oClasses, DataTable.ext.classes, oInit.oClasses );
1045 $this.addClass( oClasses.sTable );
1046
1047
1048 if ( oSettings.iInitDisplayStart === undefined )
1049 {
1050 /* Display start point, taking into account the save saving */
1051 oSettings.iInitDisplayStart = oInit.iDisplayStart;
1052 oSettings._iDisplayStart = oInit.iDisplayStart;
1053 }
1054
1055 if ( oInit.iDeferLoading !== null )
1056 {
1057 oSettings.bDeferLoading = true;
1058 var tmp = Array.isArray( oInit.iDeferLoading );
1059 oSettings._iRecordsDisplay = tmp ? oInit.iDeferLoading[0] : oInit.iDeferLoading;
1060 oSettings._iRecordsTotal = tmp ? oInit.iDeferLoading[1] : oInit.iDeferLoading;
1061 }
1062
1063 /* Language definitions */
1064 var oLanguage = oSettings.oLanguage;
1065 $.extend( true, oLanguage, oInit.oLanguage );
1066
1067 if ( oLanguage.sUrl )
1068 {
1069 /* Get the language definitions from a file - because this Ajax call makes the language
1070 * get async to the remainder of this function we use bInitHandedOff to indicate that
1071 * _fnInitialise will be fired by the returned Ajax handler, rather than the constructor
1072 */
1073 $.ajax( {
1074 dataType: 'json',
1075 url: oLanguage.sUrl,
1076 success: function ( json ) {
1077 _fnCamelToHungarian( defaults.oLanguage, json );
1078 _fnLanguageCompat( json );
1079 $.extend( true, oLanguage, json, oSettings.oInit.oLanguage );
1080
1081 _fnCallbackFire( oSettings, null, 'i18n', [oSettings]);
1082 _fnInitialise( oSettings );
1083 },
1084 error: function () {
1085 // Error occurred loading language file, continue on as best we can
1086 _fnInitialise( oSettings );
1087 }
1088 } );
1089 bInitHandedOff = true;
1090 }
1091 else {
1092 _fnCallbackFire( oSettings, null, 'i18n', [oSettings]);
1093 }
1094
1095 /*
1096 * Stripes
1097 */
1098 if ( oInit.asStripeClasses === null )
1099 {
1100 oSettings.asStripeClasses =[
1101 oClasses.sStripeOdd,
1102 oClasses.sStripeEven
1103 ];
1104 }
1105
1106 /* Remove row stripe classes if they are already on the table row */
1107 var stripeClasses = oSettings.asStripeClasses;
1108 var rowOne = $this.children('tbody').find('tr').eq(0);
1109 if ( $.inArray( true, $.map( stripeClasses, function(el, i) {
1110 return rowOne.hasClass(el);
1111 } ) ) !== -1 ) {
1112 $('tbody tr', this).removeClass( stripeClasses.join(' ') );
1113 oSettings.asDestroyStripes = stripeClasses.slice();
1114 }
1115
1116 /*
1117 * Columns
1118 * See if we should load columns automatically or use defined ones
1119 */
1120 var anThs = [];
1121 var aoColumnsInit;
1122 var nThead = this.getElementsByTagName('thead');
1123 if ( nThead.length !== 0 )
1124 {
1125 _fnDetectHeader( oSettings.aoHeader, nThead[0] );
1126 anThs = _fnGetUniqueThs( oSettings );
1127 }
1128
1129 /* If not given a column array, generate one with nulls */
1130 if ( oInit.aoColumns === null )
1131 {
1132 aoColumnsInit = [];
1133 for ( i=0, iLen=anThs.length ; i<iLen ; i++ )
1134 {
1135 aoColumnsInit.push( null );
1136 }
1137 }
1138 else
1139 {
1140 aoColumnsInit = oInit.aoColumns;
1141 }
1142
1143 /* Add the columns */
1144 for ( i=0, iLen=aoColumnsInit.length ; i<iLen ; i++ )
1145 {
1146 _fnAddColumn( oSettings, anThs ? anThs[i] : null );
1147 }
1148
1149 /* Apply the column definitions */
1150 _fnApplyColumnDefs( oSettings, oInit.aoColumnDefs, aoColumnsInit, function (iCol, oDef) {
1151 _fnColumnOptions( oSettings, iCol, oDef );
1152 } );
1153
1154 /* HTML5 attribute detection - build an mData object automatically if the
1155 * attributes are found
1156 */
1157 if ( rowOne.length ) {
1158 var a = function ( cell, name ) {
1159 return cell.getAttribute( 'data-'+name ) !== null ? name : null;
1160 };
1161
1162 $( rowOne[0] ).children('th, td').each( function (i, cell) {
1163 var col = oSettings.aoColumns[i];
1164
1165 if (! col) {
1166 _fnLog( oSettings, 0, 'Incorrect column count', 18 );
1167 }
1168
1169 if ( col.mData === i ) {
1170 var sort = a( cell, 'sort' ) || a( cell, 'order' );
1171 var filter = a( cell, 'filter' ) || a( cell, 'search' );
1172
1173 if ( sort !== null || filter !== null ) {
1174 col.mData = {
1175 _: i+'.display',
1176 sort: sort !== null ? i+'.@data-'+sort : undefined,
1177 type: sort !== null ? i+'.@data-'+sort : undefined,
1178 filter: filter !== null ? i+'.@data-'+filter : undefined
1179 };
1180
1181 _fnColumnOptions( oSettings, i );
1182 }
1183 }
1184 } );
1185 }
1186
1187 var features = oSettings.oFeatures;
1188 var loadedInit = function () {
1189 /*
1190 * Sorting
1191 * @todo For modularisation (1.11) this needs to do into a sort start up handler
1192 */
1193
1194 // If aaSorting is not defined, then we use the first indicator in asSorting
1195 // in case that has been altered, so the default sort reflects that option
1196 if ( oInit.aaSorting === undefined ) {
1197 var sorting = oSettings.aaSorting;
1198 for ( i=0, iLen=sorting.length ; i<iLen ; i++ ) {
1199 sorting[i][1] = oSettings.aoColumns[ i ].asSorting[0];
1200 }
1201 }
1202
1203 /* Do a first pass on the sorting classes (allows any size changes to be taken into
1204 * account, and also will apply sorting disabled classes if disabled
1205 */
1206 _fnSortingClasses( oSettings );
1207
1208 if ( features.bSort ) {
1209 _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
1210 if ( oSettings.bSorted ) {
1211 var aSort = _fnSortFlatten( oSettings );
1212 var sortedColumns = {};
1213
1214 $.each( aSort, function (i, val) {
1215 sortedColumns[ val.src ] = val.dir;
1216 } );
1217
1218 _fnCallbackFire( oSettings, null, 'order', [oSettings, aSort, sortedColumns] );
1219 _fnSortAria( oSettings );
1220 }
1221 } );
1222 }
1223
1224 _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
1225 if ( oSettings.bSorted || _fnDataSource( oSettings ) === 'ssp' || features.bDeferRender ) {
1226 _fnSortingClasses( oSettings );
1227 }
1228 }, 'sc' );
1229
1230
1231 /*
1232 * Final init
1233 * Cache the header, body and footer as required, creating them if needed
1234 */
1235
1236 // Work around for Webkit bug 83867 - store the caption-side before removing from doc
1237 var captions = $this.children('caption').each( function () {
1238 this._captionSide = $(this).css('caption-side');
1239 } );
1240
1241 var thead = $this.children('thead');
1242 if ( thead.length === 0 ) {
1243 thead = $('<thead/>').appendTo($this);
1244 }
1245 oSettings.nTHead = thead[0];
1246
1247 var tbody = $this.children('tbody');
1248 if ( tbody.length === 0 ) {
1249 tbody = $('<tbody/>').insertAfter(thead);
1250 }
1251 oSettings.nTBody = tbody[0];
1252
1253 var tfoot = $this.children('tfoot');
1254 if ( tfoot.length === 0 && captions.length > 0 && (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") ) {
1255 // If we are a scrolling table, and no footer has been given, then we need to create
1256 // a tfoot element for the caption element to be appended to
1257 tfoot = $('<tfoot/>').appendTo($this);
1258 }
1259
1260 if ( tfoot.length === 0 || tfoot.children().length === 0 ) {
1261 $this.addClass( oClasses.sNoFooter );
1262 }
1263 else if ( tfoot.length > 0 ) {
1264 oSettings.nTFoot = tfoot[0];
1265 _fnDetectHeader( oSettings.aoFooter, oSettings.nTFoot );
1266 }
1267
1268 /* Check if there is data passing into the constructor */
1269 if ( oInit.aaData ) {
1270 for ( i=0 ; i<oInit.aaData.length ; i++ ) {
1271 _fnAddData( oSettings, oInit.aaData[ i ] );
1272 }
1273 }
1274 else if ( oSettings.bDeferLoading || _fnDataSource( oSettings ) == 'dom' ) {
1275 /* Grab the data from the page - only do this when deferred loading or no Ajax
1276 * source since there is no point in reading the DOM data if we are then going
1277 * to replace it with Ajax data
1278 */
1279 _fnAddTr( oSettings, $(oSettings.nTBody).children('tr') );
1280 }
1281
1282 /* Copy the data index array */
1283 oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
1284
1285 /* Initialisation complete - table can be drawn */
1286 oSettings.bInitialised = true;
1287
1288 /* Check if we need to initialise the table (it might not have been handed off to the
1289 * language processor)
1290 */
1291 if ( bInitHandedOff === false ) {
1292 _fnInitialise( oSettings );
1293 }
1294 };
1295
1296 /* Must be done after everything which can be overridden by the state saving! */
1297 _fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState, 'state_save' );
1298
1299 if ( oInit.bStateSave )
1300 {
1301 features.bStateSave = true;
1302 _fnLoadState( oSettings, oInit, loadedInit );
1303 }
1304 else {
1305 loadedInit();
1306 }
1307
1308 } );
1309 _that = null;
1310 return this;
1311 };
1312
1313
1314 /*
1315 * It is useful to have variables which are scoped locally so only the
1316 * DataTables functions can access them and they don't leak into global space.
1317 * At the same time these functions are often useful over multiple files in the
1318 * core and API, so we list, or at least document, all variables which are used
1319 * by DataTables as private variables here. This also ensures that there is no
1320 * clashing of variable names and that they can easily referenced for reuse.
1321 */
1322
1323
1324 // Defined else where
1325 // _selector_run
1326 // _selector_opts
1327 // _selector_first
1328 // _selector_row_indexes
1329
1330 var _ext; // DataTable.ext
1331 var _Api; // DataTable.Api
1332 var _api_register; // DataTable.Api.register
1333 var _api_registerPlural; // DataTable.Api.registerPlural
1334
1335 var _re_dic = {};
1336 var _re_new_lines = /[\r\n\u2028]/g;
1337 var _re_html = /<.*?>/g;
1338
1339 // This is not strict ISO8601 - Date.parse() is quite lax, although
1340 // implementations differ between browsers.
1341 var _re_date = /^\d{2,4}[\.\/\-]\d{1,2}[\.\/\-]\d{1,2}([T ]{1}\d{1,2}[:\.]\d{2}([\.:]\d{2})?)?$/;
1342
1343 // Escape regular expression special characters
1344 var _re_escape_regex = new RegExp( '(\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ].join('|\\') + ')', 'g' );
1345
1346 // http://en.wikipedia.org/wiki/Foreign_exchange_market
1347 // - \u20BD - Russian ruble.
1348 // - \u20a9 - South Korean Won
1349 // - \u20BA - Turkish Lira
1350 // - \u20B9 - Indian Rupee
1351 // - R - Brazil (R$) and South Africa
1352 // - fr - Swiss Franc
1353 // - kr - Swedish krona, Norwegian krone and Danish krone
1354 // - \u2009 is thin space and \u202F is narrow no-break space, both used in many
1355 // - Ƀ - Bitcoin
1356 // - Ξ - Ethereum
1357 // standards as thousands separators.
1358 var _re_formatted_numeric = /['\u00A0,$£€¥%\u2009\u202F\u20BD\u20a9\u20BArfkɃΞ]/gi;
1359
1360
1361 var _empty = function ( d ) {
1362 return !d || d === true || d === '-' ? true : false;
1363 };
1364
1365
1366 var _intVal = function ( s ) {
1367 var integer = parseInt( s, 10 );
1368 return !isNaN(integer) && isFinite(s) ? integer : null;
1369 };
1370
1371 // Convert from a formatted number with characters other than `.` as the
1372 // decimal place, to a Javascript number
1373 var _numToDecimal = function ( num, decimalPoint ) {
1374 // Cache created regular expressions for speed as this function is called often
1375 if ( ! _re_dic[ decimalPoint ] ) {
1376 _re_dic[ decimalPoint ] = new RegExp( _fnEscapeRegex( decimalPoint ), 'g' );
1377 }
1378 return typeof num === 'string' && decimalPoint !== '.' ?
1379 num.replace( /\./g, '' ).replace( _re_dic[ decimalPoint ], '.' ) :
1380 num;
1381 };
1382
1383
1384 var _isNumber = function ( d, decimalPoint, formatted ) {
1385 var strType = typeof d === 'string';
1386
1387 // If empty return immediately so there must be a number if it is a
1388 // formatted string (this stops the string "k", or "kr", etc being detected
1389 // as a formatted number for currency
1390 if ( _empty( d ) ) {
1391 return true;
1392 }
1393
1394 if ( decimalPoint && strType ) {
1395 d = _numToDecimal( d, decimalPoint );
1396 }
1397
1398 if ( formatted && strType ) {
1399 d = d.replace( _re_formatted_numeric, '' );
1400 }
1401
1402 return !isNaN( parseFloat(d) ) && isFinite( d );
1403 };
1404
1405
1406 // A string without HTML in it can be considered to be HTML still
1407 var _isHtml = function ( d ) {
1408 return _empty( d ) || typeof d === 'string';
1409 };
1410
1411
1412 var _htmlNumeric = function ( d, decimalPoint, formatted ) {
1413 if ( _empty( d ) ) {
1414 return true;
1415 }
1416
1417 var html = _isHtml( d );
1418 return ! html ?
1419 null :
1420 _isNumber( _stripHtml( d ), decimalPoint, formatted ) ?
1421 true :
1422 null;
1423 };
1424
1425
1426 var _pluck = function ( a, prop, prop2 ) {
1427 var out = [];
1428 var i=0, ien=a.length;
1429
1430 // Could have the test in the loop for slightly smaller code, but speed
1431 // is essential here
1432 if ( prop2 !== undefined ) {
1433 for ( ; i<ien ; i++ ) {
1434 if ( a[i] && a[i][ prop ] ) {
1435 out.push( a[i][ prop ][ prop2 ] );
1436 }
1437 }
1438 }
1439 else {
1440 for ( ; i<ien ; i++ ) {
1441 if ( a[i] ) {
1442 out.push( a[i][ prop ] );
1443 }
1444 }
1445 }
1446
1447 return out;
1448 };
1449
1450
1451 // Basically the same as _pluck, but rather than looping over `a` we use `order`
1452 // as the indexes to pick from `a`
1453 var _pluck_order = function ( a, order, prop, prop2 )
1454 {
1455 var out = [];
1456 var i=0, ien=order.length;
1457
1458 // Could have the test in the loop for slightly smaller code, but speed
1459 // is essential here
1460 if ( prop2 !== undefined ) {
1461 for ( ; i<ien ; i++ ) {
1462 if ( a[ order[i] ][ prop ] ) {
1463 out.push( a[ order[i] ][ prop ][ prop2 ] );
1464 }
1465 }
1466 }
1467 else {
1468 for ( ; i<ien ; i++ ) {
1469 out.push( a[ order[i] ][ prop ] );
1470 }
1471 }
1472
1473 return out;
1474 };
1475
1476
1477 var _range = function ( len, start )
1478 {
1479 var out = [];
1480 var end;
1481
1482 if ( start === undefined ) {
1483 start = 0;
1484 end = len;
1485 }
1486 else {
1487 end = start;
1488 start = len;
1489 }
1490
1491 for ( var i=start ; i<end ; i++ ) {
1492 out.push( i );
1493 }
1494
1495 return out;
1496 };
1497
1498
1499 var _removeEmpty = function ( a )
1500 {
1501 var out = [];
1502
1503 for ( var i=0, ien=a.length ; i<ien ; i++ ) {
1504 if ( a[i] ) { // careful - will remove all falsy values!
1505 out.push( a[i] );
1506 }
1507 }
1508
1509 return out;
1510 };
1511
1512
1513 var _stripHtml = function ( d ) {
1514 return d.replace( _re_html, '' );
1515 };
1516
1517
1518 /**
1519 * Determine if all values in the array are unique. This means we can short
1520 * cut the _unique method at the cost of a single loop. A sorted array is used
1521 * to easily check the values.
1522 *
1523 * @param {array} src Source array
1524 * @return {boolean} true if all unique, false otherwise
1525 * @ignore
1526 */
1527 var _areAllUnique = function ( src ) {
1528 if ( src.length < 2 ) {
1529 return true;
1530 }
1531
1532 var sorted = src.slice().sort();
1533 var last = sorted[0];
1534
1535 for ( var i=1, ien=sorted.length ; i<ien ; i++ ) {
1536 if ( sorted[i] === last ) {
1537 return false;
1538 }
1539
1540 last = sorted[i];
1541 }
1542
1543 return true;
1544 };
1545
1546
1547 /**
1548 * Find the unique elements in a source array.
1549 *
1550 * @param {array} src Source array
1551 * @return {array} Array of unique items
1552 * @ignore
1553 */
1554 var _unique = function ( src )
1555 {
1556 if ( _areAllUnique( src ) ) {
1557 return src.slice();
1558 }
1559
1560 // A faster unique method is to use object keys to identify used values,
1561 // but this doesn't work with arrays or objects, which we must also
1562 // consider. See jsperf.com/compare-array-unique-versions/4 for more
1563 // information.
1564 var
1565 out = [],
1566 val,
1567 i, ien=src.length,
1568 j, k=0;
1569
1570 again: for ( i=0 ; i<ien ; i++ ) {
1571 val = src[i];
1572
1573 for ( j=0 ; j<k ; j++ ) {
1574 if ( out[j] === val ) {
1575 continue again;
1576 }
1577 }
1578
1579 out.push( val );
1580 k++;
1581 }
1582
1583 return out;
1584 };
1585
1586 // Surprisingly this is faster than [].concat.apply
1587 // https://jsperf.com/flatten-an-array-loop-vs-reduce/2
1588 var _flatten = function (out, val) {
1589 if (Array.isArray(val)) {
1590 for (var i=0 ; i<val.length ; i++) {
1591 _flatten(out, val[i]);
1592 }
1593 }
1594 else {
1595 out.push(val);
1596 }
1597
1598 return out;
1599 }
1600
1601 var _includes = function (search, start) {
1602 if (start === undefined) {
1603 start = 0;
1604 }
1605
1606 return this.indexOf(search, start) !== -1;
1607 };
1608
1609 // Array.isArray polyfill.
1610 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
1611 if (! Array.isArray) {
1612 Array.isArray = function(arg) {
1613 return Object.prototype.toString.call(arg) === '[object Array]';
1614 };
1615 }
1616
1617 if (! Array.prototype.includes) {
1618 Array.prototype.includes = _includes;
1619 }
1620
1621 // .trim() polyfill
1622 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trim
1623 if (!String.prototype.trim) {
1624 String.prototype.trim = function () {
1625 return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
1626 };
1627 }
1628
1629 if (! String.prototype.includes) {
1630 String.prototype.includes = _includes;
1631 }
1632
1633 /**
1634 * DataTables utility methods
1635 *
1636 * This namespace provides helper methods that DataTables uses internally to
1637 * create a DataTable, but which are not exclusively used only for DataTables.
1638 * These methods can be used by extension authors to save the duplication of
1639 * code.
1640 *
1641 * @namespace
1642 */
1643 DataTable.util = {
1644 /**
1645 * Throttle the calls to a function. Arguments and context are maintained
1646 * for the throttled function.
1647 *
1648 * @param {function} fn Function to be called
1649 * @param {integer} freq Call frequency in mS
1650 * @return {function} Wrapped function
1651 */
1652 throttle: function ( fn, freq ) {
1653 var
1654 frequency = freq !== undefined ? freq : 200,
1655 last,
1656 timer;
1657
1658 return function () {
1659 var
1660 that = this,
1661 now = +new Date(),
1662 args = arguments;
1663
1664 if ( last && now < last + frequency ) {
1665 clearTimeout( timer );
1666
1667 timer = setTimeout( function () {
1668 last = undefined;
1669 fn.apply( that, args );
1670 }, frequency );
1671 }
1672 else {
1673 last = now;
1674 fn.apply( that, args );
1675 }
1676 };
1677 },
1678
1679
1680 /**
1681 * Escape a string such that it can be used in a regular expression
1682 *
1683 * @param {string} val string to escape
1684 * @returns {string} escaped string
1685 */
1686 escapeRegex: function ( val ) {
1687 return val.replace( _re_escape_regex, '\\$1' );
1688 },
1689
1690 /**
1691 * Create a function that will write to a nested object or array
1692 * @param {*} source JSON notation string
1693 * @returns Write function
1694 */
1695 set: function ( source ) {
1696 if ( $.isPlainObject( source ) ) {
1697 /* Unlike get, only the underscore (global) option is used for for
1698 * setting data since we don't know the type here. This is why an object
1699 * option is not documented for `mData` (which is read/write), but it is
1700 * for `mRender` which is read only.
1701 */
1702 return DataTable.util.set( source._ );
1703 }
1704 else if ( source === null ) {
1705 // Nothing to do when the data source is null
1706 return function () {};
1707 }
1708 else if ( typeof source === 'function' ) {
1709 return function (data, val, meta) {
1710 source( data, 'set', val, meta );
1711 };
1712 }
1713 else if ( typeof source === 'string' && (source.indexOf('.') !== -1 ||
1714 source.indexOf('[') !== -1 || source.indexOf('(') !== -1) )
1715 {
1716 // Like the get, we need to get data from a nested object
1717 var setData = function (data, val, src) {
1718 var a = _fnSplitObjNotation( src ), b;
1719 var aLast = a[a.length-1];
1720 var arrayNotation, funcNotation, o, innerSrc;
1721
1722 for ( var i=0, iLen=a.length-1 ; i<iLen ; i++ ) {
1723 // Protect against prototype pollution
1724 if (a[i] === '__proto__' || a[i] === 'constructor') {
1725 throw new Error('Cannot set prototype values');
1726 }
1727
1728 // Check if we are dealing with an array notation request
1729 arrayNotation = a[i].match(__reArray);
1730 funcNotation = a[i].match(__reFn);
1731
1732 if ( arrayNotation ) {
1733 a[i] = a[i].replace(__reArray, '');
1734 data[ a[i] ] = [];
1735
1736 // Get the remainder of the nested object to set so we can recurse
1737 b = a.slice();
1738 b.splice( 0, i+1 );
1739 innerSrc = b.join('.');
1740
1741 // Traverse each entry in the array setting the properties requested
1742 if ( Array.isArray( val ) ) {
1743 for ( var j=0, jLen=val.length ; j<jLen ; j++ ) {
1744 o = {};
1745 setData( o, val[j], innerSrc );
1746 data[ a[i] ].push( o );
1747 }
1748 }
1749 else {
1750 // We've been asked to save data to an array, but it
1751 // isn't array data to be saved. Best that can be done
1752 // is to just save the value.
1753 data[ a[i] ] = val;
1754 }
1755
1756 // The inner call to setData has already traversed through the remainder
1757 // of the source and has set the data, thus we can exit here
1758 return;
1759 }
1760 else if ( funcNotation ) {
1761 // Function call
1762 a[i] = a[i].replace(__reFn, '');
1763 data = data[ a[i] ]( val );
1764 }
1765
1766 // If the nested object doesn't currently exist - since we are
1767 // trying to set the value - create it
1768 if ( data[ a[i] ] === null || data[ a[i] ] === undefined ) {
1769 data[ a[i] ] = {};
1770 }
1771 data = data[ a[i] ];
1772 }
1773
1774 // Last item in the input - i.e, the actual set
1775 if ( aLast.match(__reFn ) ) {
1776 // Function call
1777 data = data[ aLast.replace(__reFn, '') ]( val );
1778 }
1779 else {
1780 // If array notation is used, we just want to strip it and use the property name
1781 // and assign the value. If it isn't used, then we get the result we want anyway
1782 data[ aLast.replace(__reArray, '') ] = val;
1783 }
1784 };
1785
1786 return function (data, val) { // meta is also passed in, but not used
1787 return setData( data, val, source );
1788 };
1789 }
1790 else {
1791 // Array or flat object mapping
1792 return function (data, val) { // meta is also passed in, but not used
1793 data[source] = val;
1794 };
1795 }
1796 },
1797
1798 /**
1799 * Create a function that will read nested objects from arrays, based on JSON notation
1800 * @param {*} source JSON notation string
1801 * @returns Value read
1802 */
1803 get: function ( source ) {
1804 if ( $.isPlainObject( source ) ) {
1805 // Build an object of get functions, and wrap them in a single call
1806 var o = {};
1807 $.each( source, function (key, val) {
1808 if ( val ) {
1809 o[key] = DataTable.util.get( val );
1810 }
1811 } );
1812
1813 return function (data, type, row, meta) {
1814 var t = o[type] || o._;
1815 return t !== undefined ?
1816 t(data, type, row, meta) :
1817 data;
1818 };
1819 }
1820 else if ( source === null ) {
1821 // Give an empty string for rendering / sorting etc
1822 return function (data) { // type, row and meta also passed, but not used
1823 return data;
1824 };
1825 }
1826 else if ( typeof source === 'function' ) {
1827 return function (data, type, row, meta) {
1828 return source( data, type, row, meta );
1829 };
1830 }
1831 else if ( typeof source === 'string' && (source.indexOf('.') !== -1 ||
1832 source.indexOf('[') !== -1 || source.indexOf('(') !== -1) )
1833 {
1834 /* If there is a . in the source string then the data source is in a
1835 * nested object so we loop over the data for each level to get the next
1836 * level down. On each loop we test for undefined, and if found immediately
1837 * return. This allows entire objects to be missing and sDefaultContent to
1838 * be used if defined, rather than throwing an error
1839 */
1840 var fetchData = function (data, type, src) {
1841 var arrayNotation, funcNotation, out, innerSrc;
1842
1843 if ( src !== "" ) {
1844 var a = _fnSplitObjNotation( src );
1845
1846 for ( var i=0, iLen=a.length ; i<iLen ; i++ ) {
1847 // Check if we are dealing with special notation
1848 arrayNotation = a[i].match(__reArray);
1849 funcNotation = a[i].match(__reFn);
1850
1851 if ( arrayNotation ) {
1852 // Array notation
1853 a[i] = a[i].replace(__reArray, '');
1854
1855 // Condition allows simply [] to be passed in
1856 if ( a[i] !== "" ) {
1857 data = data[ a[i] ];
1858 }
1859 out = [];
1860
1861 // Get the remainder of the nested object to get
1862 a.splice( 0, i+1 );
1863 innerSrc = a.join('.');
1864
1865 // Traverse each entry in the array getting the properties requested
1866 if ( Array.isArray( data ) ) {
1867 for ( var j=0, jLen=data.length ; j<jLen ; j++ ) {
1868 out.push( fetchData( data[j], type, innerSrc ) );
1869 }
1870 }
1871
1872 // If a string is given in between the array notation indicators, that
1873 // is used to join the strings together, otherwise an array is returned
1874 var join = arrayNotation[0].substring(1, arrayNotation[0].length-1);
1875 data = (join==="") ? out : out.join(join);
1876
1877 // The inner call to fetchData has already traversed through the remainder
1878 // of the source requested, so we exit from the loop
1879 break;
1880 }
1881 else if ( funcNotation ) {
1882 // Function call
1883 a[i] = a[i].replace(__reFn, '');
1884 data = data[ a[i] ]();
1885 continue;
1886 }
1887
1888 if ( data === null || data[ a[i] ] === undefined ) {
1889 return undefined;
1890 }
1891
1892 data = data[ a[i] ];
1893 }
1894 }
1895
1896 return data;
1897 };
1898
1899 return function (data, type) { // row and meta also passed, but not used
1900 return fetchData( data, type, source );
1901 };
1902 }
1903 else {
1904 // Array or flat object mapping
1905 return function (data, type) { // row and meta also passed, but not used
1906 return data[source];
1907 };
1908 }
1909 }
1910 };
1911
1912
1913
1914 /**
1915 * Create a mapping object that allows camel case parameters to be looked up
1916 * for their Hungarian counterparts. The mapping is stored in a private
1917 * parameter called `_hungarianMap` which can be accessed on the source object.
1918 * @param {object} o
1919 * @memberof DataTable#oApi
1920 */
1921 function _fnHungarianMap ( o )
1922 {
1923 var
1924 hungarian = 'a aa ai ao as b fn i m o s ',
1925 match,
1926 newKey,
1927 map = {};
1928
1929 $.each( o, function (key, val) {
1930 match = key.match(/^([^A-Z]+?)([A-Z])/);
1931
1932 if ( match && hungarian.indexOf(match[1]+' ') !== -1 )
1933 {
1934 newKey = key.replace( match[0], match[2].toLowerCase() );
1935 map[ newKey ] = key;
1936
1937 if ( match[1] === 'o' )
1938 {
1939 _fnHungarianMap( o[key] );
1940 }
1941 }
1942 } );
1943
1944 o._hungarianMap = map;
1945 }
1946
1947
1948 /**
1949 * Convert from camel case parameters to Hungarian, based on a Hungarian map
1950 * created by _fnHungarianMap.
1951 * @param {object} src The model object which holds all parameters that can be
1952 * mapped.
1953 * @param {object} user The object to convert from camel case to Hungarian.
1954 * @param {boolean} force When set to `true`, properties which already have a
1955 * Hungarian value in the `user` object will be overwritten. Otherwise they
1956 * won't be.
1957 * @memberof DataTable#oApi
1958 */
1959 function _fnCamelToHungarian ( src, user, force )
1960 {
1961 if ( ! src._hungarianMap ) {
1962 _fnHungarianMap( src );
1963 }
1964
1965 var hungarianKey;
1966
1967 $.each( user, function (key, val) {
1968 hungarianKey = src._hungarianMap[ key ];
1969
1970 if ( hungarianKey !== undefined && (force || user[hungarianKey] === undefined) )
1971 {
1972 // For objects, we need to buzz down into the object to copy parameters
1973 if ( hungarianKey.charAt(0) === 'o' )
1974 {
1975 // Copy the camelCase options over to the hungarian
1976 if ( ! user[ hungarianKey ] ) {
1977 user[ hungarianKey ] = {};
1978 }
1979 $.extend( true, user[hungarianKey], user[key] );
1980
1981 _fnCamelToHungarian( src[hungarianKey], user[hungarianKey], force );
1982 }
1983 else {
1984 user[hungarianKey] = user[ key ];
1985 }
1986 }
1987 } );
1988 }
1989
1990
1991 /**
1992 * Language compatibility - when certain options are given, and others aren't, we
1993 * need to duplicate the values over, in order to provide backwards compatibility
1994 * with older language files.
1995 * @param {object} oSettings dataTables settings object
1996 * @memberof DataTable#oApi
1997 */
1998 function _fnLanguageCompat( lang )
1999 {
2000 // Note the use of the Hungarian notation for the parameters in this method as
2001 // this is called after the mapping of camelCase to Hungarian
2002 var defaults = DataTable.defaults.oLanguage;
2003
2004 // Default mapping
2005 var defaultDecimal = defaults.sDecimal;
2006 if ( defaultDecimal ) {
2007 _addNumericSort( defaultDecimal );
2008 }
2009
2010 if ( lang ) {
2011 var zeroRecords = lang.sZeroRecords;
2012
2013 // Backwards compatibility - if there is no sEmptyTable given, then use the same as
2014 // sZeroRecords - assuming that is given.
2015 if ( ! lang.sEmptyTable && zeroRecords &&
2016 defaults.sEmptyTable === "No data available in table" )
2017 {
2018 _fnMap( lang, lang, 'sZeroRecords', 'sEmptyTable' );
2019 }
2020
2021 // Likewise with loading records
2022 if ( ! lang.sLoadingRecords && zeroRecords &&
2023 defaults.sLoadingRecords === "Loading..." )
2024 {
2025 _fnMap( lang, lang, 'sZeroRecords', 'sLoadingRecords' );
2026 }
2027
2028 // Old parameter name of the thousands separator mapped onto the new
2029 if ( lang.sInfoThousands ) {
2030 lang.sThousands = lang.sInfoThousands;
2031 }
2032
2033 var decimal = lang.sDecimal;
2034 if ( decimal && defaultDecimal !== decimal ) {
2035 _addNumericSort( decimal );
2036 }
2037 }
2038 }
2039
2040
2041 /**
2042 * Map one parameter onto another
2043 * @param {object} o Object to map
2044 * @param {*} knew The new parameter name
2045 * @param {*} old The old parameter name
2046 */
2047 var _fnCompatMap = function ( o, knew, old ) {
2048 if ( o[ knew ] !== undefined ) {
2049 o[ old ] = o[ knew ];
2050 }
2051 };
2052
2053
2054 /**
2055 * Provide backwards compatibility for the main DT options. Note that the new
2056 * options are mapped onto the old parameters, so this is an external interface
2057 * change only.
2058 * @param {object} init Object to map
2059 */
2060 function _fnCompatOpts ( init )
2061 {
2062 _fnCompatMap( init, 'ordering', 'bSort' );
2063 _fnCompatMap( init, 'orderMulti', 'bSortMulti' );
2064 _fnCompatMap( init, 'orderClasses', 'bSortClasses' );
2065 _fnCompatMap( init, 'orderCellsTop', 'bSortCellsTop' );
2066 _fnCompatMap( init, 'order', 'aaSorting' );
2067 _fnCompatMap( init, 'orderFixed', 'aaSortingFixed' );
2068 _fnCompatMap( init, 'paging', 'bPaginate' );
2069 _fnCompatMap( init, 'pagingType', 'sPaginationType' );
2070 _fnCompatMap( init, 'pageLength', 'iDisplayLength' );
2071 _fnCompatMap( init, 'searching', 'bFilter' );
2072
2073 // Boolean initialisation of x-scrolling
2074 if ( typeof init.sScrollX === 'boolean' ) {
2075 init.sScrollX = init.sScrollX ? '100%' : '';
2076 }
2077 if ( typeof init.scrollX === 'boolean' ) {
2078 init.scrollX = init.scrollX ? '100%' : '';
2079 }
2080
2081 // Column search objects are in an array, so it needs to be converted
2082 // element by element
2083 var searchCols = init.aoSearchCols;
2084
2085 if ( searchCols ) {
2086 for ( var i=0, ien=searchCols.length ; i<ien ; i++ ) {
2087 if ( searchCols[i] ) {
2088 _fnCamelToHungarian( DataTable.models.oSearch, searchCols[i] );
2089 }
2090 }
2091 }
2092 }
2093
2094
2095 /**
2096 * Provide backwards compatibility for column options. Note that the new options
2097 * are mapped onto the old parameters, so this is an external interface change
2098 * only.
2099 * @param {object} init Object to map
2100 */
2101 function _fnCompatCols ( init )
2102 {
2103 _fnCompatMap( init, 'orderable', 'bSortable' );
2104 _fnCompatMap( init, 'orderData', 'aDataSort' );
2105 _fnCompatMap( init, 'orderSequence', 'asSorting' );
2106 _fnCompatMap( init, 'orderDataType', 'sortDataType' );
2107
2108 // orderData can be given as an integer
2109 var dataSort = init.aDataSort;
2110 if ( typeof dataSort === 'number' && ! Array.isArray( dataSort ) ) {
2111 init.aDataSort = [ dataSort ];
2112 }
2113 }
2114
2115
2116 /**
2117 * Browser feature detection for capabilities, quirks
2118 * @param {object} settings dataTables settings object
2119 * @memberof DataTable#oApi
2120 */
2121 function _fnBrowserDetect( settings )
2122 {
2123 // We don't need to do this every time DataTables is constructed, the values
2124 // calculated are specific to the browser and OS configuration which we
2125 // don't expect to change between initialisations
2126 if ( ! DataTable.__browser ) {
2127 var browser = {};
2128 DataTable.__browser = browser;
2129
2130 // Scrolling feature / quirks detection
2131 var n = $('<div/>')
2132 .css( {
2133 position: 'fixed',
2134 top: 0,
2135 left: $(window).scrollLeft()*-1, // allow for scrolling
2136 height: 1,
2137 width: 1,
2138 overflow: 'hidden'
2139 } )
2140 .append(
2141 $('<div/>')
2142 .css( {
2143 position: 'absolute',
2144 top: 1,
2145 left: 1,
2146 width: 100,
2147 overflow: 'scroll'
2148 } )
2149 .append(
2150 $('<div/>')
2151 .css( {
2152 width: '100%',
2153 height: 10
2154 } )
2155 )
2156 )
2157 .appendTo( 'body' );
2158
2159 var outer = n.children();
2160 var inner = outer.children();
2161
2162 // Numbers below, in order, are:
2163 // inner.offsetWidth, inner.clientWidth, outer.offsetWidth, outer.clientWidth
2164 //
2165 // IE6 XP: 100 100 100 83
2166 // IE7 Vista: 100 100 100 83
2167 // IE 8+ Windows: 83 83 100 83
2168 // Evergreen Windows: 83 83 100 83
2169 // Evergreen Mac with scrollbars: 85 85 100 85
2170 // Evergreen Mac without scrollbars: 100 100 100 100
2171
2172 // Get scrollbar width
2173 browser.barWidth = outer[0].offsetWidth - outer[0].clientWidth;
2174
2175 // IE6/7 will oversize a width 100% element inside a scrolling element, to
2176 // include the width of the scrollbar, while other browsers ensure the inner
2177 // element is contained without forcing scrolling
2178 browser.bScrollOversize = inner[0].offsetWidth === 100 && outer[0].clientWidth !== 100;
2179
2180 // In rtl text layout, some browsers (most, but not all) will place the
2181 // scrollbar on the left, rather than the right.
2182 browser.bScrollbarLeft = Math.round( inner.offset().left ) !== 1;
2183
2184 // IE8- don't provide height and width for getBoundingClientRect
2185 browser.bBounding = n[0].getBoundingClientRect().width ? true : false;
2186
2187 n.remove();
2188 }
2189
2190 $.extend( settings.oBrowser, DataTable.__browser );
2191 settings.oScroll.iBarWidth = DataTable.__browser.barWidth;
2192 }
2193
2194
2195 /**
2196 * Array.prototype reduce[Right] method, used for browsers which don't support
2197 * JS 1.6. Done this way to reduce code size, since we iterate either way
2198 * @param {object} settings dataTables settings object
2199 * @memberof DataTable#oApi
2200 */
2201 function _fnReduce ( that, fn, init, start, end, inc )
2202 {
2203 var
2204 i = start,
2205 value,
2206 isSet = false;
2207
2208 if ( init !== undefined ) {
2209 value = init;
2210 isSet = true;
2211 }
2212
2213 while ( i !== end ) {
2214 if ( ! that.hasOwnProperty(i) ) {
2215 continue;
2216 }
2217
2218 value = isSet ?
2219 fn( value, that[i], i, that ) :
2220 that[i];
2221
2222 isSet = true;
2223 i += inc;
2224 }
2225
2226 return value;
2227 }
2228
2229 /**
2230 * Add a column to the list used for the table with default values
2231 * @param {object} oSettings dataTables settings object
2232 * @param {node} nTh The th element for this column
2233 * @memberof DataTable#oApi
2234 */
2235 function _fnAddColumn( oSettings, nTh )
2236 {
2237 // Add column to aoColumns array
2238 var oDefaults = DataTable.defaults.column;
2239 var iCol = oSettings.aoColumns.length;
2240 var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, {
2241 "nTh": nTh ? nTh : document.createElement('th'),
2242 "sTitle": oDefaults.sTitle ? oDefaults.sTitle : nTh ? nTh.innerHTML : '',
2243 "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol],
2244 "mData": oDefaults.mData ? oDefaults.mData : iCol,
2245 idx: iCol
2246 } );
2247 oSettings.aoColumns.push( oCol );
2248
2249 // Add search object for column specific search. Note that the `searchCols[ iCol ]`
2250 // passed into extend can be undefined. This allows the user to give a default
2251 // with only some of the parameters defined, and also not give a default
2252 var searchCols = oSettings.aoPreSearchCols;
2253 searchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch, searchCols[ iCol ] );
2254
2255 // Use the default column options function to initialise classes etc
2256 _fnColumnOptions( oSettings, iCol, $(nTh).data() );
2257 }
2258
2259
2260 /**
2261 * Apply options for a column
2262 * @param {object} oSettings dataTables settings object
2263 * @param {int} iCol column index to consider
2264 * @param {object} oOptions object with sType, bVisible and bSearchable etc
2265 * @memberof DataTable#oApi
2266 */
2267 function _fnColumnOptions( oSettings, iCol, oOptions )
2268 {
2269 var oCol = oSettings.aoColumns[ iCol ];
2270 var oClasses = oSettings.oClasses;
2271 var th = $(oCol.nTh);
2272
2273 // Try to get width information from the DOM. We can't get it from CSS
2274 // as we'd need to parse the CSS stylesheet. `width` option can override
2275 if ( ! oCol.sWidthOrig ) {
2276 // Width attribute
2277 oCol.sWidthOrig = th.attr('width') || null;
2278
2279 // Style attribute
2280 var t = (th.attr('style') || '').match(/width:\s*(\d+[pxem%]+)/);
2281 if ( t ) {
2282 oCol.sWidthOrig = t[1];
2283 }
2284 }
2285
2286 /* User specified column options */
2287 if ( oOptions !== undefined && oOptions !== null )
2288 {
2289 // Backwards compatibility
2290 _fnCompatCols( oOptions );
2291
2292 // Map camel case parameters to their Hungarian counterparts
2293 _fnCamelToHungarian( DataTable.defaults.column, oOptions, true );
2294
2295 /* Backwards compatibility for mDataProp */
2296 if ( oOptions.mDataProp !== undefined && !oOptions.mData )
2297 {
2298 oOptions.mData = oOptions.mDataProp;
2299 }
2300
2301 if ( oOptions.sType )
2302 {
2303 oCol._sManualType = oOptions.sType;
2304 }
2305
2306 // `class` is a reserved word in Javascript, so we need to provide
2307 // the ability to use a valid name for the camel case input
2308 if ( oOptions.className && ! oOptions.sClass )
2309 {
2310 oOptions.sClass = oOptions.className;
2311 }
2312 if ( oOptions.sClass ) {
2313 th.addClass( oOptions.sClass );
2314 }
2315
2316 var origClass = oCol.sClass;
2317
2318 $.extend( oCol, oOptions );
2319 _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" );
2320
2321 // Merge class from previously defined classes with this one, rather than just
2322 // overwriting it in the extend above
2323 if (origClass !== oCol.sClass) {
2324 oCol.sClass = origClass + ' ' + oCol.sClass;
2325 }
2326
2327 /* iDataSort to be applied (backwards compatibility), but aDataSort will take
2328 * priority if defined
2329 */
2330 if ( oOptions.iDataSort !== undefined )
2331 {
2332 oCol.aDataSort = [ oOptions.iDataSort ];
2333 }
2334 _fnMap( oCol, oOptions, "aDataSort" );
2335 }
2336
2337 /* Cache the data get and set functions for speed */
2338 var mDataSrc = oCol.mData;
2339 var mData = _fnGetObjectDataFn( mDataSrc );
2340 var mRender = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null;
2341
2342 var attrTest = function( src ) {
2343 return typeof src === 'string' && src.indexOf('@') !== -1;
2344 };
2345 oCol._bAttrSrc = $.isPlainObject( mDataSrc ) && (
2346 attrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter)
2347 );
2348 oCol._setter = null;
2349
2350 oCol.fnGetData = function (rowData, type, meta) {
2351 var innerData = mData( rowData, type, undefined, meta );
2352
2353 return mRender && type ?
2354 mRender( innerData, type, rowData, meta ) :
2355 innerData;
2356 };
2357 oCol.fnSetData = function ( rowData, val, meta ) {
2358 return _fnSetObjectDataFn( mDataSrc )( rowData, val, meta );
2359 };
2360
2361 // Indicate if DataTables should read DOM data as an object or array
2362 // Used in _fnGetRowElements
2363 if ( typeof mDataSrc !== 'number' ) {
2364 oSettings._rowReadObject = true;
2365 }
2366
2367 /* Feature sorting overrides column specific when off */
2368 if ( !oSettings.oFeatures.bSort )
2369 {
2370 oCol.bSortable = false;
2371 th.addClass( oClasses.sSortableNone ); // Have to add class here as order event isn't called
2372 }
2373
2374 /* Check that the class assignment is correct for sorting */
2375 var bAsc = $.inArray('asc', oCol.asSorting) !== -1;
2376 var bDesc = $.inArray('desc', oCol.asSorting) !== -1;
2377 if ( !oCol.bSortable || (!bAsc && !bDesc) )
2378 {
2379 oCol.sSortingClass = oClasses.sSortableNone;
2380 oCol.sSortingClassJUI = "";
2381 }
2382 else if ( bAsc && !bDesc )
2383 {
2384 oCol.sSortingClass = oClasses.sSortableAsc;
2385 oCol.sSortingClassJUI = oClasses.sSortJUIAscAllowed;
2386 }
2387 else if ( !bAsc && bDesc )
2388 {
2389 oCol.sSortingClass = oClasses.sSortableDesc;
2390 oCol.sSortingClassJUI = oClasses.sSortJUIDescAllowed;
2391 }
2392 else
2393 {
2394 oCol.sSortingClass = oClasses.sSortable;
2395 oCol.sSortingClassJUI = oClasses.sSortJUI;
2396 }
2397 }
2398
2399
2400 /**
2401 * Adjust the table column widths for new data. Note: you would probably want to
2402 * do a redraw after calling this function!
2403 * @param {object} settings dataTables settings object
2404 * @memberof DataTable#oApi
2405 */
2406 function _fnAdjustColumnSizing ( settings )
2407 {
2408 /* Not interested in doing column width calculation if auto-width is disabled */
2409 if ( settings.oFeatures.bAutoWidth !== false )
2410 {
2411 var columns = settings.aoColumns;
2412
2413 _fnCalculateColumnWidths( settings );
2414 for ( var i=0 , iLen=columns.length ; i<iLen ; i++ )
2415 {
2416 columns[i].nTh.style.width = columns[i].sWidth;
2417 }
2418 }
2419
2420 var scroll = settings.oScroll;
2421 if ( scroll.sY !== '' || scroll.sX !== '')
2422 {
2423 _fnScrollDraw( settings );
2424 }
2425
2426 _fnCallbackFire( settings, null, 'column-sizing', [settings] );
2427 }
2428
2429
2430 /**
2431 * Convert the index of a visible column to the index in the data array (take account
2432 * of hidden columns)
2433 * @param {object} oSettings dataTables settings object
2434 * @param {int} iMatch Visible column index to lookup
2435 * @returns {int} i the data index
2436 * @memberof DataTable#oApi
2437 */
2438 function _fnVisibleToColumnIndex( oSettings, iMatch )
2439 {
2440 var aiVis = _fnGetColumns( oSettings, 'bVisible' );
2441
2442 return typeof aiVis[iMatch] === 'number' ?
2443 aiVis[iMatch] :
2444 null;
2445 }
2446
2447
2448 /**
2449 * Convert the index of an index in the data array and convert it to the visible
2450 * column index (take account of hidden columns)
2451 * @param {int} iMatch Column index to lookup
2452 * @param {object} oSettings dataTables settings object
2453 * @returns {int} i the data index
2454 * @memberof DataTable#oApi
2455 */
2456 function _fnColumnIndexToVisible( oSettings, iMatch )
2457 {
2458 var aiVis = _fnGetColumns( oSettings, 'bVisible' );
2459 var iPos = $.inArray( iMatch, aiVis );
2460
2461 return iPos !== -1 ? iPos : null;
2462 }
2463
2464
2465 /**
2466 * Get the number of visible columns
2467 * @param {object} oSettings dataTables settings object
2468 * @returns {int} i the number of visible columns
2469 * @memberof DataTable#oApi
2470 */
2471 function _fnVisbleColumns( oSettings )
2472 {
2473 var vis = 0;
2474
2475 // No reduce in IE8, use a loop for now
2476 $.each( oSettings.aoColumns, function ( i, col ) {
2477 if ( col.bVisible && $(col.nTh).css('display') !== 'none' ) {
2478 vis++;
2479 }
2480 } );
2481
2482 return vis;
2483 }
2484
2485
2486 /**
2487 * Get an array of column indexes that match a given property
2488 * @param {object} oSettings dataTables settings object
2489 * @param {string} sParam Parameter in aoColumns to look for - typically
2490 * bVisible or bSearchable
2491 * @returns {array} Array of indexes with matched properties
2492 * @memberof DataTable#oApi
2493 */
2494 function _fnGetColumns( oSettings, sParam )
2495 {
2496 var a = [];
2497
2498 $.map( oSettings.aoColumns, function(val, i) {
2499 if ( val[sParam] ) {
2500 a.push( i );
2501 }
2502 } );
2503
2504 return a;
2505 }
2506
2507
2508 /**
2509 * Calculate the 'type' of a column
2510 * @param {object} settings dataTables settings object
2511 * @memberof DataTable#oApi
2512 */
2513 function _fnColumnTypes ( settings )
2514 {
2515 var columns = settings.aoColumns;
2516 var data = settings.aoData;
2517 var types = DataTable.ext.type.detect;
2518 var i, ien, j, jen, k, ken;
2519 var col, cell, detectedType, cache;
2520
2521 // For each column, spin over the
2522 for ( i=0, ien=columns.length ; i<ien ; i++ ) {
2523 col = columns[i];
2524 cache = [];
2525
2526 if ( ! col.sType && col._sManualType ) {
2527 col.sType = col._sManualType;
2528 }
2529 else if ( ! col.sType ) {
2530 for ( j=0, jen=types.length ; j<jen ; j++ ) {
2531 for ( k=0, ken=data.length ; k<ken ; k++ ) {
2532 // Use a cache array so we only need to get the type data
2533 // from the formatter once (when using multiple detectors)
2534 if ( cache[k] === undefined ) {
2535 cache[k] = _fnGetCellData( settings, k, i, 'type' );
2536 }
2537
2538 detectedType = types[j]( cache[k], settings );
2539
2540 // If null, then this type can't apply to this column, so
2541 // rather than testing all cells, break out. There is an
2542 // exception for the last type which is `html`. We need to
2543 // scan all rows since it is possible to mix string and HTML
2544 // types
2545 if ( ! detectedType && j !== types.length-1 ) {
2546 break;
2547 }
2548
2549 // Only a single match is needed for html type since it is
2550 // bottom of the pile and very similar to string - but it
2551 // must not be empty
2552 if ( detectedType === 'html' && ! _empty(cache[k]) ) {
2553 break;
2554 }
2555 }
2556
2557 // Type is valid for all data points in the column - use this
2558 // type
2559 if ( detectedType ) {
2560 col.sType = detectedType;
2561 break;
2562 }
2563 }
2564
2565 // Fall back - if no type was detected, always use string
2566 if ( ! col.sType ) {
2567 col.sType = 'string';
2568 }
2569 }
2570 }
2571 }
2572
2573
2574 /**
2575 * Take the column definitions and static columns arrays and calculate how
2576 * they relate to column indexes. The callback function will then apply the
2577 * definition found for a column to a suitable configuration object.
2578 * @param {object} oSettings dataTables settings object
2579 * @param {array} aoColDefs The aoColumnDefs array that is to be applied
2580 * @param {array} aoCols The aoColumns array that defines columns individually
2581 * @param {function} fn Callback function - takes two parameters, the calculated
2582 * column index and the definition for that column.
2583 * @memberof DataTable#oApi
2584 */
2585 function _fnApplyColumnDefs( oSettings, aoColDefs, aoCols, fn )
2586 {
2587 var i, iLen, j, jLen, k, kLen, def;
2588 var columns = oSettings.aoColumns;
2589
2590 // Column definitions with aTargets
2591 if ( aoColDefs )
2592 {
2593 /* Loop over the definitions array - loop in reverse so first instance has priority */
2594 for ( i=aoColDefs.length-1 ; i>=0 ; i-- )
2595 {
2596 def = aoColDefs[i];
2597
2598 /* Each definition can target multiple columns, as it is an array */
2599 var aTargets = def.target !== undefined
2600 ? def.target
2601 : def.targets !== undefined
2602 ? def.targets
2603 : def.aTargets;
2604
2605 if ( ! Array.isArray( aTargets ) )
2606 {
2607 aTargets = [ aTargets ];
2608 }
2609
2610 for ( j=0, jLen=aTargets.length ; j<jLen ; j++ )
2611 {
2612 if ( typeof aTargets[j] === 'number' && aTargets[j] >= 0 )
2613 {
2614 /* Add columns that we don't yet know about */
2615 while( columns.length <= aTargets[j] )
2616 {
2617 _fnAddColumn( oSettings );
2618 }
2619
2620 /* Integer, basic index */
2621 fn( aTargets[j], def );
2622 }
2623 else if ( typeof aTargets[j] === 'number' && aTargets[j] < 0 )
2624 {
2625 /* Negative integer, right to left column counting */
2626 fn( columns.length+aTargets[j], def );
2627 }
2628 else if ( typeof aTargets[j] === 'string' )
2629 {
2630 /* Class name matching on TH element */
2631 for ( k=0, kLen=columns.length ; k<kLen ; k++ )
2632 {
2633 if ( aTargets[j] == "_all" ||
2634 $(columns[k].nTh).hasClass( aTargets[j] ) )
2635 {
2636 fn( k, def );
2637 }
2638 }
2639 }
2640 }
2641 }
2642 }
2643
2644 // Statically defined columns array
2645 if ( aoCols )
2646 {
2647 for ( i=0, iLen=aoCols.length ; i<iLen ; i++ )
2648 {
2649 fn( i, aoCols[i] );
2650 }
2651 }
2652 }
2653
2654 /**
2655 * Add a data array to the table, creating DOM node etc. This is the parallel to
2656 * _fnGatherData, but for adding rows from a Javascript source, rather than a
2657 * DOM source.
2658 * @param {object} oSettings dataTables settings object
2659 * @param {array} aData data array to be added
2660 * @param {node} [nTr] TR element to add to the table - optional. If not given,
2661 * DataTables will create a row automatically
2662 * @param {array} [anTds] Array of TD|TH elements for the row - must be given
2663 * if nTr is.
2664 * @returns {int} >=0 if successful (index of new aoData entry), -1 if failed
2665 * @memberof DataTable#oApi
2666 */
2667 function _fnAddData ( oSettings, aDataIn, nTr, anTds )
2668 {
2669 /* Create the object for storing information about this new row */
2670 var iRow = oSettings.aoData.length;
2671 var oData = $.extend( true, {}, DataTable.models.oRow, {
2672 src: nTr ? 'dom' : 'data',
2673 idx: iRow
2674 } );
2675
2676 oData._aData = aDataIn;
2677 oSettings.aoData.push( oData );
2678
2679 /* Create the cells */
2680 var nTd, sThisType;
2681 var columns = oSettings.aoColumns;
2682
2683 // Invalidate the column types as the new data needs to be revalidated
2684 for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
2685 {
2686 columns[i].sType = null;
2687 }
2688
2689 /* Add to the display array */
2690 oSettings.aiDisplayMaster.push( iRow );
2691
2692 var id = oSettings.rowIdFn( aDataIn );
2693 if ( id !== undefined ) {
2694 oSettings.aIds[ id ] = oData;
2695 }
2696
2697 /* Create the DOM information, or register it if already present */
2698 if ( nTr || ! oSettings.oFeatures.bDeferRender )
2699 {
2700 _fnCreateTr( oSettings, iRow, nTr, anTds );
2701 }
2702
2703 return iRow;
2704 }
2705
2706
2707 /**
2708 * Add one or more TR elements to the table. Generally we'd expect to
2709 * use this for reading data from a DOM sourced table, but it could be
2710 * used for an TR element. Note that if a TR is given, it is used (i.e.
2711 * it is not cloned).
2712 * @param {object} settings dataTables settings object
2713 * @param {array|node|jQuery} trs The TR element(s) to add to the table
2714 * @returns {array} Array of indexes for the added rows
2715 * @memberof DataTable#oApi
2716 */
2717 function _fnAddTr( settings, trs )
2718 {
2719 var row;
2720
2721 // Allow an individual node to be passed in
2722 if ( ! (trs instanceof $) ) {
2723 trs = $(trs);
2724 }
2725
2726 return trs.map( function (i, el) {
2727 row = _fnGetRowElements( settings, el );
2728 return _fnAddData( settings, row.data, el, row.cells );
2729 } );
2730 }
2731
2732
2733 /**
2734 * Take a TR element and convert it to an index in aoData
2735 * @param {object} oSettings dataTables settings object
2736 * @param {node} n the TR element to find
2737 * @returns {int} index if the node is found, null if not
2738 * @memberof DataTable#oApi
2739 */
2740 function _fnNodeToDataIndex( oSettings, n )
2741 {
2742 return (n._DT_RowIndex!==undefined) ? n._DT_RowIndex : null;
2743 }
2744
2745
2746 /**
2747 * Take a TD element and convert it into a column data index (not the visible index)
2748 * @param {object} oSettings dataTables settings object
2749 * @param {int} iRow The row number the TD/TH can be found in
2750 * @param {node} n The TD/TH element to find
2751 * @returns {int} index if the node is found, -1 if not
2752 * @memberof DataTable#oApi
2753 */
2754 function _fnNodeToColumnIndex( oSettings, iRow, n )
2755 {
2756 return $.inArray( n, oSettings.aoData[ iRow ].anCells );
2757 }
2758
2759
2760 /**
2761 * Get the data for a given cell from the internal cache, taking into account data mapping
2762 * @param {object} settings dataTables settings object
2763 * @param {int} rowIdx aoData row id
2764 * @param {int} colIdx Column index
2765 * @param {string} type data get type ('display', 'type' 'filter|search' 'sort|order')
2766 * @returns {*} Cell data
2767 * @memberof DataTable#oApi
2768 */
2769 function _fnGetCellData( settings, rowIdx, colIdx, type )
2770 {
2771 if (type === 'search') {
2772 type = 'filter';
2773 }
2774 else if (type === 'order') {
2775 type = 'sort';
2776 }
2777
2778 var draw = settings.iDraw;
2779 var col = settings.aoColumns[colIdx];
2780 var rowData = settings.aoData[rowIdx]._aData;
2781 var defaultContent = col.sDefaultContent;
2782 var cellData = col.fnGetData( rowData, type, {
2783 settings: settings,
2784 row: rowIdx,
2785 col: colIdx
2786 } );
2787
2788 if ( cellData === undefined ) {
2789 if ( settings.iDrawError != draw && defaultContent === null ) {
2790 _fnLog( settings, 0, "Requested unknown parameter "+
2791 (typeof col.mData=='function' ? '{function}' : "'"+col.mData+"'")+
2792 " for row "+rowIdx+", column "+colIdx, 4 );
2793 settings.iDrawError = draw;
2794 }
2795 return defaultContent;
2796 }
2797
2798 // When the data source is null and a specific data type is requested (i.e.
2799 // not the original data), we can use default column data
2800 if ( (cellData === rowData || cellData === null) && defaultContent !== null && type !== undefined ) {
2801 cellData = defaultContent;
2802 }
2803 else if ( typeof cellData === 'function' ) {
2804 // If the data source is a function, then we run it and use the return,
2805 // executing in the scope of the data object (for instances)
2806 return cellData.call( rowData );
2807 }
2808
2809 if ( cellData === null && type === 'display' ) {
2810 return '';
2811 }
2812
2813 if ( type === 'filter' ) {
2814 var fomatters = DataTable.ext.type.search;
2815
2816 if ( fomatters[ col.sType ] ) {
2817 cellData = fomatters[ col.sType ]( cellData );
2818 }
2819 }
2820
2821 return cellData;
2822 }
2823
2824
2825 /**
2826 * Set the value for a specific cell, into the internal data cache
2827 * @param {object} settings dataTables settings object
2828 * @param {int} rowIdx aoData row id
2829 * @param {int} colIdx Column index
2830 * @param {*} val Value to set
2831 * @memberof DataTable#oApi
2832 */
2833 function _fnSetCellData( settings, rowIdx, colIdx, val )
2834 {
2835 var col = settings.aoColumns[colIdx];
2836 var rowData = settings.aoData[rowIdx]._aData;
2837
2838 col.fnSetData( rowData, val, {
2839 settings: settings,
2840 row: rowIdx,
2841 col: colIdx
2842 } );
2843 }
2844
2845
2846 // Private variable that is used to match action syntax in the data property object
2847 var __reArray = /\[.*?\]$/;
2848 var __reFn = /\(\)$/;
2849
2850 /**
2851 * Split string on periods, taking into account escaped periods
2852 * @param {string} str String to split
2853 * @return {array} Split string
2854 */
2855 function _fnSplitObjNotation( str )
2856 {
2857 return $.map( str.match(/(\\.|[^\.])+/g) || [''], function ( s ) {
2858 return s.replace(/\\\./g, '.');
2859 } );
2860 }
2861
2862
2863 /**
2864 * Return a function that can be used to get data from a source object, taking
2865 * into account the ability to use nested objects as a source
2866 * @param {string|int|function} mSource The data source for the object
2867 * @returns {function} Data get function
2868 * @memberof DataTable#oApi
2869 */
2870 var _fnGetObjectDataFn = DataTable.util.get;
2871
2872
2873 /**
2874 * Return a function that can be used to set data from a source object, taking
2875 * into account the ability to use nested objects as a source
2876 * @param {string|int|function} mSource The data source for the object
2877 * @returns {function} Data set function
2878 * @memberof DataTable#oApi
2879 */
2880 var _fnSetObjectDataFn = DataTable.util.set;
2881
2882
2883 /**
2884 * Return an array with the full table data
2885 * @param {object} oSettings dataTables settings object
2886 * @returns array {array} aData Master data array
2887 * @memberof DataTable#oApi
2888 */
2889 function _fnGetDataMaster ( settings )
2890 {
2891 return _pluck( settings.aoData, '_aData' );
2892 }
2893
2894
2895 /**
2896 * Nuke the table
2897 * @param {object} oSettings dataTables settings object
2898 * @memberof DataTable#oApi
2899 */
2900 function _fnClearTable( settings )
2901 {
2902 settings.aoData.length = 0;
2903 settings.aiDisplayMaster.length = 0;
2904 settings.aiDisplay.length = 0;
2905 settings.aIds = {};
2906 }
2907
2908
2909 /**
2910 * Take an array of integers (index array) and remove a target integer (value - not
2911 * the key!)
2912 * @param {array} a Index array to target
2913 * @param {int} iTarget value to find
2914 * @memberof DataTable#oApi
2915 */
2916 function _fnDeleteIndex( a, iTarget, splice )
2917 {
2918 var iTargetIndex = -1;
2919
2920 for ( var i=0, iLen=a.length ; i<iLen ; i++ )
2921 {
2922 if ( a[i] == iTarget )
2923 {
2924 iTargetIndex = i;
2925 }
2926 else if ( a[i] > iTarget )
2927 {
2928 a[i]--;
2929 }
2930 }
2931
2932 if ( iTargetIndex != -1 && splice === undefined )
2933 {
2934 a.splice( iTargetIndex, 1 );
2935 }
2936 }
2937
2938
2939 /**
2940 * Mark cached data as invalid such that a re-read of the data will occur when
2941 * the cached data is next requested. Also update from the data source object.
2942 *
2943 * @param {object} settings DataTables settings object
2944 * @param {int} rowIdx Row index to invalidate
2945 * @param {string} [src] Source to invalidate from: undefined, 'auto', 'dom'
2946 * or 'data'
2947 * @param {int} [colIdx] Column index to invalidate. If undefined the whole
2948 * row will be invalidated
2949 * @memberof DataTable#oApi
2950 *
2951 * @todo For the modularisation of v1.11 this will need to become a callback, so
2952 * the sort and filter methods can subscribe to it. That will required
2953 * initialisation options for sorting, which is why it is not already baked in
2954 */
2955 function _fnInvalidate( settings, rowIdx, src, colIdx )
2956 {
2957 var row = settings.aoData[ rowIdx ];
2958 var i, ien;
2959 var cellWrite = function ( cell, col ) {
2960 // This is very frustrating, but in IE if you just write directly
2961 // to innerHTML, and elements that are overwritten are GC'ed,
2962 // even if there is a reference to them elsewhere
2963 while ( cell.childNodes.length ) {
2964 cell.removeChild( cell.firstChild );
2965 }
2966
2967 cell.innerHTML = _fnGetCellData( settings, rowIdx, col, 'display' );
2968 };
2969
2970 // Are we reading last data from DOM or the data object?
2971 if ( src === 'dom' || ((! src || src === 'auto') && row.src === 'dom') ) {
2972 // Read the data from the DOM
2973 row._aData = _fnGetRowElements(
2974 settings, row, colIdx, colIdx === undefined ? undefined : row._aData
2975 )
2976 .data;
2977 }
2978 else {
2979 // Reading from data object, update the DOM
2980 var cells = row.anCells;
2981
2982 if ( cells ) {
2983 if ( colIdx !== undefined ) {
2984 cellWrite( cells[colIdx], colIdx );
2985 }
2986 else {
2987 for ( i=0, ien=cells.length ; i<ien ; i++ ) {
2988 cellWrite( cells[i], i );
2989 }
2990 }
2991 }
2992 }
2993
2994 // For both row and cell invalidation, the cached data for sorting and
2995 // filtering is nulled out
2996 row._aSortData = null;
2997 row._aFilterData = null;
2998
2999 // Invalidate the type for a specific column (if given) or all columns since
3000 // the data might have changed
3001 var cols = settings.aoColumns;
3002 if ( colIdx !== undefined ) {
3003 cols[ colIdx ].sType = null;
3004 }
3005 else {
3006 for ( i=0, ien=cols.length ; i<ien ; i++ ) {
3007 cols[i].sType = null;
3008 }
3009
3010 // Update DataTables special `DT_*` attributes for the row
3011 _fnRowAttributes( settings, row );
3012 }
3013 }
3014
3015
3016 /**
3017 * Build a data source object from an HTML row, reading the contents of the
3018 * cells that are in the row.
3019 *
3020 * @param {object} settings DataTables settings object
3021 * @param {node|object} TR element from which to read data or existing row
3022 * object from which to re-read the data from the cells
3023 * @param {int} [colIdx] Optional column index
3024 * @param {array|object} [d] Data source object. If `colIdx` is given then this
3025 * parameter should also be given and will be used to write the data into.
3026 * Only the column in question will be written
3027 * @returns {object} Object with two parameters: `data` the data read, in
3028 * document order, and `cells` and array of nodes (they can be useful to the
3029 * caller, so rather than needing a second traversal to get them, just return
3030 * them from here).
3031 * @memberof DataTable#oApi
3032 */
3033 function _fnGetRowElements( settings, row, colIdx, d )
3034 {
3035 var
3036 tds = [],
3037 td = row.firstChild,
3038 name, col, o, i=0, contents,
3039 columns = settings.aoColumns,
3040 objectRead = settings._rowReadObject;
3041
3042 // Allow the data object to be passed in, or construct
3043 d = d !== undefined ?
3044 d :
3045 objectRead ?
3046 {} :
3047 [];
3048
3049 var attr = function ( str, td ) {
3050 if ( typeof str === 'string' ) {
3051 var idx = str.indexOf('@');
3052
3053 if ( idx !== -1 ) {
3054 var attr = str.substring( idx+1 );
3055 var setter = _fnSetObjectDataFn( str );
3056 setter( d, td.getAttribute( attr ) );
3057 }
3058 }
3059 };
3060
3061 // Read data from a cell and store into the data object
3062 var cellProcess = function ( cell ) {
3063 if ( colIdx === undefined || colIdx === i ) {
3064 col = columns[i];
3065 contents = (cell.innerHTML).trim();
3066
3067 if ( col && col._bAttrSrc ) {
3068 var setter = _fnSetObjectDataFn( col.mData._ );
3069 setter( d, contents );
3070
3071 attr( col.mData.sort, cell );
3072 attr( col.mData.type, cell );
3073 attr( col.mData.filter, cell );
3074 }
3075 else {
3076 // Depending on the `data` option for the columns the data can
3077 // be read to either an object or an array.
3078 if ( objectRead ) {
3079 if ( ! col._setter ) {
3080 // Cache the setter function
3081 col._setter = _fnSetObjectDataFn( col.mData );
3082 }
3083 col._setter( d, contents );
3084 }
3085 else {
3086 d[i] = contents;
3087 }
3088 }
3089 }
3090
3091 i++;
3092 };
3093
3094 if ( td ) {
3095 // `tr` element was passed in
3096 while ( td ) {
3097 name = td.nodeName.toUpperCase();
3098
3099 if ( name == "TD" || name == "TH" ) {
3100 cellProcess( td );
3101 tds.push( td );
3102 }
3103
3104 td = td.nextSibling;
3105 }
3106 }
3107 else {
3108 // Existing row object passed in
3109 tds = row.anCells;
3110
3111 for ( var j=0, jen=tds.length ; j<jen ; j++ ) {
3112 cellProcess( tds[j] );
3113 }
3114 }
3115
3116 // Read the ID from the DOM if present
3117 var rowNode = row.firstChild ? row : row.nTr;
3118
3119 if ( rowNode ) {
3120 var id = rowNode.getAttribute( 'id' );
3121
3122 if ( id ) {
3123 _fnSetObjectDataFn( settings.rowId )( d, id );
3124 }
3125 }
3126
3127 return {
3128 data: d,
3129 cells: tds
3130 };
3131 }
3132 /**
3133 * Create a new TR element (and it's TD children) for a row
3134 * @param {object} oSettings dataTables settings object
3135 * @param {int} iRow Row to consider
3136 * @param {node} [nTrIn] TR element to add to the table - optional. If not given,
3137 * DataTables will create a row automatically
3138 * @param {array} [anTds] Array of TD|TH elements for the row - must be given
3139 * if nTr is.
3140 * @memberof DataTable#oApi
3141 */
3142 function _fnCreateTr ( oSettings, iRow, nTrIn, anTds )
3143 {
3144 var
3145 row = oSettings.aoData[iRow],
3146 rowData = row._aData,
3147 cells = [],
3148 nTr, nTd, oCol,
3149 i, iLen, create;
3150
3151 if ( row.nTr === null )
3152 {
3153 nTr = nTrIn || document.createElement('tr');
3154
3155 row.nTr = nTr;
3156 row.anCells = cells;
3157
3158 /* Use a private property on the node to allow reserve mapping from the node
3159 * to the aoData array for fast look up
3160 */
3161 nTr._DT_RowIndex = iRow;
3162
3163 /* Special parameters can be given by the data source to be used on the row */
3164 _fnRowAttributes( oSettings, row );
3165
3166 /* Process each column */
3167 for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
3168 {
3169 oCol = oSettings.aoColumns[i];
3170 create = nTrIn ? false : true;
3171
3172 nTd = create ? document.createElement( oCol.sCellType ) : anTds[i];
3173
3174 if (! nTd) {
3175 _fnLog( oSettings, 0, 'Incorrect column count', 18 );
3176 }
3177
3178 nTd._DT_CellIndex = {
3179 row: iRow,
3180 column: i
3181 };
3182
3183 cells.push( nTd );
3184
3185 // Need to create the HTML if new, or if a rendering function is defined
3186 if ( create || ((oCol.mRender || oCol.mData !== i) &&
3187 (!$.isPlainObject(oCol.mData) || oCol.mData._ !== i+'.display')
3188 )) {
3189 nTd.innerHTML = _fnGetCellData( oSettings, iRow, i, 'display' );
3190 }
3191
3192 /* Add user defined class */
3193 if ( oCol.sClass )
3194 {
3195 nTd.className += ' '+oCol.sClass;
3196 }
3197
3198 // Visibility - add or remove as required
3199 if ( oCol.bVisible && ! nTrIn )
3200 {
3201 nTr.appendChild( nTd );
3202 }
3203 else if ( ! oCol.bVisible && nTrIn )
3204 {
3205 nTd.parentNode.removeChild( nTd );
3206 }
3207
3208 if ( oCol.fnCreatedCell )
3209 {
3210 oCol.fnCreatedCell.call( oSettings.oInstance,
3211 nTd, _fnGetCellData( oSettings, iRow, i ), rowData, iRow, i
3212 );
3213 }
3214 }
3215
3216 _fnCallbackFire( oSettings, 'aoRowCreatedCallback', null, [nTr, rowData, iRow, cells] );
3217 }
3218 }
3219
3220
3221 /**
3222 * Add attributes to a row based on the special `DT_*` parameters in a data
3223 * source object.
3224 * @param {object} settings DataTables settings object
3225 * @param {object} DataTables row object for the row to be modified
3226 * @memberof DataTable#oApi
3227 */
3228 function _fnRowAttributes( settings, row )
3229 {
3230 var tr = row.nTr;
3231 var data = row._aData;
3232
3233 if ( tr ) {
3234 var id = settings.rowIdFn( data );
3235
3236 if ( id ) {
3237 tr.id = id;
3238 }
3239
3240 if ( data.DT_RowClass ) {
3241 // Remove any classes added by DT_RowClass before
3242 var a = data.DT_RowClass.split(' ');
3243 row.__rowc = row.__rowc ?
3244 _unique( row.__rowc.concat( a ) ) :
3245 a;
3246
3247 $(tr)
3248 .removeClass( row.__rowc.join(' ') )
3249 .addClass( data.DT_RowClass );
3250 }
3251
3252 if ( data.DT_RowAttr ) {
3253 $(tr).attr( data.DT_RowAttr );
3254 }
3255
3256 if ( data.DT_RowData ) {
3257 $(tr).data( data.DT_RowData );
3258 }
3259 }
3260 }
3261
3262
3263 /**
3264 * Create the HTML header for the table
3265 * @param {object} oSettings dataTables settings object
3266 * @memberof DataTable#oApi
3267 */
3268 function _fnBuildHead( oSettings )
3269 {
3270 var i, ien, cell, row, column;
3271 var thead = oSettings.nTHead;
3272 var tfoot = oSettings.nTFoot;
3273 var createHeader = $('th, td', thead).length === 0;
3274 var classes = oSettings.oClasses;
3275 var columns = oSettings.aoColumns;
3276
3277 if ( createHeader ) {
3278 row = $('<tr/>').appendTo( thead );
3279 }
3280
3281 for ( i=0, ien=columns.length ; i<ien ; i++ ) {
3282 column = columns[i];
3283 cell = $( column.nTh ).addClass( column.sClass );
3284
3285 if ( createHeader ) {
3286 cell.appendTo( row );
3287 }
3288
3289 // 1.11 move into sorting
3290 if ( oSettings.oFeatures.bSort ) {
3291 cell.addClass( column.sSortingClass );
3292
3293 if ( column.bSortable !== false ) {
3294 cell
3295 .attr( 'tabindex', oSettings.iTabIndex )
3296 .attr( 'aria-controls', oSettings.sTableId );
3297
3298 _fnSortAttachListener( oSettings, column.nTh, i );
3299 }
3300 }
3301
3302 if ( column.sTitle != cell[0].innerHTML ) {
3303 cell.html( column.sTitle );
3304 }
3305
3306 _fnRenderer( oSettings, 'header' )(
3307 oSettings, cell, column, classes
3308 );
3309 }
3310
3311 if ( createHeader ) {
3312 _fnDetectHeader( oSettings.aoHeader, thead );
3313 }
3314
3315 /* Deal with the footer - add classes if required */
3316 $(thead).children('tr').children('th, td').addClass( classes.sHeaderTH );
3317 $(tfoot).children('tr').children('th, td').addClass( classes.sFooterTH );
3318
3319 // Cache the footer cells. Note that we only take the cells from the first
3320 // row in the footer. If there is more than one row the user wants to
3321 // interact with, they need to use the table().foot() method. Note also this
3322 // allows cells to be used for multiple columns using colspan
3323 if ( tfoot !== null ) {
3324 var cells = oSettings.aoFooter[0];
3325
3326 for ( i=0, ien=cells.length ; i<ien ; i++ ) {
3327 column = columns[i];
3328
3329 if (column) {
3330 column.nTf = cells[i].cell;
3331
3332 if ( column.sClass ) {
3333 $(column.nTf).addClass( column.sClass );
3334 }
3335 }
3336 else {
3337 _fnLog( oSettings, 0, 'Incorrect column count', 18 );
3338 }
3339 }
3340 }
3341 }
3342
3343
3344 /**
3345 * Draw the header (or footer) element based on the column visibility states. The
3346 * methodology here is to use the layout array from _fnDetectHeader, modified for
3347 * the instantaneous column visibility, to construct the new layout. The grid is
3348 * traversed over cell at a time in a rows x columns grid fashion, although each
3349 * cell insert can cover multiple elements in the grid - which is tracks using the
3350 * aApplied array. Cell inserts in the grid will only occur where there isn't
3351 * already a cell in that position.
3352 * @param {object} oSettings dataTables settings object
3353 * @param array {objects} aoSource Layout array from _fnDetectHeader
3354 * @param {boolean} [bIncludeHidden=false] If true then include the hidden columns in the calc,
3355 * @memberof DataTable#oApi
3356 */
3357 function _fnDrawHead( oSettings, aoSource, bIncludeHidden )
3358 {
3359 var i, iLen, j, jLen, k, kLen, n, nLocalTr;
3360 var aoLocal = [];
3361 var aApplied = [];
3362 var iColumns = oSettings.aoColumns.length;
3363 var iRowspan, iColspan;
3364
3365 if ( ! aoSource )
3366 {
3367 return;
3368 }
3369
3370 if ( bIncludeHidden === undefined )
3371 {
3372 bIncludeHidden = false;
3373 }
3374
3375 /* Make a copy of the master layout array, but without the visible columns in it */
3376 for ( i=0, iLen=aoSource.length ; i<iLen ; i++ )
3377 {
3378 aoLocal[i] = aoSource[i].slice();
3379 aoLocal[i].nTr = aoSource[i].nTr;
3380
3381 /* Remove any columns which are currently hidden */
3382 for ( j=iColumns-1 ; j>=0 ; j-- )
3383 {
3384 if ( !oSettings.aoColumns[j].bVisible && !bIncludeHidden )
3385 {
3386 aoLocal[i].splice( j, 1 );
3387 }
3388 }
3389
3390 /* Prep the applied array - it needs an element for each row */
3391 aApplied.push( [] );
3392 }
3393
3394 for ( i=0, iLen=aoLocal.length ; i<iLen ; i++ )
3395 {
3396 nLocalTr = aoLocal[i].nTr;
3397
3398 /* All cells are going to be replaced, so empty out the row */
3399 if ( nLocalTr )
3400 {
3401 while( (n = nLocalTr.firstChild) )
3402 {
3403 nLocalTr.removeChild( n );
3404 }
3405 }
3406
3407 for ( j=0, jLen=aoLocal[i].length ; j<jLen ; j++ )
3408 {
3409 iRowspan = 1;
3410 iColspan = 1;
3411
3412 /* Check to see if there is already a cell (row/colspan) covering our target
3413 * insert point. If there is, then there is nothing to do.
3414 */
3415 if ( aApplied[i][j] === undefined )
3416 {
3417 nLocalTr.appendChild( aoLocal[i][j].cell );
3418 aApplied[i][j] = 1;
3419
3420 /* Expand the cell to cover as many rows as needed */
3421 while ( aoLocal[i+iRowspan] !== undefined &&
3422 aoLocal[i][j].cell == aoLocal[i+iRowspan][j].cell )
3423 {
3424 aApplied[i+iRowspan][j] = 1;
3425 iRowspan++;
3426 }
3427
3428 /* Expand the cell to cover as many columns as needed */
3429 while ( aoLocal[i][j+iColspan] !== undefined &&
3430 aoLocal[i][j].cell == aoLocal[i][j+iColspan].cell )
3431 {
3432 /* Must update the applied array over the rows for the columns */
3433 for ( k=0 ; k<iRowspan ; k++ )
3434 {
3435 aApplied[i+k][j+iColspan] = 1;
3436 }
3437 iColspan++;
3438 }
3439
3440 /* Do the actual expansion in the DOM */
3441 $(aoLocal[i][j].cell)
3442 .attr('rowspan', iRowspan)
3443 .attr('colspan', iColspan);
3444 }
3445 }
3446 }
3447 }
3448
3449
3450 /**
3451 * Insert the required TR nodes into the table for display
3452 * @param {object} oSettings dataTables settings object
3453 * @param ajaxComplete true after ajax call to complete rendering
3454 * @memberof DataTable#oApi
3455 */
3456 function _fnDraw( oSettings, ajaxComplete )
3457 {
3458 // Allow for state saving and a custom start position
3459 _fnStart( oSettings );
3460
3461 /* Provide a pre-callback function which can be used to cancel the draw is false is returned */
3462 var aPreDraw = _fnCallbackFire( oSettings, 'aoPreDrawCallback', 'preDraw', [oSettings] );
3463 if ( $.inArray( false, aPreDraw ) !== -1 )
3464 {
3465 _fnProcessingDisplay( oSettings, false );
3466 return;
3467 }
3468
3469 var anRows = [];
3470 var iRowCount = 0;
3471 var asStripeClasses = oSettings.asStripeClasses;
3472 var iStripes = asStripeClasses.length;
3473 var oLang = oSettings.oLanguage;
3474 var bServerSide = _fnDataSource( oSettings ) == 'ssp';
3475 var aiDisplay = oSettings.aiDisplay;
3476 var iDisplayStart = oSettings._iDisplayStart;
3477 var iDisplayEnd = oSettings.fnDisplayEnd();
3478
3479 oSettings.bDrawing = true;
3480
3481 /* Server-side processing draw intercept */
3482 if ( oSettings.bDeferLoading )
3483 {
3484 oSettings.bDeferLoading = false;
3485 oSettings.iDraw++;
3486 _fnProcessingDisplay( oSettings, false );
3487 }
3488 else if ( !bServerSide )
3489 {
3490 oSettings.iDraw++;
3491 }
3492 else if ( !oSettings.bDestroying && !ajaxComplete)
3493 {
3494 _fnAjaxUpdate( oSettings );
3495 return;
3496 }
3497
3498 if ( aiDisplay.length !== 0 )
3499 {
3500 var iStart = bServerSide ? 0 : iDisplayStart;
3501 var iEnd = bServerSide ? oSettings.aoData.length : iDisplayEnd;
3502
3503 for ( var j=iStart ; j<iEnd ; j++ )
3504 {
3505 var iDataIndex = aiDisplay[j];
3506 var aoData = oSettings.aoData[ iDataIndex ];
3507 if ( aoData.nTr === null )
3508 {
3509 _fnCreateTr( oSettings, iDataIndex );
3510 }
3511
3512 var nRow = aoData.nTr;
3513
3514 /* Remove the old striping classes and then add the new one */
3515 if ( iStripes !== 0 )
3516 {
3517 var sStripe = asStripeClasses[ iRowCount % iStripes ];
3518 if ( aoData._sRowStripe != sStripe )
3519 {
3520 $(nRow).removeClass( aoData._sRowStripe ).addClass( sStripe );
3521 aoData._sRowStripe = sStripe;
3522 }
3523 }
3524
3525 // Row callback functions - might want to manipulate the row
3526 // iRowCount and j are not currently documented. Are they at all
3527 // useful?
3528 _fnCallbackFire( oSettings, 'aoRowCallback', null,
3529 [nRow, aoData._aData, iRowCount, j, iDataIndex] );
3530
3531 anRows.push( nRow );
3532 iRowCount++;
3533 }
3534 }
3535 else
3536 {
3537 /* Table is empty - create a row with an empty message in it */
3538 var sZero = oLang.sZeroRecords;
3539 if ( oSettings.iDraw == 1 && _fnDataSource( oSettings ) == 'ajax' )
3540 {
3541 sZero = oLang.sLoadingRecords;
3542 }
3543 else if ( oLang.sEmptyTable && oSettings.fnRecordsTotal() === 0 )
3544 {
3545 sZero = oLang.sEmptyTable;
3546 }
3547
3548 anRows[ 0 ] = $( '<tr/>', { 'class': iStripes ? asStripeClasses[0] : '' } )
3549 .append( $('<td />', {
3550 'valign': 'top',
3551 'colSpan': _fnVisbleColumns( oSettings ),
3552 'class': oSettings.oClasses.sRowEmpty
3553 } ).html( sZero ) )[0];
3554 }
3555
3556 /* Header and footer callbacks */
3557 _fnCallbackFire( oSettings, 'aoHeaderCallback', 'header', [ $(oSettings.nTHead).children('tr')[0],
3558 _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );
3559
3560 _fnCallbackFire( oSettings, 'aoFooterCallback', 'footer', [ $(oSettings.nTFoot).children('tr')[0],
3561 _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );
3562
3563 var body = $(oSettings.nTBody);
3564
3565 body.children().detach();
3566 body.append( $(anRows) );
3567
3568 /* Call all required callback functions for the end of a draw */
3569 _fnCallbackFire( oSettings, 'aoDrawCallback', 'draw', [oSettings] );
3570
3571 /* Draw is complete, sorting and filtering must be as well */
3572 oSettings.bSorted = false;
3573 oSettings.bFiltered = false;
3574 oSettings.bDrawing = false;
3575 }
3576
3577
3578 /**
3579 * Redraw the table - taking account of the various features which are enabled
3580 * @param {object} oSettings dataTables settings object
3581 * @param {boolean} [holdPosition] Keep the current paging position. By default
3582 * the paging is reset to the first page
3583 * @memberof DataTable#oApi
3584 */
3585 function _fnReDraw( settings, holdPosition )
3586 {
3587 var
3588 features = settings.oFeatures,
3589 sort = features.bSort,
3590 filter = features.bFilter;
3591
3592 if ( sort ) {
3593 _fnSort( settings );
3594 }
3595
3596 if ( filter ) {
3597 _fnFilterComplete( settings, settings.oPreviousSearch );
3598 }
3599 else {
3600 // No filtering, so we want to just use the display master
3601 settings.aiDisplay = settings.aiDisplayMaster.slice();
3602 }
3603
3604 if ( holdPosition !== true ) {
3605 settings._iDisplayStart = 0;
3606 }
3607
3608 // Let any modules know about the draw hold position state (used by
3609 // scrolling internally)
3610 settings._drawHold = holdPosition;
3611
3612 _fnDraw( settings );
3613
3614 settings._drawHold = false;
3615 }
3616
3617
3618 /**
3619 * Add the options to the page HTML for the table
3620 * @param {object} oSettings dataTables settings object
3621 * @memberof DataTable#oApi
3622 */
3623 function _fnAddOptionsHtml ( oSettings )
3624 {
3625 var classes = oSettings.oClasses;
3626 var table = $(oSettings.nTable);
3627 var holding = $('<div/>').insertBefore( table ); // Holding element for speed
3628 var features = oSettings.oFeatures;
3629
3630 // All DataTables are wrapped in a div
3631 var insert = $('<div/>', {
3632 id: oSettings.sTableId+'_wrapper',
3633 'class': classes.sWrapper + (oSettings.nTFoot ? '' : ' '+classes.sNoFooter)
3634 } );
3635
3636 oSettings.nHolding = holding[0];
3637 oSettings.nTableWrapper = insert[0];
3638 oSettings.nTableReinsertBefore = oSettings.nTable.nextSibling;
3639
3640 /* Loop over the user set positioning and place the elements as needed */
3641 var aDom = oSettings.sDom.split('');
3642 var featureNode, cOption, nNewNode, cNext, sAttr, j;
3643 for ( var i=0 ; i<aDom.length ; i++ )
3644 {
3645 featureNode = null;
3646 cOption = aDom[i];
3647
3648 if ( cOption == '<' )
3649 {
3650 /* New container div */
3651 nNewNode = $('<div/>')[0];
3652
3653 /* Check to see if we should append an id and/or a class name to the container */
3654 cNext = aDom[i+1];
3655 if ( cNext == "'" || cNext == '"' )
3656 {
3657 sAttr = "";
3658 j = 2;
3659 while ( aDom[i+j] != cNext )
3660 {
3661 sAttr += aDom[i+j];
3662 j++;
3663 }
3664
3665 /* Replace jQuery UI constants @todo depreciated */
3666 if ( sAttr == "H" )
3667 {
3668 sAttr = classes.sJUIHeader;
3669 }
3670 else if ( sAttr == "F" )
3671 {
3672 sAttr = classes.sJUIFooter;
3673 }
3674
3675 /* The attribute can be in the format of "#id.class", "#id" or "class" This logic
3676 * breaks the string into parts and applies them as needed
3677 */
3678 if ( sAttr.indexOf('.') != -1 )
3679 {
3680 var aSplit = sAttr.split('.');
3681 nNewNode.id = aSplit[0].substr(1, aSplit[0].length-1);
3682 nNewNode.className = aSplit[1];
3683 }
3684 else if ( sAttr.charAt(0) == "#" )
3685 {
3686 nNewNode.id = sAttr.substr(1, sAttr.length-1);
3687 }
3688 else
3689 {
3690 nNewNode.className = sAttr;
3691 }
3692
3693 i += j; /* Move along the position array */
3694 }
3695
3696 insert.append( nNewNode );
3697 insert = $(nNewNode);
3698 }
3699 else if ( cOption == '>' )
3700 {
3701 /* End container div */
3702 insert = insert.parent();
3703 }
3704 // @todo Move options into their own plugins?
3705 else if ( cOption == 'l' && features.bPaginate && features.bLengthChange )
3706 {
3707 /* Length */
3708 featureNode = _fnFeatureHtmlLength( oSettings );
3709 }
3710 else if ( cOption == 'f' && features.bFilter )
3711 {
3712 /* Filter */
3713 featureNode = _fnFeatureHtmlFilter( oSettings );
3714 }
3715 else if ( cOption == 'r' && features.bProcessing )
3716 {
3717 /* pRocessing */
3718 featureNode = _fnFeatureHtmlProcessing( oSettings );
3719 }
3720 else if ( cOption == 't' )
3721 {
3722 /* Table */
3723 featureNode = _fnFeatureHtmlTable( oSettings );
3724 }
3725 else if ( cOption == 'i' && features.bInfo )
3726 {
3727 /* Info */
3728 featureNode = _fnFeatureHtmlInfo( oSettings );
3729 }
3730 else if ( cOption == 'p' && features.bPaginate )
3731 {
3732 /* Pagination */
3733 featureNode = _fnFeatureHtmlPaginate( oSettings );
3734 }
3735 else if ( DataTable.ext.feature.length !== 0 )
3736 {
3737 /* Plug-in features */
3738 var aoFeatures = DataTable.ext.feature;
3739 for ( var k=0, kLen=aoFeatures.length ; k<kLen ; k++ )
3740 {
3741 if ( cOption == aoFeatures[k].cFeature )
3742 {
3743 featureNode = aoFeatures[k].fnInit( oSettings );
3744 break;
3745 }
3746 }
3747 }
3748
3749 /* Add to the 2D features array */
3750 if ( featureNode )
3751 {
3752 var aanFeatures = oSettings.aanFeatures;
3753
3754 if ( ! aanFeatures[cOption] )
3755 {
3756 aanFeatures[cOption] = [];
3757 }
3758
3759 aanFeatures[cOption].push( featureNode );
3760 insert.append( featureNode );
3761 }
3762 }
3763
3764 /* Built our DOM structure - replace the holding div with what we want */
3765 holding.replaceWith( insert );
3766 oSettings.nHolding = null;
3767 }
3768
3769
3770 /**
3771 * Use the DOM source to create up an array of header cells. The idea here is to
3772 * create a layout grid (array) of rows x columns, which contains a reference
3773 * to the cell that that point in the grid (regardless of col/rowspan), such that
3774 * any column / row could be removed and the new grid constructed
3775 * @param array {object} aLayout Array to store the calculated layout in
3776 * @param {node} nThead The header/footer element for the table
3777 * @memberof DataTable#oApi
3778 */
3779 function _fnDetectHeader ( aLayout, nThead )
3780 {
3781 var nTrs = $(nThead).children('tr');
3782 var nTr, nCell;
3783 var i, k, l, iLen, jLen, iColShifted, iColumn, iColspan, iRowspan;
3784 var bUnique;
3785 var fnShiftCol = function ( a, i, j ) {
3786 var k = a[i];
3787 while ( k[j] ) {
3788 j++;
3789 }
3790 return j;
3791 };
3792
3793 aLayout.splice( 0, aLayout.length );
3794
3795 /* We know how many rows there are in the layout - so prep it */
3796 for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
3797 {
3798 aLayout.push( [] );
3799 }
3800
3801 /* Calculate a layout array */
3802 for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
3803 {
3804 nTr = nTrs[i];
3805 iColumn = 0;
3806
3807 /* For every cell in the row... */
3808 nCell = nTr.firstChild;
3809 while ( nCell ) {
3810 if ( nCell.nodeName.toUpperCase() == "TD" ||
3811 nCell.nodeName.toUpperCase() == "TH" )
3812 {
3813 /* Get the col and rowspan attributes from the DOM and sanitise them */
3814 iColspan = nCell.getAttribute('colspan') * 1;
3815 iRowspan = nCell.getAttribute('rowspan') * 1;
3816 iColspan = (!iColspan || iColspan===0 || iColspan===1) ? 1 : iColspan;
3817 iRowspan = (!iRowspan || iRowspan===0 || iRowspan===1) ? 1 : iRowspan;
3818
3819 /* There might be colspan cells already in this row, so shift our target
3820 * accordingly
3821 */
3822 iColShifted = fnShiftCol( aLayout, i, iColumn );
3823
3824 /* Cache calculation for unique columns */
3825 bUnique = iColspan === 1 ? true : false;
3826
3827 /* If there is col / rowspan, copy the information into the layout grid */
3828 for ( l=0 ; l<iColspan ; l++ )
3829 {
3830 for ( k=0 ; k<iRowspan ; k++ )
3831 {
3832 aLayout[i+k][iColShifted+l] = {
3833 "cell": nCell,
3834 "unique": bUnique
3835 };
3836 aLayout[i+k].nTr = nTr;
3837 }
3838 }
3839 }
3840 nCell = nCell.nextSibling;
3841 }
3842 }
3843 }
3844
3845
3846 /**
3847 * Get an array of unique th elements, one for each column
3848 * @param {object} oSettings dataTables settings object
3849 * @param {node} nHeader automatically detect the layout from this node - optional
3850 * @param {array} aLayout thead/tfoot layout from _fnDetectHeader - optional
3851 * @returns array {node} aReturn list of unique th's
3852 * @memberof DataTable#oApi
3853 */
3854 function _fnGetUniqueThs ( oSettings, nHeader, aLayout )
3855 {
3856 var aReturn = [];
3857 if ( !aLayout )
3858 {
3859 aLayout = oSettings.aoHeader;
3860 if ( nHeader )
3861 {
3862 aLayout = [];
3863 _fnDetectHeader( aLayout, nHeader );
3864 }
3865 }
3866
3867 for ( var i=0, iLen=aLayout.length ; i<iLen ; i++ )
3868 {
3869 for ( var j=0, jLen=aLayout[i].length ; j<jLen ; j++ )
3870 {
3871 if ( aLayout[i][j].unique &&
3872 (!aReturn[j] || !oSettings.bSortCellsTop) )
3873 {
3874 aReturn[j] = aLayout[i][j].cell;
3875 }
3876 }
3877 }
3878
3879 return aReturn;
3880 }
3881
3882 /**
3883 * Set the start position for draw
3884 * @param {object} oSettings dataTables settings object
3885 */
3886 function _fnStart( oSettings )
3887 {
3888 var bServerSide = _fnDataSource( oSettings ) == 'ssp';
3889 var iInitDisplayStart = oSettings.iInitDisplayStart;
3890
3891 // Check and see if we have an initial draw position from state saving
3892 if ( iInitDisplayStart !== undefined && iInitDisplayStart !== -1 )
3893 {
3894 oSettings._iDisplayStart = bServerSide ?
3895 iInitDisplayStart :
3896 iInitDisplayStart >= oSettings.fnRecordsDisplay() ?
3897 0 :
3898 iInitDisplayStart;
3899
3900 oSettings.iInitDisplayStart = -1;
3901 }
3902 }
3903
3904 /**
3905 * Create an Ajax call based on the table's settings, taking into account that
3906 * parameters can have multiple forms, and backwards compatibility.
3907 *
3908 * @param {object} oSettings dataTables settings object
3909 * @param {array} data Data to send to the server, required by
3910 * DataTables - may be augmented by developer callbacks
3911 * @param {function} fn Callback function to run when data is obtained
3912 */
3913 function _fnBuildAjax( oSettings, data, fn )
3914 {
3915 // Compatibility with 1.9-, allow fnServerData and event to manipulate
3916 _fnCallbackFire( oSettings, 'aoServerParams', 'serverParams', [data] );
3917
3918 // Convert to object based for 1.10+ if using the old array scheme which can
3919 // come from server-side processing or serverParams
3920 if ( data && Array.isArray(data) ) {
3921 var tmp = {};
3922 var rbracket = /(.*?)\[\]$/;
3923
3924 $.each( data, function (key, val) {
3925 var match = val.name.match(rbracket);
3926
3927 if ( match ) {
3928 // Support for arrays
3929 var name = match[0];
3930
3931 if ( ! tmp[ name ] ) {
3932 tmp[ name ] = [];
3933 }
3934 tmp[ name ].push( val.value );
3935 }
3936 else {
3937 tmp[val.name] = val.value;
3938 }
3939 } );
3940 data = tmp;
3941 }
3942
3943 var ajaxData;
3944 var ajax = oSettings.ajax;
3945 var instance = oSettings.oInstance;
3946 var callback = function ( json ) {
3947 var status = oSettings.jqXHR
3948 ? oSettings.jqXHR.status
3949 : null;
3950
3951 if ( json === null || (typeof status === 'number' && status == 204 ) ) {
3952 json = {};
3953 _fnAjaxDataSrc( oSettings, json, [] );
3954 }
3955
3956 var error = json.error || json.sError;
3957 if ( error ) {
3958 _fnLog( oSettings, 0, error );
3959 }
3960
3961 oSettings.json = json;
3962
3963 _fnCallbackFire( oSettings, null, 'xhr', [oSettings, json, oSettings.jqXHR] );
3964 fn( json );
3965 };
3966
3967 if ( $.isPlainObject( ajax ) && ajax.data )
3968 {
3969 ajaxData = ajax.data;
3970
3971 var newData = typeof ajaxData === 'function' ?
3972 ajaxData( data, oSettings ) : // fn can manipulate data or return
3973 ajaxData; // an object object or array to merge
3974
3975 // If the function returned something, use that alone
3976 data = typeof ajaxData === 'function' && newData ?
3977 newData :
3978 $.extend( true, data, newData );
3979
3980 // Remove the data property as we've resolved it already and don't want
3981 // jQuery to do it again (it is restored at the end of the function)
3982 delete ajax.data;
3983 }
3984
3985 var baseAjax = {
3986 "data": data,
3987 "success": callback,
3988 "dataType": "json",
3989 "cache": false,
3990 "type": oSettings.sServerMethod,
3991 "error": function (xhr, error, thrown) {
3992 var ret = _fnCallbackFire( oSettings, null, 'xhr', [oSettings, null, oSettings.jqXHR] );
3993
3994 if ( $.inArray( true, ret ) === -1 ) {
3995 if ( error == "parsererror" ) {
3996 _fnLog( oSettings, 0, 'Invalid JSON response', 1 );
3997 }
3998 else if ( xhr.readyState === 4 ) {
3999 _fnLog( oSettings, 0, 'Ajax error', 7 );
4000 }
4001 }
4002
4003 _fnProcessingDisplay( oSettings, false );
4004 }
4005 };
4006
4007 // Store the data submitted for the API
4008 oSettings.oAjaxData = data;
4009
4010 // Allow plug-ins and external processes to modify the data
4011 _fnCallbackFire( oSettings, null, 'preXhr', [oSettings, data] );
4012
4013 if ( oSettings.fnServerData )
4014 {
4015 // DataTables 1.9- compatibility
4016 oSettings.fnServerData.call( instance,
4017 oSettings.sAjaxSource,
4018 $.map( data, function (val, key) { // Need to convert back to 1.9 trad format
4019 return { name: key, value: val };
4020 } ),
4021 callback,
4022 oSettings
4023 );
4024 }
4025 else if ( oSettings.sAjaxSource || typeof ajax === 'string' )
4026 {
4027 // DataTables 1.9- compatibility
4028 oSettings.jqXHR = $.ajax( $.extend( baseAjax, {
4029 url: ajax || oSettings.sAjaxSource
4030 } ) );
4031 }
4032 else if ( typeof ajax === 'function' )
4033 {
4034 // Is a function - let the caller define what needs to be done
4035 oSettings.jqXHR = ajax.call( instance, data, callback, oSettings );
4036 }
4037 else
4038 {
4039 // Object to extend the base settings
4040 oSettings.jqXHR = $.ajax( $.extend( baseAjax, ajax ) );
4041
4042 // Restore for next time around
4043 ajax.data = ajaxData;
4044 }
4045 }
4046
4047
4048 /**
4049 * Update the table using an Ajax call
4050 * @param {object} settings dataTables settings object
4051 * @returns {boolean} Block the table drawing or not
4052 * @memberof DataTable#oApi
4053 */
4054 function _fnAjaxUpdate( settings )
4055 {
4056 settings.iDraw++;
4057 _fnProcessingDisplay( settings, true );
4058
4059 _fnBuildAjax(
4060 settings,
4061 _fnAjaxParameters( settings ),
4062 function(json) {
4063 _fnAjaxUpdateDraw( settings, json );
4064 }
4065 );
4066 }
4067
4068
4069 /**
4070 * Build up the parameters in an object needed for a server-side processing
4071 * request. Note that this is basically done twice, is different ways - a modern
4072 * method which is used by default in DataTables 1.10 which uses objects and
4073 * arrays, or the 1.9- method with is name / value pairs. 1.9 method is used if
4074 * the sAjaxSource option is used in the initialisation, or the legacyAjax
4075 * option is set.
4076 * @param {object} oSettings dataTables settings object
4077 * @returns {bool} block the table drawing or not
4078 * @memberof DataTable#oApi
4079 */
4080 function _fnAjaxParameters( settings )
4081 {
4082 var
4083 columns = settings.aoColumns,
4084 columnCount = columns.length,
4085 features = settings.oFeatures,
4086 preSearch = settings.oPreviousSearch,
4087 preColSearch = settings.aoPreSearchCols,
4088 i, data = [], dataProp, column, columnSearch,
4089 sort = _fnSortFlatten( settings ),
4090 displayStart = settings._iDisplayStart,
4091 displayLength = features.bPaginate !== false ?
4092 settings._iDisplayLength :
4093 -1;
4094
4095 var param = function ( name, value ) {
4096 data.push( { 'name': name, 'value': value } );
4097 };
4098
4099 // DataTables 1.9- compatible method
4100 param( 'sEcho', settings.iDraw );
4101 param( 'iColumns', columnCount );
4102 param( 'sColumns', _pluck( columns, 'sName' ).join(',') );
4103 param( 'iDisplayStart', displayStart );
4104 param( 'iDisplayLength', displayLength );
4105
4106 // DataTables 1.10+ method
4107 var d = {
4108 draw: settings.iDraw,
4109 columns: [],
4110 order: [],
4111 start: displayStart,
4112 length: displayLength,
4113 search: {
4114 value: preSearch.sSearch,
4115 regex: preSearch.bRegex
4116 }
4117 };
4118
4119 for ( i=0 ; i<columnCount ; i++ ) {
4120 column = columns[i];
4121 columnSearch = preColSearch[i];
4122 dataProp = typeof column.mData=="function" ? 'function' : column.mData ;
4123
4124 d.columns.push( {
4125 data: dataProp,
4126 name: column.sName,
4127 searchable: column.bSearchable,
4128 orderable: column.bSortable,
4129 search: {
4130 value: columnSearch.sSearch,
4131 regex: columnSearch.bRegex
4132 }
4133 } );
4134
4135 param( "mDataProp_"+i, dataProp );
4136
4137 if ( features.bFilter ) {
4138 param( 'sSearch_'+i, columnSearch.sSearch );
4139 param( 'bRegex_'+i, columnSearch.bRegex );
4140 param( 'bSearchable_'+i, column.bSearchable );
4141 }
4142
4143 if ( features.bSort ) {
4144 param( 'bSortable_'+i, column.bSortable );
4145 }
4146 }
4147
4148 if ( features.bFilter ) {
4149 param( 'sSearch', preSearch.sSearch );
4150 param( 'bRegex', preSearch.bRegex );
4151 }
4152
4153 if ( features.bSort ) {
4154 $.each( sort, function ( i, val ) {
4155 d.order.push( { column: val.col, dir: val.dir } );
4156
4157 param( 'iSortCol_'+i, val.col );
4158 param( 'sSortDir_'+i, val.dir );
4159 } );
4160
4161 param( 'iSortingCols', sort.length );
4162 }
4163
4164 // If the legacy.ajax parameter is null, then we automatically decide which
4165 // form to use, based on sAjaxSource
4166 var legacy = DataTable.ext.legacy.ajax;
4167 if ( legacy === null ) {
4168 return settings.sAjaxSource ? data : d;
4169 }
4170
4171 // Otherwise, if legacy has been specified then we use that to decide on the
4172 // form
4173 return legacy ? data : d;
4174 }
4175
4176
4177 /**
4178 * Data the data from the server (nuking the old) and redraw the table
4179 * @param {object} oSettings dataTables settings object
4180 * @param {object} json json data return from the server.
4181 * @param {string} json.sEcho Tracking flag for DataTables to match requests
4182 * @param {int} json.iTotalRecords Number of records in the data set, not accounting for filtering
4183 * @param {int} json.iTotalDisplayRecords Number of records in the data set, accounting for filtering
4184 * @param {array} json.aaData The data to display on this page
4185 * @param {string} [json.sColumns] Column ordering (sName, comma separated)
4186 * @memberof DataTable#oApi
4187 */
4188 function _fnAjaxUpdateDraw ( settings, json )
4189 {
4190 // v1.10 uses camelCase variables, while 1.9 uses Hungarian notation.
4191 // Support both
4192 var compat = function ( old, modern ) {
4193 return json[old] !== undefined ? json[old] : json[modern];
4194 };
4195
4196 var data = _fnAjaxDataSrc( settings, json );
4197 var draw = compat( 'sEcho', 'draw' );
4198 var recordsTotal = compat( 'iTotalRecords', 'recordsTotal' );
4199 var recordsFiltered = compat( 'iTotalDisplayRecords', 'recordsFiltered' );
4200
4201 if ( draw !== undefined ) {
4202 // Protect against out of sequence returns
4203 if ( draw*1 < settings.iDraw ) {
4204 return;
4205 }
4206 settings.iDraw = draw * 1;
4207 }
4208
4209 // No data in returned object, so rather than an array, we show an empty table
4210 if ( ! data ) {
4211 data = [];
4212 }
4213
4214 _fnClearTable( settings );
4215 settings._iRecordsTotal = parseInt(recordsTotal, 10);
4216 settings._iRecordsDisplay = parseInt(recordsFiltered, 10);
4217
4218 for ( var i=0, ien=data.length ; i<ien ; i++ ) {
4219 _fnAddData( settings, data[i] );
4220 }
4221 settings.aiDisplay = settings.aiDisplayMaster.slice();
4222
4223 _fnDraw( settings, true );
4224
4225 if ( ! settings._bInitComplete ) {
4226 _fnInitComplete( settings, json );
4227 }
4228
4229 _fnProcessingDisplay( settings, false );
4230 }
4231
4232
4233 /**
4234 * Get the data from the JSON data source to use for drawing a table. Using
4235 * `_fnGetObjectDataFn` allows the data to be sourced from a property of the
4236 * source object, or from a processing function.
4237 * @param {object} oSettings dataTables settings object
4238 * @param {object} json Data source object / array from the server
4239 * @return {array} Array of data to use
4240 */
4241 function _fnAjaxDataSrc ( oSettings, json, write )
4242 {
4243 var dataSrc = $.isPlainObject( oSettings.ajax ) && oSettings.ajax.dataSrc !== undefined ?
4244 oSettings.ajax.dataSrc :
4245 oSettings.sAjaxDataProp; // Compatibility with 1.9-.
4246
4247 if ( ! write ) {
4248 if ( dataSrc === 'data' ) {
4249 // If the default, then we still want to support the old style, and safely ignore
4250 // it if possible
4251 return json.aaData || json[dataSrc];
4252 }
4253
4254 return dataSrc !== "" ?
4255 _fnGetObjectDataFn( dataSrc )( json ) :
4256 json;
4257 }
4258
4259 // set
4260 _fnSetObjectDataFn( dataSrc )( json, write );
4261 }
4262
4263 /**
4264 * Generate the node required for filtering text
4265 * @returns {node} Filter control element
4266 * @param {object} oSettings dataTables settings object
4267 * @memberof DataTable#oApi
4268 */
4269 function _fnFeatureHtmlFilter ( settings )
4270 {
4271 var classes = settings.oClasses;
4272 var tableId = settings.sTableId;
4273 var language = settings.oLanguage;
4274 var previousSearch = settings.oPreviousSearch;
4275 var features = settings.aanFeatures;
4276 var input = '<input type="search" class="'+classes.sFilterInput+'"/>';
4277
4278 var str = language.sSearch;
4279 str = str.match(/_INPUT_/) ?
4280 str.replace('_INPUT_', input) :
4281 str+input;
4282
4283 var filter = $('<div/>', {
4284 'id': ! features.f ? tableId+'_filter' : null,
4285 'class': classes.sFilter
4286 } )
4287 .append( $('<label/>' ).append( str ) );
4288
4289 var searchFn = function(event) {
4290 /* Update all other filter input elements for the new display */
4291 var n = features.f;
4292 var val = !this.value ? "" : this.value; // mental IE8 fix :-(
4293 if(previousSearch.return && event.key !== "Enter") {
4294 return;
4295 }
4296 /* Now do the filter */
4297 if ( val != previousSearch.sSearch ) {
4298 _fnFilterComplete( settings, {
4299 "sSearch": val,
4300 "bRegex": previousSearch.bRegex,
4301 "bSmart": previousSearch.bSmart ,
4302 "bCaseInsensitive": previousSearch.bCaseInsensitive,
4303 "return": previousSearch.return
4304 } );
4305
4306 // Need to redraw, without resorting
4307 settings._iDisplayStart = 0;
4308 _fnDraw( settings );
4309 }
4310 };
4311
4312 var searchDelay = settings.searchDelay !== null ?
4313 settings.searchDelay :
4314 _fnDataSource( settings ) === 'ssp' ?
4315 400 :
4316 0;
4317
4318 var jqFilter = $('input', filter)
4319 .val( previousSearch.sSearch )
4320 .attr( 'placeholder', language.sSearchPlaceholder )
4321 .on(
4322 'keyup.DT search.DT input.DT paste.DT cut.DT',
4323 searchDelay ?
4324 _fnThrottle( searchFn, searchDelay ) :
4325 searchFn
4326 )
4327 .on( 'mouseup', function(e) {
4328 // Edge fix! Edge 17 does not trigger anything other than mouse events when clicking
4329 // on the clear icon (Edge bug 17584515). This is safe in other browsers as `searchFn`
4330 // checks the value to see if it has changed. In other browsers it won't have.
4331 setTimeout( function () {
4332 searchFn.call(jqFilter[0], e);
4333 }, 10);
4334 } )
4335 .on( 'keypress.DT', function(e) {
4336 /* Prevent form submission */
4337 if ( e.keyCode == 13 ) {
4338 return false;
4339 }
4340 } )
4341 .attr('aria-controls', tableId);
4342
4343 // Update the input elements whenever the table is filtered
4344 $(settings.nTable).on( 'search.dt.DT', function ( ev, s ) {
4345 if ( settings === s ) {
4346 // IE9 throws an 'unknown error' if document.activeElement is used
4347 // inside an iframe or frame...
4348 try {
4349 if ( jqFilter[0] !== document.activeElement ) {
4350 jqFilter.val( previousSearch.sSearch );
4351 }
4352 }
4353 catch ( e ) {}
4354 }
4355 } );
4356
4357 return filter[0];
4358 }
4359
4360
4361 /**
4362 * Filter the table using both the global filter and column based filtering
4363 * @param {object} oSettings dataTables settings object
4364 * @param {object} oSearch search information
4365 * @param {int} [iForce] force a research of the master array (1) or not (undefined or 0)
4366 * @memberof DataTable#oApi
4367 */
4368 function _fnFilterComplete ( oSettings, oInput, iForce )
4369 {
4370 var oPrevSearch = oSettings.oPreviousSearch;
4371 var aoPrevSearch = oSettings.aoPreSearchCols;
4372 var fnSaveFilter = function ( oFilter ) {
4373 /* Save the filtering values */
4374 oPrevSearch.sSearch = oFilter.sSearch;
4375 oPrevSearch.bRegex = oFilter.bRegex;
4376 oPrevSearch.bSmart = oFilter.bSmart;
4377 oPrevSearch.bCaseInsensitive = oFilter.bCaseInsensitive;
4378 oPrevSearch.return = oFilter.return;
4379 };
4380 var fnRegex = function ( o ) {
4381 // Backwards compatibility with the bEscapeRegex option
4382 return o.bEscapeRegex !== undefined ? !o.bEscapeRegex : o.bRegex;
4383 };
4384
4385 // Resolve any column types that are unknown due to addition or invalidation
4386 // @todo As per sort - can this be moved into an event handler?
4387 _fnColumnTypes( oSettings );
4388
4389 /* In server-side processing all filtering is done by the server, so no point hanging around here */
4390 if ( _fnDataSource( oSettings ) != 'ssp' )
4391 {
4392 /* Global filter */
4393 _fnFilter( oSettings, oInput.sSearch, iForce, fnRegex(oInput), oInput.bSmart, oInput.bCaseInsensitive, oInput.return );
4394 fnSaveFilter( oInput );
4395
4396 /* Now do the individual column filter */
4397 for ( var i=0 ; i<aoPrevSearch.length ; i++ )
4398 {
4399 _fnFilterColumn( oSettings, aoPrevSearch[i].sSearch, i, fnRegex(aoPrevSearch[i]),
4400 aoPrevSearch[i].bSmart, aoPrevSearch[i].bCaseInsensitive );
4401 }
4402
4403 /* Custom filtering */
4404 _fnFilterCustom( oSettings );
4405 }
4406 else
4407 {
4408 fnSaveFilter( oInput );
4409 }
4410
4411 /* Tell the draw function we have been filtering */
4412 oSettings.bFiltered = true;
4413 _fnCallbackFire( oSettings, null, 'search', [oSettings] );
4414 }
4415
4416
4417 /**
4418 * Apply custom filtering functions
4419 * @param {object} oSettings dataTables settings object
4420 * @memberof DataTable#oApi
4421 */
4422 function _fnFilterCustom( settings )
4423 {
4424 var filters = DataTable.ext.search;
4425 var displayRows = settings.aiDisplay;
4426 var row, rowIdx;
4427
4428 for ( var i=0, ien=filters.length ; i<ien ; i++ ) {
4429 var rows = [];
4430
4431 // Loop over each row and see if it should be included
4432 for ( var j=0, jen=displayRows.length ; j<jen ; j++ ) {
4433 rowIdx = displayRows[ j ];
4434 row = settings.aoData[ rowIdx ];
4435
4436 if ( filters[i]( settings, row._aFilterData, rowIdx, row._aData, j ) ) {
4437 rows.push( rowIdx );
4438 }
4439 }
4440
4441 // So the array reference doesn't break set the results into the
4442 // existing array
4443 displayRows.length = 0;
4444 $.merge( displayRows, rows );
4445 }
4446 }
4447
4448
4449 /**
4450 * Filter the table on a per-column basis
4451 * @param {object} oSettings dataTables settings object
4452 * @param {string} sInput string to filter on
4453 * @param {int} iColumn column to filter
4454 * @param {bool} bRegex treat search string as a regular expression or not
4455 * @param {bool} bSmart use smart filtering or not
4456 * @param {bool} bCaseInsensitive Do case insensitive matching or not
4457 * @memberof DataTable#oApi
4458 */
4459 function _fnFilterColumn ( settings, searchStr, colIdx, regex, smart, caseInsensitive )
4460 {
4461 if ( searchStr === '' ) {
4462 return;
4463 }
4464
4465 var data;
4466 var out = [];
4467 var display = settings.aiDisplay;
4468 var rpSearch = _fnFilterCreateSearch( searchStr, regex, smart, caseInsensitive );
4469
4470 for ( var i=0 ; i<display.length ; i++ ) {
4471 data = settings.aoData[ display[i] ]._aFilterData[ colIdx ];
4472
4473 if ( rpSearch.test( data ) ) {
4474 out.push( display[i] );
4475 }
4476 }
4477
4478 settings.aiDisplay = out;
4479 }
4480
4481
4482 /**
4483 * Filter the data table based on user input and draw the table
4484 * @param {object} settings dataTables settings object
4485 * @param {string} input string to filter on
4486 * @param {int} force optional - force a research of the master array (1) or not (undefined or 0)
4487 * @param {bool} regex treat as a regular expression or not
4488 * @param {bool} smart perform smart filtering or not
4489 * @param {bool} caseInsensitive Do case insensitive matching or not
4490 * @memberof DataTable#oApi
4491 */
4492 function _fnFilter( settings, input, force, regex, smart, caseInsensitive )
4493 {
4494 var rpSearch = _fnFilterCreateSearch( input, regex, smart, caseInsensitive );
4495 var prevSearch = settings.oPreviousSearch.sSearch;
4496 var displayMaster = settings.aiDisplayMaster;
4497 var display, invalidated, i;
4498 var filtered = [];
4499
4500 // Need to take account of custom filtering functions - always filter
4501 if ( DataTable.ext.search.length !== 0 ) {
4502 force = true;
4503 }
4504
4505 // Check if any of the rows were invalidated
4506 invalidated = _fnFilterData( settings );
4507
4508 // If the input is blank - we just want the full data set
4509 if ( input.length <= 0 ) {
4510 settings.aiDisplay = displayMaster.slice();
4511 }
4512 else {
4513 // New search - start from the master array
4514 if ( invalidated ||
4515 force ||
4516 regex ||
4517 prevSearch.length > input.length ||
4518 input.indexOf(prevSearch) !== 0 ||
4519 settings.bSorted // On resort, the display master needs to be
4520 // re-filtered since indexes will have changed
4521 ) {
4522 settings.aiDisplay = displayMaster.slice();
4523 }
4524
4525 // Search the display array
4526 display = settings.aiDisplay;
4527
4528 for ( i=0 ; i<display.length ; i++ ) {
4529 if ( rpSearch.test( settings.aoData[ display[i] ]._sFilterRow ) ) {
4530 filtered.push( display[i] );
4531 }
4532 }
4533
4534 settings.aiDisplay = filtered;
4535 }
4536 }
4537
4538
4539 /**
4540 * Build a regular expression object suitable for searching a table
4541 * @param {string} sSearch string to search for
4542 * @param {bool} bRegex treat as a regular expression or not
4543 * @param {bool} bSmart perform smart filtering or not
4544 * @param {bool} bCaseInsensitive Do case insensitive matching or not
4545 * @returns {RegExp} constructed object
4546 * @memberof DataTable#oApi
4547 */
4548 function _fnFilterCreateSearch( search, regex, smart, caseInsensitive )
4549 {
4550 search = regex ?
4551 search :
4552 _fnEscapeRegex( search );
4553
4554 if ( smart ) {
4555 /* For smart filtering we want to allow the search to work regardless of
4556 * word order. We also want double quoted text to be preserved, so word
4557 * order is important - a la google. So this is what we want to
4558 * generate:
4559 *
4560 * ^(?=.*?\bone\b)(?=.*?\btwo three\b)(?=.*?\bfour\b).*$
4561 */
4562 var a = $.map( search.match( /"[^"]+"|[^ ]+/g ) || [''], function ( word ) {
4563 if ( word.charAt(0) === '"' ) {
4564 var m = word.match( /^"(.*)"$/ );
4565 word = m ? m[1] : word;
4566 }
4567
4568 return word.replace('"', '');
4569 } );
4570
4571 search = '^(?=.*?'+a.join( ')(?=.*?' )+').*$';
4572 }
4573
4574 return new RegExp( search, caseInsensitive ? 'i' : '' );
4575 }
4576
4577
4578 /**
4579 * Escape a string such that it can be used in a regular expression
4580 * @param {string} sVal string to escape
4581 * @returns {string} escaped string
4582 * @memberof DataTable#oApi
4583 */
4584 var _fnEscapeRegex = DataTable.util.escapeRegex;
4585
4586 var __filter_div = $('<div>')[0];
4587 var __filter_div_textContent = __filter_div.textContent !== undefined;
4588
4589 // Update the filtering data for each row if needed (by invalidation or first run)
4590 function _fnFilterData ( settings )
4591 {
4592 var columns = settings.aoColumns;
4593 var column;
4594 var i, j, ien, jen, filterData, cellData, row;
4595 var wasInvalidated = false;
4596
4597 for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
4598 row = settings.aoData[i];
4599
4600 if ( ! row._aFilterData ) {
4601 filterData = [];
4602
4603 for ( j=0, jen=columns.length ; j<jen ; j++ ) {
4604 column = columns[j];
4605
4606 if ( column.bSearchable ) {
4607 cellData = _fnGetCellData( settings, i, j, 'filter' );
4608
4609 // Search in DataTables 1.10 is string based. In 1.11 this
4610 // should be altered to also allow strict type checking.
4611 if ( cellData === null ) {
4612 cellData = '';
4613 }
4614
4615 if ( typeof cellData !== 'string' && cellData.toString ) {
4616 cellData = cellData.toString();
4617 }
4618 }
4619 else {
4620 cellData = '';
4621 }
4622
4623 // If it looks like there is an HTML entity in the string,
4624 // attempt to decode it so sorting works as expected. Note that
4625 // we could use a single line of jQuery to do this, but the DOM
4626 // method used here is much faster http://jsperf.com/html-decode
4627 if ( cellData.indexOf && cellData.indexOf('&') !== -1 ) {
4628 __filter_div.innerHTML = cellData;
4629 cellData = __filter_div_textContent ?
4630 __filter_div.textContent :
4631 __filter_div.innerText;
4632 }
4633
4634 if ( cellData.replace ) {
4635 cellData = cellData.replace(/[\r\n\u2028]/g, '');
4636 }
4637
4638 filterData.push( cellData );
4639 }
4640
4641 row._aFilterData = filterData;
4642 row._sFilterRow = filterData.join(' ');
4643 wasInvalidated = true;
4644 }
4645 }
4646
4647 return wasInvalidated;
4648 }
4649
4650
4651 /**
4652 * Convert from the internal Hungarian notation to camelCase for external
4653 * interaction
4654 * @param {object} obj Object to convert
4655 * @returns {object} Inverted object
4656 * @memberof DataTable#oApi
4657 */
4658 function _fnSearchToCamel ( obj )
4659 {
4660 return {
4661 search: obj.sSearch,
4662 smart: obj.bSmart,
4663 regex: obj.bRegex,
4664 caseInsensitive: obj.bCaseInsensitive
4665 };
4666 }
4667
4668
4669
4670 /**
4671 * Convert from camelCase notation to the internal Hungarian. We could use the
4672 * Hungarian convert function here, but this is cleaner
4673 * @param {object} obj Object to convert
4674 * @returns {object} Inverted object
4675 * @memberof DataTable#oApi
4676 */
4677 function _fnSearchToHung ( obj )
4678 {
4679 return {
4680 sSearch: obj.search,
4681 bSmart: obj.smart,
4682 bRegex: obj.regex,
4683 bCaseInsensitive: obj.caseInsensitive
4684 };
4685 }
4686
4687 /**
4688 * Generate the node required for the info display
4689 * @param {object} oSettings dataTables settings object
4690 * @returns {node} Information element
4691 * @memberof DataTable#oApi
4692 */
4693 function _fnFeatureHtmlInfo ( settings )
4694 {
4695 var
4696 tid = settings.sTableId,
4697 nodes = settings.aanFeatures.i,
4698 n = $('<div/>', {
4699 'class': settings.oClasses.sInfo,
4700 'id': ! nodes ? tid+'_info' : null
4701 } );
4702
4703 if ( ! nodes ) {
4704 // Update display on each draw
4705 settings.aoDrawCallback.push( {
4706 "fn": _fnUpdateInfo,
4707 "sName": "information"
4708 } );
4709
4710 n
4711 .attr( 'role', 'status' )
4712 .attr( 'aria-live', 'polite' );
4713
4714 // Table is described by our info div
4715 $(settings.nTable).attr( 'aria-describedby', tid+'_info' );
4716 }
4717
4718 return n[0];
4719 }
4720
4721
4722 /**
4723 * Update the information elements in the display
4724 * @param {object} settings dataTables settings object
4725 * @memberof DataTable#oApi
4726 */
4727 function _fnUpdateInfo ( settings )
4728 {
4729 /* Show information about the table */
4730 var nodes = settings.aanFeatures.i;
4731 if ( nodes.length === 0 ) {
4732 return;
4733 }
4734
4735 var
4736 lang = settings.oLanguage,
4737 start = settings._iDisplayStart+1,
4738 end = settings.fnDisplayEnd(),
4739 max = settings.fnRecordsTotal(),
4740 total = settings.fnRecordsDisplay(),
4741 out = total ?
4742 lang.sInfo :
4743 lang.sInfoEmpty;
4744
4745 if ( total !== max ) {
4746 /* Record set after filtering */
4747 out += ' ' + lang.sInfoFiltered;
4748 }
4749
4750 // Convert the macros
4751 out += lang.sInfoPostFix;
4752 out = _fnInfoMacros( settings, out );
4753
4754 var callback = lang.fnInfoCallback;
4755 if ( callback !== null ) {
4756 out = callback.call( settings.oInstance,
4757 settings, start, end, max, total, out
4758 );
4759 }
4760
4761 $(nodes).html( out );
4762 }
4763
4764
4765 function _fnInfoMacros ( settings, str )
4766 {
4767 // When infinite scrolling, we are always starting at 1. _iDisplayStart is used only
4768 // internally
4769 var
4770 formatter = settings.fnFormatNumber,
4771 start = settings._iDisplayStart+1,
4772 len = settings._iDisplayLength,
4773 vis = settings.fnRecordsDisplay(),
4774 all = len === -1;
4775
4776 return str.
4777 replace(/_START_/g, formatter.call( settings, start ) ).
4778 replace(/_END_/g, formatter.call( settings, settings.fnDisplayEnd() ) ).
4779 replace(/_MAX_/g, formatter.call( settings, settings.fnRecordsTotal() ) ).
4780 replace(/_TOTAL_/g, formatter.call( settings, vis ) ).
4781 replace(/_PAGE_/g, formatter.call( settings, all ? 1 : Math.ceil( start / len ) ) ).
4782 replace(/_PAGES_/g, formatter.call( settings, all ? 1 : Math.ceil( vis / len ) ) );
4783 }
4784
4785
4786
4787 /**
4788 * Draw the table for the first time, adding all required features
4789 * @param {object} settings dataTables settings object
4790 * @memberof DataTable#oApi
4791 */
4792 function _fnInitialise ( settings )
4793 {
4794 var i, iLen, iAjaxStart=settings.iInitDisplayStart;
4795 var columns = settings.aoColumns, column;
4796 var features = settings.oFeatures;
4797 var deferLoading = settings.bDeferLoading; // value modified by the draw
4798
4799 /* Ensure that the table data is fully initialised */
4800 if ( ! settings.bInitialised ) {
4801 setTimeout( function(){ _fnInitialise( settings ); }, 200 );
4802 return;
4803 }
4804
4805 /* Show the display HTML options */
4806 _fnAddOptionsHtml( settings );
4807
4808 /* Build and draw the header / footer for the table */
4809 _fnBuildHead( settings );
4810 _fnDrawHead( settings, settings.aoHeader );
4811 _fnDrawHead( settings, settings.aoFooter );
4812
4813 /* Okay to show that something is going on now */
4814 _fnProcessingDisplay( settings, true );
4815
4816 /* Calculate sizes for columns */
4817 if ( features.bAutoWidth ) {
4818 _fnCalculateColumnWidths( settings );
4819 }
4820
4821 for ( i=0, iLen=columns.length ; i<iLen ; i++ ) {
4822 column = columns[i];
4823
4824 if ( column.sWidth ) {
4825 column.nTh.style.width = _fnStringToCss( column.sWidth );
4826 }
4827 }
4828
4829 _fnCallbackFire( settings, null, 'preInit', [settings] );
4830
4831 // If there is default sorting required - let's do it. The sort function
4832 // will do the drawing for us. Otherwise we draw the table regardless of the
4833 // Ajax source - this allows the table to look initialised for Ajax sourcing
4834 // data (show 'loading' message possibly)
4835 _fnReDraw( settings );
4836
4837 // Server-side processing init complete is done by _fnAjaxUpdateDraw
4838 var dataSrc = _fnDataSource( settings );
4839 if ( dataSrc != 'ssp' || deferLoading ) {
4840 // if there is an ajax source load the data
4841 if ( dataSrc == 'ajax' ) {
4842 _fnBuildAjax( settings, [], function(json) {
4843 var aData = _fnAjaxDataSrc( settings, json );
4844
4845 // Got the data - add it to the table
4846 for ( i=0 ; i<aData.length ; i++ ) {
4847 _fnAddData( settings, aData[i] );
4848 }
4849
4850 // Reset the init display for cookie saving. We've already done
4851 // a filter, and therefore cleared it before. So we need to make
4852 // it appear 'fresh'
4853 settings.iInitDisplayStart = iAjaxStart;
4854
4855 _fnReDraw( settings );
4856
4857 _fnProcessingDisplay( settings, false );
4858 _fnInitComplete( settings, json );
4859 }, settings );
4860 }
4861 else {
4862 _fnProcessingDisplay( settings, false );
4863 _fnInitComplete( settings );
4864 }
4865 }
4866 }
4867
4868
4869 /**
4870 * Draw the table for the first time, adding all required features
4871 * @param {object} oSettings dataTables settings object
4872 * @param {object} [json] JSON from the server that completed the table, if using Ajax source
4873 * with client-side processing (optional)
4874 * @memberof DataTable#oApi
4875 */
4876 function _fnInitComplete ( settings, json )
4877 {
4878 settings._bInitComplete = true;
4879
4880 // When data was added after the initialisation (data or Ajax) we need to
4881 // calculate the column sizing
4882 if ( json || settings.oInit.aaData ) {
4883 _fnAdjustColumnSizing( settings );
4884 }
4885
4886 _fnCallbackFire( settings, null, 'plugin-init', [settings, json] );
4887 _fnCallbackFire( settings, 'aoInitComplete', 'init', [settings, json] );
4888 }
4889
4890
4891 function _fnLengthChange ( settings, val )
4892 {
4893 var len = parseInt( val, 10 );
4894 settings._iDisplayLength = len;
4895
4896 _fnLengthOverflow( settings );
4897
4898 // Fire length change event
4899 _fnCallbackFire( settings, null, 'length', [settings, len] );
4900 }
4901
4902
4903 /**
4904 * Generate the node required for user display length changing
4905 * @param {object} settings dataTables settings object
4906 * @returns {node} Display length feature node
4907 * @memberof DataTable#oApi
4908 */
4909 function _fnFeatureHtmlLength ( settings )
4910 {
4911 var
4912 classes = settings.oClasses,
4913 tableId = settings.sTableId,
4914 menu = settings.aLengthMenu,
4915 d2 = Array.isArray( menu[0] ),
4916 lengths = d2 ? menu[0] : menu,
4917 language = d2 ? menu[1] : menu;
4918
4919 var select = $('<select/>', {
4920 'name': tableId+'_length',
4921 'aria-controls': tableId,
4922 'class': classes.sLengthSelect
4923 } );
4924
4925 for ( var i=0, ien=lengths.length ; i<ien ; i++ ) {
4926 select[0][ i ] = new Option(
4927 typeof language[i] === 'number' ?
4928 settings.fnFormatNumber( language[i] ) :
4929 language[i],
4930 lengths[i]
4931 );
4932 }
4933
4934 var div = $('<div><label/></div>').addClass( classes.sLength );
4935 if ( ! settings.aanFeatures.l ) {
4936 div[0].id = tableId+'_length';
4937 }
4938
4939 div.children().append(
4940 settings.oLanguage.sLengthMenu.replace( '_MENU_', select[0].outerHTML )
4941 );
4942
4943 // Can't use `select` variable as user might provide their own and the
4944 // reference is broken by the use of outerHTML
4945 $('select', div)
4946 .val( settings._iDisplayLength )
4947 .on( 'change.DT', function(e) {
4948 _fnLengthChange( settings, $(this).val() );
4949 _fnDraw( settings );
4950 } );
4951
4952 // Update node value whenever anything changes the table's length
4953 $(settings.nTable).on( 'length.dt.DT', function (e, s, len) {
4954 if ( settings === s ) {
4955 $('select', div).val( len );
4956 }
4957 } );
4958
4959 return div[0];
4960 }
4961
4962
4963
4964 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
4965 * Note that most of the paging logic is done in
4966 * DataTable.ext.pager
4967 */
4968
4969 /**
4970 * Generate the node required for default pagination
4971 * @param {object} oSettings dataTables settings object
4972 * @returns {node} Pagination feature node
4973 * @memberof DataTable#oApi
4974 */
4975 function _fnFeatureHtmlPaginate ( settings )
4976 {
4977 var
4978 type = settings.sPaginationType,
4979 plugin = DataTable.ext.pager[ type ],
4980 modern = typeof plugin === 'function',
4981 redraw = function( settings ) {
4982 _fnDraw( settings );
4983 },
4984 node = $('<div/>').addClass( settings.oClasses.sPaging + type )[0],
4985 features = settings.aanFeatures;
4986
4987 if ( ! modern ) {
4988 plugin.fnInit( settings, node, redraw );
4989 }
4990
4991 /* Add a draw callback for the pagination on first instance, to update the paging display */
4992 if ( ! features.p )
4993 {
4994 node.id = settings.sTableId+'_paginate';
4995
4996 settings.aoDrawCallback.push( {
4997 "fn": function( settings ) {
4998 if ( modern ) {
4999 var
5000 start = settings._iDisplayStart,
5001 len = settings._iDisplayLength,
5002 visRecords = settings.fnRecordsDisplay(),
5003 all = len === -1,
5004 page = all ? 0 : Math.ceil( start / len ),
5005 pages = all ? 1 : Math.ceil( visRecords / len ),
5006 buttons = plugin(page, pages),
5007 i, ien;
5008
5009 for ( i=0, ien=features.p.length ; i<ien ; i++ ) {
5010 _fnRenderer( settings, 'pageButton' )(
5011 settings, features.p[i], i, buttons, page, pages
5012 );
5013 }
5014 }
5015 else {
5016 plugin.fnUpdate( settings, redraw );
5017 }
5018 },
5019 "sName": "pagination"
5020 } );
5021 }
5022
5023 return node;
5024 }
5025
5026
5027 /**
5028 * Alter the display settings to change the page
5029 * @param {object} settings DataTables settings object
5030 * @param {string|int} action Paging action to take: "first", "previous",
5031 * "next" or "last" or page number to jump to (integer)
5032 * @param [bool] redraw Automatically draw the update or not
5033 * @returns {bool} true page has changed, false - no change
5034 * @memberof DataTable#oApi
5035 */
5036 function _fnPageChange ( settings, action, redraw )
5037 {
5038 var
5039 start = settings._iDisplayStart,
5040 len = settings._iDisplayLength,
5041 records = settings.fnRecordsDisplay();
5042
5043 if ( records === 0 || len === -1 )
5044 {
5045 start = 0;
5046 }
5047 else if ( typeof action === "number" )
5048 {
5049 start = action * len;
5050
5051 if ( start > records )
5052 {
5053 start = 0;
5054 }
5055 }
5056 else if ( action == "first" )
5057 {
5058 start = 0;
5059 }
5060 else if ( action == "previous" )
5061 {
5062 start = len >= 0 ?
5063 start - len :
5064 0;
5065
5066 if ( start < 0 )
5067 {
5068 start = 0;
5069 }
5070 }
5071 else if ( action == "next" )
5072 {
5073 if ( start + len < records )
5074 {
5075 start += len;
5076 }
5077 }
5078 else if ( action == "last" )
5079 {
5080 start = Math.floor( (records-1) / len) * len;
5081 }
5082 else
5083 {
5084 _fnLog( settings, 0, "Unknown paging action: "+action, 5 );
5085 }
5086
5087 var changed = settings._iDisplayStart !== start;
5088 settings._iDisplayStart = start;
5089
5090 if ( changed ) {
5091 _fnCallbackFire( settings, null, 'page', [settings] );
5092
5093 if ( redraw ) {
5094 _fnDraw( settings );
5095 }
5096 }
5097 else {
5098 // No change event - paging was called, but no change
5099 _fnCallbackFire( settings, null, 'page-nc', [settings] );
5100 }
5101
5102 return changed;
5103 }
5104
5105
5106
5107 /**
5108 * Generate the node required for the processing node
5109 * @param {object} settings dataTables settings object
5110 * @returns {node} Processing element
5111 * @memberof DataTable#oApi
5112 */
5113 function _fnFeatureHtmlProcessing ( settings )
5114 {
5115 return $('<div/>', {
5116 'id': ! settings.aanFeatures.r ? settings.sTableId+'_processing' : null,
5117 'class': settings.oClasses.sProcessing
5118 } )
5119 .html( settings.oLanguage.sProcessing )
5120 .append('<div><div></div><div></div><div></div><div></div></div>')
5121 .insertBefore( settings.nTable )[0];
5122 }
5123
5124
5125 /**
5126 * Display or hide the processing indicator
5127 * @param {object} settings dataTables settings object
5128 * @param {bool} show Show the processing indicator (true) or not (false)
5129 * @memberof DataTable#oApi
5130 */
5131 function _fnProcessingDisplay ( settings, show )
5132 {
5133 if ( settings.oFeatures.bProcessing ) {
5134 $(settings.aanFeatures.r).css( 'display', show ? 'block' : 'none' );
5135 }
5136
5137 _fnCallbackFire( settings, null, 'processing', [settings, show] );
5138 }
5139
5140 /**
5141 * Add any control elements for the table - specifically scrolling
5142 * @param {object} settings dataTables settings object
5143 * @returns {node} Node to add to the DOM
5144 * @memberof DataTable#oApi
5145 */
5146 function _fnFeatureHtmlTable ( settings )
5147 {
5148 var table = $(settings.nTable);
5149
5150 // Scrolling from here on in
5151 var scroll = settings.oScroll;
5152
5153 if ( scroll.sX === '' && scroll.sY === '' ) {
5154 return settings.nTable;
5155 }
5156
5157 var scrollX = scroll.sX;
5158 var scrollY = scroll.sY;
5159 var classes = settings.oClasses;
5160 var caption = table.children('caption');
5161 var captionSide = caption.length ? caption[0]._captionSide : null;
5162 var headerClone = $( table[0].cloneNode(false) );
5163 var footerClone = $( table[0].cloneNode(false) );
5164 var footer = table.children('tfoot');
5165 var _div = '<div/>';
5166 var size = function ( s ) {
5167 return !s ? null : _fnStringToCss( s );
5168 };
5169
5170 if ( ! footer.length ) {
5171 footer = null;
5172 }
5173
5174 /*
5175 * The HTML structure that we want to generate in this function is:
5176 * div - scroller
5177 * div - scroll head
5178 * div - scroll head inner
5179 * table - scroll head table
5180 * thead - thead
5181 * div - scroll body
5182 * table - table (master table)
5183 * thead - thead clone for sizing
5184 * tbody - tbody
5185 * div - scroll foot
5186 * div - scroll foot inner
5187 * table - scroll foot table
5188 * tfoot - tfoot
5189 */
5190 var scroller = $( _div, { 'class': classes.sScrollWrapper } )
5191 .append(
5192 $(_div, { 'class': classes.sScrollHead } )
5193 .css( {
5194 overflow: 'hidden',
5195 position: 'relative',
5196 border: 0,
5197 width: scrollX ? size(scrollX) : '100%'
5198 } )
5199 .append(
5200 $(_div, { 'class': classes.sScrollHeadInner } )
5201 .css( {
5202 'box-sizing': 'content-box',
5203 width: scroll.sXInner || '100%'
5204 } )
5205 .append(
5206 headerClone
5207 .removeAttr('id')
5208 .css( 'margin-left', 0 )
5209 .append( captionSide === 'top' ? caption : null )
5210 .append(
5211 table.children('thead')
5212 )
5213 )
5214 )
5215 )
5216 .append(
5217 $(_div, { 'class': classes.sScrollBody } )
5218 .css( {
5219 position: 'relative',
5220 overflow: 'auto',
5221 width: size( scrollX )
5222 } )
5223 .append( table )
5224 );
5225
5226 if ( footer ) {
5227 scroller.append(
5228 $(_div, { 'class': classes.sScrollFoot } )
5229 .css( {
5230 overflow: 'hidden',
5231 border: 0,
5232 width: scrollX ? size(scrollX) : '100%'
5233 } )
5234 .append(
5235 $(_div, { 'class': classes.sScrollFootInner } )
5236 .append(
5237 footerClone
5238 .removeAttr('id')
5239 .css( 'margin-left', 0 )
5240 .append( captionSide === 'bottom' ? caption : null )
5241 .append(
5242 table.children('tfoot')
5243 )
5244 )
5245 )
5246 );
5247 }
5248
5249 var children = scroller.children();
5250 var scrollHead = children[0];
5251 var scrollBody = children[1];
5252 var scrollFoot = footer ? children[2] : null;
5253
5254 // When the body is scrolled, then we also want to scroll the headers
5255 if ( scrollX ) {
5256 $(scrollBody).on( 'scroll.DT', function (e) {
5257 var scrollLeft = this.scrollLeft;
5258
5259 scrollHead.scrollLeft = scrollLeft;
5260
5261 if ( footer ) {
5262 scrollFoot.scrollLeft = scrollLeft;
5263 }
5264 } );
5265 }
5266
5267 $(scrollBody).css('max-height', scrollY);
5268 if (! scroll.bCollapse) {
5269 $(scrollBody).css('height', scrollY);
5270 }
5271
5272 settings.nScrollHead = scrollHead;
5273 settings.nScrollBody = scrollBody;
5274 settings.nScrollFoot = scrollFoot;
5275
5276 // On redraw - align columns
5277 settings.aoDrawCallback.push( {
5278 "fn": _fnScrollDraw,
5279 "sName": "scrolling"
5280 } );
5281
5282 return scroller[0];
5283 }
5284
5285
5286
5287 /**
5288 * Update the header, footer and body tables for resizing - i.e. column
5289 * alignment.
5290 *
5291 * Welcome to the most horrible function DataTables. The process that this
5292 * function follows is basically:
5293 * 1. Re-create the table inside the scrolling div
5294 * 2. Take live measurements from the DOM
5295 * 3. Apply the measurements to align the columns
5296 * 4. Clean up
5297 *
5298 * @param {object} settings dataTables settings object
5299 * @memberof DataTable#oApi
5300 */
5301 function _fnScrollDraw ( settings )
5302 {
5303 // Given that this is such a monster function, a lot of variables are use
5304 // to try and keep the minimised size as small as possible
5305 var
5306 scroll = settings.oScroll,
5307 scrollX = scroll.sX,
5308 scrollXInner = scroll.sXInner,
5309 scrollY = scroll.sY,
5310 barWidth = scroll.iBarWidth,
5311 divHeader = $(settings.nScrollHead),
5312 divHeaderStyle = divHeader[0].style,
5313 divHeaderInner = divHeader.children('div'),
5314 divHeaderInnerStyle = divHeaderInner[0].style,
5315 divHeaderTable = divHeaderInner.children('table'),
5316 divBodyEl = settings.nScrollBody,
5317 divBody = $(divBodyEl),
5318 divBodyStyle = divBodyEl.style,
5319 divFooter = $(settings.nScrollFoot),
5320 divFooterInner = divFooter.children('div'),
5321 divFooterTable = divFooterInner.children('table'),
5322 header = $(settings.nTHead),
5323 table = $(settings.nTable),
5324 tableEl = table[0],
5325 tableStyle = tableEl.style,
5326 footer = settings.nTFoot ? $(settings.nTFoot) : null,
5327 browser = settings.oBrowser,
5328 ie67 = browser.bScrollOversize,
5329 dtHeaderCells = _pluck( settings.aoColumns, 'nTh' ),
5330 headerTrgEls, footerTrgEls,
5331 headerSrcEls, footerSrcEls,
5332 headerCopy, footerCopy,
5333 headerWidths=[], footerWidths=[],
5334 headerContent=[], footerContent=[],
5335 idx, correction, sanityWidth,
5336 zeroOut = function(nSizer) {
5337 var style = nSizer.style;
5338 style.paddingTop = "0";
5339 style.paddingBottom = "0";
5340 style.borderTopWidth = "0";
5341 style.borderBottomWidth = "0";
5342 style.height = 0;
5343 };
5344
5345 // If the scrollbar visibility has changed from the last draw, we need to
5346 // adjust the column sizes as the table width will have changed to account
5347 // for the scrollbar
5348 var scrollBarVis = divBodyEl.scrollHeight > divBodyEl.clientHeight;
5349
5350 if ( settings.scrollBarVis !== scrollBarVis && settings.scrollBarVis !== undefined ) {
5351 settings.scrollBarVis = scrollBarVis;
5352 _fnAdjustColumnSizing( settings );
5353 return; // adjust column sizing will call this function again
5354 }
5355 else {
5356 settings.scrollBarVis = scrollBarVis;
5357 }
5358
5359 /*
5360 * 1. Re-create the table inside the scrolling div
5361 */
5362
5363 // Remove the old minimised thead and tfoot elements in the inner table
5364 table.children('thead, tfoot').remove();
5365
5366 if ( footer ) {
5367 footerCopy = footer.clone().prependTo( table );
5368 footerTrgEls = footer.find('tr'); // the original tfoot is in its own table and must be sized
5369 footerSrcEls = footerCopy.find('tr');
5370 footerCopy.find('[id]').removeAttr('id');
5371 }
5372
5373 // Clone the current header and footer elements and then place it into the inner table
5374 headerCopy = header.clone().prependTo( table );
5375 headerTrgEls = header.find('tr'); // original header is in its own table
5376 headerSrcEls = headerCopy.find('tr');
5377 headerCopy.find('th, td').removeAttr('tabindex');
5378 headerCopy.find('[id]').removeAttr('id');
5379
5380
5381 /*
5382 * 2. Take live measurements from the DOM - do not alter the DOM itself!
5383 */
5384
5385 // Remove old sizing and apply the calculated column widths
5386 // Get the unique column headers in the newly created (cloned) header. We want to apply the
5387 // calculated sizes to this header
5388 if ( ! scrollX )
5389 {
5390 divBodyStyle.width = '100%';
5391 divHeader[0].style.width = '100%';
5392 }
5393
5394 $.each( _fnGetUniqueThs( settings, headerCopy ), function ( i, el ) {
5395 idx = _fnVisibleToColumnIndex( settings, i );
5396 el.style.width = settings.aoColumns[idx].sWidth;
5397 } );
5398
5399 if ( footer ) {
5400 _fnApplyToChildren( function(n) {
5401 n.style.width = "";
5402 }, footerSrcEls );
5403 }
5404
5405 // Size the table as a whole
5406 sanityWidth = table.outerWidth();
5407 if ( scrollX === "" ) {
5408 // No x scrolling
5409 tableStyle.width = "100%";
5410
5411 // IE7 will make the width of the table when 100% include the scrollbar
5412 // - which is shouldn't. When there is a scrollbar we need to take this
5413 // into account.
5414 if ( ie67 && (table.find('tbody').height() > divBodyEl.offsetHeight ||
5415 divBody.css('overflow-y') == "scroll")
5416 ) {
5417 tableStyle.width = _fnStringToCss( table.outerWidth() - barWidth);
5418 }
5419
5420 // Recalculate the sanity width
5421 sanityWidth = table.outerWidth();
5422 }
5423 else if ( scrollXInner !== "" ) {
5424 // legacy x scroll inner has been given - use it
5425 tableStyle.width = _fnStringToCss(scrollXInner);
5426
5427 // Recalculate the sanity width
5428 sanityWidth = table.outerWidth();
5429 }
5430
5431 // Hidden header should have zero height, so remove padding and borders. Then
5432 // set the width based on the real headers
5433
5434 // Apply all styles in one pass
5435 _fnApplyToChildren( zeroOut, headerSrcEls );
5436
5437 // Read all widths in next pass
5438 _fnApplyToChildren( function(nSizer) {
5439 var style = window.getComputedStyle ?
5440 window.getComputedStyle(nSizer).width :
5441 _fnStringToCss( $(nSizer).width() );
5442
5443 headerContent.push( nSizer.innerHTML );
5444 headerWidths.push( style );
5445 }, headerSrcEls );
5446
5447 // Apply all widths in final pass
5448 _fnApplyToChildren( function(nToSize, i) {
5449 nToSize.style.width = headerWidths[i];
5450 }, headerTrgEls );
5451
5452 $(headerSrcEls).css('height', 0);
5453
5454 /* Same again with the footer if we have one */
5455 if ( footer )
5456 {
5457 _fnApplyToChildren( zeroOut, footerSrcEls );
5458
5459 _fnApplyToChildren( function(nSizer) {
5460 footerContent.push( nSizer.innerHTML );
5461 footerWidths.push( _fnStringToCss( $(nSizer).css('width') ) );
5462 }, footerSrcEls );
5463
5464 _fnApplyToChildren( function(nToSize, i) {
5465 nToSize.style.width = footerWidths[i];
5466 }, footerTrgEls );
5467
5468 $(footerSrcEls).height(0);
5469 }
5470
5471
5472 /*
5473 * 3. Apply the measurements
5474 */
5475
5476 // "Hide" the header and footer that we used for the sizing. We need to keep
5477 // the content of the cell so that the width applied to the header and body
5478 // both match, but we want to hide it completely. We want to also fix their
5479 // width to what they currently are
5480 _fnApplyToChildren( function(nSizer, i) {
5481 nSizer.innerHTML = '<div class="dataTables_sizing">'+headerContent[i]+'</div>';
5482 nSizer.childNodes[0].style.height = "0";
5483 nSizer.childNodes[0].style.overflow = "hidden";
5484 nSizer.style.width = headerWidths[i];
5485 }, headerSrcEls );
5486
5487 if ( footer )
5488 {
5489 _fnApplyToChildren( function(nSizer, i) {
5490 nSizer.innerHTML = '<div class="dataTables_sizing">'+footerContent[i]+'</div>';
5491 nSizer.childNodes[0].style.height = "0";
5492 nSizer.childNodes[0].style.overflow = "hidden";
5493 nSizer.style.width = footerWidths[i];
5494 }, footerSrcEls );
5495 }
5496
5497 // Sanity check that the table is of a sensible width. If not then we are going to get
5498 // misalignment - try to prevent this by not allowing the table to shrink below its min width
5499 if ( Math.round(table.outerWidth()) < Math.round(sanityWidth) )
5500 {
5501 // The min width depends upon if we have a vertical scrollbar visible or not */
5502 correction = ((divBodyEl.scrollHeight > divBodyEl.offsetHeight ||
5503 divBody.css('overflow-y') == "scroll")) ?
5504 sanityWidth+barWidth :
5505 sanityWidth;
5506
5507 // IE6/7 are a law unto themselves...
5508 if ( ie67 && (divBodyEl.scrollHeight >
5509 divBodyEl.offsetHeight || divBody.css('overflow-y') == "scroll")
5510 ) {
5511 tableStyle.width = _fnStringToCss( correction-barWidth );
5512 }
5513
5514 // And give the user a warning that we've stopped the table getting too small
5515 if ( scrollX === "" || scrollXInner !== "" ) {
5516 _fnLog( settings, 1, 'Possible column misalignment', 6 );
5517 }
5518 }
5519 else
5520 {
5521 correction = '100%';
5522 }
5523
5524 // Apply to the container elements
5525 divBodyStyle.width = _fnStringToCss( correction );
5526 divHeaderStyle.width = _fnStringToCss( correction );
5527
5528 if ( footer ) {
5529 settings.nScrollFoot.style.width = _fnStringToCss( correction );
5530 }
5531
5532
5533 /*
5534 * 4. Clean up
5535 */
5536 if ( ! scrollY ) {
5537 /* IE7< puts a vertical scrollbar in place (when it shouldn't be) due to subtracting
5538 * the scrollbar height from the visible display, rather than adding it on. We need to
5539 * set the height in order to sort this. Don't want to do it in any other browsers.
5540 */
5541 if ( ie67 ) {
5542 divBodyStyle.height = _fnStringToCss( tableEl.offsetHeight+barWidth );
5543 }
5544 }
5545
5546 /* Finally set the width's of the header and footer tables */
5547 var iOuterWidth = table.outerWidth();
5548 divHeaderTable[0].style.width = _fnStringToCss( iOuterWidth );
5549 divHeaderInnerStyle.width = _fnStringToCss( iOuterWidth );
5550
5551 // Figure out if there are scrollbar present - if so then we need a the header and footer to
5552 // provide a bit more space to allow "overflow" scrolling (i.e. past the scrollbar)
5553 var bScrolling = table.height() > divBodyEl.clientHeight || divBody.css('overflow-y') == "scroll";
5554 var padding = 'padding' + (browser.bScrollbarLeft ? 'Left' : 'Right' );
5555 divHeaderInnerStyle[ padding ] = bScrolling ? barWidth+"px" : "0px";
5556
5557 if ( footer ) {
5558 divFooterTable[0].style.width = _fnStringToCss( iOuterWidth );
5559 divFooterInner[0].style.width = _fnStringToCss( iOuterWidth );
5560 divFooterInner[0].style[padding] = bScrolling ? barWidth+"px" : "0px";
5561 }
5562
5563 // Correct DOM ordering for colgroup - comes before the thead
5564 table.children('colgroup').insertBefore( table.children('thead') );
5565
5566 /* Adjust the position of the header in case we loose the y-scrollbar */
5567 divBody.trigger('scroll');
5568
5569 // If sorting or filtering has occurred, jump the scrolling back to the top
5570 // only if we aren't holding the position
5571 if ( (settings.bSorted || settings.bFiltered) && ! settings._drawHold ) {
5572 divBodyEl.scrollTop = 0;
5573 }
5574 }
5575
5576
5577
5578 /**
5579 * Apply a given function to the display child nodes of an element array (typically
5580 * TD children of TR rows
5581 * @param {function} fn Method to apply to the objects
5582 * @param array {nodes} an1 List of elements to look through for display children
5583 * @param array {nodes} an2 Another list (identical structure to the first) - optional
5584 * @memberof DataTable#oApi
5585 */
5586 function _fnApplyToChildren( fn, an1, an2 )
5587 {
5588 var index=0, i=0, iLen=an1.length;
5589 var nNode1, nNode2;
5590
5591 while ( i < iLen ) {
5592 nNode1 = an1[i].firstChild;
5593 nNode2 = an2 ? an2[i].firstChild : null;
5594
5595 while ( nNode1 ) {
5596 if ( nNode1.nodeType === 1 ) {
5597 if ( an2 ) {
5598 fn( nNode1, nNode2, index );
5599 }
5600 else {
5601 fn( nNode1, index );
5602 }
5603
5604 index++;
5605 }
5606
5607 nNode1 = nNode1.nextSibling;
5608 nNode2 = an2 ? nNode2.nextSibling : null;
5609 }
5610
5611 i++;
5612 }
5613 }
5614
5615
5616
5617 var __re_html_remove = /<.*?>/g;
5618
5619
5620 /**
5621 * Calculate the width of columns for the table
5622 * @param {object} oSettings dataTables settings object
5623 * @memberof DataTable#oApi
5624 */
5625 function _fnCalculateColumnWidths ( oSettings )
5626 {
5627 var
5628 table = oSettings.nTable,
5629 columns = oSettings.aoColumns,
5630 scroll = oSettings.oScroll,
5631 scrollY = scroll.sY,
5632 scrollX = scroll.sX,
5633 scrollXInner = scroll.sXInner,
5634 columnCount = columns.length,
5635 visibleColumns = _fnGetColumns( oSettings, 'bVisible' ),
5636 headerCells = $('th', oSettings.nTHead),
5637 tableWidthAttr = table.getAttribute('width'), // from DOM element
5638 tableContainer = table.parentNode,
5639 userInputs = false,
5640 i, column, columnIdx, width, outerWidth,
5641 browser = oSettings.oBrowser,
5642 ie67 = browser.bScrollOversize;
5643
5644 var styleWidth = table.style.width;
5645 if ( styleWidth && styleWidth.indexOf('%') !== -1 ) {
5646 tableWidthAttr = styleWidth;
5647 }
5648
5649 /* Convert any user input sizes into pixel sizes */
5650 for ( i=0 ; i<visibleColumns.length ; i++ ) {
5651 column = columns[ visibleColumns[i] ];
5652
5653 if ( column.sWidth !== null ) {
5654 column.sWidth = _fnConvertToWidth( column.sWidthOrig, tableContainer );
5655
5656 userInputs = true;
5657 }
5658 }
5659
5660 /* If the number of columns in the DOM equals the number that we have to
5661 * process in DataTables, then we can use the offsets that are created by
5662 * the web- browser. No custom sizes can be set in order for this to happen,
5663 * nor scrolling used
5664 */
5665 if ( ie67 || ! userInputs && ! scrollX && ! scrollY &&
5666 columnCount == _fnVisbleColumns( oSettings ) &&
5667 columnCount == headerCells.length
5668 ) {
5669 for ( i=0 ; i<columnCount ; i++ ) {
5670 var colIdx = _fnVisibleToColumnIndex( oSettings, i );
5671
5672 if ( colIdx !== null ) {
5673 columns[ colIdx ].sWidth = _fnStringToCss( headerCells.eq(i).width() );
5674 }
5675 }
5676 }
5677 else
5678 {
5679 // Otherwise construct a single row, worst case, table with the widest
5680 // node in the data, assign any user defined widths, then insert it into
5681 // the DOM and allow the browser to do all the hard work of calculating
5682 // table widths
5683 var tmpTable = $(table).clone() // don't use cloneNode - IE8 will remove events on the main table
5684 .css( 'visibility', 'hidden' )
5685 .removeAttr( 'id' );
5686
5687 // Clean up the table body
5688 tmpTable.find('tbody tr').remove();
5689 var tr = $('<tr/>').appendTo( tmpTable.find('tbody') );
5690
5691 // Clone the table header and footer - we can't use the header / footer
5692 // from the cloned table, since if scrolling is active, the table's
5693 // real header and footer are contained in different table tags
5694 tmpTable.find('thead, tfoot').remove();
5695 tmpTable
5696 .append( $(oSettings.nTHead).clone() )
5697 .append( $(oSettings.nTFoot).clone() );
5698
5699 // Remove any assigned widths from the footer (from scrolling)
5700 tmpTable.find('tfoot th, tfoot td').css('width', '');
5701
5702 // Apply custom sizing to the cloned header
5703 headerCells = _fnGetUniqueThs( oSettings, tmpTable.find('thead')[0] );
5704
5705 for ( i=0 ; i<visibleColumns.length ; i++ ) {
5706 column = columns[ visibleColumns[i] ];
5707
5708 headerCells[i].style.width = column.sWidthOrig !== null && column.sWidthOrig !== '' ?
5709 _fnStringToCss( column.sWidthOrig ) :
5710 '';
5711
5712 // For scrollX we need to force the column width otherwise the
5713 // browser will collapse it. If this width is smaller than the
5714 // width the column requires, then it will have no effect
5715 if ( column.sWidthOrig && scrollX ) {
5716 $( headerCells[i] ).append( $('<div/>').css( {
5717 width: column.sWidthOrig,
5718 margin: 0,
5719 padding: 0,
5720 border: 0,
5721 height: 1
5722 } ) );
5723 }
5724 }
5725
5726 // Find the widest cell for each column and put it into the table
5727 if ( oSettings.aoData.length ) {
5728 for ( i=0 ; i<visibleColumns.length ; i++ ) {
5729 columnIdx = visibleColumns[i];
5730 column = columns[ columnIdx ];
5731
5732 $( _fnGetWidestNode( oSettings, columnIdx ) )
5733 .clone( false )
5734 .append( column.sContentPadding )
5735 .appendTo( tr );
5736 }
5737 }
5738
5739 // Tidy the temporary table - remove name attributes so there aren't
5740 // duplicated in the dom (radio elements for example)
5741 $('[name]', tmpTable).removeAttr('name');
5742
5743 // Table has been built, attach to the document so we can work with it.
5744 // A holding element is used, positioned at the top of the container
5745 // with minimal height, so it has no effect on if the container scrolls
5746 // or not. Otherwise it might trigger scrolling when it actually isn't
5747 // needed
5748 var holder = $('<div/>').css( scrollX || scrollY ?
5749 {
5750 position: 'absolute',
5751 top: 0,
5752 left: 0,
5753 height: 1,
5754 right: 0,
5755 overflow: 'hidden'
5756 } :
5757 {}
5758 )
5759 .append( tmpTable )
5760 .appendTo( tableContainer );
5761
5762 // When scrolling (X or Y) we want to set the width of the table as
5763 // appropriate. However, when not scrolling leave the table width as it
5764 // is. This results in slightly different, but I think correct behaviour
5765 if ( scrollX && scrollXInner ) {
5766 tmpTable.width( scrollXInner );
5767 }
5768 else if ( scrollX ) {
5769 tmpTable.css( 'width', 'auto' );
5770 tmpTable.removeAttr('width');
5771
5772 // If there is no width attribute or style, then allow the table to
5773 // collapse
5774 if ( tmpTable.width() < tableContainer.clientWidth && tableWidthAttr ) {
5775 tmpTable.width( tableContainer.clientWidth );
5776 }
5777 }
5778 else if ( scrollY ) {
5779 tmpTable.width( tableContainer.clientWidth );
5780 }
5781 else if ( tableWidthAttr ) {
5782 tmpTable.width( tableWidthAttr );
5783 }
5784
5785 // Get the width of each column in the constructed table - we need to
5786 // know the inner width (so it can be assigned to the other table's
5787 // cells) and the outer width so we can calculate the full width of the
5788 // table. This is safe since DataTables requires a unique cell for each
5789 // column, but if ever a header can span multiple columns, this will
5790 // need to be modified.
5791 var total = 0;
5792 for ( i=0 ; i<visibleColumns.length ; i++ ) {
5793 var cell = $(headerCells[i]);
5794 var border = cell.outerWidth() - cell.width();
5795
5796 // Use getBounding... where possible (not IE8-) because it can give
5797 // sub-pixel accuracy, which we then want to round up!
5798 var bounding = browser.bBounding ?
5799 Math.ceil( headerCells[i].getBoundingClientRect().width ) :
5800 cell.outerWidth();
5801
5802 // Total is tracked to remove any sub-pixel errors as the outerWidth
5803 // of the table might not equal the total given here (IE!).
5804 total += bounding;
5805
5806 // Width for each column to use
5807 columns[ visibleColumns[i] ].sWidth = _fnStringToCss( bounding - border );
5808 }
5809
5810 table.style.width = _fnStringToCss( total );
5811
5812 // Finished with the table - ditch it
5813 holder.remove();
5814 }
5815
5816 // If there is a width attr, we want to attach an event listener which
5817 // allows the table sizing to automatically adjust when the window is
5818 // resized. Use the width attr rather than CSS, since we can't know if the
5819 // CSS is a relative value or absolute - DOM read is always px.
5820 if ( tableWidthAttr ) {
5821 table.style.width = _fnStringToCss( tableWidthAttr );
5822 }
5823
5824 if ( (tableWidthAttr || scrollX) && ! oSettings._reszEvt ) {
5825 var bindResize = function () {
5826 $(window).on('resize.DT-'+oSettings.sInstance, _fnThrottle( function () {
5827 _fnAdjustColumnSizing( oSettings );
5828 } ) );
5829 };
5830
5831 // IE6/7 will crash if we bind a resize event handler on page load.
5832 // To be removed in 1.11 which drops IE6/7 support
5833 if ( ie67 ) {
5834 setTimeout( bindResize, 1000 );
5835 }
5836 else {
5837 bindResize();
5838 }
5839
5840 oSettings._reszEvt = true;
5841 }
5842 }
5843
5844
5845 /**
5846 * Throttle the calls to a function. Arguments and context are maintained for
5847 * the throttled function
5848 * @param {function} fn Function to be called
5849 * @param {int} [freq=200] call frequency in mS
5850 * @returns {function} wrapped function
5851 * @memberof DataTable#oApi
5852 */
5853 var _fnThrottle = DataTable.util.throttle;
5854
5855
5856 /**
5857 * Convert a CSS unit width to pixels (e.g. 2em)
5858 * @param {string} width width to be converted
5859 * @param {node} parent parent to get the with for (required for relative widths) - optional
5860 * @returns {int} width in pixels
5861 * @memberof DataTable#oApi
5862 */
5863 function _fnConvertToWidth ( width, parent )
5864 {
5865 if ( ! width ) {
5866 return 0;
5867 }
5868
5869 var n = $('<div/>')
5870 .css( 'width', _fnStringToCss( width ) )
5871 .appendTo( parent || document.body );
5872
5873 var val = n[0].offsetWidth;
5874 n.remove();
5875
5876 return val;
5877 }
5878
5879
5880 /**
5881 * Get the widest node
5882 * @param {object} settings dataTables settings object
5883 * @param {int} colIdx column of interest
5884 * @returns {node} widest table node
5885 * @memberof DataTable#oApi
5886 */
5887 function _fnGetWidestNode( settings, colIdx )
5888 {
5889 var idx = _fnGetMaxLenString( settings, colIdx );
5890 if ( idx < 0 ) {
5891 return null;
5892 }
5893
5894 var data = settings.aoData[ idx ];
5895 return ! data.nTr ? // Might not have been created when deferred rendering
5896 $('<td/>').html( _fnGetCellData( settings, idx, colIdx, 'display' ) )[0] :
5897 data.anCells[ colIdx ];
5898 }
5899
5900
5901 /**
5902 * Get the maximum strlen for each data column
5903 * @param {object} settings dataTables settings object
5904 * @param {int} colIdx column of interest
5905 * @returns {string} max string length for each column
5906 * @memberof DataTable#oApi
5907 */
5908 function _fnGetMaxLenString( settings, colIdx )
5909 {
5910 var s, max=-1, maxIdx = -1;
5911
5912 for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
5913 s = _fnGetCellData( settings, i, colIdx, 'display' )+'';
5914 s = s.replace( __re_html_remove, '' );
5915 s = s.replace( /&nbsp;/g, ' ' );
5916
5917 if ( s.length > max ) {
5918 max = s.length;
5919 maxIdx = i;
5920 }
5921 }
5922
5923 return maxIdx;
5924 }
5925
5926
5927 /**
5928 * Append a CSS unit (only if required) to a string
5929 * @param {string} value to css-ify
5930 * @returns {string} value with css unit
5931 * @memberof DataTable#oApi
5932 */
5933 function _fnStringToCss( s )
5934 {
5935 if ( s === null ) {
5936 return '0px';
5937 }
5938
5939 if ( typeof s == 'number' ) {
5940 return s < 0 ?
5941 '0px' :
5942 s+'px';
5943 }
5944
5945 // Check it has a unit character already
5946 return s.match(/\d$/) ?
5947 s+'px' :
5948 s;
5949 }
5950
5951
5952
5953 function _fnSortFlatten ( settings )
5954 {
5955 var
5956 i, iLen, k, kLen,
5957 aSort = [],
5958 aiOrig = [],
5959 aoColumns = settings.aoColumns,
5960 aDataSort, iCol, sType, srcCol,
5961 fixed = settings.aaSortingFixed,
5962 fixedObj = $.isPlainObject( fixed ),
5963 nestedSort = [],
5964 add = function ( a ) {
5965 if ( a.length && ! Array.isArray( a[0] ) ) {
5966 // 1D array
5967 nestedSort.push( a );
5968 }
5969 else {
5970 // 2D array
5971 $.merge( nestedSort, a );
5972 }
5973 };
5974
5975 // Build the sort array, with pre-fix and post-fix options if they have been
5976 // specified
5977 if ( Array.isArray( fixed ) ) {
5978 add( fixed );
5979 }
5980
5981 if ( fixedObj && fixed.pre ) {
5982 add( fixed.pre );
5983 }
5984
5985 add( settings.aaSorting );
5986
5987 if (fixedObj && fixed.post ) {
5988 add( fixed.post );
5989 }
5990
5991 for ( i=0 ; i<nestedSort.length ; i++ )
5992 {
5993 srcCol = nestedSort[i][0];
5994 aDataSort = aoColumns[ srcCol ].aDataSort;
5995
5996 for ( k=0, kLen=aDataSort.length ; k<kLen ; k++ )
5997 {
5998 iCol = aDataSort[k];
5999 sType = aoColumns[ iCol ].sType || 'string';
6000
6001 if ( nestedSort[i]._idx === undefined ) {
6002 nestedSort[i]._idx = $.inArray( nestedSort[i][1], aoColumns[iCol].asSorting );
6003 }
6004
6005 aSort.push( {
6006 src: srcCol,
6007 col: iCol,
6008 dir: nestedSort[i][1],
6009 index: nestedSort[i]._idx,
6010 type: sType,
6011 formatter: DataTable.ext.type.order[ sType+"-pre" ]
6012 } );
6013 }
6014 }
6015
6016 return aSort;
6017 }
6018
6019 /**
6020 * Change the order of the table
6021 * @param {object} oSettings dataTables settings object
6022 * @memberof DataTable#oApi
6023 * @todo This really needs split up!
6024 */
6025 function _fnSort ( oSettings )
6026 {
6027 var
6028 i, ien, iLen, j, jLen, k, kLen,
6029 sDataType, nTh,
6030 aiOrig = [],
6031 oExtSort = DataTable.ext.type.order,
6032 aoData = oSettings.aoData,
6033 aoColumns = oSettings.aoColumns,
6034 aDataSort, data, iCol, sType, oSort,
6035 formatters = 0,
6036 sortCol,
6037 displayMaster = oSettings.aiDisplayMaster,
6038 aSort;
6039
6040 // Resolve any column types that are unknown due to addition or invalidation
6041 // @todo Can this be moved into a 'data-ready' handler which is called when
6042 // data is going to be used in the table?
6043 _fnColumnTypes( oSettings );
6044
6045 aSort = _fnSortFlatten( oSettings );
6046
6047 for ( i=0, ien=aSort.length ; i<ien ; i++ ) {
6048 sortCol = aSort[i];
6049
6050 // Track if we can use the fast sort algorithm
6051 if ( sortCol.formatter ) {
6052 formatters++;
6053 }
6054
6055 // Load the data needed for the sort, for each cell
6056 _fnSortData( oSettings, sortCol.col );
6057 }
6058
6059 /* No sorting required if server-side or no sorting array */
6060 if ( _fnDataSource( oSettings ) != 'ssp' && aSort.length !== 0 )
6061 {
6062 // Create a value - key array of the current row positions such that we can use their
6063 // current position during the sort, if values match, in order to perform stable sorting
6064 for ( i=0, iLen=displayMaster.length ; i<iLen ; i++ ) {
6065 aiOrig[ displayMaster[i] ] = i;
6066 }
6067
6068 /* Do the sort - here we want multi-column sorting based on a given data source (column)
6069 * and sorting function (from oSort) in a certain direction. It's reasonably complex to
6070 * follow on it's own, but this is what we want (example two column sorting):
6071 * fnLocalSorting = function(a,b){
6072 * var iTest;
6073 * iTest = oSort['string-asc']('data11', 'data12');
6074 * if (iTest !== 0)
6075 * return iTest;
6076 * iTest = oSort['numeric-desc']('data21', 'data22');
6077 * if (iTest !== 0)
6078 * return iTest;
6079 * return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
6080 * }
6081 * Basically we have a test for each sorting column, if the data in that column is equal,
6082 * test the next column. If all columns match, then we use a numeric sort on the row
6083 * positions in the original data array to provide a stable sort.
6084 *
6085 * Note - I know it seems excessive to have two sorting methods, but the first is around
6086 * 15% faster, so the second is only maintained for backwards compatibility with sorting
6087 * methods which do not have a pre-sort formatting function.
6088 */
6089 if ( formatters === aSort.length ) {
6090 // All sort types have formatting functions
6091 displayMaster.sort( function ( a, b ) {
6092 var
6093 x, y, k, test, sort,
6094 len=aSort.length,
6095 dataA = aoData[a]._aSortData,
6096 dataB = aoData[b]._aSortData;
6097
6098 for ( k=0 ; k<len ; k++ ) {
6099 sort = aSort[k];
6100
6101 x = dataA[ sort.col ];
6102 y = dataB[ sort.col ];
6103
6104 test = x<y ? -1 : x>y ? 1 : 0;
6105 if ( test !== 0 ) {
6106 return sort.dir === 'asc' ? test : -test;
6107 }
6108 }
6109
6110 x = aiOrig[a];
6111 y = aiOrig[b];
6112 return x<y ? -1 : x>y ? 1 : 0;
6113 } );
6114 }
6115 else {
6116 // Depreciated - remove in 1.11 (providing a plug-in option)
6117 // Not all sort types have formatting methods, so we have to call their sorting
6118 // methods.
6119 displayMaster.sort( function ( a, b ) {
6120 var
6121 x, y, k, l, test, sort, fn,
6122 len=aSort.length,
6123 dataA = aoData[a]._aSortData,
6124 dataB = aoData[b]._aSortData;
6125
6126 for ( k=0 ; k<len ; k++ ) {
6127 sort = aSort[k];
6128
6129 x = dataA[ sort.col ];
6130 y = dataB[ sort.col ];
6131
6132 fn = oExtSort[ sort.type+"-"+sort.dir ] || oExtSort[ "string-"+sort.dir ];
6133 test = fn( x, y );
6134 if ( test !== 0 ) {
6135 return test;
6136 }
6137 }
6138
6139 x = aiOrig[a];
6140 y = aiOrig[b];
6141 return x<y ? -1 : x>y ? 1 : 0;
6142 } );
6143 }
6144 }
6145
6146 /* Tell the draw function that we have sorted the data */
6147 oSettings.bSorted = true;
6148 }
6149
6150
6151 function _fnSortAria ( settings )
6152 {
6153 var label;
6154 var nextSort;
6155 var columns = settings.aoColumns;
6156 var aSort = _fnSortFlatten( settings );
6157 var oAria = settings.oLanguage.oAria;
6158
6159 // ARIA attributes - need to loop all columns, to update all (removing old
6160 // attributes as needed)
6161 for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
6162 {
6163 var col = columns[i];
6164 var asSorting = col.asSorting;
6165 var sTitle = col.ariaTitle || col.sTitle.replace( /<.*?>/g, "" );
6166 var th = col.nTh;
6167
6168 // IE7 is throwing an error when setting these properties with jQuery's
6169 // attr() and removeAttr() methods...
6170 th.removeAttribute('aria-sort');
6171
6172 /* In ARIA only the first sorting column can be marked as sorting - no multi-sort option */
6173 if ( col.bSortable ) {
6174 if ( aSort.length > 0 && aSort[0].col == i ) {
6175 th.setAttribute('aria-sort', aSort[0].dir=="asc" ? "ascending" : "descending" );
6176 nextSort = asSorting[ aSort[0].index+1 ] || asSorting[0];
6177 }
6178 else {
6179 nextSort = asSorting[0];
6180 }
6181
6182 label = sTitle + ( nextSort === "asc" ?
6183 oAria.sSortAscending :
6184 oAria.sSortDescending
6185 );
6186 }
6187 else {
6188 label = sTitle;
6189 }
6190
6191 th.setAttribute('aria-label', label);
6192 }
6193 }
6194
6195
6196 /**
6197 * Function to run on user sort request
6198 * @param {object} settings dataTables settings object
6199 * @param {node} attachTo node to attach the handler to
6200 * @param {int} colIdx column sorting index
6201 * @param {boolean} [append=false] Append the requested sort to the existing
6202 * sort if true (i.e. multi-column sort)
6203 * @param {function} [callback] callback function
6204 * @memberof DataTable#oApi
6205 */
6206 function _fnSortListener ( settings, colIdx, append, callback )
6207 {
6208 var col = settings.aoColumns[ colIdx ];
6209 var sorting = settings.aaSorting;
6210 var asSorting = col.asSorting;
6211 var nextSortIdx;
6212 var next = function ( a, overflow ) {
6213 var idx = a._idx;
6214 if ( idx === undefined ) {
6215 idx = $.inArray( a[1], asSorting );
6216 }
6217
6218 return idx+1 < asSorting.length ?
6219 idx+1 :
6220 overflow ?
6221 null :
6222 0;
6223 };
6224
6225 // Convert to 2D array if needed
6226 if ( typeof sorting[0] === 'number' ) {
6227 sorting = settings.aaSorting = [ sorting ];
6228 }
6229
6230 // If appending the sort then we are multi-column sorting
6231 if ( append && settings.oFeatures.bSortMulti ) {
6232 // Are we already doing some kind of sort on this column?
6233 var sortIdx = $.inArray( colIdx, _pluck(sorting, '0') );
6234
6235 if ( sortIdx !== -1 ) {
6236 // Yes, modify the sort
6237 nextSortIdx = next( sorting[sortIdx], true );
6238
6239 if ( nextSortIdx === null && sorting.length === 1 ) {
6240 nextSortIdx = 0; // can't remove sorting completely
6241 }
6242
6243 if ( nextSortIdx === null ) {
6244 sorting.splice( sortIdx, 1 );
6245 }
6246 else {
6247 sorting[sortIdx][1] = asSorting[ nextSortIdx ];
6248 sorting[sortIdx]._idx = nextSortIdx;
6249 }
6250 }
6251 else {
6252 // No sort on this column yet
6253 sorting.push( [ colIdx, asSorting[0], 0 ] );
6254 sorting[sorting.length-1]._idx = 0;
6255 }
6256 }
6257 else if ( sorting.length && sorting[0][0] == colIdx ) {
6258 // Single column - already sorting on this column, modify the sort
6259 nextSortIdx = next( sorting[0] );
6260
6261 sorting.length = 1;
6262 sorting[0][1] = asSorting[ nextSortIdx ];
6263 sorting[0]._idx = nextSortIdx;
6264 }
6265 else {
6266 // Single column - sort only on this column
6267 sorting.length = 0;
6268 sorting.push( [ colIdx, asSorting[0] ] );
6269 sorting[0]._idx = 0;
6270 }
6271
6272 // Run the sort by calling a full redraw
6273 _fnReDraw( settings );
6274
6275 // callback used for async user interaction
6276 if ( typeof callback == 'function' ) {
6277 callback( settings );
6278 }
6279 }
6280
6281
6282 /**
6283 * Attach a sort handler (click) to a node
6284 * @param {object} settings dataTables settings object
6285 * @param {node} attachTo node to attach the handler to
6286 * @param {int} colIdx column sorting index
6287 * @param {function} [callback] callback function
6288 * @memberof DataTable#oApi
6289 */
6290 function _fnSortAttachListener ( settings, attachTo, colIdx, callback )
6291 {
6292 var col = settings.aoColumns[ colIdx ];
6293
6294 _fnBindAction( attachTo, {}, function (e) {
6295 /* If the column is not sortable - don't to anything */
6296 if ( col.bSortable === false ) {
6297 return;
6298 }
6299
6300 // If processing is enabled use a timeout to allow the processing
6301 // display to be shown - otherwise to it synchronously
6302 if ( settings.oFeatures.bProcessing ) {
6303 _fnProcessingDisplay( settings, true );
6304
6305 setTimeout( function() {
6306 _fnSortListener( settings, colIdx, e.shiftKey, callback );
6307
6308 // In server-side processing, the draw callback will remove the
6309 // processing display
6310 if ( _fnDataSource( settings ) !== 'ssp' ) {
6311 _fnProcessingDisplay( settings, false );
6312 }
6313 }, 0 );
6314 }
6315 else {
6316 _fnSortListener( settings, colIdx, e.shiftKey, callback );
6317 }
6318 } );
6319 }
6320
6321
6322 /**
6323 * Set the sorting classes on table's body, Note: it is safe to call this function
6324 * when bSort and bSortClasses are false
6325 * @param {object} oSettings dataTables settings object
6326 * @memberof DataTable#oApi
6327 */
6328 function _fnSortingClasses( settings )
6329 {
6330 var oldSort = settings.aLastSort;
6331 var sortClass = settings.oClasses.sSortColumn;
6332 var sort = _fnSortFlatten( settings );
6333 var features = settings.oFeatures;
6334 var i, ien, colIdx;
6335
6336 if ( features.bSort && features.bSortClasses ) {
6337 // Remove old sorting classes
6338 for ( i=0, ien=oldSort.length ; i<ien ; i++ ) {
6339 colIdx = oldSort[i].src;
6340
6341 // Remove column sorting
6342 $( _pluck( settings.aoData, 'anCells', colIdx ) )
6343 .removeClass( sortClass + (i<2 ? i+1 : 3) );
6344 }
6345
6346 // Add new column sorting
6347 for ( i=0, ien=sort.length ; i<ien ; i++ ) {
6348 colIdx = sort[i].src;
6349
6350 $( _pluck( settings.aoData, 'anCells', colIdx ) )
6351 .addClass( sortClass + (i<2 ? i+1 : 3) );
6352 }
6353 }
6354
6355 settings.aLastSort = sort;
6356 }
6357
6358
6359 // Get the data to sort a column, be it from cache, fresh (populating the
6360 // cache), or from a sort formatter
6361 function _fnSortData( settings, idx )
6362 {
6363 // Custom sorting function - provided by the sort data type
6364 var column = settings.aoColumns[ idx ];
6365 var customSort = DataTable.ext.order[ column.sSortDataType ];
6366 var customData;
6367
6368 if ( customSort ) {
6369 customData = customSort.call( settings.oInstance, settings, idx,
6370 _fnColumnIndexToVisible( settings, idx )
6371 );
6372 }
6373
6374 // Use / populate cache
6375 var row, cellData;
6376 var formatter = DataTable.ext.type.order[ column.sType+"-pre" ];
6377
6378 for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
6379 row = settings.aoData[i];
6380
6381 if ( ! row._aSortData ) {
6382 row._aSortData = [];
6383 }
6384
6385 if ( ! row._aSortData[idx] || customSort ) {
6386 cellData = customSort ?
6387 customData[i] : // If there was a custom sort function, use data from there
6388 _fnGetCellData( settings, i, idx, 'sort' );
6389
6390 row._aSortData[ idx ] = formatter ?
6391 formatter( cellData ) :
6392 cellData;
6393 }
6394 }
6395 }
6396
6397
6398
6399 /**
6400 * Save the state of a table
6401 * @param {object} oSettings dataTables settings object
6402 * @memberof DataTable#oApi
6403 */
6404 function _fnSaveState ( settings )
6405 {
6406 if (settings._bLoadingState) {
6407 return;
6408 }
6409
6410 /* Store the interesting variables */
6411 var state = {
6412 time: +new Date(),
6413 start: settings._iDisplayStart,
6414 length: settings._iDisplayLength,
6415 order: $.extend( true, [], settings.aaSorting ),
6416 search: _fnSearchToCamel( settings.oPreviousSearch ),
6417 columns: $.map( settings.aoColumns, function ( col, i ) {
6418 return {
6419 visible: col.bVisible,
6420 search: _fnSearchToCamel( settings.aoPreSearchCols[i] )
6421 };
6422 } )
6423 };
6424
6425 settings.oSavedState = state;
6426 _fnCallbackFire( settings, "aoStateSaveParams", 'stateSaveParams', [settings, state] );
6427
6428 if ( settings.oFeatures.bStateSave && !settings.bDestroying )
6429 {
6430 settings.fnStateSaveCallback.call( settings.oInstance, settings, state );
6431 }
6432 }
6433
6434
6435 /**
6436 * Attempt to load a saved table state
6437 * @param {object} oSettings dataTables settings object
6438 * @param {object} oInit DataTables init object so we can override settings
6439 * @param {function} callback Callback to execute when the state has been loaded
6440 * @memberof DataTable#oApi
6441 */
6442 function _fnLoadState ( settings, oInit, callback )
6443 {
6444 if ( ! settings.oFeatures.bStateSave ) {
6445 callback();
6446 return;
6447 }
6448
6449 var loaded = function(state) {
6450 _fnImplementState(settings, state, callback);
6451 }
6452
6453 var state = settings.fnStateLoadCallback.call( settings.oInstance, settings, loaded );
6454
6455 if ( state !== undefined ) {
6456 _fnImplementState( settings, state, callback );
6457 }
6458 // otherwise, wait for the loaded callback to be executed
6459
6460 return true;
6461 }
6462
6463 function _fnImplementState ( settings, s, callback) {
6464 var i, ien;
6465 var columns = settings.aoColumns;
6466 settings._bLoadingState = true;
6467
6468 // When StateRestore was introduced the state could now be implemented at any time
6469 // Not just initialisation. To do this an api instance is required in some places
6470 var api = settings._bInitComplete ? new DataTable.Api(settings) : null;
6471
6472 if ( ! s || ! s.time ) {
6473 settings._bLoadingState = false;
6474 callback();
6475 return;
6476 }
6477
6478 // Allow custom and plug-in manipulation functions to alter the saved data set and
6479 // cancelling of loading by returning false
6480 var abStateLoad = _fnCallbackFire( settings, 'aoStateLoadParams', 'stateLoadParams', [settings, s] );
6481 if ( $.inArray( false, abStateLoad ) !== -1 ) {
6482 settings._bLoadingState = false;
6483 callback();
6484 return;
6485 }
6486
6487 // Reject old data
6488 var duration = settings.iStateDuration;
6489 if ( duration > 0 && s.time < +new Date() - (duration*1000) ) {
6490 settings._bLoadingState = false;
6491 callback();
6492 return;
6493 }
6494
6495 // Number of columns have changed - all bets are off, no restore of settings
6496 if ( s.columns && columns.length !== s.columns.length ) {
6497 settings._bLoadingState = false;
6498 callback();
6499 return;
6500 }
6501
6502 // Store the saved state so it might be accessed at any time
6503 settings.oLoadedState = $.extend( true, {}, s );
6504
6505 // Page Length
6506 if ( s.length !== undefined ) {
6507 // If already initialised just set the value directly so that the select element is also updated
6508 if (api) {
6509 api.page.len(s.length)
6510 }
6511 else {
6512 settings._iDisplayLength = s.length;
6513 }
6514 }
6515
6516 // Restore key features - todo - for 1.11 this needs to be done by
6517 // subscribed events
6518 if ( s.start !== undefined ) {
6519 if(api === null) {
6520 settings._iDisplayStart = s.start;
6521 settings.iInitDisplayStart = s.start;
6522 }
6523 else {
6524 _fnPageChange(settings, s.start/settings._iDisplayLength);
6525 }
6526 }
6527
6528 // Order
6529 if ( s.order !== undefined ) {
6530 settings.aaSorting = [];
6531 $.each( s.order, function ( i, col ) {
6532 settings.aaSorting.push( col[0] >= columns.length ?
6533 [ 0, col[1] ] :
6534 col
6535 );
6536 } );
6537 }
6538
6539 // Search
6540 if ( s.search !== undefined ) {
6541 $.extend( settings.oPreviousSearch, _fnSearchToHung( s.search ) );
6542 }
6543
6544 // Columns
6545 if ( s.columns ) {
6546 for ( i=0, ien=s.columns.length ; i<ien ; i++ ) {
6547 var col = s.columns[i];
6548
6549 // Visibility
6550 if ( col.visible !== undefined ) {
6551 // If the api is defined, the table has been initialised so we need to use it rather than internal settings
6552 if (api) {
6553 // Don't redraw the columns on every iteration of this loop, we will do this at the end instead
6554 api.column(i).visible(col.visible, false);
6555 }
6556 else {
6557 columns[i].bVisible = col.visible;
6558 }
6559 }
6560
6561 // Search
6562 if ( col.search !== undefined ) {
6563 $.extend( settings.aoPreSearchCols[i], _fnSearchToHung( col.search ) );
6564 }
6565 }
6566
6567 // If the api is defined then we need to adjust the columns once the visibility has been changed
6568 if (api) {
6569 api.columns.adjust();
6570 }
6571 }
6572
6573 settings._bLoadingState = false;
6574 _fnCallbackFire( settings, 'aoStateLoaded', 'stateLoaded', [settings, s] );
6575 callback();
6576 };
6577
6578
6579 /**
6580 * Return the settings object for a particular table
6581 * @param {node} table table we are using as a dataTable
6582 * @returns {object} Settings object - or null if not found
6583 * @memberof DataTable#oApi
6584 */
6585 function _fnSettingsFromNode ( table )
6586 {
6587 var settings = DataTable.settings;
6588 var idx = $.inArray( table, _pluck( settings, 'nTable' ) );
6589
6590 return idx !== -1 ?
6591 settings[ idx ] :
6592 null;
6593 }
6594
6595
6596 /**
6597 * Log an error message
6598 * @param {object} settings dataTables settings object
6599 * @param {int} level log error messages, or display them to the user
6600 * @param {string} msg error message
6601 * @param {int} tn Technical note id to get more information about the error.
6602 * @memberof DataTable#oApi
6603 */
6604 function _fnLog( settings, level, msg, tn )
6605 {
6606 msg = 'DataTables warning: '+
6607 (settings ? 'table id='+settings.sTableId+' - ' : '')+msg;
6608
6609 if ( tn ) {
6610 msg += '. For more information about this error, please see '+
6611 'http://datatables.net/tn/'+tn;
6612 }
6613
6614 if ( ! level ) {
6615 // Backwards compatibility pre 1.10
6616 var ext = DataTable.ext;
6617 var type = ext.sErrMode || ext.errMode;
6618
6619 if ( settings ) {
6620 _fnCallbackFire( settings, null, 'error', [ settings, tn, msg ] );
6621 }
6622
6623 if ( type == 'alert' ) {
6624 alert( msg );
6625 }
6626 else if ( type == 'throw' ) {
6627 throw new Error(msg);
6628 }
6629 else if ( typeof type == 'function' ) {
6630 type( settings, tn, msg );
6631 }
6632 }
6633 else if ( window.console && console.log ) {
6634 console.log( msg );
6635 }
6636 }
6637
6638
6639 /**
6640 * See if a property is defined on one object, if so assign it to the other object
6641 * @param {object} ret target object
6642 * @param {object} src source object
6643 * @param {string} name property
6644 * @param {string} [mappedName] name to map too - optional, name used if not given
6645 * @memberof DataTable#oApi
6646 */
6647 function _fnMap( ret, src, name, mappedName )
6648 {
6649 if ( Array.isArray( name ) ) {
6650 $.each( name, function (i, val) {
6651 if ( Array.isArray( val ) ) {
6652 _fnMap( ret, src, val[0], val[1] );
6653 }
6654 else {
6655 _fnMap( ret, src, val );
6656 }
6657 } );
6658
6659 return;
6660 }
6661
6662 if ( mappedName === undefined ) {
6663 mappedName = name;
6664 }
6665
6666 if ( src[name] !== undefined ) {
6667 ret[mappedName] = src[name];
6668 }
6669 }
6670
6671
6672 /**
6673 * Extend objects - very similar to jQuery.extend, but deep copy objects, and
6674 * shallow copy arrays. The reason we need to do this, is that we don't want to
6675 * deep copy array init values (such as aaSorting) since the dev wouldn't be
6676 * able to override them, but we do want to deep copy arrays.
6677 * @param {object} out Object to extend
6678 * @param {object} extender Object from which the properties will be applied to
6679 * out
6680 * @param {boolean} breakRefs If true, then arrays will be sliced to take an
6681 * independent copy with the exception of the `data` or `aaData` parameters
6682 * if they are present. This is so you can pass in a collection to
6683 * DataTables and have that used as your data source without breaking the
6684 * references
6685 * @returns {object} out Reference, just for convenience - out === the return.
6686 * @memberof DataTable#oApi
6687 * @todo This doesn't take account of arrays inside the deep copied objects.
6688 */
6689 function _fnExtend( out, extender, breakRefs )
6690 {
6691 var val;
6692
6693 for ( var prop in extender ) {
6694 if ( extender.hasOwnProperty(prop) ) {
6695 val = extender[prop];
6696
6697 if ( $.isPlainObject( val ) ) {
6698 if ( ! $.isPlainObject( out[prop] ) ) {
6699 out[prop] = {};
6700 }
6701 $.extend( true, out[prop], val );
6702 }
6703 else if ( breakRefs && prop !== 'data' && prop !== 'aaData' && Array.isArray(val) ) {
6704 out[prop] = val.slice();
6705 }
6706 else {
6707 out[prop] = val;
6708 }
6709 }
6710 }
6711
6712 return out;
6713 }
6714
6715
6716 /**
6717 * Bind an event handers to allow a click or return key to activate the callback.
6718 * This is good for accessibility since a return on the keyboard will have the
6719 * same effect as a click, if the element has focus.
6720 * @param {element} n Element to bind the action to
6721 * @param {object} oData Data object to pass to the triggered function
6722 * @param {function} fn Callback function for when the event is triggered
6723 * @memberof DataTable#oApi
6724 */
6725 function _fnBindAction( n, oData, fn )
6726 {
6727 $(n)
6728 .on( 'click.DT', oData, function (e) {
6729 $(n).trigger('blur'); // Remove focus outline for mouse users
6730 fn(e);
6731 } )
6732 .on( 'keypress.DT', oData, function (e){
6733 if ( e.which === 13 ) {
6734 e.preventDefault();
6735 fn(e);
6736 }
6737 } )
6738 .on( 'selectstart.DT', function () {
6739 /* Take the brutal approach to cancelling text selection */
6740 return false;
6741 } );
6742 }
6743
6744
6745 /**
6746 * Register a callback function. Easily allows a callback function to be added to
6747 * an array store of callback functions that can then all be called together.
6748 * @param {object} oSettings dataTables settings object
6749 * @param {string} sStore Name of the array storage for the callbacks in oSettings
6750 * @param {function} fn Function to be called back
6751 * @param {string} sName Identifying name for the callback (i.e. a label)
6752 * @memberof DataTable#oApi
6753 */
6754 function _fnCallbackReg( oSettings, sStore, fn, sName )
6755 {
6756 if ( fn )
6757 {
6758 oSettings[sStore].push( {
6759 "fn": fn,
6760 "sName": sName
6761 } );
6762 }
6763 }
6764
6765
6766 /**
6767 * Fire callback functions and trigger events. Note that the loop over the
6768 * callback array store is done backwards! Further note that you do not want to
6769 * fire off triggers in time sensitive applications (for example cell creation)
6770 * as its slow.
6771 * @param {object} settings dataTables settings object
6772 * @param {string} callbackArr Name of the array storage for the callbacks in
6773 * oSettings
6774 * @param {string} eventName Name of the jQuery custom event to trigger. If
6775 * null no trigger is fired
6776 * @param {array} args Array of arguments to pass to the callback function /
6777 * trigger
6778 * @memberof DataTable#oApi
6779 */
6780 function _fnCallbackFire( settings, callbackArr, eventName, args )
6781 {
6782 var ret = [];
6783
6784 if ( callbackArr ) {
6785 ret = $.map( settings[callbackArr].slice().reverse(), function (val, i) {
6786 return val.fn.apply( settings.oInstance, args );
6787 } );
6788 }
6789
6790 if ( eventName !== null ) {
6791 var e = $.Event( eventName+'.dt' );
6792
6793 $(settings.nTable).trigger( e, args );
6794
6795 ret.push( e.result );
6796 }
6797
6798 return ret;
6799 }
6800
6801
6802 function _fnLengthOverflow ( settings )
6803 {
6804 var
6805 start = settings._iDisplayStart,
6806 end = settings.fnDisplayEnd(),
6807 len = settings._iDisplayLength;
6808
6809 /* If we have space to show extra rows (backing up from the end point - then do so */
6810 if ( start >= end )
6811 {
6812 start = end - len;
6813 }
6814
6815 // Keep the start record on the current page
6816 start -= (start % len);
6817
6818 if ( len === -1 || start < 0 )
6819 {
6820 start = 0;
6821 }
6822
6823 settings._iDisplayStart = start;
6824 }
6825
6826
6827 function _fnRenderer( settings, type )
6828 {
6829 var renderer = settings.renderer;
6830 var host = DataTable.ext.renderer[type];
6831
6832 if ( $.isPlainObject( renderer ) && renderer[type] ) {
6833 // Specific renderer for this type. If available use it, otherwise use
6834 // the default.
6835 return host[renderer[type]] || host._;
6836 }
6837 else if ( typeof renderer === 'string' ) {
6838 // Common renderer - if there is one available for this type use it,
6839 // otherwise use the default
6840 return host[renderer] || host._;
6841 }
6842
6843 // Use the default
6844 return host._;
6845 }
6846
6847
6848 /**
6849 * Detect the data source being used for the table. Used to simplify the code
6850 * a little (ajax) and to make it compress a little smaller.
6851 *
6852 * @param {object} settings dataTables settings object
6853 * @returns {string} Data source
6854 * @memberof DataTable#oApi
6855 */
6856 function _fnDataSource ( settings )
6857 {
6858 if ( settings.oFeatures.bServerSide ) {
6859 return 'ssp';
6860 }
6861 else if ( settings.ajax || settings.sAjaxSource ) {
6862 return 'ajax';
6863 }
6864 return 'dom';
6865 }
6866
6867
6868
6869
6870 /**
6871 * Computed structure of the DataTables API, defined by the options passed to
6872 * `DataTable.Api.register()` when building the API.
6873 *
6874 * The structure is built in order to speed creation and extension of the Api
6875 * objects since the extensions are effectively pre-parsed.
6876 *
6877 * The array is an array of objects with the following structure, where this
6878 * base array represents the Api prototype base:
6879 *
6880 * [
6881 * {
6882 * name: 'data' -- string - Property name
6883 * val: function () {}, -- function - Api method (or undefined if just an object
6884 * methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result
6885 * propExt: [ ... ] -- array - Array of Api object definitions to extend the property
6886 * },
6887 * {
6888 * name: 'row'
6889 * val: {},
6890 * methodExt: [ ... ],
6891 * propExt: [
6892 * {
6893 * name: 'data'
6894 * val: function () {},
6895 * methodExt: [ ... ],
6896 * propExt: [ ... ]
6897 * },
6898 * ...
6899 * ]
6900 * }
6901 * ]
6902 *
6903 * @type {Array}
6904 * @ignore
6905 */
6906 var __apiStruct = [];
6907
6908
6909 /**
6910 * `Array.prototype` reference.
6911 *
6912 * @type object
6913 * @ignore
6914 */
6915 var __arrayProto = Array.prototype;
6916
6917
6918 /**
6919 * Abstraction for `context` parameter of the `Api` constructor to allow it to
6920 * take several different forms for ease of use.
6921 *
6922 * Each of the input parameter types will be converted to a DataTables settings
6923 * object where possible.
6924 *
6925 * @param {string|node|jQuery|object} mixed DataTable identifier. Can be one
6926 * of:
6927 *
6928 * * `string` - jQuery selector. Any DataTables' matching the given selector
6929 * with be found and used.
6930 * * `node` - `TABLE` node which has already been formed into a DataTable.
6931 * * `jQuery` - A jQuery object of `TABLE` nodes.
6932 * * `object` - DataTables settings object
6933 * * `DataTables.Api` - API instance
6934 * @return {array|null} Matching DataTables settings objects. `null` or
6935 * `undefined` is returned if no matching DataTable is found.
6936 * @ignore
6937 */
6938 var _toSettings = function ( mixed )
6939 {
6940 var idx, jq;
6941 var settings = DataTable.settings;
6942 var tables = $.map( settings, function (el, i) {
6943 return el.nTable;
6944 } );
6945
6946 if ( ! mixed ) {
6947 return [];
6948 }
6949 else if ( mixed.nTable && mixed.oApi ) {
6950 // DataTables settings object
6951 return [ mixed ];
6952 }
6953 else if ( mixed.nodeName && mixed.nodeName.toLowerCase() === 'table' ) {
6954 // Table node
6955 idx = $.inArray( mixed, tables );
6956 return idx !== -1 ? [ settings[idx] ] : null;
6957 }
6958 else if ( mixed && typeof mixed.settings === 'function' ) {
6959 return mixed.settings().toArray();
6960 }
6961 else if ( typeof mixed === 'string' ) {
6962 // jQuery selector
6963 jq = $(mixed);
6964 }
6965 else if ( mixed instanceof $ ) {
6966 // jQuery object (also DataTables instance)
6967 jq = mixed;
6968 }
6969
6970 if ( jq ) {
6971 return jq.map( function(i) {
6972 idx = $.inArray( this, tables );
6973 return idx !== -1 ? settings[idx] : null;
6974 } ).toArray();
6975 }
6976 };
6977
6978
6979 /**
6980 * DataTables API class - used to control and interface with one or more
6981 * DataTables enhanced tables.
6982 *
6983 * The API class is heavily based on jQuery, presenting a chainable interface
6984 * that you can use to interact with tables. Each instance of the API class has
6985 * a "context" - i.e. the tables that it will operate on. This could be a single
6986 * table, all tables on a page or a sub-set thereof.
6987 *
6988 * Additionally the API is designed to allow you to easily work with the data in
6989 * the tables, retrieving and manipulating it as required. This is done by
6990 * presenting the API class as an array like interface. The contents of the
6991 * array depend upon the actions requested by each method (for example
6992 * `rows().nodes()` will return an array of nodes, while `rows().data()` will
6993 * return an array of objects or arrays depending upon your table's
6994 * configuration). The API object has a number of array like methods (`push`,
6995 * `pop`, `reverse` etc) as well as additional helper methods (`each`, `pluck`,
6996 * `unique` etc) to assist your working with the data held in a table.
6997 *
6998 * Most methods (those which return an Api instance) are chainable, which means
6999 * the return from a method call also has all of the methods available that the
7000 * top level object had. For example, these two calls are equivalent:
7001 *
7002 * // Not chained
7003 * api.row.add( {...} );
7004 * api.draw();
7005 *
7006 * // Chained
7007 * api.row.add( {...} ).draw();
7008 *
7009 * @class DataTable.Api
7010 * @param {array|object|string|jQuery} context DataTable identifier. This is
7011 * used to define which DataTables enhanced tables this API will operate on.
7012 * Can be one of:
7013 *
7014 * * `string` - jQuery selector. Any DataTables' matching the given selector
7015 * with be found and used.
7016 * * `node` - `TABLE` node which has already been formed into a DataTable.
7017 * * `jQuery` - A jQuery object of `TABLE` nodes.
7018 * * `object` - DataTables settings object
7019 * @param {array} [data] Data to initialise the Api instance with.
7020 *
7021 * @example
7022 * // Direct initialisation during DataTables construction
7023 * var api = $('#example').DataTable();
7024 *
7025 * @example
7026 * // Initialisation using a DataTables jQuery object
7027 * var api = $('#example').dataTable().api();
7028 *
7029 * @example
7030 * // Initialisation as a constructor
7031 * var api = new $.fn.DataTable.Api( 'table.dataTable' );
7032 */
7033 _Api = function ( context, data )
7034 {
7035 if ( ! (this instanceof _Api) ) {
7036 return new _Api( context, data );
7037 }
7038
7039 var settings = [];
7040 var ctxSettings = function ( o ) {
7041 var a = _toSettings( o );
7042 if ( a ) {
7043 settings.push.apply( settings, a );
7044 }
7045 };
7046
7047 if ( Array.isArray( context ) ) {
7048 for ( var i=0, ien=context.length ; i<ien ; i++ ) {
7049 ctxSettings( context[i] );
7050 }
7051 }
7052 else {
7053 ctxSettings( context );
7054 }
7055
7056 // Remove duplicates
7057 this.context = _unique( settings );
7058
7059 // Initial data
7060 if ( data ) {
7061 $.merge( this, data );
7062 }
7063
7064 // selector
7065 this.selector = {
7066 rows: null,
7067 cols: null,
7068 opts: null
7069 };
7070
7071 _Api.extend( this, this, __apiStruct );
7072 };
7073
7074 DataTable.Api = _Api;
7075
7076 // Don't destroy the existing prototype, just extend it. Required for jQuery 2's
7077 // isPlainObject.
7078 $.extend( _Api.prototype, {
7079 any: function ()
7080 {
7081 return this.count() !== 0;
7082 },
7083
7084
7085 concat: __arrayProto.concat,
7086
7087
7088 context: [], // array of table settings objects
7089
7090
7091 count: function ()
7092 {
7093 return this.flatten().length;
7094 },
7095
7096
7097 each: function ( fn )
7098 {
7099 for ( var i=0, ien=this.length ; i<ien; i++ ) {
7100 fn.call( this, this[i], i, this );
7101 }
7102
7103 return this;
7104 },
7105
7106
7107 eq: function ( idx )
7108 {
7109 var ctx = this.context;
7110
7111 return ctx.length > idx ?
7112 new _Api( ctx[idx], this[idx] ) :
7113 null;
7114 },
7115
7116
7117 filter: function ( fn )
7118 {
7119 var a = [];
7120
7121 if ( __arrayProto.filter ) {
7122 a = __arrayProto.filter.call( this, fn, this );
7123 }
7124 else {
7125 // Compatibility for browsers without EMCA-252-5 (JS 1.6)
7126 for ( var i=0, ien=this.length ; i<ien ; i++ ) {
7127 if ( fn.call( this, this[i], i, this ) ) {
7128 a.push( this[i] );
7129 }
7130 }
7131 }
7132
7133 return new _Api( this.context, a );
7134 },
7135
7136
7137 flatten: function ()
7138 {
7139 var a = [];
7140 return new _Api( this.context, a.concat.apply( a, this.toArray() ) );
7141 },
7142
7143
7144 join: __arrayProto.join,
7145
7146
7147 indexOf: __arrayProto.indexOf || function (obj, start)
7148 {
7149 for ( var i=(start || 0), ien=this.length ; i<ien ; i++ ) {
7150 if ( this[i] === obj ) {
7151 return i;
7152 }
7153 }
7154 return -1;
7155 },
7156
7157 iterator: function ( flatten, type, fn, alwaysNew ) {
7158 var
7159 a = [], ret,
7160 i, ien, j, jen,
7161 context = this.context,
7162 rows, items, item,
7163 selector = this.selector;
7164
7165 // Argument shifting
7166 if ( typeof flatten === 'string' ) {
7167 alwaysNew = fn;
7168 fn = type;
7169 type = flatten;
7170 flatten = false;
7171 }
7172
7173 for ( i=0, ien=context.length ; i<ien ; i++ ) {
7174 var apiInst = new _Api( context[i] );
7175
7176 if ( type === 'table' ) {
7177 ret = fn.call( apiInst, context[i], i );
7178
7179 if ( ret !== undefined ) {
7180 a.push( ret );
7181 }
7182 }
7183 else if ( type === 'columns' || type === 'rows' ) {
7184 // this has same length as context - one entry for each table
7185 ret = fn.call( apiInst, context[i], this[i], i );
7186
7187 if ( ret !== undefined ) {
7188 a.push( ret );
7189 }
7190 }
7191 else if ( type === 'column' || type === 'column-rows' || type === 'row' || type === 'cell' ) {
7192 // columns and rows share the same structure.
7193 // 'this' is an array of column indexes for each context
7194 items = this[i];
7195
7196 if ( type === 'column-rows' ) {
7197 rows = _selector_row_indexes( context[i], selector.opts );
7198 }
7199
7200 for ( j=0, jen=items.length ; j<jen ; j++ ) {
7201 item = items[j];
7202
7203 if ( type === 'cell' ) {
7204 ret = fn.call( apiInst, context[i], item.row, item.column, i, j );
7205 }
7206 else {
7207 ret = fn.call( apiInst, context[i], item, i, j, rows );
7208 }
7209
7210 if ( ret !== undefined ) {
7211 a.push( ret );
7212 }
7213 }
7214 }
7215 }
7216
7217 if ( a.length || alwaysNew ) {
7218 var api = new _Api( context, flatten ? a.concat.apply( [], a ) : a );
7219 var apiSelector = api.selector;
7220 apiSelector.rows = selector.rows;
7221 apiSelector.cols = selector.cols;
7222 apiSelector.opts = selector.opts;
7223 return api;
7224 }
7225 return this;
7226 },
7227
7228
7229 lastIndexOf: __arrayProto.lastIndexOf || function (obj, start)
7230 {
7231 // Bit cheeky...
7232 return this.indexOf.apply( this.toArray.reverse(), arguments );
7233 },
7234
7235
7236 length: 0,
7237
7238
7239 map: function ( fn )
7240 {
7241 var a = [];
7242
7243 if ( __arrayProto.map ) {
7244 a = __arrayProto.map.call( this, fn, this );
7245 }
7246 else {
7247 // Compatibility for browsers without EMCA-252-5 (JS 1.6)
7248 for ( var i=0, ien=this.length ; i<ien ; i++ ) {
7249 a.push( fn.call( this, this[i], i ) );
7250 }
7251 }
7252
7253 return new _Api( this.context, a );
7254 },
7255
7256
7257 pluck: function ( prop )
7258 {
7259 let fn = DataTable.util.get(prop);
7260
7261 return this.map( function ( el ) {
7262 return fn(el);
7263 } );
7264 },
7265
7266 pop: __arrayProto.pop,
7267
7268
7269 push: __arrayProto.push,
7270
7271
7272 // Does not return an API instance
7273 reduce: __arrayProto.reduce || function ( fn, init )
7274 {
7275 return _fnReduce( this, fn, init, 0, this.length, 1 );
7276 },
7277
7278
7279 reduceRight: __arrayProto.reduceRight || function ( fn, init )
7280 {
7281 return _fnReduce( this, fn, init, this.length-1, -1, -1 );
7282 },
7283
7284
7285 reverse: __arrayProto.reverse,
7286
7287
7288 // Object with rows, columns and opts
7289 selector: null,
7290
7291
7292 shift: __arrayProto.shift,
7293
7294
7295 slice: function () {
7296 return new _Api( this.context, this );
7297 },
7298
7299
7300 sort: __arrayProto.sort, // ? name - order?
7301
7302
7303 splice: __arrayProto.splice,
7304
7305
7306 toArray: function ()
7307 {
7308 return __arrayProto.slice.call( this );
7309 },
7310
7311
7312 to$: function ()
7313 {
7314 return $( this );
7315 },
7316
7317
7318 toJQuery: function ()
7319 {
7320 return $( this );
7321 },
7322
7323
7324 unique: function ()
7325 {
7326 return new _Api( this.context, _unique(this) );
7327 },
7328
7329
7330 unshift: __arrayProto.unshift
7331 } );
7332
7333
7334 _Api.extend = function ( scope, obj, ext )
7335 {
7336 // Only extend API instances and static properties of the API
7337 if ( ! ext.length || ! obj || ( ! (obj instanceof _Api) && ! obj.__dt_wrapper ) ) {
7338 return;
7339 }
7340
7341 var
7342 i, ien,
7343 struct,
7344 methodScoping = function ( scope, fn, struc ) {
7345 return function () {
7346 var ret = fn.apply( scope, arguments );
7347
7348 // Method extension
7349 _Api.extend( ret, ret, struc.methodExt );
7350 return ret;
7351 };
7352 };
7353
7354 for ( i=0, ien=ext.length ; i<ien ; i++ ) {
7355 struct = ext[i];
7356
7357 // Value
7358 obj[ struct.name ] = struct.type === 'function' ?
7359 methodScoping( scope, struct.val, struct ) :
7360 struct.type === 'object' ?
7361 {} :
7362 struct.val;
7363
7364 obj[ struct.name ].__dt_wrapper = true;
7365
7366 // Property extension
7367 _Api.extend( scope, obj[ struct.name ], struct.propExt );
7368 }
7369 };
7370
7371
7372 // @todo - Is there need for an augment function?
7373 // _Api.augment = function ( inst, name )
7374 // {
7375 // // Find src object in the structure from the name
7376 // var parts = name.split('.');
7377
7378 // _Api.extend( inst, obj );
7379 // };
7380
7381
7382 // [
7383 // {
7384 // name: 'data' -- string - Property name
7385 // val: function () {}, -- function - Api method (or undefined if just an object
7386 // methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result
7387 // propExt: [ ... ] -- array - Array of Api object definitions to extend the property
7388 // },
7389 // {
7390 // name: 'row'
7391 // val: {},
7392 // methodExt: [ ... ],
7393 // propExt: [
7394 // {
7395 // name: 'data'
7396 // val: function () {},
7397 // methodExt: [ ... ],
7398 // propExt: [ ... ]
7399 // },
7400 // ...
7401 // ]
7402 // }
7403 // ]
7404
7405 _Api.register = _api_register = function ( name, val )
7406 {
7407 if ( Array.isArray( name ) ) {
7408 for ( var j=0, jen=name.length ; j<jen ; j++ ) {
7409 _Api.register( name[j], val );
7410 }
7411 return;
7412 }
7413
7414 var
7415 i, ien,
7416 heir = name.split('.'),
7417 struct = __apiStruct,
7418 key, method;
7419
7420 var find = function ( src, name ) {
7421 for ( var i=0, ien=src.length ; i<ien ; i++ ) {
7422 if ( src[i].name === name ) {
7423 return src[i];
7424 }
7425 }
7426 return null;
7427 };
7428
7429 for ( i=0, ien=heir.length ; i<ien ; i++ ) {
7430 method = heir[i].indexOf('()') !== -1;
7431 key = method ?
7432 heir[i].replace('()', '') :
7433 heir[i];
7434
7435 var src = find( struct, key );
7436 if ( ! src ) {
7437 src = {
7438 name: key,
7439 val: {},
7440 methodExt: [],
7441 propExt: [],
7442 type: 'object'
7443 };
7444 struct.push( src );
7445 }
7446
7447 if ( i === ien-1 ) {
7448 src.val = val;
7449 src.type = typeof val === 'function' ?
7450 'function' :
7451 $.isPlainObject( val ) ?
7452 'object' :
7453 'other';
7454 }
7455 else {
7456 struct = method ?
7457 src.methodExt :
7458 src.propExt;
7459 }
7460 }
7461 };
7462
7463 _Api.registerPlural = _api_registerPlural = function ( pluralName, singularName, val ) {
7464 _Api.register( pluralName, val );
7465
7466 _Api.register( singularName, function () {
7467 var ret = val.apply( this, arguments );
7468
7469 if ( ret === this ) {
7470 // Returned item is the API instance that was passed in, return it
7471 return this;
7472 }
7473 else if ( ret instanceof _Api ) {
7474 // New API instance returned, want the value from the first item
7475 // in the returned array for the singular result.
7476 return ret.length ?
7477 Array.isArray( ret[0] ) ?
7478 new _Api( ret.context, ret[0] ) : // Array results are 'enhanced'
7479 ret[0] :
7480 undefined;
7481 }
7482
7483 // Non-API return - just fire it back
7484 return ret;
7485 } );
7486 };
7487
7488
7489 /**
7490 * Selector for HTML tables. Apply the given selector to the give array of
7491 * DataTables settings objects.
7492 *
7493 * @param {string|integer} [selector] jQuery selector string or integer
7494 * @param {array} Array of DataTables settings objects to be filtered
7495 * @return {array}
7496 * @ignore
7497 */
7498 var __table_selector = function ( selector, a )
7499 {
7500 if ( Array.isArray(selector) ) {
7501 return $.map( selector, function (item) {
7502 return __table_selector(item, a);
7503 } );
7504 }
7505
7506 // Integer is used to pick out a table by index
7507 if ( typeof selector === 'number' ) {
7508 return [ a[ selector ] ];
7509 }
7510
7511 // Perform a jQuery selector on the table nodes
7512 var nodes = $.map( a, function (el, i) {
7513 return el.nTable;
7514 } );
7515
7516 return $(nodes)
7517 .filter( selector )
7518 .map( function (i) {
7519 // Need to translate back from the table node to the settings
7520 var idx = $.inArray( this, nodes );
7521 return a[ idx ];
7522 } )
7523 .toArray();
7524 };
7525
7526
7527
7528 /**
7529 * Context selector for the API's context (i.e. the tables the API instance
7530 * refers to.
7531 *
7532 * @name DataTable.Api#tables
7533 * @param {string|integer} [selector] Selector to pick which tables the iterator
7534 * should operate on. If not given, all tables in the current context are
7535 * used. This can be given as a jQuery selector (for example `':gt(0)'`) to
7536 * select multiple tables or as an integer to select a single table.
7537 * @returns {DataTable.Api} Returns a new API instance if a selector is given.
7538 */
7539 _api_register( 'tables()', function ( selector ) {
7540 // A new instance is created if there was a selector specified
7541 return selector !== undefined && selector !== null ?
7542 new _Api( __table_selector( selector, this.context ) ) :
7543 this;
7544 } );
7545
7546
7547 _api_register( 'table()', function ( selector ) {
7548 var tables = this.tables( selector );
7549 var ctx = tables.context;
7550
7551 // Truncate to the first matched table
7552 return ctx.length ?
7553 new _Api( ctx[0] ) :
7554 tables;
7555 } );
7556
7557
7558 _api_registerPlural( 'tables().nodes()', 'table().node()' , function () {
7559 return this.iterator( 'table', function ( ctx ) {
7560 return ctx.nTable;
7561 }, 1 );
7562 } );
7563
7564
7565 _api_registerPlural( 'tables().body()', 'table().body()' , function () {
7566 return this.iterator( 'table', function ( ctx ) {
7567 return ctx.nTBody;
7568 }, 1 );
7569 } );
7570
7571
7572 _api_registerPlural( 'tables().header()', 'table().header()' , function () {
7573 return this.iterator( 'table', function ( ctx ) {
7574 return ctx.nTHead;
7575 }, 1 );
7576 } );
7577
7578
7579 _api_registerPlural( 'tables().footer()', 'table().footer()' , function () {
7580 return this.iterator( 'table', function ( ctx ) {
7581 return ctx.nTFoot;
7582 }, 1 );
7583 } );
7584
7585
7586 _api_registerPlural( 'tables().containers()', 'table().container()' , function () {
7587 return this.iterator( 'table', function ( ctx ) {
7588 return ctx.nTableWrapper;
7589 }, 1 );
7590 } );
7591
7592
7593
7594 /**
7595 * Redraw the tables in the current context.
7596 */
7597 _api_register( 'draw()', function ( paging ) {
7598 return this.iterator( 'table', function ( settings ) {
7599 if ( paging === 'page' ) {
7600 _fnDraw( settings );
7601 }
7602 else {
7603 if ( typeof paging === 'string' ) {
7604 paging = paging === 'full-hold' ?
7605 false :
7606 true;
7607 }
7608
7609 _fnReDraw( settings, paging===false );
7610 }
7611 } );
7612 } );
7613
7614
7615
7616 /**
7617 * Get the current page index.
7618 *
7619 * @return {integer} Current page index (zero based)
7620 *//**
7621 * Set the current page.
7622 *
7623 * Note that if you attempt to show a page which does not exist, DataTables will
7624 * not throw an error, but rather reset the paging.
7625 *
7626 * @param {integer|string} action The paging action to take. This can be one of:
7627 * * `integer` - The page index to jump to
7628 * * `string` - An action to take:
7629 * * `first` - Jump to first page.
7630 * * `next` - Jump to the next page
7631 * * `previous` - Jump to previous page
7632 * * `last` - Jump to the last page.
7633 * @returns {DataTables.Api} this
7634 */
7635 _api_register( 'page()', function ( action ) {
7636 if ( action === undefined ) {
7637 return this.page.info().page; // not an expensive call
7638 }
7639
7640 // else, have an action to take on all tables
7641 return this.iterator( 'table', function ( settings ) {
7642 _fnPageChange( settings, action );
7643 } );
7644 } );
7645
7646
7647 /**
7648 * Paging information for the first table in the current context.
7649 *
7650 * If you require paging information for another table, use the `table()` method
7651 * with a suitable selector.
7652 *
7653 * @return {object} Object with the following properties set:
7654 * * `page` - Current page index (zero based - i.e. the first page is `0`)
7655 * * `pages` - Total number of pages
7656 * * `start` - Display index for the first record shown on the current page
7657 * * `end` - Display index for the last record shown on the current page
7658 * * `length` - Display length (number of records). Note that generally `start
7659 * + length = end`, but this is not always true, for example if there are
7660 * only 2 records to show on the final page, with a length of 10.
7661 * * `recordsTotal` - Full data set length
7662 * * `recordsDisplay` - Data set length once the current filtering criterion
7663 * are applied.
7664 */
7665 _api_register( 'page.info()', function ( action ) {
7666 if ( this.context.length === 0 ) {
7667 return undefined;
7668 }
7669
7670 var
7671 settings = this.context[0],
7672 start = settings._iDisplayStart,
7673 len = settings.oFeatures.bPaginate ? settings._iDisplayLength : -1,
7674 visRecords = settings.fnRecordsDisplay(),
7675 all = len === -1;
7676
7677 return {
7678 "page": all ? 0 : Math.floor( start / len ),
7679 "pages": all ? 1 : Math.ceil( visRecords / len ),
7680 "start": start,
7681 "end": settings.fnDisplayEnd(),
7682 "length": len,
7683 "recordsTotal": settings.fnRecordsTotal(),
7684 "recordsDisplay": visRecords,
7685 "serverSide": _fnDataSource( settings ) === 'ssp'
7686 };
7687 } );
7688
7689
7690 /**
7691 * Get the current page length.
7692 *
7693 * @return {integer} Current page length. Note `-1` indicates that all records
7694 * are to be shown.
7695 *//**
7696 * Set the current page length.
7697 *
7698 * @param {integer} Page length to set. Use `-1` to show all records.
7699 * @returns {DataTables.Api} this
7700 */
7701 _api_register( 'page.len()', function ( len ) {
7702 // Note that we can't call this function 'length()' because `length`
7703 // is a Javascript property of functions which defines how many arguments
7704 // the function expects.
7705 if ( len === undefined ) {
7706 return this.context.length !== 0 ?
7707 this.context[0]._iDisplayLength :
7708 undefined;
7709 }
7710
7711 // else, set the page length
7712 return this.iterator( 'table', function ( settings ) {
7713 _fnLengthChange( settings, len );
7714 } );
7715 } );
7716
7717
7718
7719 var __reload = function ( settings, holdPosition, callback ) {
7720 // Use the draw event to trigger a callback
7721 if ( callback ) {
7722 var api = new _Api( settings );
7723
7724 api.one( 'draw', function () {
7725 callback( api.ajax.json() );
7726 } );
7727 }
7728
7729 if ( _fnDataSource( settings ) == 'ssp' ) {
7730 _fnReDraw( settings, holdPosition );
7731 }
7732 else {
7733 _fnProcessingDisplay( settings, true );
7734
7735 // Cancel an existing request
7736 var xhr = settings.jqXHR;
7737 if ( xhr && xhr.readyState !== 4 ) {
7738 xhr.abort();
7739 }
7740
7741 // Trigger xhr
7742 _fnBuildAjax( settings, [], function( json ) {
7743 _fnClearTable( settings );
7744
7745 var data = _fnAjaxDataSrc( settings, json );
7746 for ( var i=0, ien=data.length ; i<ien ; i++ ) {
7747 _fnAddData( settings, data[i] );
7748 }
7749
7750 _fnReDraw( settings, holdPosition );
7751 _fnProcessingDisplay( settings, false );
7752 } );
7753 }
7754 };
7755
7756
7757 /**
7758 * Get the JSON response from the last Ajax request that DataTables made to the
7759 * server. Note that this returns the JSON from the first table in the current
7760 * context.
7761 *
7762 * @return {object} JSON received from the server.
7763 */
7764 _api_register( 'ajax.json()', function () {
7765 var ctx = this.context;
7766
7767 if ( ctx.length > 0 ) {
7768 return ctx[0].json;
7769 }
7770
7771 // else return undefined;
7772 } );
7773
7774
7775 /**
7776 * Get the data submitted in the last Ajax request
7777 */
7778 _api_register( 'ajax.params()', function () {
7779 var ctx = this.context;
7780
7781 if ( ctx.length > 0 ) {
7782 return ctx[0].oAjaxData;
7783 }
7784
7785 // else return undefined;
7786 } );
7787
7788
7789 /**
7790 * Reload tables from the Ajax data source. Note that this function will
7791 * automatically re-draw the table when the remote data has been loaded.
7792 *
7793 * @param {boolean} [reset=true] Reset (default) or hold the current paging
7794 * position. A full re-sort and re-filter is performed when this method is
7795 * called, which is why the pagination reset is the default action.
7796 * @returns {DataTables.Api} this
7797 */
7798 _api_register( 'ajax.reload()', function ( callback, resetPaging ) {
7799 return this.iterator( 'table', function (settings) {
7800 __reload( settings, resetPaging===false, callback );
7801 } );
7802 } );
7803
7804
7805 /**
7806 * Get the current Ajax URL. Note that this returns the URL from the first
7807 * table in the current context.
7808 *
7809 * @return {string} Current Ajax source URL
7810 *//**
7811 * Set the Ajax URL. Note that this will set the URL for all tables in the
7812 * current context.
7813 *
7814 * @param {string} url URL to set.
7815 * @returns {DataTables.Api} this
7816 */
7817 _api_register( 'ajax.url()', function ( url ) {
7818 var ctx = this.context;
7819
7820 if ( url === undefined ) {
7821 // get
7822 if ( ctx.length === 0 ) {
7823 return undefined;
7824 }
7825 ctx = ctx[0];
7826
7827 return ctx.ajax ?
7828 $.isPlainObject( ctx.ajax ) ?
7829 ctx.ajax.url :
7830 ctx.ajax :
7831 ctx.sAjaxSource;
7832 }
7833
7834 // set
7835 return this.iterator( 'table', function ( settings ) {
7836 if ( $.isPlainObject( settings.ajax ) ) {
7837 settings.ajax.url = url;
7838 }
7839 else {
7840 settings.ajax = url;
7841 }
7842 // No need to consider sAjaxSource here since DataTables gives priority
7843 // to `ajax` over `sAjaxSource`. So setting `ajax` here, renders any
7844 // value of `sAjaxSource` redundant.
7845 } );
7846 } );
7847
7848
7849 /**
7850 * Load data from the newly set Ajax URL. Note that this method is only
7851 * available when `ajax.url()` is used to set a URL. Additionally, this method
7852 * has the same effect as calling `ajax.reload()` but is provided for
7853 * convenience when setting a new URL. Like `ajax.reload()` it will
7854 * automatically redraw the table once the remote data has been loaded.
7855 *
7856 * @returns {DataTables.Api} this
7857 */
7858 _api_register( 'ajax.url().load()', function ( callback, resetPaging ) {
7859 // Same as a reload, but makes sense to present it for easy access after a
7860 // url change
7861 return this.iterator( 'table', function ( ctx ) {
7862 __reload( ctx, resetPaging===false, callback );
7863 } );
7864 } );
7865
7866
7867
7868
7869 var _selector_run = function ( type, selector, selectFn, settings, opts )
7870 {
7871 var
7872 out = [], res,
7873 a, i, ien, j, jen,
7874 selectorType = typeof selector;
7875
7876 // Can't just check for isArray here, as an API or jQuery instance might be
7877 // given with their array like look
7878 if ( ! selector || selectorType === 'string' || selectorType === 'function' || selector.length === undefined ) {
7879 selector = [ selector ];
7880 }
7881
7882 for ( i=0, ien=selector.length ; i<ien ; i++ ) {
7883 // Only split on simple strings - complex expressions will be jQuery selectors
7884 a = selector[i] && selector[i].split && ! selector[i].match(/[\[\(:]/) ?
7885 selector[i].split(',') :
7886 [ selector[i] ];
7887
7888 for ( j=0, jen=a.length ; j<jen ; j++ ) {
7889 res = selectFn( typeof a[j] === 'string' ? (a[j]).trim() : a[j] );
7890
7891 if ( res && res.length ) {
7892 out = out.concat( res );
7893 }
7894 }
7895 }
7896
7897 // selector extensions
7898 var ext = _ext.selector[ type ];
7899 if ( ext.length ) {
7900 for ( i=0, ien=ext.length ; i<ien ; i++ ) {
7901 out = ext[i]( settings, opts, out );
7902 }
7903 }
7904
7905 return _unique( out );
7906 };
7907
7908
7909 var _selector_opts = function ( opts )
7910 {
7911 if ( ! opts ) {
7912 opts = {};
7913 }
7914
7915 // Backwards compatibility for 1.9- which used the terminology filter rather
7916 // than search
7917 if ( opts.filter && opts.search === undefined ) {
7918 opts.search = opts.filter;
7919 }
7920
7921 return $.extend( {
7922 search: 'none',
7923 order: 'current',
7924 page: 'all'
7925 }, opts );
7926 };
7927
7928
7929 var _selector_first = function ( inst )
7930 {
7931 // Reduce the API instance to the first item found
7932 for ( var i=0, ien=inst.length ; i<ien ; i++ ) {
7933 if ( inst[i].length > 0 ) {
7934 // Assign the first element to the first item in the instance
7935 // and truncate the instance and context
7936 inst[0] = inst[i];
7937 inst[0].length = 1;
7938 inst.length = 1;
7939 inst.context = [ inst.context[i] ];
7940
7941 return inst;
7942 }
7943 }
7944
7945 // Not found - return an empty instance
7946 inst.length = 0;
7947 return inst;
7948 };
7949
7950
7951 var _selector_row_indexes = function ( settings, opts )
7952 {
7953 var
7954 i, ien, tmp, a=[],
7955 displayFiltered = settings.aiDisplay,
7956 displayMaster = settings.aiDisplayMaster;
7957
7958 var
7959 search = opts.search, // none, applied, removed
7960 order = opts.order, // applied, current, index (original - compatibility with 1.9)
7961 page = opts.page; // all, current
7962
7963 if ( _fnDataSource( settings ) == 'ssp' ) {
7964 // In server-side processing mode, most options are irrelevant since
7965 // rows not shown don't exist and the index order is the applied order
7966 // Removed is a special case - for consistency just return an empty
7967 // array
7968 return search === 'removed' ?
7969 [] :
7970 _range( 0, displayMaster.length );
7971 }
7972 else if ( page == 'current' ) {
7973 // Current page implies that order=current and filter=applied, since it is
7974 // fairly senseless otherwise, regardless of what order and search actually
7975 // are
7976 for ( i=settings._iDisplayStart, ien=settings.fnDisplayEnd() ; i<ien ; i++ ) {
7977 a.push( displayFiltered[i] );
7978 }
7979 }
7980 else if ( order == 'current' || order == 'applied' ) {
7981 if ( search == 'none') {
7982 a = displayMaster.slice();
7983 }
7984 else if ( search == 'applied' ) {
7985 a = displayFiltered.slice();
7986 }
7987 else if ( search == 'removed' ) {
7988 // O(n+m) solution by creating a hash map
7989 var displayFilteredMap = {};
7990
7991 for ( var i=0, ien=displayFiltered.length ; i<ien ; i++ ) {
7992 displayFilteredMap[displayFiltered[i]] = null;
7993 }
7994
7995 a = $.map( displayMaster, function (el) {
7996 return ! displayFilteredMap.hasOwnProperty(el) ?
7997 el :
7998 null;
7999 } );
8000 }
8001 }
8002 else if ( order == 'index' || order == 'original' ) {
8003 for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
8004 if ( search == 'none' ) {
8005 a.push( i );
8006 }
8007 else { // applied | removed
8008 tmp = $.inArray( i, displayFiltered );
8009
8010 if ((tmp === -1 && search == 'removed') ||
8011 (tmp >= 0 && search == 'applied') )
8012 {
8013 a.push( i );
8014 }
8015 }
8016 }
8017 }
8018
8019 return a;
8020 };
8021
8022
8023 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8024 * Rows
8025 *
8026 * {} - no selector - use all available rows
8027 * {integer} - row aoData index
8028 * {node} - TR node
8029 * {string} - jQuery selector to apply to the TR elements
8030 * {array} - jQuery array of nodes, or simply an array of TR nodes
8031 *
8032 */
8033 var __row_selector = function ( settings, selector, opts )
8034 {
8035 var rows;
8036 var run = function ( sel ) {
8037 var selInt = _intVal( sel );
8038 var i, ien;
8039 var aoData = settings.aoData;
8040
8041 // Short cut - selector is a number and no options provided (default is
8042 // all records, so no need to check if the index is in there, since it
8043 // must be - dev error if the index doesn't exist).
8044 if ( selInt !== null && ! opts ) {
8045 return [ selInt ];
8046 }
8047
8048 if ( ! rows ) {
8049 rows = _selector_row_indexes( settings, opts );
8050 }
8051
8052 if ( selInt !== null && $.inArray( selInt, rows ) !== -1 ) {
8053 // Selector - integer
8054 return [ selInt ];
8055 }
8056 else if ( sel === null || sel === undefined || sel === '' ) {
8057 // Selector - none
8058 return rows;
8059 }
8060
8061 // Selector - function
8062 if ( typeof sel === 'function' ) {
8063 return $.map( rows, function (idx) {
8064 var row = aoData[ idx ];
8065 return sel( idx, row._aData, row.nTr ) ? idx : null;
8066 } );
8067 }
8068
8069 // Selector - node
8070 if ( sel.nodeName ) {
8071 var rowIdx = sel._DT_RowIndex; // Property added by DT for fast lookup
8072 var cellIdx = sel._DT_CellIndex;
8073
8074 if ( rowIdx !== undefined ) {
8075 // Make sure that the row is actually still present in the table
8076 return aoData[ rowIdx ] && aoData[ rowIdx ].nTr === sel ?
8077 [ rowIdx ] :
8078 [];
8079 }
8080 else if ( cellIdx ) {
8081 return aoData[ cellIdx.row ] && aoData[ cellIdx.row ].nTr === sel.parentNode ?
8082 [ cellIdx.row ] :
8083 [];
8084 }
8085 else {
8086 var host = $(sel).closest('*[data-dt-row]');
8087 return host.length ?
8088 [ host.data('dt-row') ] :
8089 [];
8090 }
8091 }
8092
8093 // ID selector. Want to always be able to select rows by id, regardless
8094 // of if the tr element has been created or not, so can't rely upon
8095 // jQuery here - hence a custom implementation. This does not match
8096 // Sizzle's fast selector or HTML4 - in HTML5 the ID can be anything,
8097 // but to select it using a CSS selector engine (like Sizzle or
8098 // querySelect) it would need to need to be escaped for some characters.
8099 // DataTables simplifies this for row selectors since you can select
8100 // only a row. A # indicates an id any anything that follows is the id -
8101 // unescaped.
8102 if ( typeof sel === 'string' && sel.charAt(0) === '#' ) {
8103 // get row index from id
8104 var rowObj = settings.aIds[ sel.replace( /^#/, '' ) ];
8105 if ( rowObj !== undefined ) {
8106 return [ rowObj.idx ];
8107 }
8108
8109 // need to fall through to jQuery in case there is DOM id that
8110 // matches
8111 }
8112
8113 // Get nodes in the order from the `rows` array with null values removed
8114 var nodes = _removeEmpty(
8115 _pluck_order( settings.aoData, rows, 'nTr' )
8116 );
8117
8118 // Selector - jQuery selector string, array of nodes or jQuery object/
8119 // As jQuery's .filter() allows jQuery objects to be passed in filter,
8120 // it also allows arrays, so this will cope with all three options
8121 return $(nodes)
8122 .filter( sel )
8123 .map( function () {
8124 return this._DT_RowIndex;
8125 } )
8126 .toArray();
8127 };
8128
8129 return _selector_run( 'row', selector, run, settings, opts );
8130 };
8131
8132
8133 _api_register( 'rows()', function ( selector, opts ) {
8134 // argument shifting
8135 if ( selector === undefined ) {
8136 selector = '';
8137 }
8138 else if ( $.isPlainObject( selector ) ) {
8139 opts = selector;
8140 selector = '';
8141 }
8142
8143 opts = _selector_opts( opts );
8144
8145 var inst = this.iterator( 'table', function ( settings ) {
8146 return __row_selector( settings, selector, opts );
8147 }, 1 );
8148
8149 // Want argument shifting here and in __row_selector?
8150 inst.selector.rows = selector;
8151 inst.selector.opts = opts;
8152
8153 return inst;
8154 } );
8155
8156 _api_register( 'rows().nodes()', function () {
8157 return this.iterator( 'row', function ( settings, row ) {
8158 return settings.aoData[ row ].nTr || undefined;
8159 }, 1 );
8160 } );
8161
8162 _api_register( 'rows().data()', function () {
8163 return this.iterator( true, 'rows', function ( settings, rows ) {
8164 return _pluck_order( settings.aoData, rows, '_aData' );
8165 }, 1 );
8166 } );
8167
8168 _api_registerPlural( 'rows().cache()', 'row().cache()', function ( type ) {
8169 return this.iterator( 'row', function ( settings, row ) {
8170 var r = settings.aoData[ row ];
8171 return type === 'search' ? r._aFilterData : r._aSortData;
8172 }, 1 );
8173 } );
8174
8175 _api_registerPlural( 'rows().invalidate()', 'row().invalidate()', function ( src ) {
8176 return this.iterator( 'row', function ( settings, row ) {
8177 _fnInvalidate( settings, row, src );
8178 } );
8179 } );
8180
8181 _api_registerPlural( 'rows().indexes()', 'row().index()', function () {
8182 return this.iterator( 'row', function ( settings, row ) {
8183 return row;
8184 }, 1 );
8185 } );
8186
8187 _api_registerPlural( 'rows().ids()', 'row().id()', function ( hash ) {
8188 var a = [];
8189 var context = this.context;
8190
8191 // `iterator` will drop undefined values, but in this case we want them
8192 for ( var i=0, ien=context.length ; i<ien ; i++ ) {
8193 for ( var j=0, jen=this[i].length ; j<jen ; j++ ) {
8194 var id = context[i].rowIdFn( context[i].aoData[ this[i][j] ]._aData );
8195 a.push( (hash === true ? '#' : '' )+ id );
8196 }
8197 }
8198
8199 return new _Api( context, a );
8200 } );
8201
8202 _api_registerPlural( 'rows().remove()', 'row().remove()', function () {
8203 var that = this;
8204
8205 this.iterator( 'row', function ( settings, row, thatIdx ) {
8206 var data = settings.aoData;
8207 var rowData = data[ row ];
8208 var i, ien, j, jen;
8209 var loopRow, loopCells;
8210
8211 data.splice( row, 1 );
8212
8213 // Update the cached indexes
8214 for ( i=0, ien=data.length ; i<ien ; i++ ) {
8215 loopRow = data[i];
8216 loopCells = loopRow.anCells;
8217
8218 // Rows
8219 if ( loopRow.nTr !== null ) {
8220 loopRow.nTr._DT_RowIndex = i;
8221 }
8222
8223 // Cells
8224 if ( loopCells !== null ) {
8225 for ( j=0, jen=loopCells.length ; j<jen ; j++ ) {
8226 loopCells[j]._DT_CellIndex.row = i;
8227 }
8228 }
8229 }
8230
8231 // Delete from the display arrays
8232 _fnDeleteIndex( settings.aiDisplayMaster, row );
8233 _fnDeleteIndex( settings.aiDisplay, row );
8234 _fnDeleteIndex( that[ thatIdx ], row, false ); // maintain local indexes
8235
8236 // For server-side processing tables - subtract the deleted row from the count
8237 if ( settings._iRecordsDisplay > 0 ) {
8238 settings._iRecordsDisplay--;
8239 }
8240
8241 // Check for an 'overflow' they case for displaying the table
8242 _fnLengthOverflow( settings );
8243
8244 // Remove the row's ID reference if there is one
8245 var id = settings.rowIdFn( rowData._aData );
8246 if ( id !== undefined ) {
8247 delete settings.aIds[ id ];
8248 }
8249 } );
8250
8251 this.iterator( 'table', function ( settings ) {
8252 for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
8253 settings.aoData[i].idx = i;
8254 }
8255 } );
8256
8257 return this;
8258 } );
8259
8260
8261 _api_register( 'rows.add()', function ( rows ) {
8262 var newRows = this.iterator( 'table', function ( settings ) {
8263 var row, i, ien;
8264 var out = [];
8265
8266 for ( i=0, ien=rows.length ; i<ien ; i++ ) {
8267 row = rows[i];
8268
8269 if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {
8270 out.push( _fnAddTr( settings, row )[0] );
8271 }
8272 else {
8273 out.push( _fnAddData( settings, row ) );
8274 }
8275 }
8276
8277 return out;
8278 }, 1 );
8279
8280 // Return an Api.rows() extended instance, so rows().nodes() etc can be used
8281 var modRows = this.rows( -1 );
8282 modRows.pop();
8283 $.merge( modRows, newRows );
8284
8285 return modRows;
8286 } );
8287
8288
8289
8290
8291
8292 /**
8293 *
8294 */
8295 _api_register( 'row()', function ( selector, opts ) {
8296 return _selector_first( this.rows( selector, opts ) );
8297 } );
8298
8299
8300 _api_register( 'row().data()', function ( data ) {
8301 var ctx = this.context;
8302
8303 if ( data === undefined ) {
8304 // Get
8305 return ctx.length && this.length ?
8306 ctx[0].aoData[ this[0] ]._aData :
8307 undefined;
8308 }
8309
8310 // Set
8311 var row = ctx[0].aoData[ this[0] ];
8312 row._aData = data;
8313
8314 // If the DOM has an id, and the data source is an array
8315 if ( Array.isArray( data ) && row.nTr && row.nTr.id ) {
8316 _fnSetObjectDataFn( ctx[0].rowId )( data, row.nTr.id );
8317 }
8318
8319 // Automatically invalidate
8320 _fnInvalidate( ctx[0], this[0], 'data' );
8321
8322 return this;
8323 } );
8324
8325
8326 _api_register( 'row().node()', function () {
8327 var ctx = this.context;
8328
8329 return ctx.length && this.length ?
8330 ctx[0].aoData[ this[0] ].nTr || null :
8331 null;
8332 } );
8333
8334
8335 _api_register( 'row.add()', function ( row ) {
8336 // Allow a jQuery object to be passed in - only a single row is added from
8337 // it though - the first element in the set
8338 if ( row instanceof $ && row.length ) {
8339 row = row[0];
8340 }
8341
8342 var rows = this.iterator( 'table', function ( settings ) {
8343 if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {
8344 return _fnAddTr( settings, row )[0];
8345 }
8346 return _fnAddData( settings, row );
8347 } );
8348
8349 // Return an Api.rows() extended instance, with the newly added row selected
8350 return this.row( rows[0] );
8351 } );
8352
8353
8354 $(document).on('plugin-init.dt', function (e, context) {
8355 var api = new _Api( context );
8356
8357 const namespace = 'on-plugin-init';
8358 const stateSaveParamsEvent = `stateSaveParams.${namespace}`;
8359 const destroyEvent = `destroy.${namespace}`;
8360
8361 api.on( stateSaveParamsEvent, function ( e, settings, d ) {
8362 // This could be more compact with the API, but it is a lot faster as a simple
8363 // internal loop
8364 var idFn = settings.rowIdFn;
8365 var data = settings.aoData;
8366 var ids = [];
8367
8368 for (var i=0 ; i<data.length ; i++) {
8369 if (data[i]._detailsShow) {
8370 ids.push( '#' + idFn(data[i]._aData) );
8371 }
8372 }
8373
8374 d.childRows = ids;
8375 });
8376
8377 api.on( destroyEvent, function () {
8378 api.off(`${stateSaveParamsEvent} ${destroyEvent}`);
8379 });
8380
8381 var loaded = api.state.loaded();
8382
8383 if ( loaded && loaded.childRows ) {
8384 api
8385 .rows( $.map(loaded.childRows, function (id){
8386 return id.replace(/:/g, '\\:')
8387 }) )
8388 .every( function () {
8389 _fnCallbackFire( context, null, 'requestChild', [ this ] )
8390 });
8391 }
8392 });
8393
8394 var __details_add = function ( ctx, row, data, klass )
8395 {
8396 // Convert to array of TR elements
8397 var rows = [];
8398 var addRow = function ( r, k ) {
8399 // Recursion to allow for arrays of jQuery objects
8400 if ( Array.isArray( r ) || r instanceof $ ) {
8401 for ( var i=0, ien=r.length ; i<ien ; i++ ) {
8402 addRow( r[i], k );
8403 }
8404 return;
8405 }
8406
8407 // If we get a TR element, then just add it directly - up to the dev
8408 // to add the correct number of columns etc
8409 if ( r.nodeName && r.nodeName.toLowerCase() === 'tr' ) {
8410 rows.push( r );
8411 }
8412 else {
8413 // Otherwise create a row with a wrapper
8414 var created = $('<tr><td></td></tr>').addClass( k );
8415 $('td', created)
8416 .addClass( k )
8417 .html( r )
8418 [0].colSpan = _fnVisbleColumns( ctx );
8419
8420 rows.push( created[0] );
8421 }
8422 };
8423
8424 addRow( data, klass );
8425
8426 if ( row._details ) {
8427 row._details.detach();
8428 }
8429
8430 row._details = $(rows);
8431
8432 // If the children were already shown, that state should be retained
8433 if ( row._detailsShow ) {
8434 row._details.insertAfter( row.nTr );
8435 }
8436 };
8437
8438
8439 // Make state saving of child row details async to allow them to be batch processed
8440 var __details_state = DataTable.util.throttle(
8441 function (ctx) {
8442 _fnSaveState( ctx[0] )
8443 },
8444 500
8445 );
8446
8447
8448 var __details_remove = function ( api, idx )
8449 {
8450 var ctx = api.context;
8451
8452 if ( ctx.length ) {
8453 var row = ctx[0].aoData[ idx !== undefined ? idx : api[0] ];
8454
8455 if ( row && row._details ) {
8456 row._details.remove();
8457
8458 row._detailsShow = undefined;
8459 row._details = undefined;
8460 $( row.nTr ).removeClass( 'dt-hasChild' );
8461 __details_state( ctx );
8462 }
8463 }
8464 };
8465
8466
8467 var __details_display = function ( api, show ) {
8468 var ctx = api.context;
8469
8470 if ( ctx.length && api.length ) {
8471 var row = ctx[0].aoData[ api[0] ];
8472
8473 if ( row._details ) {
8474 row._detailsShow = show;
8475
8476 if ( show ) {
8477 row._details.insertAfter( row.nTr );
8478 $( row.nTr ).addClass( 'dt-hasChild' );
8479 }
8480 else {
8481 row._details.detach();
8482 $( row.nTr ).removeClass( 'dt-hasChild' );
8483 }
8484
8485 _fnCallbackFire( ctx[0], null, 'childRow', [ show, api.row( api[0] ) ] )
8486
8487 __details_events( ctx[0] );
8488 __details_state( ctx );
8489 }
8490 }
8491 };
8492
8493
8494 var __details_events = function ( settings )
8495 {
8496 var api = new _Api( settings );
8497 var namespace = '.dt.DT_details';
8498 var drawEvent = 'draw'+namespace;
8499 var colvisEvent = 'column-sizing'+namespace;
8500 var destroyEvent = 'destroy'+namespace;
8501 var data = settings.aoData;
8502
8503 api.off( drawEvent +' '+ colvisEvent +' '+ destroyEvent );
8504
8505 if ( _pluck( data, '_details' ).length > 0 ) {
8506 // On each draw, insert the required elements into the document
8507 api.on( drawEvent, function ( e, ctx ) {
8508 if ( settings !== ctx ) {
8509 return;
8510 }
8511
8512 api.rows( {page:'current'} ).eq(0).each( function (idx) {
8513 // Internal data grab
8514 var row = data[ idx ];
8515
8516 if ( row._detailsShow ) {
8517 row._details.insertAfter( row.nTr );
8518 }
8519 } );
8520 } );
8521
8522 // Column visibility change - update the colspan
8523 api.on( colvisEvent, function ( e, ctx, idx, vis ) {
8524 if ( settings !== ctx ) {
8525 return;
8526 }
8527
8528 // Update the colspan for the details rows (note, only if it already has
8529 // a colspan)
8530 var row, visible = _fnVisbleColumns( ctx );
8531
8532 for ( var i=0, ien=data.length ; i<ien ; i++ ) {
8533 row = data[i];
8534
8535 if ( row._details ) {
8536 row._details.children('td[colspan]').attr('colspan', visible );
8537 }
8538 }
8539 } );
8540
8541 // Table destroyed - nuke any child rows
8542 api.on( destroyEvent, function ( e, ctx ) {
8543 if ( settings !== ctx ) {
8544 return;
8545 }
8546
8547 for ( var i=0, ien=data.length ; i<ien ; i++ ) {
8548 if ( data[i]._details ) {
8549 __details_remove( api, i );
8550 }
8551 }
8552 } );
8553 }
8554 };
8555
8556 // Strings for the method names to help minification
8557 var _emp = '';
8558 var _child_obj = _emp+'row().child';
8559 var _child_mth = _child_obj+'()';
8560
8561 // data can be:
8562 // tr
8563 // string
8564 // jQuery or array of any of the above
8565 _api_register( _child_mth, function ( data, klass ) {
8566 var ctx = this.context;
8567
8568 if ( data === undefined ) {
8569 // get
8570 return ctx.length && this.length ?
8571 ctx[0].aoData[ this[0] ]._details :
8572 undefined;
8573 }
8574 else if ( data === true ) {
8575 // show
8576 this.child.show();
8577 }
8578 else if ( data === false ) {
8579 // remove
8580 __details_remove( this );
8581 }
8582 else if ( ctx.length && this.length ) {
8583 // set
8584 __details_add( ctx[0], ctx[0].aoData[ this[0] ], data, klass );
8585 }
8586
8587 return this;
8588 } );
8589
8590
8591 _api_register( [
8592 _child_obj+'.show()',
8593 _child_mth+'.show()' // only when `child()` was called with parameters (without
8594 ], function ( show ) { // it returns an object and this method is not executed)
8595 __details_display( this, true );
8596 return this;
8597 } );
8598
8599
8600 _api_register( [
8601 _child_obj+'.hide()',
8602 _child_mth+'.hide()' // only when `child()` was called with parameters (without
8603 ], function () { // it returns an object and this method is not executed)
8604 __details_display( this, false );
8605 return this;
8606 } );
8607
8608
8609 _api_register( [
8610 _child_obj+'.remove()',
8611 _child_mth+'.remove()' // only when `child()` was called with parameters (without
8612 ], function () { // it returns an object and this method is not executed)
8613 __details_remove( this );
8614 return this;
8615 } );
8616
8617
8618 _api_register( _child_obj+'.isShown()', function () {
8619 var ctx = this.context;
8620
8621 if ( ctx.length && this.length ) {
8622 // _detailsShown as false or undefined will fall through to return false
8623 return ctx[0].aoData[ this[0] ]._detailsShow || false;
8624 }
8625 return false;
8626 } );
8627
8628
8629
8630 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8631 * Columns
8632 *
8633 * {integer} - column index (>=0 count from left, <0 count from right)
8634 * "{integer}:visIdx" - visible column index (i.e. translate to column index) (>=0 count from left, <0 count from right)
8635 * "{integer}:visible" - alias for {integer}:visIdx (>=0 count from left, <0 count from right)
8636 * "{string}:name" - column name
8637 * "{string}" - jQuery selector on column header nodes
8638 *
8639 */
8640
8641 // can be an array of these items, comma separated list, or an array of comma
8642 // separated lists
8643
8644 var __re_column_selector = /^([^:]+):(name|visIdx|visible)$/;
8645
8646
8647 // r1 and r2 are redundant - but it means that the parameters match for the
8648 // iterator callback in columns().data()
8649 var __columnData = function ( settings, column, r1, r2, rows ) {
8650 var a = [];
8651 for ( var row=0, ien=rows.length ; row<ien ; row++ ) {
8652 a.push( _fnGetCellData( settings, rows[row], column ) );
8653 }
8654 return a;
8655 };
8656
8657
8658 var __column_selector = function ( settings, selector, opts )
8659 {
8660 var
8661 columns = settings.aoColumns,
8662 names = _pluck( columns, 'sName' ),
8663 nodes = _pluck( columns, 'nTh' );
8664
8665 var run = function ( s ) {
8666 var selInt = _intVal( s );
8667
8668 // Selector - all
8669 if ( s === '' ) {
8670 return _range( columns.length );
8671 }
8672
8673 // Selector - index
8674 if ( selInt !== null ) {
8675 return [ selInt >= 0 ?
8676 selInt : // Count from left
8677 columns.length + selInt // Count from right (+ because its a negative value)
8678 ];
8679 }
8680
8681 // Selector = function
8682 if ( typeof s === 'function' ) {
8683 var rows = _selector_row_indexes( settings, opts );
8684
8685 return $.map( columns, function (col, idx) {
8686 return s(
8687 idx,
8688 __columnData( settings, idx, 0, 0, rows ),
8689 nodes[ idx ]
8690 ) ? idx : null;
8691 } );
8692 }
8693
8694 // jQuery or string selector
8695 var match = typeof s === 'string' ?
8696 s.match( __re_column_selector ) :
8697 '';
8698
8699 if ( match ) {
8700 switch( match[2] ) {
8701 case 'visIdx':
8702 case 'visible':
8703 var idx = parseInt( match[1], 10 );
8704 // Visible index given, convert to column index
8705 if ( idx < 0 ) {
8706 // Counting from the right
8707 var visColumns = $.map( columns, function (col,i) {
8708 return col.bVisible ? i : null;
8709 } );
8710 return [ visColumns[ visColumns.length + idx ] ];
8711 }
8712 // Counting from the left
8713 return [ _fnVisibleToColumnIndex( settings, idx ) ];
8714
8715 case 'name':
8716 // match by name. `names` is column index complete and in order
8717 return $.map( names, function (name, i) {
8718 return name === match[1] ? i : null;
8719 } );
8720
8721 default:
8722 return [];
8723 }
8724 }
8725
8726 // Cell in the table body
8727 if ( s.nodeName && s._DT_CellIndex ) {
8728 return [ s._DT_CellIndex.column ];
8729 }
8730
8731 // jQuery selector on the TH elements for the columns
8732 var jqResult = $( nodes )
8733 .filter( s )
8734 .map( function () {
8735 return $.inArray( this, nodes ); // `nodes` is column index complete and in order
8736 } )
8737 .toArray();
8738
8739 if ( jqResult.length || ! s.nodeName ) {
8740 return jqResult;
8741 }
8742
8743 // Otherwise a node which might have a `dt-column` data attribute, or be
8744 // a child or such an element
8745 var host = $(s).closest('*[data-dt-column]');
8746 return host.length ?
8747 [ host.data('dt-column') ] :
8748 [];
8749 };
8750
8751 return _selector_run( 'column', selector, run, settings, opts );
8752 };
8753
8754
8755 var __setColumnVis = function ( settings, column, vis ) {
8756 var
8757 cols = settings.aoColumns,
8758 col = cols[ column ],
8759 data = settings.aoData,
8760 row, cells, i, ien, tr;
8761
8762 // Get
8763 if ( vis === undefined ) {
8764 return col.bVisible;
8765 }
8766
8767 // Set
8768 // No change
8769 if ( col.bVisible === vis ) {
8770 return;
8771 }
8772
8773 if ( vis ) {
8774 // Insert column
8775 // Need to decide if we should use appendChild or insertBefore
8776 var insertBefore = $.inArray( true, _pluck(cols, 'bVisible'), column+1 );
8777
8778 for ( i=0, ien=data.length ; i<ien ; i++ ) {
8779 tr = data[i].nTr;
8780 cells = data[i].anCells;
8781
8782 if ( tr ) {
8783 // insertBefore can act like appendChild if 2nd arg is null
8784 tr.insertBefore( cells[ column ], cells[ insertBefore ] || null );
8785 }
8786 }
8787 }
8788 else {
8789 // Remove column
8790 $( _pluck( settings.aoData, 'anCells', column ) ).detach();
8791 }
8792
8793 // Common actions
8794 col.bVisible = vis;
8795 };
8796
8797
8798 _api_register( 'columns()', function ( selector, opts ) {
8799 // argument shifting
8800 if ( selector === undefined ) {
8801 selector = '';
8802 }
8803 else if ( $.isPlainObject( selector ) ) {
8804 opts = selector;
8805 selector = '';
8806 }
8807
8808 opts = _selector_opts( opts );
8809
8810 var inst = this.iterator( 'table', function ( settings ) {
8811 return __column_selector( settings, selector, opts );
8812 }, 1 );
8813
8814 // Want argument shifting here and in _row_selector?
8815 inst.selector.cols = selector;
8816 inst.selector.opts = opts;
8817
8818 return inst;
8819 } );
8820
8821 _api_registerPlural( 'columns().header()', 'column().header()', function ( selector, opts ) {
8822 return this.iterator( 'column', function ( settings, column ) {
8823 return settings.aoColumns[column].nTh;
8824 }, 1 );
8825 } );
8826
8827 _api_registerPlural( 'columns().footer()', 'column().footer()', function ( selector, opts ) {
8828 return this.iterator( 'column', function ( settings, column ) {
8829 return settings.aoColumns[column].nTf;
8830 }, 1 );
8831 } );
8832
8833 _api_registerPlural( 'columns().data()', 'column().data()', function () {
8834 return this.iterator( 'column-rows', __columnData, 1 );
8835 } );
8836
8837 _api_registerPlural( 'columns().dataSrc()', 'column().dataSrc()', function () {
8838 return this.iterator( 'column', function ( settings, column ) {
8839 return settings.aoColumns[column].mData;
8840 }, 1 );
8841 } );
8842
8843 _api_registerPlural( 'columns().cache()', 'column().cache()', function ( type ) {
8844 return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
8845 return _pluck_order( settings.aoData, rows,
8846 type === 'search' ? '_aFilterData' : '_aSortData', column
8847 );
8848 }, 1 );
8849 } );
8850
8851 _api_registerPlural( 'columns().nodes()', 'column().nodes()', function () {
8852 return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
8853 return _pluck_order( settings.aoData, rows, 'anCells', column ) ;
8854 }, 1 );
8855 } );
8856
8857 _api_registerPlural( 'columns().visible()', 'column().visible()', function ( vis, calc ) {
8858 var that = this;
8859 var ret = this.iterator( 'column', function ( settings, column ) {
8860 if ( vis === undefined ) {
8861 return settings.aoColumns[ column ].bVisible;
8862 } // else
8863 __setColumnVis( settings, column, vis );
8864 } );
8865
8866 // Group the column visibility changes
8867 if ( vis !== undefined ) {
8868 this.iterator( 'table', function ( settings ) {
8869 // Redraw the header after changes
8870 _fnDrawHead( settings, settings.aoHeader );
8871 _fnDrawHead( settings, settings.aoFooter );
8872
8873 // Update colspan for no records display. Child rows and extensions will use their own
8874 // listeners to do this - only need to update the empty table item here
8875 if ( ! settings.aiDisplay.length ) {
8876 $(settings.nTBody).find('td[colspan]').attr('colspan', _fnVisbleColumns(settings));
8877 }
8878
8879 _fnSaveState( settings );
8880
8881 // Second loop once the first is done for events
8882 that.iterator( 'column', function ( settings, column ) {
8883 _fnCallbackFire( settings, null, 'column-visibility', [settings, column, vis, calc] );
8884 } );
8885
8886 if ( calc === undefined || calc ) {
8887 that.columns.adjust();
8888 }
8889 });
8890 }
8891
8892 return ret;
8893 } );
8894
8895 _api_registerPlural( 'columns().indexes()', 'column().index()', function ( type ) {
8896 return this.iterator( 'column', function ( settings, column ) {
8897 return type === 'visible' ?
8898 _fnColumnIndexToVisible( settings, column ) :
8899 column;
8900 }, 1 );
8901 } );
8902
8903 _api_register( 'columns.adjust()', function () {
8904 return this.iterator( 'table', function ( settings ) {
8905 _fnAdjustColumnSizing( settings );
8906 }, 1 );
8907 } );
8908
8909 _api_register( 'column.index()', function ( type, idx ) {
8910 if ( this.context.length !== 0 ) {
8911 var ctx = this.context[0];
8912
8913 if ( type === 'fromVisible' || type === 'toData' ) {
8914 return _fnVisibleToColumnIndex( ctx, idx );
8915 }
8916 else if ( type === 'fromData' || type === 'toVisible' ) {
8917 return _fnColumnIndexToVisible( ctx, idx );
8918 }
8919 }
8920 } );
8921
8922 _api_register( 'column()', function ( selector, opts ) {
8923 return _selector_first( this.columns( selector, opts ) );
8924 } );
8925
8926 var __cell_selector = function ( settings, selector, opts )
8927 {
8928 var data = settings.aoData;
8929 var rows = _selector_row_indexes( settings, opts );
8930 var cells = _removeEmpty( _pluck_order( data, rows, 'anCells' ) );
8931 var allCells = $(_flatten( [], cells ));
8932 var row;
8933 var columns = settings.aoColumns.length;
8934 var a, i, ien, j, o, host;
8935
8936 var run = function ( s ) {
8937 var fnSelector = typeof s === 'function';
8938
8939 if ( s === null || s === undefined || fnSelector ) {
8940 // All cells and function selectors
8941 a = [];
8942
8943 for ( i=0, ien=rows.length ; i<ien ; i++ ) {
8944 row = rows[i];
8945
8946 for ( j=0 ; j<columns ; j++ ) {
8947 o = {
8948 row: row,
8949 column: j
8950 };
8951
8952 if ( fnSelector ) {
8953 // Selector - function
8954 host = data[ row ];
8955
8956 if ( s( o, _fnGetCellData(settings, row, j), host.anCells ? host.anCells[j] : null ) ) {
8957 a.push( o );
8958 }
8959 }
8960 else {
8961 // Selector - all
8962 a.push( o );
8963 }
8964 }
8965 }
8966
8967 return a;
8968 }
8969
8970 // Selector - index
8971 if ( $.isPlainObject( s ) ) {
8972 // Valid cell index and its in the array of selectable rows
8973 return s.column !== undefined && s.row !== undefined && $.inArray( s.row, rows ) !== -1 ?
8974 [s] :
8975 [];
8976 }
8977
8978 // Selector - jQuery filtered cells
8979 var jqResult = allCells
8980 .filter( s )
8981 .map( function (i, el) {
8982 return { // use a new object, in case someone changes the values
8983 row: el._DT_CellIndex.row,
8984 column: el._DT_CellIndex.column
8985 };
8986 } )
8987 .toArray();
8988
8989 if ( jqResult.length || ! s.nodeName ) {
8990 return jqResult;
8991 }
8992
8993 // Otherwise the selector is a node, and there is one last option - the
8994 // element might be a child of an element which has dt-row and dt-column
8995 // data attributes
8996 host = $(s).closest('*[data-dt-row]');
8997 return host.length ?
8998 [ {
8999 row: host.data('dt-row'),
9000 column: host.data('dt-column')
9001 } ] :
9002 [];
9003 };
9004
9005 return _selector_run( 'cell', selector, run, settings, opts );
9006 };
9007
9008
9009
9010
9011 _api_register( 'cells()', function ( rowSelector, columnSelector, opts ) {
9012 // Argument shifting
9013 if ( $.isPlainObject( rowSelector ) ) {
9014 // Indexes
9015 if ( rowSelector.row === undefined ) {
9016 // Selector options in first parameter
9017 opts = rowSelector;
9018 rowSelector = null;
9019 }
9020 else {
9021 // Cell index objects in first parameter
9022 opts = columnSelector;
9023 columnSelector = null;
9024 }
9025 }
9026 if ( $.isPlainObject( columnSelector ) ) {
9027 opts = columnSelector;
9028 columnSelector = null;
9029 }
9030
9031 // Cell selector
9032 if ( columnSelector === null || columnSelector === undefined ) {
9033 return this.iterator( 'table', function ( settings ) {
9034 return __cell_selector( settings, rowSelector, _selector_opts( opts ) );
9035 } );
9036 }
9037
9038 // The default built in options need to apply to row and columns
9039 var internalOpts = opts ? {
9040 page: opts.page,
9041 order: opts.order,
9042 search: opts.search
9043 } : {};
9044
9045 // Row + column selector
9046 var columns = this.columns( columnSelector, internalOpts );
9047 var rows = this.rows( rowSelector, internalOpts );
9048 var i, ien, j, jen;
9049
9050 var cellsNoOpts = this.iterator( 'table', function ( settings, idx ) {
9051 var a = [];
9052
9053 for ( i=0, ien=rows[idx].length ; i<ien ; i++ ) {
9054 for ( j=0, jen=columns[idx].length ; j<jen ; j++ ) {
9055 a.push( {
9056 row: rows[idx][i],
9057 column: columns[idx][j]
9058 } );
9059 }
9060 }
9061
9062 return a;
9063 }, 1 );
9064
9065 // There is currently only one extension which uses a cell selector extension
9066 // It is a _major_ performance drag to run this if it isn't needed, so this is
9067 // an extension specific check at the moment
9068 var cells = opts && opts.selected ?
9069 this.cells( cellsNoOpts, opts ) :
9070 cellsNoOpts;
9071
9072 $.extend( cells.selector, {
9073 cols: columnSelector,
9074 rows: rowSelector,
9075 opts: opts
9076 } );
9077
9078 return cells;
9079 } );
9080
9081
9082 _api_registerPlural( 'cells().nodes()', 'cell().node()', function () {
9083 return this.iterator( 'cell', function ( settings, row, column ) {
9084 var data = settings.aoData[ row ];
9085
9086 return data && data.anCells ?
9087 data.anCells[ column ] :
9088 undefined;
9089 }, 1 );
9090 } );
9091
9092
9093 _api_register( 'cells().data()', function () {
9094 return this.iterator( 'cell', function ( settings, row, column ) {
9095 return _fnGetCellData( settings, row, column );
9096 }, 1 );
9097 } );
9098
9099
9100 _api_registerPlural( 'cells().cache()', 'cell().cache()', function ( type ) {
9101 type = type === 'search' ? '_aFilterData' : '_aSortData';
9102
9103 return this.iterator( 'cell', function ( settings, row, column ) {
9104 return settings.aoData[ row ][ type ][ column ];
9105 }, 1 );
9106 } );
9107
9108
9109 _api_registerPlural( 'cells().render()', 'cell().render()', function ( type ) {
9110 return this.iterator( 'cell', function ( settings, row, column ) {
9111 return _fnGetCellData( settings, row, column, type );
9112 }, 1 );
9113 } );
9114
9115
9116 _api_registerPlural( 'cells().indexes()', 'cell().index()', function () {
9117 return this.iterator( 'cell', function ( settings, row, column ) {
9118 return {
9119 row: row,
9120 column: column,
9121 columnVisible: _fnColumnIndexToVisible( settings, column )
9122 };
9123 }, 1 );
9124 } );
9125
9126
9127 _api_registerPlural( 'cells().invalidate()', 'cell().invalidate()', function ( src ) {
9128 return this.iterator( 'cell', function ( settings, row, column ) {
9129 _fnInvalidate( settings, row, src, column );
9130 } );
9131 } );
9132
9133
9134
9135 _api_register( 'cell()', function ( rowSelector, columnSelector, opts ) {
9136 return _selector_first( this.cells( rowSelector, columnSelector, opts ) );
9137 } );
9138
9139
9140 _api_register( 'cell().data()', function ( data ) {
9141 var ctx = this.context;
9142 var cell = this[0];
9143
9144 if ( data === undefined ) {
9145 // Get
9146 return ctx.length && cell.length ?
9147 _fnGetCellData( ctx[0], cell[0].row, cell[0].column ) :
9148 undefined;
9149 }
9150
9151 // Set
9152 _fnSetCellData( ctx[0], cell[0].row, cell[0].column, data );
9153 _fnInvalidate( ctx[0], cell[0].row, 'data', cell[0].column );
9154
9155 return this;
9156 } );
9157
9158
9159
9160 /**
9161 * Get current ordering (sorting) that has been applied to the table.
9162 *
9163 * @returns {array} 2D array containing the sorting information for the first
9164 * table in the current context. Each element in the parent array represents
9165 * a column being sorted upon (i.e. multi-sorting with two columns would have
9166 * 2 inner arrays). The inner arrays may have 2 or 3 elements. The first is
9167 * the column index that the sorting condition applies to, the second is the
9168 * direction of the sort (`desc` or `asc`) and, optionally, the third is the
9169 * index of the sorting order from the `column.sorting` initialisation array.
9170 *//**
9171 * Set the ordering for the table.
9172 *
9173 * @param {integer} order Column index to sort upon.
9174 * @param {string} direction Direction of the sort to be applied (`asc` or `desc`)
9175 * @returns {DataTables.Api} this
9176 *//**
9177 * Set the ordering for the table.
9178 *
9179 * @param {array} order 1D array of sorting information to be applied.
9180 * @param {array} [...] Optional additional sorting conditions
9181 * @returns {DataTables.Api} this
9182 *//**
9183 * Set the ordering for the table.
9184 *
9185 * @param {array} order 2D array of sorting information to be applied.
9186 * @returns {DataTables.Api} this
9187 */
9188 _api_register( 'order()', function ( order, dir ) {
9189 var ctx = this.context;
9190
9191 if ( order === undefined ) {
9192 // get
9193 return ctx.length !== 0 ?
9194 ctx[0].aaSorting :
9195 undefined;
9196 }
9197
9198 // set
9199 if ( typeof order === 'number' ) {
9200 // Simple column / direction passed in
9201 order = [ [ order, dir ] ];
9202 }
9203 else if ( order.length && ! Array.isArray( order[0] ) ) {
9204 // Arguments passed in (list of 1D arrays)
9205 order = Array.prototype.slice.call( arguments );
9206 }
9207 // otherwise a 2D array was passed in
9208
9209 return this.iterator( 'table', function ( settings ) {
9210 settings.aaSorting = order.slice();
9211 } );
9212 } );
9213
9214
9215 /**
9216 * Attach a sort listener to an element for a given column
9217 *
9218 * @param {node|jQuery|string} node Identifier for the element(s) to attach the
9219 * listener to. This can take the form of a single DOM node, a jQuery
9220 * collection of nodes or a jQuery selector which will identify the node(s).
9221 * @param {integer} column the column that a click on this node will sort on
9222 * @param {function} [callback] callback function when sort is run
9223 * @returns {DataTables.Api} this
9224 */
9225 _api_register( 'order.listener()', function ( node, column, callback ) {
9226 return this.iterator( 'table', function ( settings ) {
9227 _fnSortAttachListener( settings, node, column, callback );
9228 } );
9229 } );
9230
9231
9232 _api_register( 'order.fixed()', function ( set ) {
9233 if ( ! set ) {
9234 var ctx = this.context;
9235 var fixed = ctx.length ?
9236 ctx[0].aaSortingFixed :
9237 undefined;
9238
9239 return Array.isArray( fixed ) ?
9240 { pre: fixed } :
9241 fixed;
9242 }
9243
9244 return this.iterator( 'table', function ( settings ) {
9245 settings.aaSortingFixed = $.extend( true, {}, set );
9246 } );
9247 } );
9248
9249
9250 // Order by the selected column(s)
9251 _api_register( [
9252 'columns().order()',
9253 'column().order()'
9254 ], function ( dir ) {
9255 var that = this;
9256
9257 return this.iterator( 'table', function ( settings, i ) {
9258 var sort = [];
9259
9260 $.each( that[i], function (j, col) {
9261 sort.push( [ col, dir ] );
9262 } );
9263
9264 settings.aaSorting = sort;
9265 } );
9266 } );
9267
9268
9269
9270 _api_register( 'search()', function ( input, regex, smart, caseInsen ) {
9271 var ctx = this.context;
9272
9273 if ( input === undefined ) {
9274 // get
9275 return ctx.length !== 0 ?
9276 ctx[0].oPreviousSearch.sSearch :
9277 undefined;
9278 }
9279
9280 // set
9281 return this.iterator( 'table', function ( settings ) {
9282 if ( ! settings.oFeatures.bFilter ) {
9283 return;
9284 }
9285
9286 _fnFilterComplete( settings, $.extend( {}, settings.oPreviousSearch, {
9287 "sSearch": input+"",
9288 "bRegex": regex === null ? false : regex,
9289 "bSmart": smart === null ? true : smart,
9290 "bCaseInsensitive": caseInsen === null ? true : caseInsen
9291 } ), 1 );
9292 } );
9293 } );
9294
9295
9296 _api_registerPlural(
9297 'columns().search()',
9298 'column().search()',
9299 function ( input, regex, smart, caseInsen ) {
9300 return this.iterator( 'column', function ( settings, column ) {
9301 var preSearch = settings.aoPreSearchCols;
9302
9303 if ( input === undefined ) {
9304 // get
9305 return preSearch[ column ].sSearch;
9306 }
9307
9308 // set
9309 if ( ! settings.oFeatures.bFilter ) {
9310 return;
9311 }
9312
9313 $.extend( preSearch[ column ], {
9314 "sSearch": input+"",
9315 "bRegex": regex === null ? false : regex,
9316 "bSmart": smart === null ? true : smart,
9317 "bCaseInsensitive": caseInsen === null ? true : caseInsen
9318 } );
9319
9320 _fnFilterComplete( settings, settings.oPreviousSearch, 1 );
9321 } );
9322 }
9323 );
9324
9325 /*
9326 * State API methods
9327 */
9328
9329 _api_register( 'state()', function () {
9330 return this.context.length ?
9331 this.context[0].oSavedState :
9332 null;
9333 } );
9334
9335
9336 _api_register( 'state.clear()', function () {
9337 return this.iterator( 'table', function ( settings ) {
9338 // Save an empty object
9339 settings.fnStateSaveCallback.call( settings.oInstance, settings, {} );
9340 } );
9341 } );
9342
9343
9344 _api_register( 'state.loaded()', function () {
9345 return this.context.length ?
9346 this.context[0].oLoadedState :
9347 null;
9348 } );
9349
9350
9351 _api_register( 'state.save()', function () {
9352 return this.iterator( 'table', function ( settings ) {
9353 _fnSaveState( settings );
9354 } );
9355 } );
9356
9357
9358
9359 /**
9360 * Provide a common method for plug-ins to check the version of DataTables being
9361 * used, in order to ensure compatibility.
9362 *
9363 * @param {string} version Version string to check for, in the format "X.Y.Z".
9364 * Note that the formats "X" and "X.Y" are also acceptable.
9365 * @returns {boolean} true if this version of DataTables is greater or equal to
9366 * the required version, or false if this version of DataTales is not
9367 * suitable
9368 * @static
9369 * @dtopt API-Static
9370 *
9371 * @example
9372 * alert( $.fn.dataTable.versionCheck( '1.9.0' ) );
9373 */
9374 DataTable.versionCheck = DataTable.fnVersionCheck = function( version )
9375 {
9376 var aThis = DataTable.version.split('.');
9377 var aThat = version.split('.');
9378 var iThis, iThat;
9379
9380 for ( var i=0, iLen=aThat.length ; i<iLen ; i++ ) {
9381 iThis = parseInt( aThis[i], 10 ) || 0;
9382 iThat = parseInt( aThat[i], 10 ) || 0;
9383
9384 // Parts are the same, keep comparing
9385 if (iThis === iThat) {
9386 continue;
9387 }
9388
9389 // Parts are different, return immediately
9390 return iThis > iThat;
9391 }
9392
9393 return true;
9394 };
9395
9396
9397 /**
9398 * Check if a `<table>` node is a DataTable table already or not.
9399 *
9400 * @param {node|jquery|string} table Table node, jQuery object or jQuery
9401 * selector for the table to test. Note that if more than more than one
9402 * table is passed on, only the first will be checked
9403 * @returns {boolean} true the table given is a DataTable, or false otherwise
9404 * @static
9405 * @dtopt API-Static
9406 *
9407 * @example
9408 * if ( ! $.fn.DataTable.isDataTable( '#example' ) ) {
9409 * $('#example').dataTable();
9410 * }
9411 */
9412 DataTable.isDataTable = DataTable.fnIsDataTable = function ( table )
9413 {
9414 var t = $(table).get(0);
9415 var is = false;
9416
9417 if ( table instanceof DataTable.Api ) {
9418 return true;
9419 }
9420
9421 $.each( DataTable.settings, function (i, o) {
9422 var head = o.nScrollHead ? $('table', o.nScrollHead)[0] : null;
9423 var foot = o.nScrollFoot ? $('table', o.nScrollFoot)[0] : null;
9424
9425 if ( o.nTable === t || head === t || foot === t ) {
9426 is = true;
9427 }
9428 } );
9429
9430 return is;
9431 };
9432
9433
9434 /**
9435 * Get all DataTable tables that have been initialised - optionally you can
9436 * select to get only currently visible tables.
9437 *
9438 * @param {boolean} [visible=false] Flag to indicate if you want all (default)
9439 * or visible tables only.
9440 * @returns {array} Array of `table` nodes (not DataTable instances) which are
9441 * DataTables
9442 * @static
9443 * @dtopt API-Static
9444 *
9445 * @example
9446 * $.each( $.fn.dataTable.tables(true), function () {
9447 * $(table).DataTable().columns.adjust();
9448 * } );
9449 */
9450 DataTable.tables = DataTable.fnTables = function ( visible )
9451 {
9452 var api = false;
9453
9454 if ( $.isPlainObject( visible ) ) {
9455 api = visible.api;
9456 visible = visible.visible;
9457 }
9458
9459 var a = $.map( DataTable.settings, function (o) {
9460 if ( !visible || (visible && $(o.nTable).is(':visible')) ) {
9461 return o.nTable;
9462 }
9463 } );
9464
9465 return api ?
9466 new _Api( a ) :
9467 a;
9468 };
9469
9470
9471 /**
9472 * Convert from camel case parameters to Hungarian notation. This is made public
9473 * for the extensions to provide the same ability as DataTables core to accept
9474 * either the 1.9 style Hungarian notation, or the 1.10+ style camelCase
9475 * parameters.
9476 *
9477 * @param {object} src The model object which holds all parameters that can be
9478 * mapped.
9479 * @param {object} user The object to convert from camel case to Hungarian.
9480 * @param {boolean} force When set to `true`, properties which already have a
9481 * Hungarian value in the `user` object will be overwritten. Otherwise they
9482 * won't be.
9483 */
9484 DataTable.camelToHungarian = _fnCamelToHungarian;
9485
9486
9487
9488 /**
9489 *
9490 */
9491 _api_register( '$()', function ( selector, opts ) {
9492 var
9493 rows = this.rows( opts ).nodes(), // Get all rows
9494 jqRows = $(rows);
9495
9496 return $( [].concat(
9497 jqRows.filter( selector ).toArray(),
9498 jqRows.find( selector ).toArray()
9499 ) );
9500 } );
9501
9502
9503 // jQuery functions to operate on the tables
9504 $.each( [ 'on', 'one', 'off' ], function (i, key) {
9505 _api_register( key+'()', function ( /* event, handler */ ) {
9506 var args = Array.prototype.slice.call(arguments);
9507
9508 // Add the `dt` namespace automatically if it isn't already present
9509 args[0] = $.map( args[0].split( /\s/ ), function ( e ) {
9510 return ! e.match(/\.dt\b/) ?
9511 e+'.dt' :
9512 e;
9513 } ).join( ' ' );
9514
9515 var inst = $( this.tables().nodes() );
9516 inst[key].apply( inst, args );
9517 return this;
9518 } );
9519 } );
9520
9521
9522 _api_register( 'clear()', function () {
9523 return this.iterator( 'table', function ( settings ) {
9524 _fnClearTable( settings );
9525 } );
9526 } );
9527
9528
9529 _api_register( 'settings()', function () {
9530 return new _Api( this.context, this.context );
9531 } );
9532
9533
9534 _api_register( 'init()', function () {
9535 var ctx = this.context;
9536 return ctx.length ? ctx[0].oInit : null;
9537 } );
9538
9539
9540 _api_register( 'data()', function () {
9541 return this.iterator( 'table', function ( settings ) {
9542 return _pluck( settings.aoData, '_aData' );
9543 } ).flatten();
9544 } );
9545
9546
9547 _api_register( 'destroy()', function ( remove ) {
9548 remove = remove || false;
9549
9550 return this.iterator( 'table', function ( settings ) {
9551 var classes = settings.oClasses;
9552 var table = settings.nTable;
9553 var tbody = settings.nTBody;
9554 var thead = settings.nTHead;
9555 var tfoot = settings.nTFoot;
9556 var jqTable = $(table);
9557 var jqTbody = $(tbody);
9558 var jqWrapper = $(settings.nTableWrapper);
9559 var rows = $.map( settings.aoData, function (r) { return r.nTr; } );
9560 var i, ien;
9561
9562 // Flag to note that the table is currently being destroyed - no action
9563 // should be taken
9564 settings.bDestroying = true;
9565
9566 // Fire off the destroy callbacks for plug-ins etc
9567 _fnCallbackFire( settings, "aoDestroyCallback", "destroy", [settings] );
9568
9569 // If not being removed from the document, make all columns visible
9570 if ( ! remove ) {
9571 new _Api( settings ).columns().visible( true );
9572 }
9573
9574 // Blitz all `DT` namespaced events (these are internal events, the
9575 // lowercase, `dt` events are user subscribed and they are responsible
9576 // for removing them
9577 jqWrapper.off('.DT').find(':not(tbody *)').off('.DT');
9578 $(window).off('.DT-'+settings.sInstance);
9579
9580 // When scrolling we had to break the table up - restore it
9581 if ( table != thead.parentNode ) {
9582 jqTable.children('thead').detach();
9583 jqTable.append( thead );
9584 }
9585
9586 if ( tfoot && table != tfoot.parentNode ) {
9587 jqTable.children('tfoot').detach();
9588 jqTable.append( tfoot );
9589 }
9590
9591 settings.aaSorting = [];
9592 settings.aaSortingFixed = [];
9593 _fnSortingClasses( settings );
9594
9595 $( rows ).removeClass( settings.asStripeClasses.join(' ') );
9596
9597 $('th, td', thead).removeClass( classes.sSortable+' '+
9598 classes.sSortableAsc+' '+classes.sSortableDesc+' '+classes.sSortableNone
9599 );
9600
9601 // Add the TR elements back into the table in their original order
9602 jqTbody.children().detach();
9603 jqTbody.append( rows );
9604
9605 var orig = settings.nTableWrapper.parentNode;
9606
9607 // Remove the DataTables generated nodes, events and classes
9608 var removedMethod = remove ? 'remove' : 'detach';
9609 jqTable[ removedMethod ]();
9610 jqWrapper[ removedMethod ]();
9611
9612 // If we need to reattach the table to the document
9613 if ( ! remove && orig ) {
9614 // insertBefore acts like appendChild if !arg[1]
9615 orig.insertBefore( table, settings.nTableReinsertBefore );
9616
9617 // Restore the width of the original table - was read from the style property,
9618 // so we can restore directly to that
9619 jqTable
9620 .css( 'width', settings.sDestroyWidth )
9621 .removeClass( classes.sTable );
9622
9623 // If the were originally stripe classes - then we add them back here.
9624 // Note this is not fool proof (for example if not all rows had stripe
9625 // classes - but it's a good effort without getting carried away
9626 ien = settings.asDestroyStripes.length;
9627
9628 if ( ien ) {
9629 jqTbody.children().each( function (i) {
9630 $(this).addClass( settings.asDestroyStripes[i % ien] );
9631 } );
9632 }
9633 }
9634
9635 /* Remove the settings object from the settings array */
9636 var idx = $.inArray( settings, DataTable.settings );
9637 if ( idx !== -1 ) {
9638 DataTable.settings.splice( idx, 1 );
9639 }
9640 } );
9641 } );
9642
9643
9644 // Add the `every()` method for rows, columns and cells in a compact form
9645 $.each( [ 'column', 'row', 'cell' ], function ( i, type ) {
9646 _api_register( type+'s().every()', function ( fn ) {
9647 var opts = this.selector.opts;
9648 var api = this;
9649
9650 return this.iterator( type, function ( settings, arg1, arg2, arg3, arg4 ) {
9651 // Rows and columns:
9652 // arg1 - index
9653 // arg2 - table counter
9654 // arg3 - loop counter
9655 // arg4 - undefined
9656 // Cells:
9657 // arg1 - row index
9658 // arg2 - column index
9659 // arg3 - table counter
9660 // arg4 - loop counter
9661 fn.call(
9662 api[ type ](
9663 arg1,
9664 type==='cell' ? arg2 : opts,
9665 type==='cell' ? opts : undefined
9666 ),
9667 arg1, arg2, arg3, arg4
9668 );
9669 } );
9670 } );
9671 } );
9672
9673
9674 // i18n method for extensions to be able to use the language object from the
9675 // DataTable
9676 _api_register( 'i18n()', function ( token, def, plural ) {
9677 var ctx = this.context[0];
9678 var resolved = _fnGetObjectDataFn( token )( ctx.oLanguage );
9679
9680 if ( resolved === undefined ) {
9681 resolved = def;
9682 }
9683
9684 if ( plural !== undefined && $.isPlainObject( resolved ) ) {
9685 resolved = resolved[ plural ] !== undefined ?
9686 resolved[ plural ] :
9687 resolved._;
9688 }
9689
9690 return resolved.replace( '%d', plural ); // nb: plural might be undefined,
9691 } );
9692 /**
9693 * Version string for plug-ins to check compatibility. Allowed format is
9694 * `a.b.c-d` where: a:int, b:int, c:int, d:string(dev|beta|alpha). `d` is used
9695 * only for non-release builds. See http://semver.org/ for more information.
9696 * @member
9697 * @type string
9698 * @default Version number
9699 */
9700 DataTable.version = "1.13.1";
9701
9702 /**
9703 * Private data store, containing all of the settings objects that are
9704 * created for the tables on a given page.
9705 *
9706 * Note that the `DataTable.settings` object is aliased to
9707 * `jQuery.fn.dataTableExt` through which it may be accessed and
9708 * manipulated, or `jQuery.fn.dataTable.settings`.
9709 * @member
9710 * @type array
9711 * @default []
9712 * @private
9713 */
9714 DataTable.settings = [];
9715
9716 /**
9717 * Object models container, for the various models that DataTables has
9718 * available to it. These models define the objects that are used to hold
9719 * the active state and configuration of the table.
9720 * @namespace
9721 */
9722 DataTable.models = {};
9723
9724
9725
9726 /**
9727 * Template object for the way in which DataTables holds information about
9728 * search information for the global filter and individual column filters.
9729 * @namespace
9730 */
9731 DataTable.models.oSearch = {
9732 /**
9733 * Flag to indicate if the filtering should be case insensitive or not
9734 * @type boolean
9735 * @default true
9736 */
9737 "bCaseInsensitive": true,
9738
9739 /**
9740 * Applied search term
9741 * @type string
9742 * @default <i>Empty string</i>
9743 */
9744 "sSearch": "",
9745
9746 /**
9747 * Flag to indicate if the search term should be interpreted as a
9748 * regular expression (true) or not (false) and therefore and special
9749 * regex characters escaped.
9750 * @type boolean
9751 * @default false
9752 */
9753 "bRegex": false,
9754
9755 /**
9756 * Flag to indicate if DataTables is to use its smart filtering or not.
9757 * @type boolean
9758 * @default true
9759 */
9760 "bSmart": true,
9761
9762 /**
9763 * Flag to indicate if DataTables should only trigger a search when
9764 * the return key is pressed.
9765 * @type boolean
9766 * @default false
9767 */
9768 "return": false
9769 };
9770
9771
9772
9773
9774 /**
9775 * Template object for the way in which DataTables holds information about
9776 * each individual row. This is the object format used for the settings
9777 * aoData array.
9778 * @namespace
9779 */
9780 DataTable.models.oRow = {
9781 /**
9782 * TR element for the row
9783 * @type node
9784 * @default null
9785 */
9786 "nTr": null,
9787
9788 /**
9789 * Array of TD elements for each row. This is null until the row has been
9790 * created.
9791 * @type array nodes
9792 * @default []
9793 */
9794 "anCells": null,
9795
9796 /**
9797 * Data object from the original data source for the row. This is either
9798 * an array if using the traditional form of DataTables, or an object if
9799 * using mData options. The exact type will depend on the passed in
9800 * data from the data source, or will be an array if using DOM a data
9801 * source.
9802 * @type array|object
9803 * @default []
9804 */
9805 "_aData": [],
9806
9807 /**
9808 * Sorting data cache - this array is ostensibly the same length as the
9809 * number of columns (although each index is generated only as it is
9810 * needed), and holds the data that is used for sorting each column in the
9811 * row. We do this cache generation at the start of the sort in order that
9812 * the formatting of the sort data need be done only once for each cell
9813 * per sort. This array should not be read from or written to by anything
9814 * other than the master sorting methods.
9815 * @type array
9816 * @default null
9817 * @private
9818 */
9819 "_aSortData": null,
9820
9821 /**
9822 * Per cell filtering data cache. As per the sort data cache, used to
9823 * increase the performance of the filtering in DataTables
9824 * @type array
9825 * @default null
9826 * @private
9827 */
9828 "_aFilterData": null,
9829
9830 /**
9831 * Filtering data cache. This is the same as the cell filtering cache, but
9832 * in this case a string rather than an array. This is easily computed with
9833 * a join on `_aFilterData`, but is provided as a cache so the join isn't
9834 * needed on every search (memory traded for performance)
9835 * @type array
9836 * @default null
9837 * @private
9838 */
9839 "_sFilterRow": null,
9840
9841 /**
9842 * Cache of the class name that DataTables has applied to the row, so we
9843 * can quickly look at this variable rather than needing to do a DOM check
9844 * on className for the nTr property.
9845 * @type string
9846 * @default <i>Empty string</i>
9847 * @private
9848 */
9849 "_sRowStripe": "",
9850
9851 /**
9852 * Denote if the original data source was from the DOM, or the data source
9853 * object. This is used for invalidating data, so DataTables can
9854 * automatically read data from the original source, unless uninstructed
9855 * otherwise.
9856 * @type string
9857 * @default null
9858 * @private
9859 */
9860 "src": null,
9861
9862 /**
9863 * Index in the aoData array. This saves an indexOf lookup when we have the
9864 * object, but want to know the index
9865 * @type integer
9866 * @default -1
9867 * @private
9868 */
9869 "idx": -1
9870 };
9871
9872
9873 /**
9874 * Template object for the column information object in DataTables. This object
9875 * is held in the settings aoColumns array and contains all the information that
9876 * DataTables needs about each individual column.
9877 *
9878 * Note that this object is related to {@link DataTable.defaults.column}
9879 * but this one is the internal data store for DataTables's cache of columns.
9880 * It should NOT be manipulated outside of DataTables. Any configuration should
9881 * be done through the initialisation options.
9882 * @namespace
9883 */
9884 DataTable.models.oColumn = {
9885 /**
9886 * Column index. This could be worked out on-the-fly with $.inArray, but it
9887 * is faster to just hold it as a variable
9888 * @type integer
9889 * @default null
9890 */
9891 "idx": null,
9892
9893 /**
9894 * A list of the columns that sorting should occur on when this column
9895 * is sorted. That this property is an array allows multi-column sorting
9896 * to be defined for a column (for example first name / last name columns
9897 * would benefit from this). The values are integers pointing to the
9898 * columns to be sorted on (typically it will be a single integer pointing
9899 * at itself, but that doesn't need to be the case).
9900 * @type array
9901 */
9902 "aDataSort": null,
9903
9904 /**
9905 * Define the sorting directions that are applied to the column, in sequence
9906 * as the column is repeatedly sorted upon - i.e. the first value is used
9907 * as the sorting direction when the column if first sorted (clicked on).
9908 * Sort it again (click again) and it will move on to the next index.
9909 * Repeat until loop.
9910 * @type array
9911 */
9912 "asSorting": null,
9913
9914 /**
9915 * Flag to indicate if the column is searchable, and thus should be included
9916 * in the filtering or not.
9917 * @type boolean
9918 */
9919 "bSearchable": null,
9920
9921 /**
9922 * Flag to indicate if the column is sortable or not.
9923 * @type boolean
9924 */
9925 "bSortable": null,
9926
9927 /**
9928 * Flag to indicate if the column is currently visible in the table or not
9929 * @type boolean
9930 */
9931 "bVisible": null,
9932
9933 /**
9934 * Store for manual type assignment using the `column.type` option. This
9935 * is held in store so we can manipulate the column's `sType` property.
9936 * @type string
9937 * @default null
9938 * @private
9939 */
9940 "_sManualType": null,
9941
9942 /**
9943 * Flag to indicate if HTML5 data attributes should be used as the data
9944 * source for filtering or sorting. True is either are.
9945 * @type boolean
9946 * @default false
9947 * @private
9948 */
9949 "_bAttrSrc": false,
9950
9951 /**
9952 * Developer definable function that is called whenever a cell is created (Ajax source,
9953 * etc) or processed for input (DOM source). This can be used as a compliment to mRender
9954 * allowing you to modify the DOM element (add background colour for example) when the
9955 * element is available.
9956 * @type function
9957 * @param {element} nTd The TD node that has been created
9958 * @param {*} sData The Data for the cell
9959 * @param {array|object} oData The data for the whole row
9960 * @param {int} iRow The row index for the aoData data store
9961 * @default null
9962 */
9963 "fnCreatedCell": null,
9964
9965 /**
9966 * Function to get data from a cell in a column. You should <b>never</b>
9967 * access data directly through _aData internally in DataTables - always use
9968 * the method attached to this property. It allows mData to function as
9969 * required. This function is automatically assigned by the column
9970 * initialisation method
9971 * @type function
9972 * @param {array|object} oData The data array/object for the array
9973 * (i.e. aoData[]._aData)
9974 * @param {string} sSpecific The specific data type you want to get -
9975 * 'display', 'type' 'filter' 'sort'
9976 * @returns {*} The data for the cell from the given row's data
9977 * @default null
9978 */
9979 "fnGetData": null,
9980
9981 /**
9982 * Function to set data for a cell in the column. You should <b>never</b>
9983 * set the data directly to _aData internally in DataTables - always use
9984 * this method. It allows mData to function as required. This function
9985 * is automatically assigned by the column initialisation method
9986 * @type function
9987 * @param {array|object} oData The data array/object for the array
9988 * (i.e. aoData[]._aData)
9989 * @param {*} sValue Value to set
9990 * @default null
9991 */
9992 "fnSetData": null,
9993
9994 /**
9995 * Property to read the value for the cells in the column from the data
9996 * source array / object. If null, then the default content is used, if a
9997 * function is given then the return from the function is used.
9998 * @type function|int|string|null
9999 * @default null
10000 */
10001 "mData": null,
10002
10003 /**
10004 * Partner property to mData which is used (only when defined) to get
10005 * the data - i.e. it is basically the same as mData, but without the
10006 * 'set' option, and also the data fed to it is the result from mData.
10007 * This is the rendering method to match the data method of mData.
10008 * @type function|int|string|null
10009 * @default null
10010 */
10011 "mRender": null,
10012
10013 /**
10014 * Unique header TH/TD element for this column - this is what the sorting
10015 * listener is attached to (if sorting is enabled.)
10016 * @type node
10017 * @default null
10018 */
10019 "nTh": null,
10020
10021 /**
10022 * Unique footer TH/TD element for this column (if there is one). Not used
10023 * in DataTables as such, but can be used for plug-ins to reference the
10024 * footer for each column.
10025 * @type node
10026 * @default null
10027 */
10028 "nTf": null,
10029
10030 /**
10031 * The class to apply to all TD elements in the table's TBODY for the column
10032 * @type string
10033 * @default null
10034 */
10035 "sClass": null,
10036
10037 /**
10038 * When DataTables calculates the column widths to assign to each column,
10039 * it finds the longest string in each column and then constructs a
10040 * temporary table and reads the widths from that. The problem with this
10041 * is that "mmm" is much wider then "iiii", but the latter is a longer
10042 * string - thus the calculation can go wrong (doing it properly and putting
10043 * it into an DOM object and measuring that is horribly(!) slow). Thus as
10044 * a "work around" we provide this option. It will append its value to the
10045 * text that is found to be the longest string for the column - i.e. padding.
10046 * @type string
10047 */
10048 "sContentPadding": null,
10049
10050 /**
10051 * Allows a default value to be given for a column's data, and will be used
10052 * whenever a null data source is encountered (this can be because mData
10053 * is set to null, or because the data source itself is null).
10054 * @type string
10055 * @default null
10056 */
10057 "sDefaultContent": null,
10058
10059 /**
10060 * Name for the column, allowing reference to the column by name as well as
10061 * by index (needs a lookup to work by name).
10062 * @type string
10063 */
10064 "sName": null,
10065
10066 /**
10067 * Custom sorting data type - defines which of the available plug-ins in
10068 * afnSortData the custom sorting will use - if any is defined.
10069 * @type string
10070 * @default std
10071 */
10072 "sSortDataType": 'std',
10073
10074 /**
10075 * Class to be applied to the header element when sorting on this column
10076 * @type string
10077 * @default null
10078 */
10079 "sSortingClass": null,
10080
10081 /**
10082 * Class to be applied to the header element when sorting on this column -
10083 * when jQuery UI theming is used.
10084 * @type string
10085 * @default null
10086 */
10087 "sSortingClassJUI": null,
10088
10089 /**
10090 * Title of the column - what is seen in the TH element (nTh).
10091 * @type string
10092 */
10093 "sTitle": null,
10094
10095 /**
10096 * Column sorting and filtering type
10097 * @type string
10098 * @default null
10099 */
10100 "sType": null,
10101
10102 /**
10103 * Width of the column
10104 * @type string
10105 * @default null
10106 */
10107 "sWidth": null,
10108
10109 /**
10110 * Width of the column when it was first "encountered"
10111 * @type string
10112 * @default null
10113 */
10114 "sWidthOrig": null
10115 };
10116
10117
10118 /*
10119 * Developer note: The properties of the object below are given in Hungarian
10120 * notation, that was used as the interface for DataTables prior to v1.10, however
10121 * from v1.10 onwards the primary interface is camel case. In order to avoid
10122 * breaking backwards compatibility utterly with this change, the Hungarian
10123 * version is still, internally the primary interface, but is is not documented
10124 * - hence the @name tags in each doc comment. This allows a Javascript function
10125 * to create a map from Hungarian notation to camel case (going the other direction
10126 * would require each property to be listed, which would add around 3K to the size
10127 * of DataTables, while this method is about a 0.5K hit).
10128 *
10129 * Ultimately this does pave the way for Hungarian notation to be dropped
10130 * completely, but that is a massive amount of work and will break current
10131 * installs (therefore is on-hold until v2).
10132 */
10133
10134 /**
10135 * Initialisation options that can be given to DataTables at initialisation
10136 * time.
10137 * @namespace
10138 */
10139 DataTable.defaults = {
10140 /**
10141 * An array of data to use for the table, passed in at initialisation which
10142 * will be used in preference to any data which is already in the DOM. This is
10143 * particularly useful for constructing tables purely in Javascript, for
10144 * example with a custom Ajax call.
10145 * @type array
10146 * @default null
10147 *
10148 * @dtopt Option
10149 * @name DataTable.defaults.data
10150 *
10151 * @example
10152 * // Using a 2D array data source
10153 * $(document).ready( function () {
10154 * $('#example').dataTable( {
10155 * "data": [
10156 * ['Trident', 'Internet Explorer 4.0', 'Win 95+', 4, 'X'],
10157 * ['Trident', 'Internet Explorer 5.0', 'Win 95+', 5, 'C'],
10158 * ],
10159 * "columns": [
10160 * { "title": "Engine" },
10161 * { "title": "Browser" },
10162 * { "title": "Platform" },
10163 * { "title": "Version" },
10164 * { "title": "Grade" }
10165 * ]
10166 * } );
10167 * } );
10168 *
10169 * @example
10170 * // Using an array of objects as a data source (`data`)
10171 * $(document).ready( function () {
10172 * $('#example').dataTable( {
10173 * "data": [
10174 * {
10175 * "engine": "Trident",
10176 * "browser": "Internet Explorer 4.0",
10177 * "platform": "Win 95+",
10178 * "version": 4,
10179 * "grade": "X"
10180 * },
10181 * {
10182 * "engine": "Trident",
10183 * "browser": "Internet Explorer 5.0",
10184 * "platform": "Win 95+",
10185 * "version": 5,
10186 * "grade": "C"
10187 * }
10188 * ],
10189 * "columns": [
10190 * { "title": "Engine", "data": "engine" },
10191 * { "title": "Browser", "data": "browser" },
10192 * { "title": "Platform", "data": "platform" },
10193 * { "title": "Version", "data": "version" },
10194 * { "title": "Grade", "data": "grade" }
10195 * ]
10196 * } );
10197 * } );
10198 */
10199 "aaData": null,
10200
10201
10202 /**
10203 * If ordering is enabled, then DataTables will perform a first pass sort on
10204 * initialisation. You can define which column(s) the sort is performed
10205 * upon, and the sorting direction, with this variable. The `sorting` array
10206 * should contain an array for each column to be sorted initially containing
10207 * the column's index and a direction string ('asc' or 'desc').
10208 * @type array
10209 * @default [[0,'asc']]
10210 *
10211 * @dtopt Option
10212 * @name DataTable.defaults.order
10213 *
10214 * @example
10215 * // Sort by 3rd column first, and then 4th column
10216 * $(document).ready( function() {
10217 * $('#example').dataTable( {
10218 * "order": [[2,'asc'], [3,'desc']]
10219 * } );
10220 * } );
10221 *
10222 * // No initial sorting
10223 * $(document).ready( function() {
10224 * $('#example').dataTable( {
10225 * "order": []
10226 * } );
10227 * } );
10228 */
10229 "aaSorting": [[0,'asc']],
10230
10231
10232 /**
10233 * This parameter is basically identical to the `sorting` parameter, but
10234 * cannot be overridden by user interaction with the table. What this means
10235 * is that you could have a column (visible or hidden) which the sorting
10236 * will always be forced on first - any sorting after that (from the user)
10237 * will then be performed as required. This can be useful for grouping rows
10238 * together.
10239 * @type array
10240 * @default null
10241 *
10242 * @dtopt Option
10243 * @name DataTable.defaults.orderFixed
10244 *
10245 * @example
10246 * $(document).ready( function() {
10247 * $('#example').dataTable( {
10248 * "orderFixed": [[0,'asc']]
10249 * } );
10250 * } )
10251 */
10252 "aaSortingFixed": [],
10253
10254
10255 /**
10256 * DataTables can be instructed to load data to display in the table from a
10257 * Ajax source. This option defines how that Ajax call is made and where to.
10258 *
10259 * The `ajax` property has three different modes of operation, depending on
10260 * how it is defined. These are:
10261 *
10262 * * `string` - Set the URL from where the data should be loaded from.
10263 * * `object` - Define properties for `jQuery.ajax`.
10264 * * `function` - Custom data get function
10265 *
10266 * `string`
10267 * --------
10268 *
10269 * As a string, the `ajax` property simply defines the URL from which
10270 * DataTables will load data.
10271 *
10272 * `object`
10273 * --------
10274 *
10275 * As an object, the parameters in the object are passed to
10276 * [jQuery.ajax](http://api.jquery.com/jQuery.ajax/) allowing fine control
10277 * of the Ajax request. DataTables has a number of default parameters which
10278 * you can override using this option. Please refer to the jQuery
10279 * documentation for a full description of the options available, although
10280 * the following parameters provide additional options in DataTables or
10281 * require special consideration:
10282 *
10283 * * `data` - As with jQuery, `data` can be provided as an object, but it
10284 * can also be used as a function to manipulate the data DataTables sends
10285 * to the server. The function takes a single parameter, an object of
10286 * parameters with the values that DataTables has readied for sending. An
10287 * object may be returned which will be merged into the DataTables
10288 * defaults, or you can add the items to the object that was passed in and
10289 * not return anything from the function. This supersedes `fnServerParams`
10290 * from DataTables 1.9-.
10291 *
10292 * * `dataSrc` - By default DataTables will look for the property `data` (or
10293 * `aaData` for compatibility with DataTables 1.9-) when obtaining data
10294 * from an Ajax source or for server-side processing - this parameter
10295 * allows that property to be changed. You can use Javascript dotted
10296 * object notation to get a data source for multiple levels of nesting, or
10297 * it my be used as a function. As a function it takes a single parameter,
10298 * the JSON returned from the server, which can be manipulated as
10299 * required, with the returned value being that used by DataTables as the
10300 * data source for the table. This supersedes `sAjaxDataProp` from
10301 * DataTables 1.9-.
10302 *
10303 * * `success` - Should not be overridden it is used internally in
10304 * DataTables. To manipulate / transform the data returned by the server
10305 * use `ajax.dataSrc`, or use `ajax` as a function (see below).
10306 *
10307 * `function`
10308 * ----------
10309 *
10310 * As a function, making the Ajax call is left up to yourself allowing
10311 * complete control of the Ajax request. Indeed, if desired, a method other
10312 * than Ajax could be used to obtain the required data, such as Web storage
10313 * or an AIR database.
10314 *
10315 * The function is given four parameters and no return is required. The
10316 * parameters are:
10317 *
10318 * 1. _object_ - Data to send to the server
10319 * 2. _function_ - Callback function that must be executed when the required
10320 * data has been obtained. That data should be passed into the callback
10321 * as the only parameter
10322 * 3. _object_ - DataTables settings object for the table
10323 *
10324 * Note that this supersedes `fnServerData` from DataTables 1.9-.
10325 *
10326 * @type string|object|function
10327 * @default null
10328 *
10329 * @dtopt Option
10330 * @name DataTable.defaults.ajax
10331 * @since 1.10.0
10332 *
10333 * @example
10334 * // Get JSON data from a file via Ajax.
10335 * // Note DataTables expects data in the form `{ data: [ ...data... ] }` by default).
10336 * $('#example').dataTable( {
10337 * "ajax": "data.json"
10338 * } );
10339 *
10340 * @example
10341 * // Get JSON data from a file via Ajax, using `dataSrc` to change
10342 * // `data` to `tableData` (i.e. `{ tableData: [ ...data... ] }`)
10343 * $('#example').dataTable( {
10344 * "ajax": {
10345 * "url": "data.json",
10346 * "dataSrc": "tableData"
10347 * }
10348 * } );
10349 *
10350 * @example
10351 * // Get JSON data from a file via Ajax, using `dataSrc` to read data
10352 * // from a plain array rather than an array in an object
10353 * $('#example').dataTable( {
10354 * "ajax": {
10355 * "url": "data.json",
10356 * "dataSrc": ""
10357 * }
10358 * } );
10359 *
10360 * @example
10361 * // Manipulate the data returned from the server - add a link to data
10362 * // (note this can, should, be done using `render` for the column - this
10363 * // is just a simple example of how the data can be manipulated).
10364 * $('#example').dataTable( {
10365 * "ajax": {
10366 * "url": "data.json",
10367 * "dataSrc": function ( json ) {
10368 * for ( var i=0, ien=json.length ; i<ien ; i++ ) {
10369 * json[i][0] = '<a href="/message/'+json[i][0]+'>View message</a>';
10370 * }
10371 * return json;
10372 * }
10373 * }
10374 * } );
10375 *
10376 * @example
10377 * // Add data to the request
10378 * $('#example').dataTable( {
10379 * "ajax": {
10380 * "url": "data.json",
10381 * "data": function ( d ) {
10382 * return {
10383 * "extra_search": $('#extra').val()
10384 * };
10385 * }
10386 * }
10387 * } );
10388 *
10389 * @example
10390 * // Send request as POST
10391 * $('#example').dataTable( {
10392 * "ajax": {
10393 * "url": "data.json",
10394 * "type": "POST"
10395 * }
10396 * } );
10397 *
10398 * @example
10399 * // Get the data from localStorage (could interface with a form for
10400 * // adding, editing and removing rows).
10401 * $('#example').dataTable( {
10402 * "ajax": function (data, callback, settings) {
10403 * callback(
10404 * JSON.parse( localStorage.getItem('dataTablesData') )
10405 * );
10406 * }
10407 * } );
10408 */
10409 "ajax": null,
10410
10411
10412 /**
10413 * This parameter allows you to readily specify the entries in the length drop
10414 * down menu that DataTables shows when pagination is enabled. It can be
10415 * either a 1D array of options which will be used for both the displayed
10416 * option and the value, or a 2D array which will use the array in the first
10417 * position as the value, and the array in the second position as the
10418 * displayed options (useful for language strings such as 'All').
10419 *
10420 * Note that the `pageLength` property will be automatically set to the
10421 * first value given in this array, unless `pageLength` is also provided.
10422 * @type array
10423 * @default [ 10, 25, 50, 100 ]
10424 *
10425 * @dtopt Option
10426 * @name DataTable.defaults.lengthMenu
10427 *
10428 * @example
10429 * $(document).ready( function() {
10430 * $('#example').dataTable( {
10431 * "lengthMenu": [[10, 25, 50, -1], [10, 25, 50, "All"]]
10432 * } );
10433 * } );
10434 */
10435 "aLengthMenu": [ 10, 25, 50, 100 ],
10436
10437
10438 /**
10439 * The `columns` option in the initialisation parameter allows you to define
10440 * details about the way individual columns behave. For a full list of
10441 * column options that can be set, please see
10442 * {@link DataTable.defaults.column}. Note that if you use `columns` to
10443 * define your columns, you must have an entry in the array for every single
10444 * column that you have in your table (these can be null if you don't which
10445 * to specify any options).
10446 * @member
10447 *
10448 * @name DataTable.defaults.column
10449 */
10450 "aoColumns": null,
10451
10452 /**
10453 * Very similar to `columns`, `columnDefs` allows you to target a specific
10454 * column, multiple columns, or all columns, using the `targets` property of
10455 * each object in the array. This allows great flexibility when creating
10456 * tables, as the `columnDefs` arrays can be of any length, targeting the
10457 * columns you specifically want. `columnDefs` may use any of the column
10458 * options available: {@link DataTable.defaults.column}, but it _must_
10459 * have `targets` defined in each object in the array. Values in the `targets`
10460 * array may be:
10461 * <ul>
10462 * <li>a string - class name will be matched on the TH for the column</li>
10463 * <li>0 or a positive integer - column index counting from the left</li>
10464 * <li>a negative integer - column index counting from the right</li>
10465 * <li>the string "_all" - all columns (i.e. assign a default)</li>
10466 * </ul>
10467 * @member
10468 *
10469 * @name DataTable.defaults.columnDefs
10470 */
10471 "aoColumnDefs": null,
10472
10473
10474 /**
10475 * Basically the same as `search`, this parameter defines the individual column
10476 * filtering state at initialisation time. The array must be of the same size
10477 * as the number of columns, and each element be an object with the parameters
10478 * `search` and `escapeRegex` (the latter is optional). 'null' is also
10479 * accepted and the default will be used.
10480 * @type array
10481 * @default []
10482 *
10483 * @dtopt Option
10484 * @name DataTable.defaults.searchCols
10485 *
10486 * @example
10487 * $(document).ready( function() {
10488 * $('#example').dataTable( {
10489 * "searchCols": [
10490 * null,
10491 * { "search": "My filter" },
10492 * null,
10493 * { "search": "^[0-9]", "escapeRegex": false }
10494 * ]
10495 * } );
10496 * } )
10497 */
10498 "aoSearchCols": [],
10499
10500
10501 /**
10502 * An array of CSS classes that should be applied to displayed rows. This
10503 * array may be of any length, and DataTables will apply each class
10504 * sequentially, looping when required.
10505 * @type array
10506 * @default null <i>Will take the values determined by the `oClasses.stripe*`
10507 * options</i>
10508 *
10509 * @dtopt Option
10510 * @name DataTable.defaults.stripeClasses
10511 *
10512 * @example
10513 * $(document).ready( function() {
10514 * $('#example').dataTable( {
10515 * "stripeClasses": [ 'strip1', 'strip2', 'strip3' ]
10516 * } );
10517 * } )
10518 */
10519 "asStripeClasses": null,
10520
10521
10522 /**
10523 * Enable or disable automatic column width calculation. This can be disabled
10524 * as an optimisation (it takes some time to calculate the widths) if the
10525 * tables widths are passed in using `columns`.
10526 * @type boolean
10527 * @default true
10528 *
10529 * @dtopt Features
10530 * @name DataTable.defaults.autoWidth
10531 *
10532 * @example
10533 * $(document).ready( function () {
10534 * $('#example').dataTable( {
10535 * "autoWidth": false
10536 * } );
10537 * } );
10538 */
10539 "bAutoWidth": true,
10540
10541
10542 /**
10543 * Deferred rendering can provide DataTables with a huge speed boost when you
10544 * are using an Ajax or JS data source for the table. This option, when set to
10545 * true, will cause DataTables to defer the creation of the table elements for
10546 * each row until they are needed for a draw - saving a significant amount of
10547 * time.
10548 * @type boolean
10549 * @default false
10550 *
10551 * @dtopt Features
10552 * @name DataTable.defaults.deferRender
10553 *
10554 * @example
10555 * $(document).ready( function() {
10556 * $('#example').dataTable( {
10557 * "ajax": "sources/arrays.txt",
10558 * "deferRender": true
10559 * } );
10560 * } );
10561 */
10562 "bDeferRender": false,
10563
10564
10565 /**
10566 * Replace a DataTable which matches the given selector and replace it with
10567 * one which has the properties of the new initialisation object passed. If no
10568 * table matches the selector, then the new DataTable will be constructed as
10569 * per normal.
10570 * @type boolean
10571 * @default false
10572 *
10573 * @dtopt Options
10574 * @name DataTable.defaults.destroy
10575 *
10576 * @example
10577 * $(document).ready( function() {
10578 * $('#example').dataTable( {
10579 * "srollY": "200px",
10580 * "paginate": false
10581 * } );
10582 *
10583 * // Some time later....
10584 * $('#example').dataTable( {
10585 * "filter": false,
10586 * "destroy": true
10587 * } );
10588 * } );
10589 */
10590 "bDestroy": false,
10591
10592
10593 /**
10594 * Enable or disable filtering of data. Filtering in DataTables is "smart" in
10595 * that it allows the end user to input multiple words (space separated) and
10596 * will match a row containing those words, even if not in the order that was
10597 * specified (this allow matching across multiple columns). Note that if you
10598 * wish to use filtering in DataTables this must remain 'true' - to remove the
10599 * default filtering input box and retain filtering abilities, please use
10600 * {@link DataTable.defaults.dom}.
10601 * @type boolean
10602 * @default true
10603 *
10604 * @dtopt Features
10605 * @name DataTable.defaults.searching
10606 *
10607 * @example
10608 * $(document).ready( function () {
10609 * $('#example').dataTable( {
10610 * "searching": false
10611 * } );
10612 * } );
10613 */
10614 "bFilter": true,
10615
10616
10617 /**
10618 * Enable or disable the table information display. This shows information
10619 * about the data that is currently visible on the page, including information
10620 * about filtered data if that action is being performed.
10621 * @type boolean
10622 * @default true
10623 *
10624 * @dtopt Features
10625 * @name DataTable.defaults.info
10626 *
10627 * @example
10628 * $(document).ready( function () {
10629 * $('#example').dataTable( {
10630 * "info": false
10631 * } );
10632 * } );
10633 */
10634 "bInfo": true,
10635
10636
10637 /**
10638 * Allows the end user to select the size of a formatted page from a select
10639 * menu (sizes are 10, 25, 50 and 100). Requires pagination (`paginate`).
10640 * @type boolean
10641 * @default true
10642 *
10643 * @dtopt Features
10644 * @name DataTable.defaults.lengthChange
10645 *
10646 * @example
10647 * $(document).ready( function () {
10648 * $('#example').dataTable( {
10649 * "lengthChange": false
10650 * } );
10651 * } );
10652 */
10653 "bLengthChange": true,
10654
10655
10656 /**
10657 * Enable or disable pagination.
10658 * @type boolean
10659 * @default true
10660 *
10661 * @dtopt Features
10662 * @name DataTable.defaults.paging
10663 *
10664 * @example
10665 * $(document).ready( function () {
10666 * $('#example').dataTable( {
10667 * "paging": false
10668 * } );
10669 * } );
10670 */
10671 "bPaginate": true,
10672
10673
10674 /**
10675 * Enable or disable the display of a 'processing' indicator when the table is
10676 * being processed (e.g. a sort). This is particularly useful for tables with
10677 * large amounts of data where it can take a noticeable amount of time to sort
10678 * the entries.
10679 * @type boolean
10680 * @default false
10681 *
10682 * @dtopt Features
10683 * @name DataTable.defaults.processing
10684 *
10685 * @example
10686 * $(document).ready( function () {
10687 * $('#example').dataTable( {
10688 * "processing": true
10689 * } );
10690 * } );
10691 */
10692 "bProcessing": false,
10693
10694
10695 /**
10696 * Retrieve the DataTables object for the given selector. Note that if the
10697 * table has already been initialised, this parameter will cause DataTables
10698 * to simply return the object that has already been set up - it will not take
10699 * account of any changes you might have made to the initialisation object
10700 * passed to DataTables (setting this parameter to true is an acknowledgement
10701 * that you understand this). `destroy` can be used to reinitialise a table if
10702 * you need.
10703 * @type boolean
10704 * @default false
10705 *
10706 * @dtopt Options
10707 * @name DataTable.defaults.retrieve
10708 *
10709 * @example
10710 * $(document).ready( function() {
10711 * initTable();
10712 * tableActions();
10713 * } );
10714 *
10715 * function initTable ()
10716 * {
10717 * return $('#example').dataTable( {
10718 * "scrollY": "200px",
10719 * "paginate": false,
10720 * "retrieve": true
10721 * } );
10722 * }
10723 *
10724 * function tableActions ()
10725 * {
10726 * var table = initTable();
10727 * // perform API operations with oTable
10728 * }
10729 */
10730 "bRetrieve": false,
10731
10732
10733 /**
10734 * When vertical (y) scrolling is enabled, DataTables will force the height of
10735 * the table's viewport to the given height at all times (useful for layout).
10736 * However, this can look odd when filtering data down to a small data set,
10737 * and the footer is left "floating" further down. This parameter (when
10738 * enabled) will cause DataTables to collapse the table's viewport down when
10739 * the result set will fit within the given Y height.
10740 * @type boolean
10741 * @default false
10742 *
10743 * @dtopt Options
10744 * @name DataTable.defaults.scrollCollapse
10745 *
10746 * @example
10747 * $(document).ready( function() {
10748 * $('#example').dataTable( {
10749 * "scrollY": "200",
10750 * "scrollCollapse": true
10751 * } );
10752 * } );
10753 */
10754 "bScrollCollapse": false,
10755
10756
10757 /**
10758 * Configure DataTables to use server-side processing. Note that the
10759 * `ajax` parameter must also be given in order to give DataTables a
10760 * source to obtain the required data for each draw.
10761 * @type boolean
10762 * @default false
10763 *
10764 * @dtopt Features
10765 * @dtopt Server-side
10766 * @name DataTable.defaults.serverSide
10767 *
10768 * @example
10769 * $(document).ready( function () {
10770 * $('#example').dataTable( {
10771 * "serverSide": true,
10772 * "ajax": "xhr.php"
10773 * } );
10774 * } );
10775 */
10776 "bServerSide": false,
10777
10778
10779 /**
10780 * Enable or disable sorting of columns. Sorting of individual columns can be
10781 * disabled by the `sortable` option for each column.
10782 * @type boolean
10783 * @default true
10784 *
10785 * @dtopt Features
10786 * @name DataTable.defaults.ordering
10787 *
10788 * @example
10789 * $(document).ready( function () {
10790 * $('#example').dataTable( {
10791 * "ordering": false
10792 * } );
10793 * } );
10794 */
10795 "bSort": true,
10796
10797
10798 /**
10799 * Enable or display DataTables' ability to sort multiple columns at the
10800 * same time (activated by shift-click by the user).
10801 * @type boolean
10802 * @default true
10803 *
10804 * @dtopt Options
10805 * @name DataTable.defaults.orderMulti
10806 *
10807 * @example
10808 * // Disable multiple column sorting ability
10809 * $(document).ready( function () {
10810 * $('#example').dataTable( {
10811 * "orderMulti": false
10812 * } );
10813 * } );
10814 */
10815 "bSortMulti": true,
10816
10817
10818 /**
10819 * Allows control over whether DataTables should use the top (true) unique
10820 * cell that is found for a single column, or the bottom (false - default).
10821 * This is useful when using complex headers.
10822 * @type boolean
10823 * @default false
10824 *
10825 * @dtopt Options
10826 * @name DataTable.defaults.orderCellsTop
10827 *
10828 * @example
10829 * $(document).ready( function() {
10830 * $('#example').dataTable( {
10831 * "orderCellsTop": true
10832 * } );
10833 * } );
10834 */
10835 "bSortCellsTop": false,
10836
10837
10838 /**
10839 * Enable or disable the addition of the classes `sorting\_1`, `sorting\_2` and
10840 * `sorting\_3` to the columns which are currently being sorted on. This is
10841 * presented as a feature switch as it can increase processing time (while
10842 * classes are removed and added) so for large data sets you might want to
10843 * turn this off.
10844 * @type boolean
10845 * @default true
10846 *
10847 * @dtopt Features
10848 * @name DataTable.defaults.orderClasses
10849 *
10850 * @example
10851 * $(document).ready( function () {
10852 * $('#example').dataTable( {
10853 * "orderClasses": false
10854 * } );
10855 * } );
10856 */
10857 "bSortClasses": true,
10858
10859
10860 /**
10861 * Enable or disable state saving. When enabled HTML5 `localStorage` will be
10862 * used to save table display information such as pagination information,
10863 * display length, filtering and sorting. As such when the end user reloads
10864 * the page the display display will match what thy had previously set up.
10865 *
10866 * Due to the use of `localStorage` the default state saving is not supported
10867 * in IE6 or 7. If state saving is required in those browsers, use
10868 * `stateSaveCallback` to provide a storage solution such as cookies.
10869 * @type boolean
10870 * @default false
10871 *
10872 * @dtopt Features
10873 * @name DataTable.defaults.stateSave
10874 *
10875 * @example
10876 * $(document).ready( function () {
10877 * $('#example').dataTable( {
10878 * "stateSave": true
10879 * } );
10880 * } );
10881 */
10882 "bStateSave": false,
10883
10884
10885 /**
10886 * This function is called when a TR element is created (and all TD child
10887 * elements have been inserted), or registered if using a DOM source, allowing
10888 * manipulation of the TR element (adding classes etc).
10889 * @type function
10890 * @param {node} row "TR" element for the current row
10891 * @param {array} data Raw data array for this row
10892 * @param {int} dataIndex The index of this row in the internal aoData array
10893 *
10894 * @dtopt Callbacks
10895 * @name DataTable.defaults.createdRow
10896 *
10897 * @example
10898 * $(document).ready( function() {
10899 * $('#example').dataTable( {
10900 * "createdRow": function( row, data, dataIndex ) {
10901 * // Bold the grade for all 'A' grade browsers
10902 * if ( data[4] == "A" )
10903 * {
10904 * $('td:eq(4)', row).html( '<b>A</b>' );
10905 * }
10906 * }
10907 * } );
10908 * } );
10909 */
10910 "fnCreatedRow": null,
10911
10912
10913 /**
10914 * This function is called on every 'draw' event, and allows you to
10915 * dynamically modify any aspect you want about the created DOM.
10916 * @type function
10917 * @param {object} settings DataTables settings object
10918 *
10919 * @dtopt Callbacks
10920 * @name DataTable.defaults.drawCallback
10921 *
10922 * @example
10923 * $(document).ready( function() {
10924 * $('#example').dataTable( {
10925 * "drawCallback": function( settings ) {
10926 * alert( 'DataTables has redrawn the table' );
10927 * }
10928 * } );
10929 * } );
10930 */
10931 "fnDrawCallback": null,
10932
10933
10934 /**
10935 * Identical to fnHeaderCallback() but for the table footer this function
10936 * allows you to modify the table footer on every 'draw' event.
10937 * @type function
10938 * @param {node} foot "TR" element for the footer
10939 * @param {array} data Full table data (as derived from the original HTML)
10940 * @param {int} start Index for the current display starting point in the
10941 * display array
10942 * @param {int} end Index for the current display ending point in the
10943 * display array
10944 * @param {array int} display Index array to translate the visual position
10945 * to the full data array
10946 *
10947 * @dtopt Callbacks
10948 * @name DataTable.defaults.footerCallback
10949 *
10950 * @example
10951 * $(document).ready( function() {
10952 * $('#example').dataTable( {
10953 * "footerCallback": function( tfoot, data, start, end, display ) {
10954 * tfoot.getElementsByTagName('th')[0].innerHTML = "Starting index is "+start;
10955 * }
10956 * } );
10957 * } )
10958 */
10959 "fnFooterCallback": null,
10960
10961
10962 /**
10963 * When rendering large numbers in the information element for the table
10964 * (i.e. "Showing 1 to 10 of 57 entries") DataTables will render large numbers
10965 * to have a comma separator for the 'thousands' units (e.g. 1 million is
10966 * rendered as "1,000,000") to help readability for the end user. This
10967 * function will override the default method DataTables uses.
10968 * @type function
10969 * @member
10970 * @param {int} toFormat number to be formatted
10971 * @returns {string} formatted string for DataTables to show the number
10972 *
10973 * @dtopt Callbacks
10974 * @name DataTable.defaults.formatNumber
10975 *
10976 * @example
10977 * // Format a number using a single quote for the separator (note that
10978 * // this can also be done with the language.thousands option)
10979 * $(document).ready( function() {
10980 * $('#example').dataTable( {
10981 * "formatNumber": function ( toFormat ) {
10982 * return toFormat.toString().replace(
10983 * /\B(?=(\d{3})+(?!\d))/g, "'"
10984 * );
10985 * };
10986 * } );
10987 * } );
10988 */
10989 "fnFormatNumber": function ( toFormat ) {
10990 return toFormat.toString().replace(
10991 /\B(?=(\d{3})+(?!\d))/g,
10992 this.oLanguage.sThousands
10993 );
10994 },
10995
10996
10997 /**
10998 * This function is called on every 'draw' event, and allows you to
10999 * dynamically modify the header row. This can be used to calculate and
11000 * display useful information about the table.
11001 * @type function
11002 * @param {node} head "TR" element for the header
11003 * @param {array} data Full table data (as derived from the original HTML)
11004 * @param {int} start Index for the current display starting point in the
11005 * display array
11006 * @param {int} end Index for the current display ending point in the
11007 * display array
11008 * @param {array int} display Index array to translate the visual position
11009 * to the full data array
11010 *
11011 * @dtopt Callbacks
11012 * @name DataTable.defaults.headerCallback
11013 *
11014 * @example
11015 * $(document).ready( function() {
11016 * $('#example').dataTable( {
11017 * "fheaderCallback": function( head, data, start, end, display ) {
11018 * head.getElementsByTagName('th')[0].innerHTML = "Displaying "+(end-start)+" records";
11019 * }
11020 * } );
11021 * } )
11022 */
11023 "fnHeaderCallback": null,
11024
11025
11026 /**
11027 * The information element can be used to convey information about the current
11028 * state of the table. Although the internationalisation options presented by
11029 * DataTables are quite capable of dealing with most customisations, there may
11030 * be times where you wish to customise the string further. This callback
11031 * allows you to do exactly that.
11032 * @type function
11033 * @param {object} oSettings DataTables settings object
11034 * @param {int} start Starting position in data for the draw
11035 * @param {int} end End position in data for the draw
11036 * @param {int} max Total number of rows in the table (regardless of
11037 * filtering)
11038 * @param {int} total Total number of rows in the data set, after filtering
11039 * @param {string} pre The string that DataTables has formatted using it's
11040 * own rules
11041 * @returns {string} The string to be displayed in the information element.
11042 *
11043 * @dtopt Callbacks
11044 * @name DataTable.defaults.infoCallback
11045 *
11046 * @example
11047 * $('#example').dataTable( {
11048 * "infoCallback": function( settings, start, end, max, total, pre ) {
11049 * return start +" to "+ end;
11050 * }
11051 * } );
11052 */
11053 "fnInfoCallback": null,
11054
11055
11056 /**
11057 * Called when the table has been initialised. Normally DataTables will
11058 * initialise sequentially and there will be no need for this function,
11059 * however, this does not hold true when using external language information
11060 * since that is obtained using an async XHR call.
11061 * @type function
11062 * @param {object} settings DataTables settings object
11063 * @param {object} json The JSON object request from the server - only
11064 * present if client-side Ajax sourced data is used
11065 *
11066 * @dtopt Callbacks
11067 * @name DataTable.defaults.initComplete
11068 *
11069 * @example
11070 * $(document).ready( function() {
11071 * $('#example').dataTable( {
11072 * "initComplete": function(settings, json) {
11073 * alert( 'DataTables has finished its initialisation.' );
11074 * }
11075 * } );
11076 * } )
11077 */
11078 "fnInitComplete": null,
11079
11080
11081 /**
11082 * Called at the very start of each table draw and can be used to cancel the
11083 * draw by returning false, any other return (including undefined) results in
11084 * the full draw occurring).
11085 * @type function
11086 * @param {object} settings DataTables settings object
11087 * @returns {boolean} False will cancel the draw, anything else (including no
11088 * return) will allow it to complete.
11089 *
11090 * @dtopt Callbacks
11091 * @name DataTable.defaults.preDrawCallback
11092 *
11093 * @example
11094 * $(document).ready( function() {
11095 * $('#example').dataTable( {
11096 * "preDrawCallback": function( settings ) {
11097 * if ( $('#test').val() == 1 ) {
11098 * return false;
11099 * }
11100 * }
11101 * } );
11102 * } );
11103 */
11104 "fnPreDrawCallback": null,
11105
11106
11107 /**
11108 * This function allows you to 'post process' each row after it have been
11109 * generated for each table draw, but before it is rendered on screen. This
11110 * function might be used for setting the row class name etc.
11111 * @type function
11112 * @param {node} row "TR" element for the current row
11113 * @param {array} data Raw data array for this row
11114 * @param {int} displayIndex The display index for the current table draw
11115 * @param {int} displayIndexFull The index of the data in the full list of
11116 * rows (after filtering)
11117 *
11118 * @dtopt Callbacks
11119 * @name DataTable.defaults.rowCallback
11120 *
11121 * @example
11122 * $(document).ready( function() {
11123 * $('#example').dataTable( {
11124 * "rowCallback": function( row, data, displayIndex, displayIndexFull ) {
11125 * // Bold the grade for all 'A' grade browsers
11126 * if ( data[4] == "A" ) {
11127 * $('td:eq(4)', row).html( '<b>A</b>' );
11128 * }
11129 * }
11130 * } );
11131 * } );
11132 */
11133 "fnRowCallback": null,
11134
11135
11136 /**
11137 * __Deprecated__ The functionality provided by this parameter has now been
11138 * superseded by that provided through `ajax`, which should be used instead.
11139 *
11140 * This parameter allows you to override the default function which obtains
11141 * the data from the server so something more suitable for your application.
11142 * For example you could use POST data, or pull information from a Gears or
11143 * AIR database.
11144 * @type function
11145 * @member
11146 * @param {string} source HTTP source to obtain the data from (`ajax`)
11147 * @param {array} data A key/value pair object containing the data to send
11148 * to the server
11149 * @param {function} callback to be called on completion of the data get
11150 * process that will draw the data on the page.
11151 * @param {object} settings DataTables settings object
11152 *
11153 * @dtopt Callbacks
11154 * @dtopt Server-side
11155 * @name DataTable.defaults.serverData
11156 *
11157 * @deprecated 1.10. Please use `ajax` for this functionality now.
11158 */
11159 "fnServerData": null,
11160
11161
11162 /**
11163 * __Deprecated__ The functionality provided by this parameter has now been
11164 * superseded by that provided through `ajax`, which should be used instead.
11165 *
11166 * It is often useful to send extra data to the server when making an Ajax
11167 * request - for example custom filtering information, and this callback
11168 * function makes it trivial to send extra information to the server. The
11169 * passed in parameter is the data set that has been constructed by
11170 * DataTables, and you can add to this or modify it as you require.
11171 * @type function
11172 * @param {array} data Data array (array of objects which are name/value
11173 * pairs) that has been constructed by DataTables and will be sent to the
11174 * server. In the case of Ajax sourced data with server-side processing
11175 * this will be an empty array, for server-side processing there will be a
11176 * significant number of parameters!
11177 * @returns {undefined} Ensure that you modify the data array passed in,
11178 * as this is passed by reference.
11179 *
11180 * @dtopt Callbacks
11181 * @dtopt Server-side
11182 * @name DataTable.defaults.serverParams
11183 *
11184 * @deprecated 1.10. Please use `ajax` for this functionality now.
11185 */
11186 "fnServerParams": null,
11187
11188
11189 /**
11190 * Load the table state. With this function you can define from where, and how, the
11191 * state of a table is loaded. By default DataTables will load from `localStorage`
11192 * but you might wish to use a server-side database or cookies.
11193 * @type function
11194 * @member
11195 * @param {object} settings DataTables settings object
11196 * @param {object} callback Callback that can be executed when done. It
11197 * should be passed the loaded state object.
11198 * @return {object} The DataTables state object to be loaded
11199 *
11200 * @dtopt Callbacks
11201 * @name DataTable.defaults.stateLoadCallback
11202 *
11203 * @example
11204 * $(document).ready( function() {
11205 * $('#example').dataTable( {
11206 * "stateSave": true,
11207 * "stateLoadCallback": function (settings, callback) {
11208 * $.ajax( {
11209 * "url": "/state_load",
11210 * "dataType": "json",
11211 * "success": function (json) {
11212 * callback( json );
11213 * }
11214 * } );
11215 * }
11216 * } );
11217 * } );
11218 */
11219 "fnStateLoadCallback": function ( settings ) {
11220 try {
11221 return JSON.parse(
11222 (settings.iStateDuration === -1 ? sessionStorage : localStorage).getItem(
11223 'DataTables_'+settings.sInstance+'_'+location.pathname
11224 )
11225 );
11226 } catch (e) {
11227 return {};
11228 }
11229 },
11230
11231
11232 /**
11233 * Callback which allows modification of the saved state prior to loading that state.
11234 * This callback is called when the table is loading state from the stored data, but
11235 * prior to the settings object being modified by the saved state. Note that for
11236 * plug-in authors, you should use the `stateLoadParams` event to load parameters for
11237 * a plug-in.
11238 * @type function
11239 * @param {object} settings DataTables settings object
11240 * @param {object} data The state object that is to be loaded
11241 *
11242 * @dtopt Callbacks
11243 * @name DataTable.defaults.stateLoadParams
11244 *
11245 * @example
11246 * // Remove a saved filter, so filtering is never loaded
11247 * $(document).ready( function() {
11248 * $('#example').dataTable( {
11249 * "stateSave": true,
11250 * "stateLoadParams": function (settings, data) {
11251 * data.oSearch.sSearch = "";
11252 * }
11253 * } );
11254 * } );
11255 *
11256 * @example
11257 * // Disallow state loading by returning false
11258 * $(document).ready( function() {
11259 * $('#example').dataTable( {
11260 * "stateSave": true,
11261 * "stateLoadParams": function (settings, data) {
11262 * return false;
11263 * }
11264 * } );
11265 * } );
11266 */
11267 "fnStateLoadParams": null,
11268
11269
11270 /**
11271 * Callback that is called when the state has been loaded from the state saving method
11272 * and the DataTables settings object has been modified as a result of the loaded state.
11273 * @type function
11274 * @param {object} settings DataTables settings object
11275 * @param {object} data The state object that was loaded
11276 *
11277 * @dtopt Callbacks
11278 * @name DataTable.defaults.stateLoaded
11279 *
11280 * @example
11281 * // Show an alert with the filtering value that was saved
11282 * $(document).ready( function() {
11283 * $('#example').dataTable( {
11284 * "stateSave": true,
11285 * "stateLoaded": function (settings, data) {
11286 * alert( 'Saved filter was: '+data.oSearch.sSearch );
11287 * }
11288 * } );
11289 * } );
11290 */
11291 "fnStateLoaded": null,
11292
11293
11294 /**
11295 * Save the table state. This function allows you to define where and how the state
11296 * information for the table is stored By default DataTables will use `localStorage`
11297 * but you might wish to use a server-side database or cookies.
11298 * @type function
11299 * @member
11300 * @param {object} settings DataTables settings object
11301 * @param {object} data The state object to be saved
11302 *
11303 * @dtopt Callbacks
11304 * @name DataTable.defaults.stateSaveCallback
11305 *
11306 * @example
11307 * $(document).ready( function() {
11308 * $('#example').dataTable( {
11309 * "stateSave": true,
11310 * "stateSaveCallback": function (settings, data) {
11311 * // Send an Ajax request to the server with the state object
11312 * $.ajax( {
11313 * "url": "/state_save",
11314 * "data": data,
11315 * "dataType": "json",
11316 * "method": "POST"
11317 * "success": function () {}
11318 * } );
11319 * }
11320 * } );
11321 * } );
11322 */
11323 "fnStateSaveCallback": function ( settings, data ) {
11324 try {
11325 (settings.iStateDuration === -1 ? sessionStorage : localStorage).setItem(
11326 'DataTables_'+settings.sInstance+'_'+location.pathname,
11327 JSON.stringify( data )
11328 );
11329 } catch (e) {}
11330 },
11331
11332
11333 /**
11334 * Callback which allows modification of the state to be saved. Called when the table
11335 * has changed state a new state save is required. This method allows modification of
11336 * the state saving object prior to actually doing the save, including addition or
11337 * other state properties or modification. Note that for plug-in authors, you should
11338 * use the `stateSaveParams` event to save parameters for a plug-in.
11339 * @type function
11340 * @param {object} settings DataTables settings object
11341 * @param {object} data The state object to be saved
11342 *
11343 * @dtopt Callbacks
11344 * @name DataTable.defaults.stateSaveParams
11345 *
11346 * @example
11347 * // Remove a saved filter, so filtering is never saved
11348 * $(document).ready( function() {
11349 * $('#example').dataTable( {
11350 * "stateSave": true,
11351 * "stateSaveParams": function (settings, data) {
11352 * data.oSearch.sSearch = "";
11353 * }
11354 * } );
11355 * } );
11356 */
11357 "fnStateSaveParams": null,
11358
11359
11360 /**
11361 * Duration for which the saved state information is considered valid. After this period
11362 * has elapsed the state will be returned to the default.
11363 * Value is given in seconds.
11364 * @type int
11365 * @default 7200 <i>(2 hours)</i>
11366 *
11367 * @dtopt Options
11368 * @name DataTable.defaults.stateDuration
11369 *
11370 * @example
11371 * $(document).ready( function() {
11372 * $('#example').dataTable( {
11373 * "stateDuration": 60*60*24; // 1 day
11374 * } );
11375 * } )
11376 */
11377 "iStateDuration": 7200,
11378
11379
11380 /**
11381 * When enabled DataTables will not make a request to the server for the first
11382 * page draw - rather it will use the data already on the page (no sorting etc
11383 * will be applied to it), thus saving on an XHR at load time. `deferLoading`
11384 * is used to indicate that deferred loading is required, but it is also used
11385 * to tell DataTables how many records there are in the full table (allowing
11386 * the information element and pagination to be displayed correctly). In the case
11387 * where a filtering is applied to the table on initial load, this can be
11388 * indicated by giving the parameter as an array, where the first element is
11389 * the number of records available after filtering and the second element is the
11390 * number of records without filtering (allowing the table information element
11391 * to be shown correctly).
11392 * @type int | array
11393 * @default null
11394 *
11395 * @dtopt Options
11396 * @name DataTable.defaults.deferLoading
11397 *
11398 * @example
11399 * // 57 records available in the table, no filtering applied
11400 * $(document).ready( function() {
11401 * $('#example').dataTable( {
11402 * "serverSide": true,
11403 * "ajax": "scripts/server_processing.php",
11404 * "deferLoading": 57
11405 * } );
11406 * } );
11407 *
11408 * @example
11409 * // 57 records after filtering, 100 without filtering (an initial filter applied)
11410 * $(document).ready( function() {
11411 * $('#example').dataTable( {
11412 * "serverSide": true,
11413 * "ajax": "scripts/server_processing.php",
11414 * "deferLoading": [ 57, 100 ],
11415 * "search": {
11416 * "search": "my_filter"
11417 * }
11418 * } );
11419 * } );
11420 */
11421 "iDeferLoading": null,
11422
11423
11424 /**
11425 * Number of rows to display on a single page when using pagination. If
11426 * feature enabled (`lengthChange`) then the end user will be able to override
11427 * this to a custom setting using a pop-up menu.
11428 * @type int
11429 * @default 10
11430 *
11431 * @dtopt Options
11432 * @name DataTable.defaults.pageLength
11433 *
11434 * @example
11435 * $(document).ready( function() {
11436 * $('#example').dataTable( {
11437 * "pageLength": 50
11438 * } );
11439 * } )
11440 */
11441 "iDisplayLength": 10,
11442
11443
11444 /**
11445 * Define the starting point for data display when using DataTables with
11446 * pagination. Note that this parameter is the number of records, rather than
11447 * the page number, so if you have 10 records per page and want to start on
11448 * the third page, it should be "20".
11449 * @type int
11450 * @default 0
11451 *
11452 * @dtopt Options
11453 * @name DataTable.defaults.displayStart
11454 *
11455 * @example
11456 * $(document).ready( function() {
11457 * $('#example').dataTable( {
11458 * "displayStart": 20
11459 * } );
11460 * } )
11461 */
11462 "iDisplayStart": 0,
11463
11464
11465 /**
11466 * By default DataTables allows keyboard navigation of the table (sorting, paging,
11467 * and filtering) by adding a `tabindex` attribute to the required elements. This
11468 * allows you to tab through the controls and press the enter key to activate them.
11469 * The tabindex is default 0, meaning that the tab follows the flow of the document.
11470 * You can overrule this using this parameter if you wish. Use a value of -1 to
11471 * disable built-in keyboard navigation.
11472 * @type int
11473 * @default 0
11474 *
11475 * @dtopt Options
11476 * @name DataTable.defaults.tabIndex
11477 *
11478 * @example
11479 * $(document).ready( function() {
11480 * $('#example').dataTable( {
11481 * "tabIndex": 1
11482 * } );
11483 * } );
11484 */
11485 "iTabIndex": 0,
11486
11487
11488 /**
11489 * Classes that DataTables assigns to the various components and features
11490 * that it adds to the HTML table. This allows classes to be configured
11491 * during initialisation in addition to through the static
11492 * {@link DataTable.ext.oStdClasses} object).
11493 * @namespace
11494 * @name DataTable.defaults.classes
11495 */
11496 "oClasses": {},
11497
11498
11499 /**
11500 * All strings that DataTables uses in the user interface that it creates
11501 * are defined in this object, allowing you to modified them individually or
11502 * completely replace them all as required.
11503 * @namespace
11504 * @name DataTable.defaults.language
11505 */
11506 "oLanguage": {
11507 /**
11508 * Strings that are used for WAI-ARIA labels and controls only (these are not
11509 * actually visible on the page, but will be read by screenreaders, and thus
11510 * must be internationalised as well).
11511 * @namespace
11512 * @name DataTable.defaults.language.aria
11513 */
11514 "oAria": {
11515 /**
11516 * ARIA label that is added to the table headers when the column may be
11517 * sorted ascending by activing the column (click or return when focused).
11518 * Note that the column header is prefixed to this string.
11519 * @type string
11520 * @default : activate to sort column ascending
11521 *
11522 * @dtopt Language
11523 * @name DataTable.defaults.language.aria.sortAscending
11524 *
11525 * @example
11526 * $(document).ready( function() {
11527 * $('#example').dataTable( {
11528 * "language": {
11529 * "aria": {
11530 * "sortAscending": " - click/return to sort ascending"
11531 * }
11532 * }
11533 * } );
11534 * } );
11535 */
11536 "sSortAscending": ": activate to sort column ascending",
11537
11538 /**
11539 * ARIA label that is added to the table headers when the column may be
11540 * sorted descending by activing the column (click or return when focused).
11541 * Note that the column header is prefixed to this string.
11542 * @type string
11543 * @default : activate to sort column ascending
11544 *
11545 * @dtopt Language
11546 * @name DataTable.defaults.language.aria.sortDescending
11547 *
11548 * @example
11549 * $(document).ready( function() {
11550 * $('#example').dataTable( {
11551 * "language": {
11552 * "aria": {
11553 * "sortDescending": " - click/return to sort descending"
11554 * }
11555 * }
11556 * } );
11557 * } );
11558 */
11559 "sSortDescending": ": activate to sort column descending"
11560 },
11561
11562 /**
11563 * Pagination string used by DataTables for the built-in pagination
11564 * control types.
11565 * @namespace
11566 * @name DataTable.defaults.language.paginate
11567 */
11568 "oPaginate": {
11569 /**
11570 * Text to use when using the 'full_numbers' type of pagination for the
11571 * button to take the user to the first page.
11572 * @type string
11573 * @default First
11574 *
11575 * @dtopt Language
11576 * @name DataTable.defaults.language.paginate.first
11577 *
11578 * @example
11579 * $(document).ready( function() {
11580 * $('#example').dataTable( {
11581 * "language": {
11582 * "paginate": {
11583 * "first": "First page"
11584 * }
11585 * }
11586 * } );
11587 * } );
11588 */
11589 "sFirst": "First",
11590
11591
11592 /**
11593 * Text to use when using the 'full_numbers' type of pagination for the
11594 * button to take the user to the last page.
11595 * @type string
11596 * @default Last
11597 *
11598 * @dtopt Language
11599 * @name DataTable.defaults.language.paginate.last
11600 *
11601 * @example
11602 * $(document).ready( function() {
11603 * $('#example').dataTable( {
11604 * "language": {
11605 * "paginate": {
11606 * "last": "Last page"
11607 * }
11608 * }
11609 * } );
11610 * } );
11611 */
11612 "sLast": "Last",
11613
11614
11615 /**
11616 * Text to use for the 'next' pagination button (to take the user to the
11617 * next page).
11618 * @type string
11619 * @default Next
11620 *
11621 * @dtopt Language
11622 * @name DataTable.defaults.language.paginate.next
11623 *
11624 * @example
11625 * $(document).ready( function() {
11626 * $('#example').dataTable( {
11627 * "language": {
11628 * "paginate": {
11629 * "next": "Next page"
11630 * }
11631 * }
11632 * } );
11633 * } );
11634 */
11635 "sNext": "Next",
11636
11637
11638 /**
11639 * Text to use for the 'previous' pagination button (to take the user to
11640 * the previous page).
11641 * @type string
11642 * @default Previous
11643 *
11644 * @dtopt Language
11645 * @name DataTable.defaults.language.paginate.previous
11646 *
11647 * @example
11648 * $(document).ready( function() {
11649 * $('#example').dataTable( {
11650 * "language": {
11651 * "paginate": {
11652 * "previous": "Previous page"
11653 * }
11654 * }
11655 * } );
11656 * } );
11657 */
11658 "sPrevious": "Previous"
11659 },
11660
11661 /**
11662 * This string is shown in preference to `zeroRecords` when the table is
11663 * empty of data (regardless of filtering). Note that this is an optional
11664 * parameter - if it is not given, the value of `zeroRecords` will be used
11665 * instead (either the default or given value).
11666 * @type string
11667 * @default No data available in table
11668 *
11669 * @dtopt Language
11670 * @name DataTable.defaults.language.emptyTable
11671 *
11672 * @example
11673 * $(document).ready( function() {
11674 * $('#example').dataTable( {
11675 * "language": {
11676 * "emptyTable": "No data available in table"
11677 * }
11678 * } );
11679 * } );
11680 */
11681 "sEmptyTable": "No data available in table",
11682
11683
11684 /**
11685 * This string gives information to the end user about the information
11686 * that is current on display on the page. The following tokens can be
11687 * used in the string and will be dynamically replaced as the table
11688 * display updates. This tokens can be placed anywhere in the string, or
11689 * removed as needed by the language requires:
11690 *
11691 * * `\_START\_` - Display index of the first record on the current page
11692 * * `\_END\_` - Display index of the last record on the current page
11693 * * `\_TOTAL\_` - Number of records in the table after filtering
11694 * * `\_MAX\_` - Number of records in the table without filtering
11695 * * `\_PAGE\_` - Current page number
11696 * * `\_PAGES\_` - Total number of pages of data in the table
11697 *
11698 * @type string
11699 * @default Showing _START_ to _END_ of _TOTAL_ entries
11700 *
11701 * @dtopt Language
11702 * @name DataTable.defaults.language.info
11703 *
11704 * @example
11705 * $(document).ready( function() {
11706 * $('#example').dataTable( {
11707 * "language": {
11708 * "info": "Showing page _PAGE_ of _PAGES_"
11709 * }
11710 * } );
11711 * } );
11712 */
11713 "sInfo": "Showing _START_ to _END_ of _TOTAL_ entries",
11714
11715
11716 /**
11717 * Display information string for when the table is empty. Typically the
11718 * format of this string should match `info`.
11719 * @type string
11720 * @default Showing 0 to 0 of 0 entries
11721 *
11722 * @dtopt Language
11723 * @name DataTable.defaults.language.infoEmpty
11724 *
11725 * @example
11726 * $(document).ready( function() {
11727 * $('#example').dataTable( {
11728 * "language": {
11729 * "infoEmpty": "No entries to show"
11730 * }
11731 * } );
11732 * } );
11733 */
11734 "sInfoEmpty": "Showing 0 to 0 of 0 entries",
11735
11736
11737 /**
11738 * When a user filters the information in a table, this string is appended
11739 * to the information (`info`) to give an idea of how strong the filtering
11740 * is. The variable _MAX_ is dynamically updated.
11741 * @type string
11742 * @default (filtered from _MAX_ total entries)
11743 *
11744 * @dtopt Language
11745 * @name DataTable.defaults.language.infoFiltered
11746 *
11747 * @example
11748 * $(document).ready( function() {
11749 * $('#example').dataTable( {
11750 * "language": {
11751 * "infoFiltered": " - filtering from _MAX_ records"
11752 * }
11753 * } );
11754 * } );
11755 */
11756 "sInfoFiltered": "(filtered from _MAX_ total entries)",
11757
11758
11759 /**
11760 * If can be useful to append extra information to the info string at times,
11761 * and this variable does exactly that. This information will be appended to
11762 * the `info` (`infoEmpty` and `infoFiltered` in whatever combination they are
11763 * being used) at all times.
11764 * @type string
11765 * @default <i>Empty string</i>
11766 *
11767 * @dtopt Language
11768 * @name DataTable.defaults.language.infoPostFix
11769 *
11770 * @example
11771 * $(document).ready( function() {
11772 * $('#example').dataTable( {
11773 * "language": {
11774 * "infoPostFix": "All records shown are derived from real information."
11775 * }
11776 * } );
11777 * } );
11778 */
11779 "sInfoPostFix": "",
11780
11781
11782 /**
11783 * This decimal place operator is a little different from the other
11784 * language options since DataTables doesn't output floating point
11785 * numbers, so it won't ever use this for display of a number. Rather,
11786 * what this parameter does is modify the sort methods of the table so
11787 * that numbers which are in a format which has a character other than
11788 * a period (`.`) as a decimal place will be sorted numerically.
11789 *
11790 * Note that numbers with different decimal places cannot be shown in
11791 * the same table and still be sortable, the table must be consistent.
11792 * However, multiple different tables on the page can use different
11793 * decimal place characters.
11794 * @type string
11795 * @default
11796 *
11797 * @dtopt Language
11798 * @name DataTable.defaults.language.decimal
11799 *
11800 * @example
11801 * $(document).ready( function() {
11802 * $('#example').dataTable( {
11803 * "language": {
11804 * "decimal": ","
11805 * "thousands": "."
11806 * }
11807 * } );
11808 * } );
11809 */
11810 "sDecimal": "",
11811
11812
11813 /**
11814 * DataTables has a build in number formatter (`formatNumber`) which is
11815 * used to format large numbers that are used in the table information.
11816 * By default a comma is used, but this can be trivially changed to any
11817 * character you wish with this parameter.
11818 * @type string
11819 * @default ,
11820 *
11821 * @dtopt Language
11822 * @name DataTable.defaults.language.thousands
11823 *
11824 * @example
11825 * $(document).ready( function() {
11826 * $('#example').dataTable( {
11827 * "language": {
11828 * "thousands": "'"
11829 * }
11830 * } );
11831 * } );
11832 */
11833 "sThousands": ",",
11834
11835
11836 /**
11837 * Detail the action that will be taken when the drop down menu for the
11838 * pagination length option is changed. The '_MENU_' variable is replaced
11839 * with a default select list of 10, 25, 50 and 100, and can be replaced
11840 * with a custom select box if required.
11841 * @type string
11842 * @default Show _MENU_ entries
11843 *
11844 * @dtopt Language
11845 * @name DataTable.defaults.language.lengthMenu
11846 *
11847 * @example
11848 * // Language change only
11849 * $(document).ready( function() {
11850 * $('#example').dataTable( {
11851 * "language": {
11852 * "lengthMenu": "Display _MENU_ records"
11853 * }
11854 * } );
11855 * } );
11856 *
11857 * @example
11858 * // Language and options change
11859 * $(document).ready( function() {
11860 * $('#example').dataTable( {
11861 * "language": {
11862 * "lengthMenu": 'Display <select>'+
11863 * '<option value="10">10</option>'+
11864 * '<option value="20">20</option>'+
11865 * '<option value="30">30</option>'+
11866 * '<option value="40">40</option>'+
11867 * '<option value="50">50</option>'+
11868 * '<option value="-1">All</option>'+
11869 * '</select> records'
11870 * }
11871 * } );
11872 * } );
11873 */
11874 "sLengthMenu": "Show _MENU_ entries",
11875
11876
11877 /**
11878 * When using Ajax sourced data and during the first draw when DataTables is
11879 * gathering the data, this message is shown in an empty row in the table to
11880 * indicate to the end user the the data is being loaded. Note that this
11881 * parameter is not used when loading data by server-side processing, just
11882 * Ajax sourced data with client-side processing.
11883 * @type string
11884 * @default Loading...
11885 *
11886 * @dtopt Language
11887 * @name DataTable.defaults.language.loadingRecords
11888 *
11889 * @example
11890 * $(document).ready( function() {
11891 * $('#example').dataTable( {
11892 * "language": {
11893 * "loadingRecords": "Please wait - loading..."
11894 * }
11895 * } );
11896 * } );
11897 */
11898 "sLoadingRecords": "Loading...",
11899
11900
11901 /**
11902 * Text which is displayed when the table is processing a user action
11903 * (usually a sort command or similar).
11904 * @type string
11905 *
11906 * @dtopt Language
11907 * @name DataTable.defaults.language.processing
11908 *
11909 * @example
11910 * $(document).ready( function() {
11911 * $('#example').dataTable( {
11912 * "language": {
11913 * "processing": "DataTables is currently busy"
11914 * }
11915 * } );
11916 * } );
11917 */
11918 "sProcessing": "",
11919
11920
11921 /**
11922 * Details the actions that will be taken when the user types into the
11923 * filtering input text box. The variable "_INPUT_", if used in the string,
11924 * is replaced with the HTML text box for the filtering input allowing
11925 * control over where it appears in the string. If "_INPUT_" is not given
11926 * then the input box is appended to the string automatically.
11927 * @type string
11928 * @default Search:
11929 *
11930 * @dtopt Language
11931 * @name DataTable.defaults.language.search
11932 *
11933 * @example
11934 * // Input text box will be appended at the end automatically
11935 * $(document).ready( function() {
11936 * $('#example').dataTable( {
11937 * "language": {
11938 * "search": "Filter records:"
11939 * }
11940 * } );
11941 * } );
11942 *
11943 * @example
11944 * // Specify where the filter should appear
11945 * $(document).ready( function() {
11946 * $('#example').dataTable( {
11947 * "language": {
11948 * "search": "Apply filter _INPUT_ to table"
11949 * }
11950 * } );
11951 * } );
11952 */
11953 "sSearch": "Search:",
11954
11955
11956 /**
11957 * Assign a `placeholder` attribute to the search `input` element
11958 * @type string
11959 * @default
11960 *
11961 * @dtopt Language
11962 * @name DataTable.defaults.language.searchPlaceholder
11963 */
11964 "sSearchPlaceholder": "",
11965
11966
11967 /**
11968 * All of the language information can be stored in a file on the
11969 * server-side, which DataTables will look up if this parameter is passed.
11970 * It must store the URL of the language file, which is in a JSON format,
11971 * and the object has the same properties as the oLanguage object in the
11972 * initialiser object (i.e. the above parameters). Please refer to one of
11973 * the example language files to see how this works in action.
11974 * @type string
11975 * @default <i>Empty string - i.e. disabled</i>
11976 *
11977 * @dtopt Language
11978 * @name DataTable.defaults.language.url
11979 *
11980 * @example
11981 * $(document).ready( function() {
11982 * $('#example').dataTable( {
11983 * "language": {
11984 * "url": "http://www.sprymedia.co.uk/dataTables/lang.txt"
11985 * }
11986 * } );
11987 * } );
11988 */
11989 "sUrl": "",
11990
11991
11992 /**
11993 * Text shown inside the table records when the is no information to be
11994 * displayed after filtering. `emptyTable` is shown when there is simply no
11995 * information in the table at all (regardless of filtering).
11996 * @type string
11997 * @default No matching records found
11998 *
11999 * @dtopt Language
12000 * @name DataTable.defaults.language.zeroRecords
12001 *
12002 * @example
12003 * $(document).ready( function() {
12004 * $('#example').dataTable( {
12005 * "language": {
12006 * "zeroRecords": "No records to display"
12007 * }
12008 * } );
12009 * } );
12010 */
12011 "sZeroRecords": "No matching records found"
12012 },
12013
12014
12015 /**
12016 * This parameter allows you to have define the global filtering state at
12017 * initialisation time. As an object the `search` parameter must be
12018 * defined, but all other parameters are optional. When `regex` is true,
12019 * the search string will be treated as a regular expression, when false
12020 * (default) it will be treated as a straight string. When `smart`
12021 * DataTables will use it's smart filtering methods (to word match at
12022 * any point in the data), when false this will not be done.
12023 * @namespace
12024 * @extends DataTable.models.oSearch
12025 *
12026 * @dtopt Options
12027 * @name DataTable.defaults.search
12028 *
12029 * @example
12030 * $(document).ready( function() {
12031 * $('#example').dataTable( {
12032 * "search": {"search": "Initial search"}
12033 * } );
12034 * } )
12035 */
12036 "oSearch": $.extend( {}, DataTable.models.oSearch ),
12037
12038
12039 /**
12040 * __Deprecated__ The functionality provided by this parameter has now been
12041 * superseded by that provided through `ajax`, which should be used instead.
12042 *
12043 * By default DataTables will look for the property `data` (or `aaData` for
12044 * compatibility with DataTables 1.9-) when obtaining data from an Ajax
12045 * source or for server-side processing - this parameter allows that
12046 * property to be changed. You can use Javascript dotted object notation to
12047 * get a data source for multiple levels of nesting.
12048 * @type string
12049 * @default data
12050 *
12051 * @dtopt Options
12052 * @dtopt Server-side
12053 * @name DataTable.defaults.ajaxDataProp
12054 *
12055 * @deprecated 1.10. Please use `ajax` for this functionality now.
12056 */
12057 "sAjaxDataProp": "data",
12058
12059
12060 /**
12061 * __Deprecated__ The functionality provided by this parameter has now been
12062 * superseded by that provided through `ajax`, which should be used instead.
12063 *
12064 * You can instruct DataTables to load data from an external
12065 * source using this parameter (use aData if you want to pass data in you
12066 * already have). Simply provide a url a JSON object can be obtained from.
12067 * @type string
12068 * @default null
12069 *
12070 * @dtopt Options
12071 * @dtopt Server-side
12072 * @name DataTable.defaults.ajaxSource
12073 *
12074 * @deprecated 1.10. Please use `ajax` for this functionality now.
12075 */
12076 "sAjaxSource": null,
12077
12078
12079 /**
12080 * This initialisation variable allows you to specify exactly where in the
12081 * DOM you want DataTables to inject the various controls it adds to the page
12082 * (for example you might want the pagination controls at the top of the
12083 * table). DIV elements (with or without a custom class) can also be added to
12084 * aid styling. The follow syntax is used:
12085 * <ul>
12086 * <li>The following options are allowed:
12087 * <ul>
12088 * <li>'l' - Length changing</li>
12089 * <li>'f' - Filtering input</li>
12090 * <li>'t' - The table!</li>
12091 * <li>'i' - Information</li>
12092 * <li>'p' - Pagination</li>
12093 * <li>'r' - pRocessing</li>
12094 * </ul>
12095 * </li>
12096 * <li>The following constants are allowed:
12097 * <ul>
12098 * <li>'H' - jQueryUI theme "header" classes ('fg-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix')</li>
12099 * <li>'F' - jQueryUI theme "footer" classes ('fg-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix')</li>
12100 * </ul>
12101 * </li>
12102 * <li>The following syntax is expected:
12103 * <ul>
12104 * <li>'&lt;' and '&gt;' - div elements</li>
12105 * <li>'&lt;"class" and '&gt;' - div with a class</li>
12106 * <li>'&lt;"#id" and '&gt;' - div with an ID</li>
12107 * </ul>
12108 * </li>
12109 * <li>Examples:
12110 * <ul>
12111 * <li>'&lt;"wrapper"flipt&gt;'</li>
12112 * <li>'&lt;lf&lt;t&gt;ip&gt;'</li>
12113 * </ul>
12114 * </li>
12115 * </ul>
12116 * @type string
12117 * @default lfrtip <i>(when `jQueryUI` is false)</i> <b>or</b>
12118 * <"H"lfr>t<"F"ip> <i>(when `jQueryUI` is true)</i>
12119 *
12120 * @dtopt Options
12121 * @name DataTable.defaults.dom
12122 *
12123 * @example
12124 * $(document).ready( function() {
12125 * $('#example').dataTable( {
12126 * "dom": '&lt;"top"i&gt;rt&lt;"bottom"flp&gt;&lt;"clear"&gt;'
12127 * } );
12128 * } );
12129 */
12130 "sDom": "lfrtip",
12131
12132
12133 /**
12134 * Search delay option. This will throttle full table searches that use the
12135 * DataTables provided search input element (it does not effect calls to
12136 * `dt-api search()`, providing a delay before the search is made.
12137 * @type integer
12138 * @default 0
12139 *
12140 * @dtopt Options
12141 * @name DataTable.defaults.searchDelay
12142 *
12143 * @example
12144 * $(document).ready( function() {
12145 * $('#example').dataTable( {
12146 * "searchDelay": 200
12147 * } );
12148 * } )
12149 */
12150 "searchDelay": null,
12151
12152
12153 /**
12154 * DataTables features six different built-in options for the buttons to
12155 * display for pagination control:
12156 *
12157 * * `numbers` - Page number buttons only
12158 * * `simple` - 'Previous' and 'Next' buttons only
12159 * * 'simple_numbers` - 'Previous' and 'Next' buttons, plus page numbers
12160 * * `full` - 'First', 'Previous', 'Next' and 'Last' buttons
12161 * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus page numbers
12162 * * `first_last_numbers` - 'First' and 'Last' buttons, plus page numbers
12163 *
12164 * Further methods can be added using {@link DataTable.ext.oPagination}.
12165 * @type string
12166 * @default simple_numbers
12167 *
12168 * @dtopt Options
12169 * @name DataTable.defaults.pagingType
12170 *
12171 * @example
12172 * $(document).ready( function() {
12173 * $('#example').dataTable( {
12174 * "pagingType": "full_numbers"
12175 * } );
12176 * } )
12177 */
12178 "sPaginationType": "simple_numbers",
12179
12180
12181 /**
12182 * Enable horizontal scrolling. When a table is too wide to fit into a
12183 * certain layout, or you have a large number of columns in the table, you
12184 * can enable x-scrolling to show the table in a viewport, which can be
12185 * scrolled. This property can be `true` which will allow the table to
12186 * scroll horizontally when needed, or any CSS unit, or a number (in which
12187 * case it will be treated as a pixel measurement). Setting as simply `true`
12188 * is recommended.
12189 * @type boolean|string
12190 * @default <i>blank string - i.e. disabled</i>
12191 *
12192 * @dtopt Features
12193 * @name DataTable.defaults.scrollX
12194 *
12195 * @example
12196 * $(document).ready( function() {
12197 * $('#example').dataTable( {
12198 * "scrollX": true,
12199 * "scrollCollapse": true
12200 * } );
12201 * } );
12202 */
12203 "sScrollX": "",
12204
12205
12206 /**
12207 * This property can be used to force a DataTable to use more width than it
12208 * might otherwise do when x-scrolling is enabled. For example if you have a
12209 * table which requires to be well spaced, this parameter is useful for
12210 * "over-sizing" the table, and thus forcing scrolling. This property can by
12211 * any CSS unit, or a number (in which case it will be treated as a pixel
12212 * measurement).
12213 * @type string
12214 * @default <i>blank string - i.e. disabled</i>
12215 *
12216 * @dtopt Options
12217 * @name DataTable.defaults.scrollXInner
12218 *
12219 * @example
12220 * $(document).ready( function() {
12221 * $('#example').dataTable( {
12222 * "scrollX": "100%",
12223 * "scrollXInner": "110%"
12224 * } );
12225 * } );
12226 */
12227 "sScrollXInner": "",
12228
12229
12230 /**
12231 * Enable vertical scrolling. Vertical scrolling will constrain the DataTable
12232 * to the given height, and enable scrolling for any data which overflows the
12233 * current viewport. This can be used as an alternative to paging to display
12234 * a lot of data in a small area (although paging and scrolling can both be
12235 * enabled at the same time). This property can be any CSS unit, or a number
12236 * (in which case it will be treated as a pixel measurement).
12237 * @type string
12238 * @default <i>blank string - i.e. disabled</i>
12239 *
12240 * @dtopt Features
12241 * @name DataTable.defaults.scrollY
12242 *
12243 * @example
12244 * $(document).ready( function() {
12245 * $('#example').dataTable( {
12246 * "scrollY": "200px",
12247 * "paginate": false
12248 * } );
12249 * } );
12250 */
12251 "sScrollY": "",
12252
12253
12254 /**
12255 * __Deprecated__ The functionality provided by this parameter has now been
12256 * superseded by that provided through `ajax`, which should be used instead.
12257 *
12258 * Set the HTTP method that is used to make the Ajax call for server-side
12259 * processing or Ajax sourced data.
12260 * @type string
12261 * @default GET
12262 *
12263 * @dtopt Options
12264 * @dtopt Server-side
12265 * @name DataTable.defaults.serverMethod
12266 *
12267 * @deprecated 1.10. Please use `ajax` for this functionality now.
12268 */
12269 "sServerMethod": "GET",
12270
12271
12272 /**
12273 * DataTables makes use of renderers when displaying HTML elements for
12274 * a table. These renderers can be added or modified by plug-ins to
12275 * generate suitable mark-up for a site. For example the Bootstrap
12276 * integration plug-in for DataTables uses a paging button renderer to
12277 * display pagination buttons in the mark-up required by Bootstrap.
12278 *
12279 * For further information about the renderers available see
12280 * DataTable.ext.renderer
12281 * @type string|object
12282 * @default null
12283 *
12284 * @name DataTable.defaults.renderer
12285 *
12286 */
12287 "renderer": null,
12288
12289
12290 /**
12291 * Set the data property name that DataTables should use to get a row's id
12292 * to set as the `id` property in the node.
12293 * @type string
12294 * @default DT_RowId
12295 *
12296 * @name DataTable.defaults.rowId
12297 */
12298 "rowId": "DT_RowId"
12299 };
12300
12301 _fnHungarianMap( DataTable.defaults );
12302
12303
12304
12305 /*
12306 * Developer note - See note in model.defaults.js about the use of Hungarian
12307 * notation and camel case.
12308 */
12309
12310 /**
12311 * Column options that can be given to DataTables at initialisation time.
12312 * @namespace
12313 */
12314 DataTable.defaults.column = {
12315 /**
12316 * Define which column(s) an order will occur on for this column. This
12317 * allows a column's ordering to take multiple columns into account when
12318 * doing a sort or use the data from a different column. For example first
12319 * name / last name columns make sense to do a multi-column sort over the
12320 * two columns.
12321 * @type array|int
12322 * @default null <i>Takes the value of the column index automatically</i>
12323 *
12324 * @name DataTable.defaults.column.orderData
12325 * @dtopt Columns
12326 *
12327 * @example
12328 * // Using `columnDefs`
12329 * $(document).ready( function() {
12330 * $('#example').dataTable( {
12331 * "columnDefs": [
12332 * { "orderData": [ 0, 1 ], "targets": [ 0 ] },
12333 * { "orderData": [ 1, 0 ], "targets": [ 1 ] },
12334 * { "orderData": 2, "targets": [ 2 ] }
12335 * ]
12336 * } );
12337 * } );
12338 *
12339 * @example
12340 * // Using `columns`
12341 * $(document).ready( function() {
12342 * $('#example').dataTable( {
12343 * "columns": [
12344 * { "orderData": [ 0, 1 ] },
12345 * { "orderData": [ 1, 0 ] },
12346 * { "orderData": 2 },
12347 * null,
12348 * null
12349 * ]
12350 * } );
12351 * } );
12352 */
12353 "aDataSort": null,
12354 "iDataSort": -1,
12355
12356
12357 /**
12358 * You can control the default ordering direction, and even alter the
12359 * behaviour of the sort handler (i.e. only allow ascending ordering etc)
12360 * using this parameter.
12361 * @type array
12362 * @default [ 'asc', 'desc' ]
12363 *
12364 * @name DataTable.defaults.column.orderSequence
12365 * @dtopt Columns
12366 *
12367 * @example
12368 * // Using `columnDefs`
12369 * $(document).ready( function() {
12370 * $('#example').dataTable( {
12371 * "columnDefs": [
12372 * { "orderSequence": [ "asc" ], "targets": [ 1 ] },
12373 * { "orderSequence": [ "desc", "asc", "asc" ], "targets": [ 2 ] },
12374 * { "orderSequence": [ "desc" ], "targets": [ 3 ] }
12375 * ]
12376 * } );
12377 * } );
12378 *
12379 * @example
12380 * // Using `columns`
12381 * $(document).ready( function() {
12382 * $('#example').dataTable( {
12383 * "columns": [
12384 * null,
12385 * { "orderSequence": [ "asc" ] },
12386 * { "orderSequence": [ "desc", "asc", "asc" ] },
12387 * { "orderSequence": [ "desc" ] },
12388 * null
12389 * ]
12390 * } );
12391 * } );
12392 */
12393 "asSorting": [ 'asc', 'desc' ],
12394
12395
12396 /**
12397 * Enable or disable filtering on the data in this column.
12398 * @type boolean
12399 * @default true
12400 *
12401 * @name DataTable.defaults.column.searchable
12402 * @dtopt Columns
12403 *
12404 * @example
12405 * // Using `columnDefs`
12406 * $(document).ready( function() {
12407 * $('#example').dataTable( {
12408 * "columnDefs": [
12409 * { "searchable": false, "targets": [ 0 ] }
12410 * ] } );
12411 * } );
12412 *
12413 * @example
12414 * // Using `columns`
12415 * $(document).ready( function() {
12416 * $('#example').dataTable( {
12417 * "columns": [
12418 * { "searchable": false },
12419 * null,
12420 * null,
12421 * null,
12422 * null
12423 * ] } );
12424 * } );
12425 */
12426 "bSearchable": true,
12427
12428
12429 /**
12430 * Enable or disable ordering on this column.
12431 * @type boolean
12432 * @default true
12433 *
12434 * @name DataTable.defaults.column.orderable
12435 * @dtopt Columns
12436 *
12437 * @example
12438 * // Using `columnDefs`
12439 * $(document).ready( function() {
12440 * $('#example').dataTable( {
12441 * "columnDefs": [
12442 * { "orderable": false, "targets": [ 0 ] }
12443 * ] } );
12444 * } );
12445 *
12446 * @example
12447 * // Using `columns`
12448 * $(document).ready( function() {
12449 * $('#example').dataTable( {
12450 * "columns": [
12451 * { "orderable": false },
12452 * null,
12453 * null,
12454 * null,
12455 * null
12456 * ] } );
12457 * } );
12458 */
12459 "bSortable": true,
12460
12461
12462 /**
12463 * Enable or disable the display of this column.
12464 * @type boolean
12465 * @default true
12466 *
12467 * @name DataTable.defaults.column.visible
12468 * @dtopt Columns
12469 *
12470 * @example
12471 * // Using `columnDefs`
12472 * $(document).ready( function() {
12473 * $('#example').dataTable( {
12474 * "columnDefs": [
12475 * { "visible": false, "targets": [ 0 ] }
12476 * ] } );
12477 * } );
12478 *
12479 * @example
12480 * // Using `columns`
12481 * $(document).ready( function() {
12482 * $('#example').dataTable( {
12483 * "columns": [
12484 * { "visible": false },
12485 * null,
12486 * null,
12487 * null,
12488 * null
12489 * ] } );
12490 * } );
12491 */
12492 "bVisible": true,
12493
12494
12495 /**
12496 * Developer definable function that is called whenever a cell is created (Ajax source,
12497 * etc) or processed for input (DOM source). This can be used as a compliment to mRender
12498 * allowing you to modify the DOM element (add background colour for example) when the
12499 * element is available.
12500 * @type function
12501 * @param {element} td The TD node that has been created
12502 * @param {*} cellData The Data for the cell
12503 * @param {array|object} rowData The data for the whole row
12504 * @param {int} row The row index for the aoData data store
12505 * @param {int} col The column index for aoColumns
12506 *
12507 * @name DataTable.defaults.column.createdCell
12508 * @dtopt Columns
12509 *
12510 * @example
12511 * $(document).ready( function() {
12512 * $('#example').dataTable( {
12513 * "columnDefs": [ {
12514 * "targets": [3],
12515 * "createdCell": function (td, cellData, rowData, row, col) {
12516 * if ( cellData == "1.7" ) {
12517 * $(td).css('color', 'blue')
12518 * }
12519 * }
12520 * } ]
12521 * });
12522 * } );
12523 */
12524 "fnCreatedCell": null,
12525
12526
12527 /**
12528 * This parameter has been replaced by `data` in DataTables to ensure naming
12529 * consistency. `dataProp` can still be used, as there is backwards
12530 * compatibility in DataTables for this option, but it is strongly
12531 * recommended that you use `data` in preference to `dataProp`.
12532 * @name DataTable.defaults.column.dataProp
12533 */
12534
12535
12536 /**
12537 * This property can be used to read data from any data source property,
12538 * including deeply nested objects / properties. `data` can be given in a
12539 * number of different ways which effect its behaviour:
12540 *
12541 * * `integer` - treated as an array index for the data source. This is the
12542 * default that DataTables uses (incrementally increased for each column).
12543 * * `string` - read an object property from the data source. There are
12544 * three 'special' options that can be used in the string to alter how
12545 * DataTables reads the data from the source object:
12546 * * `.` - Dotted Javascript notation. Just as you use a `.` in
12547 * Javascript to read from nested objects, so to can the options
12548 * specified in `data`. For example: `browser.version` or
12549 * `browser.name`. If your object parameter name contains a period, use
12550 * `\\` to escape it - i.e. `first\\.name`.
12551 * * `[]` - Array notation. DataTables can automatically combine data
12552 * from and array source, joining the data with the characters provided
12553 * between the two brackets. For example: `name[, ]` would provide a
12554 * comma-space separated list from the source array. If no characters
12555 * are provided between the brackets, the original array source is
12556 * returned.
12557 * * `()` - Function notation. Adding `()` to the end of a parameter will
12558 * execute a function of the name given. For example: `browser()` for a
12559 * simple function on the data source, `browser.version()` for a
12560 * function in a nested property or even `browser().version` to get an
12561 * object property if the function called returns an object. Note that
12562 * function notation is recommended for use in `render` rather than
12563 * `data` as it is much simpler to use as a renderer.
12564 * * `null` - use the original data source for the row rather than plucking
12565 * data directly from it. This action has effects on two other
12566 * initialisation options:
12567 * * `defaultContent` - When null is given as the `data` option and
12568 * `defaultContent` is specified for the column, the value defined by
12569 * `defaultContent` will be used for the cell.
12570 * * `render` - When null is used for the `data` option and the `render`
12571 * option is specified for the column, the whole data source for the
12572 * row is used for the renderer.
12573 * * `function` - the function given will be executed whenever DataTables
12574 * needs to set or get the data for a cell in the column. The function
12575 * takes three parameters:
12576 * * Parameters:
12577 * * `{array|object}` The data source for the row
12578 * * `{string}` The type call data requested - this will be 'set' when
12579 * setting data or 'filter', 'display', 'type', 'sort' or undefined
12580 * when gathering data. Note that when `undefined` is given for the
12581 * type DataTables expects to get the raw data for the object back<
12582 * * `{*}` Data to set when the second parameter is 'set'.
12583 * * Return:
12584 * * The return value from the function is not required when 'set' is
12585 * the type of call, but otherwise the return is what will be used
12586 * for the data requested.
12587 *
12588 * Note that `data` is a getter and setter option. If you just require
12589 * formatting of data for output, you will likely want to use `render` which
12590 * is simply a getter and thus simpler to use.
12591 *
12592 * Note that prior to DataTables 1.9.2 `data` was called `mDataProp`. The
12593 * name change reflects the flexibility of this property and is consistent
12594 * with the naming of mRender. If 'mDataProp' is given, then it will still
12595 * be used by DataTables, as it automatically maps the old name to the new
12596 * if required.
12597 *
12598 * @type string|int|function|null
12599 * @default null <i>Use automatically calculated column index</i>
12600 *
12601 * @name DataTable.defaults.column.data
12602 * @dtopt Columns
12603 *
12604 * @example
12605 * // Read table data from objects
12606 * // JSON structure for each row:
12607 * // {
12608 * // "engine": {value},
12609 * // "browser": {value},
12610 * // "platform": {value},
12611 * // "version": {value},
12612 * // "grade": {value}
12613 * // }
12614 * $(document).ready( function() {
12615 * $('#example').dataTable( {
12616 * "ajaxSource": "sources/objects.txt",
12617 * "columns": [
12618 * { "data": "engine" },
12619 * { "data": "browser" },
12620 * { "data": "platform" },
12621 * { "data": "version" },
12622 * { "data": "grade" }
12623 * ]
12624 * } );
12625 * } );
12626 *
12627 * @example
12628 * // Read information from deeply nested objects
12629 * // JSON structure for each row:
12630 * // {
12631 * // "engine": {value},
12632 * // "browser": {value},
12633 * // "platform": {
12634 * // "inner": {value}
12635 * // },
12636 * // "details": [
12637 * // {value}, {value}
12638 * // ]
12639 * // }
12640 * $(document).ready( function() {
12641 * $('#example').dataTable( {
12642 * "ajaxSource": "sources/deep.txt",
12643 * "columns": [
12644 * { "data": "engine" },
12645 * { "data": "browser" },
12646 * { "data": "platform.inner" },
12647 * { "data": "details.0" },
12648 * { "data": "details.1" }
12649 * ]
12650 * } );
12651 * } );
12652 *
12653 * @example
12654 * // Using `data` as a function to provide different information for
12655 * // sorting, filtering and display. In this case, currency (price)
12656 * $(document).ready( function() {
12657 * $('#example').dataTable( {
12658 * "columnDefs": [ {
12659 * "targets": [ 0 ],
12660 * "data": function ( source, type, val ) {
12661 * if (type === 'set') {
12662 * source.price = val;
12663 * // Store the computed display and filter values for efficiency
12664 * source.price_display = val=="" ? "" : "$"+numberFormat(val);
12665 * source.price_filter = val=="" ? "" : "$"+numberFormat(val)+" "+val;
12666 * return;
12667 * }
12668 * else if (type === 'display') {
12669 * return source.price_display;
12670 * }
12671 * else if (type === 'filter') {
12672 * return source.price_filter;
12673 * }
12674 * // 'sort', 'type' and undefined all just use the integer
12675 * return source.price;
12676 * }
12677 * } ]
12678 * } );
12679 * } );
12680 *
12681 * @example
12682 * // Using default content
12683 * $(document).ready( function() {
12684 * $('#example').dataTable( {
12685 * "columnDefs": [ {
12686 * "targets": [ 0 ],
12687 * "data": null,
12688 * "defaultContent": "Click to edit"
12689 * } ]
12690 * } );
12691 * } );
12692 *
12693 * @example
12694 * // Using array notation - outputting a list from an array
12695 * $(document).ready( function() {
12696 * $('#example').dataTable( {
12697 * "columnDefs": [ {
12698 * "targets": [ 0 ],
12699 * "data": "name[, ]"
12700 * } ]
12701 * } );
12702 * } );
12703 *
12704 */
12705 "mData": null,
12706
12707
12708 /**
12709 * This property is the rendering partner to `data` and it is suggested that
12710 * when you want to manipulate data for display (including filtering,
12711 * sorting etc) without altering the underlying data for the table, use this
12712 * property. `render` can be considered to be the the read only companion to
12713 * `data` which is read / write (then as such more complex). Like `data`
12714 * this option can be given in a number of different ways to effect its
12715 * behaviour:
12716 *
12717 * * `integer` - treated as an array index for the data source. This is the
12718 * default that DataTables uses (incrementally increased for each column).
12719 * * `string` - read an object property from the data source. There are
12720 * three 'special' options that can be used in the string to alter how
12721 * DataTables reads the data from the source object:
12722 * * `.` - Dotted Javascript notation. Just as you use a `.` in
12723 * Javascript to read from nested objects, so to can the options
12724 * specified in `data`. For example: `browser.version` or
12725 * `browser.name`. If your object parameter name contains a period, use
12726 * `\\` to escape it - i.e. `first\\.name`.
12727 * * `[]` - Array notation. DataTables can automatically combine data
12728 * from and array source, joining the data with the characters provided
12729 * between the two brackets. For example: `name[, ]` would provide a
12730 * comma-space separated list from the source array. If no characters
12731 * are provided between the brackets, the original array source is
12732 * returned.
12733 * * `()` - Function notation. Adding `()` to the end of a parameter will
12734 * execute a function of the name given. For example: `browser()` for a
12735 * simple function on the data source, `browser.version()` for a
12736 * function in a nested property or even `browser().version` to get an
12737 * object property if the function called returns an object.
12738 * * `object` - use different data for the different data types requested by
12739 * DataTables ('filter', 'display', 'type' or 'sort'). The property names
12740 * of the object is the data type the property refers to and the value can
12741 * defined using an integer, string or function using the same rules as
12742 * `render` normally does. Note that an `_` option _must_ be specified.
12743 * This is the default value to use if you haven't specified a value for
12744 * the data type requested by DataTables.
12745 * * `function` - the function given will be executed whenever DataTables
12746 * needs to set or get the data for a cell in the column. The function
12747 * takes three parameters:
12748 * * Parameters:
12749 * * {array|object} The data source for the row (based on `data`)
12750 * * {string} The type call data requested - this will be 'filter',
12751 * 'display', 'type' or 'sort'.
12752 * * {array|object} The full data source for the row (not based on
12753 * `data`)
12754 * * Return:
12755 * * The return value from the function is what will be used for the
12756 * data requested.
12757 *
12758 * @type string|int|function|object|null
12759 * @default null Use the data source value.
12760 *
12761 * @name DataTable.defaults.column.render
12762 * @dtopt Columns
12763 *
12764 * @example
12765 * // Create a comma separated list from an array of objects
12766 * $(document).ready( function() {
12767 * $('#example').dataTable( {
12768 * "ajaxSource": "sources/deep.txt",
12769 * "columns": [
12770 * { "data": "engine" },
12771 * { "data": "browser" },
12772 * {
12773 * "data": "platform",
12774 * "render": "[, ].name"
12775 * }
12776 * ]
12777 * } );
12778 * } );
12779 *
12780 * @example
12781 * // Execute a function to obtain data
12782 * $(document).ready( function() {
12783 * $('#example').dataTable( {
12784 * "columnDefs": [ {
12785 * "targets": [ 0 ],
12786 * "data": null, // Use the full data source object for the renderer's source
12787 * "render": "browserName()"
12788 * } ]
12789 * } );
12790 * } );
12791 *
12792 * @example
12793 * // As an object, extracting different data for the different types
12794 * // This would be used with a data source such as:
12795 * // { "phone": 5552368, "phone_filter": "5552368 555-2368", "phone_display": "555-2368" }
12796 * // Here the `phone` integer is used for sorting and type detection, while `phone_filter`
12797 * // (which has both forms) is used for filtering for if a user inputs either format, while
12798 * // the formatted phone number is the one that is shown in the table.
12799 * $(document).ready( function() {
12800 * $('#example').dataTable( {
12801 * "columnDefs": [ {
12802 * "targets": [ 0 ],
12803 * "data": null, // Use the full data source object for the renderer's source
12804 * "render": {
12805 * "_": "phone",
12806 * "filter": "phone_filter",
12807 * "display": "phone_display"
12808 * }
12809 * } ]
12810 * } );
12811 * } );
12812 *
12813 * @example
12814 * // Use as a function to create a link from the data source
12815 * $(document).ready( function() {
12816 * $('#example').dataTable( {
12817 * "columnDefs": [ {
12818 * "targets": [ 0 ],
12819 * "data": "download_link",
12820 * "render": function ( data, type, full ) {
12821 * return '<a href="'+data+'">Download</a>';
12822 * }
12823 * } ]
12824 * } );
12825 * } );
12826 */
12827 "mRender": null,
12828
12829
12830 /**
12831 * Change the cell type created for the column - either TD cells or TH cells. This
12832 * can be useful as TH cells have semantic meaning in the table body, allowing them
12833 * to act as a header for a row (you may wish to add scope='row' to the TH elements).
12834 * @type string
12835 * @default td
12836 *
12837 * @name DataTable.defaults.column.cellType
12838 * @dtopt Columns
12839 *
12840 * @example
12841 * // Make the first column use TH cells
12842 * $(document).ready( function() {
12843 * $('#example').dataTable( {
12844 * "columnDefs": [ {
12845 * "targets": [ 0 ],
12846 * "cellType": "th"
12847 * } ]
12848 * } );
12849 * } );
12850 */
12851 "sCellType": "td",
12852
12853
12854 /**
12855 * Class to give to each cell in this column.
12856 * @type string
12857 * @default <i>Empty string</i>
12858 *
12859 * @name DataTable.defaults.column.class
12860 * @dtopt Columns
12861 *
12862 * @example
12863 * // Using `columnDefs`
12864 * $(document).ready( function() {
12865 * $('#example').dataTable( {
12866 * "columnDefs": [
12867 * { "class": "my_class", "targets": [ 0 ] }
12868 * ]
12869 * } );
12870 * } );
12871 *
12872 * @example
12873 * // Using `columns`
12874 * $(document).ready( function() {
12875 * $('#example').dataTable( {
12876 * "columns": [
12877 * { "class": "my_class" },
12878 * null,
12879 * null,
12880 * null,
12881 * null
12882 * ]
12883 * } );
12884 * } );
12885 */
12886 "sClass": "",
12887
12888 /**
12889 * When DataTables calculates the column widths to assign to each column,
12890 * it finds the longest string in each column and then constructs a
12891 * temporary table and reads the widths from that. The problem with this
12892 * is that "mmm" is much wider then "iiii", but the latter is a longer
12893 * string - thus the calculation can go wrong (doing it properly and putting
12894 * it into an DOM object and measuring that is horribly(!) slow). Thus as
12895 * a "work around" we provide this option. It will append its value to the
12896 * text that is found to be the longest string for the column - i.e. padding.
12897 * Generally you shouldn't need this!
12898 * @type string
12899 * @default <i>Empty string<i>
12900 *
12901 * @name DataTable.defaults.column.contentPadding
12902 * @dtopt Columns
12903 *
12904 * @example
12905 * // Using `columns`
12906 * $(document).ready( function() {
12907 * $('#example').dataTable( {
12908 * "columns": [
12909 * null,
12910 * null,
12911 * null,
12912 * {
12913 * "contentPadding": "mmm"
12914 * }
12915 * ]
12916 * } );
12917 * } );
12918 */
12919 "sContentPadding": "",
12920
12921
12922 /**
12923 * Allows a default value to be given for a column's data, and will be used
12924 * whenever a null data source is encountered (this can be because `data`
12925 * is set to null, or because the data source itself is null).
12926 * @type string
12927 * @default null
12928 *
12929 * @name DataTable.defaults.column.defaultContent
12930 * @dtopt Columns
12931 *
12932 * @example
12933 * // Using `columnDefs`
12934 * $(document).ready( function() {
12935 * $('#example').dataTable( {
12936 * "columnDefs": [
12937 * {
12938 * "data": null,
12939 * "defaultContent": "Edit",
12940 * "targets": [ -1 ]
12941 * }
12942 * ]
12943 * } );
12944 * } );
12945 *
12946 * @example
12947 * // Using `columns`
12948 * $(document).ready( function() {
12949 * $('#example').dataTable( {
12950 * "columns": [
12951 * null,
12952 * null,
12953 * null,
12954 * {
12955 * "data": null,
12956 * "defaultContent": "Edit"
12957 * }
12958 * ]
12959 * } );
12960 * } );
12961 */
12962 "sDefaultContent": null,
12963
12964
12965 /**
12966 * This parameter is only used in DataTables' server-side processing. It can
12967 * be exceptionally useful to know what columns are being displayed on the
12968 * client side, and to map these to database fields. When defined, the names
12969 * also allow DataTables to reorder information from the server if it comes
12970 * back in an unexpected order (i.e. if you switch your columns around on the
12971 * client-side, your server-side code does not also need updating).
12972 * @type string
12973 * @default <i>Empty string</i>
12974 *
12975 * @name DataTable.defaults.column.name
12976 * @dtopt Columns
12977 *
12978 * @example
12979 * // Using `columnDefs`
12980 * $(document).ready( function() {
12981 * $('#example').dataTable( {
12982 * "columnDefs": [
12983 * { "name": "engine", "targets": [ 0 ] },
12984 * { "name": "browser", "targets": [ 1 ] },
12985 * { "name": "platform", "targets": [ 2 ] },
12986 * { "name": "version", "targets": [ 3 ] },
12987 * { "name": "grade", "targets": [ 4 ] }
12988 * ]
12989 * } );
12990 * } );
12991 *
12992 * @example
12993 * // Using `columns`
12994 * $(document).ready( function() {
12995 * $('#example').dataTable( {
12996 * "columns": [
12997 * { "name": "engine" },
12998 * { "name": "browser" },
12999 * { "name": "platform" },
13000 * { "name": "version" },
13001 * { "name": "grade" }
13002 * ]
13003 * } );
13004 * } );
13005 */
13006 "sName": "",
13007
13008
13009 /**
13010 * Defines a data source type for the ordering which can be used to read
13011 * real-time information from the table (updating the internally cached
13012 * version) prior to ordering. This allows ordering to occur on user
13013 * editable elements such as form inputs.
13014 * @type string
13015 * @default std
13016 *
13017 * @name DataTable.defaults.column.orderDataType
13018 * @dtopt Columns
13019 *
13020 * @example
13021 * // Using `columnDefs`
13022 * $(document).ready( function() {
13023 * $('#example').dataTable( {
13024 * "columnDefs": [
13025 * { "orderDataType": "dom-text", "targets": [ 2, 3 ] },
13026 * { "type": "numeric", "targets": [ 3 ] },
13027 * { "orderDataType": "dom-select", "targets": [ 4 ] },
13028 * { "orderDataType": "dom-checkbox", "targets": [ 5 ] }
13029 * ]
13030 * } );
13031 * } );
13032 *
13033 * @example
13034 * // Using `columns`
13035 * $(document).ready( function() {
13036 * $('#example').dataTable( {
13037 * "columns": [
13038 * null,
13039 * null,
13040 * { "orderDataType": "dom-text" },
13041 * { "orderDataType": "dom-text", "type": "numeric" },
13042 * { "orderDataType": "dom-select" },
13043 * { "orderDataType": "dom-checkbox" }
13044 * ]
13045 * } );
13046 * } );
13047 */
13048 "sSortDataType": "std",
13049
13050
13051 /**
13052 * The title of this column.
13053 * @type string
13054 * @default null <i>Derived from the 'TH' value for this column in the
13055 * original HTML table.</i>
13056 *
13057 * @name DataTable.defaults.column.title
13058 * @dtopt Columns
13059 *
13060 * @example
13061 * // Using `columnDefs`
13062 * $(document).ready( function() {
13063 * $('#example').dataTable( {
13064 * "columnDefs": [
13065 * { "title": "My column title", "targets": [ 0 ] }
13066 * ]
13067 * } );
13068 * } );
13069 *
13070 * @example
13071 * // Using `columns`
13072 * $(document).ready( function() {
13073 * $('#example').dataTable( {
13074 * "columns": [
13075 * { "title": "My column title" },
13076 * null,
13077 * null,
13078 * null,
13079 * null
13080 * ]
13081 * } );
13082 * } );
13083 */
13084 "sTitle": null,
13085
13086
13087 /**
13088 * The type allows you to specify how the data for this column will be
13089 * ordered. Four types (string, numeric, date and html (which will strip
13090 * HTML tags before ordering)) are currently available. Note that only date
13091 * formats understood by Javascript's Date() object will be accepted as type
13092 * date. For example: "Mar 26, 2008 5:03 PM". May take the values: 'string',
13093 * 'numeric', 'date' or 'html' (by default). Further types can be adding
13094 * through plug-ins.
13095 * @type string
13096 * @default null <i>Auto-detected from raw data</i>
13097 *
13098 * @name DataTable.defaults.column.type
13099 * @dtopt Columns
13100 *
13101 * @example
13102 * // Using `columnDefs`
13103 * $(document).ready( function() {
13104 * $('#example').dataTable( {
13105 * "columnDefs": [
13106 * { "type": "html", "targets": [ 0 ] }
13107 * ]
13108 * } );
13109 * } );
13110 *
13111 * @example
13112 * // Using `columns`
13113 * $(document).ready( function() {
13114 * $('#example').dataTable( {
13115 * "columns": [
13116 * { "type": "html" },
13117 * null,
13118 * null,
13119 * null,
13120 * null
13121 * ]
13122 * } );
13123 * } );
13124 */
13125 "sType": null,
13126
13127
13128 /**
13129 * Defining the width of the column, this parameter may take any CSS value
13130 * (3em, 20px etc). DataTables applies 'smart' widths to columns which have not
13131 * been given a specific width through this interface ensuring that the table
13132 * remains readable.
13133 * @type string
13134 * @default null <i>Automatic</i>
13135 *
13136 * @name DataTable.defaults.column.width
13137 * @dtopt Columns
13138 *
13139 * @example
13140 * // Using `columnDefs`
13141 * $(document).ready( function() {
13142 * $('#example').dataTable( {
13143 * "columnDefs": [
13144 * { "width": "20%", "targets": [ 0 ] }
13145 * ]
13146 * } );
13147 * } );
13148 *
13149 * @example
13150 * // Using `columns`
13151 * $(document).ready( function() {
13152 * $('#example').dataTable( {
13153 * "columns": [
13154 * { "width": "20%" },
13155 * null,
13156 * null,
13157 * null,
13158 * null
13159 * ]
13160 * } );
13161 * } );
13162 */
13163 "sWidth": null
13164 };
13165
13166 _fnHungarianMap( DataTable.defaults.column );
13167
13168
13169
13170 /**
13171 * DataTables settings object - this holds all the information needed for a
13172 * given table, including configuration, data and current application of the
13173 * table options. DataTables does not have a single instance for each DataTable
13174 * with the settings attached to that instance, but rather instances of the
13175 * DataTable "class" are created on-the-fly as needed (typically by a
13176 * $().dataTable() call) and the settings object is then applied to that
13177 * instance.
13178 *
13179 * Note that this object is related to {@link DataTable.defaults} but this
13180 * one is the internal data store for DataTables's cache of columns. It should
13181 * NOT be manipulated outside of DataTables. Any configuration should be done
13182 * through the initialisation options.
13183 * @namespace
13184 * @todo Really should attach the settings object to individual instances so we
13185 * don't need to create new instances on each $().dataTable() call (if the
13186 * table already exists). It would also save passing oSettings around and
13187 * into every single function. However, this is a very significant
13188 * architecture change for DataTables and will almost certainly break
13189 * backwards compatibility with older installations. This is something that
13190 * will be done in 2.0.
13191 */
13192 DataTable.models.oSettings = {
13193 /**
13194 * Primary features of DataTables and their enablement state.
13195 * @namespace
13196 */
13197 "oFeatures": {
13198
13199 /**
13200 * Flag to say if DataTables should automatically try to calculate the
13201 * optimum table and columns widths (true) or not (false).
13202 * Note that this parameter will be set by the initialisation routine. To
13203 * set a default use {@link DataTable.defaults}.
13204 * @type boolean
13205 */
13206 "bAutoWidth": null,
13207
13208 /**
13209 * Delay the creation of TR and TD elements until they are actually
13210 * needed by a driven page draw. This can give a significant speed
13211 * increase for Ajax source and Javascript source data, but makes no
13212 * difference at all for DOM and server-side processing tables.
13213 * Note that this parameter will be set by the initialisation routine. To
13214 * set a default use {@link DataTable.defaults}.
13215 * @type boolean
13216 */
13217 "bDeferRender": null,
13218
13219 /**
13220 * Enable filtering on the table or not. Note that if this is disabled
13221 * then there is no filtering at all on the table, including fnFilter.
13222 * To just remove the filtering input use sDom and remove the 'f' option.
13223 * Note that this parameter will be set by the initialisation routine. To
13224 * set a default use {@link DataTable.defaults}.
13225 * @type boolean
13226 */
13227 "bFilter": null,
13228
13229 /**
13230 * Table information element (the 'Showing x of y records' div) enable
13231 * flag.
13232 * Note that this parameter will be set by the initialisation routine. To
13233 * set a default use {@link DataTable.defaults}.
13234 * @type boolean
13235 */
13236 "bInfo": null,
13237
13238 /**
13239 * Present a user control allowing the end user to change the page size
13240 * when pagination is enabled.
13241 * Note that this parameter will be set by the initialisation routine. To
13242 * set a default use {@link DataTable.defaults}.
13243 * @type boolean
13244 */
13245 "bLengthChange": null,
13246
13247 /**
13248 * Pagination enabled or not. Note that if this is disabled then length
13249 * changing must also be disabled.
13250 * Note that this parameter will be set by the initialisation routine. To
13251 * set a default use {@link DataTable.defaults}.
13252 * @type boolean
13253 */
13254 "bPaginate": null,
13255
13256 /**
13257 * Processing indicator enable flag whenever DataTables is enacting a
13258 * user request - typically an Ajax request for server-side processing.
13259 * Note that this parameter will be set by the initialisation routine. To
13260 * set a default use {@link DataTable.defaults}.
13261 * @type boolean
13262 */
13263 "bProcessing": null,
13264
13265 /**
13266 * Server-side processing enabled flag - when enabled DataTables will
13267 * get all data from the server for every draw - there is no filtering,
13268 * sorting or paging done on the client-side.
13269 * Note that this parameter will be set by the initialisation routine. To
13270 * set a default use {@link DataTable.defaults}.
13271 * @type boolean
13272 */
13273 "bServerSide": null,
13274
13275 /**
13276 * Sorting enablement flag.
13277 * Note that this parameter will be set by the initialisation routine. To
13278 * set a default use {@link DataTable.defaults}.
13279 * @type boolean
13280 */
13281 "bSort": null,
13282
13283 /**
13284 * Multi-column sorting
13285 * Note that this parameter will be set by the initialisation routine. To
13286 * set a default use {@link DataTable.defaults}.
13287 * @type boolean
13288 */
13289 "bSortMulti": null,
13290
13291 /**
13292 * Apply a class to the columns which are being sorted to provide a
13293 * visual highlight or not. This can slow things down when enabled since
13294 * there is a lot of DOM interaction.
13295 * Note that this parameter will be set by the initialisation routine. To
13296 * set a default use {@link DataTable.defaults}.
13297 * @type boolean
13298 */
13299 "bSortClasses": null,
13300
13301 /**
13302 * State saving enablement flag.
13303 * Note that this parameter will be set by the initialisation routine. To
13304 * set a default use {@link DataTable.defaults}.
13305 * @type boolean
13306 */
13307 "bStateSave": null
13308 },
13309
13310
13311 /**
13312 * Scrolling settings for a table.
13313 * @namespace
13314 */
13315 "oScroll": {
13316 /**
13317 * When the table is shorter in height than sScrollY, collapse the
13318 * table container down to the height of the table (when true).
13319 * Note that this parameter will be set by the initialisation routine. To
13320 * set a default use {@link DataTable.defaults}.
13321 * @type boolean
13322 */
13323 "bCollapse": null,
13324
13325 /**
13326 * Width of the scrollbar for the web-browser's platform. Calculated
13327 * during table initialisation.
13328 * @type int
13329 * @default 0
13330 */
13331 "iBarWidth": 0,
13332
13333 /**
13334 * Viewport width for horizontal scrolling. Horizontal scrolling is
13335 * disabled if an empty string.
13336 * Note that this parameter will be set by the initialisation routine. To
13337 * set a default use {@link DataTable.defaults}.
13338 * @type string
13339 */
13340 "sX": null,
13341
13342 /**
13343 * Width to expand the table to when using x-scrolling. Typically you
13344 * should not need to use this.
13345 * Note that this parameter will be set by the initialisation routine. To
13346 * set a default use {@link DataTable.defaults}.
13347 * @type string
13348 * @deprecated
13349 */
13350 "sXInner": null,
13351
13352 /**
13353 * Viewport height for vertical scrolling. Vertical scrolling is disabled
13354 * if an empty string.
13355 * Note that this parameter will be set by the initialisation routine. To
13356 * set a default use {@link DataTable.defaults}.
13357 * @type string
13358 */
13359 "sY": null
13360 },
13361
13362 /**
13363 * Language information for the table.
13364 * @namespace
13365 * @extends DataTable.defaults.oLanguage
13366 */
13367 "oLanguage": {
13368 /**
13369 * Information callback function. See
13370 * {@link DataTable.defaults.fnInfoCallback}
13371 * @type function
13372 * @default null
13373 */
13374 "fnInfoCallback": null
13375 },
13376
13377 /**
13378 * Browser support parameters
13379 * @namespace
13380 */
13381 "oBrowser": {
13382 /**
13383 * Indicate if the browser incorrectly calculates width:100% inside a
13384 * scrolling element (IE6/7)
13385 * @type boolean
13386 * @default false
13387 */
13388 "bScrollOversize": false,
13389
13390 /**
13391 * Determine if the vertical scrollbar is on the right or left of the
13392 * scrolling container - needed for rtl language layout, although not
13393 * all browsers move the scrollbar (Safari).
13394 * @type boolean
13395 * @default false
13396 */
13397 "bScrollbarLeft": false,
13398
13399 /**
13400 * Flag for if `getBoundingClientRect` is fully supported or not
13401 * @type boolean
13402 * @default false
13403 */
13404 "bBounding": false,
13405
13406 /**
13407 * Browser scrollbar width
13408 * @type integer
13409 * @default 0
13410 */
13411 "barWidth": 0
13412 },
13413
13414
13415 "ajax": null,
13416
13417
13418 /**
13419 * Array referencing the nodes which are used for the features. The
13420 * parameters of this object match what is allowed by sDom - i.e.
13421 * <ul>
13422 * <li>'l' - Length changing</li>
13423 * <li>'f' - Filtering input</li>
13424 * <li>'t' - The table!</li>
13425 * <li>'i' - Information</li>
13426 * <li>'p' - Pagination</li>
13427 * <li>'r' - pRocessing</li>
13428 * </ul>
13429 * @type array
13430 * @default []
13431 */
13432 "aanFeatures": [],
13433
13434 /**
13435 * Store data information - see {@link DataTable.models.oRow} for detailed
13436 * information.
13437 * @type array
13438 * @default []
13439 */
13440 "aoData": [],
13441
13442 /**
13443 * Array of indexes which are in the current display (after filtering etc)
13444 * @type array
13445 * @default []
13446 */
13447 "aiDisplay": [],
13448
13449 /**
13450 * Array of indexes for display - no filtering
13451 * @type array
13452 * @default []
13453 */
13454 "aiDisplayMaster": [],
13455
13456 /**
13457 * Map of row ids to data indexes
13458 * @type object
13459 * @default {}
13460 */
13461 "aIds": {},
13462
13463 /**
13464 * Store information about each column that is in use
13465 * @type array
13466 * @default []
13467 */
13468 "aoColumns": [],
13469
13470 /**
13471 * Store information about the table's header
13472 * @type array
13473 * @default []
13474 */
13475 "aoHeader": [],
13476
13477 /**
13478 * Store information about the table's footer
13479 * @type array
13480 * @default []
13481 */
13482 "aoFooter": [],
13483
13484 /**
13485 * Store the applied global search information in case we want to force a
13486 * research or compare the old search to a new one.
13487 * Note that this parameter will be set by the initialisation routine. To
13488 * set a default use {@link DataTable.defaults}.
13489 * @namespace
13490 * @extends DataTable.models.oSearch
13491 */
13492 "oPreviousSearch": {},
13493
13494 /**
13495 * Store the applied search for each column - see
13496 * {@link DataTable.models.oSearch} for the format that is used for the
13497 * filtering information for each column.
13498 * @type array
13499 * @default []
13500 */
13501 "aoPreSearchCols": [],
13502
13503 /**
13504 * Sorting that is applied to the table. Note that the inner arrays are
13505 * used in the following manner:
13506 * <ul>
13507 * <li>Index 0 - column number</li>
13508 * <li>Index 1 - current sorting direction</li>
13509 * </ul>
13510 * Note that this parameter will be set by the initialisation routine. To
13511 * set a default use {@link DataTable.defaults}.
13512 * @type array
13513 * @todo These inner arrays should really be objects
13514 */
13515 "aaSorting": null,
13516
13517 /**
13518 * Sorting that is always applied to the table (i.e. prefixed in front of
13519 * aaSorting).
13520 * Note that this parameter will be set by the initialisation routine. To
13521 * set a default use {@link DataTable.defaults}.
13522 * @type array
13523 * @default []
13524 */
13525 "aaSortingFixed": [],
13526
13527 /**
13528 * Classes to use for the striping of a table.
13529 * Note that this parameter will be set by the initialisation routine. To
13530 * set a default use {@link DataTable.defaults}.
13531 * @type array
13532 * @default []
13533 */
13534 "asStripeClasses": null,
13535
13536 /**
13537 * If restoring a table - we should restore its striping classes as well
13538 * @type array
13539 * @default []
13540 */
13541 "asDestroyStripes": [],
13542
13543 /**
13544 * If restoring a table - we should restore its width
13545 * @type int
13546 * @default 0
13547 */
13548 "sDestroyWidth": 0,
13549
13550 /**
13551 * Callback functions array for every time a row is inserted (i.e. on a draw).
13552 * @type array
13553 * @default []
13554 */
13555 "aoRowCallback": [],
13556
13557 /**
13558 * Callback functions for the header on each draw.
13559 * @type array
13560 * @default []
13561 */
13562 "aoHeaderCallback": [],
13563
13564 /**
13565 * Callback function for the footer on each draw.
13566 * @type array
13567 * @default []
13568 */
13569 "aoFooterCallback": [],
13570
13571 /**
13572 * Array of callback functions for draw callback functions
13573 * @type array
13574 * @default []
13575 */
13576 "aoDrawCallback": [],
13577
13578 /**
13579 * Array of callback functions for row created function
13580 * @type array
13581 * @default []
13582 */
13583 "aoRowCreatedCallback": [],
13584
13585 /**
13586 * Callback functions for just before the table is redrawn. A return of
13587 * false will be used to cancel the draw.
13588 * @type array
13589 * @default []
13590 */
13591 "aoPreDrawCallback": [],
13592
13593 /**
13594 * Callback functions for when the table has been initialised.
13595 * @type array
13596 * @default []
13597 */
13598 "aoInitComplete": [],
13599
13600
13601 /**
13602 * Callbacks for modifying the settings to be stored for state saving, prior to
13603 * saving state.
13604 * @type array
13605 * @default []
13606 */
13607 "aoStateSaveParams": [],
13608
13609 /**
13610 * Callbacks for modifying the settings that have been stored for state saving
13611 * prior to using the stored values to restore the state.
13612 * @type array
13613 * @default []
13614 */
13615 "aoStateLoadParams": [],
13616
13617 /**
13618 * Callbacks for operating on the settings object once the saved state has been
13619 * loaded
13620 * @type array
13621 * @default []
13622 */
13623 "aoStateLoaded": [],
13624
13625 /**
13626 * Cache the table ID for quick access
13627 * @type string
13628 * @default <i>Empty string</i>
13629 */
13630 "sTableId": "",
13631
13632 /**
13633 * The TABLE node for the main table
13634 * @type node
13635 * @default null
13636 */
13637 "nTable": null,
13638
13639 /**
13640 * Permanent ref to the thead element
13641 * @type node
13642 * @default null
13643 */
13644 "nTHead": null,
13645
13646 /**
13647 * Permanent ref to the tfoot element - if it exists
13648 * @type node
13649 * @default null
13650 */
13651 "nTFoot": null,
13652
13653 /**
13654 * Permanent ref to the tbody element
13655 * @type node
13656 * @default null
13657 */
13658 "nTBody": null,
13659
13660 /**
13661 * Cache the wrapper node (contains all DataTables controlled elements)
13662 * @type node
13663 * @default null
13664 */
13665 "nTableWrapper": null,
13666
13667 /**
13668 * Indicate if when using server-side processing the loading of data
13669 * should be deferred until the second draw.
13670 * Note that this parameter will be set by the initialisation routine. To
13671 * set a default use {@link DataTable.defaults}.
13672 * @type boolean
13673 * @default false
13674 */
13675 "bDeferLoading": false,
13676
13677 /**
13678 * Indicate if all required information has been read in
13679 * @type boolean
13680 * @default false
13681 */
13682 "bInitialised": false,
13683
13684 /**
13685 * Information about open rows. Each object in the array has the parameters
13686 * 'nTr' and 'nParent'
13687 * @type array
13688 * @default []
13689 */
13690 "aoOpenRows": [],
13691
13692 /**
13693 * Dictate the positioning of DataTables' control elements - see
13694 * {@link DataTable.model.oInit.sDom}.
13695 * Note that this parameter will be set by the initialisation routine. To
13696 * set a default use {@link DataTable.defaults}.
13697 * @type string
13698 * @default null
13699 */
13700 "sDom": null,
13701
13702 /**
13703 * Search delay (in mS)
13704 * @type integer
13705 * @default null
13706 */
13707 "searchDelay": null,
13708
13709 /**
13710 * Which type of pagination should be used.
13711 * Note that this parameter will be set by the initialisation routine. To
13712 * set a default use {@link DataTable.defaults}.
13713 * @type string
13714 * @default two_button
13715 */
13716 "sPaginationType": "two_button",
13717
13718 /**
13719 * The state duration (for `stateSave`) in seconds.
13720 * Note that this parameter will be set by the initialisation routine. To
13721 * set a default use {@link DataTable.defaults}.
13722 * @type int
13723 * @default 0
13724 */
13725 "iStateDuration": 0,
13726
13727 /**
13728 * Array of callback functions for state saving. Each array element is an
13729 * object with the following parameters:
13730 * <ul>
13731 * <li>function:fn - function to call. Takes two parameters, oSettings
13732 * and the JSON string to save that has been thus far created. Returns
13733 * a JSON string to be inserted into a json object
13734 * (i.e. '"param": [ 0, 1, 2]')</li>
13735 * <li>string:sName - name of callback</li>
13736 * </ul>
13737 * @type array
13738 * @default []
13739 */
13740 "aoStateSave": [],
13741
13742 /**
13743 * Array of callback functions for state loading. Each array element is an
13744 * object with the following parameters:
13745 * <ul>
13746 * <li>function:fn - function to call. Takes two parameters, oSettings
13747 * and the object stored. May return false to cancel state loading</li>
13748 * <li>string:sName - name of callback</li>
13749 * </ul>
13750 * @type array
13751 * @default []
13752 */
13753 "aoStateLoad": [],
13754
13755 /**
13756 * State that was saved. Useful for back reference
13757 * @type object
13758 * @default null
13759 */
13760 "oSavedState": null,
13761
13762 /**
13763 * State that was loaded. Useful for back reference
13764 * @type object
13765 * @default null
13766 */
13767 "oLoadedState": null,
13768
13769 /**
13770 * Source url for AJAX data for the table.
13771 * Note that this parameter will be set by the initialisation routine. To
13772 * set a default use {@link DataTable.defaults}.
13773 * @type string
13774 * @default null
13775 */
13776 "sAjaxSource": null,
13777
13778 /**
13779 * Property from a given object from which to read the table data from. This
13780 * can be an empty string (when not server-side processing), in which case
13781 * it is assumed an an array is given directly.
13782 * Note that this parameter will be set by the initialisation routine. To
13783 * set a default use {@link DataTable.defaults}.
13784 * @type string
13785 */
13786 "sAjaxDataProp": null,
13787
13788 /**
13789 * The last jQuery XHR object that was used for server-side data gathering.
13790 * This can be used for working with the XHR information in one of the
13791 * callbacks
13792 * @type object
13793 * @default null
13794 */
13795 "jqXHR": null,
13796
13797 /**
13798 * JSON returned from the server in the last Ajax request
13799 * @type object
13800 * @default undefined
13801 */
13802 "json": undefined,
13803
13804 /**
13805 * Data submitted as part of the last Ajax request
13806 * @type object
13807 * @default undefined
13808 */
13809 "oAjaxData": undefined,
13810
13811 /**
13812 * Function to get the server-side data.
13813 * Note that this parameter will be set by the initialisation routine. To
13814 * set a default use {@link DataTable.defaults}.
13815 * @type function
13816 */
13817 "fnServerData": null,
13818
13819 /**
13820 * Functions which are called prior to sending an Ajax request so extra
13821 * parameters can easily be sent to the server
13822 * @type array
13823 * @default []
13824 */
13825 "aoServerParams": [],
13826
13827 /**
13828 * Send the XHR HTTP method - GET or POST (could be PUT or DELETE if
13829 * required).
13830 * Note that this parameter will be set by the initialisation routine. To
13831 * set a default use {@link DataTable.defaults}.
13832 * @type string
13833 */
13834 "sServerMethod": null,
13835
13836 /**
13837 * Format numbers for display.
13838 * Note that this parameter will be set by the initialisation routine. To
13839 * set a default use {@link DataTable.defaults}.
13840 * @type function
13841 */
13842 "fnFormatNumber": null,
13843
13844 /**
13845 * List of options that can be used for the user selectable length menu.
13846 * Note that this parameter will be set by the initialisation routine. To
13847 * set a default use {@link DataTable.defaults}.
13848 * @type array
13849 * @default []
13850 */
13851 "aLengthMenu": null,
13852
13853 /**
13854 * Counter for the draws that the table does. Also used as a tracker for
13855 * server-side processing
13856 * @type int
13857 * @default 0
13858 */
13859 "iDraw": 0,
13860
13861 /**
13862 * Indicate if a redraw is being done - useful for Ajax
13863 * @type boolean
13864 * @default false
13865 */
13866 "bDrawing": false,
13867
13868 /**
13869 * Draw index (iDraw) of the last error when parsing the returned data
13870 * @type int
13871 * @default -1
13872 */
13873 "iDrawError": -1,
13874
13875 /**
13876 * Paging display length
13877 * @type int
13878 * @default 10
13879 */
13880 "_iDisplayLength": 10,
13881
13882 /**
13883 * Paging start point - aiDisplay index
13884 * @type int
13885 * @default 0
13886 */
13887 "_iDisplayStart": 0,
13888
13889 /**
13890 * Server-side processing - number of records in the result set
13891 * (i.e. before filtering), Use fnRecordsTotal rather than
13892 * this property to get the value of the number of records, regardless of
13893 * the server-side processing setting.
13894 * @type int
13895 * @default 0
13896 * @private
13897 */
13898 "_iRecordsTotal": 0,
13899
13900 /**
13901 * Server-side processing - number of records in the current display set
13902 * (i.e. after filtering). Use fnRecordsDisplay rather than
13903 * this property to get the value of the number of records, regardless of
13904 * the server-side processing setting.
13905 * @type boolean
13906 * @default 0
13907 * @private
13908 */
13909 "_iRecordsDisplay": 0,
13910
13911 /**
13912 * The classes to use for the table
13913 * @type object
13914 * @default {}
13915 */
13916 "oClasses": {},
13917
13918 /**
13919 * Flag attached to the settings object so you can check in the draw
13920 * callback if filtering has been done in the draw. Deprecated in favour of
13921 * events.
13922 * @type boolean
13923 * @default false
13924 * @deprecated
13925 */
13926 "bFiltered": false,
13927
13928 /**
13929 * Flag attached to the settings object so you can check in the draw
13930 * callback if sorting has been done in the draw. Deprecated in favour of
13931 * events.
13932 * @type boolean
13933 * @default false
13934 * @deprecated
13935 */
13936 "bSorted": false,
13937
13938 /**
13939 * Indicate that if multiple rows are in the header and there is more than
13940 * one unique cell per column, if the top one (true) or bottom one (false)
13941 * should be used for sorting / title by DataTables.
13942 * Note that this parameter will be set by the initialisation routine. To
13943 * set a default use {@link DataTable.defaults}.
13944 * @type boolean
13945 */
13946 "bSortCellsTop": null,
13947
13948 /**
13949 * Initialisation object that is used for the table
13950 * @type object
13951 * @default null
13952 */
13953 "oInit": null,
13954
13955 /**
13956 * Destroy callback functions - for plug-ins to attach themselves to the
13957 * destroy so they can clean up markup and events.
13958 * @type array
13959 * @default []
13960 */
13961 "aoDestroyCallback": [],
13962
13963
13964 /**
13965 * Get the number of records in the current record set, before filtering
13966 * @type function
13967 */
13968 "fnRecordsTotal": function ()
13969 {
13970 return _fnDataSource( this ) == 'ssp' ?
13971 this._iRecordsTotal * 1 :
13972 this.aiDisplayMaster.length;
13973 },
13974
13975 /**
13976 * Get the number of records in the current record set, after filtering
13977 * @type function
13978 */
13979 "fnRecordsDisplay": function ()
13980 {
13981 return _fnDataSource( this ) == 'ssp' ?
13982 this._iRecordsDisplay * 1 :
13983 this.aiDisplay.length;
13984 },
13985
13986 /**
13987 * Get the display end point - aiDisplay index
13988 * @type function
13989 */
13990 "fnDisplayEnd": function ()
13991 {
13992 var
13993 len = this._iDisplayLength,
13994 start = this._iDisplayStart,
13995 calc = start + len,
13996 records = this.aiDisplay.length,
13997 features = this.oFeatures,
13998 paginate = features.bPaginate;
13999
14000 if ( features.bServerSide ) {
14001 return paginate === false || len === -1 ?
14002 start + records :
14003 Math.min( start+len, this._iRecordsDisplay );
14004 }
14005 else {
14006 return ! paginate || calc>records || len===-1 ?
14007 records :
14008 calc;
14009 }
14010 },
14011
14012 /**
14013 * The DataTables object for this table
14014 * @type object
14015 * @default null
14016 */
14017 "oInstance": null,
14018
14019 /**
14020 * Unique identifier for each instance of the DataTables object. If there
14021 * is an ID on the table node, then it takes that value, otherwise an
14022 * incrementing internal counter is used.
14023 * @type string
14024 * @default null
14025 */
14026 "sInstance": null,
14027
14028 /**
14029 * tabindex attribute value that is added to DataTables control elements, allowing
14030 * keyboard navigation of the table and its controls.
14031 */
14032 "iTabIndex": 0,
14033
14034 /**
14035 * DIV container for the footer scrolling table if scrolling
14036 */
14037 "nScrollHead": null,
14038
14039 /**
14040 * DIV container for the footer scrolling table if scrolling
14041 */
14042 "nScrollFoot": null,
14043
14044 /**
14045 * Last applied sort
14046 * @type array
14047 * @default []
14048 */
14049 "aLastSort": [],
14050
14051 /**
14052 * Stored plug-in instances
14053 * @type object
14054 * @default {}
14055 */
14056 "oPlugins": {},
14057
14058 /**
14059 * Function used to get a row's id from the row's data
14060 * @type function
14061 * @default null
14062 */
14063 "rowIdFn": null,
14064
14065 /**
14066 * Data location where to store a row's id
14067 * @type string
14068 * @default null
14069 */
14070 "rowId": null
14071 };
14072
14073 /**
14074 * Extension object for DataTables that is used to provide all extension
14075 * options.
14076 *
14077 * Note that the `DataTable.ext` object is available through
14078 * `jQuery.fn.dataTable.ext` where it may be accessed and manipulated. It is
14079 * also aliased to `jQuery.fn.dataTableExt` for historic reasons.
14080 * @namespace
14081 * @extends DataTable.models.ext
14082 */
14083
14084
14085 /**
14086 * DataTables extensions
14087 *
14088 * This namespace acts as a collection area for plug-ins that can be used to
14089 * extend DataTables capabilities. Indeed many of the build in methods
14090 * use this method to provide their own capabilities (sorting methods for
14091 * example).
14092 *
14093 * Note that this namespace is aliased to `jQuery.fn.dataTableExt` for legacy
14094 * reasons
14095 *
14096 * @namespace
14097 */
14098 DataTable.ext = _ext = {
14099 /**
14100 * Buttons. For use with the Buttons extension for DataTables. This is
14101 * defined here so other extensions can define buttons regardless of load
14102 * order. It is _not_ used by DataTables core.
14103 *
14104 * @type object
14105 * @default {}
14106 */
14107 buttons: {},
14108
14109
14110 /**
14111 * Element class names
14112 *
14113 * @type object
14114 * @default {}
14115 */
14116 classes: {},
14117
14118
14119 /**
14120 * DataTables build type (expanded by the download builder)
14121 *
14122 * @type string
14123 */
14124 build:"bs5/dt-1.13.1/r-2.4.0/sl-1.5.0",
14125
14126
14127 /**
14128 * Error reporting.
14129 *
14130 * How should DataTables report an error. Can take the value 'alert',
14131 * 'throw', 'none' or a function.
14132 *
14133 * @type string|function
14134 * @default alert
14135 */
14136 errMode: "alert",
14137
14138
14139 /**
14140 * Feature plug-ins.
14141 *
14142 * This is an array of objects which describe the feature plug-ins that are
14143 * available to DataTables. These feature plug-ins are then available for
14144 * use through the `dom` initialisation option.
14145 *
14146 * Each feature plug-in is described by an object which must have the
14147 * following properties:
14148 *
14149 * * `fnInit` - function that is used to initialise the plug-in,
14150 * * `cFeature` - a character so the feature can be enabled by the `dom`
14151 * instillation option. This is case sensitive.
14152 *
14153 * The `fnInit` function has the following input parameters:
14154 *
14155 * 1. `{object}` DataTables settings object: see
14156 * {@link DataTable.models.oSettings}
14157 *
14158 * And the following return is expected:
14159 *
14160 * * {node|null} The element which contains your feature. Note that the
14161 * return may also be void if your plug-in does not require to inject any
14162 * DOM elements into DataTables control (`dom`) - for example this might
14163 * be useful when developing a plug-in which allows table control via
14164 * keyboard entry
14165 *
14166 * @type array
14167 *
14168 * @example
14169 * $.fn.dataTable.ext.features.push( {
14170 * "fnInit": function( oSettings ) {
14171 * return new TableTools( { "oDTSettings": oSettings } );
14172 * },
14173 * "cFeature": "T"
14174 * } );
14175 */
14176 feature: [],
14177
14178
14179 /**
14180 * Row searching.
14181 *
14182 * This method of searching is complimentary to the default type based
14183 * searching, and a lot more comprehensive as it allows you complete control
14184 * over the searching logic. Each element in this array is a function
14185 * (parameters described below) that is called for every row in the table,
14186 * and your logic decides if it should be included in the searching data set
14187 * or not.
14188 *
14189 * Searching functions have the following input parameters:
14190 *
14191 * 1. `{object}` DataTables settings object: see
14192 * {@link DataTable.models.oSettings}
14193 * 2. `{array|object}` Data for the row to be processed (same as the
14194 * original format that was passed in as the data source, or an array
14195 * from a DOM data source
14196 * 3. `{int}` Row index ({@link DataTable.models.oSettings.aoData}), which
14197 * can be useful to retrieve the `TR` element if you need DOM interaction.
14198 *
14199 * And the following return is expected:
14200 *
14201 * * {boolean} Include the row in the searched result set (true) or not
14202 * (false)
14203 *
14204 * Note that as with the main search ability in DataTables, technically this
14205 * is "filtering", since it is subtractive. However, for consistency in
14206 * naming we call it searching here.
14207 *
14208 * @type array
14209 * @default []
14210 *
14211 * @example
14212 * // The following example shows custom search being applied to the
14213 * // fourth column (i.e. the data[3] index) based on two input values
14214 * // from the end-user, matching the data in a certain range.
14215 * $.fn.dataTable.ext.search.push(
14216 * function( settings, data, dataIndex ) {
14217 * var min = document.getElementById('min').value * 1;
14218 * var max = document.getElementById('max').value * 1;
14219 * var version = data[3] == "-" ? 0 : data[3]*1;
14220 *
14221 * if ( min == "" && max == "" ) {
14222 * return true;
14223 * }
14224 * else if ( min == "" && version < max ) {
14225 * return true;
14226 * }
14227 * else if ( min < version && "" == max ) {
14228 * return true;
14229 * }
14230 * else if ( min < version && version < max ) {
14231 * return true;
14232 * }
14233 * return false;
14234 * }
14235 * );
14236 */
14237 search: [],
14238
14239
14240 /**
14241 * Selector extensions
14242 *
14243 * The `selector` option can be used to extend the options available for the
14244 * selector modifier options (`selector-modifier` object data type) that
14245 * each of the three built in selector types offer (row, column and cell +
14246 * their plural counterparts). For example the Select extension uses this
14247 * mechanism to provide an option to select only rows, columns and cells
14248 * that have been marked as selected by the end user (`{selected: true}`),
14249 * which can be used in conjunction with the existing built in selector
14250 * options.
14251 *
14252 * Each property is an array to which functions can be pushed. The functions
14253 * take three attributes:
14254 *
14255 * * Settings object for the host table
14256 * * Options object (`selector-modifier` object type)
14257 * * Array of selected item indexes
14258 *
14259 * The return is an array of the resulting item indexes after the custom
14260 * selector has been applied.
14261 *
14262 * @type object
14263 */
14264 selector: {
14265 cell: [],
14266 column: [],
14267 row: []
14268 },
14269
14270
14271 /**
14272 * Internal functions, exposed for used in plug-ins.
14273 *
14274 * Please note that you should not need to use the internal methods for
14275 * anything other than a plug-in (and even then, try to avoid if possible).
14276 * The internal function may change between releases.
14277 *
14278 * @type object
14279 * @default {}
14280 */
14281 internal: {},
14282
14283
14284 /**
14285 * Legacy configuration options. Enable and disable legacy options that
14286 * are available in DataTables.
14287 *
14288 * @type object
14289 */
14290 legacy: {
14291 /**
14292 * Enable / disable DataTables 1.9 compatible server-side processing
14293 * requests
14294 *
14295 * @type boolean
14296 * @default null
14297 */
14298 ajax: null
14299 },
14300
14301
14302 /**
14303 * Pagination plug-in methods.
14304 *
14305 * Each entry in this object is a function and defines which buttons should
14306 * be shown by the pagination rendering method that is used for the table:
14307 * {@link DataTable.ext.renderer.pageButton}. The renderer addresses how the
14308 * buttons are displayed in the document, while the functions here tell it
14309 * what buttons to display. This is done by returning an array of button
14310 * descriptions (what each button will do).
14311 *
14312 * Pagination types (the four built in options and any additional plug-in
14313 * options defined here) can be used through the `paginationType`
14314 * initialisation parameter.
14315 *
14316 * The functions defined take two parameters:
14317 *
14318 * 1. `{int} page` The current page index
14319 * 2. `{int} pages` The number of pages in the table
14320 *
14321 * Each function is expected to return an array where each element of the
14322 * array can be one of:
14323 *
14324 * * `first` - Jump to first page when activated
14325 * * `last` - Jump to last page when activated
14326 * * `previous` - Show previous page when activated
14327 * * `next` - Show next page when activated
14328 * * `{int}` - Show page of the index given
14329 * * `{array}` - A nested array containing the above elements to add a
14330 * containing 'DIV' element (might be useful for styling).
14331 *
14332 * Note that DataTables v1.9- used this object slightly differently whereby
14333 * an object with two functions would be defined for each plug-in. That
14334 * ability is still supported by DataTables 1.10+ to provide backwards
14335 * compatibility, but this option of use is now decremented and no longer
14336 * documented in DataTables 1.10+.
14337 *
14338 * @type object
14339 * @default {}
14340 *
14341 * @example
14342 * // Show previous, next and current page buttons only
14343 * $.fn.dataTableExt.oPagination.current = function ( page, pages ) {
14344 * return [ 'previous', page, 'next' ];
14345 * };
14346 */
14347 pager: {},
14348
14349
14350 renderer: {
14351 pageButton: {},
14352 header: {}
14353 },
14354
14355
14356 /**
14357 * Ordering plug-ins - custom data source
14358 *
14359 * The extension options for ordering of data available here is complimentary
14360 * to the default type based ordering that DataTables typically uses. It
14361 * allows much greater control over the the data that is being used to
14362 * order a column, but is necessarily therefore more complex.
14363 *
14364 * This type of ordering is useful if you want to do ordering based on data
14365 * live from the DOM (for example the contents of an 'input' element) rather
14366 * than just the static string that DataTables knows of.
14367 *
14368 * The way these plug-ins work is that you create an array of the values you
14369 * wish to be ordering for the column in question and then return that
14370 * array. The data in the array much be in the index order of the rows in
14371 * the table (not the currently ordering order!). Which order data gathering
14372 * function is run here depends on the `dt-init columns.orderDataType`
14373 * parameter that is used for the column (if any).
14374 *
14375 * The functions defined take two parameters:
14376 *
14377 * 1. `{object}` DataTables settings object: see
14378 * {@link DataTable.models.oSettings}
14379 * 2. `{int}` Target column index
14380 *
14381 * Each function is expected to return an array:
14382 *
14383 * * `{array}` Data for the column to be ordering upon
14384 *
14385 * @type array
14386 *
14387 * @example
14388 * // Ordering using `input` node values
14389 * $.fn.dataTable.ext.order['dom-text'] = function ( settings, col )
14390 * {
14391 * return this.api().column( col, {order:'index'} ).nodes().map( function ( td, i ) {
14392 * return $('input', td).val();
14393 * } );
14394 * }
14395 */
14396 order: {},
14397
14398
14399 /**
14400 * Type based plug-ins.
14401 *
14402 * Each column in DataTables has a type assigned to it, either by automatic
14403 * detection or by direct assignment using the `type` option for the column.
14404 * The type of a column will effect how it is ordering and search (plug-ins
14405 * can also make use of the column type if required).
14406 *
14407 * @namespace
14408 */
14409 type: {
14410 /**
14411 * Type detection functions.
14412 *
14413 * The functions defined in this object are used to automatically detect
14414 * a column's type, making initialisation of DataTables super easy, even
14415 * when complex data is in the table.
14416 *
14417 * The functions defined take two parameters:
14418 *
14419 * 1. `{*}` Data from the column cell to be analysed
14420 * 2. `{settings}` DataTables settings object. This can be used to
14421 * perform context specific type detection - for example detection
14422 * based on language settings such as using a comma for a decimal
14423 * place. Generally speaking the options from the settings will not
14424 * be required
14425 *
14426 * Each function is expected to return:
14427 *
14428 * * `{string|null}` Data type detected, or null if unknown (and thus
14429 * pass it on to the other type detection functions.
14430 *
14431 * @type array
14432 *
14433 * @example
14434 * // Currency type detection plug-in:
14435 * $.fn.dataTable.ext.type.detect.push(
14436 * function ( data, settings ) {
14437 * // Check the numeric part
14438 * if ( ! data.substring(1).match(/[0-9]/) ) {
14439 * return null;
14440 * }
14441 *
14442 * // Check prefixed by currency
14443 * if ( data.charAt(0) == '$' || data.charAt(0) == '&pound;' ) {
14444 * return 'currency';
14445 * }
14446 * return null;
14447 * }
14448 * );
14449 */
14450 detect: [],
14451
14452
14453 /**
14454 * Type based search formatting.
14455 *
14456 * The type based searching functions can be used to pre-format the
14457 * data to be search on. For example, it can be used to strip HTML
14458 * tags or to de-format telephone numbers for numeric only searching.
14459 *
14460 * Note that is a search is not defined for a column of a given type,
14461 * no search formatting will be performed.
14462 *
14463 * Pre-processing of searching data plug-ins - When you assign the sType
14464 * for a column (or have it automatically detected for you by DataTables
14465 * or a type detection plug-in), you will typically be using this for
14466 * custom sorting, but it can also be used to provide custom searching
14467 * by allowing you to pre-processing the data and returning the data in
14468 * the format that should be searched upon. This is done by adding
14469 * functions this object with a parameter name which matches the sType
14470 * for that target column. This is the corollary of <i>afnSortData</i>
14471 * for searching data.
14472 *
14473 * The functions defined take a single parameter:
14474 *
14475 * 1. `{*}` Data from the column cell to be prepared for searching
14476 *
14477 * Each function is expected to return:
14478 *
14479 * * `{string|null}` Formatted string that will be used for the searching.
14480 *
14481 * @type object
14482 * @default {}
14483 *
14484 * @example
14485 * $.fn.dataTable.ext.type.search['title-numeric'] = function ( d ) {
14486 * return d.replace(/\n/g," ").replace( /<.*?>/g, "" );
14487 * }
14488 */
14489 search: {},
14490
14491
14492 /**
14493 * Type based ordering.
14494 *
14495 * The column type tells DataTables what ordering to apply to the table
14496 * when a column is sorted upon. The order for each type that is defined,
14497 * is defined by the functions available in this object.
14498 *
14499 * Each ordering option can be described by three properties added to
14500 * this object:
14501 *
14502 * * `{type}-pre` - Pre-formatting function
14503 * * `{type}-asc` - Ascending order function
14504 * * `{type}-desc` - Descending order function
14505 *
14506 * All three can be used together, only `{type}-pre` or only
14507 * `{type}-asc` and `{type}-desc` together. It is generally recommended
14508 * that only `{type}-pre` is used, as this provides the optimal
14509 * implementation in terms of speed, although the others are provided
14510 * for compatibility with existing Javascript sort functions.
14511 *
14512 * `{type}-pre`: Functions defined take a single parameter:
14513 *
14514 * 1. `{*}` Data from the column cell to be prepared for ordering
14515 *
14516 * And return:
14517 *
14518 * * `{*}` Data to be sorted upon
14519 *
14520 * `{type}-asc` and `{type}-desc`: Functions are typical Javascript sort
14521 * functions, taking two parameters:
14522 *
14523 * 1. `{*}` Data to compare to the second parameter
14524 * 2. `{*}` Data to compare to the first parameter
14525 *
14526 * And returning:
14527 *
14528 * * `{*}` Ordering match: <0 if first parameter should be sorted lower
14529 * than the second parameter, ===0 if the two parameters are equal and
14530 * >0 if the first parameter should be sorted height than the second
14531 * parameter.
14532 *
14533 * @type object
14534 * @default {}
14535 *
14536 * @example
14537 * // Numeric ordering of formatted numbers with a pre-formatter
14538 * $.extend( $.fn.dataTable.ext.type.order, {
14539 * "string-pre": function(x) {
14540 * a = (a === "-" || a === "") ? 0 : a.replace( /[^\d\-\.]/g, "" );
14541 * return parseFloat( a );
14542 * }
14543 * } );
14544 *
14545 * @example
14546 * // Case-sensitive string ordering, with no pre-formatting method
14547 * $.extend( $.fn.dataTable.ext.order, {
14548 * "string-case-asc": function(x,y) {
14549 * return ((x < y) ? -1 : ((x > y) ? 1 : 0));
14550 * },
14551 * "string-case-desc": function(x,y) {
14552 * return ((x < y) ? 1 : ((x > y) ? -1 : 0));
14553 * }
14554 * } );
14555 */
14556 order: {}
14557 },
14558
14559 /**
14560 * Unique DataTables instance counter
14561 *
14562 * @type int
14563 * @private
14564 */
14565 _unique: 0,
14566
14567
14568 //
14569 // Depreciated
14570 // The following properties are retained for backwards compatibility only.
14571 // The should not be used in new projects and will be removed in a future
14572 // version
14573 //
14574
14575 /**
14576 * Version check function.
14577 * @type function
14578 * @depreciated Since 1.10
14579 */
14580 fnVersionCheck: DataTable.fnVersionCheck,
14581
14582
14583 /**
14584 * Index for what 'this' index API functions should use
14585 * @type int
14586 * @deprecated Since v1.10
14587 */
14588 iApiIndex: 0,
14589
14590
14591 /**
14592 * jQuery UI class container
14593 * @type object
14594 * @deprecated Since v1.10
14595 */
14596 oJUIClasses: {},
14597
14598
14599 /**
14600 * Software version
14601 * @type string
14602 * @deprecated Since v1.10
14603 */
14604 sVersion: DataTable.version
14605 };
14606
14607
14608 //
14609 // Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts
14610 //
14611 $.extend( _ext, {
14612 afnFiltering: _ext.search,
14613 aTypes: _ext.type.detect,
14614 ofnSearch: _ext.type.search,
14615 oSort: _ext.type.order,
14616 afnSortData: _ext.order,
14617 aoFeatures: _ext.feature,
14618 oApi: _ext.internal,
14619 oStdClasses: _ext.classes,
14620 oPagination: _ext.pager
14621 } );
14622
14623
14624 $.extend( DataTable.ext.classes, {
14625 "sTable": "dataTable",
14626 "sNoFooter": "no-footer",
14627
14628 /* Paging buttons */
14629 "sPageButton": "paginate_button",
14630 "sPageButtonActive": "current",
14631 "sPageButtonDisabled": "disabled",
14632
14633 /* Striping classes */
14634 "sStripeOdd": "odd",
14635 "sStripeEven": "even",
14636
14637 /* Empty row */
14638 "sRowEmpty": "dataTables_empty",
14639
14640 /* Features */
14641 "sWrapper": "dataTables_wrapper",
14642 "sFilter": "dataTables_filter",
14643 "sInfo": "dataTables_info",
14644 "sPaging": "dataTables_paginate paging_", /* Note that the type is postfixed */
14645 "sLength": "dataTables_length",
14646 "sProcessing": "dataTables_processing",
14647
14648 /* Sorting */
14649 "sSortAsc": "sorting_asc",
14650 "sSortDesc": "sorting_desc",
14651 "sSortable": "sorting", /* Sortable in both directions */
14652 "sSortableAsc": "sorting_desc_disabled",
14653 "sSortableDesc": "sorting_asc_disabled",
14654 "sSortableNone": "sorting_disabled",
14655 "sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */
14656
14657 /* Filtering */
14658 "sFilterInput": "",
14659
14660 /* Page length */
14661 "sLengthSelect": "",
14662
14663 /* Scrolling */
14664 "sScrollWrapper": "dataTables_scroll",
14665 "sScrollHead": "dataTables_scrollHead",
14666 "sScrollHeadInner": "dataTables_scrollHeadInner",
14667 "sScrollBody": "dataTables_scrollBody",
14668 "sScrollFoot": "dataTables_scrollFoot",
14669 "sScrollFootInner": "dataTables_scrollFootInner",
14670
14671 /* Misc */
14672 "sHeaderTH": "",
14673 "sFooterTH": "",
14674
14675 // Deprecated
14676 "sSortJUIAsc": "",
14677 "sSortJUIDesc": "",
14678 "sSortJUI": "",
14679 "sSortJUIAscAllowed": "",
14680 "sSortJUIDescAllowed": "",
14681 "sSortJUIWrapper": "",
14682 "sSortIcon": "",
14683 "sJUIHeader": "",
14684 "sJUIFooter": ""
14685 } );
14686
14687
14688 var extPagination = DataTable.ext.pager;
14689
14690 function _numbers ( page, pages ) {
14691 var
14692 numbers = [],
14693 buttons = extPagination.numbers_length,
14694 half = Math.floor( buttons / 2 ),
14695 i = 1;
14696
14697 if ( pages <= buttons ) {
14698 numbers = _range( 0, pages );
14699 }
14700 else if ( page <= half ) {
14701 numbers = _range( 0, buttons-2 );
14702 numbers.push( 'ellipsis' );
14703 numbers.push( pages-1 );
14704 }
14705 else if ( page >= pages - 1 - half ) {
14706 numbers = _range( pages-(buttons-2), pages );
14707 numbers.splice( 0, 0, 'ellipsis' ); // no unshift in ie6
14708 numbers.splice( 0, 0, 0 );
14709 }
14710 else {
14711 numbers = _range( page-half+2, page+half-1 );
14712 numbers.push( 'ellipsis' );
14713 numbers.push( pages-1 );
14714 numbers.splice( 0, 0, 'ellipsis' );
14715 numbers.splice( 0, 0, 0 );
14716 }
14717
14718 numbers.DT_el = 'span';
14719 return numbers;
14720 }
14721
14722
14723 $.extend( extPagination, {
14724 simple: function ( page, pages ) {
14725 return [ 'previous', 'next' ];
14726 },
14727
14728 full: function ( page, pages ) {
14729 return [ 'first', 'previous', 'next', 'last' ];
14730 },
14731
14732 numbers: function ( page, pages ) {
14733 return [ _numbers(page, pages) ];
14734 },
14735
14736 simple_numbers: function ( page, pages ) {
14737 return [ 'previous', _numbers(page, pages), 'next' ];
14738 },
14739
14740 full_numbers: function ( page, pages ) {
14741 return [ 'first', 'previous', _numbers(page, pages), 'next', 'last' ];
14742 },
14743
14744 first_last_numbers: function (page, pages) {
14745 return ['first', _numbers(page, pages), 'last'];
14746 },
14747
14748 // For testing and plug-ins to use
14749 _numbers: _numbers,
14750
14751 // Number of number buttons (including ellipsis) to show. _Must be odd!_
14752 numbers_length: 7
14753 } );
14754
14755
14756 $.extend( true, DataTable.ext.renderer, {
14757 pageButton: {
14758 _: function ( settings, host, idx, buttons, page, pages ) {
14759 var classes = settings.oClasses;
14760 var lang = settings.oLanguage.oPaginate;
14761 var aria = settings.oLanguage.oAria.paginate || {};
14762 var btnDisplay, btnClass;
14763
14764 var attach = function( container, buttons ) {
14765 var i, ien, node, button, tabIndex;
14766 var disabledClass = classes.sPageButtonDisabled;
14767 var clickHandler = function ( e ) {
14768 _fnPageChange( settings, e.data.action, true );
14769 };
14770
14771 for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
14772 button = buttons[i];
14773
14774 if ( Array.isArray( button ) ) {
14775 var inner = $( '<'+(button.DT_el || 'div')+'/>' )
14776 .appendTo( container );
14777 attach( inner, button );
14778 }
14779 else {
14780 btnDisplay = null;
14781 btnClass = button;
14782 tabIndex = settings.iTabIndex;
14783
14784 switch ( button ) {
14785 case 'ellipsis':
14786 container.append('<span class="ellipsis">&#x2026;</span>');
14787 break;
14788
14789 case 'first':
14790 btnDisplay = lang.sFirst;
14791
14792 if ( page === 0 ) {
14793 tabIndex = -1;
14794 btnClass += ' ' + disabledClass;
14795 }
14796 break;
14797
14798 case 'previous':
14799 btnDisplay = lang.sPrevious;
14800
14801 if ( page === 0 ) {
14802 tabIndex = -1;
14803 btnClass += ' ' + disabledClass;
14804 }
14805 break;
14806
14807 case 'next':
14808 btnDisplay = lang.sNext;
14809
14810 if ( pages === 0 || page === pages-1 ) {
14811 tabIndex = -1;
14812 btnClass += ' ' + disabledClass;
14813 }
14814 break;
14815
14816 case 'last':
14817 btnDisplay = lang.sLast;
14818
14819 if ( pages === 0 || page === pages-1 ) {
14820 tabIndex = -1;
14821 btnClass += ' ' + disabledClass;
14822 }
14823 break;
14824
14825 default:
14826 btnDisplay = settings.fnFormatNumber( button + 1 );
14827 btnClass = page === button ?
14828 classes.sPageButtonActive : '';
14829 break;
14830 }
14831
14832 if ( btnDisplay !== null ) {
14833 node = $('<a>', {
14834 'class': classes.sPageButton+' '+btnClass,
14835 'aria-controls': settings.sTableId,
14836 'aria-label': aria[ button ],
14837 'data-dt-idx': button,
14838 'tabindex': tabIndex,
14839 'id': idx === 0 && typeof button === 'string' ?
14840 settings.sTableId +'_'+ button :
14841 null
14842 } )
14843 .html( btnDisplay )
14844 .appendTo( container );
14845
14846 _fnBindAction(
14847 node, {action: button}, clickHandler
14848 );
14849 }
14850 }
14851 }
14852 };
14853
14854 // IE9 throws an 'unknown error' if document.activeElement is used
14855 // inside an iframe or frame. Try / catch the error. Not good for
14856 // accessibility, but neither are frames.
14857 var activeEl;
14858
14859 try {
14860 // Because this approach is destroying and recreating the paging
14861 // elements, focus is lost on the select button which is bad for
14862 // accessibility. So we want to restore focus once the draw has
14863 // completed
14864 activeEl = $(host).find(document.activeElement).data('dt-idx');
14865 }
14866 catch (e) {}
14867
14868 attach( $(host).empty(), buttons );
14869
14870 if ( activeEl !== undefined ) {
14871 $(host).find( '[data-dt-idx='+activeEl+']' ).trigger('focus');
14872 }
14873 }
14874 }
14875 } );
14876
14877
14878
14879 // Built in type detection. See model.ext.aTypes for information about
14880 // what is required from this methods.
14881 $.extend( DataTable.ext.type.detect, [
14882 // Plain numbers - first since V8 detects some plain numbers as dates
14883 // e.g. Date.parse('55') (but not all, e.g. Date.parse('22')...).
14884 function ( d, settings )
14885 {
14886 var decimal = settings.oLanguage.sDecimal;
14887 return _isNumber( d, decimal ) ? 'num'+decimal : null;
14888 },
14889
14890 // Dates (only those recognised by the browser's Date.parse)
14891 function ( d, settings )
14892 {
14893 // V8 tries _very_ hard to make a string passed into `Date.parse()`
14894 // valid, so we need to use a regex to restrict date formats. Use a
14895 // plug-in for anything other than ISO8601 style strings
14896 if ( d && !(d instanceof Date) && ! _re_date.test(d) ) {
14897 return null;
14898 }
14899 var parsed = Date.parse(d);
14900 return (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null;
14901 },
14902
14903 // Formatted numbers
14904 function ( d, settings )
14905 {
14906 var decimal = settings.oLanguage.sDecimal;
14907 return _isNumber( d, decimal, true ) ? 'num-fmt'+decimal : null;
14908 },
14909
14910 // HTML numeric
14911 function ( d, settings )
14912 {
14913 var decimal = settings.oLanguage.sDecimal;
14914 return _htmlNumeric( d, decimal ) ? 'html-num'+decimal : null;
14915 },
14916
14917 // HTML numeric, formatted
14918 function ( d, settings )
14919 {
14920 var decimal = settings.oLanguage.sDecimal;
14921 return _htmlNumeric( d, decimal, true ) ? 'html-num-fmt'+decimal : null;
14922 },
14923
14924 // HTML (this is strict checking - there must be html)
14925 function ( d, settings )
14926 {
14927 return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1) ?
14928 'html' : null;
14929 }
14930 ] );
14931
14932
14933
14934 // Filter formatting functions. See model.ext.ofnSearch for information about
14935 // what is required from these methods.
14936 //
14937 // Note that additional search methods are added for the html numbers and
14938 // html formatted numbers by `_addNumericSort()` when we know what the decimal
14939 // place is
14940
14941
14942 $.extend( DataTable.ext.type.search, {
14943 html: function ( data ) {
14944 return _empty(data) ?
14945 data :
14946 typeof data === 'string' ?
14947 data
14948 .replace( _re_new_lines, " " )
14949 .replace( _re_html, "" ) :
14950 '';
14951 },
14952
14953 string: function ( data ) {
14954 return _empty(data) ?
14955 data :
14956 typeof data === 'string' ?
14957 data.replace( _re_new_lines, " " ) :
14958 data;
14959 }
14960 } );
14961
14962
14963
14964 var __numericReplace = function ( d, decimalPlace, re1, re2 ) {
14965 if ( d !== 0 && (!d || d === '-') ) {
14966 return -Infinity;
14967 }
14968
14969 // If a decimal place other than `.` is used, it needs to be given to the
14970 // function so we can detect it and replace with a `.` which is the only
14971 // decimal place Javascript recognises - it is not locale aware.
14972 if ( decimalPlace ) {
14973 d = _numToDecimal( d, decimalPlace );
14974 }
14975
14976 if ( d.replace ) {
14977 if ( re1 ) {
14978 d = d.replace( re1, '' );
14979 }
14980
14981 if ( re2 ) {
14982 d = d.replace( re2, '' );
14983 }
14984 }
14985
14986 return d * 1;
14987 };
14988
14989
14990 // Add the numeric 'deformatting' functions for sorting and search. This is done
14991 // in a function to provide an easy ability for the language options to add
14992 // additional methods if a non-period decimal place is used.
14993 function _addNumericSort ( decimalPlace ) {
14994 $.each(
14995 {
14996 // Plain numbers
14997 "num": function ( d ) {
14998 return __numericReplace( d, decimalPlace );
14999 },
15000
15001 // Formatted numbers
15002 "num-fmt": function ( d ) {
15003 return __numericReplace( d, decimalPlace, _re_formatted_numeric );
15004 },
15005
15006 // HTML numeric
15007 "html-num": function ( d ) {
15008 return __numericReplace( d, decimalPlace, _re_html );
15009 },
15010
15011 // HTML numeric, formatted
15012 "html-num-fmt": function ( d ) {
15013 return __numericReplace( d, decimalPlace, _re_html, _re_formatted_numeric );
15014 }
15015 },
15016 function ( key, fn ) {
15017 // Add the ordering method
15018 _ext.type.order[ key+decimalPlace+'-pre' ] = fn;
15019
15020 // For HTML types add a search formatter that will strip the HTML
15021 if ( key.match(/^html\-/) ) {
15022 _ext.type.search[ key+decimalPlace ] = _ext.type.search.html;
15023 }
15024 }
15025 );
15026 }
15027
15028
15029 // Default sort methods
15030 $.extend( _ext.type.order, {
15031 // Dates
15032 "date-pre": function ( d ) {
15033 var ts = Date.parse( d );
15034 return isNaN(ts) ? -Infinity : ts;
15035 },
15036
15037 // html
15038 "html-pre": function ( a ) {
15039 return _empty(a) ?
15040 '' :
15041 a.replace ?
15042 a.replace( /<.*?>/g, "" ).toLowerCase() :
15043 a+'';
15044 },
15045
15046 // string
15047 "string-pre": function ( a ) {
15048 // This is a little complex, but faster than always calling toString,
15049 // http://jsperf.com/tostring-v-check
15050 return _empty(a) ?
15051 '' :
15052 typeof a === 'string' ?
15053 a.toLowerCase() :
15054 ! a.toString ?
15055 '' :
15056 a.toString();
15057 },
15058
15059 // string-asc and -desc are retained only for compatibility with the old
15060 // sort methods
15061 "string-asc": function ( x, y ) {
15062 return ((x < y) ? -1 : ((x > y) ? 1 : 0));
15063 },
15064
15065 "string-desc": function ( x, y ) {
15066 return ((x < y) ? 1 : ((x > y) ? -1 : 0));
15067 }
15068 } );
15069
15070
15071 // Numeric sorting types - order doesn't matter here
15072 _addNumericSort( '' );
15073
15074
15075 $.extend( true, DataTable.ext.renderer, {
15076 header: {
15077 _: function ( settings, cell, column, classes ) {
15078 // No additional mark-up required
15079 // Attach a sort listener to update on sort - note that using the
15080 // `DT` namespace will allow the event to be removed automatically
15081 // on destroy, while the `dt` namespaced event is the one we are
15082 // listening for
15083 $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {
15084 if ( settings !== ctx ) { // need to check this this is the host
15085 return; // table, not a nested one
15086 }
15087
15088 var colIdx = column.idx;
15089
15090 cell
15091 .removeClass(
15092 classes.sSortAsc +' '+
15093 classes.sSortDesc
15094 )
15095 .addClass( columns[ colIdx ] == 'asc' ?
15096 classes.sSortAsc : columns[ colIdx ] == 'desc' ?
15097 classes.sSortDesc :
15098 column.sSortingClass
15099 );
15100 } );
15101 },
15102
15103 jqueryui: function ( settings, cell, column, classes ) {
15104 $('<div/>')
15105 .addClass( classes.sSortJUIWrapper )
15106 .append( cell.contents() )
15107 .append( $('<span/>')
15108 .addClass( classes.sSortIcon+' '+column.sSortingClassJUI )
15109 )
15110 .appendTo( cell );
15111
15112 // Attach a sort listener to update on sort
15113 $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {
15114 if ( settings !== ctx ) {
15115 return;
15116 }
15117
15118 var colIdx = column.idx;
15119
15120 cell
15121 .removeClass( classes.sSortAsc +" "+classes.sSortDesc )
15122 .addClass( columns[ colIdx ] == 'asc' ?
15123 classes.sSortAsc : columns[ colIdx ] == 'desc' ?
15124 classes.sSortDesc :
15125 column.sSortingClass
15126 );
15127
15128 cell
15129 .find( 'span.'+classes.sSortIcon )
15130 .removeClass(
15131 classes.sSortJUIAsc +" "+
15132 classes.sSortJUIDesc +" "+
15133 classes.sSortJUI +" "+
15134 classes.sSortJUIAscAllowed +" "+
15135 classes.sSortJUIDescAllowed
15136 )
15137 .addClass( columns[ colIdx ] == 'asc' ?
15138 classes.sSortJUIAsc : columns[ colIdx ] == 'desc' ?
15139 classes.sSortJUIDesc :
15140 column.sSortingClassJUI
15141 );
15142 } );
15143 }
15144 }
15145 } );
15146
15147 /*
15148 * Public helper functions. These aren't used internally by DataTables, or
15149 * called by any of the options passed into DataTables, but they can be used
15150 * externally by developers working with DataTables. They are helper functions
15151 * to make working with DataTables a little bit easier.
15152 */
15153
15154 var __htmlEscapeEntities = function ( d ) {
15155 if (Array.isArray(d)) {
15156 d = d.join(',');
15157 }
15158
15159 return typeof d === 'string' ?
15160 d
15161 .replace(/&/g, '&amp;')
15162 .replace(/</g, '&lt;')
15163 .replace(/>/g, '&gt;')
15164 .replace(/"/g, '&quot;') :
15165 d;
15166 };
15167
15168 // Common logic for moment, luxon or a date action
15169 function __mld( dt, momentFn, luxonFn, dateFn, arg1 ) {
15170 if (window.moment) {
15171 return dt[momentFn]( arg1 );
15172 }
15173 else if (window.luxon) {
15174 return dt[luxonFn]( arg1 );
15175 }
15176
15177 return dateFn ? dt[dateFn]( arg1 ) : dt;
15178 }
15179
15180
15181 var __mlWarning = false;
15182 function __mldObj (d, format, locale) {
15183 var dt;
15184
15185 if (window.moment) {
15186 dt = window.moment.utc( d, format, locale, true );
15187
15188 if (! dt.isValid()) {
15189 return null;
15190 }
15191 }
15192 else if (window.luxon) {
15193 dt = format && typeof d === 'string'
15194 ? window.luxon.DateTime.fromFormat( d, format )
15195 : window.luxon.DateTime.fromISO( d );
15196
15197 if (! dt.isValid) {
15198 return null;
15199 }
15200
15201 dt.setLocale(locale);
15202 }
15203 else if (! format) {
15204 // No format given, must be ISO
15205 dt = new Date(d);
15206 }
15207 else {
15208 if (! __mlWarning) {
15209 alert('DataTables warning: Formatted date without Moment.js or Luxon - https://datatables.net/tn/17');
15210 }
15211
15212 __mlWarning = true;
15213 }
15214
15215 return dt;
15216 }
15217
15218 // Wrapper for date, datetime and time which all operate the same way with the exception of
15219 // the output string for auto locale support
15220 function __mlHelper (localeString) {
15221 return function ( from, to, locale, def ) {
15222 // Luxon and Moment support
15223 // Argument shifting
15224 if ( arguments.length === 0 ) {
15225 locale = 'en';
15226 to = null; // means toLocaleString
15227 from = null; // means iso8601
15228 }
15229 else if ( arguments.length === 1 ) {
15230 locale = 'en';
15231 to = from;
15232 from = null;
15233 }
15234 else if ( arguments.length === 2 ) {
15235 locale = to;
15236 to = from;
15237 from = null;
15238 }
15239
15240 var typeName = 'datetime-' + to;
15241
15242 // Add type detection and sorting specific to this date format - we need to be able to identify
15243 // date type columns as such, rather than as numbers in extensions. Hence the need for this.
15244 if (! DataTable.ext.type.order[typeName]) {
15245 // The renderer will give the value to type detect as the type!
15246 DataTable.ext.type.detect.unshift(function (d) {
15247 return d === typeName ? typeName : false;
15248 });
15249
15250 // The renderer gives us Moment, Luxon or Date obects for the sorting, all of which have a
15251 // `valueOf` which gives milliseconds epoch
15252 DataTable.ext.type.order[typeName + '-asc'] = function (a, b) {
15253 var x = a.valueOf();
15254 var y = b.valueOf();
15255
15256 return x === y
15257 ? 0
15258 : x < y
15259 ? -1
15260 : 1;
15261 }
15262
15263 DataTable.ext.type.order[typeName + '-desc'] = function (a, b) {
15264 var x = a.valueOf();
15265 var y = b.valueOf();
15266
15267 return x === y
15268 ? 0
15269 : x > y
15270 ? -1
15271 : 1;
15272 }
15273 }
15274
15275 return function ( d, type ) {
15276 // Allow for a default value
15277 if (d === null || d === undefined) {
15278 if (def === '--now') {
15279 // We treat everything as UTC further down, so no changes are
15280 // made, as such need to get the local date / time as if it were
15281 // UTC
15282 var local = new Date();
15283 d = new Date( Date.UTC(
15284 local.getFullYear(), local.getMonth(), local.getDate(),
15285 local.getHours(), local.getMinutes(), local.getSeconds()
15286 ) );
15287 }
15288 else {
15289 d = '';
15290 }
15291 }
15292
15293 if (type === 'type') {
15294 // Typing uses the type name for fast matching
15295 return typeName;
15296 }
15297
15298 if (d === '') {
15299 return type !== 'sort'
15300 ? ''
15301 : __mldObj('0000-01-01 00:00:00', null, locale);
15302 }
15303
15304 // Shortcut. If `from` and `to` are the same, we are using the renderer to
15305 // format for ordering, not display - its already in the display format.
15306 if ( to !== null && from === to && type !== 'sort' && type !== 'type' && ! (d instanceof Date) ) {
15307 return d;
15308 }
15309
15310 var dt = __mldObj(d, from, locale);
15311
15312 if (dt === null) {
15313 return d;
15314 }
15315
15316 if (type === 'sort') {
15317 return dt;
15318 }
15319
15320 var formatted = to === null
15321 ? __mld(dt, 'toDate', 'toJSDate', '')[localeString]()
15322 : __mld(dt, 'format', 'toFormat', 'toISOString', to);
15323
15324 // XSS protection
15325 return type === 'display' ?
15326 __htmlEscapeEntities( formatted ) :
15327 formatted;
15328 };
15329 }
15330 }
15331
15332 // Based on locale, determine standard number formatting
15333 // Fallback for legacy browsers is US English
15334 var __thousands = ',';
15335 var __decimal = '.';
15336
15337 if (Intl) {
15338 try {
15339 var num = new Intl.NumberFormat().formatToParts(100000.1);
15340
15341 for (var i=0 ; i<num.length ; i++) {
15342 if (num[i].type === 'group') {
15343 __thousands = num[i].value;
15344 }
15345 else if (num[i].type === 'decimal') {
15346 __decimal = num[i].value;
15347 }
15348 }
15349 }
15350 catch (e) {
15351 // noop
15352 }
15353 }
15354
15355 // Formatted date time detection - use by declaring the formats you are going to use
15356 DataTable.datetime = function ( format, locale ) {
15357 var typeName = 'datetime-detect-' + format;
15358
15359 if (! locale) {
15360 locale = 'en';
15361 }
15362
15363 if (! DataTable.ext.type.order[typeName]) {
15364 DataTable.ext.type.detect.unshift(function (d) {
15365 var dt = __mldObj(d, format, locale);
15366 return d === '' || dt ? typeName : false;
15367 });
15368
15369 DataTable.ext.type.order[typeName + '-pre'] = function (d) {
15370 return __mldObj(d, format, locale) || 0;
15371 }
15372 }
15373 }
15374
15375 /**
15376 * Helpers for `columns.render`.
15377 *
15378 * The options defined here can be used with the `columns.render` initialisation
15379 * option to provide a display renderer. The following functions are defined:
15380 *
15381 * * `number` - Will format numeric data (defined by `columns.data`) for
15382 * display, retaining the original unformatted data for sorting and filtering.
15383 * It takes 5 parameters:
15384 * * `string` - Thousands grouping separator
15385 * * `string` - Decimal point indicator
15386 * * `integer` - Number of decimal points to show
15387 * * `string` (optional) - Prefix.
15388 * * `string` (optional) - Postfix (/suffix).
15389 * * `text` - Escape HTML to help prevent XSS attacks. It has no optional
15390 * parameters.
15391 *
15392 * @example
15393 * // Column definition using the number renderer
15394 * {
15395 * data: "salary",
15396 * render: $.fn.dataTable.render.number( '\'', '.', 0, '$' )
15397 * }
15398 *
15399 * @namespace
15400 */
15401 DataTable.render = {
15402 date: __mlHelper('toLocaleDateString'),
15403 datetime: __mlHelper('toLocaleString'),
15404 time: __mlHelper('toLocaleTimeString'),
15405 number: function ( thousands, decimal, precision, prefix, postfix ) {
15406 // Auto locale detection
15407 if (thousands === null || thousands === undefined) {
15408 thousands = __thousands;
15409 }
15410
15411 if (decimal === null || decimal === undefined) {
15412 decimal = __decimal;
15413 }
15414
15415 return {
15416 display: function ( d ) {
15417 if ( typeof d !== 'number' && typeof d !== 'string' ) {
15418 return d;
15419 }
15420
15421 if (d === '' || d === null) {
15422 return d;
15423 }
15424
15425 var negative = d < 0 ? '-' : '';
15426 var flo = parseFloat( d );
15427
15428 // If NaN then there isn't much formatting that we can do - just
15429 // return immediately, escaping any HTML (this was supposed to
15430 // be a number after all)
15431 if ( isNaN( flo ) ) {
15432 return __htmlEscapeEntities( d );
15433 }
15434
15435 flo = flo.toFixed( precision );
15436 d = Math.abs( flo );
15437
15438 var intPart = parseInt( d, 10 );
15439 var floatPart = precision ?
15440 decimal+(d - intPart).toFixed( precision ).substring( 2 ):
15441 '';
15442
15443 // If zero, then can't have a negative prefix
15444 if (intPart === 0 && parseFloat(floatPart) === 0) {
15445 negative = '';
15446 }
15447
15448 return negative + (prefix||'') +
15449 intPart.toString().replace(
15450 /\B(?=(\d{3})+(?!\d))/g, thousands
15451 ) +
15452 floatPart +
15453 (postfix||'');
15454 }
15455 };
15456 },
15457
15458 text: function () {
15459 return {
15460 display: __htmlEscapeEntities,
15461 filter: __htmlEscapeEntities
15462 };
15463 }
15464 };
15465
15466
15467 /*
15468 * This is really a good bit rubbish this method of exposing the internal methods
15469 * publicly... - To be fixed in 2.0 using methods on the prototype
15470 */
15471
15472
15473 /**
15474 * Create a wrapper function for exporting an internal functions to an external API.
15475 * @param {string} fn API function name
15476 * @returns {function} wrapped function
15477 * @memberof DataTable#internal
15478 */
15479 function _fnExternApiFunc (fn)
15480 {
15481 return function() {
15482 var args = [_fnSettingsFromNode( this[DataTable.ext.iApiIndex] )].concat(
15483 Array.prototype.slice.call(arguments)
15484 );
15485 return DataTable.ext.internal[fn].apply( this, args );
15486 };
15487 }
15488
15489
15490 /**
15491 * Reference to internal functions for use by plug-in developers. Note that
15492 * these methods are references to internal functions and are considered to be
15493 * private. If you use these methods, be aware that they are liable to change
15494 * between versions.
15495 * @namespace
15496 */
15497 $.extend( DataTable.ext.internal, {
15498 _fnExternApiFunc: _fnExternApiFunc,
15499 _fnBuildAjax: _fnBuildAjax,
15500 _fnAjaxUpdate: _fnAjaxUpdate,
15501 _fnAjaxParameters: _fnAjaxParameters,
15502 _fnAjaxUpdateDraw: _fnAjaxUpdateDraw,
15503 _fnAjaxDataSrc: _fnAjaxDataSrc,
15504 _fnAddColumn: _fnAddColumn,
15505 _fnColumnOptions: _fnColumnOptions,
15506 _fnAdjustColumnSizing: _fnAdjustColumnSizing,
15507 _fnVisibleToColumnIndex: _fnVisibleToColumnIndex,
15508 _fnColumnIndexToVisible: _fnColumnIndexToVisible,
15509 _fnVisbleColumns: _fnVisbleColumns,
15510 _fnGetColumns: _fnGetColumns,
15511 _fnColumnTypes: _fnColumnTypes,
15512 _fnApplyColumnDefs: _fnApplyColumnDefs,
15513 _fnHungarianMap: _fnHungarianMap,
15514 _fnCamelToHungarian: _fnCamelToHungarian,
15515 _fnLanguageCompat: _fnLanguageCompat,
15516 _fnBrowserDetect: _fnBrowserDetect,
15517 _fnAddData: _fnAddData,
15518 _fnAddTr: _fnAddTr,
15519 _fnNodeToDataIndex: _fnNodeToDataIndex,
15520 _fnNodeToColumnIndex: _fnNodeToColumnIndex,
15521 _fnGetCellData: _fnGetCellData,
15522 _fnSetCellData: _fnSetCellData,
15523 _fnSplitObjNotation: _fnSplitObjNotation,
15524 _fnGetObjectDataFn: _fnGetObjectDataFn,
15525 _fnSetObjectDataFn: _fnSetObjectDataFn,
15526 _fnGetDataMaster: _fnGetDataMaster,
15527 _fnClearTable: _fnClearTable,
15528 _fnDeleteIndex: _fnDeleteIndex,
15529 _fnInvalidate: _fnInvalidate,
15530 _fnGetRowElements: _fnGetRowElements,
15531 _fnCreateTr: _fnCreateTr,
15532 _fnBuildHead: _fnBuildHead,
15533 _fnDrawHead: _fnDrawHead,
15534 _fnDraw: _fnDraw,
15535 _fnReDraw: _fnReDraw,
15536 _fnAddOptionsHtml: _fnAddOptionsHtml,
15537 _fnDetectHeader: _fnDetectHeader,
15538 _fnGetUniqueThs: _fnGetUniqueThs,
15539 _fnFeatureHtmlFilter: _fnFeatureHtmlFilter,
15540 _fnFilterComplete: _fnFilterComplete,
15541 _fnFilterCustom: _fnFilterCustom,
15542 _fnFilterColumn: _fnFilterColumn,
15543 _fnFilter: _fnFilter,
15544 _fnFilterCreateSearch: _fnFilterCreateSearch,
15545 _fnEscapeRegex: _fnEscapeRegex,
15546 _fnFilterData: _fnFilterData,
15547 _fnFeatureHtmlInfo: _fnFeatureHtmlInfo,
15548 _fnUpdateInfo: _fnUpdateInfo,
15549 _fnInfoMacros: _fnInfoMacros,
15550 _fnInitialise: _fnInitialise,
15551 _fnInitComplete: _fnInitComplete,
15552 _fnLengthChange: _fnLengthChange,
15553 _fnFeatureHtmlLength: _fnFeatureHtmlLength,
15554 _fnFeatureHtmlPaginate: _fnFeatureHtmlPaginate,
15555 _fnPageChange: _fnPageChange,
15556 _fnFeatureHtmlProcessing: _fnFeatureHtmlProcessing,
15557 _fnProcessingDisplay: _fnProcessingDisplay,
15558 _fnFeatureHtmlTable: _fnFeatureHtmlTable,
15559 _fnScrollDraw: _fnScrollDraw,
15560 _fnApplyToChildren: _fnApplyToChildren,
15561 _fnCalculateColumnWidths: _fnCalculateColumnWidths,
15562 _fnThrottle: _fnThrottle,
15563 _fnConvertToWidth: _fnConvertToWidth,
15564 _fnGetWidestNode: _fnGetWidestNode,
15565 _fnGetMaxLenString: _fnGetMaxLenString,
15566 _fnStringToCss: _fnStringToCss,
15567 _fnSortFlatten: _fnSortFlatten,
15568 _fnSort: _fnSort,
15569 _fnSortAria: _fnSortAria,
15570 _fnSortListener: _fnSortListener,
15571 _fnSortAttachListener: _fnSortAttachListener,
15572 _fnSortingClasses: _fnSortingClasses,
15573 _fnSortData: _fnSortData,
15574 _fnSaveState: _fnSaveState,
15575 _fnLoadState: _fnLoadState,
15576 _fnImplementState: _fnImplementState,
15577 _fnSettingsFromNode: _fnSettingsFromNode,
15578 _fnLog: _fnLog,
15579 _fnMap: _fnMap,
15580 _fnBindAction: _fnBindAction,
15581 _fnCallbackReg: _fnCallbackReg,
15582 _fnCallbackFire: _fnCallbackFire,
15583 _fnLengthOverflow: _fnLengthOverflow,
15584 _fnRenderer: _fnRenderer,
15585 _fnDataSource: _fnDataSource,
15586 _fnRowAttributes: _fnRowAttributes,
15587 _fnExtend: _fnExtend,
15588 _fnCalculateEnd: function () {} // Used by a lot of plug-ins, but redundant
15589 // in 1.10, so this dead-end function is
15590 // added to prevent errors
15591 } );
15592
15593
15594 // jQuery access
15595 $.fn.dataTable = DataTable;
15596
15597 // Provide access to the host jQuery object (circular reference)
15598 DataTable.$ = $;
15599
15600 // Legacy aliases
15601 $.fn.dataTableSettings = DataTable.settings;
15602 $.fn.dataTableExt = DataTable.ext;
15603
15604 // With a capital `D` we return a DataTables API instance rather than a
15605 // jQuery object
15606 $.fn.DataTable = function ( opts ) {
15607 return $(this).dataTable( opts ).api();
15608 };
15609
15610 // All properties that are available to $.fn.dataTable should also be
15611 // available on $.fn.DataTable
15612 $.each( DataTable, function ( prop, val ) {
15613 $.fn.DataTable[ prop ] = val;
15614 } );
15615
15616 return DataTable;
15617}));
15618
15619
15620/*! DataTables Bootstrap 5 integration
15621 * 2020 SpryMedia Ltd - datatables.net/license
15622 */
15623
15624(function( factory ){
15625 if ( typeof define === 'function' && define.amd ) {
15626 // AMD
15627 define( ['jquery', 'datatables.net'], function ( $ ) {
15628 return factory( $, window, document );
15629 } );
15630 }
15631 else if ( typeof exports === 'object' ) {
15632 // CommonJS
15633 module.exports = function (root, $) {
15634 if ( ! root ) {
15635 // CommonJS environments without a window global must pass a
15636 // root. This will give an error otherwise
15637 root = window;
15638 }
15639
15640 if ( ! $ ) {
15641 $ = typeof window !== 'undefined' ? // jQuery's factory checks for a global window
15642 require('jquery') :
15643 require('jquery')( root );
15644 }
15645
15646 if ( ! $.fn.dataTable ) {
15647 require('datatables.net')(root, $);
15648 }
15649
15650
15651 return factory( $, root, root.document );
15652 };
15653 }
15654 else {
15655 // Browser
15656 factory( jQuery, window, document );
15657 }
15658}(function( $, window, document, undefined ) {
15659'use strict';
15660var DataTable = $.fn.dataTable;
15661
15662
15663
15664/**
15665 * DataTables integration for Bootstrap 5. This requires Bootstrap 5 and
15666 * DataTables 1.10 or newer.
15667 *
15668 * This file sets the defaults and adds options to DataTables to style its
15669 * controls using Bootstrap. See http://datatables.net/manual/styling/bootstrap
15670 * for further information.
15671 */
15672
15673/* Set the defaults for DataTables initialisation */
15674$.extend( true, DataTable.defaults, {
15675 dom:
15676 "<'row'<'col-sm-12 col-md-6'l><'col-sm-12 col-md-6'f>>" +
15677 "<'row dt-row'<'col-sm-12'tr>>" +
15678 "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
15679 renderer: 'bootstrap'
15680} );
15681
15682
15683/* Default class modification */
15684$.extend( DataTable.ext.classes, {
15685 sWrapper: "dataTables_wrapper dt-bootstrap5",
15686 sFilterInput: "form-control form-control-sm",
15687 sLengthSelect: "form-select form-select-sm",
15688 sProcessing: "dataTables_processing card",
15689 sPageButton: "paginate_button page-item"
15690} );
15691
15692
15693/* Bootstrap paging button renderer */
15694DataTable.ext.renderer.pageButton.bootstrap = function ( settings, host, idx, buttons, page, pages ) {
15695 var api = new DataTable.Api( settings );
15696 var classes = settings.oClasses;
15697 var lang = settings.oLanguage.oPaginate;
15698 var aria = settings.oLanguage.oAria.paginate || {};
15699 var btnDisplay, btnClass;
15700
15701 var attach = function( container, buttons ) {
15702 var i, ien, node, button;
15703 var clickHandler = function ( e ) {
15704 e.preventDefault();
15705 if ( !$(e.currentTarget).hasClass('disabled') && api.page() != e.data.action ) {
15706 api.page( e.data.action ).draw( 'page' );
15707 }
15708 };
15709
15710 for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
15711 button = buttons[i];
15712
15713 if ( Array.isArray( button ) ) {
15714 attach( container, button );
15715 }
15716 else {
15717 btnDisplay = '';
15718 btnClass = '';
15719
15720 switch ( button ) {
15721 case 'ellipsis':
15722 btnDisplay = '&#x2026;';
15723 btnClass = 'disabled';
15724 break;
15725
15726 case 'first':
15727 btnDisplay = lang.sFirst;
15728 btnClass = button + (page > 0 ?
15729 '' : ' disabled');
15730 break;
15731
15732 case 'previous':
15733 btnDisplay = lang.sPrevious;
15734 btnClass = button + (page > 0 ?
15735 '' : ' disabled');
15736 break;
15737
15738 case 'next':
15739 btnDisplay = lang.sNext;
15740 btnClass = button + (page < pages-1 ?
15741 '' : ' disabled');
15742 break;
15743
15744 case 'last':
15745 btnDisplay = lang.sLast;
15746 btnClass = button + (page < pages-1 ?
15747 '' : ' disabled');
15748 break;
15749
15750 default:
15751 btnDisplay = button + 1;
15752 btnClass = page === button ?
15753 'active' : '';
15754 break;
15755 }
15756
15757 if ( btnDisplay ) {
15758 node = $('<li>', {
15759 'class': classes.sPageButton+' '+btnClass,
15760 'id': idx === 0 && typeof button === 'string' ?
15761 settings.sTableId +'_'+ button :
15762 null
15763 } )
15764 .append( $('<a>', {
15765 'href': '#',
15766 'aria-controls': settings.sTableId,
15767 'aria-label': aria[ button ],
15768 'data-dt-idx': button,
15769 'tabindex': settings.iTabIndex,
15770 'class': 'page-link'
15771 } )
15772 .html( btnDisplay )
15773 )
15774 .appendTo( container );
15775
15776 settings.oApi._fnBindAction(
15777 node, {action: button}, clickHandler
15778 );
15779 }
15780 }
15781 }
15782 };
15783
15784 var hostEl = $(host);
15785 // IE9 throws an 'unknown error' if document.activeElement is used
15786 // inside an iframe or frame.
15787 var activeEl;
15788
15789 try {
15790 // Because this approach is destroying and recreating the paging
15791 // elements, focus is lost on the select button which is bad for
15792 // accessibility. So we want to restore focus once the draw has
15793 // completed
15794 activeEl = hostEl.find(document.activeElement).data('dt-idx');
15795 }
15796 catch (e) {}
15797
15798 var paginationEl = hostEl.children('ul.pagination');
15799
15800 if (paginationEl.length) {
15801 paginationEl.empty();
15802 }
15803 else {
Matthias Andreas Benkardd1f5b682023-11-18 13:18:30 +010015804 paginationEl = hostEl.html('<ul/>').children('ul').addClass('pagination pagination-sm');
Matthias Andreas Benkard1ba53812022-12-27 17:32:58 +010015805 }
15806
15807 attach(
15808 paginationEl,
15809 buttons
15810 );
15811
15812 if ( activeEl !== undefined ) {
15813 hostEl.find('[data-dt-idx='+activeEl+']').trigger('focus');
15814 }
15815};
15816
15817
15818return DataTable;
15819}));
15820
15821
15822/*! Responsive 2.4.0
15823 * 2014-2022 SpryMedia Ltd - datatables.net/license
15824 */
15825
15826(function( factory ){
15827 if ( typeof define === 'function' && define.amd ) {
15828 // AMD
15829 define( ['jquery', 'datatables.net'], function ( $ ) {
15830 return factory( $, window, document );
15831 } );
15832 }
15833 else if ( typeof exports === 'object' ) {
15834 // CommonJS
15835 module.exports = function (root, $) {
15836 if ( ! root ) {
15837 // CommonJS environments without a window global must pass a
15838 // root. This will give an error otherwise
15839 root = window;
15840 }
15841
15842 if ( ! $ ) {
15843 $ = typeof window !== 'undefined' ? // jQuery's factory checks for a global window
15844 require('jquery') :
15845 require('jquery')( root );
15846 }
15847
15848 if ( ! $.fn.dataTable ) {
15849 require('datatables.net')(root, $);
15850 }
15851
15852
15853 return factory( $, root, root.document );
15854 };
15855 }
15856 else {
15857 // Browser
15858 factory( jQuery, window, document );
15859 }
15860}(function( $, window, document, undefined ) {
15861'use strict';
15862var DataTable = $.fn.dataTable;
15863
15864
15865
15866/**
15867 * @summary Responsive
15868 * @description Responsive tables plug-in for DataTables
15869 * @version 2.4.0
15870 * @author SpryMedia Ltd (www.sprymedia.co.uk)
15871 * @contact www.sprymedia.co.uk/contact
15872 * @copyright SpryMedia Ltd.
15873 *
15874 * This source file is free software, available under the following license:
15875 * MIT license - http://datatables.net/license/mit
15876 *
15877 * This source file is distributed in the hope that it will be useful, but
15878 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15879 * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
15880 *
15881 * For details please refer to: http://www.datatables.net
15882 */
15883
15884/**
15885 * Responsive is a plug-in for the DataTables library that makes use of
15886 * DataTables' ability to change the visibility of columns, changing the
15887 * visibility of columns so the displayed columns fit into the table container.
15888 * The end result is that complex tables will be dynamically adjusted to fit
15889 * into the viewport, be it on a desktop, tablet or mobile browser.
15890 *
15891 * Responsive for DataTables has two modes of operation, which can used
15892 * individually or combined:
15893 *
15894 * * Class name based control - columns assigned class names that match the
15895 * breakpoint logic can be shown / hidden as required for each breakpoint.
15896 * * Automatic control - columns are automatically hidden when there is no
15897 * room left to display them. Columns removed from the right.
15898 *
15899 * In additional to column visibility control, Responsive also has built into
15900 * options to use DataTables' child row display to show / hide the information
15901 * from the table that has been hidden. There are also two modes of operation
15902 * for this child row display:
15903 *
15904 * * Inline - when the control element that the user can use to show / hide
15905 * child rows is displayed inside the first column of the table.
15906 * * Column - where a whole column is dedicated to be the show / hide control.
15907 *
15908 * Initialisation of Responsive is performed by:
15909 *
15910 * * Adding the class `responsive` or `dt-responsive` to the table. In this case
15911 * Responsive will automatically be initialised with the default configuration
15912 * options when the DataTable is created.
15913 * * Using the `responsive` option in the DataTables configuration options. This
15914 * can also be used to specify the configuration options, or simply set to
15915 * `true` to use the defaults.
15916 *
15917 * @class
15918 * @param {object} settings DataTables settings object for the host table
15919 * @param {object} [opts] Configuration options
15920 * @requires jQuery 1.7+
15921 * @requires DataTables 1.10.3+
15922 *
15923 * @example
15924 * $('#example').DataTable( {
15925 * responsive: true
15926 * } );
15927 * } );
15928 */
15929var Responsive = function ( settings, opts ) {
15930 // Sanity check that we are using DataTables 1.10 or newer
15931 if ( ! DataTable.versionCheck || ! DataTable.versionCheck( '1.10.10' ) ) {
15932 throw 'DataTables Responsive requires DataTables 1.10.10 or newer';
15933 }
15934
15935 this.s = {
15936 childNodeStore: {},
15937 columns: [],
15938 current: [],
15939 dt: new DataTable.Api( settings )
15940 };
15941
15942 // Check if responsive has already been initialised on this table
15943 if ( this.s.dt.settings()[0].responsive ) {
15944 return;
15945 }
15946
15947 // details is an object, but for simplicity the user can give it as a string
15948 // or a boolean
15949 if ( opts && typeof opts.details === 'string' ) {
15950 opts.details = { type: opts.details };
15951 }
15952 else if ( opts && opts.details === false ) {
15953 opts.details = { type: false };
15954 }
15955 else if ( opts && opts.details === true ) {
15956 opts.details = { type: 'inline' };
15957 }
15958
15959 this.c = $.extend( true, {}, Responsive.defaults, DataTable.defaults.responsive, opts );
15960 settings.responsive = this;
15961 this._constructor();
15962};
15963
15964$.extend( Responsive.prototype, {
15965 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
15966 * Constructor
15967 */
15968
15969 /**
15970 * Initialise the Responsive instance
15971 *
15972 * @private
15973 */
15974 _constructor: function ()
15975 {
15976 var that = this;
15977 var dt = this.s.dt;
15978 var dtPrivateSettings = dt.settings()[0];
15979 var oldWindowWidth = $(window).innerWidth();
15980
15981 dt.settings()[0]._responsive = this;
15982
15983 // Use DataTables' throttle function to avoid processor thrashing on
15984 // resize
15985 $(window).on( 'resize.dtr orientationchange.dtr', DataTable.util.throttle( function () {
15986 // iOS has a bug whereby resize can fire when only scrolling
15987 // See: http://stackoverflow.com/questions/8898412
15988 var width = $(window).innerWidth();
15989
15990 if ( width !== oldWindowWidth ) {
15991 that._resize();
15992 oldWindowWidth = width;
15993 }
15994 } ) );
15995
15996 // DataTables doesn't currently trigger an event when a row is added, so
15997 // we need to hook into its private API to enforce the hidden rows when
15998 // new data is added
15999 dtPrivateSettings.oApi._fnCallbackReg( dtPrivateSettings, 'aoRowCreatedCallback', function (tr, data, idx) {
16000 if ( $.inArray( false, that.s.current ) !== -1 ) {
16001 $('>td, >th', tr).each( function ( i ) {
16002 var idx = dt.column.index( 'toData', i );
16003
16004 if ( that.s.current[idx] === false ) {
16005 $(this).css('display', 'none');
16006 }
16007 } );
16008 }
16009 } );
16010
16011 // Destroy event handler
16012 dt.on( 'destroy.dtr', function () {
16013 dt.off( '.dtr' );
16014 $( dt.table().body() ).off( '.dtr' );
16015 $(window).off( 'resize.dtr orientationchange.dtr' );
16016 dt.cells('.dtr-control').nodes().to$().removeClass('dtr-control');
16017
16018 // Restore the columns that we've hidden
16019 $.each( that.s.current, function ( i, val ) {
16020 if ( val === false ) {
16021 that._setColumnVis( i, true );
16022 }
16023 } );
16024 } );
16025
16026 // Reorder the breakpoints array here in case they have been added out
16027 // of order
16028 this.c.breakpoints.sort( function (a, b) {
16029 return a.width < b.width ? 1 :
16030 a.width > b.width ? -1 : 0;
16031 } );
16032
16033 this._classLogic();
16034 this._resizeAuto();
16035
16036 // Details handler
16037 var details = this.c.details;
16038
16039 if ( details.type !== false ) {
16040 that._detailsInit();
16041
16042 // DataTables will trigger this event on every column it shows and
16043 // hides individually
16044 dt.on( 'column-visibility.dtr', function () {
16045 // Use a small debounce to allow multiple columns to be set together
16046 if ( that._timer ) {
16047 clearTimeout( that._timer );
16048 }
16049
16050 that._timer = setTimeout( function () {
16051 that._timer = null;
16052
16053 that._classLogic();
16054 that._resizeAuto();
16055 that._resize(true);
16056
16057 that._redrawChildren();
16058 }, 100 );
16059 } );
16060
16061 // Redraw the details box on each draw which will happen if the data
16062 // has changed. This is used until DataTables implements a native
16063 // `updated` event for rows
16064 dt.on( 'draw.dtr', function () {
16065 that._redrawChildren();
16066 } );
16067
16068 $(dt.table().node()).addClass( 'dtr-'+details.type );
16069 }
16070
16071 dt.on( 'column-reorder.dtr', function (e, settings, details) {
16072 that._classLogic();
16073 that._resizeAuto();
16074 that._resize(true);
16075 } );
16076
16077 // Change in column sizes means we need to calc
16078 dt.on( 'column-sizing.dtr', function () {
16079 that._resizeAuto();
16080 that._resize();
16081 });
16082
16083 // DT2 let's us tell it if we are hiding columns
16084 dt.on( 'column-calc.dt', function (e, d) {
16085 var curr = that.s.current;
16086
16087 for (var i=0 ; i<curr.length ; i++) {
16088 var idx = d.visible.indexOf(i);
16089
16090 if (curr[i] === false && idx >= 0) {
16091 d.visible.splice(idx, 1);
16092 }
16093 }
16094 } );
16095
16096 // On Ajax reload we want to reopen any child rows which are displayed
16097 // by responsive
16098 dt.on( 'preXhr.dtr', function () {
16099 var rowIds = [];
16100 dt.rows().every( function () {
16101 if ( this.child.isShown() ) {
16102 rowIds.push( this.id(true) );
16103 }
16104 } );
16105
16106 dt.one( 'draw.dtr', function () {
16107 that._resizeAuto();
16108 that._resize();
16109
16110 dt.rows( rowIds ).every( function () {
16111 that._detailsDisplay( this, false );
16112 } );
16113 } );
16114 });
16115
16116 dt
16117 .on( 'draw.dtr', function () {
16118 that._controlClass();
16119 })
16120 .on( 'init.dtr', function (e, settings, details) {
16121 if ( e.namespace !== 'dt' ) {
16122 return;
16123 }
16124
16125 that._resizeAuto();
16126 that._resize();
16127
16128 // If columns were hidden, then DataTables needs to adjust the
16129 // column sizing
16130 if ( $.inArray( false, that.s.current ) ) {
16131 dt.columns.adjust();
16132 }
16133 } );
16134
16135 // First pass - draw the table for the current viewport size
16136 this._resize();
16137 },
16138
16139
16140 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16141 * Private methods
16142 */
16143
16144 /**
16145 * Get and store nodes from a cell - use for node moving renderers
16146 *
16147 * @param {*} dt DT instance
16148 * @param {*} row Row index
16149 * @param {*} col Column index
16150 */
16151 _childNodes: function( dt, row, col ) {
16152 var name = row+'-'+col;
16153
16154 if ( this.s.childNodeStore[ name ] ) {
16155 return this.s.childNodeStore[ name ];
16156 }
16157
16158 // https://jsperf.com/childnodes-array-slice-vs-loop
16159 var nodes = [];
16160 var children = dt.cell( row, col ).node().childNodes;
16161 for ( var i=0, ien=children.length ; i<ien ; i++ ) {
16162 nodes.push( children[i] );
16163 }
16164
16165 this.s.childNodeStore[ name ] = nodes;
16166
16167 return nodes;
16168 },
16169
16170 /**
16171 * Restore nodes from the cache to a table cell
16172 *
16173 * @param {*} dt DT instance
16174 * @param {*} row Row index
16175 * @param {*} col Column index
16176 */
16177 _childNodesRestore: function( dt, row, col ) {
16178 var name = row+'-'+col;
16179
16180 if ( ! this.s.childNodeStore[ name ] ) {
16181 return;
16182 }
16183
16184 var node = dt.cell( row, col ).node();
16185 var store = this.s.childNodeStore[ name ];
16186 var parent = store[0].parentNode;
16187 var parentChildren = parent.childNodes;
16188 var a = [];
16189
16190 for ( var i=0, ien=parentChildren.length ; i<ien ; i++ ) {
16191 a.push( parentChildren[i] );
16192 }
16193
16194 for ( var j=0, jen=a.length ; j<jen ; j++ ) {
16195 node.appendChild( a[j] );
16196 }
16197
16198 this.s.childNodeStore[ name ] = undefined;
16199 },
16200
16201 /**
16202 * Calculate the visibility for the columns in a table for a given
16203 * breakpoint. The result is pre-determined based on the class logic if
16204 * class names are used to control all columns, but the width of the table
16205 * is also used if there are columns which are to be automatically shown
16206 * and hidden.
16207 *
16208 * @param {string} breakpoint Breakpoint name to use for the calculation
16209 * @return {array} Array of boolean values initiating the visibility of each
16210 * column.
16211 * @private
16212 */
16213 _columnsVisiblity: function ( breakpoint )
16214 {
16215 var dt = this.s.dt;
16216 var columns = this.s.columns;
16217 var i, ien;
16218
16219 // Create an array that defines the column ordering based first on the
16220 // column's priority, and secondly the column index. This allows the
16221 // columns to be removed from the right if the priority matches
16222 var order = columns
16223 .map( function ( col, idx ) {
16224 return {
16225 columnIdx: idx,
16226 priority: col.priority
16227 };
16228 } )
16229 .sort( function ( a, b ) {
16230 if ( a.priority !== b.priority ) {
16231 return a.priority - b.priority;
16232 }
16233 return a.columnIdx - b.columnIdx;
16234 } );
16235
16236 // Class logic - determine which columns are in this breakpoint based
16237 // on the classes. If no class control (i.e. `auto`) then `-` is used
16238 // to indicate this to the rest of the function
16239 var display = $.map( columns, function ( col, i ) {
16240 if ( dt.column(i).visible() === false ) {
16241 return 'not-visible';
16242 }
16243 return col.auto && col.minWidth === null ?
16244 false :
16245 col.auto === true ?
16246 '-' :
16247 $.inArray( breakpoint, col.includeIn ) !== -1;
16248 } );
16249
16250 // Auto column control - first pass: how much width is taken by the
16251 // ones that must be included from the non-auto columns
16252 var requiredWidth = 0;
16253 for ( i=0, ien=display.length ; i<ien ; i++ ) {
16254 if ( display[i] === true ) {
16255 requiredWidth += columns[i].minWidth;
16256 }
16257 }
16258
16259 // Second pass, use up any remaining width for other columns. For
16260 // scrolling tables we need to subtract the width of the scrollbar. It
16261 // may not be requires which makes this sub-optimal, but it would
16262 // require another full redraw to make complete use of those extra few
16263 // pixels
16264 var scrolling = dt.settings()[0].oScroll;
16265 var bar = scrolling.sY || scrolling.sX ? scrolling.iBarWidth : 0;
16266 var widthAvailable = dt.table().container().offsetWidth - bar;
16267 var usedWidth = widthAvailable - requiredWidth;
16268
16269 // Control column needs to always be included. This makes it sub-
16270 // optimal in terms of using the available with, but to stop layout
16271 // thrashing or overflow. Also we need to account for the control column
16272 // width first so we know how much width is available for the other
16273 // columns, since the control column might not be the first one shown
16274 for ( i=0, ien=display.length ; i<ien ; i++ ) {
16275 if ( columns[i].control ) {
16276 usedWidth -= columns[i].minWidth;
16277 }
16278 }
16279
16280 // Allow columns to be shown (counting by priority and then right to
16281 // left) until we run out of room
16282 var empty = false;
16283 for ( i=0, ien=order.length ; i<ien ; i++ ) {
16284 var colIdx = order[i].columnIdx;
16285
16286 if ( display[colIdx] === '-' && ! columns[colIdx].control && columns[colIdx].minWidth ) {
16287 // Once we've found a column that won't fit we don't let any
16288 // others display either, or columns might disappear in the
16289 // middle of the table
16290 if ( empty || usedWidth - columns[colIdx].minWidth < 0 ) {
16291 empty = true;
16292 display[colIdx] = false;
16293 }
16294 else {
16295 display[colIdx] = true;
16296 }
16297
16298 usedWidth -= columns[colIdx].minWidth;
16299 }
16300 }
16301
16302 // Determine if the 'control' column should be shown (if there is one).
16303 // This is the case when there is a hidden column (that is not the
16304 // control column). The two loops look inefficient here, but they are
16305 // trivial and will fly through. We need to know the outcome from the
16306 // first , before the action in the second can be taken
16307 var showControl = false;
16308
16309 for ( i=0, ien=columns.length ; i<ien ; i++ ) {
16310 if ( ! columns[i].control && ! columns[i].never && display[i] === false ) {
16311 showControl = true;
16312 break;
16313 }
16314 }
16315
16316 for ( i=0, ien=columns.length ; i<ien ; i++ ) {
16317 if ( columns[i].control ) {
16318 display[i] = showControl;
16319 }
16320
16321 // Replace not visible string with false from the control column detection above
16322 if ( display[i] === 'not-visible' ) {
16323 display[i] = false;
16324 }
16325 }
16326
16327 // Finally we need to make sure that there is at least one column that
16328 // is visible
16329 if ( $.inArray( true, display ) === -1 ) {
16330 display[0] = true;
16331 }
16332
16333 return display;
16334 },
16335
16336
16337 /**
16338 * Create the internal `columns` array with information about the columns
16339 * for the table. This includes determining which breakpoints the column
16340 * will appear in, based upon class names in the column, which makes up the
16341 * vast majority of this method.
16342 *
16343 * @private
16344 */
16345 _classLogic: function ()
16346 {
16347 var that = this;
16348 var calc = {};
16349 var breakpoints = this.c.breakpoints;
16350 var dt = this.s.dt;
16351 var columns = dt.columns().eq(0).map( function (i) {
16352 var column = this.column(i);
16353 var className = column.header().className;
16354 var priority = dt.settings()[0].aoColumns[i].responsivePriority;
16355 var dataPriority = column.header().getAttribute('data-priority');
16356
16357 if ( priority === undefined ) {
16358 priority = dataPriority === undefined || dataPriority === null?
16359 10000 :
16360 dataPriority * 1;
16361 }
16362
16363 return {
16364 className: className,
16365 includeIn: [],
16366 auto: false,
16367 control: false,
16368 never: className.match(/\b(dtr\-)?never\b/) ? true : false,
16369 priority: priority
16370 };
16371 } );
16372
16373 // Simply add a breakpoint to `includeIn` array, ensuring that there are
16374 // no duplicates
16375 var add = function ( colIdx, name ) {
16376 var includeIn = columns[ colIdx ].includeIn;
16377
16378 if ( $.inArray( name, includeIn ) === -1 ) {
16379 includeIn.push( name );
16380 }
16381 };
16382
16383 var column = function ( colIdx, name, operator, matched ) {
16384 var size, i, ien;
16385
16386 if ( ! operator ) {
16387 columns[ colIdx ].includeIn.push( name );
16388 }
16389 else if ( operator === 'max-' ) {
16390 // Add this breakpoint and all smaller
16391 size = that._find( name ).width;
16392
16393 for ( i=0, ien=breakpoints.length ; i<ien ; i++ ) {
16394 if ( breakpoints[i].width <= size ) {
16395 add( colIdx, breakpoints[i].name );
16396 }
16397 }
16398 }
16399 else if ( operator === 'min-' ) {
16400 // Add this breakpoint and all larger
16401 size = that._find( name ).width;
16402
16403 for ( i=0, ien=breakpoints.length ; i<ien ; i++ ) {
16404 if ( breakpoints[i].width >= size ) {
16405 add( colIdx, breakpoints[i].name );
16406 }
16407 }
16408 }
16409 else if ( operator === 'not-' ) {
16410 // Add all but this breakpoint
16411 for ( i=0, ien=breakpoints.length ; i<ien ; i++ ) {
16412 if ( breakpoints[i].name.indexOf( matched ) === -1 ) {
16413 add( colIdx, breakpoints[i].name );
16414 }
16415 }
16416 }
16417 };
16418
16419 // Loop over each column and determine if it has a responsive control
16420 // class
16421 columns.each( function ( col, i ) {
16422 var classNames = col.className.split(' ');
16423 var hasClass = false;
16424
16425 // Split the class name up so multiple rules can be applied if needed
16426 for ( var k=0, ken=classNames.length ; k<ken ; k++ ) {
16427 var className = classNames[k].trim();
16428
16429 if ( className === 'all' || className === 'dtr-all' ) {
16430 // Include in all
16431 hasClass = true;
16432 col.includeIn = $.map( breakpoints, function (a) {
16433 return a.name;
16434 } );
16435 return;
16436 }
16437 else if ( className === 'none' || className === 'dtr-none' || col.never ) {
16438 // Include in none (default) and no auto
16439 hasClass = true;
16440 return;
16441 }
16442 else if ( className === 'control' || className === 'dtr-control' ) {
16443 // Special column that is only visible, when one of the other
16444 // columns is hidden. This is used for the details control
16445 hasClass = true;
16446 col.control = true;
16447 return;
16448 }
16449
16450 $.each( breakpoints, function ( j, breakpoint ) {
16451 // Does this column have a class that matches this breakpoint?
16452 var brokenPoint = breakpoint.name.split('-');
16453 var re = new RegExp( '(min\\-|max\\-|not\\-)?('+brokenPoint[0]+')(\\-[_a-zA-Z0-9])?' );
16454 var match = className.match( re );
16455
16456 if ( match ) {
16457 hasClass = true;
16458
16459 if ( match[2] === brokenPoint[0] && match[3] === '-'+brokenPoint[1] ) {
16460 // Class name matches breakpoint name fully
16461 column( i, breakpoint.name, match[1], match[2]+match[3] );
16462 }
16463 else if ( match[2] === brokenPoint[0] && ! match[3] ) {
16464 // Class name matched primary breakpoint name with no qualifier
16465 column( i, breakpoint.name, match[1], match[2] );
16466 }
16467 }
16468 } );
16469 }
16470
16471 // If there was no control class, then automatic sizing is used
16472 if ( ! hasClass ) {
16473 col.auto = true;
16474 }
16475 } );
16476
16477 this.s.columns = columns;
16478 },
16479
16480 /**
16481 * Update the cells to show the correct control class / button
16482 * @private
16483 */
16484 _controlClass: function ()
16485 {
16486 if ( this.c.details.type === 'inline' ) {
16487 var dt = this.s.dt;
16488 var columnsVis = this.s.current;
16489 var firstVisible = $.inArray(true, columnsVis);
16490
16491 // Remove from any cells which shouldn't have it
16492 dt.cells(
16493 null,
16494 function(idx) {
16495 return idx !== firstVisible;
16496 },
16497 {page: 'current'}
16498 )
16499 .nodes()
16500 .to$()
16501 .filter('.dtr-control')
16502 .removeClass('dtr-control');
16503
16504 dt.cells(null, firstVisible, {page: 'current'})
16505 .nodes()
16506 .to$()
16507 .addClass('dtr-control');
16508 }
16509 },
16510
16511 /**
16512 * Show the details for the child row
16513 *
16514 * @param {DataTables.Api} row API instance for the row
16515 * @param {boolean} update Update flag
16516 * @private
16517 */
16518 _detailsDisplay: function ( row, update )
16519 {
16520 var that = this;
16521 var dt = this.s.dt;
16522 var details = this.c.details;
16523
16524 if ( details && details.type !== false ) {
16525 var renderer = typeof details.renderer === 'string'
16526 ? Responsive.renderer[details.renderer]()
16527 : details.renderer;
16528
16529 var res = details.display( row, update, function () {
16530 return renderer.call(
16531 that, dt, row[0], that._detailsObj(row[0])
16532 );
16533 } );
16534
16535 if ( res === true || res === false ) {
16536 $(dt.table().node()).triggerHandler( 'responsive-display.dt', [dt, row, res, update] );
16537 }
16538 }
16539 },
16540
16541
16542 /**
16543 * Initialisation for the details handler
16544 *
16545 * @private
16546 */
16547 _detailsInit: function ()
16548 {
16549 var that = this;
16550 var dt = this.s.dt;
16551 var details = this.c.details;
16552
16553 // The inline type always uses the first child as the target
16554 if ( details.type === 'inline' ) {
16555 details.target = 'td.dtr-control, th.dtr-control';
16556 }
16557
16558 // Keyboard accessibility
16559 dt.on( 'draw.dtr', function () {
16560 that._tabIndexes();
16561 } );
16562 that._tabIndexes(); // Initial draw has already happened
16563
16564 $( dt.table().body() ).on( 'keyup.dtr', 'td, th', function (e) {
16565 if ( e.keyCode === 13 && $(this).data('dtr-keyboard') ) {
16566 $(this).click();
16567 }
16568 } );
16569
16570 // type.target can be a string jQuery selector or a column index
16571 var target = details.target;
16572 var selector = typeof target === 'string' ? target : 'td, th';
16573
16574 if ( target !== undefined || target !== null ) {
16575 // Click handler to show / hide the details rows when they are available
16576 $( dt.table().body() )
16577 .on( 'click.dtr mousedown.dtr mouseup.dtr', selector, function (e) {
16578 // If the table is not collapsed (i.e. there is no hidden columns)
16579 // then take no action
16580 if ( ! $(dt.table().node()).hasClass('collapsed' ) ) {
16581 return;
16582 }
16583
16584 // Check that the row is actually a DataTable's controlled node
16585 if ( $.inArray( $(this).closest('tr').get(0), dt.rows().nodes().toArray() ) === -1 ) {
16586 return;
16587 }
16588
16589 // For column index, we determine if we should act or not in the
16590 // handler - otherwise it is already okay
16591 if ( typeof target === 'number' ) {
16592 var targetIdx = target < 0 ?
16593 dt.columns().eq(0).length + target :
16594 target;
16595
16596 if ( dt.cell( this ).index().column !== targetIdx ) {
16597 return;
16598 }
16599 }
16600
16601 // $().closest() includes itself in its check
16602 var row = dt.row( $(this).closest('tr') );
16603
16604 // Check event type to do an action
16605 if ( e.type === 'click' ) {
16606 // The renderer is given as a function so the caller can execute it
16607 // only when they need (i.e. if hiding there is no point is running
16608 // the renderer)
16609 that._detailsDisplay( row, false );
16610 }
16611 else if ( e.type === 'mousedown' ) {
16612 // For mouse users, prevent the focus ring from showing
16613 $(this).css('outline', 'none');
16614 }
16615 else if ( e.type === 'mouseup' ) {
16616 // And then re-allow at the end of the click
16617 $(this).trigger('blur').css('outline', '');
16618 }
16619 } );
16620 }
16621 },
16622
16623
16624 /**
16625 * Get the details to pass to a renderer for a row
16626 * @param {int} rowIdx Row index
16627 * @private
16628 */
16629 _detailsObj: function ( rowIdx )
16630 {
16631 var that = this;
16632 var dt = this.s.dt;
16633
16634 return $.map( this.s.columns, function( col, i ) {
16635 // Never and control columns should not be passed to the renderer
16636 if ( col.never || col.control ) {
16637 return;
16638 }
16639
16640 var dtCol = dt.settings()[0].aoColumns[ i ];
16641
16642 return {
16643 className: dtCol.sClass,
16644 columnIndex: i,
16645 data: dt.cell( rowIdx, i ).render( that.c.orthogonal ),
16646 hidden: dt.column( i ).visible() && !that.s.current[ i ],
16647 rowIndex: rowIdx,
16648 title: dtCol.sTitle !== null ?
16649 dtCol.sTitle :
16650 $(dt.column(i).header()).text()
16651 };
16652 } );
16653 },
16654
16655
16656 /**
16657 * Find a breakpoint object from a name
16658 *
16659 * @param {string} name Breakpoint name to find
16660 * @return {object} Breakpoint description object
16661 * @private
16662 */
16663 _find: function ( name )
16664 {
16665 var breakpoints = this.c.breakpoints;
16666
16667 for ( var i=0, ien=breakpoints.length ; i<ien ; i++ ) {
16668 if ( breakpoints[i].name === name ) {
16669 return breakpoints[i];
16670 }
16671 }
16672 },
16673
16674
16675 /**
16676 * Re-create the contents of the child rows as the display has changed in
16677 * some way.
16678 *
16679 * @private
16680 */
16681 _redrawChildren: function ()
16682 {
16683 var that = this;
16684 var dt = this.s.dt;
16685
16686 dt.rows( {page: 'current'} ).iterator( 'row', function ( settings, idx ) {
16687 var row = dt.row( idx );
16688
16689 that._detailsDisplay( dt.row( idx ), true );
16690 } );
16691 },
16692
16693
16694 /**
16695 * Alter the table display for a resized viewport. This involves first
16696 * determining what breakpoint the window currently is in, getting the
16697 * column visibilities to apply and then setting them.
16698 *
16699 * @param {boolean} forceRedraw Force a redraw
16700 * @private
16701 */
16702 _resize: function (forceRedraw)
16703 {
16704 var that = this;
16705 var dt = this.s.dt;
16706 var width = $(window).innerWidth();
16707 var breakpoints = this.c.breakpoints;
16708 var breakpoint = breakpoints[0].name;
16709 var columns = this.s.columns;
16710 var i, ien;
16711 var oldVis = this.s.current.slice();
16712
16713 // Determine what breakpoint we are currently at
16714 for ( i=breakpoints.length-1 ; i>=0 ; i-- ) {
16715 if ( width <= breakpoints[i].width ) {
16716 breakpoint = breakpoints[i].name;
16717 break;
16718 }
16719 }
16720
16721 // Show the columns for that break point
16722 var columnsVis = this._columnsVisiblity( breakpoint );
16723 this.s.current = columnsVis;
16724
16725 // Set the class before the column visibility is changed so event
16726 // listeners know what the state is. Need to determine if there are
16727 // any columns that are not visible but can be shown
16728 var collapsedClass = false;
16729
16730 for ( i=0, ien=columns.length ; i<ien ; i++ ) {
16731 if ( columnsVis[i] === false && ! columns[i].never && ! columns[i].control && ! dt.column(i).visible() === false ) {
16732 collapsedClass = true;
16733 break;
16734 }
16735 }
16736
16737 $( dt.table().node() ).toggleClass( 'collapsed', collapsedClass );
16738
16739 var changed = false;
16740 var visible = 0;
16741
16742 dt.columns().eq(0).each( function ( colIdx, i ) {
16743 if ( columnsVis[i] === true ) {
16744 visible++;
16745 }
16746
16747 if ( forceRedraw || columnsVis[i] !== oldVis[i] ) {
16748 changed = true;
16749 that._setColumnVis( colIdx, columnsVis[i] );
16750 }
16751 } );
16752
16753 // Always need to update the display, regardless of if it has changed or not, so nodes
16754 // can be re-inserted for listHiddenNodes
16755 this._redrawChildren();
16756
16757 if ( changed ) {
16758 // Inform listeners of the change
16759 $(dt.table().node()).trigger( 'responsive-resize.dt', [dt, this.s.current] );
16760
16761 // If no records, update the "No records" display element
16762 if ( dt.page.info().recordsDisplay === 0 ) {
16763 $('td', dt.table().body()).eq(0).attr('colspan', visible);
16764 }
16765 }
16766
16767 that._controlClass();
16768 },
16769
16770
16771 /**
16772 * Determine the width of each column in the table so the auto column hiding
16773 * has that information to work with. This method is never going to be 100%
16774 * perfect since column widths can change slightly per page, but without
16775 * seriously compromising performance this is quite effective.
16776 *
16777 * @private
16778 */
16779 _resizeAuto: function ()
16780 {
16781 var dt = this.s.dt;
16782 var columns = this.s.columns;
16783 var that = this;
16784
16785 // Are we allowed to do auto sizing?
16786 if ( ! this.c.auto ) {
16787 return;
16788 }
16789
16790 // Are there any columns that actually need auto-sizing, or do they all
16791 // have classes defined
16792 if ( $.inArray( true, $.map( columns, function (c) { return c.auto; } ) ) === -1 ) {
16793 return;
16794 }
16795
16796 // Need to restore all children. They will be reinstated by a re-render
16797 if ( ! $.isEmptyObject( this.s.childNodeStore ) ) {
16798 $.each( this.s.childNodeStore, function ( key ) {
16799 var idx = key.split('-');
16800
16801 that._childNodesRestore( dt, idx[0]*1, idx[1]*1 );
16802 } );
16803 }
16804
16805 // Clone the table with the current data in it
16806 var tableWidth = dt.table().node().offsetWidth;
16807 var columnWidths = dt.columns;
16808 var clonedTable = dt.table().node().cloneNode( false );
16809 var clonedHeader = $( dt.table().header().cloneNode( false ) ).appendTo( clonedTable );
16810 var clonedBody = $( dt.table().body() ).clone( false, false ).empty().appendTo( clonedTable ); // use jQuery because of IE8
16811
16812 clonedTable.style.width = 'auto';
16813
16814 // Header
16815 var headerCells = dt.columns()
16816 .header()
16817 .filter( function (idx) {
16818 return dt.column(idx).visible();
16819 } )
16820 .to$()
16821 .clone( false )
16822 .css( 'display', 'table-cell' )
16823 .css( 'width', 'auto' )
16824 .css( 'min-width', 0 );
16825
16826 // Body rows - we don't need to take account of DataTables' column
16827 // visibility since we implement our own here (hence the `display` set)
16828 $(clonedBody)
16829 .append( $(dt.rows( { page: 'current' } ).nodes()).clone( false ) )
16830 .find( 'th, td' ).css( 'display', '' );
16831
16832 // Footer
16833 var footer = dt.table().footer();
16834 if ( footer ) {
16835 var clonedFooter = $( footer.cloneNode( false ) ).appendTo( clonedTable );
16836 var footerCells = dt.columns()
16837 .footer()
16838 .filter( function (idx) {
16839 return dt.column(idx).visible();
16840 } )
16841 .to$()
16842 .clone( false )
16843 .css( 'display', 'table-cell' );
16844
16845 $('<tr/>')
16846 .append( footerCells )
16847 .appendTo( clonedFooter );
16848 }
16849
16850 $('<tr/>')
16851 .append( headerCells )
16852 .appendTo( clonedHeader );
16853
16854 // In the inline case extra padding is applied to the first column to
16855 // give space for the show / hide icon. We need to use this in the
16856 // calculation
16857 if ( this.c.details.type === 'inline' ) {
16858 $(clonedTable).addClass( 'dtr-inline collapsed' );
16859 }
16860
16861 // It is unsafe to insert elements with the same name into the DOM
16862 // multiple times. For example, cloning and inserting a checked radio
16863 // clears the chcecked state of the original radio.
16864 $( clonedTable ).find( '[name]' ).removeAttr( 'name' );
16865
16866 // A position absolute table would take the table out of the flow of
16867 // our container element, bypassing the height and width (Scroller)
16868 $( clonedTable ).css( 'position', 'relative' )
16869
16870 var inserted = $('<div/>')
16871 .css( {
16872 width: 1,
16873 height: 1,
16874 overflow: 'hidden',
16875 clear: 'both'
16876 } )
16877 .append( clonedTable );
16878
16879 inserted.insertBefore( dt.table().node() );
16880
16881 // The cloned header now contains the smallest that each column can be
16882 headerCells.each( function (i) {
16883 var idx = dt.column.index( 'fromVisible', i );
16884 columns[ idx ].minWidth = this.offsetWidth || 0;
16885 } );
16886
16887 inserted.remove();
16888 },
16889
16890 /**
16891 * Get the state of the current hidden columns - controlled by Responsive only
16892 */
16893 _responsiveOnlyHidden: function ()
16894 {
16895 var dt = this.s.dt;
16896
16897 return $.map( this.s.current, function (v, i) {
16898 // If the column is hidden by DataTables then it can't be hidden by
16899 // Responsive!
16900 if ( dt.column(i).visible() === false ) {
16901 return true;
16902 }
16903 return v;
16904 } );
16905 },
16906
16907 /**
16908 * Set a column's visibility.
16909 *
16910 * We don't use DataTables' column visibility controls in order to ensure
16911 * that column visibility can Responsive can no-exist. Since only IE8+ is
16912 * supported (and all evergreen browsers of course) the control of the
16913 * display attribute works well.
16914 *
16915 * @param {integer} col Column index
16916 * @param {boolean} showHide Show or hide (true or false)
16917 * @private
16918 */
16919 _setColumnVis: function ( col, showHide )
16920 {
16921 var that = this;
16922 var dt = this.s.dt;
16923 var display = showHide ? '' : 'none'; // empty string will remove the attr
16924
16925 $( dt.column( col ).header() )
16926 .css( 'display', display )
16927 .toggleClass('dtr-hidden', !showHide);
16928
16929 $( dt.column( col ).footer() )
16930 .css( 'display', display )
16931 .toggleClass('dtr-hidden', !showHide);
16932
16933 dt.column( col ).nodes().to$()
16934 .css( 'display', display )
16935 .toggleClass('dtr-hidden', !showHide);
16936
16937 // If the are child nodes stored, we might need to reinsert them
16938 if ( ! $.isEmptyObject( this.s.childNodeStore ) ) {
16939 dt.cells( null, col ).indexes().each( function (idx) {
16940 that._childNodesRestore( dt, idx.row, idx.column );
16941 } );
16942 }
16943 },
16944
16945
16946 /**
16947 * Update the cell tab indexes for keyboard accessibility. This is called on
16948 * every table draw - that is potentially inefficient, but also the least
16949 * complex option given that column visibility can change on the fly. Its a
16950 * shame user-focus was removed from CSS 3 UI, as it would have solved this
16951 * issue with a single CSS statement.
16952 *
16953 * @private
16954 */
16955 _tabIndexes: function ()
16956 {
16957 var dt = this.s.dt;
16958 var cells = dt.cells( { page: 'current' } ).nodes().to$();
16959 var ctx = dt.settings()[0];
16960 var target = this.c.details.target;
16961
16962 cells.filter( '[data-dtr-keyboard]' ).removeData( '[data-dtr-keyboard]' );
16963
16964 if ( typeof target === 'number' ) {
16965 dt.cells( null, target, { page: 'current' } ).nodes().to$()
16966 .attr( 'tabIndex', ctx.iTabIndex )
16967 .data( 'dtr-keyboard', 1 );
16968 }
16969 else {
16970 // This is a bit of a hack - we need to limit the selected nodes to just
16971 // those of this table
16972 if ( target === 'td:first-child, th:first-child' ) {
16973 target = '>td:first-child, >th:first-child';
16974 }
16975
16976 $( target, dt.rows( { page: 'current' } ).nodes() )
16977 .attr( 'tabIndex', ctx.iTabIndex )
16978 .data( 'dtr-keyboard', 1 );
16979 }
16980 }
16981} );
16982
16983
16984/**
16985 * List of default breakpoints. Each item in the array is an object with two
16986 * properties:
16987 *
16988 * * `name` - the breakpoint name.
16989 * * `width` - the breakpoint width
16990 *
16991 * @name Responsive.breakpoints
16992 * @static
16993 */
16994Responsive.breakpoints = [
16995 { name: 'desktop', width: Infinity },
16996 { name: 'tablet-l', width: 1024 },
16997 { name: 'tablet-p', width: 768 },
16998 { name: 'mobile-l', width: 480 },
16999 { name: 'mobile-p', width: 320 }
17000];
17001
17002
17003/**
17004 * Display methods - functions which define how the hidden data should be shown
17005 * in the table.
17006 *
17007 * @namespace
17008 * @name Responsive.defaults
17009 * @static
17010 */
17011Responsive.display = {
17012 childRow: function ( row, update, render ) {
17013 if ( update ) {
17014 if ( $(row.node()).hasClass('parent') ) {
17015 row.child( render(), 'child' ).show();
17016
17017 return true;
17018 }
17019 }
17020 else {
17021 if ( ! row.child.isShown() ) {
17022 row.child( render(), 'child' ).show();
17023 $( row.node() ).addClass( 'parent' );
17024
17025 return true;
17026 }
17027 else {
17028 row.child( false );
17029 $( row.node() ).removeClass( 'parent' );
17030
17031 return false;
17032 }
17033 }
17034 },
17035
17036 childRowImmediate: function ( row, update, render ) {
17037 if ( (! update && row.child.isShown()) || ! row.responsive.hasHidden() ) {
17038 // User interaction and the row is show, or nothing to show
17039 row.child( false );
17040 $( row.node() ).removeClass( 'parent' );
17041
17042 return false;
17043 }
17044 else {
17045 // Display
17046 row.child( render(), 'child' ).show();
17047 $( row.node() ).addClass( 'parent' );
17048
17049 return true;
17050 }
17051 },
17052
17053 // This is a wrapper so the modal options for Bootstrap and jQuery UI can
17054 // have options passed into them. This specific one doesn't need to be a
17055 // function but it is for consistency in the `modal` name
17056 modal: function ( options ) {
17057 return function ( row, update, render ) {
17058 if ( ! update ) {
17059 // Show a modal
17060 var close = function () {
17061 modal.remove(); // will tidy events for us
17062 $(document).off( 'keypress.dtr' );
17063 };
17064
17065 var modal = $('<div class="dtr-modal"/>')
17066 .append( $('<div class="dtr-modal-display"/>')
17067 .append( $('<div class="dtr-modal-content"/>')
17068 .append( render() )
17069 )
17070 .append( $('<div class="dtr-modal-close">&times;</div>' )
17071 .click( function () {
17072 close();
17073 } )
17074 )
17075 )
17076 .append( $('<div class="dtr-modal-background"/>')
17077 .click( function () {
17078 close();
17079 } )
17080 )
17081 .appendTo( 'body' );
17082
17083 $(document).on( 'keyup.dtr', function (e) {
17084 if ( e.keyCode === 27 ) {
17085 e.stopPropagation();
17086
17087 close();
17088 }
17089 } );
17090 }
17091 else {
17092 $('div.dtr-modal-content')
17093 .empty()
17094 .append( render() );
17095 }
17096
17097 if ( options && options.header ) {
17098 $('div.dtr-modal-content').prepend(
17099 '<h2>'+options.header( row )+'</h2>'
17100 );
17101 }
17102 };
17103 }
17104};
17105
17106
17107/**
17108 * Display methods - functions which define how the hidden data should be shown
17109 * in the table.
17110 *
17111 * @namespace
17112 * @name Responsive.defaults
17113 * @static
17114 */
17115Responsive.renderer = {
17116 listHiddenNodes: function () {
17117 return function ( api, rowIdx, columns ) {
17118 var that = this;
17119 var ul = $('<ul data-dtr-index="'+rowIdx+'" class="dtr-details"/>');
17120 var found = false;
17121
17122 var data = $.each( columns, function ( i, col ) {
17123 if ( col.hidden ) {
17124 var klass = col.className ?
17125 'class="'+ col.className +'"' :
17126 '';
17127
17128 $(
17129 '<li '+klass+' data-dtr-index="'+col.columnIndex+'" data-dt-row="'+col.rowIndex+'" data-dt-column="'+col.columnIndex+'">'+
17130 '<span class="dtr-title">'+
17131 col.title+
17132 '</span> '+
17133 '</li>'
17134 )
17135 .append( $('<span class="dtr-data"/>').append( that._childNodes( api, col.rowIndex, col.columnIndex ) ) )// api.cell( col.rowIndex, col.columnIndex ).node().childNodes ) )
17136 .appendTo( ul );
17137
17138 found = true;
17139 }
17140 } );
17141
17142 return found ?
17143 ul :
17144 false;
17145 };
17146 },
17147
17148 listHidden: function () {
17149 return function ( api, rowIdx, columns ) {
17150 var data = $.map( columns, function ( col ) {
17151 var klass = col.className ?
17152 'class="'+ col.className +'"' :
17153 '';
17154
17155 return col.hidden ?
17156 '<li '+klass+' data-dtr-index="'+col.columnIndex+'" data-dt-row="'+col.rowIndex+'" data-dt-column="'+col.columnIndex+'">'+
17157 '<span class="dtr-title">'+
17158 col.title+
17159 '</span> '+
17160 '<span class="dtr-data">'+
17161 col.data+
17162 '</span>'+
17163 '</li>' :
17164 '';
17165 } ).join('');
17166
17167 return data ?
17168 $('<ul data-dtr-index="'+rowIdx+'" class="dtr-details"/>').append( data ) :
17169 false;
17170 }
17171 },
17172
17173 tableAll: function ( options ) {
17174 options = $.extend( {
17175 tableClass: ''
17176 }, options );
17177
17178 return function ( api, rowIdx, columns ) {
17179 var data = $.map( columns, function ( col ) {
17180 var klass = col.className ?
17181 'class="'+ col.className +'"' :
17182 '';
17183
17184 return '<tr '+klass+' data-dt-row="'+col.rowIndex+'" data-dt-column="'+col.columnIndex+'">'+
17185 '<td>'+col.title+':'+'</td> '+
17186 '<td>'+col.data+'</td>'+
17187 '</tr>';
17188 } ).join('');
17189
17190 return $('<table class="'+options.tableClass+' dtr-details" width="100%"/>').append( data );
17191 }
17192 }
17193};
17194
17195/**
17196 * Responsive default settings for initialisation
17197 *
17198 * @namespace
17199 * @name Responsive.defaults
17200 * @static
17201 */
17202Responsive.defaults = {
17203 /**
17204 * List of breakpoints for the instance. Note that this means that each
17205 * instance can have its own breakpoints. Additionally, the breakpoints
17206 * cannot be changed once an instance has been creased.
17207 *
17208 * @type {Array}
17209 * @default Takes the value of `Responsive.breakpoints`
17210 */
17211 breakpoints: Responsive.breakpoints,
17212
17213 /**
17214 * Enable / disable auto hiding calculations. It can help to increase
17215 * performance slightly if you disable this option, but all columns would
17216 * need to have breakpoint classes assigned to them
17217 *
17218 * @type {Boolean}
17219 * @default `true`
17220 */
17221 auto: true,
17222
17223 /**
17224 * Details control. If given as a string value, the `type` property of the
17225 * default object is set to that value, and the defaults used for the rest
17226 * of the object - this is for ease of implementation.
17227 *
17228 * The object consists of the following properties:
17229 *
17230 * * `display` - A function that is used to show and hide the hidden details
17231 * * `renderer` - function that is called for display of the child row data.
17232 * The default function will show the data from the hidden columns
17233 * * `target` - Used as the selector for what objects to attach the child
17234 * open / close to
17235 * * `type` - `false` to disable the details display, `inline` or `column`
17236 * for the two control types
17237 *
17238 * @type {Object|string}
17239 */
17240 details: {
17241 display: Responsive.display.childRow,
17242
17243 renderer: Responsive.renderer.listHidden(),
17244
17245 target: 0,
17246
17247 type: 'inline'
17248 },
17249
17250 /**
17251 * Orthogonal data request option. This is used to define the data type
17252 * requested when Responsive gets the data to show in the child row.
17253 *
17254 * @type {String}
17255 */
17256 orthogonal: 'display'
17257};
17258
17259
17260/*
17261 * API
17262 */
17263var Api = $.fn.dataTable.Api;
17264
17265// Doesn't do anything - work around for a bug in DT... Not documented
17266Api.register( 'responsive()', function () {
17267 return this;
17268} );
17269
17270Api.register( 'responsive.index()', function ( li ) {
17271 li = $(li);
17272
17273 return {
17274 column: li.data('dtr-index'),
17275 row: li.parent().data('dtr-index')
17276 };
17277} );
17278
17279Api.register( 'responsive.rebuild()', function () {
17280 return this.iterator( 'table', function ( ctx ) {
17281 if ( ctx._responsive ) {
17282 ctx._responsive._classLogic();
17283 }
17284 } );
17285} );
17286
17287Api.register( 'responsive.recalc()', function () {
17288 return this.iterator( 'table', function ( ctx ) {
17289 if ( ctx._responsive ) {
17290 ctx._responsive._resizeAuto();
17291 ctx._responsive._resize();
17292 }
17293 } );
17294} );
17295
17296Api.register( 'responsive.hasHidden()', function () {
17297 var ctx = this.context[0];
17298
17299 return ctx._responsive ?
17300 $.inArray( false, ctx._responsive._responsiveOnlyHidden() ) !== -1 :
17301 false;
17302} );
17303
17304Api.registerPlural( 'columns().responsiveHidden()', 'column().responsiveHidden()', function () {
17305 return this.iterator( 'column', function ( settings, column ) {
17306 return settings._responsive ?
17307 settings._responsive._responsiveOnlyHidden()[ column ] :
17308 false;
17309 }, 1 );
17310} );
17311
17312
17313/**
17314 * Version information
17315 *
17316 * @name Responsive.version
17317 * @static
17318 */
17319Responsive.version = '2.4.0';
17320
17321
17322$.fn.dataTable.Responsive = Responsive;
17323$.fn.DataTable.Responsive = Responsive;
17324
17325// Attach a listener to the document which listens for DataTables initialisation
17326// events so we can automatically initialise
17327$(document).on( 'preInit.dt.dtr', function (e, settings, json) {
17328 if ( e.namespace !== 'dt' ) {
17329 return;
17330 }
17331
17332 if ( $(settings.nTable).hasClass( 'responsive' ) ||
17333 $(settings.nTable).hasClass( 'dt-responsive' ) ||
17334 settings.oInit.responsive ||
17335 DataTable.defaults.responsive
17336 ) {
17337 var init = settings.oInit.responsive;
17338
17339 if ( init !== false ) {
17340 new Responsive( settings, $.isPlainObject( init ) ? init : {} );
17341 }
17342 }
17343} );
17344
17345
17346return DataTable;
17347}));
17348
17349
17350/*! Bootstrap 5 integration for DataTables' Responsive
17351 * © SpryMedia Ltd - datatables.net/license
17352 */
17353
17354(function( factory ){
17355 if ( typeof define === 'function' && define.amd ) {
17356 // AMD
17357 define( ['jquery', 'datatables.net-bs5', 'datatables.net-responsive'], function ( $ ) {
17358 return factory( $, window, document );
17359 } );
17360 }
17361 else if ( typeof exports === 'object' ) {
17362 // CommonJS
17363 module.exports = function (root, $) {
17364 if ( ! root ) {
17365 // CommonJS environments without a window global must pass a
17366 // root. This will give an error otherwise
17367 root = window;
17368 }
17369
17370 if ( ! $ ) {
17371 $ = typeof window !== 'undefined' ? // jQuery's factory checks for a global window
17372 require('jquery') :
17373 require('jquery')( root );
17374 }
17375
17376 if ( ! $.fn.dataTable ) {
17377 require('datatables.net-bs5')(root, $);
17378 }
17379
17380 if ( ! $.fn.dataTable ) {
17381 require('datatables.net-responsive')(root, $);
17382 }
17383
17384
17385 return factory( $, root, root.document );
17386 };
17387 }
17388 else {
17389 // Browser
17390 factory( jQuery, window, document );
17391 }
17392}(function( $, window, document, undefined ) {
17393'use strict';
17394var DataTable = $.fn.dataTable;
17395
17396
17397
17398var _display = DataTable.Responsive.display;
17399var _original = _display.modal;
17400var _modal = $(
17401 '<div class="modal fade dtr-bs-modal" role="dialog">'+
17402 '<div class="modal-dialog" role="document">'+
17403 '<div class="modal-content">'+
17404 '<div class="modal-header">'+
17405 '<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>'+
17406 '</div>'+
17407 '<div class="modal-body"/>'+
17408 '</div>'+
17409 '</div>'+
17410 '</div>'
17411);
17412var modal;
17413
17414// Note this could be undefined at the time of initialisation - the
17415// DataTable.Responsive.bootstrap function can be used to set a different
17416// bootstrap object
17417var _bs = window.bootstrap;
17418
17419DataTable.Responsive.bootstrap = function (bs) {
17420 _bs = bs;
17421}
17422
17423_display.modal = function ( options ) {
17424 if (! modal) {
17425 modal = new _bs.Modal(_modal[0]);
17426 }
17427
17428 return function ( row, update, render ) {
17429 if ( ! $.fn.modal ) {
17430 _original( row, update, render );
17431 }
17432 else {
17433 if ( ! update ) {
17434 if ( options && options.header ) {
17435 var header = _modal.find('div.modal-header');
17436 var button = header.find('button').detach();
17437
17438 header
17439 .empty()
17440 .append( '<h4 class="modal-title">'+options.header( row )+'</h4>' )
17441 .append( button );
17442 }
17443
17444 _modal.find( 'div.modal-body' )
17445 .empty()
17446 .append( render() );
17447
17448 _modal
17449 .appendTo( 'body' )
17450 .modal();
17451
17452 modal.show();
17453 }
17454 }
17455 };
17456};
17457
17458
17459return DataTable;
17460}));
17461
17462
17463/*! Select for DataTables 1.5.0
17464 * 2015-2021 SpryMedia Ltd - datatables.net/license/mit
17465 */
17466
17467(function( factory ){
17468 if ( typeof define === 'function' && define.amd ) {
17469 // AMD
17470 define( ['jquery', 'datatables.net'], function ( $ ) {
17471 return factory( $, window, document );
17472 } );
17473 }
17474 else if ( typeof exports === 'object' ) {
17475 // CommonJS
17476 module.exports = function (root, $) {
17477 if ( ! root ) {
17478 // CommonJS environments without a window global must pass a
17479 // root. This will give an error otherwise
17480 root = window;
17481 }
17482
17483 if ( ! $ ) {
17484 $ = typeof window !== 'undefined' ? // jQuery's factory checks for a global window
17485 require('jquery') :
17486 require('jquery')( root );
17487 }
17488
17489 if ( ! $.fn.dataTable ) {
17490 require('datatables.net')(root, $);
17491 }
17492
17493
17494 return factory( $, root, root.document );
17495 };
17496 }
17497 else {
17498 // Browser
17499 factory( jQuery, window, document );
17500 }
17501}(function( $, window, document, undefined ) {
17502'use strict';
17503var DataTable = $.fn.dataTable;
17504
17505
17506
17507// Version information for debugger
17508DataTable.select = {};
17509
17510DataTable.select.version = '1.5.0';
17511
17512DataTable.select.init = function ( dt ) {
17513 var ctx = dt.settings()[0];
17514
17515 if (ctx._select) {
17516 return;
17517 }
17518
17519 var savedSelected = dt.state.loaded();
17520
17521 var selectAndSave = function(e, settings, data) {
17522 if(data === null || data.select === undefined) {
17523 return;
17524 }
17525
17526 // Clear any currently selected rows, before restoring state
17527 // None will be selected on first initialisation
17528 if (dt.rows({selected: true}).any()) {
17529 dt.rows().deselect();
17530 }
17531 if (data.select.rows !== undefined) {
17532 dt.rows(data.select.rows).select();
17533 }
17534
17535 if (dt.columns({selected: true}).any()) {
17536 dt.columns().deselect();
17537 }
17538 if (data.select.columns !== undefined) {
17539 dt.columns(data.select.columns).select();
17540 }
17541
17542 if (dt.cells({selected: true}).any()) {
17543 dt.cells().deselect();
17544 }
17545 if (data.select.cells !== undefined) {
17546 for(var i = 0; i < data.select.cells.length; i++) {
17547 dt.cell(data.select.cells[i].row, data.select.cells[i].column).select();
17548 }
17549 }
17550 dt.state.save();
17551 }
17552
17553 dt.one('init', function() {
17554 dt.on('stateSaveParams', function(e, settings, data) {
17555 data.select = {};
17556 data.select.rows = dt.rows({selected:true}).ids(true).toArray();
17557 data.select.columns = dt.columns({selected:true})[0];
17558 data.select.cells = dt.cells({selected:true})[0].map(function(coords) {
17559 return {row: dt.row(coords.row).id(true), column: coords.column}
17560 });
17561 })
17562
17563 selectAndSave(undefined, undefined, savedSelected)
17564 dt.on('stateLoaded stateLoadParams', selectAndSave)
17565 })
17566
17567 var init = ctx.oInit.select;
17568 var defaults = DataTable.defaults.select;
17569 var opts = init === undefined ?
17570 defaults :
17571 init;
17572
17573 // Set defaults
17574 var items = 'row';
17575 var style = 'api';
17576 var blurable = false;
17577 var toggleable = true;
17578 var info = true;
17579 var selector = 'td, th';
17580 var className = 'selected';
17581 var setStyle = false;
17582
17583 ctx._select = {};
17584
17585 // Initialisation customisations
17586 if ( opts === true ) {
17587 style = 'os';
17588 setStyle = true;
17589 }
17590 else if ( typeof opts === 'string' ) {
17591 style = opts;
17592 setStyle = true;
17593 }
17594 else if ( $.isPlainObject( opts ) ) {
17595 if ( opts.blurable !== undefined ) {
17596 blurable = opts.blurable;
17597 }
17598
17599 if ( opts.toggleable !== undefined ) {
17600 toggleable = opts.toggleable;
17601 }
17602
17603 if ( opts.info !== undefined ) {
17604 info = opts.info;
17605 }
17606
17607 if ( opts.items !== undefined ) {
17608 items = opts.items;
17609 }
17610
17611 if ( opts.style !== undefined ) {
17612 style = opts.style;
17613 setStyle = true;
17614 }
17615 else {
17616 style = 'os';
17617 setStyle = true;
17618 }
17619
17620 if ( opts.selector !== undefined ) {
17621 selector = opts.selector;
17622 }
17623
17624 if ( opts.className !== undefined ) {
17625 className = opts.className;
17626 }
17627 }
17628
17629 dt.select.selector( selector );
17630 dt.select.items( items );
17631 dt.select.style( style );
17632 dt.select.blurable( blurable );
17633 dt.select.toggleable( toggleable );
17634 dt.select.info( info );
17635 ctx._select.className = className;
17636
17637
17638 // Sort table based on selected rows. Requires Select Datatables extension
17639 $.fn.dataTable.ext.order['select-checkbox'] = function ( settings, col ) {
17640 return this.api().column( col, {order: 'index'} ).nodes().map( function ( td ) {
17641 if ( settings._select.items === 'row' ) {
17642 return $( td ).parent().hasClass( settings._select.className );
17643 } else if ( settings._select.items === 'cell' ) {
17644 return $( td ).hasClass( settings._select.className );
17645 }
17646 return false;
17647 });
17648 };
17649
17650 // If the init options haven't enabled select, but there is a selectable
17651 // class name, then enable
17652 if ( ! setStyle && $( dt.table().node() ).hasClass( 'selectable' ) ) {
17653 dt.select.style( 'os' );
17654 }
17655};
17656
17657/*
17658
17659Select is a collection of API methods, event handlers, event emitters and
17660buttons (for the `Buttons` extension) for DataTables. It provides the following
17661features, with an overview of how they are implemented:
17662
17663## Selection of rows, columns and cells. Whether an item is selected or not is
17664 stored in:
17665
17666* rows: a `_select_selected` property which contains a boolean value of the
17667 DataTables' `aoData` object for each row
17668* columns: a `_select_selected` property which contains a boolean value of the
17669 DataTables' `aoColumns` object for each column
17670* cells: a `_selected_cells` property which contains an array of boolean values
17671 of the `aoData` object for each row. The array is the same length as the
17672 columns array, with each element of it representing a cell.
17673
17674This method of using boolean flags allows Select to operate when nodes have not
17675been created for rows / cells (DataTables' defer rendering feature).
17676
17677## API methods
17678
17679A range of API methods are available for triggering selection and de-selection
17680of rows. Methods are also available to configure the selection events that can
17681be triggered by an end user (such as which items are to be selected). To a large
17682extent, these of API methods *is* Select. It is basically a collection of helper
17683functions that can be used to select items in a DataTable.
17684
17685Configuration of select is held in the object `_select` which is attached to the
17686DataTables settings object on initialisation. Select being available on a table
17687is not optional when Select is loaded, but its default is for selection only to
17688be available via the API - so the end user wouldn't be able to select rows
17689without additional configuration.
17690
17691The `_select` object contains the following properties:
17692
17693```
17694{
17695 items:string - Can be `rows`, `columns` or `cells`. Defines what item
17696 will be selected if the user is allowed to activate row
17697 selection using the mouse.
17698 style:string - Can be `none`, `single`, `multi` or `os`. Defines the
17699 interaction style when selecting items
17700 blurable:boolean - If row selection can be cleared by clicking outside of
17701 the table
17702 toggleable:boolean - If row selection can be cancelled by repeated clicking
17703 on the row
17704 info:boolean - If the selection summary should be shown in the table
17705 information elements
17706}
17707```
17708
17709In addition to the API methods, Select also extends the DataTables selector
17710options for rows, columns and cells adding a `selected` option to the selector
17711options object, allowing the developer to select only selected items or
17712unselected items.
17713
17714## Mouse selection of items
17715
17716Clicking on items can be used to select items. This is done by a simple event
17717handler that will select the items using the API methods.
17718
17719 */
17720
17721
17722/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
17723 * Local functions
17724 */
17725
17726/**
17727 * Add one or more cells to the selection when shift clicking in OS selection
17728 * style cell selection.
17729 *
17730 * Cell range is more complicated than row and column as we want to select
17731 * in the visible grid rather than by index in sequence. For example, if you
17732 * click first in cell 1-1 and then shift click in 2-2 - cells 1-2 and 2-1
17733 * should also be selected (and not 1-3, 1-4. etc)
17734 *
17735 * @param {DataTable.Api} dt DataTable
17736 * @param {object} idx Cell index to select to
17737 * @param {object} last Cell index to select from
17738 * @private
17739 */
17740function cellRange( dt, idx, last )
17741{
17742 var indexes;
17743 var columnIndexes;
17744 var rowIndexes;
17745 var selectColumns = function ( start, end ) {
17746 if ( start > end ) {
17747 var tmp = end;
17748 end = start;
17749 start = tmp;
17750 }
17751
17752 var record = false;
17753 return dt.columns( ':visible' ).indexes().filter( function (i) {
17754 if ( i === start ) {
17755 record = true;
17756 }
17757
17758 if ( i === end ) { // not else if, as start might === end
17759 record = false;
17760 return true;
17761 }
17762
17763 return record;
17764 } );
17765 };
17766
17767 var selectRows = function ( start, end ) {
17768 var indexes = dt.rows( { search: 'applied' } ).indexes();
17769
17770 // Which comes first - might need to swap
17771 if ( indexes.indexOf( start ) > indexes.indexOf( end ) ) {
17772 var tmp = end;
17773 end = start;
17774 start = tmp;
17775 }
17776
17777 var record = false;
17778 return indexes.filter( function (i) {
17779 if ( i === start ) {
17780 record = true;
17781 }
17782
17783 if ( i === end ) {
17784 record = false;
17785 return true;
17786 }
17787
17788 return record;
17789 } );
17790 };
17791
17792 if ( ! dt.cells( { selected: true } ).any() && ! last ) {
17793 // select from the top left cell to this one
17794 columnIndexes = selectColumns( 0, idx.column );
17795 rowIndexes = selectRows( 0 , idx.row );
17796 }
17797 else {
17798 // Get column indexes between old and new
17799 columnIndexes = selectColumns( last.column, idx.column );
17800 rowIndexes = selectRows( last.row , idx.row );
17801 }
17802
17803 indexes = dt.cells( rowIndexes, columnIndexes ).flatten();
17804
17805 if ( ! dt.cells( idx, { selected: true } ).any() ) {
17806 // Select range
17807 dt.cells( indexes ).select();
17808 }
17809 else {
17810 // Deselect range
17811 dt.cells( indexes ).deselect();
17812 }
17813}
17814
17815/**
17816 * Disable mouse selection by removing the selectors
17817 *
17818 * @param {DataTable.Api} dt DataTable to remove events from
17819 * @private
17820 */
17821function disableMouseSelection( dt )
17822{
17823 var ctx = dt.settings()[0];
17824 var selector = ctx._select.selector;
17825
17826 $( dt.table().container() )
17827 .off( 'mousedown.dtSelect', selector )
17828 .off( 'mouseup.dtSelect', selector )
17829 .off( 'click.dtSelect', selector );
17830
17831 $('body').off( 'click.dtSelect' + _safeId(dt.table().node()) );
17832}
17833
17834/**
17835 * Attach mouse listeners to the table to allow mouse selection of items
17836 *
17837 * @param {DataTable.Api} dt DataTable to remove events from
17838 * @private
17839 */
17840function enableMouseSelection ( dt )
17841{
17842 var container = $( dt.table().container() );
17843 var ctx = dt.settings()[0];
17844 var selector = ctx._select.selector;
17845 var matchSelection;
17846
17847 container
17848 .on( 'mousedown.dtSelect', selector, function(e) {
17849 // Disallow text selection for shift clicking on the table so multi
17850 // element selection doesn't look terrible!
17851 if ( e.shiftKey || e.metaKey || e.ctrlKey ) {
17852 container
17853 .css( '-moz-user-select', 'none' )
17854 .one('selectstart.dtSelect', selector, function () {
17855 return false;
17856 } );
17857 }
17858
17859 if ( window.getSelection ) {
17860 matchSelection = window.getSelection();
17861 }
17862 } )
17863 .on( 'mouseup.dtSelect', selector, function() {
17864 // Allow text selection to occur again, Mozilla style (tested in FF
17865 // 35.0.1 - still required)
17866 container.css( '-moz-user-select', '' );
17867 } )
17868 .on( 'click.dtSelect', selector, function ( e ) {
17869 var items = dt.select.items();
17870 var idx;
17871
17872 // If text was selected (click and drag), then we shouldn't change
17873 // the row's selected state
17874 if ( matchSelection ) {
17875 var selection = window.getSelection();
17876
17877 // If the element that contains the selection is not in the table, we can ignore it
17878 // This can happen if the developer selects text from the click event
17879 if ( ! selection.anchorNode || $(selection.anchorNode).closest('table')[0] === dt.table().node() ) {
17880 if ( selection !== matchSelection ) {
17881 return;
17882 }
17883 }
17884 }
17885
17886 var ctx = dt.settings()[0];
17887 var wrapperClass = dt.settings()[0].oClasses.sWrapper.trim().replace(/ +/g, '.');
17888
17889 // Ignore clicks inside a sub-table
17890 if ( $(e.target).closest('div.'+wrapperClass)[0] != dt.table().container() ) {
17891 return;
17892 }
17893
17894 var cell = dt.cell( $(e.target).closest('td, th') );
17895
17896 // Check the cell actually belongs to the host DataTable (so child
17897 // rows, etc, are ignored)
17898 if ( ! cell.any() ) {
17899 return;
17900 }
17901
17902 var event = $.Event('user-select.dt');
17903 eventTrigger( dt, event, [ items, cell, e ] );
17904
17905 if ( event.isDefaultPrevented() ) {
17906 return;
17907 }
17908
17909 var cellIndex = cell.index();
17910 if ( items === 'row' ) {
17911 idx = cellIndex.row;
17912 typeSelect( e, dt, ctx, 'row', idx );
17913 }
17914 else if ( items === 'column' ) {
17915 idx = cell.index().column;
17916 typeSelect( e, dt, ctx, 'column', idx );
17917 }
17918 else if ( items === 'cell' ) {
17919 idx = cell.index();
17920 typeSelect( e, dt, ctx, 'cell', idx );
17921 }
17922
17923 ctx._select_lastCell = cellIndex;
17924 } );
17925
17926 // Blurable
17927 $('body').on( 'click.dtSelect' + _safeId(dt.table().node()), function ( e ) {
17928 if ( ctx._select.blurable ) {
17929 // If the click was inside the DataTables container, don't blur
17930 if ( $(e.target).parents().filter( dt.table().container() ).length ) {
17931 return;
17932 }
17933
17934 // Ignore elements which have been removed from the DOM (i.e. paging
17935 // buttons)
17936 if ( $(e.target).parents('html').length === 0 ) {
17937 return;
17938 }
17939
17940 // Don't blur in Editor form
17941 if ( $(e.target).parents('div.DTE').length ) {
17942 return;
17943 }
17944
17945 var event = $.Event('select-blur.dt');
17946 eventTrigger( dt, event, [ e.target, e ] );
17947
17948 if ( event.isDefaultPrevented() ) {
17949 return;
17950 }
17951
17952 clear( ctx, true );
17953 }
17954 } );
17955}
17956
17957/**
17958 * Trigger an event on a DataTable
17959 *
17960 * @param {DataTable.Api} api DataTable to trigger events on
17961 * @param {boolean} selected true if selected, false if deselected
17962 * @param {string} type Item type acting on
17963 * @param {boolean} any Require that there are values before
17964 * triggering
17965 * @private
17966 */
17967function eventTrigger ( api, type, args, any )
17968{
17969 if ( any && ! api.flatten().length ) {
17970 return;
17971 }
17972
17973 if ( typeof type === 'string' ) {
17974 type = type +'.dt';
17975 }
17976
17977 args.unshift( api );
17978
17979 $(api.table().node()).trigger( type, args );
17980}
17981
17982/**
17983 * Update the information element of the DataTable showing information about the
17984 * items selected. This is done by adding tags to the existing text
17985 *
17986 * @param {DataTable.Api} api DataTable to update
17987 * @private
17988 */
17989function info ( api )
17990{
17991 var ctx = api.settings()[0];
17992
17993 if ( ! ctx._select.info || ! ctx.aanFeatures.i ) {
17994 return;
17995 }
17996
17997 if ( api.select.style() === 'api' ) {
17998 return;
17999 }
18000
18001 var rows = api.rows( { selected: true } ).flatten().length;
18002 var columns = api.columns( { selected: true } ).flatten().length;
18003 var cells = api.cells( { selected: true } ).flatten().length;
18004
18005 var add = function ( el, name, num ) {
18006 el.append( $('<span class="select-item"/>').append( api.i18n(
18007 'select.'+name+'s',
18008 { _: '%d '+name+'s selected', 0: '', 1: '1 '+name+' selected' },
18009 num
18010 ) ) );
18011 };
18012
18013 // Internal knowledge of DataTables to loop over all information elements
18014 $.each( ctx.aanFeatures.i, function ( i, el ) {
18015 el = $(el);
18016
18017 var output = $('<span class="select-info"/>');
18018 add( output, 'row', rows );
18019 add( output, 'column', columns );
18020 add( output, 'cell', cells );
18021
18022 var exisiting = el.children('span.select-info');
18023 if ( exisiting.length ) {
18024 exisiting.remove();
18025 }
18026
18027 if ( output.text() !== '' ) {
18028 el.append( output );
18029 }
18030 } );
18031}
18032
18033/**
18034 * Initialisation of a new table. Attach event handlers and callbacks to allow
18035 * Select to operate correctly.
18036 *
18037 * This will occur _after_ the initial DataTables initialisation, although
18038 * before Ajax data is rendered, if there is ajax data
18039 *
18040 * @param {DataTable.settings} ctx Settings object to operate on
18041 * @private
18042 */
18043function init ( ctx ) {
18044 var api = new DataTable.Api( ctx );
18045 ctx._select_init = true;
18046
18047 // Row callback so that classes can be added to rows and cells if the item
18048 // was selected before the element was created. This will happen with the
18049 // `deferRender` option enabled.
18050 //
18051 // This method of attaching to `aoRowCreatedCallback` is a hack until
18052 // DataTables has proper events for row manipulation If you are reviewing
18053 // this code to create your own plug-ins, please do not do this!
18054 ctx.aoRowCreatedCallback.push( {
18055 fn: function ( row, data, index ) {
18056 var i, ien;
18057 var d = ctx.aoData[ index ];
18058
18059 // Row
18060 if ( d._select_selected ) {
18061 $( row ).addClass( ctx._select.className );
18062 }
18063
18064 // Cells and columns - if separated out, we would need to do two
18065 // loops, so it makes sense to combine them into a single one
18066 for ( i=0, ien=ctx.aoColumns.length ; i<ien ; i++ ) {
18067 if ( ctx.aoColumns[i]._select_selected || (d._selected_cells && d._selected_cells[i]) ) {
18068 $(d.anCells[i]).addClass( ctx._select.className );
18069 }
18070 }
18071 },
18072 sName: 'select-deferRender'
18073 } );
18074
18075 // On Ajax reload we want to reselect all rows which are currently selected,
18076 // if there is an rowId (i.e. a unique value to identify each row with)
18077 api.on( 'preXhr.dt.dtSelect', function (e, settings) {
18078 if (settings !== api.settings()[0]) {
18079 // Not triggered by our DataTable!
18080 return;
18081 }
18082
18083 // note that column selection doesn't need to be cached and then
18084 // reselected, as they are already selected
18085 var rows = api.rows( { selected: true } ).ids( true ).filter( function ( d ) {
18086 return d !== undefined;
18087 } );
18088
18089 var cells = api.cells( { selected: true } ).eq(0).map( function ( cellIdx ) {
18090 var id = api.row( cellIdx.row ).id( true );
18091 return id ?
18092 { row: id, column: cellIdx.column } :
18093 undefined;
18094 } ).filter( function ( d ) {
18095 return d !== undefined;
18096 } );
18097
18098 // On the next draw, reselect the currently selected items
18099 api.one( 'draw.dt.dtSelect', function () {
18100 api.rows( rows ).select();
18101
18102 // `cells` is not a cell index selector, so it needs a loop
18103 if ( cells.any() ) {
18104 cells.each( function ( id ) {
18105 api.cells( id.row, id.column ).select();
18106 } );
18107 }
18108 } );
18109 } );
18110
18111 // Update the table information element with selected item summary
18112 api.on( 'draw.dtSelect.dt select.dtSelect.dt deselect.dtSelect.dt info.dt', function () {
18113 info( api );
18114 api.state.save();
18115 } );
18116
18117 // Clean up and release
18118 api.on( 'destroy.dtSelect', function () {
18119 api.rows({selected: true}).deselect();
18120
18121 disableMouseSelection( api );
18122 api.off( '.dtSelect' );
18123 $('body').off('.dtSelect' + _safeId(api.table().node()));
18124 } );
18125}
18126
18127/**
18128 * Add one or more items (rows or columns) to the selection when shift clicking
18129 * in OS selection style
18130 *
18131 * @param {DataTable.Api} dt DataTable
18132 * @param {string} type Row or column range selector
18133 * @param {object} idx Item index to select to
18134 * @param {object} last Item index to select from
18135 * @private
18136 */
18137function rowColumnRange( dt, type, idx, last )
18138{
18139 // Add a range of rows from the last selected row to this one
18140 var indexes = dt[type+'s']( { search: 'applied' } ).indexes();
18141 var idx1 = $.inArray( last, indexes );
18142 var idx2 = $.inArray( idx, indexes );
18143
18144 if ( ! dt[type+'s']( { selected: true } ).any() && idx1 === -1 ) {
18145 // select from top to here - slightly odd, but both Windows and Mac OS
18146 // do this
18147 indexes.splice( $.inArray( idx, indexes )+1, indexes.length );
18148 }
18149 else {
18150 // reverse so we can shift click 'up' as well as down
18151 if ( idx1 > idx2 ) {
18152 var tmp = idx2;
18153 idx2 = idx1;
18154 idx1 = tmp;
18155 }
18156
18157 indexes.splice( idx2+1, indexes.length );
18158 indexes.splice( 0, idx1 );
18159 }
18160
18161 if ( ! dt[type]( idx, { selected: true } ).any() ) {
18162 // Select range
18163 dt[type+'s']( indexes ).select();
18164 }
18165 else {
18166 // Deselect range - need to keep the clicked on row selected
18167 indexes.splice( $.inArray( idx, indexes ), 1 );
18168 dt[type+'s']( indexes ).deselect();
18169 }
18170}
18171
18172/**
18173 * Clear all selected items
18174 *
18175 * @param {DataTable.settings} ctx Settings object of the host DataTable
18176 * @param {boolean} [force=false] Force the de-selection to happen, regardless
18177 * of selection style
18178 * @private
18179 */
18180function clear( ctx, force )
18181{
18182 if ( force || ctx._select.style === 'single' ) {
18183 var api = new DataTable.Api( ctx );
18184
18185 api.rows( { selected: true } ).deselect();
18186 api.columns( { selected: true } ).deselect();
18187 api.cells( { selected: true } ).deselect();
18188 }
18189}
18190
18191/**
18192 * Select items based on the current configuration for style and items.
18193 *
18194 * @param {object} e Mouse event object
18195 * @param {DataTables.Api} dt DataTable
18196 * @param {DataTable.settings} ctx Settings object of the host DataTable
18197 * @param {string} type Items to select
18198 * @param {int|object} idx Index of the item to select
18199 * @private
18200 */
18201function typeSelect ( e, dt, ctx, type, idx )
18202{
18203 var style = dt.select.style();
18204 var toggleable = dt.select.toggleable();
18205 var isSelected = dt[type]( idx, { selected: true } ).any();
18206
18207 if ( isSelected && ! toggleable ) {
18208 return;
18209 }
18210
18211 if ( style === 'os' ) {
18212 if ( e.ctrlKey || e.metaKey ) {
18213 // Add or remove from the selection
18214 dt[type]( idx ).select( ! isSelected );
18215 }
18216 else if ( e.shiftKey ) {
18217 if ( type === 'cell' ) {
18218 cellRange( dt, idx, ctx._select_lastCell || null );
18219 }
18220 else {
18221 rowColumnRange( dt, type, idx, ctx._select_lastCell ?
18222 ctx._select_lastCell[type] :
18223 null
18224 );
18225 }
18226 }
18227 else {
18228 // No cmd or shift click - deselect if selected, or select
18229 // this row only
18230 var selected = dt[type+'s']( { selected: true } );
18231
18232 if ( isSelected && selected.flatten().length === 1 ) {
18233 dt[type]( idx ).deselect();
18234 }
18235 else {
18236 selected.deselect();
18237 dt[type]( idx ).select();
18238 }
18239 }
18240 } else if ( style == 'multi+shift' ) {
18241 if ( e.shiftKey ) {
18242 if ( type === 'cell' ) {
18243 cellRange( dt, idx, ctx._select_lastCell || null );
18244 }
18245 else {
18246 rowColumnRange( dt, type, idx, ctx._select_lastCell ?
18247 ctx._select_lastCell[type] :
18248 null
18249 );
18250 }
18251 }
18252 else {
18253 dt[ type ]( idx ).select( ! isSelected );
18254 }
18255 }
18256 else {
18257 dt[ type ]( idx ).select( ! isSelected );
18258 }
18259}
18260
18261function _safeId( node ) {
18262 return node.id.replace(/[^a-zA-Z0-9\-\_]/g, '-');
18263}
18264
18265
18266
18267/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
18268 * DataTables selectors
18269 */
18270
18271// row and column are basically identical just assigned to different properties
18272// and checking a different array, so we can dynamically create the functions to
18273// reduce the code size
18274$.each( [
18275 { type: 'row', prop: 'aoData' },
18276 { type: 'column', prop: 'aoColumns' }
18277], function ( i, o ) {
18278 DataTable.ext.selector[ o.type ].push( function ( settings, opts, indexes ) {
18279 var selected = opts.selected;
18280 var data;
18281 var out = [];
18282
18283 if ( selected !== true && selected !== false ) {
18284 return indexes;
18285 }
18286
18287 for ( var i=0, ien=indexes.length ; i<ien ; i++ ) {
18288 data = settings[ o.prop ][ indexes[i] ];
18289
18290 if ( (selected === true && data._select_selected === true) ||
18291 (selected === false && ! data._select_selected )
18292 ) {
18293 out.push( indexes[i] );
18294 }
18295 }
18296
18297 return out;
18298 } );
18299} );
18300
18301DataTable.ext.selector.cell.push( function ( settings, opts, cells ) {
18302 var selected = opts.selected;
18303 var rowData;
18304 var out = [];
18305
18306 if ( selected === undefined ) {
18307 return cells;
18308 }
18309
18310 for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
18311 rowData = settings.aoData[ cells[i].row ];
18312
18313 if ( (selected === true && rowData._selected_cells && rowData._selected_cells[ cells[i].column ] === true) ||
18314 (selected === false && ( ! rowData._selected_cells || ! rowData._selected_cells[ cells[i].column ] ) )
18315 ) {
18316 out.push( cells[i] );
18317 }
18318 }
18319
18320 return out;
18321} );
18322
18323
18324
18325/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
18326 * DataTables API
18327 *
18328 * For complete documentation, please refer to the docs/api directory or the
18329 * DataTables site
18330 */
18331
18332// Local variables to improve compression
18333var apiRegister = DataTable.Api.register;
18334var apiRegisterPlural = DataTable.Api.registerPlural;
18335
18336apiRegister( 'select()', function () {
18337 return this.iterator( 'table', function ( ctx ) {
18338 DataTable.select.init( new DataTable.Api( ctx ) );
18339 } );
18340} );
18341
18342apiRegister( 'select.blurable()', function ( flag ) {
18343 if ( flag === undefined ) {
18344 return this.context[0]._select.blurable;
18345 }
18346
18347 return this.iterator( 'table', function ( ctx ) {
18348 ctx._select.blurable = flag;
18349 } );
18350} );
18351
18352apiRegister( 'select.toggleable()', function ( flag ) {
18353 if ( flag === undefined ) {
18354 return this.context[0]._select.toggleable;
18355 }
18356
18357 return this.iterator( 'table', function ( ctx ) {
18358 ctx._select.toggleable = flag;
18359 } );
18360} );
18361
18362apiRegister( 'select.info()', function ( flag ) {
18363 if ( flag === undefined ) {
18364 return this.context[0]._select.info;
18365 }
18366
18367 return this.iterator( 'table', function ( ctx ) {
18368 ctx._select.info = flag;
18369 } );
18370} );
18371
18372apiRegister( 'select.items()', function ( items ) {
18373 if ( items === undefined ) {
18374 return this.context[0]._select.items;
18375 }
18376
18377 return this.iterator( 'table', function ( ctx ) {
18378 ctx._select.items = items;
18379
18380 eventTrigger( new DataTable.Api( ctx ), 'selectItems', [ items ] );
18381 } );
18382} );
18383
18384// Takes effect from the _next_ selection. None disables future selection, but
18385// does not clear the current selection. Use the `deselect` methods for that
18386apiRegister( 'select.style()', function ( style ) {
18387 if ( style === undefined ) {
18388 return this.context[0]._select.style;
18389 }
18390
18391 return this.iterator( 'table', function ( ctx ) {
18392 if ( ! ctx._select ) {
18393 DataTable.select.init( new DataTable.Api(ctx) );
18394 }
18395
18396 if ( ! ctx._select_init ) {
18397 init(ctx);
18398 }
18399
18400 ctx._select.style = style;
18401
18402 // Add / remove mouse event handlers. They aren't required when only
18403 // API selection is available
18404 var dt = new DataTable.Api( ctx );
18405 disableMouseSelection( dt );
18406
18407 if ( style !== 'api' ) {
18408 enableMouseSelection( dt );
18409 }
18410
18411 eventTrigger( new DataTable.Api( ctx ), 'selectStyle', [ style ] );
18412 } );
18413} );
18414
18415apiRegister( 'select.selector()', function ( selector ) {
18416 if ( selector === undefined ) {
18417 return this.context[0]._select.selector;
18418 }
18419
18420 return this.iterator( 'table', function ( ctx ) {
18421 disableMouseSelection( new DataTable.Api( ctx ) );
18422
18423 ctx._select.selector = selector;
18424
18425 if ( ctx._select.style !== 'api' ) {
18426 enableMouseSelection( new DataTable.Api( ctx ) );
18427 }
18428 } );
18429} );
18430
18431
18432
18433apiRegisterPlural( 'rows().select()', 'row().select()', function ( select ) {
18434 var api = this;
18435
18436 if ( select === false ) {
18437 return this.deselect();
18438 }
18439
18440 this.iterator( 'row', function ( ctx, idx ) {
18441 clear( ctx );
18442
18443 ctx.aoData[ idx ]._select_selected = true;
18444 $( ctx.aoData[ idx ].nTr ).addClass( ctx._select.className );
18445 } );
18446
18447 this.iterator( 'table', function ( ctx, i ) {
18448 eventTrigger( api, 'select', [ 'row', api[i] ], true );
18449 } );
18450
18451 return this;
18452} );
18453
18454apiRegister( 'row().selected()', function () {
18455 var ctx = this.context[0];
18456
18457 if (
18458 ctx &&
18459 this.length &&
18460 ctx.aoData[this[0]] &&
18461 ctx.aoData[this[0]]._select_selected
18462 ) {
18463 return true;
18464 }
18465
18466 return false;
18467} );
18468
18469apiRegisterPlural( 'columns().select()', 'column().select()', function ( select ) {
18470 var api = this;
18471
18472 if ( select === false ) {
18473 return this.deselect();
18474 }
18475
18476 this.iterator( 'column', function ( ctx, idx ) {
18477 clear( ctx );
18478
18479 ctx.aoColumns[ idx ]._select_selected = true;
18480
18481 var column = new DataTable.Api( ctx ).column( idx );
18482
18483 $( column.header() ).addClass( ctx._select.className );
18484 $( column.footer() ).addClass( ctx._select.className );
18485
18486 column.nodes().to$().addClass( ctx._select.className );
18487 } );
18488
18489 this.iterator( 'table', function ( ctx, i ) {
18490 eventTrigger( api, 'select', [ 'column', api[i] ], true );
18491 } );
18492
18493 return this;
18494} );
18495
18496apiRegister( 'column().selected()', function () {
18497 var ctx = this.context[0];
18498
18499 if (
18500 ctx &&
18501 this.length &&
18502 ctx.aoColumns[this[0]] &&
18503 ctx.aoColumns[this[0]]._select_selected
18504 ) {
18505 return true;
18506 }
18507
18508 return false;
18509} );
18510
18511apiRegisterPlural( 'cells().select()', 'cell().select()', function ( select ) {
18512 var api = this;
18513
18514 if ( select === false ) {
18515 return this.deselect();
18516 }
18517
18518 this.iterator( 'cell', function ( ctx, rowIdx, colIdx ) {
18519 clear( ctx );
18520
18521 var data = ctx.aoData[ rowIdx ];
18522
18523 if ( data._selected_cells === undefined ) {
18524 data._selected_cells = [];
18525 }
18526
18527 data._selected_cells[ colIdx ] = true;
18528
18529 if ( data.anCells ) {
18530 $( data.anCells[ colIdx ] ).addClass( ctx._select.className );
18531 }
18532 } );
18533
18534 this.iterator( 'table', function ( ctx, i ) {
18535 eventTrigger( api, 'select', [ 'cell', api.cells(api[i]).indexes().toArray() ], true );
18536 } );
18537
18538 return this;
18539} );
18540
18541apiRegister( 'cell().selected()', function () {
18542 var ctx = this.context[0];
18543
18544 if (ctx && this.length) {
18545 var row = ctx.aoData[this[0][0].row];
18546
18547 if (row && row._selected_cells && row._selected_cells[this[0][0].column]) {
18548 return true;
18549 }
18550 }
18551
18552 return false;
18553} );
18554
18555
18556apiRegisterPlural( 'rows().deselect()', 'row().deselect()', function () {
18557 var api = this;
18558
18559 this.iterator( 'row', function ( ctx, idx ) {
18560 ctx.aoData[ idx ]._select_selected = false;
18561 ctx._select_lastCell = null;
18562 $( ctx.aoData[ idx ].nTr ).removeClass( ctx._select.className );
18563 } );
18564
18565 this.iterator( 'table', function ( ctx, i ) {
18566 eventTrigger( api, 'deselect', [ 'row', api[i] ], true );
18567 } );
18568
18569 return this;
18570} );
18571
18572apiRegisterPlural( 'columns().deselect()', 'column().deselect()', function () {
18573 var api = this;
18574
18575 this.iterator( 'column', function ( ctx, idx ) {
18576 ctx.aoColumns[ idx ]._select_selected = false;
18577
18578 var api = new DataTable.Api( ctx );
18579 var column = api.column( idx );
18580
18581 $( column.header() ).removeClass( ctx._select.className );
18582 $( column.footer() ).removeClass( ctx._select.className );
18583
18584 // Need to loop over each cell, rather than just using
18585 // `column().nodes()` as cells which are individually selected should
18586 // not have the `selected` class removed from them
18587 api.cells( null, idx ).indexes().each( function (cellIdx) {
18588 var data = ctx.aoData[ cellIdx.row ];
18589 var cellSelected = data._selected_cells;
18590
18591 if ( data.anCells && (! cellSelected || ! cellSelected[ cellIdx.column ]) ) {
18592 $( data.anCells[ cellIdx.column ] ).removeClass( ctx._select.className );
18593 }
18594 } );
18595 } );
18596
18597 this.iterator( 'table', function ( ctx, i ) {
18598 eventTrigger( api, 'deselect', [ 'column', api[i] ], true );
18599 } );
18600
18601 return this;
18602} );
18603
18604apiRegisterPlural( 'cells().deselect()', 'cell().deselect()', function () {
18605 var api = this;
18606
18607 this.iterator( 'cell', function ( ctx, rowIdx, colIdx ) {
18608 var data = ctx.aoData[ rowIdx ];
18609
18610 if(data._selected_cells !== undefined) {
18611 data._selected_cells[ colIdx ] = false;
18612 }
18613
18614 // Remove class only if the cells exist, and the cell is not column
18615 // selected, in which case the class should remain (since it is selected
18616 // in the column)
18617 if ( data.anCells && ! ctx.aoColumns[ colIdx ]._select_selected ) {
18618 $( data.anCells[ colIdx ] ).removeClass( ctx._select.className );
18619 }
18620 } );
18621
18622 this.iterator( 'table', function ( ctx, i ) {
18623 eventTrigger( api, 'deselect', [ 'cell', api[i] ], true );
18624 } );
18625
18626 return this;
18627} );
18628
18629
18630
18631/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
18632 * Buttons
18633 */
18634function i18n( label, def ) {
18635 return function (dt) {
18636 return dt.i18n( 'buttons.'+label, def );
18637 };
18638}
18639
18640// Common events with suitable namespaces
18641function namespacedEvents ( config ) {
18642 var unique = config._eventNamespace;
18643
18644 return 'draw.dt.DT'+unique+' select.dt.DT'+unique+' deselect.dt.DT'+unique;
18645}
18646
18647function enabled ( dt, config ) {
18648 if ( $.inArray( 'rows', config.limitTo ) !== -1 && dt.rows( { selected: true } ).any() ) {
18649 return true;
18650 }
18651
18652 if ( $.inArray( 'columns', config.limitTo ) !== -1 && dt.columns( { selected: true } ).any() ) {
18653 return true;
18654 }
18655
18656 if ( $.inArray( 'cells', config.limitTo ) !== -1 && dt.cells( { selected: true } ).any() ) {
18657 return true;
18658 }
18659
18660 return false;
18661}
18662
18663var _buttonNamespace = 0;
18664
18665$.extend( DataTable.ext.buttons, {
18666 selected: {
18667 text: i18n( 'selected', 'Selected' ),
18668 className: 'buttons-selected',
18669 limitTo: [ 'rows', 'columns', 'cells' ],
18670 init: function ( dt, node, config ) {
18671 var that = this;
18672 config._eventNamespace = '.select'+(_buttonNamespace++);
18673
18674 // .DT namespace listeners are removed by DataTables automatically
18675 // on table destroy
18676 dt.on( namespacedEvents(config), function () {
18677 that.enable( enabled(dt, config) );
18678 } );
18679
18680 this.disable();
18681 },
18682 destroy: function ( dt, node, config ) {
18683 dt.off( config._eventNamespace );
18684 }
18685 },
18686 selectedSingle: {
18687 text: i18n( 'selectedSingle', 'Selected single' ),
18688 className: 'buttons-selected-single',
18689 init: function ( dt, node, config ) {
18690 var that = this;
18691 config._eventNamespace = '.select'+(_buttonNamespace++);
18692
18693 dt.on( namespacedEvents(config), function () {
18694 var count = dt.rows( { selected: true } ).flatten().length +
18695 dt.columns( { selected: true } ).flatten().length +
18696 dt.cells( { selected: true } ).flatten().length;
18697
18698 that.enable( count === 1 );
18699 } );
18700
18701 this.disable();
18702 },
18703 destroy: function ( dt, node, config ) {
18704 dt.off( config._eventNamespace );
18705 }
18706 },
18707 selectAll: {
18708 text: i18n( 'selectAll', 'Select all' ),
18709 className: 'buttons-select-all',
18710 action: function () {
18711 var items = this.select.items();
18712 this[ items+'s' ]().select();
18713 }
18714 },
18715 selectNone: {
18716 text: i18n( 'selectNone', 'Deselect all' ),
18717 className: 'buttons-select-none',
18718 action: function () {
18719 clear( this.settings()[0], true );
18720 },
18721 init: function ( dt, node, config ) {
18722 var that = this;
18723 config._eventNamespace = '.select'+(_buttonNamespace++);
18724
18725 dt.on( namespacedEvents(config), function () {
18726 var count = dt.rows( { selected: true } ).flatten().length +
18727 dt.columns( { selected: true } ).flatten().length +
18728 dt.cells( { selected: true } ).flatten().length;
18729
18730 that.enable( count > 0 );
18731 } );
18732
18733 this.disable();
18734 },
18735 destroy: function ( dt, node, config ) {
18736 dt.off( config._eventNamespace );
18737 }
18738 }
18739} );
18740
18741$.each( [ 'Row', 'Column', 'Cell' ], function ( i, item ) {
18742 var lc = item.toLowerCase();
18743
18744 DataTable.ext.buttons[ 'select'+item+'s' ] = {
18745 text: i18n( 'select'+item+'s', 'Select '+lc+'s' ),
18746 className: 'buttons-select-'+lc+'s',
18747 action: function () {
18748 this.select.items( lc );
18749 },
18750 init: function ( dt ) {
18751 var that = this;
18752
18753 dt.on( 'selectItems.dt.DT', function ( e, ctx, items ) {
18754 that.active( items === lc );
18755 } );
18756 }
18757 };
18758} );
18759
18760
18761
18762/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
18763 * Initialisation
18764 */
18765
18766// DataTables creation - check if select has been defined in the options. Note
18767// this required that the table be in the document! If it isn't then something
18768// needs to trigger this method unfortunately. The next major release of
18769// DataTables will rework the events and address this.
18770$(document).on( 'preInit.dt.dtSelect', function (e, ctx) {
18771 if ( e.namespace !== 'dt' ) {
18772 return;
18773 }
18774
18775 DataTable.select.init( new DataTable.Api( ctx ) );
18776} );
18777
18778
18779return DataTable;
18780}));
18781