##// 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, (4232 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 );
31 // AMD
32 define( ['jquery'], function ( $ ) {
33 return factory( $, window, document );
34 } );
35 35 }
36 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 );
44 }
45 }
46 (/** @lends <global> */function( $ ) {
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 );
57 }
58 }
59 (function( $, window, document, undefined ) {
47 60 "use strict";
48 61
49 62 /**
@@ -78,7 +91,1251 b''
78 91 * } );
79 92 * } );
80 93 */
81 var DataTable;
94 var DataTable = function ( options )
95 {
96 /**
97 * Perform a jQuery selector action on the table's TR elements (from the tbody) and
98 * return the resulting jQuery object.
99 * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on
100 * @param {object} [oOpts] Optional parameters for modifying the rows to be included
101 * @param {string} [oOpts.filter=none] Select TR elements that meet the current filter
102 * criterion ("applied") or all TR elements (i.e. no filter).
103 * @param {string} [oOpts.order=current] Order of the TR elements in the processed array.
104 * Can be either 'current', whereby the current sorting of the table is used, or
105 * 'original' whereby the original order the data was read into the table is used.
106 * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page
107 * ("current") or not ("all"). If 'current' is given, then order is assumed to be
108 * 'current' and filter is 'applied', regardless of what they might be given as.
109 * @returns {object} jQuery object, filtered by the given selector.
110 * @dtopt API
111 * @deprecated Since v1.10
112 *
113 * @example
114 * $(document).ready(function() {
115 * var oTable = $('#example').dataTable();
116 *
117 * // Highlight every second row
118 * oTable.$('tr:odd').css('backgroundColor', 'blue');
119 * } );
120 *
121 * @example
122 * $(document).ready(function() {
123 * var oTable = $('#example').dataTable();
124 *
125 * // Filter to rows with 'Webkit' in them, add a background colour and then
126 * // remove the filter, thus highlighting the 'Webkit' rows only.
127 * oTable.fnFilter('Webkit');
128 * oTable.$('tr', {"search": "applied"}).css('backgroundColor', 'blue');
129 * oTable.fnFilter('');
130 * } );
131 */
132 this.$ = function ( sSelector, oOpts )
133 {
134 return this.api(true).$( sSelector, oOpts );
135 };
136
137
138 /**
139 * Almost identical to $ in operation, but in this case returns the data for the matched
140 * rows - as such, the jQuery selector used should match TR row nodes or TD/TH cell nodes
141 * rather than any descendants, so the data can be obtained for the row/cell. If matching
142 * rows are found, the data returned is the original data array/object that was used to
143 * create the row (or a generated array if from a DOM source).
144 *
145 * This method is often useful in-combination with $ where both functions are given the
146 * same parameters and the array indexes will match identically.
147 * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on
148 * @param {object} [oOpts] Optional parameters for modifying the rows to be included
149 * @param {string} [oOpts.filter=none] Select elements that meet the current filter
150 * criterion ("applied") or all elements (i.e. no filter).
151 * @param {string} [oOpts.order=current] Order of the data in the processed array.
152 * Can be either 'current', whereby the current sorting of the table is used, or
153 * 'original' whereby the original order the data was read into the table is used.
154 * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page
155 * ("current") or not ("all"). If 'current' is given, then order is assumed to be
156 * 'current' and filter is 'applied', regardless of what they might be given as.
157 * @returns {array} Data for the matched elements. If any elements, as a result of the
158 * selector, were not TR, TD or TH elements in the DataTable, they will have a null
159 * entry in the array.
160 * @dtopt API
161 * @deprecated Since v1.10
162 *
163 * @example
164 * $(document).ready(function() {
165 * var oTable = $('#example').dataTable();
166 *
167 * // Get the data from the first row in the table
168 * var data = oTable._('tr:first');
169 *
170 * // Do something useful with the data
171 * alert( "First cell is: "+data[0] );
172 * } );
173 *
174 * @example
175 * $(document).ready(function() {
176 * var oTable = $('#example').dataTable();
177 *
178 * // Filter to 'Webkit' and get all data for
179 * oTable.fnFilter('Webkit');
180 * var data = oTable._('tr', {"search": "applied"});
181 *
182 * // Do something with the data
183 * alert( data.length+" rows matched the search" );
184 * } );
185 */
186 this._ = function ( sSelector, oOpts )
187 {
188 return this.api(true).rows( sSelector, oOpts ).data();
189 };
190
191
192 /**
193 * Create a DataTables Api instance, with the currently selected tables for
194 * the Api's context.
195 * @param {boolean} [traditional=false] Set the API instance's context to be
196 * only the table referred to by the `DataTable.ext.iApiIndex` option, as was
197 * used in the API presented by DataTables 1.9- (i.e. the traditional mode),
198 * or if all tables captured in the jQuery object should be used.
199 * @return {DataTables.Api}
200 */
201 this.api = function ( traditional )
202 {
203 return traditional ?
204 new _Api(
205 _fnSettingsFromNode( this[ _ext.iApiIndex ] )
206 ) :
207 new _Api( this );
208 };
209
210
211 /**
212 * Add a single new row or multiple rows of data to the table. Please note
213 * that this is suitable for client-side processing only - if you are using
214 * server-side processing (i.e. "bServerSide": true), then to add data, you
215 * must add it to the data source, i.e. the server-side, through an Ajax call.
216 * @param {array|object} data The data to be added to the table. This can be:
217 * <ul>
218 * <li>1D array of data - add a single row with the data provided</li>
219 * <li>2D array of arrays - add multiple rows in a single call</li>
220 * <li>object - data object when using <i>mData</i></li>
221 * <li>array of objects - multiple data objects when using <i>mData</i></li>
222 * </ul>
223 * @param {bool} [redraw=true] redraw the table or not
224 * @returns {array} An array of integers, representing the list of indexes in
225 * <i>aoData</i> ({@link DataTable.models.oSettings}) that have been added to
226 * the table.
227 * @dtopt API
228 * @deprecated Since v1.10
229 *
230 * @example
231 * // Global var for counter
232 * var giCount = 2;
233 *
234 * $(document).ready(function() {
235 * $('#example').dataTable();
236 * } );
237 *
238 * function fnClickAddRow() {
239 * $('#example').dataTable().fnAddData( [
240 * giCount+".1",
241 * giCount+".2",
242 * giCount+".3",
243 * giCount+".4" ]
244 * );
245 *
246 * giCount++;
247 * }
248 */
249 this.fnAddData = function( data, redraw )
250 {
251 var api = this.api( true );
252
253 /* Check if we want to add multiple rows or not */
254 var rows = $.isArray(data) && ( $.isArray(data[0]) || $.isPlainObject(data[0]) ) ?
255 api.rows.add( data ) :
256 api.row.add( data );
257
258 if ( redraw === undefined || redraw ) {
259 api.draw();
260 }
261
262 return rows.flatten().toArray();
263 };
264
265
266 /**
267 * This function will make DataTables recalculate the column sizes, based on the data
268 * contained in the table and the sizes applied to the columns (in the DOM, CSS or
269 * through the sWidth parameter). This can be useful when the width of the table's
270 * parent element changes (for example a window resize).
271 * @param {boolean} [bRedraw=true] Redraw the table or not, you will typically want to
272 * @dtopt API
273 * @deprecated Since v1.10
274 *
275 * @example
276 * $(document).ready(function() {
277 * var oTable = $('#example').dataTable( {
278 * "sScrollY": "200px",
279 * "bPaginate": false
280 * } );
281 *
282 * $(window).on('resize', function () {
283 * oTable.fnAdjustColumnSizing();
284 * } );
285 * } );
286 */
287 this.fnAdjustColumnSizing = function ( bRedraw )
288 {
289 var api = this.api( true ).columns.adjust();
290 var settings = api.settings()[0];
291 var scroll = settings.oScroll;
292
293 if ( bRedraw === undefined || bRedraw ) {
294 api.draw( false );
295 }
296 else if ( scroll.sX !== "" || scroll.sY !== "" ) {
297 /* If not redrawing, but scrolling, we want to apply the new column sizes anyway */
298 _fnScrollDraw( settings );
299 }
300 };
301
302
303 /**
304 * Quickly and simply clear a table
305 * @param {bool} [bRedraw=true] redraw the table or not
306 * @dtopt API
307 * @deprecated Since v1.10
308 *
309 * @example
310 * $(document).ready(function() {
311 * var oTable = $('#example').dataTable();
312 *
313 * // Immediately 'nuke' the current rows (perhaps waiting for an Ajax callback...)
314 * oTable.fnClearTable();
315 * } );
316 */
317 this.fnClearTable = function( bRedraw )
318 {
319 var api = this.api( true ).clear();
320
321 if ( bRedraw === undefined || bRedraw ) {
322 api.draw();
323 }
324 };
325
326
327 /**
328 * The exact opposite of 'opening' a row, this function will close any rows which
329 * are currently 'open'.
330 * @param {node} nTr the table row to 'close'
331 * @returns {int} 0 on success, or 1 if failed (can't find the row)
332 * @dtopt API
333 * @deprecated Since v1.10
334 *
335 * @example
336 * $(document).ready(function() {
337 * var oTable;
338 *
339 * // 'open' an information row when a row is clicked on
340 * $('#example tbody tr').click( function () {
341 * if ( oTable.fnIsOpen(this) ) {
342 * oTable.fnClose( this );
343 * } else {
344 * oTable.fnOpen( this, "Temporary row opened", "info_row" );
345 * }
346 * } );
347 *
348 * oTable = $('#example').dataTable();
349 * } );
350 */
351 this.fnClose = function( nTr )
352 {
353 this.api( true ).row( nTr ).child.hide();
354 };
355
356
357 /**
358 * Remove a row for the table
359 * @param {mixed} target The index of the row from aoData to be deleted, or
360 * the TR element you want to delete
361 * @param {function|null} [callBack] Callback function
362 * @param {bool} [redraw=true] Redraw the table or not
363 * @returns {array} The row that was deleted
364 * @dtopt API
365 * @deprecated Since v1.10
366 *
367 * @example
368 * $(document).ready(function() {
369 * var oTable = $('#example').dataTable();
370 *
371 * // Immediately remove the first row
372 * oTable.fnDeleteRow( 0 );
373 * } );
374 */
375 this.fnDeleteRow = function( target, callback, redraw )
376 {
377 var api = this.api( true );
378 var rows = api.rows( target );
379 var settings = rows.settings()[0];
380 var data = settings.aoData[ rows[0][0] ];
381
382 rows.remove();
383
384 if ( callback ) {
385 callback.call( this, settings, data );
386 }
387
388 if ( redraw === undefined || redraw ) {
389 api.draw();
390 }
391
392 return data;
393 };
394
395
396 /**
397 * Restore the table to it's original state in the DOM by removing all of DataTables
398 * enhancements, alterations to the DOM structure of the table and event listeners.
399 * @param {boolean} [remove=false] Completely remove the table from the DOM
400 * @dtopt API
401 * @deprecated Since v1.10
402 *
403 * @example
404 * $(document).ready(function() {
405 * // This example is fairly pointless in reality, but shows how fnDestroy can be used
406 * var oTable = $('#example').dataTable();
407 * oTable.fnDestroy();
408 * } );
409 */
410 this.fnDestroy = function ( remove )
411 {
412 this.api( true ).destroy( remove );
413 };
414
415
416 /**
417 * Redraw the table
418 * @param {bool} [complete=true] Re-filter and resort (if enabled) the table before the draw.
419 * @dtopt API
420 * @deprecated Since v1.10
421 *
422 * @example
423 * $(document).ready(function() {
424 * var oTable = $('#example').dataTable();
425 *
426 * // Re-draw the table - you wouldn't want to do it here, but it's an example :-)
427 * oTable.fnDraw();
428 * } );
429 */
430 this.fnDraw = function( complete )
431 {
432 // Note that this isn't an exact match to the old call to _fnDraw - it takes
433 // into account the new data, but can hold position.
434 this.api( true ).draw( complete );
435 };
436
437
438 /**
439 * Filter the input based on data
440 * @param {string} sInput String to filter the table on
441 * @param {int|null} [iColumn] Column to limit filtering to
442 * @param {bool} [bRegex=false] Treat as regular expression or not
443 * @param {bool} [bSmart=true] Perform smart filtering or not
444 * @param {bool} [bShowGlobal=true] Show the input global filter in it's input box(es)
445 * @param {bool} [bCaseInsensitive=true] Do case-insensitive matching (true) or not (false)
446 * @dtopt API
447 * @deprecated Since v1.10
448 *
449 * @example
450 * $(document).ready(function() {
451 * var oTable = $('#example').dataTable();
452 *
453 * // Sometime later - filter...
454 * oTable.fnFilter( 'test string' );
455 * } );
456 */
457 this.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal, bCaseInsensitive )
458 {
459 var api = this.api( true );
460
461 if ( iColumn === null || iColumn === undefined ) {
462 api.search( sInput, bRegex, bSmart, bCaseInsensitive );
463 }
464 else {
465 api.column( iColumn ).search( sInput, bRegex, bSmart, bCaseInsensitive );
466 }
467
468 api.draw();
469 };
470
471
472 /**
473 * Get the data for the whole table, an individual row or an individual cell based on the
474 * provided parameters.
475 * @param {int|node} [src] A TR row node, TD/TH cell node or an integer. If given as
476 * a TR node then the data source for the whole row will be returned. If given as a
477 * TD/TH cell node then iCol will be automatically calculated and the data for the
478 * cell returned. If given as an integer, then this is treated as the aoData internal
479 * data index for the row (see fnGetPosition) and the data for that row used.
480 * @param {int} [col] Optional column index that you want the data of.
481 * @returns {array|object|string} If mRow is undefined, then the data for all rows is
482 * returned. If mRow is defined, just data for that row, and is iCol is
483 * defined, only data for the designated cell is returned.
484 * @dtopt API
485 * @deprecated Since v1.10
486 *
487 * @example
488 * // Row data
489 * $(document).ready(function() {
490 * oTable = $('#example').dataTable();
491 *
492 * oTable.$('tr').click( function () {
493 * var data = oTable.fnGetData( this );
494 * // ... do something with the array / object of data for the row
495 * } );
496 * } );
497 *
498 * @example
499 * // Individual cell data
500 * $(document).ready(function() {
501 * oTable = $('#example').dataTable();
502 *
503 * oTable.$('td').click( function () {
504 * var sData = oTable.fnGetData( this );
505 * alert( 'The cell clicked on had the value of '+sData );
506 * } );
507 * } );
508 */
509 this.fnGetData = function( src, col )
510 {
511 var api = this.api( true );
512
513 if ( src !== undefined ) {
514 var type = src.nodeName ? src.nodeName.toLowerCase() : '';
515
516 return col !== undefined || type == 'td' || type == 'th' ?
517 api.cell( src, col ).data() :
518 api.row( src ).data() || null;
519 }
520
521 return api.data().toArray();
522 };
523
524
525 /**
526 * Get an array of the TR nodes that are used in the table's body. Note that you will
527 * typically want to use the '$' API method in preference to this as it is more
528 * flexible.
529 * @param {int} [iRow] Optional row index for the TR element you want
530 * @returns {array|node} If iRow is undefined, returns an array of all TR elements
531 * in the table's body, or iRow is defined, just the TR element requested.
532 * @dtopt API
533 * @deprecated Since v1.10
534 *
535 * @example
536 * $(document).ready(function() {
537 * var oTable = $('#example').dataTable();
538 *
539 * // Get the nodes from the table
540 * var nNodes = oTable.fnGetNodes( );
541 * } );
542 */
543 this.fnGetNodes = function( iRow )
544 {
545 var api = this.api( true );
546
547 return iRow !== undefined ?
548 api.row( iRow ).node() :
549 api.rows().nodes().flatten().toArray();
550 };
551
552
553 /**
554 * Get the array indexes of a particular cell from it's DOM element
555 * and column index including hidden columns
556 * @param {node} node this can either be a TR, TD or TH in the table's body
557 * @returns {int} If nNode is given as a TR, then a single index is returned, or
558 * if given as a cell, an array of [row index, column index (visible),
559 * column index (all)] is given.
560 * @dtopt API
561 * @deprecated Since v1.10
562 *
563 * @example
564 * $(document).ready(function() {
565 * $('#example tbody td').click( function () {
566 * // Get the position of the current data from the node
567 * var aPos = oTable.fnGetPosition( this );
568 *
569 * // Get the data array for this row
570 * var aData = oTable.fnGetData( aPos[0] );
571 *
572 * // Update the data array and return the value
573 * aData[ aPos[1] ] = 'clicked';
574 * this.innerHTML = 'clicked';
575 * } );
576 *
577 * // Init DataTables
578 * oTable = $('#example').dataTable();
579 * } );
580 */
581 this.fnGetPosition = function( node )
582 {
583 var api = this.api( true );
584 var nodeName = node.nodeName.toUpperCase();
585
586 if ( nodeName == 'TR' ) {
587 return api.row( node ).index();
588 }
589 else if ( nodeName == 'TD' || nodeName == 'TH' ) {
590 var cell = api.cell( node ).index();
591
592 return [
593 cell.row,
594 cell.columnVisible,
595 cell.column
596 ];
597 }
598 return null;
599 };
600
601
602 /**
603 * Check to see if a row is 'open' or not.
604 * @param {node} nTr the table row to check
605 * @returns {boolean} true if the row is currently open, false otherwise
606 * @dtopt API
607 * @deprecated Since v1.10
608 *
609 * @example
610 * $(document).ready(function() {
611 * var oTable;
612 *
613 * // 'open' an information row when a row is clicked on
614 * $('#example tbody tr').click( function () {
615 * if ( oTable.fnIsOpen(this) ) {
616 * oTable.fnClose( this );
617 * } else {
618 * oTable.fnOpen( this, "Temporary row opened", "info_row" );
619 * }
620 * } );
621 *
622 * oTable = $('#example').dataTable();
623 * } );
624 */
625 this.fnIsOpen = function( nTr )
626 {
627 return this.api( true ).row( nTr ).child.isShown();
628 };
629
630
631 /**
632 * This function will place a new row directly after a row which is currently
633 * on display on the page, with the HTML contents that is passed into the
634 * function. This can be used, for example, to ask for confirmation that a
635 * particular record should be deleted.
636 * @param {node} nTr The table row to 'open'
637 * @param {string|node|jQuery} mHtml The HTML to put into the row
638 * @param {string} sClass Class to give the new TD cell
639 * @returns {node} The row opened. Note that if the table row passed in as the
640 * first parameter, is not found in the table, this method will silently
641 * return.
642 * @dtopt API
643 * @deprecated Since v1.10
644 *
645 * @example
646 * $(document).ready(function() {
647 * var oTable;
648 *
649 * // 'open' an information row when a row is clicked on
650 * $('#example tbody tr').click( function () {
651 * if ( oTable.fnIsOpen(this) ) {
652 * oTable.fnClose( this );
653 * } else {
654 * oTable.fnOpen( this, "Temporary row opened", "info_row" );
655 * }
656 * } );
657 *
658 * oTable = $('#example').dataTable();
659 * } );
660 */
661 this.fnOpen = function( nTr, mHtml, sClass )
662 {
663 return this.api( true )
664 .row( nTr )
665 .child( mHtml, sClass )
666 .show()
667 .child()[0];
668 };
669
670
671 /**
672 * Change the pagination - provides the internal logic for pagination in a simple API
673 * function. With this function you can have a DataTables table go to the next,
674 * previous, first or last pages.
675 * @param {string|int} mAction Paging action to take: "first", "previous", "next" or "last"
676 * or page number to jump to (integer), note that page 0 is the first page.
677 * @param {bool} [bRedraw=true] Redraw the table or not
678 * @dtopt API
679 * @deprecated Since v1.10
680 *
681 * @example
682 * $(document).ready(function() {
683 * var oTable = $('#example').dataTable();
684 * oTable.fnPageChange( 'next' );
685 * } );
686 */
687 this.fnPageChange = function ( mAction, bRedraw )
688 {
689 var api = this.api( true ).page( mAction );
690
691 if ( bRedraw === undefined || bRedraw ) {
692 api.draw(false);
693 }
694 };
695
696
697 /**
698 * Show a particular column
699 * @param {int} iCol The column whose display should be changed
700 * @param {bool} bShow Show (true) or hide (false) the column
701 * @param {bool} [bRedraw=true] Redraw the table or not
702 * @dtopt API
703 * @deprecated Since v1.10
704 *
705 * @example
706 * $(document).ready(function() {
707 * var oTable = $('#example').dataTable();
708 *
709 * // Hide the second column after initialisation
710 * oTable.fnSetColumnVis( 1, false );
711 * } );
712 */
713 this.fnSetColumnVis = function ( iCol, bShow, bRedraw )
714 {
715 var api = this.api( true ).column( iCol ).visible( bShow );
716
717 if ( bRedraw === undefined || bRedraw ) {
718 api.columns.adjust().draw();
719 }
720 };
721
722
723 /**
724 * Get the settings for a particular table for external manipulation
725 * @returns {object} DataTables settings object. See
726 * {@link DataTable.models.oSettings}
727 * @dtopt API
728 * @deprecated Since v1.10
729 *
730 * @example
731 * $(document).ready(function() {
732 * var oTable = $('#example').dataTable();
733 * var oSettings = oTable.fnSettings();
734 *
735 * // Show an example parameter from the settings
736 * alert( oSettings._iDisplayStart );
737 * } );
738 */
739 this.fnSettings = function()
740 {
741 return _fnSettingsFromNode( this[_ext.iApiIndex] );
742 };
743
744
745 /**
746 * Sort the table by a particular column
747 * @param {int} iCol the data index to sort on. Note that this will not match the
748 * 'display index' if you have hidden data entries
749 * @dtopt API
750 * @deprecated Since v1.10
751 *
752 * @example
753 * $(document).ready(function() {
754 * var oTable = $('#example').dataTable();
755 *
756 * // Sort immediately with columns 0 and 1
757 * oTable.fnSort( [ [0,'asc'], [1,'asc'] ] );
758 * } );
759 */
760 this.fnSort = function( aaSort )
761 {
762 this.api( true ).order( aaSort ).draw();
763 };
764
765
766 /**
767 * Attach a sort listener to an element for a given column
768 * @param {node} nNode the element to attach the sort listener to
769 * @param {int} iColumn the column that a click on this node will sort on
770 * @param {function} [fnCallback] callback function when sort is run
771 * @dtopt API
772 * @deprecated Since v1.10
773 *
774 * @example
775 * $(document).ready(function() {
776 * var oTable = $('#example').dataTable();
777 *
778 * // Sort on column 1, when 'sorter' is clicked on
779 * oTable.fnSortListener( document.getElementById('sorter'), 1 );
780 * } );
781 */
782 this.fnSortListener = function( nNode, iColumn, fnCallback )
783 {
784 this.api( true ).order.listener( nNode, iColumn, fnCallback );
785 };
786
787
788 /**
789 * Update a table cell or row - this method will accept either a single value to
790 * update the cell with, an array of values with one element for each column or
791 * an object in the same format as the original data source. The function is
792 * self-referencing in order to make the multi column updates easier.
793 * @param {object|array|string} mData Data to update the cell/row with
794 * @param {node|int} mRow TR element you want to update or the aoData index
795 * @param {int} [iColumn] The column to update, give as null or undefined to
796 * update a whole row.
797 * @param {bool} [bRedraw=true] Redraw the table or not
798 * @param {bool} [bAction=true] Perform pre-draw actions or not
799 * @returns {int} 0 on success, 1 on error
800 * @dtopt API
801 * @deprecated Since v1.10
802 *
803 * @example
804 * $(document).ready(function() {
805 * var oTable = $('#example').dataTable();
806 * oTable.fnUpdate( 'Example update', 0, 0 ); // Single cell
807 * oTable.fnUpdate( ['a', 'b', 'c', 'd', 'e'], $('tbody tr')[0] ); // Row
808 * } );
809 */
810 this.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction )
811 {
812 var api = this.api( true );
813
814 if ( iColumn === undefined || iColumn === null ) {
815 api.row( mRow ).data( mData );
816 }
817 else {
818 api.cell( mRow, iColumn ).data( mData );
819 }
820
821 if ( bAction === undefined || bAction ) {
822 api.columns.adjust();
823 }
824
825 if ( bRedraw === undefined || bRedraw ) {
826 api.draw();
827 }
828 return 0;
829 };
830
831
832 /**
833 * Provide a common method for plug-ins to check the version of DataTables being used, in order
834 * to ensure compatibility.
835 * @param {string} sVersion Version string to check for, in the format "X.Y.Z". Note that the
836 * formats "X" and "X.Y" are also acceptable.
837 * @returns {boolean} true if this version of DataTables is greater or equal to the required
838 * version, or false if this version of DataTales is not suitable
839 * @method
840 * @dtopt API
841 * @deprecated Since v1.10
842 *
843 * @example
844 * $(document).ready(function() {
845 * var oTable = $('#example').dataTable();
846 * alert( oTable.fnVersionCheck( '1.9.0' ) );
847 * } );
848 */
849 this.fnVersionCheck = _ext.fnVersionCheck;
850
851
852 var _that = this;
853 var emptyInit = options === undefined;
854 var len = this.length;
855
856 if ( emptyInit ) {
857 options = {};
858 }
859
860 this.oApi = this.internal = _ext.internal;
861
862 // Extend with old style plug-in API methods
863 for ( var fn in DataTable.ext.internal ) {
864 if ( fn ) {
865 this[fn] = _fnExternApiFunc(fn);
866 }
867 }
868
869 this.each(function() {
870 // For each initialisation we want to give it a clean initialisation
871 // object that can be bashed around
872 var o = {};
873 var oInit = len > 1 ? // optimisation for single table case
874 _fnExtend( o, options, true ) :
875 options;
876
877 /*global oInit,_that,emptyInit*/
878 var i=0, iLen, j, jLen, k, kLen;
879 var sId = this.getAttribute( 'id' );
880 var bInitHandedOff = false;
881 var defaults = DataTable.defaults;
882 var $this = $(this);
883
884
885 /* Sanity check */
886 if ( this.nodeName.toLowerCase() != 'table' )
887 {
888 _fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 );
889 return;
890 }
891
892 /* Backwards compatibility for the defaults */
893 _fnCompatOpts( defaults );
894 _fnCompatCols( defaults.column );
895
896 /* Convert the camel-case defaults to Hungarian */
897 _fnCamelToHungarian( defaults, defaults, true );
898 _fnCamelToHungarian( defaults.column, defaults.column, true );
899
900 /* Setting up the initialisation object */
901 _fnCamelToHungarian( defaults, $.extend( oInit, $this.data() ) );
902
903
904
905 /* Check to see if we are re-initialising a table */
906 var allSettings = DataTable.settings;
907 for ( i=0, iLen=allSettings.length ; i<iLen ; i++ )
908 {
909 var s = allSettings[i];
910
911 /* Base check on table node */
912 if ( s.nTable == this || s.nTHead.parentNode == this || (s.nTFoot && s.nTFoot.parentNode == this) )
913 {
914 var bRetrieve = oInit.bRetrieve !== undefined ? oInit.bRetrieve : defaults.bRetrieve;
915 var bDestroy = oInit.bDestroy !== undefined ? oInit.bDestroy : defaults.bDestroy;
916
917 if ( emptyInit || bRetrieve )
918 {
919 return s.oInstance;
920 }
921 else if ( bDestroy )
922 {
923 s.oInstance.fnDestroy();
924 break;
925 }
926 else
927 {
928 _fnLog( s, 0, 'Cannot reinitialise DataTable', 3 );
929 return;
930 }
931 }
932
933 /* If the element we are initialising has the same ID as a table which was previously
934 * initialised, but the table nodes don't match (from before) then we destroy the old
935 * instance by simply deleting it. This is under the assumption that the table has been
936 * destroyed by other methods. Anyone using non-id selectors will need to do this manually
937 */
938 if ( s.sTableId == this.id )
939 {
940 allSettings.splice( i, 1 );
941 break;
942 }
943 }
944
945 /* Ensure the table has an ID - required for accessibility */
946 if ( sId === null || sId === "" )
947 {
948 sId = "DataTables_Table_"+(DataTable.ext._unique++);
949 this.id = sId;
950 }
951
952 /* Create the settings object for this table and set some of the default parameters */
953 var oSettings = $.extend( true, {}, DataTable.models.oSettings, {
954 "sDestroyWidth": $this[0].style.width,
955 "sInstance": sId,
956 "sTableId": sId
957 } );
958 oSettings.nTable = this;
959 oSettings.oApi = _that.internal;
960 oSettings.oInit = oInit;
961
962 allSettings.push( oSettings );
963
964 // Need to add the instance after the instance after the settings object has been added
965 // to the settings array, so we can self reference the table instance if more than one
966 oSettings.oInstance = (_that.length===1) ? _that : $this.dataTable();
967
968 // Backwards compatibility, before we apply all the defaults
969 _fnCompatOpts( oInit );
970
971 if ( oInit.oLanguage )
972 {
973 _fnLanguageCompat( oInit.oLanguage );
974 }
975
976 // If the length menu is given, but the init display length is not, use the length menu
977 if ( oInit.aLengthMenu && ! oInit.iDisplayLength )
978 {
979 oInit.iDisplayLength = $.isArray( oInit.aLengthMenu[0] ) ?
980 oInit.aLengthMenu[0][0] : oInit.aLengthMenu[0];
981 }
982
983 // Apply the defaults and init options to make a single init object will all
984 // options defined from defaults and instance options.
985 oInit = _fnExtend( $.extend( true, {}, defaults ), oInit );
986
987
988 // Map the initialisation options onto the settings object
989 _fnMap( oSettings.oFeatures, oInit, [
990 "bPaginate",
991 "bLengthChange",
992 "bFilter",
993 "bSort",
994 "bSortMulti",
995 "bInfo",
996 "bProcessing",
997 "bAutoWidth",
998 "bSortClasses",
999 "bServerSide",
1000 "bDeferRender"
1001 ] );
1002 _fnMap( oSettings, oInit, [
1003 "asStripeClasses",
1004 "ajax",
1005 "fnServerData",
1006 "fnFormatNumber",
1007 "sServerMethod",
1008 "aaSorting",
1009 "aaSortingFixed",
1010 "aLengthMenu",
1011 "sPaginationType",
1012 "sAjaxSource",
1013 "sAjaxDataProp",
1014 "iStateDuration",
1015 "sDom",
1016 "bSortCellsTop",
1017 "iTabIndex",
1018 "fnStateLoadCallback",
1019 "fnStateSaveCallback",
1020 "renderer",
1021 "searchDelay",
1022 "rowId",
1023 [ "iCookieDuration", "iStateDuration" ], // backwards compat
1024 [ "oSearch", "oPreviousSearch" ],
1025 [ "aoSearchCols", "aoPreSearchCols" ],
1026 [ "iDisplayLength", "_iDisplayLength" ],
1027 [ "bJQueryUI", "bJUI" ]
1028 ] );
1029 _fnMap( oSettings.oScroll, oInit, [
1030 [ "sScrollX", "sX" ],
1031 [ "sScrollXInner", "sXInner" ],
1032 [ "sScrollY", "sY" ],
1033 [ "bScrollCollapse", "bCollapse" ]
1034 ] );
1035 _fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" );
1036
1037 /* Callback functions which are array driven */
1038 _fnCallbackReg( oSettings, 'aoDrawCallback', oInit.fnDrawCallback, 'user' );
1039 _fnCallbackReg( oSettings, 'aoServerParams', oInit.fnServerParams, 'user' );
1040 _fnCallbackReg( oSettings, 'aoStateSaveParams', oInit.fnStateSaveParams, 'user' );
1041 _fnCallbackReg( oSettings, 'aoStateLoadParams', oInit.fnStateLoadParams, 'user' );
1042 _fnCallbackReg( oSettings, 'aoStateLoaded', oInit.fnStateLoaded, 'user' );
1043 _fnCallbackReg( oSettings, 'aoRowCallback', oInit.fnRowCallback, 'user' );
1044 _fnCallbackReg( oSettings, 'aoRowCreatedCallback', oInit.fnCreatedRow, 'user' );
1045 _fnCallbackReg( oSettings, 'aoHeaderCallback', oInit.fnHeaderCallback, 'user' );
1046 _fnCallbackReg( oSettings, 'aoFooterCallback', oInit.fnFooterCallback, 'user' );
1047 _fnCallbackReg( oSettings, 'aoInitComplete', oInit.fnInitComplete, 'user' );
1048 _fnCallbackReg( oSettings, 'aoPreDrawCallback', oInit.fnPreDrawCallback, 'user' );
1049
1050 oSettings.rowIdFn = _fnGetObjectDataFn( oInit.rowId );
1051
1052 /* Browser support detection */
1053 _fnBrowserDetect( oSettings );
1054
1055 var oClasses = oSettings.oClasses;
1056
1057 // @todo Remove in 1.11
1058 if ( oInit.bJQueryUI )
1059 {
1060 /* Use the JUI classes object for display. You could clone the oStdClasses object if
1061 * you want to have multiple tables with multiple independent classes
1062 */
1063 $.extend( oClasses, DataTable.ext.oJUIClasses, oInit.oClasses );
1064
1065 if ( oInit.sDom === defaults.sDom && defaults.sDom === "lfrtip" )
1066 {
1067 /* Set the DOM to use a layout suitable for jQuery UI's theming */
1068 oSettings.sDom = '<"H"lfr>t<"F"ip>';
1069 }
1070
1071 if ( ! oSettings.renderer ) {
1072 oSettings.renderer = 'jqueryui';
1073 }
1074 else if ( $.isPlainObject( oSettings.renderer ) && ! oSettings.renderer.header ) {
1075 oSettings.renderer.header = 'jqueryui';
1076 }
1077 }
1078 else
1079 {
1080 $.extend( oClasses, DataTable.ext.classes, oInit.oClasses );
1081 }
1082 $this.addClass( oClasses.sTable );
1083
1084
1085 if ( oSettings.iInitDisplayStart === undefined )
1086 {
1087 /* Display start point, taking into account the save saving */
1088 oSettings.iInitDisplayStart = oInit.iDisplayStart;
1089 oSettings._iDisplayStart = oInit.iDisplayStart;
1090 }
1091
1092 if ( oInit.iDeferLoading !== null )
1093 {
1094 oSettings.bDeferLoading = true;
1095 var tmp = $.isArray( oInit.iDeferLoading );
1096 oSettings._iRecordsDisplay = tmp ? oInit.iDeferLoading[0] : oInit.iDeferLoading;
1097 oSettings._iRecordsTotal = tmp ? oInit.iDeferLoading[1] : oInit.iDeferLoading;
1098 }
1099
1100 /* Language definitions */
1101 var oLanguage = oSettings.oLanguage;
1102 $.extend( true, oLanguage, oInit.oLanguage );
1103
1104 if ( oLanguage.sUrl )
1105 {
1106 /* Get the language definitions from a file - because this Ajax call makes the language
1107 * get async to the remainder of this function we use bInitHandedOff to indicate that
1108 * _fnInitialise will be fired by the returned Ajax handler, rather than the constructor
1109 */
1110 $.ajax( {
1111 dataType: 'json',
1112 url: oLanguage.sUrl,
1113 success: function ( json ) {
1114 _fnLanguageCompat( json );
1115 _fnCamelToHungarian( defaults.oLanguage, json );
1116 $.extend( true, oLanguage, json );
1117 _fnInitialise( oSettings );
1118 },
1119 error: function () {
1120 // Error occurred loading language file, continue on as best we can
1121 _fnInitialise( oSettings );
1122 }
1123 } );
1124 bInitHandedOff = true;
1125 }
1126
1127 /*
1128 * Stripes
1129 */
1130 if ( oInit.asStripeClasses === null )
1131 {
1132 oSettings.asStripeClasses =[
1133 oClasses.sStripeOdd,
1134 oClasses.sStripeEven
1135 ];
1136 }
1137
1138 /* Remove row stripe classes if they are already on the table row */
1139 var stripeClasses = oSettings.asStripeClasses;
1140 var rowOne = $this.children('tbody').find('tr').eq(0);
1141 if ( $.inArray( true, $.map( stripeClasses, function(el, i) {
1142 return rowOne.hasClass(el);
1143 } ) ) !== -1 ) {
1144 $('tbody tr', this).removeClass( stripeClasses.join(' ') );
1145 oSettings.asDestroyStripes = stripeClasses.slice();
1146 }
1147
1148 /*
1149 * Columns
1150 * See if we should load columns automatically or use defined ones
1151 */
1152 var anThs = [];
1153 var aoColumnsInit;
1154 var nThead = this.getElementsByTagName('thead');
1155 if ( nThead.length !== 0 )
1156 {
1157 _fnDetectHeader( oSettings.aoHeader, nThead[0] );
1158 anThs = _fnGetUniqueThs( oSettings );
1159 }
1160
1161 /* If not given a column array, generate one with nulls */
1162 if ( oInit.aoColumns === null )
1163 {
1164 aoColumnsInit = [];
1165 for ( i=0, iLen=anThs.length ; i<iLen ; i++ )
1166 {
1167 aoColumnsInit.push( null );
1168 }
1169 }
1170 else
1171 {
1172 aoColumnsInit = oInit.aoColumns;
1173 }
1174
1175 /* Add the columns */
1176 for ( i=0, iLen=aoColumnsInit.length ; i<iLen ; i++ )
1177 {
1178 _fnAddColumn( oSettings, anThs ? anThs[i] : null );
1179 }
1180
1181 /* Apply the column definitions */
1182 _fnApplyColumnDefs( oSettings, oInit.aoColumnDefs, aoColumnsInit, function (iCol, oDef) {
1183 _fnColumnOptions( oSettings, iCol, oDef );
1184 } );
1185
1186 /* HTML5 attribute detection - build an mData object automatically if the
1187 * attributes are found
1188 */
1189 if ( rowOne.length ) {
1190 var a = function ( cell, name ) {
1191 return cell.getAttribute( 'data-'+name ) !== null ? name : null;
1192 };
1193
1194 $( rowOne[0] ).children('th, td').each( function (i, cell) {
1195 var col = oSettings.aoColumns[i];
1196
1197 if ( col.mData === i ) {
1198 var sort = a( cell, 'sort' ) || a( cell, 'order' );
1199 var filter = a( cell, 'filter' ) || a( cell, 'search' );
1200
1201 if ( sort !== null || filter !== null ) {
1202 col.mData = {
1203 _: i+'.display',
1204 sort: sort !== null ? i+'.@data-'+sort : undefined,
1205 type: sort !== null ? i+'.@data-'+sort : undefined,
1206 filter: filter !== null ? i+'.@data-'+filter : undefined
1207 };
1208
1209 _fnColumnOptions( oSettings, i );
1210 }
1211 }
1212 } );
1213 }
1214
1215 var features = oSettings.oFeatures;
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
1324 /* Must be done after everything which can be overridden by the state saving! */
1325 if ( oInit.bStateSave )
1326 {
1327 features.bStateSave = true;
1328 _fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState, 'state_save' );
1329 _fnLoadState( oSettings, oInit, loadedInit );
1330 }
1331 else {
1332 loadedInit();
1333 }
1334
1335 } );
1336 _that = null;
1337 return this;
1338 };
82 1339
83 1340
84 1341 /*
@@ -105,15 +1362,25 b''
105 1362 var _re_dic = {};
106 1363 var _re_new_lines = /[\r\n]/g;
107 1364 var _re_html = /<.*?>/g;
108 var _re_date_start = /^[\w\+\-]/;
109 var _re_date_end = /[\w\+\-]$/;
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})?)?$/;
110 1369
111 1370 // Escape regular expression special characters
112 1371 var _re_escape_regex = new RegExp( '(\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ].join('|\\') + ')', 'g' );
113 1372
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;
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;
117 1384
118 1385
119 1386 var _empty = function ( d ) {
@@ -142,6 +1409,13 b''
142 1409 var _isNumber = function ( d, decimalPoint, formatted ) {
143 1410 var strType = typeof d === 'string';
144 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
145 1419 if ( decimalPoint && strType ) {
146 1420 d = _numToDecimal( d, decimalPoint );
147 1421 }
@@ -150,7 +1424,7 b''
150 1424 d = d.replace( _re_formatted_numeric, '' );
151 1425 }
152 1426
153 return _empty( d ) || (!isNaN( parseFloat(d) ) && isFinite( d ));
1427 return !isNaN( parseFloat(d) ) && isFinite( d );
154 1428 };
155 1429
156 1430
@@ -302,6 +1576,65 b''
302 1576 };
303 1577
304 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
305 1638
306 1639 /**
307 1640 * Create a mapping object that allows camel case parameters to be looked up
@@ -452,6 +1785,14 b''
452 1785 _fnCompatMap( init, 'pageLength', 'iDisplayLength' );
453 1786 _fnCompatMap( init, 'searching', 'bFilter' );
454 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
455 1796 // Column search objects are in an array, so it needs to be converted
456 1797 // element by element
457 1798 var searchCols = init.aoSearchCols;
@@ -478,6 +1819,12 b''
478 1819 _fnCompatMap( init, 'orderData', 'aDataSort' );
479 1820 _fnCompatMap( init, 'orderSequence', 'asSorting' );
480 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 }
481 1828 }
482 1829
483 1830
@@ -488,14 +1835,19 b''
488 1835 */
489 1836 function _fnBrowserDetect( settings )
490 1837 {
491 var browser = settings.oBrowser;
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;
492 1844
493 1845 // Scrolling feature / quirks detection
494 1846 var n = $('<div/>')
495 1847 .css( {
496 position: 'absolute',
1848 position: 'fixed',
497 1849 top: 0,
498 left: 0,
1850 left: $(window).scrollLeft()*-1, // allow for scrolling
499 1851 height: 1,
500 1852 width: 1,
501 1853 overflow: 'hidden'
@@ -510,7 +1862,7 b''
510 1862 overflow: 'scroll'
511 1863 } )
512 1864 .append(
513 $('<div class="test"/>')
1865 $('<div/>')
514 1866 .css( {
515 1867 width: '100%',
516 1868 height: 10
@@ -519,20 +1871,41 b''
519 1871 )
520 1872 .appendTo( 'body' );
521 1873
522 var test = n.find('.test');
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;
523 1889
524 1890 // IE6/7 will oversize a width 100% element inside a scrolling element, to
525 1891 // include the width of the scrollbar, while other browsers ensure the inner
526 1892 // element is contained without forcing scrolling
527 browser.bScrollOversize = test[0].offsetWidth === 100;
1893 browser.bScrollOversize = inner[0].offsetWidth === 100 && outer[0].clientWidth !== 100;
528 1894
529 1895 // In rtl text layout, some browsers (most, but not all) will place the
530 1896 // scrollbar on the left, rather than the right.
531 browser.bScrollbarLeft = test.offset().left !== 1;
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;
532 1901
533 1902 n.remove();
534 1903 }
535 1904
1905 $.extend( settings.oBrowser, DataTable.__browser );
1906 settings.oScroll.iBarWidth = DataTable.__browser.barWidth;
1907 }
1908
536 1909
537 1910 /**
538 1911 * Array.prototype reduce[Right] method, used for browsers which don't support
@@ -595,7 +1968,7 b''
595 1968 searchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch, searchCols[ iCol ] );
596 1969
597 1970 // Use the default column options function to initialise classes etc
598 _fnColumnOptions( oSettings, iCol, null );
1971 _fnColumnOptions( oSettings, iCol, $(nTh).data() );
599 1972 }
600 1973
601 1974
@@ -658,7 +2031,7 b''
658 2031 /* iDataSort to be applied (backwards compatibility), but aDataSort will take
659 2032 * priority if defined
660 2033 */
661 if ( typeof oOptions.iDataSort === 'number' )
2034 if ( oOptions.iDataSort !== undefined )
662 2035 {
663 2036 oCol.aDataSort = [ oOptions.iDataSort ];
664 2037 }
@@ -676,6 +2049,7 b''
676 2049 oCol._bAttrSrc = $.isPlainObject( mDataSrc ) && (
677 2050 attrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter)
678 2051 );
2052 oCol._setter = null;
679 2053
680 2054 oCol.fnGetData = function (rowData, type, meta) {
681 2055 var innerData = mData( rowData, type, undefined, meta );
@@ -800,7 +2174,16 b''
800 2174 */
801 2175 function _fnVisbleColumns( oSettings )
802 2176 {
803 return _fnGetColumns( oSettings, 'bVisible' ).length;
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;
804 2187 }
805 2188
806 2189
@@ -987,7 +2370,8 b''
987 2370 /* Create the object for storing information about this new row */
988 2371 var iRow = oSettings.aoData.length;
989 2372 var oData = $.extend( true, {}, DataTable.models.oRow, {
990 src: nTr ? 'dom' : 'data'
2373 src: nTr ? 'dom' : 'data',
2374 idx: iRow
991 2375 } );
992 2376
993 2377 oData._aData = aDataIn;
@@ -996,20 +2380,21 b''
996 2380 /* Create the cells */
997 2381 var nTd, sThisType;
998 2382 var columns = oSettings.aoColumns;
2383
2384 // Invalidate the column types as the new data needs to be revalidated
999 2385 for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
1000 2386 {
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 2387 columns[i].sType = null;
1008 2388 }
1009 2389
1010 2390 /* Add to the display array */
1011 2391 oSettings.aiDisplayMaster.push( iRow );
1012 2392
2393 var id = oSettings.rowIdFn( aDataIn );
2394 if ( id !== undefined ) {
2395 oSettings.aIds[ id ] = oData;
2396 }
2397
1013 2398 /* Create the DOM information, or register it if already present */
1014 2399 if ( nTr || ! oSettings.oFeatures.bDeferRender )
1015 2400 {
@@ -1098,14 +2483,15 b''
1098 2483 if ( settings.iDrawError != draw && defaultContent === null ) {
1099 2484 _fnLog( settings, 0, "Requested unknown parameter "+
1100 2485 (typeof col.mData=='function' ? '{function}' : "'"+col.mData+"'")+
1101 " for row "+rowIdx, 4 );
2486 " for row "+rowIdx+", column "+colIdx, 4 );
1102 2487 settings.iDrawError = draw;
1103 2488 }
1104 2489 return defaultContent;
1105 2490 }
1106 2491
1107 /* When the data source is null, we can use default column data */
1108 if ( (cellData === rowData || cellData === null) && defaultContent !== null ) {
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 ) {
1109 2495 cellData = defaultContent;
1110 2496 }
1111 2497 else if ( typeof cellData === 'function' ) {
@@ -1153,8 +2539,8 b''
1153 2539 */
1154 2540 function _fnSplitObjNotation( str )
1155 2541 {
1156 return $.map( str.match(/(\\.|[^\.])+/g), function ( s ) {
1157 return s.replace(/\\./g, '.');
2542 return $.map( str.match(/(\\.|[^\.])+/g) || [''], function ( s ) {
2543 return s.replace(/\\\./g, '.');
1158 2544 } );
1159 2545 }
1160 2546
@@ -1236,9 +2622,11 b''
1236 2622 innerSrc = a.join('.');
1237 2623
1238 2624 // Traverse each entry in the array getting the properties requested
2625 if ( $.isArray( data ) ) {
1239 2626 for ( var j=0, jLen=data.length ; j<jLen ; j++ ) {
1240 2627 out.push( fetchData( data[j], type, innerSrc ) );
1241 2628 }
2629 }
1242 2630
1243 2631 // If a string is given in between the array notation indicators, that
1244 2632 // is used to join the strings together, otherwise an array is returned
@@ -1337,12 +2725,22 b''
1337 2725 innerSrc = b.join('.');
1338 2726
1339 2727 // Traverse each entry in the array setting the properties requested
2728 if ( $.isArray( val ) )
2729 {
1340 2730 for ( var j=0, jLen=val.length ; j<jLen ; j++ )
1341 2731 {
1342 2732 o = {};
1343 2733 setData( o, val[j], innerSrc );
1344 2734 data[ a[i] ].push( o );
1345 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 }
1346 2744
1347 2745 // The inner call to setData has already traversed through the remainder
1348 2746 // of the source and has set the data, thus we can exit here
@@ -1414,6 +2812,7 b''
1414 2812 settings.aoData.length = 0;
1415 2813 settings.aiDisplayMaster.length = 0;
1416 2814 settings.aiDisplay.length = 0;
2815 settings.aIds = {};
1417 2816 }
1418 2817
1419 2818
@@ -1519,7 +2918,7 b''
1519 2918 }
1520 2919
1521 2920 // Update DataTables special `DT_*` attributes for the row
1522 _fnRowAttributes( row );
2921 _fnRowAttributes( settings, row );
1523 2922 }
1524 2923 }
1525 2924
@@ -1551,7 +2950,11 b''
1551 2950 objectRead = settings._rowReadObject;
1552 2951
1553 2952 // Allow the data object to be passed in, or construct
1554 d = d || objectRead ? {} : [];
2953 d = d !== undefined ?
2954 d :
2955 objectRead ?
2956 {} :
2957 [];
1555 2958
1556 2959 var attr = function ( str, td ) {
1557 2960 if ( typeof str === 'string' ) {
@@ -1620,6 +3023,17 b''
1620 3023 }
1621 3024 }
1622 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
1623 3037 return {
1624 3038 data: d,
1625 3039 cells: tds
@@ -1657,7 +3071,7 b''
1657 3071 nTr._DT_RowIndex = iRow;
1658 3072
1659 3073 /* Special parameters can be given by the data source to be used on the row */
1660 _fnRowAttributes( row );
3074 _fnRowAttributes( oSettings, row );
1661 3075
1662 3076 /* Process each column */
1663 3077 for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
@@ -1665,11 +3079,17 b''
1665 3079 oCol = oSettings.aoColumns[i];
1666 3080
1667 3081 nTd = nTrIn ? anTds[i] : document.createElement( oCol.sCellType );
3082 nTd._DT_CellIndex = {
3083 row: iRow,
3084 column: i
3085 };
3086
1668 3087 cells.push( nTd );
1669 3088
1670 3089 // Need to create the HTML if new, or if a rendering function is defined
1671 if ( !nTrIn || oCol.mRender || oCol.mData !== i )
1672 {
3090 if ( (!nTrIn || oCol.mRender || oCol.mData !== i) &&
3091 (!$.isPlainObject(oCol.mData) || oCol.mData._ !== i+'.display')
3092 ) {
1673 3093 nTd.innerHTML = _fnGetCellData( oSettings, iRow, i, 'display' );
1674 3094 }
1675 3095
@@ -1709,17 +3129,20 b''
1709 3129 /**
1710 3130 * Add attributes to a row based on the special `DT_*` parameters in a data
1711 3131 * source object.
3132 * @param {object} settings DataTables settings object
1712 3133 * @param {object} DataTables row object for the row to be modified
1713 3134 * @memberof DataTable#oApi
1714 3135 */
1715 function _fnRowAttributes( row )
3136 function _fnRowAttributes( settings, row )
1716 3137 {
1717 3138 var tr = row.nTr;
1718 3139 var data = row._aData;
1719 3140
1720 3141 if ( tr ) {
1721 if ( data.DT_RowId ) {
1722 tr.id = data.DT_RowId;
3142 var id = settings.rowIdFn( data );
3143
3144 if ( id ) {
3145 tr.id = id;
1723 3146 }
1724 3147
1725 3148 if ( data.DT_RowClass ) {
@@ -1734,6 +3157,10 b''
1734 3157 .addClass( data.DT_RowClass );
1735 3158 }
1736 3159
3160 if ( data.DT_RowAttr ) {
3161 $(tr).attr( data.DT_RowAttr );
3162 }
3163
1737 3164 if ( data.DT_RowData ) {
1738 3165 $(tr).data( data.DT_RowData );
1739 3166 }
@@ -1780,7 +3207,7 b''
1780 3207 }
1781 3208 }
1782 3209
1783 if ( column.sTitle != cell.html() ) {
3210 if ( column.sTitle != cell[0].innerHTML ) {
1784 3211 cell.html( column.sTitle );
1785 3212 }
1786 3213
@@ -2252,6 +3679,7 b''
2252 3679
2253 3680 /* Built our DOM structure - replace the holding div with what we want */
2254 3681 holding.replaceWith( insert );
3682 oSettings.nHolding = null;
2255 3683 }
2256 3684
2257 3685
@@ -2367,8 +3795,6 b''
2367 3795 return aReturn;
2368 3796 }
2369 3797
2370
2371
2372 3798 /**
2373 3799 * Create an Ajax call based on the table's settings, taking into account that
2374 3800 * parameters can have multiple forms, and backwards compatibility.
@@ -2411,16 +3837,20 b''
2411 3837 var ajaxData;
2412 3838 var ajax = oSettings.ajax;
2413 3839 var instance = oSettings.oInstance;
3840 var callback = function ( json ) {
3841 _fnCallbackFire( oSettings, null, 'xhr', [oSettings, json, oSettings.jqXHR] );
3842 fn( json );
3843 };
2414 3844
2415 3845 if ( $.isPlainObject( ajax ) && ajax.data )
2416 3846 {
2417 3847 ajaxData = ajax.data;
2418 3848
2419 3849 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
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
2424 3854 data = $.isFunction( ajaxData ) && newData ?
2425 3855 newData :
2426 3856 $.extend( true, data, newData );
@@ -2435,24 +3865,25 b''
2435 3865 "success": function (json) {
2436 3866 var error = json.error || json.sError;
2437 3867 if ( error ) {
2438 oSettings.oApi._fnLog( oSettings, 0, error );
3868 _fnLog( oSettings, 0, error );
2439 3869 }
2440 3870
2441 3871 oSettings.json = json;
2442 _fnCallbackFire( oSettings, null, 'xhr', [oSettings, json] );
2443 fn( json );
3872 callback( json );
2444 3873 },
2445 3874 "dataType": "json",
2446 3875 "cache": false,
2447 3876 "type": oSettings.sServerMethod,
2448 3877 "error": function (xhr, error, thrown) {
2449 var log = oSettings.oApi._fnLog;
2450
3878 var ret = _fnCallbackFire( oSettings, null, 'xhr', [oSettings, null, oSettings.jqXHR] );
3879
3880 if ( $.inArray( true, ret ) === -1 ) {
2451 3881 if ( error == "parsererror" ) {
2452 log( oSettings, 0, 'Invalid JSON response', 1 );
3882 _fnLog( oSettings, 0, 'Invalid JSON response', 1 );
2453 3883 }
2454 3884 else if ( xhr.readyState === 4 ) {
2455 log( oSettings, 0, 'Ajax error', 7 );
3885 _fnLog( oSettings, 0, 'Ajax error', 7 );
3886 }
2456 3887 }
2457 3888
2458 3889 _fnProcessingDisplay( oSettings, false );
@@ -2473,7 +3904,7 b''
2473 3904 $.map( data, function (val, key) { // Need to convert back to 1.9 trad format
2474 3905 return { name: key, value: val };
2475 3906 } ),
2476 fn,
3907 callback,
2477 3908 oSettings
2478 3909 );
2479 3910 }
@@ -2487,7 +3918,7 b''
2487 3918 else if ( $.isFunction( ajax ) )
2488 3919 {
2489 3920 // Is a function - let the caller define what needs to be done
2490 oSettings.jqXHR = ajax.call( instance, data, fn, oSettings );
3921 oSettings.jqXHR = ajax.call( instance, data, callback, oSettings );
2491 3922 }
2492 3923 else
2493 3924 {
@@ -2653,6 +4084,7 b''
2653 4084 return json[old] !== undefined ? json[old] : json[modern];
2654 4085 };
2655 4086
4087 var data = _fnAjaxDataSrc( settings, json );
2656 4088 var draw = compat( 'sEcho', 'draw' );
2657 4089 var recordsTotal = compat( 'iTotalRecords', 'recordsTotal' );
2658 4090 var recordsFiltered = compat( 'iTotalDisplayRecords', 'recordsFiltered' );
@@ -2669,7 +4101,6 b''
2669 4101 settings._iRecordsTotal = parseInt(recordsTotal, 10);
2670 4102 settings._iRecordsDisplay = parseInt(recordsFiltered, 10);
2671 4103
2672 var data = _fnAjaxDataSrc( settings, json );
2673 4104 for ( var i=0, ien=data.length ; i<ien ; i++ ) {
2674 4105 _fnAddData( settings, data[i] );
2675 4106 }
@@ -2712,7 +4143,6 b''
2712 4143 json;
2713 4144 }
2714 4145
2715
2716 4146 /**
2717 4147 * Generate the node required for filtering text
2718 4148 * @returns {node} Filter control element
@@ -2768,13 +4198,13 b''
2768 4198 var jqFilter = $('input', filter)
2769 4199 .val( previousSearch.sSearch )
2770 4200 .attr( 'placeholder', language.sSearchPlaceholder )
2771 .bind(
4201 .on(
2772 4202 'keyup.DT search.DT input.DT paste.DT cut.DT',
2773 4203 searchDelay ?
2774 4204 _fnThrottle( searchFn, searchDelay ) :
2775 4205 searchFn
2776 4206 )
2777 .bind( 'keypress.DT', function(e) {
4207 .on( 'keypress.DT', function(e) {
2778 4208 /* Prevent form submission */
2779 4209 if ( e.keyCode == 13 ) {
2780 4210 return false;
@@ -2882,7 +4312,7 b''
2882 4312 // So the array reference doesn't break set the results into the
2883 4313 // existing array
2884 4314 displayRows.length = 0;
2885 displayRows.push.apply( displayRows, rows );
4315 $.merge( displayRows, rows );
2886 4316 }
2887 4317 }
2888 4318
@@ -2904,16 +4334,19 b''
2904 4334 }
2905 4335
2906 4336 var data;
4337 var out = [];
2907 4338 var display = settings.aiDisplay;
2908 4339 var rpSearch = _fnFilterCreateSearch( searchStr, regex, smart, caseInsensitive );
2909 4340
2910 for ( var i=display.length-1 ; i>=0 ; i-- ) {
4341 for ( var i=0 ; i<display.length ; i++ ) {
2911 4342 data = settings.aoData[ display[i] ]._aFilterData[ colIdx ];
2912 4343
2913 if ( ! rpSearch.test( data ) ) {
2914 display.splice( i, 1 );
2915 }
2916 }
4344 if ( rpSearch.test( data ) ) {
4345 out.push( display[i] );
4346 }
4347 }
4348
4349 settings.aiDisplay = out;
2917 4350 }
2918 4351
2919 4352
@@ -2933,6 +4366,7 b''
2933 4366 var prevSearch = settings.oPreviousSearch.sSearch;
2934 4367 var displayMaster = settings.aiDisplayMaster;
2935 4368 var display, invalidated, i;
4369 var filtered = [];
2936 4370
2937 4371 // Need to take account of custom filtering functions - always filter
2938 4372 if ( DataTable.ext.search.length !== 0 ) {
@@ -2961,11 +4395,13 b''
2961 4395 // Search the display array
2962 4396 display = settings.aiDisplay;
2963 4397
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 }
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;
2969 4405 }
2970 4406 }
2971 4407
@@ -2993,7 +4429,7 b''
2993 4429 *
2994 4430 * ^(?=.*?\bone\b)(?=.*?\btwo three\b)(?=.*?\bfour\b).*$
2995 4431 */
2996 var a = $.map( search.match( /"[^"]+"|[^ ]+/g ) || '', function ( word ) {
4432 var a = $.map( search.match( /"[^"]+"|[^ ]+/g ) || [''], function ( word ) {
2997 4433 if ( word.charAt(0) === '"' ) {
2998 4434 var m = word.match( /^"(.*)"$/ );
2999 4435 word = m ? m[1] : word;
@@ -3015,12 +4451,7 b''
3015 4451 * @returns {string} escaped string
3016 4452 * @memberof DataTable#oApi
3017 4453 */
3018 function _fnEscapeRegex ( sVal )
3019 {
3020 return sVal.replace( _re_escape_regex, '\\$1' );
3021 }
3022
3023
4454 var _fnEscapeRegex = DataTable.util.escapeRegex;
3024 4455
3025 4456 var __filter_div = $('<div>')[0];
3026 4457 var __filter_div_textContent = __filter_div.textContent !== undefined;
@@ -3238,6 +4669,7 b''
3238 4669 var i, iLen, iAjaxStart=settings.iInitDisplayStart;
3239 4670 var columns = settings.aoColumns, column;
3240 4671 var features = settings.oFeatures;
4672 var deferLoading = settings.bDeferLoading; // value modified by the draw
3241 4673
3242 4674 /* Ensure that the table data is fully initialised */
3243 4675 if ( ! settings.bInitialised ) {
@@ -3269,6 +4701,8 b''
3269 4701 }
3270 4702 }
3271 4703
4704 _fnCallbackFire( settings, null, 'preInit', [settings] );
4705
3272 4706 // If there is default sorting required - let's do it. The sort function
3273 4707 // will do the drawing for us. Otherwise we draw the table regardless of the
3274 4708 // Ajax source - this allows the table to look initialised for Ajax sourcing
@@ -3277,7 +4711,7 b''
3277 4711
3278 4712 // Server-side processing init complete is done by _fnAjaxUpdateDraw
3279 4713 var dataSrc = _fnDataSource( settings );
3280 if ( dataSrc != 'ssp' ) {
4714 if ( dataSrc != 'ssp' || deferLoading ) {
3281 4715 // if there is an ajax source load the data
3282 4716 if ( dataSrc == 'ajax' ) {
3283 4717 _fnBuildAjax( settings, [], function(json) {
@@ -3318,12 +4752,13 b''
3318 4752 {
3319 4753 settings._bInitComplete = true;
3320 4754
3321 // On an Ajax load we now have data and therefore want to apply the column
3322 // sizing
3323 if ( json ) {
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 ) {
3324 4758 _fnAdjustColumnSizing( settings );
3325 4759 }
3326 4760
4761 _fnCallbackFire( settings, null, 'plugin-init', [settings, json] );
3327 4762 _fnCallbackFire( settings, 'aoInitComplete', 'init', [settings, json] );
3328 4763 }
3329 4764
@@ -3379,13 +4814,13 b''
3379 4814 // reference is broken by the use of outerHTML
3380 4815 $('select', div)
3381 4816 .val( settings._iDisplayLength )
3382 .bind( 'change.DT', function(e) {
4817 .on( 'change.DT', function(e) {
3383 4818 _fnLengthChange( settings, $(this).val() );
3384 4819 _fnDraw( settings );
3385 4820 } );
3386 4821
3387 4822 // Update node value whenever anything changes the table's length
3388 $(settings.nTable).bind( 'length.dt.DT', function (e, s, len) {
4823 $(settings.nTable).on( 'length.dt.DT', function (e, s, len) {
3389 4824 if ( settings === s ) {
3390 4825 $('select', div).val( len );
3391 4826 }
@@ -3600,17 +5035,6 b''
3600 5035 return !s ? null : _fnStringToCss( s );
3601 5036 };
3602 5037
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 5038 if ( ! footer.length ) {
3615 5039 footer = null;
3616 5040 }
@@ -3660,8 +5084,8 b''
3660 5084 .append(
3661 5085 $(_div, { 'class': classes.sScrollBody } )
3662 5086 .css( {
5087 position: 'relative',
3663 5088 overflow: 'auto',
3664 height: size( scrollY ),
3665 5089 width: size( scrollX )
3666 5090 } )
3667 5091 .append( table )
@@ -3697,7 +5121,7 b''
3697 5121
3698 5122 // When the body is scrolled, then we also want to scroll the headers
3699 5123 if ( scrollX ) {
3700 $(scrollBody).scroll( function (e) {
5124 $(scrollBody).on( 'scroll.DT', function (e) {
3701 5125 var scrollLeft = this.scrollLeft;
3702 5126
3703 5127 scrollHead.scrollLeft = scrollLeft;
@@ -3708,6 +5132,11 b''
3708 5132 } );
3709 5133 }
3710 5134
5135 $(scrollBody).css(
5136 scrollY && scroll.bCollapse ? 'max-height' : 'height',
5137 scrollY
5138 );
5139
3711 5140 settings.nScrollHead = scrollHead;
3712 5141 settings.nScrollBody = scrollBody;
3713 5142 settings.nScrollFoot = scrollFoot;
@@ -3765,11 +5194,12 b''
3765 5194 footer = settings.nTFoot ? $(settings.nTFoot) : null,
3766 5195 browser = settings.oBrowser,
3767 5196 ie67 = browser.bScrollOversize,
5197 dtHeaderCells = _pluck( settings.aoColumns, 'nTh' ),
3768 5198 headerTrgEls, footerTrgEls,
3769 5199 headerSrcEls, footerSrcEls,
3770 5200 headerCopy, footerCopy,
3771 5201 headerWidths=[], footerWidths=[],
3772 headerContent=[],
5202 headerContent=[], footerContent=[],
3773 5203 idx, correction, sanityWidth,
3774 5204 zeroOut = function(nSizer) {
3775 5205 var style = nSizer.style;
@@ -3780,6 +5210,20 b''
3780 5210 style.height = 0;
3781 5211 };
3782 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
3783 5227 /*
3784 5228 * 1. Re-create the table inside the scrolling div
3785 5229 */
@@ -3787,18 +5231,18 b''
3787 5231 // Remove the old minimised thead and tfoot elements in the inner table
3788 5232 table.children('thead, tfoot').remove();
3789 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
3790 5240 // Clone the current header and footer elements and then place it into the inner table
3791 5241 headerCopy = header.clone().prependTo( table );
3792 5242 headerTrgEls = header.find('tr'); // original header is in its own table
3793 5243 headerSrcEls = headerCopy.find('tr');
3794 5244 headerCopy.find('th, td').removeAttr('tabindex');
3795 5245
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 5246
3803 5247 /*
3804 5248 * 2. Take live measurements from the DOM - do not alter the DOM itself!
@@ -3824,13 +5268,6 b''
3824 5268 }, footerSrcEls );
3825 5269 }
3826 5270
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 5271 // Size the table as a whole
3835 5272 sanityWidth = table.outerWidth();
3836 5273 if ( scrollX === "" ) {
@@ -3845,32 +5282,17 b''
3845 5282 ) {
3846 5283 tableStyle.width = _fnStringToCss( table.outerWidth() - barWidth);
3847 5284 }
3848 }
3849 else
3850 {
3851 // x scrolling
3852 if ( scrollXInner !== "" ) {
3853 // x scroll inner has been given - use it
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
3854 5291 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.
5292
5293 // Recalculate the sanity width
3873 5294 sanityWidth = table.outerWidth();
5295 }
3874 5296
3875 5297 // Hidden header should have zero height, so remove padding and borders. Then
3876 5298 // set the width based on the real headers
@@ -3886,7 +5308,11 b''
3886 5308
3887 5309 // Apply all widths in final pass
3888 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 ) {
3889 5314 nToSize.style.width = headerWidths[i];
5315 }
3890 5316 }, headerTrgEls );
3891 5317
3892 5318 $(headerSrcEls).height(0);
@@ -3897,6 +5323,7 b''
3897 5323 _fnApplyToChildren( zeroOut, footerSrcEls );
3898 5324
3899 5325 _fnApplyToChildren( function(nSizer) {
5326 footerContent.push( nSizer.innerHTML );
3900 5327 footerWidths.push( _fnStringToCss( $(nSizer).css('width') ) );
3901 5328 }, footerSrcEls );
3902 5329
@@ -3924,7 +5351,7 b''
3924 5351 if ( footer )
3925 5352 {
3926 5353 _fnApplyToChildren( function(nSizer, i) {
3927 nSizer.innerHTML = "";
5354 nSizer.innerHTML = '<div class="dataTables_sizing" style="height:0;overflow:hidden;">'+footerContent[i]+'</div>';
3928 5355 nSizer.style.width = footerWidths[i];
3929 5356 }, footerSrcEls );
3930 5357 }
@@ -3978,18 +5405,6 b''
3978 5405 }
3979 5406 }
3980 5407
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 5408 /* Finally set the width's of the header and footer tables */
3994 5409 var iOuterWidth = table.outerWidth();
3995 5410 divHeaderTable[0].style.width = _fnStringToCss( iOuterWidth );
@@ -4007,6 +5422,9 b''
4007 5422 divFooterInner[0].style[padding] = bScrolling ? barWidth+"px" : "0px";
4008 5423 }
4009 5424
5425 // Correct DOM ordering for colgroup - comes before the thead
5426 table.children('colgroup').insertBefore( table.children('thead') );
5427
4010 5428 /* Adjust the position of the header in case we loose the y-scrollbar */
4011 5429 divBody.scroll();
4012 5430
@@ -4078,10 +5496,17 b''
4078 5496 columnCount = columns.length,
4079 5497 visibleColumns = _fnGetColumns( oSettings, 'bVisible' ),
4080 5498 headerCells = $('th', oSettings.nTHead),
4081 tableWidthAttr = table.getAttribute('width'),
5499 tableWidthAttr = table.getAttribute('width'), // from DOM element
4082 5500 tableContainer = table.parentNode,
4083 5501 userInputs = false,
4084 i, column, columnIdx, width, outerWidth;
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 }
4085 5510
4086 5511 /* Convert any user input sizes into pixel sizes */
4087 5512 for ( i=0 ; i<visibleColumns.length ; i++ ) {
@@ -4099,32 +5524,43 b''
4099 5524 * the web- browser. No custom sizes can be set in order for this to happen,
4100 5525 * nor scrolling used
4101 5526 */
4102 if ( ! userInputs && ! scrollX && ! scrollY &&
5527 if ( ie67 || ! userInputs && ! scrollX && ! scrollY &&
4103 5528 columnCount == _fnVisbleColumns( oSettings ) &&
4104 5529 columnCount == headerCells.length
4105 5530 ) {
4106 5531 for ( i=0 ; i<columnCount ; i++ ) {
4107 columns[i].sWidth = _fnStringToCss( headerCells.eq(i).width() );
5532 var colIdx = _fnVisibleToColumnIndex( oSettings, i );
5533
5534 if ( colIdx !== null ) {
5535 columns[ colIdx ].sWidth = _fnStringToCss( headerCells.eq(i).width() );
5536 }
4108 5537 }
4109 5538 }
4110 5539 else
4111 5540 {
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
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
4115 5545 var tmpTable = $(table).clone() // don't use cloneNode - IE8 will remove events on the main table
4116 .empty()
4117 5546 .css( 'visibility', 'hidden' )
4118 .removeAttr( 'id' )
4119 .append( $(oSettings.nTHead).clone( false ) )
4120 .append( $(oSettings.nTFoot).clone( false ) )
4121 .append( $('<tbody><tr/></tbody>') );
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() );
4122 5560
4123 5561 // Remove any assigned widths from the footer (from scrolling)
4124 5562 tmpTable.find('tfoot th, tfoot td').css('width', '');
4125 5563
4126 var tr = tmpTable.find( 'tbody tr' );
4127
4128 5564 // Apply custom sizing to the cloned header
4129 5565 headerCells = _fnGetUniqueThs( oSettings, tmpTable.find('thead')[0] );
4130 5566
@@ -4134,6 +5570,19 b''
4134 5570 headerCells[i].style.width = column.sWidthOrig !== null && column.sWidthOrig !== '' ?
4135 5571 _fnStringToCss( column.sWidthOrig ) :
4136 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 }
4137 5586 }
4138 5587
4139 5588 // Find the widest cell for each column and put it into the table
@@ -4149,8 +5598,28 b''
4149 5598 }
4150 5599 }
4151 5600
4152 // Table has been built, attach to the document so we can work with it
4153 tmpTable.appendTo( tableContainer );
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 );
4154 5623
4155 5624 // When scrolling (X or Y) we want to set the width of the table as
4156 5625 // appropriate. However, when not scrolling leave the table width as it
@@ -4160,57 +5629,50 b''
4160 5629 }
4161 5630 else if ( scrollX ) {
4162 5631 tmpTable.css( 'width', 'auto' );
4163
4164 if ( tmpTable.width() < tableContainer.offsetWidth ) {
4165 tmpTable.width( tableContainer.offsetWidth );
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 );
4166 5638 }
4167 5639 }
4168 5640 else if ( scrollY ) {
4169 tmpTable.width( tableContainer.offsetWidth );
5641 tmpTable.width( tableContainer.clientWidth );
4170 5642 }
4171 5643 else if ( tableWidthAttr ) {
4172 5644 tmpTable.width( tableWidthAttr );
4173 5645 }
4174 5646
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 {
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.
4185 5653 var total = 0;
4186
4187 5654 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 ) );
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
4197 5672 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 5673
4212 5674 // Finished with the table - ditch it
4213 tmpTable.remove();
5675 holder.remove();
4214 5676 }
4215 5677
4216 5678 // If there is a width attr, we want to attach an event listener which
@@ -4222,9 +5684,20 b''
4222 5684 }
4223 5685
4224 5686 if ( (tableWidthAttr || scrollX) && ! oSettings._reszEvt ) {
4225 $(window).bind('resize.DT-'+oSettings.sInstance, _fnThrottle( function () {
5687 var bindResize = function () {
5688 $(window).on('resize.DT-'+oSettings.sInstance, _fnThrottle( function () {
4226 5689 _fnAdjustColumnSizing( oSettings );
4227 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 }
4228 5701
4229 5702 oSettings._reszEvt = true;
4230 5703 }
@@ -4239,35 +5712,7 b''
4239 5712 * @returns {function} wrapped function
4240 5713 * @memberof DataTable#oApi
4241 5714 */
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 }
5715 var _fnThrottle = DataTable.util.throttle;
4271 5716
4272 5717
4273 5718 /**
@@ -4295,27 +5740,6 b''
4295 5740
4296 5741
4297 5742 /**
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 5743 * Get the widest node
4320 5744 * @param {object} settings dataTables settings object
4321 5745 * @param {int} colIdx column of interest
@@ -4350,6 +5774,7 b''
4350 5774 for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
4351 5775 s = _fnGetCellData( settings, i, colIdx, 'display' )+'';
4352 5776 s = s.replace( __re_html_remove, '' );
5777 s = s.replace( /&nbsp;/g, ' ' );
4353 5778
4354 5779 if ( s.length > max ) {
4355 5780 max = s.length;
@@ -4386,53 +5811,6 b''
4386 5811 }
4387 5812
4388 5813
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 5814
4437 5815 function _fnSortFlatten ( settings )
4438 5816 {
@@ -4452,7 +5830,7 b''
4452 5830 }
4453 5831 else {
4454 5832 // 2D array
4455 nestedSort.push.apply( nestedSort, a );
5833 $.merge( nestedSort, a );
4456 5834 }
4457 5835 };
4458 5836
@@ -4720,6 +6098,10 b''
4720 6098 // Yes, modify the sort
4721 6099 nextSortIdx = next( sorting[sortIdx], true );
4722 6100
6101 if ( nextSortIdx === null && sorting.length === 1 ) {
6102 nextSortIdx = 0; // can't remove sorting completely
6103 }
6104
4723 6105 if ( nextSortIdx === null ) {
4724 6106 sorting.splice( sortIdx, 1 );
4725 6107 }
@@ -4914,38 +6296,37 b''
4914 6296 * Attempt to load a saved table state
4915 6297 * @param {object} oSettings dataTables settings object
4916 6298 * @param {object} oInit DataTables init object so we can override settings
4917 * @memberof DataTable#oApi
4918 */
4919 function _fnLoadState ( settings, oInit )
6299 * @param {function} callback Callback to execute when the state has been loaded
6300 * @memberof DataTable#oApi
6301 */
6302 function _fnLoadState ( settings, oInit, callback )
4920 6303 {
4921 6304 var i, ien;
4922 6305 var columns = settings.aoColumns;
4923
4924 if ( ! settings.oFeatures.bStateSave ) {
6306 var loaded = function ( s ) {
6307 if ( ! s || ! s.time ) {
6308 callback();
4925 6309 return;
4926 6310 }
4927 6311
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 */
6312 // Allow custom and plug-in manipulation functions to alter the saved data set and
6313 // cancelling of loading by returning false
4936 6314 var abStateLoad = _fnCallbackFire( settings, 'aoStateLoadParams', 'stateLoadParams', [settings, state] );
4937 6315 if ( $.inArray( false, abStateLoad ) !== -1 ) {
6316 callback();
4938 6317 return;
4939 6318 }
4940 6319
4941 /* Reject old data */
6320 // Reject old data
4942 6321 var duration = settings.iStateDuration;
4943 if ( duration > 0 && state.time < +new Date() - (duration*1000) ) {
6322 if ( duration > 0 && s.time < +new Date() - (duration*1000) ) {
6323 callback();
4944 6324 return;
4945 6325 }
4946 6326
4947 6327 // Number of columns have changed - all bets are off, no restore of settings
4948 if ( columns.length !== state.columns.length ) {
6328 if ( s.columns && columns.length !== s.columns.length ) {
6329 callback();
4949 6330 return;
4950 6331 }
4951 6332
@@ -4954,34 +6335,63 b''
4954 6335
4955 6336 // Restore key features - todo - for 1.11 this needs to be done by
4956 6337 // subscribed events
4957 settings._iDisplayStart = state.start;
4958 settings.iInitDisplayStart = state.start;
4959 settings._iDisplayLength = state.length;
4960 settings.aaSorting = [];
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 }
4961 6345
4962 6346 // Order
4963 $.each( state.order, function ( i, col ) {
6347 if ( s.order !== undefined ) {
6348 settings.aaSorting = [];
6349 $.each( s.order, function ( i, col ) {
4964 6350 settings.aaSorting.push( col[0] >= columns.length ?
4965 6351 [ 0, col[1] ] :
4966 6352 col
4967 6353 );
4968 6354 } );
6355 }
4969 6356
4970 6357 // Search
4971 $.extend( settings.oPreviousSearch, _fnSearchToHung( state.search ) );
6358 if ( s.search !== undefined ) {
6359 $.extend( settings.oPreviousSearch, _fnSearchToHung( s.search ) );
6360 }
4972 6361
4973 6362 // Columns
4974 for ( i=0, ien=state.columns.length ; i<ien ; i++ ) {
4975 var col = state.columns[i];
6363 //
6364 if ( s.columns ) {
6365 for ( i=0, ien=s.columns.length ; i<ien ; i++ ) {
6366 var col = s.columns[i];
4976 6367
4977 6368 // Visibility
6369 if ( col.visible !== undefined ) {
4978 6370 columns[i].bVisible = col.visible;
6371 }
4979 6372
4980 6373 // Search
6374 if ( col.search !== undefined ) {
4981 6375 $.extend( settings.aoPreSearchCols[i], _fnSearchToHung( col.search ) );
4982 6376 }
6377 }
6378 }
4983 6379
4984 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
4985 6395 }
4986 6396
4987 6397
@@ -5013,7 +6423,7 b''
5013 6423 function _fnLog( settings, level, msg, tn )
5014 6424 {
5015 6425 msg = 'DataTables warning: '+
5016 (settings!==null ? 'table id='+settings.sTableId+' - ' : '')+msg;
6426 (settings ? 'table id='+settings.sTableId+' - ' : '')+msg;
5017 6427
5018 6428 if ( tn ) {
5019 6429 msg += '. For more information about this error, please see '+
@@ -5025,12 +6435,19 b''
5025 6435 var ext = DataTable.ext;
5026 6436 var type = ext.sErrMode || ext.errMode;
5027 6437
6438 if ( settings ) {
6439 _fnCallbackFire( settings, null, 'error', [ settings, tn, msg ] );
6440 }
6441
5028 6442 if ( type == 'alert' ) {
5029 6443 alert( msg );
5030 6444 }
5031 else {
6445 else if ( type == 'throw' ) {
5032 6446 throw new Error(msg);
5033 6447 }
6448 else if ( typeof type == 'function' ) {
6449 type( settings, tn, msg );
6450 }
5034 6451 }
5035 6452 else if ( window.console && console.log ) {
5036 6453 console.log( msg );
@@ -5127,17 +6544,17 b''
5127 6544 function _fnBindAction( n, oData, fn )
5128 6545 {
5129 6546 $(n)
5130 .bind( 'click.DT', oData, function (e) {
6547 .on( 'click.DT', oData, function (e) {
5131 6548 n.blur(); // Remove focus outline for mouse users
5132 6549 fn(e);
5133 6550 } )
5134 .bind( 'keypress.DT', oData, function (e){
6551 .on( 'keypress.DT', oData, function (e){
5135 6552 if ( e.which === 13 ) {
5136 6553 e.preventDefault();
5137 6554 fn(e);
5138 6555 }
5139 6556 } )
5140 .bind( 'selectstart.DT', function () {
6557 .on( 'selectstart.DT', function () {
5141 6558 /* Take the brutal approach to cancelling text selection */
5142 6559 return false;
5143 6560 } );
@@ -5173,13 +6590,13 b''
5173 6590 * @param {object} settings dataTables settings object
5174 6591 * @param {string} callbackArr Name of the array storage for the callbacks in
5175 6592 * oSettings
5176 * @param {string} event Name of the jQuery custom event to trigger. If null no
5177 * trigger is fired
6593 * @param {string} eventName Name of the jQuery custom event to trigger. If
6594 * null no trigger is fired
5178 6595 * @param {array} args Array of arguments to pass to the callback function /
5179 6596 * trigger
5180 6597 * @memberof DataTable#oApi
5181 6598 */
5182 function _fnCallbackFire( settings, callbackArr, e, args )
6599 function _fnCallbackFire( settings, callbackArr, eventName, args )
5183 6600 {
5184 6601 var ret = [];
5185 6602
@@ -5189,8 +6606,12 b''
5189 6606 } );
5190 6607 }
5191 6608
5192 if ( e !== null ) {
5193 $(settings.nTable).trigger( e+'.dt', args );
6609 if ( eventName !== null ) {
6610 var e = $.Event( eventName+'.dt' );
6611
6612 $(settings.nTable).trigger( e, args );
6613
6614 ret.push( e.result );
5194 6615 }
5195 6616
5196 6617 return ret;
@@ -5263,1257 +6684,6 b''
5263 6684 }
5264 6685
5265 6686
5266 DataTable = function( options )
5267 {
5268 /**
5269 * Perform a jQuery selector action on the table's TR elements (from the tbody) and
5270 * return the resulting jQuery object.
5271 * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on
5272 * @param {object} [oOpts] Optional parameters for modifying the rows to be included
5273 * @param {string} [oOpts.filter=none] Select TR elements that meet the current filter
5274 * criterion ("applied") or all TR elements (i.e. no filter).
5275 * @param {string} [oOpts.order=current] Order of the TR elements in the processed array.
5276 * Can be either 'current', whereby the current sorting of the table is used, or
5277 * 'original' whereby the original order the data was read into the table is used.
5278 * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page
5279 * ("current") or not ("all"). If 'current' is given, then order is assumed to be
5280 * 'current' and filter is 'applied', regardless of what they might be given as.
5281 * @returns {object} jQuery object, filtered by the given selector.
5282 * @dtopt API
5283 * @deprecated Since v1.10
5284 *
5285 * @example
5286 * $(document).ready(function() {
5287 * var oTable = $('#example').dataTable();
5288 *
5289 * // Highlight every second row
5290 * oTable.$('tr:odd').css('backgroundColor', 'blue');
5291 * } );
5292 *
5293 * @example
5294 * $(document).ready(function() {
5295 * var oTable = $('#example').dataTable();
5296 *
5297 * // Filter to rows with 'Webkit' in them, add a background colour and then
5298 * // remove the filter, thus highlighting the 'Webkit' rows only.
5299 * oTable.fnFilter('Webkit');
5300 * oTable.$('tr', {"search": "applied"}).css('backgroundColor', 'blue');
5301 * oTable.fnFilter('');
5302 * } );
5303 */
5304 this.$ = function ( sSelector, oOpts )
5305 {
5306 return this.api(true).$( sSelector, oOpts );
5307 };
5308
5309
5310 /**
5311 * Almost identical to $ in operation, but in this case returns the data for the matched
5312 * rows - as such, the jQuery selector used should match TR row nodes or TD/TH cell nodes
5313 * rather than any descendants, so the data can be obtained for the row/cell. If matching
5314 * rows are found, the data returned is the original data array/object that was used to
5315 * create the row (or a generated array if from a DOM source).
5316 *
5317 * This method is often useful in-combination with $ where both functions are given the
5318 * same parameters and the array indexes will match identically.
5319 * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on
5320 * @param {object} [oOpts] Optional parameters for modifying the rows to be included
5321 * @param {string} [oOpts.filter=none] Select elements that meet the current filter
5322 * criterion ("applied") or all elements (i.e. no filter).
5323 * @param {string} [oOpts.order=current] Order of the data in the processed array.
5324 * Can be either 'current', whereby the current sorting of the table is used, or
5325 * 'original' whereby the original order the data was read into the table is used.
5326 * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page
5327 * ("current") or not ("all"). If 'current' is given, then order is assumed to be
5328 * 'current' and filter is 'applied', regardless of what they might be given as.
5329 * @returns {array} Data for the matched elements. If any elements, as a result of the
5330 * selector, were not TR, TD or TH elements in the DataTable, they will have a null
5331 * entry in the array.
5332 * @dtopt API
5333 * @deprecated Since v1.10
5334 *
5335 * @example
5336 * $(document).ready(function() {
5337 * var oTable = $('#example').dataTable();
5338 *
5339 * // Get the data from the first row in the table
5340 * var data = oTable._('tr:first');
5341 *
5342 * // Do something useful with the data
5343 * alert( "First cell is: "+data[0] );
5344 * } );
5345 *
5346 * @example
5347 * $(document).ready(function() {
5348 * var oTable = $('#example').dataTable();
5349 *
5350 * // Filter to 'Webkit' and get all data for
5351 * oTable.fnFilter('Webkit');
5352 * var data = oTable._('tr', {"search": "applied"});
5353 *
5354 * // Do something with the data
5355 * alert( data.length+" rows matched the search" );
5356 * } );
5357 */
5358 this._ = function ( sSelector, oOpts )
5359 {
5360 return this.api(true).rows( sSelector, oOpts ).data();
5361 };
5362
5363
5364 /**
5365 * Create a DataTables Api instance, with the currently selected tables for
5366 * the Api's context.
5367 * @param {boolean} [traditional=false] Set the API instance's context to be
5368 * only the table referred to by the `DataTable.ext.iApiIndex` option, as was
5369 * used in the API presented by DataTables 1.9- (i.e. the traditional mode),
5370 * or if all tables captured in the jQuery object should be used.
5371 * @return {DataTables.Api}
5372 */
5373 this.api = function ( traditional )
5374 {
5375 return traditional ?
5376 new _Api(
5377 _fnSettingsFromNode( this[ _ext.iApiIndex ] )
5378 ) :
5379 new _Api( this );
5380 };
5381
5382
5383 /**
5384 * Add a single new row or multiple rows of data to the table. Please note
5385 * that this is suitable for client-side processing only - if you are using
5386 * server-side processing (i.e. "bServerSide": true), then to add data, you
5387 * must add it to the data source, i.e. the server-side, through an Ajax call.
5388 * @param {array|object} data The data to be added to the table. This can be:
5389 * <ul>
5390 * <li>1D array of data - add a single row with the data provided</li>
5391 * <li>2D array of arrays - add multiple rows in a single call</li>
5392 * <li>object - data object when using <i>mData</i></li>
5393 * <li>array of objects - multiple data objects when using <i>mData</i></li>
5394 * </ul>
5395 * @param {bool} [redraw=true] redraw the table or not
5396 * @returns {array} An array of integers, representing the list of indexes in
5397 * <i>aoData</i> ({@link DataTable.models.oSettings}) that have been added to
5398 * the table.
5399 * @dtopt API
5400 * @deprecated Since v1.10
5401 *
5402 * @example
5403 * // Global var for counter
5404 * var giCount = 2;
5405 *
5406 * $(document).ready(function() {
5407 * $('#example').dataTable();
5408 * } );
5409 *
5410 * function fnClickAddRow() {
5411 * $('#example').dataTable().fnAddData( [
5412 * giCount+".1",
5413 * giCount+".2",
5414 * giCount+".3",
5415 * giCount+".4" ]
5416 * );
5417 *
5418 * giCount++;
5419 * }
5420 */
5421 this.fnAddData = function( data, redraw )
5422 {
5423 var api = this.api( true );
5424
5425 /* Check if we want to add multiple rows or not */
5426 var rows = $.isArray(data) && ( $.isArray(data[0]) || $.isPlainObject(data[0]) ) ?
5427 api.rows.add( data ) :
5428 api.row.add( data );
5429
5430 if ( redraw === undefined || redraw ) {
5431 api.draw();
5432 }
5433
5434 return rows.flatten().toArray();
5435 };
5436
5437
5438 /**
5439 * This function will make DataTables recalculate the column sizes, based on the data
5440 * contained in the table and the sizes applied to the columns (in the DOM, CSS or
5441 * through the sWidth parameter). This can be useful when the width of the table's
5442 * parent element changes (for example a window resize).
5443 * @param {boolean} [bRedraw=true] Redraw the table or not, you will typically want to
5444 * @dtopt API
5445 * @deprecated Since v1.10
5446 *
5447 * @example
5448 * $(document).ready(function() {
5449 * var oTable = $('#example').dataTable( {
5450 * "sScrollY": "200px",
5451 * "bPaginate": false
5452 * } );
5453 *
5454 * $(window).bind('resize', function () {
5455 * oTable.fnAdjustColumnSizing();
5456 * } );
5457 * } );
5458 */
5459 this.fnAdjustColumnSizing = function ( bRedraw )
5460 {
5461 var api = this.api( true ).columns.adjust();
5462 var settings = api.settings()[0];
5463 var scroll = settings.oScroll;
5464
5465 if ( bRedraw === undefined || bRedraw ) {
5466 api.draw( false );
5467 }
5468 else if ( scroll.sX !== "" || scroll.sY !== "" ) {
5469 /* If not redrawing, but scrolling, we want to apply the new column sizes anyway */
5470 _fnScrollDraw( settings );
5471 }
5472 };
5473
5474
5475 /**
5476 * Quickly and simply clear a table
5477 * @param {bool} [bRedraw=true] redraw the table or not
5478 * @dtopt API
5479 * @deprecated Since v1.10
5480 *
5481 * @example
5482 * $(document).ready(function() {
5483 * var oTable = $('#example').dataTable();
5484 *
5485 * // Immediately 'nuke' the current rows (perhaps waiting for an Ajax callback...)
5486 * oTable.fnClearTable();
5487 * } );
5488 */
5489 this.fnClearTable = function( bRedraw )
5490 {
5491 var api = this.api( true ).clear();
5492
5493 if ( bRedraw === undefined || bRedraw ) {
5494 api.draw();
5495 }
5496 };
5497
5498
5499 /**
5500 * The exact opposite of 'opening' a row, this function will close any rows which
5501 * are currently 'open'.
5502 * @param {node} nTr the table row to 'close'
5503 * @returns {int} 0 on success, or 1 if failed (can't find the row)
5504 * @dtopt API
5505 * @deprecated Since v1.10
5506 *
5507 * @example
5508 * $(document).ready(function() {
5509 * var oTable;
5510 *
5511 * // 'open' an information row when a row is clicked on
5512 * $('#example tbody tr').click( function () {
5513 * if ( oTable.fnIsOpen(this) ) {
5514 * oTable.fnClose( this );
5515 * } else {
5516 * oTable.fnOpen( this, "Temporary row opened", "info_row" );
5517 * }
5518 * } );
5519 *
5520 * oTable = $('#example').dataTable();
5521 * } );
5522 */
5523 this.fnClose = function( nTr )
5524 {
5525 this.api( true ).row( nTr ).child.hide();
5526 };
5527
5528
5529 /**
5530 * Remove a row for the table
5531 * @param {mixed} target The index of the row from aoData to be deleted, or
5532 * the TR element you want to delete
5533 * @param {function|null} [callBack] Callback function
5534 * @param {bool} [redraw=true] Redraw the table or not
5535 * @returns {array} The row that was deleted
5536 * @dtopt API
5537 * @deprecated Since v1.10
5538 *
5539 * @example
5540 * $(document).ready(function() {
5541 * var oTable = $('#example').dataTable();
5542 *
5543 * // Immediately remove the first row
5544 * oTable.fnDeleteRow( 0 );
5545 * } );
5546 */
5547 this.fnDeleteRow = function( target, callback, redraw )
5548 {
5549 var api = this.api( true );
5550 var rows = api.rows( target );
5551 var settings = rows.settings()[0];
5552 var data = settings.aoData[ rows[0][0] ];
5553
5554 rows.remove();
5555
5556 if ( callback ) {
5557 callback.call( this, settings, data );
5558 }
5559
5560 if ( redraw === undefined || redraw ) {
5561 api.draw();
5562 }
5563
5564 return data;
5565 };
5566
5567
5568 /**
5569 * Restore the table to it's original state in the DOM by removing all of DataTables
5570 * enhancements, alterations to the DOM structure of the table and event listeners.
5571 * @param {boolean} [remove=false] Completely remove the table from the DOM
5572 * @dtopt API
5573 * @deprecated Since v1.10
5574 *
5575 * @example
5576 * $(document).ready(function() {
5577 * // This example is fairly pointless in reality, but shows how fnDestroy can be used
5578 * var oTable = $('#example').dataTable();
5579 * oTable.fnDestroy();
5580 * } );
5581 */
5582 this.fnDestroy = function ( remove )
5583 {
5584 this.api( true ).destroy( remove );
5585 };
5586
5587
5588 /**
5589 * Redraw the table
5590 * @param {bool} [complete=true] Re-filter and resort (if enabled) the table before the draw.
5591 * @dtopt API
5592 * @deprecated Since v1.10
5593 *
5594 * @example
5595 * $(document).ready(function() {
5596 * var oTable = $('#example').dataTable();
5597 *
5598 * // Re-draw the table - you wouldn't want to do it here, but it's an example :-)
5599 * oTable.fnDraw();
5600 * } );
5601 */
5602 this.fnDraw = function( complete )
5603 {
5604 // 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 );
5607 };
5608
5609
5610 /**
5611 * Filter the input based on data
5612 * @param {string} sInput String to filter the table on
5613 * @param {int|null} [iColumn] Column to limit filtering to
5614 * @param {bool} [bRegex=false] Treat as regular expression or not
5615 * @param {bool} [bSmart=true] Perform smart filtering or not
5616 * @param {bool} [bShowGlobal=true] Show the input global filter in it's input box(es)
5617 * @param {bool} [bCaseInsensitive=true] Do case-insensitive matching (true) or not (false)
5618 * @dtopt API
5619 * @deprecated Since v1.10
5620 *
5621 * @example
5622 * $(document).ready(function() {
5623 * var oTable = $('#example').dataTable();
5624 *
5625 * // Sometime later - filter...
5626 * oTable.fnFilter( 'test string' );
5627 * } );
5628 */
5629 this.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal, bCaseInsensitive )
5630 {
5631 var api = this.api( true );
5632
5633 if ( iColumn === null || iColumn === undefined ) {
5634 api.search( sInput, bRegex, bSmart, bCaseInsensitive );
5635 }
5636 else {
5637 api.column( iColumn ).search( sInput, bRegex, bSmart, bCaseInsensitive );
5638 }
5639
5640 api.draw();
5641 };
5642
5643
5644 /**
5645 * Get the data for the whole table, an individual row or an individual cell based on the
5646 * provided parameters.
5647 * @param {int|node} [src] A TR row node, TD/TH cell node or an integer. If given as
5648 * a TR node then the data source for the whole row will be returned. If given as a
5649 * TD/TH cell node then iCol will be automatically calculated and the data for the
5650 * cell returned. If given as an integer, then this is treated as the aoData internal
5651 * data index for the row (see fnGetPosition) and the data for that row used.
5652 * @param {int} [col] Optional column index that you want the data of.
5653 * @returns {array|object|string} If mRow is undefined, then the data for all rows is
5654 * returned. If mRow is defined, just data for that row, and is iCol is
5655 * defined, only data for the designated cell is returned.
5656 * @dtopt API
5657 * @deprecated Since v1.10
5658 *
5659 * @example
5660 * // Row data
5661 * $(document).ready(function() {
5662 * oTable = $('#example').dataTable();
5663 *
5664 * oTable.$('tr').click( function () {
5665 * var data = oTable.fnGetData( this );
5666 * // ... do something with the array / object of data for the row
5667 * } );
5668 * } );
5669 *
5670 * @example
5671 * // Individual cell data
5672 * $(document).ready(function() {
5673 * oTable = $('#example').dataTable();
5674 *
5675 * oTable.$('td').click( function () {
5676 * var sData = oTable.fnGetData( this );
5677 * alert( 'The cell clicked on had the value of '+sData );
5678 * } );
5679 * } );
5680 */
5681 this.fnGetData = function( src, col )
5682 {
5683 var api = this.api( true );
5684
5685 if ( src !== undefined ) {
5686 var type = src.nodeName ? src.nodeName.toLowerCase() : '';
5687
5688 return col !== undefined || type == 'td' || type == 'th' ?
5689 api.cell( src, col ).data() :
5690 api.row( src ).data() || null;
5691 }
5692
5693 return api.data().toArray();
5694 };
5695
5696
5697 /**
5698 * Get an array of the TR nodes that are used in the table's body. Note that you will
5699 * typically want to use the '$' API method in preference to this as it is more
5700 * flexible.
5701 * @param {int} [iRow] Optional row index for the TR element you want
5702 * @returns {array|node} If iRow is undefined, returns an array of all TR elements
5703 * in the table's body, or iRow is defined, just the TR element requested.
5704 * @dtopt API
5705 * @deprecated Since v1.10
5706 *
5707 * @example
5708 * $(document).ready(function() {
5709 * var oTable = $('#example').dataTable();
5710 *
5711 * // Get the nodes from the table
5712 * var nNodes = oTable.fnGetNodes( );
5713 * } );
5714 */
5715 this.fnGetNodes = function( iRow )
5716 {
5717 var api = this.api( true );
5718
5719 return iRow !== undefined ?
5720 api.row( iRow ).node() :
5721 api.rows().nodes().flatten().toArray();
5722 };
5723
5724
5725 /**
5726 * Get the array indexes of a particular cell from it's DOM element
5727 * and column index including hidden columns
5728 * @param {node} node this can either be a TR, TD or TH in the table's body
5729 * @returns {int} If nNode is given as a TR, then a single index is returned, or
5730 * if given as a cell, an array of [row index, column index (visible),
5731 * column index (all)] is given.
5732 * @dtopt API
5733 * @deprecated Since v1.10
5734 *
5735 * @example
5736 * $(document).ready(function() {
5737 * $('#example tbody td').click( function () {
5738 * // Get the position of the current data from the node
5739 * var aPos = oTable.fnGetPosition( this );
5740 *
5741 * // Get the data array for this row
5742 * var aData = oTable.fnGetData( aPos[0] );
5743 *
5744 * // Update the data array and return the value
5745 * aData[ aPos[1] ] = 'clicked';
5746 * this.innerHTML = 'clicked';
5747 * } );
5748 *
5749 * // Init DataTables
5750 * oTable = $('#example').dataTable();
5751 * } );
5752 */
5753 this.fnGetPosition = function( node )
5754 {
5755 var api = this.api( true );
5756 var nodeName = node.nodeName.toUpperCase();
5757
5758 if ( nodeName == 'TR' ) {
5759 return api.row( node ).index();
5760 }
5761 else if ( nodeName == 'TD' || nodeName == 'TH' ) {
5762 var cell = api.cell( node ).index();
5763
5764 return [
5765 cell.row,
5766 cell.columnVisible,
5767 cell.column
5768 ];
5769 }
5770 return null;
5771 };
5772
5773
5774 /**
5775 * Check to see if a row is 'open' or not.
5776 * @param {node} nTr the table row to check
5777 * @returns {boolean} true if the row is currently open, false otherwise
5778 * @dtopt API
5779 * @deprecated Since v1.10
5780 *
5781 * @example
5782 * $(document).ready(function() {
5783 * var oTable;
5784 *
5785 * // 'open' an information row when a row is clicked on
5786 * $('#example tbody tr').click( function () {
5787 * if ( oTable.fnIsOpen(this) ) {
5788 * oTable.fnClose( this );
5789 * } else {
5790 * oTable.fnOpen( this, "Temporary row opened", "info_row" );
5791 * }
5792 * } );
5793 *
5794 * oTable = $('#example').dataTable();
5795 * } );
5796 */
5797 this.fnIsOpen = function( nTr )
5798 {
5799 return this.api( true ).row( nTr ).child.isShown();
5800 };
5801
5802
5803 /**
5804 * This function will place a new row directly after a row which is currently
5805 * on display on the page, with the HTML contents that is passed into the
5806 * function. This can be used, for example, to ask for confirmation that a
5807 * particular record should be deleted.
5808 * @param {node} nTr The table row to 'open'
5809 * @param {string|node|jQuery} mHtml The HTML to put into the row
5810 * @param {string} sClass Class to give the new TD cell
5811 * @returns {node} The row opened. Note that if the table row passed in as the
5812 * first parameter, is not found in the table, this method will silently
5813 * return.
5814 * @dtopt API
5815 * @deprecated Since v1.10
5816 *
5817 * @example
5818 * $(document).ready(function() {
5819 * var oTable;
5820 *
5821 * // 'open' an information row when a row is clicked on
5822 * $('#example tbody tr').click( function () {
5823 * if ( oTable.fnIsOpen(this) ) {
5824 * oTable.fnClose( this );
5825 * } else {
5826 * oTable.fnOpen( this, "Temporary row opened", "info_row" );
5827 * }
5828 * } );
5829 *
5830 * oTable = $('#example').dataTable();
5831 * } );
5832 */
5833 this.fnOpen = function( nTr, mHtml, sClass )
5834 {
5835 return this.api( true )
5836 .row( nTr )
5837 .child( mHtml, sClass )
5838 .show()
5839 .child()[0];
5840 };
5841
5842
5843 /**
5844 * Change the pagination - provides the internal logic for pagination in a simple API
5845 * function. With this function you can have a DataTables table go to the next,
5846 * previous, first or last pages.
5847 * @param {string|int} mAction Paging action to take: "first", "previous", "next" or "last"
5848 * or page number to jump to (integer), note that page 0 is the first page.
5849 * @param {bool} [bRedraw=true] Redraw the table or not
5850 * @dtopt API
5851 * @deprecated Since v1.10
5852 *
5853 * @example
5854 * $(document).ready(function() {
5855 * var oTable = $('#example').dataTable();
5856 * oTable.fnPageChange( 'next' );
5857 * } );
5858 */
5859 this.fnPageChange = function ( mAction, bRedraw )
5860 {
5861 var api = this.api( true ).page( mAction );
5862
5863 if ( bRedraw === undefined || bRedraw ) {
5864 api.draw(false);
5865 }
5866 };
5867
5868
5869 /**
5870 * Show a particular column
5871 * @param {int} iCol The column whose display should be changed
5872 * @param {bool} bShow Show (true) or hide (false) the column
5873 * @param {bool} [bRedraw=true] Redraw the table or not
5874 * @dtopt API
5875 * @deprecated Since v1.10
5876 *
5877 * @example
5878 * $(document).ready(function() {
5879 * var oTable = $('#example').dataTable();
5880 *
5881 * // Hide the second column after initialisation
5882 * oTable.fnSetColumnVis( 1, false );
5883 * } );
5884 */
5885 this.fnSetColumnVis = function ( iCol, bShow, bRedraw )
5886 {
5887 var api = this.api( true ).column( iCol ).visible( bShow );
5888
5889 if ( bRedraw === undefined || bRedraw ) {
5890 api.columns.adjust().draw();
5891 }
5892 };
5893
5894
5895 /**
5896 * Get the settings for a particular table for external manipulation
5897 * @returns {object} DataTables settings object. See
5898 * {@link DataTable.models.oSettings}
5899 * @dtopt API
5900 * @deprecated Since v1.10
5901 *
5902 * @example
5903 * $(document).ready(function() {
5904 * var oTable = $('#example').dataTable();
5905 * var oSettings = oTable.fnSettings();
5906 *
5907 * // Show an example parameter from the settings
5908 * alert( oSettings._iDisplayStart );
5909 * } );
5910 */
5911 this.fnSettings = function()
5912 {
5913 return _fnSettingsFromNode( this[_ext.iApiIndex] );
5914 };
5915
5916
5917 /**
5918 * Sort the table by a particular column
5919 * @param {int} iCol the data index to sort on. Note that this will not match the
5920 * 'display index' if you have hidden data entries
5921 * @dtopt API
5922 * @deprecated Since v1.10
5923 *
5924 * @example
5925 * $(document).ready(function() {
5926 * var oTable = $('#example').dataTable();
5927 *
5928 * // Sort immediately with columns 0 and 1
5929 * oTable.fnSort( [ [0,'asc'], [1,'asc'] ] );
5930 * } );
5931 */
5932 this.fnSort = function( aaSort )
5933 {
5934 this.api( true ).order( aaSort ).draw();
5935 };
5936
5937
5938 /**
5939 * Attach a sort listener to an element for a given column
5940 * @param {node} nNode the element to attach the sort listener to
5941 * @param {int} iColumn the column that a click on this node will sort on
5942 * @param {function} [fnCallback] callback function when sort is run
5943 * @dtopt API
5944 * @deprecated Since v1.10
5945 *
5946 * @example
5947 * $(document).ready(function() {
5948 * var oTable = $('#example').dataTable();
5949 *
5950 * // Sort on column 1, when 'sorter' is clicked on
5951 * oTable.fnSortListener( document.getElementById('sorter'), 1 );
5952 * } );
5953 */
5954 this.fnSortListener = function( nNode, iColumn, fnCallback )
5955 {
5956 this.api( true ).order.listener( nNode, iColumn, fnCallback );
5957 };
5958
5959
5960 /**
5961 * Update a table cell or row - this method will accept either a single value to
5962 * update the cell with, an array of values with one element for each column or
5963 * an object in the same format as the original data source. The function is
5964 * self-referencing in order to make the multi column updates easier.
5965 * @param {object|array|string} mData Data to update the cell/row with
5966 * @param {node|int} mRow TR element you want to update or the aoData index
5967 * @param {int} [iColumn] The column to update, give as null or undefined to
5968 * update a whole row.
5969 * @param {bool} [bRedraw=true] Redraw the table or not
5970 * @param {bool} [bAction=true] Perform pre-draw actions or not
5971 * @returns {int} 0 on success, 1 on error
5972 * @dtopt API
5973 * @deprecated Since v1.10
5974 *
5975 * @example
5976 * $(document).ready(function() {
5977 * var oTable = $('#example').dataTable();
5978 * oTable.fnUpdate( 'Example update', 0, 0 ); // Single cell
5979 * oTable.fnUpdate( ['a', 'b', 'c', 'd', 'e'], $('tbody tr')[0] ); // Row
5980 * } );
5981 */
5982 this.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction )
5983 {
5984 var api = this.api( true );
5985
5986 if ( iColumn === undefined || iColumn === null ) {
5987 api.row( mRow ).data( mData );
5988 }
5989 else {
5990 api.cell( mRow, iColumn ).data( mData );
5991 }
5992
5993 if ( bAction === undefined || bAction ) {
5994 api.columns.adjust();
5995 }
5996
5997 if ( bRedraw === undefined || bRedraw ) {
5998 api.draw();
5999 }
6000 return 0;
6001 };
6002
6003
6004 /**
6005 * Provide a common method for plug-ins to check the version of DataTables being used, in order
6006 * to ensure compatibility.
6007 * @param {string} sVersion Version string to check for, in the format "X.Y.Z". Note that the
6008 * formats "X" and "X.Y" are also acceptable.
6009 * @returns {boolean} true if this version of DataTables is greater or equal to the required
6010 * version, or false if this version of DataTales is not suitable
6011 * @method
6012 * @dtopt API
6013 * @deprecated Since v1.10
6014 *
6015 * @example
6016 * $(document).ready(function() {
6017 * var oTable = $('#example').dataTable();
6018 * alert( oTable.fnVersionCheck( '1.9.0' ) );
6019 * } );
6020 */
6021 this.fnVersionCheck = _ext.fnVersionCheck;
6022
6023
6024 var _that = this;
6025 var emptyInit = options === undefined;
6026 var len = this.length;
6027
6028 if ( emptyInit ) {
6029 options = {};
6030 }
6031
6032 this.oApi = this.internal = _ext.internal;
6033
6034 // Extend with old style plug-in API methods
6035 for ( var fn in DataTable.ext.internal ) {
6036 if ( fn ) {
6037 this[fn] = _fnExternApiFunc(fn);
6038 }
6039 }
6040
6041 this.each(function() {
6042 // For each initialisation we want to give it a clean initialisation
6043 // object that can be bashed around
6044 var o = {};
6045 var oInit = len > 1 ? // optimisation for single table case
6046 _fnExtend( o, options, true ) :
6047 options;
6048
6049 /*global oInit,_that,emptyInit*/
6050 var i=0, iLen, j, jLen, k, kLen;
6051 var sId = this.getAttribute( 'id' );
6052 var bInitHandedOff = false;
6053 var defaults = DataTable.defaults;
6054
6055
6056 /* Sanity check */
6057 if ( this.nodeName.toLowerCase() != 'table' )
6058 {
6059 _fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 );
6060 return;
6061 }
6062
6063 /* Backwards compatibility for the defaults */
6064 _fnCompatOpts( defaults );
6065 _fnCompatCols( defaults.column );
6066
6067 /* Convert the camel-case defaults to Hungarian */
6068 _fnCamelToHungarian( defaults, defaults, true );
6069 _fnCamelToHungarian( defaults.column, defaults.column, true );
6070
6071 /* Setting up the initialisation object */
6072 _fnCamelToHungarian( defaults, oInit );
6073
6074 /* Check to see if we are re-initialising a table */
6075 var allSettings = DataTable.settings;
6076 for ( i=0, iLen=allSettings.length ; i<iLen ; i++ )
6077 {
6078 /* Base check on table node */
6079 if ( allSettings[i].nTable == this )
6080 {
6081 var bRetrieve = oInit.bRetrieve !== undefined ? oInit.bRetrieve : defaults.bRetrieve;
6082 var bDestroy = oInit.bDestroy !== undefined ? oInit.bDestroy : defaults.bDestroy;
6083
6084 if ( emptyInit || bRetrieve )
6085 {
6086 return allSettings[i].oInstance;
6087 }
6088 else if ( bDestroy )
6089 {
6090 allSettings[i].oInstance.fnDestroy();
6091 break;
6092 }
6093 else
6094 {
6095 _fnLog( allSettings[i], 0, 'Cannot reinitialise DataTable', 3 );
6096 return;
6097 }
6098 }
6099
6100 /* If the element we are initialising has the same ID as a table which was previously
6101 * initialised, but the table nodes don't match (from before) then we destroy the old
6102 * instance by simply deleting it. This is under the assumption that the table has been
6103 * destroyed by other methods. Anyone using non-id selectors will need to do this manually
6104 */
6105 if ( allSettings[i].sTableId == this.id )
6106 {
6107 allSettings.splice( i, 1 );
6108 break;
6109 }
6110 }
6111
6112 /* Ensure the table has an ID - required for accessibility */
6113 if ( sId === null || sId === "" )
6114 {
6115 sId = "DataTables_Table_"+(DataTable.ext._unique++);
6116 this.id = sId;
6117 }
6118
6119 /* Create the settings object for this table and set some of the default parameters */
6120 var oSettings = $.extend( true, {}, DataTable.models.oSettings, {
6121 "nTable": this,
6122 "oApi": _that.internal,
6123 "oInit": oInit,
6124 "sDestroyWidth": $(this)[0].style.width,
6125 "sInstance": sId,
6126 "sTableId": sId
6127 } );
6128 allSettings.push( oSettings );
6129
6130 // Need to add the instance after the instance after the settings object has been added
6131 // to the settings array, so we can self reference the table instance if more than one
6132 oSettings.oInstance = (_that.length===1) ? _that : $(this).dataTable();
6133
6134 // Backwards compatibility, before we apply all the defaults
6135 _fnCompatOpts( oInit );
6136
6137 if ( oInit.oLanguage )
6138 {
6139 _fnLanguageCompat( oInit.oLanguage );
6140 }
6141
6142 // If the length menu is given, but the init display length is not, use the length menu
6143 if ( oInit.aLengthMenu && ! oInit.iDisplayLength )
6144 {
6145 oInit.iDisplayLength = $.isArray( oInit.aLengthMenu[0] ) ?
6146 oInit.aLengthMenu[0][0] : oInit.aLengthMenu[0];
6147 }
6148
6149 // Apply the defaults and init options to make a single init object will all
6150 // options defined from defaults and instance options.
6151 oInit = _fnExtend( $.extend( true, {}, defaults ), oInit );
6152
6153
6154 // Map the initialisation options onto the settings object
6155 _fnMap( oSettings.oFeatures, oInit, [
6156 "bPaginate",
6157 "bLengthChange",
6158 "bFilter",
6159 "bSort",
6160 "bSortMulti",
6161 "bInfo",
6162 "bProcessing",
6163 "bAutoWidth",
6164 "bSortClasses",
6165 "bServerSide",
6166 "bDeferRender"
6167 ] );
6168 _fnMap( oSettings, oInit, [
6169 "asStripeClasses",
6170 "ajax",
6171 "fnServerData",
6172 "fnFormatNumber",
6173 "sServerMethod",
6174 "aaSorting",
6175 "aaSortingFixed",
6176 "aLengthMenu",
6177 "sPaginationType",
6178 "sAjaxSource",
6179 "sAjaxDataProp",
6180 "iStateDuration",
6181 "sDom",
6182 "bSortCellsTop",
6183 "iTabIndex",
6184 "fnStateLoadCallback",
6185 "fnStateSaveCallback",
6186 "renderer",
6187 "searchDelay",
6188 [ "iCookieDuration", "iStateDuration" ], // backwards compat
6189 [ "oSearch", "oPreviousSearch" ],
6190 [ "aoSearchCols", "aoPreSearchCols" ],
6191 [ "iDisplayLength", "_iDisplayLength" ],
6192 [ "bJQueryUI", "bJUI" ]
6193 ] );
6194 _fnMap( oSettings.oScroll, oInit, [
6195 [ "sScrollX", "sX" ],
6196 [ "sScrollXInner", "sXInner" ],
6197 [ "sScrollY", "sY" ],
6198 [ "bScrollCollapse", "bCollapse" ]
6199 ] );
6200 _fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" );
6201
6202 /* Callback functions which are array driven */
6203 _fnCallbackReg( oSettings, 'aoDrawCallback', oInit.fnDrawCallback, 'user' );
6204 _fnCallbackReg( oSettings, 'aoServerParams', oInit.fnServerParams, 'user' );
6205 _fnCallbackReg( oSettings, 'aoStateSaveParams', oInit.fnStateSaveParams, 'user' );
6206 _fnCallbackReg( oSettings, 'aoStateLoadParams', oInit.fnStateLoadParams, 'user' );
6207 _fnCallbackReg( oSettings, 'aoStateLoaded', oInit.fnStateLoaded, 'user' );
6208 _fnCallbackReg( oSettings, 'aoRowCallback', oInit.fnRowCallback, 'user' );
6209 _fnCallbackReg( oSettings, 'aoRowCreatedCallback', oInit.fnCreatedRow, 'user' );
6210 _fnCallbackReg( oSettings, 'aoHeaderCallback', oInit.fnHeaderCallback, 'user' );
6211 _fnCallbackReg( oSettings, 'aoFooterCallback', oInit.fnFooterCallback, 'user' );
6212 _fnCallbackReg( oSettings, 'aoInitComplete', oInit.fnInitComplete, 'user' );
6213 _fnCallbackReg( oSettings, 'aoPreDrawCallback', oInit.fnPreDrawCallback, 'user' );
6214
6215 var oClasses = oSettings.oClasses;
6216
6217 // @todo Remove in 1.11
6218 if ( oInit.bJQueryUI )
6219 {
6220 /* Use the JUI classes object for display. You could clone the oStdClasses object if
6221 * you want to have multiple tables with multiple independent classes
6222 */
6223 $.extend( oClasses, DataTable.ext.oJUIClasses, oInit.oClasses );
6224
6225 if ( oInit.sDom === defaults.sDom && defaults.sDom === "lfrtip" )
6226 {
6227 /* Set the DOM to use a layout suitable for jQuery UI's theming */
6228 oSettings.sDom = '<"H"lfr>t<"F"ip>';
6229 }
6230
6231 if ( ! oSettings.renderer ) {
6232 oSettings.renderer = 'jqueryui';
6233 }
6234 else if ( $.isPlainObject( oSettings.renderer ) && ! oSettings.renderer.header ) {
6235 oSettings.renderer.header = 'jqueryui';
6236 }
6237 }
6238 else
6239 {
6240 $.extend( oClasses, DataTable.ext.classes, oInit.oClasses );
6241 }
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
6253 if ( oSettings.iInitDisplayStart === undefined )
6254 {
6255 /* Display start point, taking into account the save saving */
6256 oSettings.iInitDisplayStart = oInit.iDisplayStart;
6257 oSettings._iDisplayStart = oInit.iDisplayStart;
6258 }
6259
6260 if ( oInit.iDeferLoading !== null )
6261 {
6262 oSettings.bDeferLoading = true;
6263 var tmp = $.isArray( oInit.iDeferLoading );
6264 oSettings._iRecordsDisplay = tmp ? oInit.iDeferLoading[0] : oInit.iDeferLoading;
6265 oSettings._iRecordsTotal = tmp ? oInit.iDeferLoading[1] : oInit.iDeferLoading;
6266 }
6267
6268 /* Language definitions */
6269 var oLanguage = oSettings.oLanguage;
6270 $.extend( true, oLanguage, oInit.oLanguage );
6271
6272 if ( oLanguage.sUrl !== "" )
6273 {
6274 /* Get the language definitions from a file - because this Ajax call makes the language
6275 * get async to the remainder of this function we use bInitHandedOff to indicate that
6276 * _fnInitialise will be fired by the returned Ajax handler, rather than the constructor
6277 */
6278 $.ajax( {
6279 dataType: 'json',
6280 url: oLanguage.sUrl,
6281 success: function ( json ) {
6282 _fnLanguageCompat( json );
6283 _fnCamelToHungarian( defaults.oLanguage, json );
6284 $.extend( true, oLanguage, json );
6285 _fnInitialise( oSettings );
6286 },
6287 error: function () {
6288 // Error occurred loading language file, continue on as best we can
6289 _fnInitialise( oSettings );
6290 }
6291 } );
6292 bInitHandedOff = true;
6293 }
6294
6295 /*
6296 * Stripes
6297 */
6298 if ( oInit.asStripeClasses === null )
6299 {
6300 oSettings.asStripeClasses =[
6301 oClasses.sStripeOdd,
6302 oClasses.sStripeEven
6303 ];
6304 }
6305
6306 /* Remove row stripe classes if they are already on the table row */
6307 var stripeClasses = oSettings.asStripeClasses;
6308 var rowOne = $('tbody tr:eq(0)', this);
6309 if ( $.inArray( true, $.map( stripeClasses, function(el, i) {
6310 return rowOne.hasClass(el);
6311 } ) ) !== -1 ) {
6312 $('tbody tr', this).removeClass( stripeClasses.join(' ') );
6313 oSettings.asDestroyStripes = stripeClasses.slice();
6314 }
6315
6316 /*
6317 * Columns
6318 * See if we should load columns automatically or use defined ones
6319 */
6320 var anThs = [];
6321 var aoColumnsInit;
6322 var nThead = this.getElementsByTagName('thead');
6323 if ( nThead.length !== 0 )
6324 {
6325 _fnDetectHeader( oSettings.aoHeader, nThead[0] );
6326 anThs = _fnGetUniqueThs( oSettings );
6327 }
6328
6329 /* If not given a column array, generate one with nulls */
6330 if ( oInit.aoColumns === null )
6331 {
6332 aoColumnsInit = [];
6333 for ( i=0, iLen=anThs.length ; i<iLen ; i++ )
6334 {
6335 aoColumnsInit.push( null );
6336 }
6337 }
6338 else
6339 {
6340 aoColumnsInit = oInit.aoColumns;
6341 }
6342
6343 /* Add the columns */
6344 for ( i=0, iLen=aoColumnsInit.length ; i<iLen ; i++ )
6345 {
6346 _fnAddColumn( oSettings, anThs ? anThs[i] : null );
6347 }
6348
6349 /* Apply the column definitions */
6350 _fnApplyColumnDefs( oSettings, oInit.aoColumnDefs, aoColumnsInit, function (iCol, oDef) {
6351 _fnColumnOptions( oSettings, iCol, oDef );
6352 } );
6353
6354 /* HTML5 attribute detection - build an mData object automatically if the
6355 * attributes are found
6356 */
6357 if ( rowOne.length ) {
6358 var a = function ( cell, name ) {
6359 return cell.getAttribute( 'data-'+name ) ? name : null;
6360 };
6361
6362 $.each( _fnGetRowElements( oSettings, rowOne[0] ).cells, function (i, cell) {
6363 var col = oSettings.aoColumns[i];
6364
6365 if ( col.mData === i ) {
6366 var sort = a( cell, 'sort' ) || a( cell, 'order' );
6367 var filter = a( cell, 'filter' ) || a( cell, 'search' );
6368
6369 if ( sort !== null || filter !== null ) {
6370 col.mData = {
6371 _: i+'.display',
6372 sort: sort !== null ? i+'.@data-'+sort : undefined,
6373 type: sort !== null ? i+'.@data-'+sort : undefined,
6374 filter: filter !== null ? i+'.@data-'+filter : undefined
6375 };
6376
6377 _fnColumnOptions( oSettings, i );
6378 }
6379 }
6380 } );
6381 }
6382
6383 var features = oSettings.oFeatures;
6384
6385 /* Must be done after everything which can be overridden by the state saving! */
6386 if ( oInit.bStateSave )
6387 {
6388 features.bStateSave = true;
6389 _fnLoadState( oSettings, oInit );
6390 _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 }
6512 } );
6513 _that = null;
6514 return this;
6515 };
6516
6517 6687
6518 6688
6519 6689 /**
@@ -6681,17 +6851,15 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 );
6854 if ( ! (this instanceof _Api) ) {
6855 return new _Api( context, data );
6688 6856 }
6689 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
@@ -6709,7 +6877,7 b''
6709 6877
6710 6878 // Initial data
6711 6879 if ( data ) {
6712 this.push.apply( this, data.toArray ? data.toArray() : data );
6880 $.merge( this, data );
6713 6881 }
6714 6882
6715 6883 // selector
@@ -6724,25 +6892,27 b''
6724 6892
6725 6893 DataTable.Api = _Api;
6726 6894
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 */
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 6905
6742 6906
6743 6907 context: [], // array of table settings objects
6744 6908
6745 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++ ) {
@@ -6803,7 +6973,6 b''
6803 6973 return -1;
6804 6974 },
6805 6975
6806 // Note that `alwaysNew` is internal - use iteratorNew externally
6807 6976 iterator: function ( flatten, type, fn, alwaysNew ) {
6808 6977 var
6809 6978 a = [], ret,
@@ -6971,13 +7140,13 b''
6971 7140
6972 7141
6973 7142 unshift: __arrayProto.unshift
6974 };
7143 } );
6975 7144
6976 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
@@ -7226,15 +7395,21 b''
7226 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 7415
@@ -7297,7 +7472,7 b''
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
@@ -7308,7 +7483,8 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 7490
@@ -7343,13 +7519,28 b''
7343 7519
7344 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
@@ -7362,16 +7553,6 b''
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 7557
7377 7558
@@ -7487,7 +7668,7 b''
7487 7668
7488 7669
7489 7670
7490 var _selector_run = function ( selector, select )
7671 var _selector_run = function ( type, selector, selectFn, settings, opts )
7491 7672 {
7492 7673 var
7493 7674 out = [], res,
@@ -7501,20 +7682,29 b''
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] );
7691 res = selectFn( typeof a[j] === 'string' ? $.trim(a[j]) : a[j] );
7510 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 7709
7520 7710
@@ -7526,15 +7716,15 b''
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 7722
7533 return {
7534 search: opts.search || 'none',
7535 order: opts.order || 'current',
7536 page: opts.page || 'all'
7537 };
7723 return $.extend( {
7724 search: 'none',
7725 order: 'current',
7726 page: 'all'
7727 }, opts );
7538 7728 };
7539 7729
7540 7730
@@ -7546,6 +7736,7 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
@@ -7632,7 +7823,8 b''
7632 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
@@ -7643,13 +7835,15 b''
7643 7835 return [ selInt ];
7644 7836 }
7645 7837
7646 var rows = _selector_row_indexes( settings, opts );
7838 if ( ! rows ) {
7839 rows = _selector_row_indexes( settings, opts );
7840 }
7647 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 }
@@ -7669,10 +7863,38 b''
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 }
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
7676 7898 }
7677 7899
7678 7900 // Selector - jQuery selector string, array of nodes or jQuery object/
@@ -7684,13 +7906,12 b''
7684 7906 return this._DT_RowIndex;
7685 7907 } )
7686 7908 .toArray();
7687 } );
7688 };
7689
7690
7691 /**
7692 *
7693 */
7909 };
7910
7911 return _selector_run( 'row', selector, run, settings, opts );
7912 };
7913
7914
7694 7915 _api_register( 'rows()', function ( selector, opts ) {
7695 7916 // argument shifting
7696 7917 if ( selector === undefined ) {
@@ -7714,7 +7935,6 b''
7714 7935 return inst;
7715 7936 } );
7716 7937
7717
7718 7938 _api_register( 'rows().nodes()', function () {
7719 7939 return this.iterator( 'row', function ( settings, row ) {
7720 7940 return settings.aoData[ row ].nTr || undefined;
@@ -7746,23 +7966,49 b''
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 7986
7752 return this.iterator( 'row', function ( settings, row, thatIdx ) {
7987 this.iterator( 'row', function ( settings, row, thatIdx ) {
7753 7988 var data = settings.aoData;
7989 var rowData = data[ row ];
7990 var i, ien, j, jen;
7991 var loopRow, loopCells;
7754 7992
7755 7993 data.splice( row, 1 );
7756 7994
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 );
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 }
7766 8012
7767 8013 // Delete from the display arrays
7768 8014 _fnDeleteIndex( settings.aiDisplayMaster, row );
@@ -7771,7 +8017,21 b''
7771 8017
7772 8018 // Check for an 'overflow' they case for displaying the table
7773 8019 _fnLengthOverflow( settings );
7774 } );
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 }
8026 } );
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;
7775 8035 } );
7776 8036
7777 8037
@@ -7797,7 +8057,7 b''
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() );
8060 $.merge( modRows, newRows );
7801 8061
7802 8062 return modRows;
7803 8063 } );
@@ -7868,6 +8128,14 b''
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' ) {
@@ -7885,17 +8153,10 b''
7885 8153 }
7886 8154 };
7887 8155
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 8156 addRow( data, klass );
7895 }
7896 8157
7897 8158 if ( row._details ) {
7898 row._details.remove();
8159 row._details.detach();
7899 8160 }
7900 8161
7901 8162 row._details = $(rows);
@@ -7914,7 +8175,7 b''
7914 8175 if ( ctx.length ) {
7915 8176 var row = ctx[0].aoData[ idx !== undefined ? idx : api[0] ];
7916 8177
7917 if ( row._details ) {
8178 if ( row && row._details ) {
7918 8179 row._details.remove();
7919 8180
7920 8181 row._detailsShow = undefined;
@@ -8096,7 +8357,7 b''
8096 8357 // can be an array of these items, comma separated list, or an array of comma
8097 8358 // separated lists
8098 8359
8099 var __re_column_selector = /^(.+):(name|visIdx|visible)$/;
8360 var __re_column_selector = /^([^:]+):(name|visIdx|visible)$/;
8100 8361
8101 8362
8102 8363 // r1 and r2 are redundant - but it means that the parameters match for the
@@ -8117,7 +8378,7 b''
8117 8378 names = _pluck( columns, 'sName' ),
8118 8379 nodes = _pluck( columns, 'nTh' );
8119 8380
8120 return _selector_run( selector, function ( s ) {
8381 var run = function ( s ) {
8121 8382 var selInt = _intVal( s );
8122 8383
8123 8384 // Selector - all
@@ -8172,22 +8433,42 b''
8172 8433 return $.map( names, function (name, i) {
8173 8434 return name === match[1] ? i : null;
8174 8435 } );
8175 }
8176 }
8177 else {
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
8178 8447 // jQuery selector on the TH elements for the columns
8179 return $( nodes )
8448 var jqResult = $( nodes )
8180 8449 .filter( s )
8181 8450 .map( function () {
8182 8451 return $.inArray( this, nodes ); // `nodes` is column index complete and in order
8183 8452 } )
8184 8453 .toArray();
8185 }
8186 } );
8187 };
8188
8189
8190 var __setColumnVis = function ( settings, column, vis, recalc ) {
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 );
8468 };
8469
8470
8471 var __setColumnVis = function ( settings, column, vis ) {
8191 8472 var
8192 8473 cols = settings.aoColumns,
8193 8474 col = cols[ column ],
@@ -8230,25 +8511,10 b''
8230 8511 _fnDrawHead( settings, settings.aoHeader );
8231 8512 _fnDrawHead( settings, settings.aoFooter );
8232 8513
8233 if ( recalc === undefined || recalc ) {
8234 // Automatically adjust column sizing
8235 _fnAdjustColumnSizing( settings );
8236
8237 // Realign columns for scrolling
8238 if ( settings.oScroll.sX || settings.oScroll.sY ) {
8239 _fnScrollDraw( settings );
8240 }
8241 }
8242
8243 _fnCallbackFire( settings, null, 'column-visibility', [settings, column, vis] );
8244
8245 8514 _fnSaveState( settings );
8246 8515 };
8247 8516
8248 8517
8249 /**
8250 *
8251 */
8252 8518 _api_register( 'columns()', function ( selector, opts ) {
8253 8519 // argument shifting
8254 8520 if ( selector === undefined ) {
@@ -8272,42 +8538,28 b''
8272 8538 return inst;
8273 8539 } );
8274 8540
8275
8276 /**
8277 *
8278 */
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 8546
8285
8286 /**
8287 *
8288 */
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 8552
8295
8296 /**
8297 *
8298 */
8299 8553 _api_registerPlural( 'columns().data()', 'column().data()', function () {
8300 8554 return this.iterator( 'column-rows', __columnData, 1 );
8301 8555 } );
8302 8556
8303
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 8562
8310
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,
@@ -8316,25 +8568,34 b''
8316 8568 }, 1 );
8317 8569 } );
8318 8570
8319
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 8576
8326
8327
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 );
8334 } );
8335 } );
8336
8337
8582 __setColumnVis( settings, column, vis );
8583 } );
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 } );
8338 8599
8339 8600 _api_registerPlural( 'columns().indexes()', 'column().index()', function ( type ) {
8340 8601 return this.iterator( 'column', function ( settings, column ) {
@@ -8344,28 +8605,12 b''
8344 8605 }, 1 );
8345 8606 } );
8346 8607
8347
8348 // _api_register( 'columns().show()', function () {
8349 // var selector = this.selector;
8350 // return this.columns( selector.cols, selector.opts ).visible( true );
8351 // } );
8352
8353
8354 // _api_register( 'columns().hide()', function () {
8355 // var selector = this.selector;
8356 // return this.columns( selector.cols, selector.opts ).visible( false );
8357 // } );
8358
8359
8360
8361 8608 _api_register( 'columns.adjust()', function () {
8362 8609 return this.iterator( 'table', function ( settings ) {
8363 8610 _fnAdjustColumnSizing( settings );
8364 8611 }, 1 );
8365 8612 } );
8366 8613
8367
8368 // Convert from one column index type, to another type
8369 8614 _api_register( 'column.index()', function ( type, idx ) {
8370 8615 if ( this.context.length !== 0 ) {
8371 8616 var ctx = this.context[0];
@@ -8379,14 +8624,12 b''
8379 8624 }
8380 8625 } );
8381 8626
8382
8383 8627 _api_register( 'column()', function ( selector, opts ) {
8384 8628 return _selector_first( this.columns( selector, opts ) );
8385 8629 } );
8386 8630
8387 8631
8388 8632
8389
8390 8633 var __cell_selector = function ( settings, selector, opts )
8391 8634 {
8392 8635 var data = settings.aoData;
@@ -8397,7 +8640,7 b''
8397 8640 var columns = settings.aoColumns.length;
8398 8641 var a, i, ien, j, o, host;
8399 8642
8400 return _selector_run( selector, function ( s ) {
8643 var run = function ( s ) {
8401 8644 var fnSelector = typeof s === 'function';
8402 8645
8403 8646 if ( s === null || s === undefined || fnSelector ) {
@@ -8415,9 +8658,9 b''
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 }
@@ -8437,18 +8680,33 b''
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 )
8686 return { // use a new object, in case someone changes the values
8687 row: el._DT_CellIndex.row,
8688 column: el._DT_CellIndex.column
8448 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 8711
8454 8712
@@ -8458,14 +8716,16 b''
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;
@@ -8511,9 +8771,10 b''
8511 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 } );
@@ -8629,7 +8890,7 b''
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 }
@@ -8658,6 +8919,24 b''
8658 8919 } );
8659 8920
8660 8921
8922 _api_register( 'order.fixed()', function ( set ) {
8923 if ( ! set ) {
8924 var ctx = this.context;
8925 var fixed = ctx.length ?
8926 ctx[0].aaSortingFixed :
8927 undefined;
8928
8929 return $.isArray( fixed ) ?
8930 { pre: fixed } :
8931 fixed;
8932 }
8933
8934 return this.iterator( 'table', function ( settings ) {
8935 settings.aaSortingFixed = $.extend( true, {}, set );
8936 } );
8937 } );
8938
8939
8661 8940 // Order by the selected column(s)
8662 8941 _api_register( [
8663 8942 'columns().order()',
@@ -8825,8 +9104,15 b''
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 } );
@@ -8853,43 +9139,22 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 } );
8861 };
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
9154
9155 return api ?
9156 new _Api( a ) :
9157 a;
8893 9158 };
8894 9159
8895 9160
@@ -8931,9 +9196,11 b''
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 }
9199 args[0] = $.map( args[0].split( /\s/ ), function ( e ) {
9200 return ! e.match(/\.dt\b/) ?
9201 e+'.dt' :
9202 e;
9203 } ).join( ' ' );
8937 9204
8938 9205 var inst = $( this.tables().nodes() );
8939 9206 inst[key].apply( inst, args );
@@ -8954,6 +9221,12 b''
8954 9221 } );
8955 9222
8956 9223
9224 _api_register( 'init()', function () {
9225 var ctx = this.context;
9226 return ctx.length ? ctx[0].oInit : null;
9227 } );
9228
9229
8957 9230 _api_register( 'data()', function () {
8958 9231 return this.iterator( 'table', function ( settings ) {
8959 9232 return _pluck( settings.aoData, '_aData' );
@@ -8992,8 +9265,8 b''
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);
9268 jqWrapper.off('.DT').find(':not(tbody *)').off('.DT');
9269 $(window).off('.DT-'+settings.sInstance);
8997 9270
8998 9271 // When scrolling we had to break the table up - restore it
8999 9272 if ( table != thead.parentNode ) {
@@ -9006,10 +9279,6 b''
9006 9279 jqTable.append( tfoot );
9007 9280 }
9008 9281
9009 // Remove the DataTables generated nodes, events and classes
9010 jqTable.detach();
9011 jqWrapper.detach();
9012
9013 9282 settings.aaSorting = [];
9014 9283 settings.aaSortingFixed = [];
9015 9284 _fnSortingClasses( settings );
@@ -9029,14 +9298,19 b''
9029 9298 } );
9030 9299 }
9031 9300
9301 // Add the TR elements back into the table in their original order
9302 jqTbody.children().detach();
9303 jqTbody.append( rows );
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
9032 9311 if ( ! remove && orig ) {
9033 9312 // insertBefore acts like appendChild if !arg[1]
9034 9313 orig.insertBefore( table, settings.nTableReinsertBefore );
9035 }
9036
9037 // Add the TR elements back into the table in their original order
9038 jqTbody.children().detach();
9039 jqTbody.append( rows );
9040 9314
9041 9315 // Restore the width of the original table - was read from the style property,
9042 9316 // so we can restore directly to that
@@ -9054,6 +9328,7 b''
9054 9328 $(this).addClass( settings.asDestroyStripes[i % ien] );
9055 9329 } );
9056 9330 }
9331 }
9057 9332
9058 9333 /* Remove the settings object from the settings array */
9059 9334 var idx = $.inArray( settings, DataTable.settings );
@@ -9064,6 +9339,55 b''
9064 9339 } );
9065 9340
9066 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 } );
9390
9067 9391 /**
9068 9392 * Version string for plug-ins to check compatibility. Allowed format is
9069 9393 * `a.b.c-d` where: a:int, b:int, c:int, d:string(dev|beta|alpha). `d` is used
@@ -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
@@ -9224,7 +9548,16 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 9562
9230 9563
@@ -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;
10594 * }
10595 * } );
10596 *
10597 * return o;
10923 * callback( json );
10924 * }
10925 * } );
10598 10926 * }
10599 10927 * } );
10600 10928 * } );
@@ -11533,14 +11861,15 b''
11533 11861
11534 11862
11535 11863 /**
11536 * DataTables features four different built-in options for the buttons to
11864 * DataTables features six different built-in options for the buttons to
11537 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
11871 * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus page numbers
11872 * * `first_last_numbers` - 'First' and 'Last' buttons, plus page numbers
11544 11873 *
11545 11874 * Further methods can be added using {@link DataTable.ext.oPagination}.
11546 11875 * @type string
@@ -11665,7 +11994,18 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 );
@@ -12764,7 +13104,21 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 13123
12770 13124
@@ -12810,6 +13164,13 b''
12810 13164 "aiDisplayMaster": [],
12811 13165
12812 13166 /**
13167 * Map of row ids to data indexes
13168 * @type object
13169 * @default {}
13170 */
13171 "aIds": {},
13172
13173 /**
12813 13174 * Store information about each column that is in use
12814 13175 * @type array
12815 13176 * @default []
@@ -13417,7 +13778,21 b''
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 /**
@@ -13447,6 +13822,17 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
@@ -13456,12 +13842,20 b''
13456 13842
13457 13843
13458 13844 /**
13845 * DataTables build type (expanded by the download builder)
13846 *
13847 * @type string
13848 */
13849 builder: "-source-",
13850
13851
13852 /**
13459 13853 * Error reporting.
13460 13854 *
13461 * How should DataTables report an error. Can take the value 'alert' or
13462 * 'throw'
13463 *
13464 * @type string
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",
@@ -13569,6 +13963,37 b''
13569 13963
13570 13964
13571 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
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
@@ -14061,7 +14486,7 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' );
@@ -14082,6 +14507,10 b''
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 },
@@ -14090,8 +14519,14 b''
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 14532
@@ -14101,6 +14536,7 b''
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 ) {
@@ -14118,13 +14554,12 b''
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 14561 case 'ellipsis':
14126 //RhodeCode fixed, added class paginate_button
14127 container.append('<span class=\"paginate_button\">&hellip;</span>');
14562 container.append('<span class="ellipsis">&#x2026;</span>');
14128 14563 break;
14129 14564
14130 14565 case 'first':
@@ -14158,10 +14593,11 b''
14158 14593 break;
14159 14594 }
14160 14595
14161 if ( btnDisplay ) {
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' ?
@@ -14184,21 +14620,23 b''
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');
14630 activeEl = $(host).find(document.activeElement).data('dt-idx');
14631 }
14632 catch (e) {}
14193 14633
14194 14634 attach( $(host).empty(), buttons );
14195 14635
14196 if ( activeEl !== null ) {
14636 if ( activeEl !== undefined ) {
14197 14637 $(host).find( '[data-dt-idx='+activeEl+']' ).focus();
14198 14638 }
14199 14639 }
14200 catch (e) {}
14201 }
14202 14640 }
14203 14641 } );
14204 14642
@@ -14218,10 +14656,10 b''
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);
@@ -14358,7 +14796,7 b''
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
@@ -14479,6 +14917,12 b''
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,11 +14950,25 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 ) );
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 );
14511 14972
14512 14973 var intPart = parseInt( d, 10 );
14513 14974 var floatPart = precision ?
@@ -14518,8 +14979,15 b''
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 };
@@ -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,
@@ -14655,6 +15121,9 b''
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