##// 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
2 * ©2008-2014 SpryMedia Ltd - datatables.net/license
1 /*! DataTables 1.10.13
2 * ©2008-2016 SpryMedia Ltd - datatables.net/license
3 3 */
4 4
5 5 /**
6 6 * @summary DataTables
7 7 * @description Paginate, search and order HTML tables
8 * @version 1.10.4
8 * @version 1.10.13
9 9 * @file jquery.dataTables.js
10 * @author SpryMedia Ltd (www.sprymedia.co.uk)
11 * @contact www.sprymedia.co.uk/contact
12 * @copyright Copyright 2008-2014 SpryMedia Ltd.
10 * @author SpryMedia Ltd
11 * @contact www.datatables.net
12 * @copyright Copyright 2008-2016 SpryMedia Ltd.
13 13 *
14 14 * This source file is free software, available under the following license:
15 15 * MIT license - http://datatables.net/license
@@ -22,28 +22,41 b''
22 22 */
23 23
24 24 /*jslint evil: true, undef: true, browser: true */
25 /*globals $,require,jQuery,define,_selector_run,_selector_opts,_selector_first,_selector_row_indexes,_ext,_Api,_api_register,_api_registerPlural,_re_new_lines,_re_html,_re_formatted_numeric,_re_escape_regex,_empty,_intVal,_numToDecimal,_isNumber,_isHtml,_htmlNumeric,_pluck,_pluck_order,_range,_stripHtml,_unique,_fnBuildAjax,_fnAjaxUpdate,_fnAjaxParameters,_fnAjaxUpdateDraw,_fnAjaxDataSrc,_fnAddColumn,_fnColumnOptions,_fnAdjustColumnSizing,_fnVisibleToColumnIndex,_fnColumnIndexToVisible,_fnVisbleColumns,_fnGetColumns,_fnColumnTypes,_fnApplyColumnDefs,_fnHungarianMap,_fnCamelToHungarian,_fnLanguageCompat,_fnBrowserDetect,_fnAddData,_fnAddTr,_fnNodeToDataIndex,_fnNodeToColumnIndex,_fnGetCellData,_fnSetCellData,_fnSplitObjNotation,_fnGetObjectDataFn,_fnSetObjectDataFn,_fnGetDataMaster,_fnClearTable,_fnDeleteIndex,_fnInvalidate,_fnGetRowElements,_fnCreateTr,_fnBuildHead,_fnDrawHead,_fnDraw,_fnReDraw,_fnAddOptionsHtml,_fnDetectHeader,_fnGetUniqueThs,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterColumn,_fnFilter,_fnFilterCreateSearch,_fnEscapeRegex,_fnFilterData,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnInfoMacros,_fnInitialise,_fnInitComplete,_fnLengthChange,_fnFeatureHtmlLength,_fnFeatureHtmlPaginate,_fnPageChange,_fnFeatureHtmlProcessing,_fnProcessingDisplay,_fnFeatureHtmlTable,_fnScrollDraw,_fnApplyToChildren,_fnCalculateColumnWidths,_fnThrottle,_fnConvertToWidth,_fnScrollingWidthAdjust,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnScrollBarWidth,_fnSortFlatten,_fnSort,_fnSortAria,_fnSortListener,_fnSortAttachListener,_fnSortingClasses,_fnSortData,_fnSaveState,_fnLoadState,_fnSettingsFromNode,_fnLog,_fnMap,_fnBindAction,_fnCallbackReg,_fnCallbackFire,_fnLengthOverflow,_fnRenderer,_fnDataSource,_fnRowAttributes*/
26
27 (/** @lends <global> */function( window, document, undefined ) {
25 /*globals $,require,jQuery,define,_selector_run,_selector_opts,_selector_first,_selector_row_indexes,_ext,_Api,_api_register,_api_registerPlural,_re_new_lines,_re_html,_re_formatted_numeric,_re_escape_regex,_empty,_intVal,_numToDecimal,_isNumber,_isHtml,_htmlNumeric,_pluck,_pluck_order,_range,_stripHtml,_unique,_fnBuildAjax,_fnAjaxUpdate,_fnAjaxParameters,_fnAjaxUpdateDraw,_fnAjaxDataSrc,_fnAddColumn,_fnColumnOptions,_fnAdjustColumnSizing,_fnVisibleToColumnIndex,_fnColumnIndexToVisible,_fnVisbleColumns,_fnGetColumns,_fnColumnTypes,_fnApplyColumnDefs,_fnHungarianMap,_fnCamelToHungarian,_fnLanguageCompat,_fnBrowserDetect,_fnAddData,_fnAddTr,_fnNodeToDataIndex,_fnNodeToColumnIndex,_fnGetCellData,_fnSetCellData,_fnSplitObjNotation,_fnGetObjectDataFn,_fnSetObjectDataFn,_fnGetDataMaster,_fnClearTable,_fnDeleteIndex,_fnInvalidate,_fnGetRowElements,_fnCreateTr,_fnBuildHead,_fnDrawHead,_fnDraw,_fnReDraw,_fnAddOptionsHtml,_fnDetectHeader,_fnGetUniqueThs,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterColumn,_fnFilter,_fnFilterCreateSearch,_fnEscapeRegex,_fnFilterData,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnInfoMacros,_fnInitialise,_fnInitComplete,_fnLengthChange,_fnFeatureHtmlLength,_fnFeatureHtmlPaginate,_fnPageChange,_fnFeatureHtmlProcessing,_fnProcessingDisplay,_fnFeatureHtmlTable,_fnScrollDraw,_fnApplyToChildren,_fnCalculateColumnWidths,_fnThrottle,_fnConvertToWidth,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnSortFlatten,_fnSort,_fnSortAria,_fnSortListener,_fnSortAttachListener,_fnSortingClasses,_fnSortData,_fnSaveState,_fnLoadState,_fnSettingsFromNode,_fnLog,_fnMap,_fnBindAction,_fnCallbackReg,_fnCallbackFire,_fnLengthOverflow,_fnRenderer,_fnDataSource,_fnRowAttributes*/
28 26
29 27 (function( factory ) {
30 28 "use strict";
31 29
32 30 if ( typeof define === 'function' && define.amd ) {
33 // Define as an AMD module if possible
34 define( 'datatables', ['jquery'], factory );
35 }
36 else if ( typeof exports === 'object' ) {
37 // Node/CommonJS
38 factory( require( 'jquery' ) );
39 }
40 else if ( jQuery && !jQuery.fn.dataTable ) {
41 // Define using browser globals otherwise
42 // Prevent multiple instantiations if the script is loaded twice
43 factory( jQuery );
31 // AMD
32 define( ['jquery'], function ( $ ) {
33 return factory( $, window, document );
34 } );
35 }
36 else if ( typeof exports === 'object' ) {
37 // CommonJS
38 module.exports = function (root, $) {
39 if ( ! root ) {
40 // CommonJS environments without a window global must pass a
41 // root. This will give an error otherwise
42 root = window;
43 }
44
45 if ( ! $ ) {
46 $ = typeof window !== 'undefined' ? // jQuery's factory checks for a global window
47 require('jquery') :
48 require('jquery')( root );
49 }
50
51 return factory( $, root, root.document );
52 };
53 }
54 else {
55 // Browser
56 factory( jQuery, window, document );
44 57 }
45 58 }
46 (/** @lends <global> */function( $ ) {
59 (function( $, window, document, undefined ) {
47 60 "use strict";
48 61
49 62 /**
@@ -78,5192 +91,7 b''
78 91 * } );
79 92 * } );
80 93 */
81 var DataTable;
82
83
84 /*
85 * It is useful to have variables which are scoped locally so only the
86 * DataTables functions can access them and they don't leak into global space.
87 * At the same time these functions are often useful over multiple files in the
88 * core and API, so we list, or at least document, all variables which are used
89 * by DataTables as private variables here. This also ensures that there is no
90 * clashing of variable names and that they can easily referenced for reuse.
91 */
92
93
94 // Defined else where
95 // _selector_run
96 // _selector_opts
97 // _selector_first
98 // _selector_row_indexes
99
100 var _ext; // DataTable.ext
101 var _Api; // DataTable.Api
102 var _api_register; // DataTable.Api.register
103 var _api_registerPlural; // DataTable.Api.registerPlural
104
105 var _re_dic = {};
106 var _re_new_lines = /[\r\n]/g;
107 var _re_html = /<.*?>/g;
108 var _re_date_start = /^[\w\+\-]/;
109 var _re_date_end = /[\w\+\-]$/;
110
111 // Escape regular expression special characters
112 var _re_escape_regex = new RegExp( '(\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ].join('|\\') + ')', 'g' );
113
114 // U+2009 is thin space and U+202F is narrow no-break space, both used in many
115 // standards as thousands separators
116 var _re_formatted_numeric = /[',$£€¥%\u2009\u202F]/g;
117
118
119 var _empty = function ( d ) {
120 return !d || d === true || d === '-' ? true : false;
121 };
122
123
124 var _intVal = function ( s ) {
125 var integer = parseInt( s, 10 );
126 return !isNaN(integer) && isFinite(s) ? integer : null;
127 };
128
129 // Convert from a formatted number with characters other than `.` as the
130 // decimal place, to a Javascript number
131 var _numToDecimal = function ( num, decimalPoint ) {
132 // Cache created regular expressions for speed as this function is called often
133 if ( ! _re_dic[ decimalPoint ] ) {
134 _re_dic[ decimalPoint ] = new RegExp( _fnEscapeRegex( decimalPoint ), 'g' );
135 }
136 return typeof num === 'string' && decimalPoint !== '.' ?
137 num.replace( /\./g, '' ).replace( _re_dic[ decimalPoint ], '.' ) :
138 num;
139 };
140
141
142 var _isNumber = function ( d, decimalPoint, formatted ) {
143 var strType = typeof d === 'string';
144
145 if ( decimalPoint && strType ) {
146 d = _numToDecimal( d, decimalPoint );
147 }
148
149 if ( formatted && strType ) {
150 d = d.replace( _re_formatted_numeric, '' );
151 }
152
153 return _empty( d ) || (!isNaN( parseFloat(d) ) && isFinite( d ));
154 };
155
156
157 // A string without HTML in it can be considered to be HTML still
158 var _isHtml = function ( d ) {
159 return _empty( d ) || typeof d === 'string';
160 };
161
162
163 var _htmlNumeric = function ( d, decimalPoint, formatted ) {
164 if ( _empty( d ) ) {
165 return true;
166 }
167
168 var html = _isHtml( d );
169 return ! html ?
170 null :
171 _isNumber( _stripHtml( d ), decimalPoint, formatted ) ?
172 true :
173 null;
174 };
175
176
177 var _pluck = function ( a, prop, prop2 ) {
178 var out = [];
179 var i=0, ien=a.length;
180
181 // Could have the test in the loop for slightly smaller code, but speed
182 // is essential here
183 if ( prop2 !== undefined ) {
184 for ( ; i<ien ; i++ ) {
185 if ( a[i] && a[i][ prop ] ) {
186 out.push( a[i][ prop ][ prop2 ] );
187 }
188 }
189 }
190 else {
191 for ( ; i<ien ; i++ ) {
192 if ( a[i] ) {
193 out.push( a[i][ prop ] );
194 }
195 }
196 }
197
198 return out;
199 };
200
201
202 // Basically the same as _pluck, but rather than looping over `a` we use `order`
203 // as the indexes to pick from `a`
204 var _pluck_order = function ( a, order, prop, prop2 )
205 {
206 var out = [];
207 var i=0, ien=order.length;
208
209 // Could have the test in the loop for slightly smaller code, but speed
210 // is essential here
211 if ( prop2 !== undefined ) {
212 for ( ; i<ien ; i++ ) {
213 if ( a[ order[i] ][ prop ] ) {
214 out.push( a[ order[i] ][ prop ][ prop2 ] );
215 }
216 }
217 }
218 else {
219 for ( ; i<ien ; i++ ) {
220 out.push( a[ order[i] ][ prop ] );
221 }
222 }
223
224 return out;
225 };
226
227
228 var _range = function ( len, start )
229 {
230 var out = [];
231 var end;
232
233 if ( start === undefined ) {
234 start = 0;
235 end = len;
236 }
237 else {
238 end = start;
239 start = len;
240 }
241
242 for ( var i=start ; i<end ; i++ ) {
243 out.push( i );
244 }
245
246 return out;
247 };
248
249
250 var _removeEmpty = function ( a )
251 {
252 var out = [];
253
254 for ( var i=0, ien=a.length ; i<ien ; i++ ) {
255 if ( a[i] ) { // careful - will remove all falsy values!
256 out.push( a[i] );
257 }
258 }
259
260 return out;
261 };
262
263
264 var _stripHtml = function ( d ) {
265 return d.replace( _re_html, '' );
266 };
267
268
269 /**
270 * Find the unique elements in a source array.
271 *
272 * @param {array} src Source array
273 * @return {array} Array of unique items
274 * @ignore
275 */
276 var _unique = function ( src )
277 {
278 // A faster unique method is to use object keys to identify used values,
279 // but this doesn't work with arrays or objects, which we must also
280 // consider. See jsperf.com/compare-array-unique-versions/4 for more
281 // information.
282 var
283 out = [],
284 val,
285 i, ien=src.length,
286 j, k=0;
287
288 again: for ( i=0 ; i<ien ; i++ ) {
289 val = src[i];
290
291 for ( j=0 ; j<k ; j++ ) {
292 if ( out[j] === val ) {
293 continue again;
294 }
295 }
296
297 out.push( val );
298 k++;
299 }
300
301 return out;
302 };
303
304
305
306 /**
307 * Create a mapping object that allows camel case parameters to be looked up
308 * for their Hungarian counterparts. The mapping is stored in a private
309 * parameter called `_hungarianMap` which can be accessed on the source object.
310 * @param {object} o
311 * @memberof DataTable#oApi
312 */
313 function _fnHungarianMap ( o )
314 {
315 var
316 hungarian = 'a aa ai ao as b fn i m o s ',
317 match,
318 newKey,
319 map = {};
320
321 $.each( o, function (key, val) {
322 match = key.match(/^([^A-Z]+?)([A-Z])/);
323
324 if ( match && hungarian.indexOf(match[1]+' ') !== -1 )
325 {
326 newKey = key.replace( match[0], match[2].toLowerCase() );
327 map[ newKey ] = key;
328
329 if ( match[1] === 'o' )
330 {
331 _fnHungarianMap( o[key] );
332 }
333 }
334 } );
335
336 o._hungarianMap = map;
337 }
338
339
340 /**
341 * Convert from camel case parameters to Hungarian, based on a Hungarian map
342 * created by _fnHungarianMap.
343 * @param {object} src The model object which holds all parameters that can be
344 * mapped.
345 * @param {object} user The object to convert from camel case to Hungarian.
346 * @param {boolean} force When set to `true`, properties which already have a
347 * Hungarian value in the `user` object will be overwritten. Otherwise they
348 * won't be.
349 * @memberof DataTable#oApi
350 */
351 function _fnCamelToHungarian ( src, user, force )
352 {
353 if ( ! src._hungarianMap ) {
354 _fnHungarianMap( src );
355 }
356
357 var hungarianKey;
358
359 $.each( user, function (key, val) {
360 hungarianKey = src._hungarianMap[ key ];
361
362 if ( hungarianKey !== undefined && (force || user[hungarianKey] === undefined) )
363 {
364 // For objects, we need to buzz down into the object to copy parameters
365 if ( hungarianKey.charAt(0) === 'o' )
366 {
367 // Copy the camelCase options over to the hungarian
368 if ( ! user[ hungarianKey ] ) {
369 user[ hungarianKey ] = {};
370 }
371 $.extend( true, user[hungarianKey], user[key] );
372
373 _fnCamelToHungarian( src[hungarianKey], user[hungarianKey], force );
374 }
375 else {
376 user[hungarianKey] = user[ key ];
377 }
378 }
379 } );
380 }
381
382
383 /**
384 * Language compatibility - when certain options are given, and others aren't, we
385 * need to duplicate the values over, in order to provide backwards compatibility
386 * with older language files.
387 * @param {object} oSettings dataTables settings object
388 * @memberof DataTable#oApi
389 */
390 function _fnLanguageCompat( lang )
391 {
392 var defaults = DataTable.defaults.oLanguage;
393 var zeroRecords = lang.sZeroRecords;
394
395 /* Backwards compatibility - if there is no sEmptyTable given, then use the same as
396 * sZeroRecords - assuming that is given.
397 */
398 if ( ! lang.sEmptyTable && zeroRecords &&
399 defaults.sEmptyTable === "No data available in table" )
400 {
401 _fnMap( lang, lang, 'sZeroRecords', 'sEmptyTable' );
402 }
403
404 /* Likewise with loading records */
405 if ( ! lang.sLoadingRecords && zeroRecords &&
406 defaults.sLoadingRecords === "Loading..." )
407 {
408 _fnMap( lang, lang, 'sZeroRecords', 'sLoadingRecords' );
409 }
410
411 // Old parameter name of the thousands separator mapped onto the new
412 if ( lang.sInfoThousands ) {
413 lang.sThousands = lang.sInfoThousands;
414 }
415
416 var decimal = lang.sDecimal;
417 if ( decimal ) {
418 _addNumericSort( decimal );
419 }
420 }
421
422
423 /**
424 * Map one parameter onto another
425 * @param {object} o Object to map
426 * @param {*} knew The new parameter name
427 * @param {*} old The old parameter name
428 */
429 var _fnCompatMap = function ( o, knew, old ) {
430 if ( o[ knew ] !== undefined ) {
431 o[ old ] = o[ knew ];
432 }
433 };
434
435
436 /**
437 * Provide backwards compatibility for the main DT options. Note that the new
438 * options are mapped onto the old parameters, so this is an external interface
439 * change only.
440 * @param {object} init Object to map
441 */
442 function _fnCompatOpts ( init )
443 {
444 _fnCompatMap( init, 'ordering', 'bSort' );
445 _fnCompatMap( init, 'orderMulti', 'bSortMulti' );
446 _fnCompatMap( init, 'orderClasses', 'bSortClasses' );
447 _fnCompatMap( init, 'orderCellsTop', 'bSortCellsTop' );
448 _fnCompatMap( init, 'order', 'aaSorting' );
449 _fnCompatMap( init, 'orderFixed', 'aaSortingFixed' );
450 _fnCompatMap( init, 'paging', 'bPaginate' );
451 _fnCompatMap( init, 'pagingType', 'sPaginationType' );
452 _fnCompatMap( init, 'pageLength', 'iDisplayLength' );
453 _fnCompatMap( init, 'searching', 'bFilter' );
454
455 // Column search objects are in an array, so it needs to be converted
456 // element by element
457 var searchCols = init.aoSearchCols;
458
459 if ( searchCols ) {
460 for ( var i=0, ien=searchCols.length ; i<ien ; i++ ) {
461 if ( searchCols[i] ) {
462 _fnCamelToHungarian( DataTable.models.oSearch, searchCols[i] );
463 }
464 }
465 }
466 }
467
468
469 /**
470 * Provide backwards compatibility for column options. Note that the new options
471 * are mapped onto the old parameters, so this is an external interface change
472 * only.
473 * @param {object} init Object to map
474 */
475 function _fnCompatCols ( init )
476 {
477 _fnCompatMap( init, 'orderable', 'bSortable' );
478 _fnCompatMap( init, 'orderData', 'aDataSort' );
479 _fnCompatMap( init, 'orderSequence', 'asSorting' );
480 _fnCompatMap( init, 'orderDataType', 'sortDataType' );
481 }
482
483
484 /**
485 * Browser feature detection for capabilities, quirks
486 * @param {object} settings dataTables settings object
487 * @memberof DataTable#oApi
488 */
489 function _fnBrowserDetect( settings )
490 {
491 var browser = settings.oBrowser;
492
493 // Scrolling feature / quirks detection
494 var n = $('<div/>')
495 .css( {
496 position: 'absolute',
497 top: 0,
498 left: 0,
499 height: 1,
500 width: 1,
501 overflow: 'hidden'
502 } )
503 .append(
504 $('<div/>')
505 .css( {
506 position: 'absolute',
507 top: 1,
508 left: 1,
509 width: 100,
510 overflow: 'scroll'
511 } )
512 .append(
513 $('<div class="test"/>')
514 .css( {
515 width: '100%',
516 height: 10
517 } )
518 )
519 )
520 .appendTo( 'body' );
521
522 var test = n.find('.test');
523
524 // IE6/7 will oversize a width 100% element inside a scrolling element, to
525 // include the width of the scrollbar, while other browsers ensure the inner
526 // element is contained without forcing scrolling
527 browser.bScrollOversize = test[0].offsetWidth === 100;
528
529 // In rtl text layout, some browsers (most, but not all) will place the
530 // scrollbar on the left, rather than the right.
531 browser.bScrollbarLeft = test.offset().left !== 1;
532
533 n.remove();
534 }
535
536
537 /**
538 * Array.prototype reduce[Right] method, used for browsers which don't support
539 * JS 1.6. Done this way to reduce code size, since we iterate either way
540 * @param {object} settings dataTables settings object
541 * @memberof DataTable#oApi
542 */
543 function _fnReduce ( that, fn, init, start, end, inc )
544 {
545 var
546 i = start,
547 value,
548 isSet = false;
549
550 if ( init !== undefined ) {
551 value = init;
552 isSet = true;
553 }
554
555 while ( i !== end ) {
556 if ( ! that.hasOwnProperty(i) ) {
557 continue;
558 }
559
560 value = isSet ?
561 fn( value, that[i], i, that ) :
562 that[i];
563
564 isSet = true;
565 i += inc;
566 }
567
568 return value;
569 }
570
571 /**
572 * Add a column to the list used for the table with default values
573 * @param {object} oSettings dataTables settings object
574 * @param {node} nTh The th element for this column
575 * @memberof DataTable#oApi
576 */
577 function _fnAddColumn( oSettings, nTh )
578 {
579 // Add column to aoColumns array
580 var oDefaults = DataTable.defaults.column;
581 var iCol = oSettings.aoColumns.length;
582 var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, {
583 "nTh": nTh ? nTh : document.createElement('th'),
584 "sTitle": oDefaults.sTitle ? oDefaults.sTitle : nTh ? nTh.innerHTML : '',
585 "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol],
586 "mData": oDefaults.mData ? oDefaults.mData : iCol,
587 idx: iCol
588 } );
589 oSettings.aoColumns.push( oCol );
590
591 // Add search object for column specific search. Note that the `searchCols[ iCol ]`
592 // passed into extend can be undefined. This allows the user to give a default
593 // with only some of the parameters defined, and also not give a default
594 var searchCols = oSettings.aoPreSearchCols;
595 searchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch, searchCols[ iCol ] );
596
597 // Use the default column options function to initialise classes etc
598 _fnColumnOptions( oSettings, iCol, null );
599 }
600
601
602 /**
603 * Apply options for a column
604 * @param {object} oSettings dataTables settings object
605 * @param {int} iCol column index to consider
606 * @param {object} oOptions object with sType, bVisible and bSearchable etc
607 * @memberof DataTable#oApi
608 */
609 function _fnColumnOptions( oSettings, iCol, oOptions )
610 {
611 var oCol = oSettings.aoColumns[ iCol ];
612 var oClasses = oSettings.oClasses;
613 var th = $(oCol.nTh);
614
615 // Try to get width information from the DOM. We can't get it from CSS
616 // as we'd need to parse the CSS stylesheet. `width` option can override
617 if ( ! oCol.sWidthOrig ) {
618 // Width attribute
619 oCol.sWidthOrig = th.attr('width') || null;
620
621 // Style attribute
622 var t = (th.attr('style') || '').match(/width:\s*(\d+[pxem%]+)/);
623 if ( t ) {
624 oCol.sWidthOrig = t[1];
625 }
626 }
627
628 /* User specified column options */
629 if ( oOptions !== undefined && oOptions !== null )
630 {
631 // Backwards compatibility
632 _fnCompatCols( oOptions );
633
634 // Map camel case parameters to their Hungarian counterparts
635 _fnCamelToHungarian( DataTable.defaults.column, oOptions );
636
637 /* Backwards compatibility for mDataProp */
638 if ( oOptions.mDataProp !== undefined && !oOptions.mData )
639 {
640 oOptions.mData = oOptions.mDataProp;
641 }
642
643 if ( oOptions.sType )
644 {
645 oCol._sManualType = oOptions.sType;
646 }
647
648 // `class` is a reserved word in Javascript, so we need to provide
649 // the ability to use a valid name for the camel case input
650 if ( oOptions.className && ! oOptions.sClass )
651 {
652 oOptions.sClass = oOptions.className;
653 }
654
655 $.extend( oCol, oOptions );
656 _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" );
657
658 /* iDataSort to be applied (backwards compatibility), but aDataSort will take
659 * priority if defined
660 */
661 if ( typeof oOptions.iDataSort === 'number' )
662 {
663 oCol.aDataSort = [ oOptions.iDataSort ];
664 }
665 _fnMap( oCol, oOptions, "aDataSort" );
666 }
667
668 /* Cache the data get and set functions for speed */
669 var mDataSrc = oCol.mData;
670 var mData = _fnGetObjectDataFn( mDataSrc );
671 var mRender = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null;
672
673 var attrTest = function( src ) {
674 return typeof src === 'string' && src.indexOf('@') !== -1;
675 };
676 oCol._bAttrSrc = $.isPlainObject( mDataSrc ) && (
677 attrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter)
678 );
679
680 oCol.fnGetData = function (rowData, type, meta) {
681 var innerData = mData( rowData, type, undefined, meta );
682
683 return mRender && type ?
684 mRender( innerData, type, rowData, meta ) :
685 innerData;
686 };
687 oCol.fnSetData = function ( rowData, val, meta ) {
688 return _fnSetObjectDataFn( mDataSrc )( rowData, val, meta );
689 };
690
691 // Indicate if DataTables should read DOM data as an object or array
692 // Used in _fnGetRowElements
693 if ( typeof mDataSrc !== 'number' ) {
694 oSettings._rowReadObject = true;
695 }
696
697 /* Feature sorting overrides column specific when off */
698 if ( !oSettings.oFeatures.bSort )
699 {
700 oCol.bSortable = false;
701 th.addClass( oClasses.sSortableNone ); // Have to add class here as order event isn't called
702 }
703
704 /* Check that the class assignment is correct for sorting */
705 var bAsc = $.inArray('asc', oCol.asSorting) !== -1;
706 var bDesc = $.inArray('desc', oCol.asSorting) !== -1;
707 if ( !oCol.bSortable || (!bAsc && !bDesc) )
708 {
709 oCol.sSortingClass = oClasses.sSortableNone;
710 oCol.sSortingClassJUI = "";
711 }
712 else if ( bAsc && !bDesc )
713 {
714 oCol.sSortingClass = oClasses.sSortableAsc;
715 oCol.sSortingClassJUI = oClasses.sSortJUIAscAllowed;
716 }
717 else if ( !bAsc && bDesc )
718 {
719 oCol.sSortingClass = oClasses.sSortableDesc;
720 oCol.sSortingClassJUI = oClasses.sSortJUIDescAllowed;
721 }
722 else
723 {
724 oCol.sSortingClass = oClasses.sSortable;
725 oCol.sSortingClassJUI = oClasses.sSortJUI;
726 }
727 }
728
729
730 /**
731 * Adjust the table column widths for new data. Note: you would probably want to
732 * do a redraw after calling this function!
733 * @param {object} settings dataTables settings object
734 * @memberof DataTable#oApi
735 */
736 function _fnAdjustColumnSizing ( settings )
737 {
738 /* Not interested in doing column width calculation if auto-width is disabled */
739 if ( settings.oFeatures.bAutoWidth !== false )
740 {
741 var columns = settings.aoColumns;
742
743 _fnCalculateColumnWidths( settings );
744 for ( var i=0 , iLen=columns.length ; i<iLen ; i++ )
745 {
746 columns[i].nTh.style.width = columns[i].sWidth;
747 }
748 }
749
750 var scroll = settings.oScroll;
751 if ( scroll.sY !== '' || scroll.sX !== '')
752 {
753 _fnScrollDraw( settings );
754 }
755
756 _fnCallbackFire( settings, null, 'column-sizing', [settings] );
757 }
758
759
760 /**
761 * Covert the index of a visible column to the index in the data array (take account
762 * of hidden columns)
763 * @param {object} oSettings dataTables settings object
764 * @param {int} iMatch Visible column index to lookup
765 * @returns {int} i the data index
766 * @memberof DataTable#oApi
767 */
768 function _fnVisibleToColumnIndex( oSettings, iMatch )
769 {
770 var aiVis = _fnGetColumns( oSettings, 'bVisible' );
771
772 return typeof aiVis[iMatch] === 'number' ?
773 aiVis[iMatch] :
774 null;
775 }
776
777
778 /**
779 * Covert the index of an index in the data array and convert it to the visible
780 * column index (take account of hidden columns)
781 * @param {int} iMatch Column index to lookup
782 * @param {object} oSettings dataTables settings object
783 * @returns {int} i the data index
784 * @memberof DataTable#oApi
785 */
786 function _fnColumnIndexToVisible( oSettings, iMatch )
787 {
788 var aiVis = _fnGetColumns( oSettings, 'bVisible' );
789 var iPos = $.inArray( iMatch, aiVis );
790
791 return iPos !== -1 ? iPos : null;
792 }
793
794
795 /**
796 * Get the number of visible columns
797 * @param {object} oSettings dataTables settings object
798 * @returns {int} i the number of visible columns
799 * @memberof DataTable#oApi
800 */
801 function _fnVisbleColumns( oSettings )
802 {
803 return _fnGetColumns( oSettings, 'bVisible' ).length;
804 }
805
806
807 /**
808 * Get an array of column indexes that match a given property
809 * @param {object} oSettings dataTables settings object
810 * @param {string} sParam Parameter in aoColumns to look for - typically
811 * bVisible or bSearchable
812 * @returns {array} Array of indexes with matched properties
813 * @memberof DataTable#oApi
814 */
815 function _fnGetColumns( oSettings, sParam )
816 {
817 var a = [];
818
819 $.map( oSettings.aoColumns, function(val, i) {
820 if ( val[sParam] ) {
821 a.push( i );
822 }
823 } );
824
825 return a;
826 }
827
828
829 /**
830 * Calculate the 'type' of a column
831 * @param {object} settings dataTables settings object
832 * @memberof DataTable#oApi
833 */
834 function _fnColumnTypes ( settings )
835 {
836 var columns = settings.aoColumns;
837 var data = settings.aoData;
838 var types = DataTable.ext.type.detect;
839 var i, ien, j, jen, k, ken;
840 var col, cell, detectedType, cache;
841
842 // For each column, spin over the
843 for ( i=0, ien=columns.length ; i<ien ; i++ ) {
844 col = columns[i];
845 cache = [];
846
847 if ( ! col.sType && col._sManualType ) {
848 col.sType = col._sManualType;
849 }
850 else if ( ! col.sType ) {
851 for ( j=0, jen=types.length ; j<jen ; j++ ) {
852 for ( k=0, ken=data.length ; k<ken ; k++ ) {
853 // Use a cache array so we only need to get the type data
854 // from the formatter once (when using multiple detectors)
855 if ( cache[k] === undefined ) {
856 cache[k] = _fnGetCellData( settings, k, i, 'type' );
857 }
858
859 detectedType = types[j]( cache[k], settings );
860
861 // If null, then this type can't apply to this column, so
862 // rather than testing all cells, break out. There is an
863 // exception for the last type which is `html`. We need to
864 // scan all rows since it is possible to mix string and HTML
865 // types
866 if ( ! detectedType && j !== types.length-1 ) {
867 break;
868 }
869
870 // Only a single match is needed for html type since it is
871 // bottom of the pile and very similar to string
872 if ( detectedType === 'html' ) {
873 break;
874 }
875 }
876
877 // Type is valid for all data points in the column - use this
878 // type
879 if ( detectedType ) {
880 col.sType = detectedType;
881 break;
882 }
883 }
884
885 // Fall back - if no type was detected, always use string
886 if ( ! col.sType ) {
887 col.sType = 'string';
888 }
889 }
890 }
891 }
892
893
894 /**
895 * Take the column definitions and static columns arrays and calculate how
896 * they relate to column indexes. The callback function will then apply the
897 * definition found for a column to a suitable configuration object.
898 * @param {object} oSettings dataTables settings object
899 * @param {array} aoColDefs The aoColumnDefs array that is to be applied
900 * @param {array} aoCols The aoColumns array that defines columns individually
901 * @param {function} fn Callback function - takes two parameters, the calculated
902 * column index and the definition for that column.
903 * @memberof DataTable#oApi
904 */
905 function _fnApplyColumnDefs( oSettings, aoColDefs, aoCols, fn )
906 {
907 var i, iLen, j, jLen, k, kLen, def;
908 var columns = oSettings.aoColumns;
909
910 // Column definitions with aTargets
911 if ( aoColDefs )
912 {
913 /* Loop over the definitions array - loop in reverse so first instance has priority */
914 for ( i=aoColDefs.length-1 ; i>=0 ; i-- )
915 {
916 def = aoColDefs[i];
917
918 /* Each definition can target multiple columns, as it is an array */
919 var aTargets = def.targets !== undefined ?
920 def.targets :
921 def.aTargets;
922
923 if ( ! $.isArray( aTargets ) )
924 {
925 aTargets = [ aTargets ];
926 }
927
928 for ( j=0, jLen=aTargets.length ; j<jLen ; j++ )
929 {
930 if ( typeof aTargets[j] === 'number' && aTargets[j] >= 0 )
931 {
932 /* Add columns that we don't yet know about */
933 while( columns.length <= aTargets[j] )
934 {
935 _fnAddColumn( oSettings );
936 }
937
938 /* Integer, basic index */
939 fn( aTargets[j], def );
940 }
941 else if ( typeof aTargets[j] === 'number' && aTargets[j] < 0 )
942 {
943 /* Negative integer, right to left column counting */
944 fn( columns.length+aTargets[j], def );
945 }
946 else if ( typeof aTargets[j] === 'string' )
947 {
948 /* Class name matching on TH element */
949 for ( k=0, kLen=columns.length ; k<kLen ; k++ )
950 {
951 if ( aTargets[j] == "_all" ||
952 $(columns[k].nTh).hasClass( aTargets[j] ) )
953 {
954 fn( k, def );
955 }
956 }
957 }
958 }
959 }
960 }
961
962 // Statically defined columns array
963 if ( aoCols )
964 {
965 for ( i=0, iLen=aoCols.length ; i<iLen ; i++ )
966 {
967 fn( i, aoCols[i] );
968 }
969 }
970 }
971
972 /**
973 * Add a data array to the table, creating DOM node etc. This is the parallel to
974 * _fnGatherData, but for adding rows from a Javascript source, rather than a
975 * DOM source.
976 * @param {object} oSettings dataTables settings object
977 * @param {array} aData data array to be added
978 * @param {node} [nTr] TR element to add to the table - optional. If not given,
979 * DataTables will create a row automatically
980 * @param {array} [anTds] Array of TD|TH elements for the row - must be given
981 * if nTr is.
982 * @returns {int} >=0 if successful (index of new aoData entry), -1 if failed
983 * @memberof DataTable#oApi
984 */
985 function _fnAddData ( oSettings, aDataIn, nTr, anTds )
986 {
987 /* Create the object for storing information about this new row */
988 var iRow = oSettings.aoData.length;
989 var oData = $.extend( true, {}, DataTable.models.oRow, {
990 src: nTr ? 'dom' : 'data'
991 } );
992
993 oData._aData = aDataIn;
994 oSettings.aoData.push( oData );
995
996 /* Create the cells */
997 var nTd, sThisType;
998 var columns = oSettings.aoColumns;
999 for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
1000 {
1001 // When working with a row, the data source object must be populated. In
1002 // all other cases, the data source object is already populated, so we
1003 // don't overwrite it, which might break bindings etc
1004 if ( nTr ) {
1005 _fnSetCellData( oSettings, iRow, i, _fnGetCellData( oSettings, iRow, i ) );
1006 }
1007 columns[i].sType = null;
1008 }
1009
1010 /* Add to the display array */
1011 oSettings.aiDisplayMaster.push( iRow );
1012
1013 /* Create the DOM information, or register it if already present */
1014 if ( nTr || ! oSettings.oFeatures.bDeferRender )
1015 {
1016 _fnCreateTr( oSettings, iRow, nTr, anTds );
1017 }
1018
1019 return iRow;
1020 }
1021
1022
1023 /**
1024 * Add one or more TR elements to the table. Generally we'd expect to
1025 * use this for reading data from a DOM sourced table, but it could be
1026 * used for an TR element. Note that if a TR is given, it is used (i.e.
1027 * it is not cloned).
1028 * @param {object} settings dataTables settings object
1029 * @param {array|node|jQuery} trs The TR element(s) to add to the table
1030 * @returns {array} Array of indexes for the added rows
1031 * @memberof DataTable#oApi
1032 */
1033 function _fnAddTr( settings, trs )
1034 {
1035 var row;
1036
1037 // Allow an individual node to be passed in
1038 if ( ! (trs instanceof $) ) {
1039 trs = $(trs);
1040 }
1041
1042 return trs.map( function (i, el) {
1043 row = _fnGetRowElements( settings, el );
1044 return _fnAddData( settings, row.data, el, row.cells );
1045 } );
1046 }
1047
1048
1049 /**
1050 * Take a TR element and convert it to an index in aoData
1051 * @param {object} oSettings dataTables settings object
1052 * @param {node} n the TR element to find
1053 * @returns {int} index if the node is found, null if not
1054 * @memberof DataTable#oApi
1055 */
1056 function _fnNodeToDataIndex( oSettings, n )
1057 {
1058 return (n._DT_RowIndex!==undefined) ? n._DT_RowIndex : null;
1059 }
1060
1061
1062 /**
1063 * Take a TD element and convert it into a column data index (not the visible index)
1064 * @param {object} oSettings dataTables settings object
1065 * @param {int} iRow The row number the TD/TH can be found in
1066 * @param {node} n The TD/TH element to find
1067 * @returns {int} index if the node is found, -1 if not
1068 * @memberof DataTable#oApi
1069 */
1070 function _fnNodeToColumnIndex( oSettings, iRow, n )
1071 {
1072 return $.inArray( n, oSettings.aoData[ iRow ].anCells );
1073 }
1074
1075
1076 /**
1077 * Get the data for a given cell from the internal cache, taking into account data mapping
1078 * @param {object} settings dataTables settings object
1079 * @param {int} rowIdx aoData row id
1080 * @param {int} colIdx Column index
1081 * @param {string} type data get type ('display', 'type' 'filter' 'sort')
1082 * @returns {*} Cell data
1083 * @memberof DataTable#oApi
1084 */
1085 function _fnGetCellData( settings, rowIdx, colIdx, type )
1086 {
1087 var draw = settings.iDraw;
1088 var col = settings.aoColumns[colIdx];
1089 var rowData = settings.aoData[rowIdx]._aData;
1090 var defaultContent = col.sDefaultContent;
1091 var cellData = col.fnGetData( rowData, type, {
1092 settings: settings,
1093 row: rowIdx,
1094 col: colIdx
1095 } );
1096
1097 if ( cellData === undefined ) {
1098 if ( settings.iDrawError != draw && defaultContent === null ) {
1099 _fnLog( settings, 0, "Requested unknown parameter "+
1100 (typeof col.mData=='function' ? '{function}' : "'"+col.mData+"'")+
1101 " for row "+rowIdx, 4 );
1102 settings.iDrawError = draw;
1103 }
1104 return defaultContent;
1105 }
1106
1107 /* When the data source is null, we can use default column data */
1108 if ( (cellData === rowData || cellData === null) && defaultContent !== null ) {
1109 cellData = defaultContent;
1110 }
1111 else if ( typeof cellData === 'function' ) {
1112 // If the data source is a function, then we run it and use the return,
1113 // executing in the scope of the data object (for instances)
1114 return cellData.call( rowData );
1115 }
1116
1117 if ( cellData === null && type == 'display' ) {
1118 return '';
1119 }
1120 return cellData;
1121 }
1122
1123
1124 /**
1125 * Set the value for a specific cell, into the internal data cache
1126 * @param {object} settings dataTables settings object
1127 * @param {int} rowIdx aoData row id
1128 * @param {int} colIdx Column index
1129 * @param {*} val Value to set
1130 * @memberof DataTable#oApi
1131 */
1132 function _fnSetCellData( settings, rowIdx, colIdx, val )
1133 {
1134 var col = settings.aoColumns[colIdx];
1135 var rowData = settings.aoData[rowIdx]._aData;
1136
1137 col.fnSetData( rowData, val, {
1138 settings: settings,
1139 row: rowIdx,
1140 col: colIdx
1141 } );
1142 }
1143
1144
1145 // Private variable that is used to match action syntax in the data property object
1146 var __reArray = /\[.*?\]$/;
1147 var __reFn = /\(\)$/;
1148
1149 /**
1150 * Split string on periods, taking into account escaped periods
1151 * @param {string} str String to split
1152 * @return {array} Split string
1153 */
1154 function _fnSplitObjNotation( str )
1155 {
1156 return $.map( str.match(/(\\.|[^\.])+/g), function ( s ) {
1157 return s.replace(/\\./g, '.');
1158 } );
1159 }
1160
1161
1162 /**
1163 * Return a function that can be used to get data from a source object, taking
1164 * into account the ability to use nested objects as a source
1165 * @param {string|int|function} mSource The data source for the object
1166 * @returns {function} Data get function
1167 * @memberof DataTable#oApi
1168 */
1169 function _fnGetObjectDataFn( mSource )
1170 {
1171 if ( $.isPlainObject( mSource ) )
1172 {
1173 /* Build an object of get functions, and wrap them in a single call */
1174 var o = {};
1175 $.each( mSource, function (key, val) {
1176 if ( val ) {
1177 o[key] = _fnGetObjectDataFn( val );
1178 }
1179 } );
1180
1181 return function (data, type, row, meta) {
1182 var t = o[type] || o._;
1183 return t !== undefined ?
1184 t(data, type, row, meta) :
1185 data;
1186 };
1187 }
1188 else if ( mSource === null )
1189 {
1190 /* Give an empty string for rendering / sorting etc */
1191 return function (data) { // type, row and meta also passed, but not used
1192 return data;
1193 };
1194 }
1195 else if ( typeof mSource === 'function' )
1196 {
1197 return function (data, type, row, meta) {
1198 return mSource( data, type, row, meta );
1199 };
1200 }
1201 else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 ||
1202 mSource.indexOf('[') !== -1 || mSource.indexOf('(') !== -1) )
1203 {
1204 /* If there is a . in the source string then the data source is in a
1205 * nested object so we loop over the data for each level to get the next
1206 * level down. On each loop we test for undefined, and if found immediately
1207 * return. This allows entire objects to be missing and sDefaultContent to
1208 * be used if defined, rather than throwing an error
1209 */
1210 var fetchData = function (data, type, src) {
1211 var arrayNotation, funcNotation, out, innerSrc;
1212
1213 if ( src !== "" )
1214 {
1215 var a = _fnSplitObjNotation( src );
1216
1217 for ( var i=0, iLen=a.length ; i<iLen ; i++ )
1218 {
1219 // Check if we are dealing with special notation
1220 arrayNotation = a[i].match(__reArray);
1221 funcNotation = a[i].match(__reFn);
1222
1223 if ( arrayNotation )
1224 {
1225 // Array notation
1226 a[i] = a[i].replace(__reArray, '');
1227
1228 // Condition allows simply [] to be passed in
1229 if ( a[i] !== "" ) {
1230 data = data[ a[i] ];
1231 }
1232 out = [];
1233
1234 // Get the remainder of the nested object to get
1235 a.splice( 0, i+1 );
1236 innerSrc = a.join('.');
1237
1238 // Traverse each entry in the array getting the properties requested
1239 for ( var j=0, jLen=data.length ; j<jLen ; j++ ) {
1240 out.push( fetchData( data[j], type, innerSrc ) );
1241 }
1242
1243 // If a string is given in between the array notation indicators, that
1244 // is used to join the strings together, otherwise an array is returned
1245 var join = arrayNotation[0].substring(1, arrayNotation[0].length-1);
1246 data = (join==="") ? out : out.join(join);
1247
1248 // The inner call to fetchData has already traversed through the remainder
1249 // of the source requested, so we exit from the loop
1250 break;
1251 }
1252 else if ( funcNotation )
1253 {
1254 // Function call
1255 a[i] = a[i].replace(__reFn, '');
1256 data = data[ a[i] ]();
1257 continue;
1258 }
1259
1260 if ( data === null || data[ a[i] ] === undefined )
1261 {
1262 return undefined;
1263 }
1264 data = data[ a[i] ];
1265 }
1266 }
1267
1268 return data;
1269 };
1270
1271 return function (data, type) { // row and meta also passed, but not used
1272 return fetchData( data, type, mSource );
1273 };
1274 }
1275 else
1276 {
1277 /* Array or flat object mapping */
1278 return function (data, type) { // row and meta also passed, but not used
1279 return data[mSource];
1280 };
1281 }
1282 }
1283
1284
1285 /**
1286 * Return a function that can be used to set data from a source object, taking
1287 * into account the ability to use nested objects as a source
1288 * @param {string|int|function} mSource The data source for the object
1289 * @returns {function} Data set function
1290 * @memberof DataTable#oApi
1291 */
1292 function _fnSetObjectDataFn( mSource )
1293 {
1294 if ( $.isPlainObject( mSource ) )
1295 {
1296 /* Unlike get, only the underscore (global) option is used for for
1297 * setting data since we don't know the type here. This is why an object
1298 * option is not documented for `mData` (which is read/write), but it is
1299 * for `mRender` which is read only.
1300 */
1301 return _fnSetObjectDataFn( mSource._ );
1302 }
1303 else if ( mSource === null )
1304 {
1305 /* Nothing to do when the data source is null */
1306 return function () {};
1307 }
1308 else if ( typeof mSource === 'function' )
1309 {
1310 return function (data, val, meta) {
1311 mSource( data, 'set', val, meta );
1312 };
1313 }
1314 else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 ||
1315 mSource.indexOf('[') !== -1 || mSource.indexOf('(') !== -1) )
1316 {
1317 /* Like the get, we need to get data from a nested object */
1318 var setData = function (data, val, src) {
1319 var a = _fnSplitObjNotation( src ), b;
1320 var aLast = a[a.length-1];
1321 var arrayNotation, funcNotation, o, innerSrc;
1322
1323 for ( var i=0, iLen=a.length-1 ; i<iLen ; i++ )
1324 {
1325 // Check if we are dealing with an array notation request
1326 arrayNotation = a[i].match(__reArray);
1327 funcNotation = a[i].match(__reFn);
1328
1329 if ( arrayNotation )
1330 {
1331 a[i] = a[i].replace(__reArray, '');
1332 data[ a[i] ] = [];
1333
1334 // Get the remainder of the nested object to set so we can recurse
1335 b = a.slice();
1336 b.splice( 0, i+1 );
1337 innerSrc = b.join('.');
1338
1339 // Traverse each entry in the array setting the properties requested
1340 for ( var j=0, jLen=val.length ; j<jLen ; j++ )
1341 {
1342 o = {};
1343 setData( o, val[j], innerSrc );
1344 data[ a[i] ].push( o );
1345 }
1346
1347 // The inner call to setData has already traversed through the remainder
1348 // of the source and has set the data, thus we can exit here
1349 return;
1350 }
1351 else if ( funcNotation )
1352 {
1353 // Function call
1354 a[i] = a[i].replace(__reFn, '');
1355 data = data[ a[i] ]( val );
1356 }
1357
1358 // If the nested object doesn't currently exist - since we are
1359 // trying to set the value - create it
1360 if ( data[ a[i] ] === null || data[ a[i] ] === undefined )
1361 {
1362 data[ a[i] ] = {};
1363 }
1364 data = data[ a[i] ];
1365 }
1366
1367 // Last item in the input - i.e, the actual set
1368 if ( aLast.match(__reFn ) )
1369 {
1370 // Function call
1371 data = data[ aLast.replace(__reFn, '') ]( val );
1372 }
1373 else
1374 {
1375 // If array notation is used, we just want to strip it and use the property name
1376 // and assign the value. If it isn't used, then we get the result we want anyway
1377 data[ aLast.replace(__reArray, '') ] = val;
1378 }
1379 };
1380
1381 return function (data, val) { // meta is also passed in, but not used
1382 return setData( data, val, mSource );
1383 };
1384 }
1385 else
1386 {
1387 /* Array or flat object mapping */
1388 return function (data, val) { // meta is also passed in, but not used
1389 data[mSource] = val;
1390 };
1391 }
1392 }
1393
1394
1395 /**
1396 * Return an array with the full table data
1397 * @param {object} oSettings dataTables settings object
1398 * @returns array {array} aData Master data array
1399 * @memberof DataTable#oApi
1400 */
1401 function _fnGetDataMaster ( settings )
1402 {
1403 return _pluck( settings.aoData, '_aData' );
1404 }
1405
1406
1407 /**
1408 * Nuke the table
1409 * @param {object} oSettings dataTables settings object
1410 * @memberof DataTable#oApi
1411 */
1412 function _fnClearTable( settings )
1413 {
1414 settings.aoData.length = 0;
1415 settings.aiDisplayMaster.length = 0;
1416 settings.aiDisplay.length = 0;
1417 }
1418
1419
1420 /**
1421 * Take an array of integers (index array) and remove a target integer (value - not
1422 * the key!)
1423 * @param {array} a Index array to target
1424 * @param {int} iTarget value to find
1425 * @memberof DataTable#oApi
1426 */
1427 function _fnDeleteIndex( a, iTarget, splice )
1428 {
1429 var iTargetIndex = -1;
1430
1431 for ( var i=0, iLen=a.length ; i<iLen ; i++ )
1432 {
1433 if ( a[i] == iTarget )
1434 {
1435 iTargetIndex = i;
1436 }
1437 else if ( a[i] > iTarget )
1438 {
1439 a[i]--;
1440 }
1441 }
1442
1443 if ( iTargetIndex != -1 && splice === undefined )
1444 {
1445 a.splice( iTargetIndex, 1 );
1446 }
1447 }
1448
1449
1450 /**
1451 * Mark cached data as invalid such that a re-read of the data will occur when
1452 * the cached data is next requested. Also update from the data source object.
1453 *
1454 * @param {object} settings DataTables settings object
1455 * @param {int} rowIdx Row index to invalidate
1456 * @param {string} [src] Source to invalidate from: undefined, 'auto', 'dom'
1457 * or 'data'
1458 * @param {int} [colIdx] Column index to invalidate. If undefined the whole
1459 * row will be invalidated
1460 * @memberof DataTable#oApi
1461 *
1462 * @todo For the modularisation of v1.11 this will need to become a callback, so
1463 * the sort and filter methods can subscribe to it. That will required
1464 * initialisation options for sorting, which is why it is not already baked in
1465 */
1466 function _fnInvalidate( settings, rowIdx, src, colIdx )
1467 {
1468 var row = settings.aoData[ rowIdx ];
1469 var i, ien;
1470 var cellWrite = function ( cell, col ) {
1471 // This is very frustrating, but in IE if you just write directly
1472 // to innerHTML, and elements that are overwritten are GC'ed,
1473 // even if there is a reference to them elsewhere
1474 while ( cell.childNodes.length ) {
1475 cell.removeChild( cell.firstChild );
1476 }
1477
1478 cell.innerHTML = _fnGetCellData( settings, rowIdx, col, 'display' );
1479 };
1480
1481 // Are we reading last data from DOM or the data object?
1482 if ( src === 'dom' || ((! src || src === 'auto') && row.src === 'dom') ) {
1483 // Read the data from the DOM
1484 row._aData = _fnGetRowElements(
1485 settings, row, colIdx, colIdx === undefined ? undefined : row._aData
1486 )
1487 .data;
1488 }
1489 else {
1490 // Reading from data object, update the DOM
1491 var cells = row.anCells;
1492
1493 if ( cells ) {
1494 if ( colIdx !== undefined ) {
1495 cellWrite( cells[colIdx], colIdx );
1496 }
1497 else {
1498 for ( i=0, ien=cells.length ; i<ien ; i++ ) {
1499 cellWrite( cells[i], i );
1500 }
1501 }
1502 }
1503 }
1504
1505 // For both row and cell invalidation, the cached data for sorting and
1506 // filtering is nulled out
1507 row._aSortData = null;
1508 row._aFilterData = null;
1509
1510 // Invalidate the type for a specific column (if given) or all columns since
1511 // the data might have changed
1512 var cols = settings.aoColumns;
1513 if ( colIdx !== undefined ) {
1514 cols[ colIdx ].sType = null;
1515 }
1516 else {
1517 for ( i=0, ien=cols.length ; i<ien ; i++ ) {
1518 cols[i].sType = null;
1519 }
1520
1521 // Update DataTables special `DT_*` attributes for the row
1522 _fnRowAttributes( row );
1523 }
1524 }
1525
1526
1527 /**
1528 * Build a data source object from an HTML row, reading the contents of the
1529 * cells that are in the row.
1530 *
1531 * @param {object} settings DataTables settings object
1532 * @param {node|object} TR element from which to read data or existing row
1533 * object from which to re-read the data from the cells
1534 * @param {int} [colIdx] Optional column index
1535 * @param {array|object} [d] Data source object. If `colIdx` is given then this
1536 * parameter should also be given and will be used to write the data into.
1537 * Only the column in question will be written
1538 * @returns {object} Object with two parameters: `data` the data read, in
1539 * document order, and `cells` and array of nodes (they can be useful to the
1540 * caller, so rather than needing a second traversal to get them, just return
1541 * them from here).
1542 * @memberof DataTable#oApi
1543 */
1544 function _fnGetRowElements( settings, row, colIdx, d )
1545 {
1546 var
1547 tds = [],
1548 td = row.firstChild,
1549 name, col, o, i=0, contents,
1550 columns = settings.aoColumns,
1551 objectRead = settings._rowReadObject;
1552
1553 // Allow the data object to be passed in, or construct
1554 d = d || objectRead ? {} : [];
1555
1556 var attr = function ( str, td ) {
1557 if ( typeof str === 'string' ) {
1558 var idx = str.indexOf('@');
1559
1560 if ( idx !== -1 ) {
1561 var attr = str.substring( idx+1 );
1562 var setter = _fnSetObjectDataFn( str );
1563 setter( d, td.getAttribute( attr ) );
1564 }
1565 }
1566 };
1567
1568 // Read data from a cell and store into the data object
1569 var cellProcess = function ( cell ) {
1570 if ( colIdx === undefined || colIdx === i ) {
1571 col = columns[i];
1572 contents = $.trim(cell.innerHTML);
1573
1574 if ( col && col._bAttrSrc ) {
1575 var setter = _fnSetObjectDataFn( col.mData._ );
1576 setter( d, contents );
1577
1578 attr( col.mData.sort, cell );
1579 attr( col.mData.type, cell );
1580 attr( col.mData.filter, cell );
1581 }
1582 else {
1583 // Depending on the `data` option for the columns the data can
1584 // be read to either an object or an array.
1585 if ( objectRead ) {
1586 if ( ! col._setter ) {
1587 // Cache the setter function
1588 col._setter = _fnSetObjectDataFn( col.mData );
1589 }
1590 col._setter( d, contents );
1591 }
1592 else {
1593 d[i] = contents;
1594 }
1595 }
1596 }
1597
1598 i++;
1599 };
1600
1601 if ( td ) {
1602 // `tr` element was passed in
1603 while ( td ) {
1604 name = td.nodeName.toUpperCase();
1605
1606 if ( name == "TD" || name == "TH" ) {
1607 cellProcess( td );
1608 tds.push( td );
1609 }
1610
1611 td = td.nextSibling;
1612 }
1613 }
1614 else {
1615 // Existing row object passed in
1616 tds = row.anCells;
1617
1618 for ( var j=0, jen=tds.length ; j<jen ; j++ ) {
1619 cellProcess( tds[j] );
1620 }
1621 }
1622
1623 return {
1624 data: d,
1625 cells: tds
1626 };
1627 }
1628 /**
1629 * Create a new TR element (and it's TD children) for a row
1630 * @param {object} oSettings dataTables settings object
1631 * @param {int} iRow Row to consider
1632 * @param {node} [nTrIn] TR element to add to the table - optional. If not given,
1633 * DataTables will create a row automatically
1634 * @param {array} [anTds] Array of TD|TH elements for the row - must be given
1635 * if nTr is.
1636 * @memberof DataTable#oApi
1637 */
1638 function _fnCreateTr ( oSettings, iRow, nTrIn, anTds )
1639 {
1640 var
1641 row = oSettings.aoData[iRow],
1642 rowData = row._aData,
1643 cells = [],
1644 nTr, nTd, oCol,
1645 i, iLen;
1646
1647 if ( row.nTr === null )
1648 {
1649 nTr = nTrIn || document.createElement('tr');
1650
1651 row.nTr = nTr;
1652 row.anCells = cells;
1653
1654 /* Use a private property on the node to allow reserve mapping from the node
1655 * to the aoData array for fast look up
1656 */
1657 nTr._DT_RowIndex = iRow;
1658
1659 /* Special parameters can be given by the data source to be used on the row */
1660 _fnRowAttributes( row );
1661
1662 /* Process each column */
1663 for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
1664 {
1665 oCol = oSettings.aoColumns[i];
1666
1667 nTd = nTrIn ? anTds[i] : document.createElement( oCol.sCellType );
1668 cells.push( nTd );
1669
1670 // Need to create the HTML if new, or if a rendering function is defined
1671 if ( !nTrIn || oCol.mRender || oCol.mData !== i )
1672 {
1673 nTd.innerHTML = _fnGetCellData( oSettings, iRow, i, 'display' );
1674 }
1675
1676 /* Add user defined class */
1677 if ( oCol.sClass )
1678 {
1679 nTd.className += ' '+oCol.sClass;
1680 }
1681
1682 // Visibility - add or remove as required
1683 if ( oCol.bVisible && ! nTrIn )
1684 {
1685 nTr.appendChild( nTd );
1686 }
1687 else if ( ! oCol.bVisible && nTrIn )
1688 {
1689 nTd.parentNode.removeChild( nTd );
1690 }
1691
1692 if ( oCol.fnCreatedCell )
1693 {
1694 oCol.fnCreatedCell.call( oSettings.oInstance,
1695 nTd, _fnGetCellData( oSettings, iRow, i ), rowData, iRow, i
1696 );
1697 }
1698 }
1699
1700 _fnCallbackFire( oSettings, 'aoRowCreatedCallback', null, [nTr, rowData, iRow] );
1701 }
1702
1703 // Remove once webkit bug 131819 and Chromium bug 365619 have been resolved
1704 // and deployed
1705 row.nTr.setAttribute( 'role', 'row' );
1706 }
1707
1708
1709 /**
1710 * Add attributes to a row based on the special `DT_*` parameters in a data
1711 * source object.
1712 * @param {object} DataTables row object for the row to be modified
1713 * @memberof DataTable#oApi
1714 */
1715 function _fnRowAttributes( row )
1716 {
1717 var tr = row.nTr;
1718 var data = row._aData;
1719
1720 if ( tr ) {
1721 if ( data.DT_RowId ) {
1722 tr.id = data.DT_RowId;
1723 }
1724
1725 if ( data.DT_RowClass ) {
1726 // Remove any classes added by DT_RowClass before
1727 var a = data.DT_RowClass.split(' ');
1728 row.__rowc = row.__rowc ?
1729 _unique( row.__rowc.concat( a ) ) :
1730 a;
1731
1732 $(tr)
1733 .removeClass( row.__rowc.join(' ') )
1734 .addClass( data.DT_RowClass );
1735 }
1736
1737 if ( data.DT_RowData ) {
1738 $(tr).data( data.DT_RowData );
1739 }
1740 }
1741 }
1742
1743
1744 /**
1745 * Create the HTML header for the table
1746 * @param {object} oSettings dataTables settings object
1747 * @memberof DataTable#oApi
1748 */
1749 function _fnBuildHead( oSettings )
1750 {
1751 var i, ien, cell, row, column;
1752 var thead = oSettings.nTHead;
1753 var tfoot = oSettings.nTFoot;
1754 var createHeader = $('th, td', thead).length === 0;
1755 var classes = oSettings.oClasses;
1756 var columns = oSettings.aoColumns;
1757
1758 if ( createHeader ) {
1759 row = $('<tr/>').appendTo( thead );
1760 }
1761
1762 for ( i=0, ien=columns.length ; i<ien ; i++ ) {
1763 column = columns[i];
1764 cell = $( column.nTh ).addClass( column.sClass );
1765
1766 if ( createHeader ) {
1767 cell.appendTo( row );
1768 }
1769
1770 // 1.11 move into sorting
1771 if ( oSettings.oFeatures.bSort ) {
1772 cell.addClass( column.sSortingClass );
1773
1774 if ( column.bSortable !== false ) {
1775 cell
1776 .attr( 'tabindex', oSettings.iTabIndex )
1777 .attr( 'aria-controls', oSettings.sTableId );
1778
1779 _fnSortAttachListener( oSettings, column.nTh, i );
1780 }
1781 }
1782
1783 if ( column.sTitle != cell.html() ) {
1784 cell.html( column.sTitle );
1785 }
1786
1787 _fnRenderer( oSettings, 'header' )(
1788 oSettings, cell, column, classes
1789 );
1790 }
1791
1792 if ( createHeader ) {
1793 _fnDetectHeader( oSettings.aoHeader, thead );
1794 }
1795
1796 /* ARIA role for the rows */
1797 $(thead).find('>tr').attr('role', 'row');
1798
1799 /* Deal with the footer - add classes if required */
1800 $(thead).find('>tr>th, >tr>td').addClass( classes.sHeaderTH );
1801 $(tfoot).find('>tr>th, >tr>td').addClass( classes.sFooterTH );
1802
1803 // Cache the footer cells. Note that we only take the cells from the first
1804 // row in the footer. If there is more than one row the user wants to
1805 // interact with, they need to use the table().foot() method. Note also this
1806 // allows cells to be used for multiple columns using colspan
1807 if ( tfoot !== null ) {
1808 var cells = oSettings.aoFooter[0];
1809
1810 for ( i=0, ien=cells.length ; i<ien ; i++ ) {
1811 column = columns[i];
1812 column.nTf = cells[i].cell;
1813
1814 if ( column.sClass ) {
1815 $(column.nTf).addClass( column.sClass );
1816 }
1817 }
1818 }
1819 }
1820
1821
1822 /**
1823 * Draw the header (or footer) element based on the column visibility states. The
1824 * methodology here is to use the layout array from _fnDetectHeader, modified for
1825 * the instantaneous column visibility, to construct the new layout. The grid is
1826 * traversed over cell at a time in a rows x columns grid fashion, although each
1827 * cell insert can cover multiple elements in the grid - which is tracks using the
1828 * aApplied array. Cell inserts in the grid will only occur where there isn't
1829 * already a cell in that position.
1830 * @param {object} oSettings dataTables settings object
1831 * @param array {objects} aoSource Layout array from _fnDetectHeader
1832 * @param {boolean} [bIncludeHidden=false] If true then include the hidden columns in the calc,
1833 * @memberof DataTable#oApi
1834 */
1835 function _fnDrawHead( oSettings, aoSource, bIncludeHidden )
1836 {
1837 var i, iLen, j, jLen, k, kLen, n, nLocalTr;
1838 var aoLocal = [];
1839 var aApplied = [];
1840 var iColumns = oSettings.aoColumns.length;
1841 var iRowspan, iColspan;
1842
1843 if ( ! aoSource )
1844 {
1845 return;
1846 }
1847
1848 if ( bIncludeHidden === undefined )
1849 {
1850 bIncludeHidden = false;
1851 }
1852
1853 /* Make a copy of the master layout array, but without the visible columns in it */
1854 for ( i=0, iLen=aoSource.length ; i<iLen ; i++ )
1855 {
1856 aoLocal[i] = aoSource[i].slice();
1857 aoLocal[i].nTr = aoSource[i].nTr;
1858
1859 /* Remove any columns which are currently hidden */
1860 for ( j=iColumns-1 ; j>=0 ; j-- )
1861 {
1862 if ( !oSettings.aoColumns[j].bVisible && !bIncludeHidden )
1863 {
1864 aoLocal[i].splice( j, 1 );
1865 }
1866 }
1867
1868 /* Prep the applied array - it needs an element for each row */
1869 aApplied.push( [] );
1870 }
1871
1872 for ( i=0, iLen=aoLocal.length ; i<iLen ; i++ )
1873 {
1874 nLocalTr = aoLocal[i].nTr;
1875
1876 /* All cells are going to be replaced, so empty out the row */
1877 if ( nLocalTr )
1878 {
1879 while( (n = nLocalTr.firstChild) )
1880 {
1881 nLocalTr.removeChild( n );
1882 }
1883 }
1884
1885 for ( j=0, jLen=aoLocal[i].length ; j<jLen ; j++ )
1886 {
1887 iRowspan = 1;
1888 iColspan = 1;
1889
1890 /* Check to see if there is already a cell (row/colspan) covering our target
1891 * insert point. If there is, then there is nothing to do.
1892 */
1893 if ( aApplied[i][j] === undefined )
1894 {
1895 nLocalTr.appendChild( aoLocal[i][j].cell );
1896 aApplied[i][j] = 1;
1897
1898 /* Expand the cell to cover as many rows as needed */
1899 while ( aoLocal[i+iRowspan] !== undefined &&
1900 aoLocal[i][j].cell == aoLocal[i+iRowspan][j].cell )
1901 {
1902 aApplied[i+iRowspan][j] = 1;
1903 iRowspan++;
1904 }
1905
1906 /* Expand the cell to cover as many columns as needed */
1907 while ( aoLocal[i][j+iColspan] !== undefined &&
1908 aoLocal[i][j].cell == aoLocal[i][j+iColspan].cell )
1909 {
1910 /* Must update the applied array over the rows for the columns */
1911 for ( k=0 ; k<iRowspan ; k++ )
1912 {
1913 aApplied[i+k][j+iColspan] = 1;
1914 }
1915 iColspan++;
1916 }
1917
1918 /* Do the actual expansion in the DOM */
1919 $(aoLocal[i][j].cell)
1920 .attr('rowspan', iRowspan)
1921 .attr('colspan', iColspan);
1922 }
1923 }
1924 }
1925 }
1926
1927
1928 /**
1929 * Insert the required TR nodes into the table for display
1930 * @param {object} oSettings dataTables settings object
1931 * @memberof DataTable#oApi
1932 */
1933 function _fnDraw( oSettings )
1934 {
1935 /* Provide a pre-callback function which can be used to cancel the draw is false is returned */
1936 var aPreDraw = _fnCallbackFire( oSettings, 'aoPreDrawCallback', 'preDraw', [oSettings] );
1937 if ( $.inArray( false, aPreDraw ) !== -1 )
1938 {
1939 _fnProcessingDisplay( oSettings, false );
1940 return;
1941 }
1942
1943 var i, iLen, n;
1944 var anRows = [];
1945 var iRowCount = 0;
1946 var asStripeClasses = oSettings.asStripeClasses;
1947 var iStripes = asStripeClasses.length;
1948 var iOpenRows = oSettings.aoOpenRows.length;
1949 var oLang = oSettings.oLanguage;
1950 var iInitDisplayStart = oSettings.iInitDisplayStart;
1951 var bServerSide = _fnDataSource( oSettings ) == 'ssp';
1952 var aiDisplay = oSettings.aiDisplay;
1953
1954 oSettings.bDrawing = true;
1955
1956 /* Check and see if we have an initial draw position from state saving */
1957 if ( iInitDisplayStart !== undefined && iInitDisplayStart !== -1 )
1958 {
1959 oSettings._iDisplayStart = bServerSide ?
1960 iInitDisplayStart :
1961 iInitDisplayStart >= oSettings.fnRecordsDisplay() ?
1962 0 :
1963 iInitDisplayStart;
1964
1965 oSettings.iInitDisplayStart = -1;
1966 }
1967
1968 var iDisplayStart = oSettings._iDisplayStart;
1969 var iDisplayEnd = oSettings.fnDisplayEnd();
1970
1971 /* Server-side processing draw intercept */
1972 if ( oSettings.bDeferLoading )
1973 {
1974 oSettings.bDeferLoading = false;
1975 oSettings.iDraw++;
1976 _fnProcessingDisplay( oSettings, false );
1977 }
1978 else if ( !bServerSide )
1979 {
1980 oSettings.iDraw++;
1981 }
1982 else if ( !oSettings.bDestroying && !_fnAjaxUpdate( oSettings ) )
1983 {
1984 return;
1985 }
1986
1987 if ( aiDisplay.length !== 0 )
1988 {
1989 var iStart = bServerSide ? 0 : iDisplayStart;
1990 var iEnd = bServerSide ? oSettings.aoData.length : iDisplayEnd;
1991
1992 for ( var j=iStart ; j<iEnd ; j++ )
1993 {
1994 var iDataIndex = aiDisplay[j];
1995 var aoData = oSettings.aoData[ iDataIndex ];
1996 if ( aoData.nTr === null )
1997 {
1998 _fnCreateTr( oSettings, iDataIndex );
1999 }
2000
2001 var nRow = aoData.nTr;
2002
2003 /* Remove the old striping classes and then add the new one */
2004 if ( iStripes !== 0 )
2005 {
2006 var sStripe = asStripeClasses[ iRowCount % iStripes ];
2007 if ( aoData._sRowStripe != sStripe )
2008 {
2009 $(nRow).removeClass( aoData._sRowStripe ).addClass( sStripe );
2010 aoData._sRowStripe = sStripe;
2011 }
2012 }
2013
2014 // Row callback functions - might want to manipulate the row
2015 // iRowCount and j are not currently documented. Are they at all
2016 // useful?
2017 _fnCallbackFire( oSettings, 'aoRowCallback', null,
2018 [nRow, aoData._aData, iRowCount, j] );
2019
2020 anRows.push( nRow );
2021 iRowCount++;
2022 }
2023 }
2024 else
2025 {
2026 /* Table is empty - create a row with an empty message in it */
2027 var sZero = oLang.sZeroRecords;
2028 if ( oSettings.iDraw == 1 && _fnDataSource( oSettings ) == 'ajax' )
2029 {
2030 sZero = oLang.sLoadingRecords;
2031 }
2032 else if ( oLang.sEmptyTable && oSettings.fnRecordsTotal() === 0 )
2033 {
2034 sZero = oLang.sEmptyTable;
2035 }
2036
2037 anRows[ 0 ] = $( '<tr/>', { 'class': iStripes ? asStripeClasses[0] : '' } )
2038 .append( $('<td />', {
2039 'valign': 'top',
2040 'colSpan': _fnVisbleColumns( oSettings ),
2041 'class': oSettings.oClasses.sRowEmpty
2042 } ).html( sZero ) )[0];
2043 }
2044
2045 /* Header and footer callbacks */
2046 _fnCallbackFire( oSettings, 'aoHeaderCallback', 'header', [ $(oSettings.nTHead).children('tr')[0],
2047 _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );
2048
2049 _fnCallbackFire( oSettings, 'aoFooterCallback', 'footer', [ $(oSettings.nTFoot).children('tr')[0],
2050 _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );
2051
2052 var body = $(oSettings.nTBody);
2053
2054 body.children().detach();
2055 body.append( $(anRows) );
2056
2057 /* Call all required callback functions for the end of a draw */
2058 _fnCallbackFire( oSettings, 'aoDrawCallback', 'draw', [oSettings] );
2059
2060 /* Draw is complete, sorting and filtering must be as well */
2061 oSettings.bSorted = false;
2062 oSettings.bFiltered = false;
2063 oSettings.bDrawing = false;
2064 }
2065
2066
2067 /**
2068 * Redraw the table - taking account of the various features which are enabled
2069 * @param {object} oSettings dataTables settings object
2070 * @param {boolean} [holdPosition] Keep the current paging position. By default
2071 * the paging is reset to the first page
2072 * @memberof DataTable#oApi
2073 */
2074 function _fnReDraw( settings, holdPosition )
2075 {
2076 var
2077 features = settings.oFeatures,
2078 sort = features.bSort,
2079 filter = features.bFilter;
2080
2081 if ( sort ) {
2082 _fnSort( settings );
2083 }
2084
2085 if ( filter ) {
2086 _fnFilterComplete( settings, settings.oPreviousSearch );
2087 }
2088 else {
2089 // No filtering, so we want to just use the display master
2090 settings.aiDisplay = settings.aiDisplayMaster.slice();
2091 }
2092
2093 if ( holdPosition !== true ) {
2094 settings._iDisplayStart = 0;
2095 }
2096
2097 // Let any modules know about the draw hold position state (used by
2098 // scrolling internally)
2099 settings._drawHold = holdPosition;
2100
2101 _fnDraw( settings );
2102
2103 settings._drawHold = false;
2104 }
2105
2106
2107 /**
2108 * Add the options to the page HTML for the table
2109 * @param {object} oSettings dataTables settings object
2110 * @memberof DataTable#oApi
2111 */
2112 function _fnAddOptionsHtml ( oSettings )
2113 {
2114 var classes = oSettings.oClasses;
2115 var table = $(oSettings.nTable);
2116 var holding = $('<div/>').insertBefore( table ); // Holding element for speed
2117 var features = oSettings.oFeatures;
2118
2119 // All DataTables are wrapped in a div
2120 var insert = $('<div/>', {
2121 id: oSettings.sTableId+'_wrapper',
2122 'class': classes.sWrapper + (oSettings.nTFoot ? '' : ' '+classes.sNoFooter)
2123 } );
2124
2125 oSettings.nHolding = holding[0];
2126 oSettings.nTableWrapper = insert[0];
2127 oSettings.nTableReinsertBefore = oSettings.nTable.nextSibling;
2128
2129 /* Loop over the user set positioning and place the elements as needed */
2130 var aDom = oSettings.sDom.split('');
2131 var featureNode, cOption, nNewNode, cNext, sAttr, j;
2132 for ( var i=0 ; i<aDom.length ; i++ )
2133 {
2134 featureNode = null;
2135 cOption = aDom[i];
2136
2137 if ( cOption == '<' )
2138 {
2139 /* New container div */
2140 nNewNode = $('<div/>')[0];
2141
2142 /* Check to see if we should append an id and/or a class name to the container */
2143 cNext = aDom[i+1];
2144 if ( cNext == "'" || cNext == '"' )
2145 {
2146 sAttr = "";
2147 j = 2;
2148 while ( aDom[i+j] != cNext )
2149 {
2150 sAttr += aDom[i+j];
2151 j++;
2152 }
2153
2154 /* Replace jQuery UI constants @todo depreciated */
2155 if ( sAttr == "H" )
2156 {
2157 sAttr = classes.sJUIHeader;
2158 }
2159 else if ( sAttr == "F" )
2160 {
2161 sAttr = classes.sJUIFooter;
2162 }
2163
2164 /* The attribute can be in the format of "#id.class", "#id" or "class" This logic
2165 * breaks the string into parts and applies them as needed
2166 */
2167 if ( sAttr.indexOf('.') != -1 )
2168 {
2169 var aSplit = sAttr.split('.');
2170 nNewNode.id = aSplit[0].substr(1, aSplit[0].length-1);
2171 nNewNode.className = aSplit[1];
2172 }
2173 else if ( sAttr.charAt(0) == "#" )
2174 {
2175 nNewNode.id = sAttr.substr(1, sAttr.length-1);
2176 }
2177 else
2178 {
2179 nNewNode.className = sAttr;
2180 }
2181
2182 i += j; /* Move along the position array */
2183 }
2184
2185 insert.append( nNewNode );
2186 insert = $(nNewNode);
2187 }
2188 else if ( cOption == '>' )
2189 {
2190 /* End container div */
2191 insert = insert.parent();
2192 }
2193 // @todo Move options into their own plugins?
2194 else if ( cOption == 'l' && features.bPaginate && features.bLengthChange )
2195 {
2196 /* Length */
2197 featureNode = _fnFeatureHtmlLength( oSettings );
2198 }
2199 else if ( cOption == 'f' && features.bFilter )
2200 {
2201 /* Filter */
2202 featureNode = _fnFeatureHtmlFilter( oSettings );
2203 }
2204 else if ( cOption == 'r' && features.bProcessing )
2205 {
2206 /* pRocessing */
2207 featureNode = _fnFeatureHtmlProcessing( oSettings );
2208 }
2209 else if ( cOption == 't' )
2210 {
2211 /* Table */
2212 featureNode = _fnFeatureHtmlTable( oSettings );
2213 }
2214 else if ( cOption == 'i' && features.bInfo )
2215 {
2216 /* Info */
2217 featureNode = _fnFeatureHtmlInfo( oSettings );
2218 }
2219 else if ( cOption == 'p' && features.bPaginate )
2220 {
2221 /* Pagination */
2222 featureNode = _fnFeatureHtmlPaginate( oSettings );
2223 }
2224 else if ( DataTable.ext.feature.length !== 0 )
2225 {
2226 /* Plug-in features */
2227 var aoFeatures = DataTable.ext.feature;
2228 for ( var k=0, kLen=aoFeatures.length ; k<kLen ; k++ )
2229 {
2230 if ( cOption == aoFeatures[k].cFeature )
2231 {
2232 featureNode = aoFeatures[k].fnInit( oSettings );
2233 break;
2234 }
2235 }
2236 }
2237
2238 /* Add to the 2D features array */
2239 if ( featureNode )
2240 {
2241 var aanFeatures = oSettings.aanFeatures;
2242
2243 if ( ! aanFeatures[cOption] )
2244 {
2245 aanFeatures[cOption] = [];
2246 }
2247
2248 aanFeatures[cOption].push( featureNode );
2249 insert.append( featureNode );
2250 }
2251 }
2252
2253 /* Built our DOM structure - replace the holding div with what we want */
2254 holding.replaceWith( insert );
2255 }
2256
2257
2258 /**
2259 * Use the DOM source to create up an array of header cells. The idea here is to
2260 * create a layout grid (array) of rows x columns, which contains a reference
2261 * to the cell that that point in the grid (regardless of col/rowspan), such that
2262 * any column / row could be removed and the new grid constructed
2263 * @param array {object} aLayout Array to store the calculated layout in
2264 * @param {node} nThead The header/footer element for the table
2265 * @memberof DataTable#oApi
2266 */
2267 function _fnDetectHeader ( aLayout, nThead )
2268 {
2269 var nTrs = $(nThead).children('tr');
2270 var nTr, nCell;
2271 var i, k, l, iLen, jLen, iColShifted, iColumn, iColspan, iRowspan;
2272 var bUnique;
2273 var fnShiftCol = function ( a, i, j ) {
2274 var k = a[i];
2275 while ( k[j] ) {
2276 j++;
2277 }
2278 return j;
2279 };
2280
2281 aLayout.splice( 0, aLayout.length );
2282
2283 /* We know how many rows there are in the layout - so prep it */
2284 for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
2285 {
2286 aLayout.push( [] );
2287 }
2288
2289 /* Calculate a layout array */
2290 for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
2291 {
2292 nTr = nTrs[i];
2293 iColumn = 0;
2294
2295 /* For every cell in the row... */
2296 nCell = nTr.firstChild;
2297 while ( nCell ) {
2298 if ( nCell.nodeName.toUpperCase() == "TD" ||
2299 nCell.nodeName.toUpperCase() == "TH" )
2300 {
2301 /* Get the col and rowspan attributes from the DOM and sanitise them */
2302 iColspan = nCell.getAttribute('colspan') * 1;
2303 iRowspan = nCell.getAttribute('rowspan') * 1;
2304 iColspan = (!iColspan || iColspan===0 || iColspan===1) ? 1 : iColspan;
2305 iRowspan = (!iRowspan || iRowspan===0 || iRowspan===1) ? 1 : iRowspan;
2306
2307 /* There might be colspan cells already in this row, so shift our target
2308 * accordingly
2309 */
2310 iColShifted = fnShiftCol( aLayout, i, iColumn );
2311
2312 /* Cache calculation for unique columns */
2313 bUnique = iColspan === 1 ? true : false;
2314
2315 /* If there is col / rowspan, copy the information into the layout grid */
2316 for ( l=0 ; l<iColspan ; l++ )
2317 {
2318 for ( k=0 ; k<iRowspan ; k++ )
2319 {
2320 aLayout[i+k][iColShifted+l] = {
2321 "cell": nCell,
2322 "unique": bUnique
2323 };
2324 aLayout[i+k].nTr = nTr;
2325 }
2326 }
2327 }
2328 nCell = nCell.nextSibling;
2329 }
2330 }
2331 }
2332
2333
2334 /**
2335 * Get an array of unique th elements, one for each column
2336 * @param {object} oSettings dataTables settings object
2337 * @param {node} nHeader automatically detect the layout from this node - optional
2338 * @param {array} aLayout thead/tfoot layout from _fnDetectHeader - optional
2339 * @returns array {node} aReturn list of unique th's
2340 * @memberof DataTable#oApi
2341 */
2342 function _fnGetUniqueThs ( oSettings, nHeader, aLayout )
2343 {
2344 var aReturn = [];
2345 if ( !aLayout )
2346 {
2347 aLayout = oSettings.aoHeader;
2348 if ( nHeader )
2349 {
2350 aLayout = [];
2351 _fnDetectHeader( aLayout, nHeader );
2352 }
2353 }
2354
2355 for ( var i=0, iLen=aLayout.length ; i<iLen ; i++ )
2356 {
2357 for ( var j=0, jLen=aLayout[i].length ; j<jLen ; j++ )
2358 {
2359 if ( aLayout[i][j].unique &&
2360 (!aReturn[j] || !oSettings.bSortCellsTop) )
2361 {
2362 aReturn[j] = aLayout[i][j].cell;
2363 }
2364 }
2365 }
2366
2367 return aReturn;
2368 }
2369
2370
2371
2372 /**
2373 * Create an Ajax call based on the table's settings, taking into account that
2374 * parameters can have multiple forms, and backwards compatibility.
2375 *
2376 * @param {object} oSettings dataTables settings object
2377 * @param {array} data Data to send to the server, required by
2378 * DataTables - may be augmented by developer callbacks
2379 * @param {function} fn Callback function to run when data is obtained
2380 */
2381 function _fnBuildAjax( oSettings, data, fn )
2382 {
2383 // Compatibility with 1.9-, allow fnServerData and event to manipulate
2384 _fnCallbackFire( oSettings, 'aoServerParams', 'serverParams', [data] );
2385
2386 // Convert to object based for 1.10+ if using the old array scheme which can
2387 // come from server-side processing or serverParams
2388 if ( data && $.isArray(data) ) {
2389 var tmp = {};
2390 var rbracket = /(.*?)\[\]$/;
2391
2392 $.each( data, function (key, val) {
2393 var match = val.name.match(rbracket);
2394
2395 if ( match ) {
2396 // Support for arrays
2397 var name = match[0];
2398
2399 if ( ! tmp[ name ] ) {
2400 tmp[ name ] = [];
2401 }
2402 tmp[ name ].push( val.value );
2403 }
2404 else {
2405 tmp[val.name] = val.value;
2406 }
2407 } );
2408 data = tmp;
2409 }
2410
2411 var ajaxData;
2412 var ajax = oSettings.ajax;
2413 var instance = oSettings.oInstance;
2414
2415 if ( $.isPlainObject( ajax ) && ajax.data )
2416 {
2417 ajaxData = ajax.data;
2418
2419 var newData = $.isFunction( ajaxData ) ?
2420 ajaxData( data ) : // fn can manipulate data or return an object
2421 ajaxData; // object or array to merge
2422
2423 // If the function returned an object, use that alone
2424 data = $.isFunction( ajaxData ) && newData ?
2425 newData :
2426 $.extend( true, data, newData );
2427
2428 // Remove the data property as we've resolved it already and don't want
2429 // jQuery to do it again (it is restored at the end of the function)
2430 delete ajax.data;
2431 }
2432
2433 var baseAjax = {
2434 "data": data,
2435 "success": function (json) {
2436 var error = json.error || json.sError;
2437 if ( error ) {
2438 oSettings.oApi._fnLog( oSettings, 0, error );
2439 }
2440
2441 oSettings.json = json;
2442 _fnCallbackFire( oSettings, null, 'xhr', [oSettings, json] );
2443 fn( json );
2444 },
2445 "dataType": "json",
2446 "cache": false,
2447 "type": oSettings.sServerMethod,
2448 "error": function (xhr, error, thrown) {
2449 var log = oSettings.oApi._fnLog;
2450
2451 if ( error == "parsererror" ) {
2452 log( oSettings, 0, 'Invalid JSON response', 1 );
2453 }
2454 else if ( xhr.readyState === 4 ) {
2455 log( oSettings, 0, 'Ajax error', 7 );
2456 }
2457
2458 _fnProcessingDisplay( oSettings, false );
2459 }
2460 };
2461
2462 // Store the data submitted for the API
2463 oSettings.oAjaxData = data;
2464
2465 // Allow plug-ins and external processes to modify the data
2466 _fnCallbackFire( oSettings, null, 'preXhr', [oSettings, data] );
2467
2468 if ( oSettings.fnServerData )
2469 {
2470 // DataTables 1.9- compatibility
2471 oSettings.fnServerData.call( instance,
2472 oSettings.sAjaxSource,
2473 $.map( data, function (val, key) { // Need to convert back to 1.9 trad format
2474 return { name: key, value: val };
2475 } ),
2476 fn,
2477 oSettings
2478 );
2479 }
2480 else if ( oSettings.sAjaxSource || typeof ajax === 'string' )
2481 {
2482 // DataTables 1.9- compatibility
2483 oSettings.jqXHR = $.ajax( $.extend( baseAjax, {
2484 url: ajax || oSettings.sAjaxSource
2485 } ) );
2486 }
2487 else if ( $.isFunction( ajax ) )
2488 {
2489 // Is a function - let the caller define what needs to be done
2490 oSettings.jqXHR = ajax.call( instance, data, fn, oSettings );
2491 }
2492 else
2493 {
2494 // Object to extend the base settings
2495 oSettings.jqXHR = $.ajax( $.extend( baseAjax, ajax ) );
2496
2497 // Restore for next time around
2498 ajax.data = ajaxData;
2499 }
2500 }
2501
2502
2503 /**
2504 * Update the table using an Ajax call
2505 * @param {object} settings dataTables settings object
2506 * @returns {boolean} Block the table drawing or not
2507 * @memberof DataTable#oApi
2508 */
2509 function _fnAjaxUpdate( settings )
2510 {
2511 if ( settings.bAjaxDataGet ) {
2512 settings.iDraw++;
2513 _fnProcessingDisplay( settings, true );
2514
2515 _fnBuildAjax(
2516 settings,
2517 _fnAjaxParameters( settings ),
2518 function(json) {
2519 _fnAjaxUpdateDraw( settings, json );
2520 }
2521 );
2522
2523 return false;
2524 }
2525 return true;
2526 }
2527
2528
2529 /**
2530 * Build up the parameters in an object needed for a server-side processing
2531 * request. Note that this is basically done twice, is different ways - a modern
2532 * method which is used by default in DataTables 1.10 which uses objects and
2533 * arrays, or the 1.9- method with is name / value pairs. 1.9 method is used if
2534 * the sAjaxSource option is used in the initialisation, or the legacyAjax
2535 * option is set.
2536 * @param {object} oSettings dataTables settings object
2537 * @returns {bool} block the table drawing or not
2538 * @memberof DataTable#oApi
2539 */
2540 function _fnAjaxParameters( settings )
2541 {
2542 var
2543 columns = settings.aoColumns,
2544 columnCount = columns.length,
2545 features = settings.oFeatures,
2546 preSearch = settings.oPreviousSearch,
2547 preColSearch = settings.aoPreSearchCols,
2548 i, data = [], dataProp, column, columnSearch,
2549 sort = _fnSortFlatten( settings ),
2550 displayStart = settings._iDisplayStart,
2551 displayLength = features.bPaginate !== false ?
2552 settings._iDisplayLength :
2553 -1;
2554
2555 var param = function ( name, value ) {
2556 data.push( { 'name': name, 'value': value } );
2557 };
2558
2559 // DataTables 1.9- compatible method
2560 param( 'sEcho', settings.iDraw );
2561 param( 'iColumns', columnCount );
2562 param( 'sColumns', _pluck( columns, 'sName' ).join(',') );
2563 param( 'iDisplayStart', displayStart );
2564 param( 'iDisplayLength', displayLength );
2565
2566 // DataTables 1.10+ method
2567 var d = {
2568 draw: settings.iDraw,
2569 columns: [],
2570 order: [],
2571 start: displayStart,
2572 length: displayLength,
2573 search: {
2574 value: preSearch.sSearch,
2575 regex: preSearch.bRegex
2576 }
2577 };
2578
2579 for ( i=0 ; i<columnCount ; i++ ) {
2580 column = columns[i];
2581 columnSearch = preColSearch[i];
2582 dataProp = typeof column.mData=="function" ? 'function' : column.mData ;
2583
2584 d.columns.push( {
2585 data: dataProp,
2586 name: column.sName,
2587 searchable: column.bSearchable,
2588 orderable: column.bSortable,
2589 search: {
2590 value: columnSearch.sSearch,
2591 regex: columnSearch.bRegex
2592 }
2593 } );
2594
2595 param( "mDataProp_"+i, dataProp );
2596
2597 if ( features.bFilter ) {
2598 param( 'sSearch_'+i, columnSearch.sSearch );
2599 param( 'bRegex_'+i, columnSearch.bRegex );
2600 param( 'bSearchable_'+i, column.bSearchable );
2601 }
2602
2603 if ( features.bSort ) {
2604 param( 'bSortable_'+i, column.bSortable );
2605 }
2606 }
2607
2608 if ( features.bFilter ) {
2609 param( 'sSearch', preSearch.sSearch );
2610 param( 'bRegex', preSearch.bRegex );
2611 }
2612
2613 if ( features.bSort ) {
2614 $.each( sort, function ( i, val ) {
2615 d.order.push( { column: val.col, dir: val.dir } );
2616
2617 param( 'iSortCol_'+i, val.col );
2618 param( 'sSortDir_'+i, val.dir );
2619 } );
2620
2621 param( 'iSortingCols', sort.length );
2622 }
2623
2624 // If the legacy.ajax parameter is null, then we automatically decide which
2625 // form to use, based on sAjaxSource
2626 var legacy = DataTable.ext.legacy.ajax;
2627 if ( legacy === null ) {
2628 return settings.sAjaxSource ? data : d;
2629 }
2630
2631 // Otherwise, if legacy has been specified then we use that to decide on the
2632 // form
2633 return legacy ? data : d;
2634 }
2635
2636
2637 /**
2638 * Data the data from the server (nuking the old) and redraw the table
2639 * @param {object} oSettings dataTables settings object
2640 * @param {object} json json data return from the server.
2641 * @param {string} json.sEcho Tracking flag for DataTables to match requests
2642 * @param {int} json.iTotalRecords Number of records in the data set, not accounting for filtering
2643 * @param {int} json.iTotalDisplayRecords Number of records in the data set, accounting for filtering
2644 * @param {array} json.aaData The data to display on this page
2645 * @param {string} [json.sColumns] Column ordering (sName, comma separated)
2646 * @memberof DataTable#oApi
2647 */
2648 function _fnAjaxUpdateDraw ( settings, json )
2649 {
2650 // v1.10 uses camelCase variables, while 1.9 uses Hungarian notation.
2651 // Support both
2652 var compat = function ( old, modern ) {
2653 return json[old] !== undefined ? json[old] : json[modern];
2654 };
2655
2656 var draw = compat( 'sEcho', 'draw' );
2657 var recordsTotal = compat( 'iTotalRecords', 'recordsTotal' );
2658 var recordsFiltered = compat( 'iTotalDisplayRecords', 'recordsFiltered' );
2659
2660 if ( draw ) {
2661 // Protect against out of sequence returns
2662 if ( draw*1 < settings.iDraw ) {
2663 return;
2664 }
2665 settings.iDraw = draw * 1;
2666 }
2667
2668 _fnClearTable( settings );
2669 settings._iRecordsTotal = parseInt(recordsTotal, 10);
2670 settings._iRecordsDisplay = parseInt(recordsFiltered, 10);
2671
2672 var data = _fnAjaxDataSrc( settings, json );
2673 for ( var i=0, ien=data.length ; i<ien ; i++ ) {
2674 _fnAddData( settings, data[i] );
2675 }
2676 settings.aiDisplay = settings.aiDisplayMaster.slice();
2677
2678 settings.bAjaxDataGet = false;
2679 _fnDraw( settings );
2680
2681 if ( ! settings._bInitComplete ) {
2682 _fnInitComplete( settings, json );
2683 }
2684
2685 settings.bAjaxDataGet = true;
2686 _fnProcessingDisplay( settings, false );
2687 }
2688
2689
2690 /**
2691 * Get the data from the JSON data source to use for drawing a table. Using
2692 * `_fnGetObjectDataFn` allows the data to be sourced from a property of the
2693 * source object, or from a processing function.
2694 * @param {object} oSettings dataTables settings object
2695 * @param {object} json Data source object / array from the server
2696 * @return {array} Array of data to use
2697 */
2698 function _fnAjaxDataSrc ( oSettings, json )
2699 {
2700 var dataSrc = $.isPlainObject( oSettings.ajax ) && oSettings.ajax.dataSrc !== undefined ?
2701 oSettings.ajax.dataSrc :
2702 oSettings.sAjaxDataProp; // Compatibility with 1.9-.
2703
2704 // Compatibility with 1.9-. In order to read from aaData, check if the
2705 // default has been changed, if not, check for aaData
2706 if ( dataSrc === 'data' ) {
2707 return json.aaData || json[dataSrc];
2708 }
2709
2710 return dataSrc !== "" ?
2711 _fnGetObjectDataFn( dataSrc )( json ) :
2712 json;
2713 }
2714
2715
2716 /**
2717 * Generate the node required for filtering text
2718 * @returns {node} Filter control element
2719 * @param {object} oSettings dataTables settings object
2720 * @memberof DataTable#oApi
2721 */
2722 function _fnFeatureHtmlFilter ( settings )
2723 {
2724 var classes = settings.oClasses;
2725 var tableId = settings.sTableId;
2726 var language = settings.oLanguage;
2727 var previousSearch = settings.oPreviousSearch;
2728 var features = settings.aanFeatures;
2729 var input = '<input type="search" class="'+classes.sFilterInput+'"/>';
2730
2731 var str = language.sSearch;
2732 str = str.match(/_INPUT_/) ?
2733 str.replace('_INPUT_', input) :
2734 str+input;
2735
2736 var filter = $('<div/>', {
2737 'id': ! features.f ? tableId+'_filter' : null,
2738 'class': classes.sFilter
2739 } )
2740 .append( $('<label/>' ).append( str ) );
2741
2742 var searchFn = function() {
2743 /* Update all other filter input elements for the new display */
2744 var n = features.f;
2745 var val = !this.value ? "" : this.value; // mental IE8 fix :-(
2746
2747 /* Now do the filter */
2748 if ( val != previousSearch.sSearch ) {
2749 _fnFilterComplete( settings, {
2750 "sSearch": val,
2751 "bRegex": previousSearch.bRegex,
2752 "bSmart": previousSearch.bSmart ,
2753 "bCaseInsensitive": previousSearch.bCaseInsensitive
2754 } );
2755
2756 // Need to redraw, without resorting
2757 settings._iDisplayStart = 0;
2758 _fnDraw( settings );
2759 }
2760 };
2761
2762 var searchDelay = settings.searchDelay !== null ?
2763 settings.searchDelay :
2764 _fnDataSource( settings ) === 'ssp' ?
2765 400 :
2766 0;
2767
2768 var jqFilter = $('input', filter)
2769 .val( previousSearch.sSearch )
2770 .attr( 'placeholder', language.sSearchPlaceholder )
2771 .bind(
2772 'keyup.DT search.DT input.DT paste.DT cut.DT',
2773 searchDelay ?
2774 _fnThrottle( searchFn, searchDelay ) :
2775 searchFn
2776 )
2777 .bind( 'keypress.DT', function(e) {
2778 /* Prevent form submission */
2779 if ( e.keyCode == 13 ) {
2780 return false;
2781 }
2782 } )
2783 .attr('aria-controls', tableId);
2784
2785 // Update the input elements whenever the table is filtered
2786 $(settings.nTable).on( 'search.dt.DT', function ( ev, s ) {
2787 if ( settings === s ) {
2788 // IE9 throws an 'unknown error' if document.activeElement is used
2789 // inside an iframe or frame...
2790 try {
2791 if ( jqFilter[0] !== document.activeElement ) {
2792 jqFilter.val( previousSearch.sSearch );
2793 }
2794 }
2795 catch ( e ) {}
2796 }
2797 } );
2798
2799 return filter[0];
2800 }
2801
2802
2803 /**
2804 * Filter the table using both the global filter and column based filtering
2805 * @param {object} oSettings dataTables settings object
2806 * @param {object} oSearch search information
2807 * @param {int} [iForce] force a research of the master array (1) or not (undefined or 0)
2808 * @memberof DataTable#oApi
2809 */
2810 function _fnFilterComplete ( oSettings, oInput, iForce )
2811 {
2812 var oPrevSearch = oSettings.oPreviousSearch;
2813 var aoPrevSearch = oSettings.aoPreSearchCols;
2814 var fnSaveFilter = function ( oFilter ) {
2815 /* Save the filtering values */
2816 oPrevSearch.sSearch = oFilter.sSearch;
2817 oPrevSearch.bRegex = oFilter.bRegex;
2818 oPrevSearch.bSmart = oFilter.bSmart;
2819 oPrevSearch.bCaseInsensitive = oFilter.bCaseInsensitive;
2820 };
2821 var fnRegex = function ( o ) {
2822 // Backwards compatibility with the bEscapeRegex option
2823 return o.bEscapeRegex !== undefined ? !o.bEscapeRegex : o.bRegex;
2824 };
2825
2826 // Resolve any column types that are unknown due to addition or invalidation
2827 // @todo As per sort - can this be moved into an event handler?
2828 _fnColumnTypes( oSettings );
2829
2830 /* In server-side processing all filtering is done by the server, so no point hanging around here */
2831 if ( _fnDataSource( oSettings ) != 'ssp' )
2832 {
2833 /* Global filter */
2834 _fnFilter( oSettings, oInput.sSearch, iForce, fnRegex(oInput), oInput.bSmart, oInput.bCaseInsensitive );
2835 fnSaveFilter( oInput );
2836
2837 /* Now do the individual column filter */
2838 for ( var i=0 ; i<aoPrevSearch.length ; i++ )
2839 {
2840 _fnFilterColumn( oSettings, aoPrevSearch[i].sSearch, i, fnRegex(aoPrevSearch[i]),
2841 aoPrevSearch[i].bSmart, aoPrevSearch[i].bCaseInsensitive );
2842 }
2843
2844 /* Custom filtering */
2845 _fnFilterCustom( oSettings );
2846 }
2847 else
2848 {
2849 fnSaveFilter( oInput );
2850 }
2851
2852 /* Tell the draw function we have been filtering */
2853 oSettings.bFiltered = true;
2854 _fnCallbackFire( oSettings, null, 'search', [oSettings] );
2855 }
2856
2857
2858 /**
2859 * Apply custom filtering functions
2860 * @param {object} oSettings dataTables settings object
2861 * @memberof DataTable#oApi
2862 */
2863 function _fnFilterCustom( settings )
2864 {
2865 var filters = DataTable.ext.search;
2866 var displayRows = settings.aiDisplay;
2867 var row, rowIdx;
2868
2869 for ( var i=0, ien=filters.length ; i<ien ; i++ ) {
2870 var rows = [];
2871
2872 // Loop over each row and see if it should be included
2873 for ( var j=0, jen=displayRows.length ; j<jen ; j++ ) {
2874 rowIdx = displayRows[ j ];
2875 row = settings.aoData[ rowIdx ];
2876
2877 if ( filters[i]( settings, row._aFilterData, rowIdx, row._aData, j ) ) {
2878 rows.push( rowIdx );
2879 }
2880 }
2881
2882 // So the array reference doesn't break set the results into the
2883 // existing array
2884 displayRows.length = 0;
2885 displayRows.push.apply( displayRows, rows );
2886 }
2887 }
2888
2889
2890 /**
2891 * Filter the table on a per-column basis
2892 * @param {object} oSettings dataTables settings object
2893 * @param {string} sInput string to filter on
2894 * @param {int} iColumn column to filter
2895 * @param {bool} bRegex treat search string as a regular expression or not
2896 * @param {bool} bSmart use smart filtering or not
2897 * @param {bool} bCaseInsensitive Do case insenstive matching or not
2898 * @memberof DataTable#oApi
2899 */
2900 function _fnFilterColumn ( settings, searchStr, colIdx, regex, smart, caseInsensitive )
2901 {
2902 if ( searchStr === '' ) {
2903 return;
2904 }
2905
2906 var data;
2907 var display = settings.aiDisplay;
2908 var rpSearch = _fnFilterCreateSearch( searchStr, regex, smart, caseInsensitive );
2909
2910 for ( var i=display.length-1 ; i>=0 ; i-- ) {
2911 data = settings.aoData[ display[i] ]._aFilterData[ colIdx ];
2912
2913 if ( ! rpSearch.test( data ) ) {
2914 display.splice( i, 1 );
2915 }
2916 }
2917 }
2918
2919
2920 /**
2921 * Filter the data table based on user input and draw the table
2922 * @param {object} settings dataTables settings object
2923 * @param {string} input string to filter on
2924 * @param {int} force optional - force a research of the master array (1) or not (undefined or 0)
2925 * @param {bool} regex treat as a regular expression or not
2926 * @param {bool} smart perform smart filtering or not
2927 * @param {bool} caseInsensitive Do case insenstive matching or not
2928 * @memberof DataTable#oApi
2929 */
2930 function _fnFilter( settings, input, force, regex, smart, caseInsensitive )
2931 {
2932 var rpSearch = _fnFilterCreateSearch( input, regex, smart, caseInsensitive );
2933 var prevSearch = settings.oPreviousSearch.sSearch;
2934 var displayMaster = settings.aiDisplayMaster;
2935 var display, invalidated, i;
2936
2937 // Need to take account of custom filtering functions - always filter
2938 if ( DataTable.ext.search.length !== 0 ) {
2939 force = true;
2940 }
2941
2942 // Check if any of the rows were invalidated
2943 invalidated = _fnFilterData( settings );
2944
2945 // If the input is blank - we just want the full data set
2946 if ( input.length <= 0 ) {
2947 settings.aiDisplay = displayMaster.slice();
2948 }
2949 else {
2950 // New search - start from the master array
2951 if ( invalidated ||
2952 force ||
2953 prevSearch.length > input.length ||
2954 input.indexOf(prevSearch) !== 0 ||
2955 settings.bSorted // On resort, the display master needs to be
2956 // re-filtered since indexes will have changed
2957 ) {
2958 settings.aiDisplay = displayMaster.slice();
2959 }
2960
2961 // Search the display array
2962 display = settings.aiDisplay;
2963
2964 for ( i=display.length-1 ; i>=0 ; i-- ) {
2965 if ( ! rpSearch.test( settings.aoData[ display[i] ]._sFilterRow ) ) {
2966 display.splice( i, 1 );
2967 }
2968 }
2969 }
2970 }
2971
2972
2973 /**
2974 * Build a regular expression object suitable for searching a table
2975 * @param {string} sSearch string to search for
2976 * @param {bool} bRegex treat as a regular expression or not
2977 * @param {bool} bSmart perform smart filtering or not
2978 * @param {bool} bCaseInsensitive Do case insensitive matching or not
2979 * @returns {RegExp} constructed object
2980 * @memberof DataTable#oApi
2981 */
2982 function _fnFilterCreateSearch( search, regex, smart, caseInsensitive )
2983 {
2984 search = regex ?
2985 search :
2986 _fnEscapeRegex( search );
2987
2988 if ( smart ) {
2989 /* For smart filtering we want to allow the search to work regardless of
2990 * word order. We also want double quoted text to be preserved, so word
2991 * order is important - a la google. So this is what we want to
2992 * generate:
2993 *
2994 * ^(?=.*?\bone\b)(?=.*?\btwo three\b)(?=.*?\bfour\b).*$
2995 */
2996 var a = $.map( search.match( /"[^"]+"|[^ ]+/g ) || '', function ( word ) {
2997 if ( word.charAt(0) === '"' ) {
2998 var m = word.match( /^"(.*)"$/ );
2999 word = m ? m[1] : word;
3000 }
3001
3002 return word.replace('"', '');
3003 } );
3004
3005 search = '^(?=.*?'+a.join( ')(?=.*?' )+').*$';
3006 }
3007
3008 return new RegExp( search, caseInsensitive ? 'i' : '' );
3009 }
3010
3011
3012 /**
3013 * Escape a string such that it can be used in a regular expression
3014 * @param {string} sVal string to escape
3015 * @returns {string} escaped string
3016 * @memberof DataTable#oApi
3017 */
3018 function _fnEscapeRegex ( sVal )
3019 {
3020 return sVal.replace( _re_escape_regex, '\\$1' );
3021 }
3022
3023
3024
3025 var __filter_div = $('<div>')[0];
3026 var __filter_div_textContent = __filter_div.textContent !== undefined;
3027
3028 // Update the filtering data for each row if needed (by invalidation or first run)
3029 function _fnFilterData ( settings )
3030 {
3031 var columns = settings.aoColumns;
3032 var column;
3033 var i, j, ien, jen, filterData, cellData, row;
3034 var fomatters = DataTable.ext.type.search;
3035 var wasInvalidated = false;
3036
3037 for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
3038 row = settings.aoData[i];
3039
3040 if ( ! row._aFilterData ) {
3041 filterData = [];
3042
3043 for ( j=0, jen=columns.length ; j<jen ; j++ ) {
3044 column = columns[j];
3045
3046 if ( column.bSearchable ) {
3047 cellData = _fnGetCellData( settings, i, j, 'filter' );
3048
3049 if ( fomatters[ column.sType ] ) {
3050 cellData = fomatters[ column.sType ]( cellData );
3051 }
3052
3053 // Search in DataTables 1.10 is string based. In 1.11 this
3054 // should be altered to also allow strict type checking.
3055 if ( cellData === null ) {
3056 cellData = '';
3057 }
3058
3059 if ( typeof cellData !== 'string' && cellData.toString ) {
3060 cellData = cellData.toString();
3061 }
3062 }
3063 else {
3064 cellData = '';
3065 }
3066
3067 // If it looks like there is an HTML entity in the string,
3068 // attempt to decode it so sorting works as expected. Note that
3069 // we could use a single line of jQuery to do this, but the DOM
3070 // method used here is much faster http://jsperf.com/html-decode
3071 if ( cellData.indexOf && cellData.indexOf('&') !== -1 ) {
3072 __filter_div.innerHTML = cellData;
3073 cellData = __filter_div_textContent ?
3074 __filter_div.textContent :
3075 __filter_div.innerText;
3076 }
3077
3078 if ( cellData.replace ) {
3079 cellData = cellData.replace(/[\r\n]/g, '');
3080 }
3081
3082 filterData.push( cellData );
3083 }
3084
3085 row._aFilterData = filterData;
3086 row._sFilterRow = filterData.join(' ');
3087 wasInvalidated = true;
3088 }
3089 }
3090
3091 return wasInvalidated;
3092 }
3093
3094
3095 /**
3096 * Convert from the internal Hungarian notation to camelCase for external
3097 * interaction
3098 * @param {object} obj Object to convert
3099 * @returns {object} Inverted object
3100 * @memberof DataTable#oApi
3101 */
3102 function _fnSearchToCamel ( obj )
3103 {
3104 return {
3105 search: obj.sSearch,
3106 smart: obj.bSmart,
3107 regex: obj.bRegex,
3108 caseInsensitive: obj.bCaseInsensitive
3109 };
3110 }
3111
3112
3113
3114 /**
3115 * Convert from camelCase notation to the internal Hungarian. We could use the
3116 * Hungarian convert function here, but this is cleaner
3117 * @param {object} obj Object to convert
3118 * @returns {object} Inverted object
3119 * @memberof DataTable#oApi
3120 */
3121 function _fnSearchToHung ( obj )
3122 {
3123 return {
3124 sSearch: obj.search,
3125 bSmart: obj.smart,
3126 bRegex: obj.regex,
3127 bCaseInsensitive: obj.caseInsensitive
3128 };
3129 }
3130
3131 /**
3132 * Generate the node required for the info display
3133 * @param {object} oSettings dataTables settings object
3134 * @returns {node} Information element
3135 * @memberof DataTable#oApi
3136 */
3137 function _fnFeatureHtmlInfo ( settings )
3138 {
3139 var
3140 tid = settings.sTableId,
3141 nodes = settings.aanFeatures.i,
3142 n = $('<div/>', {
3143 'class': settings.oClasses.sInfo,
3144 'id': ! nodes ? tid+'_info' : null
3145 } );
3146
3147 if ( ! nodes ) {
3148 // Update display on each draw
3149 settings.aoDrawCallback.push( {
3150 "fn": _fnUpdateInfo,
3151 "sName": "information"
3152 } );
3153
3154 n
3155 .attr( 'role', 'status' )
3156 .attr( 'aria-live', 'polite' );
3157
3158 // Table is described by our info div
3159 $(settings.nTable).attr( 'aria-describedby', tid+'_info' );
3160 }
3161
3162 return n[0];
3163 }
3164
3165
3166 /**
3167 * Update the information elements in the display
3168 * @param {object} settings dataTables settings object
3169 * @memberof DataTable#oApi
3170 */
3171 function _fnUpdateInfo ( settings )
3172 {
3173 /* Show information about the table */
3174 var nodes = settings.aanFeatures.i;
3175 if ( nodes.length === 0 ) {
3176 return;
3177 }
3178
3179 var
3180 lang = settings.oLanguage,
3181 start = settings._iDisplayStart+1,
3182 end = settings.fnDisplayEnd(),
3183 max = settings.fnRecordsTotal(),
3184 total = settings.fnRecordsDisplay(),
3185 out = total ?
3186 lang.sInfo :
3187 lang.sInfoEmpty;
3188
3189 if ( total !== max ) {
3190 /* Record set after filtering */
3191 out += ' ' + lang.sInfoFiltered;
3192 }
3193
3194 // Convert the macros
3195 out += lang.sInfoPostFix;
3196 out = _fnInfoMacros( settings, out );
3197
3198 var callback = lang.fnInfoCallback;
3199 if ( callback !== null ) {
3200 out = callback.call( settings.oInstance,
3201 settings, start, end, max, total, out
3202 );
3203 }
3204
3205 $(nodes).html( out );
3206 }
3207
3208
3209 function _fnInfoMacros ( settings, str )
3210 {
3211 // When infinite scrolling, we are always starting at 1. _iDisplayStart is used only
3212 // internally
3213 var
3214 formatter = settings.fnFormatNumber,
3215 start = settings._iDisplayStart+1,
3216 len = settings._iDisplayLength,
3217 vis = settings.fnRecordsDisplay(),
3218 all = len === -1;
3219
3220 return str.
3221 replace(/_START_/g, formatter.call( settings, start ) ).
3222 replace(/_END_/g, formatter.call( settings, settings.fnDisplayEnd() ) ).
3223 replace(/_MAX_/g, formatter.call( settings, settings.fnRecordsTotal() ) ).
3224 replace(/_TOTAL_/g, formatter.call( settings, vis ) ).
3225 replace(/_PAGE_/g, formatter.call( settings, all ? 1 : Math.ceil( start / len ) ) ).
3226 replace(/_PAGES_/g, formatter.call( settings, all ? 1 : Math.ceil( vis / len ) ) );
3227 }
3228
3229
3230
3231 /**
3232 * Draw the table for the first time, adding all required features
3233 * @param {object} settings dataTables settings object
3234 * @memberof DataTable#oApi
3235 */
3236 function _fnInitialise ( settings )
3237 {
3238 var i, iLen, iAjaxStart=settings.iInitDisplayStart;
3239 var columns = settings.aoColumns, column;
3240 var features = settings.oFeatures;
3241
3242 /* Ensure that the table data is fully initialised */
3243 if ( ! settings.bInitialised ) {
3244 setTimeout( function(){ _fnInitialise( settings ); }, 200 );
3245 return;
3246 }
3247
3248 /* Show the display HTML options */
3249 _fnAddOptionsHtml( settings );
3250
3251 /* Build and draw the header / footer for the table */
3252 _fnBuildHead( settings );
3253 _fnDrawHead( settings, settings.aoHeader );
3254 _fnDrawHead( settings, settings.aoFooter );
3255
3256 /* Okay to show that something is going on now */
3257 _fnProcessingDisplay( settings, true );
3258
3259 /* Calculate sizes for columns */
3260 if ( features.bAutoWidth ) {
3261 _fnCalculateColumnWidths( settings );
3262 }
3263
3264 for ( i=0, iLen=columns.length ; i<iLen ; i++ ) {
3265 column = columns[i];
3266
3267 if ( column.sWidth ) {
3268 column.nTh.style.width = _fnStringToCss( column.sWidth );
3269 }
3270 }
3271
3272 // If there is default sorting required - let's do it. The sort function
3273 // will do the drawing for us. Otherwise we draw the table regardless of the
3274 // Ajax source - this allows the table to look initialised for Ajax sourcing
3275 // data (show 'loading' message possibly)
3276 _fnReDraw( settings );
3277
3278 // Server-side processing init complete is done by _fnAjaxUpdateDraw
3279 var dataSrc = _fnDataSource( settings );
3280 if ( dataSrc != 'ssp' ) {
3281 // if there is an ajax source load the data
3282 if ( dataSrc == 'ajax' ) {
3283 _fnBuildAjax( settings, [], function(json) {
3284 var aData = _fnAjaxDataSrc( settings, json );
3285
3286 // Got the data - add it to the table
3287 for ( i=0 ; i<aData.length ; i++ ) {
3288 _fnAddData( settings, aData[i] );
3289 }
3290
3291 // Reset the init display for cookie saving. We've already done
3292 // a filter, and therefore cleared it before. So we need to make
3293 // it appear 'fresh'
3294 settings.iInitDisplayStart = iAjaxStart;
3295
3296 _fnReDraw( settings );
3297
3298 _fnProcessingDisplay( settings, false );
3299 _fnInitComplete( settings, json );
3300 }, settings );
3301 }
3302 else {
3303 _fnProcessingDisplay( settings, false );
3304 _fnInitComplete( settings );
3305 }
3306 }
3307 }
3308
3309
3310 /**
3311 * Draw the table for the first time, adding all required features
3312 * @param {object} oSettings dataTables settings object
3313 * @param {object} [json] JSON from the server that completed the table, if using Ajax source
3314 * with client-side processing (optional)
3315 * @memberof DataTable#oApi
3316 */
3317 function _fnInitComplete ( settings, json )
3318 {
3319 settings._bInitComplete = true;
3320
3321 // On an Ajax load we now have data and therefore want to apply the column
3322 // sizing
3323 if ( json ) {
3324 _fnAdjustColumnSizing( settings );
3325 }
3326
3327 _fnCallbackFire( settings, 'aoInitComplete', 'init', [settings, json] );
3328 }
3329
3330
3331 function _fnLengthChange ( settings, val )
3332 {
3333 var len = parseInt( val, 10 );
3334 settings._iDisplayLength = len;
3335
3336 _fnLengthOverflow( settings );
3337
3338 // Fire length change event
3339 _fnCallbackFire( settings, null, 'length', [settings, len] );
3340 }
3341
3342
3343 /**
3344 * Generate the node required for user display length changing
3345 * @param {object} settings dataTables settings object
3346 * @returns {node} Display length feature node
3347 * @memberof DataTable#oApi
3348 */
3349 function _fnFeatureHtmlLength ( settings )
3350 {
3351 var
3352 classes = settings.oClasses,
3353 tableId = settings.sTableId,
3354 menu = settings.aLengthMenu,
3355 d2 = $.isArray( menu[0] ),
3356 lengths = d2 ? menu[0] : menu,
3357 language = d2 ? menu[1] : menu;
3358
3359 var select = $('<select/>', {
3360 'name': tableId+'_length',
3361 'aria-controls': tableId,
3362 'class': classes.sLengthSelect
3363 } );
3364
3365 for ( var i=0, ien=lengths.length ; i<ien ; i++ ) {
3366 select[0][ i ] = new Option( language[i], lengths[i] );
3367 }
3368
3369 var div = $('<div><label/></div>').addClass( classes.sLength );
3370 if ( ! settings.aanFeatures.l ) {
3371 div[0].id = tableId+'_length';
3372 }
3373
3374 div.children().append(
3375 settings.oLanguage.sLengthMenu.replace( '_MENU_', select[0].outerHTML )
3376 );
3377
3378 // Can't use `select` variable as user might provide their own and the
3379 // reference is broken by the use of outerHTML
3380 $('select', div)
3381 .val( settings._iDisplayLength )
3382 .bind( 'change.DT', function(e) {
3383 _fnLengthChange( settings, $(this).val() );
3384 _fnDraw( settings );
3385 } );
3386
3387 // Update node value whenever anything changes the table's length
3388 $(settings.nTable).bind( 'length.dt.DT', function (e, s, len) {
3389 if ( settings === s ) {
3390 $('select', div).val( len );
3391 }
3392 } );
3393
3394 return div[0];
3395 }
3396
3397
3398
3399 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
3400 * Note that most of the paging logic is done in
3401 * DataTable.ext.pager
3402 */
3403
3404 /**
3405 * Generate the node required for default pagination
3406 * @param {object} oSettings dataTables settings object
3407 * @returns {node} Pagination feature node
3408 * @memberof DataTable#oApi
3409 */
3410 function _fnFeatureHtmlPaginate ( settings )
3411 {
3412 var
3413 type = settings.sPaginationType,
3414 plugin = DataTable.ext.pager[ type ],
3415 modern = typeof plugin === 'function',
3416 redraw = function( settings ) {
3417 _fnDraw( settings );
3418 },
3419 node = $('<div/>').addClass( settings.oClasses.sPaging + type )[0],
3420 features = settings.aanFeatures;
3421
3422 if ( ! modern ) {
3423 plugin.fnInit( settings, node, redraw );
3424 }
3425
3426 /* Add a draw callback for the pagination on first instance, to update the paging display */
3427 if ( ! features.p )
3428 {
3429 node.id = settings.sTableId+'_paginate';
3430
3431 settings.aoDrawCallback.push( {
3432 "fn": function( settings ) {
3433 if ( modern ) {
3434 var
3435 start = settings._iDisplayStart,
3436 len = settings._iDisplayLength,
3437 visRecords = settings.fnRecordsDisplay(),
3438 all = len === -1,
3439 page = all ? 0 : Math.ceil( start / len ),
3440 pages = all ? 1 : Math.ceil( visRecords / len ),
3441 buttons = plugin(page, pages),
3442 i, ien;
3443
3444 for ( i=0, ien=features.p.length ; i<ien ; i++ ) {
3445 _fnRenderer( settings, 'pageButton' )(
3446 settings, features.p[i], i, buttons, page, pages
3447 );
3448 }
3449 }
3450 else {
3451 plugin.fnUpdate( settings, redraw );
3452 }
3453 },
3454 "sName": "pagination"
3455 } );
3456 }
3457
3458 return node;
3459 }
3460
3461
3462 /**
3463 * Alter the display settings to change the page
3464 * @param {object} settings DataTables settings object
3465 * @param {string|int} action Paging action to take: "first", "previous",
3466 * "next" or "last" or page number to jump to (integer)
3467 * @param [bool] redraw Automatically draw the update or not
3468 * @returns {bool} true page has changed, false - no change
3469 * @memberof DataTable#oApi
3470 */
3471 function _fnPageChange ( settings, action, redraw )
3472 {
3473 var
3474 start = settings._iDisplayStart,
3475 len = settings._iDisplayLength,
3476 records = settings.fnRecordsDisplay();
3477
3478 if ( records === 0 || len === -1 )
3479 {
3480 start = 0;
3481 }
3482 else if ( typeof action === "number" )
3483 {
3484 start = action * len;
3485
3486 if ( start > records )
3487 {
3488 start = 0;
3489 }
3490 }
3491 else if ( action == "first" )
3492 {
3493 start = 0;
3494 }
3495 else if ( action == "previous" )
3496 {
3497 start = len >= 0 ?
3498 start - len :
3499 0;
3500
3501 if ( start < 0 )
3502 {
3503 start = 0;
3504 }
3505 }
3506 else if ( action == "next" )
3507 {
3508 if ( start + len < records )
3509 {
3510 start += len;
3511 }
3512 }
3513 else if ( action == "last" )
3514 {
3515 start = Math.floor( (records-1) / len) * len;
3516 }
3517 else
3518 {
3519 _fnLog( settings, 0, "Unknown paging action: "+action, 5 );
3520 }
3521
3522 var changed = settings._iDisplayStart !== start;
3523 settings._iDisplayStart = start;
3524
3525 if ( changed ) {
3526 _fnCallbackFire( settings, null, 'page', [settings] );
3527
3528 if ( redraw ) {
3529 _fnDraw( settings );
3530 }
3531 }
3532
3533 return changed;
3534 }
3535
3536
3537
3538 /**
3539 * Generate the node required for the processing node
3540 * @param {object} settings dataTables settings object
3541 * @returns {node} Processing element
3542 * @memberof DataTable#oApi
3543 */
3544 function _fnFeatureHtmlProcessing ( settings )
3545 {
3546 return $('<div/>', {
3547 'id': ! settings.aanFeatures.r ? settings.sTableId+'_processing' : null,
3548 'class': settings.oClasses.sProcessing
3549 } )
3550 .html( settings.oLanguage.sProcessing )
3551 .insertBefore( settings.nTable )[0];
3552 }
3553
3554
3555 /**
3556 * Display or hide the processing indicator
3557 * @param {object} settings dataTables settings object
3558 * @param {bool} show Show the processing indicator (true) or not (false)
3559 * @memberof DataTable#oApi
3560 */
3561 function _fnProcessingDisplay ( settings, show )
3562 {
3563 if ( settings.oFeatures.bProcessing ) {
3564 $(settings.aanFeatures.r).css( 'display', show ? 'block' : 'none' );
3565 }
3566
3567 _fnCallbackFire( settings, null, 'processing', [settings, show] );
3568 }
3569
3570 /**
3571 * Add any control elements for the table - specifically scrolling
3572 * @param {object} settings dataTables settings object
3573 * @returns {node} Node to add to the DOM
3574 * @memberof DataTable#oApi
3575 */
3576 function _fnFeatureHtmlTable ( settings )
3577 {
3578 var table = $(settings.nTable);
3579
3580 // Add the ARIA grid role to the table
3581 table.attr( 'role', 'grid' );
3582
3583 // Scrolling from here on in
3584 var scroll = settings.oScroll;
3585
3586 if ( scroll.sX === '' && scroll.sY === '' ) {
3587 return settings.nTable;
3588 }
3589
3590 var scrollX = scroll.sX;
3591 var scrollY = scroll.sY;
3592 var classes = settings.oClasses;
3593 var caption = table.children('caption');
3594 var captionSide = caption.length ? caption[0]._captionSide : null;
3595 var headerClone = $( table[0].cloneNode(false) );
3596 var footerClone = $( table[0].cloneNode(false) );
3597 var footer = table.children('tfoot');
3598 var _div = '<div/>';
3599 var size = function ( s ) {
3600 return !s ? null : _fnStringToCss( s );
3601 };
3602
3603 // This is fairly messy, but with x scrolling enabled, if the table has a
3604 // width attribute, regardless of any width applied using the column width
3605 // options, the browser will shrink or grow the table as needed to fit into
3606 // that 100%. That would make the width options useless. So we remove it.
3607 // This is okay, under the assumption that width:100% is applied to the
3608 // table in CSS (it is in the default stylesheet) which will set the table
3609 // width as appropriate (the attribute and css behave differently...)
3610 if ( scroll.sX && table.attr('width') === '100%' ) {
3611 table.removeAttr('width');
3612 }
3613
3614 if ( ! footer.length ) {
3615 footer = null;
3616 }
3617
3618 /*
3619 * The HTML structure that we want to generate in this function is:
3620 * div - scroller
3621 * div - scroll head
3622 * div - scroll head inner
3623 * table - scroll head table
3624 * thead - thead
3625 * div - scroll body
3626 * table - table (master table)
3627 * thead - thead clone for sizing
3628 * tbody - tbody
3629 * div - scroll foot
3630 * div - scroll foot inner
3631 * table - scroll foot table
3632 * tfoot - tfoot
3633 */
3634 var scroller = $( _div, { 'class': classes.sScrollWrapper } )
3635 .append(
3636 $(_div, { 'class': classes.sScrollHead } )
3637 .css( {
3638 overflow: 'hidden',
3639 position: 'relative',
3640 border: 0,
3641 width: scrollX ? size(scrollX) : '100%'
3642 } )
3643 .append(
3644 $(_div, { 'class': classes.sScrollHeadInner } )
3645 .css( {
3646 'box-sizing': 'content-box',
3647 width: scroll.sXInner || '100%'
3648 } )
3649 .append(
3650 headerClone
3651 .removeAttr('id')
3652 .css( 'margin-left', 0 )
3653 .append( captionSide === 'top' ? caption : null )
3654 .append(
3655 table.children('thead')
3656 )
3657 )
3658 )
3659 )
3660 .append(
3661 $(_div, { 'class': classes.sScrollBody } )
3662 .css( {
3663 overflow: 'auto',
3664 height: size( scrollY ),
3665 width: size( scrollX )
3666 } )
3667 .append( table )
3668 );
3669
3670 if ( footer ) {
3671 scroller.append(
3672 $(_div, { 'class': classes.sScrollFoot } )
3673 .css( {
3674 overflow: 'hidden',
3675 border: 0,
3676 width: scrollX ? size(scrollX) : '100%'
3677 } )
3678 .append(
3679 $(_div, { 'class': classes.sScrollFootInner } )
3680 .append(
3681 footerClone
3682 .removeAttr('id')
3683 .css( 'margin-left', 0 )
3684 .append( captionSide === 'bottom' ? caption : null )
3685 .append(
3686 table.children('tfoot')
3687 )
3688 )
3689 )
3690 );
3691 }
3692
3693 var children = scroller.children();
3694 var scrollHead = children[0];
3695 var scrollBody = children[1];
3696 var scrollFoot = footer ? children[2] : null;
3697
3698 // When the body is scrolled, then we also want to scroll the headers
3699 if ( scrollX ) {
3700 $(scrollBody).scroll( function (e) {
3701 var scrollLeft = this.scrollLeft;
3702
3703 scrollHead.scrollLeft = scrollLeft;
3704
3705 if ( footer ) {
3706 scrollFoot.scrollLeft = scrollLeft;
3707 }
3708 } );
3709 }
3710
3711 settings.nScrollHead = scrollHead;
3712 settings.nScrollBody = scrollBody;
3713 settings.nScrollFoot = scrollFoot;
3714
3715 // On redraw - align columns
3716 settings.aoDrawCallback.push( {
3717 "fn": _fnScrollDraw,
3718 "sName": "scrolling"
3719 } );
3720
3721 return scroller[0];
3722 }
3723
3724
3725
3726 /**
3727 * Update the header, footer and body tables for resizing - i.e. column
3728 * alignment.
3729 *
3730 * Welcome to the most horrible function DataTables. The process that this
3731 * function follows is basically:
3732 * 1. Re-create the table inside the scrolling div
3733 * 2. Take live measurements from the DOM
3734 * 3. Apply the measurements to align the columns
3735 * 4. Clean up
3736 *
3737 * @param {object} settings dataTables settings object
3738 * @memberof DataTable#oApi
3739 */
3740 function _fnScrollDraw ( settings )
3741 {
3742 // Given that this is such a monster function, a lot of variables are use
3743 // to try and keep the minimised size as small as possible
3744 var
3745 scroll = settings.oScroll,
3746 scrollX = scroll.sX,
3747 scrollXInner = scroll.sXInner,
3748 scrollY = scroll.sY,
3749 barWidth = scroll.iBarWidth,
3750 divHeader = $(settings.nScrollHead),
3751 divHeaderStyle = divHeader[0].style,
3752 divHeaderInner = divHeader.children('div'),
3753 divHeaderInnerStyle = divHeaderInner[0].style,
3754 divHeaderTable = divHeaderInner.children('table'),
3755 divBodyEl = settings.nScrollBody,
3756 divBody = $(divBodyEl),
3757 divBodyStyle = divBodyEl.style,
3758 divFooter = $(settings.nScrollFoot),
3759 divFooterInner = divFooter.children('div'),
3760 divFooterTable = divFooterInner.children('table'),
3761 header = $(settings.nTHead),
3762 table = $(settings.nTable),
3763 tableEl = table[0],
3764 tableStyle = tableEl.style,
3765 footer = settings.nTFoot ? $(settings.nTFoot) : null,
3766 browser = settings.oBrowser,
3767 ie67 = browser.bScrollOversize,
3768 headerTrgEls, footerTrgEls,
3769 headerSrcEls, footerSrcEls,
3770 headerCopy, footerCopy,
3771 headerWidths=[], footerWidths=[],
3772 headerContent=[],
3773 idx, correction, sanityWidth,
3774 zeroOut = function(nSizer) {
3775 var style = nSizer.style;
3776 style.paddingTop = "0";
3777 style.paddingBottom = "0";
3778 style.borderTopWidth = "0";
3779 style.borderBottomWidth = "0";
3780 style.height = 0;
3781 };
3782
3783 /*
3784 * 1. Re-create the table inside the scrolling div
3785 */
3786
3787 // Remove the old minimised thead and tfoot elements in the inner table
3788 table.children('thead, tfoot').remove();
3789
3790 // Clone the current header and footer elements and then place it into the inner table
3791 headerCopy = header.clone().prependTo( table );
3792 headerTrgEls = header.find('tr'); // original header is in its own table
3793 headerSrcEls = headerCopy.find('tr');
3794 headerCopy.find('th, td').removeAttr('tabindex');
3795
3796 if ( footer ) {
3797 footerCopy = footer.clone().prependTo( table );
3798 footerTrgEls = footer.find('tr'); // the original tfoot is in its own table and must be sized
3799 footerSrcEls = footerCopy.find('tr');
3800 }
3801
3802
3803 /*
3804 * 2. Take live measurements from the DOM - do not alter the DOM itself!
3805 */
3806
3807 // Remove old sizing and apply the calculated column widths
3808 // Get the unique column headers in the newly created (cloned) header. We want to apply the
3809 // calculated sizes to this header
3810 if ( ! scrollX )
3811 {
3812 divBodyStyle.width = '100%';
3813 divHeader[0].style.width = '100%';
3814 }
3815
3816 $.each( _fnGetUniqueThs( settings, headerCopy ), function ( i, el ) {
3817 idx = _fnVisibleToColumnIndex( settings, i );
3818 el.style.width = settings.aoColumns[idx].sWidth;
3819 } );
3820
3821 if ( footer ) {
3822 _fnApplyToChildren( function(n) {
3823 n.style.width = "";
3824 }, footerSrcEls );
3825 }
3826
3827 // If scroll collapse is enabled, when we put the headers back into the body for sizing, we
3828 // will end up forcing the scrollbar to appear, making our measurements wrong for when we
3829 // then hide it (end of this function), so add the header height to the body scroller.
3830 if ( scroll.bCollapse && scrollY !== "" ) {
3831 divBodyStyle.height = (divBody[0].offsetHeight + header[0].offsetHeight)+"px";
3832 }
3833
3834 // Size the table as a whole
3835 sanityWidth = table.outerWidth();
3836 if ( scrollX === "" ) {
3837 // No x scrolling
3838 tableStyle.width = "100%";
3839
3840 // IE7 will make the width of the table when 100% include the scrollbar
3841 // - which is shouldn't. When there is a scrollbar we need to take this
3842 // into account.
3843 if ( ie67 && (table.find('tbody').height() > divBodyEl.offsetHeight ||
3844 divBody.css('overflow-y') == "scroll")
3845 ) {
3846 tableStyle.width = _fnStringToCss( table.outerWidth() - barWidth);
3847 }
3848 }
3849 else
3850 {
3851 // x scrolling
3852 if ( scrollXInner !== "" ) {
3853 // x scroll inner has been given - use it
3854 tableStyle.width = _fnStringToCss(scrollXInner);
3855 }
3856 else if ( sanityWidth == divBody.width() && divBody.height() < table.height() ) {
3857 // There is y-scrolling - try to take account of the y scroll bar
3858 tableStyle.width = _fnStringToCss( sanityWidth-barWidth );
3859 if ( table.outerWidth() > sanityWidth-barWidth ) {
3860 // Not possible to take account of it
3861 tableStyle.width = _fnStringToCss( sanityWidth );
3862 }
3863 }
3864 else {
3865 // When all else fails
3866 tableStyle.width = _fnStringToCss( sanityWidth );
3867 }
3868 }
3869
3870 // Recalculate the sanity width - now that we've applied the required width,
3871 // before it was a temporary variable. This is required because the column
3872 // width calculation is done before this table DOM is created.
3873 sanityWidth = table.outerWidth();
3874
3875 // Hidden header should have zero height, so remove padding and borders. Then
3876 // set the width based on the real headers
3877
3878 // Apply all styles in one pass
3879 _fnApplyToChildren( zeroOut, headerSrcEls );
3880
3881 // Read all widths in next pass
3882 _fnApplyToChildren( function(nSizer) {
3883 headerContent.push( nSizer.innerHTML );
3884 headerWidths.push( _fnStringToCss( $(nSizer).css('width') ) );
3885 }, headerSrcEls );
3886
3887 // Apply all widths in final pass
3888 _fnApplyToChildren( function(nToSize, i) {
3889 nToSize.style.width = headerWidths[i];
3890 }, headerTrgEls );
3891
3892 $(headerSrcEls).height(0);
3893
3894 /* Same again with the footer if we have one */
3895 if ( footer )
3896 {
3897 _fnApplyToChildren( zeroOut, footerSrcEls );
3898
3899 _fnApplyToChildren( function(nSizer) {
3900 footerWidths.push( _fnStringToCss( $(nSizer).css('width') ) );
3901 }, footerSrcEls );
3902
3903 _fnApplyToChildren( function(nToSize, i) {
3904 nToSize.style.width = footerWidths[i];
3905 }, footerTrgEls );
3906
3907 $(footerSrcEls).height(0);
3908 }
3909
3910
3911 /*
3912 * 3. Apply the measurements
3913 */
3914
3915 // "Hide" the header and footer that we used for the sizing. We need to keep
3916 // the content of the cell so that the width applied to the header and body
3917 // both match, but we want to hide it completely. We want to also fix their
3918 // width to what they currently are
3919 _fnApplyToChildren( function(nSizer, i) {
3920 nSizer.innerHTML = '<div class="dataTables_sizing" style="height:0;overflow:hidden;">'+headerContent[i]+'</div>';
3921 nSizer.style.width = headerWidths[i];
3922 }, headerSrcEls );
3923
3924 if ( footer )
3925 {
3926 _fnApplyToChildren( function(nSizer, i) {
3927 nSizer.innerHTML = "";
3928 nSizer.style.width = footerWidths[i];
3929 }, footerSrcEls );
3930 }
3931
3932 // Sanity check that the table is of a sensible width. If not then we are going to get
3933 // misalignment - try to prevent this by not allowing the table to shrink below its min width
3934 if ( table.outerWidth() < sanityWidth )
3935 {
3936 // The min width depends upon if we have a vertical scrollbar visible or not */
3937 correction = ((divBodyEl.scrollHeight > divBodyEl.offsetHeight ||
3938 divBody.css('overflow-y') == "scroll")) ?
3939 sanityWidth+barWidth :
3940 sanityWidth;
3941
3942 // IE6/7 are a law unto themselves...
3943 if ( ie67 && (divBodyEl.scrollHeight >
3944 divBodyEl.offsetHeight || divBody.css('overflow-y') == "scroll")
3945 ) {
3946 tableStyle.width = _fnStringToCss( correction-barWidth );
3947 }
3948
3949 // And give the user a warning that we've stopped the table getting too small
3950 if ( scrollX === "" || scrollXInner !== "" ) {
3951 _fnLog( settings, 1, 'Possible column misalignment', 6 );
3952 }
3953 }
3954 else
3955 {
3956 correction = '100%';
3957 }
3958
3959 // Apply to the container elements
3960 divBodyStyle.width = _fnStringToCss( correction );
3961 divHeaderStyle.width = _fnStringToCss( correction );
3962
3963 if ( footer ) {
3964 settings.nScrollFoot.style.width = _fnStringToCss( correction );
3965 }
3966
3967
3968 /*
3969 * 4. Clean up
3970 */
3971 if ( ! scrollY ) {
3972 /* IE7< puts a vertical scrollbar in place (when it shouldn't be) due to subtracting
3973 * the scrollbar height from the visible display, rather than adding it on. We need to
3974 * set the height in order to sort this. Don't want to do it in any other browsers.
3975 */
3976 if ( ie67 ) {
3977 divBodyStyle.height = _fnStringToCss( tableEl.offsetHeight+barWidth );
3978 }
3979 }
3980
3981 if ( scrollY && scroll.bCollapse ) {
3982 divBodyStyle.height = _fnStringToCss( scrollY );
3983
3984 var iExtra = (scrollX && tableEl.offsetWidth > divBodyEl.offsetWidth) ?
3985 barWidth :
3986 0;
3987
3988 if ( tableEl.offsetHeight < divBodyEl.offsetHeight ) {
3989 divBodyStyle.height = _fnStringToCss( tableEl.offsetHeight+iExtra );
3990 }
3991 }
3992
3993 /* Finally set the width's of the header and footer tables */
3994 var iOuterWidth = table.outerWidth();
3995 divHeaderTable[0].style.width = _fnStringToCss( iOuterWidth );
3996 divHeaderInnerStyle.width = _fnStringToCss( iOuterWidth );
3997
3998 // Figure out if there are scrollbar present - if so then we need a the header and footer to
3999 // provide a bit more space to allow "overflow" scrolling (i.e. past the scrollbar)
4000 var bScrolling = table.height() > divBodyEl.clientHeight || divBody.css('overflow-y') == "scroll";
4001 var padding = 'padding' + (browser.bScrollbarLeft ? 'Left' : 'Right' );
4002 divHeaderInnerStyle[ padding ] = bScrolling ? barWidth+"px" : "0px";
4003
4004 if ( footer ) {
4005 divFooterTable[0].style.width = _fnStringToCss( iOuterWidth );
4006 divFooterInner[0].style.width = _fnStringToCss( iOuterWidth );
4007 divFooterInner[0].style[padding] = bScrolling ? barWidth+"px" : "0px";
4008 }
4009
4010 /* Adjust the position of the header in case we loose the y-scrollbar */
4011 divBody.scroll();
4012
4013 // If sorting or filtering has occurred, jump the scrolling back to the top
4014 // only if we aren't holding the position
4015 if ( (settings.bSorted || settings.bFiltered) && ! settings._drawHold ) {
4016 divBodyEl.scrollTop = 0;
4017 }
4018 }
4019
4020
4021
4022 /**
4023 * Apply a given function to the display child nodes of an element array (typically
4024 * TD children of TR rows
4025 * @param {function} fn Method to apply to the objects
4026 * @param array {nodes} an1 List of elements to look through for display children
4027 * @param array {nodes} an2 Another list (identical structure to the first) - optional
4028 * @memberof DataTable#oApi
4029 */
4030 function _fnApplyToChildren( fn, an1, an2 )
4031 {
4032 var index=0, i=0, iLen=an1.length;
4033 var nNode1, nNode2;
4034
4035 while ( i < iLen ) {
4036 nNode1 = an1[i].firstChild;
4037 nNode2 = an2 ? an2[i].firstChild : null;
4038
4039 while ( nNode1 ) {
4040 if ( nNode1.nodeType === 1 ) {
4041 if ( an2 ) {
4042 fn( nNode1, nNode2, index );
4043 }
4044 else {
4045 fn( nNode1, index );
4046 }
4047
4048 index++;
4049 }
4050
4051 nNode1 = nNode1.nextSibling;
4052 nNode2 = an2 ? nNode2.nextSibling : null;
4053 }
4054
4055 i++;
4056 }
4057 }
4058
4059
4060
4061 var __re_html_remove = /<.*?>/g;
4062
4063
4064 /**
4065 * Calculate the width of columns for the table
4066 * @param {object} oSettings dataTables settings object
4067 * @memberof DataTable#oApi
4068 */
4069 function _fnCalculateColumnWidths ( oSettings )
4070 {
4071 var
4072 table = oSettings.nTable,
4073 columns = oSettings.aoColumns,
4074 scroll = oSettings.oScroll,
4075 scrollY = scroll.sY,
4076 scrollX = scroll.sX,
4077 scrollXInner = scroll.sXInner,
4078 columnCount = columns.length,
4079 visibleColumns = _fnGetColumns( oSettings, 'bVisible' ),
4080 headerCells = $('th', oSettings.nTHead),
4081 tableWidthAttr = table.getAttribute('width'),
4082 tableContainer = table.parentNode,
4083 userInputs = false,
4084 i, column, columnIdx, width, outerWidth;
4085
4086 /* Convert any user input sizes into pixel sizes */
4087 for ( i=0 ; i<visibleColumns.length ; i++ ) {
4088 column = columns[ visibleColumns[i] ];
4089
4090 if ( column.sWidth !== null ) {
4091 column.sWidth = _fnConvertToWidth( column.sWidthOrig, tableContainer );
4092
4093 userInputs = true;
4094 }
4095 }
4096
4097 /* If the number of columns in the DOM equals the number that we have to
4098 * process in DataTables, then we can use the offsets that are created by
4099 * the web- browser. No custom sizes can be set in order for this to happen,
4100 * nor scrolling used
4101 */
4102 if ( ! userInputs && ! scrollX && ! scrollY &&
4103 columnCount == _fnVisbleColumns( oSettings ) &&
4104 columnCount == headerCells.length
4105 ) {
4106 for ( i=0 ; i<columnCount ; i++ ) {
4107 columns[i].sWidth = _fnStringToCss( headerCells.eq(i).width() );
4108 }
4109 }
4110 else
4111 {
4112 // Otherwise construct a single row table with the widest node in the
4113 // data, assign any user defined widths, then insert it into the DOM and
4114 // allow the browser to do all the hard work of calculating table widths
4115 var tmpTable = $(table).clone() // don't use cloneNode - IE8 will remove events on the main table
4116 .empty()
4117 .css( 'visibility', 'hidden' )
4118 .removeAttr( 'id' )
4119 .append( $(oSettings.nTHead).clone( false ) )
4120 .append( $(oSettings.nTFoot).clone( false ) )
4121 .append( $('<tbody><tr/></tbody>') );
4122
4123 // Remove any assigned widths from the footer (from scrolling)
4124 tmpTable.find('tfoot th, tfoot td').css('width', '');
4125
4126 var tr = tmpTable.find( 'tbody tr' );
4127
4128 // Apply custom sizing to the cloned header
4129 headerCells = _fnGetUniqueThs( oSettings, tmpTable.find('thead')[0] );
4130
4131 for ( i=0 ; i<visibleColumns.length ; i++ ) {
4132 column = columns[ visibleColumns[i] ];
4133
4134 headerCells[i].style.width = column.sWidthOrig !== null && column.sWidthOrig !== '' ?
4135 _fnStringToCss( column.sWidthOrig ) :
4136 '';
4137 }
4138
4139 // Find the widest cell for each column and put it into the table
4140 if ( oSettings.aoData.length ) {
4141 for ( i=0 ; i<visibleColumns.length ; i++ ) {
4142 columnIdx = visibleColumns[i];
4143 column = columns[ columnIdx ];
4144
4145 $( _fnGetWidestNode( oSettings, columnIdx ) )
4146 .clone( false )
4147 .append( column.sContentPadding )
4148 .appendTo( tr );
4149 }
4150 }
4151
4152 // Table has been built, attach to the document so we can work with it
4153 tmpTable.appendTo( tableContainer );
4154
4155 // When scrolling (X or Y) we want to set the width of the table as
4156 // appropriate. However, when not scrolling leave the table width as it
4157 // is. This results in slightly different, but I think correct behaviour
4158 if ( scrollX && scrollXInner ) {
4159 tmpTable.width( scrollXInner );
4160 }
4161 else if ( scrollX ) {
4162 tmpTable.css( 'width', 'auto' );
4163
4164 if ( tmpTable.width() < tableContainer.offsetWidth ) {
4165 tmpTable.width( tableContainer.offsetWidth );
4166 }
4167 }
4168 else if ( scrollY ) {
4169 tmpTable.width( tableContainer.offsetWidth );
4170 }
4171 else if ( tableWidthAttr ) {
4172 tmpTable.width( tableWidthAttr );
4173 }
4174
4175 // Take into account the y scrollbar
4176 _fnScrollingWidthAdjust( oSettings, tmpTable[0] );
4177
4178 // Browsers need a bit of a hand when a width is assigned to any columns
4179 // when x-scrolling as they tend to collapse the table to the min-width,
4180 // even if we sent the column widths. So we need to keep track of what
4181 // the table width should be by summing the user given values, and the
4182 // automatic values
4183 if ( scrollX )
4184 {
4185 var total = 0;
4186
4187 for ( i=0 ; i<visibleColumns.length ; i++ ) {
4188 column = columns[ visibleColumns[i] ];
4189 outerWidth = $(headerCells[i]).outerWidth();
4190
4191 total += column.sWidthOrig === null ?
4192 outerWidth :
4193 parseInt( column.sWidth, 10 ) + outerWidth - $(headerCells[i]).width();
4194 }
4195
4196 tmpTable.width( _fnStringToCss( total ) );
4197 table.style.width = _fnStringToCss( total );
4198 }
4199
4200 // Get the width of each column in the constructed table
4201 for ( i=0 ; i<visibleColumns.length ; i++ ) {
4202 column = columns[ visibleColumns[i] ];
4203 width = $(headerCells[i]).width();
4204
4205 if ( width ) {
4206 column.sWidth = _fnStringToCss( width );
4207 }
4208 }
4209
4210 table.style.width = _fnStringToCss( tmpTable.css('width') );
4211
4212 // Finished with the table - ditch it
4213 tmpTable.remove();
4214 }
4215
4216 // If there is a width attr, we want to attach an event listener which
4217 // allows the table sizing to automatically adjust when the window is
4218 // resized. Use the width attr rather than CSS, since we can't know if the
4219 // CSS is a relative value or absolute - DOM read is always px.
4220 if ( tableWidthAttr ) {
4221 table.style.width = _fnStringToCss( tableWidthAttr );
4222 }
4223
4224 if ( (tableWidthAttr || scrollX) && ! oSettings._reszEvt ) {
4225 $(window).bind('resize.DT-'+oSettings.sInstance, _fnThrottle( function () {
4226 _fnAdjustColumnSizing( oSettings );
4227 } ) );
4228
4229 oSettings._reszEvt = true;
4230 }
4231 }
4232
4233
4234 /**
4235 * Throttle the calls to a function. Arguments and context are maintained for
4236 * the throttled function
4237 * @param {function} fn Function to be called
4238 * @param {int} [freq=200] call frequency in mS
4239 * @returns {function} wrapped function
4240 * @memberof DataTable#oApi
4241 */
4242 function _fnThrottle( fn, freq ) {
4243 var
4244 frequency = freq !== undefined ? freq : 200,
4245 last,
4246 timer;
4247
4248 return function () {
4249 var
4250 that = this,
4251 now = +new Date(),
4252 args = arguments;
4253
4254 if ( last && now < last + frequency ) {
4255 clearTimeout( timer );
4256
4257 timer = setTimeout( function () {
4258 last = undefined;
4259 fn.apply( that, args );
4260 }, frequency );
4261 }
4262 else if ( last ) {
4263 last = now;
4264 fn.apply( that, args );
4265 }
4266 else {
4267 last = now;
4268 }
4269 };
4270 }
4271
4272
4273 /**
4274 * Convert a CSS unit width to pixels (e.g. 2em)
4275 * @param {string} width width to be converted
4276 * @param {node} parent parent to get the with for (required for relative widths) - optional
4277 * @returns {int} width in pixels
4278 * @memberof DataTable#oApi
4279 */
4280 function _fnConvertToWidth ( width, parent )
4281 {
4282 if ( ! width ) {
4283 return 0;
4284 }
4285
4286 var n = $('<div/>')
4287 .css( 'width', _fnStringToCss( width ) )
4288 .appendTo( parent || document.body );
4289
4290 var val = n[0].offsetWidth;
4291 n.remove();
4292
4293 return val;
4294 }
4295
4296
4297 /**
4298 * Adjust a table's width to take account of vertical scroll bar
4299 * @param {object} oSettings dataTables settings object
4300 * @param {node} n table node
4301 * @memberof DataTable#oApi
4302 */
4303
4304 function _fnScrollingWidthAdjust ( settings, n )
4305 {
4306 var scroll = settings.oScroll;
4307
4308 if ( scroll.sX || scroll.sY ) {
4309 // When y-scrolling only, we want to remove the width of the scroll bar
4310 // so the table + scroll bar will fit into the area available, otherwise
4311 // we fix the table at its current size with no adjustment
4312 var correction = ! scroll.sX ? scroll.iBarWidth : 0;
4313 n.style.width = _fnStringToCss( $(n).outerWidth() - correction );
4314 }
4315 }
4316
4317
4318 /**
4319 * Get the widest node
4320 * @param {object} settings dataTables settings object
4321 * @param {int} colIdx column of interest
4322 * @returns {node} widest table node
4323 * @memberof DataTable#oApi
4324 */
4325 function _fnGetWidestNode( settings, colIdx )
4326 {
4327 var idx = _fnGetMaxLenString( settings, colIdx );
4328 if ( idx < 0 ) {
4329 return null;
4330 }
4331
4332 var data = settings.aoData[ idx ];
4333 return ! data.nTr ? // Might not have been created when deferred rendering
4334 $('<td/>').html( _fnGetCellData( settings, idx, colIdx, 'display' ) )[0] :
4335 data.anCells[ colIdx ];
4336 }
4337
4338
4339 /**
4340 * Get the maximum strlen for each data column
4341 * @param {object} settings dataTables settings object
4342 * @param {int} colIdx column of interest
4343 * @returns {string} max string length for each column
4344 * @memberof DataTable#oApi
4345 */
4346 function _fnGetMaxLenString( settings, colIdx )
4347 {
4348 var s, max=-1, maxIdx = -1;
4349
4350 for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
4351 s = _fnGetCellData( settings, i, colIdx, 'display' )+'';
4352 s = s.replace( __re_html_remove, '' );
4353
4354 if ( s.length > max ) {
4355 max = s.length;
4356 maxIdx = i;
4357 }
4358 }
4359
4360 return maxIdx;
4361 }
4362
4363
4364 /**
4365 * Append a CSS unit (only if required) to a string
4366 * @param {string} value to css-ify
4367 * @returns {string} value with css unit
4368 * @memberof DataTable#oApi
4369 */
4370 function _fnStringToCss( s )
4371 {
4372 if ( s === null ) {
4373 return '0px';
4374 }
4375
4376 if ( typeof s == 'number' ) {
4377 return s < 0 ?
4378 '0px' :
4379 s+'px';
4380 }
4381
4382 // Check it has a unit character already
4383 return s.match(/\d$/) ?
4384 s+'px' :
4385 s;
4386 }
4387
4388
4389 /**
4390 * Get the width of a scroll bar in this browser being used
4391 * @returns {int} width in pixels
4392 * @memberof DataTable#oApi
4393 */
4394 function _fnScrollBarWidth ()
4395 {
4396 // On first run a static variable is set, since this is only needed once.
4397 // Subsequent runs will just use the previously calculated value
4398 if ( ! DataTable.__scrollbarWidth ) {
4399 var inner = $('<p/>').css( {
4400 width: '100%',
4401 height: 200,
4402 padding: 0
4403 } )[0];
4404
4405 var outer = $('<div/>')
4406 .css( {
4407 position: 'absolute',
4408 top: 0,
4409 left: 0,
4410 width: 200,
4411 height: 150,
4412 padding: 0,
4413 overflow: 'hidden',
4414 visibility: 'hidden'
4415 } )
4416 .append( inner )
4417 .appendTo( 'body' );
4418
4419 var w1 = inner.offsetWidth;
4420 outer.css( 'overflow', 'scroll' );
4421 var w2 = inner.offsetWidth;
4422
4423 if ( w1 === w2 ) {
4424 w2 = outer[0].clientWidth;
4425 }
4426
4427 outer.remove();
4428
4429 DataTable.__scrollbarWidth = w1 - w2;
4430 }
4431
4432 return DataTable.__scrollbarWidth;
4433 }
4434
4435
4436
4437 function _fnSortFlatten ( settings )
4438 {
4439 var
4440 i, iLen, k, kLen,
4441 aSort = [],
4442 aiOrig = [],
4443 aoColumns = settings.aoColumns,
4444 aDataSort, iCol, sType, srcCol,
4445 fixed = settings.aaSortingFixed,
4446 fixedObj = $.isPlainObject( fixed ),
4447 nestedSort = [],
4448 add = function ( a ) {
4449 if ( a.length && ! $.isArray( a[0] ) ) {
4450 // 1D array
4451 nestedSort.push( a );
4452 }
4453 else {
4454 // 2D array
4455 nestedSort.push.apply( nestedSort, a );
4456 }
4457 };
4458
4459 // Build the sort array, with pre-fix and post-fix options if they have been
4460 // specified
4461 if ( $.isArray( fixed ) ) {
4462 add( fixed );
4463 }
4464
4465 if ( fixedObj && fixed.pre ) {
4466 add( fixed.pre );
4467 }
4468
4469 add( settings.aaSorting );
4470
4471 if (fixedObj && fixed.post ) {
4472 add( fixed.post );
4473 }
4474
4475 for ( i=0 ; i<nestedSort.length ; i++ )
4476 {
4477 srcCol = nestedSort[i][0];
4478 aDataSort = aoColumns[ srcCol ].aDataSort;
4479
4480 for ( k=0, kLen=aDataSort.length ; k<kLen ; k++ )
4481 {
4482 iCol = aDataSort[k];
4483 sType = aoColumns[ iCol ].sType || 'string';
4484
4485 if ( nestedSort[i]._idx === undefined ) {
4486 nestedSort[i]._idx = $.inArray( nestedSort[i][1], aoColumns[iCol].asSorting );
4487 }
4488
4489 aSort.push( {
4490 src: srcCol,
4491 col: iCol,
4492 dir: nestedSort[i][1],
4493 index: nestedSort[i]._idx,
4494 type: sType,
4495 formatter: DataTable.ext.type.order[ sType+"-pre" ]
4496 } );
4497 }
4498 }
4499
4500 return aSort;
4501 }
4502
4503 /**
4504 * Change the order of the table
4505 * @param {object} oSettings dataTables settings object
4506 * @memberof DataTable#oApi
4507 * @todo This really needs split up!
4508 */
4509 function _fnSort ( oSettings )
4510 {
4511 var
4512 i, ien, iLen, j, jLen, k, kLen,
4513 sDataType, nTh,
4514 aiOrig = [],
4515 oExtSort = DataTable.ext.type.order,
4516 aoData = oSettings.aoData,
4517 aoColumns = oSettings.aoColumns,
4518 aDataSort, data, iCol, sType, oSort,
4519 formatters = 0,
4520 sortCol,
4521 displayMaster = oSettings.aiDisplayMaster,
4522 aSort;
4523
4524 // Resolve any column types that are unknown due to addition or invalidation
4525 // @todo Can this be moved into a 'data-ready' handler which is called when
4526 // data is going to be used in the table?
4527 _fnColumnTypes( oSettings );
4528
4529 aSort = _fnSortFlatten( oSettings );
4530
4531 for ( i=0, ien=aSort.length ; i<ien ; i++ ) {
4532 sortCol = aSort[i];
4533
4534 // Track if we can use the fast sort algorithm
4535 if ( sortCol.formatter ) {
4536 formatters++;
4537 }
4538
4539 // Load the data needed for the sort, for each cell
4540 _fnSortData( oSettings, sortCol.col );
4541 }
4542
4543 /* No sorting required if server-side or no sorting array */
4544 if ( _fnDataSource( oSettings ) != 'ssp' && aSort.length !== 0 )
4545 {
4546 // Create a value - key array of the current row positions such that we can use their
4547 // current position during the sort, if values match, in order to perform stable sorting
4548 for ( i=0, iLen=displayMaster.length ; i<iLen ; i++ ) {
4549 aiOrig[ displayMaster[i] ] = i;
4550 }
4551
4552 /* Do the sort - here we want multi-column sorting based on a given data source (column)
4553 * and sorting function (from oSort) in a certain direction. It's reasonably complex to
4554 * follow on it's own, but this is what we want (example two column sorting):
4555 * fnLocalSorting = function(a,b){
4556 * var iTest;
4557 * iTest = oSort['string-asc']('data11', 'data12');
4558 * if (iTest !== 0)
4559 * return iTest;
4560 * iTest = oSort['numeric-desc']('data21', 'data22');
4561 * if (iTest !== 0)
4562 * return iTest;
4563 * return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
4564 * }
4565 * Basically we have a test for each sorting column, if the data in that column is equal,
4566 * test the next column. If all columns match, then we use a numeric sort on the row
4567 * positions in the original data array to provide a stable sort.
4568 *
4569 * Note - I know it seems excessive to have two sorting methods, but the first is around
4570 * 15% faster, so the second is only maintained for backwards compatibility with sorting
4571 * methods which do not have a pre-sort formatting function.
4572 */
4573 if ( formatters === aSort.length ) {
4574 // All sort types have formatting functions
4575 displayMaster.sort( function ( a, b ) {
4576 var
4577 x, y, k, test, sort,
4578 len=aSort.length,
4579 dataA = aoData[a]._aSortData,
4580 dataB = aoData[b]._aSortData;
4581
4582 for ( k=0 ; k<len ; k++ ) {
4583 sort = aSort[k];
4584
4585 x = dataA[ sort.col ];
4586 y = dataB[ sort.col ];
4587
4588 test = x<y ? -1 : x>y ? 1 : 0;
4589 if ( test !== 0 ) {
4590 return sort.dir === 'asc' ? test : -test;
4591 }
4592 }
4593
4594 x = aiOrig[a];
4595 y = aiOrig[b];
4596 return x<y ? -1 : x>y ? 1 : 0;
4597 } );
4598 }
4599 else {
4600 // Depreciated - remove in 1.11 (providing a plug-in option)
4601 // Not all sort types have formatting methods, so we have to call their sorting
4602 // methods.
4603 displayMaster.sort( function ( a, b ) {
4604 var
4605 x, y, k, l, test, sort, fn,
4606 len=aSort.length,
4607 dataA = aoData[a]._aSortData,
4608 dataB = aoData[b]._aSortData;
4609
4610 for ( k=0 ; k<len ; k++ ) {
4611 sort = aSort[k];
4612
4613 x = dataA[ sort.col ];
4614 y = dataB[ sort.col ];
4615
4616 fn = oExtSort[ sort.type+"-"+sort.dir ] || oExtSort[ "string-"+sort.dir ];
4617 test = fn( x, y );
4618 if ( test !== 0 ) {
4619 return test;
4620 }
4621 }
4622
4623 x = aiOrig[a];
4624 y = aiOrig[b];
4625 return x<y ? -1 : x>y ? 1 : 0;
4626 } );
4627 }
4628 }
4629
4630 /* Tell the draw function that we have sorted the data */
4631 oSettings.bSorted = true;
4632 }
4633
4634
4635 function _fnSortAria ( settings )
4636 {
4637 var label;
4638 var nextSort;
4639 var columns = settings.aoColumns;
4640 var aSort = _fnSortFlatten( settings );
4641 var oAria = settings.oLanguage.oAria;
4642
4643 // ARIA attributes - need to loop all columns, to update all (removing old
4644 // attributes as needed)
4645 for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
4646 {
4647 var col = columns[i];
4648 var asSorting = col.asSorting;
4649 var sTitle = col.sTitle.replace( /<.*?>/g, "" );
4650 var th = col.nTh;
4651
4652 // IE7 is throwing an error when setting these properties with jQuery's
4653 // attr() and removeAttr() methods...
4654 th.removeAttribute('aria-sort');
4655
4656 /* In ARIA only the first sorting column can be marked as sorting - no multi-sort option */
4657 if ( col.bSortable ) {
4658 if ( aSort.length > 0 && aSort[0].col == i ) {
4659 th.setAttribute('aria-sort', aSort[0].dir=="asc" ? "ascending" : "descending" );
4660 nextSort = asSorting[ aSort[0].index+1 ] || asSorting[0];
4661 }
4662 else {
4663 nextSort = asSorting[0];
4664 }
4665
4666 label = sTitle + ( nextSort === "asc" ?
4667 oAria.sSortAscending :
4668 oAria.sSortDescending
4669 );
4670 }
4671 else {
4672 label = sTitle;
4673 }
4674
4675 th.setAttribute('aria-label', label);
4676 }
4677 }
4678
4679
4680 /**
4681 * Function to run on user sort request
4682 * @param {object} settings dataTables settings object
4683 * @param {node} attachTo node to attach the handler to
4684 * @param {int} colIdx column sorting index
4685 * @param {boolean} [append=false] Append the requested sort to the existing
4686 * sort if true (i.e. multi-column sort)
4687 * @param {function} [callback] callback function
4688 * @memberof DataTable#oApi
4689 */
4690 function _fnSortListener ( settings, colIdx, append, callback )
4691 {
4692 var col = settings.aoColumns[ colIdx ];
4693 var sorting = settings.aaSorting;
4694 var asSorting = col.asSorting;
4695 var nextSortIdx;
4696 var next = function ( a, overflow ) {
4697 var idx = a._idx;
4698 if ( idx === undefined ) {
4699 idx = $.inArray( a[1], asSorting );
4700 }
4701
4702 return idx+1 < asSorting.length ?
4703 idx+1 :
4704 overflow ?
4705 null :
4706 0;
4707 };
4708
4709 // Convert to 2D array if needed
4710 if ( typeof sorting[0] === 'number' ) {
4711 sorting = settings.aaSorting = [ sorting ];
4712 }
4713
4714 // If appending the sort then we are multi-column sorting
4715 if ( append && settings.oFeatures.bSortMulti ) {
4716 // Are we already doing some kind of sort on this column?
4717 var sortIdx = $.inArray( colIdx, _pluck(sorting, '0') );
4718
4719 if ( sortIdx !== -1 ) {
4720 // Yes, modify the sort
4721 nextSortIdx = next( sorting[sortIdx], true );
4722
4723 if ( nextSortIdx === null ) {
4724 sorting.splice( sortIdx, 1 );
4725 }
4726 else {
4727 sorting[sortIdx][1] = asSorting[ nextSortIdx ];
4728 sorting[sortIdx]._idx = nextSortIdx;
4729 }
4730 }
4731 else {
4732 // No sort on this column yet
4733 sorting.push( [ colIdx, asSorting[0], 0 ] );
4734 sorting[sorting.length-1]._idx = 0;
4735 }
4736 }
4737 else if ( sorting.length && sorting[0][0] == colIdx ) {
4738 // Single column - already sorting on this column, modify the sort
4739 nextSortIdx = next( sorting[0] );
4740
4741 sorting.length = 1;
4742 sorting[0][1] = asSorting[ nextSortIdx ];
4743 sorting[0]._idx = nextSortIdx;
4744 }
4745 else {
4746 // Single column - sort only on this column
4747 sorting.length = 0;
4748 sorting.push( [ colIdx, asSorting[0] ] );
4749 sorting[0]._idx = 0;
4750 }
4751
4752 // Run the sort by calling a full redraw
4753 _fnReDraw( settings );
4754
4755 // callback used for async user interaction
4756 if ( typeof callback == 'function' ) {
4757 callback( settings );
4758 }
4759 }
4760
4761
4762 /**
4763 * Attach a sort handler (click) to a node
4764 * @param {object} settings dataTables settings object
4765 * @param {node} attachTo node to attach the handler to
4766 * @param {int} colIdx column sorting index
4767 * @param {function} [callback] callback function
4768 * @memberof DataTable#oApi
4769 */
4770 function _fnSortAttachListener ( settings, attachTo, colIdx, callback )
4771 {
4772 var col = settings.aoColumns[ colIdx ];
4773
4774 _fnBindAction( attachTo, {}, function (e) {
4775 /* If the column is not sortable - don't to anything */
4776 if ( col.bSortable === false ) {
4777 return;
4778 }
4779
4780 // If processing is enabled use a timeout to allow the processing
4781 // display to be shown - otherwise to it synchronously
4782 if ( settings.oFeatures.bProcessing ) {
4783 _fnProcessingDisplay( settings, true );
4784
4785 setTimeout( function() {
4786 _fnSortListener( settings, colIdx, e.shiftKey, callback );
4787
4788 // In server-side processing, the draw callback will remove the
4789 // processing display
4790 if ( _fnDataSource( settings ) !== 'ssp' ) {
4791 _fnProcessingDisplay( settings, false );
4792 }
4793 }, 0 );
4794 }
4795 else {
4796 _fnSortListener( settings, colIdx, e.shiftKey, callback );
4797 }
4798 } );
4799 }
4800
4801
4802 /**
4803 * Set the sorting classes on table's body, Note: it is safe to call this function
4804 * when bSort and bSortClasses are false
4805 * @param {object} oSettings dataTables settings object
4806 * @memberof DataTable#oApi
4807 */
4808 function _fnSortingClasses( settings )
4809 {
4810 var oldSort = settings.aLastSort;
4811 var sortClass = settings.oClasses.sSortColumn;
4812 var sort = _fnSortFlatten( settings );
4813 var features = settings.oFeatures;
4814 var i, ien, colIdx;
4815
4816 if ( features.bSort && features.bSortClasses ) {
4817 // Remove old sorting classes
4818 for ( i=0, ien=oldSort.length ; i<ien ; i++ ) {
4819 colIdx = oldSort[i].src;
4820
4821 // Remove column sorting
4822 $( _pluck( settings.aoData, 'anCells', colIdx ) )
4823 .removeClass( sortClass + (i<2 ? i+1 : 3) );
4824 }
4825
4826 // Add new column sorting
4827 for ( i=0, ien=sort.length ; i<ien ; i++ ) {
4828 colIdx = sort[i].src;
4829
4830 $( _pluck( settings.aoData, 'anCells', colIdx ) )
4831 .addClass( sortClass + (i<2 ? i+1 : 3) );
4832 }
4833 }
4834
4835 settings.aLastSort = sort;
4836 }
4837
4838
4839 // Get the data to sort a column, be it from cache, fresh (populating the
4840 // cache), or from a sort formatter
4841 function _fnSortData( settings, idx )
4842 {
4843 // Custom sorting function - provided by the sort data type
4844 var column = settings.aoColumns[ idx ];
4845 var customSort = DataTable.ext.order[ column.sSortDataType ];
4846 var customData;
4847
4848 if ( customSort ) {
4849 customData = customSort.call( settings.oInstance, settings, idx,
4850 _fnColumnIndexToVisible( settings, idx )
4851 );
4852 }
4853
4854 // Use / populate cache
4855 var row, cellData;
4856 var formatter = DataTable.ext.type.order[ column.sType+"-pre" ];
4857
4858 for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
4859 row = settings.aoData[i];
4860
4861 if ( ! row._aSortData ) {
4862 row._aSortData = [];
4863 }
4864
4865 if ( ! row._aSortData[idx] || customSort ) {
4866 cellData = customSort ?
4867 customData[i] : // If there was a custom sort function, use data from there
4868 _fnGetCellData( settings, i, idx, 'sort' );
4869
4870 row._aSortData[ idx ] = formatter ?
4871 formatter( cellData ) :
4872 cellData;
4873 }
4874 }
4875 }
4876
4877
4878
4879 /**
4880 * Save the state of a table
4881 * @param {object} oSettings dataTables settings object
4882 * @memberof DataTable#oApi
4883 */
4884 function _fnSaveState ( settings )
4885 {
4886 if ( !settings.oFeatures.bStateSave || settings.bDestroying )
4887 {
4888 return;
4889 }
4890
4891 /* Store the interesting variables */
4892 var state = {
4893 time: +new Date(),
4894 start: settings._iDisplayStart,
4895 length: settings._iDisplayLength,
4896 order: $.extend( true, [], settings.aaSorting ),
4897 search: _fnSearchToCamel( settings.oPreviousSearch ),
4898 columns: $.map( settings.aoColumns, function ( col, i ) {
4899 return {
4900 visible: col.bVisible,
4901 search: _fnSearchToCamel( settings.aoPreSearchCols[i] )
4902 };
4903 } )
4904 };
4905
4906 _fnCallbackFire( settings, "aoStateSaveParams", 'stateSaveParams', [settings, state] );
4907
4908 settings.oSavedState = state;
4909 settings.fnStateSaveCallback.call( settings.oInstance, settings, state );
4910 }
4911
4912
4913 /**
4914 * Attempt to load a saved table state
4915 * @param {object} oSettings dataTables settings object
4916 * @param {object} oInit DataTables init object so we can override settings
4917 * @memberof DataTable#oApi
4918 */
4919 function _fnLoadState ( settings, oInit )
4920 {
4921 var i, ien;
4922 var columns = settings.aoColumns;
4923
4924 if ( ! settings.oFeatures.bStateSave ) {
4925 return;
4926 }
4927
4928 var state = settings.fnStateLoadCallback.call( settings.oInstance, settings );
4929 if ( ! state || ! state.time ) {
4930 return;
4931 }
4932
4933 /* Allow custom and plug-in manipulation functions to alter the saved data set and
4934 * cancelling of loading by returning false
4935 */
4936 var abStateLoad = _fnCallbackFire( settings, 'aoStateLoadParams', 'stateLoadParams', [settings, state] );
4937 if ( $.inArray( false, abStateLoad ) !== -1 ) {
4938 return;
4939 }
4940
4941 /* Reject old data */
4942 var duration = settings.iStateDuration;
4943 if ( duration > 0 && state.time < +new Date() - (duration*1000) ) {
4944 return;
4945 }
4946
4947 // Number of columns have changed - all bets are off, no restore of settings
4948 if ( columns.length !== state.columns.length ) {
4949 return;
4950 }
4951
4952 // Store the saved state so it might be accessed at any time
4953 settings.oLoadedState = $.extend( true, {}, state );
4954
4955 // Restore key features - todo - for 1.11 this needs to be done by
4956 // subscribed events
4957 settings._iDisplayStart = state.start;
4958 settings.iInitDisplayStart = state.start;
4959 settings._iDisplayLength = state.length;
4960 settings.aaSorting = [];
4961
4962 // Order
4963 $.each( state.order, function ( i, col ) {
4964 settings.aaSorting.push( col[0] >= columns.length ?
4965 [ 0, col[1] ] :
4966 col
4967 );
4968 } );
4969
4970 // Search
4971 $.extend( settings.oPreviousSearch, _fnSearchToHung( state.search ) );
4972
4973 // Columns
4974 for ( i=0, ien=state.columns.length ; i<ien ; i++ ) {
4975 var col = state.columns[i];
4976
4977 // Visibility
4978 columns[i].bVisible = col.visible;
4979
4980 // Search
4981 $.extend( settings.aoPreSearchCols[i], _fnSearchToHung( col.search ) );
4982 }
4983
4984 _fnCallbackFire( settings, 'aoStateLoaded', 'stateLoaded', [settings, state] );
4985 }
4986
4987
4988 /**
4989 * Return the settings object for a particular table
4990 * @param {node} table table we are using as a dataTable
4991 * @returns {object} Settings object - or null if not found
4992 * @memberof DataTable#oApi
4993 */
4994 function _fnSettingsFromNode ( table )
4995 {
4996 var settings = DataTable.settings;
4997 var idx = $.inArray( table, _pluck( settings, 'nTable' ) );
4998
4999 return idx !== -1 ?
5000 settings[ idx ] :
5001 null;
5002 }
5003
5004
5005 /**
5006 * Log an error message
5007 * @param {object} settings dataTables settings object
5008 * @param {int} level log error messages, or display them to the user
5009 * @param {string} msg error message
5010 * @param {int} tn Technical note id to get more information about the error.
5011 * @memberof DataTable#oApi
5012 */
5013 function _fnLog( settings, level, msg, tn )
5014 {
5015 msg = 'DataTables warning: '+
5016 (settings!==null ? 'table id='+settings.sTableId+' - ' : '')+msg;
5017
5018 if ( tn ) {
5019 msg += '. For more information about this error, please see '+
5020 'http://datatables.net/tn/'+tn;
5021 }
5022
5023 if ( ! level ) {
5024 // Backwards compatibility pre 1.10
5025 var ext = DataTable.ext;
5026 var type = ext.sErrMode || ext.errMode;
5027
5028 if ( type == 'alert' ) {
5029 alert( msg );
5030 }
5031 else {
5032 throw new Error(msg);
5033 }
5034 }
5035 else if ( window.console && console.log ) {
5036 console.log( msg );
5037 }
5038 }
5039
5040
5041 /**
5042 * See if a property is defined on one object, if so assign it to the other object
5043 * @param {object} ret target object
5044 * @param {object} src source object
5045 * @param {string} name property
5046 * @param {string} [mappedName] name to map too - optional, name used if not given
5047 * @memberof DataTable#oApi
5048 */
5049 function _fnMap( ret, src, name, mappedName )
5050 {
5051 if ( $.isArray( name ) ) {
5052 $.each( name, function (i, val) {
5053 if ( $.isArray( val ) ) {
5054 _fnMap( ret, src, val[0], val[1] );
5055 }
5056 else {
5057 _fnMap( ret, src, val );
5058 }
5059 } );
5060
5061 return;
5062 }
5063
5064 if ( mappedName === undefined ) {
5065 mappedName = name;
5066 }
5067
5068 if ( src[name] !== undefined ) {
5069 ret[mappedName] = src[name];
5070 }
5071 }
5072
5073
5074 /**
5075 * Extend objects - very similar to jQuery.extend, but deep copy objects, and
5076 * shallow copy arrays. The reason we need to do this, is that we don't want to
5077 * deep copy array init values (such as aaSorting) since the dev wouldn't be
5078 * able to override them, but we do want to deep copy arrays.
5079 * @param {object} out Object to extend
5080 * @param {object} extender Object from which the properties will be applied to
5081 * out
5082 * @param {boolean} breakRefs If true, then arrays will be sliced to take an
5083 * independent copy with the exception of the `data` or `aaData` parameters
5084 * if they are present. This is so you can pass in a collection to
5085 * DataTables and have that used as your data source without breaking the
5086 * references
5087 * @returns {object} out Reference, just for convenience - out === the return.
5088 * @memberof DataTable#oApi
5089 * @todo This doesn't take account of arrays inside the deep copied objects.
5090 */
5091 function _fnExtend( out, extender, breakRefs )
5092 {
5093 var val;
5094
5095 for ( var prop in extender ) {
5096 if ( extender.hasOwnProperty(prop) ) {
5097 val = extender[prop];
5098
5099 if ( $.isPlainObject( val ) ) {
5100 if ( ! $.isPlainObject( out[prop] ) ) {
5101 out[prop] = {};
5102 }
5103 $.extend( true, out[prop], val );
5104 }
5105 else if ( breakRefs && prop !== 'data' && prop !== 'aaData' && $.isArray(val) ) {
5106 out[prop] = val.slice();
5107 }
5108 else {
5109 out[prop] = val;
5110 }
5111 }
5112 }
5113
5114 return out;
5115 }
5116
5117
5118 /**
5119 * Bind an event handers to allow a click or return key to activate the callback.
5120 * This is good for accessibility since a return on the keyboard will have the
5121 * same effect as a click, if the element has focus.
5122 * @param {element} n Element to bind the action to
5123 * @param {object} oData Data object to pass to the triggered function
5124 * @param {function} fn Callback function for when the event is triggered
5125 * @memberof DataTable#oApi
5126 */
5127 function _fnBindAction( n, oData, fn )
5128 {
5129 $(n)
5130 .bind( 'click.DT', oData, function (e) {
5131 n.blur(); // Remove focus outline for mouse users
5132 fn(e);
5133 } )
5134 .bind( 'keypress.DT', oData, function (e){
5135 if ( e.which === 13 ) {
5136 e.preventDefault();
5137 fn(e);
5138 }
5139 } )
5140 .bind( 'selectstart.DT', function () {
5141 /* Take the brutal approach to cancelling text selection */
5142 return false;
5143 } );
5144 }
5145
5146
5147 /**
5148 * Register a callback function. Easily allows a callback function to be added to
5149 * an array store of callback functions that can then all be called together.
5150 * @param {object} oSettings dataTables settings object
5151 * @param {string} sStore Name of the array storage for the callbacks in oSettings
5152 * @param {function} fn Function to be called back
5153 * @param {string} sName Identifying name for the callback (i.e. a label)
5154 * @memberof DataTable#oApi
5155 */
5156 function _fnCallbackReg( oSettings, sStore, fn, sName )
5157 {
5158 if ( fn )
5159 {
5160 oSettings[sStore].push( {
5161 "fn": fn,
5162 "sName": sName
5163 } );
5164 }
5165 }
5166
5167
5168 /**
5169 * Fire callback functions and trigger events. Note that the loop over the
5170 * callback array store is done backwards! Further note that you do not want to
5171 * fire off triggers in time sensitive applications (for example cell creation)
5172 * as its slow.
5173 * @param {object} settings dataTables settings object
5174 * @param {string} callbackArr Name of the array storage for the callbacks in
5175 * oSettings
5176 * @param {string} event Name of the jQuery custom event to trigger. If null no
5177 * trigger is fired
5178 * @param {array} args Array of arguments to pass to the callback function /
5179 * trigger
5180 * @memberof DataTable#oApi
5181 */
5182 function _fnCallbackFire( settings, callbackArr, e, args )
5183 {
5184 var ret = [];
5185
5186 if ( callbackArr ) {
5187 ret = $.map( settings[callbackArr].slice().reverse(), function (val, i) {
5188 return val.fn.apply( settings.oInstance, args );
5189 } );
5190 }
5191
5192 if ( e !== null ) {
5193 $(settings.nTable).trigger( e+'.dt', args );
5194 }
5195
5196 return ret;
5197 }
5198
5199
5200 function _fnLengthOverflow ( settings )
5201 {
5202 var
5203 start = settings._iDisplayStart,
5204 end = settings.fnDisplayEnd(),
5205 len = settings._iDisplayLength;
5206
5207 /* If we have space to show extra rows (backing up from the end point - then do so */
5208 if ( start >= end )
5209 {
5210 start = end - len;
5211 }
5212
5213 // Keep the start record on the current page
5214 start -= (start % len);
5215
5216 if ( len === -1 || start < 0 )
5217 {
5218 start = 0;
5219 }
5220
5221 settings._iDisplayStart = start;
5222 }
5223
5224
5225 function _fnRenderer( settings, type )
5226 {
5227 var renderer = settings.renderer;
5228 var host = DataTable.ext.renderer[type];
5229
5230 if ( $.isPlainObject( renderer ) && renderer[type] ) {
5231 // Specific renderer for this type. If available use it, otherwise use
5232 // the default.
5233 return host[renderer[type]] || host._;
5234 }
5235 else if ( typeof renderer === 'string' ) {
5236 // Common renderer - if there is one available for this type use it,
5237 // otherwise use the default
5238 return host[renderer] || host._;
5239 }
5240
5241 // Use the default
5242 return host._;
5243 }
5244
5245
5246 /**
5247 * Detect the data source being used for the table. Used to simplify the code
5248 * a little (ajax) and to make it compress a little smaller.
5249 *
5250 * @param {object} settings dataTables settings object
5251 * @returns {string} Data source
5252 * @memberof DataTable#oApi
5253 */
5254 function _fnDataSource ( settings )
5255 {
5256 if ( settings.oFeatures.bServerSide ) {
5257 return 'ssp';
5258 }
5259 else if ( settings.ajax || settings.sAjaxSource ) {
5260 return 'ajax';
5261 }
5262 return 'dom';
5263 }
5264
5265
5266 DataTable = function( options )
94 var DataTable = function ( options )
5267 95 {
5268 96 /**
5269 97 * Perform a jQuery selector action on the table's TR elements (from the tbody) and
@@ -5305,8 +133,8 b''
5305 133 {
5306 134 return this.api(true).$( sSelector, oOpts );
5307 135 };
5308
5309
136
137
5310 138 /**
5311 139 * Almost identical to $ in operation, but in this case returns the data for the matched
5312 140 * rows - as such, the jQuery selector used should match TR row nodes or TD/TH cell nodes
@@ -5359,8 +187,8 b''
5359 187 {
5360 188 return this.api(true).rows( sSelector, oOpts ).data();
5361 189 };
5362
5363
190
191
5364 192 /**
5365 193 * Create a DataTables Api instance, with the currently selected tables for
5366 194 * the Api's context.
@@ -5378,8 +206,8 b''
5378 206 ) :
5379 207 new _Api( this );
5380 208 };
5381
5382
209
210
5383 211 /**
5384 212 * Add a single new row or multiple rows of data to the table. Please note
5385 213 * that this is suitable for client-side processing only - if you are using
@@ -5421,20 +249,20 b''
5421 249 this.fnAddData = function( data, redraw )
5422 250 {
5423 251 var api = this.api( true );
5424
252
5425 253 /* Check if we want to add multiple rows or not */
5426 254 var rows = $.isArray(data) && ( $.isArray(data[0]) || $.isPlainObject(data[0]) ) ?
5427 255 api.rows.add( data ) :
5428 256 api.row.add( data );
5429
257
5430 258 if ( redraw === undefined || redraw ) {
5431 259 api.draw();
5432 260 }
5433
261
5434 262 return rows.flatten().toArray();
5435 263 };
5436
5437
264
265
5438 266 /**
5439 267 * This function will make DataTables recalculate the column sizes, based on the data
5440 268 * contained in the table and the sizes applied to the columns (in the DOM, CSS or
@@ -5451,7 +279,7 b''
5451 279 * "bPaginate": false
5452 280 * } );
5453 281 *
5454 * $(window).bind('resize', function () {
282 * $(window).on('resize', function () {
5455 283 * oTable.fnAdjustColumnSizing();
5456 284 * } );
5457 285 * } );
@@ -5461,7 +289,7 b''
5461 289 var api = this.api( true ).columns.adjust();
5462 290 var settings = api.settings()[0];
5463 291 var scroll = settings.oScroll;
5464
292
5465 293 if ( bRedraw === undefined || bRedraw ) {
5466 294 api.draw( false );
5467 295 }
@@ -5470,8 +298,8 b''
5470 298 _fnScrollDraw( settings );
5471 299 }
5472 300 };
5473
5474
301
302
5475 303 /**
5476 304 * Quickly and simply clear a table
5477 305 * @param {bool} [bRedraw=true] redraw the table or not
@@ -5489,13 +317,13 b''
5489 317 this.fnClearTable = function( bRedraw )
5490 318 {
5491 319 var api = this.api( true ).clear();
5492
320
5493 321 if ( bRedraw === undefined || bRedraw ) {
5494 322 api.draw();
5495 323 }
5496 324 };
5497
5498
325
326
5499 327 /**
5500 328 * The exact opposite of 'opening' a row, this function will close any rows which
5501 329 * are currently 'open'.
@@ -5524,8 +352,8 b''
5524 352 {
5525 353 this.api( true ).row( nTr ).child.hide();
5526 354 };
5527
5528
355
356
5529 357 /**
5530 358 * Remove a row for the table
5531 359 * @param {mixed} target The index of the row from aoData to be deleted, or
@@ -5550,21 +378,21 b''
5550 378 var rows = api.rows( target );
5551 379 var settings = rows.settings()[0];
5552 380 var data = settings.aoData[ rows[0][0] ];
5553
381
5554 382 rows.remove();
5555
383
5556 384 if ( callback ) {
5557 385 callback.call( this, settings, data );
5558 386 }
5559
387
5560 388 if ( redraw === undefined || redraw ) {
5561 389 api.draw();
5562 390 }
5563
391
5564 392 return data;
5565 393 };
5566
5567
394
395
5568 396 /**
5569 397 * Restore the table to it's original state in the DOM by removing all of DataTables
5570 398 * enhancements, alterations to the DOM structure of the table and event listeners.
@@ -5583,8 +411,8 b''
5583 411 {
5584 412 this.api( true ).destroy( remove );
5585 413 };
5586
5587
414
415
5588 416 /**
5589 417 * Redraw the table
5590 418 * @param {bool} [complete=true] Re-filter and resort (if enabled) the table before the draw.
@@ -5602,11 +430,11 b''
5602 430 this.fnDraw = function( complete )
5603 431 {
5604 432 // Note that this isn't an exact match to the old call to _fnDraw - it takes
5605 // into account the new data, but can old position.
5606 this.api( true ).draw( ! complete );
433 // into account the new data, but can hold position.
434 this.api( true ).draw( complete );
5607 435 };
5608
5609
436
437
5610 438 /**
5611 439 * Filter the input based on data
5612 440 * @param {string} sInput String to filter the table on
@@ -5629,18 +457,18 b''
5629 457 this.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal, bCaseInsensitive )
5630 458 {
5631 459 var api = this.api( true );
5632
460
5633 461 if ( iColumn === null || iColumn === undefined ) {
5634 462 api.search( sInput, bRegex, bSmart, bCaseInsensitive );
5635 463 }
5636 464 else {
5637 465 api.column( iColumn ).search( sInput, bRegex, bSmart, bCaseInsensitive );
5638 466 }
5639
467
5640 468 api.draw();
5641 469 };
5642
5643
470
471
5644 472 /**
5645 473 * Get the data for the whole table, an individual row or an individual cell based on the
5646 474 * provided parameters.
@@ -5681,19 +509,19 b''
5681 509 this.fnGetData = function( src, col )
5682 510 {
5683 511 var api = this.api( true );
5684
512
5685 513 if ( src !== undefined ) {
5686 514 var type = src.nodeName ? src.nodeName.toLowerCase() : '';
5687
515
5688 516 return col !== undefined || type == 'td' || type == 'th' ?
5689 517 api.cell( src, col ).data() :
5690 518 api.row( src ).data() || null;
5691 519 }
5692
520
5693 521 return api.data().toArray();
5694 522 };
5695
5696
523
524
5697 525 /**
5698 526 * Get an array of the TR nodes that are used in the table's body. Note that you will
5699 527 * typically want to use the '$' API method in preference to this as it is more
@@ -5715,13 +543,13 b''
5715 543 this.fnGetNodes = function( iRow )
5716 544 {
5717 545 var api = this.api( true );
5718
546
5719 547 return iRow !== undefined ?
5720 548 api.row( iRow ).node() :
5721 549 api.rows().nodes().flatten().toArray();
5722 550 };
5723
5724
551
552
5725 553 /**
5726 554 * Get the array indexes of a particular cell from it's DOM element
5727 555 * and column index including hidden columns
@@ -5754,13 +582,13 b''
5754 582 {
5755 583 var api = this.api( true );
5756 584 var nodeName = node.nodeName.toUpperCase();
5757
585
5758 586 if ( nodeName == 'TR' ) {
5759 587 return api.row( node ).index();
5760 588 }
5761 589 else if ( nodeName == 'TD' || nodeName == 'TH' ) {
5762 590 var cell = api.cell( node ).index();
5763
591
5764 592 return [
5765 593 cell.row,
5766 594 cell.columnVisible,
@@ -5769,8 +597,8 b''
5769 597 }
5770 598 return null;
5771 599 };
5772
5773
600
601
5774 602 /**
5775 603 * Check to see if a row is 'open' or not.
5776 604 * @param {node} nTr the table row to check
@@ -5798,8 +626,8 b''
5798 626 {
5799 627 return this.api( true ).row( nTr ).child.isShown();
5800 628 };
5801
5802
629
630
5803 631 /**
5804 632 * This function will place a new row directly after a row which is currently
5805 633 * on display on the page, with the HTML contents that is passed into the
@@ -5838,8 +666,8 b''
5838 666 .show()
5839 667 .child()[0];
5840 668 };
5841
5842
669
670
5843 671 /**
5844 672 * Change the pagination - provides the internal logic for pagination in a simple API
5845 673 * function. With this function you can have a DataTables table go to the next,
@@ -5859,13 +687,13 b''
5859 687 this.fnPageChange = function ( mAction, bRedraw )
5860 688 {
5861 689 var api = this.api( true ).page( mAction );
5862
690
5863 691 if ( bRedraw === undefined || bRedraw ) {
5864 692 api.draw(false);
5865 693 }
5866 694 };
5867
5868
695
696
5869 697 /**
5870 698 * Show a particular column
5871 699 * @param {int} iCol The column whose display should be changed
@@ -5885,13 +713,13 b''
5885 713 this.fnSetColumnVis = function ( iCol, bShow, bRedraw )
5886 714 {
5887 715 var api = this.api( true ).column( iCol ).visible( bShow );
5888
716
5889 717 if ( bRedraw === undefined || bRedraw ) {
5890 718 api.columns.adjust().draw();
5891 719 }
5892 720 };
5893
5894
721
722
5895 723 /**
5896 724 * Get the settings for a particular table for external manipulation
5897 725 * @returns {object} DataTables settings object. See
@@ -5912,8 +740,8 b''
5912 740 {
5913 741 return _fnSettingsFromNode( this[_ext.iApiIndex] );
5914 742 };
5915
5916
743
744
5917 745 /**
5918 746 * Sort the table by a particular column
5919 747 * @param {int} iCol the data index to sort on. Note that this will not match the
@@ -5933,8 +761,8 b''
5933 761 {
5934 762 this.api( true ).order( aaSort ).draw();
5935 763 };
5936
5937
764
765
5938 766 /**
5939 767 * Attach a sort listener to an element for a given column
5940 768 * @param {node} nNode the element to attach the sort listener to
@@ -5955,8 +783,8 b''
5955 783 {
5956 784 this.api( true ).order.listener( nNode, iColumn, fnCallback );
5957 785 };
5958
5959
786
787
5960 788 /**
5961 789 * Update a table cell or row - this method will accept either a single value to
5962 790 * update the cell with, an array of values with one element for each column or
@@ -5982,25 +810,25 b''
5982 810 this.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction )
5983 811 {
5984 812 var api = this.api( true );
5985
813
5986 814 if ( iColumn === undefined || iColumn === null ) {
5987 815 api.row( mRow ).data( mData );
5988 816 }
5989 817 else {
5990 818 api.cell( mRow, iColumn ).data( mData );
5991 819 }
5992
820
5993 821 if ( bAction === undefined || bAction ) {
5994 822 api.columns.adjust();
5995 823 }
5996
824
5997 825 if ( bRedraw === undefined || bRedraw ) {
5998 826 api.draw();
5999 827 }
6000 828 return 0;
6001 829 };
6002
6003
830
831
6004 832 /**
6005 833 * Provide a common method for plug-ins to check the version of DataTables being used, in order
6006 834 * to ensure compatibility.
@@ -6019,7 +847,7 b''
6019 847 * } );
6020 848 */
6021 849 this.fnVersionCheck = _ext.fnVersionCheck;
6022
850
6023 851
6024 852 var _that = this;
6025 853 var emptyInit = options === undefined;
@@ -6051,106 +879,112 b''
6051 879 var sId = this.getAttribute( 'id' );
6052 880 var bInitHandedOff = false;
6053 881 var defaults = DataTable.defaults;
6054
6055
882 var $this = $(this);
883
884
6056 885 /* Sanity check */
6057 886 if ( this.nodeName.toLowerCase() != 'table' )
6058 887 {
6059 888 _fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 );
6060 889 return;
6061 890 }
6062
891
6063 892 /* Backwards compatibility for the defaults */
6064 893 _fnCompatOpts( defaults );
6065 894 _fnCompatCols( defaults.column );
6066
895
6067 896 /* Convert the camel-case defaults to Hungarian */
6068 897 _fnCamelToHungarian( defaults, defaults, true );
6069 898 _fnCamelToHungarian( defaults.column, defaults.column, true );
6070
899
6071 900 /* Setting up the initialisation object */
6072 _fnCamelToHungarian( defaults, oInit );
6073
901 _fnCamelToHungarian( defaults, $.extend( oInit, $this.data() ) );
902
903
904
6074 905 /* Check to see if we are re-initialising a table */
6075 906 var allSettings = DataTable.settings;
6076 907 for ( i=0, iLen=allSettings.length ; i<iLen ; i++ )
6077 908 {
909 var s = allSettings[i];
910
6078 911 /* Base check on table node */
6079 if ( allSettings[i].nTable == this )
912 if ( s.nTable == this || s.nTHead.parentNode == this || (s.nTFoot && s.nTFoot.parentNode == this) )
6080 913 {
6081 914 var bRetrieve = oInit.bRetrieve !== undefined ? oInit.bRetrieve : defaults.bRetrieve;
6082 915 var bDestroy = oInit.bDestroy !== undefined ? oInit.bDestroy : defaults.bDestroy;
6083
916
6084 917 if ( emptyInit || bRetrieve )
6085 918 {
6086 return allSettings[i].oInstance;
919 return s.oInstance;
6087 920 }
6088 921 else if ( bDestroy )
6089 922 {
6090 allSettings[i].oInstance.fnDestroy();
923 s.oInstance.fnDestroy();
6091 924 break;
6092 925 }
6093 926 else
6094 927 {
6095 _fnLog( allSettings[i], 0, 'Cannot reinitialise DataTable', 3 );
928 _fnLog( s, 0, 'Cannot reinitialise DataTable', 3 );
6096 929 return;
6097 930 }
6098 931 }
6099
932
6100 933 /* If the element we are initialising has the same ID as a table which was previously
6101 934 * initialised, but the table nodes don't match (from before) then we destroy the old
6102 935 * instance by simply deleting it. This is under the assumption that the table has been
6103 936 * destroyed by other methods. Anyone using non-id selectors will need to do this manually
6104 937 */
6105 if ( allSettings[i].sTableId == this.id )
938 if ( s.sTableId == this.id )
6106 939 {
6107 940 allSettings.splice( i, 1 );
6108 941 break;
6109 942 }
6110 943 }
6111
944
6112 945 /* Ensure the table has an ID - required for accessibility */
6113 946 if ( sId === null || sId === "" )
6114 947 {
6115 948 sId = "DataTables_Table_"+(DataTable.ext._unique++);
6116 949 this.id = sId;
6117 950 }
6118
951
6119 952 /* Create the settings object for this table and set some of the default parameters */
6120 953 var oSettings = $.extend( true, {}, DataTable.models.oSettings, {
6121 "nTable": this,
6122 "oApi": _that.internal,
6123 "oInit": oInit,
6124 "sDestroyWidth": $(this)[0].style.width,
954 "sDestroyWidth": $this[0].style.width,
6125 955 "sInstance": sId,
6126 956 "sTableId": sId
6127 957 } );
958 oSettings.nTable = this;
959 oSettings.oApi = _that.internal;
960 oSettings.oInit = oInit;
961
6128 962 allSettings.push( oSettings );
6129
963
6130 964 // Need to add the instance after the instance after the settings object has been added
6131 965 // to the settings array, so we can self reference the table instance if more than one
6132 oSettings.oInstance = (_that.length===1) ? _that : $(this).dataTable();
6133
966 oSettings.oInstance = (_that.length===1) ? _that : $this.dataTable();
967
6134 968 // Backwards compatibility, before we apply all the defaults
6135 969 _fnCompatOpts( oInit );
6136
970
6137 971 if ( oInit.oLanguage )
6138 972 {
6139 973 _fnLanguageCompat( oInit.oLanguage );
6140 974 }
6141
975
6142 976 // If the length menu is given, but the init display length is not, use the length menu
6143 977 if ( oInit.aLengthMenu && ! oInit.iDisplayLength )
6144 978 {
6145 979 oInit.iDisplayLength = $.isArray( oInit.aLengthMenu[0] ) ?
6146 980 oInit.aLengthMenu[0][0] : oInit.aLengthMenu[0];
6147 981 }
6148
982
6149 983 // Apply the defaults and init options to make a single init object will all
6150 984 // options defined from defaults and instance options.
6151 985 oInit = _fnExtend( $.extend( true, {}, defaults ), oInit );
6152
6153
986
987
6154 988 // Map the initialisation options onto the settings object
6155 989 _fnMap( oSettings.oFeatures, oInit, [
6156 990 "bPaginate",
@@ -6185,6 +1019,7 b''
6185 1019 "fnStateSaveCallback",
6186 1020 "renderer",
6187 1021 "searchDelay",
1022 "rowId",
6188 1023 [ "iCookieDuration", "iStateDuration" ], // backwards compat
6189 1024 [ "oSearch", "oPreviousSearch" ],
6190 1025 [ "aoSearchCols", "aoPreSearchCols" ],
@@ -6198,7 +1033,7 b''
6198 1033 [ "bScrollCollapse", "bCollapse" ]
6199 1034 ] );
6200 1035 _fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" );
6201
1036
6202 1037 /* Callback functions which are array driven */
6203 1038 _fnCallbackReg( oSettings, 'aoDrawCallback', oInit.fnDrawCallback, 'user' );
6204 1039 _fnCallbackReg( oSettings, 'aoServerParams', oInit.fnServerParams, 'user' );
@@ -6211,9 +1046,14 b''
6211 1046 _fnCallbackReg( oSettings, 'aoFooterCallback', oInit.fnFooterCallback, 'user' );
6212 1047 _fnCallbackReg( oSettings, 'aoInitComplete', oInit.fnInitComplete, 'user' );
6213 1048 _fnCallbackReg( oSettings, 'aoPreDrawCallback', oInit.fnPreDrawCallback, 'user' );
6214
1049
1050 oSettings.rowIdFn = _fnGetObjectDataFn( oInit.rowId );
1051
1052 /* Browser support detection */
1053 _fnBrowserDetect( oSettings );
1054
6215 1055 var oClasses = oSettings.oClasses;
6216
1056
6217 1057 // @todo Remove in 1.11
6218 1058 if ( oInit.bJQueryUI )
6219 1059 {
@@ -6221,13 +1061,13 b''
6221 1061 * you want to have multiple tables with multiple independent classes
6222 1062 */
6223 1063 $.extend( oClasses, DataTable.ext.oJUIClasses, oInit.oClasses );
6224
1064
6225 1065 if ( oInit.sDom === defaults.sDom && defaults.sDom === "lfrtip" )
6226 1066 {
6227 1067 /* Set the DOM to use a layout suitable for jQuery UI's theming */
6228 1068 oSettings.sDom = '<"H"lfr>t<"F"ip>';
6229 1069 }
6230
1070
6231 1071 if ( ! oSettings.renderer ) {
6232 1072 oSettings.renderer = 'jqueryui';
6233 1073 }
@@ -6239,24 +1079,16 b''
6239 1079 {
6240 1080 $.extend( oClasses, DataTable.ext.classes, oInit.oClasses );
6241 1081 }
6242 $(this).addClass( oClasses.sTable );
6243
6244 /* Calculate the scroll bar width and cache it for use later on */
6245 if ( oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "" )
6246 {
6247 oSettings.oScroll.iBarWidth = _fnScrollBarWidth();
6248 }
6249 if ( oSettings.oScroll.sX === true ) { // Easy initialisation of x-scrolling
6250 oSettings.oScroll.sX = '100%';
6251 }
6252
1082 $this.addClass( oClasses.sTable );
1083
1084
6253 1085 if ( oSettings.iInitDisplayStart === undefined )
6254 1086 {
6255 1087 /* Display start point, taking into account the save saving */
6256 1088 oSettings.iInitDisplayStart = oInit.iDisplayStart;
6257 1089 oSettings._iDisplayStart = oInit.iDisplayStart;
6258 1090 }
6259
1091
6260 1092 if ( oInit.iDeferLoading !== null )
6261 1093 {
6262 1094 oSettings.bDeferLoading = true;
@@ -6264,12 +1096,12 b''
6264 1096 oSettings._iRecordsDisplay = tmp ? oInit.iDeferLoading[0] : oInit.iDeferLoading;
6265 1097 oSettings._iRecordsTotal = tmp ? oInit.iDeferLoading[1] : oInit.iDeferLoading;
6266 1098 }
6267
1099
6268 1100 /* Language definitions */
6269 1101 var oLanguage = oSettings.oLanguage;
6270 1102 $.extend( true, oLanguage, oInit.oLanguage );
6271
6272 if ( oLanguage.sUrl !== "" )
1103
1104 if ( oLanguage.sUrl )
6273 1105 {
6274 1106 /* Get the language definitions from a file - because this Ajax call makes the language
6275 1107 * get async to the remainder of this function we use bInitHandedOff to indicate that
@@ -6291,7 +1123,7 b''
6291 1123 } );
6292 1124 bInitHandedOff = true;
6293 1125 }
6294
1126
6295 1127 /*
6296 1128 * Stripes
6297 1129 */
@@ -6302,17 +1134,17 b''
6302 1134 oClasses.sStripeEven
6303 1135 ];
6304 1136 }
6305
1137
6306 1138 /* Remove row stripe classes if they are already on the table row */
6307 1139 var stripeClasses = oSettings.asStripeClasses;
6308 var rowOne = $('tbody tr:eq(0)', this);
1140 var rowOne = $this.children('tbody').find('tr').eq(0);
6309 1141 if ( $.inArray( true, $.map( stripeClasses, function(el, i) {
6310 1142 return rowOne.hasClass(el);
6311 1143 } ) ) !== -1 ) {
6312 1144 $('tbody tr', this).removeClass( stripeClasses.join(' ') );
6313 1145 oSettings.asDestroyStripes = stripeClasses.slice();
6314 1146 }
6315
1147
6316 1148 /*
6317 1149 * Columns
6318 1150 * See if we should load columns automatically or use defined ones
@@ -6325,7 +1157,7 b''
6325 1157 _fnDetectHeader( oSettings.aoHeader, nThead[0] );
6326 1158 anThs = _fnGetUniqueThs( oSettings );
6327 1159 }
6328
1160
6329 1161 /* If not given a column array, generate one with nulls */
6330 1162 if ( oInit.aoColumns === null )
6331 1163 {
@@ -6339,33 +1171,33 b''
6339 1171 {
6340 1172 aoColumnsInit = oInit.aoColumns;
6341 1173 }
6342
1174
6343 1175 /* Add the columns */
6344 1176 for ( i=0, iLen=aoColumnsInit.length ; i<iLen ; i++ )
6345 1177 {
6346 1178 _fnAddColumn( oSettings, anThs ? anThs[i] : null );
6347 1179 }
6348
1180
6349 1181 /* Apply the column definitions */
6350 1182 _fnApplyColumnDefs( oSettings, oInit.aoColumnDefs, aoColumnsInit, function (iCol, oDef) {
6351 1183 _fnColumnOptions( oSettings, iCol, oDef );
6352 1184 } );
6353
1185
6354 1186 /* HTML5 attribute detection - build an mData object automatically if the
6355 1187 * attributes are found
6356 1188 */
6357 1189 if ( rowOne.length ) {
6358 1190 var a = function ( cell, name ) {
6359 return cell.getAttribute( 'data-'+name ) ? name : null;
1191 return cell.getAttribute( 'data-'+name ) !== null ? name : null;
6360 1192 };
6361
6362 $.each( _fnGetRowElements( oSettings, rowOne[0] ).cells, function (i, cell) {
1193
1194 $( rowOne[0] ).children('th, td').each( function (i, cell) {
6363 1195 var col = oSettings.aoColumns[i];
6364
1196
6365 1197 if ( col.mData === i ) {
6366 1198 var sort = a( cell, 'sort' ) || a( cell, 'order' );
6367 1199 var filter = a( cell, 'filter' ) || a( cell, 'search' );
6368
1200
6369 1201 if ( sort !== null || filter !== null ) {
6370 1202 col.mData = {
6371 1203 _: i+'.display',
@@ -6373,149 +1205,5487 b''
6373 1205 type: sort !== null ? i+'.@data-'+sort : undefined,
6374 1206 filter: filter !== null ? i+'.@data-'+filter : undefined
6375 1207 };
6376
1208
6377 1209 _fnColumnOptions( oSettings, i );
6378 1210 }
6379 1211 }
6380 1212 } );
6381 1213 }
6382
1214
6383 1215 var features = oSettings.oFeatures;
6384
1216 var loadedInit = function () {
1217 /*
1218 * Sorting
1219 * @todo For modularisation (1.11) this needs to do into a sort start up handler
1220 */
1221
1222 // If aaSorting is not defined, then we use the first indicator in asSorting
1223 // in case that has been altered, so the default sort reflects that option
1224 if ( oInit.aaSorting === undefined ) {
1225 var sorting = oSettings.aaSorting;
1226 for ( i=0, iLen=sorting.length ; i<iLen ; i++ ) {
1227 sorting[i][1] = oSettings.aoColumns[ i ].asSorting[0];
1228 }
1229 }
1230
1231 /* Do a first pass on the sorting classes (allows any size changes to be taken into
1232 * account, and also will apply sorting disabled classes if disabled
1233 */
1234 _fnSortingClasses( oSettings );
1235
1236 if ( features.bSort ) {
1237 _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
1238 if ( oSettings.bSorted ) {
1239 var aSort = _fnSortFlatten( oSettings );
1240 var sortedColumns = {};
1241
1242 $.each( aSort, function (i, val) {
1243 sortedColumns[ val.src ] = val.dir;
1244 } );
1245
1246 _fnCallbackFire( oSettings, null, 'order', [oSettings, aSort, sortedColumns] );
1247 _fnSortAria( oSettings );
1248 }
1249 } );
1250 }
1251
1252 _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
1253 if ( oSettings.bSorted || _fnDataSource( oSettings ) === 'ssp' || features.bDeferRender ) {
1254 _fnSortingClasses( oSettings );
1255 }
1256 }, 'sc' );
1257
1258
1259 /*
1260 * Final init
1261 * Cache the header, body and footer as required, creating them if needed
1262 */
1263
1264 // Work around for Webkit bug 83867 - store the caption-side before removing from doc
1265 var captions = $this.children('caption').each( function () {
1266 this._captionSide = $(this).css('caption-side');
1267 } );
1268
1269 var thead = $this.children('thead');
1270 if ( thead.length === 0 ) {
1271 thead = $('<thead/>').appendTo($this);
1272 }
1273 oSettings.nTHead = thead[0];
1274
1275 var tbody = $this.children('tbody');
1276 if ( tbody.length === 0 ) {
1277 tbody = $('<tbody/>').appendTo($this);
1278 }
1279 oSettings.nTBody = tbody[0];
1280
1281 var tfoot = $this.children('tfoot');
1282 if ( tfoot.length === 0 && captions.length > 0 && (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") ) {
1283 // If we are a scrolling table, and no footer has been given, then we need to create
1284 // a tfoot element for the caption element to be appended to
1285 tfoot = $('<tfoot/>').appendTo($this);
1286 }
1287
1288 if ( tfoot.length === 0 || tfoot.children().length === 0 ) {
1289 $this.addClass( oClasses.sNoFooter );
1290 }
1291 else if ( tfoot.length > 0 ) {
1292 oSettings.nTFoot = tfoot[0];
1293 _fnDetectHeader( oSettings.aoFooter, oSettings.nTFoot );
1294 }
1295
1296 /* Check if there is data passing into the constructor */
1297 if ( oInit.aaData ) {
1298 for ( i=0 ; i<oInit.aaData.length ; i++ ) {
1299 _fnAddData( oSettings, oInit.aaData[ i ] );
1300 }
1301 }
1302 else if ( oSettings.bDeferLoading || _fnDataSource( oSettings ) == 'dom' ) {
1303 /* Grab the data from the page - only do this when deferred loading or no Ajax
1304 * source since there is no point in reading the DOM data if we are then going
1305 * to replace it with Ajax data
1306 */
1307 _fnAddTr( oSettings, $(oSettings.nTBody).children('tr') );
1308 }
1309
1310 /* Copy the data index array */
1311 oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
1312
1313 /* Initialisation complete - table can be drawn */
1314 oSettings.bInitialised = true;
1315
1316 /* Check if we need to initialise the table (it might not have been handed off to the
1317 * language processor)
1318 */
1319 if ( bInitHandedOff === false ) {
1320 _fnInitialise( oSettings );
1321 }
1322 };
1323
6385 1324 /* Must be done after everything which can be overridden by the state saving! */
6386 1325 if ( oInit.bStateSave )
6387 1326 {
6388 1327 features.bStateSave = true;
6389 _fnLoadState( oSettings, oInit );
6390 1328 _fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState, 'state_save' );
6391 }
6392
6393
6394 /*
6395 * Sorting
6396 * @todo For modularisation (1.11) this needs to do into a sort start up handler
6397 */
6398
6399 // If aaSorting is not defined, then we use the first indicator in asSorting
6400 // in case that has been altered, so the default sort reflects that option
6401 if ( oInit.aaSorting === undefined )
6402 {
6403 var sorting = oSettings.aaSorting;
6404 for ( i=0, iLen=sorting.length ; i<iLen ; i++ )
6405 {
6406 sorting[i][1] = oSettings.aoColumns[ i ].asSorting[0];
6407 }
6408 }
6409
6410 /* Do a first pass on the sorting classes (allows any size changes to be taken into
6411 * account, and also will apply sorting disabled classes if disabled
6412 */
6413 _fnSortingClasses( oSettings );
6414
6415 if ( features.bSort )
6416 {
6417 _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
6418 if ( oSettings.bSorted ) {
6419 var aSort = _fnSortFlatten( oSettings );
6420 var sortedColumns = {};
6421
6422 $.each( aSort, function (i, val) {
6423 sortedColumns[ val.src ] = val.dir;
6424 } );
6425
6426 _fnCallbackFire( oSettings, null, 'order', [oSettings, aSort, sortedColumns] );
6427 _fnSortAria( oSettings );
6428 }
6429 } );
6430 }
6431
6432 _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
6433 if ( oSettings.bSorted || _fnDataSource( oSettings ) === 'ssp' || features.bDeferRender ) {
6434 _fnSortingClasses( oSettings );
6435 }
6436 }, 'sc' );
6437
6438
6439 /*
6440 * Final init
6441 * Cache the header, body and footer as required, creating them if needed
6442 */
6443
6444 /* Browser support detection */
6445 _fnBrowserDetect( oSettings );
6446
6447 // Work around for Webkit bug 83867 - store the caption-side before removing from doc
6448 var captions = $(this).children('caption').each( function () {
6449 this._captionSide = $(this).css('caption-side');
6450 } );
6451
6452 var thead = $(this).children('thead');
6453 if ( thead.length === 0 )
6454 {
6455 thead = $('<thead/>').appendTo(this);
6456 }
6457 oSettings.nTHead = thead[0];
6458
6459 var tbody = $(this).children('tbody');
6460 if ( tbody.length === 0 )
6461 {
6462 tbody = $('<tbody/>').appendTo(this);
6463 }
6464 oSettings.nTBody = tbody[0];
6465
6466 var tfoot = $(this).children('tfoot');
6467 if ( tfoot.length === 0 && captions.length > 0 && (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") )
6468 {
6469 // If we are a scrolling table, and no footer has been given, then we need to create
6470 // a tfoot element for the caption element to be appended to
6471 tfoot = $('<tfoot/>').appendTo(this);
6472 }
6473
6474 if ( tfoot.length === 0 || tfoot.children().length === 0 ) {
6475 $(this).addClass( oClasses.sNoFooter );
6476 }
6477 else if ( tfoot.length > 0 ) {
6478 oSettings.nTFoot = tfoot[0];
6479 _fnDetectHeader( oSettings.aoFooter, oSettings.nTFoot );
6480 }
6481
6482 /* Check if there is data passing into the constructor */
6483 if ( oInit.aaData )
6484 {
6485 for ( i=0 ; i<oInit.aaData.length ; i++ )
6486 {
6487 _fnAddData( oSettings, oInit.aaData[ i ] );
6488 }
6489 }
6490 else if ( oSettings.bDeferLoading || _fnDataSource( oSettings ) == 'dom' )
6491 {
6492 /* Grab the data from the page - only do this when deferred loading or no Ajax
6493 * source since there is no point in reading the DOM data if we are then going
6494 * to replace it with Ajax data
6495 */
6496 _fnAddTr( oSettings, $(oSettings.nTBody).children('tr') );
6497 }
6498
6499 /* Copy the data index array */
6500 oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
6501
6502 /* Initialisation complete - table can be drawn */
6503 oSettings.bInitialised = true;
6504
6505 /* Check if we need to initialise the table (it might not have been handed off to the
6506 * language processor)
6507 */
6508 if ( bInitHandedOff === false )
6509 {
6510 _fnInitialise( oSettings );
6511 }
1329 _fnLoadState( oSettings, oInit, loadedInit );
1330 }
1331 else {
1332 loadedInit();
1333 }
1334
6512 1335 } );
6513 1336 _that = null;
6514 1337 return this;
6515 1338 };
6516 1339
6517
6518
1340
1341 /*
1342 * It is useful to have variables which are scoped locally so only the
1343 * DataTables functions can access them and they don't leak into global space.
1344 * At the same time these functions are often useful over multiple files in the
1345 * core and API, so we list, or at least document, all variables which are used
1346 * by DataTables as private variables here. This also ensures that there is no
1347 * clashing of variable names and that they can easily referenced for reuse.
1348 */
1349
1350
1351 // Defined else where
1352 // _selector_run
1353 // _selector_opts
1354 // _selector_first
1355 // _selector_row_indexes
1356
1357 var _ext; // DataTable.ext
1358 var _Api; // DataTable.Api
1359 var _api_register; // DataTable.Api.register
1360 var _api_registerPlural; // DataTable.Api.registerPlural
1361
1362 var _re_dic = {};
1363 var _re_new_lines = /[\r\n]/g;
1364 var _re_html = /<.*?>/g;
1365
1366 // This is not strict ISO8601 - Date.parse() is quite lax, although
1367 // implementations differ between browsers.
1368 var _re_date = /^\d{2,4}[\.\/\-]\d{1,2}[\.\/\-]\d{1,2}([T ]{1}\d{1,2}[:\.]\d{2}([\.:]\d{2})?)?$/;
1369
1370 // Escape regular expression special characters
1371 var _re_escape_regex = new RegExp( '(\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ].join('|\\') + ')', 'g' );
1372
1373 // http://en.wikipedia.org/wiki/Foreign_exchange_market
1374 // - \u20BD - Russian ruble.
1375 // - \u20a9 - South Korean Won
1376 // - \u20BA - Turkish Lira
1377 // - \u20B9 - Indian Rupee
1378 // - R - Brazil (R$) and South Africa
1379 // - fr - Swiss Franc
1380 // - kr - Swedish krona, Norwegian krone and Danish krone
1381 // - \u2009 is thin space and \u202F is narrow no-break space, both used in many
1382 // standards as thousands separators.
1383 var _re_formatted_numeric = /[',$£€¥%\u2009\u202F\u20BD\u20a9\u20BArfk]/gi;
1384
1385
1386 var _empty = function ( d ) {
1387 return !d || d === true || d === '-' ? true : false;
1388 };
1389
1390
1391 var _intVal = function ( s ) {
1392 var integer = parseInt( s, 10 );
1393 return !isNaN(integer) && isFinite(s) ? integer : null;
1394 };
1395
1396 // Convert from a formatted number with characters other than `.` as the
1397 // decimal place, to a Javascript number
1398 var _numToDecimal = function ( num, decimalPoint ) {
1399 // Cache created regular expressions for speed as this function is called often
1400 if ( ! _re_dic[ decimalPoint ] ) {
1401 _re_dic[ decimalPoint ] = new RegExp( _fnEscapeRegex( decimalPoint ), 'g' );
1402 }
1403 return typeof num === 'string' && decimalPoint !== '.' ?
1404 num.replace( /\./g, '' ).replace( _re_dic[ decimalPoint ], '.' ) :
1405 num;
1406 };
1407
1408
1409 var _isNumber = function ( d, decimalPoint, formatted ) {
1410 var strType = typeof d === 'string';
1411
1412 // If empty return immediately so there must be a number if it is a
1413 // formatted string (this stops the string "k", or "kr", etc being detected
1414 // as a formatted number for currency
1415 if ( _empty( d ) ) {
1416 return true;
1417 }
1418
1419 if ( decimalPoint && strType ) {
1420 d = _numToDecimal( d, decimalPoint );
1421 }
1422
1423 if ( formatted && strType ) {
1424 d = d.replace( _re_formatted_numeric, '' );
1425 }
1426
1427 return !isNaN( parseFloat(d) ) && isFinite( d );
1428 };
1429
1430
1431 // A string without HTML in it can be considered to be HTML still
1432 var _isHtml = function ( d ) {
1433 return _empty( d ) || typeof d === 'string';
1434 };
1435
1436
1437 var _htmlNumeric = function ( d, decimalPoint, formatted ) {
1438 if ( _empty( d ) ) {
1439 return true;
1440 }
1441
1442 var html = _isHtml( d );
1443 return ! html ?
1444 null :
1445 _isNumber( _stripHtml( d ), decimalPoint, formatted ) ?
1446 true :
1447 null;
1448 };
1449
1450
1451 var _pluck = function ( a, prop, prop2 ) {
1452 var out = [];
1453 var i=0, ien=a.length;
1454
1455 // Could have the test in the loop for slightly smaller code, but speed
1456 // is essential here
1457 if ( prop2 !== undefined ) {
1458 for ( ; i<ien ; i++ ) {
1459 if ( a[i] && a[i][ prop ] ) {
1460 out.push( a[i][ prop ][ prop2 ] );
1461 }
1462 }
1463 }
1464 else {
1465 for ( ; i<ien ; i++ ) {
1466 if ( a[i] ) {
1467 out.push( a[i][ prop ] );
1468 }
1469 }
1470 }
1471
1472 return out;
1473 };
1474
1475
1476 // Basically the same as _pluck, but rather than looping over `a` we use `order`
1477 // as the indexes to pick from `a`
1478 var _pluck_order = function ( a, order, prop, prop2 )
1479 {
1480 var out = [];
1481 var i=0, ien=order.length;
1482
1483 // Could have the test in the loop for slightly smaller code, but speed
1484 // is essential here
1485 if ( prop2 !== undefined ) {
1486 for ( ; i<ien ; i++ ) {
1487 if ( a[ order[i] ][ prop ] ) {
1488 out.push( a[ order[i] ][ prop ][ prop2 ] );
1489 }
1490 }
1491 }
1492 else {
1493 for ( ; i<ien ; i++ ) {
1494 out.push( a[ order[i] ][ prop ] );
1495 }
1496 }
1497
1498 return out;
1499 };
1500
1501
1502 var _range = function ( len, start )
1503 {
1504 var out = [];
1505 var end;
1506
1507 if ( start === undefined ) {
1508 start = 0;
1509 end = len;
1510 }
1511 else {
1512 end = start;
1513 start = len;
1514 }
1515
1516 for ( var i=start ; i<end ; i++ ) {
1517 out.push( i );
1518 }
1519
1520 return out;
1521 };
1522
1523
1524 var _removeEmpty = function ( a )
1525 {
1526 var out = [];
1527
1528 for ( var i=0, ien=a.length ; i<ien ; i++ ) {
1529 if ( a[i] ) { // careful - will remove all falsy values!
1530 out.push( a[i] );
1531 }
1532 }
1533
1534 return out;
1535 };
1536
1537
1538 var _stripHtml = function ( d ) {
1539 return d.replace( _re_html, '' );
1540 };
1541
1542
1543 /**
1544 * Find the unique elements in a source array.
1545 *
1546 * @param {array} src Source array
1547 * @return {array} Array of unique items
1548 * @ignore
1549 */
1550 var _unique = function ( src )
1551 {
1552 // A faster unique method is to use object keys to identify used values,
1553 // but this doesn't work with arrays or objects, which we must also
1554 // consider. See jsperf.com/compare-array-unique-versions/4 for more
1555 // information.
1556 var
1557 out = [],
1558 val,
1559 i, ien=src.length,
1560 j, k=0;
1561
1562 again: for ( i=0 ; i<ien ; i++ ) {
1563 val = src[i];
1564
1565 for ( j=0 ; j<k ; j++ ) {
1566 if ( out[j] === val ) {
1567 continue again;
1568 }
1569 }
1570
1571 out.push( val );
1572 k++;
1573 }
1574
1575 return out;
1576 };
1577
1578
1579 /**
1580 * DataTables utility methods
1581 *
1582 * This namespace provides helper methods that DataTables uses internally to
1583 * create a DataTable, but which are not exclusively used only for DataTables.
1584 * These methods can be used by extension authors to save the duplication of
1585 * code.
1586 *
1587 * @namespace
1588 */
1589 DataTable.util = {
1590 /**
1591 * Throttle the calls to a function. Arguments and context are maintained
1592 * for the throttled function.
1593 *
1594 * @param {function} fn Function to be called
1595 * @param {integer} freq Call frequency in mS
1596 * @return {function} Wrapped function
1597 */
1598 throttle: function ( fn, freq ) {
1599 var
1600 frequency = freq !== undefined ? freq : 200,
1601 last,
1602 timer;
1603
1604 return function () {
1605 var
1606 that = this,
1607 now = +new Date(),
1608 args = arguments;
1609
1610 if ( last && now < last + frequency ) {
1611 clearTimeout( timer );
1612
1613 timer = setTimeout( function () {
1614 last = undefined;
1615 fn.apply( that, args );
1616 }, frequency );
1617 }
1618 else {
1619 last = now;
1620 fn.apply( that, args );
1621 }
1622 };
1623 },
1624
1625
1626 /**
1627 * Escape a string such that it can be used in a regular expression
1628 *
1629 * @param {string} val string to escape
1630 * @returns {string} escaped string
1631 */
1632 escapeRegex: function ( val ) {
1633 return val.replace( _re_escape_regex, '\\$1' );
1634 }
1635 };
1636
1637
1638
1639 /**
1640 * Create a mapping object that allows camel case parameters to be looked up
1641 * for their Hungarian counterparts. The mapping is stored in a private
1642 * parameter called `_hungarianMap` which can be accessed on the source object.
1643 * @param {object} o
1644 * @memberof DataTable#oApi
1645 */
1646 function _fnHungarianMap ( o )
1647 {
1648 var
1649 hungarian = 'a aa ai ao as b fn i m o s ',
1650 match,
1651 newKey,
1652 map = {};
1653
1654 $.each( o, function (key, val) {
1655 match = key.match(/^([^A-Z]+?)([A-Z])/);
1656
1657 if ( match && hungarian.indexOf(match[1]+' ') !== -1 )
1658 {
1659 newKey = key.replace( match[0], match[2].toLowerCase() );
1660 map[ newKey ] = key;
1661
1662 if ( match[1] === 'o' )
1663 {
1664 _fnHungarianMap( o[key] );
1665 }
1666 }
1667 } );
1668
1669 o._hungarianMap = map;
1670 }
1671
1672
1673 /**
1674 * Convert from camel case parameters to Hungarian, based on a Hungarian map
1675 * created by _fnHungarianMap.
1676 * @param {object} src The model object which holds all parameters that can be
1677 * mapped.
1678 * @param {object} user The object to convert from camel case to Hungarian.
1679 * @param {boolean} force When set to `true`, properties which already have a
1680 * Hungarian value in the `user` object will be overwritten. Otherwise they
1681 * won't be.
1682 * @memberof DataTable#oApi
1683 */
1684 function _fnCamelToHungarian ( src, user, force )
1685 {
1686 if ( ! src._hungarianMap ) {
1687 _fnHungarianMap( src );
1688 }
1689
1690 var hungarianKey;
1691
1692 $.each( user, function (key, val) {
1693 hungarianKey = src._hungarianMap[ key ];
1694
1695 if ( hungarianKey !== undefined && (force || user[hungarianKey] === undefined) )
1696 {
1697 // For objects, we need to buzz down into the object to copy parameters
1698 if ( hungarianKey.charAt(0) === 'o' )
1699 {
1700 // Copy the camelCase options over to the hungarian
1701 if ( ! user[ hungarianKey ] ) {
1702 user[ hungarianKey ] = {};
1703 }
1704 $.extend( true, user[hungarianKey], user[key] );
1705
1706 _fnCamelToHungarian( src[hungarianKey], user[hungarianKey], force );
1707 }
1708 else {
1709 user[hungarianKey] = user[ key ];
1710 }
1711 }
1712 } );
1713 }
1714
1715
1716 /**
1717 * Language compatibility - when certain options are given, and others aren't, we
1718 * need to duplicate the values over, in order to provide backwards compatibility
1719 * with older language files.
1720 * @param {object} oSettings dataTables settings object
1721 * @memberof DataTable#oApi
1722 */
1723 function _fnLanguageCompat( lang )
1724 {
1725 var defaults = DataTable.defaults.oLanguage;
1726 var zeroRecords = lang.sZeroRecords;
1727
1728 /* Backwards compatibility - if there is no sEmptyTable given, then use the same as
1729 * sZeroRecords - assuming that is given.
1730 */
1731 if ( ! lang.sEmptyTable && zeroRecords &&
1732 defaults.sEmptyTable === "No data available in table" )
1733 {
1734 _fnMap( lang, lang, 'sZeroRecords', 'sEmptyTable' );
1735 }
1736
1737 /* Likewise with loading records */
1738 if ( ! lang.sLoadingRecords && zeroRecords &&
1739 defaults.sLoadingRecords === "Loading..." )
1740 {
1741 _fnMap( lang, lang, 'sZeroRecords', 'sLoadingRecords' );
1742 }
1743
1744 // Old parameter name of the thousands separator mapped onto the new
1745 if ( lang.sInfoThousands ) {
1746 lang.sThousands = lang.sInfoThousands;
1747 }
1748
1749 var decimal = lang.sDecimal;
1750 if ( decimal ) {
1751 _addNumericSort( decimal );
1752 }
1753 }
1754
1755
1756 /**
1757 * Map one parameter onto another
1758 * @param {object} o Object to map
1759 * @param {*} knew The new parameter name
1760 * @param {*} old The old parameter name
1761 */
1762 var _fnCompatMap = function ( o, knew, old ) {
1763 if ( o[ knew ] !== undefined ) {
1764 o[ old ] = o[ knew ];
1765 }
1766 };
1767
1768
1769 /**
1770 * Provide backwards compatibility for the main DT options. Note that the new
1771 * options are mapped onto the old parameters, so this is an external interface
1772 * change only.
1773 * @param {object} init Object to map
1774 */
1775 function _fnCompatOpts ( init )
1776 {
1777 _fnCompatMap( init, 'ordering', 'bSort' );
1778 _fnCompatMap( init, 'orderMulti', 'bSortMulti' );
1779 _fnCompatMap( init, 'orderClasses', 'bSortClasses' );
1780 _fnCompatMap( init, 'orderCellsTop', 'bSortCellsTop' );
1781 _fnCompatMap( init, 'order', 'aaSorting' );
1782 _fnCompatMap( init, 'orderFixed', 'aaSortingFixed' );
1783 _fnCompatMap( init, 'paging', 'bPaginate' );
1784 _fnCompatMap( init, 'pagingType', 'sPaginationType' );
1785 _fnCompatMap( init, 'pageLength', 'iDisplayLength' );
1786 _fnCompatMap( init, 'searching', 'bFilter' );
1787
1788 // Boolean initialisation of x-scrolling
1789 if ( typeof init.sScrollX === 'boolean' ) {
1790 init.sScrollX = init.sScrollX ? '100%' : '';
1791 }
1792 if ( typeof init.scrollX === 'boolean' ) {
1793 init.scrollX = init.scrollX ? '100%' : '';
1794 }
1795
1796 // Column search objects are in an array, so it needs to be converted
1797 // element by element
1798 var searchCols = init.aoSearchCols;
1799
1800 if ( searchCols ) {
1801 for ( var i=0, ien=searchCols.length ; i<ien ; i++ ) {
1802 if ( searchCols[i] ) {
1803 _fnCamelToHungarian( DataTable.models.oSearch, searchCols[i] );
1804 }
1805 }
1806 }
1807 }
1808
1809
1810 /**
1811 * Provide backwards compatibility for column options. Note that the new options
1812 * are mapped onto the old parameters, so this is an external interface change
1813 * only.
1814 * @param {object} init Object to map
1815 */
1816 function _fnCompatCols ( init )
1817 {
1818 _fnCompatMap( init, 'orderable', 'bSortable' );
1819 _fnCompatMap( init, 'orderData', 'aDataSort' );
1820 _fnCompatMap( init, 'orderSequence', 'asSorting' );
1821 _fnCompatMap( init, 'orderDataType', 'sortDataType' );
1822
1823 // orderData can be given as an integer
1824 var dataSort = init.aDataSort;
1825 if ( dataSort && ! $.isArray( dataSort ) ) {
1826 init.aDataSort = [ dataSort ];
1827 }
1828 }
1829
1830
1831 /**
1832 * Browser feature detection for capabilities, quirks
1833 * @param {object} settings dataTables settings object
1834 * @memberof DataTable#oApi
1835 */
1836 function _fnBrowserDetect( settings )
1837 {
1838 // We don't need to do this every time DataTables is constructed, the values
1839 // calculated are specific to the browser and OS configuration which we
1840 // don't expect to change between initialisations
1841 if ( ! DataTable.__browser ) {
1842 var browser = {};
1843 DataTable.__browser = browser;
1844
1845 // Scrolling feature / quirks detection
1846 var n = $('<div/>')
1847 .css( {
1848 position: 'fixed',
1849 top: 0,
1850 left: $(window).scrollLeft()*-1, // allow for scrolling
1851 height: 1,
1852 width: 1,
1853 overflow: 'hidden'
1854 } )
1855 .append(
1856 $('<div/>')
1857 .css( {
1858 position: 'absolute',
1859 top: 1,
1860 left: 1,
1861 width: 100,
1862 overflow: 'scroll'
1863 } )
1864 .append(
1865 $('<div/>')
1866 .css( {
1867 width: '100%',
1868 height: 10
1869 } )
1870 )
1871 )
1872 .appendTo( 'body' );
1873
1874 var outer = n.children();
1875 var inner = outer.children();
1876
1877 // Numbers below, in order, are:
1878 // inner.offsetWidth, inner.clientWidth, outer.offsetWidth, outer.clientWidth
1879 //
1880 // IE6 XP: 100 100 100 83
1881 // IE7 Vista: 100 100 100 83
1882 // IE 8+ Windows: 83 83 100 83
1883 // Evergreen Windows: 83 83 100 83
1884 // Evergreen Mac with scrollbars: 85 85 100 85
1885 // Evergreen Mac without scrollbars: 100 100 100 100
1886
1887 // Get scrollbar width
1888 browser.barWidth = outer[0].offsetWidth - outer[0].clientWidth;
1889
1890 // IE6/7 will oversize a width 100% element inside a scrolling element, to
1891 // include the width of the scrollbar, while other browsers ensure the inner
1892 // element is contained without forcing scrolling
1893 browser.bScrollOversize = inner[0].offsetWidth === 100 && outer[0].clientWidth !== 100;
1894
1895 // In rtl text layout, some browsers (most, but not all) will place the
1896 // scrollbar on the left, rather than the right.
1897 browser.bScrollbarLeft = Math.round( inner.offset().left ) !== 1;
1898
1899 // IE8- don't provide height and width for getBoundingClientRect
1900 browser.bBounding = n[0].getBoundingClientRect().width ? true : false;
1901
1902 n.remove();
1903 }
1904
1905 $.extend( settings.oBrowser, DataTable.__browser );
1906 settings.oScroll.iBarWidth = DataTable.__browser.barWidth;
1907 }
1908
1909
1910 /**
1911 * Array.prototype reduce[Right] method, used for browsers which don't support
1912 * JS 1.6. Done this way to reduce code size, since we iterate either way
1913 * @param {object} settings dataTables settings object
1914 * @memberof DataTable#oApi
1915 */
1916 function _fnReduce ( that, fn, init, start, end, inc )
1917 {
1918 var
1919 i = start,
1920 value,
1921 isSet = false;
1922
1923 if ( init !== undefined ) {
1924 value = init;
1925 isSet = true;
1926 }
1927
1928 while ( i !== end ) {
1929 if ( ! that.hasOwnProperty(i) ) {
1930 continue;
1931 }
1932
1933 value = isSet ?
1934 fn( value, that[i], i, that ) :
1935 that[i];
1936
1937 isSet = true;
1938 i += inc;
1939 }
1940
1941 return value;
1942 }
1943
1944 /**
1945 * Add a column to the list used for the table with default values
1946 * @param {object} oSettings dataTables settings object
1947 * @param {node} nTh The th element for this column
1948 * @memberof DataTable#oApi
1949 */
1950 function _fnAddColumn( oSettings, nTh )
1951 {
1952 // Add column to aoColumns array
1953 var oDefaults = DataTable.defaults.column;
1954 var iCol = oSettings.aoColumns.length;
1955 var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, {
1956 "nTh": nTh ? nTh : document.createElement('th'),
1957 "sTitle": oDefaults.sTitle ? oDefaults.sTitle : nTh ? nTh.innerHTML : '',
1958 "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol],
1959 "mData": oDefaults.mData ? oDefaults.mData : iCol,
1960 idx: iCol
1961 } );
1962 oSettings.aoColumns.push( oCol );
1963
1964 // Add search object for column specific search. Note that the `searchCols[ iCol ]`
1965 // passed into extend can be undefined. This allows the user to give a default
1966 // with only some of the parameters defined, and also not give a default
1967 var searchCols = oSettings.aoPreSearchCols;
1968 searchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch, searchCols[ iCol ] );
1969
1970 // Use the default column options function to initialise classes etc
1971 _fnColumnOptions( oSettings, iCol, $(nTh).data() );
1972 }
1973
1974
1975 /**
1976 * Apply options for a column
1977 * @param {object} oSettings dataTables settings object
1978 * @param {int} iCol column index to consider
1979 * @param {object} oOptions object with sType, bVisible and bSearchable etc
1980 * @memberof DataTable#oApi
1981 */
1982 function _fnColumnOptions( oSettings, iCol, oOptions )
1983 {
1984 var oCol = oSettings.aoColumns[ iCol ];
1985 var oClasses = oSettings.oClasses;
1986 var th = $(oCol.nTh);
1987
1988 // Try to get width information from the DOM. We can't get it from CSS
1989 // as we'd need to parse the CSS stylesheet. `width` option can override
1990 if ( ! oCol.sWidthOrig ) {
1991 // Width attribute
1992 oCol.sWidthOrig = th.attr('width') || null;
1993
1994 // Style attribute
1995 var t = (th.attr('style') || '').match(/width:\s*(\d+[pxem%]+)/);
1996 if ( t ) {
1997 oCol.sWidthOrig = t[1];
1998 }
1999 }
2000
2001 /* User specified column options */
2002 if ( oOptions !== undefined && oOptions !== null )
2003 {
2004 // Backwards compatibility
2005 _fnCompatCols( oOptions );
2006
2007 // Map camel case parameters to their Hungarian counterparts
2008 _fnCamelToHungarian( DataTable.defaults.column, oOptions );
2009
2010 /* Backwards compatibility for mDataProp */
2011 if ( oOptions.mDataProp !== undefined && !oOptions.mData )
2012 {
2013 oOptions.mData = oOptions.mDataProp;
2014 }
2015
2016 if ( oOptions.sType )
2017 {
2018 oCol._sManualType = oOptions.sType;
2019 }
2020
2021 // `class` is a reserved word in Javascript, so we need to provide
2022 // the ability to use a valid name for the camel case input
2023 if ( oOptions.className && ! oOptions.sClass )
2024 {
2025 oOptions.sClass = oOptions.className;
2026 }
2027
2028 $.extend( oCol, oOptions );
2029 _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" );
2030
2031 /* iDataSort to be applied (backwards compatibility), but aDataSort will take
2032 * priority if defined
2033 */
2034 if ( oOptions.iDataSort !== undefined )
2035 {
2036 oCol.aDataSort = [ oOptions.iDataSort ];
2037 }
2038 _fnMap( oCol, oOptions, "aDataSort" );
2039 }
2040
2041 /* Cache the data get and set functions for speed */
2042 var mDataSrc = oCol.mData;
2043 var mData = _fnGetObjectDataFn( mDataSrc );
2044 var mRender = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null;
2045
2046 var attrTest = function( src ) {
2047 return typeof src === 'string' && src.indexOf('@') !== -1;
2048 };
2049 oCol._bAttrSrc = $.isPlainObject( mDataSrc ) && (
2050 attrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter)
2051 );
2052 oCol._setter = null;
2053
2054 oCol.fnGetData = function (rowData, type, meta) {
2055 var innerData = mData( rowData, type, undefined, meta );
2056
2057 return mRender && type ?
2058 mRender( innerData, type, rowData, meta ) :
2059 innerData;
2060 };
2061 oCol.fnSetData = function ( rowData, val, meta ) {
2062 return _fnSetObjectDataFn( mDataSrc )( rowData, val, meta );
2063 };
2064
2065 // Indicate if DataTables should read DOM data as an object or array
2066 // Used in _fnGetRowElements
2067 if ( typeof mDataSrc !== 'number' ) {
2068 oSettings._rowReadObject = true;
2069 }
2070
2071 /* Feature sorting overrides column specific when off */
2072 if ( !oSettings.oFeatures.bSort )
2073 {
2074 oCol.bSortable = false;
2075 th.addClass( oClasses.sSortableNone ); // Have to add class here as order event isn't called
2076 }
2077
2078 /* Check that the class assignment is correct for sorting */
2079 var bAsc = $.inArray('asc', oCol.asSorting) !== -1;
2080 var bDesc = $.inArray('desc', oCol.asSorting) !== -1;
2081 if ( !oCol.bSortable || (!bAsc && !bDesc) )
2082 {
2083 oCol.sSortingClass = oClasses.sSortableNone;
2084 oCol.sSortingClassJUI = "";
2085 }
2086 else if ( bAsc && !bDesc )
2087 {
2088 oCol.sSortingClass = oClasses.sSortableAsc;
2089 oCol.sSortingClassJUI = oClasses.sSortJUIAscAllowed;
2090 }
2091 else if ( !bAsc && bDesc )
2092 {
2093 oCol.sSortingClass = oClasses.sSortableDesc;
2094 oCol.sSortingClassJUI = oClasses.sSortJUIDescAllowed;
2095 }
2096 else
2097 {
2098 oCol.sSortingClass = oClasses.sSortable;
2099 oCol.sSortingClassJUI = oClasses.sSortJUI;
2100 }
2101 }
2102
2103
2104 /**
2105 * Adjust the table column widths for new data. Note: you would probably want to
2106 * do a redraw after calling this function!
2107 * @param {object} settings dataTables settings object
2108 * @memberof DataTable#oApi
2109 */
2110 function _fnAdjustColumnSizing ( settings )
2111 {
2112 /* Not interested in doing column width calculation if auto-width is disabled */
2113 if ( settings.oFeatures.bAutoWidth !== false )
2114 {
2115 var columns = settings.aoColumns;
2116
2117 _fnCalculateColumnWidths( settings );
2118 for ( var i=0 , iLen=columns.length ; i<iLen ; i++ )
2119 {
2120 columns[i].nTh.style.width = columns[i].sWidth;
2121 }
2122 }
2123
2124 var scroll = settings.oScroll;
2125 if ( scroll.sY !== '' || scroll.sX !== '')
2126 {
2127 _fnScrollDraw( settings );
2128 }
2129
2130 _fnCallbackFire( settings, null, 'column-sizing', [settings] );
2131 }
2132
2133
2134 /**
2135 * Covert the index of a visible column to the index in the data array (take account
2136 * of hidden columns)
2137 * @param {object} oSettings dataTables settings object
2138 * @param {int} iMatch Visible column index to lookup
2139 * @returns {int} i the data index
2140 * @memberof DataTable#oApi
2141 */
2142 function _fnVisibleToColumnIndex( oSettings, iMatch )
2143 {
2144 var aiVis = _fnGetColumns( oSettings, 'bVisible' );
2145
2146 return typeof aiVis[iMatch] === 'number' ?
2147 aiVis[iMatch] :
2148 null;
2149 }
2150
2151
2152 /**
2153 * Covert the index of an index in the data array and convert it to the visible
2154 * column index (take account of hidden columns)
2155 * @param {int} iMatch Column index to lookup
2156 * @param {object} oSettings dataTables settings object
2157 * @returns {int} i the data index
2158 * @memberof DataTable#oApi
2159 */
2160 function _fnColumnIndexToVisible( oSettings, iMatch )
2161 {
2162 var aiVis = _fnGetColumns( oSettings, 'bVisible' );
2163 var iPos = $.inArray( iMatch, aiVis );
2164
2165 return iPos !== -1 ? iPos : null;
2166 }
2167
2168
2169 /**
2170 * Get the number of visible columns
2171 * @param {object} oSettings dataTables settings object
2172 * @returns {int} i the number of visible columns
2173 * @memberof DataTable#oApi
2174 */
2175 function _fnVisbleColumns( oSettings )
2176 {
2177 var vis = 0;
2178
2179 // No reduce in IE8, use a loop for now
2180 $.each( oSettings.aoColumns, function ( i, col ) {
2181 if ( col.bVisible && $(col.nTh).css('display') !== 'none' ) {
2182 vis++;
2183 }
2184 } );
2185
2186 return vis;
2187 }
2188
2189
2190 /**
2191 * Get an array of column indexes that match a given property
2192 * @param {object} oSettings dataTables settings object
2193 * @param {string} sParam Parameter in aoColumns to look for - typically
2194 * bVisible or bSearchable
2195 * @returns {array} Array of indexes with matched properties
2196 * @memberof DataTable#oApi
2197 */
2198 function _fnGetColumns( oSettings, sParam )
2199 {
2200 var a = [];
2201
2202 $.map( oSettings.aoColumns, function(val, i) {
2203 if ( val[sParam] ) {
2204 a.push( i );
2205 }
2206 } );
2207
2208 return a;
2209 }
2210
2211
2212 /**
2213 * Calculate the 'type' of a column
2214 * @param {object} settings dataTables settings object
2215 * @memberof DataTable#oApi
2216 */
2217 function _fnColumnTypes ( settings )
2218 {
2219 var columns = settings.aoColumns;
2220 var data = settings.aoData;
2221 var types = DataTable.ext.type.detect;
2222 var i, ien, j, jen, k, ken;
2223 var col, cell, detectedType, cache;
2224
2225 // For each column, spin over the
2226 for ( i=0, ien=columns.length ; i<ien ; i++ ) {
2227 col = columns[i];
2228 cache = [];
2229
2230 if ( ! col.sType && col._sManualType ) {
2231 col.sType = col._sManualType;
2232 }
2233 else if ( ! col.sType ) {
2234 for ( j=0, jen=types.length ; j<jen ; j++ ) {
2235 for ( k=0, ken=data.length ; k<ken ; k++ ) {
2236 // Use a cache array so we only need to get the type data
2237 // from the formatter once (when using multiple detectors)
2238 if ( cache[k] === undefined ) {
2239 cache[k] = _fnGetCellData( settings, k, i, 'type' );
2240 }
2241
2242 detectedType = types[j]( cache[k], settings );
2243
2244 // If null, then this type can't apply to this column, so
2245 // rather than testing all cells, break out. There is an
2246 // exception for the last type which is `html`. We need to
2247 // scan all rows since it is possible to mix string and HTML
2248 // types
2249 if ( ! detectedType && j !== types.length-1 ) {
2250 break;
2251 }
2252
2253 // Only a single match is needed for html type since it is
2254 // bottom of the pile and very similar to string
2255 if ( detectedType === 'html' ) {
2256 break;
2257 }
2258 }
2259
2260 // Type is valid for all data points in the column - use this
2261 // type
2262 if ( detectedType ) {
2263 col.sType = detectedType;
2264 break;
2265 }
2266 }
2267
2268 // Fall back - if no type was detected, always use string
2269 if ( ! col.sType ) {
2270 col.sType = 'string';
2271 }
2272 }
2273 }
2274 }
2275
2276
2277 /**
2278 * Take the column definitions and static columns arrays and calculate how
2279 * they relate to column indexes. The callback function will then apply the
2280 * definition found for a column to a suitable configuration object.
2281 * @param {object} oSettings dataTables settings object
2282 * @param {array} aoColDefs The aoColumnDefs array that is to be applied
2283 * @param {array} aoCols The aoColumns array that defines columns individually
2284 * @param {function} fn Callback function - takes two parameters, the calculated
2285 * column index and the definition for that column.
2286 * @memberof DataTable#oApi
2287 */
2288 function _fnApplyColumnDefs( oSettings, aoColDefs, aoCols, fn )
2289 {
2290 var i, iLen, j, jLen, k, kLen, def;
2291 var columns = oSettings.aoColumns;
2292
2293 // Column definitions with aTargets
2294 if ( aoColDefs )
2295 {
2296 /* Loop over the definitions array - loop in reverse so first instance has priority */
2297 for ( i=aoColDefs.length-1 ; i>=0 ; i-- )
2298 {
2299 def = aoColDefs[i];
2300
2301 /* Each definition can target multiple columns, as it is an array */
2302 var aTargets = def.targets !== undefined ?
2303 def.targets :
2304 def.aTargets;
2305
2306 if ( ! $.isArray( aTargets ) )
2307 {
2308 aTargets = [ aTargets ];
2309 }
2310
2311 for ( j=0, jLen=aTargets.length ; j<jLen ; j++ )
2312 {
2313 if ( typeof aTargets[j] === 'number' && aTargets[j] >= 0 )
2314 {
2315 /* Add columns that we don't yet know about */
2316 while( columns.length <= aTargets[j] )
2317 {
2318 _fnAddColumn( oSettings );
2319 }
2320
2321 /* Integer, basic index */
2322 fn( aTargets[j], def );
2323 }
2324 else if ( typeof aTargets[j] === 'number' && aTargets[j] < 0 )
2325 {
2326 /* Negative integer, right to left column counting */
2327 fn( columns.length+aTargets[j], def );
2328 }
2329 else if ( typeof aTargets[j] === 'string' )
2330 {
2331 /* Class name matching on TH element */
2332 for ( k=0, kLen=columns.length ; k<kLen ; k++ )
2333 {
2334 if ( aTargets[j] == "_all" ||
2335 $(columns[k].nTh).hasClass( aTargets[j] ) )
2336 {
2337 fn( k, def );
2338 }
2339 }
2340 }
2341 }
2342 }
2343 }
2344
2345 // Statically defined columns array
2346 if ( aoCols )
2347 {
2348 for ( i=0, iLen=aoCols.length ; i<iLen ; i++ )
2349 {
2350 fn( i, aoCols[i] );
2351 }
2352 }
2353 }
2354
2355 /**
2356 * Add a data array to the table, creating DOM node etc. This is the parallel to
2357 * _fnGatherData, but for adding rows from a Javascript source, rather than a
2358 * DOM source.
2359 * @param {object} oSettings dataTables settings object
2360 * @param {array} aData data array to be added
2361 * @param {node} [nTr] TR element to add to the table - optional. If not given,
2362 * DataTables will create a row automatically
2363 * @param {array} [anTds] Array of TD|TH elements for the row - must be given
2364 * if nTr is.
2365 * @returns {int} >=0 if successful (index of new aoData entry), -1 if failed
2366 * @memberof DataTable#oApi
2367 */
2368 function _fnAddData ( oSettings, aDataIn, nTr, anTds )
2369 {
2370 /* Create the object for storing information about this new row */
2371 var iRow = oSettings.aoData.length;
2372 var oData = $.extend( true, {}, DataTable.models.oRow, {
2373 src: nTr ? 'dom' : 'data',
2374 idx: iRow
2375 } );
2376
2377 oData._aData = aDataIn;
2378 oSettings.aoData.push( oData );
2379
2380 /* Create the cells */
2381 var nTd, sThisType;
2382 var columns = oSettings.aoColumns;
2383
2384 // Invalidate the column types as the new data needs to be revalidated
2385 for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
2386 {
2387 columns[i].sType = null;
2388 }
2389
2390 /* Add to the display array */
2391 oSettings.aiDisplayMaster.push( iRow );
2392
2393 var id = oSettings.rowIdFn( aDataIn );
2394 if ( id !== undefined ) {
2395 oSettings.aIds[ id ] = oData;
2396 }
2397
2398 /* Create the DOM information, or register it if already present */
2399 if ( nTr || ! oSettings.oFeatures.bDeferRender )
2400 {
2401 _fnCreateTr( oSettings, iRow, nTr, anTds );
2402 }
2403
2404 return iRow;
2405 }
2406
2407
2408 /**
2409 * Add one or more TR elements to the table. Generally we'd expect to
2410 * use this for reading data from a DOM sourced table, but it could be
2411 * used for an TR element. Note that if a TR is given, it is used (i.e.
2412 * it is not cloned).
2413 * @param {object} settings dataTables settings object
2414 * @param {array|node|jQuery} trs The TR element(s) to add to the table
2415 * @returns {array} Array of indexes for the added rows
2416 * @memberof DataTable#oApi
2417 */
2418 function _fnAddTr( settings, trs )
2419 {
2420 var row;
2421
2422 // Allow an individual node to be passed in
2423 if ( ! (trs instanceof $) ) {
2424 trs = $(trs);
2425 }
2426
2427 return trs.map( function (i, el) {
2428 row = _fnGetRowElements( settings, el );
2429 return _fnAddData( settings, row.data, el, row.cells );
2430 } );
2431 }
2432
2433
2434 /**
2435 * Take a TR element and convert it to an index in aoData
2436 * @param {object} oSettings dataTables settings object
2437 * @param {node} n the TR element to find
2438 * @returns {int} index if the node is found, null if not
2439 * @memberof DataTable#oApi
2440 */
2441 function _fnNodeToDataIndex( oSettings, n )
2442 {
2443 return (n._DT_RowIndex!==undefined) ? n._DT_RowIndex : null;
2444 }
2445
2446
2447 /**
2448 * Take a TD element and convert it into a column data index (not the visible index)
2449 * @param {object} oSettings dataTables settings object
2450 * @param {int} iRow The row number the TD/TH can be found in
2451 * @param {node} n The TD/TH element to find
2452 * @returns {int} index if the node is found, -1 if not
2453 * @memberof DataTable#oApi
2454 */
2455 function _fnNodeToColumnIndex( oSettings, iRow, n )
2456 {
2457 return $.inArray( n, oSettings.aoData[ iRow ].anCells );
2458 }
2459
2460
2461 /**
2462 * Get the data for a given cell from the internal cache, taking into account data mapping
2463 * @param {object} settings dataTables settings object
2464 * @param {int} rowIdx aoData row id
2465 * @param {int} colIdx Column index
2466 * @param {string} type data get type ('display', 'type' 'filter' 'sort')
2467 * @returns {*} Cell data
2468 * @memberof DataTable#oApi
2469 */
2470 function _fnGetCellData( settings, rowIdx, colIdx, type )
2471 {
2472 var draw = settings.iDraw;
2473 var col = settings.aoColumns[colIdx];
2474 var rowData = settings.aoData[rowIdx]._aData;
2475 var defaultContent = col.sDefaultContent;
2476 var cellData = col.fnGetData( rowData, type, {
2477 settings: settings,
2478 row: rowIdx,
2479 col: colIdx
2480 } );
2481
2482 if ( cellData === undefined ) {
2483 if ( settings.iDrawError != draw && defaultContent === null ) {
2484 _fnLog( settings, 0, "Requested unknown parameter "+
2485 (typeof col.mData=='function' ? '{function}' : "'"+col.mData+"'")+
2486 " for row "+rowIdx+", column "+colIdx, 4 );
2487 settings.iDrawError = draw;
2488 }
2489 return defaultContent;
2490 }
2491
2492 // When the data source is null and a specific data type is requested (i.e.
2493 // not the original data), we can use default column data
2494 if ( (cellData === rowData || cellData === null) && defaultContent !== null && type !== undefined ) {
2495 cellData = defaultContent;
2496 }
2497 else if ( typeof cellData === 'function' ) {
2498 // If the data source is a function, then we run it and use the return,
2499 // executing in the scope of the data object (for instances)
2500 return cellData.call( rowData );
2501 }
2502
2503 if ( cellData === null && type == 'display' ) {
2504 return '';
2505 }
2506 return cellData;
2507 }
2508
2509
2510 /**
2511 * Set the value for a specific cell, into the internal data cache
2512 * @param {object} settings dataTables settings object
2513 * @param {int} rowIdx aoData row id
2514 * @param {int} colIdx Column index
2515 * @param {*} val Value to set
2516 * @memberof DataTable#oApi
2517 */
2518 function _fnSetCellData( settings, rowIdx, colIdx, val )
2519 {
2520 var col = settings.aoColumns[colIdx];
2521 var rowData = settings.aoData[rowIdx]._aData;
2522
2523 col.fnSetData( rowData, val, {
2524 settings: settings,
2525 row: rowIdx,
2526 col: colIdx
2527 } );
2528 }
2529
2530
2531 // Private variable that is used to match action syntax in the data property object
2532 var __reArray = /\[.*?\]$/;
2533 var __reFn = /\(\)$/;
2534
2535 /**
2536 * Split string on periods, taking into account escaped periods
2537 * @param {string} str String to split
2538 * @return {array} Split string
2539 */
2540 function _fnSplitObjNotation( str )
2541 {
2542 return $.map( str.match(/(\\.|[^\.])+/g) || [''], function ( s ) {
2543 return s.replace(/\\\./g, '.');
2544 } );
2545 }
2546
2547
2548 /**
2549 * Return a function that can be used to get data from a source object, taking
2550 * into account the ability to use nested objects as a source
2551 * @param {string|int|function} mSource The data source for the object
2552 * @returns {function} Data get function
2553 * @memberof DataTable#oApi
2554 */
2555 function _fnGetObjectDataFn( mSource )
2556 {
2557 if ( $.isPlainObject( mSource ) )
2558 {
2559 /* Build an object of get functions, and wrap them in a single call */
2560 var o = {};
2561 $.each( mSource, function (key, val) {
2562 if ( val ) {
2563 o[key] = _fnGetObjectDataFn( val );
2564 }
2565 } );
2566
2567 return function (data, type, row, meta) {
2568 var t = o[type] || o._;
2569 return t !== undefined ?
2570 t(data, type, row, meta) :
2571 data;
2572 };
2573 }
2574 else if ( mSource === null )
2575 {
2576 /* Give an empty string for rendering / sorting etc */
2577 return function (data) { // type, row and meta also passed, but not used
2578 return data;
2579 };
2580 }
2581 else if ( typeof mSource === 'function' )
2582 {
2583 return function (data, type, row, meta) {
2584 return mSource( data, type, row, meta );
2585 };
2586 }
2587 else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 ||
2588 mSource.indexOf('[') !== -1 || mSource.indexOf('(') !== -1) )
2589 {
2590 /* If there is a . in the source string then the data source is in a
2591 * nested object so we loop over the data for each level to get the next
2592 * level down. On each loop we test for undefined, and if found immediately
2593 * return. This allows entire objects to be missing and sDefaultContent to
2594 * be used if defined, rather than throwing an error
2595 */
2596 var fetchData = function (data, type, src) {
2597 var arrayNotation, funcNotation, out, innerSrc;
2598
2599 if ( src !== "" )
2600 {
2601 var a = _fnSplitObjNotation( src );
2602
2603 for ( var i=0, iLen=a.length ; i<iLen ; i++ )
2604 {
2605 // Check if we are dealing with special notation
2606 arrayNotation = a[i].match(__reArray);
2607 funcNotation = a[i].match(__reFn);
2608
2609 if ( arrayNotation )
2610 {
2611 // Array notation
2612 a[i] = a[i].replace(__reArray, '');
2613
2614 // Condition allows simply [] to be passed in
2615 if ( a[i] !== "" ) {
2616 data = data[ a[i] ];
2617 }
2618 out = [];
2619
2620 // Get the remainder of the nested object to get
2621 a.splice( 0, i+1 );
2622 innerSrc = a.join('.');
2623
2624 // Traverse each entry in the array getting the properties requested
2625 if ( $.isArray( data ) ) {
2626 for ( var j=0, jLen=data.length ; j<jLen ; j++ ) {
2627 out.push( fetchData( data[j], type, innerSrc ) );
2628 }
2629 }
2630
2631 // If a string is given in between the array notation indicators, that
2632 // is used to join the strings together, otherwise an array is returned
2633 var join = arrayNotation[0].substring(1, arrayNotation[0].length-1);
2634 data = (join==="") ? out : out.join(join);
2635
2636 // The inner call to fetchData has already traversed through the remainder
2637 // of the source requested, so we exit from the loop
2638 break;
2639 }
2640 else if ( funcNotation )
2641 {
2642 // Function call
2643 a[i] = a[i].replace(__reFn, '');
2644 data = data[ a[i] ]();
2645 continue;
2646 }
2647
2648 if ( data === null || data[ a[i] ] === undefined )
2649 {
2650 return undefined;
2651 }
2652 data = data[ a[i] ];
2653 }
2654 }
2655
2656 return data;
2657 };
2658
2659 return function (data, type) { // row and meta also passed, but not used
2660 return fetchData( data, type, mSource );
2661 };
2662 }
2663 else
2664 {
2665 /* Array or flat object mapping */
2666 return function (data, type) { // row and meta also passed, but not used
2667 return data[mSource];
2668 };
2669 }
2670 }
2671
2672
2673 /**
2674 * Return a function that can be used to set data from a source object, taking
2675 * into account the ability to use nested objects as a source
2676 * @param {string|int|function} mSource The data source for the object
2677 * @returns {function} Data set function
2678 * @memberof DataTable#oApi
2679 */
2680 function _fnSetObjectDataFn( mSource )
2681 {
2682 if ( $.isPlainObject( mSource ) )
2683 {
2684 /* Unlike get, only the underscore (global) option is used for for
2685 * setting data since we don't know the type here. This is why an object
2686 * option is not documented for `mData` (which is read/write), but it is
2687 * for `mRender` which is read only.
2688 */
2689 return _fnSetObjectDataFn( mSource._ );
2690 }
2691 else if ( mSource === null )
2692 {
2693 /* Nothing to do when the data source is null */
2694 return function () {};
2695 }
2696 else if ( typeof mSource === 'function' )
2697 {
2698 return function (data, val, meta) {
2699 mSource( data, 'set', val, meta );
2700 };
2701 }
2702 else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 ||
2703 mSource.indexOf('[') !== -1 || mSource.indexOf('(') !== -1) )
2704 {
2705 /* Like the get, we need to get data from a nested object */
2706 var setData = function (data, val, src) {
2707 var a = _fnSplitObjNotation( src ), b;
2708 var aLast = a[a.length-1];
2709 var arrayNotation, funcNotation, o, innerSrc;
2710
2711 for ( var i=0, iLen=a.length-1 ; i<iLen ; i++ )
2712 {
2713 // Check if we are dealing with an array notation request
2714 arrayNotation = a[i].match(__reArray);
2715 funcNotation = a[i].match(__reFn);
2716
2717 if ( arrayNotation )
2718 {
2719 a[i] = a[i].replace(__reArray, '');
2720 data[ a[i] ] = [];
2721
2722 // Get the remainder of the nested object to set so we can recurse
2723 b = a.slice();
2724 b.splice( 0, i+1 );
2725 innerSrc = b.join('.');
2726
2727 // Traverse each entry in the array setting the properties requested
2728 if ( $.isArray( val ) )
2729 {
2730 for ( var j=0, jLen=val.length ; j<jLen ; j++ )
2731 {
2732 o = {};
2733 setData( o, val[j], innerSrc );
2734 data[ a[i] ].push( o );
2735 }
2736 }
2737 else
2738 {
2739 // We've been asked to save data to an array, but it
2740 // isn't array data to be saved. Best that can be done
2741 // is to just save the value.
2742 data[ a[i] ] = val;
2743 }
2744
2745 // The inner call to setData has already traversed through the remainder
2746 // of the source and has set the data, thus we can exit here
2747 return;
2748 }
2749 else if ( funcNotation )
2750 {
2751 // Function call
2752 a[i] = a[i].replace(__reFn, '');
2753 data = data[ a[i] ]( val );
2754 }
2755
2756 // If the nested object doesn't currently exist - since we are
2757 // trying to set the value - create it
2758 if ( data[ a[i] ] === null || data[ a[i] ] === undefined )
2759 {
2760 data[ a[i] ] = {};
2761 }
2762 data = data[ a[i] ];
2763 }
2764
2765 // Last item in the input - i.e, the actual set
2766 if ( aLast.match(__reFn ) )
2767 {
2768 // Function call
2769 data = data[ aLast.replace(__reFn, '') ]( val );
2770 }
2771 else
2772 {
2773 // If array notation is used, we just want to strip it and use the property name
2774 // and assign the value. If it isn't used, then we get the result we want anyway
2775 data[ aLast.replace(__reArray, '') ] = val;
2776 }
2777 };
2778
2779 return function (data, val) { // meta is also passed in, but not used
2780 return setData( data, val, mSource );
2781 };
2782 }
2783 else
2784 {
2785 /* Array or flat object mapping */
2786 return function (data, val) { // meta is also passed in, but not used
2787 data[mSource] = val;
2788 };
2789 }
2790 }
2791
2792
2793 /**
2794 * Return an array with the full table data
2795 * @param {object} oSettings dataTables settings object
2796 * @returns array {array} aData Master data array
2797 * @memberof DataTable#oApi
2798 */
2799 function _fnGetDataMaster ( settings )
2800 {
2801 return _pluck( settings.aoData, '_aData' );
2802 }
2803
2804
2805 /**
2806 * Nuke the table
2807 * @param {object} oSettings dataTables settings object
2808 * @memberof DataTable#oApi
2809 */
2810 function _fnClearTable( settings )
2811 {
2812 settings.aoData.length = 0;
2813 settings.aiDisplayMaster.length = 0;
2814 settings.aiDisplay.length = 0;
2815 settings.aIds = {};
2816 }
2817
2818
2819 /**
2820 * Take an array of integers (index array) and remove a target integer (value - not
2821 * the key!)
2822 * @param {array} a Index array to target
2823 * @param {int} iTarget value to find
2824 * @memberof DataTable#oApi
2825 */
2826 function _fnDeleteIndex( a, iTarget, splice )
2827 {
2828 var iTargetIndex = -1;
2829
2830 for ( var i=0, iLen=a.length ; i<iLen ; i++ )
2831 {
2832 if ( a[i] == iTarget )
2833 {
2834 iTargetIndex = i;
2835 }
2836 else if ( a[i] > iTarget )
2837 {
2838 a[i]--;
2839 }
2840 }
2841
2842 if ( iTargetIndex != -1 && splice === undefined )
2843 {
2844 a.splice( iTargetIndex, 1 );
2845 }
2846 }
2847
2848
2849 /**
2850 * Mark cached data as invalid such that a re-read of the data will occur when
2851 * the cached data is next requested. Also update from the data source object.
2852 *
2853 * @param {object} settings DataTables settings object
2854 * @param {int} rowIdx Row index to invalidate
2855 * @param {string} [src] Source to invalidate from: undefined, 'auto', 'dom'
2856 * or 'data'
2857 * @param {int} [colIdx] Column index to invalidate. If undefined the whole
2858 * row will be invalidated
2859 * @memberof DataTable#oApi
2860 *
2861 * @todo For the modularisation of v1.11 this will need to become a callback, so
2862 * the sort and filter methods can subscribe to it. That will required
2863 * initialisation options for sorting, which is why it is not already baked in
2864 */
2865 function _fnInvalidate( settings, rowIdx, src, colIdx )
2866 {
2867 var row = settings.aoData[ rowIdx ];
2868 var i, ien;
2869 var cellWrite = function ( cell, col ) {
2870 // This is very frustrating, but in IE if you just write directly
2871 // to innerHTML, and elements that are overwritten are GC'ed,
2872 // even if there is a reference to them elsewhere
2873 while ( cell.childNodes.length ) {
2874 cell.removeChild( cell.firstChild );
2875 }
2876
2877 cell.innerHTML = _fnGetCellData( settings, rowIdx, col, 'display' );
2878 };
2879
2880 // Are we reading last data from DOM or the data object?
2881 if ( src === 'dom' || ((! src || src === 'auto') && row.src === 'dom') ) {
2882 // Read the data from the DOM
2883 row._aData = _fnGetRowElements(
2884 settings, row, colIdx, colIdx === undefined ? undefined : row._aData
2885 )
2886 .data;
2887 }
2888 else {
2889 // Reading from data object, update the DOM
2890 var cells = row.anCells;
2891
2892 if ( cells ) {
2893 if ( colIdx !== undefined ) {
2894 cellWrite( cells[colIdx], colIdx );
2895 }
2896 else {
2897 for ( i=0, ien=cells.length ; i<ien ; i++ ) {
2898 cellWrite( cells[i], i );
2899 }
2900 }
2901 }
2902 }
2903
2904 // For both row and cell invalidation, the cached data for sorting and
2905 // filtering is nulled out
2906 row._aSortData = null;
2907 row._aFilterData = null;
2908
2909 // Invalidate the type for a specific column (if given) or all columns since
2910 // the data might have changed
2911 var cols = settings.aoColumns;
2912 if ( colIdx !== undefined ) {
2913 cols[ colIdx ].sType = null;
2914 }
2915 else {
2916 for ( i=0, ien=cols.length ; i<ien ; i++ ) {
2917 cols[i].sType = null;
2918 }
2919
2920 // Update DataTables special `DT_*` attributes for the row
2921 _fnRowAttributes( settings, row );
2922 }
2923 }
2924
2925
2926 /**
2927 * Build a data source object from an HTML row, reading the contents of the
2928 * cells that are in the row.
2929 *
2930 * @param {object} settings DataTables settings object
2931 * @param {node|object} TR element from which to read data or existing row
2932 * object from which to re-read the data from the cells
2933 * @param {int} [colIdx] Optional column index
2934 * @param {array|object} [d] Data source object. If `colIdx` is given then this
2935 * parameter should also be given and will be used to write the data into.
2936 * Only the column in question will be written
2937 * @returns {object} Object with two parameters: `data` the data read, in
2938 * document order, and `cells` and array of nodes (they can be useful to the
2939 * caller, so rather than needing a second traversal to get them, just return
2940 * them from here).
2941 * @memberof DataTable#oApi
2942 */
2943 function _fnGetRowElements( settings, row, colIdx, d )
2944 {
2945 var
2946 tds = [],
2947 td = row.firstChild,
2948 name, col, o, i=0, contents,
2949 columns = settings.aoColumns,
2950 objectRead = settings._rowReadObject;
2951
2952 // Allow the data object to be passed in, or construct
2953 d = d !== undefined ?
2954 d :
2955 objectRead ?
2956 {} :
2957 [];
2958
2959 var attr = function ( str, td ) {
2960 if ( typeof str === 'string' ) {
2961 var idx = str.indexOf('@');
2962
2963 if ( idx !== -1 ) {
2964 var attr = str.substring( idx+1 );
2965 var setter = _fnSetObjectDataFn( str );
2966 setter( d, td.getAttribute( attr ) );
2967 }
2968 }
2969 };
2970
2971 // Read data from a cell and store into the data object
2972 var cellProcess = function ( cell ) {
2973 if ( colIdx === undefined || colIdx === i ) {
2974 col = columns[i];
2975 contents = $.trim(cell.innerHTML);
2976
2977 if ( col && col._bAttrSrc ) {
2978 var setter = _fnSetObjectDataFn( col.mData._ );
2979 setter( d, contents );
2980
2981 attr( col.mData.sort, cell );
2982 attr( col.mData.type, cell );
2983 attr( col.mData.filter, cell );
2984 }
2985 else {
2986 // Depending on the `data` option for the columns the data can
2987 // be read to either an object or an array.
2988 if ( objectRead ) {
2989 if ( ! col._setter ) {
2990 // Cache the setter function
2991 col._setter = _fnSetObjectDataFn( col.mData );
2992 }
2993 col._setter( d, contents );
2994 }
2995 else {
2996 d[i] = contents;
2997 }
2998 }
2999 }
3000
3001 i++;
3002 };
3003
3004 if ( td ) {
3005 // `tr` element was passed in
3006 while ( td ) {
3007 name = td.nodeName.toUpperCase();
3008
3009 if ( name == "TD" || name == "TH" ) {
3010 cellProcess( td );
3011 tds.push( td );
3012 }
3013
3014 td = td.nextSibling;
3015 }
3016 }
3017 else {
3018 // Existing row object passed in
3019 tds = row.anCells;
3020
3021 for ( var j=0, jen=tds.length ; j<jen ; j++ ) {
3022 cellProcess( tds[j] );
3023 }
3024 }
3025
3026 // Read the ID from the DOM if present
3027 var rowNode = row.firstChild ? row : row.nTr;
3028
3029 if ( rowNode ) {
3030 var id = rowNode.getAttribute( 'id' );
3031
3032 if ( id ) {
3033 _fnSetObjectDataFn( settings.rowId )( d, id );
3034 }
3035 }
3036
3037 return {
3038 data: d,
3039 cells: tds
3040 };
3041 }
3042 /**
3043 * Create a new TR element (and it's TD children) for a row
3044 * @param {object} oSettings dataTables settings object
3045 * @param {int} iRow Row to consider
3046 * @param {node} [nTrIn] TR element to add to the table - optional. If not given,
3047 * DataTables will create a row automatically
3048 * @param {array} [anTds] Array of TD|TH elements for the row - must be given
3049 * if nTr is.
3050 * @memberof DataTable#oApi
3051 */
3052 function _fnCreateTr ( oSettings, iRow, nTrIn, anTds )
3053 {
3054 var
3055 row = oSettings.aoData[iRow],
3056 rowData = row._aData,
3057 cells = [],
3058 nTr, nTd, oCol,
3059 i, iLen;
3060
3061 if ( row.nTr === null )
3062 {
3063 nTr = nTrIn || document.createElement('tr');
3064
3065 row.nTr = nTr;
3066 row.anCells = cells;
3067
3068 /* Use a private property on the node to allow reserve mapping from the node
3069 * to the aoData array for fast look up
3070 */
3071 nTr._DT_RowIndex = iRow;
3072
3073 /* Special parameters can be given by the data source to be used on the row */
3074 _fnRowAttributes( oSettings, row );
3075
3076 /* Process each column */
3077 for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
3078 {
3079 oCol = oSettings.aoColumns[i];
3080
3081 nTd = nTrIn ? anTds[i] : document.createElement( oCol.sCellType );
3082 nTd._DT_CellIndex = {
3083 row: iRow,
3084 column: i
3085 };
3086
3087 cells.push( nTd );
3088
3089 // Need to create the HTML if new, or if a rendering function is defined
3090 if ( (!nTrIn || oCol.mRender || oCol.mData !== i) &&
3091 (!$.isPlainObject(oCol.mData) || oCol.mData._ !== i+'.display')
3092 ) {
3093 nTd.innerHTML = _fnGetCellData( oSettings, iRow, i, 'display' );
3094 }
3095
3096 /* Add user defined class */
3097 if ( oCol.sClass )
3098 {
3099 nTd.className += ' '+oCol.sClass;
3100 }
3101
3102 // Visibility - add or remove as required
3103 if ( oCol.bVisible && ! nTrIn )
3104 {
3105 nTr.appendChild( nTd );
3106 }
3107 else if ( ! oCol.bVisible && nTrIn )
3108 {
3109 nTd.parentNode.removeChild( nTd );
3110 }
3111
3112 if ( oCol.fnCreatedCell )
3113 {
3114 oCol.fnCreatedCell.call( oSettings.oInstance,
3115 nTd, _fnGetCellData( oSettings, iRow, i ), rowData, iRow, i
3116 );
3117 }
3118 }
3119
3120 _fnCallbackFire( oSettings, 'aoRowCreatedCallback', null, [nTr, rowData, iRow] );
3121 }
3122
3123 // Remove once webkit bug 131819 and Chromium bug 365619 have been resolved
3124 // and deployed
3125 row.nTr.setAttribute( 'role', 'row' );
3126 }
3127
3128
3129 /**
3130 * Add attributes to a row based on the special `DT_*` parameters in a data
3131 * source object.
3132 * @param {object} settings DataTables settings object
3133 * @param {object} DataTables row object for the row to be modified
3134 * @memberof DataTable#oApi
3135 */
3136 function _fnRowAttributes( settings, row )
3137 {
3138 var tr = row.nTr;
3139 var data = row._aData;
3140
3141 if ( tr ) {
3142 var id = settings.rowIdFn( data );
3143
3144 if ( id ) {
3145 tr.id = id;
3146 }
3147
3148 if ( data.DT_RowClass ) {
3149 // Remove any classes added by DT_RowClass before
3150 var a = data.DT_RowClass.split(' ');
3151 row.__rowc = row.__rowc ?
3152 _unique( row.__rowc.concat( a ) ) :
3153 a;
3154
3155 $(tr)
3156 .removeClass( row.__rowc.join(' ') )
3157 .addClass( data.DT_RowClass );
3158 }
3159
3160 if ( data.DT_RowAttr ) {
3161 $(tr).attr( data.DT_RowAttr );
3162 }
3163
3164 if ( data.DT_RowData ) {
3165 $(tr).data( data.DT_RowData );
3166 }
3167 }
3168 }
3169
3170
3171 /**
3172 * Create the HTML header for the table
3173 * @param {object} oSettings dataTables settings object
3174 * @memberof DataTable#oApi
3175 */
3176 function _fnBuildHead( oSettings )
3177 {
3178 var i, ien, cell, row, column;
3179 var thead = oSettings.nTHead;
3180 var tfoot = oSettings.nTFoot;
3181 var createHeader = $('th, td', thead).length === 0;
3182 var classes = oSettings.oClasses;
3183 var columns = oSettings.aoColumns;
3184
3185 if ( createHeader ) {
3186 row = $('<tr/>').appendTo( thead );
3187 }
3188
3189 for ( i=0, ien=columns.length ; i<ien ; i++ ) {
3190 column = columns[i];
3191 cell = $( column.nTh ).addClass( column.sClass );
3192
3193 if ( createHeader ) {
3194 cell.appendTo( row );
3195 }
3196
3197 // 1.11 move into sorting
3198 if ( oSettings.oFeatures.bSort ) {
3199 cell.addClass( column.sSortingClass );
3200
3201 if ( column.bSortable !== false ) {
3202 cell
3203 .attr( 'tabindex', oSettings.iTabIndex )
3204 .attr( 'aria-controls', oSettings.sTableId );
3205
3206 _fnSortAttachListener( oSettings, column.nTh, i );
3207 }
3208 }
3209
3210 if ( column.sTitle != cell[0].innerHTML ) {
3211 cell.html( column.sTitle );
3212 }
3213
3214 _fnRenderer( oSettings, 'header' )(
3215 oSettings, cell, column, classes
3216 );
3217 }
3218
3219 if ( createHeader ) {
3220 _fnDetectHeader( oSettings.aoHeader, thead );
3221 }
3222
3223 /* ARIA role for the rows */
3224 $(thead).find('>tr').attr('role', 'row');
3225
3226 /* Deal with the footer - add classes if required */
3227 $(thead).find('>tr>th, >tr>td').addClass( classes.sHeaderTH );
3228 $(tfoot).find('>tr>th, >tr>td').addClass( classes.sFooterTH );
3229
3230 // Cache the footer cells. Note that we only take the cells from the first
3231 // row in the footer. If there is more than one row the user wants to
3232 // interact with, they need to use the table().foot() method. Note also this
3233 // allows cells to be used for multiple columns using colspan
3234 if ( tfoot !== null ) {
3235 var cells = oSettings.aoFooter[0];
3236
3237 for ( i=0, ien=cells.length ; i<ien ; i++ ) {
3238 column = columns[i];
3239 column.nTf = cells[i].cell;
3240
3241 if ( column.sClass ) {
3242 $(column.nTf).addClass( column.sClass );
3243 }
3244 }
3245 }
3246 }
3247
3248
3249 /**
3250 * Draw the header (or footer) element based on the column visibility states. The
3251 * methodology here is to use the layout array from _fnDetectHeader, modified for
3252 * the instantaneous column visibility, to construct the new layout. The grid is
3253 * traversed over cell at a time in a rows x columns grid fashion, although each
3254 * cell insert can cover multiple elements in the grid - which is tracks using the
3255 * aApplied array. Cell inserts in the grid will only occur where there isn't
3256 * already a cell in that position.
3257 * @param {object} oSettings dataTables settings object
3258 * @param array {objects} aoSource Layout array from _fnDetectHeader
3259 * @param {boolean} [bIncludeHidden=false] If true then include the hidden columns in the calc,
3260 * @memberof DataTable#oApi
3261 */
3262 function _fnDrawHead( oSettings, aoSource, bIncludeHidden )
3263 {
3264 var i, iLen, j, jLen, k, kLen, n, nLocalTr;
3265 var aoLocal = [];
3266 var aApplied = [];
3267 var iColumns = oSettings.aoColumns.length;
3268 var iRowspan, iColspan;
3269
3270 if ( ! aoSource )
3271 {
3272 return;
3273 }
3274
3275 if ( bIncludeHidden === undefined )
3276 {
3277 bIncludeHidden = false;
3278 }
3279
3280 /* Make a copy of the master layout array, but without the visible columns in it */
3281 for ( i=0, iLen=aoSource.length ; i<iLen ; i++ )
3282 {
3283 aoLocal[i] = aoSource[i].slice();
3284 aoLocal[i].nTr = aoSource[i].nTr;
3285
3286 /* Remove any columns which are currently hidden */
3287 for ( j=iColumns-1 ; j>=0 ; j-- )
3288 {
3289 if ( !oSettings.aoColumns[j].bVisible && !bIncludeHidden )
3290 {
3291 aoLocal[i].splice( j, 1 );
3292 }
3293 }
3294
3295 /* Prep the applied array - it needs an element for each row */
3296 aApplied.push( [] );
3297 }
3298
3299 for ( i=0, iLen=aoLocal.length ; i<iLen ; i++ )
3300 {
3301 nLocalTr = aoLocal[i].nTr;
3302
3303 /* All cells are going to be replaced, so empty out the row */
3304 if ( nLocalTr )
3305 {
3306 while( (n = nLocalTr.firstChild) )
3307 {
3308 nLocalTr.removeChild( n );
3309 }
3310 }
3311
3312 for ( j=0, jLen=aoLocal[i].length ; j<jLen ; j++ )
3313 {
3314 iRowspan = 1;
3315 iColspan = 1;
3316
3317 /* Check to see if there is already a cell (row/colspan) covering our target
3318 * insert point. If there is, then there is nothing to do.
3319 */
3320 if ( aApplied[i][j] === undefined )
3321 {
3322 nLocalTr.appendChild( aoLocal[i][j].cell );
3323 aApplied[i][j] = 1;
3324
3325 /* Expand the cell to cover as many rows as needed */
3326 while ( aoLocal[i+iRowspan] !== undefined &&
3327 aoLocal[i][j].cell == aoLocal[i+iRowspan][j].cell )
3328 {
3329 aApplied[i+iRowspan][j] = 1;
3330 iRowspan++;
3331 }
3332
3333 /* Expand the cell to cover as many columns as needed */
3334 while ( aoLocal[i][j+iColspan] !== undefined &&
3335 aoLocal[i][j].cell == aoLocal[i][j+iColspan].cell )
3336 {
3337 /* Must update the applied array over the rows for the columns */
3338 for ( k=0 ; k<iRowspan ; k++ )
3339 {
3340 aApplied[i+k][j+iColspan] = 1;
3341 }
3342 iColspan++;
3343 }
3344
3345 /* Do the actual expansion in the DOM */
3346 $(aoLocal[i][j].cell)
3347 .attr('rowspan', iRowspan)
3348 .attr('colspan', iColspan);
3349 }
3350 }
3351 }
3352 }
3353
3354
3355 /**
3356 * Insert the required TR nodes into the table for display
3357 * @param {object} oSettings dataTables settings object
3358 * @memberof DataTable#oApi
3359 */
3360 function _fnDraw( oSettings )
3361 {
3362 /* Provide a pre-callback function which can be used to cancel the draw is false is returned */
3363 var aPreDraw = _fnCallbackFire( oSettings, 'aoPreDrawCallback', 'preDraw', [oSettings] );
3364 if ( $.inArray( false, aPreDraw ) !== -1 )
3365 {
3366 _fnProcessingDisplay( oSettings, false );
3367 return;
3368 }
3369
3370 var i, iLen, n;
3371 var anRows = [];
3372 var iRowCount = 0;
3373 var asStripeClasses = oSettings.asStripeClasses;
3374 var iStripes = asStripeClasses.length;
3375 var iOpenRows = oSettings.aoOpenRows.length;
3376 var oLang = oSettings.oLanguage;
3377 var iInitDisplayStart = oSettings.iInitDisplayStart;
3378 var bServerSide = _fnDataSource( oSettings ) == 'ssp';
3379 var aiDisplay = oSettings.aiDisplay;
3380
3381 oSettings.bDrawing = true;
3382
3383 /* Check and see if we have an initial draw position from state saving */
3384 if ( iInitDisplayStart !== undefined && iInitDisplayStart !== -1 )
3385 {
3386 oSettings._iDisplayStart = bServerSide ?
3387 iInitDisplayStart :
3388 iInitDisplayStart >= oSettings.fnRecordsDisplay() ?
3389 0 :
3390 iInitDisplayStart;
3391
3392 oSettings.iInitDisplayStart = -1;
3393 }
3394
3395 var iDisplayStart = oSettings._iDisplayStart;
3396 var iDisplayEnd = oSettings.fnDisplayEnd();
3397
3398 /* Server-side processing draw intercept */
3399 if ( oSettings.bDeferLoading )
3400 {
3401 oSettings.bDeferLoading = false;
3402 oSettings.iDraw++;
3403 _fnProcessingDisplay( oSettings, false );
3404 }
3405 else if ( !bServerSide )
3406 {
3407 oSettings.iDraw++;
3408 }
3409 else if ( !oSettings.bDestroying && !_fnAjaxUpdate( oSettings ) )
3410 {
3411 return;
3412 }
3413
3414 if ( aiDisplay.length !== 0 )
3415 {
3416 var iStart = bServerSide ? 0 : iDisplayStart;
3417 var iEnd = bServerSide ? oSettings.aoData.length : iDisplayEnd;
3418
3419 for ( var j=iStart ; j<iEnd ; j++ )
3420 {
3421 var iDataIndex = aiDisplay[j];
3422 var aoData = oSettings.aoData[ iDataIndex ];
3423 if ( aoData.nTr === null )
3424 {
3425 _fnCreateTr( oSettings, iDataIndex );
3426 }
3427
3428 var nRow = aoData.nTr;
3429
3430 /* Remove the old striping classes and then add the new one */
3431 if ( iStripes !== 0 )
3432 {
3433 var sStripe = asStripeClasses[ iRowCount % iStripes ];
3434 if ( aoData._sRowStripe != sStripe )
3435 {
3436 $(nRow).removeClass( aoData._sRowStripe ).addClass( sStripe );
3437 aoData._sRowStripe = sStripe;
3438 }
3439 }
3440
3441 // Row callback functions - might want to manipulate the row
3442 // iRowCount and j are not currently documented. Are they at all
3443 // useful?
3444 _fnCallbackFire( oSettings, 'aoRowCallback', null,
3445 [nRow, aoData._aData, iRowCount, j] );
3446
3447 anRows.push( nRow );
3448 iRowCount++;
3449 }
3450 }
3451 else
3452 {
3453 /* Table is empty - create a row with an empty message in it */
3454 var sZero = oLang.sZeroRecords;
3455 if ( oSettings.iDraw == 1 && _fnDataSource( oSettings ) == 'ajax' )
3456 {
3457 sZero = oLang.sLoadingRecords;
3458 }
3459 else if ( oLang.sEmptyTable && oSettings.fnRecordsTotal() === 0 )
3460 {
3461 sZero = oLang.sEmptyTable;
3462 }
3463
3464 anRows[ 0 ] = $( '<tr/>', { 'class': iStripes ? asStripeClasses[0] : '' } )
3465 .append( $('<td />', {
3466 'valign': 'top',
3467 'colSpan': _fnVisbleColumns( oSettings ),
3468 'class': oSettings.oClasses.sRowEmpty
3469 } ).html( sZero ) )[0];
3470 }
3471
3472 /* Header and footer callbacks */
3473 _fnCallbackFire( oSettings, 'aoHeaderCallback', 'header', [ $(oSettings.nTHead).children('tr')[0],
3474 _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );
3475
3476 _fnCallbackFire( oSettings, 'aoFooterCallback', 'footer', [ $(oSettings.nTFoot).children('tr')[0],
3477 _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );
3478
3479 var body = $(oSettings.nTBody);
3480
3481 body.children().detach();
3482 body.append( $(anRows) );
3483
3484 /* Call all required callback functions for the end of a draw */
3485 _fnCallbackFire( oSettings, 'aoDrawCallback', 'draw', [oSettings] );
3486
3487 /* Draw is complete, sorting and filtering must be as well */
3488 oSettings.bSorted = false;
3489 oSettings.bFiltered = false;
3490 oSettings.bDrawing = false;
3491 }
3492
3493
3494 /**
3495 * Redraw the table - taking account of the various features which are enabled
3496 * @param {object} oSettings dataTables settings object
3497 * @param {boolean} [holdPosition] Keep the current paging position. By default
3498 * the paging is reset to the first page
3499 * @memberof DataTable#oApi
3500 */
3501 function _fnReDraw( settings, holdPosition )
3502 {
3503 var
3504 features = settings.oFeatures,
3505 sort = features.bSort,
3506 filter = features.bFilter;
3507
3508 if ( sort ) {
3509 _fnSort( settings );
3510 }
3511
3512 if ( filter ) {
3513 _fnFilterComplete( settings, settings.oPreviousSearch );
3514 }
3515 else {
3516 // No filtering, so we want to just use the display master
3517 settings.aiDisplay = settings.aiDisplayMaster.slice();
3518 }
3519
3520 if ( holdPosition !== true ) {
3521 settings._iDisplayStart = 0;
3522 }
3523
3524 // Let any modules know about the draw hold position state (used by
3525 // scrolling internally)
3526 settings._drawHold = holdPosition;
3527
3528 _fnDraw( settings );
3529
3530 settings._drawHold = false;
3531 }
3532
3533
3534 /**
3535 * Add the options to the page HTML for the table
3536 * @param {object} oSettings dataTables settings object
3537 * @memberof DataTable#oApi
3538 */
3539 function _fnAddOptionsHtml ( oSettings )
3540 {
3541 var classes = oSettings.oClasses;
3542 var table = $(oSettings.nTable);
3543 var holding = $('<div/>').insertBefore( table ); // Holding element for speed
3544 var features = oSettings.oFeatures;
3545
3546 // All DataTables are wrapped in a div
3547 var insert = $('<div/>', {
3548 id: oSettings.sTableId+'_wrapper',
3549 'class': classes.sWrapper + (oSettings.nTFoot ? '' : ' '+classes.sNoFooter)
3550 } );
3551
3552 oSettings.nHolding = holding[0];
3553 oSettings.nTableWrapper = insert[0];
3554 oSettings.nTableReinsertBefore = oSettings.nTable.nextSibling;
3555
3556 /* Loop over the user set positioning and place the elements as needed */
3557 var aDom = oSettings.sDom.split('');
3558 var featureNode, cOption, nNewNode, cNext, sAttr, j;
3559 for ( var i=0 ; i<aDom.length ; i++ )
3560 {
3561 featureNode = null;
3562 cOption = aDom[i];
3563
3564 if ( cOption == '<' )
3565 {
3566 /* New container div */
3567 nNewNode = $('<div/>')[0];
3568
3569 /* Check to see if we should append an id and/or a class name to the container */
3570 cNext = aDom[i+1];
3571 if ( cNext == "'" || cNext == '"' )
3572 {
3573 sAttr = "";
3574 j = 2;
3575 while ( aDom[i+j] != cNext )
3576 {
3577 sAttr += aDom[i+j];
3578 j++;
3579 }
3580
3581 /* Replace jQuery UI constants @todo depreciated */
3582 if ( sAttr == "H" )
3583 {
3584 sAttr = classes.sJUIHeader;
3585 }
3586 else if ( sAttr == "F" )
3587 {
3588 sAttr = classes.sJUIFooter;
3589 }
3590
3591 /* The attribute can be in the format of "#id.class", "#id" or "class" This logic
3592 * breaks the string into parts and applies them as needed
3593 */
3594 if ( sAttr.indexOf('.') != -1 )
3595 {
3596 var aSplit = sAttr.split('.');
3597 nNewNode.id = aSplit[0].substr(1, aSplit[0].length-1);
3598 nNewNode.className = aSplit[1];
3599 }
3600 else if ( sAttr.charAt(0) == "#" )
3601 {
3602 nNewNode.id = sAttr.substr(1, sAttr.length-1);
3603 }
3604 else
3605 {
3606 nNewNode.className = sAttr;
3607 }
3608
3609 i += j; /* Move along the position array */
3610 }
3611
3612 insert.append( nNewNode );
3613 insert = $(nNewNode);
3614 }
3615 else if ( cOption == '>' )
3616 {
3617 /* End container div */
3618 insert = insert.parent();
3619 }
3620 // @todo Move options into their own plugins?
3621 else if ( cOption == 'l' && features.bPaginate && features.bLengthChange )
3622 {
3623 /* Length */
3624 featureNode = _fnFeatureHtmlLength( oSettings );
3625 }
3626 else if ( cOption == 'f' && features.bFilter )
3627 {
3628 /* Filter */
3629 featureNode = _fnFeatureHtmlFilter( oSettings );
3630 }
3631 else if ( cOption == 'r' && features.bProcessing )
3632 {
3633 /* pRocessing */
3634 featureNode = _fnFeatureHtmlProcessing( oSettings );
3635 }
3636 else if ( cOption == 't' )
3637 {
3638 /* Table */
3639 featureNode = _fnFeatureHtmlTable( oSettings );
3640 }
3641 else if ( cOption == 'i' && features.bInfo )
3642 {
3643 /* Info */
3644 featureNode = _fnFeatureHtmlInfo( oSettings );
3645 }
3646 else if ( cOption == 'p' && features.bPaginate )
3647 {
3648 /* Pagination */
3649 featureNode = _fnFeatureHtmlPaginate( oSettings );
3650 }
3651 else if ( DataTable.ext.feature.length !== 0 )
3652 {
3653 /* Plug-in features */
3654 var aoFeatures = DataTable.ext.feature;
3655 for ( var k=0, kLen=aoFeatures.length ; k<kLen ; k++ )
3656 {
3657 if ( cOption == aoFeatures[k].cFeature )
3658 {
3659 featureNode = aoFeatures[k].fnInit( oSettings );
3660 break;
3661 }
3662 }
3663 }
3664
3665 /* Add to the 2D features array */
3666 if ( featureNode )
3667 {
3668 var aanFeatures = oSettings.aanFeatures;
3669
3670 if ( ! aanFeatures[cOption] )
3671 {
3672 aanFeatures[cOption] = [];
3673 }
3674
3675 aanFeatures[cOption].push( featureNode );
3676 insert.append( featureNode );
3677 }
3678 }
3679
3680 /* Built our DOM structure - replace the holding div with what we want */
3681 holding.replaceWith( insert );
3682 oSettings.nHolding = null;
3683 }
3684
3685
3686 /**
3687 * Use the DOM source to create up an array of header cells. The idea here is to
3688 * create a layout grid (array) of rows x columns, which contains a reference
3689 * to the cell that that point in the grid (regardless of col/rowspan), such that
3690 * any column / row could be removed and the new grid constructed
3691 * @param array {object} aLayout Array to store the calculated layout in
3692 * @param {node} nThead The header/footer element for the table
3693 * @memberof DataTable#oApi
3694 */
3695 function _fnDetectHeader ( aLayout, nThead )
3696 {
3697 var nTrs = $(nThead).children('tr');
3698 var nTr, nCell;
3699 var i, k, l, iLen, jLen, iColShifted, iColumn, iColspan, iRowspan;
3700 var bUnique;
3701 var fnShiftCol = function ( a, i, j ) {
3702 var k = a[i];
3703 while ( k[j] ) {
3704 j++;
3705 }
3706 return j;
3707 };
3708
3709 aLayout.splice( 0, aLayout.length );
3710
3711 /* We know how many rows there are in the layout - so prep it */
3712 for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
3713 {
3714 aLayout.push( [] );
3715 }
3716
3717 /* Calculate a layout array */
3718 for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
3719 {
3720 nTr = nTrs[i];
3721 iColumn = 0;
3722
3723 /* For every cell in the row... */
3724 nCell = nTr.firstChild;
3725 while ( nCell ) {
3726 if ( nCell.nodeName.toUpperCase() == "TD" ||
3727 nCell.nodeName.toUpperCase() == "TH" )
3728 {
3729 /* Get the col and rowspan attributes from the DOM and sanitise them */
3730 iColspan = nCell.getAttribute('colspan') * 1;
3731 iRowspan = nCell.getAttribute('rowspan') * 1;
3732 iColspan = (!iColspan || iColspan===0 || iColspan===1) ? 1 : iColspan;
3733 iRowspan = (!iRowspan || iRowspan===0 || iRowspan===1) ? 1 : iRowspan;
3734
3735 /* There might be colspan cells already in this row, so shift our target
3736 * accordingly
3737 */
3738 iColShifted = fnShiftCol( aLayout, i, iColumn );
3739
3740 /* Cache calculation for unique columns */
3741 bUnique = iColspan === 1 ? true : false;
3742
3743 /* If there is col / rowspan, copy the information into the layout grid */
3744 for ( l=0 ; l<iColspan ; l++ )
3745 {
3746 for ( k=0 ; k<iRowspan ; k++ )
3747 {
3748 aLayout[i+k][iColShifted+l] = {
3749 "cell": nCell,
3750 "unique": bUnique
3751 };
3752 aLayout[i+k].nTr = nTr;
3753 }
3754 }
3755 }
3756 nCell = nCell.nextSibling;
3757 }
3758 }
3759 }
3760
3761
3762 /**
3763 * Get an array of unique th elements, one for each column
3764 * @param {object} oSettings dataTables settings object
3765 * @param {node} nHeader automatically detect the layout from this node - optional
3766 * @param {array} aLayout thead/tfoot layout from _fnDetectHeader - optional
3767 * @returns array {node} aReturn list of unique th's
3768 * @memberof DataTable#oApi
3769 */
3770 function _fnGetUniqueThs ( oSettings, nHeader, aLayout )
3771 {
3772 var aReturn = [];
3773 if ( !aLayout )
3774 {
3775 aLayout = oSettings.aoHeader;
3776 if ( nHeader )
3777 {
3778 aLayout = [];
3779 _fnDetectHeader( aLayout, nHeader );
3780 }
3781 }
3782
3783 for ( var i=0, iLen=aLayout.length ; i<iLen ; i++ )
3784 {
3785 for ( var j=0, jLen=aLayout[i].length ; j<jLen ; j++ )
3786 {
3787 if ( aLayout[i][j].unique &&
3788 (!aReturn[j] || !oSettings.bSortCellsTop) )
3789 {
3790 aReturn[j] = aLayout[i][j].cell;
3791 }
3792 }
3793 }
3794
3795 return aReturn;
3796 }
3797
3798 /**
3799 * Create an Ajax call based on the table's settings, taking into account that
3800 * parameters can have multiple forms, and backwards compatibility.
3801 *
3802 * @param {object} oSettings dataTables settings object
3803 * @param {array} data Data to send to the server, required by
3804 * DataTables - may be augmented by developer callbacks
3805 * @param {function} fn Callback function to run when data is obtained
3806 */
3807 function _fnBuildAjax( oSettings, data, fn )
3808 {
3809 // Compatibility with 1.9-, allow fnServerData and event to manipulate
3810 _fnCallbackFire( oSettings, 'aoServerParams', 'serverParams', [data] );
3811
3812 // Convert to object based for 1.10+ if using the old array scheme which can
3813 // come from server-side processing or serverParams
3814 if ( data && $.isArray(data) ) {
3815 var tmp = {};
3816 var rbracket = /(.*?)\[\]$/;
3817
3818 $.each( data, function (key, val) {
3819 var match = val.name.match(rbracket);
3820
3821 if ( match ) {
3822 // Support for arrays
3823 var name = match[0];
3824
3825 if ( ! tmp[ name ] ) {
3826 tmp[ name ] = [];
3827 }
3828 tmp[ name ].push( val.value );
3829 }
3830 else {
3831 tmp[val.name] = val.value;
3832 }
3833 } );
3834 data = tmp;
3835 }
3836
3837 var ajaxData;
3838 var ajax = oSettings.ajax;
3839 var instance = oSettings.oInstance;
3840 var callback = function ( json ) {
3841 _fnCallbackFire( oSettings, null, 'xhr', [oSettings, json, oSettings.jqXHR] );
3842 fn( json );
3843 };
3844
3845 if ( $.isPlainObject( ajax ) && ajax.data )
3846 {
3847 ajaxData = ajax.data;
3848
3849 var newData = $.isFunction( ajaxData ) ?
3850 ajaxData( data, oSettings ) : // fn can manipulate data or return
3851 ajaxData; // an object object or array to merge
3852
3853 // If the function returned something, use that alone
3854 data = $.isFunction( ajaxData ) && newData ?
3855 newData :
3856 $.extend( true, data, newData );
3857
3858 // Remove the data property as we've resolved it already and don't want
3859 // jQuery to do it again (it is restored at the end of the function)
3860 delete ajax.data;
3861 }
3862
3863 var baseAjax = {
3864 "data": data,
3865 "success": function (json) {
3866 var error = json.error || json.sError;
3867 if ( error ) {
3868 _fnLog( oSettings, 0, error );
3869 }
3870
3871 oSettings.json = json;
3872 callback( json );
3873 },
3874 "dataType": "json",
3875 "cache": false,
3876 "type": oSettings.sServerMethod,
3877 "error": function (xhr, error, thrown) {
3878 var ret = _fnCallbackFire( oSettings, null, 'xhr', [oSettings, null, oSettings.jqXHR] );
3879
3880 if ( $.inArray( true, ret ) === -1 ) {
3881 if ( error == "parsererror" ) {
3882 _fnLog( oSettings, 0, 'Invalid JSON response', 1 );
3883 }
3884 else if ( xhr.readyState === 4 ) {
3885 _fnLog( oSettings, 0, 'Ajax error', 7 );
3886 }
3887 }
3888
3889 _fnProcessingDisplay( oSettings, false );
3890 }
3891 };
3892
3893 // Store the data submitted for the API
3894 oSettings.oAjaxData = data;
3895
3896 // Allow plug-ins and external processes to modify the data
3897 _fnCallbackFire( oSettings, null, 'preXhr', [oSettings, data] );
3898
3899 if ( oSettings.fnServerData )
3900 {
3901 // DataTables 1.9- compatibility
3902 oSettings.fnServerData.call( instance,
3903 oSettings.sAjaxSource,
3904 $.map( data, function (val, key) { // Need to convert back to 1.9 trad format
3905 return { name: key, value: val };
3906 } ),
3907 callback,
3908 oSettings
3909 );
3910 }
3911 else if ( oSettings.sAjaxSource || typeof ajax === 'string' )
3912 {
3913 // DataTables 1.9- compatibility
3914 oSettings.jqXHR = $.ajax( $.extend( baseAjax, {
3915 url: ajax || oSettings.sAjaxSource
3916 } ) );
3917 }
3918 else if ( $.isFunction( ajax ) )
3919 {
3920 // Is a function - let the caller define what needs to be done
3921 oSettings.jqXHR = ajax.call( instance, data, callback, oSettings );
3922 }
3923 else
3924 {
3925 // Object to extend the base settings
3926 oSettings.jqXHR = $.ajax( $.extend( baseAjax, ajax ) );
3927
3928 // Restore for next time around
3929 ajax.data = ajaxData;
3930 }
3931 }
3932
3933
3934 /**
3935 * Update the table using an Ajax call
3936 * @param {object} settings dataTables settings object
3937 * @returns {boolean} Block the table drawing or not
3938 * @memberof DataTable#oApi
3939 */
3940 function _fnAjaxUpdate( settings )
3941 {
3942 if ( settings.bAjaxDataGet ) {
3943 settings.iDraw++;
3944 _fnProcessingDisplay( settings, true );
3945
3946 _fnBuildAjax(
3947 settings,
3948 _fnAjaxParameters( settings ),
3949 function(json) {
3950 _fnAjaxUpdateDraw( settings, json );
3951 }
3952 );
3953
3954 return false;
3955 }
3956 return true;
3957 }
3958
3959
3960 /**
3961 * Build up the parameters in an object needed for a server-side processing
3962 * request. Note that this is basically done twice, is different ways - a modern
3963 * method which is used by default in DataTables 1.10 which uses objects and
3964 * arrays, or the 1.9- method with is name / value pairs. 1.9 method is used if
3965 * the sAjaxSource option is used in the initialisation, or the legacyAjax
3966 * option is set.
3967 * @param {object} oSettings dataTables settings object
3968 * @returns {bool} block the table drawing or not
3969 * @memberof DataTable#oApi
3970 */
3971 function _fnAjaxParameters( settings )
3972 {
3973 var
3974 columns = settings.aoColumns,
3975 columnCount = columns.length,
3976 features = settings.oFeatures,
3977 preSearch = settings.oPreviousSearch,
3978 preColSearch = settings.aoPreSearchCols,
3979 i, data = [], dataProp, column, columnSearch,
3980 sort = _fnSortFlatten( settings ),
3981 displayStart = settings._iDisplayStart,
3982 displayLength = features.bPaginate !== false ?
3983 settings._iDisplayLength :
3984 -1;
3985
3986 var param = function ( name, value ) {
3987 data.push( { 'name': name, 'value': value } );
3988 };
3989
3990 // DataTables 1.9- compatible method
3991 param( 'sEcho', settings.iDraw );
3992 param( 'iColumns', columnCount );
3993 param( 'sColumns', _pluck( columns, 'sName' ).join(',') );
3994 param( 'iDisplayStart', displayStart );
3995 param( 'iDisplayLength', displayLength );
3996
3997 // DataTables 1.10+ method
3998 var d = {
3999 draw: settings.iDraw,
4000 columns: [],
4001 order: [],
4002 start: displayStart,
4003 length: displayLength,
4004 search: {
4005 value: preSearch.sSearch,
4006 regex: preSearch.bRegex
4007 }
4008 };
4009
4010 for ( i=0 ; i<columnCount ; i++ ) {
4011 column = columns[i];
4012 columnSearch = preColSearch[i];
4013 dataProp = typeof column.mData=="function" ? 'function' : column.mData ;
4014
4015 d.columns.push( {
4016 data: dataProp,
4017 name: column.sName,
4018 searchable: column.bSearchable,
4019 orderable: column.bSortable,
4020 search: {
4021 value: columnSearch.sSearch,
4022 regex: columnSearch.bRegex
4023 }
4024 } );
4025
4026 param( "mDataProp_"+i, dataProp );
4027
4028 if ( features.bFilter ) {
4029 param( 'sSearch_'+i, columnSearch.sSearch );
4030 param( 'bRegex_'+i, columnSearch.bRegex );
4031 param( 'bSearchable_'+i, column.bSearchable );
4032 }
4033
4034 if ( features.bSort ) {
4035 param( 'bSortable_'+i, column.bSortable );
4036 }
4037 }
4038
4039 if ( features.bFilter ) {
4040 param( 'sSearch', preSearch.sSearch );
4041 param( 'bRegex', preSearch.bRegex );
4042 }
4043
4044 if ( features.bSort ) {
4045 $.each( sort, function ( i, val ) {
4046 d.order.push( { column: val.col, dir: val.dir } );
4047
4048 param( 'iSortCol_'+i, val.col );
4049 param( 'sSortDir_'+i, val.dir );
4050 } );
4051
4052 param( 'iSortingCols', sort.length );
4053 }
4054
4055 // If the legacy.ajax parameter is null, then we automatically decide which
4056 // form to use, based on sAjaxSource
4057 var legacy = DataTable.ext.legacy.ajax;
4058 if ( legacy === null ) {
4059 return settings.sAjaxSource ? data : d;
4060 }
4061
4062 // Otherwise, if legacy has been specified then we use that to decide on the
4063 // form
4064 return legacy ? data : d;
4065 }
4066
4067
4068 /**
4069 * Data the data from the server (nuking the old) and redraw the table
4070 * @param {object} oSettings dataTables settings object
4071 * @param {object} json json data return from the server.
4072 * @param {string} json.sEcho Tracking flag for DataTables to match requests
4073 * @param {int} json.iTotalRecords Number of records in the data set, not accounting for filtering
4074 * @param {int} json.iTotalDisplayRecords Number of records in the data set, accounting for filtering
4075 * @param {array} json.aaData The data to display on this page
4076 * @param {string} [json.sColumns] Column ordering (sName, comma separated)
4077 * @memberof DataTable#oApi
4078 */
4079 function _fnAjaxUpdateDraw ( settings, json )
4080 {
4081 // v1.10 uses camelCase variables, while 1.9 uses Hungarian notation.
4082 // Support both
4083 var compat = function ( old, modern ) {
4084 return json[old] !== undefined ? json[old] : json[modern];
4085 };
4086
4087 var data = _fnAjaxDataSrc( settings, json );
4088 var draw = compat( 'sEcho', 'draw' );
4089 var recordsTotal = compat( 'iTotalRecords', 'recordsTotal' );
4090 var recordsFiltered = compat( 'iTotalDisplayRecords', 'recordsFiltered' );
4091
4092 if ( draw ) {
4093 // Protect against out of sequence returns
4094 if ( draw*1 < settings.iDraw ) {
4095 return;
4096 }
4097 settings.iDraw = draw * 1;
4098 }
4099
4100 _fnClearTable( settings );
4101 settings._iRecordsTotal = parseInt(recordsTotal, 10);
4102 settings._iRecordsDisplay = parseInt(recordsFiltered, 10);
4103
4104 for ( var i=0, ien=data.length ; i<ien ; i++ ) {
4105 _fnAddData( settings, data[i] );
4106 }
4107 settings.aiDisplay = settings.aiDisplayMaster.slice();
4108
4109 settings.bAjaxDataGet = false;
4110 _fnDraw( settings );
4111
4112 if ( ! settings._bInitComplete ) {
4113 _fnInitComplete( settings, json );
4114 }
4115
4116 settings.bAjaxDataGet = true;
4117 _fnProcessingDisplay( settings, false );
4118 }
4119
4120
4121 /**
4122 * Get the data from the JSON data source to use for drawing a table. Using
4123 * `_fnGetObjectDataFn` allows the data to be sourced from a property of the
4124 * source object, or from a processing function.
4125 * @param {object} oSettings dataTables settings object
4126 * @param {object} json Data source object / array from the server
4127 * @return {array} Array of data to use
4128 */
4129 function _fnAjaxDataSrc ( oSettings, json )
4130 {
4131 var dataSrc = $.isPlainObject( oSettings.ajax ) && oSettings.ajax.dataSrc !== undefined ?
4132 oSettings.ajax.dataSrc :
4133 oSettings.sAjaxDataProp; // Compatibility with 1.9-.
4134
4135 // Compatibility with 1.9-. In order to read from aaData, check if the
4136 // default has been changed, if not, check for aaData
4137 if ( dataSrc === 'data' ) {
4138 return json.aaData || json[dataSrc];
4139 }
4140
4141 return dataSrc !== "" ?
4142 _fnGetObjectDataFn( dataSrc )( json ) :
4143 json;
4144 }
4145
4146 /**
4147 * Generate the node required for filtering text
4148 * @returns {node} Filter control element
4149 * @param {object} oSettings dataTables settings object
4150 * @memberof DataTable#oApi
4151 */
4152 function _fnFeatureHtmlFilter ( settings )
4153 {
4154 var classes = settings.oClasses;
4155 var tableId = settings.sTableId;
4156 var language = settings.oLanguage;
4157 var previousSearch = settings.oPreviousSearch;
4158 var features = settings.aanFeatures;
4159 var input = '<input type="search" class="'+classes.sFilterInput+'"/>';
4160
4161 var str = language.sSearch;
4162 str = str.match(/_INPUT_/) ?
4163 str.replace('_INPUT_', input) :
4164 str+input;
4165
4166 var filter = $('<div/>', {
4167 'id': ! features.f ? tableId+'_filter' : null,
4168 'class': classes.sFilter
4169 } )
4170 .append( $('<label/>' ).append( str ) );
4171
4172 var searchFn = function() {
4173 /* Update all other filter input elements for the new display */
4174 var n = features.f;
4175 var val = !this.value ? "" : this.value; // mental IE8 fix :-(
4176
4177 /* Now do the filter */
4178 if ( val != previousSearch.sSearch ) {
4179 _fnFilterComplete( settings, {
4180 "sSearch": val,
4181 "bRegex": previousSearch.bRegex,
4182 "bSmart": previousSearch.bSmart ,
4183 "bCaseInsensitive": previousSearch.bCaseInsensitive
4184 } );
4185
4186 // Need to redraw, without resorting
4187 settings._iDisplayStart = 0;
4188 _fnDraw( settings );
4189 }
4190 };
4191
4192 var searchDelay = settings.searchDelay !== null ?
4193 settings.searchDelay :
4194 _fnDataSource( settings ) === 'ssp' ?
4195 400 :
4196 0;
4197
4198 var jqFilter = $('input', filter)
4199 .val( previousSearch.sSearch )
4200 .attr( 'placeholder', language.sSearchPlaceholder )
4201 .on(
4202 'keyup.DT search.DT input.DT paste.DT cut.DT',
4203 searchDelay ?
4204 _fnThrottle( searchFn, searchDelay ) :
4205 searchFn
4206 )
4207 .on( 'keypress.DT', function(e) {
4208 /* Prevent form submission */
4209 if ( e.keyCode == 13 ) {
4210 return false;
4211 }
4212 } )
4213 .attr('aria-controls', tableId);
4214
4215 // Update the input elements whenever the table is filtered
4216 $(settings.nTable).on( 'search.dt.DT', function ( ev, s ) {
4217 if ( settings === s ) {
4218 // IE9 throws an 'unknown error' if document.activeElement is used
4219 // inside an iframe or frame...
4220 try {
4221 if ( jqFilter[0] !== document.activeElement ) {
4222 jqFilter.val( previousSearch.sSearch );
4223 }
4224 }
4225 catch ( e ) {}
4226 }
4227 } );
4228
4229 return filter[0];
4230 }
4231
4232
4233 /**
4234 * Filter the table using both the global filter and column based filtering
4235 * @param {object} oSettings dataTables settings object
4236 * @param {object} oSearch search information
4237 * @param {int} [iForce] force a research of the master array (1) or not (undefined or 0)
4238 * @memberof DataTable#oApi
4239 */
4240 function _fnFilterComplete ( oSettings, oInput, iForce )
4241 {
4242 var oPrevSearch = oSettings.oPreviousSearch;
4243 var aoPrevSearch = oSettings.aoPreSearchCols;
4244 var fnSaveFilter = function ( oFilter ) {
4245 /* Save the filtering values */
4246 oPrevSearch.sSearch = oFilter.sSearch;
4247 oPrevSearch.bRegex = oFilter.bRegex;
4248 oPrevSearch.bSmart = oFilter.bSmart;
4249 oPrevSearch.bCaseInsensitive = oFilter.bCaseInsensitive;
4250 };
4251 var fnRegex = function ( o ) {
4252 // Backwards compatibility with the bEscapeRegex option
4253 return o.bEscapeRegex !== undefined ? !o.bEscapeRegex : o.bRegex;
4254 };
4255
4256 // Resolve any column types that are unknown due to addition or invalidation
4257 // @todo As per sort - can this be moved into an event handler?
4258 _fnColumnTypes( oSettings );
4259
4260 /* In server-side processing all filtering is done by the server, so no point hanging around here */
4261 if ( _fnDataSource( oSettings ) != 'ssp' )
4262 {
4263 /* Global filter */
4264 _fnFilter( oSettings, oInput.sSearch, iForce, fnRegex(oInput), oInput.bSmart, oInput.bCaseInsensitive );
4265 fnSaveFilter( oInput );
4266
4267 /* Now do the individual column filter */
4268 for ( var i=0 ; i<aoPrevSearch.length ; i++ )
4269 {
4270 _fnFilterColumn( oSettings, aoPrevSearch[i].sSearch, i, fnRegex(aoPrevSearch[i]),
4271 aoPrevSearch[i].bSmart, aoPrevSearch[i].bCaseInsensitive );
4272 }
4273
4274 /* Custom filtering */
4275 _fnFilterCustom( oSettings );
4276 }
4277 else
4278 {
4279 fnSaveFilter( oInput );
4280 }
4281
4282 /* Tell the draw function we have been filtering */
4283 oSettings.bFiltered = true;
4284 _fnCallbackFire( oSettings, null, 'search', [oSettings] );
4285 }
4286
4287
4288 /**
4289 * Apply custom filtering functions
4290 * @param {object} oSettings dataTables settings object
4291 * @memberof DataTable#oApi
4292 */
4293 function _fnFilterCustom( settings )
4294 {
4295 var filters = DataTable.ext.search;
4296 var displayRows = settings.aiDisplay;
4297 var row, rowIdx;
4298
4299 for ( var i=0, ien=filters.length ; i<ien ; i++ ) {
4300 var rows = [];
4301
4302 // Loop over each row and see if it should be included
4303 for ( var j=0, jen=displayRows.length ; j<jen ; j++ ) {
4304 rowIdx = displayRows[ j ];
4305 row = settings.aoData[ rowIdx ];
4306
4307 if ( filters[i]( settings, row._aFilterData, rowIdx, row._aData, j ) ) {
4308 rows.push( rowIdx );
4309 }
4310 }
4311
4312 // So the array reference doesn't break set the results into the
4313 // existing array
4314 displayRows.length = 0;
4315 $.merge( displayRows, rows );
4316 }
4317 }
4318
4319
4320 /**
4321 * Filter the table on a per-column basis
4322 * @param {object} oSettings dataTables settings object
4323 * @param {string} sInput string to filter on
4324 * @param {int} iColumn column to filter
4325 * @param {bool} bRegex treat search string as a regular expression or not
4326 * @param {bool} bSmart use smart filtering or not
4327 * @param {bool} bCaseInsensitive Do case insenstive matching or not
4328 * @memberof DataTable#oApi
4329 */
4330 function _fnFilterColumn ( settings, searchStr, colIdx, regex, smart, caseInsensitive )
4331 {
4332 if ( searchStr === '' ) {
4333 return;
4334 }
4335
4336 var data;
4337 var out = [];
4338 var display = settings.aiDisplay;
4339 var rpSearch = _fnFilterCreateSearch( searchStr, regex, smart, caseInsensitive );
4340
4341 for ( var i=0 ; i<display.length ; i++ ) {
4342 data = settings.aoData[ display[i] ]._aFilterData[ colIdx ];
4343
4344 if ( rpSearch.test( data ) ) {
4345 out.push( display[i] );
4346 }
4347 }
4348
4349 settings.aiDisplay = out;
4350 }
4351
4352
4353 /**
4354 * Filter the data table based on user input and draw the table
4355 * @param {object} settings dataTables settings object
4356 * @param {string} input string to filter on
4357 * @param {int} force optional - force a research of the master array (1) or not (undefined or 0)
4358 * @param {bool} regex treat as a regular expression or not
4359 * @param {bool} smart perform smart filtering or not
4360 * @param {bool} caseInsensitive Do case insenstive matching or not
4361 * @memberof DataTable#oApi
4362 */
4363 function _fnFilter( settings, input, force, regex, smart, caseInsensitive )
4364 {
4365 var rpSearch = _fnFilterCreateSearch( input, regex, smart, caseInsensitive );
4366 var prevSearch = settings.oPreviousSearch.sSearch;
4367 var displayMaster = settings.aiDisplayMaster;
4368 var display, invalidated, i;
4369 var filtered = [];
4370
4371 // Need to take account of custom filtering functions - always filter
4372 if ( DataTable.ext.search.length !== 0 ) {
4373 force = true;
4374 }
4375
4376 // Check if any of the rows were invalidated
4377 invalidated = _fnFilterData( settings );
4378
4379 // If the input is blank - we just want the full data set
4380 if ( input.length <= 0 ) {
4381 settings.aiDisplay = displayMaster.slice();
4382 }
4383 else {
4384 // New search - start from the master array
4385 if ( invalidated ||
4386 force ||
4387 prevSearch.length > input.length ||
4388 input.indexOf(prevSearch) !== 0 ||
4389 settings.bSorted // On resort, the display master needs to be
4390 // re-filtered since indexes will have changed
4391 ) {
4392 settings.aiDisplay = displayMaster.slice();
4393 }
4394
4395 // Search the display array
4396 display = settings.aiDisplay;
4397
4398 for ( i=0 ; i<display.length ; i++ ) {
4399 if ( rpSearch.test( settings.aoData[ display[i] ]._sFilterRow ) ) {
4400 filtered.push( display[i] );
4401 }
4402 }
4403
4404 settings.aiDisplay = filtered;
4405 }
4406 }
4407
4408
4409 /**
4410 * Build a regular expression object suitable for searching a table
4411 * @param {string} sSearch string to search for
4412 * @param {bool} bRegex treat as a regular expression or not
4413 * @param {bool} bSmart perform smart filtering or not
4414 * @param {bool} bCaseInsensitive Do case insensitive matching or not
4415 * @returns {RegExp} constructed object
4416 * @memberof DataTable#oApi
4417 */
4418 function _fnFilterCreateSearch( search, regex, smart, caseInsensitive )
4419 {
4420 search = regex ?
4421 search :
4422 _fnEscapeRegex( search );
4423
4424 if ( smart ) {
4425 /* For smart filtering we want to allow the search to work regardless of
4426 * word order. We also want double quoted text to be preserved, so word
4427 * order is important - a la google. So this is what we want to
4428 * generate:
4429 *
4430 * ^(?=.*?\bone\b)(?=.*?\btwo three\b)(?=.*?\bfour\b).*$
4431 */
4432 var a = $.map( search.match( /"[^"]+"|[^ ]+/g ) || [''], function ( word ) {
4433 if ( word.charAt(0) === '"' ) {
4434 var m = word.match( /^"(.*)"$/ );
4435 word = m ? m[1] : word;
4436 }
4437
4438 return word.replace('"', '');
4439 } );
4440
4441 search = '^(?=.*?'+a.join( ')(?=.*?' )+').*$';
4442 }
4443
4444 return new RegExp( search, caseInsensitive ? 'i' : '' );
4445 }
4446
4447
4448 /**
4449 * Escape a string such that it can be used in a regular expression
4450 * @param {string} sVal string to escape
4451 * @returns {string} escaped string
4452 * @memberof DataTable#oApi
4453 */
4454 var _fnEscapeRegex = DataTable.util.escapeRegex;
4455
4456 var __filter_div = $('<div>')[0];
4457 var __filter_div_textContent = __filter_div.textContent !== undefined;
4458
4459 // Update the filtering data for each row if needed (by invalidation or first run)
4460 function _fnFilterData ( settings )
4461 {
4462 var columns = settings.aoColumns;
4463 var column;
4464 var i, j, ien, jen, filterData, cellData, row;
4465 var fomatters = DataTable.ext.type.search;
4466 var wasInvalidated = false;
4467
4468 for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
4469 row = settings.aoData[i];
4470
4471 if ( ! row._aFilterData ) {
4472 filterData = [];
4473
4474 for ( j=0, jen=columns.length ; j<jen ; j++ ) {
4475 column = columns[j];
4476
4477 if ( column.bSearchable ) {
4478 cellData = _fnGetCellData( settings, i, j, 'filter' );
4479
4480 if ( fomatters[ column.sType ] ) {
4481 cellData = fomatters[ column.sType ]( cellData );
4482 }
4483
4484 // Search in DataTables 1.10 is string based. In 1.11 this
4485 // should be altered to also allow strict type checking.
4486 if ( cellData === null ) {
4487 cellData = '';
4488 }
4489
4490 if ( typeof cellData !== 'string' && cellData.toString ) {
4491 cellData = cellData.toString();
4492 }
4493 }
4494 else {
4495 cellData = '';
4496 }
4497
4498 // If it looks like there is an HTML entity in the string,
4499 // attempt to decode it so sorting works as expected. Note that
4500 // we could use a single line of jQuery to do this, but the DOM
4501 // method used here is much faster http://jsperf.com/html-decode
4502 if ( cellData.indexOf && cellData.indexOf('&') !== -1 ) {
4503 __filter_div.innerHTML = cellData;
4504 cellData = __filter_div_textContent ?
4505 __filter_div.textContent :
4506 __filter_div.innerText;
4507 }
4508
4509 if ( cellData.replace ) {
4510 cellData = cellData.replace(/[\r\n]/g, '');
4511 }
4512
4513 filterData.push( cellData );
4514 }
4515
4516 row._aFilterData = filterData;
4517 row._sFilterRow = filterData.join(' ');
4518 wasInvalidated = true;
4519 }
4520 }
4521
4522 return wasInvalidated;
4523 }
4524
4525
4526 /**
4527 * Convert from the internal Hungarian notation to camelCase for external
4528 * interaction
4529 * @param {object} obj Object to convert
4530 * @returns {object} Inverted object
4531 * @memberof DataTable#oApi
4532 */
4533 function _fnSearchToCamel ( obj )
4534 {
4535 return {
4536 search: obj.sSearch,
4537 smart: obj.bSmart,
4538 regex: obj.bRegex,
4539 caseInsensitive: obj.bCaseInsensitive
4540 };
4541 }
4542
4543
4544
4545 /**
4546 * Convert from camelCase notation to the internal Hungarian. We could use the
4547 * Hungarian convert function here, but this is cleaner
4548 * @param {object} obj Object to convert
4549 * @returns {object} Inverted object
4550 * @memberof DataTable#oApi
4551 */
4552 function _fnSearchToHung ( obj )
4553 {
4554 return {
4555 sSearch: obj.search,
4556 bSmart: obj.smart,
4557 bRegex: obj.regex,
4558 bCaseInsensitive: obj.caseInsensitive
4559 };
4560 }
4561
4562 /**
4563 * Generate the node required for the info display
4564 * @param {object} oSettings dataTables settings object
4565 * @returns {node} Information element
4566 * @memberof DataTable#oApi
4567 */
4568 function _fnFeatureHtmlInfo ( settings )
4569 {
4570 var
4571 tid = settings.sTableId,
4572 nodes = settings.aanFeatures.i,
4573 n = $('<div/>', {
4574 'class': settings.oClasses.sInfo,
4575 'id': ! nodes ? tid+'_info' : null
4576 } );
4577
4578 if ( ! nodes ) {
4579 // Update display on each draw
4580 settings.aoDrawCallback.push( {
4581 "fn": _fnUpdateInfo,
4582 "sName": "information"
4583 } );
4584
4585 n
4586 .attr( 'role', 'status' )
4587 .attr( 'aria-live', 'polite' );
4588
4589 // Table is described by our info div
4590 $(settings.nTable).attr( 'aria-describedby', tid+'_info' );
4591 }
4592
4593 return n[0];
4594 }
4595
4596
4597 /**
4598 * Update the information elements in the display
4599 * @param {object} settings dataTables settings object
4600 * @memberof DataTable#oApi
4601 */
4602 function _fnUpdateInfo ( settings )
4603 {
4604 /* Show information about the table */
4605 var nodes = settings.aanFeatures.i;
4606 if ( nodes.length === 0 ) {
4607 return;
4608 }
4609
4610 var
4611 lang = settings.oLanguage,
4612 start = settings._iDisplayStart+1,
4613 end = settings.fnDisplayEnd(),
4614 max = settings.fnRecordsTotal(),
4615 total = settings.fnRecordsDisplay(),
4616 out = total ?
4617 lang.sInfo :
4618 lang.sInfoEmpty;
4619
4620 if ( total !== max ) {
4621 /* Record set after filtering */
4622 out += ' ' + lang.sInfoFiltered;
4623 }
4624
4625 // Convert the macros
4626 out += lang.sInfoPostFix;
4627 out = _fnInfoMacros( settings, out );
4628
4629 var callback = lang.fnInfoCallback;
4630 if ( callback !== null ) {
4631 out = callback.call( settings.oInstance,
4632 settings, start, end, max, total, out
4633 );
4634 }
4635
4636 $(nodes).html( out );
4637 }
4638
4639
4640 function _fnInfoMacros ( settings, str )
4641 {
4642 // When infinite scrolling, we are always starting at 1. _iDisplayStart is used only
4643 // internally
4644 var
4645 formatter = settings.fnFormatNumber,
4646 start = settings._iDisplayStart+1,
4647 len = settings._iDisplayLength,
4648 vis = settings.fnRecordsDisplay(),
4649 all = len === -1;
4650
4651 return str.
4652 replace(/_START_/g, formatter.call( settings, start ) ).
4653 replace(/_END_/g, formatter.call( settings, settings.fnDisplayEnd() ) ).
4654 replace(/_MAX_/g, formatter.call( settings, settings.fnRecordsTotal() ) ).
4655 replace(/_TOTAL_/g, formatter.call( settings, vis ) ).
4656 replace(/_PAGE_/g, formatter.call( settings, all ? 1 : Math.ceil( start / len ) ) ).
4657 replace(/_PAGES_/g, formatter.call( settings, all ? 1 : Math.ceil( vis / len ) ) );
4658 }
4659
4660
4661
4662 /**
4663 * Draw the table for the first time, adding all required features
4664 * @param {object} settings dataTables settings object
4665 * @memberof DataTable#oApi
4666 */
4667 function _fnInitialise ( settings )
4668 {
4669 var i, iLen, iAjaxStart=settings.iInitDisplayStart;
4670 var columns = settings.aoColumns, column;
4671 var features = settings.oFeatures;
4672 var deferLoading = settings.bDeferLoading; // value modified by the draw
4673
4674 /* Ensure that the table data is fully initialised */
4675 if ( ! settings.bInitialised ) {
4676 setTimeout( function(){ _fnInitialise( settings ); }, 200 );
4677 return;
4678 }
4679
4680 /* Show the display HTML options */
4681 _fnAddOptionsHtml( settings );
4682
4683 /* Build and draw the header / footer for the table */
4684 _fnBuildHead( settings );
4685 _fnDrawHead( settings, settings.aoHeader );
4686 _fnDrawHead( settings, settings.aoFooter );
4687
4688 /* Okay to show that something is going on now */
4689 _fnProcessingDisplay( settings, true );
4690
4691 /* Calculate sizes for columns */
4692 if ( features.bAutoWidth ) {
4693 _fnCalculateColumnWidths( settings );
4694 }
4695
4696 for ( i=0, iLen=columns.length ; i<iLen ; i++ ) {
4697 column = columns[i];
4698
4699 if ( column.sWidth ) {
4700 column.nTh.style.width = _fnStringToCss( column.sWidth );
4701 }
4702 }
4703
4704 _fnCallbackFire( settings, null, 'preInit', [settings] );
4705
4706 // If there is default sorting required - let's do it. The sort function
4707 // will do the drawing for us. Otherwise we draw the table regardless of the
4708 // Ajax source - this allows the table to look initialised for Ajax sourcing
4709 // data (show 'loading' message possibly)
4710 _fnReDraw( settings );
4711
4712 // Server-side processing init complete is done by _fnAjaxUpdateDraw
4713 var dataSrc = _fnDataSource( settings );
4714 if ( dataSrc != 'ssp' || deferLoading ) {
4715 // if there is an ajax source load the data
4716 if ( dataSrc == 'ajax' ) {
4717 _fnBuildAjax( settings, [], function(json) {
4718 var aData = _fnAjaxDataSrc( settings, json );
4719
4720 // Got the data - add it to the table
4721 for ( i=0 ; i<aData.length ; i++ ) {
4722 _fnAddData( settings, aData[i] );
4723 }
4724
4725 // Reset the init display for cookie saving. We've already done
4726 // a filter, and therefore cleared it before. So we need to make
4727 // it appear 'fresh'
4728 settings.iInitDisplayStart = iAjaxStart;
4729
4730 _fnReDraw( settings );
4731
4732 _fnProcessingDisplay( settings, false );
4733 _fnInitComplete( settings, json );
4734 }, settings );
4735 }
4736 else {
4737 _fnProcessingDisplay( settings, false );
4738 _fnInitComplete( settings );
4739 }
4740 }
4741 }
4742
4743
4744 /**
4745 * Draw the table for the first time, adding all required features
4746 * @param {object} oSettings dataTables settings object
4747 * @param {object} [json] JSON from the server that completed the table, if using Ajax source
4748 * with client-side processing (optional)
4749 * @memberof DataTable#oApi
4750 */
4751 function _fnInitComplete ( settings, json )
4752 {
4753 settings._bInitComplete = true;
4754
4755 // When data was added after the initialisation (data or Ajax) we need to
4756 // calculate the column sizing
4757 if ( json || settings.oInit.aaData ) {
4758 _fnAdjustColumnSizing( settings );
4759 }
4760
4761 _fnCallbackFire( settings, null, 'plugin-init', [settings, json] );
4762 _fnCallbackFire( settings, 'aoInitComplete', 'init', [settings, json] );
4763 }
4764
4765
4766 function _fnLengthChange ( settings, val )
4767 {
4768 var len = parseInt( val, 10 );
4769 settings._iDisplayLength = len;
4770
4771 _fnLengthOverflow( settings );
4772
4773 // Fire length change event
4774 _fnCallbackFire( settings, null, 'length', [settings, len] );
4775 }
4776
4777
4778 /**
4779 * Generate the node required for user display length changing
4780 * @param {object} settings dataTables settings object
4781 * @returns {node} Display length feature node
4782 * @memberof DataTable#oApi
4783 */
4784 function _fnFeatureHtmlLength ( settings )
4785 {
4786 var
4787 classes = settings.oClasses,
4788 tableId = settings.sTableId,
4789 menu = settings.aLengthMenu,
4790 d2 = $.isArray( menu[0] ),
4791 lengths = d2 ? menu[0] : menu,
4792 language = d2 ? menu[1] : menu;
4793
4794 var select = $('<select/>', {
4795 'name': tableId+'_length',
4796 'aria-controls': tableId,
4797 'class': classes.sLengthSelect
4798 } );
4799
4800 for ( var i=0, ien=lengths.length ; i<ien ; i++ ) {
4801 select[0][ i ] = new Option( language[i], lengths[i] );
4802 }
4803
4804 var div = $('<div><label/></div>').addClass( classes.sLength );
4805 if ( ! settings.aanFeatures.l ) {
4806 div[0].id = tableId+'_length';
4807 }
4808
4809 div.children().append(
4810 settings.oLanguage.sLengthMenu.replace( '_MENU_', select[0].outerHTML )
4811 );
4812
4813 // Can't use `select` variable as user might provide their own and the
4814 // reference is broken by the use of outerHTML
4815 $('select', div)
4816 .val( settings._iDisplayLength )
4817 .on( 'change.DT', function(e) {
4818 _fnLengthChange( settings, $(this).val() );
4819 _fnDraw( settings );
4820 } );
4821
4822 // Update node value whenever anything changes the table's length
4823 $(settings.nTable).on( 'length.dt.DT', function (e, s, len) {
4824 if ( settings === s ) {
4825 $('select', div).val( len );
4826 }
4827 } );
4828
4829 return div[0];
4830 }
4831
4832
4833
4834 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
4835 * Note that most of the paging logic is done in
4836 * DataTable.ext.pager
4837 */
4838
4839 /**
4840 * Generate the node required for default pagination
4841 * @param {object} oSettings dataTables settings object
4842 * @returns {node} Pagination feature node
4843 * @memberof DataTable#oApi
4844 */
4845 function _fnFeatureHtmlPaginate ( settings )
4846 {
4847 var
4848 type = settings.sPaginationType,
4849 plugin = DataTable.ext.pager[ type ],
4850 modern = typeof plugin === 'function',
4851 redraw = function( settings ) {
4852 _fnDraw( settings );
4853 },
4854 node = $('<div/>').addClass( settings.oClasses.sPaging + type )[0],
4855 features = settings.aanFeatures;
4856
4857 if ( ! modern ) {
4858 plugin.fnInit( settings, node, redraw );
4859 }
4860
4861 /* Add a draw callback for the pagination on first instance, to update the paging display */
4862 if ( ! features.p )
4863 {
4864 node.id = settings.sTableId+'_paginate';
4865
4866 settings.aoDrawCallback.push( {
4867 "fn": function( settings ) {
4868 if ( modern ) {
4869 var
4870 start = settings._iDisplayStart,
4871 len = settings._iDisplayLength,
4872 visRecords = settings.fnRecordsDisplay(),
4873 all = len === -1,
4874 page = all ? 0 : Math.ceil( start / len ),
4875 pages = all ? 1 : Math.ceil( visRecords / len ),
4876 buttons = plugin(page, pages),
4877 i, ien;
4878
4879 for ( i=0, ien=features.p.length ; i<ien ; i++ ) {
4880 _fnRenderer( settings, 'pageButton' )(
4881 settings, features.p[i], i, buttons, page, pages
4882 );
4883 }
4884 }
4885 else {
4886 plugin.fnUpdate( settings, redraw );
4887 }
4888 },
4889 "sName": "pagination"
4890 } );
4891 }
4892
4893 return node;
4894 }
4895
4896
4897 /**
4898 * Alter the display settings to change the page
4899 * @param {object} settings DataTables settings object
4900 * @param {string|int} action Paging action to take: "first", "previous",
4901 * "next" or "last" or page number to jump to (integer)
4902 * @param [bool] redraw Automatically draw the update or not
4903 * @returns {bool} true page has changed, false - no change
4904 * @memberof DataTable#oApi
4905 */
4906 function _fnPageChange ( settings, action, redraw )
4907 {
4908 var
4909 start = settings._iDisplayStart,
4910 len = settings._iDisplayLength,
4911 records = settings.fnRecordsDisplay();
4912
4913 if ( records === 0 || len === -1 )
4914 {
4915 start = 0;
4916 }
4917 else if ( typeof action === "number" )
4918 {
4919 start = action * len;
4920
4921 if ( start > records )
4922 {
4923 start = 0;
4924 }
4925 }
4926 else if ( action == "first" )
4927 {
4928 start = 0;
4929 }
4930 else if ( action == "previous" )
4931 {
4932 start = len >= 0 ?
4933 start - len :
4934 0;
4935
4936 if ( start < 0 )
4937 {
4938 start = 0;
4939 }
4940 }
4941 else if ( action == "next" )
4942 {
4943 if ( start + len < records )
4944 {
4945 start += len;
4946 }
4947 }
4948 else if ( action == "last" )
4949 {
4950 start = Math.floor( (records-1) / len) * len;
4951 }
4952 else
4953 {
4954 _fnLog( settings, 0, "Unknown paging action: "+action, 5 );
4955 }
4956
4957 var changed = settings._iDisplayStart !== start;
4958 settings._iDisplayStart = start;
4959
4960 if ( changed ) {
4961 _fnCallbackFire( settings, null, 'page', [settings] );
4962
4963 if ( redraw ) {
4964 _fnDraw( settings );
4965 }
4966 }
4967
4968 return changed;
4969 }
4970
4971
4972
4973 /**
4974 * Generate the node required for the processing node
4975 * @param {object} settings dataTables settings object
4976 * @returns {node} Processing element
4977 * @memberof DataTable#oApi
4978 */
4979 function _fnFeatureHtmlProcessing ( settings )
4980 {
4981 return $('<div/>', {
4982 'id': ! settings.aanFeatures.r ? settings.sTableId+'_processing' : null,
4983 'class': settings.oClasses.sProcessing
4984 } )
4985 .html( settings.oLanguage.sProcessing )
4986 .insertBefore( settings.nTable )[0];
4987 }
4988
4989
4990 /**
4991 * Display or hide the processing indicator
4992 * @param {object} settings dataTables settings object
4993 * @param {bool} show Show the processing indicator (true) or not (false)
4994 * @memberof DataTable#oApi
4995 */
4996 function _fnProcessingDisplay ( settings, show )
4997 {
4998 if ( settings.oFeatures.bProcessing ) {
4999 $(settings.aanFeatures.r).css( 'display', show ? 'block' : 'none' );
5000 }
5001
5002 _fnCallbackFire( settings, null, 'processing', [settings, show] );
5003 }
5004
5005 /**
5006 * Add any control elements for the table - specifically scrolling
5007 * @param {object} settings dataTables settings object
5008 * @returns {node} Node to add to the DOM
5009 * @memberof DataTable#oApi
5010 */
5011 function _fnFeatureHtmlTable ( settings )
5012 {
5013 var table = $(settings.nTable);
5014
5015 // Add the ARIA grid role to the table
5016 table.attr( 'role', 'grid' );
5017
5018 // Scrolling from here on in
5019 var scroll = settings.oScroll;
5020
5021 if ( scroll.sX === '' && scroll.sY === '' ) {
5022 return settings.nTable;
5023 }
5024
5025 var scrollX = scroll.sX;
5026 var scrollY = scroll.sY;
5027 var classes = settings.oClasses;
5028 var caption = table.children('caption');
5029 var captionSide = caption.length ? caption[0]._captionSide : null;
5030 var headerClone = $( table[0].cloneNode(false) );
5031 var footerClone = $( table[0].cloneNode(false) );
5032 var footer = table.children('tfoot');
5033 var _div = '<div/>';
5034 var size = function ( s ) {
5035 return !s ? null : _fnStringToCss( s );
5036 };
5037
5038 if ( ! footer.length ) {
5039 footer = null;
5040 }
5041
5042 /*
5043 * The HTML structure that we want to generate in this function is:
5044 * div - scroller
5045 * div - scroll head
5046 * div - scroll head inner
5047 * table - scroll head table
5048 * thead - thead
5049 * div - scroll body
5050 * table - table (master table)
5051 * thead - thead clone for sizing
5052 * tbody - tbody
5053 * div - scroll foot
5054 * div - scroll foot inner
5055 * table - scroll foot table
5056 * tfoot - tfoot
5057 */
5058 var scroller = $( _div, { 'class': classes.sScrollWrapper } )
5059 .append(
5060 $(_div, { 'class': classes.sScrollHead } )
5061 .css( {
5062 overflow: 'hidden',
5063 position: 'relative',
5064 border: 0,
5065 width: scrollX ? size(scrollX) : '100%'
5066 } )
5067 .append(
5068 $(_div, { 'class': classes.sScrollHeadInner } )
5069 .css( {
5070 'box-sizing': 'content-box',
5071 width: scroll.sXInner || '100%'
5072 } )
5073 .append(
5074 headerClone
5075 .removeAttr('id')
5076 .css( 'margin-left', 0 )
5077 .append( captionSide === 'top' ? caption : null )
5078 .append(
5079 table.children('thead')
5080 )
5081 )
5082 )
5083 )
5084 .append(
5085 $(_div, { 'class': classes.sScrollBody } )
5086 .css( {
5087 position: 'relative',
5088 overflow: 'auto',
5089 width: size( scrollX )
5090 } )
5091 .append( table )
5092 );
5093
5094 if ( footer ) {
5095 scroller.append(
5096 $(_div, { 'class': classes.sScrollFoot } )
5097 .css( {
5098 overflow: 'hidden',
5099 border: 0,
5100 width: scrollX ? size(scrollX) : '100%'
5101 } )
5102 .append(
5103 $(_div, { 'class': classes.sScrollFootInner } )
5104 .append(
5105 footerClone
5106 .removeAttr('id')
5107 .css( 'margin-left', 0 )
5108 .append( captionSide === 'bottom' ? caption : null )
5109 .append(
5110 table.children('tfoot')
5111 )
5112 )
5113 )
5114 );
5115 }
5116
5117 var children = scroller.children();
5118 var scrollHead = children[0];
5119 var scrollBody = children[1];
5120 var scrollFoot = footer ? children[2] : null;
5121
5122 // When the body is scrolled, then we also want to scroll the headers
5123 if ( scrollX ) {
5124 $(scrollBody).on( 'scroll.DT', function (e) {
5125 var scrollLeft = this.scrollLeft;
5126
5127 scrollHead.scrollLeft = scrollLeft;
5128
5129 if ( footer ) {
5130 scrollFoot.scrollLeft = scrollLeft;
5131 }
5132 } );
5133 }
5134
5135 $(scrollBody).css(
5136 scrollY && scroll.bCollapse ? 'max-height' : 'height',
5137 scrollY
5138 );
5139
5140 settings.nScrollHead = scrollHead;
5141 settings.nScrollBody = scrollBody;
5142 settings.nScrollFoot = scrollFoot;
5143
5144 // On redraw - align columns
5145 settings.aoDrawCallback.push( {
5146 "fn": _fnScrollDraw,
5147 "sName": "scrolling"
5148 } );
5149
5150 return scroller[0];
5151 }
5152
5153
5154
5155 /**
5156 * Update the header, footer and body tables for resizing - i.e. column
5157 * alignment.
5158 *
5159 * Welcome to the most horrible function DataTables. The process that this
5160 * function follows is basically:
5161 * 1. Re-create the table inside the scrolling div
5162 * 2. Take live measurements from the DOM
5163 * 3. Apply the measurements to align the columns
5164 * 4. Clean up
5165 *
5166 * @param {object} settings dataTables settings object
5167 * @memberof DataTable#oApi
5168 */
5169 function _fnScrollDraw ( settings )
5170 {
5171 // Given that this is such a monster function, a lot of variables are use
5172 // to try and keep the minimised size as small as possible
5173 var
5174 scroll = settings.oScroll,
5175 scrollX = scroll.sX,
5176 scrollXInner = scroll.sXInner,
5177 scrollY = scroll.sY,
5178 barWidth = scroll.iBarWidth,
5179 divHeader = $(settings.nScrollHead),
5180 divHeaderStyle = divHeader[0].style,
5181 divHeaderInner = divHeader.children('div'),
5182 divHeaderInnerStyle = divHeaderInner[0].style,
5183 divHeaderTable = divHeaderInner.children('table'),
5184 divBodyEl = settings.nScrollBody,
5185 divBody = $(divBodyEl),
5186 divBodyStyle = divBodyEl.style,
5187 divFooter = $(settings.nScrollFoot),
5188 divFooterInner = divFooter.children('div'),
5189 divFooterTable = divFooterInner.children('table'),
5190 header = $(settings.nTHead),
5191 table = $(settings.nTable),
5192 tableEl = table[0],
5193 tableStyle = tableEl.style,
5194 footer = settings.nTFoot ? $(settings.nTFoot) : null,
5195 browser = settings.oBrowser,
5196 ie67 = browser.bScrollOversize,
5197 dtHeaderCells = _pluck( settings.aoColumns, 'nTh' ),
5198 headerTrgEls, footerTrgEls,
5199 headerSrcEls, footerSrcEls,
5200 headerCopy, footerCopy,
5201 headerWidths=[], footerWidths=[],
5202 headerContent=[], footerContent=[],
5203 idx, correction, sanityWidth,
5204 zeroOut = function(nSizer) {
5205 var style = nSizer.style;
5206 style.paddingTop = "0";
5207 style.paddingBottom = "0";
5208 style.borderTopWidth = "0";
5209 style.borderBottomWidth = "0";
5210 style.height = 0;
5211 };
5212
5213 // If the scrollbar visibility has changed from the last draw, we need to
5214 // adjust the column sizes as the table width will have changed to account
5215 // for the scrollbar
5216 var scrollBarVis = divBodyEl.scrollHeight > divBodyEl.clientHeight;
5217
5218 if ( settings.scrollBarVis !== scrollBarVis && settings.scrollBarVis !== undefined ) {
5219 settings.scrollBarVis = scrollBarVis;
5220 _fnAdjustColumnSizing( settings );
5221 return; // adjust column sizing will call this function again
5222 }
5223 else {
5224 settings.scrollBarVis = scrollBarVis;
5225 }
5226
5227 /*
5228 * 1. Re-create the table inside the scrolling div
5229 */
5230
5231 // Remove the old minimised thead and tfoot elements in the inner table
5232 table.children('thead, tfoot').remove();
5233
5234 if ( footer ) {
5235 footerCopy = footer.clone().prependTo( table );
5236 footerTrgEls = footer.find('tr'); // the original tfoot is in its own table and must be sized
5237 footerSrcEls = footerCopy.find('tr');
5238 }
5239
5240 // Clone the current header and footer elements and then place it into the inner table
5241 headerCopy = header.clone().prependTo( table );
5242 headerTrgEls = header.find('tr'); // original header is in its own table
5243 headerSrcEls = headerCopy.find('tr');
5244 headerCopy.find('th, td').removeAttr('tabindex');
5245
5246
5247 /*
5248 * 2. Take live measurements from the DOM - do not alter the DOM itself!
5249 */
5250
5251 // Remove old sizing and apply the calculated column widths
5252 // Get the unique column headers in the newly created (cloned) header. We want to apply the
5253 // calculated sizes to this header
5254 if ( ! scrollX )
5255 {
5256 divBodyStyle.width = '100%';
5257 divHeader[0].style.width = '100%';
5258 }
5259
5260 $.each( _fnGetUniqueThs( settings, headerCopy ), function ( i, el ) {
5261 idx = _fnVisibleToColumnIndex( settings, i );
5262 el.style.width = settings.aoColumns[idx].sWidth;
5263 } );
5264
5265 if ( footer ) {
5266 _fnApplyToChildren( function(n) {
5267 n.style.width = "";
5268 }, footerSrcEls );
5269 }
5270
5271 // Size the table as a whole
5272 sanityWidth = table.outerWidth();
5273 if ( scrollX === "" ) {
5274 // No x scrolling
5275 tableStyle.width = "100%";
5276
5277 // IE7 will make the width of the table when 100% include the scrollbar
5278 // - which is shouldn't. When there is a scrollbar we need to take this
5279 // into account.
5280 if ( ie67 && (table.find('tbody').height() > divBodyEl.offsetHeight ||
5281 divBody.css('overflow-y') == "scroll")
5282 ) {
5283 tableStyle.width = _fnStringToCss( table.outerWidth() - barWidth);
5284 }
5285
5286 // Recalculate the sanity width
5287 sanityWidth = table.outerWidth();
5288 }
5289 else if ( scrollXInner !== "" ) {
5290 // legacy x scroll inner has been given - use it
5291 tableStyle.width = _fnStringToCss(scrollXInner);
5292
5293 // Recalculate the sanity width
5294 sanityWidth = table.outerWidth();
5295 }
5296
5297 // Hidden header should have zero height, so remove padding and borders. Then
5298 // set the width based on the real headers
5299
5300 // Apply all styles in one pass
5301 _fnApplyToChildren( zeroOut, headerSrcEls );
5302
5303 // Read all widths in next pass
5304 _fnApplyToChildren( function(nSizer) {
5305 headerContent.push( nSizer.innerHTML );
5306 headerWidths.push( _fnStringToCss( $(nSizer).css('width') ) );
5307 }, headerSrcEls );
5308
5309 // Apply all widths in final pass
5310 _fnApplyToChildren( function(nToSize, i) {
5311 // Only apply widths to the DataTables detected header cells - this
5312 // prevents complex headers from having contradictory sizes applied
5313 if ( $.inArray( nToSize, dtHeaderCells ) !== -1 ) {
5314 nToSize.style.width = headerWidths[i];
5315 }
5316 }, headerTrgEls );
5317
5318 $(headerSrcEls).height(0);
5319
5320 /* Same again with the footer if we have one */
5321 if ( footer )
5322 {
5323 _fnApplyToChildren( zeroOut, footerSrcEls );
5324
5325 _fnApplyToChildren( function(nSizer) {
5326 footerContent.push( nSizer.innerHTML );
5327 footerWidths.push( _fnStringToCss( $(nSizer).css('width') ) );
5328 }, footerSrcEls );
5329
5330 _fnApplyToChildren( function(nToSize, i) {
5331 nToSize.style.width = footerWidths[i];
5332 }, footerTrgEls );
5333
5334 $(footerSrcEls).height(0);
5335 }
5336
5337
5338 /*
5339 * 3. Apply the measurements
5340 */
5341
5342 // "Hide" the header and footer that we used for the sizing. We need to keep
5343 // the content of the cell so that the width applied to the header and body
5344 // both match, but we want to hide it completely. We want to also fix their
5345 // width to what they currently are
5346 _fnApplyToChildren( function(nSizer, i) {
5347 nSizer.innerHTML = '<div class="dataTables_sizing" style="height:0;overflow:hidden;">'+headerContent[i]+'</div>';
5348 nSizer.style.width = headerWidths[i];
5349 }, headerSrcEls );
5350
5351 if ( footer )
5352 {
5353 _fnApplyToChildren( function(nSizer, i) {
5354 nSizer.innerHTML = '<div class="dataTables_sizing" style="height:0;overflow:hidden;">'+footerContent[i]+'</div>';
5355 nSizer.style.width = footerWidths[i];
5356 }, footerSrcEls );
5357 }
5358
5359 // Sanity check that the table is of a sensible width. If not then we are going to get
5360 // misalignment - try to prevent this by not allowing the table to shrink below its min width
5361 if ( table.outerWidth() < sanityWidth )
5362 {
5363 // The min width depends upon if we have a vertical scrollbar visible or not */
5364 correction = ((divBodyEl.scrollHeight > divBodyEl.offsetHeight ||
5365 divBody.css('overflow-y') == "scroll")) ?
5366 sanityWidth+barWidth :
5367 sanityWidth;
5368
5369 // IE6/7 are a law unto themselves...
5370 if ( ie67 && (divBodyEl.scrollHeight >
5371 divBodyEl.offsetHeight || divBody.css('overflow-y') == "scroll")
5372 ) {
5373 tableStyle.width = _fnStringToCss( correction-barWidth );
5374 }
5375
5376 // And give the user a warning that we've stopped the table getting too small
5377 if ( scrollX === "" || scrollXInner !== "" ) {
5378 _fnLog( settings, 1, 'Possible column misalignment', 6 );
5379 }
5380 }
5381 else
5382 {
5383 correction = '100%';
5384 }
5385
5386 // Apply to the container elements
5387 divBodyStyle.width = _fnStringToCss( correction );
5388 divHeaderStyle.width = _fnStringToCss( correction );
5389
5390 if ( footer ) {
5391 settings.nScrollFoot.style.width = _fnStringToCss( correction );
5392 }
5393
5394
5395 /*
5396 * 4. Clean up
5397 */
5398 if ( ! scrollY ) {
5399 /* IE7< puts a vertical scrollbar in place (when it shouldn't be) due to subtracting
5400 * the scrollbar height from the visible display, rather than adding it on. We need to
5401 * set the height in order to sort this. Don't want to do it in any other browsers.
5402 */
5403 if ( ie67 ) {
5404 divBodyStyle.height = _fnStringToCss( tableEl.offsetHeight+barWidth );
5405 }
5406 }
5407
5408 /* Finally set the width's of the header and footer tables */
5409 var iOuterWidth = table.outerWidth();
5410 divHeaderTable[0].style.width = _fnStringToCss( iOuterWidth );
5411 divHeaderInnerStyle.width = _fnStringToCss( iOuterWidth );
5412
5413 // Figure out if there are scrollbar present - if so then we need a the header and footer to
5414 // provide a bit more space to allow "overflow" scrolling (i.e. past the scrollbar)
5415 var bScrolling = table.height() > divBodyEl.clientHeight || divBody.css('overflow-y') == "scroll";
5416 var padding = 'padding' + (browser.bScrollbarLeft ? 'Left' : 'Right' );
5417 divHeaderInnerStyle[ padding ] = bScrolling ? barWidth+"px" : "0px";
5418
5419 if ( footer ) {
5420 divFooterTable[0].style.width = _fnStringToCss( iOuterWidth );
5421 divFooterInner[0].style.width = _fnStringToCss( iOuterWidth );
5422 divFooterInner[0].style[padding] = bScrolling ? barWidth+"px" : "0px";
5423 }
5424
5425 // Correct DOM ordering for colgroup - comes before the thead
5426 table.children('colgroup').insertBefore( table.children('thead') );
5427
5428 /* Adjust the position of the header in case we loose the y-scrollbar */
5429 divBody.scroll();
5430
5431 // If sorting or filtering has occurred, jump the scrolling back to the top
5432 // only if we aren't holding the position
5433 if ( (settings.bSorted || settings.bFiltered) && ! settings._drawHold ) {
5434 divBodyEl.scrollTop = 0;
5435 }
5436 }
5437
5438
5439
5440 /**
5441 * Apply a given function to the display child nodes of an element array (typically
5442 * TD children of TR rows
5443 * @param {function} fn Method to apply to the objects
5444 * @param array {nodes} an1 List of elements to look through for display children
5445 * @param array {nodes} an2 Another list (identical structure to the first) - optional
5446 * @memberof DataTable#oApi
5447 */
5448 function _fnApplyToChildren( fn, an1, an2 )
5449 {
5450 var index=0, i=0, iLen=an1.length;
5451 var nNode1, nNode2;
5452
5453 while ( i < iLen ) {
5454 nNode1 = an1[i].firstChild;
5455 nNode2 = an2 ? an2[i].firstChild : null;
5456
5457 while ( nNode1 ) {
5458 if ( nNode1.nodeType === 1 ) {
5459 if ( an2 ) {
5460 fn( nNode1, nNode2, index );
5461 }
5462 else {
5463 fn( nNode1, index );
5464 }
5465
5466 index++;
5467 }
5468
5469 nNode1 = nNode1.nextSibling;
5470 nNode2 = an2 ? nNode2.nextSibling : null;
5471 }
5472
5473 i++;
5474 }
5475 }
5476
5477
5478
5479 var __re_html_remove = /<.*?>/g;
5480
5481
5482 /**
5483 * Calculate the width of columns for the table
5484 * @param {object} oSettings dataTables settings object
5485 * @memberof DataTable#oApi
5486 */
5487 function _fnCalculateColumnWidths ( oSettings )
5488 {
5489 var
5490 table = oSettings.nTable,
5491 columns = oSettings.aoColumns,
5492 scroll = oSettings.oScroll,
5493 scrollY = scroll.sY,
5494 scrollX = scroll.sX,
5495 scrollXInner = scroll.sXInner,
5496 columnCount = columns.length,
5497 visibleColumns = _fnGetColumns( oSettings, 'bVisible' ),
5498 headerCells = $('th', oSettings.nTHead),
5499 tableWidthAttr = table.getAttribute('width'), // from DOM element
5500 tableContainer = table.parentNode,
5501 userInputs = false,
5502 i, column, columnIdx, width, outerWidth,
5503 browser = oSettings.oBrowser,
5504 ie67 = browser.bScrollOversize;
5505
5506 var styleWidth = table.style.width;
5507 if ( styleWidth && styleWidth.indexOf('%') !== -1 ) {
5508 tableWidthAttr = styleWidth;
5509 }
5510
5511 /* Convert any user input sizes into pixel sizes */
5512 for ( i=0 ; i<visibleColumns.length ; i++ ) {
5513 column = columns[ visibleColumns[i] ];
5514
5515 if ( column.sWidth !== null ) {
5516 column.sWidth = _fnConvertToWidth( column.sWidthOrig, tableContainer );
5517
5518 userInputs = true;
5519 }
5520 }
5521
5522 /* If the number of columns in the DOM equals the number that we have to
5523 * process in DataTables, then we can use the offsets that are created by
5524 * the web- browser. No custom sizes can be set in order for this to happen,
5525 * nor scrolling used
5526 */
5527 if ( ie67 || ! userInputs && ! scrollX && ! scrollY &&
5528 columnCount == _fnVisbleColumns( oSettings ) &&
5529 columnCount == headerCells.length
5530 ) {
5531 for ( i=0 ; i<columnCount ; i++ ) {
5532 var colIdx = _fnVisibleToColumnIndex( oSettings, i );
5533
5534 if ( colIdx !== null ) {
5535 columns[ colIdx ].sWidth = _fnStringToCss( headerCells.eq(i).width() );
5536 }
5537 }
5538 }
5539 else
5540 {
5541 // Otherwise construct a single row, worst case, table with the widest
5542 // node in the data, assign any user defined widths, then insert it into
5543 // the DOM and allow the browser to do all the hard work of calculating
5544 // table widths
5545 var tmpTable = $(table).clone() // don't use cloneNode - IE8 will remove events on the main table
5546 .css( 'visibility', 'hidden' )
5547 .removeAttr( 'id' );
5548
5549 // Clean up the table body
5550 tmpTable.find('tbody tr').remove();
5551 var tr = $('<tr/>').appendTo( tmpTable.find('tbody') );
5552
5553 // Clone the table header and footer - we can't use the header / footer
5554 // from the cloned table, since if scrolling is active, the table's
5555 // real header and footer are contained in different table tags
5556 tmpTable.find('thead, tfoot').remove();
5557 tmpTable
5558 .append( $(oSettings.nTHead).clone() )
5559 .append( $(oSettings.nTFoot).clone() );
5560
5561 // Remove any assigned widths from the footer (from scrolling)
5562 tmpTable.find('tfoot th, tfoot td').css('width', '');
5563
5564 // Apply custom sizing to the cloned header
5565 headerCells = _fnGetUniqueThs( oSettings, tmpTable.find('thead')[0] );
5566
5567 for ( i=0 ; i<visibleColumns.length ; i++ ) {
5568 column = columns[ visibleColumns[i] ];
5569
5570 headerCells[i].style.width = column.sWidthOrig !== null && column.sWidthOrig !== '' ?
5571 _fnStringToCss( column.sWidthOrig ) :
5572 '';
5573
5574 // For scrollX we need to force the column width otherwise the
5575 // browser will collapse it. If this width is smaller than the
5576 // width the column requires, then it will have no effect
5577 if ( column.sWidthOrig && scrollX ) {
5578 $( headerCells[i] ).append( $('<div/>').css( {
5579 width: column.sWidthOrig,
5580 margin: 0,
5581 padding: 0,
5582 border: 0,
5583 height: 1
5584 } ) );
5585 }
5586 }
5587
5588 // Find the widest cell for each column and put it into the table
5589 if ( oSettings.aoData.length ) {
5590 for ( i=0 ; i<visibleColumns.length ; i++ ) {
5591 columnIdx = visibleColumns[i];
5592 column = columns[ columnIdx ];
5593
5594 $( _fnGetWidestNode( oSettings, columnIdx ) )
5595 .clone( false )
5596 .append( column.sContentPadding )
5597 .appendTo( tr );
5598 }
5599 }
5600
5601 // Tidy the temporary table - remove name attributes so there aren't
5602 // duplicated in the dom (radio elements for example)
5603 $('[name]', tmpTable).removeAttr('name');
5604
5605 // Table has been built, attach to the document so we can work with it.
5606 // A holding element is used, positioned at the top of the container
5607 // with minimal height, so it has no effect on if the container scrolls
5608 // or not. Otherwise it might trigger scrolling when it actually isn't
5609 // needed
5610 var holder = $('<div/>').css( scrollX || scrollY ?
5611 {
5612 position: 'absolute',
5613 top: 0,
5614 left: 0,
5615 height: 1,
5616 right: 0,
5617 overflow: 'hidden'
5618 } :
5619 {}
5620 )
5621 .append( tmpTable )
5622 .appendTo( tableContainer );
5623
5624 // When scrolling (X or Y) we want to set the width of the table as
5625 // appropriate. However, when not scrolling leave the table width as it
5626 // is. This results in slightly different, but I think correct behaviour
5627 if ( scrollX && scrollXInner ) {
5628 tmpTable.width( scrollXInner );
5629 }
5630 else if ( scrollX ) {
5631 tmpTable.css( 'width', 'auto' );
5632 tmpTable.removeAttr('width');
5633
5634 // If there is no width attribute or style, then allow the table to
5635 // collapse
5636 if ( tmpTable.width() < tableContainer.clientWidth && tableWidthAttr ) {
5637 tmpTable.width( tableContainer.clientWidth );
5638 }
5639 }
5640 else if ( scrollY ) {
5641 tmpTable.width( tableContainer.clientWidth );
5642 }
5643 else if ( tableWidthAttr ) {
5644 tmpTable.width( tableWidthAttr );
5645 }
5646
5647 // Get the width of each column in the constructed table - we need to
5648 // know the inner width (so it can be assigned to the other table's
5649 // cells) and the outer width so we can calculate the full width of the
5650 // table. This is safe since DataTables requires a unique cell for each
5651 // column, but if ever a header can span multiple columns, this will
5652 // need to be modified.
5653 var total = 0;
5654 for ( i=0 ; i<visibleColumns.length ; i++ ) {
5655 var cell = $(headerCells[i]);
5656 var border = cell.outerWidth() - cell.width();
5657
5658 // Use getBounding... where possible (not IE8-) because it can give
5659 // sub-pixel accuracy, which we then want to round up!
5660 var bounding = browser.bBounding ?
5661 Math.ceil( headerCells[i].getBoundingClientRect().width ) :
5662 cell.outerWidth();
5663
5664 // Total is tracked to remove any sub-pixel errors as the outerWidth
5665 // of the table might not equal the total given here (IE!).
5666 total += bounding;
5667
5668 // Width for each column to use
5669 columns[ visibleColumns[i] ].sWidth = _fnStringToCss( bounding - border );
5670 }
5671
5672 table.style.width = _fnStringToCss( total );
5673
5674 // Finished with the table - ditch it
5675 holder.remove();
5676 }
5677
5678 // If there is a width attr, we want to attach an event listener which
5679 // allows the table sizing to automatically adjust when the window is
5680 // resized. Use the width attr rather than CSS, since we can't know if the
5681 // CSS is a relative value or absolute - DOM read is always px.
5682 if ( tableWidthAttr ) {
5683 table.style.width = _fnStringToCss( tableWidthAttr );
5684 }
5685
5686 if ( (tableWidthAttr || scrollX) && ! oSettings._reszEvt ) {
5687 var bindResize = function () {
5688 $(window).on('resize.DT-'+oSettings.sInstance, _fnThrottle( function () {
5689 _fnAdjustColumnSizing( oSettings );
5690 } ) );
5691 };
5692
5693 // IE6/7 will crash if we bind a resize event handler on page load.
5694 // To be removed in 1.11 which drops IE6/7 support
5695 if ( ie67 ) {
5696 setTimeout( bindResize, 1000 );
5697 }
5698 else {
5699 bindResize();
5700 }
5701
5702 oSettings._reszEvt = true;
5703 }
5704 }
5705
5706
5707 /**
5708 * Throttle the calls to a function. Arguments and context are maintained for
5709 * the throttled function
5710 * @param {function} fn Function to be called
5711 * @param {int} [freq=200] call frequency in mS
5712 * @returns {function} wrapped function
5713 * @memberof DataTable#oApi
5714 */
5715 var _fnThrottle = DataTable.util.throttle;
5716
5717
5718 /**
5719 * Convert a CSS unit width to pixels (e.g. 2em)
5720 * @param {string} width width to be converted
5721 * @param {node} parent parent to get the with for (required for relative widths) - optional
5722 * @returns {int} width in pixels
5723 * @memberof DataTable#oApi
5724 */
5725 function _fnConvertToWidth ( width, parent )
5726 {
5727 if ( ! width ) {
5728 return 0;
5729 }
5730
5731 var n = $('<div/>')
5732 .css( 'width', _fnStringToCss( width ) )
5733 .appendTo( parent || document.body );
5734
5735 var val = n[0].offsetWidth;
5736 n.remove();
5737
5738 return val;
5739 }
5740
5741
5742 /**
5743 * Get the widest node
5744 * @param {object} settings dataTables settings object
5745 * @param {int} colIdx column of interest
5746 * @returns {node} widest table node
5747 * @memberof DataTable#oApi
5748 */
5749 function _fnGetWidestNode( settings, colIdx )
5750 {
5751 var idx = _fnGetMaxLenString( settings, colIdx );
5752 if ( idx < 0 ) {
5753 return null;
5754 }
5755
5756 var data = settings.aoData[ idx ];
5757 return ! data.nTr ? // Might not have been created when deferred rendering
5758 $('<td/>').html( _fnGetCellData( settings, idx, colIdx, 'display' ) )[0] :
5759 data.anCells[ colIdx ];
5760 }
5761
5762
5763 /**
5764 * Get the maximum strlen for each data column
5765 * @param {object} settings dataTables settings object
5766 * @param {int} colIdx column of interest
5767 * @returns {string} max string length for each column
5768 * @memberof DataTable#oApi
5769 */
5770 function _fnGetMaxLenString( settings, colIdx )
5771 {
5772 var s, max=-1, maxIdx = -1;
5773
5774 for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
5775 s = _fnGetCellData( settings, i, colIdx, 'display' )+'';
5776 s = s.replace( __re_html_remove, '' );
5777 s = s.replace( /&nbsp;/g, ' ' );
5778
5779 if ( s.length > max ) {
5780 max = s.length;
5781 maxIdx = i;
5782 }
5783 }
5784
5785 return maxIdx;
5786 }
5787
5788
5789 /**
5790 * Append a CSS unit (only if required) to a string
5791 * @param {string} value to css-ify
5792 * @returns {string} value with css unit
5793 * @memberof DataTable#oApi
5794 */
5795 function _fnStringToCss( s )
5796 {
5797 if ( s === null ) {
5798 return '0px';
5799 }
5800
5801 if ( typeof s == 'number' ) {
5802 return s < 0 ?
5803 '0px' :
5804 s+'px';
5805 }
5806
5807 // Check it has a unit character already
5808 return s.match(/\d$/) ?
5809 s+'px' :
5810 s;
5811 }
5812
5813
5814
5815 function _fnSortFlatten ( settings )
5816 {
5817 var
5818 i, iLen, k, kLen,
5819 aSort = [],
5820 aiOrig = [],
5821 aoColumns = settings.aoColumns,
5822 aDataSort, iCol, sType, srcCol,
5823 fixed = settings.aaSortingFixed,
5824 fixedObj = $.isPlainObject( fixed ),
5825 nestedSort = [],
5826 add = function ( a ) {
5827 if ( a.length && ! $.isArray( a[0] ) ) {
5828 // 1D array
5829 nestedSort.push( a );
5830 }
5831 else {
5832 // 2D array
5833 $.merge( nestedSort, a );
5834 }
5835 };
5836
5837 // Build the sort array, with pre-fix and post-fix options if they have been
5838 // specified
5839 if ( $.isArray( fixed ) ) {
5840 add( fixed );
5841 }
5842
5843 if ( fixedObj && fixed.pre ) {
5844 add( fixed.pre );
5845 }
5846
5847 add( settings.aaSorting );
5848
5849 if (fixedObj && fixed.post ) {
5850 add( fixed.post );
5851 }
5852
5853 for ( i=0 ; i<nestedSort.length ; i++ )
5854 {
5855 srcCol = nestedSort[i][0];
5856 aDataSort = aoColumns[ srcCol ].aDataSort;
5857
5858 for ( k=0, kLen=aDataSort.length ; k<kLen ; k++ )
5859 {
5860 iCol = aDataSort[k];
5861 sType = aoColumns[ iCol ].sType || 'string';
5862
5863 if ( nestedSort[i]._idx === undefined ) {
5864 nestedSort[i]._idx = $.inArray( nestedSort[i][1], aoColumns[iCol].asSorting );
5865 }
5866
5867 aSort.push( {
5868 src: srcCol,
5869 col: iCol,
5870 dir: nestedSort[i][1],
5871 index: nestedSort[i]._idx,
5872 type: sType,
5873 formatter: DataTable.ext.type.order[ sType+"-pre" ]
5874 } );
5875 }
5876 }
5877
5878 return aSort;
5879 }
5880
5881 /**
5882 * Change the order of the table
5883 * @param {object} oSettings dataTables settings object
5884 * @memberof DataTable#oApi
5885 * @todo This really needs split up!
5886 */
5887 function _fnSort ( oSettings )
5888 {
5889 var
5890 i, ien, iLen, j, jLen, k, kLen,
5891 sDataType, nTh,
5892 aiOrig = [],
5893 oExtSort = DataTable.ext.type.order,
5894 aoData = oSettings.aoData,
5895 aoColumns = oSettings.aoColumns,
5896 aDataSort, data, iCol, sType, oSort,
5897 formatters = 0,
5898 sortCol,
5899 displayMaster = oSettings.aiDisplayMaster,
5900 aSort;
5901
5902 // Resolve any column types that are unknown due to addition or invalidation
5903 // @todo Can this be moved into a 'data-ready' handler which is called when
5904 // data is going to be used in the table?
5905 _fnColumnTypes( oSettings );
5906
5907 aSort = _fnSortFlatten( oSettings );
5908
5909 for ( i=0, ien=aSort.length ; i<ien ; i++ ) {
5910 sortCol = aSort[i];
5911
5912 // Track if we can use the fast sort algorithm
5913 if ( sortCol.formatter ) {
5914 formatters++;
5915 }
5916
5917 // Load the data needed for the sort, for each cell
5918 _fnSortData( oSettings, sortCol.col );
5919 }
5920
5921 /* No sorting required if server-side or no sorting array */
5922 if ( _fnDataSource( oSettings ) != 'ssp' && aSort.length !== 0 )
5923 {
5924 // Create a value - key array of the current row positions such that we can use their
5925 // current position during the sort, if values match, in order to perform stable sorting
5926 for ( i=0, iLen=displayMaster.length ; i<iLen ; i++ ) {
5927 aiOrig[ displayMaster[i] ] = i;
5928 }
5929
5930 /* Do the sort - here we want multi-column sorting based on a given data source (column)
5931 * and sorting function (from oSort) in a certain direction. It's reasonably complex to
5932 * follow on it's own, but this is what we want (example two column sorting):
5933 * fnLocalSorting = function(a,b){
5934 * var iTest;
5935 * iTest = oSort['string-asc']('data11', 'data12');
5936 * if (iTest !== 0)
5937 * return iTest;
5938 * iTest = oSort['numeric-desc']('data21', 'data22');
5939 * if (iTest !== 0)
5940 * return iTest;
5941 * return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
5942 * }
5943 * Basically we have a test for each sorting column, if the data in that column is equal,
5944 * test the next column. If all columns match, then we use a numeric sort on the row
5945 * positions in the original data array to provide a stable sort.
5946 *
5947 * Note - I know it seems excessive to have two sorting methods, but the first is around
5948 * 15% faster, so the second is only maintained for backwards compatibility with sorting
5949 * methods which do not have a pre-sort formatting function.
5950 */
5951 if ( formatters === aSort.length ) {
5952 // All sort types have formatting functions
5953 displayMaster.sort( function ( a, b ) {
5954 var
5955 x, y, k, test, sort,
5956 len=aSort.length,
5957 dataA = aoData[a]._aSortData,
5958 dataB = aoData[b]._aSortData;
5959
5960 for ( k=0 ; k<len ; k++ ) {
5961 sort = aSort[k];
5962
5963 x = dataA[ sort.col ];
5964 y = dataB[ sort.col ];
5965
5966 test = x<y ? -1 : x>y ? 1 : 0;
5967 if ( test !== 0 ) {
5968 return sort.dir === 'asc' ? test : -test;
5969 }
5970 }
5971
5972 x = aiOrig[a];
5973 y = aiOrig[b];
5974 return x<y ? -1 : x>y ? 1 : 0;
5975 } );
5976 }
5977 else {
5978 // Depreciated - remove in 1.11 (providing a plug-in option)
5979 // Not all sort types have formatting methods, so we have to call their sorting
5980 // methods.
5981 displayMaster.sort( function ( a, b ) {
5982 var
5983 x, y, k, l, test, sort, fn,
5984 len=aSort.length,
5985 dataA = aoData[a]._aSortData,
5986 dataB = aoData[b]._aSortData;
5987
5988 for ( k=0 ; k<len ; k++ ) {
5989 sort = aSort[k];
5990
5991 x = dataA[ sort.col ];
5992 y = dataB[ sort.col ];
5993
5994 fn = oExtSort[ sort.type+"-"+sort.dir ] || oExtSort[ "string-"+sort.dir ];
5995 test = fn( x, y );
5996 if ( test !== 0 ) {
5997 return test;
5998 }
5999 }
6000
6001 x = aiOrig[a];
6002 y = aiOrig[b];
6003 return x<y ? -1 : x>y ? 1 : 0;
6004 } );
6005 }
6006 }
6007
6008 /* Tell the draw function that we have sorted the data */
6009 oSettings.bSorted = true;
6010 }
6011
6012
6013 function _fnSortAria ( settings )
6014 {
6015 var label;
6016 var nextSort;
6017 var columns = settings.aoColumns;
6018 var aSort = _fnSortFlatten( settings );
6019 var oAria = settings.oLanguage.oAria;
6020
6021 // ARIA attributes - need to loop all columns, to update all (removing old
6022 // attributes as needed)
6023 for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
6024 {
6025 var col = columns[i];
6026 var asSorting = col.asSorting;
6027 var sTitle = col.sTitle.replace( /<.*?>/g, "" );
6028 var th = col.nTh;
6029
6030 // IE7 is throwing an error when setting these properties with jQuery's
6031 // attr() and removeAttr() methods...
6032 th.removeAttribute('aria-sort');
6033
6034 /* In ARIA only the first sorting column can be marked as sorting - no multi-sort option */
6035 if ( col.bSortable ) {
6036 if ( aSort.length > 0 && aSort[0].col == i ) {
6037 th.setAttribute('aria-sort', aSort[0].dir=="asc" ? "ascending" : "descending" );
6038 nextSort = asSorting[ aSort[0].index+1 ] || asSorting[0];
6039 }
6040 else {
6041 nextSort = asSorting[0];
6042 }
6043
6044 label = sTitle + ( nextSort === "asc" ?
6045 oAria.sSortAscending :
6046 oAria.sSortDescending
6047 );
6048 }
6049 else {
6050 label = sTitle;
6051 }
6052
6053 th.setAttribute('aria-label', label);
6054 }
6055 }
6056
6057
6058 /**
6059 * Function to run on user sort request
6060 * @param {object} settings dataTables settings object
6061 * @param {node} attachTo node to attach the handler to
6062 * @param {int} colIdx column sorting index
6063 * @param {boolean} [append=false] Append the requested sort to the existing
6064 * sort if true (i.e. multi-column sort)
6065 * @param {function} [callback] callback function
6066 * @memberof DataTable#oApi
6067 */
6068 function _fnSortListener ( settings, colIdx, append, callback )
6069 {
6070 var col = settings.aoColumns[ colIdx ];
6071 var sorting = settings.aaSorting;
6072 var asSorting = col.asSorting;
6073 var nextSortIdx;
6074 var next = function ( a, overflow ) {
6075 var idx = a._idx;
6076 if ( idx === undefined ) {
6077 idx = $.inArray( a[1], asSorting );
6078 }
6079
6080 return idx+1 < asSorting.length ?
6081 idx+1 :
6082 overflow ?
6083 null :
6084 0;
6085 };
6086
6087 // Convert to 2D array if needed
6088 if ( typeof sorting[0] === 'number' ) {
6089 sorting = settings.aaSorting = [ sorting ];
6090 }
6091
6092 // If appending the sort then we are multi-column sorting
6093 if ( append && settings.oFeatures.bSortMulti ) {
6094 // Are we already doing some kind of sort on this column?
6095 var sortIdx = $.inArray( colIdx, _pluck(sorting, '0') );
6096
6097 if ( sortIdx !== -1 ) {
6098 // Yes, modify the sort
6099 nextSortIdx = next( sorting[sortIdx], true );
6100
6101 if ( nextSortIdx === null && sorting.length === 1 ) {
6102 nextSortIdx = 0; // can't remove sorting completely
6103 }
6104
6105 if ( nextSortIdx === null ) {
6106 sorting.splice( sortIdx, 1 );
6107 }
6108 else {
6109 sorting[sortIdx][1] = asSorting[ nextSortIdx ];
6110 sorting[sortIdx]._idx = nextSortIdx;
6111 }
6112 }
6113 else {
6114 // No sort on this column yet
6115 sorting.push( [ colIdx, asSorting[0], 0 ] );
6116 sorting[sorting.length-1]._idx = 0;
6117 }
6118 }
6119 else if ( sorting.length && sorting[0][0] == colIdx ) {
6120 // Single column - already sorting on this column, modify the sort
6121 nextSortIdx = next( sorting[0] );
6122
6123 sorting.length = 1;
6124 sorting[0][1] = asSorting[ nextSortIdx ];
6125 sorting[0]._idx = nextSortIdx;
6126 }
6127 else {
6128 // Single column - sort only on this column
6129 sorting.length = 0;
6130 sorting.push( [ colIdx, asSorting[0] ] );
6131 sorting[0]._idx = 0;
6132 }
6133
6134 // Run the sort by calling a full redraw
6135 _fnReDraw( settings );
6136
6137 // callback used for async user interaction
6138 if ( typeof callback == 'function' ) {
6139 callback( settings );
6140 }
6141 }
6142
6143
6144 /**
6145 * Attach a sort handler (click) to a node
6146 * @param {object} settings dataTables settings object
6147 * @param {node} attachTo node to attach the handler to
6148 * @param {int} colIdx column sorting index
6149 * @param {function} [callback] callback function
6150 * @memberof DataTable#oApi
6151 */
6152 function _fnSortAttachListener ( settings, attachTo, colIdx, callback )
6153 {
6154 var col = settings.aoColumns[ colIdx ];
6155
6156 _fnBindAction( attachTo, {}, function (e) {
6157 /* If the column is not sortable - don't to anything */
6158 if ( col.bSortable === false ) {
6159 return;
6160 }
6161
6162 // If processing is enabled use a timeout to allow the processing
6163 // display to be shown - otherwise to it synchronously
6164 if ( settings.oFeatures.bProcessing ) {
6165 _fnProcessingDisplay( settings, true );
6166
6167 setTimeout( function() {
6168 _fnSortListener( settings, colIdx, e.shiftKey, callback );
6169
6170 // In server-side processing, the draw callback will remove the
6171 // processing display
6172 if ( _fnDataSource( settings ) !== 'ssp' ) {
6173 _fnProcessingDisplay( settings, false );
6174 }
6175 }, 0 );
6176 }
6177 else {
6178 _fnSortListener( settings, colIdx, e.shiftKey, callback );
6179 }
6180 } );
6181 }
6182
6183
6184 /**
6185 * Set the sorting classes on table's body, Note: it is safe to call this function
6186 * when bSort and bSortClasses are false
6187 * @param {object} oSettings dataTables settings object
6188 * @memberof DataTable#oApi
6189 */
6190 function _fnSortingClasses( settings )
6191 {
6192 var oldSort = settings.aLastSort;
6193 var sortClass = settings.oClasses.sSortColumn;
6194 var sort = _fnSortFlatten( settings );
6195 var features = settings.oFeatures;
6196 var i, ien, colIdx;
6197
6198 if ( features.bSort && features.bSortClasses ) {
6199 // Remove old sorting classes
6200 for ( i=0, ien=oldSort.length ; i<ien ; i++ ) {
6201 colIdx = oldSort[i].src;
6202
6203 // Remove column sorting
6204 $( _pluck( settings.aoData, 'anCells', colIdx ) )
6205 .removeClass( sortClass + (i<2 ? i+1 : 3) );
6206 }
6207
6208 // Add new column sorting
6209 for ( i=0, ien=sort.length ; i<ien ; i++ ) {
6210 colIdx = sort[i].src;
6211
6212 $( _pluck( settings.aoData, 'anCells', colIdx ) )
6213 .addClass( sortClass + (i<2 ? i+1 : 3) );
6214 }
6215 }
6216
6217 settings.aLastSort = sort;
6218 }
6219
6220
6221 // Get the data to sort a column, be it from cache, fresh (populating the
6222 // cache), or from a sort formatter
6223 function _fnSortData( settings, idx )
6224 {
6225 // Custom sorting function - provided by the sort data type
6226 var column = settings.aoColumns[ idx ];
6227 var customSort = DataTable.ext.order[ column.sSortDataType ];
6228 var customData;
6229
6230 if ( customSort ) {
6231 customData = customSort.call( settings.oInstance, settings, idx,
6232 _fnColumnIndexToVisible( settings, idx )
6233 );
6234 }
6235
6236 // Use / populate cache
6237 var row, cellData;
6238 var formatter = DataTable.ext.type.order[ column.sType+"-pre" ];
6239
6240 for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
6241 row = settings.aoData[i];
6242
6243 if ( ! row._aSortData ) {
6244 row._aSortData = [];
6245 }
6246
6247 if ( ! row._aSortData[idx] || customSort ) {
6248 cellData = customSort ?
6249 customData[i] : // If there was a custom sort function, use data from there
6250 _fnGetCellData( settings, i, idx, 'sort' );
6251
6252 row._aSortData[ idx ] = formatter ?
6253 formatter( cellData ) :
6254 cellData;
6255 }
6256 }
6257 }
6258
6259
6260
6261 /**
6262 * Save the state of a table
6263 * @param {object} oSettings dataTables settings object
6264 * @memberof DataTable#oApi
6265 */
6266 function _fnSaveState ( settings )
6267 {
6268 if ( !settings.oFeatures.bStateSave || settings.bDestroying )
6269 {
6270 return;
6271 }
6272
6273 /* Store the interesting variables */
6274 var state = {
6275 time: +new Date(),
6276 start: settings._iDisplayStart,
6277 length: settings._iDisplayLength,
6278 order: $.extend( true, [], settings.aaSorting ),
6279 search: _fnSearchToCamel( settings.oPreviousSearch ),
6280 columns: $.map( settings.aoColumns, function ( col, i ) {
6281 return {
6282 visible: col.bVisible,
6283 search: _fnSearchToCamel( settings.aoPreSearchCols[i] )
6284 };
6285 } )
6286 };
6287
6288 _fnCallbackFire( settings, "aoStateSaveParams", 'stateSaveParams', [settings, state] );
6289
6290 settings.oSavedState = state;
6291 settings.fnStateSaveCallback.call( settings.oInstance, settings, state );
6292 }
6293
6294
6295 /**
6296 * Attempt to load a saved table state
6297 * @param {object} oSettings dataTables settings object
6298 * @param {object} oInit DataTables init object so we can override settings
6299 * @param {function} callback Callback to execute when the state has been loaded
6300 * @memberof DataTable#oApi
6301 */
6302 function _fnLoadState ( settings, oInit, callback )
6303 {
6304 var i, ien;
6305 var columns = settings.aoColumns;
6306 var loaded = function ( s ) {
6307 if ( ! s || ! s.time ) {
6308 callback();
6309 return;
6310 }
6311
6312 // Allow custom and plug-in manipulation functions to alter the saved data set and
6313 // cancelling of loading by returning false
6314 var abStateLoad = _fnCallbackFire( settings, 'aoStateLoadParams', 'stateLoadParams', [settings, state] );
6315 if ( $.inArray( false, abStateLoad ) !== -1 ) {
6316 callback();
6317 return;
6318 }
6319
6320 // Reject old data
6321 var duration = settings.iStateDuration;
6322 if ( duration > 0 && s.time < +new Date() - (duration*1000) ) {
6323 callback();
6324 return;
6325 }
6326
6327 // Number of columns have changed - all bets are off, no restore of settings
6328 if ( s.columns && columns.length !== s.columns.length ) {
6329 callback();
6330 return;
6331 }
6332
6333 // Store the saved state so it might be accessed at any time
6334 settings.oLoadedState = $.extend( true, {}, state );
6335
6336 // Restore key features - todo - for 1.11 this needs to be done by
6337 // subscribed events
6338 if ( s.start !== undefined ) {
6339 settings._iDisplayStart = s.start;
6340 settings.iInitDisplayStart = s.start;
6341 }
6342 if ( s.length !== undefined ) {
6343 settings._iDisplayLength = s.length;
6344 }
6345
6346 // Order
6347 if ( s.order !== undefined ) {
6348 settings.aaSorting = [];
6349 $.each( s.order, function ( i, col ) {
6350 settings.aaSorting.push( col[0] >= columns.length ?
6351 [ 0, col[1] ] :
6352 col
6353 );
6354 } );
6355 }
6356
6357 // Search
6358 if ( s.search !== undefined ) {
6359 $.extend( settings.oPreviousSearch, _fnSearchToHung( s.search ) );
6360 }
6361
6362 // Columns
6363 //
6364 if ( s.columns ) {
6365 for ( i=0, ien=s.columns.length ; i<ien ; i++ ) {
6366 var col = s.columns[i];
6367
6368 // Visibility
6369 if ( col.visible !== undefined ) {
6370 columns[i].bVisible = col.visible;
6371 }
6372
6373 // Search
6374 if ( col.search !== undefined ) {
6375 $.extend( settings.aoPreSearchCols[i], _fnSearchToHung( col.search ) );
6376 }
6377 }
6378 }
6379
6380 _fnCallbackFire( settings, 'aoStateLoaded', 'stateLoaded', [settings, state] );
6381 callback();
6382 }
6383
6384 if ( ! settings.oFeatures.bStateSave ) {
6385 callback();
6386 return;
6387 }
6388
6389 var state = settings.fnStateLoadCallback.call( settings.oInstance, settings, loaded );
6390
6391 if ( state !== undefined ) {
6392 loaded( state );
6393 }
6394 // otherwise, wait for the loaded callback to be executed
6395 }
6396
6397
6398 /**
6399 * Return the settings object for a particular table
6400 * @param {node} table table we are using as a dataTable
6401 * @returns {object} Settings object - or null if not found
6402 * @memberof DataTable#oApi
6403 */
6404 function _fnSettingsFromNode ( table )
6405 {
6406 var settings = DataTable.settings;
6407 var idx = $.inArray( table, _pluck( settings, 'nTable' ) );
6408
6409 return idx !== -1 ?
6410 settings[ idx ] :
6411 null;
6412 }
6413
6414
6415 /**
6416 * Log an error message
6417 * @param {object} settings dataTables settings object
6418 * @param {int} level log error messages, or display them to the user
6419 * @param {string} msg error message
6420 * @param {int} tn Technical note id to get more information about the error.
6421 * @memberof DataTable#oApi
6422 */
6423 function _fnLog( settings, level, msg, tn )
6424 {
6425 msg = 'DataTables warning: '+
6426 (settings ? 'table id='+settings.sTableId+' - ' : '')+msg;
6427
6428 if ( tn ) {
6429 msg += '. For more information about this error, please see '+
6430 'http://datatables.net/tn/'+tn;
6431 }
6432
6433 if ( ! level ) {
6434 // Backwards compatibility pre 1.10
6435 var ext = DataTable.ext;
6436 var type = ext.sErrMode || ext.errMode;
6437
6438 if ( settings ) {
6439 _fnCallbackFire( settings, null, 'error', [ settings, tn, msg ] );
6440 }
6441
6442 if ( type == 'alert' ) {
6443 alert( msg );
6444 }
6445 else if ( type == 'throw' ) {
6446 throw new Error(msg);
6447 }
6448 else if ( typeof type == 'function' ) {
6449 type( settings, tn, msg );
6450 }
6451 }
6452 else if ( window.console && console.log ) {
6453 console.log( msg );
6454 }
6455 }
6456
6457
6458 /**
6459 * See if a property is defined on one object, if so assign it to the other object
6460 * @param {object} ret target object
6461 * @param {object} src source object
6462 * @param {string} name property
6463 * @param {string} [mappedName] name to map too - optional, name used if not given
6464 * @memberof DataTable#oApi
6465 */
6466 function _fnMap( ret, src, name, mappedName )
6467 {
6468 if ( $.isArray( name ) ) {
6469 $.each( name, function (i, val) {
6470 if ( $.isArray( val ) ) {
6471 _fnMap( ret, src, val[0], val[1] );
6472 }
6473 else {
6474 _fnMap( ret, src, val );
6475 }
6476 } );
6477
6478 return;
6479 }
6480
6481 if ( mappedName === undefined ) {
6482 mappedName = name;
6483 }
6484
6485 if ( src[name] !== undefined ) {
6486 ret[mappedName] = src[name];
6487 }
6488 }
6489
6490
6491 /**
6492 * Extend objects - very similar to jQuery.extend, but deep copy objects, and
6493 * shallow copy arrays. The reason we need to do this, is that we don't want to
6494 * deep copy array init values (such as aaSorting) since the dev wouldn't be
6495 * able to override them, but we do want to deep copy arrays.
6496 * @param {object} out Object to extend
6497 * @param {object} extender Object from which the properties will be applied to
6498 * out
6499 * @param {boolean} breakRefs If true, then arrays will be sliced to take an
6500 * independent copy with the exception of the `data` or `aaData` parameters
6501 * if they are present. This is so you can pass in a collection to
6502 * DataTables and have that used as your data source without breaking the
6503 * references
6504 * @returns {object} out Reference, just for convenience - out === the return.
6505 * @memberof DataTable#oApi
6506 * @todo This doesn't take account of arrays inside the deep copied objects.
6507 */
6508 function _fnExtend( out, extender, breakRefs )
6509 {
6510 var val;
6511
6512 for ( var prop in extender ) {
6513 if ( extender.hasOwnProperty(prop) ) {
6514 val = extender[prop];
6515
6516 if ( $.isPlainObject( val ) ) {
6517 if ( ! $.isPlainObject( out[prop] ) ) {
6518 out[prop] = {};
6519 }
6520 $.extend( true, out[prop], val );
6521 }
6522 else if ( breakRefs && prop !== 'data' && prop !== 'aaData' && $.isArray(val) ) {
6523 out[prop] = val.slice();
6524 }
6525 else {
6526 out[prop] = val;
6527 }
6528 }
6529 }
6530
6531 return out;
6532 }
6533
6534
6535 /**
6536 * Bind an event handers to allow a click or return key to activate the callback.
6537 * This is good for accessibility since a return on the keyboard will have the
6538 * same effect as a click, if the element has focus.
6539 * @param {element} n Element to bind the action to
6540 * @param {object} oData Data object to pass to the triggered function
6541 * @param {function} fn Callback function for when the event is triggered
6542 * @memberof DataTable#oApi
6543 */
6544 function _fnBindAction( n, oData, fn )
6545 {
6546 $(n)
6547 .on( 'click.DT', oData, function (e) {
6548 n.blur(); // Remove focus outline for mouse users
6549 fn(e);
6550 } )
6551 .on( 'keypress.DT', oData, function (e){
6552 if ( e.which === 13 ) {
6553 e.preventDefault();
6554 fn(e);
6555 }
6556 } )
6557 .on( 'selectstart.DT', function () {
6558 /* Take the brutal approach to cancelling text selection */
6559 return false;
6560 } );
6561 }
6562
6563
6564 /**
6565 * Register a callback function. Easily allows a callback function to be added to
6566 * an array store of callback functions that can then all be called together.
6567 * @param {object} oSettings dataTables settings object
6568 * @param {string} sStore Name of the array storage for the callbacks in oSettings
6569 * @param {function} fn Function to be called back
6570 * @param {string} sName Identifying name for the callback (i.e. a label)
6571 * @memberof DataTable#oApi
6572 */
6573 function _fnCallbackReg( oSettings, sStore, fn, sName )
6574 {
6575 if ( fn )
6576 {
6577 oSettings[sStore].push( {
6578 "fn": fn,
6579 "sName": sName
6580 } );
6581 }
6582 }
6583
6584
6585 /**
6586 * Fire callback functions and trigger events. Note that the loop over the
6587 * callback array store is done backwards! Further note that you do not want to
6588 * fire off triggers in time sensitive applications (for example cell creation)
6589 * as its slow.
6590 * @param {object} settings dataTables settings object
6591 * @param {string} callbackArr Name of the array storage for the callbacks in
6592 * oSettings
6593 * @param {string} eventName Name of the jQuery custom event to trigger. If
6594 * null no trigger is fired
6595 * @param {array} args Array of arguments to pass to the callback function /
6596 * trigger
6597 * @memberof DataTable#oApi
6598 */
6599 function _fnCallbackFire( settings, callbackArr, eventName, args )
6600 {
6601 var ret = [];
6602
6603 if ( callbackArr ) {
6604 ret = $.map( settings[callbackArr].slice().reverse(), function (val, i) {
6605 return val.fn.apply( settings.oInstance, args );
6606 } );
6607 }
6608
6609 if ( eventName !== null ) {
6610 var e = $.Event( eventName+'.dt' );
6611
6612 $(settings.nTable).trigger( e, args );
6613
6614 ret.push( e.result );
6615 }
6616
6617 return ret;
6618 }
6619
6620
6621 function _fnLengthOverflow ( settings )
6622 {
6623 var
6624 start = settings._iDisplayStart,
6625 end = settings.fnDisplayEnd(),
6626 len = settings._iDisplayLength;
6627
6628 /* If we have space to show extra rows (backing up from the end point - then do so */
6629 if ( start >= end )
6630 {
6631 start = end - len;
6632 }
6633
6634 // Keep the start record on the current page
6635 start -= (start % len);
6636
6637 if ( len === -1 || start < 0 )
6638 {
6639 start = 0;
6640 }
6641
6642 settings._iDisplayStart = start;
6643 }
6644
6645
6646 function _fnRenderer( settings, type )
6647 {
6648 var renderer = settings.renderer;
6649 var host = DataTable.ext.renderer[type];
6650
6651 if ( $.isPlainObject( renderer ) && renderer[type] ) {
6652 // Specific renderer for this type. If available use it, otherwise use
6653 // the default.
6654 return host[renderer[type]] || host._;
6655 }
6656 else if ( typeof renderer === 'string' ) {
6657 // Common renderer - if there is one available for this type use it,
6658 // otherwise use the default
6659 return host[renderer] || host._;
6660 }
6661
6662 // Use the default
6663 return host._;
6664 }
6665
6666
6667 /**
6668 * Detect the data source being used for the table. Used to simplify the code
6669 * a little (ajax) and to make it compress a little smaller.
6670 *
6671 * @param {object} settings dataTables settings object
6672 * @returns {string} Data source
6673 * @memberof DataTable#oApi
6674 */
6675 function _fnDataSource ( settings )
6676 {
6677 if ( settings.oFeatures.bServerSide ) {
6678 return 'ssp';
6679 }
6680 else if ( settings.ajax || settings.sAjaxSource ) {
6681 return 'ajax';
6682 }
6683 return 'dom';
6684 }
6685
6686
6687
6688
6519 6689 /**
6520 6690 * Computed structure of the DataTables API, defined by the options passed to
6521 6691 * `DataTable.Api.register()` when building the API.
@@ -6553,8 +6723,8 b''
6553 6723 * @ignore
6554 6724 */
6555 6725 var __apiStruct = [];
6556
6557
6726
6727
6558 6728 /**
6559 6729 * `Array.prototype` reference.
6560 6730 *
@@ -6562,8 +6732,8 b''
6562 6732 * @ignore
6563 6733 */
6564 6734 var __arrayProto = Array.prototype;
6565
6566
6735
6736
6567 6737 /**
6568 6738 * Abstraction for `context` parameter of the `Api` constructor to allow it to
6569 6739 * take several different forms for ease of use.
@@ -6591,7 +6761,7 b''
6591 6761 var tables = $.map( settings, function (el, i) {
6592 6762 return el.nTable;
6593 6763 } );
6594
6764
6595 6765 if ( ! mixed ) {
6596 6766 return [];
6597 6767 }
@@ -6615,7 +6785,7 b''
6615 6785 // jQuery object (also DataTables instance)
6616 6786 jq = mixed;
6617 6787 }
6618
6788
6619 6789 if ( jq ) {
6620 6790 return jq.map( function(i) {
6621 6791 idx = $.inArray( this, tables );
@@ -6623,8 +6793,8 b''
6623 6793 } ).toArray();
6624 6794 }
6625 6795 };
6626
6627
6796
6797
6628 6798 /**
6629 6799 * DataTables API class - used to control and interface with one or more
6630 6800 * DataTables enhanced tables.
@@ -6681,20 +6851,18 b''
6681 6851 */
6682 6852 _Api = function ( context, data )
6683 6853 {
6684 if ( ! this instanceof _Api ) {
6685 throw 'DT API must be constructed as a new object';
6686 // or should it do the 'new' for the caller?
6687 // return new _Api.apply( this, arguments );
6688 }
6689
6854 if ( ! (this instanceof _Api) ) {
6855 return new _Api( context, data );
6856 }
6857
6690 6858 var settings = [];
6691 6859 var ctxSettings = function ( o ) {
6692 6860 var a = _toSettings( o );
6693 6861 if ( a ) {
6694 settings.push.apply( settings, a );
6862 settings = settings.concat( a );
6695 6863 }
6696 6864 };
6697
6865
6698 6866 if ( $.isArray( context ) ) {
6699 6867 for ( var i=0, ien=context.length ; i<ien ; i++ ) {
6700 6868 ctxSettings( context[i] );
@@ -6703,70 +6871,72 b''
6703 6871 else {
6704 6872 ctxSettings( context );
6705 6873 }
6706
6874
6707 6875 // Remove duplicates
6708 6876 this.context = _unique( settings );
6709
6877
6710 6878 // Initial data
6711 6879 if ( data ) {
6712 this.push.apply( this, data.toArray ? data.toArray() : data );
6713 }
6714
6880 $.merge( this, data );
6881 }
6882
6715 6883 // selector
6716 6884 this.selector = {
6717 6885 rows: null,
6718 6886 cols: null,
6719 6887 opts: null
6720 6888 };
6721
6889
6722 6890 _Api.extend( this, this, __apiStruct );
6723 6891 };
6724
6892
6725 6893 DataTable.Api = _Api;
6726
6727 _Api.prototype = /** @lends DataTables.Api */{
6728 /**
6729 * Return a new Api instance, comprised of the data held in the current
6730 * instance, join with the other array(s) and/or value(s).
6731 *
6732 * An alias for `Array.prototype.concat`.
6733 *
6734 * @type method
6735 * @param {*} value1 Arrays and/or values to concatenate.
6736 * @param {*} [...] Additional arrays and/or values to concatenate.
6737 * @returns {DataTables.Api} New API instance, comprising of the combined
6738 * array.
6739 */
6894
6895 // Don't destroy the existing prototype, just extend it. Required for jQuery 2's
6896 // isPlainObject.
6897 $.extend( _Api.prototype, {
6898 any: function ()
6899 {
6900 return this.count() !== 0;
6901 },
6902
6903
6740 6904 concat: __arrayProto.concat,
6741
6742
6905
6906
6743 6907 context: [], // array of table settings objects
6744
6745
6908
6909
6910 count: function ()
6911 {
6912 return this.flatten().length;
6913 },
6914
6915
6746 6916 each: function ( fn )
6747 6917 {
6748 6918 for ( var i=0, ien=this.length ; i<ien; i++ ) {
6749 6919 fn.call( this, this[i], i, this );
6750 6920 }
6751
6921
6752 6922 return this;
6753 6923 },
6754
6755
6924
6925
6756 6926 eq: function ( idx )
6757 6927 {
6758 6928 var ctx = this.context;
6759
6929
6760 6930 return ctx.length > idx ?
6761 6931 new _Api( ctx[idx], this[idx] ) :
6762 6932 null;
6763 6933 },
6764
6765
6934
6935
6766 6936 filter: function ( fn )
6767 6937 {
6768 6938 var a = [];
6769
6939
6770 6940 if ( __arrayProto.filter ) {
6771 6941 a = __arrayProto.filter.call( this, fn, this );
6772 6942 }
@@ -6778,21 +6948,21 b''
6778 6948 }
6779 6949 }
6780 6950 }
6781
6951
6782 6952 return new _Api( this.context, a );
6783 6953 },
6784
6785
6954
6955
6786 6956 flatten: function ()
6787 6957 {
6788 6958 var a = [];
6789 6959 return new _Api( this.context, a.concat.apply( a, this.toArray() ) );
6790 6960 },
6791
6792
6961
6962
6793 6963 join: __arrayProto.join,
6794
6795
6964
6965
6796 6966 indexOf: __arrayProto.indexOf || function (obj, start)
6797 6967 {
6798 6968 for ( var i=(start || 0), ien=this.length ; i<ien ; i++ ) {
@@ -6802,8 +6972,7 b''
6802 6972 }
6803 6973 return -1;
6804 6974 },
6805
6806 // Note that `alwaysNew` is internal - use iteratorNew externally
6975
6807 6976 iterator: function ( flatten, type, fn, alwaysNew ) {
6808 6977 var
6809 6978 a = [], ret,
@@ -6811,7 +6980,7 b''
6811 6980 context = this.context,
6812 6981 rows, items, item,
6813 6982 selector = this.selector;
6814
6983
6815 6984 // Argument shifting
6816 6985 if ( typeof flatten === 'string' ) {
6817 6986 alwaysNew = fn;
@@ -6819,13 +6988,13 b''
6819 6988 type = flatten;
6820 6989 flatten = false;
6821 6990 }
6822
6991
6823 6992 for ( i=0, ien=context.length ; i<ien ; i++ ) {
6824 6993 var apiInst = new _Api( context[i] );
6825
6994
6826 6995 if ( type === 'table' ) {
6827 6996 ret = fn.call( apiInst, context[i], i );
6828
6997
6829 6998 if ( ret !== undefined ) {
6830 6999 a.push( ret );
6831 7000 }
@@ -6833,7 +7002,7 b''
6833 7002 else if ( type === 'columns' || type === 'rows' ) {
6834 7003 // this has same length as context - one entry for each table
6835 7004 ret = fn.call( apiInst, context[i], this[i], i );
6836
7005
6837 7006 if ( ret !== undefined ) {
6838 7007 a.push( ret );
6839 7008 }
@@ -6842,28 +7011,28 b''
6842 7011 // columns and rows share the same structure.
6843 7012 // 'this' is an array of column indexes for each context
6844 7013 items = this[i];
6845
7014
6846 7015 if ( type === 'column-rows' ) {
6847 7016 rows = _selector_row_indexes( context[i], selector.opts );
6848 7017 }
6849
7018
6850 7019 for ( j=0, jen=items.length ; j<jen ; j++ ) {
6851 7020 item = items[j];
6852
7021
6853 7022 if ( type === 'cell' ) {
6854 7023 ret = fn.call( apiInst, context[i], item.row, item.column, i, j );
6855 7024 }
6856 7025 else {
6857 7026 ret = fn.call( apiInst, context[i], item, i, j, rows );
6858 7027 }
6859
7028
6860 7029 if ( ret !== undefined ) {
6861 7030 a.push( ret );
6862 7031 }
6863 7032 }
6864 7033 }
6865 7034 }
6866
7035
6867 7036 if ( a.length || alwaysNew ) {
6868 7037 var api = new _Api( context, flatten ? a.concat.apply( [], a ) : a );
6869 7038 var apiSelector = api.selector;
@@ -6874,22 +7043,22 b''
6874 7043 }
6875 7044 return this;
6876 7045 },
6877
6878
7046
7047
6879 7048 lastIndexOf: __arrayProto.lastIndexOf || function (obj, start)
6880 7049 {
6881 7050 // Bit cheeky...
6882 7051 return this.indexOf.apply( this.toArray.reverse(), arguments );
6883 7052 },
6884
6885
7053
7054
6886 7055 length: 0,
6887
6888
7056
7057
6889 7058 map: function ( fn )
6890 7059 {
6891 7060 var a = [];
6892
7061
6893 7062 if ( __arrayProto.map ) {
6894 7063 a = __arrayProto.map.call( this, fn, this );
6895 7064 }
@@ -6899,88 +7068,88 b''
6899 7068 a.push( fn.call( this, this[i], i ) );
6900 7069 }
6901 7070 }
6902
7071
6903 7072 return new _Api( this.context, a );
6904 7073 },
6905
6906
7074
7075
6907 7076 pluck: function ( prop )
6908 7077 {
6909 7078 return this.map( function ( el ) {
6910 7079 return el[ prop ];
6911 7080 } );
6912 7081 },
6913
7082
6914 7083 pop: __arrayProto.pop,
6915
6916
7084
7085
6917 7086 push: __arrayProto.push,
6918
6919
7087
7088
6920 7089 // Does not return an API instance
6921 7090 reduce: __arrayProto.reduce || function ( fn, init )
6922 7091 {
6923 7092 return _fnReduce( this, fn, init, 0, this.length, 1 );
6924 7093 },
6925
6926
7094
7095
6927 7096 reduceRight: __arrayProto.reduceRight || function ( fn, init )
6928 7097 {
6929 7098 return _fnReduce( this, fn, init, this.length-1, -1, -1 );
6930 7099 },
6931
6932
7100
7101
6933 7102 reverse: __arrayProto.reverse,
6934
6935
7103
7104
6936 7105 // Object with rows, columns and opts
6937 7106 selector: null,
6938
6939
7107
7108
6940 7109 shift: __arrayProto.shift,
6941
6942
7110
7111
6943 7112 sort: __arrayProto.sort, // ? name - order?
6944
6945
7113
7114
6946 7115 splice: __arrayProto.splice,
6947
6948
7116
7117
6949 7118 toArray: function ()
6950 7119 {
6951 7120 return __arrayProto.slice.call( this );
6952 7121 },
6953
6954
7122
7123
6955 7124 to$: function ()
6956 7125 {
6957 7126 return $( this );
6958 7127 },
6959
6960
7128
7129
6961 7130 toJQuery: function ()
6962 7131 {
6963 7132 return $( this );
6964 7133 },
6965
6966
7134
7135
6967 7136 unique: function ()
6968 7137 {
6969 7138 return new _Api( this.context, _unique(this) );
6970 7139 },
6971
6972
7140
7141
6973 7142 unshift: __arrayProto.unshift
6974 };
6975
6976
7143 } );
7144
7145
6977 7146 _Api.extend = function ( scope, obj, ext )
6978 7147 {
6979 7148 // Only extend API instances and static properties of the API
6980 if ( ! obj || ( ! (obj instanceof _Api) && ! obj.__dt_wrapper ) ) {
7149 if ( ! ext.length || ! obj || ( ! (obj instanceof _Api) && ! obj.__dt_wrapper ) ) {
6981 7150 return;
6982 7151 }
6983
7152
6984 7153 var
6985 7154 i, ien,
6986 7155 j, jen,
@@ -6988,41 +7157,41 b''
6988 7157 methodScoping = function ( scope, fn, struc ) {
6989 7158 return function () {
6990 7159 var ret = fn.apply( scope, arguments );
6991
7160
6992 7161 // Method extension
6993 7162 _Api.extend( ret, ret, struc.methodExt );
6994 7163 return ret;
6995 7164 };
6996 7165 };
6997
7166
6998 7167 for ( i=0, ien=ext.length ; i<ien ; i++ ) {
6999 7168 struct = ext[i];
7000
7169
7001 7170 // Value
7002 7171 obj[ struct.name ] = typeof struct.val === 'function' ?
7003 7172 methodScoping( scope, struct.val, struct ) :
7004 7173 $.isPlainObject( struct.val ) ?
7005 7174 {} :
7006 7175 struct.val;
7007
7176
7008 7177 obj[ struct.name ].__dt_wrapper = true;
7009
7178
7010 7179 // Property extension
7011 7180 _Api.extend( scope, obj[ struct.name ], struct.propExt );
7012 7181 }
7013 7182 };
7014
7015
7183
7184
7016 7185 // @todo - Is there need for an augment function?
7017 7186 // _Api.augment = function ( inst, name )
7018 7187 // {
7019 7188 // // Find src object in the structure from the name
7020 7189 // var parts = name.split('.');
7021
7190
7022 7191 // _Api.extend( inst, obj );
7023 7192 // };
7024
7025
7193
7194
7026 7195 // [
7027 7196 // {
7028 7197 // name: 'data' -- string - Property name
@@ -7045,7 +7214,7 b''
7045 7214 // ]
7046 7215 // }
7047 7216 // ]
7048
7217
7049 7218 _Api.register = _api_register = function ( name, val )
7050 7219 {
7051 7220 if ( $.isArray( name ) ) {
@@ -7054,13 +7223,13 b''
7054 7223 }
7055 7224 return;
7056 7225 }
7057
7226
7058 7227 var
7059 7228 i, ien,
7060 7229 heir = name.split('.'),
7061 7230 struct = __apiStruct,
7062 7231 key, method;
7063
7232
7064 7233 var find = function ( src, name ) {
7065 7234 for ( var i=0, ien=src.length ; i<ien ; i++ ) {
7066 7235 if ( src[i].name === name ) {
@@ -7069,13 +7238,13 b''
7069 7238 }
7070 7239 return null;
7071 7240 };
7072
7241
7073 7242 for ( i=0, ien=heir.length ; i<ien ; i++ ) {
7074 7243 method = heir[i].indexOf('()') !== -1;
7075 7244 key = method ?
7076 7245 heir[i].replace('()', '') :
7077 7246 heir[i];
7078
7247
7079 7248 var src = find( struct, key );
7080 7249 if ( ! src ) {
7081 7250 src = {
@@ -7086,7 +7255,7 b''
7086 7255 };
7087 7256 struct.push( src );
7088 7257 }
7089
7258
7090 7259 if ( i === ien-1 ) {
7091 7260 src.val = val;
7092 7261 }
@@ -7097,14 +7266,14 b''
7097 7266 }
7098 7267 }
7099 7268 };
7100
7101
7269
7270
7102 7271 _Api.registerPlural = _api_registerPlural = function ( pluralName, singularName, val ) {
7103 7272 _Api.register( pluralName, val );
7104
7273
7105 7274 _Api.register( singularName, function () {
7106 7275 var ret = val.apply( this, arguments );
7107
7276
7108 7277 if ( ret === this ) {
7109 7278 // Returned item is the API instance that was passed in, return it
7110 7279 return this;
@@ -7118,13 +7287,13 b''
7118 7287 ret[0] :
7119 7288 undefined;
7120 7289 }
7121
7290
7122 7291 // Non-API return - just fire it back
7123 7292 return ret;
7124 7293 } );
7125 7294 };
7126
7127
7295
7296
7128 7297 /**
7129 7298 * Selector for HTML tables. Apply the given selector to the give array of
7130 7299 * DataTables settings objects.
@@ -7140,12 +7309,12 b''
7140 7309 if ( typeof selector === 'number' ) {
7141 7310 return [ a[ selector ] ];
7142 7311 }
7143
7312
7144 7313 // Perform a jQuery selector on the table nodes
7145 7314 var nodes = $.map( a, function (el, i) {
7146 7315 return el.nTable;
7147 7316 } );
7148
7317
7149 7318 return $(nodes)
7150 7319 .filter( selector )
7151 7320 .map( function (i) {
@@ -7155,9 +7324,9 b''
7155 7324 } )
7156 7325 .toArray();
7157 7326 };
7158
7159
7160
7327
7328
7329
7161 7330 /**
7162 7331 * Context selector for the API's context (i.e. the tables the API instance
7163 7332 * refers to.
@@ -7175,71 +7344,77 b''
7175 7344 new _Api( __table_selector( selector, this.context ) ) :
7176 7345 this;
7177 7346 } );
7178
7179
7347
7348
7180 7349 _api_register( 'table()', function ( selector ) {
7181 7350 var tables = this.tables( selector );
7182 7351 var ctx = tables.context;
7183
7352
7184 7353 // Truncate to the first matched table
7185 7354 return ctx.length ?
7186 7355 new _Api( ctx[0] ) :
7187 7356 tables;
7188 7357 } );
7189
7190
7358
7359
7191 7360 _api_registerPlural( 'tables().nodes()', 'table().node()' , function () {
7192 7361 return this.iterator( 'table', function ( ctx ) {
7193 7362 return ctx.nTable;
7194 7363 }, 1 );
7195 7364 } );
7196
7197
7365
7366
7198 7367 _api_registerPlural( 'tables().body()', 'table().body()' , function () {
7199 7368 return this.iterator( 'table', function ( ctx ) {
7200 7369 return ctx.nTBody;
7201 7370 }, 1 );
7202 7371 } );
7203
7204
7372
7373
7205 7374 _api_registerPlural( 'tables().header()', 'table().header()' , function () {
7206 7375 return this.iterator( 'table', function ( ctx ) {
7207 7376 return ctx.nTHead;
7208 7377 }, 1 );
7209 7378 } );
7210
7211
7379
7380
7212 7381 _api_registerPlural( 'tables().footer()', 'table().footer()' , function () {
7213 7382 return this.iterator( 'table', function ( ctx ) {
7214 7383 return ctx.nTFoot;
7215 7384 }, 1 );
7216 7385 } );
7217
7218
7386
7387
7219 7388 _api_registerPlural( 'tables().containers()', 'table().container()' , function () {
7220 7389 return this.iterator( 'table', function ( ctx ) {
7221 7390 return ctx.nTableWrapper;
7222 7391 }, 1 );
7223 7392 } );
7224
7225
7226
7393
7394
7395
7227 7396 /**
7228 7397 * Redraw the tables in the current context.
7229 *
7230 * @param {boolean} [reset=true] Reset (default) or hold the current paging
7231 * position. A full re-sort and re-filter is performed when this method is
7232 * called, which is why the pagination reset is the default action.
7233 * @returns {DataTables.Api} this
7234 */
7235 _api_register( 'draw()', function ( resetPaging ) {
7398 */
7399 _api_register( 'draw()', function ( paging ) {
7236 7400 return this.iterator( 'table', function ( settings ) {
7237 _fnReDraw( settings, resetPaging===false );
7401 if ( paging === 'page' ) {
7402 _fnDraw( settings );
7403 }
7404 else {
7405 if ( typeof paging === 'string' ) {
7406 paging = paging === 'full-hold' ?
7407 false :
7408 true;
7409 }
7410
7411 _fnReDraw( settings, paging===false );
7412 }
7238 7413 } );
7239 7414 } );
7240
7241
7242
7415
7416
7417
7243 7418 /**
7244 7419 * Get the current page index.
7245 7420 *
@@ -7263,14 +7438,14 b''
7263 7438 if ( action === undefined ) {
7264 7439 return this.page.info().page; // not an expensive call
7265 7440 }
7266
7441
7267 7442 // else, have an action to take on all tables
7268 7443 return this.iterator( 'table', function ( settings ) {
7269 7444 _fnPageChange( settings, action );
7270 7445 } );
7271 7446 } );
7272
7273
7447
7448
7274 7449 /**
7275 7450 * Paging information for the first table in the current context.
7276 7451 *
@@ -7293,14 +7468,14 b''
7293 7468 if ( this.context.length === 0 ) {
7294 7469 return undefined;
7295 7470 }
7296
7471
7297 7472 var
7298 7473 settings = this.context[0],
7299 7474 start = settings._iDisplayStart,
7300 len = settings._iDisplayLength,
7475 len = settings.oFeatures.bPaginate ? settings._iDisplayLength : -1,
7301 7476 visRecords = settings.fnRecordsDisplay(),
7302 7477 all = len === -1;
7303
7478
7304 7479 return {
7305 7480 "page": all ? 0 : Math.floor( start / len ),
7306 7481 "pages": all ? 1 : Math.ceil( visRecords / len ),
@@ -7308,11 +7483,12 b''
7308 7483 "end": settings.fnDisplayEnd(),
7309 7484 "length": len,
7310 7485 "recordsTotal": settings.fnRecordsTotal(),
7311 "recordsDisplay": visRecords
7486 "recordsDisplay": visRecords,
7487 "serverSide": _fnDataSource( settings ) === 'ssp'
7312 7488 };
7313 7489 } );
7314
7315
7490
7491
7316 7492 /**
7317 7493 * Get the current page length.
7318 7494 *
@@ -7333,48 +7509,53 b''
7333 7509 this.context[0]._iDisplayLength :
7334 7510 undefined;
7335 7511 }
7336
7512
7337 7513 // else, set the page length
7338 7514 return this.iterator( 'table', function ( settings ) {
7339 7515 _fnLengthChange( settings, len );
7340 7516 } );
7341 7517 } );
7342
7343
7344
7518
7519
7520
7345 7521 var __reload = function ( settings, holdPosition, callback ) {
7522 // Use the draw event to trigger a callback
7523 if ( callback ) {
7524 var api = new _Api( settings );
7525
7526 api.one( 'draw', function () {
7527 callback( api.ajax.json() );
7528 } );
7529 }
7530
7346 7531 if ( _fnDataSource( settings ) == 'ssp' ) {
7347 7532 _fnReDraw( settings, holdPosition );
7348 7533 }
7349 7534 else {
7535 _fnProcessingDisplay( settings, true );
7536
7537 // Cancel an existing request
7538 var xhr = settings.jqXHR;
7539 if ( xhr && xhr.readyState !== 4 ) {
7540 xhr.abort();
7541 }
7542
7350 7543 // Trigger xhr
7351 _fnProcessingDisplay( settings, true );
7352
7353 7544 _fnBuildAjax( settings, [], function( json ) {
7354 7545 _fnClearTable( settings );
7355
7546
7356 7547 var data = _fnAjaxDataSrc( settings, json );
7357 7548 for ( var i=0, ien=data.length ; i<ien ; i++ ) {
7358 7549 _fnAddData( settings, data[i] );
7359 7550 }
7360
7551
7361 7552 _fnReDraw( settings, holdPosition );
7362 7553 _fnProcessingDisplay( settings, false );
7363 7554 } );
7364 7555 }
7365
7366 // Use the draw event to trigger a callback, regardless of if it is an async
7367 // or sync draw
7368 if ( callback ) {
7369 var api = new _Api( settings );
7370
7371 api.one( 'draw', function () {
7372 callback( api.ajax.json() );
7373 } );
7374 }
7375 7556 };
7376
7377
7557
7558
7378 7559 /**
7379 7560 * Get the JSON response from the last Ajax request that DataTables made to the
7380 7561 * server. Note that this returns the JSON from the first table in the current
@@ -7384,29 +7565,29 b''
7384 7565 */
7385 7566 _api_register( 'ajax.json()', function () {
7386 7567 var ctx = this.context;
7387
7568
7388 7569 if ( ctx.length > 0 ) {
7389 7570 return ctx[0].json;
7390 7571 }
7391
7572
7392 7573 // else return undefined;
7393 7574 } );
7394
7395
7575
7576
7396 7577 /**
7397 7578 * Get the data submitted in the last Ajax request
7398 7579 */
7399 7580 _api_register( 'ajax.params()', function () {
7400 7581 var ctx = this.context;
7401
7582
7402 7583 if ( ctx.length > 0 ) {
7403 7584 return ctx[0].oAjaxData;
7404 7585 }
7405
7586
7406 7587 // else return undefined;
7407 7588 } );
7408
7409
7589
7590
7410 7591 /**
7411 7592 * Reload tables from the Ajax data source. Note that this function will
7412 7593 * automatically re-draw the table when the remote data has been loaded.
@@ -7421,8 +7602,8 b''
7421 7602 __reload( settings, resetPaging===false, callback );
7422 7603 } );
7423 7604 } );
7424
7425
7605
7606
7426 7607 /**
7427 7608 * Get the current Ajax URL. Note that this returns the URL from the first
7428 7609 * table in the current context.
@@ -7437,21 +7618,21 b''
7437 7618 */
7438 7619 _api_register( 'ajax.url()', function ( url ) {
7439 7620 var ctx = this.context;
7440
7621
7441 7622 if ( url === undefined ) {
7442 7623 // get
7443 7624 if ( ctx.length === 0 ) {
7444 7625 return undefined;
7445 7626 }
7446 7627 ctx = ctx[0];
7447
7628
7448 7629 return ctx.ajax ?
7449 7630 $.isPlainObject( ctx.ajax ) ?
7450 7631 ctx.ajax.url :
7451 7632 ctx.ajax :
7452 7633 ctx.sAjaxSource;
7453 7634 }
7454
7635
7455 7636 // set
7456 7637 return this.iterator( 'table', function ( settings ) {
7457 7638 if ( $.isPlainObject( settings.ajax ) ) {
@@ -7465,8 +7646,8 b''
7465 7646 // value of `sAjaxSource` redundant.
7466 7647 } );
7467 7648 } );
7468
7469
7649
7650
7470 7651 /**
7471 7652 * Load data from the newly set Ajax URL. Note that this method is only
7472 7653 * available when `ajax.url()` is used to set a URL. Additionally, this method
@@ -7483,61 +7664,70 b''
7483 7664 __reload( ctx, resetPaging===false, callback );
7484 7665 } );
7485 7666 } );
7486
7487
7488
7489
7490 var _selector_run = function ( selector, select )
7667
7668
7669
7670
7671 var _selector_run = function ( type, selector, selectFn, settings, opts )
7491 7672 {
7492 7673 var
7493 7674 out = [], res,
7494 7675 a, i, ien, j, jen,
7495 7676 selectorType = typeof selector;
7496
7677
7497 7678 // Can't just check for isArray here, as an API or jQuery instance might be
7498 7679 // given with their array like look
7499 7680 if ( ! selector || selectorType === 'string' || selectorType === 'function' || selector.length === undefined ) {
7500 7681 selector = [ selector ];
7501 7682 }
7502
7683
7503 7684 for ( i=0, ien=selector.length ; i<ien ; i++ ) {
7504 a = selector[i] && selector[i].split ?
7685 // Only split on simple strings - complex expressions will be jQuery selectors
7686 a = selector[i] && selector[i].split && ! selector[i].match(/[\[\(:]/) ?
7505 7687 selector[i].split(',') :
7506 7688 [ selector[i] ];
7507
7689
7508 7690 for ( j=0, jen=a.length ; j<jen ; j++ ) {
7509 res = select( typeof a[j] === 'string' ? $.trim(a[j]) : a[j] );
7510
7691 res = selectFn( typeof a[j] === 'string' ? $.trim(a[j]) : a[j] );
7692
7511 7693 if ( res && res.length ) {
7512 out.push.apply( out, res );
7513 }
7514 }
7515 }
7516
7517 return out;
7694 out = out.concat( res );
7695 }
7696 }
7697 }
7698
7699 // selector extensions
7700 var ext = _ext.selector[ type ];
7701 if ( ext.length ) {
7702 for ( i=0, ien=ext.length ; i<ien ; i++ ) {
7703 out = ext[i]( settings, opts, out );
7704 }
7705 }
7706
7707 return _unique( out );
7518 7708 };
7519
7520
7709
7710
7521 7711 var _selector_opts = function ( opts )
7522 7712 {
7523 7713 if ( ! opts ) {
7524 7714 opts = {};
7525 7715 }
7526
7716
7527 7717 // Backwards compatibility for 1.9- which used the terminology filter rather
7528 7718 // than search
7529 if ( opts.filter && ! opts.search ) {
7719 if ( opts.filter && opts.search === undefined ) {
7530 7720 opts.search = opts.filter;
7531 7721 }
7532
7533 return {
7534 search: opts.search || 'none',
7535 order: opts.order || 'current',
7536 page: opts.page || 'all'
7537 };
7722
7723 return $.extend( {
7724 search: 'none',
7725 order: 'current',
7726 page: 'all'
7727 }, opts );
7538 7728 };
7539
7540
7729
7730
7541 7731 var _selector_first = function ( inst )
7542 7732 {
7543 7733 // Reduce the API instance to the first item found
@@ -7546,31 +7736,32 b''
7546 7736 // Assign the first element to the first item in the instance
7547 7737 // and truncate the instance and context
7548 7738 inst[0] = inst[i];
7739 inst[0].length = 1;
7549 7740 inst.length = 1;
7550 7741 inst.context = [ inst.context[i] ];
7551
7742
7552 7743 return inst;
7553 7744 }
7554 7745 }
7555
7746
7556 7747 // Not found - return an empty instance
7557 7748 inst.length = 0;
7558 7749 return inst;
7559 7750 };
7560
7561
7751
7752
7562 7753 var _selector_row_indexes = function ( settings, opts )
7563 7754 {
7564 7755 var
7565 7756 i, ien, tmp, a=[],
7566 7757 displayFiltered = settings.aiDisplay,
7567 7758 displayMaster = settings.aiDisplayMaster;
7568
7759
7569 7760 var
7570 7761 search = opts.search, // none, applied, removed
7571 7762 order = opts.order, // applied, current, index (original - compatibility with 1.9)
7572 7763 page = opts.page; // all, current
7573
7764
7574 7765 if ( _fnDataSource( settings ) == 'ssp' ) {
7575 7766 // In server-side processing mode, most options are irrelevant since
7576 7767 // rows not shown don't exist and the index order is the applied order
@@ -7604,7 +7795,7 b''
7604 7795 }
7605 7796 else { // applied | removed
7606 7797 tmp = $.inArray( i, displayFiltered );
7607
7798
7608 7799 if ((tmp === -1 && search == 'removed') ||
7609 7800 (tmp >= 0 && search == 'applied') )
7610 7801 {
@@ -7613,11 +7804,11 b''
7613 7804 }
7614 7805 }
7615 7806 }
7616
7807
7617 7808 return a;
7618 7809 };
7619
7620
7810
7811
7621 7812 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7622 7813 * Rows
7623 7814 *
@@ -7628,32 +7819,35 b''
7628 7819 * {array} - jQuery array of nodes, or simply an array of TR nodes
7629 7820 *
7630 7821 */
7631
7632
7822
7823
7633 7824 var __row_selector = function ( settings, selector, opts )
7634 7825 {
7635 return _selector_run( selector, function ( sel ) {
7826 var rows;
7827 var run = function ( sel ) {
7636 7828 var selInt = _intVal( sel );
7637 7829 var i, ien;
7638
7830
7639 7831 // Short cut - selector is a number and no options provided (default is
7640 7832 // all records, so no need to check if the index is in there, since it
7641 7833 // must be - dev error if the index doesn't exist).
7642 7834 if ( selInt !== null && ! opts ) {
7643 7835 return [ selInt ];
7644 7836 }
7645
7646 var rows = _selector_row_indexes( settings, opts );
7647
7837
7838 if ( ! rows ) {
7839 rows = _selector_row_indexes( settings, opts );
7840 }
7841
7648 7842 if ( selInt !== null && $.inArray( selInt, rows ) !== -1 ) {
7649 7843 // Selector - integer
7650 7844 return [ selInt ];
7651 7845 }
7652 else if ( ! sel ) {
7846 else if ( sel === null || sel === undefined || sel === '' ) {
7653 7847 // Selector - none
7654 7848 return rows;
7655 7849 }
7656
7850
7657 7851 // Selector - function
7658 7852 if ( typeof sel === 'function' ) {
7659 7853 return $.map( rows, function (idx) {
@@ -7661,20 +7855,48 b''
7661 7855 return sel( idx, row._aData, row.nTr ) ? idx : null;
7662 7856 } );
7663 7857 }
7664
7858
7665 7859 // Get nodes in the order from the `rows` array with null values removed
7666 7860 var nodes = _removeEmpty(
7667 7861 _pluck_order( settings.aoData, rows, 'nTr' )
7668 7862 );
7669
7863
7670 7864 // Selector - node
7671 7865 if ( sel.nodeName ) {
7672 if ( $.inArray( sel, nodes ) !== -1 ) {
7673 return [ sel._DT_RowIndex ]; // sel is a TR node that is in the table
7674 // and DataTables adds a prop for fast lookup
7675 }
7676 }
7677
7866 if ( sel._DT_RowIndex !== undefined ) {
7867 return [ sel._DT_RowIndex ]; // Property added by DT for fast lookup
7868 }
7869 else if ( sel._DT_CellIndex ) {
7870 return [ sel._DT_CellIndex.row ];
7871 }
7872 else {
7873 var host = $(sel).closest('*[data-dt-row]');
7874 return host.length ?
7875 [ host.data('dt-row') ] :
7876 [];
7877 }
7878 }
7879
7880 // ID selector. Want to always be able to select rows by id, regardless
7881 // of if the tr element has been created or not, so can't rely upon
7882 // jQuery here - hence a custom implementation. This does not match
7883 // Sizzle's fast selector or HTML4 - in HTML5 the ID can be anything,
7884 // but to select it using a CSS selector engine (like Sizzle or
7885 // querySelect) it would need to need to be escaped for some characters.
7886 // DataTables simplifies this for row selectors since you can select
7887 // only a row. A # indicates an id any anything that follows is the id -
7888 // unescaped.
7889 if ( typeof sel === 'string' && sel.charAt(0) === '#' ) {
7890 // get row index from id
7891 var rowObj = settings.aIds[ sel.replace( /^#/, '' ) ];
7892 if ( rowObj !== undefined ) {
7893 return [ rowObj.idx ];
7894 }
7895
7896 // need to fall through to jQuery in case there is DOM id that
7897 // matches
7898 }
7899
7678 7900 // Selector - jQuery selector string, array of nodes or jQuery object/
7679 7901 // As jQuery's .filter() allows jQuery objects to be passed in filter,
7680 7902 // it also allows arrays, so this will cope with all three options
@@ -7684,13 +7906,12 b''
7684 7906 return this._DT_RowIndex;
7685 7907 } )
7686 7908 .toArray();
7687 } );
7909 };
7910
7911 return _selector_run( 'row', selector, run, settings, opts );
7688 7912 };
7689
7690
7691 /**
7692 *
7693 */
7913
7914
7694 7915 _api_register( 'rows()', function ( selector, opts ) {
7695 7916 // argument shifting
7696 7917 if ( selector === undefined ) {
@@ -7700,89 +7921,128 b''
7700 7921 opts = selector;
7701 7922 selector = '';
7702 7923 }
7703
7924
7704 7925 opts = _selector_opts( opts );
7705
7926
7706 7927 var inst = this.iterator( 'table', function ( settings ) {
7707 7928 return __row_selector( settings, selector, opts );
7708 7929 }, 1 );
7709
7930
7710 7931 // Want argument shifting here and in __row_selector?
7711 7932 inst.selector.rows = selector;
7712 7933 inst.selector.opts = opts;
7713
7934
7714 7935 return inst;
7715 7936 } );
7716
7717
7937
7718 7938 _api_register( 'rows().nodes()', function () {
7719 7939 return this.iterator( 'row', function ( settings, row ) {
7720 7940 return settings.aoData[ row ].nTr || undefined;
7721 7941 }, 1 );
7722 7942 } );
7723
7943
7724 7944 _api_register( 'rows().data()', function () {
7725 7945 return this.iterator( true, 'rows', function ( settings, rows ) {
7726 7946 return _pluck_order( settings.aoData, rows, '_aData' );
7727 7947 }, 1 );
7728 7948 } );
7729
7949
7730 7950 _api_registerPlural( 'rows().cache()', 'row().cache()', function ( type ) {
7731 7951 return this.iterator( 'row', function ( settings, row ) {
7732 7952 var r = settings.aoData[ row ];
7733 7953 return type === 'search' ? r._aFilterData : r._aSortData;
7734 7954 }, 1 );
7735 7955 } );
7736
7956
7737 7957 _api_registerPlural( 'rows().invalidate()', 'row().invalidate()', function ( src ) {
7738 7958 return this.iterator( 'row', function ( settings, row ) {
7739 7959 _fnInvalidate( settings, row, src );
7740 7960 } );
7741 7961 } );
7742
7962
7743 7963 _api_registerPlural( 'rows().indexes()', 'row().index()', function () {
7744 7964 return this.iterator( 'row', function ( settings, row ) {
7745 7965 return row;
7746 7966 }, 1 );
7747 7967 } );
7748
7968
7969 _api_registerPlural( 'rows().ids()', 'row().id()', function ( hash ) {
7970 var a = [];
7971 var context = this.context;
7972
7973 // `iterator` will drop undefined values, but in this case we want them
7974 for ( var i=0, ien=context.length ; i<ien ; i++ ) {
7975 for ( var j=0, jen=this[i].length ; j<jen ; j++ ) {
7976 var id = context[i].rowIdFn( context[i].aoData[ this[i][j] ]._aData );
7977 a.push( (hash === true ? '#' : '' )+ id );
7978 }
7979 }
7980
7981 return new _Api( context, a );
7982 } );
7983
7749 7984 _api_registerPlural( 'rows().remove()', 'row().remove()', function () {
7750 7985 var that = this;
7751
7752 return this.iterator( 'row', function ( settings, row, thatIdx ) {
7986
7987 this.iterator( 'row', function ( settings, row, thatIdx ) {
7753 7988 var data = settings.aoData;
7754
7989 var rowData = data[ row ];
7990 var i, ien, j, jen;
7991 var loopRow, loopCells;
7992
7755 7993 data.splice( row, 1 );
7756
7757 // Update the _DT_RowIndex parameter on all rows in the table
7758 for ( var i=0, ien=data.length ; i<ien ; i++ ) {
7759 if ( data[i].nTr !== null ) {
7760 data[i].nTr._DT_RowIndex = i;
7761 }
7762 }
7763
7764 // Remove the target row from the search array
7765 var displayIndex = $.inArray( row, settings.aiDisplay );
7766
7994
7995 // Update the cached indexes
7996 for ( i=0, ien=data.length ; i<ien ; i++ ) {
7997 loopRow = data[i];
7998 loopCells = loopRow.anCells;
7999
8000 // Rows
8001 if ( loopRow.nTr !== null ) {
8002 loopRow.nTr._DT_RowIndex = i;
8003 }
8004
8005 // Cells
8006 if ( loopCells !== null ) {
8007 for ( j=0, jen=loopCells.length ; j<jen ; j++ ) {
8008 loopCells[j]._DT_CellIndex.row = i;
8009 }
8010 }
8011 }
8012
7767 8013 // Delete from the display arrays
7768 8014 _fnDeleteIndex( settings.aiDisplayMaster, row );
7769 8015 _fnDeleteIndex( settings.aiDisplay, row );
7770 8016 _fnDeleteIndex( that[ thatIdx ], row, false ); // maintain local indexes
7771
8017
7772 8018 // Check for an 'overflow' they case for displaying the table
7773 8019 _fnLengthOverflow( settings );
8020
8021 // Remove the row's ID reference if there is one
8022 var id = settings.rowIdFn( rowData._aData );
8023 if ( id !== undefined ) {
8024 delete settings.aIds[ id ];
8025 }
7774 8026 } );
7775 } );
7776
7777
8027
8028 this.iterator( 'table', function ( settings ) {
8029 for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
8030 settings.aoData[i].idx = i;
8031 }
8032 } );
8033
8034 return this;
8035 } );
8036
8037
7778 8038 _api_register( 'rows.add()', function ( rows ) {
7779 8039 var newRows = this.iterator( 'table', function ( settings ) {
7780 8040 var row, i, ien;
7781 8041 var out = [];
7782
8042
7783 8043 for ( i=0, ien=rows.length ; i<ien ; i++ ) {
7784 8044 row = rows[i];
7785
8045
7786 8046 if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {
7787 8047 out.push( _fnAddTr( settings, row )[0] );
7788 8048 }
@@ -7790,84 +8050,92 b''
7790 8050 out.push( _fnAddData( settings, row ) );
7791 8051 }
7792 8052 }
7793
8053
7794 8054 return out;
7795 8055 }, 1 );
7796
8056
7797 8057 // Return an Api.rows() extended instance, so rows().nodes() etc can be used
7798 8058 var modRows = this.rows( -1 );
7799 8059 modRows.pop();
7800 modRows.push.apply( modRows, newRows.toArray() );
7801
8060 $.merge( modRows, newRows );
8061
7802 8062 return modRows;
7803 8063 } );
7804
7805
7806
7807
7808
8064
8065
8066
8067
8068
7809 8069 /**
7810 8070 *
7811 8071 */
7812 8072 _api_register( 'row()', function ( selector, opts ) {
7813 8073 return _selector_first( this.rows( selector, opts ) );
7814 8074 } );
7815
7816
8075
8076
7817 8077 _api_register( 'row().data()', function ( data ) {
7818 8078 var ctx = this.context;
7819
8079
7820 8080 if ( data === undefined ) {
7821 8081 // Get
7822 8082 return ctx.length && this.length ?
7823 8083 ctx[0].aoData[ this[0] ]._aData :
7824 8084 undefined;
7825 8085 }
7826
8086
7827 8087 // Set
7828 8088 ctx[0].aoData[ this[0] ]._aData = data;
7829
8089
7830 8090 // Automatically invalidate
7831 8091 _fnInvalidate( ctx[0], this[0], 'data' );
7832
8092
7833 8093 return this;
7834 8094 } );
7835
7836
8095
8096
7837 8097 _api_register( 'row().node()', function () {
7838 8098 var ctx = this.context;
7839
8099
7840 8100 return ctx.length && this.length ?
7841 8101 ctx[0].aoData[ this[0] ].nTr || null :
7842 8102 null;
7843 8103 } );
7844
7845
8104
8105
7846 8106 _api_register( 'row.add()', function ( row ) {
7847 8107 // Allow a jQuery object to be passed in - only a single row is added from
7848 8108 // it though - the first element in the set
7849 8109 if ( row instanceof $ && row.length ) {
7850 8110 row = row[0];
7851 8111 }
7852
8112
7853 8113 var rows = this.iterator( 'table', function ( settings ) {
7854 8114 if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {
7855 8115 return _fnAddTr( settings, row )[0];
7856 8116 }
7857 8117 return _fnAddData( settings, row );
7858 8118 } );
7859
8119
7860 8120 // Return an Api.rows() extended instance, with the newly added row selected
7861 8121 return this.row( rows[0] );
7862 8122 } );
7863
7864
7865
8123
8124
8125
7866 8126 var __details_add = function ( ctx, row, data, klass )
7867 8127 {
7868 8128 // Convert to array of TR elements
7869 8129 var rows = [];
7870 8130 var addRow = function ( r, k ) {
8131 // Recursion to allow for arrays of jQuery objects
8132 if ( $.isArray( r ) || r instanceof $ ) {
8133 for ( var i=0, ien=r.length ; i<ien ; i++ ) {
8134 addRow( r[i], k );
8135 }
8136 return;
8137 }
8138
7871 8139 // If we get a TR element, then just add it directly - up to the dev
7872 8140 // to add the correct number of columns etc
7873 8141 if ( r.nodeName && r.nodeName.toLowerCase() === 'tr' ) {
@@ -7880,72 +8148,65 b''
7880 8148 .addClass( k )
7881 8149 .html( r )
7882 8150 [0].colSpan = _fnVisbleColumns( ctx );
7883
8151
7884 8152 rows.push( created[0] );
7885 8153 }
7886 8154 };
7887
7888 if ( $.isArray( data ) || data instanceof $ ) {
7889 for ( var i=0, ien=data.length ; i<ien ; i++ ) {
7890 addRow( data[i], klass );
7891 }
7892 }
7893 else {
7894 addRow( data, klass );
7895 }
7896
8155
8156 addRow( data, klass );
8157
7897 8158 if ( row._details ) {
7898 row._details.remove();
7899 }
7900
8159 row._details.detach();
8160 }
8161
7901 8162 row._details = $(rows);
7902
8163
7903 8164 // If the children were already shown, that state should be retained
7904 8165 if ( row._detailsShow ) {
7905 8166 row._details.insertAfter( row.nTr );
7906 8167 }
7907 8168 };
7908
7909
8169
8170
7910 8171 var __details_remove = function ( api, idx )
7911 8172 {
7912 8173 var ctx = api.context;
7913
8174
7914 8175 if ( ctx.length ) {
7915 8176 var row = ctx[0].aoData[ idx !== undefined ? idx : api[0] ];
7916
7917 if ( row._details ) {
8177
8178 if ( row && row._details ) {
7918 8179 row._details.remove();
7919
8180
7920 8181 row._detailsShow = undefined;
7921 8182 row._details = undefined;
7922 8183 }
7923 8184 }
7924 8185 };
7925
7926
8186
8187
7927 8188 var __details_display = function ( api, show ) {
7928 8189 var ctx = api.context;
7929
8190
7930 8191 if ( ctx.length && api.length ) {
7931 8192 var row = ctx[0].aoData[ api[0] ];
7932
8193
7933 8194 if ( row._details ) {
7934 8195 row._detailsShow = show;
7935
8196
7936 8197 if ( show ) {
7937 8198 row._details.insertAfter( row.nTr );
7938 8199 }
7939 8200 else {
7940 8201 row._details.detach();
7941 8202 }
7942
8203
7943 8204 __details_events( ctx[0] );
7944 8205 }
7945 8206 }
7946 8207 };
7947
7948
8208
8209
7949 8210 var __details_events = function ( settings )
7950 8211 {
7951 8212 var api = new _Api( settings );
@@ -7954,51 +8215,51 b''
7954 8215 var colvisEvent = 'column-visibility'+namespace;
7955 8216 var destroyEvent = 'destroy'+namespace;
7956 8217 var data = settings.aoData;
7957
8218
7958 8219 api.off( drawEvent +' '+ colvisEvent +' '+ destroyEvent );
7959
8220
7960 8221 if ( _pluck( data, '_details' ).length > 0 ) {
7961 8222 // On each draw, insert the required elements into the document
7962 8223 api.on( drawEvent, function ( e, ctx ) {
7963 8224 if ( settings !== ctx ) {
7964 8225 return;
7965 8226 }
7966
8227
7967 8228 api.rows( {page:'current'} ).eq(0).each( function (idx) {
7968 8229 // Internal data grab
7969 8230 var row = data[ idx ];
7970
8231
7971 8232 if ( row._detailsShow ) {
7972 8233 row._details.insertAfter( row.nTr );
7973 8234 }
7974 8235 } );
7975 8236 } );
7976
8237
7977 8238 // Column visibility change - update the colspan
7978 8239 api.on( colvisEvent, function ( e, ctx, idx, vis ) {
7979 8240 if ( settings !== ctx ) {
7980 8241 return;
7981 8242 }
7982
8243
7983 8244 // Update the colspan for the details rows (note, only if it already has
7984 8245 // a colspan)
7985 8246 var row, visible = _fnVisbleColumns( ctx );
7986
8247
7987 8248 for ( var i=0, ien=data.length ; i<ien ; i++ ) {
7988 8249 row = data[i];
7989
8250
7990 8251 if ( row._details ) {
7991 8252 row._details.children('td[colspan]').attr('colspan', visible );
7992 8253 }
7993 8254 }
7994 8255 } );
7995
8256
7996 8257 // Table destroyed - nuke any child rows
7997 8258 api.on( destroyEvent, function ( e, ctx ) {
7998 8259 if ( settings !== ctx ) {
7999 8260 return;
8000 8261 }
8001
8262
8002 8263 for ( var i=0, ien=data.length ; i<ien ; i++ ) {
8003 8264 if ( data[i]._details ) {
8004 8265 __details_remove( api, i );
@@ -8007,19 +8268,19 b''
8007 8268 } );
8008 8269 }
8009 8270 };
8010
8271
8011 8272 // Strings for the method names to help minification
8012 8273 var _emp = '';
8013 8274 var _child_obj = _emp+'row().child';
8014 8275 var _child_mth = _child_obj+'()';
8015
8276
8016 8277 // data can be:
8017 8278 // tr
8018 8279 // string
8019 8280 // jQuery or array of any of the above
8020 8281 _api_register( _child_mth, function ( data, klass ) {
8021 8282 var ctx = this.context;
8022
8283
8023 8284 if ( data === undefined ) {
8024 8285 // get
8025 8286 return ctx.length && this.length ?
@@ -8038,11 +8299,11 b''
8038 8299 // set
8039 8300 __details_add( ctx[0], ctx[0].aoData[ this[0] ], data, klass );
8040 8301 }
8041
8302
8042 8303 return this;
8043 8304 } );
8044
8045
8305
8306
8046 8307 _api_register( [
8047 8308 _child_obj+'.show()',
8048 8309 _child_mth+'.show()' // only when `child()` was called with parameters (without
@@ -8050,8 +8311,8 b''
8050 8311 __details_display( this, true );
8051 8312 return this;
8052 8313 } );
8053
8054
8314
8315
8055 8316 _api_register( [
8056 8317 _child_obj+'.hide()',
8057 8318 _child_mth+'.hide()' // only when `child()` was called with parameters (without
@@ -8059,8 +8320,8 b''
8059 8320 __details_display( this, false );
8060 8321 return this;
8061 8322 } );
8062
8063
8323
8324
8064 8325 _api_register( [
8065 8326 _child_obj+'.remove()',
8066 8327 _child_mth+'.remove()' // only when `child()` was called with parameters (without
@@ -8068,20 +8329,20 b''
8068 8329 __details_remove( this );
8069 8330 return this;
8070 8331 } );
8071
8072
8332
8333
8073 8334 _api_register( _child_obj+'.isShown()', function () {
8074 8335 var ctx = this.context;
8075
8336
8076 8337 if ( ctx.length && this.length ) {
8077 8338 // _detailsShown as false or undefined will fall through to return false
8078 8339 return ctx[0].aoData[ this[0] ]._detailsShow || false;
8079 8340 }
8080 8341 return false;
8081 8342 } );
8082
8083
8084
8343
8344
8345
8085 8346 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
8086 8347 * Columns
8087 8348 *
@@ -8092,13 +8353,13 b''
8092 8353 * "{string}" - jQuery selector on column header nodes
8093 8354 *
8094 8355 */
8095
8356
8096 8357 // can be an array of these items, comma separated list, or an array of comma
8097 8358 // separated lists
8098
8099 var __re_column_selector = /^(.+):(name|visIdx|visible)$/;
8100
8101
8359
8360 var __re_column_selector = /^([^:]+):(name|visIdx|visible)$/;
8361
8362
8102 8363 // r1 and r2 are redundant - but it means that the parameters match for the
8103 8364 // iterator callback in columns().data()
8104 8365 var __columnData = function ( settings, column, r1, r2, rows ) {
@@ -8108,23 +8369,23 b''
8108 8369 }
8109 8370 return a;
8110 8371 };
8111
8112
8372
8373
8113 8374 var __column_selector = function ( settings, selector, opts )
8114 8375 {
8115 8376 var
8116 8377 columns = settings.aoColumns,
8117 8378 names = _pluck( columns, 'sName' ),
8118 8379 nodes = _pluck( columns, 'nTh' );
8119
8120 return _selector_run( selector, function ( s ) {
8380
8381 var run = function ( s ) {
8121 8382 var selInt = _intVal( s );
8122
8383
8123 8384 // Selector - all
8124 8385 if ( s === '' ) {
8125 8386 return _range( columns.length );
8126 8387 }
8127
8388
8128 8389 // Selector - index
8129 8390 if ( selInt !== null ) {
8130 8391 return [ selInt >= 0 ?
@@ -8132,11 +8393,11 b''
8132 8393 columns.length + selInt // Count from right (+ because its a negative value)
8133 8394 ];
8134 8395 }
8135
8396
8136 8397 // Selector = function
8137 8398 if ( typeof s === 'function' ) {
8138 8399 var rows = _selector_row_indexes( settings, opts );
8139
8400
8140 8401 return $.map( columns, function (col, idx) {
8141 8402 return s(
8142 8403 idx,
@@ -8145,12 +8406,12 b''
8145 8406 ) ? idx : null;
8146 8407 } );
8147 8408 }
8148
8409
8149 8410 // jQuery or string selector
8150 8411 var match = typeof s === 'string' ?
8151 8412 s.match( __re_column_selector ) :
8152 8413 '';
8153
8414
8154 8415 if ( match ) {
8155 8416 switch( match[2] ) {
8156 8417 case 'visIdx':
@@ -8166,54 +8427,74 b''
8166 8427 }
8167 8428 // Counting from the left
8168 8429 return [ _fnVisibleToColumnIndex( settings, idx ) ];
8169
8430
8170 8431 case 'name':
8171 8432 // match by name. `names` is column index complete and in order
8172 8433 return $.map( names, function (name, i) {
8173 8434 return name === match[1] ? i : null;
8174 8435 } );
8175 }
8176 }
8177 else {
8178 // jQuery selector on the TH elements for the columns
8179 return $( nodes )
8180 .filter( s )
8181 .map( function () {
8182 return $.inArray( this, nodes ); // `nodes` is column index complete and in order
8183 } )
8184 .toArray();
8185 }
8186 } );
8436
8437 default:
8438 return [];
8439 }
8440 }
8441
8442 // Cell in the table body
8443 if ( s.nodeName && s._DT_CellIndex ) {
8444 return [ s._DT_CellIndex.column ];
8445 }
8446
8447 // jQuery selector on the TH elements for the columns
8448 var jqResult = $( nodes )
8449 .filter( s )
8450 .map( function () {
8451 return $.inArray( this, nodes ); // `nodes` is column index complete and in order
8452 } )
8453 .toArray();
8454
8455 if ( jqResult.length || ! s.nodeName ) {
8456 return jqResult;
8457 }
8458
8459 // Otherwise a node which might have a `dt-column` data attribute, or be
8460 // a child or such an element
8461 var host = $(s).closest('*[data-dt-column]');
8462 return host.length ?
8463 [ host.data('dt-column') ] :
8464 [];
8465 };
8466
8467 return _selector_run( 'column', selector, run, settings, opts );
8187 8468 };
8188
8189
8190 var __setColumnVis = function ( settings, column, vis, recalc ) {
8469
8470
8471 var __setColumnVis = function ( settings, column, vis ) {
8191 8472 var
8192 8473 cols = settings.aoColumns,
8193 8474 col = cols[ column ],
8194 8475 data = settings.aoData,
8195 8476 row, cells, i, ien, tr;
8196
8477
8197 8478 // Get
8198 8479 if ( vis === undefined ) {
8199 8480 return col.bVisible;
8200 8481 }
8201
8482
8202 8483 // Set
8203 8484 // No change
8204 8485 if ( col.bVisible === vis ) {
8205 8486 return;
8206 8487 }
8207
8488
8208 8489 if ( vis ) {
8209 8490 // Insert column
8210 8491 // Need to decide if we should use appendChild or insertBefore
8211 8492 var insertBefore = $.inArray( true, _pluck(cols, 'bVisible'), column+1 );
8212
8493
8213 8494 for ( i=0, ien=data.length ; i<ien ; i++ ) {
8214 8495 tr = data[i].nTr;
8215 8496 cells = data[i].anCells;
8216
8497
8217 8498 if ( tr ) {
8218 8499 // insertBefore can act like appendChild if 2nd arg is null
8219 8500 tr.insertBefore( cells[ column ], cells[ insertBefore ] || null );
@@ -8224,31 +8505,16 b''
8224 8505 // Remove column
8225 8506 $( _pluck( settings.aoData, 'anCells', column ) ).detach();
8226 8507 }
8227
8508
8228 8509 // Common actions
8229 8510 col.bVisible = vis;
8230 8511 _fnDrawHead( settings, settings.aoHeader );
8231 8512 _fnDrawHead( settings, settings.aoFooter );
8232
8233 if ( recalc === undefined || recalc ) {
8234 // Automatically adjust column sizing
8235 _fnAdjustColumnSizing( settings );
8236
8237 // Realign columns for scrolling
8238 if ( settings.oScroll.sX || settings.oScroll.sY ) {
8239 _fnScrollDraw( settings );
8240 }
8241 }
8242
8243 _fnCallbackFire( settings, null, 'column-visibility', [settings, column, vis] );
8244
8513
8245 8514 _fnSaveState( settings );
8246 8515 };
8247
8248
8249 /**
8250 *
8251 */
8516
8517
8252 8518 _api_register( 'columns()', function ( selector, opts ) {
8253 8519 // argument shifting
8254 8520 if ( selector === undefined ) {
@@ -8258,56 +8524,42 b''
8258 8524 opts = selector;
8259 8525 selector = '';
8260 8526 }
8261
8527
8262 8528 opts = _selector_opts( opts );
8263
8529
8264 8530 var inst = this.iterator( 'table', function ( settings ) {
8265 8531 return __column_selector( settings, selector, opts );
8266 8532 }, 1 );
8267
8533
8268 8534 // Want argument shifting here and in _row_selector?
8269 8535 inst.selector.cols = selector;
8270 8536 inst.selector.opts = opts;
8271
8537
8272 8538 return inst;
8273 8539 } );
8274
8275
8276 /**
8277 *
8278 */
8540
8279 8541 _api_registerPlural( 'columns().header()', 'column().header()', function ( selector, opts ) {
8280 8542 return this.iterator( 'column', function ( settings, column ) {
8281 8543 return settings.aoColumns[column].nTh;
8282 8544 }, 1 );
8283 8545 } );
8284
8285
8286 /**
8287 *
8288 */
8546
8289 8547 _api_registerPlural( 'columns().footer()', 'column().footer()', function ( selector, opts ) {
8290 8548 return this.iterator( 'column', function ( settings, column ) {
8291 8549 return settings.aoColumns[column].nTf;
8292 8550 }, 1 );
8293 8551 } );
8294
8295
8296 /**
8297 *
8298 */
8552
8299 8553 _api_registerPlural( 'columns().data()', 'column().data()', function () {
8300 8554 return this.iterator( 'column-rows', __columnData, 1 );
8301 8555 } );
8302
8303
8556
8304 8557 _api_registerPlural( 'columns().dataSrc()', 'column().dataSrc()', function () {
8305 8558 return this.iterator( 'column', function ( settings, column ) {
8306 8559 return settings.aoColumns[column].mData;
8307 8560 }, 1 );
8308 8561 } );
8309
8310
8562
8311 8563 _api_registerPlural( 'columns().cache()', 'column().cache()', function ( type ) {
8312 8564 return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
8313 8565 return _pluck_order( settings.aoData, rows,
@@ -8315,27 +8567,36 b''
8315 8567 );
8316 8568 }, 1 );
8317 8569 } );
8318
8319
8570
8320 8571 _api_registerPlural( 'columns().nodes()', 'column().nodes()', function () {
8321 8572 return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
8322 8573 return _pluck_order( settings.aoData, rows, 'anCells', column ) ;
8323 8574 }, 1 );
8324 8575 } );
8325
8326
8327
8576
8328 8577 _api_registerPlural( 'columns().visible()', 'column().visible()', function ( vis, calc ) {
8329 return this.iterator( 'column', function ( settings, column ) {
8578 var ret = this.iterator( 'column', function ( settings, column ) {
8330 8579 if ( vis === undefined ) {
8331 8580 return settings.aoColumns[ column ].bVisible;
8332 8581 } // else
8333 __setColumnVis( settings, column, vis, calc );
8582 __setColumnVis( settings, column, vis );
8334 8583 } );
8335 } );
8336
8337
8338
8584
8585 // Group the column visibility changes
8586 if ( vis !== undefined ) {
8587 // Second loop once the first is done for events
8588 this.iterator( 'column', function ( settings, column ) {
8589 _fnCallbackFire( settings, null, 'column-visibility', [settings, column, vis, calc] );
8590 } );
8591
8592 if ( calc === undefined || calc ) {
8593 this.columns.adjust();
8594 }
8595 }
8596
8597 return ret;
8598 } );
8599
8339 8600 _api_registerPlural( 'columns().indexes()', 'column().index()', function ( type ) {
8340 8601 return this.iterator( 'column', function ( settings, column ) {
8341 8602 return type === 'visible' ?
@@ -8343,33 +8604,17 b''
8343 8604 column;
8344 8605 }, 1 );
8345 8606 } );
8346
8347
8348 // _api_register( 'columns().show()', function () {
8349 // var selector = this.selector;
8350 // return this.columns( selector.cols, selector.opts ).visible( true );
8351 // } );
8352
8353
8354 // _api_register( 'columns().hide()', function () {
8355 // var selector = this.selector;
8356 // return this.columns( selector.cols, selector.opts ).visible( false );
8357 // } );
8358
8359
8360
8607
8361 8608 _api_register( 'columns.adjust()', function () {
8362 8609 return this.iterator( 'table', function ( settings ) {
8363 8610 _fnAdjustColumnSizing( settings );
8364 8611 }, 1 );
8365 8612 } );
8366
8367
8368 // Convert from one column index type, to another type
8613
8369 8614 _api_register( 'column.index()', function ( type, idx ) {
8370 8615 if ( this.context.length !== 0 ) {
8371 8616 var ctx = this.context[0];
8372
8617
8373 8618 if ( type === 'fromVisible' || type === 'toData' ) {
8374 8619 return _fnVisibleToColumnIndex( ctx, idx );
8375 8620 }
@@ -8378,15 +8623,13 b''
8378 8623 }
8379 8624 }
8380 8625 } );
8381
8382
8626
8383 8627 _api_register( 'column()', function ( selector, opts ) {
8384 8628 return _selector_first( this.columns( selector, opts ) );
8385 8629 } );
8386
8387
8388
8389
8630
8631
8632
8390 8633 var __cell_selector = function ( settings, selector, opts )
8391 8634 {
8392 8635 var data = settings.aoData;
@@ -8396,28 +8639,28 b''
8396 8639 var row;
8397 8640 var columns = settings.aoColumns.length;
8398 8641 var a, i, ien, j, o, host;
8399
8400 return _selector_run( selector, function ( s ) {
8642
8643 var run = function ( s ) {
8401 8644 var fnSelector = typeof s === 'function';
8402
8645
8403 8646 if ( s === null || s === undefined || fnSelector ) {
8404 8647 // All cells and function selectors
8405 8648 a = [];
8406
8649
8407 8650 for ( i=0, ien=rows.length ; i<ien ; i++ ) {
8408 8651 row = rows[i];
8409
8652
8410 8653 for ( j=0 ; j<columns ; j++ ) {
8411 8654 o = {
8412 8655 row: row,
8413 8656 column: j
8414 8657 };
8415
8658
8416 8659 if ( fnSelector ) {
8417 8660 // Selector - function
8418 host = settings.aoData[ row ];
8419
8420 if ( s( o, _fnGetCellData(settings, row, j), host.anCells[j] ) ) {
8661 host = data[ row ];
8662
8663 if ( s( o, _fnGetCellData(settings, row, j), host.anCells ? host.anCells[j] : null ) ) {
8421 8664 a.push( o );
8422 8665 }
8423 8666 }
@@ -8427,66 +8670,83 b''
8427 8670 }
8428 8671 }
8429 8672 }
8430
8673
8431 8674 return a;
8432 8675 }
8433
8676
8434 8677 // Selector - index
8435 8678 if ( $.isPlainObject( s ) ) {
8436 8679 return [s];
8437 8680 }
8438
8681
8439 8682 // Selector - jQuery filtered cells
8440 return allCells
8683 var jqResult = allCells
8441 8684 .filter( s )
8442 8685 .map( function (i, el) {
8443 row = el.parentNode._DT_RowIndex;
8444
8445 return {
8446 row: row,
8447 column: $.inArray( el, data[ row ].anCells )
8448 };
8686 return { // use a new object, in case someone changes the values
8687 row: el._DT_CellIndex.row,
8688 column: el._DT_CellIndex.column
8689 };
8449 8690 } )
8450 8691 .toArray();
8451 } );
8692
8693 if ( jqResult.length || ! s.nodeName ) {
8694 return jqResult;
8695 }
8696
8697 // Otherwise the selector is a node, and there is one last option - the
8698 // element might be a child of an element which has dt-row and dt-column
8699 // data attributes
8700 host = $(s).closest('*[data-dt-row]');
8701 return host.length ?
8702 [ {
8703 row: host.data('dt-row'),
8704 column: host.data('dt-column')
8705 } ] :
8706 [];
8707 };
8708
8709 return _selector_run( 'cell', selector, run, settings, opts );
8452 8710 };
8453
8454
8455
8456
8711
8712
8713
8714
8457 8715 _api_register( 'cells()', function ( rowSelector, columnSelector, opts ) {
8458 8716 // Argument shifting
8459 8717 if ( $.isPlainObject( rowSelector ) ) {
8460 8718 // Indexes
8461 if ( typeof rowSelector.row !== undefined ) {
8719 if ( rowSelector.row === undefined ) {
8720 // Selector options in first parameter
8721 opts = rowSelector;
8722 rowSelector = null;
8723 }
8724 else {
8725 // Cell index objects in first parameter
8462 8726 opts = columnSelector;
8463 8727 columnSelector = null;
8464 8728 }
8465 else {
8466 opts = rowSelector;
8467 rowSelector = null;
8468 }
8469 8729 }
8470 8730 if ( $.isPlainObject( columnSelector ) ) {
8471 8731 opts = columnSelector;
8472 8732 columnSelector = null;
8473 8733 }
8474
8734
8475 8735 // Cell selector
8476 8736 if ( columnSelector === null || columnSelector === undefined ) {
8477 8737 return this.iterator( 'table', function ( settings ) {
8478 8738 return __cell_selector( settings, rowSelector, _selector_opts( opts ) );
8479 8739 } );
8480 8740 }
8481
8741
8482 8742 // Row + column selector
8483 8743 var columns = this.columns( columnSelector, opts );
8484 8744 var rows = this.rows( rowSelector, opts );
8485 8745 var a, i, ien, j, jen;
8486
8746
8487 8747 var cells = this.iterator( 'table', function ( settings, idx ) {
8488 8748 a = [];
8489
8749
8490 8750 for ( i=0, ien=rows[idx].length ; i<ien ; i++ ) {
8491 8751 for ( j=0, jen=columns[idx].length ; j<jen ; j++ ) {
8492 8752 a.push( {
@@ -8495,53 +8755,54 b''
8495 8755 } );
8496 8756 }
8497 8757 }
8498
8758
8499 8759 return a;
8500 8760 }, 1 );
8501
8761
8502 8762 $.extend( cells.selector, {
8503 8763 cols: columnSelector,
8504 8764 rows: rowSelector,
8505 8765 opts: opts
8506 8766 } );
8507
8767
8508 8768 return cells;
8509 8769 } );
8510
8511
8770
8771
8512 8772 _api_registerPlural( 'cells().nodes()', 'cell().node()', function () {
8513 8773 return this.iterator( 'cell', function ( settings, row, column ) {
8514 var cells = settings.aoData[ row ].anCells;
8515 return cells ?
8516 cells[ column ] :
8774 var data = settings.aoData[ row ];
8775
8776 return data && data.anCells ?
8777 data.anCells[ column ] :
8517 8778 undefined;
8518 8779 }, 1 );
8519 8780 } );
8520
8521
8781
8782
8522 8783 _api_register( 'cells().data()', function () {
8523 8784 return this.iterator( 'cell', function ( settings, row, column ) {
8524 8785 return _fnGetCellData( settings, row, column );
8525 8786 }, 1 );
8526 8787 } );
8527
8528
8788
8789
8529 8790 _api_registerPlural( 'cells().cache()', 'cell().cache()', function ( type ) {
8530 8791 type = type === 'search' ? '_aFilterData' : '_aSortData';
8531
8792
8532 8793 return this.iterator( 'cell', function ( settings, row, column ) {
8533 8794 return settings.aoData[ row ][ type ][ column ];
8534 8795 }, 1 );
8535 8796 } );
8536
8537
8797
8798
8538 8799 _api_registerPlural( 'cells().render()', 'cell().render()', function ( type ) {
8539 8800 return this.iterator( 'cell', function ( settings, row, column ) {
8540 8801 return _fnGetCellData( settings, row, column, type );
8541 8802 }, 1 );
8542 8803 } );
8543
8544
8804
8805
8545 8806 _api_registerPlural( 'cells().indexes()', 'cell().index()', function () {
8546 8807 return this.iterator( 'cell', function ( settings, row, column ) {
8547 8808 return {
@@ -8551,41 +8812,41 b''
8551 8812 };
8552 8813 }, 1 );
8553 8814 } );
8554
8555
8815
8816
8556 8817 _api_registerPlural( 'cells().invalidate()', 'cell().invalidate()', function ( src ) {
8557 8818 return this.iterator( 'cell', function ( settings, row, column ) {
8558 8819 _fnInvalidate( settings, row, src, column );
8559 8820 } );
8560 8821 } );
8561
8562
8563
8822
8823
8824
8564 8825 _api_register( 'cell()', function ( rowSelector, columnSelector, opts ) {
8565 8826 return _selector_first( this.cells( rowSelector, columnSelector, opts ) );
8566 8827 } );
8567
8568
8828
8829
8569 8830 _api_register( 'cell().data()', function ( data ) {
8570 8831 var ctx = this.context;
8571 8832 var cell = this[0];
8572
8833
8573 8834 if ( data === undefined ) {
8574 8835 // Get
8575 8836 return ctx.length && cell.length ?
8576 8837 _fnGetCellData( ctx[0], cell[0].row, cell[0].column ) :
8577 8838 undefined;
8578 8839 }
8579
8840
8580 8841 // Set
8581 8842 _fnSetCellData( ctx[0], cell[0].row, cell[0].column, data );
8582 8843 _fnInvalidate( ctx[0], cell[0].row, 'data', cell[0].column );
8583
8844
8584 8845 return this;
8585 8846 } );
8586
8587
8588
8847
8848
8849
8589 8850 /**
8590 8851 * Get current ordering (sorting) that has been applied to the table.
8591 8852 *
@@ -8616,31 +8877,31 b''
8616 8877 */
8617 8878 _api_register( 'order()', function ( order, dir ) {
8618 8879 var ctx = this.context;
8619
8880
8620 8881 if ( order === undefined ) {
8621 8882 // get
8622 8883 return ctx.length !== 0 ?
8623 8884 ctx[0].aaSorting :
8624 8885 undefined;
8625 8886 }
8626
8887
8627 8888 // set
8628 8889 if ( typeof order === 'number' ) {
8629 8890 // Simple column / direction passed in
8630 8891 order = [ [ order, dir ] ];
8631 8892 }
8632 else if ( ! $.isArray( order[0] ) ) {
8893 else if ( order.length && ! $.isArray( order[0] ) ) {
8633 8894 // Arguments passed in (list of 1D arrays)
8634 8895 order = Array.prototype.slice.call( arguments );
8635 8896 }
8636 8897 // otherwise a 2D array was passed in
8637
8898
8638 8899 return this.iterator( 'table', function ( settings ) {
8639 8900 settings.aaSorting = order.slice();
8640 8901 } );
8641 8902 } );
8642
8643
8903
8904
8644 8905 /**
8645 8906 * Attach a sort listener to an element for a given column
8646 8907 *
@@ -8656,44 +8917,62 b''
8656 8917 _fnSortAttachListener( settings, node, column, callback );
8657 8918 } );
8658 8919 } );
8659
8660
8920
8921
8922 _api_register( 'order.fixed()', function ( set ) {
8923 if ( ! set ) {
8924 var ctx = this.context;
8925 var fixed = ctx.length ?
8926 ctx[0].aaSortingFixed :
8927 undefined;
8928
8929 return $.isArray( fixed ) ?
8930 { pre: fixed } :
8931 fixed;
8932 }
8933
8934 return this.iterator( 'table', function ( settings ) {
8935 settings.aaSortingFixed = $.extend( true, {}, set );
8936 } );
8937 } );
8938
8939
8661 8940 // Order by the selected column(s)
8662 8941 _api_register( [
8663 8942 'columns().order()',
8664 8943 'column().order()'
8665 8944 ], function ( dir ) {
8666 8945 var that = this;
8667
8946
8668 8947 return this.iterator( 'table', function ( settings, i ) {
8669 8948 var sort = [];
8670
8949
8671 8950 $.each( that[i], function (j, col) {
8672 8951 sort.push( [ col, dir ] );
8673 8952 } );
8674
8953
8675 8954 settings.aaSorting = sort;
8676 8955 } );
8677 8956 } );
8678
8679
8680
8957
8958
8959
8681 8960 _api_register( 'search()', function ( input, regex, smart, caseInsen ) {
8682 8961 var ctx = this.context;
8683
8962
8684 8963 if ( input === undefined ) {
8685 8964 // get
8686 8965 return ctx.length !== 0 ?
8687 8966 ctx[0].oPreviousSearch.sSearch :
8688 8967 undefined;
8689 8968 }
8690
8969
8691 8970 // set
8692 8971 return this.iterator( 'table', function ( settings ) {
8693 8972 if ( ! settings.oFeatures.bFilter ) {
8694 8973 return;
8695 8974 }
8696
8975
8697 8976 _fnFilterComplete( settings, $.extend( {}, settings.oPreviousSearch, {
8698 8977 "sSearch": input+"",
8699 8978 "bRegex": regex === null ? false : regex,
@@ -8702,71 +8981,71 b''
8702 8981 } ), 1 );
8703 8982 } );
8704 8983 } );
8705
8706
8984
8985
8707 8986 _api_registerPlural(
8708 8987 'columns().search()',
8709 8988 'column().search()',
8710 8989 function ( input, regex, smart, caseInsen ) {
8711 8990 return this.iterator( 'column', function ( settings, column ) {
8712 8991 var preSearch = settings.aoPreSearchCols;
8713
8992
8714 8993 if ( input === undefined ) {
8715 8994 // get
8716 8995 return preSearch[ column ].sSearch;
8717 8996 }
8718
8997
8719 8998 // set
8720 8999 if ( ! settings.oFeatures.bFilter ) {
8721 9000 return;
8722 9001 }
8723
9002
8724 9003 $.extend( preSearch[ column ], {
8725 9004 "sSearch": input+"",
8726 9005 "bRegex": regex === null ? false : regex,
8727 9006 "bSmart": smart === null ? true : smart,
8728 9007 "bCaseInsensitive": caseInsen === null ? true : caseInsen
8729 9008 } );
8730
9009
8731 9010 _fnFilterComplete( settings, settings.oPreviousSearch, 1 );
8732 9011 } );
8733 9012 }
8734 9013 );
8735
9014
8736 9015 /*
8737 9016 * State API methods
8738 9017 */
8739
9018
8740 9019 _api_register( 'state()', function () {
8741 9020 return this.context.length ?
8742 9021 this.context[0].oSavedState :
8743 9022 null;
8744 9023 } );
8745
8746
9024
9025
8747 9026 _api_register( 'state.clear()', function () {
8748 9027 return this.iterator( 'table', function ( settings ) {
8749 9028 // Save an empty object
8750 9029 settings.fnStateSaveCallback.call( settings.oInstance, settings, {} );
8751 9030 } );
8752 9031 } );
8753
8754
9032
9033
8755 9034 _api_register( 'state.loaded()', function () {
8756 9035 return this.context.length ?
8757 9036 this.context[0].oLoadedState :
8758 9037 null;
8759 9038 } );
8760
8761
9039
9040
8762 9041 _api_register( 'state.save()', function () {
8763 9042 return this.iterator( 'table', function ( settings ) {
8764 9043 _fnSaveState( settings );
8765 9044 } );
8766 9045 } );
8767
8768
8769
9046
9047
9048
8770 9049 /**
8771 9050 * Provide a common method for plug-ins to check the version of DataTables being
8772 9051 * used, in order to ensure compatibility.
@@ -8787,24 +9066,24 b''
8787 9066 var aThis = DataTable.version.split('.');
8788 9067 var aThat = version.split('.');
8789 9068 var iThis, iThat;
8790
9069
8791 9070 for ( var i=0, iLen=aThat.length ; i<iLen ; i++ ) {
8792 9071 iThis = parseInt( aThis[i], 10 ) || 0;
8793 9072 iThat = parseInt( aThat[i], 10 ) || 0;
8794
9073
8795 9074 // Parts are the same, keep comparing
8796 9075 if (iThis === iThat) {
8797 9076 continue;
8798 9077 }
8799
9078
8800 9079 // Parts are different, return immediately
8801 9080 return iThis > iThat;
8802 9081 }
8803
9082
8804 9083 return true;
8805 9084 };
8806
8807
9085
9086
8808 9087 /**
8809 9088 * Check if a `<table>` node is a DataTable table already or not.
8810 9089 *
@@ -8824,17 +9103,24 b''
8824 9103 {
8825 9104 var t = $(table).get(0);
8826 9105 var is = false;
8827
9106
9107 if ( table instanceof DataTable.Api ) {
9108 return true;
9109 }
9110
8828 9111 $.each( DataTable.settings, function (i, o) {
8829 if ( o.nTable === t || o.nScrollHead === t || o.nScrollFoot === t ) {
9112 var head = o.nScrollHead ? $('table', o.nScrollHead)[0] : null;
9113 var foot = o.nScrollFoot ? $('table', o.nScrollFoot)[0] : null;
9114
9115 if ( o.nTable === t || head === t || foot === t ) {
8830 9116 is = true;
8831 9117 }
8832 9118 } );
8833
9119
8834 9120 return is;
8835 9121 };
8836
8837
9122
9123
8838 9124 /**
8839 9125 * Get all DataTable tables that have been initialised - optionally you can
8840 9126 * select to get only currently visible tables.
@@ -8853,46 +9139,25 b''
8853 9139 */
8854 9140 DataTable.tables = DataTable.fnTables = function ( visible )
8855 9141 {
8856 return $.map( DataTable.settings, function (o) {
9142 var api = false;
9143
9144 if ( $.isPlainObject( visible ) ) {
9145 api = visible.api;
9146 visible = visible.visible;
9147 }
9148
9149 var a = $.map( DataTable.settings, function (o) {
8857 9150 if ( !visible || (visible && $(o.nTable).is(':visible')) ) {
8858 9151 return o.nTable;
8859 9152 }
8860 9153 } );
9154
9155 return api ?
9156 new _Api( a ) :
9157 a;
8861 9158 };
8862
8863
8864 /**
8865 * DataTables utility methods
8866 *
8867 * This namespace provides helper methods that DataTables uses internally to
8868 * create a DataTable, but which are not exclusively used only for DataTables.
8869 * These methods can be used by extension authors to save the duplication of
8870 * code.
8871 *
8872 * @namespace
8873 */
8874 DataTable.util = {
8875 /**
8876 * Throttle the calls to a function. Arguments and context are maintained
8877 * for the throttled function.
8878 *
8879 * @param {function} fn Function to be called
8880 * @param {integer} freq Call frequency in mS
8881 * @return {function} Wrapped function
8882 */
8883 throttle: _fnThrottle,
8884
8885
8886 /**
8887 * Escape a string such that it can be used in a regular expression
8888 *
8889 * @param {string} sVal string to escape
8890 * @returns {string} escaped string
8891 */
8892 escapeRegex: _fnEscapeRegex
8893 };
8894
8895
9159
9160
8896 9161 /**
8897 9162 * Convert from camel case parameters to Hungarian notation. This is made public
8898 9163 * for the extensions to provide the same ability as DataTables core to accept
@@ -8907,9 +9172,9 b''
8907 9172 * won't be.
8908 9173 */
8909 9174 DataTable.camelToHungarian = _fnCamelToHungarian;
8910
8911
8912
9175
9176
9177
8913 9178 /**
8914 9179 *
8915 9180 */
@@ -8917,53 +9182,61 b''
8917 9182 var
8918 9183 rows = this.rows( opts ).nodes(), // Get all rows
8919 9184 jqRows = $(rows);
8920
9185
8921 9186 return $( [].concat(
8922 9187 jqRows.filter( selector ).toArray(),
8923 9188 jqRows.find( selector ).toArray()
8924 9189 ) );
8925 9190 } );
8926
8927
9191
9192
8928 9193 // jQuery functions to operate on the tables
8929 9194 $.each( [ 'on', 'one', 'off' ], function (i, key) {
8930 9195 _api_register( key+'()', function ( /* event, handler */ ) {
8931 9196 var args = Array.prototype.slice.call(arguments);
8932
9197
8933 9198 // Add the `dt` namespace automatically if it isn't already present
8934 if ( ! args[0].match(/\.dt\b/) ) {
8935 args[0] += '.dt';
8936 }
8937
9199 args[0] = $.map( args[0].split( /\s/ ), function ( e ) {
9200 return ! e.match(/\.dt\b/) ?
9201 e+'.dt' :
9202 e;
9203 } ).join( ' ' );
9204
8938 9205 var inst = $( this.tables().nodes() );
8939 9206 inst[key].apply( inst, args );
8940 9207 return this;
8941 9208 } );
8942 9209 } );
8943
8944
9210
9211
8945 9212 _api_register( 'clear()', function () {
8946 9213 return this.iterator( 'table', function ( settings ) {
8947 9214 _fnClearTable( settings );
8948 9215 } );
8949 9216 } );
8950
8951
9217
9218
8952 9219 _api_register( 'settings()', function () {
8953 9220 return new _Api( this.context, this.context );
8954 9221 } );
8955
8956
9222
9223
9224 _api_register( 'init()', function () {
9225 var ctx = this.context;
9226 return ctx.length ? ctx[0].oInit : null;
9227 } );
9228
9229
8957 9230 _api_register( 'data()', function () {
8958 9231 return this.iterator( 'table', function ( settings ) {
8959 9232 return _pluck( settings.aoData, '_aData' );
8960 9233 } ).flatten();
8961 9234 } );
8962
8963
9235
9236
8964 9237 _api_register( 'destroy()', function ( remove ) {
8965 9238 remove = remove || false;
8966
9239
8967 9240 return this.iterator( 'table', function ( settings ) {
8968 9241 var orig = settings.nTableWrapper.parentNode;
8969 9242 var classes = settings.oClasses;
@@ -8976,50 +9249,46 b''
8976 9249 var jqWrapper = $(settings.nTableWrapper);
8977 9250 var rows = $.map( settings.aoData, function (r) { return r.nTr; } );
8978 9251 var i, ien;
8979
9252
8980 9253 // Flag to note that the table is currently being destroyed - no action
8981 9254 // should be taken
8982 9255 settings.bDestroying = true;
8983
9256
8984 9257 // Fire off the destroy callbacks for plug-ins etc
8985 9258 _fnCallbackFire( settings, "aoDestroyCallback", "destroy", [settings] );
8986
9259
8987 9260 // If not being removed from the document, make all columns visible
8988 9261 if ( ! remove ) {
8989 9262 new _Api( settings ).columns().visible( true );
8990 9263 }
8991
9264
8992 9265 // Blitz all `DT` namespaced events (these are internal events, the
8993 9266 // lowercase, `dt` events are user subscribed and they are responsible
8994 9267 // for removing them
8995 jqWrapper.unbind('.DT').find(':not(tbody *)').unbind('.DT');
8996 $(window).unbind('.DT-'+settings.sInstance);
8997
9268 jqWrapper.off('.DT').find(':not(tbody *)').off('.DT');
9269 $(window).off('.DT-'+settings.sInstance);
9270
8998 9271 // When scrolling we had to break the table up - restore it
8999 9272 if ( table != thead.parentNode ) {
9000 9273 jqTable.children('thead').detach();
9001 9274 jqTable.append( thead );
9002 9275 }
9003
9276
9004 9277 if ( tfoot && table != tfoot.parentNode ) {
9005 9278 jqTable.children('tfoot').detach();
9006 9279 jqTable.append( tfoot );
9007 9280 }
9008
9009 // Remove the DataTables generated nodes, events and classes
9010 jqTable.detach();
9011 jqWrapper.detach();
9012
9281
9013 9282 settings.aaSorting = [];
9014 9283 settings.aaSortingFixed = [];
9015 9284 _fnSortingClasses( settings );
9016
9285
9017 9286 $( rows ).removeClass( settings.asStripeClasses.join(' ') );
9018
9287
9019 9288 $('th, td', thead).removeClass( classes.sSortable+' '+
9020 9289 classes.sSortableAsc+' '+classes.sSortableDesc+' '+classes.sSortableNone
9021 9290 );
9022
9291
9023 9292 if ( settings.bJUI ) {
9024 9293 $('th span.'+classes.sSortIcon+ ', td span.'+classes.sSortIcon, thead).detach();
9025 9294 $('th, td', thead).each( function () {
@@ -9028,33 +9297,39 b''
9028 9297 wrapper.detach();
9029 9298 } );
9030 9299 }
9031
9032 if ( ! remove && orig ) {
9033 // insertBefore acts like appendChild if !arg[1]
9034 orig.insertBefore( table, settings.nTableReinsertBefore );
9035 }
9036
9300
9037 9301 // Add the TR elements back into the table in their original order
9038 9302 jqTbody.children().detach();
9039 9303 jqTbody.append( rows );
9040
9041 // Restore the width of the original table - was read from the style property,
9042 // so we can restore directly to that
9043 jqTable
9044 .css( 'width', settings.sDestroyWidth )
9045 .removeClass( classes.sTable );
9046
9047 // If the were originally stripe classes - then we add them back here.
9048 // Note this is not fool proof (for example if not all rows had stripe
9049 // classes - but it's a good effort without getting carried away
9050 ien = settings.asDestroyStripes.length;
9051
9052 if ( ien ) {
9053 jqTbody.children().each( function (i) {
9054 $(this).addClass( settings.asDestroyStripes[i % ien] );
9055 } );
9056 }
9057
9304
9305 // Remove the DataTables generated nodes, events and classes
9306 var removedMethod = remove ? 'remove' : 'detach';
9307 jqTable[ removedMethod ]();
9308 jqWrapper[ removedMethod ]();
9309
9310 // If we need to reattach the table to the document
9311 if ( ! remove && orig ) {
9312 // insertBefore acts like appendChild if !arg[1]
9313 orig.insertBefore( table, settings.nTableReinsertBefore );
9314
9315 // Restore the width of the original table - was read from the style property,
9316 // so we can restore directly to that
9317 jqTable
9318 .css( 'width', settings.sDestroyWidth )
9319 .removeClass( classes.sTable );
9320
9321 // If the were originally stripe classes - then we add them back here.
9322 // Note this is not fool proof (for example if not all rows had stripe
9323 // classes - but it's a good effort without getting carried away
9324 ien = settings.asDestroyStripes.length;
9325
9326 if ( ien ) {
9327 jqTbody.children().each( function (i) {
9328 $(this).addClass( settings.asDestroyStripes[i % ien] );
9329 } );
9330 }
9331 }
9332
9058 9333 /* Remove the settings object from the settings array */
9059 9334 var idx = $.inArray( settings, DataTable.settings );
9060 9335 if ( idx !== -1 ) {
@@ -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 9392 * Version string for plug-ins to check compatibility. Allowed format is
@@ -9072,7 +9396,7 b''
9072 9396 * @type string
9073 9397 * @default Version number
9074 9398 */
9075 DataTable.version = "1.10.4";
9399 DataTable.version = "1.10.13";
9076 9400
9077 9401 /**
9078 9402 * Private data store, containing all of the settings objects that are
@@ -9095,9 +9419,9 b''
9095 9419 * @namespace
9096 9420 */
9097 9421 DataTable.models = {};
9098
9099
9100
9422
9423
9424
9101 9425 /**
9102 9426 * Template object for the way in which DataTables holds information about
9103 9427 * search information for the global filter and individual column filters.
@@ -9110,14 +9434,14 b''
9110 9434 * @default true
9111 9435 */
9112 9436 "bCaseInsensitive": true,
9113
9437
9114 9438 /**
9115 9439 * Applied search term
9116 9440 * @type string
9117 9441 * @default <i>Empty string</i>
9118 9442 */
9119 9443 "sSearch": "",
9120
9444
9121 9445 /**
9122 9446 * Flag to indicate if the search term should be interpreted as a
9123 9447 * regular expression (true) or not (false) and therefore and special
@@ -9126,7 +9450,7 b''
9126 9450 * @default false
9127 9451 */
9128 9452 "bRegex": false,
9129
9453
9130 9454 /**
9131 9455 * Flag to indicate if DataTables is to use its smart filtering or not.
9132 9456 * @type boolean
@@ -9134,10 +9458,10 b''
9134 9458 */
9135 9459 "bSmart": true
9136 9460 };
9137
9138
9139
9140
9461
9462
9463
9464
9141 9465 /**
9142 9466 * Template object for the way in which DataTables holds information about
9143 9467 * each individual row. This is the object format used for the settings
@@ -9151,7 +9475,7 b''
9151 9475 * @default null
9152 9476 */
9153 9477 "nTr": null,
9154
9478
9155 9479 /**
9156 9480 * Array of TD elements for each row. This is null until the row has been
9157 9481 * created.
@@ -9159,7 +9483,7 b''
9159 9483 * @default []
9160 9484 */
9161 9485 "anCells": null,
9162
9486
9163 9487 /**
9164 9488 * Data object from the original data source for the row. This is either
9165 9489 * an array if using the traditional form of DataTables, or an object if
@@ -9170,7 +9494,7 b''
9170 9494 * @default []
9171 9495 */
9172 9496 "_aData": [],
9173
9497
9174 9498 /**
9175 9499 * Sorting data cache - this array is ostensibly the same length as the
9176 9500 * number of columns (although each index is generated only as it is
@@ -9184,7 +9508,7 b''
9184 9508 * @private
9185 9509 */
9186 9510 "_aSortData": null,
9187
9511
9188 9512 /**
9189 9513 * Per cell filtering data cache. As per the sort data cache, used to
9190 9514 * increase the performance of the filtering in DataTables
@@ -9193,7 +9517,7 b''
9193 9517 * @private
9194 9518 */
9195 9519 "_aFilterData": null,
9196
9520
9197 9521 /**
9198 9522 * Filtering data cache. This is the same as the cell filtering cache, but
9199 9523 * in this case a string rather than an array. This is easily computed with
@@ -9204,7 +9528,7 b''
9204 9528 * @private
9205 9529 */
9206 9530 "_sFilterRow": null,
9207
9531
9208 9532 /**
9209 9533 * Cache of the class name that DataTables has applied to the row, so we
9210 9534 * can quickly look at this variable rather than needing to do a DOM check
@@ -9214,7 +9538,7 b''
9214 9538 * @private
9215 9539 */
9216 9540 "_sRowStripe": "",
9217
9541
9218 9542 /**
9219 9543 * Denote if the original data source was from the DOM, or the data source
9220 9544 * object. This is used for invalidating data, so DataTables can
@@ -9224,10 +9548,19 b''
9224 9548 * @default null
9225 9549 * @private
9226 9550 */
9227 "src": null
9551 "src": null,
9552
9553 /**
9554 * Index in the aoData array. This saves an indexOf lookup when we have the
9555 * object, but want to know the index
9556 * @type integer
9557 * @default -1
9558 * @private
9559 */
9560 "idx": -1
9228 9561 };
9229
9230
9562
9563
9231 9564 /**
9232 9565 * Template object for the column information object in DataTables. This object
9233 9566 * is held in the settings aoColumns array and contains all the information that
@@ -9247,7 +9580,7 b''
9247 9580 * @default null
9248 9581 */
9249 9582 "idx": null,
9250
9583
9251 9584 /**
9252 9585 * A list of the columns that sorting should occur on when this column
9253 9586 * is sorted. That this property is an array allows multi-column sorting
@@ -9258,7 +9591,7 b''
9258 9591 * @type array
9259 9592 */
9260 9593 "aDataSort": null,
9261
9594
9262 9595 /**
9263 9596 * Define the sorting directions that are applied to the column, in sequence
9264 9597 * as the column is repeatedly sorted upon - i.e. the first value is used
@@ -9268,26 +9601,26 b''
9268 9601 * @type array
9269 9602 */
9270 9603 "asSorting": null,
9271
9604
9272 9605 /**
9273 9606 * Flag to indicate if the column is searchable, and thus should be included
9274 9607 * in the filtering or not.
9275 9608 * @type boolean
9276 9609 */
9277 9610 "bSearchable": null,
9278
9611
9279 9612 /**
9280 9613 * Flag to indicate if the column is sortable or not.
9281 9614 * @type boolean
9282 9615 */
9283 9616 "bSortable": null,
9284
9617
9285 9618 /**
9286 9619 * Flag to indicate if the column is currently visible in the table or not
9287 9620 * @type boolean
9288 9621 */
9289 9622 "bVisible": null,
9290
9623
9291 9624 /**
9292 9625 * Store for manual type assignment using the `column.type` option. This
9293 9626 * is held in store so we can manipulate the column's `sType` property.
@@ -9296,7 +9629,7 b''
9296 9629 * @private
9297 9630 */
9298 9631 "_sManualType": null,
9299
9632
9300 9633 /**
9301 9634 * Flag to indicate if HTML5 data attributes should be used as the data
9302 9635 * source for filtering or sorting. True is either are.
@@ -9305,7 +9638,7 b''
9305 9638 * @private
9306 9639 */
9307 9640 "_bAttrSrc": false,
9308
9641
9309 9642 /**
9310 9643 * Developer definable function that is called whenever a cell is created (Ajax source,
9311 9644 * etc) or processed for input (DOM source). This can be used as a compliment to mRender
@@ -9319,7 +9652,7 b''
9319 9652 * @default null
9320 9653 */
9321 9654 "fnCreatedCell": null,
9322
9655
9323 9656 /**
9324 9657 * Function to get data from a cell in a column. You should <b>never</b>
9325 9658 * access data directly through _aData internally in DataTables - always use
@@ -9335,7 +9668,7 b''
9335 9668 * @default null
9336 9669 */
9337 9670 "fnGetData": null,
9338
9671
9339 9672 /**
9340 9673 * Function to set data for a cell in the column. You should <b>never</b>
9341 9674 * set the data directly to _aData internally in DataTables - always use
@@ -9348,7 +9681,7 b''
9348 9681 * @default null
9349 9682 */
9350 9683 "fnSetData": null,
9351
9684
9352 9685 /**
9353 9686 * Property to read the value for the cells in the column from the data
9354 9687 * source array / object. If null, then the default content is used, if a
@@ -9357,7 +9690,7 b''
9357 9690 * @default null
9358 9691 */
9359 9692 "mData": null,
9360
9693
9361 9694 /**
9362 9695 * Partner property to mData which is used (only when defined) to get
9363 9696 * the data - i.e. it is basically the same as mData, but without the
@@ -9367,7 +9700,7 b''
9367 9700 * @default null
9368 9701 */
9369 9702 "mRender": null,
9370
9703
9371 9704 /**
9372 9705 * Unique header TH/TD element for this column - this is what the sorting
9373 9706 * listener is attached to (if sorting is enabled.)
@@ -9375,7 +9708,7 b''
9375 9708 * @default null
9376 9709 */
9377 9710 "nTh": null,
9378
9711
9379 9712 /**
9380 9713 * Unique footer TH/TD element for this column (if there is one). Not used
9381 9714 * in DataTables as such, but can be used for plug-ins to reference the
@@ -9384,14 +9717,14 b''
9384 9717 * @default null
9385 9718 */
9386 9719 "nTf": null,
9387
9720
9388 9721 /**
9389 9722 * The class to apply to all TD elements in the table's TBODY for the column
9390 9723 * @type string
9391 9724 * @default null
9392 9725 */
9393 9726 "sClass": null,
9394
9727
9395 9728 /**
9396 9729 * When DataTables calculates the column widths to assign to each column,
9397 9730 * it finds the longest string in each column and then constructs a
@@ -9404,7 +9737,7 b''
9404 9737 * @type string
9405 9738 */
9406 9739 "sContentPadding": null,
9407
9740
9408 9741 /**
9409 9742 * Allows a default value to be given for a column's data, and will be used
9410 9743 * whenever a null data source is encountered (this can be because mData
@@ -9413,14 +9746,14 b''
9413 9746 * @default null
9414 9747 */
9415 9748 "sDefaultContent": null,
9416
9749
9417 9750 /**
9418 9751 * Name for the column, allowing reference to the column by name as well as
9419 9752 * by index (needs a lookup to work by name).
9420 9753 * @type string
9421 9754 */
9422 9755 "sName": null,
9423
9756
9424 9757 /**
9425 9758 * Custom sorting data type - defines which of the available plug-ins in
9426 9759 * afnSortData the custom sorting will use - if any is defined.
@@ -9428,14 +9761,14 b''
9428 9761 * @default std
9429 9762 */
9430 9763 "sSortDataType": 'std',
9431
9764
9432 9765 /**
9433 9766 * Class to be applied to the header element when sorting on this column
9434 9767 * @type string
9435 9768 * @default null
9436 9769 */
9437 9770 "sSortingClass": null,
9438
9771
9439 9772 /**
9440 9773 * Class to be applied to the header element when sorting on this column -
9441 9774 * when jQuery UI theming is used.
@@ -9443,27 +9776,27 b''
9443 9776 * @default null
9444 9777 */
9445 9778 "sSortingClassJUI": null,
9446
9779
9447 9780 /**
9448 9781 * Title of the column - what is seen in the TH element (nTh).
9449 9782 * @type string
9450 9783 */
9451 9784 "sTitle": null,
9452
9785
9453 9786 /**
9454 9787 * Column sorting and filtering type
9455 9788 * @type string
9456 9789 * @default null
9457 9790 */
9458 9791 "sType": null,
9459
9792
9460 9793 /**
9461 9794 * Width of the column
9462 9795 * @type string
9463 9796 * @default null
9464 9797 */
9465 9798 "sWidth": null,
9466
9799
9467 9800 /**
9468 9801 * Width of the column when it was first "encountered"
9469 9802 * @type string
@@ -9471,8 +9804,8 b''
9471 9804 */
9472 9805 "sWidthOrig": null
9473 9806 };
9474
9475
9807
9808
9476 9809 /*
9477 9810 * Developer note: The properties of the object below are given in Hungarian
9478 9811 * notation, that was used as the interface for DataTables prior to v1.10, however
@@ -9488,7 +9821,7 b''
9488 9821 * completely, but that is a massive amount of work and will break current
9489 9822 * installs (therefore is on-hold until v2).
9490 9823 */
9491
9824
9492 9825 /**
9493 9826 * Initialisation options that can be given to DataTables at initialisation
9494 9827 * time.
@@ -9555,8 +9888,8 b''
9555 9888 * } );
9556 9889 */
9557 9890 "aaData": null,
9558
9559
9891
9892
9560 9893 /**
9561 9894 * If ordering is enabled, then DataTables will perform a first pass sort on
9562 9895 * initialisation. You can define which column(s) the sort is performed
@@ -9585,8 +9918,8 b''
9585 9918 * } );
9586 9919 */
9587 9920 "aaSorting": [[0,'asc']],
9588
9589
9921
9922
9590 9923 /**
9591 9924 * This parameter is basically identical to the `sorting` parameter, but
9592 9925 * cannot be overridden by user interaction with the table. What this means
@@ -9608,8 +9941,8 b''
9608 9941 * } )
9609 9942 */
9610 9943 "aaSortingFixed": [],
9611
9612
9944
9945
9613 9946 /**
9614 9947 * DataTables can be instructed to load data to display in the table from a
9615 9948 * Ajax source. This option defines how that Ajax call is made and where to.
@@ -9765,8 +10098,8 b''
9765 10098 * } );
9766 10099 */
9767 10100 "ajax": null,
9768
9769
10101
10102
9770 10103 /**
9771 10104 * This parameter allows you to readily specify the entries in the length drop
9772 10105 * down menu that DataTables shows when pagination is enabled. It can be
@@ -9791,8 +10124,8 b''
9791 10124 * } );
9792 10125 */
9793 10126 "aLengthMenu": [ 10, 25, 50, 100 ],
9794
9795
10127
10128
9796 10129 /**
9797 10130 * The `columns` option in the initialisation parameter allows you to define
9798 10131 * details about the way individual columns behave. For a full list of
@@ -9806,7 +10139,7 b''
9806 10139 * @name DataTable.defaults.column
9807 10140 */
9808 10141 "aoColumns": null,
9809
10142
9810 10143 /**
9811 10144 * Very similar to `columns`, `columnDefs` allows you to target a specific
9812 10145 * column, multiple columns, or all columns, using the `targets` property of
@@ -9827,8 +10160,8 b''
9827 10160 * @name DataTable.defaults.columnDefs
9828 10161 */
9829 10162 "aoColumnDefs": null,
9830
9831
10163
10164
9832 10165 /**
9833 10166 * Basically the same as `search`, this parameter defines the individual column
9834 10167 * filtering state at initialisation time. The array must be of the same size
@@ -9854,8 +10187,8 b''
9854 10187 * } )
9855 10188 */
9856 10189 "aoSearchCols": [],
9857
9858
10190
10191
9859 10192 /**
9860 10193 * An array of CSS classes that should be applied to displayed rows. This
9861 10194 * array may be of any length, and DataTables will apply each class
@@ -9875,8 +10208,8 b''
9875 10208 * } )
9876 10209 */
9877 10210 "asStripeClasses": null,
9878
9879
10211
10212
9880 10213 /**
9881 10214 * Enable or disable automatic column width calculation. This can be disabled
9882 10215 * as an optimisation (it takes some time to calculate the widths) if the
@@ -9895,8 +10228,8 b''
9895 10228 * } );
9896 10229 */
9897 10230 "bAutoWidth": true,
9898
9899
10231
10232
9900 10233 /**
9901 10234 * Deferred rendering can provide DataTables with a huge speed boost when you
9902 10235 * are using an Ajax or JS data source for the table. This option, when set to
@@ -9918,8 +10251,8 b''
9918 10251 * } );
9919 10252 */
9920 10253 "bDeferRender": false,
9921
9922
10254
10255
9923 10256 /**
9924 10257 * Replace a DataTable which matches the given selector and replace it with
9925 10258 * one which has the properties of the new initialisation object passed. If no
@@ -9946,8 +10279,8 b''
9946 10279 * } );
9947 10280 */
9948 10281 "bDestroy": false,
9949
9950
10282
10283
9951 10284 /**
9952 10285 * Enable or disable filtering of data. Filtering in DataTables is "smart" in
9953 10286 * that it allows the end user to input multiple words (space separated) and
@@ -9970,8 +10303,8 b''
9970 10303 * } );
9971 10304 */
9972 10305 "bFilter": true,
9973
9974
10306
10307
9975 10308 /**
9976 10309 * Enable or disable the table information display. This shows information
9977 10310 * about the data that is currently visible on the page, including information
@@ -9990,8 +10323,8 b''
9990 10323 * } );
9991 10324 */
9992 10325 "bInfo": true,
9993
9994
10326
10327
9995 10328 /**
9996 10329 * Enable jQuery UI ThemeRoller support (required as ThemeRoller requires some
9997 10330 * slightly different and additional mark-up from what DataTables has
@@ -10010,8 +10343,8 b''
10010 10343 * } );
10011 10344 */
10012 10345 "bJQueryUI": false,
10013
10014
10346
10347
10015 10348 /**
10016 10349 * Allows the end user to select the size of a formatted page from a select
10017 10350 * menu (sizes are 10, 25, 50 and 100). Requires pagination (`paginate`).
@@ -10029,8 +10362,8 b''
10029 10362 * } );
10030 10363 */
10031 10364 "bLengthChange": true,
10032
10033
10365
10366
10034 10367 /**
10035 10368 * Enable or disable pagination.
10036 10369 * @type boolean
@@ -10047,8 +10380,8 b''
10047 10380 * } );
10048 10381 */
10049 10382 "bPaginate": true,
10050
10051
10383
10384
10052 10385 /**
10053 10386 * Enable or disable the display of a 'processing' indicator when the table is
10054 10387 * being processed (e.g. a sort). This is particularly useful for tables with
@@ -10068,8 +10401,8 b''
10068 10401 * } );
10069 10402 */
10070 10403 "bProcessing": false,
10071
10072
10404
10405
10073 10406 /**
10074 10407 * Retrieve the DataTables object for the given selector. Note that if the
10075 10408 * table has already been initialised, this parameter will cause DataTables
@@ -10106,8 +10439,8 b''
10106 10439 * }
10107 10440 */
10108 10441 "bRetrieve": false,
10109
10110
10442
10443
10111 10444 /**
10112 10445 * When vertical (y) scrolling is enabled, DataTables will force the height of
10113 10446 * the table's viewport to the given height at all times (useful for layout).
@@ -10130,8 +10463,8 b''
10130 10463 * } );
10131 10464 */
10132 10465 "bScrollCollapse": false,
10133
10134
10466
10467
10135 10468 /**
10136 10469 * Configure DataTables to use server-side processing. Note that the
10137 10470 * `ajax` parameter must also be given in order to give DataTables a
@@ -10152,8 +10485,8 b''
10152 10485 * } );
10153 10486 */
10154 10487 "bServerSide": false,
10155
10156
10488
10489
10157 10490 /**
10158 10491 * Enable or disable sorting of columns. Sorting of individual columns can be
10159 10492 * disabled by the `sortable` option for each column.
@@ -10171,8 +10504,8 b''
10171 10504 * } );
10172 10505 */
10173 10506 "bSort": true,
10174
10175
10507
10508
10176 10509 /**
10177 10510 * Enable or display DataTables' ability to sort multiple columns at the
10178 10511 * same time (activated by shift-click by the user).
@@ -10191,8 +10524,8 b''
10191 10524 * } );
10192 10525 */
10193 10526 "bSortMulti": true,
10194
10195
10527
10528
10196 10529 /**
10197 10530 * Allows control over whether DataTables should use the top (true) unique
10198 10531 * cell that is found for a single column, or the bottom (false - default).
@@ -10211,8 +10544,8 b''
10211 10544 * } );
10212 10545 */
10213 10546 "bSortCellsTop": false,
10214
10215
10547
10548
10216 10549 /**
10217 10550 * Enable or disable the addition of the classes `sorting\_1`, `sorting\_2` and
10218 10551 * `sorting\_3` to the columns which are currently being sorted on. This is
@@ -10233,8 +10566,8 b''
10233 10566 * } );
10234 10567 */
10235 10568 "bSortClasses": true,
10236
10237
10569
10570
10238 10571 /**
10239 10572 * Enable or disable state saving. When enabled HTML5 `localStorage` will be
10240 10573 * used to save table display information such as pagination information,
@@ -10258,8 +10591,8 b''
10258 10591 * } );
10259 10592 */
10260 10593 "bStateSave": false,
10261
10262
10594
10595
10263 10596 /**
10264 10597 * This function is called when a TR element is created (and all TD child
10265 10598 * elements have been inserted), or registered if using a DOM source, allowing
@@ -10286,8 +10619,8 b''
10286 10619 * } );
10287 10620 */
10288 10621 "fnCreatedRow": null,
10289
10290
10622
10623
10291 10624 /**
10292 10625 * This function is called on every 'draw' event, and allows you to
10293 10626 * dynamically modify any aspect you want about the created DOM.
@@ -10307,8 +10640,8 b''
10307 10640 * } );
10308 10641 */
10309 10642 "fnDrawCallback": null,
10310
10311
10643
10644
10312 10645 /**
10313 10646 * Identical to fnHeaderCallback() but for the table footer this function
10314 10647 * allows you to modify the table footer on every 'draw' event.
@@ -10335,8 +10668,8 b''
10335 10668 * } )
10336 10669 */
10337 10670 "fnFooterCallback": null,
10338
10339
10671
10672
10340 10673 /**
10341 10674 * When rendering large numbers in the information element for the table
10342 10675 * (i.e. "Showing 1 to 10 of 57 entries") DataTables will render large numbers
@@ -10370,8 +10703,8 b''
10370 10703 this.oLanguage.sThousands
10371 10704 );
10372 10705 },
10373
10374
10706
10707
10375 10708 /**
10376 10709 * This function is called on every 'draw' event, and allows you to
10377 10710 * dynamically modify the header row. This can be used to calculate and
@@ -10399,8 +10732,8 b''
10399 10732 * } )
10400 10733 */
10401 10734 "fnHeaderCallback": null,
10402
10403
10735
10736
10404 10737 /**
10405 10738 * The information element can be used to convey information about the current
10406 10739 * state of the table. Although the internationalisation options presented by
@@ -10429,8 +10762,8 b''
10429 10762 * } );
10430 10763 */
10431 10764 "fnInfoCallback": null,
10432
10433
10765
10766
10434 10767 /**
10435 10768 * Called when the table has been initialised. Normally DataTables will
10436 10769 * initialise sequentially and there will be no need for this function,
@@ -10454,8 +10787,8 b''
10454 10787 * } )
10455 10788 */
10456 10789 "fnInitComplete": null,
10457
10458
10790
10791
10459 10792 /**
10460 10793 * Called at the very start of each table draw and can be used to cancel the
10461 10794 * draw by returning false, any other return (including undefined) results in
@@ -10480,8 +10813,8 b''
10480 10813 * } );
10481 10814 */
10482 10815 "fnPreDrawCallback": null,
10483
10484
10816
10817
10485 10818 /**
10486 10819 * This function allows you to 'post process' each row after it have been
10487 10820 * generated for each table draw, but before it is rendered on screen. This
@@ -10509,8 +10842,8 b''
10509 10842 * } );
10510 10843 */
10511 10844 "fnRowCallback": null,
10512
10513
10845
10846
10514 10847 /**
10515 10848 * __Deprecated__ The functionality provided by this parameter has now been
10516 10849 * superseded by that provided through `ajax`, which should be used instead.
@@ -10535,8 +10868,8 b''
10535 10868 * @deprecated 1.10. Please use `ajax` for this functionality now.
10536 10869 */
10537 10870 "fnServerData": null,
10538
10539
10871
10872
10540 10873 /**
10541 10874 * __Deprecated__ The functionality provided by this parameter has now been
10542 10875 * superseded by that provided through `ajax`, which should be used instead.
@@ -10562,8 +10895,8 b''
10562 10895 * @deprecated 1.10. Please use `ajax` for this functionality now.
10563 10896 */
10564 10897 "fnServerParams": null,
10565
10566
10898
10899
10567 10900 /**
10568 10901 * Load the table state. With this function you can define from where, and how, the
10569 10902 * state of a table is loaded. By default DataTables will load from `localStorage`
@@ -10571,6 +10904,8 b''
10571 10904 * @type function
10572 10905 * @member
10573 10906 * @param {object} settings DataTables settings object
10907 * @param {object} callback Callback that can be executed when done. It
10908 * should be passed the loaded state object.
10574 10909 * @return {object} The DataTables state object to be loaded
10575 10910 *
10576 10911 * @dtopt Callbacks
@@ -10580,21 +10915,14 b''
10580 10915 * $(document).ready( function() {
10581 10916 * $('#example').dataTable( {
10582 10917 * "stateSave": true,
10583 * "stateLoadCallback": function (settings) {
10584 * var o;
10585 *
10586 * // Send an Ajax request to the server to get the data. Note that
10587 * // this is a synchronous request.
10918 * "stateLoadCallback": function (settings, callback) {
10588 10919 * $.ajax( {
10589 10920 * "url": "/state_load",
10590 * "async": false,
10591 10921 * "dataType": "json",
10592 10922 * "success": function (json) {
10593 * o = json;
10923 * callback( json );
10594 10924 * }
10595 10925 * } );
10596 *
10597 * return o;
10598 10926 * }
10599 10927 * } );
10600 10928 * } );
@@ -10608,8 +10936,8 b''
10608 10936 );
10609 10937 } catch (e) {}
10610 10938 },
10611
10612
10939
10940
10613 10941 /**
10614 10942 * Callback which allows modification of the saved state prior to loading that state.
10615 10943 * This callback is called when the table is loading state from the stored data, but
@@ -10646,8 +10974,8 b''
10646 10974 * } );
10647 10975 */
10648 10976 "fnStateLoadParams": null,
10649
10650
10977
10978
10651 10979 /**
10652 10980 * Callback that is called when the state has been loaded from the state saving method
10653 10981 * and the DataTables settings object has been modified as a result of the loaded state.
@@ -10670,8 +10998,8 b''
10670 10998 * } );
10671 10999 */
10672 11000 "fnStateLoaded": null,
10673
10674
11001
11002
10675 11003 /**
10676 11004 * Save the table state. This function allows you to define where and how the state
10677 11005 * information for the table is stored By default DataTables will use `localStorage`
@@ -10709,8 +11037,8 b''
10709 11037 );
10710 11038 } catch (e) {}
10711 11039 },
10712
10713
11040
11041
10714 11042 /**
10715 11043 * Callback which allows modification of the state to be saved. Called when the table
10716 11044 * has changed state a new state save is required. This method allows modification of
@@ -10736,8 +11064,8 b''
10736 11064 * } );
10737 11065 */
10738 11066 "fnStateSaveParams": null,
10739
10740
11067
11068
10741 11069 /**
10742 11070 * Duration for which the saved state information is considered valid. After this period
10743 11071 * has elapsed the state will be returned to the default.
@@ -10756,8 +11084,8 b''
10756 11084 * } )
10757 11085 */
10758 11086 "iStateDuration": 7200,
10759
10760
11087
11088
10761 11089 /**
10762 11090 * When enabled DataTables will not make a request to the server for the first
10763 11091 * page draw - rather it will use the data already on the page (no sorting etc
@@ -10800,8 +11128,8 b''
10800 11128 * } );
10801 11129 */
10802 11130 "iDeferLoading": null,
10803
10804
11131
11132
10805 11133 /**
10806 11134 * Number of rows to display on a single page when using pagination. If
10807 11135 * feature enabled (`lengthChange`) then the end user will be able to override
@@ -10820,8 +11148,8 b''
10820 11148 * } )
10821 11149 */
10822 11150 "iDisplayLength": 10,
10823
10824
11151
11152
10825 11153 /**
10826 11154 * Define the starting point for data display when using DataTables with
10827 11155 * pagination. Note that this parameter is the number of records, rather than
@@ -10841,8 +11169,8 b''
10841 11169 * } )
10842 11170 */
10843 11171 "iDisplayStart": 0,
10844
10845
11172
11173
10846 11174 /**
10847 11175 * By default DataTables allows keyboard navigation of the table (sorting, paging,
10848 11176 * and filtering) by adding a `tabindex` attribute to the required elements. This
@@ -10864,8 +11192,8 b''
10864 11192 * } );
10865 11193 */
10866 11194 "iTabIndex": 0,
10867
10868
11195
11196
10869 11197 /**
10870 11198 * Classes that DataTables assigns to the various components and features
10871 11199 * that it adds to the HTML table. This allows classes to be configured
@@ -10875,8 +11203,8 b''
10875 11203 * @name DataTable.defaults.classes
10876 11204 */
10877 11205 "oClasses": {},
10878
10879
11206
11207
10880 11208 /**
10881 11209 * All strings that DataTables uses in the user interface that it creates
10882 11210 * are defined in this object, allowing you to modified them individually or
@@ -10915,7 +11243,7 b''
10915 11243 * } );
10916 11244 */
10917 11245 "sSortAscending": ": activate to sort column ascending",
10918
11246
10919 11247 /**
10920 11248 * ARIA label that is added to the table headers when the column may be
10921 11249 * sorted descending by activing the column (click or return when focused).
@@ -10939,7 +11267,7 b''
10939 11267 */
10940 11268 "sSortDescending": ": activate to sort column descending"
10941 11269 },
10942
11270
10943 11271 /**
10944 11272 * Pagination string used by DataTables for the built-in pagination
10945 11273 * control types.
@@ -10968,8 +11296,8 b''
10968 11296 * } );
10969 11297 */
10970 11298 "sFirst": "First",
10971
10972
11299
11300
10973 11301 /**
10974 11302 * Text to use when using the 'full_numbers' type of pagination for the
10975 11303 * button to take the user to the last page.
@@ -10991,8 +11319,8 b''
10991 11319 * } );
10992 11320 */
10993 11321 "sLast": "Last",
10994
10995
11322
11323
10996 11324 /**
10997 11325 * Text to use for the 'next' pagination button (to take the user to the
10998 11326 * next page).
@@ -11014,8 +11342,8 b''
11014 11342 * } );
11015 11343 */
11016 11344 "sNext": "Next",
11017
11018
11345
11346
11019 11347 /**
11020 11348 * Text to use for the 'previous' pagination button (to take the user to
11021 11349 * the previous page).
@@ -11038,7 +11366,7 b''
11038 11366 */
11039 11367 "sPrevious": "Previous"
11040 11368 },
11041
11369
11042 11370 /**
11043 11371 * This string is shown in preference to `zeroRecords` when the table is
11044 11372 * empty of data (regardless of filtering). Note that this is an optional
@@ -11060,8 +11388,8 b''
11060 11388 * } );
11061 11389 */
11062 11390 "sEmptyTable": "No data available in table",
11063
11064
11391
11392
11065 11393 /**
11066 11394 * This string gives information to the end user about the information
11067 11395 * that is current on display on the page. The following tokens can be
@@ -11092,8 +11420,8 b''
11092 11420 * } );
11093 11421 */
11094 11422 "sInfo": "Showing _START_ to _END_ of _TOTAL_ entries",
11095
11096
11423
11424
11097 11425 /**
11098 11426 * Display information string for when the table is empty. Typically the
11099 11427 * format of this string should match `info`.
@@ -11113,8 +11441,8 b''
11113 11441 * } );
11114 11442 */
11115 11443 "sInfoEmpty": "Showing 0 to 0 of 0 entries",
11116
11117
11444
11445
11118 11446 /**
11119 11447 * When a user filters the information in a table, this string is appended
11120 11448 * to the information (`info`) to give an idea of how strong the filtering
@@ -11135,8 +11463,8 b''
11135 11463 * } );
11136 11464 */
11137 11465 "sInfoFiltered": "(filtered from _MAX_ total entries)",
11138
11139
11466
11467
11140 11468 /**
11141 11469 * If can be useful to append extra information to the info string at times,
11142 11470 * and this variable does exactly that. This information will be appended to
@@ -11158,8 +11486,8 b''
11158 11486 * } );
11159 11487 */
11160 11488 "sInfoPostFix": "",
11161
11162
11489
11490
11163 11491 /**
11164 11492 * This decimal place operator is a little different from the other
11165 11493 * language options since DataTables doesn't output floating point
@@ -11173,7 +11501,7 b''
11173 11501 * However, multiple different tables on the page can use different
11174 11502 * decimal place characters.
11175 11503 * @type string
11176 * @default
11504 * @default
11177 11505 *
11178 11506 * @dtopt Language
11179 11507 * @name DataTable.defaults.language.decimal
@@ -11189,8 +11517,8 b''
11189 11517 * } );
11190 11518 */
11191 11519 "sDecimal": "",
11192
11193
11520
11521
11194 11522 /**
11195 11523 * DataTables has a build in number formatter (`formatNumber`) which is
11196 11524 * used to format large numbers that are used in the table information.
@@ -11212,8 +11540,8 b''
11212 11540 * } );
11213 11541 */
11214 11542 "sThousands": ",",
11215
11216
11543
11544
11217 11545 /**
11218 11546 * Detail the action that will be taken when the drop down menu for the
11219 11547 * pagination length option is changed. The '_MENU_' variable is replaced
@@ -11253,8 +11581,8 b''
11253 11581 * } );
11254 11582 */
11255 11583 "sLengthMenu": "Show _MENU_ entries",
11256
11257
11584
11585
11258 11586 /**
11259 11587 * When using Ajax sourced data and during the first draw when DataTables is
11260 11588 * gathering the data, this message is shown in an empty row in the table to
@@ -11277,8 +11605,8 b''
11277 11605 * } );
11278 11606 */
11279 11607 "sLoadingRecords": "Loading...",
11280
11281
11608
11609
11282 11610 /**
11283 11611 * Text which is displayed when the table is processing a user action
11284 11612 * (usually a sort command or similar).
@@ -11298,8 +11626,8 b''
11298 11626 * } );
11299 11627 */
11300 11628 "sProcessing": "Processing...",
11301
11302
11629
11630
11303 11631 /**
11304 11632 * Details the actions that will be taken when the user types into the
11305 11633 * filtering input text box. The variable "_INPUT_", if used in the string,
@@ -11333,19 +11661,19 b''
11333 11661 * } );
11334 11662 */
11335 11663 "sSearch": "Search:",
11336
11337
11664
11665
11338 11666 /**
11339 11667 * Assign a `placeholder` attribute to the search `input` element
11340 11668 * @type string
11341 * @default
11669 * @default
11342 11670 *
11343 11671 * @dtopt Language
11344 11672 * @name DataTable.defaults.language.searchPlaceholder
11345 11673 */
11346 11674 "sSearchPlaceholder": "",
11347
11348
11675
11676
11349 11677 /**
11350 11678 * All of the language information can be stored in a file on the
11351 11679 * server-side, which DataTables will look up if this parameter is passed.
@@ -11369,8 +11697,8 b''
11369 11697 * } );
11370 11698 */
11371 11699 "sUrl": "",
11372
11373
11700
11701
11374 11702 /**
11375 11703 * Text shown inside the table records when the is no information to be
11376 11704 * displayed after filtering. `emptyTable` is shown when there is simply no
@@ -11392,8 +11720,8 b''
11392 11720 */
11393 11721 "sZeroRecords": "No matching records found"
11394 11722 },
11395
11396
11723
11724
11397 11725 /**
11398 11726 * This parameter allows you to have define the global filtering state at
11399 11727 * initialisation time. As an object the `search` parameter must be
@@ -11416,8 +11744,8 b''
11416 11744 * } )
11417 11745 */
11418 11746 "oSearch": $.extend( {}, DataTable.models.oSearch ),
11419
11420
11747
11748
11421 11749 /**
11422 11750 * __Deprecated__ The functionality provided by this parameter has now been
11423 11751 * superseded by that provided through `ajax`, which should be used instead.
@@ -11437,8 +11765,8 b''
11437 11765 * @deprecated 1.10. Please use `ajax` for this functionality now.
11438 11766 */
11439 11767 "sAjaxDataProp": "data",
11440
11441
11768
11769
11442 11770 /**
11443 11771 * __Deprecated__ The functionality provided by this parameter has now been
11444 11772 * superseded by that provided through `ajax`, which should be used instead.
@@ -11456,8 +11784,8 b''
11456 11784 * @deprecated 1.10. Please use `ajax` for this functionality now.
11457 11785 */
11458 11786 "sAjaxSource": null,
11459
11460
11787
11788
11461 11789 /**
11462 11790 * This initialisation variable allows you to specify exactly where in the
11463 11791 * DOM you want DataTables to inject the various controls it adds to the page
@@ -11510,8 +11838,8 b''
11510 11838 * } );
11511 11839 */
11512 11840 "sDom": "lfrtip",
11513
11514
11841
11842
11515 11843 /**
11516 11844 * Search delay option. This will throttle full table searches that use the
11517 11845 * DataTables provided search input element (it does not effect calls to
@@ -11530,18 +11858,19 b''
11530 11858 * } )
11531 11859 */
11532 11860 "searchDelay": null,
11533
11534
11535 /**
11536 * DataTables features four different built-in options for the buttons to
11861
11862
11863 /**
11864 * DataTables features six different built-in options for the buttons to
11537 11865 * display for pagination control:
11538 11866 *
11867 * * `numbers` - Page number buttons only
11539 11868 * * `simple` - 'Previous' and 'Next' buttons only
11540 11869 * * 'simple_numbers` - 'Previous' and 'Next' buttons, plus page numbers
11541 11870 * * `full` - 'First', 'Previous', 'Next' and 'Last' buttons
11542 * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus
11543 * page numbers
11544 *
11871 * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus page numbers
11872 * * `first_last_numbers` - 'First' and 'Last' buttons, plus page numbers
11873 *
11545 11874 * Further methods can be added using {@link DataTable.ext.oPagination}.
11546 11875 * @type string
11547 11876 * @default simple_numbers
@@ -11557,8 +11886,8 b''
11557 11886 * } )
11558 11887 */
11559 11888 "sPaginationType": "simple_numbers",
11560
11561
11889
11890
11562 11891 /**
11563 11892 * Enable horizontal scrolling. When a table is too wide to fit into a
11564 11893 * certain layout, or you have a large number of columns in the table, you
@@ -11582,8 +11911,8 b''
11582 11911 * } );
11583 11912 */
11584 11913 "sScrollX": "",
11585
11586
11914
11915
11587 11916 /**
11588 11917 * This property can be used to force a DataTable to use more width than it
11589 11918 * might otherwise do when x-scrolling is enabled. For example if you have a
@@ -11606,8 +11935,8 b''
11606 11935 * } );
11607 11936 */
11608 11937 "sScrollXInner": "",
11609
11610
11938
11939
11611 11940 /**
11612 11941 * Enable vertical scrolling. Vertical scrolling will constrain the DataTable
11613 11942 * to the given height, and enable scrolling for any data which overflows the
@@ -11630,8 +11959,8 b''
11630 11959 * } );
11631 11960 */
11632 11961 "sScrollY": "",
11633
11634
11962
11963
11635 11964 /**
11636 11965 * __Deprecated__ The functionality provided by this parameter has now been
11637 11966 * superseded by that provided through `ajax`, which should be used instead.
@@ -11648,8 +11977,8 b''
11648 11977 * @deprecated 1.10. Please use `ajax` for this functionality now.
11649 11978 */
11650 11979 "sServerMethod": "GET",
11651
11652
11980
11981
11653 11982 /**
11654 11983 * DataTables makes use of renderers when displaying HTML elements for
11655 11984 * a table. These renderers can be added or modified by plug-ins to
@@ -11665,18 +11994,29 b''
11665 11994 * @name DataTable.defaults.renderer
11666 11995 *
11667 11996 */
11668 "renderer": null
11997 "renderer": null,
11998
11999
12000 /**
12001 * Set the data property name that DataTables should use to get a row's id
12002 * to set as the `id` property in the node.
12003 * @type string
12004 * @default DT_RowId
12005 *
12006 * @name DataTable.defaults.rowId
12007 */
12008 "rowId": "DT_RowId"
11669 12009 };
11670
12010
11671 12011 _fnHungarianMap( DataTable.defaults );
11672
11673
11674
12012
12013
12014
11675 12015 /*
11676 12016 * Developer note - See note in model.defaults.js about the use of Hungarian
11677 12017 * notation and camel case.
11678 12018 */
11679
12019
11680 12020 /**
11681 12021 * Column options that can be given to DataTables at initialisation time.
11682 12022 * @namespace
@@ -11722,8 +12062,8 b''
11722 12062 */
11723 12063 "aDataSort": null,
11724 12064 "iDataSort": -1,
11725
11726
12065
12066
11727 12067 /**
11728 12068 * You can control the default ordering direction, and even alter the
11729 12069 * behaviour of the sort handler (i.e. only allow ascending ordering etc)
@@ -11761,8 +12101,8 b''
11761 12101 * } );
11762 12102 */
11763 12103 "asSorting": [ 'asc', 'desc' ],
11764
11765
12104
12105
11766 12106 /**
11767 12107 * Enable or disable filtering on the data in this column.
11768 12108 * @type boolean
@@ -11794,8 +12134,8 b''
11794 12134 * } );
11795 12135 */
11796 12136 "bSearchable": true,
11797
11798
12137
12138
11799 12139 /**
11800 12140 * Enable or disable ordering on this column.
11801 12141 * @type boolean
@@ -11827,8 +12167,8 b''
11827 12167 * } );
11828 12168 */
11829 12169 "bSortable": true,
11830
11831
12170
12171
11832 12172 /**
11833 12173 * Enable or disable the display of this column.
11834 12174 * @type boolean
@@ -11860,8 +12200,8 b''
11860 12200 * } );
11861 12201 */
11862 12202 "bVisible": true,
11863
11864
12203
12204
11865 12205 /**
11866 12206 * Developer definable function that is called whenever a cell is created (Ajax source,
11867 12207 * etc) or processed for input (DOM source). This can be used as a compliment to mRender
@@ -11892,8 +12232,8 b''
11892 12232 * } );
11893 12233 */
11894 12234 "fnCreatedCell": null,
11895
11896
12235
12236
11897 12237 /**
11898 12238 * This parameter has been replaced by `data` in DataTables to ensure naming
11899 12239 * consistency. `dataProp` can still be used, as there is backwards
@@ -11901,8 +12241,8 b''
11901 12241 * recommended that you use `data` in preference to `dataProp`.
11902 12242 * @name DataTable.defaults.column.dataProp
11903 12243 */
11904
11905
12244
12245
11906 12246 /**
11907 12247 * This property can be used to read data from any data source property,
11908 12248 * including deeply nested objects / properties. `data` can be given in a
@@ -12073,8 +12413,8 b''
12073 12413 *
12074 12414 */
12075 12415 "mData": null,
12076
12077
12416
12417
12078 12418 /**
12079 12419 * This property is the rendering partner to `data` and it is suggested that
12080 12420 * when you want to manipulate data for display (including filtering,
@@ -12195,8 +12535,8 b''
12195 12535 * } );
12196 12536 */
12197 12537 "mRender": null,
12198
12199
12538
12539
12200 12540 /**
12201 12541 * Change the cell type created for the column - either TD cells or TH cells. This
12202 12542 * can be useful as TH cells have semantic meaning in the table body, allowing them
@@ -12219,8 +12559,8 b''
12219 12559 * } );
12220 12560 */
12221 12561 "sCellType": "td",
12222
12223
12562
12563
12224 12564 /**
12225 12565 * Class to give to each cell in this column.
12226 12566 * @type string
@@ -12254,7 +12594,7 b''
12254 12594 * } );
12255 12595 */
12256 12596 "sClass": "",
12257
12597
12258 12598 /**
12259 12599 * When DataTables calculates the column widths to assign to each column,
12260 12600 * it finds the longest string in each column and then constructs a
@@ -12287,8 +12627,8 b''
12287 12627 * } );
12288 12628 */
12289 12629 "sContentPadding": "",
12290
12291
12630
12631
12292 12632 /**
12293 12633 * Allows a default value to be given for a column's data, and will be used
12294 12634 * whenever a null data source is encountered (this can be because `data`
@@ -12330,8 +12670,8 b''
12330 12670 * } );
12331 12671 */
12332 12672 "sDefaultContent": null,
12333
12334
12673
12674
12335 12675 /**
12336 12676 * This parameter is only used in DataTables' server-side processing. It can
12337 12677 * be exceptionally useful to know what columns are being displayed on the
@@ -12374,8 +12714,8 b''
12374 12714 * } );
12375 12715 */
12376 12716 "sName": "",
12377
12378
12717
12718
12379 12719 /**
12380 12720 * Defines a data source type for the ordering which can be used to read
12381 12721 * real-time information from the table (updating the internally cached
@@ -12416,8 +12756,8 b''
12416 12756 * } );
12417 12757 */
12418 12758 "sSortDataType": "std",
12419
12420
12759
12760
12421 12761 /**
12422 12762 * The title of this column.
12423 12763 * @type string
@@ -12452,8 +12792,8 b''
12452 12792 * } );
12453 12793 */
12454 12794 "sTitle": null,
12455
12456
12795
12796
12457 12797 /**
12458 12798 * The type allows you to specify how the data for this column will be
12459 12799 * ordered. Four types (string, numeric, date and html (which will strip
@@ -12493,8 +12833,8 b''
12493 12833 * } );
12494 12834 */
12495 12835 "sType": null,
12496
12497
12836
12837
12498 12838 /**
12499 12839 * Defining the width of the column, this parameter may take any CSS value
12500 12840 * (3em, 20px etc). DataTables applies 'smart' widths to columns which have not
@@ -12532,11 +12872,11 b''
12532 12872 */
12533 12873 "sWidth": null
12534 12874 };
12535
12875
12536 12876 _fnHungarianMap( DataTable.defaults.column );
12537
12538
12539
12877
12878
12879
12540 12880 /**
12541 12881 * DataTables settings object - this holds all the information needed for a
12542 12882 * given table, including configuration, data and current application of the
@@ -12565,7 +12905,7 b''
12565 12905 * @namespace
12566 12906 */
12567 12907 "oFeatures": {
12568
12908
12569 12909 /**
12570 12910 * Flag to say if DataTables should automatically try to calculate the
12571 12911 * optimum table and columns widths (true) or not (false).
@@ -12574,7 +12914,7 b''
12574 12914 * @type boolean
12575 12915 */
12576 12916 "bAutoWidth": null,
12577
12917
12578 12918 /**
12579 12919 * Delay the creation of TR and TD elements until they are actually
12580 12920 * needed by a driven page draw. This can give a significant speed
@@ -12585,7 +12925,7 b''
12585 12925 * @type boolean
12586 12926 */
12587 12927 "bDeferRender": null,
12588
12928
12589 12929 /**
12590 12930 * Enable filtering on the table or not. Note that if this is disabled
12591 12931 * then there is no filtering at all on the table, including fnFilter.
@@ -12595,7 +12935,7 b''
12595 12935 * @type boolean
12596 12936 */
12597 12937 "bFilter": null,
12598
12938
12599 12939 /**
12600 12940 * Table information element (the 'Showing x of y records' div) enable
12601 12941 * flag.
@@ -12604,7 +12944,7 b''
12604 12944 * @type boolean
12605 12945 */
12606 12946 "bInfo": null,
12607
12947
12608 12948 /**
12609 12949 * Present a user control allowing the end user to change the page size
12610 12950 * when pagination is enabled.
@@ -12613,7 +12953,7 b''
12613 12953 * @type boolean
12614 12954 */
12615 12955 "bLengthChange": null,
12616
12956
12617 12957 /**
12618 12958 * Pagination enabled or not. Note that if this is disabled then length
12619 12959 * changing must also be disabled.
@@ -12622,7 +12962,7 b''
12622 12962 * @type boolean
12623 12963 */
12624 12964 "bPaginate": null,
12625
12965
12626 12966 /**
12627 12967 * Processing indicator enable flag whenever DataTables is enacting a
12628 12968 * user request - typically an Ajax request for server-side processing.
@@ -12631,7 +12971,7 b''
12631 12971 * @type boolean
12632 12972 */
12633 12973 "bProcessing": null,
12634
12974
12635 12975 /**
12636 12976 * Server-side processing enabled flag - when enabled DataTables will
12637 12977 * get all data from the server for every draw - there is no filtering,
@@ -12641,7 +12981,7 b''
12641 12981 * @type boolean
12642 12982 */
12643 12983 "bServerSide": null,
12644
12984
12645 12985 /**
12646 12986 * Sorting enablement flag.
12647 12987 * Note that this parameter will be set by the initialisation routine. To
@@ -12649,7 +12989,7 b''
12649 12989 * @type boolean
12650 12990 */
12651 12991 "bSort": null,
12652
12992
12653 12993 /**
12654 12994 * Multi-column sorting
12655 12995 * Note that this parameter will be set by the initialisation routine. To
@@ -12657,7 +12997,7 b''
12657 12997 * @type boolean
12658 12998 */
12659 12999 "bSortMulti": null,
12660
13000
12661 13001 /**
12662 13002 * Apply a class to the columns which are being sorted to provide a
12663 13003 * visual highlight or not. This can slow things down when enabled since
@@ -12667,7 +13007,7 b''
12667 13007 * @type boolean
12668 13008 */
12669 13009 "bSortClasses": null,
12670
13010
12671 13011 /**
12672 13012 * State saving enablement flag.
12673 13013 * Note that this parameter will be set by the initialisation routine. To
@@ -12676,8 +13016,8 b''
12676 13016 */
12677 13017 "bStateSave": null
12678 13018 },
12679
12680
13019
13020
12681 13021 /**
12682 13022 * Scrolling settings for a table.
12683 13023 * @namespace
@@ -12691,7 +13031,7 b''
12691 13031 * @type boolean
12692 13032 */
12693 13033 "bCollapse": null,
12694
13034
12695 13035 /**
12696 13036 * Width of the scrollbar for the web-browser's platform. Calculated
12697 13037 * during table initialisation.
@@ -12699,7 +13039,7 b''
12699 13039 * @default 0
12700 13040 */
12701 13041 "iBarWidth": 0,
12702
13042
12703 13043 /**
12704 13044 * Viewport width for horizontal scrolling. Horizontal scrolling is
12705 13045 * disabled if an empty string.
@@ -12708,7 +13048,7 b''
12708 13048 * @type string
12709 13049 */
12710 13050 "sX": null,
12711
13051
12712 13052 /**
12713 13053 * Width to expand the table to when using x-scrolling. Typically you
12714 13054 * should not need to use this.
@@ -12718,7 +13058,7 b''
12718 13058 * @deprecated
12719 13059 */
12720 13060 "sXInner": null,
12721
13061
12722 13062 /**
12723 13063 * Viewport height for vertical scrolling. Vertical scrolling is disabled
12724 13064 * if an empty string.
@@ -12728,7 +13068,7 b''
12728 13068 */
12729 13069 "sY": null
12730 13070 },
12731
13071
12732 13072 /**
12733 13073 * Language information for the table.
12734 13074 * @namespace
@@ -12743,7 +13083,7 b''
12743 13083 */
12744 13084 "fnInfoCallback": null
12745 13085 },
12746
13086
12747 13087 /**
12748 13088 * Browser support parameters
12749 13089 * @namespace
@@ -12756,7 +13096,7 b''
12756 13096 * @default false
12757 13097 */
12758 13098 "bScrollOversize": false,
12759
13099
12760 13100 /**
12761 13101 * Determine if the vertical scrollbar is on the right or left of the
12762 13102 * scrolling container - needed for rtl language layout, although not
@@ -12764,13 +13104,27 b''
12764 13104 * @type boolean
12765 13105 * @default false
12766 13106 */
12767 "bScrollbarLeft": false
13107 "bScrollbarLeft": false,
13108
13109 /**
13110 * Flag for if `getBoundingClientRect` is fully supported or not
13111 * @type boolean
13112 * @default false
13113 */
13114 "bBounding": false,
13115
13116 /**
13117 * Browser scrollbar width
13118 * @type integer
13119 * @default 0
13120 */
13121 "barWidth": 0
12768 13122 },
12769
12770
13123
13124
12771 13125 "ajax": null,
12772
12773
13126
13127
12774 13128 /**
12775 13129 * Array referencing the nodes which are used for the features. The
12776 13130 * parameters of this object match what is allowed by sDom - i.e.
@@ -12786,7 +13140,7 b''
12786 13140 * @default []
12787 13141 */
12788 13142 "aanFeatures": [],
12789
13143
12790 13144 /**
12791 13145 * Store data information - see {@link DataTable.models.oRow} for detailed
12792 13146 * information.
@@ -12794,42 +13148,49 b''
12794 13148 * @default []
12795 13149 */
12796 13150 "aoData": [],
12797
13151
12798 13152 /**
12799 13153 * Array of indexes which are in the current display (after filtering etc)
12800 13154 * @type array
12801 13155 * @default []
12802 13156 */
12803 13157 "aiDisplay": [],
12804
13158
12805 13159 /**
12806 13160 * Array of indexes for display - no filtering
12807 13161 * @type array
12808 13162 * @default []
12809 13163 */
12810 13164 "aiDisplayMaster": [],
12811
13165
13166 /**
13167 * Map of row ids to data indexes
13168 * @type object
13169 * @default {}
13170 */
13171 "aIds": {},
13172
12812 13173 /**
12813 13174 * Store information about each column that is in use
12814 13175 * @type array
12815 13176 * @default []
12816 13177 */
12817 13178 "aoColumns": [],
12818
13179
12819 13180 /**
12820 13181 * Store information about the table's header
12821 13182 * @type array
12822 13183 * @default []
12823 13184 */
12824 13185 "aoHeader": [],
12825
13186
12826 13187 /**
12827 13188 * Store information about the table's footer
12828 13189 * @type array
12829 13190 * @default []
12830 13191 */
12831 13192 "aoFooter": [],
12832
13193
12833 13194 /**
12834 13195 * Store the applied global search information in case we want to force a
12835 13196 * research or compare the old search to a new one.
@@ -12839,7 +13200,7 b''
12839 13200 * @extends DataTable.models.oSearch
12840 13201 */
12841 13202 "oPreviousSearch": {},
12842
13203
12843 13204 /**
12844 13205 * Store the applied search for each column - see
12845 13206 * {@link DataTable.models.oSearch} for the format that is used for the
@@ -12848,7 +13209,7 b''
12848 13209 * @default []
12849 13210 */
12850 13211 "aoPreSearchCols": [],
12851
13212
12852 13213 /**
12853 13214 * Sorting that is applied to the table. Note that the inner arrays are
12854 13215 * used in the following manner:
@@ -12862,7 +13223,7 b''
12862 13223 * @todo These inner arrays should really be objects
12863 13224 */
12864 13225 "aaSorting": null,
12865
13226
12866 13227 /**
12867 13228 * Sorting that is always applied to the table (i.e. prefixed in front of
12868 13229 * aaSorting).
@@ -12872,7 +13233,7 b''
12872 13233 * @default []
12873 13234 */
12874 13235 "aaSortingFixed": [],
12875
13236
12876 13237 /**
12877 13238 * Classes to use for the striping of a table.
12878 13239 * Note that this parameter will be set by the initialisation routine. To
@@ -12881,56 +13242,56 b''
12881 13242 * @default []
12882 13243 */
12883 13244 "asStripeClasses": null,
12884
13245
12885 13246 /**
12886 13247 * If restoring a table - we should restore its striping classes as well
12887 13248 * @type array
12888 13249 * @default []
12889 13250 */
12890 13251 "asDestroyStripes": [],
12891
13252
12892 13253 /**
12893 13254 * If restoring a table - we should restore its width
12894 13255 * @type int
12895 13256 * @default 0
12896 13257 */
12897 13258 "sDestroyWidth": 0,
12898
13259
12899 13260 /**
12900 13261 * Callback functions array for every time a row is inserted (i.e. on a draw).
12901 13262 * @type array
12902 13263 * @default []
12903 13264 */
12904 13265 "aoRowCallback": [],
12905
13266
12906 13267 /**
12907 13268 * Callback functions for the header on each draw.
12908 13269 * @type array
12909 13270 * @default []
12910 13271 */
12911 13272 "aoHeaderCallback": [],
12912
13273
12913 13274 /**
12914 13275 * Callback function for the footer on each draw.
12915 13276 * @type array
12916 13277 * @default []
12917 13278 */
12918 13279 "aoFooterCallback": [],
12919
13280
12920 13281 /**
12921 13282 * Array of callback functions for draw callback functions
12922 13283 * @type array
12923 13284 * @default []
12924 13285 */
12925 13286 "aoDrawCallback": [],
12926
13287
12927 13288 /**
12928 13289 * Array of callback functions for row created function
12929 13290 * @type array
12930 13291 * @default []
12931 13292 */
12932 13293 "aoRowCreatedCallback": [],
12933
13294
12934 13295 /**
12935 13296 * Callback functions for just before the table is redrawn. A return of
12936 13297 * false will be used to cancel the draw.
@@ -12938,15 +13299,15 b''
12938 13299 * @default []
12939 13300 */
12940 13301 "aoPreDrawCallback": [],
12941
13302
12942 13303 /**
12943 13304 * Callback functions for when the table has been initialised.
12944 13305 * @type array
12945 13306 * @default []
12946 13307 */
12947 13308 "aoInitComplete": [],
12948
12949
13309
13310
12950 13311 /**
12951 13312 * Callbacks for modifying the settings to be stored for state saving, prior to
12952 13313 * saving state.
@@ -12954,7 +13315,7 b''
12954 13315 * @default []
12955 13316 */
12956 13317 "aoStateSaveParams": [],
12957
13318
12958 13319 /**
12959 13320 * Callbacks for modifying the settings that have been stored for state saving
12960 13321 * prior to using the stored values to restore the state.
@@ -12962,7 +13323,7 b''
12962 13323 * @default []
12963 13324 */
12964 13325 "aoStateLoadParams": [],
12965
13326
12966 13327 /**
12967 13328 * Callbacks for operating on the settings object once the saved state has been
12968 13329 * loaded
@@ -12970,49 +13331,49 b''
12970 13331 * @default []
12971 13332 */
12972 13333 "aoStateLoaded": [],
12973
13334
12974 13335 /**
12975 13336 * Cache the table ID for quick access
12976 13337 * @type string
12977 13338 * @default <i>Empty string</i>
12978 13339 */
12979 13340 "sTableId": "",
12980
13341
12981 13342 /**
12982 13343 * The TABLE node for the main table
12983 13344 * @type node
12984 13345 * @default null
12985 13346 */
12986 13347 "nTable": null,
12987
13348
12988 13349 /**
12989 13350 * Permanent ref to the thead element
12990 13351 * @type node
12991 13352 * @default null
12992 13353 */
12993 13354 "nTHead": null,
12994
13355
12995 13356 /**
12996 13357 * Permanent ref to the tfoot element - if it exists
12997 13358 * @type node
12998 13359 * @default null
12999 13360 */
13000 13361 "nTFoot": null,
13001
13362
13002 13363 /**
13003 13364 * Permanent ref to the tbody element
13004 13365 * @type node
13005 13366 * @default null
13006 13367 */
13007 13368 "nTBody": null,
13008
13369
13009 13370 /**
13010 13371 * Cache the wrapper node (contains all DataTables controlled elements)
13011 13372 * @type node
13012 13373 * @default null
13013 13374 */
13014 13375 "nTableWrapper": null,
13015
13376
13016 13377 /**
13017 13378 * Indicate if when using server-side processing the loading of data
13018 13379 * should be deferred until the second draw.
@@ -13022,14 +13383,14 b''
13022 13383 * @default false
13023 13384 */
13024 13385 "bDeferLoading": false,
13025
13386
13026 13387 /**
13027 13388 * Indicate if all required information has been read in
13028 13389 * @type boolean
13029 13390 * @default false
13030 13391 */
13031 13392 "bInitialised": false,
13032
13393
13033 13394 /**
13034 13395 * Information about open rows. Each object in the array has the parameters
13035 13396 * 'nTr' and 'nParent'
@@ -13037,7 +13398,7 b''
13037 13398 * @default []
13038 13399 */
13039 13400 "aoOpenRows": [],
13040
13401
13041 13402 /**
13042 13403 * Dictate the positioning of DataTables' control elements - see
13043 13404 * {@link DataTable.model.oInit.sDom}.
@@ -13047,14 +13408,14 b''
13047 13408 * @default null
13048 13409 */
13049 13410 "sDom": null,
13050
13411
13051 13412 /**
13052 13413 * Search delay (in mS)
13053 13414 * @type integer
13054 13415 * @default null
13055 13416 */
13056 13417 "searchDelay": null,
13057
13418
13058 13419 /**
13059 13420 * Which type of pagination should be used.
13060 13421 * Note that this parameter will be set by the initialisation routine. To
@@ -13063,7 +13424,7 b''
13063 13424 * @default two_button
13064 13425 */
13065 13426 "sPaginationType": "two_button",
13066
13427
13067 13428 /**
13068 13429 * The state duration (for `stateSave`) in seconds.
13069 13430 * Note that this parameter will be set by the initialisation routine. To
@@ -13072,7 +13433,7 b''
13072 13433 * @default 0
13073 13434 */
13074 13435 "iStateDuration": 0,
13075
13436
13076 13437 /**
13077 13438 * Array of callback functions for state saving. Each array element is an
13078 13439 * object with the following parameters:
@@ -13087,7 +13448,7 b''
13087 13448 * @default []
13088 13449 */
13089 13450 "aoStateSave": [],
13090
13451
13091 13452 /**
13092 13453 * Array of callback functions for state loading. Each array element is an
13093 13454 * object with the following parameters:
@@ -13100,21 +13461,21 b''
13100 13461 * @default []
13101 13462 */
13102 13463 "aoStateLoad": [],
13103
13464
13104 13465 /**
13105 13466 * State that was saved. Useful for back reference
13106 13467 * @type object
13107 13468 * @default null
13108 13469 */
13109 13470 "oSavedState": null,
13110
13471
13111 13472 /**
13112 13473 * State that was loaded. Useful for back reference
13113 13474 * @type object
13114 13475 * @default null
13115 13476 */
13116 13477 "oLoadedState": null,
13117
13478
13118 13479 /**
13119 13480 * Source url for AJAX data for the table.
13120 13481 * Note that this parameter will be set by the initialisation routine. To
@@ -13123,7 +13484,7 b''
13123 13484 * @default null
13124 13485 */
13125 13486 "sAjaxSource": null,
13126
13487
13127 13488 /**
13128 13489 * Property from a given object from which to read the table data from. This
13129 13490 * can be an empty string (when not server-side processing), in which case
@@ -13133,14 +13494,14 b''
13133 13494 * @type string
13134 13495 */
13135 13496 "sAjaxDataProp": null,
13136
13497
13137 13498 /**
13138 13499 * Note if draw should be blocked while getting data
13139 13500 * @type boolean
13140 13501 * @default true
13141 13502 */
13142 13503 "bAjaxDataGet": true,
13143
13504
13144 13505 /**
13145 13506 * The last jQuery XHR object that was used for server-side data gathering.
13146 13507 * This can be used for working with the XHR information in one of the
@@ -13149,21 +13510,21 b''
13149 13510 * @default null
13150 13511 */
13151 13512 "jqXHR": null,
13152
13513
13153 13514 /**
13154 13515 * JSON returned from the server in the last Ajax request
13155 13516 * @type object
13156 13517 * @default undefined
13157 13518 */
13158 13519 "json": undefined,
13159
13520
13160 13521 /**
13161 13522 * Data submitted as part of the last Ajax request
13162 13523 * @type object
13163 13524 * @default undefined
13164 13525 */
13165 13526 "oAjaxData": undefined,
13166
13527
13167 13528 /**
13168 13529 * Function to get the server-side data.
13169 13530 * Note that this parameter will be set by the initialisation routine. To
@@ -13171,7 +13532,7 b''
13171 13532 * @type function
13172 13533 */
13173 13534 "fnServerData": null,
13174
13535
13175 13536 /**
13176 13537 * Functions which are called prior to sending an Ajax request so extra
13177 13538 * parameters can easily be sent to the server
@@ -13179,7 +13540,7 b''
13179 13540 * @default []
13180 13541 */
13181 13542 "aoServerParams": [],
13182
13543
13183 13544 /**
13184 13545 * Send the XHR HTTP method - GET or POST (could be PUT or DELETE if
13185 13546 * required).
@@ -13188,7 +13549,7 b''
13188 13549 * @type string
13189 13550 */
13190 13551 "sServerMethod": null,
13191
13552
13192 13553 /**
13193 13554 * Format numbers for display.
13194 13555 * Note that this parameter will be set by the initialisation routine. To
@@ -13196,7 +13557,7 b''
13196 13557 * @type function
13197 13558 */
13198 13559 "fnFormatNumber": null,
13199
13560
13200 13561 /**
13201 13562 * List of options that can be used for the user selectable length menu.
13202 13563 * Note that this parameter will be set by the initialisation routine. To
@@ -13205,7 +13566,7 b''
13205 13566 * @default []
13206 13567 */
13207 13568 "aLengthMenu": null,
13208
13569
13209 13570 /**
13210 13571 * Counter for the draws that the table does. Also used as a tracker for
13211 13572 * server-side processing
@@ -13213,35 +13574,35 b''
13213 13574 * @default 0
13214 13575 */
13215 13576 "iDraw": 0,
13216
13577
13217 13578 /**
13218 13579 * Indicate if a redraw is being done - useful for Ajax
13219 13580 * @type boolean
13220 13581 * @default false
13221 13582 */
13222 13583 "bDrawing": false,
13223
13584
13224 13585 /**
13225 13586 * Draw index (iDraw) of the last error when parsing the returned data
13226 13587 * @type int
13227 13588 * @default -1
13228 13589 */
13229 13590 "iDrawError": -1,
13230
13591
13231 13592 /**
13232 13593 * Paging display length
13233 13594 * @type int
13234 13595 * @default 10
13235 13596 */
13236 13597 "_iDisplayLength": 10,
13237
13598
13238 13599 /**
13239 13600 * Paging start point - aiDisplay index
13240 13601 * @type int
13241 13602 * @default 0
13242 13603 */
13243 13604 "_iDisplayStart": 0,
13244
13605
13245 13606 /**
13246 13607 * Server-side processing - number of records in the result set
13247 13608 * (i.e. before filtering), Use fnRecordsTotal rather than
@@ -13252,7 +13613,7 b''
13252 13613 * @private
13253 13614 */
13254 13615 "_iRecordsTotal": 0,
13255
13616
13256 13617 /**
13257 13618 * Server-side processing - number of records in the current display set
13258 13619 * (i.e. after filtering). Use fnRecordsDisplay rather than
@@ -13263,7 +13624,7 b''
13263 13624 * @private
13264 13625 */
13265 13626 "_iRecordsDisplay": 0,
13266
13627
13267 13628 /**
13268 13629 * Flag to indicate if jQuery UI marking and classes should be used.
13269 13630 * Note that this parameter will be set by the initialisation routine. To
@@ -13271,14 +13632,14 b''
13271 13632 * @type boolean
13272 13633 */
13273 13634 "bJUI": null,
13274
13635
13275 13636 /**
13276 13637 * The classes to use for the table
13277 13638 * @type object
13278 13639 * @default {}
13279 13640 */
13280 13641 "oClasses": {},
13281
13642
13282 13643 /**
13283 13644 * Flag attached to the settings object so you can check in the draw
13284 13645 * callback if filtering has been done in the draw. Deprecated in favour of
@@ -13288,7 +13649,7 b''
13288 13649 * @deprecated
13289 13650 */
13290 13651 "bFiltered": false,
13291
13652
13292 13653 /**
13293 13654 * Flag attached to the settings object so you can check in the draw
13294 13655 * callback if sorting has been done in the draw. Deprecated in favour of
@@ -13298,7 +13659,7 b''
13298 13659 * @deprecated
13299 13660 */
13300 13661 "bSorted": false,
13301
13662
13302 13663 /**
13303 13664 * Indicate that if multiple rows are in the header and there is more than
13304 13665 * one unique cell per column, if the top one (true) or bottom one (false)
@@ -13308,14 +13669,14 b''
13308 13669 * @type boolean
13309 13670 */
13310 13671 "bSortCellsTop": null,
13311
13672
13312 13673 /**
13313 13674 * Initialisation object that is used for the table
13314 13675 * @type object
13315 13676 * @default null
13316 13677 */
13317 13678 "oInit": null,
13318
13679
13319 13680 /**
13320 13681 * Destroy callback functions - for plug-ins to attach themselves to the
13321 13682 * destroy so they can clean up markup and events.
@@ -13323,8 +13684,8 b''
13323 13684 * @default []
13324 13685 */
13325 13686 "aoDestroyCallback": [],
13326
13327
13687
13688
13328 13689 /**
13329 13690 * Get the number of records in the current record set, before filtering
13330 13691 * @type function
@@ -13335,7 +13696,7 b''
13335 13696 this._iRecordsTotal * 1 :
13336 13697 this.aiDisplayMaster.length;
13337 13698 },
13338
13699
13339 13700 /**
13340 13701 * Get the number of records in the current record set, after filtering
13341 13702 * @type function
@@ -13346,7 +13707,7 b''
13346 13707 this._iRecordsDisplay * 1 :
13347 13708 this.aiDisplay.length;
13348 13709 },
13349
13710
13350 13711 /**
13351 13712 * Get the display end point - aiDisplay index
13352 13713 * @type function
@@ -13360,7 +13721,7 b''
13360 13721 records = this.aiDisplay.length,
13361 13722 features = this.oFeatures,
13362 13723 paginate = features.bPaginate;
13363
13724
13364 13725 if ( features.bServerSide ) {
13365 13726 return paginate === false || len === -1 ?
13366 13727 start + records :
@@ -13372,14 +13733,14 b''
13372 13733 calc;
13373 13734 }
13374 13735 },
13375
13736
13376 13737 /**
13377 13738 * The DataTables object for this table
13378 13739 * @type object
13379 13740 * @default null
13380 13741 */
13381 13742 "oInstance": null,
13382
13743
13383 13744 /**
13384 13745 * Unique identifier for each instance of the DataTables object. If there
13385 13746 * is an ID on the table node, then it takes that value, otherwise an
@@ -13388,36 +13749,50 b''
13388 13749 * @default null
13389 13750 */
13390 13751 "sInstance": null,
13391
13752
13392 13753 /**
13393 13754 * tabindex attribute value that is added to DataTables control elements, allowing
13394 13755 * keyboard navigation of the table and its controls.
13395 13756 */
13396 13757 "iTabIndex": 0,
13397
13758
13398 13759 /**
13399 13760 * DIV container for the footer scrolling table if scrolling
13400 13761 */
13401 13762 "nScrollHead": null,
13402
13763
13403 13764 /**
13404 13765 * DIV container for the footer scrolling table if scrolling
13405 13766 */
13406 13767 "nScrollFoot": null,
13407
13768
13408 13769 /**
13409 13770 * Last applied sort
13410 13771 * @type array
13411 13772 * @default []
13412 13773 */
13413 13774 "aLastSort": [],
13414
13775
13415 13776 /**
13416 13777 * Stored plug-in instances
13417 13778 * @type object
13418 13779 * @default {}
13419 13780 */
13420 "oPlugins": {}
13781 "oPlugins": {},
13782
13783 /**
13784 * Function used to get a row's id from the row's data
13785 * @type function
13786 * @default null
13787 */
13788 "rowIdFn": null,
13789
13790 /**
13791 * Data location where to store a row's id
13792 * @type string
13793 * @default null
13794 */
13795 "rowId": null
13421 13796 };
13422 13797
13423 13798 /**
@@ -13430,11 +13805,11 b''
13430 13805 * @namespace
13431 13806 * @extends DataTable.models.ext
13432 13807 */
13433
13434
13808
13809
13435 13810 /**
13436 13811 * DataTables extensions
13437 *
13812 *
13438 13813 * This namespace acts as a collection area for plug-ins that can be used to
13439 13814 * extend DataTables capabilities. Indeed many of the build in methods
13440 13815 * use this method to provide their own capabilities (sorting methods for
@@ -13447,36 +13822,55 b''
13447 13822 */
13448 13823 DataTable.ext = _ext = {
13449 13824 /**
13825 * Buttons. For use with the Buttons extension for DataTables. This is
13826 * defined here so other extensions can define buttons regardless of load
13827 * order. It is _not_ used by DataTables core.
13828 *
13829 * @type object
13830 * @default {}
13831 */
13832 buttons: {},
13833
13834
13835 /**
13450 13836 * Element class names
13451 13837 *
13452 13838 * @type object
13453 13839 * @default {}
13454 13840 */
13455 13841 classes: {},
13456
13457
13842
13843
13844 /**
13845 * DataTables build type (expanded by the download builder)
13846 *
13847 * @type string
13848 */
13849 builder: "-source-",
13850
13851
13458 13852 /**
13459 13853 * Error reporting.
13460 *
13461 * How should DataTables report an error. Can take the value 'alert' or
13462 * 'throw'
13463 *
13464 * @type string
13854 *
13855 * How should DataTables report an error. Can take the value 'alert',
13856 * 'throw', 'none' or a function.
13857 *
13858 * @type string|function
13465 13859 * @default alert
13466 13860 */
13467 13861 errMode: "alert",
13468
13469
13862
13863
13470 13864 /**
13471 13865 * Feature plug-ins.
13472 *
13866 *
13473 13867 * This is an array of objects which describe the feature plug-ins that are
13474 13868 * available to DataTables. These feature plug-ins are then available for
13475 13869 * use through the `dom` initialisation option.
13476 *
13870 *
13477 13871 * Each feature plug-in is described by an object which must have the
13478 13872 * following properties:
13479 *
13873 *
13480 13874 * * `fnInit` - function that is used to initialise the plug-in,
13481 13875 * * `cFeature` - a character so the feature can be enabled by the `dom`
13482 13876 * instillation option. This is case sensitive.
@@ -13487,7 +13881,7 b''
13487 13881 * {@link DataTable.models.oSettings}
13488 13882 *
13489 13883 * And the following return is expected:
13490 *
13884 *
13491 13885 * * {node|null} The element which contains your feature. Note that the
13492 13886 * return may also be void if your plug-in does not require to inject any
13493 13887 * DOM elements into DataTables control (`dom`) - for example this might
@@ -13505,11 +13899,11 b''
13505 13899 * } );
13506 13900 */
13507 13901 feature: [],
13508
13509
13902
13903
13510 13904 /**
13511 13905 * Row searching.
13512 *
13906 *
13513 13907 * This method of searching is complimentary to the default type based
13514 13908 * searching, and a lot more comprehensive as it allows you complete control
13515 13909 * over the searching logic. Each element in this array is a function
@@ -13566,11 +13960,42 b''
13566 13960 * );
13567 13961 */
13568 13962 search: [],
13569
13570
13963
13964
13965 /**
13966 * Selector extensions
13967 *
13968 * The `selector` option can be used to extend the options available for the
13969 * selector modifier options (`selector-modifier` object data type) that
13970 * each of the three built in selector types offer (row, column and cell +
13971 * their plural counterparts). For example the Select extension uses this
13972 * mechanism to provide an option to select only rows, columns and cells
13973 * that have been marked as selected by the end user (`{selected: true}`),
13974 * which can be used in conjunction with the existing built in selector
13975 * options.
13976 *
13977 * Each property is an array to which functions can be pushed. The functions
13978 * take three attributes:
13979 *
13980 * * Settings object for the host table
13981 * * Options object (`selector-modifier` object type)
13982 * * Array of selected item indexes
13983 *
13984 * The return is an array of the resulting item indexes after the custom
13985 * selector has been applied.
13986 *
13987 * @type object
13988 */
13989 selector: {
13990 cell: [],
13991 column: [],
13992 row: []
13993 },
13994
13995
13571 13996 /**
13572 13997 * Internal functions, exposed for used in plug-ins.
13573 *
13998 *
13574 13999 * Please note that you should not need to use the internal methods for
13575 14000 * anything other than a plug-in (and even then, try to avoid if possible).
13576 14001 * The internal function may change between releases.
@@ -13579,8 +14004,8 b''
13579 14004 * @default {}
13580 14005 */
13581 14006 internal: {},
13582
13583
14007
14008
13584 14009 /**
13585 14010 * Legacy configuration options. Enable and disable legacy options that
13586 14011 * are available in DataTables.
@@ -13597,11 +14022,11 b''
13597 14022 */
13598 14023 ajax: null
13599 14024 },
13600
13601
14025
14026
13602 14027 /**
13603 14028 * Pagination plug-in methods.
13604 *
14029 *
13605 14030 * Each entry in this object is a function and defines which buttons should
13606 14031 * be shown by the pagination rendering method that is used for the table:
13607 14032 * {@link DataTable.ext.renderer.pageButton}. The renderer addresses how the
@@ -13645,26 +14070,26 b''
13645 14070 * };
13646 14071 */
13647 14072 pager: {},
13648
13649
14073
14074
13650 14075 renderer: {
13651 14076 pageButton: {},
13652 14077 header: {}
13653 14078 },
13654
13655
14079
14080
13656 14081 /**
13657 14082 * Ordering plug-ins - custom data source
13658 *
14083 *
13659 14084 * The extension options for ordering of data available here is complimentary
13660 14085 * to the default type based ordering that DataTables typically uses. It
13661 14086 * allows much greater control over the the data that is being used to
13662 14087 * order a column, but is necessarily therefore more complex.
13663 *
14088 *
13664 14089 * This type of ordering is useful if you want to do ordering based on data
13665 14090 * live from the DOM (for example the contents of an 'input' element) rather
13666 14091 * than just the static string that DataTables knows of.
13667 *
14092 *
13668 14093 * The way these plug-ins work is that you create an array of the values you
13669 14094 * wish to be ordering for the column in question and then return that
13670 14095 * array. The data in the array much be in the index order of the rows in
@@ -13694,8 +14119,8 b''
13694 14119 * }
13695 14120 */
13696 14121 order: {},
13697
13698
14122
14123
13699 14124 /**
13700 14125 * Type based plug-ins.
13701 14126 *
@@ -13748,8 +14173,8 b''
13748 14173 * );
13749 14174 */
13750 14175 detect: [],
13751
13752
14176
14177
13753 14178 /**
13754 14179 * Type based search formatting.
13755 14180 *
@@ -13759,7 +14184,7 b''
13759 14184 *
13760 14185 * Note that is a search is not defined for a column of a given type,
13761 14186 * no search formatting will be performed.
13762 *
14187 *
13763 14188 * Pre-processing of searching data plug-ins - When you assign the sType
13764 14189 * for a column (or have it automatically detected for you by DataTables
13765 14190 * or a type detection plug-in), you will typically be using this for
@@ -13787,8 +14212,8 b''
13787 14212 * }
13788 14213 */
13789 14214 search: {},
13790
13791
14215
14216
13792 14217 /**
13793 14218 * Type based ordering.
13794 14219 *
@@ -13829,7 +14254,7 b''
13829 14254 * than the second parameter, ===0 if the two parameters are equal and
13830 14255 * >0 if the first parameter should be sorted height than the second
13831 14256 * parameter.
13832 *
14257 *
13833 14258 * @type object
13834 14259 * @default {}
13835 14260 *
@@ -13855,7 +14280,7 b''
13855 14280 */
13856 14281 order: {}
13857 14282 },
13858
14283
13859 14284 /**
13860 14285 * Unique DataTables instance counter
13861 14286 *
@@ -13863,39 +14288,39 b''
13863 14288 * @private
13864 14289 */
13865 14290 _unique: 0,
13866
13867
14291
14292
13868 14293 //
13869 14294 // Depreciated
13870 14295 // The following properties are retained for backwards compatiblity only.
13871 14296 // The should not be used in new projects and will be removed in a future
13872 14297 // version
13873 14298 //
13874
14299
13875 14300 /**
13876 14301 * Version check function.
13877 14302 * @type function
13878 14303 * @depreciated Since 1.10
13879 14304 */
13880 14305 fnVersionCheck: DataTable.fnVersionCheck,
13881
13882
14306
14307
13883 14308 /**
13884 14309 * Index for what 'this' index API functions should use
13885 14310 * @type int
13886 14311 * @deprecated Since v1.10
13887 14312 */
13888 14313 iApiIndex: 0,
13889
13890
14314
14315
13891 14316 /**
13892 14317 * jQuery UI class container
13893 14318 * @type object
13894 14319 * @deprecated Since v1.10
13895 14320 */
13896 14321 oJUIClasses: {},
13897
13898
14322
14323
13899 14324 /**
13900 14325 * Software version
13901 14326 * @type string
@@ -13903,8 +14328,8 b''
13903 14328 */
13904 14329 sVersion: DataTable.version
13905 14330 };
13906
13907
14331
14332
13908 14333 //
13909 14334 // Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts
13910 14335 //
@@ -13919,24 +14344,24 b''
13919 14344 oStdClasses: _ext.classes,
13920 14345 oPagination: _ext.pager
13921 14346 } );
13922
13923
14347
14348
13924 14349 $.extend( DataTable.ext.classes, {
13925 14350 "sTable": "dataTable",
13926 14351 "sNoFooter": "no-footer",
13927
14352
13928 14353 /* Paging buttons */
13929 14354 "sPageButton": "paginate_button",
13930 14355 "sPageButtonActive": "current",
13931 14356 "sPageButtonDisabled": "disabled",
13932
14357
13933 14358 /* Striping classes */
13934 14359 "sStripeOdd": "odd",
13935 14360 "sStripeEven": "even",
13936
14361
13937 14362 /* Empty row */
13938 14363 "sRowEmpty": "dataTables_empty",
13939
14364
13940 14365 /* Features */
13941 14366 "sWrapper": "dataTables_wrapper",
13942 14367 "sFilter": "dataTables_filter",
@@ -13944,7 +14369,7 b''
13944 14369 "sPaging": "dataTables_paginate paging_", /* Note that the type is postfixed */
13945 14370 "sLength": "dataTables_length",
13946 14371 "sProcessing": "dataTables_processing",
13947
14372
13948 14373 /* Sorting */
13949 14374 "sSortAsc": "sorting_asc",
13950 14375 "sSortDesc": "sorting_desc",
@@ -13953,13 +14378,13 b''
13953 14378 "sSortableDesc": "sorting_desc_disabled",
13954 14379 "sSortableNone": "sorting_disabled",
13955 14380 "sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */
13956
14381
13957 14382 /* Filtering */
13958 14383 "sFilterInput": "",
13959
14384
13960 14385 /* Page length */
13961 14386 "sLengthSelect": "",
13962
14387
13963 14388 /* Scrolling */
13964 14389 "sScrollWrapper": "dataTables_scroll",
13965 14390 "sScrollHead": "dataTables_scrollHead",
@@ -13967,11 +14392,11 b''
13967 14392 "sScrollBody": "dataTables_scrollBody",
13968 14393 "sScrollFoot": "dataTables_scrollFoot",
13969 14394 "sScrollFootInner": "dataTables_scrollFootInner",
13970
14395
13971 14396 /* Misc */
13972 14397 "sHeaderTH": "",
13973 14398 "sFooterTH": "",
13974
14399
13975 14400 // Deprecated
13976 14401 "sSortJUIAsc": "",
13977 14402 "sSortJUIDesc": "",
@@ -13983,31 +14408,31 b''
13983 14408 "sJUIHeader": "",
13984 14409 "sJUIFooter": ""
13985 14410 } );
13986
13987
14411
14412
13988 14413 (function() {
13989
14414
13990 14415 // Reused strings for better compression. Closure compiler appears to have a
13991 14416 // weird edge case where it is trying to expand strings rather than use the
13992 14417 // variable version. This results in about 200 bytes being added, for very
13993 14418 // little preference benefit since it this run on script load only.
13994 14419 var _empty = '';
13995 14420 _empty = '';
13996
14421
13997 14422 var _stateDefault = _empty + 'ui-state-default';
13998 14423 var _sortIcon = _empty + 'css_right ui-icon ui-icon-';
13999 14424 var _headerFooter = _empty + 'fg-toolbar ui-toolbar ui-widget-header ui-helper-clearfix';
14000
14425
14001 14426 $.extend( DataTable.ext.oJUIClasses, DataTable.ext.classes, {
14002 14427 /* Full numbers paging buttons */
14003 14428 "sPageButton": "fg-button ui-button "+_stateDefault,
14004 14429 "sPageButtonActive": "ui-state-disabled",
14005 14430 "sPageButtonDisabled": "ui-state-disabled",
14006
14431
14007 14432 /* Features */
14008 14433 "sPaging": "dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi "+
14009 14434 "ui-buttonset-multi paging_", /* Note that the type is postfixed */
14010
14435
14011 14436 /* Sorting */
14012 14437 "sSortAsc": _stateDefault+" sorting_asc",
14013 14438 "sSortDesc": _stateDefault+" sorting_desc",
@@ -14022,31 +14447,31 b''
14022 14447 "sSortJUIDescAllowed": _sortIcon+"carat-1-s",
14023 14448 "sSortJUIWrapper": "DataTables_sort_wrapper",
14024 14449 "sSortIcon": "DataTables_sort_icon",
14025
14450
14026 14451 /* Scrolling */
14027 14452 "sScrollHead": "dataTables_scrollHead "+_stateDefault,
14028 14453 "sScrollFoot": "dataTables_scrollFoot "+_stateDefault,
14029
14454
14030 14455 /* Misc */
14031 14456 "sHeaderTH": _stateDefault,
14032 14457 "sFooterTH": _stateDefault,
14033 14458 "sJUIHeader": _headerFooter+" ui-corner-tl ui-corner-tr",
14034 14459 "sJUIFooter": _headerFooter+" ui-corner-bl ui-corner-br"
14035 14460 } );
14036
14461
14037 14462 }());
14038
14039
14040
14463
14464
14465
14041 14466 var extPagination = DataTable.ext.pager;
14042
14467
14043 14468 function _numbers ( page, pages ) {
14044 14469 var
14045 14470 numbers = [],
14046 14471 buttons = extPagination.numbers_length,
14047 14472 half = Math.floor( buttons / 2 ),
14048 14473 i = 1;
14049
14474
14050 14475 if ( pages <= buttons ) {
14051 14476 numbers = _range( 0, pages );
14052 14477 }
@@ -14061,107 +14486,118 b''
14061 14486 numbers.splice( 0, 0, 0 );
14062 14487 }
14063 14488 else {
14064 numbers = _range( page-1, page+2 );
14489 numbers = _range( page-half+2, page+half-1 );
14065 14490 numbers.push( 'ellipsis' );
14066 14491 numbers.push( pages-1 );
14067 14492 numbers.splice( 0, 0, 'ellipsis' );
14068 14493 numbers.splice( 0, 0, 0 );
14069 14494 }
14070
14495
14071 14496 numbers.DT_el = 'span';
14072 14497 return numbers;
14073 14498 }
14074
14075
14499
14500
14076 14501 $.extend( extPagination, {
14077 14502 simple: function ( page, pages ) {
14078 14503 return [ 'previous', 'next' ];
14079 14504 },
14080
14505
14081 14506 full: function ( page, pages ) {
14082 14507 return [ 'first', 'previous', 'next', 'last' ];
14083 14508 },
14084
14509
14510 numbers: function ( page, pages ) {
14511 return [ _numbers(page, pages) ];
14512 },
14513
14085 14514 simple_numbers: function ( page, pages ) {
14086 14515 return [ 'previous', _numbers(page, pages), 'next' ];
14087 14516 },
14088
14517
14089 14518 full_numbers: function ( page, pages ) {
14090 14519 return [ 'first', 'previous', _numbers(page, pages), 'next', 'last' ];
14091 14520 },
14092
14521
14522 first_last_numbers: function (page, pages) {
14523 return ['first', _numbers(page, pages), 'last'];
14524 },
14525
14093 14526 // For testing and plug-ins to use
14094 14527 _numbers: _numbers,
14528
14529 // Number of number buttons (including ellipsis) to show. _Must be odd!_
14095 14530 numbers_length: 7
14096 14531 } );
14097
14098
14532
14533
14099 14534 $.extend( true, DataTable.ext.renderer, {
14100 14535 pageButton: {
14101 14536 _: function ( settings, host, idx, buttons, page, pages ) {
14102 14537 var classes = settings.oClasses;
14103 14538 var lang = settings.oLanguage.oPaginate;
14539 var aria = settings.oLanguage.oAria.paginate || {};
14104 14540 var btnDisplay, btnClass, counter=0;
14105
14541
14106 14542 var attach = function( container, buttons ) {
14107 14543 var i, ien, node, button;
14108 14544 var clickHandler = function ( e ) {
14109 14545 _fnPageChange( settings, e.data.action, true );
14110 14546 };
14111
14547
14112 14548 for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
14113 14549 button = buttons[i];
14114
14550
14115 14551 if ( $.isArray( button ) ) {
14116 14552 var inner = $( '<'+(button.DT_el || 'div')+'/>' )
14117 14553 .appendTo( container );
14118 14554 attach( inner, button );
14119 14555 }
14120 14556 else {
14121 btnDisplay = '';
14557 btnDisplay = null;
14122 14558 btnClass = '';
14123
14559
14124 14560 switch ( button ) {
14125 case 'ellipsis':
14126 //RhodeCode fixed, added class paginate_button
14127 container.append('<span class=\"paginate_button\">&hellip;</span>');
14561 case 'ellipsis':
14562 container.append('<span class="ellipsis">&#x2026;</span>');
14128 14563 break;
14129
14564
14130 14565 case 'first':
14131 14566 btnDisplay = lang.sFirst;
14132 14567 btnClass = button + (page > 0 ?
14133 14568 '' : ' '+classes.sPageButtonDisabled);
14134 14569 break;
14135
14570
14136 14571 case 'previous':
14137 14572 btnDisplay = lang.sPrevious;
14138 14573 btnClass = button + (page > 0 ?
14139 14574 '' : ' '+classes.sPageButtonDisabled);
14140 14575 break;
14141
14576
14142 14577 case 'next':
14143 14578 btnDisplay = lang.sNext;
14144 14579 btnClass = button + (page < pages-1 ?
14145 14580 '' : ' '+classes.sPageButtonDisabled);
14146 14581 break;
14147
14582
14148 14583 case 'last':
14149 14584 btnDisplay = lang.sLast;
14150 14585 btnClass = button + (page < pages-1 ?
14151 14586 '' : ' '+classes.sPageButtonDisabled);
14152 14587 break;
14153
14588
14154 14589 default:
14155 14590 btnDisplay = button + 1;
14156 14591 btnClass = page === button ?
14157 14592 classes.sPageButtonActive : '';
14158 14593 break;
14159 14594 }
14160
14161 if ( btnDisplay ) {
14595
14596 if ( btnDisplay !== null ) {
14162 14597 node = $('<a>', {
14163 14598 'class': classes.sPageButton+' '+btnClass,
14164 14599 'aria-controls': settings.sTableId,
14600 'aria-label': aria[ button ],
14165 14601 'data-dt-idx': counter,
14166 14602 'tabindex': settings.iTabIndex,
14167 14603 'id': idx === 0 && typeof button === 'string' ?
@@ -14170,40 +14606,42 b''
14170 14606 } )
14171 14607 .html( btnDisplay )
14172 14608 .appendTo( container );
14173
14609
14174 14610 _fnBindAction(
14175 14611 node, {action: button}, clickHandler
14176 14612 );
14177
14613
14178 14614 counter++;
14179 14615 }
14180 14616 }
14181 14617 }
14182 14618 };
14183
14619
14184 14620 // IE9 throws an 'unknown error' if document.activeElement is used
14185 14621 // inside an iframe or frame. Try / catch the error. Not good for
14186 14622 // accessibility, but neither are frames.
14623 var activeEl;
14624
14187 14625 try {
14188 14626 // Because this approach is destroying and recreating the paging
14189 14627 // elements, focus is lost on the select button which is bad for
14190 14628 // accessibility. So we want to restore focus once the draw has
14191 14629 // completed
14192 var activeEl = $(document.activeElement).data('dt-idx');
14193
14194 attach( $(host).empty(), buttons );
14195
14196 if ( activeEl !== null ) {
14197 $(host).find( '[data-dt-idx='+activeEl+']' ).focus();
14198 }
14630 activeEl = $(host).find(document.activeElement).data('dt-idx');
14199 14631 }
14200 14632 catch (e) {}
14201 }
14202 }
14203 } );
14204
14205
14206
14633
14634 attach( $(host).empty(), buttons );
14635
14636 if ( activeEl !== undefined ) {
14637 $(host).find( '[data-dt-idx='+activeEl+']' ).focus();
14638 }
14639 }
14640 }
14641 } );
14642
14643
14644
14207 14645 // Built in type detection. See model.ext.aTypes for information about
14208 14646 // what is required from this methods.
14209 14647 $.extend( DataTable.ext.type.detect, [
@@ -14214,41 +14652,41 b''
14214 14652 var decimal = settings.oLanguage.sDecimal;
14215 14653 return _isNumber( d, decimal ) ? 'num'+decimal : null;
14216 14654 },
14217
14655
14218 14656 // Dates (only those recognised by the browser's Date.parse)
14219 14657 function ( d, settings )
14220 14658 {
14221 // V8 will remove any unknown characters at the start and end of the
14222 // expression, leading to false matches such as `$245.12` or `10%` being
14223 // a valid date. See forum thread 18941 for detail.
14224 if ( d && !(d instanceof Date) && ( ! _re_date_start.test(d) || ! _re_date_end.test(d) ) ) {
14659 // V8 tries _very_ hard to make a string passed into `Date.parse()`
14660 // valid, so we need to use a regex to restrict date formats. Use a
14661 // plug-in for anything other than ISO8601 style strings
14662 if ( d && !(d instanceof Date) && ! _re_date.test(d) ) {
14225 14663 return null;
14226 14664 }
14227 14665 var parsed = Date.parse(d);
14228 14666 return (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null;
14229 14667 },
14230
14668
14231 14669 // Formatted numbers
14232 14670 function ( d, settings )
14233 14671 {
14234 14672 var decimal = settings.oLanguage.sDecimal;
14235 14673 return _isNumber( d, decimal, true ) ? 'num-fmt'+decimal : null;
14236 14674 },
14237
14675
14238 14676 // HTML numeric
14239 14677 function ( d, settings )
14240 14678 {
14241 14679 var decimal = settings.oLanguage.sDecimal;
14242 14680 return _htmlNumeric( d, decimal ) ? 'html-num'+decimal : null;
14243 14681 },
14244
14682
14245 14683 // HTML numeric, formatted
14246 14684 function ( d, settings )
14247 14685 {
14248 14686 var decimal = settings.oLanguage.sDecimal;
14249 14687 return _htmlNumeric( d, decimal, true ) ? 'html-num-fmt'+decimal : null;
14250 14688 },
14251
14689
14252 14690 // HTML (this is strict checking - there must be html)
14253 14691 function ( d, settings )
14254 14692 {
@@ -14256,17 +14694,17 b''
14256 14694 'html' : null;
14257 14695 }
14258 14696 ] );
14259
14260
14261
14697
14698
14699
14262 14700 // Filter formatting functions. See model.ext.ofnSearch for information about
14263 14701 // what is required from these methods.
14264 //
14702 //
14265 14703 // Note that additional search methods are added for the html numbers and
14266 14704 // html formatted numbers by `_addNumericSort()` when we know what the decimal
14267 14705 // place is
14268
14269
14706
14707
14270 14708 $.extend( DataTable.ext.type.search, {
14271 14709 html: function ( data ) {
14272 14710 return _empty(data) ?
@@ -14277,7 +14715,7 b''
14277 14715 .replace( _re_html, "" ) :
14278 14716 '';
14279 14717 },
14280
14718
14281 14719 string: function ( data ) {
14282 14720 return _empty(data) ?
14283 14721 data :
@@ -14286,35 +14724,35 b''
14286 14724 data;
14287 14725 }
14288 14726 } );
14289
14290
14291
14727
14728
14729
14292 14730 var __numericReplace = function ( d, decimalPlace, re1, re2 ) {
14293 14731 if ( d !== 0 && (!d || d === '-') ) {
14294 14732 return -Infinity;
14295 14733 }
14296
14734
14297 14735 // If a decimal place other than `.` is used, it needs to be given to the
14298 14736 // function so we can detect it and replace with a `.` which is the only
14299 14737 // decimal place Javascript recognises - it is not locale aware.
14300 14738 if ( decimalPlace ) {
14301 14739 d = _numToDecimal( d, decimalPlace );
14302 14740 }
14303
14741
14304 14742 if ( d.replace ) {
14305 14743 if ( re1 ) {
14306 14744 d = d.replace( re1, '' );
14307 14745 }
14308
14746
14309 14747 if ( re2 ) {
14310 14748 d = d.replace( re2, '' );
14311 14749 }
14312 14750 }
14313
14751
14314 14752 return d * 1;
14315 14753 };
14316
14317
14754
14755
14318 14756 // Add the numeric 'deformatting' functions for sorting and search. This is done
14319 14757 // in a function to provide an easy ability for the language options to add
14320 14758 // additional methods if a non-period decimal place is used.
@@ -14325,17 +14763,17 b''
14325 14763 "num": function ( d ) {
14326 14764 return __numericReplace( d, decimalPlace );
14327 14765 },
14328
14766
14329 14767 // Formatted numbers
14330 14768 "num-fmt": function ( d ) {
14331 14769 return __numericReplace( d, decimalPlace, _re_formatted_numeric );
14332 14770 },
14333
14771
14334 14772 // HTML numeric
14335 14773 "html-num": function ( d ) {
14336 14774 return __numericReplace( d, decimalPlace, _re_html );
14337 14775 },
14338
14776
14339 14777 // HTML numeric, formatted
14340 14778 "html-num-fmt": function ( d ) {
14341 14779 return __numericReplace( d, decimalPlace, _re_html, _re_formatted_numeric );
@@ -14344,7 +14782,7 b''
14344 14782 function ( key, fn ) {
14345 14783 // Add the ordering method
14346 14784 _ext.type.order[ key+decimalPlace+'-pre' ] = fn;
14347
14785
14348 14786 // For HTML types add a search formatter that will strip the HTML
14349 14787 if ( key.match(/^html\-/) ) {
14350 14788 _ext.type.search[ key+decimalPlace ] = _ext.type.search.html;
@@ -14352,15 +14790,15 b''
14352 14790 }
14353 14791 );
14354 14792 }
14355
14356
14793
14794
14357 14795 // Default sort methods
14358 14796 $.extend( _ext.type.order, {
14359 14797 // Dates
14360 14798 "date-pre": function ( d ) {
14361 return Date.parse( d ) || 0;
14799 return Date.parse( d ) || -Infinity;
14362 14800 },
14363
14801
14364 14802 // html
14365 14803 "html-pre": function ( a ) {
14366 14804 return _empty(a) ?
@@ -14369,7 +14807,7 b''
14369 14807 a.replace( /<.*?>/g, "" ).toLowerCase() :
14370 14808 a+'';
14371 14809 },
14372
14810
14373 14811 // string
14374 14812 "string-pre": function ( a ) {
14375 14813 // This is a little complex, but faster than always calling toString,
@@ -14382,23 +14820,23 b''
14382 14820 '' :
14383 14821 a.toString();
14384 14822 },
14385
14823
14386 14824 // string-asc and -desc are retained only for compatibility with the old
14387 14825 // sort methods
14388 14826 "string-asc": function ( x, y ) {
14389 14827 return ((x < y) ? -1 : ((x > y) ? 1 : 0));
14390 14828 },
14391
14829
14392 14830 "string-desc": function ( x, y ) {
14393 14831 return ((x < y) ? 1 : ((x > y) ? -1 : 0));
14394 14832 }
14395 14833 } );
14396
14397
14834
14835
14398 14836 // Numeric sorting types - order doesn't matter here
14399 14837 _addNumericSort( '' );
14400
14401
14838
14839
14402 14840 $.extend( true, DataTable.ext.renderer, {
14403 14841 header: {
14404 14842 _: function ( settings, cell, column, classes ) {
@@ -14411,9 +14849,9 b''
14411 14849 if ( settings !== ctx ) { // need to check this this is the host
14412 14850 return; // table, not a nested one
14413 14851 }
14414
14852
14415 14853 var colIdx = column.idx;
14416
14854
14417 14855 cell
14418 14856 .removeClass(
14419 14857 column.sSortingClass +' '+
@@ -14427,7 +14865,7 b''
14427 14865 );
14428 14866 } );
14429 14867 },
14430
14868
14431 14869 jqueryui: function ( settings, cell, column, classes ) {
14432 14870 $('<div/>')
14433 14871 .addClass( classes.sSortJUIWrapper )
@@ -14436,15 +14874,15 b''
14436 14874 .addClass( classes.sSortIcon+' '+column.sSortingClassJUI )
14437 14875 )
14438 14876 .appendTo( cell );
14439
14877
14440 14878 // Attach a sort listener to update on sort
14441 14879 $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {
14442 14880 if ( settings !== ctx ) {
14443 14881 return;
14444 14882 }
14445
14883
14446 14884 var colIdx = column.idx;
14447
14885
14448 14886 cell
14449 14887 .removeClass( classes.sSortAsc +" "+classes.sSortDesc )
14450 14888 .addClass( columns[ colIdx ] == 'asc' ?
@@ -14452,7 +14890,7 b''
14452 14890 classes.sSortDesc :
14453 14891 column.sSortingClass
14454 14892 );
14455
14893
14456 14894 cell
14457 14895 .find( 'span.'+classes.sSortIcon )
14458 14896 .removeClass(
@@ -14471,14 +14909,20 b''
14471 14909 }
14472 14910 }
14473 14911 } );
14474
14912
14475 14913 /*
14476 14914 * Public helper functions. These aren't used internally by DataTables, or
14477 14915 * called by any of the options passed into DataTables, but they can be used
14478 14916 * externally by developers working with DataTables. They are helper functions
14479 14917 * to make working with DataTables a little bit easier.
14480 14918 */
14481
14919
14920 var __htmlEscapeEntities = function ( d ) {
14921 return typeof d === 'string' ?
14922 d.replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;') :
14923 d;
14924 };
14925
14482 14926 /**
14483 14927 * Helpers for `columns.render`.
14484 14928 *
@@ -14487,11 +14931,14 b''
14487 14931 *
14488 14932 * * `number` - Will format numeric data (defined by `columns.data`) for
14489 14933 * display, retaining the original unformatted data for sorting and filtering.
14490 * It takes 4 parameters:
14934 * It takes 5 parameters:
14491 14935 * * `string` - Thousands grouping separator
14492 14936 * * `string` - Decimal point indicator
14493 14937 * * `integer` - Number of decimal points to show
14494 14938 * * `string` (optional) - Prefix.
14939 * * `string` (optional) - Postfix (/suffix).
14940 * * `text` - Escape HTML to help prevent XSS attacks. It has no optional
14941 * parameters.
14495 14942 *
14496 14943 * @example
14497 14944 * // Column definition using the number renderer
@@ -14503,34 +14950,55 b''
14503 14950 * @namespace
14504 14951 */
14505 14952 DataTable.render = {
14506 number: function ( thousands, decimal, precision, prefix ) {
14953 number: function ( thousands, decimal, precision, prefix, postfix ) {
14507 14954 return {
14508 14955 display: function ( d ) {
14956 if ( typeof d !== 'number' && typeof d !== 'string' ) {
14957 return d;
14958 }
14959
14509 14960 var negative = d < 0 ? '-' : '';
14510 d = Math.abs( parseFloat( d ) );
14511
14961 var flo = parseFloat( d );
14962
14963 // If NaN then there isn't much formatting that we can do - just
14964 // return immediately, escaping any HTML (this was supposed to
14965 // be a number after all)
14966 if ( isNaN( flo ) ) {
14967 return __htmlEscapeEntities( d );
14968 }
14969
14970 flo = flo.toFixed( precision );
14971 d = Math.abs( flo );
14972
14512 14973 var intPart = parseInt( d, 10 );
14513 14974 var floatPart = precision ?
14514 14975 decimal+(d - intPart).toFixed( precision ).substring( 2 ):
14515 14976 '';
14516
14977
14517 14978 return negative + (prefix||'') +
14518 14979 intPart.toString().replace(
14519 14980 /\B(?=(\d{3})+(?!\d))/g, thousands
14520 14981 ) +
14521 floatPart;
14522 }
14982 floatPart +
14983 (postfix||'');
14984 }
14985 };
14986 },
14987
14988 text: function () {
14989 return {
14990 display: __htmlEscapeEntities
14523 14991 };
14524 14992 }
14525 14993 };
14526
14527
14994
14995
14528 14996 /*
14529 14997 * This is really a good bit rubbish this method of exposing the internal methods
14530 14998 * publicly... - To be fixed in 2.0 using methods on the prototype
14531 14999 */
14532
14533
15000
15001
14534 15002 /**
14535 15003 * Create a wrapper function for exporting an internal functions to an external API.
14536 15004 * @param {string} fn API function name
@@ -14546,8 +15014,8 b''
14546 15014 return DataTable.ext.internal[fn].apply( this, args );
14547 15015 };
14548 15016 }
14549
14550
15017
15018
14551 15019 /**
14552 15020 * Reference to internal functions for use by plug-in developers. Note that
14553 15021 * these methods are references to internal functions and are considered to be
@@ -14622,11 +15090,9 b''
14622 15090 _fnCalculateColumnWidths: _fnCalculateColumnWidths,
14623 15091 _fnThrottle: _fnThrottle,
14624 15092 _fnConvertToWidth: _fnConvertToWidth,
14625 _fnScrollingWidthAdjust: _fnScrollingWidthAdjust,
14626 15093 _fnGetWidestNode: _fnGetWidestNode,
14627 15094 _fnGetMaxLenString: _fnGetMaxLenString,
14628 15095 _fnStringToCss: _fnStringToCss,
14629 _fnScrollBarWidth: _fnScrollBarWidth,
14630 15096 _fnSortFlatten: _fnSortFlatten,
14631 15097 _fnSort: _fnSort,
14632 15098 _fnSortAria: _fnSortAria,
@@ -14650,11 +15116,14 b''
14650 15116 // in 1.10, so this dead-end function is
14651 15117 // added to prevent errors
14652 15118 } );
14653
15119
14654 15120
14655 15121 // jQuery access
14656 15122 $.fn.dataTable = DataTable;
14657 15123
15124 // Provide access to the host jQuery object (circular reference)
15125 DataTable.$ = $;
15126
14658 15127 // Legacy aliases
14659 15128 $.fn.dataTableSettings = DataTable.settings;
14660 15129 $.fn.dataTableExt = DataTable.ext;
@@ -14835,7 +15304,4 b''
14835 15304 */
14836 15305
14837 15306 return $.fn.dataTable;
14838 }));
14839
14840 }(window, document));
14841
15307 })); No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now