##// END OF EJS Templates
datatables: updated to latest version 1.10.13
marcink -
r1514:5660c532 default
parent child Browse files
Show More
This diff has been collapsed as it changes many lines, (13906 lines changed) Show them Hide them
@@ -1,14841 +1,15307
1 /*! DataTables 1.10.4
2 * ©2008-2014 SpryMedia Ltd - datatables.net/license
1 /*! DataTables 1.10.13
2 * ©2008-2016 SpryMedia Ltd - datatables.net/license
3 3 */
4 4
5 5 /**
6 6 * @summary DataTables
7 7 * @description Paginate, search and order HTML tables
8 * @version 1.10.4
8 * @version 1.10.13
9 9 * @file jquery.dataTables.js
10 * @author SpryMedia Ltd (www.sprymedia.co.uk)
11 * @contact www.sprymedia.co.uk/contact
12 * @copyright Copyright 2008-2014 SpryMedia Ltd.
10 * @author SpryMedia Ltd
11 * @contact www.datatables.net
12 * @copyright Copyright 2008-2016 SpryMedia Ltd.
13 13 *
14 14 * This source file is free software, available under the following license:
15 15 * MIT license - http://datatables.net/license
16 16 *
17 17 * This source file is distributed in the hope that it will be useful, but
18 18 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 19 * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
20 20 *
21 21 * For details please refer to: http://www.datatables.net
22 22 */
23 23
24 24 /*jslint evil: true, undef: true, browser: true */
25 /*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,_fnScrollingWidthAdjust,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnScrollBarWidth,_fnSortFlatten,_fnSort,_fnSortAria,_fnSortListener,_fnSortAttachListener,_fnSortingClasses,_fnSortData,_fnSaveState,_fnLoadState,_fnSettingsFromNode,_fnLog,_fnMap,_fnBindAction,_fnCallbackReg,_fnCallbackFire,_fnLengthOverflow,_fnRenderer,_fnDataSource,_fnRowAttributes*/
26
27 (/** @lends <global> */function( window, document, undefined ) {
25 /*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*/
28 26
29 27 (function( factory ) {
30 28 "use strict";
31 29
32 30 if ( typeof define === 'function' && define.amd ) {
33 // Define as an AMD module if possible
34 define( 'datatables', ['jquery'], factory );
35 }
36 else if ( typeof exports === 'object' ) {
37 // Node/CommonJS
38 factory( require( 'jquery' ) );
39 }
40 else if ( jQuery && !jQuery.fn.dataTable ) {
41 // Define using browser globals otherwise
42 // Prevent multiple instantiations if the script is loaded twice
43 factory( jQuery );
31 // AMD
32 define( ['jquery'], function ( $ ) {
33 return factory( $, window, document );
34 } );
35 }
36 else if ( typeof exports === 'object' ) {
37 // CommonJS
38 module.exports = function (root, $) {
39 if ( ! root ) {
40 // CommonJS environments without a window global must pass a
41 // root. This will give an error otherwise
42 root = window;
43 }
44
45 if ( ! $ ) {
46 $ = typeof window !== 'undefined' ? // jQuery's factory checks for a global window
47 require('jquery') :
48 require('jquery')( root );
49 }
50
51 return factory( $, root, root.document );
52 };
53 }
54 else {
55 // Browser
56 factory( jQuery, window, document );
44 57 }
45 58 }
46 (/** @lends <global> */function( $ ) {
59 (function( $, window, document, undefined ) {
47 60 "use strict";
48 61
49 62 /**
50 63 * DataTables is a plug-in for the jQuery Javascript library. It is a highly
51 64 * flexible tool, based upon the foundations of progressive enhancement,
52 65 * which will add advanced interaction controls to any HTML table. For a
53 66 * full list of features please refer to
54 67 * [DataTables.net](href="http://datatables.net).
55 68 *
56 69 * Note that the `DataTable` object is not a global variable but is aliased
57 70 * to `jQuery.fn.DataTable` and `jQuery.fn.dataTable` through which it may
58 71 * be accessed.
59 72 *
60 73 * @class
61 74 * @param {object} [init={}] Configuration object for DataTables. Options
62 75 * are defined by {@link DataTable.defaults}
63 76 * @requires jQuery 1.7+
64 77 *
65 78 * @example
66 79 * // Basic initialisation
67 80 * $(document).ready( function {
68 81 * $('#example').dataTable();
69 82 * } );
70 83 *
71 84 * @example
72 85 * // Initialisation with configuration options - in this case, disable
73 86 * // pagination and sorting.
74 87 * $(document).ready( function {
75 88 * $('#example').dataTable( {
76 89 * "paginate": false,
77 90 * "sort": false
78 91 * } );
79 92 * } );
80 93 */
81 var DataTable;
82
83
84 /*
85 * It is useful to have variables which are scoped locally so only the
86 * DataTables functions can access them and they don't leak into global space.
87 * At the same time these functions are often useful over multiple files in the
88 * core and API, so we list, or at least document, all variables which are used
89 * by DataTables as private variables here. This also ensures that there is no
90 * clashing of variable names and that they can easily referenced for reuse.
91 */
92
93
94 // Defined else where
95 // _selector_run
96 // _selector_opts
97 // _selector_first
98 // _selector_row_indexes
99
100 var _ext; // DataTable.ext
101 var _Api; // DataTable.Api
102 var _api_register; // DataTable.Api.register
103 var _api_registerPlural; // DataTable.Api.registerPlural
104
105 var _re_dic = {};
106 var _re_new_lines = /[\r\n]/g;
107 var _re_html = /<.*?>/g;
108 var _re_date_start = /^[\w\+\-]/;
109 var _re_date_end = /[\w\+\-]$/;
110
111 // Escape regular expression special characters
112 var _re_escape_regex = new RegExp( '(\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ].join('|\\') + ')', 'g' );
113
114 // U+2009 is thin space and U+202F is narrow no-break space, both used in many
115 // standards as thousands separators
116 var _re_formatted_numeric = /[',$£€¥%\u2009\u202F]/g;
117
118
119 var _empty = function ( d ) {
120 return !d || d === true || d === '-' ? true : false;
121 };
122
123
124 var _intVal = function ( s ) {
125 var integer = parseInt( s, 10 );
126 return !isNaN(integer) && isFinite(s) ? integer : null;
127 };
128
129 // Convert from a formatted number with characters other than `.` as the
130 // decimal place, to a Javascript number
131 var _numToDecimal = function ( num, decimalPoint ) {
132 // Cache created regular expressions for speed as this function is called often
133 if ( ! _re_dic[ decimalPoint ] ) {
134 _re_dic[ decimalPoint ] = new RegExp( _fnEscapeRegex( decimalPoint ), 'g' );
135 }
136 return typeof num === 'string' && decimalPoint !== '.' ?
137 num.replace( /\./g, '' ).replace( _re_dic[ decimalPoint ], '.' ) :
138 num;
139 };
140
141
142 var _isNumber = function ( d, decimalPoint, formatted ) {
143 var strType = typeof d === 'string';
144
145 if ( decimalPoint && strType ) {
146 d = _numToDecimal( d, decimalPoint );
147 }
148
149 if ( formatted && strType ) {
150 d = d.replace( _re_formatted_numeric, '' );
151 }
152
153 return _empty( d ) || (!isNaN( parseFloat(d) ) && isFinite( d ));
154 };
155
156
157 // A string without HTML in it can be considered to be HTML still
158 var _isHtml = function ( d ) {
159 return _empty( d ) || typeof d === 'string';
160 };
161
162
163 var _htmlNumeric = function ( d, decimalPoint, formatted ) {
164 if ( _empty( d ) ) {
165 return true;
166 }
167
168 var html = _isHtml( d );
169 return ! html ?
170 null :
171 _isNumber( _stripHtml( d ), decimalPoint, formatted ) ?
172 true :
173 null;
174 };
175
176
177 var _pluck = function ( a, prop, prop2 ) {
178 var out = [];
179 var i=0, ien=a.length;
180
181 // Could have the test in the loop for slightly smaller code, but speed
182 // is essential here
183 if ( prop2 !== undefined ) {
184 for ( ; i<ien ; i++ ) {
185 if ( a[i] && a[i][ prop ] ) {
186 out.push( a[i][ prop ][ prop2 ] );
187 }
188 }
189 }
190 else {
191 for ( ; i<ien ; i++ ) {
192 if ( a[i] ) {
193 out.push( a[i][ prop ] );
194 }
195 }
196 }
197
198 return out;
199 };
200
201
202 // Basically the same as _pluck, but rather than looping over `a` we use `order`
203 // as the indexes to pick from `a`
204 var _pluck_order = function ( a, order, prop, prop2 )
205 {
206 var out = [];
207 var i=0, ien=order.length;
208
209 // Could have the test in the loop for slightly smaller code, but speed
210 // is essential here
211 if ( prop2 !== undefined ) {
212 for ( ; i<ien ; i++ ) {
213 if ( a[ order[i] ][ prop ] ) {
214 out.push( a[ order[i] ][ prop ][ prop2 ] );
215 }
216 }
217 }
218 else {
219 for ( ; i<ien ; i++ ) {
220 out.push( a[ order[i] ][ prop ] );
221 }
222 }
223
224 return out;
225 };
226
227
228 var _range = function ( len, start )
229 {
230 var out = [];
231 var end;
232
233 if ( start === undefined ) {
234 start = 0;
235 end = len;
236 }
237 else {
238 end = start;
239 start = len;
240 }
241
242 for ( var i=start ; i<end ; i++ ) {
243 out.push( i );
244 }
245
246 return out;
247 };
248
249
250 var _removeEmpty = function ( a )
251 {
252 var out = [];
253
254 for ( var i=0, ien=a.length ; i<ien ; i++ ) {
255 if ( a[i] ) { // careful - will remove all falsy values!
256 out.push( a[i] );
257 }
258 }
259
260 return out;
261 };
262
263
264 var _stripHtml = function ( d ) {
265 return d.replace( _re_html, '' );
266 };
267
268
269 /**
270 * Find the unique elements in a source array.
271 *
272 * @param {array} src Source array
273 * @return {array} Array of unique items
274 * @ignore
275 */
276 var _unique = function ( src )
277 {
278 // A faster unique method is to use object keys to identify used values,
279 // but this doesn't work with arrays or objects, which we must also
280 // consider. See jsperf.com/compare-array-unique-versions/4 for more
281 // information.
282 var
283 out = [],
284 val,
285 i, ien=src.length,
286 j, k=0;
287
288 again: for ( i=0 ; i<ien ; i++ ) {
289 val = src[i];
290
291 for ( j=0 ; j<k ; j++ ) {
292 if ( out[j] === val ) {
293 continue again;
294 }
295 }
296
297 out.push( val );
298 k++;
299 }
300
301 return out;
302 };
303
304
305
306 /**
307 * Create a mapping object that allows camel case parameters to be looked up
308 * for their Hungarian counterparts. The mapping is stored in a private
309 * parameter called `_hungarianMap` which can be accessed on the source object.
310 * @param {object} o
311 * @memberof DataTable#oApi
312 */
313 function _fnHungarianMap ( o )
314 {
315 var
316 hungarian = 'a aa ai ao as b fn i m o s ',
317 match,
318 newKey,
319 map = {};
320
321 $.each( o, function (key, val) {
322 match = key.match(/^([^A-Z]+?)([A-Z])/);
323
324 if ( match && hungarian.indexOf(match[1]+' ') !== -1 )
325 {
326 newKey = key.replace( match[0], match[2].toLowerCase() );
327 map[ newKey ] = key;
328
329 if ( match[1] === 'o' )
330 {
331 _fnHungarianMap( o[key] );
332 }
333 }
334 } );
335
336 o._hungarianMap = map;
337 }
338
339
340 /**
341 * Convert from camel case parameters to Hungarian, based on a Hungarian map
342 * created by _fnHungarianMap.
343 * @param {object} src The model object which holds all parameters that can be
344 * mapped.
345 * @param {object} user The object to convert from camel case to Hungarian.
346 * @param {boolean} force When set to `true`, properties which already have a
347 * Hungarian value in the `user` object will be overwritten. Otherwise they
348 * won't be.
349 * @memberof DataTable#oApi
350 */
351 function _fnCamelToHungarian ( src, user, force )
352 {
353 if ( ! src._hungarianMap ) {
354 _fnHungarianMap( src );
355 }
356
357 var hungarianKey;
358
359 $.each( user, function (key, val) {
360 hungarianKey = src._hungarianMap[ key ];
361
362 if ( hungarianKey !== undefined && (force || user[hungarianKey] === undefined) )
363 {
364 // For objects, we need to buzz down into the object to copy parameters
365 if ( hungarianKey.charAt(0) === 'o' )
366 {
367 // Copy the camelCase options over to the hungarian
368 if ( ! user[ hungarianKey ] ) {
369 user[ hungarianKey ] = {};
370 }
371 $.extend( true, user[hungarianKey], user[key] );
372
373 _fnCamelToHungarian( src[hungarianKey], user[hungarianKey], force );
374 }
375 else {
376 user[hungarianKey] = user[ key ];
377 }
378 }
379 } );
380 }
381
382
383 /**
384 * Language compatibility - when certain options are given, and others aren't, we
385 * need to duplicate the values over, in order to provide backwards compatibility
386 * with older language files.
387 * @param {object} oSettings dataTables settings object
388 * @memberof DataTable#oApi
389 */
390 function _fnLanguageCompat( lang )
391 {
392 var defaults = DataTable.defaults.oLanguage;
393 var zeroRecords = lang.sZeroRecords;
394
395 /* Backwards compatibility - if there is no sEmptyTable given, then use the same as
396 * sZeroRecords - assuming that is given.
397 */
398 if ( ! lang.sEmptyTable && zeroRecords &&
399 defaults.sEmptyTable === "No data available in table" )
400 {
401 _fnMap( lang, lang, 'sZeroRecords', 'sEmptyTable' );
402 }
403
404 /* Likewise with loading records */
405 if ( ! lang.sLoadingRecords && zeroRecords &&
406 defaults.sLoadingRecords === "Loading..." )
407 {
408 _fnMap( lang, lang, 'sZeroRecords', 'sLoadingRecords' );
409 }
410
411 // Old parameter name of the thousands separator mapped onto the new
412 if ( lang.sInfoThousands ) {
413 lang.sThousands = lang.sInfoThousands;
414 }
415
416 var decimal = lang.sDecimal;
417 if ( decimal ) {
418 _addNumericSort( decimal );
419 }
420 }
421
422
423 /**
424 * Map one parameter onto another
425 * @param {object} o Object to map
426 * @param {*} knew The new parameter name
427 * @param {*} old The old parameter name
428 */
429 var _fnCompatMap = function ( o, knew, old ) {
430 if ( o[ knew ] !== undefined ) {
431 o[ old ] = o[ knew ];
432 }
433 };
434
435
436 /**
437 * Provide backwards compatibility for the main DT options. Note that the new
438 * options are mapped onto the old parameters, so this is an external interface
439 * change only.
440 * @param {object} init Object to map
441 */
442 function _fnCompatOpts ( init )
443 {
444 _fnCompatMap( init, 'ordering', 'bSort' );
445 _fnCompatMap( init, 'orderMulti', 'bSortMulti' );
446 _fnCompatMap( init, 'orderClasses', 'bSortClasses' );
447 _fnCompatMap( init, 'orderCellsTop', 'bSortCellsTop' );
448 _fnCompatMap( init, 'order', 'aaSorting' );
449 _fnCompatMap( init, 'orderFixed', 'aaSortingFixed' );
450 _fnCompatMap( init, 'paging', 'bPaginate' );
451 _fnCompatMap( init, 'pagingType', 'sPaginationType' );
452 _fnCompatMap( init, 'pageLength', 'iDisplayLength' );
453 _fnCompatMap( init, 'searching', 'bFilter' );
454
455 // Column search objects are in an array, so it needs to be converted
456 // element by element
457 var searchCols = init.aoSearchCols;
458
459 if ( searchCols ) {
460 for ( var i=0, ien=searchCols.length ; i<ien ; i++ ) {
461 if ( searchCols[i] ) {
462 _fnCamelToHungarian( DataTable.models.oSearch, searchCols[i] );
463 }
464 }
465 }
466 }
467
468
469 /**
470 * Provide backwards compatibility for column options. Note that the new options
471 * are mapped onto the old parameters, so this is an external interface change
472 * only.
473 * @param {object} init Object to map
474 */
475 function _fnCompatCols ( init )
476 {
477 _fnCompatMap( init, 'orderable', 'bSortable' );
478 _fnCompatMap( init, 'orderData', 'aDataSort' );
479 _fnCompatMap( init, 'orderSequence', 'asSorting' );
480 _fnCompatMap( init, 'orderDataType', 'sortDataType' );
481 }
482
483
484 /**
485 * Browser feature detection for capabilities, quirks
486 * @param {object} settings dataTables settings object
487 * @memberof DataTable#oApi
488 */
489 function _fnBrowserDetect( settings )
490 {
491 var browser = settings.oBrowser;
492
493 // Scrolling feature / quirks detection
494 var n = $('<div/>')
495 .css( {
496 position: 'absolute',
497 top: 0,
498 left: 0,
499 height: 1,
500 width: 1,
501 overflow: 'hidden'
502 } )
503 .append(
504 $('<div/>')
505 .css( {
506 position: 'absolute',
507 top: 1,
508 left: 1,
509 width: 100,
510 overflow: 'scroll'
511 } )
512 .append(
513 $('<div class="test"/>')
514 .css( {
515 width: '100%',
516 height: 10
517 } )
518 )
519 )
520 .appendTo( 'body' );
521
522 var test = n.find('.test');
523
524 // IE6/7 will oversize a width 100% element inside a scrolling element, to
525 // include the width of the scrollbar, while other browsers ensure the inner
526 // element is contained without forcing scrolling
527 browser.bScrollOversize = test[0].offsetWidth === 100;
528
529 // In rtl text layout, some browsers (most, but not all) will place the
530 // scrollbar on the left, rather than the right.
531 browser.bScrollbarLeft = test.offset().left !== 1;
532
533 n.remove();
534 }
535
536
537 /**
538 * Array.prototype reduce[Right] method, used for browsers which don't support
539 * JS 1.6. Done this way to reduce code size, since we iterate either way
540 * @param {object} settings dataTables settings object
541 * @memberof DataTable#oApi
542 */
543 function _fnReduce ( that, fn, init, start, end, inc )
544 {
545 var
546 i = start,
547 value,
548 isSet = false;
549
550 if ( init !== undefined ) {
551 value = init;
552 isSet = true;
553 }
554
555 while ( i !== end ) {
556 if ( ! that.hasOwnProperty(i) ) {
557 continue;
558 }
559
560 value = isSet ?
561 fn( value, that[i], i, that ) :
562 that[i];
563
564 isSet = true;
565 i += inc;
566 }
567
568 return value;
569 }
570
571 /**
572 * Add a column to the list used for the table with default values
573 * @param {object} oSettings dataTables settings object
574 * @param {node} nTh The th element for this column
575 * @memberof DataTable#oApi
576 */
577 function _fnAddColumn( oSettings, nTh )
578 {
579 // Add column to aoColumns array
580 var oDefaults = DataTable.defaults.column;
581 var iCol = oSettings.aoColumns.length;
582 var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, {
583 "nTh": nTh ? nTh : document.createElement('th'),
584 "sTitle": oDefaults.sTitle ? oDefaults.sTitle : nTh ? nTh.innerHTML : '',
585 "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol],
586 "mData": oDefaults.mData ? oDefaults.mData : iCol,
587 idx: iCol
588 } );
589 oSettings.aoColumns.push( oCol );
590
591 // Add search object for column specific search. Note that the `searchCols[ iCol ]`
592 // passed into extend can be undefined. This allows the user to give a default
593 // with only some of the parameters defined, and also not give a default
594 var searchCols = oSettings.aoPreSearchCols;
595 searchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch, searchCols[ iCol ] );
596
597 // Use the default column options function to initialise classes etc
598 _fnColumnOptions( oSettings, iCol, null );
599 }
600
601
602 /**
603 * Apply options for a column
604 * @param {object} oSettings dataTables settings object
605 * @param {int} iCol column index to consider
606 * @param {object} oOptions object with sType, bVisible and bSearchable etc
607 * @memberof DataTable#oApi
608 */
609 function _fnColumnOptions( oSettings, iCol, oOptions )
610 {
611 var oCol = oSettings.aoColumns[ iCol ];
612 var oClasses = oSettings.oClasses;
613 var th = $(oCol.nTh);
614
615 // Try to get width information from the DOM. We can't get it from CSS
616 // as we'd need to parse the CSS stylesheet. `width` option can override
617 if ( ! oCol.sWidthOrig ) {
618 // Width attribute
619 oCol.sWidthOrig = th.attr('width') || null;
620
621 // Style attribute
622 var t = (th.attr('style') || '').match(/width:\s*(\d+[pxem%]+)/);
623 if ( t ) {
624 oCol.sWidthOrig = t[1];
625 }
626 }
627
628 /* User specified column options */
629 if ( oOptions !== undefined && oOptions !== null )
630 {
631 // Backwards compatibility
632 _fnCompatCols( oOptions );
633
634 // Map camel case parameters to their Hungarian counterparts
635 _fnCamelToHungarian( DataTable.defaults.column, oOptions );
636
637 /* Backwards compatibility for mDataProp */
638 if ( oOptions.mDataProp !== undefined && !oOptions.mData )
639 {
640 oOptions.mData = oOptions.mDataProp;
641 }
642
643 if ( oOptions.sType )
644 {
645 oCol._sManualType = oOptions.sType;
646 }
647
648 // `class` is a reserved word in Javascript, so we need to provide
649 // the ability to use a valid name for the camel case input
650 if ( oOptions.className && ! oOptions.sClass )
651 {
652 oOptions.sClass = oOptions.className;
653 }
654
655 $.extend( oCol, oOptions );
656 _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" );
657
658 /* iDataSort to be applied (backwards compatibility), but aDataSort will take
659 * priority if defined
660 */
661 if ( typeof oOptions.iDataSort === 'number' )
662 {
663 oCol.aDataSort = [ oOptions.iDataSort ];
664 }
665 _fnMap( oCol, oOptions, "aDataSort" );
666 }
667
668 /* Cache the data get and set functions for speed */
669 var mDataSrc = oCol.mData;
670 var mData = _fnGetObjectDataFn( mDataSrc );
671 var mRender = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null;
672
673 var attrTest = function( src ) {
674 return typeof src === 'string' && src.indexOf('@') !== -1;
675 };
676 oCol._bAttrSrc = $.isPlainObject( mDataSrc ) && (
677 attrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter)
678 );
679
680 oCol.fnGetData = function (rowData, type, meta) {
681 var innerData = mData( rowData, type, undefined, meta );
682
683 return mRender && type ?
684 mRender( innerData, type, rowData, meta ) :
685 innerData;
686 };
687 oCol.fnSetData = function ( rowData, val, meta ) {
688 return _fnSetObjectDataFn( mDataSrc )( rowData, val, meta );
689 };
690
691 // Indicate if DataTables should read DOM data as an object or array
692 // Used in _fnGetRowElements
693 if ( typeof mDataSrc !== 'number' ) {
694 oSettings._rowReadObject = true;
695 }
696
697 /* Feature sorting overrides column specific when off */
698 if ( !oSettings.oFeatures.bSort )
699 {
700 oCol.bSortable = false;
701 th.addClass( oClasses.sSortableNone ); // Have to add class here as order event isn't called
702 }
703
704 /* Check that the class assignment is correct for sorting */
705 var bAsc = $.inArray('asc', oCol.asSorting) !== -1;
706 var bDesc = $.inArray('desc', oCol.asSorting) !== -1;
707 if ( !oCol.bSortable || (!bAsc && !bDesc) )
708 {
709 oCol.sSortingClass = oClasses.sSortableNone;
710 oCol.sSortingClassJUI = "";
711 }
712 else if ( bAsc && !bDesc )
713 {
714 oCol.sSortingClass = oClasses.sSortableAsc;
715 oCol.sSortingClassJUI = oClasses.sSortJUIAscAllowed;
716 }
717 else if ( !bAsc && bDesc )
718 {
719 oCol.sSortingClass = oClasses.sSortableDesc;
720 oCol.sSortingClassJUI = oClasses.sSortJUIDescAllowed;
721 }
722 else
723 {
724 oCol.sSortingClass = oClasses.sSortable;
725 oCol.sSortingClassJUI = oClasses.sSortJUI;
726 }
727 }
728
729
730 /**
731 * Adjust the table column widths for new data. Note: you would probably want to
732 * do a redraw after calling this function!
733 * @param {object} settings dataTables settings object
734 * @memberof DataTable#oApi
735 */
736 function _fnAdjustColumnSizing ( settings )
737 {
738 /* Not interested in doing column width calculation if auto-width is disabled */
739 if ( settings.oFeatures.bAutoWidth !== false )
740 {
741 var columns = settings.aoColumns;
742
743 _fnCalculateColumnWidths( settings );
744 for ( var i=0 , iLen=columns.length ; i<iLen ; i++ )
745 {
746 columns[i].nTh.style.width = columns[i].sWidth;
747 }
748 }
749
750 var scroll = settings.oScroll;
751 if ( scroll.sY !== '' || scroll.sX !== '')
752 {
753 _fnScrollDraw( settings );
754 }
755
756 _fnCallbackFire( settings, null, 'column-sizing', [settings] );
757 }
758
759
760 /**
761 * Covert the index of a visible column to the index in the data array (take account
762 * of hidden columns)
763 * @param {object} oSettings dataTables settings object
764 * @param {int} iMatch Visible column index to lookup
765 * @returns {int} i the data index
766 * @memberof DataTable#oApi
767 */
768 function _fnVisibleToColumnIndex( oSettings, iMatch )
769 {
770 var aiVis = _fnGetColumns( oSettings, 'bVisible' );
771
772 return typeof aiVis[iMatch] === 'number' ?
773 aiVis[iMatch] :
774 null;
775 }
776
777
778 /**
779 * Covert the index of an index in the data array and convert it to the visible
780 * column index (take account of hidden columns)
781 * @param {int} iMatch Column index to lookup
782 * @param {object} oSettings dataTables settings object
783 * @returns {int} i the data index
784 * @memberof DataTable#oApi
785 */
786 function _fnColumnIndexToVisible( oSettings, iMatch )
787 {
788 var aiVis = _fnGetColumns( oSettings, 'bVisible' );
789 var iPos = $.inArray( iMatch, aiVis );
790
791 return iPos !== -1 ? iPos : null;
792 }
793
794
795 /**
796 * Get the number of visible columns
797 * @param {object} oSettings dataTables settings object
798 * @returns {int} i the number of visible columns
799 * @memberof DataTable#oApi
800 */
801 function _fnVisbleColumns( oSettings )
802 {
803 return _fnGetColumns( oSettings, 'bVisible' ).length;
804 }
805
806
807 /**
808 * Get an array of column indexes that match a given property
809 * @param {object} oSettings dataTables settings object
810 * @param {string} sParam Parameter in aoColumns to look for - typically
811 * bVisible or bSearchable
812 * @returns {array} Array of indexes with matched properties
813 * @memberof DataTable#oApi
814 */
815 function _fnGetColumns( oSettings, sParam )
816 {
817 var a = [];
818
819 $.map( oSettings.aoColumns, function(val, i) {
820 if ( val[sParam] ) {
821 a.push( i );
822 }
823 } );
824
825 return a;
826 }
827
828
829 /**
830 * Calculate the 'type' of a column
831 * @param {object} settings dataTables settings object
832 * @memberof DataTable#oApi
833 */
834 function _fnColumnTypes ( settings )
835 {
836 var columns = settings.aoColumns;
837 var data = settings.aoData;
838 var types = DataTable.ext.type.detect;
839 var i, ien, j, jen, k, ken;
840 var col, cell, detectedType, cache;
841
842 // For each column, spin over the
843 for ( i=0, ien=columns.length ; i<ien ; i++ ) {
844 col = columns[i];
845 cache = [];
846
847 if ( ! col.sType && col._sManualType ) {
848 col.sType = col._sManualType;
849 }
850 else if ( ! col.sType ) {
851 for ( j=0, jen=types.length ; j<jen ; j++ ) {
852 for ( k=0, ken=data.length ; k<ken ; k++ ) {
853 // Use a cache array so we only need to get the type data
854 // from the formatter once (when using multiple detectors)
855 if ( cache[k] === undefined ) {
856 cache[k] = _fnGetCellData( settings, k, i, 'type' );
857 }
858
859 detectedType = types[j]( cache[k], settings );
860
861 // If null, then this type can't apply to this column, so
862 // rather than testing all cells, break out. There is an
863 // exception for the last type which is `html`. We need to
864 // scan all rows since it is possible to mix string and HTML
865 // types
866 if ( ! detectedType && j !== types.length-1 ) {
867 break;
868 }
869
870 // Only a single match is needed for html type since it is
871 // bottom of the pile and very similar to string
872 if ( detectedType === 'html' ) {
873 break;
874 }
875 }
876
877 // Type is valid for all data points in the column - use this
878 // type
879 if ( detectedType ) {
880 col.sType = detectedType;
881 break;
882 }
883 }
884
885 // Fall back - if no type was detected, always use string
886 if ( ! col.sType ) {
887 col.sType = 'string';
888 }
889 }
890 }
891 }
892
893
894 /**
895 * Take the column definitions and static columns arrays and calculate how
896 * they relate to column indexes. The callback function will then apply the
897 * definition found for a column to a suitable configuration object.
898 * @param {object} oSettings dataTables settings object
899 * @param {array} aoColDefs The aoColumnDefs array that is to be applied
900 * @param {array} aoCols The aoColumns array that defines columns individually
901 * @param {function} fn Callback function - takes two parameters, the calculated
902 * column index and the definition for that column.
903 * @memberof DataTable#oApi
904 */
905 function _fnApplyColumnDefs( oSettings, aoColDefs, aoCols, fn )
906 {
907 var i, iLen, j, jLen, k, kLen, def;
908 var columns = oSettings.aoColumns;
909
910 // Column definitions with aTargets
911 if ( aoColDefs )
912 {
913 /* Loop over the definitions array - loop in reverse so first instance has priority */
914 for ( i=aoColDefs.length-1 ; i>=0 ; i-- )
915 {
916 def = aoColDefs[i];
917
918 /* Each definition can target multiple columns, as it is an array */
919 var aTargets = def.targets !== undefined ?
920 def.targets :
921 def.aTargets;
922
923 if ( ! $.isArray( aTargets ) )
924 {
925 aTargets = [ aTargets ];
926 }
927
928 for ( j=0, jLen=aTargets.length ; j<jLen ; j++ )
929 {
930 if ( typeof aTargets[j] === 'number' && aTargets[j] >= 0 )
931 {
932 /* Add columns that we don't yet know about */
933 while( columns.length <= aTargets[j] )
934 {
935 _fnAddColumn( oSettings );
936 }
937
938 /* Integer, basic index */
939 fn( aTargets[j], def );
940 }
941 else if ( typeof aTargets[j] === 'number' && aTargets[j] < 0 )
942 {
943 /* Negative integer, right to left column counting */
944 fn( columns.length+aTargets[j], def );
945 }
946 else if ( typeof aTargets[j] === 'string' )
947 {
948 /* Class name matching on TH element */
949 for ( k=0, kLen=columns.length ; k<kLen ; k++ )
950 {
951 if ( aTargets[j] == "_all" ||
952 $(columns[k].nTh).hasClass( aTargets[j] ) )
953 {
954 fn( k, def );
955 }
956 }
957 }
958 }
959 }
960 }
961
962 // Statically defined columns array
963 if ( aoCols )
964 {
965 for ( i=0, iLen=aoCols.length ; i<iLen ; i++ )
966 {
967 fn( i, aoCols[i] );
968 }
969 }
970 }
971
972 /**
973 * Add a data array to the table, creating DOM node etc. This is the parallel to
974 * _fnGatherData, but for adding rows from a Javascript source, rather than a
975 * DOM source.
976 * @param {object} oSettings dataTables settings object
977 * @param {array} aData data array to be added
978 * @param {node} [nTr] TR element to add to the table - optional. If not given,
979 * DataTables will create a row automatically
980 * @param {array} [anTds] Array of TD|TH elements for the row - must be given
981 * if nTr is.
982 * @returns {int} >=0 if successful (index of new aoData entry), -1 if failed
983 * @memberof DataTable#oApi
984 */
985 function _fnAddData ( oSettings, aDataIn, nTr, anTds )
986 {
987 /* Create the object for storing information about this new row */
988 var iRow = oSettings.aoData.length;
989 var oData = $.extend( true, {}, DataTable.models.oRow, {
990 src: nTr ? 'dom' : 'data'
991 } );
992
993 oData._aData = aDataIn;
994 oSettings.aoData.push( oData );
995
996 /* Create the cells */
997 var nTd, sThisType;
998 var columns = oSettings.aoColumns;
999 for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
1000 {
1001 // When working with a row, the data source object must be populated. In
1002 // all other cases, the data source object is already populated, so we
1003 // don't overwrite it, which might break bindings etc
1004 if ( nTr ) {
1005 _fnSetCellData( oSettings, iRow, i, _fnGetCellData( oSettings, iRow, i ) );
1006 }
1007 columns[i].sType = null;
1008 }
1009
1010 /* Add to the display array */
1011 oSettings.aiDisplayMaster.push( iRow );
1012
1013 /* Create the DOM information, or register it if already present */
1014 if ( nTr || ! oSettings.oFeatures.bDeferRender )
1015 {
1016 _fnCreateTr( oSettings, iRow, nTr, anTds );
1017 }
1018
1019 return iRow;
1020 }
1021
1022
1023 /**
1024 * Add one or more TR elements to the table. Generally we'd expect to
1025 * use this for reading data from a DOM sourced table, but it could be
1026 * used for an TR element. Note that if a TR is given, it is used (i.e.
1027 * it is not cloned).
1028 * @param {object} settings dataTables settings object
1029 * @param {array|node|jQuery} trs The TR element(s) to add to the table
1030 * @returns {array} Array of indexes for the added rows
1031 * @memberof DataTable#oApi
1032 */
1033 function _fnAddTr( settings, trs )
1034 {
1035 var row;
1036
1037 // Allow an individual node to be passed in
1038 if ( ! (trs instanceof $) ) {
1039 trs = $(trs);
1040 }
1041
1042 return trs.map( function (i, el) {
1043 row = _fnGetRowElements( settings, el );
1044 return _fnAddData( settings, row.data, el, row.cells );
1045 } );
1046 }
1047
1048
1049 /**
1050 * Take a TR element and convert it to an index in aoData
1051 * @param {object} oSettings dataTables settings object
1052 * @param {node} n the TR element to find
1053 * @returns {int} index if the node is found, null if not
1054 * @memberof DataTable#oApi
1055 */
1056 function _fnNodeToDataIndex( oSettings, n )
1057 {
1058 return (n._DT_RowIndex!==undefined) ? n._DT_RowIndex : null;
1059 }
1060
1061
1062 /**
1063 * Take a TD element and convert it into a column data index (not the visible index)
1064 * @param {object} oSettings dataTables settings object
1065 * @param {int} iRow The row number the TD/TH can be found in
1066 * @param {node} n The TD/TH element to find
1067 * @returns {int} index if the node is found, -1 if not
1068 * @memberof DataTable#oApi
1069 */
1070 function _fnNodeToColumnIndex( oSettings, iRow, n )
1071 {
1072 return $.inArray( n, oSettings.aoData[ iRow ].anCells );
1073 }
1074
1075
1076 /**
1077 * Get the data for a given cell from the internal cache, taking into account data mapping
1078 * @param {object} settings dataTables settings object
1079 * @param {int} rowIdx aoData row id
1080 * @param {int} colIdx Column index
1081 * @param {string} type data get type ('display', 'type' 'filter' 'sort')
1082 * @returns {*} Cell data
1083 * @memberof DataTable#oApi
1084 */
1085 function _fnGetCellData( settings, rowIdx, colIdx, type )
1086 {
1087 var draw = settings.iDraw;
1088 var col = settings.aoColumns[colIdx];
1089 var rowData = settings.aoData[rowIdx]._aData;
1090 var defaultContent = col.sDefaultContent;
1091 var cellData = col.fnGetData( rowData, type, {
1092 settings: settings,
1093 row: rowIdx,
1094 col: colIdx
1095 } );
1096
1097 if ( cellData === undefined ) {
1098 if ( settings.iDrawError != draw && defaultContent === null ) {
1099 _fnLog( settings, 0, "Requested unknown parameter "+
1100 (typeof col.mData=='function' ? '{function}' : "'"+col.mData+"'")+
1101 " for row "+rowIdx, 4 );
1102 settings.iDrawError = draw;
1103 }
1104 return defaultContent;
1105 }
1106
1107 /* When the data source is null, we can use default column data */
1108 if ( (cellData === rowData || cellData === null) && defaultContent !== null ) {
1109 cellData = defaultContent;
1110 }
1111 else if ( typeof cellData === 'function' ) {
1112 // If the data source is a function, then we run it and use the return,
1113 // executing in the scope of the data object (for instances)
1114 return cellData.call( rowData );
1115 }
1116
1117 if ( cellData === null && type == 'display' ) {
1118 return '';
1119 }
1120 return cellData;
1121 }
1122
1123
1124 /**
1125 * Set the value for a specific cell, into the internal data cache
1126 * @param {object} settings dataTables settings object
1127 * @param {int} rowIdx aoData row id
1128 * @param {int} colIdx Column index
1129 * @param {*} val Value to set
1130 * @memberof DataTable#oApi
1131 */
1132 function _fnSetCellData( settings, rowIdx, colIdx, val )
1133 {
1134 var col = settings.aoColumns[colIdx];
1135 var rowData = settings.aoData[rowIdx]._aData;
1136
1137 col.fnSetData( rowData, val, {
1138 settings: settings,
1139 row: rowIdx,
1140 col: colIdx
1141 } );
1142 }
1143
1144
1145 // Private variable that is used to match action syntax in the data property object
1146 var __reArray = /\[.*?\]$/;
1147 var __reFn = /\(\)$/;
1148
1149 /**
1150 * Split string on periods, taking into account escaped periods
1151 * @param {string} str String to split
1152 * @return {array} Split string
1153 */
1154 function _fnSplitObjNotation( str )
1155 {
1156 return $.map( str.match(/(\\.|[^\.])+/g), function ( s ) {
1157 return s.replace(/\\./g, '.');
1158 } );
1159 }
1160
1161
1162 /**
1163 * Return a function that can be used to get data from a source object, taking
1164 * into account the ability to use nested objects as a source
1165 * @param {string|int|function} mSource The data source for the object
1166 * @returns {function} Data get function
1167 * @memberof DataTable#oApi
1168 */
1169 function _fnGetObjectDataFn( mSource )
1170 {
1171 if ( $.isPlainObject( mSource ) )
1172 {
1173 /* Build an object of get functions, and wrap them in a single call */
1174 var o = {};
1175 $.each( mSource, function (key, val) {
1176 if ( val ) {
1177 o[key] = _fnGetObjectDataFn( val );
1178 }
1179 } );
1180
1181 return function (data, type, row, meta) {
1182 var t = o[type] || o._;
1183 return t !== undefined ?
1184 t(data, type, row, meta) :
1185 data;
1186 };
1187 }
1188 else if ( mSource === null )
1189 {
1190 /* Give an empty string for rendering / sorting etc */
1191 return function (data) { // type, row and meta also passed, but not used
1192 return data;
1193 };
1194 }
1195 else if ( typeof mSource === 'function' )
1196 {
1197 return function (data, type, row, meta) {
1198 return mSource( data, type, row, meta );
1199 };
1200 }
1201 else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 ||
1202 mSource.indexOf('[') !== -1 || mSource.indexOf('(') !== -1) )
1203 {
1204 /* If there is a . in the source string then the data source is in a
1205 * nested object so we loop over the data for each level to get the next
1206 * level down. On each loop we test for undefined, and if found immediately
1207 * return. This allows entire objects to be missing and sDefaultContent to
1208 * be used if defined, rather than throwing an error
1209 */
1210 var fetchData = function (data, type, src) {
1211 var arrayNotation, funcNotation, out, innerSrc;
1212
1213 if ( src !== "" )
1214 {
1215 var a = _fnSplitObjNotation( src );
1216
1217 for ( var i=0, iLen=a.length ; i<iLen ; i++ )
1218 {
1219 // Check if we are dealing with special notation
1220 arrayNotation = a[i].match(__reArray);
1221 funcNotation = a[i].match(__reFn);
1222
1223 if ( arrayNotation )
1224 {
1225 // Array notation
1226 a[i] = a[i].replace(__reArray, '');
1227
1228 // Condition allows simply [] to be passed in
1229 if ( a[i] !== "" ) {
1230 data = data[ a[i] ];
1231 }
1232 out = [];
1233
1234 // Get the remainder of the nested object to get
1235 a.splice( 0, i+1 );
1236 innerSrc = a.join('.');
1237
1238 // Traverse each entry in the array getting the properties requested
1239 for ( var j=0, jLen=data.length ; j<jLen ; j++ ) {
1240 out.push( fetchData( data[j], type, innerSrc ) );
1241 }
1242
1243 // If a string is given in between the array notation indicators, that
1244 // is used to join the strings together, otherwise an array is returned
1245 var join = arrayNotation[0].substring(1, arrayNotation[0].length-1);
1246 data = (join==="") ? out : out.join(join);
1247
1248 // The inner call to fetchData has already traversed through the remainder
1249 // of the source requested, so we exit from the loop
1250 break;
1251 }
1252 else if ( funcNotation )
1253 {
1254 // Function call
1255 a[i] = a[i].replace(__reFn, '');
1256 data = data[ a[i] ]();
1257 continue;
1258 }
1259
1260 if ( data === null || data[ a[i] ] === undefined )
1261 {
1262 return undefined;
1263 }
1264 data = data[ a[i] ];
1265 }
1266 }
1267
1268 return data;
1269 };
1270
1271 return function (data, type) { // row and meta also passed, but not used
1272 return fetchData( data, type, mSource );
1273 };
1274 }
1275 else
1276 {
1277 /* Array or flat object mapping */
1278 return function (data, type) { // row and meta also passed, but not used
1279 return data[mSource];
1280 };
1281 }
1282 }
1283
1284
1285 /**
1286 * Return a function that can be used to set data from a source object, taking
1287 * into account the ability to use nested objects as a source
1288 * @param {string|int|function} mSource The data source for the object
1289 * @returns {function} Data set function
1290 * @memberof DataTable#oApi
1291 */
1292 function _fnSetObjectDataFn( mSource )
1293 {
1294 if ( $.isPlainObject( mSource ) )
1295 {
1296 /* Unlike get, only the underscore (global) option is used for for
1297 * setting data since we don't know the type here. This is why an object
1298 * option is not documented for `mData` (which is read/write), but it is
1299 * for `mRender` which is read only.
1300 */
1301 return _fnSetObjectDataFn( mSource._ );
1302 }
1303 else if ( mSource === null )
1304 {
1305 /* Nothing to do when the data source is null */
1306 return function () {};
1307 }
1308 else if ( typeof mSource === 'function' )
1309 {
1310 return function (data, val, meta) {
1311 mSource( data, 'set', val, meta );
1312 };
1313 }
1314 else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 ||
1315 mSource.indexOf('[') !== -1 || mSource.indexOf('(') !== -1) )
1316 {
1317 /* Like the get, we need to get data from a nested object */
1318 var setData = function (data, val, src) {
1319 var a = _fnSplitObjNotation( src ), b;
1320 var aLast = a[a.length-1];
1321 var arrayNotation, funcNotation, o, innerSrc;
1322
1323 for ( var i=0, iLen=a.length-1 ; i<iLen ; i++ )
1324 {
1325 // Check if we are dealing with an array notation request
1326 arrayNotation = a[i].match(__reArray);
1327 funcNotation = a[i].match(__reFn);
1328
1329 if ( arrayNotation )
1330 {
1331 a[i] = a[i].replace(__reArray, '');
1332 data[ a[i] ] = [];
1333
1334 // Get the remainder of the nested object to set so we can recurse
1335 b = a.slice();
1336 b.splice( 0, i+1 );
1337 innerSrc = b.join('.');
1338
1339 // Traverse each entry in the array setting the properties requested
1340 for ( var j=0, jLen=val.length ; j<jLen ; j++ )
1341 {
1342 o = {};
1343 setData( o, val[j], innerSrc );
1344 data[ a[i] ].push( o );
1345 }
1346
1347 // The inner call to setData has already traversed through the remainder
1348 // of the source and has set the data, thus we can exit here
1349 return;
1350 }
1351 else if ( funcNotation )
1352 {
1353 // Function call
1354 a[i] = a[i].replace(__reFn, '');
1355 data = data[ a[i] ]( val );
1356 }
1357
1358 // If the nested object doesn't currently exist - since we are
1359 // trying to set the value - create it
1360 if ( data[ a[i] ] === null || data[ a[i] ] === undefined )
1361 {
1362 data[ a[i] ] = {};
1363 }
1364 data = data[ a[i] ];
1365 }
1366
1367 // Last item in the input - i.e, the actual set
1368 if ( aLast.match(__reFn ) )
1369 {
1370 // Function call
1371 data = data[ aLast.replace(__reFn, '') ]( val );
1372 }
1373 else
1374 {
1375 // If array notation is used, we just want to strip it and use the property name
1376 // and assign the value. If it isn't used, then we get the result we want anyway
1377 data[ aLast.replace(__reArray, '') ] = val;
1378 }
1379 };
1380
1381 return function (data, val) { // meta is also passed in, but not used
1382 return setData( data, val, mSource );
1383 };
1384 }
1385 else
1386 {
1387 /* Array or flat object mapping */
1388 return function (data, val) { // meta is also passed in, but not used
1389 data[mSource] = val;
1390 };
1391 }
1392 }
1393
1394
1395 /**
1396 * Return an array with the full table data
1397 * @param {object} oSettings dataTables settings object
1398 * @returns array {array} aData Master data array
1399 * @memberof DataTable#oApi
1400 */
1401 function _fnGetDataMaster ( settings )
1402 {
1403 return _pluck( settings.aoData, '_aData' );
1404 }
1405
1406
1407 /**
1408 * Nuke the table
1409 * @param {object} oSettings dataTables settings object
1410 * @memberof DataTable#oApi
1411 */
1412 function _fnClearTable( settings )
1413 {
1414 settings.aoData.length = 0;
1415 settings.aiDisplayMaster.length = 0;
1416 settings.aiDisplay.length = 0;
1417 }
1418
1419
1420 /**
1421 * Take an array of integers (index array) and remove a target integer (value - not
1422 * the key!)
1423 * @param {array} a Index array to target
1424 * @param {int} iTarget value to find
1425 * @memberof DataTable#oApi
1426 */
1427 function _fnDeleteIndex( a, iTarget, splice )
1428 {
1429 var iTargetIndex = -1;
1430
1431 for ( var i=0, iLen=a.length ; i<iLen ; i++ )
1432 {
1433 if ( a[i] == iTarget )
1434 {
1435 iTargetIndex = i;
1436 }
1437 else if ( a[i] > iTarget )
1438 {
1439 a[i]--;
1440 }
1441 }
1442
1443 if ( iTargetIndex != -1 && splice === undefined )
1444 {
1445 a.splice( iTargetIndex, 1 );
1446 }
1447 }
1448
1449
1450 /**
1451 * Mark cached data as invalid such that a re-read of the data will occur when
1452 * the cached data is next requested. Also update from the data source object.
1453 *
1454 * @param {object} settings DataTables settings object
1455 * @param {int} rowIdx Row index to invalidate
1456 * @param {string} [src] Source to invalidate from: undefined, 'auto', 'dom'
1457 * or 'data'
1458 * @param {int} [colIdx] Column index to invalidate. If undefined the whole
1459 * row will be invalidated
1460 * @memberof DataTable#oApi
1461 *
1462 * @todo For the modularisation of v1.11 this will need to become a callback, so
1463 * the sort and filter methods can subscribe to it. That will required
1464 * initialisation options for sorting, which is why it is not already baked in
1465 */
1466 function _fnInvalidate( settings, rowIdx, src, colIdx )
1467 {
1468 var row = settings.aoData[ rowIdx ];
1469 var i, ien;
1470 var cellWrite = function ( cell, col ) {
1471 // This is very frustrating, but in IE if you just write directly
1472 // to innerHTML, and elements that are overwritten are GC'ed,
1473 // even if there is a reference to them elsewhere
1474 while ( cell.childNodes.length ) {
1475 cell.removeChild( cell.firstChild );
1476 }
1477
1478 cell.innerHTML = _fnGetCellData( settings, rowIdx, col, 'display' );
1479 };
1480
1481 // Are we reading last data from DOM or the data object?
1482 if ( src === 'dom' || ((! src || src === 'auto') && row.src === 'dom') ) {
1483 // Read the data from the DOM
1484 row._aData = _fnGetRowElements(
1485 settings, row, colIdx, colIdx === undefined ? undefined : row._aData
1486 )
1487 .data;
1488 }
1489 else {
1490 // Reading from data object, update the DOM
1491 var cells = row.anCells;
1492
1493 if ( cells ) {
1494 if ( colIdx !== undefined ) {
1495 cellWrite( cells[colIdx], colIdx );
1496 }
1497 else {
1498 for ( i=0, ien=cells.length ; i<ien ; i++ ) {
1499 cellWrite( cells[i], i );
1500 }
1501 }
1502 }
1503 }
1504
1505 // For both row and cell invalidation, the cached data for sorting and
1506 // filtering is nulled out
1507 row._aSortData = null;
1508 row._aFilterData = null;
1509
1510 // Invalidate the type for a specific column (if given) or all columns since
1511 // the data might have changed
1512 var cols = settings.aoColumns;
1513 if ( colIdx !== undefined ) {
1514 cols[ colIdx ].sType = null;
1515 }
1516 else {
1517 for ( i=0, ien=cols.length ; i<ien ; i++ ) {
1518 cols[i].sType = null;
1519 }
1520
1521 // Update DataTables special `DT_*` attributes for the row
1522 _fnRowAttributes( row );
1523 }
1524 }
1525
1526
1527 /**
1528 * Build a data source object from an HTML row, reading the contents of the
1529 * cells that are in the row.
1530 *
1531 * @param {object} settings DataTables settings object
1532 * @param {node|object} TR element from which to read data or existing row
1533 * object from which to re-read the data from the cells
1534 * @param {int} [colIdx] Optional column index
1535 * @param {array|object} [d] Data source object. If `colIdx` is given then this
1536 * parameter should also be given and will be used to write the data into.
1537 * Only the column in question will be written
1538 * @returns {object} Object with two parameters: `data` the data read, in
1539 * document order, and `cells` and array of nodes (they can be useful to the
1540 * caller, so rather than needing a second traversal to get them, just return
1541 * them from here).
1542 * @memberof DataTable#oApi
1543 */
1544 function _fnGetRowElements( settings, row, colIdx, d )
1545 {
1546 var
1547 tds = [],
1548 td = row.firstChild,
1549 name, col, o, i=0, contents,
1550 columns = settings.aoColumns,
1551 objectRead = settings._rowReadObject;
1552
1553 // Allow the data object to be passed in, or construct
1554 d = d || objectRead ? {} : [];
1555
1556 var attr = function ( str, td ) {
1557 if ( typeof str === 'string' ) {
1558 var idx = str.indexOf('@');
1559
1560 if ( idx !== -1 ) {
1561 var attr = str.substring( idx+1 );
1562 var setter = _fnSetObjectDataFn( str );
1563 setter( d, td.getAttribute( attr ) );
1564 }
1565 }
1566 };
1567
1568 // Read data from a cell and store into the data object
1569 var cellProcess = function ( cell ) {
1570 if ( colIdx === undefined || colIdx === i ) {
1571 col = columns[i];
1572 contents = $.trim(cell.innerHTML);
1573
1574 if ( col && col._bAttrSrc ) {
1575 var setter = _fnSetObjectDataFn( col.mData._ );
1576 setter( d, contents );
1577
1578 attr( col.mData.sort, cell );
1579 attr( col.mData.type, cell );
1580 attr( col.mData.filter, cell );
1581 }
1582 else {
1583 // Depending on the `data` option for the columns the data can
1584 // be read to either an object or an array.
1585 if ( objectRead ) {
1586 if ( ! col._setter ) {
1587 // Cache the setter function
1588 col._setter = _fnSetObjectDataFn( col.mData );
1589 }
1590 col._setter( d, contents );
1591 }
1592 else {
1593 d[i] = contents;
1594 }
1595 }
1596 }
1597
1598 i++;
1599 };
1600
1601 if ( td ) {
1602 // `tr` element was passed in
1603 while ( td ) {
1604 name = td.nodeName.toUpperCase();
1605
1606 if ( name == "TD" || name == "TH" ) {
1607 cellProcess( td );
1608 tds.push( td );
1609 }
1610
1611 td = td.nextSibling;
1612 }
1613 }
1614 else {
1615 // Existing row object passed in
1616 tds = row.anCells;
1617
1618 for ( var j=0, jen=tds.length ; j<jen ; j++ ) {
1619 cellProcess( tds[j] );
1620 }
1621 }
1622
1623 return {
1624 data: d,
1625 cells: tds
1626 };
1627 }
1628 /**
1629 * Create a new TR element (and it's TD children) for a row
1630 * @param {object} oSettings dataTables settings object
1631 * @param {int} iRow Row to consider
1632 * @param {node} [nTrIn] TR element to add to the table - optional. If not given,
1633 * DataTables will create a row automatically
1634 * @param {array} [anTds] Array of TD|TH elements for the row - must be given
1635 * if nTr is.
1636 * @memberof DataTable#oApi
1637 */
1638 function _fnCreateTr ( oSettings, iRow, nTrIn, anTds )
1639 {
1640 var
1641 row = oSettings.aoData[iRow],
1642 rowData = row._aData,
1643 cells = [],
1644 nTr, nTd, oCol,
1645 i, iLen;
1646
1647 if ( row.nTr === null )
1648 {
1649 nTr = nTrIn || document.createElement('tr');
1650
1651 row.nTr = nTr;
1652 row.anCells = cells;
1653
1654 /* Use a private property on the node to allow reserve mapping from the node
1655 * to the aoData array for fast look up
1656 */
1657 nTr._DT_RowIndex = iRow;
1658
1659 /* Special parameters can be given by the data source to be used on the row */
1660 _fnRowAttributes( row );
1661
1662 /* Process each column */
1663 for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
1664 {
1665 oCol = oSettings.aoColumns[i];
1666
1667 nTd = nTrIn ? anTds[i] : document.createElement( oCol.sCellType );
1668 cells.push( nTd );
1669
1670 // Need to create the HTML if new, or if a rendering function is defined
1671 if ( !nTrIn || oCol.mRender || oCol.mData !== i )
1672 {
1673 nTd.innerHTML = _fnGetCellData( oSettings, iRow, i, 'display' );
1674 }
1675
1676 /* Add user defined class */
1677 if ( oCol.sClass )
1678 {
1679 nTd.className += ' '+oCol.sClass;
1680 }
1681
1682 // Visibility - add or remove as required
1683 if ( oCol.bVisible && ! nTrIn )
1684 {
1685 nTr.appendChild( nTd );
1686 }
1687 else if ( ! oCol.bVisible && nTrIn )
1688 {
1689 nTd.parentNode.removeChild( nTd );
1690 }
1691
1692 if ( oCol.fnCreatedCell )
1693 {
1694 oCol.fnCreatedCell.call( oSettings.oInstance,
1695 nTd, _fnGetCellData( oSettings, iRow, i ), rowData, iRow, i
1696 );
1697 }
1698 }
1699
1700 _fnCallbackFire( oSettings, 'aoRowCreatedCallback', null, [nTr, rowData, iRow] );
1701 }
1702
1703 // Remove once webkit bug 131819 and Chromium bug 365619 have been resolved
1704 // and deployed
1705 row.nTr.setAttribute( 'role', 'row' );
1706 }
1707
1708
1709 /**
1710 * Add attributes to a row based on the special `DT_*` parameters in a data
1711 * source object.
1712 * @param {object} DataTables row object for the row to be modified
1713 * @memberof DataTable#oApi
1714 */
1715 function _fnRowAttributes( row )
1716 {
1717 var tr = row.nTr;
1718 var data = row._aData;
1719
1720 if ( tr ) {
1721 if ( data.DT_RowId ) {
1722 tr.id = data.DT_RowId;
1723 }
1724
1725 if ( data.DT_RowClass ) {
1726 // Remove any classes added by DT_RowClass before
1727 var a = data.DT_RowClass.split(' ');
1728 row.__rowc = row.__rowc ?
1729 _unique( row.__rowc.concat( a ) ) :
1730 a;
1731
1732 $(tr)
1733 .removeClass( row.__rowc.join(' ') )
1734 .addClass( data.DT_RowClass );
1735 }
1736
1737 if ( data.DT_RowData ) {
1738 $(tr).data( data.DT_RowData );
1739 }
1740 }
1741 }
1742
1743
1744 /**
1745 * Create the HTML header for the table
1746 * @param {object} oSettings dataTables settings object
1747 * @memberof DataTable#oApi
1748 */
1749 function _fnBuildHead( oSettings )
1750 {
1751 var i, ien, cell, row, column;
1752 var thead = oSettings.nTHead;
1753 var tfoot = oSettings.nTFoot;
1754 var createHeader = $('th, td', thead).length === 0;
1755 var classes = oSettings.oClasses;
1756 var columns = oSettings.aoColumns;
1757
1758 if ( createHeader ) {
1759 row = $('<tr/>').appendTo( thead );
1760 }
1761
1762 for ( i=0, ien=columns.length ; i<ien ; i++ ) {
1763 column = columns[i];
1764 cell = $( column.nTh ).addClass( column.sClass );
1765
1766 if ( createHeader ) {
1767 cell.appendTo( row );
1768 }
1769
1770 // 1.11 move into sorting
1771 if ( oSettings.oFeatures.bSort ) {
1772 cell.addClass( column.sSortingClass );
1773
1774 if ( column.bSortable !== false ) {
1775 cell
1776 .attr( 'tabindex', oSettings.iTabIndex )
1777 .attr( 'aria-controls', oSettings.sTableId );
1778
1779 _fnSortAttachListener( oSettings, column.nTh, i );
1780 }
1781 }
1782
1783 if ( column.sTitle != cell.html() ) {
1784 cell.html( column.sTitle );
1785 }
1786
1787 _fnRenderer( oSettings, 'header' )(
1788 oSettings, cell, column, classes
1789 );
1790 }
1791
1792 if ( createHeader ) {
1793 _fnDetectHeader( oSettings.aoHeader, thead );
1794 }
1795
1796 /* ARIA role for the rows */
1797 $(thead).find('>tr').attr('role', 'row');
1798
1799 /* Deal with the footer - add classes if required */
1800 $(thead).find('>tr>th, >tr>td').addClass( classes.sHeaderTH );
1801 $(tfoot).find('>tr>th, >tr>td').addClass( classes.sFooterTH );
1802
1803 // Cache the footer cells. Note that we only take the cells from the first
1804 // row in the footer. If there is more than one row the user wants to
1805 // interact with, they need to use the table().foot() method. Note also this
1806 // allows cells to be used for multiple columns using colspan
1807 if ( tfoot !== null ) {
1808 var cells = oSettings.aoFooter[0];
1809
1810 for ( i=0, ien=cells.length ; i<ien ; i++ ) {
1811 column = columns[i];
1812 column.nTf = cells[i].cell;
1813
1814 if ( column.sClass ) {
1815 $(column.nTf).addClass( column.sClass );
1816 }
1817 }
1818 }
1819 }
1820
1821
1822 /**
1823 * Draw the header (or footer) element based on the column visibility states. The
1824 * methodology here is to use the layout array from _fnDetectHeader, modified for
1825 * the instantaneous column visibility, to construct the new layout. The grid is
1826 * traversed over cell at a time in a rows x columns grid fashion, although each
1827 * cell insert can cover multiple elements in the grid - which is tracks using the
1828 * aApplied array. Cell inserts in the grid will only occur where there isn't
1829 * already a cell in that position.
1830 * @param {object} oSettings dataTables settings object
1831 * @param array {objects} aoSource Layout array from _fnDetectHeader
1832 * @param {boolean} [bIncludeHidden=false] If true then include the hidden columns in the calc,
1833 * @memberof DataTable#oApi
1834 */
1835 function _fnDrawHead( oSettings, aoSource, bIncludeHidden )
1836 {
1837 var i, iLen, j, jLen, k, kLen, n, nLocalTr;
1838 var aoLocal = [];
1839 var aApplied = [];
1840 var iColumns = oSettings.aoColumns.length;
1841 var iRowspan, iColspan;
1842
1843 if ( ! aoSource )
1844 {
1845 return;
1846 }
1847
1848 if ( bIncludeHidden === undefined )
1849 {
1850 bIncludeHidden = false;
1851 }
1852
1853 /* Make a copy of the master layout array, but without the visible columns in it */
1854 for ( i=0, iLen=aoSource.length ; i<iLen ; i++ )
1855 {
1856 aoLocal[i] = aoSource[i].slice();
1857 aoLocal[i].nTr = aoSource[i].nTr;
1858
1859 /* Remove any columns which are currently hidden */
1860 for ( j=iColumns-1 ; j>=0 ; j-- )
1861 {
1862 if ( !oSettings.aoColumns[j].bVisible && !bIncludeHidden )
1863 {
1864 aoLocal[i].splice( j, 1 );
1865 }
1866 }
1867
1868 /* Prep the applied array - it needs an element for each row */
1869 aApplied.push( [] );
1870 }
1871
1872 for ( i=0, iLen=aoLocal.length ; i<iLen ; i++ )
1873 {
1874 nLocalTr = aoLocal[i].nTr;
1875
1876 /* All cells are going to be replaced, so empty out the row */
1877 if ( nLocalTr )
1878 {
1879 while( (n = nLocalTr.firstChild) )
1880 {
1881 nLocalTr.removeChild( n );
1882 }
1883 }
1884
1885 for ( j=0, jLen=aoLocal[i].length ; j<jLen ; j++ )
1886 {
1887 iRowspan = 1;
1888 iColspan = 1;
1889
1890 /* Check to see if there is already a cell (row/colspan) covering our target
1891 * insert point. If there is, then there is nothing to do.
1892 */
1893 if ( aApplied[i][j] === undefined )
1894 {
1895 nLocalTr.appendChild( aoLocal[i][j].cell );
1896 aApplied[i][j] = 1;
1897
1898 /* Expand the cell to cover as many rows as needed */
1899 while ( aoLocal[i+iRowspan] !== undefined &&
1900 aoLocal[i][j].cell == aoLocal[i+iRowspan][j].cell )
1901 {
1902 aApplied[i+iRowspan][j] = 1;
1903 iRowspan++;
1904 }
1905
1906 /* Expand the cell to cover as many columns as needed */
1907 while ( aoLocal[i][j+iColspan] !== undefined &&
1908 aoLocal[i][j].cell == aoLocal[i][j+iColspan].cell )
1909 {
1910 /* Must update the applied array over the rows for the columns */
1911 for ( k=0 ; k<iRowspan ; k++ )
1912 {
1913 aApplied[i+k][j+iColspan] = 1;
1914 }
1915 iColspan++;
1916 }
1917
1918 /* Do the actual expansion in the DOM */
1919 $(aoLocal[i][j].cell)
1920 .attr('rowspan', iRowspan)
1921 .attr('colspan', iColspan);
1922 }
1923 }
1924 }
1925 }
1926
1927
1928 /**
1929 * Insert the required TR nodes into the table for display
1930 * @param {object} oSettings dataTables settings object
1931 * @memberof DataTable#oApi
1932 */
1933 function _fnDraw( oSettings )
1934 {
1935 /* Provide a pre-callback function which can be used to cancel the draw is false is returned */
1936 var aPreDraw = _fnCallbackFire( oSettings, 'aoPreDrawCallback', 'preDraw', [oSettings] );
1937 if ( $.inArray( false, aPreDraw ) !== -1 )
1938 {
1939 _fnProcessingDisplay( oSettings, false );
1940 return;
1941 }
1942
1943 var i, iLen, n;
1944 var anRows = [];
1945 var iRowCount = 0;
1946 var asStripeClasses = oSettings.asStripeClasses;
1947 var iStripes = asStripeClasses.length;
1948 var iOpenRows = oSettings.aoOpenRows.length;
1949 var oLang = oSettings.oLanguage;
1950 var iInitDisplayStart = oSettings.iInitDisplayStart;
1951 var bServerSide = _fnDataSource( oSettings ) == 'ssp';
1952 var aiDisplay = oSettings.aiDisplay;
1953
1954 oSettings.bDrawing = true;
1955
1956 /* Check and see if we have an initial draw position from state saving */
1957 if ( iInitDisplayStart !== undefined && iInitDisplayStart !== -1 )
1958 {
1959 oSettings._iDisplayStart = bServerSide ?
1960 iInitDisplayStart :
1961 iInitDisplayStart >= oSettings.fnRecordsDisplay() ?
1962 0 :
1963 iInitDisplayStart;
1964
1965 oSettings.iInitDisplayStart = -1;
1966 }
1967
1968 var iDisplayStart = oSettings._iDisplayStart;
1969 var iDisplayEnd = oSettings.fnDisplayEnd();
1970
1971 /* Server-side processing draw intercept */
1972 if ( oSettings.bDeferLoading )
1973 {
1974 oSettings.bDeferLoading = false;
1975 oSettings.iDraw++;
1976 _fnProcessingDisplay( oSettings, false );
1977 }
1978 else if ( !bServerSide )
1979 {
1980 oSettings.iDraw++;
1981 }
1982 else if ( !oSettings.bDestroying && !_fnAjaxUpdate( oSettings ) )
1983 {
1984 return;
1985 }
1986
1987 if ( aiDisplay.length !== 0 )
1988 {
1989 var iStart = bServerSide ? 0 : iDisplayStart;
1990 var iEnd = bServerSide ? oSettings.aoData.length : iDisplayEnd;
1991
1992 for ( var j=iStart ; j<iEnd ; j++ )
1993 {
1994 var iDataIndex = aiDisplay[j];
1995 var aoData = oSettings.aoData[ iDataIndex ];
1996 if ( aoData.nTr === null )
1997 {
1998 _fnCreateTr( oSettings, iDataIndex );
1999 }
2000
2001 var nRow = aoData.nTr;
2002
2003 /* Remove the old striping classes and then add the new one */
2004 if ( iStripes !== 0 )
2005 {
2006 var sStripe = asStripeClasses[ iRowCount % iStripes ];
2007 if ( aoData._sRowStripe != sStripe )
2008 {
2009 $(nRow).removeClass( aoData._sRowStripe ).addClass( sStripe );
2010 aoData._sRowStripe = sStripe;
2011 }
2012 }
2013
2014 // Row callback functions - might want to manipulate the row
2015 // iRowCount and j are not currently documented. Are they at all
2016 // useful?
2017 _fnCallbackFire( oSettings, 'aoRowCallback', null,
2018 [nRow, aoData._aData, iRowCount, j] );
2019
2020 anRows.push( nRow );
2021 iRowCount++;
2022 }
2023 }
2024 else
2025 {
2026 /* Table is empty - create a row with an empty message in it */
2027 var sZero = oLang.sZeroRecords;
2028 if ( oSettings.iDraw == 1 && _fnDataSource( oSettings ) == 'ajax' )
2029 {
2030 sZero = oLang.sLoadingRecords;
2031 }
2032 else if ( oLang.sEmptyTable && oSettings.fnRecordsTotal() === 0 )
2033 {
2034 sZero = oLang.sEmptyTable;
2035 }
2036
2037 anRows[ 0 ] = $( '<tr/>', { 'class': iStripes ? asStripeClasses[0] : '' } )
2038 .append( $('<td />', {
2039 'valign': 'top',
2040 'colSpan': _fnVisbleColumns( oSettings ),
2041 'class': oSettings.oClasses.sRowEmpty
2042 } ).html( sZero ) )[0];
2043 }
2044
2045 /* Header and footer callbacks */
2046 _fnCallbackFire( oSettings, 'aoHeaderCallback', 'header', [ $(oSettings.nTHead).children('tr')[0],
2047 _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );
2048
2049 _fnCallbackFire( oSettings, 'aoFooterCallback', 'footer', [ $(oSettings.nTFoot).children('tr')[0],
2050 _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );
2051
2052 var body = $(oSettings.nTBody);
2053
2054 body.children().detach();
2055 body.append( $(anRows) );
2056
2057 /* Call all required callback functions for the end of a draw */
2058 _fnCallbackFire( oSettings, 'aoDrawCallback', 'draw', [oSettings] );
2059
2060 /* Draw is complete, sorting and filtering must be as well */
2061 oSettings.bSorted = false;
2062 oSettings.bFiltered = false;
2063 oSettings.bDrawing = false;
2064 }
2065
2066
2067 /**
2068 * Redraw the table - taking account of the various features which are enabled
2069 * @param {object} oSettings dataTables settings object
2070 * @param {boolean} [holdPosition] Keep the current paging position. By default
2071 * the paging is reset to the first page
2072 * @memberof DataTable#oApi
2073 */
2074 function _fnReDraw( settings, holdPosition )
2075 {
2076 var
2077 features = settings.oFeatures,
2078 sort = features.bSort,
2079 filter = features.bFilter;
2080
2081 if ( sort ) {
2082 _fnSort( settings );
2083 }
2084
2085 if ( filter ) {
2086 _fnFilterComplete( settings, settings.oPreviousSearch );
2087 }
2088 else {
2089 // No filtering, so we want to just use the display master
2090 settings.aiDisplay = settings.aiDisplayMaster.slice();
2091 }
2092
2093 if ( holdPosition !== true ) {
2094 settings._iDisplayStart = 0;
2095 }
2096
2097 // Let any modules know about the draw hold position state (used by
2098 // scrolling internally)
2099 settings._drawHold = holdPosition;
2100
2101 _fnDraw( settings );
2102
2103 settings._drawHold = false;
2104 }
2105
2106
2107 /**
2108 * Add the options to the page HTML for the table
2109 * @param {object} oSettings dataTables settings object
2110 * @memberof DataTable#oApi
2111 */
2112 function _fnAddOptionsHtml ( oSettings )
2113 {
2114 var classes = oSettings.oClasses;
2115 var table = $(oSettings.nTable);
2116 var holding = $('<div/>').insertBefore( table ); // Holding element for speed
2117 var features = oSettings.oFeatures;
2118
2119 // All DataTables are wrapped in a div
2120 var insert = $('<div/>', {
2121 id: oSettings.sTableId+'_wrapper',
2122 'class': classes.sWrapper + (oSettings.nTFoot ? '' : ' '+classes.sNoFooter)
2123 } );
2124
2125 oSettings.nHolding = holding[0];
2126 oSettings.nTableWrapper = insert[0];
2127 oSettings.nTableReinsertBefore = oSettings.nTable.nextSibling;
2128
2129 /* Loop over the user set positioning and place the elements as needed */
2130 var aDom = oSettings.sDom.split('');
2131 var featureNode, cOption, nNewNode, cNext, sAttr, j;
2132 for ( var i=0 ; i<aDom.length ; i++ )
2133 {
2134 featureNode = null;
2135 cOption = aDom[i];
2136
2137 if ( cOption == '<' )
2138 {
2139 /* New container div */
2140 nNewNode = $('<div/>')[0];
2141
2142 /* Check to see if we should append an id and/or a class name to the container */
2143 cNext = aDom[i+1];
2144 if ( cNext == "'" || cNext == '"' )
2145 {
2146 sAttr = "";
2147 j = 2;
2148 while ( aDom[i+j] != cNext )
2149 {
2150 sAttr += aDom[i+j];
2151 j++;
2152 }
2153
2154 /* Replace jQuery UI constants @todo depreciated */
2155 if ( sAttr == "H" )
2156 {
2157 sAttr = classes.sJUIHeader;
2158 }
2159 else if ( sAttr == "F" )
2160 {
2161 sAttr = classes.sJUIFooter;
2162 }
2163
2164 /* The attribute can be in the format of "#id.class", "#id" or "class" This logic
2165 * breaks the string into parts and applies them as needed
2166 */
2167 if ( sAttr.indexOf('.') != -1 )
2168 {
2169 var aSplit = sAttr.split('.');
2170 nNewNode.id = aSplit[0].substr(1, aSplit[0].length-1);
2171 nNewNode.className = aSplit[1];
2172 }
2173 else if ( sAttr.charAt(0) == "#" )
2174 {
2175 nNewNode.id = sAttr.substr(1, sAttr.length-1);
2176 }
2177 else
2178 {
2179 nNewNode.className = sAttr;
2180 }
2181
2182 i += j; /* Move along the position array */
2183 }
2184
2185 insert.append( nNewNode );
2186 insert = $(nNewNode);
2187 }
2188 else if ( cOption == '>' )
2189 {
2190 /* End container div */
2191 insert = insert.parent();
2192 }
2193 // @todo Move options into their own plugins?
2194 else if ( cOption == 'l' && features.bPaginate && features.bLengthChange )
2195 {
2196 /* Length */
2197 featureNode = _fnFeatureHtmlLength( oSettings );
2198 }
2199 else if ( cOption == 'f' && features.bFilter )
2200 {
2201 /* Filter */
2202 featureNode = _fnFeatureHtmlFilter( oSettings );
2203 }
2204 else if ( cOption == 'r' && features.bProcessing )
2205 {
2206 /* pRocessing */
2207 featureNode = _fnFeatureHtmlProcessing( oSettings );
2208 }
2209 else if ( cOption == 't' )
2210 {
2211 /* Table */
2212 featureNode = _fnFeatureHtmlTable( oSettings );
2213 }
2214 else if ( cOption == 'i' && features.bInfo )
2215 {
2216 /* Info */
2217 featureNode = _fnFeatureHtmlInfo( oSettings );
2218 }
2219 else if ( cOption == 'p' && features.bPaginate )
2220 {
2221 /* Pagination */
2222 featureNode = _fnFeatureHtmlPaginate( oSettings );
2223 }
2224 else if ( DataTable.ext.feature.length !== 0 )
2225 {
2226 /* Plug-in features */
2227 var aoFeatures = DataTable.ext.feature;
2228 for ( var k=0, kLen=aoFeatures.length ; k<kLen ; k++ )
2229 {
2230 if ( cOption == aoFeatures[k].cFeature )
2231 {
2232 featureNode = aoFeatures[k].fnInit( oSettings );
2233 break;
2234 }
2235 }
2236 }
2237
2238 /* Add to the 2D features array */
2239 if ( featureNode )
2240 {
2241 var aanFeatures = oSettings.aanFeatures;
2242
2243 if ( ! aanFeatures[cOption] )
2244 {
2245 aanFeatures[cOption] = [];
2246 }
2247
2248 aanFeatures[cOption].push( featureNode );
2249 insert.append( featureNode );
2250 }
2251 }
2252
2253 /* Built our DOM structure - replace the holding div with what we want */
2254 holding.replaceWith( insert );
2255 }
2256
2257
2258 /**
2259 * Use the DOM source to create up an array of header cells. The idea here is to
2260 * create a layout grid (array) of rows x columns, which contains a reference
2261 * to the cell that that point in the grid (regardless of col/rowspan), such that
2262 * any column / row could be removed and the new grid constructed
2263 * @param array {object} aLayout Array to store the calculated layout in
2264 * @param {node} nThead The header/footer element for the table
2265 * @memberof DataTable#oApi
2266 */
2267 function _fnDetectHeader ( aLayout, nThead )
2268 {
2269 var nTrs = $(nThead).children('tr');
2270 var nTr, nCell;
2271 var i, k, l, iLen, jLen, iColShifted, iColumn, iColspan, iRowspan;
2272 var bUnique;
2273 var fnShiftCol = function ( a, i, j ) {
2274 var k = a[i];
2275 while ( k[j] ) {
2276 j++;
2277 }
2278 return j;
2279 };
2280
2281 aLayout.splice( 0, aLayout.length );
2282
2283 /* We know how many rows there are in the layout - so prep it */
2284 for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
2285 {
2286 aLayout.push( [] );
2287 }
2288
2289 /* Calculate a layout array */
2290 for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
2291 {
2292 nTr = nTrs[i];
2293 iColumn = 0;
2294
2295 /* For every cell in the row... */
2296 nCell = nTr.firstChild;
2297 while ( nCell ) {
2298 if ( nCell.nodeName.toUpperCase() == "TD" ||
2299 nCell.nodeName.toUpperCase() == "TH" )
2300 {
2301 /* Get the col and rowspan attributes from the DOM and sanitise them */
2302 iColspan = nCell.getAttribute('colspan') * 1;
2303 iRowspan = nCell.getAttribute('rowspan') * 1;
2304 iColspan = (!iColspan || iColspan===0 || iColspan===1) ? 1 : iColspan;
2305 iRowspan = (!iRowspan || iRowspan===0 || iRowspan===1) ? 1 : iRowspan;
2306
2307 /* There might be colspan cells already in this row, so shift our target
2308 * accordingly
2309 */
2310 iColShifted = fnShiftCol( aLayout, i, iColumn );
2311
2312 /* Cache calculation for unique columns */
2313 bUnique = iColspan === 1 ? true : false;
2314
2315 /* If there is col / rowspan, copy the information into the layout grid */
2316 for ( l=0 ; l<iColspan ; l++ )
2317 {
2318 for ( k=0 ; k<iRowspan ; k++ )
2319 {
2320 aLayout[i+k][iColShifted+l] = {
2321 "cell": nCell,
2322 "unique": bUnique
2323 };
2324 aLayout[i+k].nTr = nTr;
2325 }
2326 }
2327 }
2328 nCell = nCell.nextSibling;
2329 }
2330 }
2331 }
2332
2333
2334 /**
2335 * Get an array of unique th elements, one for each column
2336 * @param {object} oSettings dataTables settings object
2337 * @param {node} nHeader automatically detect the layout from this node - optional
2338 * @param {array} aLayout thead/tfoot layout from _fnDetectHeader - optional
2339 * @returns array {node} aReturn list of unique th's
2340 * @memberof DataTable#oApi
2341 */
2342 function _fnGetUniqueThs ( oSettings, nHeader, aLayout )
2343 {
2344 var aReturn = [];
2345 if ( !aLayout )
2346 {
2347 aLayout = oSettings.aoHeader;
2348 if ( nHeader )
2349 {
2350 aLayout = [];
2351 _fnDetectHeader( aLayout, nHeader );
2352 }
2353 }
2354
2355 for ( var i=0, iLen=aLayout.length ; i<iLen ; i++ )
2356 {
2357 for ( var j=0, jLen=aLayout[i].length ; j<jLen ; j++ )
2358 {
2359 if ( aLayout[i][j].unique &&
2360 (!aReturn[j] || !oSettings.bSortCellsTop) )
2361 {
2362 aReturn[j] = aLayout[i][j].cell;
2363 }
2364 }
2365 }
2366
2367 return aReturn;
2368 }
2369
2370
2371
2372 /**
2373 * Create an Ajax call based on the table's settings, taking into account that
2374 * parameters can have multiple forms, and backwards compatibility.
2375 *
2376 * @param {object} oSettings dataTables settings object
2377 * @param {array} data Data to send to the server, required by
2378 * DataTables - may be augmented by developer callbacks
2379 * @param {function} fn Callback function to run when data is obtained
2380 */
2381 function _fnBuildAjax( oSettings, data, fn )
2382 {
2383 // Compatibility with 1.9-, allow fnServerData and event to manipulate
2384 _fnCallbackFire( oSettings, 'aoServerParams', 'serverParams', [data] );
2385
2386 // Convert to object based for 1.10+ if using the old array scheme which can
2387 // come from server-side processing or serverParams
2388 if ( data && $.isArray(data) ) {
2389 var tmp = {};
2390 var rbracket = /(.*?)\[\]$/;
2391
2392 $.each( data, function (key, val) {
2393 var match = val.name.match(rbracket);
2394
2395 if ( match ) {
2396 // Support for arrays
2397 var name = match[0];
2398
2399 if ( ! tmp[ name ] ) {
2400 tmp[ name ] = [];
2401 }
2402 tmp[ name ].push( val.value );
2403 }
2404 else {
2405 tmp[val.name] = val.value;
2406 }
2407 } );
2408 data = tmp;
2409 }
2410
2411 var ajaxData;
2412 var ajax = oSettings.ajax;
2413 var instance = oSettings.oInstance;
2414
2415 if ( $.isPlainObject( ajax ) && ajax.data )
2416 {
2417 ajaxData = ajax.data;
2418
2419 var newData = $.isFunction( ajaxData ) ?
2420 ajaxData( data ) : // fn can manipulate data or return an object
2421 ajaxData; // object or array to merge
2422
2423 // If the function returned an object, use that alone
2424 data = $.isFunction( ajaxData ) && newData ?
2425 newData :
2426 $.extend( true, data, newData );
2427
2428 // Remove the data property as we've resolved it already and don't want
2429 // jQuery to do it again (it is restored at the end of the function)
2430 delete ajax.data;
2431 }
2432
2433 var baseAjax = {
2434 "data": data,
2435 "success": function (json) {
2436 var error = json.error || json.sError;
2437 if ( error ) {
2438 oSettings.oApi._fnLog( oSettings, 0, error );
2439 }
2440
2441 oSettings.json = json;
2442 _fnCallbackFire( oSettings, null, 'xhr', [oSettings, json] );
2443 fn( json );
2444 },
2445 "dataType": "json",
2446 "cache": false,
2447 "type": oSettings.sServerMethod,
2448 "error": function (xhr, error, thrown) {
2449 var log = oSettings.oApi._fnLog;
2450
2451 if ( error == "parsererror" ) {
2452 log( oSettings, 0, 'Invalid JSON response', 1 );
2453 }
2454 else if ( xhr.readyState === 4 ) {
2455 log( oSettings, 0, 'Ajax error', 7 );
2456 }
2457
2458 _fnProcessingDisplay( oSettings, false );
2459 }
2460 };
2461
2462 // Store the data submitted for the API
2463 oSettings.oAjaxData = data;
2464
2465 // Allow plug-ins and external processes to modify the data
2466 _fnCallbackFire( oSettings, null, 'preXhr', [oSettings, data] );
2467
2468 if ( oSettings.fnServerData )
2469 {
2470 // DataTables 1.9- compatibility
2471 oSettings.fnServerData.call( instance,
2472 oSettings.sAjaxSource,
2473 $.map( data, function (val, key) { // Need to convert back to 1.9 trad format
2474 return { name: key, value: val };
2475 } ),
2476 fn,
2477 oSettings
2478 );
2479 }
2480 else if ( oSettings.sAjaxSource || typeof ajax === 'string' )
2481 {
2482 // DataTables 1.9- compatibility
2483 oSettings.jqXHR = $.ajax( $.extend( baseAjax, {
2484 url: ajax || oSettings.sAjaxSource
2485 } ) );
2486 }
2487 else if ( $.isFunction( ajax ) )
2488 {
2489 // Is a function - let the caller define what needs to be done
2490 oSettings.jqXHR = ajax.call( instance, data, fn, oSettings );
2491 }
2492 else
2493 {
2494 // Object to extend the base settings
2495 oSettings.jqXHR = $.ajax( $.extend( baseAjax, ajax ) );
2496
2497 // Restore for next time around
2498 ajax.data = ajaxData;
2499 }
2500 }
2501
2502
2503 /**
2504 * Update the table using an Ajax call
2505 * @param {object} settings dataTables settings object
2506 * @returns {boolean} Block the table drawing or not
2507 * @memberof DataTable#oApi
2508 */
2509 function _fnAjaxUpdate( settings )
2510 {
2511 if ( settings.bAjaxDataGet ) {
2512 settings.iDraw++;
2513 _fnProcessingDisplay( settings, true );
2514
2515 _fnBuildAjax(
2516 settings,
2517 _fnAjaxParameters( settings ),
2518 function(json) {
2519 _fnAjaxUpdateDraw( settings, json );
2520 }
2521 );
2522
2523 return false;
2524 }
2525 return true;
2526 }
2527
2528
2529 /**
2530 * Build up the parameters in an object needed for a server-side processing
2531 * request. Note that this is basically done twice, is different ways - a modern
2532 * method which is used by default in DataTables 1.10 which uses objects and
2533 * arrays, or the 1.9- method with is name / value pairs. 1.9 method is used if
2534 * the sAjaxSource option is used in the initialisation, or the legacyAjax
2535 * option is set.
2536 * @param {object} oSettings dataTables settings object
2537 * @returns {bool} block the table drawing or not
2538 * @memberof DataTable#oApi
2539 */
2540 function _fnAjaxParameters( settings )
2541 {
2542 var
2543 columns = settings.aoColumns,
2544 columnCount = columns.length,
2545 features = settings.oFeatures,
2546 preSearch = settings.oPreviousSearch,
2547 preColSearch = settings.aoPreSearchCols,
2548 i, data = [], dataProp, column, columnSearch,
2549 sort = _fnSortFlatten( settings ),
2550 displayStart = settings._iDisplayStart,
2551 displayLength = features.bPaginate !== false ?
2552 settings._iDisplayLength :
2553 -1;
2554
2555 var param = function ( name, value ) {
2556 data.push( { 'name': name, 'value': value } );
2557 };
2558
2559 // DataTables 1.9- compatible method
2560 param( 'sEcho', settings.iDraw );
2561 param( 'iColumns', columnCount );
2562 param( 'sColumns', _pluck( columns, 'sName' ).join(',') );
2563 param( 'iDisplayStart', displayStart );
2564 param( 'iDisplayLength', displayLength );
2565
2566 // DataTables 1.10+ method
2567 var d = {
2568 draw: settings.iDraw,
2569 columns: [],
2570 order: [],
2571 start: displayStart,
2572 length: displayLength,
2573 search: {
2574 value: preSearch.sSearch,
2575 regex: preSearch.bRegex
2576 }
2577 };
2578
2579 for ( i=0 ; i<columnCount ; i++ ) {
2580 column = columns[i];
2581 columnSearch = preColSearch[i];
2582 dataProp = typeof column.mData=="function" ? 'function' : column.mData ;
2583
2584 d.columns.push( {
2585 data: dataProp,
2586 name: column.sName,
2587 searchable: column.bSearchable,
2588 orderable: column.bSortable,
2589 search: {
2590 value: columnSearch.sSearch,
2591 regex: columnSearch.bRegex
2592 }
2593 } );
2594
2595 param( "mDataProp_"+i, dataProp );
2596
2597 if ( features.bFilter ) {
2598 param( 'sSearch_'+i, columnSearch.sSearch );
2599 param( 'bRegex_'+i, columnSearch.bRegex );
2600 param( 'bSearchable_'+i, column.bSearchable );
2601 }
2602
2603 if ( features.bSort ) {
2604 param( 'bSortable_'+i, column.bSortable );
2605 }
2606 }
2607
2608 if ( features.bFilter ) {
2609 param( 'sSearch', preSearch.sSearch );
2610 param( 'bRegex', preSearch.bRegex );
2611 }
2612
2613 if ( features.bSort ) {
2614 $.each( sort, function ( i, val ) {
2615 d.order.push( { column: val.col, dir: val.dir } );
2616
2617 param( 'iSortCol_'+i, val.col );
2618 param( 'sSortDir_'+i, val.dir );
2619 } );
2620
2621 param( 'iSortingCols', sort.length );
2622 }
2623
2624 // If the legacy.ajax parameter is null, then we automatically decide which
2625 // form to use, based on sAjaxSource
2626 var legacy = DataTable.ext.legacy.ajax;
2627 if ( legacy === null ) {
2628 return settings.sAjaxSource ? data : d;
2629 }
2630
2631 // Otherwise, if legacy has been specified then we use that to decide on the
2632 // form
2633 return legacy ? data : d;
2634 }
2635
2636
2637 /**
2638 * Data the data from the server (nuking the old) and redraw the table
2639 * @param {object} oSettings dataTables settings object
2640 * @param {object} json json data return from the server.
2641 * @param {string} json.sEcho Tracking flag for DataTables to match requests
2642 * @param {int} json.iTotalRecords Number of records in the data set, not accounting for filtering
2643 * @param {int} json.iTotalDisplayRecords Number of records in the data set, accounting for filtering
2644 * @param {array} json.aaData The data to display on this page
2645 * @param {string} [json.sColumns] Column ordering (sName, comma separated)
2646 * @memberof DataTable#oApi
2647 */
2648 function _fnAjaxUpdateDraw ( settings, json )
2649 {
2650 // v1.10 uses camelCase variables, while 1.9 uses Hungarian notation.
2651 // Support both
2652 var compat = function ( old, modern ) {
2653 return json[old] !== undefined ? json[old] : json[modern];
2654 };
2655
2656 var draw = compat( 'sEcho', 'draw' );
2657 var recordsTotal = compat( 'iTotalRecords', 'recordsTotal' );
2658 var recordsFiltered = compat( 'iTotalDisplayRecords', 'recordsFiltered' );
2659
2660 if ( draw ) {
2661 // Protect against out of sequence returns
2662 if ( draw*1 < settings.iDraw ) {
2663 return;
2664 }
2665 settings.iDraw = draw * 1;
2666 }
2667
2668 _fnClearTable( settings );
2669 settings._iRecordsTotal = parseInt(recordsTotal, 10);
2670 settings._iRecordsDisplay = parseInt(recordsFiltered, 10);
2671
2672 var data = _fnAjaxDataSrc( settings, json );
2673 for ( var i=0, ien=data.length ; i<ien ; i++ ) {
2674 _fnAddData( settings, data[i] );
2675 }
2676 settings.aiDisplay = settings.aiDisplayMaster.slice();
2677
2678 settings.bAjaxDataGet = false;
2679 _fnDraw( settings );
2680
2681 if ( ! settings._bInitComplete ) {
2682 _fnInitComplete( settings, json );
2683 }
2684
2685 settings.bAjaxDataGet = true;
2686 _fnProcessingDisplay( settings, false );
2687 }
2688
2689
2690 /**
2691 * Get the data from the JSON data source to use for drawing a table. Using
2692 * `_fnGetObjectDataFn` allows the data to be sourced from a property of the
2693 * source object, or from a processing function.
2694 * @param {object} oSettings dataTables settings object
2695 * @param {object} json Data source object / array from the server
2696 * @return {array} Array of data to use
2697 */
2698 function _fnAjaxDataSrc ( oSettings, json )
2699 {
2700 var dataSrc = $.isPlainObject( oSettings.ajax ) && oSettings.ajax.dataSrc !== undefined ?
2701 oSettings.ajax.dataSrc :
2702 oSettings.sAjaxDataProp; // Compatibility with 1.9-.
2703
2704 // Compatibility with 1.9-. In order to read from aaData, check if the
2705 // default has been changed, if not, check for aaData
2706 if ( dataSrc === 'data' ) {
2707 return json.aaData || json[dataSrc];
2708 }
2709
2710 return dataSrc !== "" ?
2711 _fnGetObjectDataFn( dataSrc )( json ) :
2712 json;
2713 }
2714
2715
2716 /**
2717 * Generate the node required for filtering text
2718 * @returns {node} Filter control element
2719 * @param {object} oSettings dataTables settings object
2720 * @memberof DataTable#oApi
2721 */
2722 function _fnFeatureHtmlFilter ( settings )
2723 {
2724 var classes = settings.oClasses;
2725 var tableId = settings.sTableId;
2726 var language = settings.oLanguage;
2727 var previousSearch = settings.oPreviousSearch;
2728 var features = settings.aanFeatures;
2729 var input = '<input type="search" class="'+classes.sFilterInput+'"/>';
2730
2731 var str = language.sSearch;
2732 str = str.match(/_INPUT_/) ?
2733 str.replace('_INPUT_', input) :
2734 str+input;
2735
2736 var filter = $('<div/>', {
2737 'id': ! features.f ? tableId+'_filter' : null,
2738 'class': classes.sFilter
2739 } )
2740 .append( $('<label/>' ).append( str ) );
2741
2742 var searchFn = function() {
2743 /* Update all other filter input elements for the new display */
2744 var n = features.f;
2745 var val = !this.value ? "" : this.value; // mental IE8 fix :-(
2746
2747 /* Now do the filter */
2748 if ( val != previousSearch.sSearch ) {
2749 _fnFilterComplete( settings, {
2750 "sSearch": val,
2751 "bRegex": previousSearch.bRegex,
2752 "bSmart": previousSearch.bSmart ,
2753 "bCaseInsensitive": previousSearch.bCaseInsensitive
2754 } );
2755
2756 // Need to redraw, without resorting
2757 settings._iDisplayStart = 0;
2758 _fnDraw( settings );
2759 }
2760 };
2761
2762 var searchDelay = settings.searchDelay !== null ?
2763 settings.searchDelay :
2764 _fnDataSource( settings ) === 'ssp' ?
2765 400 :
2766 0;
2767
2768 var jqFilter = $('input', filter)
2769 .val( previousSearch.sSearch )
2770 .attr( 'placeholder', language.sSearchPlaceholder )
2771 .bind(
2772 'keyup.DT search.DT input.DT paste.DT cut.DT',
2773 searchDelay ?
2774 _fnThrottle( searchFn, searchDelay ) :
2775 searchFn
2776 )
2777 .bind( 'keypress.DT', function(e) {
2778 /* Prevent form submission */
2779 if ( e.keyCode == 13 ) {
2780 return false;
2781 }
2782 } )
2783 .attr('aria-controls', tableId);
2784
2785 // Update the input elements whenever the table is filtered
2786 $(settings.nTable).on( 'search.dt.DT', function ( ev, s ) {
2787 if ( settings === s ) {
2788 // IE9 throws an 'unknown error' if document.activeElement is used
2789 // inside an iframe or frame...
2790 try {
2791 if ( jqFilter[0] !== document.activeElement ) {
2792 jqFilter.val( previousSearch.sSearch );
2793 }
2794 }
2795 catch ( e ) {}
2796 }
2797 } );
2798
2799 return filter[0];
2800 }
2801
2802
2803 /**
2804 * Filter the table using both the global filter and column based filtering
2805 * @param {object} oSettings dataTables settings object
2806 * @param {object} oSearch search information
2807 * @param {int} [iForce] force a research of the master array (1) or not (undefined or 0)
2808 * @memberof DataTable#oApi
2809 */
2810 function _fnFilterComplete ( oSettings, oInput, iForce )
2811 {
2812 var oPrevSearch = oSettings.oPreviousSearch;
2813 var aoPrevSearch = oSettings.aoPreSearchCols;
2814 var fnSaveFilter = function ( oFilter ) {
2815 /* Save the filtering values */
2816 oPrevSearch.sSearch = oFilter.sSearch;
2817 oPrevSearch.bRegex = oFilter.bRegex;
2818 oPrevSearch.bSmart = oFilter.bSmart;
2819 oPrevSearch.bCaseInsensitive = oFilter.bCaseInsensitive;
2820 };
2821 var fnRegex = function ( o ) {
2822 // Backwards compatibility with the bEscapeRegex option
2823 return o.bEscapeRegex !== undefined ? !o.bEscapeRegex : o.bRegex;
2824 };
2825
2826 // Resolve any column types that are unknown due to addition or invalidation
2827 // @todo As per sort - can this be moved into an event handler?
2828 _fnColumnTypes( oSettings );
2829
2830 /* In server-side processing all filtering is done by the server, so no point hanging around here */
2831 if ( _fnDataSource( oSettings ) != 'ssp' )
2832 {
2833 /* Global filter */
2834 _fnFilter( oSettings, oInput.sSearch, iForce, fnRegex(oInput), oInput.bSmart, oInput.bCaseInsensitive );
2835 fnSaveFilter( oInput );
2836
2837 /* Now do the individual column filter */
2838 for ( var i=0 ; i<aoPrevSearch.length ; i++ )
2839 {
2840 _fnFilterColumn( oSettings, aoPrevSearch[i].sSearch, i, fnRegex(aoPrevSearch[i]),
2841 aoPrevSearch[i].bSmart, aoPrevSearch[i].bCaseInsensitive );
2842 }
2843
2844 /* Custom filtering */
2845 _fnFilterCustom( oSettings );
2846 }
2847 else
2848 {
2849 fnSaveFilter( oInput );
2850 }
2851
2852 /* Tell the draw function we have been filtering */
2853 oSettings.bFiltered = true;
2854 _fnCallbackFire( oSettings, null, 'search', [oSettings] );
2855 }
2856
2857
2858 /**
2859 * Apply custom filtering functions
2860 * @param {object} oSettings dataTables settings object
2861 * @memberof DataTable#oApi
2862 */
2863 function _fnFilterCustom( settings )
2864 {
2865 var filters = DataTable.ext.search;
2866 var displayRows = settings.aiDisplay;
2867 var row, rowIdx;
2868
2869 for ( var i=0, ien=filters.length ; i<ien ; i++ ) {
2870 var rows = [];
2871
2872 // Loop over each row and see if it should be included
2873 for ( var j=0, jen=displayRows.length ; j<jen ; j++ ) {
2874 rowIdx = displayRows[ j ];
2875 row = settings.aoData[ rowIdx ];
2876
2877 if ( filters[i]( settings, row._aFilterData, rowIdx, row._aData, j ) ) {
2878 rows.push( rowIdx );
2879 }
2880 }
2881
2882 // So the array reference doesn't break set the results into the
2883 // existing array
2884 displayRows.length = 0;
2885 displayRows.push.apply( displayRows, rows );
2886 }
2887 }
2888
2889
2890 /**
2891 * Filter the table on a per-column basis
2892 * @param {object} oSettings dataTables settings object
2893 * @param {string} sInput string to filter on
2894 * @param {int} iColumn column to filter
2895 * @param {bool} bRegex treat search string as a regular expression or not
2896 * @param {bool} bSmart use smart filtering or not
2897 * @param {bool} bCaseInsensitive Do case insenstive matching or not
2898 * @memberof DataTable#oApi
2899 */
2900 function _fnFilterColumn ( settings, searchStr, colIdx, regex, smart, caseInsensitive )
2901 {
2902 if ( searchStr === '' ) {
2903 return;
2904 }
2905
2906 var data;
2907 var display = settings.aiDisplay;
2908 var rpSearch = _fnFilterCreateSearch( searchStr, regex, smart, caseInsensitive );
2909
2910 for ( var i=display.length-1 ; i>=0 ; i-- ) {
2911 data = settings.aoData[ display[i] ]._aFilterData[ colIdx ];
2912
2913 if ( ! rpSearch.test( data ) ) {
2914 display.splice( i, 1 );
2915 }
2916 }
2917 }
2918
2919
2920 /**
2921 * Filter the data table based on user input and draw the table
2922 * @param {object} settings dataTables settings object
2923 * @param {string} input string to filter on
2924 * @param {int} force optional - force a research of the master array (1) or not (undefined or 0)
2925 * @param {bool} regex treat as a regular expression or not
2926 * @param {bool} smart perform smart filtering or not
2927 * @param {bool} caseInsensitive Do case insenstive matching or not
2928 * @memberof DataTable#oApi
2929 */
2930 function _fnFilter( settings, input, force, regex, smart, caseInsensitive )
2931 {
2932 var rpSearch = _fnFilterCreateSearch( input, regex, smart, caseInsensitive );
2933 var prevSearch = settings.oPreviousSearch.sSearch;
2934 var displayMaster = settings.aiDisplayMaster;
2935 var display, invalidated, i;
2936
2937 // Need to take account of custom filtering functions - always filter
2938 if ( DataTable.ext.search.length !== 0 ) {
2939 force = true;
2940 }
2941
2942 // Check if any of the rows were invalidated
2943 invalidated = _fnFilterData( settings );
2944
2945 // If the input is blank - we just want the full data set
2946 if ( input.length <= 0 ) {
2947 settings.aiDisplay = displayMaster.slice();
2948 }
2949 else {
2950 // New search - start from the master array
2951 if ( invalidated ||
2952 force ||
2953 prevSearch.length > input.length ||
2954 input.indexOf(prevSearch) !== 0 ||
2955 settings.bSorted // On resort, the display master needs to be
2956 // re-filtered since indexes will have changed
2957 ) {
2958 settings.aiDisplay = displayMaster.slice();
2959 }
2960
2961 // Search the display array
2962 display = settings.aiDisplay;
2963
2964 for ( i=display.length-1 ; i>=0 ; i-- ) {
2965 if ( ! rpSearch.test( settings.aoData[ display[i] ]._sFilterRow ) ) {
2966 display.splice( i, 1 );
2967 }
2968 }
2969 }
2970 }
2971
2972
2973 /**
2974 * Build a regular expression object suitable for searching a table
2975 * @param {string} sSearch string to search for
2976 * @param {bool} bRegex treat as a regular expression or not
2977 * @param {bool} bSmart perform smart filtering or not
2978 * @param {bool} bCaseInsensitive Do case insensitive matching or not
2979 * @returns {RegExp} constructed object
2980 * @memberof DataTable#oApi
2981 */
2982 function _fnFilterCreateSearch( search, regex, smart, caseInsensitive )
2983 {
2984 search = regex ?
2985 search :
2986 _fnEscapeRegex( search );
2987
2988 if ( smart ) {
2989 /* For smart filtering we want to allow the search to work regardless of
2990 * word order. We also want double quoted text to be preserved, so word
2991 * order is important - a la google. So this is what we want to
2992 * generate:
2993 *
2994 * ^(?=.*?\bone\b)(?=.*?\btwo three\b)(?=.*?\bfour\b).*$
2995 */
2996 var a = $.map( search.match( /"[^"]+"|[^ ]+/g ) || '', function ( word ) {
2997 if ( word.charAt(0) === '"' ) {
2998 var m = word.match( /^"(.*)"$/ );
2999 word = m ? m[1] : word;
3000 }
3001
3002 return word.replace('"', '');
3003 } );
3004
3005 search = '^(?=.*?'+a.join( ')(?=.*?' )+').*$';
3006 }
3007
3008 return new RegExp( search, caseInsensitive ? 'i' : '' );
3009 }
3010
3011
3012 /**
3013 * Escape a string such that it can be used in a regular expression
3014 * @param {string} sVal string to escape
3015 * @returns {string} escaped string
3016 * @memberof DataTable#oApi
3017 */
3018 function _fnEscapeRegex ( sVal )
3019 {
3020 return sVal.replace( _re_escape_regex, '\\$1' );
3021 }
3022
3023
3024
3025 var __filter_div = $('<div>')[0];
3026 var __filter_div_textContent = __filter_div.textContent !== undefined;
3027
3028 // Update the filtering data for each row if needed (by invalidation or first run)
3029 function _fnFilterData ( settings )
3030 {
3031 var columns = settings.aoColumns;
3032 var column;
3033 var i, j, ien, jen, filterData, cellData, row;
3034 var fomatters = DataTable.ext.type.search;
3035 var wasInvalidated = false;
3036
3037 for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
3038 row = settings.aoData[i];
3039
3040 if ( ! row._aFilterData ) {
3041 filterData = [];
3042
3043 for ( j=0, jen=columns.length ; j<jen ; j++ ) {
3044 column = columns[j];
3045
3046 if ( column.bSearchable ) {
3047 cellData = _fnGetCellData( settings, i, j, 'filter' );
3048
3049 if ( fomatters[ column.sType ] ) {
3050 cellData = fomatters[ column.sType ]( cellData );
3051 }
3052
3053 // Search in DataTables 1.10 is string based. In 1.11 this
3054 // should be altered to also allow strict type checking.
3055 if ( cellData === null ) {
3056 cellData = '';
3057 }
3058
3059 if ( typeof cellData !== 'string' && cellData.toString ) {
3060 cellData = cellData.toString();
3061 }
3062 }
3063 else {
3064 cellData = '';
3065 }
3066
3067 // If it looks like there is an HTML entity in the string,
3068 // attempt to decode it so sorting works as expected. Note that
3069 // we could use a single line of jQuery to do this, but the DOM
3070 // method used here is much faster http://jsperf.com/html-decode
3071 if ( cellData.indexOf && cellData.indexOf('&') !== -1 ) {
3072 __filter_div.innerHTML = cellData;
3073 cellData = __filter_div_textContent ?
3074 __filter_div.textContent :
3075 __filter_div.innerText;
3076 }
3077
3078 if ( cellData.replace ) {
3079 cellData = cellData.replace(/[\r\n]/g, '');
3080 }
3081
3082 filterData.push( cellData );
3083 }
3084
3085 row._aFilterData = filterData;
3086 row._sFilterRow = filterData.join(' ');
3087 wasInvalidated = true;
3088 }
3089 }
3090
3091 return wasInvalidated;
3092 }
3093
3094
3095 /**
3096 * Convert from the internal Hungarian notation to camelCase for external
3097 * interaction
3098 * @param {object} obj Object to convert
3099 * @returns {object} Inverted object
3100 * @memberof DataTable#oApi
3101 */
3102 function _fnSearchToCamel ( obj )
3103 {
3104 return {
3105 search: obj.sSearch,
3106 smart: obj.bSmart,
3107 regex: obj.bRegex,
3108 caseInsensitive: obj.bCaseInsensitive
3109 };
3110 }
3111
3112
3113
3114 /**
3115 * Convert from camelCase notation to the internal Hungarian. We could use the
3116 * Hungarian convert function here, but this is cleaner
3117 * @param {object} obj Object to convert
3118 * @returns {object} Inverted object
3119 * @memberof DataTable#oApi
3120 */
3121 function _fnSearchToHung ( obj )
3122 {
3123 return {
3124 sSearch: obj.search,
3125 bSmart: obj.smart,
3126 bRegex: obj.regex,
3127 bCaseInsensitive: obj.caseInsensitive
3128 };
3129 }
3130
3131 /**
3132 * Generate the node required for the info display
3133 * @param {object} oSettings dataTables settings object
3134 * @returns {node} Information element
3135 * @memberof DataTable#oApi
3136 */
3137 function _fnFeatureHtmlInfo ( settings )
3138 {
3139 var
3140 tid = settings.sTableId,
3141 nodes = settings.aanFeatures.i,
3142 n = $('<div/>', {
3143 'class': settings.oClasses.sInfo,
3144 'id': ! nodes ? tid+'_info' : null
3145 } );
3146
3147 if ( ! nodes ) {
3148 // Update display on each draw
3149 settings.aoDrawCallback.push( {
3150 "fn": _fnUpdateInfo,
3151 "sName": "information"
3152 } );
3153
3154 n
3155 .attr( 'role', 'status' )
3156 .attr( 'aria-live', 'polite' );
3157
3158 // Table is described by our info div
3159 $(settings.nTable).attr( 'aria-describedby', tid+'_info' );
3160 }
3161
3162 return n[0];
3163 }
3164
3165
3166 /**
3167 * Update the information elements in the display
3168 * @param {object} settings dataTables settings object
3169 * @memberof DataTable#oApi
3170 */
3171 function _fnUpdateInfo ( settings )
3172 {
3173 /* Show information about the table */
3174 var nodes = settings.aanFeatures.i;
3175 if ( nodes.length === 0 ) {
3176 return;
3177 }
3178
3179 var
3180 lang = settings.oLanguage,
3181 start = settings._iDisplayStart+1,
3182 end = settings.fnDisplayEnd(),
3183 max = settings.fnRecordsTotal(),
3184 total = settings.fnRecordsDisplay(),
3185 out = total ?
3186 lang.sInfo :
3187 lang.sInfoEmpty;
3188
3189 if ( total !== max ) {
3190 /* Record set after filtering */
3191 out += ' ' + lang.sInfoFiltered;
3192 }
3193
3194 // Convert the macros
3195 out += lang.sInfoPostFix;
3196 out = _fnInfoMacros( settings, out );
3197
3198 var callback = lang.fnInfoCallback;
3199 if ( callback !== null ) {
3200 out = callback.call( settings.oInstance,
3201 settings, start, end, max, total, out
3202 );
3203 }
3204
3205 $(nodes).html( out );
3206 }
3207
3208
3209 function _fnInfoMacros ( settings, str )
3210 {
3211 // When infinite scrolling, we are always starting at 1. _iDisplayStart is used only
3212 // internally
3213 var
3214 formatter = settings.fnFormatNumber,
3215 start = settings._iDisplayStart+1,
3216 len = settings._iDisplayLength,
3217 vis = settings.fnRecordsDisplay(),
3218 all = len === -1;
3219
3220 return str.
3221 replace(/_START_/g, formatter.call( settings, start ) ).
3222 replace(/_END_/g, formatter.call( settings, settings.fnDisplayEnd() ) ).
3223 replace(/_MAX_/g, formatter.call( settings, settings.fnRecordsTotal() ) ).
3224 replace(/_TOTAL_/g, formatter.call( settings, vis ) ).
3225 replace(/_PAGE_/g, formatter.call( settings, all ? 1 : Math.ceil( start / len ) ) ).
3226 replace(/_PAGES_/g, formatter.call( settings, all ? 1 : Math.ceil( vis / len ) ) );
3227 }
3228
3229
3230
3231 /**
3232 * Draw the table for the first time, adding all required features
3233 * @param {object} settings dataTables settings object
3234 * @memberof DataTable#oApi
3235 */
3236 function _fnInitialise ( settings )
3237 {
3238 var i, iLen, iAjaxStart=settings.iInitDisplayStart;
3239 var columns = settings.aoColumns, column;
3240 var features = settings.oFeatures;
3241
3242 /* Ensure that the table data is fully initialised */
3243 if ( ! settings.bInitialised ) {
3244 setTimeout( function(){ _fnInitialise( settings ); }, 200 );
3245 return;
3246 }
3247
3248 /* Show the display HTML options */
3249 _fnAddOptionsHtml( settings );
3250
3251 /* Build and draw the header / footer for the table */
3252 _fnBuildHead( settings );
3253 _fnDrawHead( settings, settings.aoHeader );
3254 _fnDrawHead( settings, settings.aoFooter );
3255
3256 /* Okay to show that something is going on now */
3257 _fnProcessingDisplay( settings, true );
3258
3259 /* Calculate sizes for columns */
3260 if ( features.bAutoWidth ) {
3261 _fnCalculateColumnWidths( settings );
3262 }
3263
3264 for ( i=0, iLen=columns.length ; i<iLen ; i++ ) {
3265 column = columns[i];
3266
3267 if ( column.sWidth ) {
3268 column.nTh.style.width = _fnStringToCss( column.sWidth );
3269 }
3270 }
3271
3272 // If there is default sorting required - let's do it. The sort function
3273 // will do the drawing for us. Otherwise we draw the table regardless of the
3274 // Ajax source - this allows the table to look initialised for Ajax sourcing
3275 // data (show 'loading' message possibly)
3276 _fnReDraw( settings );
3277
3278 // Server-side processing init complete is done by _fnAjaxUpdateDraw
3279 var dataSrc = _fnDataSource( settings );
3280 if ( dataSrc != 'ssp' ) {
3281 // if there is an ajax source load the data
3282 if ( dataSrc == 'ajax' ) {
3283 _fnBuildAjax( settings, [], function(json) {
3284 var aData = _fnAjaxDataSrc( settings, json );
3285
3286 // Got the data - add it to the table
3287 for ( i=0 ; i<aData.length ; i++ ) {
3288 _fnAddData( settings, aData[i] );
3289 }
3290
3291 // Reset the init display for cookie saving. We've already done
3292 // a filter, and therefore cleared it before. So we need to make
3293 // it appear 'fresh'
3294 settings.iInitDisplayStart = iAjaxStart;
3295
3296 _fnReDraw( settings );
3297
3298 _fnProcessingDisplay( settings, false );
3299 _fnInitComplete( settings, json );
3300 }, settings );
3301 }
3302 else {
3303 _fnProcessingDisplay( settings, false );
3304 _fnInitComplete( settings );
3305 }
3306 }
3307 }
3308
3309
3310 /**
3311 * Draw the table for the first time, adding all required features
3312 * @param {object} oSettings dataTables settings object
3313 * @param {object} [json] JSON from the server that completed the table, if using Ajax source
3314 * with client-side processing (optional)
3315 * @memberof DataTable#oApi
3316 */
3317 function _fnInitComplete ( settings, json )
3318 {
3319 settings._bInitComplete = true;
3320
3321 // On an Ajax load we now have data and therefore want to apply the column
3322 // sizing
3323 if ( json ) {
3324 _fnAdjustColumnSizing( settings );
3325 }
3326
3327 _fnCallbackFire( settings, 'aoInitComplete', 'init', [settings, json] );
3328 }
3329
3330
3331 function _fnLengthChange ( settings, val )
3332 {
3333 var len = parseInt( val, 10 );
3334 settings._iDisplayLength = len;
3335
3336 _fnLengthOverflow( settings );
3337
3338 // Fire length change event
3339 _fnCallbackFire( settings, null, 'length', [settings, len] );
3340 }
3341
3342
3343 /**
3344 * Generate the node required for user display length changing
3345 * @param {object} settings dataTables settings object
3346 * @returns {node} Display length feature node
3347 * @memberof DataTable#oApi
3348 */
3349 function _fnFeatureHtmlLength ( settings )
3350 {
3351 var
3352 classes = settings.oClasses,
3353 tableId = settings.sTableId,
3354 menu = settings.aLengthMenu,
3355 d2 = $.isArray( menu[0] ),
3356 lengths = d2 ? menu[0] : menu,
3357 language = d2 ? menu[1] : menu;
3358
3359 var select = $('<select/>', {
3360 'name': tableId+'_length',
3361 'aria-controls': tableId,
3362 'class': classes.sLengthSelect
3363 } );
3364
3365 for ( var i=0, ien=lengths.length ; i<ien ; i++ ) {
3366 select[0][ i ] = new Option( language[i], lengths[i] );
3367 }
3368
3369 var div = $('<div><label/></div>').addClass( classes.sLength );
3370 if ( ! settings.aanFeatures.l ) {
3371 div[0].id = tableId+'_length';
3372 }
3373
3374 div.children().append(
3375 settings.oLanguage.sLengthMenu.replace( '_MENU_', select[0].outerHTML )
3376 );
3377
3378 // Can't use `select` variable as user might provide their own and the
3379 // reference is broken by the use of outerHTML
3380 $('select', div)
3381 .val( settings._iDisplayLength )
3382 .bind( 'change.DT', function(e) {
3383 _fnLengthChange( settings, $(this).val() );
3384 _fnDraw( settings );
3385 } );
3386
3387 // Update node value whenever anything changes the table's length
3388 $(settings.nTable).bind( 'length.dt.DT', function (e, s, len) {
3389 if ( settings === s ) {
3390 $('select', div).val( len );
3391 }
3392 } );
3393
3394 return div[0];
3395 }
3396
3397
3398
3399 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
3400 * Note that most of the paging logic is done in
3401 * DataTable.ext.pager
3402 */
3403
3404 /**
3405 * Generate the node required for default pagination
3406 * @param {object} oSettings dataTables settings object
3407 * @returns {node} Pagination feature node
3408 * @memberof DataTable#oApi
3409 */
3410 function _fnFeatureHtmlPaginate ( settings )
3411 {
3412 var
3413 type = settings.sPaginationType,
3414 plugin = DataTable.ext.pager[ type ],
3415 modern = typeof plugin === 'function',
3416 redraw = function( settings ) {
3417 _fnDraw( settings );
3418 },
3419 node = $('<div/>').addClass( settings.oClasses.sPaging + type )[0],
3420 features = settings.aanFeatures;
3421
3422 if ( ! modern ) {
3423 plugin.fnInit( settings, node, redraw );
3424 }
3425
3426 /* Add a draw callback for the pagination on first instance, to update the paging display */
3427 if ( ! features.p )
3428 {
3429 node.id = settings.sTableId+'_paginate';
3430
3431 settings.aoDrawCallback.push( {
3432 "fn": function( settings ) {
3433 if ( modern ) {
3434 var
3435 start = settings._iDisplayStart,
3436 len = settings._iDisplayLength,
3437 visRecords = settings.fnRecordsDisplay(),
3438 all = len === -1,
3439 page = all ? 0 : Math.ceil( start / len ),
3440 pages = all ? 1 : Math.ceil( visRecords / len ),
3441 buttons = plugin(page, pages),
3442 i, ien;
3443
3444 for ( i=0, ien=features.p.length ; i<ien ; i++ ) {
3445 _fnRenderer( settings, 'pageButton' )(
3446 settings, features.p[i], i, buttons, page, pages
3447 );
3448 }
3449 }
3450 else {
3451 plugin.fnUpdate( settings, redraw );
3452 }
3453 },
3454 "sName": "pagination"
3455 } );
3456 }
3457
3458 return node;
3459 }
3460
3461
3462 /**
3463 * Alter the display settings to change the page
3464 * @param {object} settings DataTables settings object
3465 * @param {string|int} action Paging action to take: "first", "previous",
3466 * "next" or "last" or page number to jump to (integer)
3467 * @param [bool] redraw Automatically draw the update or not
3468 * @returns {bool} true page has changed, false - no change
3469 * @memberof DataTable#oApi
3470 */
3471 function _fnPageChange ( settings, action, redraw )
3472 {
3473 var
3474 start = settings._iDisplayStart,
3475 len = settings._iDisplayLength,
3476 records = settings.fnRecordsDisplay();
3477
3478 if ( records === 0 || len === -1 )
3479 {
3480 start = 0;
3481 }
3482 else if ( typeof action === "number" )
3483 {
3484 start = action * len;
3485
3486 if ( start > records )
3487 {
3488 start = 0;
3489 }
3490 }
3491 else if ( action == "first" )
3492 {
3493 start = 0;
3494 }
3495 else if ( action == "previous" )
3496 {
3497 start = len >= 0 ?
3498 start - len :
3499 0;
3500
3501 if ( start < 0 )
3502 {
3503 start = 0;
3504 }
3505 }
3506 else if ( action == "next" )
3507 {
3508 if ( start + len < records )
3509 {
3510 start += len;
3511 }
3512 }
3513 else if ( action == "last" )
3514 {
3515 start = Math.floor( (records-1) / len) * len;
3516 }
3517 else
3518 {
3519 _fnLog( settings, 0, "Unknown paging action: "+action, 5 );
3520 }
3521
3522 var changed = settings._iDisplayStart !== start;
3523 settings._iDisplayStart = start;
3524
3525 if ( changed ) {
3526 _fnCallbackFire( settings, null, 'page', [settings] );
3527
3528 if ( redraw ) {
3529 _fnDraw( settings );
3530 }
3531 }
3532
3533 return changed;
3534 }
3535
3536
3537
3538 /**
3539 * Generate the node required for the processing node
3540 * @param {object} settings dataTables settings object
3541 * @returns {node} Processing element
3542 * @memberof DataTable#oApi
3543 */
3544 function _fnFeatureHtmlProcessing ( settings )
3545 {
3546 return $('<div/>', {
3547 'id': ! settings.aanFeatures.r ? settings.sTableId+'_processing' : null,
3548 'class': settings.oClasses.sProcessing
3549 } )
3550 .html( settings.oLanguage.sProcessing )
3551 .insertBefore( settings.nTable )[0];
3552 }
3553
3554
3555 /**
3556 * Display or hide the processing indicator
3557 * @param {object} settings dataTables settings object
3558 * @param {bool} show Show the processing indicator (true) or not (false)
3559 * @memberof DataTable#oApi
3560 */
3561 function _fnProcessingDisplay ( settings, show )
3562 {
3563 if ( settings.oFeatures.bProcessing ) {
3564 $(settings.aanFeatures.r).css( 'display', show ? 'block' : 'none' );
3565 }
3566
3567 _fnCallbackFire( settings, null, 'processing', [settings, show] );
3568 }
3569
3570 /**
3571 * Add any control elements for the table - specifically scrolling
3572 * @param {object} settings dataTables settings object
3573 * @returns {node} Node to add to the DOM
3574 * @memberof DataTable#oApi
3575 */
3576 function _fnFeatureHtmlTable ( settings )
3577 {
3578 var table = $(settings.nTable);
3579
3580 // Add the ARIA grid role to the table
3581 table.attr( 'role', 'grid' );
3582
3583 // Scrolling from here on in
3584 var scroll = settings.oScroll;
3585
3586 if ( scroll.sX === '' && scroll.sY === '' ) {
3587 return settings.nTable;
3588 }
3589
3590 var scrollX = scroll.sX;
3591 var scrollY = scroll.sY;
3592 var classes = settings.oClasses;
3593 var caption = table.children('caption');
3594 var captionSide = caption.length ? caption[0]._captionSide : null;
3595 var headerClone = $( table[0].cloneNode(false) );
3596 var footerClone = $( table[0].cloneNode(false) );
3597 var footer = table.children('tfoot');
3598 var _div = '<div/>';
3599 var size = function ( s ) {
3600 return !s ? null : _fnStringToCss( s );
3601 };
3602
3603 // This is fairly messy, but with x scrolling enabled, if the table has a
3604 // width attribute, regardless of any width applied using the column width
3605 // options, the browser will shrink or grow the table as needed to fit into
3606 // that 100%. That would make the width options useless. So we remove it.
3607 // This is okay, under the assumption that width:100% is applied to the
3608 // table in CSS (it is in the default stylesheet) which will set the table
3609 // width as appropriate (the attribute and css behave differently...)
3610 if ( scroll.sX && table.attr('width') === '100%' ) {
3611 table.removeAttr('width');
3612 }
3613
3614 if ( ! footer.length ) {
3615 footer = null;
3616 }
3617
3618 /*
3619 * The HTML structure that we want to generate in this function is:
3620 * div - scroller
3621 * div - scroll head
3622 * div - scroll head inner
3623 * table - scroll head table
3624 * thead - thead
3625 * div - scroll body
3626 * table - table (master table)
3627 * thead - thead clone for sizing
3628 * tbody - tbody
3629 * div - scroll foot
3630 * div - scroll foot inner
3631 * table - scroll foot table
3632 * tfoot - tfoot
3633 */
3634 var scroller = $( _div, { 'class': classes.sScrollWrapper } )
3635 .append(
3636 $(_div, { 'class': classes.sScrollHead } )
3637 .css( {
3638 overflow: 'hidden',
3639 position: 'relative',
3640 border: 0,
3641 width: scrollX ? size(scrollX) : '100%'
3642 } )
3643 .append(
3644 $(_div, { 'class': classes.sScrollHeadInner } )
3645 .css( {
3646 'box-sizing': 'content-box',
3647 width: scroll.sXInner || '100%'
3648 } )
3649 .append(
3650 headerClone
3651 .removeAttr('id')
3652 .css( 'margin-left', 0 )
3653 .append( captionSide === 'top' ? caption : null )
3654 .append(
3655 table.children('thead')
3656 )
3657 )
3658 )
3659 )
3660 .append(
3661 $(_div, { 'class': classes.sScrollBody } )
3662 .css( {
3663 overflow: 'auto',
3664 height: size( scrollY ),
3665 width: size( scrollX )
3666 } )
3667 .append( table )
3668 );
3669
3670 if ( footer ) {
3671 scroller.append(
3672 $(_div, { 'class': classes.sScrollFoot } )
3673 .css( {
3674 overflow: 'hidden',
3675 border: 0,
3676 width: scrollX ? size(scrollX) : '100%'
3677 } )
3678 .append(
3679 $(_div, { 'class': classes.sScrollFootInner } )
3680 .append(
3681 footerClone
3682 .removeAttr('id')
3683 .css( 'margin-left', 0 )
3684 .append( captionSide === 'bottom' ? caption : null )
3685 .append(
3686 table.children('tfoot')
3687 )
3688 )
3689 )
3690 );
3691 }
3692
3693 var children = scroller.children();
3694 var scrollHead = children[0];
3695 var scrollBody = children[1];
3696 var scrollFoot = footer ? children[2] : null;
3697
3698 // When the body is scrolled, then we also want to scroll the headers
3699 if ( scrollX ) {
3700 $(scrollBody).scroll( function (e) {
3701 var scrollLeft = this.scrollLeft;
3702
3703 scrollHead.scrollLeft = scrollLeft;
3704
3705 if ( footer ) {
3706 scrollFoot.scrollLeft = scrollLeft;
3707 }
3708 } );
3709 }
3710
3711 settings.nScrollHead = scrollHead;
3712 settings.nScrollBody = scrollBody;
3713 settings.nScrollFoot = scrollFoot;
3714
3715 // On redraw - align columns
3716 settings.aoDrawCallback.push( {
3717 "fn": _fnScrollDraw,
3718 "sName": "scrolling"
3719 } );
3720
3721 return scroller[0];
3722 }
3723
3724
3725
3726 /**
3727 * Update the header, footer and body tables for resizing - i.e. column
3728 * alignment.
3729 *
3730 * Welcome to the most horrible function DataTables. The process that this
3731 * function follows is basically:
3732 * 1. Re-create the table inside the scrolling div
3733 * 2. Take live measurements from the DOM
3734 * 3. Apply the measurements to align the columns
3735 * 4. Clean up
3736 *
3737 * @param {object} settings dataTables settings object
3738 * @memberof DataTable#oApi
3739 */
3740 function _fnScrollDraw ( settings )
3741 {
3742 // Given that this is such a monster function, a lot of variables are use
3743 // to try and keep the minimised size as small as possible
3744 var
3745 scroll = settings.oScroll,
3746 scrollX = scroll.sX,
3747 scrollXInner = scroll.sXInner,
3748 scrollY = scroll.sY,
3749 barWidth = scroll.iBarWidth,
3750 divHeader = $(settings.nScrollHead),
3751 divHeaderStyle = divHeader[0].style,
3752 divHeaderInner = divHeader.children('div'),
3753 divHeaderInnerStyle = divHeaderInner[0].style,
3754 divHeaderTable = divHeaderInner.children('table'),
3755 divBodyEl = settings.nScrollBody,
3756 divBody = $(divBodyEl),
3757 divBodyStyle = divBodyEl.style,
3758 divFooter = $(settings.nScrollFoot),
3759 divFooterInner = divFooter.children('div'),
3760 divFooterTable = divFooterInner.children('table'),
3761 header = $(settings.nTHead),
3762 table = $(settings.nTable),
3763 tableEl = table[0],
3764 tableStyle = tableEl.style,
3765 footer = settings.nTFoot ? $(settings.nTFoot) : null,
3766 browser = settings.oBrowser,
3767 ie67 = browser.bScrollOversize,
3768 headerTrgEls, footerTrgEls,
3769 headerSrcEls, footerSrcEls,
3770 headerCopy, footerCopy,
3771 headerWidths=[], footerWidths=[],
3772 headerContent=[],
3773 idx, correction, sanityWidth,
3774 zeroOut = function(nSizer) {
3775 var style = nSizer.style;
3776 style.paddingTop = "0";
3777 style.paddingBottom = "0";
3778 style.borderTopWidth = "0";
3779 style.borderBottomWidth = "0";
3780 style.height = 0;
3781 };
3782
3783 /*
3784 * 1. Re-create the table inside the scrolling div
3785 */
3786
3787 // Remove the old minimised thead and tfoot elements in the inner table
3788 table.children('thead, tfoot').remove();
3789
3790 // Clone the current header and footer elements and then place it into the inner table
3791 headerCopy = header.clone().prependTo( table );
3792 headerTrgEls = header.find('tr'); // original header is in its own table
3793 headerSrcEls = headerCopy.find('tr');
3794 headerCopy.find('th, td').removeAttr('tabindex');
3795
3796 if ( footer ) {
3797 footerCopy = footer.clone().prependTo( table );
3798 footerTrgEls = footer.find('tr'); // the original tfoot is in its own table and must be sized
3799 footerSrcEls = footerCopy.find('tr');
3800 }
3801
3802
3803 /*
3804 * 2. Take live measurements from the DOM - do not alter the DOM itself!
3805 */
3806
3807 // Remove old sizing and apply the calculated column widths
3808 // Get the unique column headers in the newly created (cloned) header. We want to apply the
3809 // calculated sizes to this header
3810 if ( ! scrollX )
3811 {
3812 divBodyStyle.width = '100%';
3813 divHeader[0].style.width = '100%';
3814 }
3815
3816 $.each( _fnGetUniqueThs( settings, headerCopy ), function ( i, el ) {
3817 idx = _fnVisibleToColumnIndex( settings, i );
3818 el.style.width = settings.aoColumns[idx].sWidth;
3819 } );
3820
3821 if ( footer ) {
3822 _fnApplyToChildren( function(n) {
3823 n.style.width = "";
3824 }, footerSrcEls );
3825 }
3826
3827 // If scroll collapse is enabled, when we put the headers back into the body for sizing, we
3828 // will end up forcing the scrollbar to appear, making our measurements wrong for when we
3829 // then hide it (end of this function), so add the header height to the body scroller.
3830 if ( scroll.bCollapse && scrollY !== "" ) {
3831 divBodyStyle.height = (divBody[0].offsetHeight + header[0].offsetHeight)+"px";
3832 }
3833
3834 // Size the table as a whole
3835 sanityWidth = table.outerWidth();
3836 if ( scrollX === "" ) {
3837 // No x scrolling
3838 tableStyle.width = "100%";
3839
3840 // IE7 will make the width of the table when 100% include the scrollbar
3841 // - which is shouldn't. When there is a scrollbar we need to take this
3842 // into account.
3843 if ( ie67 && (table.find('tbody').height() > divBodyEl.offsetHeight ||
3844 divBody.css('overflow-y') == "scroll")
3845 ) {
3846 tableStyle.width = _fnStringToCss( table.outerWidth() - barWidth);
3847 }
3848 }
3849 else
3850 {
3851 // x scrolling
3852 if ( scrollXInner !== "" ) {
3853 // x scroll inner has been given - use it
3854 tableStyle.width = _fnStringToCss(scrollXInner);
3855 }
3856 else if ( sanityWidth == divBody.width() && divBody.height() < table.height() ) {
3857 // There is y-scrolling - try to take account of the y scroll bar
3858 tableStyle.width = _fnStringToCss( sanityWidth-barWidth );
3859 if ( table.outerWidth() > sanityWidth-barWidth ) {
3860 // Not possible to take account of it
3861 tableStyle.width = _fnStringToCss( sanityWidth );
3862 }
3863 }
3864 else {
3865 // When all else fails
3866 tableStyle.width = _fnStringToCss( sanityWidth );
3867 }
3868 }
3869
3870 // Recalculate the sanity width - now that we've applied the required width,
3871 // before it was a temporary variable. This is required because the column
3872 // width calculation is done before this table DOM is created.
3873 sanityWidth = table.outerWidth();
3874
3875 // Hidden header should have zero height, so remove padding and borders. Then
3876 // set the width based on the real headers
3877
3878 // Apply all styles in one pass
3879 _fnApplyToChildren( zeroOut, headerSrcEls );
3880
3881 // Read all widths in next pass
3882 _fnApplyToChildren( function(nSizer) {
3883 headerContent.push( nSizer.innerHTML );
3884 headerWidths.push( _fnStringToCss( $(nSizer).css('width') ) );
3885 }, headerSrcEls );
3886
3887 // Apply all widths in final pass
3888 _fnApplyToChildren( function(nToSize, i) {
3889 nToSize.style.width = headerWidths[i];
3890 }, headerTrgEls );
3891
3892 $(headerSrcEls).height(0);
3893
3894 /* Same again with the footer if we have one */
3895 if ( footer )
3896 {
3897 _fnApplyToChildren( zeroOut, footerSrcEls );
3898
3899 _fnApplyToChildren( function(nSizer) {
3900 footerWidths.push( _fnStringToCss( $(nSizer).css('width') ) );
3901 }, footerSrcEls );
3902
3903 _fnApplyToChildren( function(nToSize, i) {
3904 nToSize.style.width = footerWidths[i];
3905 }, footerTrgEls );
3906
3907 $(footerSrcEls).height(0);
3908 }
3909
3910
3911 /*
3912 * 3. Apply the measurements
3913 */
3914
3915 // "Hide" the header and footer that we used for the sizing. We need to keep
3916 // the content of the cell so that the width applied to the header and body
3917 // both match, but we want to hide it completely. We want to also fix their
3918 // width to what they currently are
3919 _fnApplyToChildren( function(nSizer, i) {
3920 nSizer.innerHTML = '<div class="dataTables_sizing" style="height:0;overflow:hidden;">'+headerContent[i]+'</div>';
3921 nSizer.style.width = headerWidths[i];
3922 }, headerSrcEls );
3923
3924 if ( footer )
3925 {
3926 _fnApplyToChildren( function(nSizer, i) {
3927 nSizer.innerHTML = "";
3928 nSizer.style.width = footerWidths[i];
3929 }, footerSrcEls );
3930 }
3931
3932 // Sanity check that the table is of a sensible width. If not then we are going to get
3933 // misalignment - try to prevent this by not allowing the table to shrink below its min width
3934 if ( table.outerWidth() < sanityWidth )
3935 {
3936 // The min width depends upon if we have a vertical scrollbar visible or not */
3937 correction = ((divBodyEl.scrollHeight > divBodyEl.offsetHeight ||
3938 divBody.css('overflow-y') == "scroll")) ?
3939 sanityWidth+barWidth :
3940 sanityWidth;
3941
3942 // IE6/7 are a law unto themselves...
3943 if ( ie67 && (divBodyEl.scrollHeight >
3944 divBodyEl.offsetHeight || divBody.css('overflow-y') == "scroll")
3945 ) {
3946 tableStyle.width = _fnStringToCss( correction-barWidth );
3947 }
3948
3949 // And give the user a warning that we've stopped the table getting too small
3950 if ( scrollX === "" || scrollXInner !== "" ) {
3951 _fnLog( settings, 1, 'Possible column misalignment', 6 );
3952 }
3953 }
3954 else
3955 {
3956 correction = '100%';
3957 }
3958
3959 // Apply to the container elements
3960 divBodyStyle.width = _fnStringToCss( correction );
3961 divHeaderStyle.width = _fnStringToCss( correction );
3962
3963 if ( footer ) {
3964 settings.nScrollFoot.style.width = _fnStringToCss( correction );
3965 }
3966
3967
3968 /*
3969 * 4. Clean up
3970 */
3971 if ( ! scrollY ) {
3972 /* IE7< puts a vertical scrollbar in place (when it shouldn't be) due to subtracting
3973 * the scrollbar height from the visible display, rather than adding it on. We need to
3974 * set the height in order to sort this. Don't want to do it in any other browsers.
3975 */
3976 if ( ie67 ) {
3977 divBodyStyle.height = _fnStringToCss( tableEl.offsetHeight+barWidth );
3978 }
3979 }
3980
3981 if ( scrollY && scroll.bCollapse ) {
3982 divBodyStyle.height = _fnStringToCss( scrollY );
3983
3984 var iExtra = (scrollX && tableEl.offsetWidth > divBodyEl.offsetWidth) ?
3985 barWidth :
3986 0;
3987
3988 if ( tableEl.offsetHeight < divBodyEl.offsetHeight ) {
3989 divBodyStyle.height = _fnStringToCss( tableEl.offsetHeight+iExtra );
3990 }
3991 }
3992
3993 /* Finally set the width's of the header and footer tables */
3994 var iOuterWidth = table.outerWidth();
3995 divHeaderTable[0].style.width = _fnStringToCss( iOuterWidth );
3996 divHeaderInnerStyle.width = _fnStringToCss( iOuterWidth );
3997
3998 // Figure out if there are scrollbar present - if so then we need a the header and footer to
3999 // provide a bit more space to allow "overflow" scrolling (i.e. past the scrollbar)
4000 var bScrolling = table.height() > divBodyEl.clientHeight || divBody.css('overflow-y') == "scroll";
4001 var padding = 'padding' + (browser.bScrollbarLeft ? 'Left' : 'Right' );
4002 divHeaderInnerStyle[ padding ] = bScrolling ? barWidth+"px" : "0px";
4003
4004 if ( footer ) {
4005 divFooterTable[0].style.width = _fnStringToCss( iOuterWidth );
4006 divFooterInner[0].style.width = _fnStringToCss( iOuterWidth );
4007 divFooterInner[0].style[padding] = bScrolling ? barWidth+"px" : "0px";
4008 }
4009
4010 /* Adjust the position of the header in case we loose the y-scrollbar */
4011 divBody.scroll();
4012
4013 // If sorting or filtering has occurred, jump the scrolling back to the top
4014 // only if we aren't holding the position
4015 if ( (settings.bSorted || settings.bFiltered) && ! settings._drawHold ) {
4016 divBodyEl.scrollTop = 0;
4017 }
4018 }
4019
4020
4021
4022 /**
4023 * Apply a given function to the display child nodes of an element array (typically
4024 * TD children of TR rows
4025 * @param {function} fn Method to apply to the objects
4026 * @param array {nodes} an1 List of elements to look through for display children
4027 * @param array {nodes} an2 Another list (identical structure to the first) - optional
4028 * @memberof DataTable#oApi
4029 */
4030 function _fnApplyToChildren( fn, an1, an2 )
4031 {
4032 var index=0, i=0, iLen=an1.length;
4033 var nNode1, nNode2;
4034
4035 while ( i < iLen ) {
4036 nNode1 = an1[i].firstChild;
4037 nNode2 = an2 ? an2[i].firstChild : null;
4038
4039 while ( nNode1 ) {
4040 if ( nNode1.nodeType === 1 ) {
4041 if ( an2 ) {
4042 fn( nNode1, nNode2, index );
4043 }
4044 else {
4045 fn( nNode1, index );
4046 }
4047
4048 index++;
4049 }
4050
4051 nNode1 = nNode1.nextSibling;
4052 nNode2 = an2 ? nNode2.nextSibling : null;
4053 }
4054
4055 i++;
4056 }
4057 }
4058
4059
4060
4061 var __re_html_remove = /<.*?>/g;
4062
4063
4064 /**
4065 * Calculate the width of columns for the table
4066 * @param {object} oSettings dataTables settings object
4067 * @memberof DataTable#oApi
4068 */
4069 function _fnCalculateColumnWidths ( oSettings )
4070 {
4071 var
4072 table = oSettings.nTable,
4073 columns = oSettings.aoColumns,
4074 scroll = oSettings.oScroll,
4075 scrollY = scroll.sY,
4076 scrollX = scroll.sX,
4077 scrollXInner = scroll.sXInner,
4078 columnCount = columns.length,
4079 visibleColumns = _fnGetColumns( oSettings, 'bVisible' ),
4080 headerCells = $('th', oSettings.nTHead),
4081 tableWidthAttr = table.getAttribute('width'),
4082 tableContainer = table.parentNode,
4083 userInputs = false,
4084 i, column, columnIdx, width, outerWidth;
4085
4086 /* Convert any user input sizes into pixel sizes */
4087 for ( i=0 ; i<visibleColumns.length ; i++ ) {
4088 column = columns[ visibleColumns[i] ];
4089
4090 if ( column.sWidth !== null ) {
4091 column.sWidth = _fnConvertToWidth( column.sWidthOrig, tableContainer );
4092
4093 userInputs = true;
4094 }
4095 }
4096
4097 /* If the number of columns in the DOM equals the number that we have to
4098 * process in DataTables, then we can use the offsets that are created by
4099 * the web- browser. No custom sizes can be set in order for this to happen,
4100 * nor scrolling used
4101 */
4102 if ( ! userInputs && ! scrollX && ! scrollY &&
4103 columnCount == _fnVisbleColumns( oSettings ) &&
4104 columnCount == headerCells.length
4105 ) {
4106 for ( i=0 ; i<columnCount ; i++ ) {
4107 columns[i].sWidth = _fnStringToCss( headerCells.eq(i).width() );
4108 }
4109 }
4110 else
4111 {
4112 // Otherwise construct a single row table with the widest node in the
4113 // data, assign any user defined widths, then insert it into the DOM and
4114 // allow the browser to do all the hard work of calculating table widths
4115 var tmpTable = $(table).clone() // don't use cloneNode - IE8 will remove events on the main table
4116 .empty()
4117 .css( 'visibility', 'hidden' )
4118 .removeAttr( 'id' )
4119 .append( $(oSettings.nTHead).clone( false ) )
4120 .append( $(oSettings.nTFoot).clone( false ) )
4121 .append( $('<tbody><tr/></tbody>') );
4122
4123 // Remove any assigned widths from the footer (from scrolling)
4124 tmpTable.find('tfoot th, tfoot td').css('width', '');
4125
4126 var tr = tmpTable.find( 'tbody tr' );
4127
4128 // Apply custom sizing to the cloned header
4129 headerCells = _fnGetUniqueThs( oSettings, tmpTable.find('thead')[0] );
4130
4131 for ( i=0 ; i<visibleColumns.length ; i++ ) {
4132 column = columns[ visibleColumns[i] ];
4133
4134 headerCells[i].style.width = column.sWidthOrig !== null && column.sWidthOrig !== '' ?
4135 _fnStringToCss( column.sWidthOrig ) :
4136 '';
4137 }
4138
4139 // Find the widest cell for each column and put it into the table
4140 if ( oSettings.aoData.length ) {
4141 for ( i=0 ; i<visibleColumns.length ; i++ ) {
4142 columnIdx = visibleColumns[i];
4143 column = columns[ columnIdx ];
4144
4145 $( _fnGetWidestNode( oSettings, columnIdx ) )
4146 .clone( false )
4147 .append( column.sContentPadding )
4148 .appendTo( tr );
4149 }
4150 }
4151
4152 // Table has been built, attach to the document so we can work with it
4153 tmpTable.appendTo( tableContainer );
4154
4155 // When scrolling (X or Y) we want to set the width of the table as
4156 // appropriate. However, when not scrolling leave the table width as it
4157 // is. This results in slightly different, but I think correct behaviour
4158 if ( scrollX && scrollXInner ) {
4159 tmpTable.width( scrollXInner );
4160 }
4161 else if ( scrollX ) {
4162 tmpTable.css( 'width', 'auto' );
4163
4164 if ( tmpTable.width() < tableContainer.offsetWidth ) {
4165 tmpTable.width( tableContainer.offsetWidth );
4166 }
4167 }
4168 else if ( scrollY ) {
4169 tmpTable.width( tableContainer.offsetWidth );
4170 }
4171 else if ( tableWidthAttr ) {
4172 tmpTable.width( tableWidthAttr );
4173 }
4174
4175 // Take into account the y scrollbar
4176 _fnScrollingWidthAdjust( oSettings, tmpTable[0] );
4177
4178 // Browsers need a bit of a hand when a width is assigned to any columns
4179 // when x-scrolling as they tend to collapse the table to the min-width,
4180 // even if we sent the column widths. So we need to keep track of what
4181 // the table width should be by summing the user given values, and the
4182 // automatic values
4183 if ( scrollX )
4184 {
4185 var total = 0;
4186
4187 for ( i=0 ; i<visibleColumns.length ; i++ ) {
4188 column = columns[ visibleColumns[i] ];
4189 outerWidth = $(headerCells[i]).outerWidth();
4190
4191 total += column.sWidthOrig === null ?
4192 outerWidth :
4193 parseInt( column.sWidth, 10 ) + outerWidth - $(headerCells[i]).width();
4194 }
4195
4196 tmpTable.width( _fnStringToCss( total ) );
4197 table.style.width = _fnStringToCss( total );
4198 }
4199
4200 // Get the width of each column in the constructed table
4201 for ( i=0 ; i<visibleColumns.length ; i++ ) {
4202 column = columns[ visibleColumns[i] ];
4203 width = $(headerCells[i]).width();
4204
4205 if ( width ) {
4206 column.sWidth = _fnStringToCss( width );
4207 }
4208 }
4209
4210 table.style.width = _fnStringToCss( tmpTable.css('width') );
4211
4212 // Finished with the table - ditch it
4213 tmpTable.remove();
4214 }
4215
4216 // If there is a width attr, we want to attach an event listener which
4217 // allows the table sizing to automatically adjust when the window is
4218 // resized. Use the width attr rather than CSS, since we can't know if the
4219 // CSS is a relative value or absolute - DOM read is always px.
4220 if ( tableWidthAttr ) {
4221 table.style.width = _fnStringToCss( tableWidthAttr );
4222 }
4223
4224 if ( (tableWidthAttr || scrollX) && ! oSettings._reszEvt ) {
4225 $(window).bind('resize.DT-'+oSettings.sInstance, _fnThrottle( function () {
4226 _fnAdjustColumnSizing( oSettings );
4227 } ) );
4228
4229 oSettings._reszEvt = true;
4230 }
4231 }
4232
4233
4234 /**
4235 * Throttle the calls to a function. Arguments and context are maintained for
4236 * the throttled function
4237 * @param {function} fn Function to be called
4238 * @param {int} [freq=200] call frequency in mS
4239 * @returns {function} wrapped function
4240 * @memberof DataTable#oApi
4241 */
4242 function _fnThrottle( fn, freq ) {
4243 var
4244 frequency = freq !== undefined ? freq : 200,
4245 last,
4246 timer;
4247
4248 return function () {
4249 var
4250 that = this,
4251 now = +new Date(),
4252 args = arguments;
4253
4254 if ( last && now < last + frequency ) {
4255 clearTimeout( timer );
4256
4257 timer = setTimeout( function () {
4258 last = undefined;
4259 fn.apply( that, args );
4260 }, frequency );
4261 }
4262 else if ( last ) {
4263 last = now;
4264 fn.apply( that, args );
4265 }
4266 else {
4267 last = now;
4268 }
4269 };
4270 }
4271
4272
4273 /**
4274 * Convert a CSS unit width to pixels (e.g. 2em)
4275 * @param {string} width width to be converted
4276 * @param {node} parent parent to get the with for (required for relative widths) - optional
4277 * @returns {int} width in pixels
4278 * @memberof DataTable#oApi
4279 */
4280 function _fnConvertToWidth ( width, parent )
4281 {
4282 if ( ! width ) {
4283 return 0;
4284 }
4285
4286 var n = $('<div/>')
4287 .css( 'width', _fnStringToCss( width ) )
4288 .appendTo( parent || document.body );
4289
4290 var val = n[0].offsetWidth;
4291 n.remove();
4292
4293 return val;
4294 }
4295
4296
4297 /**
4298 * Adjust a table's width to take account of vertical scroll bar
4299 * @param {object} oSettings dataTables settings object
4300 * @param {node} n table node
4301 * @memberof DataTable#oApi
4302 */
4303
4304 function _fnScrollingWidthAdjust ( settings, n )
4305 {
4306 var scroll = settings.oScroll;
4307
4308 if ( scroll.sX || scroll.sY ) {
4309 // When y-scrolling only, we want to remove the width of the scroll bar
4310 // so the table + scroll bar will fit into the area available, otherwise
4311 // we fix the table at its current size with no adjustment
4312 var correction = ! scroll.sX ? scroll.iBarWidth : 0;
4313 n.style.width = _fnStringToCss( $(n).outerWidth() - correction );
4314 }
4315 }
4316
4317
4318 /**
4319 * Get the widest node
4320 * @param {object} settings dataTables settings object
4321 * @param {int} colIdx column of interest
4322 * @returns {node} widest table node
4323 * @memberof DataTable#oApi
4324 */
4325 function _fnGetWidestNode( settings, colIdx )
4326 {
4327 var idx = _fnGetMaxLenString( settings, colIdx );
4328 if ( idx < 0 ) {
4329 return null;
4330 }
4331
4332 var data = settings.aoData[ idx ];
4333 return ! data.nTr ? // Might not have been created when deferred rendering
4334 $('<td/>').html( _fnGetCellData( settings, idx, colIdx, 'display' ) )[0] :
4335 data.anCells[ colIdx ];
4336 }
4337
4338
4339 /**
4340 * Get the maximum strlen for each data column
4341 * @param {object} settings dataTables settings object
4342 * @param {int} colIdx column of interest
4343 * @returns {string} max string length for each column
4344 * @memberof DataTable#oApi
4345 */
4346 function _fnGetMaxLenString( settings, colIdx )
4347 {
4348 var s, max=-1, maxIdx = -1;
4349
4350 for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
4351 s = _fnGetCellData( settings, i, colIdx, 'display' )+'';
4352 s = s.replace( __re_html_remove, '' );
4353
4354 if ( s.length > max ) {
4355 max = s.length;
4356 maxIdx = i;
4357 }
4358 }
4359
4360 return maxIdx;
4361 }
4362
4363
4364 /**
4365 * Append a CSS unit (only if required) to a string
4366 * @param {string} value to css-ify
4367 * @returns {string} value with css unit
4368 * @memberof DataTable#oApi
4369 */
4370 function _fnStringToCss( s )
4371 {
4372 if ( s === null ) {
4373 return '0px';
4374 }
4375
4376 if ( typeof s == 'number' ) {
4377 return s < 0 ?
4378 '0px' :
4379 s+'px';
4380 }
4381
4382 // Check it has a unit character already
4383 return s.match(/\d$/) ?
4384 s+'px' :
4385 s;
4386 }
4387
4388
4389 /**
4390 * Get the width of a scroll bar in this browser being used
4391 * @returns {int} width in pixels
4392 * @memberof DataTable#oApi
4393 */
4394 function _fnScrollBarWidth ()
4395 {
4396 // On first run a static variable is set, since this is only needed once.
4397 // Subsequent runs will just use the previously calculated value
4398 if ( ! DataTable.__scrollbarWidth ) {
4399 var inner = $('<p/>').css( {
4400 width: '100%',
4401 height: 200,
4402 padding: 0
4403 } )[0];
4404
4405 var outer = $('<div/>')
4406 .css( {
4407 position: 'absolute',
4408 top: 0,
4409 left: 0,
4410 width: 200,
4411 height: 150,
4412 padding: 0,
4413 overflow: 'hidden',
4414 visibility: 'hidden'
4415 } )
4416 .append( inner )
4417 .appendTo( 'body' );
4418
4419 var w1 = inner.offsetWidth;
4420 outer.css( 'overflow', 'scroll' );
4421 var w2 = inner.offsetWidth;
4422
4423 if ( w1 === w2 ) {
4424 w2 = outer[0].clientWidth;
4425 }
4426
4427 outer.remove();
4428
4429 DataTable.__scrollbarWidth = w1 - w2;
4430 }
4431
4432 return DataTable.__scrollbarWidth;
4433 }
4434
4435
4436
4437 function _fnSortFlatten ( settings )
4438 {
4439 var
4440 i, iLen, k, kLen,
4441 aSort = [],
4442 aiOrig = [],
4443 aoColumns = settings.aoColumns,
4444 aDataSort, iCol, sType, srcCol,
4445 fixed = settings.aaSortingFixed,
4446 fixedObj = $.isPlainObject( fixed ),
4447 nestedSort = [],
4448 add = function ( a ) {
4449 if ( a.length && ! $.isArray( a[0] ) ) {
4450 // 1D array
4451 nestedSort.push( a );
4452 }
4453 else {
4454 // 2D array
4455 nestedSort.push.apply( nestedSort, a );
4456 }
4457 };
4458
4459 // Build the sort array, with pre-fix and post-fix options if they have been
4460 // specified
4461 if ( $.isArray( fixed ) ) {
4462 add( fixed );
4463 }
4464
4465 if ( fixedObj && fixed.pre ) {
4466 add( fixed.pre );
4467 }
4468
4469 add( settings.aaSorting );
4470
4471 if (fixedObj && fixed.post ) {
4472 add( fixed.post );
4473 }
4474
4475 for ( i=0 ; i<nestedSort.length ; i++ )
4476 {
4477 srcCol = nestedSort[i][0];
4478 aDataSort = aoColumns[ srcCol ].aDataSort;
4479
4480 for ( k=0, kLen=aDataSort.length ; k<kLen ; k++ )
4481 {
4482 iCol = aDataSort[k];
4483 sType = aoColumns[ iCol ].sType || 'string';
4484
4485 if ( nestedSort[i]._idx === undefined ) {
4486 nestedSort[i]._idx = $.inArray( nestedSort[i][1], aoColumns[iCol].asSorting );
4487 }
4488
4489 aSort.push( {
4490 src: srcCol,
4491 col: iCol,
4492 dir: nestedSort[i][1],
4493 index: nestedSort[i]._idx,
4494 type: sType,
4495 formatter: DataTable.ext.type.order[ sType+"-pre" ]
4496 } );
4497 }
4498 }
4499
4500 return aSort;
4501 }
4502
4503 /**
4504 * Change the order of the table
4505 * @param {object} oSettings dataTables settings object
4506 * @memberof DataTable#oApi
4507 * @todo This really needs split up!
4508 */
4509 function _fnSort ( oSettings )
4510 {
4511 var
4512 i, ien, iLen, j, jLen, k, kLen,
4513 sDataType, nTh,
4514 aiOrig = [],
4515 oExtSort = DataTable.ext.type.order,
4516 aoData = oSettings.aoData,
4517 aoColumns = oSettings.aoColumns,
4518 aDataSort, data, iCol, sType, oSort,
4519 formatters = 0,
4520 sortCol,
4521 displayMaster = oSettings.aiDisplayMaster,
4522 aSort;
4523
4524 // Resolve any column types that are unknown due to addition or invalidation
4525 // @todo Can this be moved into a 'data-ready' handler which is called when
4526 // data is going to be used in the table?
4527 _fnColumnTypes( oSettings );
4528
4529 aSort = _fnSortFlatten( oSettings );
4530
4531 for ( i=0, ien=aSort.length ; i<ien ; i++ ) {
4532 sortCol = aSort[i];
4533
4534 // Track if we can use the fast sort algorithm
4535 if ( sortCol.formatter ) {
4536 formatters++;
4537 }
4538
4539 // Load the data needed for the sort, for each cell
4540 _fnSortData( oSettings, sortCol.col );
4541 }
4542
4543 /* No sorting required if server-side or no sorting array */
4544 if ( _fnDataSource( oSettings ) != 'ssp' && aSort.length !== 0 )
4545 {
4546 // Create a value - key array of the current row positions such that we can use their
4547 // current position during the sort, if values match, in order to perform stable sorting
4548 for ( i=0, iLen=displayMaster.length ; i<iLen ; i++ ) {
4549 aiOrig[ displayMaster[i] ] = i;
4550 }
4551
4552 /* Do the sort - here we want multi-column sorting based on a given data source (column)
4553 * and sorting function (from oSort) in a certain direction. It's reasonably complex to
4554 * follow on it's own, but this is what we want (example two column sorting):
4555 * fnLocalSorting = function(a,b){
4556 * var iTest;
4557 * iTest = oSort['string-asc']('data11', 'data12');
4558 * if (iTest !== 0)
4559 * return iTest;
4560 * iTest = oSort['numeric-desc']('data21', 'data22');
4561 * if (iTest !== 0)
4562 * return iTest;
4563 * return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
4564 * }
4565 * Basically we have a test for each sorting column, if the data in that column is equal,
4566 * test the next column. If all columns match, then we use a numeric sort on the row
4567 * positions in the original data array to provide a stable sort.
4568 *
4569 * Note - I know it seems excessive to have two sorting methods, but the first is around
4570 * 15% faster, so the second is only maintained for backwards compatibility with sorting
4571 * methods which do not have a pre-sort formatting function.
4572 */
4573 if ( formatters === aSort.length ) {
4574 // All sort types have formatting functions
4575 displayMaster.sort( function ( a, b ) {
4576 var
4577 x, y, k, test, sort,
4578 len=aSort.length,
4579 dataA = aoData[a]._aSortData,
4580 dataB = aoData[b]._aSortData;
4581
4582 for ( k=0 ; k<len ; k++ ) {
4583 sort = aSort[k];
4584
4585 x = dataA[ sort.col ];
4586 y = dataB[ sort.col ];
4587
4588 test = x<y ? -1 : x>y ? 1 : 0;
4589 if ( test !== 0 ) {
4590 return sort.dir === 'asc' ? test : -test;
4591 }
4592 }
4593
4594 x = aiOrig[a];
4595 y = aiOrig[b];
4596 return x<y ? -1 : x>y ? 1 : 0;
4597 } );
4598 }
4599 else {
4600 // Depreciated - remove in 1.11 (providing a plug-in option)
4601 // Not all sort types have formatting methods, so we have to call their sorting
4602 // methods.
4603 displayMaster.sort( function ( a, b ) {
4604 var
4605 x, y, k, l, test, sort, fn,
4606 len=aSort.length,
4607 dataA = aoData[a]._aSortData,
4608 dataB = aoData[b]._aSortData;
4609
4610 for ( k=0 ; k<len ; k++ ) {
4611 sort = aSort[k];
4612
4613 x = dataA[ sort.col ];
4614 y = dataB[ sort.col ];
4615
4616 fn = oExtSort[ sort.type+"-"+sort.dir ] || oExtSort[ "string-"+sort.dir ];
4617 test = fn( x, y );
4618 if ( test !== 0 ) {
4619 return test;
4620 }
4621 }
4622
4623 x = aiOrig[a];
4624 y = aiOrig[b];
4625 return x<y ? -1 : x>y ? 1 : 0;
4626 } );
4627 }
4628 }
4629
4630 /* Tell the draw function that we have sorted the data */
4631 oSettings.bSorted = true;
4632 }
4633
4634
4635 function _fnSortAria ( settings )
4636 {
4637 var label;
4638 var nextSort;
4639 var columns = settings.aoColumns;
4640 var aSort = _fnSortFlatten( settings );
4641 var oAria = settings.oLanguage.oAria;
4642
4643 // ARIA attributes - need to loop all columns, to update all (removing old
4644 // attributes as needed)
4645 for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
4646 {
4647 var col = columns[i];
4648 var asSorting = col.asSorting;
4649 var sTitle = col.sTitle.replace( /<.*?>/g, "" );
4650 var th = col.nTh;
4651
4652 // IE7 is throwing an error when setting these properties with jQuery's
4653 // attr() and removeAttr() methods...
4654 th.removeAttribute('aria-sort');
4655
4656 /* In ARIA only the first sorting column can be marked as sorting - no multi-sort option */
4657 if ( col.bSortable ) {
4658 if ( aSort.length > 0 && aSort[0].col == i ) {
4659 th.setAttribute('aria-sort', aSort[0].dir=="asc" ? "ascending" : "descending" );
4660 nextSort = asSorting[ aSort[0].index+1 ] || asSorting[0];
4661 }
4662 else {
4663 nextSort = asSorting[0];
4664 }
4665
4666 label = sTitle + ( nextSort === "asc" ?
4667 oAria.sSortAscending :
4668 oAria.sSortDescending
4669 );
4670 }
4671 else {
4672 label = sTitle;
4673 }
4674
4675 th.setAttribute('aria-label', label);
4676 }
4677 }
4678
4679
4680 /**
4681 * Function to run on user sort request
4682 * @param {object} settings dataTables settings object
4683 * @param {node} attachTo node to attach the handler to
4684 * @param {int} colIdx column sorting index
4685 * @param {boolean} [append=false] Append the requested sort to the existing
4686 * sort if true (i.e. multi-column sort)
4687 * @param {function} [callback] callback function
4688 * @memberof DataTable#oApi
4689 */
4690 function _fnSortListener ( settings, colIdx, append, callback )
4691 {
4692 var col = settings.aoColumns[ colIdx ];
4693 var sorting = settings.aaSorting;
4694 var asSorting = col.asSorting;
4695 var nextSortIdx;
4696 var next = function ( a, overflow ) {
4697 var idx = a._idx;
4698 if ( idx === undefined ) {
4699 idx = $.inArray( a[1], asSorting );
4700 }
4701
4702 return idx+1 < asSorting.length ?
4703 idx+1 :
4704 overflow ?
4705 null :
4706 0;
4707 };
4708
4709 // Convert to 2D array if needed
4710 if ( typeof sorting[0] === 'number' ) {
4711 sorting = settings.aaSorting = [ sorting ];
4712 }
4713
4714 // If appending the sort then we are multi-column sorting
4715 if ( append && settings.oFeatures.bSortMulti ) {
4716 // Are we already doing some kind of sort on this column?
4717 var sortIdx = $.inArray( colIdx, _pluck(sorting, '0') );
4718
4719 if ( sortIdx !== -1 ) {
4720 // Yes, modify the sort
4721 nextSortIdx = next( sorting[sortIdx], true );
4722
4723 if ( nextSortIdx === null ) {
4724 sorting.splice( sortIdx, 1 );
4725 }
4726 else {
4727 sorting[sortIdx][1] = asSorting[ nextSortIdx ];
4728 sorting[sortIdx]._idx = nextSortIdx;
4729 }
4730 }
4731 else {
4732 // No sort on this column yet
4733 sorting.push( [ colIdx, asSorting[0], 0 ] );
4734 sorting[sorting.length-1]._idx = 0;
4735 }
4736 }
4737 else if ( sorting.length && sorting[0][0] == colIdx ) {
4738 // Single column - already sorting on this column, modify the sort
4739 nextSortIdx = next( sorting[0] );
4740
4741 sorting.length = 1;
4742 sorting[0][1] = asSorting[ nextSortIdx ];
4743 sorting[0]._idx = nextSortIdx;
4744 }
4745 else {
4746 // Single column - sort only on this column
4747 sorting.length = 0;
4748 sorting.push( [ colIdx, asSorting[0] ] );
4749 sorting[0]._idx = 0;
4750 }
4751
4752 // Run the sort by calling a full redraw
4753 _fnReDraw( settings );
4754
4755 // callback used for async user interaction
4756 if ( typeof callback == 'function' ) {
4757 callback( settings );
4758 }
4759 }
4760
4761
4762 /**
4763 * Attach a sort handler (click) to a node
4764 * @param {object} settings dataTables settings object
4765 * @param {node} attachTo node to attach the handler to
4766 * @param {int} colIdx column sorting index
4767 * @param {function} [callback] callback function
4768 * @memberof DataTable#oApi
4769 */
4770 function _fnSortAttachListener ( settings, attachTo, colIdx, callback )
4771 {
4772 var col = settings.aoColumns[ colIdx ];
4773
4774 _fnBindAction( attachTo, {}, function (e) {
4775 /* If the column is not sortable - don't to anything */
4776 if ( col.bSortable === false ) {
4777 return;
4778 }
4779
4780 // If processing is enabled use a timeout to allow the processing
4781 // display to be shown - otherwise to it synchronously
4782 if ( settings.oFeatures.bProcessing ) {
4783 _fnProcessingDisplay( settings, true );
4784
4785 setTimeout( function() {
4786 _fnSortListener( settings, colIdx, e.shiftKey, callback );
4787
4788 // In server-side processing, the draw callback will remove the
4789 // processing display
4790 if ( _fnDataSource( settings ) !== 'ssp' ) {
4791 _fnProcessingDisplay( settings, false );
4792 }
4793 }, 0 );
4794 }
4795 else {
4796 _fnSortListener( settings, colIdx, e.shiftKey, callback );
4797 }
4798 } );
4799 }
4800
4801
4802 /**
4803 * Set the sorting classes on table's body, Note: it is safe to call this function
4804 * when bSort and bSortClasses are false
4805 * @param {object} oSettings dataTables settings object
4806 * @memberof DataTable#oApi
4807 */
4808 function _fnSortingClasses( settings )
4809 {
4810 var oldSort = settings.aLastSort;
4811 var sortClass = settings.oClasses.sSortColumn;
4812 var sort = _fnSortFlatten( settings );
4813 var features = settings.oFeatures;
4814 var i, ien, colIdx;
4815
4816 if ( features.bSort && features.bSortClasses ) {
4817 // Remove old sorting classes
4818 for ( i=0, ien=oldSort.length ; i<ien ; i++ ) {
4819 colIdx = oldSort[i].src;
4820
4821 // Remove column sorting
4822 $( _pluck( settings.aoData, 'anCells', colIdx ) )
4823 .removeClass( sortClass + (i<2 ? i+1 : 3) );
4824 }
4825
4826 // Add new column sorting
4827 for ( i=0, ien=sort.length ; i<ien ; i++ ) {
4828 colIdx = sort[i].src;
4829
4830 $( _pluck( settings.aoData, 'anCells', colIdx ) )
4831 .addClass( sortClass + (i<2 ? i+1 : 3) );
4832 }
4833 }
4834
4835 settings.aLastSort = sort;
4836 }
4837
4838
4839 // Get the data to sort a column, be it from cache, fresh (populating the
4840 // cache), or from a sort formatter
4841 function _fnSortData( settings, idx )
4842 {
4843 // Custom sorting function - provided by the sort data type
4844 var column = settings.aoColumns[ idx ];
4845 var customSort = DataTable.ext.order[ column.sSortDataType ];
4846 var customData;
4847
4848 if ( customSort ) {
4849 customData = customSort.call( settings.oInstance, settings, idx,
4850 _fnColumnIndexToVisible( settings, idx )
4851 );
4852 }
4853
4854 // Use / populate cache
4855 var row, cellData;
4856 var formatter = DataTable.ext.type.order[ column.sType+"-pre" ];
4857
4858 for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
4859 row = settings.aoData[i];
4860
4861 if ( ! row._aSortData ) {
4862 row._aSortData = [];
4863 }
4864
4865 if ( ! row._aSortData[idx] || customSort ) {
4866 cellData = customSort ?
4867 customData[i] : // If there was a custom sort function, use data from there
4868 _fnGetCellData( settings, i, idx, 'sort' );
4869
4870 row._aSortData[ idx ] = formatter ?
4871 formatter( cellData ) :
4872 cellData;
4873 }
4874 }
4875 }
4876
4877
4878
4879 /**
4880 * Save the state of a table
4881 * @param {object} oSettings dataTables settings object
4882 * @memberof DataTable#oApi
4883 */
4884 function _fnSaveState ( settings )
4885 {
4886 if ( !settings.oFeatures.bStateSave || settings.bDestroying )
4887 {
4888 return;
4889 }
4890
4891 /* Store the interesting variables */
4892 var state = {
4893 time: +new Date(),
4894 start: settings._iDisplayStart,
4895 length: settings._iDisplayLength,
4896 order: $.extend( true, [], settings.aaSorting ),
4897 search: _fnSearchToCamel( settings.oPreviousSearch ),
4898 columns: $.map( settings.aoColumns, function ( col, i ) {
4899 return {
4900 visible: col.bVisible,
4901 search: _fnSearchToCamel( settings.aoPreSearchCols[i] )
4902 };
4903 } )
4904 };
4905
4906 _fnCallbackFire( settings, "aoStateSaveParams", 'stateSaveParams', [settings, state] );
4907
4908 settings.oSavedState = state;
4909 settings.fnStateSaveCallback.call( settings.oInstance, settings, state );
4910 }
4911
4912
4913 /**
4914 * Attempt to load a saved table state
4915 * @param {object} oSettings dataTables settings object
4916 * @param {object} oInit DataTables init object so we can override settings
4917 * @memberof DataTable#oApi
4918 */
4919 function _fnLoadState ( settings, oInit )
4920 {
4921 var i, ien;
4922 var columns = settings.aoColumns;
4923
4924 if ( ! settings.oFeatures.bStateSave ) {
4925 return;
4926 }
4927
4928 var state = settings.fnStateLoadCallback.call( settings.oInstance, settings );
4929 if ( ! state || ! state.time ) {
4930 return;
4931 }
4932
4933 /* Allow custom and plug-in manipulation functions to alter the saved data set and
4934 * cancelling of loading by returning false
4935 */
4936 var abStateLoad = _fnCallbackFire( settings, 'aoStateLoadParams', 'stateLoadParams', [settings, state] );
4937 if ( $.inArray( false, abStateLoad ) !== -1 ) {
4938 return;
4939 }
4940
4941 /* Reject old data */
4942 var duration = settings.iStateDuration;
4943 if ( duration > 0 && state.time < +new Date() - (duration*1000) ) {
4944 return;
4945 }
4946
4947 // Number of columns have changed - all bets are off, no restore of settings
4948 if ( columns.length !== state.columns.length ) {
4949 return;
4950 }
4951
4952 // Store the saved state so it might be accessed at any time
4953 settings.oLoadedState = $.extend( true, {}, state );
4954
4955 // Restore key features - todo - for 1.11 this needs to be done by
4956 // subscribed events
4957 settings._iDisplayStart = state.start;
4958 settings.iInitDisplayStart = state.start;
4959 settings._iDisplayLength = state.length;
4960 settings.aaSorting = [];
4961
4962 // Order
4963 $.each( state.order, function ( i, col ) {
4964 settings.aaSorting.push( col[0] >= columns.length ?
4965 [ 0, col[1] ] :
4966 col
4967 );
4968 } );
4969
4970 // Search
4971 $.extend( settings.oPreviousSearch, _fnSearchToHung( state.search ) );
4972
4973 // Columns
4974 for ( i=0, ien=state.columns.length ; i<ien ; i++ ) {
4975 var col = state.columns[i];
4976
4977 // Visibility
4978 columns[i].bVisible = col.visible;
4979
4980 // Search
4981 $.extend( settings.aoPreSearchCols[i], _fnSearchToHung( col.search ) );
4982 }
4983
4984 _fnCallbackFire( settings, 'aoStateLoaded', 'stateLoaded', [settings, state] );
4985 }
4986
4987
4988 /**
4989 * Return the settings object for a particular table
4990 * @param {node} table table we are using as a dataTable
4991 * @returns {object} Settings object - or null if not found
4992 * @memberof DataTable#oApi
4993 */
4994 function _fnSettingsFromNode ( table )
4995 {
4996 var settings = DataTable.settings;
4997 var idx = $.inArray( table, _pluck( settings, 'nTable' ) );
4998
4999 return idx !== -1 ?
5000 settings[ idx ] :
5001 null;
5002 }
5003
5004
5005 /**
5006 * Log an error message
5007 * @param {object} settings dataTables settings object
5008 * @param {int} level log error messages, or display them to the user
5009 * @param {string} msg error message
5010 * @param {int} tn Technical note id to get more information about the error.
5011 * @memberof DataTable#oApi
5012 */
5013 function _fnLog( settings, level, msg, tn )
5014 {
5015 msg = 'DataTables warning: '+
5016 (settings!==null ? 'table id='+settings.sTableId+' - ' : '')+msg;
5017
5018 if ( tn ) {
5019 msg += '. For more information about this error, please see '+
5020 'http://datatables.net/tn/'+tn;
5021 }
5022
5023 if ( ! level ) {
5024 // Backwards compatibility pre 1.10
5025 var ext = DataTable.ext;
5026 var type = ext.sErrMode || ext.errMode;
5027
5028 if ( type == 'alert' ) {
5029 alert( msg );
5030 }
5031 else {
5032 throw new Error(msg);
5033 }
5034 }
5035 else if ( window.console && console.log ) {
5036 console.log( msg );
5037 }
5038 }
5039
5040
5041 /**
5042 * See if a property is defined on one object, if so assign it to the other object
5043 * @param {object} ret target object
5044 * @param {object} src source object
5045 * @param {string} name property
5046 * @param {string} [mappedName] name to map too - optional, name used if not given
5047 * @memberof DataTable#oApi
5048 */
5049 function _fnMap( ret, src, name, mappedName )
5050 {
5051 if ( $.isArray( name ) ) {
5052 $.each( name, function (i, val) {
5053 if ( $.isArray( val ) ) {
5054 _fnMap( ret, src, val[0], val[1] );
5055 }
5056 else {
5057 _fnMap( ret, src, val );
5058 }
5059 } );
5060
5061 return;
5062 }
5063
5064 if ( mappedName === undefined ) {
5065 mappedName = name;
5066 }
5067
5068 if ( src[name] !== undefined ) {
5069 ret[mappedName] = src[name];
5070 }
5071 }
5072
5073
5074 /**
5075 * Extend objects - very similar to jQuery.extend, but deep copy objects, and
5076 * shallow copy arrays. The reason we need to do this, is that we don't want to
5077 * deep copy array init values (such as aaSorting) since the dev wouldn't be
5078 * able to override them, but we do want to deep copy arrays.
5079 * @param {object} out Object to extend
5080 * @param {object} extender Object from which the properties will be applied to
5081 * out
5082 * @param {boolean} breakRefs If true, then arrays will be sliced to take an
5083 * independent copy with the exception of the `data` or `aaData` parameters
5084 * if they are present. This is so you can pass in a collection to
5085 * DataTables and have that used as your data source without breaking the
5086 * references
5087 * @returns {object} out Reference, just for convenience - out === the return.
5088 * @memberof DataTable#oApi
5089 * @todo This doesn't take account of arrays inside the deep copied objects.
5090 */
5091 function _fnExtend( out, extender, breakRefs )
5092 {
5093 var val;
5094
5095 for ( var prop in extender ) {
5096 if ( extender.hasOwnProperty(prop) ) {
5097 val = extender[prop];
5098
5099 if ( $.isPlainObject( val ) ) {
5100 if ( ! $.isPlainObject( out[prop] ) ) {
5101 out[prop] = {};
5102 }
5103 $.extend( true, out[prop], val );
5104 }
5105 else if ( breakRefs && prop !== 'data' && prop !== 'aaData' && $.isArray(val) ) {
5106 out[prop] = val.slice();
5107 }
5108 else {
5109 out[prop] = val;
5110 }
5111 }
5112 }
5113
5114 return out;
5115 }
5116
5117
5118 /**
5119 * Bind an event handers to allow a click or return key to activate the callback.
5120 * This is good for accessibility since a return on the keyboard will have the
5121 * same effect as a click, if the element has focus.
5122 * @param {element} n Element to bind the action to
5123 * @param {object} oData Data object to pass to the triggered function
5124 * @param {function} fn Callback function for when the event is triggered
5125 * @memberof DataTable#oApi
5126 */
5127 function _fnBindAction( n, oData, fn )
5128 {
5129 $(n)
5130 .bind( 'click.DT', oData, function (e) {
5131 n.blur(); // Remove focus outline for mouse users
5132 fn(e);
5133 } )
5134 .bind( 'keypress.DT', oData, function (e){
5135 if ( e.which === 13 ) {
5136 e.preventDefault();
5137 fn(e);
5138 }
5139 } )
5140 .bind( 'selectstart.DT', function () {
5141 /* Take the brutal approach to cancelling text selection */
5142 return false;
5143 } );
5144 }
5145
5146
5147 /**
5148 * Register a callback function. Easily allows a callback function to be added to
5149 * an array store of callback functions that can then all be called together.
5150 * @param {object} oSettings dataTables settings object
5151 * @param {string} sStore Name of the array storage for the callbacks in oSettings
5152 * @param {function} fn Function to be called back
5153 * @param {string} sName Identifying name for the callback (i.e. a label)
5154 * @memberof DataTable#oApi
5155 */
5156 function _fnCallbackReg( oSettings, sStore, fn, sName )
5157 {
5158 if ( fn )
5159 {
5160 oSettings[sStore].push( {
5161 "fn": fn,
5162 "sName": sName
5163 } );
5164 }
5165 }
5166
5167
5168 /**
5169 * Fire callback functions and trigger events. Note that the loop over the
5170 * callback array store is done backwards! Further note that you do not want to
5171 * fire off triggers in time sensitive applications (for example cell creation)
5172 * as its slow.
5173 * @param {object} settings dataTables settings object
5174 * @param {string} callbackArr Name of the array storage for the callbacks in
5175 * oSettings
5176 * @param {string} event Name of the jQuery custom event to trigger. If null no
5177 * trigger is fired
5178 * @param {array} args Array of arguments to pass to the callback function /
5179 * trigger
5180 * @memberof DataTable#oApi
5181 */
5182 function _fnCallbackFire( settings, callbackArr, e, args )
5183 {
5184 var ret = [];
5185
5186 if ( callbackArr ) {
5187 ret = $.map( settings[callbackArr].slice().reverse(), function (val, i) {
5188 return val.fn.apply( settings.oInstance, args );
5189 } );
5190 }
5191
5192 if ( e !== null ) {
5193 $(settings.nTable).trigger( e+'.dt', args );
5194 }
5195
5196 return ret;
5197 }
5198
5199
5200 function _fnLengthOverflow ( settings )
5201 {
5202 var
5203 start = settings._iDisplayStart,
5204 end = settings.fnDisplayEnd(),
5205 len = settings._iDisplayLength;
5206
5207 /* If we have space to show extra rows (backing up from the end point - then do so */
5208 if ( start >= end )
5209 {
5210 start = end - len;
5211 }
5212
5213 // Keep the start record on the current page
5214 start -= (start % len);
5215
5216 if ( len === -1 || start < 0 )
5217 {
5218 start = 0;
5219 }
5220
5221 settings._iDisplayStart = start;
5222 }
5223
5224
5225 function _fnRenderer( settings, type )
5226 {
5227 var renderer = settings.renderer;
5228 var host = DataTable.ext.renderer[type];
5229
5230 if ( $.isPlainObject( renderer ) && renderer[type] ) {
5231 // Specific renderer for this type. If available use it, otherwise use
5232 // the default.
5233 return host[renderer[type]] || host._;
5234 }
5235 else if ( typeof renderer === 'string' ) {
5236 // Common renderer - if there is one available for this type use it,
5237 // otherwise use the default
5238 return host[renderer] || host._;
5239 }
5240
5241 // Use the default
5242 return host._;
5243 }
5244
5245
5246 /**
5247 * Detect the data source being used for the table. Used to simplify the code
5248 * a little (ajax) and to make it compress a little smaller.
5249 *
5250 * @param {object} settings dataTables settings object
5251 * @returns {string} Data source
5252 * @memberof DataTable#oApi
5253 */
5254 function _fnDataSource ( settings )
5255 {
5256 if ( settings.oFeatures.bServerSide ) {
5257 return 'ssp';
5258 }
5259 else if ( settings.ajax || settings.sAjaxSource ) {
5260 return 'ajax';
5261 }
5262 return 'dom';
5263 }
5264
5265
5266 DataTable = function( options )
94 var DataTable = function ( options )
5267 95 {
5268 96 /**
5269 97 * Perform a jQuery selector action on the table's TR elements (from the tbody) and
5270 98 * return the resulting jQuery object.
5271 99 * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on
5272 100 * @param {object} [oOpts] Optional parameters for modifying the rows to be included
5273 101 * @param {string} [oOpts.filter=none] Select TR elements that meet the current filter
5274 102 * criterion ("applied") or all TR elements (i.e. no filter).
5275 103 * @param {string} [oOpts.order=current] Order of the TR elements in the processed array.
5276 104 * Can be either 'current', whereby the current sorting of the table is used, or
5277 105 * 'original' whereby the original order the data was read into the table is used.
5278 106 * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page
5279 107 * ("current") or not ("all"). If 'current' is given, then order is assumed to be
5280 108 * 'current' and filter is 'applied', regardless of what they might be given as.
5281 109 * @returns {object} jQuery object, filtered by the given selector.
5282 110 * @dtopt API
5283 111 * @deprecated Since v1.10
5284 112 *
5285 113 * @example
5286 114 * $(document).ready(function() {
5287 115 * var oTable = $('#example').dataTable();
5288 116 *
5289 117 * // Highlight every second row
5290 118 * oTable.$('tr:odd').css('backgroundColor', 'blue');
5291 119 * } );
5292 120 *
5293 121 * @example
5294 122 * $(document).ready(function() {
5295 123 * var oTable = $('#example').dataTable();
5296 124 *
5297 125 * // Filter to rows with 'Webkit' in them, add a background colour and then
5298 126 * // remove the filter, thus highlighting the 'Webkit' rows only.
5299 127 * oTable.fnFilter('Webkit');
5300 128 * oTable.$('tr', {"search": "applied"}).css('backgroundColor', 'blue');
5301 129 * oTable.fnFilter('');
5302 130 * } );
5303 131 */
5304 132 this.$ = function ( sSelector, oOpts )
5305 133 {
5306 134 return this.api(true).$( sSelector, oOpts );
5307 135 };
5308
5309
136
137
5310 138 /**
5311 139 * Almost identical to $ in operation, but in this case returns the data for the matched
5312 140 * rows - as such, the jQuery selector used should match TR row nodes or TD/TH cell nodes
5313 141 * rather than any descendants, so the data can be obtained for the row/cell. If matching
5314 142 * rows are found, the data returned is the original data array/object that was used to
5315 143 * create the row (or a generated array if from a DOM source).
5316 144 *
5317 145 * This method is often useful in-combination with $ where both functions are given the
5318 146 * same parameters and the array indexes will match identically.
5319 147 * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on
5320 148 * @param {object} [oOpts] Optional parameters for modifying the rows to be included
5321 149 * @param {string} [oOpts.filter=none] Select elements that meet the current filter
5322 150 * criterion ("applied") or all elements (i.e. no filter).
5323 151 * @param {string} [oOpts.order=current] Order of the data in the processed array.
5324 152 * Can be either 'current', whereby the current sorting of the table is used, or
5325 153 * 'original' whereby the original order the data was read into the table is used.
5326 154 * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page
5327 155 * ("current") or not ("all"). If 'current' is given, then order is assumed to be
5328 156 * 'current' and filter is 'applied', regardless of what they might be given as.
5329 157 * @returns {array} Data for the matched elements. If any elements, as a result of the
5330 158 * selector, were not TR, TD or TH elements in the DataTable, they will have a null
5331 159 * entry in the array.
5332 160 * @dtopt API
5333 161 * @deprecated Since v1.10
5334 162 *
5335 163 * @example
5336 164 * $(document).ready(function() {
5337 165 * var oTable = $('#example').dataTable();
5338 166 *
5339 167 * // Get the data from the first row in the table
5340 168 * var data = oTable._('tr:first');
5341 169 *
5342 170 * // Do something useful with the data
5343 171 * alert( "First cell is: "+data[0] );
5344 172 * } );
5345 173 *
5346 174 * @example
5347 175 * $(document).ready(function() {
5348 176 * var oTable = $('#example').dataTable();
5349 177 *
5350 178 * // Filter to 'Webkit' and get all data for
5351 179 * oTable.fnFilter('Webkit');
5352 180 * var data = oTable._('tr', {"search": "applied"});
5353 181 *
5354 182 * // Do something with the data
5355 183 * alert( data.length+" rows matched the search" );
5356 184 * } );
5357 185 */
5358 186 this._ = function ( sSelector, oOpts )
5359 187 {
5360 188 return this.api(true).rows( sSelector, oOpts ).data();
5361 189 };
5362
5363
190
191
5364 192 /**
5365 193 * Create a DataTables Api instance, with the currently selected tables for
5366 194 * the Api's context.
5367 195 * @param {boolean} [traditional=false] Set the API instance's context to be
5368 196 * only the table referred to by the `DataTable.ext.iApiIndex` option, as was
5369 197 * used in the API presented by DataTables 1.9- (i.e. the traditional mode),
5370 198 * or if all tables captured in the jQuery object should be used.
5371 199 * @return {DataTables.Api}
5372 200 */
5373 201 this.api = function ( traditional )
5374 202 {
5375 203 return traditional ?
5376 204 new _Api(
5377 205 _fnSettingsFromNode( this[ _ext.iApiIndex ] )
5378 206 ) :
5379 207 new _Api( this );
5380 208 };
5381
5382
209
210
5383 211 /**
5384 212 * Add a single new row or multiple rows of data to the table. Please note
5385 213 * that this is suitable for client-side processing only - if you are using
5386 214 * server-side processing (i.e. "bServerSide": true), then to add data, you
5387 215 * must add it to the data source, i.e. the server-side, through an Ajax call.
5388 216 * @param {array|object} data The data to be added to the table. This can be:
5389 217 * <ul>
5390 218 * <li>1D array of data - add a single row with the data provided</li>
5391 219 * <li>2D array of arrays - add multiple rows in a single call</li>
5392 220 * <li>object - data object when using <i>mData</i></li>
5393 221 * <li>array of objects - multiple data objects when using <i>mData</i></li>
5394 222 * </ul>
5395 223 * @param {bool} [redraw=true] redraw the table or not
5396 224 * @returns {array} An array of integers, representing the list of indexes in
5397 225 * <i>aoData</i> ({@link DataTable.models.oSettings}) that have been added to
5398 226 * the table.
5399 227 * @dtopt API
5400 228 * @deprecated Since v1.10
5401 229 *
5402 230 * @example
5403 231 * // Global var for counter
5404 232 * var giCount = 2;
5405 233 *
5406 234 * $(document).ready(function() {
5407 235 * $('#example').dataTable();
5408 236 * } );
5409 237 *
5410 238 * function fnClickAddRow() {
5411 239 * $('#example').dataTable().fnAddData( [
5412 240 * giCount+".1",
5413 241 * giCount+".2",
5414 242 * giCount+".3",
5415 243 * giCount+".4" ]
5416 244 * );
5417 245 *
5418 246 * giCount++;
5419 247 * }
5420 248 */
5421 249 this.fnAddData = function( data, redraw )
5422 250 {
5423 251 var api = this.api( true );
5424
252
5425 253 /* Check if we want to add multiple rows or not */
5426 254 var rows = $.isArray(data) && ( $.isArray(data[0]) || $.isPlainObject(data[0]) ) ?
5427 255 api.rows.add( data ) :
5428 256 api.row.add( data );
5429
257
5430 258 if ( redraw === undefined || redraw ) {
5431 259 api.draw();
5432 260 }
5433
261
5434 262 return rows.flatten().toArray();
5435 263 };
5436
5437
264
265
5438 266 /**
5439 267 * This function will make DataTables recalculate the column sizes, based on the data
5440 268 * contained in the table and the sizes applied to the columns (in the DOM, CSS or
5441 269 * through the sWidth parameter). This can be useful when the width of the table's
5442 270 * parent element changes (for example a window resize).
5443 271 * @param {boolean} [bRedraw=true] Redraw the table or not, you will typically want to
5444 272 * @dtopt API
5445 273 * @deprecated Since v1.10
5446 274 *
5447 275 * @example
5448 276 * $(document).ready(function() {
5449 277 * var oTable = $('#example').dataTable( {
5450 278 * "sScrollY": "200px",
5451 279 * "bPaginate": false
5452 280 * } );
5453 281 *
5454 * $(window).bind('resize', function () {
282 * $(window).on('resize', function () {
5455 283 * oTable.fnAdjustColumnSizing();
5456 284 * } );
5457 285 * } );
5458 286 */
5459 287 this.fnAdjustColumnSizing = function ( bRedraw )
5460 288 {
5461 289 var api = this.api( true ).columns.adjust();
5462 290 var settings = api.settings()[0];
5463 291 var scroll = settings.oScroll;
5464
292
5465 293 if ( bRedraw === undefined || bRedraw ) {
5466 294 api.draw( false );
5467 295 }
5468 296 else if ( scroll.sX !== "" || scroll.sY !== "" ) {
5469 297 /* If not redrawing, but scrolling, we want to apply the new column sizes anyway */
5470 298 _fnScrollDraw( settings );
5471 299 }
5472 300 };
5473
5474
301
302
5475 303 /**
5476 304 * Quickly and simply clear a table
5477 305 * @param {bool} [bRedraw=true] redraw the table or not
5478 306 * @dtopt API
5479 307 * @deprecated Since v1.10
5480 308 *
5481 309 * @example
5482 310 * $(document).ready(function() {
5483 311 * var oTable = $('#example').dataTable();
5484 312 *
5485 313 * // Immediately 'nuke' the current rows (perhaps waiting for an Ajax callback...)
5486 314 * oTable.fnClearTable();
5487 315 * } );
5488 316 */
5489 317 this.fnClearTable = function( bRedraw )
5490 318 {
5491 319 var api = this.api( true ).clear();
5492
320
5493 321 if ( bRedraw === undefined || bRedraw ) {
5494 322 api.draw();
5495 323 }
5496 324 };
5497
5498
325
326
5499 327 /**
5500 328 * The exact opposite of 'opening' a row, this function will close any rows which
5501 329 * are currently 'open'.
5502 330 * @param {node} nTr the table row to 'close'
5503 331 * @returns {int} 0 on success, or 1 if failed (can't find the row)
5504 332 * @dtopt API
5505 333 * @deprecated Since v1.10
5506 334 *
5507 335 * @example
5508 336 * $(document).ready(function() {
5509 337 * var oTable;
5510 338 *
5511 339 * // 'open' an information row when a row is clicked on
5512 340 * $('#example tbody tr').click( function () {
5513 341 * if ( oTable.fnIsOpen(this) ) {
5514 342 * oTable.fnClose( this );
5515 343 * } else {
5516 344 * oTable.fnOpen( this, "Temporary row opened", "info_row" );
5517 345 * }
5518 346 * } );
5519 347 *
5520 348 * oTable = $('#example').dataTable();
5521 349 * } );
5522 350 */
5523 351 this.fnClose = function( nTr )
5524 352 {
5525 353 this.api( true ).row( nTr ).child.hide();
5526 354 };
5527
5528
355
356
5529 357 /**
5530 358 * Remove a row for the table
5531 359 * @param {mixed} target The index of the row from aoData to be deleted, or
5532 360 * the TR element you want to delete
5533 361 * @param {function|null} [callBack] Callback function
5534 362 * @param {bool} [redraw=true] Redraw the table or not
5535 363 * @returns {array} The row that was deleted
5536 364 * @dtopt API
5537 365 * @deprecated Since v1.10
5538 366 *
5539 367 * @example
5540 368 * $(document).ready(function() {
5541 369 * var oTable = $('#example').dataTable();
5542 370 *
5543 371 * // Immediately remove the first row
5544 372 * oTable.fnDeleteRow( 0 );
5545 373 * } );
5546 374 */
5547 375 this.fnDeleteRow = function( target, callback, redraw )
5548 376 {
5549 377 var api = this.api( true );
5550 378 var rows = api.rows( target );
5551 379 var settings = rows.settings()[0];
5552 380 var data = settings.aoData[ rows[0][0] ];
5553
381
5554 382 rows.remove();
5555
383
5556 384 if ( callback ) {
5557 385 callback.call( this, settings, data );
5558 386 }
5559
387
5560 388 if ( redraw === undefined || redraw ) {
5561 389 api.draw();
5562 390 }
5563
391
5564 392 return data;
5565 393 };
5566
5567
394
395
5568 396 /**
5569 397 * Restore the table to it's original state in the DOM by removing all of DataTables
5570 398 * enhancements, alterations to the DOM structure of the table and event listeners.
5571 399 * @param {boolean} [remove=false] Completely remove the table from the DOM
5572 400 * @dtopt API
5573 401 * @deprecated Since v1.10
5574 402 *
5575 403 * @example
5576 404 * $(document).ready(function() {
5577 405 * // This example is fairly pointless in reality, but shows how fnDestroy can be used
5578 406 * var oTable = $('#example').dataTable();
5579 407 * oTable.fnDestroy();
5580 408 * } );
5581 409 */
5582 410 this.fnDestroy = function ( remove )
5583 411 {
5584 412 this.api( true ).destroy( remove );
5585 413 };
5586
5587
414
415
5588 416 /**
5589 417 * Redraw the table
5590 418 * @param {bool} [complete=true] Re-filter and resort (if enabled) the table before the draw.
5591 419 * @dtopt API
5592 420 * @deprecated Since v1.10
5593 421 *
5594 422 * @example
5595 423 * $(document).ready(function() {
5596 424 * var oTable = $('#example').dataTable();
5597 425 *
5598 426 * // Re-draw the table - you wouldn't want to do it here, but it's an example :-)
5599 427 * oTable.fnDraw();
5600 428 * } );
5601 429 */
5602 430 this.fnDraw = function( complete )
5603 431 {
5604 432 // Note that this isn't an exact match to the old call to _fnDraw - it takes
5605 // into account the new data, but can old position.
5606 this.api( true ).draw( ! complete );
433 // into account the new data, but can hold position.
434 this.api( true ).draw( complete );
5607 435 };
5608
5609
436
437
5610 438 /**
5611 439 * Filter the input based on data
5612 440 * @param {string} sInput String to filter the table on
5613 441 * @param {int|null} [iColumn] Column to limit filtering to
5614 442 * @param {bool} [bRegex=false] Treat as regular expression or not
5615 443 * @param {bool} [bSmart=true] Perform smart filtering or not
5616 444 * @param {bool} [bShowGlobal=true] Show the input global filter in it's input box(es)
5617 445 * @param {bool} [bCaseInsensitive=true] Do case-insensitive matching (true) or not (false)
5618 446 * @dtopt API
5619 447 * @deprecated Since v1.10
5620 448 *
5621 449 * @example
5622 450 * $(document).ready(function() {
5623 451 * var oTable = $('#example').dataTable();
5624 452 *
5625 453 * // Sometime later - filter...
5626 454 * oTable.fnFilter( 'test string' );
5627 455 * } );
5628 456 */
5629 457 this.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal, bCaseInsensitive )
5630 458 {
5631 459 var api = this.api( true );
5632
460
5633 461 if ( iColumn === null || iColumn === undefined ) {
5634 462 api.search( sInput, bRegex, bSmart, bCaseInsensitive );
5635 463 }
5636 464 else {
5637 465 api.column( iColumn ).search( sInput, bRegex, bSmart, bCaseInsensitive );
5638 466 }
5639
467
5640 468 api.draw();
5641 469 };
5642
5643
470
471
5644 472 /**
5645 473 * Get the data for the whole table, an individual row or an individual cell based on the
5646 474 * provided parameters.
5647 475 * @param {int|node} [src] A TR row node, TD/TH cell node or an integer. If given as
5648 476 * a TR node then the data source for the whole row will be returned. If given as a
5649 477 * TD/TH cell node then iCol will be automatically calculated and the data for the
5650 478 * cell returned. If given as an integer, then this is treated as the aoData internal
5651 479 * data index for the row (see fnGetPosition) and the data for that row used.
5652 480 * @param {int} [col] Optional column index that you want the data of.
5653 481 * @returns {array|object|string} If mRow is undefined, then the data for all rows is
5654 482 * returned. If mRow is defined, just data for that row, and is iCol is
5655 483 * defined, only data for the designated cell is returned.
5656 484 * @dtopt API
5657 485 * @deprecated Since v1.10
5658 486 *
5659 487 * @example
5660 488 * // Row data
5661 489 * $(document).ready(function() {
5662 490 * oTable = $('#example').dataTable();
5663 491 *
5664 492 * oTable.$('tr').click( function () {
5665 493 * var data = oTable.fnGetData( this );
5666 494 * // ... do something with the array / object of data for the row
5667 495 * } );
5668 496 * } );
5669 497 *
5670 498 * @example
5671 499 * // Individual cell data
5672 500 * $(document).ready(function() {
5673 501 * oTable = $('#example').dataTable();
5674 502 *
5675 503 * oTable.$('td').click( function () {
5676 504 * var sData = oTable.fnGetData( this );
5677 505 * alert( 'The cell clicked on had the value of '+sData );
5678 506 * } );
5679 507 * } );
5680 508 */
5681 509 this.fnGetData = function( src, col )
5682 510 {
5683 511 var api = this.api( true );
5684
512
5685 513 if ( src !== undefined ) {
5686 514 var type = src.nodeName ? src.nodeName.toLowerCase() : '';
5687
515
5688 516 return col !== undefined || type == 'td' || type == 'th' ?
5689 517 api.cell( src, col ).data() :
5690 518 api.row( src ).data() || null;
5691 519 }
5692
520
5693 521 return api.data().toArray();
5694 522 };
5695
5696
523
524
5697 525 /**
5698 526 * Get an array of the TR nodes that are used in the table's body. Note that you will
5699 527 * typically want to use the '$' API method in preference to this as it is more
5700 528 * flexible.
5701 529 * @param {int} [iRow] Optional row index for the TR element you want
5702 530 * @returns {array|node} If iRow is undefined, returns an array of all TR elements
5703 531 * in the table's body, or iRow is defined, just the TR element requested.
5704 532 * @dtopt API
5705 533 * @deprecated Since v1.10
5706 534 *
5707 535 * @example
5708 536 * $(document).ready(function() {
5709 537 * var oTable = $('#example').dataTable();
5710 538 *
5711 539 * // Get the nodes from the table
5712 540 * var nNodes = oTable.fnGetNodes( );
5713 541 * } );
5714 542 */
5715 543 this.fnGetNodes = function( iRow )
5716 544 {
5717 545 var api = this.api( true );
5718
546
5719 547 return iRow !== undefined ?
5720 548 api.row( iRow ).node() :
5721 549 api.rows().nodes().flatten().toArray();
5722 550 };
5723
5724
551
552
5725 553 /**
5726 554 * Get the array indexes of a particular cell from it's DOM element
5727 555 * and column index including hidden columns
5728 556 * @param {node} node this can either be a TR, TD or TH in the table's body
5729 557 * @returns {int} If nNode is given as a TR, then a single index is returned, or
5730 558 * if given as a cell, an array of [row index, column index (visible),
5731 559 * column index (all)] is given.
5732 560 * @dtopt API
5733 561 * @deprecated Since v1.10
5734 562 *
5735 563 * @example
5736 564 * $(document).ready(function() {
5737 565 * $('#example tbody td').click( function () {
5738 566 * // Get the position of the current data from the node
5739 567 * var aPos = oTable.fnGetPosition( this );
5740 568 *
5741 569 * // Get the data array for this row
5742 570 * var aData = oTable.fnGetData( aPos[0] );
5743 571 *
5744 572 * // Update the data array and return the value
5745 573 * aData[ aPos[1] ] = 'clicked';
5746 574 * this.innerHTML = 'clicked';
5747 575 * } );
5748 576 *
5749 577 * // Init DataTables
5750 578 * oTable = $('#example').dataTable();
5751 579 * } );
5752 580 */
5753 581 this.fnGetPosition = function( node )
5754 582 {
5755 583 var api = this.api( true );
5756 584 var nodeName = node.nodeName.toUpperCase();
5757
585
5758 586 if ( nodeName == 'TR' ) {
5759 587 return api.row( node ).index();
5760 588 }
5761 589 else if ( nodeName == 'TD' || nodeName == 'TH' ) {
5762 590 var cell = api.cell( node ).index();
5763
591
5764 592 return [
5765 593 cell.row,
5766 594 cell.columnVisible,
5767 595 cell.column
5768 596 ];
5769 597 }
5770 598 return null;
5771 599 };
5772
5773
600
601
5774 602 /**
5775 603 * Check to see if a row is 'open' or not.
5776 604 * @param {node} nTr the table row to check
5777 605 * @returns {boolean} true if the row is currently open, false otherwise
5778 606 * @dtopt API
5779 607 * @deprecated Since v1.10
5780 608 *
5781 609 * @example
5782 610 * $(document).ready(function() {
5783 611 * var oTable;
5784 612 *
5785 613 * // 'open' an information row when a row is clicked on
5786 614 * $('#example tbody tr').click( function () {
5787 615 * if ( oTable.fnIsOpen(this) ) {
5788 616 * oTable.fnClose( this );
5789 617 * } else {
5790 618 * oTable.fnOpen( this, "Temporary row opened", "info_row" );
5791 619 * }
5792 620 * } );
5793 621 *
5794 622 * oTable = $('#example').dataTable();
5795 623 * } );
5796 624 */
5797 625 this.fnIsOpen = function( nTr )
5798 626 {
5799 627 return this.api( true ).row( nTr ).child.isShown();
5800 628 };
5801
5802
629
630
5803 631 /**
5804 632 * This function will place a new row directly after a row which is currently
5805 633 * on display on the page, with the HTML contents that is passed into the
5806 634 * function. This can be used, for example, to ask for confirmation that a
5807 635 * particular record should be deleted.
5808 636 * @param {node} nTr The table row to 'open'
5809 637 * @param {string|node|jQuery} mHtml The HTML to put into the row
5810 638 * @param {string} sClass Class to give the new TD cell
5811 639 * @returns {node} The row opened. Note that if the table row passed in as the
5812 640 * first parameter, is not found in the table, this method will silently
5813 641 * return.
5814 642 * @dtopt API
5815 643 * @deprecated Since v1.10
5816 644 *
5817 645 * @example
5818 646 * $(document).ready(function() {
5819 647 * var oTable;
5820 648 *
5821 649 * // 'open' an information row when a row is clicked on
5822 650 * $('#example tbody tr').click( function () {
5823 651 * if ( oTable.fnIsOpen(this) ) {
5824 652 * oTable.fnClose( this );
5825 653 * } else {
5826 654 * oTable.fnOpen( this, "Temporary row opened", "info_row" );
5827 655 * }
5828 656 * } );
5829 657 *
5830 658 * oTable = $('#example').dataTable();
5831 659 * } );
5832 660 */
5833 661 this.fnOpen = function( nTr, mHtml, sClass )
5834 662 {
5835 663 return this.api( true )
5836 664 .row( nTr )
5837 665 .child( mHtml, sClass )
5838 666 .show()
5839 667 .child()[0];
5840 668 };
5841
5842
669
670
5843 671 /**
5844 672 * Change the pagination - provides the internal logic for pagination in a simple API
5845 673 * function. With this function you can have a DataTables table go to the next,
5846 674 * previous, first or last pages.
5847 675 * @param {string|int} mAction Paging action to take: "first", "previous", "next" or "last"
5848 676 * or page number to jump to (integer), note that page 0 is the first page.
5849 677 * @param {bool} [bRedraw=true] Redraw the table or not
5850 678 * @dtopt API
5851 679 * @deprecated Since v1.10
5852 680 *
5853 681 * @example
5854 682 * $(document).ready(function() {
5855 683 * var oTable = $('#example').dataTable();
5856 684 * oTable.fnPageChange( 'next' );
5857 685 * } );
5858 686 */
5859 687 this.fnPageChange = function ( mAction, bRedraw )
5860 688 {
5861 689 var api = this.api( true ).page( mAction );
5862
690
5863 691 if ( bRedraw === undefined || bRedraw ) {
5864 692 api.draw(false);
5865 693 }
5866 694 };
5867
5868
695
696
5869 697 /**
5870 698 * Show a particular column
5871 699 * @param {int} iCol The column whose display should be changed
5872 700 * @param {bool} bShow Show (true) or hide (false) the column
5873 701 * @param {bool} [bRedraw=true] Redraw the table or not
5874 702 * @dtopt API
5875 703 * @deprecated Since v1.10
5876 704 *
5877 705 * @example
5878 706 * $(document).ready(function() {
5879 707 * var oTable = $('#example').dataTable();
5880 708 *
5881 709 * // Hide the second column after initialisation
5882 710 * oTable.fnSetColumnVis( 1, false );
5883 711 * } );
5884 712 */
5885 713 this.fnSetColumnVis = function ( iCol, bShow, bRedraw )
5886 714 {
5887 715 var api = this.api( true ).column( iCol ).visible( bShow );
5888
716
5889 717 if ( bRedraw === undefined || bRedraw ) {
5890 718 api.columns.adjust().draw();
5891 719 }
5892 720 };
5893
5894
721
722
5895 723 /**
5896 724 * Get the settings for a particular table for external manipulation
5897 725 * @returns {object} DataTables settings object. See
5898 726 * {@link DataTable.models.oSettings}
5899 727 * @dtopt API
5900 728 * @deprecated Since v1.10
5901 729 *
5902 730 * @example
5903 731 * $(document).ready(function() {
5904 732 * var oTable = $('#example').dataTable();
5905 733 * var oSettings = oTable.fnSettings();
5906 734 *
5907 735 * // Show an example parameter from the settings
5908 736 * alert( oSettings._iDisplayStart );
5909 737 * } );
5910 738 */
5911 739 this.fnSettings = function()
5912 740 {
5913 741 return _fnSettingsFromNode( this[_ext.iApiIndex] );
5914 742 };
5915
5916
743
744
5917 745 /**
5918 746 * Sort the table by a particular column
5919 747 * @param {int} iCol the data index to sort on. Note that this will not match the
5920 748 * 'display index' if you have hidden data entries
5921 749 * @dtopt API
5922 750 * @deprecated Since v1.10
5923 751 *
5924 752 * @example
5925 753 * $(document).ready(function() {
5926 754 * var oTable = $('#example').dataTable();
5927 755 *
5928 756 * // Sort immediately with columns 0 and 1
5929 757 * oTable.fnSort( [ [0,'asc'], [1,'asc'] ] );
5930 758 * } );
5931 759 */
5932 760 this.fnSort = function( aaSort )
5933 761 {
5934 762 this.api( true ).order( aaSort ).draw();
5935 763 };
5936
5937
764
765
5938 766 /**
5939 767 * Attach a sort listener to an element for a given column
5940 768 * @param {node} nNode the element to attach the sort listener to
5941 769 * @param {int} iColumn the column that a click on this node will sort on
5942 770 * @param {function} [fnCallback] callback function when sort is run
5943 771 * @dtopt API
5944 772 * @deprecated Since v1.10
5945 773 *
5946 774 * @example
5947 775 * $(document).ready(function() {
5948 776 * var oTable = $('#example').dataTable();
5949 777 *
5950 778 * // Sort on column 1, when 'sorter' is clicked on
5951 779 * oTable.fnSortListener( document.getElementById('sorter'), 1 );
5952 780 * } );
5953 781 */
5954 782 this.fnSortListener = function( nNode, iColumn, fnCallback )
5955 783 {
5956 784 this.api( true ).order.listener( nNode, iColumn, fnCallback );
5957 785 };
5958
5959
786
787
5960 788 /**
5961 789 * Update a table cell or row - this method will accept either a single value to
5962 790 * update the cell with, an array of values with one element for each column or
5963 791 * an object in the same format as the original data source. The function is
5964 792 * self-referencing in order to make the multi column updates easier.
5965 793 * @param {object|array|string} mData Data to update the cell/row with
5966 794 * @param {node|int} mRow TR element you want to update or the aoData index
5967 795 * @param {int} [iColumn] The column to update, give as null or undefined to
5968 796 * update a whole row.
5969 797 * @param {bool} [bRedraw=true] Redraw the table or not
5970 798 * @param {bool} [bAction=true] Perform pre-draw actions or not
5971 799 * @returns {int} 0 on success, 1 on error
5972 800 * @dtopt API
5973 801 * @deprecated Since v1.10
5974 802 *
5975 803 * @example
5976 804 * $(document).ready(function() {
5977 805 * var oTable = $('#example').dataTable();
5978 806 * oTable.fnUpdate( 'Example update', 0, 0 ); // Single cell
5979 807 * oTable.fnUpdate( ['a', 'b', 'c', 'd', 'e'], $('tbody tr')[0] ); // Row
5980 808 * } );
5981 809 */
5982 810 this.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction )
5983 811 {
5984 812 var api = this.api( true );
5985
813
5986 814 if ( iColumn === undefined || iColumn === null ) {
5987 815 api.row( mRow ).data( mData );
5988 816 }
5989 817 else {
5990 818 api.cell( mRow, iColumn ).data( mData );
5991 819 }
5992
820
5993 821 if ( bAction === undefined || bAction ) {
5994 822 api.columns.adjust();
5995 823 }
5996
824
5997 825 if ( bRedraw === undefined || bRedraw ) {
5998 826 api.draw();
5999 827 }
6000 828 return 0;
6001 829 };
6002
6003
830
831
6004 832 /**
6005 833 * Provide a common method for plug-ins to check the version of DataTables being used, in order
6006 834 * to ensure compatibility.
6007 835 * @param {string} sVersion Version string to check for, in the format "X.Y.Z". Note that the
6008 836 * formats "X" and "X.Y" are also acceptable.
6009 837 * @returns {boolean} true if this version of DataTables is greater or equal to the required
6010 838 * version, or false if this version of DataTales is not suitable
6011 839 * @method
6012 840 * @dtopt API
6013 841 * @deprecated Since v1.10
6014 842 *
6015 843 * @example
6016 844 * $(document).ready(function() {
6017 845 * var oTable = $('#example').dataTable();
6018 846 * alert( oTable.fnVersionCheck( '1.9.0' ) );
6019 847 * } );
6020 848 */
6021 849 this.fnVersionCheck = _ext.fnVersionCheck;
6022
850
6023 851
6024 852 var _that = this;
6025 853 var emptyInit = options === undefined;
6026 854 var len = this.length;
6027 855
6028 856 if ( emptyInit ) {
6029 857 options = {};
6030 858 }
6031 859
6032 860 this.oApi = this.internal = _ext.internal;
6033 861
6034 862 // Extend with old style plug-in API methods
6035 863 for ( var fn in DataTable.ext.internal ) {
6036 864 if ( fn ) {
6037 865 this[fn] = _fnExternApiFunc(fn);
6038 866 }
6039 867 }
6040 868
6041 869 this.each(function() {
6042 870 // For each initialisation we want to give it a clean initialisation
6043 871 // object that can be bashed around
6044 872 var o = {};
6045 873 var oInit = len > 1 ? // optimisation for single table case
6046 874 _fnExtend( o, options, true ) :
6047 875 options;
6048 876
6049 877 /*global oInit,_that,emptyInit*/
6050 878 var i=0, iLen, j, jLen, k, kLen;
6051 879 var sId = this.getAttribute( 'id' );
6052 880 var bInitHandedOff = false;
6053 881 var defaults = DataTable.defaults;
6054
6055
882 var $this = $(this);
883
884
6056 885 /* Sanity check */
6057 886 if ( this.nodeName.toLowerCase() != 'table' )
6058 887 {
6059 888 _fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 );
6060 889 return;
6061 890 }
6062
891
6063 892 /* Backwards compatibility for the defaults */
6064 893 _fnCompatOpts( defaults );
6065 894 _fnCompatCols( defaults.column );
6066
895
6067 896 /* Convert the camel-case defaults to Hungarian */
6068 897 _fnCamelToHungarian( defaults, defaults, true );
6069 898 _fnCamelToHungarian( defaults.column, defaults.column, true );
6070
899
6071 900 /* Setting up the initialisation object */
6072 _fnCamelToHungarian( defaults, oInit );
6073
901 _fnCamelToHungarian( defaults, $.extend( oInit, $this.data() ) );
902
903
904
6074 905 /* Check to see if we are re-initialising a table */
6075 906 var allSettings = DataTable.settings;
6076 907 for ( i=0, iLen=allSettings.length ; i<iLen ; i++ )
6077 908 {
909 var s = allSettings[i];
910
6078 911 /* Base check on table node */
6079 if ( allSettings[i].nTable == this )
912 if ( s.nTable == this || s.nTHead.parentNode == this || (s.nTFoot && s.nTFoot.parentNode == this) )
6080 913 {
6081 914 var bRetrieve = oInit.bRetrieve !== undefined ? oInit.bRetrieve : defaults.bRetrieve;
6082 915 var bDestroy = oInit.bDestroy !== undefined ? oInit.bDestroy : defaults.bDestroy;
6083
916
6084 917 if ( emptyInit || bRetrieve )
6085 918 {
6086 return allSettings[i].oInstance;
919 return s.oInstance;
6087 920 }
6088 921 else if ( bDestroy )
6089 922 {
6090 allSettings[i].oInstance.fnDestroy();
923 s.oInstance.fnDestroy();
6091 924 break;
6092 925 }
6093 926 else
6094 927 {
6095 _fnLog( allSettings[i], 0, 'Cannot reinitialise DataTable', 3 );
928 _fnLog( s, 0, 'Cannot reinitialise DataTable', 3 );
6096 929 return;
6097 930 }
6098 931 }
6099
932
6100 933 /* If the element we are initialising has the same ID as a table which was previously
6101 934 * initialised, but the table nodes don't match (from before) then we destroy the old
6102 935 * instance by simply deleting it. This is under the assumption that the table has been
6103 936 * destroyed by other methods. Anyone using non-id selectors will need to do this manually
6104 937 */
6105 if ( allSettings[i].sTableId == this.id )
938 if ( s.sTableId == this.id )
6106 939 {
6107 940 allSettings.splice( i, 1 );
6108 941 break;
6109 942 }
6110 943 }
6111
944
6112 945 /* Ensure the table has an ID - required for accessibility */
6113 946 if ( sId === null || sId === "" )
6114 947 {
6115 948 sId = "DataTables_Table_"+(DataTable.ext._unique++);
6116 949 this.id = sId;
6117 950 }
6118
951
6119 952 /* Create the settings object for this table and set some of the default parameters */
6120 953 var oSettings = $.extend( true, {}, DataTable.models.oSettings, {
6121 "nTable": this,
6122 "oApi": _that.internal,
6123 "oInit": oInit,
6124 "sDestroyWidth": $(this)[0].style.width,
954 "sDestroyWidth": $this[0].style.width,
6125 955 "sInstance": sId,
6126 956 "sTableId": sId
6127 957 } );
958 oSettings.nTable = this;
959 oSettings.oApi = _that.internal;
960 oSettings.oInit = oInit;
961
6128 962 allSettings.push( oSettings );
6129
963
6130 964 // Need to add the instance after the instance after the settings object has been added
6131 965 // to the settings array, so we can self reference the table instance if more than one
6132 oSettings.oInstance = (_that.length===1) ? _that : $(this).dataTable();
6133
966 oSettings.oInstance = (_that.length===1) ? _that : $this.dataTable();
967
6134 968 // Backwards compatibility, before we apply all the defaults
6135 969 _fnCompatOpts( oInit );
6136
970
6137 971 if ( oInit.oLanguage )
6138 972 {
6139 973 _fnLanguageCompat( oInit.oLanguage );
6140 974 }
6141
975
6142 976 // If the length menu is given, but the init display length is not, use the length menu
6143 977 if ( oInit.aLengthMenu && ! oInit.iDisplayLength )
6144 978 {
6145 979 oInit.iDisplayLength = $.isArray( oInit.aLengthMenu[0] ) ?
6146 980 oInit.aLengthMenu[0][0] : oInit.aLengthMenu[0];
6147 981 }
6148
982
6149 983 // Apply the defaults and init options to make a single init object will all
6150 984 // options defined from defaults and instance options.
6151 985 oInit = _fnExtend( $.extend( true, {}, defaults ), oInit );
6152
6153
986
987
6154 988 // Map the initialisation options onto the settings object
6155 989 _fnMap( oSettings.oFeatures, oInit, [
6156 990 "bPaginate",
6157 991 "bLengthChange",
6158 992 "bFilter",
6159 993 "bSort",
6160 994 "bSortMulti",
6161 995 "bInfo",
6162 996 "bProcessing",
6163 997 "bAutoWidth",
6164 998 "bSortClasses",
6165 999 "bServerSide",
6166 1000 "bDeferRender"
6167 1001 ] );
6168 1002 _fnMap( oSettings, oInit, [
6169 1003 "asStripeClasses",
6170 1004 "ajax",
6171 1005 "fnServerData",
6172 1006 "fnFormatNumber",
6173 1007 "sServerMethod",
6174 1008 "aaSorting",
6175 1009 "aaSortingFixed",
6176 1010 "aLengthMenu",
6177 1011 "sPaginationType",
6178 1012 "sAjaxSource",
6179 1013 "sAjaxDataProp",
6180 1014 "iStateDuration",
6181 1015 "sDom",
6182 1016 "bSortCellsTop",
6183 1017 "iTabIndex",
6184 1018 "fnStateLoadCallback",
6185 1019 "fnStateSaveCallback",
6186 1020 "renderer",
6187 1021 "searchDelay",
1022 "rowId",
6188 1023 [ "iCookieDuration", "iStateDuration" ], // backwards compat
6189 1024 [ "oSearch", "oPreviousSearch" ],
6190 1025 [ "aoSearchCols", "aoPreSearchCols" ],
6191 1026 [ "iDisplayLength", "_iDisplayLength" ],
6192 1027 [ "bJQueryUI", "bJUI" ]
6193 1028 ] );
6194 1029 _fnMap( oSettings.oScroll, oInit, [
6195 1030 [ "sScrollX", "sX" ],
6196 1031 [ "sScrollXInner", "sXInner" ],
6197 1032 [ "sScrollY", "sY" ],
6198 1033 [ "bScrollCollapse", "bCollapse" ]
6199 1034 ] );
6200 1035 _fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" );
6201
1036
6202 1037 /* Callback functions which are array driven */
6203 1038 _fnCallbackReg( oSettings, 'aoDrawCallback', oInit.fnDrawCallback, 'user' );
6204 1039 _fnCallbackReg( oSettings, 'aoServerParams', oInit.fnServerParams, 'user' );
6205 1040 _fnCallbackReg( oSettings, 'aoStateSaveParams', oInit.fnStateSaveParams, 'user' );
6206 1041 _fnCallbackReg( oSettings, 'aoStateLoadParams', oInit.fnStateLoadParams, 'user' );
6207 1042 _fnCallbackReg( oSettings, 'aoStateLoaded', oInit.fnStateLoaded, 'user' );
6208 1043 _fnCallbackReg( oSettings, 'aoRowCallback', oInit.fnRowCallback, 'user' );
6209 1044 _fnCallbackReg( oSettings, 'aoRowCreatedCallback', oInit.fnCreatedRow, 'user' );
6210 1045 _fnCallbackReg( oSettings, 'aoHeaderCallback', oInit.fnHeaderCallback, 'user' );
6211 1046 _fnCallbackReg( oSettings, 'aoFooterCallback', oInit.fnFooterCallback, 'user' );
6212 1047 _fnCallbackReg( oSettings, 'aoInitComplete', oInit.fnInitComplete, 'user' );
6213 1048 _fnCallbackReg( oSettings, 'aoPreDrawCallback', oInit.fnPreDrawCallback, 'user' );
6214
1049
1050 oSettings.rowIdFn = _fnGetObjectDataFn( oInit.rowId );
1051
1052 /* Browser support detection */
1053 _fnBrowserDetect( oSettings );
1054
6215 1055 var oClasses = oSettings.oClasses;
6216
1056
6217 1057 // @todo Remove in 1.11
6218 1058 if ( oInit.bJQueryUI )
6219 1059 {
6220 1060 /* Use the JUI classes object for display. You could clone the oStdClasses object if
6221 1061 * you want to have multiple tables with multiple independent classes
6222 1062 */
6223 1063 $.extend( oClasses, DataTable.ext.oJUIClasses, oInit.oClasses );
6224
1064
6225 1065 if ( oInit.sDom === defaults.sDom && defaults.sDom === "lfrtip" )
6226 1066 {
6227 1067 /* Set the DOM to use a layout suitable for jQuery UI's theming */
6228 1068 oSettings.sDom = '<"H"lfr>t<"F"ip>';
6229 1069 }
6230
1070
6231 1071 if ( ! oSettings.renderer ) {
6232 1072 oSettings.renderer = 'jqueryui';
6233 1073 }
6234 1074 else if ( $.isPlainObject( oSettings.renderer ) && ! oSettings.renderer.header ) {
6235 1075 oSettings.renderer.header = 'jqueryui';
6236 1076 }
6237 1077 }
6238 1078 else
6239 1079 {
6240 1080 $.extend( oClasses, DataTable.ext.classes, oInit.oClasses );
6241 1081 }
6242 $(this).addClass( oClasses.sTable );
6243
6244 /* Calculate the scroll bar width and cache it for use later on */
6245 if ( oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "" )
6246 {
6247 oSettings.oScroll.iBarWidth = _fnScrollBarWidth();
6248 }
6249 if ( oSettings.oScroll.sX === true ) { // Easy initialisation of x-scrolling
6250 oSettings.oScroll.sX = '100%';
6251 }
6252
1082 $this.addClass( oClasses.sTable );
1083
1084
6253 1085 if ( oSettings.iInitDisplayStart === undefined )
6254 1086 {
6255 1087 /* Display start point, taking into account the save saving */
6256 1088 oSettings.iInitDisplayStart = oInit.iDisplayStart;
6257 1089 oSettings._iDisplayStart = oInit.iDisplayStart;
6258 1090 }
6259
1091
6260 1092 if ( oInit.iDeferLoading !== null )
6261 1093 {
6262 1094 oSettings.bDeferLoading = true;
6263 1095 var tmp = $.isArray( oInit.iDeferLoading );
6264 1096 oSettings._iRecordsDisplay = tmp ? oInit.iDeferLoading[0] : oInit.iDeferLoading;
6265 1097 oSettings._iRecordsTotal = tmp ? oInit.iDeferLoading[1] : oInit.iDeferLoading;
6266 1098 }
6267
1099
6268 1100 /* Language definitions */
6269 1101 var oLanguage = oSettings.oLanguage;
6270 1102 $.extend( true, oLanguage, oInit.oLanguage );
6271
6272 if ( oLanguage.sUrl !== "" )
1103
1104 if ( oLanguage.sUrl )
6273 1105 {
6274 1106 /* Get the language definitions from a file - because this Ajax call makes the language
6275 1107 * get async to the remainder of this function we use bInitHandedOff to indicate that
6276 1108 * _fnInitialise will be fired by the returned Ajax handler, rather than the constructor
6277 1109 */
6278 1110 $.ajax( {
6279 1111 dataType: 'json',
6280 1112 url: oLanguage.sUrl,
6281 1113 success: function ( json ) {
6282 1114 _fnLanguageCompat( json );
6283 1115 _fnCamelToHungarian( defaults.oLanguage, json );
6284 1116 $.extend( true, oLanguage, json );
6285 1117 _fnInitialise( oSettings );
6286 1118 },
6287 1119 error: function () {
6288 1120 // Error occurred loading language file, continue on as best we can
6289 1121 _fnInitialise( oSettings );
6290 1122 }
6291 1123 } );
6292 1124 bInitHandedOff = true;
6293 1125 }
6294
1126
6295 1127 /*
6296 1128 * Stripes
6297 1129 */
6298 1130 if ( oInit.asStripeClasses === null )
6299 1131 {
6300 1132 oSettings.asStripeClasses =[
6301 1133 oClasses.sStripeOdd,
6302 1134 oClasses.sStripeEven
6303 1135 ];
6304 1136 }
6305
1137
6306 1138 /* Remove row stripe classes if they are already on the table row */
6307 1139 var stripeClasses = oSettings.asStripeClasses;
6308 var rowOne = $('tbody tr:eq(0)', this);
1140 var rowOne = $this.children('tbody').find('tr').eq(0);
6309 1141 if ( $.inArray( true, $.map( stripeClasses, function(el, i) {
6310 1142 return rowOne.hasClass(el);
6311 1143 } ) ) !== -1 ) {
6312 1144 $('tbody tr', this).removeClass( stripeClasses.join(' ') );
6313 1145 oSettings.asDestroyStripes = stripeClasses.slice();
6314 1146 }
6315
1147
6316 1148 /*
6317 1149 * Columns
6318 1150 * See if we should load columns automatically or use defined ones
6319 1151 */
6320 1152 var anThs = [];
6321 1153 var aoColumnsInit;
6322 1154 var nThead = this.getElementsByTagName('thead');
6323 1155 if ( nThead.length !== 0 )
6324 1156 {
6325 1157 _fnDetectHeader( oSettings.aoHeader, nThead[0] );
6326 1158 anThs = _fnGetUniqueThs( oSettings );
6327 1159 }
6328
1160
6329 1161 /* If not given a column array, generate one with nulls */
6330 1162 if ( oInit.aoColumns === null )
6331 1163 {
6332 1164 aoColumnsInit = [];
6333 1165 for ( i=0, iLen=anThs.length ; i<iLen ; i++ )
6334 1166 {
6335 1167 aoColumnsInit.push( null );
6336 1168 }
6337 1169 }
6338 1170 else
6339 1171 {
6340 1172 aoColumnsInit = oInit.aoColumns;
6341 1173 }
6342
1174
6343 1175 /* Add the columns */
6344 1176 for ( i=0, iLen=aoColumnsInit.length ; i<iLen ; i++ )
6345 1177 {
6346 1178 _fnAddColumn( oSettings, anThs ? anThs[i] : null );
6347 1179 }
6348
1180
6349 1181 /* Apply the column definitions */
6350 1182 _fnApplyColumnDefs( oSettings, oInit.aoColumnDefs, aoColumnsInit, function (iCol, oDef) {
6351 1183 _fnColumnOptions( oSettings, iCol, oDef );
6352 1184 } );
6353
1185
6354 1186 /* HTML5 attribute detection - build an mData object automatically if the
6355 1187 * attributes are found
6356 1188 */
6357 1189 if ( rowOne.length ) {
6358 1190 var a = function ( cell, name ) {
6359 return cell.getAttribute( 'data-'+name ) ? name : null;
1191 return cell.getAttribute( 'data-'+name ) !== null ? name : null;
6360 1192 };
6361
6362 $.each( _fnGetRowElements( oSettings, rowOne[0] ).cells, function (i, cell) {
1193
1194 $( rowOne[0] ).children('th, td').each( function (i, cell) {
6363 1195 var col = oSettings.aoColumns[i];
6364
1196
6365 1197 if ( col.mData === i ) {
6366 1198 var sort = a( cell, 'sort' ) || a( cell, 'order' );
6367 1199 var filter = a( cell, 'filter' ) || a( cell, 'search' );
6368
1200
6369 1201 if ( sort !== null || filter !== null ) {
6370 1202 col.mData = {
6371 1203 _: i+'.display',
6372 1204 sort: sort !== null ? i+'.@data-'+sort : undefined,
6373 1205 type: sort !== null ? i+'.@data-'+sort : undefined,
6374 1206 filter: filter !== null ? i+'.@data-'+filter : undefined
6375 1207 };
6376
1208
6377 1209 _fnColumnOptions( oSettings, i );
6378 1210 }
6379 1211 }
6380 1212 } );
6381 1213 }
6382
1214
6383 1215 var features = oSettings.oFeatures;
6384
1216 var loadedInit = function () {
1217 /*
1218 * Sorting
1219 * @todo For modularisation (1.11) this needs to do into a sort start up handler
1220 */
1221
1222 // If aaSorting is not defined, then we use the first indicator in asSorting
1223 // in case that has been altered, so the default sort reflects that option
1224 if ( oInit.aaSorting === undefined ) {
1225 var sorting = oSettings.aaSorting;
1226 for ( i=0, iLen=sorting.length ; i<iLen ; i++ ) {
1227 sorting[i][1] = oSettings.aoColumns[ i ].asSorting[0];
1228 }
1229 }
1230
1231 /* Do a first pass on the sorting classes (allows any size changes to be taken into
1232 * account, and also will apply sorting disabled classes if disabled
1233 */
1234 _fnSortingClasses( oSettings );
1235
1236 if ( features.bSort ) {
1237 _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
1238 if ( oSettings.bSorted ) {
1239 var aSort = _fnSortFlatten( oSettings );
1240 var sortedColumns = {};
1241
1242 $.each( aSort, function (i, val) {
1243 sortedColumns[ val.src ] = val.dir;
1244 } );
1245
1246 _fnCallbackFire( oSettings, null, 'order', [oSettings, aSort, sortedColumns] );
1247 _fnSortAria( oSettings );
1248 }
1249 } );
1250 }
1251
1252 _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
1253 if ( oSettings.bSorted || _fnDataSource( oSettings ) === 'ssp' || features.bDeferRender ) {
1254 _fnSortingClasses( oSettings );
1255 }
1256 }, 'sc' );
1257
1258
1259 /*
1260 * Final init
1261 * Cache the header, body and footer as required, creating them if needed
1262 */
1263
1264 // Work around for Webkit bug 83867 - store the caption-side before removing from doc
1265 var captions = $this.children('caption').each( function () {
1266 this._captionSide = $(this).css('caption-side');
1267 } );
1268
1269 var thead = $this.children('thead');
1270 if ( thead.length === 0 ) {
1271 thead = $('<thead/>').appendTo($this);
1272 }
1273 oSettings.nTHead = thead[0];
1274
1275 var tbody = $this.children('tbody');
1276 if ( tbody.length === 0 ) {
1277 tbody = $('<tbody/>').appendTo($this);
1278 }
1279 oSettings.nTBody = tbody[0];
1280
1281 var tfoot = $this.children('tfoot');
1282 if ( tfoot.length === 0 && captions.length > 0 && (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") ) {
1283 // If we are a scrolling table, and no footer has been given, then we need to create
1284 // a tfoot element for the caption element to be appended to
1285 tfoot = $('<tfoot/>').appendTo($this);
1286 }
1287
1288 if ( tfoot.length === 0 || tfoot.children().length === 0 ) {
1289 $this.addClass( oClasses.sNoFooter );
1290 }
1291 else if ( tfoot.length > 0 ) {
1292 oSettings.nTFoot = tfoot[0];
1293 _fnDetectHeader( oSettings.aoFooter, oSettings.nTFoot );
1294 }
1295
1296 /* Check if there is data passing into the constructor */
1297 if ( oInit.aaData ) {
1298 for ( i=0 ; i<oInit.aaData.length ; i++ ) {
1299 _fnAddData( oSettings, oInit.aaData[ i ] );
1300 }
1301 }
1302 else if ( oSettings.bDeferLoading || _fnDataSource( oSettings ) == 'dom' ) {
1303 /* Grab the data from the page - only do this when deferred loading or no Ajax
1304 * source since there is no point in reading the DOM data if we are then going
1305 * to replace it with Ajax data
1306 */
1307 _fnAddTr( oSettings, $(oSettings.nTBody).children('tr') );
1308 }
1309
1310 /* Copy the data index array */
1311 oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
1312
1313 /* Initialisation complete - table can be drawn */
1314 oSettings.bInitialised = true;
1315
1316 /* Check if we need to initialise the table (it might not have been handed off to the
1317 * language processor)
1318 */
1319 if ( bInitHandedOff === false ) {
1320 _fnInitialise( oSettings );
1321 }
1322 };
1323
6385 1324 /* Must be done after everything which can be overridden by the state saving! */
6386 1325 if ( oInit.bStateSave )
6387 1326 {
6388 1327 features.bStateSave = true;
6389 _fnLoadState( oSettings, oInit );
6390 1328 _fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState, 'state_save' );
6391 }
6392
6393
6394 /*
6395 * Sorting
6396 * @todo For modularisation (1.11) this needs to do into a sort start up handler
6397 */
6398
6399 // If aaSorting is not defined, then we use the first indicator in asSorting
6400 // in case that has been altered, so the default sort reflects that option
6401 if ( oInit.aaSorting === undefined )
6402 {
6403 var sorting = oSettings.aaSorting;
6404 for ( i=0, iLen=sorting.length ; i<iLen ; i++ )
6405 {
6406 sorting[i][1] = oSettings.aoColumns[ i ].asSorting[0];
6407 }
6408 }
6409
6410 /* Do a first pass on the sorting classes (allows any size changes to be taken into
6411 * account, and also will apply sorting disabled classes if disabled
6412 */
6413 _fnSortingClasses( oSettings );
6414
6415 if ( features.bSort )
6416 {
6417 _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
6418 if ( oSettings.bSorted ) {
6419 var aSort = _fnSortFlatten( oSettings );
6420 var sortedColumns = {};
6421
6422 $.each( aSort, function (i, val) {
6423 sortedColumns[ val.src ] = val.dir;
6424 } );
6425
6426 _fnCallbackFire( oSettings, null, 'order', [oSettings, aSort, sortedColumns] );
6427 _fnSortAria( oSettings );
6428 }
6429 } );
6430 }
6431
6432 _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
6433 if ( oSettings.bSorted || _fnDataSource( oSettings ) === 'ssp' || features.bDeferRender ) {
6434 _fnSortingClasses( oSettings );
6435 }
6436 }, 'sc' );
6437
6438
6439 /*
6440 * Final init
6441 * Cache the header, body and footer as required, creating them if needed
6442 */
6443
6444 /* Browser support detection */
6445 _fnBrowserDetect( oSettings );
6446
6447 // Work around for Webkit bug 83867 - store the caption-side before removing from doc
6448 var captions = $(this).children('caption').each( function () {
6449 this._captionSide = $(this).css('caption-side');
6450 } );
6451
6452 var thead = $(this).children('thead');
6453 if ( thead.length === 0 )
6454 {
6455 thead = $('<thead/>').appendTo(this);
6456 }
6457 oSettings.nTHead = thead[0];
6458
6459 var tbody = $(this).children('tbody');
6460 if ( tbody.length === 0 )
6461 {
6462 tbody = $('<tbody/>').appendTo(this);
6463 }
6464 oSettings.nTBody = tbody[0];
6465
6466 var tfoot = $(this).children('tfoot');
6467 if ( tfoot.length === 0 && captions.length > 0 && (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") )
6468 {
6469 // If we are a scrolling table, and no footer has been given, then we need to create
6470 // a tfoot element for the caption element to be appended to
6471 tfoot = $('<tfoot/>').appendTo(this);
6472 }
6473
6474 if ( tfoot.length === 0 || tfoot.children().length === 0 ) {
6475 $(this).addClass( oClasses.sNoFooter );
6476 }
6477 else if ( tfoot.length > 0 ) {
6478 oSettings.nTFoot = tfoot[0];
6479 _fnDetectHeader( oSettings.aoFooter, oSettings.nTFoot );
6480 }
6481
6482 /* Check if there is data passing into the constructor */
6483 if ( oInit.aaData )
6484 {
6485 for ( i=0 ; i<oInit.aaData.length ; i++ )
6486 {
6487 _fnAddData( oSettings, oInit.aaData[ i ] );
6488 }
6489 }
6490 else if ( oSettings.bDeferLoading || _fnDataSource( oSettings ) == 'dom' )
6491 {
6492 /* Grab the data from the page - only do this when deferred loading or no Ajax
6493 * source since there is no point in reading the DOM data if we are then going
6494 * to replace it with Ajax data
6495 */
6496 _fnAddTr( oSettings, $(oSettings.nTBody).children('tr') );
6497 }
6498
6499 /* Copy the data index array */
6500 oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
6501
6502 /* Initialisation complete - table can be drawn */
6503 oSettings.bInitialised = true;
6504
6505 /* Check if we need to initialise the table (it might not have been handed off to the
6506 * language processor)
6507 */
6508 if ( bInitHandedOff === false )
6509 {
6510 _fnInitialise( oSettings );
6511 }
1329 _fnLoadState( oSettings, oInit, loadedInit );
1330 }
1331 else {
1332 loadedInit();
1333 }
1334
6512 1335 } );
6513 1336 _that = null;
6514 1337 return this;
6515 1338 };
6516 1339
6517
6518
1340
1341 /*
1342 * It is useful to have variables which are scoped locally so only the
1343 * DataTables functions can access them and they don't leak into global space.
1344 * At the same time these functions are often useful over multiple files in the
1345 * core and API, so we list, or at least document, all variables which are used
1346 * by DataTables as private variables here. This also ensures that there is no
1347 * clashing of variable names and that they can easily referenced for reuse.
1348 */
1349
1350
1351 // Defined else where
1352 // _selector_run
1353 // _selector_opts
1354 // _selector_first
1355 // _selector_row_indexes
1356
1357 var _ext; // DataTable.ext
1358 var _Api; // DataTable.Api
1359 var _api_register; // DataTable.Api.register
1360 var _api_registerPlural; // DataTable.Api.registerPlural
1361
1362 var _re_dic = {};
1363 var _re_new_lines = /[\r\n]/g;
1364 var _re_html = /<.*?>/g;
1365
1366 // This is not strict ISO8601 - Date.parse() is quite lax, although
1367 // implementations differ between browsers.
1368 var _re_date = /^\d{2,4}[\.\/\-]\d{1,2}[\.\/\-]\d{1,2}([T ]{1}\d{1,2}[:\.]\d{2}([\.:]\d{2})?)?$/;
1369
1370 // Escape regular expression special characters
1371 var _re_escape_regex = new RegExp( '(\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ].join('|\\') + ')', 'g' );
1372
1373 // http://en.wikipedia.org/wiki/Foreign_exchange_market
1374 // - \u20BD - Russian ruble.
1375 // - \u20a9 - South Korean Won
1376 // - \u20BA - Turkish Lira
1377 // - \u20B9 - Indian Rupee
1378 // - R - Brazil (R$) and South Africa
1379 // - fr - Swiss Franc
1380 // - kr - Swedish krona, Norwegian krone and Danish krone
1381 // - \u2009 is thin space and \u202F is narrow no-break space, both used in many
1382 // standards as thousands separators.
1383 var _re_formatted_numeric = /[',$£€¥%\u2009\u202F\u20BD\u20a9\u20BArfk]/gi;
1384
1385
1386 var _empty = function ( d ) {
1387 return !d || d === true || d === '-' ? true : false;
1388 };
1389
1390
1391 var _intVal = function ( s ) {
1392 var integer = parseInt( s, 10 );
1393 return !isNaN(integer) && isFinite(s) ? integer : null;
1394 };
1395
1396 // Convert from a formatted number with characters other than `.` as the
1397 // decimal place, to a Javascript number
1398 var _numToDecimal = function ( num, decimalPoint ) {
1399 // Cache created regular expressions for speed as this function is called often
1400 if ( ! _re_dic[ decimalPoint ] ) {
1401 _re_dic[ decimalPoint ] = new RegExp( _fnEscapeRegex( decimalPoint ), 'g' );
1402 }
1403 return typeof num === 'string' && decimalPoint !== '.' ?
1404 num.replace( /\./g, '' ).replace( _re_dic[ decimalPoint ], '.' ) :
1405 num;
1406 };
1407
1408
1409 var _isNumber = function ( d, decimalPoint, formatted ) {
1410 var strType = typeof d === 'string';
1411
1412 // If empty return immediately so there must be a number if it is a
1413 // formatted string (this stops the string "k", or "kr", etc being detected
1414 // as a formatted number for currency
1415 if ( _empty( d ) ) {
1416 return true;
1417 }
1418
1419 if ( decimalPoint && strType ) {
1420 d = _numToDecimal( d, decimalPoint );
1421 }
1422
1423 if ( formatted && strType ) {
1424 d = d.replace( _re_formatted_numeric, '' );
1425 }
1426
1427 return !isNaN( parseFloat(d) ) && isFinite( d );
1428 };
1429
1430
1431 // A string without HTML in it can be considered to be HTML still
1432 var _isHtml = function ( d ) {
1433 return _empty( d ) || typeof d === 'string';
1434 };
1435
1436
1437 var _htmlNumeric = function ( d, decimalPoint, formatted ) {
1438 if ( _empty( d ) ) {
1439 return true;
1440 }
1441
1442 var html = _isHtml( d );
1443 return ! html ?
1444 null :
1445 _isNumber( _stripHtml( d ), decimalPoint, formatted ) ?
1446 true :
1447 null;
1448 };
1449
1450
1451 var _pluck = function ( a, prop, prop2 ) {
1452 var out = [];
1453 var i=0, ien=a.length;
1454
1455 // Could have the test in the loop for slightly smaller code, but speed
1456 // is essential here
1457 if ( prop2 !== undefined ) {
1458 for ( ; i<ien ; i++ ) {
1459 if ( a[i] && a[i][ prop ] ) {
1460 out.push( a[i][ prop ][ prop2 ] );
1461 }
1462 }
1463 }
1464 else {
1465 for ( ; i<ien ; i++ ) {
1466 if ( a[i] ) {
1467 out.push( a[i][ prop ] );
1468 }
1469 }
1470 }
1471
1472 return out;
1473 };
1474
1475
1476 // Basically the same as _pluck, but rather than looping over `a` we use `order`
1477 // as the indexes to pick from `a`
1478 var _pluck_order = function ( a, order, prop, prop2 )
1479 {
1480 var out = [];
1481 var i=0, ien=order.length;
1482
1483 // Could have the test in the loop for slightly smaller code, but speed
1484 // is essential here
1485 if ( prop2 !== undefined ) {
1486 for ( ; i<ien ; i++ ) {
1487 if ( a[ order[i] ][ prop ] ) {
1488 out.push( a[ order[i] ][ prop ][ prop2 ] );
1489 }
1490 }
1491 }
1492 else {
1493 for ( ; i<ien ; i++ ) {
1494 out.push( a[ order[i] ][ prop ] );
1495 }
1496 }
1497
1498 return out;
1499 };
1500
1501
1502 var _range = function ( len, start )
1503 {
1504 var out = [];
1505 var end;
1506
1507 if ( start === undefined ) {
1508 start = 0;
1509 end = len;
1510 }
1511 else {
1512 end = start;
1513 start = len;
1514 }
1515
1516 for ( var i=start ; i<end ; i++ ) {
1517 out.push( i );
1518 }
1519
1520 return out;
1521 };
1522
1523
1524 var _removeEmpty = function ( a )
1525 {
1526 var out = [];
1527
1528 for ( var i=0, ien=a.length ; i<ien ; i++ ) {
1529 if ( a[i] ) { // careful - will remove all falsy values!
1530 out.push( a[i] );
1531 }
1532 }
1533
1534 return out;
1535 };
1536
1537
1538 var _stripHtml = function ( d ) {
1539 return d.replace( _re_html, '' );
1540 };
1541
1542
1543 /**
1544 * Find the unique elements in a source array.
1545 *
1546 * @param {array} src Source array
1547 * @return {array} Array of unique items
1548 * @ignore
1549 */
1550 var _unique = function ( src )
1551 {
1552 // A faster unique method is to use object keys to identify used values,
1553 // but this doesn't work with arrays or objects, which we must also
1554 // consider. See jsperf.com/compare-array-unique-versions/4 for more
1555 // information.
1556 var
1557 out = [],
1558 val,
1559 i, ien=src.length,
1560 j, k=0;
1561
1562 again: for ( i=0 ; i<ien ; i++ ) {
1563 val = src[i];
1564
1565 for ( j=0 ; j<k ; j++ ) {
1566 if ( out[j] === val ) {
1567 continue again;
1568 }
1569 }
1570
1571 out.push( val );
1572 k++;
1573 }
1574
1575 return out;
1576 };
1577
1578
1579 /**
1580 * DataTables utility methods
1581 *
1582 * This namespace provides helper methods that DataTables uses internally to
1583 * create a DataTable, but which are not exclusively used only for DataTables.
1584 * These methods can be used by extension authors to save the duplication of
1585 * code.
1586 *
1587 * @namespace
1588 */
1589 DataTable.util = {
1590 /**
1591 * Throttle the calls to a function. Arguments and context are maintained
1592 * for the throttled function.
1593 *
1594 * @param {function} fn Function to be called
1595 * @param {integer} freq Call frequency in mS
1596 * @return {function} Wrapped function
1597 */
1598 throttle: function ( fn, freq ) {
1599 var
1600 frequency = freq !== undefined ? freq : 200,
1601 last,
1602 timer;
1603
1604 return function () {
1605 var
1606 that = this,
1607 now = +new Date(),
1608 args = arguments;
1609
1610 if ( last && now < last + frequency ) {
1611 clearTimeout( timer );
1612
1613 timer = setTimeout( function () {
1614 last = undefined;
1615 fn.apply( that, args );
1616 }, frequency );
1617 }
1618 else {
1619 last = now;
1620 fn.apply( that, args );
1621 }
1622 };
1623 },
1624
1625
1626 /**
1627 * Escape a string such that it can be used in a regular expression
1628 *
1629 * @param {string} val string to escape
1630 * @returns {string} escaped string
1631 */
1632 escapeRegex: function ( val ) {
1633 return val.replace( _re_escape_regex, '\\$1' );
1634 }
1635 };
1636
1637
1638
1639 /**
1640 * Create a mapping object that allows camel case parameters to be looked up
1641 * for their Hungarian counterparts. The mapping is stored in a private
1642 * parameter called `_hungarianMap` which can be accessed on the source object.
1643 * @param {object} o
1644 * @memberof DataTable#oApi
1645 */
1646 function _fnHungarianMap ( o )
1647 {
1648 var
1649 hungarian = 'a aa ai ao as b fn i m o s ',
1650 match,
1651 newKey,
1652 map = {};
1653
1654 $.each( o, function (key, val) {
1655 match = key.match(/^([^A-Z]+?)([A-Z])/);
1656
1657 if ( match && hungarian.indexOf(match[1]+' ') !== -1 )
1658 {
1659 newKey = key.replace( match[0], match[2].toLowerCase() );
1660 map[ newKey ] = key;
1661
1662 if ( match[1] === 'o' )
1663 {
1664 _fnHungarianMap( o[key] );
1665 }
1666 }
1667 } );
1668
1669 o._hungarianMap = map;
1670 }
1671
1672
1673 /**
1674 * Convert from camel case parameters to Hungarian, based on a Hungarian map
1675 * created by _fnHungarianMap.
1676 * @param {object} src The model object which holds all parameters that can be
1677 * mapped.
1678 * @param {object} user The object to convert from camel case to Hungarian.
1679 * @param {boolean} force When set to `true`, properties which already have a
1680 * Hungarian value in the `user` object will be overwritten. Otherwise they
1681 * won't be.
1682 * @memberof DataTable#oApi
1683 */
1684 function _fnCamelToHungarian ( src, user, force )
1685 {
1686 if ( ! src._hungarianMap ) {
1687 _fnHungarianMap( src );
1688 }
1689
1690 var hungarianKey;
1691
1692 $.each( user, function (key, val) {
1693 hungarianKey = src._hungarianMap[ key ];
1694
1695 if ( hungarianKey !== undefined && (force || user[hungarianKey] === undefined) )
1696 {
1697 // For objects, we need to buzz down into the object to copy parameters
1698 if ( hungarianKey.charAt(0) === 'o' )
1699 {
1700 // Copy the camelCase options over to the hungarian
1701 if ( ! user[ hungarianKey ] ) {
1702 user[ hungarianKey ] = {};
1703 }
1704 $.extend( true, user[hungarianKey], user[key] );
1705
1706 _fnCamelToHungarian( src[hungarianKey], user[hungarianKey], force );
1707 }
1708 else {
1709 user[hungarianKey] = user[ key ];
1710 }
1711 }
1712 } );
1713 }
1714
1715
1716 /**
1717 * Language compatibility - when certain options are given, and others aren't, we
1718 * need to duplicate the values over, in order to provide backwards compatibility
1719 * with older language files.
1720 * @param {object} oSettings dataTables settings object
1721 * @memberof DataTable#oApi
1722 */
1723 function _fnLanguageCompat( lang )
1724 {
1725 var defaults = DataTable.defaults.oLanguage;
1726 var zeroRecords = lang.sZeroRecords;
1727
1728 /* Backwards compatibility - if there is no sEmptyTable given, then use the same as
1729 * sZeroRecords - assuming that is given.
1730 */
1731 if ( ! lang.sEmptyTable && zeroRecords &&
1732 defaults.sEmptyTable === "No data available in table" )
1733 {
1734 _fnMap( lang, lang, 'sZeroRecords', 'sEmptyTable' );
1735 }
1736
1737 /* Likewise with loading records */
1738 if ( ! lang.sLoadingRecords && zeroRecords &&
1739 defaults.sLoadingRecords === "Loading..." )
1740 {
1741 _fnMap( lang, lang, 'sZeroRecords', 'sLoadingRecords' );
1742 }
1743
1744 // Old parameter name of the thousands separator mapped onto the new
1745 if ( lang.sInfoThousands ) {
1746 lang.sThousands = lang.sInfoThousands;
1747 }
1748
1749 var decimal = lang.sDecimal;
1750 if ( decimal ) {
1751 _addNumericSort( decimal );
1752 }
1753 }
1754
1755
1756 /**
1757 * Map one parameter onto another
1758 * @param {object} o Object to map
1759 * @param {*} knew The new parameter name
1760 * @param {*} old The old parameter name
1761 */
1762 var _fnCompatMap = function ( o, knew, old ) {
1763 if ( o[ knew ] !== undefined ) {
1764 o[ old ] = o[ knew ];
1765 }
1766 };
1767
1768
1769 /**
1770 * Provide backwards compatibility for the main DT options. Note that the new
1771 * options are mapped onto the old parameters, so this is an external interface
1772 * change only.
1773 * @param {object} init Object to map
1774 */
1775 function _fnCompatOpts ( init )
1776 {
1777 _fnCompatMap( init, 'ordering', 'bSort' );
1778 _fnCompatMap( init, 'orderMulti', 'bSortMulti' );
1779 _fnCompatMap( init, 'orderClasses', 'bSortClasses' );
1780 _fnCompatMap( init, 'orderCellsTop', 'bSortCellsTop' );
1781 _fnCompatMap( init, 'order', 'aaSorting' );
1782 _fnCompatMap( init, 'orderFixed', 'aaSortingFixed' );
1783 _fnCompatMap( init, 'paging', 'bPaginate' );
1784 _fnCompatMap( init, 'pagingType', 'sPaginationType' );
1785 _fnCompatMap( init, 'pageLength', 'iDisplayLength' );
1786 _fnCompatMap( init, 'searching', 'bFilter' );
1787
1788 // Boolean initialisation of x-scrolling
1789 if ( typeof init.sScrollX === 'boolean' ) {
1790 init.sScrollX = init.sScrollX ? '100%' : '';
1791 }
1792 if ( typeof init.scrollX === 'boolean' ) {
1793 init.scrollX = init.scrollX ? '100%' : '';
1794 }
1795
1796 // Column search objects are in an array, so it needs to be converted
1797 // element by element
1798 var searchCols = init.aoSearchCols;
1799
1800 if ( searchCols ) {
1801 for ( var i=0, ien=searchCols.length ; i<ien ; i++ ) {
1802 if ( searchCols[i] ) {
1803 _fnCamelToHungarian( DataTable.models.oSearch, searchCols[i] );
1804 }
1805 }
1806 }
1807 }
1808
1809
1810 /**
1811 * Provide backwards compatibility for column options. Note that the new options
1812 * are mapped onto the old parameters, so this is an external interface change
1813 * only.
1814 * @param {object} init Object to map
1815 */
1816 function _fnCompatCols ( init )
1817 {
1818 _fnCompatMap( init, 'orderable', 'bSortable' );
1819 _fnCompatMap( init, 'orderData', 'aDataSort' );
1820 _fnCompatMap( init, 'orderSequence', 'asSorting' );
1821 _fnCompatMap( init, 'orderDataType', 'sortDataType' );
1822
1823 // orderData can be given as an integer
1824 var dataSort = init.aDataSort;
1825 if ( dataSort && ! $.isArray( dataSort ) ) {
1826 init.aDataSort = [ dataSort ];
1827 }
1828 }
1829
1830
1831 /**
1832 * Browser feature detection for capabilities, quirks
1833 * @param {object} settings dataTables settings object
1834 * @memberof DataTable#oApi
1835 */
1836 function _fnBrowserDetect( settings )
1837 {
1838 // We don't need to do this every time DataTables is constructed, the values
1839 // calculated are specific to the browser and OS configuration which we
1840 // don't expect to change between initialisations
1841 if ( ! DataTable.__browser ) {
1842 var browser = {};
1843 DataTable.__browser = browser;
1844
1845 // Scrolling feature / quirks detection
1846 var n = $('<div/>')
1847 .css( {
1848 position: 'fixed',
1849 top: 0,
1850 left: $(window).scrollLeft()*-1, // allow for scrolling
1851 height: 1,
1852 width: 1,
1853 overflow: 'hidden'
1854 } )
1855 .append(
1856 $('<div/>')
1857 .css( {
1858 position: 'absolute',
1859 top: 1,
1860 left: 1,
1861 width: 100,
1862 overflow: 'scroll'
1863 } )
1864 .append(
1865 $('<div/>')
1866 .css( {
1867 width: '100%',
1868 height: 10
1869 } )
1870 )
1871 )
1872 .appendTo( 'body' );
1873
1874 var outer = n.children();
1875 var inner = outer.children();
1876
1877 // Numbers below, in order, are:
1878 // inner.offsetWidth, inner.clientWidth, outer.offsetWidth, outer.clientWidth
1879 //
1880 // IE6 XP: 100 100 100 83
1881 // IE7 Vista: 100 100 100 83
1882 // IE 8+ Windows: 83 83 100 83
1883 // Evergreen Windows: 83 83 100 83
1884 // Evergreen Mac with scrollbars: 85 85 100 85
1885 // Evergreen Mac without scrollbars: 100 100 100 100
1886
1887 // Get scrollbar width
1888 browser.barWidth = outer[0].offsetWidth - outer[0].clientWidth;
1889
1890 // IE6/7 will oversize a width 100% element inside a scrolling element, to
1891 // include the width of the scrollbar, while other browsers ensure the inner
1892 // element is contained without forcing scrolling
1893 browser.bScrollOversize = inner[0].offsetWidth === 100 && outer[0].clientWidth !== 100;
1894
1895 // In rtl text layout, some browsers (most, but not all) will place the
1896 // scrollbar on the left, rather than the right.
1897 browser.bScrollbarLeft = Math.round( inner.offset().left ) !== 1;
1898
1899 // IE8- don't provide height and width for getBoundingClientRect
1900 browser.bBounding = n[0].getBoundingClientRect().width ? true : false;
1901
1902 n.remove();
1903 }
1904
1905 $.extend( settings.oBrowser, DataTable.__browser );
1906 settings.oScroll.iBarWidth = DataTable.__browser.barWidth;
1907 }
1908
1909
1910 /**
1911 * Array.prototype reduce[Right] method, used for browsers which don't support
1912 * JS 1.6. Done this way to reduce code size, since we iterate either way
1913 * @param {object} settings dataTables settings object
1914 * @memberof DataTable#oApi
1915 */
1916 function _fnReduce ( that, fn, init, start, end, inc )
1917 {
1918 var
1919 i = start,
1920 value,
1921 isSet = false;
1922
1923 if ( init !== undefined ) {
1924 value = init;
1925 isSet = true;
1926 }
1927
1928 while ( i !== end ) {
1929 if ( ! that.hasOwnProperty(i) ) {
1930 continue;
1931 }
1932
1933 value = isSet ?
1934 fn( value, that[i], i, that ) :
1935 that[i];
1936
1937 isSet = true;
1938 i += inc;
1939 }
1940
1941 return value;
1942 }
1943
1944 /**
1945 * Add a column to the list used for the table with default values
1946 * @param {object} oSettings dataTables settings object
1947 * @param {node} nTh The th element for this column
1948 * @memberof DataTable#oApi
1949 */
1950 function _fnAddColumn( oSettings, nTh )
1951 {
1952 // Add column to aoColumns array
1953 var oDefaults = DataTable.defaults.column;
1954 var iCol = oSettings.aoColumns.length;
1955 var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, {
1956 "nTh": nTh ? nTh : document.createElement('th'),
1957 "sTitle": oDefaults.sTitle ? oDefaults.sTitle : nTh ? nTh.innerHTML : '',
1958 "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol],
1959 "mData": oDefaults.mData ? oDefaults.mData : iCol,
1960 idx: iCol
1961 } );
1962 oSettings.aoColumns.push( oCol );
1963
1964 // Add search object for column specific search. Note that the `searchCols[ iCol ]`
1965 // passed into extend can be undefined. This allows the user to give a default
1966 // with only some of the parameters defined, and also not give a default
1967 var searchCols = oSettings.aoPreSearchCols;
1968 searchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch, searchCols[ iCol ] );
1969
1970 // Use the default column options function to initialise classes etc
1971 _fnColumnOptions( oSettings, iCol, $(nTh).data() );
1972 }
1973
1974
1975 /**
1976 * Apply options for a column
1977 * @param {object} oSettings dataTables settings object
1978 * @param {int} iCol column index to consider
1979 * @param {object} oOptions object with sType, bVisible and bSearchable etc
1980 * @memberof DataTable#oApi
1981 */
1982 function _fnColumnOptions( oSettings, iCol, oOptions )
1983 {
1984 var oCol = oSettings.aoColumns[ iCol ];
1985 var oClasses = oSettings.oClasses;
1986 var th = $(oCol.nTh);
1987
1988 // Try to get width information from the DOM. We can't get it from CSS
1989 // as we'd need to parse the CSS stylesheet. `width` option can override
1990 if ( ! oCol.sWidthOrig ) {
1991 // Width attribute
1992 oCol.sWidthOrig = th.attr('width') || null;
1993
1994 // Style attribute
1995 var t = (th.attr('style') || '').match(/width:\s*(\d+[pxem%]+)/);
1996 if ( t ) {
1997 oCol.sWidthOrig = t[1];
1998 }
1999 }
2000
2001 /* User specified column options */
2002 if ( oOptions !== undefined && oOptions !== null )
2003 {
2004 // Backwards compatibility
2005 _fnCompatCols( oOptions );
2006
2007 // Map camel case parameters to their Hungarian counterparts
2008 _fnCamelToHungarian( DataTable.defaults.column, oOptions );
2009
2010 /* Backwards compatibility for mDataProp */
2011 if ( oOptions.mDataProp !== undefined && !oOptions.mData )
2012 {
2013 oOptions.mData = oOptions.mDataProp;
2014 }
2015
2016 if ( oOptions.sType )
2017 {
2018 oCol._sManualType = oOptions.sType;
2019 }
2020
2021 // `class` is a reserved word in Javascript, so we need to provide
2022 // the ability to use a valid name for the camel case input
2023 if ( oOptions.className && ! oOptions.sClass )
2024 {
2025 oOptions.sClass = oOptions.className;
2026 }
2027
2028 $.extend( oCol, oOptions );
2029 _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" );
2030
2031 /* iDataSort to be applied (backwards compatibility), but aDataSort will take
2032 * priority if defined
2033 */
2034 if ( oOptions.iDataSort !== undefined )
2035 {
2036 oCol.aDataSort = [ oOptions.iDataSort ];
2037 }
2038 _fnMap( oCol, oOptions, "aDataSort" );
2039 }
2040
2041 /* Cache the data get and set functions for speed */
2042 var mDataSrc = oCol.mData;
2043 var mData = _fnGetObjectDataFn( mDataSrc );
2044 var mRender = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null;
2045
2046 var attrTest = function( src ) {
2047 return typeof src === 'string' && src.indexOf('@') !== -1;
2048 };
2049 oCol._bAttrSrc = $.isPlainObject( mDataSrc ) && (
2050 attrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter)
2051 );
2052 oCol._setter = null;
2053
2054 oCol.fnGetData = function (rowData, type, meta) {
2055 var innerData = mData( rowData, type, undefined, meta );
2056
2057 return mRender && type ?
2058 mRender( innerData, type, rowData, meta ) :
2059 innerData;
2060 };
2061 oCol.fnSetData = function ( rowData, val, meta ) {
2062 return _fnSetObjectDataFn( mDataSrc )( rowData, val, meta );
2063 };
2064
2065 // Indicate if DataTables should read DOM data as an object or array
2066 // Used in _fnGetRowElements
2067 if ( typeof mDataSrc !== 'number' ) {
2068 oSettings._rowReadObject = true;
2069 }
2070
2071 /* Feature sorting overrides column specific when off */
2072 if ( !oSettings.oFeatures.bSort )
2073 {
2074 oCol.bSortable = false;
2075 th.addClass( oClasses.sSortableNone ); // Have to add class here as order event isn't called
2076 }
2077
2078 /* Check that the class assignment is correct for sorting */
2079 var bAsc = $.inArray('asc', oCol.asSorting) !== -1;
2080 var bDesc = $.inArray('desc', oCol.asSorting) !== -1;
2081 if ( !oCol.bSortable || (!bAsc && !bDesc) )
2082 {
2083 oCol.sSortingClass = oClasses.sSortableNone;
2084 oCol.sSortingClassJUI = "";
2085 }
2086 else if ( bAsc && !bDesc )
2087 {
2088 oCol.sSortingClass = oClasses.sSortableAsc;
2089 oCol.sSortingClassJUI = oClasses.sSortJUIAscAllowed;
2090 }
2091 else if ( !bAsc && bDesc )
2092 {
2093 oCol.sSortingClass = oClasses.sSortableDesc;
2094 oCol.sSortingClassJUI = oClasses.sSortJUIDescAllowed;
2095 }
2096 else
2097 {
2098 oCol.sSortingClass = oClasses.sSortable;
2099 oCol.sSortingClassJUI = oClasses.sSortJUI;
2100 }
2101 }
2102
2103
2104 /**
2105 * Adjust the table column widths for new data. Note: you would probably want to
2106 * do a redraw after calling this function!
2107 * @param {object} settings dataTables settings object
2108 * @memberof DataTable#oApi
2109 */
2110 function _fnAdjustColumnSizing ( settings )
2111 {
2112 /* Not interested in doing column width calculation if auto-width is disabled */
2113 if ( settings.oFeatures.bAutoWidth !== false )
2114 {
2115 var columns = settings.aoColumns;
2116
2117 _fnCalculateColumnWidths( settings );
2118 for ( var i=0 , iLen=columns.length ; i<iLen ; i++ )
2119 {
2120 columns[i].nTh.style.width = columns[i].sWidth;
2121 }
2122 }
2123
2124 var scroll = settings.oScroll;
2125 if ( scroll.sY !== '' || scroll.sX !== '')
2126 {
2127 _fnScrollDraw( settings );
2128 }
2129
2130 _fnCallbackFire( settings, null, 'column-sizing', [settings] );
2131 }
2132
2133
2134 /**
2135 * Covert the index of a visible column to the index in the data array (take account
2136 * of hidden columns)
2137 * @param {object} oSettings dataTables settings object
2138 * @param {int} iMatch Visible column index to lookup
2139 * @returns {int} i the data index
2140 * @memberof DataTable#oApi
2141 */
2142 function _fnVisibleToColumnIndex( oSettings, iMatch )
2143 {
2144 var aiVis = _fnGetColumns( oSettings, 'bVisible' );
2145
2146 return typeof aiVis[iMatch] === 'number' ?
2147 aiVis[iMatch] :
2148 null;
2149 }
2150
2151
2152 /**
2153 * Covert the index of an index in the data array and convert it to the visible
2154 * column index (take account of hidden columns)
2155 * @param {int} iMatch Column index to lookup
2156 * @param {object} oSettings dataTables settings object
2157 * @returns {int} i the data index
2158 * @memberof DataTable#oApi
2159 */
2160 function _fnColumnIndexToVisible( oSettings, iMatch )
2161 {
2162 var aiVis = _fnGetColumns( oSettings, 'bVisible' );
2163 var iPos = $.inArray( iMatch, aiVis );
2164
2165 return iPos !== -1 ? iPos : null;
2166 }
2167
2168
2169 /**
2170 * Get the number of visible columns
2171 * @param {object} oSettings dataTables settings object
2172 * @returns {int} i the number of visible columns
2173 * @memberof DataTable#oApi
2174 */
2175 function _fnVisbleColumns( oSettings )
2176 {
2177 var vis = 0;
2178
2179 // No reduce in IE8, use a loop for now
2180 $.each( oSettings.aoColumns, function ( i, col ) {
2181 if ( col.bVisible && $(col.nTh).css('display') !== 'none' ) {
2182 vis++;
2183 }
2184 } );
2185
2186 return vis;
2187 }
2188
2189
2190 /**
2191 * Get an array of column indexes that match a given property
2192 * @param {object} oSettings dataTables settings object
2193 * @param {string} sParam Parameter in aoColumns to look for - typically
2194 * bVisible or bSearchable
2195 * @returns {array} Array of indexes with matched properties
2196 * @memberof DataTable#oApi
2197 */
2198 function _fnGetColumns( oSettings, sParam )
2199 {
2200 var a = [];
2201
2202 $.map( oSettings.aoColumns, function(val, i) {
2203 if ( val[sParam] ) {
2204 a.push( i );
2205 }
2206 } );
2207
2208 return a;
2209 }
2210
2211
2212 /**
2213 * Calculate the 'type' of a column
2214 * @param {object} settings dataTables settings object
2215 * @memberof DataTable#oApi
2216 */
2217 function _fnColumnTypes ( settings )
2218 {
2219 var columns = settings.aoColumns;
2220 var data = settings.aoData;
2221 var types = DataTable.ext.type.detect;
2222 var i, ien, j, jen, k, ken;
2223 var col, cell, detectedType, cache;
2224
2225 // For each column, spin over the
2226 for ( i=0, ien=columns.length ; i<ien ; i++ ) {
2227 col = columns[i];
2228 cache = [];
2229
2230 if ( ! col.sType && col._sManualType ) {
2231 col.sType = col._sManualType;
2232 }
2233 else if ( ! col.sType ) {
2234 for ( j=0, jen=types.length ; j<jen ; j++ ) {
2235 for ( k=0, ken=data.length ; k<ken ; k++ ) {
2236 // Use a cache array so we only need to get the type data
2237 // from the formatter once (when using multiple detectors)
2238 if ( cache[k] === undefined ) {
2239 cache[k] = _fnGetCellData( settings, k, i, 'type' );
2240 }
2241
2242 detectedType = types[j]( cache[k], settings );
2243
2244 // If null, then this type can't apply to this column, so
2245 // rather than testing all cells, break out. There is an
2246 // exception for the last type which is `html`. We need to
2247 // scan all rows since it is possible to mix string and HTML
2248 // types
2249 if ( ! detectedType && j !== types.length-1 ) {
2250 break;
2251 }
2252
2253 // Only a single match is needed for html type since it is
2254 // bottom of the pile and very similar to string
2255 if ( detectedType === 'html' ) {
2256 break;
2257 }
2258 }
2259
2260 // Type is valid for all data points in the column - use this
2261 // type
2262 if ( detectedType ) {
2263 col.sType = detectedType;
2264 break;
2265 }
2266 }
2267
2268 // Fall back - if no type was detected, always use string
2269 if ( ! col.sType ) {
2270 col.sType = 'string';
2271 }
2272 }
2273 }
2274 }
2275
2276
2277 /**
2278 * Take the column definitions and static columns arrays and calculate how
2279 * they relate to column indexes. The callback function will then apply the
2280 * definition found for a column to a suitable configuration object.
2281 * @param {object} oSettings dataTables settings object
2282 * @param {array} aoColDefs The aoColumnDefs array that is to be applied
2283 * @param {array} aoCols The aoColumns array that defines columns individually
2284 * @param {function} fn Callback function - takes two parameters, the calculated
2285 * column index and the definition for that column.
2286 * @memberof DataTable#oApi
2287 */
2288 function _fnApplyColumnDefs( oSettings, aoColDefs, aoCols, fn )
2289 {
2290 var i, iLen, j, jLen, k, kLen, def;
2291 var columns = oSettings.aoColumns;
2292
2293 // Column definitions with aTargets
2294 if ( aoColDefs )
2295 {
2296 /* Loop over the definitions array - loop in reverse so first instance has priority */
2297 for ( i=aoColDefs.length-1 ; i>=0 ; i-- )
2298 {
2299 def = aoColDefs[i];
2300
2301 /* Each definition can target multiple columns, as it is an array */
2302 var aTargets = def.targets !== undefined ?
2303 def.targets :
2304 def.aTargets;
2305
2306 if ( ! $.isArray( aTargets ) )
2307 {
2308 aTargets = [ aTargets ];
2309 }
2310
2311 for ( j=0, jLen=aTargets.length ; j<jLen ; j++ )
2312 {
2313 if ( typeof aTargets[j] === 'number' && aTargets[j] >= 0 )
2314 {
2315 /* Add columns that we don't yet know about */
2316 while( columns.length <= aTargets[j] )
2317 {
2318 _fnAddColumn( oSettings );
2319 }
2320
2321 /* Integer, basic index */
2322 fn( aTargets[j], def );
2323 }
2324 else if ( typeof aTargets[j] === 'number' && aTargets[j] < 0 )
2325 {
2326 /* Negative integer, right to left column counting */
2327 fn( columns.length+aTargets[j], def );
2328 }
2329 else if ( typeof aTargets[j] === 'string' )
2330 {
2331 /* Class name matching on TH element */
2332 for ( k=0, kLen=columns.length ; k<kLen ; k++ )
2333 {
2334 if ( aTargets[j] == "_all" ||
2335 $(columns[k].nTh).hasClass( aTargets[j] ) )
2336 {
2337 fn( k, def );
2338 }
2339 }
2340 }
2341 }
2342 }
2343 }
2344
2345 // Statically defined columns array
2346 if ( aoCols )
2347 {
2348 for ( i=0, iLen=aoCols.length ; i<iLen ; i++ )
2349 {
2350 fn( i, aoCols[i] );
2351 }
2352 }
2353 }
2354
2355 /**
2356 * Add a data array to the table, creating DOM node etc. This is the parallel to
2357 * _fnGatherData, but for adding rows from a Javascript source, rather than a
2358 * DOM source.
2359 * @param {object} oSettings dataTables settings object
2360 * @param {array} aData data array to be added
2361 * @param {node} [nTr] TR element to add to the table - optional. If not given,
2362 * DataTables will create a row automatically
2363 * @param {array} [anTds] Array of TD|TH elements for the row - must be given
2364 * if nTr is.
2365 * @returns {int} >=0 if successful (index of new aoData entry), -1 if failed
2366 * @memberof DataTable#oApi
2367 */
2368 function _fnAddData ( oSettings, aDataIn, nTr, anTds )
2369 {
2370 /* Create the object for storing information about this new row */
2371 var iRow = oSettings.aoData.length;
2372 var oData = $.extend( true, {}, DataTable.models.oRow, {
2373 src: nTr ? 'dom' : 'data',
2374 idx: iRow
2375 } );
2376
2377 oData._aData = aDataIn;
2378 oSettings.aoData.push( oData );
2379
2380 /* Create the cells */
2381 var nTd, sThisType;
2382 var columns = oSettings.aoColumns;
2383
2384 // Invalidate the column types as the new data needs to be revalidated
2385 for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
2386 {
2387 columns[i].sType = null;
2388 }
2389
2390 /* Add to the display array */
2391 oSettings.aiDisplayMaster.push( iRow );
2392
2393 var id = oSettings.rowIdFn( aDataIn );
2394 if ( id !== undefined ) {
2395 oSettings.aIds[ id ] = oData;
2396 }
2397
2398 /* Create the DOM information, or register it if already present */
2399 if ( nTr || ! oSettings.oFeatures.bDeferRender )
2400 {
2401 _fnCreateTr( oSettings, iRow, nTr, anTds );
2402 }
2403
2404 return iRow;
2405 }
2406
2407
2408 /**
2409 * Add one or more TR elements to the table. Generally we'd expect to
2410 * use this for reading data from a DOM sourced table, but it could be
2411 * used for an TR element. Note that if a TR is given, it is used (i.e.
2412 * it is not cloned).
2413 * @param {object} settings dataTables settings object
2414 * @param {array|node|jQuery} trs The TR element(s) to add to the table
2415 * @returns {array} Array of indexes for the added rows
2416 * @memberof DataTable#oApi
2417 */
2418 function _fnAddTr( settings, trs )
2419 {
2420 var row;
2421
2422 // Allow an individual node to be passed in
2423 if ( ! (trs instanceof $) ) {
2424 trs = $(trs);
2425 }
2426
2427 return trs.map( function (i, el) {
2428 row = _fnGetRowElements( settings, el );
2429 return _fnAddData( settings, row.data, el, row.cells );
2430 } );
2431 }
2432
2433
2434 /**
2435 * Take a TR element and convert it to an index in aoData
2436 * @param {object} oSettings dataTables settings object
2437 * @param {node} n the TR element to find
2438 * @returns {int} index if the node is found, null if not
2439 * @memberof DataTable#oApi
2440 */
2441 function _fnNodeToDataIndex( oSettings, n )
2442 {
2443 return (n._DT_RowIndex!==undefined) ? n._DT_RowIndex : null;
2444 }
2445
2446
2447 /**
2448 * Take a TD element and convert it into a column data index (not the visible index)
2449 * @param {object} oSettings dataTables settings object
2450 * @param {int} iRow The row number the TD/TH can be found in
2451 * @param {node} n The TD/TH element to find
2452 * @returns {int} index if the node is found, -1 if not
2453 * @memberof DataTable#oApi
2454 */
2455 function _fnNodeToColumnIndex( oSettings, iRow, n )
2456 {
2457 return $.inArray( n, oSettings.aoData[ iRow ].anCells );
2458 }
2459
2460
2461 /**
2462 * Get the data for a given cell from the internal cache, taking into account data mapping
2463 * @param {object} settings dataTables settings object
2464 * @param {int} rowIdx aoData row id
2465 * @param {int} colIdx Column index
2466 * @param {string} type data get type ('display', 'type' 'filter' 'sort')
2467 * @returns {*} Cell data
2468 * @memberof DataTable#oApi
2469 */
2470 function _fnGetCellData( settings, rowIdx, colIdx, type )
2471 {
2472 var draw = settings.iDraw;
2473 var col = settings.aoColumns[colIdx];
2474 var rowData = settings.aoData[rowIdx]._aData;
2475 var defaultContent = col.sDefaultContent;
2476 var cellData = col.fnGetData( rowData, type, {
2477 settings: settings,
2478 row: rowIdx,
2479 col: colIdx
2480 } );
2481
2482 if ( cellData === undefined ) {
2483 if ( settings.iDrawError != draw && defaultContent === null ) {
2484 _fnLog( settings, 0, "Requested unknown parameter "+
2485 (typeof col.mData=='function' ? '{function}' : "'"+col.mData+"'")+
2486 " for row "+rowIdx+", column "+colIdx, 4 );
2487 settings.iDrawError = draw;
2488 }
2489 return defaultContent;
2490 }
2491
2492 // When the data source is null and a specific data type is requested (i.e.
2493 // not the original data), we can use default column data
2494 if ( (cellData === rowData || cellData === null) && defaultContent !== null && type !== undefined ) {
2495 cellData = defaultContent;
2496 }
2497 else if ( typeof cellData === 'function' ) {
2498 // If the data source is a function, then we run it and use the return,
2499 // executing in the scope of the data object (for instances)
2500 return cellData.call( rowData );
2501 }
2502
2503 if ( cellData === null && type == 'display' ) {
2504 return '';
2505 }
2506 return cellData;
2507 }
2508
2509
2510 /**
2511 * Set the value for a specific cell, into the internal data cache
2512 * @param {object} settings dataTables settings object
2513 * @param {int} rowIdx aoData row id
2514 * @param {int} colIdx Column index
2515 * @param {*} val Value to set
2516 * @memberof DataTable#oApi
2517 */
2518 function _fnSetCellData( settings, rowIdx, colIdx, val )
2519 {
2520 var col = settings.aoColumns[colIdx];
2521 var rowData = settings.aoData[rowIdx]._aData;
2522
2523 col.fnSetData( rowData, val, {
2524 settings: settings,
2525 row: rowIdx,
2526 col: colIdx
2527 } );
2528 }
2529
2530
2531 // Private variable that is used to match action syntax in the data property object
2532 var __reArray = /\[.*?\]$/;
2533 var __reFn = /\(\)$/;
2534
2535 /**
2536 * Split string on periods, taking into account escaped periods
2537 * @param {string} str String to split
2538 * @return {array} Split string
2539 */
2540 function _fnSplitObjNotation( str )
2541 {
2542 return $.map( str.match(/(\\.|[^\.])+/g) || [''], function ( s ) {
2543 return s.replace(/\\\./g, '.');
2544 } );
2545 }
2546
2547
2548 /**
2549 * Return a function that can be used to get data from a source object, taking
2550 * into account the ability to use nested objects as a source
2551 * @param {string|int|function} mSource The data source for the object
2552 * @returns {function} Data get function
2553 * @memberof DataTable#oApi
2554 */
2555 function _fnGetObjectDataFn( mSource )
2556 {
2557 if ( $.isPlainObject( mSource ) )
2558 {
2559 /* Build an object of get functions, and wrap them in a single call */
2560 var o = {};
2561 $.each( mSource, function (key, val) {
2562 if ( val ) {
2563 o[key] = _fnGetObjectDataFn( val );
2564 }
2565 } );
2566
2567 return function (data, type, row, meta) {
2568 var t = o[type] || o._;
2569 return t !== undefined ?
2570 t(data, type, row, meta) :
2571 data;
2572 };
2573 }
2574 else if ( mSource === null )
2575 {
2576 /* Give an empty string for rendering / sorting etc */
2577 return function (data) { // type, row and meta also passed, but not used
2578 return data;
2579 };
2580 }
2581 else if ( typeof mSource === 'function' )
2582 {
2583 return function (data, type, row, meta) {
2584 return mSource( data, type, row, meta );
2585 };
2586 }
2587 else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 ||
2588 mSource.indexOf('[') !== -1 || mSource.indexOf('(') !== -1) )
2589 {
2590 /* If there is a . in the source string then the data source is in a
2591 * nested object so we loop over the data for each level to get the next
2592 * level down. On each loop we test for undefined, and if found immediately
2593 * return. This allows entire objects to be missing and sDefaultContent to
2594 * be used if defined, rather than throwing an error
2595 */
2596 var fetchData = function (data, type, src) {
2597 var arrayNotation, funcNotation, out, innerSrc;
2598
2599 if ( src !== "" )
2600 {
2601 var a = _fnSplitObjNotation( src );
2602
2603 for ( var i=0, iLen=a.length ; i<iLen ; i++ )
2604 {
2605 // Check if we are dealing with special notation
2606 arrayNotation = a[i].match(__reArray);
2607 funcNotation = a[i].match(__reFn);
2608
2609 if ( arrayNotation )
2610 {
2611 // Array notation
2612 a[i] = a[i].replace(__reArray, '');
2613
2614 // Condition allows simply [] to be passed in
2615 if ( a[i] !== "" ) {
2616 data = data[ a[i] ];
2617 }
2618 out = [];
2619
2620 // Get the remainder of the nested object to get
2621 a.splice( 0, i+1 );
2622 innerSrc = a.join('.');
2623
2624 // Traverse each entry in the array getting the properties requested
2625 if ( $.isArray( data ) ) {
2626 for ( var j=0, jLen=data.length ; j<jLen ; j++ ) {
2627 out.push( fetchData( data[j], type, innerSrc ) );
2628 }
2629 }
2630
2631 // If a string is given in between the array notation indicators, that
2632 // is used to join the strings together, otherwise an array is returned
2633 var join = arrayNotation[0].substring(1, arrayNotation[0].length-1);
2634 data = (join==="") ? out : out.join(join);
2635
2636 // The inner call to fetchData has already traversed through the remainder
2637 // of the source requested, so we exit from the loop
2638 break;
2639 }
2640 else if ( funcNotation )
2641 {
2642 // Function call
2643 a[i] = a[i].replace(__reFn, '');
2644 data = data[ a[i] ]();
2645 continue;
2646 }
2647
2648 if ( data === null || data[ a[i] ] === undefined )
2649 {
2650 return undefined;
2651 }
2652 data = data[ a[i] ];
2653 }
2654 }
2655
2656 return data;
2657 };
2658
2659 return function (data, type) { // row and meta also passed, but not used
2660 return fetchData( data, type, mSource );
2661 };
2662 }
2663 else
2664 {
2665 /* Array or flat object mapping */
2666 return function (data, type) { // row and meta also passed, but not used
2667 return data[mSource];
2668 };
2669 }
2670 }
2671
2672
2673 /**
2674 * Return a function that can be used to set data from a source object, taking
2675 * into account the ability to use nested objects as a source
2676 * @param {string|int|function} mSource The data source for the object
2677 * @returns {function} Data set function
2678 * @memberof DataTable#oApi
2679 */
2680 function _fnSetObjectDataFn( mSource )
2681 {
2682 if ( $.isPlainObject( mSource ) )
2683 {
2684 /* Unlike get, only the underscore (global) option is used for for
2685 * setting data since we don't know the type here. This is why an object
2686 * option is not documented for `mData` (which is read/write), but it is
2687 * for `mRender` which is read only.
2688 */
2689 return _fnSetObjectDataFn( mSource._ );
2690 }
2691 else if ( mSource === null )
2692 {
2693 /* Nothing to do when the data source is null */
2694 return function () {};
2695 }
2696 else if ( typeof mSource === 'function' )
2697 {
2698 return function (data, val, meta) {
2699 mSource( data, 'set', val, meta );
2700 };
2701 }
2702 else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 ||
2703 mSource.indexOf('[') !== -1 || mSource.indexOf('(') !== -1) )
2704 {
2705 /* Like the get, we need to get data from a nested object */
2706 var setData = function (data, val, src) {
2707 var a = _fnSplitObjNotation( src ), b;
2708 var aLast = a[a.length-1];
2709 var arrayNotation, funcNotation, o, innerSrc;
2710
2711 for ( var i=0, iLen=a.length-1 ; i<iLen ; i++ )
2712 {
2713 // Check if we are dealing with an array notation request
2714 arrayNotation = a[i].match(__reArray);
2715 funcNotation = a[i].match(__reFn);
2716
2717 if ( arrayNotation )
2718 {
2719 a[i] = a[i].replace(__reArray, '');
2720 data[ a[i] ] = [];
2721
2722 // Get the remainder of the nested object to set so we can recurse
2723 b = a.slice();
2724 b.splice( 0, i+1 );
2725 innerSrc = b.join('.');
2726
2727 // Traverse each entry in the array setting the properties requested
2728 if ( $.isArray( val ) )
2729 {
2730 for ( var j=0, jLen=val.length ; j<jLen ; j++ )
2731 {
2732 o = {};
2733 setData( o, val[j], innerSrc );
2734 data[ a[i] ].push( o );
2735 }
2736 }
2737 else
2738 {
2739 // We've been asked to save data to an array, but it
2740 // isn't array data to be saved. Best that can be done
2741 // is to just save the value.
2742 data[ a[i] ] = val;
2743 }
2744
2745 // The inner call to setData has already traversed through the remainder
2746 // of the source and has set the data, thus we can exit here
2747 return;
2748 }
2749 else if ( funcNotation )
2750 {
2751 // Function call
2752 a[i] = a[i].replace(__reFn, '');
2753 data = data[ a[i] ]( val );
2754 }
2755
2756 // If the nested object doesn't currently exist - since we are
2757 // trying to set the value - create it
2758 if ( data[ a[i] ] === null || data[ a[i] ] === undefined )
2759 {
2760 data[ a[i] ] = {};
2761 }
2762 data = data[ a[i] ];
2763 }
2764
2765 // Last item in the input - i.e, the actual set
2766 if ( aLast.match(__reFn ) )
2767 {
2768 // Function call
2769 data = data[ aLast.replace(__reFn, '') ]( val );
2770 }
2771 else
2772 {
2773 // If array notation is used, we just want to strip it and use the property name
2774 // and assign the value. If it isn't used, then we get the result we want anyway
2775 data[ aLast.replace(__reArray, '') ] = val;
2776 }
2777 };
2778
2779 return function (data, val) { // meta is also passed in, but not used
2780 return setData( data, val, mSource );
2781 };
2782 }
2783 else
2784 {
2785 /* Array or flat object mapping */
2786 return function (data, val) { // meta is also passed in, but not used
2787 data[mSource] = val;
2788 };
2789 }
2790 }
2791
2792
2793 /**
2794 * Return an array with the full table data
2795 * @param {object} oSettings dataTables settings object
2796 * @returns array {array} aData Master data array
2797 * @memberof DataTable#oApi
2798 */
2799 function _fnGetDataMaster ( settings )
2800 {
2801 return _pluck( settings.aoData, '_aData' );
2802 }
2803
2804
2805 /**
2806 * Nuke the table
2807 * @param {object} oSettings dataTables settings object
2808 * @memberof DataTable#oApi
2809 */
2810 function _fnClearTable( settings )
2811 {
2812 settings.aoData.length = 0;
2813 settings.aiDisplayMaster.length = 0;
2814 settings.aiDisplay.length = 0;
2815 settings.aIds = {};
2816 }
2817
2818
2819 /**
2820 * Take an array of integers (index array) and remove a target integer (value - not
2821 * the key!)
2822 * @param {array} a Index array to target
2823 * @param {int} iTarget value to find
2824 * @memberof DataTable#oApi
2825 */
2826 function _fnDeleteIndex( a, iTarget, splice )
2827 {
2828 var iTargetIndex = -1;
2829
2830 for ( var i=0, iLen=a.length ; i<iLen ; i++ )
2831 {
2832 if ( a[i] == iTarget )
2833 {
2834 iTargetIndex = i;
2835 }
2836 else if ( a[i] > iTarget )
2837 {
2838 a[i]--;
2839 }
2840 }
2841
2842 if ( iTargetIndex != -1 && splice === undefined )
2843 {
2844 a.splice( iTargetIndex, 1 );
2845 }
2846 }
2847
2848
2849 /**
2850 * Mark cached data as invalid such that a re-read of the data will occur when
2851 * the cached data is next requested. Also update from the data source object.
2852 *
2853 * @param {object} settings DataTables settings object
2854 * @param {int} rowIdx Row index to invalidate
2855 * @param {string} [src] Source to invalidate from: undefined, 'auto', 'dom'
2856 * or 'data'
2857 * @param {int} [colIdx] Column index to invalidate. If undefined the whole
2858 * row will be invalidated
2859 * @memberof DataTable#oApi
2860 *
2861 * @todo For the modularisation of v1.11 this will need to become a callback, so
2862 * the sort and filter methods can subscribe to it. That will required
2863 * initialisation options for sorting, which is why it is not already baked in
2864 */
2865 function _fnInvalidate( settings, rowIdx, src, colIdx )
2866 {
2867 var row = settings.aoData[ rowIdx ];
2868 var i, ien;
2869 var cellWrite = function ( cell, col ) {
2870 // This is very frustrating, but in IE if you just write directly
2871 // to innerHTML, and elements that are overwritten are GC'ed,
2872 // even if there is a reference to them elsewhere
2873 while ( cell.childNodes.length ) {
2874 cell.removeChild( cell.firstChild );
2875 }
2876
2877 cell.innerHTML = _fnGetCellData( settings, rowIdx, col, 'display' );
2878 };
2879
2880 // Are we reading last data from DOM or the data object?
2881 if ( src === 'dom' || ((! src || src === 'auto') && row.src === 'dom') ) {
2882 // Read the data from the DOM
2883 row._aData = _fnGetRowElements(
2884 settings, row, colIdx, colIdx === undefined ? undefined : row._aData
2885 )
2886 .data;
2887 }
2888 else {
2889 // Reading from data object, update the DOM
2890 var cells = row.anCells;
2891
2892 if ( cells ) {
2893 if ( colIdx !== undefined ) {
2894 cellWrite( cells[colIdx], colIdx );
2895 }
2896 else {
2897 for ( i=0, ien=cells.length ; i<ien ; i++ ) {
2898 cellWrite( cells[i], i );
2899 }
2900 }
2901 }
2902 }
2903
2904 // For both row and cell invalidation, the cached data for sorting and
2905 // filtering is nulled out
2906 row._aSortData = null;
2907 row._aFilterData = null;
2908
2909 // Invalidate the type for a specific column (if given) or all columns since
2910 // the data might have changed
2911 var cols = settings.aoColumns;
2912 if ( colIdx !== undefined ) {
2913 cols[ colIdx ].sType = null;
2914 }
2915 else {
2916 for ( i=0, ien=cols.length ; i<ien ; i++ ) {
2917 cols[i].sType = null;
2918 }
2919
2920 // Update DataTables special `DT_*` attributes for the row
2921 _fnRowAttributes( settings, row );
2922 }
2923 }
2924
2925
2926 /**
2927 * Build a data source object from an HTML row, reading the contents of the
2928 * cells that are in the row.
2929 *
2930 * @param {object} settings DataTables settings object
2931 * @param {node|object} TR element from which to read data or existing row
2932 * object from which to re-read the data from the cells
2933 * @param {int} [colIdx] Optional column index
2934 * @param {array|object} [d] Data source object. If `colIdx` is given then this
2935 * parameter should also be given and will be used to write the data into.
2936 * Only the column in question will be written
2937 * @returns {object} Object with two parameters: `data` the data read, in
2938 * document order, and `cells` and array of nodes (they can be useful to the
2939 * caller, so rather than needing a second traversal to get them, just return
2940 * them from here).
2941 * @memberof DataTable#oApi
2942 */
2943 function _fnGetRowElements( settings, row, colIdx, d )
2944 {
2945 var
2946 tds = [],
2947 td = row.firstChild,
2948 name, col, o, i=0, contents,
2949 columns = settings.aoColumns,
2950 objectRead = settings._rowReadObject;
2951
2952 // Allow the data object to be passed in, or construct
2953 d = d !== undefined ?
2954 d :
2955 objectRead ?
2956 {} :
2957 [];
2958
2959 var attr = function ( str, td ) {
2960 if ( typeof str === 'string' ) {
2961 var idx = str.indexOf('@');
2962
2963 if ( idx !== -1 ) {
2964 var attr = str.substring( idx+1 );
2965 var setter = _fnSetObjectDataFn( str );
2966 setter( d, td.getAttribute( attr ) );
2967 }
2968 }
2969 };
2970
2971 // Read data from a cell and store into the data object
2972 var cellProcess = function ( cell ) {
2973 if ( colIdx === undefined || colIdx === i ) {
2974 col = columns[i];
2975 contents = $.trim(cell.innerHTML);
2976
2977 if ( col && col._bAttrSrc ) {
2978 var setter = _fnSetObjectDataFn( col.mData._ );
2979 setter( d, contents );
2980
2981 attr( col.mData.sort, cell );
2982 attr( col.mData.type, cell );
2983 attr( col.mData.filter, cell );
2984 }
2985 else {
2986 // Depending on the `data` option for the columns the data can
2987 // be read to either an object or an array.
2988 if ( objectRead ) {
2989 if ( ! col._setter ) {
2990 // Cache the setter function
2991 col._setter = _fnSetObjectDataFn( col.mData );
2992 }
2993 col._setter( d, contents );
2994 }
2995 else {
2996 d[i] = contents;
2997 }
2998 }
2999 }
3000
3001 i++;
3002 };
3003
3004 if ( td ) {
3005 // `tr` element was passed in
3006 while ( td ) {
3007 name = td.nodeName.toUpperCase();
3008
3009 if ( name == "TD" || name == "TH" ) {
3010 cellProcess( td );
3011 tds.push( td );
3012 }
3013
3014 td = td.nextSibling;
3015 }
3016 }
3017 else {
3018 // Existing row object passed in
3019 tds = row.anCells;
3020
3021 for ( var j=0, jen=tds.length ; j<jen ; j++ ) {
3022 cellProcess( tds[j] );
3023 }
3024 }
3025
3026 // Read the ID from the DOM if present
3027 var rowNode = row.firstChild ? row : row.nTr;
3028
3029 if ( rowNode ) {
3030 var id = rowNode.getAttribute( 'id' );
3031
3032 if ( id ) {
3033 _fnSetObjectDataFn( settings.rowId )( d, id );
3034 }
3035 }
3036
3037 return {
3038 data: d,
3039 cells: tds
3040 };
3041 }
3042 /**
3043 * Create a new TR element (and it's TD children) for a row
3044 * @param {object} oSettings dataTables settings object
3045 * @param {int} iRow Row to consider
3046 * @param {node} [nTrIn] TR element to add to the table - optional. If not given,
3047 * DataTables will create a row automatically
3048 * @param {array} [anTds] Array of TD|TH elements for the row - must be given
3049 * if nTr is.
3050 * @memberof DataTable#oApi
3051 */
3052 function _fnCreateTr ( oSettings, iRow, nTrIn, anTds )
3053 {
3054 var
3055 row = oSettings.aoData[iRow],
3056 rowData = row._aData,
3057 cells = [],
3058 nTr, nTd, oCol,
3059 i, iLen;
3060
3061 if ( row.nTr === null )
3062 {
3063 nTr = nTrIn || document.createElement('tr');
3064
3065 row.nTr = nTr;
3066 row.anCells = cells;
3067
3068 /* Use a private property on the node to allow reserve mapping from the node
3069 * to the aoData array for fast look up
3070 */
3071 nTr._DT_RowIndex = iRow;
3072
3073 /* Special parameters can be given by the data source to be used on the row */
3074 _fnRowAttributes( oSettings, row );
3075
3076 /* Process each column */
3077 for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
3078 {
3079 oCol = oSettings.aoColumns[i];
3080
3081 nTd = nTrIn ? anTds[i] : document.createElement( oCol.sCellType );
3082 nTd._DT_CellIndex = {
3083 row: iRow,
3084 column: i
3085 };
3086
3087 cells.push( nTd );
3088
3089 // Need to create the HTML if new, or if a rendering function is defined
3090 if ( (!nTrIn || oCol.mRender || oCol.mData !== i) &&
3091 (!$.isPlainObject(oCol.mData) || oCol.mData._ !== i+'.display')
3092 ) {
3093 nTd.innerHTML = _fnGetCellData( oSettings, iRow, i, 'display' );
3094 }
3095
3096 /* Add user defined class */
3097 if ( oCol.sClass )
3098 {
3099 nTd.className += ' '+oCol.sClass;
3100 }
3101
3102 // Visibility - add or remove as required
3103 if ( oCol.bVisible && ! nTrIn )
3104 {
3105 nTr.appendChild( nTd );
3106 }
3107 else if ( ! oCol.bVisible && nTrIn )
3108 {
3109 nTd.parentNode.removeChild( nTd );
3110 }
3111
3112 if ( oCol.fnCreatedCell )
3113 {
3114 oCol.fnCreatedCell.call( oSettings.oInstance,
3115 nTd, _fnGetCellData( oSettings, iRow, i ), rowData, iRow, i
3116 );
3117 }
3118 }
3119
3120 _fnCallbackFire( oSettings, 'aoRowCreatedCallback', null, [nTr, rowData, iRow] );
3121 }
3122
3123 // Remove once webkit bug 131819 and Chromium bug 365619 have been resolved
3124 // and deployed
3125 row.nTr.setAttribute( 'role', 'row' );
3126 }
3127
3128
3129 /**
3130 * Add attributes to a row based on the special `DT_*` parameters in a data
3131 * source object.
3132 * @param {object} settings DataTables settings object
3133 * @param {object} DataTables row object for the row to be modified
3134 * @memberof DataTable#oApi
3135 */
3136 function _fnRowAttributes( settings, row )
3137 {
3138 var tr = row.nTr;
3139 var data = row._aData;
3140
3141 if ( tr ) {
3142 var id = settings.rowIdFn( data );
3143
3144 if ( id ) {
3145 tr.id = id;
3146 }
3147
3148 if ( data.DT_RowClass ) {
3149 // Remove any classes added by DT_RowClass before
3150 var a = data.DT_RowClass.split(' ');
3151 row.__rowc = row.__rowc ?
3152 _unique( row.__rowc.concat( a ) ) :
3153 a;
3154
3155 $(tr)
3156 .removeClass( row.__rowc.join(' ') )
3157 .addClass( data.DT_RowClass );
3158 }
3159
3160 if ( data.DT_RowAttr ) {
3161 $(tr).attr( data.DT_RowAttr );
3162 }
3163
3164 if ( data.DT_RowData ) {
3165 $(tr).data( data.DT_RowData );
3166 }
3167 }
3168 }
3169
3170
3171 /**
3172 * Create the HTML header for the table
3173 * @param {object} oSettings dataTables settings object
3174 * @memberof DataTable#oApi
3175 */
3176 function _fnBuildHead( oSettings )
3177 {
3178 var i, ien, cell, row, column;
3179 var thead = oSettings.nTHead;
3180 var tfoot = oSettings.nTFoot;
3181 var createHeader = $('th, td', thead).length === 0;
3182 var classes = oSettings.oClasses;
3183 var columns = oSettings.aoColumns;
3184
3185 if ( createHeader ) {
3186 row = $('<tr/>').appendTo( thead );
3187 }
3188
3189 for ( i=0, ien=columns.length ; i<ien ; i++ ) {
3190 column = columns[i];
3191 cell = $( column.nTh ).addClass( column.sClass );
3192
3193 if ( createHeader ) {
3194 cell.appendTo( row );
3195 }
3196
3197 // 1.11 move into sorting
3198 if ( oSettings.oFeatures.bSort ) {
3199 cell.addClass( column.sSortingClass );
3200
3201 if ( column.bSortable !== false ) {
3202 cell
3203 .attr( 'tabindex', oSettings.iTabIndex )
3204 .attr( 'aria-controls', oSettings.sTableId );
3205
3206 _fnSortAttachListener( oSettings, column.nTh, i );
3207 }
3208 }
3209
3210 if ( column.sTitle != cell[0].innerHTML ) {
3211 cell.html( column.sTitle );
3212 }
3213
3214 _fnRenderer( oSettings, 'header' )(
3215 oSettings, cell, column, classes
3216 );
3217 }
3218
3219 if ( createHeader ) {
3220 _fnDetectHeader( oSettings.aoHeader, thead );
3221 }
3222
3223 /* ARIA role for the rows */
3224 $(thead).find('>tr').attr('role', 'row');
3225
3226 /* Deal with the footer - add classes if required */
3227 $(thead).find('>tr>th, >tr>td').addClass( classes.sHeaderTH );
3228 $(tfoot).find('>tr>th, >tr>td').addClass( classes.sFooterTH );
3229
3230 // Cache the footer cells. Note that we only take the cells from the first
3231 // row in the footer. If there is more than one row the user wants to
3232 // interact with, they need to use the table().foot() method. Note also this
3233 // allows cells to be used for multiple columns using colspan
3234 if ( tfoot !== null ) {
3235 var cells = oSettings.aoFooter[0];
3236
3237 for ( i=0, ien=cells.length ; i<ien ; i++ ) {
3238 column = columns[i];
3239 column.nTf = cells[i].cell;
3240
3241 if ( column.sClass ) {
3242 $(column.nTf).addClass( column.sClass );
3243 }
3244 }
3245 }
3246 }
3247
3248
3249 /**
3250 * Draw the header (or footer) element based on the column visibility states. The
3251 * methodology here is to use the layout array from _fnDetectHeader, modified for
3252 * the instantaneous column visibility, to construct the new layout. The grid is
3253 * traversed over cell at a time in a rows x columns grid fashion, although each
3254 * cell insert can cover multiple elements in the grid - which is tracks using the
3255 * aApplied array. Cell inserts in the grid will only occur where there isn't
3256 * already a cell in that position.
3257 * @param {object} oSettings dataTables settings object
3258 * @param array {objects} aoSource Layout array from _fnDetectHeader
3259 * @param {boolean} [bIncludeHidden=false] If true then include the hidden columns in the calc,
3260 * @memberof DataTable#oApi
3261 */
3262 function _fnDrawHead( oSettings, aoSource, bIncludeHidden )
3263 {
3264 var i, iLen, j, jLen, k, kLen, n, nLocalTr;
3265 var aoLocal = [];
3266 var aApplied = [];
3267 var iColumns = oSettings.aoColumns.length;
3268 var iRowspan, iColspan;
3269
3270 if ( ! aoSource )
3271 {
3272 return;
3273 }
3274
3275 if ( bIncludeHidden === undefined )
3276 {
3277 bIncludeHidden = false;
3278 }
3279
3280 /* Make a copy of the master layout array, but without the visible columns in it */
3281 for ( i=0, iLen=aoSource.length ; i<iLen ; i++ )
3282 {
3283 aoLocal[i] = aoSource[i].slice();
3284 aoLocal[i].nTr = aoSource[i].nTr;
3285
3286 /* Remove any columns which are currently hidden */
3287 for ( j=iColumns-1 ; j>=0 ; j-- )
3288 {
3289 if ( !oSettings.aoColumns[j].bVisible && !bIncludeHidden )
3290 {
3291 aoLocal[i].splice( j, 1 );
3292 }
3293 }
3294
3295 /* Prep the applied array - it needs an element for each row */
3296 aApplied.push( [] );
3297 }
3298
3299 for ( i=0, iLen=aoLocal.length ; i<iLen ; i++ )
3300 {
3301 nLocalTr = aoLocal[i].nTr;
3302
3303 /* All cells are going to be replaced, so empty out the row */
3304 if ( nLocalTr )
3305 {
3306 while( (n = nLocalTr.firstChild) )
3307 {
3308 nLocalTr.removeChild( n );
3309 }
3310 }
3311
3312 for ( j=0, jLen=aoLocal[i].length ; j<jLen ; j++ )
3313 {
3314 iRowspan = 1;
3315 iColspan = 1;
3316
3317 /* Check to see if there is already a cell (row/colspan) covering our target
3318 * insert point. If there is, then there is nothing to do.
3319 */
3320 if ( aApplied[i][j] === undefined )
3321 {
3322 nLocalTr.appendChild( aoLocal[i][j].cell );
3323 aApplied[i][j] = 1;
3324
3325 /* Expand the cell to cover as many rows as needed */
3326 while ( aoLocal[i+iRowspan] !== undefined &&
3327 aoLocal[i][j].cell == aoLocal[i+iRowspan][j].cell )
3328 {
3329 aApplied[i+iRowspan][j] = 1;
3330 iRowspan++;
3331 }
3332
3333 /* Expand the cell to cover as many columns as needed */
3334 while ( aoLocal[i][j+iColspan] !== undefined &&
3335 aoLocal[i][j].cell == aoLocal[i][j+iColspan].cell )
3336 {
3337 /* Must update the applied array over the rows for the columns */
3338 for ( k=0 ; k<iRowspan ; k++ )
3339 {
3340 aApplied[i+k][j+iColspan] = 1;
3341 }
3342 iColspan++;
3343 }
3344
3345 /* Do the actual expansion in the DOM */
3346 $(aoLocal[i][j].cell)
3347 .attr('rowspan', iRowspan)
3348 .attr('colspan', iColspan);
3349 }
3350 }
3351 }
3352 }
3353
3354
3355 /**
3356 * Insert the required TR nodes into the table for display
3357 * @param {object} oSettings dataTables settings object
3358 * @memberof DataTable#oApi
3359 */
3360 function _fnDraw( oSettings )
3361 {
3362 /* Provide a pre-callback function which can be used to cancel the draw is false is returned */
3363 var aPreDraw = _fnCallbackFire( oSettings, 'aoPreDrawCallback', 'preDraw', [oSettings] );
3364 if ( $.inArray( false, aPreDraw ) !== -1 )
3365 {
3366 _fnProcessingDisplay( oSettings, false );
3367 return;
3368 }
3369
3370 var i, iLen, n;
3371 var anRows = [];
3372 var iRowCount = 0;
3373 var asStripeClasses = oSettings.asStripeClasses;
3374 var iStripes = asStripeClasses.length;
3375 var iOpenRows = oSettings.aoOpenRows.length;
3376 var oLang = oSettings.oLanguage;
3377 var iInitDisplayStart = oSettings.iInitDisplayStart;
3378 var bServerSide = _fnDataSource( oSettings ) == 'ssp';
3379 var aiDisplay = oSettings.aiDisplay;
3380
3381 oSettings.bDrawing = true;
3382
3383 /* Check and see if we have an initial draw position from state saving */
3384 if ( iInitDisplayStart !== undefined && iInitDisplayStart !== -1 )
3385 {
3386 oSettings._iDisplayStart = bServerSide ?
3387 iInitDisplayStart :
3388 iInitDisplayStart >= oSettings.fnRecordsDisplay() ?
3389 0 :
3390 iInitDisplayStart;
3391
3392 oSettings.iInitDisplayStart = -1;
3393 }
3394
3395 var iDisplayStart = oSettings._iDisplayStart;
3396 var iDisplayEnd = oSettings.fnDisplayEnd();
3397
3398 /* Server-side processing draw intercept */
3399 if ( oSettings.bDeferLoading )
3400 {
3401 oSettings.bDeferLoading = false;
3402 oSettings.iDraw++;
3403 _fnProcessingDisplay( oSettings, false );
3404 }
3405 else if ( !bServerSide )
3406 {
3407 oSettings.iDraw++;
3408 }
3409 else if ( !oSettings.bDestroying && !_fnAjaxUpdate( oSettings ) )
3410 {
3411 return;
3412 }
3413
3414 if ( aiDisplay.length !== 0 )
3415 {
3416 var iStart = bServerSide ? 0 : iDisplayStart;
3417 var iEnd = bServerSide ? oSettings.aoData.length : iDisplayEnd;
3418
3419 for ( var j=iStart ; j<iEnd ; j++ )
3420 {
3421 var iDataIndex = aiDisplay[j];
3422 var aoData = oSettings.aoData[ iDataIndex ];
3423 if ( aoData.nTr === null )
3424 {
3425 _fnCreateTr( oSettings, iDataIndex );
3426 }
3427
3428 var nRow = aoData.nTr;
3429
3430 /* Remove the old striping classes and then add the new one */
3431 if ( iStripes !== 0 )
3432 {
3433 var sStripe = asStripeClasses[ iRowCount % iStripes ];
3434 if ( aoData._sRowStripe != sStripe )
3435 {
3436 $(nRow).removeClass( aoData._sRowStripe ).addClass( sStripe );
3437 aoData._sRowStripe = sStripe;
3438 }
3439 }
3440
3441 // Row callback functions - might want to manipulate the row
3442 // iRowCount and j are not currently documented. Are they at all
3443 // useful?
3444 _fnCallbackFire( oSettings, 'aoRowCallback', null,
3445 [nRow, aoData._aData, iRowCount, j] );
3446
3447 anRows.push( nRow );
3448 iRowCount++;
3449 }
3450 }
3451 else
3452 {
3453 /* Table is empty - create a row with an empty message in it */
3454 var sZero = oLang.sZeroRecords;
3455 if ( oSettings.iDraw == 1 && _fnDataSource( oSettings ) == 'ajax' )
3456 {
3457 sZero = oLang.sLoadingRecords;
3458 }
3459 else if ( oLang.sEmptyTable && oSettings.fnRecordsTotal() === 0 )
3460 {
3461 sZero = oLang.sEmptyTable;
3462 }
3463
3464 anRows[ 0 ] = $( '<tr/>', { 'class': iStripes ? asStripeClasses[0] : '' } )
3465 .append( $('<td />', {
3466 'valign': 'top',
3467 'colSpan': _fnVisbleColumns( oSettings ),
3468 'class': oSettings.oClasses.sRowEmpty
3469 } ).html( sZero ) )[0];
3470 }
3471
3472 /* Header and footer callbacks */
3473 _fnCallbackFire( oSettings, 'aoHeaderCallback', 'header', [ $(oSettings.nTHead).children('tr')[0],
3474 _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );
3475
3476 _fnCallbackFire( oSettings, 'aoFooterCallback', 'footer', [ $(oSettings.nTFoot).children('tr')[0],
3477 _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );
3478
3479 var body = $(oSettings.nTBody);
3480
3481 body.children().detach();
3482 body.append( $(anRows) );
3483
3484 /* Call all required callback functions for the end of a draw */
3485 _fnCallbackFire( oSettings, 'aoDrawCallback', 'draw', [oSettings] );
3486
3487 /* Draw is complete, sorting and filtering must be as well */
3488 oSettings.bSorted = false;
3489 oSettings.bFiltered = false;
3490 oSettings.bDrawing = false;
3491 }
3492
3493
3494 /**
3495 * Redraw the table - taking account of the various features which are enabled
3496 * @param {object} oSettings dataTables settings object
3497 * @param {boolean} [holdPosition] Keep the current paging position. By default
3498 * the paging is reset to the first page
3499 * @memberof DataTable#oApi
3500 */
3501 function _fnReDraw( settings, holdPosition )
3502 {
3503 var
3504 features = settings.oFeatures,
3505 sort = features.bSort,
3506 filter = features.bFilter;
3507
3508 if ( sort ) {
3509 _fnSort( settings );
3510 }
3511
3512 if ( filter ) {
3513 _fnFilterComplete( settings, settings.oPreviousSearch );
3514 }
3515 else {
3516 // No filtering, so we want to just use the display master
3517 settings.aiDisplay = settings.aiDisplayMaster.slice();
3518 }
3519
3520 if ( holdPosition !== true ) {
3521 settings._iDisplayStart = 0;
3522 }
3523
3524 // Let any modules know about the draw hold position state (used by
3525 // scrolling internally)
3526 settings._drawHold = holdPosition;
3527
3528 _fnDraw( settings );
3529
3530 settings._drawHold = false;
3531 }
3532
3533
3534 /**
3535 * Add the options to the page HTML for the table
3536 * @param {object} oSettings dataTables settings object
3537 * @memberof DataTable#oApi
3538 */
3539 function _fnAddOptionsHtml ( oSettings )
3540 {
3541 var classes = oSettings.oClasses;
3542 var table = $(oSettings.nTable);
3543 var holding = $('<div/>').insertBefore( table ); // Holding element for speed
3544 var features = oSettings.oFeatures;
3545
3546 // All DataTables are wrapped in a div
3547 var insert = $('<div/>', {
3548 id: oSettings.sTableId+'_wrapper',
3549 'class': classes.sWrapper + (oSettings.nTFoot ? '' : ' '+classes.sNoFooter)
3550 } );
3551
3552 oSettings.nHolding = holding[0];
3553 oSettings.nTableWrapper = insert[0];
3554 oSettings.nTableReinsertBefore = oSettings.nTable.nextSibling;
3555
3556 /* Loop over the user set positioning and place the elements as needed */
3557 var aDom = oSettings.sDom.split('');
3558 var featureNode, cOption, nNewNode, cNext, sAttr, j;
3559 for ( var i=0 ; i<aDom.length ; i++ )
3560 {
3561 featureNode = null;
3562 cOption = aDom[i];
3563
3564 if ( cOption == '<' )
3565 {
3566 /* New container div */
3567 nNewNode = $('<div/>')[0];
3568
3569 /* Check to see if we should append an id and/or a class name to the container */
3570 cNext = aDom[i+1];
3571 if ( cNext == "'" || cNext == '"' )
3572 {
3573 sAttr = "";
3574 j = 2;
3575 while ( aDom[i+j] != cNext )
3576 {
3577 sAttr += aDom[i+j];
3578 j++;
3579 }
3580
3581 /* Replace jQuery UI constants @todo depreciated */
3582 if ( sAttr == "H" )
3583 {
3584 sAttr = classes.sJUIHeader;
3585 }
3586 else if ( sAttr == "F" )
3587 {
3588 sAttr = classes.sJUIFooter;
3589 }
3590
3591 /* The attribute can be in the format of "#id.class", "#id" or "class" This logic
3592 * breaks the string into parts and applies them as needed
3593 */
3594 if ( sAttr.indexOf('.') != -1 )
3595 {
3596 var aSplit = sAttr.split('.');
3597 nNewNode.id = aSplit[0].substr(1, aSplit[0].length-1);
3598 nNewNode.className = aSplit[1];
3599 }
3600 else if ( sAttr.charAt(0) == "#" )
3601 {
3602 nNewNode.id = sAttr.substr(1, sAttr.length-1);
3603 }
3604 else
3605 {
3606 nNewNode.className = sAttr;
3607 }
3608
3609 i += j; /* Move along the position array */
3610 }
3611
3612 insert.append( nNewNode );
3613 insert = $(nNewNode);
3614 }
3615 else if ( cOption == '>' )
3616 {
3617 /* End container div */
3618 insert = insert.parent();
3619 }
3620 // @todo Move options into their own plugins?
3621 else if ( cOption == 'l' && features.bPaginate && features.bLengthChange )
3622 {
3623 /* Length */
3624 featureNode = _fnFeatureHtmlLength( oSettings );
3625 }
3626 else if ( cOption == 'f' && features.bFilter )
3627 {
3628 /* Filter */
3629 featureNode = _fnFeatureHtmlFilter( oSettings );
3630 }
3631 else if ( cOption == 'r' && features.bProcessing )
3632 {
3633 /* pRocessing */
3634 featureNode = _fnFeatureHtmlProcessing( oSettings );
3635 }
3636 else if ( cOption == 't' )
3637 {
3638 /* Table */
3639 featureNode = _fnFeatureHtmlTable( oSettings );
3640 }
3641 else if ( cOption == 'i' && features.bInfo )
3642 {
3643 /* Info */
3644 featureNode = _fnFeatureHtmlInfo( oSettings );
3645 }
3646 else if ( cOption == 'p' && features.bPaginate )
3647 {
3648 /* Pagination */
3649 featureNode = _fnFeatureHtmlPaginate( oSettings );
3650 }
3651 else if ( DataTable.ext.feature.length !== 0 )
3652 {
3653 /* Plug-in features */
3654 var aoFeatures = DataTable.ext.feature;
3655 for ( var k=0, kLen=aoFeatures.length ; k<kLen ; k++ )
3656 {
3657 if ( cOption == aoFeatures[k].cFeature )
3658 {
3659 featureNode = aoFeatures[k].fnInit( oSettings );
3660 break;
3661 }
3662 }
3663 }
3664
3665 /* Add to the 2D features array */
3666 if ( featureNode )
3667 {
3668 var aanFeatures = oSettings.aanFeatures;
3669
3670 if ( ! aanFeatures[cOption] )
3671 {
3672 aanFeatures[cOption] = [];
3673 }
3674
3675 aanFeatures[cOption].push( featureNode );
3676 insert.append( featureNode );
3677 }
3678 }
3679
3680 /* Built our DOM structure - replace the holding div with what we want */
3681 holding.replaceWith( insert );
3682 oSettings.nHolding = null;
3683 }
3684
3685
3686 /**
3687 * Use the DOM source to create up an array of header cells. The idea here is to
3688 * create a layout grid (array) of rows x columns, which contains a reference
3689 * to the cell that that point in the grid (regardless of col/rowspan), such that
3690 * any column / row could be removed and the new grid constructed
3691 * @param array {object} aLayout Array to store the calculated layout in
3692 * @param {node} nThead The header/footer element for the table
3693 * @memberof DataTable#oApi
3694 */
3695 function _fnDetectHeader ( aLayout, nThead )
3696 {
3697 var nTrs = $(nThead).children('tr');
3698 var nTr, nCell;
3699 var i, k, l, iLen, jLen, iColShifted, iColumn, iColspan, iRowspan;
3700 var bUnique;
3701 var fnShiftCol = function ( a, i, j ) {
3702 var k = a[i];
3703 while ( k[j] ) {
3704 j++;
3705 }
3706 return j;
3707 };
3708
3709 aLayout.splice( 0, aLayout.length );
3710
3711 /* We know how many rows there are in the layout - so prep it */
3712 for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
3713 {
3714 aLayout.push( [] );
3715 }
3716
3717 /* Calculate a layout array */
3718 for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
3719 {
3720 nTr = nTrs[i];
3721 iColumn = 0;
3722
3723 /* For every cell in the row... */
3724 nCell = nTr.firstChild;
3725 while ( nCell ) {
3726 if ( nCell.nodeName.toUpperCase() == "TD" ||
3727 nCell.nodeName.toUpperCase() == "TH" )
3728 {
3729 /* Get the col and rowspan attributes from the DOM and sanitise them */
3730 iColspan = nCell.getAttribute('colspan') * 1;
3731 iRowspan = nCell.getAttribute('rowspan') * 1;
3732 iColspan = (!iColspan || iColspan===0 || iColspan===1) ? 1 : iColspan;
3733 iRowspan = (!iRowspan || iRowspan===0 || iRowspan===1) ? 1 : iRowspan;
3734
3735 /* There might be colspan cells already in this row, so shift our target
3736 * accordingly
3737 */
3738 iColShifted = fnShiftCol( aLayout, i, iColumn );
3739
3740 /* Cache calculation for unique columns */
3741 bUnique = iColspan === 1 ? true : false;
3742
3743 /* If there is col / rowspan, copy the information into the layout grid */
3744 for ( l=0 ; l<iColspan ; l++ )
3745 {
3746 for ( k=0 ; k<iRowspan ; k++ )
3747 {
3748 aLayout[i+k][iColShifted+l] = {
3749 "cell": nCell,
3750 "unique": bUnique
3751 };
3752 aLayout[i+k].nTr = nTr;
3753 }
3754 }
3755 }
3756 nCell = nCell.nextSibling;
3757 }
3758 }
3759 }
3760
3761
3762 /**
3763 * Get an array of unique th elements, one for each column
3764 * @param {object} oSettings dataTables settings object
3765 * @param {node} nHeader automatically detect the layout from this node - optional
3766 * @param {array} aLayout thead/tfoot layout from _fnDetectHeader - optional
3767 * @returns array {node} aReturn list of unique th's
3768 * @memberof DataTable#oApi
3769 */
3770 function _fnGetUniqueThs ( oSettings, nHeader, aLayout )
3771 {
3772 var aReturn = [];
3773 if ( !aLayout )
3774 {
3775 aLayout = oSettings.aoHeader;
3776 if ( nHeader )
3777 {
3778 aLayout = [];
3779 _fnDetectHeader( aLayout, nHeader );
3780 }
3781 }
3782
3783 for ( var i=0, iLen=aLayout.length ; i<iLen ; i++ )
3784 {
3785 for ( var j=0, jLen=aLayout[i].length ; j<jLen ; j++ )
3786 {
3787 if ( aLayout[i][j].unique &&
3788 (!aReturn[j] || !oSettings.bSortCellsTop) )
3789 {
3790 aReturn[j] = aLayout[i][j].cell;
3791 }
3792 }
3793 }
3794
3795 return aReturn;
3796 }
3797
3798 /**
3799 * Create an Ajax call based on the table's settings, taking into account that
3800 * parameters can have multiple forms, and backwards compatibility.
3801 *
3802 * @param {object} oSettings dataTables settings object
3803 * @param {array} data Data to send to the server, required by
3804 * DataTables - may be augmented by developer callbacks
3805 * @param {function} fn Callback function to run when data is obtained
3806 */
3807 function _fnBuildAjax( oSettings, data, fn )
3808 {
3809 // Compatibility with 1.9-, allow fnServerData and event to manipulate
3810 _fnCallbackFire( oSettings, 'aoServerParams', 'serverParams', [data] );
3811
3812 // Convert to object based for 1.10+ if using the old array scheme which can
3813 // come from server-side processing or serverParams
3814 if ( data && $.isArray(data) ) {
3815 var tmp = {};
3816 var rbracket = /(.*?)\[\]$/;
3817
3818 $.each( data, function (key, val) {
3819 var match = val.name.match(rbracket);
3820
3821 if ( match ) {
3822 // Support for arrays
3823 var name = match[0];
3824
3825 if ( ! tmp[ name ] ) {
3826 tmp[ name ] = [];
3827 }
3828 tmp[ name ].push( val.value );
3829 }
3830 else {
3831 tmp[val.name] = val.value;
3832 }
3833 } );
3834 data = tmp;
3835 }
3836
3837 var ajaxData;
3838 var ajax = oSettings.ajax;
3839 var instance = oSettings.oInstance;
3840 var callback = function ( json ) {
3841 _fnCallbackFire( oSettings, null, 'xhr', [oSettings, json, oSettings.jqXHR] );
3842 fn( json );
3843 };
3844
3845 if ( $.isPlainObject( ajax ) && ajax.data )
3846 {
3847 ajaxData = ajax.data;
3848
3849 var newData = $.isFunction( ajaxData ) ?
3850 ajaxData( data, oSettings ) : // fn can manipulate data or return
3851 ajaxData; // an object object or array to merge
3852
3853 // If the function returned something, use that alone
3854 data = $.isFunction( ajaxData ) && newData ?
3855 newData :
3856 $.extend( true, data, newData );
3857
3858 // Remove the data property as we've resolved it already and don't want
3859 // jQuery to do it again (it is restored at the end of the function)
3860 delete ajax.data;
3861 }
3862
3863 var baseAjax = {
3864 "data": data,
3865 "success": function (json) {
3866 var error = json.error || json.sError;
3867 if ( error ) {
3868 _fnLog( oSettings, 0, error );
3869 }
3870
3871 oSettings.json = json;
3872 callback( json );
3873 },
3874 "dataType": "json",
3875 "cache": false,
3876 "type": oSettings.sServerMethod,
3877 "error": function (xhr, error, thrown) {
3878 var ret = _fnCallbackFire( oSettings, null, 'xhr', [oSettings, null, oSettings.jqXHR] );
3879
3880 if ( $.inArray( true, ret ) === -1 ) {
3881 if ( error == "parsererror" ) {
3882 _fnLog( oSettings, 0, 'Invalid JSON response', 1 );
3883 }
3884 else if ( xhr.readyState === 4 ) {
3885 _fnLog( oSettings, 0, 'Ajax error', 7 );
3886 }
3887 }
3888
3889 _fnProcessingDisplay( oSettings, false );
3890 }
3891 };
3892
3893 // Store the data submitted for the API
3894 oSettings.oAjaxData = data;
3895
3896 // Allow plug-ins and external processes to modify the data
3897 _fnCallbackFire( oSettings, null, 'preXhr', [oSettings, data] );
3898
3899 if ( oSettings.fnServerData )
3900 {
3901 // DataTables 1.9- compatibility
3902 oSettings.fnServerData.call( instance,
3903 oSettings.sAjaxSource,
3904 $.map( data, function (val, key) { // Need to convert back to 1.9 trad format
3905 return { name: key, value: val };
3906 } ),
3907 callback,
3908 oSettings
3909 );
3910 }
3911 else if ( oSettings.sAjaxSource || typeof ajax === 'string' )
3912 {
3913 // DataTables 1.9- compatibility
3914 oSettings.jqXHR = $.ajax( $.extend( baseAjax, {
3915 url: ajax || oSettings.sAjaxSource
3916 } ) );
3917 }
3918 else if ( $.isFunction( ajax ) )
3919 {
3920 // Is a function - let the caller define what needs to be done
3921 oSettings.jqXHR = ajax.call( instance, data, callback, oSettings );
3922 }
3923 else
3924 {
3925 // Object to extend the base settings
3926 oSettings.jqXHR = $.ajax( $.extend( baseAjax, ajax ) );
3927
3928 // Restore for next time around
3929 ajax.data = ajaxData;
3930 }
3931 }
3932
3933
3934 /**
3935 * Update the table using an Ajax call
3936 * @param {object} settings dataTables settings object
3937 * @returns {boolean} Block the table drawing or not
3938 * @memberof DataTable#oApi
3939 */
3940 function _fnAjaxUpdate( settings )
3941 {
3942 if ( settings.bAjaxDataGet ) {
3943 settings.iDraw++;
3944 _fnProcessingDisplay( settings, true );
3945
3946 _fnBuildAjax(
3947 settings,
3948 _fnAjaxParameters( settings ),
3949 function(json) {
3950 _fnAjaxUpdateDraw( settings, json );
3951 }
3952 );
3953
3954 return false;
3955 }
3956 return true;
3957 }
3958
3959
3960 /**
3961 * Build up the parameters in an object needed for a server-side processing
3962 * request. Note that this is basically done twice, is different ways - a modern
3963 * method which is used by default in DataTables 1.10 which uses objects and
3964 * arrays, or the 1.9- method with is name / value pairs. 1.9 method is used if
3965 * the sAjaxSource option is used in the initialisation, or the legacyAjax
3966 * option is set.
3967 * @param {object} oSettings dataTables settings object
3968 * @returns {bool} block the table drawing or not
3969 * @memberof DataTable#oApi
3970 */
3971 function _fnAjaxParameters( settings )
3972 {
3973 var
3974 columns = settings.aoColumns,
3975 columnCount = columns.length,
3976 features = settings.oFeatures,
3977 preSearch = settings.oPreviousSearch,
3978 preColSearch = settings.aoPreSearchCols,
3979 i, data = [], dataProp, column, columnSearch,
3980 sort = _fnSortFlatten( settings ),
3981 displayStart = settings._iDisplayStart,
3982 displayLength = features.bPaginate !== false ?
3983 settings._iDisplayLength :
3984 -1;
3985
3986 var param = function ( name, value ) {
3987 data.push( { 'name': name, 'value': value } );
3988 };
3989
3990 // DataTables 1.9- compatible method
3991 param( 'sEcho', settings.iDraw );
3992 param( 'iColumns', columnCount );
3993 param( 'sColumns', _pluck( columns, 'sName' ).join(',') );
3994 param( 'iDisplayStart', displayStart );
3995 param( 'iDisplayLength', displayLength );
3996
3997 // DataTables 1.10+ method
3998 var d = {
3999 draw: settings.iDraw,
4000 columns: [],
4001 order: [],
4002 start: displayStart,
4003 length: displayLength,
4004 search: {
4005 value: preSearch.sSearch,
4006 regex: preSearch.bRegex
4007 }
4008 };
4009
4010 for ( i=0 ; i<columnCount ; i++ ) {
4011 column = columns[i];
4012 columnSearch = preColSearch[i];
4013 dataProp = typeof column.mData=="function" ? 'function' : column.mData ;
4014
4015 d.columns.push( {
4016 data: dataProp,
4017 name: column.sName,
4018 searchable: column.bSearchable,
4019 orderable: column.bSortable,
4020 search: {
4021 value: columnSearch.sSearch,
4022 regex: columnSearch.bRegex
4023 }
4024 } );
4025
4026 param( "mDataProp_"+i, dataProp );
4027
4028 if ( features.bFilter ) {
4029 param( 'sSearch_'+i, columnSearch.sSearch );
4030 param( 'bRegex_'+i, columnSearch.bRegex );
4031 param( 'bSearchable_'+i, column.bSearchable );
4032 }
4033
4034 if ( features.bSort ) {
4035 param( 'bSortable_'+i, column.bSortable );
4036 }
4037 }
4038
4039 if ( features.bFilter ) {
4040 param( 'sSearch', preSearch.sSearch );
4041 param( 'bRegex', preSearch.bRegex );
4042 }
4043
4044 if ( features.bSort ) {
4045 $.each( sort, function ( i, val ) {
4046 d.order.push( { column: val.col, dir: val.dir } );
4047
4048 param( 'iSortCol_'+i, val.col );
4049 param( 'sSortDir_'+i, val.dir );
4050 } );
4051
4052 param( 'iSortingCols', sort.length );
4053 }
4054
4055 // If the legacy.ajax parameter is null, then we automatically decide which
4056 // form to use, based on sAjaxSource
4057 var legacy = DataTable.ext.legacy.ajax;
4058 if ( legacy === null ) {
4059 return settings.sAjaxSource ? data : d;
4060 }
4061
4062 // Otherwise, if legacy has been specified then we use that to decide on the
4063 // form
4064 return legacy ? data : d;
4065 }
4066
4067
4068 /**
4069 * Data the data from the server (nuking the old) and redraw the table
4070 * @param {object} oSettings dataTables settings object
4071 * @param {object} json json data return from the server.
4072 * @param {string} json.sEcho Tracking flag for DataTables to match requests
4073 * @param {int} json.iTotalRecords Number of records in the data set, not accounting for filtering
4074 * @param {int} json.iTotalDisplayRecords Number of records in the data set, accounting for filtering
4075 * @param {array} json.aaData The data to display on this page
4076 * @param {string} [json.sColumns] Column ordering (sName, comma separated)
4077 * @memberof DataTable#oApi
4078 */
4079 function _fnAjaxUpdateDraw ( settings, json )
4080 {
4081 // v1.10 uses camelCase variables, while 1.9 uses Hungarian notation.
4082 // Support both
4083 var compat = function ( old, modern ) {
4084 return json[old] !== undefined ? json[old] : json[modern];
4085 };
4086
4087 var data = _fnAjaxDataSrc( settings, json );
4088 var draw = compat( 'sEcho', 'draw' );
4089 var recordsTotal = compat( 'iTotalRecords', 'recordsTotal' );
4090 var recordsFiltered = compat( 'iTotalDisplayRecords', 'recordsFiltered' );
4091
4092 if ( draw ) {
4093 // Protect against out of sequence returns
4094 if ( draw*1 < settings.iDraw ) {
4095 return;
4096 }
4097 settings.iDraw = draw * 1;
4098 }
4099
4100 _fnClearTable( settings );
4101 settings._iRecordsTotal = parseInt(recordsTotal, 10);
4102 settings._iRecordsDisplay = parseInt(recordsFiltered, 10);
4103
4104 for ( var i=0, ien=data.length ; i<ien ; i++ ) {
4105 _fnAddData( settings, data[i] );
4106 }
4107 settings.aiDisplay = settings.aiDisplayMaster.slice();
4108
4109 settings.bAjaxDataGet = false;
4110 _fnDraw( settings );
4111
4112 if ( ! settings._bInitComplete ) {
4113 _fnInitComplete( settings, json );
4114 }
4115
4116 settings.bAjaxDataGet = true;
4117 _fnProcessingDisplay( settings, false );
4118 }
4119
4120
4121 /**
4122 * Get the data from the JSON data source to use for drawing a table. Using
4123 * `_fnGetObjectDataFn` allows the data to be sourced from a property of the
4124 * source object, or from a processing function.
4125 * @param {object} oSettings dataTables settings object
4126 * @param {object} json Data source object / array from the server
4127 * @return {array} Array of data to use
4128 */
4129 function _fnAjaxDataSrc ( oSettings, json )
4130 {
4131 var dataSrc = $.isPlainObject( oSettings.ajax ) && oSettings.ajax.dataSrc !== undefined ?
4132 oSettings.ajax.dataSrc :
4133 oSettings.sAjaxDataProp; // Compatibility with 1.9-.
4134
4135 // Compatibility with 1.9-. In order to read from aaData, check if the
4136 // default has been changed, if not, check for aaData
4137 if ( dataSrc === 'data' ) {
4138 return json.aaData || json[dataSrc];
4139 }
4140
4141 return dataSrc !== "" ?
4142 _fnGetObjectDataFn( dataSrc )( json ) :
4143 json;
4144 }
4145
4146 /**
4147 * Generate the node required for filtering text
4148 * @returns {node} Filter control element
4149 * @param {object} oSettings dataTables settings object
4150 * @memberof DataTable#oApi
4151 */
4152 function _fnFeatureHtmlFilter ( settings )
4153 {
4154 var classes = settings.oClasses;
4155 var tableId = settings.sTableId;
4156 var language = settings.oLanguage;
4157 var previousSearch = settings.oPreviousSearch;
4158 var features = settings.aanFeatures;
4159 var input = '<input type="search" class="'+classes.sFilterInput+'"/>';
4160
4161 var str = language.sSearch;
4162 str = str.match(/_INPUT_/) ?
4163 str.replace('_INPUT_', input) :
4164 str+input;
4165
4166 var filter = $('<div/>', {
4167 'id': ! features.f ? tableId+'_filter' : null,
4168 'class': classes.sFilter
4169 } )
4170 .append( $('<label/>' ).append( str ) );
4171
4172 var searchFn = function() {
4173 /* Update all other filter input elements for the new display */
4174 var n = features.f;
4175 var val = !this.value ? "" : this.value; // mental IE8 fix :-(
4176
4177 /* Now do the filter */
4178 if ( val != previousSearch.sSearch ) {
4179 _fnFilterComplete( settings, {
4180 "sSearch": val,
4181 "bRegex": previousSearch.bRegex,
4182 "bSmart": previousSearch.bSmart ,
4183 "bCaseInsensitive": previousSearch.bCaseInsensitive
4184 } );
4185
4186 // Need to redraw, without resorting
4187 settings._iDisplayStart = 0;
4188 _fnDraw( settings );
4189 }
4190 };
4191
4192 var searchDelay = settings.searchDelay !== null ?
4193 settings.searchDelay :
4194 _fnDataSource( settings ) === 'ssp' ?
4195 400 :
4196 0;
4197
4198 var jqFilter = $('input', filter)
4199 .val( previousSearch.sSearch )
4200 .attr( 'placeholder', language.sSearchPlaceholder )
4201 .on(
4202 'keyup.DT search.DT input.DT paste.DT cut.DT',
4203 searchDelay ?
4204 _fnThrottle( searchFn, searchDelay ) :
4205 searchFn
4206 )
4207 .on( 'keypress.DT', function(e) {
4208 /* Prevent form submission */
4209 if ( e.keyCode == 13 ) {
4210 return false;
4211 }
4212 } )
4213 .attr('aria-controls', tableId);
4214
4215 // Update the input elements whenever the table is filtered
4216 $(settings.nTable).on( 'search.dt.DT', function ( ev, s ) {
4217 if ( settings === s ) {
4218 // IE9 throws an 'unknown error' if document.activeElement is used
4219 // inside an iframe or frame...
4220 try {
4221 if ( jqFilter[0] !== document.activeElement ) {
4222 jqFilter.val( previousSearch.sSearch );
4223 }
4224 }
4225 catch ( e ) {}
4226 }
4227 } );
4228
4229 return filter[0];
4230 }
4231
4232
4233 /**
4234 * Filter the table using both the global filter and column based filtering
4235 * @param {object} oSettings dataTables settings object
4236 * @param {object} oSearch search information
4237 * @param {int} [iForce] force a research of the master array (1) or not (undefined or 0)
4238 * @memberof DataTable#oApi
4239 */
4240 function _fnFilterComplete ( oSettings, oInput, iForce )
4241 {
4242 var oPrevSearch = oSettings.oPreviousSearch;
4243 var aoPrevSearch = oSettings.aoPreSearchCols;
4244 var fnSaveFilter = function ( oFilter ) {
4245 /* Save the filtering values */
4246 oPrevSearch.sSearch = oFilter.sSearch;
4247 oPrevSearch.bRegex = oFilter.bRegex;
4248 oPrevSearch.bSmart = oFilter.bSmart;
4249 oPrevSearch.bCaseInsensitive = oFilter.bCaseInsensitive;
4250 };
4251 var fnRegex = function ( o ) {
4252 // Backwards compatibility with the bEscapeRegex option
4253 return o.bEscapeRegex !== undefined ? !o.bEscapeRegex : o.bRegex;
4254 };
4255
4256 // Resolve any column types that are unknown due to addition or invalidation
4257 // @todo As per sort - can this be moved into an event handler?
4258 _fnColumnTypes( oSettings );
4259
4260 /* In server-side processing all filtering is done by the server, so no point hanging around here */
4261 if ( _fnDataSource( oSettings ) != 'ssp' )
4262 {
4263 /* Global filter */
4264 _fnFilter( oSettings, oInput.sSearch, iForce, fnRegex(oInput), oInput.bSmart, oInput.bCaseInsensitive );
4265 fnSaveFilter( oInput );
4266
4267 /* Now do the individual column filter */
4268 for ( var i=0 ; i<aoPrevSearch.length ; i++ )
4269 {
4270 _fnFilterColumn( oSettings, aoPrevSearch[i].sSearch, i, fnRegex(aoPrevSearch[i]),
4271 aoPrevSearch[i].bSmart, aoPrevSearch[i].bCaseInsensitive );
4272 }
4273
4274 /* Custom filtering */
4275 _fnFilterCustom( oSettings );
4276 }
4277 else
4278 {
4279 fnSaveFilter( oInput );
4280 }
4281
4282 /* Tell the draw function we have been filtering */
4283 oSettings.bFiltered = true;
4284 _fnCallbackFire( oSettings, null, 'search', [oSettings] );
4285 }
4286
4287
4288 /**
4289 * Apply custom filtering functions
4290 * @param {object} oSettings dataTables settings object
4291 * @memberof DataTable#oApi
4292 */
4293 function _fnFilterCustom( settings )
4294 {
4295 var filters = DataTable.ext.search;
4296 var displayRows = settings.aiDisplay;
4297 var row, rowIdx;
4298
4299 for ( var i=0, ien=filters.length ; i<ien ; i++ ) {
4300 var rows = [];
4301
4302 // Loop over each row and see if it should be included
4303 for ( var j=0, jen=displayRows.length ; j<jen ; j++ ) {
4304 rowIdx = displayRows[ j ];
4305 row = settings.aoData[ rowIdx ];
4306
4307 if ( filters[i]( settings, row._aFilterData, rowIdx, row._aData, j ) ) {
4308 rows.push( rowIdx );
4309 }
4310 }
4311
4312 // So the array reference doesn't break set the results into the
4313 // existing array
4314 displayRows.length = 0;
4315 $.merge( displayRows, rows );
4316 }
4317 }
4318
4319
4320 /**
4321 * Filter the table on a per-column basis
4322 * @param {object} oSettings dataTables settings object
4323 * @param {string} sInput string to filter on
4324 * @param {int} iColumn column to filter
4325 * @param {bool} bRegex treat search string as a regular expression or not
4326 * @param {bool} bSmart use smart filtering or not
4327 * @param {bool} bCaseInsensitive Do case insenstive matching or not
4328 * @memberof DataTable#oApi
4329 */
4330 function _fnFilterColumn ( settings, searchStr, colIdx, regex, smart, caseInsensitive )
4331 {
4332 if ( searchStr === '' ) {
4333 return;
4334 }
4335
4336 var data;
4337 var out = [];
4338 var display = settings.aiDisplay;
4339 var rpSearch = _fnFilterCreateSearch( searchStr, regex, smart, caseInsensitive );
4340
4341 for ( var i=0 ; i<display.length ; i++ ) {
4342 data = settings.aoData[ display[i] ]._aFilterData[ colIdx ];
4343
4344 if ( rpSearch.test( data ) ) {
4345 out.push( display[i] );
4346 }
4347 }
4348
4349 settings.aiDisplay = out;
4350 }
4351
4352
4353 /**
4354 * Filter the data table based on user input and draw the table
4355 * @param {object} settings dataTables settings object
4356 * @param {string} input string to filter on
4357 * @param {int} force optional - force a research of the master array (1) or not (undefined or 0)
4358 * @param {bool} regex treat as a regular expression or not
4359 * @param {bool} smart perform smart filtering or not
4360 * @param {bool} caseInsensitive Do case insenstive matching or not
4361 * @memberof DataTable#oApi
4362 */
4363 function _fnFilter( settings, input, force, regex, smart, caseInsensitive )
4364 {
4365 var rpSearch = _fnFilterCreateSearch( input, regex, smart, caseInsensitive );
4366 var prevSearch = settings.oPreviousSearch.sSearch;
4367 var displayMaster = settings.aiDisplayMaster;
4368 var display, invalidated, i;
4369 var filtered = [];
4370
4371 // Need to take account of custom filtering functions - always filter
4372 if ( DataTable.ext.search.length !== 0 ) {
4373 force = true;
4374 }
4375
4376 // Check if any of the rows were invalidated
4377 invalidated = _fnFilterData( settings );
4378
4379 // If the input is blank - we just want the full data set
4380 if ( input.length <= 0 ) {
4381 settings.aiDisplay = displayMaster.slice();
4382 }
4383 else {
4384 // New search - start from the master array
4385 if ( invalidated ||
4386 force ||
4387 prevSearch.length > input.length ||
4388 input.indexOf(prevSearch) !== 0 ||
4389 settings.bSorted // On resort, the display master needs to be
4390 // re-filtered since indexes will have changed
4391 ) {
4392 settings.aiDisplay = displayMaster.slice();
4393 }
4394
4395 // Search the display array
4396 display = settings.aiDisplay;
4397
4398 for ( i=0 ; i<display.length ; i++ ) {
4399 if ( rpSearch.test( settings.aoData[ display[i] ]._sFilterRow ) ) {
4400 filtered.push( display[i] );
4401 }
4402 }
4403
4404 settings.aiDisplay = filtered;
4405 }
4406 }
4407
4408
4409 /**
4410 * Build a regular expression object suitable for searching a table
4411 * @param {string} sSearch string to search for
4412 * @param {bool} bRegex treat as a regular expression or not
4413 * @param {bool} bSmart perform smart filtering or not
4414 * @param {bool} bCaseInsensitive Do case insensitive matching or not
4415 * @returns {RegExp} constructed object
4416 * @memberof DataTable#oApi
4417 */
4418 function _fnFilterCreateSearch( search, regex, smart, caseInsensitive )
4419 {
4420 search = regex ?
4421 search :
4422 _fnEscapeRegex( search );
4423
4424 if ( smart ) {
4425 /* For smart filtering we want to allow the search to work regardless of
4426 * word order. We also want double quoted text to be preserved, so word
4427 * order is important - a la google. So this is what we want to
4428 * generate:
4429 *
4430 * ^(?=.*?\bone\b)(?=.*?\btwo three\b)(?=.*?\bfour\b).*$
4431 */
4432 var a = $.map( search.match( /"[^"]+"|[^ ]+/g ) || [''], function ( word ) {
4433 if ( word.charAt(0) === '"' ) {
4434 var m = word.match( /^"(.*)"$/ );
4435 word = m ? m[1] : word;
4436 }
4437
4438 return word.replace('"', '');
4439 } );
4440
4441 search = '^(?=.*?'+a.join( ')(?=.*?' )+').*$';
4442 }
4443
4444 return new RegExp( search, caseInsensitive ? 'i' : '' );
4445 }
4446
4447
4448 /**
4449 * Escape a string such that it can be used in a regular expression
4450 * @param {string} sVal string to escape
4451 * @returns {string} escaped string
4452 * @memberof DataTable#oApi
4453 */
4454 var _fnEscapeRegex = DataTable.util.escapeRegex;
4455
4456 var __filter_div = $('<div>')[0];
4457 var __filter_div_textContent = __filter_div.textContent !== undefined;
4458
4459 // Update the filtering data for each row if needed (by invalidation or first run)
4460 function _fnFilterData ( settings )
4461 {
4462 var columns = settings.aoColumns;
4463 var column;
4464 var i, j, ien, jen, filterData, cellData, row;
4465 var fomatters = DataTable.ext.type.search;
4466 var wasInvalidated = false;
4467
4468 for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
4469 row = settings.aoData[i];
4470
4471 if ( ! row._aFilterData ) {
4472 filterData = [];
4473
4474 for ( j=0, jen=columns.length ; j<jen ; j++ ) {
4475 column = columns[j];
4476
4477 if ( column.bSearchable ) {
4478 cellData = _fnGetCellData( settings, i, j, 'filter' );
4479
4480 if ( fomatters[ column.sType ] ) {
4481 cellData = fomatters[ column.sType ]( cellData );
4482 }
4483
4484 // Search in DataTables 1.10 is string based. In 1.11 this
4485 // should be altered to also allow strict type checking.
4486 if ( cellData === null ) {
4487 cellData = '';
4488 }
4489
4490 if ( typeof cellData !== 'string' && cellData.toString ) {
4491 cellData = cellData.toString();
4492 }
4493 }
4494 else {
4495 cellData = '';
4496 }
4497
4498 // If it looks like there is an HTML entity in the string,
4499 // attempt to decode it so sorting works as expected. Note that
4500 // we could use a single line of jQuery to do this, but the DOM
4501 // method used here is much faster http://jsperf.com/html-decode
4502 if ( cellData.indexOf && cellData.indexOf('&') !== -1 ) {
4503 __filter_div.innerHTML = cellData;
4504 cellData = __filter_div_textContent ?
4505 __filter_div.textContent :
4506 __filter_div.innerText;
4507 }
4508
4509 if ( cellData.replace ) {
4510 cellData = cellData.replace(/[\r\n]/g, '');
4511 }
4512
4513 filterData.push( cellData );
4514 }
4515
4516 row._aFilterData = filterData;
4517 row._sFilterRow = filterData.join(' ');
4518 wasInvalidated = true;
4519 }
4520 }
4521
4522 return wasInvalidated;
4523 }
4524
4525
4526 /**
4527 * Convert from the internal Hungarian notation to camelCase for external
4528 * interaction
4529 * @param {object} obj Object to convert
4530 * @returns {object} Inverted object
4531 * @memberof DataTable#oApi
4532 */
4533 function _fnSearchToCamel ( obj )
4534 {
4535 return {
4536 search: obj.sSearch,
4537 smart: obj.bSmart,
4538 regex: obj.bRegex,
4539 caseInsensitive: obj.bCaseInsensitive
4540 };
4541 }
4542
4543
4544
4545 /**
4546 * Convert from camelCase notation to the internal Hungarian. We could use the
4547 * Hungarian convert function here, but this is cleaner
4548 * @param {object} obj Object to convert
4549 * @returns {object} Inverted object
4550 * @memberof DataTable#oApi
4551 */
4552 function _fnSearchToHung ( obj )
4553 {
4554 return {
4555 sSearch: obj.search,
4556 bSmart: obj.smart,
4557 bRegex: obj.regex,
4558 bCaseInsensitive: obj.caseInsensitive
4559 };
4560 }
4561
4562 /**
4563 * Generate the node required for the info display
4564 * @param {object} oSettings dataTables settings object
4565 * @returns {node} Information element
4566 * @memberof DataTable#oApi
4567 */
4568 function _fnFeatureHtmlInfo ( settings )
4569 {
4570 var
4571 tid = settings.sTableId,
4572 nodes = settings.aanFeatures.i,
4573 n = $('<div/>', {
4574 'class': settings.oClasses.sInfo,
4575 'id': ! nodes ? tid+'_info' : null
4576 } );
4577
4578 if ( ! nodes ) {
4579 // Update display on each draw
4580 settings.aoDrawCallback.push( {
4581 "fn": _fnUpdateInfo,
4582 "sName": "information"
4583 } );
4584
4585 n
4586 .attr( 'role', 'status' )
4587 .attr( 'aria-live', 'polite' );
4588
4589 // Table is described by our info div
4590 $(settings.nTable).attr( 'aria-describedby', tid+'_info' );
4591 }
4592
4593 return n[0];
4594 }
4595
4596
4597 /**
4598 * Update the information elements in the display
4599 * @param {object} settings dataTables settings object
4600 * @memberof DataTable#oApi
4601 */
4602 function _fnUpdateInfo ( settings )
4603 {
4604 /* Show information about the table */
4605 var nodes = settings.aanFeatures.i;
4606 if ( nodes.length === 0 ) {
4607 return;
4608 }
4609
4610 var
4611 lang = settings.oLanguage,
4612 start = settings._iDisplayStart+1,
4613 end = settings.fnDisplayEnd(),
4614 max = settings.fnRecordsTotal(),
4615 total = settings.fnRecordsDisplay(),
4616 out = total ?
4617 lang.sInfo :
4618 lang.sInfoEmpty;
4619
4620 if ( total !== max ) {
4621 /* Record set after filtering */
4622 out += ' ' + lang.sInfoFiltered;
4623 }
4624
4625 // Convert the macros
4626 out += lang.sInfoPostFix;
4627 out = _fnInfoMacros( settings, out );
4628
4629 var callback = lang.fnInfoCallback;
4630 if ( callback !== null ) {
4631 out = callback.call( settings.oInstance,
4632 settings, start, end, max, total, out
4633 );
4634 }
4635
4636 $(nodes).html( out );
4637 }
4638
4639
4640 function _fnInfoMacros ( settings, str )
4641 {
4642 // When infinite scrolling, we are always starting at 1. _iDisplayStart is used only
4643 // internally
4644 var
4645 formatter = settings.fnFormatNumber,
4646 start = settings._iDisplayStart+1,
4647 len = settings._iDisplayLength,
4648 vis = settings.fnRecordsDisplay(),
4649 all = len === -1;
4650
4651 return str.
4652 replace(/_START_/g, formatter.call( settings, start ) ).
4653 replace(/_END_/g, formatter.call( settings, settings.fnDisplayEnd() ) ).
4654 replace(/_MAX_/g, formatter.call( settings, settings.fnRecordsTotal() ) ).
4655 replace(/_TOTAL_/g, formatter.call( settings, vis ) ).
4656 replace(/_PAGE_/g, formatter.call( settings, all ? 1 : Math.ceil( start / len ) ) ).
4657 replace(/_PAGES_/g, formatter.call( settings, all ? 1 : Math.ceil( vis / len ) ) );
4658 }
4659
4660
4661
4662 /**
4663 * Draw the table for the first time, adding all required features
4664 * @param {object} settings dataTables settings object
4665 * @memberof DataTable#oApi
4666 */
4667 function _fnInitialise ( settings )
4668 {
4669 var i, iLen, iAjaxStart=settings.iInitDisplayStart;
4670 var columns = settings.aoColumns, column;
4671 var features = settings.oFeatures;
4672 var deferLoading = settings.bDeferLoading; // value modified by the draw
4673
4674 /* Ensure that the table data is fully initialised */
4675 if ( ! settings.bInitialised ) {
4676 setTimeout( function(){ _fnInitialise( settings ); }, 200 );
4677 return;
4678 }
4679
4680 /* Show the display HTML options */
4681 _fnAddOptionsHtml( settings );
4682
4683 /* Build and draw the header / footer for the table */
4684 _fnBuildHead( settings );
4685 _fnDrawHead( settings, settings.aoHeader );
4686 _fnDrawHead( settings, settings.aoFooter );
4687
4688 /* Okay to show that something is going on now */
4689 _fnProcessingDisplay( settings, true );
4690
4691 /* Calculate sizes for columns */
4692 if ( features.bAutoWidth ) {
4693 _fnCalculateColumnWidths( settings );
4694 }
4695
4696 for ( i=0, iLen=columns.length ; i<iLen ; i++ ) {
4697 column = columns[i];
4698
4699 if ( column.sWidth ) {
4700 column.nTh.style.width = _fnStringToCss( column.sWidth );
4701 }
4702 }
4703
4704 _fnCallbackFire( settings, null, 'preInit', [settings] );
4705
4706 // If there is default sorting required - let's do it. The sort function
4707 // will do the drawing for us. Otherwise we draw the table regardless of the
4708 // Ajax source - this allows the table to look initialised for Ajax sourcing
4709 // data (show 'loading' message possibly)
4710 _fnReDraw( settings );
4711
4712 // Server-side processing init complete is done by _fnAjaxUpdateDraw
4713 var dataSrc = _fnDataSource( settings );
4714 if ( dataSrc != 'ssp' || deferLoading ) {
4715 // if there is an ajax source load the data
4716 if ( dataSrc == 'ajax' ) {
4717 _fnBuildAjax( settings, [], function(json) {
4718 var aData = _fnAjaxDataSrc( settings, json );
4719
4720 // Got the data - add it to the table
4721 for ( i=0 ; i<aData.length ; i++ ) {
4722 _fnAddData( settings, aData[i] );
4723 }
4724
4725 // Reset the init display for cookie saving. We've already done
4726 // a filter, and therefore cleared it before. So we need to make
4727 // it appear 'fresh'
4728 settings.iInitDisplayStart = iAjaxStart;
4729
4730 _fnReDraw( settings );
4731
4732 _fnProcessingDisplay( settings, false );
4733 _fnInitComplete( settings, json );
4734 }, settings );
4735 }
4736 else {
4737 _fnProcessingDisplay( settings, false );
4738 _fnInitComplete( settings );
4739 }
4740 }
4741 }
4742
4743
4744 /**
4745 * Draw the table for the first time, adding all required features
4746 * @param {object} oSettings dataTables settings object
4747 * @param {object} [json] JSON from the server that completed the table, if using Ajax source
4748 * with client-side processing (optional)
4749 * @memberof DataTable#oApi
4750 */
4751 function _fnInitComplete ( settings, json )
4752 {
4753 settings._bInitComplete = true;
4754
4755 // When data was added after the initialisation (data or Ajax) we need to
4756 // calculate the column sizing
4757 if ( json || settings.oInit.aaData ) {
4758 _fnAdjustColumnSizing( settings );
4759 }
4760
4761 _fnCallbackFire( settings, null, 'plugin-init', [settings, json] );
4762 _fnCallbackFire( settings, 'aoInitComplete', 'init', [settings, json] );
4763 }
4764
4765
4766 function _fnLengthChange ( settings, val )
4767 {
4768 var len = parseInt( val, 10 );
4769 settings._iDisplayLength = len;
4770
4771 _fnLengthOverflow( settings );
4772
4773 // Fire length change event
4774 _fnCallbackFire( settings, null, 'length', [settings, len] );
4775 }
4776
4777
4778 /**
4779 * Generate the node required for user display length changing
4780 * @param {object} settings dataTables settings object
4781 * @returns {node} Display length feature node
4782 * @memberof DataTable#oApi
4783 */
4784 function _fnFeatureHtmlLength ( settings )
4785 {
4786 var
4787 classes = settings.oClasses,
4788 tableId = settings.sTableId,
4789 menu = settings.aLengthMenu,
4790 d2 = $.isArray( menu[0] ),
4791 lengths = d2 ? menu[0] : menu,
4792 language = d2 ? menu[1] : menu;
4793
4794 var select = $('<select/>', {
4795 'name': tableId+'_length',
4796 'aria-controls': tableId,
4797 'class': classes.sLengthSelect
4798 } );
4799
4800 for ( var i=0, ien=lengths.length ; i<ien ; i++ ) {
4801 select[0][ i ] = new Option( language[i], lengths[i] );
4802 }
4803
4804 var div = $('<div><label/></div>').addClass( classes.sLength );
4805 if ( ! settings.aanFeatures.l ) {
4806 div[0].id = tableId+'_length';
4807 }
4808
4809 div.children().append(
4810 settings.oLanguage.sLengthMenu.replace( '_MENU_', select[0].outerHTML )
4811 );
4812
4813 // Can't use `select` variable as user might provide their own and the
4814 // reference is broken by the use of outerHTML
4815 $('select', div)
4816 .val( settings._iDisplayLength )
4817 .on( 'change.DT', function(e) {
4818 _fnLengthChange( settings, $(this).val() );
4819 _fnDraw( settings );
4820 } );
4821
4822 // Update node value whenever anything changes the table's length
4823 $(settings.nTable).on( 'length.dt.DT', function (e, s, len) {
4824 if ( settings === s ) {
4825 $('select', div).val( len );
4826 }
4827 } );
4828
4829 return div[0];
4830 }
4831
4832
4833
4834 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
4835 * Note that most of the paging logic is done in
4836 * DataTable.ext.pager
4837 */
4838
4839 /**
4840 * Generate the node required for default pagination
4841 * @param {object} oSettings dataTables settings object
4842 * @returns {node} Pagination feature node
4843 * @memberof DataTable#oApi
4844 */
4845 function _fnFeatureHtmlPaginate ( settings )
4846 {
4847 var
4848 type = settings.sPaginationType,
4849 plugin = DataTable.ext.pager[ type ],
4850 modern = typeof plugin === 'function',
4851 redraw = function( settings ) {
4852 _fnDraw( settings );
4853 },
4854 node = $('<div/>').addClass( settings.oClasses.sPaging + type )[0],
4855 features = settings.aanFeatures;
4856
4857 if ( ! modern ) {
4858 plugin.fnInit( settings, node, redraw );
4859 }
4860
4861 /* Add a draw callback for the pagination on first instance, to update the paging display */
4862 if ( ! features.p )
4863 {
4864 node.id = settings.sTableId+'_paginate';
4865
4866 settings.aoDrawCallback.push( {
4867 "fn": function( settings ) {
4868 if ( modern ) {
4869 var
4870 start = settings._iDisplayStart,
4871 len = settings._iDisplayLength,
4872 visRecords = settings.fnRecordsDisplay(),
4873 all = len === -1,
4874 page = all ? 0 : Math.ceil( start / len ),
4875 pages = all ? 1 : Math.ceil( visRecords / len ),
4876 buttons = plugin(page, pages),
4877 i, ien;
4878
4879 for ( i=0, ien=features.p.length ; i<ien ; i++ ) {
4880 _fnRenderer( settings, 'pageButton' )(
4881 settings, features.p[i], i, buttons, page, pages
4882 );
4883 }
4884 }
4885 else {
4886 plugin.fnUpdate( settings, redraw );
4887 }
4888 },
4889 "sName": "pagination"
4890 } );
4891 }
4892
4893 return node;
4894 }
4895
4896
4897 /**
4898 * Alter the display settings to change the page
4899 * @param {object} settings DataTables settings object
4900 * @param {string|int} action Paging action to take: "first", "previous",
4901 * "next" or "last" or page number to jump to (integer)
4902 * @param [bool] redraw Automatically draw the update or not
4903 * @returns {bool} true page has changed, false - no change
4904 * @memberof DataTable#oApi
4905 */
4906 function _fnPageChange ( settings, action, redraw )
4907 {
4908 var
4909 start = settings._iDisplayStart,
4910 len = settings._iDisplayLength,
4911 records = settings.fnRecordsDisplay();
4912
4913 if ( records === 0 || len === -1 )
4914 {
4915 start = 0;
4916 }
4917 else if ( typeof action === "number" )
4918 {
4919 start = action * len;
4920
4921 if ( start > records )
4922 {
4923 start = 0;
4924 }
4925 }
4926 else if ( action == "first" )
4927 {
4928 start = 0;
4929 }
4930 else if ( action == "previous" )
4931 {
4932 start = len >= 0 ?
4933 start - len :
4934 0;
4935
4936 if ( start < 0 )
4937 {
4938 start = 0;
4939 }
4940 }
4941 else if ( action == "next" )
4942 {
4943 if ( start + len < records )
4944 {
4945 start += len;
4946 }
4947 }
4948 else if ( action == "last" )
4949 {
4950 start = Math.floor( (records-1) / len) * len;
4951 }
4952 else
4953 {
4954 _fnLog( settings, 0, "Unknown paging action: "+action, 5 );
4955 }
4956
4957 var changed = settings._iDisplayStart !== start;
4958 settings._iDisplayStart = start;
4959
4960 if ( changed ) {
4961 _fnCallbackFire( settings, null, 'page', [settings] );
4962
4963 if ( redraw ) {
4964 _fnDraw( settings );
4965 }
4966 }
4967
4968 return changed;
4969 }
4970
4971
4972
4973 /**
4974 * Generate the node required for the processing node
4975 * @param {object} settings dataTables settings object
4976 * @returns {node} Processing element
4977 * @memberof DataTable#oApi
4978 */
4979 function _fnFeatureHtmlProcessing ( settings )
4980 {
4981 return $('<div/>', {
4982 'id': ! settings.aanFeatures.r ? settings.sTableId+'_processing' : null,
4983 'class': settings.oClasses.sProcessing
4984 } )
4985 .html( settings.oLanguage.sProcessing )
4986 .insertBefore( settings.nTable )[0];
4987 }
4988
4989
4990 /**
4991 * Display or hide the processing indicator
4992 * @param {object} settings dataTables settings object
4993 * @param {bool} show Show the processing indicator (true) or not (false)
4994 * @memberof DataTable#oApi
4995 */
4996 function _fnProcessingDisplay ( settings, show )
4997 {
4998 if ( settings.oFeatures.bProcessing ) {
4999 $(settings.aanFeatures.r).css( 'display', show ? 'block' : 'none' );
5000 }
5001
5002 _fnCallbackFire( settings, null, 'processing', [settings, show] );
5003 }
5004
5005 /**
5006 * Add any control elements for the table - specifically scrolling
5007 * @param {object} settings dataTables settings object
5008 * @returns {node} Node to add to the DOM
5009 * @memberof DataTable#oApi
5010 */
5011 function _fnFeatureHtmlTable ( settings )
5012 {
5013 var table = $(settings.nTable);
5014
5015 // Add the ARIA grid role to the table
5016 table.attr( 'role', 'grid' );
5017
5018 // Scrolling from here on in
5019 var scroll = settings.oScroll;
5020
5021 if ( scroll.sX === '' && scroll.sY === '' ) {
5022 return settings.nTable;
5023 }
5024
5025 var scrollX = scroll.sX;
5026 var scrollY = scroll.sY;
5027 var classes = settings.oClasses;
5028 var caption = table.children('caption');
5029 var captionSide = caption.length ? caption[0]._captionSide : null;
5030 var headerClone = $( table[0].cloneNode(false) );
5031 var footerClone = $( table[0].cloneNode(false) );
5032 var footer = table.children('tfoot');
5033 var _div = '<div/>';
5034 var size = function ( s ) {
5035 return !s ? null : _fnStringToCss( s );
5036 };
5037
5038 if ( ! footer.length ) {
5039 footer = null;
5040 }
5041
5042 /*
5043 * The HTML structure that we want to generate in this function is:
5044 * div - scroller
5045 * div - scroll head
5046 * div - scroll head inner
5047 * table - scroll head table
5048 * thead - thead
5049 * div - scroll body
5050 * table - table (master table)
5051 * thead - thead clone for sizing
5052 * tbody - tbody
5053 * div - scroll foot
5054 * div - scroll foot inner
5055 * table - scroll foot table
5056 * tfoot - tfoot
5057 */
5058 var scroller = $( _div, { 'class': classes.sScrollWrapper } )
5059 .append(
5060 $(_div, { 'class': classes.sScrollHead } )
5061 .css( {
5062 overflow: 'hidden',
5063 position: 'relative',
5064 border: 0,
5065 width: scrollX ? size(scrollX) : '100%'
5066 } )
5067 .append(
5068 $(_div, { 'class': classes.sScrollHeadInner } )
5069 .css( {
5070 'box-sizing': 'content-box',
5071 width: scroll.sXInner || '100%'
5072 } )
5073 .append(
5074 headerClone
5075 .removeAttr('id')
5076 .css( 'margin-left', 0 )
5077 .append( captionSide === 'top' ? caption : null )
5078 .append(
5079 table.children('thead')
5080 )
5081 )
5082 )
5083 )
5084 .append(
5085 $(_div, { 'class': classes.sScrollBody } )
5086 .css( {
5087 position: 'relative',
5088 overflow: 'auto',
5089 width: size( scrollX )
5090 } )
5091 .append( table )
5092 );
5093
5094 if ( footer ) {
5095 scroller.append(
5096 $(_div, { 'class': classes.sScrollFoot } )
5097 .css( {
5098 overflow: 'hidden',
5099 border: 0,
5100 width: scrollX ? size(scrollX) : '100%'
5101 } )
5102 .append(
5103 $(_div, { 'class': classes.sScrollFootInner } )
5104 .append(
5105 footerClone
5106 .removeAttr('id')
5107 .css( 'margin-left', 0 )
5108 .append( captionSide === 'bottom' ? caption : null )
5109 .append(
5110 table.children('tfoot')
5111 )
5112 )
5113 )
5114 );
5115 }
5116
5117 var children = scroller.children();
5118 var scrollHead = children[0];
5119 var scrollBody = children[1];
5120 var scrollFoot = footer ? children[2] : null;
5121
5122 // When the body is scrolled, then we also want to scroll the headers
5123 if ( scrollX ) {
5124 $(scrollBody).on( 'scroll.DT', function (e) {
5125 var scrollLeft = this.scrollLeft;
5126
5127 scrollHead.scrollLeft = scrollLeft;
5128
5129 if ( footer ) {
5130 scrollFoot.scrollLeft = scrollLeft;
5131 }
5132 } );
5133 }
5134
5135 $(scrollBody).css(
5136 scrollY && scroll.bCollapse ? 'max-height' : 'height',
5137 scrollY
5138 );
5139
5140 settings.nScrollHead = scrollHead;
5141 settings.nScrollBody = scrollBody;
5142 settings.nScrollFoot = scrollFoot;
5143
5144 // On redraw - align columns
5145 settings.aoDrawCallback.push( {
5146 "fn": _fnScrollDraw,
5147 "sName": "scrolling"
5148 } );
5149
5150 return scroller[0];
5151 }
5152
5153
5154
5155 /**
5156 * Update the header, footer and body tables for resizing - i.e. column
5157 * alignment.
5158 *
5159 * Welcome to the most horrible function DataTables. The process that this
5160 * function follows is basically:
5161 * 1. Re-create the table inside the scrolling div
5162 * 2. Take live measurements from the DOM
5163 * 3. Apply the measurements to align the columns
5164 * 4. Clean up
5165 *
5166 * @param {object} settings dataTables settings object
5167 * @memberof DataTable#oApi
5168 */
5169 function _fnScrollDraw ( settings )
5170 {
5171 // Given that this is such a monster function, a lot of variables are use
5172 // to try and keep the minimised size as small as possible
5173 var
5174 scroll = settings.oScroll,
5175 scrollX = scroll.sX,
5176 scrollXInner = scroll.sXInner,
5177 scrollY = scroll.sY,
5178 barWidth = scroll.iBarWidth,
5179 divHeader = $(settings.nScrollHead),
5180 divHeaderStyle = divHeader[0].style,
5181 divHeaderInner = divHeader.children('div'),
5182 divHeaderInnerStyle = divHeaderInner[0].style,
5183 divHeaderTable = divHeaderInner.children('table'),
5184 divBodyEl = settings.nScrollBody,
5185 divBody = $(divBodyEl),
5186 divBodyStyle = divBodyEl.style,
5187 divFooter = $(settings.nScrollFoot),
5188 divFooterInner = divFooter.children('div'),
5189 divFooterTable = divFooterInner.children('table'),
5190 header = $(settings.nTHead),
5191 table = $(settings.nTable),
5192 tableEl = table[0],
5193 tableStyle = tableEl.style,
5194 footer = settings.nTFoot ? $(settings.nTFoot) : null,
5195 browser = settings.oBrowser,
5196 ie67 = browser.bScrollOversize,
5197 dtHeaderCells = _pluck( settings.aoColumns, 'nTh' ),
5198 headerTrgEls, footerTrgEls,
5199 headerSrcEls, footerSrcEls,
5200 headerCopy, footerCopy,
5201 headerWidths=[], footerWidths=[],
5202 headerContent=[], footerContent=[],
5203 idx, correction, sanityWidth,
5204 zeroOut = function(nSizer) {
5205 var style = nSizer.style;
5206 style.paddingTop = "0";
5207 style.paddingBottom = "0";
5208 style.borderTopWidth = "0";
5209 style.borderBottomWidth = "0";
5210 style.height = 0;
5211 };
5212
5213 // If the scrollbar visibility has changed from the last draw, we need to
5214 // adjust the column sizes as the table width will have changed to account
5215 // for the scrollbar
5216 var scrollBarVis = divBodyEl.scrollHeight > divBodyEl.clientHeight;
5217
5218 if ( settings.scrollBarVis !== scrollBarVis && settings.scrollBarVis !== undefined ) {
5219 settings.scrollBarVis = scrollBarVis;
5220 _fnAdjustColumnSizing( settings );
5221 return; // adjust column sizing will call this function again
5222 }
5223 else {
5224 settings.scrollBarVis = scrollBarVis;
5225 }
5226
5227 /*
5228 * 1. Re-create the table inside the scrolling div
5229 */
5230
5231 // Remove the old minimised thead and tfoot elements in the inner table
5232 table.children('thead, tfoot').remove();
5233
5234 if ( footer ) {
5235 footerCopy = footer.clone().prependTo( table );
5236 footerTrgEls = footer.find('tr'); // the original tfoot is in its own table and must be sized
5237 footerSrcEls = footerCopy.find('tr');
5238 }
5239
5240 // Clone the current header and footer elements and then place it into the inner table
5241 headerCopy = header.clone().prependTo( table );
5242 headerTrgEls = header.find('tr'); // original header is in its own table
5243 headerSrcEls = headerCopy.find('tr');
5244 headerCopy.find('th, td').removeAttr('tabindex');
5245
5246
5247 /*
5248 * 2. Take live measurements from the DOM - do not alter the DOM itself!
5249 */
5250
5251 // Remove old sizing and apply the calculated column widths
5252 // Get the unique column headers in the newly created (cloned) header. We want to apply the
5253 // calculated sizes to this header
5254 if ( ! scrollX )
5255 {
5256 divBodyStyle.width = '100%';
5257 divHeader[0].style.width = '100%';
5258 }
5259
5260 $.each( _fnGetUniqueThs( settings, headerCopy ), function ( i, el ) {
5261 idx = _fnVisibleToColumnIndex( settings, i );
5262 el.style.width = settings.aoColumns[idx].sWidth;
5263 } );
5264
5265 if ( footer ) {
5266 _fnApplyToChildren( function(n) {
5267 n.style.width = "";
5268 }, footerSrcEls );
5269 }
5270
5271 // Size the table as a whole
5272 sanityWidth = table.outerWidth();
5273 if ( scrollX === "" ) {
5274 // No x scrolling
5275 tableStyle.width = "100%";
5276
5277 // IE7 will make the width of the table when 100% include the scrollbar
5278 // - which is shouldn't. When there is a scrollbar we need to take this
5279 // into account.
5280 if ( ie67 && (table.find('tbody').height() > divBodyEl.offsetHeight ||
5281 divBody.css('overflow-y') == "scroll")
5282 ) {
5283 tableStyle.width = _fnStringToCss( table.outerWidth() - barWidth);
5284 }
5285
5286 // Recalculate the sanity width
5287 sanityWidth = table.outerWidth();
5288 }
5289 else if ( scrollXInner !== "" ) {
5290 // legacy x scroll inner has been given - use it
5291 tableStyle.width = _fnStringToCss(scrollXInner);
5292
5293 // Recalculate the sanity width
5294 sanityWidth = table.outerWidth();
5295 }
5296
5297 // Hidden header should have zero height, so remove padding and borders. Then
5298 // set the width based on the real headers
5299
5300 // Apply all styles in one pass
5301 _fnApplyToChildren( zeroOut, headerSrcEls );
5302
5303 // Read all widths in next pass
5304 _fnApplyToChildren( function(nSizer) {
5305 headerContent.push( nSizer.innerHTML );
5306 headerWidths.push( _fnStringToCss( $(nSizer).css('width') ) );
5307 }, headerSrcEls );
5308
5309 // Apply all widths in final pass
5310 _fnApplyToChildren( function(nToSize, i) {
5311 // Only apply widths to the DataTables detected header cells - this
5312 // prevents complex headers from having contradictory sizes applied
5313 if ( $.inArray( nToSize, dtHeaderCells ) !== -1 ) {
5314 nToSize.style.width = headerWidths[i];
5315 }
5316 }, headerTrgEls );
5317
5318 $(headerSrcEls).height(0);
5319
5320 /* Same again with the footer if we have one */
5321 if ( footer )
5322 {
5323 _fnApplyToChildren( zeroOut, footerSrcEls );
5324
5325 _fnApplyToChildren( function(nSizer) {
5326 footerContent.push( nSizer.innerHTML );
5327 footerWidths.push( _fnStringToCss( $(nSizer).css('width') ) );
5328 }, footerSrcEls );
5329
5330 _fnApplyToChildren( function(nToSize, i) {
5331 nToSize.style.width = footerWidths[i];
5332 }, footerTrgEls );
5333
5334 $(footerSrcEls).height(0);
5335 }
5336
5337
5338 /*
5339 * 3. Apply the measurements
5340 */
5341
5342 // "Hide" the header and footer that we used for the sizing. We need to keep
5343 // the content of the cell so that the width applied to the header and body
5344 // both match, but we want to hide it completely. We want to also fix their
5345 // width to what they currently are
5346 _fnApplyToChildren( function(nSizer, i) {
5347 nSizer.innerHTML = '<div class="dataTables_sizing" style="height:0;overflow:hidden;">'+headerContent[i]+'</div>';
5348 nSizer.style.width = headerWidths[i];
5349 }, headerSrcEls );
5350
5351 if ( footer )
5352 {
5353 _fnApplyToChildren( function(nSizer, i) {
5354 nSizer.innerHTML = '<div class="dataTables_sizing" style="height:0;overflow:hidden;">'+footerContent[i]+'</div>';
5355 nSizer.style.width = footerWidths[i];
5356 }, footerSrcEls );
5357 }
5358
5359 // Sanity check that the table is of a sensible width. If not then we are going to get
5360 // misalignment - try to prevent this by not allowing the table to shrink below its min width
5361 if ( table.outerWidth() < sanityWidth )
5362 {
5363 // The min width depends upon if we have a vertical scrollbar visible or not */
5364 correction = ((divBodyEl.scrollHeight > divBodyEl.offsetHeight ||
5365 divBody.css('overflow-y') == "scroll")) ?
5366 sanityWidth+barWidth :
5367 sanityWidth;
5368
5369 // IE6/7 are a law unto themselves...
5370 if ( ie67 && (divBodyEl.scrollHeight >
5371 divBodyEl.offsetHeight || divBody.css('overflow-y') == "scroll")
5372 ) {
5373 tableStyle.width = _fnStringToCss( correction-barWidth );
5374 }
5375
5376 // And give the user a warning that we've stopped the table getting too small
5377 if ( scrollX === "" || scrollXInner !== "" ) {
5378 _fnLog( settings, 1, 'Possible column misalignment', 6 );
5379 }
5380 }
5381 else
5382 {
5383 correction = '100%';
5384 }
5385
5386 // Apply to the container elements
5387 divBodyStyle.width = _fnStringToCss( correction );
5388 divHeaderStyle.width = _fnStringToCss( correction );
5389
5390 if ( footer ) {
5391 settings.nScrollFoot.style.width = _fnStringToCss( correction );
5392 }
5393
5394
5395 /*
5396 * 4. Clean up
5397 */
5398 if ( ! scrollY ) {
5399 /* IE7< puts a vertical scrollbar in place (when it shouldn't be) due to subtracting
5400 * the scrollbar height from the visible display, rather than adding it on. We need to
5401 * set the height in order to sort this. Don't want to do it in any other browsers.
5402 */
5403 if ( ie67 ) {
5404 divBodyStyle.height = _fnStringToCss( tableEl.offsetHeight+barWidth );
5405 }
5406 }
5407
5408 /* Finally set the width's of the header and footer tables */
5409 var iOuterWidth = table.outerWidth();
5410 divHeaderTable[0].style.width = _fnStringToCss( iOuterWidth );
5411 divHeaderInnerStyle.width = _fnStringToCss( iOuterWidth );
5412
5413 // Figure out if there are scrollbar present - if so then we need a the header and footer to
5414 // provide a bit more space to allow "overflow" scrolling (i.e. past the scrollbar)
5415 var bScrolling = table.height() > divBodyEl.clientHeight || divBody.css('overflow-y') == "scroll";
5416 var padding = 'padding' + (browser.bScrollbarLeft ? 'Left' : 'Right' );
5417 divHeaderInnerStyle[ padding ] = bScrolling ? barWidth+"px" : "0px";
5418
5419 if ( footer ) {
5420 divFooterTable[0].style.width = _fnStringToCss( iOuterWidth );
5421 divFooterInner[0].style.width = _fnStringToCss( iOuterWidth );
5422 divFooterInner[0].style[padding] = bScrolling ? barWidth+"px" : "0px";
5423 }
5424
5425 // Correct DOM ordering for colgroup - comes before the thead
5426 table.children('colgroup').insertBefore( table.children('thead') );
5427
5428 /* Adjust the position of the header in case we loose the y-scrollbar */
5429 divBody.scroll();
5430
5431 // If sorting or filtering has occurred, jump the scrolling back to the top
5432 // only if we aren't holding the position
5433 if ( (settings.bSorted || settings.bFiltered) && ! settings._drawHold ) {
5434 divBodyEl.scrollTop = 0;
5435 }
5436 }
5437
5438
5439
5440 /**
5441 * Apply a given function to the display child nodes of an element array (typically
5442 * TD children of TR rows
5443 * @param {function} fn Method to apply to the objects
5444 * @param array {nodes} an1 List of elements to look through for display children
5445 * @param array {nodes} an2 Another list (identical structure to the first) - optional
5446 * @memberof DataTable#oApi
5447 */
5448 function _fnApplyToChildren( fn, an1, an2 )
5449 {
5450 var index=0, i=0, iLen=an1.length;
5451 var nNode1, nNode2;
5452
5453 while ( i < iLen ) {
5454 nNode1 = an1[i].firstChild;
5455 nNode2 = an2 ? an2[i].firstChild : null;
5456
5457 while ( nNode1 ) {
5458 if ( nNode1.nodeType === 1 ) {
5459 if ( an2 ) {
5460 fn( nNode1, nNode2, index );
5461 }
5462 else {
5463 fn( nNode1, index );
5464 }
5465
5466 index++;
5467 }
5468
5469 nNode1 = nNode1.nextSibling;
5470 nNode2 = an2 ? nNode2.nextSibling : null;
5471 }
5472
5473 i++;
5474 }
5475 }
5476
5477
5478
5479 var __re_html_remove = /<.*?>/g;
5480
5481
5482 /**
5483 * Calculate the width of columns for the table
5484 * @param {object} oSettings dataTables settings object
5485 * @memberof DataTable#oApi
5486 */
5487 function _fnCalculateColumnWidths ( oSettings )
5488 {
5489 var
5490 table = oSettings.nTable,
5491 columns = oSettings.aoColumns,
5492 scroll = oSettings.oScroll,
5493 scrollY = scroll.sY,
5494 scrollX = scroll.sX,
5495 scrollXInner = scroll.sXInner,
5496 columnCount = columns.length,
5497 visibleColumns = _fnGetColumns( oSettings, 'bVisible' ),
5498 headerCells = $('th', oSettings.nTHead),
5499 tableWidthAttr = table.getAttribute('width'), // from DOM element
5500 tableContainer = table.parentNode,
5501 userInputs = false,
5502 i, column, columnIdx, width, outerWidth,
5503 browser = oSettings.oBrowser,
5504 ie67 = browser.bScrollOversize;
5505
5506 var styleWidth = table.style.width;
5507 if ( styleWidth && styleWidth.indexOf('%') !== -1 ) {
5508 tableWidthAttr = styleWidth;
5509 }
5510
5511 /* Convert any user input sizes into pixel sizes */
5512 for ( i=0 ; i<visibleColumns.length ; i++ ) {
5513 column = columns[ visibleColumns[i] ];
5514
5515 if ( column.sWidth !== null ) {
5516 column.sWidth = _fnConvertToWidth( column.sWidthOrig, tableContainer );
5517
5518 userInputs = true;
5519 }
5520 }
5521
5522 /* If the number of columns in the DOM equals the number that we have to
5523 * process in DataTables, then we can use the offsets that are created by
5524 * the web- browser. No custom sizes can be set in order for this to happen,
5525 * nor scrolling used
5526 */
5527 if ( ie67 || ! userInputs && ! scrollX && ! scrollY &&
5528 columnCount == _fnVisbleColumns( oSettings ) &&
5529 columnCount == headerCells.length
5530 ) {
5531 for ( i=0 ; i<columnCount ; i++ ) {
5532 var colIdx = _fnVisibleToColumnIndex( oSettings, i );
5533
5534 if ( colIdx !== null ) {
5535 columns[ colIdx ].sWidth = _fnStringToCss( headerCells.eq(i).width() );
5536 }
5537 }
5538 }
5539 else
5540 {
5541 // Otherwise construct a single row, worst case, table with the widest
5542 // node in the data, assign any user defined widths, then insert it into
5543 // the DOM and allow the browser to do all the hard work of calculating
5544 // table widths
5545 var tmpTable = $(table).clone() // don't use cloneNode - IE8 will remove events on the main table
5546 .css( 'visibility', 'hidden' )
5547 .removeAttr( 'id' );
5548
5549 // Clean up the table body
5550 tmpTable.find('tbody tr').remove();
5551 var tr = $('<tr/>').appendTo( tmpTable.find('tbody') );
5552
5553 // Clone the table header and footer - we can't use the header / footer
5554 // from the cloned table, since if scrolling is active, the table's
5555 // real header and footer are contained in different table tags
5556 tmpTable.find('thead, tfoot').remove();
5557 tmpTable
5558 .append( $(oSettings.nTHead).clone() )
5559 .append( $(oSettings.nTFoot).clone() );
5560
5561 // Remove any assigned widths from the footer (from scrolling)
5562 tmpTable.find('tfoot th, tfoot td').css('width', '');
5563
5564 // Apply custom sizing to the cloned header
5565 headerCells = _fnGetUniqueThs( oSettings, tmpTable.find('thead')[0] );
5566
5567 for ( i=0 ; i<visibleColumns.length ; i++ ) {
5568 column = columns[ visibleColumns[i] ];
5569
5570 headerCells[i].style.width = column.sWidthOrig !== null && column.sWidthOrig !== '' ?
5571 _fnStringToCss( column.sWidthOrig ) :
5572 '';
5573
5574 // For scrollX we need to force the column width otherwise the
5575 // browser will collapse it. If this width is smaller than the
5576 // width the column requires, then it will have no effect
5577 if ( column.sWidthOrig && scrollX ) {
5578 $( headerCells[i] ).append( $('<div/>').css( {
5579 width: column.sWidthOrig,
5580 margin: 0,
5581 padding: 0,
5582 border: 0,
5583 height: 1
5584 } ) );
5585 }
5586 }
5587
5588 // Find the widest cell for each column and put it into the table
5589 if ( oSettings.aoData.length ) {
5590 for ( i=0 ; i<visibleColumns.length ; i++ ) {
5591 columnIdx = visibleColumns[i];
5592 column = columns[ columnIdx ];
5593
5594 $( _fnGetWidestNode( oSettings, columnIdx ) )
5595 .clone( false )
5596 .append( column.sContentPadding )
5597 .appendTo( tr );
5598 }
5599 }
5600
5601 // Tidy the temporary table - remove name attributes so there aren't
5602 // duplicated in the dom (radio elements for example)
5603 $('[name]', tmpTable).removeAttr('name');
5604
5605 // Table has been built, attach to the document so we can work with it.
5606 // A holding element is used, positioned at the top of the container
5607 // with minimal height, so it has no effect on if the container scrolls
5608 // or not. Otherwise it might trigger scrolling when it actually isn't
5609 // needed
5610 var holder = $('<div/>').css( scrollX || scrollY ?
5611 {
5612 position: 'absolute',
5613 top: 0,
5614 left: 0,
5615 height: 1,
5616 right: 0,
5617 overflow: 'hidden'
5618 } :
5619 {}
5620 )
5621 .append( tmpTable )
5622 .appendTo( tableContainer );
5623
5624 // When scrolling (X or Y) we want to set the width of the table as
5625 // appropriate. However, when not scrolling leave the table width as it
5626 // is. This results in slightly different, but I think correct behaviour
5627 if ( scrollX && scrollXInner ) {
5628 tmpTable.width( scrollXInner );
5629 }
5630 else if ( scrollX ) {
5631 tmpTable.css( 'width', 'auto' );
5632 tmpTable.removeAttr('width');
5633
5634 // If there is no width attribute or style, then allow the table to
5635 // collapse
5636 if ( tmpTable.width() < tableContainer.clientWidth && tableWidthAttr ) {
5637 tmpTable.width( tableContainer.clientWidth );
5638 }
5639 }
5640 else if ( scrollY ) {
5641 tmpTable.width( tableContainer.clientWidth );
5642 }
5643 else if ( tableWidthAttr ) {
5644 tmpTable.width( tableWidthAttr );
5645 }
5646
5647 // Get the width of each column in the constructed table - we need to
5648 // know the inner width (so it can be assigned to the other table's
5649 // cells) and the outer width so we can calculate the full width of the
5650 // table. This is safe since DataTables requires a unique cell for each
5651 // column, but if ever a header can span multiple columns, this will
5652 // need to be modified.
5653 var total = 0;
5654 for ( i=0 ; i<visibleColumns.length ; i++ ) {
5655 var cell = $(headerCells[i]);
5656 var border = cell.outerWidth() - cell.width();
5657
5658 // Use getBounding... where possible (not IE8-) because it can give
5659 // sub-pixel accuracy, which we then want to round up!
5660 var bounding = browser.bBounding ?
5661 Math.ceil( headerCells[i].getBoundingClientRect().width ) :
5662 cell.outerWidth();
5663
5664 // Total is tracked to remove any sub-pixel errors as the outerWidth
5665 // of the table might not equal the total given here (IE!).
5666 total += bounding;
5667
5668 // Width for each column to use
5669 columns[ visibleColumns[i] ].sWidth = _fnStringToCss( bounding - border );
5670 }
5671
5672 table.style.width = _fnStringToCss( total );
5673
5674 // Finished with the table - ditch it
5675 holder.remove();
5676 }
5677
5678 // If there is a width attr, we want to attach an event listener which
5679 // allows the table sizing to automatically adjust when the window is
5680 // resized. Use the width attr rather than CSS, since we can't know if the
5681 // CSS is a relative value or absolute - DOM read is always px.
5682 if ( tableWidthAttr ) {
5683 table.style.width = _fnStringToCss( tableWidthAttr );
5684 }
5685
5686 if ( (tableWidthAttr || scrollX) && ! oSettings._reszEvt ) {
5687 var bindResize = function () {
5688 $(window).on('resize.DT-'+oSettings.sInstance, _fnThrottle( function () {
5689 _fnAdjustColumnSizing( oSettings );
5690 } ) );
5691 };
5692
5693 // IE6/7 will crash if we bind a resize event handler on page load.
5694 // To be removed in 1.11 which drops IE6/7 support
5695 if ( ie67 ) {
5696 setTimeout( bindResize, 1000 );
5697 }
5698 else {
5699 bindResize();
5700 }
5701
5702 oSettings._reszEvt = true;
5703 }
5704 }
5705
5706
5707 /**
5708 * Throttle the calls to a function. Arguments and context are maintained for
5709 * the throttled function
5710 * @param {function} fn Function to be called
5711 * @param {int} [freq=200] call frequency in mS
5712 * @returns {function} wrapped function
5713 * @memberof DataTable#oApi
5714 */
5715 var _fnThrottle = DataTable.util.throttle;
5716
5717
5718 /**
5719 * Convert a CSS unit width to pixels (e.g. 2em)
5720 * @param {string} width width to be converted
5721 * @param {node} parent parent to get the with for (required for relative widths) - optional
5722 * @returns {int} width in pixels
5723 * @memberof DataTable#oApi
5724 */
5725 function _fnConvertToWidth ( width, parent )
5726 {
5727 if ( ! width ) {
5728 return 0;
5729 }
5730
5731 var n = $('<div/>')
5732 .css( 'width', _fnStringToCss( width ) )
5733 .appendTo( parent || document.body );
5734
5735 var val = n[0].offsetWidth;
5736 n.remove();
5737
5738 return val;
5739 }
5740
5741
5742 /**
5743 * Get the widest node
5744 * @param {object} settings dataTables settings object
5745 * @param {int} colIdx column of interest
5746 * @returns {node} widest table node
5747 * @memberof DataTable#oApi
5748 */
5749 function _fnGetWidestNode( settings, colIdx )
5750 {
5751 var idx = _fnGetMaxLenString( settings, colIdx );
5752 if ( idx < 0 ) {
5753 return null;
5754 }
5755
5756 var data = settings.aoData[ idx ];
5757 return ! data.nTr ? // Might not have been created when deferred rendering
5758 $('<td/>').html( _fnGetCellData( settings, idx, colIdx, 'display' ) )[0] :
5759 data.anCells[ colIdx ];
5760 }
5761
5762
5763 /**
5764 * Get the maximum strlen for each data column
5765 * @param {object} settings dataTables settings object
5766 * @param {int} colIdx column of interest
5767 * @returns {string} max string length for each column
5768 * @memberof DataTable#oApi
5769 */
5770 function _fnGetMaxLenString( settings, colIdx )
5771 {
5772 var s, max=-1, maxIdx = -1;
5773
5774 for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
5775 s = _fnGetCellData( settings, i, colIdx, 'display' )+'';
5776 s = s.replace( __re_html_remove, '' );
5777 s = s.replace( /&nbsp;/g, ' ' );
5778
5779 if ( s.length > max ) {
5780 max = s.length;
5781 maxIdx = i;
5782 }
5783 }
5784
5785 return maxIdx;
5786 }
5787
5788
5789 /**
5790 * Append a CSS unit (only if required) to a string
5791 * @param {string} value to css-ify
5792 * @returns {string} value with css unit
5793 * @memberof DataTable#oApi
5794 */
5795 function _fnStringToCss( s )
5796 {
5797 if ( s === null ) {
5798 return '0px';
5799 }
5800
5801 if ( typeof s == 'number' ) {
5802 return s < 0 ?
5803 '0px' :
5804 s+'px';
5805 }
5806
5807 // Check it has a unit character already
5808 return s.match(/\d$/) ?
5809 s+'px' :
5810 s;
5811 }
5812
5813
5814
5815 function _fnSortFlatten ( settings )
5816 {
5817 var
5818 i, iLen, k, kLen,
5819 aSort = [],
5820 aiOrig = [],
5821 aoColumns = settings.aoColumns,
5822 aDataSort, iCol, sType, srcCol,
5823 fixed = settings.aaSortingFixed,
5824 fixedObj = $.isPlainObject( fixed ),
5825 nestedSort = [],
5826 add = function ( a ) {
5827 if ( a.length && ! $.isArray( a[0] ) ) {
5828 // 1D array
5829 nestedSort.push( a );
5830 }
5831 else {
5832 // 2D array
5833 $.merge( nestedSort, a );
5834 }
5835 };
5836
5837 // Build the sort array, with pre-fix and post-fix options if they have been
5838 // specified
5839 if ( $.isArray( fixed ) ) {
5840 add( fixed );
5841 }
5842
5843 if ( fixedObj && fixed.pre ) {
5844 add( fixed.pre );
5845 }
5846
5847 add( settings.aaSorting );
5848
5849 if (fixedObj && fixed.post ) {
5850 add( fixed.post );
5851 }
5852
5853 for ( i=0 ; i<nestedSort.length ; i++ )
5854 {
5855 srcCol = nestedSort[i][0];
5856 aDataSort = aoColumns[ srcCol ].aDataSort;
5857
5858 for ( k=0, kLen=aDataSort.length ; k<kLen ; k++ )
5859 {
5860 iCol = aDataSort[k];
5861 sType = aoColumns[ iCol ].sType || 'string';
5862
5863 if ( nestedSort[i]._idx === undefined ) {
5864 nestedSort[i]._idx = $.inArray( nestedSort[i][1], aoColumns[iCol].asSorting );
5865 }
5866
5867 aSort.push( {
5868 src: srcCol,
5869 col: iCol,
5870 dir: nestedSort[i][1],
5871 index: nestedSort[i]._idx,
5872 type: sType,
5873 formatter: DataTable.ext.type.order[ sType+"-pre" ]
5874 } );
5875 }
5876 }
5877
5878 return aSort;
5879 }
5880
5881 /**
5882 * Change the order of the table
5883 * @param {object} oSettings dataTables settings object
5884 * @memberof DataTable#oApi
5885 * @todo This really needs split up!
5886 */
5887 function _fnSort ( oSettings )
5888 {
5889 var
5890 i, ien, iLen, j, jLen, k, kLen,
5891 sDataType, nTh,
5892 aiOrig = [],
5893 oExtSort = DataTable.ext.type.order,
5894 aoData = oSettings.aoData,
5895 aoColumns = oSettings.aoColumns,
5896 aDataSort, data, iCol, sType, oSort,
5897 formatters = 0,
5898 sortCol,
5899 displayMaster = oSettings.aiDisplayMaster,
5900 aSort;
5901
5902 // Resolve any column types that are unknown due to addition or invalidation
5903 // @todo Can this be moved into a 'data-ready' handler which is called when
5904 // data is going to be used in the table?
5905 _fnColumnTypes( oSettings );
5906
5907 aSort = _fnSortFlatten( oSettings );
5908
5909 for ( i=0, ien=aSort.length ; i<ien ; i++ ) {
5910 sortCol = aSort[i];
5911
5912 // Track if we can use the fast sort algorithm
5913 if ( sortCol.formatter ) {
5914 formatters++;
5915 }
5916
5917 // Load the data needed for the sort, for each cell
5918 _fnSortData( oSettings, sortCol.col );
5919 }
5920
5921 /* No sorting required if server-side or no sorting array */
5922 if ( _fnDataSource( oSettings ) != 'ssp' && aSort.length !== 0 )
5923 {
5924 // Create a value - key array of the current row positions such that we can use their
5925 // current position during the sort, if values match, in order to perform stable sorting
5926 for ( i=0, iLen=displayMaster.length ; i<iLen ; i++ ) {
5927 aiOrig[ displayMaster[i] ] = i;
5928 }
5929
5930 /* Do the sort - here we want multi-column sorting based on a given data source (column)
5931 * and sorting function (from oSort) in a certain direction. It's reasonably complex to
5932 * follow on it's own, but this is what we want (example two column sorting):
5933 * fnLocalSorting = function(a,b){
5934 * var iTest;
5935 * iTest = oSort['string-asc']('data11', 'data12');
5936 * if (iTest !== 0)
5937 * return iTest;
5938 * iTest = oSort['numeric-desc']('data21', 'data22');
5939 * if (iTest !== 0)
5940 * return iTest;
5941 * return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
5942 * }
5943 * Basically we have a test for each sorting column, if the data in that column is equal,
5944 * test the next column. If all columns match, then we use a numeric sort on the row
5945 * positions in the original data array to provide a stable sort.
5946 *
5947 * Note - I know it seems excessive to have two sorting methods, but the first is around
5948 * 15% faster, so the second is only maintained for backwards compatibility with sorting
5949 * methods which do not have a pre-sort formatting function.
5950 */
5951 if ( formatters === aSort.length ) {
5952 // All sort types have formatting functions
5953 displayMaster.sort( function ( a, b ) {
5954 var
5955 x, y, k, test, sort,
5956 len=aSort.length,
5957 dataA = aoData[a]._aSortData,
5958 dataB = aoData[b]._aSortData;
5959
5960 for ( k=0 ; k<len ; k++ ) {
5961 sort = aSort[k];
5962
5963 x = dataA[ sort.col ];
5964 y = dataB[ sort.col ];
5965
5966 test = x<y ? -1 : x>y ? 1 : 0;
5967 if ( test !== 0 ) {
5968 return sort.dir === 'asc' ? test : -test;
5969 }
5970 }
5971
5972 x = aiOrig[a];
5973 y = aiOrig[b];
5974 return x<y ? -1 : x>y ? 1 : 0;
5975 } );
5976 }
5977 else {
5978 // Depreciated - remove in 1.11 (providing a plug-in option)
5979 // Not all sort types have formatting methods, so we have to call their sorting
5980 // methods.
5981 displayMaster.sort( function ( a, b ) {
5982 var
5983 x, y, k, l, test, sort, fn,
5984 len=aSort.length,
5985 dataA = aoData[a]._aSortData,
5986 dataB = aoData[b]._aSortData;
5987
5988 for ( k=0 ; k<len ; k++ ) {
5989 sort = aSort[k];
5990
5991 x = dataA[ sort.col ];
5992 y = dataB[ sort.col ];
5993
5994 fn = oExtSort[ sort.type+"-"+sort.dir ] || oExtSort[ "string-"+sort.dir ];
5995 test = fn( x, y );
5996 if ( test !== 0 ) {
5997 return test;
5998 }
5999 }
6000
6001 x = aiOrig[a];
6002 y = aiOrig[b];
6003 return x<y ? -1 : x>y ? 1 : 0;
6004 } );
6005 }
6006 }
6007
6008 /* Tell the draw function that we have sorted the data */
6009 oSettings.bSorted = true;
6010 }
6011
6012
6013 function _fnSortAria ( settings )
6014 {
6015 var label;
6016 var nextSort;
6017 var columns = settings.aoColumns;
6018 var aSort = _fnSortFlatten( settings );
6019 var oAria = settings.oLanguage.oAria;
6020
6021 // ARIA attributes - need to loop all columns, to update all (removing old
6022 // attributes as needed)
6023 for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
6024 {
6025 var col = columns[i];
6026 var asSorting = col.asSorting;
6027 var sTitle = col.sTitle.replace( /<.*?>/g, "" );
6028 var th = col.nTh;
6029
6030 // IE7 is throwing an error when setting these properties with jQuery's
6031 // attr() and removeAttr() methods...
6032 th.removeAttribute('aria-sort');
6033
6034 /* In ARIA only the first sorting column can be marked as sorting - no multi-sort option */
6035 if ( col.bSortable ) {
6036 if ( aSort.length > 0 && aSort[0].col == i ) {
6037 th.setAttribute('aria-sort', aSort[0].dir=="asc" ? "ascending" : "descending" );
6038 nextSort = asSorting[ aSort[0].index+1 ] || asSorting[0];
6039 }
6040 else {
6041 nextSort = asSorting[0];
6042 }
6043
6044 label = sTitle + ( nextSort === "asc" ?
6045 oAria.sSortAscending :
6046 oAria.sSortDescending
6047 );
6048 }
6049 else {
6050 label = sTitle;
6051 }
6052
6053 th.setAttribute('aria-label', label);
6054 }
6055 }
6056
6057
6058 /**
6059 * Function to run on user sort request
6060 * @param {object} settings dataTables settings object
6061 * @param {node} attachTo node to attach the handler to
6062 * @param {int} colIdx column sorting index
6063 * @param {boolean} [append=false] Append the requested sort to the existing
6064 * sort if true (i.e. multi-column sort)
6065 * @param {function} [callback] callback function
6066 * @memberof DataTable#oApi
6067 */
6068 function _fnSortListener ( settings, colIdx, append, callback )
6069 {
6070 var col = settings.aoColumns[ colIdx ];
6071 var sorting = settings.aaSorting;
6072 var asSorting = col.asSorting;
6073 var nextSortIdx;
6074 var next = function ( a, overflow ) {
6075 var idx = a._idx;
6076 if ( idx === undefined ) {
6077 idx = $.inArray( a[1], asSorting );
6078 }
6079
6080 return idx+1 < asSorting.length ?
6081 idx+1 :
6082 overflow ?
6083 null :
6084 0;
6085 };
6086
6087 // Convert to 2D array if needed
6088 if ( typeof sorting[0] === 'number' ) {
6089 sorting = settings.aaSorting = [ sorting ];
6090 }
6091
6092 // If appending the sort then we are multi-column sorting
6093 if ( append && settings.oFeatures.bSortMulti ) {
6094 // Are we already doing some kind of sort on this column?
6095 var sortIdx = $.inArray( colIdx, _pluck(sorting, '0') );
6096
6097 if ( sortIdx !== -1 ) {
6098 // Yes, modify the sort
6099 nextSortIdx = next( sorting[sortIdx], true );
6100
6101 if ( nextSortIdx === null && sorting.length === 1 ) {
6102 nextSortIdx = 0; // can't remove sorting completely
6103 }
6104
6105 if ( nextSortIdx === null ) {
6106 sorting.splice( sortIdx, 1 );
6107 }
6108 else {
6109 sorting[sortIdx][1] = asSorting[ nextSortIdx ];
6110 sorting[sortIdx]._idx = nextSortIdx;
6111 }
6112 }
6113 else {
6114 // No sort on this column yet
6115 sorting.push( [ colIdx, asSorting[0], 0 ] );
6116 sorting[sorting.length-1]._idx = 0;
6117 }
6118 }
6119 else if ( sorting.length && sorting[0][0] == colIdx ) {
6120 // Single column - already sorting on this column, modify the sort
6121 nextSortIdx = next( sorting[0] );
6122
6123 sorting.length = 1;
6124 sorting[0][1] = asSorting[ nextSortIdx ];
6125 sorting[0]._idx = nextSortIdx;
6126 }
6127 else {
6128 // Single column - sort only on this column
6129 sorting.length = 0;
6130 sorting.push( [ colIdx, asSorting[0] ] );
6131 sorting[0]._idx = 0;
6132 }
6133
6134 // Run the sort by calling a full redraw
6135 _fnReDraw( settings );
6136
6137 // callback used for async user interaction
6138 if ( typeof callback == 'function' ) {
6139 callback( settings );
6140 }
6141 }
6142
6143
6144 /**
6145 * Attach a sort handler (click) to a node
6146 * @param {object} settings dataTables settings object
6147 * @param {node} attachTo node to attach the handler to
6148 * @param {int} colIdx column sorting index
6149 * @param {function} [callback] callback function
6150 * @memberof DataTable#oApi
6151 */
6152 function _fnSortAttachListener ( settings, attachTo, colIdx, callback )
6153 {
6154 var col = settings.aoColumns[ colIdx ];
6155
6156 _fnBindAction( attachTo, {}, function (e) {
6157 /* If the column is not sortable - don't to anything */
6158 if ( col.bSortable === false ) {
6159 return;
6160 }
6161
6162 // If processing is enabled use a timeout to allow the processing
6163 // display to be shown - otherwise to it synchronously
6164 if ( settings.oFeatures.bProcessing ) {
6165 _fnProcessingDisplay( settings, true );
6166
6167 setTimeout( function() {
6168 _fnSortListener( settings, colIdx, e.shiftKey, callback );
6169
6170 // In server-side processing, the draw callback will remove the
6171 // processing display
6172 if ( _fnDataSource( settings ) !== 'ssp' ) {
6173 _fnProcessingDisplay( settings, false );
6174 }
6175 }, 0 );
6176 }
6177 else {
6178 _fnSortListener( settings, colIdx, e.shiftKey, callback );
6179 }
6180 } );
6181 }
6182
6183
6184 /**
6185 * Set the sorting classes on table's body, Note: it is safe to call this function
6186 * when bSort and bSortClasses are false
6187 * @param {object} oSettings dataTables settings object
6188 * @memberof DataTable#oApi
6189 */
6190 function _fnSortingClasses( settings )
6191 {
6192 var oldSort = settings.aLastSort;
6193 var sortClass = settings.oClasses.sSortColumn;
6194 var sort = _fnSortFlatten( settings );
6195 var features = settings.oFeatures;
6196 var i, ien, colIdx;
6197
6198 if ( features.bSort && features.bSortClasses ) {
6199 // Remove old sorting classes
6200 for ( i=0, ien=oldSort.length ; i<ien ; i++ ) {
6201 colIdx = oldSort[i].src;
6202
6203 // Remove column sorting
6204 $( _pluck( settings.aoData, 'anCells', colIdx ) )
6205 .removeClass( sortClass + (i<2 ? i+1 : 3) );
6206 }
6207
6208 // Add new column sorting
6209 for ( i=0, ien=sort.length ; i<ien ; i++ ) {
6210 colIdx = sort[i].src;
6211
6212 $( _pluck( settings.aoData, 'anCells', colIdx ) )
6213 .addClass( sortClass + (i<2 ? i+1 : 3) );
6214 }
6215 }
6216
6217 settings.aLastSort = sort;
6218 }
6219
6220
6221 // Get the data to sort a column, be it from cache, fresh (populating the
6222 // cache), or from a sort formatter
6223 function _fnSortData( settings, idx )
6224 {
6225 // Custom sorting function - provided by the sort data type
6226 var column = settings.aoColumns[ idx ];
6227 var customSort = DataTable.ext.order[ column.sSortDataType ];
6228 var customData;
6229
6230 if ( customSort ) {
6231 customData = customSort.call( settings.oInstance, settings, idx,
6232 _fnColumnIndexToVisible( settings, idx )
6233 );
6234 }
6235
6236 // Use / populate cache
6237 var row, cellData;
6238 var formatter = DataTable.ext.type.order[ column.sType+"-pre" ];
6239
6240 for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
6241 row = settings.aoData[i];
6242
6243 if ( ! row._aSortData ) {
6244 row._aSortData = [];
6245 }
6246
6247 if ( ! row._aSortData[idx] || customSort ) {
6248 cellData = customSort ?
6249 customData[i] : // If there was a custom sort function, use data from there
6250 _fnGetCellData( settings, i, idx, 'sort' );
6251
6252 row._aSortData[ idx ] = formatter ?
6253 formatter( cellData ) :
6254 cellData;
6255 }
6256 }
6257 }
6258
6259
6260
6261 /**
6262 * Save the state of a table
6263 * @param {object} oSettings dataTables settings object
6264 * @memberof DataTable#oApi
6265 */
6266 function _fnSaveState ( settings )
6267 {
6268 if ( !settings.oFeatures.bStateSave || settings.bDestroying )
6269 {
6270 return;
6271 }
6272
6273 /* Store the interesting variables */
6274 var state = {
6275 time: +new Date(),
6276 start: settings._iDisplayStart,
6277 length: settings._iDisplayLength,
6278 order: $.extend( true, [], settings.aaSorting ),
6279 search: _fnSearchToCamel( settings.oPreviousSearch ),
6280 columns: $.map( settings.aoColumns, function ( col, i ) {
6281 return {
6282 visible: col.bVisible,
6283 search: _fnSearchToCamel( settings.aoPreSearchCols[i] )
6284 };
6285 } )
6286 };
6287
6288 _fnCallbackFire( settings, "aoStateSaveParams", 'stateSaveParams', [settings, state] );
6289
6290 settings.oSavedState = state;
6291 settings.fnStateSaveCallback.call( settings.oInstance, settings, state );
6292 }
6293
6294
6295 /**
6296 * Attempt to load a saved table state
6297 * @param {object} oSettings dataTables settings object
6298 * @param {object} oInit DataTables init object so we can override settings
6299 * @param {function} callback Callback to execute when the state has been loaded
6300 * @memberof DataTable#oApi
6301 */
6302 function _fnLoadState ( settings, oInit, callback )
6303 {
6304 var i, ien;
6305 var columns = settings.aoColumns;
6306 var loaded = function ( s ) {
6307 if ( ! s || ! s.time ) {
6308 callback();
6309 return;
6310 }
6311
6312 // Allow custom and plug-in manipulation functions to alter the saved data set and
6313 // cancelling of loading by returning false
6314 var abStateLoad = _fnCallbackFire( settings, 'aoStateLoadParams', 'stateLoadParams', [settings, state] );
6315 if ( $.inArray( false, abStateLoad ) !== -1 ) {
6316 callback();
6317 return;
6318 }
6319
6320 // Reject old data
6321 var duration = settings.iStateDuration;
6322 if ( duration > 0 && s.time < +new Date() - (duration*1000) ) {
6323 callback();
6324 return;
6325 }
6326
6327 // Number of columns have changed - all bets are off, no restore of settings
6328 if ( s.columns && columns.length !== s.columns.length ) {
6329 callback();
6330 return;
6331 }
6332
6333 // Store the saved state so it might be accessed at any time
6334 settings.oLoadedState = $.extend( true, {}, state );
6335
6336 // Restore key features - todo - for 1.11 this needs to be done by
6337 // subscribed events
6338 if ( s.start !== undefined ) {
6339 settings._iDisplayStart = s.start;
6340 settings.iInitDisplayStart = s.start;
6341 }
6342 if ( s.length !== undefined ) {
6343 settings._iDisplayLength = s.length;
6344 }
6345
6346 // Order
6347 if ( s.order !== undefined ) {
6348 settings.aaSorting = [];
6349 $.each( s.order, function ( i, col ) {
6350 settings.aaSorting.push( col[0] >= columns.length ?
6351 [ 0, col[1] ] :
6352 col
6353 );
6354 } );
6355 }
6356
6357 // Search
6358 if ( s.search !== undefined ) {
6359 $.extend( settings.oPreviousSearch, _fnSearchToHung( s.search ) );
6360 }
6361
6362 // Columns
6363 //
6364 if ( s.columns ) {
6365 for ( i=0, ien=s.columns.length ; i<ien ; i++ ) {
6366 var col = s.columns[i];
6367
6368 // Visibility
6369 if ( col.visible !== undefined ) {
6370 columns[i].bVisible = col.visible;
6371 }
6372
6373 // Search
6374 if ( col.search !== undefined ) {
6375 $.extend( settings.aoPreSearchCols[i], _fnSearchToHung( col.search ) );
6376 }
6377 }
6378 }
6379
6380 _fnCallbackFire( settings, 'aoStateLoaded', 'stateLoaded', [settings, state] );
6381 callback();
6382 }
6383
6384 if ( ! settings.oFeatures.bStateSave ) {
6385 callback();
6386 return;
6387 }
6388
6389 var state = settings.fnStateLoadCallback.call( settings.oInstance, settings, loaded );
6390
6391 if ( state !== undefined ) {
6392 loaded( state );
6393 }
6394 // otherwise, wait for the loaded callback to be executed
6395 }
6396
6397
6398 /**
6399 * Return the settings object for a particular table
6400 * @param {node} table table we are using as a dataTable
6401 * @returns {object} Settings object - or null if not found
6402 * @memberof DataTable#oApi
6403 */
6404 function _fnSettingsFromNode ( table )
6405 {
6406 var settings = DataTable.settings;
6407 var idx = $.inArray( table, _pluck( settings, 'nTable' ) );
6408
6409 return idx !== -1 ?
6410 settings[ idx ] :
6411 null;
6412 }
6413
6414
6415 /**
6416 * Log an error message
6417 * @param {object} settings dataTables settings object
6418 * @param {int} level log error messages, or display them to the user
6419 * @param {string} msg error message
6420 * @param {int} tn Technical note id to get more information about the error.
6421 * @memberof DataTable#oApi
6422 */
6423 function _fnLog( settings, level, msg, tn )
6424 {
6425 msg = 'DataTables warning: '+
6426 (settings ? 'table id='+settings.sTableId+' - ' : '')+msg;
6427
6428 if ( tn ) {
6429 msg += '. For more information about this error, please see '+
6430 'http://datatables.net/tn/'+tn;
6431 }
6432
6433 if ( ! level ) {
6434 // Backwards compatibility pre 1.10
6435 var ext = DataTable.ext;
6436 var type = ext.sErrMode || ext.errMode;
6437
6438 if ( settings ) {
6439 _fnCallbackFire( settings, null, 'error', [ settings, tn, msg ] );
6440 }
6441
6442 if ( type == 'alert' ) {
6443 alert( msg );
6444 }
6445 else if ( type == 'throw' ) {
6446 throw new Error(msg);
6447 }
6448 else if ( typeof type == 'function' ) {
6449 type( settings, tn, msg );
6450 }
6451 }
6452 else if ( window.console && console.log ) {
6453 console.log( msg );
6454 }
6455 }
6456
6457
6458 /**
6459 * See if a property is defined on one object, if so assign it to the other object
6460 * @param {object} ret target object
6461 * @param {object} src source object
6462 * @param {string} name property
6463 * @param {string} [mappedName] name to map too - optional, name used if not given
6464 * @memberof DataTable#oApi
6465 */
6466 function _fnMap( ret, src, name, mappedName )
6467 {
6468 if ( $.isArray( name ) ) {
6469 $.each( name, function (i, val) {
6470 if ( $.isArray( val ) ) {
6471 _fnMap( ret, src, val[0], val[1] );
6472 }
6473 else {
6474 _fnMap( ret, src, val );
6475 }
6476 } );
6477
6478 return;
6479 }
6480
6481 if ( mappedName === undefined ) {
6482 mappedName = name;
6483 }
6484
6485 if ( src[name] !== undefined ) {
6486 ret[mappedName] = src[name];
6487 }
6488 }
6489
6490
6491 /**
6492 * Extend objects - very similar to jQuery.extend, but deep copy objects, and
6493 * shallow copy arrays. The reason we need to do this, is that we don't want to
6494 * deep copy array init values (such as aaSorting) since the dev wouldn't be
6495 * able to override them, but we do want to deep copy arrays.
6496 * @param {object} out Object to extend
6497 * @param {object} extender Object from which the properties will be applied to
6498 * out
6499 * @param {boolean} breakRefs If true, then arrays will be sliced to take an
6500 * independent copy with the exception of the `data` or `aaData` parameters
6501 * if they are present. This is so you can pass in a collection to
6502 * DataTables and have that used as your data source without breaking the
6503 * references
6504 * @returns {object} out Reference, just for convenience - out === the return.
6505 * @memberof DataTable#oApi
6506 * @todo This doesn't take account of arrays inside the deep copied objects.
6507 */
6508 function _fnExtend( out, extender, breakRefs )
6509 {
6510 var val;
6511
6512 for ( var prop in extender ) {
6513 if ( extender.hasOwnProperty(prop) ) {
6514 val = extender[prop];
6515
6516 if ( $.isPlainObject( val ) ) {
6517 if ( ! $.isPlainObject( out[prop] ) ) {
6518 out[prop] = {};
6519 }
6520 $.extend( true, out[prop], val );
6521 }
6522 else if ( breakRefs && prop !== 'data' && prop !== 'aaData' && $.isArray(val) ) {
6523 out[prop] = val.slice();
6524 }
6525 else {
6526 out[prop] = val;
6527 }
6528 }
6529 }
6530
6531 return out;
6532 }
6533
6534
6535 /**
6536 * Bind an event handers to allow a click or return key to activate the callback.
6537 * This is good for accessibility since a return on the keyboard will have the
6538 * same effect as a click, if the element has focus.
6539 * @param {element} n Element to bind the action to
6540 * @param {object} oData Data object to pass to the triggered function
6541 * @param {function} fn Callback function for when the event is triggered
6542 * @memberof DataTable#oApi
6543 */
6544 function _fnBindAction( n, oData, fn )
6545 {
6546 $(n)
6547 .on( 'click.DT', oData, function (e) {
6548 n.blur(); // Remove focus outline for mouse users
6549 fn(e);
6550 } )
6551 .on( 'keypress.DT', oData, function (e){
6552 if ( e.which === 13 ) {
6553 e.preventDefault();
6554 fn(e);
6555 }
6556 } )
6557 .on( 'selectstart.DT', function () {
6558 /* Take the brutal approach to cancelling text selection */
6559 return false;
6560 } );
6561 }
6562
6563
6564 /**
6565 * Register a callback function. Easily allows a callback function to be added to
6566 * an array store of callback functions that can then all be called together.
6567 * @param {object} oSettings dataTables settings object
6568 * @param {string} sStore Name of the array storage for the callbacks in oSettings
6569 * @param {function} fn Function to be called back
6570 * @param {string} sName Identifying name for the callback (i.e. a label)
6571 * @memberof DataTable#oApi
6572 */
6573 function _fnCallbackReg( oSettings, sStore, fn, sName )
6574 {
6575 if ( fn )
6576 {
6577 oSettings[sStore].push( {
6578 "fn": fn,
6579 "sName": sName
6580 } );
6581 }
6582 }
6583
6584
6585 /**
6586 * Fire callback functions and trigger events. Note that the loop over the
6587 * callback array store is done backwards! Further note that you do not want to
6588 * fire off triggers in time sensitive applications (for example cell creation)
6589 * as its slow.
6590 * @param {object} settings dataTables settings object
6591 * @param {string} callbackArr Name of the array storage for the callbacks in
6592 * oSettings
6593 * @param {string} eventName Name of the jQuery custom event to trigger. If
6594 * null no trigger is fired
6595 * @param {array} args Array of arguments to pass to the callback function /
6596 * trigger
6597 * @memberof DataTable#oApi
6598 */
6599 function _fnCallbackFire( settings, callbackArr, eventName, args )
6600 {
6601 var ret = [];
6602
6603 if ( callbackArr ) {
6604 ret = $.map( settings[callbackArr].slice().reverse(), function (val, i) {
6605 return val.fn.apply( settings.oInstance, args );
6606 } );
6607 }
6608
6609 if ( eventName !== null ) {
6610 var e = $.Event( eventName+'.dt' );
6611
6612 $(settings.nTable).trigger( e, args );
6613
6614 ret.push( e.result );
6615 }
6616
6617 return ret;
6618 }
6619
6620
6621 function _fnLengthOverflow ( settings )
6622 {
6623 var
6624 start = settings._iDisplayStart,
6625 end = settings.fnDisplayEnd(),
6626 len = settings._iDisplayLength;
6627
6628 /* If we have space to show extra rows (backing up from the end point - then do so */
6629 if ( start >= end )
6630 {
6631 start = end - len;
6632 }
6633
6634 // Keep the start record on the current page
6635 start -= (start % len);
6636
6637 if ( len === -1 || start < 0 )
6638 {
6639 start = 0;
6640 }
6641
6642 settings._iDisplayStart = start;
6643 }
6644
6645
6646 function _fnRenderer( settings, type )
6647 {
6648 var renderer = settings.renderer;
6649 var host = DataTable.ext.renderer[type];
6650
6651 if ( $.isPlainObject( renderer ) && renderer[type] ) {
6652 // Specific renderer for this type. If available use it, otherwise use
6653 // the default.
6654 return host[renderer[type]] || host._;
6655 }
6656 else if ( typeof renderer === 'string' ) {
6657 // Common renderer - if there is one available for this type use it,
6658 // otherwise use the default
6659 return host[renderer] || host._;
6660 }
6661
6662 // Use the default
6663 return host._;
6664 }
6665
6666
6667 /**
6668 * Detect the data source being used for the table. Used to simplify the code
6669 * a little (ajax) and to make it compress a little smaller.
6670 *
6671 * @param {object} settings dataTables settings object
6672 * @returns {string} Data source
6673 * @memberof DataTable#oApi
6674 */
6675 function _fnDataSource ( settings )
6676 {
6677 if ( settings.oFeatures.bServerSide ) {
6678 return 'ssp';
6679 }
6680 else if ( settings.ajax || settings.sAjaxSource ) {
6681 return 'ajax';
6682 }
6683 return 'dom';
6684 }
6685
6686
6687
6688
6519 6689 /**
6520 6690 * Computed structure of the DataTables API, defined by the options passed to
6521 6691 * `DataTable.Api.register()` when building the API.
6522 6692 *
6523 6693 * The structure is built in order to speed creation and extension of the Api
6524 6694 * objects since the extensions are effectively pre-parsed.
6525 6695 *
6526 6696 * The array is an array of objects with the following structure, where this
6527 6697 * base array represents the Api prototype base:
6528 6698 *
6529 6699 * [
6530 6700 * {
6531 6701 * name: 'data' -- string - Property name
6532 6702 * val: function () {}, -- function - Api method (or undefined if just an object
6533 6703 * methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result
6534 6704 * propExt: [ ... ] -- array - Array of Api object definitions to extend the property
6535 6705 * },
6536 6706 * {
6537 6707 * name: 'row'
6538 6708 * val: {},
6539 6709 * methodExt: [ ... ],
6540 6710 * propExt: [
6541 6711 * {
6542 6712 * name: 'data'
6543 6713 * val: function () {},
6544 6714 * methodExt: [ ... ],
6545 6715 * propExt: [ ... ]
6546 6716 * },
6547 6717 * ...
6548 6718 * ]
6549 6719 * }
6550 6720 * ]
6551 6721 *
6552 6722 * @type {Array}
6553 6723 * @ignore
6554 6724 */
6555 6725 var __apiStruct = [];
6556
6557
6726
6727
6558 6728 /**
6559 6729 * `Array.prototype` reference.
6560 6730 *
6561 6731 * @type object
6562 6732 * @ignore
6563 6733 */
6564 6734 var __arrayProto = Array.prototype;
6565
6566
6735
6736
6567 6737 /**
6568 6738 * Abstraction for `context` parameter of the `Api` constructor to allow it to
6569 6739 * take several different forms for ease of use.
6570 6740 *
6571 6741 * Each of the input parameter types will be converted to a DataTables settings
6572 6742 * object where possible.
6573 6743 *
6574 6744 * @param {string|node|jQuery|object} mixed DataTable identifier. Can be one
6575 6745 * of:
6576 6746 *
6577 6747 * * `string` - jQuery selector. Any DataTables' matching the given selector
6578 6748 * with be found and used.
6579 6749 * * `node` - `TABLE` node which has already been formed into a DataTable.
6580 6750 * * `jQuery` - A jQuery object of `TABLE` nodes.
6581 6751 * * `object` - DataTables settings object
6582 6752 * * `DataTables.Api` - API instance
6583 6753 * @return {array|null} Matching DataTables settings objects. `null` or
6584 6754 * `undefined` is returned if no matching DataTable is found.
6585 6755 * @ignore
6586 6756 */
6587 6757 var _toSettings = function ( mixed )
6588 6758 {
6589 6759 var idx, jq;
6590 6760 var settings = DataTable.settings;
6591 6761 var tables = $.map( settings, function (el, i) {
6592 6762 return el.nTable;
6593 6763 } );
6594
6764
6595 6765 if ( ! mixed ) {
6596 6766 return [];
6597 6767 }
6598 6768 else if ( mixed.nTable && mixed.oApi ) {
6599 6769 // DataTables settings object
6600 6770 return [ mixed ];
6601 6771 }
6602 6772 else if ( mixed.nodeName && mixed.nodeName.toLowerCase() === 'table' ) {
6603 6773 // Table node
6604 6774 idx = $.inArray( mixed, tables );
6605 6775 return idx !== -1 ? [ settings[idx] ] : null;
6606 6776 }
6607 6777 else if ( mixed && typeof mixed.settings === 'function' ) {
6608 6778 return mixed.settings().toArray();
6609 6779 }
6610 6780 else if ( typeof mixed === 'string' ) {
6611 6781 // jQuery selector
6612 6782 jq = $(mixed);
6613 6783 }
6614 6784 else if ( mixed instanceof $ ) {
6615 6785 // jQuery object (also DataTables instance)
6616 6786 jq = mixed;
6617 6787 }
6618
6788
6619 6789 if ( jq ) {
6620 6790 return jq.map( function(i) {
6621 6791 idx = $.inArray( this, tables );
6622 6792 return idx !== -1 ? settings[idx] : null;
6623 6793 } ).toArray();
6624 6794 }
6625 6795 };
6626
6627
6796
6797
6628 6798 /**
6629 6799 * DataTables API class - used to control and interface with one or more
6630 6800 * DataTables enhanced tables.
6631 6801 *
6632 6802 * The API class is heavily based on jQuery, presenting a chainable interface
6633 6803 * that you can use to interact with tables. Each instance of the API class has
6634 6804 * a "context" - i.e. the tables that it will operate on. This could be a single
6635 6805 * table, all tables on a page or a sub-set thereof.
6636 6806 *
6637 6807 * Additionally the API is designed to allow you to easily work with the data in
6638 6808 * the tables, retrieving and manipulating it as required. This is done by
6639 6809 * presenting the API class as an array like interface. The contents of the
6640 6810 * array depend upon the actions requested by each method (for example
6641 6811 * `rows().nodes()` will return an array of nodes, while `rows().data()` will
6642 6812 * return an array of objects or arrays depending upon your table's
6643 6813 * configuration). The API object has a number of array like methods (`push`,
6644 6814 * `pop`, `reverse` etc) as well as additional helper methods (`each`, `pluck`,
6645 6815 * `unique` etc) to assist your working with the data held in a table.
6646 6816 *
6647 6817 * Most methods (those which return an Api instance) are chainable, which means
6648 6818 * the return from a method call also has all of the methods available that the
6649 6819 * top level object had. For example, these two calls are equivalent:
6650 6820 *
6651 6821 * // Not chained
6652 6822 * api.row.add( {...} );
6653 6823 * api.draw();
6654 6824 *
6655 6825 * // Chained
6656 6826 * api.row.add( {...} ).draw();
6657 6827 *
6658 6828 * @class DataTable.Api
6659 6829 * @param {array|object|string|jQuery} context DataTable identifier. This is
6660 6830 * used to define which DataTables enhanced tables this API will operate on.
6661 6831 * Can be one of:
6662 6832 *
6663 6833 * * `string` - jQuery selector. Any DataTables' matching the given selector
6664 6834 * with be found and used.
6665 6835 * * `node` - `TABLE` node which has already been formed into a DataTable.
6666 6836 * * `jQuery` - A jQuery object of `TABLE` nodes.
6667 6837 * * `object` - DataTables settings object
6668 6838 * @param {array} [data] Data to initialise the Api instance with.
6669 6839 *
6670 6840 * @example
6671 6841 * // Direct initialisation during DataTables construction
6672 6842 * var api = $('#example').DataTable();
6673 6843 *
6674 6844 * @example
6675 6845 * // Initialisation using a DataTables jQuery object
6676 6846 * var api = $('#example').dataTable().api();
6677 6847 *
6678 6848 * @example
6679 6849 * // Initialisation as a constructor
6680 6850 * var api = new $.fn.DataTable.Api( 'table.dataTable' );
6681 6851 */
6682 6852 _Api = function ( context, data )
6683 6853 {
6684 if ( ! this instanceof _Api ) {
6685 throw 'DT API must be constructed as a new object';
6686 // or should it do the 'new' for the caller?
6687 // return new _Api.apply( this, arguments );
6688 }
6689
6854 if ( ! (this instanceof _Api) ) {
6855 return new _Api( context, data );
6856 }
6857
6690 6858 var settings = [];
6691 6859 var ctxSettings = function ( o ) {
6692 6860 var a = _toSettings( o );
6693 6861 if ( a ) {
6694 settings.push.apply( settings, a );
6862 settings = settings.concat( a );
6695 6863 }
6696 6864 };
6697
6865
6698 6866 if ( $.isArray( context ) ) {
6699 6867 for ( var i=0, ien=context.length ; i<ien ; i++ ) {
6700 6868 ctxSettings( context[i] );
6701 6869 }
6702 6870 }
6703 6871 else {
6704 6872 ctxSettings( context );
6705 6873 }
6706
6874
6707 6875 // Remove duplicates
6708 6876 this.context = _unique( settings );
6709
6877
6710 6878 // Initial data
6711 6879 if ( data ) {
6712 this.push.apply( this, data.toArray ? data.toArray() : data );
6713 }
6714
6880 $.merge( this, data );
6881 }
6882
6715 6883 // selector
6716 6884 this.selector = {
6717 6885 rows: null,
6718 6886 cols: null,
6719 6887 opts: null
6720 6888 };
6721
6889
6722 6890 _Api.extend( this, this, __apiStruct );
6723 6891 };
6724
6892
6725 6893 DataTable.Api = _Api;
6726
6727 _Api.prototype = /** @lends DataTables.Api */{
6728 /**
6729 * Return a new Api instance, comprised of the data held in the current
6730 * instance, join with the other array(s) and/or value(s).
6731 *
6732 * An alias for `Array.prototype.concat`.
6733 *
6734 * @type method
6735 * @param {*} value1 Arrays and/or values to concatenate.
6736 * @param {*} [...] Additional arrays and/or values to concatenate.
6737 * @returns {DataTables.Api} New API instance, comprising of the combined
6738 * array.
6739 */
6894
6895 // Don't destroy the existing prototype, just extend it. Required for jQuery 2's
6896 // isPlainObject.
6897 $.extend( _Api.prototype, {
6898 any: function ()
6899 {
6900 return this.count() !== 0;
6901 },
6902
6903
6740 6904 concat: __arrayProto.concat,
6741
6742
6905
6906
6743 6907 context: [], // array of table settings objects
6744
6745
6908
6909
6910 count: function ()
6911 {
6912 return this.flatten().length;
6913 },
6914
6915
6746 6916 each: function ( fn )
6747 6917 {
6748 6918 for ( var i=0, ien=this.length ; i<ien; i++ ) {
6749 6919 fn.call( this, this[i], i, this );
6750 6920 }
6751
6921
6752 6922 return this;
6753 6923 },
6754
6755
6924
6925
6756 6926 eq: function ( idx )
6757 6927 {
6758 6928 var ctx = this.context;
6759
6929
6760 6930 return ctx.length > idx ?
6761 6931 new _Api( ctx[idx], this[idx] ) :
6762 6932 null;
6763 6933 },
6764
6765
6934
6935
6766 6936 filter: function ( fn )
6767 6937 {
6768 6938 var a = [];
6769
6939
6770 6940 if ( __arrayProto.filter ) {
6771 6941 a = __arrayProto.filter.call( this, fn, this );
6772 6942 }
6773 6943 else {
6774 6944 // Compatibility for browsers without EMCA-252-5 (JS 1.6)
6775 6945 for ( var i=0, ien=this.length ; i<ien ; i++ ) {
6776 6946 if ( fn.call( this, this[i], i, this ) ) {
6777 6947 a.push( this[i] );
6778 6948 }
6779 6949 }
6780 6950 }
6781
6951
6782 6952 return new _Api( this.context, a );
6783 6953 },
6784
6785
6954
6955
6786 6956 flatten: function ()
6787 6957 {
6788 6958 var a = [];
6789 6959 return new _Api( this.context, a.concat.apply( a, this.toArray() ) );
6790 6960 },
6791
6792
6961
6962
6793 6963 join: __arrayProto.join,
6794
6795
6964
6965
6796 6966 indexOf: __arrayProto.indexOf || function (obj, start)
6797 6967 {
6798 6968 for ( var i=(start || 0), ien=this.length ; i<ien ; i++ ) {
6799 6969 if ( this[i] === obj ) {
6800 6970 return i;
6801 6971 }
6802 6972 }
6803 6973 return -1;
6804 6974 },
6805
6806 // Note that `alwaysNew` is internal - use iteratorNew externally
6975
6807 6976 iterator: function ( flatten, type, fn, alwaysNew ) {
6808 6977 var
6809 6978 a = [], ret,
6810 6979 i, ien, j, jen,
6811 6980 context = this.context,
6812 6981 rows, items, item,
6813 6982 selector = this.selector;
6814
6983
6815 6984 // Argument shifting
6816 6985 if ( typeof flatten === 'string' ) {
6817 6986 alwaysNew = fn;
6818 6987 fn = type;
6819 6988 type = flatten;
6820 6989 flatten = false;
6821 6990 }
6822
6991
6823 6992 for ( i=0, ien=context.length ; i<ien ; i++ ) {
6824 6993 var apiInst = new _Api( context[i] );
6825
6994
6826 6995 if ( type === 'table' ) {
6827 6996 ret = fn.call( apiInst, context[i], i );
6828
6997
6829 6998 if ( ret !== undefined ) {
6830 6999 a.push( ret );
6831 7000 }
6832 7001 }
6833 7002 else if ( type === 'columns' || type === 'rows' ) {
6834 7003 // this has same length as context - one entry for each table
6835 7004 ret = fn.call( apiInst, context[i], this[i], i );
6836
7005
6837 7006 if ( ret !== undefined ) {
6838 7007 a.push( ret );
6839 7008 }
6840 7009 }
6841 7010 else if ( type === 'column' || type === 'column-rows' || type === 'row' || type === 'cell' ) {
6842 7011 // columns and rows share the same structure.
6843 7012 // 'this' is an array of column indexes for each context
6844 7013 items = this[i];
6845
7014
6846 7015 if ( type === 'column-rows' ) {
6847 7016 rows = _selector_row_indexes( context[i], selector.opts );
6848 7017 }
6849
7018
6850 7019 for ( j=0, jen=items.length ; j<jen ; j++ ) {
6851 7020 item = items[j];
6852
7021
6853 7022 if ( type === 'cell' ) {
6854 7023 ret = fn.call( apiInst, context[i], item.row, item.column, i, j );
6855 7024 }
6856 7025 else {
6857 7026 ret = fn.call( apiInst, context[i], item, i, j, rows );
6858 7027 }
6859
7028
6860 7029 if ( ret !== undefined ) {
6861 7030 a.push( ret );
6862 7031 }
6863 7032 }
6864 7033 }
6865 7034 }
6866
7035
6867 7036 if ( a.length || alwaysNew ) {
6868 7037 var api = new _Api( context, flatten ? a.concat.apply( [], a ) : a );
6869 7038 var apiSelector = api.selector;
6870 7039 apiSelector.rows = selector.rows;
6871 7040 apiSelector.cols = selector.cols;
6872 7041 apiSelector.opts = selector.opts;
6873 7042 return api;
6874 7043 }
6875 7044 return this;
6876 7045 },
6877
6878
7046
7047
6879 7048 lastIndexOf: __arrayProto.lastIndexOf || function (obj, start)
6880 7049 {
6881 7050 // Bit cheeky...
6882 7051 return this.indexOf.apply( this.toArray.reverse(), arguments );
6883 7052 },
6884
6885
7053
7054
6886 7055 length: 0,
6887
6888
7056
7057
6889 7058 map: function ( fn )
6890 7059 {
6891 7060 var a = [];
6892
7061
6893 7062 if ( __arrayProto.map ) {
6894 7063 a = __arrayProto.map.call( this, fn, this );
6895 7064 }
6896 7065 else {
6897 7066 // Compatibility for browsers without EMCA-252-5 (JS 1.6)
6898 7067 for ( var i=0, ien=this.length ; i<ien ; i++ ) {
6899 7068 a.push( fn.call( this, this[i], i ) );
6900 7069 }
6901 7070 }
6902
7071
6903 7072 return new _Api( this.context, a );
6904 7073 },
6905
6906
7074
7075
6907 7076 pluck: function ( prop )
6908 7077 {
6909 7078 return this.map( function ( el ) {
6910 7079 return el[ prop ];
6911 7080 } );
6912 7081 },
6913
7082
6914 7083 pop: __arrayProto.pop,
6915
6916
7084
7085
6917 7086 push: __arrayProto.push,
6918
6919
7087
7088
6920 7089 // Does not return an API instance
6921 7090 reduce: __arrayProto.reduce || function ( fn, init )
6922 7091 {
6923 7092 return _fnReduce( this, fn, init, 0, this.length, 1 );
6924 7093 },
6925
6926
7094
7095
6927 7096 reduceRight: __arrayProto.reduceRight || function ( fn, init )
6928 7097 {
6929 7098 return _fnReduce( this, fn, init, this.length-1, -1, -1 );
6930 7099 },
6931
6932
7100
7101
6933 7102 reverse: __arrayProto.reverse,
6934
6935
7103
7104
6936 7105 // Object with rows, columns and opts
6937 7106 selector: null,
6938
6939
7107
7108
6940 7109 shift: __arrayProto.shift,
6941
6942
7110
7111
6943 7112 sort: __arrayProto.sort, // ? name - order?
6944
6945
7113
7114
6946 7115 splice: __arrayProto.splice,
6947
6948
7116
7117
6949 7118 toArray: function ()
6950 7119 {
6951 7120 return __arrayProto.slice.call( this );
6952 7121 },
6953
6954
7122
7123
6955 7124 to$: function ()
6956 7125 {
6957 7126 return $( this );
6958 7127 },
6959
6960
7128
7129
6961 7130 toJQuery: function ()
6962 7131 {
6963 7132 return $( this );
6964 7133 },
6965
6966
7134
7135
6967 7136 unique: function ()
6968 7137 {
6969 7138 return new _Api( this.context, _unique(this) );
6970 7139 },
6971
6972
7140
7141
6973 7142 unshift: __arrayProto.unshift
6974 };
6975
6976
7143 } );
7144
7145
6977 7146 _Api.extend = function ( scope, obj, ext )
6978 7147 {
6979 7148 // Only extend API instances and static properties of the API
6980 if ( ! obj || ( ! (obj instanceof _Api) && ! obj.__dt_wrapper ) ) {
7149 if ( ! ext.length || ! obj || ( ! (obj instanceof _Api) && ! obj.__dt_wrapper ) ) {
6981 7150 return;
6982 7151 }
6983
7152
6984 7153 var
6985 7154 i, ien,
6986 7155 j, jen,
6987 7156 struct, inner,
6988 7157 methodScoping = function ( scope, fn, struc ) {
6989 7158 return function () {
6990 7159 var ret = fn.apply( scope, arguments );
6991
7160
6992 7161 // Method extension
6993 7162 _Api.extend( ret, ret, struc.methodExt );
6994 7163 return ret;
6995 7164 };
6996 7165 };
6997
7166
6998 7167 for ( i=0, ien=ext.length ; i<ien ; i++ ) {
6999 7168 struct = ext[i];
7000
7169
7001 7170 // Value
7002 7171 obj[ struct.name ] = typeof struct.val === 'function' ?
7003 7172 methodScoping( scope, struct.val, struct ) :
7004 7173 $.isPlainObject( struct.val ) ?
7005 7174 {} :
7006 7175 struct.val;
7007
7176
7008 7177 obj[ struct.name ].__dt_wrapper = true;
7009
7178
7010 7179 // Property extension
7011 7180 _Api.extend( scope, obj[ struct.name ], struct.propExt );
7012 7181 }
7013 7182 };
7014
7015
7183
7184
7016 7185 // @todo - Is there need for an augment function?
7017 7186 // _Api.augment = function ( inst, name )
7018 7187 // {
7019 7188 // // Find src object in the structure from the name
7020 7189 // var parts = name.split('.');
7021
7190
7022 7191 // _Api.extend( inst, obj );
7023 7192 // };
7024
7025
7193
7194
7026 7195 // [
7027 7196 // {
7028 7197 // name: 'data' -- string - Property name
7029 7198 // val: function () {}, -- function - Api method (or undefined if just an object
7030 7199 // methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result
7031 7200 // propExt: [ ... ] -- array - Array of Api object definitions to extend the property
7032 7201 // },
7033 7202 // {
7034 7203 // name: 'row'
7035 7204 // val: {},
7036 7205 // methodExt: [ ... ],
7037 7206 // propExt: [
7038 7207 // {
7039 7208 // name: 'data'
7040 7209 // val: function () {},
7041 7210 // methodExt: [ ... ],
7042 7211 // propExt: [ ... ]
7043 7212 // },
7044 7213 // ...
7045 7214 // ]
7046 7215 // }
7047 7216 // ]
7048
7217
7049 7218 _Api.register = _api_register = function ( name, val )
7050 7219 {
7051 7220 if ( $.isArray( name ) ) {
7052 7221 for ( var j=0, jen=name.length ; j<jen ; j++ ) {
7053 7222 _Api.register( name[j], val );
7054 7223 }
7055 7224 return;
7056 7225 }
7057
7226
7058 7227 var
7059 7228 i, ien,
7060 7229 heir = name.split('.'),
7061 7230 struct = __apiStruct,
7062 7231 key, method;
7063
7232
7064 7233 var find = function ( src, name ) {
7065 7234 for ( var i=0, ien=src.length ; i<ien ; i++ ) {
7066 7235 if ( src[i].name === name ) {
7067 7236 return src[i];
7068 7237 }
7069 7238 }
7070 7239 return null;
7071 7240 };
7072
7241
7073 7242 for ( i=0, ien=heir.length ; i<ien ; i++ ) {
7074 7243 method = heir[i].indexOf('()') !== -1;
7075 7244 key = method ?
7076 7245 heir[i].replace('()', '') :
7077 7246 heir[i];
7078
7247
7079 7248 var src = find( struct, key );
7080 7249 if ( ! src ) {
7081 7250 src = {
7082 7251 name: key,
7083 7252 val: {},
7084 7253 methodExt: [],
7085 7254 propExt: []
7086 7255 };
7087 7256 struct.push( src );
7088 7257 }
7089
7258
7090 7259 if ( i === ien-1 ) {
7091 7260 src.val = val;
7092 7261 }
7093 7262 else {
7094 7263 struct = method ?
7095 7264 src.methodExt :
7096 7265 src.propExt;
7097 7266 }
7098 7267 }
7099 7268 };
7100
7101
7269
7270
7102 7271 _Api.registerPlural = _api_registerPlural = function ( pluralName, singularName, val ) {
7103 7272 _Api.register( pluralName, val );
7104
7273
7105 7274 _Api.register( singularName, function () {
7106 7275 var ret = val.apply( this, arguments );
7107
7276
7108 7277 if ( ret === this ) {
7109 7278 // Returned item is the API instance that was passed in, return it
7110 7279 return this;
7111 7280 }
7112 7281 else if ( ret instanceof _Api ) {
7113 7282 // New API instance returned, want the value from the first item
7114 7283 // in the returned array for the singular result.
7115 7284 return ret.length ?
7116 7285 $.isArray( ret[0] ) ?
7117 7286 new _Api( ret.context, ret[0] ) : // Array results are 'enhanced'
7118 7287 ret[0] :
7119 7288 undefined;
7120 7289 }
7121
7290
7122 7291 // Non-API return - just fire it back
7123 7292 return ret;
7124 7293 } );
7125 7294 };
7126
7127
7295
7296
7128 7297 /**
7129 7298 * Selector for HTML tables. Apply the given selector to the give array of
7130 7299 * DataTables settings objects.
7131 7300 *
7132 7301 * @param {string|integer} [selector] jQuery selector string or integer
7133 7302 * @param {array} Array of DataTables settings objects to be filtered
7134 7303 * @return {array}
7135 7304 * @ignore
7136 7305 */
7137 7306 var __table_selector = function ( selector, a )
7138 7307 {
7139 7308 // Integer is used to pick out a table by index
7140 7309 if ( typeof selector === 'number' ) {
7141 7310 return [ a[ selector ] ];
7142 7311 }
7143
7312
7144 7313 // Perform a jQuery selector on the table nodes
7145 7314 var nodes = $.map( a, function (el, i) {
7146 7315 return el.nTable;
7147 7316 } );
7148
7317
7149 7318 return $(nodes)
7150 7319 .filter( selector )
7151 7320 .map( function (i) {
7152 7321 // Need to translate back from the table node to the settings
7153 7322 var idx = $.inArray( this, nodes );
7154 7323 return a[ idx ];
7155 7324 } )
7156 7325 .toArray();
7157 7326 };
7158
7159
7160
7327
7328
7329
7161 7330 /**
7162 7331 * Context selector for the API's context (i.e. the tables the API instance
7163 7332 * refers to.
7164 7333 *
7165 7334 * @name DataTable.Api#tables
7166 7335 * @param {string|integer} [selector] Selector to pick which tables the iterator
7167 7336 * should operate on. If not given, all tables in the current context are
7168 7337 * used. This can be given as a jQuery selector (for example `':gt(0)'`) to
7169 7338 * select multiple tables or as an integer to select a single table.
7170 7339 * @returns {DataTable.Api} Returns a new API instance if a selector is given.
7171 7340 */
7172 7341 _api_register( 'tables()', function ( selector ) {
7173 7342 // A new instance is created if there was a selector specified
7174 7343 return selector ?
7175 7344 new _Api( __table_selector( selector, this.context ) ) :
7176 7345 this;
7177 7346 } );
7178
7179
7347
7348
7180 7349 _api_register( 'table()', function ( selector ) {
7181 7350 var tables = this.tables( selector );
7182 7351 var ctx = tables.context;
7183
7352
7184 7353 // Truncate to the first matched table
7185 7354 return ctx.length ?
7186 7355 new _Api( ctx[0] ) :
7187 7356 tables;
7188 7357 } );
7189
7190
7358
7359
7191 7360 _api_registerPlural( 'tables().nodes()', 'table().node()' , function () {
7192 7361 return this.iterator( 'table', function ( ctx ) {
7193 7362 return ctx.nTable;
7194 7363 }, 1 );
7195 7364 } );
7196
7197
7365
7366
7198 7367 _api_registerPlural( 'tables().body()', 'table().body()' , function () {
7199 7368 return this.iterator( 'table', function ( ctx ) {
7200 7369 return ctx.nTBody;
7201 7370 }, 1 );
7202 7371 } );
7203
7204
7372
7373
7205 7374 _api_registerPlural( 'tables().header()', 'table().header()' , function () {
7206 7375 return this.iterator( 'table', function ( ctx ) {
7207 7376 return ctx.nTHead;
7208 7377 }, 1 );
7209 7378 } );
7210
7211
7379
7380
7212 7381 _api_registerPlural( 'tables().footer()', 'table().footer()' , function () {
7213 7382 return this.iterator( 'table', function ( ctx ) {
7214 7383 return ctx.nTFoot;
7215 7384 }, 1 );
7216 7385 } );
7217
7218
7386
7387
7219 7388 _api_registerPlural( 'tables().containers()', 'table().container()' , function () {
7220 7389 return this.iterator( 'table', function ( ctx ) {
7221 7390 return ctx.nTableWrapper;
7222 7391 }, 1 );
7223 7392 } );
7224
7225
7226
7393
7394
7395
7227 7396 /**
7228 7397 * Redraw the tables in the current context.
7229 *
7230 * @param {boolean} [reset=true] Reset (default) or hold the current paging
7231 * position. A full re-sort and re-filter is performed when this method is
7232 * called, which is why the pagination reset is the default action.
7233 * @returns {DataTables.Api} this
7234 */
7235 _api_register( 'draw()', function ( resetPaging ) {
7398 */
7399 _api_register( 'draw()', function ( paging ) {
7236 7400 return this.iterator( 'table', function ( settings ) {
7237 _fnReDraw( settings, resetPaging===false );
7401 if ( paging === 'page' ) {
7402 _fnDraw( settings );
7403 }
7404 else {
7405 if ( typeof paging === 'string' ) {
7406 paging = paging === 'full-hold' ?
7407 false :
7408 true;
7409 }
7410
7411 _fnReDraw( settings, paging===false );
7412 }
7238 7413 } );
7239 7414 } );
7240
7241
7242
7415
7416
7417
7243 7418 /**
7244 7419 * Get the current page index.
7245 7420 *
7246 7421 * @return {integer} Current page index (zero based)
7247 7422 *//**
7248 7423 * Set the current page.
7249 7424 *
7250 7425 * Note that if you attempt to show a page which does not exist, DataTables will
7251 7426 * not throw an error, but rather reset the paging.
7252 7427 *
7253 7428 * @param {integer|string} action The paging action to take. This can be one of:
7254 7429 * * `integer` - The page index to jump to
7255 7430 * * `string` - An action to take:
7256 7431 * * `first` - Jump to first page.
7257 7432 * * `next` - Jump to the next page
7258 7433 * * `previous` - Jump to previous page
7259 7434 * * `last` - Jump to the last page.
7260 7435 * @returns {DataTables.Api} this
7261 7436 */
7262 7437 _api_register( 'page()', function ( action ) {
7263 7438 if ( action === undefined ) {
7264 7439 return this.page.info().page; // not an expensive call
7265 7440 }
7266
7441
7267 7442 // else, have an action to take on all tables
7268 7443 return this.iterator( 'table', function ( settings ) {
7269 7444 _fnPageChange( settings, action );
7270 7445 } );
7271 7446 } );
7272
7273
7447
7448
7274 7449 /**
7275 7450 * Paging information for the first table in the current context.
7276 7451 *
7277 7452 * If you require paging information for another table, use the `table()` method
7278 7453 * with a suitable selector.
7279 7454 *
7280 7455 * @return {object} Object with the following properties set:
7281 7456 * * `page` - Current page index (zero based - i.e. the first page is `0`)
7282 7457 * * `pages` - Total number of pages
7283 7458 * * `start` - Display index for the first record shown on the current page
7284 7459 * * `end` - Display index for the last record shown on the current page
7285 7460 * * `length` - Display length (number of records). Note that generally `start
7286 7461 * + length = end`, but this is not always true, for example if there are
7287 7462 * only 2 records to show on the final page, with a length of 10.
7288 7463 * * `recordsTotal` - Full data set length
7289 7464 * * `recordsDisplay` - Data set length once the current filtering criterion
7290 7465 * are applied.
7291 7466 */
7292 7467 _api_register( 'page.info()', function ( action ) {
7293 7468 if ( this.context.length === 0 ) {
7294 7469 return undefined;
7295 7470 }
7296
7471
7297 7472 var
7298 7473 settings = this.context[0],
7299 7474 start = settings._iDisplayStart,
7300 len = settings._iDisplayLength,
7475 len = settings.oFeatures.bPaginate ? settings._iDisplayLength : -1,
7301 7476 visRecords = settings.fnRecordsDisplay(),
7302 7477 all = len === -1;
7303
7478
7304 7479 return {
7305 7480 "page": all ? 0 : Math.floor( start / len ),
7306 7481 "pages": all ? 1 : Math.ceil( visRecords / len ),
7307 7482 "start": start,
7308 7483 "end": settings.fnDisplayEnd(),
7309 7484 "length": len,
7310 7485 "recordsTotal": settings.fnRecordsTotal(),
7311 "recordsDisplay": visRecords
7486 "recordsDisplay": visRecords,
7487 "serverSide": _fnDataSource( settings ) === 'ssp'
7312 7488 };
7313 7489 } );
7314
7315
7490
7491
7316 7492 /**
7317 7493 * Get the current page length.
7318 7494 *
7319 7495 * @return {integer} Current page length. Note `-1` indicates that all records
7320 7496 * are to be shown.
7321 7497 *//**
7322 7498 * Set the current page length.
7323 7499 *
7324 7500 * @param {integer} Page length to set. Use `-1` to show all records.
7325 7501 * @returns {DataTables.Api} this
7326 7502 */
7327 7503 _api_register( 'page.len()', function ( len ) {
7328 7504 // Note that we can't call this function 'length()' because `length`
7329 7505 // is a Javascript property of functions which defines how many arguments
7330 7506 // the function expects.
7331 7507 if ( len === undefined ) {
7332 7508 return this.context.length !== 0 ?
7333 7509 this.context[0]._iDisplayLength :
7334 7510 undefined;
7335 7511 }
7336
7512
7337 7513 // else, set the page length
7338 7514 return this.iterator( 'table', function ( settings ) {
7339 7515 _fnLengthChange( settings, len );
7340 7516 } );
7341 7517 } );
7342
7343
7344
7518
7519
7520
7345 7521 var __reload = function ( settings, holdPosition, callback ) {
7522 // Use the draw event to trigger a callback
7523 if ( callback ) {
7524 var api = new _Api( settings );
7525
7526 api.one( 'draw', function () {
7527 callback( api.ajax.json() );
7528 } );
7529 }
7530
7346 7531 if ( _fnDataSource( settings ) == 'ssp' ) {
7347 7532 _fnReDraw( settings, holdPosition );
7348 7533 }
7349 7534 else {
7535 _fnProcessingDisplay( settings, true );
7536
7537 // Cancel an existing request
7538 var xhr = settings.jqXHR;
7539 if ( xhr && xhr.readyState !== 4 ) {
7540 xhr.abort();
7541 }
7542
7350 7543 // Trigger xhr
7351 _fnProcessingDisplay( settings, true );
7352
7353 7544 _fnBuildAjax( settings, [], function( json ) {
7354 7545 _fnClearTable( settings );
7355
7546
7356 7547 var data = _fnAjaxDataSrc( settings, json );
7357 7548 for ( var i=0, ien=data.length ; i<ien ; i++ ) {
7358 7549 _fnAddData( settings, data[i] );
7359 7550 }
7360
7551
7361 7552 _fnReDraw( settings, holdPosition );
7362 7553 _fnProcessingDisplay( settings, false );
7363 7554 } );
7364 7555 }
7365
7366 // Use the draw event to trigger a callback, regardless of if it is an async
7367 // or sync draw
7368 if ( callback ) {
7369 var api = new _Api( settings );
7370
7371 api.one( 'draw', function () {
7372 callback( api.ajax.json() );
7373 } );
7374 }
7375 7556 };
7376
7377
7557
7558
7378 7559 /**
7379 7560 * Get the JSON response from the last Ajax request that DataTables made to the
7380 7561 * server. Note that this returns the JSON from the first table in the current
7381 7562 * context.
7382 7563 *
7383 7564 * @return {object} JSON received from the server.
7384 7565 */
7385 7566 _api_register( 'ajax.json()', function () {
7386 7567 var ctx = this.context;
7387
7568
7388 7569 if ( ctx.length > 0 ) {
7389 7570 return ctx[0].json;
7390 7571 }
7391
7572
7392 7573 // else return undefined;
7393 7574 } );
7394
7395
7575
7576
7396 7577 /**
7397 7578 * Get the data submitted in the last Ajax request
7398 7579 */
7399 7580 _api_register( 'ajax.params()', function () {
7400 7581 var ctx = this.context;
7401
7582
7402 7583 if ( ctx.length > 0 ) {
7403 7584 return ctx[0].oAjaxData;
7404 7585 }
7405
7586
7406 7587 // else return undefined;
7407 7588 } );
7408
7409
7589
7590
7410 7591 /**
7411 7592 * Reload tables from the Ajax data source. Note that this function will
7412 7593 * automatically re-draw the table when the remote data has been loaded.
7413 7594 *
7414 7595 * @param {boolean} [reset=true] Reset (default) or hold the current paging
7415 7596 * position. A full re-sort and re-filter is performed when this method is
7416 7597 * called, which is why the pagination reset is the default action.
7417 7598 * @returns {DataTables.Api} this
7418 7599 */
7419 7600 _api_register( 'ajax.reload()', function ( callback, resetPaging ) {
7420 7601 return this.iterator( 'table', function (settings) {
7421 7602 __reload( settings, resetPaging===false, callback );
7422 7603 } );
7423 7604 } );
7424
7425
7605
7606
7426 7607 /**
7427 7608 * Get the current Ajax URL. Note that this returns the URL from the first
7428 7609 * table in the current context.
7429 7610 *
7430 7611 * @return {string} Current Ajax source URL
7431 7612 *//**
7432 7613 * Set the Ajax URL. Note that this will set the URL for all tables in the
7433 7614 * current context.
7434 7615 *
7435 7616 * @param {string} url URL to set.
7436 7617 * @returns {DataTables.Api} this
7437 7618 */
7438 7619 _api_register( 'ajax.url()', function ( url ) {
7439 7620 var ctx = this.context;
7440
7621
7441 7622 if ( url === undefined ) {
7442 7623 // get
7443 7624 if ( ctx.length === 0 ) {
7444 7625 return undefined;
7445 7626 }
7446 7627 ctx = ctx[0];
7447
7628
7448 7629 return ctx.ajax ?
7449 7630 $.isPlainObject( ctx.ajax ) ?
7450 7631 ctx.ajax.url :
7451 7632 ctx.ajax :
7452 7633 ctx.sAjaxSource;
7453 7634 }
7454
7635
7455 7636 // set
7456 7637 return this.iterator( 'table', function ( settings ) {
7457 7638 if ( $.isPlainObject( settings.ajax ) ) {
7458 7639 settings.ajax.url = url;
7459 7640 }
7460 7641 else {
7461 7642 settings.ajax = url;
7462 7643 }
7463 7644 // No need to consider sAjaxSource here since DataTables gives priority
7464 7645 // to `ajax` over `sAjaxSource`. So setting `ajax` here, renders any
7465 7646 // value of `sAjaxSource` redundant.
7466 7647 } );
7467 7648 } );
7468
7469
7649
7650
7470 7651 /**
7471 7652 * Load data from the newly set Ajax URL. Note that this method is only
7472 7653 * available when `ajax.url()` is used to set a URL. Additionally, this method
7473 7654 * has the same effect as calling `ajax.reload()` but is provided for
7474 7655 * convenience when setting a new URL. Like `ajax.reload()` it will
7475 7656 * automatically redraw the table once the remote data has been loaded.
7476 7657 *
7477 7658 * @returns {DataTables.Api} this
7478 7659 */
7479 7660 _api_register( 'ajax.url().load()', function ( callback, resetPaging ) {
7480 7661 // Same as a reload, but makes sense to present it for easy access after a
7481 7662 // url change
7482 7663 return this.iterator( 'table', function ( ctx ) {
7483 7664 __reload( ctx, resetPaging===false, callback );
7484 7665 } );
7485 7666 } );
7486
7487
7488
7489
7490 var _selector_run = function ( selector, select )
7667
7668
7669
7670
7671 var _selector_run = function ( type, selector, selectFn, settings, opts )
7491 7672 {
7492 7673 var
7493 7674 out = [], res,
7494 7675 a, i, ien, j, jen,
7495 7676 selectorType = typeof selector;
7496
7677
7497 7678 // Can't just check for isArray here, as an API or jQuery instance might be
7498 7679 // given with their array like look
7499 7680 if ( ! selector || selectorType === 'string' || selectorType === 'function' || selector.length === undefined ) {
7500 7681 selector = [ selector ];
7501 7682 }
7502
7683
7503 7684 for ( i=0, ien=selector.length ; i<ien ; i++ ) {
7504 a = selector[i] && selector[i].split ?
7685 // Only split on simple strings - complex expressions will be jQuery selectors
7686 a = selector[i] && selector[i].split && ! selector[i].match(/[\[\(:]/) ?
7505 7687 selector[i].split(',') :
7506 7688 [ selector[i] ];
7507
7689
7508 7690 for ( j=0, jen=a.length ; j<jen ; j++ ) {
7509 res = select( typeof a[j] === 'string' ? $.trim(a[j]) : a[j] );
7510
7691 res = selectFn( typeof a[j] === 'string' ? $.trim(a[j]) : a[j] );
7692
7511 7693 if ( res && res.length ) {
7512 out.push.apply( out, res );
7513 }
7514 }
7515 }
7516
7517 return out;
7694 out = out.concat( res );
7695 }
7696 }
7697 }
7698
7699 // selector extensions
7700 var ext = _ext.selector[ type ];
7701 if ( ext.length ) {
7702 for ( i=0, ien=ext.length ; i<ien ; i++ ) {
7703 out = ext[i]( settings, opts, out );
7704 }
7705 }
7706
7707 return _unique( out );
7518 7708 };
7519
7520
7709
7710
7521 7711 var _selector_opts = function ( opts )
7522 7712 {
7523 7713 if ( ! opts ) {
7524 7714 opts = {};
7525 7715 }
7526
7716
7527 7717 // Backwards compatibility for 1.9- which used the terminology filter rather
7528 7718 // than search
7529 if ( opts.filter && ! opts.search ) {
7719 if ( opts.filter && opts.search === undefined ) {
7530 7720 opts.search = opts.filter;
7531 7721 }
7532
7533 return {
7534 search: opts.search || 'none',
7535 order: opts.order || 'current',
7536 page: opts.page || 'all'
7537 };
7722
7723 return $.extend( {
7724 search: 'none',
7725 order: 'current',
7726 page: 'all'
7727 }, opts );
7538 7728 };
7539
7540
7729
7730
7541 7731 var _selector_first = function ( inst )
7542 7732 {
7543 7733 // Reduce the API instance to the first item found
7544 7734 for ( var i=0, ien=inst.length ; i<ien ; i++ ) {
7545 7735 if ( inst[i].length > 0 ) {
7546 7736 // Assign the first element to the first item in the instance
7547 7737 // and truncate the instance and context
7548 7738 inst[0] = inst[i];
7739 inst[0].length = 1;
7549 7740 inst.length = 1;
7550 7741 inst.context = [ inst.context[i] ];
7551
7742
7552 7743 return inst;
7553 7744 }
7554 7745 }
7555
7746
7556 7747 // Not found - return an empty instance
7557 7748 inst.length = 0;
7558 7749 return inst;
7559 7750 };
7560
7561
7751
7752
7562 7753 var _selector_row_indexes = function ( settings, opts )
7563 7754 {
7564 7755 var
7565 7756 i, ien, tmp, a=[],
7566 7757 displayFiltered = settings.aiDisplay,
7567 7758 displayMaster = settings.aiDisplayMaster;
7568
7759
7569 7760 var
7570 7761 search = opts.search, // none, applied, removed
7571 7762 order = opts.order, // applied, current, index (original - compatibility with 1.9)
7572 7763 page = opts.page; // all, current
7573
7764
7574 7765 if ( _fnDataSource( settings ) == 'ssp' ) {
7575 7766 // In server-side processing mode, most options are irrelevant since
7576 7767 // rows not shown don't exist and the index order is the applied order
7577 7768 // Removed is a special case - for consistency just return an empty
7578 7769 // array
7579 7770 return search === 'removed' ?
7580 7771 [] :
7581 7772 _range( 0, displayMaster.length );
7582 7773 }
7583 7774 else if ( page == 'current' ) {
7584 7775 // Current page implies that order=current and fitler=applied, since it is
7585 7776 // fairly senseless otherwise, regardless of what order and search actually
7586 7777 // are
7587 7778 for ( i=settings._iDisplayStart, ien=settings.fnDisplayEnd() ; i<ien ; i++ ) {
7588 7779 a.push( displayFiltered[i] );
7589 7780 }
7590 7781 }
7591 7782 else if ( order == 'current' || order == 'applied' ) {
7592 7783 a = search == 'none' ?
7593 7784 displayMaster.slice() : // no search
7594 7785 search == 'applied' ?
7595 7786 displayFiltered.slice() : // applied search
7596 7787 $.map( displayMaster, function (el, i) { // removed search
7597 7788 return $.inArray( el, displayFiltered ) === -1 ? el : null;
7598 7789 } );
7599 7790 }
7600 7791 else if ( order == 'index' || order == 'original' ) {
7601 7792 for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
7602 7793 if ( search == 'none' ) {
7603 7794 a.push( i );
7604 7795 }
7605 7796 else { // applied | removed
7606 7797 tmp = $.inArray( i, displayFiltered );
7607
7798
7608 7799 if ((tmp === -1 && search == 'removed') ||
7609 7800 (tmp >= 0 && search == 'applied') )
7610 7801 {
7611 7802 a.push( i );
7612 7803 }
7613 7804 }
7614 7805 }
7615 7806 }
7616
7807
7617 7808 return a;
7618 7809 };
7619
7620
7810
7811
7621 7812 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7622 7813 * Rows
7623 7814 *
7624 7815 * {} - no selector - use all available rows
7625 7816 * {integer} - row aoData index
7626 7817 * {node} - TR node
7627 7818 * {string} - jQuery selector to apply to the TR elements
7628 7819 * {array} - jQuery array of nodes, or simply an array of TR nodes
7629 7820 *
7630 7821 */
7631
7632
7822
7823
7633 7824 var __row_selector = function ( settings, selector, opts )
7634 7825 {
7635 return _selector_run( selector, function ( sel ) {
7826 var rows;
7827 var run = function ( sel ) {
7636 7828 var selInt = _intVal( sel );
7637 7829 var i, ien;
7638
7830
7639 7831 // Short cut - selector is a number and no options provided (default is
7640 7832 // all records, so no need to check if the index is in there, since it
7641 7833 // must be - dev error if the index doesn't exist).
7642 7834 if ( selInt !== null && ! opts ) {
7643 7835 return [ selInt ];
7644 7836 }
7645
7646 var rows = _selector_row_indexes( settings, opts );
7647
7837
7838 if ( ! rows ) {
7839 rows = _selector_row_indexes( settings, opts );
7840 }
7841
7648 7842 if ( selInt !== null && $.inArray( selInt, rows ) !== -1 ) {
7649 7843 // Selector - integer
7650 7844 return [ selInt ];
7651 7845 }
7652 else if ( ! sel ) {
7846 else if ( sel === null || sel === undefined || sel === '' ) {
7653 7847 // Selector - none
7654 7848 return rows;
7655 7849 }
7656
7850
7657 7851 // Selector - function
7658 7852 if ( typeof sel === 'function' ) {
7659 7853 return $.map( rows, function (idx) {
7660 7854 var row = settings.aoData[ idx ];
7661 7855 return sel( idx, row._aData, row.nTr ) ? idx : null;
7662 7856 } );
7663 7857 }
7664
7858
7665 7859 // Get nodes in the order from the `rows` array with null values removed
7666 7860 var nodes = _removeEmpty(
7667 7861 _pluck_order( settings.aoData, rows, 'nTr' )
7668 7862 );
7669
7863
7670 7864 // Selector - node
7671 7865 if ( sel.nodeName ) {
7672 if ( $.inArray( sel, nodes ) !== -1 ) {
7673 return [ sel._DT_RowIndex ]; // sel is a TR node that is in the table
7674 // and DataTables adds a prop for fast lookup
7675 }
7676 }
7677
7866 if ( sel._DT_RowIndex !== undefined ) {
7867 return [ sel._DT_RowIndex ]; // Property added by DT for fast lookup
7868 }
7869 else if ( sel._DT_CellIndex ) {
7870 return [ sel._DT_CellIndex.row ];
7871 }
7872 else {
7873 var host = $(sel).closest('*[data-dt-row]');
7874 return host.length ?
7875 [ host.data('dt-row') ] :
7876 [];
7877 }
7878 }
7879
7880 // ID selector. Want to always be able to select rows by id, regardless
7881 // of if the tr element has been created or not, so can't rely upon
7882 // jQuery here - hence a custom implementation. This does not match
7883 // Sizzle's fast selector or HTML4 - in HTML5 the ID can be anything,
7884 // but to select it using a CSS selector engine (like Sizzle or
7885 // querySelect) it would need to need to be escaped for some characters.
7886 // DataTables simplifies this for row selectors since you can select
7887 // only a row. A # indicates an id any anything that follows is the id -
7888 // unescaped.
7889 if ( typeof sel === 'string' && sel.charAt(0) === '#' ) {
7890 // get row index from id
7891 var rowObj = settings.aIds[ sel.replace( /^#/, '' ) ];
7892 if ( rowObj !== undefined ) {
7893 return [ rowObj.idx ];
7894 }
7895
7896 // need to fall through to jQuery in case there is DOM id that
7897 // matches
7898 }
7899
7678 7900 // Selector - jQuery selector string, array of nodes or jQuery object/
7679 7901 // As jQuery's .filter() allows jQuery objects to be passed in filter,
7680 7902 // it also allows arrays, so this will cope with all three options
7681 7903 return $(nodes)
7682 7904 .filter( sel )
7683 7905 .map( function () {
7684 7906 return this._DT_RowIndex;
7685 7907 } )
7686 7908 .toArray();
7687 } );
7909 };
7910
7911 return _selector_run( 'row', selector, run, settings, opts );
7688 7912 };
7689
7690
7691 /**
7692 *
7693 */
7913
7914
7694 7915 _api_register( 'rows()', function ( selector, opts ) {
7695 7916 // argument shifting
7696 7917 if ( selector === undefined ) {
7697 7918 selector = '';
7698 7919 }
7699 7920 else if ( $.isPlainObject( selector ) ) {
7700 7921 opts = selector;
7701 7922 selector = '';
7702 7923 }
7703
7924
7704 7925 opts = _selector_opts( opts );
7705
7926
7706 7927 var inst = this.iterator( 'table', function ( settings ) {
7707 7928 return __row_selector( settings, selector, opts );
7708 7929 }, 1 );
7709
7930
7710 7931 // Want argument shifting here and in __row_selector?
7711 7932 inst.selector.rows = selector;
7712 7933 inst.selector.opts = opts;
7713
7934
7714 7935 return inst;
7715 7936 } );
7716
7717
7937
7718 7938 _api_register( 'rows().nodes()', function () {
7719 7939 return this.iterator( 'row', function ( settings, row ) {
7720 7940 return settings.aoData[ row ].nTr || undefined;
7721 7941 }, 1 );
7722 7942 } );
7723
7943
7724 7944 _api_register( 'rows().data()', function () {
7725 7945 return this.iterator( true, 'rows', function ( settings, rows ) {
7726 7946 return _pluck_order( settings.aoData, rows, '_aData' );
7727 7947 }, 1 );
7728 7948 } );
7729
7949
7730 7950 _api_registerPlural( 'rows().cache()', 'row().cache()', function ( type ) {
7731 7951 return this.iterator( 'row', function ( settings, row ) {
7732 7952 var r = settings.aoData[ row ];
7733 7953 return type === 'search' ? r._aFilterData : r._aSortData;
7734 7954 }, 1 );
7735 7955 } );
7736
7956
7737 7957 _api_registerPlural( 'rows().invalidate()', 'row().invalidate()', function ( src ) {
7738 7958 return this.iterator( 'row', function ( settings, row ) {
7739 7959 _fnInvalidate( settings, row, src );
7740 7960 } );
7741 7961 } );
7742
7962
7743 7963 _api_registerPlural( 'rows().indexes()', 'row().index()', function () {
7744 7964 return this.iterator( 'row', function ( settings, row ) {
7745 7965 return row;
7746 7966 }, 1 );
7747 7967 } );
7748
7968
7969 _api_registerPlural( 'rows().ids()', 'row().id()', function ( hash ) {
7970 var a = [];
7971 var context = this.context;
7972
7973 // `iterator` will drop undefined values, but in this case we want them
7974 for ( var i=0, ien=context.length ; i<ien ; i++ ) {
7975 for ( var j=0, jen=this[i].length ; j<jen ; j++ ) {
7976 var id = context[i].rowIdFn( context[i].aoData[ this[i][j] ]._aData );
7977 a.push( (hash === true ? '#' : '' )+ id );
7978 }
7979 }
7980
7981 return new _Api( context, a );
7982 } );
7983
7749 7984 _api_registerPlural( 'rows().remove()', 'row().remove()', function () {
7750 7985 var that = this;
7751
7752 return this.iterator( 'row', function ( settings, row, thatIdx ) {
7986
7987 this.iterator( 'row', function ( settings, row, thatIdx ) {
7753 7988 var data = settings.aoData;
7754
7989 var rowData = data[ row ];
7990 var i, ien, j, jen;
7991 var loopRow, loopCells;
7992
7755 7993 data.splice( row, 1 );
7756
7757 // Update the _DT_RowIndex parameter on all rows in the table
7758 for ( var i=0, ien=data.length ; i<ien ; i++ ) {
7759 if ( data[i].nTr !== null ) {
7760 data[i].nTr._DT_RowIndex = i;
7761 }
7762 }
7763
7764 // Remove the target row from the search array
7765 var displayIndex = $.inArray( row, settings.aiDisplay );
7766
7994
7995 // Update the cached indexes
7996 for ( i=0, ien=data.length ; i<ien ; i++ ) {
7997 loopRow = data[i];
7998 loopCells = loopRow.anCells;
7999
8000 // Rows
8001 if ( loopRow.nTr !== null ) {
8002 loopRow.nTr._DT_RowIndex = i;
8003 }
8004
8005 // Cells
8006 if ( loopCells !== null ) {
8007 for ( j=0, jen=loopCells.length ; j<jen ; j++ ) {
8008 loopCells[j]._DT_CellIndex.row = i;
8009 }
8010 }
8011 }
8012
7767 8013 // Delete from the display arrays
7768 8014 _fnDeleteIndex( settings.aiDisplayMaster, row );
7769 8015 _fnDeleteIndex( settings.aiDisplay, row );
7770 8016 _fnDeleteIndex( that[ thatIdx ], row, false ); // maintain local indexes
7771
8017
7772 8018 // Check for an 'overflow' they case for displaying the table
7773 8019 _fnLengthOverflow( settings );
8020
8021 // Remove the row's ID reference if there is one
8022 var id = settings.rowIdFn( rowData._aData );
8023 if ( id !== undefined ) {
8024 delete settings.aIds[ id ];
8025 }
7774 8026 } );
7775 } );
7776
7777
8027
8028 this.iterator( 'table', function ( settings ) {
8029 for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
8030 settings.aoData[i].idx = i;
8031 }
8032 } );
8033
8034 return this;
8035 } );
8036
8037
7778 8038 _api_register( 'rows.add()', function ( rows ) {
7779 8039 var newRows = this.iterator( 'table', function ( settings ) {
7780 8040 var row, i, ien;
7781 8041 var out = [];
7782
8042
7783 8043 for ( i=0, ien=rows.length ; i<ien ; i++ ) {
7784 8044 row = rows[i];
7785
8045
7786 8046 if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {
7787 8047 out.push( _fnAddTr( settings, row )[0] );
7788 8048 }
7789 8049 else {
7790 8050 out.push( _fnAddData( settings, row ) );
7791 8051 }
7792 8052 }
7793
8053
7794 8054 return out;
7795 8055 }, 1 );
7796
8056
7797 8057 // Return an Api.rows() extended instance, so rows().nodes() etc can be used
7798 8058 var modRows = this.rows( -1 );
7799 8059 modRows.pop();
7800 modRows.push.apply( modRows, newRows.toArray() );
7801
8060 $.merge( modRows, newRows );
8061
7802 8062 return modRows;
7803 8063 } );
7804
7805
7806
7807
7808
8064
8065
8066
8067
8068
7809 8069 /**
7810 8070 *
7811 8071 */
7812 8072 _api_register( 'row()', function ( selector, opts ) {
7813 8073 return _selector_first( this.rows( selector, opts ) );
7814 8074 } );
7815
7816
8075
8076
7817 8077 _api_register( 'row().data()', function ( data ) {
7818 8078 var ctx = this.context;
7819
8079
7820 8080 if ( data === undefined ) {
7821 8081 // Get
7822 8082 return ctx.length && this.length ?
7823 8083 ctx[0].aoData[ this[0] ]._aData :
7824 8084 undefined;
7825 8085 }
7826
8086
7827 8087 // Set
7828 8088 ctx[0].aoData[ this[0] ]._aData = data;
7829
8089
7830 8090 // Automatically invalidate
7831 8091 _fnInvalidate( ctx[0], this[0], 'data' );
7832
8092
7833 8093 return this;
7834 8094 } );
7835
7836
8095
8096
7837 8097 _api_register( 'row().node()', function () {
7838 8098 var ctx = this.context;
7839
8099
7840 8100 return ctx.length && this.length ?
7841 8101 ctx[0].aoData[ this[0] ].nTr || null :
7842 8102 null;
7843 8103 } );
7844
7845
8104
8105
7846 8106 _api_register( 'row.add()', function ( row ) {
7847 8107 // Allow a jQuery object to be passed in - only a single row is added from
7848 8108 // it though - the first element in the set
7849 8109 if ( row instanceof $ && row.length ) {
7850 8110 row = row[0];
7851 8111 }
7852
8112
7853 8113 var rows = this.iterator( 'table', function ( settings ) {
7854 8114 if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {
7855 8115 return _fnAddTr( settings, row )[0];
7856 8116 }
7857 8117 return _fnAddData( settings, row );
7858 8118 } );
7859
8119
7860 8120 // Return an Api.rows() extended instance, with the newly added row selected
7861 8121 return this.row( rows[0] );
7862 8122 } );
7863
7864
7865
8123
8124
8125
7866 8126 var __details_add = function ( ctx, row, data, klass )
7867 8127 {
7868 8128 // Convert to array of TR elements
7869 8129 var rows = [];
7870 8130 var addRow = function ( r, k ) {
8131 // Recursion to allow for arrays of jQuery objects
8132 if ( $.isArray( r ) || r instanceof $ ) {
8133 for ( var i=0, ien=r.length ; i<ien ; i++ ) {
8134 addRow( r[i], k );
8135 }
8136 return;
8137 }
8138
7871 8139 // If we get a TR element, then just add it directly - up to the dev
7872 8140 // to add the correct number of columns etc
7873 8141 if ( r.nodeName && r.nodeName.toLowerCase() === 'tr' ) {
7874 8142 rows.push( r );
7875 8143 }
7876 8144 else {
7877 8145 // Otherwise create a row with a wrapper
7878 8146 var created = $('<tr><td/></tr>').addClass( k );
7879 8147 $('td', created)
7880 8148 .addClass( k )
7881 8149 .html( r )
7882 8150 [0].colSpan = _fnVisbleColumns( ctx );
7883
8151
7884 8152 rows.push( created[0] );
7885 8153 }
7886 8154 };
7887
7888 if ( $.isArray( data ) || data instanceof $ ) {
7889 for ( var i=0, ien=data.length ; i<ien ; i++ ) {
7890 addRow( data[i], klass );
7891 }
7892 }
7893 else {
7894 addRow( data, klass );
7895 }
7896
8155
8156 addRow( data, klass );
8157
7897 8158 if ( row._details ) {
7898 row._details.remove();
7899 }
7900
8159 row._details.detach();
8160 }
8161
7901 8162 row._details = $(rows);
7902
8163
7903 8164 // If the children were already shown, that state should be retained
7904 8165 if ( row._detailsShow ) {
7905 8166 row._details.insertAfter( row.nTr );
7906 8167 }
7907 8168 };
7908
7909
8169
8170
7910 8171 var __details_remove = function ( api, idx )
7911 8172 {
7912 8173 var ctx = api.context;
7913
8174
7914 8175 if ( ctx.length ) {
7915 8176 var row = ctx[0].aoData[ idx !== undefined ? idx : api[0] ];
7916
7917 if ( row._details ) {
8177
8178 if ( row && row._details ) {
7918 8179 row._details.remove();
7919
8180
7920 8181 row._detailsShow = undefined;
7921 8182 row._details = undefined;
7922 8183 }
7923 8184 }
7924 8185 };
7925
7926
8186
8187
7927 8188 var __details_display = function ( api, show ) {
7928 8189 var ctx = api.context;
7929
8190
7930 8191 if ( ctx.length && api.length ) {
7931 8192 var row = ctx[0].aoData[ api[0] ];
7932
8193
7933 8194 if ( row._details ) {
7934 8195 row._detailsShow = show;
7935
8196
7936 8197 if ( show ) {
7937 8198 row._details.insertAfter( row.nTr );
7938 8199 }
7939 8200 else {
7940 8201 row._details.detach();
7941 8202 }
7942
8203
7943 8204 __details_events( ctx[0] );
7944 8205 }
7945 8206 }
7946 8207 };
7947
7948
8208
8209
7949 8210 var __details_events = function ( settings )
7950 8211 {
7951 8212 var api = new _Api( settings );
7952 8213 var namespace = '.dt.DT_details';
7953 8214 var drawEvent = 'draw'+namespace;
7954 8215 var colvisEvent = 'column-visibility'+namespace;
7955 8216 var destroyEvent = 'destroy'+namespace;
7956 8217 var data = settings.aoData;
7957
8218
7958 8219 api.off( drawEvent +' '+ colvisEvent +' '+ destroyEvent );
7959
8220
7960 8221 if ( _pluck( data, '_details' ).length > 0 ) {
7961 8222 // On each draw, insert the required elements into the document
7962 8223 api.on( drawEvent, function ( e, ctx ) {
7963 8224 if ( settings !== ctx ) {
7964 8225 return;
7965 8226 }
7966
8227
7967 8228 api.rows( {page:'current'} ).eq(0).each( function (idx) {
7968 8229 // Internal data grab
7969 8230 var row = data[ idx ];
7970
8231
7971 8232 if ( row._detailsShow ) {
7972 8233 row._details.insertAfter( row.nTr );
7973 8234 }
7974 8235 } );
7975 8236 } );
7976
8237
7977 8238 // Column visibility change - update the colspan
7978 8239 api.on( colvisEvent, function ( e, ctx, idx, vis ) {
7979 8240 if ( settings !== ctx ) {
7980 8241 return;
7981 8242 }
7982
8243
7983 8244 // Update the colspan for the details rows (note, only if it already has
7984 8245 // a colspan)
7985 8246 var row, visible = _fnVisbleColumns( ctx );
7986
8247
7987 8248 for ( var i=0, ien=data.length ; i<ien ; i++ ) {
7988 8249 row = data[i];
7989
8250
7990 8251 if ( row._details ) {
7991 8252 row._details.children('td[colspan]').attr('colspan', visible );
7992 8253 }
7993 8254 }
7994 8255 } );
7995
8256
7996 8257 // Table destroyed - nuke any child rows
7997 8258 api.on( destroyEvent, function ( e, ctx ) {
7998 8259 if ( settings !== ctx ) {
7999 8260 return;
8000 8261 }
8001
8262
8002 8263 for ( var i=0, ien=data.length ; i<ien ; i++ ) {
8003 8264 if ( data[i]._details ) {
8004 8265 __details_remove( api, i );
8005 8266 }
8006 8267 }
8007 8268 } );
8008 8269 }
8009 8270 };
8010
8271
8011 8272 // Strings for the method names to help minification
8012 8273 var _emp = '';
8013 8274 var _child_obj = _emp+'row().child';
8014 8275 var _child_mth = _child_obj+'()';
8015
8276
8016 8277 // data can be:
8017 8278 // tr
8018 8279 // string
8019 8280 // jQuery or array of any of the above
8020 8281 _api_register( _child_mth, function ( data, klass ) {
8021 8282 var ctx = this.context;
8022
8283
8023 8284 if ( data === undefined ) {
8024 8285 // get
8025 8286 return ctx.length && this.length ?
8026 8287 ctx[0].aoData[ this[0] ]._details :
8027 8288 undefined;
8028 8289 }
8029 8290 else if ( data === true ) {
8030 8291 // show
8031 8292 this.child.show();
8032 8293 }
8033 8294 else if ( data === false ) {
8034 8295 // remove
8035 8296 __details_remove( this );
8036 8297 }
8037 8298 else if ( ctx.length && this.length ) {
8038 8299 // set
8039 8300 __details_add( ctx[0], ctx[0].aoData[ this[0] ], data, klass );
8040 8301 }
8041
8302
8042 8303 return this;
8043 8304 } );
8044
8045
8305
8306
8046 8307 _api_register( [
8047 8308 _child_obj+'.show()',
8048 8309 _child_mth+'.show()' // only when `child()` was called with parameters (without
8049 8310 ], function ( show ) { // it returns an object and this method is not executed)
8050 8311 __details_display( this, true );
8051 8312 return this;
8052 8313 } );
8053
8054
8314
8315
8055 8316 _api_register( [
8056 8317 _child_obj+'.hide()',
8057 8318 _child_mth+'.hide()' // only when `child()` was called with parameters (without
8058 8319 ], function () { // it returns an object and this method is not executed)
8059 8320 __details_display( this, false );
8060 8321 return this;
8061 8322 } );
8062
8063
8323
8324
8064 8325 _api_register( [
8065 8326 _child_obj+'.remove()',
8066 8327 _child_mth+'.remove()' // only when `child()` was called with parameters (without
8067 8328 ], function () { // it returns an object and this method is not executed)
8068 8329 __details_remove( this );
8069 8330 return this;
8070 8331 } );
8071
8072
8332
8333
8073 8334 _api_register( _child_obj+'.isShown()', function () {
8074 8335 var ctx = this.context;
8075
8336
8076 8337 if ( ctx.length && this.length ) {
8077 8338 // _detailsShown as false or undefined will fall through to return false
8078 8339 return ctx[0].aoData[ this[0] ]._detailsShow || false;
8079 8340 }
8080 8341 return false;
8081 8342 } );
8082
8083
8084
8343
8344
8345
8085 8346 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8086 8347 * Columns
8087 8348 *
8088 8349 * {integer} - column index (>=0 count from left, <0 count from right)
8089 8350 * "{integer}:visIdx" - visible column index (i.e. translate to column index) (>=0 count from left, <0 count from right)
8090 8351 * "{integer}:visible" - alias for {integer}:visIdx (>=0 count from left, <0 count from right)
8091 8352 * "{string}:name" - column name
8092 8353 * "{string}" - jQuery selector on column header nodes
8093 8354 *
8094 8355 */
8095
8356
8096 8357 // can be an array of these items, comma separated list, or an array of comma
8097 8358 // separated lists
8098
8099 var __re_column_selector = /^(.+):(name|visIdx|visible)$/;
8100
8101
8359
8360 var __re_column_selector = /^([^:]+):(name|visIdx|visible)$/;
8361
8362
8102 8363 // r1 and r2 are redundant - but it means that the parameters match for the
8103 8364 // iterator callback in columns().data()
8104 8365 var __columnData = function ( settings, column, r1, r2, rows ) {
8105 8366 var a = [];
8106 8367 for ( var row=0, ien=rows.length ; row<ien ; row++ ) {
8107 8368 a.push( _fnGetCellData( settings, rows[row], column ) );
8108 8369 }
8109 8370 return a;
8110 8371 };
8111
8112
8372
8373
8113 8374 var __column_selector = function ( settings, selector, opts )
8114 8375 {
8115 8376 var
8116 8377 columns = settings.aoColumns,
8117 8378 names = _pluck( columns, 'sName' ),
8118 8379 nodes = _pluck( columns, 'nTh' );
8119
8120 return _selector_run( selector, function ( s ) {
8380
8381 var run = function ( s ) {
8121 8382 var selInt = _intVal( s );
8122
8383
8123 8384 // Selector - all
8124 8385 if ( s === '' ) {
8125 8386 return _range( columns.length );
8126 8387 }
8127
8388
8128 8389 // Selector - index
8129 8390 if ( selInt !== null ) {
8130 8391 return [ selInt >= 0 ?
8131 8392 selInt : // Count from left
8132 8393 columns.length + selInt // Count from right (+ because its a negative value)
8133 8394 ];
8134 8395 }
8135
8396
8136 8397 // Selector = function
8137 8398 if ( typeof s === 'function' ) {
8138 8399 var rows = _selector_row_indexes( settings, opts );
8139
8400
8140 8401 return $.map( columns, function (col, idx) {
8141 8402 return s(
8142 8403 idx,
8143 8404 __columnData( settings, idx, 0, 0, rows ),
8144 8405 nodes[ idx ]
8145 8406 ) ? idx : null;
8146 8407 } );
8147 8408 }
8148
8409
8149 8410 // jQuery or string selector
8150 8411 var match = typeof s === 'string' ?
8151 8412 s.match( __re_column_selector ) :
8152 8413 '';
8153
8414
8154 8415 if ( match ) {
8155 8416 switch( match[2] ) {
8156 8417 case 'visIdx':
8157 8418 case 'visible':
8158 8419 var idx = parseInt( match[1], 10 );
8159 8420 // Visible index given, convert to column index
8160 8421 if ( idx < 0 ) {
8161 8422 // Counting from the right
8162 8423 var visColumns = $.map( columns, function (col,i) {
8163 8424 return col.bVisible ? i : null;
8164 8425 } );
8165 8426 return [ visColumns[ visColumns.length + idx ] ];
8166 8427 }
8167 8428 // Counting from the left
8168 8429 return [ _fnVisibleToColumnIndex( settings, idx ) ];
8169
8430
8170 8431 case 'name':
8171 8432 // match by name. `names` is column index complete and in order
8172 8433 return $.map( names, function (name, i) {
8173 8434 return name === match[1] ? i : null;
8174 8435 } );
8175 }
8176 }
8177 else {
8178 // jQuery selector on the TH elements for the columns
8179 return $( nodes )
8180 .filter( s )
8181 .map( function () {
8182 return $.inArray( this, nodes ); // `nodes` is column index complete and in order
8183 } )
8184 .toArray();
8185 }
8186 } );
8436
8437 default:
8438 return [];
8439 }
8440 }
8441
8442 // Cell in the table body
8443 if ( s.nodeName && s._DT_CellIndex ) {
8444 return [ s._DT_CellIndex.column ];
8445 }
8446
8447 // jQuery selector on the TH elements for the columns
8448 var jqResult = $( nodes )
8449 .filter( s )
8450 .map( function () {
8451 return $.inArray( this, nodes ); // `nodes` is column index complete and in order
8452 } )
8453 .toArray();
8454
8455 if ( jqResult.length || ! s.nodeName ) {
8456 return jqResult;
8457 }
8458
8459 // Otherwise a node which might have a `dt-column` data attribute, or be
8460 // a child or such an element
8461 var host = $(s).closest('*[data-dt-column]');
8462 return host.length ?
8463 [ host.data('dt-column') ] :
8464 [];
8465 };
8466
8467 return _selector_run( 'column', selector, run, settings, opts );
8187 8468 };
8188
8189
8190 var __setColumnVis = function ( settings, column, vis, recalc ) {
8469
8470
8471 var __setColumnVis = function ( settings, column, vis ) {
8191 8472 var
8192 8473 cols = settings.aoColumns,
8193 8474 col = cols[ column ],
8194 8475 data = settings.aoData,
8195 8476 row, cells, i, ien, tr;
8196
8477
8197 8478 // Get
8198 8479 if ( vis === undefined ) {
8199 8480 return col.bVisible;
8200 8481 }
8201
8482
8202 8483 // Set
8203 8484 // No change
8204 8485 if ( col.bVisible === vis ) {
8205 8486 return;
8206 8487 }
8207
8488
8208 8489 if ( vis ) {
8209 8490 // Insert column
8210 8491 // Need to decide if we should use appendChild or insertBefore
8211 8492 var insertBefore = $.inArray( true, _pluck(cols, 'bVisible'), column+1 );
8212
8493
8213 8494 for ( i=0, ien=data.length ; i<ien ; i++ ) {
8214 8495 tr = data[i].nTr;
8215 8496 cells = data[i].anCells;
8216
8497
8217 8498 if ( tr ) {
8218 8499 // insertBefore can act like appendChild if 2nd arg is null
8219 8500 tr.insertBefore( cells[ column ], cells[ insertBefore ] || null );
8220 8501 }
8221 8502 }
8222 8503 }
8223 8504 else {
8224 8505 // Remove column
8225 8506 $( _pluck( settings.aoData, 'anCells', column ) ).detach();
8226 8507 }
8227
8508
8228 8509 // Common actions
8229 8510 col.bVisible = vis;
8230 8511 _fnDrawHead( settings, settings.aoHeader );
8231 8512 _fnDrawHead( settings, settings.aoFooter );
8232
8233 if ( recalc === undefined || recalc ) {
8234 // Automatically adjust column sizing
8235 _fnAdjustColumnSizing( settings );
8236
8237 // Realign columns for scrolling
8238 if ( settings.oScroll.sX || settings.oScroll.sY ) {
8239 _fnScrollDraw( settings );
8240 }
8241 }
8242
8243 _fnCallbackFire( settings, null, 'column-visibility', [settings, column, vis] );
8244
8513
8245 8514 _fnSaveState( settings );
8246 8515 };
8247
8248
8249 /**
8250 *
8251 */
8516
8517
8252 8518 _api_register( 'columns()', function ( selector, opts ) {
8253 8519 // argument shifting
8254 8520 if ( selector === undefined ) {
8255 8521 selector = '';
8256 8522 }
8257 8523 else if ( $.isPlainObject( selector ) ) {
8258 8524 opts = selector;
8259 8525 selector = '';
8260 8526 }
8261
8527
8262 8528 opts = _selector_opts( opts );
8263
8529
8264 8530 var inst = this.iterator( 'table', function ( settings ) {
8265 8531 return __column_selector( settings, selector, opts );
8266 8532 }, 1 );
8267
8533
8268 8534 // Want argument shifting here and in _row_selector?
8269 8535 inst.selector.cols = selector;
8270 8536 inst.selector.opts = opts;
8271
8537
8272 8538 return inst;
8273 8539 } );
8274
8275
8276 /**
8277 *
8278 */
8540
8279 8541 _api_registerPlural( 'columns().header()', 'column().header()', function ( selector, opts ) {
8280 8542 return this.iterator( 'column', function ( settings, column ) {
8281 8543 return settings.aoColumns[column].nTh;
8282 8544 }, 1 );
8283 8545 } );
8284
8285
8286 /**
8287 *
8288 */
8546
8289 8547 _api_registerPlural( 'columns().footer()', 'column().footer()', function ( selector, opts ) {
8290 8548 return this.iterator( 'column', function ( settings, column ) {
8291 8549 return settings.aoColumns[column].nTf;
8292 8550 }, 1 );
8293 8551 } );
8294
8295
8296 /**
8297 *
8298 */
8552
8299 8553 _api_registerPlural( 'columns().data()', 'column().data()', function () {
8300 8554 return this.iterator( 'column-rows', __columnData, 1 );
8301 8555 } );
8302
8303
8556
8304 8557 _api_registerPlural( 'columns().dataSrc()', 'column().dataSrc()', function () {
8305 8558 return this.iterator( 'column', function ( settings, column ) {
8306 8559 return settings.aoColumns[column].mData;
8307 8560 }, 1 );
8308 8561 } );
8309
8310
8562
8311 8563 _api_registerPlural( 'columns().cache()', 'column().cache()', function ( type ) {
8312 8564 return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
8313 8565 return _pluck_order( settings.aoData, rows,
8314 8566 type === 'search' ? '_aFilterData' : '_aSortData', column
8315 8567 );
8316 8568 }, 1 );
8317 8569 } );
8318
8319
8570
8320 8571 _api_registerPlural( 'columns().nodes()', 'column().nodes()', function () {
8321 8572 return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
8322 8573 return _pluck_order( settings.aoData, rows, 'anCells', column ) ;
8323 8574 }, 1 );
8324 8575 } );
8325
8326
8327
8576
8328 8577 _api_registerPlural( 'columns().visible()', 'column().visible()', function ( vis, calc ) {
8329 return this.iterator( 'column', function ( settings, column ) {
8578 var ret = this.iterator( 'column', function ( settings, column ) {
8330 8579 if ( vis === undefined ) {
8331 8580 return settings.aoColumns[ column ].bVisible;
8332 8581 } // else
8333 __setColumnVis( settings, column, vis, calc );
8582 __setColumnVis( settings, column, vis );
8334 8583 } );
8335 } );
8336
8337
8338
8584
8585 // Group the column visibility changes
8586 if ( vis !== undefined ) {
8587 // Second loop once the first is done for events
8588 this.iterator( 'column', function ( settings, column ) {
8589 _fnCallbackFire( settings, null, 'column-visibility', [settings, column, vis, calc] );
8590 } );
8591
8592 if ( calc === undefined || calc ) {
8593 this.columns.adjust();
8594 }
8595 }
8596
8597 return ret;
8598 } );
8599
8339 8600 _api_registerPlural( 'columns().indexes()', 'column().index()', function ( type ) {
8340 8601 return this.iterator( 'column', function ( settings, column ) {
8341 8602 return type === 'visible' ?
8342 8603 _fnColumnIndexToVisible( settings, column ) :
8343 8604 column;
8344 8605 }, 1 );
8345 8606 } );
8346
8347
8348 // _api_register( 'columns().show()', function () {
8349 // var selector = this.selector;
8350 // return this.columns( selector.cols, selector.opts ).visible( true );
8351 // } );
8352
8353
8354 // _api_register( 'columns().hide()', function () {
8355 // var selector = this.selector;
8356 // return this.columns( selector.cols, selector.opts ).visible( false );
8357 // } );
8358
8359
8360
8607
8361 8608 _api_register( 'columns.adjust()', function () {
8362 8609 return this.iterator( 'table', function ( settings ) {
8363 8610 _fnAdjustColumnSizing( settings );
8364 8611 }, 1 );
8365 8612 } );
8366
8367
8368 // Convert from one column index type, to another type
8613
8369 8614 _api_register( 'column.index()', function ( type, idx ) {
8370 8615 if ( this.context.length !== 0 ) {
8371 8616 var ctx = this.context[0];
8372
8617
8373 8618 if ( type === 'fromVisible' || type === 'toData' ) {
8374 8619 return _fnVisibleToColumnIndex( ctx, idx );
8375 8620 }
8376 8621 else if ( type === 'fromData' || type === 'toVisible' ) {
8377 8622 return _fnColumnIndexToVisible( ctx, idx );
8378 8623 }
8379 8624 }
8380 8625 } );
8381
8382
8626
8383 8627 _api_register( 'column()', function ( selector, opts ) {
8384 8628 return _selector_first( this.columns( selector, opts ) );
8385 8629 } );
8386
8387
8388
8389
8630
8631
8632
8390 8633 var __cell_selector = function ( settings, selector, opts )
8391 8634 {
8392 8635 var data = settings.aoData;
8393 8636 var rows = _selector_row_indexes( settings, opts );
8394 8637 var cells = _removeEmpty( _pluck_order( data, rows, 'anCells' ) );
8395 8638 var allCells = $( [].concat.apply([], cells) );
8396 8639 var row;
8397 8640 var columns = settings.aoColumns.length;
8398 8641 var a, i, ien, j, o, host;
8399
8400 return _selector_run( selector, function ( s ) {
8642
8643 var run = function ( s ) {
8401 8644 var fnSelector = typeof s === 'function';
8402
8645
8403 8646 if ( s === null || s === undefined || fnSelector ) {
8404 8647 // All cells and function selectors
8405 8648 a = [];
8406
8649
8407 8650 for ( i=0, ien=rows.length ; i<ien ; i++ ) {
8408 8651 row = rows[i];
8409
8652
8410 8653 for ( j=0 ; j<columns ; j++ ) {
8411 8654 o = {
8412 8655 row: row,
8413 8656 column: j
8414 8657 };
8415
8658
8416 8659 if ( fnSelector ) {
8417 8660 // Selector - function
8418 host = settings.aoData[ row ];
8419
8420 if ( s( o, _fnGetCellData(settings, row, j), host.anCells[j] ) ) {
8661 host = data[ row ];
8662
8663 if ( s( o, _fnGetCellData(settings, row, j), host.anCells ? host.anCells[j] : null ) ) {
8421 8664 a.push( o );
8422 8665 }
8423 8666 }
8424 8667 else {
8425 8668 // Selector - all
8426 8669 a.push( o );
8427 8670 }
8428 8671 }
8429 8672 }
8430
8673
8431 8674 return a;
8432 8675 }
8433
8676
8434 8677 // Selector - index
8435 8678 if ( $.isPlainObject( s ) ) {
8436 8679 return [s];
8437 8680 }
8438
8681
8439 8682 // Selector - jQuery filtered cells
8440 return allCells
8683 var jqResult = allCells
8441 8684 .filter( s )
8442 8685 .map( function (i, el) {
8443 row = el.parentNode._DT_RowIndex;
8444
8445 return {
8446 row: row,
8447 column: $.inArray( el, data[ row ].anCells )
8448 };
8686 return { // use a new object, in case someone changes the values
8687 row: el._DT_CellIndex.row,
8688 column: el._DT_CellIndex.column
8689 };
8449 8690 } )
8450 8691 .toArray();
8451 } );
8692
8693 if ( jqResult.length || ! s.nodeName ) {
8694 return jqResult;
8695 }
8696
8697 // Otherwise the selector is a node, and there is one last option - the
8698 // element might be a child of an element which has dt-row and dt-column
8699 // data attributes
8700 host = $(s).closest('*[data-dt-row]');
8701 return host.length ?
8702 [ {
8703 row: host.data('dt-row'),
8704 column: host.data('dt-column')
8705 } ] :
8706 [];
8707 };
8708
8709 return _selector_run( 'cell', selector, run, settings, opts );
8452 8710 };
8453
8454
8455
8456
8711
8712
8713
8714
8457 8715 _api_register( 'cells()', function ( rowSelector, columnSelector, opts ) {
8458 8716 // Argument shifting
8459 8717 if ( $.isPlainObject( rowSelector ) ) {
8460 8718 // Indexes
8461 if ( typeof rowSelector.row !== undefined ) {
8719 if ( rowSelector.row === undefined ) {
8720 // Selector options in first parameter
8721 opts = rowSelector;
8722 rowSelector = null;
8723 }
8724 else {
8725 // Cell index objects in first parameter
8462 8726 opts = columnSelector;
8463 8727 columnSelector = null;
8464 8728 }
8465 else {
8466 opts = rowSelector;
8467 rowSelector = null;
8468 }
8469 8729 }
8470 8730 if ( $.isPlainObject( columnSelector ) ) {
8471 8731 opts = columnSelector;
8472 8732 columnSelector = null;
8473 8733 }
8474
8734
8475 8735 // Cell selector
8476 8736 if ( columnSelector === null || columnSelector === undefined ) {
8477 8737 return this.iterator( 'table', function ( settings ) {
8478 8738 return __cell_selector( settings, rowSelector, _selector_opts( opts ) );
8479 8739 } );
8480 8740 }
8481
8741
8482 8742 // Row + column selector
8483 8743 var columns = this.columns( columnSelector, opts );
8484 8744 var rows = this.rows( rowSelector, opts );
8485 8745 var a, i, ien, j, jen;
8486
8746
8487 8747 var cells = this.iterator( 'table', function ( settings, idx ) {
8488 8748 a = [];
8489
8749
8490 8750 for ( i=0, ien=rows[idx].length ; i<ien ; i++ ) {
8491 8751 for ( j=0, jen=columns[idx].length ; j<jen ; j++ ) {
8492 8752 a.push( {
8493 8753 row: rows[idx][i],
8494 8754 column: columns[idx][j]
8495 8755 } );
8496 8756 }
8497 8757 }
8498
8758
8499 8759 return a;
8500 8760 }, 1 );
8501
8761
8502 8762 $.extend( cells.selector, {
8503 8763 cols: columnSelector,
8504 8764 rows: rowSelector,
8505 8765 opts: opts
8506 8766 } );
8507
8767
8508 8768 return cells;
8509 8769 } );
8510
8511
8770
8771
8512 8772 _api_registerPlural( 'cells().nodes()', 'cell().node()', function () {
8513 8773 return this.iterator( 'cell', function ( settings, row, column ) {
8514 var cells = settings.aoData[ row ].anCells;
8515 return cells ?
8516 cells[ column ] :
8774 var data = settings.aoData[ row ];
8775
8776 return data && data.anCells ?
8777 data.anCells[ column ] :
8517 8778 undefined;
8518 8779 }, 1 );
8519 8780 } );
8520
8521
8781
8782
8522 8783 _api_register( 'cells().data()', function () {
8523 8784 return this.iterator( 'cell', function ( settings, row, column ) {
8524 8785 return _fnGetCellData( settings, row, column );
8525 8786 }, 1 );
8526 8787 } );
8527
8528
8788
8789
8529 8790 _api_registerPlural( 'cells().cache()', 'cell().cache()', function ( type ) {
8530 8791 type = type === 'search' ? '_aFilterData' : '_aSortData';
8531
8792
8532 8793 return this.iterator( 'cell', function ( settings, row, column ) {
8533 8794 return settings.aoData[ row ][ type ][ column ];
8534 8795 }, 1 );
8535 8796 } );
8536
8537
8797
8798
8538 8799 _api_registerPlural( 'cells().render()', 'cell().render()', function ( type ) {
8539 8800 return this.iterator( 'cell', function ( settings, row, column ) {
8540 8801 return _fnGetCellData( settings, row, column, type );
8541 8802 }, 1 );
8542 8803 } );
8543
8544
8804
8805
8545 8806 _api_registerPlural( 'cells().indexes()', 'cell().index()', function () {
8546 8807 return this.iterator( 'cell', function ( settings, row, column ) {
8547 8808 return {
8548 8809 row: row,
8549 8810 column: column,
8550 8811 columnVisible: _fnColumnIndexToVisible( settings, column )
8551 8812 };
8552 8813 }, 1 );
8553 8814 } );
8554
8555
8815
8816
8556 8817 _api_registerPlural( 'cells().invalidate()', 'cell().invalidate()', function ( src ) {
8557 8818 return this.iterator( 'cell', function ( settings, row, column ) {
8558 8819 _fnInvalidate( settings, row, src, column );
8559 8820 } );
8560 8821 } );
8561
8562
8563
8822
8823
8824
8564 8825 _api_register( 'cell()', function ( rowSelector, columnSelector, opts ) {
8565 8826 return _selector_first( this.cells( rowSelector, columnSelector, opts ) );
8566 8827 } );
8567
8568
8828
8829
8569 8830 _api_register( 'cell().data()', function ( data ) {
8570 8831 var ctx = this.context;
8571 8832 var cell = this[0];
8572
8833
8573 8834 if ( data === undefined ) {
8574 8835 // Get
8575 8836 return ctx.length && cell.length ?
8576 8837 _fnGetCellData( ctx[0], cell[0].row, cell[0].column ) :
8577 8838 undefined;
8578 8839 }
8579
8840
8580 8841 // Set
8581 8842 _fnSetCellData( ctx[0], cell[0].row, cell[0].column, data );
8582 8843 _fnInvalidate( ctx[0], cell[0].row, 'data', cell[0].column );
8583
8844
8584 8845 return this;
8585 8846 } );
8586
8587
8588
8847
8848
8849
8589 8850 /**
8590 8851 * Get current ordering (sorting) that has been applied to the table.
8591 8852 *
8592 8853 * @returns {array} 2D array containing the sorting information for the first
8593 8854 * table in the current context. Each element in the parent array represents
8594 8855 * a column being sorted upon (i.e. multi-sorting with two columns would have
8595 8856 * 2 inner arrays). The inner arrays may have 2 or 3 elements. The first is
8596 8857 * the column index that the sorting condition applies to, the second is the
8597 8858 * direction of the sort (`desc` or `asc`) and, optionally, the third is the
8598 8859 * index of the sorting order from the `column.sorting` initialisation array.
8599 8860 *//**
8600 8861 * Set the ordering for the table.
8601 8862 *
8602 8863 * @param {integer} order Column index to sort upon.
8603 8864 * @param {string} direction Direction of the sort to be applied (`asc` or `desc`)
8604 8865 * @returns {DataTables.Api} this
8605 8866 *//**
8606 8867 * Set the ordering for the table.
8607 8868 *
8608 8869 * @param {array} order 1D array of sorting information to be applied.
8609 8870 * @param {array} [...] Optional additional sorting conditions
8610 8871 * @returns {DataTables.Api} this
8611 8872 *//**
8612 8873 * Set the ordering for the table.
8613 8874 *
8614 8875 * @param {array} order 2D array of sorting information to be applied.
8615 8876 * @returns {DataTables.Api} this
8616 8877 */
8617 8878 _api_register( 'order()', function ( order, dir ) {
8618 8879 var ctx = this.context;
8619
8880
8620 8881 if ( order === undefined ) {
8621 8882 // get
8622 8883 return ctx.length !== 0 ?
8623 8884 ctx[0].aaSorting :
8624 8885 undefined;
8625 8886 }
8626
8887
8627 8888 // set
8628 8889 if ( typeof order === 'number' ) {
8629 8890 // Simple column / direction passed in
8630 8891 order = [ [ order, dir ] ];
8631 8892 }
8632 else if ( ! $.isArray( order[0] ) ) {
8893 else if ( order.length && ! $.isArray( order[0] ) ) {
8633 8894 // Arguments passed in (list of 1D arrays)
8634 8895 order = Array.prototype.slice.call( arguments );
8635 8896 }
8636 8897 // otherwise a 2D array was passed in
8637
8898
8638 8899 return this.iterator( 'table', function ( settings ) {
8639 8900 settings.aaSorting = order.slice();
8640 8901 } );
8641 8902 } );
8642
8643
8903
8904
8644 8905 /**
8645 8906 * Attach a sort listener to an element for a given column
8646 8907 *
8647 8908 * @param {node|jQuery|string} node Identifier for the element(s) to attach the
8648 8909 * listener to. This can take the form of a single DOM node, a jQuery
8649 8910 * collection of nodes or a jQuery selector which will identify the node(s).
8650 8911 * @param {integer} column the column that a click on this node will sort on
8651 8912 * @param {function} [callback] callback function when sort is run
8652 8913 * @returns {DataTables.Api} this
8653 8914 */
8654 8915 _api_register( 'order.listener()', function ( node, column, callback ) {
8655 8916 return this.iterator( 'table', function ( settings ) {
8656 8917 _fnSortAttachListener( settings, node, column, callback );
8657 8918 } );
8658 8919 } );
8659
8660
8920
8921
8922 _api_register( 'order.fixed()', function ( set ) {
8923 if ( ! set ) {
8924 var ctx = this.context;
8925 var fixed = ctx.length ?
8926 ctx[0].aaSortingFixed :
8927 undefined;
8928
8929 return $.isArray( fixed ) ?
8930 { pre: fixed } :
8931 fixed;
8932 }
8933
8934 return this.iterator( 'table', function ( settings ) {
8935 settings.aaSortingFixed = $.extend( true, {}, set );
8936 } );
8937 } );
8938
8939
8661 8940 // Order by the selected column(s)
8662 8941 _api_register( [
8663 8942 'columns().order()',
8664 8943 'column().order()'
8665 8944 ], function ( dir ) {
8666 8945 var that = this;
8667
8946
8668 8947 return this.iterator( 'table', function ( settings, i ) {
8669 8948 var sort = [];
8670
8949
8671 8950 $.each( that[i], function (j, col) {
8672 8951 sort.push( [ col, dir ] );
8673 8952 } );
8674
8953
8675 8954 settings.aaSorting = sort;
8676 8955 } );
8677 8956 } );
8678
8679
8680
8957
8958
8959
8681 8960 _api_register( 'search()', function ( input, regex, smart, caseInsen ) {
8682 8961 var ctx = this.context;
8683
8962
8684 8963 if ( input === undefined ) {
8685 8964 // get
8686 8965 return ctx.length !== 0 ?
8687 8966 ctx[0].oPreviousSearch.sSearch :
8688 8967 undefined;
8689 8968 }
8690
8969
8691 8970 // set
8692 8971 return this.iterator( 'table', function ( settings ) {
8693 8972 if ( ! settings.oFeatures.bFilter ) {
8694 8973 return;
8695 8974 }
8696
8975
8697 8976 _fnFilterComplete( settings, $.extend( {}, settings.oPreviousSearch, {
8698 8977 "sSearch": input+"",
8699 8978 "bRegex": regex === null ? false : regex,
8700 8979 "bSmart": smart === null ? true : smart,
8701 8980 "bCaseInsensitive": caseInsen === null ? true : caseInsen
8702 8981 } ), 1 );
8703 8982 } );
8704 8983 } );
8705
8706
8984
8985
8707 8986 _api_registerPlural(
8708 8987 'columns().search()',
8709 8988 'column().search()',
8710 8989 function ( input, regex, smart, caseInsen ) {
8711 8990 return this.iterator( 'column', function ( settings, column ) {
8712 8991 var preSearch = settings.aoPreSearchCols;
8713
8992
8714 8993 if ( input === undefined ) {
8715 8994 // get
8716 8995 return preSearch[ column ].sSearch;
8717 8996 }
8718
8997
8719 8998 // set
8720 8999 if ( ! settings.oFeatures.bFilter ) {
8721 9000 return;
8722 9001 }
8723
9002
8724 9003 $.extend( preSearch[ column ], {
8725 9004 "sSearch": input+"",
8726 9005 "bRegex": regex === null ? false : regex,
8727 9006 "bSmart": smart === null ? true : smart,
8728 9007 "bCaseInsensitive": caseInsen === null ? true : caseInsen
8729 9008 } );
8730
9009
8731 9010 _fnFilterComplete( settings, settings.oPreviousSearch, 1 );
8732 9011 } );
8733 9012 }
8734 9013 );
8735
9014
8736 9015 /*
8737 9016 * State API methods
8738 9017 */
8739
9018
8740 9019 _api_register( 'state()', function () {
8741 9020 return this.context.length ?
8742 9021 this.context[0].oSavedState :
8743 9022 null;
8744 9023 } );
8745
8746
9024
9025
8747 9026 _api_register( 'state.clear()', function () {
8748 9027 return this.iterator( 'table', function ( settings ) {
8749 9028 // Save an empty object
8750 9029 settings.fnStateSaveCallback.call( settings.oInstance, settings, {} );
8751 9030 } );
8752 9031 } );
8753
8754
9032
9033
8755 9034 _api_register( 'state.loaded()', function () {
8756 9035 return this.context.length ?
8757 9036 this.context[0].oLoadedState :
8758 9037 null;
8759 9038 } );
8760
8761
9039
9040
8762 9041 _api_register( 'state.save()', function () {
8763 9042 return this.iterator( 'table', function ( settings ) {
8764 9043 _fnSaveState( settings );
8765 9044 } );
8766 9045 } );
8767
8768
8769
9046
9047
9048
8770 9049 /**
8771 9050 * Provide a common method for plug-ins to check the version of DataTables being
8772 9051 * used, in order to ensure compatibility.
8773 9052 *
8774 9053 * @param {string} version Version string to check for, in the format "X.Y.Z".
8775 9054 * Note that the formats "X" and "X.Y" are also acceptable.
8776 9055 * @returns {boolean} true if this version of DataTables is greater or equal to
8777 9056 * the required version, or false if this version of DataTales is not
8778 9057 * suitable
8779 9058 * @static
8780 9059 * @dtopt API-Static
8781 9060 *
8782 9061 * @example
8783 9062 * alert( $.fn.dataTable.versionCheck( '1.9.0' ) );
8784 9063 */
8785 9064 DataTable.versionCheck = DataTable.fnVersionCheck = function( version )
8786 9065 {
8787 9066 var aThis = DataTable.version.split('.');
8788 9067 var aThat = version.split('.');
8789 9068 var iThis, iThat;
8790
9069
8791 9070 for ( var i=0, iLen=aThat.length ; i<iLen ; i++ ) {
8792 9071 iThis = parseInt( aThis[i], 10 ) || 0;
8793 9072 iThat = parseInt( aThat[i], 10 ) || 0;
8794
9073
8795 9074 // Parts are the same, keep comparing
8796 9075 if (iThis === iThat) {
8797 9076 continue;
8798 9077 }
8799
9078
8800 9079 // Parts are different, return immediately
8801 9080 return iThis > iThat;
8802 9081 }
8803
9082
8804 9083 return true;
8805 9084 };
8806
8807
9085
9086
8808 9087 /**
8809 9088 * Check if a `<table>` node is a DataTable table already or not.
8810 9089 *
8811 9090 * @param {node|jquery|string} table Table node, jQuery object or jQuery
8812 9091 * selector for the table to test. Note that if more than more than one
8813 9092 * table is passed on, only the first will be checked
8814 9093 * @returns {boolean} true the table given is a DataTable, or false otherwise
8815 9094 * @static
8816 9095 * @dtopt API-Static
8817 9096 *
8818 9097 * @example
8819 9098 * if ( ! $.fn.DataTable.isDataTable( '#example' ) ) {
8820 9099 * $('#example').dataTable();
8821 9100 * }
8822 9101 */
8823 9102 DataTable.isDataTable = DataTable.fnIsDataTable = function ( table )
8824 9103 {
8825 9104 var t = $(table).get(0);
8826 9105 var is = false;
8827
9106
9107 if ( table instanceof DataTable.Api ) {
9108 return true;
9109 }
9110
8828 9111 $.each( DataTable.settings, function (i, o) {
8829 if ( o.nTable === t || o.nScrollHead === t || o.nScrollFoot === t ) {
9112 var head = o.nScrollHead ? $('table', o.nScrollHead)[0] : null;
9113 var foot = o.nScrollFoot ? $('table', o.nScrollFoot)[0] : null;
9114
9115 if ( o.nTable === t || head === t || foot === t ) {
8830 9116 is = true;
8831 9117 }
8832 9118 } );
8833
9119
8834 9120 return is;
8835 9121 };
8836
8837
9122
9123
8838 9124 /**
8839 9125 * Get all DataTable tables that have been initialised - optionally you can
8840 9126 * select to get only currently visible tables.
8841 9127 *
8842 9128 * @param {boolean} [visible=false] Flag to indicate if you want all (default)
8843 9129 * or visible tables only.
8844 9130 * @returns {array} Array of `table` nodes (not DataTable instances) which are
8845 9131 * DataTables
8846 9132 * @static
8847 9133 * @dtopt API-Static
8848 9134 *
8849 9135 * @example
8850 9136 * $.each( $.fn.dataTable.tables(true), function () {
8851 9137 * $(table).DataTable().columns.adjust();
8852 9138 * } );
8853 9139 */
8854 9140 DataTable.tables = DataTable.fnTables = function ( visible )
8855 9141 {
8856 return $.map( DataTable.settings, function (o) {
9142 var api = false;
9143
9144 if ( $.isPlainObject( visible ) ) {
9145 api = visible.api;
9146 visible = visible.visible;
9147 }
9148
9149 var a = $.map( DataTable.settings, function (o) {
8857 9150 if ( !visible || (visible && $(o.nTable).is(':visible')) ) {
8858 9151 return o.nTable;
8859 9152 }
8860 9153 } );
9154
9155 return api ?
9156 new _Api( a ) :
9157 a;
8861 9158 };
8862
8863
8864 /**
8865 * DataTables utility methods
8866 *
8867 * This namespace provides helper methods that DataTables uses internally to
8868 * create a DataTable, but which are not exclusively used only for DataTables.
8869 * These methods can be used by extension authors to save the duplication of
8870 * code.
8871 *
8872 * @namespace
8873 */
8874 DataTable.util = {
8875 /**
8876 * Throttle the calls to a function. Arguments and context are maintained
8877 * for the throttled function.
8878 *
8879 * @param {function} fn Function to be called
8880 * @param {integer} freq Call frequency in mS
8881 * @return {function} Wrapped function
8882 */
8883 throttle: _fnThrottle,
8884
8885
8886 /**
8887 * Escape a string such that it can be used in a regular expression
8888 *
8889 * @param {string} sVal string to escape
8890 * @returns {string} escaped string
8891 */
8892 escapeRegex: _fnEscapeRegex
8893 };
8894
8895
9159
9160
8896 9161 /**
8897 9162 * Convert from camel case parameters to Hungarian notation. This is made public
8898 9163 * for the extensions to provide the same ability as DataTables core to accept
8899 9164 * either the 1.9 style Hungarian notation, or the 1.10+ style camelCase
8900 9165 * parameters.
8901 9166 *
8902 9167 * @param {object} src The model object which holds all parameters that can be
8903 9168 * mapped.
8904 9169 * @param {object} user The object to convert from camel case to Hungarian.
8905 9170 * @param {boolean} force When set to `true`, properties which already have a
8906 9171 * Hungarian value in the `user` object will be overwritten. Otherwise they
8907 9172 * won't be.
8908 9173 */
8909 9174 DataTable.camelToHungarian = _fnCamelToHungarian;
8910
8911
8912
9175
9176
9177
8913 9178 /**
8914 9179 *
8915 9180 */
8916 9181 _api_register( '$()', function ( selector, opts ) {
8917 9182 var
8918 9183 rows = this.rows( opts ).nodes(), // Get all rows
8919 9184 jqRows = $(rows);
8920
9185
8921 9186 return $( [].concat(
8922 9187 jqRows.filter( selector ).toArray(),
8923 9188 jqRows.find( selector ).toArray()
8924 9189 ) );
8925 9190 } );
8926
8927
9191
9192
8928 9193 // jQuery functions to operate on the tables
8929 9194 $.each( [ 'on', 'one', 'off' ], function (i, key) {
8930 9195 _api_register( key+'()', function ( /* event, handler */ ) {
8931 9196 var args = Array.prototype.slice.call(arguments);
8932
9197
8933 9198 // Add the `dt` namespace automatically if it isn't already present
8934 if ( ! args[0].match(/\.dt\b/) ) {
8935 args[0] += '.dt';
8936 }
8937
9199 args[0] = $.map( args[0].split( /\s/ ), function ( e ) {
9200 return ! e.match(/\.dt\b/) ?
9201 e+'.dt' :
9202 e;
9203 } ).join( ' ' );
9204
8938 9205 var inst = $( this.tables().nodes() );
8939 9206 inst[key].apply( inst, args );
8940 9207 return this;
8941 9208 } );
8942 9209 } );
8943
8944
9210
9211
8945 9212 _api_register( 'clear()', function () {
8946 9213 return this.iterator( 'table', function ( settings ) {
8947 9214 _fnClearTable( settings );
8948 9215 } );
8949 9216 } );
8950
8951
9217
9218
8952 9219 _api_register( 'settings()', function () {
8953 9220 return new _Api( this.context, this.context );
8954 9221 } );
8955
8956
9222
9223
9224 _api_register( 'init()', function () {
9225 var ctx = this.context;
9226 return ctx.length ? ctx[0].oInit : null;
9227 } );
9228
9229
8957 9230 _api_register( 'data()', function () {
8958 9231 return this.iterator( 'table', function ( settings ) {
8959 9232 return _pluck( settings.aoData, '_aData' );
8960 9233 } ).flatten();
8961 9234 } );
8962
8963
9235
9236
8964 9237 _api_register( 'destroy()', function ( remove ) {
8965 9238 remove = remove || false;
8966
9239
8967 9240 return this.iterator( 'table', function ( settings ) {
8968 9241 var orig = settings.nTableWrapper.parentNode;
8969 9242 var classes = settings.oClasses;
8970 9243 var table = settings.nTable;
8971 9244 var tbody = settings.nTBody;
8972 9245 var thead = settings.nTHead;
8973 9246 var tfoot = settings.nTFoot;
8974 9247 var jqTable = $(table);
8975 9248 var jqTbody = $(tbody);
8976 9249 var jqWrapper = $(settings.nTableWrapper);
8977 9250 var rows = $.map( settings.aoData, function (r) { return r.nTr; } );
8978 9251 var i, ien;
8979
9252
8980 9253 // Flag to note that the table is currently being destroyed - no action
8981 9254 // should be taken
8982 9255 settings.bDestroying = true;
8983
9256
8984 9257 // Fire off the destroy callbacks for plug-ins etc
8985 9258 _fnCallbackFire( settings, "aoDestroyCallback", "destroy", [settings] );
8986
9259
8987 9260 // If not being removed from the document, make all columns visible
8988 9261 if ( ! remove ) {
8989 9262 new _Api( settings ).columns().visible( true );
8990 9263 }
8991
9264
8992 9265 // Blitz all `DT` namespaced events (these are internal events, the
8993 9266 // lowercase, `dt` events are user subscribed and they are responsible
8994 9267 // for removing them
8995 jqWrapper.unbind('.DT').find(':not(tbody *)').unbind('.DT');
8996 $(window).unbind('.DT-'+settings.sInstance);
8997
9268 jqWrapper.off('.DT').find(':not(tbody *)').off('.DT');
9269 $(window).off('.DT-'+settings.sInstance);
9270
8998 9271 // When scrolling we had to break the table up - restore it
8999 9272 if ( table != thead.parentNode ) {
9000 9273 jqTable.children('thead').detach();
9001 9274 jqTable.append( thead );
9002 9275 }
9003
9276
9004 9277 if ( tfoot && table != tfoot.parentNode ) {
9005 9278 jqTable.children('tfoot').detach();
9006 9279 jqTable.append( tfoot );
9007 9280 }
9008
9009 // Remove the DataTables generated nodes, events and classes
9010 jqTable.detach();
9011 jqWrapper.detach();
9012
9281
9013 9282 settings.aaSorting = [];
9014 9283 settings.aaSortingFixed = [];
9015 9284 _fnSortingClasses( settings );
9016
9285
9017 9286 $( rows ).removeClass( settings.asStripeClasses.join(' ') );
9018
9287
9019 9288 $('th, td', thead).removeClass( classes.sSortable+' '+
9020 9289 classes.sSortableAsc+' '+classes.sSortableDesc+' '+classes.sSortableNone
9021 9290 );
9022
9291
9023 9292 if ( settings.bJUI ) {
9024 9293 $('th span.'+classes.sSortIcon+ ', td span.'+classes.sSortIcon, thead).detach();
9025 9294 $('th, td', thead).each( function () {
9026 9295 var wrapper = $('div.'+classes.sSortJUIWrapper, this);
9027 9296 $(this).append( wrapper.contents() );
9028 9297 wrapper.detach();
9029 9298 } );
9030 9299 }
9031
9032 if ( ! remove && orig ) {
9033 // insertBefore acts like appendChild if !arg[1]
9034 orig.insertBefore( table, settings.nTableReinsertBefore );
9035 }
9036
9300
9037 9301 // Add the TR elements back into the table in their original order
9038 9302 jqTbody.children().detach();
9039 9303 jqTbody.append( rows );
9040
9041 // Restore the width of the original table - was read from the style property,
9042 // so we can restore directly to that
9043 jqTable
9044 .css( 'width', settings.sDestroyWidth )
9045 .removeClass( classes.sTable );
9046
9047 // If the were originally stripe classes - then we add them back here.
9048 // Note this is not fool proof (for example if not all rows had stripe
9049 // classes - but it's a good effort without getting carried away
9050 ien = settings.asDestroyStripes.length;
9051
9052 if ( ien ) {
9053 jqTbody.children().each( function (i) {
9054 $(this).addClass( settings.asDestroyStripes[i % ien] );
9055 } );
9056 }
9057
9304
9305 // Remove the DataTables generated nodes, events and classes
9306 var removedMethod = remove ? 'remove' : 'detach';
9307 jqTable[ removedMethod ]();
9308 jqWrapper[ removedMethod ]();
9309
9310 // If we need to reattach the table to the document
9311 if ( ! remove && orig ) {
9312 // insertBefore acts like appendChild if !arg[1]
9313 orig.insertBefore( table, settings.nTableReinsertBefore );
9314
9315 // Restore the width of the original table - was read from the style property,
9316 // so we can restore directly to that
9317 jqTable
9318 .css( 'width', settings.sDestroyWidth )
9319 .removeClass( classes.sTable );
9320
9321 // If the were originally stripe classes - then we add them back here.
9322 // Note this is not fool proof (for example if not all rows had stripe
9323 // classes - but it's a good effort without getting carried away
9324 ien = settings.asDestroyStripes.length;
9325
9326 if ( ien ) {
9327 jqTbody.children().each( function (i) {
9328 $(this).addClass( settings.asDestroyStripes[i % ien] );
9329 } );
9330 }
9331 }
9332
9058 9333 /* Remove the settings object from the settings array */
9059 9334 var idx = $.inArray( settings, DataTable.settings );
9060 9335 if ( idx !== -1 ) {
9061 9336 DataTable.settings.splice( idx, 1 );
9062 9337 }
9063 9338 } );
9064 9339 } );
9065
9340
9341
9342 // Add the `every()` method for rows, columns and cells in a compact form
9343 $.each( [ 'column', 'row', 'cell' ], function ( i, type ) {
9344 _api_register( type+'s().every()', function ( fn ) {
9345 var opts = this.selector.opts;
9346 var api = this;
9347
9348 return this.iterator( type, function ( settings, arg1, arg2, arg3, arg4 ) {
9349 // Rows and columns:
9350 // arg1 - index
9351 // arg2 - table counter
9352 // arg3 - loop counter
9353 // arg4 - undefined
9354 // Cells:
9355 // arg1 - row index
9356 // arg2 - column index
9357 // arg3 - table counter
9358 // arg4 - loop counter
9359 fn.call(
9360 api[ type ](
9361 arg1,
9362 type==='cell' ? arg2 : opts,
9363 type==='cell' ? opts : undefined
9364 ),
9365 arg1, arg2, arg3, arg4
9366 );
9367 } );
9368 } );
9369 } );
9370
9371
9372 // i18n method for extensions to be able to use the language object from the
9373 // DataTable
9374 _api_register( 'i18n()', function ( token, def, plural ) {
9375 var ctx = this.context[0];
9376 var resolved = _fnGetObjectDataFn( token )( ctx.oLanguage );
9377
9378 if ( resolved === undefined ) {
9379 resolved = def;
9380 }
9381
9382 if ( plural !== undefined && $.isPlainObject( resolved ) ) {
9383 resolved = resolved[ plural ] !== undefined ?
9384 resolved[ plural ] :
9385 resolved._;
9386 }
9387
9388 return resolved.replace( '%d', plural ); // nb: plural might be undefined,
9389 } );
9066 9390
9067 9391 /**
9068 9392 * Version string for plug-ins to check compatibility. Allowed format is
9069 9393 * `a.b.c-d` where: a:int, b:int, c:int, d:string(dev|beta|alpha). `d` is used
9070 9394 * only for non-release builds. See http://semver.org/ for more information.
9071 9395 * @member
9072 9396 * @type string
9073 9397 * @default Version number
9074 9398 */
9075 DataTable.version = "1.10.4";
9399 DataTable.version = "1.10.13";
9076 9400
9077 9401 /**
9078 9402 * Private data store, containing all of the settings objects that are
9079 9403 * created for the tables on a given page.
9080 9404 *
9081 9405 * Note that the `DataTable.settings` object is aliased to
9082 9406 * `jQuery.fn.dataTableExt` through which it may be accessed and
9083 9407 * manipulated, or `jQuery.fn.dataTable.settings`.
9084 9408 * @member
9085 9409 * @type array
9086 9410 * @default []
9087 9411 * @private
9088 9412 */
9089 9413 DataTable.settings = [];
9090 9414
9091 9415 /**
9092 9416 * Object models container, for the various models that DataTables has
9093 9417 * available to it. These models define the objects that are used to hold
9094 9418 * the active state and configuration of the table.
9095 9419 * @namespace
9096 9420 */
9097 9421 DataTable.models = {};
9098
9099
9100
9422
9423
9424
9101 9425 /**
9102 9426 * Template object for the way in which DataTables holds information about
9103 9427 * search information for the global filter and individual column filters.
9104 9428 * @namespace
9105 9429 */
9106 9430 DataTable.models.oSearch = {
9107 9431 /**
9108 9432 * Flag to indicate if the filtering should be case insensitive or not
9109 9433 * @type boolean
9110 9434 * @default true
9111 9435 */
9112 9436 "bCaseInsensitive": true,
9113
9437
9114 9438 /**
9115 9439 * Applied search term
9116 9440 * @type string
9117 9441 * @default <i>Empty string</i>
9118 9442 */
9119 9443 "sSearch": "",
9120
9444
9121 9445 /**
9122 9446 * Flag to indicate if the search term should be interpreted as a
9123 9447 * regular expression (true) or not (false) and therefore and special
9124 9448 * regex characters escaped.
9125 9449 * @type boolean
9126 9450 * @default false
9127 9451 */
9128 9452 "bRegex": false,
9129
9453
9130 9454 /**
9131 9455 * Flag to indicate if DataTables is to use its smart filtering or not.
9132 9456 * @type boolean
9133 9457 * @default true
9134 9458 */
9135 9459 "bSmart": true
9136 9460 };
9137
9138
9139
9140
9461
9462
9463
9464
9141 9465 /**
9142 9466 * Template object for the way in which DataTables holds information about
9143 9467 * each individual row. This is the object format used for the settings
9144 9468 * aoData array.
9145 9469 * @namespace
9146 9470 */
9147 9471 DataTable.models.oRow = {
9148 9472 /**
9149 9473 * TR element for the row
9150 9474 * @type node
9151 9475 * @default null
9152 9476 */
9153 9477 "nTr": null,
9154
9478
9155 9479 /**
9156 9480 * Array of TD elements for each row. This is null until the row has been
9157 9481 * created.
9158 9482 * @type array nodes
9159 9483 * @default []
9160 9484 */
9161 9485 "anCells": null,
9162
9486
9163 9487 /**
9164 9488 * Data object from the original data source for the row. This is either
9165 9489 * an array if using the traditional form of DataTables, or an object if
9166 9490 * using mData options. The exact type will depend on the passed in
9167 9491 * data from the data source, or will be an array if using DOM a data
9168 9492 * source.
9169 9493 * @type array|object
9170 9494 * @default []
9171 9495 */
9172 9496 "_aData": [],
9173
9497
9174 9498 /**
9175 9499 * Sorting data cache - this array is ostensibly the same length as the
9176 9500 * number of columns (although each index is generated only as it is
9177 9501 * needed), and holds the data that is used for sorting each column in the
9178 9502 * row. We do this cache generation at the start of the sort in order that
9179 9503 * the formatting of the sort data need be done only once for each cell
9180 9504 * per sort. This array should not be read from or written to by anything
9181 9505 * other than the master sorting methods.
9182 9506 * @type array
9183 9507 * @default null
9184 9508 * @private
9185 9509 */
9186 9510 "_aSortData": null,
9187
9511
9188 9512 /**
9189 9513 * Per cell filtering data cache. As per the sort data cache, used to
9190 9514 * increase the performance of the filtering in DataTables
9191 9515 * @type array
9192 9516 * @default null
9193 9517 * @private
9194 9518 */
9195 9519 "_aFilterData": null,
9196
9520
9197 9521 /**
9198 9522 * Filtering data cache. This is the same as the cell filtering cache, but
9199 9523 * in this case a string rather than an array. This is easily computed with
9200 9524 * a join on `_aFilterData`, but is provided as a cache so the join isn't
9201 9525 * needed on every search (memory traded for performance)
9202 9526 * @type array
9203 9527 * @default null
9204 9528 * @private
9205 9529 */
9206 9530 "_sFilterRow": null,
9207
9531
9208 9532 /**
9209 9533 * Cache of the class name that DataTables has applied to the row, so we
9210 9534 * can quickly look at this variable rather than needing to do a DOM check
9211 9535 * on className for the nTr property.
9212 9536 * @type string
9213 9537 * @default <i>Empty string</i>
9214 9538 * @private
9215 9539 */
9216 9540 "_sRowStripe": "",
9217
9541
9218 9542 /**
9219 9543 * Denote if the original data source was from the DOM, or the data source
9220 9544 * object. This is used for invalidating data, so DataTables can
9221 9545 * automatically read data from the original source, unless uninstructed
9222 9546 * otherwise.
9223 9547 * @type string
9224 9548 * @default null
9225 9549 * @private
9226 9550 */
9227 "src": null
9551 "src": null,
9552
9553 /**
9554 * Index in the aoData array. This saves an indexOf lookup when we have the
9555 * object, but want to know the index
9556 * @type integer
9557 * @default -1
9558 * @private
9559 */
9560 "idx": -1
9228 9561 };
9229
9230
9562
9563
9231 9564 /**
9232 9565 * Template object for the column information object in DataTables. This object
9233 9566 * is held in the settings aoColumns array and contains all the information that
9234 9567 * DataTables needs about each individual column.
9235 9568 *
9236 9569 * Note that this object is related to {@link DataTable.defaults.column}
9237 9570 * but this one is the internal data store for DataTables's cache of columns.
9238 9571 * It should NOT be manipulated outside of DataTables. Any configuration should
9239 9572 * be done through the initialisation options.
9240 9573 * @namespace
9241 9574 */
9242 9575 DataTable.models.oColumn = {
9243 9576 /**
9244 9577 * Column index. This could be worked out on-the-fly with $.inArray, but it
9245 9578 * is faster to just hold it as a variable
9246 9579 * @type integer
9247 9580 * @default null
9248 9581 */
9249 9582 "idx": null,
9250
9583
9251 9584 /**
9252 9585 * A list of the columns that sorting should occur on when this column
9253 9586 * is sorted. That this property is an array allows multi-column sorting
9254 9587 * to be defined for a column (for example first name / last name columns
9255 9588 * would benefit from this). The values are integers pointing to the
9256 9589 * columns to be sorted on (typically it will be a single integer pointing
9257 9590 * at itself, but that doesn't need to be the case).
9258 9591 * @type array
9259 9592 */
9260 9593 "aDataSort": null,
9261
9594
9262 9595 /**
9263 9596 * Define the sorting directions that are applied to the column, in sequence
9264 9597 * as the column is repeatedly sorted upon - i.e. the first value is used
9265 9598 * as the sorting direction when the column if first sorted (clicked on).
9266 9599 * Sort it again (click again) and it will move on to the next index.
9267 9600 * Repeat until loop.
9268 9601 * @type array
9269 9602 */
9270 9603 "asSorting": null,
9271
9604
9272 9605 /**
9273 9606 * Flag to indicate if the column is searchable, and thus should be included
9274 9607 * in the filtering or not.
9275 9608 * @type boolean
9276 9609 */
9277 9610 "bSearchable": null,
9278
9611
9279 9612 /**
9280 9613 * Flag to indicate if the column is sortable or not.
9281 9614 * @type boolean
9282 9615 */
9283 9616 "bSortable": null,
9284
9617
9285 9618 /**
9286 9619 * Flag to indicate if the column is currently visible in the table or not
9287 9620 * @type boolean
9288 9621 */
9289 9622 "bVisible": null,
9290
9623
9291 9624 /**
9292 9625 * Store for manual type assignment using the `column.type` option. This
9293 9626 * is held in store so we can manipulate the column's `sType` property.
9294 9627 * @type string
9295 9628 * @default null
9296 9629 * @private
9297 9630 */
9298 9631 "_sManualType": null,
9299
9632
9300 9633 /**
9301 9634 * Flag to indicate if HTML5 data attributes should be used as the data
9302 9635 * source for filtering or sorting. True is either are.
9303 9636 * @type boolean
9304 9637 * @default false
9305 9638 * @private
9306 9639 */
9307 9640 "_bAttrSrc": false,
9308
9641
9309 9642 /**
9310 9643 * Developer definable function that is called whenever a cell is created (Ajax source,
9311 9644 * etc) or processed for input (DOM source). This can be used as a compliment to mRender
9312 9645 * allowing you to modify the DOM element (add background colour for example) when the
9313 9646 * element is available.
9314 9647 * @type function
9315 9648 * @param {element} nTd The TD node that has been created
9316 9649 * @param {*} sData The Data for the cell
9317 9650 * @param {array|object} oData The data for the whole row
9318 9651 * @param {int} iRow The row index for the aoData data store
9319 9652 * @default null
9320 9653 */
9321 9654 "fnCreatedCell": null,
9322
9655
9323 9656 /**
9324 9657 * Function to get data from a cell in a column. You should <b>never</b>
9325 9658 * access data directly through _aData internally in DataTables - always use
9326 9659 * the method attached to this property. It allows mData to function as
9327 9660 * required. This function is automatically assigned by the column
9328 9661 * initialisation method
9329 9662 * @type function
9330 9663 * @param {array|object} oData The data array/object for the array
9331 9664 * (i.e. aoData[]._aData)
9332 9665 * @param {string} sSpecific The specific data type you want to get -
9333 9666 * 'display', 'type' 'filter' 'sort'
9334 9667 * @returns {*} The data for the cell from the given row's data
9335 9668 * @default null
9336 9669 */
9337 9670 "fnGetData": null,
9338
9671
9339 9672 /**
9340 9673 * Function to set data for a cell in the column. You should <b>never</b>
9341 9674 * set the data directly to _aData internally in DataTables - always use
9342 9675 * this method. It allows mData to function as required. This function
9343 9676 * is automatically assigned by the column initialisation method
9344 9677 * @type function
9345 9678 * @param {array|object} oData The data array/object for the array
9346 9679 * (i.e. aoData[]._aData)
9347 9680 * @param {*} sValue Value to set
9348 9681 * @default null
9349 9682 */
9350 9683 "fnSetData": null,
9351
9684
9352 9685 /**
9353 9686 * Property to read the value for the cells in the column from the data
9354 9687 * source array / object. If null, then the default content is used, if a
9355 9688 * function is given then the return from the function is used.
9356 9689 * @type function|int|string|null
9357 9690 * @default null
9358 9691 */
9359 9692 "mData": null,
9360
9693
9361 9694 /**
9362 9695 * Partner property to mData which is used (only when defined) to get
9363 9696 * the data - i.e. it is basically the same as mData, but without the
9364 9697 * 'set' option, and also the data fed to it is the result from mData.
9365 9698 * This is the rendering method to match the data method of mData.
9366 9699 * @type function|int|string|null
9367 9700 * @default null
9368 9701 */
9369 9702 "mRender": null,
9370
9703
9371 9704 /**
9372 9705 * Unique header TH/TD element for this column - this is what the sorting
9373 9706 * listener is attached to (if sorting is enabled.)
9374 9707 * @type node
9375 9708 * @default null
9376 9709 */
9377 9710 "nTh": null,
9378
9711
9379 9712 /**
9380 9713 * Unique footer TH/TD element for this column (if there is one). Not used
9381 9714 * in DataTables as such, but can be used for plug-ins to reference the
9382 9715 * footer for each column.
9383 9716 * @type node
9384 9717 * @default null
9385 9718 */
9386 9719 "nTf": null,
9387
9720
9388 9721 /**
9389 9722 * The class to apply to all TD elements in the table's TBODY for the column
9390 9723 * @type string
9391 9724 * @default null
9392 9725 */
9393 9726 "sClass": null,
9394
9727
9395 9728 /**
9396 9729 * When DataTables calculates the column widths to assign to each column,
9397 9730 * it finds the longest string in each column and then constructs a
9398 9731 * temporary table and reads the widths from that. The problem with this
9399 9732 * is that "mmm" is much wider then "iiii", but the latter is a longer
9400 9733 * string - thus the calculation can go wrong (doing it properly and putting
9401 9734 * it into an DOM object and measuring that is horribly(!) slow). Thus as
9402 9735 * a "work around" we provide this option. It will append its value to the
9403 9736 * text that is found to be the longest string for the column - i.e. padding.
9404 9737 * @type string
9405 9738 */
9406 9739 "sContentPadding": null,
9407
9740
9408 9741 /**
9409 9742 * Allows a default value to be given for a column's data, and will be used
9410 9743 * whenever a null data source is encountered (this can be because mData
9411 9744 * is set to null, or because the data source itself is null).
9412 9745 * @type string
9413 9746 * @default null
9414 9747 */
9415 9748 "sDefaultContent": null,
9416
9749
9417 9750 /**
9418 9751 * Name for the column, allowing reference to the column by name as well as
9419 9752 * by index (needs a lookup to work by name).
9420 9753 * @type string
9421 9754 */
9422 9755 "sName": null,
9423
9756
9424 9757 /**
9425 9758 * Custom sorting data type - defines which of the available plug-ins in
9426 9759 * afnSortData the custom sorting will use - if any is defined.
9427 9760 * @type string
9428 9761 * @default std
9429 9762 */
9430 9763 "sSortDataType": 'std',
9431
9764
9432 9765 /**
9433 9766 * Class to be applied to the header element when sorting on this column
9434 9767 * @type string
9435 9768 * @default null
9436 9769 */
9437 9770 "sSortingClass": null,
9438
9771
9439 9772 /**
9440 9773 * Class to be applied to the header element when sorting on this column -
9441 9774 * when jQuery UI theming is used.
9442 9775 * @type string
9443 9776 * @default null
9444 9777 */
9445 9778 "sSortingClassJUI": null,
9446
9779
9447 9780 /**
9448 9781 * Title of the column - what is seen in the TH element (nTh).
9449 9782 * @type string
9450 9783 */
9451 9784 "sTitle": null,
9452
9785
9453 9786 /**
9454 9787 * Column sorting and filtering type
9455 9788 * @type string
9456 9789 * @default null
9457 9790 */
9458 9791 "sType": null,
9459
9792
9460 9793 /**
9461 9794 * Width of the column
9462 9795 * @type string
9463 9796 * @default null
9464 9797 */
9465 9798 "sWidth": null,
9466
9799
9467 9800 /**
9468 9801 * Width of the column when it was first "encountered"
9469 9802 * @type string
9470 9803 * @default null
9471 9804 */
9472 9805 "sWidthOrig": null
9473 9806 };
9474
9475
9807
9808
9476 9809 /*
9477 9810 * Developer note: The properties of the object below are given in Hungarian
9478 9811 * notation, that was used as the interface for DataTables prior to v1.10, however
9479 9812 * from v1.10 onwards the primary interface is camel case. In order to avoid
9480 9813 * breaking backwards compatibility utterly with this change, the Hungarian
9481 9814 * version is still, internally the primary interface, but is is not documented
9482 9815 * - hence the @name tags in each doc comment. This allows a Javascript function
9483 9816 * to create a map from Hungarian notation to camel case (going the other direction
9484 9817 * would require each property to be listed, which would at around 3K to the size
9485 9818 * of DataTables, while this method is about a 0.5K hit.
9486 9819 *
9487 9820 * Ultimately this does pave the way for Hungarian notation to be dropped
9488 9821 * completely, but that is a massive amount of work and will break current
9489 9822 * installs (therefore is on-hold until v2).
9490 9823 */
9491
9824
9492 9825 /**
9493 9826 * Initialisation options that can be given to DataTables at initialisation
9494 9827 * time.
9495 9828 * @namespace
9496 9829 */
9497 9830 DataTable.defaults = {
9498 9831 /**
9499 9832 * An array of data to use for the table, passed in at initialisation which
9500 9833 * will be used in preference to any data which is already in the DOM. This is
9501 9834 * particularly useful for constructing tables purely in Javascript, for
9502 9835 * example with a custom Ajax call.
9503 9836 * @type array
9504 9837 * @default null
9505 9838 *
9506 9839 * @dtopt Option
9507 9840 * @name DataTable.defaults.data
9508 9841 *
9509 9842 * @example
9510 9843 * // Using a 2D array data source
9511 9844 * $(document).ready( function () {
9512 9845 * $('#example').dataTable( {
9513 9846 * "data": [
9514 9847 * ['Trident', 'Internet Explorer 4.0', 'Win 95+', 4, 'X'],
9515 9848 * ['Trident', 'Internet Explorer 5.0', 'Win 95+', 5, 'C'],
9516 9849 * ],
9517 9850 * "columns": [
9518 9851 * { "title": "Engine" },
9519 9852 * { "title": "Browser" },
9520 9853 * { "title": "Platform" },
9521 9854 * { "title": "Version" },
9522 9855 * { "title": "Grade" }
9523 9856 * ]
9524 9857 * } );
9525 9858 * } );
9526 9859 *
9527 9860 * @example
9528 9861 * // Using an array of objects as a data source (`data`)
9529 9862 * $(document).ready( function () {
9530 9863 * $('#example').dataTable( {
9531 9864 * "data": [
9532 9865 * {
9533 9866 * "engine": "Trident",
9534 9867 * "browser": "Internet Explorer 4.0",
9535 9868 * "platform": "Win 95+",
9536 9869 * "version": 4,
9537 9870 * "grade": "X"
9538 9871 * },
9539 9872 * {
9540 9873 * "engine": "Trident",
9541 9874 * "browser": "Internet Explorer 5.0",
9542 9875 * "platform": "Win 95+",
9543 9876 * "version": 5,
9544 9877 * "grade": "C"
9545 9878 * }
9546 9879 * ],
9547 9880 * "columns": [
9548 9881 * { "title": "Engine", "data": "engine" },
9549 9882 * { "title": "Browser", "data": "browser" },
9550 9883 * { "title": "Platform", "data": "platform" },
9551 9884 * { "title": "Version", "data": "version" },
9552 9885 * { "title": "Grade", "data": "grade" }
9553 9886 * ]
9554 9887 * } );
9555 9888 * } );
9556 9889 */
9557 9890 "aaData": null,
9558
9559
9891
9892
9560 9893 /**
9561 9894 * If ordering is enabled, then DataTables will perform a first pass sort on
9562 9895 * initialisation. You can define which column(s) the sort is performed
9563 9896 * upon, and the sorting direction, with this variable. The `sorting` array
9564 9897 * should contain an array for each column to be sorted initially containing
9565 9898 * the column's index and a direction string ('asc' or 'desc').
9566 9899 * @type array
9567 9900 * @default [[0,'asc']]
9568 9901 *
9569 9902 * @dtopt Option
9570 9903 * @name DataTable.defaults.order
9571 9904 *
9572 9905 * @example
9573 9906 * // Sort by 3rd column first, and then 4th column
9574 9907 * $(document).ready( function() {
9575 9908 * $('#example').dataTable( {
9576 9909 * "order": [[2,'asc'], [3,'desc']]
9577 9910 * } );
9578 9911 * } );
9579 9912 *
9580 9913 * // No initial sorting
9581 9914 * $(document).ready( function() {
9582 9915 * $('#example').dataTable( {
9583 9916 * "order": []
9584 9917 * } );
9585 9918 * } );
9586 9919 */
9587 9920 "aaSorting": [[0,'asc']],
9588
9589
9921
9922
9590 9923 /**
9591 9924 * This parameter is basically identical to the `sorting` parameter, but
9592 9925 * cannot be overridden by user interaction with the table. What this means
9593 9926 * is that you could have a column (visible or hidden) which the sorting
9594 9927 * will always be forced on first - any sorting after that (from the user)
9595 9928 * will then be performed as required. This can be useful for grouping rows
9596 9929 * together.
9597 9930 * @type array
9598 9931 * @default null
9599 9932 *
9600 9933 * @dtopt Option
9601 9934 * @name DataTable.defaults.orderFixed
9602 9935 *
9603 9936 * @example
9604 9937 * $(document).ready( function() {
9605 9938 * $('#example').dataTable( {
9606 9939 * "orderFixed": [[0,'asc']]
9607 9940 * } );
9608 9941 * } )
9609 9942 */
9610 9943 "aaSortingFixed": [],
9611
9612
9944
9945
9613 9946 /**
9614 9947 * DataTables can be instructed to load data to display in the table from a
9615 9948 * Ajax source. This option defines how that Ajax call is made and where to.
9616 9949 *
9617 9950 * The `ajax` property has three different modes of operation, depending on
9618 9951 * how it is defined. These are:
9619 9952 *
9620 9953 * * `string` - Set the URL from where the data should be loaded from.
9621 9954 * * `object` - Define properties for `jQuery.ajax`.
9622 9955 * * `function` - Custom data get function
9623 9956 *
9624 9957 * `string`
9625 9958 * --------
9626 9959 *
9627 9960 * As a string, the `ajax` property simply defines the URL from which
9628 9961 * DataTables will load data.
9629 9962 *
9630 9963 * `object`
9631 9964 * --------
9632 9965 *
9633 9966 * As an object, the parameters in the object are passed to
9634 9967 * [jQuery.ajax](http://api.jquery.com/jQuery.ajax/) allowing fine control
9635 9968 * of the Ajax request. DataTables has a number of default parameters which
9636 9969 * you can override using this option. Please refer to the jQuery
9637 9970 * documentation for a full description of the options available, although
9638 9971 * the following parameters provide additional options in DataTables or
9639 9972 * require special consideration:
9640 9973 *
9641 9974 * * `data` - As with jQuery, `data` can be provided as an object, but it
9642 9975 * can also be used as a function to manipulate the data DataTables sends
9643 9976 * to the server. The function takes a single parameter, an object of
9644 9977 * parameters with the values that DataTables has readied for sending. An
9645 9978 * object may be returned which will be merged into the DataTables
9646 9979 * defaults, or you can add the items to the object that was passed in and
9647 9980 * not return anything from the function. This supersedes `fnServerParams`
9648 9981 * from DataTables 1.9-.
9649 9982 *
9650 9983 * * `dataSrc` - By default DataTables will look for the property `data` (or
9651 9984 * `aaData` for compatibility with DataTables 1.9-) when obtaining data
9652 9985 * from an Ajax source or for server-side processing - this parameter
9653 9986 * allows that property to be changed. You can use Javascript dotted
9654 9987 * object notation to get a data source for multiple levels of nesting, or
9655 9988 * it my be used as a function. As a function it takes a single parameter,
9656 9989 * the JSON returned from the server, which can be manipulated as
9657 9990 * required, with the returned value being that used by DataTables as the
9658 9991 * data source for the table. This supersedes `sAjaxDataProp` from
9659 9992 * DataTables 1.9-.
9660 9993 *
9661 9994 * * `success` - Should not be overridden it is used internally in
9662 9995 * DataTables. To manipulate / transform the data returned by the server
9663 9996 * use `ajax.dataSrc`, or use `ajax` as a function (see below).
9664 9997 *
9665 9998 * `function`
9666 9999 * ----------
9667 10000 *
9668 10001 * As a function, making the Ajax call is left up to yourself allowing
9669 10002 * complete control of the Ajax request. Indeed, if desired, a method other
9670 10003 * than Ajax could be used to obtain the required data, such as Web storage
9671 10004 * or an AIR database.
9672 10005 *
9673 10006 * The function is given four parameters and no return is required. The
9674 10007 * parameters are:
9675 10008 *
9676 10009 * 1. _object_ - Data to send to the server
9677 10010 * 2. _function_ - Callback function that must be executed when the required
9678 10011 * data has been obtained. That data should be passed into the callback
9679 10012 * as the only parameter
9680 10013 * 3. _object_ - DataTables settings object for the table
9681 10014 *
9682 10015 * Note that this supersedes `fnServerData` from DataTables 1.9-.
9683 10016 *
9684 10017 * @type string|object|function
9685 10018 * @default null
9686 10019 *
9687 10020 * @dtopt Option
9688 10021 * @name DataTable.defaults.ajax
9689 10022 * @since 1.10.0
9690 10023 *
9691 10024 * @example
9692 10025 * // Get JSON data from a file via Ajax.
9693 10026 * // Note DataTables expects data in the form `{ data: [ ...data... ] }` by default).
9694 10027 * $('#example').dataTable( {
9695 10028 * "ajax": "data.json"
9696 10029 * } );
9697 10030 *
9698 10031 * @example
9699 10032 * // Get JSON data from a file via Ajax, using `dataSrc` to change
9700 10033 * // `data` to `tableData` (i.e. `{ tableData: [ ...data... ] }`)
9701 10034 * $('#example').dataTable( {
9702 10035 * "ajax": {
9703 10036 * "url": "data.json",
9704 10037 * "dataSrc": "tableData"
9705 10038 * }
9706 10039 * } );
9707 10040 *
9708 10041 * @example
9709 10042 * // Get JSON data from a file via Ajax, using `dataSrc` to read data
9710 10043 * // from a plain array rather than an array in an object
9711 10044 * $('#example').dataTable( {
9712 10045 * "ajax": {
9713 10046 * "url": "data.json",
9714 10047 * "dataSrc": ""
9715 10048 * }
9716 10049 * } );
9717 10050 *
9718 10051 * @example
9719 10052 * // Manipulate the data returned from the server - add a link to data
9720 10053 * // (note this can, should, be done using `render` for the column - this
9721 10054 * // is just a simple example of how the data can be manipulated).
9722 10055 * $('#example').dataTable( {
9723 10056 * "ajax": {
9724 10057 * "url": "data.json",
9725 10058 * "dataSrc": function ( json ) {
9726 10059 * for ( var i=0, ien=json.length ; i<ien ; i++ ) {
9727 10060 * json[i][0] = '<a href="/message/'+json[i][0]+'>View message</a>';
9728 10061 * }
9729 10062 * return json;
9730 10063 * }
9731 10064 * }
9732 10065 * } );
9733 10066 *
9734 10067 * @example
9735 10068 * // Add data to the request
9736 10069 * $('#example').dataTable( {
9737 10070 * "ajax": {
9738 10071 * "url": "data.json",
9739 10072 * "data": function ( d ) {
9740 10073 * return {
9741 10074 * "extra_search": $('#extra').val()
9742 10075 * };
9743 10076 * }
9744 10077 * }
9745 10078 * } );
9746 10079 *
9747 10080 * @example
9748 10081 * // Send request as POST
9749 10082 * $('#example').dataTable( {
9750 10083 * "ajax": {
9751 10084 * "url": "data.json",
9752 10085 * "type": "POST"
9753 10086 * }
9754 10087 * } );
9755 10088 *
9756 10089 * @example
9757 10090 * // Get the data from localStorage (could interface with a form for
9758 10091 * // adding, editing and removing rows).
9759 10092 * $('#example').dataTable( {
9760 10093 * "ajax": function (data, callback, settings) {
9761 10094 * callback(
9762 10095 * JSON.parse( localStorage.getItem('dataTablesData') )
9763 10096 * );
9764 10097 * }
9765 10098 * } );
9766 10099 */
9767 10100 "ajax": null,
9768
9769
10101
10102
9770 10103 /**
9771 10104 * This parameter allows you to readily specify the entries in the length drop
9772 10105 * down menu that DataTables shows when pagination is enabled. It can be
9773 10106 * either a 1D array of options which will be used for both the displayed
9774 10107 * option and the value, or a 2D array which will use the array in the first
9775 10108 * position as the value, and the array in the second position as the
9776 10109 * displayed options (useful for language strings such as 'All').
9777 10110 *
9778 10111 * Note that the `pageLength` property will be automatically set to the
9779 10112 * first value given in this array, unless `pageLength` is also provided.
9780 10113 * @type array
9781 10114 * @default [ 10, 25, 50, 100 ]
9782 10115 *
9783 10116 * @dtopt Option
9784 10117 * @name DataTable.defaults.lengthMenu
9785 10118 *
9786 10119 * @example
9787 10120 * $(document).ready( function() {
9788 10121 * $('#example').dataTable( {
9789 10122 * "lengthMenu": [[10, 25, 50, -1], [10, 25, 50, "All"]]
9790 10123 * } );
9791 10124 * } );
9792 10125 */
9793 10126 "aLengthMenu": [ 10, 25, 50, 100 ],
9794
9795
10127
10128
9796 10129 /**
9797 10130 * The `columns` option in the initialisation parameter allows you to define
9798 10131 * details about the way individual columns behave. For a full list of
9799 10132 * column options that can be set, please see
9800 10133 * {@link DataTable.defaults.column}. Note that if you use `columns` to
9801 10134 * define your columns, you must have an entry in the array for every single
9802 10135 * column that you have in your table (these can be null if you don't which
9803 10136 * to specify any options).
9804 10137 * @member
9805 10138 *
9806 10139 * @name DataTable.defaults.column
9807 10140 */
9808 10141 "aoColumns": null,
9809
10142
9810 10143 /**
9811 10144 * Very similar to `columns`, `columnDefs` allows you to target a specific
9812 10145 * column, multiple columns, or all columns, using the `targets` property of
9813 10146 * each object in the array. This allows great flexibility when creating
9814 10147 * tables, as the `columnDefs` arrays can be of any length, targeting the
9815 10148 * columns you specifically want. `columnDefs` may use any of the column
9816 10149 * options available: {@link DataTable.defaults.column}, but it _must_
9817 10150 * have `targets` defined in each object in the array. Values in the `targets`
9818 10151 * array may be:
9819 10152 * <ul>
9820 10153 * <li>a string - class name will be matched on the TH for the column</li>
9821 10154 * <li>0 or a positive integer - column index counting from the left</li>
9822 10155 * <li>a negative integer - column index counting from the right</li>
9823 10156 * <li>the string "_all" - all columns (i.e. assign a default)</li>
9824 10157 * </ul>
9825 10158 * @member
9826 10159 *
9827 10160 * @name DataTable.defaults.columnDefs
9828 10161 */
9829 10162 "aoColumnDefs": null,
9830
9831
10163
10164
9832 10165 /**
9833 10166 * Basically the same as `search`, this parameter defines the individual column
9834 10167 * filtering state at initialisation time. The array must be of the same size
9835 10168 * as the number of columns, and each element be an object with the parameters
9836 10169 * `search` and `escapeRegex` (the latter is optional). 'null' is also
9837 10170 * accepted and the default will be used.
9838 10171 * @type array
9839 10172 * @default []
9840 10173 *
9841 10174 * @dtopt Option
9842 10175 * @name DataTable.defaults.searchCols
9843 10176 *
9844 10177 * @example
9845 10178 * $(document).ready( function() {
9846 10179 * $('#example').dataTable( {
9847 10180 * "searchCols": [
9848 10181 * null,
9849 10182 * { "search": "My filter" },
9850 10183 * null,
9851 10184 * { "search": "^[0-9]", "escapeRegex": false }
9852 10185 * ]
9853 10186 * } );
9854 10187 * } )
9855 10188 */
9856 10189 "aoSearchCols": [],
9857
9858
10190
10191
9859 10192 /**
9860 10193 * An array of CSS classes that should be applied to displayed rows. This
9861 10194 * array may be of any length, and DataTables will apply each class
9862 10195 * sequentially, looping when required.
9863 10196 * @type array
9864 10197 * @default null <i>Will take the values determined by the `oClasses.stripe*`
9865 10198 * options</i>
9866 10199 *
9867 10200 * @dtopt Option
9868 10201 * @name DataTable.defaults.stripeClasses
9869 10202 *
9870 10203 * @example
9871 10204 * $(document).ready( function() {
9872 10205 * $('#example').dataTable( {
9873 10206 * "stripeClasses": [ 'strip1', 'strip2', 'strip3' ]
9874 10207 * } );
9875 10208 * } )
9876 10209 */
9877 10210 "asStripeClasses": null,
9878
9879
10211
10212
9880 10213 /**
9881 10214 * Enable or disable automatic column width calculation. This can be disabled
9882 10215 * as an optimisation (it takes some time to calculate the widths) if the
9883 10216 * tables widths are passed in using `columns`.
9884 10217 * @type boolean
9885 10218 * @default true
9886 10219 *
9887 10220 * @dtopt Features
9888 10221 * @name DataTable.defaults.autoWidth
9889 10222 *
9890 10223 * @example
9891 10224 * $(document).ready( function () {
9892 10225 * $('#example').dataTable( {
9893 10226 * "autoWidth": false
9894 10227 * } );
9895 10228 * } );
9896 10229 */
9897 10230 "bAutoWidth": true,
9898
9899
10231
10232
9900 10233 /**
9901 10234 * Deferred rendering can provide DataTables with a huge speed boost when you
9902 10235 * are using an Ajax or JS data source for the table. This option, when set to
9903 10236 * true, will cause DataTables to defer the creation of the table elements for
9904 10237 * each row until they are needed for a draw - saving a significant amount of
9905 10238 * time.
9906 10239 * @type boolean
9907 10240 * @default false
9908 10241 *
9909 10242 * @dtopt Features
9910 10243 * @name DataTable.defaults.deferRender
9911 10244 *
9912 10245 * @example
9913 10246 * $(document).ready( function() {
9914 10247 * $('#example').dataTable( {
9915 10248 * "ajax": "sources/arrays.txt",
9916 10249 * "deferRender": true
9917 10250 * } );
9918 10251 * } );
9919 10252 */
9920 10253 "bDeferRender": false,
9921
9922
10254
10255
9923 10256 /**
9924 10257 * Replace a DataTable which matches the given selector and replace it with
9925 10258 * one which has the properties of the new initialisation object passed. If no
9926 10259 * table matches the selector, then the new DataTable will be constructed as
9927 10260 * per normal.
9928 10261 * @type boolean
9929 10262 * @default false
9930 10263 *
9931 10264 * @dtopt Options
9932 10265 * @name DataTable.defaults.destroy
9933 10266 *
9934 10267 * @example
9935 10268 * $(document).ready( function() {
9936 10269 * $('#example').dataTable( {
9937 10270 * "srollY": "200px",
9938 10271 * "paginate": false
9939 10272 * } );
9940 10273 *
9941 10274 * // Some time later....
9942 10275 * $('#example').dataTable( {
9943 10276 * "filter": false,
9944 10277 * "destroy": true
9945 10278 * } );
9946 10279 * } );
9947 10280 */
9948 10281 "bDestroy": false,
9949
9950
10282
10283
9951 10284 /**
9952 10285 * Enable or disable filtering of data. Filtering in DataTables is "smart" in
9953 10286 * that it allows the end user to input multiple words (space separated) and
9954 10287 * will match a row containing those words, even if not in the order that was
9955 10288 * specified (this allow matching across multiple columns). Note that if you
9956 10289 * wish to use filtering in DataTables this must remain 'true' - to remove the
9957 10290 * default filtering input box and retain filtering abilities, please use
9958 10291 * {@link DataTable.defaults.dom}.
9959 10292 * @type boolean
9960 10293 * @default true
9961 10294 *
9962 10295 * @dtopt Features
9963 10296 * @name DataTable.defaults.searching
9964 10297 *
9965 10298 * @example
9966 10299 * $(document).ready( function () {
9967 10300 * $('#example').dataTable( {
9968 10301 * "searching": false
9969 10302 * } );
9970 10303 * } );
9971 10304 */
9972 10305 "bFilter": true,
9973
9974
10306
10307
9975 10308 /**
9976 10309 * Enable or disable the table information display. This shows information
9977 10310 * about the data that is currently visible on the page, including information
9978 10311 * about filtered data if that action is being performed.
9979 10312 * @type boolean
9980 10313 * @default true
9981 10314 *
9982 10315 * @dtopt Features
9983 10316 * @name DataTable.defaults.info
9984 10317 *
9985 10318 * @example
9986 10319 * $(document).ready( function () {
9987 10320 * $('#example').dataTable( {
9988 10321 * "info": false
9989 10322 * } );
9990 10323 * } );
9991 10324 */
9992 10325 "bInfo": true,
9993
9994
10326
10327
9995 10328 /**
9996 10329 * Enable jQuery UI ThemeRoller support (required as ThemeRoller requires some
9997 10330 * slightly different and additional mark-up from what DataTables has
9998 10331 * traditionally used).
9999 10332 * @type boolean
10000 10333 * @default false
10001 10334 *
10002 10335 * @dtopt Features
10003 10336 * @name DataTable.defaults.jQueryUI
10004 10337 *
10005 10338 * @example
10006 10339 * $(document).ready( function() {
10007 10340 * $('#example').dataTable( {
10008 10341 * "jQueryUI": true
10009 10342 * } );
10010 10343 * } );
10011 10344 */
10012 10345 "bJQueryUI": false,
10013
10014
10346
10347
10015 10348 /**
10016 10349 * Allows the end user to select the size of a formatted page from a select
10017 10350 * menu (sizes are 10, 25, 50 and 100). Requires pagination (`paginate`).
10018 10351 * @type boolean
10019 10352 * @default true
10020 10353 *
10021 10354 * @dtopt Features
10022 10355 * @name DataTable.defaults.lengthChange
10023 10356 *
10024 10357 * @example
10025 10358 * $(document).ready( function () {
10026 10359 * $('#example').dataTable( {
10027 10360 * "lengthChange": false
10028 10361 * } );
10029 10362 * } );
10030 10363 */
10031 10364 "bLengthChange": true,
10032
10033
10365
10366
10034 10367 /**
10035 10368 * Enable or disable pagination.
10036 10369 * @type boolean
10037 10370 * @default true
10038 10371 *
10039 10372 * @dtopt Features
10040 10373 * @name DataTable.defaults.paging
10041 10374 *
10042 10375 * @example
10043 10376 * $(document).ready( function () {
10044 10377 * $('#example').dataTable( {
10045 10378 * "paging": false
10046 10379 * } );
10047 10380 * } );
10048 10381 */
10049 10382 "bPaginate": true,
10050
10051
10383
10384
10052 10385 /**
10053 10386 * Enable or disable the display of a 'processing' indicator when the table is
10054 10387 * being processed (e.g. a sort). This is particularly useful for tables with
10055 10388 * large amounts of data where it can take a noticeable amount of time to sort
10056 10389 * the entries.
10057 10390 * @type boolean
10058 10391 * @default false
10059 10392 *
10060 10393 * @dtopt Features
10061 10394 * @name DataTable.defaults.processing
10062 10395 *
10063 10396 * @example
10064 10397 * $(document).ready( function () {
10065 10398 * $('#example').dataTable( {
10066 10399 * "processing": true
10067 10400 * } );
10068 10401 * } );
10069 10402 */
10070 10403 "bProcessing": false,
10071
10072
10404
10405
10073 10406 /**
10074 10407 * Retrieve the DataTables object for the given selector. Note that if the
10075 10408 * table has already been initialised, this parameter will cause DataTables
10076 10409 * to simply return the object that has already been set up - it will not take
10077 10410 * account of any changes you might have made to the initialisation object
10078 10411 * passed to DataTables (setting this parameter to true is an acknowledgement
10079 10412 * that you understand this). `destroy` can be used to reinitialise a table if
10080 10413 * you need.
10081 10414 * @type boolean
10082 10415 * @default false
10083 10416 *
10084 10417 * @dtopt Options
10085 10418 * @name DataTable.defaults.retrieve
10086 10419 *
10087 10420 * @example
10088 10421 * $(document).ready( function() {
10089 10422 * initTable();
10090 10423 * tableActions();
10091 10424 * } );
10092 10425 *
10093 10426 * function initTable ()
10094 10427 * {
10095 10428 * return $('#example').dataTable( {
10096 10429 * "scrollY": "200px",
10097 10430 * "paginate": false,
10098 10431 * "retrieve": true
10099 10432 * } );
10100 10433 * }
10101 10434 *
10102 10435 * function tableActions ()
10103 10436 * {
10104 10437 * var table = initTable();
10105 10438 * // perform API operations with oTable
10106 10439 * }
10107 10440 */
10108 10441 "bRetrieve": false,
10109
10110
10442
10443
10111 10444 /**
10112 10445 * When vertical (y) scrolling is enabled, DataTables will force the height of
10113 10446 * the table's viewport to the given height at all times (useful for layout).
10114 10447 * However, this can look odd when filtering data down to a small data set,
10115 10448 * and the footer is left "floating" further down. This parameter (when
10116 10449 * enabled) will cause DataTables to collapse the table's viewport down when
10117 10450 * the result set will fit within the given Y height.
10118 10451 * @type boolean
10119 10452 * @default false
10120 10453 *
10121 10454 * @dtopt Options
10122 10455 * @name DataTable.defaults.scrollCollapse
10123 10456 *
10124 10457 * @example
10125 10458 * $(document).ready( function() {
10126 10459 * $('#example').dataTable( {
10127 10460 * "scrollY": "200",
10128 10461 * "scrollCollapse": true
10129 10462 * } );
10130 10463 * } );
10131 10464 */
10132 10465 "bScrollCollapse": false,
10133
10134
10466
10467
10135 10468 /**
10136 10469 * Configure DataTables to use server-side processing. Note that the
10137 10470 * `ajax` parameter must also be given in order to give DataTables a
10138 10471 * source to obtain the required data for each draw.
10139 10472 * @type boolean
10140 10473 * @default false
10141 10474 *
10142 10475 * @dtopt Features
10143 10476 * @dtopt Server-side
10144 10477 * @name DataTable.defaults.serverSide
10145 10478 *
10146 10479 * @example
10147 10480 * $(document).ready( function () {
10148 10481 * $('#example').dataTable( {
10149 10482 * "serverSide": true,
10150 10483 * "ajax": "xhr.php"
10151 10484 * } );
10152 10485 * } );
10153 10486 */
10154 10487 "bServerSide": false,
10155
10156
10488
10489
10157 10490 /**
10158 10491 * Enable or disable sorting of columns. Sorting of individual columns can be
10159 10492 * disabled by the `sortable` option for each column.
10160 10493 * @type boolean
10161 10494 * @default true
10162 10495 *
10163 10496 * @dtopt Features
10164 10497 * @name DataTable.defaults.ordering
10165 10498 *
10166 10499 * @example
10167 10500 * $(document).ready( function () {
10168 10501 * $('#example').dataTable( {
10169 10502 * "ordering": false
10170 10503 * } );
10171 10504 * } );
10172 10505 */
10173 10506 "bSort": true,
10174
10175
10507
10508
10176 10509 /**
10177 10510 * Enable or display DataTables' ability to sort multiple columns at the
10178 10511 * same time (activated by shift-click by the user).
10179 10512 * @type boolean
10180 10513 * @default true
10181 10514 *
10182 10515 * @dtopt Options
10183 10516 * @name DataTable.defaults.orderMulti
10184 10517 *
10185 10518 * @example
10186 10519 * // Disable multiple column sorting ability
10187 10520 * $(document).ready( function () {
10188 10521 * $('#example').dataTable( {
10189 10522 * "orderMulti": false
10190 10523 * } );
10191 10524 * } );
10192 10525 */
10193 10526 "bSortMulti": true,
10194
10195
10527
10528
10196 10529 /**
10197 10530 * Allows control over whether DataTables should use the top (true) unique
10198 10531 * cell that is found for a single column, or the bottom (false - default).
10199 10532 * This is useful when using complex headers.
10200 10533 * @type boolean
10201 10534 * @default false
10202 10535 *
10203 10536 * @dtopt Options
10204 10537 * @name DataTable.defaults.orderCellsTop
10205 10538 *
10206 10539 * @example
10207 10540 * $(document).ready( function() {
10208 10541 * $('#example').dataTable( {
10209 10542 * "orderCellsTop": true
10210 10543 * } );
10211 10544 * } );
10212 10545 */
10213 10546 "bSortCellsTop": false,
10214
10215
10547
10548
10216 10549 /**
10217 10550 * Enable or disable the addition of the classes `sorting\_1`, `sorting\_2` and
10218 10551 * `sorting\_3` to the columns which are currently being sorted on. This is
10219 10552 * presented as a feature switch as it can increase processing time (while
10220 10553 * classes are removed and added) so for large data sets you might want to
10221 10554 * turn this off.
10222 10555 * @type boolean
10223 10556 * @default true
10224 10557 *
10225 10558 * @dtopt Features
10226 10559 * @name DataTable.defaults.orderClasses
10227 10560 *
10228 10561 * @example
10229 10562 * $(document).ready( function () {
10230 10563 * $('#example').dataTable( {
10231 10564 * "orderClasses": false
10232 10565 * } );
10233 10566 * } );
10234 10567 */
10235 10568 "bSortClasses": true,
10236
10237
10569
10570
10238 10571 /**
10239 10572 * Enable or disable state saving. When enabled HTML5 `localStorage` will be
10240 10573 * used to save table display information such as pagination information,
10241 10574 * display length, filtering and sorting. As such when the end user reloads
10242 10575 * the page the display display will match what thy had previously set up.
10243 10576 *
10244 10577 * Due to the use of `localStorage` the default state saving is not supported
10245 10578 * in IE6 or 7. If state saving is required in those browsers, use
10246 10579 * `stateSaveCallback` to provide a storage solution such as cookies.
10247 10580 * @type boolean
10248 10581 * @default false
10249 10582 *
10250 10583 * @dtopt Features
10251 10584 * @name DataTable.defaults.stateSave
10252 10585 *
10253 10586 * @example
10254 10587 * $(document).ready( function () {
10255 10588 * $('#example').dataTable( {
10256 10589 * "stateSave": true
10257 10590 * } );
10258 10591 * } );
10259 10592 */
10260 10593 "bStateSave": false,
10261
10262
10594
10595
10263 10596 /**
10264 10597 * This function is called when a TR element is created (and all TD child
10265 10598 * elements have been inserted), or registered if using a DOM source, allowing
10266 10599 * manipulation of the TR element (adding classes etc).
10267 10600 * @type function
10268 10601 * @param {node} row "TR" element for the current row
10269 10602 * @param {array} data Raw data array for this row
10270 10603 * @param {int} dataIndex The index of this row in the internal aoData array
10271 10604 *
10272 10605 * @dtopt Callbacks
10273 10606 * @name DataTable.defaults.createdRow
10274 10607 *
10275 10608 * @example
10276 10609 * $(document).ready( function() {
10277 10610 * $('#example').dataTable( {
10278 10611 * "createdRow": function( row, data, dataIndex ) {
10279 10612 * // Bold the grade for all 'A' grade browsers
10280 10613 * if ( data[4] == "A" )
10281 10614 * {
10282 10615 * $('td:eq(4)', row).html( '<b>A</b>' );
10283 10616 * }
10284 10617 * }
10285 10618 * } );
10286 10619 * } );
10287 10620 */
10288 10621 "fnCreatedRow": null,
10289
10290
10622
10623
10291 10624 /**
10292 10625 * This function is called on every 'draw' event, and allows you to
10293 10626 * dynamically modify any aspect you want about the created DOM.
10294 10627 * @type function
10295 10628 * @param {object} settings DataTables settings object
10296 10629 *
10297 10630 * @dtopt Callbacks
10298 10631 * @name DataTable.defaults.drawCallback
10299 10632 *
10300 10633 * @example
10301 10634 * $(document).ready( function() {
10302 10635 * $('#example').dataTable( {
10303 10636 * "drawCallback": function( settings ) {
10304 10637 * alert( 'DataTables has redrawn the table' );
10305 10638 * }
10306 10639 * } );
10307 10640 * } );
10308 10641 */
10309 10642 "fnDrawCallback": null,
10310
10311
10643
10644
10312 10645 /**
10313 10646 * Identical to fnHeaderCallback() but for the table footer this function
10314 10647 * allows you to modify the table footer on every 'draw' event.
10315 10648 * @type function
10316 10649 * @param {node} foot "TR" element for the footer
10317 10650 * @param {array} data Full table data (as derived from the original HTML)
10318 10651 * @param {int} start Index for the current display starting point in the
10319 10652 * display array
10320 10653 * @param {int} end Index for the current display ending point in the
10321 10654 * display array
10322 10655 * @param {array int} display Index array to translate the visual position
10323 10656 * to the full data array
10324 10657 *
10325 10658 * @dtopt Callbacks
10326 10659 * @name DataTable.defaults.footerCallback
10327 10660 *
10328 10661 * @example
10329 10662 * $(document).ready( function() {
10330 10663 * $('#example').dataTable( {
10331 10664 * "footerCallback": function( tfoot, data, start, end, display ) {
10332 10665 * tfoot.getElementsByTagName('th')[0].innerHTML = "Starting index is "+start;
10333 10666 * }
10334 10667 * } );
10335 10668 * } )
10336 10669 */
10337 10670 "fnFooterCallback": null,
10338
10339
10671
10672
10340 10673 /**
10341 10674 * When rendering large numbers in the information element for the table
10342 10675 * (i.e. "Showing 1 to 10 of 57 entries") DataTables will render large numbers
10343 10676 * to have a comma separator for the 'thousands' units (e.g. 1 million is
10344 10677 * rendered as "1,000,000") to help readability for the end user. This
10345 10678 * function will override the default method DataTables uses.
10346 10679 * @type function
10347 10680 * @member
10348 10681 * @param {int} toFormat number to be formatted
10349 10682 * @returns {string} formatted string for DataTables to show the number
10350 10683 *
10351 10684 * @dtopt Callbacks
10352 10685 * @name DataTable.defaults.formatNumber
10353 10686 *
10354 10687 * @example
10355 10688 * // Format a number using a single quote for the separator (note that
10356 10689 * // this can also be done with the language.thousands option)
10357 10690 * $(document).ready( function() {
10358 10691 * $('#example').dataTable( {
10359 10692 * "formatNumber": function ( toFormat ) {
10360 10693 * return toFormat.toString().replace(
10361 10694 * /\B(?=(\d{3})+(?!\d))/g, "'"
10362 10695 * );
10363 10696 * };
10364 10697 * } );
10365 10698 * } );
10366 10699 */
10367 10700 "fnFormatNumber": function ( toFormat ) {
10368 10701 return toFormat.toString().replace(
10369 10702 /\B(?=(\d{3})+(?!\d))/g,
10370 10703 this.oLanguage.sThousands
10371 10704 );
10372 10705 },
10373
10374
10706
10707
10375 10708 /**
10376 10709 * This function is called on every 'draw' event, and allows you to
10377 10710 * dynamically modify the header row. This can be used to calculate and
10378 10711 * display useful information about the table.
10379 10712 * @type function
10380 10713 * @param {node} head "TR" element for the header
10381 10714 * @param {array} data Full table data (as derived from the original HTML)
10382 10715 * @param {int} start Index for the current display starting point in the
10383 10716 * display array
10384 10717 * @param {int} end Index for the current display ending point in the
10385 10718 * display array
10386 10719 * @param {array int} display Index array to translate the visual position
10387 10720 * to the full data array
10388 10721 *
10389 10722 * @dtopt Callbacks
10390 10723 * @name DataTable.defaults.headerCallback
10391 10724 *
10392 10725 * @example
10393 10726 * $(document).ready( function() {
10394 10727 * $('#example').dataTable( {
10395 10728 * "fheaderCallback": function( head, data, start, end, display ) {
10396 10729 * head.getElementsByTagName('th')[0].innerHTML = "Displaying "+(end-start)+" records";
10397 10730 * }
10398 10731 * } );
10399 10732 * } )
10400 10733 */
10401 10734 "fnHeaderCallback": null,
10402
10403
10735
10736
10404 10737 /**
10405 10738 * The information element can be used to convey information about the current
10406 10739 * state of the table. Although the internationalisation options presented by
10407 10740 * DataTables are quite capable of dealing with most customisations, there may
10408 10741 * be times where you wish to customise the string further. This callback
10409 10742 * allows you to do exactly that.
10410 10743 * @type function
10411 10744 * @param {object} oSettings DataTables settings object
10412 10745 * @param {int} start Starting position in data for the draw
10413 10746 * @param {int} end End position in data for the draw
10414 10747 * @param {int} max Total number of rows in the table (regardless of
10415 10748 * filtering)
10416 10749 * @param {int} total Total number of rows in the data set, after filtering
10417 10750 * @param {string} pre The string that DataTables has formatted using it's
10418 10751 * own rules
10419 10752 * @returns {string} The string to be displayed in the information element.
10420 10753 *
10421 10754 * @dtopt Callbacks
10422 10755 * @name DataTable.defaults.infoCallback
10423 10756 *
10424 10757 * @example
10425 10758 * $('#example').dataTable( {
10426 10759 * "infoCallback": function( settings, start, end, max, total, pre ) {
10427 10760 * return start +" to "+ end;
10428 10761 * }
10429 10762 * } );
10430 10763 */
10431 10764 "fnInfoCallback": null,
10432
10433
10765
10766
10434 10767 /**
10435 10768 * Called when the table has been initialised. Normally DataTables will
10436 10769 * initialise sequentially and there will be no need for this function,
10437 10770 * however, this does not hold true when using external language information
10438 10771 * since that is obtained using an async XHR call.
10439 10772 * @type function
10440 10773 * @param {object} settings DataTables settings object
10441 10774 * @param {object} json The JSON object request from the server - only
10442 10775 * present if client-side Ajax sourced data is used
10443 10776 *
10444 10777 * @dtopt Callbacks
10445 10778 * @name DataTable.defaults.initComplete
10446 10779 *
10447 10780 * @example
10448 10781 * $(document).ready( function() {
10449 10782 * $('#example').dataTable( {
10450 10783 * "initComplete": function(settings, json) {
10451 10784 * alert( 'DataTables has finished its initialisation.' );
10452 10785 * }
10453 10786 * } );
10454 10787 * } )
10455 10788 */
10456 10789 "fnInitComplete": null,
10457
10458
10790
10791
10459 10792 /**
10460 10793 * Called at the very start of each table draw and can be used to cancel the
10461 10794 * draw by returning false, any other return (including undefined) results in
10462 10795 * the full draw occurring).
10463 10796 * @type function
10464 10797 * @param {object} settings DataTables settings object
10465 10798 * @returns {boolean} False will cancel the draw, anything else (including no
10466 10799 * return) will allow it to complete.
10467 10800 *
10468 10801 * @dtopt Callbacks
10469 10802 * @name DataTable.defaults.preDrawCallback
10470 10803 *
10471 10804 * @example
10472 10805 * $(document).ready( function() {
10473 10806 * $('#example').dataTable( {
10474 10807 * "preDrawCallback": function( settings ) {
10475 10808 * if ( $('#test').val() == 1 ) {
10476 10809 * return false;
10477 10810 * }
10478 10811 * }
10479 10812 * } );
10480 10813 * } );
10481 10814 */
10482 10815 "fnPreDrawCallback": null,
10483
10484
10816
10817
10485 10818 /**
10486 10819 * This function allows you to 'post process' each row after it have been
10487 10820 * generated for each table draw, but before it is rendered on screen. This
10488 10821 * function might be used for setting the row class name etc.
10489 10822 * @type function
10490 10823 * @param {node} row "TR" element for the current row
10491 10824 * @param {array} data Raw data array for this row
10492 10825 * @param {int} displayIndex The display index for the current table draw
10493 10826 * @param {int} displayIndexFull The index of the data in the full list of
10494 10827 * rows (after filtering)
10495 10828 *
10496 10829 * @dtopt Callbacks
10497 10830 * @name DataTable.defaults.rowCallback
10498 10831 *
10499 10832 * @example
10500 10833 * $(document).ready( function() {
10501 10834 * $('#example').dataTable( {
10502 10835 * "rowCallback": function( row, data, displayIndex, displayIndexFull ) {
10503 10836 * // Bold the grade for all 'A' grade browsers
10504 10837 * if ( data[4] == "A" ) {
10505 10838 * $('td:eq(4)', row).html( '<b>A</b>' );
10506 10839 * }
10507 10840 * }
10508 10841 * } );
10509 10842 * } );
10510 10843 */
10511 10844 "fnRowCallback": null,
10512
10513
10845
10846
10514 10847 /**
10515 10848 * __Deprecated__ The functionality provided by this parameter has now been
10516 10849 * superseded by that provided through `ajax`, which should be used instead.
10517 10850 *
10518 10851 * This parameter allows you to override the default function which obtains
10519 10852 * the data from the server so something more suitable for your application.
10520 10853 * For example you could use POST data, or pull information from a Gears or
10521 10854 * AIR database.
10522 10855 * @type function
10523 10856 * @member
10524 10857 * @param {string} source HTTP source to obtain the data from (`ajax`)
10525 10858 * @param {array} data A key/value pair object containing the data to send
10526 10859 * to the server
10527 10860 * @param {function} callback to be called on completion of the data get
10528 10861 * process that will draw the data on the page.
10529 10862 * @param {object} settings DataTables settings object
10530 10863 *
10531 10864 * @dtopt Callbacks
10532 10865 * @dtopt Server-side
10533 10866 * @name DataTable.defaults.serverData
10534 10867 *
10535 10868 * @deprecated 1.10. Please use `ajax` for this functionality now.
10536 10869 */
10537 10870 "fnServerData": null,
10538
10539
10871
10872
10540 10873 /**
10541 10874 * __Deprecated__ The functionality provided by this parameter has now been
10542 10875 * superseded by that provided through `ajax`, which should be used instead.
10543 10876 *
10544 10877 * It is often useful to send extra data to the server when making an Ajax
10545 10878 * request - for example custom filtering information, and this callback
10546 10879 * function makes it trivial to send extra information to the server. The
10547 10880 * passed in parameter is the data set that has been constructed by
10548 10881 * DataTables, and you can add to this or modify it as you require.
10549 10882 * @type function
10550 10883 * @param {array} data Data array (array of objects which are name/value
10551 10884 * pairs) that has been constructed by DataTables and will be sent to the
10552 10885 * server. In the case of Ajax sourced data with server-side processing
10553 10886 * this will be an empty array, for server-side processing there will be a
10554 10887 * significant number of parameters!
10555 10888 * @returns {undefined} Ensure that you modify the data array passed in,
10556 10889 * as this is passed by reference.
10557 10890 *
10558 10891 * @dtopt Callbacks
10559 10892 * @dtopt Server-side
10560 10893 * @name DataTable.defaults.serverParams
10561 10894 *
10562 10895 * @deprecated 1.10. Please use `ajax` for this functionality now.
10563 10896 */
10564 10897 "fnServerParams": null,
10565
10566
10898
10899
10567 10900 /**
10568 10901 * Load the table state. With this function you can define from where, and how, the
10569 10902 * state of a table is loaded. By default DataTables will load from `localStorage`
10570 10903 * but you might wish to use a server-side database or cookies.
10571 10904 * @type function
10572 10905 * @member
10573 10906 * @param {object} settings DataTables settings object
10907 * @param {object} callback Callback that can be executed when done. It
10908 * should be passed the loaded state object.
10574 10909 * @return {object} The DataTables state object to be loaded
10575 10910 *
10576 10911 * @dtopt Callbacks
10577 10912 * @name DataTable.defaults.stateLoadCallback
10578 10913 *
10579 10914 * @example
10580 10915 * $(document).ready( function() {
10581 10916 * $('#example').dataTable( {
10582 10917 * "stateSave": true,
10583 * "stateLoadCallback": function (settings) {
10584 * var o;
10585 *
10586 * // Send an Ajax request to the server to get the data. Note that
10587 * // this is a synchronous request.
10918 * "stateLoadCallback": function (settings, callback) {
10588 10919 * $.ajax( {
10589 10920 * "url": "/state_load",
10590 * "async": false,
10591 10921 * "dataType": "json",
10592 10922 * "success": function (json) {
10593 * o = json;
10923 * callback( json );
10594 10924 * }
10595 10925 * } );
10596 *
10597 * return o;
10598 10926 * }
10599 10927 * } );
10600 10928 * } );
10601 10929 */
10602 10930 "fnStateLoadCallback": function ( settings ) {
10603 10931 try {
10604 10932 return JSON.parse(
10605 10933 (settings.iStateDuration === -1 ? sessionStorage : localStorage).getItem(
10606 10934 'DataTables_'+settings.sInstance+'_'+location.pathname
10607 10935 )
10608 10936 );
10609 10937 } catch (e) {}
10610 10938 },
10611
10612
10939
10940
10613 10941 /**
10614 10942 * Callback which allows modification of the saved state prior to loading that state.
10615 10943 * This callback is called when the table is loading state from the stored data, but
10616 10944 * prior to the settings object being modified by the saved state. Note that for
10617 10945 * plug-in authors, you should use the `stateLoadParams` event to load parameters for
10618 10946 * a plug-in.
10619 10947 * @type function
10620 10948 * @param {object} settings DataTables settings object
10621 10949 * @param {object} data The state object that is to be loaded
10622 10950 *
10623 10951 * @dtopt Callbacks
10624 10952 * @name DataTable.defaults.stateLoadParams
10625 10953 *
10626 10954 * @example
10627 10955 * // Remove a saved filter, so filtering is never loaded
10628 10956 * $(document).ready( function() {
10629 10957 * $('#example').dataTable( {
10630 10958 * "stateSave": true,
10631 10959 * "stateLoadParams": function (settings, data) {
10632 10960 * data.oSearch.sSearch = "";
10633 10961 * }
10634 10962 * } );
10635 10963 * } );
10636 10964 *
10637 10965 * @example
10638 10966 * // Disallow state loading by returning false
10639 10967 * $(document).ready( function() {
10640 10968 * $('#example').dataTable( {
10641 10969 * "stateSave": true,
10642 10970 * "stateLoadParams": function (settings, data) {
10643 10971 * return false;
10644 10972 * }
10645 10973 * } );
10646 10974 * } );
10647 10975 */
10648 10976 "fnStateLoadParams": null,
10649
10650
10977
10978
10651 10979 /**
10652 10980 * Callback that is called when the state has been loaded from the state saving method
10653 10981 * and the DataTables settings object has been modified as a result of the loaded state.
10654 10982 * @type function
10655 10983 * @param {object} settings DataTables settings object
10656 10984 * @param {object} data The state object that was loaded
10657 10985 *
10658 10986 * @dtopt Callbacks
10659 10987 * @name DataTable.defaults.stateLoaded
10660 10988 *
10661 10989 * @example
10662 10990 * // Show an alert with the filtering value that was saved
10663 10991 * $(document).ready( function() {
10664 10992 * $('#example').dataTable( {
10665 10993 * "stateSave": true,
10666 10994 * "stateLoaded": function (settings, data) {
10667 10995 * alert( 'Saved filter was: '+data.oSearch.sSearch );
10668 10996 * }
10669 10997 * } );
10670 10998 * } );
10671 10999 */
10672 11000 "fnStateLoaded": null,
10673
10674
11001
11002
10675 11003 /**
10676 11004 * Save the table state. This function allows you to define where and how the state
10677 11005 * information for the table is stored By default DataTables will use `localStorage`
10678 11006 * but you might wish to use a server-side database or cookies.
10679 11007 * @type function
10680 11008 * @member
10681 11009 * @param {object} settings DataTables settings object
10682 11010 * @param {object} data The state object to be saved
10683 11011 *
10684 11012 * @dtopt Callbacks
10685 11013 * @name DataTable.defaults.stateSaveCallback
10686 11014 *
10687 11015 * @example
10688 11016 * $(document).ready( function() {
10689 11017 * $('#example').dataTable( {
10690 11018 * "stateSave": true,
10691 11019 * "stateSaveCallback": function (settings, data) {
10692 11020 * // Send an Ajax request to the server with the state object
10693 11021 * $.ajax( {
10694 11022 * "url": "/state_save",
10695 11023 * "data": data,
10696 11024 * "dataType": "json",
10697 11025 * "method": "POST"
10698 11026 * "success": function () {}
10699 11027 * } );
10700 11028 * }
10701 11029 * } );
10702 11030 * } );
10703 11031 */
10704 11032 "fnStateSaveCallback": function ( settings, data ) {
10705 11033 try {
10706 11034 (settings.iStateDuration === -1 ? sessionStorage : localStorage).setItem(
10707 11035 'DataTables_'+settings.sInstance+'_'+location.pathname,
10708 11036 JSON.stringify( data )
10709 11037 );
10710 11038 } catch (e) {}
10711 11039 },
10712
10713
11040
11041
10714 11042 /**
10715 11043 * Callback which allows modification of the state to be saved. Called when the table
10716 11044 * has changed state a new state save is required. This method allows modification of
10717 11045 * the state saving object prior to actually doing the save, including addition or
10718 11046 * other state properties or modification. Note that for plug-in authors, you should
10719 11047 * use the `stateSaveParams` event to save parameters for a plug-in.
10720 11048 * @type function
10721 11049 * @param {object} settings DataTables settings object
10722 11050 * @param {object} data The state object to be saved
10723 11051 *
10724 11052 * @dtopt Callbacks
10725 11053 * @name DataTable.defaults.stateSaveParams
10726 11054 *
10727 11055 * @example
10728 11056 * // Remove a saved filter, so filtering is never saved
10729 11057 * $(document).ready( function() {
10730 11058 * $('#example').dataTable( {
10731 11059 * "stateSave": true,
10732 11060 * "stateSaveParams": function (settings, data) {
10733 11061 * data.oSearch.sSearch = "";
10734 11062 * }
10735 11063 * } );
10736 11064 * } );
10737 11065 */
10738 11066 "fnStateSaveParams": null,
10739
10740
11067
11068
10741 11069 /**
10742 11070 * Duration for which the saved state information is considered valid. After this period
10743 11071 * has elapsed the state will be returned to the default.
10744 11072 * Value is given in seconds.
10745 11073 * @type int
10746 11074 * @default 7200 <i>(2 hours)</i>
10747 11075 *
10748 11076 * @dtopt Options
10749 11077 * @name DataTable.defaults.stateDuration
10750 11078 *
10751 11079 * @example
10752 11080 * $(document).ready( function() {
10753 11081 * $('#example').dataTable( {
10754 11082 * "stateDuration": 60*60*24; // 1 day
10755 11083 * } );
10756 11084 * } )
10757 11085 */
10758 11086 "iStateDuration": 7200,
10759
10760
11087
11088
10761 11089 /**
10762 11090 * When enabled DataTables will not make a request to the server for the first
10763 11091 * page draw - rather it will use the data already on the page (no sorting etc
10764 11092 * will be applied to it), thus saving on an XHR at load time. `deferLoading`
10765 11093 * is used to indicate that deferred loading is required, but it is also used
10766 11094 * to tell DataTables how many records there are in the full table (allowing
10767 11095 * the information element and pagination to be displayed correctly). In the case
10768 11096 * where a filtering is applied to the table on initial load, this can be
10769 11097 * indicated by giving the parameter as an array, where the first element is
10770 11098 * the number of records available after filtering and the second element is the
10771 11099 * number of records without filtering (allowing the table information element
10772 11100 * to be shown correctly).
10773 11101 * @type int | array
10774 11102 * @default null
10775 11103 *
10776 11104 * @dtopt Options
10777 11105 * @name DataTable.defaults.deferLoading
10778 11106 *
10779 11107 * @example
10780 11108 * // 57 records available in the table, no filtering applied
10781 11109 * $(document).ready( function() {
10782 11110 * $('#example').dataTable( {
10783 11111 * "serverSide": true,
10784 11112 * "ajax": "scripts/server_processing.php",
10785 11113 * "deferLoading": 57
10786 11114 * } );
10787 11115 * } );
10788 11116 *
10789 11117 * @example
10790 11118 * // 57 records after filtering, 100 without filtering (an initial filter applied)
10791 11119 * $(document).ready( function() {
10792 11120 * $('#example').dataTable( {
10793 11121 * "serverSide": true,
10794 11122 * "ajax": "scripts/server_processing.php",
10795 11123 * "deferLoading": [ 57, 100 ],
10796 11124 * "search": {
10797 11125 * "search": "my_filter"
10798 11126 * }
10799 11127 * } );
10800 11128 * } );
10801 11129 */
10802 11130 "iDeferLoading": null,
10803
10804
11131
11132
10805 11133 /**
10806 11134 * Number of rows to display on a single page when using pagination. If
10807 11135 * feature enabled (`lengthChange`) then the end user will be able to override
10808 11136 * this to a custom setting using a pop-up menu.
10809 11137 * @type int
10810 11138 * @default 10
10811 11139 *
10812 11140 * @dtopt Options
10813 11141 * @name DataTable.defaults.pageLength
10814 11142 *
10815 11143 * @example
10816 11144 * $(document).ready( function() {
10817 11145 * $('#example').dataTable( {
10818 11146 * "pageLength": 50
10819 11147 * } );
10820 11148 * } )
10821 11149 */
10822 11150 "iDisplayLength": 10,
10823
10824
11151
11152
10825 11153 /**
10826 11154 * Define the starting point for data display when using DataTables with
10827 11155 * pagination. Note that this parameter is the number of records, rather than
10828 11156 * the page number, so if you have 10 records per page and want to start on
10829 11157 * the third page, it should be "20".
10830 11158 * @type int
10831 11159 * @default 0
10832 11160 *
10833 11161 * @dtopt Options
10834 11162 * @name DataTable.defaults.displayStart
10835 11163 *
10836 11164 * @example
10837 11165 * $(document).ready( function() {
10838 11166 * $('#example').dataTable( {
10839 11167 * "displayStart": 20
10840 11168 * } );
10841 11169 * } )
10842 11170 */
10843 11171 "iDisplayStart": 0,
10844
10845
11172
11173
10846 11174 /**
10847 11175 * By default DataTables allows keyboard navigation of the table (sorting, paging,
10848 11176 * and filtering) by adding a `tabindex` attribute to the required elements. This
10849 11177 * allows you to tab through the controls and press the enter key to activate them.
10850 11178 * The tabindex is default 0, meaning that the tab follows the flow of the document.
10851 11179 * You can overrule this using this parameter if you wish. Use a value of -1 to
10852 11180 * disable built-in keyboard navigation.
10853 11181 * @type int
10854 11182 * @default 0
10855 11183 *
10856 11184 * @dtopt Options
10857 11185 * @name DataTable.defaults.tabIndex
10858 11186 *
10859 11187 * @example
10860 11188 * $(document).ready( function() {
10861 11189 * $('#example').dataTable( {
10862 11190 * "tabIndex": 1
10863 11191 * } );
10864 11192 * } );
10865 11193 */
10866 11194 "iTabIndex": 0,
10867
10868
11195
11196
10869 11197 /**
10870 11198 * Classes that DataTables assigns to the various components and features
10871 11199 * that it adds to the HTML table. This allows classes to be configured
10872 11200 * during initialisation in addition to through the static
10873 11201 * {@link DataTable.ext.oStdClasses} object).
10874 11202 * @namespace
10875 11203 * @name DataTable.defaults.classes
10876 11204 */
10877 11205 "oClasses": {},
10878
10879
11206
11207
10880 11208 /**
10881 11209 * All strings that DataTables uses in the user interface that it creates
10882 11210 * are defined in this object, allowing you to modified them individually or
10883 11211 * completely replace them all as required.
10884 11212 * @namespace
10885 11213 * @name DataTable.defaults.language
10886 11214 */
10887 11215 "oLanguage": {
10888 11216 /**
10889 11217 * Strings that are used for WAI-ARIA labels and controls only (these are not
10890 11218 * actually visible on the page, but will be read by screenreaders, and thus
10891 11219 * must be internationalised as well).
10892 11220 * @namespace
10893 11221 * @name DataTable.defaults.language.aria
10894 11222 */
10895 11223 "oAria": {
10896 11224 /**
10897 11225 * ARIA label that is added to the table headers when the column may be
10898 11226 * sorted ascending by activing the column (click or return when focused).
10899 11227 * Note that the column header is prefixed to this string.
10900 11228 * @type string
10901 11229 * @default : activate to sort column ascending
10902 11230 *
10903 11231 * @dtopt Language
10904 11232 * @name DataTable.defaults.language.aria.sortAscending
10905 11233 *
10906 11234 * @example
10907 11235 * $(document).ready( function() {
10908 11236 * $('#example').dataTable( {
10909 11237 * "language": {
10910 11238 * "aria": {
10911 11239 * "sortAscending": " - click/return to sort ascending"
10912 11240 * }
10913 11241 * }
10914 11242 * } );
10915 11243 * } );
10916 11244 */
10917 11245 "sSortAscending": ": activate to sort column ascending",
10918
11246
10919 11247 /**
10920 11248 * ARIA label that is added to the table headers when the column may be
10921 11249 * sorted descending by activing the column (click or return when focused).
10922 11250 * Note that the column header is prefixed to this string.
10923 11251 * @type string
10924 11252 * @default : activate to sort column ascending
10925 11253 *
10926 11254 * @dtopt Language
10927 11255 * @name DataTable.defaults.language.aria.sortDescending
10928 11256 *
10929 11257 * @example
10930 11258 * $(document).ready( function() {
10931 11259 * $('#example').dataTable( {
10932 11260 * "language": {
10933 11261 * "aria": {
10934 11262 * "sortDescending": " - click/return to sort descending"
10935 11263 * }
10936 11264 * }
10937 11265 * } );
10938 11266 * } );
10939 11267 */
10940 11268 "sSortDescending": ": activate to sort column descending"
10941 11269 },
10942
11270
10943 11271 /**
10944 11272 * Pagination string used by DataTables for the built-in pagination
10945 11273 * control types.
10946 11274 * @namespace
10947 11275 * @name DataTable.defaults.language.paginate
10948 11276 */
10949 11277 "oPaginate": {
10950 11278 /**
10951 11279 * Text to use when using the 'full_numbers' type of pagination for the
10952 11280 * button to take the user to the first page.
10953 11281 * @type string
10954 11282 * @default First
10955 11283 *
10956 11284 * @dtopt Language
10957 11285 * @name DataTable.defaults.language.paginate.first
10958 11286 *
10959 11287 * @example
10960 11288 * $(document).ready( function() {
10961 11289 * $('#example').dataTable( {
10962 11290 * "language": {
10963 11291 * "paginate": {
10964 11292 * "first": "First page"
10965 11293 * }
10966 11294 * }
10967 11295 * } );
10968 11296 * } );
10969 11297 */
10970 11298 "sFirst": "First",
10971
10972
11299
11300
10973 11301 /**
10974 11302 * Text to use when using the 'full_numbers' type of pagination for the
10975 11303 * button to take the user to the last page.
10976 11304 * @type string
10977 11305 * @default Last
10978 11306 *
10979 11307 * @dtopt Language
10980 11308 * @name DataTable.defaults.language.paginate.last
10981 11309 *
10982 11310 * @example
10983 11311 * $(document).ready( function() {
10984 11312 * $('#example').dataTable( {
10985 11313 * "language": {
10986 11314 * "paginate": {
10987 11315 * "last": "Last page"
10988 11316 * }
10989 11317 * }
10990 11318 * } );
10991 11319 * } );
10992 11320 */
10993 11321 "sLast": "Last",
10994
10995
11322
11323
10996 11324 /**
10997 11325 * Text to use for the 'next' pagination button (to take the user to the
10998 11326 * next page).
10999 11327 * @type string
11000 11328 * @default Next
11001 11329 *
11002 11330 * @dtopt Language
11003 11331 * @name DataTable.defaults.language.paginate.next
11004 11332 *
11005 11333 * @example
11006 11334 * $(document).ready( function() {
11007 11335 * $('#example').dataTable( {
11008 11336 * "language": {
11009 11337 * "paginate": {
11010 11338 * "next": "Next page"
11011 11339 * }
11012 11340 * }
11013 11341 * } );
11014 11342 * } );
11015 11343 */
11016 11344 "sNext": "Next",
11017
11018
11345
11346
11019 11347 /**
11020 11348 * Text to use for the 'previous' pagination button (to take the user to
11021 11349 * the previous page).
11022 11350 * @type string
11023 11351 * @default Previous
11024 11352 *
11025 11353 * @dtopt Language
11026 11354 * @name DataTable.defaults.language.paginate.previous
11027 11355 *
11028 11356 * @example
11029 11357 * $(document).ready( function() {
11030 11358 * $('#example').dataTable( {
11031 11359 * "language": {
11032 11360 * "paginate": {
11033 11361 * "previous": "Previous page"
11034 11362 * }
11035 11363 * }
11036 11364 * } );
11037 11365 * } );
11038 11366 */
11039 11367 "sPrevious": "Previous"
11040 11368 },
11041
11369
11042 11370 /**
11043 11371 * This string is shown in preference to `zeroRecords` when the table is
11044 11372 * empty of data (regardless of filtering). Note that this is an optional
11045 11373 * parameter - if it is not given, the value of `zeroRecords` will be used
11046 11374 * instead (either the default or given value).
11047 11375 * @type string
11048 11376 * @default No data available in table
11049 11377 *
11050 11378 * @dtopt Language
11051 11379 * @name DataTable.defaults.language.emptyTable
11052 11380 *
11053 11381 * @example
11054 11382 * $(document).ready( function() {
11055 11383 * $('#example').dataTable( {
11056 11384 * "language": {
11057 11385 * "emptyTable": "No data available in table"
11058 11386 * }
11059 11387 * } );
11060 11388 * } );
11061 11389 */
11062 11390 "sEmptyTable": "No data available in table",
11063
11064
11391
11392
11065 11393 /**
11066 11394 * This string gives information to the end user about the information
11067 11395 * that is current on display on the page. The following tokens can be
11068 11396 * used in the string and will be dynamically replaced as the table
11069 11397 * display updates. This tokens can be placed anywhere in the string, or
11070 11398 * removed as needed by the language requires:
11071 11399 *
11072 11400 * * `\_START\_` - Display index of the first record on the current page
11073 11401 * * `\_END\_` - Display index of the last record on the current page
11074 11402 * * `\_TOTAL\_` - Number of records in the table after filtering
11075 11403 * * `\_MAX\_` - Number of records in the table without filtering
11076 11404 * * `\_PAGE\_` - Current page number
11077 11405 * * `\_PAGES\_` - Total number of pages of data in the table
11078 11406 *
11079 11407 * @type string
11080 11408 * @default Showing _START_ to _END_ of _TOTAL_ entries
11081 11409 *
11082 11410 * @dtopt Language
11083 11411 * @name DataTable.defaults.language.info
11084 11412 *
11085 11413 * @example
11086 11414 * $(document).ready( function() {
11087 11415 * $('#example').dataTable( {
11088 11416 * "language": {
11089 11417 * "info": "Showing page _PAGE_ of _PAGES_"
11090 11418 * }
11091 11419 * } );
11092 11420 * } );
11093 11421 */
11094 11422 "sInfo": "Showing _START_ to _END_ of _TOTAL_ entries",
11095
11096
11423
11424
11097 11425 /**
11098 11426 * Display information string for when the table is empty. Typically the
11099 11427 * format of this string should match `info`.
11100 11428 * @type string
11101 11429 * @default Showing 0 to 0 of 0 entries
11102 11430 *
11103 11431 * @dtopt Language
11104 11432 * @name DataTable.defaults.language.infoEmpty
11105 11433 *
11106 11434 * @example
11107 11435 * $(document).ready( function() {
11108 11436 * $('#example').dataTable( {
11109 11437 * "language": {
11110 11438 * "infoEmpty": "No entries to show"
11111 11439 * }
11112 11440 * } );
11113 11441 * } );
11114 11442 */
11115 11443 "sInfoEmpty": "Showing 0 to 0 of 0 entries",
11116
11117
11444
11445
11118 11446 /**
11119 11447 * When a user filters the information in a table, this string is appended
11120 11448 * to the information (`info`) to give an idea of how strong the filtering
11121 11449 * is. The variable _MAX_ is dynamically updated.
11122 11450 * @type string
11123 11451 * @default (filtered from _MAX_ total entries)
11124 11452 *
11125 11453 * @dtopt Language
11126 11454 * @name DataTable.defaults.language.infoFiltered
11127 11455 *
11128 11456 * @example
11129 11457 * $(document).ready( function() {
11130 11458 * $('#example').dataTable( {
11131 11459 * "language": {
11132 11460 * "infoFiltered": " - filtering from _MAX_ records"
11133 11461 * }
11134 11462 * } );
11135 11463 * } );
11136 11464 */
11137 11465 "sInfoFiltered": "(filtered from _MAX_ total entries)",
11138
11139
11466
11467
11140 11468 /**
11141 11469 * If can be useful to append extra information to the info string at times,
11142 11470 * and this variable does exactly that. This information will be appended to
11143 11471 * the `info` (`infoEmpty` and `infoFiltered` in whatever combination they are
11144 11472 * being used) at all times.
11145 11473 * @type string
11146 11474 * @default <i>Empty string</i>
11147 11475 *
11148 11476 * @dtopt Language
11149 11477 * @name DataTable.defaults.language.infoPostFix
11150 11478 *
11151 11479 * @example
11152 11480 * $(document).ready( function() {
11153 11481 * $('#example').dataTable( {
11154 11482 * "language": {
11155 11483 * "infoPostFix": "All records shown are derived from real information."
11156 11484 * }
11157 11485 * } );
11158 11486 * } );
11159 11487 */
11160 11488 "sInfoPostFix": "",
11161
11162
11489
11490
11163 11491 /**
11164 11492 * This decimal place operator is a little different from the other
11165 11493 * language options since DataTables doesn't output floating point
11166 11494 * numbers, so it won't ever use this for display of a number. Rather,
11167 11495 * what this parameter does is modify the sort methods of the table so
11168 11496 * that numbers which are in a format which has a character other than
11169 11497 * a period (`.`) as a decimal place will be sorted numerically.
11170 11498 *
11171 11499 * Note that numbers with different decimal places cannot be shown in
11172 11500 * the same table and still be sortable, the table must be consistent.
11173 11501 * However, multiple different tables on the page can use different
11174 11502 * decimal place characters.
11175 11503 * @type string
11176 * @default
11504 * @default
11177 11505 *
11178 11506 * @dtopt Language
11179 11507 * @name DataTable.defaults.language.decimal
11180 11508 *
11181 11509 * @example
11182 11510 * $(document).ready( function() {
11183 11511 * $('#example').dataTable( {
11184 11512 * "language": {
11185 11513 * "decimal": ","
11186 11514 * "thousands": "."
11187 11515 * }
11188 11516 * } );
11189 11517 * } );
11190 11518 */
11191 11519 "sDecimal": "",
11192
11193
11520
11521
11194 11522 /**
11195 11523 * DataTables has a build in number formatter (`formatNumber`) which is
11196 11524 * used to format large numbers that are used in the table information.
11197 11525 * By default a comma is used, but this can be trivially changed to any
11198 11526 * character you wish with this parameter.
11199 11527 * @type string
11200 11528 * @default ,
11201 11529 *
11202 11530 * @dtopt Language
11203 11531 * @name DataTable.defaults.language.thousands
11204 11532 *
11205 11533 * @example
11206 11534 * $(document).ready( function() {
11207 11535 * $('#example').dataTable( {
11208 11536 * "language": {
11209 11537 * "thousands": "'"
11210 11538 * }
11211 11539 * } );
11212 11540 * } );
11213 11541 */
11214 11542 "sThousands": ",",
11215
11216
11543
11544
11217 11545 /**
11218 11546 * Detail the action that will be taken when the drop down menu for the
11219 11547 * pagination length option is changed. The '_MENU_' variable is replaced
11220 11548 * with a default select list of 10, 25, 50 and 100, and can be replaced
11221 11549 * with a custom select box if required.
11222 11550 * @type string
11223 11551 * @default Show _MENU_ entries
11224 11552 *
11225 11553 * @dtopt Language
11226 11554 * @name DataTable.defaults.language.lengthMenu
11227 11555 *
11228 11556 * @example
11229 11557 * // Language change only
11230 11558 * $(document).ready( function() {
11231 11559 * $('#example').dataTable( {
11232 11560 * "language": {
11233 11561 * "lengthMenu": "Display _MENU_ records"
11234 11562 * }
11235 11563 * } );
11236 11564 * } );
11237 11565 *
11238 11566 * @example
11239 11567 * // Language and options change
11240 11568 * $(document).ready( function() {
11241 11569 * $('#example').dataTable( {
11242 11570 * "language": {
11243 11571 * "lengthMenu": 'Display <select>'+
11244 11572 * '<option value="10">10</option>'+
11245 11573 * '<option value="20">20</option>'+
11246 11574 * '<option value="30">30</option>'+
11247 11575 * '<option value="40">40</option>'+
11248 11576 * '<option value="50">50</option>'+
11249 11577 * '<option value="-1">All</option>'+
11250 11578 * '</select> records'
11251 11579 * }
11252 11580 * } );
11253 11581 * } );
11254 11582 */
11255 11583 "sLengthMenu": "Show _MENU_ entries",
11256
11257
11584
11585
11258 11586 /**
11259 11587 * When using Ajax sourced data and during the first draw when DataTables is
11260 11588 * gathering the data, this message is shown in an empty row in the table to
11261 11589 * indicate to the end user the the data is being loaded. Note that this
11262 11590 * parameter is not used when loading data by server-side processing, just
11263 11591 * Ajax sourced data with client-side processing.
11264 11592 * @type string
11265 11593 * @default Loading...
11266 11594 *
11267 11595 * @dtopt Language
11268 11596 * @name DataTable.defaults.language.loadingRecords
11269 11597 *
11270 11598 * @example
11271 11599 * $(document).ready( function() {
11272 11600 * $('#example').dataTable( {
11273 11601 * "language": {
11274 11602 * "loadingRecords": "Please wait - loading..."
11275 11603 * }
11276 11604 * } );
11277 11605 * } );
11278 11606 */
11279 11607 "sLoadingRecords": "Loading...",
11280
11281
11608
11609
11282 11610 /**
11283 11611 * Text which is displayed when the table is processing a user action
11284 11612 * (usually a sort command or similar).
11285 11613 * @type string
11286 11614 * @default Processing...
11287 11615 *
11288 11616 * @dtopt Language
11289 11617 * @name DataTable.defaults.language.processing
11290 11618 *
11291 11619 * @example
11292 11620 * $(document).ready( function() {
11293 11621 * $('#example').dataTable( {
11294 11622 * "language": {
11295 11623 * "processing": "DataTables is currently busy"
11296 11624 * }
11297 11625 * } );
11298 11626 * } );
11299 11627 */
11300 11628 "sProcessing": "Processing...",
11301
11302
11629
11630
11303 11631 /**
11304 11632 * Details the actions that will be taken when the user types into the
11305 11633 * filtering input text box. The variable "_INPUT_", if used in the string,
11306 11634 * is replaced with the HTML text box for the filtering input allowing
11307 11635 * control over where it appears in the string. If "_INPUT_" is not given
11308 11636 * then the input box is appended to the string automatically.
11309 11637 * @type string
11310 11638 * @default Search:
11311 11639 *
11312 11640 * @dtopt Language
11313 11641 * @name DataTable.defaults.language.search
11314 11642 *
11315 11643 * @example
11316 11644 * // Input text box will be appended at the end automatically
11317 11645 * $(document).ready( function() {
11318 11646 * $('#example').dataTable( {
11319 11647 * "language": {
11320 11648 * "search": "Filter records:"
11321 11649 * }
11322 11650 * } );
11323 11651 * } );
11324 11652 *
11325 11653 * @example
11326 11654 * // Specify where the filter should appear
11327 11655 * $(document).ready( function() {
11328 11656 * $('#example').dataTable( {
11329 11657 * "language": {
11330 11658 * "search": "Apply filter _INPUT_ to table"
11331 11659 * }
11332 11660 * } );
11333 11661 * } );
11334 11662 */
11335 11663 "sSearch": "Search:",
11336
11337
11664
11665
11338 11666 /**
11339 11667 * Assign a `placeholder` attribute to the search `input` element
11340 11668 * @type string
11341 * @default
11669 * @default
11342 11670 *
11343 11671 * @dtopt Language
11344 11672 * @name DataTable.defaults.language.searchPlaceholder
11345 11673 */
11346 11674 "sSearchPlaceholder": "",
11347
11348
11675
11676
11349 11677 /**
11350 11678 * All of the language information can be stored in a file on the
11351 11679 * server-side, which DataTables will look up if this parameter is passed.
11352 11680 * It must store the URL of the language file, which is in a JSON format,
11353 11681 * and the object has the same properties as the oLanguage object in the
11354 11682 * initialiser object (i.e. the above parameters). Please refer to one of
11355 11683 * the example language files to see how this works in action.
11356 11684 * @type string
11357 11685 * @default <i>Empty string - i.e. disabled</i>
11358 11686 *
11359 11687 * @dtopt Language
11360 11688 * @name DataTable.defaults.language.url
11361 11689 *
11362 11690 * @example
11363 11691 * $(document).ready( function() {
11364 11692 * $('#example').dataTable( {
11365 11693 * "language": {
11366 11694 * "url": "http://www.sprymedia.co.uk/dataTables/lang.txt"
11367 11695 * }
11368 11696 * } );
11369 11697 * } );
11370 11698 */
11371 11699 "sUrl": "",
11372
11373
11700
11701
11374 11702 /**
11375 11703 * Text shown inside the table records when the is no information to be
11376 11704 * displayed after filtering. `emptyTable` is shown when there is simply no
11377 11705 * information in the table at all (regardless of filtering).
11378 11706 * @type string
11379 11707 * @default No matching records found
11380 11708 *
11381 11709 * @dtopt Language
11382 11710 * @name DataTable.defaults.language.zeroRecords
11383 11711 *
11384 11712 * @example
11385 11713 * $(document).ready( function() {
11386 11714 * $('#example').dataTable( {
11387 11715 * "language": {
11388 11716 * "zeroRecords": "No records to display"
11389 11717 * }
11390 11718 * } );
11391 11719 * } );
11392 11720 */
11393 11721 "sZeroRecords": "No matching records found"
11394 11722 },
11395
11396
11723
11724
11397 11725 /**
11398 11726 * This parameter allows you to have define the global filtering state at
11399 11727 * initialisation time. As an object the `search` parameter must be
11400 11728 * defined, but all other parameters are optional. When `regex` is true,
11401 11729 * the search string will be treated as a regular expression, when false
11402 11730 * (default) it will be treated as a straight string. When `smart`
11403 11731 * DataTables will use it's smart filtering methods (to word match at
11404 11732 * any point in the data), when false this will not be done.
11405 11733 * @namespace
11406 11734 * @extends DataTable.models.oSearch
11407 11735 *
11408 11736 * @dtopt Options
11409 11737 * @name DataTable.defaults.search
11410 11738 *
11411 11739 * @example
11412 11740 * $(document).ready( function() {
11413 11741 * $('#example').dataTable( {
11414 11742 * "search": {"search": "Initial search"}
11415 11743 * } );
11416 11744 * } )
11417 11745 */
11418 11746 "oSearch": $.extend( {}, DataTable.models.oSearch ),
11419
11420
11747
11748
11421 11749 /**
11422 11750 * __Deprecated__ The functionality provided by this parameter has now been
11423 11751 * superseded by that provided through `ajax`, which should be used instead.
11424 11752 *
11425 11753 * By default DataTables will look for the property `data` (or `aaData` for
11426 11754 * compatibility with DataTables 1.9-) when obtaining data from an Ajax
11427 11755 * source or for server-side processing - this parameter allows that
11428 11756 * property to be changed. You can use Javascript dotted object notation to
11429 11757 * get a data source for multiple levels of nesting.
11430 11758 * @type string
11431 11759 * @default data
11432 11760 *
11433 11761 * @dtopt Options
11434 11762 * @dtopt Server-side
11435 11763 * @name DataTable.defaults.ajaxDataProp
11436 11764 *
11437 11765 * @deprecated 1.10. Please use `ajax` for this functionality now.
11438 11766 */
11439 11767 "sAjaxDataProp": "data",
11440
11441
11768
11769
11442 11770 /**
11443 11771 * __Deprecated__ The functionality provided by this parameter has now been
11444 11772 * superseded by that provided through `ajax`, which should be used instead.
11445 11773 *
11446 11774 * You can instruct DataTables to load data from an external
11447 11775 * source using this parameter (use aData if you want to pass data in you
11448 11776 * already have). Simply provide a url a JSON object can be obtained from.
11449 11777 * @type string
11450 11778 * @default null
11451 11779 *
11452 11780 * @dtopt Options
11453 11781 * @dtopt Server-side
11454 11782 * @name DataTable.defaults.ajaxSource
11455 11783 *
11456 11784 * @deprecated 1.10. Please use `ajax` for this functionality now.
11457 11785 */
11458 11786 "sAjaxSource": null,
11459
11460
11787
11788
11461 11789 /**
11462 11790 * This initialisation variable allows you to specify exactly where in the
11463 11791 * DOM you want DataTables to inject the various controls it adds to the page
11464 11792 * (for example you might want the pagination controls at the top of the
11465 11793 * table). DIV elements (with or without a custom class) can also be added to
11466 11794 * aid styling. The follow syntax is used:
11467 11795 * <ul>
11468 11796 * <li>The following options are allowed:
11469 11797 * <ul>
11470 11798 * <li>'l' - Length changing</li>
11471 11799 * <li>'f' - Filtering input</li>
11472 11800 * <li>'t' - The table!</li>
11473 11801 * <li>'i' - Information</li>
11474 11802 * <li>'p' - Pagination</li>
11475 11803 * <li>'r' - pRocessing</li>
11476 11804 * </ul>
11477 11805 * </li>
11478 11806 * <li>The following constants are allowed:
11479 11807 * <ul>
11480 11808 * <li>'H' - jQueryUI theme "header" classes ('fg-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix')</li>
11481 11809 * <li>'F' - jQueryUI theme "footer" classes ('fg-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix')</li>
11482 11810 * </ul>
11483 11811 * </li>
11484 11812 * <li>The following syntax is expected:
11485 11813 * <ul>
11486 11814 * <li>'&lt;' and '&gt;' - div elements</li>
11487 11815 * <li>'&lt;"class" and '&gt;' - div with a class</li>
11488 11816 * <li>'&lt;"#id" and '&gt;' - div with an ID</li>
11489 11817 * </ul>
11490 11818 * </li>
11491 11819 * <li>Examples:
11492 11820 * <ul>
11493 11821 * <li>'&lt;"wrapper"flipt&gt;'</li>
11494 11822 * <li>'&lt;lf&lt;t&gt;ip&gt;'</li>
11495 11823 * </ul>
11496 11824 * </li>
11497 11825 * </ul>
11498 11826 * @type string
11499 11827 * @default lfrtip <i>(when `jQueryUI` is false)</i> <b>or</b>
11500 11828 * <"H"lfr>t<"F"ip> <i>(when `jQueryUI` is true)</i>
11501 11829 *
11502 11830 * @dtopt Options
11503 11831 * @name DataTable.defaults.dom
11504 11832 *
11505 11833 * @example
11506 11834 * $(document).ready( function() {
11507 11835 * $('#example').dataTable( {
11508 11836 * "dom": '&lt;"top"i&gt;rt&lt;"bottom"flp&gt;&lt;"clear"&gt;'
11509 11837 * } );
11510 11838 * } );
11511 11839 */
11512 11840 "sDom": "lfrtip",
11513
11514
11841
11842
11515 11843 /**
11516 11844 * Search delay option. This will throttle full table searches that use the
11517 11845 * DataTables provided search input element (it does not effect calls to
11518 11846 * `dt-api search()`, providing a delay before the search is made.
11519 11847 * @type integer
11520 11848 * @default 0
11521 11849 *
11522 11850 * @dtopt Options
11523 11851 * @name DataTable.defaults.searchDelay
11524 11852 *
11525 11853 * @example
11526 11854 * $(document).ready( function() {
11527 11855 * $('#example').dataTable( {
11528 11856 * "searchDelay": 200
11529 11857 * } );
11530 11858 * } )
11531 11859 */
11532 11860 "searchDelay": null,
11533
11534
11535 /**
11536 * DataTables features four different built-in options for the buttons to
11861
11862
11863 /**
11864 * DataTables features six different built-in options for the buttons to
11537 11865 * display for pagination control:
11538 11866 *
11867 * * `numbers` - Page number buttons only
11539 11868 * * `simple` - 'Previous' and 'Next' buttons only
11540 11869 * * 'simple_numbers` - 'Previous' and 'Next' buttons, plus page numbers
11541 11870 * * `full` - 'First', 'Previous', 'Next' and 'Last' buttons
11542 * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus
11543 * page numbers
11544 *
11871 * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus page numbers
11872 * * `first_last_numbers` - 'First' and 'Last' buttons, plus page numbers
11873 *
11545 11874 * Further methods can be added using {@link DataTable.ext.oPagination}.
11546 11875 * @type string
11547 11876 * @default simple_numbers
11548 11877 *
11549 11878 * @dtopt Options
11550 11879 * @name DataTable.defaults.pagingType
11551 11880 *
11552 11881 * @example
11553 11882 * $(document).ready( function() {
11554 11883 * $('#example').dataTable( {
11555 11884 * "pagingType": "full_numbers"
11556 11885 * } );
11557 11886 * } )
11558 11887 */
11559 11888 "sPaginationType": "simple_numbers",
11560
11561
11889
11890
11562 11891 /**
11563 11892 * Enable horizontal scrolling. When a table is too wide to fit into a
11564 11893 * certain layout, or you have a large number of columns in the table, you
11565 11894 * can enable x-scrolling to show the table in a viewport, which can be
11566 11895 * scrolled. This property can be `true` which will allow the table to
11567 11896 * scroll horizontally when needed, or any CSS unit, or a number (in which
11568 11897 * case it will be treated as a pixel measurement). Setting as simply `true`
11569 11898 * is recommended.
11570 11899 * @type boolean|string
11571 11900 * @default <i>blank string - i.e. disabled</i>
11572 11901 *
11573 11902 * @dtopt Features
11574 11903 * @name DataTable.defaults.scrollX
11575 11904 *
11576 11905 * @example
11577 11906 * $(document).ready( function() {
11578 11907 * $('#example').dataTable( {
11579 11908 * "scrollX": true,
11580 11909 * "scrollCollapse": true
11581 11910 * } );
11582 11911 * } );
11583 11912 */
11584 11913 "sScrollX": "",
11585
11586
11914
11915
11587 11916 /**
11588 11917 * This property can be used to force a DataTable to use more width than it
11589 11918 * might otherwise do when x-scrolling is enabled. For example if you have a
11590 11919 * table which requires to be well spaced, this parameter is useful for
11591 11920 * "over-sizing" the table, and thus forcing scrolling. This property can by
11592 11921 * any CSS unit, or a number (in which case it will be treated as a pixel
11593 11922 * measurement).
11594 11923 * @type string
11595 11924 * @default <i>blank string - i.e. disabled</i>
11596 11925 *
11597 11926 * @dtopt Options
11598 11927 * @name DataTable.defaults.scrollXInner
11599 11928 *
11600 11929 * @example
11601 11930 * $(document).ready( function() {
11602 11931 * $('#example').dataTable( {
11603 11932 * "scrollX": "100%",
11604 11933 * "scrollXInner": "110%"
11605 11934 * } );
11606 11935 * } );
11607 11936 */
11608 11937 "sScrollXInner": "",
11609
11610
11938
11939
11611 11940 /**
11612 11941 * Enable vertical scrolling. Vertical scrolling will constrain the DataTable
11613 11942 * to the given height, and enable scrolling for any data which overflows the
11614 11943 * current viewport. This can be used as an alternative to paging to display
11615 11944 * a lot of data in a small area (although paging and scrolling can both be
11616 11945 * enabled at the same time). This property can be any CSS unit, or a number
11617 11946 * (in which case it will be treated as a pixel measurement).
11618 11947 * @type string
11619 11948 * @default <i>blank string - i.e. disabled</i>
11620 11949 *
11621 11950 * @dtopt Features
11622 11951 * @name DataTable.defaults.scrollY
11623 11952 *
11624 11953 * @example
11625 11954 * $(document).ready( function() {
11626 11955 * $('#example').dataTable( {
11627 11956 * "scrollY": "200px",
11628 11957 * "paginate": false
11629 11958 * } );
11630 11959 * } );
11631 11960 */
11632 11961 "sScrollY": "",
11633
11634
11962
11963
11635 11964 /**
11636 11965 * __Deprecated__ The functionality provided by this parameter has now been
11637 11966 * superseded by that provided through `ajax`, which should be used instead.
11638 11967 *
11639 11968 * Set the HTTP method that is used to make the Ajax call for server-side
11640 11969 * processing or Ajax sourced data.
11641 11970 * @type string
11642 11971 * @default GET
11643 11972 *
11644 11973 * @dtopt Options
11645 11974 * @dtopt Server-side
11646 11975 * @name DataTable.defaults.serverMethod
11647 11976 *
11648 11977 * @deprecated 1.10. Please use `ajax` for this functionality now.
11649 11978 */
11650 11979 "sServerMethod": "GET",
11651
11652
11980
11981
11653 11982 /**
11654 11983 * DataTables makes use of renderers when displaying HTML elements for
11655 11984 * a table. These renderers can be added or modified by plug-ins to
11656 11985 * generate suitable mark-up for a site. For example the Bootstrap
11657 11986 * integration plug-in for DataTables uses a paging button renderer to
11658 11987 * display pagination buttons in the mark-up required by Bootstrap.
11659 11988 *
11660 11989 * For further information about the renderers available see
11661 11990 * DataTable.ext.renderer
11662 11991 * @type string|object
11663 11992 * @default null
11664 11993 *
11665 11994 * @name DataTable.defaults.renderer
11666 11995 *
11667 11996 */
11668 "renderer": null
11997 "renderer": null,
11998
11999
12000 /**
12001 * Set the data property name that DataTables should use to get a row's id
12002 * to set as the `id` property in the node.
12003 * @type string
12004 * @default DT_RowId
12005 *
12006 * @name DataTable.defaults.rowId
12007 */
12008 "rowId": "DT_RowId"
11669 12009 };
11670
12010
11671 12011 _fnHungarianMap( DataTable.defaults );
11672
11673
11674
12012
12013
12014
11675 12015 /*
11676 12016 * Developer note - See note in model.defaults.js about the use of Hungarian
11677 12017 * notation and camel case.
11678 12018 */
11679
12019
11680 12020 /**
11681 12021 * Column options that can be given to DataTables at initialisation time.
11682 12022 * @namespace
11683 12023 */
11684 12024 DataTable.defaults.column = {
11685 12025 /**
11686 12026 * Define which column(s) an order will occur on for this column. This
11687 12027 * allows a column's ordering to take multiple columns into account when
11688 12028 * doing a sort or use the data from a different column. For example first
11689 12029 * name / last name columns make sense to do a multi-column sort over the
11690 12030 * two columns.
11691 12031 * @type array|int
11692 12032 * @default null <i>Takes the value of the column index automatically</i>
11693 12033 *
11694 12034 * @name DataTable.defaults.column.orderData
11695 12035 * @dtopt Columns
11696 12036 *
11697 12037 * @example
11698 12038 * // Using `columnDefs`
11699 12039 * $(document).ready( function() {
11700 12040 * $('#example').dataTable( {
11701 12041 * "columnDefs": [
11702 12042 * { "orderData": [ 0, 1 ], "targets": [ 0 ] },
11703 12043 * { "orderData": [ 1, 0 ], "targets": [ 1 ] },
11704 12044 * { "orderData": 2, "targets": [ 2 ] }
11705 12045 * ]
11706 12046 * } );
11707 12047 * } );
11708 12048 *
11709 12049 * @example
11710 12050 * // Using `columns`
11711 12051 * $(document).ready( function() {
11712 12052 * $('#example').dataTable( {
11713 12053 * "columns": [
11714 12054 * { "orderData": [ 0, 1 ] },
11715 12055 * { "orderData": [ 1, 0 ] },
11716 12056 * { "orderData": 2 },
11717 12057 * null,
11718 12058 * null
11719 12059 * ]
11720 12060 * } );
11721 12061 * } );
11722 12062 */
11723 12063 "aDataSort": null,
11724 12064 "iDataSort": -1,
11725
11726
12065
12066
11727 12067 /**
11728 12068 * You can control the default ordering direction, and even alter the
11729 12069 * behaviour of the sort handler (i.e. only allow ascending ordering etc)
11730 12070 * using this parameter.
11731 12071 * @type array
11732 12072 * @default [ 'asc', 'desc' ]
11733 12073 *
11734 12074 * @name DataTable.defaults.column.orderSequence
11735 12075 * @dtopt Columns
11736 12076 *
11737 12077 * @example
11738 12078 * // Using `columnDefs`
11739 12079 * $(document).ready( function() {
11740 12080 * $('#example').dataTable( {
11741 12081 * "columnDefs": [
11742 12082 * { "orderSequence": [ "asc" ], "targets": [ 1 ] },
11743 12083 * { "orderSequence": [ "desc", "asc", "asc" ], "targets": [ 2 ] },
11744 12084 * { "orderSequence": [ "desc" ], "targets": [ 3 ] }
11745 12085 * ]
11746 12086 * } );
11747 12087 * } );
11748 12088 *
11749 12089 * @example
11750 12090 * // Using `columns`
11751 12091 * $(document).ready( function() {
11752 12092 * $('#example').dataTable( {
11753 12093 * "columns": [
11754 12094 * null,
11755 12095 * { "orderSequence": [ "asc" ] },
11756 12096 * { "orderSequence": [ "desc", "asc", "asc" ] },
11757 12097 * { "orderSequence": [ "desc" ] },
11758 12098 * null
11759 12099 * ]
11760 12100 * } );
11761 12101 * } );
11762 12102 */
11763 12103 "asSorting": [ 'asc', 'desc' ],
11764
11765
12104
12105
11766 12106 /**
11767 12107 * Enable or disable filtering on the data in this column.
11768 12108 * @type boolean
11769 12109 * @default true
11770 12110 *
11771 12111 * @name DataTable.defaults.column.searchable
11772 12112 * @dtopt Columns
11773 12113 *
11774 12114 * @example
11775 12115 * // Using `columnDefs`
11776 12116 * $(document).ready( function() {
11777 12117 * $('#example').dataTable( {
11778 12118 * "columnDefs": [
11779 12119 * { "searchable": false, "targets": [ 0 ] }
11780 12120 * ] } );
11781 12121 * } );
11782 12122 *
11783 12123 * @example
11784 12124 * // Using `columns`
11785 12125 * $(document).ready( function() {
11786 12126 * $('#example').dataTable( {
11787 12127 * "columns": [
11788 12128 * { "searchable": false },
11789 12129 * null,
11790 12130 * null,
11791 12131 * null,
11792 12132 * null
11793 12133 * ] } );
11794 12134 * } );
11795 12135 */
11796 12136 "bSearchable": true,
11797
11798
12137
12138
11799 12139 /**
11800 12140 * Enable or disable ordering on this column.
11801 12141 * @type boolean
11802 12142 * @default true
11803 12143 *
11804 12144 * @name DataTable.defaults.column.orderable
11805 12145 * @dtopt Columns
11806 12146 *
11807 12147 * @example
11808 12148 * // Using `columnDefs`
11809 12149 * $(document).ready( function() {
11810 12150 * $('#example').dataTable( {
11811 12151 * "columnDefs": [
11812 12152 * { "orderable": false, "targets": [ 0 ] }
11813 12153 * ] } );
11814 12154 * } );
11815 12155 *
11816 12156 * @example
11817 12157 * // Using `columns`
11818 12158 * $(document).ready( function() {
11819 12159 * $('#example').dataTable( {
11820 12160 * "columns": [
11821 12161 * { "orderable": false },
11822 12162 * null,
11823 12163 * null,
11824 12164 * null,
11825 12165 * null
11826 12166 * ] } );
11827 12167 * } );
11828 12168 */
11829 12169 "bSortable": true,
11830
11831
12170
12171
11832 12172 /**
11833 12173 * Enable or disable the display of this column.
11834 12174 * @type boolean
11835 12175 * @default true
11836 12176 *
11837 12177 * @name DataTable.defaults.column.visible
11838 12178 * @dtopt Columns
11839 12179 *
11840 12180 * @example
11841 12181 * // Using `columnDefs`
11842 12182 * $(document).ready( function() {
11843 12183 * $('#example').dataTable( {
11844 12184 * "columnDefs": [
11845 12185 * { "visible": false, "targets": [ 0 ] }
11846 12186 * ] } );
11847 12187 * } );
11848 12188 *
11849 12189 * @example
11850 12190 * // Using `columns`
11851 12191 * $(document).ready( function() {
11852 12192 * $('#example').dataTable( {
11853 12193 * "columns": [
11854 12194 * { "visible": false },
11855 12195 * null,
11856 12196 * null,
11857 12197 * null,
11858 12198 * null
11859 12199 * ] } );
11860 12200 * } );
11861 12201 */
11862 12202 "bVisible": true,
11863
11864
12203
12204
11865 12205 /**
11866 12206 * Developer definable function that is called whenever a cell is created (Ajax source,
11867 12207 * etc) or processed for input (DOM source). This can be used as a compliment to mRender
11868 12208 * allowing you to modify the DOM element (add background colour for example) when the
11869 12209 * element is available.
11870 12210 * @type function
11871 12211 * @param {element} td The TD node that has been created
11872 12212 * @param {*} cellData The Data for the cell
11873 12213 * @param {array|object} rowData The data for the whole row
11874 12214 * @param {int} row The row index for the aoData data store
11875 12215 * @param {int} col The column index for aoColumns
11876 12216 *
11877 12217 * @name DataTable.defaults.column.createdCell
11878 12218 * @dtopt Columns
11879 12219 *
11880 12220 * @example
11881 12221 * $(document).ready( function() {
11882 12222 * $('#example').dataTable( {
11883 12223 * "columnDefs": [ {
11884 12224 * "targets": [3],
11885 12225 * "createdCell": function (td, cellData, rowData, row, col) {
11886 12226 * if ( cellData == "1.7" ) {
11887 12227 * $(td).css('color', 'blue')
11888 12228 * }
11889 12229 * }
11890 12230 * } ]
11891 12231 * });
11892 12232 * } );
11893 12233 */
11894 12234 "fnCreatedCell": null,
11895
11896
12235
12236
11897 12237 /**
11898 12238 * This parameter has been replaced by `data` in DataTables to ensure naming
11899 12239 * consistency. `dataProp` can still be used, as there is backwards
11900 12240 * compatibility in DataTables for this option, but it is strongly
11901 12241 * recommended that you use `data` in preference to `dataProp`.
11902 12242 * @name DataTable.defaults.column.dataProp
11903 12243 */
11904
11905
12244
12245
11906 12246 /**
11907 12247 * This property can be used to read data from any data source property,
11908 12248 * including deeply nested objects / properties. `data` can be given in a
11909 12249 * number of different ways which effect its behaviour:
11910 12250 *
11911 12251 * * `integer` - treated as an array index for the data source. This is the
11912 12252 * default that DataTables uses (incrementally increased for each column).
11913 12253 * * `string` - read an object property from the data source. There are
11914 12254 * three 'special' options that can be used in the string to alter how
11915 12255 * DataTables reads the data from the source object:
11916 12256 * * `.` - Dotted Javascript notation. Just as you use a `.` in
11917 12257 * Javascript to read from nested objects, so to can the options
11918 12258 * specified in `data`. For example: `browser.version` or
11919 12259 * `browser.name`. If your object parameter name contains a period, use
11920 12260 * `\\` to escape it - i.e. `first\\.name`.
11921 12261 * * `[]` - Array notation. DataTables can automatically combine data
11922 12262 * from and array source, joining the data with the characters provided
11923 12263 * between the two brackets. For example: `name[, ]` would provide a
11924 12264 * comma-space separated list from the source array. If no characters
11925 12265 * are provided between the brackets, the original array source is
11926 12266 * returned.
11927 12267 * * `()` - Function notation. Adding `()` to the end of a parameter will
11928 12268 * execute a function of the name given. For example: `browser()` for a
11929 12269 * simple function on the data source, `browser.version()` for a
11930 12270 * function in a nested property or even `browser().version` to get an
11931 12271 * object property if the function called returns an object. Note that
11932 12272 * function notation is recommended for use in `render` rather than
11933 12273 * `data` as it is much simpler to use as a renderer.
11934 12274 * * `null` - use the original data source for the row rather than plucking
11935 12275 * data directly from it. This action has effects on two other
11936 12276 * initialisation options:
11937 12277 * * `defaultContent` - When null is given as the `data` option and
11938 12278 * `defaultContent` is specified for the column, the value defined by
11939 12279 * `defaultContent` will be used for the cell.
11940 12280 * * `render` - When null is used for the `data` option and the `render`
11941 12281 * option is specified for the column, the whole data source for the
11942 12282 * row is used for the renderer.
11943 12283 * * `function` - the function given will be executed whenever DataTables
11944 12284 * needs to set or get the data for a cell in the column. The function
11945 12285 * takes three parameters:
11946 12286 * * Parameters:
11947 12287 * * `{array|object}` The data source for the row
11948 12288 * * `{string}` The type call data requested - this will be 'set' when
11949 12289 * setting data or 'filter', 'display', 'type', 'sort' or undefined
11950 12290 * when gathering data. Note that when `undefined` is given for the
11951 12291 * type DataTables expects to get the raw data for the object back<
11952 12292 * * `{*}` Data to set when the second parameter is 'set'.
11953 12293 * * Return:
11954 12294 * * The return value from the function is not required when 'set' is
11955 12295 * the type of call, but otherwise the return is what will be used
11956 12296 * for the data requested.
11957 12297 *
11958 12298 * Note that `data` is a getter and setter option. If you just require
11959 12299 * formatting of data for output, you will likely want to use `render` which
11960 12300 * is simply a getter and thus simpler to use.
11961 12301 *
11962 12302 * Note that prior to DataTables 1.9.2 `data` was called `mDataProp`. The
11963 12303 * name change reflects the flexibility of this property and is consistent
11964 12304 * with the naming of mRender. If 'mDataProp' is given, then it will still
11965 12305 * be used by DataTables, as it automatically maps the old name to the new
11966 12306 * if required.
11967 12307 *
11968 12308 * @type string|int|function|null
11969 12309 * @default null <i>Use automatically calculated column index</i>
11970 12310 *
11971 12311 * @name DataTable.defaults.column.data
11972 12312 * @dtopt Columns
11973 12313 *
11974 12314 * @example
11975 12315 * // Read table data from objects
11976 12316 * // JSON structure for each row:
11977 12317 * // {
11978 12318 * // "engine": {value},
11979 12319 * // "browser": {value},
11980 12320 * // "platform": {value},
11981 12321 * // "version": {value},
11982 12322 * // "grade": {value}
11983 12323 * // }
11984 12324 * $(document).ready( function() {
11985 12325 * $('#example').dataTable( {
11986 12326 * "ajaxSource": "sources/objects.txt",
11987 12327 * "columns": [
11988 12328 * { "data": "engine" },
11989 12329 * { "data": "browser" },
11990 12330 * { "data": "platform" },
11991 12331 * { "data": "version" },
11992 12332 * { "data": "grade" }
11993 12333 * ]
11994 12334 * } );
11995 12335 * } );
11996 12336 *
11997 12337 * @example
11998 12338 * // Read information from deeply nested objects
11999 12339 * // JSON structure for each row:
12000 12340 * // {
12001 12341 * // "engine": {value},
12002 12342 * // "browser": {value},
12003 12343 * // "platform": {
12004 12344 * // "inner": {value}
12005 12345 * // },
12006 12346 * // "details": [
12007 12347 * // {value}, {value}
12008 12348 * // ]
12009 12349 * // }
12010 12350 * $(document).ready( function() {
12011 12351 * $('#example').dataTable( {
12012 12352 * "ajaxSource": "sources/deep.txt",
12013 12353 * "columns": [
12014 12354 * { "data": "engine" },
12015 12355 * { "data": "browser" },
12016 12356 * { "data": "platform.inner" },
12017 12357 * { "data": "platform.details.0" },
12018 12358 * { "data": "platform.details.1" }
12019 12359 * ]
12020 12360 * } );
12021 12361 * } );
12022 12362 *
12023 12363 * @example
12024 12364 * // Using `data` as a function to provide different information for
12025 12365 * // sorting, filtering and display. In this case, currency (price)
12026 12366 * $(document).ready( function() {
12027 12367 * $('#example').dataTable( {
12028 12368 * "columnDefs": [ {
12029 12369 * "targets": [ 0 ],
12030 12370 * "data": function ( source, type, val ) {
12031 12371 * if (type === 'set') {
12032 12372 * source.price = val;
12033 12373 * // Store the computed dislay and filter values for efficiency
12034 12374 * source.price_display = val=="" ? "" : "$"+numberFormat(val);
12035 12375 * source.price_filter = val=="" ? "" : "$"+numberFormat(val)+" "+val;
12036 12376 * return;
12037 12377 * }
12038 12378 * else if (type === 'display') {
12039 12379 * return source.price_display;
12040 12380 * }
12041 12381 * else if (type === 'filter') {
12042 12382 * return source.price_filter;
12043 12383 * }
12044 12384 * // 'sort', 'type' and undefined all just use the integer
12045 12385 * return source.price;
12046 12386 * }
12047 12387 * } ]
12048 12388 * } );
12049 12389 * } );
12050 12390 *
12051 12391 * @example
12052 12392 * // Using default content
12053 12393 * $(document).ready( function() {
12054 12394 * $('#example').dataTable( {
12055 12395 * "columnDefs": [ {
12056 12396 * "targets": [ 0 ],
12057 12397 * "data": null,
12058 12398 * "defaultContent": "Click to edit"
12059 12399 * } ]
12060 12400 * } );
12061 12401 * } );
12062 12402 *
12063 12403 * @example
12064 12404 * // Using array notation - outputting a list from an array
12065 12405 * $(document).ready( function() {
12066 12406 * $('#example').dataTable( {
12067 12407 * "columnDefs": [ {
12068 12408 * "targets": [ 0 ],
12069 12409 * "data": "name[, ]"
12070 12410 * } ]
12071 12411 * } );
12072 12412 * } );
12073 12413 *
12074 12414 */
12075 12415 "mData": null,
12076
12077
12416
12417
12078 12418 /**
12079 12419 * This property is the rendering partner to `data` and it is suggested that
12080 12420 * when you want to manipulate data for display (including filtering,
12081 12421 * sorting etc) without altering the underlying data for the table, use this
12082 12422 * property. `render` can be considered to be the the read only companion to
12083 12423 * `data` which is read / write (then as such more complex). Like `data`
12084 12424 * this option can be given in a number of different ways to effect its
12085 12425 * behaviour:
12086 12426 *
12087 12427 * * `integer` - treated as an array index for the data source. This is the
12088 12428 * default that DataTables uses (incrementally increased for each column).
12089 12429 * * `string` - read an object property from the data source. There are
12090 12430 * three 'special' options that can be used in the string to alter how
12091 12431 * DataTables reads the data from the source object:
12092 12432 * * `.` - Dotted Javascript notation. Just as you use a `.` in
12093 12433 * Javascript to read from nested objects, so to can the options
12094 12434 * specified in `data`. For example: `browser.version` or
12095 12435 * `browser.name`. If your object parameter name contains a period, use
12096 12436 * `\\` to escape it - i.e. `first\\.name`.
12097 12437 * * `[]` - Array notation. DataTables can automatically combine data
12098 12438 * from and array source, joining the data with the characters provided
12099 12439 * between the two brackets. For example: `name[, ]` would provide a
12100 12440 * comma-space separated list from the source array. If no characters
12101 12441 * are provided between the brackets, the original array source is
12102 12442 * returned.
12103 12443 * * `()` - Function notation. Adding `()` to the end of a parameter will
12104 12444 * execute a function of the name given. For example: `browser()` for a
12105 12445 * simple function on the data source, `browser.version()` for a
12106 12446 * function in a nested property or even `browser().version` to get an
12107 12447 * object property if the function called returns an object.
12108 12448 * * `object` - use different data for the different data types requested by
12109 12449 * DataTables ('filter', 'display', 'type' or 'sort'). The property names
12110 12450 * of the object is the data type the property refers to and the value can
12111 12451 * defined using an integer, string or function using the same rules as
12112 12452 * `render` normally does. Note that an `_` option _must_ be specified.
12113 12453 * This is the default value to use if you haven't specified a value for
12114 12454 * the data type requested by DataTables.
12115 12455 * * `function` - the function given will be executed whenever DataTables
12116 12456 * needs to set or get the data for a cell in the column. The function
12117 12457 * takes three parameters:
12118 12458 * * Parameters:
12119 12459 * * {array|object} The data source for the row (based on `data`)
12120 12460 * * {string} The type call data requested - this will be 'filter',
12121 12461 * 'display', 'type' or 'sort'.
12122 12462 * * {array|object} The full data source for the row (not based on
12123 12463 * `data`)
12124 12464 * * Return:
12125 12465 * * The return value from the function is what will be used for the
12126 12466 * data requested.
12127 12467 *
12128 12468 * @type string|int|function|object|null
12129 12469 * @default null Use the data source value.
12130 12470 *
12131 12471 * @name DataTable.defaults.column.render
12132 12472 * @dtopt Columns
12133 12473 *
12134 12474 * @example
12135 12475 * // Create a comma separated list from an array of objects
12136 12476 * $(document).ready( function() {
12137 12477 * $('#example').dataTable( {
12138 12478 * "ajaxSource": "sources/deep.txt",
12139 12479 * "columns": [
12140 12480 * { "data": "engine" },
12141 12481 * { "data": "browser" },
12142 12482 * {
12143 12483 * "data": "platform",
12144 12484 * "render": "[, ].name"
12145 12485 * }
12146 12486 * ]
12147 12487 * } );
12148 12488 * } );
12149 12489 *
12150 12490 * @example
12151 12491 * // Execute a function to obtain data
12152 12492 * $(document).ready( function() {
12153 12493 * $('#example').dataTable( {
12154 12494 * "columnDefs": [ {
12155 12495 * "targets": [ 0 ],
12156 12496 * "data": null, // Use the full data source object for the renderer's source
12157 12497 * "render": "browserName()"
12158 12498 * } ]
12159 12499 * } );
12160 12500 * } );
12161 12501 *
12162 12502 * @example
12163 12503 * // As an object, extracting different data for the different types
12164 12504 * // This would be used with a data source such as:
12165 12505 * // { "phone": 5552368, "phone_filter": "5552368 555-2368", "phone_display": "555-2368" }
12166 12506 * // Here the `phone` integer is used for sorting and type detection, while `phone_filter`
12167 12507 * // (which has both forms) is used for filtering for if a user inputs either format, while
12168 12508 * // the formatted phone number is the one that is shown in the table.
12169 12509 * $(document).ready( function() {
12170 12510 * $('#example').dataTable( {
12171 12511 * "columnDefs": [ {
12172 12512 * "targets": [ 0 ],
12173 12513 * "data": null, // Use the full data source object for the renderer's source
12174 12514 * "render": {
12175 12515 * "_": "phone",
12176 12516 * "filter": "phone_filter",
12177 12517 * "display": "phone_display"
12178 12518 * }
12179 12519 * } ]
12180 12520 * } );
12181 12521 * } );
12182 12522 *
12183 12523 * @example
12184 12524 * // Use as a function to create a link from the data source
12185 12525 * $(document).ready( function() {
12186 12526 * $('#example').dataTable( {
12187 12527 * "columnDefs": [ {
12188 12528 * "targets": [ 0 ],
12189 12529 * "data": "download_link",
12190 12530 * "render": function ( data, type, full ) {
12191 12531 * return '<a href="'+data+'">Download</a>';
12192 12532 * }
12193 12533 * } ]
12194 12534 * } );
12195 12535 * } );
12196 12536 */
12197 12537 "mRender": null,
12198
12199
12538
12539
12200 12540 /**
12201 12541 * Change the cell type created for the column - either TD cells or TH cells. This
12202 12542 * can be useful as TH cells have semantic meaning in the table body, allowing them
12203 12543 * to act as a header for a row (you may wish to add scope='row' to the TH elements).
12204 12544 * @type string
12205 12545 * @default td
12206 12546 *
12207 12547 * @name DataTable.defaults.column.cellType
12208 12548 * @dtopt Columns
12209 12549 *
12210 12550 * @example
12211 12551 * // Make the first column use TH cells
12212 12552 * $(document).ready( function() {
12213 12553 * $('#example').dataTable( {
12214 12554 * "columnDefs": [ {
12215 12555 * "targets": [ 0 ],
12216 12556 * "cellType": "th"
12217 12557 * } ]
12218 12558 * } );
12219 12559 * } );
12220 12560 */
12221 12561 "sCellType": "td",
12222
12223
12562
12563
12224 12564 /**
12225 12565 * Class to give to each cell in this column.
12226 12566 * @type string
12227 12567 * @default <i>Empty string</i>
12228 12568 *
12229 12569 * @name DataTable.defaults.column.class
12230 12570 * @dtopt Columns
12231 12571 *
12232 12572 * @example
12233 12573 * // Using `columnDefs`
12234 12574 * $(document).ready( function() {
12235 12575 * $('#example').dataTable( {
12236 12576 * "columnDefs": [
12237 12577 * { "class": "my_class", "targets": [ 0 ] }
12238 12578 * ]
12239 12579 * } );
12240 12580 * } );
12241 12581 *
12242 12582 * @example
12243 12583 * // Using `columns`
12244 12584 * $(document).ready( function() {
12245 12585 * $('#example').dataTable( {
12246 12586 * "columns": [
12247 12587 * { "class": "my_class" },
12248 12588 * null,
12249 12589 * null,
12250 12590 * null,
12251 12591 * null
12252 12592 * ]
12253 12593 * } );
12254 12594 * } );
12255 12595 */
12256 12596 "sClass": "",
12257
12597
12258 12598 /**
12259 12599 * When DataTables calculates the column widths to assign to each column,
12260 12600 * it finds the longest string in each column and then constructs a
12261 12601 * temporary table and reads the widths from that. The problem with this
12262 12602 * is that "mmm" is much wider then "iiii", but the latter is a longer
12263 12603 * string - thus the calculation can go wrong (doing it properly and putting
12264 12604 * it into an DOM object and measuring that is horribly(!) slow). Thus as
12265 12605 * a "work around" we provide this option. It will append its value to the
12266 12606 * text that is found to be the longest string for the column - i.e. padding.
12267 12607 * Generally you shouldn't need this!
12268 12608 * @type string
12269 12609 * @default <i>Empty string<i>
12270 12610 *
12271 12611 * @name DataTable.defaults.column.contentPadding
12272 12612 * @dtopt Columns
12273 12613 *
12274 12614 * @example
12275 12615 * // Using `columns`
12276 12616 * $(document).ready( function() {
12277 12617 * $('#example').dataTable( {
12278 12618 * "columns": [
12279 12619 * null,
12280 12620 * null,
12281 12621 * null,
12282 12622 * {
12283 12623 * "contentPadding": "mmm"
12284 12624 * }
12285 12625 * ]
12286 12626 * } );
12287 12627 * } );
12288 12628 */
12289 12629 "sContentPadding": "",
12290
12291
12630
12631
12292 12632 /**
12293 12633 * Allows a default value to be given for a column's data, and will be used
12294 12634 * whenever a null data source is encountered (this can be because `data`
12295 12635 * is set to null, or because the data source itself is null).
12296 12636 * @type string
12297 12637 * @default null
12298 12638 *
12299 12639 * @name DataTable.defaults.column.defaultContent
12300 12640 * @dtopt Columns
12301 12641 *
12302 12642 * @example
12303 12643 * // Using `columnDefs`
12304 12644 * $(document).ready( function() {
12305 12645 * $('#example').dataTable( {
12306 12646 * "columnDefs": [
12307 12647 * {
12308 12648 * "data": null,
12309 12649 * "defaultContent": "Edit",
12310 12650 * "targets": [ -1 ]
12311 12651 * }
12312 12652 * ]
12313 12653 * } );
12314 12654 * } );
12315 12655 *
12316 12656 * @example
12317 12657 * // Using `columns`
12318 12658 * $(document).ready( function() {
12319 12659 * $('#example').dataTable( {
12320 12660 * "columns": [
12321 12661 * null,
12322 12662 * null,
12323 12663 * null,
12324 12664 * {
12325 12665 * "data": null,
12326 12666 * "defaultContent": "Edit"
12327 12667 * }
12328 12668 * ]
12329 12669 * } );
12330 12670 * } );
12331 12671 */
12332 12672 "sDefaultContent": null,
12333
12334
12673
12674
12335 12675 /**
12336 12676 * This parameter is only used in DataTables' server-side processing. It can
12337 12677 * be exceptionally useful to know what columns are being displayed on the
12338 12678 * client side, and to map these to database fields. When defined, the names
12339 12679 * also allow DataTables to reorder information from the server if it comes
12340 12680 * back in an unexpected order (i.e. if you switch your columns around on the
12341 12681 * client-side, your server-side code does not also need updating).
12342 12682 * @type string
12343 12683 * @default <i>Empty string</i>
12344 12684 *
12345 12685 * @name DataTable.defaults.column.name
12346 12686 * @dtopt Columns
12347 12687 *
12348 12688 * @example
12349 12689 * // Using `columnDefs`
12350 12690 * $(document).ready( function() {
12351 12691 * $('#example').dataTable( {
12352 12692 * "columnDefs": [
12353 12693 * { "name": "engine", "targets": [ 0 ] },
12354 12694 * { "name": "browser", "targets": [ 1 ] },
12355 12695 * { "name": "platform", "targets": [ 2 ] },
12356 12696 * { "name": "version", "targets": [ 3 ] },
12357 12697 * { "name": "grade", "targets": [ 4 ] }
12358 12698 * ]
12359 12699 * } );
12360 12700 * } );
12361 12701 *
12362 12702 * @example
12363 12703 * // Using `columns`
12364 12704 * $(document).ready( function() {
12365 12705 * $('#example').dataTable( {
12366 12706 * "columns": [
12367 12707 * { "name": "engine" },
12368 12708 * { "name": "browser" },
12369 12709 * { "name": "platform" },
12370 12710 * { "name": "version" },
12371 12711 * { "name": "grade" }
12372 12712 * ]
12373 12713 * } );
12374 12714 * } );
12375 12715 */
12376 12716 "sName": "",
12377
12378
12717
12718
12379 12719 /**
12380 12720 * Defines a data source type for the ordering which can be used to read
12381 12721 * real-time information from the table (updating the internally cached
12382 12722 * version) prior to ordering. This allows ordering to occur on user
12383 12723 * editable elements such as form inputs.
12384 12724 * @type string
12385 12725 * @default std
12386 12726 *
12387 12727 * @name DataTable.defaults.column.orderDataType
12388 12728 * @dtopt Columns
12389 12729 *
12390 12730 * @example
12391 12731 * // Using `columnDefs`
12392 12732 * $(document).ready( function() {
12393 12733 * $('#example').dataTable( {
12394 12734 * "columnDefs": [
12395 12735 * { "orderDataType": "dom-text", "targets": [ 2, 3 ] },
12396 12736 * { "type": "numeric", "targets": [ 3 ] },
12397 12737 * { "orderDataType": "dom-select", "targets": [ 4 ] },
12398 12738 * { "orderDataType": "dom-checkbox", "targets": [ 5 ] }
12399 12739 * ]
12400 12740 * } );
12401 12741 * } );
12402 12742 *
12403 12743 * @example
12404 12744 * // Using `columns`
12405 12745 * $(document).ready( function() {
12406 12746 * $('#example').dataTable( {
12407 12747 * "columns": [
12408 12748 * null,
12409 12749 * null,
12410 12750 * { "orderDataType": "dom-text" },
12411 12751 * { "orderDataType": "dom-text", "type": "numeric" },
12412 12752 * { "orderDataType": "dom-select" },
12413 12753 * { "orderDataType": "dom-checkbox" }
12414 12754 * ]
12415 12755 * } );
12416 12756 * } );
12417 12757 */
12418 12758 "sSortDataType": "std",
12419
12420
12759
12760
12421 12761 /**
12422 12762 * The title of this column.
12423 12763 * @type string
12424 12764 * @default null <i>Derived from the 'TH' value for this column in the
12425 12765 * original HTML table.</i>
12426 12766 *
12427 12767 * @name DataTable.defaults.column.title
12428 12768 * @dtopt Columns
12429 12769 *
12430 12770 * @example
12431 12771 * // Using `columnDefs`
12432 12772 * $(document).ready( function() {
12433 12773 * $('#example').dataTable( {
12434 12774 * "columnDefs": [
12435 12775 * { "title": "My column title", "targets": [ 0 ] }
12436 12776 * ]
12437 12777 * } );
12438 12778 * } );
12439 12779 *
12440 12780 * @example
12441 12781 * // Using `columns`
12442 12782 * $(document).ready( function() {
12443 12783 * $('#example').dataTable( {
12444 12784 * "columns": [
12445 12785 * { "title": "My column title" },
12446 12786 * null,
12447 12787 * null,
12448 12788 * null,
12449 12789 * null
12450 12790 * ]
12451 12791 * } );
12452 12792 * } );
12453 12793 */
12454 12794 "sTitle": null,
12455
12456
12795
12796
12457 12797 /**
12458 12798 * The type allows you to specify how the data for this column will be
12459 12799 * ordered. Four types (string, numeric, date and html (which will strip
12460 12800 * HTML tags before ordering)) are currently available. Note that only date
12461 12801 * formats understood by Javascript's Date() object will be accepted as type
12462 12802 * date. For example: "Mar 26, 2008 5:03 PM". May take the values: 'string',
12463 12803 * 'numeric', 'date' or 'html' (by default). Further types can be adding
12464 12804 * through plug-ins.
12465 12805 * @type string
12466 12806 * @default null <i>Auto-detected from raw data</i>
12467 12807 *
12468 12808 * @name DataTable.defaults.column.type
12469 12809 * @dtopt Columns
12470 12810 *
12471 12811 * @example
12472 12812 * // Using `columnDefs`
12473 12813 * $(document).ready( function() {
12474 12814 * $('#example').dataTable( {
12475 12815 * "columnDefs": [
12476 12816 * { "type": "html", "targets": [ 0 ] }
12477 12817 * ]
12478 12818 * } );
12479 12819 * } );
12480 12820 *
12481 12821 * @example
12482 12822 * // Using `columns`
12483 12823 * $(document).ready( function() {
12484 12824 * $('#example').dataTable( {
12485 12825 * "columns": [
12486 12826 * { "type": "html" },
12487 12827 * null,
12488 12828 * null,
12489 12829 * null,
12490 12830 * null
12491 12831 * ]
12492 12832 * } );
12493 12833 * } );
12494 12834 */
12495 12835 "sType": null,
12496
12497
12836
12837
12498 12838 /**
12499 12839 * Defining the width of the column, this parameter may take any CSS value
12500 12840 * (3em, 20px etc). DataTables applies 'smart' widths to columns which have not
12501 12841 * been given a specific width through this interface ensuring that the table
12502 12842 * remains readable.
12503 12843 * @type string
12504 12844 * @default null <i>Automatic</i>
12505 12845 *
12506 12846 * @name DataTable.defaults.column.width
12507 12847 * @dtopt Columns
12508 12848 *
12509 12849 * @example
12510 12850 * // Using `columnDefs`
12511 12851 * $(document).ready( function() {
12512 12852 * $('#example').dataTable( {
12513 12853 * "columnDefs": [
12514 12854 * { "width": "20%", "targets": [ 0 ] }
12515 12855 * ]
12516 12856 * } );
12517 12857 * } );
12518 12858 *
12519 12859 * @example
12520 12860 * // Using `columns`
12521 12861 * $(document).ready( function() {
12522 12862 * $('#example').dataTable( {
12523 12863 * "columns": [
12524 12864 * { "width": "20%" },
12525 12865 * null,
12526 12866 * null,
12527 12867 * null,
12528 12868 * null
12529 12869 * ]
12530 12870 * } );
12531 12871 * } );
12532 12872 */
12533 12873 "sWidth": null
12534 12874 };
12535
12875
12536 12876 _fnHungarianMap( DataTable.defaults.column );
12537
12538
12539
12877
12878
12879
12540 12880 /**
12541 12881 * DataTables settings object - this holds all the information needed for a
12542 12882 * given table, including configuration, data and current application of the
12543 12883 * table options. DataTables does not have a single instance for each DataTable
12544 12884 * with the settings attached to that instance, but rather instances of the
12545 12885 * DataTable "class" are created on-the-fly as needed (typically by a
12546 12886 * $().dataTable() call) and the settings object is then applied to that
12547 12887 * instance.
12548 12888 *
12549 12889 * Note that this object is related to {@link DataTable.defaults} but this
12550 12890 * one is the internal data store for DataTables's cache of columns. It should
12551 12891 * NOT be manipulated outside of DataTables. Any configuration should be done
12552 12892 * through the initialisation options.
12553 12893 * @namespace
12554 12894 * @todo Really should attach the settings object to individual instances so we
12555 12895 * don't need to create new instances on each $().dataTable() call (if the
12556 12896 * table already exists). It would also save passing oSettings around and
12557 12897 * into every single function. However, this is a very significant
12558 12898 * architecture change for DataTables and will almost certainly break
12559 12899 * backwards compatibility with older installations. This is something that
12560 12900 * will be done in 2.0.
12561 12901 */
12562 12902 DataTable.models.oSettings = {
12563 12903 /**
12564 12904 * Primary features of DataTables and their enablement state.
12565 12905 * @namespace
12566 12906 */
12567 12907 "oFeatures": {
12568
12908
12569 12909 /**
12570 12910 * Flag to say if DataTables should automatically try to calculate the
12571 12911 * optimum table and columns widths (true) or not (false).
12572 12912 * Note that this parameter will be set by the initialisation routine. To
12573 12913 * set a default use {@link DataTable.defaults}.
12574 12914 * @type boolean
12575 12915 */
12576 12916 "bAutoWidth": null,
12577
12917
12578 12918 /**
12579 12919 * Delay the creation of TR and TD elements until they are actually
12580 12920 * needed by a driven page draw. This can give a significant speed
12581 12921 * increase for Ajax source and Javascript source data, but makes no
12582 12922 * difference at all fro DOM and server-side processing tables.
12583 12923 * Note that this parameter will be set by the initialisation routine. To
12584 12924 * set a default use {@link DataTable.defaults}.
12585 12925 * @type boolean
12586 12926 */
12587 12927 "bDeferRender": null,
12588
12928
12589 12929 /**
12590 12930 * Enable filtering on the table or not. Note that if this is disabled
12591 12931 * then there is no filtering at all on the table, including fnFilter.
12592 12932 * To just remove the filtering input use sDom and remove the 'f' option.
12593 12933 * Note that this parameter will be set by the initialisation routine. To
12594 12934 * set a default use {@link DataTable.defaults}.
12595 12935 * @type boolean
12596 12936 */
12597 12937 "bFilter": null,
12598
12938
12599 12939 /**
12600 12940 * Table information element (the 'Showing x of y records' div) enable
12601 12941 * flag.
12602 12942 * Note that this parameter will be set by the initialisation routine. To
12603 12943 * set a default use {@link DataTable.defaults}.
12604 12944 * @type boolean
12605 12945 */
12606 12946 "bInfo": null,
12607
12947
12608 12948 /**
12609 12949 * Present a user control allowing the end user to change the page size
12610 12950 * when pagination is enabled.
12611 12951 * Note that this parameter will be set by the initialisation routine. To
12612 12952 * set a default use {@link DataTable.defaults}.
12613 12953 * @type boolean
12614 12954 */
12615 12955 "bLengthChange": null,
12616
12956
12617 12957 /**
12618 12958 * Pagination enabled or not. Note that if this is disabled then length
12619 12959 * changing must also be disabled.
12620 12960 * Note that this parameter will be set by the initialisation routine. To
12621 12961 * set a default use {@link DataTable.defaults}.
12622 12962 * @type boolean
12623 12963 */
12624 12964 "bPaginate": null,
12625
12965
12626 12966 /**
12627 12967 * Processing indicator enable flag whenever DataTables is enacting a
12628 12968 * user request - typically an Ajax request for server-side processing.
12629 12969 * Note that this parameter will be set by the initialisation routine. To
12630 12970 * set a default use {@link DataTable.defaults}.
12631 12971 * @type boolean
12632 12972 */
12633 12973 "bProcessing": null,
12634
12974
12635 12975 /**
12636 12976 * Server-side processing enabled flag - when enabled DataTables will
12637 12977 * get all data from the server for every draw - there is no filtering,
12638 12978 * sorting or paging done on the client-side.
12639 12979 * Note that this parameter will be set by the initialisation routine. To
12640 12980 * set a default use {@link DataTable.defaults}.
12641 12981 * @type boolean
12642 12982 */
12643 12983 "bServerSide": null,
12644
12984
12645 12985 /**
12646 12986 * Sorting enablement flag.
12647 12987 * Note that this parameter will be set by the initialisation routine. To
12648 12988 * set a default use {@link DataTable.defaults}.
12649 12989 * @type boolean
12650 12990 */
12651 12991 "bSort": null,
12652
12992
12653 12993 /**
12654 12994 * Multi-column sorting
12655 12995 * Note that this parameter will be set by the initialisation routine. To
12656 12996 * set a default use {@link DataTable.defaults}.
12657 12997 * @type boolean
12658 12998 */
12659 12999 "bSortMulti": null,
12660
13000
12661 13001 /**
12662 13002 * Apply a class to the columns which are being sorted to provide a
12663 13003 * visual highlight or not. This can slow things down when enabled since
12664 13004 * there is a lot of DOM interaction.
12665 13005 * Note that this parameter will be set by the initialisation routine. To
12666 13006 * set a default use {@link DataTable.defaults}.
12667 13007 * @type boolean
12668 13008 */
12669 13009 "bSortClasses": null,
12670
13010
12671 13011 /**
12672 13012 * State saving enablement flag.
12673 13013 * Note that this parameter will be set by the initialisation routine. To
12674 13014 * set a default use {@link DataTable.defaults}.
12675 13015 * @type boolean
12676 13016 */
12677 13017 "bStateSave": null
12678 13018 },
12679
12680
13019
13020
12681 13021 /**
12682 13022 * Scrolling settings for a table.
12683 13023 * @namespace
12684 13024 */
12685 13025 "oScroll": {
12686 13026 /**
12687 13027 * When the table is shorter in height than sScrollY, collapse the
12688 13028 * table container down to the height of the table (when true).
12689 13029 * Note that this parameter will be set by the initialisation routine. To
12690 13030 * set a default use {@link DataTable.defaults}.
12691 13031 * @type boolean
12692 13032 */
12693 13033 "bCollapse": null,
12694
13034
12695 13035 /**
12696 13036 * Width of the scrollbar for the web-browser's platform. Calculated
12697 13037 * during table initialisation.
12698 13038 * @type int
12699 13039 * @default 0
12700 13040 */
12701 13041 "iBarWidth": 0,
12702
13042
12703 13043 /**
12704 13044 * Viewport width for horizontal scrolling. Horizontal scrolling is
12705 13045 * disabled if an empty string.
12706 13046 * Note that this parameter will be set by the initialisation routine. To
12707 13047 * set a default use {@link DataTable.defaults}.
12708 13048 * @type string
12709 13049 */
12710 13050 "sX": null,
12711
13051
12712 13052 /**
12713 13053 * Width to expand the table to when using x-scrolling. Typically you
12714 13054 * should not need to use this.
12715 13055 * Note that this parameter will be set by the initialisation routine. To
12716 13056 * set a default use {@link DataTable.defaults}.
12717 13057 * @type string
12718 13058 * @deprecated
12719 13059 */
12720 13060 "sXInner": null,
12721
13061
12722 13062 /**
12723 13063 * Viewport height for vertical scrolling. Vertical scrolling is disabled
12724 13064 * if an empty string.
12725 13065 * Note that this parameter will be set by the initialisation routine. To
12726 13066 * set a default use {@link DataTable.defaults}.
12727 13067 * @type string
12728 13068 */
12729 13069 "sY": null
12730 13070 },
12731
13071
12732 13072 /**
12733 13073 * Language information for the table.
12734 13074 * @namespace
12735 13075 * @extends DataTable.defaults.oLanguage
12736 13076 */
12737 13077 "oLanguage": {
12738 13078 /**
12739 13079 * Information callback function. See
12740 13080 * {@link DataTable.defaults.fnInfoCallback}
12741 13081 * @type function
12742 13082 * @default null
12743 13083 */
12744 13084 "fnInfoCallback": null
12745 13085 },
12746
13086
12747 13087 /**
12748 13088 * Browser support parameters
12749 13089 * @namespace
12750 13090 */
12751 13091 "oBrowser": {
12752 13092 /**
12753 13093 * Indicate if the browser incorrectly calculates width:100% inside a
12754 13094 * scrolling element (IE6/7)
12755 13095 * @type boolean
12756 13096 * @default false
12757 13097 */
12758 13098 "bScrollOversize": false,
12759
13099
12760 13100 /**
12761 13101 * Determine if the vertical scrollbar is on the right or left of the
12762 13102 * scrolling container - needed for rtl language layout, although not
12763 13103 * all browsers move the scrollbar (Safari).
12764 13104 * @type boolean
12765 13105 * @default false
12766 13106 */
12767 "bScrollbarLeft": false
13107 "bScrollbarLeft": false,
13108
13109 /**
13110 * Flag for if `getBoundingClientRect` is fully supported or not
13111 * @type boolean
13112 * @default false
13113 */
13114 "bBounding": false,
13115
13116 /**
13117 * Browser scrollbar width
13118 * @type integer
13119 * @default 0
13120 */
13121 "barWidth": 0
12768 13122 },
12769
12770
13123
13124
12771 13125 "ajax": null,
12772
12773
13126
13127
12774 13128 /**
12775 13129 * Array referencing the nodes which are used for the features. The
12776 13130 * parameters of this object match what is allowed by sDom - i.e.
12777 13131 * <ul>
12778 13132 * <li>'l' - Length changing</li>
12779 13133 * <li>'f' - Filtering input</li>
12780 13134 * <li>'t' - The table!</li>
12781 13135 * <li>'i' - Information</li>
12782 13136 * <li>'p' - Pagination</li>
12783 13137 * <li>'r' - pRocessing</li>
12784 13138 * </ul>
12785 13139 * @type array
12786 13140 * @default []
12787 13141 */
12788 13142 "aanFeatures": [],
12789
13143
12790 13144 /**
12791 13145 * Store data information - see {@link DataTable.models.oRow} for detailed
12792 13146 * information.
12793 13147 * @type array
12794 13148 * @default []
12795 13149 */
12796 13150 "aoData": [],
12797
13151
12798 13152 /**
12799 13153 * Array of indexes which are in the current display (after filtering etc)
12800 13154 * @type array
12801 13155 * @default []
12802 13156 */
12803 13157 "aiDisplay": [],
12804
13158
12805 13159 /**
12806 13160 * Array of indexes for display - no filtering
12807 13161 * @type array
12808 13162 * @default []
12809 13163 */
12810 13164 "aiDisplayMaster": [],
12811
13165
13166 /**
13167 * Map of row ids to data indexes
13168 * @type object
13169 * @default {}
13170 */
13171 "aIds": {},
13172
12812 13173 /**
12813 13174 * Store information about each column that is in use
12814 13175 * @type array
12815 13176 * @default []
12816 13177 */
12817 13178 "aoColumns": [],
12818
13179
12819 13180 /**
12820 13181 * Store information about the table's header
12821 13182 * @type array
12822 13183 * @default []
12823 13184 */
12824 13185 "aoHeader": [],
12825
13186
12826 13187 /**
12827 13188 * Store information about the table's footer
12828 13189 * @type array
12829 13190 * @default []
12830 13191 */
12831 13192 "aoFooter": [],
12832
13193
12833 13194 /**
12834 13195 * Store the applied global search information in case we want to force a
12835 13196 * research or compare the old search to a new one.
12836 13197 * Note that this parameter will be set by the initialisation routine. To
12837 13198 * set a default use {@link DataTable.defaults}.
12838 13199 * @namespace
12839 13200 * @extends DataTable.models.oSearch
12840 13201 */
12841 13202 "oPreviousSearch": {},
12842
13203
12843 13204 /**
12844 13205 * Store the applied search for each column - see
12845 13206 * {@link DataTable.models.oSearch} for the format that is used for the
12846 13207 * filtering information for each column.
12847 13208 * @type array
12848 13209 * @default []
12849 13210 */
12850 13211 "aoPreSearchCols": [],
12851
13212
12852 13213 /**
12853 13214 * Sorting that is applied to the table. Note that the inner arrays are
12854 13215 * used in the following manner:
12855 13216 * <ul>
12856 13217 * <li>Index 0 - column number</li>
12857 13218 * <li>Index 1 - current sorting direction</li>
12858 13219 * </ul>
12859 13220 * Note that this parameter will be set by the initialisation routine. To
12860 13221 * set a default use {@link DataTable.defaults}.
12861 13222 * @type array
12862 13223 * @todo These inner arrays should really be objects
12863 13224 */
12864 13225 "aaSorting": null,
12865
13226
12866 13227 /**
12867 13228 * Sorting that is always applied to the table (i.e. prefixed in front of
12868 13229 * aaSorting).
12869 13230 * Note that this parameter will be set by the initialisation routine. To
12870 13231 * set a default use {@link DataTable.defaults}.
12871 13232 * @type array
12872 13233 * @default []
12873 13234 */
12874 13235 "aaSortingFixed": [],
12875
13236
12876 13237 /**
12877 13238 * Classes to use for the striping of a table.
12878 13239 * Note that this parameter will be set by the initialisation routine. To
12879 13240 * set a default use {@link DataTable.defaults}.
12880 13241 * @type array
12881 13242 * @default []
12882 13243 */
12883 13244 "asStripeClasses": null,
12884
13245
12885 13246 /**
12886 13247 * If restoring a table - we should restore its striping classes as well
12887 13248 * @type array
12888 13249 * @default []
12889 13250 */
12890 13251 "asDestroyStripes": [],
12891
13252
12892 13253 /**
12893 13254 * If restoring a table - we should restore its width
12894 13255 * @type int
12895 13256 * @default 0
12896 13257 */
12897 13258 "sDestroyWidth": 0,
12898
13259
12899 13260 /**
12900 13261 * Callback functions array for every time a row is inserted (i.e. on a draw).
12901 13262 * @type array
12902 13263 * @default []
12903 13264 */
12904 13265 "aoRowCallback": [],
12905
13266
12906 13267 /**
12907 13268 * Callback functions for the header on each draw.
12908 13269 * @type array
12909 13270 * @default []
12910 13271 */
12911 13272 "aoHeaderCallback": [],
12912
13273
12913 13274 /**
12914 13275 * Callback function for the footer on each draw.
12915 13276 * @type array
12916 13277 * @default []
12917 13278 */
12918 13279 "aoFooterCallback": [],
12919
13280
12920 13281 /**
12921 13282 * Array of callback functions for draw callback functions
12922 13283 * @type array
12923 13284 * @default []
12924 13285 */
12925 13286 "aoDrawCallback": [],
12926
13287
12927 13288 /**
12928 13289 * Array of callback functions for row created function
12929 13290 * @type array
12930 13291 * @default []
12931 13292 */
12932 13293 "aoRowCreatedCallback": [],
12933
13294
12934 13295 /**
12935 13296 * Callback functions for just before the table is redrawn. A return of
12936 13297 * false will be used to cancel the draw.
12937 13298 * @type array
12938 13299 * @default []
12939 13300 */
12940 13301 "aoPreDrawCallback": [],
12941
13302
12942 13303 /**
12943 13304 * Callback functions for when the table has been initialised.
12944 13305 * @type array
12945 13306 * @default []
12946 13307 */
12947 13308 "aoInitComplete": [],
12948
12949
13309
13310
12950 13311 /**
12951 13312 * Callbacks for modifying the settings to be stored for state saving, prior to
12952 13313 * saving state.
12953 13314 * @type array
12954 13315 * @default []
12955 13316 */
12956 13317 "aoStateSaveParams": [],
12957
13318
12958 13319 /**
12959 13320 * Callbacks for modifying the settings that have been stored for state saving
12960 13321 * prior to using the stored values to restore the state.
12961 13322 * @type array
12962 13323 * @default []
12963 13324 */
12964 13325 "aoStateLoadParams": [],
12965
13326
12966 13327 /**
12967 13328 * Callbacks for operating on the settings object once the saved state has been
12968 13329 * loaded
12969 13330 * @type array
12970 13331 * @default []
12971 13332 */
12972 13333 "aoStateLoaded": [],
12973
13334
12974 13335 /**
12975 13336 * Cache the table ID for quick access
12976 13337 * @type string
12977 13338 * @default <i>Empty string</i>
12978 13339 */
12979 13340 "sTableId": "",
12980
13341
12981 13342 /**
12982 13343 * The TABLE node for the main table
12983 13344 * @type node
12984 13345 * @default null
12985 13346 */
12986 13347 "nTable": null,
12987
13348
12988 13349 /**
12989 13350 * Permanent ref to the thead element
12990 13351 * @type node
12991 13352 * @default null
12992 13353 */
12993 13354 "nTHead": null,
12994
13355
12995 13356 /**
12996 13357 * Permanent ref to the tfoot element - if it exists
12997 13358 * @type node
12998 13359 * @default null
12999 13360 */
13000 13361 "nTFoot": null,
13001
13362
13002 13363 /**
13003 13364 * Permanent ref to the tbody element
13004 13365 * @type node
13005 13366 * @default null
13006 13367 */
13007 13368 "nTBody": null,
13008
13369
13009 13370 /**
13010 13371 * Cache the wrapper node (contains all DataTables controlled elements)
13011 13372 * @type node
13012 13373 * @default null
13013 13374 */
13014 13375 "nTableWrapper": null,
13015
13376
13016 13377 /**
13017 13378 * Indicate if when using server-side processing the loading of data
13018 13379 * should be deferred until the second draw.
13019 13380 * Note that this parameter will be set by the initialisation routine. To
13020 13381 * set a default use {@link DataTable.defaults}.
13021 13382 * @type boolean
13022 13383 * @default false
13023 13384 */
13024 13385 "bDeferLoading": false,
13025
13386
13026 13387 /**
13027 13388 * Indicate if all required information has been read in
13028 13389 * @type boolean
13029 13390 * @default false
13030 13391 */
13031 13392 "bInitialised": false,
13032
13393
13033 13394 /**
13034 13395 * Information about open rows. Each object in the array has the parameters
13035 13396 * 'nTr' and 'nParent'
13036 13397 * @type array
13037 13398 * @default []
13038 13399 */
13039 13400 "aoOpenRows": [],
13040
13401
13041 13402 /**
13042 13403 * Dictate the positioning of DataTables' control elements - see
13043 13404 * {@link DataTable.model.oInit.sDom}.
13044 13405 * Note that this parameter will be set by the initialisation routine. To
13045 13406 * set a default use {@link DataTable.defaults}.
13046 13407 * @type string
13047 13408 * @default null
13048 13409 */
13049 13410 "sDom": null,
13050
13411
13051 13412 /**
13052 13413 * Search delay (in mS)
13053 13414 * @type integer
13054 13415 * @default null
13055 13416 */
13056 13417 "searchDelay": null,
13057
13418
13058 13419 /**
13059 13420 * Which type of pagination should be used.
13060 13421 * Note that this parameter will be set by the initialisation routine. To
13061 13422 * set a default use {@link DataTable.defaults}.
13062 13423 * @type string
13063 13424 * @default two_button
13064 13425 */
13065 13426 "sPaginationType": "two_button",
13066
13427
13067 13428 /**
13068 13429 * The state duration (for `stateSave`) in seconds.
13069 13430 * Note that this parameter will be set by the initialisation routine. To
13070 13431 * set a default use {@link DataTable.defaults}.
13071 13432 * @type int
13072 13433 * @default 0
13073 13434 */
13074 13435 "iStateDuration": 0,
13075
13436
13076 13437 /**
13077 13438 * Array of callback functions for state saving. Each array element is an
13078 13439 * object with the following parameters:
13079 13440 * <ul>
13080 13441 * <li>function:fn - function to call. Takes two parameters, oSettings
13081 13442 * and the JSON string to save that has been thus far created. Returns
13082 13443 * a JSON string to be inserted into a json object
13083 13444 * (i.e. '"param": [ 0, 1, 2]')</li>
13084 13445 * <li>string:sName - name of callback</li>
13085 13446 * </ul>
13086 13447 * @type array
13087 13448 * @default []
13088 13449 */
13089 13450 "aoStateSave": [],
13090
13451
13091 13452 /**
13092 13453 * Array of callback functions for state loading. Each array element is an
13093 13454 * object with the following parameters:
13094 13455 * <ul>
13095 13456 * <li>function:fn - function to call. Takes two parameters, oSettings
13096 13457 * and the object stored. May return false to cancel state loading</li>
13097 13458 * <li>string:sName - name of callback</li>
13098 13459 * </ul>
13099 13460 * @type array
13100 13461 * @default []
13101 13462 */
13102 13463 "aoStateLoad": [],
13103
13464
13104 13465 /**
13105 13466 * State that was saved. Useful for back reference
13106 13467 * @type object
13107 13468 * @default null
13108 13469 */
13109 13470 "oSavedState": null,
13110
13471
13111 13472 /**
13112 13473 * State that was loaded. Useful for back reference
13113 13474 * @type object
13114 13475 * @default null
13115 13476 */
13116 13477 "oLoadedState": null,
13117
13478
13118 13479 /**
13119 13480 * Source url for AJAX data for the table.
13120 13481 * Note that this parameter will be set by the initialisation routine. To
13121 13482 * set a default use {@link DataTable.defaults}.
13122 13483 * @type string
13123 13484 * @default null
13124 13485 */
13125 13486 "sAjaxSource": null,
13126
13487
13127 13488 /**
13128 13489 * Property from a given object from which to read the table data from. This
13129 13490 * can be an empty string (when not server-side processing), in which case
13130 13491 * it is assumed an an array is given directly.
13131 13492 * Note that this parameter will be set by the initialisation routine. To
13132 13493 * set a default use {@link DataTable.defaults}.
13133 13494 * @type string
13134 13495 */
13135 13496 "sAjaxDataProp": null,
13136
13497
13137 13498 /**
13138 13499 * Note if draw should be blocked while getting data
13139 13500 * @type boolean
13140 13501 * @default true
13141 13502 */
13142 13503 "bAjaxDataGet": true,
13143
13504
13144 13505 /**
13145 13506 * The last jQuery XHR object that was used for server-side data gathering.
13146 13507 * This can be used for working with the XHR information in one of the
13147 13508 * callbacks
13148 13509 * @type object
13149 13510 * @default null
13150 13511 */
13151 13512 "jqXHR": null,
13152
13513
13153 13514 /**
13154 13515 * JSON returned from the server in the last Ajax request
13155 13516 * @type object
13156 13517 * @default undefined
13157 13518 */
13158 13519 "json": undefined,
13159
13520
13160 13521 /**
13161 13522 * Data submitted as part of the last Ajax request
13162 13523 * @type object
13163 13524 * @default undefined
13164 13525 */
13165 13526 "oAjaxData": undefined,
13166
13527
13167 13528 /**
13168 13529 * Function to get the server-side data.
13169 13530 * Note that this parameter will be set by the initialisation routine. To
13170 13531 * set a default use {@link DataTable.defaults}.
13171 13532 * @type function
13172 13533 */
13173 13534 "fnServerData": null,
13174
13535
13175 13536 /**
13176 13537 * Functions which are called prior to sending an Ajax request so extra
13177 13538 * parameters can easily be sent to the server
13178 13539 * @type array
13179 13540 * @default []
13180 13541 */
13181 13542 "aoServerParams": [],
13182
13543
13183 13544 /**
13184 13545 * Send the XHR HTTP method - GET or POST (could be PUT or DELETE if
13185 13546 * required).
13186 13547 * Note that this parameter will be set by the initialisation routine. To
13187 13548 * set a default use {@link DataTable.defaults}.
13188 13549 * @type string
13189 13550 */
13190 13551 "sServerMethod": null,
13191
13552
13192 13553 /**
13193 13554 * Format numbers for display.
13194 13555 * Note that this parameter will be set by the initialisation routine. To
13195 13556 * set a default use {@link DataTable.defaults}.
13196 13557 * @type function
13197 13558 */
13198 13559 "fnFormatNumber": null,
13199
13560
13200 13561 /**
13201 13562 * List of options that can be used for the user selectable length menu.
13202 13563 * Note that this parameter will be set by the initialisation routine. To
13203 13564 * set a default use {@link DataTable.defaults}.
13204 13565 * @type array
13205 13566 * @default []
13206 13567 */
13207 13568 "aLengthMenu": null,
13208
13569
13209 13570 /**
13210 13571 * Counter for the draws that the table does. Also used as a tracker for
13211 13572 * server-side processing
13212 13573 * @type int
13213 13574 * @default 0
13214 13575 */
13215 13576 "iDraw": 0,
13216
13577
13217 13578 /**
13218 13579 * Indicate if a redraw is being done - useful for Ajax
13219 13580 * @type boolean
13220 13581 * @default false
13221 13582 */
13222 13583 "bDrawing": false,
13223
13584
13224 13585 /**
13225 13586 * Draw index (iDraw) of the last error when parsing the returned data
13226 13587 * @type int
13227 13588 * @default -1
13228 13589 */
13229 13590 "iDrawError": -1,
13230
13591
13231 13592 /**
13232 13593 * Paging display length
13233 13594 * @type int
13234 13595 * @default 10
13235 13596 */
13236 13597 "_iDisplayLength": 10,
13237
13598
13238 13599 /**
13239 13600 * Paging start point - aiDisplay index
13240 13601 * @type int
13241 13602 * @default 0
13242 13603 */
13243 13604 "_iDisplayStart": 0,
13244
13605
13245 13606 /**
13246 13607 * Server-side processing - number of records in the result set
13247 13608 * (i.e. before filtering), Use fnRecordsTotal rather than
13248 13609 * this property to get the value of the number of records, regardless of
13249 13610 * the server-side processing setting.
13250 13611 * @type int
13251 13612 * @default 0
13252 13613 * @private
13253 13614 */
13254 13615 "_iRecordsTotal": 0,
13255
13616
13256 13617 /**
13257 13618 * Server-side processing - number of records in the current display set
13258 13619 * (i.e. after filtering). Use fnRecordsDisplay rather than
13259 13620 * this property to get the value of the number of records, regardless of
13260 13621 * the server-side processing setting.
13261 13622 * @type boolean
13262 13623 * @default 0
13263 13624 * @private
13264 13625 */
13265 13626 "_iRecordsDisplay": 0,
13266
13627
13267 13628 /**
13268 13629 * Flag to indicate if jQuery UI marking and classes should be used.
13269 13630 * Note that this parameter will be set by the initialisation routine. To
13270 13631 * set a default use {@link DataTable.defaults}.
13271 13632 * @type boolean
13272 13633 */
13273 13634 "bJUI": null,
13274
13635
13275 13636 /**
13276 13637 * The classes to use for the table
13277 13638 * @type object
13278 13639 * @default {}
13279 13640 */
13280 13641 "oClasses": {},
13281
13642
13282 13643 /**
13283 13644 * Flag attached to the settings object so you can check in the draw
13284 13645 * callback if filtering has been done in the draw. Deprecated in favour of
13285 13646 * events.
13286 13647 * @type boolean
13287 13648 * @default false
13288 13649 * @deprecated
13289 13650 */
13290 13651 "bFiltered": false,
13291
13652
13292 13653 /**
13293 13654 * Flag attached to the settings object so you can check in the draw
13294 13655 * callback if sorting has been done in the draw. Deprecated in favour of
13295 13656 * events.
13296 13657 * @type boolean
13297 13658 * @default false
13298 13659 * @deprecated
13299 13660 */
13300 13661 "bSorted": false,
13301
13662
13302 13663 /**
13303 13664 * Indicate that if multiple rows are in the header and there is more than
13304 13665 * one unique cell per column, if the top one (true) or bottom one (false)
13305 13666 * should be used for sorting / title by DataTables.
13306 13667 * Note that this parameter will be set by the initialisation routine. To
13307 13668 * set a default use {@link DataTable.defaults}.
13308 13669 * @type boolean
13309 13670 */
13310 13671 "bSortCellsTop": null,
13311
13672
13312 13673 /**
13313 13674 * Initialisation object that is used for the table
13314 13675 * @type object
13315 13676 * @default null
13316 13677 */
13317 13678 "oInit": null,
13318
13679
13319 13680 /**
13320 13681 * Destroy callback functions - for plug-ins to attach themselves to the
13321 13682 * destroy so they can clean up markup and events.
13322 13683 * @type array
13323 13684 * @default []
13324 13685 */
13325 13686 "aoDestroyCallback": [],
13326
13327
13687
13688
13328 13689 /**
13329 13690 * Get the number of records in the current record set, before filtering
13330 13691 * @type function
13331 13692 */
13332 13693 "fnRecordsTotal": function ()
13333 13694 {
13334 13695 return _fnDataSource( this ) == 'ssp' ?
13335 13696 this._iRecordsTotal * 1 :
13336 13697 this.aiDisplayMaster.length;
13337 13698 },
13338
13699
13339 13700 /**
13340 13701 * Get the number of records in the current record set, after filtering
13341 13702 * @type function
13342 13703 */
13343 13704 "fnRecordsDisplay": function ()
13344 13705 {
13345 13706 return _fnDataSource( this ) == 'ssp' ?
13346 13707 this._iRecordsDisplay * 1 :
13347 13708 this.aiDisplay.length;
13348 13709 },
13349
13710
13350 13711 /**
13351 13712 * Get the display end point - aiDisplay index
13352 13713 * @type function
13353 13714 */
13354 13715 "fnDisplayEnd": function ()
13355 13716 {
13356 13717 var
13357 13718 len = this._iDisplayLength,
13358 13719 start = this._iDisplayStart,
13359 13720 calc = start + len,
13360 13721 records = this.aiDisplay.length,
13361 13722 features = this.oFeatures,
13362 13723 paginate = features.bPaginate;
13363
13724
13364 13725 if ( features.bServerSide ) {
13365 13726 return paginate === false || len === -1 ?
13366 13727 start + records :
13367 13728 Math.min( start+len, this._iRecordsDisplay );
13368 13729 }
13369 13730 else {
13370 13731 return ! paginate || calc>records || len===-1 ?
13371 13732 records :
13372 13733 calc;
13373 13734 }
13374 13735 },
13375
13736
13376 13737 /**
13377 13738 * The DataTables object for this table
13378 13739 * @type object
13379 13740 * @default null
13380 13741 */
13381 13742 "oInstance": null,
13382
13743
13383 13744 /**
13384 13745 * Unique identifier for each instance of the DataTables object. If there
13385 13746 * is an ID on the table node, then it takes that value, otherwise an
13386 13747 * incrementing internal counter is used.
13387 13748 * @type string
13388 13749 * @default null
13389 13750 */
13390 13751 "sInstance": null,
13391
13752
13392 13753 /**
13393 13754 * tabindex attribute value that is added to DataTables control elements, allowing
13394 13755 * keyboard navigation of the table and its controls.
13395 13756 */
13396 13757 "iTabIndex": 0,
13397
13758
13398 13759 /**
13399 13760 * DIV container for the footer scrolling table if scrolling
13400 13761 */
13401 13762 "nScrollHead": null,
13402
13763
13403 13764 /**
13404 13765 * DIV container for the footer scrolling table if scrolling
13405 13766 */
13406 13767 "nScrollFoot": null,
13407
13768
13408 13769 /**
13409 13770 * Last applied sort
13410 13771 * @type array
13411 13772 * @default []
13412 13773 */
13413 13774 "aLastSort": [],
13414
13775
13415 13776 /**
13416 13777 * Stored plug-in instances
13417 13778 * @type object
13418 13779 * @default {}
13419 13780 */
13420 "oPlugins": {}
13781 "oPlugins": {},
13782
13783 /**
13784 * Function used to get a row's id from the row's data
13785 * @type function
13786 * @default null
13787 */
13788 "rowIdFn": null,
13789
13790 /**
13791 * Data location where to store a row's id
13792 * @type string
13793 * @default null
13794 */
13795 "rowId": null
13421 13796 };
13422 13797
13423 13798 /**
13424 13799 * Extension object for DataTables that is used to provide all extension
13425 13800 * options.
13426 13801 *
13427 13802 * Note that the `DataTable.ext` object is available through
13428 13803 * `jQuery.fn.dataTable.ext` where it may be accessed and manipulated. It is
13429 13804 * also aliased to `jQuery.fn.dataTableExt` for historic reasons.
13430 13805 * @namespace
13431 13806 * @extends DataTable.models.ext
13432 13807 */
13433
13434
13808
13809
13435 13810 /**
13436 13811 * DataTables extensions
13437 *
13812 *
13438 13813 * This namespace acts as a collection area for plug-ins that can be used to
13439 13814 * extend DataTables capabilities. Indeed many of the build in methods
13440 13815 * use this method to provide their own capabilities (sorting methods for
13441 13816 * example).
13442 13817 *
13443 13818 * Note that this namespace is aliased to `jQuery.fn.dataTableExt` for legacy
13444 13819 * reasons
13445 13820 *
13446 13821 * @namespace
13447 13822 */
13448 13823 DataTable.ext = _ext = {
13449 13824 /**
13825 * Buttons. For use with the Buttons extension for DataTables. This is
13826 * defined here so other extensions can define buttons regardless of load
13827 * order. It is _not_ used by DataTables core.
13828 *
13829 * @type object
13830 * @default {}
13831 */
13832 buttons: {},
13833
13834
13835 /**
13450 13836 * Element class names
13451 13837 *
13452 13838 * @type object
13453 13839 * @default {}
13454 13840 */
13455 13841 classes: {},
13456
13457
13842
13843
13844 /**
13845 * DataTables build type (expanded by the download builder)
13846 *
13847 * @type string
13848 */
13849 builder: "-source-",
13850
13851
13458 13852 /**
13459 13853 * Error reporting.
13460 *
13461 * How should DataTables report an error. Can take the value 'alert' or
13462 * 'throw'
13463 *
13464 * @type string
13854 *
13855 * How should DataTables report an error. Can take the value 'alert',
13856 * 'throw', 'none' or a function.
13857 *
13858 * @type string|function
13465 13859 * @default alert
13466 13860 */
13467 13861 errMode: "alert",
13468
13469
13862
13863
13470 13864 /**
13471 13865 * Feature plug-ins.
13472 *
13866 *
13473 13867 * This is an array of objects which describe the feature plug-ins that are
13474 13868 * available to DataTables. These feature plug-ins are then available for
13475 13869 * use through the `dom` initialisation option.
13476 *
13870 *
13477 13871 * Each feature plug-in is described by an object which must have the
13478 13872 * following properties:
13479 *
13873 *
13480 13874 * * `fnInit` - function that is used to initialise the plug-in,
13481 13875 * * `cFeature` - a character so the feature can be enabled by the `dom`
13482 13876 * instillation option. This is case sensitive.
13483 13877 *
13484 13878 * The `fnInit` function has the following input parameters:
13485 13879 *
13486 13880 * 1. `{object}` DataTables settings object: see
13487 13881 * {@link DataTable.models.oSettings}
13488 13882 *
13489 13883 * And the following return is expected:
13490 *
13884 *
13491 13885 * * {node|null} The element which contains your feature. Note that the
13492 13886 * return may also be void if your plug-in does not require to inject any
13493 13887 * DOM elements into DataTables control (`dom`) - for example this might
13494 13888 * be useful when developing a plug-in which allows table control via
13495 13889 * keyboard entry
13496 13890 *
13497 13891 * @type array
13498 13892 *
13499 13893 * @example
13500 13894 * $.fn.dataTable.ext.features.push( {
13501 13895 * "fnInit": function( oSettings ) {
13502 13896 * return new TableTools( { "oDTSettings": oSettings } );
13503 13897 * },
13504 13898 * "cFeature": "T"
13505 13899 * } );
13506 13900 */
13507 13901 feature: [],
13508
13509
13902
13903
13510 13904 /**
13511 13905 * Row searching.
13512 *
13906 *
13513 13907 * This method of searching is complimentary to the default type based
13514 13908 * searching, and a lot more comprehensive as it allows you complete control
13515 13909 * over the searching logic. Each element in this array is a function
13516 13910 * (parameters described below) that is called for every row in the table,
13517 13911 * and your logic decides if it should be included in the searching data set
13518 13912 * or not.
13519 13913 *
13520 13914 * Searching functions have the following input parameters:
13521 13915 *
13522 13916 * 1. `{object}` DataTables settings object: see
13523 13917 * {@link DataTable.models.oSettings}
13524 13918 * 2. `{array|object}` Data for the row to be processed (same as the
13525 13919 * original format that was passed in as the data source, or an array
13526 13920 * from a DOM data source
13527 13921 * 3. `{int}` Row index ({@link DataTable.models.oSettings.aoData}), which
13528 13922 * can be useful to retrieve the `TR` element if you need DOM interaction.
13529 13923 *
13530 13924 * And the following return is expected:
13531 13925 *
13532 13926 * * {boolean} Include the row in the searched result set (true) or not
13533 13927 * (false)
13534 13928 *
13535 13929 * Note that as with the main search ability in DataTables, technically this
13536 13930 * is "filtering", since it is subtractive. However, for consistency in
13537 13931 * naming we call it searching here.
13538 13932 *
13539 13933 * @type array
13540 13934 * @default []
13541 13935 *
13542 13936 * @example
13543 13937 * // The following example shows custom search being applied to the
13544 13938 * // fourth column (i.e. the data[3] index) based on two input values
13545 13939 * // from the end-user, matching the data in a certain range.
13546 13940 * $.fn.dataTable.ext.search.push(
13547 13941 * function( settings, data, dataIndex ) {
13548 13942 * var min = document.getElementById('min').value * 1;
13549 13943 * var max = document.getElementById('max').value * 1;
13550 13944 * var version = data[3] == "-" ? 0 : data[3]*1;
13551 13945 *
13552 13946 * if ( min == "" && max == "" ) {
13553 13947 * return true;
13554 13948 * }
13555 13949 * else if ( min == "" && version < max ) {
13556 13950 * return true;
13557 13951 * }
13558 13952 * else if ( min < version && "" == max ) {
13559 13953 * return true;
13560 13954 * }
13561 13955 * else if ( min < version && version < max ) {
13562 13956 * return true;
13563 13957 * }
13564 13958 * return false;
13565 13959 * }
13566 13960 * );
13567 13961 */
13568 13962 search: [],
13569
13570
13963
13964
13965 /**
13966 * Selector extensions
13967 *
13968 * The `selector` option can be used to extend the options available for the
13969 * selector modifier options (`selector-modifier` object data type) that
13970 * each of the three built in selector types offer (row, column and cell +
13971 * their plural counterparts). For example the Select extension uses this
13972 * mechanism to provide an option to select only rows, columns and cells
13973 * that have been marked as selected by the end user (`{selected: true}`),
13974 * which can be used in conjunction with the existing built in selector
13975 * options.
13976 *
13977 * Each property is an array to which functions can be pushed. The functions
13978 * take three attributes:
13979 *
13980 * * Settings object for the host table
13981 * * Options object (`selector-modifier` object type)
13982 * * Array of selected item indexes
13983 *
13984 * The return is an array of the resulting item indexes after the custom
13985 * selector has been applied.
13986 *
13987 * @type object
13988 */
13989 selector: {
13990 cell: [],
13991 column: [],
13992 row: []
13993 },
13994
13995
13571 13996 /**
13572 13997 * Internal functions, exposed for used in plug-ins.
13573 *
13998 *
13574 13999 * Please note that you should not need to use the internal methods for
13575 14000 * anything other than a plug-in (and even then, try to avoid if possible).
13576 14001 * The internal function may change between releases.
13577 14002 *
13578 14003 * @type object
13579 14004 * @default {}
13580 14005 */
13581 14006 internal: {},
13582
13583
14007
14008
13584 14009 /**
13585 14010 * Legacy configuration options. Enable and disable legacy options that
13586 14011 * are available in DataTables.
13587 14012 *
13588 14013 * @type object
13589 14014 */
13590 14015 legacy: {
13591 14016 /**
13592 14017 * Enable / disable DataTables 1.9 compatible server-side processing
13593 14018 * requests
13594 14019 *
13595 14020 * @type boolean
13596 14021 * @default null
13597 14022 */
13598 14023 ajax: null
13599 14024 },
13600
13601
14025
14026
13602 14027 /**
13603 14028 * Pagination plug-in methods.
13604 *
14029 *
13605 14030 * Each entry in this object is a function and defines which buttons should
13606 14031 * be shown by the pagination rendering method that is used for the table:
13607 14032 * {@link DataTable.ext.renderer.pageButton}. The renderer addresses how the
13608 14033 * buttons are displayed in the document, while the functions here tell it
13609 14034 * what buttons to display. This is done by returning an array of button
13610 14035 * descriptions (what each button will do).
13611 14036 *
13612 14037 * Pagination types (the four built in options and any additional plug-in
13613 14038 * options defined here) can be used through the `paginationType`
13614 14039 * initialisation parameter.
13615 14040 *
13616 14041 * The functions defined take two parameters:
13617 14042 *
13618 14043 * 1. `{int} page` The current page index
13619 14044 * 2. `{int} pages` The number of pages in the table
13620 14045 *
13621 14046 * Each function is expected to return an array where each element of the
13622 14047 * array can be one of:
13623 14048 *
13624 14049 * * `first` - Jump to first page when activated
13625 14050 * * `last` - Jump to last page when activated
13626 14051 * * `previous` - Show previous page when activated
13627 14052 * * `next` - Show next page when activated
13628 14053 * * `{int}` - Show page of the index given
13629 14054 * * `{array}` - A nested array containing the above elements to add a
13630 14055 * containing 'DIV' element (might be useful for styling).
13631 14056 *
13632 14057 * Note that DataTables v1.9- used this object slightly differently whereby
13633 14058 * an object with two functions would be defined for each plug-in. That
13634 14059 * ability is still supported by DataTables 1.10+ to provide backwards
13635 14060 * compatibility, but this option of use is now decremented and no longer
13636 14061 * documented in DataTables 1.10+.
13637 14062 *
13638 14063 * @type object
13639 14064 * @default {}
13640 14065 *
13641 14066 * @example
13642 14067 * // Show previous, next and current page buttons only
13643 14068 * $.fn.dataTableExt.oPagination.current = function ( page, pages ) {
13644 14069 * return [ 'previous', page, 'next' ];
13645 14070 * };
13646 14071 */
13647 14072 pager: {},
13648
13649
14073
14074
13650 14075 renderer: {
13651 14076 pageButton: {},
13652 14077 header: {}
13653 14078 },
13654
13655
14079
14080
13656 14081 /**
13657 14082 * Ordering plug-ins - custom data source
13658 *
14083 *
13659 14084 * The extension options for ordering of data available here is complimentary
13660 14085 * to the default type based ordering that DataTables typically uses. It
13661 14086 * allows much greater control over the the data that is being used to
13662 14087 * order a column, but is necessarily therefore more complex.
13663 *
14088 *
13664 14089 * This type of ordering is useful if you want to do ordering based on data
13665 14090 * live from the DOM (for example the contents of an 'input' element) rather
13666 14091 * than just the static string that DataTables knows of.
13667 *
14092 *
13668 14093 * The way these plug-ins work is that you create an array of the values you
13669 14094 * wish to be ordering for the column in question and then return that
13670 14095 * array. The data in the array much be in the index order of the rows in
13671 14096 * the table (not the currently ordering order!). Which order data gathering
13672 14097 * function is run here depends on the `dt-init columns.orderDataType`
13673 14098 * parameter that is used for the column (if any).
13674 14099 *
13675 14100 * The functions defined take two parameters:
13676 14101 *
13677 14102 * 1. `{object}` DataTables settings object: see
13678 14103 * {@link DataTable.models.oSettings}
13679 14104 * 2. `{int}` Target column index
13680 14105 *
13681 14106 * Each function is expected to return an array:
13682 14107 *
13683 14108 * * `{array}` Data for the column to be ordering upon
13684 14109 *
13685 14110 * @type array
13686 14111 *
13687 14112 * @example
13688 14113 * // Ordering using `input` node values
13689 14114 * $.fn.dataTable.ext.order['dom-text'] = function ( settings, col )
13690 14115 * {
13691 14116 * return this.api().column( col, {order:'index'} ).nodes().map( function ( td, i ) {
13692 14117 * return $('input', td).val();
13693 14118 * } );
13694 14119 * }
13695 14120 */
13696 14121 order: {},
13697
13698
14122
14123
13699 14124 /**
13700 14125 * Type based plug-ins.
13701 14126 *
13702 14127 * Each column in DataTables has a type assigned to it, either by automatic
13703 14128 * detection or by direct assignment using the `type` option for the column.
13704 14129 * The type of a column will effect how it is ordering and search (plug-ins
13705 14130 * can also make use of the column type if required).
13706 14131 *
13707 14132 * @namespace
13708 14133 */
13709 14134 type: {
13710 14135 /**
13711 14136 * Type detection functions.
13712 14137 *
13713 14138 * The functions defined in this object are used to automatically detect
13714 14139 * a column's type, making initialisation of DataTables super easy, even
13715 14140 * when complex data is in the table.
13716 14141 *
13717 14142 * The functions defined take two parameters:
13718 14143 *
13719 14144 * 1. `{*}` Data from the column cell to be analysed
13720 14145 * 2. `{settings}` DataTables settings object. This can be used to
13721 14146 * perform context specific type detection - for example detection
13722 14147 * based on language settings such as using a comma for a decimal
13723 14148 * place. Generally speaking the options from the settings will not
13724 14149 * be required
13725 14150 *
13726 14151 * Each function is expected to return:
13727 14152 *
13728 14153 * * `{string|null}` Data type detected, or null if unknown (and thus
13729 14154 * pass it on to the other type detection functions.
13730 14155 *
13731 14156 * @type array
13732 14157 *
13733 14158 * @example
13734 14159 * // Currency type detection plug-in:
13735 14160 * $.fn.dataTable.ext.type.detect.push(
13736 14161 * function ( data, settings ) {
13737 14162 * // Check the numeric part
13738 14163 * if ( ! $.isNumeric( data.substring(1) ) ) {
13739 14164 * return null;
13740 14165 * }
13741 14166 *
13742 14167 * // Check prefixed by currency
13743 14168 * if ( data.charAt(0) == '$' || data.charAt(0) == '&pound;' ) {
13744 14169 * return 'currency';
13745 14170 * }
13746 14171 * return null;
13747 14172 * }
13748 14173 * );
13749 14174 */
13750 14175 detect: [],
13751
13752
14176
14177
13753 14178 /**
13754 14179 * Type based search formatting.
13755 14180 *
13756 14181 * The type based searching functions can be used to pre-format the
13757 14182 * data to be search on. For example, it can be used to strip HTML
13758 14183 * tags or to de-format telephone numbers for numeric only searching.
13759 14184 *
13760 14185 * Note that is a search is not defined for a column of a given type,
13761 14186 * no search formatting will be performed.
13762 *
14187 *
13763 14188 * Pre-processing of searching data plug-ins - When you assign the sType
13764 14189 * for a column (or have it automatically detected for you by DataTables
13765 14190 * or a type detection plug-in), you will typically be using this for
13766 14191 * custom sorting, but it can also be used to provide custom searching
13767 14192 * by allowing you to pre-processing the data and returning the data in
13768 14193 * the format that should be searched upon. This is done by adding
13769 14194 * functions this object with a parameter name which matches the sType
13770 14195 * for that target column. This is the corollary of <i>afnSortData</i>
13771 14196 * for searching data.
13772 14197 *
13773 14198 * The functions defined take a single parameter:
13774 14199 *
13775 14200 * 1. `{*}` Data from the column cell to be prepared for searching
13776 14201 *
13777 14202 * Each function is expected to return:
13778 14203 *
13779 14204 * * `{string|null}` Formatted string that will be used for the searching.
13780 14205 *
13781 14206 * @type object
13782 14207 * @default {}
13783 14208 *
13784 14209 * @example
13785 14210 * $.fn.dataTable.ext.type.search['title-numeric'] = function ( d ) {
13786 14211 * return d.replace(/\n/g," ").replace( /<.*?>/g, "" );
13787 14212 * }
13788 14213 */
13789 14214 search: {},
13790
13791
14215
14216
13792 14217 /**
13793 14218 * Type based ordering.
13794 14219 *
13795 14220 * The column type tells DataTables what ordering to apply to the table
13796 14221 * when a column is sorted upon. The order for each type that is defined,
13797 14222 * is defined by the functions available in this object.
13798 14223 *
13799 14224 * Each ordering option can be described by three properties added to
13800 14225 * this object:
13801 14226 *
13802 14227 * * `{type}-pre` - Pre-formatting function
13803 14228 * * `{type}-asc` - Ascending order function
13804 14229 * * `{type}-desc` - Descending order function
13805 14230 *
13806 14231 * All three can be used together, only `{type}-pre` or only
13807 14232 * `{type}-asc` and `{type}-desc` together. It is generally recommended
13808 14233 * that only `{type}-pre` is used, as this provides the optimal
13809 14234 * implementation in terms of speed, although the others are provided
13810 14235 * for compatibility with existing Javascript sort functions.
13811 14236 *
13812 14237 * `{type}-pre`: Functions defined take a single parameter:
13813 14238 *
13814 14239 * 1. `{*}` Data from the column cell to be prepared for ordering
13815 14240 *
13816 14241 * And return:
13817 14242 *
13818 14243 * * `{*}` Data to be sorted upon
13819 14244 *
13820 14245 * `{type}-asc` and `{type}-desc`: Functions are typical Javascript sort
13821 14246 * functions, taking two parameters:
13822 14247 *
13823 14248 * 1. `{*}` Data to compare to the second parameter
13824 14249 * 2. `{*}` Data to compare to the first parameter
13825 14250 *
13826 14251 * And returning:
13827 14252 *
13828 14253 * * `{*}` Ordering match: <0 if first parameter should be sorted lower
13829 14254 * than the second parameter, ===0 if the two parameters are equal and
13830 14255 * >0 if the first parameter should be sorted height than the second
13831 14256 * parameter.
13832 *
14257 *
13833 14258 * @type object
13834 14259 * @default {}
13835 14260 *
13836 14261 * @example
13837 14262 * // Numeric ordering of formatted numbers with a pre-formatter
13838 14263 * $.extend( $.fn.dataTable.ext.type.order, {
13839 14264 * "string-pre": function(x) {
13840 14265 * a = (a === "-" || a === "") ? 0 : a.replace( /[^\d\-\.]/g, "" );
13841 14266 * return parseFloat( a );
13842 14267 * }
13843 14268 * } );
13844 14269 *
13845 14270 * @example
13846 14271 * // Case-sensitive string ordering, with no pre-formatting method
13847 14272 * $.extend( $.fn.dataTable.ext.order, {
13848 14273 * "string-case-asc": function(x,y) {
13849 14274 * return ((x < y) ? -1 : ((x > y) ? 1 : 0));
13850 14275 * },
13851 14276 * "string-case-desc": function(x,y) {
13852 14277 * return ((x < y) ? 1 : ((x > y) ? -1 : 0));
13853 14278 * }
13854 14279 * } );
13855 14280 */
13856 14281 order: {}
13857 14282 },
13858
14283
13859 14284 /**
13860 14285 * Unique DataTables instance counter
13861 14286 *
13862 14287 * @type int
13863 14288 * @private
13864 14289 */
13865 14290 _unique: 0,
13866
13867
14291
14292
13868 14293 //
13869 14294 // Depreciated
13870 14295 // The following properties are retained for backwards compatiblity only.
13871 14296 // The should not be used in new projects and will be removed in a future
13872 14297 // version
13873 14298 //
13874
14299
13875 14300 /**
13876 14301 * Version check function.
13877 14302 * @type function
13878 14303 * @depreciated Since 1.10
13879 14304 */
13880 14305 fnVersionCheck: DataTable.fnVersionCheck,
13881
13882
14306
14307
13883 14308 /**
13884 14309 * Index for what 'this' index API functions should use
13885 14310 * @type int
13886 14311 * @deprecated Since v1.10
13887 14312 */
13888 14313 iApiIndex: 0,
13889
13890
14314
14315
13891 14316 /**
13892 14317 * jQuery UI class container
13893 14318 * @type object
13894 14319 * @deprecated Since v1.10
13895 14320 */
13896 14321 oJUIClasses: {},
13897
13898
14322
14323
13899 14324 /**
13900 14325 * Software version
13901 14326 * @type string
13902 14327 * @deprecated Since v1.10
13903 14328 */
13904 14329 sVersion: DataTable.version
13905 14330 };
13906
13907
14331
14332
13908 14333 //
13909 14334 // Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts
13910 14335 //
13911 14336 $.extend( _ext, {
13912 14337 afnFiltering: _ext.search,
13913 14338 aTypes: _ext.type.detect,
13914 14339 ofnSearch: _ext.type.search,
13915 14340 oSort: _ext.type.order,
13916 14341 afnSortData: _ext.order,
13917 14342 aoFeatures: _ext.feature,
13918 14343 oApi: _ext.internal,
13919 14344 oStdClasses: _ext.classes,
13920 14345 oPagination: _ext.pager
13921 14346 } );
13922
13923
14347
14348
13924 14349 $.extend( DataTable.ext.classes, {
13925 14350 "sTable": "dataTable",
13926 14351 "sNoFooter": "no-footer",
13927
14352
13928 14353 /* Paging buttons */
13929 14354 "sPageButton": "paginate_button",
13930 14355 "sPageButtonActive": "current",
13931 14356 "sPageButtonDisabled": "disabled",
13932
14357
13933 14358 /* Striping classes */
13934 14359 "sStripeOdd": "odd",
13935 14360 "sStripeEven": "even",
13936
14361
13937 14362 /* Empty row */
13938 14363 "sRowEmpty": "dataTables_empty",
13939
14364
13940 14365 /* Features */
13941 14366 "sWrapper": "dataTables_wrapper",
13942 14367 "sFilter": "dataTables_filter",
13943 14368 "sInfo": "dataTables_info",
13944 14369 "sPaging": "dataTables_paginate paging_", /* Note that the type is postfixed */
13945 14370 "sLength": "dataTables_length",
13946 14371 "sProcessing": "dataTables_processing",
13947
14372
13948 14373 /* Sorting */
13949 14374 "sSortAsc": "sorting_asc",
13950 14375 "sSortDesc": "sorting_desc",
13951 14376 "sSortable": "sorting", /* Sortable in both directions */
13952 14377 "sSortableAsc": "sorting_asc_disabled",
13953 14378 "sSortableDesc": "sorting_desc_disabled",
13954 14379 "sSortableNone": "sorting_disabled",
13955 14380 "sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */
13956
14381
13957 14382 /* Filtering */
13958 14383 "sFilterInput": "",
13959
14384
13960 14385 /* Page length */
13961 14386 "sLengthSelect": "",
13962
14387
13963 14388 /* Scrolling */
13964 14389 "sScrollWrapper": "dataTables_scroll",
13965 14390 "sScrollHead": "dataTables_scrollHead",
13966 14391 "sScrollHeadInner": "dataTables_scrollHeadInner",
13967 14392 "sScrollBody": "dataTables_scrollBody",
13968 14393 "sScrollFoot": "dataTables_scrollFoot",
13969 14394 "sScrollFootInner": "dataTables_scrollFootInner",
13970
14395
13971 14396 /* Misc */
13972 14397 "sHeaderTH": "",
13973 14398 "sFooterTH": "",
13974
14399
13975 14400 // Deprecated
13976 14401 "sSortJUIAsc": "",
13977 14402 "sSortJUIDesc": "",
13978 14403 "sSortJUI": "",
13979 14404 "sSortJUIAscAllowed": "",
13980 14405 "sSortJUIDescAllowed": "",
13981 14406 "sSortJUIWrapper": "",
13982 14407 "sSortIcon": "",
13983 14408 "sJUIHeader": "",
13984 14409 "sJUIFooter": ""
13985 14410 } );
13986
13987
14411
14412
13988 14413 (function() {
13989
14414
13990 14415 // Reused strings for better compression. Closure compiler appears to have a
13991 14416 // weird edge case where it is trying to expand strings rather than use the
13992 14417 // variable version. This results in about 200 bytes being added, for very
13993 14418 // little preference benefit since it this run on script load only.
13994 14419 var _empty = '';
13995 14420 _empty = '';
13996
14421
13997 14422 var _stateDefault = _empty + 'ui-state-default';
13998 14423 var _sortIcon = _empty + 'css_right ui-icon ui-icon-';
13999 14424 var _headerFooter = _empty + 'fg-toolbar ui-toolbar ui-widget-header ui-helper-clearfix';
14000
14425
14001 14426 $.extend( DataTable.ext.oJUIClasses, DataTable.ext.classes, {
14002 14427 /* Full numbers paging buttons */
14003 14428 "sPageButton": "fg-button ui-button "+_stateDefault,
14004 14429 "sPageButtonActive": "ui-state-disabled",
14005 14430 "sPageButtonDisabled": "ui-state-disabled",
14006
14431
14007 14432 /* Features */
14008 14433 "sPaging": "dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi "+
14009 14434 "ui-buttonset-multi paging_", /* Note that the type is postfixed */
14010
14435
14011 14436 /* Sorting */
14012 14437 "sSortAsc": _stateDefault+" sorting_asc",
14013 14438 "sSortDesc": _stateDefault+" sorting_desc",
14014 14439 "sSortable": _stateDefault+" sorting",
14015 14440 "sSortableAsc": _stateDefault+" sorting_asc_disabled",
14016 14441 "sSortableDesc": _stateDefault+" sorting_desc_disabled",
14017 14442 "sSortableNone": _stateDefault+" sorting_disabled",
14018 14443 "sSortJUIAsc": _sortIcon+"triangle-1-n",
14019 14444 "sSortJUIDesc": _sortIcon+"triangle-1-s",
14020 14445 "sSortJUI": _sortIcon+"carat-2-n-s",
14021 14446 "sSortJUIAscAllowed": _sortIcon+"carat-1-n",
14022 14447 "sSortJUIDescAllowed": _sortIcon+"carat-1-s",
14023 14448 "sSortJUIWrapper": "DataTables_sort_wrapper",
14024 14449 "sSortIcon": "DataTables_sort_icon",
14025
14450
14026 14451 /* Scrolling */
14027 14452 "sScrollHead": "dataTables_scrollHead "+_stateDefault,
14028 14453 "sScrollFoot": "dataTables_scrollFoot "+_stateDefault,
14029
14454
14030 14455 /* Misc */
14031 14456 "sHeaderTH": _stateDefault,
14032 14457 "sFooterTH": _stateDefault,
14033 14458 "sJUIHeader": _headerFooter+" ui-corner-tl ui-corner-tr",
14034 14459 "sJUIFooter": _headerFooter+" ui-corner-bl ui-corner-br"
14035 14460 } );
14036
14461
14037 14462 }());
14038
14039
14040
14463
14464
14465
14041 14466 var extPagination = DataTable.ext.pager;
14042
14467
14043 14468 function _numbers ( page, pages ) {
14044 14469 var
14045 14470 numbers = [],
14046 14471 buttons = extPagination.numbers_length,
14047 14472 half = Math.floor( buttons / 2 ),
14048 14473 i = 1;
14049
14474
14050 14475 if ( pages <= buttons ) {
14051 14476 numbers = _range( 0, pages );
14052 14477 }
14053 14478 else if ( page <= half ) {
14054 14479 numbers = _range( 0, buttons-2 );
14055 14480 numbers.push( 'ellipsis' );
14056 14481 numbers.push( pages-1 );
14057 14482 }
14058 14483 else if ( page >= pages - 1 - half ) {
14059 14484 numbers = _range( pages-(buttons-2), pages );
14060 14485 numbers.splice( 0, 0, 'ellipsis' ); // no unshift in ie6
14061 14486 numbers.splice( 0, 0, 0 );
14062 14487 }
14063 14488 else {
14064 numbers = _range( page-1, page+2 );
14489 numbers = _range( page-half+2, page+half-1 );
14065 14490 numbers.push( 'ellipsis' );
14066 14491 numbers.push( pages-1 );
14067 14492 numbers.splice( 0, 0, 'ellipsis' );
14068 14493 numbers.splice( 0, 0, 0 );
14069 14494 }
14070
14495
14071 14496 numbers.DT_el = 'span';
14072 14497 return numbers;
14073 14498 }
14074
14075
14499
14500
14076 14501 $.extend( extPagination, {
14077 14502 simple: function ( page, pages ) {
14078 14503 return [ 'previous', 'next' ];
14079 14504 },
14080
14505
14081 14506 full: function ( page, pages ) {
14082 14507 return [ 'first', 'previous', 'next', 'last' ];
14083 14508 },
14084
14509
14510 numbers: function ( page, pages ) {
14511 return [ _numbers(page, pages) ];
14512 },
14513
14085 14514 simple_numbers: function ( page, pages ) {
14086 14515 return [ 'previous', _numbers(page, pages), 'next' ];
14087 14516 },
14088
14517
14089 14518 full_numbers: function ( page, pages ) {
14090 14519 return [ 'first', 'previous', _numbers(page, pages), 'next', 'last' ];
14091 14520 },
14092
14521
14522 first_last_numbers: function (page, pages) {
14523 return ['first', _numbers(page, pages), 'last'];
14524 },
14525
14093 14526 // For testing and plug-ins to use
14094 14527 _numbers: _numbers,
14528
14529 // Number of number buttons (including ellipsis) to show. _Must be odd!_
14095 14530 numbers_length: 7
14096 14531 } );
14097
14098
14532
14533
14099 14534 $.extend( true, DataTable.ext.renderer, {
14100 14535 pageButton: {
14101 14536 _: function ( settings, host, idx, buttons, page, pages ) {
14102 14537 var classes = settings.oClasses;
14103 14538 var lang = settings.oLanguage.oPaginate;
14539 var aria = settings.oLanguage.oAria.paginate || {};
14104 14540 var btnDisplay, btnClass, counter=0;
14105
14541
14106 14542 var attach = function( container, buttons ) {
14107 14543 var i, ien, node, button;
14108 14544 var clickHandler = function ( e ) {
14109 14545 _fnPageChange( settings, e.data.action, true );
14110 14546 };
14111
14547
14112 14548 for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
14113 14549 button = buttons[i];
14114
14550
14115 14551 if ( $.isArray( button ) ) {
14116 14552 var inner = $( '<'+(button.DT_el || 'div')+'/>' )
14117 14553 .appendTo( container );
14118 14554 attach( inner, button );
14119 14555 }
14120 14556 else {
14121 btnDisplay = '';
14557 btnDisplay = null;
14122 14558 btnClass = '';
14123
14559
14124 14560 switch ( button ) {
14125 case 'ellipsis':
14126 //RhodeCode fixed, added class paginate_button
14127 container.append('<span class=\"paginate_button\">&hellip;</span>');
14561 case 'ellipsis':
14562 container.append('<span class="ellipsis">&#x2026;</span>');
14128 14563 break;
14129
14564
14130 14565 case 'first':
14131 14566 btnDisplay = lang.sFirst;
14132 14567 btnClass = button + (page > 0 ?
14133 14568 '' : ' '+classes.sPageButtonDisabled);
14134 14569 break;
14135
14570
14136 14571 case 'previous':
14137 14572 btnDisplay = lang.sPrevious;
14138 14573 btnClass = button + (page > 0 ?
14139 14574 '' : ' '+classes.sPageButtonDisabled);
14140 14575 break;
14141
14576
14142 14577 case 'next':
14143 14578 btnDisplay = lang.sNext;
14144 14579 btnClass = button + (page < pages-1 ?
14145 14580 '' : ' '+classes.sPageButtonDisabled);
14146 14581 break;
14147
14582
14148 14583 case 'last':
14149 14584 btnDisplay = lang.sLast;
14150 14585 btnClass = button + (page < pages-1 ?
14151 14586 '' : ' '+classes.sPageButtonDisabled);
14152 14587 break;
14153
14588
14154 14589 default:
14155 14590 btnDisplay = button + 1;
14156 14591 btnClass = page === button ?
14157 14592 classes.sPageButtonActive : '';
14158 14593 break;
14159 14594 }
14160
14161 if ( btnDisplay ) {
14595
14596 if ( btnDisplay !== null ) {
14162 14597 node = $('<a>', {
14163 14598 'class': classes.sPageButton+' '+btnClass,
14164 14599 'aria-controls': settings.sTableId,
14600 'aria-label': aria[ button ],
14165 14601 'data-dt-idx': counter,
14166 14602 'tabindex': settings.iTabIndex,
14167 14603 'id': idx === 0 && typeof button === 'string' ?
14168 14604 settings.sTableId +'_'+ button :
14169 14605 null
14170 14606 } )
14171 14607 .html( btnDisplay )
14172 14608 .appendTo( container );
14173
14609
14174 14610 _fnBindAction(
14175 14611 node, {action: button}, clickHandler
14176 14612 );
14177
14613
14178 14614 counter++;
14179 14615 }
14180 14616 }
14181 14617 }
14182 14618 };
14183
14619
14184 14620 // IE9 throws an 'unknown error' if document.activeElement is used
14185 14621 // inside an iframe or frame. Try / catch the error. Not good for
14186 14622 // accessibility, but neither are frames.
14623 var activeEl;
14624
14187 14625 try {
14188 14626 // Because this approach is destroying and recreating the paging
14189 14627 // elements, focus is lost on the select button which is bad for
14190 14628 // accessibility. So we want to restore focus once the draw has
14191 14629 // completed
14192 var activeEl = $(document.activeElement).data('dt-idx');
14193
14194 attach( $(host).empty(), buttons );
14195
14196 if ( activeEl !== null ) {
14197 $(host).find( '[data-dt-idx='+activeEl+']' ).focus();
14198 }
14630 activeEl = $(host).find(document.activeElement).data('dt-idx');
14199 14631 }
14200 14632 catch (e) {}
14201 }
14202 }
14203 } );
14204
14205
14206
14633
14634 attach( $(host).empty(), buttons );
14635
14636 if ( activeEl !== undefined ) {
14637 $(host).find( '[data-dt-idx='+activeEl+']' ).focus();
14638 }
14639 }
14640 }
14641 } );
14642
14643
14644
14207 14645 // Built in type detection. See model.ext.aTypes for information about
14208 14646 // what is required from this methods.
14209 14647 $.extend( DataTable.ext.type.detect, [
14210 14648 // Plain numbers - first since V8 detects some plain numbers as dates
14211 14649 // e.g. Date.parse('55') (but not all, e.g. Date.parse('22')...).
14212 14650 function ( d, settings )
14213 14651 {
14214 14652 var decimal = settings.oLanguage.sDecimal;
14215 14653 return _isNumber( d, decimal ) ? 'num'+decimal : null;
14216 14654 },
14217
14655
14218 14656 // Dates (only those recognised by the browser's Date.parse)
14219 14657 function ( d, settings )
14220 14658 {
14221 // V8 will remove any unknown characters at the start and end of the
14222 // expression, leading to false matches such as `$245.12` or `10%` being
14223 // a valid date. See forum thread 18941 for detail.
14224 if ( d && !(d instanceof Date) && ( ! _re_date_start.test(d) || ! _re_date_end.test(d) ) ) {
14659 // V8 tries _very_ hard to make a string passed into `Date.parse()`
14660 // valid, so we need to use a regex to restrict date formats. Use a
14661 // plug-in for anything other than ISO8601 style strings
14662 if ( d && !(d instanceof Date) && ! _re_date.test(d) ) {
14225 14663 return null;
14226 14664 }
14227 14665 var parsed = Date.parse(d);
14228 14666 return (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null;
14229 14667 },
14230
14668
14231 14669 // Formatted numbers
14232 14670 function ( d, settings )
14233 14671 {
14234 14672 var decimal = settings.oLanguage.sDecimal;
14235 14673 return _isNumber( d, decimal, true ) ? 'num-fmt'+decimal : null;
14236 14674 },
14237
14675
14238 14676 // HTML numeric
14239 14677 function ( d, settings )
14240 14678 {
14241 14679 var decimal = settings.oLanguage.sDecimal;
14242 14680 return _htmlNumeric( d, decimal ) ? 'html-num'+decimal : null;
14243 14681 },
14244
14682
14245 14683 // HTML numeric, formatted
14246 14684 function ( d, settings )
14247 14685 {
14248 14686 var decimal = settings.oLanguage.sDecimal;
14249 14687 return _htmlNumeric( d, decimal, true ) ? 'html-num-fmt'+decimal : null;
14250 14688 },
14251
14689
14252 14690 // HTML (this is strict checking - there must be html)
14253 14691 function ( d, settings )
14254 14692 {
14255 14693 return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1) ?
14256 14694 'html' : null;
14257 14695 }
14258 14696 ] );
14259
14260
14261
14697
14698
14699
14262 14700 // Filter formatting functions. See model.ext.ofnSearch for information about
14263 14701 // what is required from these methods.
14264 //
14702 //
14265 14703 // Note that additional search methods are added for the html numbers and
14266 14704 // html formatted numbers by `_addNumericSort()` when we know what the decimal
14267 14705 // place is
14268
14269
14706
14707
14270 14708 $.extend( DataTable.ext.type.search, {
14271 14709 html: function ( data ) {
14272 14710 return _empty(data) ?
14273 14711 data :
14274 14712 typeof data === 'string' ?
14275 14713 data
14276 14714 .replace( _re_new_lines, " " )
14277 14715 .replace( _re_html, "" ) :
14278 14716 '';
14279 14717 },
14280
14718
14281 14719 string: function ( data ) {
14282 14720 return _empty(data) ?
14283 14721 data :
14284 14722 typeof data === 'string' ?
14285 14723 data.replace( _re_new_lines, " " ) :
14286 14724 data;
14287 14725 }
14288 14726 } );
14289
14290
14291
14727
14728
14729
14292 14730 var __numericReplace = function ( d, decimalPlace, re1, re2 ) {
14293 14731 if ( d !== 0 && (!d || d === '-') ) {
14294 14732 return -Infinity;
14295 14733 }
14296
14734
14297 14735 // If a decimal place other than `.` is used, it needs to be given to the
14298 14736 // function so we can detect it and replace with a `.` which is the only
14299 14737 // decimal place Javascript recognises - it is not locale aware.
14300 14738 if ( decimalPlace ) {
14301 14739 d = _numToDecimal( d, decimalPlace );
14302 14740 }
14303
14741
14304 14742 if ( d.replace ) {
14305 14743 if ( re1 ) {
14306 14744 d = d.replace( re1, '' );
14307 14745 }
14308
14746
14309 14747 if ( re2 ) {
14310 14748 d = d.replace( re2, '' );
14311 14749 }
14312 14750 }
14313
14751
14314 14752 return d * 1;
14315 14753 };
14316
14317
14754
14755
14318 14756 // Add the numeric 'deformatting' functions for sorting and search. This is done
14319 14757 // in a function to provide an easy ability for the language options to add
14320 14758 // additional methods if a non-period decimal place is used.
14321 14759 function _addNumericSort ( decimalPlace ) {
14322 14760 $.each(
14323 14761 {
14324 14762 // Plain numbers
14325 14763 "num": function ( d ) {
14326 14764 return __numericReplace( d, decimalPlace );
14327 14765 },
14328
14766
14329 14767 // Formatted numbers
14330 14768 "num-fmt": function ( d ) {
14331 14769 return __numericReplace( d, decimalPlace, _re_formatted_numeric );
14332 14770 },
14333
14771
14334 14772 // HTML numeric
14335 14773 "html-num": function ( d ) {
14336 14774 return __numericReplace( d, decimalPlace, _re_html );
14337 14775 },
14338
14776
14339 14777 // HTML numeric, formatted
14340 14778 "html-num-fmt": function ( d ) {
14341 14779 return __numericReplace( d, decimalPlace, _re_html, _re_formatted_numeric );
14342 14780 }
14343 14781 },
14344 14782 function ( key, fn ) {
14345 14783 // Add the ordering method
14346 14784 _ext.type.order[ key+decimalPlace+'-pre' ] = fn;
14347
14785
14348 14786 // For HTML types add a search formatter that will strip the HTML
14349 14787 if ( key.match(/^html\-/) ) {
14350 14788 _ext.type.search[ key+decimalPlace ] = _ext.type.search.html;
14351 14789 }
14352 14790 }
14353 14791 );
14354 14792 }
14355
14356
14793
14794
14357 14795 // Default sort methods
14358 14796 $.extend( _ext.type.order, {
14359 14797 // Dates
14360 14798 "date-pre": function ( d ) {
14361 return Date.parse( d ) || 0;
14799 return Date.parse( d ) || -Infinity;
14362 14800 },
14363
14801
14364 14802 // html
14365 14803 "html-pre": function ( a ) {
14366 14804 return _empty(a) ?
14367 14805 '' :
14368 14806 a.replace ?
14369 14807 a.replace( /<.*?>/g, "" ).toLowerCase() :
14370 14808 a+'';
14371 14809 },
14372
14810
14373 14811 // string
14374 14812 "string-pre": function ( a ) {
14375 14813 // This is a little complex, but faster than always calling toString,
14376 14814 // http://jsperf.com/tostring-v-check
14377 14815 return _empty(a) ?
14378 14816 '' :
14379 14817 typeof a === 'string' ?
14380 14818 a.toLowerCase() :
14381 14819 ! a.toString ?
14382 14820 '' :
14383 14821 a.toString();
14384 14822 },
14385
14823
14386 14824 // string-asc and -desc are retained only for compatibility with the old
14387 14825 // sort methods
14388 14826 "string-asc": function ( x, y ) {
14389 14827 return ((x < y) ? -1 : ((x > y) ? 1 : 0));
14390 14828 },
14391
14829
14392 14830 "string-desc": function ( x, y ) {
14393 14831 return ((x < y) ? 1 : ((x > y) ? -1 : 0));
14394 14832 }
14395 14833 } );
14396
14397
14834
14835
14398 14836 // Numeric sorting types - order doesn't matter here
14399 14837 _addNumericSort( '' );
14400
14401
14838
14839
14402 14840 $.extend( true, DataTable.ext.renderer, {
14403 14841 header: {
14404 14842 _: function ( settings, cell, column, classes ) {
14405 14843 // No additional mark-up required
14406 14844 // Attach a sort listener to update on sort - note that using the
14407 14845 // `DT` namespace will allow the event to be removed automatically
14408 14846 // on destroy, while the `dt` namespaced event is the one we are
14409 14847 // listening for
14410 14848 $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {
14411 14849 if ( settings !== ctx ) { // need to check this this is the host
14412 14850 return; // table, not a nested one
14413 14851 }
14414
14852
14415 14853 var colIdx = column.idx;
14416
14854
14417 14855 cell
14418 14856 .removeClass(
14419 14857 column.sSortingClass +' '+
14420 14858 classes.sSortAsc +' '+
14421 14859 classes.sSortDesc
14422 14860 )
14423 14861 .addClass( columns[ colIdx ] == 'asc' ?
14424 14862 classes.sSortAsc : columns[ colIdx ] == 'desc' ?
14425 14863 classes.sSortDesc :
14426 14864 column.sSortingClass
14427 14865 );
14428 14866 } );
14429 14867 },
14430
14868
14431 14869 jqueryui: function ( settings, cell, column, classes ) {
14432 14870 $('<div/>')
14433 14871 .addClass( classes.sSortJUIWrapper )
14434 14872 .append( cell.contents() )
14435 14873 .append( $('<span/>')
14436 14874 .addClass( classes.sSortIcon+' '+column.sSortingClassJUI )
14437 14875 )
14438 14876 .appendTo( cell );
14439
14877
14440 14878 // Attach a sort listener to update on sort
14441 14879 $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {
14442 14880 if ( settings !== ctx ) {
14443 14881 return;
14444 14882 }
14445
14883
14446 14884 var colIdx = column.idx;
14447
14885
14448 14886 cell
14449 14887 .removeClass( classes.sSortAsc +" "+classes.sSortDesc )
14450 14888 .addClass( columns[ colIdx ] == 'asc' ?
14451 14889 classes.sSortAsc : columns[ colIdx ] == 'desc' ?
14452 14890 classes.sSortDesc :
14453 14891 column.sSortingClass
14454 14892 );
14455
14893
14456 14894 cell
14457 14895 .find( 'span.'+classes.sSortIcon )
14458 14896 .removeClass(
14459 14897 classes.sSortJUIAsc +" "+
14460 14898 classes.sSortJUIDesc +" "+
14461 14899 classes.sSortJUI +" "+
14462 14900 classes.sSortJUIAscAllowed +" "+
14463 14901 classes.sSortJUIDescAllowed
14464 14902 )
14465 14903 .addClass( columns[ colIdx ] == 'asc' ?
14466 14904 classes.sSortJUIAsc : columns[ colIdx ] == 'desc' ?
14467 14905 classes.sSortJUIDesc :
14468 14906 column.sSortingClassJUI
14469 14907 );
14470 14908 } );
14471 14909 }
14472 14910 }
14473 14911 } );
14474
14912
14475 14913 /*
14476 14914 * Public helper functions. These aren't used internally by DataTables, or
14477 14915 * called by any of the options passed into DataTables, but they can be used
14478 14916 * externally by developers working with DataTables. They are helper functions
14479 14917 * to make working with DataTables a little bit easier.
14480 14918 */
14481
14919
14920 var __htmlEscapeEntities = function ( d ) {
14921 return typeof d === 'string' ?
14922 d.replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;') :
14923 d;
14924 };
14925
14482 14926 /**
14483 14927 * Helpers for `columns.render`.
14484 14928 *
14485 14929 * The options defined here can be used with the `columns.render` initialisation
14486 14930 * option to provide a display renderer. The following functions are defined:
14487 14931 *
14488 14932 * * `number` - Will format numeric data (defined by `columns.data`) for
14489 14933 * display, retaining the original unformatted data for sorting and filtering.
14490 * It takes 4 parameters:
14934 * It takes 5 parameters:
14491 14935 * * `string` - Thousands grouping separator
14492 14936 * * `string` - Decimal point indicator
14493 14937 * * `integer` - Number of decimal points to show
14494 14938 * * `string` (optional) - Prefix.
14939 * * `string` (optional) - Postfix (/suffix).
14940 * * `text` - Escape HTML to help prevent XSS attacks. It has no optional
14941 * parameters.
14495 14942 *
14496 14943 * @example
14497 14944 * // Column definition using the number renderer
14498 14945 * {
14499 14946 * data: "salary",
14500 14947 * render: $.fn.dataTable.render.number( '\'', '.', 0, '$' )
14501 14948 * }
14502 14949 *
14503 14950 * @namespace
14504 14951 */
14505 14952 DataTable.render = {
14506 number: function ( thousands, decimal, precision, prefix ) {
14953 number: function ( thousands, decimal, precision, prefix, postfix ) {
14507 14954 return {
14508 14955 display: function ( d ) {
14956 if ( typeof d !== 'number' && typeof d !== 'string' ) {
14957 return d;
14958 }
14959
14509 14960 var negative = d < 0 ? '-' : '';
14510 d = Math.abs( parseFloat( d ) );
14511
14961 var flo = parseFloat( d );
14962
14963 // If NaN then there isn't much formatting that we can do - just
14964 // return immediately, escaping any HTML (this was supposed to
14965 // be a number after all)
14966 if ( isNaN( flo ) ) {
14967 return __htmlEscapeEntities( d );
14968 }
14969
14970 flo = flo.toFixed( precision );
14971 d = Math.abs( flo );
14972
14512 14973 var intPart = parseInt( d, 10 );
14513 14974 var floatPart = precision ?
14514 14975 decimal+(d - intPart).toFixed( precision ).substring( 2 ):
14515 14976 '';
14516
14977
14517 14978 return negative + (prefix||'') +
14518 14979 intPart.toString().replace(
14519 14980 /\B(?=(\d{3})+(?!\d))/g, thousands
14520 14981 ) +
14521 floatPart;
14522 }
14982 floatPart +
14983 (postfix||'');
14984 }
14985 };
14986 },
14987
14988 text: function () {
14989 return {
14990 display: __htmlEscapeEntities
14523 14991 };
14524 14992 }
14525 14993 };
14526
14527
14994
14995
14528 14996 /*
14529 14997 * This is really a good bit rubbish this method of exposing the internal methods
14530 14998 * publicly... - To be fixed in 2.0 using methods on the prototype
14531 14999 */
14532
14533
15000
15001
14534 15002 /**
14535 15003 * Create a wrapper function for exporting an internal functions to an external API.
14536 15004 * @param {string} fn API function name
14537 15005 * @returns {function} wrapped function
14538 15006 * @memberof DataTable#internal
14539 15007 */
14540 15008 function _fnExternApiFunc (fn)
14541 15009 {
14542 15010 return function() {
14543 15011 var args = [_fnSettingsFromNode( this[DataTable.ext.iApiIndex] )].concat(
14544 15012 Array.prototype.slice.call(arguments)
14545 15013 );
14546 15014 return DataTable.ext.internal[fn].apply( this, args );
14547 15015 };
14548 15016 }
14549
14550
15017
15018
14551 15019 /**
14552 15020 * Reference to internal functions for use by plug-in developers. Note that
14553 15021 * these methods are references to internal functions and are considered to be
14554 15022 * private. If you use these methods, be aware that they are liable to change
14555 15023 * between versions.
14556 15024 * @namespace
14557 15025 */
14558 15026 $.extend( DataTable.ext.internal, {
14559 15027 _fnExternApiFunc: _fnExternApiFunc,
14560 15028 _fnBuildAjax: _fnBuildAjax,
14561 15029 _fnAjaxUpdate: _fnAjaxUpdate,
14562 15030 _fnAjaxParameters: _fnAjaxParameters,
14563 15031 _fnAjaxUpdateDraw: _fnAjaxUpdateDraw,
14564 15032 _fnAjaxDataSrc: _fnAjaxDataSrc,
14565 15033 _fnAddColumn: _fnAddColumn,
14566 15034 _fnColumnOptions: _fnColumnOptions,
14567 15035 _fnAdjustColumnSizing: _fnAdjustColumnSizing,
14568 15036 _fnVisibleToColumnIndex: _fnVisibleToColumnIndex,
14569 15037 _fnColumnIndexToVisible: _fnColumnIndexToVisible,
14570 15038 _fnVisbleColumns: _fnVisbleColumns,
14571 15039 _fnGetColumns: _fnGetColumns,
14572 15040 _fnColumnTypes: _fnColumnTypes,
14573 15041 _fnApplyColumnDefs: _fnApplyColumnDefs,
14574 15042 _fnHungarianMap: _fnHungarianMap,
14575 15043 _fnCamelToHungarian: _fnCamelToHungarian,
14576 15044 _fnLanguageCompat: _fnLanguageCompat,
14577 15045 _fnBrowserDetect: _fnBrowserDetect,
14578 15046 _fnAddData: _fnAddData,
14579 15047 _fnAddTr: _fnAddTr,
14580 15048 _fnNodeToDataIndex: _fnNodeToDataIndex,
14581 15049 _fnNodeToColumnIndex: _fnNodeToColumnIndex,
14582 15050 _fnGetCellData: _fnGetCellData,
14583 15051 _fnSetCellData: _fnSetCellData,
14584 15052 _fnSplitObjNotation: _fnSplitObjNotation,
14585 15053 _fnGetObjectDataFn: _fnGetObjectDataFn,
14586 15054 _fnSetObjectDataFn: _fnSetObjectDataFn,
14587 15055 _fnGetDataMaster: _fnGetDataMaster,
14588 15056 _fnClearTable: _fnClearTable,
14589 15057 _fnDeleteIndex: _fnDeleteIndex,
14590 15058 _fnInvalidate: _fnInvalidate,
14591 15059 _fnGetRowElements: _fnGetRowElements,
14592 15060 _fnCreateTr: _fnCreateTr,
14593 15061 _fnBuildHead: _fnBuildHead,
14594 15062 _fnDrawHead: _fnDrawHead,
14595 15063 _fnDraw: _fnDraw,
14596 15064 _fnReDraw: _fnReDraw,
14597 15065 _fnAddOptionsHtml: _fnAddOptionsHtml,
14598 15066 _fnDetectHeader: _fnDetectHeader,
14599 15067 _fnGetUniqueThs: _fnGetUniqueThs,
14600 15068 _fnFeatureHtmlFilter: _fnFeatureHtmlFilter,
14601 15069 _fnFilterComplete: _fnFilterComplete,
14602 15070 _fnFilterCustom: _fnFilterCustom,
14603 15071 _fnFilterColumn: _fnFilterColumn,
14604 15072 _fnFilter: _fnFilter,
14605 15073 _fnFilterCreateSearch: _fnFilterCreateSearch,
14606 15074 _fnEscapeRegex: _fnEscapeRegex,
14607 15075 _fnFilterData: _fnFilterData,
14608 15076 _fnFeatureHtmlInfo: _fnFeatureHtmlInfo,
14609 15077 _fnUpdateInfo: _fnUpdateInfo,
14610 15078 _fnInfoMacros: _fnInfoMacros,
14611 15079 _fnInitialise: _fnInitialise,
14612 15080 _fnInitComplete: _fnInitComplete,
14613 15081 _fnLengthChange: _fnLengthChange,
14614 15082 _fnFeatureHtmlLength: _fnFeatureHtmlLength,
14615 15083 _fnFeatureHtmlPaginate: _fnFeatureHtmlPaginate,
14616 15084 _fnPageChange: _fnPageChange,
14617 15085 _fnFeatureHtmlProcessing: _fnFeatureHtmlProcessing,
14618 15086 _fnProcessingDisplay: _fnProcessingDisplay,
14619 15087 _fnFeatureHtmlTable: _fnFeatureHtmlTable,
14620 15088 _fnScrollDraw: _fnScrollDraw,
14621 15089 _fnApplyToChildren: _fnApplyToChildren,
14622 15090 _fnCalculateColumnWidths: _fnCalculateColumnWidths,
14623 15091 _fnThrottle: _fnThrottle,
14624 15092 _fnConvertToWidth: _fnConvertToWidth,
14625 _fnScrollingWidthAdjust: _fnScrollingWidthAdjust,
14626 15093 _fnGetWidestNode: _fnGetWidestNode,
14627 15094 _fnGetMaxLenString: _fnGetMaxLenString,
14628 15095 _fnStringToCss: _fnStringToCss,
14629 _fnScrollBarWidth: _fnScrollBarWidth,
14630 15096 _fnSortFlatten: _fnSortFlatten,
14631 15097 _fnSort: _fnSort,
14632 15098 _fnSortAria: _fnSortAria,
14633 15099 _fnSortListener: _fnSortListener,
14634 15100 _fnSortAttachListener: _fnSortAttachListener,
14635 15101 _fnSortingClasses: _fnSortingClasses,
14636 15102 _fnSortData: _fnSortData,
14637 15103 _fnSaveState: _fnSaveState,
14638 15104 _fnLoadState: _fnLoadState,
14639 15105 _fnSettingsFromNode: _fnSettingsFromNode,
14640 15106 _fnLog: _fnLog,
14641 15107 _fnMap: _fnMap,
14642 15108 _fnBindAction: _fnBindAction,
14643 15109 _fnCallbackReg: _fnCallbackReg,
14644 15110 _fnCallbackFire: _fnCallbackFire,
14645 15111 _fnLengthOverflow: _fnLengthOverflow,
14646 15112 _fnRenderer: _fnRenderer,
14647 15113 _fnDataSource: _fnDataSource,
14648 15114 _fnRowAttributes: _fnRowAttributes,
14649 15115 _fnCalculateEnd: function () {} // Used by a lot of plug-ins, but redundant
14650 15116 // in 1.10, so this dead-end function is
14651 15117 // added to prevent errors
14652 15118 } );
14653
15119
14654 15120
14655 15121 // jQuery access
14656 15122 $.fn.dataTable = DataTable;
14657 15123
15124 // Provide access to the host jQuery object (circular reference)
15125 DataTable.$ = $;
15126
14658 15127 // Legacy aliases
14659 15128 $.fn.dataTableSettings = DataTable.settings;
14660 15129 $.fn.dataTableExt = DataTable.ext;
14661 15130
14662 15131 // With a capital `D` we return a DataTables API instance rather than a
14663 15132 // jQuery object
14664 15133 $.fn.DataTable = function ( opts ) {
14665 15134 return $(this).dataTable( opts ).api();
14666 15135 };
14667 15136
14668 15137 // All properties that are available to $.fn.dataTable should also be
14669 15138 // available on $.fn.DataTable
14670 15139 $.each( DataTable, function ( prop, val ) {
14671 15140 $.fn.DataTable[ prop ] = val;
14672 15141 } );
14673 15142
14674 15143
14675 15144 // Information about events fired by DataTables - for documentation.
14676 15145 /**
14677 15146 * Draw event, fired whenever the table is redrawn on the page, at the same
14678 15147 * point as fnDrawCallback. This may be useful for binding events or
14679 15148 * performing calculations when the table is altered at all.
14680 15149 * @name DataTable#draw.dt
14681 15150 * @event
14682 15151 * @param {event} e jQuery event object
14683 15152 * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
14684 15153 */
14685 15154
14686 15155 /**
14687 15156 * Search event, fired when the searching applied to the table (using the
14688 15157 * built-in global search, or column filters) is altered.
14689 15158 * @name DataTable#search.dt
14690 15159 * @event
14691 15160 * @param {event} e jQuery event object
14692 15161 * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
14693 15162 */
14694 15163
14695 15164 /**
14696 15165 * Page change event, fired when the paging of the table is altered.
14697 15166 * @name DataTable#page.dt
14698 15167 * @event
14699 15168 * @param {event} e jQuery event object
14700 15169 * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
14701 15170 */
14702 15171
14703 15172 /**
14704 15173 * Order event, fired when the ordering applied to the table is altered.
14705 15174 * @name DataTable#order.dt
14706 15175 * @event
14707 15176 * @param {event} e jQuery event object
14708 15177 * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
14709 15178 */
14710 15179
14711 15180 /**
14712 15181 * DataTables initialisation complete event, fired when the table is fully
14713 15182 * drawn, including Ajax data loaded, if Ajax data is required.
14714 15183 * @name DataTable#init.dt
14715 15184 * @event
14716 15185 * @param {event} e jQuery event object
14717 15186 * @param {object} oSettings DataTables settings object
14718 15187 * @param {object} json The JSON object request from the server - only
14719 15188 * present if client-side Ajax sourced data is used</li></ol>
14720 15189 */
14721 15190
14722 15191 /**
14723 15192 * State save event, fired when the table has changed state a new state save
14724 15193 * is required. This event allows modification of the state saving object
14725 15194 * prior to actually doing the save, including addition or other state
14726 15195 * properties (for plug-ins) or modification of a DataTables core property.
14727 15196 * @name DataTable#stateSaveParams.dt
14728 15197 * @event
14729 15198 * @param {event} e jQuery event object
14730 15199 * @param {object} oSettings DataTables settings object
14731 15200 * @param {object} json The state information to be saved
14732 15201 */
14733 15202
14734 15203 /**
14735 15204 * State load event, fired when the table is loading state from the stored
14736 15205 * data, but prior to the settings object being modified by the saved state
14737 15206 * - allowing modification of the saved state is required or loading of
14738 15207 * state for a plug-in.
14739 15208 * @name DataTable#stateLoadParams.dt
14740 15209 * @event
14741 15210 * @param {event} e jQuery event object
14742 15211 * @param {object} oSettings DataTables settings object
14743 15212 * @param {object} json The saved state information
14744 15213 */
14745 15214
14746 15215 /**
14747 15216 * State loaded event, fired when state has been loaded from stored data and
14748 15217 * the settings object has been modified by the loaded data.
14749 15218 * @name DataTable#stateLoaded.dt
14750 15219 * @event
14751 15220 * @param {event} e jQuery event object
14752 15221 * @param {object} oSettings DataTables settings object
14753 15222 * @param {object} json The saved state information
14754 15223 */
14755 15224
14756 15225 /**
14757 15226 * Processing event, fired when DataTables is doing some kind of processing
14758 15227 * (be it, order, searcg or anything else). It can be used to indicate to
14759 15228 * the end user that there is something happening, or that something has
14760 15229 * finished.
14761 15230 * @name DataTable#processing.dt
14762 15231 * @event
14763 15232 * @param {event} e jQuery event object
14764 15233 * @param {object} oSettings DataTables settings object
14765 15234 * @param {boolean} bShow Flag for if DataTables is doing processing or not
14766 15235 */
14767 15236
14768 15237 /**
14769 15238 * Ajax (XHR) event, fired whenever an Ajax request is completed from a
14770 15239 * request to made to the server for new data. This event is called before
14771 15240 * DataTables processed the returned data, so it can also be used to pre-
14772 15241 * process the data returned from the server, if needed.
14773 15242 *
14774 15243 * Note that this trigger is called in `fnServerData`, if you override
14775 15244 * `fnServerData` and which to use this event, you need to trigger it in you
14776 15245 * success function.
14777 15246 * @name DataTable#xhr.dt
14778 15247 * @event
14779 15248 * @param {event} e jQuery event object
14780 15249 * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
14781 15250 * @param {object} json JSON returned from the server
14782 15251 *
14783 15252 * @example
14784 15253 * // Use a custom property returned from the server in another DOM element
14785 15254 * $('#table').dataTable().on('xhr.dt', function (e, settings, json) {
14786 15255 * $('#status').html( json.status );
14787 15256 * } );
14788 15257 *
14789 15258 * @example
14790 15259 * // Pre-process the data returned from the server
14791 15260 * $('#table').dataTable().on('xhr.dt', function (e, settings, json) {
14792 15261 * for ( var i=0, ien=json.aaData.length ; i<ien ; i++ ) {
14793 15262 * json.aaData[i].sum = json.aaData[i].one + json.aaData[i].two;
14794 15263 * }
14795 15264 * // Note no return - manipulate the data directly in the JSON object.
14796 15265 * } );
14797 15266 */
14798 15267
14799 15268 /**
14800 15269 * Destroy event, fired when the DataTable is destroyed by calling fnDestroy
14801 15270 * or passing the bDestroy:true parameter in the initialisation object. This
14802 15271 * can be used to remove bound events, added DOM nodes, etc.
14803 15272 * @name DataTable#destroy.dt
14804 15273 * @event
14805 15274 * @param {event} e jQuery event object
14806 15275 * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
14807 15276 */
14808 15277
14809 15278 /**
14810 15279 * Page length change event, fired when number of records to show on each
14811 15280 * page (the length) is changed.
14812 15281 * @name DataTable#length.dt
14813 15282 * @event
14814 15283 * @param {event} e jQuery event object
14815 15284 * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
14816 15285 * @param {integer} len New length
14817 15286 */
14818 15287
14819 15288 /**
14820 15289 * Column sizing has changed.
14821 15290 * @name DataTable#column-sizing.dt
14822 15291 * @event
14823 15292 * @param {event} e jQuery event object
14824 15293 * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
14825 15294 */
14826 15295
14827 15296 /**
14828 15297 * Column visibility has changed.
14829 15298 * @name DataTable#column-visibility.dt
14830 15299 * @event
14831 15300 * @param {event} e jQuery event object
14832 15301 * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
14833 15302 * @param {int} column Column index
14834 15303 * @param {bool} vis `false` if column now hidden, or `true` if visible
14835 15304 */
14836 15305
14837 15306 return $.fn.dataTable;
14838 }));
14839
14840 }(window, document));
14841
15307 })); No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now