1/*! DataTables 1.13.5 2 * ©2008-2023 SpryMedia Ltd - datatables.net/license 3 */ 4 5/** 6 * @summary DataTables 7 * @description Paginate, search and order HTML tables 8 * @version 1.13.4 9 * @author SpryMedia Ltd 10 * @contact www.datatables.net 11 * @copyright SpryMedia Ltd. 12 * 13 * This source file is free software, available under the following license: 14 * MIT license - http://datatables.net/license 15 * 16 * This source file is distributed in the hope that it will be useful, but 17 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 18 * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details. 19 * 20 * For details please refer to: http://www.datatables.net 21 */ 22 23/*jslint evil: true, undef: true, browser: true */ 24/*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*/ 25 26(function( factory ) { 27 "use strict"; 28 29 if ( typeof define === 'function' && define.amd ) { 30 // AMD 31 define( ['jquery'], function ( $ ) { 32 return factory( $, window, document ); 33 } ); 34 } 35 else if ( typeof exports === 'object' ) { 36 // CommonJS 37 // jQuery's factory checks for a global window - if it isn't present then it 38 // returns a factory function that expects the window object 39 var jq = require('jquery'); 40 41 if (typeof window !== 'undefined') { 42 module.exports = function (root, $) { 43 if ( ! root ) { 44 // CommonJS environments without a window global must pass a 45 // root. This will give an error otherwise 46 root = window; 47 } 48 49 if ( ! $ ) { 50 $ = jq( root ); 51 } 52 53 return factory( $, root, root.document ); 54 }; 55 } 56 else { 57 return factory( jq, window, window.document ); 58 } 59 } 60 else { 61 // Browser 62 window.DataTable = factory( jQuery, window, document ); 63 } 64} 65(function( $, window, document, undefined ) { 66 "use strict"; 67 68 69 var DataTable = function ( selector, options ) 70 { 71 // Check if called with a window or jQuery object for DOM less applications 72 // This is for backwards compatibility 73 if (DataTable.factory(selector, options)) { 74 return DataTable; 75 } 76 77 // When creating with `new`, create a new DataTable, returning the API instance 78 if (this instanceof DataTable) { 79 return $(selector).DataTable(options); 80 } 81 else { 82 // Argument switching 83 options = selector; 84 } 85 86 /** 87 * Perform a jQuery selector action on the table's TR elements (from the tbody) and 88 * return the resulting jQuery object. 89 * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on 90 * @param {object} [oOpts] Optional parameters for modifying the rows to be included 91 * @param {string} [oOpts.filter=none] Select TR elements that meet the current filter 92 * criterion ("applied") or all TR elements (i.e. no filter). 93 * @param {string} [oOpts.order=current] Order of the TR elements in the processed array. 94 * Can be either 'current', whereby the current sorting of the table is used, or 95 * 'original' whereby the original order the data was read into the table is used. 96 * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page 97 * ("current") or not ("all"). If 'current' is given, then order is assumed to be 98 * 'current' and filter is 'applied', regardless of what they might be given as. 99 * @returns {object} jQuery object, filtered by the given selector. 100 * @dtopt API 101 * @deprecated Since v1.10 102 * 103 * @example 104 * $(document).ready(function() { 105 * var oTable = $('#example').dataTable(); 106 * 107 * // Highlight every second row 108 * oTable.$('tr:odd').css('backgroundColor', 'blue'); 109 * } ); 110 * 111 * @example 112 * $(document).ready(function() { 113 * var oTable = $('#example').dataTable(); 114 * 115 * // Filter to rows with 'Webkit' in them, add a background colour and then 116 * // remove the filter, thus highlighting the 'Webkit' rows only. 117 * oTable.fnFilter('Webkit'); 118 * oTable.$('tr', {"search": "applied"}).css('backgroundColor', 'blue'); 119 * oTable.fnFilter(''); 120 * } ); 121 */ 122 this.$ = function ( sSelector, oOpts ) 123 { 124 return this.api(true).$( sSelector, oOpts ); 125 }; 126 127 128 /** 129 * Almost identical to $ in operation, but in this case returns the data for the matched 130 * rows - as such, the jQuery selector used should match TR row nodes or TD/TH cell nodes 131 * rather than any descendants, so the data can be obtained for the row/cell. If matching 132 * rows are found, the data returned is the original data array/object that was used to 133 * create the row (or a generated array if from a DOM source). 134 * 135 * This method is often useful in-combination with $ where both functions are given the 136 * same parameters and the array indexes will match identically. 137 * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on 138 * @param {object} [oOpts] Optional parameters for modifying the rows to be included 139 * @param {string} [oOpts.filter=none] Select elements that meet the current filter 140 * criterion ("applied") or all elements (i.e. no filter). 141 * @param {string} [oOpts.order=current] Order of the data in the processed array. 142 * Can be either 'current', whereby the current sorting of the table is used, or 143 * 'original' whereby the original order the data was read into the table is used. 144 * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page 145 * ("current") or not ("all"). If 'current' is given, then order is assumed to be 146 * 'current' and filter is 'applied', regardless of what they might be given as. 147 * @returns {array} Data for the matched elements. If any elements, as a result of the 148 * selector, were not TR, TD or TH elements in the DataTable, they will have a null 149 * entry in the array. 150 * @dtopt API 151 * @deprecated Since v1.10 152 * 153 * @example 154 * $(document).ready(function() { 155 * var oTable = $('#example').dataTable(); 156 * 157 * // Get the data from the first row in the table 158 * var data = oTable._('tr:first'); 159 * 160 * // Do something useful with the data 161 * alert( "First cell is: "+data[0] ); 162 * } ); 163 * 164 * @example 165 * $(document).ready(function() { 166 * var oTable = $('#example').dataTable(); 167 * 168 * // Filter to 'Webkit' and get all data for 169 * oTable.fnFilter('Webkit'); 170 * var data = oTable._('tr', {"search": "applied"}); 171 * 172 * // Do something with the data 173 * alert( data.length+" rows matched the search" ); 174 * } ); 175 */ 176 this._ = function ( sSelector, oOpts ) 177 { 178 return this.api(true).rows( sSelector, oOpts ).data(); 179 }; 180 181 182 /** 183 * Create a DataTables Api instance, with the currently selected tables for 184 * the Api's context. 185 * @param {boolean} [traditional=false] Set the API instance's context to be 186 * only the table referred to by the `DataTable.ext.iApiIndex` option, as was 187 * used in the API presented by DataTables 1.9- (i.e. the traditional mode), 188 * or if all tables captured in the jQuery object should be used. 189 * @return {DataTables.Api} 190 */ 191 this.api = function ( traditional ) 192 { 193 return traditional ? 194 new _Api( 195 _fnSettingsFromNode( this[ _ext.iApiIndex ] ) 196 ) : 197 new _Api( this ); 198 }; 199 200 201 /** 202 * Add a single new row or multiple rows of data to the table. Please note 203 * that this is suitable for client-side processing only - if you are using 204 * server-side processing (i.e. "bServerSide": true), then to add data, you 205 * must add it to the data source, i.e. the server-side, through an Ajax call. 206 * @param {array|object} data The data to be added to the table. This can be: 207 * <ul> 208 * <li>1D array of data - add a single row with the data provided</li> 209 * <li>2D array of arrays - add multiple rows in a single call</li> 210 * <li>object - data object when using <i>mData</i></li> 211 * <li>array of objects - multiple data objects when using <i>mData</i></li> 212 * </ul> 213 * @param {bool} [redraw=true] redraw the table or not 214 * @returns {array} An array of integers, representing the list of indexes in 215 * <i>aoData</i> ({@link DataTable.models.oSettings}) that have been added to 216 * the table. 217 * @dtopt API 218 * @deprecated Since v1.10 219 * 220 * @example 221 * // Global var for counter 222 * var giCount = 2; 223 * 224 * $(document).ready(function() { 225 * $('#example').dataTable(); 226 * } ); 227 * 228 * function fnClickAddRow() { 229 * $('#example').dataTable().fnAddData( [ 230 * giCount+".1", 231 * giCount+".2", 232 * giCount+".3", 233 * giCount+".4" ] 234 * ); 235 * 236 * giCount++; 237 * } 238 */ 239 this.fnAddData = function( data, redraw ) 240 { 241 var api = this.api( true ); 242 243 /* Check if we want to add multiple rows or not */ 244 var rows = Array.isArray(data) && ( Array.isArray(data[0]) || $.isPlainObject(data[0]) ) ? 245 api.rows.add( data ) : 246 api.row.add( data ); 247 248 if ( redraw === undefined || redraw ) { 249 api.draw(); 250 } 251 252 return rows.flatten().toArray(); 253 }; 254 255 256 /** 257 * This function will make DataTables recalculate the column sizes, based on the data 258 * contained in the table and the sizes applied to the columns (in the DOM, CSS or 259 * through the sWidth parameter). This can be useful when the width of the table's 260 * parent element changes (for example a window resize). 261 * @param {boolean} [bRedraw=true] Redraw the table or not, you will typically want to 262 * @dtopt API 263 * @deprecated Since v1.10 264 * 265 * @example 266 * $(document).ready(function() { 267 * var oTable = $('#example').dataTable( { 268 * "sScrollY": "200px", 269 * "bPaginate": false 270 * } ); 271 * 272 * $(window).on('resize', function () { 273 * oTable.fnAdjustColumnSizing(); 274 * } ); 275 * } ); 276 */ 277 this.fnAdjustColumnSizing = function ( bRedraw ) 278 { 279 var api = this.api( true ).columns.adjust(); 280 var settings = api.settings()[0]; 281 var scroll = settings.oScroll; 282 283 if ( bRedraw === undefined || bRedraw ) { 284 api.draw( false ); 285 } 286 else if ( scroll.sX !== "" || scroll.sY !== "" ) { 287 /* If not redrawing, but scrolling, we want to apply the new column sizes anyway */ 288 _fnScrollDraw( settings ); 289 } 290 }; 291 292 293 /** 294 * Quickly and simply clear a table 295 * @param {bool} [bRedraw=true] redraw the table or not 296 * @dtopt API 297 * @deprecated Since v1.10 298 * 299 * @example 300 * $(document).ready(function() { 301 * var oTable = $('#example').dataTable(); 302 * 303 * // Immediately 'nuke' the current rows (perhaps waiting for an Ajax callback...) 304 * oTable.fnClearTable(); 305 * } ); 306 */ 307 this.fnClearTable = function( bRedraw ) 308 { 309 var api = this.api( true ).clear(); 310 311 if ( bRedraw === undefined || bRedraw ) { 312 api.draw(); 313 } 314 }; 315 316 317 /** 318 * The exact opposite of 'opening' a row, this function will close any rows which 319 * are currently 'open'. 320 * @param {node} nTr the table row to 'close' 321 * @returns {int} 0 on success, or 1 if failed (can't find the row) 322 * @dtopt API 323 * @deprecated Since v1.10 324 * 325 * @example 326 * $(document).ready(function() { 327 * var oTable; 328 * 329 * // 'open' an information row when a row is clicked on 330 * $('#example tbody tr').click( function () { 331 * if ( oTable.fnIsOpen(this) ) { 332 * oTable.fnClose( this ); 333 * } else { 334 * oTable.fnOpen( this, "Temporary row opened", "info_row" ); 335 * } 336 * } ); 337 * 338 * oTable = $('#example').dataTable(); 339 * } ); 340 */ 341 this.fnClose = function( nTr ) 342 { 343 this.api( true ).row( nTr ).child.hide(); 344 }; 345 346 347 /** 348 * Remove a row for the table 349 * @param {mixed} target The index of the row from aoData to be deleted, or 350 * the TR element you want to delete 351 * @param {function|null} [callBack] Callback function 352 * @param {bool} [redraw=true] Redraw the table or not 353 * @returns {array} The row that was deleted 354 * @dtopt API 355 * @deprecated Since v1.10 356 * 357 * @example 358 * $(document).ready(function() { 359 * var oTable = $('#example').dataTable(); 360 * 361 * // Immediately remove the first row 362 * oTable.fnDeleteRow( 0 ); 363 * } ); 364 */ 365 this.fnDeleteRow = function( target, callback, redraw ) 366 { 367 var api = this.api( true ); 368 var rows = api.rows( target ); 369 var settings = rows.settings()[0]; 370 var data = settings.aoData[ rows[0][0] ]; 371 372 rows.remove(); 373 374 if ( callback ) { 375 callback.call( this, settings, data ); 376 } 377 378 if ( redraw === undefined || redraw ) { 379 api.draw(); 380 } 381 382 return data; 383 }; 384 385 386 /** 387 * Restore the table to it's original state in the DOM by removing all of DataTables 388 * enhancements, alterations to the DOM structure of the table and event listeners. 389 * @param {boolean} [remove=false] Completely remove the table from the DOM 390 * @dtopt API 391 * @deprecated Since v1.10 392 * 393 * @example 394 * $(document).ready(function() { 395 * // This example is fairly pointless in reality, but shows how fnDestroy can be used 396 * var oTable = $('#example').dataTable(); 397 * oTable.fnDestroy(); 398 * } ); 399 */ 400 this.fnDestroy = function ( remove ) 401 { 402 this.api( true ).destroy( remove ); 403 }; 404 405 406 /** 407 * Redraw the table 408 * @param {bool} [complete=true] Re-filter and resort (if enabled) the table before the draw. 409 * @dtopt API 410 * @deprecated Since v1.10 411 * 412 * @example 413 * $(document).ready(function() { 414 * var oTable = $('#example').dataTable(); 415 * 416 * // Re-draw the table - you wouldn't want to do it here, but it's an example :-) 417 * oTable.fnDraw(); 418 * } ); 419 */ 420 this.fnDraw = function( complete ) 421 { 422 // Note that this isn't an exact match to the old call to _fnDraw - it takes 423 // into account the new data, but can hold position. 424 this.api( true ).draw( complete ); 425 }; 426 427 428 /** 429 * Filter the input based on data 430 * @param {string} sInput String to filter the table on 431 * @param {int|null} [iColumn] Column to limit filtering to 432 * @param {bool} [bRegex=false] Treat as regular expression or not 433 * @param {bool} [bSmart=true] Perform smart filtering or not 434 * @param {bool} [bShowGlobal=true] Show the input global filter in it's input box(es) 435 * @param {bool} [bCaseInsensitive=true] Do case-insensitive matching (true) or not (false) 436 * @dtopt API 437 * @deprecated Since v1.10 438 * 439 * @example 440 * $(document).ready(function() { 441 * var oTable = $('#example').dataTable(); 442 * 443 * // Sometime later - filter... 444 * oTable.fnFilter( 'test string' ); 445 * } ); 446 */ 447 this.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal, bCaseInsensitive ) 448 { 449 var api = this.api( true ); 450 451 if ( iColumn === null || iColumn === undefined ) { 452 api.search( sInput, bRegex, bSmart, bCaseInsensitive ); 453 } 454 else { 455 api.column( iColumn ).search( sInput, bRegex, bSmart, bCaseInsensitive ); 456 } 457 458 api.draw(); 459 }; 460 461 462 /** 463 * Get the data for the whole table, an individual row or an individual cell based on the 464 * provided parameters. 465 * @param {int|node} [src] A TR row node, TD/TH cell node or an integer. If given as 466 * a TR node then the data source for the whole row will be returned. If given as a 467 * TD/TH cell node then iCol will be automatically calculated and the data for the 468 * cell returned. If given as an integer, then this is treated as the aoData internal 469 * data index for the row (see fnGetPosition) and the data for that row used. 470 * @param {int} [col] Optional column index that you want the data of. 471 * @returns {array|object|string} If mRow is undefined, then the data for all rows is 472 * returned. If mRow is defined, just data for that row, and is iCol is 473 * defined, only data for the designated cell is returned. 474 * @dtopt API 475 * @deprecated Since v1.10 476 * 477 * @example 478 * // Row data 479 * $(document).ready(function() { 480 * oTable = $('#example').dataTable(); 481 * 482 * oTable.$('tr').click( function () { 483 * var data = oTable.fnGetData( this ); 484 * // ... do something with the array / object of data for the row 485 * } ); 486 * } ); 487 * 488 * @example 489 * // Individual cell data 490 * $(document).ready(function() { 491 * oTable = $('#example').dataTable(); 492 * 493 * oTable.$('td').click( function () { 494 * var sData = oTable.fnGetData( this ); 495 * alert( 'The cell clicked on had the value of '+sData ); 496 * } ); 497 * } ); 498 */ 499 this.fnGetData = function( src, col ) 500 { 501 var api = this.api( true ); 502 503 if ( src !== undefined ) { 504 var type = src.nodeName ? src.nodeName.toLowerCase() : ''; 505 506 return col !== undefined || type == 'td' || type == 'th' ? 507 api.cell( src, col ).data() : 508 api.row( src ).data() || null; 509 } 510 511 return api.data().toArray(); 512 }; 513 514 515 /** 516 * Get an array of the TR nodes that are used in the table's body. Note that you will 517 * typically want to use the '$' API method in preference to this as it is more 518 * flexible. 519 * @param {int} [iRow] Optional row index for the TR element you want 520 * @returns {array|node} If iRow is undefined, returns an array of all TR elements 521 * in the table's body, or iRow is defined, just the TR element requested. 522 * @dtopt API 523 * @deprecated Since v1.10 524 * 525 * @example 526 * $(document).ready(function() { 527 * var oTable = $('#example').dataTable(); 528 * 529 * // Get the nodes from the table 530 * var nNodes = oTable.fnGetNodes( ); 531 * } ); 532 */ 533 this.fnGetNodes = function( iRow ) 534 { 535 var api = this.api( true ); 536 537 return iRow !== undefined ? 538 api.row( iRow ).node() : 539 api.rows().nodes().flatten().toArray(); 540 }; 541 542 543 /** 544 * Get the array indexes of a particular cell from it's DOM element 545 * and column index including hidden columns 546 * @param {node} node this can either be a TR, TD or TH in the table's body 547 * @returns {int} If nNode is given as a TR, then a single index is returned, or 548 * if given as a cell, an array of [row index, column index (visible), 549 * column index (all)] is given. 550 * @dtopt API 551 * @deprecated Since v1.10 552 * 553 * @example 554 * $(document).ready(function() { 555 * $('#example tbody td').click( function () { 556 * // Get the position of the current data from the node 557 * var aPos = oTable.fnGetPosition( this ); 558 * 559 * // Get the data array for this row 560 * var aData = oTable.fnGetData( aPos[0] ); 561 * 562 * // Update the data array and return the value 563 * aData[ aPos[1] ] = 'clicked'; 564 * this.innerHTML = 'clicked'; 565 * } ); 566 * 567 * // Init DataTables 568 * oTable = $('#example').dataTable(); 569 * } ); 570 */ 571 this.fnGetPosition = function( node ) 572 { 573 var api = this.api( true ); 574 var nodeName = node.nodeName.toUpperCase(); 575 576 if ( nodeName == 'TR' ) { 577 return api.row( node ).index(); 578 } 579 else if ( nodeName == 'TD' || nodeName == 'TH' ) { 580 var cell = api.cell( node ).index(); 581 582 return [ 583 cell.row, 584 cell.columnVisible, 585 cell.column 586 ]; 587 } 588 return null; 589 }; 590 591 592 /** 593 * Check to see if a row is 'open' or not. 594 * @param {node} nTr the table row to check 595 * @returns {boolean} true if the row is currently open, false otherwise 596 * @dtopt API 597 * @deprecated Since v1.10 598 * 599 * @example 600 * $(document).ready(function() { 601 * var oTable; 602 * 603 * // 'open' an information row when a row is clicked on 604 * $('#example tbody tr').click( function () { 605 * if ( oTable.fnIsOpen(this) ) { 606 * oTable.fnClose( this ); 607 * } else { 608 * oTable.fnOpen( this, "Temporary row opened", "info_row" ); 609 * } 610 * } ); 611 * 612 * oTable = $('#example').dataTable(); 613 * } ); 614 */ 615 this.fnIsOpen = function( nTr ) 616 { 617 return this.api( true ).row( nTr ).child.isShown(); 618 }; 619 620 621 /** 622 * This function will place a new row directly after a row which is currently 623 * on display on the page, with the HTML contents that is passed into the 624 * function. This can be used, for example, to ask for confirmation that a 625 * particular record should be deleted. 626 * @param {node} nTr The table row to 'open' 627 * @param {string|node|jQuery} mHtml The HTML to put into the row 628 * @param {string} sClass Class to give the new TD cell 629 * @returns {node} The row opened. Note that if the table row passed in as the 630 * first parameter, is not found in the table, this method will silently 631 * return. 632 * @dtopt API 633 * @deprecated Since v1.10 634 * 635 * @example 636 * $(document).ready(function() { 637 * var oTable; 638 * 639 * // 'open' an information row when a row is clicked on 640 * $('#example tbody tr').click( function () { 641 * if ( oTable.fnIsOpen(this) ) { 642 * oTable.fnClose( this ); 643 * } else { 644 * oTable.fnOpen( this, "Temporary row opened", "info_row" ); 645 * } 646 * } ); 647 * 648 * oTable = $('#example').dataTable(); 649 * } ); 650 */ 651 this.fnOpen = function( nTr, mHtml, sClass ) 652 { 653 return this.api( true ) 654 .row( nTr ) 655 .child( mHtml, sClass ) 656 .show() 657 .child()[0]; 658 }; 659 660 661 /** 662 * Change the pagination - provides the internal logic for pagination in a simple API 663 * function. With this function you can have a DataTables table go to the next, 664 * previous, first or last pages. 665 * @param {string|int} mAction Paging action to take: "first", "previous", "next" or "last" 666 * or page number to jump to (integer), note that page 0 is the first page. 667 * @param {bool} [bRedraw=true] Redraw the table or not 668 * @dtopt API 669 * @deprecated Since v1.10 670 * 671 * @example 672 * $(document).ready(function() { 673 * var oTable = $('#example').dataTable(); 674 * oTable.fnPageChange( 'next' ); 675 * } ); 676 */ 677 this.fnPageChange = function ( mAction, bRedraw ) 678 { 679 var api = this.api( true ).page( mAction ); 680 681 if ( bRedraw === undefined || bRedraw ) { 682 api.draw(false); 683 } 684 }; 685 686 687 /** 688 * Show a particular column 689 * @param {int} iCol The column whose display should be changed 690 * @param {bool} bShow Show (true) or hide (false) the column 691 * @param {bool} [bRedraw=true] Redraw the table or not 692 * @dtopt API 693 * @deprecated Since v1.10 694 * 695 * @example 696 * $(document).ready(function() { 697 * var oTable = $('#example').dataTable(); 698 * 699 * // Hide the second column after initialisation 700 * oTable.fnSetColumnVis( 1, false ); 701 * } ); 702 */ 703 this.fnSetColumnVis = function ( iCol, bShow, bRedraw ) 704 { 705 var api = this.api( true ).column( iCol ).visible( bShow ); 706 707 if ( bRedraw === undefined || bRedraw ) { 708 api.columns.adjust().draw(); 709 } 710 }; 711 712 713 /** 714 * Get the settings for a particular table for external manipulation 715 * @returns {object} DataTables settings object. See 716 * {@link DataTable.models.oSettings} 717 * @dtopt API 718 * @deprecated Since v1.10 719 * 720 * @example 721 * $(document).ready(function() { 722 * var oTable = $('#example').dataTable(); 723 * var oSettings = oTable.fnSettings(); 724 * 725 * // Show an example parameter from the settings 726 * alert( oSettings._iDisplayStart ); 727 * } ); 728 */ 729 this.fnSettings = function() 730 { 731 return _fnSettingsFromNode( this[_ext.iApiIndex] ); 732 }; 733 734 735 /** 736 * Sort the table by a particular column 737 * @param {int} iCol the data index to sort on. Note that this will not match the 738 * 'display index' if you have hidden data entries 739 * @dtopt API 740 * @deprecated Since v1.10 741 * 742 * @example 743 * $(document).ready(function() { 744 * var oTable = $('#example').dataTable(); 745 * 746 * // Sort immediately with columns 0 and 1 747 * oTable.fnSort( [ [0,'asc'], [1,'asc'] ] ); 748 * } ); 749 */ 750 this.fnSort = function( aaSort ) 751 { 752 this.api( true ).order( aaSort ).draw(); 753 }; 754 755 756 /** 757 * Attach a sort listener to an element for a given column 758 * @param {node} nNode the element to attach the sort listener to 759 * @param {int} iColumn the column that a click on this node will sort on 760 * @param {function} [fnCallback] callback function when sort is run 761 * @dtopt API 762 * @deprecated Since v1.10 763 * 764 * @example 765 * $(document).ready(function() { 766 * var oTable = $('#example').dataTable(); 767 * 768 * // Sort on column 1, when 'sorter' is clicked on 769 * oTable.fnSortListener( document.getElementById('sorter'), 1 ); 770 * } ); 771 */ 772 this.fnSortListener = function( nNode, iColumn, fnCallback ) 773 { 774 this.api( true ).order.listener( nNode, iColumn, fnCallback ); 775 }; 776 777 778 /** 779 * Update a table cell or row - this method will accept either a single value to 780 * update the cell with, an array of values with one element for each column or 781 * an object in the same format as the original data source. The function is 782 * self-referencing in order to make the multi column updates easier. 783 * @param {object|array|string} mData Data to update the cell/row with 784 * @param {node|int} mRow TR element you want to update or the aoData index 785 * @param {int} [iColumn] The column to update, give as null or undefined to 786 * update a whole row. 787 * @param {bool} [bRedraw=true] Redraw the table or not 788 * @param {bool} [bAction=true] Perform pre-draw actions or not 789 * @returns {int} 0 on success, 1 on error 790 * @dtopt API 791 * @deprecated Since v1.10 792 * 793 * @example 794 * $(document).ready(function() { 795 * var oTable = $('#example').dataTable(); 796 * oTable.fnUpdate( 'Example update', 0, 0 ); // Single cell 797 * oTable.fnUpdate( ['a', 'b', 'c', 'd', 'e'], $('tbody tr')[0] ); // Row 798 * } ); 799 */ 800 this.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction ) 801 { 802 var api = this.api( true ); 803 804 if ( iColumn === undefined || iColumn === null ) { 805 api.row( mRow ).data( mData ); 806 } 807 else { 808 api.cell( mRow, iColumn ).data( mData ); 809 } 810 811 if ( bAction === undefined || bAction ) { 812 api.columns.adjust(); 813 } 814 815 if ( bRedraw === undefined || bRedraw ) { 816 api.draw(); 817 } 818 return 0; 819 }; 820 821 822 /** 823 * Provide a common method for plug-ins to check the version of DataTables being used, in order 824 * to ensure compatibility. 825 * @param {string} sVersion Version string to check for, in the format "X.Y.Z". Note that the 826 * formats "X" and "X.Y" are also acceptable. 827 * @returns {boolean} true if this version of DataTables is greater or equal to the required 828 * version, or false if this version of DataTales is not suitable 829 * @method 830 * @dtopt API 831 * @deprecated Since v1.10 832 * 833 * @example 834 * $(document).ready(function() { 835 * var oTable = $('#example').dataTable(); 836 * alert( oTable.fnVersionCheck( '1.9.0' ) ); 837 * } ); 838 */ 839 this.fnVersionCheck = _ext.fnVersionCheck; 840 841 842 var _that = this; 843 var emptyInit = options === undefined; 844 var len = this.length; 845 846 if ( emptyInit ) { 847 options = {}; 848 } 849 850 this.oApi = this.internal = _ext.internal; 851 852 // Extend with old style plug-in API methods 853 for ( var fn in DataTable.ext.internal ) { 854 if ( fn ) { 855 this[fn] = _fnExternApiFunc(fn); 856 } 857 } 858 859 this.each(function() { 860 // For each initialisation we want to give it a clean initialisation 861 // object that can be bashed around 862 var o = {}; 863 var oInit = len > 1 ? // optimisation for single table case 864 _fnExtend( o, options, true ) : 865 options; 866 867 /*global oInit,_that,emptyInit*/ 868 var i=0, iLen, j, jLen, k, kLen; 869 var sId = this.getAttribute( 'id' ); 870 var bInitHandedOff = false; 871 var defaults = DataTable.defaults; 872 var $this = $(this); 873 874 875 /* Sanity check */ 876 if ( this.nodeName.toLowerCase() != 'table' ) 877 { 878 _fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 ); 879 return; 880 } 881 882 /* Backwards compatibility for the defaults */ 883 _fnCompatOpts( defaults ); 884 _fnCompatCols( defaults.column ); 885 886 /* Convert the camel-case defaults to Hungarian */ 887 _fnCamelToHungarian( defaults, defaults, true ); 888 _fnCamelToHungarian( defaults.column, defaults.column, true ); 889 890 /* Setting up the initialisation object */ 891 _fnCamelToHungarian( defaults, $.extend( oInit, $this.data() ), true ); 892 893 894 895 /* Check to see if we are re-initialising a table */ 896 var allSettings = DataTable.settings; 897 for ( i=0, iLen=allSettings.length ; i<iLen ; i++ ) 898 { 899 var s = allSettings[i]; 900 901 /* Base check on table node */ 902 if ( 903 s.nTable == this || 904 (s.nTHead && s.nTHead.parentNode == this) || 905 (s.nTFoot && s.nTFoot.parentNode == this) 906 ) { 907 var bRetrieve = oInit.bRetrieve !== undefined ? oInit.bRetrieve : defaults.bRetrieve; 908 var bDestroy = oInit.bDestroy !== undefined ? oInit.bDestroy : defaults.bDestroy; 909 910 if ( emptyInit || bRetrieve ) 911 { 912 return s.oInstance; 913 } 914 else if ( bDestroy ) 915 { 916 s.oInstance.fnDestroy(); 917 break; 918 } 919 else 920 { 921 _fnLog( s, 0, 'Cannot reinitialise DataTable', 3 ); 922 return; 923 } 924 } 925 926 /* If the element we are initialising has the same ID as a table which was previously 927 * initialised, but the table nodes don't match (from before) then we destroy the old 928 * instance by simply deleting it. This is under the assumption that the table has been 929 * destroyed by other methods. Anyone using non-id selectors will need to do this manually 930 */ 931 if ( s.sTableId == this.id ) 932 { 933 allSettings.splice( i, 1 ); 934 break; 935 } 936 } 937 938 /* Ensure the table has an ID - required for accessibility */ 939 if ( sId === null || sId === "" ) 940 { 941 sId = "DataTables_Table_"+(DataTable.ext._unique++); 942 this.id = sId; 943 } 944 945 /* Create the settings object for this table and set some of the default parameters */ 946 var oSettings = $.extend( true, {}, DataTable.models.oSettings, { 947 "sDestroyWidth": $this[0].style.width, 948 "sInstance": sId, 949 "sTableId": sId 950 } ); 951 oSettings.nTable = this; 952 oSettings.oApi = _that.internal; 953 oSettings.oInit = oInit; 954 955 allSettings.push( oSettings ); 956 957 // Need to add the instance after the instance after the settings object has been added 958 // to the settings array, so we can self reference the table instance if more than one 959 oSettings.oInstance = (_that.length===1) ? _that : $this.dataTable(); 960 961 // Backwards compatibility, before we apply all the defaults 962 _fnCompatOpts( oInit ); 963 _fnLanguageCompat( oInit.oLanguage ); 964 965 // If the length menu is given, but the init display length is not, use the length menu 966 if ( oInit.aLengthMenu && ! oInit.iDisplayLength ) 967 { 968 oInit.iDisplayLength = Array.isArray( oInit.aLengthMenu[0] ) ? 969 oInit.aLengthMenu[0][0] : oInit.aLengthMenu[0]; 970 } 971 972 // Apply the defaults and init options to make a single init object will all 973 // options defined from defaults and instance options. 974 oInit = _fnExtend( $.extend( true, {}, defaults ), oInit ); 975 976 977 // Map the initialisation options onto the settings object 978 _fnMap( oSettings.oFeatures, oInit, [ 979 "bPaginate", 980 "bLengthChange", 981 "bFilter", 982 "bSort", 983 "bSortMulti", 984 "bInfo", 985 "bProcessing", 986 "bAutoWidth", 987 "bSortClasses", 988 "bServerSide", 989 "bDeferRender" 990 ] ); 991 _fnMap( oSettings, oInit, [ 992 "asStripeClasses", 993 "ajax", 994 "fnServerData", 995 "fnFormatNumber", 996 "sServerMethod", 997 "aaSorting", 998 "aaSortingFixed", 999 "aLengthMenu", 1000 "sPaginationType", 1001 "sAjaxSource", 1002 "sAjaxDataProp", 1003 "iStateDuration", 1004 "sDom", 1005 "bSortCellsTop", 1006 "iTabIndex", 1007 "fnStateLoadCallback", 1008 "fnStateSaveCallback", 1009 "renderer", 1010 "searchDelay", 1011 "rowId", 1012 [ "iCookieDuration", "iStateDuration" ], // backwards compat 1013 [ "oSearch", "oPreviousSearch" ], 1014 [ "aoSearchCols", "aoPreSearchCols" ], 1015 [ "iDisplayLength", "_iDisplayLength" ] 1016 ] ); 1017 _fnMap( oSettings.oScroll, oInit, [ 1018 [ "sScrollX", "sX" ], 1019 [ "sScrollXInner", "sXInner" ], 1020 [ "sScrollY", "sY" ], 1021 [ "bScrollCollapse", "bCollapse" ] 1022 ] ); 1023 _fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" ); 1024 1025 /* Callback functions which are array driven */ 1026 _fnCallbackReg( oSettings, 'aoDrawCallback', oInit.fnDrawCallback, 'user' ); 1027 _fnCallbackReg( oSettings, 'aoServerParams', oInit.fnServerParams, 'user' ); 1028 _fnCallbackReg( oSettings, 'aoStateSaveParams', oInit.fnStateSaveParams, 'user' ); 1029 _fnCallbackReg( oSettings, 'aoStateLoadParams', oInit.fnStateLoadParams, 'user' ); 1030 _fnCallbackReg( oSettings, 'aoStateLoaded', oInit.fnStateLoaded, 'user' ); 1031 _fnCallbackReg( oSettings, 'aoRowCallback', oInit.fnRowCallback, 'user' ); 1032 _fnCallbackReg( oSettings, 'aoRowCreatedCallback', oInit.fnCreatedRow, 'user' ); 1033 _fnCallbackReg( oSettings, 'aoHeaderCallback', oInit.fnHeaderCallback, 'user' ); 1034 _fnCallbackReg( oSettings, 'aoFooterCallback', oInit.fnFooterCallback, 'user' ); 1035 _fnCallbackReg( oSettings, 'aoInitComplete', oInit.fnInitComplete, 'user' ); 1036 _fnCallbackReg( oSettings, 'aoPreDrawCallback', oInit.fnPreDrawCallback, 'user' ); 1037 1038 oSettings.rowIdFn = _fnGetObjectDataFn( oInit.rowId ); 1039 1040 /* Browser support detection */ 1041 _fnBrowserDetect( oSettings ); 1042 1043 var oClasses = oSettings.oClasses; 1044 1045 $.extend( oClasses, DataTable.ext.classes, oInit.oClasses ); 1046 $this.addClass( oClasses.sTable ); 1047 1048 1049 if ( oSettings.iInitDisplayStart === undefined ) 1050 { 1051 /* Display start point, taking into account the save saving */ 1052 oSettings.iInitDisplayStart = oInit.iDisplayStart; 1053 oSettings._iDisplayStart = oInit.iDisplayStart; 1054 } 1055 1056 if ( oInit.iDeferLoading !== null ) 1057 { 1058 oSettings.bDeferLoading = true; 1059 var tmp = Array.isArray( oInit.iDeferLoading ); 1060 oSettings._iRecordsDisplay = tmp ? oInit.iDeferLoading[0] : oInit.iDeferLoading; 1061 oSettings._iRecordsTotal = tmp ? oInit.iDeferLoading[1] : oInit.iDeferLoading; 1062 } 1063 1064 /* Language definitions */ 1065 var oLanguage = oSettings.oLanguage; 1066 $.extend( true, oLanguage, oInit.oLanguage ); 1067 1068 if ( oLanguage.sUrl ) 1069 { 1070 /* Get the language definitions from a file - because this Ajax call makes the language 1071 * get async to the remainder of this function we use bInitHandedOff to indicate that 1072 * _fnInitialise will be fired by the returned Ajax handler, rather than the constructor 1073 */ 1074 $.ajax( { 1075 dataType: 'json', 1076 url: oLanguage.sUrl, 1077 success: function ( json ) { 1078 _fnCamelToHungarian( defaults.oLanguage, json ); 1079 _fnLanguageCompat( json ); 1080 $.extend( true, oLanguage, json, oSettings.oInit.oLanguage ); 1081 1082 _fnCallbackFire( oSettings, null, 'i18n', [oSettings]); 1083 _fnInitialise( oSettings ); 1084 }, 1085 error: function () { 1086 // Error occurred loading language file, continue on as best we can 1087 _fnInitialise( oSettings ); 1088 } 1089 } ); 1090 bInitHandedOff = true; 1091 } 1092 else { 1093 _fnCallbackFire( oSettings, null, 'i18n', [oSettings]); 1094 } 1095 1096 /* 1097 * Stripes 1098 */ 1099 if ( oInit.asStripeClasses === null ) 1100 { 1101 oSettings.asStripeClasses =[ 1102 oClasses.sStripeOdd, 1103 oClasses.sStripeEven 1104 ]; 1105 } 1106 1107 /* Remove row stripe classes if they are already on the table row */ 1108 var stripeClasses = oSettings.asStripeClasses; 1109 var rowOne = $this.children('tbody').find('tr').eq(0); 1110 if ( $.inArray( true, $.map( stripeClasses, function(el, i) { 1111 return rowOne.hasClass(el); 1112 } ) ) !== -1 ) { 1113 $('tbody tr', this).removeClass( stripeClasses.join(' ') ); 1114 oSettings.asDestroyStripes = stripeClasses.slice(); 1115 } 1116 1117 /* 1118 * Columns 1119 * See if we should load columns automatically or use defined ones 1120 */ 1121 var anThs = []; 1122 var aoColumnsInit; 1123 var nThead = this.getElementsByTagName('thead'); 1124 if ( nThead.length !== 0 ) 1125 { 1126 _fnDetectHeader( oSettings.aoHeader, nThead[0] ); 1127 anThs = _fnGetUniqueThs( oSettings ); 1128 } 1129 1130 /* If not given a column array, generate one with nulls */ 1131 if ( oInit.aoColumns === null ) 1132 { 1133 aoColumnsInit = []; 1134 for ( i=0, iLen=anThs.length ; i<iLen ; i++ ) 1135 { 1136 aoColumnsInit.push( null ); 1137 } 1138 } 1139 else 1140 { 1141 aoColumnsInit = oInit.aoColumns; 1142 } 1143 1144 /* Add the columns */ 1145 for ( i=0, iLen=aoColumnsInit.length ; i<iLen ; i++ ) 1146 { 1147 _fnAddColumn( oSettings, anThs ? anThs[i] : null ); 1148 } 1149 1150 /* Apply the column definitions */ 1151 _fnApplyColumnDefs( oSettings, oInit.aoColumnDefs, aoColumnsInit, function (iCol, oDef) { 1152 _fnColumnOptions( oSettings, iCol, oDef ); 1153 } ); 1154 1155 /* HTML5 attribute detection - build an mData object automatically if the 1156 * attributes are found 1157 */ 1158 if ( rowOne.length ) { 1159 var a = function ( cell, name ) { 1160 return cell.getAttribute( 'data-'+name ) !== null ? name : null; 1161 }; 1162 1163 $( rowOne[0] ).children('th, td').each( function (i, cell) { 1164 var col = oSettings.aoColumns[i]; 1165 1166 if (! col) { 1167 _fnLog( oSettings, 0, 'Incorrect column count', 18 ); 1168 } 1169 1170 if ( col.mData === i ) { 1171 var sort = a( cell, 'sort' ) || a( cell, 'order' ); 1172 var filter = a( cell, 'filter' ) || a( cell, 'search' ); 1173 1174 if ( sort !== null || filter !== null ) { 1175 col.mData = { 1176 _: i+'.display', 1177 sort: sort !== null ? i+'.@data-'+sort : undefined, 1178 type: sort !== null ? i+'.@data-'+sort : undefined, 1179 filter: filter !== null ? i+'.@data-'+filter : undefined 1180 }; 1181 col._isArrayHost = true; 1182 1183 _fnColumnOptions( oSettings, i ); 1184 } 1185 } 1186 } ); 1187 } 1188 1189 var features = oSettings.oFeatures; 1190 var loadedInit = function () { 1191 /* 1192 * Sorting 1193 * @todo For modularisation (1.11) this needs to do into a sort start up handler 1194 */ 1195 1196 // If aaSorting is not defined, then we use the first indicator in asSorting 1197 // in case that has been altered, so the default sort reflects that option 1198 if ( oInit.aaSorting === undefined ) { 1199 var sorting = oSettings.aaSorting; 1200 for ( i=0, iLen=sorting.length ; i<iLen ; i++ ) { 1201 sorting[i][1] = oSettings.aoColumns[ i ].asSorting[0]; 1202 } 1203 } 1204 1205 /* Do a first pass on the sorting classes (allows any size changes to be taken into 1206 * account, and also will apply sorting disabled classes if disabled 1207 */ 1208 _fnSortingClasses( oSettings ); 1209 1210 if ( features.bSort ) { 1211 _fnCallbackReg( oSettings, 'aoDrawCallback', function () { 1212 if ( oSettings.bSorted ) { 1213 var aSort = _fnSortFlatten( oSettings ); 1214 var sortedColumns = {}; 1215 1216 $.each( aSort, function (i, val) { 1217 sortedColumns[ val.src ] = val.dir; 1218 } ); 1219 1220 _fnCallbackFire( oSettings, null, 'order', [oSettings, aSort, sortedColumns] ); 1221 _fnSortAria( oSettings ); 1222 } 1223 } ); 1224 } 1225 1226 _fnCallbackReg( oSettings, 'aoDrawCallback', function () { 1227 if ( oSettings.bSorted || _fnDataSource( oSettings ) === 'ssp' || features.bDeferRender ) { 1228 _fnSortingClasses( oSettings ); 1229 } 1230 }, 'sc' ); 1231 1232 1233 /* 1234 * Final init 1235 * Cache the header, body and footer as required, creating them if needed 1236 */ 1237 1238 // Work around for Webkit bug 83867 - store the caption-side before removing from doc 1239 var captions = $this.children('caption').each( function () { 1240 this._captionSide = $(this).css('caption-side'); 1241 } ); 1242 1243 var thead = $this.children('thead'); 1244 if ( thead.length === 0 ) { 1245 thead = $('<thead/>').appendTo($this); 1246 } 1247 oSettings.nTHead = thead[0]; 1248 1249 var tbody = $this.children('tbody'); 1250 if ( tbody.length === 0 ) { 1251 tbody = $('<tbody/>').insertAfter(thead); 1252 } 1253 oSettings.nTBody = tbody[0]; 1254 1255 var tfoot = $this.children('tfoot'); 1256 if ( tfoot.length === 0 && captions.length > 0 && (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") ) { 1257 // If we are a scrolling table, and no footer has been given, then we need to create 1258 // a tfoot element for the caption element to be appended to 1259 tfoot = $('<tfoot/>').appendTo($this); 1260 } 1261 1262 if ( tfoot.length === 0 || tfoot.children().length === 0 ) { 1263 $this.addClass( oClasses.sNoFooter ); 1264 } 1265 else if ( tfoot.length > 0 ) { 1266 oSettings.nTFoot = tfoot[0]; 1267 _fnDetectHeader( oSettings.aoFooter, oSettings.nTFoot ); 1268 } 1269 1270 /* Check if there is data passing into the constructor */ 1271 if ( oInit.aaData ) { 1272 for ( i=0 ; i<oInit.aaData.length ; i++ ) { 1273 _fnAddData( oSettings, oInit.aaData[ i ] ); 1274 } 1275 } 1276 else if ( oSettings.bDeferLoading || _fnDataSource( oSettings ) == 'dom' ) { 1277 /* Grab the data from the page - only do this when deferred loading or no Ajax 1278 * source since there is no point in reading the DOM data if we are then going 1279 * to replace it with Ajax data 1280 */ 1281 _fnAddTr( oSettings, $(oSettings.nTBody).children('tr') ); 1282 } 1283 1284 /* Copy the data index array */ 1285 oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); 1286 1287 /* Initialisation complete - table can be drawn */ 1288 oSettings.bInitialised = true; 1289 1290 /* Check if we need to initialise the table (it might not have been handed off to the 1291 * language processor) 1292 */ 1293 if ( bInitHandedOff === false ) { 1294 _fnInitialise( oSettings ); 1295 } 1296 }; 1297 1298 /* Must be done after everything which can be overridden by the state saving! */ 1299 _fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState, 'state_save' ); 1300 1301 if ( oInit.bStateSave ) 1302 { 1303 features.bStateSave = true; 1304 _fnLoadState( oSettings, oInit, loadedInit ); 1305 } 1306 else { 1307 loadedInit(); 1308 } 1309 1310 } ); 1311 _that = null; 1312 return this; 1313 }; 1314 1315 1316 /* 1317 * It is useful to have variables which are scoped locally so only the 1318 * DataTables functions can access them and they don't leak into global space. 1319 * At the same time these functions are often useful over multiple files in the 1320 * core and API, so we list, or at least document, all variables which are used 1321 * by DataTables as private variables here. This also ensures that there is no 1322 * clashing of variable names and that they can easily referenced for reuse. 1323 */ 1324 1325 1326 // Defined else where 1327 // _selector_run 1328 // _selector_opts 1329 // _selector_first 1330 // _selector_row_indexes 1331 1332 var _ext; // DataTable.ext 1333 var _Api; // DataTable.Api 1334 var _api_register; // DataTable.Api.register 1335 var _api_registerPlural; // DataTable.Api.registerPlural 1336 1337 var _re_dic = {}; 1338 var _re_new_lines = /[\r\n\u2028]/g; 1339 var _re_html = /<.*?>/g; 1340 1341 // This is not strict ISO8601 - Date.parse() is quite lax, although 1342 // implementations differ between browsers. 1343 var _re_date = /^\d{2,4}[\.\/\-]\d{1,2}[\.\/\-]\d{1,2}([T ]{1}\d{1,2}[:\.]\d{2}([\.:]\d{2})?)?$/; 1344 1345 // Escape regular expression special characters 1346 var _re_escape_regex = new RegExp( '(\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ].join('|\\') + ')', 'g' ); 1347 1348 // http://en.wikipedia.org/wiki/Foreign_exchange_market 1349 // - \u20BD - Russian ruble. 1350 // - \u20a9 - South Korean Won 1351 // - \u20BA - Turkish Lira 1352 // - \u20B9 - Indian Rupee 1353 // - R - Brazil (R$) and South Africa 1354 // - fr - Swiss Franc 1355 // - kr - Swedish krona, Norwegian krone and Danish krone 1356 // - \u2009 is thin space and \u202F is narrow no-break space, both used in many 1357 // - Ƀ - Bitcoin 1358 // - Ξ - Ethereum 1359 // standards as thousands separators. 1360 var _re_formatted_numeric = /['\u00A0,$£€¥%\u2009\u202F\u20BD\u20a9\u20BArfkɃΞ]/gi; 1361 1362 1363 var _empty = function ( d ) { 1364 return !d || d === true || d === '-' ? true : false; 1365 }; 1366 1367 1368 var _intVal = function ( s ) { 1369 var integer = parseInt( s, 10 ); 1370 return !isNaN(integer) && isFinite(s) ? integer : null; 1371 }; 1372 1373 // Convert from a formatted number with characters other than `.` as the 1374 // decimal place, to a Javascript number 1375 var _numToDecimal = function ( num, decimalPoint ) { 1376 // Cache created regular expressions for speed as this function is called often 1377 if ( ! _re_dic[ decimalPoint ] ) { 1378 _re_dic[ decimalPoint ] = new RegExp( _fnEscapeRegex( decimalPoint ), 'g' ); 1379 } 1380 return typeof num === 'string' && decimalPoint !== '.' ? 1381 num.replace( /\./g, '' ).replace( _re_dic[ decimalPoint ], '.' ) : 1382 num; 1383 }; 1384 1385 1386 var _isNumber = function ( d, decimalPoint, formatted ) { 1387 var type = typeof d; 1388 var strType = type === 'string'; 1389 1390 if ( type === 'number' || type === 'bigint') { 1391 return true; 1392 } 1393 1394 // If empty return immediately so there must be a number if it is a 1395 // formatted string (this stops the string "k", or "kr", etc being detected 1396 // as a formatted number for currency 1397 if ( _empty( d ) ) { 1398 return true; 1399 } 1400 1401 if ( decimalPoint && strType ) { 1402 d = _numToDecimal( d, decimalPoint ); 1403 } 1404 1405 if ( formatted && strType ) { 1406 d = d.replace( _re_formatted_numeric, '' ); 1407 } 1408 1409 return !isNaN( parseFloat(d) ) && isFinite( d ); 1410 }; 1411 1412 1413 // A string without HTML in it can be considered to be HTML still 1414 var _isHtml = function ( d ) { 1415 return _empty( d ) || typeof d === 'string'; 1416 }; 1417 1418 1419 var _htmlNumeric = function ( d, decimalPoint, formatted ) { 1420 if ( _empty( d ) ) { 1421 return true; 1422 } 1423 1424 var html = _isHtml( d ); 1425 return ! html ? 1426 null : 1427 _isNumber( _stripHtml( d ), decimalPoint, formatted ) ? 1428 true : 1429 null; 1430 }; 1431 1432 1433 var _pluck = function ( a, prop, prop2 ) { 1434 var out = []; 1435 var i=0, ien=a.length; 1436 1437 // Could have the test in the loop for slightly smaller code, but speed 1438 // is essential here 1439 if ( prop2 !== undefined ) { 1440 for ( ; i<ien ; i++ ) { 1441 if ( a[i] && a[i][ prop ] ) { 1442 out.push( a[i][ prop ][ prop2 ] ); 1443 } 1444 } 1445 } 1446 else { 1447 for ( ; i<ien ; i++ ) { 1448 if ( a[i] ) { 1449 out.push( a[i][ prop ] ); 1450 } 1451 } 1452 } 1453 1454 return out; 1455 }; 1456 1457 1458 // Basically the same as _pluck, but rather than looping over `a` we use `order` 1459 // as the indexes to pick from `a` 1460 var _pluck_order = function ( a, order, prop, prop2 ) 1461 { 1462 var out = []; 1463 var i=0, ien=order.length; 1464 1465 // Could have the test in the loop for slightly smaller code, but speed 1466 // is essential here 1467 if ( prop2 !== undefined ) { 1468 for ( ; i<ien ; i++ ) { 1469 if ( a[ order[i] ][ prop ] ) { 1470 out.push( a[ order[i] ][ prop ][ prop2 ] ); 1471 } 1472 } 1473 } 1474 else { 1475 for ( ; i<ien ; i++ ) { 1476 out.push( a[ order[i] ][ prop ] ); 1477 } 1478 } 1479 1480 return out; 1481 }; 1482 1483 1484 var _range = function ( len, start ) 1485 { 1486 var out = []; 1487 var end; 1488 1489 if ( start === undefined ) { 1490 start = 0; 1491 end = len; 1492 } 1493 else { 1494 end = start; 1495 start = len; 1496 } 1497 1498 for ( var i=start ; i<end ; i++ ) { 1499 out.push( i ); 1500 } 1501 1502 return out; 1503 }; 1504 1505 1506 var _removeEmpty = function ( a ) 1507 { 1508 var out = []; 1509 1510 for ( var i=0, ien=a.length ; i<ien ; i++ ) { 1511 if ( a[i] ) { // careful - will remove all falsy values! 1512 out.push( a[i] ); 1513 } 1514 } 1515 1516 return out; 1517 }; 1518 1519 1520 var _stripHtml = function ( d ) { 1521 return d 1522 .replace( _re_html, '' ) // Complete tags 1523 .replace(/<script/i, ''); // Safety for incomplete script tag 1524 }; 1525 1526 1527 /** 1528 * Determine if all values in the array are unique. This means we can short 1529 * cut the _unique method at the cost of a single loop. A sorted array is used 1530 * to easily check the values. 1531 * 1532 * @param {array} src Source array 1533 * @return {boolean} true if all unique, false otherwise 1534 * @ignore 1535 */ 1536 var _areAllUnique = function ( src ) { 1537 if ( src.length < 2 ) { 1538 return true; 1539 } 1540 1541 var sorted = src.slice().sort(); 1542 var last = sorted[0]; 1543 1544 for ( var i=1, ien=sorted.length ; i<ien ; i++ ) { 1545 if ( sorted[i] === last ) { 1546 return false; 1547 } 1548 1549 last = sorted[i]; 1550 } 1551 1552 return true; 1553 }; 1554 1555 1556 /** 1557 * Find the unique elements in a source array. 1558 * 1559 * @param {array} src Source array 1560 * @return {array} Array of unique items 1561 * @ignore 1562 */ 1563 var _unique = function ( src ) 1564 { 1565 if ( _areAllUnique( src ) ) { 1566 return src.slice(); 1567 } 1568 1569 // A faster unique method is to use object keys to identify used values, 1570 // but this doesn't work with arrays or objects, which we must also 1571 // consider. See jsperf.com/compare-array-unique-versions/4 for more 1572 // information. 1573 var 1574 out = [], 1575 val, 1576 i, ien=src.length, 1577 j, k=0; 1578 1579 again: for ( i=0 ; i<ien ; i++ ) { 1580 val = src[i]; 1581 1582 for ( j=0 ; j<k ; j++ ) { 1583 if ( out[j] === val ) { 1584 continue again; 1585 } 1586 } 1587 1588 out.push( val ); 1589 k++; 1590 } 1591 1592 return out; 1593 }; 1594 1595 // Surprisingly this is faster than [].concat.apply 1596 // https://jsperf.com/flatten-an-array-loop-vs-reduce/2 1597 var _flatten = function (out, val) { 1598 if (Array.isArray(val)) { 1599 for (var i=0 ; i<val.length ; i++) { 1600 _flatten(out, val[i]); 1601 } 1602 } 1603 else { 1604 out.push(val); 1605 } 1606 1607 return out; 1608 } 1609 1610 var _includes = function (search, start) { 1611 if (start === undefined) { 1612 start = 0; 1613 } 1614 1615 return this.indexOf(search, start) !== -1; 1616 }; 1617 1618 // Array.isArray polyfill. 1619 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray 1620 if (! Array.isArray) { 1621 Array.isArray = function(arg) { 1622 return Object.prototype.toString.call(arg) === '[object Array]'; 1623 }; 1624 } 1625 1626 if (! Array.prototype.includes) { 1627 Array.prototype.includes = _includes; 1628 } 1629 1630 // .trim() polyfill 1631 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trim 1632 if (!String.prototype.trim) { 1633 String.prototype.trim = function () { 1634 return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''); 1635 }; 1636 } 1637 1638 if (! String.prototype.includes) { 1639 String.prototype.includes = _includes; 1640 } 1641 1642 /** 1643 * DataTables utility methods 1644 * 1645 * This namespace provides helper methods that DataTables uses internally to 1646 * create a DataTable, but which are not exclusively used only for DataTables. 1647 * These methods can be used by extension authors to save the duplication of 1648 * code. 1649 * 1650 * @namespace 1651 */ 1652 DataTable.util = { 1653 /** 1654 * Throttle the calls to a function. Arguments and context are maintained 1655 * for the throttled function. 1656 * 1657 * @param {function} fn Function to be called 1658 * @param {integer} freq Call frequency in mS 1659 * @return {function} Wrapped function 1660 */ 1661 throttle: function ( fn, freq ) { 1662 var 1663 frequency = freq !== undefined ? freq : 200, 1664 last, 1665 timer; 1666 1667 return function () { 1668 var 1669 that = this, 1670 now = +new Date(), 1671 args = arguments; 1672 1673 if ( last && now < last + frequency ) { 1674 clearTimeout( timer ); 1675 1676 timer = setTimeout( function () { 1677 last = undefined; 1678 fn.apply( that, args ); 1679 }, frequency ); 1680 } 1681 else { 1682 last = now; 1683 fn.apply( that, args ); 1684 } 1685 }; 1686 }, 1687 1688 1689 /** 1690 * Escape a string such that it can be used in a regular expression 1691 * 1692 * @param {string} val string to escape 1693 * @returns {string} escaped string 1694 */ 1695 escapeRegex: function ( val ) { 1696 return val.replace( _re_escape_regex, '\\$1' ); 1697 }, 1698 1699 /** 1700 * Create a function that will write to a nested object or array 1701 * @param {*} source JSON notation string 1702 * @returns Write function 1703 */ 1704 set: function ( source ) { 1705 if ( $.isPlainObject( source ) ) { 1706 /* Unlike get, only the underscore (global) option is used for for 1707 * setting data since we don't know the type here. This is why an object 1708 * option is not documented for `mData` (which is read/write), but it is 1709 * for `mRender` which is read only. 1710 */ 1711 return DataTable.util.set( source._ ); 1712 } 1713 else if ( source === null ) { 1714 // Nothing to do when the data source is null 1715 return function () {}; 1716 } 1717 else if ( typeof source === 'function' ) { 1718 return function (data, val, meta) { 1719 source( data, 'set', val, meta ); 1720 }; 1721 } 1722 else if ( typeof source === 'string' && (source.indexOf('.') !== -1 || 1723 source.indexOf('[') !== -1 || source.indexOf('(') !== -1) ) 1724 { 1725 // Like the get, we need to get data from a nested object 1726 var setData = function (data, val, src) { 1727 var a = _fnSplitObjNotation( src ), b; 1728 var aLast = a[a.length-1]; 1729 var arrayNotation, funcNotation, o, innerSrc; 1730 1731 for ( var i=0, iLen=a.length-1 ; i<iLen ; i++ ) { 1732 // Protect against prototype pollution 1733 if (a[i] === '__proto__' || a[i] === 'constructor') { 1734 throw new Error('Cannot set prototype values'); 1735 } 1736 1737 // Check if we are dealing with an array notation request 1738 arrayNotation = a[i].match(__reArray); 1739 funcNotation = a[i].match(__reFn); 1740 1741 if ( arrayNotation ) { 1742 a[i] = a[i].replace(__reArray, ''); 1743 data[ a[i] ] = []; 1744 1745 // Get the remainder of the nested object to set so we can recurse 1746 b = a.slice(); 1747 b.splice( 0, i+1 ); 1748 innerSrc = b.join('.'); 1749 1750 // Traverse each entry in the array setting the properties requested 1751 if ( Array.isArray( val ) ) { 1752 for ( var j=0, jLen=val.length ; j<jLen ; j++ ) { 1753 o = {}; 1754 setData( o, val[j], innerSrc ); 1755 data[ a[i] ].push( o ); 1756 } 1757 } 1758 else { 1759 // We've been asked to save data to an array, but it 1760 // isn't array data to be saved. Best that can be done 1761 // is to just save the value. 1762 data[ a[i] ] = val; 1763 } 1764 1765 // The inner call to setData has already traversed through the remainder 1766 // of the source and has set the data, thus we can exit here 1767 return; 1768 } 1769 else if ( funcNotation ) { 1770 // Function call 1771 a[i] = a[i].replace(__reFn, ''); 1772 data = data[ a[i] ]( val ); 1773 } 1774 1775 // If the nested object doesn't currently exist - since we are 1776 // trying to set the value - create it 1777 if ( data[ a[i] ] === null || data[ a[i] ] === undefined ) { 1778 data[ a[i] ] = {}; 1779 } 1780 data = data[ a[i] ]; 1781 } 1782 1783 // Last item in the input - i.e, the actual set 1784 if ( aLast.match(__reFn ) ) { 1785 // Function call 1786 data = data[ aLast.replace(__reFn, '') ]( val ); 1787 } 1788 else { 1789 // If array notation is used, we just want to strip it and use the property name 1790 // and assign the value. If it isn't used, then we get the result we want anyway 1791 data[ aLast.replace(__reArray, '') ] = val; 1792 } 1793 }; 1794 1795 return function (data, val) { // meta is also passed in, but not used 1796 return setData( data, val, source ); 1797 }; 1798 } 1799 else { 1800 // Array or flat object mapping 1801 return function (data, val) { // meta is also passed in, but not used 1802 data[source] = val; 1803 }; 1804 } 1805 }, 1806 1807 /** 1808 * Create a function that will read nested objects from arrays, based on JSON notation 1809 * @param {*} source JSON notation string 1810 * @returns Value read 1811 */ 1812 get: function ( source ) { 1813 if ( $.isPlainObject( source ) ) { 1814 // Build an object of get functions, and wrap them in a single call 1815 var o = {}; 1816 $.each( source, function (key, val) { 1817 if ( val ) { 1818 o[key] = DataTable.util.get( val ); 1819 } 1820 } ); 1821 1822 return function (data, type, row, meta) { 1823 var t = o[type] || o._; 1824 return t !== undefined ? 1825 t(data, type, row, meta) : 1826 data; 1827 }; 1828 } 1829 else if ( source === null ) { 1830 // Give an empty string for rendering / sorting etc 1831 return function (data) { // type, row and meta also passed, but not used 1832 return data; 1833 }; 1834 } 1835 else if ( typeof source === 'function' ) { 1836 return function (data, type, row, meta) { 1837 return source( data, type, row, meta ); 1838 }; 1839 } 1840 else if ( typeof source === 'string' && (source.indexOf('.') !== -1 || 1841 source.indexOf('[') !== -1 || source.indexOf('(') !== -1) ) 1842 { 1843 /* If there is a . in the source string then the data source is in a 1844 * nested object so we loop over the data for each level to get the next 1845 * level down. On each loop we test for undefined, and if found immediately 1846 * return. This allows entire objects to be missing and sDefaultContent to 1847 * be used if defined, rather than throwing an error 1848 */ 1849 var fetchData = function (data, type, src) { 1850 var arrayNotation, funcNotation, out, innerSrc; 1851 1852 if ( src !== "" ) { 1853 var a = _fnSplitObjNotation( src ); 1854 1855 for ( var i=0, iLen=a.length ; i<iLen ; i++ ) { 1856 // Check if we are dealing with special notation 1857 arrayNotation = a[i].match(__reArray); 1858 funcNotation = a[i].match(__reFn); 1859 1860 if ( arrayNotation ) { 1861 // Array notation 1862 a[i] = a[i].replace(__reArray, ''); 1863 1864 // Condition allows simply [] to be passed in 1865 if ( a[i] !== "" ) { 1866 data = data[ a[i] ]; 1867 } 1868 out = []; 1869 1870 // Get the remainder of the nested object to get 1871 a.splice( 0, i+1 ); 1872 innerSrc = a.join('.'); 1873 1874 // Traverse each entry in the array getting the properties requested 1875 if ( Array.isArray( data ) ) { 1876 for ( var j=0, jLen=data.length ; j<jLen ; j++ ) { 1877 out.push( fetchData( data[j], type, innerSrc ) ); 1878 } 1879 } 1880 1881 // If a string is given in between the array notation indicators, that 1882 // is used to join the strings together, otherwise an array is returned 1883 var join = arrayNotation[0].substring(1, arrayNotation[0].length-1); 1884 data = (join==="") ? out : out.join(join); 1885 1886 // The inner call to fetchData has already traversed through the remainder 1887 // of the source requested, so we exit from the loop 1888 break; 1889 } 1890 else if ( funcNotation ) { 1891 // Function call 1892 a[i] = a[i].replace(__reFn, ''); 1893 data = data[ a[i] ](); 1894 continue; 1895 } 1896 1897 if (data === null || data[ a[i] ] === null) { 1898 return null; 1899 } 1900 else if ( data === undefined || data[ a[i] ] === undefined ) { 1901 return undefined; 1902 } 1903 1904 data = data[ a[i] ]; 1905 } 1906 } 1907 1908 return data; 1909 }; 1910 1911 return function (data, type) { // row and meta also passed, but not used 1912 return fetchData( data, type, source ); 1913 }; 1914 } 1915 else { 1916 // Array or flat object mapping 1917 return function (data, type) { // row and meta also passed, but not used 1918 return data[source]; 1919 }; 1920 } 1921 } 1922 }; 1923 1924 1925 1926 /** 1927 * Create a mapping object that allows camel case parameters to be looked up 1928 * for their Hungarian counterparts. The mapping is stored in a private 1929 * parameter called `_hungarianMap` which can be accessed on the source object. 1930 * @param {object} o 1931 * @memberof DataTable#oApi 1932 */ 1933 function _fnHungarianMap ( o ) 1934 { 1935 var 1936 hungarian = 'a aa ai ao as b fn i m o s ', 1937 match, 1938 newKey, 1939 map = {}; 1940 1941 $.each( o, function (key, val) { 1942 match = key.match(/^([^A-Z]+?)([A-Z])/); 1943 1944 if ( match && hungarian.indexOf(match[1]+' ') !== -1 ) 1945 { 1946 newKey = key.replace( match[0], match[2].toLowerCase() ); 1947 map[ newKey ] = key; 1948 1949 if ( match[1] === 'o' ) 1950 { 1951 _fnHungarianMap( o[key] ); 1952 } 1953 } 1954 } ); 1955 1956 o._hungarianMap = map; 1957 } 1958 1959 1960 /** 1961 * Convert from camel case parameters to Hungarian, based on a Hungarian map 1962 * created by _fnHungarianMap. 1963 * @param {object} src The model object which holds all parameters that can be 1964 * mapped. 1965 * @param {object} user The object to convert from camel case to Hungarian. 1966 * @param {boolean} force When set to `true`, properties which already have a 1967 * Hungarian value in the `user` object will be overwritten. Otherwise they 1968 * won't be. 1969 * @memberof DataTable#oApi 1970 */ 1971 function _fnCamelToHungarian ( src, user, force ) 1972 { 1973 if ( ! src._hungarianMap ) { 1974 _fnHungarianMap( src ); 1975 } 1976 1977 var hungarianKey; 1978 1979 $.each( user, function (key, val) { 1980 hungarianKey = src._hungarianMap[ key ]; 1981 1982 if ( hungarianKey !== undefined && (force || user[hungarianKey] === undefined) ) 1983 { 1984 // For objects, we need to buzz down into the object to copy parameters 1985 if ( hungarianKey.charAt(0) === 'o' ) 1986 { 1987 // Copy the camelCase options over to the hungarian 1988 if ( ! user[ hungarianKey ] ) { 1989 user[ hungarianKey ] = {}; 1990 } 1991 $.extend( true, user[hungarianKey], user[key] ); 1992 1993 _fnCamelToHungarian( src[hungarianKey], user[hungarianKey], force ); 1994 } 1995 else { 1996 user[hungarianKey] = user[ key ]; 1997 } 1998 } 1999 } ); 2000 } 2001 2002 2003 /** 2004 * Language compatibility - when certain options are given, and others aren't, we 2005 * need to duplicate the values over, in order to provide backwards compatibility 2006 * with older language files. 2007 * @param {object} oSettings dataTables settings object 2008 * @memberof DataTable#oApi 2009 */ 2010 function _fnLanguageCompat( lang ) 2011 { 2012 // Note the use of the Hungarian notation for the parameters in this method as 2013 // this is called after the mapping of camelCase to Hungarian 2014 var defaults = DataTable.defaults.oLanguage; 2015 2016 // Default mapping 2017 var defaultDecimal = defaults.sDecimal; 2018 if ( defaultDecimal ) { 2019 _addNumericSort( defaultDecimal ); 2020 } 2021 2022 if ( lang ) { 2023 var zeroRecords = lang.sZeroRecords; 2024 2025 // Backwards compatibility - if there is no sEmptyTable given, then use the same as 2026 // sZeroRecords - assuming that is given. 2027 if ( ! lang.sEmptyTable && zeroRecords && 2028 defaults.sEmptyTable === "No data available in table" ) 2029 { 2030 _fnMap( lang, lang, 'sZeroRecords', 'sEmptyTable' ); 2031 } 2032 2033 // Likewise with loading records 2034 if ( ! lang.sLoadingRecords && zeroRecords && 2035 defaults.sLoadingRecords === "Loading..." ) 2036 { 2037 _fnMap( lang, lang, 'sZeroRecords', 'sLoadingRecords' ); 2038 } 2039 2040 // Old parameter name of the thousands separator mapped onto the new 2041 if ( lang.sInfoThousands ) { 2042 lang.sThousands = lang.sInfoThousands; 2043 } 2044 2045 var decimal = lang.sDecimal; 2046 if ( decimal && defaultDecimal !== decimal ) { 2047 _addNumericSort( decimal ); 2048 } 2049 } 2050 } 2051 2052 2053 /** 2054 * Map one parameter onto another 2055 * @param {object} o Object to map 2056 * @param {*} knew The new parameter name 2057 * @param {*} old The old parameter name 2058 */ 2059 var _fnCompatMap = function ( o, knew, old ) { 2060 if ( o[ knew ] !== undefined ) { 2061 o[ old ] = o[ knew ]; 2062 } 2063 }; 2064 2065 2066 /** 2067 * Provide backwards compatibility for the main DT options. Note that the new 2068 * options are mapped onto the old parameters, so this is an external interface 2069 * change only. 2070 * @param {object} init Object to map 2071 */ 2072 function _fnCompatOpts ( init ) 2073 { 2074 _fnCompatMap( init, 'ordering', 'bSort' ); 2075 _fnCompatMap( init, 'orderMulti', 'bSortMulti' ); 2076 _fnCompatMap( init, 'orderClasses', 'bSortClasses' ); 2077 _fnCompatMap( init, 'orderCellsTop', 'bSortCellsTop' ); 2078 _fnCompatMap( init, 'order', 'aaSorting' ); 2079 _fnCompatMap( init, 'orderFixed', 'aaSortingFixed' ); 2080 _fnCompatMap( init, 'paging', 'bPaginate' ); 2081 _fnCompatMap( init, 'pagingType', 'sPaginationType' ); 2082 _fnCompatMap( init, 'pageLength', 'iDisplayLength' ); 2083 _fnCompatMap( init, 'searching', 'bFilter' ); 2084 2085 // Boolean initialisation of x-scrolling 2086 if ( typeof init.sScrollX === 'boolean' ) { 2087 init.sScrollX = init.sScrollX ? '100%' : ''; 2088 } 2089 if ( typeof init.scrollX === 'boolean' ) { 2090 init.scrollX = init.scrollX ? '100%' : ''; 2091 } 2092 2093 // Column search objects are in an array, so it needs to be converted 2094 // element by element 2095 var searchCols = init.aoSearchCols; 2096 2097 if ( searchCols ) { 2098 for ( var i=0, ien=searchCols.length ; i<ien ; i++ ) { 2099 if ( searchCols[i] ) { 2100 _fnCamelToHungarian( DataTable.models.oSearch, searchCols[i] ); 2101 } 2102 } 2103 } 2104 } 2105 2106 2107 /** 2108 * Provide backwards compatibility for column options. Note that the new options 2109 * are mapped onto the old parameters, so this is an external interface change 2110 * only. 2111 * @param {object} init Object to map 2112 */ 2113 function _fnCompatCols ( init ) 2114 { 2115 _fnCompatMap( init, 'orderable', 'bSortable' ); 2116 _fnCompatMap( init, 'orderData', 'aDataSort' ); 2117 _fnCompatMap( init, 'orderSequence', 'asSorting' ); 2118 _fnCompatMap( init, 'orderDataType', 'sortDataType' ); 2119 2120 // orderData can be given as an integer 2121 var dataSort = init.aDataSort; 2122 if ( typeof dataSort === 'number' && ! Array.isArray( dataSort ) ) { 2123 init.aDataSort = [ dataSort ]; 2124 } 2125 } 2126 2127 2128 /** 2129 * Browser feature detection for capabilities, quirks 2130 * @param {object} settings dataTables settings object 2131 * @memberof DataTable#oApi 2132 */ 2133 function _fnBrowserDetect( settings ) 2134 { 2135 // We don't need to do this every time DataTables is constructed, the values 2136 // calculated are specific to the browser and OS configuration which we 2137 // don't expect to change between initialisations 2138 if ( ! DataTable.__browser ) { 2139 var browser = {}; 2140 DataTable.__browser = browser; 2141 2142 // Scrolling feature / quirks detection 2143 var n = $('<div/>') 2144 .css( { 2145 position: 'fixed', 2146 top: 0, 2147 left: $(window).scrollLeft()*-1, // allow for scrolling 2148 height: 1, 2149 width: 1, 2150 overflow: 'hidden' 2151 } ) 2152 .append( 2153 $('<div/>') 2154 .css( { 2155 position: 'absolute', 2156 top: 1, 2157 left: 1, 2158 width: 100, 2159 overflow: 'scroll' 2160 } ) 2161 .append( 2162 $('<div/>') 2163 .css( { 2164 width: '100%', 2165 height: 10 2166 } ) 2167 ) 2168 ) 2169 .appendTo( 'body' ); 2170 2171 var outer = n.children(); 2172 var inner = outer.children(); 2173 2174 // Numbers below, in order, are: 2175 // inner.offsetWidth, inner.clientWidth, outer.offsetWidth, outer.clientWidth 2176 // 2177 // IE6 XP: 100 100 100 83 2178 // IE7 Vista: 100 100 100 83 2179 // IE 8+ Windows: 83 83 100 83 2180 // Evergreen Windows: 83 83 100 83 2181 // Evergreen Mac with scrollbars: 85 85 100 85 2182 // Evergreen Mac without scrollbars: 100 100 100 100 2183 2184 // Get scrollbar width 2185 browser.barWidth = outer[0].offsetWidth - outer[0].clientWidth; 2186 2187 // IE6/7 will oversize a width 100% element inside a scrolling element, to 2188 // include the width of the scrollbar, while other browsers ensure the inner 2189 // element is contained without forcing scrolling 2190 browser.bScrollOversize = inner[0].offsetWidth === 100 && outer[0].clientWidth !== 100; 2191 2192 // In rtl text layout, some browsers (most, but not all) will place the 2193 // scrollbar on the left, rather than the right. 2194 browser.bScrollbarLeft = Math.round( inner.offset().left ) !== 1; 2195 2196 // IE8- don't provide height and width for getBoundingClientRect 2197 browser.bBounding = n[0].getBoundingClientRect().width ? true : false; 2198 2199 n.remove(); 2200 } 2201 2202 $.extend( settings.oBrowser, DataTable.__browser ); 2203 settings.oScroll.iBarWidth = DataTable.__browser.barWidth; 2204 } 2205 2206 2207 /** 2208 * Array.prototype reduce[Right] method, used for browsers which don't support 2209 * JS 1.6. Done this way to reduce code size, since we iterate either way 2210 * @param {object} settings dataTables settings object 2211 * @memberof DataTable#oApi 2212 */ 2213 function _fnReduce ( that, fn, init, start, end, inc ) 2214 { 2215 var 2216 i = start, 2217 value, 2218 isSet = false; 2219 2220 if ( init !== undefined ) { 2221 value = init; 2222 isSet = true; 2223 } 2224 2225 while ( i !== end ) { 2226 if ( ! that.hasOwnProperty(i) ) { 2227 continue; 2228 } 2229 2230 value = isSet ? 2231 fn( value, that[i], i, that ) : 2232 that[i]; 2233 2234 isSet = true; 2235 i += inc; 2236 } 2237 2238 return value; 2239 } 2240 2241 /** 2242 * Add a column to the list used for the table with default values 2243 * @param {object} oSettings dataTables settings object 2244 * @param {node} nTh The th element for this column 2245 * @memberof DataTable#oApi 2246 */ 2247 function _fnAddColumn( oSettings, nTh ) 2248 { 2249 // Add column to aoColumns array 2250 var oDefaults = DataTable.defaults.column; 2251 var iCol = oSettings.aoColumns.length; 2252 var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, { 2253 "nTh": nTh ? nTh : document.createElement('th'), 2254 "sTitle": oDefaults.sTitle ? oDefaults.sTitle : nTh ? nTh.innerHTML : '', 2255 "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol], 2256 "mData": oDefaults.mData ? oDefaults.mData : iCol, 2257 idx: iCol 2258 } ); 2259 oSettings.aoColumns.push( oCol ); 2260 2261 // Add search object for column specific search. Note that the `searchCols[ iCol ]` 2262 // passed into extend can be undefined. This allows the user to give a default 2263 // with only some of the parameters defined, and also not give a default 2264 var searchCols = oSettings.aoPreSearchCols; 2265 searchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch, searchCols[ iCol ] ); 2266 2267 // Use the default column options function to initialise classes etc 2268 _fnColumnOptions( oSettings, iCol, $(nTh).data() ); 2269 } 2270 2271 2272 /** 2273 * Apply options for a column 2274 * @param {object} oSettings dataTables settings object 2275 * @param {int} iCol column index to consider 2276 * @param {object} oOptions object with sType, bVisible and bSearchable etc 2277 * @memberof DataTable#oApi 2278 */ 2279 function _fnColumnOptions( oSettings, iCol, oOptions ) 2280 { 2281 var oCol = oSettings.aoColumns[ iCol ]; 2282 var oClasses = oSettings.oClasses; 2283 var th = $(oCol.nTh); 2284 2285 // Try to get width information from the DOM. We can't get it from CSS 2286 // as we'd need to parse the CSS stylesheet. `width` option can override 2287 if ( ! oCol.sWidthOrig ) { 2288 // Width attribute 2289 oCol.sWidthOrig = th.attr('width') || null; 2290 2291 // Style attribute 2292 var t = (th.attr('style') || '').match(/width:\s*(\d+[pxem%]+)/); 2293 if ( t ) { 2294 oCol.sWidthOrig = t[1]; 2295 } 2296 } 2297 2298 /* User specified column options */ 2299 if ( oOptions !== undefined && oOptions !== null ) 2300 { 2301 // Backwards compatibility 2302 _fnCompatCols( oOptions ); 2303 2304 // Map camel case parameters to their Hungarian counterparts 2305 _fnCamelToHungarian( DataTable.defaults.column, oOptions, true ); 2306 2307 /* Backwards compatibility for mDataProp */ 2308 if ( oOptions.mDataProp !== undefined && !oOptions.mData ) 2309 { 2310 oOptions.mData = oOptions.mDataProp; 2311 } 2312 2313 if ( oOptions.sType ) 2314 { 2315 oCol._sManualType = oOptions.sType; 2316 } 2317 2318 // `class` is a reserved word in Javascript, so we need to provide 2319 // the ability to use a valid name for the camel case input 2320 if ( oOptions.className && ! oOptions.sClass ) 2321 { 2322 oOptions.sClass = oOptions.className; 2323 } 2324 if ( oOptions.sClass ) { 2325 th.addClass( oOptions.sClass ); 2326 } 2327 2328 var origClass = oCol.sClass; 2329 2330 $.extend( oCol, oOptions ); 2331 _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" ); 2332 2333 // Merge class from previously defined classes with this one, rather than just 2334 // overwriting it in the extend above 2335 if (origClass !== oCol.sClass) { 2336 oCol.sClass = origClass + ' ' + oCol.sClass; 2337 } 2338 2339 /* iDataSort to be applied (backwards compatibility), but aDataSort will take 2340 * priority if defined 2341 */ 2342 if ( oOptions.iDataSort !== undefined ) 2343 { 2344 oCol.aDataSort = [ oOptions.iDataSort ]; 2345 } 2346 _fnMap( oCol, oOptions, "aDataSort" ); 2347 } 2348 2349 /* Cache the data get and set functions for speed */ 2350 var mDataSrc = oCol.mData; 2351 var mData = _fnGetObjectDataFn( mDataSrc ); 2352 var mRender = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null; 2353 2354 var attrTest = function( src ) { 2355 return typeof src === 'string' && src.indexOf('@') !== -1; 2356 }; 2357 oCol._bAttrSrc = $.isPlainObject( mDataSrc ) && ( 2358 attrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter) 2359 ); 2360 oCol._setter = null; 2361 2362 oCol.fnGetData = function (rowData, type, meta) { 2363 var innerData = mData( rowData, type, undefined, meta ); 2364 2365 return mRender && type ? 2366 mRender( innerData, type, rowData, meta ) : 2367 innerData; 2368 }; 2369 oCol.fnSetData = function ( rowData, val, meta ) { 2370 return _fnSetObjectDataFn( mDataSrc )( rowData, val, meta ); 2371 }; 2372 2373 // Indicate if DataTables should read DOM data as an object or array 2374 // Used in _fnGetRowElements 2375 if ( typeof mDataSrc !== 'number' && ! oCol._isArrayHost ) { 2376 oSettings._rowReadObject = true; 2377 } 2378 2379 /* Feature sorting overrides column specific when off */ 2380 if ( !oSettings.oFeatures.bSort ) 2381 { 2382 oCol.bSortable = false; 2383 th.addClass( oClasses.sSortableNone ); // Have to add class here as order event isn't called 2384 } 2385 2386 /* Check that the class assignment is correct for sorting */ 2387 var bAsc = $.inArray('asc', oCol.asSorting) !== -1; 2388 var bDesc = $.inArray('desc', oCol.asSorting) !== -1; 2389 if ( !oCol.bSortable || (!bAsc && !bDesc) ) 2390 { 2391 oCol.sSortingClass = oClasses.sSortableNone; 2392 oCol.sSortingClassJUI = ""; 2393 } 2394 else if ( bAsc && !bDesc ) 2395 { 2396 oCol.sSortingClass = oClasses.sSortableAsc; 2397 oCol.sSortingClassJUI = oClasses.sSortJUIAscAllowed; 2398 } 2399 else if ( !bAsc && bDesc ) 2400 { 2401 oCol.sSortingClass = oClasses.sSortableDesc; 2402 oCol.sSortingClassJUI = oClasses.sSortJUIDescAllowed; 2403 } 2404 else 2405 { 2406 oCol.sSortingClass = oClasses.sSortable; 2407 oCol.sSortingClassJUI = oClasses.sSortJUI; 2408 } 2409 } 2410 2411 2412 /** 2413 * Adjust the table column widths for new data. Note: you would probably want to 2414 * do a redraw after calling this function! 2415 * @param {object} settings dataTables settings object 2416 * @memberof DataTable#oApi 2417 */ 2418 function _fnAdjustColumnSizing ( settings ) 2419 { 2420 /* Not interested in doing column width calculation if auto-width is disabled */ 2421 if ( settings.oFeatures.bAutoWidth !== false ) 2422 { 2423 var columns = settings.aoColumns; 2424 2425 _fnCalculateColumnWidths( settings ); 2426 for ( var i=0 , iLen=columns.length ; i<iLen ; i++ ) 2427 { 2428 columns[i].nTh.style.width = columns[i].sWidth; 2429 } 2430 } 2431 2432 var scroll = settings.oScroll; 2433 if ( scroll.sY !== '' || scroll.sX !== '') 2434 { 2435 _fnScrollDraw( settings ); 2436 } 2437 2438 _fnCallbackFire( settings, null, 'column-sizing', [settings] ); 2439 } 2440 2441 2442 /** 2443 * Convert the index of a visible column to the index in the data array (take account 2444 * of hidden columns) 2445 * @param {object} oSettings dataTables settings object 2446 * @param {int} iMatch Visible column index to lookup 2447 * @returns {int} i the data index 2448 * @memberof DataTable#oApi 2449 */ 2450 function _fnVisibleToColumnIndex( oSettings, iMatch ) 2451 { 2452 var aiVis = _fnGetColumns( oSettings, 'bVisible' ); 2453 2454 return typeof aiVis[iMatch] === 'number' ? 2455 aiVis[iMatch] : 2456 null; 2457 } 2458 2459 2460 /** 2461 * Convert the index of an index in the data array and convert it to the visible 2462 * column index (take account of hidden columns) 2463 * @param {int} iMatch Column index to lookup 2464 * @param {object} oSettings dataTables settings object 2465 * @returns {int} i the data index 2466 * @memberof DataTable#oApi 2467 */ 2468 function _fnColumnIndexToVisible( oSettings, iMatch ) 2469 { 2470 var aiVis = _fnGetColumns( oSettings, 'bVisible' ); 2471 var iPos = $.inArray( iMatch, aiVis ); 2472 2473 return iPos !== -1 ? iPos : null; 2474 } 2475 2476 2477 /** 2478 * Get the number of visible columns 2479 * @param {object} oSettings dataTables settings object 2480 * @returns {int} i the number of visible columns 2481 * @memberof DataTable#oApi 2482 */ 2483 function _fnVisbleColumns( oSettings ) 2484 { 2485 var vis = 0; 2486 2487 // No reduce in IE8, use a loop for now 2488 $.each( oSettings.aoColumns, function ( i, col ) { 2489 if ( col.bVisible && $(col.nTh).css('display') !== 'none' ) { 2490 vis++; 2491 } 2492 } ); 2493 2494 return vis; 2495 } 2496 2497 2498 /** 2499 * Get an array of column indexes that match a given property 2500 * @param {object} oSettings dataTables settings object 2501 * @param {string} sParam Parameter in aoColumns to look for - typically 2502 * bVisible or bSearchable 2503 * @returns {array} Array of indexes with matched properties 2504 * @memberof DataTable#oApi 2505 */ 2506 function _fnGetColumns( oSettings, sParam ) 2507 { 2508 var a = []; 2509 2510 $.map( oSettings.aoColumns, function(val, i) { 2511 if ( val[sParam] ) { 2512 a.push( i ); 2513 } 2514 } ); 2515 2516 return a; 2517 } 2518 2519 2520 /** 2521 * Calculate the 'type' of a column 2522 * @param {object} settings dataTables settings object 2523 * @memberof DataTable#oApi 2524 */ 2525 function _fnColumnTypes ( settings ) 2526 { 2527 var columns = settings.aoColumns; 2528 var data = settings.aoData; 2529 var types = DataTable.ext.type.detect; 2530 var i, ien, j, jen, k, ken; 2531 var col, cell, detectedType, cache; 2532 2533 // For each column, spin over the 2534 for ( i=0, ien=columns.length ; i<ien ; i++ ) { 2535 col = columns[i]; 2536 cache = []; 2537 2538 if ( ! col.sType && col._sManualType ) { 2539 col.sType = col._sManualType; 2540 } 2541 else if ( ! col.sType ) { 2542 for ( j=0, jen=types.length ; j<jen ; j++ ) { 2543 for ( k=0, ken=data.length ; k<ken ; k++ ) { 2544 // Use a cache array so we only need to get the type data 2545 // from the formatter once (when using multiple detectors) 2546 if ( cache[k] === undefined ) { 2547 cache[k] = _fnGetCellData( settings, k, i, 'type' ); 2548 } 2549 2550 detectedType = types[j]( cache[k], settings ); 2551 2552 // If null, then this type can't apply to this column, so 2553 // rather than testing all cells, break out. There is an 2554 // exception for the last type which is `html`. We need to 2555 // scan all rows since it is possible to mix string and HTML 2556 // types 2557 if ( ! detectedType && j !== types.length-1 ) { 2558 break; 2559 } 2560 2561 // Only a single match is needed for html type since it is 2562 // bottom of the pile and very similar to string - but it 2563 // must not be empty 2564 if ( detectedType === 'html' && ! _empty(cache[k]) ) { 2565 break; 2566 } 2567 } 2568 2569 // Type is valid for all data points in the column - use this 2570 // type 2571 if ( detectedType ) { 2572 col.sType = detectedType; 2573 break; 2574 } 2575 } 2576 2577 // Fall back - if no type was detected, always use string 2578 if ( ! col.sType ) { 2579 col.sType = 'string'; 2580 } 2581 } 2582 } 2583 } 2584 2585 2586 /** 2587 * Take the column definitions and static columns arrays and calculate how 2588 * they relate to column indexes. The callback function will then apply the 2589 * definition found for a column to a suitable configuration object. 2590 * @param {object} oSettings dataTables settings object 2591 * @param {array} aoColDefs The aoColumnDefs array that is to be applied 2592 * @param {array} aoCols The aoColumns array that defines columns individually 2593 * @param {function} fn Callback function - takes two parameters, the calculated 2594 * column index and the definition for that column. 2595 * @memberof DataTable#oApi 2596 */ 2597 function _fnApplyColumnDefs( oSettings, aoColDefs, aoCols, fn ) 2598 { 2599 var i, iLen, j, jLen, k, kLen, def; 2600 var columns = oSettings.aoColumns; 2601 2602 // Column definitions with aTargets 2603 if ( aoColDefs ) 2604 { 2605 /* Loop over the definitions array - loop in reverse so first instance has priority */ 2606 for ( i=aoColDefs.length-1 ; i>=0 ; i-- ) 2607 { 2608 def = aoColDefs[i]; 2609 2610 /* Each definition can target multiple columns, as it is an array */ 2611 var aTargets = def.target !== undefined 2612 ? def.target 2613 : def.targets !== undefined 2614 ? def.targets 2615 : def.aTargets; 2616 2617 if ( ! Array.isArray( aTargets ) ) 2618 { 2619 aTargets = [ aTargets ]; 2620 } 2621 2622 for ( j=0, jLen=aTargets.length ; j<jLen ; j++ ) 2623 { 2624 if ( typeof aTargets[j] === 'number' && aTargets[j] >= 0 ) 2625 { 2626 /* Add columns that we don't yet know about */ 2627 while( columns.length <= aTargets[j] ) 2628 { 2629 _fnAddColumn( oSettings ); 2630 } 2631 2632 /* Integer, basic index */ 2633 fn( aTargets[j], def ); 2634 } 2635 else if ( typeof aTargets[j] === 'number' && aTargets[j] < 0 ) 2636 { 2637 /* Negative integer, right to left column counting */ 2638 fn( columns.length+aTargets[j], def ); 2639 } 2640 else if ( typeof aTargets[j] === 'string' ) 2641 { 2642 /* Class name matching on TH element */ 2643 for ( k=0, kLen=columns.length ; k<kLen ; k++ ) 2644 { 2645 if ( aTargets[j] == "_all" || 2646 $(columns[k].nTh).hasClass( aTargets[j] ) ) 2647 { 2648 fn( k, def ); 2649 } 2650 } 2651 } 2652 } 2653 } 2654 } 2655 2656 // Statically defined columns array 2657 if ( aoCols ) 2658 { 2659 for ( i=0, iLen=aoCols.length ; i<iLen ; i++ ) 2660 { 2661 fn( i, aoCols[i] ); 2662 } 2663 } 2664 } 2665 2666 /** 2667 * Add a data array to the table, creating DOM node etc. This is the parallel to 2668 * _fnGatherData, but for adding rows from a Javascript source, rather than a 2669 * DOM source. 2670 * @param {object} oSettings dataTables settings object 2671 * @param {array} aData data array to be added 2672 * @param {node} [nTr] TR element to add to the table - optional. If not given, 2673 * DataTables will create a row automatically 2674 * @param {array} [anTds] Array of TD|TH elements for the row - must be given 2675 * if nTr is. 2676 * @returns {int} >=0 if successful (index of new aoData entry), -1 if failed 2677 * @memberof DataTable#oApi 2678 */ 2679 function _fnAddData ( oSettings, aDataIn, nTr, anTds ) 2680 { 2681 /* Create the object for storing information about this new row */ 2682 var iRow = oSettings.aoData.length; 2683 var oData = $.extend( true, {}, DataTable.models.oRow, { 2684 src: nTr ? 'dom' : 'data', 2685 idx: iRow 2686 } ); 2687 2688 oData._aData = aDataIn; 2689 oSettings.aoData.push( oData ); 2690 2691 /* Create the cells */ 2692 var nTd, sThisType; 2693 var columns = oSettings.aoColumns; 2694 2695 // Invalidate the column types as the new data needs to be revalidated 2696 for ( var i=0, iLen=columns.length ; i<iLen ; i++ ) 2697 { 2698 columns[i].sType = null; 2699 } 2700 2701 /* Add to the display array */ 2702 oSettings.aiDisplayMaster.push( iRow ); 2703 2704 var id = oSettings.rowIdFn( aDataIn ); 2705 if ( id !== undefined ) { 2706 oSettings.aIds[ id ] = oData; 2707 } 2708 2709 /* Create the DOM information, or register it if already present */ 2710 if ( nTr || ! oSettings.oFeatures.bDeferRender ) 2711 { 2712 _fnCreateTr( oSettings, iRow, nTr, anTds ); 2713 } 2714 2715 return iRow; 2716 } 2717 2718 2719 /** 2720 * Add one or more TR elements to the table. Generally we'd expect to 2721 * use this for reading data from a DOM sourced table, but it could be 2722 * used for an TR element. Note that if a TR is given, it is used (i.e. 2723 * it is not cloned). 2724 * @param {object} settings dataTables settings object 2725 * @param {array|node|jQuery} trs The TR element(s) to add to the table 2726 * @returns {array} Array of indexes for the added rows 2727 * @memberof DataTable#oApi 2728 */ 2729 function _fnAddTr( settings, trs ) 2730 { 2731 var row; 2732 2733 // Allow an individual node to be passed in 2734 if ( ! (trs instanceof $) ) { 2735 trs = $(trs); 2736 } 2737 2738 return trs.map( function (i, el) { 2739 row = _fnGetRowElements( settings, el ); 2740 return _fnAddData( settings, row.data, el, row.cells ); 2741 } ); 2742 } 2743 2744 2745 /** 2746 * Take a TR element and convert it to an index in aoData 2747 * @param {object} oSettings dataTables settings object 2748 * @param {node} n the TR element to find 2749 * @returns {int} index if the node is found, null if not 2750 * @memberof DataTable#oApi 2751 */ 2752 function _fnNodeToDataIndex( oSettings, n ) 2753 { 2754 return (n._DT_RowIndex!==undefined) ? n._DT_RowIndex : null; 2755 } 2756 2757 2758 /** 2759 * Take a TD element and convert it into a column data index (not the visible index) 2760 * @param {object} oSettings dataTables settings object 2761 * @param {int} iRow The row number the TD/TH can be found in 2762 * @param {node} n The TD/TH element to find 2763 * @returns {int} index if the node is found, -1 if not 2764 * @memberof DataTable#oApi 2765 */ 2766 function _fnNodeToColumnIndex( oSettings, iRow, n ) 2767 { 2768 return $.inArray( n, oSettings.aoData[ iRow ].anCells ); 2769 } 2770 2771 2772 /** 2773 * Get the data for a given cell from the internal cache, taking into account data mapping 2774 * @param {object} settings dataTables settings object 2775 * @param {int} rowIdx aoData row id 2776 * @param {int} colIdx Column index 2777 * @param {string} type data get type ('display', 'type' 'filter|search' 'sort|order') 2778 * @returns {*} Cell data 2779 * @memberof DataTable#oApi 2780 */ 2781 function _fnGetCellData( settings, rowIdx, colIdx, type ) 2782 { 2783 if (type === 'search') { 2784 type = 'filter'; 2785 } 2786 else if (type === 'order') { 2787 type = 'sort'; 2788 } 2789 2790 var draw = settings.iDraw; 2791 var col = settings.aoColumns[colIdx]; 2792 var rowData = settings.aoData[rowIdx]._aData; 2793 var defaultContent = col.sDefaultContent; 2794 var cellData = col.fnGetData( rowData, type, { 2795 settings: settings, 2796 row: rowIdx, 2797 col: colIdx 2798 } ); 2799 2800 if ( cellData === undefined ) { 2801 if ( settings.iDrawError != draw && defaultContent === null ) { 2802 _fnLog( settings, 0, "Requested unknown parameter "+ 2803 (typeof col.mData=='function' ? '{function}' : "'"+col.mData+"'")+ 2804 " for row "+rowIdx+", column "+colIdx, 4 ); 2805 settings.iDrawError = draw; 2806 } 2807 return defaultContent; 2808 } 2809 2810 // When the data source is null and a specific data type is requested (i.e. 2811 // not the original data), we can use default column data 2812 if ( (cellData === rowData || cellData === null) && defaultContent !== null && type !== undefined ) { 2813 cellData = defaultContent; 2814 } 2815 else if ( typeof cellData === 'function' ) { 2816 // If the data source is a function, then we run it and use the return, 2817 // executing in the scope of the data object (for instances) 2818 return cellData.call( rowData ); 2819 } 2820 2821 if ( cellData === null && type === 'display' ) { 2822 return ''; 2823 } 2824 2825 if ( type === 'filter' ) { 2826 var fomatters = DataTable.ext.type.search; 2827 2828 if ( fomatters[ col.sType ] ) { 2829 cellData = fomatters[ col.sType ]( cellData ); 2830 } 2831 } 2832 2833 return cellData; 2834 } 2835 2836 2837 /** 2838 * Set the value for a specific cell, into the internal data cache 2839 * @param {object} settings dataTables settings object 2840 * @param {int} rowIdx aoData row id 2841 * @param {int} colIdx Column index 2842 * @param {*} val Value to set 2843 * @memberof DataTable#oApi 2844 */ 2845 function _fnSetCellData( settings, rowIdx, colIdx, val ) 2846 { 2847 var col = settings.aoColumns[colIdx]; 2848 var rowData = settings.aoData[rowIdx]._aData; 2849 2850 col.fnSetData( rowData, val, { 2851 settings: settings, 2852 row: rowIdx, 2853 col: colIdx 2854 } ); 2855 } 2856 2857 2858 // Private variable that is used to match action syntax in the data property object 2859 var __reArray = /\[.*?\]$/; 2860 var __reFn = /\(\)$/; 2861 2862 /** 2863 * Split string on periods, taking into account escaped periods 2864 * @param {string} str String to split 2865 * @return {array} Split string 2866 */ 2867 function _fnSplitObjNotation( str ) 2868 { 2869 return $.map( str.match(/(\\.|[^\.])+/g) || [''], function ( s ) { 2870 return s.replace(/\\\./g, '.'); 2871 } ); 2872 } 2873 2874 2875 /** 2876 * Return a function that can be used to get data from a source object, taking 2877 * into account the ability to use nested objects as a source 2878 * @param {string|int|function} mSource The data source for the object 2879 * @returns {function} Data get function 2880 * @memberof DataTable#oApi 2881 */ 2882 var _fnGetObjectDataFn = DataTable.util.get; 2883 2884 2885 /** 2886 * Return a function that can be used to set data from a source object, taking 2887 * into account the ability to use nested objects as a source 2888 * @param {string|int|function} mSource The data source for the object 2889 * @returns {function} Data set function 2890 * @memberof DataTable#oApi 2891 */ 2892 var _fnSetObjectDataFn = DataTable.util.set; 2893 2894 2895 /** 2896 * Return an array with the full table data 2897 * @param {object} oSettings dataTables settings object 2898 * @returns array {array} aData Master data array 2899 * @memberof DataTable#oApi 2900 */ 2901 function _fnGetDataMaster ( settings ) 2902 { 2903 return _pluck( settings.aoData, '_aData' ); 2904 } 2905 2906 2907 /** 2908 * Nuke the table 2909 * @param {object} oSettings dataTables settings object 2910 * @memberof DataTable#oApi 2911 */ 2912 function _fnClearTable( settings ) 2913 { 2914 settings.aoData.length = 0; 2915 settings.aiDisplayMaster.length = 0; 2916 settings.aiDisplay.length = 0; 2917 settings.aIds = {}; 2918 } 2919 2920 2921 /** 2922 * Take an array of integers (index array) and remove a target integer (value - not 2923 * the key!) 2924 * @param {array} a Index array to target 2925 * @param {int} iTarget value to find 2926 * @memberof DataTable#oApi 2927 */ 2928 function _fnDeleteIndex( a, iTarget, splice ) 2929 { 2930 var iTargetIndex = -1; 2931 2932 for ( var i=0, iLen=a.length ; i<iLen ; i++ ) 2933 { 2934 if ( a[i] == iTarget ) 2935 { 2936 iTargetIndex = i; 2937 } 2938 else if ( a[i] > iTarget ) 2939 { 2940 a[i]--; 2941 } 2942 } 2943 2944 if ( iTargetIndex != -1 && splice === undefined ) 2945 { 2946 a.splice( iTargetIndex, 1 ); 2947 } 2948 } 2949 2950 2951 /** 2952 * Mark cached data as invalid such that a re-read of the data will occur when 2953 * the cached data is next requested. Also update from the data source object. 2954 * 2955 * @param {object} settings DataTables settings object 2956 * @param {int} rowIdx Row index to invalidate 2957 * @param {string} [src] Source to invalidate from: undefined, 'auto', 'dom' 2958 * or 'data' 2959 * @param {int} [colIdx] Column index to invalidate. If undefined the whole 2960 * row will be invalidated 2961 * @memberof DataTable#oApi 2962 * 2963 * @todo For the modularisation of v1.11 this will need to become a callback, so 2964 * the sort and filter methods can subscribe to it. That will required 2965 * initialisation options for sorting, which is why it is not already baked in 2966 */ 2967 function _fnInvalidate( settings, rowIdx, src, colIdx ) 2968 { 2969 var row = settings.aoData[ rowIdx ]; 2970 var i, ien; 2971 var cellWrite = function ( cell, col ) { 2972 // This is very frustrating, but in IE if you just write directly 2973 // to innerHTML, and elements that are overwritten are GC'ed, 2974 // even if there is a reference to them elsewhere 2975 while ( cell.childNodes.length ) { 2976 cell.removeChild( cell.firstChild ); 2977 } 2978 2979 cell.innerHTML = _fnGetCellData( settings, rowIdx, col, 'display' ); 2980 }; 2981 2982 // Are we reading last data from DOM or the data object? 2983 if ( src === 'dom' || ((! src || src === 'auto') && row.src === 'dom') ) { 2984 // Read the data from the DOM 2985 row._aData = _fnGetRowElements( 2986 settings, row, colIdx, colIdx === undefined ? undefined : row._aData 2987 ) 2988 .data; 2989 } 2990 else { 2991 // Reading from data object, update the DOM 2992 var cells = row.anCells; 2993 2994 if ( cells ) { 2995 if ( colIdx !== undefined ) { 2996 cellWrite( cells[colIdx], colIdx ); 2997 } 2998 else { 2999 for ( i=0, ien=cells.length ; i<ien ; i++ ) { 3000 cellWrite( cells[i], i ); 3001 } 3002 } 3003 } 3004 } 3005 3006 // For both row and cell invalidation, the cached data for sorting and 3007 // filtering is nulled out 3008 row._aSortData = null; 3009 row._aFilterData = null; 3010 3011 // Invalidate the type for a specific column (if given) or all columns since 3012 // the data might have changed 3013 var cols = settings.aoColumns; 3014 if ( colIdx !== undefined ) { 3015 cols[ colIdx ].sType = null; 3016 } 3017 else { 3018 for ( i=0, ien=cols.length ; i<ien ; i++ ) { 3019 cols[i].sType = null; 3020 } 3021 3022 // Update DataTables special `DT_*` attributes for the row 3023 _fnRowAttributes( settings, row ); 3024 } 3025 } 3026 3027 3028 /** 3029 * Build a data source object from an HTML row, reading the contents of the 3030 * cells that are in the row. 3031 * 3032 * @param {object} settings DataTables settings object 3033 * @param {node|object} TR element from which to read data or existing row 3034 * object from which to re-read the data from the cells 3035 * @param {int} [colIdx] Optional column index 3036 * @param {array|object} [d] Data source object. If `colIdx` is given then this 3037 * parameter should also be given and will be used to write the data into. 3038 * Only the column in question will be written 3039 * @returns {object} Object with two parameters: `data` the data read, in 3040 * document order, and `cells` and array of nodes (they can be useful to the 3041 * caller, so rather than needing a second traversal to get them, just return 3042 * them from here). 3043 * @memberof DataTable#oApi 3044 */ 3045 function _fnGetRowElements( settings, row, colIdx, d ) 3046 { 3047 var 3048 tds = [], 3049 td = row.firstChild, 3050 name, col, o, i=0, contents, 3051 columns = settings.aoColumns, 3052 objectRead = settings._rowReadObject; 3053 3054 // Allow the data object to be passed in, or construct 3055 d = d !== undefined ? 3056 d : 3057 objectRead ? 3058 {} : 3059 []; 3060 3061 var attr = function ( str, td ) { 3062 if ( typeof str === 'string' ) { 3063 var idx = str.indexOf('@'); 3064 3065 if ( idx !== -1 ) { 3066 var attr = str.substring( idx+1 ); 3067 var setter = _fnSetObjectDataFn( str ); 3068 setter( d, td.getAttribute( attr ) ); 3069 } 3070 } 3071 }; 3072 3073 // Read data from a cell and store into the data object 3074 var cellProcess = function ( cell ) { 3075 if ( colIdx === undefined || colIdx === i ) { 3076 col = columns[i]; 3077 contents = (cell.innerHTML).trim(); 3078 3079 if ( col && col._bAttrSrc ) { 3080 var setter = _fnSetObjectDataFn( col.mData._ ); 3081 setter( d, contents ); 3082 3083 attr( col.mData.sort, cell ); 3084 attr( col.mData.type, cell ); 3085 attr( col.mData.filter, cell ); 3086 } 3087 else { 3088 // Depending on the `data` option for the columns the data can 3089 // be read to either an object or an array. 3090 if ( objectRead ) { 3091 if ( ! col._setter ) { 3092 // Cache the setter function 3093 col._setter = _fnSetObjectDataFn( col.mData ); 3094 } 3095 col._setter( d, contents ); 3096 } 3097 else { 3098 d[i] = contents; 3099 } 3100 } 3101 } 3102 3103 i++; 3104 }; 3105 3106 if ( td ) { 3107 // `tr` element was passed in 3108 while ( td ) { 3109 name = td.nodeName.toUpperCase(); 3110 3111 if ( name == "TD" || name == "TH" ) { 3112 cellProcess( td ); 3113 tds.push( td ); 3114 } 3115 3116 td = td.nextSibling; 3117 } 3118 } 3119 else { 3120 // Existing row object passed in 3121 tds = row.anCells; 3122 3123 for ( var j=0, jen=tds.length ; j<jen ; j++ ) { 3124 cellProcess( tds[j] ); 3125 } 3126 } 3127 3128 // Read the ID from the DOM if present 3129 var rowNode = row.firstChild ? row : row.nTr; 3130 3131 if ( rowNode ) { 3132 var id = rowNode.getAttribute( 'id' ); 3133 3134 if ( id ) { 3135 _fnSetObjectDataFn( settings.rowId )( d, id ); 3136 } 3137 } 3138 3139 return { 3140 data: d, 3141 cells: tds 3142 }; 3143 } 3144 /** 3145 * Create a new TR element (and it's TD children) for a row 3146 * @param {object} oSettings dataTables settings object 3147 * @param {int} iRow Row to consider 3148 * @param {node} [nTrIn] TR element to add to the table - optional. If not given, 3149 * DataTables will create a row automatically 3150 * @param {array} [anTds] Array of TD|TH elements for the row - must be given 3151 * if nTr is. 3152 * @memberof DataTable#oApi 3153 */ 3154 function _fnCreateTr ( oSettings, iRow, nTrIn, anTds ) 3155 { 3156 var 3157 row = oSettings.aoData[iRow], 3158 rowData = row._aData, 3159 cells = [], 3160 nTr, nTd, oCol, 3161 i, iLen, create; 3162 3163 if ( row.nTr === null ) 3164 { 3165 nTr = nTrIn || document.createElement('tr'); 3166 3167 row.nTr = nTr; 3168 row.anCells = cells; 3169 3170 /* Use a private property on the node to allow reserve mapping from the node 3171 * to the aoData array for fast look up 3172 */ 3173 nTr._DT_RowIndex = iRow; 3174 3175 /* Special parameters can be given by the data source to be used on the row */ 3176 _fnRowAttributes( oSettings, row ); 3177 3178 /* Process each column */ 3179 for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ ) 3180 { 3181 oCol = oSettings.aoColumns[i]; 3182 create = nTrIn ? false : true; 3183 3184 nTd = create ? document.createElement( oCol.sCellType ) : anTds[i]; 3185 3186 if (! nTd) { 3187 _fnLog( oSettings, 0, 'Incorrect column count', 18 ); 3188 } 3189 3190 nTd._DT_CellIndex = { 3191 row: iRow, 3192 column: i 3193 }; 3194 3195 cells.push( nTd ); 3196 3197 // Need to create the HTML if new, or if a rendering function is defined 3198 if ( create || ((oCol.mRender || oCol.mData !== i) && 3199 (!$.isPlainObject(oCol.mData) || oCol.mData._ !== i+'.display') 3200 )) { 3201 nTd.innerHTML = _fnGetCellData( oSettings, iRow, i, 'display' ); 3202 } 3203 3204 /* Add user defined class */ 3205 if ( oCol.sClass ) 3206 { 3207 nTd.className += ' '+oCol.sClass; 3208 } 3209 3210 // Visibility - add or remove as required 3211 if ( oCol.bVisible && ! nTrIn ) 3212 { 3213 nTr.appendChild( nTd ); 3214 } 3215 else if ( ! oCol.bVisible && nTrIn ) 3216 { 3217 nTd.parentNode.removeChild( nTd ); 3218 } 3219 3220 if ( oCol.fnCreatedCell ) 3221 { 3222 oCol.fnCreatedCell.call( oSettings.oInstance, 3223 nTd, _fnGetCellData( oSettings, iRow, i ), rowData, iRow, i 3224 ); 3225 } 3226 } 3227 3228 _fnCallbackFire( oSettings, 'aoRowCreatedCallback', null, [nTr, rowData, iRow, cells] ); 3229 } 3230 } 3231 3232 3233 /** 3234 * Add attributes to a row based on the special `DT_*` parameters in a data 3235 * source object. 3236 * @param {object} settings DataTables settings object 3237 * @param {object} DataTables row object for the row to be modified 3238 * @memberof DataTable#oApi 3239 */ 3240 function _fnRowAttributes( settings, row ) 3241 { 3242 var tr = row.nTr; 3243 var data = row._aData; 3244 3245 if ( tr ) { 3246 var id = settings.rowIdFn( data ); 3247 3248 if ( id ) { 3249 tr.id = id; 3250 } 3251 3252 if ( data.DT_RowClass ) { 3253 // Remove any classes added by DT_RowClass before 3254 var a = data.DT_RowClass.split(' '); 3255 row.__rowc = row.__rowc ? 3256 _unique( row.__rowc.concat( a ) ) : 3257 a; 3258 3259 $(tr) 3260 .removeClass( row.__rowc.join(' ') ) 3261 .addClass( data.DT_RowClass ); 3262 } 3263 3264 if ( data.DT_RowAttr ) { 3265 $(tr).attr( data.DT_RowAttr ); 3266 } 3267 3268 if ( data.DT_RowData ) { 3269 $(tr).data( data.DT_RowData ); 3270 } 3271 } 3272 } 3273 3274 3275 /** 3276 * Create the HTML header for the table 3277 * @param {object} oSettings dataTables settings object 3278 * @memberof DataTable#oApi 3279 */ 3280 function _fnBuildHead( oSettings ) 3281 { 3282 var i, ien, cell, row, column; 3283 var thead = oSettings.nTHead; 3284 var tfoot = oSettings.nTFoot; 3285 var createHeader = $('th, td', thead).length === 0; 3286 var classes = oSettings.oClasses; 3287 var columns = oSettings.aoColumns; 3288 3289 if ( createHeader ) { 3290 row = $('<tr/>').appendTo( thead ); 3291 } 3292 3293 for ( i=0, ien=columns.length ; i<ien ; i++ ) { 3294 column = columns[i]; 3295 cell = $( column.nTh ).addClass( column.sClass ); 3296 3297 if ( createHeader ) { 3298 cell.appendTo( row ); 3299 } 3300 3301 // 1.11 move into sorting 3302 if ( oSettings.oFeatures.bSort ) { 3303 cell.addClass( column.sSortingClass ); 3304 3305 if ( column.bSortable !== false ) { 3306 cell 3307 .attr( 'tabindex', oSettings.iTabIndex ) 3308 .attr( 'aria-controls', oSettings.sTableId ); 3309 3310 _fnSortAttachListener( oSettings, column.nTh, i ); 3311 } 3312 } 3313 3314 if ( column.sTitle != cell[0].innerHTML ) { 3315 cell.html( column.sTitle ); 3316 } 3317 3318 _fnRenderer( oSettings, 'header' )( 3319 oSettings, cell, column, classes 3320 ); 3321 } 3322 3323 if ( createHeader ) { 3324 _fnDetectHeader( oSettings.aoHeader, thead ); 3325 } 3326 3327 /* Deal with the footer - add classes if required */ 3328 $(thead).children('tr').children('th, td').addClass( classes.sHeaderTH ); 3329 $(tfoot).children('tr').children('th, td').addClass( classes.sFooterTH ); 3330 3331 // Cache the footer cells. Note that we only take the cells from the first 3332 // row in the footer. If there is more than one row the user wants to 3333 // interact with, they need to use the table().foot() method. Note also this 3334 // allows cells to be used for multiple columns using colspan 3335 if ( tfoot !== null ) { 3336 var cells = oSettings.aoFooter[0]; 3337 3338 for ( i=0, ien=cells.length ; i<ien ; i++ ) { 3339 column = columns[i]; 3340 3341 if (column) { 3342 column.nTf = cells[i].cell; 3343 3344 if ( column.sClass ) { 3345 $(column.nTf).addClass( column.sClass ); 3346 } 3347 } 3348 else { 3349 _fnLog( oSettings, 0, 'Incorrect column count', 18 ); 3350 } 3351 } 3352 } 3353 } 3354 3355 3356 /** 3357 * Draw the header (or footer) element based on the column visibility states. The 3358 * methodology here is to use the layout array from _fnDetectHeader, modified for 3359 * the instantaneous column visibility, to construct the new layout. The grid is 3360 * traversed over cell at a time in a rows x columns grid fashion, although each 3361 * cell insert can cover multiple elements in the grid - which is tracks using the 3362 * aApplied array. Cell inserts in the grid will only occur where there isn't 3363 * already a cell in that position. 3364 * @param {object} oSettings dataTables settings object 3365 * @param array {objects} aoSource Layout array from _fnDetectHeader 3366 * @param {boolean} [bIncludeHidden=false] If true then include the hidden columns in the calc, 3367 * @memberof DataTable#oApi 3368 */ 3369 function _fnDrawHead( oSettings, aoSource, bIncludeHidden ) 3370 { 3371 var i, iLen, j, jLen, k, kLen, n, nLocalTr; 3372 var aoLocal = []; 3373 var aApplied = []; 3374 var iColumns = oSettings.aoColumns.length; 3375 var iRowspan, iColspan; 3376 3377 if ( ! aoSource ) 3378 { 3379 return; 3380 } 3381 3382 if ( bIncludeHidden === undefined ) 3383 { 3384 bIncludeHidden = false; 3385 } 3386 3387 /* Make a copy of the master layout array, but without the visible columns in it */ 3388 for ( i=0, iLen=aoSource.length ; i<iLen ; i++ ) 3389 { 3390 aoLocal[i] = aoSource[i].slice(); 3391 aoLocal[i].nTr = aoSource[i].nTr; 3392 3393 /* Remove any columns which are currently hidden */ 3394 for ( j=iColumns-1 ; j>=0 ; j-- ) 3395 { 3396 if ( !oSettings.aoColumns[j].bVisible && !bIncludeHidden ) 3397 { 3398 aoLocal[i].splice( j, 1 ); 3399 } 3400 } 3401 3402 /* Prep the applied array - it needs an element for each row */ 3403 aApplied.push( [] ); 3404 } 3405 3406 for ( i=0, iLen=aoLocal.length ; i<iLen ; i++ ) 3407 { 3408 nLocalTr = aoLocal[i].nTr; 3409 3410 /* All cells are going to be replaced, so empty out the row */ 3411 if ( nLocalTr ) 3412 { 3413 while( (n = nLocalTr.firstChild) ) 3414 { 3415 nLocalTr.removeChild( n ); 3416 } 3417 } 3418 3419 for ( j=0, jLen=aoLocal[i].length ; j<jLen ; j++ ) 3420 { 3421 iRowspan = 1; 3422 iColspan = 1; 3423 3424 /* Check to see if there is already a cell (row/colspan) covering our target 3425 * insert point. If there is, then there is nothing to do. 3426 */ 3427 if ( aApplied[i][j] === undefined ) 3428 { 3429 nLocalTr.appendChild( aoLocal[i][j].cell ); 3430 aApplied[i][j] = 1; 3431 3432 /* Expand the cell to cover as many rows as needed */ 3433 while ( aoLocal[i+iRowspan] !== undefined && 3434 aoLocal[i][j].cell == aoLocal[i+iRowspan][j].cell ) 3435 { 3436 aApplied[i+iRowspan][j] = 1; 3437 iRowspan++; 3438 } 3439 3440 /* Expand the cell to cover as many columns as needed */ 3441 while ( aoLocal[i][j+iColspan] !== undefined && 3442 aoLocal[i][j].cell == aoLocal[i][j+iColspan].cell ) 3443 { 3444 /* Must update the applied array over the rows for the columns */ 3445 for ( k=0 ; k<iRowspan ; k++ ) 3446 { 3447 aApplied[i+k][j+iColspan] = 1; 3448 } 3449 iColspan++; 3450 } 3451 3452 /* Do the actual expansion in the DOM */ 3453 $(aoLocal[i][j].cell) 3454 .attr('rowspan', iRowspan) 3455 .attr('colspan', iColspan); 3456 } 3457 } 3458 } 3459 } 3460 3461 3462 /** 3463 * Insert the required TR nodes into the table for display 3464 * @param {object} oSettings dataTables settings object 3465 * @param ajaxComplete true after ajax call to complete rendering 3466 * @memberof DataTable#oApi 3467 */ 3468 function _fnDraw( oSettings, ajaxComplete ) 3469 { 3470 // Allow for state saving and a custom start position 3471 _fnStart( oSettings ); 3472 3473 /* Provide a pre-callback function which can be used to cancel the draw is false is returned */ 3474 var aPreDraw = _fnCallbackFire( oSettings, 'aoPreDrawCallback', 'preDraw', [oSettings] ); 3475 if ( $.inArray( false, aPreDraw ) !== -1 ) 3476 { 3477 _fnProcessingDisplay( oSettings, false ); 3478 return; 3479 } 3480 3481 var anRows = []; 3482 var iRowCount = 0; 3483 var asStripeClasses = oSettings.asStripeClasses; 3484 var iStripes = asStripeClasses.length; 3485 var oLang = oSettings.oLanguage; 3486 var bServerSide = _fnDataSource( oSettings ) == 'ssp'; 3487 var aiDisplay = oSettings.aiDisplay; 3488 var iDisplayStart = oSettings._iDisplayStart; 3489 var iDisplayEnd = oSettings.fnDisplayEnd(); 3490 3491 oSettings.bDrawing = true; 3492 3493 /* Server-side processing draw intercept */ 3494 if ( oSettings.bDeferLoading ) 3495 { 3496 oSettings.bDeferLoading = false; 3497 oSettings.iDraw++; 3498 _fnProcessingDisplay( oSettings, false ); 3499 } 3500 else if ( !bServerSide ) 3501 { 3502 oSettings.iDraw++; 3503 } 3504 else if ( !oSettings.bDestroying && !ajaxComplete) 3505 { 3506 _fnAjaxUpdate( oSettings ); 3507 return; 3508 } 3509 3510 if ( aiDisplay.length !== 0 ) 3511 { 3512 var iStart = bServerSide ? 0 : iDisplayStart; 3513 var iEnd = bServerSide ? oSettings.aoData.length : iDisplayEnd; 3514 3515 for ( var j=iStart ; j<iEnd ; j++ ) 3516 { 3517 var iDataIndex = aiDisplay[j]; 3518 var aoData = oSettings.aoData[ iDataIndex ]; 3519 if ( aoData.nTr === null ) 3520 { 3521 _fnCreateTr( oSettings, iDataIndex ); 3522 } 3523 3524 var nRow = aoData.nTr; 3525 3526 /* Remove the old striping classes and then add the new one */ 3527 if ( iStripes !== 0 ) 3528 { 3529 var sStripe = asStripeClasses[ iRowCount % iStripes ]; 3530 if ( aoData._sRowStripe != sStripe ) 3531 { 3532 $(nRow).removeClass( aoData._sRowStripe ).addClass( sStripe ); 3533 aoData._sRowStripe = sStripe; 3534 } 3535 } 3536 3537 // Row callback functions - might want to manipulate the row 3538 // iRowCount and j are not currently documented. Are they at all 3539 // useful? 3540 _fnCallbackFire( oSettings, 'aoRowCallback', null, 3541 [nRow, aoData._aData, iRowCount, j, iDataIndex] ); 3542 3543 anRows.push( nRow ); 3544 iRowCount++; 3545 } 3546 } 3547 else 3548 { 3549 /* Table is empty - create a row with an empty message in it */ 3550 var sZero = oLang.sZeroRecords; 3551 if ( oSettings.iDraw == 1 && _fnDataSource( oSettings ) == 'ajax' ) 3552 { 3553 sZero = oLang.sLoadingRecords; 3554 } 3555 else if ( oLang.sEmptyTable && oSettings.fnRecordsTotal() === 0 ) 3556 { 3557 sZero = oLang.sEmptyTable; 3558 } 3559 3560 anRows[ 0 ] = $( '<tr/>', { 'class': iStripes ? asStripeClasses[0] : '' } ) 3561 .append( $('<td />', { 3562 'valign': 'top', 3563 'colSpan': _fnVisbleColumns( oSettings ), 3564 'class': oSettings.oClasses.sRowEmpty 3565 } ).html( sZero ) )[0]; 3566 } 3567 3568 /* Header and footer callbacks */ 3569 _fnCallbackFire( oSettings, 'aoHeaderCallback', 'header', [ $(oSettings.nTHead).children('tr')[0], 3570 _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] ); 3571 3572 _fnCallbackFire( oSettings, 'aoFooterCallback', 'footer', [ $(oSettings.nTFoot).children('tr')[0], 3573 _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] ); 3574 3575 var body = $(oSettings.nTBody); 3576 3577 body.children().detach(); 3578 body.append( $(anRows) ); 3579 3580 /* Call all required callback functions for the end of a draw */ 3581 _fnCallbackFire( oSettings, 'aoDrawCallback', 'draw', [oSettings] ); 3582 3583 /* Draw is complete, sorting and filtering must be as well */ 3584 oSettings.bSorted = false; 3585 oSettings.bFiltered = false; 3586 oSettings.bDrawing = false; 3587 } 3588 3589 3590 /** 3591 * Redraw the table - taking account of the various features which are enabled 3592 * @param {object} oSettings dataTables settings object 3593 * @param {boolean} [holdPosition] Keep the current paging position. By default 3594 * the paging is reset to the first page 3595 * @memberof DataTable#oApi 3596 */ 3597 function _fnReDraw( settings, holdPosition ) 3598 { 3599 var 3600 features = settings.oFeatures, 3601 sort = features.bSort, 3602 filter = features.bFilter; 3603 3604 if ( sort ) { 3605 _fnSort( settings ); 3606 } 3607 3608 if ( filter ) { 3609 _fnFilterComplete( settings, settings.oPreviousSearch ); 3610 } 3611 else { 3612 // No filtering, so we want to just use the display master 3613 settings.aiDisplay = settings.aiDisplayMaster.slice(); 3614 } 3615 3616 if ( holdPosition !== true ) { 3617 settings._iDisplayStart = 0; 3618 } 3619 3620 // Let any modules know about the draw hold position state (used by 3621 // scrolling internally) 3622 settings._drawHold = holdPosition; 3623 3624 _fnDraw( settings ); 3625 3626 settings._drawHold = false; 3627 } 3628 3629 3630 /** 3631 * Add the options to the page HTML for the table 3632 * @param {object} oSettings dataTables settings object 3633 * @memberof DataTable#oApi 3634 */ 3635 function _fnAddOptionsHtml ( oSettings ) 3636 { 3637 var classes = oSettings.oClasses; 3638 var table = $(oSettings.nTable); 3639 var holding = $('<div/>').insertBefore( table ); // Holding element for speed 3640 var features = oSettings.oFeatures; 3641 3642 // All DataTables are wrapped in a div 3643 var insert = $('<div/>', { 3644 id: oSettings.sTableId+'_wrapper', 3645 'class': classes.sWrapper + (oSettings.nTFoot ? '' : ' '+classes.sNoFooter) 3646 } ); 3647 3648 oSettings.nHolding = holding[0]; 3649 oSettings.nTableWrapper = insert[0]; 3650 oSettings.nTableReinsertBefore = oSettings.nTable.nextSibling; 3651 3652 /* Loop over the user set positioning and place the elements as needed */ 3653 var aDom = oSettings.sDom.split(''); 3654 var featureNode, cOption, nNewNode, cNext, sAttr, j; 3655 for ( var i=0 ; i<aDom.length ; i++ ) 3656 { 3657 featureNode = null; 3658 cOption = aDom[i]; 3659 3660 if ( cOption == '<' ) 3661 { 3662 /* New container div */ 3663 nNewNode = $('<div/>')[0]; 3664 3665 /* Check to see if we should append an id and/or a class name to the container */ 3666 cNext = aDom[i+1]; 3667 if ( cNext == "'" || cNext == '"' ) 3668 { 3669 sAttr = ""; 3670 j = 2; 3671 while ( aDom[i+j] != cNext ) 3672 { 3673 sAttr += aDom[i+j]; 3674 j++; 3675 } 3676 3677 /* Replace jQuery UI constants @todo depreciated */ 3678 if ( sAttr == "H" ) 3679 { 3680 sAttr = classes.sJUIHeader; 3681 } 3682 else if ( sAttr == "F" ) 3683 { 3684 sAttr = classes.sJUIFooter; 3685 } 3686 3687 /* The attribute can be in the format of "#id.class", "#id" or "class" This logic 3688 * breaks the string into parts and applies them as needed 3689 */ 3690 if ( sAttr.indexOf('.') != -1 ) 3691 { 3692 var aSplit = sAttr.split('.'); 3693 nNewNode.id = aSplit[0].substr(1, aSplit[0].length-1); 3694 nNewNode.className = aSplit[1]; 3695 } 3696 else if ( sAttr.charAt(0) == "#" ) 3697 { 3698 nNewNode.id = sAttr.substr(1, sAttr.length-1); 3699 } 3700 else 3701 { 3702 nNewNode.className = sAttr; 3703 } 3704 3705 i += j; /* Move along the position array */ 3706 } 3707 3708 insert.append( nNewNode ); 3709 insert = $(nNewNode); 3710 } 3711 else if ( cOption == '>' ) 3712 { 3713 /* End container div */ 3714 insert = insert.parent(); 3715 } 3716 // @todo Move options into their own plugins? 3717 else if ( cOption == 'l' && features.bPaginate && features.bLengthChange ) 3718 { 3719 /* Length */ 3720 featureNode = _fnFeatureHtmlLength( oSettings ); 3721 } 3722 else if ( cOption == 'f' && features.bFilter ) 3723 { 3724 /* Filter */ 3725 featureNode = _fnFeatureHtmlFilter( oSettings ); 3726 } 3727 else if ( cOption == 'r' && features.bProcessing ) 3728 { 3729 /* pRocessing */ 3730 featureNode = _fnFeatureHtmlProcessing( oSettings ); 3731 } 3732 else if ( cOption == 't' ) 3733 { 3734 /* Table */ 3735 featureNode = _fnFeatureHtmlTable( oSettings ); 3736 } 3737 else if ( cOption == 'i' && features.bInfo ) 3738 { 3739 /* Info */ 3740 featureNode = _fnFeatureHtmlInfo( oSettings ); 3741 } 3742 else if ( cOption == 'p' && features.bPaginate ) 3743 { 3744 /* Pagination */ 3745 featureNode = _fnFeatureHtmlPaginate( oSettings ); 3746 } 3747 else if ( DataTable.ext.feature.length !== 0 ) 3748 { 3749 /* Plug-in features */ 3750 var aoFeatures = DataTable.ext.feature; 3751 for ( var k=0, kLen=aoFeatures.length ; k<kLen ; k++ ) 3752 { 3753 if ( cOption == aoFeatures[k].cFeature ) 3754 { 3755 featureNode = aoFeatures[k].fnInit( oSettings ); 3756 break; 3757 } 3758 } 3759 } 3760 3761 /* Add to the 2D features array */ 3762 if ( featureNode ) 3763 { 3764 var aanFeatures = oSettings.aanFeatures; 3765 3766 if ( ! aanFeatures[cOption] ) 3767 { 3768 aanFeatures[cOption] = []; 3769 } 3770 3771 aanFeatures[cOption].push( featureNode ); 3772 insert.append( featureNode ); 3773 } 3774 } 3775 3776 /* Built our DOM structure - replace the holding div with what we want */ 3777 holding.replaceWith( insert ); 3778 oSettings.nHolding = null; 3779 } 3780 3781 3782 /** 3783 * Use the DOM source to create up an array of header cells. The idea here is to 3784 * create a layout grid (array) of rows x columns, which contains a reference 3785 * to the cell that that point in the grid (regardless of col/rowspan), such that 3786 * any column / row could be removed and the new grid constructed 3787 * @param array {object} aLayout Array to store the calculated layout in 3788 * @param {node} nThead The header/footer element for the table 3789 * @memberof DataTable#oApi 3790 */ 3791 function _fnDetectHeader ( aLayout, nThead ) 3792 { 3793 var nTrs = $(nThead).children('tr'); 3794 var nTr, nCell; 3795 var i, k, l, iLen, jLen, iColShifted, iColumn, iColspan, iRowspan; 3796 var bUnique; 3797 var fnShiftCol = function ( a, i, j ) { 3798 var k = a[i]; 3799 while ( k[j] ) { 3800 j++; 3801 } 3802 return j; 3803 }; 3804 3805 aLayout.splice( 0, aLayout.length ); 3806 3807 /* We know how many rows there are in the layout - so prep it */ 3808 for ( i=0, iLen=nTrs.length ; i<iLen ; i++ ) 3809 { 3810 aLayout.push( [] ); 3811 } 3812 3813 /* Calculate a layout array */ 3814 for ( i=0, iLen=nTrs.length ; i<iLen ; i++ ) 3815 { 3816 nTr = nTrs[i]; 3817 iColumn = 0; 3818 3819 /* For every cell in the row... */ 3820 nCell = nTr.firstChild; 3821 while ( nCell ) { 3822 if ( nCell.nodeName.toUpperCase() == "TD" || 3823 nCell.nodeName.toUpperCase() == "TH" ) 3824 { 3825 /* Get the col and rowspan attributes from the DOM and sanitise them */ 3826 iColspan = nCell.getAttribute('colspan') * 1; 3827 iRowspan = nCell.getAttribute('rowspan') * 1; 3828 iColspan = (!iColspan || iColspan===0 || iColspan===1) ? 1 : iColspan; 3829 iRowspan = (!iRowspan || iRowspan===0 || iRowspan===1) ? 1 : iRowspan; 3830 3831 /* There might be colspan cells already in this row, so shift our target 3832 * accordingly 3833 */ 3834 iColShifted = fnShiftCol( aLayout, i, iColumn ); 3835 3836 /* Cache calculation for unique columns */ 3837 bUnique = iColspan === 1 ? true : false; 3838 3839 /* If there is col / rowspan, copy the information into the layout grid */ 3840 for ( l=0 ; l<iColspan ; l++ ) 3841 { 3842 for ( k=0 ; k<iRowspan ; k++ ) 3843 { 3844 aLayout[i+k][iColShifted+l] = { 3845 "cell": nCell, 3846 "unique": bUnique 3847 }; 3848 aLayout[i+k].nTr = nTr; 3849 } 3850 } 3851 } 3852 nCell = nCell.nextSibling; 3853 } 3854 } 3855 } 3856 3857 3858 /** 3859 * Get an array of unique th elements, one for each column 3860 * @param {object} oSettings dataTables settings object 3861 * @param {node} nHeader automatically detect the layout from this node - optional 3862 * @param {array} aLayout thead/tfoot layout from _fnDetectHeader - optional 3863 * @returns array {node} aReturn list of unique th's 3864 * @memberof DataTable#oApi 3865 */ 3866 function _fnGetUniqueThs ( oSettings, nHeader, aLayout ) 3867 { 3868 var aReturn = []; 3869 if ( !aLayout ) 3870 { 3871 aLayout = oSettings.aoHeader; 3872 if ( nHeader ) 3873 { 3874 aLayout = []; 3875 _fnDetectHeader( aLayout, nHeader ); 3876 } 3877 } 3878 3879 for ( var i=0, iLen=aLayout.length ; i<iLen ; i++ ) 3880 { 3881 for ( var j=0, jLen=aLayout[i].length ; j<jLen ; j++ ) 3882 { 3883 if ( aLayout[i][j].unique && 3884 (!aReturn[j] || !oSettings.bSortCellsTop) ) 3885 { 3886 aReturn[j] = aLayout[i][j].cell; 3887 } 3888 } 3889 } 3890 3891 return aReturn; 3892 } 3893 3894 /** 3895 * Set the start position for draw 3896 * @param {object} oSettings dataTables settings object 3897 */ 3898 function _fnStart( oSettings ) 3899 { 3900 var bServerSide = _fnDataSource( oSettings ) == 'ssp'; 3901 var iInitDisplayStart = oSettings.iInitDisplayStart; 3902 3903 // Check and see if we have an initial draw position from state saving 3904 if ( iInitDisplayStart !== undefined && iInitDisplayStart !== -1 ) 3905 { 3906 oSettings._iDisplayStart = bServerSide ? 3907 iInitDisplayStart : 3908 iInitDisplayStart >= oSettings.fnRecordsDisplay() ? 3909 0 : 3910 iInitDisplayStart; 3911 3912 oSettings.iInitDisplayStart = -1; 3913 } 3914 } 3915 3916 /** 3917 * Create an Ajax call based on the table's settings, taking into account that 3918 * parameters can have multiple forms, and backwards compatibility. 3919 * 3920 * @param {object} oSettings dataTables settings object 3921 * @param {array} data Data to send to the server, required by 3922 * DataTables - may be augmented by developer callbacks 3923 * @param {function} fn Callback function to run when data is obtained 3924 */ 3925 function _fnBuildAjax( oSettings, data, fn ) 3926 { 3927 // Compatibility with 1.9-, allow fnServerData and event to manipulate 3928 _fnCallbackFire( oSettings, 'aoServerParams', 'serverParams', [data] ); 3929 3930 // Convert to object based for 1.10+ if using the old array scheme which can 3931 // come from server-side processing or serverParams 3932 if ( data && Array.isArray(data) ) { 3933 var tmp = {}; 3934 var rbracket = /(.*?)\[\]$/; 3935 3936 $.each( data, function (key, val) { 3937 var match = val.name.match(rbracket); 3938 3939 if ( match ) { 3940 // Support for arrays 3941 var name = match[0]; 3942 3943 if ( ! tmp[ name ] ) { 3944 tmp[ name ] = []; 3945 } 3946 tmp[ name ].push( val.value ); 3947 } 3948 else { 3949 tmp[val.name] = val.value; 3950 } 3951 } ); 3952 data = tmp; 3953 } 3954 3955 var ajaxData; 3956 var ajax = oSettings.ajax; 3957 var instance = oSettings.oInstance; 3958 var callback = function ( json ) { 3959 var status = oSettings.jqXHR 3960 ? oSettings.jqXHR.status 3961 : null; 3962 3963 if ( json === null || (typeof status === 'number' && status == 204 ) ) { 3964 json = {}; 3965 _fnAjaxDataSrc( oSettings, json, [] ); 3966 } 3967 3968 var error = json.error || json.sError; 3969 if ( error ) { 3970 _fnLog( oSettings, 0, error ); 3971 } 3972 3973 oSettings.json = json; 3974 3975 _fnCallbackFire( oSettings, null, 'xhr', [oSettings, json, oSettings.jqXHR] ); 3976 fn( json ); 3977 }; 3978 3979 if ( $.isPlainObject( ajax ) && ajax.data ) 3980 { 3981 ajaxData = ajax.data; 3982 3983 var newData = typeof ajaxData === 'function' ? 3984 ajaxData( data, oSettings ) : // fn can manipulate data or return 3985 ajaxData; // an object object or array to merge 3986 3987 // If the function returned something, use that alone 3988 data = typeof ajaxData === 'function' && newData ? 3989 newData : 3990 $.extend( true, data, newData ); 3991 3992 // Remove the data property as we've resolved it already and don't want 3993 // jQuery to do it again (it is restored at the end of the function) 3994 delete ajax.data; 3995 } 3996 3997 var baseAjax = { 3998 "data": data, 3999 "success": callback, 4000 "dataType": "json", 4001 "cache": false, 4002 "type": oSettings.sServerMethod, 4003 "error": function (xhr, error, thrown) { 4004 var ret = _fnCallbackFire( oSettings, null, 'xhr', [oSettings, null, oSettings.jqXHR] ); 4005 4006 if ( $.inArray( true, ret ) === -1 ) { 4007 if ( error == "parsererror" ) { 4008 _fnLog( oSettings, 0, 'Invalid JSON response', 1 ); 4009 } 4010 else if ( xhr.readyState === 4 ) { 4011 _fnLog( oSettings, 0, 'Ajax error', 7 ); 4012 } 4013 } 4014 4015 _fnProcessingDisplay( oSettings, false ); 4016 } 4017 }; 4018 4019 // Store the data submitted for the API 4020 oSettings.oAjaxData = data; 4021 4022 // Allow plug-ins and external processes to modify the data 4023 _fnCallbackFire( oSettings, null, 'preXhr', [oSettings, data] ); 4024 4025 if ( oSettings.fnServerData ) 4026 { 4027 // DataTables 1.9- compatibility 4028 oSettings.fnServerData.call( instance, 4029 oSettings.sAjaxSource, 4030 $.map( data, function (val, key) { // Need to convert back to 1.9 trad format 4031 return { name: key, value: val }; 4032 } ), 4033 callback, 4034 oSettings 4035 ); 4036 } 4037 else if ( oSettings.sAjaxSource || typeof ajax === 'string' ) 4038 { 4039 // DataTables 1.9- compatibility 4040 oSettings.jqXHR = $.ajax( $.extend( baseAjax, { 4041 url: ajax || oSettings.sAjaxSource 4042 } ) ); 4043 } 4044 else if ( typeof ajax === 'function' ) 4045 { 4046 // Is a function - let the caller define what needs to be done 4047 oSettings.jqXHR = ajax.call( instance, data, callback, oSettings ); 4048 } 4049 else 4050 { 4051 // Object to extend the base settings 4052 oSettings.jqXHR = $.ajax( $.extend( baseAjax, ajax ) ); 4053 4054 // Restore for next time around 4055 ajax.data = ajaxData; 4056 } 4057 } 4058 4059 4060 /** 4061 * Update the table using an Ajax call 4062 * @param {object} settings dataTables settings object 4063 * @returns {boolean} Block the table drawing or not 4064 * @memberof DataTable#oApi 4065 */ 4066 function _fnAjaxUpdate( settings ) 4067 { 4068 settings.iDraw++; 4069 _fnProcessingDisplay( settings, true ); 4070 4071 // Keep track of drawHold state to handle scrolling after the Ajax call 4072 var drawHold = settings._drawHold; 4073 4074 _fnBuildAjax( 4075 settings, 4076 _fnAjaxParameters( settings ), 4077 function(json) { 4078 settings._drawHold = drawHold; 4079 _fnAjaxUpdateDraw( settings, json ); 4080 settings._drawHold = false; 4081 } 4082 ); 4083 } 4084 4085 4086 /** 4087 * Build up the parameters in an object needed for a server-side processing 4088 * request. Note that this is basically done twice, is different ways - a modern 4089 * method which is used by default in DataTables 1.10 which uses objects and 4090 * arrays, or the 1.9- method with is name / value pairs. 1.9 method is used if 4091 * the sAjaxSource option is used in the initialisation, or the legacyAjax 4092 * option is set. 4093 * @param {object} oSettings dataTables settings object 4094 * @returns {bool} block the table drawing or not 4095 * @memberof DataTable#oApi 4096 */ 4097 function _fnAjaxParameters( settings ) 4098 { 4099 var 4100 columns = settings.aoColumns, 4101 columnCount = columns.length, 4102 features = settings.oFeatures, 4103 preSearch = settings.oPreviousSearch, 4104 preColSearch = settings.aoPreSearchCols, 4105 i, data = [], dataProp, column, columnSearch, 4106 sort = _fnSortFlatten( settings ), 4107 displayStart = settings._iDisplayStart, 4108 displayLength = features.bPaginate !== false ? 4109 settings._iDisplayLength : 4110 -1; 4111 4112 var param = function ( name, value ) { 4113 data.push( { 'name': name, 'value': value } ); 4114 }; 4115 4116 // DataTables 1.9- compatible method 4117 param( 'sEcho', settings.iDraw ); 4118 param( 'iColumns', columnCount ); 4119 param( 'sColumns', _pluck( columns, 'sName' ).join(',') ); 4120 param( 'iDisplayStart', displayStart ); 4121 param( 'iDisplayLength', displayLength ); 4122 4123 // DataTables 1.10+ method 4124 var d = { 4125 draw: settings.iDraw, 4126 columns: [], 4127 order: [], 4128 start: displayStart, 4129 length: displayLength, 4130 search: { 4131 value: preSearch.sSearch, 4132 regex: preSearch.bRegex 4133 } 4134 }; 4135 4136 for ( i=0 ; i<columnCount ; i++ ) { 4137 column = columns[i]; 4138 columnSearch = preColSearch[i]; 4139 dataProp = typeof column.mData=="function" ? 'function' : column.mData ; 4140 4141 d.columns.push( { 4142 data: dataProp, 4143 name: column.sName, 4144 searchable: column.bSearchable, 4145 orderable: column.bSortable, 4146 search: { 4147 value: columnSearch.sSearch, 4148 regex: columnSearch.bRegex 4149 } 4150 } ); 4151 4152 param( "mDataProp_"+i, dataProp ); 4153 4154 if ( features.bFilter ) { 4155 param( 'sSearch_'+i, columnSearch.sSearch ); 4156 param( 'bRegex_'+i, columnSearch.bRegex ); 4157 param( 'bSearchable_'+i, column.bSearchable ); 4158 } 4159 4160 if ( features.bSort ) { 4161 param( 'bSortable_'+i, column.bSortable ); 4162 } 4163 } 4164 4165 if ( features.bFilter ) { 4166 param( 'sSearch', preSearch.sSearch ); 4167 param( 'bRegex', preSearch.bRegex ); 4168 } 4169 4170 if ( features.bSort ) { 4171 $.each( sort, function ( i, val ) { 4172 d.order.push( { column: val.col, dir: val.dir } ); 4173 4174 param( 'iSortCol_'+i, val.col ); 4175 param( 'sSortDir_'+i, val.dir ); 4176 } ); 4177 4178 param( 'iSortingCols', sort.length ); 4179 } 4180 4181 // If the legacy.ajax parameter is null, then we automatically decide which 4182 // form to use, based on sAjaxSource 4183 var legacy = DataTable.ext.legacy.ajax; 4184 if ( legacy === null ) { 4185 return settings.sAjaxSource ? data : d; 4186 } 4187 4188 // Otherwise, if legacy has been specified then we use that to decide on the 4189 // form 4190 return legacy ? data : d; 4191 } 4192 4193 4194 /** 4195 * Data the data from the server (nuking the old) and redraw the table 4196 * @param {object} oSettings dataTables settings object 4197 * @param {object} json json data return from the server. 4198 * @param {string} json.sEcho Tracking flag for DataTables to match requests 4199 * @param {int} json.iTotalRecords Number of records in the data set, not accounting for filtering 4200 * @param {int} json.iTotalDisplayRecords Number of records in the data set, accounting for filtering 4201 * @param {array} json.aaData The data to display on this page 4202 * @param {string} [json.sColumns] Column ordering (sName, comma separated) 4203 * @memberof DataTable#oApi 4204 */ 4205 function _fnAjaxUpdateDraw ( settings, json ) 4206 { 4207 // v1.10 uses camelCase variables, while 1.9 uses Hungarian notation. 4208 // Support both 4209 var compat = function ( old, modern ) { 4210 return json[old] !== undefined ? json[old] : json[modern]; 4211 }; 4212 4213 var data = _fnAjaxDataSrc( settings, json ); 4214 var draw = compat( 'sEcho', 'draw' ); 4215 var recordsTotal = compat( 'iTotalRecords', 'recordsTotal' ); 4216 var recordsFiltered = compat( 'iTotalDisplayRecords', 'recordsFiltered' ); 4217 4218 if ( draw !== undefined ) { 4219 // Protect against out of sequence returns 4220 if ( draw*1 < settings.iDraw ) { 4221 return; 4222 } 4223 settings.iDraw = draw * 1; 4224 } 4225 4226 // No data in returned object, so rather than an array, we show an empty table 4227 if ( ! data ) { 4228 data = []; 4229 } 4230 4231 _fnClearTable( settings ); 4232 settings._iRecordsTotal = parseInt(recordsTotal, 10); 4233 settings._iRecordsDisplay = parseInt(recordsFiltered, 10); 4234 4235 for ( var i=0, ien=data.length ; i<ien ; i++ ) { 4236 _fnAddData( settings, data[i] ); 4237 } 4238 settings.aiDisplay = settings.aiDisplayMaster.slice(); 4239 4240 _fnDraw( settings, true ); 4241 4242 if ( ! settings._bInitComplete ) { 4243 _fnInitComplete( settings, json ); 4244 } 4245 4246 _fnProcessingDisplay( settings, false ); 4247 } 4248 4249 4250 /** 4251 * Get the data from the JSON data source to use for drawing a table. Using 4252 * `_fnGetObjectDataFn` allows the data to be sourced from a property of the 4253 * source object, or from a processing function. 4254 * @param {object} oSettings dataTables settings object 4255 * @param {object} json Data source object / array from the server 4256 * @return {array} Array of data to use 4257 */ 4258 function _fnAjaxDataSrc ( oSettings, json, write ) 4259 { 4260 var dataSrc = $.isPlainObject( oSettings.ajax ) && oSettings.ajax.dataSrc !== undefined ? 4261 oSettings.ajax.dataSrc : 4262 oSettings.sAjaxDataProp; // Compatibility with 1.9-. 4263 4264 if ( ! write ) { 4265 if ( dataSrc === 'data' ) { 4266 // If the default, then we still want to support the old style, and safely ignore 4267 // it if possible 4268 return json.aaData || json[dataSrc]; 4269 } 4270 4271 return dataSrc !== "" ? 4272 _fnGetObjectDataFn( dataSrc )( json ) : 4273 json; 4274 } 4275 4276 // set 4277 _fnSetObjectDataFn( dataSrc )( json, write ); 4278 } 4279 4280 /** 4281 * Generate the node required for filtering text 4282 * @returns {node} Filter control element 4283 * @param {object} oSettings dataTables settings object 4284 * @memberof DataTable#oApi 4285 */ 4286 function _fnFeatureHtmlFilter ( settings ) 4287 { 4288 var classes = settings.oClasses; 4289 var tableId = settings.sTableId; 4290 var language = settings.oLanguage; 4291 var previousSearch = settings.oPreviousSearch; 4292 var features = settings.aanFeatures; 4293 var input = '<input type="search" class="'+classes.sFilterInput+'"/>'; 4294 4295 var str = language.sSearch; 4296 str = str.match(/_INPUT_/) ? 4297 str.replace('_INPUT_', input) : 4298 str+input; 4299 4300 var filter = $('<div/>', { 4301 'id': ! features.f ? tableId+'_filter' : null, 4302 'class': classes.sFilter 4303 } ) 4304 .append( $('<label/>' ).append( str ) ); 4305 4306 var searchFn = function(event) { 4307 /* Update all other filter input elements for the new display */ 4308 var n = features.f; 4309 var val = !this.value ? "" : this.value; // mental IE8 fix :-( 4310 if(previousSearch.return && event.key !== "Enter") { 4311 return; 4312 } 4313 /* Now do the filter */ 4314 if ( val != previousSearch.sSearch ) { 4315 _fnFilterComplete( settings, { 4316 "sSearch": val, 4317 "bRegex": previousSearch.bRegex, 4318 "bSmart": previousSearch.bSmart , 4319 "bCaseInsensitive": previousSearch.bCaseInsensitive, 4320 "return": previousSearch.return 4321 } ); 4322 4323 // Need to redraw, without resorting 4324 settings._iDisplayStart = 0; 4325 _fnDraw( settings ); 4326 } 4327 }; 4328 4329 var searchDelay = settings.searchDelay !== null ? 4330 settings.searchDelay : 4331 _fnDataSource( settings ) === 'ssp' ? 4332 400 : 4333 0; 4334 4335 var jqFilter = $('input', filter) 4336 .val( previousSearch.sSearch ) 4337 .attr( 'placeholder', language.sSearchPlaceholder ) 4338 .on( 4339 'keyup.DT search.DT input.DT paste.DT cut.DT', 4340 searchDelay ? 4341 _fnThrottle( searchFn, searchDelay ) : 4342 searchFn 4343 ) 4344 .on( 'mouseup.DT', function(e) { 4345 // Edge fix! Edge 17 does not trigger anything other than mouse events when clicking 4346 // on the clear icon (Edge bug 17584515). This is safe in other browsers as `searchFn` 4347 // checks the value to see if it has changed. In other browsers it won't have. 4348 setTimeout( function () { 4349 searchFn.call(jqFilter[0], e); 4350 }, 10); 4351 } ) 4352 .on( 'keypress.DT', function(e) { 4353 /* Prevent form submission */ 4354 if ( e.keyCode == 13 ) { 4355 return false; 4356 } 4357 } ) 4358 .attr('aria-controls', tableId); 4359 4360 // Update the input elements whenever the table is filtered 4361 $(settings.nTable).on( 'search.dt.DT', function ( ev, s ) { 4362 if ( settings === s ) { 4363 // IE9 throws an 'unknown error' if document.activeElement is used 4364 // inside an iframe or frame... 4365 try { 4366 if ( jqFilter[0] !== document.activeElement ) { 4367 jqFilter.val( previousSearch.sSearch ); 4368 } 4369 } 4370 catch ( e ) {} 4371 } 4372 } ); 4373 4374 return filter[0]; 4375 } 4376 4377 4378 /** 4379 * Filter the table using both the global filter and column based filtering 4380 * @param {object} oSettings dataTables settings object 4381 * @param {object} oSearch search information 4382 * @param {int} [iForce] force a research of the master array (1) or not (undefined or 0) 4383 * @memberof DataTable#oApi 4384 */ 4385 function _fnFilterComplete ( oSettings, oInput, iForce ) 4386 { 4387 var oPrevSearch = oSettings.oPreviousSearch; 4388 var aoPrevSearch = oSettings.aoPreSearchCols; 4389 var fnSaveFilter = function ( oFilter ) { 4390 /* Save the filtering values */ 4391 oPrevSearch.sSearch = oFilter.sSearch; 4392 oPrevSearch.bRegex = oFilter.bRegex; 4393 oPrevSearch.bSmart = oFilter.bSmart; 4394 oPrevSearch.bCaseInsensitive = oFilter.bCaseInsensitive; 4395 oPrevSearch.return = oFilter.return; 4396 }; 4397 var fnRegex = function ( o ) { 4398 // Backwards compatibility with the bEscapeRegex option 4399 return o.bEscapeRegex !== undefined ? !o.bEscapeRegex : o.bRegex; 4400 }; 4401 4402 // Resolve any column types that are unknown due to addition or invalidation 4403 // @todo As per sort - can this be moved into an event handler? 4404 _fnColumnTypes( oSettings ); 4405 4406 /* In server-side processing all filtering is done by the server, so no point hanging around here */ 4407 if ( _fnDataSource( oSettings ) != 'ssp' ) 4408 { 4409 /* Global filter */ 4410 _fnFilter( oSettings, oInput.sSearch, iForce, fnRegex(oInput), oInput.bSmart, oInput.bCaseInsensitive ); 4411 fnSaveFilter( oInput ); 4412 4413 /* Now do the individual column filter */ 4414 for ( var i=0 ; i<aoPrevSearch.length ; i++ ) 4415 { 4416 _fnFilterColumn( oSettings, aoPrevSearch[i].sSearch, i, fnRegex(aoPrevSearch[i]), 4417 aoPrevSearch[i].bSmart, aoPrevSearch[i].bCaseInsensitive ); 4418 } 4419 4420 /* Custom filtering */ 4421 _fnFilterCustom( oSettings ); 4422 } 4423 else 4424 { 4425 fnSaveFilter( oInput ); 4426 } 4427 4428 /* Tell the draw function we have been filtering */ 4429 oSettings.bFiltered = true; 4430 _fnCallbackFire( oSettings, null, 'search', [oSettings] ); 4431 } 4432 4433 4434 /** 4435 * Apply custom filtering functions 4436 * @param {object} oSettings dataTables settings object 4437 * @memberof DataTable#oApi 4438 */ 4439 function _fnFilterCustom( settings ) 4440 { 4441 var filters = DataTable.ext.search; 4442 var displayRows = settings.aiDisplay; 4443 var row, rowIdx; 4444 4445 for ( var i=0, ien=filters.length ; i<ien ; i++ ) { 4446 var rows = []; 4447 4448 // Loop over each row and see if it should be included 4449 for ( var j=0, jen=displayRows.length ; j<jen ; j++ ) { 4450 rowIdx = displayRows[ j ]; 4451 row = settings.aoData[ rowIdx ]; 4452 4453 if ( filters[i]( settings, row._aFilterData, rowIdx, row._aData, j ) ) { 4454 rows.push( rowIdx ); 4455 } 4456 } 4457 4458 // So the array reference doesn't break set the results into the 4459 // existing array 4460 displayRows.length = 0; 4461 $.merge( displayRows, rows ); 4462 } 4463 } 4464 4465 4466 /** 4467 * Filter the table on a per-column basis 4468 * @param {object} oSettings dataTables settings object 4469 * @param {string} sInput string to filter on 4470 * @param {int} iColumn column to filter 4471 * @param {bool} bRegex treat search string as a regular expression or not 4472 * @param {bool} bSmart use smart filtering or not 4473 * @param {bool} bCaseInsensitive Do case insensitive matching or not 4474 * @memberof DataTable#oApi 4475 */ 4476 function _fnFilterColumn ( settings, searchStr, colIdx, regex, smart, caseInsensitive ) 4477 { 4478 if ( searchStr === '' ) { 4479 return; 4480 } 4481 4482 var data; 4483 var out = []; 4484 var display = settings.aiDisplay; 4485 var rpSearch = _fnFilterCreateSearch( searchStr, regex, smart, caseInsensitive ); 4486 4487 for ( var i=0 ; i<display.length ; i++ ) { 4488 data = settings.aoData[ display[i] ]._aFilterData[ colIdx ]; 4489 4490 if ( rpSearch.test( data ) ) { 4491 out.push( display[i] ); 4492 } 4493 } 4494 4495 settings.aiDisplay = out; 4496 } 4497 4498 4499 /** 4500 * Filter the data table based on user input and draw the table 4501 * @param {object} settings dataTables settings object 4502 * @param {string} input string to filter on 4503 * @param {int} force optional - force a research of the master array (1) or not (undefined or 0) 4504 * @param {bool} regex treat as a regular expression or not 4505 * @param {bool} smart perform smart filtering or not 4506 * @param {bool} caseInsensitive Do case insensitive matching or not 4507 * @memberof DataTable#oApi 4508 */ 4509 function _fnFilter( settings, input, force, regex, smart, caseInsensitive ) 4510 { 4511 var rpSearch = _fnFilterCreateSearch( input, regex, smart, caseInsensitive ); 4512 var prevSearch = settings.oPreviousSearch.sSearch; 4513 var displayMaster = settings.aiDisplayMaster; 4514 var display, invalidated, i; 4515 var filtered = []; 4516 4517 // Need to take account of custom filtering functions - always filter 4518 if ( DataTable.ext.search.length !== 0 ) { 4519 force = true; 4520 } 4521 4522 // Check if any of the rows were invalidated 4523 invalidated = _fnFilterData( settings ); 4524 4525 // If the input is blank - we just want the full data set 4526 if ( input.length <= 0 ) { 4527 settings.aiDisplay = displayMaster.slice(); 4528 } 4529 else { 4530 // New search - start from the master array 4531 if ( invalidated || 4532 force || 4533 regex || 4534 prevSearch.length > input.length || 4535 input.indexOf(prevSearch) !== 0 || 4536 settings.bSorted // On resort, the display master needs to be 4537 // re-filtered since indexes will have changed 4538 ) { 4539 settings.aiDisplay = displayMaster.slice(); 4540 } 4541 4542 // Search the display array 4543 display = settings.aiDisplay; 4544 4545 for ( i=0 ; i<display.length ; i++ ) { 4546 if ( rpSearch.test( settings.aoData[ display[i] ]._sFilterRow ) ) { 4547 filtered.push( display[i] ); 4548 } 4549 } 4550 4551 settings.aiDisplay = filtered; 4552 } 4553 } 4554 4555 4556 /** 4557 * Build a regular expression object suitable for searching a table 4558 * @param {string} sSearch string to search for 4559 * @param {bool} bRegex treat as a regular expression or not 4560 * @param {bool} bSmart perform smart filtering or not 4561 * @param {bool} bCaseInsensitive Do case insensitive matching or not 4562 * @returns {RegExp} constructed object 4563 * @memberof DataTable#oApi 4564 */ 4565 function _fnFilterCreateSearch( search, regex, smart, caseInsensitive ) 4566 { 4567 search = regex ? 4568 search : 4569 _fnEscapeRegex( search ); 4570 4571 if ( smart ) { 4572 /* For smart filtering we want to allow the search to work regardless of 4573 * word order. We also want double quoted text to be preserved, so word 4574 * order is important - a la google. So this is what we want to 4575 * generate: 4576 * 4577 * ^(?=.*?\bone\b)(?=.*?\btwo three\b)(?=.*?\bfour\b).*$ 4578 */ 4579 var a = $.map( search.match( /["\u201C][^"\u201D]+["\u201D]|[^ ]+/g ) || [''], function ( word ) { 4580 if ( word.charAt(0) === '"' ) { 4581 var m = word.match( /^"(.*)"$/ ); 4582 word = m ? m[1] : word; 4583 } 4584 else if ( word.charAt(0) === '\u201C' ) { 4585 var m = word.match( /^\u201C(.*)\u201D$/ ); 4586 word = m ? m[1] : word; 4587 } 4588 4589 return word.replace('"', ''); 4590 } ); 4591 4592 search = '^(?=.*?'+a.join( ')(?=.*?' )+').*$'; 4593 } 4594 4595 return new RegExp( search, caseInsensitive ? 'i' : '' ); 4596 } 4597 4598 4599 /** 4600 * Escape a string such that it can be used in a regular expression 4601 * @param {string} sVal string to escape 4602 * @returns {string} escaped string 4603 * @memberof DataTable#oApi 4604 */ 4605 var _fnEscapeRegex = DataTable.util.escapeRegex; 4606 4607 var __filter_div = $('<div>')[0]; 4608 var __filter_div_textContent = __filter_div.textContent !== undefined; 4609 4610 // Update the filtering data for each row if needed (by invalidation or first run) 4611 function _fnFilterData ( settings ) 4612 { 4613 var columns = settings.aoColumns; 4614 var column; 4615 var i, j, ien, jen, filterData, cellData, row; 4616 var wasInvalidated = false; 4617 4618 for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) { 4619 row = settings.aoData[i]; 4620 4621 if ( ! row._aFilterData ) { 4622 filterData = []; 4623 4624 for ( j=0, jen=columns.length ; j<jen ; j++ ) { 4625 column = columns[j]; 4626 4627 if ( column.bSearchable ) { 4628 cellData = _fnGetCellData( settings, i, j, 'filter' ); 4629 4630 // Search in DataTables 1.10 is string based. In 1.11 this 4631 // should be altered to also allow strict type checking. 4632 if ( cellData === null ) { 4633 cellData = ''; 4634 } 4635 4636 if ( typeof cellData !== 'string' && cellData.toString ) { 4637 cellData = cellData.toString(); 4638 } 4639 } 4640 else { 4641 cellData = ''; 4642 } 4643 4644 // If it looks like there is an HTML entity in the string, 4645 // attempt to decode it so sorting works as expected. Note that 4646 // we could use a single line of jQuery to do this, but the DOM 4647 // method used here is much faster http://jsperf.com/html-decode 4648 if ( cellData.indexOf && cellData.indexOf('&') !== -1 ) { 4649 __filter_div.innerHTML = cellData; 4650 cellData = __filter_div_textContent ? 4651 __filter_div.textContent : 4652 __filter_div.innerText; 4653 } 4654 4655 if ( cellData.replace ) { 4656 cellData = cellData.replace(/[\r\n\u2028]/g, ''); 4657 } 4658 4659 filterData.push( cellData ); 4660 } 4661 4662 row._aFilterData = filterData; 4663 row._sFilterRow = filterData.join(' '); 4664 wasInvalidated = true; 4665 } 4666 } 4667 4668 return wasInvalidated; 4669 } 4670 4671 4672 /** 4673 * Convert from the internal Hungarian notation to camelCase for external 4674 * interaction 4675 * @param {object} obj Object to convert 4676 * @returns {object} Inverted object 4677 * @memberof DataTable#oApi 4678 */ 4679 function _fnSearchToCamel ( obj ) 4680 { 4681 return { 4682 search: obj.sSearch, 4683 smart: obj.bSmart, 4684 regex: obj.bRegex, 4685 caseInsensitive: obj.bCaseInsensitive 4686 }; 4687 } 4688 4689 4690 4691 /** 4692 * Convert from camelCase notation to the internal Hungarian. We could use the 4693 * Hungarian convert function here, but this is cleaner 4694 * @param {object} obj Object to convert 4695 * @returns {object} Inverted object 4696 * @memberof DataTable#oApi 4697 */ 4698 function _fnSearchToHung ( obj ) 4699 { 4700 return { 4701 sSearch: obj.search, 4702 bSmart: obj.smart, 4703 bRegex: obj.regex, 4704 bCaseInsensitive: obj.caseInsensitive 4705 }; 4706 } 4707 4708 /** 4709 * Generate the node required for the info display 4710 * @param {object} oSettings dataTables settings object 4711 * @returns {node} Information element 4712 * @memberof DataTable#oApi 4713 */ 4714 function _fnFeatureHtmlInfo ( settings ) 4715 { 4716 var 4717 tid = settings.sTableId, 4718 nodes = settings.aanFeatures.i, 4719 n = $('<div/>', { 4720 'class': settings.oClasses.sInfo, 4721 'id': ! nodes ? tid+'_info' : null 4722 } ); 4723 4724 if ( ! nodes ) { 4725 // Update display on each draw 4726 settings.aoDrawCallback.push( { 4727 "fn": _fnUpdateInfo, 4728 "sName": "information" 4729 } ); 4730 4731 n 4732 .attr( 'role', 'status' ) 4733 .attr( 'aria-live', 'polite' ); 4734 4735 // Table is described by our info div 4736 $(settings.nTable).attr( 'aria-describedby', tid+'_info' ); 4737 } 4738 4739 return n[0]; 4740 } 4741 4742 4743 /** 4744 * Update the information elements in the display 4745 * @param {object} settings dataTables settings object 4746 * @memberof DataTable#oApi 4747 */ 4748 function _fnUpdateInfo ( settings ) 4749 { 4750 /* Show information about the table */ 4751 var nodes = settings.aanFeatures.i; 4752 if ( nodes.length === 0 ) { 4753 return; 4754 } 4755 4756 var 4757 lang = settings.oLanguage, 4758 start = settings._iDisplayStart+1, 4759 end = settings.fnDisplayEnd(), 4760 max = settings.fnRecordsTotal(), 4761 total = settings.fnRecordsDisplay(), 4762 out = total ? 4763 lang.sInfo : 4764 lang.sInfoEmpty; 4765 4766 if ( total !== max ) { 4767 /* Record set after filtering */ 4768 out += ' ' + lang.sInfoFiltered; 4769 } 4770 4771 // Convert the macros 4772 out += lang.sInfoPostFix; 4773 out = _fnInfoMacros( settings, out ); 4774 4775 var callback = lang.fnInfoCallback; 4776 if ( callback !== null ) { 4777 out = callback.call( settings.oInstance, 4778 settings, start, end, max, total, out 4779 ); 4780 } 4781 4782 $(nodes).html( out ); 4783 } 4784 4785 4786 function _fnInfoMacros ( settings, str ) 4787 { 4788 // When infinite scrolling, we are always starting at 1. _iDisplayStart is used only 4789 // internally 4790 var 4791 formatter = settings.fnFormatNumber, 4792 start = settings._iDisplayStart+1, 4793 len = settings._iDisplayLength, 4794 vis = settings.fnRecordsDisplay(), 4795 all = len === -1; 4796 4797 return str. 4798 replace(/_START_/g, formatter.call( settings, start ) ). 4799 replace(/_END_/g, formatter.call( settings, settings.fnDisplayEnd() ) ). 4800 replace(/_MAX_/g, formatter.call( settings, settings.fnRecordsTotal() ) ). 4801 replace(/_TOTAL_/g, formatter.call( settings, vis ) ). 4802 replace(/_PAGE_/g, formatter.call( settings, all ? 1 : Math.ceil( start / len ) ) ). 4803 replace(/_PAGES_/g, formatter.call( settings, all ? 1 : Math.ceil( vis / len ) ) ); 4804 } 4805 4806 4807 4808 /** 4809 * Draw the table for the first time, adding all required features 4810 * @param {object} settings dataTables settings object 4811 * @memberof DataTable#oApi 4812 */ 4813 function _fnInitialise ( settings ) 4814 { 4815 var i, iLen, iAjaxStart=settings.iInitDisplayStart; 4816 var columns = settings.aoColumns, column; 4817 var features = settings.oFeatures; 4818 var deferLoading = settings.bDeferLoading; // value modified by the draw 4819 4820 /* Ensure that the table data is fully initialised */ 4821 if ( ! settings.bInitialised ) { 4822 setTimeout( function(){ _fnInitialise( settings ); }, 200 ); 4823 return; 4824 } 4825 4826 /* Show the display HTML options */ 4827 _fnAddOptionsHtml( settings ); 4828 4829 /* Build and draw the header / footer for the table */ 4830 _fnBuildHead( settings ); 4831 _fnDrawHead( settings, settings.aoHeader ); 4832 _fnDrawHead( settings, settings.aoFooter ); 4833 4834 /* Okay to show that something is going on now */ 4835 _fnProcessingDisplay( settings, true ); 4836 4837 /* Calculate sizes for columns */ 4838 if ( features.bAutoWidth ) { 4839 _fnCalculateColumnWidths( settings ); 4840 } 4841 4842 for ( i=0, iLen=columns.length ; i<iLen ; i++ ) { 4843 column = columns[i]; 4844 4845 if ( column.sWidth ) { 4846 column.nTh.style.width = _fnStringToCss( column.sWidth ); 4847 } 4848 } 4849 4850 _fnCallbackFire( settings, null, 'preInit', [settings] ); 4851 4852 // If there is default sorting required - let's do it. The sort function 4853 // will do the drawing for us. Otherwise we draw the table regardless of the 4854 // Ajax source - this allows the table to look initialised for Ajax sourcing 4855 // data (show 'loading' message possibly) 4856 _fnReDraw( settings ); 4857 4858 // Server-side processing init complete is done by _fnAjaxUpdateDraw 4859 var dataSrc = _fnDataSource( settings ); 4860 if ( dataSrc != 'ssp' || deferLoading ) { 4861 // if there is an ajax source load the data 4862 if ( dataSrc == 'ajax' ) { 4863 _fnBuildAjax( settings, [], function(json) { 4864 var aData = _fnAjaxDataSrc( settings, json ); 4865 4866 // Got the data - add it to the table 4867 for ( i=0 ; i<aData.length ; i++ ) { 4868 _fnAddData( settings, aData[i] ); 4869 } 4870 4871 // Reset the init display for cookie saving. We've already done 4872 // a filter, and therefore cleared it before. So we need to make 4873 // it appear 'fresh' 4874 settings.iInitDisplayStart = iAjaxStart; 4875 4876 _fnReDraw( settings ); 4877 4878 _fnProcessingDisplay( settings, false ); 4879 _fnInitComplete( settings, json ); 4880 }, settings ); 4881 } 4882 else { 4883 _fnProcessingDisplay( settings, false ); 4884 _fnInitComplete( settings ); 4885 } 4886 } 4887 } 4888 4889 4890 /** 4891 * Draw the table for the first time, adding all required features 4892 * @param {object} oSettings dataTables settings object 4893 * @param {object} [json] JSON from the server that completed the table, if using Ajax source 4894 * with client-side processing (optional) 4895 * @memberof DataTable#oApi 4896 */ 4897 function _fnInitComplete ( settings, json ) 4898 { 4899 settings._bInitComplete = true; 4900 4901 // When data was added after the initialisation (data or Ajax) we need to 4902 // calculate the column sizing 4903 if ( json || settings.oInit.aaData ) { 4904 _fnAdjustColumnSizing( settings ); 4905 } 4906 4907 _fnCallbackFire( settings, null, 'plugin-init', [settings, json] ); 4908 _fnCallbackFire( settings, 'aoInitComplete', 'init', [settings, json] ); 4909 } 4910 4911 4912 function _fnLengthChange ( settings, val ) 4913 { 4914 var len = parseInt( val, 10 ); 4915 settings._iDisplayLength = len; 4916 4917 _fnLengthOverflow( settings ); 4918 4919 // Fire length change event 4920 _fnCallbackFire( settings, null, 'length', [settings, len] ); 4921 } 4922 4923 4924 /** 4925 * Generate the node required for user display length changing 4926 * @param {object} settings dataTables settings object 4927 * @returns {node} Display length feature node 4928 * @memberof DataTable#oApi 4929 */ 4930 function _fnFeatureHtmlLength ( settings ) 4931 { 4932 var 4933 classes = settings.oClasses, 4934 tableId = settings.sTableId, 4935 menu = settings.aLengthMenu, 4936 d2 = Array.isArray( menu[0] ), 4937 lengths = d2 ? menu[0] : menu, 4938 language = d2 ? menu[1] : menu; 4939 4940 var select = $('<select/>', { 4941 'name': tableId+'_length', 4942 'aria-controls': tableId, 4943 'class': classes.sLengthSelect 4944 } ); 4945 4946 for ( var i=0, ien=lengths.length ; i<ien ; i++ ) { 4947 select[0][ i ] = new Option( 4948 typeof language[i] === 'number' ? 4949 settings.fnFormatNumber( language[i] ) : 4950 language[i], 4951 lengths[i] 4952 ); 4953 } 4954 4955 var div = $('<div><label/></div>').addClass( classes.sLength ); 4956 if ( ! settings.aanFeatures.l ) { 4957 div[0].id = tableId+'_length'; 4958 } 4959 4960 div.children().append( 4961 settings.oLanguage.sLengthMenu.replace( '_MENU_', select[0].outerHTML ) 4962 ); 4963 4964 // Can't use `select` variable as user might provide their own and the 4965 // reference is broken by the use of outerHTML 4966 $('select', div) 4967 .val( settings._iDisplayLength ) 4968 .on( 'change.DT', function(e) { 4969 _fnLengthChange( settings, $(this).val() ); 4970 _fnDraw( settings ); 4971 } ); 4972 4973 // Update node value whenever anything changes the table's length 4974 $(settings.nTable).on( 'length.dt.DT', function (e, s, len) { 4975 if ( settings === s ) { 4976 $('select', div).val( len ); 4977 } 4978 } ); 4979 4980 return div[0]; 4981 } 4982 4983 4984 4985 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 4986 * Note that most of the paging logic is done in 4987 * DataTable.ext.pager 4988 */ 4989 4990 /** 4991 * Generate the node required for default pagination 4992 * @param {object} oSettings dataTables settings object 4993 * @returns {node} Pagination feature node 4994 * @memberof DataTable#oApi 4995 */ 4996 function _fnFeatureHtmlPaginate ( settings ) 4997 { 4998 var 4999 type = settings.sPaginationType, 5000 plugin = DataTable.ext.pager[ type ], 5001 modern = typeof plugin === 'function', 5002 redraw = function( settings ) { 5003 _fnDraw( settings ); 5004 }, 5005 node = $('<div/>').addClass( settings.oClasses.sPaging + type )[0], 5006 features = settings.aanFeatures; 5007 5008 if ( ! modern ) { 5009 plugin.fnInit( settings, node, redraw ); 5010 } 5011 5012 /* Add a draw callback for the pagination on first instance, to update the paging display */ 5013 if ( ! features.p ) 5014 { 5015 node.id = settings.sTableId+'_paginate'; 5016 5017 settings.aoDrawCallback.push( { 5018 "fn": function( settings ) { 5019 if ( modern ) { 5020 var 5021 start = settings._iDisplayStart, 5022 len = settings._iDisplayLength, 5023 visRecords = settings.fnRecordsDisplay(), 5024 all = len === -1, 5025 page = all ? 0 : Math.ceil( start / len ), 5026 pages = all ? 1 : Math.ceil( visRecords / len ), 5027 buttons = plugin(page, pages), 5028 i, ien; 5029 5030 for ( i=0, ien=features.p.length ; i<ien ; i++ ) { 5031 _fnRenderer( settings, 'pageButton' )( 5032 settings, features.p[i], i, buttons, page, pages 5033 ); 5034 } 5035 } 5036 else { 5037 plugin.fnUpdate( settings, redraw ); 5038 } 5039 }, 5040 "sName": "pagination" 5041 } ); 5042 } 5043 5044 return node; 5045 } 5046 5047 5048 /** 5049 * Alter the display settings to change the page 5050 * @param {object} settings DataTables settings object 5051 * @param {string|int} action Paging action to take: "first", "previous", 5052 * "next" or "last" or page number to jump to (integer) 5053 * @param [bool] redraw Automatically draw the update or not 5054 * @returns {bool} true page has changed, false - no change 5055 * @memberof DataTable#oApi 5056 */ 5057 function _fnPageChange ( settings, action, redraw ) 5058 { 5059 var 5060 start = settings._iDisplayStart, 5061 len = settings._iDisplayLength, 5062 records = settings.fnRecordsDisplay(); 5063 5064 if ( records === 0 || len === -1 ) 5065 { 5066 start = 0; 5067 } 5068 else if ( typeof action === "number" ) 5069 { 5070 start = action * len; 5071 5072 if ( start > records ) 5073 { 5074 start = 0; 5075 } 5076 } 5077 else if ( action == "first" ) 5078 { 5079 start = 0; 5080 } 5081 else if ( action == "previous" ) 5082 { 5083 start = len >= 0 ? 5084 start - len : 5085 0; 5086 5087 if ( start < 0 ) 5088 { 5089 start = 0; 5090 } 5091 } 5092 else if ( action == "next" ) 5093 { 5094 if ( start + len < records ) 5095 { 5096 start += len; 5097 } 5098 } 5099 else if ( action == "last" ) 5100 { 5101 start = Math.floor( (records-1) / len) * len; 5102 } 5103 else 5104 { 5105 _fnLog( settings, 0, "Unknown paging action: "+action, 5 ); 5106 } 5107 5108 var changed = settings._iDisplayStart !== start; 5109 settings._iDisplayStart = start; 5110 5111 if ( changed ) { 5112 _fnCallbackFire( settings, null, 'page', [settings] ); 5113 5114 if ( redraw ) { 5115 _fnDraw( settings ); 5116 } 5117 } 5118 else { 5119 // No change event - paging was called, but no change 5120 _fnCallbackFire( settings, null, 'page-nc', [settings] ); 5121 } 5122 5123 return changed; 5124 } 5125 5126 5127 5128 /** 5129 * Generate the node required for the processing node 5130 * @param {object} settings dataTables settings object 5131 * @returns {node} Processing element 5132 * @memberof DataTable#oApi 5133 */ 5134 function _fnFeatureHtmlProcessing ( settings ) 5135 { 5136 return $('<div/>', { 5137 'id': ! settings.aanFeatures.r ? settings.sTableId+'_processing' : null, 5138 'class': settings.oClasses.sProcessing, 5139 'role': 'status' 5140 } ) 5141 .html( settings.oLanguage.sProcessing ) 5142 .append('<div><div></div><div></div><div></div><div></div></div>') 5143 .insertBefore( settings.nTable )[0]; 5144 } 5145 5146 5147 /** 5148 * Display or hide the processing indicator 5149 * @param {object} settings dataTables settings object 5150 * @param {bool} show Show the processing indicator (true) or not (false) 5151 * @memberof DataTable#oApi 5152 */ 5153 function _fnProcessingDisplay ( settings, show ) 5154 { 5155 if ( settings.oFeatures.bProcessing ) { 5156 $(settings.aanFeatures.r).css( 'display', show ? 'block' : 'none' ); 5157 } 5158 5159 _fnCallbackFire( settings, null, 'processing', [settings, show] ); 5160 } 5161 5162 /** 5163 * Add any control elements for the table - specifically scrolling 5164 * @param {object} settings dataTables settings object 5165 * @returns {node} Node to add to the DOM 5166 * @memberof DataTable#oApi 5167 */ 5168 function _fnFeatureHtmlTable ( settings ) 5169 { 5170 var table = $(settings.nTable); 5171 5172 // Scrolling from here on in 5173 var scroll = settings.oScroll; 5174 5175 if ( scroll.sX === '' && scroll.sY === '' ) { 5176 return settings.nTable; 5177 } 5178 5179 var scrollX = scroll.sX; 5180 var scrollY = scroll.sY; 5181 var classes = settings.oClasses; 5182 var caption = table.children('caption'); 5183 var captionSide = caption.length ? caption[0]._captionSide : null; 5184 var headerClone = $( table[0].cloneNode(false) ); 5185 var footerClone = $( table[0].cloneNode(false) ); 5186 var footer = table.children('tfoot'); 5187 var _div = '<div/>'; 5188 var size = function ( s ) { 5189 return !s ? null : _fnStringToCss( s ); 5190 }; 5191 5192 if ( ! footer.length ) { 5193 footer = null; 5194 } 5195 5196 /* 5197 * The HTML structure that we want to generate in this function is: 5198 * div - scroller 5199 * div - scroll head 5200 * div - scroll head inner 5201 * table - scroll head table 5202 * thead - thead 5203 * div - scroll body 5204 * table - table (master table) 5205 * thead - thead clone for sizing 5206 * tbody - tbody 5207 * div - scroll foot 5208 * div - scroll foot inner 5209 * table - scroll foot table 5210 * tfoot - tfoot 5211 */ 5212 var scroller = $( _div, { 'class': classes.sScrollWrapper } ) 5213 .append( 5214 $(_div, { 'class': classes.sScrollHead } ) 5215 .css( { 5216 overflow: 'hidden', 5217 position: 'relative', 5218 border: 0, 5219 width: scrollX ? size(scrollX) : '100%' 5220 } ) 5221 .append( 5222 $(_div, { 'class': classes.sScrollHeadInner } ) 5223 .css( { 5224 'box-sizing': 'content-box', 5225 width: scroll.sXInner || '100%' 5226 } ) 5227 .append( 5228 headerClone 5229 .removeAttr('id') 5230 .css( 'margin-left', 0 ) 5231 .append( captionSide === 'top' ? caption : null ) 5232 .append( 5233 table.children('thead') 5234 ) 5235 ) 5236 ) 5237 ) 5238 .append( 5239 $(_div, { 'class': classes.sScrollBody } ) 5240 .css( { 5241 position: 'relative', 5242 overflow: 'auto', 5243 width: size( scrollX ) 5244 } ) 5245 .append( table ) 5246 ); 5247 5248 if ( footer ) { 5249 scroller.append( 5250 $(_div, { 'class': classes.sScrollFoot } ) 5251 .css( { 5252 overflow: 'hidden', 5253 border: 0, 5254 width: scrollX ? size(scrollX) : '100%' 5255 } ) 5256 .append( 5257 $(_div, { 'class': classes.sScrollFootInner } ) 5258 .append( 5259 footerClone 5260 .removeAttr('id') 5261 .css( 'margin-left', 0 ) 5262 .append( captionSide === 'bottom' ? caption : null ) 5263 .append( 5264 table.children('tfoot') 5265 ) 5266 ) 5267 ) 5268 ); 5269 } 5270 5271 var children = scroller.children(); 5272 var scrollHead = children[0]; 5273 var scrollBody = children[1]; 5274 var scrollFoot = footer ? children[2] : null; 5275 5276 // When the body is scrolled, then we also want to scroll the headers 5277 if ( scrollX ) { 5278 $(scrollBody).on( 'scroll.DT', function (e) { 5279 var scrollLeft = this.scrollLeft; 5280 5281 scrollHead.scrollLeft = scrollLeft; 5282 5283 if ( footer ) { 5284 scrollFoot.scrollLeft = scrollLeft; 5285 } 5286 } ); 5287 } 5288 5289 $(scrollBody).css('max-height', scrollY); 5290 if (! scroll.bCollapse) { 5291 $(scrollBody).css('height', scrollY); 5292 } 5293 5294 settings.nScrollHead = scrollHead; 5295 settings.nScrollBody = scrollBody; 5296 settings.nScrollFoot = scrollFoot; 5297 5298 // On redraw - align columns 5299 settings.aoDrawCallback.push( { 5300 "fn": _fnScrollDraw, 5301 "sName": "scrolling" 5302 } ); 5303 5304 return scroller[0]; 5305 } 5306 5307 5308 5309 /** 5310 * Update the header, footer and body tables for resizing - i.e. column 5311 * alignment. 5312 * 5313 * Welcome to the most horrible function DataTables. The process that this 5314 * function follows is basically: 5315 * 1. Re-create the table inside the scrolling div 5316 * 2. Take live measurements from the DOM 5317 * 3. Apply the measurements to align the columns 5318 * 4. Clean up 5319 * 5320 * @param {object} settings dataTables settings object 5321 * @memberof DataTable#oApi 5322 */ 5323 function _fnScrollDraw ( settings ) 5324 { 5325 // Given that this is such a monster function, a lot of variables are use 5326 // to try and keep the minimised size as small as possible 5327 var 5328 scroll = settings.oScroll, 5329 scrollX = scroll.sX, 5330 scrollXInner = scroll.sXInner, 5331 scrollY = scroll.sY, 5332 barWidth = scroll.iBarWidth, 5333 divHeader = $(settings.nScrollHead), 5334 divHeaderStyle = divHeader[0].style, 5335 divHeaderInner = divHeader.children('div'), 5336 divHeaderInnerStyle = divHeaderInner[0].style, 5337 divHeaderTable = divHeaderInner.children('table'), 5338 divBodyEl = settings.nScrollBody, 5339 divBody = $(divBodyEl), 5340 divBodyStyle = divBodyEl.style, 5341 divFooter = $(settings.nScrollFoot), 5342 divFooterInner = divFooter.children('div'), 5343 divFooterTable = divFooterInner.children('table'), 5344 header = $(settings.nTHead), 5345 table = $(settings.nTable), 5346 tableEl = table[0], 5347 tableStyle = tableEl.style, 5348 footer = settings.nTFoot ? $(settings.nTFoot) : null, 5349 browser = settings.oBrowser, 5350 ie67 = browser.bScrollOversize, 5351 dtHeaderCells = _pluck( settings.aoColumns, 'nTh' ), 5352 headerTrgEls, footerTrgEls, 5353 headerSrcEls, footerSrcEls, 5354 headerCopy, footerCopy, 5355 headerWidths=[], footerWidths=[], 5356 headerContent=[], footerContent=[], 5357 idx, correction, sanityWidth, 5358 zeroOut = function(nSizer) { 5359 var style = nSizer.style; 5360 style.paddingTop = "0"; 5361 style.paddingBottom = "0"; 5362 style.borderTopWidth = "0"; 5363 style.borderBottomWidth = "0"; 5364 style.height = 0; 5365 }; 5366 5367 // If the scrollbar visibility has changed from the last draw, we need to 5368 // adjust the column sizes as the table width will have changed to account 5369 // for the scrollbar 5370 var scrollBarVis = divBodyEl.scrollHeight > divBodyEl.clientHeight; 5371 5372 if ( settings.scrollBarVis !== scrollBarVis && settings.scrollBarVis !== undefined ) { 5373 settings.scrollBarVis = scrollBarVis; 5374 _fnAdjustColumnSizing( settings ); 5375 return; // adjust column sizing will call this function again 5376 } 5377 else { 5378 settings.scrollBarVis = scrollBarVis; 5379 } 5380 5381 /* 5382 * 1. Re-create the table inside the scrolling div 5383 */ 5384 5385 // Remove the old minimised thead and tfoot elements in the inner table 5386 table.children('thead, tfoot').remove(); 5387 5388 if ( footer ) { 5389 footerCopy = footer.clone().prependTo( table ); 5390 footerTrgEls = footer.find('tr'); // the original tfoot is in its own table and must be sized 5391 footerSrcEls = footerCopy.find('tr'); 5392 footerCopy.find('[id]').removeAttr('id'); 5393 } 5394 5395 // Clone the current header and footer elements and then place it into the inner table 5396 headerCopy = header.clone().prependTo( table ); 5397 headerTrgEls = header.find('tr'); // original header is in its own table 5398 headerSrcEls = headerCopy.find('tr'); 5399 headerCopy.find('th, td').removeAttr('tabindex'); 5400 headerCopy.find('[id]').removeAttr('id'); 5401 5402 5403 /* 5404 * 2. Take live measurements from the DOM - do not alter the DOM itself! 5405 */ 5406 5407 // Remove old sizing and apply the calculated column widths 5408 // Get the unique column headers in the newly created (cloned) header. We want to apply the 5409 // calculated sizes to this header 5410 if ( ! scrollX ) 5411 { 5412 divBodyStyle.width = '100%'; 5413 divHeader[0].style.width = '100%'; 5414 } 5415 5416 $.each( _fnGetUniqueThs( settings, headerCopy ), function ( i, el ) { 5417 idx = _fnVisibleToColumnIndex( settings, i ); 5418 el.style.width = settings.aoColumns[idx].sWidth; 5419 } ); 5420 5421 if ( footer ) { 5422 _fnApplyToChildren( function(n) { 5423 n.style.width = ""; 5424 }, footerSrcEls ); 5425 } 5426 5427 // Size the table as a whole 5428 sanityWidth = table.outerWidth(); 5429 if ( scrollX === "" ) { 5430 // No x scrolling 5431 tableStyle.width = "100%"; 5432 5433 // IE7 will make the width of the table when 100% include the scrollbar 5434 // - which is shouldn't. When there is a scrollbar we need to take this 5435 // into account. 5436 if ( ie67 && (table.find('tbody').height() > divBodyEl.offsetHeight || 5437 divBody.css('overflow-y') == "scroll") 5438 ) { 5439 tableStyle.width = _fnStringToCss( table.outerWidth() - barWidth); 5440 } 5441 5442 // Recalculate the sanity width 5443 sanityWidth = table.outerWidth(); 5444 } 5445 else if ( scrollXInner !== "" ) { 5446 // legacy x scroll inner has been given - use it 5447 tableStyle.width = _fnStringToCss(scrollXInner); 5448 5449 // Recalculate the sanity width 5450 sanityWidth = table.outerWidth(); 5451 } 5452 5453 // Hidden header should have zero height, so remove padding and borders. Then 5454 // set the width based on the real headers 5455 5456 // Apply all styles in one pass 5457 _fnApplyToChildren( zeroOut, headerSrcEls ); 5458 5459 // Read all widths in next pass 5460 _fnApplyToChildren( function(nSizer) { 5461 var style = window.getComputedStyle ? 5462 window.getComputedStyle(nSizer).width : 5463 _fnStringToCss( $(nSizer).width() ); 5464 5465 headerContent.push( nSizer.innerHTML ); 5466 headerWidths.push( style ); 5467 }, headerSrcEls ); 5468 5469 // Apply all widths in final pass 5470 _fnApplyToChildren( function(nToSize, i) { 5471 nToSize.style.width = headerWidths[i]; 5472 }, headerTrgEls ); 5473 5474 $(headerSrcEls).css('height', 0); 5475 5476 /* Same again with the footer if we have one */ 5477 if ( footer ) 5478 { 5479 _fnApplyToChildren( zeroOut, footerSrcEls ); 5480 5481 _fnApplyToChildren( function(nSizer) { 5482 footerContent.push( nSizer.innerHTML ); 5483 footerWidths.push( _fnStringToCss( $(nSizer).css('width') ) ); 5484 }, footerSrcEls ); 5485 5486 _fnApplyToChildren( function(nToSize, i) { 5487 nToSize.style.width = footerWidths[i]; 5488 }, footerTrgEls ); 5489 5490 $(footerSrcEls).height(0); 5491 } 5492 5493 5494 /* 5495 * 3. Apply the measurements 5496 */ 5497 5498 // "Hide" the header and footer that we used for the sizing. We need to keep 5499 // the content of the cell so that the width applied to the header and body 5500 // both match, but we want to hide it completely. We want to also fix their 5501 // width to what they currently are 5502 _fnApplyToChildren( function(nSizer, i) { 5503 nSizer.innerHTML = '<div class="dataTables_sizing">'+headerContent[i]+'</div>'; 5504 nSizer.childNodes[0].style.height = "0"; 5505 nSizer.childNodes[0].style.overflow = "hidden"; 5506 nSizer.style.width = headerWidths[i]; 5507 }, headerSrcEls ); 5508 5509 if ( footer ) 5510 { 5511 _fnApplyToChildren( function(nSizer, i) { 5512 nSizer.innerHTML = '<div class="dataTables_sizing">'+footerContent[i]+'</div>'; 5513 nSizer.childNodes[0].style.height = "0"; 5514 nSizer.childNodes[0].style.overflow = "hidden"; 5515 nSizer.style.width = footerWidths[i]; 5516 }, footerSrcEls ); 5517 } 5518 5519 // Sanity check that the table is of a sensible width. If not then we are going to get 5520 // misalignment - try to prevent this by not allowing the table to shrink below its min width 5521 if ( Math.round(table.outerWidth()) < Math.round(sanityWidth) ) 5522 { 5523 // The min width depends upon if we have a vertical scrollbar visible or not */ 5524 correction = ((divBodyEl.scrollHeight > divBodyEl.offsetHeight || 5525 divBody.css('overflow-y') == "scroll")) ? 5526 sanityWidth+barWidth : 5527 sanityWidth; 5528 5529 // IE6/7 are a law unto themselves... 5530 if ( ie67 && (divBodyEl.scrollHeight > 5531 divBodyEl.offsetHeight || divBody.css('overflow-y') == "scroll") 5532 ) { 5533 tableStyle.width = _fnStringToCss( correction-barWidth ); 5534 } 5535 5536 // And give the user a warning that we've stopped the table getting too small 5537 if ( scrollX === "" || scrollXInner !== "" ) { 5538 _fnLog( settings, 1, 'Possible column misalignment', 6 ); 5539 } 5540 } 5541 else 5542 { 5543 correction = '100%'; 5544 } 5545 5546 // Apply to the container elements 5547 divBodyStyle.width = _fnStringToCss( correction ); 5548 divHeaderStyle.width = _fnStringToCss( correction ); 5549 5550 if ( footer ) { 5551 settings.nScrollFoot.style.width = _fnStringToCss( correction ); 5552 } 5553 5554 5555 /* 5556 * 4. Clean up 5557 */ 5558 if ( ! scrollY ) { 5559 /* IE7< puts a vertical scrollbar in place (when it shouldn't be) due to subtracting 5560 * the scrollbar height from the visible display, rather than adding it on. We need to 5561 * set the height in order to sort this. Don't want to do it in any other browsers. 5562 */ 5563 if ( ie67 ) { 5564 divBodyStyle.height = _fnStringToCss( tableEl.offsetHeight+barWidth ); 5565 } 5566 } 5567 5568 /* Finally set the width's of the header and footer tables */ 5569 var iOuterWidth = table.outerWidth(); 5570 divHeaderTable[0].style.width = _fnStringToCss( iOuterWidth ); 5571 divHeaderInnerStyle.width = _fnStringToCss( iOuterWidth ); 5572 5573 // Figure out if there are scrollbar present - if so then we need a the header and footer to 5574 // provide a bit more space to allow "overflow" scrolling (i.e. past the scrollbar) 5575 var bScrolling = table.height() > divBodyEl.clientHeight || divBody.css('overflow-y') == "scroll"; 5576 var padding = 'padding' + (browser.bScrollbarLeft ? 'Left' : 'Right' ); 5577 divHeaderInnerStyle[ padding ] = bScrolling ? barWidth+"px" : "0px"; 5578 5579 if ( footer ) { 5580 divFooterTable[0].style.width = _fnStringToCss( iOuterWidth ); 5581 divFooterInner[0].style.width = _fnStringToCss( iOuterWidth ); 5582 divFooterInner[0].style[padding] = bScrolling ? barWidth+"px" : "0px"; 5583 } 5584 5585 // Correct DOM ordering for colgroup - comes before the thead 5586 table.children('colgroup').insertBefore( table.children('thead') ); 5587 5588 /* Adjust the position of the header in case we loose the y-scrollbar */ 5589 divBody.trigger('scroll'); 5590 5591 // If sorting or filtering has occurred, jump the scrolling back to the top 5592 // only if we aren't holding the position 5593 if ( (settings.bSorted || settings.bFiltered) && ! settings._drawHold ) { 5594 divBodyEl.scrollTop = 0; 5595 } 5596 } 5597 5598 5599 5600 /** 5601 * Apply a given function to the display child nodes of an element array (typically 5602 * TD children of TR rows 5603 * @param {function} fn Method to apply to the objects 5604 * @param array {nodes} an1 List of elements to look through for display children 5605 * @param array {nodes} an2 Another list (identical structure to the first) - optional 5606 * @memberof DataTable#oApi 5607 */ 5608 function _fnApplyToChildren( fn, an1, an2 ) 5609 { 5610 var index=0, i=0, iLen=an1.length; 5611 var nNode1, nNode2; 5612 5613 while ( i < iLen ) { 5614 nNode1 = an1[i].firstChild; 5615 nNode2 = an2 ? an2[i].firstChild : null; 5616 5617 while ( nNode1 ) { 5618 if ( nNode1.nodeType === 1 ) { 5619 if ( an2 ) { 5620 fn( nNode1, nNode2, index ); 5621 } 5622 else { 5623 fn( nNode1, index ); 5624 } 5625 5626 index++; 5627 } 5628 5629 nNode1 = nNode1.nextSibling; 5630 nNode2 = an2 ? nNode2.nextSibling : null; 5631 } 5632 5633 i++; 5634 } 5635 } 5636 5637 5638 5639 var __re_html_remove = /<.*?>/g; 5640 5641 5642 /** 5643 * Calculate the width of columns for the table 5644 * @param {object} oSettings dataTables settings object 5645 * @memberof DataTable#oApi 5646 */ 5647 function _fnCalculateColumnWidths ( oSettings ) 5648 { 5649 var 5650 table = oSettings.nTable, 5651 columns = oSettings.aoColumns, 5652 scroll = oSettings.oScroll, 5653 scrollY = scroll.sY, 5654 scrollX = scroll.sX, 5655 scrollXInner = scroll.sXInner, 5656 columnCount = columns.length, 5657 visibleColumns = _fnGetColumns( oSettings, 'bVisible' ), 5658 headerCells = $('th', oSettings.nTHead), 5659 tableWidthAttr = table.getAttribute('width'), // from DOM element 5660 tableContainer = table.parentNode, 5661 userInputs = false, 5662 i, column, columnIdx, width, outerWidth, 5663 browser = oSettings.oBrowser, 5664 ie67 = browser.bScrollOversize; 5665 5666 var styleWidth = table.style.width; 5667 if ( styleWidth && styleWidth.indexOf('%') !== -1 ) { 5668 tableWidthAttr = styleWidth; 5669 } 5670 5671 /* Convert any user input sizes into pixel sizes */ 5672 for ( i=0 ; i<visibleColumns.length ; i++ ) { 5673 column = columns[ visibleColumns[i] ]; 5674 5675 if ( column.sWidth !== null ) { 5676 column.sWidth = _fnConvertToWidth( column.sWidthOrig, tableContainer ); 5677 5678 userInputs = true; 5679 } 5680 } 5681 5682 /* If the number of columns in the DOM equals the number that we have to 5683 * process in DataTables, then we can use the offsets that are created by 5684 * the web- browser. No custom sizes can be set in order for this to happen, 5685 * nor scrolling used 5686 */ 5687 if ( ie67 || ! userInputs && ! scrollX && ! scrollY && 5688 columnCount == _fnVisbleColumns( oSettings ) && 5689 columnCount == headerCells.length 5690 ) { 5691 for ( i=0 ; i<columnCount ; i++ ) { 5692 var colIdx = _fnVisibleToColumnIndex( oSettings, i ); 5693 5694 if ( colIdx !== null ) { 5695 columns[ colIdx ].sWidth = _fnStringToCss( headerCells.eq(i).width() ); 5696 } 5697 } 5698 } 5699 else 5700 { 5701 // Otherwise construct a single row, worst case, table with the widest 5702 // node in the data, assign any user defined widths, then insert it into 5703 // the DOM and allow the browser to do all the hard work of calculating 5704 // table widths 5705 var tmpTable = $(table).clone() // don't use cloneNode - IE8 will remove events on the main table 5706 .css( 'visibility', 'hidden' ) 5707 .removeAttr( 'id' ); 5708 5709 // Clean up the table body 5710 tmpTable.find('tbody tr').remove(); 5711 var tr = $('<tr/>').appendTo( tmpTable.find('tbody') ); 5712 5713 // Clone the table header and footer - we can't use the header / footer 5714 // from the cloned table, since if scrolling is active, the table's 5715 // real header and footer are contained in different table tags 5716 tmpTable.find('thead, tfoot').remove(); 5717 tmpTable 5718 .append( $(oSettings.nTHead).clone() ) 5719 .append( $(oSettings.nTFoot).clone() ); 5720 5721 // Remove any assigned widths from the footer (from scrolling) 5722 tmpTable.find('tfoot th, tfoot td').css('width', ''); 5723 5724 // Apply custom sizing to the cloned header 5725 headerCells = _fnGetUniqueThs( oSettings, tmpTable.find('thead')[0] ); 5726 5727 for ( i=0 ; i<visibleColumns.length ; i++ ) { 5728 column = columns[ visibleColumns[i] ]; 5729 5730 headerCells[i].style.width = column.sWidthOrig !== null && column.sWidthOrig !== '' ? 5731 _fnStringToCss( column.sWidthOrig ) : 5732 ''; 5733 5734 // For scrollX we need to force the column width otherwise the 5735 // browser will collapse it. If this width is smaller than the 5736 // width the column requires, then it will have no effect 5737 if ( column.sWidthOrig && scrollX ) { 5738 $( headerCells[i] ).append( $('<div/>').css( { 5739 width: column.sWidthOrig, 5740 margin: 0, 5741 padding: 0, 5742 border: 0, 5743 height: 1 5744 } ) ); 5745 } 5746 } 5747 5748 // Find the widest cell for each column and put it into the table 5749 if ( oSettings.aoData.length ) { 5750 for ( i=0 ; i<visibleColumns.length ; i++ ) { 5751 columnIdx = visibleColumns[i]; 5752 column = columns[ columnIdx ]; 5753 5754 $( _fnGetWidestNode( oSettings, columnIdx ) ) 5755 .clone( false ) 5756 .append( column.sContentPadding ) 5757 .appendTo( tr ); 5758 } 5759 } 5760 5761 // Tidy the temporary table - remove name attributes so there aren't 5762 // duplicated in the dom (radio elements for example) 5763 $('[name]', tmpTable).removeAttr('name'); 5764 5765 // Table has been built, attach to the document so we can work with it. 5766 // A holding element is used, positioned at the top of the container 5767 // with minimal height, so it has no effect on if the container scrolls 5768 // or not. Otherwise it might trigger scrolling when it actually isn't 5769 // needed 5770 var holder = $('<div/>').css( scrollX || scrollY ? 5771 { 5772 position: 'absolute', 5773 top: 0, 5774 left: 0, 5775 height: 1, 5776 right: 0, 5777 overflow: 'hidden' 5778 } : 5779 {} 5780 ) 5781 .append( tmpTable ) 5782 .appendTo( tableContainer ); 5783 5784 // When scrolling (X or Y) we want to set the width of the table as 5785 // appropriate. However, when not scrolling leave the table width as it 5786 // is. This results in slightly different, but I think correct behaviour 5787 if ( scrollX && scrollXInner ) { 5788 tmpTable.width( scrollXInner ); 5789 } 5790 else if ( scrollX ) { 5791 tmpTable.css( 'width', 'auto' ); 5792 tmpTable.removeAttr('width'); 5793 5794 // If there is no width attribute or style, then allow the table to 5795 // collapse 5796 if ( tmpTable.width() < tableContainer.clientWidth && tableWidthAttr ) { 5797 tmpTable.width( tableContainer.clientWidth ); 5798 } 5799 } 5800 else if ( scrollY ) { 5801 tmpTable.width( tableContainer.clientWidth ); 5802 } 5803 else if ( tableWidthAttr ) { 5804 tmpTable.width( tableWidthAttr ); 5805 } 5806 5807 // Get the width of each column in the constructed table - we need to 5808 // know the inner width (so it can be assigned to the other table's 5809 // cells) and the outer width so we can calculate the full width of the 5810 // table. This is safe since DataTables requires a unique cell for each 5811 // column, but if ever a header can span multiple columns, this will 5812 // need to be modified. 5813 var total = 0; 5814 for ( i=0 ; i<visibleColumns.length ; i++ ) { 5815 var cell = $(headerCells[i]); 5816 var border = cell.outerWidth() - cell.width(); 5817 5818 // Use getBounding... where possible (not IE8-) because it can give 5819 // sub-pixel accuracy, which we then want to round up! 5820 var bounding = browser.bBounding ? 5821 Math.ceil( headerCells[i].getBoundingClientRect().width ) : 5822 cell.outerWidth(); 5823 5824 // Total is tracked to remove any sub-pixel errors as the outerWidth 5825 // of the table might not equal the total given here (IE!). 5826 total += bounding; 5827 5828 // Width for each column to use 5829 columns[ visibleColumns[i] ].sWidth = _fnStringToCss( bounding - border ); 5830 } 5831 5832 table.style.width = _fnStringToCss( total ); 5833 5834 // Finished with the table - ditch it 5835 holder.remove(); 5836 } 5837 5838 // If there is a width attr, we want to attach an event listener which 5839 // allows the table sizing to automatically adjust when the window is 5840 // resized. Use the width attr rather than CSS, since we can't know if the 5841 // CSS is a relative value or absolute - DOM read is always px. 5842 if ( tableWidthAttr ) { 5843 table.style.width = _fnStringToCss( tableWidthAttr ); 5844 } 5845 5846 if ( (tableWidthAttr || scrollX) && ! oSettings._reszEvt ) { 5847 var bindResize = function () { 5848 $(window).on('resize.DT-'+oSettings.sInstance, _fnThrottle( function () { 5849 _fnAdjustColumnSizing( oSettings ); 5850 } ) ); 5851 }; 5852 5853 // IE6/7 will crash if we bind a resize event handler on page load. 5854 // To be removed in 1.11 which drops IE6/7 support 5855 if ( ie67 ) { 5856 setTimeout( bindResize, 1000 ); 5857 } 5858 else { 5859 bindResize(); 5860 } 5861 5862 oSettings._reszEvt = true; 5863 } 5864 } 5865 5866 5867 /** 5868 * Throttle the calls to a function. Arguments and context are maintained for 5869 * the throttled function 5870 * @param {function} fn Function to be called 5871 * @param {int} [freq=200] call frequency in mS 5872 * @returns {function} wrapped function 5873 * @memberof DataTable#oApi 5874 */ 5875 var _fnThrottle = DataTable.util.throttle; 5876 5877 5878 /** 5879 * Convert a CSS unit width to pixels (e.g. 2em) 5880 * @param {string} width width to be converted 5881 * @param {node} parent parent to get the with for (required for relative widths) - optional 5882 * @returns {int} width in pixels 5883 * @memberof DataTable#oApi 5884 */ 5885 function _fnConvertToWidth ( width, parent ) 5886 { 5887 if ( ! width ) { 5888 return 0; 5889 } 5890 5891 var n = $('<div/>') 5892 .css( 'width', _fnStringToCss( width ) ) 5893 .appendTo( parent || document.body ); 5894 5895 var val = n[0].offsetWidth; 5896 n.remove(); 5897 5898 return val; 5899 } 5900 5901 5902 /** 5903 * Get the widest node 5904 * @param {object} settings dataTables settings object 5905 * @param {int} colIdx column of interest 5906 * @returns {node} widest table node 5907 * @memberof DataTable#oApi 5908 */ 5909 function _fnGetWidestNode( settings, colIdx ) 5910 { 5911 var idx = _fnGetMaxLenString( settings, colIdx ); 5912 if ( idx < 0 ) { 5913 return null; 5914 } 5915 5916 var data = settings.aoData[ idx ]; 5917 return ! data.nTr ? // Might not have been created when deferred rendering 5918 $('<td/>').html( _fnGetCellData( settings, idx, colIdx, 'display' ) )[0] : 5919 data.anCells[ colIdx ]; 5920 } 5921 5922 5923 /** 5924 * Get the maximum strlen for each data column 5925 * @param {object} settings dataTables settings object 5926 * @param {int} colIdx column of interest 5927 * @returns {string} max string length for each column 5928 * @memberof DataTable#oApi 5929 */ 5930 function _fnGetMaxLenString( settings, colIdx ) 5931 { 5932 var s, max=-1, maxIdx = -1; 5933 5934 for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) { 5935 s = _fnGetCellData( settings, i, colIdx, 'display' )+''; 5936 s = s.replace( __re_html_remove, '' ); 5937 s = s.replace( / /g, ' ' ); 5938 5939 if ( s.length > max ) { 5940 max = s.length; 5941 maxIdx = i; 5942 } 5943 } 5944 5945 return maxIdx; 5946 } 5947 5948 5949 /** 5950 * Append a CSS unit (only if required) to a string 5951 * @param {string} value to css-ify 5952 * @returns {string} value with css unit 5953 * @memberof DataTable#oApi 5954 */ 5955 function _fnStringToCss( s ) 5956 { 5957 if ( s === null ) { 5958 return '0px'; 5959 } 5960 5961 if ( typeof s == 'number' ) { 5962 return s < 0 ? 5963 '0px' : 5964 s+'px'; 5965 } 5966 5967 // Check it has a unit character already 5968 return s.match(/\d$/) ? 5969 s+'px' : 5970 s; 5971 } 5972 5973 5974 5975 function _fnSortFlatten ( settings ) 5976 { 5977 var 5978 i, iLen, k, kLen, 5979 aSort = [], 5980 aiOrig = [], 5981 aoColumns = settings.aoColumns, 5982 aDataSort, iCol, sType, srcCol, 5983 fixed = settings.aaSortingFixed, 5984 fixedObj = $.isPlainObject( fixed ), 5985 nestedSort = [], 5986 add = function ( a ) { 5987 if ( a.length && ! Array.isArray( a[0] ) ) { 5988 // 1D array 5989 nestedSort.push( a ); 5990 } 5991 else { 5992 // 2D array 5993 $.merge( nestedSort, a ); 5994 } 5995 }; 5996 5997 // Build the sort array, with pre-fix and post-fix options if they have been 5998 // specified 5999 if ( Array.isArray( fixed ) ) { 6000 add( fixed ); 6001 } 6002 6003 if ( fixedObj && fixed.pre ) { 6004 add( fixed.pre ); 6005 } 6006 6007 add( settings.aaSorting ); 6008 6009 if (fixedObj && fixed.post ) { 6010 add( fixed.post ); 6011 } 6012 6013 for ( i=0 ; i<nestedSort.length ; i++ ) 6014 { 6015 srcCol = nestedSort[i][0]; 6016 aDataSort = aoColumns[ srcCol ].aDataSort; 6017 6018 for ( k=0, kLen=aDataSort.length ; k<kLen ; k++ ) 6019 { 6020 iCol = aDataSort[k]; 6021 sType = aoColumns[ iCol ].sType || 'string'; 6022 6023 if ( nestedSort[i]._idx === undefined ) { 6024 nestedSort[i]._idx = $.inArray( nestedSort[i][1], aoColumns[iCol].asSorting ); 6025 } 6026 6027 aSort.push( { 6028 src: srcCol, 6029 col: iCol, 6030 dir: nestedSort[i][1], 6031 index: nestedSort[i]._idx, 6032 type: sType, 6033 formatter: DataTable.ext.type.order[ sType+"-pre" ] 6034 } ); 6035 } 6036 } 6037 6038 return aSort; 6039 } 6040 6041 /** 6042 * Change the order of the table 6043 * @param {object} oSettings dataTables settings object 6044 * @memberof DataTable#oApi 6045 * @todo This really needs split up! 6046 */ 6047 function _fnSort ( oSettings ) 6048 { 6049 var 6050 i, ien, iLen, j, jLen, k, kLen, 6051 sDataType, nTh, 6052 aiOrig = [], 6053 oExtSort = DataTable.ext.type.order, 6054 aoData = oSettings.aoData, 6055 aoColumns = oSettings.aoColumns, 6056 aDataSort, data, iCol, sType, oSort, 6057 formatters = 0, 6058 sortCol, 6059 displayMaster = oSettings.aiDisplayMaster, 6060 aSort; 6061 6062 // Resolve any column types that are unknown due to addition or invalidation 6063 // @todo Can this be moved into a 'data-ready' handler which is called when 6064 // data is going to be used in the table? 6065 _fnColumnTypes( oSettings ); 6066 6067 aSort = _fnSortFlatten( oSettings ); 6068 6069 for ( i=0, ien=aSort.length ; i<ien ; i++ ) { 6070 sortCol = aSort[i]; 6071 6072 // Track if we can use the fast sort algorithm 6073 if ( sortCol.formatter ) { 6074 formatters++; 6075 } 6076 6077 // Load the data needed for the sort, for each cell 6078 _fnSortData( oSettings, sortCol.col ); 6079 } 6080 6081 /* No sorting required if server-side or no sorting array */ 6082 if ( _fnDataSource( oSettings ) != 'ssp' && aSort.length !== 0 ) 6083 { 6084 // Create a value - key array of the current row positions such that we can use their 6085 // current position during the sort, if values match, in order to perform stable sorting 6086 for ( i=0, iLen=displayMaster.length ; i<iLen ; i++ ) { 6087 aiOrig[ displayMaster[i] ] = i; 6088 } 6089 6090 /* Do the sort - here we want multi-column sorting based on a given data source (column) 6091 * and sorting function (from oSort) in a certain direction. It's reasonably complex to 6092 * follow on it's own, but this is what we want (example two column sorting): 6093 * fnLocalSorting = function(a,b){ 6094 * var iTest; 6095 * iTest = oSort['string-asc']('data11', 'data12'); 6096 * if (iTest !== 0) 6097 * return iTest; 6098 * iTest = oSort['numeric-desc']('data21', 'data22'); 6099 * if (iTest !== 0) 6100 * return iTest; 6101 * return oSort['numeric-asc']( aiOrig[a], aiOrig[b] ); 6102 * } 6103 * Basically we have a test for each sorting column, if the data in that column is equal, 6104 * test the next column. If all columns match, then we use a numeric sort on the row 6105 * positions in the original data array to provide a stable sort. 6106 * 6107 * Note - I know it seems excessive to have two sorting methods, but the first is around 6108 * 15% faster, so the second is only maintained for backwards compatibility with sorting 6109 * methods which do not have a pre-sort formatting function. 6110 */ 6111 if ( formatters === aSort.length ) { 6112 // All sort types have formatting functions 6113 displayMaster.sort( function ( a, b ) { 6114 var 6115 x, y, k, test, sort, 6116 len=aSort.length, 6117 dataA = aoData[a]._aSortData, 6118 dataB = aoData[b]._aSortData; 6119 6120 for ( k=0 ; k<len ; k++ ) { 6121 sort = aSort[k]; 6122 6123 x = dataA[ sort.col ]; 6124 y = dataB[ sort.col ]; 6125 6126 test = x<y ? -1 : x>y ? 1 : 0; 6127 if ( test !== 0 ) { 6128 return sort.dir === 'asc' ? test : -test; 6129 } 6130 } 6131 6132 x = aiOrig[a]; 6133 y = aiOrig[b]; 6134 return x<y ? -1 : x>y ? 1 : 0; 6135 } ); 6136 } 6137 else { 6138 // Depreciated - remove in 1.11 (providing a plug-in option) 6139 // Not all sort types have formatting methods, so we have to call their sorting 6140 // methods. 6141 displayMaster.sort( function ( a, b ) { 6142 var 6143 x, y, k, l, test, sort, fn, 6144 len=aSort.length, 6145 dataA = aoData[a]._aSortData, 6146 dataB = aoData[b]._aSortData; 6147 6148 for ( k=0 ; k<len ; k++ ) { 6149 sort = aSort[k]; 6150 6151 x = dataA[ sort.col ]; 6152 y = dataB[ sort.col ]; 6153 6154 fn = oExtSort[ sort.type+"-"+sort.dir ] || oExtSort[ "string-"+sort.dir ]; 6155 test = fn( x, y ); 6156 if ( test !== 0 ) { 6157 return test; 6158 } 6159 } 6160 6161 x = aiOrig[a]; 6162 y = aiOrig[b]; 6163 return x<y ? -1 : x>y ? 1 : 0; 6164 } ); 6165 } 6166 } 6167 6168 /* Tell the draw function that we have sorted the data */ 6169 oSettings.bSorted = true; 6170 } 6171 6172 6173 function _fnSortAria ( settings ) 6174 { 6175 var label; 6176 var nextSort; 6177 var columns = settings.aoColumns; 6178 var aSort = _fnSortFlatten( settings ); 6179 var oAria = settings.oLanguage.oAria; 6180 6181 // ARIA attributes - need to loop all columns, to update all (removing old 6182 // attributes as needed) 6183 for ( var i=0, iLen=columns.length ; i<iLen ; i++ ) 6184 { 6185 var col = columns[i]; 6186 var asSorting = col.asSorting; 6187 var sTitle = col.ariaTitle || col.sTitle.replace( /<.*?>/g, "" ); 6188 var th = col.nTh; 6189 6190 // IE7 is throwing an error when setting these properties with jQuery's 6191 // attr() and removeAttr() methods... 6192 th.removeAttribute('aria-sort'); 6193 6194 /* In ARIA only the first sorting column can be marked as sorting - no multi-sort option */ 6195 if ( col.bSortable ) { 6196 if ( aSort.length > 0 && aSort[0].col == i ) { 6197 th.setAttribute('aria-sort', aSort[0].dir=="asc" ? "ascending" : "descending" ); 6198 nextSort = asSorting[ aSort[0].index+1 ] || asSorting[0]; 6199 } 6200 else { 6201 nextSort = asSorting[0]; 6202 } 6203 6204 label = sTitle + ( nextSort === "asc" ? 6205 oAria.sSortAscending : 6206 oAria.sSortDescending 6207 ); 6208 } 6209 else { 6210 label = sTitle; 6211 } 6212 6213 th.setAttribute('aria-label', label); 6214 } 6215 } 6216 6217 6218 /** 6219 * Function to run on user sort request 6220 * @param {object} settings dataTables settings object 6221 * @param {node} attachTo node to attach the handler to 6222 * @param {int} colIdx column sorting index 6223 * @param {boolean} [append=false] Append the requested sort to the existing 6224 * sort if true (i.e. multi-column sort) 6225 * @param {function} [callback] callback function 6226 * @memberof DataTable#oApi 6227 */ 6228 function _fnSortListener ( settings, colIdx, append, callback ) 6229 { 6230 var col = settings.aoColumns[ colIdx ]; 6231 var sorting = settings.aaSorting; 6232 var asSorting = col.asSorting; 6233 var nextSortIdx; 6234 var next = function ( a, overflow ) { 6235 var idx = a._idx; 6236 if ( idx === undefined ) { 6237 idx = $.inArray( a[1], asSorting ); 6238 } 6239 6240 return idx+1 < asSorting.length ? 6241 idx+1 : 6242 overflow ? 6243 null : 6244 0; 6245 }; 6246 6247 // Convert to 2D array if needed 6248 if ( typeof sorting[0] === 'number' ) { 6249 sorting = settings.aaSorting = [ sorting ]; 6250 } 6251 6252 // If appending the sort then we are multi-column sorting 6253 if ( append && settings.oFeatures.bSortMulti ) { 6254 // Are we already doing some kind of sort on this column? 6255 var sortIdx = $.inArray( colIdx, _pluck(sorting, '0') ); 6256 6257 if ( sortIdx !== -1 ) { 6258 // Yes, modify the sort 6259 nextSortIdx = next( sorting[sortIdx], true ); 6260 6261 if ( nextSortIdx === null && sorting.length === 1 ) { 6262 nextSortIdx = 0; // can't remove sorting completely 6263 } 6264 6265 if ( nextSortIdx === null ) { 6266 sorting.splice( sortIdx, 1 ); 6267 } 6268 else { 6269 sorting[sortIdx][1] = asSorting[ nextSortIdx ]; 6270 sorting[sortIdx]._idx = nextSortIdx; 6271 } 6272 } 6273 else { 6274 // No sort on this column yet 6275 sorting.push( [ colIdx, asSorting[0], 0 ] ); 6276 sorting[sorting.length-1]._idx = 0; 6277 } 6278 } 6279 else if ( sorting.length && sorting[0][0] == colIdx ) { 6280 // Single column - already sorting on this column, modify the sort 6281 nextSortIdx = next( sorting[0] ); 6282 6283 sorting.length = 1; 6284 sorting[0][1] = asSorting[ nextSortIdx ]; 6285 sorting[0]._idx = nextSortIdx; 6286 } 6287 else { 6288 // Single column - sort only on this column 6289 sorting.length = 0; 6290 sorting.push( [ colIdx, asSorting[0] ] ); 6291 sorting[0]._idx = 0; 6292 } 6293 6294 // Run the sort by calling a full redraw 6295 _fnReDraw( settings ); 6296 6297 // callback used for async user interaction 6298 if ( typeof callback == 'function' ) { 6299 callback( settings ); 6300 } 6301 } 6302 6303 6304 /** 6305 * Attach a sort handler (click) to a node 6306 * @param {object} settings dataTables settings object 6307 * @param {node} attachTo node to attach the handler to 6308 * @param {int} colIdx column sorting index 6309 * @param {function} [callback] callback function 6310 * @memberof DataTable#oApi 6311 */ 6312 function _fnSortAttachListener ( settings, attachTo, colIdx, callback ) 6313 { 6314 var col = settings.aoColumns[ colIdx ]; 6315 6316 _fnBindAction( attachTo, {}, function (e) { 6317 /* If the column is not sortable - don't to anything */ 6318 if ( col.bSortable === false ) { 6319 return; 6320 } 6321 6322 // If processing is enabled use a timeout to allow the processing 6323 // display to be shown - otherwise to it synchronously 6324 if ( settings.oFeatures.bProcessing ) { 6325 _fnProcessingDisplay( settings, true ); 6326 6327 setTimeout( function() { 6328 _fnSortListener( settings, colIdx, e.shiftKey, callback ); 6329 6330 // In server-side processing, the draw callback will remove the 6331 // processing display 6332 if ( _fnDataSource( settings ) !== 'ssp' ) { 6333 _fnProcessingDisplay( settings, false ); 6334 } 6335 }, 0 ); 6336 } 6337 else { 6338 _fnSortListener( settings, colIdx, e.shiftKey, callback ); 6339 } 6340 } ); 6341 } 6342 6343 6344 /** 6345 * Set the sorting classes on table's body, Note: it is safe to call this function 6346 * when bSort and bSortClasses are false 6347 * @param {object} oSettings dataTables settings object 6348 * @memberof DataTable#oApi 6349 */ 6350 function _fnSortingClasses( settings ) 6351 { 6352 var oldSort = settings.aLastSort; 6353 var sortClass = settings.oClasses.sSortColumn; 6354 var sort = _fnSortFlatten( settings ); 6355 var features = settings.oFeatures; 6356 var i, ien, colIdx; 6357 6358 if ( features.bSort && features.bSortClasses ) { 6359 // Remove old sorting classes 6360 for ( i=0, ien=oldSort.length ; i<ien ; i++ ) { 6361 colIdx = oldSort[i].src; 6362 6363 // Remove column sorting 6364 $( _pluck( settings.aoData, 'anCells', colIdx ) ) 6365 .removeClass( sortClass + (i<2 ? i+1 : 3) ); 6366 } 6367 6368 // Add new column sorting 6369 for ( i=0, ien=sort.length ; i<ien ; i++ ) { 6370 colIdx = sort[i].src; 6371 6372 $( _pluck( settings.aoData, 'anCells', colIdx ) ) 6373 .addClass( sortClass + (i<2 ? i+1 : 3) ); 6374 } 6375 } 6376 6377 settings.aLastSort = sort; 6378 } 6379 6380 6381 // Get the data to sort a column, be it from cache, fresh (populating the 6382 // cache), or from a sort formatter 6383 function _fnSortData( settings, idx ) 6384 { 6385 // Custom sorting function - provided by the sort data type 6386 var column = settings.aoColumns[ idx ]; 6387 var customSort = DataTable.ext.order[ column.sSortDataType ]; 6388 var customData; 6389 6390 if ( customSort ) { 6391 customData = customSort.call( settings.oInstance, settings, idx, 6392 _fnColumnIndexToVisible( settings, idx ) 6393 ); 6394 } 6395 6396 // Use / populate cache 6397 var row, cellData; 6398 var formatter = DataTable.ext.type.order[ column.sType+"-pre" ]; 6399 6400 for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) { 6401 row = settings.aoData[i]; 6402 6403 if ( ! row._aSortData ) { 6404 row._aSortData = []; 6405 } 6406 6407 if ( ! row._aSortData[idx] || customSort ) { 6408 cellData = customSort ? 6409 customData[i] : // If there was a custom sort function, use data from there 6410 _fnGetCellData( settings, i, idx, 'sort' ); 6411 6412 row._aSortData[ idx ] = formatter ? 6413 formatter( cellData ) : 6414 cellData; 6415 } 6416 } 6417 } 6418 6419 6420 6421 /** 6422 * Save the state of a table 6423 * @param {object} oSettings dataTables settings object 6424 * @memberof DataTable#oApi 6425 */ 6426 function _fnSaveState ( settings ) 6427 { 6428 if (settings._bLoadingState) { 6429 return; 6430 } 6431 6432 /* Store the interesting variables */ 6433 var state = { 6434 time: +new Date(), 6435 start: settings._iDisplayStart, 6436 length: settings._iDisplayLength, 6437 order: $.extend( true, [], settings.aaSorting ), 6438 search: _fnSearchToCamel( settings.oPreviousSearch ), 6439 columns: $.map( settings.aoColumns, function ( col, i ) { 6440 return { 6441 visible: col.bVisible, 6442 search: _fnSearchToCamel( settings.aoPreSearchCols[i] ) 6443 }; 6444 } ) 6445 }; 6446 6447 settings.oSavedState = state; 6448 _fnCallbackFire( settings, "aoStateSaveParams", 'stateSaveParams', [settings, state] ); 6449 6450 if ( settings.oFeatures.bStateSave && !settings.bDestroying ) 6451 { 6452 settings.fnStateSaveCallback.call( settings.oInstance, settings, state ); 6453 } 6454 } 6455 6456 6457 /** 6458 * Attempt to load a saved table state 6459 * @param {object} oSettings dataTables settings object 6460 * @param {object} oInit DataTables init object so we can override settings 6461 * @param {function} callback Callback to execute when the state has been loaded 6462 * @memberof DataTable#oApi 6463 */ 6464 function _fnLoadState ( settings, oInit, callback ) 6465 { 6466 if ( ! settings.oFeatures.bStateSave ) { 6467 callback(); 6468 return; 6469 } 6470 6471 var loaded = function(state) { 6472 _fnImplementState(settings, state, callback); 6473 } 6474 6475 var state = settings.fnStateLoadCallback.call( settings.oInstance, settings, loaded ); 6476 6477 if ( state !== undefined ) { 6478 _fnImplementState( settings, state, callback ); 6479 } 6480 // otherwise, wait for the loaded callback to be executed 6481 6482 return true; 6483 } 6484 6485 function _fnImplementState ( settings, s, callback) { 6486 var i, ien; 6487 var columns = settings.aoColumns; 6488 settings._bLoadingState = true; 6489 6490 // When StateRestore was introduced the state could now be implemented at any time 6491 // Not just initialisation. To do this an api instance is required in some places 6492 var api = settings._bInitComplete ? new DataTable.Api(settings) : null; 6493 6494 if ( ! s || ! s.time ) { 6495 settings._bLoadingState = false; 6496 callback(); 6497 return; 6498 } 6499 6500 // Allow custom and plug-in manipulation functions to alter the saved data set and 6501 // cancelling of loading by returning false 6502 var abStateLoad = _fnCallbackFire( settings, 'aoStateLoadParams', 'stateLoadParams', [settings, s] ); 6503 if ( $.inArray( false, abStateLoad ) !== -1 ) { 6504 settings._bLoadingState = false; 6505 callback(); 6506 return; 6507 } 6508 6509 // Reject old data 6510 var duration = settings.iStateDuration; 6511 if ( duration > 0 && s.time < +new Date() - (duration*1000) ) { 6512 settings._bLoadingState = false; 6513 callback(); 6514 return; 6515 } 6516 6517 // Number of columns have changed - all bets are off, no restore of settings 6518 if ( s.columns && columns.length !== s.columns.length ) { 6519 settings._bLoadingState = false; 6520 callback(); 6521 return; 6522 } 6523 6524 // Store the saved state so it might be accessed at any time 6525 settings.oLoadedState = $.extend( true, {}, s ); 6526 6527 // Page Length 6528 if ( s.length !== undefined ) { 6529 // If already initialised just set the value directly so that the select element is also updated 6530 if (api) { 6531 api.page.len(s.length) 6532 } 6533 else { 6534 settings._iDisplayLength = s.length; 6535 } 6536 } 6537 6538 // Restore key features - todo - for 1.11 this needs to be done by 6539 // subscribed events 6540 if ( s.start !== undefined ) { 6541 if(api === null) { 6542 settings._iDisplayStart = s.start; 6543 settings.iInitDisplayStart = s.start; 6544 } 6545 else { 6546 _fnPageChange(settings, s.start/settings._iDisplayLength); 6547 } 6548 } 6549 6550 // Order 6551 if ( s.order !== undefined ) { 6552 settings.aaSorting = []; 6553 $.each( s.order, function ( i, col ) { 6554 settings.aaSorting.push( col[0] >= columns.length ? 6555 [ 0, col[1] ] : 6556 col 6557 ); 6558 } ); 6559 } 6560 6561 // Search 6562 if ( s.search !== undefined ) { 6563 $.extend( settings.oPreviousSearch, _fnSearchToHung( s.search ) ); 6564 } 6565 6566 // Columns 6567 if ( s.columns ) { 6568 for ( i=0, ien=s.columns.length ; i<ien ; i++ ) { 6569 var col = s.columns[i]; 6570 6571 // Visibility 6572 if ( col.visible !== undefined ) { 6573 // If the api is defined, the table has been initialised so we need to use it rather than internal settings 6574 if (api) { 6575 // Don't redraw the columns on every iteration of this loop, we will do this at the end instead 6576 api.column(i).visible(col.visible, false); 6577 } 6578 else { 6579 columns[i].bVisible = col.visible; 6580 } 6581 } 6582 6583 // Search 6584 if ( col.search !== undefined ) { 6585 $.extend( settings.aoPreSearchCols[i], _fnSearchToHung( col.search ) ); 6586 } 6587 } 6588 6589 // If the api is defined then we need to adjust the columns once the visibility has been changed 6590 if (api) { 6591 api.columns.adjust(); 6592 } 6593 } 6594 6595 settings._bLoadingState = false; 6596 _fnCallbackFire( settings, 'aoStateLoaded', 'stateLoaded', [settings, s] ); 6597 callback(); 6598 }; 6599 6600 6601 /** 6602 * Return the settings object for a particular table 6603 * @param {node} table table we are using as a dataTable 6604 * @returns {object} Settings object - or null if not found 6605 * @memberof DataTable#oApi 6606 */ 6607 function _fnSettingsFromNode ( table ) 6608 { 6609 var settings = DataTable.settings; 6610 var idx = $.inArray( table, _pluck( settings, 'nTable' ) ); 6611 6612 return idx !== -1 ? 6613 settings[ idx ] : 6614 null; 6615 } 6616 6617 6618 /** 6619 * Log an error message 6620 * @param {object} settings dataTables settings object 6621 * @param {int} level log error messages, or display them to the user 6622 * @param {string} msg error message 6623 * @param {int} tn Technical note id to get more information about the error. 6624 * @memberof DataTable#oApi 6625 */ 6626 function _fnLog( settings, level, msg, tn ) 6627 { 6628 msg = 'DataTables warning: '+ 6629 (settings ? 'table id='+settings.sTableId+' - ' : '')+msg; 6630 6631 if ( tn ) { 6632 msg += '. For more information about this error, please see '+ 6633 'http://datatables.net/tn/'+tn; 6634 } 6635 6636 if ( ! level ) { 6637 // Backwards compatibility pre 1.10 6638 var ext = DataTable.ext; 6639 var type = ext.sErrMode || ext.errMode; 6640 6641 if ( settings ) { 6642 _fnCallbackFire( settings, null, 'error', [ settings, tn, msg ] ); 6643 } 6644 6645 if ( type == 'alert' ) { 6646 alert( msg ); 6647 } 6648 else if ( type == 'throw' ) { 6649 throw new Error(msg); 6650 } 6651 else if ( typeof type == 'function' ) { 6652 type( settings, tn, msg ); 6653 } 6654 } 6655 else if ( window.console && console.log ) { 6656 console.log( msg ); 6657 } 6658 } 6659 6660 6661 /** 6662 * See if a property is defined on one object, if so assign it to the other object 6663 * @param {object} ret target object 6664 * @param {object} src source object 6665 * @param {string} name property 6666 * @param {string} [mappedName] name to map too - optional, name used if not given 6667 * @memberof DataTable#oApi 6668 */ 6669 function _fnMap( ret, src, name, mappedName ) 6670 { 6671 if ( Array.isArray( name ) ) { 6672 $.each( name, function (i, val) { 6673 if ( Array.isArray( val ) ) { 6674 _fnMap( ret, src, val[0], val[1] ); 6675 } 6676 else { 6677 _fnMap( ret, src, val ); 6678 } 6679 } ); 6680 6681 return; 6682 } 6683 6684 if ( mappedName === undefined ) { 6685 mappedName = name; 6686 } 6687 6688 if ( src[name] !== undefined ) { 6689 ret[mappedName] = src[name]; 6690 } 6691 } 6692 6693 6694 /** 6695 * Extend objects - very similar to jQuery.extend, but deep copy objects, and 6696 * shallow copy arrays. The reason we need to do this, is that we don't want to 6697 * deep copy array init values (such as aaSorting) since the dev wouldn't be 6698 * able to override them, but we do want to deep copy arrays. 6699 * @param {object} out Object to extend 6700 * @param {object} extender Object from which the properties will be applied to 6701 * out 6702 * @param {boolean} breakRefs If true, then arrays will be sliced to take an 6703 * independent copy with the exception of the `data` or `aaData` parameters 6704 * if they are present. This is so you can pass in a collection to 6705 * DataTables and have that used as your data source without breaking the 6706 * references 6707 * @returns {object} out Reference, just for convenience - out === the return. 6708 * @memberof DataTable#oApi 6709 * @todo This doesn't take account of arrays inside the deep copied objects. 6710 */ 6711 function _fnExtend( out, extender, breakRefs ) 6712 { 6713 var val; 6714 6715 for ( var prop in extender ) { 6716 if ( extender.hasOwnProperty(prop) ) { 6717 val = extender[prop]; 6718 6719 if ( $.isPlainObject( val ) ) { 6720 if ( ! $.isPlainObject( out[prop] ) ) { 6721 out[prop] = {}; 6722 } 6723 $.extend( true, out[prop], val ); 6724 } 6725 else if ( breakRefs && prop !== 'data' && prop !== 'aaData' && Array.isArray(val) ) { 6726 out[prop] = val.slice(); 6727 } 6728 else { 6729 out[prop] = val; 6730 } 6731 } 6732 } 6733 6734 return out; 6735 } 6736 6737 6738 /** 6739 * Bind an event handers to allow a click or return key to activate the callback. 6740 * This is good for accessibility since a return on the keyboard will have the 6741 * same effect as a click, if the element has focus. 6742 * @param {element} n Element to bind the action to 6743 * @param {object} oData Data object to pass to the triggered function 6744 * @param {function} fn Callback function for when the event is triggered 6745 * @memberof DataTable#oApi 6746 */ 6747 function _fnBindAction( n, oData, fn ) 6748 { 6749 $(n) 6750 .on( 'click.DT', oData, function (e) { 6751 $(n).trigger('blur'); // Remove focus outline for mouse users 6752 fn(e); 6753 } ) 6754 .on( 'keypress.DT', oData, function (e){ 6755 if ( e.which === 13 ) { 6756 e.preventDefault(); 6757 fn(e); 6758 } 6759 } ) 6760 .on( 'selectstart.DT', function () { 6761 /* Take the brutal approach to cancelling text selection */ 6762 return false; 6763 } ); 6764 } 6765 6766 6767 /** 6768 * Register a callback function. Easily allows a callback function to be added to 6769 * an array store of callback functions that can then all be called together. 6770 * @param {object} oSettings dataTables settings object 6771 * @param {string} sStore Name of the array storage for the callbacks in oSettings 6772 * @param {function} fn Function to be called back 6773 * @param {string} sName Identifying name for the callback (i.e. a label) 6774 * @memberof DataTable#oApi 6775 */ 6776 function _fnCallbackReg( oSettings, sStore, fn, sName ) 6777 { 6778 if ( fn ) 6779 { 6780 oSettings[sStore].push( { 6781 "fn": fn, 6782 "sName": sName 6783 } ); 6784 } 6785 } 6786 6787 6788 /** 6789 * Fire callback functions and trigger events. Note that the loop over the 6790 * callback array store is done backwards! Further note that you do not want to 6791 * fire off triggers in time sensitive applications (for example cell creation) 6792 * as its slow. 6793 * @param {object} settings dataTables settings object 6794 * @param {string} callbackArr Name of the array storage for the callbacks in 6795 * oSettings 6796 * @param {string} eventName Name of the jQuery custom event to trigger. If 6797 * null no trigger is fired 6798 * @param {array} args Array of arguments to pass to the callback function / 6799 * trigger 6800 * @memberof DataTable#oApi 6801 */ 6802 function _fnCallbackFire( settings, callbackArr, eventName, args ) 6803 { 6804 var ret = []; 6805 6806 if ( callbackArr ) { 6807 ret = $.map( settings[callbackArr].slice().reverse(), function (val, i) { 6808 return val.fn.apply( settings.oInstance, args ); 6809 } ); 6810 } 6811 6812 if ( eventName !== null ) { 6813 var e = $.Event( eventName+'.dt' ); 6814 var table = $(settings.nTable); 6815 6816 table.trigger( e, args ); 6817 6818 // If not yet attached to the document, trigger the event 6819 // on the body directly to sort of simulate the bubble 6820 if (table.parents('body').length === 0) { 6821 $('body').trigger( e, args ); 6822 } 6823 6824 ret.push( e.result ); 6825 } 6826 6827 return ret; 6828 } 6829 6830 6831 function _fnLengthOverflow ( settings ) 6832 { 6833 var 6834 start = settings._iDisplayStart, 6835 end = settings.fnDisplayEnd(), 6836 len = settings._iDisplayLength; 6837 6838 /* If we have space to show extra rows (backing up from the end point - then do so */ 6839 if ( start >= end ) 6840 { 6841 start = end - len; 6842 } 6843 6844 // Keep the start record on the current page 6845 start -= (start % len); 6846 6847 if ( len === -1 || start < 0 ) 6848 { 6849 start = 0; 6850 } 6851 6852 settings._iDisplayStart = start; 6853 } 6854 6855 6856 function _fnRenderer( settings, type ) 6857 { 6858 var renderer = settings.renderer; 6859 var host = DataTable.ext.renderer[type]; 6860 6861 if ( $.isPlainObject( renderer ) && renderer[type] ) { 6862 // Specific renderer for this type. If available use it, otherwise use 6863 // the default. 6864 return host[renderer[type]] || host._; 6865 } 6866 else if ( typeof renderer === 'string' ) { 6867 // Common renderer - if there is one available for this type use it, 6868 // otherwise use the default 6869 return host[renderer] || host._; 6870 } 6871 6872 // Use the default 6873 return host._; 6874 } 6875 6876 6877 /** 6878 * Detect the data source being used for the table. Used to simplify the code 6879 * a little (ajax) and to make it compress a little smaller. 6880 * 6881 * @param {object} settings dataTables settings object 6882 * @returns {string} Data source 6883 * @memberof DataTable#oApi 6884 */ 6885 function _fnDataSource ( settings ) 6886 { 6887 if ( settings.oFeatures.bServerSide ) { 6888 return 'ssp'; 6889 } 6890 else if ( settings.ajax || settings.sAjaxSource ) { 6891 return 'ajax'; 6892 } 6893 return 'dom'; 6894 } 6895 6896 6897 6898 6899 /** 6900 * Computed structure of the DataTables API, defined by the options passed to 6901 * `DataTable.Api.register()` when building the API. 6902 * 6903 * The structure is built in order to speed creation and extension of the Api 6904 * objects since the extensions are effectively pre-parsed. 6905 * 6906 * The array is an array of objects with the following structure, where this 6907 * base array represents the Api prototype base: 6908 * 6909 * [ 6910 * { 6911 * name: 'data' -- string - Property name 6912 * val: function () {}, -- function - Api method (or undefined if just an object 6913 * methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result 6914 * propExt: [ ... ] -- array - Array of Api object definitions to extend the property 6915 * }, 6916 * { 6917 * name: 'row' 6918 * val: {}, 6919 * methodExt: [ ... ], 6920 * propExt: [ 6921 * { 6922 * name: 'data' 6923 * val: function () {}, 6924 * methodExt: [ ... ], 6925 * propExt: [ ... ] 6926 * }, 6927 * ... 6928 * ] 6929 * } 6930 * ] 6931 * 6932 * @type {Array} 6933 * @ignore 6934 */ 6935 var __apiStruct = []; 6936 6937 6938 /** 6939 * `Array.prototype` reference. 6940 * 6941 * @type object 6942 * @ignore 6943 */ 6944 var __arrayProto = Array.prototype; 6945 6946 6947 /** 6948 * Abstraction for `context` parameter of the `Api` constructor to allow it to 6949 * take several different forms for ease of use. 6950 * 6951 * Each of the input parameter types will be converted to a DataTables settings 6952 * object where possible. 6953 * 6954 * @param {string|node|jQuery|object} mixed DataTable identifier. Can be one 6955 * of: 6956 * 6957 * * `string` - jQuery selector. Any DataTables' matching the given selector 6958 * with be found and used. 6959 * * `node` - `TABLE` node which has already been formed into a DataTable. 6960 * * `jQuery` - A jQuery object of `TABLE` nodes. 6961 * * `object` - DataTables settings object 6962 * * `DataTables.Api` - API instance 6963 * @return {array|null} Matching DataTables settings objects. `null` or 6964 * `undefined` is returned if no matching DataTable is found. 6965 * @ignore 6966 */ 6967 var _toSettings = function ( mixed ) 6968 { 6969 var idx, jq; 6970 var settings = DataTable.settings; 6971 var tables = $.map( settings, function (el, i) { 6972 return el.nTable; 6973 } ); 6974 6975 if ( ! mixed ) { 6976 return []; 6977 } 6978 else if ( mixed.nTable && mixed.oApi ) { 6979 // DataTables settings object 6980 return [ mixed ]; 6981 } 6982 else if ( mixed.nodeName && mixed.nodeName.toLowerCase() === 'table' ) { 6983 // Table node 6984 idx = $.inArray( mixed, tables ); 6985 return idx !== -1 ? [ settings[idx] ] : null; 6986 } 6987 else if ( mixed && typeof mixed.settings === 'function' ) { 6988 return mixed.settings().toArray(); 6989 } 6990 else if ( typeof mixed === 'string' ) { 6991 // jQuery selector 6992 jq = $(mixed); 6993 } 6994 else if ( mixed instanceof $ ) { 6995 // jQuery object (also DataTables instance) 6996 jq = mixed; 6997 } 6998 6999 if ( jq ) { 7000 return jq.map( function(i) { 7001 idx = $.inArray( this, tables ); 7002 return idx !== -1 ? settings[idx] : null; 7003 } ).toArray(); 7004 } 7005 }; 7006 7007 7008 /** 7009 * DataTables API class - used to control and interface with one or more 7010 * DataTables enhanced tables. 7011 * 7012 * The API class is heavily based on jQuery, presenting a chainable interface 7013 * that you can use to interact with tables. Each instance of the API class has 7014 * a "context" - i.e. the tables that it will operate on. This could be a single 7015 * table, all tables on a page or a sub-set thereof. 7016 * 7017 * Additionally the API is designed to allow you to easily work with the data in 7018 * the tables, retrieving and manipulating it as required. This is done by 7019 * presenting the API class as an array like interface. The contents of the 7020 * array depend upon the actions requested by each method (for example 7021 * `rows().nodes()` will return an array of nodes, while `rows().data()` will 7022 * return an array of objects or arrays depending upon your table's 7023 * configuration). The API object has a number of array like methods (`push`, 7024 * `pop`, `reverse` etc) as well as additional helper methods (`each`, `pluck`, 7025 * `unique` etc) to assist your working with the data held in a table. 7026 * 7027 * Most methods (those which return an Api instance) are chainable, which means 7028 * the return from a method call also has all of the methods available that the 7029 * top level object had. For example, these two calls are equivalent: 7030 * 7031 * // Not chained 7032 * api.row.add( {...} ); 7033 * api.draw(); 7034 * 7035 * // Chained 7036 * api.row.add( {...} ).draw(); 7037 * 7038 * @class DataTable.Api 7039 * @param {array|object|string|jQuery} context DataTable identifier. This is 7040 * used to define which DataTables enhanced tables this API will operate on. 7041 * Can be one of: 7042 * 7043 * * `string` - jQuery selector. Any DataTables' matching the given selector 7044 * with be found and used. 7045 * * `node` - `TABLE` node which has already been formed into a DataTable. 7046 * * `jQuery` - A jQuery object of `TABLE` nodes. 7047 * * `object` - DataTables settings object 7048 * @param {array} [data] Data to initialise the Api instance with. 7049 * 7050 * @example 7051 * // Direct initialisation during DataTables construction 7052 * var api = $('#example').DataTable(); 7053 * 7054 * @example 7055 * // Initialisation using a DataTables jQuery object 7056 * var api = $('#example').dataTable().api(); 7057 * 7058 * @example 7059 * // Initialisation as a constructor 7060 * var api = new $.fn.DataTable.Api( 'table.dataTable' ); 7061 */ 7062 _Api = function ( context, data ) 7063 { 7064 if ( ! (this instanceof _Api) ) { 7065 return new _Api( context, data ); 7066 } 7067 7068 var settings = []; 7069 var ctxSettings = function ( o ) { 7070 var a = _toSettings( o ); 7071 if ( a ) { 7072 settings.push.apply( settings, a ); 7073 } 7074 }; 7075 7076 if ( Array.isArray( context ) ) { 7077 for ( var i=0, ien=context.length ; i<ien ; i++ ) { 7078 ctxSettings( context[i] ); 7079 } 7080 } 7081 else { 7082 ctxSettings( context ); 7083 } 7084 7085 // Remove duplicates 7086 this.context = _unique( settings ); 7087 7088 // Initial data 7089 if ( data ) { 7090 $.merge( this, data ); 7091 } 7092 7093 // selector 7094 this.selector = { 7095 rows: null, 7096 cols: null, 7097 opts: null 7098 }; 7099 7100 _Api.extend( this, this, __apiStruct ); 7101 }; 7102 7103 DataTable.Api = _Api; 7104 7105 // Don't destroy the existing prototype, just extend it. Required for jQuery 2's 7106 // isPlainObject. 7107 $.extend( _Api.prototype, { 7108 any: function () 7109 { 7110 return this.count() !== 0; 7111 }, 7112 7113 7114 concat: __arrayProto.concat, 7115 7116 7117 context: [], // array of table settings objects 7118 7119 7120 count: function () 7121 { 7122 return this.flatten().length; 7123 }, 7124 7125 7126 each: function ( fn ) 7127 { 7128 for ( var i=0, ien=this.length ; i<ien; i++ ) { 7129 fn.call( this, this[i], i, this ); 7130 } 7131 7132 return this; 7133 }, 7134 7135 7136 eq: function ( idx ) 7137 { 7138 var ctx = this.context; 7139 7140 return ctx.length > idx ? 7141 new _Api( ctx[idx], this[idx] ) : 7142 null; 7143 }, 7144 7145 7146 filter: function ( fn ) 7147 { 7148 var a = []; 7149 7150 if ( __arrayProto.filter ) { 7151 a = __arrayProto.filter.call( this, fn, this ); 7152 } 7153 else { 7154 // Compatibility for browsers without EMCA-252-5 (JS 1.6) 7155 for ( var i=0, ien=this.length ; i<ien ; i++ ) { 7156 if ( fn.call( this, this[i], i, this ) ) { 7157 a.push( this[i] ); 7158 } 7159 } 7160 } 7161 7162 return new _Api( this.context, a ); 7163 }, 7164 7165 7166 flatten: function () 7167 { 7168 var a = []; 7169 return new _Api( this.context, a.concat.apply( a, this.toArray() ) ); 7170 }, 7171 7172 7173 join: __arrayProto.join, 7174 7175 7176 indexOf: __arrayProto.indexOf || function (obj, start) 7177 { 7178 for ( var i=(start || 0), ien=this.length ; i<ien ; i++ ) { 7179 if ( this[i] === obj ) { 7180 return i; 7181 } 7182 } 7183 return -1; 7184 }, 7185 7186 iterator: function ( flatten, type, fn, alwaysNew ) { 7187 var 7188 a = [], ret, 7189 i, ien, j, jen, 7190 context = this.context, 7191 rows, items, item, 7192 selector = this.selector; 7193 7194 // Argument shifting 7195 if ( typeof flatten === 'string' ) { 7196 alwaysNew = fn; 7197 fn = type; 7198 type = flatten; 7199 flatten = false; 7200 } 7201 7202 for ( i=0, ien=context.length ; i<ien ; i++ ) { 7203 var apiInst = new _Api( context[i] ); 7204 7205 if ( type === 'table' ) { 7206 ret = fn.call( apiInst, context[i], i ); 7207 7208 if ( ret !== undefined ) { 7209 a.push( ret ); 7210 } 7211 } 7212 else if ( type === 'columns' || type === 'rows' ) { 7213 // this has same length as context - one entry for each table 7214 ret = fn.call( apiInst, context[i], this[i], i ); 7215 7216 if ( ret !== undefined ) { 7217 a.push( ret ); 7218 } 7219 } 7220 else if ( type === 'column' || type === 'column-rows' || type === 'row' || type === 'cell' ) { 7221 // columns and rows share the same structure. 7222 // 'this' is an array of column indexes for each context 7223 items = this[i]; 7224 7225 if ( type === 'column-rows' ) { 7226 rows = _selector_row_indexes( context[i], selector.opts ); 7227 } 7228 7229 for ( j=0, jen=items.length ; j<jen ; j++ ) { 7230 item = items[j]; 7231 7232 if ( type === 'cell' ) { 7233 ret = fn.call( apiInst, context[i], item.row, item.column, i, j ); 7234 } 7235 else { 7236 ret = fn.call( apiInst, context[i], item, i, j, rows ); 7237 } 7238 7239 if ( ret !== undefined ) { 7240 a.push( ret ); 7241 } 7242 } 7243 } 7244 } 7245 7246 if ( a.length || alwaysNew ) { 7247 var api = new _Api( context, flatten ? a.concat.apply( [], a ) : a ); 7248 var apiSelector = api.selector; 7249 apiSelector.rows = selector.rows; 7250 apiSelector.cols = selector.cols; 7251 apiSelector.opts = selector.opts; 7252 return api; 7253 } 7254 return this; 7255 }, 7256 7257 7258 lastIndexOf: __arrayProto.lastIndexOf || function (obj, start) 7259 { 7260 // Bit cheeky... 7261 return this.indexOf.apply( this.toArray.reverse(), arguments ); 7262 }, 7263 7264 7265 length: 0, 7266 7267 7268 map: function ( fn ) 7269 { 7270 var a = []; 7271 7272 if ( __arrayProto.map ) { 7273 a = __arrayProto.map.call( this, fn, this ); 7274 } 7275 else { 7276 // Compatibility for browsers without EMCA-252-5 (JS 1.6) 7277 for ( var i=0, ien=this.length ; i<ien ; i++ ) { 7278 a.push( fn.call( this, this[i], i ) ); 7279 } 7280 } 7281 7282 return new _Api( this.context, a ); 7283 }, 7284 7285 7286 pluck: function ( prop ) 7287 { 7288 var fn = DataTable.util.get(prop); 7289 7290 return this.map( function ( el ) { 7291 return fn(el); 7292 } ); 7293 }, 7294 7295 pop: __arrayProto.pop, 7296 7297 7298 push: __arrayProto.push, 7299 7300 7301 // Does not return an API instance 7302 reduce: __arrayProto.reduce || function ( fn, init ) 7303 { 7304 return _fnReduce( this, fn, init, 0, this.length, 1 ); 7305 }, 7306 7307 7308 reduceRight: __arrayProto.reduceRight || function ( fn, init ) 7309 { 7310 return _fnReduce( this, fn, init, this.length-1, -1, -1 ); 7311 }, 7312 7313 7314 reverse: __arrayProto.reverse, 7315 7316 7317 // Object with rows, columns and opts 7318 selector: null, 7319 7320 7321 shift: __arrayProto.shift, 7322 7323 7324 slice: function () { 7325 return new _Api( this.context, this ); 7326 }, 7327 7328 7329 sort: __arrayProto.sort, // ? name - order? 7330 7331 7332 splice: __arrayProto.splice, 7333 7334 7335 toArray: function () 7336 { 7337 return __arrayProto.slice.call( this ); 7338 }, 7339 7340 7341 to$: function () 7342 { 7343 return $( this ); 7344 }, 7345 7346 7347 toJQuery: function () 7348 { 7349 return $( this ); 7350 }, 7351 7352 7353 unique: function () 7354 { 7355 return new _Api( this.context, _unique(this) ); 7356 }, 7357 7358 7359 unshift: __arrayProto.unshift 7360 } ); 7361 7362 7363 _Api.extend = function ( scope, obj, ext ) 7364 { 7365 // Only extend API instances and static properties of the API 7366 if ( ! ext.length || ! obj || ( ! (obj instanceof _Api) && ! obj.__dt_wrapper ) ) { 7367 return; 7368 } 7369 7370 var 7371 i, ien, 7372 struct, 7373 methodScoping = function ( scope, fn, struc ) { 7374 return function () { 7375 var ret = fn.apply( scope, arguments ); 7376 7377 // Method extension 7378 _Api.extend( ret, ret, struc.methodExt ); 7379 return ret; 7380 }; 7381 }; 7382 7383 for ( i=0, ien=ext.length ; i<ien ; i++ ) { 7384 struct = ext[i]; 7385 7386 // Value 7387 obj[ struct.name ] = struct.type === 'function' ? 7388 methodScoping( scope, struct.val, struct ) : 7389 struct.type === 'object' ? 7390 {} : 7391 struct.val; 7392 7393 obj[ struct.name ].__dt_wrapper = true; 7394 7395 // Property extension 7396 _Api.extend( scope, obj[ struct.name ], struct.propExt ); 7397 } 7398 }; 7399 7400 7401 // @todo - Is there need for an augment function? 7402 // _Api.augment = function ( inst, name ) 7403 // { 7404 // // Find src object in the structure from the name 7405 // var parts = name.split('.'); 7406 7407 // _Api.extend( inst, obj ); 7408 // }; 7409 7410 7411 // [ 7412 // { 7413 // name: 'data' -- string - Property name 7414 // val: function () {}, -- function - Api method (or undefined if just an object 7415 // methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result 7416 // propExt: [ ... ] -- array - Array of Api object definitions to extend the property 7417 // }, 7418 // { 7419 // name: 'row' 7420 // val: {}, 7421 // methodExt: [ ... ], 7422 // propExt: [ 7423 // { 7424 // name: 'data' 7425 // val: function () {}, 7426 // methodExt: [ ... ], 7427 // propExt: [ ... ] 7428 // }, 7429 // ... 7430 // ] 7431 // } 7432 // ] 7433 7434 _Api.register = _api_register = function ( name, val ) 7435 { 7436 if ( Array.isArray( name ) ) { 7437 for ( var j=0, jen=name.length ; j<jen ; j++ ) { 7438 _Api.register( name[j], val ); 7439 } 7440 return; 7441 } 7442 7443 var 7444 i, ien, 7445 heir = name.split('.'), 7446 struct = __apiStruct, 7447 key, method; 7448 7449 var find = function ( src, name ) { 7450 for ( var i=0, ien=src.length ; i<ien ; i++ ) { 7451 if ( src[i].name === name ) { 7452 return src[i]; 7453 } 7454 } 7455 return null; 7456 }; 7457 7458 for ( i=0, ien=heir.length ; i<ien ; i++ ) { 7459 method = heir[i].indexOf('()') !== -1; 7460 key = method ? 7461 heir[i].replace('()', '') : 7462 heir[i]; 7463 7464 var src = find( struct, key ); 7465 if ( ! src ) { 7466 src = { 7467 name: key, 7468 val: {}, 7469 methodExt: [], 7470 propExt: [], 7471 type: 'object' 7472 }; 7473 struct.push( src ); 7474 } 7475 7476 if ( i === ien-1 ) { 7477 src.val = val; 7478 src.type = typeof val === 'function' ? 7479 'function' : 7480 $.isPlainObject( val ) ? 7481 'object' : 7482 'other'; 7483 } 7484 else { 7485 struct = method ? 7486 src.methodExt : 7487 src.propExt; 7488 } 7489 } 7490 }; 7491 7492 _Api.registerPlural = _api_registerPlural = function ( pluralName, singularName, val ) { 7493 _Api.register( pluralName, val ); 7494 7495 _Api.register( singularName, function () { 7496 var ret = val.apply( this, arguments ); 7497 7498 if ( ret === this ) { 7499 // Returned item is the API instance that was passed in, return it 7500 return this; 7501 } 7502 else if ( ret instanceof _Api ) { 7503 // New API instance returned, want the value from the first item 7504 // in the returned array for the singular result. 7505 return ret.length ? 7506 Array.isArray( ret[0] ) ? 7507 new _Api( ret.context, ret[0] ) : // Array results are 'enhanced' 7508 ret[0] : 7509 undefined; 7510 } 7511 7512 // Non-API return - just fire it back 7513 return ret; 7514 } ); 7515 }; 7516 7517 7518 /** 7519 * Selector for HTML tables. Apply the given selector to the give array of 7520 * DataTables settings objects. 7521 * 7522 * @param {string|integer} [selector] jQuery selector string or integer 7523 * @param {array} Array of DataTables settings objects to be filtered 7524 * @return {array} 7525 * @ignore 7526 */ 7527 var __table_selector = function ( selector, a ) 7528 { 7529 if ( Array.isArray(selector) ) { 7530 return $.map( selector, function (item) { 7531 return __table_selector(item, a); 7532 } ); 7533 } 7534 7535 // Integer is used to pick out a table by index 7536 if ( typeof selector === 'number' ) { 7537 return [ a[ selector ] ]; 7538 } 7539 7540 // Perform a jQuery selector on the table nodes 7541 var nodes = $.map( a, function (el, i) { 7542 return el.nTable; 7543 } ); 7544 7545 return $(nodes) 7546 .filter( selector ) 7547 .map( function (i) { 7548 // Need to translate back from the table node to the settings 7549 var idx = $.inArray( this, nodes ); 7550 return a[ idx ]; 7551 } ) 7552 .toArray(); 7553 }; 7554 7555 7556 7557 /** 7558 * Context selector for the API's context (i.e. the tables the API instance 7559 * refers to. 7560 * 7561 * @name DataTable.Api#tables 7562 * @param {string|integer} [selector] Selector to pick which tables the iterator 7563 * should operate on. If not given, all tables in the current context are 7564 * used. This can be given as a jQuery selector (for example `':gt(0)'`) to 7565 * select multiple tables or as an integer to select a single table. 7566 * @returns {DataTable.Api} Returns a new API instance if a selector is given. 7567 */ 7568 _api_register( 'tables()', function ( selector ) { 7569 // A new instance is created if there was a selector specified 7570 return selector !== undefined && selector !== null ? 7571 new _Api( __table_selector( selector, this.context ) ) : 7572 this; 7573 } ); 7574 7575 7576 _api_register( 'table()', function ( selector ) { 7577 var tables = this.tables( selector ); 7578 var ctx = tables.context; 7579 7580 // Truncate to the first matched table 7581 return ctx.length ? 7582 new _Api( ctx[0] ) : 7583 tables; 7584 } ); 7585 7586 7587 _api_registerPlural( 'tables().nodes()', 'table().node()' , function () { 7588 return this.iterator( 'table', function ( ctx ) { 7589 return ctx.nTable; 7590 }, 1 ); 7591 } ); 7592 7593 7594 _api_registerPlural( 'tables().body()', 'table().body()' , function () { 7595 return this.iterator( 'table', function ( ctx ) { 7596 return ctx.nTBody; 7597 }, 1 ); 7598 } ); 7599 7600 7601 _api_registerPlural( 'tables().header()', 'table().header()' , function () { 7602 return this.iterator( 'table', function ( ctx ) { 7603 return ctx.nTHead; 7604 }, 1 ); 7605 } ); 7606 7607 7608 _api_registerPlural( 'tables().footer()', 'table().footer()' , function () { 7609 return this.iterator( 'table', function ( ctx ) { 7610 return ctx.nTFoot; 7611 }, 1 ); 7612 } ); 7613 7614 7615 _api_registerPlural( 'tables().containers()', 'table().container()' , function () { 7616 return this.iterator( 'table', function ( ctx ) { 7617 return ctx.nTableWrapper; 7618 }, 1 ); 7619 } ); 7620 7621 7622 7623 /** 7624 * Redraw the tables in the current context. 7625 */ 7626 _api_register( 'draw()', function ( paging ) { 7627 return this.iterator( 'table', function ( settings ) { 7628 if ( paging === 'page' ) { 7629 _fnDraw( settings ); 7630 } 7631 else { 7632 if ( typeof paging === 'string' ) { 7633 paging = paging === 'full-hold' ? 7634 false : 7635 true; 7636 } 7637 7638 _fnReDraw( settings, paging===false ); 7639 } 7640 } ); 7641 } ); 7642 7643 7644 7645 /** 7646 * Get the current page index. 7647 * 7648 * @return {integer} Current page index (zero based) 7649 *//** 7650 * Set the current page. 7651 * 7652 * Note that if you attempt to show a page which does not exist, DataTables will 7653 * not throw an error, but rather reset the paging. 7654 * 7655 * @param {integer|string} action The paging action to take. This can be one of: 7656 * * `integer` - The page index to jump to 7657 * * `string` - An action to take: 7658 * * `first` - Jump to first page. 7659 * * `next` - Jump to the next page 7660 * * `previous` - Jump to previous page 7661 * * `last` - Jump to the last page. 7662 * @returns {DataTables.Api} this 7663 */ 7664 _api_register( 'page()', function ( action ) { 7665 if ( action === undefined ) { 7666 return this.page.info().page; // not an expensive call 7667 } 7668 7669 // else, have an action to take on all tables 7670 return this.iterator( 'table', function ( settings ) { 7671 _fnPageChange( settings, action ); 7672 } ); 7673 } ); 7674 7675 7676 /** 7677 * Paging information for the first table in the current context. 7678 * 7679 * If you require paging information for another table, use the `table()` method 7680 * with a suitable selector. 7681 * 7682 * @return {object} Object with the following properties set: 7683 * * `page` - Current page index (zero based - i.e. the first page is `0`) 7684 * * `pages` - Total number of pages 7685 * * `start` - Display index for the first record shown on the current page 7686 * * `end` - Display index for the last record shown on the current page 7687 * * `length` - Display length (number of records). Note that generally `start 7688 * + length = end`, but this is not always true, for example if there are 7689 * only 2 records to show on the final page, with a length of 10. 7690 * * `recordsTotal` - Full data set length 7691 * * `recordsDisplay` - Data set length once the current filtering criterion 7692 * are applied. 7693 */ 7694 _api_register( 'page.info()', function ( action ) { 7695 if ( this.context.length === 0 ) { 7696 return undefined; 7697 } 7698 7699 var 7700 settings = this.context[0], 7701 start = settings._iDisplayStart, 7702 len = settings.oFeatures.bPaginate ? settings._iDisplayLength : -1, 7703 visRecords = settings.fnRecordsDisplay(), 7704 all = len === -1; 7705 7706 return { 7707 "page": all ? 0 : Math.floor( start / len ), 7708 "pages": all ? 1 : Math.ceil( visRecords / len ), 7709 "start": start, 7710 "end": settings.fnDisplayEnd(), 7711 "length": len, 7712 "recordsTotal": settings.fnRecordsTotal(), 7713 "recordsDisplay": visRecords, 7714 "serverSide": _fnDataSource( settings ) === 'ssp' 7715 }; 7716 } ); 7717 7718 7719 /** 7720 * Get the current page length. 7721 * 7722 * @return {integer} Current page length. Note `-1` indicates that all records 7723 * are to be shown. 7724 *//** 7725 * Set the current page length. 7726 * 7727 * @param {integer} Page length to set. Use `-1` to show all records. 7728 * @returns {DataTables.Api} this 7729 */ 7730 _api_register( 'page.len()', function ( len ) { 7731 // Note that we can't call this function 'length()' because `length` 7732 // is a Javascript property of functions which defines how many arguments 7733 // the function expects. 7734 if ( len === undefined ) { 7735 return this.context.length !== 0 ? 7736 this.context[0]._iDisplayLength : 7737 undefined; 7738 } 7739 7740 // else, set the page length 7741 return this.iterator( 'table', function ( settings ) { 7742 _fnLengthChange( settings, len ); 7743 } ); 7744 } ); 7745 7746 7747 7748 var __reload = function ( settings, holdPosition, callback ) { 7749 // Use the draw event to trigger a callback 7750 if ( callback ) { 7751 var api = new _Api( settings ); 7752 7753 api.one( 'draw', function () { 7754 callback( api.ajax.json() ); 7755 } ); 7756 } 7757 7758 if ( _fnDataSource( settings ) == 'ssp' ) { 7759 _fnReDraw( settings, holdPosition ); 7760 } 7761 else { 7762 _fnProcessingDisplay( settings, true ); 7763 7764 // Cancel an existing request 7765 var xhr = settings.jqXHR; 7766 if ( xhr && xhr.readyState !== 4 ) { 7767 xhr.abort(); 7768 } 7769 7770 // Trigger xhr 7771 _fnBuildAjax( settings, [], function( json ) { 7772 _fnClearTable( settings ); 7773 7774 var data = _fnAjaxDataSrc( settings, json ); 7775 for ( var i=0, ien=data.length ; i<ien ; i++ ) { 7776 _fnAddData( settings, data[i] ); 7777 } 7778 7779 _fnReDraw( settings, holdPosition ); 7780 _fnProcessingDisplay( settings, false ); 7781 } ); 7782 } 7783 }; 7784 7785 7786 /** 7787 * Get the JSON response from the last Ajax request that DataTables made to the 7788 * server. Note that this returns the JSON from the first table in the current 7789 * context. 7790 * 7791 * @return {object} JSON received from the server. 7792 */ 7793 _api_register( 'ajax.json()', function () { 7794 var ctx = this.context; 7795 7796 if ( ctx.length > 0 ) { 7797 return ctx[0].json; 7798 } 7799 7800 // else return undefined; 7801 } ); 7802 7803 7804 /** 7805 * Get the data submitted in the last Ajax request 7806 */ 7807 _api_register( 'ajax.params()', function () { 7808 var ctx = this.context; 7809 7810 if ( ctx.length > 0 ) { 7811 return ctx[0].oAjaxData; 7812 } 7813 7814 // else return undefined; 7815 } ); 7816 7817 7818 /** 7819 * Reload tables from the Ajax data source. Note that this function will 7820 * automatically re-draw the table when the remote data has been loaded. 7821 * 7822 * @param {boolean} [reset=true] Reset (default) or hold the current paging 7823 * position. A full re-sort and re-filter is performed when this method is 7824 * called, which is why the pagination reset is the default action. 7825 * @returns {DataTables.Api} this 7826 */ 7827 _api_register( 'ajax.reload()', function ( callback, resetPaging ) { 7828 return this.iterator( 'table', function (settings) { 7829 __reload( settings, resetPaging===false, callback ); 7830 } ); 7831 } ); 7832 7833 7834 /** 7835 * Get the current Ajax URL. Note that this returns the URL from the first 7836 * table in the current context. 7837 * 7838 * @return {string} Current Ajax source URL 7839 *//** 7840 * Set the Ajax URL. Note that this will set the URL for all tables in the 7841 * current context. 7842 * 7843 * @param {string} url URL to set. 7844 * @returns {DataTables.Api} this 7845 */ 7846 _api_register( 'ajax.url()', function ( url ) { 7847 var ctx = this.context; 7848 7849 if ( url === undefined ) { 7850 // get 7851 if ( ctx.length === 0 ) { 7852 return undefined; 7853 } 7854 ctx = ctx[0]; 7855 7856 return ctx.ajax ? 7857 $.isPlainObject( ctx.ajax ) ? 7858 ctx.ajax.url : 7859 ctx.ajax : 7860 ctx.sAjaxSource; 7861 } 7862 7863 // set 7864 return this.iterator( 'table', function ( settings ) { 7865 if ( $.isPlainObject( settings.ajax ) ) { 7866 settings.ajax.url = url; 7867 } 7868 else { 7869 settings.ajax = url; 7870 } 7871 // No need to consider sAjaxSource here since DataTables gives priority 7872 // to `ajax` over `sAjaxSource`. So setting `ajax` here, renders any 7873 // value of `sAjaxSource` redundant. 7874 } ); 7875 } ); 7876 7877 7878 /** 7879 * Load data from the newly set Ajax URL. Note that this method is only 7880 * available when `ajax.url()` is used to set a URL. Additionally, this method 7881 * has the same effect as calling `ajax.reload()` but is provided for 7882 * convenience when setting a new URL. Like `ajax.reload()` it will 7883 * automatically redraw the table once the remote data has been loaded. 7884 * 7885 * @returns {DataTables.Api} this 7886 */ 7887 _api_register( 'ajax.url().load()', function ( callback, resetPaging ) { 7888 // Same as a reload, but makes sense to present it for easy access after a 7889 // url change 7890 return this.iterator( 'table', function ( ctx ) { 7891 __reload( ctx, resetPaging===false, callback ); 7892 } ); 7893 } ); 7894 7895 7896 7897 7898 var _selector_run = function ( type, selector, selectFn, settings, opts ) 7899 { 7900 var 7901 out = [], res, 7902 a, i, ien, j, jen, 7903 selectorType = typeof selector; 7904 7905 // Can't just check for isArray here, as an API or jQuery instance might be 7906 // given with their array like look 7907 if ( ! selector || selectorType === 'string' || selectorType === 'function' || selector.length === undefined ) { 7908 selector = [ selector ]; 7909 } 7910 7911 for ( i=0, ien=selector.length ; i<ien ; i++ ) { 7912 // Only split on simple strings - complex expressions will be jQuery selectors 7913 a = selector[i] && selector[i].split && ! selector[i].match(/[\[\(:]/) ? 7914 selector[i].split(',') : 7915 [ selector[i] ]; 7916 7917 for ( j=0, jen=a.length ; j<jen ; j++ ) { 7918 res = selectFn( typeof a[j] === 'string' ? (a[j]).trim() : a[j] ); 7919 7920 if ( res && res.length ) { 7921 out = out.concat( res ); 7922 } 7923 } 7924 } 7925 7926 // selector extensions 7927 var ext = _ext.selector[ type ]; 7928 if ( ext.length ) { 7929 for ( i=0, ien=ext.length ; i<ien ; i++ ) { 7930 out = ext[i]( settings, opts, out ); 7931 } 7932 } 7933 7934 return _unique( out ); 7935 }; 7936 7937 7938 var _selector_opts = function ( opts ) 7939 { 7940 if ( ! opts ) { 7941 opts = {}; 7942 } 7943 7944 // Backwards compatibility for 1.9- which used the terminology filter rather 7945 // than search 7946 if ( opts.filter && opts.search === undefined ) { 7947 opts.search = opts.filter; 7948 } 7949 7950 return $.extend( { 7951 search: 'none', 7952 order: 'current', 7953 page: 'all' 7954 }, opts ); 7955 }; 7956 7957 7958 var _selector_first = function ( inst ) 7959 { 7960 // Reduce the API instance to the first item found 7961 for ( var i=0, ien=inst.length ; i<ien ; i++ ) { 7962 if ( inst[i].length > 0 ) { 7963 // Assign the first element to the first item in the instance 7964 // and truncate the instance and context 7965 inst[0] = inst[i]; 7966 inst[0].length = 1; 7967 inst.length = 1; 7968 inst.context = [ inst.context[i] ]; 7969 7970 return inst; 7971 } 7972 } 7973 7974 // Not found - return an empty instance 7975 inst.length = 0; 7976 return inst; 7977 }; 7978 7979 7980 var _selector_row_indexes = function ( settings, opts ) 7981 { 7982 var 7983 i, ien, tmp, a=[], 7984 displayFiltered = settings.aiDisplay, 7985 displayMaster = settings.aiDisplayMaster; 7986 7987 var 7988 search = opts.search, // none, applied, removed 7989 order = opts.order, // applied, current, index (original - compatibility with 1.9) 7990 page = opts.page; // all, current 7991 7992 if ( _fnDataSource( settings ) == 'ssp' ) { 7993 // In server-side processing mode, most options are irrelevant since 7994 // rows not shown don't exist and the index order is the applied order 7995 // Removed is a special case - for consistency just return an empty 7996 // array 7997 return search === 'removed' ? 7998 [] : 7999 _range( 0, displayMaster.length ); 8000 } 8001 else if ( page == 'current' ) { 8002 // Current page implies that order=current and filter=applied, since it is 8003 // fairly senseless otherwise, regardless of what order and search actually 8004 // are 8005 for ( i=settings._iDisplayStart, ien=settings.fnDisplayEnd() ; i<ien ; i++ ) { 8006 a.push( displayFiltered[i] ); 8007 } 8008 } 8009 else if ( order == 'current' || order == 'applied' ) { 8010 if ( search == 'none') { 8011 a = displayMaster.slice(); 8012 } 8013 else if ( search == 'applied' ) { 8014 a = displayFiltered.slice(); 8015 } 8016 else if ( search == 'removed' ) { 8017 // O(n+m) solution by creating a hash map 8018 var displayFilteredMap = {}; 8019 8020 for ( var i=0, ien=displayFiltered.length ; i<ien ; i++ ) { 8021 displayFilteredMap[displayFiltered[i]] = null; 8022 } 8023 8024 a = $.map( displayMaster, function (el) { 8025 return ! displayFilteredMap.hasOwnProperty(el) ? 8026 el : 8027 null; 8028 } ); 8029 } 8030 } 8031 else if ( order == 'index' || order == 'original' ) { 8032 for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) { 8033 if ( search == 'none' ) { 8034 a.push( i ); 8035 } 8036 else { // applied | removed 8037 tmp = $.inArray( i, displayFiltered ); 8038 8039 if ((tmp === -1 && search == 'removed') || 8040 (tmp >= 0 && search == 'applied') ) 8041 { 8042 a.push( i ); 8043 } 8044 } 8045 } 8046 } 8047 8048 return a; 8049 }; 8050 8051 8052 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 8053 * Rows 8054 * 8055 * {} - no selector - use all available rows 8056 * {integer} - row aoData index 8057 * {node} - TR node 8058 * {string} - jQuery selector to apply to the TR elements 8059 * {array} - jQuery array of nodes, or simply an array of TR nodes 8060 * 8061 */ 8062 var __row_selector = function ( settings, selector, opts ) 8063 { 8064 var rows; 8065 var run = function ( sel ) { 8066 var selInt = _intVal( sel ); 8067 var i, ien; 8068 var aoData = settings.aoData; 8069 8070 // Short cut - selector is a number and no options provided (default is 8071 // all records, so no need to check if the index is in there, since it 8072 // must be - dev error if the index doesn't exist). 8073 if ( selInt !== null && ! opts ) { 8074 return [ selInt ]; 8075 } 8076 8077 if ( ! rows ) { 8078 rows = _selector_row_indexes( settings, opts ); 8079 } 8080 8081 if ( selInt !== null && $.inArray( selInt, rows ) !== -1 ) { 8082 // Selector - integer 8083 return [ selInt ]; 8084 } 8085 else if ( sel === null || sel === undefined || sel === '' ) { 8086 // Selector - none 8087 return rows; 8088 } 8089 8090 // Selector - function 8091 if ( typeof sel === 'function' ) { 8092 return $.map( rows, function (idx) { 8093 var row = aoData[ idx ]; 8094 return sel( idx, row._aData, row.nTr ) ? idx : null; 8095 } ); 8096 } 8097 8098 // Selector - node 8099 if ( sel.nodeName ) { 8100 var rowIdx = sel._DT_RowIndex; // Property added by DT for fast lookup 8101 var cellIdx = sel._DT_CellIndex; 8102 8103 if ( rowIdx !== undefined ) { 8104 // Make sure that the row is actually still present in the table 8105 return aoData[ rowIdx ] && aoData[ rowIdx ].nTr === sel ? 8106 [ rowIdx ] : 8107 []; 8108 } 8109 else if ( cellIdx ) { 8110 return aoData[ cellIdx.row ] && aoData[ cellIdx.row ].nTr === sel.parentNode ? 8111 [ cellIdx.row ] : 8112 []; 8113 } 8114 else { 8115 var host = $(sel).closest('*[data-dt-row]'); 8116 return host.length ? 8117 [ host.data('dt-row') ] : 8118 []; 8119 } 8120 } 8121 8122 // ID selector. Want to always be able to select rows by id, regardless 8123 // of if the tr element has been created or not, so can't rely upon 8124 // jQuery here - hence a custom implementation. This does not match 8125 // Sizzle's fast selector or HTML4 - in HTML5 the ID can be anything, 8126 // but to select it using a CSS selector engine (like Sizzle or 8127 // querySelect) it would need to need to be escaped for some characters. 8128 // DataTables simplifies this for row selectors since you can select 8129 // only a row. A # indicates an id any anything that follows is the id - 8130 // unescaped. 8131 if ( typeof sel === 'string' && sel.charAt(0) === '#' ) { 8132 // get row index from id 8133 var rowObj = settings.aIds[ sel.replace( /^#/, '' ) ]; 8134 if ( rowObj !== undefined ) { 8135 return [ rowObj.idx ]; 8136 } 8137 8138 // need to fall through to jQuery in case there is DOM id that 8139 // matches 8140 } 8141 8142 // Get nodes in the order from the `rows` array with null values removed 8143 var nodes = _removeEmpty( 8144 _pluck_order( settings.aoData, rows, 'nTr' ) 8145 ); 8146 8147 // Selector - jQuery selector string, array of nodes or jQuery object/ 8148 // As jQuery's .filter() allows jQuery objects to be passed in filter, 8149 // it also allows arrays, so this will cope with all three options 8150 return $(nodes) 8151 .filter( sel ) 8152 .map( function () { 8153 return this._DT_RowIndex; 8154 } ) 8155 .toArray(); 8156 }; 8157 8158 return _selector_run( 'row', selector, run, settings, opts ); 8159 }; 8160 8161 8162 _api_register( 'rows()', function ( selector, opts ) { 8163 // argument shifting 8164 if ( selector === undefined ) { 8165 selector = ''; 8166 } 8167 else if ( $.isPlainObject( selector ) ) { 8168 opts = selector; 8169 selector = ''; 8170 } 8171 8172 opts = _selector_opts( opts ); 8173 8174 var inst = this.iterator( 'table', function ( settings ) { 8175 return __row_selector( settings, selector, opts ); 8176 }, 1 ); 8177 8178 // Want argument shifting here and in __row_selector? 8179 inst.selector.rows = selector; 8180 inst.selector.opts = opts; 8181 8182 return inst; 8183 } ); 8184 8185 _api_register( 'rows().nodes()', function () { 8186 return this.iterator( 'row', function ( settings, row ) { 8187 return settings.aoData[ row ].nTr || undefined; 8188 }, 1 ); 8189 } ); 8190 8191 _api_register( 'rows().data()', function () { 8192 return this.iterator( true, 'rows', function ( settings, rows ) { 8193 return _pluck_order( settings.aoData, rows, '_aData' ); 8194 }, 1 ); 8195 } ); 8196 8197 _api_registerPlural( 'rows().cache()', 'row().cache()', function ( type ) { 8198 return this.iterator( 'row', function ( settings, row ) { 8199 var r = settings.aoData[ row ]; 8200 return type === 'search' ? r._aFilterData : r._aSortData; 8201 }, 1 ); 8202 } ); 8203 8204 _api_registerPlural( 'rows().invalidate()', 'row().invalidate()', function ( src ) { 8205 return this.iterator( 'row', function ( settings, row ) { 8206 _fnInvalidate( settings, row, src ); 8207 } ); 8208 } ); 8209 8210 _api_registerPlural( 'rows().indexes()', 'row().index()', function () { 8211 return this.iterator( 'row', function ( settings, row ) { 8212 return row; 8213 }, 1 ); 8214 } ); 8215 8216 _api_registerPlural( 'rows().ids()', 'row().id()', function ( hash ) { 8217 var a = []; 8218 var context = this.context; 8219 8220 // `iterator` will drop undefined values, but in this case we want them 8221 for ( var i=0, ien=context.length ; i<ien ; i++ ) { 8222 for ( var j=0, jen=this[i].length ; j<jen ; j++ ) { 8223 var id = context[i].rowIdFn( context[i].aoData[ this[i][j] ]._aData ); 8224 a.push( (hash === true ? '#' : '' )+ id ); 8225 } 8226 } 8227 8228 return new _Api( context, a ); 8229 } ); 8230 8231 _api_registerPlural( 'rows().remove()', 'row().remove()', function () { 8232 var that = this; 8233 8234 this.iterator( 'row', function ( settings, row, thatIdx ) { 8235 var data = settings.aoData; 8236 var rowData = data[ row ]; 8237 var i, ien, j, jen; 8238 var loopRow, loopCells; 8239 8240 data.splice( row, 1 ); 8241 8242 // Update the cached indexes 8243 for ( i=0, ien=data.length ; i<ien ; i++ ) { 8244 loopRow = data[i]; 8245 loopCells = loopRow.anCells; 8246 8247 // Rows 8248 if ( loopRow.nTr !== null ) { 8249 loopRow.nTr._DT_RowIndex = i; 8250 } 8251 8252 // Cells 8253 if ( loopCells !== null ) { 8254 for ( j=0, jen=loopCells.length ; j<jen ; j++ ) { 8255 loopCells[j]._DT_CellIndex.row = i; 8256 } 8257 } 8258 } 8259 8260 // Delete from the display arrays 8261 _fnDeleteIndex( settings.aiDisplayMaster, row ); 8262 _fnDeleteIndex( settings.aiDisplay, row ); 8263 _fnDeleteIndex( that[ thatIdx ], row, false ); // maintain local indexes 8264 8265 // For server-side processing tables - subtract the deleted row from the count 8266 if ( settings._iRecordsDisplay > 0 ) { 8267 settings._iRecordsDisplay--; 8268 } 8269 8270 // Check for an 'overflow' they case for displaying the table 8271 _fnLengthOverflow( settings ); 8272 8273 // Remove the row's ID reference if there is one 8274 var id = settings.rowIdFn( rowData._aData ); 8275 if ( id !== undefined ) { 8276 delete settings.aIds[ id ]; 8277 } 8278 } ); 8279 8280 this.iterator( 'table', function ( settings ) { 8281 for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) { 8282 settings.aoData[i].idx = i; 8283 } 8284 } ); 8285 8286 return this; 8287 } ); 8288 8289 8290 _api_register( 'rows.add()', function ( rows ) { 8291 var newRows = this.iterator( 'table', function ( settings ) { 8292 var row, i, ien; 8293 var out = []; 8294 8295 for ( i=0, ien=rows.length ; i<ien ; i++ ) { 8296 row = rows[i]; 8297 8298 if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) { 8299 out.push( _fnAddTr( settings, row )[0] ); 8300 } 8301 else { 8302 out.push( _fnAddData( settings, row ) ); 8303 } 8304 } 8305 8306 return out; 8307 }, 1 ); 8308 8309 // Return an Api.rows() extended instance, so rows().nodes() etc can be used 8310 var modRows = this.rows( -1 ); 8311 modRows.pop(); 8312 $.merge( modRows, newRows ); 8313 8314 return modRows; 8315 } ); 8316 8317 8318 8319 8320 8321 /** 8322 * 8323 */ 8324 _api_register( 'row()', function ( selector, opts ) { 8325 return _selector_first( this.rows( selector, opts ) ); 8326 } ); 8327 8328 8329 _api_register( 'row().data()', function ( data ) { 8330 var ctx = this.context; 8331 8332 if ( data === undefined ) { 8333 // Get 8334 return ctx.length && this.length ? 8335 ctx[0].aoData[ this[0] ]._aData : 8336 undefined; 8337 } 8338 8339 // Set 8340 var row = ctx[0].aoData[ this[0] ]; 8341 row._aData = data; 8342 8343 // If the DOM has an id, and the data source is an array 8344 if ( Array.isArray( data ) && row.nTr && row.nTr.id ) { 8345 _fnSetObjectDataFn( ctx[0].rowId )( data, row.nTr.id ); 8346 } 8347 8348 // Automatically invalidate 8349 _fnInvalidate( ctx[0], this[0], 'data' ); 8350 8351 return this; 8352 } ); 8353 8354 8355 _api_register( 'row().node()', function () { 8356 var ctx = this.context; 8357 8358 return ctx.length && this.length ? 8359 ctx[0].aoData[ this[0] ].nTr || null : 8360 null; 8361 } ); 8362 8363 8364 _api_register( 'row.add()', function ( row ) { 8365 // Allow a jQuery object to be passed in - only a single row is added from 8366 // it though - the first element in the set 8367 if ( row instanceof $ && row.length ) { 8368 row = row[0]; 8369 } 8370 8371 var rows = this.iterator( 'table', function ( settings ) { 8372 if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) { 8373 return _fnAddTr( settings, row )[0]; 8374 } 8375 return _fnAddData( settings, row ); 8376 } ); 8377 8378 // Return an Api.rows() extended instance, with the newly added row selected 8379 return this.row( rows[0] ); 8380 } ); 8381 8382 8383 $(document).on('plugin-init.dt', function (e, context) { 8384 var api = new _Api( context ); 8385 var namespace = 'on-plugin-init'; 8386 var stateSaveParamsEvent = 'stateSaveParams.' + namespace; 8387 var destroyEvent = 'destroy. ' + namespace; 8388 8389 api.on( stateSaveParamsEvent, function ( e, settings, d ) { 8390 // This could be more compact with the API, but it is a lot faster as a simple 8391 // internal loop 8392 var idFn = settings.rowIdFn; 8393 var data = settings.aoData; 8394 var ids = []; 8395 8396 for (var i=0 ; i<data.length ; i++) { 8397 if (data[i]._detailsShow) { 8398 ids.push( '#' + idFn(data[i]._aData) ); 8399 } 8400 } 8401 8402 d.childRows = ids; 8403 }); 8404 8405 api.on( destroyEvent, function () { 8406 api.off(stateSaveParamsEvent + ' ' + destroyEvent); 8407 }); 8408 8409 var loaded = api.state.loaded(); 8410 8411 if ( loaded && loaded.childRows ) { 8412 api 8413 .rows( $.map(loaded.childRows, function (id){ 8414 return id.replace(/:/g, '\\:') 8415 }) ) 8416 .every( function () { 8417 _fnCallbackFire( context, null, 'requestChild', [ this ] ) 8418 }); 8419 } 8420 }); 8421 8422 var __details_add = function ( ctx, row, data, klass ) 8423 { 8424 // Convert to array of TR elements 8425 var rows = []; 8426 var addRow = function ( r, k ) { 8427 // Recursion to allow for arrays of jQuery objects 8428 if ( Array.isArray( r ) || r instanceof $ ) { 8429 for ( var i=0, ien=r.length ; i<ien ; i++ ) { 8430 addRow( r[i], k ); 8431 } 8432 return; 8433 } 8434 8435 // If we get a TR element, then just add it directly - up to the dev 8436 // to add the correct number of columns etc 8437 if ( r.nodeName && r.nodeName.toLowerCase() === 'tr' ) { 8438 rows.push( r ); 8439 } 8440 else { 8441 // Otherwise create a row with a wrapper 8442 var created = $('<tr><td></td></tr>').addClass( k ); 8443 $('td', created) 8444 .addClass( k ) 8445 .html( r ) 8446 [0].colSpan = _fnVisbleColumns( ctx ); 8447 8448 rows.push( created[0] ); 8449 } 8450 }; 8451 8452 addRow( data, klass ); 8453 8454 if ( row._details ) { 8455 row._details.detach(); 8456 } 8457 8458 row._details = $(rows); 8459 8460 // If the children were already shown, that state should be retained 8461 if ( row._detailsShow ) { 8462 row._details.insertAfter( row.nTr ); 8463 } 8464 }; 8465 8466 8467 // Make state saving of child row details async to allow them to be batch processed 8468 var __details_state = DataTable.util.throttle( 8469 function (ctx) { 8470 _fnSaveState( ctx[0] ) 8471 }, 8472 500 8473 ); 8474 8475 8476 var __details_remove = function ( api, idx ) 8477 { 8478 var ctx = api.context; 8479 8480 if ( ctx.length ) { 8481 var row = ctx[0].aoData[ idx !== undefined ? idx : api[0] ]; 8482 8483 if ( row && row._details ) { 8484 row._details.remove(); 8485 8486 row._detailsShow = undefined; 8487 row._details = undefined; 8488 $( row.nTr ).removeClass( 'dt-hasChild' ); 8489 __details_state( ctx ); 8490 } 8491 } 8492 }; 8493 8494 8495 var __details_display = function ( api, show ) { 8496 var ctx = api.context; 8497 8498 if ( ctx.length && api.length ) { 8499 var row = ctx[0].aoData[ api[0] ]; 8500 8501 if ( row._details ) { 8502 row._detailsShow = show; 8503 8504 if ( show ) { 8505 row._details.insertAfter( row.nTr ); 8506 $( row.nTr ).addClass( 'dt-hasChild' ); 8507 } 8508 else { 8509 row._details.detach(); 8510 $( row.nTr ).removeClass( 'dt-hasChild' ); 8511 } 8512 8513 _fnCallbackFire( ctx[0], null, 'childRow', [ show, api.row( api[0] ) ] ) 8514 8515 __details_events( ctx[0] ); 8516 __details_state( ctx ); 8517 } 8518 } 8519 }; 8520 8521 8522 var __details_events = function ( settings ) 8523 { 8524 var api = new _Api( settings ); 8525 var namespace = '.dt.DT_details'; 8526 var drawEvent = 'draw'+namespace; 8527 var colvisEvent = 'column-sizing'+namespace; 8528 var destroyEvent = 'destroy'+namespace; 8529 var data = settings.aoData; 8530 8531 api.off( drawEvent +' '+ colvisEvent +' '+ destroyEvent ); 8532 8533 if ( _pluck( data, '_details' ).length > 0 ) { 8534 // On each draw, insert the required elements into the document 8535 api.on( drawEvent, function ( e, ctx ) { 8536 if ( settings !== ctx ) { 8537 return; 8538 } 8539 8540 api.rows( {page:'current'} ).eq(0).each( function (idx) { 8541 // Internal data grab 8542 var row = data[ idx ]; 8543 8544 if ( row._detailsShow ) { 8545 row._details.insertAfter( row.nTr ); 8546 } 8547 } ); 8548 } ); 8549 8550 // Column visibility change - update the colspan 8551 api.on( colvisEvent, function ( e, ctx, idx, vis ) { 8552 if ( settings !== ctx ) { 8553 return; 8554 } 8555 8556 // Update the colspan for the details rows (note, only if it already has 8557 // a colspan) 8558 var row, visible = _fnVisbleColumns( ctx ); 8559 8560 for ( var i=0, ien=data.length ; i<ien ; i++ ) { 8561 row = data[i]; 8562 8563 if ( row._details ) { 8564 row._details.children('td[colspan]').attr('colspan', visible ); 8565 } 8566 } 8567 } ); 8568 8569 // Table destroyed - nuke any child rows 8570 api.on( destroyEvent, function ( e, ctx ) { 8571 if ( settings !== ctx ) { 8572 return; 8573 } 8574 8575 for ( var i=0, ien=data.length ; i<ien ; i++ ) { 8576 if ( data[i]._details ) { 8577 __details_remove( api, i ); 8578 } 8579 } 8580 } ); 8581 } 8582 }; 8583 8584 // Strings for the method names to help minification 8585 var _emp = ''; 8586 var _child_obj = _emp+'row().child'; 8587 var _child_mth = _child_obj+'()'; 8588 8589 // data can be: 8590 // tr 8591 // string 8592 // jQuery or array of any of the above 8593 _api_register( _child_mth, function ( data, klass ) { 8594 var ctx = this.context; 8595 8596 if ( data === undefined ) { 8597 // get 8598 return ctx.length && this.length ? 8599 ctx[0].aoData[ this[0] ]._details : 8600 undefined; 8601 } 8602 else if ( data === true ) { 8603 // show 8604 this.child.show(); 8605 } 8606 else if ( data === false ) { 8607 // remove 8608 __details_remove( this ); 8609 } 8610 else if ( ctx.length && this.length ) { 8611 // set 8612 __details_add( ctx[0], ctx[0].aoData[ this[0] ], data, klass ); 8613 } 8614 8615 return this; 8616 } ); 8617 8618 8619 _api_register( [ 8620 _child_obj+'.show()', 8621 _child_mth+'.show()' // only when `child()` was called with parameters (without 8622 ], function ( show ) { // it returns an object and this method is not executed) 8623 __details_display( this, true ); 8624 return this; 8625 } ); 8626 8627 8628 _api_register( [ 8629 _child_obj+'.hide()', 8630 _child_mth+'.hide()' // only when `child()` was called with parameters (without 8631 ], function () { // it returns an object and this method is not executed) 8632 __details_display( this, false ); 8633 return this; 8634 } ); 8635 8636 8637 _api_register( [ 8638 _child_obj+'.remove()', 8639 _child_mth+'.remove()' // only when `child()` was called with parameters (without 8640 ], function () { // it returns an object and this method is not executed) 8641 __details_remove( this ); 8642 return this; 8643 } ); 8644 8645 8646 _api_register( _child_obj+'.isShown()', function () { 8647 var ctx = this.context; 8648 8649 if ( ctx.length && this.length ) { 8650 // _detailsShown as false or undefined will fall through to return false 8651 return ctx[0].aoData[ this[0] ]._detailsShow || false; 8652 } 8653 return false; 8654 } ); 8655 8656 8657 8658 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 8659 * Columns 8660 * 8661 * {integer} - column index (>=0 count from left, <0 count from right) 8662 * "{integer}:visIdx" - visible column index (i.e. translate to column index) (>=0 count from left, <0 count from right) 8663 * "{integer}:visible" - alias for {integer}:visIdx (>=0 count from left, <0 count from right) 8664 * "{string}:name" - column name 8665 * "{string}" - jQuery selector on column header nodes 8666 * 8667 */ 8668 8669 // can be an array of these items, comma separated list, or an array of comma 8670 // separated lists 8671 8672 var __re_column_selector = /^([^:]+):(name|visIdx|visible)$/; 8673 8674 8675 // r1 and r2 are redundant - but it means that the parameters match for the 8676 // iterator callback in columns().data() 8677 var __columnData = function ( settings, column, r1, r2, rows ) { 8678 var a = []; 8679 for ( var row=0, ien=rows.length ; row<ien ; row++ ) { 8680 a.push( _fnGetCellData( settings, rows[row], column ) ); 8681 } 8682 return a; 8683 }; 8684 8685 8686 var __column_selector = function ( settings, selector, opts ) 8687 { 8688 var 8689 columns = settings.aoColumns, 8690 names = _pluck( columns, 'sName' ), 8691 nodes = _pluck( columns, 'nTh' ); 8692 8693 var run = function ( s ) { 8694 var selInt = _intVal( s ); 8695 8696 // Selector - all 8697 if ( s === '' ) { 8698 return _range( columns.length ); 8699 } 8700 8701 // Selector - index 8702 if ( selInt !== null ) { 8703 return [ selInt >= 0 ? 8704 selInt : // Count from left 8705 columns.length + selInt // Count from right (+ because its a negative value) 8706 ]; 8707 } 8708 8709 // Selector = function 8710 if ( typeof s === 'function' ) { 8711 var rows = _selector_row_indexes( settings, opts ); 8712 8713 return $.map( columns, function (col, idx) { 8714 return s( 8715 idx, 8716 __columnData( settings, idx, 0, 0, rows ), 8717 nodes[ idx ] 8718 ) ? idx : null; 8719 } ); 8720 } 8721 8722 // jQuery or string selector 8723 var match = typeof s === 'string' ? 8724 s.match( __re_column_selector ) : 8725 ''; 8726 8727 if ( match ) { 8728 switch( match[2] ) { 8729 case 'visIdx': 8730 case 'visible': 8731 var idx = parseInt( match[1], 10 ); 8732 // Visible index given, convert to column index 8733 if ( idx < 0 ) { 8734 // Counting from the right 8735 var visColumns = $.map( columns, function (col,i) { 8736 return col.bVisible ? i : null; 8737 } ); 8738 return [ visColumns[ visColumns.length + idx ] ]; 8739 } 8740 // Counting from the left 8741 return [ _fnVisibleToColumnIndex( settings, idx ) ]; 8742 8743 case 'name': 8744 // match by name. `names` is column index complete and in order 8745 return $.map( names, function (name, i) { 8746 return name === match[1] ? i : null; 8747 } ); 8748 8749 default: 8750 return []; 8751 } 8752 } 8753 8754 // Cell in the table body 8755 if ( s.nodeName && s._DT_CellIndex ) { 8756 return [ s._DT_CellIndex.column ]; 8757 } 8758 8759 // jQuery selector on the TH elements for the columns 8760 var jqResult = $( nodes ) 8761 .filter( s ) 8762 .map( function () { 8763 return $.inArray( this, nodes ); // `nodes` is column index complete and in order 8764 } ) 8765 .toArray(); 8766 8767 if ( jqResult.length || ! s.nodeName ) { 8768 return jqResult; 8769 } 8770 8771 // Otherwise a node which might have a `dt-column` data attribute, or be 8772 // a child or such an element 8773 var host = $(s).closest('*[data-dt-column]'); 8774 return host.length ? 8775 [ host.data('dt-column') ] : 8776 []; 8777 }; 8778 8779 return _selector_run( 'column', selector, run, settings, opts ); 8780 }; 8781 8782 8783 var __setColumnVis = function ( settings, column, vis ) { 8784 var 8785 cols = settings.aoColumns, 8786 col = cols[ column ], 8787 data = settings.aoData, 8788 row, cells, i, ien, tr; 8789 8790 // Get 8791 if ( vis === undefined ) { 8792 return col.bVisible; 8793 } 8794 8795 // Set 8796 // No change 8797 if ( col.bVisible === vis ) { 8798 return; 8799 } 8800 8801 if ( vis ) { 8802 // Insert column 8803 // Need to decide if we should use appendChild or insertBefore 8804 var insertBefore = $.inArray( true, _pluck(cols, 'bVisible'), column+1 ); 8805 8806 for ( i=0, ien=data.length ; i<ien ; i++ ) { 8807 tr = data[i].nTr; 8808 cells = data[i].anCells; 8809 8810 if ( tr ) { 8811 // insertBefore can act like appendChild if 2nd arg is null 8812 tr.insertBefore( cells[ column ], cells[ insertBefore ] || null ); 8813 } 8814 } 8815 } 8816 else { 8817 // Remove column 8818 $( _pluck( settings.aoData, 'anCells', column ) ).detach(); 8819 } 8820 8821 // Common actions 8822 col.bVisible = vis; 8823 }; 8824 8825 8826 _api_register( 'columns()', function ( selector, opts ) { 8827 // argument shifting 8828 if ( selector === undefined ) { 8829 selector = ''; 8830 } 8831 else if ( $.isPlainObject( selector ) ) { 8832 opts = selector; 8833 selector = ''; 8834 } 8835 8836 opts = _selector_opts( opts ); 8837 8838 var inst = this.iterator( 'table', function ( settings ) { 8839 return __column_selector( settings, selector, opts ); 8840 }, 1 ); 8841 8842 // Want argument shifting here and in _row_selector? 8843 inst.selector.cols = selector; 8844 inst.selector.opts = opts; 8845 8846 return inst; 8847 } ); 8848 8849 _api_registerPlural( 'columns().header()', 'column().header()', function ( selector, opts ) { 8850 return this.iterator( 'column', function ( settings, column ) { 8851 return settings.aoColumns[column].nTh; 8852 }, 1 ); 8853 } ); 8854 8855 _api_registerPlural( 'columns().footer()', 'column().footer()', function ( selector, opts ) { 8856 return this.iterator( 'column', function ( settings, column ) { 8857 return settings.aoColumns[column].nTf; 8858 }, 1 ); 8859 } ); 8860 8861 _api_registerPlural( 'columns().data()', 'column().data()', function () { 8862 return this.iterator( 'column-rows', __columnData, 1 ); 8863 } ); 8864 8865 _api_registerPlural( 'columns().dataSrc()', 'column().dataSrc()', function () { 8866 return this.iterator( 'column', function ( settings, column ) { 8867 return settings.aoColumns[column].mData; 8868 }, 1 ); 8869 } ); 8870 8871 _api_registerPlural( 'columns().cache()', 'column().cache()', function ( type ) { 8872 return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) { 8873 return _pluck_order( settings.aoData, rows, 8874 type === 'search' ? '_aFilterData' : '_aSortData', column 8875 ); 8876 }, 1 ); 8877 } ); 8878 8879 _api_registerPlural( 'columns().nodes()', 'column().nodes()', function () { 8880 return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) { 8881 return _pluck_order( settings.aoData, rows, 'anCells', column ) ; 8882 }, 1 ); 8883 } ); 8884 8885 _api_registerPlural( 'columns().visible()', 'column().visible()', function ( vis, calc ) { 8886 var that = this; 8887 var ret = this.iterator( 'column', function ( settings, column ) { 8888 if ( vis === undefined ) { 8889 return settings.aoColumns[ column ].bVisible; 8890 } // else 8891 __setColumnVis( settings, column, vis ); 8892 } ); 8893 8894 // Group the column visibility changes 8895 if ( vis !== undefined ) { 8896 this.iterator( 'table', function ( settings ) { 8897 // Redraw the header after changes 8898 _fnDrawHead( settings, settings.aoHeader ); 8899 _fnDrawHead( settings, settings.aoFooter ); 8900 8901 // Update colspan for no records display. Child rows and extensions will use their own 8902 // listeners to do this - only need to update the empty table item here 8903 if ( ! settings.aiDisplay.length ) { 8904 $(settings.nTBody).find('td[colspan]').attr('colspan', _fnVisbleColumns(settings)); 8905 } 8906 8907 _fnSaveState( settings ); 8908 8909 // Second loop once the first is done for events 8910 that.iterator( 'column', function ( settings, column ) { 8911 _fnCallbackFire( settings, null, 'column-visibility', [settings, column, vis, calc] ); 8912 } ); 8913 8914 if ( calc === undefined || calc ) { 8915 that.columns.adjust(); 8916 } 8917 }); 8918 } 8919 8920 return ret; 8921 } ); 8922 8923 _api_registerPlural( 'columns().indexes()', 'column().index()', function ( type ) { 8924 return this.iterator( 'column', function ( settings, column ) { 8925 return type === 'visible' ? 8926 _fnColumnIndexToVisible( settings, column ) : 8927 column; 8928 }, 1 ); 8929 } ); 8930 8931 _api_register( 'columns.adjust()', function () { 8932 return this.iterator( 'table', function ( settings ) { 8933 _fnAdjustColumnSizing( settings ); 8934 }, 1 ); 8935 } ); 8936 8937 _api_register( 'column.index()', function ( type, idx ) { 8938 if ( this.context.length !== 0 ) { 8939 var ctx = this.context[0]; 8940 8941 if ( type === 'fromVisible' || type === 'toData' ) { 8942 return _fnVisibleToColumnIndex( ctx, idx ); 8943 } 8944 else if ( type === 'fromData' || type === 'toVisible' ) { 8945 return _fnColumnIndexToVisible( ctx, idx ); 8946 } 8947 } 8948 } ); 8949 8950 _api_register( 'column()', function ( selector, opts ) { 8951 return _selector_first( this.columns( selector, opts ) ); 8952 } ); 8953 8954 var __cell_selector = function ( settings, selector, opts ) 8955 { 8956 var data = settings.aoData; 8957 var rows = _selector_row_indexes( settings, opts ); 8958 var cells = _removeEmpty( _pluck_order( data, rows, 'anCells' ) ); 8959 var allCells = $(_flatten( [], cells )); 8960 var row; 8961 var columns = settings.aoColumns.length; 8962 var a, i, ien, j, o, host; 8963 8964 var run = function ( s ) { 8965 var fnSelector = typeof s === 'function'; 8966 8967 if ( s === null || s === undefined || fnSelector ) { 8968 // All cells and function selectors 8969 a = []; 8970 8971 for ( i=0, ien=rows.length ; i<ien ; i++ ) { 8972 row = rows[i]; 8973 8974 for ( j=0 ; j<columns ; j++ ) { 8975 o = { 8976 row: row, 8977 column: j 8978 }; 8979 8980 if ( fnSelector ) { 8981 // Selector - function 8982 host = data[ row ]; 8983 8984 if ( s( o, _fnGetCellData(settings, row, j), host.anCells ? host.anCells[j] : null ) ) { 8985 a.push( o ); 8986 } 8987 } 8988 else { 8989 // Selector - all 8990 a.push( o ); 8991 } 8992 } 8993 } 8994 8995 return a; 8996 } 8997 8998 // Selector - index 8999 if ( $.isPlainObject( s ) ) { 9000 // Valid cell index and its in the array of selectable rows 9001 return s.column !== undefined && s.row !== undefined && $.inArray( s.row, rows ) !== -1 ? 9002 [s] : 9003 []; 9004 } 9005 9006 // Selector - jQuery filtered cells 9007 var jqResult = allCells 9008 .filter( s ) 9009 .map( function (i, el) { 9010 return { // use a new object, in case someone changes the values 9011 row: el._DT_CellIndex.row, 9012 column: el._DT_CellIndex.column 9013 }; 9014 } ) 9015 .toArray(); 9016 9017 if ( jqResult.length || ! s.nodeName ) { 9018 return jqResult; 9019 } 9020 9021 // Otherwise the selector is a node, and there is one last option - the 9022 // element might be a child of an element which has dt-row and dt-column 9023 // data attributes 9024 host = $(s).closest('*[data-dt-row]'); 9025 return host.length ? 9026 [ { 9027 row: host.data('dt-row'), 9028 column: host.data('dt-column') 9029 } ] : 9030 []; 9031 }; 9032 9033 return _selector_run( 'cell', selector, run, settings, opts ); 9034 }; 9035 9036 9037 9038 9039 _api_register( 'cells()', function ( rowSelector, columnSelector, opts ) { 9040 // Argument shifting 9041 if ( $.isPlainObject( rowSelector ) ) { 9042 // Indexes 9043 if ( rowSelector.row === undefined ) { 9044 // Selector options in first parameter 9045 opts = rowSelector; 9046 rowSelector = null; 9047 } 9048 else { 9049 // Cell index objects in first parameter 9050 opts = columnSelector; 9051 columnSelector = null; 9052 } 9053 } 9054 if ( $.isPlainObject( columnSelector ) ) { 9055 opts = columnSelector; 9056 columnSelector = null; 9057 } 9058 9059 // Cell selector 9060 if ( columnSelector === null || columnSelector === undefined ) { 9061 return this.iterator( 'table', function ( settings ) { 9062 return __cell_selector( settings, rowSelector, _selector_opts( opts ) ); 9063 } ); 9064 } 9065 9066 // The default built in options need to apply to row and columns 9067 var internalOpts = opts ? { 9068 page: opts.page, 9069 order: opts.order, 9070 search: opts.search 9071 } : {}; 9072 9073 // Row + column selector 9074 var columns = this.columns( columnSelector, internalOpts ); 9075 var rows = this.rows( rowSelector, internalOpts ); 9076 var i, ien, j, jen; 9077 9078 var cellsNoOpts = this.iterator( 'table', function ( settings, idx ) { 9079 var a = []; 9080 9081 for ( i=0, ien=rows[idx].length ; i<ien ; i++ ) { 9082 for ( j=0, jen=columns[idx].length ; j<jen ; j++ ) { 9083 a.push( { 9084 row: rows[idx][i], 9085 column: columns[idx][j] 9086 } ); 9087 } 9088 } 9089 9090 return a; 9091 }, 1 ); 9092 9093 // There is currently only one extension which uses a cell selector extension 9094 // It is a _major_ performance drag to run this if it isn't needed, so this is 9095 // an extension specific check at the moment 9096 var cells = opts && opts.selected ? 9097 this.cells( cellsNoOpts, opts ) : 9098 cellsNoOpts; 9099 9100 $.extend( cells.selector, { 9101 cols: columnSelector, 9102 rows: rowSelector, 9103 opts: opts 9104 } ); 9105 9106 return cells; 9107 } ); 9108 9109 9110 _api_registerPlural( 'cells().nodes()', 'cell().node()', function () { 9111 return this.iterator( 'cell', function ( settings, row, column ) { 9112 var data = settings.aoData[ row ]; 9113 9114 return data && data.anCells ? 9115 data.anCells[ column ] : 9116 undefined; 9117 }, 1 ); 9118 } ); 9119 9120 9121 _api_register( 'cells().data()', function () { 9122 return this.iterator( 'cell', function ( settings, row, column ) { 9123 return _fnGetCellData( settings, row, column ); 9124 }, 1 ); 9125 } ); 9126 9127 9128 _api_registerPlural( 'cells().cache()', 'cell().cache()', function ( type ) { 9129 type = type === 'search' ? '_aFilterData' : '_aSortData'; 9130 9131 return this.iterator( 'cell', function ( settings, row, column ) { 9132 return settings.aoData[ row ][ type ][ column ]; 9133 }, 1 ); 9134 } ); 9135 9136 9137 _api_registerPlural( 'cells().render()', 'cell().render()', function ( type ) { 9138 return this.iterator( 'cell', function ( settings, row, column ) { 9139 return _fnGetCellData( settings, row, column, type ); 9140 }, 1 ); 9141 } ); 9142 9143 9144 _api_registerPlural( 'cells().indexes()', 'cell().index()', function () { 9145 return this.iterator( 'cell', function ( settings, row, column ) { 9146 return { 9147 row: row, 9148 column: column, 9149 columnVisible: _fnColumnIndexToVisible( settings, column ) 9150 }; 9151 }, 1 ); 9152 } ); 9153 9154 9155 _api_registerPlural( 'cells().invalidate()', 'cell().invalidate()', function ( src ) { 9156 return this.iterator( 'cell', function ( settings, row, column ) { 9157 _fnInvalidate( settings, row, src, column ); 9158 } ); 9159 } ); 9160 9161 9162 9163 _api_register( 'cell()', function ( rowSelector, columnSelector, opts ) { 9164 return _selector_first( this.cells( rowSelector, columnSelector, opts ) ); 9165 } ); 9166 9167 9168 _api_register( 'cell().data()', function ( data ) { 9169 var ctx = this.context; 9170 var cell = this[0]; 9171 9172 if ( data === undefined ) { 9173 // Get 9174 return ctx.length && cell.length ? 9175 _fnGetCellData( ctx[0], cell[0].row, cell[0].column ) : 9176 undefined; 9177 } 9178 9179 // Set 9180 _fnSetCellData( ctx[0], cell[0].row, cell[0].column, data ); 9181 _fnInvalidate( ctx[0], cell[0].row, 'data', cell[0].column ); 9182 9183 return this; 9184 } ); 9185 9186 9187 9188 /** 9189 * Get current ordering (sorting) that has been applied to the table. 9190 * 9191 * @returns {array} 2D array containing the sorting information for the first 9192 * table in the current context. Each element in the parent array represents 9193 * a column being sorted upon (i.e. multi-sorting with two columns would have 9194 * 2 inner arrays). The inner arrays may have 2 or 3 elements. The first is 9195 * the column index that the sorting condition applies to, the second is the 9196 * direction of the sort (`desc` or `asc`) and, optionally, the third is the 9197 * index of the sorting order from the `column.sorting` initialisation array. 9198 *//** 9199 * Set the ordering for the table. 9200 * 9201 * @param {integer} order Column index to sort upon. 9202 * @param {string} direction Direction of the sort to be applied (`asc` or `desc`) 9203 * @returns {DataTables.Api} this 9204 *//** 9205 * Set the ordering for the table. 9206 * 9207 * @param {array} order 1D array of sorting information to be applied. 9208 * @param {array} [...] Optional additional sorting conditions 9209 * @returns {DataTables.Api} this 9210 *//** 9211 * Set the ordering for the table. 9212 * 9213 * @param {array} order 2D array of sorting information to be applied. 9214 * @returns {DataTables.Api} this 9215 */ 9216 _api_register( 'order()', function ( order, dir ) { 9217 var ctx = this.context; 9218 9219 if ( order === undefined ) { 9220 // get 9221 return ctx.length !== 0 ? 9222 ctx[0].aaSorting : 9223 undefined; 9224 } 9225 9226 // set 9227 if ( typeof order === 'number' ) { 9228 // Simple column / direction passed in 9229 order = [ [ order, dir ] ]; 9230 } 9231 else if ( order.length && ! Array.isArray( order[0] ) ) { 9232 // Arguments passed in (list of 1D arrays) 9233 order = Array.prototype.slice.call( arguments ); 9234 } 9235 // otherwise a 2D array was passed in 9236 9237 return this.iterator( 'table', function ( settings ) { 9238 settings.aaSorting = order.slice(); 9239 } ); 9240 } ); 9241 9242 9243 /** 9244 * Attach a sort listener to an element for a given column 9245 * 9246 * @param {node|jQuery|string} node Identifier for the element(s) to attach the 9247 * listener to. This can take the form of a single DOM node, a jQuery 9248 * collection of nodes or a jQuery selector which will identify the node(s). 9249 * @param {integer} column the column that a click on this node will sort on 9250 * @param {function} [callback] callback function when sort is run 9251 * @returns {DataTables.Api} this 9252 */ 9253 _api_register( 'order.listener()', function ( node, column, callback ) { 9254 return this.iterator( 'table', function ( settings ) { 9255 _fnSortAttachListener( settings, node, column, callback ); 9256 } ); 9257 } ); 9258 9259 9260 _api_register( 'order.fixed()', function ( set ) { 9261 if ( ! set ) { 9262 var ctx = this.context; 9263 var fixed = ctx.length ? 9264 ctx[0].aaSortingFixed : 9265 undefined; 9266 9267 return Array.isArray( fixed ) ? 9268 { pre: fixed } : 9269 fixed; 9270 } 9271 9272 return this.iterator( 'table', function ( settings ) { 9273 settings.aaSortingFixed = $.extend( true, {}, set ); 9274 } ); 9275 } ); 9276 9277 9278 // Order by the selected column(s) 9279 _api_register( [ 9280 'columns().order()', 9281 'column().order()' 9282 ], function ( dir ) { 9283 var that = this; 9284 9285 return this.iterator( 'table', function ( settings, i ) { 9286 var sort = []; 9287 9288 $.each( that[i], function (j, col) { 9289 sort.push( [ col, dir ] ); 9290 } ); 9291 9292 settings.aaSorting = sort; 9293 } ); 9294 } ); 9295 9296 9297 9298 _api_register( 'search()', function ( input, regex, smart, caseInsen ) { 9299 var ctx = this.context; 9300 9301 if ( input === undefined ) { 9302 // get 9303 return ctx.length !== 0 ? 9304 ctx[0].oPreviousSearch.sSearch : 9305 undefined; 9306 } 9307 9308 // set 9309 return this.iterator( 'table', function ( settings ) { 9310 if ( ! settings.oFeatures.bFilter ) { 9311 return; 9312 } 9313 9314 _fnFilterComplete( settings, $.extend( {}, settings.oPreviousSearch, { 9315 "sSearch": input+"", 9316 "bRegex": regex === null ? false : regex, 9317 "bSmart": smart === null ? true : smart, 9318 "bCaseInsensitive": caseInsen === null ? true : caseInsen 9319 } ), 1 ); 9320 } ); 9321 } ); 9322 9323 9324 _api_registerPlural( 9325 'columns().search()', 9326 'column().search()', 9327 function ( input, regex, smart, caseInsen ) { 9328 return this.iterator( 'column', function ( settings, column ) { 9329 var preSearch = settings.aoPreSearchCols; 9330 9331 if ( input === undefined ) { 9332 // get 9333 return preSearch[ column ].sSearch; 9334 } 9335 9336 // set 9337 if ( ! settings.oFeatures.bFilter ) { 9338 return; 9339 } 9340 9341 $.extend( preSearch[ column ], { 9342 "sSearch": input+"", 9343 "bRegex": regex === null ? false : regex, 9344 "bSmart": smart === null ? true : smart, 9345 "bCaseInsensitive": caseInsen === null ? true : caseInsen 9346 } ); 9347 9348 _fnFilterComplete( settings, settings.oPreviousSearch, 1 ); 9349 } ); 9350 } 9351 ); 9352 9353 /* 9354 * State API methods 9355 */ 9356 9357 _api_register( 'state()', function () { 9358 return this.context.length ? 9359 this.context[0].oSavedState : 9360 null; 9361 } ); 9362 9363 9364 _api_register( 'state.clear()', function () { 9365 return this.iterator( 'table', function ( settings ) { 9366 // Save an empty object 9367 settings.fnStateSaveCallback.call( settings.oInstance, settings, {} ); 9368 } ); 9369 } ); 9370 9371 9372 _api_register( 'state.loaded()', function () { 9373 return this.context.length ? 9374 this.context[0].oLoadedState : 9375 null; 9376 } ); 9377 9378 9379 _api_register( 'state.save()', function () { 9380 return this.iterator( 'table', function ( settings ) { 9381 _fnSaveState( settings ); 9382 } ); 9383 } ); 9384 9385 9386 9387 /** 9388 * Set the jQuery or window object to be used by DataTables 9389 * 9390 * @param {*} module Library / container object 9391 * @param {string} [type] Library or container type `lib`, `win` or `datetime`. 9392 * If not provided, automatic detection is attempted. 9393 */ 9394 DataTable.use = function (module, type) { 9395 if (type === 'lib' || module.fn) { 9396 $ = module; 9397 } 9398 else if (type == 'win' || module.document) { 9399 window = module; 9400 document = module.document; 9401 } 9402 else if (type === 'datetime' || module.type === 'DateTime') { 9403 DataTable.DateTime = module; 9404 } 9405 } 9406 9407 /** 9408 * CommonJS factory function pass through. This will check if the arguments 9409 * given are a window object or a jQuery object. If so they are set 9410 * accordingly. 9411 * @param {*} root Window 9412 * @param {*} jq jQUery 9413 * @returns {boolean} Indicator 9414 */ 9415 DataTable.factory = function (root, jq) { 9416 var is = false; 9417 9418 // Test if the first parameter is a window object 9419 if (root && root.document) { 9420 window = root; 9421 document = root.document; 9422 } 9423 9424 // Test if the second parameter is a jQuery object 9425 if (jq && jq.fn && jq.fn.jquery) { 9426 $ = jq; 9427 is = true; 9428 } 9429 9430 return is; 9431 } 9432 9433 /** 9434 * Provide a common method for plug-ins to check the version of DataTables being 9435 * used, in order to ensure compatibility. 9436 * 9437 * @param {string} version Version string to check for, in the format "X.Y.Z". 9438 * Note that the formats "X" and "X.Y" are also acceptable. 9439 * @returns {boolean} true if this version of DataTables is greater or equal to 9440 * the required version, or false if this version of DataTales is not 9441 * suitable 9442 * @static 9443 * @dtopt API-Static 9444 * 9445 * @example 9446 * alert( $.fn.dataTable.versionCheck( '1.9.0' ) ); 9447 */ 9448 DataTable.versionCheck = DataTable.fnVersionCheck = function( version ) 9449 { 9450 var aThis = DataTable.version.split('.'); 9451 var aThat = version.split('.'); 9452 var iThis, iThat; 9453 9454 for ( var i=0, iLen=aThat.length ; i<iLen ; i++ ) { 9455 iThis = parseInt( aThis[i], 10 ) || 0; 9456 iThat = parseInt( aThat[i], 10 ) || 0; 9457 9458 // Parts are the same, keep comparing 9459 if (iThis === iThat) { 9460 continue; 9461 } 9462 9463 // Parts are different, return immediately 9464 return iThis > iThat; 9465 } 9466 9467 return true; 9468 }; 9469 9470 9471 /** 9472 * Check if a `<table>` node is a DataTable table already or not. 9473 * 9474 * @param {node|jquery|string} table Table node, jQuery object or jQuery 9475 * selector for the table to test. Note that if more than more than one 9476 * table is passed on, only the first will be checked 9477 * @returns {boolean} true the table given is a DataTable, or false otherwise 9478 * @static 9479 * @dtopt API-Static 9480 * 9481 * @example 9482 * if ( ! $.fn.DataTable.isDataTable( '#example' ) ) { 9483 * $('#example').dataTable(); 9484 * } 9485 */ 9486 DataTable.isDataTable = DataTable.fnIsDataTable = function ( table ) 9487 { 9488 var t = $(table).get(0); 9489 var is = false; 9490 9491 if ( table instanceof DataTable.Api ) { 9492 return true; 9493 } 9494 9495 $.each( DataTable.settings, function (i, o) { 9496 var head = o.nScrollHead ? $('table', o.nScrollHead)[0] : null; 9497 var foot = o.nScrollFoot ? $('table', o.nScrollFoot)[0] : null; 9498 9499 if ( o.nTable === t || head === t || foot === t ) { 9500 is = true; 9501 } 9502 } ); 9503 9504 return is; 9505 }; 9506 9507 9508 /** 9509 * Get all DataTable tables that have been initialised - optionally you can 9510 * select to get only currently visible tables. 9511 * 9512 * @param {boolean} [visible=false] Flag to indicate if you want all (default) 9513 * or visible tables only. 9514 * @returns {array} Array of `table` nodes (not DataTable instances) which are 9515 * DataTables 9516 * @static 9517 * @dtopt API-Static 9518 * 9519 * @example 9520 * $.each( $.fn.dataTable.tables(true), function () { 9521 * $(table).DataTable().columns.adjust(); 9522 * } ); 9523 */ 9524 DataTable.tables = DataTable.fnTables = function ( visible ) 9525 { 9526 var api = false; 9527 9528 if ( $.isPlainObject( visible ) ) { 9529 api = visible.api; 9530 visible = visible.visible; 9531 } 9532 9533 var a = $.map( DataTable.settings, function (o) { 9534 if ( !visible || (visible && $(o.nTable).is(':visible')) ) { 9535 return o.nTable; 9536 } 9537 } ); 9538 9539 return api ? 9540 new _Api( a ) : 9541 a; 9542 }; 9543 9544 9545 /** 9546 * Convert from camel case parameters to Hungarian notation. This is made public 9547 * for the extensions to provide the same ability as DataTables core to accept 9548 * either the 1.9 style Hungarian notation, or the 1.10+ style camelCase 9549 * parameters. 9550 * 9551 * @param {object} src The model object which holds all parameters that can be 9552 * mapped. 9553 * @param {object} user The object to convert from camel case to Hungarian. 9554 * @param {boolean} force When set to `true`, properties which already have a 9555 * Hungarian value in the `user` object will be overwritten. Otherwise they 9556 * won't be. 9557 */ 9558 DataTable.camelToHungarian = _fnCamelToHungarian; 9559 9560 9561 9562 /** 9563 * 9564 */ 9565 _api_register( '$()', function ( selector, opts ) { 9566 var 9567 rows = this.rows( opts ).nodes(), // Get all rows 9568 jqRows = $(rows); 9569 9570 return $( [].concat( 9571 jqRows.filter( selector ).toArray(), 9572 jqRows.find( selector ).toArray() 9573 ) ); 9574 } ); 9575 9576 9577 // jQuery functions to operate on the tables 9578 $.each( [ 'on', 'one', 'off' ], function (i, key) { 9579 _api_register( key+'()', function ( /* event, handler */ ) { 9580 var args = Array.prototype.slice.call(arguments); 9581 9582 // Add the `dt` namespace automatically if it isn't already present 9583 args[0] = $.map( args[0].split( /\s/ ), function ( e ) { 9584 return ! e.match(/\.dt\b/) ? 9585 e+'.dt' : 9586 e; 9587 } ).join( ' ' ); 9588 9589 var inst = $( this.tables().nodes() ); 9590 inst[key].apply( inst, args ); 9591 return this; 9592 } ); 9593 } ); 9594 9595 9596 _api_register( 'clear()', function () { 9597 return this.iterator( 'table', function ( settings ) { 9598 _fnClearTable( settings ); 9599 } ); 9600 } ); 9601 9602 9603 _api_register( 'settings()', function () { 9604 return new _Api( this.context, this.context ); 9605 } ); 9606 9607 9608 _api_register( 'init()', function () { 9609 var ctx = this.context; 9610 return ctx.length ? ctx[0].oInit : null; 9611 } ); 9612 9613 9614 _api_register( 'data()', function () { 9615 return this.iterator( 'table', function ( settings ) { 9616 return _pluck( settings.aoData, '_aData' ); 9617 } ).flatten(); 9618 } ); 9619 9620 9621 _api_register( 'destroy()', function ( remove ) { 9622 remove = remove || false; 9623 9624 return this.iterator( 'table', function ( settings ) { 9625 var classes = settings.oClasses; 9626 var table = settings.nTable; 9627 var tbody = settings.nTBody; 9628 var thead = settings.nTHead; 9629 var tfoot = settings.nTFoot; 9630 var jqTable = $(table); 9631 var jqTbody = $(tbody); 9632 var jqWrapper = $(settings.nTableWrapper); 9633 var rows = $.map( settings.aoData, function (r) { return r.nTr; } ); 9634 var i, ien; 9635 9636 // Flag to note that the table is currently being destroyed - no action 9637 // should be taken 9638 settings.bDestroying = true; 9639 9640 // Fire off the destroy callbacks for plug-ins etc 9641 _fnCallbackFire( settings, "aoDestroyCallback", "destroy", [settings] ); 9642 9643 // If not being removed from the document, make all columns visible 9644 if ( ! remove ) { 9645 new _Api( settings ).columns().visible( true ); 9646 } 9647 9648 // Blitz all `DT` namespaced events (these are internal events, the 9649 // lowercase, `dt` events are user subscribed and they are responsible 9650 // for removing them 9651 jqWrapper.off('.DT').find(':not(tbody *)').off('.DT'); 9652 $(window).off('.DT-'+settings.sInstance); 9653 9654 // When scrolling we had to break the table up - restore it 9655 if ( table != thead.parentNode ) { 9656 jqTable.children('thead').detach(); 9657 jqTable.append( thead ); 9658 } 9659 9660 if ( tfoot && table != tfoot.parentNode ) { 9661 jqTable.children('tfoot').detach(); 9662 jqTable.append( tfoot ); 9663 } 9664 9665 settings.aaSorting = []; 9666 settings.aaSortingFixed = []; 9667 _fnSortingClasses( settings ); 9668 9669 $( rows ).removeClass( settings.asStripeClasses.join(' ') ); 9670 9671 $('th, td', thead).removeClass( classes.sSortable+' '+ 9672 classes.sSortableAsc+' '+classes.sSortableDesc+' '+classes.sSortableNone 9673 ); 9674 9675 // Add the TR elements back into the table in their original order 9676 jqTbody.children().detach(); 9677 jqTbody.append( rows ); 9678 9679 var orig = settings.nTableWrapper.parentNode; 9680 9681 // Remove the DataTables generated nodes, events and classes 9682 var removedMethod = remove ? 'remove' : 'detach'; 9683 jqTable[ removedMethod ](); 9684 jqWrapper[ removedMethod ](); 9685 9686 // If we need to reattach the table to the document 9687 if ( ! remove && orig ) { 9688 // insertBefore acts like appendChild if !arg[1] 9689 orig.insertBefore( table, settings.nTableReinsertBefore ); 9690 9691 // Restore the width of the original table - was read from the style property, 9692 // so we can restore directly to that 9693 jqTable 9694 .css( 'width', settings.sDestroyWidth ) 9695 .removeClass( classes.sTable ); 9696 9697 // If the were originally stripe classes - then we add them back here. 9698 // Note this is not fool proof (for example if not all rows had stripe 9699 // classes - but it's a good effort without getting carried away 9700 ien = settings.asDestroyStripes.length; 9701 9702 if ( ien ) { 9703 jqTbody.children().each( function (i) { 9704 $(this).addClass( settings.asDestroyStripes[i % ien] ); 9705 } ); 9706 } 9707 } 9708 9709 /* Remove the settings object from the settings array */ 9710 var idx = $.inArray( settings, DataTable.settings ); 9711 if ( idx !== -1 ) { 9712 DataTable.settings.splice( idx, 1 ); 9713 } 9714 } ); 9715 } ); 9716 9717 9718 // Add the `every()` method for rows, columns and cells in a compact form 9719 $.each( [ 'column', 'row', 'cell' ], function ( i, type ) { 9720 _api_register( type+'s().every()', function ( fn ) { 9721 var opts = this.selector.opts; 9722 var api = this; 9723 9724 return this.iterator( type, function ( settings, arg1, arg2, arg3, arg4 ) { 9725 // Rows and columns: 9726 // arg1 - index 9727 // arg2 - table counter 9728 // arg3 - loop counter 9729 // arg4 - undefined 9730 // Cells: 9731 // arg1 - row index 9732 // arg2 - column index 9733 // arg3 - table counter 9734 // arg4 - loop counter 9735 fn.call( 9736 api[ type ]( 9737 arg1, 9738 type==='cell' ? arg2 : opts, 9739 type==='cell' ? opts : undefined 9740 ), 9741 arg1, arg2, arg3, arg4 9742 ); 9743 } ); 9744 } ); 9745 } ); 9746 9747 9748 // i18n method for extensions to be able to use the language object from the 9749 // DataTable 9750 _api_register( 'i18n()', function ( token, def, plural ) { 9751 var ctx = this.context[0]; 9752 var resolved = _fnGetObjectDataFn( token )( ctx.oLanguage ); 9753 9754 if ( resolved === undefined ) { 9755 resolved = def; 9756 } 9757 9758 if ( plural !== undefined && $.isPlainObject( resolved ) ) { 9759 resolved = resolved[ plural ] !== undefined ? 9760 resolved[ plural ] : 9761 resolved._; 9762 } 9763 9764 return typeof resolved === 'string' 9765 ? resolved.replace( '%d', plural ) // nb: plural might be undefined, 9766 : resolved; 9767 } ); 9768 /** 9769 * Version string for plug-ins to check compatibility. Allowed format is 9770 * `a.b.c-d` where: a:int, b:int, c:int, d:string(dev|beta|alpha). `d` is used 9771 * only for non-release builds. See http://semver.org/ for more information. 9772 * @member 9773 * @type string 9774 * @default Version number 9775 */ 9776 DataTable.version = "1.13.5"; 9777 9778 /** 9779 * Private data store, containing all of the settings objects that are 9780 * created for the tables on a given page. 9781 * 9782 * Note that the `DataTable.settings` object is aliased to 9783 * `jQuery.fn.dataTableExt` through which it may be accessed and 9784 * manipulated, or `jQuery.fn.dataTable.settings`. 9785 * @member 9786 * @type array 9787 * @default [] 9788 * @private 9789 */ 9790 DataTable.settings = []; 9791 9792 /** 9793 * Object models container, for the various models that DataTables has 9794 * available to it. These models define the objects that are used to hold 9795 * the active state and configuration of the table. 9796 * @namespace 9797 */ 9798 DataTable.models = {}; 9799 9800 9801 9802 /** 9803 * Template object for the way in which DataTables holds information about 9804 * search information for the global filter and individual column filters. 9805 * @namespace 9806 */ 9807 DataTable.models.oSearch = { 9808 /** 9809 * Flag to indicate if the filtering should be case insensitive or not 9810 * @type boolean 9811 * @default true 9812 */ 9813 "bCaseInsensitive": true, 9814 9815 /** 9816 * Applied search term 9817 * @type string 9818 * @default <i>Empty string</i> 9819 */ 9820 "sSearch": "", 9821 9822 /** 9823 * Flag to indicate if the search term should be interpreted as a 9824 * regular expression (true) or not (false) and therefore and special 9825 * regex characters escaped. 9826 * @type boolean 9827 * @default false 9828 */ 9829 "bRegex": false, 9830 9831 /** 9832 * Flag to indicate if DataTables is to use its smart filtering or not. 9833 * @type boolean 9834 * @default true 9835 */ 9836 "bSmart": true, 9837 9838 /** 9839 * Flag to indicate if DataTables should only trigger a search when 9840 * the return key is pressed. 9841 * @type boolean 9842 * @default false 9843 */ 9844 "return": false 9845 }; 9846 9847 9848 9849 9850 /** 9851 * Template object for the way in which DataTables holds information about 9852 * each individual row. This is the object format used for the settings 9853 * aoData array. 9854 * @namespace 9855 */ 9856 DataTable.models.oRow = { 9857 /** 9858 * TR element for the row 9859 * @type node 9860 * @default null 9861 */ 9862 "nTr": null, 9863 9864 /** 9865 * Array of TD elements for each row. This is null until the row has been 9866 * created. 9867 * @type array nodes 9868 * @default [] 9869 */ 9870 "anCells": null, 9871 9872 /** 9873 * Data object from the original data source for the row. This is either 9874 * an array if using the traditional form of DataTables, or an object if 9875 * using mData options. The exact type will depend on the passed in 9876 * data from the data source, or will be an array if using DOM a data 9877 * source. 9878 * @type array|object 9879 * @default [] 9880 */ 9881 "_aData": [], 9882 9883 /** 9884 * Sorting data cache - this array is ostensibly the same length as the 9885 * number of columns (although each index is generated only as it is 9886 * needed), and holds the data that is used for sorting each column in the 9887 * row. We do this cache generation at the start of the sort in order that 9888 * the formatting of the sort data need be done only once for each cell 9889 * per sort. This array should not be read from or written to by anything 9890 * other than the master sorting methods. 9891 * @type array 9892 * @default null 9893 * @private 9894 */ 9895 "_aSortData": null, 9896 9897 /** 9898 * Per cell filtering data cache. As per the sort data cache, used to 9899 * increase the performance of the filtering in DataTables 9900 * @type array 9901 * @default null 9902 * @private 9903 */ 9904 "_aFilterData": null, 9905 9906 /** 9907 * Filtering data cache. This is the same as the cell filtering cache, but 9908 * in this case a string rather than an array. This is easily computed with 9909 * a join on `_aFilterData`, but is provided as a cache so the join isn't 9910 * needed on every search (memory traded for performance) 9911 * @type array 9912 * @default null 9913 * @private 9914 */ 9915 "_sFilterRow": null, 9916 9917 /** 9918 * Cache of the class name that DataTables has applied to the row, so we 9919 * can quickly look at this variable rather than needing to do a DOM check 9920 * on className for the nTr property. 9921 * @type string 9922 * @default <i>Empty string</i> 9923 * @private 9924 */ 9925 "_sRowStripe": "", 9926 9927 /** 9928 * Denote if the original data source was from the DOM, or the data source 9929 * object. This is used for invalidating data, so DataTables can 9930 * automatically read data from the original source, unless uninstructed 9931 * otherwise. 9932 * @type string 9933 * @default null 9934 * @private 9935 */ 9936 "src": null, 9937 9938 /** 9939 * Index in the aoData array. This saves an indexOf lookup when we have the 9940 * object, but want to know the index 9941 * @type integer 9942 * @default -1 9943 * @private 9944 */ 9945 "idx": -1 9946 }; 9947 9948 9949 /** 9950 * Template object for the column information object in DataTables. This object 9951 * is held in the settings aoColumns array and contains all the information that 9952 * DataTables needs about each individual column. 9953 * 9954 * Note that this object is related to {@link DataTable.defaults.column} 9955 * but this one is the internal data store for DataTables's cache of columns. 9956 * It should NOT be manipulated outside of DataTables. Any configuration should 9957 * be done through the initialisation options. 9958 * @namespace 9959 */ 9960 DataTable.models.oColumn = { 9961 /** 9962 * Column index. This could be worked out on-the-fly with $.inArray, but it 9963 * is faster to just hold it as a variable 9964 * @type integer 9965 * @default null 9966 */ 9967 "idx": null, 9968 9969 /** 9970 * A list of the columns that sorting should occur on when this column 9971 * is sorted. That this property is an array allows multi-column sorting 9972 * to be defined for a column (for example first name / last name columns 9973 * would benefit from this). The values are integers pointing to the 9974 * columns to be sorted on (typically it will be a single integer pointing 9975 * at itself, but that doesn't need to be the case). 9976 * @type array 9977 */ 9978 "aDataSort": null, 9979 9980 /** 9981 * Define the sorting directions that are applied to the column, in sequence 9982 * as the column is repeatedly sorted upon - i.e. the first value is used 9983 * as the sorting direction when the column if first sorted (clicked on). 9984 * Sort it again (click again) and it will move on to the next index. 9985 * Repeat until loop. 9986 * @type array 9987 */ 9988 "asSorting": null, 9989 9990 /** 9991 * Flag to indicate if the column is searchable, and thus should be included 9992 * in the filtering or not. 9993 * @type boolean 9994 */ 9995 "bSearchable": null, 9996 9997 /** 9998 * Flag to indicate if the column is sortable or not. 9999 * @type boolean 10000 */ 10001 "bSortable": null, 10002 10003 /** 10004 * Flag to indicate if the column is currently visible in the table or not 10005 * @type boolean 10006 */ 10007 "bVisible": null, 10008 10009 /** 10010 * Store for manual type assignment using the `column.type` option. This 10011 * is held in store so we can manipulate the column's `sType` property. 10012 * @type string 10013 * @default null 10014 * @private 10015 */ 10016 "_sManualType": null, 10017 10018 /** 10019 * Flag to indicate if HTML5 data attributes should be used as the data 10020 * source for filtering or sorting. True is either are. 10021 * @type boolean 10022 * @default false 10023 * @private 10024 */ 10025 "_bAttrSrc": false, 10026 10027 /** 10028 * Developer definable function that is called whenever a cell is created (Ajax source, 10029 * etc) or processed for input (DOM source). This can be used as a compliment to mRender 10030 * allowing you to modify the DOM element (add background colour for example) when the 10031 * element is available. 10032 * @type function 10033 * @param {element} nTd The TD node that has been created 10034 * @param {*} sData The Data for the cell 10035 * @param {array|object} oData The data for the whole row 10036 * @param {int} iRow The row index for the aoData data store 10037 * @default null 10038 */ 10039 "fnCreatedCell": null, 10040 10041 /** 10042 * Function to get data from a cell in a column. You should <b>never</b> 10043 * access data directly through _aData internally in DataTables - always use 10044 * the method attached to this property. It allows mData to function as 10045 * required. This function is automatically assigned by the column 10046 * initialisation method 10047 * @type function 10048 * @param {array|object} oData The data array/object for the array 10049 * (i.e. aoData[]._aData) 10050 * @param {string} sSpecific The specific data type you want to get - 10051 * 'display', 'type' 'filter' 'sort' 10052 * @returns {*} The data for the cell from the given row's data 10053 * @default null 10054 */ 10055 "fnGetData": null, 10056 10057 /** 10058 * Function to set data for a cell in the column. You should <b>never</b> 10059 * set the data directly to _aData internally in DataTables - always use 10060 * this method. It allows mData to function as required. This function 10061 * is automatically assigned by the column initialisation method 10062 * @type function 10063 * @param {array|object} oData The data array/object for the array 10064 * (i.e. aoData[]._aData) 10065 * @param {*} sValue Value to set 10066 * @default null 10067 */ 10068 "fnSetData": null, 10069 10070 /** 10071 * Property to read the value for the cells in the column from the data 10072 * source array / object. If null, then the default content is used, if a 10073 * function is given then the return from the function is used. 10074 * @type function|int|string|null 10075 * @default null 10076 */ 10077 "mData": null, 10078 10079 /** 10080 * Partner property to mData which is used (only when defined) to get 10081 * the data - i.e. it is basically the same as mData, but without the 10082 * 'set' option, and also the data fed to it is the result from mData. 10083 * This is the rendering method to match the data method of mData. 10084 * @type function|int|string|null 10085 * @default null 10086 */ 10087 "mRender": null, 10088 10089 /** 10090 * Unique header TH/TD element for this column - this is what the sorting 10091 * listener is attached to (if sorting is enabled.) 10092 * @type node 10093 * @default null 10094 */ 10095 "nTh": null, 10096 10097 /** 10098 * Unique footer TH/TD element for this column (if there is one). Not used 10099 * in DataTables as such, but can be used for plug-ins to reference the 10100 * footer for each column. 10101 * @type node 10102 * @default null 10103 */ 10104 "nTf": null, 10105 10106 /** 10107 * The class to apply to all TD elements in the table's TBODY for the column 10108 * @type string 10109 * @default null 10110 */ 10111 "sClass": null, 10112 10113 /** 10114 * When DataTables calculates the column widths to assign to each column, 10115 * it finds the longest string in each column and then constructs a 10116 * temporary table and reads the widths from that. The problem with this 10117 * is that "mmm" is much wider then "iiii", but the latter is a longer 10118 * string - thus the calculation can go wrong (doing it properly and putting 10119 * it into an DOM object and measuring that is horribly(!) slow). Thus as 10120 * a "work around" we provide this option. It will append its value to the 10121 * text that is found to be the longest string for the column - i.e. padding. 10122 * @type string 10123 */ 10124 "sContentPadding": null, 10125 10126 /** 10127 * Allows a default value to be given for a column's data, and will be used 10128 * whenever a null data source is encountered (this can be because mData 10129 * is set to null, or because the data source itself is null). 10130 * @type string 10131 * @default null 10132 */ 10133 "sDefaultContent": null, 10134 10135 /** 10136 * Name for the column, allowing reference to the column by name as well as 10137 * by index (needs a lookup to work by name). 10138 * @type string 10139 */ 10140 "sName": null, 10141 10142 /** 10143 * Custom sorting data type - defines which of the available plug-ins in 10144 * afnSortData the custom sorting will use - if any is defined. 10145 * @type string 10146 * @default std 10147 */ 10148 "sSortDataType": 'std', 10149 10150 /** 10151 * Class to be applied to the header element when sorting on this column 10152 * @type string 10153 * @default null 10154 */ 10155 "sSortingClass": null, 10156 10157 /** 10158 * Class to be applied to the header element when sorting on this column - 10159 * when jQuery UI theming is used. 10160 * @type string 10161 * @default null 10162 */ 10163 "sSortingClassJUI": null, 10164 10165 /** 10166 * Title of the column - what is seen in the TH element (nTh). 10167 * @type string 10168 */ 10169 "sTitle": null, 10170 10171 /** 10172 * Column sorting and filtering type 10173 * @type string 10174 * @default null 10175 */ 10176 "sType": null, 10177 10178 /** 10179 * Width of the column 10180 * @type string 10181 * @default null 10182 */ 10183 "sWidth": null, 10184 10185 /** 10186 * Width of the column when it was first "encountered" 10187 * @type string 10188 * @default null 10189 */ 10190 "sWidthOrig": null 10191 }; 10192 10193 10194 /* 10195 * Developer note: The properties of the object below are given in Hungarian 10196 * notation, that was used as the interface for DataTables prior to v1.10, however 10197 * from v1.10 onwards the primary interface is camel case. In order to avoid 10198 * breaking backwards compatibility utterly with this change, the Hungarian 10199 * version is still, internally the primary interface, but is is not documented 10200 * - hence the @name tags in each doc comment. This allows a Javascript function 10201 * to create a map from Hungarian notation to camel case (going the other direction 10202 * would require each property to be listed, which would add around 3K to the size 10203 * of DataTables, while this method is about a 0.5K hit). 10204 * 10205 * Ultimately this does pave the way for Hungarian notation to be dropped 10206 * completely, but that is a massive amount of work and will break current 10207 * installs (therefore is on-hold until v2). 10208 */ 10209 10210 /** 10211 * Initialisation options that can be given to DataTables at initialisation 10212 * time. 10213 * @namespace 10214 */ 10215 DataTable.defaults = { 10216 /** 10217 * An array of data to use for the table, passed in at initialisation which 10218 * will be used in preference to any data which is already in the DOM. This is 10219 * particularly useful for constructing tables purely in Javascript, for 10220 * example with a custom Ajax call. 10221 * @type array 10222 * @default null 10223 * 10224 * @dtopt Option 10225 * @name DataTable.defaults.data 10226 * 10227 * @example 10228 * // Using a 2D array data source 10229 * $(document).ready( function () { 10230 * $('#example').dataTable( { 10231 * "data": [ 10232 * ['Trident', 'Internet Explorer 4.0', 'Win 95+', 4, 'X'], 10233 * ['Trident', 'Internet Explorer 5.0', 'Win 95+', 5, 'C'], 10234 * ], 10235 * "columns": [ 10236 * { "title": "Engine" }, 10237 * { "title": "Browser" }, 10238 * { "title": "Platform" }, 10239 * { "title": "Version" }, 10240 * { "title": "Grade" } 10241 * ] 10242 * } ); 10243 * } ); 10244 * 10245 * @example 10246 * // Using an array of objects as a data source (`data`) 10247 * $(document).ready( function () { 10248 * $('#example').dataTable( { 10249 * "data": [ 10250 * { 10251 * "engine": "Trident", 10252 * "browser": "Internet Explorer 4.0", 10253 * "platform": "Win 95+", 10254 * "version": 4, 10255 * "grade": "X" 10256 * }, 10257 * { 10258 * "engine": "Trident", 10259 * "browser": "Internet Explorer 5.0", 10260 * "platform": "Win 95+", 10261 * "version": 5, 10262 * "grade": "C" 10263 * } 10264 * ], 10265 * "columns": [ 10266 * { "title": "Engine", "data": "engine" }, 10267 * { "title": "Browser", "data": "browser" }, 10268 * { "title": "Platform", "data": "platform" }, 10269 * { "title": "Version", "data": "version" }, 10270 * { "title": "Grade", "data": "grade" } 10271 * ] 10272 * } ); 10273 * } ); 10274 */ 10275 "aaData": null, 10276 10277 10278 /** 10279 * If ordering is enabled, then DataTables will perform a first pass sort on 10280 * initialisation. You can define which column(s) the sort is performed 10281 * upon, and the sorting direction, with this variable. The `sorting` array 10282 * should contain an array for each column to be sorted initially containing 10283 * the column's index and a direction string ('asc' or 'desc'). 10284 * @type array 10285 * @default [[0,'asc']] 10286 * 10287 * @dtopt Option 10288 * @name DataTable.defaults.order 10289 * 10290 * @example 10291 * // Sort by 3rd column first, and then 4th column 10292 * $(document).ready( function() { 10293 * $('#example').dataTable( { 10294 * "order": [[2,'asc'], [3,'desc']] 10295 * } ); 10296 * } ); 10297 * 10298 * // No initial sorting 10299 * $(document).ready( function() { 10300 * $('#example').dataTable( { 10301 * "order": [] 10302 * } ); 10303 * } ); 10304 */ 10305 "aaSorting": [[0,'asc']], 10306 10307 10308 /** 10309 * This parameter is basically identical to the `sorting` parameter, but 10310 * cannot be overridden by user interaction with the table. What this means 10311 * is that you could have a column (visible or hidden) which the sorting 10312 * will always be forced on first - any sorting after that (from the user) 10313 * will then be performed as required. This can be useful for grouping rows 10314 * together. 10315 * @type array 10316 * @default null 10317 * 10318 * @dtopt Option 10319 * @name DataTable.defaults.orderFixed 10320 * 10321 * @example 10322 * $(document).ready( function() { 10323 * $('#example').dataTable( { 10324 * "orderFixed": [[0,'asc']] 10325 * } ); 10326 * } ) 10327 */ 10328 "aaSortingFixed": [], 10329 10330 10331 /** 10332 * DataTables can be instructed to load data to display in the table from a 10333 * Ajax source. This option defines how that Ajax call is made and where to. 10334 * 10335 * The `ajax` property has three different modes of operation, depending on 10336 * how it is defined. These are: 10337 * 10338 * * `string` - Set the URL from where the data should be loaded from. 10339 * * `object` - Define properties for `jQuery.ajax`. 10340 * * `function` - Custom data get function 10341 * 10342 * `string` 10343 * -------- 10344 * 10345 * As a string, the `ajax` property simply defines the URL from which 10346 * DataTables will load data. 10347 * 10348 * `object` 10349 * -------- 10350 * 10351 * As an object, the parameters in the object are passed to 10352 * [jQuery.ajax](http://api.jquery.com/jQuery.ajax/) allowing fine control 10353 * of the Ajax request. DataTables has a number of default parameters which 10354 * you can override using this option. Please refer to the jQuery 10355 * documentation for a full description of the options available, although 10356 * the following parameters provide additional options in DataTables or 10357 * require special consideration: 10358 * 10359 * * `data` - As with jQuery, `data` can be provided as an object, but it 10360 * can also be used as a function to manipulate the data DataTables sends 10361 * to the server. The function takes a single parameter, an object of 10362 * parameters with the values that DataTables has readied for sending. An 10363 * object may be returned which will be merged into the DataTables 10364 * defaults, or you can add the items to the object that was passed in and 10365 * not return anything from the function. This supersedes `fnServerParams` 10366 * from DataTables 1.9-. 10367 * 10368 * * `dataSrc` - By default DataTables will look for the property `data` (or 10369 * `aaData` for compatibility with DataTables 1.9-) when obtaining data 10370 * from an Ajax source or for server-side processing - this parameter 10371 * allows that property to be changed. You can use Javascript dotted 10372 * object notation to get a data source for multiple levels of nesting, or 10373 * it my be used as a function. As a function it takes a single parameter, 10374 * the JSON returned from the server, which can be manipulated as 10375 * required, with the returned value being that used by DataTables as the 10376 * data source for the table. This supersedes `sAjaxDataProp` from 10377 * DataTables 1.9-. 10378 * 10379 * * `success` - Should not be overridden it is used internally in 10380 * DataTables. To manipulate / transform the data returned by the server 10381 * use `ajax.dataSrc`, or use `ajax` as a function (see below). 10382 * 10383 * `function` 10384 * ---------- 10385 * 10386 * As a function, making the Ajax call is left up to yourself allowing 10387 * complete control of the Ajax request. Indeed, if desired, a method other 10388 * than Ajax could be used to obtain the required data, such as Web storage 10389 * or an AIR database. 10390 * 10391 * The function is given four parameters and no return is required. The 10392 * parameters are: 10393 * 10394 * 1. _object_ - Data to send to the server 10395 * 2. _function_ - Callback function that must be executed when the required 10396 * data has been obtained. That data should be passed into the callback 10397 * as the only parameter 10398 * 3. _object_ - DataTables settings object for the table 10399 * 10400 * Note that this supersedes `fnServerData` from DataTables 1.9-. 10401 * 10402 * @type string|object|function 10403 * @default null 10404 * 10405 * @dtopt Option 10406 * @name DataTable.defaults.ajax 10407 * @since 1.10.0 10408 * 10409 * @example 10410 * // Get JSON data from a file via Ajax. 10411 * // Note DataTables expects data in the form `{ data: [ ...data... ] }` by default). 10412 * $('#example').dataTable( { 10413 * "ajax": "data.json" 10414 * } ); 10415 * 10416 * @example 10417 * // Get JSON data from a file via Ajax, using `dataSrc` to change 10418 * // `data` to `tableData` (i.e. `{ tableData: [ ...data... ] }`) 10419 * $('#example').dataTable( { 10420 * "ajax": { 10421 * "url": "data.json", 10422 * "dataSrc": "tableData" 10423 * } 10424 * } ); 10425 * 10426 * @example 10427 * // Get JSON data from a file via Ajax, using `dataSrc` to read data 10428 * // from a plain array rather than an array in an object 10429 * $('#example').dataTable( { 10430 * "ajax": { 10431 * "url": "data.json", 10432 * "dataSrc": "" 10433 * } 10434 * } ); 10435 * 10436 * @example 10437 * // Manipulate the data returned from the server - add a link to data 10438 * // (note this can, should, be done using `render` for the column - this 10439 * // is just a simple example of how the data can be manipulated). 10440 * $('#example').dataTable( { 10441 * "ajax": { 10442 * "url": "data.json", 10443 * "dataSrc": function ( json ) { 10444 * for ( var i=0, ien=json.length ; i<ien ; i++ ) { 10445 * json[i][0] = '<a href="/message/'+json[i][0]+'>View message</a>'; 10446 * } 10447 * return json; 10448 * } 10449 * } 10450 * } ); 10451 * 10452 * @example 10453 * // Add data to the request 10454 * $('#example').dataTable( { 10455 * "ajax": { 10456 * "url": "data.json", 10457 * "data": function ( d ) { 10458 * return { 10459 * "extra_search": $('#extra').val() 10460 * }; 10461 * } 10462 * } 10463 * } ); 10464 * 10465 * @example 10466 * // Send request as POST 10467 * $('#example').dataTable( { 10468 * "ajax": { 10469 * "url": "data.json", 10470 * "type": "POST" 10471 * } 10472 * } ); 10473 * 10474 * @example 10475 * // Get the data from localStorage (could interface with a form for 10476 * // adding, editing and removing rows). 10477 * $('#example').dataTable( { 10478 * "ajax": function (data, callback, settings) { 10479 * callback( 10480 * JSON.parse( localStorage.getItem('dataTablesData') ) 10481 * ); 10482 * } 10483 * } ); 10484 */ 10485 "ajax": null, 10486 10487 10488 /** 10489 * This parameter allows you to readily specify the entries in the length drop 10490 * down menu that DataTables shows when pagination is enabled. It can be 10491 * either a 1D array of options which will be used for both the displayed 10492 * option and the value, or a 2D array which will use the array in the first 10493 * position as the value, and the array in the second position as the 10494 * displayed options (useful for language strings such as 'All'). 10495 * 10496 * Note that the `pageLength` property will be automatically set to the 10497 * first value given in this array, unless `pageLength` is also provided. 10498 * @type array 10499 * @default [ 10, 25, 50, 100 ] 10500 * 10501 * @dtopt Option 10502 * @name DataTable.defaults.lengthMenu 10503 * 10504 * @example 10505 * $(document).ready( function() { 10506 * $('#example').dataTable( { 10507 * "lengthMenu": [[10, 25, 50, -1], [10, 25, 50, "All"]] 10508 * } ); 10509 * } ); 10510 */ 10511 "aLengthMenu": [ 10, 25, 50, 100 ], 10512 10513 10514 /** 10515 * The `columns` option in the initialisation parameter allows you to define 10516 * details about the way individual columns behave. For a full list of 10517 * column options that can be set, please see 10518 * {@link DataTable.defaults.column}. Note that if you use `columns` to 10519 * define your columns, you must have an entry in the array for every single 10520 * column that you have in your table (these can be null if you don't which 10521 * to specify any options). 10522 * @member 10523 * 10524 * @name DataTable.defaults.column 10525 */ 10526 "aoColumns": null, 10527 10528 /** 10529 * Very similar to `columns`, `columnDefs` allows you to target a specific 10530 * column, multiple columns, or all columns, using the `targets` property of 10531 * each object in the array. This allows great flexibility when creating 10532 * tables, as the `columnDefs` arrays can be of any length, targeting the 10533 * columns you specifically want. `columnDefs` may use any of the column 10534 * options available: {@link DataTable.defaults.column}, but it _must_ 10535 * have `targets` defined in each object in the array. Values in the `targets` 10536 * array may be: 10537 * <ul> 10538 * <li>a string - class name will be matched on the TH for the column</li> 10539 * <li>0 or a positive integer - column index counting from the left</li> 10540 * <li>a negative integer - column index counting from the right</li> 10541 * <li>the string "_all" - all columns (i.e. assign a default)</li> 10542 * </ul> 10543 * @member 10544 * 10545 * @name DataTable.defaults.columnDefs 10546 */ 10547 "aoColumnDefs": null, 10548 10549 10550 /** 10551 * Basically the same as `search`, this parameter defines the individual column 10552 * filtering state at initialisation time. The array must be of the same size 10553 * as the number of columns, and each element be an object with the parameters 10554 * `search` and `escapeRegex` (the latter is optional). 'null' is also 10555 * accepted and the default will be used. 10556 * @type array 10557 * @default [] 10558 * 10559 * @dtopt Option 10560 * @name DataTable.defaults.searchCols 10561 * 10562 * @example 10563 * $(document).ready( function() { 10564 * $('#example').dataTable( { 10565 * "searchCols": [ 10566 * null, 10567 * { "search": "My filter" }, 10568 * null, 10569 * { "search": "^[0-9]", "escapeRegex": false } 10570 * ] 10571 * } ); 10572 * } ) 10573 */ 10574 "aoSearchCols": [], 10575 10576 10577 /** 10578 * An array of CSS classes that should be applied to displayed rows. This 10579 * array may be of any length, and DataTables will apply each class 10580 * sequentially, looping when required. 10581 * @type array 10582 * @default null <i>Will take the values determined by the `oClasses.stripe*` 10583 * options</i> 10584 * 10585 * @dtopt Option 10586 * @name DataTable.defaults.stripeClasses 10587 * 10588 * @example 10589 * $(document).ready( function() { 10590 * $('#example').dataTable( { 10591 * "stripeClasses": [ 'strip1', 'strip2', 'strip3' ] 10592 * } ); 10593 * } ) 10594 */ 10595 "asStripeClasses": null, 10596 10597 10598 /** 10599 * Enable or disable automatic column width calculation. This can be disabled 10600 * as an optimisation (it takes some time to calculate the widths) if the 10601 * tables widths are passed in using `columns`. 10602 * @type boolean 10603 * @default true 10604 * 10605 * @dtopt Features 10606 * @name DataTable.defaults.autoWidth 10607 * 10608 * @example 10609 * $(document).ready( function () { 10610 * $('#example').dataTable( { 10611 * "autoWidth": false 10612 * } ); 10613 * } ); 10614 */ 10615 "bAutoWidth": true, 10616 10617 10618 /** 10619 * Deferred rendering can provide DataTables with a huge speed boost when you 10620 * are using an Ajax or JS data source for the table. This option, when set to 10621 * true, will cause DataTables to defer the creation of the table elements for 10622 * each row until they are needed for a draw - saving a significant amount of 10623 * time. 10624 * @type boolean 10625 * @default false 10626 * 10627 * @dtopt Features 10628 * @name DataTable.defaults.deferRender 10629 * 10630 * @example 10631 * $(document).ready( function() { 10632 * $('#example').dataTable( { 10633 * "ajax": "sources/arrays.txt", 10634 * "deferRender": true 10635 * } ); 10636 * } ); 10637 */ 10638 "bDeferRender": false, 10639 10640 10641 /** 10642 * Replace a DataTable which matches the given selector and replace it with 10643 * one which has the properties of the new initialisation object passed. If no 10644 * table matches the selector, then the new DataTable will be constructed as 10645 * per normal. 10646 * @type boolean 10647 * @default false 10648 * 10649 * @dtopt Options 10650 * @name DataTable.defaults.destroy 10651 * 10652 * @example 10653 * $(document).ready( function() { 10654 * $('#example').dataTable( { 10655 * "srollY": "200px", 10656 * "paginate": false 10657 * } ); 10658 * 10659 * // Some time later.... 10660 * $('#example').dataTable( { 10661 * "filter": false, 10662 * "destroy": true 10663 * } ); 10664 * } ); 10665 */ 10666 "bDestroy": false, 10667 10668 10669 /** 10670 * Enable or disable filtering of data. Filtering in DataTables is "smart" in 10671 * that it allows the end user to input multiple words (space separated) and 10672 * will match a row containing those words, even if not in the order that was 10673 * specified (this allow matching across multiple columns). Note that if you 10674 * wish to use filtering in DataTables this must remain 'true' - to remove the 10675 * default filtering input box and retain filtering abilities, please use 10676 * {@link DataTable.defaults.dom}. 10677 * @type boolean 10678 * @default true 10679 * 10680 * @dtopt Features 10681 * @name DataTable.defaults.searching 10682 * 10683 * @example 10684 * $(document).ready( function () { 10685 * $('#example').dataTable( { 10686 * "searching": false 10687 * } ); 10688 * } ); 10689 */ 10690 "bFilter": true, 10691 10692 10693 /** 10694 * Enable or disable the table information display. This shows information 10695 * about the data that is currently visible on the page, including information 10696 * about filtered data if that action is being performed. 10697 * @type boolean 10698 * @default true 10699 * 10700 * @dtopt Features 10701 * @name DataTable.defaults.info 10702 * 10703 * @example 10704 * $(document).ready( function () { 10705 * $('#example').dataTable( { 10706 * "info": false 10707 * } ); 10708 * } ); 10709 */ 10710 "bInfo": true, 10711 10712 10713 /** 10714 * Allows the end user to select the size of a formatted page from a select 10715 * menu (sizes are 10, 25, 50 and 100). Requires pagination (`paginate`). 10716 * @type boolean 10717 * @default true 10718 * 10719 * @dtopt Features 10720 * @name DataTable.defaults.lengthChange 10721 * 10722 * @example 10723 * $(document).ready( function () { 10724 * $('#example').dataTable( { 10725 * "lengthChange": false 10726 * } ); 10727 * } ); 10728 */ 10729 "bLengthChange": true, 10730 10731 10732 /** 10733 * Enable or disable pagination. 10734 * @type boolean 10735 * @default true 10736 * 10737 * @dtopt Features 10738 * @name DataTable.defaults.paging 10739 * 10740 * @example 10741 * $(document).ready( function () { 10742 * $('#example').dataTable( { 10743 * "paging": false 10744 * } ); 10745 * } ); 10746 */ 10747 "bPaginate": true, 10748 10749 10750 /** 10751 * Enable or disable the display of a 'processing' indicator when the table is 10752 * being processed (e.g. a sort). This is particularly useful for tables with 10753 * large amounts of data where it can take a noticeable amount of time to sort 10754 * the entries. 10755 * @type boolean 10756 * @default false 10757 * 10758 * @dtopt Features 10759 * @name DataTable.defaults.processing 10760 * 10761 * @example 10762 * $(document).ready( function () { 10763 * $('#example').dataTable( { 10764 * "processing": true 10765 * } ); 10766 * } ); 10767 */ 10768 "bProcessing": false, 10769 10770 10771 /** 10772 * Retrieve the DataTables object for the given selector. Note that if the 10773 * table has already been initialised, this parameter will cause DataTables 10774 * to simply return the object that has already been set up - it will not take 10775 * account of any changes you might have made to the initialisation object 10776 * passed to DataTables (setting this parameter to true is an acknowledgement 10777 * that you understand this). `destroy` can be used to reinitialise a table if 10778 * you need. 10779 * @type boolean 10780 * @default false 10781 * 10782 * @dtopt Options 10783 * @name DataTable.defaults.retrieve 10784 * 10785 * @example 10786 * $(document).ready( function() { 10787 * initTable(); 10788 * tableActions(); 10789 * } ); 10790 * 10791 * function initTable () 10792 * { 10793 * return $('#example').dataTable( { 10794 * "scrollY": "200px", 10795 * "paginate": false, 10796 * "retrieve": true 10797 * } ); 10798 * } 10799 * 10800 * function tableActions () 10801 * { 10802 * var table = initTable(); 10803 * // perform API operations with oTable 10804 * } 10805 */ 10806 "bRetrieve": false, 10807 10808 10809 /** 10810 * When vertical (y) scrolling is enabled, DataTables will force the height of 10811 * the table's viewport to the given height at all times (useful for layout). 10812 * However, this can look odd when filtering data down to a small data set, 10813 * and the footer is left "floating" further down. This parameter (when 10814 * enabled) will cause DataTables to collapse the table's viewport down when 10815 * the result set will fit within the given Y height. 10816 * @type boolean 10817 * @default false 10818 * 10819 * @dtopt Options 10820 * @name DataTable.defaults.scrollCollapse 10821 * 10822 * @example 10823 * $(document).ready( function() { 10824 * $('#example').dataTable( { 10825 * "scrollY": "200", 10826 * "scrollCollapse": true 10827 * } ); 10828 * } ); 10829 */ 10830 "bScrollCollapse": false, 10831 10832 10833 /** 10834 * Configure DataTables to use server-side processing. Note that the 10835 * `ajax` parameter must also be given in order to give DataTables a 10836 * source to obtain the required data for each draw. 10837 * @type boolean 10838 * @default false 10839 * 10840 * @dtopt Features 10841 * @dtopt Server-side 10842 * @name DataTable.defaults.serverSide 10843 * 10844 * @example 10845 * $(document).ready( function () { 10846 * $('#example').dataTable( { 10847 * "serverSide": true, 10848 * "ajax": "xhr.php" 10849 * } ); 10850 * } ); 10851 */ 10852 "bServerSide": false, 10853 10854 10855 /** 10856 * Enable or disable sorting of columns. Sorting of individual columns can be 10857 * disabled by the `sortable` option for each column. 10858 * @type boolean 10859 * @default true 10860 * 10861 * @dtopt Features 10862 * @name DataTable.defaults.ordering 10863 * 10864 * @example 10865 * $(document).ready( function () { 10866 * $('#example').dataTable( { 10867 * "ordering": false 10868 * } ); 10869 * } ); 10870 */ 10871 "bSort": true, 10872 10873 10874 /** 10875 * Enable or display DataTables' ability to sort multiple columns at the 10876 * same time (activated by shift-click by the user). 10877 * @type boolean 10878 * @default true 10879 * 10880 * @dtopt Options 10881 * @name DataTable.defaults.orderMulti 10882 * 10883 * @example 10884 * // Disable multiple column sorting ability 10885 * $(document).ready( function () { 10886 * $('#example').dataTable( { 10887 * "orderMulti": false 10888 * } ); 10889 * } ); 10890 */ 10891 "bSortMulti": true, 10892 10893 10894 /** 10895 * Allows control over whether DataTables should use the top (true) unique 10896 * cell that is found for a single column, or the bottom (false - default). 10897 * This is useful when using complex headers. 10898 * @type boolean 10899 * @default false 10900 * 10901 * @dtopt Options 10902 * @name DataTable.defaults.orderCellsTop 10903 * 10904 * @example 10905 * $(document).ready( function() { 10906 * $('#example').dataTable( { 10907 * "orderCellsTop": true 10908 * } ); 10909 * } ); 10910 */ 10911 "bSortCellsTop": false, 10912 10913 10914 /** 10915 * Enable or disable the addition of the classes `sorting\_1`, `sorting\_2` and 10916 * `sorting\_3` to the columns which are currently being sorted on. This is 10917 * presented as a feature switch as it can increase processing time (while 10918 * classes are removed and added) so for large data sets you might want to 10919 * turn this off. 10920 * @type boolean 10921 * @default true 10922 * 10923 * @dtopt Features 10924 * @name DataTable.defaults.orderClasses 10925 * 10926 * @example 10927 * $(document).ready( function () { 10928 * $('#example').dataTable( { 10929 * "orderClasses": false 10930 * } ); 10931 * } ); 10932 */ 10933 "bSortClasses": true, 10934 10935 10936 /** 10937 * Enable or disable state saving. When enabled HTML5 `localStorage` will be 10938 * used to save table display information such as pagination information, 10939 * display length, filtering and sorting. As such when the end user reloads 10940 * the page the display display will match what thy had previously set up. 10941 * 10942 * Due to the use of `localStorage` the default state saving is not supported 10943 * in IE6 or 7. If state saving is required in those browsers, use 10944 * `stateSaveCallback` to provide a storage solution such as cookies. 10945 * @type boolean 10946 * @default false 10947 * 10948 * @dtopt Features 10949 * @name DataTable.defaults.stateSave 10950 * 10951 * @example 10952 * $(document).ready( function () { 10953 * $('#example').dataTable( { 10954 * "stateSave": true 10955 * } ); 10956 * } ); 10957 */ 10958 "bStateSave": false, 10959 10960 10961 /** 10962 * This function is called when a TR element is created (and all TD child 10963 * elements have been inserted), or registered if using a DOM source, allowing 10964 * manipulation of the TR element (adding classes etc). 10965 * @type function 10966 * @param {node} row "TR" element for the current row 10967 * @param {array} data Raw data array for this row 10968 * @param {int} dataIndex The index of this row in the internal aoData array 10969 * 10970 * @dtopt Callbacks 10971 * @name DataTable.defaults.createdRow 10972 * 10973 * @example 10974 * $(document).ready( function() { 10975 * $('#example').dataTable( { 10976 * "createdRow": function( row, data, dataIndex ) { 10977 * // Bold the grade for all 'A' grade browsers 10978 * if ( data[4] == "A" ) 10979 * { 10980 * $('td:eq(4)', row).html( '<b>A</b>' ); 10981 * } 10982 * } 10983 * } ); 10984 * } ); 10985 */ 10986 "fnCreatedRow": null, 10987 10988 10989 /** 10990 * This function is called on every 'draw' event, and allows you to 10991 * dynamically modify any aspect you want about the created DOM. 10992 * @type function 10993 * @param {object} settings DataTables settings object 10994 * 10995 * @dtopt Callbacks 10996 * @name DataTable.defaults.drawCallback 10997 * 10998 * @example 10999 * $(document).ready( function() { 11000 * $('#example').dataTable( { 11001 * "drawCallback": function( settings ) { 11002 * alert( 'DataTables has redrawn the table' ); 11003 * } 11004 * } ); 11005 * } ); 11006 */ 11007 "fnDrawCallback": null, 11008 11009 11010 /** 11011 * Identical to fnHeaderCallback() but for the table footer this function 11012 * allows you to modify the table footer on every 'draw' event. 11013 * @type function 11014 * @param {node} foot "TR" element for the footer 11015 * @param {array} data Full table data (as derived from the original HTML) 11016 * @param {int} start Index for the current display starting point in the 11017 * display array 11018 * @param {int} end Index for the current display ending point in the 11019 * display array 11020 * @param {array int} display Index array to translate the visual position 11021 * to the full data array 11022 * 11023 * @dtopt Callbacks 11024 * @name DataTable.defaults.footerCallback 11025 * 11026 * @example 11027 * $(document).ready( function() { 11028 * $('#example').dataTable( { 11029 * "footerCallback": function( tfoot, data, start, end, display ) { 11030 * tfoot.getElementsByTagName('th')[0].innerHTML = "Starting index is "+start; 11031 * } 11032 * } ); 11033 * } ) 11034 */ 11035 "fnFooterCallback": null, 11036 11037 11038 /** 11039 * When rendering large numbers in the information element for the table 11040 * (i.e. "Showing 1 to 10 of 57 entries") DataTables will render large numbers 11041 * to have a comma separator for the 'thousands' units (e.g. 1 million is 11042 * rendered as "1,000,000") to help readability for the end user. This 11043 * function will override the default method DataTables uses. 11044 * @type function 11045 * @member 11046 * @param {int} toFormat number to be formatted 11047 * @returns {string} formatted string for DataTables to show the number 11048 * 11049 * @dtopt Callbacks 11050 * @name DataTable.defaults.formatNumber 11051 * 11052 * @example 11053 * // Format a number using a single quote for the separator (note that 11054 * // this can also be done with the language.thousands option) 11055 * $(document).ready( function() { 11056 * $('#example').dataTable( { 11057 * "formatNumber": function ( toFormat ) { 11058 * return toFormat.toString().replace( 11059 * /\B(?=(\d{3})+(?!\d))/g, "'" 11060 * ); 11061 * }; 11062 * } ); 11063 * } ); 11064 */ 11065 "fnFormatNumber": function ( toFormat ) { 11066 return toFormat.toString().replace( 11067 /\B(?=(\d{3})+(?!\d))/g, 11068 this.oLanguage.sThousands 11069 ); 11070 }, 11071 11072 11073 /** 11074 * This function is called on every 'draw' event, and allows you to 11075 * dynamically modify the header row. This can be used to calculate and 11076 * display useful information about the table. 11077 * @type function 11078 * @param {node} head "TR" element for the header 11079 * @param {array} data Full table data (as derived from the original HTML) 11080 * @param {int} start Index for the current display starting point in the 11081 * display array 11082 * @param {int} end Index for the current display ending point in the 11083 * display array 11084 * @param {array int} display Index array to translate the visual position 11085 * to the full data array 11086 * 11087 * @dtopt Callbacks 11088 * @name DataTable.defaults.headerCallback 11089 * 11090 * @example 11091 * $(document).ready( function() { 11092 * $('#example').dataTable( { 11093 * "fheaderCallback": function( head, data, start, end, display ) { 11094 * head.getElementsByTagName('th')[0].innerHTML = "Displaying "+(end-start)+" records"; 11095 * } 11096 * } ); 11097 * } ) 11098 */ 11099 "fnHeaderCallback": null, 11100 11101 11102 /** 11103 * The information element can be used to convey information about the current 11104 * state of the table. Although the internationalisation options presented by 11105 * DataTables are quite capable of dealing with most customisations, there may 11106 * be times where you wish to customise the string further. This callback 11107 * allows you to do exactly that. 11108 * @type function 11109 * @param {object} oSettings DataTables settings object 11110 * @param {int} start Starting position in data for the draw 11111 * @param {int} end End position in data for the draw 11112 * @param {int} max Total number of rows in the table (regardless of 11113 * filtering) 11114 * @param {int} total Total number of rows in the data set, after filtering 11115 * @param {string} pre The string that DataTables has formatted using it's 11116 * own rules 11117 * @returns {string} The string to be displayed in the information element. 11118 * 11119 * @dtopt Callbacks 11120 * @name DataTable.defaults.infoCallback 11121 * 11122 * @example 11123 * $('#example').dataTable( { 11124 * "infoCallback": function( settings, start, end, max, total, pre ) { 11125 * return start +" to "+ end; 11126 * } 11127 * } ); 11128 */ 11129 "fnInfoCallback": null, 11130 11131 11132 /** 11133 * Called when the table has been initialised. Normally DataTables will 11134 * initialise sequentially and there will be no need for this function, 11135 * however, this does not hold true when using external language information 11136 * since that is obtained using an async XHR call. 11137 * @type function 11138 * @param {object} settings DataTables settings object 11139 * @param {object} json The JSON object request from the server - only 11140 * present if client-side Ajax sourced data is used 11141 * 11142 * @dtopt Callbacks 11143 * @name DataTable.defaults.initComplete 11144 * 11145 * @example 11146 * $(document).ready( function() { 11147 * $('#example').dataTable( { 11148 * "initComplete": function(settings, json) { 11149 * alert( 'DataTables has finished its initialisation.' ); 11150 * } 11151 * } ); 11152 * } ) 11153 */ 11154 "fnInitComplete": null, 11155 11156 11157 /** 11158 * Called at the very start of each table draw and can be used to cancel the 11159 * draw by returning false, any other return (including undefined) results in 11160 * the full draw occurring). 11161 * @type function 11162 * @param {object} settings DataTables settings object 11163 * @returns {boolean} False will cancel the draw, anything else (including no 11164 * return) will allow it to complete. 11165 * 11166 * @dtopt Callbacks 11167 * @name DataTable.defaults.preDrawCallback 11168 * 11169 * @example 11170 * $(document).ready( function() { 11171 * $('#example').dataTable( { 11172 * "preDrawCallback": function( settings ) { 11173 * if ( $('#test').val() == 1 ) { 11174 * return false; 11175 * } 11176 * } 11177 * } ); 11178 * } ); 11179 */ 11180 "fnPreDrawCallback": null, 11181 11182 11183 /** 11184 * This function allows you to 'post process' each row after it have been 11185 * generated for each table draw, but before it is rendered on screen. This 11186 * function might be used for setting the row class name etc. 11187 * @type function 11188 * @param {node} row "TR" element for the current row 11189 * @param {array} data Raw data array for this row 11190 * @param {int} displayIndex The display index for the current table draw 11191 * @param {int} displayIndexFull The index of the data in the full list of 11192 * rows (after filtering) 11193 * 11194 * @dtopt Callbacks 11195 * @name DataTable.defaults.rowCallback 11196 * 11197 * @example 11198 * $(document).ready( function() { 11199 * $('#example').dataTable( { 11200 * "rowCallback": function( row, data, displayIndex, displayIndexFull ) { 11201 * // Bold the grade for all 'A' grade browsers 11202 * if ( data[4] == "A" ) { 11203 * $('td:eq(4)', row).html( '<b>A</b>' ); 11204 * } 11205 * } 11206 * } ); 11207 * } ); 11208 */ 11209 "fnRowCallback": null, 11210 11211 11212 /** 11213 * __Deprecated__ The functionality provided by this parameter has now been 11214 * superseded by that provided through `ajax`, which should be used instead. 11215 * 11216 * This parameter allows you to override the default function which obtains 11217 * the data from the server so something more suitable for your application. 11218 * For example you could use POST data, or pull information from a Gears or 11219 * AIR database. 11220 * @type function 11221 * @member 11222 * @param {string} source HTTP source to obtain the data from (`ajax`) 11223 * @param {array} data A key/value pair object containing the data to send 11224 * to the server 11225 * @param {function} callback to be called on completion of the data get 11226 * process that will draw the data on the page. 11227 * @param {object} settings DataTables settings object 11228 * 11229 * @dtopt Callbacks 11230 * @dtopt Server-side 11231 * @name DataTable.defaults.serverData 11232 * 11233 * @deprecated 1.10. Please use `ajax` for this functionality now. 11234 */ 11235 "fnServerData": null, 11236 11237 11238 /** 11239 * __Deprecated__ The functionality provided by this parameter has now been 11240 * superseded by that provided through `ajax`, which should be used instead. 11241 * 11242 * It is often useful to send extra data to the server when making an Ajax 11243 * request - for example custom filtering information, and this callback 11244 * function makes it trivial to send extra information to the server. The 11245 * passed in parameter is the data set that has been constructed by 11246 * DataTables, and you can add to this or modify it as you require. 11247 * @type function 11248 * @param {array} data Data array (array of objects which are name/value 11249 * pairs) that has been constructed by DataTables and will be sent to the 11250 * server. In the case of Ajax sourced data with server-side processing 11251 * this will be an empty array, for server-side processing there will be a 11252 * significant number of parameters! 11253 * @returns {undefined} Ensure that you modify the data array passed in, 11254 * as this is passed by reference. 11255 * 11256 * @dtopt Callbacks 11257 * @dtopt Server-side 11258 * @name DataTable.defaults.serverParams 11259 * 11260 * @deprecated 1.10. Please use `ajax` for this functionality now. 11261 */ 11262 "fnServerParams": null, 11263 11264 11265 /** 11266 * Load the table state. With this function you can define from where, and how, the 11267 * state of a table is loaded. By default DataTables will load from `localStorage` 11268 * but you might wish to use a server-side database or cookies. 11269 * @type function 11270 * @member 11271 * @param {object} settings DataTables settings object 11272 * @param {object} callback Callback that can be executed when done. It 11273 * should be passed the loaded state object. 11274 * @return {object} The DataTables state object to be loaded 11275 * 11276 * @dtopt Callbacks 11277 * @name DataTable.defaults.stateLoadCallback 11278 * 11279 * @example 11280 * $(document).ready( function() { 11281 * $('#example').dataTable( { 11282 * "stateSave": true, 11283 * "stateLoadCallback": function (settings, callback) { 11284 * $.ajax( { 11285 * "url": "/state_load", 11286 * "dataType": "json", 11287 * "success": function (json) { 11288 * callback( json ); 11289 * } 11290 * } ); 11291 * } 11292 * } ); 11293 * } ); 11294 */ 11295 "fnStateLoadCallback": function ( settings ) { 11296 try { 11297 return JSON.parse( 11298 (settings.iStateDuration === -1 ? sessionStorage : localStorage).getItem( 11299 'DataTables_'+settings.sInstance+'_'+location.pathname 11300 ) 11301 ); 11302 } catch (e) { 11303 return {}; 11304 } 11305 }, 11306 11307 11308 /** 11309 * Callback which allows modification of the saved state prior to loading that state. 11310 * This callback is called when the table is loading state from the stored data, but 11311 * prior to the settings object being modified by the saved state. Note that for 11312 * plug-in authors, you should use the `stateLoadParams` event to load parameters for 11313 * a plug-in. 11314 * @type function 11315 * @param {object} settings DataTables settings object 11316 * @param {object} data The state object that is to be loaded 11317 * 11318 * @dtopt Callbacks 11319 * @name DataTable.defaults.stateLoadParams 11320 * 11321 * @example 11322 * // Remove a saved filter, so filtering is never loaded 11323 * $(document).ready( function() { 11324 * $('#example').dataTable( { 11325 * "stateSave": true, 11326 * "stateLoadParams": function (settings, data) { 11327 * data.oSearch.sSearch = ""; 11328 * } 11329 * } ); 11330 * } ); 11331 * 11332 * @example 11333 * // Disallow state loading by returning false 11334 * $(document).ready( function() { 11335 * $('#example').dataTable( { 11336 * "stateSave": true, 11337 * "stateLoadParams": function (settings, data) { 11338 * return false; 11339 * } 11340 * } ); 11341 * } ); 11342 */ 11343 "fnStateLoadParams": null, 11344 11345 11346 /** 11347 * Callback that is called when the state has been loaded from the state saving method 11348 * and the DataTables settings object has been modified as a result of the loaded state. 11349 * @type function 11350 * @param {object} settings DataTables settings object 11351 * @param {object} data The state object that was loaded 11352 * 11353 * @dtopt Callbacks 11354 * @name DataTable.defaults.stateLoaded 11355 * 11356 * @example 11357 * // Show an alert with the filtering value that was saved 11358 * $(document).ready( function() { 11359 * $('#example').dataTable( { 11360 * "stateSave": true, 11361 * "stateLoaded": function (settings, data) { 11362 * alert( 'Saved filter was: '+data.oSearch.sSearch ); 11363 * } 11364 * } ); 11365 * } ); 11366 */ 11367 "fnStateLoaded": null, 11368 11369 11370 /** 11371 * Save the table state. This function allows you to define where and how the state 11372 * information for the table is stored By default DataTables will use `localStorage` 11373 * but you might wish to use a server-side database or cookies. 11374 * @type function 11375 * @member 11376 * @param {object} settings DataTables settings object 11377 * @param {object} data The state object to be saved 11378 * 11379 * @dtopt Callbacks 11380 * @name DataTable.defaults.stateSaveCallback 11381 * 11382 * @example 11383 * $(document).ready( function() { 11384 * $('#example').dataTable( { 11385 * "stateSave": true, 11386 * "stateSaveCallback": function (settings, data) { 11387 * // Send an Ajax request to the server with the state object 11388 * $.ajax( { 11389 * "url": "/state_save", 11390 * "data": data, 11391 * "dataType": "json", 11392 * "method": "POST" 11393 * "success": function () {} 11394 * } ); 11395 * } 11396 * } ); 11397 * } ); 11398 */ 11399 "fnStateSaveCallback": function ( settings, data ) { 11400 try { 11401 (settings.iStateDuration === -1 ? sessionStorage : localStorage).setItem( 11402 'DataTables_'+settings.sInstance+'_'+location.pathname, 11403 JSON.stringify( data ) 11404 ); 11405 } catch (e) {} 11406 }, 11407 11408 11409 /** 11410 * Callback which allows modification of the state to be saved. Called when the table 11411 * has changed state a new state save is required. This method allows modification of 11412 * the state saving object prior to actually doing the save, including addition or 11413 * other state properties or modification. Note that for plug-in authors, you should 11414 * use the `stateSaveParams` event to save parameters for a plug-in. 11415 * @type function 11416 * @param {object} settings DataTables settings object 11417 * @param {object} data The state object to be saved 11418 * 11419 * @dtopt Callbacks 11420 * @name DataTable.defaults.stateSaveParams 11421 * 11422 * @example 11423 * // Remove a saved filter, so filtering is never saved 11424 * $(document).ready( function() { 11425 * $('#example').dataTable( { 11426 * "stateSave": true, 11427 * "stateSaveParams": function (settings, data) { 11428 * data.oSearch.sSearch = ""; 11429 * } 11430 * } ); 11431 * } ); 11432 */ 11433 "fnStateSaveParams": null, 11434 11435 11436 /** 11437 * Duration for which the saved state information is considered valid. After this period 11438 * has elapsed the state will be returned to the default. 11439 * Value is given in seconds. 11440 * @type int 11441 * @default 7200 <i>(2 hours)</i> 11442 * 11443 * @dtopt Options 11444 * @name DataTable.defaults.stateDuration 11445 * 11446 * @example 11447 * $(document).ready( function() { 11448 * $('#example').dataTable( { 11449 * "stateDuration": 60*60*24; // 1 day 11450 * } ); 11451 * } ) 11452 */ 11453 "iStateDuration": 7200, 11454 11455 11456 /** 11457 * When enabled DataTables will not make a request to the server for the first 11458 * page draw - rather it will use the data already on the page (no sorting etc 11459 * will be applied to it), thus saving on an XHR at load time. `deferLoading` 11460 * is used to indicate that deferred loading is required, but it is also used 11461 * to tell DataTables how many records there are in the full table (allowing 11462 * the information element and pagination to be displayed correctly). In the case 11463 * where a filtering is applied to the table on initial load, this can be 11464 * indicated by giving the parameter as an array, where the first element is 11465 * the number of records available after filtering and the second element is the 11466 * number of records without filtering (allowing the table information element 11467 * to be shown correctly). 11468 * @type int | array 11469 * @default null 11470 * 11471 * @dtopt Options 11472 * @name DataTable.defaults.deferLoading 11473 * 11474 * @example 11475 * // 57 records available in the table, no filtering applied 11476 * $(document).ready( function() { 11477 * $('#example').dataTable( { 11478 * "serverSide": true, 11479 * "ajax": "scripts/server_processing.php", 11480 * "deferLoading": 57 11481 * } ); 11482 * } ); 11483 * 11484 * @example 11485 * // 57 records after filtering, 100 without filtering (an initial filter applied) 11486 * $(document).ready( function() { 11487 * $('#example').dataTable( { 11488 * "serverSide": true, 11489 * "ajax": "scripts/server_processing.php", 11490 * "deferLoading": [ 57, 100 ], 11491 * "search": { 11492 * "search": "my_filter" 11493 * } 11494 * } ); 11495 * } ); 11496 */ 11497 "iDeferLoading": null, 11498 11499 11500 /** 11501 * Number of rows to display on a single page when using pagination. If 11502 * feature enabled (`lengthChange`) then the end user will be able to override 11503 * this to a custom setting using a pop-up menu. 11504 * @type int 11505 * @default 10 11506 * 11507 * @dtopt Options 11508 * @name DataTable.defaults.pageLength 11509 * 11510 * @example 11511 * $(document).ready( function() { 11512 * $('#example').dataTable( { 11513 * "pageLength": 50 11514 * } ); 11515 * } ) 11516 */ 11517 "iDisplayLength": 10, 11518 11519 11520 /** 11521 * Define the starting point for data display when using DataTables with 11522 * pagination. Note that this parameter is the number of records, rather than 11523 * the page number, so if you have 10 records per page and want to start on 11524 * the third page, it should be "20". 11525 * @type int 11526 * @default 0 11527 * 11528 * @dtopt Options 11529 * @name DataTable.defaults.displayStart 11530 * 11531 * @example 11532 * $(document).ready( function() { 11533 * $('#example').dataTable( { 11534 * "displayStart": 20 11535 * } ); 11536 * } ) 11537 */ 11538 "iDisplayStart": 0, 11539 11540 11541 /** 11542 * By default DataTables allows keyboard navigation of the table (sorting, paging, 11543 * and filtering) by adding a `tabindex` attribute to the required elements. This 11544 * allows you to tab through the controls and press the enter key to activate them. 11545 * The tabindex is default 0, meaning that the tab follows the flow of the document. 11546 * You can overrule this using this parameter if you wish. Use a value of -1 to 11547 * disable built-in keyboard navigation. 11548 * @type int 11549 * @default 0 11550 * 11551 * @dtopt Options 11552 * @name DataTable.defaults.tabIndex 11553 * 11554 * @example 11555 * $(document).ready( function() { 11556 * $('#example').dataTable( { 11557 * "tabIndex": 1 11558 * } ); 11559 * } ); 11560 */ 11561 "iTabIndex": 0, 11562 11563 11564 /** 11565 * Classes that DataTables assigns to the various components and features 11566 * that it adds to the HTML table. This allows classes to be configured 11567 * during initialisation in addition to through the static 11568 * {@link DataTable.ext.oStdClasses} object). 11569 * @namespace 11570 * @name DataTable.defaults.classes 11571 */ 11572 "oClasses": {}, 11573 11574 11575 /** 11576 * All strings that DataTables uses in the user interface that it creates 11577 * are defined in this object, allowing you to modified them individually or 11578 * completely replace them all as required. 11579 * @namespace 11580 * @name DataTable.defaults.language 11581 */ 11582 "oLanguage": { 11583 /** 11584 * Strings that are used for WAI-ARIA labels and controls only (these are not 11585 * actually visible on the page, but will be read by screenreaders, and thus 11586 * must be internationalised as well). 11587 * @namespace 11588 * @name DataTable.defaults.language.aria 11589 */ 11590 "oAria": { 11591 /** 11592 * ARIA label that is added to the table headers when the column may be 11593 * sorted ascending by activing the column (click or return when focused). 11594 * Note that the column header is prefixed to this string. 11595 * @type string 11596 * @default : activate to sort column ascending 11597 * 11598 * @dtopt Language 11599 * @name DataTable.defaults.language.aria.sortAscending 11600 * 11601 * @example 11602 * $(document).ready( function() { 11603 * $('#example').dataTable( { 11604 * "language": { 11605 * "aria": { 11606 * "sortAscending": " - click/return to sort ascending" 11607 * } 11608 * } 11609 * } ); 11610 * } ); 11611 */ 11612 "sSortAscending": ": activate to sort column ascending", 11613 11614 /** 11615 * ARIA label that is added to the table headers when the column may be 11616 * sorted descending by activing the column (click or return when focused). 11617 * Note that the column header is prefixed to this string. 11618 * @type string 11619 * @default : activate to sort column ascending 11620 * 11621 * @dtopt Language 11622 * @name DataTable.defaults.language.aria.sortDescending 11623 * 11624 * @example 11625 * $(document).ready( function() { 11626 * $('#example').dataTable( { 11627 * "language": { 11628 * "aria": { 11629 * "sortDescending": " - click/return to sort descending" 11630 * } 11631 * } 11632 * } ); 11633 * } ); 11634 */ 11635 "sSortDescending": ": activate to sort column descending" 11636 }, 11637 11638 /** 11639 * Pagination string used by DataTables for the built-in pagination 11640 * control types. 11641 * @namespace 11642 * @name DataTable.defaults.language.paginate 11643 */ 11644 "oPaginate": { 11645 /** 11646 * Text to use when using the 'full_numbers' type of pagination for the 11647 * button to take the user to the first page. 11648 * @type string 11649 * @default First 11650 * 11651 * @dtopt Language 11652 * @name DataTable.defaults.language.paginate.first 11653 * 11654 * @example 11655 * $(document).ready( function() { 11656 * $('#example').dataTable( { 11657 * "language": { 11658 * "paginate": { 11659 * "first": "First page" 11660 * } 11661 * } 11662 * } ); 11663 * } ); 11664 */ 11665 "sFirst": "First", 11666 11667 11668 /** 11669 * Text to use when using the 'full_numbers' type of pagination for the 11670 * button to take the user to the last page. 11671 * @type string 11672 * @default Last 11673 * 11674 * @dtopt Language 11675 * @name DataTable.defaults.language.paginate.last 11676 * 11677 * @example 11678 * $(document).ready( function() { 11679 * $('#example').dataTable( { 11680 * "language": { 11681 * "paginate": { 11682 * "last": "Last page" 11683 * } 11684 * } 11685 * } ); 11686 * } ); 11687 */ 11688 "sLast": "Last", 11689 11690 11691 /** 11692 * Text to use for the 'next' pagination button (to take the user to the 11693 * next page). 11694 * @type string 11695 * @default Next 11696 * 11697 * @dtopt Language 11698 * @name DataTable.defaults.language.paginate.next 11699 * 11700 * @example 11701 * $(document).ready( function() { 11702 * $('#example').dataTable( { 11703 * "language": { 11704 * "paginate": { 11705 * "next": "Next page" 11706 * } 11707 * } 11708 * } ); 11709 * } ); 11710 */ 11711 "sNext": "Next", 11712 11713 11714 /** 11715 * Text to use for the 'previous' pagination button (to take the user to 11716 * the previous page). 11717 * @type string 11718 * @default Previous 11719 * 11720 * @dtopt Language 11721 * @name DataTable.defaults.language.paginate.previous 11722 * 11723 * @example 11724 * $(document).ready( function() { 11725 * $('#example').dataTable( { 11726 * "language": { 11727 * "paginate": { 11728 * "previous": "Previous page" 11729 * } 11730 * } 11731 * } ); 11732 * } ); 11733 */ 11734 "sPrevious": "Previous" 11735 }, 11736 11737 /** 11738 * This string is shown in preference to `zeroRecords` when the table is 11739 * empty of data (regardless of filtering). Note that this is an optional 11740 * parameter - if it is not given, the value of `zeroRecords` will be used 11741 * instead (either the default or given value). 11742 * @type string 11743 * @default No data available in table 11744 * 11745 * @dtopt Language 11746 * @name DataTable.defaults.language.emptyTable 11747 * 11748 * @example 11749 * $(document).ready( function() { 11750 * $('#example').dataTable( { 11751 * "language": { 11752 * "emptyTable": "No data available in table" 11753 * } 11754 * } ); 11755 * } ); 11756 */ 11757 "sEmptyTable": "No data available in table", 11758 11759 11760 /** 11761 * This string gives information to the end user about the information 11762 * that is current on display on the page. The following tokens can be 11763 * used in the string and will be dynamically replaced as the table 11764 * display updates. This tokens can be placed anywhere in the string, or 11765 * removed as needed by the language requires: 11766 * 11767 * * `\_START\_` - Display index of the first record on the current page 11768 * * `\_END\_` - Display index of the last record on the current page 11769 * * `\_TOTAL\_` - Number of records in the table after filtering 11770 * * `\_MAX\_` - Number of records in the table without filtering 11771 * * `\_PAGE\_` - Current page number 11772 * * `\_PAGES\_` - Total number of pages of data in the table 11773 * 11774 * @type string 11775 * @default Showing _START_ to _END_ of _TOTAL_ entries 11776 * 11777 * @dtopt Language 11778 * @name DataTable.defaults.language.info 11779 * 11780 * @example 11781 * $(document).ready( function() { 11782 * $('#example').dataTable( { 11783 * "language": { 11784 * "info": "Showing page _PAGE_ of _PAGES_" 11785 * } 11786 * } ); 11787 * } ); 11788 */ 11789 "sInfo": "Showing _START_ to _END_ of _TOTAL_ entries", 11790 11791 11792 /** 11793 * Display information string for when the table is empty. Typically the 11794 * format of this string should match `info`. 11795 * @type string 11796 * @default Showing 0 to 0 of 0 entries 11797 * 11798 * @dtopt Language 11799 * @name DataTable.defaults.language.infoEmpty 11800 * 11801 * @example 11802 * $(document).ready( function() { 11803 * $('#example').dataTable( { 11804 * "language": { 11805 * "infoEmpty": "No entries to show" 11806 * } 11807 * } ); 11808 * } ); 11809 */ 11810 "sInfoEmpty": "Showing 0 to 0 of 0 entries", 11811 11812 11813 /** 11814 * When a user filters the information in a table, this string is appended 11815 * to the information (`info`) to give an idea of how strong the filtering 11816 * is. The variable _MAX_ is dynamically updated. 11817 * @type string 11818 * @default (filtered from _MAX_ total entries) 11819 * 11820 * @dtopt Language 11821 * @name DataTable.defaults.language.infoFiltered 11822 * 11823 * @example 11824 * $(document).ready( function() { 11825 * $('#example').dataTable( { 11826 * "language": { 11827 * "infoFiltered": " - filtering from _MAX_ records" 11828 * } 11829 * } ); 11830 * } ); 11831 */ 11832 "sInfoFiltered": "(filtered from _MAX_ total entries)", 11833 11834 11835 /** 11836 * If can be useful to append extra information to the info string at times, 11837 * and this variable does exactly that. This information will be appended to 11838 * the `info` (`infoEmpty` and `infoFiltered` in whatever combination they are 11839 * being used) at all times. 11840 * @type string 11841 * @default <i>Empty string</i> 11842 * 11843 * @dtopt Language 11844 * @name DataTable.defaults.language.infoPostFix 11845 * 11846 * @example 11847 * $(document).ready( function() { 11848 * $('#example').dataTable( { 11849 * "language": { 11850 * "infoPostFix": "All records shown are derived from real information." 11851 * } 11852 * } ); 11853 * } ); 11854 */ 11855 "sInfoPostFix": "", 11856 11857 11858 /** 11859 * This decimal place operator is a little different from the other 11860 * language options since DataTables doesn't output floating point 11861 * numbers, so it won't ever use this for display of a number. Rather, 11862 * what this parameter does is modify the sort methods of the table so 11863 * that numbers which are in a format which has a character other than 11864 * a period (`.`) as a decimal place will be sorted numerically. 11865 * 11866 * Note that numbers with different decimal places cannot be shown in 11867 * the same table and still be sortable, the table must be consistent. 11868 * However, multiple different tables on the page can use different 11869 * decimal place characters. 11870 * @type string 11871 * @default 11872 * 11873 * @dtopt Language 11874 * @name DataTable.defaults.language.decimal 11875 * 11876 * @example 11877 * $(document).ready( function() { 11878 * $('#example').dataTable( { 11879 * "language": { 11880 * "decimal": "," 11881 * "thousands": "." 11882 * } 11883 * } ); 11884 * } ); 11885 */ 11886 "sDecimal": "", 11887 11888 11889 /** 11890 * DataTables has a build in number formatter (`formatNumber`) which is 11891 * used to format large numbers that are used in the table information. 11892 * By default a comma is used, but this can be trivially changed to any 11893 * character you wish with this parameter. 11894 * @type string 11895 * @default , 11896 * 11897 * @dtopt Language 11898 * @name DataTable.defaults.language.thousands 11899 * 11900 * @example 11901 * $(document).ready( function() { 11902 * $('#example').dataTable( { 11903 * "language": { 11904 * "thousands": "'" 11905 * } 11906 * } ); 11907 * } ); 11908 */ 11909 "sThousands": ",", 11910 11911 11912 /** 11913 * Detail the action that will be taken when the drop down menu for the 11914 * pagination length option is changed. The '_MENU_' variable is replaced 11915 * with a default select list of 10, 25, 50 and 100, and can be replaced 11916 * with a custom select box if required. 11917 * @type string 11918 * @default Show _MENU_ entries 11919 * 11920 * @dtopt Language 11921 * @name DataTable.defaults.language.lengthMenu 11922 * 11923 * @example 11924 * // Language change only 11925 * $(document).ready( function() { 11926 * $('#example').dataTable( { 11927 * "language": { 11928 * "lengthMenu": "Display _MENU_ records" 11929 * } 11930 * } ); 11931 * } ); 11932 * 11933 * @example 11934 * // Language and options change 11935 * $(document).ready( function() { 11936 * $('#example').dataTable( { 11937 * "language": { 11938 * "lengthMenu": 'Display <select>'+ 11939 * '<option value="10">10</option>'+ 11940 * '<option value="20">20</option>'+ 11941 * '<option value="30">30</option>'+ 11942 * '<option value="40">40</option>'+ 11943 * '<option value="50">50</option>'+ 11944 * '<option value="-1">All</option>'+ 11945 * '</select> records' 11946 * } 11947 * } ); 11948 * } ); 11949 */ 11950 "sLengthMenu": "Show _MENU_ entries", 11951 11952 11953 /** 11954 * When using Ajax sourced data and during the first draw when DataTables is 11955 * gathering the data, this message is shown in an empty row in the table to 11956 * indicate to the end user the the data is being loaded. Note that this 11957 * parameter is not used when loading data by server-side processing, just 11958 * Ajax sourced data with client-side processing. 11959 * @type string 11960 * @default Loading... 11961 * 11962 * @dtopt Language 11963 * @name DataTable.defaults.language.loadingRecords 11964 * 11965 * @example 11966 * $(document).ready( function() { 11967 * $('#example').dataTable( { 11968 * "language": { 11969 * "loadingRecords": "Please wait - loading..." 11970 * } 11971 * } ); 11972 * } ); 11973 */ 11974 "sLoadingRecords": "Loading...", 11975 11976 11977 /** 11978 * Text which is displayed when the table is processing a user action 11979 * (usually a sort command or similar). 11980 * @type string 11981 * 11982 * @dtopt Language 11983 * @name DataTable.defaults.language.processing 11984 * 11985 * @example 11986 * $(document).ready( function() { 11987 * $('#example').dataTable( { 11988 * "language": { 11989 * "processing": "DataTables is currently busy" 11990 * } 11991 * } ); 11992 * } ); 11993 */ 11994 "sProcessing": "", 11995 11996 11997 /** 11998 * Details the actions that will be taken when the user types into the 11999 * filtering input text box. The variable "_INPUT_", if used in the string, 12000 * is replaced with the HTML text box for the filtering input allowing 12001 * control over where it appears in the string. If "_INPUT_" is not given 12002 * then the input box is appended to the string automatically. 12003 * @type string 12004 * @default Search: 12005 * 12006 * @dtopt Language 12007 * @name DataTable.defaults.language.search 12008 * 12009 * @example 12010 * // Input text box will be appended at the end automatically 12011 * $(document).ready( function() { 12012 * $('#example').dataTable( { 12013 * "language": { 12014 * "search": "Filter records:" 12015 * } 12016 * } ); 12017 * } ); 12018 * 12019 * @example 12020 * // Specify where the filter should appear 12021 * $(document).ready( function() { 12022 * $('#example').dataTable( { 12023 * "language": { 12024 * "search": "Apply filter _INPUT_ to table" 12025 * } 12026 * } ); 12027 * } ); 12028 */ 12029 "sSearch": "Search:", 12030 12031 12032 /** 12033 * Assign a `placeholder` attribute to the search `input` element 12034 * @type string 12035 * @default 12036 * 12037 * @dtopt Language 12038 * @name DataTable.defaults.language.searchPlaceholder 12039 */ 12040 "sSearchPlaceholder": "", 12041 12042 12043 /** 12044 * All of the language information can be stored in a file on the 12045 * server-side, which DataTables will look up if this parameter is passed. 12046 * It must store the URL of the language file, which is in a JSON format, 12047 * and the object has the same properties as the oLanguage object in the 12048 * initialiser object (i.e. the above parameters). Please refer to one of 12049 * the example language files to see how this works in action. 12050 * @type string 12051 * @default <i>Empty string - i.e. disabled</i> 12052 * 12053 * @dtopt Language 12054 * @name DataTable.defaults.language.url 12055 * 12056 * @example 12057 * $(document).ready( function() { 12058 * $('#example').dataTable( { 12059 * "language": { 12060 * "url": "http://www.sprymedia.co.uk/dataTables/lang.txt" 12061 * } 12062 * } ); 12063 * } ); 12064 */ 12065 "sUrl": "", 12066 12067 12068 /** 12069 * Text shown inside the table records when the is no information to be 12070 * displayed after filtering. `emptyTable` is shown when there is simply no 12071 * information in the table at all (regardless of filtering). 12072 * @type string 12073 * @default No matching records found 12074 * 12075 * @dtopt Language 12076 * @name DataTable.defaults.language.zeroRecords 12077 * 12078 * @example 12079 * $(document).ready( function() { 12080 * $('#example').dataTable( { 12081 * "language": { 12082 * "zeroRecords": "No records to display" 12083 * } 12084 * } ); 12085 * } ); 12086 */ 12087 "sZeroRecords": "No matching records found" 12088 }, 12089 12090 12091 /** 12092 * This parameter allows you to have define the global filtering state at 12093 * initialisation time. As an object the `search` parameter must be 12094 * defined, but all other parameters are optional. When `regex` is true, 12095 * the search string will be treated as a regular expression, when false 12096 * (default) it will be treated as a straight string. When `smart` 12097 * DataTables will use it's smart filtering methods (to word match at 12098 * any point in the data), when false this will not be done. 12099 * @namespace 12100 * @extends DataTable.models.oSearch 12101 * 12102 * @dtopt Options 12103 * @name DataTable.defaults.search 12104 * 12105 * @example 12106 * $(document).ready( function() { 12107 * $('#example').dataTable( { 12108 * "search": {"search": "Initial search"} 12109 * } ); 12110 * } ) 12111 */ 12112 "oSearch": $.extend( {}, DataTable.models.oSearch ), 12113 12114 12115 /** 12116 * __Deprecated__ The functionality provided by this parameter has now been 12117 * superseded by that provided through `ajax`, which should be used instead. 12118 * 12119 * By default DataTables will look for the property `data` (or `aaData` for 12120 * compatibility with DataTables 1.9-) when obtaining data from an Ajax 12121 * source or for server-side processing - this parameter allows that 12122 * property to be changed. You can use Javascript dotted object notation to 12123 * get a data source for multiple levels of nesting. 12124 * @type string 12125 * @default data 12126 * 12127 * @dtopt Options 12128 * @dtopt Server-side 12129 * @name DataTable.defaults.ajaxDataProp 12130 * 12131 * @deprecated 1.10. Please use `ajax` for this functionality now. 12132 */ 12133 "sAjaxDataProp": "data", 12134 12135 12136 /** 12137 * __Deprecated__ The functionality provided by this parameter has now been 12138 * superseded by that provided through `ajax`, which should be used instead. 12139 * 12140 * You can instruct DataTables to load data from an external 12141 * source using this parameter (use aData if you want to pass data in you 12142 * already have). Simply provide a url a JSON object can be obtained from. 12143 * @type string 12144 * @default null 12145 * 12146 * @dtopt Options 12147 * @dtopt Server-side 12148 * @name DataTable.defaults.ajaxSource 12149 * 12150 * @deprecated 1.10. Please use `ajax` for this functionality now. 12151 */ 12152 "sAjaxSource": null, 12153 12154 12155 /** 12156 * This initialisation variable allows you to specify exactly where in the 12157 * DOM you want DataTables to inject the various controls it adds to the page 12158 * (for example you might want the pagination controls at the top of the 12159 * table). DIV elements (with or without a custom class) can also be added to 12160 * aid styling. The follow syntax is used: 12161 * <ul> 12162 * <li>The following options are allowed: 12163 * <ul> 12164 * <li>'l' - Length changing</li> 12165 * <li>'f' - Filtering input</li> 12166 * <li>'t' - The table!</li> 12167 * <li>'i' - Information</li> 12168 * <li>'p' - Pagination</li> 12169 * <li>'r' - pRocessing</li> 12170 * </ul> 12171 * </li> 12172 * <li>The following constants are allowed: 12173 * <ul> 12174 * <li>'H' - jQueryUI theme "header" classes ('fg-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix')</li> 12175 * <li>'F' - jQueryUI theme "footer" classes ('fg-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix')</li> 12176 * </ul> 12177 * </li> 12178 * <li>The following syntax is expected: 12179 * <ul> 12180 * <li>'<' and '>' - div elements</li> 12181 * <li>'<"class" and '>' - div with a class</li> 12182 * <li>'<"#id" and '>' - div with an ID</li> 12183 * </ul> 12184 * </li> 12185 * <li>Examples: 12186 * <ul> 12187 * <li>'<"wrapper"flipt>'</li> 12188 * <li>'<lf<t>ip>'</li> 12189 * </ul> 12190 * </li> 12191 * </ul> 12192 * @type string 12193 * @default lfrtip <i>(when `jQueryUI` is false)</i> <b>or</b> 12194 * <"H"lfr>t<"F"ip> <i>(when `jQueryUI` is true)</i> 12195 * 12196 * @dtopt Options 12197 * @name DataTable.defaults.dom 12198 * 12199 * @example 12200 * $(document).ready( function() { 12201 * $('#example').dataTable( { 12202 * "dom": '<"top"i>rt<"bottom"flp><"clear">' 12203 * } ); 12204 * } ); 12205 */ 12206 "sDom": "lfrtip", 12207 12208 12209 /** 12210 * Search delay option. This will throttle full table searches that use the 12211 * DataTables provided search input element (it does not effect calls to 12212 * `dt-api search()`, providing a delay before the search is made. 12213 * @type integer 12214 * @default 0 12215 * 12216 * @dtopt Options 12217 * @name DataTable.defaults.searchDelay 12218 * 12219 * @example 12220 * $(document).ready( function() { 12221 * $('#example').dataTable( { 12222 * "searchDelay": 200 12223 * } ); 12224 * } ) 12225 */ 12226 "searchDelay": null, 12227 12228 12229 /** 12230 * DataTables features six different built-in options for the buttons to 12231 * display for pagination control: 12232 * 12233 * * `numbers` - Page number buttons only 12234 * * `simple` - 'Previous' and 'Next' buttons only 12235 * * 'simple_numbers` - 'Previous' and 'Next' buttons, plus page numbers 12236 * * `full` - 'First', 'Previous', 'Next' and 'Last' buttons 12237 * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus page numbers 12238 * * `first_last_numbers` - 'First' and 'Last' buttons, plus page numbers 12239 * 12240 * Further methods can be added using {@link DataTable.ext.oPagination}. 12241 * @type string 12242 * @default simple_numbers 12243 * 12244 * @dtopt Options 12245 * @name DataTable.defaults.pagingType 12246 * 12247 * @example 12248 * $(document).ready( function() { 12249 * $('#example').dataTable( { 12250 * "pagingType": "full_numbers" 12251 * } ); 12252 * } ) 12253 */ 12254 "sPaginationType": "simple_numbers", 12255 12256 12257 /** 12258 * Enable horizontal scrolling. When a table is too wide to fit into a 12259 * certain layout, or you have a large number of columns in the table, you 12260 * can enable x-scrolling to show the table in a viewport, which can be 12261 * scrolled. This property can be `true` which will allow the table to 12262 * scroll horizontally when needed, or any CSS unit, or a number (in which 12263 * case it will be treated as a pixel measurement). Setting as simply `true` 12264 * is recommended. 12265 * @type boolean|string 12266 * @default <i>blank string - i.e. disabled</i> 12267 * 12268 * @dtopt Features 12269 * @name DataTable.defaults.scrollX 12270 * 12271 * @example 12272 * $(document).ready( function() { 12273 * $('#example').dataTable( { 12274 * "scrollX": true, 12275 * "scrollCollapse": true 12276 * } ); 12277 * } ); 12278 */ 12279 "sScrollX": "", 12280 12281 12282 /** 12283 * This property can be used to force a DataTable to use more width than it 12284 * might otherwise do when x-scrolling is enabled. For example if you have a 12285 * table which requires to be well spaced, this parameter is useful for 12286 * "over-sizing" the table, and thus forcing scrolling. This property can by 12287 * any CSS unit, or a number (in which case it will be treated as a pixel 12288 * measurement). 12289 * @type string 12290 * @default <i>blank string - i.e. disabled</i> 12291 * 12292 * @dtopt Options 12293 * @name DataTable.defaults.scrollXInner 12294 * 12295 * @example 12296 * $(document).ready( function() { 12297 * $('#example').dataTable( { 12298 * "scrollX": "100%", 12299 * "scrollXInner": "110%" 12300 * } ); 12301 * } ); 12302 */ 12303 "sScrollXInner": "", 12304 12305 12306 /** 12307 * Enable vertical scrolling. Vertical scrolling will constrain the DataTable 12308 * to the given height, and enable scrolling for any data which overflows the 12309 * current viewport. This can be used as an alternative to paging to display 12310 * a lot of data in a small area (although paging and scrolling can both be 12311 * enabled at the same time). This property can be any CSS unit, or a number 12312 * (in which case it will be treated as a pixel measurement). 12313 * @type string 12314 * @default <i>blank string - i.e. disabled</i> 12315 * 12316 * @dtopt Features 12317 * @name DataTable.defaults.scrollY 12318 * 12319 * @example 12320 * $(document).ready( function() { 12321 * $('#example').dataTable( { 12322 * "scrollY": "200px", 12323 * "paginate": false 12324 * } ); 12325 * } ); 12326 */ 12327 "sScrollY": "", 12328 12329 12330 /** 12331 * __Deprecated__ The functionality provided by this parameter has now been 12332 * superseded by that provided through `ajax`, which should be used instead. 12333 * 12334 * Set the HTTP method that is used to make the Ajax call for server-side 12335 * processing or Ajax sourced data. 12336 * @type string 12337 * @default GET 12338 * 12339 * @dtopt Options 12340 * @dtopt Server-side 12341 * @name DataTable.defaults.serverMethod 12342 * 12343 * @deprecated 1.10. Please use `ajax` for this functionality now. 12344 */ 12345 "sServerMethod": "GET", 12346 12347 12348 /** 12349 * DataTables makes use of renderers when displaying HTML elements for 12350 * a table. These renderers can be added or modified by plug-ins to 12351 * generate suitable mark-up for a site. For example the Bootstrap 12352 * integration plug-in for DataTables uses a paging button renderer to 12353 * display pagination buttons in the mark-up required by Bootstrap. 12354 * 12355 * For further information about the renderers available see 12356 * DataTable.ext.renderer 12357 * @type string|object 12358 * @default null 12359 * 12360 * @name DataTable.defaults.renderer 12361 * 12362 */ 12363 "renderer": null, 12364 12365 12366 /** 12367 * Set the data property name that DataTables should use to get a row's id 12368 * to set as the `id` property in the node. 12369 * @type string 12370 * @default DT_RowId 12371 * 12372 * @name DataTable.defaults.rowId 12373 */ 12374 "rowId": "DT_RowId" 12375 }; 12376 12377 _fnHungarianMap( DataTable.defaults ); 12378 12379 12380 12381 /* 12382 * Developer note - See note in model.defaults.js about the use of Hungarian 12383 * notation and camel case. 12384 */ 12385 12386 /** 12387 * Column options that can be given to DataTables at initialisation time. 12388 * @namespace 12389 */ 12390 DataTable.defaults.column = { 12391 /** 12392 * Define which column(s) an order will occur on for this column. This 12393 * allows a column's ordering to take multiple columns into account when 12394 * doing a sort or use the data from a different column. For example first 12395 * name / last name columns make sense to do a multi-column sort over the 12396 * two columns. 12397 * @type array|int 12398 * @default null <i>Takes the value of the column index automatically</i> 12399 * 12400 * @name DataTable.defaults.column.orderData 12401 * @dtopt Columns 12402 * 12403 * @example 12404 * // Using `columnDefs` 12405 * $(document).ready( function() { 12406 * $('#example').dataTable( { 12407 * "columnDefs": [ 12408 * { "orderData": [ 0, 1 ], "targets": [ 0 ] }, 12409 * { "orderData": [ 1, 0 ], "targets": [ 1 ] }, 12410 * { "orderData": 2, "targets": [ 2 ] } 12411 * ] 12412 * } ); 12413 * } ); 12414 * 12415 * @example 12416 * // Using `columns` 12417 * $(document).ready( function() { 12418 * $('#example').dataTable( { 12419 * "columns": [ 12420 * { "orderData": [ 0, 1 ] }, 12421 * { "orderData": [ 1, 0 ] }, 12422 * { "orderData": 2 }, 12423 * null, 12424 * null 12425 * ] 12426 * } ); 12427 * } ); 12428 */ 12429 "aDataSort": null, 12430 "iDataSort": -1, 12431 12432 12433 /** 12434 * You can control the default ordering direction, and even alter the 12435 * behaviour of the sort handler (i.e. only allow ascending ordering etc) 12436 * using this parameter. 12437 * @type array 12438 * @default [ 'asc', 'desc' ] 12439 * 12440 * @name DataTable.defaults.column.orderSequence 12441 * @dtopt Columns 12442 * 12443 * @example 12444 * // Using `columnDefs` 12445 * $(document).ready( function() { 12446 * $('#example').dataTable( { 12447 * "columnDefs": [ 12448 * { "orderSequence": [ "asc" ], "targets": [ 1 ] }, 12449 * { "orderSequence": [ "desc", "asc", "asc" ], "targets": [ 2 ] }, 12450 * { "orderSequence": [ "desc" ], "targets": [ 3 ] } 12451 * ] 12452 * } ); 12453 * } ); 12454 * 12455 * @example 12456 * // Using `columns` 12457 * $(document).ready( function() { 12458 * $('#example').dataTable( { 12459 * "columns": [ 12460 * null, 12461 * { "orderSequence": [ "asc" ] }, 12462 * { "orderSequence": [ "desc", "asc", "asc" ] }, 12463 * { "orderSequence": [ "desc" ] }, 12464 * null 12465 * ] 12466 * } ); 12467 * } ); 12468 */ 12469 "asSorting": [ 'asc', 'desc' ], 12470 12471 12472 /** 12473 * Enable or disable filtering on the data in this column. 12474 * @type boolean 12475 * @default true 12476 * 12477 * @name DataTable.defaults.column.searchable 12478 * @dtopt Columns 12479 * 12480 * @example 12481 * // Using `columnDefs` 12482 * $(document).ready( function() { 12483 * $('#example').dataTable( { 12484 * "columnDefs": [ 12485 * { "searchable": false, "targets": [ 0 ] } 12486 * ] } ); 12487 * } ); 12488 * 12489 * @example 12490 * // Using `columns` 12491 * $(document).ready( function() { 12492 * $('#example').dataTable( { 12493 * "columns": [ 12494 * { "searchable": false }, 12495 * null, 12496 * null, 12497 * null, 12498 * null 12499 * ] } ); 12500 * } ); 12501 */ 12502 "bSearchable": true, 12503 12504 12505 /** 12506 * Enable or disable ordering on this column. 12507 * @type boolean 12508 * @default true 12509 * 12510 * @name DataTable.defaults.column.orderable 12511 * @dtopt Columns 12512 * 12513 * @example 12514 * // Using `columnDefs` 12515 * $(document).ready( function() { 12516 * $('#example').dataTable( { 12517 * "columnDefs": [ 12518 * { "orderable": false, "targets": [ 0 ] } 12519 * ] } ); 12520 * } ); 12521 * 12522 * @example 12523 * // Using `columns` 12524 * $(document).ready( function() { 12525 * $('#example').dataTable( { 12526 * "columns": [ 12527 * { "orderable": false }, 12528 * null, 12529 * null, 12530 * null, 12531 * null 12532 * ] } ); 12533 * } ); 12534 */ 12535 "bSortable": true, 12536 12537 12538 /** 12539 * Enable or disable the display of this column. 12540 * @type boolean 12541 * @default true 12542 * 12543 * @name DataTable.defaults.column.visible 12544 * @dtopt Columns 12545 * 12546 * @example 12547 * // Using `columnDefs` 12548 * $(document).ready( function() { 12549 * $('#example').dataTable( { 12550 * "columnDefs": [ 12551 * { "visible": false, "targets": [ 0 ] } 12552 * ] } ); 12553 * } ); 12554 * 12555 * @example 12556 * // Using `columns` 12557 * $(document).ready( function() { 12558 * $('#example').dataTable( { 12559 * "columns": [ 12560 * { "visible": false }, 12561 * null, 12562 * null, 12563 * null, 12564 * null 12565 * ] } ); 12566 * } ); 12567 */ 12568 "bVisible": true, 12569 12570 12571 /** 12572 * Developer definable function that is called whenever a cell is created (Ajax source, 12573 * etc) or processed for input (DOM source). This can be used as a compliment to mRender 12574 * allowing you to modify the DOM element (add background colour for example) when the 12575 * element is available. 12576 * @type function 12577 * @param {element} td The TD node that has been created 12578 * @param {*} cellData The Data for the cell 12579 * @param {array|object} rowData The data for the whole row 12580 * @param {int} row The row index for the aoData data store 12581 * @param {int} col The column index for aoColumns 12582 * 12583 * @name DataTable.defaults.column.createdCell 12584 * @dtopt Columns 12585 * 12586 * @example 12587 * $(document).ready( function() { 12588 * $('#example').dataTable( { 12589 * "columnDefs": [ { 12590 * "targets": [3], 12591 * "createdCell": function (td, cellData, rowData, row, col) { 12592 * if ( cellData == "1.7" ) { 12593 * $(td).css('color', 'blue') 12594 * } 12595 * } 12596 * } ] 12597 * }); 12598 * } ); 12599 */ 12600 "fnCreatedCell": null, 12601 12602 12603 /** 12604 * This parameter has been replaced by `data` in DataTables to ensure naming 12605 * consistency. `dataProp` can still be used, as there is backwards 12606 * compatibility in DataTables for this option, but it is strongly 12607 * recommended that you use `data` in preference to `dataProp`. 12608 * @name DataTable.defaults.column.dataProp 12609 */ 12610 12611 12612 /** 12613 * This property can be used to read data from any data source property, 12614 * including deeply nested objects / properties. `data` can be given in a 12615 * number of different ways which effect its behaviour: 12616 * 12617 * * `integer` - treated as an array index for the data source. This is the 12618 * default that DataTables uses (incrementally increased for each column). 12619 * * `string` - read an object property from the data source. There are 12620 * three 'special' options that can be used in the string to alter how 12621 * DataTables reads the data from the source object: 12622 * * `.` - Dotted Javascript notation. Just as you use a `.` in 12623 * Javascript to read from nested objects, so to can the options 12624 * specified in `data`. For example: `browser.version` or 12625 * `browser.name`. If your object parameter name contains a period, use 12626 * `\\` to escape it - i.e. `first\\.name`. 12627 * * `[]` - Array notation. DataTables can automatically combine data 12628 * from and array source, joining the data with the characters provided 12629 * between the two brackets. For example: `name[, ]` would provide a 12630 * comma-space separated list from the source array. If no characters 12631 * are provided between the brackets, the original array source is 12632 * returned. 12633 * * `()` - Function notation. Adding `()` to the end of a parameter will 12634 * execute a function of the name given. For example: `browser()` for a 12635 * simple function on the data source, `browser.version()` for a 12636 * function in a nested property or even `browser().version` to get an 12637 * object property if the function called returns an object. Note that 12638 * function notation is recommended for use in `render` rather than 12639 * `data` as it is much simpler to use as a renderer. 12640 * * `null` - use the original data source for the row rather than plucking 12641 * data directly from it. This action has effects on two other 12642 * initialisation options: 12643 * * `defaultContent` - When null is given as the `data` option and 12644 * `defaultContent` is specified for the column, the value defined by 12645 * `defaultContent` will be used for the cell. 12646 * * `render` - When null is used for the `data` option and the `render` 12647 * option is specified for the column, the whole data source for the 12648 * row is used for the renderer. 12649 * * `function` - the function given will be executed whenever DataTables 12650 * needs to set or get the data for a cell in the column. The function 12651 * takes three parameters: 12652 * * Parameters: 12653 * * `{array|object}` The data source for the row 12654 * * `{string}` The type call data requested - this will be 'set' when 12655 * setting data or 'filter', 'display', 'type', 'sort' or undefined 12656 * when gathering data. Note that when `undefined` is given for the 12657 * type DataTables expects to get the raw data for the object back< 12658 * * `{*}` Data to set when the second parameter is 'set'. 12659 * * Return: 12660 * * The return value from the function is not required when 'set' is 12661 * the type of call, but otherwise the return is what will be used 12662 * for the data requested. 12663 * 12664 * Note that `data` is a getter and setter option. If you just require 12665 * formatting of data for output, you will likely want to use `render` which 12666 * is simply a getter and thus simpler to use. 12667 * 12668 * Note that prior to DataTables 1.9.2 `data` was called `mDataProp`. The 12669 * name change reflects the flexibility of this property and is consistent 12670 * with the naming of mRender. If 'mDataProp' is given, then it will still 12671 * be used by DataTables, as it automatically maps the old name to the new 12672 * if required. 12673 * 12674 * @type string|int|function|null 12675 * @default null <i>Use automatically calculated column index</i> 12676 * 12677 * @name DataTable.defaults.column.data 12678 * @dtopt Columns 12679 * 12680 * @example 12681 * // Read table data from objects 12682 * // JSON structure for each row: 12683 * // { 12684 * // "engine": {value}, 12685 * // "browser": {value}, 12686 * // "platform": {value}, 12687 * // "version": {value}, 12688 * // "grade": {value} 12689 * // } 12690 * $(document).ready( function() { 12691 * $('#example').dataTable( { 12692 * "ajaxSource": "sources/objects.txt", 12693 * "columns": [ 12694 * { "data": "engine" }, 12695 * { "data": "browser" }, 12696 * { "data": "platform" }, 12697 * { "data": "version" }, 12698 * { "data": "grade" } 12699 * ] 12700 * } ); 12701 * } ); 12702 * 12703 * @example 12704 * // Read information from deeply nested objects 12705 * // JSON structure for each row: 12706 * // { 12707 * // "engine": {value}, 12708 * // "browser": {value}, 12709 * // "platform": { 12710 * // "inner": {value} 12711 * // }, 12712 * // "details": [ 12713 * // {value}, {value} 12714 * // ] 12715 * // } 12716 * $(document).ready( function() { 12717 * $('#example').dataTable( { 12718 * "ajaxSource": "sources/deep.txt", 12719 * "columns": [ 12720 * { "data": "engine" }, 12721 * { "data": "browser" }, 12722 * { "data": "platform.inner" }, 12723 * { "data": "details.0" }, 12724 * { "data": "details.1" } 12725 * ] 12726 * } ); 12727 * } ); 12728 * 12729 * @example 12730 * // Using `data` as a function to provide different information for 12731 * // sorting, filtering and display. In this case, currency (price) 12732 * $(document).ready( function() { 12733 * $('#example').dataTable( { 12734 * "columnDefs": [ { 12735 * "targets": [ 0 ], 12736 * "data": function ( source, type, val ) { 12737 * if (type === 'set') { 12738 * source.price = val; 12739 * // Store the computed display and filter values for efficiency 12740 * source.price_display = val=="" ? "" : "$"+numberFormat(val); 12741 * source.price_filter = val=="" ? "" : "$"+numberFormat(val)+" "+val; 12742 * return; 12743 * } 12744 * else if (type === 'display') { 12745 * return source.price_display; 12746 * } 12747 * else if (type === 'filter') { 12748 * return source.price_filter; 12749 * } 12750 * // 'sort', 'type' and undefined all just use the integer 12751 * return source.price; 12752 * } 12753 * } ] 12754 * } ); 12755 * } ); 12756 * 12757 * @example 12758 * // Using default content 12759 * $(document).ready( function() { 12760 * $('#example').dataTable( { 12761 * "columnDefs": [ { 12762 * "targets": [ 0 ], 12763 * "data": null, 12764 * "defaultContent": "Click to edit" 12765 * } ] 12766 * } ); 12767 * } ); 12768 * 12769 * @example 12770 * // Using array notation - outputting a list from an array 12771 * $(document).ready( function() { 12772 * $('#example').dataTable( { 12773 * "columnDefs": [ { 12774 * "targets": [ 0 ], 12775 * "data": "name[, ]" 12776 * } ] 12777 * } ); 12778 * } ); 12779 * 12780 */ 12781 "mData": null, 12782 12783 12784 /** 12785 * This property is the rendering partner to `data` and it is suggested that 12786 * when you want to manipulate data for display (including filtering, 12787 * sorting etc) without altering the underlying data for the table, use this 12788 * property. `render` can be considered to be the the read only companion to 12789 * `data` which is read / write (then as such more complex). Like `data` 12790 * this option can be given in a number of different ways to effect its 12791 * behaviour: 12792 * 12793 * * `integer` - treated as an array index for the data source. This is the 12794 * default that DataTables uses (incrementally increased for each column). 12795 * * `string` - read an object property from the data source. There are 12796 * three 'special' options that can be used in the string to alter how 12797 * DataTables reads the data from the source object: 12798 * * `.` - Dotted Javascript notation. Just as you use a `.` in 12799 * Javascript to read from nested objects, so to can the options 12800 * specified in `data`. For example: `browser.version` or 12801 * `browser.name`. If your object parameter name contains a period, use 12802 * `\\` to escape it - i.e. `first\\.name`. 12803 * * `[]` - Array notation. DataTables can automatically combine data 12804 * from and array source, joining the data with the characters provided 12805 * between the two brackets. For example: `name[, ]` would provide a 12806 * comma-space separated list from the source array. If no characters 12807 * are provided between the brackets, the original array source is 12808 * returned. 12809 * * `()` - Function notation. Adding `()` to the end of a parameter will 12810 * execute a function of the name given. For example: `browser()` for a 12811 * simple function on the data source, `browser.version()` for a 12812 * function in a nested property or even `browser().version` to get an 12813 * object property if the function called returns an object. 12814 * * `object` - use different data for the different data types requested by 12815 * DataTables ('filter', 'display', 'type' or 'sort'). The property names 12816 * of the object is the data type the property refers to and the value can 12817 * defined using an integer, string or function using the same rules as 12818 * `render` normally does. Note that an `_` option _must_ be specified. 12819 * This is the default value to use if you haven't specified a value for 12820 * the data type requested by DataTables. 12821 * * `function` - the function given will be executed whenever DataTables 12822 * needs to set or get the data for a cell in the column. The function 12823 * takes three parameters: 12824 * * Parameters: 12825 * * {array|object} The data source for the row (based on `data`) 12826 * * {string} The type call data requested - this will be 'filter', 12827 * 'display', 'type' or 'sort'. 12828 * * {array|object} The full data source for the row (not based on 12829 * `data`) 12830 * * Return: 12831 * * The return value from the function is what will be used for the 12832 * data requested. 12833 * 12834 * @type string|int|function|object|null 12835 * @default null Use the data source value. 12836 * 12837 * @name DataTable.defaults.column.render 12838 * @dtopt Columns 12839 * 12840 * @example 12841 * // Create a comma separated list from an array of objects 12842 * $(document).ready( function() { 12843 * $('#example').dataTable( { 12844 * "ajaxSource": "sources/deep.txt", 12845 * "columns": [ 12846 * { "data": "engine" }, 12847 * { "data": "browser" }, 12848 * { 12849 * "data": "platform", 12850 * "render": "[, ].name" 12851 * } 12852 * ] 12853 * } ); 12854 * } ); 12855 * 12856 * @example 12857 * // Execute a function to obtain data 12858 * $(document).ready( function() { 12859 * $('#example').dataTable( { 12860 * "columnDefs": [ { 12861 * "targets": [ 0 ], 12862 * "data": null, // Use the full data source object for the renderer's source 12863 * "render": "browserName()" 12864 * } ] 12865 * } ); 12866 * } ); 12867 * 12868 * @example 12869 * // As an object, extracting different data for the different types 12870 * // This would be used with a data source such as: 12871 * // { "phone": 5552368, "phone_filter": "5552368 555-2368", "phone_display": "555-2368" } 12872 * // Here the `phone` integer is used for sorting and type detection, while `phone_filter` 12873 * // (which has both forms) is used for filtering for if a user inputs either format, while 12874 * // the formatted phone number is the one that is shown in the table. 12875 * $(document).ready( function() { 12876 * $('#example').dataTable( { 12877 * "columnDefs": [ { 12878 * "targets": [ 0 ], 12879 * "data": null, // Use the full data source object for the renderer's source 12880 * "render": { 12881 * "_": "phone", 12882 * "filter": "phone_filter", 12883 * "display": "phone_display" 12884 * } 12885 * } ] 12886 * } ); 12887 * } ); 12888 * 12889 * @example 12890 * // Use as a function to create a link from the data source 12891 * $(document).ready( function() { 12892 * $('#example').dataTable( { 12893 * "columnDefs": [ { 12894 * "targets": [ 0 ], 12895 * "data": "download_link", 12896 * "render": function ( data, type, full ) { 12897 * return '<a href="'+data+'">Download</a>'; 12898 * } 12899 * } ] 12900 * } ); 12901 * } ); 12902 */ 12903 "mRender": null, 12904 12905 12906 /** 12907 * Change the cell type created for the column - either TD cells or TH cells. This 12908 * can be useful as TH cells have semantic meaning in the table body, allowing them 12909 * to act as a header for a row (you may wish to add scope='row' to the TH elements). 12910 * @type string 12911 * @default td 12912 * 12913 * @name DataTable.defaults.column.cellType 12914 * @dtopt Columns 12915 * 12916 * @example 12917 * // Make the first column use TH cells 12918 * $(document).ready( function() { 12919 * $('#example').dataTable( { 12920 * "columnDefs": [ { 12921 * "targets": [ 0 ], 12922 * "cellType": "th" 12923 * } ] 12924 * } ); 12925 * } ); 12926 */ 12927 "sCellType": "td", 12928 12929 12930 /** 12931 * Class to give to each cell in this column. 12932 * @type string 12933 * @default <i>Empty string</i> 12934 * 12935 * @name DataTable.defaults.column.class 12936 * @dtopt Columns 12937 * 12938 * @example 12939 * // Using `columnDefs` 12940 * $(document).ready( function() { 12941 * $('#example').dataTable( { 12942 * "columnDefs": [ 12943 * { "class": "my_class", "targets": [ 0 ] } 12944 * ] 12945 * } ); 12946 * } ); 12947 * 12948 * @example 12949 * // Using `columns` 12950 * $(document).ready( function() { 12951 * $('#example').dataTable( { 12952 * "columns": [ 12953 * { "class": "my_class" }, 12954 * null, 12955 * null, 12956 * null, 12957 * null 12958 * ] 12959 * } ); 12960 * } ); 12961 */ 12962 "sClass": "", 12963 12964 /** 12965 * When DataTables calculates the column widths to assign to each column, 12966 * it finds the longest string in each column and then constructs a 12967 * temporary table and reads the widths from that. The problem with this 12968 * is that "mmm" is much wider then "iiii", but the latter is a longer 12969 * string - thus the calculation can go wrong (doing it properly and putting 12970 * it into an DOM object and measuring that is horribly(!) slow). Thus as 12971 * a "work around" we provide this option. It will append its value to the 12972 * text that is found to be the longest string for the column - i.e. padding. 12973 * Generally you shouldn't need this! 12974 * @type string 12975 * @default <i>Empty string<i> 12976 * 12977 * @name DataTable.defaults.column.contentPadding 12978 * @dtopt Columns 12979 * 12980 * @example 12981 * // Using `columns` 12982 * $(document).ready( function() { 12983 * $('#example').dataTable( { 12984 * "columns": [ 12985 * null, 12986 * null, 12987 * null, 12988 * { 12989 * "contentPadding": "mmm" 12990 * } 12991 * ] 12992 * } ); 12993 * } ); 12994 */ 12995 "sContentPadding": "", 12996 12997 12998 /** 12999 * Allows a default value to be given for a column's data, and will be used 13000 * whenever a null data source is encountered (this can be because `data` 13001 * is set to null, or because the data source itself is null). 13002 * @type string 13003 * @default null 13004 * 13005 * @name DataTable.defaults.column.defaultContent 13006 * @dtopt Columns 13007 * 13008 * @example 13009 * // Using `columnDefs` 13010 * $(document).ready( function() { 13011 * $('#example').dataTable( { 13012 * "columnDefs": [ 13013 * { 13014 * "data": null, 13015 * "defaultContent": "Edit", 13016 * "targets": [ -1 ] 13017 * } 13018 * ] 13019 * } ); 13020 * } ); 13021 * 13022 * @example 13023 * // Using `columns` 13024 * $(document).ready( function() { 13025 * $('#example').dataTable( { 13026 * "columns": [ 13027 * null, 13028 * null, 13029 * null, 13030 * { 13031 * "data": null, 13032 * "defaultContent": "Edit" 13033 * } 13034 * ] 13035 * } ); 13036 * } ); 13037 */ 13038 "sDefaultContent": null, 13039 13040 13041 /** 13042 * This parameter is only used in DataTables' server-side processing. It can 13043 * be exceptionally useful to know what columns are being displayed on the 13044 * client side, and to map these to database fields. When defined, the names 13045 * also allow DataTables to reorder information from the server if it comes 13046 * back in an unexpected order (i.e. if you switch your columns around on the 13047 * client-side, your server-side code does not also need updating). 13048 * @type string 13049 * @default <i>Empty string</i> 13050 * 13051 * @name DataTable.defaults.column.name 13052 * @dtopt Columns 13053 * 13054 * @example 13055 * // Using `columnDefs` 13056 * $(document).ready( function() { 13057 * $('#example').dataTable( { 13058 * "columnDefs": [ 13059 * { "name": "engine", "targets": [ 0 ] }, 13060 * { "name": "browser", "targets": [ 1 ] }, 13061 * { "name": "platform", "targets": [ 2 ] }, 13062 * { "name": "version", "targets": [ 3 ] }, 13063 * { "name": "grade", "targets": [ 4 ] } 13064 * ] 13065 * } ); 13066 * } ); 13067 * 13068 * @example 13069 * // Using `columns` 13070 * $(document).ready( function() { 13071 * $('#example').dataTable( { 13072 * "columns": [ 13073 * { "name": "engine" }, 13074 * { "name": "browser" }, 13075 * { "name": "platform" }, 13076 * { "name": "version" }, 13077 * { "name": "grade" } 13078 * ] 13079 * } ); 13080 * } ); 13081 */ 13082 "sName": "", 13083 13084 13085 /** 13086 * Defines a data source type for the ordering which can be used to read 13087 * real-time information from the table (updating the internally cached 13088 * version) prior to ordering. This allows ordering to occur on user 13089 * editable elements such as form inputs. 13090 * @type string 13091 * @default std 13092 * 13093 * @name DataTable.defaults.column.orderDataType 13094 * @dtopt Columns 13095 * 13096 * @example 13097 * // Using `columnDefs` 13098 * $(document).ready( function() { 13099 * $('#example').dataTable( { 13100 * "columnDefs": [ 13101 * { "orderDataType": "dom-text", "targets": [ 2, 3 ] }, 13102 * { "type": "numeric", "targets": [ 3 ] }, 13103 * { "orderDataType": "dom-select", "targets": [ 4 ] }, 13104 * { "orderDataType": "dom-checkbox", "targets": [ 5 ] } 13105 * ] 13106 * } ); 13107 * } ); 13108 * 13109 * @example 13110 * // Using `columns` 13111 * $(document).ready( function() { 13112 * $('#example').dataTable( { 13113 * "columns": [ 13114 * null, 13115 * null, 13116 * { "orderDataType": "dom-text" }, 13117 * { "orderDataType": "dom-text", "type": "numeric" }, 13118 * { "orderDataType": "dom-select" }, 13119 * { "orderDataType": "dom-checkbox" } 13120 * ] 13121 * } ); 13122 * } ); 13123 */ 13124 "sSortDataType": "std", 13125 13126 13127 /** 13128 * The title of this column. 13129 * @type string 13130 * @default null <i>Derived from the 'TH' value for this column in the 13131 * original HTML table.</i> 13132 * 13133 * @name DataTable.defaults.column.title 13134 * @dtopt Columns 13135 * 13136 * @example 13137 * // Using `columnDefs` 13138 * $(document).ready( function() { 13139 * $('#example').dataTable( { 13140 * "columnDefs": [ 13141 * { "title": "My column title", "targets": [ 0 ] } 13142 * ] 13143 * } ); 13144 * } ); 13145 * 13146 * @example 13147 * // Using `columns` 13148 * $(document).ready( function() { 13149 * $('#example').dataTable( { 13150 * "columns": [ 13151 * { "title": "My column title" }, 13152 * null, 13153 * null, 13154 * null, 13155 * null 13156 * ] 13157 * } ); 13158 * } ); 13159 */ 13160 "sTitle": null, 13161 13162 13163 /** 13164 * The type allows you to specify how the data for this column will be 13165 * ordered. Four types (string, numeric, date and html (which will strip 13166 * HTML tags before ordering)) are currently available. Note that only date 13167 * formats understood by Javascript's Date() object will be accepted as type 13168 * date. For example: "Mar 26, 2008 5:03 PM". May take the values: 'string', 13169 * 'numeric', 'date' or 'html' (by default). Further types can be adding 13170 * through plug-ins. 13171 * @type string 13172 * @default null <i>Auto-detected from raw data</i> 13173 * 13174 * @name DataTable.defaults.column.type 13175 * @dtopt Columns 13176 * 13177 * @example 13178 * // Using `columnDefs` 13179 * $(document).ready( function() { 13180 * $('#example').dataTable( { 13181 * "columnDefs": [ 13182 * { "type": "html", "targets": [ 0 ] } 13183 * ] 13184 * } ); 13185 * } ); 13186 * 13187 * @example 13188 * // Using `columns` 13189 * $(document).ready( function() { 13190 * $('#example').dataTable( { 13191 * "columns": [ 13192 * { "type": "html" }, 13193 * null, 13194 * null, 13195 * null, 13196 * null 13197 * ] 13198 * } ); 13199 * } ); 13200 */ 13201 "sType": null, 13202 13203 13204 /** 13205 * Defining the width of the column, this parameter may take any CSS value 13206 * (3em, 20px etc). DataTables applies 'smart' widths to columns which have not 13207 * been given a specific width through this interface ensuring that the table 13208 * remains readable. 13209 * @type string 13210 * @default null <i>Automatic</i> 13211 * 13212 * @name DataTable.defaults.column.width 13213 * @dtopt Columns 13214 * 13215 * @example 13216 * // Using `columnDefs` 13217 * $(document).ready( function() { 13218 * $('#example').dataTable( { 13219 * "columnDefs": [ 13220 * { "width": "20%", "targets": [ 0 ] } 13221 * ] 13222 * } ); 13223 * } ); 13224 * 13225 * @example 13226 * // Using `columns` 13227 * $(document).ready( function() { 13228 * $('#example').dataTable( { 13229 * "columns": [ 13230 * { "width": "20%" }, 13231 * null, 13232 * null, 13233 * null, 13234 * null 13235 * ] 13236 * } ); 13237 * } ); 13238 */ 13239 "sWidth": null 13240 }; 13241 13242 _fnHungarianMap( DataTable.defaults.column ); 13243 13244 13245 13246 /** 13247 * DataTables settings object - this holds all the information needed for a 13248 * given table, including configuration, data and current application of the 13249 * table options. DataTables does not have a single instance for each DataTable 13250 * with the settings attached to that instance, but rather instances of the 13251 * DataTable "class" are created on-the-fly as needed (typically by a 13252 * $().dataTable() call) and the settings object is then applied to that 13253 * instance. 13254 * 13255 * Note that this object is related to {@link DataTable.defaults} but this 13256 * one is the internal data store for DataTables's cache of columns. It should 13257 * NOT be manipulated outside of DataTables. Any configuration should be done 13258 * through the initialisation options. 13259 * @namespace 13260 * @todo Really should attach the settings object to individual instances so we 13261 * don't need to create new instances on each $().dataTable() call (if the 13262 * table already exists). It would also save passing oSettings around and 13263 * into every single function. However, this is a very significant 13264 * architecture change for DataTables and will almost certainly break 13265 * backwards compatibility with older installations. This is something that 13266 * will be done in 2.0. 13267 */ 13268 DataTable.models.oSettings = { 13269 /** 13270 * Primary features of DataTables and their enablement state. 13271 * @namespace 13272 */ 13273 "oFeatures": { 13274 13275 /** 13276 * Flag to say if DataTables should automatically try to calculate the 13277 * optimum table and columns widths (true) or not (false). 13278 * Note that this parameter will be set by the initialisation routine. To 13279 * set a default use {@link DataTable.defaults}. 13280 * @type boolean 13281 */ 13282 "bAutoWidth": null, 13283 13284 /** 13285 * Delay the creation of TR and TD elements until they are actually 13286 * needed by a driven page draw. This can give a significant speed 13287 * increase for Ajax source and Javascript source data, but makes no 13288 * difference at all for DOM and server-side processing tables. 13289 * Note that this parameter will be set by the initialisation routine. To 13290 * set a default use {@link DataTable.defaults}. 13291 * @type boolean 13292 */ 13293 "bDeferRender": null, 13294 13295 /** 13296 * Enable filtering on the table or not. Note that if this is disabled 13297 * then there is no filtering at all on the table, including fnFilter. 13298 * To just remove the filtering input use sDom and remove the 'f' option. 13299 * Note that this parameter will be set by the initialisation routine. To 13300 * set a default use {@link DataTable.defaults}. 13301 * @type boolean 13302 */ 13303 "bFilter": null, 13304 13305 /** 13306 * Table information element (the 'Showing x of y records' div) enable 13307 * flag. 13308 * Note that this parameter will be set by the initialisation routine. To 13309 * set a default use {@link DataTable.defaults}. 13310 * @type boolean 13311 */ 13312 "bInfo": null, 13313 13314 /** 13315 * Present a user control allowing the end user to change the page size 13316 * when pagination is enabled. 13317 * Note that this parameter will be set by the initialisation routine. To 13318 * set a default use {@link DataTable.defaults}. 13319 * @type boolean 13320 */ 13321 "bLengthChange": null, 13322 13323 /** 13324 * Pagination enabled or not. Note that if this is disabled then length 13325 * changing must also be disabled. 13326 * Note that this parameter will be set by the initialisation routine. To 13327 * set a default use {@link DataTable.defaults}. 13328 * @type boolean 13329 */ 13330 "bPaginate": null, 13331 13332 /** 13333 * Processing indicator enable flag whenever DataTables is enacting a 13334 * user request - typically an Ajax request for server-side processing. 13335 * Note that this parameter will be set by the initialisation routine. To 13336 * set a default use {@link DataTable.defaults}. 13337 * @type boolean 13338 */ 13339 "bProcessing": null, 13340 13341 /** 13342 * Server-side processing enabled flag - when enabled DataTables will 13343 * get all data from the server for every draw - there is no filtering, 13344 * sorting or paging done on the client-side. 13345 * Note that this parameter will be set by the initialisation routine. To 13346 * set a default use {@link DataTable.defaults}. 13347 * @type boolean 13348 */ 13349 "bServerSide": null, 13350 13351 /** 13352 * Sorting enablement flag. 13353 * Note that this parameter will be set by the initialisation routine. To 13354 * set a default use {@link DataTable.defaults}. 13355 * @type boolean 13356 */ 13357 "bSort": null, 13358 13359 /** 13360 * Multi-column sorting 13361 * Note that this parameter will be set by the initialisation routine. To 13362 * set a default use {@link DataTable.defaults}. 13363 * @type boolean 13364 */ 13365 "bSortMulti": null, 13366 13367 /** 13368 * Apply a class to the columns which are being sorted to provide a 13369 * visual highlight or not. This can slow things down when enabled since 13370 * there is a lot of DOM interaction. 13371 * Note that this parameter will be set by the initialisation routine. To 13372 * set a default use {@link DataTable.defaults}. 13373 * @type boolean 13374 */ 13375 "bSortClasses": null, 13376 13377 /** 13378 * State saving enablement flag. 13379 * Note that this parameter will be set by the initialisation routine. To 13380 * set a default use {@link DataTable.defaults}. 13381 * @type boolean 13382 */ 13383 "bStateSave": null 13384 }, 13385 13386 13387 /** 13388 * Scrolling settings for a table. 13389 * @namespace 13390 */ 13391 "oScroll": { 13392 /** 13393 * When the table is shorter in height than sScrollY, collapse the 13394 * table container down to the height of the table (when true). 13395 * Note that this parameter will be set by the initialisation routine. To 13396 * set a default use {@link DataTable.defaults}. 13397 * @type boolean 13398 */ 13399 "bCollapse": null, 13400 13401 /** 13402 * Width of the scrollbar for the web-browser's platform. Calculated 13403 * during table initialisation. 13404 * @type int 13405 * @default 0 13406 */ 13407 "iBarWidth": 0, 13408 13409 /** 13410 * Viewport width for horizontal scrolling. Horizontal scrolling is 13411 * disabled if an empty string. 13412 * Note that this parameter will be set by the initialisation routine. To 13413 * set a default use {@link DataTable.defaults}. 13414 * @type string 13415 */ 13416 "sX": null, 13417 13418 /** 13419 * Width to expand the table to when using x-scrolling. Typically you 13420 * should not need to use this. 13421 * Note that this parameter will be set by the initialisation routine. To 13422 * set a default use {@link DataTable.defaults}. 13423 * @type string 13424 * @deprecated 13425 */ 13426 "sXInner": null, 13427 13428 /** 13429 * Viewport height for vertical scrolling. Vertical scrolling is disabled 13430 * if an empty string. 13431 * Note that this parameter will be set by the initialisation routine. To 13432 * set a default use {@link DataTable.defaults}. 13433 * @type string 13434 */ 13435 "sY": null 13436 }, 13437 13438 /** 13439 * Language information for the table. 13440 * @namespace 13441 * @extends DataTable.defaults.oLanguage 13442 */ 13443 "oLanguage": { 13444 /** 13445 * Information callback function. See 13446 * {@link DataTable.defaults.fnInfoCallback} 13447 * @type function 13448 * @default null 13449 */ 13450 "fnInfoCallback": null 13451 }, 13452 13453 /** 13454 * Browser support parameters 13455 * @namespace 13456 */ 13457 "oBrowser": { 13458 /** 13459 * Indicate if the browser incorrectly calculates width:100% inside a 13460 * scrolling element (IE6/7) 13461 * @type boolean 13462 * @default false 13463 */ 13464 "bScrollOversize": false, 13465 13466 /** 13467 * Determine if the vertical scrollbar is on the right or left of the 13468 * scrolling container - needed for rtl language layout, although not 13469 * all browsers move the scrollbar (Safari). 13470 * @type boolean 13471 * @default false 13472 */ 13473 "bScrollbarLeft": false, 13474 13475 /** 13476 * Flag for if `getBoundingClientRect` is fully supported or not 13477 * @type boolean 13478 * @default false 13479 */ 13480 "bBounding": false, 13481 13482 /** 13483 * Browser scrollbar width 13484 * @type integer 13485 * @default 0 13486 */ 13487 "barWidth": 0 13488 }, 13489 13490 13491 "ajax": null, 13492 13493 13494 /** 13495 * Array referencing the nodes which are used for the features. The 13496 * parameters of this object match what is allowed by sDom - i.e. 13497 * <ul> 13498 * <li>'l' - Length changing</li> 13499 * <li>'f' - Filtering input</li> 13500 * <li>'t' - The table!</li> 13501 * <li>'i' - Information</li> 13502 * <li>'p' - Pagination</li> 13503 * <li>'r' - pRocessing</li> 13504 * </ul> 13505 * @type array 13506 * @default [] 13507 */ 13508 "aanFeatures": [], 13509 13510 /** 13511 * Store data information - see {@link DataTable.models.oRow} for detailed 13512 * information. 13513 * @type array 13514 * @default [] 13515 */ 13516 "aoData": [], 13517 13518 /** 13519 * Array of indexes which are in the current display (after filtering etc) 13520 * @type array 13521 * @default [] 13522 */ 13523 "aiDisplay": [], 13524 13525 /** 13526 * Array of indexes for display - no filtering 13527 * @type array 13528 * @default [] 13529 */ 13530 "aiDisplayMaster": [], 13531 13532 /** 13533 * Map of row ids to data indexes 13534 * @type object 13535 * @default {} 13536 */ 13537 "aIds": {}, 13538 13539 /** 13540 * Store information about each column that is in use 13541 * @type array 13542 * @default [] 13543 */ 13544 "aoColumns": [], 13545 13546 /** 13547 * Store information about the table's header 13548 * @type array 13549 * @default [] 13550 */ 13551 "aoHeader": [], 13552 13553 /** 13554 * Store information about the table's footer 13555 * @type array 13556 * @default [] 13557 */ 13558 "aoFooter": [], 13559 13560 /** 13561 * Store the applied global search information in case we want to force a 13562 * research or compare the old search to a new one. 13563 * Note that this parameter will be set by the initialisation routine. To 13564 * set a default use {@link DataTable.defaults}. 13565 * @namespace 13566 * @extends DataTable.models.oSearch 13567 */ 13568 "oPreviousSearch": {}, 13569 13570 /** 13571 * Store the applied search for each column - see 13572 * {@link DataTable.models.oSearch} for the format that is used for the 13573 * filtering information for each column. 13574 * @type array 13575 * @default [] 13576 */ 13577 "aoPreSearchCols": [], 13578 13579 /** 13580 * Sorting that is applied to the table. Note that the inner arrays are 13581 * used in the following manner: 13582 * <ul> 13583 * <li>Index 0 - column number</li> 13584 * <li>Index 1 - current sorting direction</li> 13585 * </ul> 13586 * Note that this parameter will be set by the initialisation routine. To 13587 * set a default use {@link DataTable.defaults}. 13588 * @type array 13589 * @todo These inner arrays should really be objects 13590 */ 13591 "aaSorting": null, 13592 13593 /** 13594 * Sorting that is always applied to the table (i.e. prefixed in front of 13595 * aaSorting). 13596 * Note that this parameter will be set by the initialisation routine. To 13597 * set a default use {@link DataTable.defaults}. 13598 * @type array 13599 * @default [] 13600 */ 13601 "aaSortingFixed": [], 13602 13603 /** 13604 * Classes to use for the striping of a table. 13605 * Note that this parameter will be set by the initialisation routine. To 13606 * set a default use {@link DataTable.defaults}. 13607 * @type array 13608 * @default [] 13609 */ 13610 "asStripeClasses": null, 13611 13612 /** 13613 * If restoring a table - we should restore its striping classes as well 13614 * @type array 13615 * @default [] 13616 */ 13617 "asDestroyStripes": [], 13618 13619 /** 13620 * If restoring a table - we should restore its width 13621 * @type int 13622 * @default 0 13623 */ 13624 "sDestroyWidth": 0, 13625 13626 /** 13627 * Callback functions array for every time a row is inserted (i.e. on a draw). 13628 * @type array 13629 * @default [] 13630 */ 13631 "aoRowCallback": [], 13632 13633 /** 13634 * Callback functions for the header on each draw. 13635 * @type array 13636 * @default [] 13637 */ 13638 "aoHeaderCallback": [], 13639 13640 /** 13641 * Callback function for the footer on each draw. 13642 * @type array 13643 * @default [] 13644 */ 13645 "aoFooterCallback": [], 13646 13647 /** 13648 * Array of callback functions for draw callback functions 13649 * @type array 13650 * @default [] 13651 */ 13652 "aoDrawCallback": [], 13653 13654 /** 13655 * Array of callback functions for row created function 13656 * @type array 13657 * @default [] 13658 */ 13659 "aoRowCreatedCallback": [], 13660 13661 /** 13662 * Callback functions for just before the table is redrawn. A return of 13663 * false will be used to cancel the draw. 13664 * @type array 13665 * @default [] 13666 */ 13667 "aoPreDrawCallback": [], 13668 13669 /** 13670 * Callback functions for when the table has been initialised. 13671 * @type array 13672 * @default [] 13673 */ 13674 "aoInitComplete": [], 13675 13676 13677 /** 13678 * Callbacks for modifying the settings to be stored for state saving, prior to 13679 * saving state. 13680 * @type array 13681 * @default [] 13682 */ 13683 "aoStateSaveParams": [], 13684 13685 /** 13686 * Callbacks for modifying the settings that have been stored for state saving 13687 * prior to using the stored values to restore the state. 13688 * @type array 13689 * @default [] 13690 */ 13691 "aoStateLoadParams": [], 13692 13693 /** 13694 * Callbacks for operating on the settings object once the saved state has been 13695 * loaded 13696 * @type array 13697 * @default [] 13698 */ 13699 "aoStateLoaded": [], 13700 13701 /** 13702 * Cache the table ID for quick access 13703 * @type string 13704 * @default <i>Empty string</i> 13705 */ 13706 "sTableId": "", 13707 13708 /** 13709 * The TABLE node for the main table 13710 * @type node 13711 * @default null 13712 */ 13713 "nTable": null, 13714 13715 /** 13716 * Permanent ref to the thead element 13717 * @type node 13718 * @default null 13719 */ 13720 "nTHead": null, 13721 13722 /** 13723 * Permanent ref to the tfoot element - if it exists 13724 * @type node 13725 * @default null 13726 */ 13727 "nTFoot": null, 13728 13729 /** 13730 * Permanent ref to the tbody element 13731 * @type node 13732 * @default null 13733 */ 13734 "nTBody": null, 13735 13736 /** 13737 * Cache the wrapper node (contains all DataTables controlled elements) 13738 * @type node 13739 * @default null 13740 */ 13741 "nTableWrapper": null, 13742 13743 /** 13744 * Indicate if when using server-side processing the loading of data 13745 * should be deferred until the second draw. 13746 * Note that this parameter will be set by the initialisation routine. To 13747 * set a default use {@link DataTable.defaults}. 13748 * @type boolean 13749 * @default false 13750 */ 13751 "bDeferLoading": false, 13752 13753 /** 13754 * Indicate if all required information has been read in 13755 * @type boolean 13756 * @default false 13757 */ 13758 "bInitialised": false, 13759 13760 /** 13761 * Information about open rows. Each object in the array has the parameters 13762 * 'nTr' and 'nParent' 13763 * @type array 13764 * @default [] 13765 */ 13766 "aoOpenRows": [], 13767 13768 /** 13769 * Dictate the positioning of DataTables' control elements - see 13770 * {@link DataTable.model.oInit.sDom}. 13771 * Note that this parameter will be set by the initialisation routine. To 13772 * set a default use {@link DataTable.defaults}. 13773 * @type string 13774 * @default null 13775 */ 13776 "sDom": null, 13777 13778 /** 13779 * Search delay (in mS) 13780 * @type integer 13781 * @default null 13782 */ 13783 "searchDelay": null, 13784 13785 /** 13786 * Which type of pagination should be used. 13787 * Note that this parameter will be set by the initialisation routine. To 13788 * set a default use {@link DataTable.defaults}. 13789 * @type string 13790 * @default two_button 13791 */ 13792 "sPaginationType": "two_button", 13793 13794 /** 13795 * The state duration (for `stateSave`) in seconds. 13796 * Note that this parameter will be set by the initialisation routine. To 13797 * set a default use {@link DataTable.defaults}. 13798 * @type int 13799 * @default 0 13800 */ 13801 "iStateDuration": 0, 13802 13803 /** 13804 * Array of callback functions for state saving. Each array element is an 13805 * object with the following parameters: 13806 * <ul> 13807 * <li>function:fn - function to call. Takes two parameters, oSettings 13808 * and the JSON string to save that has been thus far created. Returns 13809 * a JSON string to be inserted into a json object 13810 * (i.e. '"param": [ 0, 1, 2]')</li> 13811 * <li>string:sName - name of callback</li> 13812 * </ul> 13813 * @type array 13814 * @default [] 13815 */ 13816 "aoStateSave": [], 13817 13818 /** 13819 * Array of callback functions for state loading. Each array element is an 13820 * object with the following parameters: 13821 * <ul> 13822 * <li>function:fn - function to call. Takes two parameters, oSettings 13823 * and the object stored. May return false to cancel state loading</li> 13824 * <li>string:sName - name of callback</li> 13825 * </ul> 13826 * @type array 13827 * @default [] 13828 */ 13829 "aoStateLoad": [], 13830 13831 /** 13832 * State that was saved. Useful for back reference 13833 * @type object 13834 * @default null 13835 */ 13836 "oSavedState": null, 13837 13838 /** 13839 * State that was loaded. Useful for back reference 13840 * @type object 13841 * @default null 13842 */ 13843 "oLoadedState": null, 13844 13845 /** 13846 * Source url for AJAX data for the table. 13847 * Note that this parameter will be set by the initialisation routine. To 13848 * set a default use {@link DataTable.defaults}. 13849 * @type string 13850 * @default null 13851 */ 13852 "sAjaxSource": null, 13853 13854 /** 13855 * Property from a given object from which to read the table data from. This 13856 * can be an empty string (when not server-side processing), in which case 13857 * it is assumed an an array is given directly. 13858 * Note that this parameter will be set by the initialisation routine. To 13859 * set a default use {@link DataTable.defaults}. 13860 * @type string 13861 */ 13862 "sAjaxDataProp": null, 13863 13864 /** 13865 * The last jQuery XHR object that was used for server-side data gathering. 13866 * This can be used for working with the XHR information in one of the 13867 * callbacks 13868 * @type object 13869 * @default null 13870 */ 13871 "jqXHR": null, 13872 13873 /** 13874 * JSON returned from the server in the last Ajax request 13875 * @type object 13876 * @default undefined 13877 */ 13878 "json": undefined, 13879 13880 /** 13881 * Data submitted as part of the last Ajax request 13882 * @type object 13883 * @default undefined 13884 */ 13885 "oAjaxData": undefined, 13886 13887 /** 13888 * Function to get the server-side data. 13889 * Note that this parameter will be set by the initialisation routine. To 13890 * set a default use {@link DataTable.defaults}. 13891 * @type function 13892 */ 13893 "fnServerData": null, 13894 13895 /** 13896 * Functions which are called prior to sending an Ajax request so extra 13897 * parameters can easily be sent to the server 13898 * @type array 13899 * @default [] 13900 */ 13901 "aoServerParams": [], 13902 13903 /** 13904 * Send the XHR HTTP method - GET or POST (could be PUT or DELETE if 13905 * required). 13906 * Note that this parameter will be set by the initialisation routine. To 13907 * set a default use {@link DataTable.defaults}. 13908 * @type string 13909 */ 13910 "sServerMethod": null, 13911 13912 /** 13913 * Format numbers for display. 13914 * Note that this parameter will be set by the initialisation routine. To 13915 * set a default use {@link DataTable.defaults}. 13916 * @type function 13917 */ 13918 "fnFormatNumber": null, 13919 13920 /** 13921 * List of options that can be used for the user selectable length menu. 13922 * Note that this parameter will be set by the initialisation routine. To 13923 * set a default use {@link DataTable.defaults}. 13924 * @type array 13925 * @default [] 13926 */ 13927 "aLengthMenu": null, 13928 13929 /** 13930 * Counter for the draws that the table does. Also used as a tracker for 13931 * server-side processing 13932 * @type int 13933 * @default 0 13934 */ 13935 "iDraw": 0, 13936 13937 /** 13938 * Indicate if a redraw is being done - useful for Ajax 13939 * @type boolean 13940 * @default false 13941 */ 13942 "bDrawing": false, 13943 13944 /** 13945 * Draw index (iDraw) of the last error when parsing the returned data 13946 * @type int 13947 * @default -1 13948 */ 13949 "iDrawError": -1, 13950 13951 /** 13952 * Paging display length 13953 * @type int 13954 * @default 10 13955 */ 13956 "_iDisplayLength": 10, 13957 13958 /** 13959 * Paging start point - aiDisplay index 13960 * @type int 13961 * @default 0 13962 */ 13963 "_iDisplayStart": 0, 13964 13965 /** 13966 * Server-side processing - number of records in the result set 13967 * (i.e. before filtering), Use fnRecordsTotal rather than 13968 * this property to get the value of the number of records, regardless of 13969 * the server-side processing setting. 13970 * @type int 13971 * @default 0 13972 * @private 13973 */ 13974 "_iRecordsTotal": 0, 13975 13976 /** 13977 * Server-side processing - number of records in the current display set 13978 * (i.e. after filtering). Use fnRecordsDisplay rather than 13979 * this property to get the value of the number of records, regardless of 13980 * the server-side processing setting. 13981 * @type boolean 13982 * @default 0 13983 * @private 13984 */ 13985 "_iRecordsDisplay": 0, 13986 13987 /** 13988 * The classes to use for the table 13989 * @type object 13990 * @default {} 13991 */ 13992 "oClasses": {}, 13993 13994 /** 13995 * Flag attached to the settings object so you can check in the draw 13996 * callback if filtering has been done in the draw. Deprecated in favour of 13997 * events. 13998 * @type boolean 13999 * @default false 14000 * @deprecated 14001 */ 14002 "bFiltered": false, 14003 14004 /** 14005 * Flag attached to the settings object so you can check in the draw 14006 * callback if sorting has been done in the draw. Deprecated in favour of 14007 * events. 14008 * @type boolean 14009 * @default false 14010 * @deprecated 14011 */ 14012 "bSorted": false, 14013 14014 /** 14015 * Indicate that if multiple rows are in the header and there is more than 14016 * one unique cell per column, if the top one (true) or bottom one (false) 14017 * should be used for sorting / title by DataTables. 14018 * Note that this parameter will be set by the initialisation routine. To 14019 * set a default use {@link DataTable.defaults}. 14020 * @type boolean 14021 */ 14022 "bSortCellsTop": null, 14023 14024 /** 14025 * Initialisation object that is used for the table 14026 * @type object 14027 * @default null 14028 */ 14029 "oInit": null, 14030 14031 /** 14032 * Destroy callback functions - for plug-ins to attach themselves to the 14033 * destroy so they can clean up markup and events. 14034 * @type array 14035 * @default [] 14036 */ 14037 "aoDestroyCallback": [], 14038 14039 14040 /** 14041 * Get the number of records in the current record set, before filtering 14042 * @type function 14043 */ 14044 "fnRecordsTotal": function () 14045 { 14046 return _fnDataSource( this ) == 'ssp' ? 14047 this._iRecordsTotal * 1 : 14048 this.aiDisplayMaster.length; 14049 }, 14050 14051 /** 14052 * Get the number of records in the current record set, after filtering 14053 * @type function 14054 */ 14055 "fnRecordsDisplay": function () 14056 { 14057 return _fnDataSource( this ) == 'ssp' ? 14058 this._iRecordsDisplay * 1 : 14059 this.aiDisplay.length; 14060 }, 14061 14062 /** 14063 * Get the display end point - aiDisplay index 14064 * @type function 14065 */ 14066 "fnDisplayEnd": function () 14067 { 14068 var 14069 len = this._iDisplayLength, 14070 start = this._iDisplayStart, 14071 calc = start + len, 14072 records = this.aiDisplay.length, 14073 features = this.oFeatures, 14074 paginate = features.bPaginate; 14075 14076 if ( features.bServerSide ) { 14077 return paginate === false || len === -1 ? 14078 start + records : 14079 Math.min( start+len, this._iRecordsDisplay ); 14080 } 14081 else { 14082 return ! paginate || calc>records || len===-1 ? 14083 records : 14084 calc; 14085 } 14086 }, 14087 14088 /** 14089 * The DataTables object for this table 14090 * @type object 14091 * @default null 14092 */ 14093 "oInstance": null, 14094 14095 /** 14096 * Unique identifier for each instance of the DataTables object. If there 14097 * is an ID on the table node, then it takes that value, otherwise an 14098 * incrementing internal counter is used. 14099 * @type string 14100 * @default null 14101 */ 14102 "sInstance": null, 14103 14104 /** 14105 * tabindex attribute value that is added to DataTables control elements, allowing 14106 * keyboard navigation of the table and its controls. 14107 */ 14108 "iTabIndex": 0, 14109 14110 /** 14111 * DIV container for the footer scrolling table if scrolling 14112 */ 14113 "nScrollHead": null, 14114 14115 /** 14116 * DIV container for the footer scrolling table if scrolling 14117 */ 14118 "nScrollFoot": null, 14119 14120 /** 14121 * Last applied sort 14122 * @type array 14123 * @default [] 14124 */ 14125 "aLastSort": [], 14126 14127 /** 14128 * Stored plug-in instances 14129 * @type object 14130 * @default {} 14131 */ 14132 "oPlugins": {}, 14133 14134 /** 14135 * Function used to get a row's id from the row's data 14136 * @type function 14137 * @default null 14138 */ 14139 "rowIdFn": null, 14140 14141 /** 14142 * Data location where to store a row's id 14143 * @type string 14144 * @default null 14145 */ 14146 "rowId": null 14147 }; 14148 14149 /** 14150 * Extension object for DataTables that is used to provide all extension 14151 * options. 14152 * 14153 * Note that the `DataTable.ext` object is available through 14154 * `jQuery.fn.dataTable.ext` where it may be accessed and manipulated. It is 14155 * also aliased to `jQuery.fn.dataTableExt` for historic reasons. 14156 * @namespace 14157 * @extends DataTable.models.ext 14158 */ 14159 14160 14161 /** 14162 * DataTables extensions 14163 * 14164 * This namespace acts as a collection area for plug-ins that can be used to 14165 * extend DataTables capabilities. Indeed many of the build in methods 14166 * use this method to provide their own capabilities (sorting methods for 14167 * example). 14168 * 14169 * Note that this namespace is aliased to `jQuery.fn.dataTableExt` for legacy 14170 * reasons 14171 * 14172 * @namespace 14173 */ 14174 DataTable.ext = _ext = { 14175 /** 14176 * Buttons. For use with the Buttons extension for DataTables. This is 14177 * defined here so other extensions can define buttons regardless of load 14178 * order. It is _not_ used by DataTables core. 14179 * 14180 * @type object 14181 * @default {} 14182 */ 14183 buttons: {}, 14184 14185 14186 /** 14187 * Element class names 14188 * 14189 * @type object 14190 * @default {} 14191 */ 14192 classes: {}, 14193 14194 14195 /** 14196 * DataTables build type (expanded by the download builder) 14197 * 14198 * @type string 14199 */ 14200 builder: "-source-", 14201 14202 14203 /** 14204 * Error reporting. 14205 * 14206 * How should DataTables report an error. Can take the value 'alert', 14207 * 'throw', 'none' or a function. 14208 * 14209 * @type string|function 14210 * @default alert 14211 */ 14212 errMode: "alert", 14213 14214 14215 /** 14216 * Feature plug-ins. 14217 * 14218 * This is an array of objects which describe the feature plug-ins that are 14219 * available to DataTables. These feature plug-ins are then available for 14220 * use through the `dom` initialisation option. 14221 * 14222 * Each feature plug-in is described by an object which must have the 14223 * following properties: 14224 * 14225 * * `fnInit` - function that is used to initialise the plug-in, 14226 * * `cFeature` - a character so the feature can be enabled by the `dom` 14227 * instillation option. This is case sensitive. 14228 * 14229 * The `fnInit` function has the following input parameters: 14230 * 14231 * 1. `{object}` DataTables settings object: see 14232 * {@link DataTable.models.oSettings} 14233 * 14234 * And the following return is expected: 14235 * 14236 * * {node|null} The element which contains your feature. Note that the 14237 * return may also be void if your plug-in does not require to inject any 14238 * DOM elements into DataTables control (`dom`) - for example this might 14239 * be useful when developing a plug-in which allows table control via 14240 * keyboard entry 14241 * 14242 * @type array 14243 * 14244 * @example 14245 * $.fn.dataTable.ext.features.push( { 14246 * "fnInit": function( oSettings ) { 14247 * return new TableTools( { "oDTSettings": oSettings } ); 14248 * }, 14249 * "cFeature": "T" 14250 * } ); 14251 */ 14252 feature: [], 14253 14254 14255 /** 14256 * Row searching. 14257 * 14258 * This method of searching is complimentary to the default type based 14259 * searching, and a lot more comprehensive as it allows you complete control 14260 * over the searching logic. Each element in this array is a function 14261 * (parameters described below) that is called for every row in the table, 14262 * and your logic decides if it should be included in the searching data set 14263 * or not. 14264 * 14265 * Searching functions have the following input parameters: 14266 * 14267 * 1. `{object}` DataTables settings object: see 14268 * {@link DataTable.models.oSettings} 14269 * 2. `{array|object}` Data for the row to be processed (same as the 14270 * original format that was passed in as the data source, or an array 14271 * from a DOM data source 14272 * 3. `{int}` Row index ({@link DataTable.models.oSettings.aoData}), which 14273 * can be useful to retrieve the `TR` element if you need DOM interaction. 14274 * 14275 * And the following return is expected: 14276 * 14277 * * {boolean} Include the row in the searched result set (true) or not 14278 * (false) 14279 * 14280 * Note that as with the main search ability in DataTables, technically this 14281 * is "filtering", since it is subtractive. However, for consistency in 14282 * naming we call it searching here. 14283 * 14284 * @type array 14285 * @default [] 14286 * 14287 * @example 14288 * // The following example shows custom search being applied to the 14289 * // fourth column (i.e. the data[3] index) based on two input values 14290 * // from the end-user, matching the data in a certain range. 14291 * $.fn.dataTable.ext.search.push( 14292 * function( settings, data, dataIndex ) { 14293 * var min = document.getElementById('min').value * 1; 14294 * var max = document.getElementById('max').value * 1; 14295 * var version = data[3] == "-" ? 0 : data[3]*1; 14296 * 14297 * if ( min == "" && max == "" ) { 14298 * return true; 14299 * } 14300 * else if ( min == "" && version < max ) { 14301 * return true; 14302 * } 14303 * else if ( min < version && "" == max ) { 14304 * return true; 14305 * } 14306 * else if ( min < version && version < max ) { 14307 * return true; 14308 * } 14309 * return false; 14310 * } 14311 * ); 14312 */ 14313 search: [], 14314 14315 14316 /** 14317 * Selector extensions 14318 * 14319 * The `selector` option can be used to extend the options available for the 14320 * selector modifier options (`selector-modifier` object data type) that 14321 * each of the three built in selector types offer (row, column and cell + 14322 * their plural counterparts). For example the Select extension uses this 14323 * mechanism to provide an option to select only rows, columns and cells 14324 * that have been marked as selected by the end user (`{selected: true}`), 14325 * which can be used in conjunction with the existing built in selector 14326 * options. 14327 * 14328 * Each property is an array to which functions can be pushed. The functions 14329 * take three attributes: 14330 * 14331 * * Settings object for the host table 14332 * * Options object (`selector-modifier` object type) 14333 * * Array of selected item indexes 14334 * 14335 * The return is an array of the resulting item indexes after the custom 14336 * selector has been applied. 14337 * 14338 * @type object 14339 */ 14340 selector: { 14341 cell: [], 14342 column: [], 14343 row: [] 14344 }, 14345 14346 14347 /** 14348 * Internal functions, exposed for used in plug-ins. 14349 * 14350 * Please note that you should not need to use the internal methods for 14351 * anything other than a plug-in (and even then, try to avoid if possible). 14352 * The internal function may change between releases. 14353 * 14354 * @type object 14355 * @default {} 14356 */ 14357 internal: {}, 14358 14359 14360 /** 14361 * Legacy configuration options. Enable and disable legacy options that 14362 * are available in DataTables. 14363 * 14364 * @type object 14365 */ 14366 legacy: { 14367 /** 14368 * Enable / disable DataTables 1.9 compatible server-side processing 14369 * requests 14370 * 14371 * @type boolean 14372 * @default null 14373 */ 14374 ajax: null 14375 }, 14376 14377 14378 /** 14379 * Pagination plug-in methods. 14380 * 14381 * Each entry in this object is a function and defines which buttons should 14382 * be shown by the pagination rendering method that is used for the table: 14383 * {@link DataTable.ext.renderer.pageButton}. The renderer addresses how the 14384 * buttons are displayed in the document, while the functions here tell it 14385 * what buttons to display. This is done by returning an array of button 14386 * descriptions (what each button will do). 14387 * 14388 * Pagination types (the four built in options and any additional plug-in 14389 * options defined here) can be used through the `paginationType` 14390 * initialisation parameter. 14391 * 14392 * The functions defined take two parameters: 14393 * 14394 * 1. `{int} page` The current page index 14395 * 2. `{int} pages` The number of pages in the table 14396 * 14397 * Each function is expected to return an array where each element of the 14398 * array can be one of: 14399 * 14400 * * `first` - Jump to first page when activated 14401 * * `last` - Jump to last page when activated 14402 * * `previous` - Show previous page when activated 14403 * * `next` - Show next page when activated 14404 * * `{int}` - Show page of the index given 14405 * * `{array}` - A nested array containing the above elements to add a 14406 * containing 'DIV' element (might be useful for styling). 14407 * 14408 * Note that DataTables v1.9- used this object slightly differently whereby 14409 * an object with two functions would be defined for each plug-in. That 14410 * ability is still supported by DataTables 1.10+ to provide backwards 14411 * compatibility, but this option of use is now decremented and no longer 14412 * documented in DataTables 1.10+. 14413 * 14414 * @type object 14415 * @default {} 14416 * 14417 * @example 14418 * // Show previous, next and current page buttons only 14419 * $.fn.dataTableExt.oPagination.current = function ( page, pages ) { 14420 * return [ 'previous', page, 'next' ]; 14421 * }; 14422 */ 14423 pager: {}, 14424 14425 14426 renderer: { 14427 pageButton: {}, 14428 header: {} 14429 }, 14430 14431 14432 /** 14433 * Ordering plug-ins - custom data source 14434 * 14435 * The extension options for ordering of data available here is complimentary 14436 * to the default type based ordering that DataTables typically uses. It 14437 * allows much greater control over the the data that is being used to 14438 * order a column, but is necessarily therefore more complex. 14439 * 14440 * This type of ordering is useful if you want to do ordering based on data 14441 * live from the DOM (for example the contents of an 'input' element) rather 14442 * than just the static string that DataTables knows of. 14443 * 14444 * The way these plug-ins work is that you create an array of the values you 14445 * wish to be ordering for the column in question and then return that 14446 * array. The data in the array much be in the index order of the rows in 14447 * the table (not the currently ordering order!). Which order data gathering 14448 * function is run here depends on the `dt-init columns.orderDataType` 14449 * parameter that is used for the column (if any). 14450 * 14451 * The functions defined take two parameters: 14452 * 14453 * 1. `{object}` DataTables settings object: see 14454 * {@link DataTable.models.oSettings} 14455 * 2. `{int}` Target column index 14456 * 14457 * Each function is expected to return an array: 14458 * 14459 * * `{array}` Data for the column to be ordering upon 14460 * 14461 * @type array 14462 * 14463 * @example 14464 * // Ordering using `input` node values 14465 * $.fn.dataTable.ext.order['dom-text'] = function ( settings, col ) 14466 * { 14467 * return this.api().column( col, {order:'index'} ).nodes().map( function ( td, i ) { 14468 * return $('input', td).val(); 14469 * } ); 14470 * } 14471 */ 14472 order: {}, 14473 14474 14475 /** 14476 * Type based plug-ins. 14477 * 14478 * Each column in DataTables has a type assigned to it, either by automatic 14479 * detection or by direct assignment using the `type` option for the column. 14480 * The type of a column will effect how it is ordering and search (plug-ins 14481 * can also make use of the column type if required). 14482 * 14483 * @namespace 14484 */ 14485 type: { 14486 /** 14487 * Type detection functions. 14488 * 14489 * The functions defined in this object are used to automatically detect 14490 * a column's type, making initialisation of DataTables super easy, even 14491 * when complex data is in the table. 14492 * 14493 * The functions defined take two parameters: 14494 * 14495 * 1. `{*}` Data from the column cell to be analysed 14496 * 2. `{settings}` DataTables settings object. This can be used to 14497 * perform context specific type detection - for example detection 14498 * based on language settings such as using a comma for a decimal 14499 * place. Generally speaking the options from the settings will not 14500 * be required 14501 * 14502 * Each function is expected to return: 14503 * 14504 * * `{string|null}` Data type detected, or null if unknown (and thus 14505 * pass it on to the other type detection functions. 14506 * 14507 * @type array 14508 * 14509 * @example 14510 * // Currency type detection plug-in: 14511 * $.fn.dataTable.ext.type.detect.push( 14512 * function ( data, settings ) { 14513 * // Check the numeric part 14514 * if ( ! data.substring(1).match(/[0-9]/) ) { 14515 * return null; 14516 * } 14517 * 14518 * // Check prefixed by currency 14519 * if ( data.charAt(0) == '$' || data.charAt(0) == '£' ) { 14520 * return 'currency'; 14521 * } 14522 * return null; 14523 * } 14524 * ); 14525 */ 14526 detect: [], 14527 14528 14529 /** 14530 * Type based search formatting. 14531 * 14532 * The type based searching functions can be used to pre-format the 14533 * data to be search on. For example, it can be used to strip HTML 14534 * tags or to de-format telephone numbers for numeric only searching. 14535 * 14536 * Note that is a search is not defined for a column of a given type, 14537 * no search formatting will be performed. 14538 * 14539 * Pre-processing of searching data plug-ins - When you assign the sType 14540 * for a column (or have it automatically detected for you by DataTables 14541 * or a type detection plug-in), you will typically be using this for 14542 * custom sorting, but it can also be used to provide custom searching 14543 * by allowing you to pre-processing the data and returning the data in 14544 * the format that should be searched upon. This is done by adding 14545 * functions this object with a parameter name which matches the sType 14546 * for that target column. This is the corollary of <i>afnSortData</i> 14547 * for searching data. 14548 * 14549 * The functions defined take a single parameter: 14550 * 14551 * 1. `{*}` Data from the column cell to be prepared for searching 14552 * 14553 * Each function is expected to return: 14554 * 14555 * * `{string|null}` Formatted string that will be used for the searching. 14556 * 14557 * @type object 14558 * @default {} 14559 * 14560 * @example 14561 * $.fn.dataTable.ext.type.search['title-numeric'] = function ( d ) { 14562 * return d.replace(/\n/g," ").replace( /<.*?>/g, "" ); 14563 * } 14564 */ 14565 search: {}, 14566 14567 14568 /** 14569 * Type based ordering. 14570 * 14571 * The column type tells DataTables what ordering to apply to the table 14572 * when a column is sorted upon. The order for each type that is defined, 14573 * is defined by the functions available in this object. 14574 * 14575 * Each ordering option can be described by three properties added to 14576 * this object: 14577 * 14578 * * `{type}-pre` - Pre-formatting function 14579 * * `{type}-asc` - Ascending order function 14580 * * `{type}-desc` - Descending order function 14581 * 14582 * All three can be used together, only `{type}-pre` or only 14583 * `{type}-asc` and `{type}-desc` together. It is generally recommended 14584 * that only `{type}-pre` is used, as this provides the optimal 14585 * implementation in terms of speed, although the others are provided 14586 * for compatibility with existing Javascript sort functions. 14587 * 14588 * `{type}-pre`: Functions defined take a single parameter: 14589 * 14590 * 1. `{*}` Data from the column cell to be prepared for ordering 14591 * 14592 * And return: 14593 * 14594 * * `{*}` Data to be sorted upon 14595 * 14596 * `{type}-asc` and `{type}-desc`: Functions are typical Javascript sort 14597 * functions, taking two parameters: 14598 * 14599 * 1. `{*}` Data to compare to the second parameter 14600 * 2. `{*}` Data to compare to the first parameter 14601 * 14602 * And returning: 14603 * 14604 * * `{*}` Ordering match: <0 if first parameter should be sorted lower 14605 * than the second parameter, ===0 if the two parameters are equal and 14606 * >0 if the first parameter should be sorted height than the second 14607 * parameter. 14608 * 14609 * @type object 14610 * @default {} 14611 * 14612 * @example 14613 * // Numeric ordering of formatted numbers with a pre-formatter 14614 * $.extend( $.fn.dataTable.ext.type.order, { 14615 * "string-pre": function(x) { 14616 * a = (a === "-" || a === "") ? 0 : a.replace( /[^\d\-\.]/g, "" ); 14617 * return parseFloat( a ); 14618 * } 14619 * } ); 14620 * 14621 * @example 14622 * // Case-sensitive string ordering, with no pre-formatting method 14623 * $.extend( $.fn.dataTable.ext.order, { 14624 * "string-case-asc": function(x,y) { 14625 * return ((x < y) ? -1 : ((x > y) ? 1 : 0)); 14626 * }, 14627 * "string-case-desc": function(x,y) { 14628 * return ((x < y) ? 1 : ((x > y) ? -1 : 0)); 14629 * } 14630 * } ); 14631 */ 14632 order: {} 14633 }, 14634 14635 /** 14636 * Unique DataTables instance counter 14637 * 14638 * @type int 14639 * @private 14640 */ 14641 _unique: 0, 14642 14643 14644 // 14645 // Depreciated 14646 // The following properties are retained for backwards compatibility only. 14647 // The should not be used in new projects and will be removed in a future 14648 // version 14649 // 14650 14651 /** 14652 * Version check function. 14653 * @type function 14654 * @depreciated Since 1.10 14655 */ 14656 fnVersionCheck: DataTable.fnVersionCheck, 14657 14658 14659 /** 14660 * Index for what 'this' index API functions should use 14661 * @type int 14662 * @deprecated Since v1.10 14663 */ 14664 iApiIndex: 0, 14665 14666 14667 /** 14668 * jQuery UI class container 14669 * @type object 14670 * @deprecated Since v1.10 14671 */ 14672 oJUIClasses: {}, 14673 14674 14675 /** 14676 * Software version 14677 * @type string 14678 * @deprecated Since v1.10 14679 */ 14680 sVersion: DataTable.version 14681 }; 14682 14683 14684 // 14685 // Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts 14686 // 14687 $.extend( _ext, { 14688 afnFiltering: _ext.search, 14689 aTypes: _ext.type.detect, 14690 ofnSearch: _ext.type.search, 14691 oSort: _ext.type.order, 14692 afnSortData: _ext.order, 14693 aoFeatures: _ext.feature, 14694 oApi: _ext.internal, 14695 oStdClasses: _ext.classes, 14696 oPagination: _ext.pager 14697 } ); 14698 14699 14700 $.extend( DataTable.ext.classes, { 14701 "sTable": "dataTable", 14702 "sNoFooter": "no-footer", 14703 14704 /* Paging buttons */ 14705 "sPageButton": "paginate_button", 14706 "sPageButtonActive": "current", 14707 "sPageButtonDisabled": "disabled", 14708 14709 /* Striping classes */ 14710 "sStripeOdd": "odd", 14711 "sStripeEven": "even", 14712 14713 /* Empty row */ 14714 "sRowEmpty": "dataTables_empty", 14715 14716 /* Features */ 14717 "sWrapper": "dataTables_wrapper", 14718 "sFilter": "dataTables_filter", 14719 "sInfo": "dataTables_info", 14720 "sPaging": "dataTables_paginate paging_", /* Note that the type is postfixed */ 14721 "sLength": "dataTables_length", 14722 "sProcessing": "dataTables_processing", 14723 14724 /* Sorting */ 14725 "sSortAsc": "sorting_asc", 14726 "sSortDesc": "sorting_desc", 14727 "sSortable": "sorting", /* Sortable in both directions */ 14728 "sSortableAsc": "sorting_desc_disabled", 14729 "sSortableDesc": "sorting_asc_disabled", 14730 "sSortableNone": "sorting_disabled", 14731 "sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */ 14732 14733 /* Filtering */ 14734 "sFilterInput": "", 14735 14736 /* Page length */ 14737 "sLengthSelect": "", 14738 14739 /* Scrolling */ 14740 "sScrollWrapper": "dataTables_scroll", 14741 "sScrollHead": "dataTables_scrollHead", 14742 "sScrollHeadInner": "dataTables_scrollHeadInner", 14743 "sScrollBody": "dataTables_scrollBody", 14744 "sScrollFoot": "dataTables_scrollFoot", 14745 "sScrollFootInner": "dataTables_scrollFootInner", 14746 14747 /* Misc */ 14748 "sHeaderTH": "", 14749 "sFooterTH": "", 14750 14751 // Deprecated 14752 "sSortJUIAsc": "", 14753 "sSortJUIDesc": "", 14754 "sSortJUI": "", 14755 "sSortJUIAscAllowed": "", 14756 "sSortJUIDescAllowed": "", 14757 "sSortJUIWrapper": "", 14758 "sSortIcon": "", 14759 "sJUIHeader": "", 14760 "sJUIFooter": "" 14761 } ); 14762 14763 14764 var extPagination = DataTable.ext.pager; 14765 14766 function _numbers ( page, pages ) { 14767 var 14768 numbers = [], 14769 buttons = extPagination.numbers_length, 14770 half = Math.floor( buttons / 2 ), 14771 i = 1; 14772 14773 if ( pages <= buttons ) { 14774 numbers = _range( 0, pages ); 14775 } 14776 else if ( page <= half ) { 14777 numbers = _range( 0, buttons-2 ); 14778 numbers.push( 'ellipsis' ); 14779 numbers.push( pages-1 ); 14780 } 14781 else if ( page >= pages - 1 - half ) { 14782 numbers = _range( pages-(buttons-2), pages ); 14783 numbers.splice( 0, 0, 'ellipsis' ); // no unshift in ie6 14784 numbers.splice( 0, 0, 0 ); 14785 } 14786 else { 14787 numbers = _range( page-half+2, page+half-1 ); 14788 numbers.push( 'ellipsis' ); 14789 numbers.push( pages-1 ); 14790 numbers.splice( 0, 0, 'ellipsis' ); 14791 numbers.splice( 0, 0, 0 ); 14792 } 14793 14794 numbers.DT_el = 'span'; 14795 return numbers; 14796 } 14797 14798 14799 $.extend( extPagination, { 14800 simple: function ( page, pages ) { 14801 return [ 'previous', 'next' ]; 14802 }, 14803 14804 full: function ( page, pages ) { 14805 return [ 'first', 'previous', 'next', 'last' ]; 14806 }, 14807 14808 numbers: function ( page, pages ) { 14809 return [ _numbers(page, pages) ]; 14810 }, 14811 14812 simple_numbers: function ( page, pages ) { 14813 return [ 'previous', _numbers(page, pages), 'next' ]; 14814 }, 14815 14816 full_numbers: function ( page, pages ) { 14817 return [ 'first', 'previous', _numbers(page, pages), 'next', 'last' ]; 14818 }, 14819 14820 first_last_numbers: function (page, pages) { 14821 return ['first', _numbers(page, pages), 'last']; 14822 }, 14823 14824 // For testing and plug-ins to use 14825 _numbers: _numbers, 14826 14827 // Number of number buttons (including ellipsis) to show. _Must be odd!_ 14828 numbers_length: 7 14829 } ); 14830 14831 14832 $.extend( true, DataTable.ext.renderer, { 14833 pageButton: { 14834 _: function ( settings, host, idx, buttons, page, pages ) { 14835 var classes = settings.oClasses; 14836 var lang = settings.oLanguage.oPaginate; 14837 var aria = settings.oLanguage.oAria.paginate || {}; 14838 var btnDisplay, btnClass; 14839 14840 var attach = function( container, buttons ) { 14841 var i, ien, node, button, tabIndex; 14842 var disabledClass = classes.sPageButtonDisabled; 14843 var clickHandler = function ( e ) { 14844 _fnPageChange( settings, e.data.action, true ); 14845 }; 14846 14847 for ( i=0, ien=buttons.length ; i<ien ; i++ ) { 14848 button = buttons[i]; 14849 14850 if ( Array.isArray( button ) ) { 14851 var inner = $( '<'+(button.DT_el || 'div')+'/>' ) 14852 .appendTo( container ); 14853 attach( inner, button ); 14854 } 14855 else { 14856 btnDisplay = null; 14857 btnClass = button; 14858 tabIndex = settings.iTabIndex; 14859 14860 switch ( button ) { 14861 case 'ellipsis': 14862 container.append('<span class="ellipsis">…</span>'); 14863 break; 14864 14865 case 'first': 14866 btnDisplay = lang.sFirst; 14867 14868 if ( page === 0 ) { 14869 tabIndex = -1; 14870 btnClass += ' ' + disabledClass; 14871 } 14872 break; 14873 14874 case 'previous': 14875 btnDisplay = lang.sPrevious; 14876 14877 if ( page === 0 ) { 14878 tabIndex = -1; 14879 btnClass += ' ' + disabledClass; 14880 } 14881 break; 14882 14883 case 'next': 14884 btnDisplay = lang.sNext; 14885 14886 if ( pages === 0 || page === pages-1 ) { 14887 tabIndex = -1; 14888 btnClass += ' ' + disabledClass; 14889 } 14890 break; 14891 14892 case 'last': 14893 btnDisplay = lang.sLast; 14894 14895 if ( pages === 0 || page === pages-1 ) { 14896 tabIndex = -1; 14897 btnClass += ' ' + disabledClass; 14898 } 14899 break; 14900 14901 default: 14902 btnDisplay = settings.fnFormatNumber( button + 1 ); 14903 btnClass = page === button ? 14904 classes.sPageButtonActive : ''; 14905 break; 14906 } 14907 14908 if ( btnDisplay !== null ) { 14909 var tag = settings.oInit.pagingTag || 'a'; 14910 var disabled = btnClass.indexOf(disabledClass) !== -1; 14911 14912 14913 node = $('<'+tag+'>', { 14914 'class': classes.sPageButton+' '+btnClass, 14915 'aria-controls': settings.sTableId, 14916 'aria-disabled': disabled ? 'true' : null, 14917 'aria-label': aria[ button ], 14918 'role': 'link', 14919 'aria-current': btnClass === classes.sPageButtonActive ? 'page' : null, 14920 'data-dt-idx': button, 14921 'tabindex': tabIndex, 14922 'id': idx === 0 && typeof button === 'string' ? 14923 settings.sTableId +'_'+ button : 14924 null 14925 } ) 14926 .html( btnDisplay ) 14927 .appendTo( container ); 14928 14929 _fnBindAction( 14930 node, {action: button}, clickHandler 14931 ); 14932 } 14933 } 14934 } 14935 }; 14936 14937 // IE9 throws an 'unknown error' if document.activeElement is used 14938 // inside an iframe or frame. Try / catch the error. Not good for 14939 // accessibility, but neither are frames. 14940 var activeEl; 14941 14942 try { 14943 // Because this approach is destroying and recreating the paging 14944 // elements, focus is lost on the select button which is bad for 14945 // accessibility. So we want to restore focus once the draw has 14946 // completed 14947 activeEl = $(host).find(document.activeElement).data('dt-idx'); 14948 } 14949 catch (e) {} 14950 14951 attach( $(host).empty(), buttons ); 14952 14953 if ( activeEl !== undefined ) { 14954 $(host).find( '[data-dt-idx='+activeEl+']' ).trigger('focus'); 14955 } 14956 } 14957 } 14958 } ); 14959 14960 14961 14962 // Built in type detection. See model.ext.aTypes for information about 14963 // what is required from this methods. 14964 $.extend( DataTable.ext.type.detect, [ 14965 // Plain numbers - first since V8 detects some plain numbers as dates 14966 // e.g. Date.parse('55') (but not all, e.g. Date.parse('22')...). 14967 function ( d, settings ) 14968 { 14969 var decimal = settings.oLanguage.sDecimal; 14970 return _isNumber( d, decimal ) ? 'num'+decimal : null; 14971 }, 14972 14973 // Dates (only those recognised by the browser's Date.parse) 14974 function ( d, settings ) 14975 { 14976 // V8 tries _very_ hard to make a string passed into `Date.parse()` 14977 // valid, so we need to use a regex to restrict date formats. Use a 14978 // plug-in for anything other than ISO8601 style strings 14979 if ( d && !(d instanceof Date) && ! _re_date.test(d) ) { 14980 return null; 14981 } 14982 var parsed = Date.parse(d); 14983 return (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null; 14984 }, 14985 14986 // Formatted numbers 14987 function ( d, settings ) 14988 { 14989 var decimal = settings.oLanguage.sDecimal; 14990 return _isNumber( d, decimal, true ) ? 'num-fmt'+decimal : null; 14991 }, 14992 14993 // HTML numeric 14994 function ( d, settings ) 14995 { 14996 var decimal = settings.oLanguage.sDecimal; 14997 return _htmlNumeric( d, decimal ) ? 'html-num'+decimal : null; 14998 }, 14999 15000 // HTML numeric, formatted 15001 function ( d, settings ) 15002 { 15003 var decimal = settings.oLanguage.sDecimal; 15004 return _htmlNumeric( d, decimal, true ) ? 'html-num-fmt'+decimal : null; 15005 }, 15006 15007 // HTML (this is strict checking - there must be html) 15008 function ( d, settings ) 15009 { 15010 return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1) ? 15011 'html' : null; 15012 } 15013 ] ); 15014 15015 15016 15017 // Filter formatting functions. See model.ext.ofnSearch for information about 15018 // what is required from these methods. 15019 // 15020 // Note that additional search methods are added for the html numbers and 15021 // html formatted numbers by `_addNumericSort()` when we know what the decimal 15022 // place is 15023 15024 15025 $.extend( DataTable.ext.type.search, { 15026 html: function ( data ) { 15027 return _empty(data) ? 15028 data : 15029 typeof data === 'string' ? 15030 data 15031 .replace( _re_new_lines, " " ) 15032 .replace( _re_html, "" ) : 15033 ''; 15034 }, 15035 15036 string: function ( data ) { 15037 return _empty(data) ? 15038 data : 15039 typeof data === 'string' ? 15040 data.replace( _re_new_lines, " " ) : 15041 data; 15042 } 15043 } ); 15044 15045 15046 15047 var __numericReplace = function ( d, decimalPlace, re1, re2 ) { 15048 if ( d !== 0 && (!d || d === '-') ) { 15049 return -Infinity; 15050 } 15051 15052 var type = typeof d; 15053 15054 if (type === 'number' || type === 'bigint') { 15055 return d; 15056 } 15057 15058 // If a decimal place other than `.` is used, it needs to be given to the 15059 // function so we can detect it and replace with a `.` which is the only 15060 // decimal place Javascript recognises - it is not locale aware. 15061 if ( decimalPlace ) { 15062 d = _numToDecimal( d, decimalPlace ); 15063 } 15064 15065 if ( d.replace ) { 15066 if ( re1 ) { 15067 d = d.replace( re1, '' ); 15068 } 15069 15070 if ( re2 ) { 15071 d = d.replace( re2, '' ); 15072 } 15073 } 15074 15075 return d * 1; 15076 }; 15077 15078 15079 // Add the numeric 'deformatting' functions for sorting and search. This is done 15080 // in a function to provide an easy ability for the language options to add 15081 // additional methods if a non-period decimal place is used. 15082 function _addNumericSort ( decimalPlace ) { 15083 $.each( 15084 { 15085 // Plain numbers 15086 "num": function ( d ) { 15087 return __numericReplace( d, decimalPlace ); 15088 }, 15089 15090 // Formatted numbers 15091 "num-fmt": function ( d ) { 15092 return __numericReplace( d, decimalPlace, _re_formatted_numeric ); 15093 }, 15094 15095 // HTML numeric 15096 "html-num": function ( d ) { 15097 return __numericReplace( d, decimalPlace, _re_html ); 15098 }, 15099 15100 // HTML numeric, formatted 15101 "html-num-fmt": function ( d ) { 15102 return __numericReplace( d, decimalPlace, _re_html, _re_formatted_numeric ); 15103 } 15104 }, 15105 function ( key, fn ) { 15106 // Add the ordering method 15107 _ext.type.order[ key+decimalPlace+'-pre' ] = fn; 15108 15109 // For HTML types add a search formatter that will strip the HTML 15110 if ( key.match(/^html\-/) ) { 15111 _ext.type.search[ key+decimalPlace ] = _ext.type.search.html; 15112 } 15113 } 15114 ); 15115 } 15116 15117 15118 // Default sort methods 15119 $.extend( _ext.type.order, { 15120 // Dates 15121 "date-pre": function ( d ) { 15122 var ts = Date.parse( d ); 15123 return isNaN(ts) ? -Infinity : ts; 15124 }, 15125 15126 // html 15127 "html-pre": function ( a ) { 15128 return _empty(a) ? 15129 '' : 15130 a.replace ? 15131 a.replace( /<.*?>/g, "" ).toLowerCase() : 15132 a+''; 15133 }, 15134 15135 // string 15136 "string-pre": function ( a ) { 15137 // This is a little complex, but faster than always calling toString, 15138 // http://jsperf.com/tostring-v-check 15139 return _empty(a) ? 15140 '' : 15141 typeof a === 'string' ? 15142 a.toLowerCase() : 15143 ! a.toString ? 15144 '' : 15145 a.toString(); 15146 }, 15147 15148 // string-asc and -desc are retained only for compatibility with the old 15149 // sort methods 15150 "string-asc": function ( x, y ) { 15151 return ((x < y) ? -1 : ((x > y) ? 1 : 0)); 15152 }, 15153 15154 "string-desc": function ( x, y ) { 15155 return ((x < y) ? 1 : ((x > y) ? -1 : 0)); 15156 } 15157 } ); 15158 15159 15160 // Numeric sorting types - order doesn't matter here 15161 _addNumericSort( '' ); 15162 15163 15164 $.extend( true, DataTable.ext.renderer, { 15165 header: { 15166 _: function ( settings, cell, column, classes ) { 15167 // No additional mark-up required 15168 // Attach a sort listener to update on sort - note that using the 15169 // `DT` namespace will allow the event to be removed automatically 15170 // on destroy, while the `dt` namespaced event is the one we are 15171 // listening for 15172 $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) { 15173 if ( settings !== ctx ) { // need to check this this is the host 15174 return; // table, not a nested one 15175 } 15176 15177 var colIdx = column.idx; 15178 15179 cell 15180 .removeClass( 15181 classes.sSortAsc +' '+ 15182 classes.sSortDesc 15183 ) 15184 .addClass( columns[ colIdx ] == 'asc' ? 15185 classes.sSortAsc : columns[ colIdx ] == 'desc' ? 15186 classes.sSortDesc : 15187 column.sSortingClass 15188 ); 15189 } ); 15190 }, 15191 15192 jqueryui: function ( settings, cell, column, classes ) { 15193 $('<div/>') 15194 .addClass( classes.sSortJUIWrapper ) 15195 .append( cell.contents() ) 15196 .append( $('<span/>') 15197 .addClass( classes.sSortIcon+' '+column.sSortingClassJUI ) 15198 ) 15199 .appendTo( cell ); 15200 15201 // Attach a sort listener to update on sort 15202 $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) { 15203 if ( settings !== ctx ) { 15204 return; 15205 } 15206 15207 var colIdx = column.idx; 15208 15209 cell 15210 .removeClass( classes.sSortAsc +" "+classes.sSortDesc ) 15211 .addClass( columns[ colIdx ] == 'asc' ? 15212 classes.sSortAsc : columns[ colIdx ] == 'desc' ? 15213 classes.sSortDesc : 15214 column.sSortingClass 15215 ); 15216 15217 cell 15218 .find( 'span.'+classes.sSortIcon ) 15219 .removeClass( 15220 classes.sSortJUIAsc +" "+ 15221 classes.sSortJUIDesc +" "+ 15222 classes.sSortJUI +" "+ 15223 classes.sSortJUIAscAllowed +" "+ 15224 classes.sSortJUIDescAllowed 15225 ) 15226 .addClass( columns[ colIdx ] == 'asc' ? 15227 classes.sSortJUIAsc : columns[ colIdx ] == 'desc' ? 15228 classes.sSortJUIDesc : 15229 column.sSortingClassJUI 15230 ); 15231 } ); 15232 } 15233 } 15234 } ); 15235 15236 /* 15237 * Public helper functions. These aren't used internally by DataTables, or 15238 * called by any of the options passed into DataTables, but they can be used 15239 * externally by developers working with DataTables. They are helper functions 15240 * to make working with DataTables a little bit easier. 15241 */ 15242 15243 var __htmlEscapeEntities = function ( d ) { 15244 if (Array.isArray(d)) { 15245 d = d.join(','); 15246 } 15247 15248 return typeof d === 'string' ? 15249 d 15250 .replace(/&/g, '&') 15251 .replace(/</g, '<') 15252 .replace(/>/g, '>') 15253 .replace(/"/g, '"') : 15254 d; 15255 }; 15256 15257 // Common logic for moment, luxon or a date action 15258 function __mld( dt, momentFn, luxonFn, dateFn, arg1 ) { 15259 if (window.moment) { 15260 return dt[momentFn]( arg1 ); 15261 } 15262 else if (window.luxon) { 15263 return dt[luxonFn]( arg1 ); 15264 } 15265 15266 return dateFn ? dt[dateFn]( arg1 ) : dt; 15267 } 15268 15269 15270 var __mlWarning = false; 15271 function __mldObj (d, format, locale) { 15272 var dt; 15273 15274 if (window.moment) { 15275 dt = window.moment.utc( d, format, locale, true ); 15276 15277 if (! dt.isValid()) { 15278 return null; 15279 } 15280 } 15281 else if (window.luxon) { 15282 dt = format && typeof d === 'string' 15283 ? window.luxon.DateTime.fromFormat( d, format ) 15284 : window.luxon.DateTime.fromISO( d ); 15285 15286 if (! dt.isValid) { 15287 return null; 15288 } 15289 15290 dt.setLocale(locale); 15291 } 15292 else if (! format) { 15293 // No format given, must be ISO 15294 dt = new Date(d); 15295 } 15296 else { 15297 if (! __mlWarning) { 15298 alert('DataTables warning: Formatted date without Moment.js or Luxon - https://datatables.net/tn/17'); 15299 } 15300 15301 __mlWarning = true; 15302 } 15303 15304 return dt; 15305 } 15306 15307 // Wrapper for date, datetime and time which all operate the same way with the exception of 15308 // the output string for auto locale support 15309 function __mlHelper (localeString) { 15310 return function ( from, to, locale, def ) { 15311 // Luxon and Moment support 15312 // Argument shifting 15313 if ( arguments.length === 0 ) { 15314 locale = 'en'; 15315 to = null; // means toLocaleString 15316 from = null; // means iso8601 15317 } 15318 else if ( arguments.length === 1 ) { 15319 locale = 'en'; 15320 to = from; 15321 from = null; 15322 } 15323 else if ( arguments.length === 2 ) { 15324 locale = to; 15325 to = from; 15326 from = null; 15327 } 15328 15329 var typeName = 'datetime-' + to; 15330 15331 // Add type detection and sorting specific to this date format - we need to be able to identify 15332 // date type columns as such, rather than as numbers in extensions. Hence the need for this. 15333 if (! DataTable.ext.type.order[typeName]) { 15334 // The renderer will give the value to type detect as the type! 15335 DataTable.ext.type.detect.unshift(function (d) { 15336 return d === typeName ? typeName : false; 15337 }); 15338 15339 // The renderer gives us Moment, Luxon or Date obects for the sorting, all of which have a 15340 // `valueOf` which gives milliseconds epoch 15341 DataTable.ext.type.order[typeName + '-asc'] = function (a, b) { 15342 var x = a.valueOf(); 15343 var y = b.valueOf(); 15344 15345 return x === y 15346 ? 0 15347 : x < y 15348 ? -1 15349 : 1; 15350 } 15351 15352 DataTable.ext.type.order[typeName + '-desc'] = function (a, b) { 15353 var x = a.valueOf(); 15354 var y = b.valueOf(); 15355 15356 return x === y 15357 ? 0 15358 : x > y 15359 ? -1 15360 : 1; 15361 } 15362 } 15363 15364 return function ( d, type ) { 15365 // Allow for a default value 15366 if (d === null || d === undefined) { 15367 if (def === '--now') { 15368 // We treat everything as UTC further down, so no changes are 15369 // made, as such need to get the local date / time as if it were 15370 // UTC 15371 var local = new Date(); 15372 d = new Date( Date.UTC( 15373 local.getFullYear(), local.getMonth(), local.getDate(), 15374 local.getHours(), local.getMinutes(), local.getSeconds() 15375 ) ); 15376 } 15377 else { 15378 d = ''; 15379 } 15380 } 15381 15382 if (type === 'type') { 15383 // Typing uses the type name for fast matching 15384 return typeName; 15385 } 15386 15387 if (d === '') { 15388 return type !== 'sort' 15389 ? '' 15390 : __mldObj('0000-01-01 00:00:00', null, locale); 15391 } 15392 15393 // Shortcut. If `from` and `to` are the same, we are using the renderer to 15394 // format for ordering, not display - its already in the display format. 15395 if ( to !== null && from === to && type !== 'sort' && type !== 'type' && ! (d instanceof Date) ) { 15396 return d; 15397 } 15398 15399 var dt = __mldObj(d, from, locale); 15400 15401 if (dt === null) { 15402 return d; 15403 } 15404 15405 if (type === 'sort') { 15406 return dt; 15407 } 15408 15409 var formatted = to === null 15410 ? __mld(dt, 'toDate', 'toJSDate', '')[localeString]() 15411 : __mld(dt, 'format', 'toFormat', 'toISOString', to); 15412 15413 // XSS protection 15414 return type === 'display' ? 15415 __htmlEscapeEntities( formatted ) : 15416 formatted; 15417 }; 15418 } 15419 } 15420 15421 // Based on locale, determine standard number formatting 15422 // Fallback for legacy browsers is US English 15423 var __thousands = ','; 15424 var __decimal = '.'; 15425 15426 if (window.Intl !== undefined) { 15427 try { 15428 var num = new Intl.NumberFormat().formatToParts(100000.1); 15429 15430 for (var i=0 ; i<num.length ; i++) { 15431 if (num[i].type === 'group') { 15432 __thousands = num[i].value; 15433 } 15434 else if (num[i].type === 'decimal') { 15435 __decimal = num[i].value; 15436 } 15437 } 15438 } 15439 catch (e) { 15440 // noop 15441 } 15442 } 15443 15444 // Formatted date time detection - use by declaring the formats you are going to use 15445 DataTable.datetime = function ( format, locale ) { 15446 var typeName = 'datetime-detect-' + format; 15447 15448 if (! locale) { 15449 locale = 'en'; 15450 } 15451 15452 if (! DataTable.ext.type.order[typeName]) { 15453 DataTable.ext.type.detect.unshift(function (d) { 15454 var dt = __mldObj(d, format, locale); 15455 return d === '' || dt ? typeName : false; 15456 }); 15457 15458 DataTable.ext.type.order[typeName + '-pre'] = function (d) { 15459 return __mldObj(d, format, locale) || 0; 15460 } 15461 } 15462 } 15463 15464 /** 15465 * Helpers for `columns.render`. 15466 * 15467 * The options defined here can be used with the `columns.render` initialisation 15468 * option to provide a display renderer. The following functions are defined: 15469 * 15470 * * `number` - Will format numeric data (defined by `columns.data`) for 15471 * display, retaining the original unformatted data for sorting and filtering. 15472 * It takes 5 parameters: 15473 * * `string` - Thousands grouping separator 15474 * * `string` - Decimal point indicator 15475 * * `integer` - Number of decimal points to show 15476 * * `string` (optional) - Prefix. 15477 * * `string` (optional) - Postfix (/suffix). 15478 * * `text` - Escape HTML to help prevent XSS attacks. It has no optional 15479 * parameters. 15480 * 15481 * @example 15482 * // Column definition using the number renderer 15483 * { 15484 * data: "salary", 15485 * render: $.fn.dataTable.render.number( '\'', '.', 0, '$' ) 15486 * } 15487 * 15488 * @namespace 15489 */ 15490 DataTable.render = { 15491 date: __mlHelper('toLocaleDateString'), 15492 datetime: __mlHelper('toLocaleString'), 15493 time: __mlHelper('toLocaleTimeString'), 15494 number: function ( thousands, decimal, precision, prefix, postfix ) { 15495 // Auto locale detection 15496 if (thousands === null || thousands === undefined) { 15497 thousands = __thousands; 15498 } 15499 15500 if (decimal === null || decimal === undefined) { 15501 decimal = __decimal; 15502 } 15503 15504 return { 15505 display: function ( d ) { 15506 if ( typeof d !== 'number' && typeof d !== 'string' ) { 15507 return d; 15508 } 15509 15510 if (d === '' || d === null) { 15511 return d; 15512 } 15513 15514 var negative = d < 0 ? '-' : ''; 15515 var flo = parseFloat( d ); 15516 15517 // If NaN then there isn't much formatting that we can do - just 15518 // return immediately, escaping any HTML (this was supposed to 15519 // be a number after all) 15520 if ( isNaN( flo ) ) { 15521 return __htmlEscapeEntities( d ); 15522 } 15523 15524 flo = flo.toFixed( precision ); 15525 d = Math.abs( flo ); 15526 15527 var intPart = parseInt( d, 10 ); 15528 var floatPart = precision ? 15529 decimal+(d - intPart).toFixed( precision ).substring( 2 ): 15530 ''; 15531 15532 // If zero, then can't have a negative prefix 15533 if (intPart === 0 && parseFloat(floatPart) === 0) { 15534 negative = ''; 15535 } 15536 15537 return negative + (prefix||'') + 15538 intPart.toString().replace( 15539 /\B(?=(\d{3})+(?!\d))/g, thousands 15540 ) + 15541 floatPart + 15542 (postfix||''); 15543 } 15544 }; 15545 }, 15546 15547 text: function () { 15548 return { 15549 display: __htmlEscapeEntities, 15550 filter: __htmlEscapeEntities 15551 }; 15552 } 15553 }; 15554 15555 15556 /* 15557 * This is really a good bit rubbish this method of exposing the internal methods 15558 * publicly... - To be fixed in 2.0 using methods on the prototype 15559 */ 15560 15561 15562 /** 15563 * Create a wrapper function for exporting an internal functions to an external API. 15564 * @param {string} fn API function name 15565 * @returns {function} wrapped function 15566 * @memberof DataTable#internal 15567 */ 15568 function _fnExternApiFunc (fn) 15569 { 15570 return function() { 15571 var args = [_fnSettingsFromNode( this[DataTable.ext.iApiIndex] )].concat( 15572 Array.prototype.slice.call(arguments) 15573 ); 15574 return DataTable.ext.internal[fn].apply( this, args ); 15575 }; 15576 } 15577 15578 15579 /** 15580 * Reference to internal functions for use by plug-in developers. Note that 15581 * these methods are references to internal functions and are considered to be 15582 * private. If you use these methods, be aware that they are liable to change 15583 * between versions. 15584 * @namespace 15585 */ 15586 $.extend( DataTable.ext.internal, { 15587 _fnExternApiFunc: _fnExternApiFunc, 15588 _fnBuildAjax: _fnBuildAjax, 15589 _fnAjaxUpdate: _fnAjaxUpdate, 15590 _fnAjaxParameters: _fnAjaxParameters, 15591 _fnAjaxUpdateDraw: _fnAjaxUpdateDraw, 15592 _fnAjaxDataSrc: _fnAjaxDataSrc, 15593 _fnAddColumn: _fnAddColumn, 15594 _fnColumnOptions: _fnColumnOptions, 15595 _fnAdjustColumnSizing: _fnAdjustColumnSizing, 15596 _fnVisibleToColumnIndex: _fnVisibleToColumnIndex, 15597 _fnColumnIndexToVisible: _fnColumnIndexToVisible, 15598 _fnVisbleColumns: _fnVisbleColumns, 15599 _fnGetColumns: _fnGetColumns, 15600 _fnColumnTypes: _fnColumnTypes, 15601 _fnApplyColumnDefs: _fnApplyColumnDefs, 15602 _fnHungarianMap: _fnHungarianMap, 15603 _fnCamelToHungarian: _fnCamelToHungarian, 15604 _fnLanguageCompat: _fnLanguageCompat, 15605 _fnBrowserDetect: _fnBrowserDetect, 15606 _fnAddData: _fnAddData, 15607 _fnAddTr: _fnAddTr, 15608 _fnNodeToDataIndex: _fnNodeToDataIndex, 15609 _fnNodeToColumnIndex: _fnNodeToColumnIndex, 15610 _fnGetCellData: _fnGetCellData, 15611 _fnSetCellData: _fnSetCellData, 15612 _fnSplitObjNotation: _fnSplitObjNotation, 15613 _fnGetObjectDataFn: _fnGetObjectDataFn, 15614 _fnSetObjectDataFn: _fnSetObjectDataFn, 15615 _fnGetDataMaster: _fnGetDataMaster, 15616 _fnClearTable: _fnClearTable, 15617 _fnDeleteIndex: _fnDeleteIndex, 15618 _fnInvalidate: _fnInvalidate, 15619 _fnGetRowElements: _fnGetRowElements, 15620 _fnCreateTr: _fnCreateTr, 15621 _fnBuildHead: _fnBuildHead, 15622 _fnDrawHead: _fnDrawHead, 15623 _fnDraw: _fnDraw, 15624 _fnReDraw: _fnReDraw, 15625 _fnAddOptionsHtml: _fnAddOptionsHtml, 15626 _fnDetectHeader: _fnDetectHeader, 15627 _fnGetUniqueThs: _fnGetUniqueThs, 15628 _fnFeatureHtmlFilter: _fnFeatureHtmlFilter, 15629 _fnFilterComplete: _fnFilterComplete, 15630 _fnFilterCustom: _fnFilterCustom, 15631 _fnFilterColumn: _fnFilterColumn, 15632 _fnFilter: _fnFilter, 15633 _fnFilterCreateSearch: _fnFilterCreateSearch, 15634 _fnEscapeRegex: _fnEscapeRegex, 15635 _fnFilterData: _fnFilterData, 15636 _fnFeatureHtmlInfo: _fnFeatureHtmlInfo, 15637 _fnUpdateInfo: _fnUpdateInfo, 15638 _fnInfoMacros: _fnInfoMacros, 15639 _fnInitialise: _fnInitialise, 15640 _fnInitComplete: _fnInitComplete, 15641 _fnLengthChange: _fnLengthChange, 15642 _fnFeatureHtmlLength: _fnFeatureHtmlLength, 15643 _fnFeatureHtmlPaginate: _fnFeatureHtmlPaginate, 15644 _fnPageChange: _fnPageChange, 15645 _fnFeatureHtmlProcessing: _fnFeatureHtmlProcessing, 15646 _fnProcessingDisplay: _fnProcessingDisplay, 15647 _fnFeatureHtmlTable: _fnFeatureHtmlTable, 15648 _fnScrollDraw: _fnScrollDraw, 15649 _fnApplyToChildren: _fnApplyToChildren, 15650 _fnCalculateColumnWidths: _fnCalculateColumnWidths, 15651 _fnThrottle: _fnThrottle, 15652 _fnConvertToWidth: _fnConvertToWidth, 15653 _fnGetWidestNode: _fnGetWidestNode, 15654 _fnGetMaxLenString: _fnGetMaxLenString, 15655 _fnStringToCss: _fnStringToCss, 15656 _fnSortFlatten: _fnSortFlatten, 15657 _fnSort: _fnSort, 15658 _fnSortAria: _fnSortAria, 15659 _fnSortListener: _fnSortListener, 15660 _fnSortAttachListener: _fnSortAttachListener, 15661 _fnSortingClasses: _fnSortingClasses, 15662 _fnSortData: _fnSortData, 15663 _fnSaveState: _fnSaveState, 15664 _fnLoadState: _fnLoadState, 15665 _fnImplementState: _fnImplementState, 15666 _fnSettingsFromNode: _fnSettingsFromNode, 15667 _fnLog: _fnLog, 15668 _fnMap: _fnMap, 15669 _fnBindAction: _fnBindAction, 15670 _fnCallbackReg: _fnCallbackReg, 15671 _fnCallbackFire: _fnCallbackFire, 15672 _fnLengthOverflow: _fnLengthOverflow, 15673 _fnRenderer: _fnRenderer, 15674 _fnDataSource: _fnDataSource, 15675 _fnRowAttributes: _fnRowAttributes, 15676 _fnExtend: _fnExtend, 15677 _fnCalculateEnd: function () {} // Used by a lot of plug-ins, but redundant 15678 // in 1.10, so this dead-end function is 15679 // added to prevent errors 15680 } ); 15681 15682 15683 // jQuery access 15684 $.fn.dataTable = DataTable; 15685 15686 // Provide access to the host jQuery object (circular reference) 15687 DataTable.$ = $; 15688 15689 // Legacy aliases 15690 $.fn.dataTableSettings = DataTable.settings; 15691 $.fn.dataTableExt = DataTable.ext; 15692 15693 // With a capital `D` we return a DataTables API instance rather than a 15694 // jQuery object 15695 $.fn.DataTable = function ( opts ) { 15696 return $(this).dataTable( opts ).api(); 15697 }; 15698 15699 // All properties that are available to $.fn.dataTable should also be 15700 // available on $.fn.DataTable 15701 $.each( DataTable, function ( prop, val ) { 15702 $.fn.DataTable[ prop ] = val; 15703 } ); 15704 15705 return DataTable; 15706})); 15707