##// 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,15 +1,15 b''
1 /*! DataTables 1.10.4
1 /*! DataTables 1.10.13
2 * ©2008-2014 SpryMedia Ltd - datatables.net/license
2 * ©2008-2016 SpryMedia Ltd - datatables.net/license
3 */
3 */
4
4
5 /**
5 /**
6 * @summary DataTables
6 * @summary DataTables
7 * @description Paginate, search and order HTML tables
7 * @description Paginate, search and order HTML tables
8 * @version 1.10.4
8 * @version 1.10.13
9 * @file jquery.dataTables.js
9 * @file jquery.dataTables.js
10 * @author SpryMedia Ltd (www.sprymedia.co.uk)
10 * @author SpryMedia Ltd
11 * @contact www.sprymedia.co.uk/contact
11 * @contact www.datatables.net
12 * @copyright Copyright 2008-2014 SpryMedia Ltd.
12 * @copyright Copyright 2008-2016 SpryMedia Ltd.
13 *
13 *
14 * This source file is free software, available under the following license:
14 * This source file is free software, available under the following license:
15 * MIT license - http://datatables.net/license
15 * MIT license - http://datatables.net/license
@@ -22,28 +22,41 b''
22 */
22 */
23
23
24 /*jslint evil: true, undef: true, browser: true */
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*/
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*/
26
27 (/** @lends <global> */function( window, document, undefined ) {
28
26
29 (function( factory ) {
27 (function( factory ) {
30 "use strict";
28 "use strict";
31
29
32 if ( typeof define === 'function' && define.amd ) {
30 if ( typeof define === 'function' && define.amd ) {
33 // Define as an AMD module if possible
31 // AMD
34 define( 'datatables', ['jquery'], factory );
32 define( ['jquery'], function ( $ ) {
35 }
33 return factory( $, window, document );
36 else if ( typeof exports === 'object' ) {
34 } );
37 // Node/CommonJS
35 }
38 factory( require( 'jquery' ) );
36 else if ( typeof exports === 'object' ) {
39 }
37 // CommonJS
40 else if ( jQuery && !jQuery.fn.dataTable ) {
38 module.exports = function (root, $) {
41 // Define using browser globals otherwise
39 if ( ! root ) {
42 // Prevent multiple instantiations if the script is loaded twice
40 // CommonJS environments without a window global must pass a
43 factory( jQuery );
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 "use strict";
60 "use strict";
48
61
49 /**
62 /**
@@ -78,5192 +91,7 b''
78 * } );
91 * } );
79 * } );
92 * } );
80 */
93 */
81 var DataTable;
94 var DataTable = function ( options )
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 )
5267 {
95 {
5268 /**
96 /**
5269 * Perform a jQuery selector action on the table's TR elements (from the tbody) and
97 * Perform a jQuery selector action on the table's TR elements (from the tbody) and
@@ -5305,8 +133,8 b''
5305 {
133 {
5306 return this.api(true).$( sSelector, oOpts );
134 return this.api(true).$( sSelector, oOpts );
5307 };
135 };
5308
136
5309
137
5310 /**
138 /**
5311 * Almost identical to $ in operation, but in this case returns the data for the matched
139 * Almost identical to $ in operation, but in this case returns the data for the matched
5312 * rows - as such, the jQuery selector used should match TR row nodes or TD/TH cell nodes
140 * rows - as such, the jQuery selector used should match TR row nodes or TD/TH cell nodes
@@ -5359,8 +187,8 b''
5359 {
187 {
5360 return this.api(true).rows( sSelector, oOpts ).data();
188 return this.api(true).rows( sSelector, oOpts ).data();
5361 };
189 };
5362
190
5363
191
5364 /**
192 /**
5365 * Create a DataTables Api instance, with the currently selected tables for
193 * Create a DataTables Api instance, with the currently selected tables for
5366 * the Api's context.
194 * the Api's context.
@@ -5378,8 +206,8 b''
5378 ) :
206 ) :
5379 new _Api( this );
207 new _Api( this );
5380 };
208 };
5381
209
5382
210
5383 /**
211 /**
5384 * Add a single new row or multiple rows of data to the table. Please note
212 * Add a single new row or multiple rows of data to the table. Please note
5385 * that this is suitable for client-side processing only - if you are using
213 * that this is suitable for client-side processing only - if you are using
@@ -5421,20 +249,20 b''
5421 this.fnAddData = function( data, redraw )
249 this.fnAddData = function( data, redraw )
5422 {
250 {
5423 var api = this.api( true );
251 var api = this.api( true );
5424
252
5425 /* Check if we want to add multiple rows or not */
253 /* Check if we want to add multiple rows or not */
5426 var rows = $.isArray(data) && ( $.isArray(data[0]) || $.isPlainObject(data[0]) ) ?
254 var rows = $.isArray(data) && ( $.isArray(data[0]) || $.isPlainObject(data[0]) ) ?
5427 api.rows.add( data ) :
255 api.rows.add( data ) :
5428 api.row.add( data );
256 api.row.add( data );
5429
257
5430 if ( redraw === undefined || redraw ) {
258 if ( redraw === undefined || redraw ) {
5431 api.draw();
259 api.draw();
5432 }
260 }
5433
261
5434 return rows.flatten().toArray();
262 return rows.flatten().toArray();
5435 };
263 };
5436
264
5437
265
5438 /**
266 /**
5439 * This function will make DataTables recalculate the column sizes, based on the data
267 * This function will make DataTables recalculate the column sizes, based on the data
5440 * contained in the table and the sizes applied to the columns (in the DOM, CSS or
268 * contained in the table and the sizes applied to the columns (in the DOM, CSS or
@@ -5451,7 +279,7 b''
5451 * "bPaginate": false
279 * "bPaginate": false
5452 * } );
280 * } );
5453 *
281 *
5454 * $(window).bind('resize', function () {
282 * $(window).on('resize', function () {
5455 * oTable.fnAdjustColumnSizing();
283 * oTable.fnAdjustColumnSizing();
5456 * } );
284 * } );
5457 * } );
285 * } );
@@ -5461,7 +289,7 b''
5461 var api = this.api( true ).columns.adjust();
289 var api = this.api( true ).columns.adjust();
5462 var settings = api.settings()[0];
290 var settings = api.settings()[0];
5463 var scroll = settings.oScroll;
291 var scroll = settings.oScroll;
5464
292
5465 if ( bRedraw === undefined || bRedraw ) {
293 if ( bRedraw === undefined || bRedraw ) {
5466 api.draw( false );
294 api.draw( false );
5467 }
295 }
@@ -5470,8 +298,8 b''
5470 _fnScrollDraw( settings );
298 _fnScrollDraw( settings );
5471 }
299 }
5472 };
300 };
5473
301
5474
302
5475 /**
303 /**
5476 * Quickly and simply clear a table
304 * Quickly and simply clear a table
5477 * @param {bool} [bRedraw=true] redraw the table or not
305 * @param {bool} [bRedraw=true] redraw the table or not
@@ -5489,13 +317,13 b''
5489 this.fnClearTable = function( bRedraw )
317 this.fnClearTable = function( bRedraw )
5490 {
318 {
5491 var api = this.api( true ).clear();
319 var api = this.api( true ).clear();
5492
320
5493 if ( bRedraw === undefined || bRedraw ) {
321 if ( bRedraw === undefined || bRedraw ) {
5494 api.draw();
322 api.draw();
5495 }
323 }
5496 };
324 };
5497
325
5498
326
5499 /**
327 /**
5500 * The exact opposite of 'opening' a row, this function will close any rows which
328 * The exact opposite of 'opening' a row, this function will close any rows which
5501 * are currently 'open'.
329 * are currently 'open'.
@@ -5524,8 +352,8 b''
5524 {
352 {
5525 this.api( true ).row( nTr ).child.hide();
353 this.api( true ).row( nTr ).child.hide();
5526 };
354 };
5527
355
5528
356
5529 /**
357 /**
5530 * Remove a row for the table
358 * Remove a row for the table
5531 * @param {mixed} target The index of the row from aoData to be deleted, or
359 * @param {mixed} target The index of the row from aoData to be deleted, or
@@ -5550,21 +378,21 b''
5550 var rows = api.rows( target );
378 var rows = api.rows( target );
5551 var settings = rows.settings()[0];
379 var settings = rows.settings()[0];
5552 var data = settings.aoData[ rows[0][0] ];
380 var data = settings.aoData[ rows[0][0] ];
5553
381
5554 rows.remove();
382 rows.remove();
5555
383
5556 if ( callback ) {
384 if ( callback ) {
5557 callback.call( this, settings, data );
385 callback.call( this, settings, data );
5558 }
386 }
5559
387
5560 if ( redraw === undefined || redraw ) {
388 if ( redraw === undefined || redraw ) {
5561 api.draw();
389 api.draw();
5562 }
390 }
5563
391
5564 return data;
392 return data;
5565 };
393 };
5566
394
5567
395
5568 /**
396 /**
5569 * Restore the table to it's original state in the DOM by removing all of DataTables
397 * Restore the table to it's original state in the DOM by removing all of DataTables
5570 * enhancements, alterations to the DOM structure of the table and event listeners.
398 * enhancements, alterations to the DOM structure of the table and event listeners.
@@ -5583,8 +411,8 b''
5583 {
411 {
5584 this.api( true ).destroy( remove );
412 this.api( true ).destroy( remove );
5585 };
413 };
5586
414
5587
415
5588 /**
416 /**
5589 * Redraw the table
417 * Redraw the table
5590 * @param {bool} [complete=true] Re-filter and resort (if enabled) the table before the draw.
418 * @param {bool} [complete=true] Re-filter and resort (if enabled) the table before the draw.
@@ -5602,11 +430,11 b''
5602 this.fnDraw = function( complete )
430 this.fnDraw = function( complete )
5603 {
431 {
5604 // Note that this isn't an exact match to the old call to _fnDraw - it takes
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.
433 // into account the new data, but can hold position.
5606 this.api( true ).draw( ! complete );
434 this.api( true ).draw( complete );
5607 };
435 };
5608
436
5609
437
5610 /**
438 /**
5611 * Filter the input based on data
439 * Filter the input based on data
5612 * @param {string} sInput String to filter the table on
440 * @param {string} sInput String to filter the table on
@@ -5629,18 +457,18 b''
5629 this.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal, bCaseInsensitive )
457 this.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal, bCaseInsensitive )
5630 {
458 {
5631 var api = this.api( true );
459 var api = this.api( true );
5632
460
5633 if ( iColumn === null || iColumn === undefined ) {
461 if ( iColumn === null || iColumn === undefined ) {
5634 api.search( sInput, bRegex, bSmart, bCaseInsensitive );
462 api.search( sInput, bRegex, bSmart, bCaseInsensitive );
5635 }
463 }
5636 else {
464 else {
5637 api.column( iColumn ).search( sInput, bRegex, bSmart, bCaseInsensitive );
465 api.column( iColumn ).search( sInput, bRegex, bSmart, bCaseInsensitive );
5638 }
466 }
5639
467
5640 api.draw();
468 api.draw();
5641 };
469 };
5642
470
5643
471
5644 /**
472 /**
5645 * Get the data for the whole table, an individual row or an individual cell based on the
473 * Get the data for the whole table, an individual row or an individual cell based on the
5646 * provided parameters.
474 * provided parameters.
@@ -5681,19 +509,19 b''
5681 this.fnGetData = function( src, col )
509 this.fnGetData = function( src, col )
5682 {
510 {
5683 var api = this.api( true );
511 var api = this.api( true );
5684
512
5685 if ( src !== undefined ) {
513 if ( src !== undefined ) {
5686 var type = src.nodeName ? src.nodeName.toLowerCase() : '';
514 var type = src.nodeName ? src.nodeName.toLowerCase() : '';
5687
515
5688 return col !== undefined || type == 'td' || type == 'th' ?
516 return col !== undefined || type == 'td' || type == 'th' ?
5689 api.cell( src, col ).data() :
517 api.cell( src, col ).data() :
5690 api.row( src ).data() || null;
518 api.row( src ).data() || null;
5691 }
519 }
5692
520
5693 return api.data().toArray();
521 return api.data().toArray();
5694 };
522 };
5695
523
5696
524
5697 /**
525 /**
5698 * Get an array of the TR nodes that are used in the table's body. Note that you will
526 * Get an array of the TR nodes that are used in the table's body. Note that you will
5699 * typically want to use the '$' API method in preference to this as it is more
527 * typically want to use the '$' API method in preference to this as it is more
@@ -5715,13 +543,13 b''
5715 this.fnGetNodes = function( iRow )
543 this.fnGetNodes = function( iRow )
5716 {
544 {
5717 var api = this.api( true );
545 var api = this.api( true );
5718
546
5719 return iRow !== undefined ?
547 return iRow !== undefined ?
5720 api.row( iRow ).node() :
548 api.row( iRow ).node() :
5721 api.rows().nodes().flatten().toArray();
549 api.rows().nodes().flatten().toArray();
5722 };
550 };
5723
551
5724
552
5725 /**
553 /**
5726 * Get the array indexes of a particular cell from it's DOM element
554 * Get the array indexes of a particular cell from it's DOM element
5727 * and column index including hidden columns
555 * and column index including hidden columns
@@ -5754,13 +582,13 b''
5754 {
582 {
5755 var api = this.api( true );
583 var api = this.api( true );
5756 var nodeName = node.nodeName.toUpperCase();
584 var nodeName = node.nodeName.toUpperCase();
5757
585
5758 if ( nodeName == 'TR' ) {
586 if ( nodeName == 'TR' ) {
5759 return api.row( node ).index();
587 return api.row( node ).index();
5760 }
588 }
5761 else if ( nodeName == 'TD' || nodeName == 'TH' ) {
589 else if ( nodeName == 'TD' || nodeName == 'TH' ) {
5762 var cell = api.cell( node ).index();
590 var cell = api.cell( node ).index();
5763
591
5764 return [
592 return [
5765 cell.row,
593 cell.row,
5766 cell.columnVisible,
594 cell.columnVisible,
@@ -5769,8 +597,8 b''
5769 }
597 }
5770 return null;
598 return null;
5771 };
599 };
5772
600
5773
601
5774 /**
602 /**
5775 * Check to see if a row is 'open' or not.
603 * Check to see if a row is 'open' or not.
5776 * @param {node} nTr the table row to check
604 * @param {node} nTr the table row to check
@@ -5798,8 +626,8 b''
5798 {
626 {
5799 return this.api( true ).row( nTr ).child.isShown();
627 return this.api( true ).row( nTr ).child.isShown();
5800 };
628 };
5801
629
5802
630
5803 /**
631 /**
5804 * This function will place a new row directly after a row which is currently
632 * This function will place a new row directly after a row which is currently
5805 * on display on the page, with the HTML contents that is passed into the
633 * on display on the page, with the HTML contents that is passed into the
@@ -5838,8 +666,8 b''
5838 .show()
666 .show()
5839 .child()[0];
667 .child()[0];
5840 };
668 };
5841
669
5842
670
5843 /**
671 /**
5844 * Change the pagination - provides the internal logic for pagination in a simple API
672 * Change the pagination - provides the internal logic for pagination in a simple API
5845 * function. With this function you can have a DataTables table go to the next,
673 * function. With this function you can have a DataTables table go to the next,
@@ -5859,13 +687,13 b''
5859 this.fnPageChange = function ( mAction, bRedraw )
687 this.fnPageChange = function ( mAction, bRedraw )
5860 {
688 {
5861 var api = this.api( true ).page( mAction );
689 var api = this.api( true ).page( mAction );
5862
690
5863 if ( bRedraw === undefined || bRedraw ) {
691 if ( bRedraw === undefined || bRedraw ) {
5864 api.draw(false);
692 api.draw(false);
5865 }
693 }
5866 };
694 };
5867
695
5868
696
5869 /**
697 /**
5870 * Show a particular column
698 * Show a particular column
5871 * @param {int} iCol The column whose display should be changed
699 * @param {int} iCol The column whose display should be changed
@@ -5885,13 +713,13 b''
5885 this.fnSetColumnVis = function ( iCol, bShow, bRedraw )
713 this.fnSetColumnVis = function ( iCol, bShow, bRedraw )
5886 {
714 {
5887 var api = this.api( true ).column( iCol ).visible( bShow );
715 var api = this.api( true ).column( iCol ).visible( bShow );
5888
716
5889 if ( bRedraw === undefined || bRedraw ) {
717 if ( bRedraw === undefined || bRedraw ) {
5890 api.columns.adjust().draw();
718 api.columns.adjust().draw();
5891 }
719 }
5892 };
720 };
5893
721
5894
722
5895 /**
723 /**
5896 * Get the settings for a particular table for external manipulation
724 * Get the settings for a particular table for external manipulation
5897 * @returns {object} DataTables settings object. See
725 * @returns {object} DataTables settings object. See
@@ -5912,8 +740,8 b''
5912 {
740 {
5913 return _fnSettingsFromNode( this[_ext.iApiIndex] );
741 return _fnSettingsFromNode( this[_ext.iApiIndex] );
5914 };
742 };
5915
743
5916
744
5917 /**
745 /**
5918 * Sort the table by a particular column
746 * Sort the table by a particular column
5919 * @param {int} iCol the data index to sort on. Note that this will not match the
747 * @param {int} iCol the data index to sort on. Note that this will not match the
@@ -5933,8 +761,8 b''
5933 {
761 {
5934 this.api( true ).order( aaSort ).draw();
762 this.api( true ).order( aaSort ).draw();
5935 };
763 };
5936
764
5937
765
5938 /**
766 /**
5939 * Attach a sort listener to an element for a given column
767 * Attach a sort listener to an element for a given column
5940 * @param {node} nNode the element to attach the sort listener to
768 * @param {node} nNode the element to attach the sort listener to
@@ -5955,8 +783,8 b''
5955 {
783 {
5956 this.api( true ).order.listener( nNode, iColumn, fnCallback );
784 this.api( true ).order.listener( nNode, iColumn, fnCallback );
5957 };
785 };
5958
786
5959
787
5960 /**
788 /**
5961 * Update a table cell or row - this method will accept either a single value to
789 * Update a table cell or row - this method will accept either a single value to
5962 * update the cell with, an array of values with one element for each column or
790 * update the cell with, an array of values with one element for each column or
@@ -5982,25 +810,25 b''
5982 this.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction )
810 this.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction )
5983 {
811 {
5984 var api = this.api( true );
812 var api = this.api( true );
5985
813
5986 if ( iColumn === undefined || iColumn === null ) {
814 if ( iColumn === undefined || iColumn === null ) {
5987 api.row( mRow ).data( mData );
815 api.row( mRow ).data( mData );
5988 }
816 }
5989 else {
817 else {
5990 api.cell( mRow, iColumn ).data( mData );
818 api.cell( mRow, iColumn ).data( mData );
5991 }
819 }
5992
820
5993 if ( bAction === undefined || bAction ) {
821 if ( bAction === undefined || bAction ) {
5994 api.columns.adjust();
822 api.columns.adjust();
5995 }
823 }
5996
824
5997 if ( bRedraw === undefined || bRedraw ) {
825 if ( bRedraw === undefined || bRedraw ) {
5998 api.draw();
826 api.draw();
5999 }
827 }
6000 return 0;
828 return 0;
6001 };
829 };
6002
830
6003
831
6004 /**
832 /**
6005 * Provide a common method for plug-ins to check the version of DataTables being used, in order
833 * Provide a common method for plug-ins to check the version of DataTables being used, in order
6006 * to ensure compatibility.
834 * to ensure compatibility.
@@ -6019,7 +847,7 b''
6019 * } );
847 * } );
6020 */
848 */
6021 this.fnVersionCheck = _ext.fnVersionCheck;
849 this.fnVersionCheck = _ext.fnVersionCheck;
6022
850
6023
851
6024 var _that = this;
852 var _that = this;
6025 var emptyInit = options === undefined;
853 var emptyInit = options === undefined;
@@ -6051,106 +879,112 b''
6051 var sId = this.getAttribute( 'id' );
879 var sId = this.getAttribute( 'id' );
6052 var bInitHandedOff = false;
880 var bInitHandedOff = false;
6053 var defaults = DataTable.defaults;
881 var defaults = DataTable.defaults;
6054
882 var $this = $(this);
6055
883
884
6056 /* Sanity check */
885 /* Sanity check */
6057 if ( this.nodeName.toLowerCase() != 'table' )
886 if ( this.nodeName.toLowerCase() != 'table' )
6058 {
887 {
6059 _fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 );
888 _fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 );
6060 return;
889 return;
6061 }
890 }
6062
891
6063 /* Backwards compatibility for the defaults */
892 /* Backwards compatibility for the defaults */
6064 _fnCompatOpts( defaults );
893 _fnCompatOpts( defaults );
6065 _fnCompatCols( defaults.column );
894 _fnCompatCols( defaults.column );
6066
895
6067 /* Convert the camel-case defaults to Hungarian */
896 /* Convert the camel-case defaults to Hungarian */
6068 _fnCamelToHungarian( defaults, defaults, true );
897 _fnCamelToHungarian( defaults, defaults, true );
6069 _fnCamelToHungarian( defaults.column, defaults.column, true );
898 _fnCamelToHungarian( defaults.column, defaults.column, true );
6070
899
6071 /* Setting up the initialisation object */
900 /* Setting up the initialisation object */
6072 _fnCamelToHungarian( defaults, oInit );
901 _fnCamelToHungarian( defaults, $.extend( oInit, $this.data() ) );
6073
902
903
904
6074 /* Check to see if we are re-initialising a table */
905 /* Check to see if we are re-initialising a table */
6075 var allSettings = DataTable.settings;
906 var allSettings = DataTable.settings;
6076 for ( i=0, iLen=allSettings.length ; i<iLen ; i++ )
907 for ( i=0, iLen=allSettings.length ; i<iLen ; i++ )
6077 {
908 {
909 var s = allSettings[i];
910
6078 /* Base check on table node */
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 var bRetrieve = oInit.bRetrieve !== undefined ? oInit.bRetrieve : defaults.bRetrieve;
914 var bRetrieve = oInit.bRetrieve !== undefined ? oInit.bRetrieve : defaults.bRetrieve;
6082 var bDestroy = oInit.bDestroy !== undefined ? oInit.bDestroy : defaults.bDestroy;
915 var bDestroy = oInit.bDestroy !== undefined ? oInit.bDestroy : defaults.bDestroy;
6083
916
6084 if ( emptyInit || bRetrieve )
917 if ( emptyInit || bRetrieve )
6085 {
918 {
6086 return allSettings[i].oInstance;
919 return s.oInstance;
6087 }
920 }
6088 else if ( bDestroy )
921 else if ( bDestroy )
6089 {
922 {
6090 allSettings[i].oInstance.fnDestroy();
923 s.oInstance.fnDestroy();
6091 break;
924 break;
6092 }
925 }
6093 else
926 else
6094 {
927 {
6095 _fnLog( allSettings[i], 0, 'Cannot reinitialise DataTable', 3 );
928 _fnLog( s, 0, 'Cannot reinitialise DataTable', 3 );
6096 return;
929 return;
6097 }
930 }
6098 }
931 }
6099
932
6100 /* If the element we are initialising has the same ID as a table which was previously
933 /* If the element we are initialising has the same ID as a table which was previously
6101 * initialised, but the table nodes don't match (from before) then we destroy the old
934 * initialised, but the table nodes don't match (from before) then we destroy the old
6102 * instance by simply deleting it. This is under the assumption that the table has been
935 * instance by simply deleting it. This is under the assumption that the table has been
6103 * destroyed by other methods. Anyone using non-id selectors will need to do this manually
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 allSettings.splice( i, 1 );
940 allSettings.splice( i, 1 );
6108 break;
941 break;
6109 }
942 }
6110 }
943 }
6111
944
6112 /* Ensure the table has an ID - required for accessibility */
945 /* Ensure the table has an ID - required for accessibility */
6113 if ( sId === null || sId === "" )
946 if ( sId === null || sId === "" )
6114 {
947 {
6115 sId = "DataTables_Table_"+(DataTable.ext._unique++);
948 sId = "DataTables_Table_"+(DataTable.ext._unique++);
6116 this.id = sId;
949 this.id = sId;
6117 }
950 }
6118
951
6119 /* Create the settings object for this table and set some of the default parameters */
952 /* Create the settings object for this table and set some of the default parameters */
6120 var oSettings = $.extend( true, {}, DataTable.models.oSettings, {
953 var oSettings = $.extend( true, {}, DataTable.models.oSettings, {
6121 "nTable": this,
954 "sDestroyWidth": $this[0].style.width,
6122 "oApi": _that.internal,
6123 "oInit": oInit,
6124 "sDestroyWidth": $(this)[0].style.width,
6125 "sInstance": sId,
955 "sInstance": sId,
6126 "sTableId": sId
956 "sTableId": sId
6127 } );
957 } );
958 oSettings.nTable = this;
959 oSettings.oApi = _that.internal;
960 oSettings.oInit = oInit;
961
6128 allSettings.push( oSettings );
962 allSettings.push( oSettings );
6129
963
6130 // Need to add the instance after the instance after the settings object has been added
964 // Need to add the instance after the instance after the settings object has been added
6131 // to the settings array, so we can self reference the table instance if more than one
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();
966 oSettings.oInstance = (_that.length===1) ? _that : $this.dataTable();
6133
967
6134 // Backwards compatibility, before we apply all the defaults
968 // Backwards compatibility, before we apply all the defaults
6135 _fnCompatOpts( oInit );
969 _fnCompatOpts( oInit );
6136
970
6137 if ( oInit.oLanguage )
971 if ( oInit.oLanguage )
6138 {
972 {
6139 _fnLanguageCompat( oInit.oLanguage );
973 _fnLanguageCompat( oInit.oLanguage );
6140 }
974 }
6141
975
6142 // If the length menu is given, but the init display length is not, use the length menu
976 // If the length menu is given, but the init display length is not, use the length menu
6143 if ( oInit.aLengthMenu && ! oInit.iDisplayLength )
977 if ( oInit.aLengthMenu && ! oInit.iDisplayLength )
6144 {
978 {
6145 oInit.iDisplayLength = $.isArray( oInit.aLengthMenu[0] ) ?
979 oInit.iDisplayLength = $.isArray( oInit.aLengthMenu[0] ) ?
6146 oInit.aLengthMenu[0][0] : oInit.aLengthMenu[0];
980 oInit.aLengthMenu[0][0] : oInit.aLengthMenu[0];
6147 }
981 }
6148
982
6149 // Apply the defaults and init options to make a single init object will all
983 // Apply the defaults and init options to make a single init object will all
6150 // options defined from defaults and instance options.
984 // options defined from defaults and instance options.
6151 oInit = _fnExtend( $.extend( true, {}, defaults ), oInit );
985 oInit = _fnExtend( $.extend( true, {}, defaults ), oInit );
6152
986
6153
987
6154 // Map the initialisation options onto the settings object
988 // Map the initialisation options onto the settings object
6155 _fnMap( oSettings.oFeatures, oInit, [
989 _fnMap( oSettings.oFeatures, oInit, [
6156 "bPaginate",
990 "bPaginate",
@@ -6185,6 +1019,7 b''
6185 "fnStateSaveCallback",
1019 "fnStateSaveCallback",
6186 "renderer",
1020 "renderer",
6187 "searchDelay",
1021 "searchDelay",
1022 "rowId",
6188 [ "iCookieDuration", "iStateDuration" ], // backwards compat
1023 [ "iCookieDuration", "iStateDuration" ], // backwards compat
6189 [ "oSearch", "oPreviousSearch" ],
1024 [ "oSearch", "oPreviousSearch" ],
6190 [ "aoSearchCols", "aoPreSearchCols" ],
1025 [ "aoSearchCols", "aoPreSearchCols" ],
@@ -6198,7 +1033,7 b''
6198 [ "bScrollCollapse", "bCollapse" ]
1033 [ "bScrollCollapse", "bCollapse" ]
6199 ] );
1034 ] );
6200 _fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" );
1035 _fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" );
6201
1036
6202 /* Callback functions which are array driven */
1037 /* Callback functions which are array driven */
6203 _fnCallbackReg( oSettings, 'aoDrawCallback', oInit.fnDrawCallback, 'user' );
1038 _fnCallbackReg( oSettings, 'aoDrawCallback', oInit.fnDrawCallback, 'user' );
6204 _fnCallbackReg( oSettings, 'aoServerParams', oInit.fnServerParams, 'user' );
1039 _fnCallbackReg( oSettings, 'aoServerParams', oInit.fnServerParams, 'user' );
@@ -6211,9 +1046,14 b''
6211 _fnCallbackReg( oSettings, 'aoFooterCallback', oInit.fnFooterCallback, 'user' );
1046 _fnCallbackReg( oSettings, 'aoFooterCallback', oInit.fnFooterCallback, 'user' );
6212 _fnCallbackReg( oSettings, 'aoInitComplete', oInit.fnInitComplete, 'user' );
1047 _fnCallbackReg( oSettings, 'aoInitComplete', oInit.fnInitComplete, 'user' );
6213 _fnCallbackReg( oSettings, 'aoPreDrawCallback', oInit.fnPreDrawCallback, 'user' );
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 var oClasses = oSettings.oClasses;
1055 var oClasses = oSettings.oClasses;
6216
1056
6217 // @todo Remove in 1.11
1057 // @todo Remove in 1.11
6218 if ( oInit.bJQueryUI )
1058 if ( oInit.bJQueryUI )
6219 {
1059 {
@@ -6221,13 +1061,13 b''
6221 * you want to have multiple tables with multiple independent classes
1061 * you want to have multiple tables with multiple independent classes
6222 */
1062 */
6223 $.extend( oClasses, DataTable.ext.oJUIClasses, oInit.oClasses );
1063 $.extend( oClasses, DataTable.ext.oJUIClasses, oInit.oClasses );
6224
1064
6225 if ( oInit.sDom === defaults.sDom && defaults.sDom === "lfrtip" )
1065 if ( oInit.sDom === defaults.sDom && defaults.sDom === "lfrtip" )
6226 {
1066 {
6227 /* Set the DOM to use a layout suitable for jQuery UI's theming */
1067 /* Set the DOM to use a layout suitable for jQuery UI's theming */
6228 oSettings.sDom = '<"H"lfr>t<"F"ip>';
1068 oSettings.sDom = '<"H"lfr>t<"F"ip>';
6229 }
1069 }
6230
1070
6231 if ( ! oSettings.renderer ) {
1071 if ( ! oSettings.renderer ) {
6232 oSettings.renderer = 'jqueryui';
1072 oSettings.renderer = 'jqueryui';
6233 }
1073 }
@@ -6239,24 +1079,16 b''
6239 {
1079 {
6240 $.extend( oClasses, DataTable.ext.classes, oInit.oClasses );
1080 $.extend( oClasses, DataTable.ext.classes, oInit.oClasses );
6241 }
1081 }
6242 $(this).addClass( oClasses.sTable );
1082 $this.addClass( oClasses.sTable );
6243
1083
6244 /* Calculate the scroll bar width and cache it for use later on */
1084
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
6253 if ( oSettings.iInitDisplayStart === undefined )
1085 if ( oSettings.iInitDisplayStart === undefined )
6254 {
1086 {
6255 /* Display start point, taking into account the save saving */
1087 /* Display start point, taking into account the save saving */
6256 oSettings.iInitDisplayStart = oInit.iDisplayStart;
1088 oSettings.iInitDisplayStart = oInit.iDisplayStart;
6257 oSettings._iDisplayStart = oInit.iDisplayStart;
1089 oSettings._iDisplayStart = oInit.iDisplayStart;
6258 }
1090 }
6259
1091
6260 if ( oInit.iDeferLoading !== null )
1092 if ( oInit.iDeferLoading !== null )
6261 {
1093 {
6262 oSettings.bDeferLoading = true;
1094 oSettings.bDeferLoading = true;
@@ -6264,12 +1096,12 b''
6264 oSettings._iRecordsDisplay = tmp ? oInit.iDeferLoading[0] : oInit.iDeferLoading;
1096 oSettings._iRecordsDisplay = tmp ? oInit.iDeferLoading[0] : oInit.iDeferLoading;
6265 oSettings._iRecordsTotal = tmp ? oInit.iDeferLoading[1] : oInit.iDeferLoading;
1097 oSettings._iRecordsTotal = tmp ? oInit.iDeferLoading[1] : oInit.iDeferLoading;
6266 }
1098 }
6267
1099
6268 /* Language definitions */
1100 /* Language definitions */
6269 var oLanguage = oSettings.oLanguage;
1101 var oLanguage = oSettings.oLanguage;
6270 $.extend( true, oLanguage, oInit.oLanguage );
1102 $.extend( true, oLanguage, oInit.oLanguage );
6271
1103
6272 if ( oLanguage.sUrl !== "" )
1104 if ( oLanguage.sUrl )
6273 {
1105 {
6274 /* Get the language definitions from a file - because this Ajax call makes the language
1106 /* Get the language definitions from a file - because this Ajax call makes the language
6275 * get async to the remainder of this function we use bInitHandedOff to indicate that
1107 * get async to the remainder of this function we use bInitHandedOff to indicate that
@@ -6291,7 +1123,7 b''
6291 } );
1123 } );
6292 bInitHandedOff = true;
1124 bInitHandedOff = true;
6293 }
1125 }
6294
1126
6295 /*
1127 /*
6296 * Stripes
1128 * Stripes
6297 */
1129 */
@@ -6302,17 +1134,17 b''
6302 oClasses.sStripeEven
1134 oClasses.sStripeEven
6303 ];
1135 ];
6304 }
1136 }
6305
1137
6306 /* Remove row stripe classes if they are already on the table row */
1138 /* Remove row stripe classes if they are already on the table row */
6307 var stripeClasses = oSettings.asStripeClasses;
1139 var stripeClasses = oSettings.asStripeClasses;
6308 var rowOne = $('tbody tr:eq(0)', this);
1140 var rowOne = $this.children('tbody').find('tr').eq(0);
6309 if ( $.inArray( true, $.map( stripeClasses, function(el, i) {
1141 if ( $.inArray( true, $.map( stripeClasses, function(el, i) {
6310 return rowOne.hasClass(el);
1142 return rowOne.hasClass(el);
6311 } ) ) !== -1 ) {
1143 } ) ) !== -1 ) {
6312 $('tbody tr', this).removeClass( stripeClasses.join(' ') );
1144 $('tbody tr', this).removeClass( stripeClasses.join(' ') );
6313 oSettings.asDestroyStripes = stripeClasses.slice();
1145 oSettings.asDestroyStripes = stripeClasses.slice();
6314 }
1146 }
6315
1147
6316 /*
1148 /*
6317 * Columns
1149 * Columns
6318 * See if we should load columns automatically or use defined ones
1150 * See if we should load columns automatically or use defined ones
@@ -6325,7 +1157,7 b''
6325 _fnDetectHeader( oSettings.aoHeader, nThead[0] );
1157 _fnDetectHeader( oSettings.aoHeader, nThead[0] );
6326 anThs = _fnGetUniqueThs( oSettings );
1158 anThs = _fnGetUniqueThs( oSettings );
6327 }
1159 }
6328
1160
6329 /* If not given a column array, generate one with nulls */
1161 /* If not given a column array, generate one with nulls */
6330 if ( oInit.aoColumns === null )
1162 if ( oInit.aoColumns === null )
6331 {
1163 {
@@ -6339,33 +1171,33 b''
6339 {
1171 {
6340 aoColumnsInit = oInit.aoColumns;
1172 aoColumnsInit = oInit.aoColumns;
6341 }
1173 }
6342
1174
6343 /* Add the columns */
1175 /* Add the columns */
6344 for ( i=0, iLen=aoColumnsInit.length ; i<iLen ; i++ )
1176 for ( i=0, iLen=aoColumnsInit.length ; i<iLen ; i++ )
6345 {
1177 {
6346 _fnAddColumn( oSettings, anThs ? anThs[i] : null );
1178 _fnAddColumn( oSettings, anThs ? anThs[i] : null );
6347 }
1179 }
6348
1180
6349 /* Apply the column definitions */
1181 /* Apply the column definitions */
6350 _fnApplyColumnDefs( oSettings, oInit.aoColumnDefs, aoColumnsInit, function (iCol, oDef) {
1182 _fnApplyColumnDefs( oSettings, oInit.aoColumnDefs, aoColumnsInit, function (iCol, oDef) {
6351 _fnColumnOptions( oSettings, iCol, oDef );
1183 _fnColumnOptions( oSettings, iCol, oDef );
6352 } );
1184 } );
6353
1185
6354 /* HTML5 attribute detection - build an mData object automatically if the
1186 /* HTML5 attribute detection - build an mData object automatically if the
6355 * attributes are found
1187 * attributes are found
6356 */
1188 */
6357 if ( rowOne.length ) {
1189 if ( rowOne.length ) {
6358 var a = function ( cell, name ) {
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
1193
6362 $.each( _fnGetRowElements( oSettings, rowOne[0] ).cells, function (i, cell) {
1194 $( rowOne[0] ).children('th, td').each( function (i, cell) {
6363 var col = oSettings.aoColumns[i];
1195 var col = oSettings.aoColumns[i];
6364
1196
6365 if ( col.mData === i ) {
1197 if ( col.mData === i ) {
6366 var sort = a( cell, 'sort' ) || a( cell, 'order' );
1198 var sort = a( cell, 'sort' ) || a( cell, 'order' );
6367 var filter = a( cell, 'filter' ) || a( cell, 'search' );
1199 var filter = a( cell, 'filter' ) || a( cell, 'search' );
6368
1200
6369 if ( sort !== null || filter !== null ) {
1201 if ( sort !== null || filter !== null ) {
6370 col.mData = {
1202 col.mData = {
6371 _: i+'.display',
1203 _: i+'.display',
@@ -6373,149 +1205,5487 b''
6373 type: sort !== null ? i+'.@data-'+sort : undefined,
1205 type: sort !== null ? i+'.@data-'+sort : undefined,
6374 filter: filter !== null ? i+'.@data-'+filter : undefined
1206 filter: filter !== null ? i+'.@data-'+filter : undefined
6375 };
1207 };
6376
1208
6377 _fnColumnOptions( oSettings, i );
1209 _fnColumnOptions( oSettings, i );
6378 }
1210 }
6379 }
1211 }
6380 } );
1212 } );
6381 }
1213 }
6382
1214
6383 var features = oSettings.oFeatures;
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 /* Must be done after everything which can be overridden by the state saving! */
1324 /* Must be done after everything which can be overridden by the state saving! */
6386 if ( oInit.bStateSave )
1325 if ( oInit.bStateSave )
6387 {
1326 {
6388 features.bStateSave = true;
1327 features.bStateSave = true;
6389 _fnLoadState( oSettings, oInit );
6390 _fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState, 'state_save' );
1328 _fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState, 'state_save' );
6391 }
1329 _fnLoadState( oSettings, oInit, loadedInit );
6392
1330 }
6393
1331 else {
6394 /*
1332 loadedInit();
6395 * Sorting
1333 }
6396 * @todo For modularisation (1.11) this needs to do into a sort start up handler
1334
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 }
6512 } );
1335 } );
6513 _that = null;
1336 _that = null;
6514 return this;
1337 return this;
6515 };
1338 };
6516
1339
6517
1340
6518
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 * Computed structure of the DataTables API, defined by the options passed to
6690 * Computed structure of the DataTables API, defined by the options passed to
6521 * `DataTable.Api.register()` when building the API.
6691 * `DataTable.Api.register()` when building the API.
@@ -6553,8 +6723,8 b''
6553 * @ignore
6723 * @ignore
6554 */
6724 */
6555 var __apiStruct = [];
6725 var __apiStruct = [];
6556
6726
6557
6727
6558 /**
6728 /**
6559 * `Array.prototype` reference.
6729 * `Array.prototype` reference.
6560 *
6730 *
@@ -6562,8 +6732,8 b''
6562 * @ignore
6732 * @ignore
6563 */
6733 */
6564 var __arrayProto = Array.prototype;
6734 var __arrayProto = Array.prototype;
6565
6735
6566
6736
6567 /**
6737 /**
6568 * Abstraction for `context` parameter of the `Api` constructor to allow it to
6738 * Abstraction for `context` parameter of the `Api` constructor to allow it to
6569 * take several different forms for ease of use.
6739 * take several different forms for ease of use.
@@ -6591,7 +6761,7 b''
6591 var tables = $.map( settings, function (el, i) {
6761 var tables = $.map( settings, function (el, i) {
6592 return el.nTable;
6762 return el.nTable;
6593 } );
6763 } );
6594
6764
6595 if ( ! mixed ) {
6765 if ( ! mixed ) {
6596 return [];
6766 return [];
6597 }
6767 }
@@ -6615,7 +6785,7 b''
6615 // jQuery object (also DataTables instance)
6785 // jQuery object (also DataTables instance)
6616 jq = mixed;
6786 jq = mixed;
6617 }
6787 }
6618
6788
6619 if ( jq ) {
6789 if ( jq ) {
6620 return jq.map( function(i) {
6790 return jq.map( function(i) {
6621 idx = $.inArray( this, tables );
6791 idx = $.inArray( this, tables );
@@ -6623,8 +6793,8 b''
6623 } ).toArray();
6793 } ).toArray();
6624 }
6794 }
6625 };
6795 };
6626
6796
6627
6797
6628 /**
6798 /**
6629 * DataTables API class - used to control and interface with one or more
6799 * DataTables API class - used to control and interface with one or more
6630 * DataTables enhanced tables.
6800 * DataTables enhanced tables.
@@ -6681,20 +6851,18 b''
6681 */
6851 */
6682 _Api = function ( context, data )
6852 _Api = function ( context, data )
6683 {
6853 {
6684 if ( ! this instanceof _Api ) {
6854 if ( ! (this instanceof _Api) ) {
6685 throw 'DT API must be constructed as a new object';
6855 return new _Api( context, data );
6686 // or should it do the 'new' for the caller?
6856 }
6687 // return new _Api.apply( this, arguments );
6857
6688 }
6689
6690 var settings = [];
6858 var settings = [];
6691 var ctxSettings = function ( o ) {
6859 var ctxSettings = function ( o ) {
6692 var a = _toSettings( o );
6860 var a = _toSettings( o );
6693 if ( a ) {
6861 if ( a ) {
6694 settings.push.apply( settings, a );
6862 settings = settings.concat( a );
6695 }
6863 }
6696 };
6864 };
6697
6865
6698 if ( $.isArray( context ) ) {
6866 if ( $.isArray( context ) ) {
6699 for ( var i=0, ien=context.length ; i<ien ; i++ ) {
6867 for ( var i=0, ien=context.length ; i<ien ; i++ ) {
6700 ctxSettings( context[i] );
6868 ctxSettings( context[i] );
@@ -6703,70 +6871,72 b''
6703 else {
6871 else {
6704 ctxSettings( context );
6872 ctxSettings( context );
6705 }
6873 }
6706
6874
6707 // Remove duplicates
6875 // Remove duplicates
6708 this.context = _unique( settings );
6876 this.context = _unique( settings );
6709
6877
6710 // Initial data
6878 // Initial data
6711 if ( data ) {
6879 if ( data ) {
6712 this.push.apply( this, data.toArray ? data.toArray() : data );
6880 $.merge( this, data );
6713 }
6881 }
6714
6882
6715 // selector
6883 // selector
6716 this.selector = {
6884 this.selector = {
6717 rows: null,
6885 rows: null,
6718 cols: null,
6886 cols: null,
6719 opts: null
6887 opts: null
6720 };
6888 };
6721
6889
6722 _Api.extend( this, this, __apiStruct );
6890 _Api.extend( this, this, __apiStruct );
6723 };
6891 };
6724
6892
6725 DataTable.Api = _Api;
6893 DataTable.Api = _Api;
6726
6894
6727 _Api.prototype = /** @lends DataTables.Api */{
6895 // Don't destroy the existing prototype, just extend it. Required for jQuery 2's
6728 /**
6896 // isPlainObject.
6729 * Return a new Api instance, comprised of the data held in the current
6897 $.extend( _Api.prototype, {
6730 * instance, join with the other array(s) and/or value(s).
6898 any: function ()
6731 *
6899 {
6732 * An alias for `Array.prototype.concat`.
6900 return this.count() !== 0;
6733 *
6901 },
6734 * @type method
6902
6735 * @param {*} value1 Arrays and/or values to concatenate.
6903
6736 * @param {*} [...] Additional arrays and/or values to concatenate.
6737 * @returns {DataTables.Api} New API instance, comprising of the combined
6738 * array.
6739 */
6740 concat: __arrayProto.concat,
6904 concat: __arrayProto.concat,
6741
6905
6742
6906
6743 context: [], // array of table settings objects
6907 context: [], // array of table settings objects
6744
6908
6745
6909
6910 count: function ()
6911 {
6912 return this.flatten().length;
6913 },
6914
6915
6746 each: function ( fn )
6916 each: function ( fn )
6747 {
6917 {
6748 for ( var i=0, ien=this.length ; i<ien; i++ ) {
6918 for ( var i=0, ien=this.length ; i<ien; i++ ) {
6749 fn.call( this, this[i], i, this );
6919 fn.call( this, this[i], i, this );
6750 }
6920 }
6751
6921
6752 return this;
6922 return this;
6753 },
6923 },
6754
6924
6755
6925
6756 eq: function ( idx )
6926 eq: function ( idx )
6757 {
6927 {
6758 var ctx = this.context;
6928 var ctx = this.context;
6759
6929
6760 return ctx.length > idx ?
6930 return ctx.length > idx ?
6761 new _Api( ctx[idx], this[idx] ) :
6931 new _Api( ctx[idx], this[idx] ) :
6762 null;
6932 null;
6763 },
6933 },
6764
6934
6765
6935
6766 filter: function ( fn )
6936 filter: function ( fn )
6767 {
6937 {
6768 var a = [];
6938 var a = [];
6769
6939
6770 if ( __arrayProto.filter ) {
6940 if ( __arrayProto.filter ) {
6771 a = __arrayProto.filter.call( this, fn, this );
6941 a = __arrayProto.filter.call( this, fn, this );
6772 }
6942 }
@@ -6778,21 +6948,21 b''
6778 }
6948 }
6779 }
6949 }
6780 }
6950 }
6781
6951
6782 return new _Api( this.context, a );
6952 return new _Api( this.context, a );
6783 },
6953 },
6784
6954
6785
6955
6786 flatten: function ()
6956 flatten: function ()
6787 {
6957 {
6788 var a = [];
6958 var a = [];
6789 return new _Api( this.context, a.concat.apply( a, this.toArray() ) );
6959 return new _Api( this.context, a.concat.apply( a, this.toArray() ) );
6790 },
6960 },
6791
6961
6792
6962
6793 join: __arrayProto.join,
6963 join: __arrayProto.join,
6794
6964
6795
6965
6796 indexOf: __arrayProto.indexOf || function (obj, start)
6966 indexOf: __arrayProto.indexOf || function (obj, start)
6797 {
6967 {
6798 for ( var i=(start || 0), ien=this.length ; i<ien ; i++ ) {
6968 for ( var i=(start || 0), ien=this.length ; i<ien ; i++ ) {
@@ -6802,8 +6972,7 b''
6802 }
6972 }
6803 return -1;
6973 return -1;
6804 },
6974 },
6805
6975
6806 // Note that `alwaysNew` is internal - use iteratorNew externally
6807 iterator: function ( flatten, type, fn, alwaysNew ) {
6976 iterator: function ( flatten, type, fn, alwaysNew ) {
6808 var
6977 var
6809 a = [], ret,
6978 a = [], ret,
@@ -6811,7 +6980,7 b''
6811 context = this.context,
6980 context = this.context,
6812 rows, items, item,
6981 rows, items, item,
6813 selector = this.selector;
6982 selector = this.selector;
6814
6983
6815 // Argument shifting
6984 // Argument shifting
6816 if ( typeof flatten === 'string' ) {
6985 if ( typeof flatten === 'string' ) {
6817 alwaysNew = fn;
6986 alwaysNew = fn;
@@ -6819,13 +6988,13 b''
6819 type = flatten;
6988 type = flatten;
6820 flatten = false;
6989 flatten = false;
6821 }
6990 }
6822
6991
6823 for ( i=0, ien=context.length ; i<ien ; i++ ) {
6992 for ( i=0, ien=context.length ; i<ien ; i++ ) {
6824 var apiInst = new _Api( context[i] );
6993 var apiInst = new _Api( context[i] );
6825
6994
6826 if ( type === 'table' ) {
6995 if ( type === 'table' ) {
6827 ret = fn.call( apiInst, context[i], i );
6996 ret = fn.call( apiInst, context[i], i );
6828
6997
6829 if ( ret !== undefined ) {
6998 if ( ret !== undefined ) {
6830 a.push( ret );
6999 a.push( ret );
6831 }
7000 }
@@ -6833,7 +7002,7 b''
6833 else if ( type === 'columns' || type === 'rows' ) {
7002 else if ( type === 'columns' || type === 'rows' ) {
6834 // this has same length as context - one entry for each table
7003 // this has same length as context - one entry for each table
6835 ret = fn.call( apiInst, context[i], this[i], i );
7004 ret = fn.call( apiInst, context[i], this[i], i );
6836
7005
6837 if ( ret !== undefined ) {
7006 if ( ret !== undefined ) {
6838 a.push( ret );
7007 a.push( ret );
6839 }
7008 }
@@ -6842,28 +7011,28 b''
6842 // columns and rows share the same structure.
7011 // columns and rows share the same structure.
6843 // 'this' is an array of column indexes for each context
7012 // 'this' is an array of column indexes for each context
6844 items = this[i];
7013 items = this[i];
6845
7014
6846 if ( type === 'column-rows' ) {
7015 if ( type === 'column-rows' ) {
6847 rows = _selector_row_indexes( context[i], selector.opts );
7016 rows = _selector_row_indexes( context[i], selector.opts );
6848 }
7017 }
6849
7018
6850 for ( j=0, jen=items.length ; j<jen ; j++ ) {
7019 for ( j=0, jen=items.length ; j<jen ; j++ ) {
6851 item = items[j];
7020 item = items[j];
6852
7021
6853 if ( type === 'cell' ) {
7022 if ( type === 'cell' ) {
6854 ret = fn.call( apiInst, context[i], item.row, item.column, i, j );
7023 ret = fn.call( apiInst, context[i], item.row, item.column, i, j );
6855 }
7024 }
6856 else {
7025 else {
6857 ret = fn.call( apiInst, context[i], item, i, j, rows );
7026 ret = fn.call( apiInst, context[i], item, i, j, rows );
6858 }
7027 }
6859
7028
6860 if ( ret !== undefined ) {
7029 if ( ret !== undefined ) {
6861 a.push( ret );
7030 a.push( ret );
6862 }
7031 }
6863 }
7032 }
6864 }
7033 }
6865 }
7034 }
6866
7035
6867 if ( a.length || alwaysNew ) {
7036 if ( a.length || alwaysNew ) {
6868 var api = new _Api( context, flatten ? a.concat.apply( [], a ) : a );
7037 var api = new _Api( context, flatten ? a.concat.apply( [], a ) : a );
6869 var apiSelector = api.selector;
7038 var apiSelector = api.selector;
@@ -6874,22 +7043,22 b''
6874 }
7043 }
6875 return this;
7044 return this;
6876 },
7045 },
6877
7046
6878
7047
6879 lastIndexOf: __arrayProto.lastIndexOf || function (obj, start)
7048 lastIndexOf: __arrayProto.lastIndexOf || function (obj, start)
6880 {
7049 {
6881 // Bit cheeky...
7050 // Bit cheeky...
6882 return this.indexOf.apply( this.toArray.reverse(), arguments );
7051 return this.indexOf.apply( this.toArray.reverse(), arguments );
6883 },
7052 },
6884
7053
6885
7054
6886 length: 0,
7055 length: 0,
6887
7056
6888
7057
6889 map: function ( fn )
7058 map: function ( fn )
6890 {
7059 {
6891 var a = [];
7060 var a = [];
6892
7061
6893 if ( __arrayProto.map ) {
7062 if ( __arrayProto.map ) {
6894 a = __arrayProto.map.call( this, fn, this );
7063 a = __arrayProto.map.call( this, fn, this );
6895 }
7064 }
@@ -6899,88 +7068,88 b''
6899 a.push( fn.call( this, this[i], i ) );
7068 a.push( fn.call( this, this[i], i ) );
6900 }
7069 }
6901 }
7070 }
6902
7071
6903 return new _Api( this.context, a );
7072 return new _Api( this.context, a );
6904 },
7073 },
6905
7074
6906
7075
6907 pluck: function ( prop )
7076 pluck: function ( prop )
6908 {
7077 {
6909 return this.map( function ( el ) {
7078 return this.map( function ( el ) {
6910 return el[ prop ];
7079 return el[ prop ];
6911 } );
7080 } );
6912 },
7081 },
6913
7082
6914 pop: __arrayProto.pop,
7083 pop: __arrayProto.pop,
6915
7084
6916
7085
6917 push: __arrayProto.push,
7086 push: __arrayProto.push,
6918
7087
6919
7088
6920 // Does not return an API instance
7089 // Does not return an API instance
6921 reduce: __arrayProto.reduce || function ( fn, init )
7090 reduce: __arrayProto.reduce || function ( fn, init )
6922 {
7091 {
6923 return _fnReduce( this, fn, init, 0, this.length, 1 );
7092 return _fnReduce( this, fn, init, 0, this.length, 1 );
6924 },
7093 },
6925
7094
6926
7095
6927 reduceRight: __arrayProto.reduceRight || function ( fn, init )
7096 reduceRight: __arrayProto.reduceRight || function ( fn, init )
6928 {
7097 {
6929 return _fnReduce( this, fn, init, this.length-1, -1, -1 );
7098 return _fnReduce( this, fn, init, this.length-1, -1, -1 );
6930 },
7099 },
6931
7100
6932
7101
6933 reverse: __arrayProto.reverse,
7102 reverse: __arrayProto.reverse,
6934
7103
6935
7104
6936 // Object with rows, columns and opts
7105 // Object with rows, columns and opts
6937 selector: null,
7106 selector: null,
6938
7107
6939
7108
6940 shift: __arrayProto.shift,
7109 shift: __arrayProto.shift,
6941
7110
6942
7111
6943 sort: __arrayProto.sort, // ? name - order?
7112 sort: __arrayProto.sort, // ? name - order?
6944
7113
6945
7114
6946 splice: __arrayProto.splice,
7115 splice: __arrayProto.splice,
6947
7116
6948
7117
6949 toArray: function ()
7118 toArray: function ()
6950 {
7119 {
6951 return __arrayProto.slice.call( this );
7120 return __arrayProto.slice.call( this );
6952 },
7121 },
6953
7122
6954
7123
6955 to$: function ()
7124 to$: function ()
6956 {
7125 {
6957 return $( this );
7126 return $( this );
6958 },
7127 },
6959
7128
6960
7129
6961 toJQuery: function ()
7130 toJQuery: function ()
6962 {
7131 {
6963 return $( this );
7132 return $( this );
6964 },
7133 },
6965
7134
6966
7135
6967 unique: function ()
7136 unique: function ()
6968 {
7137 {
6969 return new _Api( this.context, _unique(this) );
7138 return new _Api( this.context, _unique(this) );
6970 },
7139 },
6971
7140
6972
7141
6973 unshift: __arrayProto.unshift
7142 unshift: __arrayProto.unshift
6974 };
7143 } );
6975
7144
6976
7145
6977 _Api.extend = function ( scope, obj, ext )
7146 _Api.extend = function ( scope, obj, ext )
6978 {
7147 {
6979 // Only extend API instances and static properties of the API
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 return;
7150 return;
6982 }
7151 }
6983
7152
6984 var
7153 var
6985 i, ien,
7154 i, ien,
6986 j, jen,
7155 j, jen,
@@ -6988,41 +7157,41 b''
6988 methodScoping = function ( scope, fn, struc ) {
7157 methodScoping = function ( scope, fn, struc ) {
6989 return function () {
7158 return function () {
6990 var ret = fn.apply( scope, arguments );
7159 var ret = fn.apply( scope, arguments );
6991
7160
6992 // Method extension
7161 // Method extension
6993 _Api.extend( ret, ret, struc.methodExt );
7162 _Api.extend( ret, ret, struc.methodExt );
6994 return ret;
7163 return ret;
6995 };
7164 };
6996 };
7165 };
6997
7166
6998 for ( i=0, ien=ext.length ; i<ien ; i++ ) {
7167 for ( i=0, ien=ext.length ; i<ien ; i++ ) {
6999 struct = ext[i];
7168 struct = ext[i];
7000
7169
7001 // Value
7170 // Value
7002 obj[ struct.name ] = typeof struct.val === 'function' ?
7171 obj[ struct.name ] = typeof struct.val === 'function' ?
7003 methodScoping( scope, struct.val, struct ) :
7172 methodScoping( scope, struct.val, struct ) :
7004 $.isPlainObject( struct.val ) ?
7173 $.isPlainObject( struct.val ) ?
7005 {} :
7174 {} :
7006 struct.val;
7175 struct.val;
7007
7176
7008 obj[ struct.name ].__dt_wrapper = true;
7177 obj[ struct.name ].__dt_wrapper = true;
7009
7178
7010 // Property extension
7179 // Property extension
7011 _Api.extend( scope, obj[ struct.name ], struct.propExt );
7180 _Api.extend( scope, obj[ struct.name ], struct.propExt );
7012 }
7181 }
7013 };
7182 };
7014
7183
7015
7184
7016 // @todo - Is there need for an augment function?
7185 // @todo - Is there need for an augment function?
7017 // _Api.augment = function ( inst, name )
7186 // _Api.augment = function ( inst, name )
7018 // {
7187 // {
7019 // // Find src object in the structure from the name
7188 // // Find src object in the structure from the name
7020 // var parts = name.split('.');
7189 // var parts = name.split('.');
7021
7190
7022 // _Api.extend( inst, obj );
7191 // _Api.extend( inst, obj );
7023 // };
7192 // };
7024
7193
7025
7194
7026 // [
7195 // [
7027 // {
7196 // {
7028 // name: 'data' -- string - Property name
7197 // name: 'data' -- string - Property name
@@ -7045,7 +7214,7 b''
7045 // ]
7214 // ]
7046 // }
7215 // }
7047 // ]
7216 // ]
7048
7217
7049 _Api.register = _api_register = function ( name, val )
7218 _Api.register = _api_register = function ( name, val )
7050 {
7219 {
7051 if ( $.isArray( name ) ) {
7220 if ( $.isArray( name ) ) {
@@ -7054,13 +7223,13 b''
7054 }
7223 }
7055 return;
7224 return;
7056 }
7225 }
7057
7226
7058 var
7227 var
7059 i, ien,
7228 i, ien,
7060 heir = name.split('.'),
7229 heir = name.split('.'),
7061 struct = __apiStruct,
7230 struct = __apiStruct,
7062 key, method;
7231 key, method;
7063
7232
7064 var find = function ( src, name ) {
7233 var find = function ( src, name ) {
7065 for ( var i=0, ien=src.length ; i<ien ; i++ ) {
7234 for ( var i=0, ien=src.length ; i<ien ; i++ ) {
7066 if ( src[i].name === name ) {
7235 if ( src[i].name === name ) {
@@ -7069,13 +7238,13 b''
7069 }
7238 }
7070 return null;
7239 return null;
7071 };
7240 };
7072
7241
7073 for ( i=0, ien=heir.length ; i<ien ; i++ ) {
7242 for ( i=0, ien=heir.length ; i<ien ; i++ ) {
7074 method = heir[i].indexOf('()') !== -1;
7243 method = heir[i].indexOf('()') !== -1;
7075 key = method ?
7244 key = method ?
7076 heir[i].replace('()', '') :
7245 heir[i].replace('()', '') :
7077 heir[i];
7246 heir[i];
7078
7247
7079 var src = find( struct, key );
7248 var src = find( struct, key );
7080 if ( ! src ) {
7249 if ( ! src ) {
7081 src = {
7250 src = {
@@ -7086,7 +7255,7 b''
7086 };
7255 };
7087 struct.push( src );
7256 struct.push( src );
7088 }
7257 }
7089
7258
7090 if ( i === ien-1 ) {
7259 if ( i === ien-1 ) {
7091 src.val = val;
7260 src.val = val;
7092 }
7261 }
@@ -7097,14 +7266,14 b''
7097 }
7266 }
7098 }
7267 }
7099 };
7268 };
7100
7269
7101
7270
7102 _Api.registerPlural = _api_registerPlural = function ( pluralName, singularName, val ) {
7271 _Api.registerPlural = _api_registerPlural = function ( pluralName, singularName, val ) {
7103 _Api.register( pluralName, val );
7272 _Api.register( pluralName, val );
7104
7273
7105 _Api.register( singularName, function () {
7274 _Api.register( singularName, function () {
7106 var ret = val.apply( this, arguments );
7275 var ret = val.apply( this, arguments );
7107
7276
7108 if ( ret === this ) {
7277 if ( ret === this ) {
7109 // Returned item is the API instance that was passed in, return it
7278 // Returned item is the API instance that was passed in, return it
7110 return this;
7279 return this;
@@ -7118,13 +7287,13 b''
7118 ret[0] :
7287 ret[0] :
7119 undefined;
7288 undefined;
7120 }
7289 }
7121
7290
7122 // Non-API return - just fire it back
7291 // Non-API return - just fire it back
7123 return ret;
7292 return ret;
7124 } );
7293 } );
7125 };
7294 };
7126
7295
7127
7296
7128 /**
7297 /**
7129 * Selector for HTML tables. Apply the given selector to the give array of
7298 * Selector for HTML tables. Apply the given selector to the give array of
7130 * DataTables settings objects.
7299 * DataTables settings objects.
@@ -7140,12 +7309,12 b''
7140 if ( typeof selector === 'number' ) {
7309 if ( typeof selector === 'number' ) {
7141 return [ a[ selector ] ];
7310 return [ a[ selector ] ];
7142 }
7311 }
7143
7312
7144 // Perform a jQuery selector on the table nodes
7313 // Perform a jQuery selector on the table nodes
7145 var nodes = $.map( a, function (el, i) {
7314 var nodes = $.map( a, function (el, i) {
7146 return el.nTable;
7315 return el.nTable;
7147 } );
7316 } );
7148
7317
7149 return $(nodes)
7318 return $(nodes)
7150 .filter( selector )
7319 .filter( selector )
7151 .map( function (i) {
7320 .map( function (i) {
@@ -7155,9 +7324,9 b''
7155 } )
7324 } )
7156 .toArray();
7325 .toArray();
7157 };
7326 };
7158
7327
7159
7328
7160
7329
7161 /**
7330 /**
7162 * Context selector for the API's context (i.e. the tables the API instance
7331 * Context selector for the API's context (i.e. the tables the API instance
7163 * refers to.
7332 * refers to.
@@ -7175,71 +7344,77 b''
7175 new _Api( __table_selector( selector, this.context ) ) :
7344 new _Api( __table_selector( selector, this.context ) ) :
7176 this;
7345 this;
7177 } );
7346 } );
7178
7347
7179
7348
7180 _api_register( 'table()', function ( selector ) {
7349 _api_register( 'table()', function ( selector ) {
7181 var tables = this.tables( selector );
7350 var tables = this.tables( selector );
7182 var ctx = tables.context;
7351 var ctx = tables.context;
7183
7352
7184 // Truncate to the first matched table
7353 // Truncate to the first matched table
7185 return ctx.length ?
7354 return ctx.length ?
7186 new _Api( ctx[0] ) :
7355 new _Api( ctx[0] ) :
7187 tables;
7356 tables;
7188 } );
7357 } );
7189
7358
7190
7359
7191 _api_registerPlural( 'tables().nodes()', 'table().node()' , function () {
7360 _api_registerPlural( 'tables().nodes()', 'table().node()' , function () {
7192 return this.iterator( 'table', function ( ctx ) {
7361 return this.iterator( 'table', function ( ctx ) {
7193 return ctx.nTable;
7362 return ctx.nTable;
7194 }, 1 );
7363 }, 1 );
7195 } );
7364 } );
7196
7365
7197
7366
7198 _api_registerPlural( 'tables().body()', 'table().body()' , function () {
7367 _api_registerPlural( 'tables().body()', 'table().body()' , function () {
7199 return this.iterator( 'table', function ( ctx ) {
7368 return this.iterator( 'table', function ( ctx ) {
7200 return ctx.nTBody;
7369 return ctx.nTBody;
7201 }, 1 );
7370 }, 1 );
7202 } );
7371 } );
7203
7372
7204
7373
7205 _api_registerPlural( 'tables().header()', 'table().header()' , function () {
7374 _api_registerPlural( 'tables().header()', 'table().header()' , function () {
7206 return this.iterator( 'table', function ( ctx ) {
7375 return this.iterator( 'table', function ( ctx ) {
7207 return ctx.nTHead;
7376 return ctx.nTHead;
7208 }, 1 );
7377 }, 1 );
7209 } );
7378 } );
7210
7379
7211
7380
7212 _api_registerPlural( 'tables().footer()', 'table().footer()' , function () {
7381 _api_registerPlural( 'tables().footer()', 'table().footer()' , function () {
7213 return this.iterator( 'table', function ( ctx ) {
7382 return this.iterator( 'table', function ( ctx ) {
7214 return ctx.nTFoot;
7383 return ctx.nTFoot;
7215 }, 1 );
7384 }, 1 );
7216 } );
7385 } );
7217
7386
7218
7387
7219 _api_registerPlural( 'tables().containers()', 'table().container()' , function () {
7388 _api_registerPlural( 'tables().containers()', 'table().container()' , function () {
7220 return this.iterator( 'table', function ( ctx ) {
7389 return this.iterator( 'table', function ( ctx ) {
7221 return ctx.nTableWrapper;
7390 return ctx.nTableWrapper;
7222 }, 1 );
7391 }, 1 );
7223 } );
7392 } );
7224
7393
7225
7394
7226
7395
7227 /**
7396 /**
7228 * Redraw the tables in the current context.
7397 * Redraw the tables in the current context.
7229 *
7398 */
7230 * @param {boolean} [reset=true] Reset (default) or hold the current paging
7399 _api_register( 'draw()', function ( 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 ) {
7236 return this.iterator( 'table', function ( settings ) {
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
7415
7241
7416
7242
7417
7243 /**
7418 /**
7244 * Get the current page index.
7419 * Get the current page index.
7245 *
7420 *
@@ -7263,14 +7438,14 b''
7263 if ( action === undefined ) {
7438 if ( action === undefined ) {
7264 return this.page.info().page; // not an expensive call
7439 return this.page.info().page; // not an expensive call
7265 }
7440 }
7266
7441
7267 // else, have an action to take on all tables
7442 // else, have an action to take on all tables
7268 return this.iterator( 'table', function ( settings ) {
7443 return this.iterator( 'table', function ( settings ) {
7269 _fnPageChange( settings, action );
7444 _fnPageChange( settings, action );
7270 } );
7445 } );
7271 } );
7446 } );
7272
7447
7273
7448
7274 /**
7449 /**
7275 * Paging information for the first table in the current context.
7450 * Paging information for the first table in the current context.
7276 *
7451 *
@@ -7293,14 +7468,14 b''
7293 if ( this.context.length === 0 ) {
7468 if ( this.context.length === 0 ) {
7294 return undefined;
7469 return undefined;
7295 }
7470 }
7296
7471
7297 var
7472 var
7298 settings = this.context[0],
7473 settings = this.context[0],
7299 start = settings._iDisplayStart,
7474 start = settings._iDisplayStart,
7300 len = settings._iDisplayLength,
7475 len = settings.oFeatures.bPaginate ? settings._iDisplayLength : -1,
7301 visRecords = settings.fnRecordsDisplay(),
7476 visRecords = settings.fnRecordsDisplay(),
7302 all = len === -1;
7477 all = len === -1;
7303
7478
7304 return {
7479 return {
7305 "page": all ? 0 : Math.floor( start / len ),
7480 "page": all ? 0 : Math.floor( start / len ),
7306 "pages": all ? 1 : Math.ceil( visRecords / len ),
7481 "pages": all ? 1 : Math.ceil( visRecords / len ),
@@ -7308,11 +7483,12 b''
7308 "end": settings.fnDisplayEnd(),
7483 "end": settings.fnDisplayEnd(),
7309 "length": len,
7484 "length": len,
7310 "recordsTotal": settings.fnRecordsTotal(),
7485 "recordsTotal": settings.fnRecordsTotal(),
7311 "recordsDisplay": visRecords
7486 "recordsDisplay": visRecords,
7487 "serverSide": _fnDataSource( settings ) === 'ssp'
7312 };
7488 };
7313 } );
7489 } );
7314
7490
7315
7491
7316 /**
7492 /**
7317 * Get the current page length.
7493 * Get the current page length.
7318 *
7494 *
@@ -7333,48 +7509,53 b''
7333 this.context[0]._iDisplayLength :
7509 this.context[0]._iDisplayLength :
7334 undefined;
7510 undefined;
7335 }
7511 }
7336
7512
7337 // else, set the page length
7513 // else, set the page length
7338 return this.iterator( 'table', function ( settings ) {
7514 return this.iterator( 'table', function ( settings ) {
7339 _fnLengthChange( settings, len );
7515 _fnLengthChange( settings, len );
7340 } );
7516 } );
7341 } );
7517 } );
7342
7518
7343
7519
7344
7520
7345 var __reload = function ( settings, holdPosition, callback ) {
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 if ( _fnDataSource( settings ) == 'ssp' ) {
7531 if ( _fnDataSource( settings ) == 'ssp' ) {
7347 _fnReDraw( settings, holdPosition );
7532 _fnReDraw( settings, holdPosition );
7348 }
7533 }
7349 else {
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 // Trigger xhr
7543 // Trigger xhr
7351 _fnProcessingDisplay( settings, true );
7352
7353 _fnBuildAjax( settings, [], function( json ) {
7544 _fnBuildAjax( settings, [], function( json ) {
7354 _fnClearTable( settings );
7545 _fnClearTable( settings );
7355
7546
7356 var data = _fnAjaxDataSrc( settings, json );
7547 var data = _fnAjaxDataSrc( settings, json );
7357 for ( var i=0, ien=data.length ; i<ien ; i++ ) {
7548 for ( var i=0, ien=data.length ; i<ien ; i++ ) {
7358 _fnAddData( settings, data[i] );
7549 _fnAddData( settings, data[i] );
7359 }
7550 }
7360
7551
7361 _fnReDraw( settings, holdPosition );
7552 _fnReDraw( settings, holdPosition );
7362 _fnProcessingDisplay( settings, false );
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
7557
7377
7558
7378 /**
7559 /**
7379 * Get the JSON response from the last Ajax request that DataTables made to the
7560 * Get the JSON response from the last Ajax request that DataTables made to the
7380 * server. Note that this returns the JSON from the first table in the current
7561 * server. Note that this returns the JSON from the first table in the current
@@ -7384,29 +7565,29 b''
7384 */
7565 */
7385 _api_register( 'ajax.json()', function () {
7566 _api_register( 'ajax.json()', function () {
7386 var ctx = this.context;
7567 var ctx = this.context;
7387
7568
7388 if ( ctx.length > 0 ) {
7569 if ( ctx.length > 0 ) {
7389 return ctx[0].json;
7570 return ctx[0].json;
7390 }
7571 }
7391
7572
7392 // else return undefined;
7573 // else return undefined;
7393 } );
7574 } );
7394
7575
7395
7576
7396 /**
7577 /**
7397 * Get the data submitted in the last Ajax request
7578 * Get the data submitted in the last Ajax request
7398 */
7579 */
7399 _api_register( 'ajax.params()', function () {
7580 _api_register( 'ajax.params()', function () {
7400 var ctx = this.context;
7581 var ctx = this.context;
7401
7582
7402 if ( ctx.length > 0 ) {
7583 if ( ctx.length > 0 ) {
7403 return ctx[0].oAjaxData;
7584 return ctx[0].oAjaxData;
7404 }
7585 }
7405
7586
7406 // else return undefined;
7587 // else return undefined;
7407 } );
7588 } );
7408
7589
7409
7590
7410 /**
7591 /**
7411 * Reload tables from the Ajax data source. Note that this function will
7592 * Reload tables from the Ajax data source. Note that this function will
7412 * automatically re-draw the table when the remote data has been loaded.
7593 * automatically re-draw the table when the remote data has been loaded.
@@ -7421,8 +7602,8 b''
7421 __reload( settings, resetPaging===false, callback );
7602 __reload( settings, resetPaging===false, callback );
7422 } );
7603 } );
7423 } );
7604 } );
7424
7605
7425
7606
7426 /**
7607 /**
7427 * Get the current Ajax URL. Note that this returns the URL from the first
7608 * Get the current Ajax URL. Note that this returns the URL from the first
7428 * table in the current context.
7609 * table in the current context.
@@ -7437,21 +7618,21 b''
7437 */
7618 */
7438 _api_register( 'ajax.url()', function ( url ) {
7619 _api_register( 'ajax.url()', function ( url ) {
7439 var ctx = this.context;
7620 var ctx = this.context;
7440
7621
7441 if ( url === undefined ) {
7622 if ( url === undefined ) {
7442 // get
7623 // get
7443 if ( ctx.length === 0 ) {
7624 if ( ctx.length === 0 ) {
7444 return undefined;
7625 return undefined;
7445 }
7626 }
7446 ctx = ctx[0];
7627 ctx = ctx[0];
7447
7628
7448 return ctx.ajax ?
7629 return ctx.ajax ?
7449 $.isPlainObject( ctx.ajax ) ?
7630 $.isPlainObject( ctx.ajax ) ?
7450 ctx.ajax.url :
7631 ctx.ajax.url :
7451 ctx.ajax :
7632 ctx.ajax :
7452 ctx.sAjaxSource;
7633 ctx.sAjaxSource;
7453 }
7634 }
7454
7635
7455 // set
7636 // set
7456 return this.iterator( 'table', function ( settings ) {
7637 return this.iterator( 'table', function ( settings ) {
7457 if ( $.isPlainObject( settings.ajax ) ) {
7638 if ( $.isPlainObject( settings.ajax ) ) {
@@ -7465,8 +7646,8 b''
7465 // value of `sAjaxSource` redundant.
7646 // value of `sAjaxSource` redundant.
7466 } );
7647 } );
7467 } );
7648 } );
7468
7649
7469
7650
7470 /**
7651 /**
7471 * Load data from the newly set Ajax URL. Note that this method is only
7652 * Load data from the newly set Ajax URL. Note that this method is only
7472 * available when `ajax.url()` is used to set a URL. Additionally, this method
7653 * available when `ajax.url()` is used to set a URL. Additionally, this method
@@ -7483,61 +7664,70 b''
7483 __reload( ctx, resetPaging===false, callback );
7664 __reload( ctx, resetPaging===false, callback );
7484 } );
7665 } );
7485 } );
7666 } );
7486
7667
7487
7668
7488
7669
7489
7670
7490 var _selector_run = function ( selector, select )
7671 var _selector_run = function ( type, selector, selectFn, settings, opts )
7491 {
7672 {
7492 var
7673 var
7493 out = [], res,
7674 out = [], res,
7494 a, i, ien, j, jen,
7675 a, i, ien, j, jen,
7495 selectorType = typeof selector;
7676 selectorType = typeof selector;
7496
7677
7497 // Can't just check for isArray here, as an API or jQuery instance might be
7678 // Can't just check for isArray here, as an API or jQuery instance might be
7498 // given with their array like look
7679 // given with their array like look
7499 if ( ! selector || selectorType === 'string' || selectorType === 'function' || selector.length === undefined ) {
7680 if ( ! selector || selectorType === 'string' || selectorType === 'function' || selector.length === undefined ) {
7500 selector = [ selector ];
7681 selector = [ selector ];
7501 }
7682 }
7502
7683
7503 for ( i=0, ien=selector.length ; i<ien ; i++ ) {
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 selector[i].split(',') :
7687 selector[i].split(',') :
7506 [ selector[i] ];
7688 [ selector[i] ];
7507
7689
7508 for ( j=0, jen=a.length ; j<jen ; j++ ) {
7690 for ( j=0, jen=a.length ; j<jen ; j++ ) {
7509 res = select( typeof a[j] === 'string' ? $.trim(a[j]) : a[j] );
7691 res = selectFn( typeof a[j] === 'string' ? $.trim(a[j]) : a[j] );
7510
7692
7511 if ( res && res.length ) {
7693 if ( res && res.length ) {
7512 out.push.apply( out, res );
7694 out = out.concat( res );
7513 }
7695 }
7514 }
7696 }
7515 }
7697 }
7516
7698
7517 return out;
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
7709
7520
7710
7521 var _selector_opts = function ( opts )
7711 var _selector_opts = function ( opts )
7522 {
7712 {
7523 if ( ! opts ) {
7713 if ( ! opts ) {
7524 opts = {};
7714 opts = {};
7525 }
7715 }
7526
7716
7527 // Backwards compatibility for 1.9- which used the terminology filter rather
7717 // Backwards compatibility for 1.9- which used the terminology filter rather
7528 // than search
7718 // than search
7529 if ( opts.filter && ! opts.search ) {
7719 if ( opts.filter && opts.search === undefined ) {
7530 opts.search = opts.filter;
7720 opts.search = opts.filter;
7531 }
7721 }
7532
7722
7533 return {
7723 return $.extend( {
7534 search: opts.search || 'none',
7724 search: 'none',
7535 order: opts.order || 'current',
7725 order: 'current',
7536 page: opts.page || 'all'
7726 page: 'all'
7537 };
7727 }, opts );
7538 };
7728 };
7539
7729
7540
7730
7541 var _selector_first = function ( inst )
7731 var _selector_first = function ( inst )
7542 {
7732 {
7543 // Reduce the API instance to the first item found
7733 // Reduce the API instance to the first item found
@@ -7546,31 +7736,32 b''
7546 // Assign the first element to the first item in the instance
7736 // Assign the first element to the first item in the instance
7547 // and truncate the instance and context
7737 // and truncate the instance and context
7548 inst[0] = inst[i];
7738 inst[0] = inst[i];
7739 inst[0].length = 1;
7549 inst.length = 1;
7740 inst.length = 1;
7550 inst.context = [ inst.context[i] ];
7741 inst.context = [ inst.context[i] ];
7551
7742
7552 return inst;
7743 return inst;
7553 }
7744 }
7554 }
7745 }
7555
7746
7556 // Not found - return an empty instance
7747 // Not found - return an empty instance
7557 inst.length = 0;
7748 inst.length = 0;
7558 return inst;
7749 return inst;
7559 };
7750 };
7560
7751
7561
7752
7562 var _selector_row_indexes = function ( settings, opts )
7753 var _selector_row_indexes = function ( settings, opts )
7563 {
7754 {
7564 var
7755 var
7565 i, ien, tmp, a=[],
7756 i, ien, tmp, a=[],
7566 displayFiltered = settings.aiDisplay,
7757 displayFiltered = settings.aiDisplay,
7567 displayMaster = settings.aiDisplayMaster;
7758 displayMaster = settings.aiDisplayMaster;
7568
7759
7569 var
7760 var
7570 search = opts.search, // none, applied, removed
7761 search = opts.search, // none, applied, removed
7571 order = opts.order, // applied, current, index (original - compatibility with 1.9)
7762 order = opts.order, // applied, current, index (original - compatibility with 1.9)
7572 page = opts.page; // all, current
7763 page = opts.page; // all, current
7573
7764
7574 if ( _fnDataSource( settings ) == 'ssp' ) {
7765 if ( _fnDataSource( settings ) == 'ssp' ) {
7575 // In server-side processing mode, most options are irrelevant since
7766 // In server-side processing mode, most options are irrelevant since
7576 // rows not shown don't exist and the index order is the applied order
7767 // rows not shown don't exist and the index order is the applied order
@@ -7604,7 +7795,7 b''
7604 }
7795 }
7605 else { // applied | removed
7796 else { // applied | removed
7606 tmp = $.inArray( i, displayFiltered );
7797 tmp = $.inArray( i, displayFiltered );
7607
7798
7608 if ((tmp === -1 && search == 'removed') ||
7799 if ((tmp === -1 && search == 'removed') ||
7609 (tmp >= 0 && search == 'applied') )
7800 (tmp >= 0 && search == 'applied') )
7610 {
7801 {
@@ -7613,11 +7804,11 b''
7613 }
7804 }
7614 }
7805 }
7615 }
7806 }
7616
7807
7617 return a;
7808 return a;
7618 };
7809 };
7619
7810
7620
7811
7621 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7812 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7622 * Rows
7813 * Rows
7623 *
7814 *
@@ -7628,32 +7819,35 b''
7628 * {array} - jQuery array of nodes, or simply an array of TR nodes
7819 * {array} - jQuery array of nodes, or simply an array of TR nodes
7629 *
7820 *
7630 */
7821 */
7631
7822
7632
7823
7633 var __row_selector = function ( settings, selector, opts )
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 var selInt = _intVal( sel );
7828 var selInt = _intVal( sel );
7637 var i, ien;
7829 var i, ien;
7638
7830
7639 // Short cut - selector is a number and no options provided (default is
7831 // Short cut - selector is a number and no options provided (default is
7640 // all records, so no need to check if the index is in there, since it
7832 // all records, so no need to check if the index is in there, since it
7641 // must be - dev error if the index doesn't exist).
7833 // must be - dev error if the index doesn't exist).
7642 if ( selInt !== null && ! opts ) {
7834 if ( selInt !== null && ! opts ) {
7643 return [ selInt ];
7835 return [ selInt ];
7644 }
7836 }
7645
7837
7646 var rows = _selector_row_indexes( settings, opts );
7838 if ( ! rows ) {
7647
7839 rows = _selector_row_indexes( settings, opts );
7840 }
7841
7648 if ( selInt !== null && $.inArray( selInt, rows ) !== -1 ) {
7842 if ( selInt !== null && $.inArray( selInt, rows ) !== -1 ) {
7649 // Selector - integer
7843 // Selector - integer
7650 return [ selInt ];
7844 return [ selInt ];
7651 }
7845 }
7652 else if ( ! sel ) {
7846 else if ( sel === null || sel === undefined || sel === '' ) {
7653 // Selector - none
7847 // Selector - none
7654 return rows;
7848 return rows;
7655 }
7849 }
7656
7850
7657 // Selector - function
7851 // Selector - function
7658 if ( typeof sel === 'function' ) {
7852 if ( typeof sel === 'function' ) {
7659 return $.map( rows, function (idx) {
7853 return $.map( rows, function (idx) {
@@ -7661,20 +7855,48 b''
7661 return sel( idx, row._aData, row.nTr ) ? idx : null;
7855 return sel( idx, row._aData, row.nTr ) ? idx : null;
7662 } );
7856 } );
7663 }
7857 }
7664
7858
7665 // Get nodes in the order from the `rows` array with null values removed
7859 // Get nodes in the order from the `rows` array with null values removed
7666 var nodes = _removeEmpty(
7860 var nodes = _removeEmpty(
7667 _pluck_order( settings.aoData, rows, 'nTr' )
7861 _pluck_order( settings.aoData, rows, 'nTr' )
7668 );
7862 );
7669
7863
7670 // Selector - node
7864 // Selector - node
7671 if ( sel.nodeName ) {
7865 if ( sel.nodeName ) {
7672 if ( $.inArray( sel, nodes ) !== -1 ) {
7866 if ( sel._DT_RowIndex !== undefined ) {
7673 return [ sel._DT_RowIndex ]; // sel is a TR node that is in the table
7867 return [ sel._DT_RowIndex ]; // Property added by DT for fast lookup
7674 // and DataTables adds a prop for fast lookup
7868 }
7675 }
7869 else if ( sel._DT_CellIndex ) {
7676 }
7870 return [ sel._DT_CellIndex.row ];
7677
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 // Selector - jQuery selector string, array of nodes or jQuery object/
7900 // Selector - jQuery selector string, array of nodes or jQuery object/
7679 // As jQuery's .filter() allows jQuery objects to be passed in filter,
7901 // As jQuery's .filter() allows jQuery objects to be passed in filter,
7680 // it also allows arrays, so this will cope with all three options
7902 // it also allows arrays, so this will cope with all three options
@@ -7684,13 +7906,12 b''
7684 return this._DT_RowIndex;
7906 return this._DT_RowIndex;
7685 } )
7907 } )
7686 .toArray();
7908 .toArray();
7687 } );
7909 };
7910
7911 return _selector_run( 'row', selector, run, settings, opts );
7688 };
7912 };
7689
7913
7690
7914
7691 /**
7692 *
7693 */
7694 _api_register( 'rows()', function ( selector, opts ) {
7915 _api_register( 'rows()', function ( selector, opts ) {
7695 // argument shifting
7916 // argument shifting
7696 if ( selector === undefined ) {
7917 if ( selector === undefined ) {
@@ -7700,89 +7921,128 b''
7700 opts = selector;
7921 opts = selector;
7701 selector = '';
7922 selector = '';
7702 }
7923 }
7703
7924
7704 opts = _selector_opts( opts );
7925 opts = _selector_opts( opts );
7705
7926
7706 var inst = this.iterator( 'table', function ( settings ) {
7927 var inst = this.iterator( 'table', function ( settings ) {
7707 return __row_selector( settings, selector, opts );
7928 return __row_selector( settings, selector, opts );
7708 }, 1 );
7929 }, 1 );
7709
7930
7710 // Want argument shifting here and in __row_selector?
7931 // Want argument shifting here and in __row_selector?
7711 inst.selector.rows = selector;
7932 inst.selector.rows = selector;
7712 inst.selector.opts = opts;
7933 inst.selector.opts = opts;
7713
7934
7714 return inst;
7935 return inst;
7715 } );
7936 } );
7716
7937
7717
7718 _api_register( 'rows().nodes()', function () {
7938 _api_register( 'rows().nodes()', function () {
7719 return this.iterator( 'row', function ( settings, row ) {
7939 return this.iterator( 'row', function ( settings, row ) {
7720 return settings.aoData[ row ].nTr || undefined;
7940 return settings.aoData[ row ].nTr || undefined;
7721 }, 1 );
7941 }, 1 );
7722 } );
7942 } );
7723
7943
7724 _api_register( 'rows().data()', function () {
7944 _api_register( 'rows().data()', function () {
7725 return this.iterator( true, 'rows', function ( settings, rows ) {
7945 return this.iterator( true, 'rows', function ( settings, rows ) {
7726 return _pluck_order( settings.aoData, rows, '_aData' );
7946 return _pluck_order( settings.aoData, rows, '_aData' );
7727 }, 1 );
7947 }, 1 );
7728 } );
7948 } );
7729
7949
7730 _api_registerPlural( 'rows().cache()', 'row().cache()', function ( type ) {
7950 _api_registerPlural( 'rows().cache()', 'row().cache()', function ( type ) {
7731 return this.iterator( 'row', function ( settings, row ) {
7951 return this.iterator( 'row', function ( settings, row ) {
7732 var r = settings.aoData[ row ];
7952 var r = settings.aoData[ row ];
7733 return type === 'search' ? r._aFilterData : r._aSortData;
7953 return type === 'search' ? r._aFilterData : r._aSortData;
7734 }, 1 );
7954 }, 1 );
7735 } );
7955 } );
7736
7956
7737 _api_registerPlural( 'rows().invalidate()', 'row().invalidate()', function ( src ) {
7957 _api_registerPlural( 'rows().invalidate()', 'row().invalidate()', function ( src ) {
7738 return this.iterator( 'row', function ( settings, row ) {
7958 return this.iterator( 'row', function ( settings, row ) {
7739 _fnInvalidate( settings, row, src );
7959 _fnInvalidate( settings, row, src );
7740 } );
7960 } );
7741 } );
7961 } );
7742
7962
7743 _api_registerPlural( 'rows().indexes()', 'row().index()', function () {
7963 _api_registerPlural( 'rows().indexes()', 'row().index()', function () {
7744 return this.iterator( 'row', function ( settings, row ) {
7964 return this.iterator( 'row', function ( settings, row ) {
7745 return row;
7965 return row;
7746 }, 1 );
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 _api_registerPlural( 'rows().remove()', 'row().remove()', function () {
7984 _api_registerPlural( 'rows().remove()', 'row().remove()', function () {
7750 var that = this;
7985 var that = this;
7751
7986
7752 return this.iterator( 'row', function ( settings, row, thatIdx ) {
7987 this.iterator( 'row', function ( settings, row, thatIdx ) {
7753 var data = settings.aoData;
7988 var data = settings.aoData;
7754
7989 var rowData = data[ row ];
7990 var i, ien, j, jen;
7991 var loopRow, loopCells;
7992
7755 data.splice( row, 1 );
7993 data.splice( row, 1 );
7756
7994
7757 // Update the _DT_RowIndex parameter on all rows in the table
7995 // Update the cached indexes
7758 for ( var i=0, ien=data.length ; i<ien ; i++ ) {
7996 for ( i=0, ien=data.length ; i<ien ; i++ ) {
7759 if ( data[i].nTr !== null ) {
7997 loopRow = data[i];
7760 data[i].nTr._DT_RowIndex = i;
7998 loopCells = loopRow.anCells;
7761 }
7999
7762 }
8000 // Rows
7763
8001 if ( loopRow.nTr !== null ) {
7764 // Remove the target row from the search array
8002 loopRow.nTr._DT_RowIndex = i;
7765 var displayIndex = $.inArray( row, settings.aiDisplay );
8003 }
7766
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 // Delete from the display arrays
8013 // Delete from the display arrays
7768 _fnDeleteIndex( settings.aiDisplayMaster, row );
8014 _fnDeleteIndex( settings.aiDisplayMaster, row );
7769 _fnDeleteIndex( settings.aiDisplay, row );
8015 _fnDeleteIndex( settings.aiDisplay, row );
7770 _fnDeleteIndex( that[ thatIdx ], row, false ); // maintain local indexes
8016 _fnDeleteIndex( that[ thatIdx ], row, false ); // maintain local indexes
7771
8017
7772 // Check for an 'overflow' they case for displaying the table
8018 // Check for an 'overflow' they case for displaying the table
7773 _fnLengthOverflow( settings );
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 } );
8027
7776
8028 this.iterator( 'table', function ( settings ) {
7777
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 _api_register( 'rows.add()', function ( rows ) {
8038 _api_register( 'rows.add()', function ( rows ) {
7779 var newRows = this.iterator( 'table', function ( settings ) {
8039 var newRows = this.iterator( 'table', function ( settings ) {
7780 var row, i, ien;
8040 var row, i, ien;
7781 var out = [];
8041 var out = [];
7782
8042
7783 for ( i=0, ien=rows.length ; i<ien ; i++ ) {
8043 for ( i=0, ien=rows.length ; i<ien ; i++ ) {
7784 row = rows[i];
8044 row = rows[i];
7785
8045
7786 if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {
8046 if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {
7787 out.push( _fnAddTr( settings, row )[0] );
8047 out.push( _fnAddTr( settings, row )[0] );
7788 }
8048 }
@@ -7790,84 +8050,92 b''
7790 out.push( _fnAddData( settings, row ) );
8050 out.push( _fnAddData( settings, row ) );
7791 }
8051 }
7792 }
8052 }
7793
8053
7794 return out;
8054 return out;
7795 }, 1 );
8055 }, 1 );
7796
8056
7797 // Return an Api.rows() extended instance, so rows().nodes() etc can be used
8057 // Return an Api.rows() extended instance, so rows().nodes() etc can be used
7798 var modRows = this.rows( -1 );
8058 var modRows = this.rows( -1 );
7799 modRows.pop();
8059 modRows.pop();
7800 modRows.push.apply( modRows, newRows.toArray() );
8060 $.merge( modRows, newRows );
7801
8061
7802 return modRows;
8062 return modRows;
7803 } );
8063 } );
7804
8064
7805
8065
7806
8066
7807
8067
7808
8068
7809 /**
8069 /**
7810 *
8070 *
7811 */
8071 */
7812 _api_register( 'row()', function ( selector, opts ) {
8072 _api_register( 'row()', function ( selector, opts ) {
7813 return _selector_first( this.rows( selector, opts ) );
8073 return _selector_first( this.rows( selector, opts ) );
7814 } );
8074 } );
7815
8075
7816
8076
7817 _api_register( 'row().data()', function ( data ) {
8077 _api_register( 'row().data()', function ( data ) {
7818 var ctx = this.context;
8078 var ctx = this.context;
7819
8079
7820 if ( data === undefined ) {
8080 if ( data === undefined ) {
7821 // Get
8081 // Get
7822 return ctx.length && this.length ?
8082 return ctx.length && this.length ?
7823 ctx[0].aoData[ this[0] ]._aData :
8083 ctx[0].aoData[ this[0] ]._aData :
7824 undefined;
8084 undefined;
7825 }
8085 }
7826
8086
7827 // Set
8087 // Set
7828 ctx[0].aoData[ this[0] ]._aData = data;
8088 ctx[0].aoData[ this[0] ]._aData = data;
7829
8089
7830 // Automatically invalidate
8090 // Automatically invalidate
7831 _fnInvalidate( ctx[0], this[0], 'data' );
8091 _fnInvalidate( ctx[0], this[0], 'data' );
7832
8092
7833 return this;
8093 return this;
7834 } );
8094 } );
7835
8095
7836
8096
7837 _api_register( 'row().node()', function () {
8097 _api_register( 'row().node()', function () {
7838 var ctx = this.context;
8098 var ctx = this.context;
7839
8099
7840 return ctx.length && this.length ?
8100 return ctx.length && this.length ?
7841 ctx[0].aoData[ this[0] ].nTr || null :
8101 ctx[0].aoData[ this[0] ].nTr || null :
7842 null;
8102 null;
7843 } );
8103 } );
7844
8104
7845
8105
7846 _api_register( 'row.add()', function ( row ) {
8106 _api_register( 'row.add()', function ( row ) {
7847 // Allow a jQuery object to be passed in - only a single row is added from
8107 // Allow a jQuery object to be passed in - only a single row is added from
7848 // it though - the first element in the set
8108 // it though - the first element in the set
7849 if ( row instanceof $ && row.length ) {
8109 if ( row instanceof $ && row.length ) {
7850 row = row[0];
8110 row = row[0];
7851 }
8111 }
7852
8112
7853 var rows = this.iterator( 'table', function ( settings ) {
8113 var rows = this.iterator( 'table', function ( settings ) {
7854 if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {
8114 if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {
7855 return _fnAddTr( settings, row )[0];
8115 return _fnAddTr( settings, row )[0];
7856 }
8116 }
7857 return _fnAddData( settings, row );
8117 return _fnAddData( settings, row );
7858 } );
8118 } );
7859
8119
7860 // Return an Api.rows() extended instance, with the newly added row selected
8120 // Return an Api.rows() extended instance, with the newly added row selected
7861 return this.row( rows[0] );
8121 return this.row( rows[0] );
7862 } );
8122 } );
7863
8123
7864
8124
7865
8125
7866 var __details_add = function ( ctx, row, data, klass )
8126 var __details_add = function ( ctx, row, data, klass )
7867 {
8127 {
7868 // Convert to array of TR elements
8128 // Convert to array of TR elements
7869 var rows = [];
8129 var rows = [];
7870 var addRow = function ( r, k ) {
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 // If we get a TR element, then just add it directly - up to the dev
8139 // If we get a TR element, then just add it directly - up to the dev
7872 // to add the correct number of columns etc
8140 // to add the correct number of columns etc
7873 if ( r.nodeName && r.nodeName.toLowerCase() === 'tr' ) {
8141 if ( r.nodeName && r.nodeName.toLowerCase() === 'tr' ) {
@@ -7880,72 +8148,65 b''
7880 .addClass( k )
8148 .addClass( k )
7881 .html( r )
8149 .html( r )
7882 [0].colSpan = _fnVisbleColumns( ctx );
8150 [0].colSpan = _fnVisbleColumns( ctx );
7883
8151
7884 rows.push( created[0] );
8152 rows.push( created[0] );
7885 }
8153 }
7886 };
8154 };
7887
8155
7888 if ( $.isArray( data ) || data instanceof $ ) {
8156 addRow( data, klass );
7889 for ( var i=0, ien=data.length ; i<ien ; i++ ) {
8157
7890 addRow( data[i], klass );
7891 }
7892 }
7893 else {
7894 addRow( data, klass );
7895 }
7896
7897 if ( row._details ) {
8158 if ( row._details ) {
7898 row._details.remove();
8159 row._details.detach();
7899 }
8160 }
7900
8161
7901 row._details = $(rows);
8162 row._details = $(rows);
7902
8163
7903 // If the children were already shown, that state should be retained
8164 // If the children were already shown, that state should be retained
7904 if ( row._detailsShow ) {
8165 if ( row._detailsShow ) {
7905 row._details.insertAfter( row.nTr );
8166 row._details.insertAfter( row.nTr );
7906 }
8167 }
7907 };
8168 };
7908
8169
7909
8170
7910 var __details_remove = function ( api, idx )
8171 var __details_remove = function ( api, idx )
7911 {
8172 {
7912 var ctx = api.context;
8173 var ctx = api.context;
7913
8174
7914 if ( ctx.length ) {
8175 if ( ctx.length ) {
7915 var row = ctx[0].aoData[ idx !== undefined ? idx : api[0] ];
8176 var row = ctx[0].aoData[ idx !== undefined ? idx : api[0] ];
7916
8177
7917 if ( row._details ) {
8178 if ( row && row._details ) {
7918 row._details.remove();
8179 row._details.remove();
7919
8180
7920 row._detailsShow = undefined;
8181 row._detailsShow = undefined;
7921 row._details = undefined;
8182 row._details = undefined;
7922 }
8183 }
7923 }
8184 }
7924 };
8185 };
7925
8186
7926
8187
7927 var __details_display = function ( api, show ) {
8188 var __details_display = function ( api, show ) {
7928 var ctx = api.context;
8189 var ctx = api.context;
7929
8190
7930 if ( ctx.length && api.length ) {
8191 if ( ctx.length && api.length ) {
7931 var row = ctx[0].aoData[ api[0] ];
8192 var row = ctx[0].aoData[ api[0] ];
7932
8193
7933 if ( row._details ) {
8194 if ( row._details ) {
7934 row._detailsShow = show;
8195 row._detailsShow = show;
7935
8196
7936 if ( show ) {
8197 if ( show ) {
7937 row._details.insertAfter( row.nTr );
8198 row._details.insertAfter( row.nTr );
7938 }
8199 }
7939 else {
8200 else {
7940 row._details.detach();
8201 row._details.detach();
7941 }
8202 }
7942
8203
7943 __details_events( ctx[0] );
8204 __details_events( ctx[0] );
7944 }
8205 }
7945 }
8206 }
7946 };
8207 };
7947
8208
7948
8209
7949 var __details_events = function ( settings )
8210 var __details_events = function ( settings )
7950 {
8211 {
7951 var api = new _Api( settings );
8212 var api = new _Api( settings );
@@ -7954,51 +8215,51 b''
7954 var colvisEvent = 'column-visibility'+namespace;
8215 var colvisEvent = 'column-visibility'+namespace;
7955 var destroyEvent = 'destroy'+namespace;
8216 var destroyEvent = 'destroy'+namespace;
7956 var data = settings.aoData;
8217 var data = settings.aoData;
7957
8218
7958 api.off( drawEvent +' '+ colvisEvent +' '+ destroyEvent );
8219 api.off( drawEvent +' '+ colvisEvent +' '+ destroyEvent );
7959
8220
7960 if ( _pluck( data, '_details' ).length > 0 ) {
8221 if ( _pluck( data, '_details' ).length > 0 ) {
7961 // On each draw, insert the required elements into the document
8222 // On each draw, insert the required elements into the document
7962 api.on( drawEvent, function ( e, ctx ) {
8223 api.on( drawEvent, function ( e, ctx ) {
7963 if ( settings !== ctx ) {
8224 if ( settings !== ctx ) {
7964 return;
8225 return;
7965 }
8226 }
7966
8227
7967 api.rows( {page:'current'} ).eq(0).each( function (idx) {
8228 api.rows( {page:'current'} ).eq(0).each( function (idx) {
7968 // Internal data grab
8229 // Internal data grab
7969 var row = data[ idx ];
8230 var row = data[ idx ];
7970
8231
7971 if ( row._detailsShow ) {
8232 if ( row._detailsShow ) {
7972 row._details.insertAfter( row.nTr );
8233 row._details.insertAfter( row.nTr );
7973 }
8234 }
7974 } );
8235 } );
7975 } );
8236 } );
7976
8237
7977 // Column visibility change - update the colspan
8238 // Column visibility change - update the colspan
7978 api.on( colvisEvent, function ( e, ctx, idx, vis ) {
8239 api.on( colvisEvent, function ( e, ctx, idx, vis ) {
7979 if ( settings !== ctx ) {
8240 if ( settings !== ctx ) {
7980 return;
8241 return;
7981 }
8242 }
7982
8243
7983 // Update the colspan for the details rows (note, only if it already has
8244 // Update the colspan for the details rows (note, only if it already has
7984 // a colspan)
8245 // a colspan)
7985 var row, visible = _fnVisbleColumns( ctx );
8246 var row, visible = _fnVisbleColumns( ctx );
7986
8247
7987 for ( var i=0, ien=data.length ; i<ien ; i++ ) {
8248 for ( var i=0, ien=data.length ; i<ien ; i++ ) {
7988 row = data[i];
8249 row = data[i];
7989
8250
7990 if ( row._details ) {
8251 if ( row._details ) {
7991 row._details.children('td[colspan]').attr('colspan', visible );
8252 row._details.children('td[colspan]').attr('colspan', visible );
7992 }
8253 }
7993 }
8254 }
7994 } );
8255 } );
7995
8256
7996 // Table destroyed - nuke any child rows
8257 // Table destroyed - nuke any child rows
7997 api.on( destroyEvent, function ( e, ctx ) {
8258 api.on( destroyEvent, function ( e, ctx ) {
7998 if ( settings !== ctx ) {
8259 if ( settings !== ctx ) {
7999 return;
8260 return;
8000 }
8261 }
8001
8262
8002 for ( var i=0, ien=data.length ; i<ien ; i++ ) {
8263 for ( var i=0, ien=data.length ; i<ien ; i++ ) {
8003 if ( data[i]._details ) {
8264 if ( data[i]._details ) {
8004 __details_remove( api, i );
8265 __details_remove( api, i );
@@ -8007,19 +8268,19 b''
8007 } );
8268 } );
8008 }
8269 }
8009 };
8270 };
8010
8271
8011 // Strings for the method names to help minification
8272 // Strings for the method names to help minification
8012 var _emp = '';
8273 var _emp = '';
8013 var _child_obj = _emp+'row().child';
8274 var _child_obj = _emp+'row().child';
8014 var _child_mth = _child_obj+'()';
8275 var _child_mth = _child_obj+'()';
8015
8276
8016 // data can be:
8277 // data can be:
8017 // tr
8278 // tr
8018 // string
8279 // string
8019 // jQuery or array of any of the above
8280 // jQuery or array of any of the above
8020 _api_register( _child_mth, function ( data, klass ) {
8281 _api_register( _child_mth, function ( data, klass ) {
8021 var ctx = this.context;
8282 var ctx = this.context;
8022
8283
8023 if ( data === undefined ) {
8284 if ( data === undefined ) {
8024 // get
8285 // get
8025 return ctx.length && this.length ?
8286 return ctx.length && this.length ?
@@ -8038,11 +8299,11 b''
8038 // set
8299 // set
8039 __details_add( ctx[0], ctx[0].aoData[ this[0] ], data, klass );
8300 __details_add( ctx[0], ctx[0].aoData[ this[0] ], data, klass );
8040 }
8301 }
8041
8302
8042 return this;
8303 return this;
8043 } );
8304 } );
8044
8305
8045
8306
8046 _api_register( [
8307 _api_register( [
8047 _child_obj+'.show()',
8308 _child_obj+'.show()',
8048 _child_mth+'.show()' // only when `child()` was called with parameters (without
8309 _child_mth+'.show()' // only when `child()` was called with parameters (without
@@ -8050,8 +8311,8 b''
8050 __details_display( this, true );
8311 __details_display( this, true );
8051 return this;
8312 return this;
8052 } );
8313 } );
8053
8314
8054
8315
8055 _api_register( [
8316 _api_register( [
8056 _child_obj+'.hide()',
8317 _child_obj+'.hide()',
8057 _child_mth+'.hide()' // only when `child()` was called with parameters (without
8318 _child_mth+'.hide()' // only when `child()` was called with parameters (without
@@ -8059,8 +8320,8 b''
8059 __details_display( this, false );
8320 __details_display( this, false );
8060 return this;
8321 return this;
8061 } );
8322 } );
8062
8323
8063
8324
8064 _api_register( [
8325 _api_register( [
8065 _child_obj+'.remove()',
8326 _child_obj+'.remove()',
8066 _child_mth+'.remove()' // only when `child()` was called with parameters (without
8327 _child_mth+'.remove()' // only when `child()` was called with parameters (without
@@ -8068,20 +8329,20 b''
8068 __details_remove( this );
8329 __details_remove( this );
8069 return this;
8330 return this;
8070 } );
8331 } );
8071
8332
8072
8333
8073 _api_register( _child_obj+'.isShown()', function () {
8334 _api_register( _child_obj+'.isShown()', function () {
8074 var ctx = this.context;
8335 var ctx = this.context;
8075
8336
8076 if ( ctx.length && this.length ) {
8337 if ( ctx.length && this.length ) {
8077 // _detailsShown as false or undefined will fall through to return false
8338 // _detailsShown as false or undefined will fall through to return false
8078 return ctx[0].aoData[ this[0] ]._detailsShow || false;
8339 return ctx[0].aoData[ this[0] ]._detailsShow || false;
8079 }
8340 }
8080 return false;
8341 return false;
8081 } );
8342 } );
8082
8343
8083
8344
8084
8345
8085 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8346 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8086 * Columns
8347 * Columns
8087 *
8348 *
@@ -8092,13 +8353,13 b''
8092 * "{string}" - jQuery selector on column header nodes
8353 * "{string}" - jQuery selector on column header nodes
8093 *
8354 *
8094 */
8355 */
8095
8356
8096 // can be an array of these items, comma separated list, or an array of comma
8357 // can be an array of these items, comma separated list, or an array of comma
8097 // separated lists
8358 // separated lists
8098
8359
8099 var __re_column_selector = /^(.+):(name|visIdx|visible)$/;
8360 var __re_column_selector = /^([^:]+):(name|visIdx|visible)$/;
8100
8361
8101
8362
8102 // r1 and r2 are redundant - but it means that the parameters match for the
8363 // r1 and r2 are redundant - but it means that the parameters match for the
8103 // iterator callback in columns().data()
8364 // iterator callback in columns().data()
8104 var __columnData = function ( settings, column, r1, r2, rows ) {
8365 var __columnData = function ( settings, column, r1, r2, rows ) {
@@ -8108,23 +8369,23 b''
8108 }
8369 }
8109 return a;
8370 return a;
8110 };
8371 };
8111
8372
8112
8373
8113 var __column_selector = function ( settings, selector, opts )
8374 var __column_selector = function ( settings, selector, opts )
8114 {
8375 {
8115 var
8376 var
8116 columns = settings.aoColumns,
8377 columns = settings.aoColumns,
8117 names = _pluck( columns, 'sName' ),
8378 names = _pluck( columns, 'sName' ),
8118 nodes = _pluck( columns, 'nTh' );
8379 nodes = _pluck( columns, 'nTh' );
8119
8380
8120 return _selector_run( selector, function ( s ) {
8381 var run = function ( s ) {
8121 var selInt = _intVal( s );
8382 var selInt = _intVal( s );
8122
8383
8123 // Selector - all
8384 // Selector - all
8124 if ( s === '' ) {
8385 if ( s === '' ) {
8125 return _range( columns.length );
8386 return _range( columns.length );
8126 }
8387 }
8127
8388
8128 // Selector - index
8389 // Selector - index
8129 if ( selInt !== null ) {
8390 if ( selInt !== null ) {
8130 return [ selInt >= 0 ?
8391 return [ selInt >= 0 ?
@@ -8132,11 +8393,11 b''
8132 columns.length + selInt // Count from right (+ because its a negative value)
8393 columns.length + selInt // Count from right (+ because its a negative value)
8133 ];
8394 ];
8134 }
8395 }
8135
8396
8136 // Selector = function
8397 // Selector = function
8137 if ( typeof s === 'function' ) {
8398 if ( typeof s === 'function' ) {
8138 var rows = _selector_row_indexes( settings, opts );
8399 var rows = _selector_row_indexes( settings, opts );
8139
8400
8140 return $.map( columns, function (col, idx) {
8401 return $.map( columns, function (col, idx) {
8141 return s(
8402 return s(
8142 idx,
8403 idx,
@@ -8145,12 +8406,12 b''
8145 ) ? idx : null;
8406 ) ? idx : null;
8146 } );
8407 } );
8147 }
8408 }
8148
8409
8149 // jQuery or string selector
8410 // jQuery or string selector
8150 var match = typeof s === 'string' ?
8411 var match = typeof s === 'string' ?
8151 s.match( __re_column_selector ) :
8412 s.match( __re_column_selector ) :
8152 '';
8413 '';
8153
8414
8154 if ( match ) {
8415 if ( match ) {
8155 switch( match[2] ) {
8416 switch( match[2] ) {
8156 case 'visIdx':
8417 case 'visIdx':
@@ -8166,54 +8427,74 b''
8166 }
8427 }
8167 // Counting from the left
8428 // Counting from the left
8168 return [ _fnVisibleToColumnIndex( settings, idx ) ];
8429 return [ _fnVisibleToColumnIndex( settings, idx ) ];
8169
8430
8170 case 'name':
8431 case 'name':
8171 // match by name. `names` is column index complete and in order
8432 // match by name. `names` is column index complete and in order
8172 return $.map( names, function (name, i) {
8433 return $.map( names, function (name, i) {
8173 return name === match[1] ? i : null;
8434 return name === match[1] ? i : null;
8174 } );
8435 } );
8175 }
8436
8176 }
8437 default:
8177 else {
8438 return [];
8178 // jQuery selector on the TH elements for the columns
8439 }
8179 return $( nodes )
8440 }
8180 .filter( s )
8441
8181 .map( function () {
8442 // Cell in the table body
8182 return $.inArray( this, nodes ); // `nodes` is column index complete and in order
8443 if ( s.nodeName && s._DT_CellIndex ) {
8183 } )
8444 return [ s._DT_CellIndex.column ];
8184 .toArray();
8445 }
8185 }
8446
8186 } );
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
8469
8189
8470
8190 var __setColumnVis = function ( settings, column, vis, recalc ) {
8471 var __setColumnVis = function ( settings, column, vis ) {
8191 var
8472 var
8192 cols = settings.aoColumns,
8473 cols = settings.aoColumns,
8193 col = cols[ column ],
8474 col = cols[ column ],
8194 data = settings.aoData,
8475 data = settings.aoData,
8195 row, cells, i, ien, tr;
8476 row, cells, i, ien, tr;
8196
8477
8197 // Get
8478 // Get
8198 if ( vis === undefined ) {
8479 if ( vis === undefined ) {
8199 return col.bVisible;
8480 return col.bVisible;
8200 }
8481 }
8201
8482
8202 // Set
8483 // Set
8203 // No change
8484 // No change
8204 if ( col.bVisible === vis ) {
8485 if ( col.bVisible === vis ) {
8205 return;
8486 return;
8206 }
8487 }
8207
8488
8208 if ( vis ) {
8489 if ( vis ) {
8209 // Insert column
8490 // Insert column
8210 // Need to decide if we should use appendChild or insertBefore
8491 // Need to decide if we should use appendChild or insertBefore
8211 var insertBefore = $.inArray( true, _pluck(cols, 'bVisible'), column+1 );
8492 var insertBefore = $.inArray( true, _pluck(cols, 'bVisible'), column+1 );
8212
8493
8213 for ( i=0, ien=data.length ; i<ien ; i++ ) {
8494 for ( i=0, ien=data.length ; i<ien ; i++ ) {
8214 tr = data[i].nTr;
8495 tr = data[i].nTr;
8215 cells = data[i].anCells;
8496 cells = data[i].anCells;
8216
8497
8217 if ( tr ) {
8498 if ( tr ) {
8218 // insertBefore can act like appendChild if 2nd arg is null
8499 // insertBefore can act like appendChild if 2nd arg is null
8219 tr.insertBefore( cells[ column ], cells[ insertBefore ] || null );
8500 tr.insertBefore( cells[ column ], cells[ insertBefore ] || null );
@@ -8224,31 +8505,16 b''
8224 // Remove column
8505 // Remove column
8225 $( _pluck( settings.aoData, 'anCells', column ) ).detach();
8506 $( _pluck( settings.aoData, 'anCells', column ) ).detach();
8226 }
8507 }
8227
8508
8228 // Common actions
8509 // Common actions
8229 col.bVisible = vis;
8510 col.bVisible = vis;
8230 _fnDrawHead( settings, settings.aoHeader );
8511 _fnDrawHead( settings, settings.aoHeader );
8231 _fnDrawHead( settings, settings.aoFooter );
8512 _fnDrawHead( settings, settings.aoFooter );
8232
8513
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
8245 _fnSaveState( settings );
8514 _fnSaveState( settings );
8246 };
8515 };
8247
8516
8248
8517
8249 /**
8250 *
8251 */
8252 _api_register( 'columns()', function ( selector, opts ) {
8518 _api_register( 'columns()', function ( selector, opts ) {
8253 // argument shifting
8519 // argument shifting
8254 if ( selector === undefined ) {
8520 if ( selector === undefined ) {
@@ -8258,56 +8524,42 b''
8258 opts = selector;
8524 opts = selector;
8259 selector = '';
8525 selector = '';
8260 }
8526 }
8261
8527
8262 opts = _selector_opts( opts );
8528 opts = _selector_opts( opts );
8263
8529
8264 var inst = this.iterator( 'table', function ( settings ) {
8530 var inst = this.iterator( 'table', function ( settings ) {
8265 return __column_selector( settings, selector, opts );
8531 return __column_selector( settings, selector, opts );
8266 }, 1 );
8532 }, 1 );
8267
8533
8268 // Want argument shifting here and in _row_selector?
8534 // Want argument shifting here and in _row_selector?
8269 inst.selector.cols = selector;
8535 inst.selector.cols = selector;
8270 inst.selector.opts = opts;
8536 inst.selector.opts = opts;
8271
8537
8272 return inst;
8538 return inst;
8273 } );
8539 } );
8274
8540
8275
8276 /**
8277 *
8278 */
8279 _api_registerPlural( 'columns().header()', 'column().header()', function ( selector, opts ) {
8541 _api_registerPlural( 'columns().header()', 'column().header()', function ( selector, opts ) {
8280 return this.iterator( 'column', function ( settings, column ) {
8542 return this.iterator( 'column', function ( settings, column ) {
8281 return settings.aoColumns[column].nTh;
8543 return settings.aoColumns[column].nTh;
8282 }, 1 );
8544 }, 1 );
8283 } );
8545 } );
8284
8546
8285
8286 /**
8287 *
8288 */
8289 _api_registerPlural( 'columns().footer()', 'column().footer()', function ( selector, opts ) {
8547 _api_registerPlural( 'columns().footer()', 'column().footer()', function ( selector, opts ) {
8290 return this.iterator( 'column', function ( settings, column ) {
8548 return this.iterator( 'column', function ( settings, column ) {
8291 return settings.aoColumns[column].nTf;
8549 return settings.aoColumns[column].nTf;
8292 }, 1 );
8550 }, 1 );
8293 } );
8551 } );
8294
8552
8295
8296 /**
8297 *
8298 */
8299 _api_registerPlural( 'columns().data()', 'column().data()', function () {
8553 _api_registerPlural( 'columns().data()', 'column().data()', function () {
8300 return this.iterator( 'column-rows', __columnData, 1 );
8554 return this.iterator( 'column-rows', __columnData, 1 );
8301 } );
8555 } );
8302
8556
8303
8304 _api_registerPlural( 'columns().dataSrc()', 'column().dataSrc()', function () {
8557 _api_registerPlural( 'columns().dataSrc()', 'column().dataSrc()', function () {
8305 return this.iterator( 'column', function ( settings, column ) {
8558 return this.iterator( 'column', function ( settings, column ) {
8306 return settings.aoColumns[column].mData;
8559 return settings.aoColumns[column].mData;
8307 }, 1 );
8560 }, 1 );
8308 } );
8561 } );
8309
8562
8310
8311 _api_registerPlural( 'columns().cache()', 'column().cache()', function ( type ) {
8563 _api_registerPlural( 'columns().cache()', 'column().cache()', function ( type ) {
8312 return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
8564 return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
8313 return _pluck_order( settings.aoData, rows,
8565 return _pluck_order( settings.aoData, rows,
@@ -8315,27 +8567,36 b''
8315 );
8567 );
8316 }, 1 );
8568 }, 1 );
8317 } );
8569 } );
8318
8570
8319
8320 _api_registerPlural( 'columns().nodes()', 'column().nodes()', function () {
8571 _api_registerPlural( 'columns().nodes()', 'column().nodes()', function () {
8321 return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
8572 return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
8322 return _pluck_order( settings.aoData, rows, 'anCells', column ) ;
8573 return _pluck_order( settings.aoData, rows, 'anCells', column ) ;
8323 }, 1 );
8574 }, 1 );
8324 } );
8575 } );
8325
8576
8326
8327
8328 _api_registerPlural( 'columns().visible()', 'column().visible()', function ( vis, calc ) {
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 if ( vis === undefined ) {
8579 if ( vis === undefined ) {
8331 return settings.aoColumns[ column ].bVisible;
8580 return settings.aoColumns[ column ].bVisible;
8332 } // else
8581 } // else
8333 __setColumnVis( settings, column, vis, calc );
8582 __setColumnVis( settings, column, vis );
8334 } );
8583 } );
8335 } );
8584
8336
8585 // Group the column visibility changes
8337
8586 if ( vis !== undefined ) {
8338
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 _api_registerPlural( 'columns().indexes()', 'column().index()', function ( type ) {
8600 _api_registerPlural( 'columns().indexes()', 'column().index()', function ( type ) {
8340 return this.iterator( 'column', function ( settings, column ) {
8601 return this.iterator( 'column', function ( settings, column ) {
8341 return type === 'visible' ?
8602 return type === 'visible' ?
@@ -8343,33 +8604,17 b''
8343 column;
8604 column;
8344 }, 1 );
8605 }, 1 );
8345 } );
8606 } );
8346
8607
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
8361 _api_register( 'columns.adjust()', function () {
8608 _api_register( 'columns.adjust()', function () {
8362 return this.iterator( 'table', function ( settings ) {
8609 return this.iterator( 'table', function ( settings ) {
8363 _fnAdjustColumnSizing( settings );
8610 _fnAdjustColumnSizing( settings );
8364 }, 1 );
8611 }, 1 );
8365 } );
8612 } );
8366
8613
8367
8368 // Convert from one column index type, to another type
8369 _api_register( 'column.index()', function ( type, idx ) {
8614 _api_register( 'column.index()', function ( type, idx ) {
8370 if ( this.context.length !== 0 ) {
8615 if ( this.context.length !== 0 ) {
8371 var ctx = this.context[0];
8616 var ctx = this.context[0];
8372
8617
8373 if ( type === 'fromVisible' || type === 'toData' ) {
8618 if ( type === 'fromVisible' || type === 'toData' ) {
8374 return _fnVisibleToColumnIndex( ctx, idx );
8619 return _fnVisibleToColumnIndex( ctx, idx );
8375 }
8620 }
@@ -8378,15 +8623,13 b''
8378 }
8623 }
8379 }
8624 }
8380 } );
8625 } );
8381
8626
8382
8383 _api_register( 'column()', function ( selector, opts ) {
8627 _api_register( 'column()', function ( selector, opts ) {
8384 return _selector_first( this.columns( selector, opts ) );
8628 return _selector_first( this.columns( selector, opts ) );
8385 } );
8629 } );
8386
8630
8387
8631
8388
8632
8389
8390 var __cell_selector = function ( settings, selector, opts )
8633 var __cell_selector = function ( settings, selector, opts )
8391 {
8634 {
8392 var data = settings.aoData;
8635 var data = settings.aoData;
@@ -8396,28 +8639,28 b''
8396 var row;
8639 var row;
8397 var columns = settings.aoColumns.length;
8640 var columns = settings.aoColumns.length;
8398 var a, i, ien, j, o, host;
8641 var a, i, ien, j, o, host;
8399
8642
8400 return _selector_run( selector, function ( s ) {
8643 var run = function ( s ) {
8401 var fnSelector = typeof s === 'function';
8644 var fnSelector = typeof s === 'function';
8402
8645
8403 if ( s === null || s === undefined || fnSelector ) {
8646 if ( s === null || s === undefined || fnSelector ) {
8404 // All cells and function selectors
8647 // All cells and function selectors
8405 a = [];
8648 a = [];
8406
8649
8407 for ( i=0, ien=rows.length ; i<ien ; i++ ) {
8650 for ( i=0, ien=rows.length ; i<ien ; i++ ) {
8408 row = rows[i];
8651 row = rows[i];
8409
8652
8410 for ( j=0 ; j<columns ; j++ ) {
8653 for ( j=0 ; j<columns ; j++ ) {
8411 o = {
8654 o = {
8412 row: row,
8655 row: row,
8413 column: j
8656 column: j
8414 };
8657 };
8415
8658
8416 if ( fnSelector ) {
8659 if ( fnSelector ) {
8417 // Selector - function
8660 // Selector - function
8418 host = settings.aoData[ row ];
8661 host = data[ row ];
8419
8662
8420 if ( s( o, _fnGetCellData(settings, row, j), host.anCells[j] ) ) {
8663 if ( s( o, _fnGetCellData(settings, row, j), host.anCells ? host.anCells[j] : null ) ) {
8421 a.push( o );
8664 a.push( o );
8422 }
8665 }
8423 }
8666 }
@@ -8427,66 +8670,83 b''
8427 }
8670 }
8428 }
8671 }
8429 }
8672 }
8430
8673
8431 return a;
8674 return a;
8432 }
8675 }
8433
8676
8434 // Selector - index
8677 // Selector - index
8435 if ( $.isPlainObject( s ) ) {
8678 if ( $.isPlainObject( s ) ) {
8436 return [s];
8679 return [s];
8437 }
8680 }
8438
8681
8439 // Selector - jQuery filtered cells
8682 // Selector - jQuery filtered cells
8440 return allCells
8683 var jqResult = allCells
8441 .filter( s )
8684 .filter( s )
8442 .map( function (i, el) {
8685 .map( function (i, el) {
8443 row = el.parentNode._DT_RowIndex;
8686 return { // use a new object, in case someone changes the values
8444
8687 row: el._DT_CellIndex.row,
8445 return {
8688 column: el._DT_CellIndex.column
8446 row: row,
8689 };
8447 column: $.inArray( el, data[ row ].anCells )
8448 };
8449 } )
8690 } )
8450 .toArray();
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
8711
8454
8712
8455
8713
8456
8714
8457 _api_register( 'cells()', function ( rowSelector, columnSelector, opts ) {
8715 _api_register( 'cells()', function ( rowSelector, columnSelector, opts ) {
8458 // Argument shifting
8716 // Argument shifting
8459 if ( $.isPlainObject( rowSelector ) ) {
8717 if ( $.isPlainObject( rowSelector ) ) {
8460 // Indexes
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 opts = columnSelector;
8726 opts = columnSelector;
8463 columnSelector = null;
8727 columnSelector = null;
8464 }
8728 }
8465 else {
8466 opts = rowSelector;
8467 rowSelector = null;
8468 }
8469 }
8729 }
8470 if ( $.isPlainObject( columnSelector ) ) {
8730 if ( $.isPlainObject( columnSelector ) ) {
8471 opts = columnSelector;
8731 opts = columnSelector;
8472 columnSelector = null;
8732 columnSelector = null;
8473 }
8733 }
8474
8734
8475 // Cell selector
8735 // Cell selector
8476 if ( columnSelector === null || columnSelector === undefined ) {
8736 if ( columnSelector === null || columnSelector === undefined ) {
8477 return this.iterator( 'table', function ( settings ) {
8737 return this.iterator( 'table', function ( settings ) {
8478 return __cell_selector( settings, rowSelector, _selector_opts( opts ) );
8738 return __cell_selector( settings, rowSelector, _selector_opts( opts ) );
8479 } );
8739 } );
8480 }
8740 }
8481
8741
8482 // Row + column selector
8742 // Row + column selector
8483 var columns = this.columns( columnSelector, opts );
8743 var columns = this.columns( columnSelector, opts );
8484 var rows = this.rows( rowSelector, opts );
8744 var rows = this.rows( rowSelector, opts );
8485 var a, i, ien, j, jen;
8745 var a, i, ien, j, jen;
8486
8746
8487 var cells = this.iterator( 'table', function ( settings, idx ) {
8747 var cells = this.iterator( 'table', function ( settings, idx ) {
8488 a = [];
8748 a = [];
8489
8749
8490 for ( i=0, ien=rows[idx].length ; i<ien ; i++ ) {
8750 for ( i=0, ien=rows[idx].length ; i<ien ; i++ ) {
8491 for ( j=0, jen=columns[idx].length ; j<jen ; j++ ) {
8751 for ( j=0, jen=columns[idx].length ; j<jen ; j++ ) {
8492 a.push( {
8752 a.push( {
@@ -8495,53 +8755,54 b''
8495 } );
8755 } );
8496 }
8756 }
8497 }
8757 }
8498
8758
8499 return a;
8759 return a;
8500 }, 1 );
8760 }, 1 );
8501
8761
8502 $.extend( cells.selector, {
8762 $.extend( cells.selector, {
8503 cols: columnSelector,
8763 cols: columnSelector,
8504 rows: rowSelector,
8764 rows: rowSelector,
8505 opts: opts
8765 opts: opts
8506 } );
8766 } );
8507
8767
8508 return cells;
8768 return cells;
8509 } );
8769 } );
8510
8770
8511
8771
8512 _api_registerPlural( 'cells().nodes()', 'cell().node()', function () {
8772 _api_registerPlural( 'cells().nodes()', 'cell().node()', function () {
8513 return this.iterator( 'cell', function ( settings, row, column ) {
8773 return this.iterator( 'cell', function ( settings, row, column ) {
8514 var cells = settings.aoData[ row ].anCells;
8774 var data = settings.aoData[ row ];
8515 return cells ?
8775
8516 cells[ column ] :
8776 return data && data.anCells ?
8777 data.anCells[ column ] :
8517 undefined;
8778 undefined;
8518 }, 1 );
8779 }, 1 );
8519 } );
8780 } );
8520
8781
8521
8782
8522 _api_register( 'cells().data()', function () {
8783 _api_register( 'cells().data()', function () {
8523 return this.iterator( 'cell', function ( settings, row, column ) {
8784 return this.iterator( 'cell', function ( settings, row, column ) {
8524 return _fnGetCellData( settings, row, column );
8785 return _fnGetCellData( settings, row, column );
8525 }, 1 );
8786 }, 1 );
8526 } );
8787 } );
8527
8788
8528
8789
8529 _api_registerPlural( 'cells().cache()', 'cell().cache()', function ( type ) {
8790 _api_registerPlural( 'cells().cache()', 'cell().cache()', function ( type ) {
8530 type = type === 'search' ? '_aFilterData' : '_aSortData';
8791 type = type === 'search' ? '_aFilterData' : '_aSortData';
8531
8792
8532 return this.iterator( 'cell', function ( settings, row, column ) {
8793 return this.iterator( 'cell', function ( settings, row, column ) {
8533 return settings.aoData[ row ][ type ][ column ];
8794 return settings.aoData[ row ][ type ][ column ];
8534 }, 1 );
8795 }, 1 );
8535 } );
8796 } );
8536
8797
8537
8798
8538 _api_registerPlural( 'cells().render()', 'cell().render()', function ( type ) {
8799 _api_registerPlural( 'cells().render()', 'cell().render()', function ( type ) {
8539 return this.iterator( 'cell', function ( settings, row, column ) {
8800 return this.iterator( 'cell', function ( settings, row, column ) {
8540 return _fnGetCellData( settings, row, column, type );
8801 return _fnGetCellData( settings, row, column, type );
8541 }, 1 );
8802 }, 1 );
8542 } );
8803 } );
8543
8804
8544
8805
8545 _api_registerPlural( 'cells().indexes()', 'cell().index()', function () {
8806 _api_registerPlural( 'cells().indexes()', 'cell().index()', function () {
8546 return this.iterator( 'cell', function ( settings, row, column ) {
8807 return this.iterator( 'cell', function ( settings, row, column ) {
8547 return {
8808 return {
@@ -8551,41 +8812,41 b''
8551 };
8812 };
8552 }, 1 );
8813 }, 1 );
8553 } );
8814 } );
8554
8815
8555
8816
8556 _api_registerPlural( 'cells().invalidate()', 'cell().invalidate()', function ( src ) {
8817 _api_registerPlural( 'cells().invalidate()', 'cell().invalidate()', function ( src ) {
8557 return this.iterator( 'cell', function ( settings, row, column ) {
8818 return this.iterator( 'cell', function ( settings, row, column ) {
8558 _fnInvalidate( settings, row, src, column );
8819 _fnInvalidate( settings, row, src, column );
8559 } );
8820 } );
8560 } );
8821 } );
8561
8822
8562
8823
8563
8824
8564 _api_register( 'cell()', function ( rowSelector, columnSelector, opts ) {
8825 _api_register( 'cell()', function ( rowSelector, columnSelector, opts ) {
8565 return _selector_first( this.cells( rowSelector, columnSelector, opts ) );
8826 return _selector_first( this.cells( rowSelector, columnSelector, opts ) );
8566 } );
8827 } );
8567
8828
8568
8829
8569 _api_register( 'cell().data()', function ( data ) {
8830 _api_register( 'cell().data()', function ( data ) {
8570 var ctx = this.context;
8831 var ctx = this.context;
8571 var cell = this[0];
8832 var cell = this[0];
8572
8833
8573 if ( data === undefined ) {
8834 if ( data === undefined ) {
8574 // Get
8835 // Get
8575 return ctx.length && cell.length ?
8836 return ctx.length && cell.length ?
8576 _fnGetCellData( ctx[0], cell[0].row, cell[0].column ) :
8837 _fnGetCellData( ctx[0], cell[0].row, cell[0].column ) :
8577 undefined;
8838 undefined;
8578 }
8839 }
8579
8840
8580 // Set
8841 // Set
8581 _fnSetCellData( ctx[0], cell[0].row, cell[0].column, data );
8842 _fnSetCellData( ctx[0], cell[0].row, cell[0].column, data );
8582 _fnInvalidate( ctx[0], cell[0].row, 'data', cell[0].column );
8843 _fnInvalidate( ctx[0], cell[0].row, 'data', cell[0].column );
8583
8844
8584 return this;
8845 return this;
8585 } );
8846 } );
8586
8847
8587
8848
8588
8849
8589 /**
8850 /**
8590 * Get current ordering (sorting) that has been applied to the table.
8851 * Get current ordering (sorting) that has been applied to the table.
8591 *
8852 *
@@ -8616,31 +8877,31 b''
8616 */
8877 */
8617 _api_register( 'order()', function ( order, dir ) {
8878 _api_register( 'order()', function ( order, dir ) {
8618 var ctx = this.context;
8879 var ctx = this.context;
8619
8880
8620 if ( order === undefined ) {
8881 if ( order === undefined ) {
8621 // get
8882 // get
8622 return ctx.length !== 0 ?
8883 return ctx.length !== 0 ?
8623 ctx[0].aaSorting :
8884 ctx[0].aaSorting :
8624 undefined;
8885 undefined;
8625 }
8886 }
8626
8887
8627 // set
8888 // set
8628 if ( typeof order === 'number' ) {
8889 if ( typeof order === 'number' ) {
8629 // Simple column / direction passed in
8890 // Simple column / direction passed in
8630 order = [ [ order, dir ] ];
8891 order = [ [ order, dir ] ];
8631 }
8892 }
8632 else if ( ! $.isArray( order[0] ) ) {
8893 else if ( order.length && ! $.isArray( order[0] ) ) {
8633 // Arguments passed in (list of 1D arrays)
8894 // Arguments passed in (list of 1D arrays)
8634 order = Array.prototype.slice.call( arguments );
8895 order = Array.prototype.slice.call( arguments );
8635 }
8896 }
8636 // otherwise a 2D array was passed in
8897 // otherwise a 2D array was passed in
8637
8898
8638 return this.iterator( 'table', function ( settings ) {
8899 return this.iterator( 'table', function ( settings ) {
8639 settings.aaSorting = order.slice();
8900 settings.aaSorting = order.slice();
8640 } );
8901 } );
8641 } );
8902 } );
8642
8903
8643
8904
8644 /**
8905 /**
8645 * Attach a sort listener to an element for a given column
8906 * Attach a sort listener to an element for a given column
8646 *
8907 *
@@ -8656,44 +8917,62 b''
8656 _fnSortAttachListener( settings, node, column, callback );
8917 _fnSortAttachListener( settings, node, column, callback );
8657 } );
8918 } );
8658 } );
8919 } );
8659
8920
8660
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 // Order by the selected column(s)
8940 // Order by the selected column(s)
8662 _api_register( [
8941 _api_register( [
8663 'columns().order()',
8942 'columns().order()',
8664 'column().order()'
8943 'column().order()'
8665 ], function ( dir ) {
8944 ], function ( dir ) {
8666 var that = this;
8945 var that = this;
8667
8946
8668 return this.iterator( 'table', function ( settings, i ) {
8947 return this.iterator( 'table', function ( settings, i ) {
8669 var sort = [];
8948 var sort = [];
8670
8949
8671 $.each( that[i], function (j, col) {
8950 $.each( that[i], function (j, col) {
8672 sort.push( [ col, dir ] );
8951 sort.push( [ col, dir ] );
8673 } );
8952 } );
8674
8953
8675 settings.aaSorting = sort;
8954 settings.aaSorting = sort;
8676 } );
8955 } );
8677 } );
8956 } );
8678
8957
8679
8958
8680
8959
8681 _api_register( 'search()', function ( input, regex, smart, caseInsen ) {
8960 _api_register( 'search()', function ( input, regex, smart, caseInsen ) {
8682 var ctx = this.context;
8961 var ctx = this.context;
8683
8962
8684 if ( input === undefined ) {
8963 if ( input === undefined ) {
8685 // get
8964 // get
8686 return ctx.length !== 0 ?
8965 return ctx.length !== 0 ?
8687 ctx[0].oPreviousSearch.sSearch :
8966 ctx[0].oPreviousSearch.sSearch :
8688 undefined;
8967 undefined;
8689 }
8968 }
8690
8969
8691 // set
8970 // set
8692 return this.iterator( 'table', function ( settings ) {
8971 return this.iterator( 'table', function ( settings ) {
8693 if ( ! settings.oFeatures.bFilter ) {
8972 if ( ! settings.oFeatures.bFilter ) {
8694 return;
8973 return;
8695 }
8974 }
8696
8975
8697 _fnFilterComplete( settings, $.extend( {}, settings.oPreviousSearch, {
8976 _fnFilterComplete( settings, $.extend( {}, settings.oPreviousSearch, {
8698 "sSearch": input+"",
8977 "sSearch": input+"",
8699 "bRegex": regex === null ? false : regex,
8978 "bRegex": regex === null ? false : regex,
@@ -8702,71 +8981,71 b''
8702 } ), 1 );
8981 } ), 1 );
8703 } );
8982 } );
8704 } );
8983 } );
8705
8984
8706
8985
8707 _api_registerPlural(
8986 _api_registerPlural(
8708 'columns().search()',
8987 'columns().search()',
8709 'column().search()',
8988 'column().search()',
8710 function ( input, regex, smart, caseInsen ) {
8989 function ( input, regex, smart, caseInsen ) {
8711 return this.iterator( 'column', function ( settings, column ) {
8990 return this.iterator( 'column', function ( settings, column ) {
8712 var preSearch = settings.aoPreSearchCols;
8991 var preSearch = settings.aoPreSearchCols;
8713
8992
8714 if ( input === undefined ) {
8993 if ( input === undefined ) {
8715 // get
8994 // get
8716 return preSearch[ column ].sSearch;
8995 return preSearch[ column ].sSearch;
8717 }
8996 }
8718
8997
8719 // set
8998 // set
8720 if ( ! settings.oFeatures.bFilter ) {
8999 if ( ! settings.oFeatures.bFilter ) {
8721 return;
9000 return;
8722 }
9001 }
8723
9002
8724 $.extend( preSearch[ column ], {
9003 $.extend( preSearch[ column ], {
8725 "sSearch": input+"",
9004 "sSearch": input+"",
8726 "bRegex": regex === null ? false : regex,
9005 "bRegex": regex === null ? false : regex,
8727 "bSmart": smart === null ? true : smart,
9006 "bSmart": smart === null ? true : smart,
8728 "bCaseInsensitive": caseInsen === null ? true : caseInsen
9007 "bCaseInsensitive": caseInsen === null ? true : caseInsen
8729 } );
9008 } );
8730
9009
8731 _fnFilterComplete( settings, settings.oPreviousSearch, 1 );
9010 _fnFilterComplete( settings, settings.oPreviousSearch, 1 );
8732 } );
9011 } );
8733 }
9012 }
8734 );
9013 );
8735
9014
8736 /*
9015 /*
8737 * State API methods
9016 * State API methods
8738 */
9017 */
8739
9018
8740 _api_register( 'state()', function () {
9019 _api_register( 'state()', function () {
8741 return this.context.length ?
9020 return this.context.length ?
8742 this.context[0].oSavedState :
9021 this.context[0].oSavedState :
8743 null;
9022 null;
8744 } );
9023 } );
8745
9024
8746
9025
8747 _api_register( 'state.clear()', function () {
9026 _api_register( 'state.clear()', function () {
8748 return this.iterator( 'table', function ( settings ) {
9027 return this.iterator( 'table', function ( settings ) {
8749 // Save an empty object
9028 // Save an empty object
8750 settings.fnStateSaveCallback.call( settings.oInstance, settings, {} );
9029 settings.fnStateSaveCallback.call( settings.oInstance, settings, {} );
8751 } );
9030 } );
8752 } );
9031 } );
8753
9032
8754
9033
8755 _api_register( 'state.loaded()', function () {
9034 _api_register( 'state.loaded()', function () {
8756 return this.context.length ?
9035 return this.context.length ?
8757 this.context[0].oLoadedState :
9036 this.context[0].oLoadedState :
8758 null;
9037 null;
8759 } );
9038 } );
8760
9039
8761
9040
8762 _api_register( 'state.save()', function () {
9041 _api_register( 'state.save()', function () {
8763 return this.iterator( 'table', function ( settings ) {
9042 return this.iterator( 'table', function ( settings ) {
8764 _fnSaveState( settings );
9043 _fnSaveState( settings );
8765 } );
9044 } );
8766 } );
9045 } );
8767
9046
8768
9047
8769
9048
8770 /**
9049 /**
8771 * Provide a common method for plug-ins to check the version of DataTables being
9050 * Provide a common method for plug-ins to check the version of DataTables being
8772 * used, in order to ensure compatibility.
9051 * used, in order to ensure compatibility.
@@ -8787,24 +9066,24 b''
8787 var aThis = DataTable.version.split('.');
9066 var aThis = DataTable.version.split('.');
8788 var aThat = version.split('.');
9067 var aThat = version.split('.');
8789 var iThis, iThat;
9068 var iThis, iThat;
8790
9069
8791 for ( var i=0, iLen=aThat.length ; i<iLen ; i++ ) {
9070 for ( var i=0, iLen=aThat.length ; i<iLen ; i++ ) {
8792 iThis = parseInt( aThis[i], 10 ) || 0;
9071 iThis = parseInt( aThis[i], 10 ) || 0;
8793 iThat = parseInt( aThat[i], 10 ) || 0;
9072 iThat = parseInt( aThat[i], 10 ) || 0;
8794
9073
8795 // Parts are the same, keep comparing
9074 // Parts are the same, keep comparing
8796 if (iThis === iThat) {
9075 if (iThis === iThat) {
8797 continue;
9076 continue;
8798 }
9077 }
8799
9078
8800 // Parts are different, return immediately
9079 // Parts are different, return immediately
8801 return iThis > iThat;
9080 return iThis > iThat;
8802 }
9081 }
8803
9082
8804 return true;
9083 return true;
8805 };
9084 };
8806
9085
8807
9086
8808 /**
9087 /**
8809 * Check if a `<table>` node is a DataTable table already or not.
9088 * Check if a `<table>` node is a DataTable table already or not.
8810 *
9089 *
@@ -8824,17 +9103,24 b''
8824 {
9103 {
8825 var t = $(table).get(0);
9104 var t = $(table).get(0);
8826 var is = false;
9105 var is = false;
8827
9106
9107 if ( table instanceof DataTable.Api ) {
9108 return true;
9109 }
9110
8828 $.each( DataTable.settings, function (i, o) {
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 is = true;
9116 is = true;
8831 }
9117 }
8832 } );
9118 } );
8833
9119
8834 return is;
9120 return is;
8835 };
9121 };
8836
9122
8837
9123
8838 /**
9124 /**
8839 * Get all DataTable tables that have been initialised - optionally you can
9125 * Get all DataTable tables that have been initialised - optionally you can
8840 * select to get only currently visible tables.
9126 * select to get only currently visible tables.
@@ -8853,46 +9139,25 b''
8853 */
9139 */
8854 DataTable.tables = DataTable.fnTables = function ( visible )
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 if ( !visible || (visible && $(o.nTable).is(':visible')) ) {
9150 if ( !visible || (visible && $(o.nTable).is(':visible')) ) {
8858 return o.nTable;
9151 return o.nTable;
8859 }
9152 }
8860 } );
9153 } );
9154
9155 return api ?
9156 new _Api( a ) :
9157 a;
8861 };
9158 };
8862
9159
8863
9160
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
8896 /**
9161 /**
8897 * Convert from camel case parameters to Hungarian notation. This is made public
9162 * Convert from camel case parameters to Hungarian notation. This is made public
8898 * for the extensions to provide the same ability as DataTables core to accept
9163 * for the extensions to provide the same ability as DataTables core to accept
@@ -8907,9 +9172,9 b''
8907 * won't be.
9172 * won't be.
8908 */
9173 */
8909 DataTable.camelToHungarian = _fnCamelToHungarian;
9174 DataTable.camelToHungarian = _fnCamelToHungarian;
8910
9175
8911
9176
8912
9177
8913 /**
9178 /**
8914 *
9179 *
8915 */
9180 */
@@ -8917,53 +9182,61 b''
8917 var
9182 var
8918 rows = this.rows( opts ).nodes(), // Get all rows
9183 rows = this.rows( opts ).nodes(), // Get all rows
8919 jqRows = $(rows);
9184 jqRows = $(rows);
8920
9185
8921 return $( [].concat(
9186 return $( [].concat(
8922 jqRows.filter( selector ).toArray(),
9187 jqRows.filter( selector ).toArray(),
8923 jqRows.find( selector ).toArray()
9188 jqRows.find( selector ).toArray()
8924 ) );
9189 ) );
8925 } );
9190 } );
8926
9191
8927
9192
8928 // jQuery functions to operate on the tables
9193 // jQuery functions to operate on the tables
8929 $.each( [ 'on', 'one', 'off' ], function (i, key) {
9194 $.each( [ 'on', 'one', 'off' ], function (i, key) {
8930 _api_register( key+'()', function ( /* event, handler */ ) {
9195 _api_register( key+'()', function ( /* event, handler */ ) {
8931 var args = Array.prototype.slice.call(arguments);
9196 var args = Array.prototype.slice.call(arguments);
8932
9197
8933 // Add the `dt` namespace automatically if it isn't already present
9198 // Add the `dt` namespace automatically if it isn't already present
8934 if ( ! args[0].match(/\.dt\b/) ) {
9199 args[0] = $.map( args[0].split( /\s/ ), function ( e ) {
8935 args[0] += '.dt';
9200 return ! e.match(/\.dt\b/) ?
8936 }
9201 e+'.dt' :
8937
9202 e;
9203 } ).join( ' ' );
9204
8938 var inst = $( this.tables().nodes() );
9205 var inst = $( this.tables().nodes() );
8939 inst[key].apply( inst, args );
9206 inst[key].apply( inst, args );
8940 return this;
9207 return this;
8941 } );
9208 } );
8942 } );
9209 } );
8943
9210
8944
9211
8945 _api_register( 'clear()', function () {
9212 _api_register( 'clear()', function () {
8946 return this.iterator( 'table', function ( settings ) {
9213 return this.iterator( 'table', function ( settings ) {
8947 _fnClearTable( settings );
9214 _fnClearTable( settings );
8948 } );
9215 } );
8949 } );
9216 } );
8950
9217
8951
9218
8952 _api_register( 'settings()', function () {
9219 _api_register( 'settings()', function () {
8953 return new _Api( this.context, this.context );
9220 return new _Api( this.context, this.context );
8954 } );
9221 } );
8955
9222
8956
9223
9224 _api_register( 'init()', function () {
9225 var ctx = this.context;
9226 return ctx.length ? ctx[0].oInit : null;
9227 } );
9228
9229
8957 _api_register( 'data()', function () {
9230 _api_register( 'data()', function () {
8958 return this.iterator( 'table', function ( settings ) {
9231 return this.iterator( 'table', function ( settings ) {
8959 return _pluck( settings.aoData, '_aData' );
9232 return _pluck( settings.aoData, '_aData' );
8960 } ).flatten();
9233 } ).flatten();
8961 } );
9234 } );
8962
9235
8963
9236
8964 _api_register( 'destroy()', function ( remove ) {
9237 _api_register( 'destroy()', function ( remove ) {
8965 remove = remove || false;
9238 remove = remove || false;
8966
9239
8967 return this.iterator( 'table', function ( settings ) {
9240 return this.iterator( 'table', function ( settings ) {
8968 var orig = settings.nTableWrapper.parentNode;
9241 var orig = settings.nTableWrapper.parentNode;
8969 var classes = settings.oClasses;
9242 var classes = settings.oClasses;
@@ -8976,50 +9249,46 b''
8976 var jqWrapper = $(settings.nTableWrapper);
9249 var jqWrapper = $(settings.nTableWrapper);
8977 var rows = $.map( settings.aoData, function (r) { return r.nTr; } );
9250 var rows = $.map( settings.aoData, function (r) { return r.nTr; } );
8978 var i, ien;
9251 var i, ien;
8979
9252
8980 // Flag to note that the table is currently being destroyed - no action
9253 // Flag to note that the table is currently being destroyed - no action
8981 // should be taken
9254 // should be taken
8982 settings.bDestroying = true;
9255 settings.bDestroying = true;
8983
9256
8984 // Fire off the destroy callbacks for plug-ins etc
9257 // Fire off the destroy callbacks for plug-ins etc
8985 _fnCallbackFire( settings, "aoDestroyCallback", "destroy", [settings] );
9258 _fnCallbackFire( settings, "aoDestroyCallback", "destroy", [settings] );
8986
9259
8987 // If not being removed from the document, make all columns visible
9260 // If not being removed from the document, make all columns visible
8988 if ( ! remove ) {
9261 if ( ! remove ) {
8989 new _Api( settings ).columns().visible( true );
9262 new _Api( settings ).columns().visible( true );
8990 }
9263 }
8991
9264
8992 // Blitz all `DT` namespaced events (these are internal events, the
9265 // Blitz all `DT` namespaced events (these are internal events, the
8993 // lowercase, `dt` events are user subscribed and they are responsible
9266 // lowercase, `dt` events are user subscribed and they are responsible
8994 // for removing them
9267 // for removing them
8995 jqWrapper.unbind('.DT').find(':not(tbody *)').unbind('.DT');
9268 jqWrapper.off('.DT').find(':not(tbody *)').off('.DT');
8996 $(window).unbind('.DT-'+settings.sInstance);
9269 $(window).off('.DT-'+settings.sInstance);
8997
9270
8998 // When scrolling we had to break the table up - restore it
9271 // When scrolling we had to break the table up - restore it
8999 if ( table != thead.parentNode ) {
9272 if ( table != thead.parentNode ) {
9000 jqTable.children('thead').detach();
9273 jqTable.children('thead').detach();
9001 jqTable.append( thead );
9274 jqTable.append( thead );
9002 }
9275 }
9003
9276
9004 if ( tfoot && table != tfoot.parentNode ) {
9277 if ( tfoot && table != tfoot.parentNode ) {
9005 jqTable.children('tfoot').detach();
9278 jqTable.children('tfoot').detach();
9006 jqTable.append( tfoot );
9279 jqTable.append( tfoot );
9007 }
9280 }
9008
9281
9009 // Remove the DataTables generated nodes, events and classes
9010 jqTable.detach();
9011 jqWrapper.detach();
9012
9013 settings.aaSorting = [];
9282 settings.aaSorting = [];
9014 settings.aaSortingFixed = [];
9283 settings.aaSortingFixed = [];
9015 _fnSortingClasses( settings );
9284 _fnSortingClasses( settings );
9016
9285
9017 $( rows ).removeClass( settings.asStripeClasses.join(' ') );
9286 $( rows ).removeClass( settings.asStripeClasses.join(' ') );
9018
9287
9019 $('th, td', thead).removeClass( classes.sSortable+' '+
9288 $('th, td', thead).removeClass( classes.sSortable+' '+
9020 classes.sSortableAsc+' '+classes.sSortableDesc+' '+classes.sSortableNone
9289 classes.sSortableAsc+' '+classes.sSortableDesc+' '+classes.sSortableNone
9021 );
9290 );
9022
9291
9023 if ( settings.bJUI ) {
9292 if ( settings.bJUI ) {
9024 $('th span.'+classes.sSortIcon+ ', td span.'+classes.sSortIcon, thead).detach();
9293 $('th span.'+classes.sSortIcon+ ', td span.'+classes.sSortIcon, thead).detach();
9025 $('th, td', thead).each( function () {
9294 $('th, td', thead).each( function () {
@@ -9028,33 +9297,39 b''
9028 wrapper.detach();
9297 wrapper.detach();
9029 } );
9298 } );
9030 }
9299 }
9031
9300
9032 if ( ! remove && orig ) {
9033 // insertBefore acts like appendChild if !arg[1]
9034 orig.insertBefore( table, settings.nTableReinsertBefore );
9035 }
9036
9037 // Add the TR elements back into the table in their original order
9301 // Add the TR elements back into the table in their original order
9038 jqTbody.children().detach();
9302 jqTbody.children().detach();
9039 jqTbody.append( rows );
9303 jqTbody.append( rows );
9040
9304
9041 // Restore the width of the original table - was read from the style property,
9305 // Remove the DataTables generated nodes, events and classes
9042 // so we can restore directly to that
9306 var removedMethod = remove ? 'remove' : 'detach';
9043 jqTable
9307 jqTable[ removedMethod ]();
9044 .css( 'width', settings.sDestroyWidth )
9308 jqWrapper[ removedMethod ]();
9045 .removeClass( classes.sTable );
9309
9046
9310 // If we need to reattach the table to the document
9047 // If the were originally stripe classes - then we add them back here.
9311 if ( ! remove && orig ) {
9048 // Note this is not fool proof (for example if not all rows had stripe
9312 // insertBefore acts like appendChild if !arg[1]
9049 // classes - but it's a good effort without getting carried away
9313 orig.insertBefore( table, settings.nTableReinsertBefore );
9050 ien = settings.asDestroyStripes.length;
9314
9051
9315 // Restore the width of the original table - was read from the style property,
9052 if ( ien ) {
9316 // so we can restore directly to that
9053 jqTbody.children().each( function (i) {
9317 jqTable
9054 $(this).addClass( settings.asDestroyStripes[i % ien] );
9318 .css( 'width', settings.sDestroyWidth )
9055 } );
9319 .removeClass( classes.sTable );
9056 }
9320
9057
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 /* Remove the settings object from the settings array */
9333 /* Remove the settings object from the settings array */
9059 var idx = $.inArray( settings, DataTable.settings );
9334 var idx = $.inArray( settings, DataTable.settings );
9060 if ( idx !== -1 ) {
9335 if ( idx !== -1 ) {
@@ -9062,7 +9337,56 b''
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 * Version string for plug-ins to check compatibility. Allowed format is
9392 * Version string for plug-ins to check compatibility. Allowed format is
@@ -9072,7 +9396,7 b''
9072 * @type string
9396 * @type string
9073 * @default Version number
9397 * @default Version number
9074 */
9398 */
9075 DataTable.version = "1.10.4";
9399 DataTable.version = "1.10.13";
9076
9400
9077 /**
9401 /**
9078 * Private data store, containing all of the settings objects that are
9402 * Private data store, containing all of the settings objects that are
@@ -9095,9 +9419,9 b''
9095 * @namespace
9419 * @namespace
9096 */
9420 */
9097 DataTable.models = {};
9421 DataTable.models = {};
9098
9422
9099
9423
9100
9424
9101 /**
9425 /**
9102 * Template object for the way in which DataTables holds information about
9426 * Template object for the way in which DataTables holds information about
9103 * search information for the global filter and individual column filters.
9427 * search information for the global filter and individual column filters.
@@ -9110,14 +9434,14 b''
9110 * @default true
9434 * @default true
9111 */
9435 */
9112 "bCaseInsensitive": true,
9436 "bCaseInsensitive": true,
9113
9437
9114 /**
9438 /**
9115 * Applied search term
9439 * Applied search term
9116 * @type string
9440 * @type string
9117 * @default <i>Empty string</i>
9441 * @default <i>Empty string</i>
9118 */
9442 */
9119 "sSearch": "",
9443 "sSearch": "",
9120
9444
9121 /**
9445 /**
9122 * Flag to indicate if the search term should be interpreted as a
9446 * Flag to indicate if the search term should be interpreted as a
9123 * regular expression (true) or not (false) and therefore and special
9447 * regular expression (true) or not (false) and therefore and special
@@ -9126,7 +9450,7 b''
9126 * @default false
9450 * @default false
9127 */
9451 */
9128 "bRegex": false,
9452 "bRegex": false,
9129
9453
9130 /**
9454 /**
9131 * Flag to indicate if DataTables is to use its smart filtering or not.
9455 * Flag to indicate if DataTables is to use its smart filtering or not.
9132 * @type boolean
9456 * @type boolean
@@ -9134,10 +9458,10 b''
9134 */
9458 */
9135 "bSmart": true
9459 "bSmart": true
9136 };
9460 };
9137
9461
9138
9462
9139
9463
9140
9464
9141 /**
9465 /**
9142 * Template object for the way in which DataTables holds information about
9466 * Template object for the way in which DataTables holds information about
9143 * each individual row. This is the object format used for the settings
9467 * each individual row. This is the object format used for the settings
@@ -9151,7 +9475,7 b''
9151 * @default null
9475 * @default null
9152 */
9476 */
9153 "nTr": null,
9477 "nTr": null,
9154
9478
9155 /**
9479 /**
9156 * Array of TD elements for each row. This is null until the row has been
9480 * Array of TD elements for each row. This is null until the row has been
9157 * created.
9481 * created.
@@ -9159,7 +9483,7 b''
9159 * @default []
9483 * @default []
9160 */
9484 */
9161 "anCells": null,
9485 "anCells": null,
9162
9486
9163 /**
9487 /**
9164 * Data object from the original data source for the row. This is either
9488 * Data object from the original data source for the row. This is either
9165 * an array if using the traditional form of DataTables, or an object if
9489 * an array if using the traditional form of DataTables, or an object if
@@ -9170,7 +9494,7 b''
9170 * @default []
9494 * @default []
9171 */
9495 */
9172 "_aData": [],
9496 "_aData": [],
9173
9497
9174 /**
9498 /**
9175 * Sorting data cache - this array is ostensibly the same length as the
9499 * Sorting data cache - this array is ostensibly the same length as the
9176 * number of columns (although each index is generated only as it is
9500 * number of columns (although each index is generated only as it is
@@ -9184,7 +9508,7 b''
9184 * @private
9508 * @private
9185 */
9509 */
9186 "_aSortData": null,
9510 "_aSortData": null,
9187
9511
9188 /**
9512 /**
9189 * Per cell filtering data cache. As per the sort data cache, used to
9513 * Per cell filtering data cache. As per the sort data cache, used to
9190 * increase the performance of the filtering in DataTables
9514 * increase the performance of the filtering in DataTables
@@ -9193,7 +9517,7 b''
9193 * @private
9517 * @private
9194 */
9518 */
9195 "_aFilterData": null,
9519 "_aFilterData": null,
9196
9520
9197 /**
9521 /**
9198 * Filtering data cache. This is the same as the cell filtering cache, but
9522 * Filtering data cache. This is the same as the cell filtering cache, but
9199 * in this case a string rather than an array. This is easily computed with
9523 * in this case a string rather than an array. This is easily computed with
@@ -9204,7 +9528,7 b''
9204 * @private
9528 * @private
9205 */
9529 */
9206 "_sFilterRow": null,
9530 "_sFilterRow": null,
9207
9531
9208 /**
9532 /**
9209 * Cache of the class name that DataTables has applied to the row, so we
9533 * Cache of the class name that DataTables has applied to the row, so we
9210 * can quickly look at this variable rather than needing to do a DOM check
9534 * can quickly look at this variable rather than needing to do a DOM check
@@ -9214,7 +9538,7 b''
9214 * @private
9538 * @private
9215 */
9539 */
9216 "_sRowStripe": "",
9540 "_sRowStripe": "",
9217
9541
9218 /**
9542 /**
9219 * Denote if the original data source was from the DOM, or the data source
9543 * Denote if the original data source was from the DOM, or the data source
9220 * object. This is used for invalidating data, so DataTables can
9544 * object. This is used for invalidating data, so DataTables can
@@ -9224,10 +9548,19 b''
9224 * @default null
9548 * @default null
9225 * @private
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
9562
9230
9563
9231 /**
9564 /**
9232 * Template object for the column information object in DataTables. This object
9565 * Template object for the column information object in DataTables. This object
9233 * is held in the settings aoColumns array and contains all the information that
9566 * is held in the settings aoColumns array and contains all the information that
@@ -9247,7 +9580,7 b''
9247 * @default null
9580 * @default null
9248 */
9581 */
9249 "idx": null,
9582 "idx": null,
9250
9583
9251 /**
9584 /**
9252 * A list of the columns that sorting should occur on when this column
9585 * A list of the columns that sorting should occur on when this column
9253 * is sorted. That this property is an array allows multi-column sorting
9586 * is sorted. That this property is an array allows multi-column sorting
@@ -9258,7 +9591,7 b''
9258 * @type array
9591 * @type array
9259 */
9592 */
9260 "aDataSort": null,
9593 "aDataSort": null,
9261
9594
9262 /**
9595 /**
9263 * Define the sorting directions that are applied to the column, in sequence
9596 * Define the sorting directions that are applied to the column, in sequence
9264 * as the column is repeatedly sorted upon - i.e. the first value is used
9597 * as the column is repeatedly sorted upon - i.e. the first value is used
@@ -9268,26 +9601,26 b''
9268 * @type array
9601 * @type array
9269 */
9602 */
9270 "asSorting": null,
9603 "asSorting": null,
9271
9604
9272 /**
9605 /**
9273 * Flag to indicate if the column is searchable, and thus should be included
9606 * Flag to indicate if the column is searchable, and thus should be included
9274 * in the filtering or not.
9607 * in the filtering or not.
9275 * @type boolean
9608 * @type boolean
9276 */
9609 */
9277 "bSearchable": null,
9610 "bSearchable": null,
9278
9611
9279 /**
9612 /**
9280 * Flag to indicate if the column is sortable or not.
9613 * Flag to indicate if the column is sortable or not.
9281 * @type boolean
9614 * @type boolean
9282 */
9615 */
9283 "bSortable": null,
9616 "bSortable": null,
9284
9617
9285 /**
9618 /**
9286 * Flag to indicate if the column is currently visible in the table or not
9619 * Flag to indicate if the column is currently visible in the table or not
9287 * @type boolean
9620 * @type boolean
9288 */
9621 */
9289 "bVisible": null,
9622 "bVisible": null,
9290
9623
9291 /**
9624 /**
9292 * Store for manual type assignment using the `column.type` option. This
9625 * Store for manual type assignment using the `column.type` option. This
9293 * is held in store so we can manipulate the column's `sType` property.
9626 * is held in store so we can manipulate the column's `sType` property.
@@ -9296,7 +9629,7 b''
9296 * @private
9629 * @private
9297 */
9630 */
9298 "_sManualType": null,
9631 "_sManualType": null,
9299
9632
9300 /**
9633 /**
9301 * Flag to indicate if HTML5 data attributes should be used as the data
9634 * Flag to indicate if HTML5 data attributes should be used as the data
9302 * source for filtering or sorting. True is either are.
9635 * source for filtering or sorting. True is either are.
@@ -9305,7 +9638,7 b''
9305 * @private
9638 * @private
9306 */
9639 */
9307 "_bAttrSrc": false,
9640 "_bAttrSrc": false,
9308
9641
9309 /**
9642 /**
9310 * Developer definable function that is called whenever a cell is created (Ajax source,
9643 * Developer definable function that is called whenever a cell is created (Ajax source,
9311 * etc) or processed for input (DOM source). This can be used as a compliment to mRender
9644 * etc) or processed for input (DOM source). This can be used as a compliment to mRender
@@ -9319,7 +9652,7 b''
9319 * @default null
9652 * @default null
9320 */
9653 */
9321 "fnCreatedCell": null,
9654 "fnCreatedCell": null,
9322
9655
9323 /**
9656 /**
9324 * Function to get data from a cell in a column. You should <b>never</b>
9657 * Function to get data from a cell in a column. You should <b>never</b>
9325 * access data directly through _aData internally in DataTables - always use
9658 * access data directly through _aData internally in DataTables - always use
@@ -9335,7 +9668,7 b''
9335 * @default null
9668 * @default null
9336 */
9669 */
9337 "fnGetData": null,
9670 "fnGetData": null,
9338
9671
9339 /**
9672 /**
9340 * Function to set data for a cell in the column. You should <b>never</b>
9673 * Function to set data for a cell in the column. You should <b>never</b>
9341 * set the data directly to _aData internally in DataTables - always use
9674 * set the data directly to _aData internally in DataTables - always use
@@ -9348,7 +9681,7 b''
9348 * @default null
9681 * @default null
9349 */
9682 */
9350 "fnSetData": null,
9683 "fnSetData": null,
9351
9684
9352 /**
9685 /**
9353 * Property to read the value for the cells in the column from the data
9686 * Property to read the value for the cells in the column from the data
9354 * source array / object. If null, then the default content is used, if a
9687 * source array / object. If null, then the default content is used, if a
@@ -9357,7 +9690,7 b''
9357 * @default null
9690 * @default null
9358 */
9691 */
9359 "mData": null,
9692 "mData": null,
9360
9693
9361 /**
9694 /**
9362 * Partner property to mData which is used (only when defined) to get
9695 * Partner property to mData which is used (only when defined) to get
9363 * the data - i.e. it is basically the same as mData, but without the
9696 * the data - i.e. it is basically the same as mData, but without the
@@ -9367,7 +9700,7 b''
9367 * @default null
9700 * @default null
9368 */
9701 */
9369 "mRender": null,
9702 "mRender": null,
9370
9703
9371 /**
9704 /**
9372 * Unique header TH/TD element for this column - this is what the sorting
9705 * Unique header TH/TD element for this column - this is what the sorting
9373 * listener is attached to (if sorting is enabled.)
9706 * listener is attached to (if sorting is enabled.)
@@ -9375,7 +9708,7 b''
9375 * @default null
9708 * @default null
9376 */
9709 */
9377 "nTh": null,
9710 "nTh": null,
9378
9711
9379 /**
9712 /**
9380 * Unique footer TH/TD element for this column (if there is one). Not used
9713 * Unique footer TH/TD element for this column (if there is one). Not used
9381 * in DataTables as such, but can be used for plug-ins to reference the
9714 * in DataTables as such, but can be used for plug-ins to reference the
@@ -9384,14 +9717,14 b''
9384 * @default null
9717 * @default null
9385 */
9718 */
9386 "nTf": null,
9719 "nTf": null,
9387
9720
9388 /**
9721 /**
9389 * The class to apply to all TD elements in the table's TBODY for the column
9722 * The class to apply to all TD elements in the table's TBODY for the column
9390 * @type string
9723 * @type string
9391 * @default null
9724 * @default null
9392 */
9725 */
9393 "sClass": null,
9726 "sClass": null,
9394
9727
9395 /**
9728 /**
9396 * When DataTables calculates the column widths to assign to each column,
9729 * When DataTables calculates the column widths to assign to each column,
9397 * it finds the longest string in each column and then constructs a
9730 * it finds the longest string in each column and then constructs a
@@ -9404,7 +9737,7 b''
9404 * @type string
9737 * @type string
9405 */
9738 */
9406 "sContentPadding": null,
9739 "sContentPadding": null,
9407
9740
9408 /**
9741 /**
9409 * Allows a default value to be given for a column's data, and will be used
9742 * Allows a default value to be given for a column's data, and will be used
9410 * whenever a null data source is encountered (this can be because mData
9743 * whenever a null data source is encountered (this can be because mData
@@ -9413,14 +9746,14 b''
9413 * @default null
9746 * @default null
9414 */
9747 */
9415 "sDefaultContent": null,
9748 "sDefaultContent": null,
9416
9749
9417 /**
9750 /**
9418 * Name for the column, allowing reference to the column by name as well as
9751 * Name for the column, allowing reference to the column by name as well as
9419 * by index (needs a lookup to work by name).
9752 * by index (needs a lookup to work by name).
9420 * @type string
9753 * @type string
9421 */
9754 */
9422 "sName": null,
9755 "sName": null,
9423
9756
9424 /**
9757 /**
9425 * Custom sorting data type - defines which of the available plug-ins in
9758 * Custom sorting data type - defines which of the available plug-ins in
9426 * afnSortData the custom sorting will use - if any is defined.
9759 * afnSortData the custom sorting will use - if any is defined.
@@ -9428,14 +9761,14 b''
9428 * @default std
9761 * @default std
9429 */
9762 */
9430 "sSortDataType": 'std',
9763 "sSortDataType": 'std',
9431
9764
9432 /**
9765 /**
9433 * Class to be applied to the header element when sorting on this column
9766 * Class to be applied to the header element when sorting on this column
9434 * @type string
9767 * @type string
9435 * @default null
9768 * @default null
9436 */
9769 */
9437 "sSortingClass": null,
9770 "sSortingClass": null,
9438
9771
9439 /**
9772 /**
9440 * Class to be applied to the header element when sorting on this column -
9773 * Class to be applied to the header element when sorting on this column -
9441 * when jQuery UI theming is used.
9774 * when jQuery UI theming is used.
@@ -9443,27 +9776,27 b''
9443 * @default null
9776 * @default null
9444 */
9777 */
9445 "sSortingClassJUI": null,
9778 "sSortingClassJUI": null,
9446
9779
9447 /**
9780 /**
9448 * Title of the column - what is seen in the TH element (nTh).
9781 * Title of the column - what is seen in the TH element (nTh).
9449 * @type string
9782 * @type string
9450 */
9783 */
9451 "sTitle": null,
9784 "sTitle": null,
9452
9785
9453 /**
9786 /**
9454 * Column sorting and filtering type
9787 * Column sorting and filtering type
9455 * @type string
9788 * @type string
9456 * @default null
9789 * @default null
9457 */
9790 */
9458 "sType": null,
9791 "sType": null,
9459
9792
9460 /**
9793 /**
9461 * Width of the column
9794 * Width of the column
9462 * @type string
9795 * @type string
9463 * @default null
9796 * @default null
9464 */
9797 */
9465 "sWidth": null,
9798 "sWidth": null,
9466
9799
9467 /**
9800 /**
9468 * Width of the column when it was first "encountered"
9801 * Width of the column when it was first "encountered"
9469 * @type string
9802 * @type string
@@ -9471,8 +9804,8 b''
9471 */
9804 */
9472 "sWidthOrig": null
9805 "sWidthOrig": null
9473 };
9806 };
9474
9807
9475
9808
9476 /*
9809 /*
9477 * Developer note: The properties of the object below are given in Hungarian
9810 * Developer note: The properties of the object below are given in Hungarian
9478 * notation, that was used as the interface for DataTables prior to v1.10, however
9811 * notation, that was used as the interface for DataTables prior to v1.10, however
@@ -9488,7 +9821,7 b''
9488 * completely, but that is a massive amount of work and will break current
9821 * completely, but that is a massive amount of work and will break current
9489 * installs (therefore is on-hold until v2).
9822 * installs (therefore is on-hold until v2).
9490 */
9823 */
9491
9824
9492 /**
9825 /**
9493 * Initialisation options that can be given to DataTables at initialisation
9826 * Initialisation options that can be given to DataTables at initialisation
9494 * time.
9827 * time.
@@ -9555,8 +9888,8 b''
9555 * } );
9888 * } );
9556 */
9889 */
9557 "aaData": null,
9890 "aaData": null,
9558
9891
9559
9892
9560 /**
9893 /**
9561 * If ordering is enabled, then DataTables will perform a first pass sort on
9894 * If ordering is enabled, then DataTables will perform a first pass sort on
9562 * initialisation. You can define which column(s) the sort is performed
9895 * initialisation. You can define which column(s) the sort is performed
@@ -9585,8 +9918,8 b''
9585 * } );
9918 * } );
9586 */
9919 */
9587 "aaSorting": [[0,'asc']],
9920 "aaSorting": [[0,'asc']],
9588
9921
9589
9922
9590 /**
9923 /**
9591 * This parameter is basically identical to the `sorting` parameter, but
9924 * This parameter is basically identical to the `sorting` parameter, but
9592 * cannot be overridden by user interaction with the table. What this means
9925 * cannot be overridden by user interaction with the table. What this means
@@ -9608,8 +9941,8 b''
9608 * } )
9941 * } )
9609 */
9942 */
9610 "aaSortingFixed": [],
9943 "aaSortingFixed": [],
9611
9944
9612
9945
9613 /**
9946 /**
9614 * DataTables can be instructed to load data to display in the table from a
9947 * DataTables can be instructed to load data to display in the table from a
9615 * Ajax source. This option defines how that Ajax call is made and where to.
9948 * Ajax source. This option defines how that Ajax call is made and where to.
@@ -9765,8 +10098,8 b''
9765 * } );
10098 * } );
9766 */
10099 */
9767 "ajax": null,
10100 "ajax": null,
9768
10101
9769
10102
9770 /**
10103 /**
9771 * This parameter allows you to readily specify the entries in the length drop
10104 * This parameter allows you to readily specify the entries in the length drop
9772 * down menu that DataTables shows when pagination is enabled. It can be
10105 * down menu that DataTables shows when pagination is enabled. It can be
@@ -9791,8 +10124,8 b''
9791 * } );
10124 * } );
9792 */
10125 */
9793 "aLengthMenu": [ 10, 25, 50, 100 ],
10126 "aLengthMenu": [ 10, 25, 50, 100 ],
9794
10127
9795
10128
9796 /**
10129 /**
9797 * The `columns` option in the initialisation parameter allows you to define
10130 * The `columns` option in the initialisation parameter allows you to define
9798 * details about the way individual columns behave. For a full list of
10131 * details about the way individual columns behave. For a full list of
@@ -9806,7 +10139,7 b''
9806 * @name DataTable.defaults.column
10139 * @name DataTable.defaults.column
9807 */
10140 */
9808 "aoColumns": null,
10141 "aoColumns": null,
9809
10142
9810 /**
10143 /**
9811 * Very similar to `columns`, `columnDefs` allows you to target a specific
10144 * Very similar to `columns`, `columnDefs` allows you to target a specific
9812 * column, multiple columns, or all columns, using the `targets` property of
10145 * column, multiple columns, or all columns, using the `targets` property of
@@ -9827,8 +10160,8 b''
9827 * @name DataTable.defaults.columnDefs
10160 * @name DataTable.defaults.columnDefs
9828 */
10161 */
9829 "aoColumnDefs": null,
10162 "aoColumnDefs": null,
9830
10163
9831
10164
9832 /**
10165 /**
9833 * Basically the same as `search`, this parameter defines the individual column
10166 * Basically the same as `search`, this parameter defines the individual column
9834 * filtering state at initialisation time. The array must be of the same size
10167 * filtering state at initialisation time. The array must be of the same size
@@ -9854,8 +10187,8 b''
9854 * } )
10187 * } )
9855 */
10188 */
9856 "aoSearchCols": [],
10189 "aoSearchCols": [],
9857
10190
9858
10191
9859 /**
10192 /**
9860 * An array of CSS classes that should be applied to displayed rows. This
10193 * An array of CSS classes that should be applied to displayed rows. This
9861 * array may be of any length, and DataTables will apply each class
10194 * array may be of any length, and DataTables will apply each class
@@ -9875,8 +10208,8 b''
9875 * } )
10208 * } )
9876 */
10209 */
9877 "asStripeClasses": null,
10210 "asStripeClasses": null,
9878
10211
9879
10212
9880 /**
10213 /**
9881 * Enable or disable automatic column width calculation. This can be disabled
10214 * Enable or disable automatic column width calculation. This can be disabled
9882 * as an optimisation (it takes some time to calculate the widths) if the
10215 * as an optimisation (it takes some time to calculate the widths) if the
@@ -9895,8 +10228,8 b''
9895 * } );
10228 * } );
9896 */
10229 */
9897 "bAutoWidth": true,
10230 "bAutoWidth": true,
9898
10231
9899
10232
9900 /**
10233 /**
9901 * Deferred rendering can provide DataTables with a huge speed boost when you
10234 * Deferred rendering can provide DataTables with a huge speed boost when you
9902 * are using an Ajax or JS data source for the table. This option, when set to
10235 * are using an Ajax or JS data source for the table. This option, when set to
@@ -9918,8 +10251,8 b''
9918 * } );
10251 * } );
9919 */
10252 */
9920 "bDeferRender": false,
10253 "bDeferRender": false,
9921
10254
9922
10255
9923 /**
10256 /**
9924 * Replace a DataTable which matches the given selector and replace it with
10257 * Replace a DataTable which matches the given selector and replace it with
9925 * one which has the properties of the new initialisation object passed. If no
10258 * one which has the properties of the new initialisation object passed. If no
@@ -9946,8 +10279,8 b''
9946 * } );
10279 * } );
9947 */
10280 */
9948 "bDestroy": false,
10281 "bDestroy": false,
9949
10282
9950
10283
9951 /**
10284 /**
9952 * Enable or disable filtering of data. Filtering in DataTables is "smart" in
10285 * Enable or disable filtering of data. Filtering in DataTables is "smart" in
9953 * that it allows the end user to input multiple words (space separated) and
10286 * that it allows the end user to input multiple words (space separated) and
@@ -9970,8 +10303,8 b''
9970 * } );
10303 * } );
9971 */
10304 */
9972 "bFilter": true,
10305 "bFilter": true,
9973
10306
9974
10307
9975 /**
10308 /**
9976 * Enable or disable the table information display. This shows information
10309 * Enable or disable the table information display. This shows information
9977 * about the data that is currently visible on the page, including information
10310 * about the data that is currently visible on the page, including information
@@ -9990,8 +10323,8 b''
9990 * } );
10323 * } );
9991 */
10324 */
9992 "bInfo": true,
10325 "bInfo": true,
9993
10326
9994
10327
9995 /**
10328 /**
9996 * Enable jQuery UI ThemeRoller support (required as ThemeRoller requires some
10329 * Enable jQuery UI ThemeRoller support (required as ThemeRoller requires some
9997 * slightly different and additional mark-up from what DataTables has
10330 * slightly different and additional mark-up from what DataTables has
@@ -10010,8 +10343,8 b''
10010 * } );
10343 * } );
10011 */
10344 */
10012 "bJQueryUI": false,
10345 "bJQueryUI": false,
10013
10346
10014
10347
10015 /**
10348 /**
10016 * Allows the end user to select the size of a formatted page from a select
10349 * Allows the end user to select the size of a formatted page from a select
10017 * menu (sizes are 10, 25, 50 and 100). Requires pagination (`paginate`).
10350 * menu (sizes are 10, 25, 50 and 100). Requires pagination (`paginate`).
@@ -10029,8 +10362,8 b''
10029 * } );
10362 * } );
10030 */
10363 */
10031 "bLengthChange": true,
10364 "bLengthChange": true,
10032
10365
10033
10366
10034 /**
10367 /**
10035 * Enable or disable pagination.
10368 * Enable or disable pagination.
10036 * @type boolean
10369 * @type boolean
@@ -10047,8 +10380,8 b''
10047 * } );
10380 * } );
10048 */
10381 */
10049 "bPaginate": true,
10382 "bPaginate": true,
10050
10383
10051
10384
10052 /**
10385 /**
10053 * Enable or disable the display of a 'processing' indicator when the table is
10386 * Enable or disable the display of a 'processing' indicator when the table is
10054 * being processed (e.g. a sort). This is particularly useful for tables with
10387 * being processed (e.g. a sort). This is particularly useful for tables with
@@ -10068,8 +10401,8 b''
10068 * } );
10401 * } );
10069 */
10402 */
10070 "bProcessing": false,
10403 "bProcessing": false,
10071
10404
10072
10405
10073 /**
10406 /**
10074 * Retrieve the DataTables object for the given selector. Note that if the
10407 * Retrieve the DataTables object for the given selector. Note that if the
10075 * table has already been initialised, this parameter will cause DataTables
10408 * table has already been initialised, this parameter will cause DataTables
@@ -10106,8 +10439,8 b''
10106 * }
10439 * }
10107 */
10440 */
10108 "bRetrieve": false,
10441 "bRetrieve": false,
10109
10442
10110
10443
10111 /**
10444 /**
10112 * When vertical (y) scrolling is enabled, DataTables will force the height of
10445 * When vertical (y) scrolling is enabled, DataTables will force the height of
10113 * the table's viewport to the given height at all times (useful for layout).
10446 * the table's viewport to the given height at all times (useful for layout).
@@ -10130,8 +10463,8 b''
10130 * } );
10463 * } );
10131 */
10464 */
10132 "bScrollCollapse": false,
10465 "bScrollCollapse": false,
10133
10466
10134
10467
10135 /**
10468 /**
10136 * Configure DataTables to use server-side processing. Note that the
10469 * Configure DataTables to use server-side processing. Note that the
10137 * `ajax` parameter must also be given in order to give DataTables a
10470 * `ajax` parameter must also be given in order to give DataTables a
@@ -10152,8 +10485,8 b''
10152 * } );
10485 * } );
10153 */
10486 */
10154 "bServerSide": false,
10487 "bServerSide": false,
10155
10488
10156
10489
10157 /**
10490 /**
10158 * Enable or disable sorting of columns. Sorting of individual columns can be
10491 * Enable or disable sorting of columns. Sorting of individual columns can be
10159 * disabled by the `sortable` option for each column.
10492 * disabled by the `sortable` option for each column.
@@ -10171,8 +10504,8 b''
10171 * } );
10504 * } );
10172 */
10505 */
10173 "bSort": true,
10506 "bSort": true,
10174
10507
10175
10508
10176 /**
10509 /**
10177 * Enable or display DataTables' ability to sort multiple columns at the
10510 * Enable or display DataTables' ability to sort multiple columns at the
10178 * same time (activated by shift-click by the user).
10511 * same time (activated by shift-click by the user).
@@ -10191,8 +10524,8 b''
10191 * } );
10524 * } );
10192 */
10525 */
10193 "bSortMulti": true,
10526 "bSortMulti": true,
10194
10527
10195
10528
10196 /**
10529 /**
10197 * Allows control over whether DataTables should use the top (true) unique
10530 * Allows control over whether DataTables should use the top (true) unique
10198 * cell that is found for a single column, or the bottom (false - default).
10531 * cell that is found for a single column, or the bottom (false - default).
@@ -10211,8 +10544,8 b''
10211 * } );
10544 * } );
10212 */
10545 */
10213 "bSortCellsTop": false,
10546 "bSortCellsTop": false,
10214
10547
10215
10548
10216 /**
10549 /**
10217 * Enable or disable the addition of the classes `sorting\_1`, `sorting\_2` and
10550 * Enable or disable the addition of the classes `sorting\_1`, `sorting\_2` and
10218 * `sorting\_3` to the columns which are currently being sorted on. This is
10551 * `sorting\_3` to the columns which are currently being sorted on. This is
@@ -10233,8 +10566,8 b''
10233 * } );
10566 * } );
10234 */
10567 */
10235 "bSortClasses": true,
10568 "bSortClasses": true,
10236
10569
10237
10570
10238 /**
10571 /**
10239 * Enable or disable state saving. When enabled HTML5 `localStorage` will be
10572 * Enable or disable state saving. When enabled HTML5 `localStorage` will be
10240 * used to save table display information such as pagination information,
10573 * used to save table display information such as pagination information,
@@ -10258,8 +10591,8 b''
10258 * } );
10591 * } );
10259 */
10592 */
10260 "bStateSave": false,
10593 "bStateSave": false,
10261
10594
10262
10595
10263 /**
10596 /**
10264 * This function is called when a TR element is created (and all TD child
10597 * This function is called when a TR element is created (and all TD child
10265 * elements have been inserted), or registered if using a DOM source, allowing
10598 * elements have been inserted), or registered if using a DOM source, allowing
@@ -10286,8 +10619,8 b''
10286 * } );
10619 * } );
10287 */
10620 */
10288 "fnCreatedRow": null,
10621 "fnCreatedRow": null,
10289
10622
10290
10623
10291 /**
10624 /**
10292 * This function is called on every 'draw' event, and allows you to
10625 * This function is called on every 'draw' event, and allows you to
10293 * dynamically modify any aspect you want about the created DOM.
10626 * dynamically modify any aspect you want about the created DOM.
@@ -10307,8 +10640,8 b''
10307 * } );
10640 * } );
10308 */
10641 */
10309 "fnDrawCallback": null,
10642 "fnDrawCallback": null,
10310
10643
10311
10644
10312 /**
10645 /**
10313 * Identical to fnHeaderCallback() but for the table footer this function
10646 * Identical to fnHeaderCallback() but for the table footer this function
10314 * allows you to modify the table footer on every 'draw' event.
10647 * allows you to modify the table footer on every 'draw' event.
@@ -10335,8 +10668,8 b''
10335 * } )
10668 * } )
10336 */
10669 */
10337 "fnFooterCallback": null,
10670 "fnFooterCallback": null,
10338
10671
10339
10672
10340 /**
10673 /**
10341 * When rendering large numbers in the information element for the table
10674 * When rendering large numbers in the information element for the table
10342 * (i.e. "Showing 1 to 10 of 57 entries") DataTables will render large numbers
10675 * (i.e. "Showing 1 to 10 of 57 entries") DataTables will render large numbers
@@ -10370,8 +10703,8 b''
10370 this.oLanguage.sThousands
10703 this.oLanguage.sThousands
10371 );
10704 );
10372 },
10705 },
10373
10706
10374
10707
10375 /**
10708 /**
10376 * This function is called on every 'draw' event, and allows you to
10709 * This function is called on every 'draw' event, and allows you to
10377 * dynamically modify the header row. This can be used to calculate and
10710 * dynamically modify the header row. This can be used to calculate and
@@ -10399,8 +10732,8 b''
10399 * } )
10732 * } )
10400 */
10733 */
10401 "fnHeaderCallback": null,
10734 "fnHeaderCallback": null,
10402
10735
10403
10736
10404 /**
10737 /**
10405 * The information element can be used to convey information about the current
10738 * The information element can be used to convey information about the current
10406 * state of the table. Although the internationalisation options presented by
10739 * state of the table. Although the internationalisation options presented by
@@ -10429,8 +10762,8 b''
10429 * } );
10762 * } );
10430 */
10763 */
10431 "fnInfoCallback": null,
10764 "fnInfoCallback": null,
10432
10765
10433
10766
10434 /**
10767 /**
10435 * Called when the table has been initialised. Normally DataTables will
10768 * Called when the table has been initialised. Normally DataTables will
10436 * initialise sequentially and there will be no need for this function,
10769 * initialise sequentially and there will be no need for this function,
@@ -10454,8 +10787,8 b''
10454 * } )
10787 * } )
10455 */
10788 */
10456 "fnInitComplete": null,
10789 "fnInitComplete": null,
10457
10790
10458
10791
10459 /**
10792 /**
10460 * Called at the very start of each table draw and can be used to cancel the
10793 * Called at the very start of each table draw and can be used to cancel the
10461 * draw by returning false, any other return (including undefined) results in
10794 * draw by returning false, any other return (including undefined) results in
@@ -10480,8 +10813,8 b''
10480 * } );
10813 * } );
10481 */
10814 */
10482 "fnPreDrawCallback": null,
10815 "fnPreDrawCallback": null,
10483
10816
10484
10817
10485 /**
10818 /**
10486 * This function allows you to 'post process' each row after it have been
10819 * This function allows you to 'post process' each row after it have been
10487 * generated for each table draw, but before it is rendered on screen. This
10820 * generated for each table draw, but before it is rendered on screen. This
@@ -10509,8 +10842,8 b''
10509 * } );
10842 * } );
10510 */
10843 */
10511 "fnRowCallback": null,
10844 "fnRowCallback": null,
10512
10845
10513
10846
10514 /**
10847 /**
10515 * __Deprecated__ The functionality provided by this parameter has now been
10848 * __Deprecated__ The functionality provided by this parameter has now been
10516 * superseded by that provided through `ajax`, which should be used instead.
10849 * superseded by that provided through `ajax`, which should be used instead.
@@ -10535,8 +10868,8 b''
10535 * @deprecated 1.10. Please use `ajax` for this functionality now.
10868 * @deprecated 1.10. Please use `ajax` for this functionality now.
10536 */
10869 */
10537 "fnServerData": null,
10870 "fnServerData": null,
10538
10871
10539
10872
10540 /**
10873 /**
10541 * __Deprecated__ The functionality provided by this parameter has now been
10874 * __Deprecated__ The functionality provided by this parameter has now been
10542 * superseded by that provided through `ajax`, which should be used instead.
10875 * superseded by that provided through `ajax`, which should be used instead.
@@ -10562,8 +10895,8 b''
10562 * @deprecated 1.10. Please use `ajax` for this functionality now.
10895 * @deprecated 1.10. Please use `ajax` for this functionality now.
10563 */
10896 */
10564 "fnServerParams": null,
10897 "fnServerParams": null,
10565
10898
10566
10899
10567 /**
10900 /**
10568 * Load the table state. With this function you can define from where, and how, the
10901 * Load the table state. With this function you can define from where, and how, the
10569 * state of a table is loaded. By default DataTables will load from `localStorage`
10902 * state of a table is loaded. By default DataTables will load from `localStorage`
@@ -10571,6 +10904,8 b''
10571 * @type function
10904 * @type function
10572 * @member
10905 * @member
10573 * @param {object} settings DataTables settings object
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 * @return {object} The DataTables state object to be loaded
10909 * @return {object} The DataTables state object to be loaded
10575 *
10910 *
10576 * @dtopt Callbacks
10911 * @dtopt Callbacks
@@ -10580,21 +10915,14 b''
10580 * $(document).ready( function() {
10915 * $(document).ready( function() {
10581 * $('#example').dataTable( {
10916 * $('#example').dataTable( {
10582 * "stateSave": true,
10917 * "stateSave": true,
10583 * "stateLoadCallback": function (settings) {
10918 * "stateLoadCallback": function (settings, callback) {
10584 * var o;
10585 *
10586 * // Send an Ajax request to the server to get the data. Note that
10587 * // this is a synchronous request.
10588 * $.ajax( {
10919 * $.ajax( {
10589 * "url": "/state_load",
10920 * "url": "/state_load",
10590 * "async": false,
10591 * "dataType": "json",
10921 * "dataType": "json",
10592 * "success": function (json) {
10922 * "success": function (json) {
10593 * o = json;
10923 * callback( json );
10594 * }
10924 * }
10595 * } );
10925 * } );
10596 *
10597 * return o;
10598 * }
10926 * }
10599 * } );
10927 * } );
10600 * } );
10928 * } );
@@ -10608,8 +10936,8 b''
10608 );
10936 );
10609 } catch (e) {}
10937 } catch (e) {}
10610 },
10938 },
10611
10939
10612
10940
10613 /**
10941 /**
10614 * Callback which allows modification of the saved state prior to loading that state.
10942 * Callback which allows modification of the saved state prior to loading that state.
10615 * This callback is called when the table is loading state from the stored data, but
10943 * This callback is called when the table is loading state from the stored data, but
@@ -10646,8 +10974,8 b''
10646 * } );
10974 * } );
10647 */
10975 */
10648 "fnStateLoadParams": null,
10976 "fnStateLoadParams": null,
10649
10977
10650
10978
10651 /**
10979 /**
10652 * Callback that is called when the state has been loaded from the state saving method
10980 * Callback that is called when the state has been loaded from the state saving method
10653 * and the DataTables settings object has been modified as a result of the loaded state.
10981 * and the DataTables settings object has been modified as a result of the loaded state.
@@ -10670,8 +10998,8 b''
10670 * } );
10998 * } );
10671 */
10999 */
10672 "fnStateLoaded": null,
11000 "fnStateLoaded": null,
10673
11001
10674
11002
10675 /**
11003 /**
10676 * Save the table state. This function allows you to define where and how the state
11004 * Save the table state. This function allows you to define where and how the state
10677 * information for the table is stored By default DataTables will use `localStorage`
11005 * information for the table is stored By default DataTables will use `localStorage`
@@ -10709,8 +11037,8 b''
10709 );
11037 );
10710 } catch (e) {}
11038 } catch (e) {}
10711 },
11039 },
10712
11040
10713
11041
10714 /**
11042 /**
10715 * Callback which allows modification of the state to be saved. Called when the table
11043 * Callback which allows modification of the state to be saved. Called when the table
10716 * has changed state a new state save is required. This method allows modification of
11044 * has changed state a new state save is required. This method allows modification of
@@ -10736,8 +11064,8 b''
10736 * } );
11064 * } );
10737 */
11065 */
10738 "fnStateSaveParams": null,
11066 "fnStateSaveParams": null,
10739
11067
10740
11068
10741 /**
11069 /**
10742 * Duration for which the saved state information is considered valid. After this period
11070 * Duration for which the saved state information is considered valid. After this period
10743 * has elapsed the state will be returned to the default.
11071 * has elapsed the state will be returned to the default.
@@ -10756,8 +11084,8 b''
10756 * } )
11084 * } )
10757 */
11085 */
10758 "iStateDuration": 7200,
11086 "iStateDuration": 7200,
10759
11087
10760
11088
10761 /**
11089 /**
10762 * When enabled DataTables will not make a request to the server for the first
11090 * When enabled DataTables will not make a request to the server for the first
10763 * page draw - rather it will use the data already on the page (no sorting etc
11091 * page draw - rather it will use the data already on the page (no sorting etc
@@ -10800,8 +11128,8 b''
10800 * } );
11128 * } );
10801 */
11129 */
10802 "iDeferLoading": null,
11130 "iDeferLoading": null,
10803
11131
10804
11132
10805 /**
11133 /**
10806 * Number of rows to display on a single page when using pagination. If
11134 * Number of rows to display on a single page when using pagination. If
10807 * feature enabled (`lengthChange`) then the end user will be able to override
11135 * feature enabled (`lengthChange`) then the end user will be able to override
@@ -10820,8 +11148,8 b''
10820 * } )
11148 * } )
10821 */
11149 */
10822 "iDisplayLength": 10,
11150 "iDisplayLength": 10,
10823
11151
10824
11152
10825 /**
11153 /**
10826 * Define the starting point for data display when using DataTables with
11154 * Define the starting point for data display when using DataTables with
10827 * pagination. Note that this parameter is the number of records, rather than
11155 * pagination. Note that this parameter is the number of records, rather than
@@ -10841,8 +11169,8 b''
10841 * } )
11169 * } )
10842 */
11170 */
10843 "iDisplayStart": 0,
11171 "iDisplayStart": 0,
10844
11172
10845
11173
10846 /**
11174 /**
10847 * By default DataTables allows keyboard navigation of the table (sorting, paging,
11175 * By default DataTables allows keyboard navigation of the table (sorting, paging,
10848 * and filtering) by adding a `tabindex` attribute to the required elements. This
11176 * and filtering) by adding a `tabindex` attribute to the required elements. This
@@ -10864,8 +11192,8 b''
10864 * } );
11192 * } );
10865 */
11193 */
10866 "iTabIndex": 0,
11194 "iTabIndex": 0,
10867
11195
10868
11196
10869 /**
11197 /**
10870 * Classes that DataTables assigns to the various components and features
11198 * Classes that DataTables assigns to the various components and features
10871 * that it adds to the HTML table. This allows classes to be configured
11199 * that it adds to the HTML table. This allows classes to be configured
@@ -10875,8 +11203,8 b''
10875 * @name DataTable.defaults.classes
11203 * @name DataTable.defaults.classes
10876 */
11204 */
10877 "oClasses": {},
11205 "oClasses": {},
10878
11206
10879
11207
10880 /**
11208 /**
10881 * All strings that DataTables uses in the user interface that it creates
11209 * All strings that DataTables uses in the user interface that it creates
10882 * are defined in this object, allowing you to modified them individually or
11210 * are defined in this object, allowing you to modified them individually or
@@ -10915,7 +11243,7 b''
10915 * } );
11243 * } );
10916 */
11244 */
10917 "sSortAscending": ": activate to sort column ascending",
11245 "sSortAscending": ": activate to sort column ascending",
10918
11246
10919 /**
11247 /**
10920 * ARIA label that is added to the table headers when the column may be
11248 * ARIA label that is added to the table headers when the column may be
10921 * sorted descending by activing the column (click or return when focused).
11249 * sorted descending by activing the column (click or return when focused).
@@ -10939,7 +11267,7 b''
10939 */
11267 */
10940 "sSortDescending": ": activate to sort column descending"
11268 "sSortDescending": ": activate to sort column descending"
10941 },
11269 },
10942
11270
10943 /**
11271 /**
10944 * Pagination string used by DataTables for the built-in pagination
11272 * Pagination string used by DataTables for the built-in pagination
10945 * control types.
11273 * control types.
@@ -10968,8 +11296,8 b''
10968 * } );
11296 * } );
10969 */
11297 */
10970 "sFirst": "First",
11298 "sFirst": "First",
10971
11299
10972
11300
10973 /**
11301 /**
10974 * Text to use when using the 'full_numbers' type of pagination for the
11302 * Text to use when using the 'full_numbers' type of pagination for the
10975 * button to take the user to the last page.
11303 * button to take the user to the last page.
@@ -10991,8 +11319,8 b''
10991 * } );
11319 * } );
10992 */
11320 */
10993 "sLast": "Last",
11321 "sLast": "Last",
10994
11322
10995
11323
10996 /**
11324 /**
10997 * Text to use for the 'next' pagination button (to take the user to the
11325 * Text to use for the 'next' pagination button (to take the user to the
10998 * next page).
11326 * next page).
@@ -11014,8 +11342,8 b''
11014 * } );
11342 * } );
11015 */
11343 */
11016 "sNext": "Next",
11344 "sNext": "Next",
11017
11345
11018
11346
11019 /**
11347 /**
11020 * Text to use for the 'previous' pagination button (to take the user to
11348 * Text to use for the 'previous' pagination button (to take the user to
11021 * the previous page).
11349 * the previous page).
@@ -11038,7 +11366,7 b''
11038 */
11366 */
11039 "sPrevious": "Previous"
11367 "sPrevious": "Previous"
11040 },
11368 },
11041
11369
11042 /**
11370 /**
11043 * This string is shown in preference to `zeroRecords` when the table is
11371 * This string is shown in preference to `zeroRecords` when the table is
11044 * empty of data (regardless of filtering). Note that this is an optional
11372 * empty of data (regardless of filtering). Note that this is an optional
@@ -11060,8 +11388,8 b''
11060 * } );
11388 * } );
11061 */
11389 */
11062 "sEmptyTable": "No data available in table",
11390 "sEmptyTable": "No data available in table",
11063
11391
11064
11392
11065 /**
11393 /**
11066 * This string gives information to the end user about the information
11394 * This string gives information to the end user about the information
11067 * that is current on display on the page. The following tokens can be
11395 * that is current on display on the page. The following tokens can be
@@ -11092,8 +11420,8 b''
11092 * } );
11420 * } );
11093 */
11421 */
11094 "sInfo": "Showing _START_ to _END_ of _TOTAL_ entries",
11422 "sInfo": "Showing _START_ to _END_ of _TOTAL_ entries",
11095
11423
11096
11424
11097 /**
11425 /**
11098 * Display information string for when the table is empty. Typically the
11426 * Display information string for when the table is empty. Typically the
11099 * format of this string should match `info`.
11427 * format of this string should match `info`.
@@ -11113,8 +11441,8 b''
11113 * } );
11441 * } );
11114 */
11442 */
11115 "sInfoEmpty": "Showing 0 to 0 of 0 entries",
11443 "sInfoEmpty": "Showing 0 to 0 of 0 entries",
11116
11444
11117
11445
11118 /**
11446 /**
11119 * When a user filters the information in a table, this string is appended
11447 * When a user filters the information in a table, this string is appended
11120 * to the information (`info`) to give an idea of how strong the filtering
11448 * to the information (`info`) to give an idea of how strong the filtering
@@ -11135,8 +11463,8 b''
11135 * } );
11463 * } );
11136 */
11464 */
11137 "sInfoFiltered": "(filtered from _MAX_ total entries)",
11465 "sInfoFiltered": "(filtered from _MAX_ total entries)",
11138
11466
11139
11467
11140 /**
11468 /**
11141 * If can be useful to append extra information to the info string at times,
11469 * If can be useful to append extra information to the info string at times,
11142 * and this variable does exactly that. This information will be appended to
11470 * and this variable does exactly that. This information will be appended to
@@ -11158,8 +11486,8 b''
11158 * } );
11486 * } );
11159 */
11487 */
11160 "sInfoPostFix": "",
11488 "sInfoPostFix": "",
11161
11489
11162
11490
11163 /**
11491 /**
11164 * This decimal place operator is a little different from the other
11492 * This decimal place operator is a little different from the other
11165 * language options since DataTables doesn't output floating point
11493 * language options since DataTables doesn't output floating point
@@ -11173,7 +11501,7 b''
11173 * However, multiple different tables on the page can use different
11501 * However, multiple different tables on the page can use different
11174 * decimal place characters.
11502 * decimal place characters.
11175 * @type string
11503 * @type string
11176 * @default
11504 * @default
11177 *
11505 *
11178 * @dtopt Language
11506 * @dtopt Language
11179 * @name DataTable.defaults.language.decimal
11507 * @name DataTable.defaults.language.decimal
@@ -11189,8 +11517,8 b''
11189 * } );
11517 * } );
11190 */
11518 */
11191 "sDecimal": "",
11519 "sDecimal": "",
11192
11520
11193
11521
11194 /**
11522 /**
11195 * DataTables has a build in number formatter (`formatNumber`) which is
11523 * DataTables has a build in number formatter (`formatNumber`) which is
11196 * used to format large numbers that are used in the table information.
11524 * used to format large numbers that are used in the table information.
@@ -11212,8 +11540,8 b''
11212 * } );
11540 * } );
11213 */
11541 */
11214 "sThousands": ",",
11542 "sThousands": ",",
11215
11543
11216
11544
11217 /**
11545 /**
11218 * Detail the action that will be taken when the drop down menu for the
11546 * Detail the action that will be taken when the drop down menu for the
11219 * pagination length option is changed. The '_MENU_' variable is replaced
11547 * pagination length option is changed. The '_MENU_' variable is replaced
@@ -11253,8 +11581,8 b''
11253 * } );
11581 * } );
11254 */
11582 */
11255 "sLengthMenu": "Show _MENU_ entries",
11583 "sLengthMenu": "Show _MENU_ entries",
11256
11584
11257
11585
11258 /**
11586 /**
11259 * When using Ajax sourced data and during the first draw when DataTables is
11587 * When using Ajax sourced data and during the first draw when DataTables is
11260 * gathering the data, this message is shown in an empty row in the table to
11588 * gathering the data, this message is shown in an empty row in the table to
@@ -11277,8 +11605,8 b''
11277 * } );
11605 * } );
11278 */
11606 */
11279 "sLoadingRecords": "Loading...",
11607 "sLoadingRecords": "Loading...",
11280
11608
11281
11609
11282 /**
11610 /**
11283 * Text which is displayed when the table is processing a user action
11611 * Text which is displayed when the table is processing a user action
11284 * (usually a sort command or similar).
11612 * (usually a sort command or similar).
@@ -11298,8 +11626,8 b''
11298 * } );
11626 * } );
11299 */
11627 */
11300 "sProcessing": "Processing...",
11628 "sProcessing": "Processing...",
11301
11629
11302
11630
11303 /**
11631 /**
11304 * Details the actions that will be taken when the user types into the
11632 * Details the actions that will be taken when the user types into the
11305 * filtering input text box. The variable "_INPUT_", if used in the string,
11633 * filtering input text box. The variable "_INPUT_", if used in the string,
@@ -11333,19 +11661,19 b''
11333 * } );
11661 * } );
11334 */
11662 */
11335 "sSearch": "Search:",
11663 "sSearch": "Search:",
11336
11664
11337
11665
11338 /**
11666 /**
11339 * Assign a `placeholder` attribute to the search `input` element
11667 * Assign a `placeholder` attribute to the search `input` element
11340 * @type string
11668 * @type string
11341 * @default
11669 * @default
11342 *
11670 *
11343 * @dtopt Language
11671 * @dtopt Language
11344 * @name DataTable.defaults.language.searchPlaceholder
11672 * @name DataTable.defaults.language.searchPlaceholder
11345 */
11673 */
11346 "sSearchPlaceholder": "",
11674 "sSearchPlaceholder": "",
11347
11675
11348
11676
11349 /**
11677 /**
11350 * All of the language information can be stored in a file on the
11678 * All of the language information can be stored in a file on the
11351 * server-side, which DataTables will look up if this parameter is passed.
11679 * server-side, which DataTables will look up if this parameter is passed.
@@ -11369,8 +11697,8 b''
11369 * } );
11697 * } );
11370 */
11698 */
11371 "sUrl": "",
11699 "sUrl": "",
11372
11700
11373
11701
11374 /**
11702 /**
11375 * Text shown inside the table records when the is no information to be
11703 * Text shown inside the table records when the is no information to be
11376 * displayed after filtering. `emptyTable` is shown when there is simply no
11704 * displayed after filtering. `emptyTable` is shown when there is simply no
@@ -11392,8 +11720,8 b''
11392 */
11720 */
11393 "sZeroRecords": "No matching records found"
11721 "sZeroRecords": "No matching records found"
11394 },
11722 },
11395
11723
11396
11724
11397 /**
11725 /**
11398 * This parameter allows you to have define the global filtering state at
11726 * This parameter allows you to have define the global filtering state at
11399 * initialisation time. As an object the `search` parameter must be
11727 * initialisation time. As an object the `search` parameter must be
@@ -11416,8 +11744,8 b''
11416 * } )
11744 * } )
11417 */
11745 */
11418 "oSearch": $.extend( {}, DataTable.models.oSearch ),
11746 "oSearch": $.extend( {}, DataTable.models.oSearch ),
11419
11747
11420
11748
11421 /**
11749 /**
11422 * __Deprecated__ The functionality provided by this parameter has now been
11750 * __Deprecated__ The functionality provided by this parameter has now been
11423 * superseded by that provided through `ajax`, which should be used instead.
11751 * superseded by that provided through `ajax`, which should be used instead.
@@ -11437,8 +11765,8 b''
11437 * @deprecated 1.10. Please use `ajax` for this functionality now.
11765 * @deprecated 1.10. Please use `ajax` for this functionality now.
11438 */
11766 */
11439 "sAjaxDataProp": "data",
11767 "sAjaxDataProp": "data",
11440
11768
11441
11769
11442 /**
11770 /**
11443 * __Deprecated__ The functionality provided by this parameter has now been
11771 * __Deprecated__ The functionality provided by this parameter has now been
11444 * superseded by that provided through `ajax`, which should be used instead.
11772 * superseded by that provided through `ajax`, which should be used instead.
@@ -11456,8 +11784,8 b''
11456 * @deprecated 1.10. Please use `ajax` for this functionality now.
11784 * @deprecated 1.10. Please use `ajax` for this functionality now.
11457 */
11785 */
11458 "sAjaxSource": null,
11786 "sAjaxSource": null,
11459
11787
11460
11788
11461 /**
11789 /**
11462 * This initialisation variable allows you to specify exactly where in the
11790 * This initialisation variable allows you to specify exactly where in the
11463 * DOM you want DataTables to inject the various controls it adds to the page
11791 * DOM you want DataTables to inject the various controls it adds to the page
@@ -11510,8 +11838,8 b''
11510 * } );
11838 * } );
11511 */
11839 */
11512 "sDom": "lfrtip",
11840 "sDom": "lfrtip",
11513
11841
11514
11842
11515 /**
11843 /**
11516 * Search delay option. This will throttle full table searches that use the
11844 * Search delay option. This will throttle full table searches that use the
11517 * DataTables provided search input element (it does not effect calls to
11845 * DataTables provided search input element (it does not effect calls to
@@ -11530,18 +11858,19 b''
11530 * } )
11858 * } )
11531 */
11859 */
11532 "searchDelay": null,
11860 "searchDelay": null,
11533
11861
11534
11862
11535 /**
11863 /**
11536 * DataTables features four different built-in options for the buttons to
11864 * DataTables features six different built-in options for the buttons to
11537 * display for pagination control:
11865 * display for pagination control:
11538 *
11866 *
11867 * * `numbers` - Page number buttons only
11539 * * `simple` - 'Previous' and 'Next' buttons only
11868 * * `simple` - 'Previous' and 'Next' buttons only
11540 * * 'simple_numbers` - 'Previous' and 'Next' buttons, plus page numbers
11869 * * 'simple_numbers` - 'Previous' and 'Next' buttons, plus page numbers
11541 * * `full` - 'First', 'Previous', 'Next' and 'Last' buttons
11870 * * `full` - 'First', 'Previous', 'Next' and 'Last' buttons
11542 * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus
11871 * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus page numbers
11543 * page numbers
11872 * * `first_last_numbers` - 'First' and 'Last' buttons, plus page numbers
11544 *
11873 *
11545 * Further methods can be added using {@link DataTable.ext.oPagination}.
11874 * Further methods can be added using {@link DataTable.ext.oPagination}.
11546 * @type string
11875 * @type string
11547 * @default simple_numbers
11876 * @default simple_numbers
@@ -11557,8 +11886,8 b''
11557 * } )
11886 * } )
11558 */
11887 */
11559 "sPaginationType": "simple_numbers",
11888 "sPaginationType": "simple_numbers",
11560
11889
11561
11890
11562 /**
11891 /**
11563 * Enable horizontal scrolling. When a table is too wide to fit into a
11892 * Enable horizontal scrolling. When a table is too wide to fit into a
11564 * certain layout, or you have a large number of columns in the table, you
11893 * certain layout, or you have a large number of columns in the table, you
@@ -11582,8 +11911,8 b''
11582 * } );
11911 * } );
11583 */
11912 */
11584 "sScrollX": "",
11913 "sScrollX": "",
11585
11914
11586
11915
11587 /**
11916 /**
11588 * This property can be used to force a DataTable to use more width than it
11917 * This property can be used to force a DataTable to use more width than it
11589 * might otherwise do when x-scrolling is enabled. For example if you have a
11918 * might otherwise do when x-scrolling is enabled. For example if you have a
@@ -11606,8 +11935,8 b''
11606 * } );
11935 * } );
11607 */
11936 */
11608 "sScrollXInner": "",
11937 "sScrollXInner": "",
11609
11938
11610
11939
11611 /**
11940 /**
11612 * Enable vertical scrolling. Vertical scrolling will constrain the DataTable
11941 * Enable vertical scrolling. Vertical scrolling will constrain the DataTable
11613 * to the given height, and enable scrolling for any data which overflows the
11942 * to the given height, and enable scrolling for any data which overflows the
@@ -11630,8 +11959,8 b''
11630 * } );
11959 * } );
11631 */
11960 */
11632 "sScrollY": "",
11961 "sScrollY": "",
11633
11962
11634
11963
11635 /**
11964 /**
11636 * __Deprecated__ The functionality provided by this parameter has now been
11965 * __Deprecated__ The functionality provided by this parameter has now been
11637 * superseded by that provided through `ajax`, which should be used instead.
11966 * superseded by that provided through `ajax`, which should be used instead.
@@ -11648,8 +11977,8 b''
11648 * @deprecated 1.10. Please use `ajax` for this functionality now.
11977 * @deprecated 1.10. Please use `ajax` for this functionality now.
11649 */
11978 */
11650 "sServerMethod": "GET",
11979 "sServerMethod": "GET",
11651
11980
11652
11981
11653 /**
11982 /**
11654 * DataTables makes use of renderers when displaying HTML elements for
11983 * DataTables makes use of renderers when displaying HTML elements for
11655 * a table. These renderers can be added or modified by plug-ins to
11984 * a table. These renderers can be added or modified by plug-ins to
@@ -11665,18 +11994,29 b''
11665 * @name DataTable.defaults.renderer
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 _fnHungarianMap( DataTable.defaults );
12011 _fnHungarianMap( DataTable.defaults );
11672
12012
11673
12013
11674
12014
11675 /*
12015 /*
11676 * Developer note - See note in model.defaults.js about the use of Hungarian
12016 * Developer note - See note in model.defaults.js about the use of Hungarian
11677 * notation and camel case.
12017 * notation and camel case.
11678 */
12018 */
11679
12019
11680 /**
12020 /**
11681 * Column options that can be given to DataTables at initialisation time.
12021 * Column options that can be given to DataTables at initialisation time.
11682 * @namespace
12022 * @namespace
@@ -11722,8 +12062,8 b''
11722 */
12062 */
11723 "aDataSort": null,
12063 "aDataSort": null,
11724 "iDataSort": -1,
12064 "iDataSort": -1,
11725
12065
11726
12066
11727 /**
12067 /**
11728 * You can control the default ordering direction, and even alter the
12068 * You can control the default ordering direction, and even alter the
11729 * behaviour of the sort handler (i.e. only allow ascending ordering etc)
12069 * behaviour of the sort handler (i.e. only allow ascending ordering etc)
@@ -11761,8 +12101,8 b''
11761 * } );
12101 * } );
11762 */
12102 */
11763 "asSorting": [ 'asc', 'desc' ],
12103 "asSorting": [ 'asc', 'desc' ],
11764
12104
11765
12105
11766 /**
12106 /**
11767 * Enable or disable filtering on the data in this column.
12107 * Enable or disable filtering on the data in this column.
11768 * @type boolean
12108 * @type boolean
@@ -11794,8 +12134,8 b''
11794 * } );
12134 * } );
11795 */
12135 */
11796 "bSearchable": true,
12136 "bSearchable": true,
11797
12137
11798
12138
11799 /**
12139 /**
11800 * Enable or disable ordering on this column.
12140 * Enable or disable ordering on this column.
11801 * @type boolean
12141 * @type boolean
@@ -11827,8 +12167,8 b''
11827 * } );
12167 * } );
11828 */
12168 */
11829 "bSortable": true,
12169 "bSortable": true,
11830
12170
11831
12171
11832 /**
12172 /**
11833 * Enable or disable the display of this column.
12173 * Enable or disable the display of this column.
11834 * @type boolean
12174 * @type boolean
@@ -11860,8 +12200,8 b''
11860 * } );
12200 * } );
11861 */
12201 */
11862 "bVisible": true,
12202 "bVisible": true,
11863
12203
11864
12204
11865 /**
12205 /**
11866 * Developer definable function that is called whenever a cell is created (Ajax source,
12206 * Developer definable function that is called whenever a cell is created (Ajax source,
11867 * etc) or processed for input (DOM source). This can be used as a compliment to mRender
12207 * etc) or processed for input (DOM source). This can be used as a compliment to mRender
@@ -11892,8 +12232,8 b''
11892 * } );
12232 * } );
11893 */
12233 */
11894 "fnCreatedCell": null,
12234 "fnCreatedCell": null,
11895
12235
11896
12236
11897 /**
12237 /**
11898 * This parameter has been replaced by `data` in DataTables to ensure naming
12238 * This parameter has been replaced by `data` in DataTables to ensure naming
11899 * consistency. `dataProp` can still be used, as there is backwards
12239 * consistency. `dataProp` can still be used, as there is backwards
@@ -11901,8 +12241,8 b''
11901 * recommended that you use `data` in preference to `dataProp`.
12241 * recommended that you use `data` in preference to `dataProp`.
11902 * @name DataTable.defaults.column.dataProp
12242 * @name DataTable.defaults.column.dataProp
11903 */
12243 */
11904
12244
11905
12245
11906 /**
12246 /**
11907 * This property can be used to read data from any data source property,
12247 * This property can be used to read data from any data source property,
11908 * including deeply nested objects / properties. `data` can be given in a
12248 * including deeply nested objects / properties. `data` can be given in a
@@ -12073,8 +12413,8 b''
12073 *
12413 *
12074 */
12414 */
12075 "mData": null,
12415 "mData": null,
12076
12416
12077
12417
12078 /**
12418 /**
12079 * This property is the rendering partner to `data` and it is suggested that
12419 * This property is the rendering partner to `data` and it is suggested that
12080 * when you want to manipulate data for display (including filtering,
12420 * when you want to manipulate data for display (including filtering,
@@ -12195,8 +12535,8 b''
12195 * } );
12535 * } );
12196 */
12536 */
12197 "mRender": null,
12537 "mRender": null,
12198
12538
12199
12539
12200 /**
12540 /**
12201 * Change the cell type created for the column - either TD cells or TH cells. This
12541 * Change the cell type created for the column - either TD cells or TH cells. This
12202 * can be useful as TH cells have semantic meaning in the table body, allowing them
12542 * can be useful as TH cells have semantic meaning in the table body, allowing them
@@ -12219,8 +12559,8 b''
12219 * } );
12559 * } );
12220 */
12560 */
12221 "sCellType": "td",
12561 "sCellType": "td",
12222
12562
12223
12563
12224 /**
12564 /**
12225 * Class to give to each cell in this column.
12565 * Class to give to each cell in this column.
12226 * @type string
12566 * @type string
@@ -12254,7 +12594,7 b''
12254 * } );
12594 * } );
12255 */
12595 */
12256 "sClass": "",
12596 "sClass": "",
12257
12597
12258 /**
12598 /**
12259 * When DataTables calculates the column widths to assign to each column,
12599 * When DataTables calculates the column widths to assign to each column,
12260 * it finds the longest string in each column and then constructs a
12600 * it finds the longest string in each column and then constructs a
@@ -12287,8 +12627,8 b''
12287 * } );
12627 * } );
12288 */
12628 */
12289 "sContentPadding": "",
12629 "sContentPadding": "",
12290
12630
12291
12631
12292 /**
12632 /**
12293 * Allows a default value to be given for a column's data, and will be used
12633 * Allows a default value to be given for a column's data, and will be used
12294 * whenever a null data source is encountered (this can be because `data`
12634 * whenever a null data source is encountered (this can be because `data`
@@ -12330,8 +12670,8 b''
12330 * } );
12670 * } );
12331 */
12671 */
12332 "sDefaultContent": null,
12672 "sDefaultContent": null,
12333
12673
12334
12674
12335 /**
12675 /**
12336 * This parameter is only used in DataTables' server-side processing. It can
12676 * This parameter is only used in DataTables' server-side processing. It can
12337 * be exceptionally useful to know what columns are being displayed on the
12677 * be exceptionally useful to know what columns are being displayed on the
@@ -12374,8 +12714,8 b''
12374 * } );
12714 * } );
12375 */
12715 */
12376 "sName": "",
12716 "sName": "",
12377
12717
12378
12718
12379 /**
12719 /**
12380 * Defines a data source type for the ordering which can be used to read
12720 * Defines a data source type for the ordering which can be used to read
12381 * real-time information from the table (updating the internally cached
12721 * real-time information from the table (updating the internally cached
@@ -12416,8 +12756,8 b''
12416 * } );
12756 * } );
12417 */
12757 */
12418 "sSortDataType": "std",
12758 "sSortDataType": "std",
12419
12759
12420
12760
12421 /**
12761 /**
12422 * The title of this column.
12762 * The title of this column.
12423 * @type string
12763 * @type string
@@ -12452,8 +12792,8 b''
12452 * } );
12792 * } );
12453 */
12793 */
12454 "sTitle": null,
12794 "sTitle": null,
12455
12795
12456
12796
12457 /**
12797 /**
12458 * The type allows you to specify how the data for this column will be
12798 * The type allows you to specify how the data for this column will be
12459 * ordered. Four types (string, numeric, date and html (which will strip
12799 * ordered. Four types (string, numeric, date and html (which will strip
@@ -12493,8 +12833,8 b''
12493 * } );
12833 * } );
12494 */
12834 */
12495 "sType": null,
12835 "sType": null,
12496
12836
12497
12837
12498 /**
12838 /**
12499 * Defining the width of the column, this parameter may take any CSS value
12839 * Defining the width of the column, this parameter may take any CSS value
12500 * (3em, 20px etc). DataTables applies 'smart' widths to columns which have not
12840 * (3em, 20px etc). DataTables applies 'smart' widths to columns which have not
@@ -12532,11 +12872,11 b''
12532 */
12872 */
12533 "sWidth": null
12873 "sWidth": null
12534 };
12874 };
12535
12875
12536 _fnHungarianMap( DataTable.defaults.column );
12876 _fnHungarianMap( DataTable.defaults.column );
12537
12877
12538
12878
12539
12879
12540 /**
12880 /**
12541 * DataTables settings object - this holds all the information needed for a
12881 * DataTables settings object - this holds all the information needed for a
12542 * given table, including configuration, data and current application of the
12882 * given table, including configuration, data and current application of the
@@ -12565,7 +12905,7 b''
12565 * @namespace
12905 * @namespace
12566 */
12906 */
12567 "oFeatures": {
12907 "oFeatures": {
12568
12908
12569 /**
12909 /**
12570 * Flag to say if DataTables should automatically try to calculate the
12910 * Flag to say if DataTables should automatically try to calculate the
12571 * optimum table and columns widths (true) or not (false).
12911 * optimum table and columns widths (true) or not (false).
@@ -12574,7 +12914,7 b''
12574 * @type boolean
12914 * @type boolean
12575 */
12915 */
12576 "bAutoWidth": null,
12916 "bAutoWidth": null,
12577
12917
12578 /**
12918 /**
12579 * Delay the creation of TR and TD elements until they are actually
12919 * Delay the creation of TR and TD elements until they are actually
12580 * needed by a driven page draw. This can give a significant speed
12920 * needed by a driven page draw. This can give a significant speed
@@ -12585,7 +12925,7 b''
12585 * @type boolean
12925 * @type boolean
12586 */
12926 */
12587 "bDeferRender": null,
12927 "bDeferRender": null,
12588
12928
12589 /**
12929 /**
12590 * Enable filtering on the table or not. Note that if this is disabled
12930 * Enable filtering on the table or not. Note that if this is disabled
12591 * then there is no filtering at all on the table, including fnFilter.
12931 * then there is no filtering at all on the table, including fnFilter.
@@ -12595,7 +12935,7 b''
12595 * @type boolean
12935 * @type boolean
12596 */
12936 */
12597 "bFilter": null,
12937 "bFilter": null,
12598
12938
12599 /**
12939 /**
12600 * Table information element (the 'Showing x of y records' div) enable
12940 * Table information element (the 'Showing x of y records' div) enable
12601 * flag.
12941 * flag.
@@ -12604,7 +12944,7 b''
12604 * @type boolean
12944 * @type boolean
12605 */
12945 */
12606 "bInfo": null,
12946 "bInfo": null,
12607
12947
12608 /**
12948 /**
12609 * Present a user control allowing the end user to change the page size
12949 * Present a user control allowing the end user to change the page size
12610 * when pagination is enabled.
12950 * when pagination is enabled.
@@ -12613,7 +12953,7 b''
12613 * @type boolean
12953 * @type boolean
12614 */
12954 */
12615 "bLengthChange": null,
12955 "bLengthChange": null,
12616
12956
12617 /**
12957 /**
12618 * Pagination enabled or not. Note that if this is disabled then length
12958 * Pagination enabled or not. Note that if this is disabled then length
12619 * changing must also be disabled.
12959 * changing must also be disabled.
@@ -12622,7 +12962,7 b''
12622 * @type boolean
12962 * @type boolean
12623 */
12963 */
12624 "bPaginate": null,
12964 "bPaginate": null,
12625
12965
12626 /**
12966 /**
12627 * Processing indicator enable flag whenever DataTables is enacting a
12967 * Processing indicator enable flag whenever DataTables is enacting a
12628 * user request - typically an Ajax request for server-side processing.
12968 * user request - typically an Ajax request for server-side processing.
@@ -12631,7 +12971,7 b''
12631 * @type boolean
12971 * @type boolean
12632 */
12972 */
12633 "bProcessing": null,
12973 "bProcessing": null,
12634
12974
12635 /**
12975 /**
12636 * Server-side processing enabled flag - when enabled DataTables will
12976 * Server-side processing enabled flag - when enabled DataTables will
12637 * get all data from the server for every draw - there is no filtering,
12977 * get all data from the server for every draw - there is no filtering,
@@ -12641,7 +12981,7 b''
12641 * @type boolean
12981 * @type boolean
12642 */
12982 */
12643 "bServerSide": null,
12983 "bServerSide": null,
12644
12984
12645 /**
12985 /**
12646 * Sorting enablement flag.
12986 * Sorting enablement flag.
12647 * Note that this parameter will be set by the initialisation routine. To
12987 * Note that this parameter will be set by the initialisation routine. To
@@ -12649,7 +12989,7 b''
12649 * @type boolean
12989 * @type boolean
12650 */
12990 */
12651 "bSort": null,
12991 "bSort": null,
12652
12992
12653 /**
12993 /**
12654 * Multi-column sorting
12994 * Multi-column sorting
12655 * Note that this parameter will be set by the initialisation routine. To
12995 * Note that this parameter will be set by the initialisation routine. To
@@ -12657,7 +12997,7 b''
12657 * @type boolean
12997 * @type boolean
12658 */
12998 */
12659 "bSortMulti": null,
12999 "bSortMulti": null,
12660
13000
12661 /**
13001 /**
12662 * Apply a class to the columns which are being sorted to provide a
13002 * Apply a class to the columns which are being sorted to provide a
12663 * visual highlight or not. This can slow things down when enabled since
13003 * visual highlight or not. This can slow things down when enabled since
@@ -12667,7 +13007,7 b''
12667 * @type boolean
13007 * @type boolean
12668 */
13008 */
12669 "bSortClasses": null,
13009 "bSortClasses": null,
12670
13010
12671 /**
13011 /**
12672 * State saving enablement flag.
13012 * State saving enablement flag.
12673 * Note that this parameter will be set by the initialisation routine. To
13013 * Note that this parameter will be set by the initialisation routine. To
@@ -12676,8 +13016,8 b''
12676 */
13016 */
12677 "bStateSave": null
13017 "bStateSave": null
12678 },
13018 },
12679
13019
12680
13020
12681 /**
13021 /**
12682 * Scrolling settings for a table.
13022 * Scrolling settings for a table.
12683 * @namespace
13023 * @namespace
@@ -12691,7 +13031,7 b''
12691 * @type boolean
13031 * @type boolean
12692 */
13032 */
12693 "bCollapse": null,
13033 "bCollapse": null,
12694
13034
12695 /**
13035 /**
12696 * Width of the scrollbar for the web-browser's platform. Calculated
13036 * Width of the scrollbar for the web-browser's platform. Calculated
12697 * during table initialisation.
13037 * during table initialisation.
@@ -12699,7 +13039,7 b''
12699 * @default 0
13039 * @default 0
12700 */
13040 */
12701 "iBarWidth": 0,
13041 "iBarWidth": 0,
12702
13042
12703 /**
13043 /**
12704 * Viewport width for horizontal scrolling. Horizontal scrolling is
13044 * Viewport width for horizontal scrolling. Horizontal scrolling is
12705 * disabled if an empty string.
13045 * disabled if an empty string.
@@ -12708,7 +13048,7 b''
12708 * @type string
13048 * @type string
12709 */
13049 */
12710 "sX": null,
13050 "sX": null,
12711
13051
12712 /**
13052 /**
12713 * Width to expand the table to when using x-scrolling. Typically you
13053 * Width to expand the table to when using x-scrolling. Typically you
12714 * should not need to use this.
13054 * should not need to use this.
@@ -12718,7 +13058,7 b''
12718 * @deprecated
13058 * @deprecated
12719 */
13059 */
12720 "sXInner": null,
13060 "sXInner": null,
12721
13061
12722 /**
13062 /**
12723 * Viewport height for vertical scrolling. Vertical scrolling is disabled
13063 * Viewport height for vertical scrolling. Vertical scrolling is disabled
12724 * if an empty string.
13064 * if an empty string.
@@ -12728,7 +13068,7 b''
12728 */
13068 */
12729 "sY": null
13069 "sY": null
12730 },
13070 },
12731
13071
12732 /**
13072 /**
12733 * Language information for the table.
13073 * Language information for the table.
12734 * @namespace
13074 * @namespace
@@ -12743,7 +13083,7 b''
12743 */
13083 */
12744 "fnInfoCallback": null
13084 "fnInfoCallback": null
12745 },
13085 },
12746
13086
12747 /**
13087 /**
12748 * Browser support parameters
13088 * Browser support parameters
12749 * @namespace
13089 * @namespace
@@ -12756,7 +13096,7 b''
12756 * @default false
13096 * @default false
12757 */
13097 */
12758 "bScrollOversize": false,
13098 "bScrollOversize": false,
12759
13099
12760 /**
13100 /**
12761 * Determine if the vertical scrollbar is on the right or left of the
13101 * Determine if the vertical scrollbar is on the right or left of the
12762 * scrolling container - needed for rtl language layout, although not
13102 * scrolling container - needed for rtl language layout, although not
@@ -12764,13 +13104,27 b''
12764 * @type boolean
13104 * @type boolean
12765 * @default false
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
13123
12770
13124
12771 "ajax": null,
13125 "ajax": null,
12772
13126
12773
13127
12774 /**
13128 /**
12775 * Array referencing the nodes which are used for the features. The
13129 * Array referencing the nodes which are used for the features. The
12776 * parameters of this object match what is allowed by sDom - i.e.
13130 * parameters of this object match what is allowed by sDom - i.e.
@@ -12786,7 +13140,7 b''
12786 * @default []
13140 * @default []
12787 */
13141 */
12788 "aanFeatures": [],
13142 "aanFeatures": [],
12789
13143
12790 /**
13144 /**
12791 * Store data information - see {@link DataTable.models.oRow} for detailed
13145 * Store data information - see {@link DataTable.models.oRow} for detailed
12792 * information.
13146 * information.
@@ -12794,42 +13148,49 b''
12794 * @default []
13148 * @default []
12795 */
13149 */
12796 "aoData": [],
13150 "aoData": [],
12797
13151
12798 /**
13152 /**
12799 * Array of indexes which are in the current display (after filtering etc)
13153 * Array of indexes which are in the current display (after filtering etc)
12800 * @type array
13154 * @type array
12801 * @default []
13155 * @default []
12802 */
13156 */
12803 "aiDisplay": [],
13157 "aiDisplay": [],
12804
13158
12805 /**
13159 /**
12806 * Array of indexes for display - no filtering
13160 * Array of indexes for display - no filtering
12807 * @type array
13161 * @type array
12808 * @default []
13162 * @default []
12809 */
13163 */
12810 "aiDisplayMaster": [],
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 * Store information about each column that is in use
13174 * Store information about each column that is in use
12814 * @type array
13175 * @type array
12815 * @default []
13176 * @default []
12816 */
13177 */
12817 "aoColumns": [],
13178 "aoColumns": [],
12818
13179
12819 /**
13180 /**
12820 * Store information about the table's header
13181 * Store information about the table's header
12821 * @type array
13182 * @type array
12822 * @default []
13183 * @default []
12823 */
13184 */
12824 "aoHeader": [],
13185 "aoHeader": [],
12825
13186
12826 /**
13187 /**
12827 * Store information about the table's footer
13188 * Store information about the table's footer
12828 * @type array
13189 * @type array
12829 * @default []
13190 * @default []
12830 */
13191 */
12831 "aoFooter": [],
13192 "aoFooter": [],
12832
13193
12833 /**
13194 /**
12834 * Store the applied global search information in case we want to force a
13195 * Store the applied global search information in case we want to force a
12835 * research or compare the old search to a new one.
13196 * research or compare the old search to a new one.
@@ -12839,7 +13200,7 b''
12839 * @extends DataTable.models.oSearch
13200 * @extends DataTable.models.oSearch
12840 */
13201 */
12841 "oPreviousSearch": {},
13202 "oPreviousSearch": {},
12842
13203
12843 /**
13204 /**
12844 * Store the applied search for each column - see
13205 * Store the applied search for each column - see
12845 * {@link DataTable.models.oSearch} for the format that is used for the
13206 * {@link DataTable.models.oSearch} for the format that is used for the
@@ -12848,7 +13209,7 b''
12848 * @default []
13209 * @default []
12849 */
13210 */
12850 "aoPreSearchCols": [],
13211 "aoPreSearchCols": [],
12851
13212
12852 /**
13213 /**
12853 * Sorting that is applied to the table. Note that the inner arrays are
13214 * Sorting that is applied to the table. Note that the inner arrays are
12854 * used in the following manner:
13215 * used in the following manner:
@@ -12862,7 +13223,7 b''
12862 * @todo These inner arrays should really be objects
13223 * @todo These inner arrays should really be objects
12863 */
13224 */
12864 "aaSorting": null,
13225 "aaSorting": null,
12865
13226
12866 /**
13227 /**
12867 * Sorting that is always applied to the table (i.e. prefixed in front of
13228 * Sorting that is always applied to the table (i.e. prefixed in front of
12868 * aaSorting).
13229 * aaSorting).
@@ -12872,7 +13233,7 b''
12872 * @default []
13233 * @default []
12873 */
13234 */
12874 "aaSortingFixed": [],
13235 "aaSortingFixed": [],
12875
13236
12876 /**
13237 /**
12877 * Classes to use for the striping of a table.
13238 * Classes to use for the striping of a table.
12878 * Note that this parameter will be set by the initialisation routine. To
13239 * Note that this parameter will be set by the initialisation routine. To
@@ -12881,56 +13242,56 b''
12881 * @default []
13242 * @default []
12882 */
13243 */
12883 "asStripeClasses": null,
13244 "asStripeClasses": null,
12884
13245
12885 /**
13246 /**
12886 * If restoring a table - we should restore its striping classes as well
13247 * If restoring a table - we should restore its striping classes as well
12887 * @type array
13248 * @type array
12888 * @default []
13249 * @default []
12889 */
13250 */
12890 "asDestroyStripes": [],
13251 "asDestroyStripes": [],
12891
13252
12892 /**
13253 /**
12893 * If restoring a table - we should restore its width
13254 * If restoring a table - we should restore its width
12894 * @type int
13255 * @type int
12895 * @default 0
13256 * @default 0
12896 */
13257 */
12897 "sDestroyWidth": 0,
13258 "sDestroyWidth": 0,
12898
13259
12899 /**
13260 /**
12900 * Callback functions array for every time a row is inserted (i.e. on a draw).
13261 * Callback functions array for every time a row is inserted (i.e. on a draw).
12901 * @type array
13262 * @type array
12902 * @default []
13263 * @default []
12903 */
13264 */
12904 "aoRowCallback": [],
13265 "aoRowCallback": [],
12905
13266
12906 /**
13267 /**
12907 * Callback functions for the header on each draw.
13268 * Callback functions for the header on each draw.
12908 * @type array
13269 * @type array
12909 * @default []
13270 * @default []
12910 */
13271 */
12911 "aoHeaderCallback": [],
13272 "aoHeaderCallback": [],
12912
13273
12913 /**
13274 /**
12914 * Callback function for the footer on each draw.
13275 * Callback function for the footer on each draw.
12915 * @type array
13276 * @type array
12916 * @default []
13277 * @default []
12917 */
13278 */
12918 "aoFooterCallback": [],
13279 "aoFooterCallback": [],
12919
13280
12920 /**
13281 /**
12921 * Array of callback functions for draw callback functions
13282 * Array of callback functions for draw callback functions
12922 * @type array
13283 * @type array
12923 * @default []
13284 * @default []
12924 */
13285 */
12925 "aoDrawCallback": [],
13286 "aoDrawCallback": [],
12926
13287
12927 /**
13288 /**
12928 * Array of callback functions for row created function
13289 * Array of callback functions for row created function
12929 * @type array
13290 * @type array
12930 * @default []
13291 * @default []
12931 */
13292 */
12932 "aoRowCreatedCallback": [],
13293 "aoRowCreatedCallback": [],
12933
13294
12934 /**
13295 /**
12935 * Callback functions for just before the table is redrawn. A return of
13296 * Callback functions for just before the table is redrawn. A return of
12936 * false will be used to cancel the draw.
13297 * false will be used to cancel the draw.
@@ -12938,15 +13299,15 b''
12938 * @default []
13299 * @default []
12939 */
13300 */
12940 "aoPreDrawCallback": [],
13301 "aoPreDrawCallback": [],
12941
13302
12942 /**
13303 /**
12943 * Callback functions for when the table has been initialised.
13304 * Callback functions for when the table has been initialised.
12944 * @type array
13305 * @type array
12945 * @default []
13306 * @default []
12946 */
13307 */
12947 "aoInitComplete": [],
13308 "aoInitComplete": [],
12948
13309
12949
13310
12950 /**
13311 /**
12951 * Callbacks for modifying the settings to be stored for state saving, prior to
13312 * Callbacks for modifying the settings to be stored for state saving, prior to
12952 * saving state.
13313 * saving state.
@@ -12954,7 +13315,7 b''
12954 * @default []
13315 * @default []
12955 */
13316 */
12956 "aoStateSaveParams": [],
13317 "aoStateSaveParams": [],
12957
13318
12958 /**
13319 /**
12959 * Callbacks for modifying the settings that have been stored for state saving
13320 * Callbacks for modifying the settings that have been stored for state saving
12960 * prior to using the stored values to restore the state.
13321 * prior to using the stored values to restore the state.
@@ -12962,7 +13323,7 b''
12962 * @default []
13323 * @default []
12963 */
13324 */
12964 "aoStateLoadParams": [],
13325 "aoStateLoadParams": [],
12965
13326
12966 /**
13327 /**
12967 * Callbacks for operating on the settings object once the saved state has been
13328 * Callbacks for operating on the settings object once the saved state has been
12968 * loaded
13329 * loaded
@@ -12970,49 +13331,49 b''
12970 * @default []
13331 * @default []
12971 */
13332 */
12972 "aoStateLoaded": [],
13333 "aoStateLoaded": [],
12973
13334
12974 /**
13335 /**
12975 * Cache the table ID for quick access
13336 * Cache the table ID for quick access
12976 * @type string
13337 * @type string
12977 * @default <i>Empty string</i>
13338 * @default <i>Empty string</i>
12978 */
13339 */
12979 "sTableId": "",
13340 "sTableId": "",
12980
13341
12981 /**
13342 /**
12982 * The TABLE node for the main table
13343 * The TABLE node for the main table
12983 * @type node
13344 * @type node
12984 * @default null
13345 * @default null
12985 */
13346 */
12986 "nTable": null,
13347 "nTable": null,
12987
13348
12988 /**
13349 /**
12989 * Permanent ref to the thead element
13350 * Permanent ref to the thead element
12990 * @type node
13351 * @type node
12991 * @default null
13352 * @default null
12992 */
13353 */
12993 "nTHead": null,
13354 "nTHead": null,
12994
13355
12995 /**
13356 /**
12996 * Permanent ref to the tfoot element - if it exists
13357 * Permanent ref to the tfoot element - if it exists
12997 * @type node
13358 * @type node
12998 * @default null
13359 * @default null
12999 */
13360 */
13000 "nTFoot": null,
13361 "nTFoot": null,
13001
13362
13002 /**
13363 /**
13003 * Permanent ref to the tbody element
13364 * Permanent ref to the tbody element
13004 * @type node
13365 * @type node
13005 * @default null
13366 * @default null
13006 */
13367 */
13007 "nTBody": null,
13368 "nTBody": null,
13008
13369
13009 /**
13370 /**
13010 * Cache the wrapper node (contains all DataTables controlled elements)
13371 * Cache the wrapper node (contains all DataTables controlled elements)
13011 * @type node
13372 * @type node
13012 * @default null
13373 * @default null
13013 */
13374 */
13014 "nTableWrapper": null,
13375 "nTableWrapper": null,
13015
13376
13016 /**
13377 /**
13017 * Indicate if when using server-side processing the loading of data
13378 * Indicate if when using server-side processing the loading of data
13018 * should be deferred until the second draw.
13379 * should be deferred until the second draw.
@@ -13022,14 +13383,14 b''
13022 * @default false
13383 * @default false
13023 */
13384 */
13024 "bDeferLoading": false,
13385 "bDeferLoading": false,
13025
13386
13026 /**
13387 /**
13027 * Indicate if all required information has been read in
13388 * Indicate if all required information has been read in
13028 * @type boolean
13389 * @type boolean
13029 * @default false
13390 * @default false
13030 */
13391 */
13031 "bInitialised": false,
13392 "bInitialised": false,
13032
13393
13033 /**
13394 /**
13034 * Information about open rows. Each object in the array has the parameters
13395 * Information about open rows. Each object in the array has the parameters
13035 * 'nTr' and 'nParent'
13396 * 'nTr' and 'nParent'
@@ -13037,7 +13398,7 b''
13037 * @default []
13398 * @default []
13038 */
13399 */
13039 "aoOpenRows": [],
13400 "aoOpenRows": [],
13040
13401
13041 /**
13402 /**
13042 * Dictate the positioning of DataTables' control elements - see
13403 * Dictate the positioning of DataTables' control elements - see
13043 * {@link DataTable.model.oInit.sDom}.
13404 * {@link DataTable.model.oInit.sDom}.
@@ -13047,14 +13408,14 b''
13047 * @default null
13408 * @default null
13048 */
13409 */
13049 "sDom": null,
13410 "sDom": null,
13050
13411
13051 /**
13412 /**
13052 * Search delay (in mS)
13413 * Search delay (in mS)
13053 * @type integer
13414 * @type integer
13054 * @default null
13415 * @default null
13055 */
13416 */
13056 "searchDelay": null,
13417 "searchDelay": null,
13057
13418
13058 /**
13419 /**
13059 * Which type of pagination should be used.
13420 * Which type of pagination should be used.
13060 * Note that this parameter will be set by the initialisation routine. To
13421 * Note that this parameter will be set by the initialisation routine. To
@@ -13063,7 +13424,7 b''
13063 * @default two_button
13424 * @default two_button
13064 */
13425 */
13065 "sPaginationType": "two_button",
13426 "sPaginationType": "two_button",
13066
13427
13067 /**
13428 /**
13068 * The state duration (for `stateSave`) in seconds.
13429 * The state duration (for `stateSave`) in seconds.
13069 * Note that this parameter will be set by the initialisation routine. To
13430 * Note that this parameter will be set by the initialisation routine. To
@@ -13072,7 +13433,7 b''
13072 * @default 0
13433 * @default 0
13073 */
13434 */
13074 "iStateDuration": 0,
13435 "iStateDuration": 0,
13075
13436
13076 /**
13437 /**
13077 * Array of callback functions for state saving. Each array element is an
13438 * Array of callback functions for state saving. Each array element is an
13078 * object with the following parameters:
13439 * object with the following parameters:
@@ -13087,7 +13448,7 b''
13087 * @default []
13448 * @default []
13088 */
13449 */
13089 "aoStateSave": [],
13450 "aoStateSave": [],
13090
13451
13091 /**
13452 /**
13092 * Array of callback functions for state loading. Each array element is an
13453 * Array of callback functions for state loading. Each array element is an
13093 * object with the following parameters:
13454 * object with the following parameters:
@@ -13100,21 +13461,21 b''
13100 * @default []
13461 * @default []
13101 */
13462 */
13102 "aoStateLoad": [],
13463 "aoStateLoad": [],
13103
13464
13104 /**
13465 /**
13105 * State that was saved. Useful for back reference
13466 * State that was saved. Useful for back reference
13106 * @type object
13467 * @type object
13107 * @default null
13468 * @default null
13108 */
13469 */
13109 "oSavedState": null,
13470 "oSavedState": null,
13110
13471
13111 /**
13472 /**
13112 * State that was loaded. Useful for back reference
13473 * State that was loaded. Useful for back reference
13113 * @type object
13474 * @type object
13114 * @default null
13475 * @default null
13115 */
13476 */
13116 "oLoadedState": null,
13477 "oLoadedState": null,
13117
13478
13118 /**
13479 /**
13119 * Source url for AJAX data for the table.
13480 * Source url for AJAX data for the table.
13120 * Note that this parameter will be set by the initialisation routine. To
13481 * Note that this parameter will be set by the initialisation routine. To
@@ -13123,7 +13484,7 b''
13123 * @default null
13484 * @default null
13124 */
13485 */
13125 "sAjaxSource": null,
13486 "sAjaxSource": null,
13126
13487
13127 /**
13488 /**
13128 * Property from a given object from which to read the table data from. This
13489 * Property from a given object from which to read the table data from. This
13129 * can be an empty string (when not server-side processing), in which case
13490 * can be an empty string (when not server-side processing), in which case
@@ -13133,14 +13494,14 b''
13133 * @type string
13494 * @type string
13134 */
13495 */
13135 "sAjaxDataProp": null,
13496 "sAjaxDataProp": null,
13136
13497
13137 /**
13498 /**
13138 * Note if draw should be blocked while getting data
13499 * Note if draw should be blocked while getting data
13139 * @type boolean
13500 * @type boolean
13140 * @default true
13501 * @default true
13141 */
13502 */
13142 "bAjaxDataGet": true,
13503 "bAjaxDataGet": true,
13143
13504
13144 /**
13505 /**
13145 * The last jQuery XHR object that was used for server-side data gathering.
13506 * The last jQuery XHR object that was used for server-side data gathering.
13146 * This can be used for working with the XHR information in one of the
13507 * This can be used for working with the XHR information in one of the
@@ -13149,21 +13510,21 b''
13149 * @default null
13510 * @default null
13150 */
13511 */
13151 "jqXHR": null,
13512 "jqXHR": null,
13152
13513
13153 /**
13514 /**
13154 * JSON returned from the server in the last Ajax request
13515 * JSON returned from the server in the last Ajax request
13155 * @type object
13516 * @type object
13156 * @default undefined
13517 * @default undefined
13157 */
13518 */
13158 "json": undefined,
13519 "json": undefined,
13159
13520
13160 /**
13521 /**
13161 * Data submitted as part of the last Ajax request
13522 * Data submitted as part of the last Ajax request
13162 * @type object
13523 * @type object
13163 * @default undefined
13524 * @default undefined
13164 */
13525 */
13165 "oAjaxData": undefined,
13526 "oAjaxData": undefined,
13166
13527
13167 /**
13528 /**
13168 * Function to get the server-side data.
13529 * Function to get the server-side data.
13169 * Note that this parameter will be set by the initialisation routine. To
13530 * Note that this parameter will be set by the initialisation routine. To
@@ -13171,7 +13532,7 b''
13171 * @type function
13532 * @type function
13172 */
13533 */
13173 "fnServerData": null,
13534 "fnServerData": null,
13174
13535
13175 /**
13536 /**
13176 * Functions which are called prior to sending an Ajax request so extra
13537 * Functions which are called prior to sending an Ajax request so extra
13177 * parameters can easily be sent to the server
13538 * parameters can easily be sent to the server
@@ -13179,7 +13540,7 b''
13179 * @default []
13540 * @default []
13180 */
13541 */
13181 "aoServerParams": [],
13542 "aoServerParams": [],
13182
13543
13183 /**
13544 /**
13184 * Send the XHR HTTP method - GET or POST (could be PUT or DELETE if
13545 * Send the XHR HTTP method - GET or POST (could be PUT or DELETE if
13185 * required).
13546 * required).
@@ -13188,7 +13549,7 b''
13188 * @type string
13549 * @type string
13189 */
13550 */
13190 "sServerMethod": null,
13551 "sServerMethod": null,
13191
13552
13192 /**
13553 /**
13193 * Format numbers for display.
13554 * Format numbers for display.
13194 * Note that this parameter will be set by the initialisation routine. To
13555 * Note that this parameter will be set by the initialisation routine. To
@@ -13196,7 +13557,7 b''
13196 * @type function
13557 * @type function
13197 */
13558 */
13198 "fnFormatNumber": null,
13559 "fnFormatNumber": null,
13199
13560
13200 /**
13561 /**
13201 * List of options that can be used for the user selectable length menu.
13562 * List of options that can be used for the user selectable length menu.
13202 * Note that this parameter will be set by the initialisation routine. To
13563 * Note that this parameter will be set by the initialisation routine. To
@@ -13205,7 +13566,7 b''
13205 * @default []
13566 * @default []
13206 */
13567 */
13207 "aLengthMenu": null,
13568 "aLengthMenu": null,
13208
13569
13209 /**
13570 /**
13210 * Counter for the draws that the table does. Also used as a tracker for
13571 * Counter for the draws that the table does. Also used as a tracker for
13211 * server-side processing
13572 * server-side processing
@@ -13213,35 +13574,35 b''
13213 * @default 0
13574 * @default 0
13214 */
13575 */
13215 "iDraw": 0,
13576 "iDraw": 0,
13216
13577
13217 /**
13578 /**
13218 * Indicate if a redraw is being done - useful for Ajax
13579 * Indicate if a redraw is being done - useful for Ajax
13219 * @type boolean
13580 * @type boolean
13220 * @default false
13581 * @default false
13221 */
13582 */
13222 "bDrawing": false,
13583 "bDrawing": false,
13223
13584
13224 /**
13585 /**
13225 * Draw index (iDraw) of the last error when parsing the returned data
13586 * Draw index (iDraw) of the last error when parsing the returned data
13226 * @type int
13587 * @type int
13227 * @default -1
13588 * @default -1
13228 */
13589 */
13229 "iDrawError": -1,
13590 "iDrawError": -1,
13230
13591
13231 /**
13592 /**
13232 * Paging display length
13593 * Paging display length
13233 * @type int
13594 * @type int
13234 * @default 10
13595 * @default 10
13235 */
13596 */
13236 "_iDisplayLength": 10,
13597 "_iDisplayLength": 10,
13237
13598
13238 /**
13599 /**
13239 * Paging start point - aiDisplay index
13600 * Paging start point - aiDisplay index
13240 * @type int
13601 * @type int
13241 * @default 0
13602 * @default 0
13242 */
13603 */
13243 "_iDisplayStart": 0,
13604 "_iDisplayStart": 0,
13244
13605
13245 /**
13606 /**
13246 * Server-side processing - number of records in the result set
13607 * Server-side processing - number of records in the result set
13247 * (i.e. before filtering), Use fnRecordsTotal rather than
13608 * (i.e. before filtering), Use fnRecordsTotal rather than
@@ -13252,7 +13613,7 b''
13252 * @private
13613 * @private
13253 */
13614 */
13254 "_iRecordsTotal": 0,
13615 "_iRecordsTotal": 0,
13255
13616
13256 /**
13617 /**
13257 * Server-side processing - number of records in the current display set
13618 * Server-side processing - number of records in the current display set
13258 * (i.e. after filtering). Use fnRecordsDisplay rather than
13619 * (i.e. after filtering). Use fnRecordsDisplay rather than
@@ -13263,7 +13624,7 b''
13263 * @private
13624 * @private
13264 */
13625 */
13265 "_iRecordsDisplay": 0,
13626 "_iRecordsDisplay": 0,
13266
13627
13267 /**
13628 /**
13268 * Flag to indicate if jQuery UI marking and classes should be used.
13629 * Flag to indicate if jQuery UI marking and classes should be used.
13269 * Note that this parameter will be set by the initialisation routine. To
13630 * Note that this parameter will be set by the initialisation routine. To
@@ -13271,14 +13632,14 b''
13271 * @type boolean
13632 * @type boolean
13272 */
13633 */
13273 "bJUI": null,
13634 "bJUI": null,
13274
13635
13275 /**
13636 /**
13276 * The classes to use for the table
13637 * The classes to use for the table
13277 * @type object
13638 * @type object
13278 * @default {}
13639 * @default {}
13279 */
13640 */
13280 "oClasses": {},
13641 "oClasses": {},
13281
13642
13282 /**
13643 /**
13283 * Flag attached to the settings object so you can check in the draw
13644 * Flag attached to the settings object so you can check in the draw
13284 * callback if filtering has been done in the draw. Deprecated in favour of
13645 * callback if filtering has been done in the draw. Deprecated in favour of
@@ -13288,7 +13649,7 b''
13288 * @deprecated
13649 * @deprecated
13289 */
13650 */
13290 "bFiltered": false,
13651 "bFiltered": false,
13291
13652
13292 /**
13653 /**
13293 * Flag attached to the settings object so you can check in the draw
13654 * Flag attached to the settings object so you can check in the draw
13294 * callback if sorting has been done in the draw. Deprecated in favour of
13655 * callback if sorting has been done in the draw. Deprecated in favour of
@@ -13298,7 +13659,7 b''
13298 * @deprecated
13659 * @deprecated
13299 */
13660 */
13300 "bSorted": false,
13661 "bSorted": false,
13301
13662
13302 /**
13663 /**
13303 * Indicate that if multiple rows are in the header and there is more than
13664 * Indicate that if multiple rows are in the header and there is more than
13304 * one unique cell per column, if the top one (true) or bottom one (false)
13665 * one unique cell per column, if the top one (true) or bottom one (false)
@@ -13308,14 +13669,14 b''
13308 * @type boolean
13669 * @type boolean
13309 */
13670 */
13310 "bSortCellsTop": null,
13671 "bSortCellsTop": null,
13311
13672
13312 /**
13673 /**
13313 * Initialisation object that is used for the table
13674 * Initialisation object that is used for the table
13314 * @type object
13675 * @type object
13315 * @default null
13676 * @default null
13316 */
13677 */
13317 "oInit": null,
13678 "oInit": null,
13318
13679
13319 /**
13680 /**
13320 * Destroy callback functions - for plug-ins to attach themselves to the
13681 * Destroy callback functions - for plug-ins to attach themselves to the
13321 * destroy so they can clean up markup and events.
13682 * destroy so they can clean up markup and events.
@@ -13323,8 +13684,8 b''
13323 * @default []
13684 * @default []
13324 */
13685 */
13325 "aoDestroyCallback": [],
13686 "aoDestroyCallback": [],
13326
13687
13327
13688
13328 /**
13689 /**
13329 * Get the number of records in the current record set, before filtering
13690 * Get the number of records in the current record set, before filtering
13330 * @type function
13691 * @type function
@@ -13335,7 +13696,7 b''
13335 this._iRecordsTotal * 1 :
13696 this._iRecordsTotal * 1 :
13336 this.aiDisplayMaster.length;
13697 this.aiDisplayMaster.length;
13337 },
13698 },
13338
13699
13339 /**
13700 /**
13340 * Get the number of records in the current record set, after filtering
13701 * Get the number of records in the current record set, after filtering
13341 * @type function
13702 * @type function
@@ -13346,7 +13707,7 b''
13346 this._iRecordsDisplay * 1 :
13707 this._iRecordsDisplay * 1 :
13347 this.aiDisplay.length;
13708 this.aiDisplay.length;
13348 },
13709 },
13349
13710
13350 /**
13711 /**
13351 * Get the display end point - aiDisplay index
13712 * Get the display end point - aiDisplay index
13352 * @type function
13713 * @type function
@@ -13360,7 +13721,7 b''
13360 records = this.aiDisplay.length,
13721 records = this.aiDisplay.length,
13361 features = this.oFeatures,
13722 features = this.oFeatures,
13362 paginate = features.bPaginate;
13723 paginate = features.bPaginate;
13363
13724
13364 if ( features.bServerSide ) {
13725 if ( features.bServerSide ) {
13365 return paginate === false || len === -1 ?
13726 return paginate === false || len === -1 ?
13366 start + records :
13727 start + records :
@@ -13372,14 +13733,14 b''
13372 calc;
13733 calc;
13373 }
13734 }
13374 },
13735 },
13375
13736
13376 /**
13737 /**
13377 * The DataTables object for this table
13738 * The DataTables object for this table
13378 * @type object
13739 * @type object
13379 * @default null
13740 * @default null
13380 */
13741 */
13381 "oInstance": null,
13742 "oInstance": null,
13382
13743
13383 /**
13744 /**
13384 * Unique identifier for each instance of the DataTables object. If there
13745 * Unique identifier for each instance of the DataTables object. If there
13385 * is an ID on the table node, then it takes that value, otherwise an
13746 * is an ID on the table node, then it takes that value, otherwise an
@@ -13388,36 +13749,50 b''
13388 * @default null
13749 * @default null
13389 */
13750 */
13390 "sInstance": null,
13751 "sInstance": null,
13391
13752
13392 /**
13753 /**
13393 * tabindex attribute value that is added to DataTables control elements, allowing
13754 * tabindex attribute value that is added to DataTables control elements, allowing
13394 * keyboard navigation of the table and its controls.
13755 * keyboard navigation of the table and its controls.
13395 */
13756 */
13396 "iTabIndex": 0,
13757 "iTabIndex": 0,
13397
13758
13398 /**
13759 /**
13399 * DIV container for the footer scrolling table if scrolling
13760 * DIV container for the footer scrolling table if scrolling
13400 */
13761 */
13401 "nScrollHead": null,
13762 "nScrollHead": null,
13402
13763
13403 /**
13764 /**
13404 * DIV container for the footer scrolling table if scrolling
13765 * DIV container for the footer scrolling table if scrolling
13405 */
13766 */
13406 "nScrollFoot": null,
13767 "nScrollFoot": null,
13407
13768
13408 /**
13769 /**
13409 * Last applied sort
13770 * Last applied sort
13410 * @type array
13771 * @type array
13411 * @default []
13772 * @default []
13412 */
13773 */
13413 "aLastSort": [],
13774 "aLastSort": [],
13414
13775
13415 /**
13776 /**
13416 * Stored plug-in instances
13777 * Stored plug-in instances
13417 * @type object
13778 * @type object
13418 * @default {}
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 /**
@@ -13430,11 +13805,11 b''
13430 * @namespace
13805 * @namespace
13431 * @extends DataTable.models.ext
13806 * @extends DataTable.models.ext
13432 */
13807 */
13433
13808
13434
13809
13435 /**
13810 /**
13436 * DataTables extensions
13811 * DataTables extensions
13437 *
13812 *
13438 * This namespace acts as a collection area for plug-ins that can be used to
13813 * This namespace acts as a collection area for plug-ins that can be used to
13439 * extend DataTables capabilities. Indeed many of the build in methods
13814 * extend DataTables capabilities. Indeed many of the build in methods
13440 * use this method to provide their own capabilities (sorting methods for
13815 * use this method to provide their own capabilities (sorting methods for
@@ -13447,36 +13822,55 b''
13447 */
13822 */
13448 DataTable.ext = _ext = {
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 * Element class names
13836 * Element class names
13451 *
13837 *
13452 * @type object
13838 * @type object
13453 * @default {}
13839 * @default {}
13454 */
13840 */
13455 classes: {},
13841 classes: {},
13456
13842
13457
13843
13844 /**
13845 * DataTables build type (expanded by the download builder)
13846 *
13847 * @type string
13848 */
13849 builder: "-source-",
13850
13851
13458 /**
13852 /**
13459 * Error reporting.
13853 * Error reporting.
13460 *
13854 *
13461 * How should DataTables report an error. Can take the value 'alert' or
13855 * How should DataTables report an error. Can take the value 'alert',
13462 * 'throw'
13856 * 'throw', 'none' or a function.
13463 *
13857 *
13464 * @type string
13858 * @type string|function
13465 * @default alert
13859 * @default alert
13466 */
13860 */
13467 errMode: "alert",
13861 errMode: "alert",
13468
13862
13469
13863
13470 /**
13864 /**
13471 * Feature plug-ins.
13865 * Feature plug-ins.
13472 *
13866 *
13473 * This is an array of objects which describe the feature plug-ins that are
13867 * This is an array of objects which describe the feature plug-ins that are
13474 * available to DataTables. These feature plug-ins are then available for
13868 * available to DataTables. These feature plug-ins are then available for
13475 * use through the `dom` initialisation option.
13869 * use through the `dom` initialisation option.
13476 *
13870 *
13477 * Each feature plug-in is described by an object which must have the
13871 * Each feature plug-in is described by an object which must have the
13478 * following properties:
13872 * following properties:
13479 *
13873 *
13480 * * `fnInit` - function that is used to initialise the plug-in,
13874 * * `fnInit` - function that is used to initialise the plug-in,
13481 * * `cFeature` - a character so the feature can be enabled by the `dom`
13875 * * `cFeature` - a character so the feature can be enabled by the `dom`
13482 * instillation option. This is case sensitive.
13876 * instillation option. This is case sensitive.
@@ -13487,7 +13881,7 b''
13487 * {@link DataTable.models.oSettings}
13881 * {@link DataTable.models.oSettings}
13488 *
13882 *
13489 * And the following return is expected:
13883 * And the following return is expected:
13490 *
13884 *
13491 * * {node|null} The element which contains your feature. Note that the
13885 * * {node|null} The element which contains your feature. Note that the
13492 * return may also be void if your plug-in does not require to inject any
13886 * return may also be void if your plug-in does not require to inject any
13493 * DOM elements into DataTables control (`dom`) - for example this might
13887 * DOM elements into DataTables control (`dom`) - for example this might
@@ -13505,11 +13899,11 b''
13505 * } );
13899 * } );
13506 */
13900 */
13507 feature: [],
13901 feature: [],
13508
13902
13509
13903
13510 /**
13904 /**
13511 * Row searching.
13905 * Row searching.
13512 *
13906 *
13513 * This method of searching is complimentary to the default type based
13907 * This method of searching is complimentary to the default type based
13514 * searching, and a lot more comprehensive as it allows you complete control
13908 * searching, and a lot more comprehensive as it allows you complete control
13515 * over the searching logic. Each element in this array is a function
13909 * over the searching logic. Each element in this array is a function
@@ -13566,11 +13960,42 b''
13566 * );
13960 * );
13567 */
13961 */
13568 search: [],
13962 search: [],
13569
13963
13570
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 * Internal functions, exposed for used in plug-ins.
13997 * Internal functions, exposed for used in plug-ins.
13573 *
13998 *
13574 * Please note that you should not need to use the internal methods for
13999 * Please note that you should not need to use the internal methods for
13575 * anything other than a plug-in (and even then, try to avoid if possible).
14000 * anything other than a plug-in (and even then, try to avoid if possible).
13576 * The internal function may change between releases.
14001 * The internal function may change between releases.
@@ -13579,8 +14004,8 b''
13579 * @default {}
14004 * @default {}
13580 */
14005 */
13581 internal: {},
14006 internal: {},
13582
14007
13583
14008
13584 /**
14009 /**
13585 * Legacy configuration options. Enable and disable legacy options that
14010 * Legacy configuration options. Enable and disable legacy options that
13586 * are available in DataTables.
14011 * are available in DataTables.
@@ -13597,11 +14022,11 b''
13597 */
14022 */
13598 ajax: null
14023 ajax: null
13599 },
14024 },
13600
14025
13601
14026
13602 /**
14027 /**
13603 * Pagination plug-in methods.
14028 * Pagination plug-in methods.
13604 *
14029 *
13605 * Each entry in this object is a function and defines which buttons should
14030 * Each entry in this object is a function and defines which buttons should
13606 * be shown by the pagination rendering method that is used for the table:
14031 * be shown by the pagination rendering method that is used for the table:
13607 * {@link DataTable.ext.renderer.pageButton}. The renderer addresses how the
14032 * {@link DataTable.ext.renderer.pageButton}. The renderer addresses how the
@@ -13645,26 +14070,26 b''
13645 * };
14070 * };
13646 */
14071 */
13647 pager: {},
14072 pager: {},
13648
14073
13649
14074
13650 renderer: {
14075 renderer: {
13651 pageButton: {},
14076 pageButton: {},
13652 header: {}
14077 header: {}
13653 },
14078 },
13654
14079
13655
14080
13656 /**
14081 /**
13657 * Ordering plug-ins - custom data source
14082 * Ordering plug-ins - custom data source
13658 *
14083 *
13659 * The extension options for ordering of data available here is complimentary
14084 * The extension options for ordering of data available here is complimentary
13660 * to the default type based ordering that DataTables typically uses. It
14085 * to the default type based ordering that DataTables typically uses. It
13661 * allows much greater control over the the data that is being used to
14086 * allows much greater control over the the data that is being used to
13662 * order a column, but is necessarily therefore more complex.
14087 * order a column, but is necessarily therefore more complex.
13663 *
14088 *
13664 * This type of ordering is useful if you want to do ordering based on data
14089 * This type of ordering is useful if you want to do ordering based on data
13665 * live from the DOM (for example the contents of an 'input' element) rather
14090 * live from the DOM (for example the contents of an 'input' element) rather
13666 * than just the static string that DataTables knows of.
14091 * than just the static string that DataTables knows of.
13667 *
14092 *
13668 * The way these plug-ins work is that you create an array of the values you
14093 * The way these plug-ins work is that you create an array of the values you
13669 * wish to be ordering for the column in question and then return that
14094 * wish to be ordering for the column in question and then return that
13670 * array. The data in the array much be in the index order of the rows in
14095 * array. The data in the array much be in the index order of the rows in
@@ -13694,8 +14119,8 b''
13694 * }
14119 * }
13695 */
14120 */
13696 order: {},
14121 order: {},
13697
14122
13698
14123
13699 /**
14124 /**
13700 * Type based plug-ins.
14125 * Type based plug-ins.
13701 *
14126 *
@@ -13748,8 +14173,8 b''
13748 * );
14173 * );
13749 */
14174 */
13750 detect: [],
14175 detect: [],
13751
14176
13752
14177
13753 /**
14178 /**
13754 * Type based search formatting.
14179 * Type based search formatting.
13755 *
14180 *
@@ -13759,7 +14184,7 b''
13759 *
14184 *
13760 * Note that is a search is not defined for a column of a given type,
14185 * Note that is a search is not defined for a column of a given type,
13761 * no search formatting will be performed.
14186 * no search formatting will be performed.
13762 *
14187 *
13763 * Pre-processing of searching data plug-ins - When you assign the sType
14188 * Pre-processing of searching data plug-ins - When you assign the sType
13764 * for a column (or have it automatically detected for you by DataTables
14189 * for a column (or have it automatically detected for you by DataTables
13765 * or a type detection plug-in), you will typically be using this for
14190 * or a type detection plug-in), you will typically be using this for
@@ -13787,8 +14212,8 b''
13787 * }
14212 * }
13788 */
14213 */
13789 search: {},
14214 search: {},
13790
14215
13791
14216
13792 /**
14217 /**
13793 * Type based ordering.
14218 * Type based ordering.
13794 *
14219 *
@@ -13829,7 +14254,7 b''
13829 * than the second parameter, ===0 if the two parameters are equal and
14254 * than the second parameter, ===0 if the two parameters are equal and
13830 * >0 if the first parameter should be sorted height than the second
14255 * >0 if the first parameter should be sorted height than the second
13831 * parameter.
14256 * parameter.
13832 *
14257 *
13833 * @type object
14258 * @type object
13834 * @default {}
14259 * @default {}
13835 *
14260 *
@@ -13855,7 +14280,7 b''
13855 */
14280 */
13856 order: {}
14281 order: {}
13857 },
14282 },
13858
14283
13859 /**
14284 /**
13860 * Unique DataTables instance counter
14285 * Unique DataTables instance counter
13861 *
14286 *
@@ -13863,39 +14288,39 b''
13863 * @private
14288 * @private
13864 */
14289 */
13865 _unique: 0,
14290 _unique: 0,
13866
14291
13867
14292
13868 //
14293 //
13869 // Depreciated
14294 // Depreciated
13870 // The following properties are retained for backwards compatiblity only.
14295 // The following properties are retained for backwards compatiblity only.
13871 // The should not be used in new projects and will be removed in a future
14296 // The should not be used in new projects and will be removed in a future
13872 // version
14297 // version
13873 //
14298 //
13874
14299
13875 /**
14300 /**
13876 * Version check function.
14301 * Version check function.
13877 * @type function
14302 * @type function
13878 * @depreciated Since 1.10
14303 * @depreciated Since 1.10
13879 */
14304 */
13880 fnVersionCheck: DataTable.fnVersionCheck,
14305 fnVersionCheck: DataTable.fnVersionCheck,
13881
14306
13882
14307
13883 /**
14308 /**
13884 * Index for what 'this' index API functions should use
14309 * Index for what 'this' index API functions should use
13885 * @type int
14310 * @type int
13886 * @deprecated Since v1.10
14311 * @deprecated Since v1.10
13887 */
14312 */
13888 iApiIndex: 0,
14313 iApiIndex: 0,
13889
14314
13890
14315
13891 /**
14316 /**
13892 * jQuery UI class container
14317 * jQuery UI class container
13893 * @type object
14318 * @type object
13894 * @deprecated Since v1.10
14319 * @deprecated Since v1.10
13895 */
14320 */
13896 oJUIClasses: {},
14321 oJUIClasses: {},
13897
14322
13898
14323
13899 /**
14324 /**
13900 * Software version
14325 * Software version
13901 * @type string
14326 * @type string
@@ -13903,8 +14328,8 b''
13903 */
14328 */
13904 sVersion: DataTable.version
14329 sVersion: DataTable.version
13905 };
14330 };
13906
14331
13907
14332
13908 //
14333 //
13909 // Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts
14334 // Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts
13910 //
14335 //
@@ -13919,24 +14344,24 b''
13919 oStdClasses: _ext.classes,
14344 oStdClasses: _ext.classes,
13920 oPagination: _ext.pager
14345 oPagination: _ext.pager
13921 } );
14346 } );
13922
14347
13923
14348
13924 $.extend( DataTable.ext.classes, {
14349 $.extend( DataTable.ext.classes, {
13925 "sTable": "dataTable",
14350 "sTable": "dataTable",
13926 "sNoFooter": "no-footer",
14351 "sNoFooter": "no-footer",
13927
14352
13928 /* Paging buttons */
14353 /* Paging buttons */
13929 "sPageButton": "paginate_button",
14354 "sPageButton": "paginate_button",
13930 "sPageButtonActive": "current",
14355 "sPageButtonActive": "current",
13931 "sPageButtonDisabled": "disabled",
14356 "sPageButtonDisabled": "disabled",
13932
14357
13933 /* Striping classes */
14358 /* Striping classes */
13934 "sStripeOdd": "odd",
14359 "sStripeOdd": "odd",
13935 "sStripeEven": "even",
14360 "sStripeEven": "even",
13936
14361
13937 /* Empty row */
14362 /* Empty row */
13938 "sRowEmpty": "dataTables_empty",
14363 "sRowEmpty": "dataTables_empty",
13939
14364
13940 /* Features */
14365 /* Features */
13941 "sWrapper": "dataTables_wrapper",
14366 "sWrapper": "dataTables_wrapper",
13942 "sFilter": "dataTables_filter",
14367 "sFilter": "dataTables_filter",
@@ -13944,7 +14369,7 b''
13944 "sPaging": "dataTables_paginate paging_", /* Note that the type is postfixed */
14369 "sPaging": "dataTables_paginate paging_", /* Note that the type is postfixed */
13945 "sLength": "dataTables_length",
14370 "sLength": "dataTables_length",
13946 "sProcessing": "dataTables_processing",
14371 "sProcessing": "dataTables_processing",
13947
14372
13948 /* Sorting */
14373 /* Sorting */
13949 "sSortAsc": "sorting_asc",
14374 "sSortAsc": "sorting_asc",
13950 "sSortDesc": "sorting_desc",
14375 "sSortDesc": "sorting_desc",
@@ -13953,13 +14378,13 b''
13953 "sSortableDesc": "sorting_desc_disabled",
14378 "sSortableDesc": "sorting_desc_disabled",
13954 "sSortableNone": "sorting_disabled",
14379 "sSortableNone": "sorting_disabled",
13955 "sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */
14380 "sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */
13956
14381
13957 /* Filtering */
14382 /* Filtering */
13958 "sFilterInput": "",
14383 "sFilterInput": "",
13959
14384
13960 /* Page length */
14385 /* Page length */
13961 "sLengthSelect": "",
14386 "sLengthSelect": "",
13962
14387
13963 /* Scrolling */
14388 /* Scrolling */
13964 "sScrollWrapper": "dataTables_scroll",
14389 "sScrollWrapper": "dataTables_scroll",
13965 "sScrollHead": "dataTables_scrollHead",
14390 "sScrollHead": "dataTables_scrollHead",
@@ -13967,11 +14392,11 b''
13967 "sScrollBody": "dataTables_scrollBody",
14392 "sScrollBody": "dataTables_scrollBody",
13968 "sScrollFoot": "dataTables_scrollFoot",
14393 "sScrollFoot": "dataTables_scrollFoot",
13969 "sScrollFootInner": "dataTables_scrollFootInner",
14394 "sScrollFootInner": "dataTables_scrollFootInner",
13970
14395
13971 /* Misc */
14396 /* Misc */
13972 "sHeaderTH": "",
14397 "sHeaderTH": "",
13973 "sFooterTH": "",
14398 "sFooterTH": "",
13974
14399
13975 // Deprecated
14400 // Deprecated
13976 "sSortJUIAsc": "",
14401 "sSortJUIAsc": "",
13977 "sSortJUIDesc": "",
14402 "sSortJUIDesc": "",
@@ -13983,31 +14408,31 b''
13983 "sJUIHeader": "",
14408 "sJUIHeader": "",
13984 "sJUIFooter": ""
14409 "sJUIFooter": ""
13985 } );
14410 } );
13986
14411
13987
14412
13988 (function() {
14413 (function() {
13989
14414
13990 // Reused strings for better compression. Closure compiler appears to have a
14415 // Reused strings for better compression. Closure compiler appears to have a
13991 // weird edge case where it is trying to expand strings rather than use the
14416 // weird edge case where it is trying to expand strings rather than use the
13992 // variable version. This results in about 200 bytes being added, for very
14417 // variable version. This results in about 200 bytes being added, for very
13993 // little preference benefit since it this run on script load only.
14418 // little preference benefit since it this run on script load only.
13994 var _empty = '';
14419 var _empty = '';
13995 _empty = '';
14420 _empty = '';
13996
14421
13997 var _stateDefault = _empty + 'ui-state-default';
14422 var _stateDefault = _empty + 'ui-state-default';
13998 var _sortIcon = _empty + 'css_right ui-icon ui-icon-';
14423 var _sortIcon = _empty + 'css_right ui-icon ui-icon-';
13999 var _headerFooter = _empty + 'fg-toolbar ui-toolbar ui-widget-header ui-helper-clearfix';
14424 var _headerFooter = _empty + 'fg-toolbar ui-toolbar ui-widget-header ui-helper-clearfix';
14000
14425
14001 $.extend( DataTable.ext.oJUIClasses, DataTable.ext.classes, {
14426 $.extend( DataTable.ext.oJUIClasses, DataTable.ext.classes, {
14002 /* Full numbers paging buttons */
14427 /* Full numbers paging buttons */
14003 "sPageButton": "fg-button ui-button "+_stateDefault,
14428 "sPageButton": "fg-button ui-button "+_stateDefault,
14004 "sPageButtonActive": "ui-state-disabled",
14429 "sPageButtonActive": "ui-state-disabled",
14005 "sPageButtonDisabled": "ui-state-disabled",
14430 "sPageButtonDisabled": "ui-state-disabled",
14006
14431
14007 /* Features */
14432 /* Features */
14008 "sPaging": "dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi "+
14433 "sPaging": "dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi "+
14009 "ui-buttonset-multi paging_", /* Note that the type is postfixed */
14434 "ui-buttonset-multi paging_", /* Note that the type is postfixed */
14010
14435
14011 /* Sorting */
14436 /* Sorting */
14012 "sSortAsc": _stateDefault+" sorting_asc",
14437 "sSortAsc": _stateDefault+" sorting_asc",
14013 "sSortDesc": _stateDefault+" sorting_desc",
14438 "sSortDesc": _stateDefault+" sorting_desc",
@@ -14022,31 +14447,31 b''
14022 "sSortJUIDescAllowed": _sortIcon+"carat-1-s",
14447 "sSortJUIDescAllowed": _sortIcon+"carat-1-s",
14023 "sSortJUIWrapper": "DataTables_sort_wrapper",
14448 "sSortJUIWrapper": "DataTables_sort_wrapper",
14024 "sSortIcon": "DataTables_sort_icon",
14449 "sSortIcon": "DataTables_sort_icon",
14025
14450
14026 /* Scrolling */
14451 /* Scrolling */
14027 "sScrollHead": "dataTables_scrollHead "+_stateDefault,
14452 "sScrollHead": "dataTables_scrollHead "+_stateDefault,
14028 "sScrollFoot": "dataTables_scrollFoot "+_stateDefault,
14453 "sScrollFoot": "dataTables_scrollFoot "+_stateDefault,
14029
14454
14030 /* Misc */
14455 /* Misc */
14031 "sHeaderTH": _stateDefault,
14456 "sHeaderTH": _stateDefault,
14032 "sFooterTH": _stateDefault,
14457 "sFooterTH": _stateDefault,
14033 "sJUIHeader": _headerFooter+" ui-corner-tl ui-corner-tr",
14458 "sJUIHeader": _headerFooter+" ui-corner-tl ui-corner-tr",
14034 "sJUIFooter": _headerFooter+" ui-corner-bl ui-corner-br"
14459 "sJUIFooter": _headerFooter+" ui-corner-bl ui-corner-br"
14035 } );
14460 } );
14036
14461
14037 }());
14462 }());
14038
14463
14039
14464
14040
14465
14041 var extPagination = DataTable.ext.pager;
14466 var extPagination = DataTable.ext.pager;
14042
14467
14043 function _numbers ( page, pages ) {
14468 function _numbers ( page, pages ) {
14044 var
14469 var
14045 numbers = [],
14470 numbers = [],
14046 buttons = extPagination.numbers_length,
14471 buttons = extPagination.numbers_length,
14047 half = Math.floor( buttons / 2 ),
14472 half = Math.floor( buttons / 2 ),
14048 i = 1;
14473 i = 1;
14049
14474
14050 if ( pages <= buttons ) {
14475 if ( pages <= buttons ) {
14051 numbers = _range( 0, pages );
14476 numbers = _range( 0, pages );
14052 }
14477 }
@@ -14061,107 +14486,118 b''
14061 numbers.splice( 0, 0, 0 );
14486 numbers.splice( 0, 0, 0 );
14062 }
14487 }
14063 else {
14488 else {
14064 numbers = _range( page-1, page+2 );
14489 numbers = _range( page-half+2, page+half-1 );
14065 numbers.push( 'ellipsis' );
14490 numbers.push( 'ellipsis' );
14066 numbers.push( pages-1 );
14491 numbers.push( pages-1 );
14067 numbers.splice( 0, 0, 'ellipsis' );
14492 numbers.splice( 0, 0, 'ellipsis' );
14068 numbers.splice( 0, 0, 0 );
14493 numbers.splice( 0, 0, 0 );
14069 }
14494 }
14070
14495
14071 numbers.DT_el = 'span';
14496 numbers.DT_el = 'span';
14072 return numbers;
14497 return numbers;
14073 }
14498 }
14074
14499
14075
14500
14076 $.extend( extPagination, {
14501 $.extend( extPagination, {
14077 simple: function ( page, pages ) {
14502 simple: function ( page, pages ) {
14078 return [ 'previous', 'next' ];
14503 return [ 'previous', 'next' ];
14079 },
14504 },
14080
14505
14081 full: function ( page, pages ) {
14506 full: function ( page, pages ) {
14082 return [ 'first', 'previous', 'next', 'last' ];
14507 return [ 'first', 'previous', 'next', 'last' ];
14083 },
14508 },
14084
14509
14510 numbers: function ( page, pages ) {
14511 return [ _numbers(page, pages) ];
14512 },
14513
14085 simple_numbers: function ( page, pages ) {
14514 simple_numbers: function ( page, pages ) {
14086 return [ 'previous', _numbers(page, pages), 'next' ];
14515 return [ 'previous', _numbers(page, pages), 'next' ];
14087 },
14516 },
14088
14517
14089 full_numbers: function ( page, pages ) {
14518 full_numbers: function ( page, pages ) {
14090 return [ 'first', 'previous', _numbers(page, pages), 'next', 'last' ];
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 // For testing and plug-ins to use
14526 // For testing and plug-ins to use
14094 _numbers: _numbers,
14527 _numbers: _numbers,
14528
14529 // Number of number buttons (including ellipsis) to show. _Must be odd!_
14095 numbers_length: 7
14530 numbers_length: 7
14096 } );
14531 } );
14097
14532
14098
14533
14099 $.extend( true, DataTable.ext.renderer, {
14534 $.extend( true, DataTable.ext.renderer, {
14100 pageButton: {
14535 pageButton: {
14101 _: function ( settings, host, idx, buttons, page, pages ) {
14536 _: function ( settings, host, idx, buttons, page, pages ) {
14102 var classes = settings.oClasses;
14537 var classes = settings.oClasses;
14103 var lang = settings.oLanguage.oPaginate;
14538 var lang = settings.oLanguage.oPaginate;
14539 var aria = settings.oLanguage.oAria.paginate || {};
14104 var btnDisplay, btnClass, counter=0;
14540 var btnDisplay, btnClass, counter=0;
14105
14541
14106 var attach = function( container, buttons ) {
14542 var attach = function( container, buttons ) {
14107 var i, ien, node, button;
14543 var i, ien, node, button;
14108 var clickHandler = function ( e ) {
14544 var clickHandler = function ( e ) {
14109 _fnPageChange( settings, e.data.action, true );
14545 _fnPageChange( settings, e.data.action, true );
14110 };
14546 };
14111
14547
14112 for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
14548 for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
14113 button = buttons[i];
14549 button = buttons[i];
14114
14550
14115 if ( $.isArray( button ) ) {
14551 if ( $.isArray( button ) ) {
14116 var inner = $( '<'+(button.DT_el || 'div')+'/>' )
14552 var inner = $( '<'+(button.DT_el || 'div')+'/>' )
14117 .appendTo( container );
14553 .appendTo( container );
14118 attach( inner, button );
14554 attach( inner, button );
14119 }
14555 }
14120 else {
14556 else {
14121 btnDisplay = '';
14557 btnDisplay = null;
14122 btnClass = '';
14558 btnClass = '';
14123
14559
14124 switch ( button ) {
14560 switch ( button ) {
14125 case 'ellipsis':
14561 case 'ellipsis':
14126 //RhodeCode fixed, added class paginate_button
14562 container.append('<span class="ellipsis">&#x2026;</span>');
14127 container.append('<span class=\"paginate_button\">&hellip;</span>');
14128 break;
14563 break;
14129
14564
14130 case 'first':
14565 case 'first':
14131 btnDisplay = lang.sFirst;
14566 btnDisplay = lang.sFirst;
14132 btnClass = button + (page > 0 ?
14567 btnClass = button + (page > 0 ?
14133 '' : ' '+classes.sPageButtonDisabled);
14568 '' : ' '+classes.sPageButtonDisabled);
14134 break;
14569 break;
14135
14570
14136 case 'previous':
14571 case 'previous':
14137 btnDisplay = lang.sPrevious;
14572 btnDisplay = lang.sPrevious;
14138 btnClass = button + (page > 0 ?
14573 btnClass = button + (page > 0 ?
14139 '' : ' '+classes.sPageButtonDisabled);
14574 '' : ' '+classes.sPageButtonDisabled);
14140 break;
14575 break;
14141
14576
14142 case 'next':
14577 case 'next':
14143 btnDisplay = lang.sNext;
14578 btnDisplay = lang.sNext;
14144 btnClass = button + (page < pages-1 ?
14579 btnClass = button + (page < pages-1 ?
14145 '' : ' '+classes.sPageButtonDisabled);
14580 '' : ' '+classes.sPageButtonDisabled);
14146 break;
14581 break;
14147
14582
14148 case 'last':
14583 case 'last':
14149 btnDisplay = lang.sLast;
14584 btnDisplay = lang.sLast;
14150 btnClass = button + (page < pages-1 ?
14585 btnClass = button + (page < pages-1 ?
14151 '' : ' '+classes.sPageButtonDisabled);
14586 '' : ' '+classes.sPageButtonDisabled);
14152 break;
14587 break;
14153
14588
14154 default:
14589 default:
14155 btnDisplay = button + 1;
14590 btnDisplay = button + 1;
14156 btnClass = page === button ?
14591 btnClass = page === button ?
14157 classes.sPageButtonActive : '';
14592 classes.sPageButtonActive : '';
14158 break;
14593 break;
14159 }
14594 }
14160
14595
14161 if ( btnDisplay ) {
14596 if ( btnDisplay !== null ) {
14162 node = $('<a>', {
14597 node = $('<a>', {
14163 'class': classes.sPageButton+' '+btnClass,
14598 'class': classes.sPageButton+' '+btnClass,
14164 'aria-controls': settings.sTableId,
14599 'aria-controls': settings.sTableId,
14600 'aria-label': aria[ button ],
14165 'data-dt-idx': counter,
14601 'data-dt-idx': counter,
14166 'tabindex': settings.iTabIndex,
14602 'tabindex': settings.iTabIndex,
14167 'id': idx === 0 && typeof button === 'string' ?
14603 'id': idx === 0 && typeof button === 'string' ?
@@ -14170,40 +14606,42 b''
14170 } )
14606 } )
14171 .html( btnDisplay )
14607 .html( btnDisplay )
14172 .appendTo( container );
14608 .appendTo( container );
14173
14609
14174 _fnBindAction(
14610 _fnBindAction(
14175 node, {action: button}, clickHandler
14611 node, {action: button}, clickHandler
14176 );
14612 );
14177
14613
14178 counter++;
14614 counter++;
14179 }
14615 }
14180 }
14616 }
14181 }
14617 }
14182 };
14618 };
14183
14619
14184 // IE9 throws an 'unknown error' if document.activeElement is used
14620 // IE9 throws an 'unknown error' if document.activeElement is used
14185 // inside an iframe or frame. Try / catch the error. Not good for
14621 // inside an iframe or frame. Try / catch the error. Not good for
14186 // accessibility, but neither are frames.
14622 // accessibility, but neither are frames.
14623 var activeEl;
14624
14187 try {
14625 try {
14188 // Because this approach is destroying and recreating the paging
14626 // Because this approach is destroying and recreating the paging
14189 // elements, focus is lost on the select button which is bad for
14627 // elements, focus is lost on the select button which is bad for
14190 // accessibility. So we want to restore focus once the draw has
14628 // accessibility. So we want to restore focus once the draw has
14191 // completed
14629 // completed
14192 var activeEl = $(document.activeElement).data('dt-idx');
14630 activeEl = $(host).find(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 }
14199 }
14631 }
14200 catch (e) {}
14632 catch (e) {}
14201 }
14633
14202 }
14634 attach( $(host).empty(), buttons );
14203 } );
14635
14204
14636 if ( activeEl !== undefined ) {
14205
14637 $(host).find( '[data-dt-idx='+activeEl+']' ).focus();
14206
14638 }
14639 }
14640 }
14641 } );
14642
14643
14644
14207 // Built in type detection. See model.ext.aTypes for information about
14645 // Built in type detection. See model.ext.aTypes for information about
14208 // what is required from this methods.
14646 // what is required from this methods.
14209 $.extend( DataTable.ext.type.detect, [
14647 $.extend( DataTable.ext.type.detect, [
@@ -14214,41 +14652,41 b''
14214 var decimal = settings.oLanguage.sDecimal;
14652 var decimal = settings.oLanguage.sDecimal;
14215 return _isNumber( d, decimal ) ? 'num'+decimal : null;
14653 return _isNumber( d, decimal ) ? 'num'+decimal : null;
14216 },
14654 },
14217
14655
14218 // Dates (only those recognised by the browser's Date.parse)
14656 // Dates (only those recognised by the browser's Date.parse)
14219 function ( d, settings )
14657 function ( d, settings )
14220 {
14658 {
14221 // V8 will remove any unknown characters at the start and end of the
14659 // V8 tries _very_ hard to make a string passed into `Date.parse()`
14222 // expression, leading to false matches such as `$245.12` or `10%` being
14660 // valid, so we need to use a regex to restrict date formats. Use a
14223 // a valid date. See forum thread 18941 for detail.
14661 // plug-in for anything other than ISO8601 style strings
14224 if ( d && !(d instanceof Date) && ( ! _re_date_start.test(d) || ! _re_date_end.test(d) ) ) {
14662 if ( d && !(d instanceof Date) && ! _re_date.test(d) ) {
14225 return null;
14663 return null;
14226 }
14664 }
14227 var parsed = Date.parse(d);
14665 var parsed = Date.parse(d);
14228 return (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null;
14666 return (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null;
14229 },
14667 },
14230
14668
14231 // Formatted numbers
14669 // Formatted numbers
14232 function ( d, settings )
14670 function ( d, settings )
14233 {
14671 {
14234 var decimal = settings.oLanguage.sDecimal;
14672 var decimal = settings.oLanguage.sDecimal;
14235 return _isNumber( d, decimal, true ) ? 'num-fmt'+decimal : null;
14673 return _isNumber( d, decimal, true ) ? 'num-fmt'+decimal : null;
14236 },
14674 },
14237
14675
14238 // HTML numeric
14676 // HTML numeric
14239 function ( d, settings )
14677 function ( d, settings )
14240 {
14678 {
14241 var decimal = settings.oLanguage.sDecimal;
14679 var decimal = settings.oLanguage.sDecimal;
14242 return _htmlNumeric( d, decimal ) ? 'html-num'+decimal : null;
14680 return _htmlNumeric( d, decimal ) ? 'html-num'+decimal : null;
14243 },
14681 },
14244
14682
14245 // HTML numeric, formatted
14683 // HTML numeric, formatted
14246 function ( d, settings )
14684 function ( d, settings )
14247 {
14685 {
14248 var decimal = settings.oLanguage.sDecimal;
14686 var decimal = settings.oLanguage.sDecimal;
14249 return _htmlNumeric( d, decimal, true ) ? 'html-num-fmt'+decimal : null;
14687 return _htmlNumeric( d, decimal, true ) ? 'html-num-fmt'+decimal : null;
14250 },
14688 },
14251
14689
14252 // HTML (this is strict checking - there must be html)
14690 // HTML (this is strict checking - there must be html)
14253 function ( d, settings )
14691 function ( d, settings )
14254 {
14692 {
@@ -14256,17 +14694,17 b''
14256 'html' : null;
14694 'html' : null;
14257 }
14695 }
14258 ] );
14696 ] );
14259
14697
14260
14698
14261
14699
14262 // Filter formatting functions. See model.ext.ofnSearch for information about
14700 // Filter formatting functions. See model.ext.ofnSearch for information about
14263 // what is required from these methods.
14701 // what is required from these methods.
14264 //
14702 //
14265 // Note that additional search methods are added for the html numbers and
14703 // Note that additional search methods are added for the html numbers and
14266 // html formatted numbers by `_addNumericSort()` when we know what the decimal
14704 // html formatted numbers by `_addNumericSort()` when we know what the decimal
14267 // place is
14705 // place is
14268
14706
14269
14707
14270 $.extend( DataTable.ext.type.search, {
14708 $.extend( DataTable.ext.type.search, {
14271 html: function ( data ) {
14709 html: function ( data ) {
14272 return _empty(data) ?
14710 return _empty(data) ?
@@ -14277,7 +14715,7 b''
14277 .replace( _re_html, "" ) :
14715 .replace( _re_html, "" ) :
14278 '';
14716 '';
14279 },
14717 },
14280
14718
14281 string: function ( data ) {
14719 string: function ( data ) {
14282 return _empty(data) ?
14720 return _empty(data) ?
14283 data :
14721 data :
@@ -14286,35 +14724,35 b''
14286 data;
14724 data;
14287 }
14725 }
14288 } );
14726 } );
14289
14727
14290
14728
14291
14729
14292 var __numericReplace = function ( d, decimalPlace, re1, re2 ) {
14730 var __numericReplace = function ( d, decimalPlace, re1, re2 ) {
14293 if ( d !== 0 && (!d || d === '-') ) {
14731 if ( d !== 0 && (!d || d === '-') ) {
14294 return -Infinity;
14732 return -Infinity;
14295 }
14733 }
14296
14734
14297 // If a decimal place other than `.` is used, it needs to be given to the
14735 // If a decimal place other than `.` is used, it needs to be given to the
14298 // function so we can detect it and replace with a `.` which is the only
14736 // function so we can detect it and replace with a `.` which is the only
14299 // decimal place Javascript recognises - it is not locale aware.
14737 // decimal place Javascript recognises - it is not locale aware.
14300 if ( decimalPlace ) {
14738 if ( decimalPlace ) {
14301 d = _numToDecimal( d, decimalPlace );
14739 d = _numToDecimal( d, decimalPlace );
14302 }
14740 }
14303
14741
14304 if ( d.replace ) {
14742 if ( d.replace ) {
14305 if ( re1 ) {
14743 if ( re1 ) {
14306 d = d.replace( re1, '' );
14744 d = d.replace( re1, '' );
14307 }
14745 }
14308
14746
14309 if ( re2 ) {
14747 if ( re2 ) {
14310 d = d.replace( re2, '' );
14748 d = d.replace( re2, '' );
14311 }
14749 }
14312 }
14750 }
14313
14751
14314 return d * 1;
14752 return d * 1;
14315 };
14753 };
14316
14754
14317
14755
14318 // Add the numeric 'deformatting' functions for sorting and search. This is done
14756 // Add the numeric 'deformatting' functions for sorting and search. This is done
14319 // in a function to provide an easy ability for the language options to add
14757 // in a function to provide an easy ability for the language options to add
14320 // additional methods if a non-period decimal place is used.
14758 // additional methods if a non-period decimal place is used.
@@ -14325,17 +14763,17 b''
14325 "num": function ( d ) {
14763 "num": function ( d ) {
14326 return __numericReplace( d, decimalPlace );
14764 return __numericReplace( d, decimalPlace );
14327 },
14765 },
14328
14766
14329 // Formatted numbers
14767 // Formatted numbers
14330 "num-fmt": function ( d ) {
14768 "num-fmt": function ( d ) {
14331 return __numericReplace( d, decimalPlace, _re_formatted_numeric );
14769 return __numericReplace( d, decimalPlace, _re_formatted_numeric );
14332 },
14770 },
14333
14771
14334 // HTML numeric
14772 // HTML numeric
14335 "html-num": function ( d ) {
14773 "html-num": function ( d ) {
14336 return __numericReplace( d, decimalPlace, _re_html );
14774 return __numericReplace( d, decimalPlace, _re_html );
14337 },
14775 },
14338
14776
14339 // HTML numeric, formatted
14777 // HTML numeric, formatted
14340 "html-num-fmt": function ( d ) {
14778 "html-num-fmt": function ( d ) {
14341 return __numericReplace( d, decimalPlace, _re_html, _re_formatted_numeric );
14779 return __numericReplace( d, decimalPlace, _re_html, _re_formatted_numeric );
@@ -14344,7 +14782,7 b''
14344 function ( key, fn ) {
14782 function ( key, fn ) {
14345 // Add the ordering method
14783 // Add the ordering method
14346 _ext.type.order[ key+decimalPlace+'-pre' ] = fn;
14784 _ext.type.order[ key+decimalPlace+'-pre' ] = fn;
14347
14785
14348 // For HTML types add a search formatter that will strip the HTML
14786 // For HTML types add a search formatter that will strip the HTML
14349 if ( key.match(/^html\-/) ) {
14787 if ( key.match(/^html\-/) ) {
14350 _ext.type.search[ key+decimalPlace ] = _ext.type.search.html;
14788 _ext.type.search[ key+decimalPlace ] = _ext.type.search.html;
@@ -14352,15 +14790,15 b''
14352 }
14790 }
14353 );
14791 );
14354 }
14792 }
14355
14793
14356
14794
14357 // Default sort methods
14795 // Default sort methods
14358 $.extend( _ext.type.order, {
14796 $.extend( _ext.type.order, {
14359 // Dates
14797 // Dates
14360 "date-pre": function ( d ) {
14798 "date-pre": function ( d ) {
14361 return Date.parse( d ) || 0;
14799 return Date.parse( d ) || -Infinity;
14362 },
14800 },
14363
14801
14364 // html
14802 // html
14365 "html-pre": function ( a ) {
14803 "html-pre": function ( a ) {
14366 return _empty(a) ?
14804 return _empty(a) ?
@@ -14369,7 +14807,7 b''
14369 a.replace( /<.*?>/g, "" ).toLowerCase() :
14807 a.replace( /<.*?>/g, "" ).toLowerCase() :
14370 a+'';
14808 a+'';
14371 },
14809 },
14372
14810
14373 // string
14811 // string
14374 "string-pre": function ( a ) {
14812 "string-pre": function ( a ) {
14375 // This is a little complex, but faster than always calling toString,
14813 // This is a little complex, but faster than always calling toString,
@@ -14382,23 +14820,23 b''
14382 '' :
14820 '' :
14383 a.toString();
14821 a.toString();
14384 },
14822 },
14385
14823
14386 // string-asc and -desc are retained only for compatibility with the old
14824 // string-asc and -desc are retained only for compatibility with the old
14387 // sort methods
14825 // sort methods
14388 "string-asc": function ( x, y ) {
14826 "string-asc": function ( x, y ) {
14389 return ((x < y) ? -1 : ((x > y) ? 1 : 0));
14827 return ((x < y) ? -1 : ((x > y) ? 1 : 0));
14390 },
14828 },
14391
14829
14392 "string-desc": function ( x, y ) {
14830 "string-desc": function ( x, y ) {
14393 return ((x < y) ? 1 : ((x > y) ? -1 : 0));
14831 return ((x < y) ? 1 : ((x > y) ? -1 : 0));
14394 }
14832 }
14395 } );
14833 } );
14396
14834
14397
14835
14398 // Numeric sorting types - order doesn't matter here
14836 // Numeric sorting types - order doesn't matter here
14399 _addNumericSort( '' );
14837 _addNumericSort( '' );
14400
14838
14401
14839
14402 $.extend( true, DataTable.ext.renderer, {
14840 $.extend( true, DataTable.ext.renderer, {
14403 header: {
14841 header: {
14404 _: function ( settings, cell, column, classes ) {
14842 _: function ( settings, cell, column, classes ) {
@@ -14411,9 +14849,9 b''
14411 if ( settings !== ctx ) { // need to check this this is the host
14849 if ( settings !== ctx ) { // need to check this this is the host
14412 return; // table, not a nested one
14850 return; // table, not a nested one
14413 }
14851 }
14414
14852
14415 var colIdx = column.idx;
14853 var colIdx = column.idx;
14416
14854
14417 cell
14855 cell
14418 .removeClass(
14856 .removeClass(
14419 column.sSortingClass +' '+
14857 column.sSortingClass +' '+
@@ -14427,7 +14865,7 b''
14427 );
14865 );
14428 } );
14866 } );
14429 },
14867 },
14430
14868
14431 jqueryui: function ( settings, cell, column, classes ) {
14869 jqueryui: function ( settings, cell, column, classes ) {
14432 $('<div/>')
14870 $('<div/>')
14433 .addClass( classes.sSortJUIWrapper )
14871 .addClass( classes.sSortJUIWrapper )
@@ -14436,15 +14874,15 b''
14436 .addClass( classes.sSortIcon+' '+column.sSortingClassJUI )
14874 .addClass( classes.sSortIcon+' '+column.sSortingClassJUI )
14437 )
14875 )
14438 .appendTo( cell );
14876 .appendTo( cell );
14439
14877
14440 // Attach a sort listener to update on sort
14878 // Attach a sort listener to update on sort
14441 $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {
14879 $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {
14442 if ( settings !== ctx ) {
14880 if ( settings !== ctx ) {
14443 return;
14881 return;
14444 }
14882 }
14445
14883
14446 var colIdx = column.idx;
14884 var colIdx = column.idx;
14447
14885
14448 cell
14886 cell
14449 .removeClass( classes.sSortAsc +" "+classes.sSortDesc )
14887 .removeClass( classes.sSortAsc +" "+classes.sSortDesc )
14450 .addClass( columns[ colIdx ] == 'asc' ?
14888 .addClass( columns[ colIdx ] == 'asc' ?
@@ -14452,7 +14890,7 b''
14452 classes.sSortDesc :
14890 classes.sSortDesc :
14453 column.sSortingClass
14891 column.sSortingClass
14454 );
14892 );
14455
14893
14456 cell
14894 cell
14457 .find( 'span.'+classes.sSortIcon )
14895 .find( 'span.'+classes.sSortIcon )
14458 .removeClass(
14896 .removeClass(
@@ -14471,14 +14909,20 b''
14471 }
14909 }
14472 }
14910 }
14473 } );
14911 } );
14474
14912
14475 /*
14913 /*
14476 * Public helper functions. These aren't used internally by DataTables, or
14914 * Public helper functions. These aren't used internally by DataTables, or
14477 * called by any of the options passed into DataTables, but they can be used
14915 * called by any of the options passed into DataTables, but they can be used
14478 * externally by developers working with DataTables. They are helper functions
14916 * externally by developers working with DataTables. They are helper functions
14479 * to make working with DataTables a little bit easier.
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 * Helpers for `columns.render`.
14927 * Helpers for `columns.render`.
14484 *
14928 *
@@ -14487,11 +14931,14 b''
14487 *
14931 *
14488 * * `number` - Will format numeric data (defined by `columns.data`) for
14932 * * `number` - Will format numeric data (defined by `columns.data`) for
14489 * display, retaining the original unformatted data for sorting and filtering.
14933 * display, retaining the original unformatted data for sorting and filtering.
14490 * It takes 4 parameters:
14934 * It takes 5 parameters:
14491 * * `string` - Thousands grouping separator
14935 * * `string` - Thousands grouping separator
14492 * * `string` - Decimal point indicator
14936 * * `string` - Decimal point indicator
14493 * * `integer` - Number of decimal points to show
14937 * * `integer` - Number of decimal points to show
14494 * * `string` (optional) - Prefix.
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 * @example
14943 * @example
14497 * // Column definition using the number renderer
14944 * // Column definition using the number renderer
@@ -14503,34 +14950,55 b''
14503 * @namespace
14950 * @namespace
14504 */
14951 */
14505 DataTable.render = {
14952 DataTable.render = {
14506 number: function ( thousands, decimal, precision, prefix ) {
14953 number: function ( thousands, decimal, precision, prefix, postfix ) {
14507 return {
14954 return {
14508 display: function ( d ) {
14955 display: function ( d ) {
14956 if ( typeof d !== 'number' && typeof d !== 'string' ) {
14957 return d;
14958 }
14959
14509 var negative = d < 0 ? '-' : '';
14960 var negative = d < 0 ? '-' : '';
14510 d = Math.abs( parseFloat( d ) );
14961 var flo = parseFloat( d );
14511
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 var intPart = parseInt( d, 10 );
14973 var intPart = parseInt( d, 10 );
14513 var floatPart = precision ?
14974 var floatPart = precision ?
14514 decimal+(d - intPart).toFixed( precision ).substring( 2 ):
14975 decimal+(d - intPart).toFixed( precision ).substring( 2 ):
14515 '';
14976 '';
14516
14977
14517 return negative + (prefix||'') +
14978 return negative + (prefix||'') +
14518 intPart.toString().replace(
14979 intPart.toString().replace(
14519 /\B(?=(\d{3})+(?!\d))/g, thousands
14980 /\B(?=(\d{3})+(?!\d))/g, thousands
14520 ) +
14981 ) +
14521 floatPart;
14982 floatPart +
14522 }
14983 (postfix||'');
14984 }
14985 };
14986 },
14987
14988 text: function () {
14989 return {
14990 display: __htmlEscapeEntities
14523 };
14991 };
14524 }
14992 }
14525 };
14993 };
14526
14994
14527
14995
14528 /*
14996 /*
14529 * This is really a good bit rubbish this method of exposing the internal methods
14997 * This is really a good bit rubbish this method of exposing the internal methods
14530 * publicly... - To be fixed in 2.0 using methods on the prototype
14998 * publicly... - To be fixed in 2.0 using methods on the prototype
14531 */
14999 */
14532
15000
14533
15001
14534 /**
15002 /**
14535 * Create a wrapper function for exporting an internal functions to an external API.
15003 * Create a wrapper function for exporting an internal functions to an external API.
14536 * @param {string} fn API function name
15004 * @param {string} fn API function name
@@ -14546,8 +15014,8 b''
14546 return DataTable.ext.internal[fn].apply( this, args );
15014 return DataTable.ext.internal[fn].apply( this, args );
14547 };
15015 };
14548 }
15016 }
14549
15017
14550
15018
14551 /**
15019 /**
14552 * Reference to internal functions for use by plug-in developers. Note that
15020 * Reference to internal functions for use by plug-in developers. Note that
14553 * these methods are references to internal functions and are considered to be
15021 * these methods are references to internal functions and are considered to be
@@ -14622,11 +15090,9 b''
14622 _fnCalculateColumnWidths: _fnCalculateColumnWidths,
15090 _fnCalculateColumnWidths: _fnCalculateColumnWidths,
14623 _fnThrottle: _fnThrottle,
15091 _fnThrottle: _fnThrottle,
14624 _fnConvertToWidth: _fnConvertToWidth,
15092 _fnConvertToWidth: _fnConvertToWidth,
14625 _fnScrollingWidthAdjust: _fnScrollingWidthAdjust,
14626 _fnGetWidestNode: _fnGetWidestNode,
15093 _fnGetWidestNode: _fnGetWidestNode,
14627 _fnGetMaxLenString: _fnGetMaxLenString,
15094 _fnGetMaxLenString: _fnGetMaxLenString,
14628 _fnStringToCss: _fnStringToCss,
15095 _fnStringToCss: _fnStringToCss,
14629 _fnScrollBarWidth: _fnScrollBarWidth,
14630 _fnSortFlatten: _fnSortFlatten,
15096 _fnSortFlatten: _fnSortFlatten,
14631 _fnSort: _fnSort,
15097 _fnSort: _fnSort,
14632 _fnSortAria: _fnSortAria,
15098 _fnSortAria: _fnSortAria,
@@ -14650,11 +15116,14 b''
14650 // in 1.10, so this dead-end function is
15116 // in 1.10, so this dead-end function is
14651 // added to prevent errors
15117 // added to prevent errors
14652 } );
15118 } );
14653
15119
14654
15120
14655 // jQuery access
15121 // jQuery access
14656 $.fn.dataTable = DataTable;
15122 $.fn.dataTable = DataTable;
14657
15123
15124 // Provide access to the host jQuery object (circular reference)
15125 DataTable.$ = $;
15126
14658 // Legacy aliases
15127 // Legacy aliases
14659 $.fn.dataTableSettings = DataTable.settings;
15128 $.fn.dataTableSettings = DataTable.settings;
14660 $.fn.dataTableExt = DataTable.ext;
15129 $.fn.dataTableExt = DataTable.ext;
@@ -14835,7 +15304,4 b''
14835 */
15304 */
14836
15305
14837 return $.fn.dataTable;
15306 return $.fn.dataTable;
14838 }));
15307 })); No newline at end of file
14839
14840 }(window, document));
14841
General Comments 0
You need to be logged in to leave comments. Login now