1/*! DataTables 2.0.8 2 * © SpryMedia Ltd - datatables.net/license 3 */ 4 5/** 6 * @summary DataTables 7 * @description Paginate, search and order HTML tables 8 * @version 2.0.8 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 - https://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: https://www.datatables.net 21 */ 22 23(function( factory ) { 24 "use strict"; 25 26 if ( typeof define === 'function' && define.amd ) { 27 // AMD 28 define( ['jquery'], function ( $ ) { 29 return factory( $, window, document ); 30 } ); 31 } 32 else if ( typeof exports === 'object' ) { 33 // CommonJS 34 // jQuery's factory checks for a global window - if it isn't present then it 35 // returns a factory function that expects the window object 36 var jq = require('jquery'); 37 38 if (typeof window === 'undefined') { 39 module.exports = function (root, $) { 40 if ( ! root ) { 41 // CommonJS environments without a window global must pass a 42 // root. This will give an error otherwise 43 root = window; 44 } 45 46 if ( ! $ ) { 47 $ = jq( root ); 48 } 49 50 return factory( $, root, root.document ); 51 }; 52 } 53 else { 54 module.exports = factory( jq, window, window.document ); 55 } 56 } 57 else { 58 // Browser 59 window.DataTable = factory( jQuery, window, document ); 60 } 61}(function( $, window, document ) { 62 "use strict"; 63 64 65 var DataTable = function ( selector, options ) 66 { 67 // Check if called with a window or jQuery object for DOM less applications 68 // This is for backwards compatibility 69 if (DataTable.factory(selector, options)) { 70 return DataTable; 71 } 72 73 // When creating with `new`, create a new DataTable, returning the API instance 74 if (this instanceof DataTable) { 75 return $(selector).DataTable(options); 76 } 77 else { 78 // Argument switching 79 options = selector; 80 } 81 82 var _that = this; 83 var emptyInit = options === undefined; 84 var len = this.length; 85 86 if ( emptyInit ) { 87 options = {}; 88 } 89 90 // Method to get DT API instance from jQuery object 91 this.api = function () 92 { 93 return new _Api( this ); 94 }; 95 96 this.each(function() { 97 // For each initialisation we want to give it a clean initialisation 98 // object that can be bashed around 99 var o = {}; 100 var oInit = len > 1 ? // optimisation for single table case 101 _fnExtend( o, options, true ) : 102 options; 103 104 105 var i=0, iLen; 106 var sId = this.getAttribute( 'id' ); 107 var bInitHandedOff = false; 108 var defaults = DataTable.defaults; 109 var $this = $(this); 110 111 112 /* Sanity check */ 113 if ( this.nodeName.toLowerCase() != 'table' ) 114 { 115 _fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 ); 116 return; 117 } 118 119 $(this).trigger( 'options.dt', oInit ); 120 121 /* Backwards compatibility for the defaults */ 122 _fnCompatOpts( defaults ); 123 _fnCompatCols( defaults.column ); 124 125 /* Convert the camel-case defaults to Hungarian */ 126 _fnCamelToHungarian( defaults, defaults, true ); 127 _fnCamelToHungarian( defaults.column, defaults.column, true ); 128 129 /* Setting up the initialisation object */ 130 _fnCamelToHungarian( defaults, $.extend( oInit, $this.data() ), true ); 131 132 133 134 /* Check to see if we are re-initialising a table */ 135 var allSettings = DataTable.settings; 136 for ( i=0, iLen=allSettings.length ; i<iLen ; i++ ) 137 { 138 var s = allSettings[i]; 139 140 /* Base check on table node */ 141 if ( 142 s.nTable == this || 143 (s.nTHead && s.nTHead.parentNode == this) || 144 (s.nTFoot && s.nTFoot.parentNode == this) 145 ) { 146 var bRetrieve = oInit.bRetrieve !== undefined ? oInit.bRetrieve : defaults.bRetrieve; 147 var bDestroy = oInit.bDestroy !== undefined ? oInit.bDestroy : defaults.bDestroy; 148 149 if ( emptyInit || bRetrieve ) 150 { 151 return s.oInstance; 152 } 153 else if ( bDestroy ) 154 { 155 new DataTable.Api(s).destroy(); 156 break; 157 } 158 else 159 { 160 _fnLog( s, 0, 'Cannot reinitialise DataTable', 3 ); 161 return; 162 } 163 } 164 165 /* If the element we are initialising has the same ID as a table which was previously 166 * initialised, but the table nodes don't match (from before) then we destroy the old 167 * instance by simply deleting it. This is under the assumption that the table has been 168 * destroyed by other methods. Anyone using non-id selectors will need to do this manually 169 */ 170 if ( s.sTableId == this.id ) 171 { 172 allSettings.splice( i, 1 ); 173 break; 174 } 175 } 176 177 /* Ensure the table has an ID - required for accessibility */ 178 if ( sId === null || sId === "" ) 179 { 180 sId = "DataTables_Table_"+(DataTable.ext._unique++); 181 this.id = sId; 182 } 183 184 /* Create the settings object for this table and set some of the default parameters */ 185 var oSettings = $.extend( true, {}, DataTable.models.oSettings, { 186 "sDestroyWidth": $this[0].style.width, 187 "sInstance": sId, 188 "sTableId": sId, 189 colgroup: $('<colgroup>').prependTo(this), 190 fastData: function (row, column, type) {
191 return _fnGetCellData(oSettings, row, column, type); 192 } 193 } ); 194 oSettings.nTable = this; 195 oSettings.oInit = oInit; 196 197 allSettings.push( oSettings ); 198 199 // Make a single API instance available for internal handling 200 oSettings.api = new _Api( oSettings ); 201 202 // Need to add the instance after the instance after the settings object has been added 203 // to the settings array, so we can self reference the table instance if more than one 204 oSettings.oInstance = (_that.length===1) ? _that : $this.dataTable(); 205 206 // Backwards compatibility, before we apply all the defaults 207 _fnCompatOpts( oInit ); 208 209 // If the length menu is given, but the init display length is not, use the length menu 210 if ( oInit.aLengthMenu && ! oInit.iDisplayLength ) 211 { 212 oInit.iDisplayLength = Array.isArray(oInit.aLengthMenu[0]) 213 ? oInit.aLengthMenu[0][0] 214 : $.isPlainObject( oInit.aLengthMenu[0] ) 215 ? oInit.aLengthMenu[0].value 216 : oInit.aLengthMenu[0]; 217 } 218 219 // Apply the defaults and init options to make a single init object will all 220 // options defined from defaults and instance options. 221 oInit = _fnExtend( $.extend( true, {}, defaults ), oInit ); 222 223 224 // Map the initialisation options onto the settings object 225 _fnMap( oSettings.oFeatures, oInit, [ 226 "bPaginate", 227 "bLengthChange", 228 "bFilter", 229 "bSort", 230 "bSortMulti", 231 "bInfo", 232 "bProcessing", 233 "bAutoWidth", 234 "bSortClasses", 235 "bServerSide", 236 "bDeferRender" 237 ] ); 238 _fnMap( oSettings, oInit, [ 239 "ajax", 240 "fnFormatNumber", 241 "sServerMethod", 242 "aaSorting", 243 "aaSortingFixed", 244 "aLengthMenu", 245 "sPaginationType", 246 "iStateDuration", 247 "bSortCellsTop", 248 "iTabIndex", 249 "sDom", 250 "fnStateLoadCallback", 251 "fnStateSaveCallback", 252 "renderer", 253 "searchDelay", 254 "rowId", 255 "caption", 256 "layout", 257 [ "iCookieDuration", "iStateDuration" ], // backwards compat 258 [ "oSearch", "oPreviousSearch" ], 259 [ "aoSearchCols", "aoPreSearchCols" ], 260 [ "iDisplayLength", "_iDisplayLength" ] 261 ] ); 262 _fnMap( oSettings.oScroll, oInit, [ 263 [ "sScrollX", "sX" ], 264 [ "sScrollXInner", "sXInner" ], 265 [ "sScrollY", "sY" ], 266 [ "bScrollCollapse", "bCollapse" ] 267 ] ); 268 _fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" ); 269 270 /* Callback functions which are array driven */ 271 _fnCallbackReg( oSettings, 'aoDrawCallback', oInit.fnDrawCallback ); 272 _fnCallbackReg( oSettings, 'aoStateSaveParams', oInit.fnStateSaveParams ); 273 _fnCallbackReg( oSettings, 'aoStateLoadParams', oInit.fnStateLoadParams ); 274 _fnCallbackReg( oSettings, 'aoStateLoaded', oInit.fnStateLoaded ); 275 _fnCallbackReg( oSettings, 'aoRowCallback', oInit.fnRowCallback ); 276 _fnCallbackReg( oSettings, 'aoRowCreatedCallback', oInit.fnCreatedRow ); 277 _fnCallbackReg( oSettings, 'aoHeaderCallback', oInit.fnHeaderCallback ); 278 _fnCallbackReg( oSettings, 'aoFooterCallback', oInit.fnFooterCallback ); 279 _fnCallbackReg( oSettings, 'aoInitComplete', oInit.fnInitComplete ); 280 _fnCallbackReg( oSettings, 'aoPreDrawCallback', oInit.fnPreDrawCallback ); 281 282 oSettings.rowIdFn = _fnGetObjectDataFn( oInit.rowId ); 283 284 /* Browser support detection */ 285 _fnBrowserDetect( oSettings ); 286 287 var oClasses = oSettings.oClasses; 288 289 $.extend( oClasses, DataTable.ext.classes, oInit.oClasses ); 290 $this.addClass( oClasses.table ); 291 292 if (! oSettings.oFeatures.bPaginate) { 293 oInit.iDisplayStart = 0; 294 } 295 296 if ( oSettings.iInitDisplayStart === undefined ) 297 { 298 /* Display start point, taking into account the save saving */ 299 oSettings.iInitDisplayStart = oInit.iDisplayStart; 300 oSettings._iDisplayStart = oInit.iDisplayStart; 301 } 302 303 /* Language definitions */ 304 var oLanguage = oSettings.oLanguage; 305 $.extend( true, oLanguage, oInit.oLanguage ); 306 307 if ( oLanguage.sUrl ) 308 { 309 /* Get the language definitions from a file - because this Ajax call makes the language 310 * get async to the remainder of this function we use bInitHandedOff to indicate that 311 * _fnInitialise will be fired by the returned Ajax handler, rather than the constructor 312 */ 313 $.ajax( { 314 dataType: 'json', 315 url: oLanguage.sUrl, 316 success: function ( json ) { 317 _fnCamelToHungarian( defaults.oLanguage, json ); 318 $.extend( true, oLanguage, json, oSettings.oInit.oLanguage ); 319 320 _fnCallbackFire( oSettings, null, 'i18n', [oSettings], true); 321 _fnInitialise( oSettings ); 322 }, 323 error: function () { 324 // Error occurred loading language file 325 _fnLog( oSettings, 0, 'i18n file loading error', 21 ); 326 327 // continue on as best we can 328 _fnInitialise( oSettings ); 329 } 330 } ); 331 bInitHandedOff = true; 332 } 333 else { 334 _fnCallbackFire( oSettings, null, 'i18n', [oSettings]); 335 } 336 337 /* 338 * Columns 339 * See if we should load columns automatically or use defined ones 340 */ 341 var columnsInit = []; 342 var thead = this.getElementsByTagName('thead'); 343 var initHeaderLayout = _fnDetectHeader( oSettings, thead[0] ); 344 345 // If we don't have a columns array, then generate one with nulls 346 if ( oInit.aoColumns ) { 347 columnsInit = oInit.aoColumns; 348 } 349 else if ( initHeaderLayout.length ) { 350 for ( i=0, iLen=initHeaderLayout[0].length ; i<iLen ; i++ ) { 351 columnsInit.push( null ); 352 } 353 } 354 355 // Add the columns 356 for ( i=0, iLen=columnsInit.length ; i<iLen ; i++ ) { 357 _fnAddColumn( oSettings ); 358 } 359 360 // Apply the column definitions 361 _fnApplyColumnDefs( oSettings, oInit.aoColumnDefs, columnsInit, initHeaderLayout, function (iCol, oDef) { 362 _fnColumnOptions( oSettings, iCol, oDef ); 363 } ); 364 365 /* HTML5 attribute detection - build an mData object automatically if the 366 * attributes are found 367 */ 368 var rowOne = $this.children('tbody').find('tr').eq(0); 369 370 if ( rowOne.length ) { 371 var a = function ( cell, name ) { 372 return cell.getAttribute( 'data-'+name ) !== null ? name : null; 373 }; 374 375 $( rowOne[0] ).children('th, td').each( function (i, cell) { 376 var col = oSettings.aoColumns[i]; 377 378 if (! col) { 379 _fnLog( oSettings, 0, 'Incorrect column count', 18 ); 380 } 381 382 if ( col.mData === i ) { 383 var sort = a( cell, 'sort' ) || a( cell, 'order' ); 384 var filter = a( cell, 'filter' ) || a( cell, 'search' ); 385 386 if ( sort !== null || filter !== null ) { 387 col.mData = { 388 _: i+'.display', 389 sort: sort !== null ? i+'.@data-'+sort : undefined, 390 type: sort !== null ? i+'.@data-'+sort : undefined, 391 filter: filter !== null ? i+'.@data-'+filter : undefined 392 }; 393 col._isArrayHost = true; 394 395 _fnColumnOptions( oSettings, i ); 396 } 397 } 398 } ); 399 } 400 401 var features = oSettings.oFeatures; 402 var loadedInit = function () { 403 /* 404 * Sorting 405 * @todo For modularisation (1.11) this needs to do into a sort start up handler 406 */ 407 408 // If aaSorting is not defined, then we use the first indicator in asSorting 409 // in case that has been altered, so the default sort reflects that option 410 if ( oInit.aaSorting === undefined ) { 411 var sorting = oSettings.aaSorting; 412 for ( i=0, iLen=sorting.length ; i<iLen ; i++ ) { 413 sorting[i][1] = oSettings.aoColumns[ i ].asSorting[0]; 414 } 415 } 416 417 /* Do a first pass on the sorting classes (allows any size changes to be taken into 418 * account, and also will apply sorting disabled classes if disabled 419 */ 420 _fnSortingClasses( oSettings ); 421 422 _fnCallbackReg( oSettings, 'aoDrawCallback', function () { 423 if ( oSettings.bSorted || _fnDataSource( oSettings ) === 'ssp' || features.bDeferRender ) { 424 _fnSortingClasses( oSettings ); 425 } 426 } ); 427 428 429 /* 430 * Final init 431 * Cache the header, body and footer as required, creating them if needed 432 */ 433 var caption = $this.children('caption'); 434 435 if ( oSettings.caption ) { 436 if ( caption.length === 0 ) { 437 caption = $('<caption/>').appendTo( $this ); 438 } 439 440 caption.html( oSettings.caption ); 441 } 442 443 // Store the caption side, so we can remove the element from the document 444 // when creating the element 445 if (caption.length) { 446 caption[0]._captionSide = caption.css('caption-side'); 447 oSettings.captionNode = caption[0]; 448 } 449 450 if ( thead.length === 0 ) { 451 thead = $('<thead/>').appendTo($this); 452 } 453 oSettings.nTHead = thead[0]; 454 $('tr', thead).addClass(oClasses.thead.row); 455 456 var tbody = $this.children('tbody'); 457 if ( tbody.length === 0 ) { 458 tbody = $('<tbody/>').insertAfter(thead); 459 } 460 oSettings.nTBody = tbody[0]; 461 462 var tfoot = $this.children('tfoot'); 463 if ( tfoot.length === 0 ) { 464 // If we are a scrolling table, and no footer has been given, then we need to create 465 // a tfoot element for the caption element to be appended to 466 tfoot = $('<tfoot/>').appendTo($this); 467 } 468 oSettings.nTFoot = tfoot[0]; 469 $('tr', tfoot).addClass(oClasses.tfoot.row); 470 471 // Check if there is data passing into the constructor 472 if ( oInit.aaData ) { 473 for ( i=0 ; i<oInit.aaData.length ; i++ ) { 474 _fnAddData( oSettings, oInit.aaData[ i ] ); 475 } 476 } 477 else if ( _fnDataSource( oSettings ) == 'dom' ) { 478 // Grab the data from the page 479 _fnAddTr( oSettings, $(oSettings.nTBody).children('tr') ); 480 } 481 482 /* Copy the data index array */ 483 oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); 484 485 /* Initialisation complete - table can be drawn */
486 oSettings.bInitialised = true; 487 488 /* Check if we need to initialise the table (it might not have been handed off to the 489 * language processor) 490 */ 491 if ( bInitHandedOff === false ) { 492 _fnInitialise( oSettings ); 493 } 494 }; 495 496 /* Must be done after everything which can be overridden by the state saving! */ 497 _fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState ); 498 499 if ( oInit.bStateSave ) 500 { 501 features.bStateSave = true; 502 _fnLoadState( oSettings, oInit, loadedInit ); 503 } 504 else { 505 loadedInit(); 506 } 507 508 } ); 509 _that = null; 510 return this; 511 }; 512 513 514 515 /** 516 * DataTables extensions 517 * 518 * This namespace acts as a collection area for plug-ins that can be used to 519 * extend DataTables capabilities. Indeed many of the build in methods 520 * use this method to provide their own capabilities (sorting methods for 521 * example). 522 * 523 * Note that this namespace is aliased to `jQuery.fn.dataTableExt` for legacy 524 * reasons 525 * 526 * @namespace 527 */ 528 DataTable.ext = _ext = { 529 /** 530 * Buttons. For use with the Buttons extension for DataTables. This is 531 * defined here so other extensions can define buttons regardless of load 532 * order. It is _not_ used by DataTables core. 533 * 534 * @type object 535 * @default {} 536 */ 537 buttons: {}, 538 539 540 /** 541 * Element class names 542 * 543 * @type object 544 * @default {} 545 */ 546 classes: {}, 547 548 549 /** 550 * DataTables build type (expanded by the download builder) 551 * 552 * @type string 553 */ 554 builder: "-source-", 555 556 557 /** 558 * Error reporting. 559 * 560 * How should DataTables report an error. Can take the value 'alert', 561 * 'throw', 'none' or a function. 562 * 563 * @type string|function 564 * @default alert 565 */ 566 errMode: "alert", 567 568 569 /** 570 * Legacy so v1 plug-ins don't throw js errors on load 571 */ 572 feature: [], 573 574 /** 575 * Feature plug-ins. 576 * 577 * This is an object of callbacks which provide the features for DataTables 578 * to be initialised via the `layout` option. 579 */ 580 features: {}, 581 582 583 /** 584 * Row searching. 585 * 586 * This method of searching is complimentary to the default type based 587 * searching, and a lot more comprehensive as it allows you complete control 588 * over the searching logic. Each element in this array is a function 589 * (parameters described below) that is called for every row in the table, 590 * and your logic decides if it should be included in the searching data set 591 * or not. 592 * 593 * Searching functions have the following input parameters: 594 * 595 * 1. `{object}` DataTables settings object: see 596 * {@link DataTable.models.oSettings} 597 * 2. `{array|object}` Data for the row to be processed (same as the 598 * original format that was passed in as the data source, or an array 599 * from a DOM data source 600 * 3. `{int}` Row index ({@link DataTable.models.oSettings.aoData}), which 601 * can be useful to retrieve the `TR` element if you need DOM interaction. 602 * 603 * And the following return is expected: 604 * 605 * * {boolean} Include the row in the searched result set (true) or not 606 * (false) 607 * 608 * Note that as with the main search ability in DataTables, technically this 609 * is "filtering", since it is subtractive. However, for consistency in 610 * naming we call it searching here. 611 * 612 * @type array 613 * @default [] 614 * 615 * @example 616 * // The following example shows custom search being applied to the 617 * // fourth column (i.e. the data[3] index) based on two input values 618 * // from the end-user, matching the data in a certain range. 619 * $.fn.dataTable.ext.search.push( 620 * function( settings, data, dataIndex ) { 621 * var min = document.getElementById('min').value * 1; 622 * var max = document.getElementById('max').value * 1; 623 * var version = data[3] == "-" ? 0 : data[3]*1; 624 * 625 * if ( min == "" && max == "" ) { 626 * return true; 627 * } 628 * else if ( min == "" && version < max ) { 629 * return true; 630 * } 631 * else if ( min < version && "" == max ) { 632 * return true; 633 * } 634 * else if ( min < version && version < max ) { 635 * return true; 636 * } 637 * return false; 638 * } 639 * ); 640 */ 641 search: [], 642 643 644 /** 645 * Selector extensions 646 * 647 * The `selector` option can be used to extend the options available for the 648 * selector modifier options (`selector-modifier` object data type) that 649 * each of the three built in selector types offer (row, column and cell + 650 * their plural counterparts). For example the Select extension uses this 651 * mechanism to provide an option to select only rows, columns and cells 652 * that have been marked as selected by the end user (`{selected: true}`), 653 * which can be used in conjunction with the existing built in selector 654 * options. 655 * 656 * Each property is an array to which functions can be pushed. The functions 657 * take three attributes: 658 * 659 * * Settings object for the host table 660 * * Options object (`selector-modifier` object type) 661 * * Array of selected item indexes 662 * 663 * The return is an array of the resulting item indexes after the custom 664 * selector has been applied. 665 * 666 * @type object 667 */ 668 selector: { 669 cell: [], 670 column: [], 671 row: [] 672 }, 673 674 675 /** 676 * Legacy configuration options. Enable and disable legacy options that 677 * are available in DataTables. 678 * 679 * @type object 680 */ 681 legacy: { 682 /** 683 * Enable / disable DataTables 1.9 compatible server-side processing 684 * requests 685 * 686 * @type boolean 687 * @default null 688 */ 689 ajax: null 690 }, 691 692 693 /** 694 * Pagination plug-in methods. 695 * 696 * Each entry in this object is a function and defines which buttons should 697 * be shown by the pagination rendering method that is used for the table: 698 * {@link DataTable.ext.renderer.pageButton}. The renderer addresses how the 699 * buttons are displayed in the document, while the functions here tell it 700 * what buttons to display. This is done by returning an array of button 701 * descriptions (what each button will do). 702 * 703 * Pagination types (the four built in options and any additional plug-in 704 * options defined here) can be used through the `paginationType` 705 * initialisation parameter. 706 * 707 * The functions defined take two parameters: 708 * 709 * 1. `{int} page` The current page index 710 * 2. `{int} pages` The number of pages in the table 711 * 712 * Each function is expected to return an array where each element of the 713 * array can be one of: 714 * 715 * * `first` - Jump to first page when activated 716 * * `last` - Jump to last page when activated 717 * * `previous` - Show previous page when activated 718 * * `next` - Show next page when activated 719 * * `{int}` - Show page of the index given 720 * * `{array}` - A nested array containing the above elements to add a 721 * containing 'DIV' element (might be useful for styling). 722 * 723 * Note that DataTables v1.9- used this object slightly differently whereby 724 * an object with two functions would be defined for each plug-in. That 725 * ability is still supported by DataTables 1.10+ to provide backwards 726 * compatibility, but this option of use is now decremented and no longer 727 * documented in DataTables 1.10+. 728 * 729 * @type object 730 * @default {} 731 * 732 * @example 733 * // Show previous, next and current page buttons only 734 * $.fn.dataTableExt.oPagination.current = function ( page, pages ) { 735 * return [ 'previous', page, 'next' ]; 736 * }; 737 */ 738 pager: {}, 739 740 741 renderer: { 742 pageButton: {}, 743 header: {} 744 }, 745 746 747 /** 748 * Ordering plug-ins - custom data source 749 *
750 * The extension options for ordering of data available here is complimentary 751 * to the default type based ordering that DataTables typically uses. It 752 * allows much greater control over the the data that is being used to 753 * order a column, but is necessarily therefore more complex. 754 * 755 * This type of ordering is useful if you want to do ordering based on data 756 * live from the DOM (for example the contents of an 'input' element) rather 757 * than just the static string that DataTables knows of. 758 * 759 * The way these plug-ins work is that you create an array of the values you 760 * wish to be ordering for the column in question and then return that 761 * array. The data in the array much be in the index order of the rows in 762 * the table (not the currently ordering order!). Which order data gathering 763 * function is run here depends on the `dt-init columns.orderDataType` 764 * parameter that is used for the column (if any). 765 * 766 * The functions defined take two parameters: 767 * 768 * 1. `{object}` DataTables settings object: see 769 * {@link DataTable.models.oSettings} 770 * 2. `{int}` Target column index 771 * 772 * Each function is expected to return an array: 773 * 774 * * `{array}` Data for the column to be ordering upon 775 * 776 * @type array 777 * 778 * @example 779 * // Ordering using `input` node values 780 * $.fn.dataTable.ext.order['dom-text'] = function ( settings, col ) 781 * { 782 * return this.api().column( col, {order:'index'} ).nodes().map( function ( td, i ) { 783 * return $('input', td).val(); 784 * } ); 785 * } 786 */ 787 order: {}, 788 789 790 /** 791 * Type based plug-ins. 792 * 793 * Each column in DataTables has a type assigned to it, either by automatic 794 * detection or by direct assignment using the `type` option for the column. 795 * The type of a column will effect how it is ordering and search (plug-ins 796 * can also make use of the column type if required). 797 * 798 * @namespace 799 */ 800 type: { 801 /** 802 * Automatic column class assignment 803 */ 804 className: {}, 805 806 /** 807 * Type detection functions. 808 * 809 * The functions defined in this object are used to automatically detect 810 * a column's type, making initialisation of DataTables super easy, even 811 * when complex data is in the table. 812 * 813 * The functions defined take two parameters: 814 * 815 * 1. `{*}` Data from the column cell to be analysed 816 * 2. `{settings}` DataTables settings object. This can be used to 817 * perform context specific type detection - for example detection 818 * based on language settings such as using a comma for a decimal 819 * place. Generally speaking the options from the settings will not 820 * be required 821 * 822 * Each function is expected to return: 823 * 824 * * `{string|null}` Data type detected, or null if unknown (and thus 825 * pass it on to the other type detection functions. 826 * 827 * @type array 828 * 829 * @example 830 * // Currency type detection plug-in: 831 * $.fn.dataTable.ext.type.detect.push( 832 * function ( data, settings ) { 833 * // Check the numeric part 834 * if ( ! data.substring(1).match(/[0-9]/) ) { 835 * return null; 836 * } 837 * 838 * // Check prefixed by currency 839 * if ( data.charAt(0) == '$' || data.charAt(0) == '£' ) { 840 * return 'currency'; 841 * } 842 * return null; 843 * } 844 * ); 845 */ 846 detect: [], 847 848 /** 849 * Automatic renderer assignment 850 */ 851 render: {}, 852 853 854 /** 855 * Type based search formatting. 856 * 857 * The type based searching functions can be used to pre-format the 858 * data to be search on. For example, it can be used to strip HTML 859 * tags or to de-format telephone numbers for numeric only searching. 860 * 861 * Note that is a search is not defined for a column of a given type, 862 * no search formatting will be performed. 863 * 864 * Pre-processing of searching data plug-ins - When you assign the sType 865 * for a column (or have it automatically detected for you by DataTables 866 * or a type detection plug-in), you will typically be using this for 867 * custom sorting, but it can also be used to provide custom searching 868 * by allowing you to pre-processing the data and returning the data in 869 * the format that should be searched upon. This is done by adding 870 * functions this object with a parameter name which matches the sType 871 * for that target column. This is the corollary of <i>afnSortData</i> 872 * for searching data. 873 * 874 * The functions defined take a single parameter: 875 * 876 * 1. `{*}` Data from the column cell to be prepared for searching 877 * 878 * Each function is expected to return: 879 * 880 * * `{string|null}` Formatted string that will be used for the searching. 881 * 882 * @type object 883 * @default {} 884 * 885 * @example 886 * $.fn.dataTable.ext.type.search['title-numeric'] = function ( d ) { 887 * return d.replace(/\n/g," ").replace( /<.*?>/g, "" ); 888 * } 889 */ 890 search: {}, 891 892 893 /** 894 * Type based ordering. 895 * 896 * The column type tells DataTables what ordering to apply to the table 897 * when a column is sorted upon. The order for each type that is defined, 898 * is defined by the functions available in this object. 899 * 900 * Each ordering option can be described by three properties added to 901 * this object: 902 * 903 * * `{type}-pre` - Pre-formatting function 904 * * `{type}-asc` - Ascending order function 905 * * `{type}-desc` - Descending order function 906 * 907 * All three can be used together, only `{type}-pre` or only 908 * `{type}-asc` and `{type}-desc` together. It is generally recommended 909 * that only `{type}-pre` is used, as this provides the optimal 910 * implementation in terms of speed, although the others are provided 911 * for compatibility with existing Javascript sort functions. 912 * 913 * `{type}-pre`: Functions defined take a single parameter: 914 * 915 * 1. `{*}` Data from the column cell to be prepared for ordering 916 * 917 * And return: 918 * 919 * * `{*}` Data to be sorted upon 920 * 921 * `{type}-asc` and `{type}-desc`: Functions are typical Javascript sort 922 * functions, taking two parameters: 923 * 924 * 1. `{*}` Data to compare to the second parameter 925 * 2. `{*}` Data to compare to the first parameter 926 * 927 * And returning: 928 * 929 * * `{*}` Ordering match: <0 if first parameter should be sorted lower 930 * than the second parameter, ===0 if the two parameters are equal and 931 * >0 if the first parameter should be sorted height than the second 932 * parameter. 933 * 934 * @type object 935 * @default {} 936 * 937 * @example 938 * // Numeric ordering of formatted numbers with a pre-formatter 939 * $.extend( $.fn.dataTable.ext.type.order, { 940 * "string-pre": function(x) { 941 * a = (a === "-" || a === "") ? 0 : a.replace( /[^\d\-\.]/g, "" ); 942 * return parseFloat( a ); 943 * } 944 * } ); 945 * 946 * @example 947 * // Case-sensitive string ordering, with no pre-formatting method 948 * $.extend( $.fn.dataTable.ext.order, { 949 * "string-case-asc": function(x,y) { 950 * return ((x < y) ? -1 : ((x > y) ? 1 : 0)); 951 * }, 952 * "string-case-desc": function(x,y) { 953 * return ((x < y) ? 1 : ((x > y) ? -1 : 0)); 954 * } 955 * } ); 956 */ 957 order: {} 958 }, 959 960 /** 961 * Unique DataTables instance counter 962 * 963 * @type int 964 * @private 965 */ 966 _unique: 0, 967 968 969 // 970 // Depreciated 971 // The following properties are retained for backwards compatibility only. 972 // The should not be used in new projects and will be removed in a future 973 // version 974 // 975 976 /** 977 * Version check function. 978 * @type function 979 * @depreciated Since 1.10 980 */ 981 fnVersionCheck: DataTable.fnVersionCheck, 982 983 984 /** 985 * Index for what 'this' index API functions should use 986 * @type int 987 * @deprecated Since v1.10 988 */ 989 iApiIndex: 0, 990 991 992 /** 993 * Software version 994 * @type string 995 * @deprecated Since v1.10 996 */
997 sVersion: DataTable.version 998 }; 999 1000 1001 // 1002 // Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts 1003 // 1004 $.extend( _ext, { 1005 afnFiltering: _ext.search, 1006 aTypes: _ext.type.detect, 1007 ofnSearch: _ext.type.search, 1008 oSort: _ext.type.order, 1009 afnSortData: _ext.order, 1010 aoFeatures: _ext.feature, 1011 oStdClasses: _ext.classes, 1012 oPagination: _ext.pager 1013 } ); 1014 1015 1016 $.extend( DataTable.ext.classes, { 1017 container: 'dt-container', 1018 empty: { 1019 row: 'dt-empty' 1020 }, 1021 info: { 1022 container: 'dt-info' 1023 }, 1024 length: { 1025 container: 'dt-length', 1026 select: 'dt-input' 1027 }, 1028 order: { 1029 canAsc: 'dt-orderable-asc', 1030 canDesc: 'dt-orderable-desc', 1031 isAsc: 'dt-ordering-asc', 1032 isDesc: 'dt-ordering-desc', 1033 none: 'dt-orderable-none', 1034 position: 'sorting_' 1035 }, 1036 processing: { 1037 container: 'dt-processing' 1038 }, 1039 scrolling: { 1040 body: 'dt-scroll-body', 1041 container: 'dt-scroll', 1042 footer: { 1043 self: 'dt-scroll-foot', 1044 inner: 'dt-scroll-footInner' 1045 }, 1046 header: { 1047 self: 'dt-scroll-head', 1048 inner: 'dt-scroll-headInner' 1049 } 1050 }, 1051 search: { 1052 container: 'dt-search', 1053 input: 'dt-input' 1054 }, 1055 table: 'dataTable', 1056 tbody: { 1057 cell: '', 1058 row: '' 1059 }, 1060 thead: { 1061 cell: '', 1062 row: '' 1063 }, 1064 tfoot: { 1065 cell: '', 1066 row: '' 1067 }, 1068 paging: { 1069 active: 'current', 1070 button: 'dt-paging-button', 1071 container: 'dt-paging', 1072 disabled: 'disabled' 1073 } 1074 } ); 1075 1076 1077 /*
1078 * It is useful to have variables which are scoped locally so only the 1079 * DataTables functions can access them and they don't leak into global space. 1080 * At the same time these functions are often useful over multiple files in the 1081 * core and API, so we list, or at least document, all variables which are used 1082 * by DataTables as private variables here. This also ensures that there is no 1083 * clashing of variable names and that they can easily referenced for reuse. 1084 */ 1085 1086 1087 // Defined else where 1088 // _selector_run 1089 // _selector_opts 1090 // _selector_row_indexes 1091 1092 var _ext; // DataTable.ext 1093 var _Api; // DataTable.Api 1094 var _api_register; // DataTable.Api.register 1095 var _api_registerPlural; // DataTable.Api.registerPlural 1096 1097 var _re_dic = {}; 1098 var _re_new_lines = /[\r\n\u2028]/g; 1099 var _re_html = /<([^>]*>)/g; 1100 var _max_str_len = Math.pow(2, 28); 1101 1102 // This is not strict ISO8601 - Date.parse() is quite lax, although 1103 // implementations differ between browsers. 1104 var _re_date = /^\d{2,4}[./-]\d{1,2}[./-]\d{1,2}([T ]{1}\d{1,2}[:.]\d{2}([.:]\d{2})?)?$/; 1105 1106 // Escape regular expression special characters 1107 var _re_escape_regex = new RegExp( '(\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ].join('|\\') + ')', 'g' ); 1108 1109 // https://en.wikipedia.org/wiki/Foreign_exchange_market 1110 // - \u20BD - Russian ruble. 1111 // - \u20a9 - South Korean Won 1112 // - \u20BA - Turkish Lira 1113 // - \u20B9 - Indian Rupee 1114 // - R - Brazil (R$) and South Africa 1115 // - fr - Swiss Franc 1116 // - kr - Swedish krona, Norwegian krone and Danish krone 1117 // - \u2009 is thin space and \u202F is narrow no-break space, both used in many 1118 // - É - Bitcoin 1119 // - Î - Ethereum 1120 // standards as thousands separators. 1121 var _re_formatted_numeric = /['\u00A0,$£â¬Â¥%\u2009\u202F\u20BD\u20a9\u20BArfkÉÎ]/gi; 1122 1123 1124 var _empty = function ( d ) { 1125 return !d || d === true || d === '-' ? true : false; 1126 }; 1127 1128 1129 var _intVal = function ( s ) { 1130 var integer = parseInt( s, 10 ); 1131 return !isNaN(integer) && isFinite(s) ? integer : null; 1132 }; 1133 1134 // Convert from a formatted number with characters other than `.` as the 1135 // decimal place, to a Javascript number 1136 var _numToDecimal = function ( num, decimalPoint ) { 1137 // Cache created regular expressions for speed as this function is called often 1138 if ( ! _re_dic[ decimalPoint ] ) { 1139 _re_dic[ decimalPoint ] = new RegExp( _fnEscapeRegex( decimalPoint ), 'g' ); 1140 } 1141 return typeof num === 'string' && decimalPoint !== '.' ? 1142 num.replace( /\./g, '' ).replace( _re_dic[ decimalPoint ], '.' ) : 1143 num; 1144 }; 1145 1146 1147 var _isNumber = function ( d, decimalPoint, formatted ) { 1148 var type = typeof d; 1149 var strType = type === 'string'; 1150 1151 if ( type === 'number' || type === 'bigint') { 1152 return true; 1153 } 1154 1155 // If empty return immediately so there must be a number if it is a 1156 // formatted string (this stops the string "k", or "kr", etc being detected 1157 // as a formatted number for currency 1158 if ( _empty( d ) ) { 1159 return true; 1160 } 1161 1162 if ( decimalPoint && strType ) { 1163 d = _numToDecimal( d, decimalPoint ); 1164 } 1165 1166 if ( formatted && strType ) { 1167 d = d.replace( _re_formatted_numeric, '' ); 1168 } 1169 1170 return !isNaN( parseFloat(d) ) && isFinite( d ); 1171 }; 1172 1173 1174 // A string without HTML in it can be considered to be HTML still 1175 var _isHtml = function ( d ) { 1176 return _empty( d ) || typeof d === 'string'; 1177 }; 1178 1179 // Is a string a number surrounded by HTML? 1180 var _htmlNumeric = function ( d, decimalPoint, formatted ) { 1181 if ( _empty( d ) ) { 1182 return true; 1183 } 1184 1185 // input and select strings mean that this isn't just a number 1186 if (typeof d === 'string' && d.match(/<(input|select)/i)) { 1187 return null; 1188 } 1189 1190 var html = _isHtml( d ); 1191 return ! html ? 1192 null : 1193 _isNumber( _stripHtml( d ), decimalPoint, formatted ) ? 1194 true : 1195 null; 1196 }; 1197 1198 1199 var _pluck = function ( a, prop, prop2 ) { 1200 var out = []; 1201 var i=0, ien=a.length; 1202 1203 // Could have the test in the loop for slightly smaller code, but speed 1204 // is essential here 1205 if ( prop2 !== undefined ) { 1206 for ( ; i<ien ; i++ ) { 1207 if ( a[i] && a[i][ prop ] ) { 1208 out.push( a[i][ prop ][ prop2 ] ); 1209 } 1210 } 1211 } 1212 else { 1213 for ( ; i<ien ; i++ ) { 1214 if ( a[i] ) { 1215 out.push( a[i][ prop ] ); 1216 } 1217 } 1218 } 1219 1220 return out; 1221 }; 1222 1223 1224 // Basically the same as _pluck, but rather than looping over `a` we use `order` 1225 // as the indexes to pick from `a` 1226 var _pluck_order = function ( a, order, prop, prop2 ) 1227 { 1228 var out = []; 1229 var i=0, ien=order.length; 1230 1231 // Could have the test in the loop for slightly smaller code, but speed 1232 // is essential here 1233 if ( prop2 !== undefined ) { 1234 for ( ; i<ien ; i++ ) { 1235 if ( a[ order[i] ][ prop ] ) { 1236 out.push( a[ order[i] ][ prop ][ prop2 ] ); 1237 } 1238 } 1239 } 1240 else { 1241 for ( ; i<ien ; i++ ) { 1242 if ( a[ order[i] ] ) { 1243 out.push( a[ order[i] ][ prop ] ); 1244 } 1245 } 1246 } 1247 1248 return out; 1249 }; 1250 1251 1252 var _range = function ( len, start ) 1253 { 1254 var out = []; 1255 var end; 1256 1257 if ( start === undefined ) { 1258 start = 0; 1259 end = len; 1260 } 1261 else { 1262 end = start; 1263 start = len; 1264 } 1265 1266 for ( var i=start ; i<end ; i++ ) { 1267 out.push( i ); 1268 } 1269 1270 return out; 1271 }; 1272 1273 1274 var _removeEmpty = function ( a ) 1275 { 1276 var out = []; 1277 1278 for ( var i=0, ien=a.length ; i<ien ; i++ ) { 1279 if ( a[i] ) { // careful - will remove all falsy values! 1280 out.push( a[i] ); 1281 } 1282 } 1283 1284 return out; 1285 }; 1286 1287 // Replaceable function in api.util 1288 var _stripHtml = function (input) { 1289 // Irrelevant check to workaround CodeQL's false positive on the regex 1290 if (input.length > _max_str_len) { 1291 throw new Error('Exceeded max str len'); 1292 } 1293 1294 var previous; 1295 1296 input = input.replace(_re_html, ''); // Complete tags 1297
1298 // Safety for incomplete script tag - use do / while to ensure that 1299 // we get all instances 1300 do { 1301 previous = input; 1302 input = input.replace(/<script/i, ''); 1303 } while (input !== previous); 1304 1305 return previous; 1306 }; 1307 1308 // Replaceable function in api.util 1309 var _escapeHtml = function ( d ) { 1310 if (Array.isArray(d)) { 1311 d = d.join(','); 1312 } 1313 1314 return typeof d === 'string' ? 1315 d 1316 .replace(/&/g, '&') 1317 .replace(/</g, '<') 1318 .replace(/>/g, '>') 1319 .replace(/"/g, '"') : 1320 d; 1321 }; 1322 1323 // Remove diacritics from a string by decomposing it and then removing 1324 // non-ascii characters 1325 var _normalize = function (str, both) { 1326 if (typeof str !== 'string') { 1327 return str; 1328 } 1329 1330 // It is faster to just run `normalize` than it is to check if 1331 // we need to with a regex! 1332 var res = str.normalize("NFD"); 1333 1334 // Equally, here we check if a regex is needed or not 1335 return res.length !== str.length 1336 ? (both === true ? str + ' ' : '' ) + res.replace(/[\u0300-\u036f]/g, "") 1337 : res; 1338 } 1339 1340 /** 1341 * Determine if all values in the array are unique. This means we can short 1342 * cut the _unique method at the cost of a single loop. A sorted array is used 1343 * to easily check the values. 1344 * 1345 * @param {array} src Source array 1346 * @return {boolean} true if all unique, false otherwise 1347 * @ignore 1348 */ 1349 var _areAllUnique = function ( src ) { 1350 if ( src.length < 2 ) { 1351 return true; 1352 } 1353 1354 var sorted = src.slice().sort(); 1355 var last = sorted[0]; 1356 1357 for ( var i=1, ien=sorted.length ; i<ien ; i++ ) { 1358 if ( sorted[i] === last ) { 1359 return false; 1360 } 1361 1362 last = sorted[i]; 1363 } 1364 1365 return true; 1366 }; 1367 1368 1369 /** 1370 * Find the unique elements in a source array. 1371 * 1372 * @param {array} src Source array 1373 * @return {array} Array of unique items 1374 * @ignore 1375 */ 1376 var _unique = function ( src ) 1377 { 1378 if (Array.from && Set) { 1379 return Array.from(new Set(src)); 1380 } 1381 1382 if ( _areAllUnique( src ) ) { 1383 return src.slice(); 1384 } 1385 1386 // A faster unique method is to use object keys to identify used values, 1387 // but this doesn't work with arrays or objects, which we must also 1388 // consider. See jsperf.app/compare-array-unique-versions/4 for more 1389 // information. 1390 var 1391 out = [], 1392 val, 1393 i, ien=src.length, 1394 j, k=0; 1395 1396 again: for ( i=0 ; i<ien ; i++ ) { 1397 val = src[i]; 1398 1399 for ( j=0 ; j<k ; j++ ) { 1400 if ( out[j] === val ) { 1401 continue again; 1402 } 1403 } 1404 1405 out.push( val ); 1406 k++; 1407 } 1408 1409 return out; 1410 }; 1411 1412 // Surprisingly this is faster than [].concat.apply 1413 // https://jsperf.com/flatten-an-array-loop-vs-reduce/2 1414 var _flatten = function (out, val) { 1415 if (Array.isArray(val)) { 1416 for (var i=0 ; i<val.length ; i++) { 1417 _flatten(out, val[i]); 1418 } 1419 } 1420 else { 1421 out.push(val); 1422 } 1423 1424 return out; 1425 } 1426 1427 // Similar to jQuery's addClass, but use classList.add 1428 function _addClass(el, name) { 1429 if (name) { 1430 name.split(' ').forEach(function (n) { 1431 if (n) { 1432 // `add` does deduplication, so no need to check `contains` 1433 el.classList.add(n); 1434 } 1435 }); 1436 } 1437 } 1438 1439 /** 1440 * DataTables utility methods 1441 * 1442 * This namespace provides helper methods that DataTables uses internally to 1443 * create a DataTable, but which are not exclusively used only for DataTables. 1444 * These methods can be used by extension authors to save the duplication of 1445 * code. 1446 * 1447 * @namespace 1448 */ 1449 DataTable.util = { 1450 /** 1451 * Return a string with diacritic characters decomposed 1452 * @param {*} mixed Function or string to normalize 1453 * @param {*} both Return original string and the normalized string 1454 * @returns String or undefined 1455 */ 1456 diacritics: function (mixed, both) { 1457 var type = typeof mixed; 1458 1459 if (type !== 'function') { 1460 return _normalize(mixed, both); 1461 } 1462 _normalize = mixed; 1463 }, 1464 1465 /** 1466 * Debounce a function 1467 * 1468 * @param {function} fn Function to be called 1469 * @param {integer} freq Call frequency in mS 1470 * @return {function} Wrapped function 1471 */ 1472 debounce: function ( fn, timeout ) { 1473 var timer; 1474 1475 return function () { 1476 var that = this; 1477 var args = arguments; 1478 1479 clearTimeout(timer); 1480 1481 timer = setTimeout( function () { 1482 fn.apply(that, args); 1483 }, timeout || 250 ); 1484 }; 1485 }, 1486 1487 /** 1488 * Throttle the calls to a function. Arguments and context are maintained 1489 * for the throttled function. 1490 * 1491 * @param {function} fn Function to be called 1492 * @param {integer} freq Call frequency in mS 1493 * @return {function} Wrapped function 1494 */ 1495 throttle: function ( fn, freq ) { 1496 var 1497 frequency = freq !== undefined ? freq : 200, 1498 last, 1499 timer; 1500 1501 return function () { 1502 var 1503 that = this, 1504 now = +new Date(), 1505 args = arguments; 1506 1507 if ( last && now < last + frequency ) { 1508 clearTimeout( timer ); 1509 1510 timer = setTimeout( function () { 1511 last = undefined; 1512 fn.apply( that, args ); 1513 }, frequency ); 1514 } 1515 else { 1516 last = now; 1517 fn.apply( that, args ); 1518 } 1519 }; 1520 }, 1521 1522 /** 1523 * Escape a string such that it can be used in a regular expression 1524 * 1525 * @param {string} val string to escape 1526 * @returns {string} escaped string 1527 */ 1528 escapeRegex: function ( val ) { 1529 return val.replace( _re_escape_regex, '\\$1' ); 1530 }, 1531 1532 /** 1533 * Create a function that will write to a nested object or array 1534 * @param {*} source JSON notation string 1535 * @returns Write function 1536 */ 1537 set: function ( source ) { 1538 if ( $.isPlainObject( source ) ) { 1539 /* Unlike get, only the underscore (global) option is used for for 1540 * setting data since we don't know the type here. This is why an object 1541 * option is not documented for `mData` (which is read/write), but it is 1542 * for `mRender` which is read only. 1543 */ 1544 return DataTable.util.set( source._ ); 1545 } 1546 else if ( source === null ) { 1547 // Nothing to do when the data source is null 1548 return function () {}; 1549 } 1550 else if ( typeof source === 'function' ) { 1551 return function (data, val, meta) { 1552 source( data, 'set', val, meta ); 1553 }; 1554 } 1555 else if ( 1556 typeof source === 'string' && (source.indexOf('.') !== -1 || 1557 source.indexOf('[') !== -1 || source.indexOf('(') !== -1) 1558 ) { 1559 // Like the get, we need to get data from a nested object 1560 var setData = function (data, val, src) { 1561 var a = _fnSplitObjNotation( src ), b; 1562 var aLast = a[a.length-1]; 1563 var arrayNotation, funcNotation, o, innerSrc; 1564 1565 for ( var i=0, iLen=a.length-1 ; i<iLen ; i++ ) { 1566 // Protect against prototype pollution 1567 if (a[i] === '__proto__' || a[i] === 'constructor') { 1568 throw new Error('Cannot set prototype values'); 1569 } 1570 1571 // Check if we are dealing with an array notation request 1572 arrayNotation = a[i].match(__reArray); 1573 funcNotation = a[i].match(__reFn); 1574 1575 if ( arrayNotation ) { 1576 a[i] = a[i].replace(__reArray, ''); 1577 data[ a[i] ] = []; 1578 1579 // Get the remainder of the nested object to set so we can recurse 1580 b = a.slice(); 1581 b.splice( 0, i+1 ); 1582 innerSrc = b.join('.'); 1583 1584 // Traverse each entry in the array setting the properties requested 1585 if ( Array.isArray( val ) ) { 1586 for ( var j=0, jLen=val.length ; j<jLen ; j++ ) { 1587 o = {}; 1588 setData( o, val[j], innerSrc ); 1589 data[ a[i] ].push( o ); 1590 } 1591 } 1592 else { 1593 // We've been asked to save data to an array, but it 1594 // isn't array data to be saved. Best that can be done 1595 // is to just save the value. 1596 data[ a[i] ] = val; 1597 } 1598 1599 // The inner call to setData has already traversed through the remainder 1600 // of the source and has set the data, thus we can exit here 1601 return; 1602 } 1603 else if ( funcNotation ) { 1604 // Function call 1605 a[i] = a[i].replace(__reFn, ''); 1606 data = data[ a[i] ]( val ); 1607 } 1608 1609 // If the nested object doesn't currently exist - since we are 1610 // trying to set the value - create it 1611 if ( data[ a[i] ] === null || data[ a[i] ] === undefined ) { 1612 data[ a[i] ] = {}; 1613 } 1614 data = data[ a[i] ]; 1615 } 1616 1617 // Last item in the input - i.e, the actual set 1618 if ( aLast.match(__reFn ) ) { 1619 // Function call 1620 data = data[ aLast.replace(__reFn, '') ]( val ); 1621 } 1622 else { 1623 // If array notation is used, we just want to strip it and use the property name 1624 // and assign the value. If it isn't used, then we get the result we want anyway 1625 data[ aLast.replace(__reArray, '') ] = val; 1626 } 1627 }; 1628 1629 return function (data, val) { // meta is also passed in, but not used 1630 return setData( data, val, source ); 1631 }; 1632 } 1633 else { 1634 // Array or flat object mapping 1635 return function (data, val) { // meta is also passed in, but not used 1636 data[source] = val; 1637 }; 1638 } 1639 }, 1640 1641 /** 1642 * Create a function that will read nested objects from arrays, based on JSON notation 1643 * @param {*} source JSON notation string 1644 * @returns Value read 1645 */ 1646 get: function ( source ) { 1647 if ( $.isPlainObject( source ) ) { 1648 // Build an object of get functions, and wrap them in a single call 1649 var o = {}; 1650 $.each( source, function (key, val) { 1651 if ( val ) { 1652 o[key] = DataTable.util.get( val ); 1653 } 1654 } ); 1655 1656 return function (data, type, row, meta) { 1657 var t = o[type] || o._; 1658 return t !== undefined ? 1659 t(data, type, row, meta) : 1660 data; 1661 }; 1662 } 1663 else if ( source === null ) { 1664 // Give an empty string for rendering / sorting etc 1665 return function (data) { // type, row and meta also passed, but not used 1666 return data; 1667 }; 1668 } 1669 else if ( typeof source === 'function' ) { 1670 return function (data, type, row, meta) { 1671 return source( data, type, row, meta ); 1672 }; 1673 } 1674 else if ( 1675 typeof source === 'string' && (source.indexOf('.') !== -1 || 1676 source.indexOf('[') !== -1 || source.indexOf('(') !== -1) 1677 ) { 1678 /* If there is a . in the source string then the data source is in a 1679 * nested object so we loop over the data for each level to get the next 1680 * level down. On each loop we test for undefined, and if found immediately 1681 * return. This allows entire objects to be missing and sDefaultContent to 1682 * be used if defined, rather than throwing an error 1683 */
1684 var fetchData = function (data, type, src) { 1685 var arrayNotation, funcNotation, out, innerSrc; 1686 1687 if ( src !== "" ) { 1688 var a = _fnSplitObjNotation( src ); 1689 1690 for ( var i=0, iLen=a.length ; i<iLen ; i++ ) { 1691 // Check if we are dealing with special notation 1692 arrayNotation = a[i].match(__reArray); 1693 funcNotation = a[i].match(__reFn); 1694 1695 if ( arrayNotation ) { 1696 // Array notation 1697 a[i] = a[i].replace(__reArray, ''); 1698 1699 // Condition allows simply [] to be passed in 1700 if ( a[i] !== "" ) { 1701 data = data[ a[i] ]; 1702 } 1703 out = []; 1704 1705 // Get the remainder of the nested object to get 1706 a.splice( 0, i+1 ); 1707 innerSrc = a.join('.'); 1708 1709 // Traverse each entry in the array getting the properties requested 1710 if ( Array.isArray( data ) ) { 1711 for ( var j=0, jLen=data.length ; j<jLen ; j++ ) { 1712 out.push( fetchData( data[j], type, innerSrc ) ); 1713 } 1714 } 1715 1716 // If a string is given in between the array notation indicators, that 1717 // is used to join the strings together, otherwise an array is returned 1718 var join = arrayNotation[0].substring(1, arrayNotation[0].length-1); 1719 data = (join==="") ? out : out.join(join); 1720 1721 // The inner call to fetchData has already traversed through the remainder 1722 // of the source requested, so we exit from the loop 1723 break; 1724 } 1725 else if ( funcNotation ) { 1726 // Function call 1727 a[i] = a[i].replace(__reFn, ''); 1728 data = data[ a[i] ](); 1729 continue; 1730 } 1731 1732 if (data === null || data[ a[i] ] === null) { 1733 return null; 1734 } 1735 else if ( data === undefined || data[ a[i] ] === undefined ) { 1736 return undefined; 1737 } 1738 1739 data = data[ a[i] ]; 1740 } 1741 } 1742 1743 return data; 1744 }; 1745 1746 return function (data, type) { // row and meta also passed, but not used 1747 return fetchData( data, type, source ); 1748 }; 1749 } 1750 else { 1751 // Array or flat object mapping 1752 return function (data) { // row and meta also passed, but not used 1753 return data[source]; 1754 }; 1755 } 1756 }, 1757 1758 stripHtml: function (mixed) { 1759 var type = typeof mixed; 1760 1761 if (type === 'function') { 1762 _stripHtml = mixed; 1763 return; 1764 } 1765 else if (type === 'string') { 1766 return _stripHtml(mixed); 1767 } 1768 return mixed; 1769 }, 1770 1771 escapeHtml: function (mixed) { 1772 var type = typeof mixed; 1773 1774 if (type === 'function') { 1775 _escapeHtml = mixed; 1776 return; 1777 } 1778 else if (type === 'string' || Array.isArray(mixed)) { 1779 return _escapeHtml(mixed); 1780 } 1781 return mixed; 1782 }, 1783 1784 unique: _unique 1785 }; 1786 1787 1788 1789 /** 1790 * Create a mapping object that allows camel case parameters to be looked up 1791 * for their Hungarian counterparts. The mapping is stored in a private 1792 * parameter called `_hungarianMap` which can be accessed on the source object. 1793 * @param {object} o 1794 * @memberof DataTable#oApi 1795 */ 1796 function _fnHungarianMap ( o ) 1797 { 1798 var 1799 hungarian = 'a aa ai ao as b fn i m o s ', 1800 match, 1801 newKey, 1802 map = {}; 1803 1804 $.each( o, function (key) { 1805 match = key.match(/^([^A-Z]+?)([A-Z])/); 1806 1807 if ( match && hungarian.indexOf(match[1]+' ') !== -1 ) 1808 { 1809 newKey = key.replace( match[0], match[2].toLowerCase() ); 1810 map[ newKey ] = key; 1811 1812 if ( match[1] === 'o' ) 1813 { 1814 _fnHungarianMap( o[key] ); 1815 } 1816 } 1817 } ); 1818 1819 o._hungarianMap = map; 1820 } 1821 1822 1823 /** 1824 * Convert from camel case parameters to Hungarian, based on a Hungarian map 1825 * created by _fnHungarianMap. 1826 * @param {object} src The model object which holds all parameters that can be 1827 * mapped. 1828 * @param {object} user The object to convert from camel case to Hungarian. 1829 * @param {boolean} force When set to `true`, properties which already have a 1830 * Hungarian value in the `user` object will be overwritten. Otherwise they 1831 * won't be. 1832 * @memberof DataTable#oApi 1833 */ 1834 function _fnCamelToHungarian ( src, user, force ) 1835 { 1836 if ( ! src._hungarianMap ) { 1837 _fnHungarianMap( src ); 1838 } 1839 1840 var hungarianKey; 1841 1842 $.each( user, function (key) { 1843 hungarianKey = src._hungarianMap[ key ]; 1844 1845 if ( hungarianKey !== undefined && (force || user[hungarianKey] === undefined) ) 1846 { 1847 // For objects, we need to buzz down into the object to copy parameters 1848 if ( hungarianKey.charAt(0) === 'o' ) 1849 { 1850 // Copy the camelCase options over to the hungarian 1851 if ( ! user[ hungarianKey ] ) { 1852 user[ hungarianKey ] = {}; 1853 } 1854 $.extend( true, user[hungarianKey], user[key] ); 1855 1856 _fnCamelToHungarian( src[hungarianKey], user[hungarianKey], force ); 1857 } 1858 else { 1859 user[hungarianKey] = user[ key ]; 1860 } 1861 } 1862 } ); 1863 } 1864 1865 /** 1866 * Map one parameter onto another 1867 * @param {object} o Object to map 1868 * @param {*} knew The new parameter name 1869 * @param {*} old The old parameter name 1870 */ 1871 var _fnCompatMap = function ( o, knew, old ) { 1872 if ( o[ knew ] !== undefined ) { 1873 o[ old ] = o[ knew ]; 1874 } 1875 }; 1876 1877 1878 /** 1879 * Provide backwards compatibility for the main DT options. Note that the new 1880 * options are mapped onto the old parameters, so this is an external interface 1881 * change only. 1882 * @param {object} init Object to map 1883 */ 1884 function _fnCompatOpts ( init ) 1885 { 1886 _fnCompatMap( init, 'ordering', 'bSort' ); 1887 _fnCompatMap( init, 'orderMulti', 'bSortMulti' ); 1888 _fnCompatMap( init, 'orderClasses', 'bSortClasses' ); 1889 _fnCompatMap( init, 'orderCellsTop', 'bSortCellsTop' ); 1890 _fnCompatMap( init, 'order', 'aaSorting' ); 1891 _fnCompatMap( init, 'orderFixed', 'aaSortingFixed' ); 1892 _fnCompatMap( init, 'paging', 'bPaginate' ); 1893 _fnCompatMap( init, 'pagingType', 'sPaginationType' ); 1894 _fnCompatMap( init, 'pageLength', 'iDisplayLength' ); 1895 _fnCompatMap( init, 'searching', 'bFilter' ); 1896 1897 // Boolean initialisation of x-scrolling 1898 if ( typeof init.sScrollX === 'boolean' ) { 1899 init.sScrollX = init.sScrollX ? '100%' : ''; 1900 } 1901 if ( typeof init.scrollX === 'boolean' ) { 1902 init.scrollX = init.scrollX ? '100%' : ''; 1903 } 1904 1905 // Column search objects are in an array, so it needs to be converted 1906 // element by element 1907 var searchCols = init.aoSearchCols; 1908 1909 if ( searchCols ) { 1910 for ( var i=0, ien=searchCols.length ; i<ien ; i++ ) { 1911 if ( searchCols[i] ) { 1912 _fnCamelToHungarian( DataTable.models.oSearch, searchCols[i] ); 1913 } 1914 } 1915 } 1916 1917 // Enable search delay if server-side processing is enabled 1918 if (init.serverSide && ! init.searchDelay) { 1919 init.searchDelay = 400; 1920 } 1921 } 1922 1923 1924 /** 1925 * Provide backwards compatibility for column options. Note that the new options 1926 * are mapped onto the old parameters, so this is an external interface change 1927 * only. 1928 * @param {object} init Object to map 1929 */ 1930 function _fnCompatCols ( init ) 1931 { 1932 _fnCompatMap( init, 'orderable', 'bSortable' ); 1933 _fnCompatMap( init, 'orderData', 'aDataSort' ); 1934 _fnCompatMap( init, 'orderSequence', 'asSorting' ); 1935 _fnCompatMap( init, 'orderDataType', 'sortDataType' ); 1936 1937 // orderData can be given as an integer 1938 var dataSort = init.aDataSort; 1939 if ( typeof dataSort === 'number' && ! Array.isArray( dataSort ) ) { 1940 init.aDataSort = [ dataSort ]; 1941 } 1942 } 1943 1944 1945 /** 1946 * Browser feature detection for capabilities, quirks 1947 * @param {object} settings dataTables settings object 1948 * @memberof DataTable#oApi 1949 */ 1950 function _fnBrowserDetect( settings ) 1951 { 1952 // We don't need to do this every time DataTables is constructed, the values 1953 // calculated are specific to the browser and OS configuration which we 1954 // don't expect to change between initialisations 1955 if ( ! DataTable.__browser ) { 1956 var browser = {}; 1957 DataTable.__browser = browser; 1958 1959 // Scrolling feature / quirks detection 1960 var n = $('<div/>') 1961 .css( { 1962 position: 'fixed', 1963 top: 0, 1964 left: -1 * window.pageXOffset, // allow for scrolling 1965 height: 1, 1966 width: 1, 1967 overflow: 'hidden' 1968 } ) 1969 .append( 1970 $('<div/>') 1971 .css( { 1972 position: 'absolute', 1973 top: 1, 1974 left: 1, 1975 width: 100, 1976 overflow: 'scroll' 1977 } ) 1978 .append( 1979 $('<div/>') 1980 .css( { 1981 width: '100%', 1982 height: 10 1983 } ) 1984 ) 1985 ) 1986 .appendTo( 'body' ); 1987 1988 var outer = n.children();
1989 var inner = outer.children(); 1990 1991 // Get scrollbar width 1992 browser.barWidth = outer[0].offsetWidth - outer[0].clientWidth; 1993 1994 // In rtl text layout, some browsers (most, but not all) will place the 1995 // scrollbar on the left, rather than the right. 1996 browser.bScrollbarLeft = Math.round( inner.offset().left ) !== 1; 1997 1998 n.remove(); 1999 } 2000 2001 $.extend( settings.oBrowser, DataTable.__browser ); 2002 settings.oScroll.iBarWidth = DataTable.__browser.barWidth; 2003 } 2004 2005 /** 2006 * Add a column to the list used for the table with default values 2007 * @param {object} oSettings dataTables settings object 2008 * @memberof DataTable#oApi 2009 */ 2010 function _fnAddColumn( oSettings ) 2011 { 2012 // Add column to aoColumns array 2013 var oDefaults = DataTable.defaults.column; 2014 var iCol = oSettings.aoColumns.length; 2015 var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, { 2016 "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol], 2017 "mData": oDefaults.mData ? oDefaults.mData : iCol, 2018 idx: iCol, 2019 searchFixed: {}, 2020 colEl: $('<col>').attr('data-dt-column', iCol) 2021 } ); 2022 oSettings.aoColumns.push( oCol ); 2023 2024 // Add search object for column specific search. Note that the `searchCols[ iCol ]` 2025 // passed into extend can be undefined. This allows the user to give a default 2026 // with only some of the parameters defined, and also not give a default 2027 var searchCols = oSettings.aoPreSearchCols; 2028 searchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch, searchCols[ iCol ] ); 2029 } 2030 2031 2032 /** 2033 * Apply options for a column 2034 * @param {object} oSettings dataTables settings object 2035 * @param {int} iCol column index to consider 2036 * @param {object} oOptions object with sType, bVisible and bSearchable etc 2037 * @memberof DataTable#oApi 2038 */ 2039 function _fnColumnOptions( oSettings, iCol, oOptions ) 2040 { 2041 var oCol = oSettings.aoColumns[ iCol ]; 2042 2043 /* User specified column options */ 2044 if ( oOptions !== undefined && oOptions !== null ) 2045 { 2046 // Backwards compatibility 2047 _fnCompatCols( oOptions ); 2048 2049 // Map camel case parameters to their Hungarian counterparts 2050 _fnCamelToHungarian( DataTable.defaults.column, oOptions, true ); 2051 2052 /* Backwards compatibility for mDataProp */ 2053 if ( oOptions.mDataProp !== undefined && !oOptions.mData ) 2054 { 2055 oOptions.mData = oOptions.mDataProp; 2056 } 2057 2058 if ( oOptions.sType ) 2059 { 2060 oCol._sManualType = oOptions.sType; 2061 } 2062 2063 // `class` is a reserved word in Javascript, so we need to provide 2064 // the ability to use a valid name for the camel case input 2065 if ( oOptions.className && ! oOptions.sClass ) 2066 { 2067 oOptions.sClass = oOptions.className; 2068 } 2069 2070 var origClass = oCol.sClass; 2071 2072 $.extend( oCol, oOptions ); 2073 _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" ); 2074 2075 // Merge class from previously defined classes with this one, rather than just 2076 // overwriting it in the extend above 2077 if (origClass !== oCol.sClass) { 2078 oCol.sClass = origClass + ' ' + oCol.sClass; 2079 } 2080 2081 /* iDataSort to be applied (backwards compatibility), but aDataSort will take 2082 * priority if defined 2083 */ 2084 if ( oOptions.iDataSort !== undefined ) 2085 { 2086 oCol.aDataSort = [ oOptions.iDataSort ]; 2087 } 2088 _fnMap( oCol, oOptions, "aDataSort" ); 2089 } 2090 2091 /* Cache the data get and set functions for speed */ 2092 var mDataSrc = oCol.mData; 2093 var mData = _fnGetObjectDataFn( mDataSrc ); 2094 2095 // The `render` option can be given as an array to access the helper rendering methods. 2096 // The first element is the rendering method to use, the rest are the parameters to pass 2097 if ( oCol.mRender && Array.isArray( oCol.mRender ) ) { 2098 var copy = oCol.mRender.slice(); 2099 var name = copy.shift(); 2100 2101 oCol.mRender = DataTable.render[name].apply(window, copy); 2102 } 2103 2104 oCol._render = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null; 2105 2106 var attrTest = function( src ) { 2107 return typeof src === 'string' && src.indexOf('@') !== -1; 2108 }; 2109 oCol._bAttrSrc = $.isPlainObject( mDataSrc ) && ( 2110 attrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter) 2111 ); 2112 oCol._setter = null; 2113 2114 oCol.fnGetData = function (rowData, type, meta) { 2115 var innerData = mData( rowData, type, undefined, meta ); 2116 2117 return oCol._render && type ? 2118 oCol._render( innerData, type, rowData, meta ) : 2119 innerData; 2120 }; 2121 oCol.fnSetData = function ( rowData, val, meta ) { 2122 return _fnSetObjectDataFn( mDataSrc )( rowData, val, meta ); 2123 }; 2124 2125 // Indicate if DataTables should read DOM data as an object or array 2126 // Used in _fnGetRowElements 2127 if ( typeof mDataSrc !== 'number' && ! oCol._isArrayHost ) { 2128 oSettings._rowReadObject = true; 2129 } 2130 2131 /* Feature sorting overrides column specific when off */ 2132 if ( !oSettings.oFeatures.bSort ) 2133 { 2134 oCol.bSortable = false; 2135 } 2136 } 2137 2138 2139 /** 2140 * Adjust the table column widths for new data. Note: you would probably want to 2141 * do a redraw after calling this function! 2142 * @param {object} settings dataTables settings object 2143 * @memberof DataTable#oApi 2144 */ 2145 function _fnAdjustColumnSizing ( settings ) 2146 { 2147 _fnCalculateColumnWidths( settings ); 2148 _fnColumnSizes( settings ); 2149 2150 var scroll = settings.oScroll;
2151 if ( scroll.sY !== '' || scroll.sX !== '') { 2152 _fnScrollDraw( settings ); 2153 } 2154 2155 _fnCallbackFire( settings, null, 'column-sizing', [settings] ); 2156 } 2157 2158 /** 2159 * Apply column sizes 2160 * 2161 * @param {*} settings DataTables settings object 2162 */ 2163 function _fnColumnSizes ( settings ) 2164 { 2165 var cols = settings.aoColumns; 2166 2167 for (var i=0 ; i<cols.length ; i++) { 2168 var width = _fnColumnsSumWidth(settings, [i], false, false); 2169 2170 cols[i].colEl.css('width', width); 2171 } 2172 } 2173 2174 2175 /** 2176 * Convert the index of a visible column to the index in the data array (take account 2177 * of hidden columns) 2178 * @param {object} oSettings dataTables settings object 2179 * @param {int} iMatch Visible column index to lookup 2180 * @returns {int} i the data index 2181 * @memberof DataTable#oApi 2182 */ 2183 function _fnVisibleToColumnIndex( oSettings, iMatch ) 2184 { 2185 var aiVis = _fnGetColumns( oSettings, 'bVisible' ); 2186 2187 return typeof aiVis[iMatch] === 'number' ? 2188 aiVis[iMatch] : 2189 null; 2190 } 2191 2192 2193 /** 2194 * Convert the index of an index in the data array and convert it to the visible 2195 * column index (take account of hidden columns) 2196 * @param {int} iMatch Column index to lookup 2197 * @param {object} oSettings dataTables settings object 2198 * @returns {int} i the data index 2199 * @memberof DataTable#oApi 2200 */ 2201 function _fnColumnIndexToVisible( oSettings, iMatch ) 2202 { 2203 var aiVis = _fnGetColumns( oSettings, 'bVisible' ); 2204 var iPos = aiVis.indexOf(iMatch); 2205 2206 return iPos !== -1 ? iPos : null; 2207 } 2208 2209 2210 /** 2211 * Get the number of visible columns 2212 * @param {object} oSettings dataTables settings object 2213 * @returns {int} i the number of visible columns 2214 * @memberof DataTable#oApi 2215 */ 2216 function _fnVisbleColumns( settings ) 2217 { 2218 var layout = settings.aoHeader; 2219 var columns = settings.aoColumns; 2220 var vis = 0; 2221 2222 if ( layout.length ) { 2223 for ( var i=0, ien=layout[0].length ; i<ien ; i++ ) { 2224 if ( columns[i].bVisible && $(layout[0][i].cell).css('display') !== 'none' ) { 2225 vis++; 2226 } 2227 } 2228 } 2229 2230 return vis; 2231 } 2232 2233 2234 /** 2235 * Get an array of column indexes that match a given property 2236 * @param {object} oSettings dataTables settings object 2237 * @param {string} sParam Parameter in aoColumns to look for - typically 2238 * bVisible or bSearchable 2239 * @returns {array} Array of indexes with matched properties 2240 * @memberof DataTable#oApi 2241 */ 2242 function _fnGetColumns( oSettings, sParam ) 2243 { 2244 var a = []; 2245 2246 oSettings.aoColumns.map( function(val, i) { 2247 if ( val[sParam] ) { 2248 a.push( i ); 2249 } 2250 } ); 2251 2252 return a; 2253 } 2254 2255 2256 /** 2257 * Calculate the 'type' of a column 2258 * @param {object} settings dataTables settings object 2259 * @memberof DataTable#oApi 2260 */ 2261 function _fnColumnTypes ( settings ) 2262 { 2263 var columns = settings.aoColumns; 2264 var data = settings.aoData; 2265 var types = DataTable.ext.type.detect; 2266 var i, ien, j, jen, k, ken; 2267 var col, detectedType, cache; 2268 2269 // For each column, spin over the 2270 for ( i=0, ien=columns.length ; i<ien ; i++ ) { 2271 col = columns[i]; 2272 cache = []; 2273 2274 if ( ! col.sType && col._sManualType ) { 2275 col.sType = col._sManualType; 2276 } 2277 else if ( ! col.sType ) { 2278 for ( j=0, jen=types.length ; j<jen ; j++ ) { 2279 for ( k=0, ken=data.length ; k<ken ; k++ ) { 2280 2281 if (! data[k]) { 2282 continue; 2283 } 2284 2285 // Use a cache array so we only need to get the type data 2286 // from the formatter once (when using multiple detectors) 2287 if ( cache[k] === undefined ) { 2288 cache[k] = _fnGetCellData( settings, k, i, 'type' ); 2289 } 2290 2291 detectedType = types[j]( cache[k], settings ); 2292 2293 // If null, then this type can't apply to this column, so 2294 // rather than testing all cells, break out. There is an 2295 // exception for the last type which is `html`. We need to 2296 // scan all rows since it is possible to mix string and HTML 2297 // types 2298 if ( ! detectedType && j !== types.length-2 ) { 2299 break; 2300 } 2301 2302 // Only a single match is needed for html type since it is 2303 // bottom of the pile and very similar to string - but it 2304 // must not be empty 2305 if ( detectedType === 'html' && ! _empty(cache[k]) ) { 2306 break; 2307 } 2308 } 2309 2310 // Type is valid for all data points in the column - use this 2311 // type 2312 if ( detectedType ) {
2313 col.sType = detectedType; 2314 break; 2315 } 2316 } 2317 2318 // Fall back - if no type was detected, always use string 2319 if ( ! col.sType ) { 2320 col.sType = 'string'; 2321 } 2322 } 2323 2324 // Set class names for header / footer for auto type classes 2325 var autoClass = _ext.type.className[col.sType]; 2326 2327 if (autoClass) { 2328 _columnAutoClass(settings.aoHeader, i, autoClass); 2329 _columnAutoClass(settings.aoFooter, i, autoClass); 2330 } 2331 2332 var renderer = _ext.type.render[col.sType]; 2333 2334 // This can only happen once! There is no way to remover 2335 // a renderer. After the first time the renderer has 2336 // already been set so createTr will run the renderer itself. 2337 if (renderer && ! col._render) { 2338 col._render = DataTable.util.get(renderer); 2339 2340 _columnAutoRender(settings, i); 2341 } 2342 } 2343 } 2344 2345 /** 2346 * Apply an auto detected renderer to data which doesn't yet have 2347 * a renderer 2348 */ 2349 function _columnAutoRender(settings, colIdx) { 2350 var data = settings.aoData; 2351 2352 for (var i=0 ; i<data.length ; i++) { 2353 if (data[i].nTr) { 2354 // We have to update the display here since there is no 2355 // invalidation check for the data 2356 var display = _fnGetCellData( settings, i, colIdx, 'display' ); 2357 2358 data[i].displayData[colIdx] = display; 2359 _fnWriteCell(data[i].anCells[colIdx], display); 2360 2361 // No need to update sort / filter data since it has 2362 // been invalidated and will be re-read with the 2363 // renderer now applied 2364 } 2365 } 2366 } 2367 2368 /** 2369 * Apply a class name to a column's header cells 2370 */ 2371 function _columnAutoClass(container, colIdx, className) { 2372 container.forEach(function (row) { 2373 if (row[colIdx] && row[colIdx].unique) { 2374 _addClass(row[colIdx].cell, className); 2375 } 2376 }); 2377 } 2378 2379 /** 2380 * Take the column definitions and static columns arrays and calculate how 2381 * they relate to column indexes. The callback function will then apply the 2382 * definition found for a column to a suitable configuration object. 2383 * @param {object} oSettings dataTables settings object 2384 * @param {array} aoColDefs The aoColumnDefs array that is to be applied 2385 * @param {array} aoCols The aoColumns array that defines columns individually 2386 * @param {array} headerLayout Layout for header as it was loaded 2387 * @param {function} fn Callback function - takes two parameters, the calculated 2388 * column index and the definition for that column. 2389 * @memberof DataTable#oApi 2390 */ 2391 function _fnApplyColumnDefs( oSettings, aoColDefs, aoCols, headerLayout, fn ) 2392 { 2393 var i, iLen, j, jLen, k, kLen, def; 2394 var columns = oSettings.aoColumns; 2395 2396 if ( aoCols ) { 2397 for ( i=0, iLen=aoCols.length ; i<iLen ; i++ ) { 2398 if (aoCols[i] && aoCols[i].name) { 2399 columns[i].sName = aoCols[i].name; 2400 } 2401 } 2402 } 2403 2404 // Column definitions with aTargets 2405 if ( aoColDefs ) 2406 { 2407 /* Loop over the definitions array - loop in reverse so first instance has priority */ 2408 for ( i=aoColDefs.length-1 ; i>=0 ; i-- ) 2409 { 2410 def = aoColDefs[i]; 2411 2412 /* Each definition can target multiple columns, as it is an array */ 2413 var aTargets = def.target !== undefined 2414 ? def.target 2415 : def.targets !== undefined 2416 ? def.targets 2417 : def.aTargets; 2418 2419 if ( ! Array.isArray( aTargets ) ) 2420 { 2421 aTargets = [ aTargets ]; 2422 } 2423 2424 for ( j=0, jLen=aTargets.length ; j<jLen ; j++ ) 2425 { 2426 var target = aTargets[j]; 2427 2428 if ( typeof target === 'number' && target >= 0 ) 2429 { 2430 /* Add columns that we don't yet know about */ 2431 while( columns.length <= target ) 2432 { 2433 _fnAddColumn( oSettings ); 2434 } 2435 2436 /* Integer, basic index */ 2437 fn( target, def ); 2438 } 2439 else if ( typeof target === 'number' && target < 0 ) 2440 { 2441 /* Negative integer, right to left column counting */ 2442 fn( columns.length+target, def ); 2443 } 2444 else if ( typeof target === 'string' ) 2445 { 2446 for ( k=0, kLen=columns.length ; k<kLen ; k++ ) { 2447 if (target === '_all') { 2448 // Apply to all columns 2449 fn( k, def ); 2450 } 2451 else if (target.indexOf(':name') !== -1) { 2452 // Column selector 2453 if (columns[k].sName === target.replace(':name', '')) { 2454 fn( k, def ); 2455 } 2456 } 2457 else { 2458 // Cell selector 2459 headerLayout.forEach(function (row) { 2460 if (row[k]) { 2461 var cell = $(row[k].cell); 2462 2463 // Legacy support. Note that it means that we don't support 2464 // an element name selector only, since they are treated as
2465 // class names for 1.x compat. 2466 if (target.match(/^[a-z][\w-]*$/i)) { 2467 target = '.' + target; 2468 } 2469 2470 if (cell.is( target )) { 2471 fn( k, def ); 2472 } 2473 } 2474 }); 2475 } 2476 } 2477 } 2478 } 2479 } 2480 } 2481 2482 // Statically defined columns array 2483 if ( aoCols ) { 2484 for ( i=0, iLen=aoCols.length ; i<iLen ; i++ ) { 2485 fn( i, aoCols[i] ); 2486 } 2487 } 2488 } 2489 2490 2491 /** 2492 * Get the width for a given set of columns 2493 * 2494 * @param {*} settings DataTables settings object 2495 * @param {*} targets Columns - comma separated string or array of numbers 2496 * @param {*} original Use the original width (true) or calculated (false) 2497 * @param {*} incVisible Include visible columns (true) or not (false) 2498 * @returns Combined CSS value 2499 */ 2500 function _fnColumnsSumWidth( settings, targets, original, incVisible ) { 2501 if ( ! Array.isArray( targets ) ) { 2502 targets = _fnColumnsFromHeader( targets ); 2503 } 2504 2505 var sum = 0; 2506 var unit; 2507 var columns = settings.aoColumns; 2508 2509 for ( var i=0, ien=targets.length ; i<ien ; i++ ) { 2510 var column = columns[ targets[i] ]; 2511 var definedWidth = original ? 2512 column.sWidthOrig : 2513 column.sWidth; 2514 2515 if ( ! incVisible && column.bVisible === false ) { 2516 continue; 2517 } 2518 2519 if ( definedWidth === null || definedWidth === undefined ) { 2520 return null; // can't determine a defined width - browser defined 2521 } 2522 else if ( typeof definedWidth === 'number' ) { 2523 unit = 'px'; 2524 sum += definedWidth; 2525 } 2526 else { 2527 var matched = definedWidth.match(/([\d\.]+)([^\d]*)/); 2528 2529 if ( matched ) { 2530 sum += matched[1] * 1; 2531 unit = matched.length === 3 ? 2532 matched[2] : 2533 'px'; 2534 } 2535 } 2536 } 2537 2538 return sum + unit; 2539 } 2540 2541 function _fnColumnsFromHeader( cell ) 2542 { 2543 var attr = $(cell).closest('[data-dt-column]').attr('data-dt-column'); 2544 2545 if ( ! attr ) { 2546 return []; 2547 } 2548 2549 return attr.split(',').map( function (val) { 2550 return val * 1; 2551 } ); 2552 } 2553 /** 2554 * Add a data array to the table, creating DOM node etc. This is the parallel to 2555 * _fnGatherData, but for adding rows from a Javascript source, rather than a 2556 * DOM source. 2557 * @param {object} settings dataTables settings object 2558 * @param {array} data data array to be added 2559 * @param {node} [tr] TR element to add to the table - optional. If not given, 2560 * DataTables will create a row automatically 2561 * @param {array} [tds] Array of TD|TH elements for the row - must be given 2562 * if nTr is. 2563 * @returns {int} >=0 if successful (index of new aoData entry), -1 if failed 2564 * @memberof DataTable#oApi 2565 */ 2566 function _fnAddData ( settings, dataIn, tr, tds ) 2567 { 2568 /* Create the object for storing information about this new row */ 2569 var rowIdx = settings.aoData.length; 2570 var rowModel = $.extend( true, {}, DataTable.models.oRow, { 2571 src: tr ? 'dom' : 'data', 2572 idx: rowIdx 2573 } ); 2574 2575 rowModel._aData = dataIn; 2576 settings.aoData.push( rowModel ); 2577 2578 var columns = settings.aoColumns; 2579 2580 for ( var i=0, iLen=columns.length ; i<iLen ; i++ ) 2581 { 2582 // Invalidate the column types as the new data needs to be revalidated 2583 columns[i].sType = null; 2584 } 2585 2586 /* Add to the display array */ 2587 settings.aiDisplayMaster.push( rowIdx ); 2588 2589 var id = settings.rowIdFn( dataIn ); 2590 if ( id !== undefined ) { 2591 settings.aIds[ id ] = rowModel; 2592 } 2593 2594 /* Create the DOM information, or register it if already present */ 2595 if ( tr || ! settings.oFeatures.bDeferRender ) 2596 { 2597 _fnCreateTr( settings, rowIdx, tr, tds ); 2598 } 2599 2600 return rowIdx; 2601 } 2602 2603 2604 /** 2605 * Add one or more TR elements to the table. Generally we'd expect to 2606 * use this for reading data from a DOM sourced table, but it could be 2607 * used for an TR element. Note that if a TR is given, it is used (i.e. 2608 * it is not cloned). 2609 * @param {object} settings dataTables settings object 2610 * @param {array|node|jQuery} trs The TR element(s) to add to the table 2611 * @returns {array} Array of indexes for the added rows 2612 * @memberof DataTable#oApi 2613 */ 2614 function _fnAddTr( settings, trs ) 2615 { 2616 var row; 2617 2618 // Allow an individual node to be passed in 2619 if ( ! (trs instanceof $) ) { 2620 trs = $(trs); 2621 } 2622 2623 return trs.map( function (i, el) { 2624 row = _fnGetRowElements( settings, el ); 2625 return _fnAddData( settings, row.data, el, row.cells ); 2626 } ); 2627 } 2628 2629 2630 /**
2631 * Get the data for a given cell from the internal cache, taking into account data mapping 2632 * @param {object} settings dataTables settings object 2633 * @param {int} rowIdx aoData row id 2634 * @param {int} colIdx Column index 2635 * @param {string} type data get type ('display', 'type' 'filter|search' 'sort|order') 2636 * @returns {*} Cell data 2637 * @memberof DataTable#oApi 2638 */ 2639 function _fnGetCellData( settings, rowIdx, colIdx, type ) 2640 { 2641 if (type === 'search') { 2642 type = 'filter'; 2643 } 2644 else if (type === 'order') { 2645 type = 'sort'; 2646 } 2647 2648 var row = settings.aoData[rowIdx]; 2649 2650 if (! row) { 2651 return undefined; 2652 } 2653 2654 var draw = settings.iDraw; 2655 var col = settings.aoColumns[colIdx]; 2656 var rowData = row._aData; 2657 var defaultContent = col.sDefaultContent; 2658 var cellData = col.fnGetData( rowData, type, { 2659 settings: settings, 2660 row: rowIdx, 2661 col: colIdx 2662 } ); 2663 2664 // Allow for a node being returned for non-display types 2665 if (type !== 'display' && cellData && typeof cellData === 'object' && cellData.nodeName) { 2666 cellData = cellData.innerHTML; 2667 } 2668 2669 if ( cellData === undefined ) { 2670 if ( settings.iDrawError != draw && defaultContent === null ) { 2671 _fnLog( settings, 0, "Requested unknown parameter "+ 2672 (typeof col.mData=='function' ? '{function}' : "'"+col.mData+"'")+ 2673 " for row "+rowIdx+", column "+colIdx, 4 ); 2674 settings.iDrawError = draw; 2675 } 2676 return defaultContent; 2677 } 2678 2679 // When the data source is null and a specific data type is requested (i.e. 2680 // not the original data), we can use default column data 2681 if ( (cellData === rowData || cellData === null) && defaultContent !== null && type !== undefined ) { 2682 cellData = defaultContent; 2683 } 2684 else if ( typeof cellData === 'function' ) { 2685 // If the data source is a function, then we run it and use the return, 2686 // executing in the scope of the data object (for instances) 2687 return cellData.call( rowData ); 2688 } 2689 2690 if ( cellData === null && type === 'display' ) { 2691 return ''; 2692 } 2693 2694 if ( type === 'filter' ) { 2695 var fomatters = DataTable.ext.type.search; 2696 2697 if ( fomatters[ col.sType ] ) { 2698 cellData = fomatters[ col.sType ]( cellData ); 2699 } 2700 } 2701 2702 return cellData; 2703 } 2704 2705 2706 /** 2707 * Set the value for a specific cell, into the internal data cache 2708 * @param {object} settings dataTables settings object 2709 * @param {int} rowIdx aoData row id 2710 * @param {int} colIdx Column index 2711 * @param {*} val Value to set 2712 * @memberof DataTable#oApi 2713 */ 2714 function _fnSetCellData( settings, rowIdx, colIdx, val ) 2715 { 2716 var col = settings.aoColumns[colIdx]; 2717 var rowData = settings.aoData[rowIdx]._aData; 2718 2719 col.fnSetData( rowData, val, { 2720 settings: settings, 2721 row: rowIdx, 2722 col: colIdx 2723 } ); 2724 } 2725 2726 /** 2727 * Write a value to a cell 2728 * @param {*} td Cell 2729 * @param {*} val Value 2730 */ 2731 function _fnWriteCell(td, val) 2732 { 2733 if (val && typeof val === 'object' && val.nodeName) { 2734 $(td) 2735 .empty() 2736 .append(val); 2737 } 2738 else { 2739 td.innerHTML = val; 2740 } 2741 } 2742 2743 2744 // Private variable that is used to match action syntax in the data property object 2745 var __reArray = /\[.*?\]$/; 2746 var __reFn = /\(\)$/; 2747 2748 /** 2749 * Split string on periods, taking into account escaped periods 2750 * @param {string} str String to split 2751 * @return {array} Split string 2752 */ 2753 function _fnSplitObjNotation( str ) 2754 { 2755 var parts = str.match(/(\\.|[^.])+/g) || ['']; 2756 2757 return parts.map( function ( s ) { 2758 return s.replace(/\\\./g, '.'); 2759 } ); 2760 } 2761 2762 2763 /** 2764 * Return a function that can be used to get data from a source object, taking 2765 * into account the ability to use nested objects as a source 2766 * @param {string|int|function} mSource The data source for the object 2767 * @returns {function} Data get function 2768 * @memberof DataTable#oApi 2769 */ 2770 var _fnGetObjectDataFn = DataTable.util.get; 2771 2772 2773 /** 2774 * Return a function that can be used to set data from a source object, taking 2775 * into account the ability to use nested objects as a source 2776 * @param {string|int|function} mSource The data source for the object 2777 * @returns {function} Data set function 2778 * @memberof DataTable#oApi 2779 */ 2780 var _fnSetObjectDataFn = DataTable.util.set; 2781 2782 2783 /** 2784 * Return an array with the full table data 2785 * @param {object} oSettings dataTables settings object 2786 * @returns array {array} aData Master data array 2787 * @memberof DataTable#oApi 2788 */ 2789 function _fnGetDataMaster ( settings ) 2790 { 2791 return _pluck( settings.aoData, '_aData' ); 2792 } 2793 2794 2795 /** 2796 * Nuke the table 2797 * @param {object} oSettings dataTables settings object 2798 * @memberof DataTable#oApi 2799 */ 2800 function _fnClearTable( settings ) 2801 { 2802 settings.aoData.length = 0; 2803 settings.aiDisplayMaster.length = 0; 2804 settings.aiDisplay.length = 0; 2805 settings.aIds = {}; 2806 } 2807 2808 2809 /**
2810 * Mark cached data as invalid such that a re-read of the data will occur when 2811 * the cached data is next requested. Also update from the data source object. 2812 * 2813 * @param {object} settings DataTables settings object 2814 * @param {int} rowIdx Row index to invalidate 2815 * @param {string} [src] Source to invalidate from: undefined, 'auto', 'dom' 2816 * or 'data' 2817 * @param {int} [colIdx] Column index to invalidate. If undefined the whole 2818 * row will be invalidated 2819 * @memberof DataTable#oApi 2820 * 2821 * @todo For the modularisation of v1.11 this will need to become a callback, so 2822 * the sort and filter methods can subscribe to it. That will required 2823 * initialisation options for sorting, which is why it is not already baked in 2824 */ 2825 function _fnInvalidate( settings, rowIdx, src, colIdx ) 2826 { 2827 var row = settings.aoData[ rowIdx ]; 2828 var i, ien; 2829 2830 // Remove the cached data for the row 2831 row._aSortData = null; 2832 row._aFilterData = null; 2833 row.displayData = null; 2834 2835 // Are we reading last data from DOM or the data object? 2836 if ( src === 'dom' || ((! src || src === 'auto') && row.src === 'dom') ) { 2837 // Read the data from the DOM 2838 row._aData = _fnGetRowElements( 2839 settings, row, colIdx, colIdx === undefined ? undefined : row._aData 2840 ) 2841 .data; 2842 } 2843 else { 2844 // Reading from data object, update the DOM 2845 var cells = row.anCells; 2846 var display = _fnGetRowDisplay(settings, rowIdx); 2847 2848 if ( cells ) { 2849 if ( colIdx !== undefined ) { 2850 _fnWriteCell(cells[colIdx], display[colIdx]); 2851 } 2852 else { 2853 for ( i=0, ien=cells.length ; i<ien ; i++ ) { 2854 _fnWriteCell(cells[i], display[i]); 2855 } 2856 } 2857 } 2858 } 2859 2860 // Column specific invalidation 2861 var cols = settings.aoColumns; 2862 if ( colIdx !== undefined ) { 2863 // Type - the data might have changed 2864 cols[ colIdx ].sType = null; 2865 2866 // Max length string. Its a fairly cheep recalculation, so not worth 2867 // something more complicated 2868 cols[ colIdx ].maxLenString = null; 2869 } 2870 else { 2871 for ( i=0, ien=cols.length ; i<ien ; i++ ) { 2872 cols[i].sType = null; 2873 cols[i].maxLenString = null; 2874 } 2875 2876 // Update DataTables special `DT_*` attributes for the row 2877 _fnRowAttributes( settings, row ); 2878 } 2879 } 2880 2881 2882 /** 2883 * Build a data source object from an HTML row, reading the contents of the 2884 * cells that are in the row. 2885 * 2886 * @param {object} settings DataTables settings object 2887 * @param {node|object} TR element from which to read data or existing row 2888 * object from which to re-read the data from the cells 2889 * @param {int} [colIdx] Optional column index 2890 * @param {array|object} [d] Data source object. If `colIdx` is given then this 2891 * parameter should also be given and will be used to write the data into. 2892 * Only the column in question will be written 2893 * @returns {object} Object with two parameters: `data` the data read, in 2894 * document order, and `cells` and array of nodes (they can be useful to the 2895 * caller, so rather than needing a second traversal to get them, just return 2896 * them from here). 2897 * @memberof DataTable#oApi 2898 */ 2899 function _fnGetRowElements( settings, row, colIdx, d ) 2900 { 2901 var 2902 tds = [], 2903 td = row.firstChild, 2904 name, col, i=0, contents, 2905 columns = settings.aoColumns, 2906 objectRead = settings._rowReadObject; 2907 2908 // Allow the data object to be passed in, or construct 2909 d = d !== undefined ? 2910 d : 2911 objectRead ? 2912 {} : 2913 []; 2914 2915 var attr = function ( str, td ) { 2916 if ( typeof str === 'string' ) { 2917 var idx = str.indexOf('@'); 2918 2919 if ( idx !== -1 ) { 2920 var attr = str.substring( idx+1 ); 2921 var setter = _fnSetObjectDataFn( str ); 2922 setter( d, td.getAttribute( attr ) ); 2923 } 2924 } 2925 }; 2926 2927 // Read data from a cell and store into the data object 2928 var cellProcess = function ( cell ) { 2929 if ( colIdx === undefined || colIdx === i ) { 2930 col = columns[i]; 2931 contents = (cell.innerHTML).trim(); 2932 2933 if ( col && col._bAttrSrc ) { 2934 var setter = _fnSetObjectDataFn( col.mData._ ); 2935 setter( d, contents ); 2936 2937 attr( col.mData.sort, cell ); 2938 attr( col.mData.type, cell ); 2939 attr( col.mData.filter, cell ); 2940 } 2941 else { 2942 // Depending on the `data` option for the columns the data can 2943 // be read to either an object or an array. 2944 if ( objectRead ) { 2945 if ( ! col._setter ) { 2946 // Cache the setter function 2947 col._setter = _fnSetObjectDataFn( col.mData ); 2948 } 2949 col._setter( d, contents ); 2950 } 2951 else { 2952 d[i] = contents; 2953 } 2954 } 2955 } 2956 2957 i++; 2958 }; 2959 2960 if ( td ) { 2961 // `tr` element was passed in 2962 while ( td ) { 2963 name = td.nodeName.toUpperCase(); 2964
2965 if ( name == "TD" || name == "TH" ) { 2966 cellProcess( td ); 2967 tds.push( td ); 2968 } 2969 2970 td = td.nextSibling; 2971 } 2972 } 2973 else { 2974 // Existing row object passed in 2975 tds = row.anCells; 2976 2977 for ( var j=0, jen=tds.length ; j<jen ; j++ ) { 2978 cellProcess( tds[j] ); 2979 } 2980 } 2981 2982 // Read the ID from the DOM if present 2983 var rowNode = row.firstChild ? row : row.nTr; 2984 2985 if ( rowNode ) { 2986 var id = rowNode.getAttribute( 'id' ); 2987 2988 if ( id ) { 2989 _fnSetObjectDataFn( settings.rowId )( d, id ); 2990 } 2991 } 2992 2993 return { 2994 data: d, 2995 cells: tds 2996 }; 2997 } 2998 2999 /** 3000 * Render and cache a row's display data for the columns, if required 3001 * @returns 3002 */ 3003 function _fnGetRowDisplay (settings, rowIdx) { 3004 let rowModal = settings.aoData[rowIdx]; 3005 let columns = settings.aoColumns; 3006 3007 if (! rowModal.displayData) { 3008 // Need to render and cache 3009 rowModal.displayData = []; 3010 3011 for ( var colIdx=0, len=columns.length ; colIdx<len ; colIdx++ ) { 3012 rowModal.displayData.push( 3013 _fnGetCellData( settings, rowIdx, colIdx, 'display' ) 3014 ); 3015 } 3016 } 3017 3018 return rowModal.displayData; 3019 } 3020 3021 /** 3022 * Create a new TR element (and it's TD children) for a row 3023 * @param {object} oSettings dataTables settings object 3024 * @param {int} iRow Row to consider 3025 * @param {node} [nTrIn] TR element to add to the table - optional. If not given, 3026 * DataTables will create a row automatically 3027 * @param {array} [anTds] Array of TD|TH elements for the row - must be given 3028 * if nTr is. 3029 * @memberof DataTable#oApi 3030 */ 3031 function _fnCreateTr ( oSettings, iRow, nTrIn, anTds ) 3032 { 3033 var 3034 row = oSettings.aoData[iRow], 3035 rowData = row._aData, 3036 cells = [], 3037 nTr, nTd, oCol, 3038 i, iLen, create, 3039 trClass = oSettings.oClasses.tbody.row; 3040 3041 if ( row.nTr === null ) 3042 { 3043 nTr = nTrIn || document.createElement('tr'); 3044 3045 row.nTr = nTr; 3046 row.anCells = cells; 3047 3048 _addClass(nTr, trClass); 3049 3050 /* Use a private property on the node to allow reserve mapping from the node 3051 * to the aoData array for fast look up 3052 */ 3053 nTr._DT_RowIndex = iRow; 3054 3055 /* Special parameters can be given by the data source to be used on the row */ 3056 _fnRowAttributes( oSettings, row ); 3057 3058 /* Process each column */ 3059 for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ ) 3060 { 3061 oCol = oSettings.aoColumns[i]; 3062 create = nTrIn && anTds[i] ? false : true; 3063 3064 nTd = create ? document.createElement( oCol.sCellType ) : anTds[i]; 3065 3066 if (! nTd) { 3067 _fnLog( oSettings, 0, 'Incorrect column count', 18 ); 3068 } 3069 3070 nTd._DT_CellIndex = { 3071 row: iRow, 3072 column: i 3073 }; 3074 3075 cells.push( nTd ); 3076 3077 var display = _fnGetRowDisplay(oSettings, iRow); 3078 3079 // Need to create the HTML if new, or if a rendering function is defined 3080 if ( 3081 create || 3082 ( 3083 (oCol.mRender || oCol.mData !== i) && 3084 (!$.isPlainObject(oCol.mData) || oCol.mData._ !== i+'.display') 3085 ) 3086 ) { 3087 _fnWriteCell(nTd, display[i]); 3088 } 3089 3090 // Visibility - add or remove as required 3091 if ( oCol.bVisible && create ) 3092 { 3093 nTr.appendChild( nTd ); 3094 } 3095 else if ( ! oCol.bVisible && ! create ) 3096 { 3097 nTd.parentNode.removeChild( nTd ); 3098 } 3099 3100 if ( oCol.fnCreatedCell ) 3101 { 3102 oCol.fnCreatedCell.call( oSettings.oInstance, 3103 nTd, _fnGetCellData( oSettings, iRow, i ), rowData, iRow, i 3104 ); 3105 } 3106 } 3107 3108 _fnCallbackFire( oSettings, 'aoRowCreatedCallback', 'row-created', [nTr, rowData, iRow, cells] ); 3109 } 3110 else { 3111 _addClass(row.nTr, trClass); 3112 } 3113 } 3114 3115 3116 /** 3117 * Add attributes to a row based on the special `DT_*` parameters in a data 3118 * source object. 3119 * @param {object} settings DataTables settings object 3120 * @param {object} DataTables row object for the row to be modified 3121 * @memberof DataTable#oApi 3122 */ 3123 function _fnRowAttributes( settings, row ) 3124 { 3125 var tr = row.nTr; 3126 var data = row._aData; 3127 3128 if ( tr ) { 3129 var id = settings.rowIdFn( data ); 3130 3131 if ( id ) { 3132 tr.id = id; 3133 } 3134 3135 if ( data.DT_RowClass ) { 3136 // Remove any classes added by DT_RowClass before 3137 var a = data.DT_RowClass.split(' '); 3138 row.__rowc = row.__rowc ? 3139 _unique( row.__rowc.concat( a ) ) : 3140 a; 3141 3142 $(tr) 3143 .removeClass( row.__rowc.join(' ') ) 3144 .addClass( data.DT_RowClass ); 3145 } 3146 3147 if ( data.DT_RowAttr ) { 3148 $(tr).attr( data.DT_RowAttr ); 3149 } 3150 3151 if ( data.DT_RowData ) { 3152 $(tr).data( data.DT_RowData ); 3153 } 3154 } 3155 } 3156 3157 3158 /** 3159 * Create the HTML header for the table 3160 * @param {object} oSettings dataTables settings object 3161 * @memberof DataTable#oApi 3162 */ 3163 function _fnBuildHead( settings, side ) 3164 { 3165 var classes = settings.oClasses; 3166 var columns = settings.aoColumns; 3167 var i, ien, row; 3168 var target = side === 'header' 3169 ? settings.nTHead 3170 : settings.nTFoot; 3171 var titleProp = side === 'header' ? 'sTitle' : side; 3172 3173 // Footer might be defined 3174 if (! target) { 3175 return; 3176 } 3177 3178 // If no cells yet and we have content for them, then create 3179 if (side === 'header' || _pluck(settings.aoColumns, titleProp).join('')) { 3180 row = $('tr', target); 3181 3182 // Add a row if needed 3183 if (! row.length) { 3184 row = $('<tr/>').appendTo(target) 3185 } 3186 3187 // Add the number of cells needed to make up to the number of columns 3188 if (row.length === 1) { 3189 var cells = $('td, th', row); 3190 3191 for ( i=cells.length, ien=columns.length ; i<ien ; i++ ) { 3192 $('<th/>') 3193 .html( columns[i][titleProp] || '' ) 3194 .appendTo( row ); 3195 } 3196 } 3197 } 3198 3199 var detected = _fnDetectHeader( settings, target, true ); 3200 3201 if (side === 'header') { 3202 settings.aoHeader = detected; 3203 } 3204 else { 3205 settings.aoFooter = detected; 3206 } 3207 3208 // ARIA role for the rows 3209 $(target).children('tr').attr('role', 'row'); 3210 3211 // Every cell needs to be passed through the renderer 3212 $(target).children('tr').children('th, td') 3213 .each( function () { 3214 _fnRenderer( settings, side )( 3215 settings, $(this), classes 3216 ); 3217 } ); 3218 } 3219 3220 /** 3221 * Build a layout structure for a header or footer 3222 * 3223 * @param {*} settings DataTables settings 3224 * @param {*} source Source layout array 3225 * @param {*} incColumns What columns should be included 3226 * @returns Layout array 3227 */ 3228 function _fnHeaderLayout( settings, source, incColumns ) 3229 {
3230 var row, column, cell; 3231 var local = []; 3232 var structure = []; 3233 var columns = settings.aoColumns; 3234 var columnCount = columns.length; 3235 var rowspan, colspan; 3236 3237 if ( ! source ) { 3238 return; 3239 } 3240 3241 // Default is to work on only visible columns 3242 if ( ! incColumns ) { 3243 incColumns = _range(columnCount) 3244 .filter(function (idx) { 3245 return columns[idx].bVisible; 3246 }); 3247 } 3248 3249 // Make a copy of the master layout array, but with only the columns we want 3250 for ( row=0 ; row<source.length ; row++ ) { 3251 // Remove any columns we haven't selected 3252 local[row] = source[row].slice().filter(function (cell, i) { 3253 return incColumns.includes(i); 3254 }); 3255 3256 // Prep the structure array - it needs an element for each row 3257 structure.push( [] ); 3258 } 3259 3260 for ( row=0 ; row<local.length ; row++ ) { 3261 for ( column=0 ; column<local[row].length ; column++ ) { 3262 rowspan = 1; 3263 colspan = 1; 3264 3265 // Check to see if there is already a cell (row/colspan) covering our target 3266 // insert point. If there is, then there is nothing to do. 3267 if ( structure[row][column] === undefined ) { 3268 cell = local[row][column].cell; 3269 3270 // Expand for rowspan 3271 while ( 3272 local[row+rowspan] !== undefined && 3273 local[row][column].cell == local[row+rowspan][column].cell 3274 ) { 3275 structure[row+rowspan][column] = null; 3276 rowspan++; 3277 } 3278 3279 // And for colspan 3280 while ( 3281 local[row][column+colspan] !== undefined && 3282 local[row][column].cell == local[row][column+colspan].cell 3283 ) { 3284 // Which also needs to go over rows 3285 for ( var k=0 ; k<rowspan ; k++ ) { 3286 structure[row+k][column+colspan] = null; 3287 } 3288 3289 colspan++; 3290 } 3291 3292 var titleSpan = $('span.dt-column-title', cell); 3293 3294 structure[row][column] = { 3295 cell: cell, 3296 colspan: colspan, 3297 rowspan: rowspan, 3298 title: titleSpan.length 3299 ? titleSpan.html() 3300 : $(cell).html() 3301 }; 3302 } 3303 } 3304 } 3305 3306 return structure; 3307 } 3308 3309 3310 /** 3311 * Draw the header (or footer) element based on the column visibility states. 3312 * 3313 * @param object oSettings dataTables settings object 3314 * @param array aoSource Layout array from _fnDetectHeader 3315 * @memberof DataTable#oApi 3316 */ 3317 function _fnDrawHead( settings, source ) 3318 { 3319 var layout = _fnHeaderLayout(settings, source); 3320 var tr, n; 3321 3322 for ( var row=0 ; row<source.length ; row++ ) { 3323 tr = source[row].row; 3324 3325 // All cells are going to be replaced, so empty out the row 3326 // Can't use $().empty() as that kills event handlers 3327 if (tr) { 3328 while( (n = tr.firstChild) ) { 3329 tr.removeChild( n ); 3330 } 3331 } 3332 3333 for ( var column=0 ; column<layout[row].length ; column++ ) { 3334 var point = layout[row][column]; 3335 3336 if (point) { 3337 $(point.cell) 3338 .appendTo(tr) 3339 .attr('rowspan', point.rowspan) 3340 .attr('colspan', point.colspan); 3341 } 3342 } 3343 } 3344 } 3345 3346 3347 /** 3348 * Insert the required TR nodes into the table for display 3349 * @param {object} oSettings dataTables settings object 3350 * @param ajaxComplete true after ajax call to complete rendering 3351 * @memberof DataTable#oApi 3352 */ 3353 function _fnDraw( oSettings, ajaxComplete ) 3354 { 3355 // Allow for state saving and a custom start position 3356 _fnStart( oSettings ); 3357 3358 /* Provide a pre-callback function which can be used to cancel the draw is false is returned */ 3359 var aPreDraw = _fnCallbackFire( oSettings, 'aoPreDrawCallback', 'preDraw', [oSettings] ); 3360 if ( aPreDraw.indexOf(false) !== -1 ) 3361 { 3362 _fnProcessingDisplay( oSettings, false ); 3363 return; 3364 } 3365 3366 var anRows = []; 3367 var iRowCount = 0; 3368 var bServerSide = _fnDataSource( oSettings ) == 'ssp'; 3369 var aiDisplay = oSettings.aiDisplay; 3370 var iDisplayStart = oSettings._iDisplayStart; 3371 var iDisplayEnd = oSettings.fnDisplayEnd(); 3372 var columns = oSettings.aoColumns; 3373 var body = $(oSettings.nTBody); 3374 3375 oSettings.bDrawing = true; 3376 3377 /* Server-side processing draw intercept */ 3378 if ( !bServerSide ) 3379 { 3380 oSettings.iDraw++; 3381 } 3382 else if ( !oSettings.bDestroying && !ajaxComplete) 3383 { 3384 // Show loading message for server-side processing 3385 if (oSettings.iDraw === 0) {
3386 body.empty().append(_emptyRow(oSettings)); 3387 } 3388 3389 _fnAjaxUpdate( oSettings ); 3390 return; 3391 } 3392 3393 if ( aiDisplay.length !== 0 ) 3394 { 3395 var iStart = bServerSide ? 0 : iDisplayStart; 3396 var iEnd = bServerSide ? oSettings.aoData.length : iDisplayEnd; 3397 3398 for ( var j=iStart ; j<iEnd ; j++ ) 3399 { 3400 var iDataIndex = aiDisplay[j]; 3401 var aoData = oSettings.aoData[ iDataIndex ]; 3402 if ( aoData.nTr === null ) 3403 { 3404 _fnCreateTr( oSettings, iDataIndex ); 3405 } 3406 3407 var nRow = aoData.nTr; 3408 3409 // Add various classes as needed 3410 for (var i=0 ; i<columns.length ; i++) { 3411 var col = columns[i]; 3412 var td = aoData.anCells[i]; 3413 3414 _addClass(td, _ext.type.className[col.sType]); // auto class 3415 _addClass(td, col.sClass); // column class 3416 _addClass(td, oSettings.oClasses.tbody.cell); // all cells 3417 } 3418 3419 // Row callback functions - might want to manipulate the row 3420 // iRowCount and j are not currently documented. Are they at all 3421 // useful? 3422 _fnCallbackFire( oSettings, 'aoRowCallback', null, 3423 [nRow, aoData._aData, iRowCount, j, iDataIndex] ); 3424 3425 anRows.push( nRow ); 3426 iRowCount++; 3427 } 3428 } 3429 else 3430 { 3431 anRows[ 0 ] = _emptyRow(oSettings); 3432 } 3433 3434 /* Header and footer callbacks */ 3435 _fnCallbackFire( oSettings, 'aoHeaderCallback', 'header', [ $(oSettings.nTHead).children('tr')[0], 3436 _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] ); 3437 3438 _fnCallbackFire( oSettings, 'aoFooterCallback', 'footer', [ $(oSettings.nTFoot).children('tr')[0], 3439 _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] ); 3440 3441 // replaceChildren is faster, but only became widespread in 2020, 3442 // so a fall back in jQuery is provided for older browsers. 3443 if (body[0].replaceChildren) { 3444 body[0].replaceChildren.apply(body[0], anRows); 3445 } 3446 else { 3447 body.children().detach(); 3448 body.append( $(anRows) ); 3449 } 3450 3451 // Empty table needs a specific class 3452 $(oSettings.nTableWrapper).toggleClass('dt-empty-footer', $('tr', oSettings.nTFoot).length === 0); 3453 3454 /* Call all required callback functions for the end of a draw */ 3455 _fnCallbackFire( oSettings, 'aoDrawCallback', 'draw', [oSettings], true ); 3456 3457 /* Draw is complete, sorting and filtering must be as well */ 3458 oSettings.bSorted = false; 3459 oSettings.bFiltered = false; 3460 oSettings.bDrawing = false; 3461 } 3462 3463 3464 /** 3465 * Redraw the table - taking account of the various features which are enabled 3466 * @param {object} oSettings dataTables settings object 3467 * @param {boolean} [holdPosition] Keep the current paging position. By default 3468 * the paging is reset to the first page 3469 * @memberof DataTable#oApi 3470 */ 3471 function _fnReDraw( settings, holdPosition, recompute ) 3472 { 3473 var 3474 features = settings.oFeatures, 3475 sort = features.bSort, 3476 filter = features.bFilter; 3477 3478 if (recompute === undefined || recompute === true) { 3479 if ( sort ) { 3480 _fnSort( settings ); 3481 } 3482 3483 if ( filter ) { 3484 _fnFilterComplete( settings, settings.oPreviousSearch ); 3485 } 3486 else { 3487 // No filtering, so we want to just use the display master 3488 settings.aiDisplay = settings.aiDisplayMaster.slice(); 3489 } 3490 } 3491 3492 if ( holdPosition !== true ) { 3493 settings._iDisplayStart = 0; 3494 } 3495 3496 // Let any modules know about the draw hold position state (used by 3497 // scrolling internally) 3498 settings._drawHold = holdPosition; 3499 3500 _fnDraw( settings ); 3501 3502 settings._drawHold = false; 3503 } 3504 3505 3506 /* 3507 * Table is empty - create a row with an empty message in it 3508 */ 3509 function _emptyRow ( settings ) { 3510 var oLang = settings.oLanguage; 3511 var zero = oLang.sZeroRecords; 3512 var dataSrc = _fnDataSource( settings ); 3513 3514 if ( 3515 (settings.iDraw < 1 && dataSrc === 'ssp') || 3516 (settings.iDraw <= 1 && dataSrc === 'ajax') 3517 ) { 3518 zero = oLang.sLoadingRecords; 3519 } 3520 else if ( oLang.sEmptyTable && settings.fnRecordsTotal() === 0 ) 3521 { 3522 zero = oLang.sEmptyTable; 3523 } 3524 3525 return $( '<tr/>' ) 3526 .append( $('<td />', { 3527 'colSpan': _fnVisbleColumns( settings ), 3528 'class': settings.oClasses.empty.row 3529 } ).html( zero ) )[0]; 3530 } 3531 3532 3533 /** 3534 * Convert a `layout` object given by a user to the object structure needed 3535 * for the renderer. This is done twice, once for above and once for below
3536 * the table. Ordering must also be considered. 3537 * 3538 * @param {*} settings DataTables settings object 3539 * @param {*} layout Layout object to convert 3540 * @param {string} side `top` or `bottom` 3541 * @returns Converted array structure - one item for each row. 3542 */ 3543 function _layoutArray ( settings, layout, side ) 3544 { 3545 var groups = {}; 3546 3547 // Combine into like groups (e.g. `top`, `top2`, etc) 3548 $.each( layout, function ( pos, val ) { 3549 if (val === null) { 3550 return; 3551 } 3552 3553 var splitPos = pos.replace(/([A-Z])/g, ' $1').split(' '); 3554 3555 if ( ! groups[ splitPos[0] ] ) { 3556 groups[ splitPos[0] ] = {}; 3557 } 3558 3559 var align = splitPos.length === 1 ? 3560 'full' : 3561 splitPos[1].toLowerCase(); 3562 var group = groups[ splitPos[0] ]; 3563 var groupRun = function (contents, innerVal) { 3564 // If it is an object, then there can be multiple features contained in it 3565 if ( $.isPlainObject( innerVal ) ) { 3566 Object.keys(innerVal).map(function (key) { 3567 contents.push( { 3568 feature: key, 3569 opts: innerVal[key] 3570 }); 3571 }); 3572 } 3573 else { 3574 contents.push(innerVal); 3575 } 3576 } 3577 3578 // Transform to an object with a contents property 3579 if (! group[align] || ! group[align].contents) { 3580 group[align] = { contents: [] }; 3581 } 3582 3583 // Allow for an array or just a single object 3584 if ( Array.isArray(val)) { 3585 for (var i=0 ; i<val.length ; i++) { 3586 groupRun(group[align].contents, val[i]); 3587 } 3588 } 3589 else { 3590 groupRun(group[ align ].contents, val); 3591 } 3592 3593 // And make contents an array 3594 if ( ! Array.isArray( group[ align ].contents ) ) { 3595 group[ align ].contents = [ group[ align ].contents ]; 3596 } 3597 } ); 3598 3599 var filtered = Object.keys(groups) 3600 .map( function ( pos ) { 3601 // Filter to only the side we need 3602 if ( pos.indexOf(side) !== 0 ) { 3603 return null; 3604 } 3605 3606 return { 3607 name: pos, 3608 val: groups[pos] 3609 }; 3610 } ) 3611 .filter( function (item) { 3612 return item !== null; 3613 }); 3614 3615 // Order by item identifier 3616 filtered.sort( function ( a, b ) { 3617 var order1 = a.name.replace(/[^0-9]/g, '') * 1; 3618 var order2 = b.name.replace(/[^0-9]/g, '') * 1; 3619 3620 return order2 - order1; 3621 } ); 3622 3623 if ( side === 'bottom' ) { 3624 filtered.reverse(); 3625 } 3626 3627 // Split into rows 3628 var rows = []; 3629 for ( var i=0, ien=filtered.length ; i<ien ; i++ ) { 3630 if ( filtered[i].val.full ) { 3631 rows.push( { full: filtered[i].val.full } ); 3632 _layoutResolve( settings, rows[ rows.length - 1 ] ); 3633 3634 delete filtered[i].val.full; 3635 } 3636 3637 if ( Object.keys(filtered[i].val).length ) { 3638 rows.push( filtered[i].val ); 3639 _layoutResolve( settings, rows[ rows.length - 1 ] ); 3640 } 3641 } 3642 3643 return rows; 3644 } 3645 3646 3647 /** 3648 * Convert the contents of a row's layout object to nodes that can be inserted 3649 * into the document by a renderer. Execute functions, look up plug-ins, etc. 3650 * 3651 * @param {*} settings DataTables settings object 3652 * @param {*} row Layout object for this row 3653 */ 3654 function _layoutResolve( settings, row ) { 3655 var getFeature = function (feature, opts) { 3656 if ( ! _ext.features[ feature ] ) { 3657 _fnLog( settings, 0, 'Unknown feature: '+ feature ); 3658 } 3659 3660 return _ext.features[ feature ].apply( this, [settings, opts] ); 3661 }; 3662 3663 var resolve = function ( item ) { 3664 var line = row[ item ].contents; 3665 3666 for ( var i=0, ien=line.length ; i<ien ; i++ ) { 3667 if ( ! line[i] ) { 3668 continue; 3669 } 3670 else if ( typeof line[i] === 'string' ) { 3671 line[i] = getFeature( line[i], null ); 3672 } 3673 else if ( $.isPlainObject(line[i]) ) { 3674 // If it's an object, it just has feature and opts properties from 3675 // the transform in _layoutArray 3676 line[i] = getFeature(line[i].feature, line[i].opts); 3677 } 3678 else if ( typeof line[i].node === 'function' ) { 3679 line[i] = line[i].node( settings ); 3680 } 3681 else if ( typeof line[i] === 'function' ) { 3682 var inst = line[i]( settings ); 3683 3684 line[i] = typeof inst.node === 'function' ? 3685 inst.node() : 3686 inst; 3687 } 3688 } 3689 }; 3690 3691 $.each( row, function ( key ) { 3692 resolve( key ); 3693 } ); 3694 } 3695 3696 3697 /** 3698 * Add the options to the page HTML for the table 3699 * @param {object} settings DataTables settings object 3700 * @memberof DataTable#oApi 3701 */ 3702 function _fnAddOptionsHtml ( settings ) 3703 { 3704 var classes = settings.oClasses; 3705 var table = $(settings.nTable); 3706 3707 // Wrapper div around everything DataTables controls
3708 var insert = $('<div/>') 3709 .attr({ 3710 id: settings.sTableId+'_wrapper', 3711 'class': classes.container 3712 }) 3713 .insertBefore(table); 3714 3715 settings.nTableWrapper = insert[0]; 3716 3717 if (settings.sDom) { 3718 // Legacy 3719 _fnLayoutDom(settings, settings.sDom, insert); 3720 } 3721 else { 3722 var top = _layoutArray( settings, settings.layout, 'top' ); 3723 var bottom = _layoutArray( settings, settings.layout, 'bottom' ); 3724 var renderer = _fnRenderer( settings, 'layout' ); 3725 3726 // Everything above - the renderer will actually insert the contents into the document 3727 top.forEach(function (item) { 3728 renderer( settings, insert, item ); 3729 }); 3730 3731 // The table - always the center of attention 3732 renderer( settings, insert, { 3733 full: { 3734 table: true, 3735 contents: [ _fnFeatureHtmlTable(settings) ] 3736 } 3737 } ); 3738 3739 // Everything below 3740 bottom.forEach(function (item) { 3741 renderer( settings, insert, item ); 3742 }); 3743 } 3744 3745 // Processing floats on top, so it isn't an inserted feature 3746 _processingHtml( settings ); 3747 } 3748 3749 /** 3750 * Draw the table with the legacy DOM property 3751 * @param {*} settings DT settings object 3752 * @param {*} dom DOM string 3753 * @param {*} insert Insert point 3754 */ 3755 function _fnLayoutDom( settings, dom, insert ) 3756 { 3757 var parts = dom.match(/(".*?")|('.*?')|./g); 3758 var featureNode, option, newNode, next, attr; 3759 3760 for ( var i=0 ; i<parts.length ; i++ ) { 3761 featureNode = null; 3762 option = parts[i]; 3763 3764 if ( option == '<' ) { 3765 // New container div 3766 newNode = $('<div/>'); 3767 3768 // Check to see if we should append an id and/or a class name to the container 3769 next = parts[i+1]; 3770 3771 if ( next[0] == "'" || next[0] == '"' ) { 3772 attr = next.replace(/['"]/g, ''); 3773 3774 var id = '', className; 3775 3776 /* The attribute can be in the format of "#id.class", "#id" or "class" This logic 3777 * breaks the string into parts and applies them as needed 3778 */ 3779 if ( attr.indexOf('.') != -1 ) { 3780 var split = attr.split('.'); 3781 3782 id = split[0]; 3783 className = split[1]; 3784 } 3785 else if ( attr[0] == "#" ) { 3786 id = attr; 3787 } 3788 else { 3789 className = attr; 3790 } 3791 3792 newNode 3793 .attr('id', id.substring(1)) 3794 .addClass(className); 3795 3796 i++; // Move along the position array 3797 } 3798 3799 insert.append( newNode ); 3800 insert = newNode; 3801 } 3802 else if ( option == '>' ) { 3803 // End container div 3804 insert = insert.parent(); 3805 } 3806 else if ( option == 't' ) { 3807 // Table 3808 featureNode = _fnFeatureHtmlTable( settings ); 3809 } 3810 else 3811 { 3812 DataTable.ext.feature.forEach(function(feature) { 3813 if ( option == feature.cFeature ) { 3814 featureNode = feature.fnInit( settings ); 3815 } 3816 }); 3817 } 3818 3819 // Add to the display 3820 if ( featureNode ) { 3821 insert.append( featureNode ); 3822 } 3823 } 3824 } 3825 3826 3827 /** 3828 * Use the DOM source to create up an array of header cells. The idea here is to 3829 * create a layout grid (array) of rows x columns, which contains a reference 3830 * to the cell that that point in the grid (regardless of col/rowspan), such that 3831 * any column / row could be removed and the new grid constructed 3832 * @param {node} thead The header/footer element for the table 3833 * @returns {array} Calculated layout array 3834 * @memberof DataTable#oApi 3835 */ 3836 function _fnDetectHeader ( settings, thead, write ) 3837 { 3838 var columns = settings.aoColumns; 3839 var rows = $(thead).children('tr'); 3840 var row, cell; 3841 var i, k, l, iLen, shifted, column, colspan, rowspan; 3842 var isHeader = thead && thead.nodeName.toLowerCase() === 'thead'; 3843 var layout = []; 3844 var unique; 3845 var shift = function ( a, i, j ) { 3846 var k = a[i]; 3847 while ( k[j] ) { 3848 j++; 3849 } 3850 return j; 3851 }; 3852 3853 // We know how many rows there are in the layout - so prep it 3854 for ( i=0, iLen=rows.length ; i<iLen ; i++ ) { 3855 layout.push( [] ); 3856 } 3857 3858 for ( i=0, iLen=rows.length ; i<iLen ; i++ ) { 3859 row = rows[i]; 3860 column = 0; 3861 3862 // For every cell in the row.. 3863 cell = row.firstChild; 3864 while ( cell ) { 3865 if ( 3866 cell.nodeName.toUpperCase() == 'TD' || 3867 cell.nodeName.toUpperCase() == 'TH' 3868 ) { 3869 var cols = []; 3870 3871 // Get the col and rowspan attributes from the DOM and sanitise them 3872 colspan = cell.getAttribute('colspan') * 1; 3873 rowspan = cell.getAttribute('rowspan') * 1; 3874 colspan = (!colspan || colspan===0 || colspan===1) ? 1 : colspan; 3875 rowspan = (!rowspan || rowspan===0 || rowspan===1) ? 1 : rows
3875pan; 3876 3877 // There might be colspan cells already in this row, so shift our target 3878 // accordingly 3879 shifted = shift( layout, i, column ); 3880 3881 // Cache calculation for unique columns 3882 unique = colspan === 1 ? 3883 true : 3884 false; 3885 3886 // Perform header setup 3887 if ( write ) { 3888 if (unique) { 3889 // Allow column options to be set from HTML attributes 3890 _fnColumnOptions( settings, shifted, $(cell).data() ); 3891 3892 // Get the width for the column. This can be defined from the 3893 // width attribute, style attribute or `columns.width` option 3894 var columnDef = columns[shifted]; 3895 var width = cell.getAttribute('width') || null; 3896 var t = cell.style.width.match(/width:\s*(\d+[pxem%]+)/); 3897 if ( t ) { 3898 width = t[1]; 3899 } 3900 3901 columnDef.sWidthOrig = columnDef.sWidth || width; 3902 3903 if (isHeader) { 3904 // Column title handling - can be user set, or read from the DOM 3905 // This happens before the render, so the original is still in place 3906 if ( columnDef.sTitle !== null && ! columnDef.autoTitle ) { 3907 cell.innerHTML = columnDef.sTitle; 3908 } 3909 3910 if (! columnDef.sTitle && unique) { 3911 columnDef.sTitle = _stripHtml(cell.innerHTML); 3912 columnDef.autoTitle = true; 3913 } 3914 } 3915 else { 3916 // Footer specific operations 3917 if (columnDef.footer) { 3918 cell.innerHTML = columnDef.footer; 3919 } 3920 } 3921 3922 // Fall back to the aria-label attribute on the table header if no ariaTitle is 3923 // provided. 3924 if (! columnDef.ariaTitle) { 3925 columnDef.ariaTitle = $(cell).attr("aria-label") || columnDef.sTitle; 3926 } 3927 3928 // Column specific class names 3929 if ( columnDef.className ) { 3930 $(cell).addClass( columnDef.className ); 3931 } 3932 } 3933 3934 // Wrap the column title so we can write to it in future 3935 if ( $('span.dt-column-title', cell).length === 0) { 3936 $('<span>') 3937 .addClass('dt-column-title') 3938 .append(cell.childNodes) 3939 .appendTo(cell); 3940 } 3941 3942 if ( isHeader && $('span.dt-column-order', cell).length === 0) { 3943 $('<span>') 3944 .addClass('dt-column-order') 3945 .appendTo(cell); 3946 } 3947 } 3948 3949 // If there is col / rowspan, copy the information into the layout grid 3950 for ( l=0 ; l<colspan ; l++ ) { 3951 for ( k=0 ; k<rowspan ; k++ ) { 3952 layout[i+k][shifted+l] = { 3953 cell: cell, 3954 unique: unique 3955 }; 3956 3957 layout[i+k].row = row; 3958 } 3959 3960 cols.push( shifted+l ); 3961 } 3962 3963 // Assign an attribute so spanning cells can still be identified 3964 // as belonging to a column 3965 cell.setAttribute('data-dt-column', _unique(cols).join(',')); 3966 } 3967 3968 cell = cell.nextSibling; 3969 } 3970 } 3971 3972 return layout; 3973 } 3974 3975 /** 3976 * Set the start position for draw 3977 * @param {object} oSettings dataTables settings object 3978 */ 3979 function _fnStart( oSettings ) 3980 { 3981 var bServerSide = _fnDataSource( oSettings ) == 'ssp'; 3982 var iInitDisplayStart = oSettings.iInitDisplayStart; 3983 3984 // Check and see if we have an initial draw position from state saving 3985 if ( iInitDisplayStart !== undefined && iInitDisplayStart !== -1 ) 3986 { 3987 oSettings._iDisplayStart = bServerSide ? 3988 iInitDisplayStart : 3989 iInitDisplayStart >= oSettings.fnRecordsDisplay() ? 3990 0 : 3991 iInitDisplayStart; 3992 3993 oSettings.iInitDisplayStart = -1; 3994 } 3995 } 3996 3997 /** 3998 * Create an Ajax call based on the table's settings, taking into account that 3999 * parameters can have multiple forms, and backwards compatibility. 4000 * 4001 * @param {object} oSettings dataTables settings object 4002 * @param {array} data Data to send to the server, required by 4003 * DataTables - may be augmented by developer callbacks 4004 * @param {function} fn Callback function to run when data is obtained 4005 */ 4006 function _fnBuildAjax( oSettings, data, fn ) 4007 { 4008 var ajaxData; 4009 var ajax = oSettings.ajax; 4010 var instance = oSettings.oInstance; 4011 var callback = function ( json ) { 4012 var status = oSettings.jqXHR 4013 ? oSettings.jqXHR.status 4014 : null; 4015 4016 if ( json === null || (typeof status === 'number' && status == 204 ) ) { 4017 json = {}; 4018 _fnAjaxDataSrc( oSettings, json, [] ); 4019 } 4020 4021 var error = json.error || json.sError; 4022 if ( error ) { 4023 _fnLog( oSettings, 0, error ); 4024 } 4025 4026 oSettings.json = json; 4027 4028 _fnCallbackFire( oSettings, null, 'xhr', [oSettings, json, oSettings.jqXHR], true ); 4029 fn( json ); 4030 }; 4031 4032 if ( $.isPlainObject( ajax ) && ajax.data ) 4033 { 4034 ajaxData = ajax.data; 4035 4036 var newData = typeof ajaxData === 'function' ? 4037 ajaxData( data, oSettings ) : // fn can manipulate data or return 4038 ajaxData; // an object object or array to merge 4039 4040 // If the function returned something, use that alone 4041 data = typeof ajaxData === 'function' && newData ? 4042 newData : 4043 $.extend( true, data, newData ); 4044
4045 // Remove the data property as we've resolved it already and don't want 4046 // jQuery to do it again (it is restored at the end of the function) 4047 delete ajax.data; 4048 } 4049 4050 var baseAjax = { 4051 "url": typeof ajax === 'string' ? 4052 ajax : 4053 '', 4054 "data": data, 4055 "success": callback, 4056 "dataType": "json", 4057 "cache": false, 4058 "type": oSettings.sServerMethod, 4059 "error": function (xhr, error) { 4060 var ret = _fnCallbackFire( oSettings, null, 'xhr', [oSettings, null, oSettings.jqXHR], true ); 4061 4062 if ( ret.indexOf(true) === -1 ) { 4063 if ( error == "parsererror" ) { 4064 _fnLog( oSettings, 0, 'Invalid JSON response', 1 ); 4065 } 4066 else if ( xhr.readyState === 4 ) { 4067 _fnLog( oSettings, 0, 'Ajax error', 7 ); 4068 } 4069 } 4070 4071 _fnProcessingDisplay( oSettings, false ); 4072 } 4073 }; 4074 4075 // If `ajax` option is an object, extend and override our default base 4076 if ( $.isPlainObject( ajax ) ) { 4077 $.extend( baseAjax, ajax ) 4078 } 4079 4080 // Store the data submitted for the API 4081 oSettings.oAjaxData = data; 4082 4083 // Allow plug-ins and external processes to modify the data 4084 _fnCallbackFire( oSettings, null, 'preXhr', [oSettings, data, baseAjax], true ); 4085 4086 if ( typeof ajax === 'function' ) 4087 { 4088 // Is a function - let the caller define what needs to be done 4089 oSettings.jqXHR = ajax.call( instance, data, callback, oSettings ); 4090 } 4091 else if (ajax.url === '') { 4092 // No url, so don't load any data. Just apply an empty data array 4093 // to the object for the callback. 4094 var empty = {}; 4095 4096 DataTable.util.set(ajax.dataSrc)(empty, []); 4097 callback(empty); 4098 } 4099 else { 4100 // Object to extend the base settings 4101 oSettings.jqXHR = $.ajax( baseAjax ); 4102 4103 // Restore for next time around 4104 if ( ajaxData ) { 4105 ajax.data = ajaxData; 4106 } 4107 } 4108 } 4109 4110 4111 /** 4112 * Update the table using an Ajax call 4113 * @param {object} settings dataTables settings object 4114 * @returns {boolean} Block the table drawing or not 4115 * @memberof DataTable#oApi 4116 */ 4117 function _fnAjaxUpdate( settings ) 4118 { 4119 settings.iDraw++; 4120 _fnProcessingDisplay( settings, true ); 4121 4122 _fnBuildAjax( 4123 settings, 4124 _fnAjaxParameters( settings ), 4125 function(json) { 4126 _fnAjaxUpdateDraw( settings, json ); 4127 } 4128 ); 4129 } 4130 4131 4132 /** 4133 * Build up the parameters in an object needed for a server-side processing 4134 * request. 4135 * @param {object} oSettings dataTables settings object 4136 * @returns {bool} block the table drawing or not 4137 * @memberof DataTable#oApi 4138 */ 4139 function _fnAjaxParameters( settings ) 4140 { 4141 var 4142 columns = settings.aoColumns, 4143 features = settings.oFeatures, 4144 preSearch = settings.oPreviousSearch, 4145 preColSearch = settings.aoPreSearchCols, 4146 colData = function ( idx, prop ) { 4147 return typeof columns[idx][prop] === 'function' ? 4148 'function' : 4149 columns[idx][prop]; 4150 }; 4151 4152 return { 4153 draw: settings.iDraw, 4154 columns: columns.map( function ( column, i ) { 4155 return { 4156 data: colData(i, 'mData'), 4157 name: column.sName, 4158 searchable: column.bSearchable, 4159 orderable: column.bSortable, 4160 search: { 4161 value: preColSearch[i].search, 4162 regex: preColSearch[i].regex, 4163 fixed: Object.keys(column.searchFixed).map( function(name) { 4164 return { 4165 name: name, 4166 term: column.searchFixed[name].toString() 4167 } 4168 }) 4169 } 4170 }; 4171 } ), 4172 order: _fnSortFlatten( settings ).map( function ( val ) { 4173 return { 4174 column: val.col, 4175 dir: val.dir, 4176 name: colData(val.col, 'sName') 4177 }; 4178 } ), 4179 start: settings._iDisplayStart, 4180 length: features.bPaginate ? 4181 settings._iDisplayLength : 4182 -1, 4183 search: { 4184 value: preSearch.search, 4185 regex: preSearch.regex, 4186 fixed: Object.keys(settings.searchFixed).map( function(name) { 4187 return { 4188 name: name, 4189 term: settings.searchFixed[name].toString() 4190 } 4191 }) 4192 } 4193 }; 4194 } 4195 4196 4197 /** 4198 * Data the data from the server (nuking the old) and redraw the table 4199 * @param {object} oSettings dataTables settings object 4200 * @param {object} json json data return from the server.
4201 * @param {string} json.sEcho Tracking flag for DataTables to match requests 4202 * @param {int} json.iTotalRecords Number of records in the data set, not accounting for filtering 4203 * @param {int} json.iTotalDisplayRecords Number of records in the data set, accounting for filtering 4204 * @param {array} json.aaData The data to display on this page 4205 * @param {string} [json.sColumns] Column ordering (sName, comma separated) 4206 * @memberof DataTable#oApi 4207 */ 4208 function _fnAjaxUpdateDraw ( settings, json ) 4209 { 4210 var data = _fnAjaxDataSrc(settings, json); 4211 var draw = _fnAjaxDataSrcParam(settings, 'draw', json); 4212 var recordsTotal = _fnAjaxDataSrcParam(settings, 'recordsTotal', json); 4213 var recordsFiltered = _fnAjaxDataSrcParam(settings, 'recordsFiltered', json); 4214 4215 if ( draw !== undefined ) { 4216 // Protect against out of sequence returns 4217 if ( draw*1 < settings.iDraw ) { 4218 return; 4219 } 4220 settings.iDraw = draw * 1; 4221 } 4222 4223 // No data in returned object, so rather than an array, we show an empty table 4224 if ( ! data ) { 4225 data = []; 4226 } 4227 4228 _fnClearTable( settings ); 4229 settings._iRecordsTotal = parseInt(recordsTotal, 10); 4230 settings._iRecordsDisplay = parseInt(recordsFiltered, 10); 4231 4232 for ( var i=0, ien=data.length ; i<ien ; i++ ) { 4233 _fnAddData( settings, data[i] ); 4234 } 4235 settings.aiDisplay = settings.aiDisplayMaster.slice(); 4236 4237 _fnDraw( settings, true ); 4238 _fnInitComplete( settings ); 4239 _fnProcessingDisplay( settings, false ); 4240 } 4241 4242 4243 /** 4244 * Get the data from the JSON data source to use for drawing a table. Using 4245 * `_fnGetObjectDataFn` allows the data to be sourced from a property of the 4246 * source object, or from a processing function. 4247 * @param {object} settings dataTables settings object 4248 * @param {object} json Data source object / array from the server 4249 * @return {array} Array of data to use 4250 */ 4251 function _fnAjaxDataSrc ( settings, json, write ) 4252 { 4253 var dataProp = 'data'; 4254 4255 if ($.isPlainObject( settings.ajax ) && settings.ajax.dataSrc !== undefined) { 4256 // Could in inside a `dataSrc` object, or not! 4257 var dataSrc = settings.ajax.dataSrc; 4258 4259 // string, function and object are valid types 4260 if (typeof dataSrc === 'string' || typeof dataSrc === 'function') { 4261 dataProp = dataSrc; 4262 } 4263 else if (dataSrc.data !== undefined) { 4264 dataProp = dataSrc.data; 4265 } 4266 } 4267 4268 if ( ! write ) { 4269 if ( dataProp === 'data' ) { 4270 // If the default, then we still want to support the old style, and safely ignore 4271 // it if possible 4272 return json.aaData || json[dataProp]; 4273 } 4274 4275 return dataProp !== "" ? 4276 _fnGetObjectDataFn( dataProp )( json ) : 4277 json; 4278 } 4279 4280 // set 4281 _fnSetObjectDataFn( dataProp )( json, write ); 4282 } 4283 4284 /** 4285 * Very similar to _fnAjaxDataSrc, but for the other SSP properties 4286 * @param {*} settings DataTables settings object 4287 * @param {*} param Target parameter 4288 * @param {*} json JSON data 4289 * @returns Resolved value 4290 */ 4291 function _fnAjaxDataSrcParam (settings, param, json) { 4292 var dataSrc = $.isPlainObject( settings.ajax ) 4293 ? settings.ajax.dataSrc 4294 : null; 4295 4296 if (dataSrc && dataSrc[param]) { 4297 // Get from custom location 4298 return _fnGetObjectDataFn( dataSrc[param] )( json ); 4299 } 4300 4301 // else - Default behaviour 4302 var old = ''; 4303 4304 // Legacy support 4305 if (param === 'draw') { 4306 old = 'sEcho'; 4307 } 4308 else if (param === 'recordsTotal') { 4309 old = 'iTotalRecords'; 4310 } 4311 else if (param === 'recordsFiltered') { 4312 old = 'iTotalDisplayRecords'; 4313 } 4314 4315 return json[old] !== undefined 4316 ? json[old] 4317 : json[param]; 4318 } 4319 4320 4321 /** 4322 * Filter the table using both the global filter and column based filtering 4323 * @param {object} settings dataTables settings object 4324 * @param {object} input search information 4325 * @memberof DataTable#oApi 4326 */ 4327 function _fnFilterComplete ( settings, input ) 4328 { 4329 var columnsSearch = settings.aoPreSearchCols; 4330 4331 // Resolve any column types that are unknown due to addition or invalidation 4332 // @todo As per sort - can this be moved into an event handler?
4333 _fnColumnTypes( settings ); 4334 4335 // In server-side processing all filtering is done by the server, so no point hanging around here 4336 if ( _fnDataSource( settings ) != 'ssp' ) 4337 { 4338 // Check if any of the rows were invalidated 4339 _fnFilterData( settings ); 4340 4341 // Start from the full data set 4342 settings.aiDisplay = settings.aiDisplayMaster.slice(); 4343 4344 // Global filter first 4345 _fnFilter( settings.aiDisplay, settings, input.search, input ); 4346 4347 $.each(settings.searchFixed, function (name, term) { 4348 _fnFilter(settings.aiDisplay, settings, term, {}); 4349 }); 4350 4351 // Then individual column filters 4352 for ( var i=0 ; i<columnsSearch.length ; i++ ) 4353 { 4354 var col = columnsSearch[i]; 4355 4356 _fnFilter( 4357 settings.aiDisplay, 4358 settings, 4359 col.search, 4360 col, 4361 i 4362 ); 4363 4364 $.each(settings.aoColumns[i].searchFixed, function (name, term) { 4365 _fnFilter(settings.aiDisplay, settings, term, {}, i); 4366 }); 4367 } 4368 4369 // And finally global filtering 4370 _fnFilterCustom( settings ); 4371 } 4372 4373 // Tell the draw function we have been filtering 4374 settings.bFiltered = true; 4375 4376 _fnCallbackFire( settings, null, 'search', [settings] ); 4377 } 4378 4379 4380 /** 4381 * Apply custom filtering functions 4382 * 4383 * This is legacy now that we have named functions, but it is widely used 4384 * from 1.x, so it is not yet deprecated. 4385 * @param {object} oSettings dataTables settings object 4386 * @memberof DataTable#oApi 4387 */ 4388 function _fnFilterCustom( settings ) 4389 { 4390 var filters = DataTable.ext.search; 4391 var displayRows = settings.aiDisplay; 4392 var row, rowIdx; 4393 4394 for ( var i=0, ien=filters.length ; i<ien ; i++ ) { 4395 var rows = []; 4396 4397 // Loop over each row and see if it should be included 4398 for ( var j=0, jen=displayRows.length ; j<jen ; j++ ) { 4399 rowIdx = displayRows[ j ]; 4400 row = settings.aoData[ rowIdx ]; 4401 4402 if ( filters[i]( settings, row._aFilterData, rowIdx, row._aData, j ) ) { 4403 rows.push( rowIdx ); 4404 } 4405 } 4406 4407 // So the array reference doesn't break set the results into the 4408 // existing array 4409 displayRows.length = 0; 4410 displayRows.push.apply(displayRows, rows); 4411 } 4412 } 4413 4414 4415 /** 4416 * Filter the data table based on user input and draw the table 4417 */ 4418 function _fnFilter( searchRows, settings, input, options, column ) 4419 { 4420 if ( input === '' ) { 4421 return; 4422 } 4423 4424 var i = 0; 4425 var matched = []; 4426 4427 // Search term can be a function, regex or string - if a string we apply our 4428 // smart filtering regex (assuming the options require that) 4429 var searchFunc = typeof input === 'function' ? input : null; 4430 var rpSearch = input instanceof RegExp 4431 ? input 4432 : searchFunc 4433 ? null 4434 : _fnFilterCreateSearch( input, options ); 4435 4436 // Then for each row, does the test pass. If not, lop the row from the array 4437 for (i=0 ; i<searchRows.length ; i++) { 4438 var row = settings.aoData[ searchRows[i] ]; 4439 var data = column === undefined 4440 ? row._sFilterRow 4441 : row._aFilterData[ column ]; 4442 4443 if ( (searchFunc && searchFunc(data, row._aData, searchRows[i], column)) || (rpSearch && rpSearch.test(data)) ) { 4444 matched.push(searchRows[i]); 4445 } 4446 } 4447 4448 // Mutate the searchRows array 4449 searchRows.length = matched.length; 4450 4451 for (i=0 ; i<matched.length ; i++) { 4452 searchRows[i] = matched[i]; 4453 } 4454 } 4455 4456 4457 /** 4458 * Build a regular expression object suitable for searching a table 4459 * @param {string} sSearch string to search for 4460 * @param {bool} bRegex treat as a regular expression or not 4461 * @param {bool} bSmart perform smart filtering or not 4462 * @param {bool} bCaseInsensitive Do case insensitive matching or not 4463 * @returns {RegExp} constructed object 4464 * @memberof DataTable#oApi 4465 */ 4466 function _fnFilterCreateSearch( search, inOpts ) 4467 { 4468 var not = []; 4469 var options = $.extend({}, { 4470 boundary: false, 4471 caseInsensitive: true, 4472 exact: false, 4473 regex: false, 4474 smart: true 4475 }, inOpts); 4476 4477 if (typeof search !== 'string') { 4478 search = search.toString(); 4479 } 4480 4481 // Remove diacritics if normalize is set up to do so 4482 search = _normalize(search); 4483 4484 if (options.exact) { 4485 return new RegExp( 4486 '^'+_fnEscapeRegex(search)+'$', 4487 options.caseInsensitive ? 'i' : '' 4488 ); 4489 } 4490 4491 search = options.regex ? 4492 search : 4493 _fnEscapeRegex( search ); 4494 4495 if ( options.smart ) { 4496 /* For smart filtering we want to allow the search to work regardless of 4497 * word order. We also want double quoted text to be preserved, so word 4498 * order is important - a la google. And a negative look around for 4499 * finding rows which don't contain a given string. 4500 * 4501 * So this is the sort of thing we want to generate: 4502 * 4503 * ^(?=.*?\bone\b)(?=.*?\btwo three\b)(?=.*?\bfour\b).*$ 4504 */ 4505 var parts = search.match( /!?["\u201C][^"\u201D]+["\u201D]|[^ ]+/g ) || ['']; 4506 var a = parts.map( function ( word ) { 4507 var negative = false; 4508 var m; 4509 4510 // Determine if it is a "does not include" 4511 if ( word.charAt(0) === '!' ) { 4512 negative = true; 4513 word = word.substring(1); 4514 } 4515 4516 // Strip the quotes from around matched phrases 4517 if ( word.charAt(0) === '"' ) { 4518 m = word.match( /^"(.*)"$/ ); 4519 word = m ? m[1] : word; 4520 } 4521 else if ( word.charAt(0) === '\u201C' ) { 4522 // Smart quote match (iPhone users) 4523 m = word.match( /^\u201C(.*)\u201D$/ ); 4524 word = m ? m[1] : word; 4525 } 4526 4527 // For our "not" case, we need to modify the string that is 4528 // allowed to match at the end of the expression. 4529 if (negative) { 4530 if (word.length > 1) { 4531 not.push('(?!'+word+')'); 4532 } 4533 4534 word = ''; 4535 } 4536 4537 return word.replace(/"/g, ''); 4538 } ); 4539 4540 var match = not.length 4541 ? not.join('') 4542 : ''; 4543 4544 var boundary = options.boundary 4545 ? '\\b' 4546 : ''; 4547 4548 search = '^(?=.*?'+boundary+a.join( ')(?=.*?'+boundary )+')('+match+'.)*$'; 4549 } 4550 4551 return new RegExp( search, options.caseInsensitive ? 'i' : '' ); 4552 } 4553 4554 4555 /** 4556 * Escape a string such that it can be used in a regular expression 4557 * @param {string} sVal string to escape 4558 * @returns {string} escaped string 4559 * @memberof DataTable#oApi 4560 */ 4561 var _fnEscapeRegex = DataTable.util.escapeRegex; 4562
4563 var __filter_div = $('<div>')[0]; 4564 var __filter_div_textContent = __filter_div.textContent !== undefined; 4565 4566 // Update the filtering data for each row if needed (by invalidation or first run) 4567 function _fnFilterData ( settings ) 4568 { 4569 var columns = settings.aoColumns; 4570 var data = settings.aoData; 4571 var column; 4572 var j, jen, filterData, cellData, row; 4573 var wasInvalidated = false; 4574 4575 for ( var rowIdx=0 ; rowIdx<data.length ; rowIdx++ ) { 4576 if (! data[rowIdx]) { 4577 continue; 4578 } 4579 4580 row = data[rowIdx]; 4581 4582 if ( ! row._aFilterData ) { 4583 filterData = []; 4584 4585 for ( j=0, jen=columns.length ; j<jen ; j++ ) { 4586 column = columns[j]; 4587 4588 if ( column.bSearchable ) { 4589 cellData = _fnGetCellData( settings, rowIdx, j, 'filter' ); 4590 4591 // Search in DataTables is string based 4592 if ( cellData === null ) { 4593 cellData = ''; 4594 } 4595 4596 if ( typeof cellData !== 'string' && cellData.toString ) { 4597 cellData = cellData.toString(); 4598 } 4599 } 4600 else { 4601 cellData = ''; 4602 } 4603 4604 // If it looks like there is an HTML entity in the string, 4605 // attempt to decode it so sorting works as expected. Note that 4606 // we could use a single line of jQuery to do this, but the DOM 4607 // method used here is much faster https://jsperf.com/html-decode 4608 if ( cellData.indexOf && cellData.indexOf('&') !== -1 ) { 4609 __filter_div.innerHTML = cellData; 4610 cellData = __filter_div_textContent ? 4611 __filter_div.textContent : 4612 __filter_div.innerText; 4613 } 4614 4615 if ( cellData.replace ) { 4616 cellData = cellData.replace(/[\r\n\u2028]/g, ''); 4617 } 4618 4619 filterData.push( cellData ); 4620 } 4621 4622 row._aFilterData = filterData; 4623 row._sFilterRow = filterData.join(' '); 4624 wasInvalidated = true; 4625 } 4626 } 4627 4628 return wasInvalidated; 4629 } 4630 4631 4632 /** 4633 * Draw the table for the first time, adding all required features 4634 * @param {object} settings dataTables settings object 4635 * @memberof DataTable#oApi 4636 */ 4637 function _fnInitialise ( settings ) 4638 { 4639 var i, iAjaxStart=settings.iInitDisplayStart; 4640 4641 /* Ensure that the table data is fully initialised */ 4642 if ( ! settings.bInitialised ) { 4643 setTimeout( function(){ _fnInitialise( settings ); }, 200 ); 4644 return; 4645 } 4646 4647 /* Build and draw the header / footer for the table */ 4648 _fnBuildHead( settings, 'header' ); 4649 _fnBuildHead( settings, 'footer' ); 4650 _fnDrawHead( settings, settings.aoHeader ); 4651 _fnDrawHead( settings, settings.aoFooter ); 4652 4653 // Enable features 4654 _fnAddOptionsHtml( settings ); 4655 _fnSortInit( settings ); 4656 4657 _colGroup( settings ); 4658 4659 /* Okay to show that something is going on now */ 4660 _fnProcessingDisplay( settings, true ); 4661 4662 _fnCallbackFire( settings, null, 'preInit', [settings], true ); 4663 4664 // If there is default sorting required - let's do it. The sort function 4665 // will do the drawing for us. Otherwise we draw the table regardless of the 4666 // Ajax source - this allows the table to look initialised for Ajax sourcing 4667 // data (show 'loading' message possibly) 4668 _fnReDraw( settings ); 4669 4670 var dataSrc = _fnDataSource( settings ); 4671 4672 // Server-side processing init complete is done by _fnAjaxUpdateDraw 4673 if ( dataSrc != 'ssp' ) { 4674 // if there is an ajax source load the data 4675 if ( dataSrc == 'ajax' ) { 4676 _fnBuildAjax( settings, {}, function(json) { 4677 var aData = _fnAjaxDataSrc( settings, json ); 4678 4679 // Got the data - add it to the table 4680 for ( i=0 ; i<aData.length ; i++ ) { 4681 _fnAddData( settings, aData[i] ); 4682 } 4683 4684 // Reset the init display for cookie saving. We've already done 4685 // a filter, and therefore cleared it before. So we need to make 4686 // it appear 'fresh' 4687 settings.iInitDisplayStart = iAjaxStart; 4688 4689 _fnReDraw( settings ); 4690 _fnProcessingDisplay( settings, false ); 4691 _fnInitComplete( settings ); 4692 }, settings ); 4693 } 4694 else { 4695 _fnInitComplete( settings ); 4696 _fnProcessingDisplay( settings, false ); 4697 } 4698 } 4699 } 4700 4701 4702 /** 4703 * Draw the table for the first time, adding all required features 4704 * @param {object} settings dataTables settings object 4705 * @memberof DataTable#oApi 4706 */ 4707 function _fnInitComplete ( settings ) 4708 { 4709 if (settings._bInitComplete) { 4710 return; 4711 } 4712 4713 var args = [settings, settings.json]; 4714 4715 settings._bInitComplete = true; 4716 4717 // Table is fully set up and we have data, so calculate the 4718 // column widths 4719 _fnAdjustColumnSizing( settings ); 4720 4721 _fnCallbackFire( settings, null, 'plugin-init', args, true ); 4722 _fnCallbackFire( settings, 'aoInitComplete', 'init', args, true ); 4723 } 4724 4725 function _fnLengthChange ( settings, val ) 4726 { 4727 var len = parseInt( val, 10 ); 4728 settings._iDisplayLength = len; 4729 4730 _fnLengthOverflow( settings ); 4731 4732 // Fire length change event 4733 _fnCallbackFire( settings, null, 'length', [settings, len] ); 4734 } 4735 4736 /** 4737 * Alter the display settings to change the page 4738 * @param {object} settings DataTables settings object 4739 * @param {string|int} action Paging action to take: "first", "previous", 4740 * "next" or "last" or page number to jump to (integer) 4741 * @param [bool] redraw Automatically draw the update or not 4742 * @returns {bool} true page has changed, false - no change 4743 * @memberof DataTable#oApi 4744 */ 4745 function _fnPageChange ( settings, action, redraw ) 4746 { 4747 var 4748 start = settings._iDisplayStart, 4749 len = settings._iDisplayLength, 4750 records = settings.fnRecordsDisplay(); 4751 4752 if ( records === 0 || len === -1 ) 4753 { 4754 start = 0; 4755 } 4756 else if ( typeof action === "number" ) 4757 { 4758 start = action * len; 4759 4760 if ( start > records ) 4761 { 4762 start = 0; 4763 } 4764 } 4765 else if ( action == "first" ) 4766 { 4767 start = 0; 4768 } 4769 else if ( action == "previous" ) 4770 { 4771 start = len >= 0 ? 4772 start - len : 4773 0; 4774 4775 if ( start < 0 ) 4776 { 4777 start = 0; 4778 } 4779 } 4780 else if ( action == "next" ) 4781 { 4782 if ( start + len < records ) 4783 { 4784 start += len; 4785 } 4786 } 4787 else if ( action == "last" ) 4788 { 4789 start = Math.floor( (records-1) / len) * len; 4790 } 4791 else if ( action === 'ellipsis' ) 4792 { 4793 return; 4794 } 4795 else 4796 { 4797 _fnLog( settings, 0, "Unknown paging action: "+action, 5 ); 4798 } 4799 4800 var changed = settings._iDisplayStart !== start; 4801 settings._iDisplayStart = start; 4802 4803 _fnCallbackFire( settings, null, changed ? 'page' : 'page-nc', [settings] ); 4804 4805 if ( changed && redraw ) { 4806 _fnDraw( settings ); 4807 } 4808 4809 return changed; 4810 } 4811 4812 4813 /** 4814 * Generate the node required for the processing node 4815 * @param {object} settings DataTables settings object 4816 */ 4817 function _processingHtml ( settings ) 4818 { 4819 var table = settings.nTable; 4820 var scrolling = settings.oScroll.sX !== '' || settings.oScroll.sY !== ''; 4821 4822 if ( settings.oFeatures.bProcessing ) { 4823 var n = $('<div/>', { 4824 'id': settings.sTableId + '_processing', 4825 'class': settings.oClasses.processing.container, 4826 'role': 'status' 4827 } ) 4828 .html( settings.oLanguage.sProcessing ) 4829 .append('<div><div></div><div></div><div></div><div></div></div>'); 4830 4831 // Different positioning depending on if scrolling is enabled or not 4832 if (scrolling) { 4833 n.prependTo( $('div.dt-scroll', settings.nTableWrapper) ); 4834 } 4835 else { 4836 n.insertBefore( table ); 4837 } 4838 4839 $(table).on( 'processing.dt.DT', function (e, s, show) { 4840 n.css( 'display', show ? 'block' : 'none' ); 4841 } ); 4842 } 4843 } 4844 4845 4846 /** 4847 * Display or hide the processing indicator 4848 * @param {object} settings DataTables settings object 4849 * @param {bool} show Show the processing indicator (true) or not (false) 4850 */ 4851 function _fnProcessingDisplay ( settings, show ) 4852 { 4853 _fnCallbackFire( settings, null, 'processing', [settings, show] ); 4854 } 4855 /** 4856 * Add any control elements for the table - specifically scrolling 4857 * @param {object} settings dataTables settings object 4858 * @returns {node} Node to add to the DOM 4859 * @memberof DataTable#oApi 4860 */ 4861 function _fnFeatureHtmlTable ( settings ) 4862 { 4863 var table = $(settings.nTable); 4864 4865 // Scrolling from here on in 4866 var scroll = settings.oScroll; 4867
4868 if ( scroll.sX === '' && scroll.sY === '' ) { 4869 return settings.nTable; 4870 } 4871 4872 var scrollX = scroll.sX; 4873 var scrollY = scroll.sY; 4874 var classes = settings.oClasses.scrolling; 4875 var caption = settings.captionNode; 4876 var captionSide = caption ? caption._captionSide : null; 4877 var headerClone = $( table[0].cloneNode(false) ); 4878 var footerClone = $( table[0].cloneNode(false) ); 4879 var footer = table.children('tfoot'); 4880 var _div = '<div/>'; 4881 var size = function ( s ) { 4882 return !s ? null : _fnStringToCss( s ); 4883 }; 4884 4885 if ( ! footer.length ) { 4886 footer = null; 4887 } 4888 4889 /* 4890 * The HTML structure that we want to generate in this function is: 4891 * div - scroller 4892 * div - scroll head 4893 * div - scroll head inner 4894 * table - scroll head table 4895 * thead - thead 4896 * div - scroll body 4897 * table - table (master table) 4898 * thead - thead clone for sizing 4899 * tbody - tbody 4900 * div - scroll foot 4901 * div - scroll foot inner 4902 * table - scroll foot table 4903 * tfoot - tfoot 4904 */ 4905 var scroller = $( _div, { 'class': classes.container } ) 4906 .append( 4907 $(_div, { 'class': classes.header.self } ) 4908 .css( { 4909 overflow: 'hidden', 4910 position: 'relative', 4911 border: 0, 4912 width: scrollX ? size(scrollX) : '100%' 4913 } ) 4914 .append( 4915 $(_div, { 'class': classes.header.inner } ) 4916 .css( { 4917 'box-sizing': 'content-box', 4918 width: scroll.sXInner || '100%' 4919 } ) 4920 .append( 4921 headerClone 4922 .removeAttr('id') 4923 .css( 'margin-left', 0 ) 4924 .append( captionSide === 'top' ? caption : null ) 4925 .append( 4926 table.children('thead') 4927 ) 4928 ) 4929 ) 4930 ) 4931 .append( 4932 $(_div, { 'class': classes.body } ) 4933 .css( { 4934 position: 'relative', 4935 overflow: 'auto', 4936 width: size( scrollX ) 4937 } ) 4938 .append( table ) 4939 ); 4940 4941 if ( footer ) { 4942 scroller.append( 4943 $(_div, { 'class': classes.footer.self } ) 4944 .css( { 4945 overflow: 'hidden', 4946 border: 0, 4947 width: scrollX ? size(scrollX) : '100%' 4948 } ) 4949 .append( 4950 $(_div, { 'class': classes.footer.inner } ) 4951 .append( 4952 footerClone 4953 .removeAttr('id') 4954 .css( 'margin-left', 0 ) 4955 .append( captionSide === 'bottom' ? caption : null ) 4956 .append( 4957 table.children('tfoot') 4958 ) 4959 ) 4960 ) 4961 ); 4962 } 4963 4964 var children = scroller.children(); 4965 var scrollHead = children[0]; 4966 var scrollBody = children[1]; 4967 var scrollFoot = footer ? children[2] : null; 4968 4969 // When the body is scrolled, then we also want to scroll the headers 4970 $(scrollBody).on( 'scroll.DT', function () { 4971 var scrollLeft = this.scrollLeft; 4972 4973 scrollHead.scrollLeft = scrollLeft; 4974 4975 if ( footer ) { 4976 scrollFoot.scrollLeft = scrollLeft; 4977 } 4978 } ); 4979 4980 // When focus is put on the header cells, we might need to scroll the body 4981 $('th, td', scrollHead).on('focus', function () { 4982 var scrollLeft = scrollHead.scrollLeft; 4983 4984 scrollBody.scrollLeft = scrollLeft; 4985 4986 if ( footer ) { 4987 scrollBody.scrollLeft = scrollLeft; 4988 } 4989 }); 4990 4991 $(scrollBody).css('max-height', scrollY); 4992 if (! scroll.bCollapse) { 4993 $(scrollBody).css('height', scrollY); 4994 } 4995 4996 settings.nScrollHead = scrollHead; 4997 settings.nScrollBody = scrollBody; 4998 settings.nScrollFoot = scrollFoot; 4999 5000 // On redraw - align columns 5001 settings.aoDrawCallback.push(_fnScrollDraw); 5002 5003 return scroller[0]; 5004 } 5005 5006 5007 5008 /** 5009 * Update the header, footer and body tables for resizing - i.e. column 5010 * alignment. 5011 * 5012 * Welcome to the most horrible function DataTables. The process that this 5013 * function follows is basically: 5014 * 1. Re-create the table inside the scrolling div 5015 * 2. Correct colgroup > col values if needed 5016 * 3. Copy colgroup > col over to header and footer 5017 * 4. Clean up 5018 * 5019 * @param {object} settings dataTables settings object 5020 * @memberof DataTable#oApi 5021 */ 5022 function _fnScrollDraw ( settings ) 5023 { 5024 // Given that this is such a monster function, a lot of variables are use 5025 // to try and keep the minimised size as small as possible 5026 var 5027 scroll = settings.oScroll, 5028 barWidth = scroll.iBarWidth, 5029 divHeader = $(settings.nScrollHead), 5030 divHeaderInner = divHeader.children('div'), 5031 divHeaderTable = divHeaderInner.children('table'), 5032 divBodyEl = settings.nScrollBody, 5033 divBody = $(divBodyEl), 5034 divFooter = $(settings.nScrollFoot), 5035 divFooterInner = divFooter.children('div'), 5036 divFooterTable = divFooterInner.children('table'), 5037 header = $(settings.nTHead), 5038 table = $(settings.nTable), 5039 footer = settings.nTFoot && $('th, td', settings.nTFoot).length ? $(settings.nTFoot) : null, 5040 browser = settings.oBrowser, 5041 headerCopy, footerCopy; 5042 5043 // If the scrollbar visibility has changed from the last draw, we need to 5044 // adjust the column sizes as the table width will have changed to account 5045 // for the scrollbar 5046 var scrollBarVis = divBodyEl.scrollHeight > divBodyEl.clientHeight; 5047 5048 if ( settings.scrollBarVis !== scrollBarVis && settings.scrollBarVis !== undefined ) { 5049 settings.scrollBarVis = scrollBarVis; 5050 _fnAdjustColumnSizing( settings ); 5051 return; // adjust column sizing will call this function again 5052 } 5053 else { 5054 settings.scrollBarVis = scrollBarVis; 5055 } 5056 5057 // 1. Re-create the table inside the scrolling div 5058 // Remove the old minimised thead and tfoot elements in the inner table 5059 table.children('thead, tfoot').remove(); 5060 5061 // Clone the current header and footer elements and then place it into the inner table 5062 headerCopy = header.clone().prependTo( table ); 5063 headerCopy.find('th, td').removeAttr('tabindex'); 5064 headerCopy.find('[id]').removeAttr('id'); 5065 5066 if ( footer ) { 5067 footerCopy = footer.clone().prependTo( table ); 5068 footerCopy.find('[id]').removeAttr('id'); 5069 } 5070 5071 // 2. Correct colgroup > col values if needed 5072 // It is possible that the cell sizes are smaller than the content, so we need to 5073 // correct colgroup>col for such cases. This can happen if the auto width detection 5074 // uses a cell which has a longer string, but isn't the widest! For example 5075 // "Chief Executive Officer (CEO)" is the longest string in the demo, but 5076 // "Systems Administrator" is actually the widest string since it doesn't collapse. 5077 // Note the use of translating into a column index to get the `col` element. This 5078 // is because of Responsive which might remove `col` elements, knocking the alignment 5079 // of the indexes out. 5080 if (settings.aiDisplay.length) { 5081 // Get the column sizes from the first row in the table 5082 var colSizes = table.children('tbody').eq(0).children('tr').eq(0).children('th, td').map(function (vis) { 5083 return { 5084 idx: _fnVisibleToColumnIndex(settings, vis), 5085 width: $(this).outerWidth() 5086 } 5087 }); 5088 5089 // Check against what the colgroup > col is set to and correct if needed 5090 for (var i=0 ; i<colSizes.length ; i++) { 5091 var colEl = settings.aoColumns[ colSizes[i].idx ].colEl[0]; 5092 var colWidth = colEl.style.width.replace('px', ''); 5093 5094 if (colWidth !== colSizes[i].width) { 5095 colEl.style.width = colSizes[i].width + 'px'; 5096 } 5097 } 5098 } 5099 5100 // 3. Copy the colgroup over to the header and footer 5101 divHeaderTable 5102 .find('colgroup') 5103 .remove(); 5104 5105 divHeaderTable.append(settings.colgroup.clone()); 5106 5107 if ( footer ) { 5108 divFooterTable 5109 .find('colgroup') 5110 .remove(); 5111 5112 divFooterTable.append(settings.colgroup.clone()); 5113 } 5114 5115 // "Hide" the header and footer that we used for the sizing. We need to keep 5116 // the content of the cell so that the width applied to the header and body 5117 // both match, but we want to hide it completely. 5118 $('th, td', headerCopy).each(function () { 5119 $(this.childNodes).wrapAll('<div class="dt-scroll-sizing">'); 5120 }); 5121 5122 if ( footer ) { 5123 $('th, td', footerCopy).each(function () { 5124 $(this.childNodes).wrapAll('<div class="dt-scroll-sizing">'); 5125 }); 5126 } 5127 5128 // 4. Clean up 5129 // Figure out if there are scrollbar present - if so then we need a the header and footer to 5130 // provide a bit more space to allow "overflow" scrolling (i.e. past the scrollbar) 5131 var isScrolling = Math.floor(table.height()) > divBodyEl.clientHeight || divBody.css('overflow-y') == "scroll"; 5132 var paddingSide = 'padding' + (browser.bScrollbarLeft ? 'Left' : 'Right' ); 5133 5134 // Set the width's of the header and footer tables 5135 var outerWidth = table.outerWidth(); 5136 5137 divHeaderTable.css('width', _fnStringToCss( outerWidth )); 5138 divHeaderInner 5139 .css('width', _fnStringToCss( outerWidth )) 5140 .css(paddingSide, isScrolling ? barWidth+"px" : "0px"); 5141 5142 if ( footer ) { 5143 divFooterTable.css('width', _fnStringToCss( outerWidth )); 5144 divFooterInner 5145 .css('width', _fnStringToCss( outerWidth )) 5146 .css(paddingSide, isScrolling ? barWidth+"px" : "0px"); 5147 } 5148 5149 // Correct DOM ordering for colgroup - comes before the thead 5150 table.children('colgroup').prependTo(table); 5151 5152 // Adjust the position of the header in case we loose the y-scrollbar 5153 divBody.trigger('scroll'); 5154 5155 // If sorting or filtering has occurred, jump the scrolling back to the top 5156 // only if we aren't holding the position 5157 if ( (settings.bSorted || settings.bFiltered) && ! settings._drawHold ) { 5158 divBodyEl.scrollTop = 0; 5159 } 5160 } 5161 5162 /** 5163 * Calculate the width of columns for the table 5164 * @param {object} settings dataTables settings object 5165 * @memberof DataTable#oApi 5166 */ 5167 function _fnCalculateColumnWidths ( settings ) 5168 { 5169 // Not interested in doing column width calculation if auto-width is disabled 5170 if (! settings.oFeatures.bAutoWidth) { 5171 return; 5172 } 5173 5174 var 5175 table = settings.nTable,
5176 columns = settings.aoColumns, 5177 scroll = settings.oScroll, 5178 scrollY = scroll.sY, 5179 scrollX = scroll.sX, 5180 scrollXInner = scroll.sXInner, 5181 visibleColumns = _fnGetColumns( settings, 'bVisible' ), 5182 tableWidthAttr = table.getAttribute('width'), // from DOM element 5183 tableContainer = table.parentNode, 5184 i, column, columnIdx; 5185 5186 var styleWidth = table.style.width; 5187 if ( styleWidth && styleWidth.indexOf('%') !== -1 ) { 5188 tableWidthAttr = styleWidth; 5189 } 5190 5191 // Let plug-ins know that we are doing a recalc, in case they have changed any of the 5192 // visible columns their own way (e.g. Responsive uses display:none). 5193 _fnCallbackFire( 5194 settings, 5195 null, 5196 'column-calc', 5197 {visible: visibleColumns}, 5198 false 5199 ); 5200 5201 // Construct a single row, worst case, table with the widest 5202 // node in the data, assign any user defined widths, then insert it into 5203 // the DOM and allow the browser to do all the hard work of calculating 5204 // table widths 5205 var tmpTable = $(table.cloneNode()) 5206 .css( 'visibility', 'hidden' ) 5207 .removeAttr( 'id' ); 5208 5209 // Clean up the table body 5210 tmpTable.append('<tbody>') 5211 var tr = $('<tr/>').appendTo( tmpTable.find('tbody') ); 5212 5213 // Clone the table header and footer - we can't use the header / footer 5214 // from the cloned table, since if scrolling is active, the table's 5215 // real header and footer are contained in different table tags 5216 tmpTable 5217 .append( $(settings.nTHead).clone() ) 5218 .append( $(settings.nTFoot).clone() ); 5219 5220 // Remove any assigned widths from the footer (from scrolling) 5221 tmpTable.find('tfoot th, tfoot td').css('width', ''); 5222 5223 // Apply custom sizing to the cloned header 5224 tmpTable.find('thead th, thead td').each( function () { 5225 // Get the `width` from the header layout 5226 var width = _fnColumnsSumWidth( settings, this, true, false ); 5227 5228 if ( width ) { 5229 this.style.width = width; 5230 5231 // For scrollX we need to force the column width otherwise the 5232 // browser will collapse it. If this width is smaller than the 5233 // width the column requires, then it will have no effect 5234 if ( scrollX ) { 5235 $( this ).append( $('<div/>').css( { 5236 width: width, 5237 margin: 0, 5238 padding: 0, 5239 border: 0, 5240 height: 1 5241 } ) ); 5242 } 5243 } 5244 else { 5245 this.style.width = ''; 5246 } 5247 } ); 5248 5249 // Find the widest piece of data for each column and put it into the table 5250 for ( i=0 ; i<visibleColumns.length ; i++ ) { 5251 columnIdx = visibleColumns[i]; 5252 column = columns[ columnIdx ]; 5253 5254 var longest = _fnGetMaxLenString(settings, columnIdx); 5255 var autoClass = _ext.type.className[column.sType]; 5256 var text = longest + column.sContentPadding; 5257 var insert = longest.indexOf('<') === -1 5258 ? document.createTextNode(text) 5259 : text 5260 5261 $('<td/>') 5262 .addClass(autoClass) 5263 .addClass(column.sClass) 5264 .append(insert) 5265 .appendTo(tr); 5266 } 5267 5268 // Tidy the temporary table - remove name attributes so there aren't 5269 // duplicated in the dom (radio elements for example) 5270 $('[name]', tmpTable).removeAttr('name'); 5271 5272 // Table has been built, attach to the document so we can work with it. 5273 // A holding element is used, positioned at the top of the container 5274 // with minimal height, so it has no effect on if the container scrolls 5275 // or not. Otherwise it might trigger scrolling when it actually isn't 5276 // needed 5277 var holder = $('<div/>').css( scrollX || scrollY ? 5278 { 5279 position: 'absolute', 5280 top: 0, 5281 left: 0, 5282 height: 1, 5283 right: 0, 5284 overflow: 'hidden' 5285 } : 5286 {} 5287 ) 5288 .append( tmpTable ) 5289 .appendTo( tableContainer ); 5290 5291 // When scrolling (X or Y) we want to set the width of the table as 5292 // appropriate. However, when not scrolling leave the table width as it 5293 // is. This results in slightly different, but I think correct behaviour 5294 if ( scrollX && scrollXInner ) { 5295 tmpTable.width( scrollXInner ); 5296 } 5297 else if ( scrollX ) { 5298 tmpTable.css( 'width', 'auto' ); 5299 tmpTable.removeAttr('width'); 5300 5301 // If there is no width attribute or style, then allow the table to 5302 // collapse
5303 if ( tmpTable.width() < tableContainer.clientWidth && tableWidthAttr ) { 5304 tmpTable.width( tableContainer.clientWidth ); 5305 } 5306 } 5307 else if ( scrollY ) { 5308 tmpTable.width( tableContainer.clientWidth ); 5309 } 5310 else if ( tableWidthAttr ) { 5311 tmpTable.width( tableWidthAttr ); 5312 } 5313 5314 // Get the width of each column in the constructed table 5315 var total = 0; 5316 var bodyCells = tmpTable.find('tbody tr').eq(0).children(); 5317 5318 for ( i=0 ; i<visibleColumns.length ; i++ ) { 5319 // Use getBounding for sub-pixel accuracy, which we then want to round up! 5320 var bounding = bodyCells[i].getBoundingClientRect().width; 5321 5322 // Total is tracked to remove any sub-pixel errors as the outerWidth 5323 // of the table might not equal the total given here 5324 total += bounding; 5325 5326 // Width for each column to use 5327 columns[ visibleColumns[i] ].sWidth = _fnStringToCss( bounding ); 5328 } 5329 5330 table.style.width = _fnStringToCss( total ); 5331 5332 // Finished with the table - ditch it 5333 holder.remove(); 5334 5335 // If there is a width attr, we want to attach an event listener which 5336 // allows the table sizing to automatically adjust when the window is 5337 // resized. Use the width attr rather than CSS, since we can't know if the 5338 // CSS is a relative value or absolute - DOM read is always px. 5339 if ( tableWidthAttr ) { 5340 table.style.width = _fnStringToCss( tableWidthAttr ); 5341 } 5342 5343 if ( (tableWidthAttr || scrollX) && ! settings._reszEvt ) { 5344 var bindResize = function () { 5345 $(window).on('resize.DT-'+settings.sInstance, DataTable.util.throttle( function () {
5346 if (! settings.bDestroying) { 5347 _fnAdjustColumnSizing( settings ); 5348 } 5349 } ) ); 5350 }; 5351 5352 bindResize(); 5353 5354 settings._reszEvt = true; 5355 } 5356 } 5357 5358 5359 /** 5360 * Get the maximum strlen for each data column 5361 * @param {object} settings dataTables settings object 5362 * @param {int} colIdx column of interest 5363 * @returns {string} string of the max length 5364 * @memberof DataTable#oApi 5365 */ 5366 function _fnGetMaxLenString( settings, colIdx ) 5367 { 5368 var column = settings.aoColumns[colIdx]; 5369 5370 if (! column.maxLenString) { 5371 var s, max='', maxLen = -1; 5372 5373 for ( var i=0, ien=settings.aiDisplayMaster.length ; i<ien ; i++ ) { 5374 var rowIdx = settings.aiDisplayMaster[i]; 5375 var data = _fnGetRowDisplay(settings, rowIdx)[colIdx]; 5376 5377 var cellString = data && typeof data === 'object' && data.nodeType 5378 ? data.innerHTML 5379 : data+''; 5380 5381 // Remove id / name attributes from elements so they 5382 // don't interfere with existing elements 5383 cellString = cellString 5384 .replace(/id=".*?"/g, '') 5385 .replace(/name=".*?"/g, ''); 5386 5387 s = _stripHtml(cellString) 5388 .replace( / /g, ' ' ); 5389 5390 if ( s.length > maxLen ) { 5391 // We want the HTML in the string, but the length that 5392 // is important is the stripped string 5393 max = cellString; 5394 maxLen = s.length; 5395 } 5396 } 5397 5398 column.maxLenString = max; 5399 } 5400 5401 return column.maxLenString; 5402 } 5403 5404 5405 /** 5406 * Append a CSS unit (only if required) to a string 5407 * @param {string} value to css-ify 5408 * @returns {string} value with css unit 5409 * @memberof DataTable#oApi 5410 */ 5411 function _fnStringToCss( s ) 5412 { 5413 if ( s === null ) { 5414 return '0px'; 5415 } 5416 5417 if ( typeof s == 'number' ) { 5418 return s < 0 ? 5419 '0px' : 5420 s+'px'; 5421 } 5422 5423 // Check it has a unit character already 5424 return s.match(/\d$/) ? 5425 s+'px' : 5426 s; 5427 } 5428 5429 /** 5430 * Re-insert the `col` elements for current visibility 5431 * 5432 * @param {*} settings DT settings 5433 */ 5434 function _colGroup( settings ) { 5435 var cols = settings.aoColumns; 5436 5437 settings.colgroup.empty(); 5438 5439 for (i=0 ; i<cols.length ; i++) { 5440 if (cols[i].bVisible) { 5441 settings.colgroup.append(cols[i].colEl); 5442 } 5443 } 5444 } 5445 5446 5447 function _fnSortInit( settings ) { 5448 var target = settings.nTHead; 5449 var headerRows = target.querySelectorAll('tr'); 5450 var legacyTop = settings.bSortCellsTop; 5451 var notSelector = ':not([data-dt-order="disable"]):not([data-dt-order="icon-only"])'; 5452 5453 // Legacy support for `orderCellsTop` 5454 if (legacyTop === true) { 5455 target = headerRows[0]; 5456 } 5457 else if (legacyTop === false) { 5458 target = headerRows[ headerRows.length - 1 ]; 5459 } 5460 5461 _fnSortAttachListener( 5462 settings, 5463 target, 5464 target === settings.nTHead 5465 ? 'tr'+notSelector+' th'+notSelector+', tr'+notSelector+' td'+notSelector 5466 : 'th'+notSelector+', td'+notSelector 5467 ); 5468 5469 // Need to resolve the user input array into our internal structure 5470 var order = []; 5471 _fnSortResolve( settings, order, settings.aaSorting ); 5472 5473 settings.aaSorting = order; 5474 } 5475 5476 5477 function _fnSortAttachListener(settings, node, selector, column, callback) { 5478 _fnBindAction( node, selector, function (e) { 5479 var run = false; 5480 var columns = column === undefined 5481 ? _fnColumnsFromHeader( e.target ) 5482 : [column]; 5483 5484 if ( columns.length ) { 5485 for ( var i=0, ien=columns.length ; i<ien ; i++ ) { 5486 var ret = _fnSortAdd( settings, columns[i], i, e.shiftKey ); 5487 5488 if (ret !== false) { 5489 run = true; 5490 } 5491 5492 // If the first entry is no sort, then subsequent 5493 // sort columns are ignored 5494 if (settings.aaSorting.length === 1 && settings.aaSorting[0][1] === '') { 5495 break; 5496 } 5497 } 5498 5499 if (run) { 5500 _fnProcessingDisplay( settings, true ); 5501 5502 // Allow the processing display to show 5503 setTimeout( function () { 5504 _fnSort( settings ); 5505 _fnSortDisplay( settings, settings.aiDisplay ); 5506 5507 // Sort processing done - redraw has its own processing display 5508 _fnProcessingDisplay( settings, false ); 5509 5510 _fnReDraw( settings, false, false ); 5511 5512 if (callback) { 5513 callback(); 5514 } 5515 }, 0); 5516 } 5517 } 5518 } ); 5519 } 5520 5521 /** 5522 * Sort the display array to match the master's order 5523 * @param {*} settings 5524 */ 5525 function _fnSortDisplay(settings, display) { 5526 if (display.length < 2) { 5527 return; 5528 } 5529 5530 var master = settings.aiDisplayMaster; 5531 var masterMap = {}; 5532 var map = {}; 5533 var i; 5534 5535 // Rather than needing an `indexOf` on master array, we can create a map 5536 for (i=0 ; i<master.length ; i++) { 5537 masterMap[master[i]] = i; 5538 } 5539 5540 // And then cache what would be the indexOf fom the display 5541 for (i=0 ; i<display.length ; i++) { 5542 map[display[i]] = masterMap[display[i]]; 5543 } 5544 5545 display.sort(function(a, b){ 5546 // Short version of this function is simply `master.indexOf(a) - master.indexOf(b);` 5547 return map[a] - map[b]; 5548 }); 5549 } 5550 5551
5552 function _fnSortResolve (settings, nestedSort, sort) { 5553 var push = function ( a ) { 5554 if ($.isPlainObject(a)) { 5555 if (a.idx !== undefined) { 5556 // Index based ordering 5557 nestedSort.push([a.idx, a.dir]); 5558 } 5559 else if (a.name) { 5560 // Name based ordering 5561 var cols = _pluck( settings.aoColumns, 'sName'); 5562 var idx = cols.indexOf(a.name); 5563 5564 if (idx !== -1) { 5565 nestedSort.push([idx, a.dir]); 5566 } 5567 } 5568 } 5569 else { 5570 // Plain column index and direction pair 5571 nestedSort.push(a); 5572 } 5573 }; 5574 5575 if ( $.isPlainObject(sort) ) { 5576 // Object 5577 push(sort); 5578 } 5579 else if ( sort.length && typeof sort[0] === 'number' ) { 5580 // 1D array 5581 push(sort); 5582 } 5583 else if ( sort.length ) { 5584 // 2D array 5585 for (var z=0; z<sort.length; z++) { 5586 push(sort[z]); // Object or array 5587 } 5588 } 5589 } 5590 5591 5592 function _fnSortFlatten ( settings ) 5593 { 5594 var 5595 i, k, kLen, 5596 aSort = [], 5597 extSort = DataTable.ext.type.order, 5598 aoColumns = settings.aoColumns, 5599 aDataSort, iCol, sType, srcCol, 5600 fixed = settings.aaSortingFixed, 5601 fixedObj = $.isPlainObject( fixed ), 5602 nestedSort = []; 5603 5604 if ( ! settings.oFeatures.bSort ) { 5605 return aSort; 5606 } 5607 5608 // Build the sort array, with pre-fix and post-fix options if they have been 5609 // specified 5610 if ( Array.isArray( fixed ) ) { 5611 _fnSortResolve( settings, nestedSort, fixed ); 5612 } 5613 5614 if ( fixedObj && fixed.pre ) { 5615 _fnSortResolve( settings, nestedSort, fixed.pre ); 5616 } 5617 5618 _fnSortResolve( settings, nestedSort, settings.aaSorting ); 5619 5620 if (fixedObj && fixed.post ) { 5621 _fnSortResolve( settings, nestedSort, fixed.post ); 5622 } 5623 5624 for ( i=0 ; i<nestedSort.length ; i++ ) 5625 { 5626 srcCol = nestedSort[i][0]; 5627 5628 if ( aoColumns[ srcCol ] ) { 5629 aDataSort = aoColumns[ srcCol ].aDataSort; 5630 5631 for ( k=0, kLen=aDataSort.length ; k<kLen ; k++ ) 5632 { 5633 iCol = aDataSort[k]; 5634 sType = aoColumns[ iCol ].sType || 'string'; 5635 5636 if ( nestedSort[i]._idx === undefined ) { 5637 nestedSort[i]._idx = aoColumns[iCol].asSorting.indexOf(nestedSort[i][1]); 5638 } 5639 5640 if ( nestedSort[i][1] ) { 5641 aSort.push( { 5642 src: srcCol, 5643 col: iCol, 5644 dir: nestedSort[i][1], 5645 index: nestedSort[i]._idx, 5646 type: sType, 5647 formatter: extSort[ sType+"-pre" ], 5648 sorter: extSort[ sType+"-"+nestedSort[i][1] ] 5649 } ); 5650 } 5651 } 5652 } 5653 } 5654 5655 return aSort; 5656 } 5657 5658 /** 5659 * Change the order of the table 5660 * @param {object} oSettings dataTables settings object 5661 * @memberof DataTable#oApi 5662 */ 5663 function _fnSort ( oSettings, col, dir ) 5664 { 5665 var 5666 i, ien, iLen, 5667 aiOrig = [], 5668 extSort = DataTable.ext.type.order, 5669 aoData = oSettings.aoData, 5670 sortCol, 5671 displayMaster = oSettings.aiDisplayMaster, 5672 aSort; 5673 5674 // Resolve any column types that are unknown due to addition or invalidation 5675 // @todo Can this be moved into a 'data-ready' handler which is called when 5676 // data is going to be used in the table? 5677 _fnColumnTypes( oSettings ); 5678 5679 // Allow a specific column to be sorted, which will _not_ alter the display 5680 // master 5681 if (col !== undefined) { 5682 var srcCol = oSettings.aoColumns[col]; 5683 aSort = [{ 5684 src: col, 5685 col: col, 5686 dir: dir, 5687 index: 0, 5688 type: srcCol.sType, 5689 formatter: extSort[ srcCol.sType+"-pre" ], 5690 sorter: extSort[ srcCol.sType+"-"+dir ] 5691 }]; 5692 displayMaster = displayMaster.slice(); 5693 } 5694 else { 5695 aSort = _fnSortFlatten( oSettings ); 5696 } 5697 5698 for ( i=0, ien=aSort.length ; i<ien ; i++ ) { 5699 sortCol = aSort[i]; 5700 5701 // Load the data needed for the sort, for each cell 5702 _fnSortData( oSettings, sortCol.col ); 5703 } 5704 5705 /* No sorting required if server-side or no sorting array */ 5706 if ( _fnDataSource( oSettings ) != 'ssp' && aSort.length !== 0 ) 5707 { 5708 // Reset the initial positions on each pass so we get a stable sort 5709 for ( i=0, iLen=displayMaster.length ; i<iLen ; i++ ) { 5710 aiOrig[ i ] = i; 5711 } 5712 5713 // If the first sort is desc, then reverse the array to preserve original 5714 // order, just in reverse 5715 if (aSort.length && aSort[0].dir === 'desc') { 5716 aiOrig.reverse(); 5717 } 5718
5719 /* Do the sort - here we want multi-column sorting based on a given data source (column) 5720 * and sorting function (from oSort) in a certain direction. It's reasonably complex to 5721 * follow on it's own, but this is what we want (example two column sorting): 5722 * fnLocalSorting = function(a,b){ 5723 * var test; 5724 * test = oSort['string-asc']('data11', 'data12'); 5725 * if (test !== 0) 5726 * return test; 5727 * test = oSort['numeric-desc']('data21', 'data22'); 5728 * if (test !== 0) 5729 * return test; 5730 * return oSort['numeric-asc']( aiOrig[a], aiOrig[b] ); 5731 * } 5732 * Basically we have a test for each sorting column, if the data in that column is equal, 5733 * test the next column. If all columns match, then we use a numeric sort on the row 5734 * positions in the original data array to provide a stable sort. 5735 */ 5736 displayMaster.sort( function ( a, b ) { 5737 var 5738 x, y, k, test, sort, 5739 len=aSort.length, 5740 dataA = aoData[a]._aSortData, 5741 dataB = aoData[b]._aSortData; 5742 5743 for ( k=0 ; k<len ; k++ ) { 5744 sort = aSort[k]; 5745 5746 // Data, which may have already been through a `-pre` function 5747 x = dataA[ sort.col ]; 5748 y = dataB[ sort.col ]; 5749 5750 if (sort.sorter) { 5751 // If there is a custom sorter (`-asc` or `-desc`) for this 5752 // data type, use it 5753 test = sort.sorter(x, y); 5754 5755 if ( test !== 0 ) { 5756 return test; 5757 } 5758 } 5759 else { 5760 // Otherwise, use generic sorting 5761 test = x<y ? -1 : x>y ? 1 : 0; 5762 5763 if ( test !== 0 ) { 5764 return sort.dir === 'asc' ? test : -test; 5765 } 5766 } 5767 } 5768 5769 x = aiOrig[a]; 5770 y = aiOrig[b]; 5771 5772 return x<y ? -1 : x>y ? 1 : 0; 5773 } ); 5774 } 5775 else if ( aSort.length === 0 ) { 5776 // Apply index order 5777 displayMaster.sort(function (x, y) { 5778 return x<y ? -1 : x>y ? 1 : 0; 5779 }); 5780 } 5781 5782 if (col === undefined) { 5783 // Tell the draw function that we have sorted the data 5784 oSettings.bSorted = true; 5785 5786 _fnCallbackFire( oSettings, null, 'order', [oSettings, aSort] ); 5787 } 5788
5789 return displayMaster; 5790 } 5791 5792 5793 /** 5794 * Function to run on user sort request 5795 * @param {object} settings dataTables settings object 5796 * @param {node} attachTo node to attach the handler to 5797 * @param {int} colIdx column sorting index 5798 * @param {int} addIndex Counter 5799 * @param {boolean} [shift=false] Shift click add 5800 * @param {function} [callback] callback function 5801 * @memberof DataTable#oApi 5802 */ 5803 function _fnSortAdd ( settings, colIdx, addIndex, shift ) 5804 { 5805 var col = settings.aoColumns[ colIdx ]; 5806 var sorting = settings.aaSorting; 5807 var asSorting = col.asSorting; 5808 var nextSortIdx; 5809 var next = function ( a, overflow ) { 5810 var idx = a._idx; 5811 if ( idx === undefined ) { 5812 idx = asSorting.indexOf(a[1]); 5813 } 5814 5815 return idx+1 < asSorting.length ? 5816 idx+1 : 5817 overflow ? 5818 null : 5819 0; 5820 }; 5821 5822 if ( ! col.bSortable ) { 5823 return false; 5824 } 5825 5826 // Convert to 2D array if needed 5827 if ( typeof sorting[0] === 'number' ) { 5828 sorting = settings.aaSorting = [ sorting ]; 5829 } 5830 5831 // If appending the sort then we are multi-column sorting 5832 if ( (shift || addIndex) && settings.oFeatures.bSortMulti ) { 5833 // Are we already doing some kind of sort on this column? 5834 var sortIdx = _pluck(sorting, '0').indexOf(colIdx); 5835 5836 if ( sortIdx !== -1 ) { 5837 // Yes, modify the sort 5838 nextSortIdx = next( sorting[sortIdx], true ); 5839 5840 if ( nextSortIdx === null && sorting.length === 1 ) { 5841 nextSortIdx = 0; // can't remove sorting completely 5842 } 5843 5844 if ( nextSortIdx === null ) { 5845 sorting.splice( sortIdx, 1 ); 5846 } 5847 else { 5848 sorting[sortIdx][1] = asSorting[ nextSortIdx ]; 5849 sorting[sortIdx]._idx = nextSortIdx; 5850 } 5851 } 5852 else if (shift) { 5853 // No sort on this column yet, being added by shift click 5854 // add it as itself 5855 sorting.push( [ colIdx, asSorting[0], 0 ] ); 5856 sorting[sorting.length-1]._idx = 0; 5857 } 5858 else { 5859 // No sort on this column yet, being added from a colspan 5860 // so add with same direction as first column 5861 sorting.push( [ colIdx, sorting[0][1], 0 ] ); 5862 sorting[sorting.length-1]._idx = 0; 5863 } 5864 } 5865 else if ( sorting.length && sorting[0][0] == colIdx ) { 5866 // Single column - already sorting on this column, modify the sort 5867 nextSortIdx = next( sorting[0] ); 5868 5869 sorting.length = 1; 5870 sorting[0][1] = asSorting[ nextSortIdx ]; 5871 sorting[0]._idx = nextSortIdx; 5872 } 5873 else { 5874 // Single column - sort only on this column 5875 sorting.length = 0; 5876 sorting.push( [ colIdx, asSorting[0] ] ); 5877 sorting[0]._idx = 0; 5878 } 5879 } 5880 5881 5882 /** 5883 * Set the sorting classes on table's body, Note: it is safe to call this function 5884 * when bSort and bSortClasses are false 5885 * @param {object} oSettings dataTables settings object 5886 * @memberof DataTable#oApi 5887 */ 5888 function _fnSortingClasses( settings ) 5889 { 5890 var oldSort = settings.aLastSort; 5891 var sortClass = settings.oClasses.order.position; 5892 var sort = _fnSortFlatten( settings ); 5893 var features = settings.oFeatures; 5894 var i, ien, colIdx; 5895 5896 if ( features.bSort && features.bSortClasses ) { 5897 // Remove old sorting classes 5898 for ( i=0, ien=oldSort.length ; i<ien ; i++ ) { 5899 colIdx = oldSort[i].src; 5900 5901 // Remove column sorting 5902 $( _pluck( settings.aoData, 'anCells', colIdx ) ) 5903 .removeClass( sortClass + (i<2 ? i+1 : 3) ); 5904 } 5905 5906 // Add new column sorting 5907 for ( i=0, ien=sort.length ; i<ien ; i++ ) { 5908 colIdx = sort[i].src; 5909 5910 $( _pluck( settings.aoData, 'anCells', colIdx ) ) 5911 .addClass( sortClass + (i<2 ? i+1 : 3) ); 5912 } 5913 } 5914 5915 settings.aLastSort = sort; 5916 } 5917 5918 5919 // Get the data to sort a column, be it from cache, fresh (populating the 5920 // cache), or from a sort formatter 5921 function _fnSortData( settings, colIdx ) 5922 { 5923 // Custom sorting function - provided by the sort data type 5924 var column = settings.aoColumns[ colIdx ]; 5925 var customSort = DataTable.ext.order[ column.sSortDataType ]; 5926 var customData; 5927 5928 if ( customSort ) { 5929 customData = customSort.call( settings.oInstance, settings, colIdx, 5930 _fnColumnIndexToVisible( settings, colIdx ) 5931 ); 5932 } 5933 5934 // Use / populate cache 5935 var row, cellData; 5936 var formatter = DataTable.ext.type.order[ column.sType+"-pre" ]; 5937 var data = settings.aoData; 5938 5939 for ( var rowIdx=0 ; rowIdx<data.length ; rowIdx++ ) { 5940 // Sparse array 5941 if (! data[rowIdx]) { 5942 continue; 5943 } 5944 5945 row = data[rowIdx]; 5946 5947 if ( ! row._aSortData ) { 5948 row._aSortData = []; 5949 } 5950 5951 if ( ! row._aSortData[colIdx] || customSort ) { 5952 cellData = customSort ? 5953 customData[rowIdx] : // If there was a custom sort function, use data from there 5954 _fnGetCellData( settings, rowIdx, colIdx, 'sort' ); 5955 5956 row._aSortData[ colIdx ] = formatter ? 5957 formatter( cellData, settings ) : 5958 cellData; 5959 } 5960 } 5961 } 5962 5963 5964 /** 5965 * State information for a table 5966 * 5967 * @param {*} settings 5968 * @returns State object 5969 */ 5970 function _fnSaveState ( settings ) 5971 { 5972 if (settings._bLoadingState) { 5973 return; 5974 } 5975 5976 /* Store the interesting variables */ 5977 var state = { 5978 time: +new Date(), 5979 start: settings._iDisplayStart, 5980 length: settings._iDisplayLength, 5981 order: $.extend( true, [], settings.aaSorting ), 5982 search: $.extend({}, settings.oPreviousSearch), 5983 columns: settings.aoColumns.map( function ( col, i ) { 5984 return { 5985 visible: col.bVisible, 5986 search: $.extend({}, settings.aoPreSearchCols[i]) 5987 }; 5988 } ) 5989 }; 5990 5991 settings.oSavedState = state; 5992 _fnCallbackFire( settings, "aoStateSaveParams", 'stateSaveParams', [settings, state] ); 5993
5994 if ( settings.oFeatures.bStateSave && !settings.bDestroying ) 5995 { 5996 settings.fnStateSaveCallback.call( settings.oInstance, settings, state ); 5997 } 5998 } 5999 6000 6001 /** 6002 * Attempt to load a saved table state 6003 * @param {object} oSettings dataTables settings object 6004 * @param {object} oInit DataTables init object so we can override settings 6005 * @param {function} callback Callback to execute when the state has been loaded 6006 * @memberof DataTable#oApi 6007 */ 6008 function _fnLoadState ( settings, init, callback ) 6009 { 6010 if ( ! settings.oFeatures.bStateSave ) { 6011 callback(); 6012 return; 6013 } 6014 6015 var loaded = function(state) { 6016 _fnImplementState(settings, state, callback); 6017 } 6018 6019 var state = settings.fnStateLoadCallback.call( settings.oInstance, settings, loaded ); 6020 6021 if ( state !== undefined ) { 6022 _fnImplementState( settings, state, callback ); 6023 } 6024 // otherwise, wait for the loaded callback to be executed 6025 6026 return true; 6027 } 6028 6029 function _fnImplementState ( settings, s, callback) { 6030 var i, ien; 6031 var columns = settings.aoColumns; 6032 settings._bLoadingState = true; 6033 6034 // When StateRestore was introduced the state could now be implemented at any time 6035 // Not just initialisation. To do this an api instance is required in some places 6036 var api = settings._bInitComplete ? new DataTable.Api(settings) : null; 6037 6038 if ( ! s || ! s.time ) { 6039 settings._bLoadingState = false; 6040 callback(); 6041 return; 6042 } 6043 6044 // Reject old data 6045 var duration = settings.iStateDuration; 6046 if ( duration > 0 && s.time < +new Date() - (duration*1000) ) { 6047 settings._bLoadingState = false; 6048 callback(); 6049 return; 6050 } 6051 6052 // Allow custom and plug-in manipulation functions to alter the saved data set and 6053 // cancelling of loading by returning false 6054 var abStateLoad = _fnCallbackFire( settings, 'aoStateLoadParams', 'stateLoadParams', [settings, s] ); 6055 if ( abStateLoad.indexOf(false) !== -1 ) { 6056 settings._bLoadingState = false; 6057 callback(); 6058 return; 6059 } 6060 6061 // Number of columns have changed - all bets are off, no restore of settings 6062 if ( s.columns && columns.length !== s.columns.length ) { 6063 settings._bLoadingState = false; 6064 callback(); 6065 return; 6066 } 6067 6068 // Store the saved state so it might be accessed at any time 6069 settings.oLoadedState = $.extend( true, {}, s ); 6070 6071 // This is needed for ColReorder, which has to happen first to allow all 6072 // the stored indexes to be usable. It is not publicly documented. 6073 _fnCallbackFire( settings, null, 'stateLoadInit', [settings, s], true ); 6074 6075 // Page Length 6076 if ( s.length !== undefined ) { 6077 // If already initialised just set the value directly so that the select element is also updated 6078 if (api) { 6079 api.page.len(s.length) 6080 } 6081 else { 6082 settings._iDisplayLength = s.length; 6083 } 6084 } 6085 6086 // Restore key features - todo - for 1.11 this needs to be done by 6087 // subscribed events 6088 if ( s.start !== undefined ) { 6089 if(api === null) { 6090 settings._iDisplayStart = s.start; 6091 settings.iInitDisplayStart = s.start; 6092 } 6093 else { 6094 _fnPageChange(settings, s.start/settings._iDisplayLength); 6095 } 6096 } 6097 6098 // Order 6099 if ( s.order !== undefined ) { 6100 settings.aaSorting = []; 6101 $.each( s.order, function ( i, col ) { 6102 settings.aaSorting.push( col[0] >= columns.length ? 6103 [ 0, col[1] ] : 6104 col 6105 ); 6106 } ); 6107 } 6108 6109 // Search 6110 if ( s.search !== undefined ) { 6111 $.extend( settings.oPreviousSearch, s.search ); 6112 } 6113 6114 // Columns 6115 if ( s.columns ) { 6116 for ( i=0, ien=s.columns.length ; i<ien ; i++ ) { 6117 var col = s.columns[i]; 6118 6119 // Visibility 6120 if ( col.visible !== undefined ) { 6121 // If the api is defined, the table has been initialised so we need to use it rather than internal settings 6122 if (api) { 6123 // Don't redraw the columns on every iteration of this loop, we will do this at the end instead 6124 api.column(i).visible(col.visible, false); 6125 } 6126 else { 6127 columns[i].bVisible = col.visible; 6128 } 6129 } 6130 6131 // Search 6132 if ( col.search !== undefined ) { 6133 $.extend( settings.aoPreSearchCols[i], col.search ); 6134 } 6135 } 6136 6137 // If the api is defined then we need to adjust the columns once the visibility has been changed 6138 if (api) { 6139 api.columns.adjust(); 6140 } 6141 } 6142 6143 settings._bLoadingState = false; 6144 _fnCallbackFire( settings, 'aoStateLoaded', 'stateLoaded', [settings, s] ); 6145 callback(); 6146 } 6147 6148 /** 6149 * Log an error message 6150 * @param {object} settings dataTables settings object 6151 * @param {int} level log error messages, or display them to the user 6152 * @param {string} msg error message 6153 * @param {int} tn Technical note id to get more information about the error. 6154 * @memberof DataTable#oApi 6155 */ 6156 function _fnLog( settings, level, msg, tn ) 6157 { 6158 msg = 'DataTables warning: '+ 6159 (settings ? 'table id='+settings.sTableId+' - ' : '')+msg; 6160 6161 if ( tn ) { 6162 msg += '. For more information about this error, please see '+ 6163 'https://datatables.net/tn/'+tn; 6164 } 6165 6166 if ( ! level ) { 6167 // Backwards compatibility pre 1.10 6168 var ext = DataTable.ext; 6169 var type = ext.sErrMode || ext.errMode; 6170 6171 if ( settings ) { 6172 _fnCallbackFire( settings, null, 'dt-error', [ settings, tn, msg ], true ); 6173 } 6174 6175 if ( type == 'alert' ) { 6176 alert( msg ); 6177 } 6178 else if ( type == 'throw' ) { 6179 throw new Error(msg); 6180 } 6181 else if ( typeof type == 'function' ) { 6182 type( settings, tn, msg ); 6183 } 6184 } 6185 else if ( window.console && console.log ) { 6186 console.log( msg ); 6187 } 6188 } 6189 6190 6191 /** 6192 * See if a property is defined on one object, if so assign it to the other object 6193 * @param {object} ret target object 6194 * @param {object} src source object 6195 * @param {string} name property 6196 * @param {string} [mappedName] name to map too - optional, name used if not given 6197 * @memberof DataTable#oApi 6198 */ 6199 function _fnMap( ret, src, name, mappedName ) 6200 { 6201 if ( Array.isArray( name ) ) { 6202 $.each( name, function (i, val) { 6203 if ( Array.isArray( val ) ) { 6204 _fnMap( ret, src, val[0], val[1] ); 6205 } 6206 else { 6207 _fnMap( ret, src, val ); 6208 } 6209 } ); 6210 6211 return; 6212 } 6213 6214 if ( mappedName === undefined ) { 6215 mappedName = name; 6216 } 6217 6218 if ( src[name] !== undefined ) { 6219 ret[mappedName] = src[name]; 6220 } 6221 } 6222 6223 6224 /** 6225 * Extend objects - very similar to jQuery.extend, but deep copy objects, and 6226 * shallow copy arrays. The reason we need to do this, is that we don't want to 6227 * deep copy array init values (such as aaSorting) since the dev wouldn't be 6228 * able to override them, but we do want to deep copy arrays. 6229 * @param {object} out Object to extend 6230 * @param {object} extender Object from which the properties will be applied to 6231 * out 6232 * @param {boolean} breakRefs If true, then arrays will be sliced to take an 6233 * independent copy with the exception of the `data` or `aaData` parameters 6234 * if they are present. This is so you can pass in a collection to 6235 * DataTables and have that used as your data source without breaking the 6236 * references 6237 * @returns {object} out Reference, just for convenience - out === the return. 6238 * @memberof DataTable#oApi 6239 * @todo This doesn't take account of arrays inside the deep copied objects. 6240 */ 6241 function _fnExtend( out, extender, breakRefs ) 6242 { 6243 var val; 6244 6245 for ( var prop in extender ) { 6246 if ( Object.prototype.hasOwnProperty.call(extender, prop) ) { 6247 val = extender[prop]; 6248 6249 if ( $.isPlainObject( val ) ) { 6250 if ( ! $.isPlainObject( out[prop] ) ) { 6251 out[prop] = {}; 6252 } 6253 $.extend( true, out[prop], val ); 6254 } 6255 else if ( breakRefs && prop !== 'data' && prop !== 'aaData' && Array.isArray(val) ) { 6256 out[prop] = val.slice(); 6257 } 6258 else { 6259 out[prop] = val; 6260 } 6261 } 6262 } 6263 6264 return out; 6265 } 6266 6267 6268 /** 6269 * Bind an event handers to allow a click or return key to activate the callback. 6270 * This is good for accessibility since a return on the keyboard will have the 6271 * same effect as a click, if the element has focus. 6272 * @param {element} n Element to bind the action to 6273 * @param {object|string} selector Selector (for delegated events) or data object 6274 * to pass to the triggered function 6275 * @param {function} fn Callback function for when the event is triggered 6276 * @memberof DataTable#oApi 6277 */ 6278 function _fnBindAction( n, selector, fn ) 6279 { 6280 $(n) 6281 .on( 'click.DT', selector, function (e) { 6282 fn(e); 6283 } ) 6284 .on( 'keypress.DT', selector, function (e){ 6285 if ( e.which === 13 ) { 6286 e.preventDefault(); 6287 fn(e); 6288 } 6289 } ) 6290 .on( 'selectstart.DT', selector, function () { 6291 // Don't want a double click resulting in text selection 6292 return false; 6293 } ); 6294 } 6295 6296 6297 /** 6298 * Register a callback function. Easily allows a callback function to be added to 6299 * an array store of callback functions that can then all be called together. 6300 * @param {object} settings dataTables settings object 6301 * @param {string} store Name of the array storage for the callbacks in oSettings 6302 * @param {function} fn Function to be called back 6303 * @memberof DataTable#oApi 6304 */ 6305 function _fnCallbackReg( settings, store, fn ) 6306 { 6307 if ( fn ) { 6308 settings[store].push(fn); 6309 } 6310 } 6311 6312 6313 /** 6314 * Fire callback functions and trigger events. Note that the loop over the 6315 * callback array store is done backwards! Further note that you do not want to 6316 * fire off triggers in time sensitive applications (for example cell creation) 6317 * as its slow. 6318 * @param {object} settings dataTables settings object 6319 * @param {string} callbackArr Name of the array storage for the callbacks in 6320 * oSettings 6321 * @param {string} eventName Name of the jQuery custom event to trigger. If 6322 * null no trigger is fired 6323 * @param {array} args Array of arguments to pass to the callback function / 6324 * trigger 6325 * @param {boolean} [bubbles] True if the event should bubble 6326 * @memberof DataTable#oApi 6327 */
6328 function _fnCallbackFire( settings, callbackArr, eventName, args, bubbles ) 6329 { 6330 var ret = []; 6331 6332 if ( callbackArr ) { 6333 ret = settings[callbackArr].slice().reverse().map( function (val) { 6334 return val.apply( settings.oInstance, args ); 6335 } ); 6336 } 6337 6338 if ( eventName !== null) { 6339 var e = $.Event( eventName+'.dt' ); 6340 var table = $(settings.nTable); 6341 6342 // Expose the DataTables API on the event object for easy access 6343 e.dt = settings.api; 6344 6345 table[bubbles ? 'trigger' : 'triggerHandler']( e, args ); 6346 6347 // If not yet attached to the document, trigger the event 6348 // on the body directly to sort of simulate the bubble 6349 if (bubbles && table.parents('body').length === 0) { 6350 $('body').trigger( e, args ); 6351 } 6352 6353 ret.push( e.result ); 6354 } 6355 6356 return ret; 6357 } 6358 6359 6360 function _fnLengthOverflow ( settings ) 6361 { 6362 var 6363 start = settings._iDisplayStart, 6364 end = settings.fnDisplayEnd(), 6365 len = settings._iDisplayLength; 6366 6367 /* If we have space to show extra rows (backing up from the end point - then do so */ 6368 if ( start >= end ) 6369 { 6370 start = end - len; 6371 } 6372 6373 // Keep the start record on the current page 6374 start -= (start % len); 6375 6376 if ( len === -1 || start < 0 ) 6377 { 6378 start = 0; 6379 } 6380 6381 settings._iDisplayStart = start; 6382 } 6383 6384 6385 function _fnRenderer( settings, type ) 6386 { 6387 var renderer = settings.renderer; 6388 var host = DataTable.ext.renderer[type]; 6389 6390 if ( $.isPlainObject( renderer ) && renderer[type] ) { 6391 // Specific renderer for this type. If available use it, otherwise use 6392 // the default. 6393 return host[renderer[type]] || host._; 6394 } 6395 else if ( typeof renderer === 'string' ) { 6396 // Common renderer - if there is one available for this type use it, 6397 // otherwise use the default 6398 return host[renderer] || host._; 6399 } 6400 6401 // Use the default 6402 return host._; 6403 } 6404 6405 6406 /** 6407 * Detect the data source being used for the table. Used to simplify the code 6408 * a little (ajax) and to make it compress a little smaller. 6409 * 6410 * @param {object} settings dataTables settings object 6411 * @returns {string} Data source 6412 * @memberof DataTable#oApi 6413 */ 6414 function _fnDataSource ( settings ) 6415 { 6416 if ( settings.oFeatures.bServerSide ) { 6417 return 'ssp'; 6418 } 6419 else if ( settings.ajax ) { 6420 return 'ajax'; 6421 } 6422 return 'dom'; 6423 } 6424 6425 /** 6426 * Common replacement for language strings 6427 * 6428 * @param {*} settings DT settings object 6429 * @param {*} str String with values to replace 6430 * @param {*} entries Plural number for _ENTRIES_ - can be undefined 6431 * @returns String 6432 */ 6433 function _fnMacros ( settings, str, entries ) 6434 { 6435 // When infinite scrolling, we are always starting at 1. _iDisplayStart is 6436 // used only internally 6437 var 6438 formatter = settings.fnFormatNumber, 6439 start = settings._iDisplayStart+1, 6440 len = settings._iDisplayLength, 6441 vis = settings.fnRecordsDisplay(), 6442 max = settings.fnRecordsTotal(), 6443 all = len === -1; 6444 6445 return str. 6446 replace(/_START_/g, formatter.call( settings, start ) ). 6447 replace(/_END_/g, formatter.call( settings, settings.fnDisplayEnd() ) ). 6448 replace(/_MAX_/g, formatter.call( settings, max ) ). 6449 replace(/_TOTAL_/g, formatter.call( settings, vis ) ). 6450 replace(/_PAGE_/g, formatter.call( settings, all ? 1 : Math.ceil( start / len ) ) ). 6451 replace(/_PAGES_/g, formatter.call( settings, all ? 1 : Math.ceil( vis / len ) ) ). 6452 replace(/_ENTRIES_/g, settings.api.i18n('entries', '', entries) ). 6453 replace(/_ENTRIES-MAX_/g, settings.api.i18n('entries', '', max) ). 6454 replace(/_ENTRIES-TOTAL_/g, settings.api.i18n('entries', '', vis) ); 6455 } 6456 6457 6458 6459 /** 6460 * Computed structure of the DataTables API, defined by the options passed to 6461 * `DataTable.Api.register()` when building the API. 6462 * 6463 * The structure is built in order to speed creation and extension of the Api 6464 * objects since the extensions are effectively pre-parsed. 6465 * 6466 * The array is an array of objects with the following structure, where this 6467 * base array represents the Api prototype base: 6468 * 6469 * [ 6470 * { 6471 * name: 'data' -- string - Property name 6472 * val: function () {}, -- function - Api method (or undefined if just an object 6473 * methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result 6474 * propExt: [ ... ] -- array - Array of Api object definitions to extend the property 6475 * }, 6476 * { 6477 * name: 'row' 6478 * val: {}, 6479 * methodExt: [ ... ], 6480 * propExt: [ 6481 * { 6482 * name: 'data' 6483 * val: function () {}, 6484 * methodExt: [ ... ], 6485 * propExt: [ ... ] 6486 * }, 6487 * ... 6488 * ] 6489 * } 6490 * ] 6491 * 6492 * @type {Array} 6493 * @ignore 6494 */ 6495 var __apiStruct = []; 6496 6497 6498 /** 6499 * `Array.prototype` reference. 6500 * 6501 * @type object 6502 * @ignore 6503 */ 6504 var __arrayProto = Array.prototype; 6505 6506 6507 /** 6508 * Abstraction for `context` parameter of the `Api` constructor to allow it to 6509 * take several different forms for ease of use. 6510 * 6511 * Each of the input parameter types will be converted to a DataTables settings 6512 * object where possible. 6513 * 6514 * @param {string|node|jQuery|object} mixed DataTable identifier. Can be one 6515 * of: 6516 * 6517 * * `string` - jQuery selector. Any DataTables' matching the given selector 6518 * with be found and used. 6519 * * `node` - `TABLE` node which has already been formed into a DataTable. 6520 * * `jQuery` - A jQuery object of `TABLE` nodes. 6521 * * `object` - DataTables settings object 6522 * * `DataTables.Api` - API instance 6523 * @return {array|null} Matching DataTables settings objects. `null` or 6524 * `undefined` is returned if no matching DataTable is found. 6525 * @ignore 6526 */ 6527 var _toSettings = function ( mixed ) 6528 { 6529 var idx, jq; 6530 var settings = DataTable.settings; 6531 var tables = _pluck(settings, 'nTable'); 6532 6533 if ( ! mixed ) { 6534 return []; 6535 } 6536 else if ( mixed.nTable && mixed.oFeatures ) { 6537 // DataTables settings object 6538 return [ mixed ]; 6539 } 6540 else if ( mixed.nodeName && mixed.nodeName.toLowerCase() === 'table' ) { 6541 // Table node 6542 idx = tables.indexOf(mixed); 6543 return idx !== -1 ? [ settings[idx] ] : null; 6544 } 6545 else if ( mixed && typeof mixed.settings === 'function' ) { 6546 return mixed.settings().toArray(); 6547 } 6548 else if ( typeof mixed === 'string' ) { 6549 // jQuery selector 6550 jq = $(mixed).get(); 6551 } 6552 else if ( mixed instanceof $ ) { 6553 // jQuery object (also DataTables instance) 6554 jq = mixed.get(); 6555 } 6556 6557 if ( jq ) { 6558 return settings.filter(function (v, idx) { 6559 return jq.includes(tables[idx]); 6560 }); 6561 } 6562 }; 6563 6564 6565 /** 6566 * DataTables API class - used to control and interface with one or more 6567 * DataTables enhanced tables. 6568 * 6569 * The API class is heavily based on jQuery, presenting a chainable interface 6570 * that you can use to interact with tables. Each instance of the API class has 6571 * a "context" - i.e. the tables that it will operate on. This could be a single 6572 * table, all tables on a page or a sub-set thereof. 6573 * 6574 * Additionally the API is designed to allow you to easily work with the data in 6575 * the tables, retrieving and manipulating it as required. This is done by 6576 * presenting the API class as an array like interface. The contents of the 6577 * array depend upon the actions requested by each method (for example 6578 * `rows().nodes()` will return an array of nodes, while `rows().data()` will
6579 * return an array of objects or arrays depending upon your table's 6580 * configuration). The API object has a number of array like methods (`push`, 6581 * `pop`, `reverse` etc) as well as additional helper methods (`each`, `pluck`, 6582 * `unique` etc) to assist your working with the data held in a table. 6583 * 6584 * Most methods (those which return an Api instance) are chainable, which means 6585 * the return from a method call also has all of the methods available that the 6586 * top level object had. For example, these two calls are equivalent: 6587 * 6588 * // Not chained 6589 * api.row.add( {...} ); 6590 * api.draw(); 6591 * 6592 * // Chained 6593 * api.row.add( {...} ).draw(); 6594 * 6595 * @class DataTable.Api 6596 * @param {array|object|string|jQuery} context DataTable identifier. This is 6597 * used to define which DataTables enhanced tables this API will operate on. 6598 * Can be one of: 6599 * 6600 * * `string` - jQuery selector. Any DataTables' matching the given selector 6601 * with be found and used. 6602 * * `node` - `TABLE` node which has already been formed into a DataTable. 6603 * * `jQuery` - A jQuery object of `TABLE` nodes. 6604 * * `object` - DataTables settings object 6605 * @param {array} [data] Data to initialise the Api instance with. 6606 * 6607 * @example 6608 * // Direct initialisation during DataTables construction 6609 * var api = $('#example').DataTable(); 6610 * 6611 * @example 6612 * // Initialisation using a DataTables jQuery object 6613 * var api = $('#example').dataTable().api(); 6614 * 6615 * @example 6616 * // Initialisation as a constructor 6617 * var api = new DataTable.Api( 'table.dataTable' ); 6618 */ 6619 _Api = function ( context, data ) 6620 { 6621 if ( ! (this instanceof _Api) ) { 6622 return new _Api( context, data ); 6623 } 6624 6625 var settings = []; 6626 var ctxSettings = function ( o ) { 6627 var a = _toSettings( o ); 6628 if ( a ) { 6629 settings.push.apply( settings, a ); 6630 } 6631 }; 6632 6633 if ( Array.isArray( context ) ) { 6634 for ( var i=0, ien=context.length ; i<ien ; i++ ) { 6635 ctxSettings( context[i] ); 6636 } 6637 } 6638 else { 6639 ctxSettings( context ); 6640 } 6641 6642 // Remove duplicates 6643 this.context = settings.length > 1 6644 ? _unique( settings ) 6645 : settings; 6646 6647 // Initial data 6648 if ( data ) { 6649 this.push.apply(this, data); 6650 } 6651 6652 // selector 6653 this.selector = { 6654 rows: null, 6655 cols: null, 6656 opts: null 6657 }; 6658 6659 _Api.extend( this, this, __apiStruct ); 6660 }; 6661 6662 DataTable.Api = _Api; 6663 6664 // Don't destroy the existing prototype, just extend it. Required for jQuery 2's 6665 // isPlainObject. 6666 $.extend( _Api.prototype, { 6667 any: function () 6668 { 6669 return this.count() !== 0; 6670 }, 6671 6672 context: [], // array of table settings objects 6673 6674 count: function () 6675 { 6676 return this.flatten().length; 6677 }, 6678 6679 each: function ( fn ) 6680 { 6681 for ( var i=0, ien=this.length ; i<ien; i++ ) { 6682 fn.call( this, this[i], i, this ); 6683 } 6684 6685 return this; 6686 }, 6687 6688 eq: function ( idx ) 6689 { 6690 var ctx = this.context; 6691 6692 return ctx.length > idx ? 6693 new _Api( ctx[idx], this[idx] ) : 6694 null; 6695 }, 6696 6697 filter: function ( fn ) 6698 { 6699 var a = __arrayProto.filter.call( this, fn, this ); 6700 6701 return new _Api( this.context, a ); 6702 }, 6703 6704 flatten: function () 6705 { 6706 var a = []; 6707 6708 return new _Api( this.context, a.concat.apply( a, this.toArray() ) ); 6709 }, 6710 6711 get: function ( idx ) 6712 { 6713 return this[ idx ]; 6714 }, 6715 6716 join: __arrayProto.join, 6717 6718 includes: function ( find ) { 6719 return this.indexOf( find ) === -1 ? false : true; 6720 }, 6721 6722 indexOf: __arrayProto.indexOf, 6723 6724 iterator: function ( flatten, type, fn, alwaysNew ) { 6725 var 6726 a = [], ret, 6727 i, ien, j, jen, 6728 context = this.context, 6729 rows, items, item, 6730 selector = this.selector; 6731 6732 // Argument shifting 6733 if ( typeof flatten === 'string' ) { 6734 alwaysNew = fn; 6735 fn = type; 6736 type = flatten; 6737 flatten = false; 6738 } 6739 6740 for ( i=0, ien=context.length ; i<ien ; i++ ) { 6741 var apiInst = new _Api( context[i] ); 6742 6743 if ( type === 'table' ) { 6744 ret = fn.call( apiInst, context[i], i ); 6745 6746 if ( ret !== undefined ) { 6747 a.push( ret ); 6748 } 6749 } 6750 else if ( type === 'columns' || type === 'rows' ) { 6751 // this has same length as context - one entry for each table 6752 ret = fn.call( apiInst, context[i], this[i], i ); 6753 6754 if ( ret !== undefined ) { 6755 a.push( ret ); 6756 } 6757 } 6758 else if ( type === 'every' || type === 'column' || type === 'column-rows' || type === 'row' || type === 'cell' ) { 6759 // columns and rows share the same structure. 6760 // 'this' is an array of column indexes for each context 6761 items = this[i]; 6762 6763 if ( type === 'column-rows' ) { 6764 rows = _selector_row_indexes( context[i], selector.opts ); 6765 } 6766 6767 for ( j=0, jen=items.length ; j<jen ; j++ ) { 6768 item = items[j];
6769 6770 if ( type === 'cell' ) { 6771 ret = fn.call( apiInst, context[i], item.row, item.column, i, j ); 6772 } 6773 else { 6774 ret = fn.call( apiInst, context[i], item, i, j, rows ); 6775 } 6776 6777 if ( ret !== undefined ) { 6778 a.push( ret ); 6779 } 6780 } 6781 } 6782 } 6783 6784 if ( a.length || alwaysNew ) { 6785 var api = new _Api( context, flatten ? a.concat.apply( [], a ) : a ); 6786 var apiSelector = api.selector; 6787 apiSelector.rows = selector.rows; 6788 apiSelector.cols = selector.cols; 6789 apiSelector.opts = selector.opts; 6790 return api; 6791 } 6792 return this; 6793 }, 6794 6795 lastIndexOf: __arrayProto.lastIndexOf, 6796 6797 length: 0, 6798 6799 map: function ( fn ) 6800 { 6801 var a = __arrayProto.map.call( this, fn, this ); 6802 6803 return new _Api( this.context, a ); 6804 }, 6805 6806 pluck: function ( prop ) 6807 { 6808 var fn = DataTable.util.get(prop); 6809 6810 return this.map( function ( el ) { 6811 return fn(el); 6812 } ); 6813 }, 6814 6815 pop: __arrayProto.pop, 6816 6817 push: __arrayProto.push, 6818 6819 reduce: __arrayProto.reduce, 6820 6821 reduceRight: __arrayProto.reduceRight, 6822 6823 reverse: __arrayProto.reverse, 6824 6825 // Object with rows, columns and opts 6826 selector: null, 6827 6828 shift: __arrayProto.shift, 6829 6830 slice: function () { 6831 return new _Api( this.context, this ); 6832 }, 6833 6834 sort: __arrayProto.sort, 6835 6836 splice: __arrayProto.splice, 6837 6838 toArray: function () 6839 { 6840 return __arrayProto.slice.call( this ); 6841 }, 6842 6843 to$: function () 6844 { 6845 return $( this ); 6846 }, 6847 6848 toJQuery: function () 6849 { 6850 return $( this ); 6851 }, 6852 6853 unique: function () 6854 { 6855 return new _Api( this.context, _unique(this.toArray()) ); 6856 }, 6857 6858 unshift: __arrayProto.unshift 6859 } ); 6860 6861 6862 function _api_scope( scope, fn, struc ) { 6863 return function () { 6864 var ret = fn.apply( scope || this, arguments ); 6865 6866 // Method extension 6867 _Api.extend( ret, ret, struc.methodExt ); 6868 return ret; 6869 }; 6870 } 6871 6872 function _api_find( src, name ) { 6873 for ( var i=0, ien=src.length ; i<ien ; i++ ) { 6874 if ( src[i].name === name ) { 6875 return src[i]; 6876 } 6877 } 6878 return null; 6879 } 6880 6881 window.__apiStruct = __apiStruct; 6882 6883 _Api.extend = function ( scope, obj, ext ) 6884 { 6885 // Only extend API instances and static properties of the API 6886 if ( ! ext.length || ! obj || ( ! (obj instanceof _Api) && ! obj.__dt_wrapper ) ) { 6887 return; 6888 } 6889 6890 var 6891 i, ien, 6892 struct; 6893 6894 for ( i=0, ien=ext.length ; i<ien ; i++ ) { 6895 struct = ext[i]; 6896 6897 if (struct.name === '__proto__') { 6898 continue; 6899 } 6900 6901 // Value 6902 obj[ struct.name ] = struct.type === 'function' ? 6903 _api_scope( scope, struct.val, struct ) : 6904 struct.type === 'object' ? 6905 {} : 6906 struct.val; 6907 6908 obj[ struct.name ].__dt_wrapper = true; 6909 6910 // Property extension 6911 _Api.extend( scope, obj[ struct.name ], struct.propExt ); 6912 } 6913 }; 6914 6915 // [ 6916 // { 6917 // name: 'data' -- string - Property name 6918 // val: function () {}, -- function - Api method (or undefined if just an object 6919 // methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result 6920 // propExt: [ ... ] -- array - Array of Api object definitions to extend the property 6921 // }, 6922 // { 6923 // name: 'row' 6924 // val: {}, 6925 // methodExt: [ ... ], 6926 // propExt: [ 6927 // { 6928 // name: 'data' 6929 // val: function () {}, 6930 // methodExt: [ ... ], 6931 // propExt: [ ... ] 6932 // }, 6933 // ... 6934 // ] 6935 // } 6936 // ] 6937 6938 6939 _Api.register = _api_register = function ( name, val ) 6940 { 6941 if ( Array.isArray( name ) ) { 6942 for ( var j=0, jen=name.length ; j<jen ; j++ ) { 6943 _Api.register( name[j], val ); 6944 } 6945 return; 6946 } 6947 6948 var 6949 i, ien, 6950 heir = name.split('.'), 6951 struct = __apiStruct, 6952 key, method; 6953 6954 for ( i=0, ien=heir.length ; i<ien ; i++ ) { 6955 method = heir[i].indexOf('()') !== -1; 6956 key = method ?
6957 heir[i].replace('()', '') : 6958 heir[i]; 6959 6960 var src = _api_find( struct, key ); 6961 if ( ! src ) { 6962 src = { 6963 name: key, 6964 val: {}, 6965 methodExt: [], 6966 propExt: [], 6967 type: 'object' 6968 }; 6969 struct.push( src ); 6970 } 6971 6972 if ( i === ien-1 ) { 6973 src.val = val; 6974 src.type = typeof val === 'function' ? 6975 'function' : 6976 $.isPlainObject( val ) ? 6977 'object' : 6978 'other'; 6979 } 6980 else { 6981 struct = method ? 6982 src.methodExt : 6983 src.propExt; 6984 } 6985 } 6986 }; 6987 6988 _Api.registerPlural = _api_registerPlural = function ( pluralName, singularName, val ) { 6989 _Api.register( pluralName, val ); 6990 6991 _Api.register( singularName, function () { 6992 var ret = val.apply( this, arguments ); 6993 6994 if ( ret === this ) { 6995 // Returned item is the API instance that was passed in, return it 6996 return this; 6997 } 6998 else if ( ret instanceof _Api ) { 6999 // New API instance returned, want the value from the first item 7000 // in the returned array for the singular result. 7001 return ret.length ? 7002 Array.isArray( ret[0] ) ? 7003 new _Api( ret.context, ret[0] ) : // Array results are 'enhanced' 7004 ret[0] : 7005 undefined; 7006 } 7007 7008 // Non-API return - just fire it back 7009 return ret; 7010 } ); 7011 }; 7012 7013 7014 /** 7015 * Selector for HTML tables. Apply the given selector to the give array of 7016 * DataTables settings objects. 7017 * 7018 * @param {string|integer} [selector] jQuery selector string or integer 7019 * @param {array} Array of DataTables settings objects to be filtered 7020 * @return {array} 7021 * @ignore 7022 */ 7023 var __table_selector = function ( selector, a ) 7024 { 7025 if ( Array.isArray(selector) ) { 7026 var result = []; 7027 7028 selector.forEach(function (sel) { 7029 var inner = __table_selector(sel, a); 7030 7031 result.push.apply(result, inner); 7032 }); 7033 7034 return result.filter( function (item) { 7035 return item; 7036 }); 7037 } 7038 7039 // Integer is used to pick out a table by index 7040 if ( typeof selector === 'number' ) { 7041 return [ a[ selector ] ]; 7042 } 7043 7044 // Perform a jQuery selector on the table nodes 7045 var nodes = a.map( function (el) { 7046 return el.nTable; 7047 } ); 7048 7049 return $(nodes) 7050 .filter( selector ) 7051 .map( function () { 7052 // Need to translate back from the table node to the settings 7053 var idx = nodes.indexOf(this); 7054 return a[ idx ]; 7055 } ) 7056 .toArray(); 7057 }; 7058 7059 7060 7061 /** 7062 * Context selector for the API's context (i.e. the tables the API instance 7063 * refers to. 7064 * 7065 * @name DataTable.Api#tables 7066 * @param {string|integer} [selector] Selector to pick which tables the iterator 7067 * should operate on. If not given, all tables in the current context are 7068 * used. This can be given as a jQuery selector (for example `':gt(0)'`) to 7069 * select multiple tables or as an integer to select a single table. 7070 * @returns {DataTable.Api} Returns a new API instance if a selector is given. 7071 */ 7072 _api_register( 'tables()', function ( selector ) { 7073 // A new instance is created if there was a selector specified 7074 return selector !== undefined && selector !== null ? 7075 new _Api( __table_selector( selector, this.context ) ) : 7076 this; 7077 } ); 7078 7079 7080 _api_register( 'table()', function ( selector ) { 7081 var tables = this.tables( selector ); 7082 var ctx = tables.context; 7083 7084 // Truncate to the first matched table 7085 return ctx.length ? 7086 new _Api( ctx[0] ) : 7087 tables; 7088 } ); 7089 7090 // Common methods, combined to reduce size 7091 [ 7092 ['nodes', 'node', 'nTable'], 7093 ['body', 'body', 'nTBody'], 7094 ['header', 'header', 'nTHead'], 7095 ['footer', 'footer', 'nTFoot'], 7096 ].forEach(function (item) { 7097 _api_registerPlural( 7098 'tables().' + item[0] + '()', 7099 'table().' + item[1] + '()' , 7100 function () { 7101 return this.iterator( 'table', function ( ctx ) { 7102 return ctx[item[2]]; 7103 }, 1 ); 7104 } 7105 ); 7106 }); 7107 7108 // Structure methods 7109 [ 7110 ['header', 'aoHeader'], 7111 ['footer', 'aoFooter'], 7112 ].forEach(function (item) { 7113 _api_register( 'table().' + item[0] + '.structure()' , function (selector) { 7114 var indexes = this.columns(selector).indexes().flatten(); 7115 var ctx = this.context[0]; 7116 7117 return _fnHeaderLayout(ctx, ctx[item[1]], indexes); 7118 } ); 7119 }) 7120 7121 7122 _api_registerPlural( 'tables().containers()', 'table().container()' , function () { 7123 return this.iterator( 'table', function ( ctx ) { 7124 return ctx.nTableWrapper; 7125 }, 1 ); 7126 } ); 7127 7128 _api_register( 'tables().every()', function ( fn ) { 7129 var that = this; 7130 7131 return this.iterator('table', function (s, i) { 7132 fn.call(that.table(i), i); 7133 }); 7134 }); 7135 7136 _api_register( 'caption()', function ( value, side ) { 7137 var context = this.context; 7138 7139 // Getter - return existing node's content 7140 if ( value === undefined ) { 7141 var caption = context[0].captionNode; 7142 7143 return caption && context.length ? 7144 caption.innerHTML : 7145 null; 7146 } 7147 7148 return this.iterator( 'table', function ( ctx ) { 7149 var table = $(ctx.nTable); 7150 var caption = $(ctx.captionNode); 7151 var container = $(ctx.nTableWrapper); 7152 7153 // Create the node if it doesn't exist yet 7154 if ( ! caption.length ) { 7155 caption = $('<caption/>').html( value ); 7156 ctx.captionNode = caption[0]; 7157 7158 // If side isn't set, we need to insert into the document to let the 7159 // CSS decide so we can read it back, otherwise there is no way to 7160 // know if the CSS would put it top or bottom for scrolling
7161 if (! side) { 7162 table.prepend(caption); 7163 7164 side = caption.css('caption-side'); 7165 } 7166 } 7167 7168 caption.html( value ); 7169 7170 if ( side ) { 7171 caption.css( 'caption-side', side ); 7172 caption[0]._captionSide = side; 7173 } 7174 7175 if (container.find('div.dataTables_scroll').length) { 7176 var selector = (side === 'top' ? 'Head' : 'Foot'); 7177 7178 container.find('div.dataTables_scroll'+ selector +' table').prepend(caption); 7179 } 7180 else { 7181 table.prepend(caption); 7182 } 7183 }, 1 ); 7184 } ); 7185 7186 _api_register( 'caption.node()', function () { 7187 var ctx = this.context; 7188 7189 return ctx.length ? ctx[0].captionNode : null; 7190 } ); 7191 7192 7193 /** 7194 * Redraw the tables in the current context. 7195 */ 7196 _api_register( 'draw()', function ( paging ) { 7197 return this.iterator( 'table', function ( settings ) { 7198 if ( paging === 'page' ) { 7199 _fnDraw( settings ); 7200 } 7201 else { 7202 if ( typeof paging === 'string' ) { 7203 paging = paging === 'full-hold' ? 7204 false : 7205 true; 7206 } 7207 7208 _fnReDraw( settings, paging===false ); 7209 } 7210 } ); 7211 } ); 7212 7213 7214 7215 /** 7216 * Get the current page index. 7217 * 7218 * @return {integer} Current page index (zero based) 7219 *//** 7220 * Set the current page. 7221 * 7222 * Note that if you attempt to show a page which does not exist, DataTables will 7223 * not throw an error, but rather reset the paging. 7224 * 7225 * @param {integer|string} action The paging action to take. This can be one of: 7226 * * `integer` - The page index to jump to 7227 * * `string` - An action to take: 7228 * * `first` - Jump to first page. 7229 * * `next` - Jump to the next page 7230 * * `previous` - Jump to previous page 7231 * * `last` - Jump to the last page. 7232 * @returns {DataTables.Api} this 7233 */ 7234 _api_register( 'page()', function ( action ) { 7235 if ( action === undefined ) { 7236 return this.page.info().page; // not an expensive call 7237 } 7238 7239 // else, have an action to take on all tables 7240 return this.iterator( 'table', function ( settings ) { 7241 _fnPageChange( settings, action ); 7242 } ); 7243 } ); 7244 7245 7246 /** 7247 * Paging information for the first table in the current context. 7248 * 7249 * If you require paging information for another table, use the `table()` method 7250 * with a suitable selector. 7251 * 7252 * @return {object} Object with the following properties set: 7253 * * `page` - Current page index (zero based - i.e. the first page is `0`) 7254 * * `pages` - Total number of pages 7255 * * `start` - Display index for the first record shown on the current page 7256 * * `end` - Display index for the last record shown on the current page 7257 * * `length` - Display length (number of records). Note that generally `start 7258 * + length = end`, but this is not always true, for example if there are 7259 * only 2 records to show on the final page, with a length of 10. 7260 * * `recordsTotal` - Full data set length 7261 * * `recordsDisplay` - Data set length once the current filtering criterion 7262 * are applied. 7263 */ 7264 _api_register( 'page.info()', function () { 7265 if ( this.context.length === 0 ) { 7266 return undefined; 7267 } 7268 7269 var 7270 settings = this.context[0], 7271 start = settings._iDisplayStart, 7272 len = settings.oFeatures.bPaginate ? settings._iDisplayLength : -1, 7273 visRecords = settings.fnRecordsDisplay(), 7274 all = len === -1; 7275 7276 return { 7277 "page": all ? 0 : Math.floor( start / len ), 7278 "pages": all ? 1 : Math.ceil( visRecords / len ), 7279 "start": start, 7280 "end": settings.fnDisplayEnd(), 7281 "length": len, 7282 "recordsTotal": settings.fnRecordsTotal(), 7283 "recordsDisplay": visRecords, 7284 "serverSide": _fnDataSource( settings ) === 'ssp' 7285 }; 7286 } ); 7287 7288 7289 /** 7290 * Get the current page length. 7291 * 7292 * @return {integer} Current page length. Note `-1` indicates that all records 7293 * are to be shown. 7294 *//** 7295 * Set the current page length. 7296 * 7297 * @param {integer} Page length to set. Use `-1` to show all records. 7298 * @returns {DataTables.Api} this 7299 */ 7300 _api_register( 'page.len()', function ( len ) { 7301 // Note that we can't call this function 'length()' because `length` 7302 // is a Javascript property of functions which defines how many arguments 7303 // the function expects. 7304 if ( len === undefined ) { 7305 return this.context.length !== 0 ? 7306 this.context[0]._iDisplayLength : 7307 undefined; 7308 } 7309 7310 // else, set the page length 7311 return this.iterator( 'table', function ( settings ) { 7312 _fnLengthChange( settings, len ); 7313 } ); 7314 } ); 7315 7316 7317 7318 var __reload = function ( settings, holdPosition, callback ) { 7319 // Use the draw event to trigger a callback 7320 if ( callback ) { 7321 var api = new _Api( settings ); 7322 7323 api.one( 'draw', function () { 7324 callback( api.ajax.json() ); 7325 } ); 7326 } 7327 7328 if ( _fnDataSource( settings ) == 'ssp' ) { 7329 _fnReDraw( settings, holdPosition ); 7330 } 7331 else { 7332 _fnProcessingDisplay( settings, true ); 7333 7334 // Cancel an existing request 7335 var xhr = settings.jqXHR; 7336 if ( xhr && xhr.readyState !== 4 ) { 7337 xhr.abort(); 7338 } 7339 7340 // Trigger xhr 7341 _fnBuildAjax( settings, {}, function( json ) { 7342 _fnClearTable( settings ); 7343 7344 var data = _fnAjaxDataSrc( settings, json ); 7345 for ( var i=0, ien=data.length ; i<ien ; i++ ) { 7346 _fnAddData( settings, data[i] ); 7347 } 7348 7349 _fnReDraw( settings, holdPosition ); 7350 _fnInitComplete( settings ); 7351 _fnProcessingDisplay( settings, false ); 7352 } ); 7353 } 7354 }; 7355 7356 7357 /** 7358 * Get the JSON response from the last Ajax request that DataTables made to the 7359 * server. Note that this returns the JSON from the first table in the current 7360 * context. 7361 * 7362 * @return {object} JSON received from the server.
7363 */ 7364 _api_register( 'ajax.json()', function () { 7365 var ctx = this.context; 7366 7367 if ( ctx.length > 0 ) { 7368 return ctx[0].json; 7369 } 7370 7371 // else return undefined; 7372 } ); 7373 7374 7375 /** 7376 * Get the data submitted in the last Ajax request 7377 */ 7378 _api_register( 'ajax.params()', function () { 7379 var ctx = this.context; 7380 7381 if ( ctx.length > 0 ) { 7382 return ctx[0].oAjaxData; 7383 } 7384 7385 // else return undefined; 7386 } ); 7387 7388 7389 /** 7390 * Reload tables from the Ajax data source. Note that this function will 7391 * automatically re-draw the table when the remote data has been loaded. 7392 * 7393 * @param {boolean} [reset=true] Reset (default) or hold the current paging 7394 * position. A full re-sort and re-filter is performed when this method is 7395 * called, which is why the pagination reset is the default action. 7396 * @returns {DataTables.Api} this 7397 */ 7398 _api_register( 'ajax.reload()', function ( callback, resetPaging ) { 7399 return this.iterator( 'table', function (settings) { 7400 __reload( settings, resetPaging===false, callback ); 7401 } ); 7402 } ); 7403 7404 7405 /** 7406 * Get the current Ajax URL. Note that this returns the URL from the first 7407 * table in the current context. 7408 * 7409 * @return {string} Current Ajax source URL 7410 *//** 7411 * Set the Ajax URL. Note that this will set the URL for all tables in the 7412 * current context. 7413 * 7414 * @param {string} url URL to set. 7415 * @returns {DataTables.Api} this 7416 */ 7417 _api_register( 'ajax.url()', function ( url ) { 7418 var ctx = this.context; 7419 7420 if ( url === undefined ) { 7421 // get 7422 if ( ctx.length === 0 ) { 7423 return undefined; 7424 } 7425 ctx = ctx[0]; 7426
7427 return $.isPlainObject( ctx.ajax ) ? 7428 ctx.ajax.url : 7429 ctx.ajax; 7430 } 7431 7432 // set 7433 return this.iterator( 'table', function ( settings ) { 7434 if ( $.isPlainObject( settings.ajax ) ) { 7435 settings.ajax.url = url; 7436 } 7437 else { 7438 settings.ajax = url; 7439 } 7440 } ); 7441 } ); 7442 7443 7444 /** 7445 * Load data from the newly set Ajax URL. Note that this method is only 7446 * available when `ajax.url()` is used to set a URL. Additionally, this method 7447 * has the same effect as calling `ajax.reload()` but is provided for 7448 * convenience when setting a new URL. Like `ajax.reload()` it will 7449 * automatically redraw the table once the remote data has been loaded. 7450 * 7451 * @returns {DataTables.Api} this 7452 */ 7453 _api_register( 'ajax.url().load()', function ( callback, resetPaging ) { 7454 // Same as a reload, but makes sense to present it for easy access after a 7455 // url change 7456 return this.iterator( 'table', function ( ctx ) { 7457 __reload( ctx, resetPaging===false, callback ); 7458 } ); 7459 } ); 7460 7461 7462 7463 7464 var _selector_run = function ( type, selector, selectFn, settings, opts ) 7465 { 7466 var 7467 out = [], res, 7468 a, i, ien, j, jen, 7469 selectorType = typeof selector; 7470 7471 // Can't just check for isArray here, as an API or jQuery instance might be 7472 // given with their array like look 7473 if ( ! selector || selectorType === 'string' || selectorType === 'function' || selector.length === undefined ) { 7474 selector = [ selector ]; 7475 } 7476 7477 for ( i=0, ien=selector.length ; i<ien ; i++ ) { 7478 // Only split on simple strings - complex expressions will be jQuery selectors 7479 a = selector[i] && selector[i].split && ! selector[i].match(/[[(:]/) ? 7480 selector[i].split(',') : 7481 [ selector[i] ]; 7482 7483 for ( j=0, jen=a.length ; j<jen ; j++ ) { 7484 res = selectFn( typeof a[j] === 'string' ? (a[j]).trim() : a[j] ); 7485 7486 // Remove empty items 7487 res = res.filter( function (item) { 7488 return item !== null && item !== undefined; 7489 }); 7490 7491 if ( res && res.length ) { 7492 out = out.concat( res ); 7493 } 7494 } 7495 } 7496 7497 // selector extensions 7498 var ext = _ext.selector[ type ]; 7499 if ( ext.length ) { 7500 for ( i=0, ien=ext.length ; i<ien ; i++ ) { 7501 out = ext[i]( settings, opts, out ); 7502 } 7503 } 7504 7505 return _unique( out ); 7506 }; 7507 7508 7509 var _selector_opts = function ( opts ) 7510 { 7511 if ( ! opts ) { 7512 opts = {}; 7513 } 7514 7515 // Backwards compatibility for 1.9- which used the terminology filter rather 7516 // than search 7517 if ( opts.filter && opts.search === undefined ) { 7518 opts.search = opts.filter; 7519 } 7520 7521 return $.extend( { 7522 search: 'none', 7523 order: 'current', 7524 page: 'all' 7525 }, opts ); 7526 }; 7527 7528 7529 // Reduce the API instance to the first item found 7530 var _selector_first = function ( old ) 7531 { 7532 let inst = new _Api(old.context[0]); 7533 7534 // Use a push rather than passing to the constructor, since it will 7535 // merge arrays down automatically, which isn't what is wanted here 7536 if (old.length) { 7537 inst.push( old[0] ); 7538 } 7539 7540 inst.selector = old.selector; 7541 7542 // Limit to a single row / column / cell 7543 if (inst.length && inst[0].length > 1) { 7544 inst[0].splice(1); 7545 } 7546 7547 return inst; 7548 }; 7549 7550 7551 var _selector_row_indexes = function ( settings, opts ) 7552 { 7553 var 7554 i, ien, tmp, a=[], 7555 displayFiltered = settings.aiDisplay, 7556 displayMaster = settings.aiDisplayMaster; 7557 7558 var 7559 search = opts.search, // none, applied, removed 7560 order = opts.order, // applied, current, index (original - compatibility with 1.9) 7561 page = opts.page; // all, current 7562 7563 if ( _fnDataSource( settings ) == 'ssp' ) { 7564 // In server-side processing mode, most options are irrelevant since 7565 // rows not shown don't exist and the index order is the applied order 7566 // Removed is a special case - for consistency just return an empty 7567 // array 7568 return search === 'removed' ? 7569 [] : 7570 _range( 0, displayMaster.length ); 7571 } 7572 7573 if ( page == 'current' ) { 7574 // Current page implies that order=current and filter=applied, since it is 7575 // fairly senseless otherwise, regardless of what order and search actually 7576 // are 7577 for ( i=settings._iDisplayStart, ien=settings.fnDisplayEnd() ; i<ien ; i++ ) { 7578 a.push( displayFiltered[i] ); 7579 } 7580 } 7581 else if ( order == 'current' || order == 'applied' ) { 7582 if ( search == 'none') {
7583 a = displayMaster.slice(); 7584 } 7585 else if ( search == 'applied' ) { 7586 a = displayFiltered.slice(); 7587 } 7588 else if ( search == 'removed' ) { 7589 // O(n+m) solution by creating a hash map 7590 var displayFilteredMap = {}; 7591 7592 for ( i=0, ien=displayFiltered.length ; i<ien ; i++ ) { 7593 displayFilteredMap[displayFiltered[i]] = null; 7594 } 7595 7596 displayMaster.forEach(function (item) { 7597 if (! Object.prototype.hasOwnProperty.call(displayFilteredMap, item)) { 7598 a.push(item); 7599 } 7600 }); 7601 } 7602 } 7603 else if ( order == 'index' || order == 'original' ) { 7604 for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) { 7605 if (! settings.aoData[i]) { 7606 continue; 7607 } 7608 7609 if ( search == 'none' ) { 7610 a.push( i ); 7611 } 7612 else { // applied | removed 7613 tmp = displayFiltered.indexOf(i); 7614 7615 if ((tmp === -1 && search == 'removed') || 7616 (tmp >= 0 && search == 'applied') ) 7617 { 7618 a.push( i ); 7619 } 7620 } 7621 } 7622 } 7623 else if ( typeof order === 'number' ) { 7624 // Order the rows by the given column 7625 var ordered = _fnSort(settings, order, 'asc'); 7626 7627 if (search === 'none') { 7628 a = ordered; 7629 } 7630 else { // applied | removed 7631 for (i=0; i<ordered.length; i++) { 7632 tmp = displayFiltered.indexOf(ordered[i]); 7633 7634 if ((tmp === -1 && search == 'removed') || 7635 (tmp >= 0 && search == 'applied') ) 7636 { 7637 a.push( ordered[i] ); 7638 } 7639 } 7640 } 7641 } 7642 7643 return a; 7644 }; 7645 7646 7647 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 7648 * Rows 7649 * 7650 * {} - no selector - use all available rows 7651 * {integer} - row aoData index 7652 * {node} - TR node 7653 * {string} - jQuery selector to apply to the TR elements 7654 * {array} - jQuery array of nodes, or simply an array of TR nodes 7655 * 7656 */ 7657 var __row_selector = function ( settings, selector, opts ) 7658 { 7659 var rows; 7660 var run = function ( sel ) { 7661 var selInt = _intVal( sel ); 7662 var aoData = settings.aoData; 7663 7664 // Short cut - selector is a number and no options provided (default is 7665 // all records, so no need to check if the index is in there, since it 7666 // must be - dev error if the index doesn't exist). 7667 if ( selInt !== null && ! opts ) { 7668 return [ selInt ]; 7669 } 7670 7671 if ( ! rows ) { 7672 rows = _selector_row_indexes( settings, opts ); 7673 } 7674 7675 if ( selInt !== null && rows.indexOf(selInt) !== -1 ) { 7676 // Selector - integer 7677 return [ selInt ]; 7678 } 7679 else if ( sel === null || sel === undefined || sel === '' ) { 7680 // Selector - none 7681 return rows; 7682 } 7683 7684 // Selector - function 7685 if ( typeof sel === 'function' ) { 7686 return rows.map( function (idx) { 7687 var row = aoData[ idx ]; 7688 return sel( idx, row._aData, row.nTr ) ? idx : null; 7689 } ); 7690 } 7691 7692 // Selector - node 7693 if ( sel.nodeName ) { 7694 var rowIdx = sel._DT_RowIndex; // Property added by DT for fast lookup 7695 var cellIdx = sel._DT_CellIndex; 7696 7697 if ( rowIdx !== undefined ) { 7698 // Make sure that the row is actually still present in the table 7699 return aoData[ rowIdx ] && aoData[ rowIdx ].nTr === sel ? 7700 [ rowIdx ] : 7701 []; 7702 } 7703 else if ( cellIdx ) { 7704 return aoData[ cellIdx.row ] && aoData[ cellIdx.row ].nTr === sel.parentNode ? 7705 [ cellIdx.row ] : 7706 []; 7707 } 7708 else { 7709 var host = $(sel).closest('*[data-dt-row]'); 7710 return host.length ? 7711 [ host.data('dt-row') ] : 7712 []; 7713 } 7714 } 7715 7716 // ID selector. Want to always be able to select rows by id, regardless 7717 // of if the tr element has been created or not, so can't rely upon 7718 // jQuery here - hence a custom implementation. This does not match 7719 // Sizzle's fast selector or HTML4 - in HTML5 the ID can be anything, 7720 // but to select it using a CSS selector engine (like Sizzle or 7721 // querySelect) it would need to need to be escaped for some characters. 7722 // DataTables simplifies this for row selectors since you can select 7723 // only a row. A # indicates an id any anything that follows is the id - 7724 // unescaped. 7725 if ( typeof sel === 'string' && sel.charAt(0) === '#' ) { 7726 // get row index from id 7727 var rowObj = settings.aIds[ sel.replace( /^#/, '' ) ]; 7728 if ( rowObj !== undefined ) { 7729 return [ rowObj.idx ]; 7730 } 7731 7732 // need to fall through to jQuery in case there is DOM id that 7733 // matches 7734 } 7735
7736 // Get nodes in the order from the `rows` array with null values removed 7737 var nodes = _removeEmpty( 7738 _pluck_order( settings.aoData, rows, 'nTr' ) 7739 ); 7740 7741 // Selector - jQuery selector string, array of nodes or jQuery object/ 7742 // As jQuery's .filter() allows jQuery objects to be passed in filter, 7743 // it also allows arrays, so this will cope with all three options 7744 return $(nodes) 7745 .filter( sel ) 7746 .map( function () { 7747 return this._DT_RowIndex; 7748 } ) 7749 .toArray(); 7750 }; 7751 7752 var matched = _selector_run( 'row', selector, run, settings, opts ); 7753 7754 if (opts.order === 'current' || opts.order === 'applied') { 7755 _fnSortDisplay(settings, matched); 7756 } 7757 7758 return matched; 7759 }; 7760 7761 7762 _api_register( 'rows()', function ( selector, opts ) { 7763 // argument shifting 7764 if ( selector === undefined ) { 7765 selector = ''; 7766 } 7767 else if ( $.isPlainObject( selector ) ) { 7768 opts = selector; 7769 selector = ''; 7770 } 7771 7772 opts = _selector_opts( opts ); 7773 7774 var inst = this.iterator( 'table', function ( settings ) { 7775 return __row_selector( settings, selector, opts ); 7776 }, 1 ); 7777 7778 // Want argument shifting here and in __row_selector? 7779 inst.selector.rows = selector; 7780 inst.selector.opts = opts; 7781 7782 return inst; 7783 } ); 7784 7785 _api_register( 'rows().nodes()', function () { 7786 return this.iterator( 'row', function ( settings, row ) { 7787 return settings.aoData[ row ].nTr || undefined; 7788 }, 1 ); 7789 } ); 7790 7791 _api_register( 'rows().data()', function () { 7792 return this.iterator( true, 'rows', function ( settings, rows ) { 7793 return _pluck_order( settings.aoData, rows, '_aData' ); 7794 }, 1 ); 7795 } ); 7796 7797 _api_registerPlural( 'rows().cache()', 'row().cache()', function ( type ) { 7798 return this.iterator( 'row', function ( settings, row ) { 7799 var r = settings.aoData[ row ]; 7800 return type === 'search' ? r._aFilterData : r._aSortData; 7801 }, 1 ); 7802 } ); 7803 7804 _api_registerPlural( 'rows().invalidate()', 'row().invalidate()', function ( src ) { 7805 return this.iterator( 'row', function ( settings, row ) { 7806 _fnInvalidate( settings, row, src ); 7807 } ); 7808 } ); 7809 7810 _api_registerPlural( 'rows().indexes()', 'row().index()', function () { 7811 return this.iterator( 'row', function ( settings, row ) { 7812 return row; 7813 }, 1 ); 7814 } ); 7815 7816 _api_registerPlural( 'rows().ids()', 'row().id()', function ( hash ) { 7817 var a = []; 7818 var context = this.context; 7819 7820 // `iterator` will drop undefined values, but in this case we want them 7821 for ( var i=0, ien=context.length ; i<ien ; i++ ) { 7822 for ( var j=0, jen=this[i].length ; j<jen ; j++ ) { 7823 var id = context[i].rowIdFn( context[i].aoData[ this[i][j] ]._aData ); 7824 a.push( (hash === true ? '#' : '' )+ id ); 7825 } 7826 } 7827 7828 return new _Api( context, a ); 7829 } ); 7830 7831 _api_registerPlural( 'rows().remove()', 'row().remove()', function () { 7832 this.iterator( 'row', function ( settings, row ) { 7833 var data = settings.aoData; 7834 var rowData = data[ row ]; 7835 7836 // Delete from the display arrays 7837 var idx = settings.aiDisplayMaster.indexOf(row); 7838 if (idx !== -1) { 7839 settings.aiDisplayMaster.splice(idx, 1); 7840 } 7841 7842 // For server-side processing tables - subtract the deleted row from the count 7843 if ( settings._iRecordsDisplay > 0 ) { 7844 settings._iRecordsDisplay--; 7845 } 7846 7847 // Check for an 'overflow' they case for displaying the table 7848 _fnLengthOverflow( settings ); 7849 7850 // Remove the row's ID reference if there is one 7851 var id = settings.rowIdFn( rowData._aData ); 7852 if ( id !== undefined ) { 7853 delete settings.aIds[ id ]; 7854 } 7855 7856 data[row] = null; 7857 } ); 7858 7859 return this; 7860 } ); 7861 7862 7863 _api_register( 'rows.add()', function ( rows ) { 7864 var newRows = this.iterator( 'table', function ( settings ) { 7865 var row, i, ien; 7866 var out = []; 7867 7868 for ( i=0, ien=rows.length ; i<ien ; i++ ) { 7869 row = rows[i]; 7870 7871 if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) { 7872 out.push( _fnAddTr( settings, row )[0] ); 7873 } 7874 else { 7875 out.push( _fnAddData( settings, row ) ); 7876 } 7877 } 7878 7879 return out; 7880 }, 1 ); 7881 7882 // Return an Api.rows() extended instance, so rows().nodes() etc can be used 7883 var modRows = this.rows( -1 ); 7884 modRows.pop(); 7885 modRows.push.apply(modRows, newRows); 7886 7887 return modRows; 7888 } ); 7889 7890 7891 7892 7893 7894 /** 7895 * 7896 */ 7897 _api_register( 'row()', function ( selector, opts ) { 7898 return _selector_first( this.rows( selector, opts ) ); 7899 } ); 7900 7901 7902 _api_register( 'row().data()', function ( data ) { 7903 var ctx = this.context; 7904 7905 if ( data === undefined ) { 7906 // Get 7907 return ctx.length && this.length && this[0].length ? 7908 ctx[0].aoData[ this[0] ]._aData : 7909 undefined; 7910 } 7911 7912 // Set 7913 var row = ctx[0].aoData[ this[0] ]; 7914 row._aData = data; 7915 7916 // If the DOM has an id, and the data source is an array 7917 if ( Array.isArray( data ) && row.nTr && row.nTr.id ) { 7918 _fnSetObjectDataFn( ctx[0].rowId )( data, row.nTr.id ); 7919 } 7920 7921 // Automatically invalidate 7922 _fnInvalidate( ctx[0], this[0], 'data' ); 7923 7924 return this; 7925 } ); 7926 7927 7928 _api_register( 'row().node()', function () { 7929 var ctx = this.context; 7930 7931 if (ctx.length && this.length && this[0].length) { 7932 var row = ctx[0].aoData[ this[0] ]; 7933 7934 if (row && row.nTr) { 7935 return row.nTr; 7936 } 7937 } 7938 7939 return null; 7940 } ); 7941 7942 7943 _api_register( 'row.add()', function ( row ) { 7944 // Allow a jQuery object to be passed in - only a single row is added from 7945 // it though - the first element in the set 7946 if ( row instanceof $ && row.length ) { 7947 row = row[0]; 7948 } 7949 7950 var rows = this.iterator( 'table', function ( settings ) { 7951 if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) { 7952 return _fnAddTr( settings, row )[0]; 7953 } 7954 return _fnAddData( settings, row ); 7955 } ); 7956 7957 // Return an Api.rows() extended instance, with the newly added row selected 7958 return this.row( rows[0] ); 7959 } ); 7960 7961 7962 $(document).on('plugin-init.dt', function (e, context) { 7963 var api = new _Api( context ); 7964 7965 api.on( 'stateSaveParams.DT', function ( e, settings, d ) { 7966 // This could be more compact with the API, but it is a lot faster as a simple 7967 // internal loop 7968 var idFn = settings.rowIdFn; 7969 var rows = settings.aiDisplayMaster; 7970 var ids = []; 7971 7972 for (var i=0 ; i<rows.length ; i++) { 7973 var rowIdx = rows[i]; 7974 var data = settings.aoData[rowIdx]; 7975 7976 if (data._detailsShow) { 7977 ids.push( '#' + idFn(data._aData) ); 7978 } 7979 } 7980 7981 d.childRows = ids; 7982 }); 7983 7984 // For future state loads (e.g. with StateRestore) 7985 api.on( 'stateLoaded.DT', function (e, settings, state) { 7986 __details_state_load( api, state ); 7987 }); 7988 7989 // And the initial load state 7990 __details_state_load( api, api.state.loaded() ); 7991 }); 7992 7993 var __details_state_load = function (api, state) 7994 { 7995 if ( state && state.childRows ) { 7996 api 7997 .rows( state.childRows.map(function (id) { 7998 // Escape any `:` characters from the row id. Accounts for 7999 // already escaped characters. 8000 return id.replace(/([^:\\]*(?:\\.[^:\\]*)*):/g, "$1\\:"); 8001 }) ) 8002 .every( function () { 8003 _fnCallbackFire( api.settings()[0], null, 'requestChild', [ this ] ) 8004 }); 8005 } 8006 } 8007 8008 var __details_add = function ( ctx, row, data, klass ) 8009 { 8010 // Convert to array of TR elements 8011 var rows = []; 8012 var addRow = function ( r, k ) { 8013 // Recursion to allow for arrays of jQuery objects 8014 if ( Array.isArray( r ) || r instanceof $ ) { 8015 for ( var i=0, ien=r.length ; i<ien ; i++ ) { 8016 addRow( r[i], k ); 8017 } 8018 return; 8019 } 8020 8021 // If we get a TR element, then just add it directly - up to the dev 8022 // to add the correct number of columns etc 8023 if ( r.nodeName && r.nodeName.toLowerCase() === 'tr' ) { 8024 r.setAttribute( 'data-dt-row', row.idx ); 8025 rows.push( r ); 8026 } 8027 else { 8028 // Otherwise create a row with a wrapper 8029 var created = $('<tr><td></td></tr>') 8030 .attr( 'data-dt-row', row.idx ) 8031 .addClass( k ); 8032 8033 $('td', created) 8034 .addClass( k ) 8035 .html( r )[0].colSpan = _fnVisbleColumns( ctx ); 8036 8037 rows.push( created[0] ); 8038 } 8039 }; 8040 8041 addRow( data, klass ); 8042 8043 if ( row._details ) { 8044 row._details.detach(); 8045 } 8046 8047 row._details = $(rows); 8048 8049 // If the children were already shown, that state should be retained 8050 if ( row._detailsShow ) { 8051 row._details.insertAfter( row.nTr ); 8052 } 8053 }; 8054 8055 8056 // Make state saving of child row details async to allow them to be batch processed 8057 var __details_state = DataTable.util.throttle( 8058 function (ctx) { 8059 _fnSaveState( ctx[0] ) 8060 }, 8061 500 8062 ); 8063 8064 8065 var __details_remove = function ( api, idx ) 8066 { 8067 var ctx = api.context; 8068 8069 if ( ctx.length ) { 8070 var row = ctx[0].aoData[ idx !== undefined ? idx : api[0] ]; 8071 8072 if ( row && row._details ) { 8073 row._details.remove(); 8074 8075 row._detailsShow = undefined; 8076 row._details = undefined; 8077 $( row.nTr ).removeClass( 'dt-hasChild' ); 8078 __details_state( ctx ); 8079 } 8080 } 8081 }; 8082 8083
8084 var __details_display = function ( api, show ) { 8085 var ctx = api.context; 8086 8087 if ( ctx.length && api.length ) { 8088 var row = ctx[0].aoData[ api[0] ]; 8089 8090 if ( row._details ) { 8091 row._detailsShow = show; 8092 8093 if ( show ) { 8094 row._details.insertAfter( row.nTr ); 8095 $( row.nTr ).addClass( 'dt-hasChild' ); 8096 } 8097 else { 8098 row._details.detach(); 8099 $( row.nTr ).removeClass( 'dt-hasChild' ); 8100 } 8101 8102 _fnCallbackFire( ctx[0], null, 'childRow', [ show, api.row( api[0] ) ] ) 8103 8104 __details_events( ctx[0] ); 8105 __details_state( ctx ); 8106 } 8107 } 8108 }; 8109 8110 8111 var __details_events = function ( settings ) 8112 { 8113 var api = new _Api( settings ); 8114 var namespace = '.dt.DT_details'; 8115 var drawEvent = 'draw'+namespace; 8116 var colvisEvent = 'column-sizing'+namespace; 8117 var destroyEvent = 'destroy'+namespace; 8118 var data = settings.aoData; 8119 8120 api.off( drawEvent +' '+ colvisEvent +' '+ destroyEvent ); 8121 8122 if ( _pluck( data, '_details' ).length > 0 ) { 8123 // On each draw, insert the required elements into the document 8124 api.on( drawEvent, function ( e, ctx ) { 8125 if ( settings !== ctx ) { 8126 return; 8127 } 8128 8129 api.rows( {page:'current'} ).eq(0).each( function (idx) { 8130 // Internal data grab 8131 var row = data[ idx ]; 8132 8133 if ( row._detailsShow ) { 8134 row._details.insertAfter( row.nTr ); 8135 } 8136 } ); 8137 } ); 8138 8139 // Column visibility change - update the colspan 8140 api.on( colvisEvent, function ( e, ctx ) { 8141 if ( settings !== ctx ) { 8142 return; 8143 } 8144 8145 // Update the colspan for the details rows (note, only if it already has 8146 // a colspan) 8147 var row, visible = _fnVisbleColumns( ctx ); 8148 8149 for ( var i=0, ien=data.length ; i<ien ; i++ ) { 8150 row = data[i]; 8151 8152 if ( row && row._details ) { 8153 row._details.each(function () { 8154 var el = $(this).children('td'); 8155 8156 if (el.length == 1) { 8157 el.attr('colspan', visible); 8158 } 8159 }); 8160 } 8161 } 8162 } ); 8163 8164 // Table destroyed - nuke any child rows 8165 api.on( destroyEvent, function ( e, ctx ) { 8166 if ( settings !== ctx ) { 8167 return; 8168 } 8169 8170 for ( var i=0, ien=data.length ; i<ien ; i++ ) { 8171 if ( data[i] && data[i]._details ) { 8172 __details_remove( api, i ); 8173 } 8174 } 8175 } ); 8176 } 8177 }; 8178 8179 // Strings for the method names to help minification 8180 var _emp = ''; 8181 var _child_obj = _emp+'row().child'; 8182 var _child_mth = _child_obj+'()'; 8183 8184 // data can be: 8185 // tr 8186 // string 8187 // jQuery or array of any of the above 8188 _api_register( _child_mth, function ( data, klass ) { 8189 var ctx = this.context; 8190 8191 if ( data === undefined ) { 8192 // get 8193 return ctx.length && this.length && ctx[0].aoData[ this[0] ] 8194 ? ctx[0].aoData[ this[0] ]._details 8195 : undefined; 8196 } 8197 else if ( data === true ) { 8198 // show 8199 this.child.show(); 8200 } 8201 else if ( data === false ) { 8202 // remove 8203 __details_remove( this ); 8204 } 8205 else if ( ctx.length && this.length ) { 8206 // set 8207 __details_add( ctx[0], ctx[0].aoData[ this[0] ], data, klass ); 8208 } 8209 8210 return this; 8211 } ); 8212 8213 8214 _api_register( [ 8215 _child_obj+'.show()', 8216 _child_mth+'.show()' // only when `child()` was called with parameters (without 8217 ], function () { // it returns an object and this method is not executed) 8218 __details_display( this, true ); 8219 return this; 8220 } ); 8221 8222 8223 _api_register( [ 8224 _child_obj+'.hide()', 8225 _child_mth+'.hide()' // only when `child()` was called with parameters (without 8226 ], function () { // it returns an object and this method is not executed) 8227 __details_display( this, false ); 8228 return this; 8229 } ); 8230 8231 8232 _api_register( [ 8233 _child_obj+'.remove()', 8234 _child_mth+'.remove()' // only when `child()` was called with parameters (without 8235 ], function () { // it returns an object and this method is not executed) 8236 __details_remove( this ); 8237 return this; 8238 } ); 8239 8240 8241 _api_register( _child_obj+'.isShown()', function () { 8242 var ctx = this.context; 8243 8244 if ( ctx.length && this.length && ctx[0].aoData[ this[0] ] ) {
8245 // _detailsShown as false or undefined will fall through to return false 8246 return ctx[0].aoData[ this[0] ]._detailsShow || false; 8247 } 8248 return false; 8249 } ); 8250 8251 8252 8253 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 8254 * Columns 8255 * 8256 * {integer} - column index (>=0 count from left, <0 count from right) 8257 * "{integer}:visIdx" - visible column index (i.e. translate to column index) (>=0 count from left, <0 count from right) 8258 * "{integer}:visible" - alias for {integer}:visIdx (>=0 count from left, <0 count from right) 8259 * "{string}:name" - column name 8260 * "{string}" - jQuery selector on column header nodes 8261 * 8262 */ 8263 8264 // can be an array of these items, comma separated list, or an array of comma 8265 // separated lists 8266 8267 var __re_column_selector = /^([^:]+)?:(name|title|visIdx|visible)$/; 8268 8269 8270 // r1 and r2 are redundant - but it means that the parameters match for the 8271 // iterator callback in columns().data() 8272 var __columnData = function ( settings, column, r1, r2, rows, type ) { 8273 var a = []; 8274 for ( var row=0, ien=rows.length ; row<ien ; row++ ) { 8275 a.push( _fnGetCellData( settings, rows[row], column, type ) ); 8276 } 8277 return a; 8278 }; 8279 8280 8281 var __column_header = function ( settings, column, row ) { 8282 var header = settings.aoHeader; 8283 var target = row !== undefined 8284 ? row 8285 : settings.bSortCellsTop // legacy support 8286 ? 0 8287 : header.length - 1; 8288 8289 return header[target][column].cell; 8290 }; 8291 8292 var __column_selector = function ( settings, selector, opts ) 8293 { 8294 var 8295 columns = settings.aoColumns, 8296 names = _pluck( columns, 'sName' ), 8297 titles = _pluck( columns, 'sTitle' ), 8298 cells = DataTable.util.get('[].[].cell')(settings.aoHeader), 8299 nodes = _unique( _flatten([], cells) ); 8300 8301 var run = function ( s ) { 8302 var selInt = _intVal( s ); 8303 8304 // Selector - all 8305 if ( s === '' ) { 8306 return _range( columns.length ); 8307 } 8308 8309 // Selector - index 8310 if ( selInt !== null ) { 8311 return [ selInt >= 0 ? 8312 selInt : // Count from left 8313 columns.length + selInt // Count from right (+ because its a negative value) 8314 ]; 8315 } 8316 8317 // Selector = function 8318 if ( typeof s === 'function' ) { 8319 var rows = _selector_row_indexes( settings, opts ); 8320 8321 return columns.map(function (col, idx) { 8322 return s( 8323 idx, 8324 __columnData( settings, idx, 0, 0, rows ), 8325 __column_header( settings, idx ) 8326 ) ? idx : null; 8327 }); 8328 } 8329 8330 // jQuery or string selector 8331 var match = typeof s === 'string' ? 8332 s.match( __re_column_selector ) : 8333 ''; 8334 8335 if ( match ) { 8336 switch( match[2] ) { 8337 case 'visIdx': 8338 case 'visible': 8339 if (match[1]) { 8340 var idx = parseInt( match[1], 10 ); 8341 // Visible index given, convert to column index 8342 if ( idx < 0 ) { 8343 // Counting from the right 8344 var visColumns = columns.map( function (col,i) { 8345 return col.bVisible ? i : null; 8346 } ); 8347 return [ visColumns[ visColumns.length + idx ] ]; 8348 } 8349 // Counting from the left 8350 return [ _fnVisibleToColumnIndex( settings, idx ) ]; 8351 } 8352 8353 // `:visible` on its own 8354 return columns.map( function (col, i) { 8355 return col.bVisible ? i : null; 8356 } ); 8357 8358 case 'name': 8359 // match by name. `names` is column index complete and in order 8360 return names.map( function (name, i) { 8361 return name === match[1] ? i : null; 8362 } ); 8363 8364 case 'title': 8365 // match by column title 8366 return titles.map( function (title, i) { 8367 return title === match[1] ? i : null; 8368 } ); 8369 8370 default: 8371 return []; 8372 } 8373 } 8374 8375 // Cell in the table body 8376 if ( s.nodeName && s._DT_CellIndex ) { 8377 return [ s._DT_CellIndex.column ]; 8378 } 8379 8380 // jQuery selector on the TH elements for the columns 8381 var jqResult = $( nodes ) 8382 .filter( s ) 8383 .map( function () { 8384 return _fnColumnsFromHeader( this ); // `nodes` is column index complete and in order 8385 } ) 8386 .toArray(); 8387 8388 if ( jqResult.length || ! s.nodeName ) {
8389 return jqResult; 8390 } 8391 8392 // Otherwise a node which might have a `dt-column` data attribute, or be 8393 // a child or such an element 8394 var host = $(s).closest('*[data-dt-column]'); 8395 return host.length ? 8396 [ host.data('dt-column') ] : 8397 []; 8398 }; 8399 8400 return _selector_run( 'column', selector, run, settings, opts ); 8401 }; 8402 8403 8404 var __setColumnVis = function ( settings, column, vis ) { 8405 var 8406 cols = settings.aoColumns, 8407 col = cols[ column ], 8408 data = settings.aoData, 8409 cells, i, ien, tr; 8410 8411 // Get 8412 if ( vis === undefined ) { 8413 return col.bVisible; 8414 } 8415 8416 // Set 8417 // No change 8418 if ( col.bVisible === vis ) { 8419 return false; 8420 } 8421 8422 if ( vis ) { 8423 // Insert column 8424 // Need to decide if we should use appendChild or insertBefore 8425 var insertBefore = _pluck(cols, 'bVisible').indexOf(true, column+1); 8426 8427 for ( i=0, ien=data.length ; i<ien ; i++ ) { 8428 if (data[i]) { 8429 tr = data[i].nTr; 8430 cells = data[i].anCells; 8431 8432 if ( tr ) { 8433 // insertBefore can act like appendChild if 2nd arg is null 8434 tr.insertBefore( cells[ column ], cells[ insertBefore ] || null ); 8435 } 8436 } 8437 } 8438 } 8439 else { 8440 // Remove column 8441 $( _pluck( settings.aoData, 'anCells', column ) ).detach(); 8442 } 8443 8444 // Common actions 8445 col.bVisible = vis; 8446 8447 _colGroup(settings); 8448 8449 return true; 8450 }; 8451 8452 8453 _api_register( 'columns()', function ( selector, opts ) { 8454 // argument shifting 8455 if ( selector === undefined ) { 8456 selector = ''; 8457 } 8458 else if ( $.isPlainObject( selector ) ) { 8459 opts = selector; 8460 selector = ''; 8461 } 8462 8463 opts = _selector_opts( opts ); 8464 8465 var inst = this.iterator( 'table', function ( settings ) { 8466 return __column_selector( settings, selector, opts ); 8467 }, 1 ); 8468 8469 // Want argument shifting here and in _row_selector? 8470 inst.selector.cols = selector; 8471 inst.selector.opts = opts; 8472 8473 return inst; 8474 } ); 8475 8476 _api_registerPlural( 'columns().header()', 'column().header()', function ( row ) { 8477 return this.iterator( 'column', function (settings, column) { 8478 return __column_header(settings, column, row); 8479 }, 1 ); 8480 } ); 8481 8482 _api_registerPlural( 'columns().footer()', 'column().footer()', function ( row ) { 8483 return this.iterator( 'column', function ( settings, column ) { 8484 var footer = settings.aoFooter; 8485 8486 if (! footer.length) { 8487 return null; 8488 } 8489 8490 return settings.aoFooter[row !== undefined ? row : 0][column].cell; 8491 }, 1 ); 8492 } ); 8493 8494 _api_registerPlural( 'columns().data()', 'column().data()', function () { 8495 return this.iterator( 'column-rows', __columnData, 1 ); 8496 } ); 8497 8498 _api_registerPlural( 'columns().render()', 'column().render()', function ( type ) { 8499 return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) { 8500 return __columnData( settings, column, i, j, rows, type ); 8501 }, 1 ); 8502 } ); 8503 8504 _api_registerPlural( 'columns().dataSrc()', 'column().dataSrc()', function () { 8505 return this.iterator( 'column', function ( settings, column ) { 8506 return settings.aoColumns[column].mData; 8507 }, 1 ); 8508 } ); 8509 8510 _api_registerPlural( 'columns().cache()', 'column().cache()', function ( type ) { 8511 return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) { 8512 return _pluck_order( settings.aoData, rows, 8513 type === 'search' ? '_aFilterData' : '_aSortData', column 8514 ); 8515 }, 1 ); 8516 } ); 8517 8518 _api_registerPlural( 'columns().init()', 'column().init()', function () { 8519 return this.iterator( 'column', function ( settings, column ) { 8520 return settings.aoColumns[column]; 8521 }, 1 ); 8522 } ); 8523 8524 _api_registerPlural( 'columns().nodes()', 'column().nodes()', function () { 8525 return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) { 8526 return _pluck_order( settings.aoData, rows, 'anCells', column ) ; 8527 }, 1 ); 8528 } ); 8529 8530 _api_registerPlural( 'columns().titles()', 'column().title()', function (title, row) { 8531 return this.iterator( 'column', function ( settings, column ) { 8532 // Argument shifting 8533 if (typeof title === 'number') { 8534 row = title; 8535 title = undefined; 8536 } 8537 8538 var span = $('span.dt-column-title', this.column(column).header(row)); 8539 8540 if (title !== undefined) { 8541 span.html(title); 8542 return this; 8543 } 8544 8545 return span.html(); 8546 }, 1 ); 8547 } ); 8548 8549 _api_registerPlural( 'columns().types()', 'column().type()', function () { 8550 return this.iterator( 'column', function ( settings, column ) { 8551 var type = settings.aoColumns[column].sType; 8552
8553 // If the type was invalidated, then resolve it. This actually does 8554 // all columns at the moment. Would only happen once if getting all 8555 // column's data types. 8556 if (! type) { 8557 _fnColumnTypes(settings); 8558 } 8559 8560 return type; 8561 }, 1 ); 8562 } ); 8563 8564 _api_registerPlural( 'columns().visible()', 'column().visible()', function ( vis, calc ) { 8565 var that = this; 8566 var changed = []; 8567 var ret = this.iterator( 'column', function ( settings, column ) { 8568 if ( vis === undefined ) { 8569 return settings.aoColumns[ column ].bVisible; 8570 } // else 8571 8572 if (__setColumnVis( settings, column, vis )) { 8573 changed.push(column); 8574 } 8575 } ); 8576 8577 // Group the column visibility changes 8578 if ( vis !== undefined ) { 8579 this.iterator( 'table', function ( settings ) { 8580 // Redraw the header after changes 8581 _fnDrawHead( settings, settings.aoHeader ); 8582 _fnDrawHead( settings, settings.aoFooter ); 8583 8584 // Update colspan for no records display. Child rows and extensions will use their own 8585 // listeners to do this - only need to update the empty table item here 8586 if ( ! settings.aiDisplay.length ) { 8587 $(settings.nTBody).find('td[colspan]').attr('colspan', _fnVisbleColumns(settings)); 8588 } 8589 8590 _fnSaveState( settings ); 8591 8592 // Second loop once the first is done for events 8593 that.iterator( 'column', function ( settings, column ) { 8594 if (changed.includes(column)) { 8595 _fnCallbackFire( settings, null, 'column-visibility', [settings, column, vis, calc] ); 8596 } 8597 } ); 8598 8599 if ( changed.length && (calc === undefined || calc) ) { 8600 that.columns.adjust(); 8601 } 8602 }); 8603 } 8604 8605 return ret; 8606 } ); 8607 8608 _api_registerPlural( 'columns().widths()', 'column().width()', function () { 8609 // Injects a fake row into the table for just a moment so the widths can 8610 // be read, regardless of colspan in the header and rows being present in 8611 // the body 8612 var columns = this.columns(':visible').count(); 8613 var row = $('<tr>').html('<td>' + Array(columns).join('</td><td>') + '</td>'); 8614 8615 $(this.table().body()).append(row); 8616 8617 var widths = row.children().map(function () { 8618 return $(this).outerWidth(); 8619 }); 8620 8621 row.remove(); 8622 8623 return this.iterator( 'column', function ( settings, column ) { 8624 var visIdx = _fnColumnIndexToVisible( settings, column ); 8625 8626 return visIdx !== null ? widths[visIdx] : 0; 8627 }, 1); 8628 } ); 8629 8630 _api_registerPlural( 'columns().indexes()', 'column().index()', function ( type ) { 8631 return this.iterator( 'column', function ( settings, column ) { 8632 return type === 'visible' ? 8633 _fnColumnIndexToVisible( settings, column ) : 8634 column; 8635 }, 1 ); 8636 } ); 8637 8638 _api_register( 'columns.adjust()', function () { 8639 return this.iterator( 'table', function ( settings ) { 8640 _fnAdjustColumnSizing( settings ); 8641 }, 1 ); 8642 } ); 8643 8644 _api_register( 'column.index()', function ( type, idx ) { 8645 if ( this.context.length !== 0 ) { 8646 var ctx = this.context[0]; 8647 8648 if ( type === 'fromVisible' || type === 'toData' ) { 8649 return _fnVisibleToColumnIndex( ctx, idx ); 8650 } 8651 else if ( type === 'fromData' || type === 'toVisible' ) { 8652 return _fnColumnIndexToVisible( ctx, idx ); 8653 } 8654 } 8655 } ); 8656 8657 _api_register( 'column()', function ( selector, opts ) { 8658 return _selector_first( this.columns( selector, opts ) ); 8659 } ); 8660 8661 var __cell_selector = function ( settings, selector, opts ) 8662 { 8663 var data = settings.aoData; 8664 var rows = _selector_row_indexes( settings, opts ); 8665 var cells = _removeEmpty( _pluck_order( data, rows, 'anCells' ) ); 8666 var allCells = $(_flatten( [], cells )); 8667 var row; 8668 var columns = settings.aoColumns.length; 8669 var a, i, ien, j, o, host; 8670 8671 var run = function ( s ) { 8672 var fnSelector = typeof s === 'function'; 8673 8674 if ( s === null || s === undefined || fnSelector ) { 8675 // All cells and function selectors 8676 a = []; 8677 8678 for ( i=0, ien=rows.length ; i<ien ; i++ ) { 8679 row = rows[i]; 8680 8681 for ( j=0 ; j<columns ; j++ ) { 8682 o = { 8683 row: row, 8684 column: j 8685 }; 8686 8687 if ( fnSelector ) { 8688 // Selector - function 8689 host = data[ row ]; 8690 8691 if ( s( o, _fnGetCellData(settings, row, j), host.anCells ? host.anCells[j] : null ) ) { 8692 a.push( o ); 8693 } 8694 } 8695 else { 8696 // Selector - all 8697 a.push( o ); 8698 } 8699 } 8700 } 8701 8702 return a; 8703 } 8704 8705 // Selector - index 8706 if ( $.isPlainObject( s ) ) { 8707 // Valid cell index and its in the array of selectable rows 8708 return s.column !== undefined && s.row !== undefined && rows.indexOf(s.row) !== -1 ? 8709 [s] : 8710 []; 8711 } 8712 8713 // Selector - jQuery filtered cells 8714 var jqResult = allCells 8715 .filter( s ) 8716 .map( function (i, el) { 8717 return { // use a new object, in case someone changes the values 8718 row: el._DT_CellIndex.row, 8719 column: el._DT_CellIndex.column 8720 }; 8721 } ) 8722 .toArray(); 8723 8724 if ( jqResult.length || ! s.nodeName ) {
8725 return jqResult; 8726 } 8727 8728 // Otherwise the selector is a node, and there is one last option - the 8729 // element might be a child of an element which has dt-row and dt-column 8730 // data attributes 8731 host = $(s).closest('*[data-dt-row]'); 8732 return host.length ? 8733 [ { 8734 row: host.data('dt-row'), 8735 column: host.data('dt-column') 8736 } ] : 8737 []; 8738 }; 8739 8740 return _selector_run( 'cell', selector, run, settings, opts ); 8741 }; 8742 8743 8744 8745 8746 _api_register( 'cells()', function ( rowSelector, columnSelector, opts ) { 8747 // Argument shifting 8748 if ( $.isPlainObject( rowSelector ) ) { 8749 // Indexes 8750 if ( rowSelector.row === undefined ) { 8751 // Selector options in first parameter 8752 opts = rowSelector; 8753 rowSelector = null; 8754 } 8755 else { 8756 // Cell index objects in first parameter 8757 opts = columnSelector; 8758 columnSelector = null; 8759 } 8760 } 8761 if ( $.isPlainObject( columnSelector ) ) { 8762 opts = columnSelector; 8763 columnSelector = null; 8764 } 8765 8766 // Cell selector 8767 if ( columnSelector === null || columnSelector === undefined ) { 8768 return this.iterator( 'table', function ( settings ) { 8769 return __cell_selector( settings, rowSelector, _selector_opts( opts ) ); 8770 } ); 8771 } 8772 8773 // The default built in options need to apply to row and columns 8774 var internalOpts = opts ? { 8775 page: opts.page, 8776 order: opts.order, 8777 search: opts.search 8778 } : {}; 8779 8780 // Row + column selector 8781 var columns = this.columns( columnSelector, internalOpts ); 8782 var rows = this.rows( rowSelector, internalOpts ); 8783 var i, ien, j, jen; 8784 8785 var cellsNoOpts = this.iterator( 'table', function ( settings, idx ) { 8786 var a = []; 8787 8788 for ( i=0, ien=rows[idx].length ; i<ien ; i++ ) { 8789 for ( j=0, jen=columns[idx].length ; j<jen ; j++ ) { 8790 a.push( { 8791 row: rows[idx][i], 8792 column: columns[idx][j] 8793 } ); 8794 } 8795 } 8796 8797 return a; 8798 }, 1 ); 8799 8800 // There is currently only one extension which uses a cell selector extension 8801 // It is a _major_ performance drag to run this if it isn't needed, so this is 8802 // an extension specific check at the moment 8803 var cells = opts && opts.selected ? 8804 this.cells( cellsNoOpts, opts ) : 8805 cellsNoOpts; 8806 8807 $.extend( cells.selector, { 8808 cols: columnSelector, 8809 rows: rowSelector, 8810 opts: opts 8811 } ); 8812 8813 return cells; 8814 } ); 8815 8816 8817 _api_registerPlural( 'cells().nodes()', 'cell().node()', function () { 8818 return this.iterator( 'cell', function ( settings, row, column ) { 8819 var data = settings.aoData[ row ]; 8820 8821 return data && data.anCells ? 8822 data.anCells[ column ] : 8823 undefined; 8824 }, 1 ); 8825 } ); 8826 8827 8828 _api_register( 'cells().data()', function () { 8829 return this.iterator( 'cell', function ( settings, row, column ) { 8830 return _fnGetCellData( settings, row, column ); 8831 }, 1 ); 8832 } ); 8833 8834 8835 _api_registerPlural( 'cells().cache()', 'cell().cache()', function ( type ) { 8836 type = type === 'search' ? '_aFilterData' : '_aSortData'; 8837 8838 return this.iterator( 'cell', function ( settings, row, column ) { 8839 return settings.aoData[ row ][ type ][ column ]; 8840 }, 1 ); 8841 } ); 8842 8843 8844 _api_registerPlural( 'cells().render()', 'cell().render()', function ( type ) { 8845 return this.iterator( 'cell', function ( settings, row, column ) { 8846 return _fnGetCellData( settings, row, column, type ); 8847 }, 1 ); 8848 } ); 8849 8850 8851 _api_registerPlural( 'cells().indexes()', 'cell().index()', function () { 8852 return this.iterator( 'cell', function ( settings, row, column ) { 8853 return { 8854 row: row, 8855 column: column, 8856 columnVisible: _fnColumnIndexToVisible( settings, column ) 8857 }; 8858 }, 1 ); 8859 } ); 8860 8861 8862 _api_registerPlural( 'cells().invalidate()', 'cell().invalidate()', function ( src ) { 8863 return this.iterator( 'cell', function ( settings, row, column ) { 8864 _fnInvalidate( settings, row, src, column ); 8865 } ); 8866 } ); 8867 8868 8869 8870 _api_register( 'cell()', function ( rowSelector, columnSelector, opts ) { 8871 return _selector_first( this.cells( rowSelector, columnSelector, opts ) ); 8872 } ); 8873 8874 8875 _api_register( 'cell().data()', function ( data ) { 8876 var ctx = this.context; 8877 var cell = this[0]; 8878 8879 if ( data === undefined ) { 8880 // Get 8881 return ctx.length && cell.length ? 8882 _fnGetCellData( ctx[0], cell[0].row, cell[0].column ) : 8883 undefined; 8884 } 8885 8886 // Set 8887 _fnSetCellData( ctx[0], cell[0].row, cell[0].column, data ); 8888 _fnInvalidate( ctx[0], cell[0].row, 'data', cell[0].column ); 8889 8890 return this; 8891 } ); 8892 8893 8894 8895 /** 8896 * Get current ordering (sorting) that has been applied to the table. 8897 * 8898 * @returns {array} 2D array containing the sorting information for the first 8899 * table in the current context. Each element in the parent array represents 8900 * a column being sorted upon (i.e. multi-sorting with two columns would have 8901 * 2 inner arrays). The inner arrays may have 2 or 3 elements. The first is 8902 * the column index that the sorting condition applies to, the second is the 8903 * direction of the sort (`desc` or `asc`) and, optionally, the third is the 8904 * index of the sorting order from the `column.sorting` initialisation array. 8905 *//** 8906 * Set the ordering for the table. 8907 * 8908 * @param {integer} order Column index to sort upon. 8909 * @param {string} direction Direction of the sort to be applied (`asc` or `desc`) 8910 * @returns {DataTables.Api} this 8911 *//** 8912 * Set the ordering for the table. 8913 * 8914 * @param {array} order 1D array of sorting information to be applied. 8915 * @param {array} [...] Optional additional sorting conditions 8916 * @returns {DataTables.Api} this 8917 *//** 8918 * Set the ordering for the table. 8919 * 8920 * @param {array} order 2D array of sorting information to be applied. 8921 * @returns {DataTables.Api} this 8922 */ 8923 _api_register( 'order()', function ( order, dir ) { 8924 var ctx = this.context; 8925 var args = Array.prototype.slice.call( arguments ); 8926 8927 if ( order === undefined ) { 8928 // get 8929 return ctx.length !== 0 ? 8930 ctx[0].aaSorting : 8931 undefined; 8932 } 8933 8934 // set 8935 if ( typeof order === 'number' ) {
8936 // Simple column / direction passed in 8937 order = [ [ order, dir ] ]; 8938 } 8939 else if ( args.length > 1 ) { 8940 // Arguments passed in (list of 1D arrays) 8941 order = args; 8942 } 8943 // otherwise a 2D array was passed in 8944 8945 return this.iterator( 'table', function ( settings ) { 8946 settings.aaSorting = Array.isArray(order) ? order.slice() : order; 8947 } ); 8948 } ); 8949 8950 8951 /** 8952 * Attach a sort listener to an element for a given column 8953 * 8954 * @param {node|jQuery|string} node Identifier for the element(s) to attach the 8955 * listener to. This can take the form of a single DOM node, a jQuery 8956 * collection of nodes or a jQuery selector which will identify the node(s). 8957 * @param {integer} column the column that a click on this node will sort on 8958 * @param {function} [callback] callback function when sort is run 8959 * @returns {DataTables.Api} this 8960 */ 8961 _api_register( 'order.listener()', function ( node, column, callback ) { 8962 return this.iterator( 'table', function ( settings ) { 8963 _fnSortAttachListener(settings, node, {}, column, callback); 8964 } ); 8965 } ); 8966 8967 8968 _api_register( 'order.fixed()', function ( set ) { 8969 if ( ! set ) { 8970 var ctx = this.context; 8971 var fixed = ctx.length ? 8972 ctx[0].aaSortingFixed : 8973 undefined; 8974 8975 return Array.isArray( fixed ) ? 8976 { pre: fixed } : 8977 fixed; 8978 } 8979 8980 return this.iterator( 'table', function ( settings ) { 8981 settings.aaSortingFixed = $.extend( true, {}, set ); 8982 } ); 8983 } ); 8984 8985 8986 // Order by the selected column(s) 8987 _api_register( [ 8988 'columns().order()', 8989 'column().order()' 8990 ], function ( dir ) { 8991 var that = this; 8992 8993 if ( ! dir ) { 8994 return this.iterator( 'column', function ( settings, idx ) { 8995 var sort = _fnSortFlatten( settings ); 8996 8997 for ( var i=0, ien=sort.length ; i<ien ; i++ ) { 8998 if ( sort[i].col === idx ) { 8999 return sort[i].dir; 9000 } 9001 } 9002 9003 return null; 9004 }, 1 ); 9005 } 9006 else { 9007 return this.iterator( 'table', function ( settings, i ) { 9008 settings.aaSorting = that[i].map( function (col) { 9009 return [ col, dir ]; 9010 } ); 9011 } ); 9012 } 9013 } ); 9014 9015 _api_registerPlural('columns().orderable()', 'column().orderable()', function ( directions ) { 9016 return this.iterator( 'column', function ( settings, idx ) { 9017 var col = settings.aoColumns[idx]; 9018 9019 return directions ? 9020 col.asSorting : 9021 col.bSortable; 9022 }, 1 ); 9023 } ); 9024 9025 9026 _api_register( 'processing()', function ( show ) { 9027 return this.iterator( 'table', function ( ctx ) { 9028 _fnProcessingDisplay( ctx, show ); 9029 } ); 9030 } ); 9031 9032 9033 _api_register( 'search()', function ( input, regex, smart, caseInsen ) { 9034 var ctx = this.context; 9035 9036 if ( input === undefined ) { 9037 // get 9038 return ctx.length !== 0 ? 9039 ctx[0].oPreviousSearch.search : 9040 undefined; 9041 } 9042 9043 // set 9044 return this.iterator( 'table', function ( settings ) { 9045 if ( ! settings.oFeatures.bFilter ) { 9046 return; 9047 } 9048 9049 if (typeof regex === 'object') { 9050 // New style options to pass to the search builder 9051 _fnFilterComplete( settings, $.extend( settings.oPreviousSearch, regex, { 9052 search: input 9053 } ) ); 9054 } 9055 else { 9056 // Compat for the old options 9057 _fnFilterComplete( settings, $.extend( settings.oPreviousSearch, { 9058 search: input, 9059 regex: regex === null ? false : regex, 9060 smart: smart === null ? true : smart, 9061 caseInsensitive: caseInsen === null ? true : caseInsen 9062 } ) ); 9063 } 9064 } ); 9065 } ); 9066 9067 _api_register( 'search.fixed()', function ( name, search ) { 9068 var ret = this.iterator( true, 'table', function ( settings ) { 9069 var fixed = settings.searchFixed; 9070 9071 if (! name) { 9072 return Object.keys(fixed) 9073 } 9074 else if (search === undefined) { 9075 return fixed[name]; 9076 } 9077 else if (search === null) { 9078 delete fixed[name]; 9079 } 9080 else { 9081 fixed[name] = search; 9082 } 9083 9084 return this; 9085 } ); 9086 9087 return name !== undefined && search === undefined 9088 ? ret[0] 9089 : ret; 9090 } ); 9091 9092 _api_registerPlural( 9093 'columns().search()', 9094 'column().search()', 9095 function ( input, regex, smart, caseInsen ) { 9096 return this.iterator( 'column', function ( settings, column ) { 9097 var preSearch = settings.aoPreSearchCols; 9098 9099 if ( input === undefined ) { 9100 // get 9101 return preSearch[ column ].search; 9102 } 9103 9104 // set 9105 if ( ! settings.oFeatures.bFilter ) { 9106 return; 9107 } 9108 9109 if (typeof regex === 'object') { 9110 // New style options to pass to the search builder 9111 $.extend( preSearch[ column ], regex, { 9112 search: input 9113 } ); 9114 } 9115 else { 9116 // Old style (with not all options available) 9117 $.extend( preSearch[ column ], { 9118 search: input, 9119 regex: regex === null ? false : regex, 9120 smart: smart === null ? true : smart, 9121 caseInsensitive: caseInsen === null ? true : caseInsen 9122 } ); 9123 } 9124 9125 _fnFilterComplete( settings, settings.oPreviousSearch ); 9126 } ); 9127 } 9128 ); 9129 9130 _api_register([ 9131 'columns().search.fixed()', 9132 'column().search.fixed()' 9133 ], 9134 function ( name, search ) { 9135 var ret = this.iterator( true, 'column', function ( settings, colIdx ) { 9136 var fixed = settings.aoColumns[colIdx].searchFixed; 9137 9138 if (! name) { 9139 return Object.keys(fixed) 9140 } 9141 else if (search === undefined) { 9142 return fixed[name]; 9143 } 9144 else if (search === null) { 9145 delete fixed[name]; 9146 } 9147 else { 9148 fixed[name] = search; 9149 } 9150 9151 return this; 9152 } ); 9153 9154 return name !== undefined && search === undefined 9155 ? ret[0] 9156 : ret; 9157 } 9158 ); 9159 /* 9160 * State API methods 9161 */ 9162 9163 _api_register( 'state()', function ( set, ignoreTime ) { 9164 // getter 9165 if ( ! set ) { 9166 return this.context.length ? 9167 this.context[0].oSavedState : 9168 null; 9169 } 9170 9171 var setMutate = $.extend( true, {}, set ); 9172 9173 // setter 9174 return this.iterator( 'table', function ( settings ) { 9175 if ( ignoreTime !== false ) { 9176 setMutate.time = +new Date() + 100; 9177 } 9178 9179 _fnImplementState( settings, setMutate, function(){} ); 9180 } ); 9181 } ); 9182 9183 9184 _api_register( 'state.clear()', function () { 9185 return this.iterator( 'table', function ( settings ) { 9186 // Save an empty object 9187 settings.fnStateSaveCallback.call( settings.oInstance, settings, {} ); 9188 } ); 9189 } ); 9190 9191 9192 _api_register( 'state.loaded()', function () { 9193 return this.context.length ? 9194 this.context[0].oLoadedState : 9195 null; 9196 } ); 9197 9198 9199 _api_register( 'state.save()', function () { 9200 return this.iterator( 'table', function ( settings ) { 9201 _fnSaveState( settings ); 9202 } ); 9203 } ); 9204 9205 /** 9206 * Set the jQuery or window object to be used by DataTables 9207 * 9208 * @param {*} module Library / container object 9209 * @param {string} [type] Library or container type `lib`, `win` or `datetime`. 9210 * If not provided, automatic detection is attempted. 9211 */ 9212 DataTable.use = function (module, type) { 9213 if (type === 'lib' || module.fn) { 9214 $ = module; 9215 } 9216 else if (type == 'win' || module.document) { 9217 window = module; 9218 document = module.document; 9219 } 9220 else if (type === 'datetime' || module.type === 'DateTime') { 9221 DataTable.DateTime = module; 9222 } 9223 } 9224 9225 /** 9226 * CommonJS factory function pass through. This will check if the arguments 9227 * given are a window object or a jQuery object. If so they are set 9228 * accordingly. 9229 * @param {*} root Window 9230 * @param {*} jq jQUery 9231 * @returns {boolean} Indicator 9232 */ 9233 DataTable.factory = function (root, jq) { 9234 var is = false; 9235 9236 // Test if the first parameter is a window object 9237 if (root && root.document) { 9238 window = root; 9239 document = root.document; 9240 } 9241 9242 // Test if the second parameter is a jQuery object 9243 if (jq && jq.fn && jq.fn.jquery) { 9244 $ = jq; 9245 is = true; 9246 } 9247 9248 return is; 9249 } 9250 9251 /**
9252 * Provide a common method for plug-ins to check the version of DataTables being 9253 * used, in order to ensure compatibility. 9254 * 9255 * @param {string} version Version string to check for, in the format "X.Y.Z". 9256 * Note that the formats "X" and "X.Y" are also acceptable. 9257 * @param {string} [version2=current DataTables version] As above, but optional. 9258 * If not given the current DataTables version will be used. 9259 * @returns {boolean} true if this version of DataTables is greater or equal to 9260 * the required version, or false if this version of DataTales is not 9261 * suitable 9262 * @static 9263 * @dtopt API-Static 9264 * 9265 * @example 9266 * alert( $.fn.dataTable.versionCheck( '1.9.0' ) ); 9267 */ 9268 DataTable.versionCheck = function( version, version2 ) 9269 { 9270 var aThis = version2 ? 9271 version2.split('.') : 9272 DataTable.version.split('.'); 9273 var aThat = version.split('.'); 9274 var iThis, iThat; 9275 9276 for ( var i=0, iLen=aThat.length ; i<iLen ; i++ ) { 9277 iThis = parseInt( aThis[i], 10 ) || 0; 9278 iThat = parseInt( aThat[i], 10 ) || 0; 9279 9280 // Parts are the same, keep comparing 9281 if (iThis === iThat) { 9282 continue; 9283 } 9284 9285 // Parts are different, return immediately 9286 return iThis > iThat; 9287 } 9288 9289 return true; 9290 }; 9291 9292 9293 /** 9294 * Check if a `<table>` node is a DataTable table already or not. 9295 * 9296 * @param {node|jquery|string} table Table node, jQuery object or jQuery 9297 * selector for the table to test. Note that if more than more than one 9298 * table is passed on, only the first will be checked 9299 * @returns {boolean} true the table given is a DataTable, or false otherwise 9300 * @static 9301 * @dtopt API-Static 9302 * 9303 * @example 9304 * if ( ! $.fn.DataTable.isDataTable( '#example' ) ) { 9305 * $('#example').dataTable(); 9306 * } 9307 */ 9308 DataTable.isDataTable = function ( table ) 9309 { 9310 var t = $(table).get(0); 9311 var is = false; 9312 9313 if ( table instanceof DataTable.Api ) { 9314 return true; 9315 } 9316 9317 $.each( DataTable.settings, function (i, o) { 9318 var head = o.nScrollHead ? $('table', o.nScrollHead)[0] : null; 9319 var foot = o.nScrollFoot ? $('table', o.nScrollFoot)[0] : null; 9320 9321 if ( o.nTable === t || head === t || foot === t ) { 9322 is = true; 9323 } 9324 } ); 9325 9326 return is; 9327 }; 9328 9329 9330 /** 9331 * Get all DataTable tables that have been initialised - optionally you can 9332 * select to get only currently visible tables. 9333 * 9334 * @param {boolean} [visible=false] Flag to indicate if you want all (default) 9335 * or visible tables only. 9336 * @returns {array} Array of `table` nodes (not DataTable instances) which are 9337 * DataTables 9338 * @static 9339 * @dtopt API-Static 9340 * 9341 * @example 9342 * $.each( $.fn.dataTable.tables(true), function () { 9343 * $(table).DataTable().columns.adjust(); 9344 * } ); 9345 */ 9346 DataTable.tables = function ( visible ) 9347 { 9348 var api = false; 9349 9350 if ( $.isPlainObject( visible ) ) { 9351 api = visible.api; 9352 visible = visible.visible; 9353 } 9354 9355 var a = DataTable.settings 9356 .filter( function (o) { 9357 return !visible || (visible && $(o.nTable).is(':visible')) 9358 ? true 9359 : false; 9360 } ) 9361 .map( function (o) { 9362 return o.nTable; 9363 }); 9364 9365 return api ? 9366 new _Api( a ) : 9367 a; 9368 }; 9369 9370 9371 /** 9372 * Convert from camel case parameters to Hungarian notation. This is made public 9373 * for the extensions to provide the same ability as DataTables core to accept 9374 * either the 1.9 style Hungarian notation, or the 1.10+ style camelCase 9375 * parameters. 9376 * 9377 * @param {object} src The model object which holds all parameters that can be 9378 * mapped. 9379 * @param {object} user The object to convert from camel case to Hungarian. 9380 * @param {boolean} force When set to `true`, properties which already have a 9381 * Hungarian value in the `user` object will be overwritten. Otherwise they 9382 * won't be. 9383 */ 9384 DataTable.camelToHungarian = _fnCamelToHungarian; 9385 9386 9387 9388 /** 9389 * 9390 */ 9391 _api_register( '$()', function ( selector, opts ) { 9392 var 9393 rows = this.rows( opts ).nodes(), // Get all rows 9394 jqRows = $(rows); 9395 9396 return $( [].concat( 9397 jqRows.filter( selector ).toArray(), 9398 jqRows.find( selector ).toArray() 9399 ) ); 9400 } ); 9401 9402 9403 // jQuery functions to operate on the tables 9404 $.each( [ 'on', 'one', 'off' ], function (i, key) { 9405 _api_register( key+'()', function ( /* event, handler */ ) { 9406 var args = Array.prototype.slice.call(arguments); 9407 9408 // Add the `dt` namespace automatically if it isn't already present 9409 args[0] = args[0].split( /\s/ ).map( function ( e ) { 9410 return ! e.match(/\.dt\b/) ? 9411 e+'.dt' : 9412 e; 9413 } ).join( ' ' ); 9414 9415 var inst = $( this.tables().nodes() ); 9416 inst[key].apply( inst, args ); 9417 return this; 9418 } ); 9419 } ); 9420 9421 9422 _api_register( 'clear()', function () { 9423 return this.iterator( 'table', function ( settings ) { 9424 _fnClearTable( settings ); 9425 } ); 9426 } ); 9427 9428 9429 _api_register( 'error()', function (msg) { 9430 return this.iterator( 'table', function ( settings ) { 9431 _fnLog( settings, 0, msg ); 9432 } ); 9433 } ); 9434 9435 9436 _api_register( 'settings()', function () { 9437 return new _Api( this.context, this.context ); 9438 } ); 9439 9440 9441 _api_register( 'init()', function () { 9442 var ctx = this.context; 9443 return ctx.length ? ctx[0].oInit : null; 9444 } ); 9445 9446 9447 _api_register( 'data()', function () { 9448 return this.iterator( 'table', function ( settings ) { 9449 return _pluck( settings.aoData, '_aData' ); 9450 } ).flatten(); 9451 } ); 9452 9453 9454 _api_register( 'trigger()', function ( name, args, bubbles ) { 9455 return this.iterator( 'table', function ( settings ) { 9456 return _fnCallbackFire( settings, null, name, args, bubbles ); 9457 } ).flatten(); 9458 } ); 9459 9460 9461 _api_register( 'ready()', function ( fn ) { 9462 var ctx = this.context; 9463 9464 // Get status of first table 9465 if (! fn) { 9466 return ctx.length 9467 ? (ctx[0]._bInitComplete || false) 9468 : null; 9469 } 9470 9471 // Function to run either once the table becomes ready or 9472 // immediately if it is already ready. 9473 return this.tables().every(function () { 9474 if (this.context[0]._bInitComplete) { 9475 fn.call(this); 9476 } 9477 else { 9478 this.on('init', function () { 9479 fn.call(this); 9480 }); 9481 } 9482 } ); 9483 } ); 9484 9485 9486 _api_register( 'destroy()', function ( remove ) { 9487 remove = remove || false; 9488 9489 return this.iterator( 'table', function ( settings ) { 9490 var classes = settings.oClasses; 9491 var table = settings.nTable; 9492 var tbody = settings.nTBody; 9493 var thead = settings.nTHead; 9494 var tfoot = settings.nTFoot; 9495 var jqTable = $(table); 9496 var jqTbody = $(tbody); 9497 var jqWrapper = $(settings.nTableWrapper); 9498 var rows = settings.aoData.map( function (r) { return r ? r.nTr : null; } ); 9499 var orderClasses = classes.order; 9500 9501 // Flag to note that the table is currently being destroyed - no action 9502 // should be taken 9503 settings.bDestroying = true; 9504
9505 // Fire off the destroy callbacks for plug-ins etc 9506 _fnCallbackFire( settings, "aoDestroyCallback", "destroy", [settings], true ); 9507 9508 // If not being removed from the document, make all columns visible 9509 if ( ! remove ) { 9510 new _Api( settings ).columns().visible( true ); 9511 } 9512 9513 // Blitz all `DT` namespaced events (these are internal events, the 9514 // lowercase, `dt` events are user subscribed and they are responsible 9515 // for removing them 9516 jqWrapper.off('.DT').find(':not(tbody *)').off('.DT'); 9517 $(window).off('.DT-'+settings.sInstance); 9518 9519 // When scrolling we had to break the table up - restore it 9520 if ( table != thead.parentNode ) { 9521 jqTable.children('thead').detach(); 9522 jqTable.append( thead ); 9523 } 9524 9525 if ( tfoot && table != tfoot.parentNode ) { 9526 jqTable.children('tfoot').detach(); 9527 jqTable.append( tfoot ); 9528 } 9529 9530 settings.colgroup.remove(); 9531 9532 settings.aaSorting = []; 9533 settings.aaSortingFixed = []; 9534 _fnSortingClasses( settings ); 9535 9536 $('th, td', thead) 9537 .removeClass( 9538 orderClasses.canAsc + ' ' + 9539 orderClasses.canDesc + ' ' + 9540 orderClasses.isAsc + ' ' + 9541 orderClasses.isDesc 9542 ) 9543 .css('width', ''); 9544 9545 // Add the TR elements back into the table in their original order 9546 jqTbody.children().detach(); 9547 jqTbody.append( rows ); 9548 9549 var orig = settings.nTableWrapper.parentNode; 9550 var insertBefore = settings.nTableWrapper.nextSibling; 9551 9552 // Remove the DataTables generated nodes, events and classes 9553 var removedMethod = remove ? 'remove' : 'detach'; 9554 jqTable[ removedMethod ](); 9555 jqWrapper[ removedMethod ](); 9556 9557 // If we need to reattach the table to the document 9558 if ( ! remove && orig ) { 9559 // insertBefore acts like appendChild if !arg[1] 9560 orig.insertBefore( table, insertBefore ); 9561 9562 // Restore the width of the original table - was read from the style property, 9563 // so we can restore directly to that 9564 jqTable 9565 .css( 'width', settings.sDestroyWidth ) 9566 .removeClass( classes.table ); 9567 } 9568 9569 /* Remove the settings object from the settings array */ 9570 var idx = DataTable.settings.indexOf(settings); 9571 if ( idx !== -1 ) { 9572 DataTable.settings.splice( idx, 1 ); 9573 } 9574 } ); 9575 } ); 9576 9577 9578 // Add the `every()` method for rows, columns and cells in a compact form 9579 $.each( [ 'column', 'row', 'cell' ], function ( i, type ) { 9580 _api_register( type+'s().every()', function ( fn ) { 9581 var opts = this.selector.opts; 9582 var api = this; 9583 var inst; 9584 var counter = 0; 9585 9586 return this.iterator( 'every', function ( settings, selectedIdx, tableIdx ) { 9587 inst = api[ type ](selectedIdx, opts); 9588 9589 if (type === 'cell') { 9590 fn.call(inst, inst[0][0].row, inst[0][0].column, tableIdx, counter); 9591 } 9592 else { 9593 fn.call(inst, selectedIdx, tableIdx, counter); 9594 } 9595 9596 counter++; 9597 } ); 9598 } ); 9599 } ); 9600 9601 9602 // i18n method for extensions to be able to use the language object from the 9603 // DataTable 9604 _api_register( 'i18n()', function ( token, def, plural ) { 9605 var ctx = this.context[0]; 9606 var resolved = _fnGetObjectDataFn( token )( ctx.oLanguage ); 9607 9608 if ( resolved === undefined ) { 9609 resolved = def; 9610 } 9611 9612 if ( $.isPlainObject( resolved ) ) { 9613 resolved = plural !== undefined && resolved[ plural ] !== undefined ? 9614 resolved[ plural ] : 9615 resolved._; 9616 } 9617 9618 return typeof resolved === 'string' 9619 ? resolved.replace( '%d', plural ) // nb: plural might be undefined, 9620 : resolved; 9621 } ); 9622 9623 /** 9624 * Version string for plug-ins to check compatibility. Allowed format is 9625 * `a.b.c-d` where: a:int, b:int, c:int, d:string(dev|beta|alpha). `d` is used 9626 * only for non-release builds. See https://semver.org/ for more information. 9627 * @member 9628 * @type string 9629 * @default Version number 9630 */ 9631 DataTable.version = "2.0.8"; 9632 9633 /** 9634 * Private data store, containing all of the settings objects that are 9635 * created for the tables on a given page. 9636 * 9637 * Note that the `DataTable.settings` object is aliased to 9638 * `jQuery.fn.dataTableExt` through which it may be accessed and 9639 * manipulated, or `jQuery.fn.dataTable.settings`. 9640 * @member 9641 * @type array 9642 * @default [] 9643 * @private 9644 */ 9645 DataTable.settings = []; 9646 9647 /** 9648 * Object models container, for the various models that DataTables has 9649 * available to it. These models define the objects that are used to hold 9650 * the active state and configuration of the table. 9651 * @namespace 9652 */ 9653 DataTable.models = {}; 9654 9655 9656 9657 /** 9658 * Template object for the way in which DataTables holds information about 9659 * search information for the global filter and individual column filters. 9660 * @namespace 9661 */ 9662 DataTable.models.oSearch = { 9663 /** 9664 * Flag to indicate if the filtering should be case insensitive or not 9665 */ 9666 "caseInsensitive": true, 9667 9668 /** 9669 * Applied search term 9670 */ 9671 "search": "", 9672 9673 /** 9674 * Flag to indicate if the search term should be interpreted as a 9675 * regular expression (true) or not (false) and therefore and special 9676 * regex characters escaped. 9677 */ 9678 "regex": false, 9679 9680 /** 9681 * Flag to indicate if DataTables is to use its smart filtering or not. 9682 */ 9683 "smart": true, 9684 9685 /** 9686 * Flag to indicate if DataTables should only trigger a search when 9687 * the return key is pressed. 9688 */ 9689 "return": false 9690 }; 9691 9692 9693 9694 9695 /** 9696 * Template object for the way in which DataTables holds information about 9697 * each individual row. This is the object format used for the settings 9698 * aoData array. 9699 * @namespace 9700 */ 9701 DataTable.models.oRow = { 9702 /** 9703 * TR element for the row 9704 */ 9705 "nTr": null, 9706 9707 /** 9708 * Array of TD elements for each row. This is null until the row has been 9709 * created. 9710 */ 9711 "anCells": null, 9712 9713 /** 9714 * Data object from the original data source for the row. This is either 9715 * an array if using the traditional form of DataTables, or an object if 9716 * using mData options. The exact type will depend on the passed in 9717 * data from the data source, or will be an array if using DOM a data 9718 * source. 9719 */ 9720 "_aData": [], 9721 9722 /** 9723 * Sorting data cache - this array is ostensibly the same length as the 9724 * number of columns (although each index is generated only as it is 9725 * needed), and holds the data that is used for sorting each column in the 9726 * row. We do this cache generation at the start of the sort in order that 9727 * the formatting of the sort data need be done only once for each cell 9728 * per sort. This array should not be read from or written to by anything 9729 * other than the master sorting methods. 9730 */ 9731 "_aSortData": null, 9732 9733 /** 9734 * Per cell filtering data cache. As per the sort data cache, used to 9735 * increase the performance of the filtering in DataTables 9736 */ 9737 "_aFilterData": null, 9738 9739 /** 9740 * Filtering data cache. This is the same as the cell filtering cache, but 9741 * in this case a string rather than an array. This is easily computed with 9742 * a join on `_aFilterData`, but is provided as a cache so the join isn't 9743 * needed on every search (memory traded for performance) 9744 */ 9745 "_sFilterRow": null, 9746 9747 /** 9748 * Denote if the original data source was from the DOM, or the data source 9749 * object. This is used for invalidating data, so DataTables can 9750 * automatically read data from the original source, unless uninstructed 9751 * otherwise. 9752 */ 9753 "src": null, 9754 9755 /** 9756 * Index in the aoData array. This saves an indexOf lookup when we have the 9757 * object, but want to know the index 9758 */ 9759 "idx": -1, 9760 9761 /** 9762 * Cached display value 9763 */ 9764 displayData: null 9765 }; 9766 9767 9768 /** 9769 * Template object for the column information object in DataTables. This object 9770 * is held in the settings aoColumns array and contains all the information that 9771 * DataTables needs about each individual column. 9772 * 9773 * Note that this object is related to {@link DataTable.defaults.column} 9774 * but this one is the internal data store for DataTables's cache of columns. 9775 * It should NOT be manipulated outside of DataTables. Any configuration should 9776 * be done through the initialisation options. 9777 * @namespace 9778 */ 9779 DataTable.models.oColumn = { 9780 /** 9781 * Column index. 9782 */ 9783 "idx": null, 9784 9785 /** 9786 * A list of the columns that sorting should occur on when this column 9787 * is sorted. That this property is an array allows multi-column s
9787orting 9788 * to be defined for a column (for example first name / last name columns 9789 * would benefit from this). The values are integers pointing to the 9790 * columns to be sorted on (typically it will be a single integer pointing 9791 * at itself, but that doesn't need to be the case). 9792 */ 9793 "aDataSort": null, 9794 9795 /** 9796 * Define the sorting directions that are applied to the column, in sequence 9797 * as the column is repeatedly sorted upon - i.e. the first value is used 9798 * as the sorting direction when the column if first sorted (clicked on). 9799 * Sort it again (click again) and it will move on to the next index. 9800 * Repeat until loop. 9801 */ 9802 "asSorting": null, 9803 9804 /** 9805 * Flag to indicate if the column is searchable, and thus should be included 9806 * in the filtering or not. 9807 */ 9808 "bSearchable": null, 9809 9810 /** 9811 * Flag to indicate if the column is sortable or not. 9812 */ 9813 "bSortable": null, 9814 9815 /** 9816 * Flag to indicate if the column is currently visible in the table or not 9817 */ 9818 "bVisible": null, 9819 9820 /** 9821 * Store for manual type assignment using the `column.type` option. This 9822 * is held in store so we can manipulate the column's `sType` property. 9823 */ 9824 "_sManualType": null, 9825 9826 /** 9827 * Flag to indicate if HTML5 data attributes should be used as the data 9828 * source for filtering or sorting. True is either are. 9829 */ 9830 "_bAttrSrc": false, 9831 9832 /** 9833 * Developer definable function that is called whenever a cell is created (Ajax source, 9834 * etc) or processed for input (DOM source). This can be used as a compliment to mRender 9835 * allowing you to modify the DOM element (add background colour for example) when the 9836 * element is available. 9837 */ 9838 "fnCreatedCell": null, 9839 9840 /** 9841 * Function to get data from a cell in a column. You should <b>never</b> 9842 * access data directly through _aData internally in DataTables - always use 9843 * the method attached to this property. It allows mData to function as 9844 * required. This function is automatically assigned by the column 9845 * initialisation method 9846 */ 9847 "fnGetData": null, 9848 9849 /** 9850 * Function to set data for a cell in the column. You should <b>never</b> 9851 * set the data directly to _aData internally in DataTables - always use 9852 * this method. It allows mData to function as required. This function 9853 * is automatically assigned by the column initialisation method 9854 */ 9855 "fnSetData": null, 9856 9857 /** 9858 * Property to read the value for the cells in the column from the data 9859 * source array / object. If null, then the default content is used, if a 9860 * function is given then the return from the function is used. 9861 */ 9862 "mData": null, 9863 9864 /** 9865 * Partner property to mData which is used (only when defined) to get 9866 * the data - i.e. it is basically the same as mData, but without the 9867 * 'set' option, and also the data fed to it is the result from mData. 9868 * This is the rendering method to match the data method of mData. 9869 */ 9870 "mRender": null, 9871 9872 /** 9873 * The class to apply to all TD elements in the table's TBODY for the column 9874 */ 9875 "sClass": null, 9876 9877 /** 9878 * When DataTables calculates the column widths to assign to each column, 9879 * it finds the longest string in each column and then constructs a 9880 * temporary table and reads the widths from that. The problem with this 9881 * is that "mmm" is much wider then "iiii", but the latter is a longer 9882 * string - thus the calculation can go wrong (doing it properly and putting 9883 * it into an DOM object and measuring that is horribly(!) slow). Thus as 9884 * a "work around" we provide this option. It will append its value to the 9885 * text that is found to be the longest string for the column - i.e. padding. 9886 */ 9887 "sContentPadding": null, 9888 9889 /** 9890 * Allows a default value to be given for a column's data, and will be used 9891 * whenever a null data source is encountered (this can be because mData 9892 * is set to null, or because the data source itself is null). 9893 */ 9894 "sDefaultContent": null, 9895 9896 /** 9897 * Name for the column, allowing reference to the column by name as well as 9898 * by index (needs a lookup to work by name). 9899 */ 9900 "sName": null, 9901 9902 /** 9903 * Custom sorting data type - defines which of the available plug-ins in 9904 * afnSortData the custom sorting will use - if any is defined. 9905 */ 9906 "sSortDataType": 'std', 9907 9908 /** 9909 * Class to be applied to the header element when sorting on this column 9910 */ 9911 "sSortingClass": null, 9912 9913 /** 9914 * Title of the column - what is seen in the TH element (nTh). 9915 */ 9916 "sTitle": null, 9917 9918 /** 9919 * Column sorting and filtering type 9920 */ 9921 "sType": null, 9922 9923 /** 9924 * Width of the column 9925 */ 9926 "sWidth": null, 9927 9928 /** 9929 * Width of the column when it was first "encountered" 9930 */ 9931 "sWidthOrig": null, 9932 9933 /** Cached string which is the longest in the column */ 9934 maxLenString: null, 9935 9936 /** 9937 * Store for named searches 9938 */ 9939 searchFixed: null 9940 }; 9941 9942 9943 /* 9944 * Developer note: The properties of the object below are given in Hungarian 9945 * notation, that was used as the interface for DataTables prior to v1.10, however 9946 * from v1.10 onwards the primary interface is camel case. In order to avoid 9947 * breaking backwards compatibility utterly with this change, the Hungarian 9948 * version is still, internally the primary interface, but is is not documented 9949 * - hence the @name tags in each doc comment. This allows a Javascript function 9950 * to create a map from Hungarian notation to camel case (going the other direction 9951 * would require each property to be listed, which would add around 3K to the size 9952 * of DataTables, while this method is about a 0.5K hit). 9953 * 9954 * Ultimately this does pave the way for Hungarian notation to be dropped 9955 * completely, but that is a massive amount of work and will break current 9956 * installs (therefore is on-hold until v2). 9957 */ 9958 9959 /** 9960 * Initialisation options that can be given to DataTables at initialisation 9961 * time. 9962 * @namespace 9963 */ 9964 DataTable.defaults = { 9965 /** 9966 * An array of data to use for the table, passed in at initialisation which 9967 * will be used in preference to any data which is already in the DOM. This is 9968 * particularly useful for constructing tables purely in Javascript, for 9969 * example with a custom Ajax call. 9970 */ 9971 "aaData": null, 9972 9973 9974 /** 9975 * If ordering is enabled, then DataTables will perform a first pass sort on 9976 * initialisation. You can define which column(s) the sort is performed 9977 * upon, and the sorting direction, with this variable. The `sorting` array 9978 * should contain an array for each column to be sorted initially containing 9979 * the column's index and a direction string ('asc' or 'desc'). 9980 */ 9981 "aaSorting": [[0,'asc']], 9982 9983 9984 /** 9985 * This parameter is basically identical to the `sorting` parameter, but 9986 * cannot be overridden by user interaction with the table. What this means 9987 * is that you could have a column (visible or hidden) which the sorting 9988 * will always be forced on first - any sorting after that (from the user) 9989 * will then be performed as required. This can be useful for grouping rows 9990 * together. 9991 */ 9992 "aaSortingFixed": [], 9993 9994 9995 /** 9996 * DataTables can be instructed to load data to display in the table from a 9997 * Ajax source. This option defines how that Ajax call is made and where to. 9998 * 9999 * The `ajax` property has three different modes of operation, depending on 10000 * how it is defined. These are: 10001 * 10002 * * `string` - Set the URL from where the data should be loaded from. 10003 * * `object` - Define properties for `jQuery.ajax`. 10004 * * `function` - Custom data get function 10005 * 10006 * `string` 10007 * -------- 10008 * 10009 * As a string, the `ajax` property simply defines the URL from which 10010 * DataTables will load data. 10011 * 10012 * `object` 10013 * -------- 10014 * 10015 * As an object, the parameters in the object are passed to 10016 * [jQuery.ajax](https://api.jquery.com/jQuery.ajax/) allowing fine control
10017 * of the Ajax request. DataTables has a number of default parameters which 10018 * you can override using this option. Please refer to the jQuery 10019 * documentation for a full description of the options available, although 10020 * the following parameters provide additional options in DataTables or 10021 * require special consideration: 10022 * 10023 * * `data` - As with jQuery, `data` can be provided as an object, but it 10024 * can also be used as a function to manipulate the data DataTables sends 10025 * to the server. The function takes a single parameter, an object of 10026 * parameters with the values that DataTables has readied for sending. An 10027 * object may be returned which will be merged into the DataTables 10028 * defaults, or you can add the items to the object that was passed in and 10029 * not return anything from the function. This supersedes `fnServerParams` 10030 * from DataTables 1.9-. 10031 * 10032 * * `dataSrc` - By default DataTables will look for the property `data` (or 10033 * `aaData` for compatibility with DataTables 1.9-) when obtaining data 10034 * from an Ajax source or for server-side processing - this parameter 10035 * allows that property to be changed. You can use Javascript dotted 10036 * object notation to get a data source for multiple levels of nesting, or 10037 * it my be used as a function. As a function it takes a single parameter, 10038 * the JSON returned from the server, which can be manipulated as 10039 * required, with the returned value being that used by DataTables as the 10040 * data source for the table. 10041 * 10042 * * `success` - Should not be overridden it is used internally in 10043 * DataTables. To manipulate / transform the data returned by the server 10044 * use `ajax.dataSrc`, or use `ajax` as a function (see below). 10045 * 10046 * `function` 10047 * ---------- 10048 * 10049 * As a function, making the Ajax call is left up to yourself allowing 10050 * complete control of the Ajax request. Indeed, if desired, a method other 10051 * than Ajax could be used to obtain the required data, such as Web storage 10052 * or an AIR database. 10053 * 10054 * The function is given four parameters and no return is required. The 10055 * parameters are: 10056 * 10057 * 1. _object_ - Data to send to the server 10058 * 2. _function_ - Callback function that must be executed when the required 10059 * data has been obtained. That data should be passed into the callback 10060 * as the only parameter 10061 * 3. _object_ - DataTables settings object for the table 10062 */ 10063 "ajax": null, 10064 10065 10066 /** 10067 * This parameter allows you to readily specify the entries in the length drop 10068 * down menu that DataTables shows when pagination is enabled. It can be 10069 * either a 1D array of options which will be used for both the displayed 10070 * option and the value, or a 2D array which will use the array in the first 10071 * position as the value, and the array in the second position as the 10072 * displayed options (useful for language strings such as 'All'). 10073 * 10074 * Note that the `pageLength` property will be automatically set to the 10075 * first value given in this array, unless `pageLength` is also provided. 10076 */ 10077 "aLengthMenu": [ 10, 25, 50, 100 ], 10078 10079 10080 /** 10081 * The `columns` option in the initialisation parameter allows you to define 10082 * details about the way individual columns behave. For a full list of 10083 * column options that can be set, please see 10084 * {@link DataTable.defaults.column}. Note that if you use `columns` to 10085 * define your columns, you must have an entry in the array for every single 10086 * column that you have in your table (these can be null if you don't which 10087 * to specify any options). 10088 */ 10089 "aoColumns": null, 10090 10091 /** 10092 * Very similar to `columns`, `columnDefs` allows you to target a specific 10093 * column, multiple columns, or all columns, using the `targets` property of 10094 * each object in the array. This allows great flexibility when creating 10095 * tables, as the `columnDefs` arrays can be of any length, targeting the 10096 * columns you specifically want. `columnDefs` may use any of the column 10097 * options available: {@link DataTable.defaults.column}, but it _must_ 10098 * have `targets` defined in each object in the array. Values in the `targets` 10099 * array may be: 10100 * <ul> 10101 * <li>
10101a string - class name will be matched on the TH for the column</li> 10102 * <li>0 or a positive integer - column index counting from the left</li> 10103 * <li>a negative integer - column index counting from the right</li> 10104 * <li>the string "_all" - all columns (i.e. assign a default)</li> 10105 * </ul> 10106 */ 10107 "aoColumnDefs": null, 10108 10109 10110 /** 10111 * Basically the same as `search`, this parameter defines the individual column 10112 * filtering state at initialisation time. The array must be of the same size 10113 * as the number of columns, and each element be an object with the parameters 10114 * `search` and `escapeRegex` (the latter is optional). 'null' is also 10115 * accepted and the default will be used. 10116 */ 10117 "aoSearchCols": [], 10118 10119 10120 /** 10121 * Enable or disable automatic column width calculation. This can be disabled 10122 * as an optimisation (it takes some time to calculate the widths) if the 10123 * tables widths are passed in using `columns`. 10124 */ 10125 "bAutoWidth": true, 10126 10127 10128 /** 10129 * Deferred rendering can provide DataTables with a huge speed boost when you 10130 * are using an Ajax or JS data source for the table. This option, when set to 10131 * true, will cause DataTables to defer the creation of the table elements for 10132 * each row until they are needed for a draw - saving a significant amount of 10133 * time. 10134 */ 10135 "bDeferRender": true, 10136 10137 10138 /** 10139 * Replace a DataTable which matches the given selector and replace it with 10140 * one which has the properties of the new initialisation object passed. If no 10141 * table matches the selector, then the new DataTable will be constructed as 10142 * per normal. 10143 */ 10144 "bDestroy": false, 10145 10146 10147 /** 10148 * Enable or disable filtering of data. Filtering in DataTables is "smart" in 10149 * that it allows the end user to input multiple words (space separated) and 10150 * will match a row containing those words, even if not in the order that was 10151 * specified (this allow matching across multiple columns). Note that if you 10152 * wish to use filtering in DataTables this must remain 'true' - to remove the 10153 * default filtering input box and retain filtering abilities, please use 10154 * {@link DataTable.defaults.dom}. 10155 */ 10156 "bFilter": true, 10157 10158 /** 10159 * Used only for compatiblity with DT1 10160 * @deprecated 10161 */ 10162 "bInfo": true, 10163 10164 /** 10165 * Used only for compatiblity with DT1 10166 * @deprecated 10167 */ 10168 "bLengthChange": true, 10169 10170 /** 10171 * Enable or disable pagination. 10172 */ 10173 "bPaginate": true, 10174 10175 10176 /** 10177 * Enable or disable the display of a 'processing' indicator when the table is 10178 * being processed (e.g. a sort). This is particularly useful for tables with 10179 * large amounts of data where it can take a noticeable amount of time to sort 10180 * the entries. 10181 */ 10182 "bProcessing": false, 10183 10184 10185 /** 10186 * Retrieve the DataTables object for the given selector. Note that if the 10187 * table has already been initialised, this parameter will cause DataTables 10188 * to simply return the object that has already been set up - it will not take 10189 * account of any changes you might have made to the initialisation object 10190 * passed to DataTables (setting this parameter to true is an acknowledgement 10191 * that you understand this). `destroy` can be used to reinitialise a table if 10192 * you need. 10193 */ 10194 "bRetrieve": false, 10195 10196 10197 /** 10198 * When vertical (y) scrolling is enabled, DataTables will force the height of 10199 * the table's viewport to the given height at all times (useful for layout). 10200 * However, this can look odd when filtering data down to a small data set, 10201 * and the footer is left "floating" further down. This parameter (when 10202 * enabled) will cause DataTables to collapse the table's viewport down when 10203 * the result set will fit within the given Y height. 10204 */ 10205 "bScrollCollapse": false, 10206 10207 10208 /** 10209 * Configure DataTables to use server-side processing. Note that the 10210 * `ajax` parameter must also be given in order to give DataTables a 10211 * source to obtain the required data for each draw. 10212 */ 10213 "bServerSide": false, 10214 10215 10216 /** 10217 * Enable or disable sorting of columns. Sorting of individual columns can be 10218 * disabled by the `sortable` option for each column. 10219 */ 10220 "bSort": true, 10221 10222 10223 /** 10224 * Enable or display DataTables' ability to sort multiple columns at the 10225 * same time (activated by shift-click by the user). 10226 */ 10227 "bSortMulti": true, 10228 10229 10230 /** 10231 * Allows control over whether DataTables should use the top (true) unique 10232 * cell that is found for a single column, or the bottom (false - default). 10233 * This is useful when using complex headers. 10234 */ 10235 "bSortCellsTop": null, 10236 10237 10238 /** 10239 * Enable or disable the addition of the classes `sorting\_1`, `sorting\_2` and 10240 * `sorting\_3` to the columns which are currently being sorted on. This is 10241 * presented as a feature switch as it can increase processing time (while 10242 * classes are removed and added) so for large data sets you might want to 10243 * turn this off. 10244 */ 10245 "bSortClasses": true, 10246 10247 10248 /** 10249 * Enable or disable state saving. When enabled HTML5 `localStorage` will be 10250 * used to save table display information such as pagination information, 10251 * display length, filtering and sorting. As such when the end user reloads 10252 * the page the display display will match what thy had previously set up. 10253 */ 10254 "bStateSave": false, 10255 10256 10257 /** 10258 * This function is called when a TR element is created (and all TD child 10259 * elements have been inserted), or registered if using a DOM source, allowing 10260 * manipulation of the TR element (adding classes etc). 10261 */ 10262 "fnCreatedRow": null, 10263 10264 10265 /** 10266 * This function is called on every 'draw' event, and allows you to 10267 * dynamically modify any aspect you want about the created DOM. 10268 */ 10269 "fnDrawCallback": null, 10270 10271 10272 /** 10273 * Identical to fnHeaderCallback() but for the table footer this function 10274 * allows you to modify the table footer on every 'draw' event. 10275 */ 10276 "fnFooterCallback": null, 10277 10278 10279 /** 10280 * When rendering large numbers in the information element for the table 10281 * (i.e. "Showing 1 to 10 of 57 entries") DataTables will render large numbers 10282 * to have a comma separator for the 'thousands' units (e.g. 1 million is 10283 * rendered as "1,000,000") to help readability for the end user. This 10284 * function will override the default method DataTables uses. 10285 */ 10286 "fnFormatNumber": function ( toFormat ) { 10287 return toFormat.toString().replace( 10288 /\B(?=(\d{3})+(?!\d))/g, 10289 this.oLanguage.sThousands 10290 ); 10291 }, 10292 10293 10294 /** 10295 * This function is called on every 'draw' event, and allows you to 10296 * dynamically modify the header row. This can be used to calculate and 10297 * display useful information about the table. 10298 */ 10299 "fnHeaderCallback": null, 10300 10301 10302 /** 10303 * The information element can be used to convey information about the current 10304 * state of the table. Although the internationalisation options presented by 10305 * DataTables are quite capable of dealing with most customisations, there may 10306 * be times where you wish to customise the string further. This callback 10307 * allows you to do exactly that. 10308 */ 10309 "fnInfoCallback": null, 10310 10311 10312 /** 10313 * Called when the table has been initialised. Normally DataTables will 10314 * initialise sequentially and there will be no need for this function, 10315 * however, this does not hold true when using external language information 10316 * since that is obtained using an async XHR call. 10317 */ 10318 "fnInitComplete": null, 10319 10320 10321 /** 10322 * Called at the very start of each table draw and can be used to cancel the 10323 * draw by returning false, any other return (including undefined) results in 10324 * the full draw occurring). 10325 */ 10326 "fnPreDrawCallback": null, 10327 10328 10329 /** 10330 * This function allows you to 'post process' each row after it have been 10331 * generated for each table draw, but before it is rendered on screen. This 10332 * function might be used for setting the row class name etc. 10333 */ 10334 "fnRowCallback": null, 10335 10336 10337 /** 10338 * Load the table state. With this function you can define from where, and how, the 10339 * state of a table is loaded. By default DataTables will load from `localStorage` 10340 * but you might wish to use a server-side database or cookies. 10341 */ 10342 "fnStateLoadCallback": function ( settings ) { 10343 try { 10344 return JSON.parse(
10345 (settings.iStateDuration === -1 ? sessionStorage : localStorage).getItem( 10346 'DataTables_'+settings.sInstance+'_'+location.pathname 10347 ) 10348 ); 10349 } catch (e) { 10350 return {}; 10351 } 10352 }, 10353 10354 10355 /** 10356 * Callback which allows modification of the saved state prior to loading that state. 10357 * This callback is called when the table is loading state from the stored data, but 10358 * prior to the settings object being modified by the saved state. Note that for 10359 * plug-in authors, you should use the `stateLoadParams` event to load parameters for 10360 * a plug-in. 10361 */ 10362 "fnStateLoadParams": null, 10363 10364 10365 /** 10366 * Callback that is called when the state has been loaded from the state saving method 10367 * and the DataTables settings object has been modified as a result of the loaded state. 10368 */ 10369 "fnStateLoaded": null, 10370 10371 10372 /** 10373 * Save the table state. This function allows you to define where and how the state 10374 * information for the table is stored By default DataTables will use `localStorage` 10375 * but you might wish to use a server-side database or cookies. 10376 */ 10377 "fnStateSaveCallback": function ( settings, data ) { 10378 try { 10379 (settings.iStateDuration === -1 ? sessionStorage : localStorage).setItem( 10380 'DataTables_'+settings.sInstance+'_'+location.pathname, 10381 JSON.stringify( data ) 10382 ); 10383 } catch (e) { 10384 // noop 10385 } 10386 }, 10387 10388 10389 /** 10390 * Callback which allows modification of the state to be saved. Called when the table 10391 * has changed state a new state save is required. This method allows modification of 10392 * the state saving object prior to actually doing the save, including addition or 10393 * other state properties or modification. Note that for plug-in authors, you should 10394 * use the `stateSaveParams` event to save parameters for a plug-in. 10395 */ 10396 "fnStateSaveParams": null, 10397 10398 10399 /** 10400 * Duration for which the saved state information is considered valid. After this period 10401 * has elapsed the state will be returned to the default. 10402 * Value is given in seconds. 10403 */ 10404 "iStateDuration": 7200, 10405 10406 10407 /** 10408 * Number of rows to display on a single page when using pagination. If 10409 * feature enabled (`lengthChange`) then the end user will be able to override 10410 * this to a custom setting using a pop-up menu. 10411 */ 10412 "iDisplayLength": 10, 10413 10414 10415 /** 10416 * Define the starting point for data display when using DataTables with 10417 * pagination. Note that this parameter is the number of records, rather than 10418 * the page number, so if you have 10 records per page and want to start on 10419 * the third page, it should be "20". 10420 */ 10421 "iDisplayStart": 0, 10422 10423 10424 /** 10425 * By default DataTables allows keyboard navigation of the table (sorting, paging, 10426 * and filtering) by adding a `tabindex` attribute to the required elements. This 10427 * allows you to tab through the controls and press the enter key to activate them. 10428 * The tabindex is default 0, meaning that the tab follows the flow of the document. 10429 * You can overrule this using this parameter if you wish. Use a value of -1 to 10430 * disable built-in keyboard navigation. 10431 */ 10432 "iTabIndex": 0, 10433 10434 10435 /** 10436 * Classes that DataTables assigns to the various components and features 10437 * that it adds to the HTML table. This allows classes to be configured 10438 * during initialisation in addition to through the static 10439 * {@link DataTable.ext.oStdClasses} object). 10440 */ 10441 "oClasses": {}, 10442 10443 10444 /** 10445 * All strings that DataTables uses in the user interface that it creates 10446 * are defined in this object, allowing you to modified them individually or 10447 * completely replace them all as required. 10448 */ 10449 "oLanguage": { 10450 /** 10451 * Strings that are used for WAI-ARIA labels and controls only (these are not 10452 * actually visible on the page, but will be read by screenreaders, and thus 10453 * must be internationalised as well). 10454 */ 10455 "oAria": { 10456 /** 10457 * ARIA label that is added to the table headers when the column may be sorted 10458 */ 10459 "orderable": ": Activate to sort", 10460 10461 /** 10462 * ARIA label that is added to the table headers when the column is currently being sorted 10463 */ 10464 "orderableReverse": ": Activate to invert sorting", 10465 10466 /** 10467 * ARIA label that is added to the table headers when the column is currently being 10468 * sorted and next step is to remove sorting 10469 */ 10470 "orderableRemove": ": Activate to remove sorting", 10471 10472 paginate: { 10473 first: 'First', 10474 last: 'Last', 10475 next: 'Next', 10476 previous: 'Previous' 10477 } 10478 }, 10479 10480 /** 10481 * Pagination string used by DataTables for the built-in pagination 10482 * control types. 10483 */ 10484 "oPaginate": { 10485 /** 10486 * Label and character for first page button («) 10487 */ 10488 "sFirst": "\u00AB", 10489 10490 /** 10491 * Last page button (») 10492 */ 10493 "sLast": "\u00BB", 10494 10495 /** 10496 * Next page button (âº) 10497 */ 10498 "sNext": "\u203A", 10499 10500 /** 10501 * Previous page button (â¹) 10502 */ 10503 "sPrevious": "\u2039", 10504 }, 10505 10506 /** 10507 * Plural object for the data type the table is showing 10508 */ 10509 entries: { 10510 _: "entries", 10511 1: "entry" 10512 }, 10513 10514 /** 10515 * This string is shown in preference to `zeroRecords` when the table is 10516 * empty of data (regardless of filtering). Note that this is an optional 10517 * parameter - if it is not given, the value of `zeroRecords` will be used 10518 * instead (either the default or given value). 10519 */ 10520 "sEmptyTable": "No data available in table", 10521 10522 10523 /** 10524 * This string gives information to the end user about the information 10525 * that is current on display on the page. The following tokens can be 10526 * used in the string and will be dynamically replaced as the table 10527 * display updates. This tokens can be placed anywhere in the string, or 10528 * removed as needed by the language requires: 10529 * 10530 * * `\_START\_` - Display index of the first record on the current page 10531 * * `\_END\_` - Display index of the last record on the current page 10532 * * `\_TOTAL\_` - Number of records in the table after filtering 10533 * * `\_MAX\_` - Number of records in the table without filtering 10534 * * `\_PAGE\_` - Current page number 10535 * * `\_PAGES\_` - Total number of pages of data in the table 10536 */ 10537 "sInfo": "Showing _START_ to _END_ of _TOTAL_ _ENTRIES-TOTAL_", 10538 10539 10540 /**
10541 * Display information string for when the table is empty. Typically the 10542 * format of this string should match `info`. 10543 */ 10544 "sInfoEmpty": "Showing 0 to 0 of 0 _ENTRIES-TOTAL_", 10545 10546 10547 /** 10548 * When a user filters the information in a table, this string is appended 10549 * to the information (`info`) to give an idea of how strong the filtering 10550 * is. The variable _MAX_ is dynamically updated. 10551 */ 10552 "sInfoFiltered": "(filtered from _MAX_ total _ENTRIES-MAX_)", 10553 10554 10555 /** 10556 * If can be useful to append extra information to the info string at times, 10557 * and this variable does exactly that. This information will be appended to 10558 * the `info` (`infoEmpty` and `infoFiltered` in whatever combination they are 10559 * being used) at all times. 10560 */ 10561 "sInfoPostFix": "", 10562 10563 10564 /** 10565 * This decimal place operator is a little different from the other 10566 * language options since DataTables doesn't output floating point 10567 * numbers, so it won't ever use this for display of a number. Rather, 10568 * what this parameter does is modify the sort methods of the table so 10569 * that numbers which are in a format which has a character other than 10570 * a period (`.`) as a decimal place will be sorted numerically. 10571 * 10572 * Note that numbers with different decimal places cannot be shown in 10573 * the same table and still be sortable, the table must be consistent. 10574 * However, multiple different tables on the page can use different 10575 * decimal place characters. 10576 */ 10577 "sDecimal": "", 10578 10579 10580 /** 10581 * DataTables has a build in number formatter (`formatNumber`) which is 10582 * used to format large numbers that are used in the table information. 10583 * By default a comma is used, but this can be trivially changed to any 10584 * character you wish with this parameter. 10585 */ 10586 "sThousands": ",", 10587 10588 10589 /** 10590 * Detail the action that will be taken when the drop down menu for the 10591 * pagination length option is changed. The '_MENU_' variable is replaced 10592 * with a default select list of 10, 25, 50 and 100, and can be replaced 10593 * with a custom select box if required. 10594 */ 10595 "sLengthMenu": "_MENU_ _ENTRIES_ per page", 10596 10597 10598 /** 10599 * When using Ajax sourced data and during the first draw when DataTables is 10600 * gathering the data, this message is shown in an empty row in the table to 10601 * indicate to the end user the the data is being loaded. Note that this 10602 * parameter is not used when loading data by server-side processing, just 10603 * Ajax sourced data with client-side processing. 10604 */ 10605 "sLoadingRecords": "Loading...", 10606 10607 10608 /** 10609 * Text which is displayed when the table is processing a user action 10610 * (usually a sort command or similar). 10611 */ 10612 "sProcessing": "", 10613 10614 10615 /** 10616 * Details the actions that will be taken when the user types into the 10617 * filtering input text box. The variable "_INPUT_", if used in the string, 10618 * is replaced with the HTML text box for the filtering input allowing 10619 * control over where it appears in the string. If "_INPUT_" is not given 10620 * then the input box is appended to the string automatically. 10621 */ 10622 "sSearch": "Search:", 10623 10624 10625 /** 10626 * Assign a `placeholder` attribute to the search `input` element 10627 * @type string 10628 * @default 10629 * 10630 * @dtopt Language 10631 * @name DataTable.defaults.language.searchPlaceholder 10632 */ 10633 "sSearchPlaceholder": "", 10634 10635 10636 /** 10637 * All of the language information can be stored in a file on the 10638 * server-side, which DataTables will look up if this parameter is passed. 10639 * It must store the URL of the language file, which is in a JSON format, 10640 * and the object has the same properties as the oLanguage object in the 10641 * initialiser object (i.e. the above parameters). Please refer to one of 10642 * the example language files to see how this works in action. 10643 */ 10644 "sUrl": "", 10645 10646 10647 /** 10648 * Text shown inside the table records when the is no information to be 10649 * displayed after filtering. `emptyTable` is shown when there is simply no 10650 * information in the table at all (regardless of filtering). 10651 */
10652 "sZeroRecords": "No matching records found" 10653 }, 10654 10655 10656 /** 10657 * This parameter allows you to have define the global filtering state at 10658 * initialisation time. As an object the `search` parameter must be 10659 * defined, but all other parameters are optional. When `regex` is true, 10660 * the search string will be treated as a regular expression, when false 10661 * (default) it will be treated as a straight string. When `smart` 10662 * DataTables will use it's smart filtering methods (to word match at 10663 * any point in the data), when false this will not be done. 10664 */ 10665 "oSearch": $.extend( {}, DataTable.models.oSearch ), 10666 10667 10668 /** 10669 * Table and control layout. This replaces the legacy `dom` option. 10670 */ 10671 layout: { 10672 topStart: 'pageLength', 10673 topEnd: 'search', 10674 bottomStart: 'info', 10675 bottomEnd: 'paging' 10676 }, 10677 10678 10679 /** 10680 * Legacy DOM layout option 10681 */ 10682 "sDom": null, 10683 10684 10685 /** 10686 * Search delay option. This will throttle full table searches that use the 10687 * DataTables provided search input element (it does not effect calls to 10688 * `dt-api search()`, providing a delay before the search is made. 10689 */ 10690 "searchDelay": null, 10691 10692 10693 /** 10694 * DataTables features six different built-in options for the buttons to 10695 * display for pagination control: 10696 * 10697 * * `numbers` - Page number buttons only 10698 * * `simple` - 'Previous' and 'Next' buttons only 10699 * * 'simple_numbers` - 'Previous' and 'Next' buttons, plus page numbers 10700 * * `full` - 'First', 'Previous', 'Next' and 'Last' buttons 10701 * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus page numbers 10702 * * `first_last_numbers` - 'First' and 'Last' buttons, plus page numbers 10703 */ 10704 "sPaginationType": "full_numbers", 10705 10706 10707 /** 10708 * Enable horizontal scrolling. When a table is too wide to fit into a 10709 * certain layout, or you have a large number of columns in the table, you 10710 * can enable x-scrolling to show the table in a viewport, which can be 10711 * scrolled. This property can be `true` which will allow the table to 10712 * scroll horizontally when needed, or any CSS unit, or a number (in which 10713 * case it will be treated as a pixel measurement). Setting as simply `true` 10714 * is recommended. 10715 */ 10716 "sScrollX": "", 10717 10718 10719 /** 10720 * This property can be used to force a DataTable to use more width than it 10721 * might otherwise do when x-scrolling is enabled. For example if you have a 10722 * table which requires to be well spaced, this parameter is useful for 10723 * "over-sizing" the table, and thus forcing scrolling. This property can by 10724 * any CSS unit, or a number (in which case it will be treated as a pixel 10725 * measurement). 10726 */ 10727 "sScrollXInner": "", 10728 10729 10730 /** 10731 * Enable vertical scrolling. Vertical scrolling will constrain the DataTable 10732 * to the given height, and enable scrolling for any data which overflows the 10733 * current viewport. This can be used as an alternative to paging to display 10734 * a lot of data in a small area (although paging and scrolling can both be 10735 * enabled at the same time). This property can be any CSS unit, or a number 10736 * (in which case it will be treated as a pixel measurement). 10737 */ 10738 "sScrollY": "", 10739 10740 10741 /** 10742 * __Deprecated__ The functionality provided by this parameter has now been 10743 * superseded by that provided through `ajax`, which should be used instead. 10744 * 10745 * Set the HTTP method that is used to make the Ajax call for server-side 10746 * processing or Ajax sourced data. 10747 */ 10748 "sServerMethod": "GET", 10749 10750 10751 /** 10752 * DataTables makes use of renderers when displaying HTML elements for 10753 * a table. These renderers can be added or modified by plug-ins to 10754 * generate suitable mark-up for a site. For example the Bootstrap 10755 * integration plug-in for DataTables uses a paging button renderer to 10756 * display pagination buttons in the mark-up required by Bootstrap. 10757 * 10758 * For further information about the renderers available see 10759 * DataTable.ext.renderer 10760 */ 10761 "renderer": null, 10762 10763 10764 /** 10765 * Set the data property name that DataTables should use to get a row's id 10766 * to set as the `id` property in the node. 10767 */ 10768 "rowId": "DT_RowId", 10769 10770 10771 /** 10772 * Caption value 10773 */ 10774 "caption": null 10775 }; 10776 10777 _fnHungarianMap( DataTable.defaults ); 10778 10779 10780 10781 /* 10782 * Developer note - See note in model.defaults.js about the use of Hungarian 10783 * notation and camel case. 10784 */ 10785 10786 /** 10787 * Column options that can be given to DataTables at initialisation time. 10788 * @namespace 10789 */ 10790 DataTable.defaults.column = { 10791 /** 10792 * Define which column(s) an order will occur on for this column. This 10793 * allows a column's ordering to take multiple columns into account when
10794 * doing a sort or use the data from a different column. For example first 10795 * name / last name columns make sense to do a multi-column sort over the 10796 * two columns. 10797 */ 10798 "aDataSort": null, 10799 "iDataSort": -1, 10800 10801 ariaTitle: '', 10802 10803 10804 /** 10805 * You can control the default ordering direction, and even alter the 10806 * behaviour of the sort handler (i.e. only allow ascending ordering etc) 10807 * using this parameter. 10808 */ 10809 "asSorting": [ 'asc', 'desc', '' ], 10810 10811 10812 /** 10813 * Enable or disable filtering on the data in this column. 10814 */ 10815 "bSearchable": true, 10816 10817 10818 /** 10819 * Enable or disable ordering on this column. 10820 */ 10821 "bSortable": true, 10822 10823 10824 /** 10825 * Enable or disable the display of this column. 10826 */ 10827 "bVisible": true, 10828 10829 10830 /** 10831 * Developer definable function that is called whenever a cell is created (Ajax source, 10832 * etc) or processed for input (DOM source). This can be used as a compliment to mRender 10833 * allowing you to modify the DOM element (add background colour for example) when the 10834 * element is available. 10835 */ 10836 "fnCreatedCell": null, 10837 10838 10839 /** 10840 * This property can be used to read data from any data source property, 10841 * including deeply nested objects / properties. `data` can be given in a 10842 * number of different ways which effect its behaviour: 10843 * 10844 * * `integer` - treated as an array index for the data source. This is the 10845 * default that DataTables uses (incrementally increased for each column). 10846 * * `string` - read an object property from the data source. There are 10847 * three 'special' options that can be used in the string to alter how 10848 * DataTables reads the data from the source object: 10849 * * `.` - Dotted Javascript notation. Just as you use a `.` in 10850 * Javascript to read from nested objects, so to can the options 10851 * specified in `data`. For example: `browser.version` or 10852 * `browser.name`. If your object parameter name contains a period, use 10853 * `\\` to escape it - i.e. `first\\.name`. 10854 * * `[]` - Array notation. DataTables can automatically combine data 10855 * from and array source, joining the data with the characters provided 10856 * between the two brackets. For example: `name[, ]` would provide a 10857 * comma-space separated list from the source array. If no characters 10858 * are provided between the brackets, the original array source is 10859 * returned. 10860 * * `()` - Function notation. Adding `()` to the end of a parameter will 10861 * execute a function of the name given. For example: `browser()` for a 10862 * simple function on the data source, `browser.version()` for a 10863 * function in a nested property or even `browser().version` to get an 10864 * object property if the function called returns an object. Note that 10865 * function notation is recommended for use in `render` rather than 10866 * `data` as it is much simpler to use as a renderer. 10867 * * `null` - use the original data source for the row rather than plucking 10868 * data directly from it. This action has effects on two other 10869 * initialisation options: 10870 * * `defaultContent` - When null is given as the `data` option and 10871 * `defaultContent` is specified for the column, the value defined by 10872 * `defaultContent` will be used for the cell. 10873 * * `render` - When null is used for the `data` option and the `render` 10874 * option is specified for the column, the whole data source for the 10875 * row is used for the renderer. 10876 * * `function` - the function given will be executed whenever DataTables 10877 * needs to set or get the data for a cell in the column. The function 10878 * takes three parameters: 10879 * * Parameters: 10880 * * `{array|object}` The data source for the row 10881 * * `{string}` The type call data requested - this will be 'set' when 10882 * setting data or 'filter', 'display', 'type', 'sort' or undefined 10883 * when gathering data. Note that when `undefined` is given for the 10884 * type DataTables expects to get the raw data for the object back< 10885 * * `{*}` Data to set when the second parameter is 'set'. 10886 * * Return: 10887 * * The return value from the function is not required when '
10887set' is 10888 * the type of call, but otherwise the return is what will be used 10889 * for the data requested. 10890 * 10891 * Note that `data` is a getter and setter option. If you just require 10892 * formatting of data for output, you will likely want to use `render` which 10893 * is simply a getter and thus simpler to use. 10894 * 10895 * Note that prior to DataTables 1.9.2 `data` was called `mDataProp`. The 10896 * name change reflects the flexibility of this property and is consistent 10897 * with the naming of mRender. If 'mDataProp' is given, then it will still 10898 * be used by DataTables, as it automatically maps the old name to the new 10899 * if required. 10900 */ 10901 "mData": null, 10902 10903 10904 /** 10905 * This property is the rendering partner to `data` and it is suggested that 10906 * when you want to manipulate data for display (including filtering, 10907 * sorting etc) without altering the underlying data for the table, use this 10908 * property. `render` can be considered to be the the read only companion to 10909 * `data` which is read / write (then as such more complex). Like `data` 10910 * this option can be given in a number of different ways to effect its 10911 * behaviour: 10912 * 10913 * * `integer` - treated as an array index for the data source. This is the 10914 * default that DataTables uses (incrementally increased for each column). 10915 * * `string` - read an object property from the data source. There are 10916 * three 'special' options that can be used in the string to alter how 10917 * DataTables reads the data from the source object: 10918 * * `.` - Dotted Javascript notation. Just as you use a `.` in 10919 * Javascript to read from nested objects, so to can the options 10920 * specified in `data`. For example: `browser.version` or 10921 * `browser.name`. If your object parameter name contains a period, use 10922 * `\\` to escape it - i.e. `first\\.name`. 10923 * * `[]` - Array notation. DataTables can automatically combine data 10924 * from and array source, joining the data with the characters provided 10925 * between the two brackets. For example: `name[, ]` would provide a 10926 * comma-space separated list from the source array. If no characters 10927 * are provided between the brackets, the original array source is 10928 * returned. 10929 * * `()` - Function notation. Adding `()` to the end of a parameter will 10930 * execute a function of the name given. For example: `browser()` for a 10931 * simple function on the data source, `browser.version()` for a 10932 * function in a nested property or even `browser().version` to get an 10933 * object property if the function called returns an object. 10934 * * `object` - use different data for the different data types requested by 10935 * DataTables ('filter', 'display', 'type' or 'sort'). The property names 10936 * of the object is the data type the property refers to and the value can 10937 * defined using an integer, string or function using the same rules as 10938 * `render` normally does. Note that an `_` option _must_ be specified. 10939 * This is the default value to use if you haven't specified a value for 10940 * the data type requested by DataTables. 10941 * * `function` - the function given will be executed whenever DataTables 10942 * needs to set or get the data for a cell in the column. The function 10943 * takes three parameters: 10944 * * Parameters: 10945 * * {array|object} The data source for the row (based on `data`) 10946 * * {string} The type call data requested - this will be 'filter', 10947 * 'display', 'type' or 'sort'. 10948 * * {array|object} The full data source for the row (not based on 10949 * `data`) 10950 * * Return: 10951 * * The return value from the function is what will be used for the 10952 * data requested. 10953 */ 10954 "mRender": null, 10955 10956 10957 /** 10958 * Change the cell type created for the column - either TD cells or TH cells. This 10959 * can be useful as TH cells have semantic meaning in the table body, allowing them 10960 * to act as a header for a row (you may wish to add scope='row' to the TH elements). 10961 */ 10962 "sCellType": "td", 10963 10964 10965 /** 10966 * Class to give to each cell in this column. 10967 */ 10968 "sClass": "", 10969 10970 /** 10971 * When DataTables calculates the column widths to assign to each column, 10972 * it finds the longest string in each column and then constructs a 10973 * temporary table and reads the widths from that. The problem with this 10974 * is that "mmm" is much wider then "iiii", but the latter is a longer 10975 * string - thus the calculation can go wrong (doing it properly and putting 10976 * it into an DOM object and measuring that is horribly(!) slow). Thus as 10977 * a "work around" we provide this option. It will append its value to the 10978 * text that is found to be the longest string for the column - i.e. padding. 10979 * Generally you shouldn't need this! 10980 */ 10981 "sContentPadding": "", 10982 10983 10984 /** 10985 * Allows a default value to be given for a column's data, and will be used 10986 * whenever a null data source is encountered (this can be because `data` 10987 * is set to null, or because the data source itself is null). 10988 */ 10989 "sDefaultContent": null, 10990 10991 10992 /** 10993 * This parameter is only used in DataTables' server-side processing. It can 10994 * be exceptionally useful to know what columns are being displayed on the 10995 * client side, and to map these to database fields. When defined, the names 10996 * also allow DataTables to reorder information from the server if it comes 10997 * back in an unexpected order (i.e. if you switch your columns around on the 10998 * client-side, your server-side code does not also need updating). 10999 */ 11000 "sName": "", 11001 11002 11003 /** 11004 * Defines a data source type for the ordering which can be used to read 11005 * real-time information from the table (updating the internally cached 11006 * version) prior to ordering. This allows ordering to occur on user 11007 * editable elements such as form inputs. 11008 */ 11009 "sSortDataType": "std", 11010 11011 11012 /** 11013 * The title of this column. 11014 */ 11015 "sTitle": null, 11016 11017 11018 /** 11019 * The type allows you to specify how the data for this column will be
11020 * ordered. Four types (string, numeric, date and html (which will strip 11021 * HTML tags before ordering)) are currently available. Note that only date 11022 * formats understood by Javascript's Date() object will be accepted as type 11023 * date. For example: "Mar 26, 2008 5:03 PM". May take the values: 'string', 11024 * 'numeric', 'date' or 'html' (by default). Further types can be adding 11025 * through plug-ins. 11026 */ 11027 "sType": null, 11028 11029 11030 /** 11031 * Defining the width of the column, this parameter may take any CSS value 11032 * (3em, 20px etc). DataTables applies 'smart' widths to columns which have not 11033 * been given a specific width through this interface ensuring that the table 11034 * remains readable. 11035 */ 11036 "sWidth": null 11037 }; 11038 11039 _fnHungarianMap( DataTable.defaults.column ); 11040 11041 11042 11043 /** 11044 * DataTables settings object - this holds all the information needed for a 11045 * given table, including configuration, data and current application of the 11046 * table options. DataTables does not have a single instance for each DataTable 11047 * with the settings attached to that instance, but rather instances of the 11048 * DataTable "class" are created on-the-fly as needed (typically by a 11049 * $().dataTable() call) and the settings object is then applied to that 11050 * instance. 11051 * 11052 * Note that this object is related to {@link DataTable.defaults} but this 11053 * one is the internal data store for DataTables's cache of columns. It should 11054 * NOT be manipulated outside of DataTables. Any configuration should be done 11055 * through the initialisation options. 11056 */ 11057 DataTable.models.oSettings = { 11058 /** 11059 * Primary features of DataTables and their enablement state. 11060 */ 11061 "oFeatures": { 11062 11063 /** 11064 * Flag to say if DataTables should automatically try to calculate the 11065 * optimum table and columns widths (true) or not (false). 11066 * Note that this parameter will be set by the initialisation routine. To 11067 * set a default use {@link DataTable.defaults}. 11068 */ 11069 "bAutoWidth": null, 11070 11071 /** 11072 * Delay the creation of TR and TD elements until they are actually 11073 * needed by a driven page draw. This can give a significant speed 11074 * increase for Ajax source and Javascript source data, but makes no 11075 * difference at all for DOM and server-side processing tables. 11076 * Note that this parameter will be set by the initialisation routine. To 11077 * set a default use {@link DataTable.defaults}. 11078 */ 11079 "bDeferRender": null, 11080 11081 /** 11082 * Enable filtering on the table or not. Note that if this is disabled 11083 * then there is no filtering at all on the table, including fnFilter. 11084 * To just remove the filtering input use sDom and remove the 'f' option. 11085 * Note that this parameter will be set by the initialisation routine. To 11086 * set a default use {@link DataTable.defaults}. 11087 */ 11088 "bFilter": null, 11089 11090 /** 11091 * Used only for compatiblity with DT1 11092 * @deprecated 11093 */ 11094 "bInfo": true, 11095 11096 /** 11097 * Used only for compatiblity with DT1 11098 * @deprecated 11099 */ 11100 "bLengthChange": true, 11101 11102 /** 11103 * Pagination enabled or not. Note that if this is disabled then length 11104 * changing must also be disabled. 11105 * Note that this parameter will be set by the initialisation routine. To 11106 * set a default use {@link DataTable.defaults}. 11107 */ 11108 "bPaginate": null, 11109 11110 /** 11111 * Processing indicator enable flag whenever DataTables is enacting a 11112 * user request - typically an Ajax request for server-side processing. 11113 * Note that this parameter will be set by the initialisation routine. To 11114 * set a default use {@link DataTable.defaults}. 11115 */ 11116 "bProcessing": null, 11117 11118 /** 11119 * Server-side processing enabled flag - when enabled DataTables will 11120 * get all data from the server for every draw - there is no filtering, 11121 * sorting or paging done on the client-side. 11122 * Note that this parameter will be set by the initialisation routine. To 11123 * set a default use {@link DataTable.defaults}. 11124 */ 11125 "bServerSide": null, 11126 11127 /** 11128 * Sorting enablement flag. 11129 * Note that this parameter will be set by the initialisation routine. To 11130 * set a default use {@link DataTable.defaults}. 11131 */ 11132 "bSort": null, 11133 11134 /**
11135 * Multi-column sorting 11136 * Note that this parameter will be set by the initialisation routine. To 11137 * set a default use {@link DataTable.defaults}. 11138 */ 11139 "bSortMulti": null, 11140 11141 /** 11142 * Apply a class to the columns which are being sorted to provide a 11143 * visual highlight or not. This can slow things down when enabled since 11144 * there is a lot of DOM interaction. 11145 * Note that this parameter will be set by the initialisation routine. To 11146 * set a default use {@link DataTable.defaults}. 11147 */ 11148 "bSortClasses": null, 11149 11150 /** 11151 * State saving enablement flag. 11152 * Note that this parameter will be set by the initialisation routine. To 11153 * set a default use {@link DataTable.defaults}. 11154 */ 11155 "bStateSave": null 11156 }, 11157 11158 11159 /** 11160 * Scrolling settings for a table. 11161 */ 11162 "oScroll": { 11163 /** 11164 * When the table is shorter in height than sScrollY, collapse the 11165 * table container down to the height of the table (when true). 11166 * Note that this parameter will be set by the initialisation routine. To 11167 * set a default use {@link DataTable.defaults}. 11168 */ 11169 "bCollapse": null, 11170 11171 /** 11172 * Width of the scrollbar for the web-browser's platform. Calculated 11173 * during table initialisation. 11174 */ 11175 "iBarWidth": 0, 11176 11177 /** 11178 * Viewport width for horizontal scrolling. Horizontal scrolling is 11179 * disabled if an empty string. 11180 * Note that this parameter will be set by the initialisation routine. To 11181 * set a default use {@link DataTable.defaults}. 11182 */ 11183 "sX": null, 11184 11185 /** 11186 * Width to expand the table to when using x-scrolling. Typically you 11187 * should not need to use this. 11188 * Note that this parameter will be set by the initialisation routine. To 11189 * set a default use {@link DataTable.defaults}. 11190 * @deprecated 11191 */ 11192 "sXInner": null, 11193 11194 /** 11195 * Viewport height for vertical scrolling. Vertical scrolling is disabled 11196 * if an empty string. 11197 * Note that this parameter will be set by the initialisation routine. To 11198 * set a default use {@link DataTable.defaults}. 11199 */ 11200 "sY": null 11201 }, 11202 11203 /** 11204 * Language information for the table. 11205 */ 11206 "oLanguage": { 11207 /** 11208 * Information callback function. See 11209 * {@link DataTable.defaults.fnInfoCallback} 11210 */ 11211 "fnInfoCallback": null 11212 }, 11213 11214 /** 11215 * Browser support parameters 11216 */ 11217 "oBrowser": { 11218 /** 11219 * Determine if the vertical scrollbar is on the right or left of the 11220 * scrolling container - needed for rtl language layout, although not 11221 * all browsers move the scrollbar (Safari). 11222 */ 11223 "bScrollbarLeft": false, 11224 11225 /** 11226 * Browser scrollbar width 11227 */ 11228 "barWidth": 0 11229 }, 11230 11231 11232 "ajax": null, 11233 11234 11235 /** 11236 * Array referencing the nodes which are used for the features. The 11237 * parameters of this object match what is allowed by sDom - i.e. 11238 * <ul> 11239 * <li>'l' - Length changing</li> 11240 * <li>'f' - Filtering input</li> 11241 * <li>'t' - The table!</li> 11242 * <li>'i' - Information</li> 11243 * <li>'p' - Pagination</li> 11244 * <li>'r' - pRocessing</li> 11245 * </ul> 11246 */ 11247 "aanFeatures": [], 11248 11249 /** 11250 * Store data information - see {@link DataTable.models.oRow} for detailed 11251 * information. 11252 */ 11253 "aoData": [], 11254 11255 /** 11256 * Array of indexes which are in the current display (after filtering etc) 11257 */ 11258 "aiDisplay": [], 11259 11260 /** 11261 * Array of indexes for display - no filtering 11262 */ 11263 "aiDisplayMaster": [], 11264 11265 /** 11266 * Map of row ids to data indexes 11267 */ 11268 "aIds": {}, 11269 11270 /** 11271 * Store information about each column that is in use 11272 */ 11273 "aoColumns": [], 11274 11275 /** 11276 * Store information about the table's header 11277 */ 11278 "aoHeader": [], 11279 11280 /** 11281 * Store information about the table's footer 11282 */ 11283 "aoFooter": [], 11284 11285 /** 11286 * Store the applied global search information in case we want to force a 11287 * research or compare the old search to a new one. 11288 * Note that this parameter will be set by the initialisation routine. To 11289 * set a default use {@link DataTable.defaults}. 11290 */ 11291 "oPreviousSearch": {}, 11292 11293 /** 11294 * Store for named searches 11295 */ 11296 searchFixed: {}, 11297 11298 /** 11299 * Store the applied search for each column - see 11300 * {@link DataTable.models.oSearch} for the format that is used for the 11301 * filtering information for each column. 11302 */ 11303 "aoPreSearchCols": [], 11304 11305 /** 11306 * Sorting that is applied to the table. Note that the inner arrays are 11307 * used in the following manner: 11308 * <ul> 11309 * <li>Index 0 - column number</li> 11310 * <li>Index 1 - current sorting direction</li> 11311 * </ul> 11312 * Note that this parameter will be set by the initialisation routine. To 11313 * set a default use {@link DataTable.defaults}. 11314 */ 11315 "aaSorting": null, 11316 11317 /** 11318 * Sorting that is always applied to the table (i.e. prefixed in front of 11319 * aaSorting). 11320 * Note that this parameter will be set by the initialisation routine. To 11321 * set a default use {@link DataTable.defaults}. 11322 */ 11323 "aaSortingFixed": [], 11324 11325 /** 11326 * If restoring a table - we should restore its width 11327 */ 11328 "sDestroyWidth": 0, 11329 11330 /** 11331 * Callback functions array for every time a row is inserted (i.e. on a draw). 11332 */ 11333 "aoRowCallback": [], 11334 11335 /** 11336 * Callback functions for the header on each draw. 11337 */ 11338 "aoHeaderCallback": [], 11339 11340 /** 11341 * Callback function for the footer on each draw. 11342 */ 11343 "aoFooterCallback": [], 11344 11345 /** 11346 * Array of callback functions for draw callback functions 11347 */ 11348 "aoDrawCallback": [], 11349 11350 /** 11351 * Array of callback functions for row created function 11352 */ 11353 "aoRowCreatedCallback": [], 11354 11355 /** 11356 * Callback functions for just before the table is redrawn. A return of 11357 * false will be used to cancel the draw. 11358 */ 11359 "aoPreDrawCallback": [], 11360 11361 /** 11362 * Callback functions for when the table has been initialised. 11363 */ 11364 "aoInitComplete": [], 11365 11366 11367 /** 11368 * Callbacks for modifying the settings to be stored for state saving, prior to 11369 * saving state. 11370 */ 11371 "aoStateSaveParams": [], 11372 11373 /** 11374 * Callbacks for modifying the settings that have been stored for state saving 11375 * prior to using the stored values to restore the state. 11376 */ 11377 "aoStateLoadParams": [], 11378 11379 /** 11380 * Callbacks for operating on the settings object once the saved state has been 11381 * loaded 11382 */ 11383 "aoStateLoaded": [], 11384 11385 /** 11386 * Cache the table ID for quick access 11387 */ 11388 "sTableId": "", 11389 11390 /** 11391 * The TABLE node for the main table 11392 */ 11393 "nTable": null, 11394 11395 /** 11396 * Permanent ref to the thead element 11397 */ 11398 "nTHead": null, 11399 11400 /** 11401 * Permanent ref to the tfoot element - if it exists 11402 */ 11403 "nTFoot": null, 11404 11405 /** 11406 * Permanent ref to the tbody element 11407 */ 11408 "nTBody": null, 11409 11410 /** 11411 * Cache the wrapper node (contains all DataTables controlled elements) 11412 */ 11413 "nTableWrapper": null, 11414 11415 /** 11416 * Indicate if all required information has been read in 11417 */ 11418 "bInitialised": false, 11419 11420 /** 11421 * Information about open rows. Each object in the array has the parameters 11422 * 'nTr' and 'nParent' 11423 */ 11424 "aoOpenRows": [], 11425 11426 /**
11427 * Dictate the positioning of DataTables' control elements - see 11428 * {@link DataTable.model.oInit.sDom}. 11429 * Note that this parameter will be set by the initialisation routine. To 11430 * set a default use {@link DataTable.defaults}. 11431 */ 11432 "sDom": null, 11433 11434 /** 11435 * Search delay (in mS) 11436 */ 11437 "searchDelay": null, 11438 11439 /** 11440 * Which type of pagination should be used. 11441 * Note that this parameter will be set by the initialisation routine. To 11442 * set a default use {@link DataTable.defaults}. 11443 */ 11444 "sPaginationType": "two_button", 11445 11446 /** 11447 * Number of paging controls on the page. Only used for backwards compatibility 11448 */ 11449 pagingControls: 0, 11450 11451 /** 11452 * The state duration (for `stateSave`) in seconds. 11453 * Note that this parameter will be set by the initialisation routine. To 11454 * set a default use {@link DataTable.defaults}. 11455 */ 11456 "iStateDuration": 0, 11457 11458 /** 11459 * Array of callback functions for state saving. Each array element is an 11460 * object with the following parameters: 11461 * <ul> 11462 * <li>function:fn - function to call. Takes two parameters, oSettings 11463 * and the JSON string to save that has been thus far created. Returns 11464 * a JSON string to be inserted into a json object 11465 * (i.e. '"param": [ 0, 1, 2]')</li> 11466 * <li>string:sName - name of callback</li> 11467 * </ul> 11468 */ 11469 "aoStateSave": [], 11470 11471 /** 11472 * Array of callback functions for state loading. Each array element is an 11473 * object with the following parameters: 11474 * <ul> 11475 * <li>function:fn - function to call. Takes two parameters, oSettings 11476 * and the object stored. May return false to cancel state loading</li> 11477 * <li>string:sName - name of callback</li> 11478 * </ul> 11479 */ 11480 "aoStateLoad": [], 11481 11482 /** 11483 * State that was saved. Useful for back reference 11484 */ 11485 "oSavedState": null, 11486 11487 /** 11488 * State that was loaded. Useful for back reference 11489 */ 11490 "oLoadedState": null, 11491 11492 /** 11493 * Note if draw should be blocked while getting data 11494 */ 11495 "bAjaxDataGet": true, 11496 11497 /** 11498 * The last jQuery XHR object that was used for server-side data gathering. 11499 * This can be used for working with the XHR information in one of the 11500 * callbacks 11501 */ 11502 "jqXHR": null, 11503 11504 /** 11505 * JSON returned from the server in the last Ajax request 11506 */ 11507 "json": undefined, 11508 11509 /** 11510 * Data submitted as part of the last Ajax request 11511 */ 11512 "oAjaxData": undefined, 11513 11514 /** 11515 * Send the XHR HTTP method - GET or POST (could be PUT or DELETE if 11516 * required). 11517 * Note that this parameter will be set by the initialisation routine. To 11518 * set a default use {@link DataTable.defaults}. 11519 */ 11520 "sServerMethod": null, 11521 11522 /** 11523 * Format numbers for display. 11524 * Note that this parameter will be set by the initialisation routine. To 11525 * set a default use {@link DataTable.defaults}. 11526 */ 11527 "fnFormatNumber": null, 11528 11529 /** 11530 * List of options that can be used for the user selectable length menu. 11531 * Note that this parameter will be set by the initialisation routine. To 11532 * set a default use {@link DataTable.defaults}. 11533 */ 11534 "aLengthMenu": null, 11535 11536 /** 11537 * Counter for the draws that the table does. Also used as a tracker for 11538 * server-side processing 11539 */ 11540 "iDraw": 0, 11541 11542 /** 11543 * Indicate if a redraw is being done - useful for Ajax 11544 */ 11545 "bDrawing": false, 11546 11547 /** 11548 * Draw index (iDraw) of the last error when parsing the returned data 11549 */ 11550 "iDrawError": -1, 11551 11552 /** 11553 * Paging display length 11554 */ 11555 "_iDisplayLength": 10, 11556 11557 /** 11558 * Paging start point - aiDisplay index 11559 */ 11560 "_iDisplayStart": 0, 11561 11562 /** 11563 * Server-side processing - number of records in the result set 11564 * (i.e. before filtering), Use fnRecordsTotal rather than 11565 * this property to get the value of the number of records, regardless of 11566 * the server-side processing setting. 11567 */ 11568 "_iRecordsTotal": 0, 11569 11570 /** 11571 * Server-side processing - number of records in the current display set 11572 * (i.e. after filtering). Use fnRecordsDisplay rather than 11573 * this property to get the value of the number of records, regardless of 11574 * the server-side processing setting. 11575 */ 11576 "_iRecordsDisplay": 0, 11577 11578 /** 11579 * The classes to use for the table 11580 */
11581 "oClasses": {}, 11582 11583 /** 11584 * Flag attached to the settings object so you can check in the draw 11585 * callback if filtering has been done in the draw. Deprecated in favour of 11586 * events. 11587 * @deprecated 11588 */ 11589 "bFiltered": false, 11590 11591 /** 11592 * Flag attached to the settings object so you can check in the draw 11593 * callback if sorting has been done in the draw. Deprecated in favour of 11594 * events. 11595 * @deprecated 11596 */ 11597 "bSorted": false, 11598 11599 /** 11600 * Indicate that if multiple rows are in the header and there is more than 11601 * one unique cell per column, if the top one (true) or bottom one (false) 11602 * should be used for sorting / title by DataTables. 11603 * Note that this parameter will be set by the initialisation routine. To 11604 * set a default use {@link DataTable.defaults}. 11605 */ 11606 "bSortCellsTop": null, 11607 11608 /** 11609 * Initialisation object that is used for the table 11610 */ 11611 "oInit": null, 11612 11613 /** 11614 * Destroy callback functions - for plug-ins to attach themselves to the 11615 * destroy so they can clean up markup and events. 11616 */ 11617 "aoDestroyCallback": [], 11618 11619 11620 /** 11621 * Get the number of records in the current record set, before filtering 11622 */ 11623 "fnRecordsTotal": function () 11624 { 11625 return _fnDataSource( this ) == 'ssp' ? 11626 this._iRecordsTotal * 1 : 11627 this.aiDisplayMaster.length; 11628 }, 11629 11630 /** 11631 * Get the number of records in the current record set, after filtering 11632 */ 11633 "fnRecordsDisplay": function () 11634 { 11635 return _fnDataSource( this ) == 'ssp' ? 11636 this._iRecordsDisplay * 1 : 11637 this.aiDisplay.length; 11638 }, 11639 11640 /** 11641 * Get the display end point - aiDisplay index 11642 */ 11643 "fnDisplayEnd": function () 11644 { 11645 var 11646 len = this._iDisplayLength, 11647 start = this._iDisplayStart, 11648 calc = start + len, 11649 records = this.aiDisplay.length, 11650 features = this.oFeatures, 11651 paginate = features.bPaginate; 11652 11653 if ( features.bServerSide ) { 11654 return paginate === false || len === -1 ? 11655 start + records : 11656 Math.min( start+len, this._iRecordsDisplay ); 11657 } 11658 else { 11659 return ! paginate || calc>records || len===-1 ? 11660 records : 11661 calc; 11662 } 11663 }, 11664 11665 /** 11666 * The DataTables object for this table 11667 */ 11668 "oInstance": null, 11669 11670 /** 11671 * Unique identifier for each instance of the DataTables object. If there 11672 * is an ID on the table node, then it takes that value, otherwise an 11673 * incrementing internal counter is used. 11674 */ 11675 "sInstance": null, 11676 11677 /** 11678 * tabindex attribute value that is added to DataTables control elements, allowing 11679 * keyboard navigation of the table and its controls. 11680 */ 11681 "iTabIndex": 0, 11682 11683 /** 11684 * DIV container for the footer scrolling table if scrolling 11685 */ 11686 "nScrollHead": null, 11687 11688 /** 11689 * DIV container for the footer scrolling table if scrolling 11690 */ 11691 "nScrollFoot": null, 11692 11693 /** 11694 * Last applied sort 11695 */ 11696 "aLastSort": [], 11697 11698 /** 11699 * Stored plug-in instances 11700 */ 11701 "oPlugins": {}, 11702 11703 /** 11704 * Function used to get a row's id from the row's data 11705 */ 11706 "rowIdFn": null, 11707 11708 /** 11709 * Data location where to store a row's id 11710 */ 11711 "rowId": null, 11712 11713 caption: '', 11714 11715 captionNode: null, 11716 11717 colgroup: null 11718 }; 11719 11720 /** 11721 * Extension object for DataTables that is used to provide all extension 11722 * options. 11723 * 11724 * Note that the `DataTable.ext` object is available through 11725 * `jQuery.fn.dataTable.ext` where it may be accessed and manipulated. It is 11726 * also aliased to `jQuery.fn.dataTableExt` for historic reasons. 11727 * @namespace 11728 * @extends DataTable.models.ext 11729 */ 11730 11731 11732 var extPagination = DataTable.ext.pager; 11733 11734 // Paging buttons configuration 11735 $.extend( extPagination, { 11736 simple: function () { 11737 return [ 'previous', 'next' ]; 11738 }, 11739 11740 full: function () { 11741 return [ 'first', 'previous', 'next', 'last' ]; 11742 }, 11743 11744 numbers: function () { 11745 return [ 'numbers' ]; 11746 }, 11747 11748 simple_numbers: function () { 11749 return [ 'previous', 'numbers', 'next' ]; 11750 }, 11751 11752 full_numbers: function () { 11753 return [ 'first', 'previous', 'numbers', 'next', 'last' ]; 11754 }, 11755 11756 first_last: function () { 11757 return ['first', 'last']; 11758 }, 11759 11760 first_last_numbers: function () { 11761 return ['first', 'numbers', 'last']; 11762 }, 11763 11764 // For testing and plug-ins to use 11765 _numbers: _pagingNumbers, 11766 11767 // Number of number buttons - legacy, use `numbers` option for paging feature 11768 numbers_length: 7 11769 } ); 11770 11771 11772 $.extend( true, DataTable.ext.renderer, { 11773 pagingButton: { 11774 _: function (settings, buttonType, content, active, disabled) { 11775 var classes = settings.oClasses.paging; 11776 var btnClasses = [classes.button]; 11777 var btn; 11778 11779 if (active) { 11780 btnClasses.push(classes.active); 11781 } 11782 11783 if (disabled) { 11784 btnClasses.push(classes.disabled) 11785 } 11786 11787 if (buttonType === 'ellipsis') { 11788 btn = $('<span class="ellipsis"></span>').html(content)[0]; 11789 } 11790 else { 11791 btn = $('<button>', { 11792 class: btnClasses.join(' '), 11793 role: 'link', 11794 type: 'button' 11795 }).html(content); 11796 } 11797 11798 return { 11799 display: btn, 11800 clicker: btn 11801 } 11802 } 11803 }, 11804 11805 pagingContainer: { 11806 _: function (settings, buttons) { 11807 // No wrapping element - just append directly to the host 11808 return buttons; 11809 } 11810 } 11811 } ); 11812 11813 // Common function to remove new lines, strip HTML and diacritic control 11814 var _filterString = function (stripHtml, normalize) { 11815 return function (str) { 11816 if (_empty(str) || typeof str !== 'string') { 11817 return str; 11818 } 11819 11820 str = str.replace( _re_new_lines, " " ); 11821 11822 if (stripHtml) { 11823 str = _stripHtml(str); 11824 } 11825 11826 if (normalize) { 11827 str = _normalize(str, false); 11828 } 11829 11830 return str; 11831 }; 11832 } 11833 11834 /* 11835 * Public helper functions. These aren't used internally by DataTables, or 11836 * called by any of the options passed into DataTables, but they can be used 11837 * externally by developers working with DataTables. They are helper functions 11838 * to make working with DataTables a little bit easier. 11839 */ 11840 11841 function __mldFnName(name) { 11842 return name.replace(/[\W]/g, '_') 11843 } 11844 11845 // Common logic for moment, luxon or a date action 11846 function __mld( dt, momentFn, luxonFn, dateFn, arg1 ) { 11847 if (window.moment) { 11848 return dt[momentFn]( arg1 ); 11849 } 11850 else if (window.luxon) { 11851 return dt[luxonFn]( arg1 ); 11852 } 11853 11854 return dateFn ? dt[dateFn]( arg1 ) : dt; 11855 } 11856 11857 11858 var __mlWarning = false; 11859 function __mldObj (d, format, locale) { 11860 var dt; 11861 11862 if (window.moment) { 11863 dt = window.moment.utc( d, format, locale, true ); 11864 11865 if (! dt.isValid()) { 11866 return null; 11867 } 11868 } 11869 else if (window.luxon) { 11870 dt = format && typeof d === 'string' 11871 ? window.luxon.DateTime.fromFormat( d, format ) 11872 : window.luxon.DateTime.fromISO( d ); 11873 11874 if (! dt.isValid) { 11875 return null; 11876 } 11877 11878 dt.setLocale(locale); 11879 } 11880 else if (! format) { 11881 // No format given, must be ISO 11882 dt = new Date(d); 11883 } 11884 else { 11885 if (! __mlWarning) { 11886 alert('DataTables warning: Formatted date without Moment.js or Luxon - https://datatables.net/tn/17'); 11887 } 11888 11889 __mlWarning = true; 11890 } 11891 11892 return dt; 11893 } 11894 11895 // Wrapper for date, datetime and time which all operate the same way with the exception of 11896 // the output string for auto locale support 11897 function __mlHelper (localeString) { 11898 return function ( from, to, locale, def ) { 11899 // Luxon and Moment support 11900 // Argument shifting 11901 if ( arguments.length === 0 ) { 11902 locale = 'en'; 11903 to = null;
11903 // means toLocaleString 11904 from = null; // means iso8601 11905 } 11906 else if ( arguments.length === 1 ) { 11907 locale = 'en'; 11908 to = from; 11909 from = null; 11910 } 11911 else if ( arguments.length === 2 ) { 11912 locale = to; 11913 to = from; 11914 from = null; 11915 } 11916 11917 var typeName = 'datetime' + (to ? '-' + __mldFnName(to) : ''); 11918 11919 // Add type detection and sorting specific to this date format - we need to be able to identify 11920 // date type columns as such, rather than as numbers in extensions. Hence the need for this. 11921 if (! DataTable.ext.type.order[typeName]) { 11922 DataTable.type(typeName, { 11923 detect: function (d) { 11924 // The renderer will give the value to type detect as the type! 11925 return d === typeName ? typeName : false; 11926 }, 11927 order: { 11928 pre: function (d) { 11929 // The renderer gives us Moment, Luxon or Date obects for the sorting, all of which have a 11930 // `valueOf` which gives milliseconds epoch 11931 return d.valueOf(); 11932 } 11933 }, 11934 className: 'dt-right' 11935 }); 11936 } 11937 11938 return function ( d, type ) { 11939 // Allow for a default value 11940 if (d === null || d === undefined) { 11941 if (def === '--now') { 11942 // We treat everything as UTC further down, so no changes are 11943 // made, as such need to get the local date / time as if it were 11944 // UTC 11945 var local = new Date(); 11946 d = new Date( Date.UTC( 11947 local.getFullYear(), local.getMonth(), local.getDate(), 11948 local.getHours(), local.getMinutes(), local.getSeconds() 11949 ) ); 11950 } 11951 else { 11952 d = ''; 11953 } 11954 } 11955 11956 if (type === 'type') { 11957 // Typing uses the type name for fast matching 11958 return typeName; 11959 } 11960 11961 if (d === '') { 11962 return type !== 'sort' 11963 ? '' 11964 : __mldObj('0000-01-01 00:00:00', null, locale); 11965 } 11966 11967 // Shortcut. If `from` and `to` are the same, we are using the renderer to 11968 // format for ordering, not display - its already in the display format. 11969 if ( to !== null && from === to && type !== 'sort' && type !== 'type' && ! (d instanceof Date) ) { 11970 return d; 11971 } 11972 11973 var dt = __mldObj(d, from, locale); 11974 11975 if (dt === null) { 11976 return d; 11977 } 11978 11979 if (type === 'sort') { 11980 return dt; 11981 } 11982 11983 var formatted = to === null 11984 ? __mld(dt, 'toDate', 'toJSDate', '')[localeString]() 11985 : __mld(dt, 'format', 'toFormat', 'toISOString', to); 11986 11987 // XSS protection 11988 return type === 'display' ? 11989 _escapeHtml( formatted ) : 11990 formatted; 11991 }; 11992 } 11993 } 11994 11995 // Based on locale, determine standard number formatting 11996 // Fallback for legacy browsers is US English 11997 var __thousands = ','; 11998 var __decimal = '.'; 11999 12000 if (window.Intl !== undefined) { 12001 try { 12002 var num = new Intl.NumberFormat().formatToParts(100000.1); 12003 12004 for (var i=0 ; i<num.length ; i++) { 12005 if (num[i].type === 'group') { 12006 __thousands = num[i].value; 12007 } 12008 else if (num[i].type === 'decimal') { 12009 __decimal = num[i].value; 12010 } 12011 } 12012 } 12013 catch (e) { 12014 // noop 12015 } 12016 } 12017 12018 // Formatted date time detection - use by declaring the formats you are going to use 12019 DataTable.datetime = function ( format, locale ) { 12020 var typeName = 'datetime-detect-' + __mldFnName(format); 12021 12022 if (! locale) { 12023 locale = 'en'; 12024 } 12025 12026 if (! DataTable.ext.type.order[typeName]) { 12027 DataTable.type(typeName, { 12028 detect: function (d) { 12029 var dt = __mldObj(d, format, locale); 12030 return d === '' || dt ? typeName : false; 12031 }, 12032 order: { 12033 pre: function (d) { 12034 return __mldObj(d, format, locale) || 0; 12035 } 12036 }, 12037 className: 'dt-right' 12038 }); 12039 } 12040 } 12041 12042 /** 12043 * Helpers for `columns.render`. 12044 * 12045 * The options defined here can be used with the `columns.render` initialisation 12046 * option to provide a display renderer. The following functions are defined: 12047 * 12048 * * `moment` - Uses the MomentJS library to convert from a given format into another. 12049 * This renderer has three overloads: 12050 * * 1 parameter: 12051 * * `string` - Format to convert to (assumes input is ISO8601 and locale is `en`) 12052 * * 2 parameters: 12053 * * `string` - Format to convert from 12054 * * `string` - Format to convert to. Assumes `en` locale 12055 * * 3 parameters: 12056 * * `string` - Format to convert from 12057 * * `string` - Format to convert to 12058 * * `string` - Locale 12059 * * `number` - Will format numeric data (defined by `columns.data`) for 12060 * display, retaining the original unformatted data for sorting and filtering. 12061 * It takes 5 parameters: 12062 * * `string` - Thousands grouping separator 12063 * * `string` - Decimal point indicator 12064 * * `integer` - Number of decimal points to show 12065 * * `string` (optional) - Prefix. 12066 * * `string` (optional) - Postfix (/suffix). 12067 * * `text` - Escape HTML to help prevent XSS attacks. It has no optional 12068 * parameters. 12069 * 12070 * @example 12071 * // Column definition using the number renderer 12072 * { 12073 * data: "salary", 12074 * render: $.fn.dataTable.render.number( '\'', '.', 0, '$' ) 12075 * } 12076 * 12077 * @namespace 12078 */ 12079 DataTable.render = { 12080 date: __mlHelper('toLocaleDateString'),
12081 datetime: __mlHelper('toLocaleString'), 12082 time: __mlHelper('toLocaleTimeString'), 12083 number: function ( thousands, decimal, precision, prefix, postfix ) { 12084 // Auto locale detection 12085 if (thousands === null || thousands === undefined) { 12086 thousands = __thousands; 12087 } 12088 12089 if (decimal === null || decimal === undefined) { 12090 decimal = __decimal; 12091 } 12092 12093 return { 12094 display: function ( d ) { 12095 if ( typeof d !== 'number' && typeof d !== 'string' ) { 12096 return d; 12097 } 12098 12099 if (d === '' || d === null) { 12100 return d; 12101 } 12102 12103 var negative = d < 0 ? '-' : ''; 12104 var flo = parseFloat( d ); 12105 var abs = Math.abs(flo); 12106 12107 // Scientific notation for large and small numbers 12108 if (abs >= 100000000000 || (abs < 0.0001 && abs !== 0) ) { 12109 var exp = flo.toExponential(precision).split(/e\+?/); 12110 return exp[0] + ' x 10<sup>' + exp[1] + '</sup>'; 12111 } 12112 12113 // If NaN then there isn't much formatting that we can do - just 12114 // return immediately, escaping any HTML (this was supposed to 12115 // be a number after all) 12116 if ( isNaN( flo ) ) { 12117 return _escapeHtml( d ); 12118 } 12119 12120 flo = flo.toFixed( precision ); 12121 d = Math.abs( flo ); 12122 12123 var intPart = parseInt( d, 10 ); 12124 var floatPart = precision ? 12125 decimal+(d - intPart).toFixed( precision ).substring( 2 ): 12126 ''; 12127 12128 // If zero, then can't have a negative prefix 12129 if (intPart === 0 && parseFloat(floatPart) === 0) { 12130 negative = ''; 12131 } 12132 12133 return negative + (prefix||'') + 12134 intPart.toString().replace( 12135 /\B(?=(\d{3})+(?!\d))/g, thousands 12136 ) + 12137 floatPart + 12138 (postfix||''); 12139 } 12140 }; 12141 }, 12142 12143 text: function () { 12144 return { 12145 display: _escapeHtml, 12146 filter: _escapeHtml 12147 }; 12148 } 12149 }; 12150 12151 12152 var _extTypes = DataTable.ext.type; 12153 12154 // Get / set type 12155 DataTable.type = function (name, prop, val) { 12156 if (! prop) { 12157 return { 12158 className: _extTypes.className[name], 12159 detect: _extTypes.detect.find(function (fn) { 12160 return fn.name === name; 12161 }), 12162 order: { 12163 pre: _extTypes.order[name + '-pre'], 12164 asc: _extTypes.order[name + '-asc'], 12165 desc: _extTypes.order[name + '-desc'] 12166 }, 12167 render: _extTypes.render[name], 12168 search: _extTypes.search[name] 12169 }; 12170 } 12171 12172 var setProp = function(prop, propVal) { 12173 _extTypes[prop][name] = propVal; 12174 }; 12175 var setDetect = function (fn) { 12176 // Wrap to allow the function to return `true` rather than 12177 // specifying the type name. 12178 var cb = function (d, s) { 12179 var ret = fn(d, s); 12180 12181 return ret === true 12182 ? name 12183 : ret; 12184 }; 12185 Object.defineProperty(cb, "name", {value: name}); 12186 12187 var idx = _extTypes.detect.findIndex(function (fn) { 12188 return fn.name === name; 12189 }); 12190 12191 if (idx === -1) { 12192 _extTypes.detect.unshift(cb); 12193 } 12194 else { 12195 _extTypes.detect.splice(idx, 1, cb); 12196 } 12197 }; 12198 var setOrder = function (obj) { 12199 _extTypes.order[name + '-pre'] = obj.pre; // can be undefined 12200 _extTypes.order[name + '-asc'] = obj.asc; // can be undefined 12201 _extTypes.order[name + '-desc'] = obj.desc; // can be undefined 12202 }; 12203 12204 // prop is optional 12205 if (val === undefined) { 12206 val = prop; 12207 prop = null; 12208 } 12209 12210 if (prop === 'className') { 12211 setProp('className', val); 12212 } 12213 else if (prop === 'detect') { 12214 setDetect(val); 12215 } 12216 else if (prop === 'order') { 12217 setOrder(val); 12218 } 12219 else if (prop === 'render') { 12220 setProp('render', val); 12221 } 12222 else if (prop === 'search') { 12223 setProp('search', val); 12224 } 12225 else if (! prop) { 12226 if (val.className) { 12227 setProp('className', val.className); 12228 } 12229 12230 if (val.detect !== undefined) { 12231 setDetect(val.detect); 12232 } 12233 12234 if (val.order) { 12235 setOrder(val.order); 12236 } 12237 12238 if (val.render !== undefined) { 12239 setProp('render', val.render); 12240 } 12241 12242 if (val.search !== undefined) { 12243 setProp('search', val.search); 12244 } 12245 } 12246 } 12247 12248 // Get a list of types 12249 DataTable.types = function () { 12250 return _extTypes.detect.map(function (fn) { 12251 return fn.name; 12252 }); 12253 }; 12254 12255 // 12256 // Built in data types 12257 // 12258 12259 DataTable.type('string', { 12260 detect: function () { 12261 return 'string'; 12262 }, 12263 order: { 12264 pre: function ( a ) { 12265 // This is a little complex, but faster than always calling toString, 12266 // http://jsperf.com/tostring-v-check 12267 return _empty(a) ? 12268 '' : 12269 typeof a === 'string' ? 12270 a.toLowerCase() : 12271 ! a.toString ? 12272 '' : 12273 a.toString(); 12274 } 12275 }, 12276 search: _filterString(false, true) 12277 }); 12278 12279 12280 DataTable.type('html', { 12281 detect: function ( d ) { 12282 return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1) ? 12283 'html' : null; 12284 }, 12285 order: { 12286 pre: function ( a ) { 12287 return _empty(a) ? 12288 '' : 12289 a.replace ?
12290 _stripHtml(a).trim().toLowerCase() : 12291 a+''; 12292 } 12293 }, 12294 search: _filterString(true, true) 12295 }); 12296 12297 12298 DataTable.type('date', { 12299 className: 'dt-type-date', 12300 detect: function ( d ) 12301 { 12302 // V8 tries _very_ hard to make a string passed into `Date.parse()` 12303 // valid, so we need to use a regex to restrict date formats. Use a 12304 // plug-in for anything other than ISO8601 style strings 12305 if ( d && !(d instanceof Date) && ! _re_date.test(d) ) { 12306 return null; 12307 } 12308 var parsed = Date.parse(d); 12309 return (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null; 12310 }, 12311 order: { 12312 pre: function ( d ) { 12313 var ts = Date.parse( d ); 12314 return isNaN(ts) ? -Infinity : ts; 12315 } 12316 } 12317 }); 12318 12319 12320 DataTable.type('html-num-fmt', { 12321 className: 'dt-type-numeric', 12322 detect: function ( d, settings ) 12323 { 12324 var decimal = settings.oLanguage.sDecimal; 12325 return _htmlNumeric( d, decimal, true ) ? 'html-num-fmt' : null; 12326 }, 12327 order: { 12328 pre: function ( d, s ) { 12329 var dp = s.oLanguage.sDecimal; 12330 return __numericReplace( d, dp, _re_html, _re_formatted_numeric ); 12331 } 12332 }, 12333 search: _filterString(true, true) 12334 }); 12335 12336 12337 DataTable.type('html-num', { 12338 className: 'dt-type-numeric', 12339 detect: function ( d, settings ) 12340 { 12341 var decimal = settings.oLanguage.sDecimal; 12342 return _htmlNumeric( d, decimal ) ? 'html-num' : null; 12343 }, 12344 order: { 12345 pre: function ( d, s ) { 12346 var dp = s.oLanguage.sDecimal; 12347 return __numericReplace( d, dp, _re_html ); 12348 } 12349 }, 12350 search: _filterString(true, true) 12351 }); 12352 12353 12354 DataTable.type('num-fmt', { 12355 className: 'dt-type-numeric', 12356 detect: function ( d, settings ) 12357 { 12358 var decimal = settings.oLanguage.sDecimal; 12359 return _isNumber( d, decimal, true ) ? 'num-fmt' : null; 12360 }, 12361 order: { 12362 pre: function ( d, s ) { 12363 var dp = s.oLanguage.sDecimal; 12364 return __numericReplace( d, dp, _re_formatted_numeric ); 12365 } 12366 } 12367 }); 12368 12369 12370 DataTable.type('num', { 12371 className: 'dt-type-numeric', 12372 detect: function ( d, settings ) 12373 { 12374 var decimal = settings.oLanguage.sDecimal; 12375 return _isNumber( d, decimal ) ? 'num' : null; 12376 }, 12377 order: { 12378 pre: function (d, s) { 12379 var dp = s.oLanguage.sDecimal; 12380 return __numericReplace( d, dp ); 12381 } 12382 } 12383 }); 12384 12385 12386 12387 12388 var __numericReplace = function ( d, decimalPlace, re1, re2 ) { 12389 if ( d !== 0 && (!d || d === '-') ) { 12390 return -Infinity; 12391 } 12392 12393 var type = typeof d; 12394 12395 if (type === 'number' || type === 'bigint') { 12396 return d; 12397 } 12398 12399 // If a decimal place other than `.` is used, it needs to be given to the 12400 // function so we can detect it and replace with a `.` which is the only 12401 // decimal place Javascript recognises - it is not locale aware. 12402 if ( decimalPlace ) { 12403 d = _numToDecimal( d, decimalPlace ); 12404 } 12405 12406 if ( d.replace ) { 12407 if ( re1 ) { 12408 d = d.replace( re1, '' ); 12409 } 12410 12411 if ( re2 ) { 12412 d = d.replace( re2, '' ); 12413 } 12414 } 12415 12416 return d * 1; 12417 }; 12418 12419 12420 $.extend( true, DataTable.ext.renderer, { 12421 footer: { 12422 _: function ( settings, cell, classes ) {
12423 cell.addClass(classes.tfoot.cell); 12424 } 12425 }, 12426 12427 header: { 12428 _: function ( settings, cell, classes ) { 12429 cell.addClass(classes.thead.cell); 12430 12431 if (! settings.oFeatures.bSort) { 12432 cell.addClass(classes.order.none); 12433 } 12434 12435 var legacyTop = settings.bSortCellsTop; 12436 var headerRows = cell.closest('thead').find('tr'); 12437 var rowIdx = cell.parent().index(); 12438 12439 // Conditions to not apply the ordering icons 12440 if ( 12441 // Cells and rows which have the attribute to disable the icons 12442 cell.attr('data-dt-order') === 'disable' || 12443 cell.parent().attr('data-dt-order') === 'disable' || 12444 12445 // Legacy support for `orderCellsTop`. If it is set, then cells 12446 // which are not in the top or bottom row of the header (depending 12447 // on the value) do not get the sorting classes applied to them 12448 (legacyTop === true && rowIdx !== 0) || 12449 (legacyTop === false && rowIdx !== headerRows.length - 1) 12450 ) { 12451 return; 12452 } 12453 12454 // No additional mark-up required 12455 // Attach a sort listener to update on sort - note that using the 12456 // `DT` namespace will allow the event to be removed automatically 12457 // on destroy, while the `dt` namespaced event is the one we are 12458 // listening for 12459 $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting ) { 12460 if ( settings !== ctx ) { // need to check this this is the host 12461 return; // table, not a nested one 12462 } 12463 12464 var orderClasses = classes.order; 12465 var columns = ctx.api.columns( cell ); 12466 var col = settings.aoColumns[columns.flatten()[0]]; 12467 var orderable = columns.orderable().includes(true); 12468 var ariaType = ''; 12469 var indexes = columns.indexes(); 12470 var sortDirs = columns.orderable(true).flatten(); 12471 var orderedColumns = ',' + sorting.map( function (val) { 12472 return val.col; 12473 } ).join(',') + ','; 12474 12475 cell 12476 .removeClass( 12477 orderClasses.isAsc +' '+ 12478 orderClasses.isDesc 12479 ) 12480 .toggleClass( orderClasses.none, ! orderable ) 12481 .toggleClass( orderClasses.canAsc, orderable && sortDirs.includes('asc') ) 12482 .toggleClass( orderClasses.canDesc, orderable && sortDirs.includes('desc') ); 12483 12484 var sortIdx = orderedColumns.indexOf( ',' + indexes.toArray().join(',') + ',' ); 12485 12486 if ( sortIdx !== -1 ) { 12487 // Get the ordering direction for the columns under this cell 12488 // Note that it is possible for a cell to be asc and desc sorting 12489 // (column spanning cells) 12490 var orderDirs = columns.order(); 12491 12492 cell.addClass( 12493 orderDirs.includes('asc') ? orderClasses.isAsc : '' + 12494 orderDirs.includes('desc') ? orderClasses.isDesc : '' 12495 ); 12496 } 12497 12498 // The ARIA spec says that only one column should be marked with aria-sort 12499 if ( sortIdx === 0 ) { 12500 var firstSort = sorting[0]; 12501 var sortOrder = col.asSorting; 12502 12503 cell.attr('aria-sort', firstSort.dir === 'asc' ? 'ascending' : 'descending'); 12504 12505 // Determine if the next click will remove sorting or change the sort 12506 ariaType = ! sortOrder[firstSort.index + 1] ? 'Remove' : 'Reverse'; 12507 } 12508 else { 12509 cell.removeAttr('aria-sort'); 12510 } 12511 12512 cell.attr('aria-label', orderable 12513 ? col.ariaTitle + ctx.api.i18n('oAria.orderable' + ariaType) 12514 : col.ariaTitle 12515 ); 12516 12517 if (orderable) { 12518 cell.find('.dt-column-title').attr('role', 'button'); 12519 cell.attr('tabindex', 0) 12520 } 12521 } ); 12522 } 12523 }, 12524 12525 layout: { 12526 _: function ( settings, container, items ) { 12527 var row = $('<div/>') 12528 .addClass('dt-layout-row') 12529 .appendTo( container ); 12530 12531 $.each( items, function (key, val) { 12532 var klass = ! val.table ? 12533 'dt-'+key+' ' : 12534 ''; 12535 12536 if (val.table) { 12537 row.addClass('dt-layout-table'); 12538 } 12539 12540 $('<div/>') 12541 .attr({ 12542 id: val.id || null, 12543 "class": 'dt-layout-cell '+klass+(val.className || '') 12544 }) 12545 .append( val.contents ) 12546 .appendTo( row ); 12547 } ); 12548 } 12549 } 12550 } ); 12551 12552 12553 DataTable.feature = {}; 12554 12555 // Third parameter is internal only! 12556 DataTable.feature.register = function ( name, cb, legacy ) { 12557 DataTable.ext.features[ name ] = cb; 12558 12559 if (legacy) { 12560 _ext.feature.push({ 12561 cFeature: legacy, 12562 fnInit: cb 12563 }); 12564 } 12565 }; 12566 12567 DataTable.feature.register( 'info', function ( settings, opts ) { 12568 // For compatibility with the legacy `info` top level option 12569 if (! settings.oFeatures.bInfo) { 12570 return null; 12571 } 12572 12573 var 12574 lang = settings.oLanguage, 12575 tid = settings.sTableId, 12576 n = $('<div/>', { 12577 'class': settings.oClasses.info.container, 12578 } ); 12579 12580 opts = $.extend({ 12581 callback: lang.fnInfoCallback, 12582 empty: lang.sInfoEmpty, 12583 postfix: lang.sInfoPostFix, 12584 search: lang.sInfoFiltered, 12585 text: lang.sInfo, 12586 }, opts); 12587 12588 12589 // Update display on each draw 12590 settings.aoDrawCallback.push(function (s) { 12591 _fnUpdateInfo(s, opts, n); 12592 }); 12593 12594 // For the first info display in the table, we add a callback and aria information. 12595 if (! settings._infoEl) { 12596 n.attr({ 12597 'aria-live': 'polite', 12598 id: tid+'_info', 12599 role: 'status' 12600 }); 12601 12602 // Table is described by our info div 12603 $(settings.nTable).attr( 'aria-describedby', tid+'_info' ); 12604 12605 settings._infoEl = n; 12606 } 12607 12608 return n; 12609 }, 'i' ); 12610 12611 /** 12612 * Update the information elements in the display 12613 * @param {object} settings dataTables settings object 12614 * @memberof DataTable#oApi 12615 */ 12616 function _fnUpdateInfo ( settings, opts, node ) 12617 { 12618 var 12619 start = settings._iDisplayStart+1, 12620 end = settings.fnDisplayEnd(), 12621 max = settings.fnRecordsTotal(), 12622 total = settings.fnRecordsDisplay(), 12623 out = total 12624 ? opts.text 12625 : opts.empty; 12626 12627 if ( total !== max ) { 12628 // Record set after filtering 12629 out += ' ' + opts.search; 12630 } 12631 12632 // Convert the macros 12633 out += opts.postfix; 12634 out = _fnMacros( settings, out ); 12635 12636 if ( opts.callback ) { 12637 out = opts.callback.call( settings.oInstance, 12638 settings, start, end, max, total, out 12639 ); 12640 } 12641 12642 node.html( out ); 12643 12644 _fnCallbackFire(settings, null, 'info', [settings, node[0], out]); 12645 } 12646 12647 var __searchCounter = 0; 12648 12649 // opts 12650 // - text 12651 // - placeholder 12652 DataTable.feature.register( 'search', function ( settings, opts ) { 12653 // Don't show the input if filtering isn't available on the table 12654 if (! settings.oFeatures.bFilter) { 12655 return null; 12656 } 12657 12658 var classes = settings.oClasses.search; 12659 var tableId = settings.sTableId; 12660 var language = settings.oLanguage; 12661 var previousSearch = settings.oPreviousSearch; 12662 var input = '<input type="search" class="'+classes.input+'"/>'; 12663 12664 opts = $.extend({ 12665 placeholder: language.sSearchPlaceholder, 12666 text: language.sSearch 12667 }, opts); 12668
12669 // The _INPUT_ is optional - is appended if not present 12670 if (opts.text.indexOf('_INPUT_') === -1) { 12671 opts.text += '_INPUT_'; 12672 } 12673 12674 opts.text = _fnMacros(settings, opts.text); 12675 12676 // We can put the <input> outside of the label if it is at the start or end 12677 // which helps improve accessability (not all screen readers like implicit 12678 // for elements). 12679 var end = opts.text.match(/_INPUT_$/); 12680 var start = opts.text.match(/^_INPUT_/); 12681 var removed = opts.text.replace(/_INPUT_/, ''); 12682 var str = '<label>' + opts.text + '</label>'; 12683 12684 if (start) { 12685 str = '_INPUT_<label>' + removed + '</label>'; 12686 } 12687 else if (end) { 12688 str = '<label>' + removed + '</label>_INPUT_'; 12689 } 12690 12691 var filter = $('<div>') 12692 .addClass(classes.container) 12693 .append(str.replace(/_INPUT_/, input)); 12694 12695 // add for and id to label and input 12696 filter.find('label').attr('for', 'dt-search-' + __searchCounter); 12697 filter.find('input').attr('id', 'dt-search-' + __searchCounter); 12698 __searchCounter++; 12699 12700 var searchFn = function(event) { 12701 var val = this.value; 12702 12703 if(previousSearch.return && event.key !== "Enter") { 12704 return; 12705 } 12706 12707 /* Now do the filter */ 12708 if ( val != previousSearch.search ) { 12709 previousSearch.search = val; 12710 12711 _fnFilterComplete( settings, previousSearch ); 12712 12713 // Need to redraw, without resorting 12714 settings._iDisplayStart = 0; 12715 _fnDraw( settings ); 12716 } 12717 }; 12718 12719 var searchDelay = settings.searchDelay !== null ? 12720 settings.searchDelay : 12721 0; 12722 12723 var jqFilter = $('input', filter) 12724 .val( previousSearch.search ) 12725 .attr( 'placeholder', opts.placeholder ) 12726 .on( 12727 'keyup.DT search.DT input.DT paste.DT cut.DT', 12728 searchDelay ? 12729 DataTable.util.debounce( searchFn, searchDelay ) : 12730 searchFn 12731 ) 12732 .on( 'mouseup.DT', function(e) { 12733 // Edge fix! Edge 17 does not trigger anything other than mouse events when clicking 12734 // on the clear icon (Edge bug 17584515). This is safe in other browsers as `searchFn` 12735 // checks the value to see if it has changed. In other browsers it won't have. 12736 setTimeout( function () { 12737 searchFn.call(jqFilter[0], e); 12738 }, 10); 12739 } ) 12740 .on( 'keypress.DT', function(e) { 12741 /* Prevent form submission */ 12742 if ( e.keyCode == 13 ) { 12743 return false; 12744 } 12745 } ) 12746 .attr('aria-controls', tableId); 12747 12748 // Update the input elements whenever the table is filtered 12749 $(settings.nTable).on( 'search.dt.DT', function ( ev, s ) { 12750 if ( settings === s && jqFilter[0] !== document.activeElement ) { 12751 jqFilter.val( typeof previousSearch.search !== 'function' 12752 ? previousSearch.search 12753 : '' 12754 ); 12755 } 12756 } ); 12757 12758 return filter; 12759 }, 'f' ); 12760 12761 // opts 12762 // - type - button configuration 12763 // - buttons - number of buttons to show - must be odd 12764 DataTable.feature.register( 'paging', function ( settings, opts ) { 12765 // Don't show the paging input if the table doesn't have paging enabled 12766 if (! settings.oFeatures.bPaginate) { 12767 return null; 12768 } 12769 12770 opts = $.extend({ 12771 buttons: DataTable.ext.pager.numbers_length, 12772 type: settings.sPaginationType, 12773 boundaryNumbers: true 12774 }, opts); 12775 12776 // To be removed in 2.1 12777 if (opts.numbers) { 12778 opts.buttons = opts.numbers; 12779 } 12780 12781 var host = $('<div/>').addClass( settings.oClasses.paging.container + ' paging_' + opts.type ); 12782 var draw = function () { 12783 _pagingDraw(settings, host, opts); 12784 }; 12785 12786 settings.aoDrawCallback.push(draw); 12787 12788 // Responsive redraw of paging control 12789 $(settings.nTable).on('column-sizing.dt.DT', draw); 12790 12791 return host; 12792 }, 'p' ); 12793 12794 function _pagingDraw(settings, host, opts) { 12795 if (! settings._bInitComplete) { 12796 return; 12797 } 12798 12799 var 12800 plugin = DataTable.ext.pager[ opts.type ], 12801 aria = settings.oLanguage.oAria.paginate || {}, 12802 start = settings._iDisplayStart, 12803 len = settings._iDisplayLength, 12804 visRecords = settings.fnRecordsDisplay(), 12805 all = len === -1, 12806 page = all ? 0 : Math.ceil( start / len ), 12807 pages = all ? 1 : Math.ceil( visRecords / len ), 12808 buttons = plugin() 12809 .map(function (val) { 12810 return val === 'numbers' 12811 ? _pagingNumbers(page, pages, opts.buttons, opts.boundaryNumbers) 12812 : val; 12813 }) 12814 .flat(); 12815 12816 var buttonEls = []; 12817 12818 for (var i=0 ; i<buttons.length ; i++) { 12819 var button = buttons[i]; 12820 12821 var btnInfo = _pagingButtonInfo(settings, button, page, pages); 12822 var btn = _fnRenderer( settings, 'pagingButton' )( 12823 settings, 12824 button, 12825 btnInfo.display, 12826 btnInfo.active, 12827 btnInfo.disabled 12828 ); 12829 12830 // Common attributes 12831 $(btn.clicker).attr({ 12832 'aria-controls': settings.sTableId, 12833 'aria-disabled': btnInfo.disabled ? 'true' : null, 12834 'aria-current': btnInfo.active ? 'page' : null, 12835 'aria-label': aria[ button ], 12836 'data-dt-idx': button, 12837 'tabIndex': btnInfo.disabled ? -1 : settings.iTabIndex, 12838 }); 12839 12840 if (typeof button !== 'number') { 12841 $(btn.clicker).addClass(button); 12842 } 12843 12844 _fnBindAction( 12845 btn.clicker, {action: button}, function(e) { 12846 e.preventDefault(); 12847 12848 _fnPageChange( settings, e.data.action, true ); 12849 } 12850 ); 12851 12852 buttonEls.push(btn.display); 12853 } 12854 12855 var wrapped = _fnRenderer(settings, 'pagingContainer')( 12856 settings, buttonEls 12857 ); 12858 12859 var activeEl = host.find(document.activeElement).data('dt-idx'); 12860 12861 host.empty().append(wrapped); 12862 12863 if ( activeEl !== undefined ) { 12864 host.find( '[data-dt-idx='+activeEl+']' ).trigger('focus'); 12865 } 12866 12867 // Responsive - check if the buttons are over two lines based on the 12868 // height of the buttons and the container. 12869 if ( 12870 buttonEls.length && // any buttons 12871 opts.numbers > 1 && // prevent infinite 12872 $(host).height() >= ($(buttonEls[0]).outerHeight() * 2) - 10 12873 ) { 12874 _pagingDraw(settings, host, $.extend({}, opts, { numbers: opts.numbers - 2 })); 12875 } 12876 } 12877 12878 /** 12879 * Get properties for a button based on the current paging state of the table 12880 * 12881 * @param {*} settings DT settings object 12882 * @param {*} button The button type in question 12883 * @param {*} page Table's current page 12884 * @param {*} pages Number of pages 12885 * @returns Info object 12886 */ 12887 function _pagingButtonInfo(settings, button, page, pages) { 12888 var lang = settings.oLanguage.oPaginate; 12889 var o = { 12890 display: '', 12891 active: false, 12892 disabled: false 12893 }; 12894 12895 switch ( button ) { 12896 case 'ellipsis': 12897 o.display = '…'; 12898 o.disabled = true; 12899 break; 12900 12901 case 'first': 12902 o.display = lang.sFirst; 12903 12904 if (page === 0) { 12905 o.disabled = true; 12906 } 12907 break; 12908 12909 case 'previous': 12910 o.display = lang.sPrevious; 12911 12912 if ( page === 0 ) { 12913 o.disabled = true; 12914 } 12915 break; 12916 12917 case 'next': 12918 o.display = lang.sNext; 12919 12920 if ( pages === 0 || page === pages-1 ) { 12921 o.disabled = true; 12922 } 12923 break; 12924 12925 case 'last': 12926 o.display = lang.sLast; 12927 12928 if ( pages === 0 || page === pages-1 ) { 12929 o.disabled = true; 12930 } 12931 break; 12932 12933 default: 12934 if ( typeof button === 'number' ) { 12935 o.display = settings.fnFormatNumber( button + 1 ); 12936 12937 if (page === button) { 12938 o.active = true; 12939 } 12940 } 12941 break; 12942 } 12943 12944 return o; 12945 } 12946 12947 /** 12948 * Compute what number buttons to show in the paging control 12949 * 12950 * @param {*} page Current page 12951 * @param {*} pages Total number of pages 12952 * @param {*} buttons Target number of number buttons 12953 * @param {boolean} addFirstLast Indicate if page 1 and end should be included 12954 * @returns Buttons to show 12955 */ 12956 function _pagingNumbers ( page, pages, buttons, addFirstLast ) { 12957 var 12958 numbers = [], 12959 half = Math.floor(buttons / 2), 12960 before = addFirstLast ? 2 : 1, 12961 after = addFirstLast ? 1 : 0; 12962 12963 if ( pages <= buttons ) { 12964 numbers = _range(0, pages); 12965 } 12966 else if (buttons === 1) { 12967 // Single button - current page only 12968 numbers = [page]; 12969 } 12970 else if (buttons === 3) { 12971 // Special logic for just three buttons 12972 if (page <= 1) { 12973 numbers = [0, 1, 'ellipsis']; 12974 } 12975 else if (page >= pages - 2) { 12976 numbers = _range(pages-2, pages); 12977 numbers.unshift('ellipsis'); 12978 } 12979 else { 12980 numbers = ['ellipsis', page, 'ellipsis']; 12981 } 12982 } 12983 else if ( page <= half ) { 12984 numbers = _range(0, buttons-before); 12985 numbers.push('ellipsis'); 12986 12987 if (addFirstLast) { 12988 numbers.push(pages-1); 12989 } 12990 } 12991 else if ( page >= pages - 1 - half ) { 12992 numbers = _range(pages-(buttons-before), pages); 12993 numbers.unshift('ellipsis'); 12994 12995 if (addFirstLast) { 12996 numbers.unshift(0);
12997 } 12998 } 12999 else { 13000 numbers = _range(page-half+before, page+half-after); 13001 numbers.push('ellipsis'); 13002 numbers.unshift('ellipsis'); 13003 13004 if (addFirstLast) { 13005 numbers.push(pages-1); 13006 numbers.unshift(0); 13007 } 13008 } 13009 13010 return numbers; 13011 } 13012 13013 var __lengthCounter = 0; 13014 13015 // opts 13016 // - menu 13017 // - text 13018 DataTable.feature.register( 'pageLength', function ( settings, opts ) { 13019 var features = settings.oFeatures; 13020 13021 // For compatibility with the legacy `pageLength` top level option 13022 if (! features.bPaginate || ! features.bLengthChange) { 13023 return null; 13024 } 13025 13026 opts = $.extend({ 13027 menu: settings.aLengthMenu, 13028 text: settings.oLanguage.sLengthMenu 13029 }, opts); 13030 13031 var 13032 classes = settings.oClasses.length, 13033 tableId = settings.sTableId, 13034 menu = opts.menu, 13035 lengths = [], 13036 language = [], 13037 i; 13038 13039 // Options can be given in a number of ways 13040 if (Array.isArray( menu[0] )) { 13041 // Old 1.x style - 2D array 13042 lengths = menu[0]; 13043 language = menu[1]; 13044 } 13045 else { 13046 for ( i=0 ; i<menu.length ; i++ ) { 13047 // An object with different label and value 13048 if ($.isPlainObject(menu[i])) { 13049 lengths.push(menu[i].value); 13050 language.push(menu[i].label); 13051 } 13052 else { 13053 // Or just a number to display and use 13054 lengths.push(menu[i]); 13055 language.push(menu[i]); 13056 } 13057 } 13058 } 13059 13060 // We can put the <select> outside of the label if it is at the start or 13061 // end which helps improve accessability (not all screen readers like 13062 // implicit for elements). 13063 var end = opts.text.match(/_MENU_$/); 13064 var start = opts.text.match(/^_MENU_/); 13065 var removed = opts.text.replace(/_MENU_/, ''); 13066 var str = '<label>' + opts.text + '</label>'; 13067 13068 if (start) { 13069 str = '_MENU_<label>' + removed + '</label>'; 13070 } 13071 else if (end) { 13072 str = '<label>' + removed + '</label>_MENU_'; 13073 } 13074 13075 // Wrapper element - use a span as a holder for where the select will go 13076 var div = $('<div/>') 13077 .addClass( classes.container ) 13078 .append( 13079 str.replace( '_MENU_', '<span></span>' ) 13080 ); 13081 13082 // Save text node content for macro updating 13083 var textNodes = []; 13084 div.find('label')[0].childNodes.forEach(function (el) { 13085 if (el.nodeType === Node.TEXT_NODE) { 13086 textNodes.push({ 13087 el: el, 13088 text: el.textContent 13089 }); 13090 } 13091 }) 13092 13093 // Update the label text in case it has an entries value 13094 var updateEntries = function (len) { 13095 textNodes.forEach(function (node) { 13096 node.el.textContent = _fnMacros(settings, node.text, len); 13097 }); 13098 } 13099 13100 // Next, the select itself, along with the options 13101 var select = $('<select/>', { 13102 'name': tableId+'_length', 13103 'aria-controls': tableId, 13104 'class': classes.select 13105 } ); 13106 13107 for ( i=0 ; i<lengths.length ; i++ ) { 13108 select[0][ i ] = new Option( 13109 typeof language[i] === 'number' ? 13110 settings.fnFormatNumber( language[i] ) : 13111 language[i], 13112 lengths[i] 13113 ); 13114 } 13115 13116 // add for and id to label and input 13117 div.find('label').attr('for', 'dt-length-' + __lengthCounter); 13118 select.attr('id', 'dt-length-' + __lengthCounter); 13119 __lengthCounter++; 13120 13121 // Swap in the select list 13122 div.find('span').replaceWith(select); 13123 13124 // Can't use `select` variable as user might provide their own and the 13125 // reference is broken by the use of outerHTML 13126 $('select', div) 13127 .val( settings._iDisplayLength ) 13128 .on( 'change.DT', function() { 13129 _fnLengthChange( settings, $(this).val() ); 13130 _fnDraw( settings ); 13131 } ); 13132 13133 // Update node value whenever anything changes the table's length 13134 $(settings.nTable).on( 'length.dt.DT', function (e, s, len) { 13135 if ( settings === s ) { 13136 $('select', div).val( len ); 13137 13138 // Resolve plurals in the text for the new length 13139 updateEntries(len); 13140 } 13141 } ); 13142 13143 updateEntries(settings._iDisplayLength); 13144 13145 return div; 13146 }, 'l' ); 13147 13148 // jQuery access 13149 $.fn.dataTable = DataTable; 13150 13151 // Provide access to the host jQuery object (circular reference) 13152 DataTable.$ = $; 13153 13154 // Legacy aliases 13155 $.fn.dataTableSettings = DataTable.settings; 13156 $.fn.dataTableExt = DataTable.ext; 13157 13158 // With a capital `D` we return a DataTables API instance rather than a 13159 // jQuery object 13160 $.fn.DataTable = function ( opts ) { 13161 return $(this).dataTable( opts ).api(); 13162 }; 13163 13164 // All properties that are available to $.fn.dataTable should also be 13165 // available on $.fn.DataTable 13166 $.each( DataTable, function ( prop, val ) { 13167 $.fn.DataTable[ prop ] = val; 13168 } ); 13169 13170 return DataTable; 13171}));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.