datatables.js (370923B)
1 /* 2 * This combined file was created by the DataTables downloader builder: 3 * https://datatables.net/download 4 * 5 * To rebuild or modify this file with the latest versions of the included 6 * software please visit: 7 * https://datatables.net/download/#bs5/dt-2.0.8 8 * 9 * Included libraries: 10 * DataTables 2.0.8 11 */ 12 13 /*! DataTables 2.0.8 14 * © SpryMedia Ltd - datatables.net/license 15 */ 16 17 /** 18 * @summary DataTables 19 * @description Paginate, search and order HTML tables 20 * @version 2.0.8 21 * @author SpryMedia Ltd 22 * @contact www.datatables.net 23 * @copyright SpryMedia Ltd. 24 * 25 * This source file is free software, available under the following license: 26 * MIT license - https://datatables.net/license 27 * 28 * This source file is distributed in the hope that it will be useful, but 29 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 30 * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details. 31 * 32 * For details please refer to: https://www.datatables.net 33 */ 34 35 (function( factory ) { 36 "use strict"; 37 38 if ( typeof define === 'function' && define.amd ) { 39 // AMD 40 define( ['jquery'], function ( $ ) { 41 return factory( $, window, document ); 42 } ); 43 } 44 else if ( typeof exports === 'object' ) { 45 // CommonJS 46 // jQuery's factory checks for a global window - if it isn't present then it 47 // returns a factory function that expects the window object 48 var jq = require('jquery'); 49 50 if (typeof window === 'undefined') { 51 module.exports = function (root, $) { 52 if ( ! root ) { 53 // CommonJS environments without a window global must pass a 54 // root. This will give an error otherwise 55 root = window; 56 } 57 58 if ( ! $ ) { 59 $ = jq( root ); 60 } 61 62 return factory( $, root, root.document ); 63 }; 64 } 65 else { 66 module.exports = factory( jq, window, window.document ); 67 } 68 } 69 else { 70 // Browser 71 window.DataTable = factory( jQuery, window, document ); 72 } 73 }(function( $, window, document ) { 74 "use strict"; 75 76 77 var DataTable = function ( selector, options ) 78 { 79 // Check if called with a window or jQuery object for DOM less applications 80 // This is for backwards compatibility 81 if (DataTable.factory(selector, options)) { 82 return DataTable; 83 } 84 85 // When creating with `new`, create a new DataTable, returning the API instance 86 if (this instanceof DataTable) { 87 return $(selector).DataTable(options); 88 } 89 else { 90 // Argument switching 91 options = selector; 92 } 93 94 var _that = this; 95 var emptyInit = options === undefined; 96 var len = this.length; 97 98 if ( emptyInit ) { 99 options = {}; 100 } 101 102 // Method to get DT API instance from jQuery object 103 this.api = function () 104 { 105 return new _Api( this ); 106 }; 107 108 this.each(function() { 109 // For each initialisation we want to give it a clean initialisation 110 // object that can be bashed around 111 var o = {}; 112 var oInit = len > 1 ? // optimisation for single table case 113 _fnExtend( o, options, true ) : 114 options; 115 116 117 var i=0, iLen; 118 var sId = this.getAttribute( 'id' ); 119 var bInitHandedOff = false; 120 var defaults = DataTable.defaults; 121 var $this = $(this); 122 123 124 /* Sanity check */ 125 if ( this.nodeName.toLowerCase() != 'table' ) 126 { 127 _fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 ); 128 return; 129 } 130 131 $(this).trigger( 'options.dt', oInit ); 132 133 /* Backwards compatibility for the defaults */ 134 _fnCompatOpts( defaults ); 135 _fnCompatCols( defaults.column ); 136 137 /* Convert the camel-case defaults to Hungarian */ 138 _fnCamelToHungarian( defaults, defaults, true ); 139 _fnCamelToHungarian( defaults.column, defaults.column, true ); 140 141 /* Setting up the initialisation object */ 142 _fnCamelToHungarian( defaults, $.extend( oInit, $this.data() ), true ); 143 144 145 146 /* Check to see if we are re-initialising a table */ 147 var allSettings = DataTable.settings; 148 for ( i=0, iLen=allSettings.length ; i<iLen ; i++ ) 149 { 150 var s = allSettings[i]; 151 152 /* Base check on table node */ 153 if ( 154 s.nTable == this || 155 (s.nTHead && s.nTHead.parentNode == this) || 156 (s.nTFoot && s.nTFoot.parentNode == this) 157 ) { 158 var bRetrieve = oInit.bRetrieve !== undefined ? oInit.bRetrieve : defaults.bRetrieve; 159 var bDestroy = oInit.bDestroy !== undefined ? oInit.bDestroy : defaults.bDestroy; 160 161 if ( emptyInit || bRetrieve ) 162 { 163 return s.oInstance; 164 } 165 else if ( bDestroy ) 166 { 167 new DataTable.Api(s).destroy(); 168 break; 169 } 170 else 171 { 172 _fnLog( s, 0, 'Cannot reinitialise DataTable', 3 ); 173 return; 174 } 175 } 176 177 /* If the element we are initialising has the same ID as a table which was previously 178 * initialised, but the table nodes don't match (from before) then we destroy the old 179 * instance by simply deleting it. This is under the assumption that the table has been 180 * destroyed by other methods. Anyone using non-id selectors will need to do this manually 181 */ 182 if ( s.sTableId == this.id ) 183 { 184 allSettings.splice( i, 1 ); 185 break; 186 } 187 } 188 189 /* Ensure the table has an ID - required for accessibility */ 190 if ( sId === null || sId === "" ) 191 { 192 sId = "DataTables_Table_"+(DataTable.ext._unique++); 193 this.id = sId; 194 } 195 196 /* Create the settings object for this table and set some of the default parameters */ 197 var oSettings = $.extend( true, {}, DataTable.models.oSettings, { 198 "sDestroyWidth": $this[0].style.width, 199 "sInstance": sId, 200 "sTableId": sId, 201 colgroup: $('<colgroup>').prependTo(this), 202 fastData: function (row, column, type) { 203 return _fnGetCellData(oSettings, row, column, type); 204 } 205 } ); 206 oSettings.nTable = this; 207 oSettings.oInit = oInit; 208 209 allSettings.push( oSettings ); 210 211 // Make a single API instance available for internal handling 212 oSettings.api = new _Api( oSettings ); 213 214 // Need to add the instance after the instance after the settings object has been added 215 // to the settings array, so we can self reference the table instance if more than one 216 oSettings.oInstance = (_that.length===1) ? _that : $this.dataTable(); 217 218 // Backwards compatibility, before we apply all the defaults 219 _fnCompatOpts( oInit ); 220 221 // If the length menu is given, but the init display length is not, use the length menu 222 if ( oInit.aLengthMenu && ! oInit.iDisplayLength ) 223 { 224 oInit.iDisplayLength = Array.isArray(oInit.aLengthMenu[0]) 225 ? oInit.aLengthMenu[0][0] 226 : $.isPlainObject( oInit.aLengthMenu[0] ) 227 ? oInit.aLengthMenu[0].value 228 : oInit.aLengthMenu[0]; 229 } 230 231 // Apply the defaults and init options to make a single init object will all 232 // options defined from defaults and instance options. 233 oInit = _fnExtend( $.extend( true, {}, defaults ), oInit ); 234 235 236 // Map the initialisation options onto the settings object 237 _fnMap( oSettings.oFeatures, oInit, [ 238 "bPaginate", 239 "bLengthChange", 240 "bFilter", 241 "bSort", 242 "bSortMulti", 243 "bInfo", 244 "bProcessing", 245 "bAutoWidth", 246 "bSortClasses", 247 "bServerSide", 248 "bDeferRender" 249 ] ); 250 _fnMap( oSettings, oInit, [ 251 "ajax", 252 "fnFormatNumber", 253 "sServerMethod", 254 "aaSorting", 255 "aaSortingFixed", 256 "aLengthMenu", 257 "sPaginationType", 258 "iStateDuration", 259 "bSortCellsTop", 260 "iTabIndex", 261 "sDom", 262 "fnStateLoadCallback", 263 "fnStateSaveCallback", 264 "renderer", 265 "searchDelay", 266 "rowId", 267 "caption", 268 "layout", 269 [ "iCookieDuration", "iStateDuration" ], // backwards compat 270 [ "oSearch", "oPreviousSearch" ], 271 [ "aoSearchCols", "aoPreSearchCols" ], 272 [ "iDisplayLength", "_iDisplayLength" ] 273 ] ); 274 _fnMap( oSettings.oScroll, oInit, [ 275 [ "sScrollX", "sX" ], 276 [ "sScrollXInner", "sXInner" ], 277 [ "sScrollY", "sY" ], 278 [ "bScrollCollapse", "bCollapse" ] 279 ] ); 280 _fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" ); 281 282 /* Callback functions which are array driven */ 283 _fnCallbackReg( oSettings, 'aoDrawCallback', oInit.fnDrawCallback ); 284 _fnCallbackReg( oSettings, 'aoStateSaveParams', oInit.fnStateSaveParams ); 285 _fnCallbackReg( oSettings, 'aoStateLoadParams', oInit.fnStateLoadParams ); 286 _fnCallbackReg( oSettings, 'aoStateLoaded', oInit.fnStateLoaded ); 287 _fnCallbackReg( oSettings, 'aoRowCallback', oInit.fnRowCallback ); 288 _fnCallbackReg( oSettings, 'aoRowCreatedCallback', oInit.fnCreatedRow ); 289 _fnCallbackReg( oSettings, 'aoHeaderCallback', oInit.fnHeaderCallback ); 290 _fnCallbackReg( oSettings, 'aoFooterCallback', oInit.fnFooterCallback ); 291 _fnCallbackReg( oSettings, 'aoInitComplete', oInit.fnInitComplete ); 292 _fnCallbackReg( oSettings, 'aoPreDrawCallback', oInit.fnPreDrawCallback ); 293 294 oSettings.rowIdFn = _fnGetObjectDataFn( oInit.rowId ); 295 296 /* Browser support detection */ 297 _fnBrowserDetect( oSettings ); 298 299 var oClasses = oSettings.oClasses; 300 301 $.extend( oClasses, DataTable.ext.classes, oInit.oClasses ); 302 $this.addClass( oClasses.table ); 303 304 if (! oSettings.oFeatures.bPaginate) { 305 oInit.iDisplayStart = 0; 306 } 307 308 if ( oSettings.iInitDisplayStart === undefined ) 309 { 310 /* Display start point, taking into account the save saving */ 311 oSettings.iInitDisplayStart = oInit.iDisplayStart; 312 oSettings._iDisplayStart = oInit.iDisplayStart; 313 } 314 315 /* Language definitions */ 316 var oLanguage = oSettings.oLanguage; 317 $.extend( true, oLanguage, oInit.oLanguage ); 318 319 if ( oLanguage.sUrl ) 320 { 321 /* Get the language definitions from a file - because this Ajax call makes the language 322 * get async to the remainder of this function we use bInitHandedOff to indicate that 323 * _fnInitialise will be fired by the returned Ajax handler, rather than the constructor 324 */ 325 $.ajax( { 326 dataType: 'json', 327 url: oLanguage.sUrl, 328 success: function ( json ) { 329 _fnCamelToHungarian( defaults.oLanguage, json ); 330 $.extend( true, oLanguage, json, oSettings.oInit.oLanguage ); 331 332 _fnCallbackFire( oSettings, null, 'i18n', [oSettings], true); 333 _fnInitialise( oSettings ); 334 }, 335 error: function () { 336 // Error occurred loading language file 337 _fnLog( oSettings, 0, 'i18n file loading error', 21 ); 338 339 // continue on as best we can 340 _fnInitialise( oSettings ); 341 } 342 } ); 343 bInitHandedOff = true; 344 } 345 else { 346 _fnCallbackFire( oSettings, null, 'i18n', [oSettings]); 347 } 348 349 /* 350 * Columns 351 * See if we should load columns automatically or use defined ones 352 */ 353 var columnsInit = []; 354 var thead = this.getElementsByTagName('thead'); 355 var initHeaderLayout = _fnDetectHeader( oSettings, thead[0] ); 356 357 // If we don't have a columns array, then generate one with nulls 358 if ( oInit.aoColumns ) { 359 columnsInit = oInit.aoColumns; 360 } 361 else if ( initHeaderLayout.length ) { 362 for ( i=0, iLen=initHeaderLayout[0].length ; i<iLen ; i++ ) { 363 columnsInit.push( null ); 364 } 365 } 366 367 // Add the columns 368 for ( i=0, iLen=columnsInit.length ; i<iLen ; i++ ) { 369 _fnAddColumn( oSettings ); 370 } 371 372 // Apply the column definitions 373 _fnApplyColumnDefs( oSettings, oInit.aoColumnDefs, columnsInit, initHeaderLayout, function (iCol, oDef) { 374 _fnColumnOptions( oSettings, iCol, oDef ); 375 } ); 376 377 /* HTML5 attribute detection - build an mData object automatically if the 378 * attributes are found 379 */ 380 var rowOne = $this.children('tbody').find('tr').eq(0); 381 382 if ( rowOne.length ) { 383 var a = function ( cell, name ) { 384 return cell.getAttribute( 'data-'+name ) !== null ? name : null; 385 }; 386 387 $( rowOne[0] ).children('th, td').each( function (i, cell) { 388 var col = oSettings.aoColumns[i]; 389 390 if (! col) { 391 _fnLog( oSettings, 0, 'Incorrect column count', 18 ); 392 } 393 394 if ( col.mData === i ) { 395 var sort = a( cell, 'sort' ) || a( cell, 'order' ); 396 var filter = a( cell, 'filter' ) || a( cell, 'search' ); 397 398 if ( sort !== null || filter !== null ) { 399 col.mData = { 400 _: i+'.display', 401 sort: sort !== null ? i+'.@data-'+sort : undefined, 402 type: sort !== null ? i+'.@data-'+sort : undefined, 403 filter: filter !== null ? i+'.@data-'+filter : undefined 404 }; 405 col._isArrayHost = true; 406 407 _fnColumnOptions( oSettings, i ); 408 } 409 } 410 } ); 411 } 412 413 var features = oSettings.oFeatures; 414 var loadedInit = function () { 415 /* 416 * Sorting 417 * @todo For modularisation (1.11) this needs to do into a sort start up handler 418 */ 419 420 // If aaSorting is not defined, then we use the first indicator in asSorting 421 // in case that has been altered, so the default sort reflects that option 422 if ( oInit.aaSorting === undefined ) { 423 var sorting = oSettings.aaSorting; 424 for ( i=0, iLen=sorting.length ; i<iLen ; i++ ) { 425 sorting[i][1] = oSettings.aoColumns[ i ].asSorting[0]; 426 } 427 } 428 429 /* Do a first pass on the sorting classes (allows any size changes to be taken into 430 * account, and also will apply sorting disabled classes if disabled 431 */ 432 _fnSortingClasses( oSettings ); 433 434 _fnCallbackReg( oSettings, 'aoDrawCallback', function () { 435 if ( oSettings.bSorted || _fnDataSource( oSettings ) === 'ssp' || features.bDeferRender ) { 436 _fnSortingClasses( oSettings ); 437 } 438 } ); 439 440 441 /* 442 * Final init 443 * Cache the header, body and footer as required, creating them if needed 444 */ 445 var caption = $this.children('caption'); 446 447 if ( oSettings.caption ) { 448 if ( caption.length === 0 ) { 449 caption = $('<caption/>').appendTo( $this ); 450 } 451 452 caption.html( oSettings.caption ); 453 } 454 455 // Store the caption side, so we can remove the element from the document 456 // when creating the element 457 if (caption.length) { 458 caption[0]._captionSide = caption.css('caption-side'); 459 oSettings.captionNode = caption[0]; 460 } 461 462 if ( thead.length === 0 ) { 463 thead = $('<thead/>').appendTo($this); 464 } 465 oSettings.nTHead = thead[0]; 466 $('tr', thead).addClass(oClasses.thead.row); 467 468 var tbody = $this.children('tbody'); 469 if ( tbody.length === 0 ) { 470 tbody = $('<tbody/>').insertAfter(thead); 471 } 472 oSettings.nTBody = tbody[0]; 473 474 var tfoot = $this.children('tfoot'); 475 if ( tfoot.length === 0 ) { 476 // If we are a scrolling table, and no footer has been given, then we need to create 477 // a tfoot element for the caption element to be appended to 478 tfoot = $('<tfoot/>').appendTo($this); 479 } 480 oSettings.nTFoot = tfoot[0]; 481 $('tr', tfoot).addClass(oClasses.tfoot.row); 482 483 // Check if there is data passing into the constructor 484 if ( oInit.aaData ) { 485 for ( i=0 ; i<oInit.aaData.length ; i++ ) { 486 _fnAddData( oSettings, oInit.aaData[ i ] ); 487 } 488 } 489 else if ( _fnDataSource( oSettings ) == 'dom' ) { 490 // Grab the data from the page 491 _fnAddTr( oSettings, $(oSettings.nTBody).children('tr') ); 492 } 493 494 /* Copy the data index array */ 495 oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); 496 497 /* Initialisation complete - table can be drawn */ 498 oSettings.bInitialised = true; 499 500 /* Check if we need to initialise the table (it might not have been handed off to the 501 * language processor) 502 */ 503 if ( bInitHandedOff === false ) { 504 _fnInitialise( oSettings ); 505 } 506 }; 507 508 /* Must be done after everything which can be overridden by the state saving! */ 509 _fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState ); 510 511 if ( oInit.bStateSave ) 512 { 513 features.bStateSave = true; 514 _fnLoadState( oSettings, oInit, loadedInit ); 515 } 516 else { 517 loadedInit(); 518 } 519 520 } ); 521 _that = null; 522 return this; 523 }; 524 525 526 527 /** 528 * DataTables extensions 529 * 530 * This namespace acts as a collection area for plug-ins that can be used to 531 * extend DataTables capabilities. Indeed many of the build in methods 532 * use this method to provide their own capabilities (sorting methods for 533 * example). 534 * 535 * Note that this namespace is aliased to `jQuery.fn.dataTableExt` for legacy 536 * reasons 537 * 538 * @namespace 539 */ 540 DataTable.ext = _ext = { 541 /** 542 * Buttons. For use with the Buttons extension for DataTables. This is 543 * defined here so other extensions can define buttons regardless of load 544 * order. It is _not_ used by DataTables core. 545 * 546 * @type object 547 * @default {} 548 */ 549 buttons: {}, 550 551 552 /** 553 * Element class names 554 * 555 * @type object 556 * @default {} 557 */ 558 classes: {}, 559 560 561 /** 562 * DataTables build type (expanded by the download builder) 563 * 564 * @type string 565 */ 566 builder: "bs5/dt-2.0.8", 567 568 569 /** 570 * Error reporting. 571 * 572 * How should DataTables report an error. Can take the value 'alert', 573 * 'throw', 'none' or a function. 574 * 575 * @type string|function 576 * @default alert 577 */ 578 errMode: "alert", 579 580 581 /** 582 * Legacy so v1 plug-ins don't throw js errors on load 583 */ 584 feature: [], 585 586 /** 587 * Feature plug-ins. 588 * 589 * This is an object of callbacks which provide the features for DataTables 590 * to be initialised via the `layout` option. 591 */ 592 features: {}, 593 594 595 /** 596 * Row searching. 597 * 598 * This method of searching is complimentary to the default type based 599 * searching, and a lot more comprehensive as it allows you complete control 600 * over the searching logic. Each element in this array is a function 601 * (parameters described below) that is called for every row in the table, 602 * and your logic decides if it should be included in the searching data set 603 * or not. 604 * 605 * Searching functions have the following input parameters: 606 * 607 * 1. `{object}` DataTables settings object: see 608 * {@link DataTable.models.oSettings} 609 * 2. `{array|object}` Data for the row to be processed (same as the 610 * original format that was passed in as the data source, or an array 611 * from a DOM data source 612 * 3. `{int}` Row index ({@link DataTable.models.oSettings.aoData}), which 613 * can be useful to retrieve the `TR` element if you need DOM interaction. 614 * 615 * And the following return is expected: 616 * 617 * * {boolean} Include the row in the searched result set (true) or not 618 * (false) 619 * 620 * Note that as with the main search ability in DataTables, technically this 621 * is "filtering", since it is subtractive. However, for consistency in 622 * naming we call it searching here. 623 * 624 * @type array 625 * @default [] 626 * 627 * @example 628 * // The following example shows custom search being applied to the 629 * // fourth column (i.e. the data[3] index) based on two input values 630 * // from the end-user, matching the data in a certain range. 631 * $.fn.dataTable.ext.search.push( 632 * function( settings, data, dataIndex ) { 633 * var min = document.getElementById('min').value * 1; 634 * var max = document.getElementById('max').value * 1; 635 * var version = data[3] == "-" ? 0 : data[3]*1; 636 * 637 * if ( min == "" && max == "" ) { 638 * return true; 639 * } 640 * else if ( min == "" && version < max ) { 641 * return true; 642 * } 643 * else if ( min < version && "" == max ) { 644 * return true; 645 * } 646 * else if ( min < version && version < max ) { 647 * return true; 648 * } 649 * return false; 650 * } 651 * ); 652 */ 653 search: [], 654 655 656 /** 657 * Selector extensions 658 * 659 * The `selector` option can be used to extend the options available for the 660 * selector modifier options (`selector-modifier` object data type) that 661 * each of the three built in selector types offer (row, column and cell + 662 * their plural counterparts). For example the Select extension uses this 663 * mechanism to provide an option to select only rows, columns and cells 664 * that have been marked as selected by the end user (`{selected: true}`), 665 * which can be used in conjunction with the existing built in selector 666 * options. 667 * 668 * Each property is an array to which functions can be pushed. The functions 669 * take three attributes: 670 * 671 * * Settings object for the host table 672 * * Options object (`selector-modifier` object type) 673 * * Array of selected item indexes 674 * 675 * The return is an array of the resulting item indexes after the custom 676 * selector has been applied. 677 * 678 * @type object 679 */ 680 selector: { 681 cell: [], 682 column: [], 683 row: [] 684 }, 685 686 687 /** 688 * Legacy configuration options. Enable and disable legacy options that 689 * are available in DataTables. 690 * 691 * @type object 692 */ 693 legacy: { 694 /** 695 * Enable / disable DataTables 1.9 compatible server-side processing 696 * requests 697 * 698 * @type boolean 699 * @default null 700 */ 701 ajax: null 702 }, 703 704 705 /** 706 * Pagination plug-in methods. 707 * 708 * Each entry in this object is a function and defines which buttons should 709 * be shown by the pagination rendering method that is used for the table: 710 * {@link DataTable.ext.renderer.pageButton}. The renderer addresses how the 711 * buttons are displayed in the document, while the functions here tell it 712 * what buttons to display. This is done by returning an array of button 713 * descriptions (what each button will do). 714 * 715 * Pagination types (the four built in options and any additional plug-in 716 * options defined here) can be used through the `paginationType` 717 * initialisation parameter. 718 * 719 * The functions defined take two parameters: 720 * 721 * 1. `{int} page` The current page index 722 * 2. `{int} pages` The number of pages in the table 723 * 724 * Each function is expected to return an array where each element of the 725 * array can be one of: 726 * 727 * * `first` - Jump to first page when activated 728 * * `last` - Jump to last page when activated 729 * * `previous` - Show previous page when activated 730 * * `next` - Show next page when activated 731 * * `{int}` - Show page of the index given 732 * * `{array}` - A nested array containing the above elements to add a 733 * containing 'DIV' element (might be useful for styling). 734 * 735 * Note that DataTables v1.9- used this object slightly differently whereby 736 * an object with two functions would be defined for each plug-in. That 737 * ability is still supported by DataTables 1.10+ to provide backwards 738 * compatibility, but this option of use is now decremented and no longer 739 * documented in DataTables 1.10+. 740 * 741 * @type object 742 * @default {} 743 * 744 * @example 745 * // Show previous, next and current page buttons only 746 * $.fn.dataTableExt.oPagination.current = function ( page, pages ) { 747 * return [ 'previous', page, 'next' ]; 748 * }; 749 */ 750 pager: {}, 751 752 753 renderer: { 754 pageButton: {}, 755 header: {} 756 }, 757 758 759 /** 760 * Ordering plug-ins - custom data source 761 * 762 * The extension options for ordering of data available here is complimentary 763 * to the default type based ordering that DataTables typically uses. It 764 * allows much greater control over the the data that is being used to 765 * order a column, but is necessarily therefore more complex. 766 * 767 * This type of ordering is useful if you want to do ordering based on data 768 * live from the DOM (for example the contents of an 'input' element) rather 769 * than just the static string that DataTables knows of. 770 * 771 * The way these plug-ins work is that you create an array of the values you 772 * wish to be ordering for the column in question and then return that 773 * array. The data in the array much be in the index order of the rows in 774 * the table (not the currently ordering order!). Which order data gathering 775 * function is run here depends on the `dt-init columns.orderDataType` 776 * parameter that is used for the column (if any). 777 * 778 * The functions defined take two parameters: 779 * 780 * 1. `{object}` DataTables settings object: see 781 * {@link DataTable.models.oSettings} 782 * 2. `{int}` Target column index 783 * 784 * Each function is expected to return an array: 785 * 786 * * `{array}` Data for the column to be ordering upon 787 * 788 * @type array 789 * 790 * @example 791 * // Ordering using `input` node values 792 * $.fn.dataTable.ext.order['dom-text'] = function ( settings, col ) 793 * { 794 * return this.api().column( col, {order:'index'} ).nodes().map( function ( td, i ) { 795 * return $('input', td).val(); 796 * } ); 797 * } 798 */ 799 order: {}, 800 801 802 /** 803 * Type based plug-ins. 804 * 805 * Each column in DataTables has a type assigned to it, either by automatic 806 * detection or by direct assignment using the `type` option for the column. 807 * The type of a column will effect how it is ordering and search (plug-ins 808 * can also make use of the column type if required). 809 * 810 * @namespace 811 */ 812 type: { 813 /** 814 * Automatic column class assignment 815 */ 816 className: {}, 817 818 /** 819 * Type detection functions. 820 * 821 * The functions defined in this object are used to automatically detect 822 * a column's type, making initialisation of DataTables super easy, even 823 * when complex data is in the table. 824 * 825 * The functions defined take two parameters: 826 * 827 * 1. `{*}` Data from the column cell to be analysed 828 * 2. `{settings}` DataTables settings object. This can be used to 829 * perform context specific type detection - for example detection 830 * based on language settings such as using a comma for a decimal 831 * place. Generally speaking the options from the settings will not 832 * be required 833 * 834 * Each function is expected to return: 835 * 836 * * `{string|null}` Data type detected, or null if unknown (and thus 837 * pass it on to the other type detection functions. 838 * 839 * @type array 840 * 841 * @example 842 * // Currency type detection plug-in: 843 * $.fn.dataTable.ext.type.detect.push( 844 * function ( data, settings ) { 845 * // Check the numeric part 846 * if ( ! data.substring(1).match(/[0-9]/) ) { 847 * return null; 848 * } 849 * 850 * // Check prefixed by currency 851 * if ( data.charAt(0) == '$' || data.charAt(0) == '£' ) { 852 * return 'currency'; 853 * } 854 * return null; 855 * } 856 * ); 857 */ 858 detect: [], 859 860 /** 861 * Automatic renderer assignment 862 */ 863 render: {}, 864 865 866 /** 867 * Type based search formatting. 868 * 869 * The type based searching functions can be used to pre-format the 870 * data to be search on. For example, it can be used to strip HTML 871 * tags or to de-format telephone numbers for numeric only searching. 872 * 873 * Note that is a search is not defined for a column of a given type, 874 * no search formatting will be performed. 875 * 876 * Pre-processing of searching data plug-ins - When you assign the sType 877 * for a column (or have it automatically detected for you by DataTables 878 * or a type detection plug-in), you will typically be using this for 879 * custom sorting, but it can also be used to provide custom searching 880 * by allowing you to pre-processing the data and returning the data in 881 * the format that should be searched upon. This is done by adding 882 * functions this object with a parameter name which matches the sType 883 * for that target column. This is the corollary of <i>afnSortData</i> 884 * for searching data. 885 * 886 * The functions defined take a single parameter: 887 * 888 * 1. `{*}` Data from the column cell to be prepared for searching 889 * 890 * Each function is expected to return: 891 * 892 * * `{string|null}` Formatted string that will be used for the searching. 893 * 894 * @type object 895 * @default {} 896 * 897 * @example 898 * $.fn.dataTable.ext.type.search['title-numeric'] = function ( d ) { 899 * return d.replace(/\n/g," ").replace( /<.*?>/g, "" ); 900 * } 901 */ 902 search: {}, 903 904 905 /** 906 * Type based ordering. 907 * 908 * The column type tells DataTables what ordering to apply to the table 909 * when a column is sorted upon. The order for each type that is defined, 910 * is defined by the functions available in this object. 911 * 912 * Each ordering option can be described by three properties added to 913 * this object: 914 * 915 * * `{type}-pre` - Pre-formatting function 916 * * `{type}-asc` - Ascending order function 917 * * `{type}-desc` - Descending order function 918 * 919 * All three can be used together, only `{type}-pre` or only 920 * `{type}-asc` and `{type}-desc` together. It is generally recommended 921 * that only `{type}-pre` is used, as this provides the optimal 922 * implementation in terms of speed, although the others are provided 923 * for compatibility with existing Javascript sort functions. 924 * 925 * `{type}-pre`: Functions defined take a single parameter: 926 * 927 * 1. `{*}` Data from the column cell to be prepared for ordering 928 * 929 * And return: 930 * 931 * * `{*}` Data to be sorted upon 932 * 933 * `{type}-asc` and `{type}-desc`: Functions are typical Javascript sort 934 * functions, taking two parameters: 935 * 936 * 1. `{*}` Data to compare to the second parameter 937 * 2. `{*}` Data to compare to the first parameter 938 * 939 * And returning: 940 * 941 * * `{*}` Ordering match: <0 if first parameter should be sorted lower 942 * than the second parameter, ===0 if the two parameters are equal and 943 * >0 if the first parameter should be sorted height than the second 944 * parameter. 945 * 946 * @type object 947 * @default {} 948 * 949 * @example 950 * // Numeric ordering of formatted numbers with a pre-formatter 951 * $.extend( $.fn.dataTable.ext.type.order, { 952 * "string-pre": function(x) { 953 * a = (a === "-" || a === "") ? 0 : a.replace( /[^\d\-\.]/g, "" ); 954 * return parseFloat( a ); 955 * } 956 * } ); 957 * 958 * @example 959 * // Case-sensitive string ordering, with no pre-formatting method 960 * $.extend( $.fn.dataTable.ext.order, { 961 * "string-case-asc": function(x,y) { 962 * return ((x < y) ? -1 : ((x > y) ? 1 : 0)); 963 * }, 964 * "string-case-desc": function(x,y) { 965 * return ((x < y) ? 1 : ((x > y) ? -1 : 0)); 966 * } 967 * } ); 968 */ 969 order: {} 970 }, 971 972 /** 973 * Unique DataTables instance counter 974 * 975 * @type int 976 * @private 977 */ 978 _unique: 0, 979 980 981 // 982 // Depreciated 983 // The following properties are retained for backwards compatibility only. 984 // The should not be used in new projects and will be removed in a future 985 // version 986 // 987 988 /** 989 * Version check function. 990 * @type function 991 * @depreciated Since 1.10 992 */ 993 fnVersionCheck: DataTable.fnVersionCheck, 994 995 996 /** 997 * Index for what 'this' index API functions should use 998 * @type int 999 * @deprecated Since v1.10 1000 */ 1001 iApiIndex: 0, 1002 1003 1004 /** 1005 * Software version 1006 * @type string 1007 * @deprecated Since v1.10 1008 */ 1009 sVersion: DataTable.version 1010 }; 1011 1012 1013 // 1014 // Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts 1015 // 1016 $.extend( _ext, { 1017 afnFiltering: _ext.search, 1018 aTypes: _ext.type.detect, 1019 ofnSearch: _ext.type.search, 1020 oSort: _ext.type.order, 1021 afnSortData: _ext.order, 1022 aoFeatures: _ext.feature, 1023 oStdClasses: _ext.classes, 1024 oPagination: _ext.pager 1025 } ); 1026 1027 1028 $.extend( DataTable.ext.classes, { 1029 container: 'dt-container', 1030 empty: { 1031 row: 'dt-empty' 1032 }, 1033 info: { 1034 container: 'dt-info' 1035 }, 1036 length: { 1037 container: 'dt-length', 1038 select: 'dt-input' 1039 }, 1040 order: { 1041 canAsc: 'dt-orderable-asc', 1042 canDesc: 'dt-orderable-desc', 1043 isAsc: 'dt-ordering-asc', 1044 isDesc: 'dt-ordering-desc', 1045 none: 'dt-orderable-none', 1046 position: 'sorting_' 1047 }, 1048 processing: { 1049 container: 'dt-processing' 1050 }, 1051 scrolling: { 1052 body: 'dt-scroll-body', 1053 container: 'dt-scroll', 1054 footer: { 1055 self: 'dt-scroll-foot', 1056 inner: 'dt-scroll-footInner' 1057 }, 1058 header: { 1059 self: 'dt-scroll-head', 1060 inner: 'dt-scroll-headInner' 1061 } 1062 }, 1063 search: { 1064 container: 'dt-search', 1065 input: 'dt-input' 1066 }, 1067 table: 'dataTable', 1068 tbody: { 1069 cell: '', 1070 row: '' 1071 }, 1072 thead: { 1073 cell: '', 1074 row: '' 1075 }, 1076 tfoot: { 1077 cell: '', 1078 row: '' 1079 }, 1080 paging: { 1081 active: 'current', 1082 button: 'dt-paging-button', 1083 container: 'dt-paging', 1084 disabled: 'disabled' 1085 } 1086 } ); 1087 1088 1089 /* 1090 * It is useful to have variables which are scoped locally so only the 1091 * DataTables functions can access them and they don't leak into global space. 1092 * At the same time these functions are often useful over multiple files in the 1093 * core and API, so we list, or at least document, all variables which are used 1094 * by DataTables as private variables here. This also ensures that there is no 1095 * clashing of variable names and that they can easily referenced for reuse. 1096 */ 1097 1098 1099 // Defined else where 1100 // _selector_run 1101 // _selector_opts 1102 // _selector_row_indexes 1103 1104 var _ext; // DataTable.ext 1105 var _Api; // DataTable.Api 1106 var _api_register; // DataTable.Api.register 1107 var _api_registerPlural; // DataTable.Api.registerPlural 1108 1109 var _re_dic = {}; 1110 var _re_new_lines = /[\r\n\u2028]/g; 1111 var _re_html = /<([^>]*>)/g; 1112 var _max_str_len = Math.pow(2, 28); 1113 1114 // This is not strict ISO8601 - Date.parse() is quite lax, although 1115 // implementations differ between browsers. 1116 var _re_date = /^\d{2,4}[./-]\d{1,2}[./-]\d{1,2}([T ]{1}\d{1,2}[:.]\d{2}([.:]\d{2})?)?$/; 1117 1118 // Escape regular expression special characters 1119 var _re_escape_regex = new RegExp( '(\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ].join('|\\') + ')', 'g' ); 1120 1121 // https://en.wikipedia.org/wiki/Foreign_exchange_market 1122 // - \u20BD - Russian ruble. 1123 // - \u20a9 - South Korean Won 1124 // - \u20BA - Turkish Lira 1125 // - \u20B9 - Indian Rupee 1126 // - R - Brazil (R$) and South Africa 1127 // - fr - Swiss Franc 1128 // - kr - Swedish krona, Norwegian krone and Danish krone 1129 // - \u2009 is thin space and \u202F is narrow no-break space, both used in many 1130 // - Ƀ - Bitcoin 1131 // - Ξ - Ethereum 1132 // standards as thousands separators. 1133 var _re_formatted_numeric = /['\u00A0,$£€¥%\u2009\u202F\u20BD\u20a9\u20BArfkɃΞ]/gi; 1134 1135 1136 var _empty = function ( d ) { 1137 return !d || d === true || d === '-' ? true : false; 1138 }; 1139 1140 1141 var _intVal = function ( s ) { 1142 var integer = parseInt( s, 10 ); 1143 return !isNaN(integer) && isFinite(s) ? integer : null; 1144 }; 1145 1146 // Convert from a formatted number with characters other than `.` as the 1147 // decimal place, to a Javascript number 1148 var _numToDecimal = function ( num, decimalPoint ) { 1149 // Cache created regular expressions for speed as this function is called often 1150 if ( ! _re_dic[ decimalPoint ] ) { 1151 _re_dic[ decimalPoint ] = new RegExp( _fnEscapeRegex( decimalPoint ), 'g' ); 1152 } 1153 return typeof num === 'string' && decimalPoint !== '.' ? 1154 num.replace( /\./g, '' ).replace( _re_dic[ decimalPoint ], '.' ) : 1155 num; 1156 }; 1157 1158 1159 var _isNumber = function ( d, decimalPoint, formatted ) { 1160 var type = typeof d; 1161 var strType = type === 'string'; 1162 1163 if ( type === 'number' || type === 'bigint') { 1164 return true; 1165 } 1166 1167 // If empty return immediately so there must be a number if it is a 1168 // formatted string (this stops the string "k", or "kr", etc being detected 1169 // as a formatted number for currency 1170 if ( _empty( d ) ) { 1171 return true; 1172 } 1173 1174 if ( decimalPoint && strType ) { 1175 d = _numToDecimal( d, decimalPoint ); 1176 } 1177 1178 if ( formatted && strType ) { 1179 d = d.replace( _re_formatted_numeric, '' ); 1180 } 1181 1182 return !isNaN( parseFloat(d) ) && isFinite( d ); 1183 }; 1184 1185 1186 // A string without HTML in it can be considered to be HTML still 1187 var _isHtml = function ( d ) { 1188 return _empty( d ) || typeof d === 'string'; 1189 }; 1190 1191 // Is a string a number surrounded by HTML? 1192 var _htmlNumeric = function ( d, decimalPoint, formatted ) { 1193 if ( _empty( d ) ) { 1194 return true; 1195 } 1196 1197 // input and select strings mean that this isn't just a number 1198 if (typeof d === 'string' && d.match(/<(input|select)/i)) { 1199 return null; 1200 } 1201 1202 var html = _isHtml( d ); 1203 return ! html ? 1204 null : 1205 _isNumber( _stripHtml( d ), decimalPoint, formatted ) ? 1206 true : 1207 null; 1208 }; 1209 1210 1211 var _pluck = function ( a, prop, prop2 ) { 1212 var out = []; 1213 var i=0, ien=a.length; 1214 1215 // Could have the test in the loop for slightly smaller code, but speed 1216 // is essential here 1217 if ( prop2 !== undefined ) { 1218 for ( ; i<ien ; i++ ) { 1219 if ( a[i] && a[i][ prop ] ) { 1220 out.push( a[i][ prop ][ prop2 ] ); 1221 } 1222 } 1223 } 1224 else { 1225 for ( ; i<ien ; i++ ) { 1226 if ( a[i] ) { 1227 out.push( a[i][ prop ] ); 1228 } 1229 } 1230 } 1231 1232 return out; 1233 }; 1234 1235 1236 // Basically the same as _pluck, but rather than looping over `a` we use `order` 1237 // as the indexes to pick from `a` 1238 var _pluck_order = function ( a, order, prop, prop2 ) 1239 { 1240 var out = []; 1241 var i=0, ien=order.length; 1242 1243 // Could have the test in the loop for slightly smaller code, but speed 1244 // is essential here 1245 if ( prop2 !== undefined ) { 1246 for ( ; i<ien ; i++ ) { 1247 if ( a[ order[i] ][ prop ] ) { 1248 out.push( a[ order[i] ][ prop ][ prop2 ] ); 1249 } 1250 } 1251 } 1252 else { 1253 for ( ; i<ien ; i++ ) { 1254 if ( a[ order[i] ] ) { 1255 out.push( a[ order[i] ][ prop ] ); 1256 } 1257 } 1258 } 1259 1260 return out; 1261 }; 1262 1263 1264 var _range = function ( len, start ) 1265 { 1266 var out = []; 1267 var end; 1268 1269 if ( start === undefined ) { 1270 start = 0; 1271 end = len; 1272 } 1273 else { 1274 end = start; 1275 start = len; 1276 } 1277 1278 for ( var i=start ; i<end ; i++ ) { 1279 out.push( i ); 1280 } 1281 1282 return out; 1283 }; 1284 1285 1286 var _removeEmpty = function ( a ) 1287 { 1288 var out = []; 1289 1290 for ( var i=0, ien=a.length ; i<ien ; i++ ) { 1291 if ( a[i] ) { // careful - will remove all falsy values! 1292 out.push( a[i] ); 1293 } 1294 } 1295 1296 return out; 1297 }; 1298 1299 // Replaceable function in api.util 1300 var _stripHtml = function (input) { 1301 // Irrelevant check to workaround CodeQL's false positive on the regex 1302 if (input.length > _max_str_len) { 1303 throw new Error('Exceeded max str len'); 1304 } 1305 1306 var previous; 1307 1308 input = input.replace(_re_html, ''); // Complete tags 1309 1310 // Safety for incomplete script tag - use do / while to ensure that 1311 // we get all instances 1312 do { 1313 previous = input; 1314 input = input.replace(/<script/i, ''); 1315 } while (input !== previous); 1316 1317 return previous; 1318 }; 1319 1320 // Replaceable function in api.util 1321 var _escapeHtml = function ( d ) { 1322 if (Array.isArray(d)) { 1323 d = d.join(','); 1324 } 1325 1326 return typeof d === 'string' ? 1327 d 1328 .replace(/&/g, '&') 1329 .replace(/</g, '<') 1330 .replace(/>/g, '>') 1331 .replace(/"/g, '"') : 1332 d; 1333 }; 1334 1335 // Remove diacritics from a string by decomposing it and then removing 1336 // non-ascii characters 1337 var _normalize = function (str, both) { 1338 if (typeof str !== 'string') { 1339 return str; 1340 } 1341 1342 // It is faster to just run `normalize` than it is to check if 1343 // we need to with a regex! 1344 var res = str.normalize("NFD"); 1345 1346 // Equally, here we check if a regex is needed or not 1347 return res.length !== str.length 1348 ? (both === true ? str + ' ' : '' ) + res.replace(/[\u0300-\u036f]/g, "") 1349 : res; 1350 } 1351 1352 /** 1353 * Determine if all values in the array are unique. This means we can short 1354 * cut the _unique method at the cost of a single loop. A sorted array is used 1355 * to easily check the values. 1356 * 1357 * @param {array} src Source array 1358 * @return {boolean} true if all unique, false otherwise 1359 * @ignore 1360 */ 1361 var _areAllUnique = function ( src ) { 1362 if ( src.length < 2 ) { 1363 return true; 1364 } 1365 1366 var sorted = src.slice().sort(); 1367 var last = sorted[0]; 1368 1369 for ( var i=1, ien=sorted.length ; i<ien ; i++ ) { 1370 if ( sorted[i] === last ) { 1371 return false; 1372 } 1373 1374 last = sorted[i]; 1375 } 1376 1377 return true; 1378 }; 1379 1380 1381 /** 1382 * Find the unique elements in a source array. 1383 * 1384 * @param {array} src Source array 1385 * @return {array} Array of unique items 1386 * @ignore 1387 */ 1388 var _unique = function ( src ) 1389 { 1390 if (Array.from && Set) { 1391 return Array.from(new Set(src)); 1392 } 1393 1394 if ( _areAllUnique( src ) ) { 1395 return src.slice(); 1396 } 1397 1398 // A faster unique method is to use object keys to identify used values, 1399 // but this doesn't work with arrays or objects, which we must also 1400 // consider. See jsperf.app/compare-array-unique-versions/4 for more 1401 // information. 1402 var 1403 out = [], 1404 val, 1405 i, ien=src.length, 1406 j, k=0; 1407 1408 again: for ( i=0 ; i<ien ; i++ ) { 1409 val = src[i]; 1410 1411 for ( j=0 ; j<k ; j++ ) { 1412 if ( out[j] === val ) { 1413 continue again; 1414 } 1415 } 1416 1417 out.push( val ); 1418 k++; 1419 } 1420 1421 return out; 1422 }; 1423 1424 // Surprisingly this is faster than [].concat.apply 1425 // https://jsperf.com/flatten-an-array-loop-vs-reduce/2 1426 var _flatten = function (out, val) { 1427 if (Array.isArray(val)) { 1428 for (var i=0 ; i<val.length ; i++) { 1429 _flatten(out, val[i]); 1430 } 1431 } 1432 else { 1433 out.push(val); 1434 } 1435 1436 return out; 1437 } 1438 1439 // Similar to jQuery's addClass, but use classList.add 1440 function _addClass(el, name) { 1441 if (name) { 1442 name.split(' ').forEach(function (n) { 1443 if (n) { 1444 // `add` does deduplication, so no need to check `contains` 1445 el.classList.add(n); 1446 } 1447 }); 1448 } 1449 } 1450 1451 /** 1452 * DataTables utility methods 1453 * 1454 * This namespace provides helper methods that DataTables uses internally to 1455 * create a DataTable, but which are not exclusively used only for DataTables. 1456 * These methods can be used by extension authors to save the duplication of 1457 * code. 1458 * 1459 * @namespace 1460 */ 1461 DataTable.util = { 1462 /** 1463 * Return a string with diacritic characters decomposed 1464 * @param {*} mixed Function or string to normalize 1465 * @param {*} both Return original string and the normalized string 1466 * @returns String or undefined 1467 */ 1468 diacritics: function (mixed, both) { 1469 var type = typeof mixed; 1470 1471 if (type !== 'function') { 1472 return _normalize(mixed, both); 1473 } 1474 _normalize = mixed; 1475 }, 1476 1477 /** 1478 * Debounce a function 1479 * 1480 * @param {function} fn Function to be called 1481 * @param {integer} freq Call frequency in mS 1482 * @return {function} Wrapped function 1483 */ 1484 debounce: function ( fn, timeout ) { 1485 var timer; 1486 1487 return function () { 1488 var that = this; 1489 var args = arguments; 1490 1491 clearTimeout(timer); 1492 1493 timer = setTimeout( function () { 1494 fn.apply(that, args); 1495 }, timeout || 250 ); 1496 }; 1497 }, 1498 1499 /** 1500 * Throttle the calls to a function. Arguments and context are maintained 1501 * for the throttled function. 1502 * 1503 * @param {function} fn Function to be called 1504 * @param {integer} freq Call frequency in mS 1505 * @return {function} Wrapped function 1506 */ 1507 throttle: function ( fn, freq ) { 1508 var 1509 frequency = freq !== undefined ? freq : 200, 1510 last, 1511 timer; 1512 1513 return function () { 1514 var 1515 that = this, 1516 now = +new Date(), 1517 args = arguments; 1518 1519 if ( last && now < last + frequency ) { 1520 clearTimeout( timer ); 1521 1522 timer = setTimeout( function () { 1523 last = undefined; 1524 fn.apply( that, args ); 1525 }, frequency ); 1526 } 1527 else { 1528 last = now; 1529 fn.apply( that, args ); 1530 } 1531 }; 1532 }, 1533 1534 /** 1535 * Escape a string such that it can be used in a regular expression 1536 * 1537 * @param {string} val string to escape 1538 * @returns {string} escaped string 1539 */ 1540 escapeRegex: function ( val ) { 1541 return val.replace( _re_escape_regex, '\\$1' ); 1542 }, 1543 1544 /** 1545 * Create a function that will write to a nested object or array 1546 * @param {*} source JSON notation string 1547 * @returns Write function 1548 */ 1549 set: function ( source ) { 1550 if ( $.isPlainObject( source ) ) { 1551 /* Unlike get, only the underscore (global) option is used for for 1552 * setting data since we don't know the type here. This is why an object 1553 * option is not documented for `mData` (which is read/write), but it is 1554 * for `mRender` which is read only. 1555 */ 1556 return DataTable.util.set( source._ ); 1557 } 1558 else if ( source === null ) { 1559 // Nothing to do when the data source is null 1560 return function () {}; 1561 } 1562 else if ( typeof source === 'function' ) { 1563 return function (data, val, meta) { 1564 source( data, 'set', val, meta ); 1565 }; 1566 } 1567 else if ( 1568 typeof source === 'string' && (source.indexOf('.') !== -1 || 1569 source.indexOf('[') !== -1 || source.indexOf('(') !== -1) 1570 ) { 1571 // Like the get, we need to get data from a nested object 1572 var setData = function (data, val, src) { 1573 var a = _fnSplitObjNotation( src ), b; 1574 var aLast = a[a.length-1]; 1575 var arrayNotation, funcNotation, o, innerSrc; 1576 1577 for ( var i=0, iLen=a.length-1 ; i<iLen ; i++ ) { 1578 // Protect against prototype pollution 1579 if (a[i] === '__proto__' || a[i] === 'constructor') { 1580 throw new Error('Cannot set prototype values'); 1581 } 1582 1583 // Check if we are dealing with an array notation request 1584 arrayNotation = a[i].match(__reArray); 1585 funcNotation = a[i].match(__reFn); 1586 1587 if ( arrayNotation ) { 1588 a[i] = a[i].replace(__reArray, ''); 1589 data[ a[i] ] = []; 1590 1591 // Get the remainder of the nested object to set so we can recurse 1592 b = a.slice(); 1593 b.splice( 0, i+1 ); 1594 innerSrc = b.join('.'); 1595 1596 // Traverse each entry in the array setting the properties requested 1597 if ( Array.isArray( val ) ) { 1598 for ( var j=0, jLen=val.length ; j<jLen ; j++ ) { 1599 o = {}; 1600 setData( o, val[j], innerSrc ); 1601 data[ a[i] ].push( o ); 1602 } 1603 } 1604 else { 1605 // We've been asked to save data to an array, but it 1606 // isn't array data to be saved. Best that can be done 1607 // is to just save the value. 1608 data[ a[i] ] = val; 1609 } 1610 1611 // The inner call to setData has already traversed through the remainder 1612 // of the source and has set the data, thus we can exit here 1613 return; 1614 } 1615 else if ( funcNotation ) { 1616 // Function call 1617 a[i] = a[i].replace(__reFn, ''); 1618 data = data[ a[i] ]( val ); 1619 } 1620 1621 // If the nested object doesn't currently exist - since we are 1622 // trying to set the value - create it 1623 if ( data[ a[i] ] === null || data[ a[i] ] === undefined ) { 1624 data[ a[i] ] = {}; 1625 } 1626 data = data[ a[i] ]; 1627 } 1628 1629 // Last item in the input - i.e, the actual set 1630 if ( aLast.match(__reFn ) ) { 1631 // Function call 1632 data = data[ aLast.replace(__reFn, '') ]( val ); 1633 } 1634 else { 1635 // If array notation is used, we just want to strip it and use the property name 1636 // and assign the value. If it isn't used, then we get the result we want anyway 1637 data[ aLast.replace(__reArray, '') ] = val; 1638 } 1639 }; 1640 1641 return function (data, val) { // meta is also passed in, but not used 1642 return setData( data, val, source ); 1643 }; 1644 } 1645 else { 1646 // Array or flat object mapping 1647 return function (data, val) { // meta is also passed in, but not used 1648 data[source] = val; 1649 }; 1650 } 1651 }, 1652 1653 /** 1654 * Create a function that will read nested objects from arrays, based on JSON notation 1655 * @param {*} source JSON notation string 1656 * @returns Value read 1657 */ 1658 get: function ( source ) { 1659 if ( $.isPlainObject( source ) ) { 1660 // Build an object of get functions, and wrap them in a single call 1661 var o = {}; 1662 $.each( source, function (key, val) { 1663 if ( val ) { 1664 o[key] = DataTable.util.get( val ); 1665 } 1666 } ); 1667 1668 return function (data, type, row, meta) { 1669 var t = o[type] || o._; 1670 return t !== undefined ? 1671 t(data, type, row, meta) : 1672 data; 1673 }; 1674 } 1675 else if ( source === null ) { 1676 // Give an empty string for rendering / sorting etc 1677 return function (data) { // type, row and meta also passed, but not used 1678 return data; 1679 }; 1680 } 1681 else if ( typeof source === 'function' ) { 1682 return function (data, type, row, meta) { 1683 return source( data, type, row, meta ); 1684 }; 1685 } 1686 else if ( 1687 typeof source === 'string' && (source.indexOf('.') !== -1 || 1688 source.indexOf('[') !== -1 || source.indexOf('(') !== -1) 1689 ) { 1690 /* If there is a . in the source string then the data source is in a 1691 * nested object so we loop over the data for each level to get the next 1692 * level down. On each loop we test for undefined, and if found immediately 1693 * return. This allows entire objects to be missing and sDefaultContent to 1694 * be used if defined, rather than throwing an error 1695 */ 1696 var fetchData = function (data, type, src) { 1697 var arrayNotation, funcNotation, out, innerSrc; 1698 1699 if ( src !== "" ) { 1700 var a = _fnSplitObjNotation( src ); 1701 1702 for ( var i=0, iLen=a.length ; i<iLen ; i++ ) { 1703 // Check if we are dealing with special notation 1704 arrayNotation = a[i].match(__reArray); 1705 funcNotation = a[i].match(__reFn); 1706 1707 if ( arrayNotation ) { 1708 // Array notation 1709 a[i] = a[i].replace(__reArray, ''); 1710 1711 // Condition allows simply [] to be passed in 1712 if ( a[i] !== "" ) { 1713 data = data[ a[i] ]; 1714 } 1715 out = []; 1716 1717 // Get the remainder of the nested object to get 1718 a.splice( 0, i+1 ); 1719 innerSrc = a.join('.'); 1720 1721 // Traverse each entry in the array getting the properties requested 1722 if ( Array.isArray( data ) ) { 1723 for ( var j=0, jLen=data.length ; j<jLen ; j++ ) { 1724 out.push( fetchData( data[j], type, innerSrc ) ); 1725 } 1726 } 1727 1728 // If a string is given in between the array notation indicators, that 1729 // is used to join the strings together, otherwise an array is returned 1730 var join = arrayNotation[0].substring(1, arrayNotation[0].length-1); 1731 data = (join==="") ? out : out.join(join); 1732 1733 // The inner call to fetchData has already traversed through the remainder 1734 // of the source requested, so we exit from the loop 1735 break; 1736 } 1737 else if ( funcNotation ) { 1738 // Function call 1739 a[i] = a[i].replace(__reFn, ''); 1740 data = data[ a[i] ](); 1741 continue; 1742 } 1743 1744 if (data === null || data[ a[i] ] === null) { 1745 return null; 1746 } 1747 else if ( data === undefined || data[ a[i] ] === undefined ) { 1748 return undefined; 1749 } 1750 1751 data = data[ a[i] ]; 1752 } 1753 } 1754 1755 return data; 1756 }; 1757 1758 return function (data, type) { // row and meta also passed, but not used 1759 return fetchData( data, type, source ); 1760 }; 1761 } 1762 else { 1763 // Array or flat object mapping 1764 return function (data) { // row and meta also passed, but not used 1765 return data[source]; 1766 }; 1767 } 1768 }, 1769 1770 stripHtml: function (mixed) { 1771 var type = typeof mixed; 1772 1773 if (type === 'function') { 1774 _stripHtml = mixed; 1775 return; 1776 } 1777 else if (type === 'string') { 1778 return _stripHtml(mixed); 1779 } 1780 return mixed; 1781 }, 1782 1783 escapeHtml: function (mixed) { 1784 var type = typeof mixed; 1785 1786 if (type === 'function') { 1787 _escapeHtml = mixed; 1788 return; 1789 } 1790 else if (type === 'string' || Array.isArray(mixed)) { 1791 return _escapeHtml(mixed); 1792 } 1793 return mixed; 1794 }, 1795 1796 unique: _unique 1797 }; 1798 1799 1800 1801 /** 1802 * Create a mapping object that allows camel case parameters to be looked up 1803 * for their Hungarian counterparts. The mapping is stored in a private 1804 * parameter called `_hungarianMap` which can be accessed on the source object. 1805 * @param {object} o 1806 * @memberof DataTable#oApi 1807 */ 1808 function _fnHungarianMap ( o ) 1809 { 1810 var 1811 hungarian = 'a aa ai ao as b fn i m o s ', 1812 match, 1813 newKey, 1814 map = {}; 1815 1816 $.each( o, function (key) { 1817 match = key.match(/^([^A-Z]+?)([A-Z])/); 1818 1819 if ( match && hungarian.indexOf(match[1]+' ') !== -1 ) 1820 { 1821 newKey = key.replace( match[0], match[2].toLowerCase() ); 1822 map[ newKey ] = key; 1823 1824 if ( match[1] === 'o' ) 1825 { 1826 _fnHungarianMap( o[key] ); 1827 } 1828 } 1829 } ); 1830 1831 o._hungarianMap = map; 1832 } 1833 1834 1835 /** 1836 * Convert from camel case parameters to Hungarian, based on a Hungarian map 1837 * created by _fnHungarianMap. 1838 * @param {object} src The model object which holds all parameters that can be 1839 * mapped. 1840 * @param {object} user The object to convert from camel case to Hungarian. 1841 * @param {boolean} force When set to `true`, properties which already have a 1842 * Hungarian value in the `user` object will be overwritten. Otherwise they 1843 * won't be. 1844 * @memberof DataTable#oApi 1845 */ 1846 function _fnCamelToHungarian ( src, user, force ) 1847 { 1848 if ( ! src._hungarianMap ) { 1849 _fnHungarianMap( src ); 1850 } 1851 1852 var hungarianKey; 1853 1854 $.each( user, function (key) { 1855 hungarianKey = src._hungarianMap[ key ]; 1856 1857 if ( hungarianKey !== undefined && (force || user[hungarianKey] === undefined) ) 1858 { 1859 // For objects, we need to buzz down into the object to copy parameters 1860 if ( hungarianKey.charAt(0) === 'o' ) 1861 { 1862 // Copy the camelCase options over to the hungarian 1863 if ( ! user[ hungarianKey ] ) { 1864 user[ hungarianKey ] = {}; 1865 } 1866 $.extend( true, user[hungarianKey], user[key] ); 1867 1868 _fnCamelToHungarian( src[hungarianKey], user[hungarianKey], force ); 1869 } 1870 else { 1871 user[hungarianKey] = user[ key ]; 1872 } 1873 } 1874 } ); 1875 } 1876 1877 /** 1878 * Map one parameter onto another 1879 * @param {object} o Object to map 1880 * @param {*} knew The new parameter name 1881 * @param {*} old The old parameter name 1882 */ 1883 var _fnCompatMap = function ( o, knew, old ) { 1884 if ( o[ knew ] !== undefined ) { 1885 o[ old ] = o[ knew ]; 1886 } 1887 }; 1888 1889 1890 /** 1891 * Provide backwards compatibility for the main DT options. Note that the new 1892 * options are mapped onto the old parameters, so this is an external interface 1893 * change only. 1894 * @param {object} init Object to map 1895 */ 1896 function _fnCompatOpts ( init ) 1897 { 1898 _fnCompatMap( init, 'ordering', 'bSort' ); 1899 _fnCompatMap( init, 'orderMulti', 'bSortMulti' ); 1900 _fnCompatMap( init, 'orderClasses', 'bSortClasses' ); 1901 _fnCompatMap( init, 'orderCellsTop', 'bSortCellsTop' ); 1902 _fnCompatMap( init, 'order', 'aaSorting' ); 1903 _fnCompatMap( init, 'orderFixed', 'aaSortingFixed' ); 1904 _fnCompatMap( init, 'paging', 'bPaginate' ); 1905 _fnCompatMap( init, 'pagingType', 'sPaginationType' ); 1906 _fnCompatMap( init, 'pageLength', 'iDisplayLength' ); 1907 _fnCompatMap( init, 'searching', 'bFilter' ); 1908 1909 // Boolean initialisation of x-scrolling 1910 if ( typeof init.sScrollX === 'boolean' ) { 1911 init.sScrollX = init.sScrollX ? '100%' : ''; 1912 } 1913 if ( typeof init.scrollX === 'boolean' ) { 1914 init.scrollX = init.scrollX ? '100%' : ''; 1915 } 1916 1917 // Column search objects are in an array, so it needs to be converted 1918 // element by element 1919 var searchCols = init.aoSearchCols; 1920 1921 if ( searchCols ) { 1922 for ( var i=0, ien=searchCols.length ; i<ien ; i++ ) { 1923 if ( searchCols[i] ) { 1924 _fnCamelToHungarian( DataTable.models.oSearch, searchCols[i] ); 1925 } 1926 } 1927 } 1928 1929 // Enable search delay if server-side processing is enabled 1930 if (init.serverSide && ! init.searchDelay) { 1931 init.searchDelay = 400; 1932 } 1933 } 1934 1935 1936 /** 1937 * Provide backwards compatibility for column options. Note that the new options 1938 * are mapped onto the old parameters, so this is an external interface change 1939 * only. 1940 * @param {object} init Object to map 1941 */ 1942 function _fnCompatCols ( init ) 1943 { 1944 _fnCompatMap( init, 'orderable', 'bSortable' ); 1945 _fnCompatMap( init, 'orderData', 'aDataSort' ); 1946 _fnCompatMap( init, 'orderSequence', 'asSorting' ); 1947 _fnCompatMap( init, 'orderDataType', 'sortDataType' ); 1948 1949 // orderData can be given as an integer 1950 var dataSort = init.aDataSort; 1951 if ( typeof dataSort === 'number' && ! Array.isArray( dataSort ) ) { 1952 init.aDataSort = [ dataSort ]; 1953 } 1954 } 1955 1956 1957 /** 1958 * Browser feature detection for capabilities, quirks 1959 * @param {object} settings dataTables settings object 1960 * @memberof DataTable#oApi 1961 */ 1962 function _fnBrowserDetect( settings ) 1963 { 1964 // We don't need to do this every time DataTables is constructed, the values 1965 // calculated are specific to the browser and OS configuration which we 1966 // don't expect to change between initialisations 1967 if ( ! DataTable.__browser ) { 1968 var browser = {}; 1969 DataTable.__browser = browser; 1970 1971 // Scrolling feature / quirks detection 1972 var n = $('<div/>') 1973 .css( { 1974 position: 'fixed', 1975 top: 0, 1976 left: -1 * window.pageXOffset, // allow for scrolling 1977 height: 1, 1978 width: 1, 1979 overflow: 'hidden' 1980 } ) 1981 .append( 1982 $('<div/>') 1983 .css( { 1984 position: 'absolute', 1985 top: 1, 1986 left: 1, 1987 width: 100, 1988 overflow: 'scroll' 1989 } ) 1990 .append( 1991 $('<div/>') 1992 .css( { 1993 width: '100%', 1994 height: 10 1995 } ) 1996 ) 1997 ) 1998 .appendTo( 'body' ); 1999 2000 var outer = n.children(); 2001 var inner = outer.children(); 2002 2003 // Get scrollbar width 2004 browser.barWidth = outer[0].offsetWidth - outer[0].clientWidth; 2005 2006 // In rtl text layout, some browsers (most, but not all) will place the 2007 // scrollbar on the left, rather than the right. 2008 browser.bScrollbarLeft = Math.round( inner.offset().left ) !== 1; 2009 2010 n.remove(); 2011 } 2012 2013 $.extend( settings.oBrowser, DataTable.__browser ); 2014 settings.oScroll.iBarWidth = DataTable.__browser.barWidth; 2015 } 2016 2017 /** 2018 * Add a column to the list used for the table with default values 2019 * @param {object} oSettings dataTables settings object 2020 * @memberof DataTable#oApi 2021 */ 2022 function _fnAddColumn( oSettings ) 2023 { 2024 // Add column to aoColumns array 2025 var oDefaults = DataTable.defaults.column; 2026 var iCol = oSettings.aoColumns.length; 2027 var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, { 2028 "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol], 2029 "mData": oDefaults.mData ? oDefaults.mData : iCol, 2030 idx: iCol, 2031 searchFixed: {}, 2032 colEl: $('<col>').attr('data-dt-column', iCol) 2033 } ); 2034 oSettings.aoColumns.push( oCol ); 2035 2036 // Add search object for column specific search. Note that the `searchCols[ iCol ]` 2037 // passed into extend can be undefined. This allows the user to give a default 2038 // with only some of the parameters defined, and also not give a default 2039 var searchCols = oSettings.aoPreSearchCols; 2040 searchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch, searchCols[ iCol ] ); 2041 } 2042 2043 2044 /** 2045 * Apply options for a column 2046 * @param {object} oSettings dataTables settings object 2047 * @param {int} iCol column index to consider 2048 * @param {object} oOptions object with sType, bVisible and bSearchable etc 2049 * @memberof DataTable#oApi 2050 */ 2051 function _fnColumnOptions( oSettings, iCol, oOptions ) 2052 { 2053 var oCol = oSettings.aoColumns[ iCol ]; 2054 2055 /* User specified column options */ 2056 if ( oOptions !== undefined && oOptions !== null ) 2057 { 2058 // Backwards compatibility 2059 _fnCompatCols( oOptions ); 2060 2061 // Map camel case parameters to their Hungarian counterparts 2062 _fnCamelToHungarian( DataTable.defaults.column, oOptions, true ); 2063 2064 /* Backwards compatibility for mDataProp */ 2065 if ( oOptions.mDataProp !== undefined && !oOptions.mData ) 2066 { 2067 oOptions.mData = oOptions.mDataProp; 2068 } 2069 2070 if ( oOptions.sType ) 2071 { 2072 oCol._sManualType = oOptions.sType; 2073 } 2074 2075 // `class` is a reserved word in Javascript, so we need to provide 2076 // the ability to use a valid name for the camel case input 2077 if ( oOptions.className && ! oOptions.sClass ) 2078 { 2079 oOptions.sClass = oOptions.className; 2080 } 2081 2082 var origClass = oCol.sClass; 2083 2084 $.extend( oCol, oOptions ); 2085 _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" ); 2086 2087 // Merge class from previously defined classes with this one, rather than just 2088 // overwriting it in the extend above 2089 if (origClass !== oCol.sClass) { 2090 oCol.sClass = origClass + ' ' + oCol.sClass; 2091 } 2092 2093 /* iDataSort to be applied (backwards compatibility), but aDataSort will take 2094 * priority if defined 2095 */ 2096 if ( oOptions.iDataSort !== undefined ) 2097 { 2098 oCol.aDataSort = [ oOptions.iDataSort ]; 2099 } 2100 _fnMap( oCol, oOptions, "aDataSort" ); 2101 } 2102 2103 /* Cache the data get and set functions for speed */ 2104 var mDataSrc = oCol.mData; 2105 var mData = _fnGetObjectDataFn( mDataSrc ); 2106 2107 // The `render` option can be given as an array to access the helper rendering methods. 2108 // The first element is the rendering method to use, the rest are the parameters to pass 2109 if ( oCol.mRender && Array.isArray( oCol.mRender ) ) { 2110 var copy = oCol.mRender.slice(); 2111 var name = copy.shift(); 2112 2113 oCol.mRender = DataTable.render[name].apply(window, copy); 2114 } 2115 2116 oCol._render = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null; 2117 2118 var attrTest = function( src ) { 2119 return typeof src === 'string' && src.indexOf('@') !== -1; 2120 }; 2121 oCol._bAttrSrc = $.isPlainObject( mDataSrc ) && ( 2122 attrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter) 2123 ); 2124 oCol._setter = null; 2125 2126 oCol.fnGetData = function (rowData, type, meta) { 2127 var innerData = mData( rowData, type, undefined, meta ); 2128 2129 return oCol._render && type ? 2130 oCol._render( innerData, type, rowData, meta ) : 2131 innerData; 2132 }; 2133 oCol.fnSetData = function ( rowData, val, meta ) { 2134 return _fnSetObjectDataFn( mDataSrc )( rowData, val, meta ); 2135 }; 2136 2137 // Indicate if DataTables should read DOM data as an object or array 2138 // Used in _fnGetRowElements 2139 if ( typeof mDataSrc !== 'number' && ! oCol._isArrayHost ) { 2140 oSettings._rowReadObject = true; 2141 } 2142 2143 /* Feature sorting overrides column specific when off */ 2144 if ( !oSettings.oFeatures.bSort ) 2145 { 2146 oCol.bSortable = false; 2147 } 2148 } 2149 2150 2151 /** 2152 * Adjust the table column widths for new data. Note: you would probably want to 2153 * do a redraw after calling this function! 2154 * @param {object} settings dataTables settings object 2155 * @memberof DataTable#oApi 2156 */ 2157 function _fnAdjustColumnSizing ( settings ) 2158 { 2159 _fnCalculateColumnWidths( settings ); 2160 _fnColumnSizes( settings ); 2161 2162 var scroll = settings.oScroll; 2163 if ( scroll.sY !== '' || scroll.sX !== '') { 2164 _fnScrollDraw( settings ); 2165 } 2166 2167 _fnCallbackFire( settings, null, 'column-sizing', [settings] ); 2168 } 2169 2170 /** 2171 * Apply column sizes 2172 * 2173 * @param {*} settings DataTables settings object 2174 */ 2175 function _fnColumnSizes ( settings ) 2176 { 2177 var cols = settings.aoColumns; 2178 2179 for (var i=0 ; i<cols.length ; i++) { 2180 var width = _fnColumnsSumWidth(settings, [i], false, false); 2181 2182 cols[i].colEl.css('width', width); 2183 } 2184 } 2185 2186 2187 /** 2188 * Convert the index of a visible column to the index in the data array (take account 2189 * of hidden columns) 2190 * @param {object} oSettings dataTables settings object 2191 * @param {int} iMatch Visible column index to lookup 2192 * @returns {int} i the data index 2193 * @memberof DataTable#oApi 2194 */ 2195 function _fnVisibleToColumnIndex( oSettings, iMatch ) 2196 { 2197 var aiVis = _fnGetColumns( oSettings, 'bVisible' ); 2198 2199 return typeof aiVis[iMatch] === 'number' ? 2200 aiVis[iMatch] : 2201 null; 2202 } 2203 2204 2205 /** 2206 * Convert the index of an index in the data array and convert it to the visible 2207 * column index (take account of hidden columns) 2208 * @param {int} iMatch Column index to lookup 2209 * @param {object} oSettings dataTables settings object 2210 * @returns {int} i the data index 2211 * @memberof DataTable#oApi 2212 */ 2213 function _fnColumnIndexToVisible( oSettings, iMatch ) 2214 { 2215 var aiVis = _fnGetColumns( oSettings, 'bVisible' ); 2216 var iPos = aiVis.indexOf(iMatch); 2217 2218 return iPos !== -1 ? iPos : null; 2219 } 2220 2221 2222 /** 2223 * Get the number of visible columns 2224 * @param {object} oSettings dataTables settings object 2225 * @returns {int} i the number of visible columns 2226 * @memberof DataTable#oApi 2227 */ 2228 function _fnVisbleColumns( settings ) 2229 { 2230 var layout = settings.aoHeader; 2231 var columns = settings.aoColumns; 2232 var vis = 0; 2233 2234 if ( layout.length ) { 2235 for ( var i=0, ien=layout[0].length ; i<ien ; i++ ) { 2236 if ( columns[i].bVisible && $(layout[0][i].cell).css('display') !== 'none' ) { 2237 vis++; 2238 } 2239 } 2240 } 2241 2242 return vis; 2243 } 2244 2245 2246 /** 2247 * Get an array of column indexes that match a given property 2248 * @param {object} oSettings dataTables settings object 2249 * @param {string} sParam Parameter in aoColumns to look for - typically 2250 * bVisible or bSearchable 2251 * @returns {array} Array of indexes with matched properties 2252 * @memberof DataTable#oApi 2253 */ 2254 function _fnGetColumns( oSettings, sParam ) 2255 { 2256 var a = []; 2257 2258 oSettings.aoColumns.map( function(val, i) { 2259 if ( val[sParam] ) { 2260 a.push( i ); 2261 } 2262 } ); 2263 2264 return a; 2265 } 2266 2267 2268 /** 2269 * Calculate the 'type' of a column 2270 * @param {object} settings dataTables settings object 2271 * @memberof DataTable#oApi 2272 */ 2273 function _fnColumnTypes ( settings ) 2274 { 2275 var columns = settings.aoColumns; 2276 var data = settings.aoData; 2277 var types = DataTable.ext.type.detect; 2278 var i, ien, j, jen, k, ken; 2279 var col, detectedType, cache; 2280 2281 // For each column, spin over the 2282 for ( i=0, ien=columns.length ; i<ien ; i++ ) { 2283 col = columns[i]; 2284 cache = []; 2285 2286 if ( ! col.sType && col._sManualType ) { 2287 col.sType = col._sManualType; 2288 } 2289 else if ( ! col.sType ) { 2290 for ( j=0, jen=types.length ; j<jen ; j++ ) { 2291 for ( k=0, ken=data.length ; k<ken ; k++ ) { 2292 2293 if (! data[k]) { 2294 continue; 2295 } 2296 2297 // Use a cache array so we only need to get the type data 2298 // from the formatter once (when using multiple detectors) 2299 if ( cache[k] === undefined ) { 2300 cache[k] = _fnGetCellData( settings, k, i, 'type' ); 2301 } 2302 2303 detectedType = types[j]( cache[k], settings ); 2304 2305 // If null, then this type can't apply to this column, so 2306 // rather than testing all cells, break out. There is an 2307 // exception for the last type which is `html`. We need to 2308 // scan all rows since it is possible to mix string and HTML 2309 // types 2310 if ( ! detectedType && j !== types.length-2 ) { 2311 break; 2312 } 2313 2314 // Only a single match is needed for html type since it is 2315 // bottom of the pile and very similar to string - but it 2316 // must not be empty 2317 if ( detectedType === 'html' && ! _empty(cache[k]) ) { 2318 break; 2319 } 2320 } 2321 2322 // Type is valid for all data points in the column - use this 2323 // type 2324 if ( detectedType ) { 2325 col.sType = detectedType; 2326 break; 2327 } 2328 } 2329 2330 // Fall back - if no type was detected, always use string 2331 if ( ! col.sType ) { 2332 col.sType = 'string'; 2333 } 2334 } 2335 2336 // Set class names for header / footer for auto type classes 2337 var autoClass = _ext.type.className[col.sType]; 2338 2339 if (autoClass) { 2340 _columnAutoClass(settings.aoHeader, i, autoClass); 2341 _columnAutoClass(settings.aoFooter, i, autoClass); 2342 } 2343 2344 var renderer = _ext.type.render[col.sType]; 2345 2346 // This can only happen once! There is no way to remover 2347 // a renderer. After the first time the renderer has 2348 // already been set so createTr will run the renderer itself. 2349 if (renderer && ! col._render) { 2350 col._render = DataTable.util.get(renderer); 2351 2352 _columnAutoRender(settings, i); 2353 } 2354 } 2355 } 2356 2357 /** 2358 * Apply an auto detected renderer to data which doesn't yet have 2359 * a renderer 2360 */ 2361 function _columnAutoRender(settings, colIdx) { 2362 var data = settings.aoData; 2363 2364 for (var i=0 ; i<data.length ; i++) { 2365 if (data[i].nTr) { 2366 // We have to update the display here since there is no 2367 // invalidation check for the data 2368 var display = _fnGetCellData( settings, i, colIdx, 'display' ); 2369 2370 data[i].displayData[colIdx] = display; 2371 _fnWriteCell(data[i].anCells[colIdx], display); 2372 2373 // No need to update sort / filter data since it has 2374 // been invalidated and will be re-read with the 2375 // renderer now applied 2376 } 2377 } 2378 } 2379 2380 /** 2381 * Apply a class name to a column's header cells 2382 */ 2383 function _columnAutoClass(container, colIdx, className) { 2384 container.forEach(function (row) { 2385 if (row[colIdx] && row[colIdx].unique) { 2386 _addClass(row[colIdx].cell, className); 2387 } 2388 }); 2389 } 2390 2391 /** 2392 * Take the column definitions and static columns arrays and calculate how 2393 * they relate to column indexes. The callback function will then apply the 2394 * definition found for a column to a suitable configuration object. 2395 * @param {object} oSettings dataTables settings object 2396 * @param {array} aoColDefs The aoColumnDefs array that is to be applied 2397 * @param {array} aoCols The aoColumns array that defines columns individually 2398 * @param {array} headerLayout Layout for header as it was loaded 2399 * @param {function} fn Callback function - takes two parameters, the calculated 2400 * column index and the definition for that column. 2401 * @memberof DataTable#oApi 2402 */ 2403 function _fnApplyColumnDefs( oSettings, aoColDefs, aoCols, headerLayout, fn ) 2404 { 2405 var i, iLen, j, jLen, k, kLen, def; 2406 var columns = oSettings.aoColumns; 2407 2408 if ( aoCols ) { 2409 for ( i=0, iLen=aoCols.length ; i<iLen ; i++ ) { 2410 if (aoCols[i] && aoCols[i].name) { 2411 columns[i].sName = aoCols[i].name; 2412 } 2413 } 2414 } 2415 2416 // Column definitions with aTargets 2417 if ( aoColDefs ) 2418 { 2419 /* Loop over the definitions array - loop in reverse so first instance has priority */ 2420 for ( i=aoColDefs.length-1 ; i>=0 ; i-- ) 2421 { 2422 def = aoColDefs[i]; 2423 2424 /* Each definition can target multiple columns, as it is an array */ 2425 var aTargets = def.target !== undefined 2426 ? def.target 2427 : def.targets !== undefined 2428 ? def.targets 2429 : def.aTargets; 2430 2431 if ( ! Array.isArray( aTargets ) ) 2432 { 2433 aTargets = [ aTargets ]; 2434 } 2435 2436 for ( j=0, jLen=aTargets.length ; j<jLen ; j++ ) 2437 { 2438 var target = aTargets[j]; 2439 2440 if ( typeof target === 'number' && target >= 0 ) 2441 { 2442 /* Add columns that we don't yet know about */ 2443 while( columns.length <= target ) 2444 { 2445 _fnAddColumn( oSettings ); 2446 } 2447 2448 /* Integer, basic index */ 2449 fn( target, def ); 2450 } 2451 else if ( typeof target === 'number' && target < 0 ) 2452 { 2453 /* Negative integer, right to left column counting */ 2454 fn( columns.length+target, def ); 2455 } 2456 else if ( typeof target === 'string' ) 2457 { 2458 for ( k=0, kLen=columns.length ; k<kLen ; k++ ) { 2459 if (target === '_all') { 2460 // Apply to all columns 2461 fn( k, def ); 2462 } 2463 else if (target.indexOf(':name') !== -1) { 2464 // Column selector 2465 if (columns[k].sName === target.replace(':name', '')) { 2466 fn( k, def ); 2467 } 2468 } 2469 else { 2470 // Cell selector 2471 headerLayout.forEach(function (row) { 2472 if (row[k]) { 2473 var cell = $(row[k].cell); 2474 2475 // Legacy support. Note that it means that we don't support 2476 // an element name selector only, since they are treated as 2477 // class names for 1.x compat. 2478 if (target.match(/^[a-z][\w-]*$/i)) { 2479 target = '.' + target; 2480 } 2481 2482 if (cell.is( target )) { 2483 fn( k, def ); 2484 } 2485 } 2486 }); 2487 } 2488 } 2489 } 2490 } 2491 } 2492 } 2493 2494 // Statically defined columns array 2495 if ( aoCols ) { 2496 for ( i=0, iLen=aoCols.length ; i<iLen ; i++ ) { 2497 fn( i, aoCols[i] ); 2498 } 2499 } 2500 } 2501 2502 2503 /** 2504 * Get the width for a given set of columns 2505 * 2506 * @param {*} settings DataTables settings object 2507 * @param {*} targets Columns - comma separated string or array of numbers 2508 * @param {*} original Use the original width (true) or calculated (false) 2509 * @param {*} incVisible Include visible columns (true) or not (false) 2510 * @returns Combined CSS value 2511 */ 2512 function _fnColumnsSumWidth( settings, targets, original, incVisible ) { 2513 if ( ! Array.isArray( targets ) ) { 2514 targets = _fnColumnsFromHeader( targets ); 2515 } 2516 2517 var sum = 0; 2518 var unit; 2519 var columns = settings.aoColumns; 2520 2521 for ( var i=0, ien=targets.length ; i<ien ; i++ ) { 2522 var column = columns[ targets[i] ]; 2523 var definedWidth = original ? 2524 column.sWidthOrig : 2525 column.sWidth; 2526 2527 if ( ! incVisible && column.bVisible === false ) { 2528 continue; 2529 } 2530 2531 if ( definedWidth === null || definedWidth === undefined ) { 2532 return null; // can't determine a defined width - browser defined 2533 } 2534 else if ( typeof definedWidth === 'number' ) { 2535 unit = 'px'; 2536 sum += definedWidth; 2537 } 2538 else { 2539 var matched = definedWidth.match(/([\d\.]+)([^\d]*)/); 2540 2541 if ( matched ) { 2542 sum += matched[1] * 1; 2543 unit = matched.length === 3 ? 2544 matched[2] : 2545 'px'; 2546 } 2547 } 2548 } 2549 2550 return sum + unit; 2551 } 2552 2553 function _fnColumnsFromHeader( cell ) 2554 { 2555 var attr = $(cell).closest('[data-dt-column]').attr('data-dt-column'); 2556 2557 if ( ! attr ) { 2558 return []; 2559 } 2560 2561 return attr.split(',').map( function (val) { 2562 return val * 1; 2563 } ); 2564 } 2565 /** 2566 * Add a data array to the table, creating DOM node etc. This is the parallel to 2567 * _fnGatherData, but for adding rows from a Javascript source, rather than a 2568 * DOM source. 2569 * @param {object} settings dataTables settings object 2570 * @param {array} data data array to be added 2571 * @param {node} [tr] TR element to add to the table - optional. If not given, 2572 * DataTables will create a row automatically 2573 * @param {array} [tds] Array of TD|TH elements for the row - must be given 2574 * if nTr is. 2575 * @returns {int} >=0 if successful (index of new aoData entry), -1 if failed 2576 * @memberof DataTable#oApi 2577 */ 2578 function _fnAddData ( settings, dataIn, tr, tds ) 2579 { 2580 /* Create the object for storing information about this new row */ 2581 var rowIdx = settings.aoData.length; 2582 var rowModel = $.extend( true, {}, DataTable.models.oRow, { 2583 src: tr ? 'dom' : 'data', 2584 idx: rowIdx 2585 } ); 2586 2587 rowModel._aData = dataIn; 2588 settings.aoData.push( rowModel ); 2589 2590 var columns = settings.aoColumns; 2591 2592 for ( var i=0, iLen=columns.length ; i<iLen ; i++ ) 2593 { 2594 // Invalidate the column types as the new data needs to be revalidated 2595 columns[i].sType = null; 2596 } 2597 2598 /* Add to the display array */ 2599 settings.aiDisplayMaster.push( rowIdx ); 2600 2601 var id = settings.rowIdFn( dataIn ); 2602 if ( id !== undefined ) { 2603 settings.aIds[ id ] = rowModel; 2604 } 2605 2606 /* Create the DOM information, or register it if already present */ 2607 if ( tr || ! settings.oFeatures.bDeferRender ) 2608 { 2609 _fnCreateTr( settings, rowIdx, tr, tds ); 2610 } 2611 2612 return rowIdx; 2613 } 2614 2615 2616 /** 2617 * Add one or more TR elements to the table. Generally we'd expect to 2618 * use this for reading data from a DOM sourced table, but it could be 2619 * used for an TR element. Note that if a TR is given, it is used (i.e. 2620 * it is not cloned). 2621 * @param {object} settings dataTables settings object 2622 * @param {array|node|jQuery} trs The TR element(s) to add to the table 2623 * @returns {array} Array of indexes for the added rows 2624 * @memberof DataTable#oApi 2625 */ 2626 function _fnAddTr( settings, trs ) 2627 { 2628 var row; 2629 2630 // Allow an individual node to be passed in 2631 if ( ! (trs instanceof $) ) { 2632 trs = $(trs); 2633 } 2634 2635 return trs.map( function (i, el) { 2636 row = _fnGetRowElements( settings, el ); 2637 return _fnAddData( settings, row.data, el, row.cells ); 2638 } ); 2639 } 2640 2641 2642 /** 2643 * Get the data for a given cell from the internal cache, taking into account data mapping 2644 * @param {object} settings dataTables settings object 2645 * @param {int} rowIdx aoData row id 2646 * @param {int} colIdx Column index 2647 * @param {string} type data get type ('display', 'type' 'filter|search' 'sort|order') 2648 * @returns {*} Cell data 2649 * @memberof DataTable#oApi 2650 */ 2651 function _fnGetCellData( settings, rowIdx, colIdx, type ) 2652 { 2653 if (type === 'search') { 2654 type = 'filter'; 2655 } 2656 else if (type === 'order') { 2657 type = 'sort'; 2658 } 2659 2660 var row = settings.aoData[rowIdx]; 2661 2662 if (! row) { 2663 return undefined; 2664 } 2665 2666 var draw = settings.iDraw; 2667 var col = settings.aoColumns[colIdx]; 2668 var rowData = row._aData; 2669 var defaultContent = col.sDefaultContent; 2670 var cellData = col.fnGetData( rowData, type, { 2671 settings: settings, 2672 row: rowIdx, 2673 col: colIdx 2674 } ); 2675 2676 // Allow for a node being returned for non-display types 2677 if (type !== 'display' && cellData && typeof cellData === 'object' && cellData.nodeName) { 2678 cellData = cellData.innerHTML; 2679 } 2680 2681 if ( cellData === undefined ) { 2682 if ( settings.iDrawError != draw && defaultContent === null ) { 2683 _fnLog( settings, 0, "Requested unknown parameter "+ 2684 (typeof col.mData=='function' ? '{function}' : "'"+col.mData+"'")+ 2685 " for row "+rowIdx+", column "+colIdx, 4 ); 2686 settings.iDrawError = draw; 2687 } 2688 return defaultContent; 2689 } 2690 2691 // When the data source is null and a specific data type is requested (i.e. 2692 // not the original data), we can use default column data 2693 if ( (cellData === rowData || cellData === null) && defaultContent !== null && type !== undefined ) { 2694 cellData = defaultContent; 2695 } 2696 else if ( typeof cellData === 'function' ) { 2697 // If the data source is a function, then we run it and use the return, 2698 // executing in the scope of the data object (for instances) 2699 return cellData.call( rowData ); 2700 } 2701 2702 if ( cellData === null && type === 'display' ) { 2703 return ''; 2704 } 2705 2706 if ( type === 'filter' ) { 2707 var fomatters = DataTable.ext.type.search; 2708 2709 if ( fomatters[ col.sType ] ) { 2710 cellData = fomatters[ col.sType ]( cellData ); 2711 } 2712 } 2713 2714 return cellData; 2715 } 2716 2717 2718 /** 2719 * Set the value for a specific cell, into the internal data cache 2720 * @param {object} settings dataTables settings object 2721 * @param {int} rowIdx aoData row id 2722 * @param {int} colIdx Column index 2723 * @param {*} val Value to set 2724 * @memberof DataTable#oApi 2725 */ 2726 function _fnSetCellData( settings, rowIdx, colIdx, val ) 2727 { 2728 var col = settings.aoColumns[colIdx]; 2729 var rowData = settings.aoData[rowIdx]._aData; 2730 2731 col.fnSetData( rowData, val, { 2732 settings: settings, 2733 row: rowIdx, 2734 col: colIdx 2735 } ); 2736 } 2737 2738 /** 2739 * Write a value to a cell 2740 * @param {*} td Cell 2741 * @param {*} val Value 2742 */ 2743 function _fnWriteCell(td, val) 2744 { 2745 if (val && typeof val === 'object' && val.nodeName) { 2746 $(td) 2747 .empty() 2748 .append(val); 2749 } 2750 else { 2751 td.innerHTML = val; 2752 } 2753 } 2754 2755 2756 // Private variable that is used to match action syntax in the data property object 2757 var __reArray = /\[.*?\]$/; 2758 var __reFn = /\(\)$/; 2759 2760 /** 2761 * Split string on periods, taking into account escaped periods 2762 * @param {string} str String to split 2763 * @return {array} Split string 2764 */ 2765 function _fnSplitObjNotation( str ) 2766 { 2767 var parts = str.match(/(\\.|[^.])+/g) || ['']; 2768 2769 return parts.map( function ( s ) { 2770 return s.replace(/\\\./g, '.'); 2771 } ); 2772 } 2773 2774 2775 /** 2776 * Return a function that can be used to get data from a source object, taking 2777 * into account the ability to use nested objects as a source 2778 * @param {string|int|function} mSource The data source for the object 2779 * @returns {function} Data get function 2780 * @memberof DataTable#oApi 2781 */ 2782 var _fnGetObjectDataFn = DataTable.util.get; 2783 2784 2785 /** 2786 * Return a function that can be used to set data from a source object, taking 2787 * into account the ability to use nested objects as a source 2788 * @param {string|int|function} mSource The data source for the object 2789 * @returns {function} Data set function 2790 * @memberof DataTable#oApi 2791 */ 2792 var _fnSetObjectDataFn = DataTable.util.set; 2793 2794 2795 /** 2796 * Return an array with the full table data 2797 * @param {object} oSettings dataTables settings object 2798 * @returns array {array} aData Master data array 2799 * @memberof DataTable#oApi 2800 */ 2801 function _fnGetDataMaster ( settings ) 2802 { 2803 return _pluck( settings.aoData, '_aData' ); 2804 } 2805 2806 2807 /** 2808 * Nuke the table 2809 * @param {object} oSettings dataTables settings object 2810 * @memberof DataTable#oApi 2811 */ 2812 function _fnClearTable( settings ) 2813 { 2814 settings.aoData.length = 0; 2815 settings.aiDisplayMaster.length = 0; 2816 settings.aiDisplay.length = 0; 2817 settings.aIds = {}; 2818 } 2819 2820 2821 /** 2822 * Mark cached data as invalid such that a re-read of the data will occur when 2823 * the cached data is next requested. Also update from the data source object. 2824 * 2825 * @param {object} settings DataTables settings object 2826 * @param {int} rowIdx Row index to invalidate 2827 * @param {string} [src] Source to invalidate from: undefined, 'auto', 'dom' 2828 * or 'data' 2829 * @param {int} [colIdx] Column index to invalidate. If undefined the whole 2830 * row will be invalidated 2831 * @memberof DataTable#oApi 2832 * 2833 * @todo For the modularisation of v1.11 this will need to become a callback, so 2834 * the sort and filter methods can subscribe to it. That will required 2835 * initialisation options for sorting, which is why it is not already baked in 2836 */ 2837 function _fnInvalidate( settings, rowIdx, src, colIdx ) 2838 { 2839 var row = settings.aoData[ rowIdx ]; 2840 var i, ien; 2841 2842 // Remove the cached data for the row 2843 row._aSortData = null; 2844 row._aFilterData = null; 2845 row.displayData = null; 2846 2847 // Are we reading last data from DOM or the data object? 2848 if ( src === 'dom' || ((! src || src === 'auto') && row.src === 'dom') ) { 2849 // Read the data from the DOM 2850 row._aData = _fnGetRowElements( 2851 settings, row, colIdx, colIdx === undefined ? undefined : row._aData 2852 ) 2853 .data; 2854 } 2855 else { 2856 // Reading from data object, update the DOM 2857 var cells = row.anCells; 2858 var display = _fnGetRowDisplay(settings, rowIdx); 2859 2860 if ( cells ) { 2861 if ( colIdx !== undefined ) { 2862 _fnWriteCell(cells[colIdx], display[colIdx]); 2863 } 2864 else { 2865 for ( i=0, ien=cells.length ; i<ien ; i++ ) { 2866 _fnWriteCell(cells[i], display[i]); 2867 } 2868 } 2869 } 2870 } 2871 2872 // Column specific invalidation 2873 var cols = settings.aoColumns; 2874 if ( colIdx !== undefined ) { 2875 // Type - the data might have changed 2876 cols[ colIdx ].sType = null; 2877 2878 // Max length string. Its a fairly cheep recalculation, so not worth 2879 // something more complicated 2880 cols[ colIdx ].maxLenString = null; 2881 } 2882 else { 2883 for ( i=0, ien=cols.length ; i<ien ; i++ ) { 2884 cols[i].sType = null; 2885 cols[i].maxLenString = null; 2886 } 2887 2888 // Update DataTables special `DT_*` attributes for the row 2889 _fnRowAttributes( settings, row ); 2890 } 2891 } 2892 2893 2894 /** 2895 * Build a data source object from an HTML row, reading the contents of the 2896 * cells that are in the row. 2897 * 2898 * @param {object} settings DataTables settings object 2899 * @param {node|object} TR element from which to read data or existing row 2900 * object from which to re-read the data from the cells 2901 * @param {int} [colIdx] Optional column index 2902 * @param {array|object} [d] Data source object. If `colIdx` is given then this 2903 * parameter should also be given and will be used to write the data into. 2904 * Only the column in question will be written 2905 * @returns {object} Object with two parameters: `data` the data read, in 2906 * document order, and `cells` and array of nodes (they can be useful to the 2907 * caller, so rather than needing a second traversal to get them, just return 2908 * them from here). 2909 * @memberof DataTable#oApi 2910 */ 2911 function _fnGetRowElements( settings, row, colIdx, d ) 2912 { 2913 var 2914 tds = [], 2915 td = row.firstChild, 2916 name, col, i=0, contents, 2917 columns = settings.aoColumns, 2918 objectRead = settings._rowReadObject; 2919 2920 // Allow the data object to be passed in, or construct 2921 d = d !== undefined ? 2922 d : 2923 objectRead ? 2924 {} : 2925 []; 2926 2927 var attr = function ( str, td ) { 2928 if ( typeof str === 'string' ) { 2929 var idx = str.indexOf('@'); 2930 2931 if ( idx !== -1 ) { 2932 var attr = str.substring( idx+1 ); 2933 var setter = _fnSetObjectDataFn( str ); 2934 setter( d, td.getAttribute( attr ) ); 2935 } 2936 } 2937 }; 2938 2939 // Read data from a cell and store into the data object 2940 var cellProcess = function ( cell ) { 2941 if ( colIdx === undefined || colIdx === i ) { 2942 col = columns[i]; 2943 contents = (cell.innerHTML).trim(); 2944 2945 if ( col && col._bAttrSrc ) { 2946 var setter = _fnSetObjectDataFn( col.mData._ ); 2947 setter( d, contents ); 2948 2949 attr( col.mData.sort, cell ); 2950 attr( col.mData.type, cell ); 2951 attr( col.mData.filter, cell ); 2952 } 2953 else { 2954 // Depending on the `data` option for the columns the data can 2955 // be read to either an object or an array. 2956 if ( objectRead ) { 2957 if ( ! col._setter ) { 2958 // Cache the setter function 2959 col._setter = _fnSetObjectDataFn( col.mData ); 2960 } 2961 col._setter( d, contents ); 2962 } 2963 else { 2964 d[i] = contents; 2965 } 2966 } 2967 } 2968 2969 i++; 2970 }; 2971 2972 if ( td ) { 2973 // `tr` element was passed in 2974 while ( td ) { 2975 name = td.nodeName.toUpperCase(); 2976 2977 if ( name == "TD" || name == "TH" ) { 2978 cellProcess( td ); 2979 tds.push( td ); 2980 } 2981 2982 td = td.nextSibling; 2983 } 2984 } 2985 else { 2986 // Existing row object passed in 2987 tds = row.anCells; 2988 2989 for ( var j=0, jen=tds.length ; j<jen ; j++ ) { 2990 cellProcess( tds[j] ); 2991 } 2992 } 2993 2994 // Read the ID from the DOM if present 2995 var rowNode = row.firstChild ? row : row.nTr; 2996 2997 if ( rowNode ) { 2998 var id = rowNode.getAttribute( 'id' ); 2999 3000 if ( id ) { 3001 _fnSetObjectDataFn( settings.rowId )( d, id ); 3002 } 3003 } 3004 3005 return { 3006 data: d, 3007 cells: tds 3008 }; 3009 } 3010 3011 /** 3012 * Render and cache a row's display data for the columns, if required 3013 * @returns 3014 */ 3015 function _fnGetRowDisplay (settings, rowIdx) { 3016 let rowModal = settings.aoData[rowIdx]; 3017 let columns = settings.aoColumns; 3018 3019 if (! rowModal.displayData) { 3020 // Need to render and cache 3021 rowModal.displayData = []; 3022 3023 for ( var colIdx=0, len=columns.length ; colIdx<len ; colIdx++ ) { 3024 rowModal.displayData.push( 3025 _fnGetCellData( settings, rowIdx, colIdx, 'display' ) 3026 ); 3027 } 3028 } 3029 3030 return rowModal.displayData; 3031 } 3032 3033 /** 3034 * Create a new TR element (and it's TD children) for a row 3035 * @param {object} oSettings dataTables settings object 3036 * @param {int} iRow Row to consider 3037 * @param {node} [nTrIn] TR element to add to the table - optional. If not given, 3038 * DataTables will create a row automatically 3039 * @param {array} [anTds] Array of TD|TH elements for the row - must be given 3040 * if nTr is. 3041 * @memberof DataTable#oApi 3042 */ 3043 function _fnCreateTr ( oSettings, iRow, nTrIn, anTds ) 3044 { 3045 var 3046 row = oSettings.aoData[iRow], 3047 rowData = row._aData, 3048 cells = [], 3049 nTr, nTd, oCol, 3050 i, iLen, create, 3051 trClass = oSettings.oClasses.tbody.row; 3052 3053 if ( row.nTr === null ) 3054 { 3055 nTr = nTrIn || document.createElement('tr'); 3056 3057 row.nTr = nTr; 3058 row.anCells = cells; 3059 3060 _addClass(nTr, trClass); 3061 3062 /* Use a private property on the node to allow reserve mapping from the node 3063 * to the aoData array for fast look up 3064 */ 3065 nTr._DT_RowIndex = iRow; 3066 3067 /* Special parameters can be given by the data source to be used on the row */ 3068 _fnRowAttributes( oSettings, row ); 3069 3070 /* Process each column */ 3071 for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ ) 3072 { 3073 oCol = oSettings.aoColumns[i]; 3074 create = nTrIn && anTds[i] ? false : true; 3075 3076 nTd = create ? document.createElement( oCol.sCellType ) : anTds[i]; 3077 3078 if (! nTd) { 3079 _fnLog( oSettings, 0, 'Incorrect column count', 18 ); 3080 } 3081 3082 nTd._DT_CellIndex = { 3083 row: iRow, 3084 column: i 3085 }; 3086 3087 cells.push( nTd ); 3088 3089 var display = _fnGetRowDisplay(oSettings, iRow); 3090 3091 // Need to create the HTML if new, or if a rendering function is defined 3092 if ( 3093 create || 3094 ( 3095 (oCol.mRender || oCol.mData !== i) && 3096 (!$.isPlainObject(oCol.mData) || oCol.mData._ !== i+'.display') 3097 ) 3098 ) { 3099 _fnWriteCell(nTd, display[i]); 3100 } 3101 3102 // Visibility - add or remove as required 3103 if ( oCol.bVisible && create ) 3104 { 3105 nTr.appendChild( nTd ); 3106 } 3107 else if ( ! oCol.bVisible && ! create ) 3108 { 3109 nTd.parentNode.removeChild( nTd ); 3110 } 3111 3112 if ( oCol.fnCreatedCell ) 3113 { 3114 oCol.fnCreatedCell.call( oSettings.oInstance, 3115 nTd, _fnGetCellData( oSettings, iRow, i ), rowData, iRow, i 3116 ); 3117 } 3118 } 3119 3120 _fnCallbackFire( oSettings, 'aoRowCreatedCallback', 'row-created', [nTr, rowData, iRow, cells] ); 3121 } 3122 else { 3123 _addClass(row.nTr, trClass); 3124 } 3125 } 3126 3127 3128 /** 3129 * Add attributes to a row based on the special `DT_*` parameters in a data 3130 * source object. 3131 * @param {object} settings DataTables settings object 3132 * @param {object} DataTables row object for the row to be modified 3133 * @memberof DataTable#oApi 3134 */ 3135 function _fnRowAttributes( settings, row ) 3136 { 3137 var tr = row.nTr; 3138 var data = row._aData; 3139 3140 if ( tr ) { 3141 var id = settings.rowIdFn( data ); 3142 3143 if ( id ) { 3144 tr.id = id; 3145 } 3146 3147 if ( data.DT_RowClass ) { 3148 // Remove any classes added by DT_RowClass before 3149 var a = data.DT_RowClass.split(' '); 3150 row.__rowc = row.__rowc ? 3151 _unique( row.__rowc.concat( a ) ) : 3152 a; 3153 3154 $(tr) 3155 .removeClass( row.__rowc.join(' ') ) 3156 .addClass( data.DT_RowClass ); 3157 } 3158 3159 if ( data.DT_RowAttr ) { 3160 $(tr).attr( data.DT_RowAttr ); 3161 } 3162 3163 if ( data.DT_RowData ) { 3164 $(tr).data( data.DT_RowData ); 3165 } 3166 } 3167 } 3168 3169 3170 /** 3171 * Create the HTML header for the table 3172 * @param {object} oSettings dataTables settings object 3173 * @memberof DataTable#oApi 3174 */ 3175 function _fnBuildHead( settings, side ) 3176 { 3177 var classes = settings.oClasses; 3178 var columns = settings.aoColumns; 3179 var i, ien, row; 3180 var target = side === 'header' 3181 ? settings.nTHead 3182 : settings.nTFoot; 3183 var titleProp = side === 'header' ? 'sTitle' : side; 3184 3185 // Footer might be defined 3186 if (! target) { 3187 return; 3188 } 3189 3190 // If no cells yet and we have content for them, then create 3191 if (side === 'header' || _pluck(settings.aoColumns, titleProp).join('')) { 3192 row = $('tr', target); 3193 3194 // Add a row if needed 3195 if (! row.length) { 3196 row = $('<tr/>').appendTo(target) 3197 } 3198 3199 // Add the number of cells needed to make up to the number of columns 3200 if (row.length === 1) { 3201 var cells = $('td, th', row); 3202 3203 for ( i=cells.length, ien=columns.length ; i<ien ; i++ ) { 3204 $('<th/>') 3205 .html( columns[i][titleProp] || '' ) 3206 .appendTo( row ); 3207 } 3208 } 3209 } 3210 3211 var detected = _fnDetectHeader( settings, target, true ); 3212 3213 if (side === 'header') { 3214 settings.aoHeader = detected; 3215 } 3216 else { 3217 settings.aoFooter = detected; 3218 } 3219 3220 // ARIA role for the rows 3221 $(target).children('tr').attr('role', 'row'); 3222 3223 // Every cell needs to be passed through the renderer 3224 $(target).children('tr').children('th, td') 3225 .each( function () { 3226 _fnRenderer( settings, side )( 3227 settings, $(this), classes 3228 ); 3229 } ); 3230 } 3231 3232 /** 3233 * Build a layout structure for a header or footer 3234 * 3235 * @param {*} settings DataTables settings 3236 * @param {*} source Source layout array 3237 * @param {*} incColumns What columns should be included 3238 * @returns Layout array 3239 */ 3240 function _fnHeaderLayout( settings, source, incColumns ) 3241 { 3242 var row, column, cell; 3243 var local = []; 3244 var structure = []; 3245 var columns = settings.aoColumns; 3246 var columnCount = columns.length; 3247 var rowspan, colspan; 3248 3249 if ( ! source ) { 3250 return; 3251 } 3252 3253 // Default is to work on only visible columns 3254 if ( ! incColumns ) { 3255 incColumns = _range(columnCount) 3256 .filter(function (idx) { 3257 return columns[idx].bVisible; 3258 }); 3259 } 3260 3261 // Make a copy of the master layout array, but with only the columns we want 3262 for ( row=0 ; row<source.length ; row++ ) { 3263 // Remove any columns we haven't selected 3264 local[row] = source[row].slice().filter(function (cell, i) { 3265 return incColumns.includes(i); 3266 }); 3267 3268 // Prep the structure array - it needs an element for each row 3269 structure.push( [] ); 3270 } 3271 3272 for ( row=0 ; row<local.length ; row++ ) { 3273 for ( column=0 ; column<local[row].length ; column++ ) { 3274 rowspan = 1; 3275 colspan = 1; 3276 3277 // Check to see if there is already a cell (row/colspan) covering our target 3278 // insert point. If there is, then there is nothing to do. 3279 if ( structure[row][column] === undefined ) { 3280 cell = local[row][column].cell; 3281 3282 // Expand for rowspan 3283 while ( 3284 local[row+rowspan] !== undefined && 3285 local[row][column].cell == local[row+rowspan][column].cell 3286 ) { 3287 structure[row+rowspan][column] = null; 3288 rowspan++; 3289 } 3290 3291 // And for colspan 3292 while ( 3293 local[row][column+colspan] !== undefined && 3294 local[row][column].cell == local[row][column+colspan].cell 3295 ) { 3296 // Which also needs to go over rows 3297 for ( var k=0 ; k<rowspan ; k++ ) { 3298 structure[row+k][column+colspan] = null; 3299 } 3300 3301 colspan++; 3302 } 3303 3304 var titleSpan = $('span.dt-column-title', cell); 3305 3306 structure[row][column] = { 3307 cell: cell, 3308 colspan: colspan, 3309 rowspan: rowspan, 3310 title: titleSpan.length 3311 ? titleSpan.html() 3312 : $(cell).html() 3313 }; 3314 } 3315 } 3316 } 3317 3318 return structure; 3319 } 3320 3321 3322 /** 3323 * Draw the header (or footer) element based on the column visibility states. 3324 * 3325 * @param object oSettings dataTables settings object 3326 * @param array aoSource Layout array from _fnDetectHeader 3327 * @memberof DataTable#oApi 3328 */ 3329 function _fnDrawHead( settings, source ) 3330 { 3331 var layout = _fnHeaderLayout(settings, source); 3332 var tr, n; 3333 3334 for ( var row=0 ; row<source.length ; row++ ) { 3335 tr = source[row].row; 3336 3337 // All cells are going to be replaced, so empty out the row 3338 // Can't use $().empty() as that kills event handlers 3339 if (tr) { 3340 while( (n = tr.firstChild) ) { 3341 tr.removeChild( n ); 3342 } 3343 } 3344 3345 for ( var column=0 ; column<layout[row].length ; column++ ) { 3346 var point = layout[row][column]; 3347 3348 if (point) { 3349 $(point.cell) 3350 .appendTo(tr) 3351 .attr('rowspan', point.rowspan) 3352 .attr('colspan', point.colspan); 3353 } 3354 } 3355 } 3356 } 3357 3358 3359 /** 3360 * Insert the required TR nodes into the table for display 3361 * @param {object} oSettings dataTables settings object 3362 * @param ajaxComplete true after ajax call to complete rendering 3363 * @memberof DataTable#oApi 3364 */ 3365 function _fnDraw( oSettings, ajaxComplete ) 3366 { 3367 // Allow for state saving and a custom start position 3368 _fnStart( oSettings ); 3369 3370 /* Provide a pre-callback function which can be used to cancel the draw is false is returned */ 3371 var aPreDraw = _fnCallbackFire( oSettings, 'aoPreDrawCallback', 'preDraw', [oSettings] ); 3372 if ( aPreDraw.indexOf(false) !== -1 ) 3373 { 3374 _fnProcessingDisplay( oSettings, false ); 3375 return; 3376 } 3377 3378 var anRows = []; 3379 var iRowCount = 0; 3380 var bServerSide = _fnDataSource( oSettings ) == 'ssp'; 3381 var aiDisplay = oSettings.aiDisplay; 3382 var iDisplayStart = oSettings._iDisplayStart; 3383 var iDisplayEnd = oSettings.fnDisplayEnd(); 3384 var columns = oSettings.aoColumns; 3385 var body = $(oSettings.nTBody); 3386 3387 oSettings.bDrawing = true; 3388 3389 /* Server-side processing draw intercept */ 3390 if ( !bServerSide ) 3391 { 3392 oSettings.iDraw++; 3393 } 3394 else if ( !oSettings.bDestroying && !ajaxComplete) 3395 { 3396 // Show loading message for server-side processing 3397 if (oSettings.iDraw === 0) { 3398 body.empty().append(_emptyRow(oSettings)); 3399 } 3400 3401 _fnAjaxUpdate( oSettings ); 3402 return; 3403 } 3404 3405 if ( aiDisplay.length !== 0 ) 3406 { 3407 var iStart = bServerSide ? 0 : iDisplayStart; 3408 var iEnd = bServerSide ? oSettings.aoData.length : iDisplayEnd; 3409 3410 for ( var j=iStart ; j<iEnd ; j++ ) 3411 { 3412 var iDataIndex = aiDisplay[j]; 3413 var aoData = oSettings.aoData[ iDataIndex ]; 3414 if ( aoData.nTr === null ) 3415 { 3416 _fnCreateTr( oSettings, iDataIndex ); 3417 } 3418 3419 var nRow = aoData.nTr; 3420 3421 // Add various classes as needed 3422 for (var i=0 ; i<columns.length ; i++) { 3423 var col = columns[i]; 3424 var td = aoData.anCells[i]; 3425 3426 _addClass(td, _ext.type.className[col.sType]); // auto class 3427 _addClass(td, col.sClass); // column class 3428 _addClass(td, oSettings.oClasses.tbody.cell); // all cells 3429 } 3430 3431 // Row callback functions - might want to manipulate the row 3432 // iRowCount and j are not currently documented. Are they at all 3433 // useful? 3434 _fnCallbackFire( oSettings, 'aoRowCallback', null, 3435 [nRow, aoData._aData, iRowCount, j, iDataIndex] ); 3436 3437 anRows.push( nRow ); 3438 iRowCount++; 3439 } 3440 } 3441 else 3442 { 3443 anRows[ 0 ] = _emptyRow(oSettings); 3444 } 3445 3446 /* Header and footer callbacks */ 3447 _fnCallbackFire( oSettings, 'aoHeaderCallback', 'header', [ $(oSettings.nTHead).children('tr')[0], 3448 _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] ); 3449 3450 _fnCallbackFire( oSettings, 'aoFooterCallback', 'footer', [ $(oSettings.nTFoot).children('tr')[0], 3451 _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] ); 3452 3453 // replaceChildren is faster, but only became widespread in 2020, 3454 // so a fall back in jQuery is provided for older browsers. 3455 if (body[0].replaceChildren) { 3456 body[0].replaceChildren.apply(body[0], anRows); 3457 } 3458 else { 3459 body.children().detach(); 3460 body.append( $(anRows) ); 3461 } 3462 3463 // Empty table needs a specific class 3464 $(oSettings.nTableWrapper).toggleClass('dt-empty-footer', $('tr', oSettings.nTFoot).length === 0); 3465 3466 /* Call all required callback functions for the end of a draw */ 3467 _fnCallbackFire( oSettings, 'aoDrawCallback', 'draw', [oSettings], true ); 3468 3469 /* Draw is complete, sorting and filtering must be as well */ 3470 oSettings.bSorted = false; 3471 oSettings.bFiltered = false; 3472 oSettings.bDrawing = false; 3473 } 3474 3475 3476 /** 3477 * Redraw the table - taking account of the various features which are enabled 3478 * @param {object} oSettings dataTables settings object 3479 * @param {boolean} [holdPosition] Keep the current paging position. By default 3480 * the paging is reset to the first page 3481 * @memberof DataTable#oApi 3482 */ 3483 function _fnReDraw( settings, holdPosition, recompute ) 3484 { 3485 var 3486 features = settings.oFeatures, 3487 sort = features.bSort, 3488 filter = features.bFilter; 3489 3490 if (recompute === undefined || recompute === true) { 3491 if ( sort ) { 3492 _fnSort( settings ); 3493 } 3494 3495 if ( filter ) { 3496 _fnFilterComplete( settings, settings.oPreviousSearch ); 3497 } 3498 else { 3499 // No filtering, so we want to just use the display master 3500 settings.aiDisplay = settings.aiDisplayMaster.slice(); 3501 } 3502 } 3503 3504 if ( holdPosition !== true ) { 3505 settings._iDisplayStart = 0; 3506 } 3507 3508 // Let any modules know about the draw hold position state (used by 3509 // scrolling internally) 3510 settings._drawHold = holdPosition; 3511 3512 _fnDraw( settings ); 3513 3514 settings._drawHold = false; 3515 } 3516 3517 3518 /* 3519 * Table is empty - create a row with an empty message in it 3520 */ 3521 function _emptyRow ( settings ) { 3522 var oLang = settings.oLanguage; 3523 var zero = oLang.sZeroRecords; 3524 var dataSrc = _fnDataSource( settings ); 3525 3526 if ( 3527 (settings.iDraw < 1 && dataSrc === 'ssp') || 3528 (settings.iDraw <= 1 && dataSrc === 'ajax') 3529 ) { 3530 zero = oLang.sLoadingRecords; 3531 } 3532 else if ( oLang.sEmptyTable && settings.fnRecordsTotal() === 0 ) 3533 { 3534 zero = oLang.sEmptyTable; 3535 } 3536 3537 return $( '<tr/>' ) 3538 .append( $('<td />', { 3539 'colSpan': _fnVisbleColumns( settings ), 3540 'class': settings.oClasses.empty.row 3541 } ).html( zero ) )[0]; 3542 } 3543 3544 3545 /** 3546 * Convert a `layout` object given by a user to the object structure needed 3547 * for the renderer. This is done twice, once for above and once for below 3548 * the table. Ordering must also be considered. 3549 * 3550 * @param {*} settings DataTables settings object 3551 * @param {*} layout Layout object to convert 3552 * @param {string} side `top` or `bottom` 3553 * @returns Converted array structure - one item for each row. 3554 */ 3555 function _layoutArray ( settings, layout, side ) 3556 { 3557 var groups = {}; 3558 3559 // Combine into like groups (e.g. `top`, `top2`, etc) 3560 $.each( layout, function ( pos, val ) { 3561 if (val === null) { 3562 return; 3563 } 3564 3565 var splitPos = pos.replace(/([A-Z])/g, ' $1').split(' '); 3566 3567 if ( ! groups[ splitPos[0] ] ) { 3568 groups[ splitPos[0] ] = {}; 3569 } 3570 3571 var align = splitPos.length === 1 ? 3572 'full' : 3573 splitPos[1].toLowerCase(); 3574 var group = groups[ splitPos[0] ]; 3575 var groupRun = function (contents, innerVal) { 3576 // If it is an object, then there can be multiple features contained in it 3577 if ( $.isPlainObject( innerVal ) ) { 3578 Object.keys(innerVal).map(function (key) { 3579 contents.push( { 3580 feature: key, 3581 opts: innerVal[key] 3582 }); 3583 }); 3584 } 3585 else { 3586 contents.push(innerVal); 3587 } 3588 } 3589 3590 // Transform to an object with a contents property 3591 if (! group[align] || ! group[align].contents) { 3592 group[align] = { contents: [] }; 3593 } 3594 3595 // Allow for an array or just a single object 3596 if ( Array.isArray(val)) { 3597 for (var i=0 ; i<val.length ; i++) { 3598 groupRun(group[align].contents, val[i]); 3599 } 3600 } 3601 else { 3602 groupRun(group[ align ].contents, val); 3603 } 3604 3605 // And make contents an array 3606 if ( ! Array.isArray( group[ align ].contents ) ) { 3607 group[ align ].contents = [ group[ align ].contents ]; 3608 } 3609 } ); 3610 3611 var filtered = Object.keys(groups) 3612 .map( function ( pos ) { 3613 // Filter to only the side we need 3614 if ( pos.indexOf(side) !== 0 ) { 3615 return null; 3616 } 3617 3618 return { 3619 name: pos, 3620 val: groups[pos] 3621 }; 3622 } ) 3623 .filter( function (item) { 3624 return item !== null; 3625 }); 3626 3627 // Order by item identifier 3628 filtered.sort( function ( a, b ) { 3629 var order1 = a.name.replace(/[^0-9]/g, '') * 1; 3630 var order2 = b.name.replace(/[^0-9]/g, '') * 1; 3631 3632 return order2 - order1; 3633 } ); 3634 3635 if ( side === 'bottom' ) { 3636 filtered.reverse(); 3637 } 3638 3639 // Split into rows 3640 var rows = []; 3641 for ( var i=0, ien=filtered.length ; i<ien ; i++ ) { 3642 if ( filtered[i].val.full ) { 3643 rows.push( { full: filtered[i].val.full } ); 3644 _layoutResolve( settings, rows[ rows.length - 1 ] ); 3645 3646 delete filtered[i].val.full; 3647 } 3648 3649 if ( Object.keys(filtered[i].val).length ) { 3650 rows.push( filtered[i].val ); 3651 _layoutResolve( settings, rows[ rows.length - 1 ] ); 3652 } 3653 } 3654 3655 return rows; 3656 } 3657 3658 3659 /** 3660 * Convert the contents of a row's layout object to nodes that can be inserted 3661 * into the document by a renderer. Execute functions, look up plug-ins, etc. 3662 * 3663 * @param {*} settings DataTables settings object 3664 * @param {*} row Layout object for this row 3665 */ 3666 function _layoutResolve( settings, row ) { 3667 var getFeature = function (feature, opts) { 3668 if ( ! _ext.features[ feature ] ) { 3669 _fnLog( settings, 0, 'Unknown feature: '+ feature ); 3670 } 3671 3672 return _ext.features[ feature ].apply( this, [settings, opts] ); 3673 }; 3674 3675 var resolve = function ( item ) { 3676 var line = row[ item ].contents; 3677 3678 for ( var i=0, ien=line.length ; i<ien ; i++ ) { 3679 if ( ! line[i] ) { 3680 continue; 3681 } 3682 else if ( typeof line[i] === 'string' ) { 3683 line[i] = getFeature( line[i], null ); 3684 } 3685 else if ( $.isPlainObject(line[i]) ) { 3686 // If it's an object, it just has feature and opts properties from 3687 // the transform in _layoutArray 3688 line[i] = getFeature(line[i].feature, line[i].opts); 3689 } 3690 else if ( typeof line[i].node === 'function' ) { 3691 line[i] = line[i].node( settings ); 3692 } 3693 else if ( typeof line[i] === 'function' ) { 3694 var inst = line[i]( settings ); 3695 3696 line[i] = typeof inst.node === 'function' ? 3697 inst.node() : 3698 inst; 3699 } 3700 } 3701 }; 3702 3703 $.each( row, function ( key ) { 3704 resolve( key ); 3705 } ); 3706 } 3707 3708 3709 /** 3710 * Add the options to the page HTML for the table 3711 * @param {object} settings DataTables settings object 3712 * @memberof DataTable#oApi 3713 */ 3714 function _fnAddOptionsHtml ( settings ) 3715 { 3716 var classes = settings.oClasses; 3717 var table = $(settings.nTable); 3718 3719 // Wrapper div around everything DataTables controls 3720 var insert = $('<div/>') 3721 .attr({ 3722 id: settings.sTableId+'_wrapper', 3723 'class': classes.container 3724 }) 3725 .insertBefore(table); 3726 3727 settings.nTableWrapper = insert[0]; 3728 3729 if (settings.sDom) { 3730 // Legacy 3731 _fnLayoutDom(settings, settings.sDom, insert); 3732 } 3733 else { 3734 var top = _layoutArray( settings, settings.layout, 'top' ); 3735 var bottom = _layoutArray( settings, settings.layout, 'bottom' ); 3736 var renderer = _fnRenderer( settings, 'layout' ); 3737 3738 // Everything above - the renderer will actually insert the contents into the document 3739 top.forEach(function (item) { 3740 renderer( settings, insert, item ); 3741 }); 3742 3743 // The table - always the center of attention 3744 renderer( settings, insert, { 3745 full: { 3746 table: true, 3747 contents: [ _fnFeatureHtmlTable(settings) ] 3748 } 3749 } ); 3750 3751 // Everything below 3752 bottom.forEach(function (item) { 3753 renderer( settings, insert, item ); 3754 }); 3755 } 3756 3757 // Processing floats on top, so it isn't an inserted feature 3758 _processingHtml( settings ); 3759 } 3760 3761 /** 3762 * Draw the table with the legacy DOM property 3763 * @param {*} settings DT settings object 3764 * @param {*} dom DOM string 3765 * @param {*} insert Insert point 3766 */ 3767 function _fnLayoutDom( settings, dom, insert ) 3768 { 3769 var parts = dom.match(/(".*?")|('.*?')|./g); 3770 var featureNode, option, newNode, next, attr; 3771 3772 for ( var i=0 ; i<parts.length ; i++ ) { 3773 featureNode = null; 3774 option = parts[i]; 3775 3776 if ( option == '<' ) { 3777 // New container div 3778 newNode = $('<div/>'); 3779 3780 // Check to see if we should append an id and/or a class name to the container 3781 next = parts[i+1]; 3782 3783 if ( next[0] == "'" || next[0] == '"' ) { 3784 attr = next.replace(/['"]/g, ''); 3785 3786 var id = '', className; 3787 3788 /* The attribute can be in the format of "#id.class", "#id" or "class" This logic 3789 * breaks the string into parts and applies them as needed 3790 */ 3791 if ( attr.indexOf('.') != -1 ) { 3792 var split = attr.split('.'); 3793 3794 id = split[0]; 3795 className = split[1]; 3796 } 3797 else if ( attr[0] == "#" ) { 3798 id = attr; 3799 } 3800 else { 3801 className = attr; 3802 } 3803 3804 newNode 3805 .attr('id', id.substring(1)) 3806 .addClass(className); 3807 3808 i++; // Move along the position array 3809 } 3810 3811 insert.append( newNode ); 3812 insert = newNode; 3813 } 3814 else if ( option == '>' ) { 3815 // End container div 3816 insert = insert.parent(); 3817 } 3818 else if ( option == 't' ) { 3819 // Table 3820 featureNode = _fnFeatureHtmlTable( settings ); 3821 } 3822 else 3823 { 3824 DataTable.ext.feature.forEach(function(feature) { 3825 if ( option == feature.cFeature ) { 3826 featureNode = feature.fnInit( settings ); 3827 } 3828 }); 3829 } 3830 3831 // Add to the display 3832 if ( featureNode ) { 3833 insert.append( featureNode ); 3834 } 3835 } 3836 } 3837 3838 3839 /** 3840 * Use the DOM source to create up an array of header cells. The idea here is to 3841 * create a layout grid (array) of rows x columns, which contains a reference 3842 * to the cell that that point in the grid (regardless of col/rowspan), such that 3843 * any column / row could be removed and the new grid constructed 3844 * @param {node} thead The header/footer element for the table 3845 * @returns {array} Calculated layout array 3846 * @memberof DataTable#oApi 3847 */ 3848 function _fnDetectHeader ( settings, thead, write ) 3849 { 3850 var columns = settings.aoColumns; 3851 var rows = $(thead).children('tr'); 3852 var row, cell; 3853 var i, k, l, iLen, shifted, column, colspan, rowspan; 3854 var isHeader = thead && thead.nodeName.toLowerCase() === 'thead'; 3855 var layout = []; 3856 var unique; 3857 var shift = function ( a, i, j ) { 3858 var k = a[i]; 3859 while ( k[j] ) { 3860 j++; 3861 } 3862 return j; 3863 }; 3864 3865 // We know how many rows there are in the layout - so prep it 3866 for ( i=0, iLen=rows.length ; i<iLen ; i++ ) { 3867 layout.push( [] ); 3868 } 3869 3870 for ( i=0, iLen=rows.length ; i<iLen ; i++ ) { 3871 row = rows[i]; 3872 column = 0; 3873 3874 // For every cell in the row.. 3875 cell = row.firstChild; 3876 while ( cell ) { 3877 if ( 3878 cell.nodeName.toUpperCase() == 'TD' || 3879 cell.nodeName.toUpperCase() == 'TH' 3880 ) { 3881 var cols = []; 3882 3883 // Get the col and rowspan attributes from the DOM and sanitise them 3884 colspan = cell.getAttribute('colspan') * 1; 3885 rowspan = cell.getAttribute('rowspan') * 1; 3886 colspan = (!colspan || colspan===0 || colspan===1) ? 1 : colspan; 3887 rowspan = (!rowspan || rowspan===0 || rowspan===1) ? 1 : rowspan; 3888 3889 // There might be colspan cells already in this row, so shift our target 3890 // accordingly 3891 shifted = shift( layout, i, column ); 3892 3893 // Cache calculation for unique columns 3894 unique = colspan === 1 ? 3895 true : 3896 false; 3897 3898 // Perform header setup 3899 if ( write ) { 3900 if (unique) { 3901 // Allow column options to be set from HTML attributes 3902 _fnColumnOptions( settings, shifted, $(cell).data() ); 3903 3904 // Get the width for the column. This can be defined from the 3905 // width attribute, style attribute or `columns.width` option 3906 var columnDef = columns[shifted]; 3907 var width = cell.getAttribute('width') || null; 3908 var t = cell.style.width.match(/width:\s*(\d+[pxem%]+)/); 3909 if ( t ) { 3910 width = t[1]; 3911 } 3912 3913 columnDef.sWidthOrig = columnDef.sWidth || width; 3914 3915 if (isHeader) { 3916 // Column title handling - can be user set, or read from the DOM 3917 // This happens before the render, so the original is still in place 3918 if ( columnDef.sTitle !== null && ! columnDef.autoTitle ) { 3919 cell.innerHTML = columnDef.sTitle; 3920 } 3921 3922 if (! columnDef.sTitle && unique) { 3923 columnDef.sTitle = _stripHtml(cell.innerHTML); 3924 columnDef.autoTitle = true; 3925 } 3926 } 3927 else { 3928 // Footer specific operations 3929 if (columnDef.footer) { 3930 cell.innerHTML = columnDef.footer; 3931 } 3932 } 3933 3934 // Fall back to the aria-label attribute on the table header if no ariaTitle is 3935 // provided. 3936 if (! columnDef.ariaTitle) { 3937 columnDef.ariaTitle = $(cell).attr("aria-label") || columnDef.sTitle; 3938 } 3939 3940 // Column specific class names 3941 if ( columnDef.className ) { 3942 $(cell).addClass( columnDef.className ); 3943 } 3944 } 3945 3946 // Wrap the column title so we can write to it in future 3947 if ( $('span.dt-column-title', cell).length === 0) { 3948 $('<span>') 3949 .addClass('dt-column-title') 3950 .append(cell.childNodes) 3951 .appendTo(cell); 3952 } 3953 3954 if ( isHeader && $('span.dt-column-order', cell).length === 0) { 3955 $('<span>') 3956 .addClass('dt-column-order') 3957 .appendTo(cell); 3958 } 3959 } 3960 3961 // If there is col / rowspan, copy the information into the layout grid 3962 for ( l=0 ; l<colspan ; l++ ) { 3963 for ( k=0 ; k<rowspan ; k++ ) { 3964 layout[i+k][shifted+l] = { 3965 cell: cell, 3966 unique: unique 3967 }; 3968 3969 layout[i+k].row = row; 3970 } 3971 3972 cols.push( shifted+l ); 3973 } 3974 3975 // Assign an attribute so spanning cells can still be identified 3976 // as belonging to a column 3977 cell.setAttribute('data-dt-column', _unique(cols).join(',')); 3978 } 3979 3980 cell = cell.nextSibling; 3981 } 3982 } 3983 3984 return layout; 3985 } 3986 3987 /** 3988 * Set the start position for draw 3989 * @param {object} oSettings dataTables settings object 3990 */ 3991 function _fnStart( oSettings ) 3992 { 3993 var bServerSide = _fnDataSource( oSettings ) == 'ssp'; 3994 var iInitDisplayStart = oSettings.iInitDisplayStart; 3995 3996 // Check and see if we have an initial draw position from state saving 3997 if ( iInitDisplayStart !== undefined && iInitDisplayStart !== -1 ) 3998 { 3999 oSettings._iDisplayStart = bServerSide ? 4000 iInitDisplayStart : 4001 iInitDisplayStart >= oSettings.fnRecordsDisplay() ? 4002 0 : 4003 iInitDisplayStart; 4004 4005 oSettings.iInitDisplayStart = -1; 4006 } 4007 } 4008 4009 /** 4010 * Create an Ajax call based on the table's settings, taking into account that 4011 * parameters can have multiple forms, and backwards compatibility. 4012 * 4013 * @param {object} oSettings dataTables settings object 4014 * @param {array} data Data to send to the server, required by 4015 * DataTables - may be augmented by developer callbacks 4016 * @param {function} fn Callback function to run when data is obtained 4017 */ 4018 function _fnBuildAjax( oSettings, data, fn ) 4019 { 4020 var ajaxData; 4021 var ajax = oSettings.ajax; 4022 var instance = oSettings.oInstance; 4023 var callback = function ( json ) { 4024 var status = oSettings.jqXHR 4025 ? oSettings.jqXHR.status 4026 : null; 4027 4028 if ( json === null || (typeof status === 'number' && status == 204 ) ) { 4029 json = {}; 4030 _fnAjaxDataSrc( oSettings, json, [] ); 4031 } 4032 4033 var error = json.error || json.sError; 4034 if ( error ) { 4035 _fnLog( oSettings, 0, error ); 4036 } 4037 4038 oSettings.json = json; 4039 4040 _fnCallbackFire( oSettings, null, 'xhr', [oSettings, json, oSettings.jqXHR], true ); 4041 fn( json ); 4042 }; 4043 4044 if ( $.isPlainObject( ajax ) && ajax.data ) 4045 { 4046 ajaxData = ajax.data; 4047 4048 var newData = typeof ajaxData === 'function' ? 4049 ajaxData( data, oSettings ) : // fn can manipulate data or return 4050 ajaxData; // an object object or array to merge 4051 4052 // If the function returned something, use that alone 4053 data = typeof ajaxData === 'function' && newData ? 4054 newData : 4055 $.extend( true, data, newData ); 4056 4057 // Remove the data property as we've resolved it already and don't want 4058 // jQuery to do it again (it is restored at the end of the function) 4059 delete ajax.data; 4060 } 4061 4062 var baseAjax = { 4063 "url": typeof ajax === 'string' ? 4064 ajax : 4065 '', 4066 "data": data, 4067 "success": callback, 4068 "dataType": "json", 4069 "cache": false, 4070 "type": oSettings.sServerMethod, 4071 "error": function (xhr, error) { 4072 var ret = _fnCallbackFire( oSettings, null, 'xhr', [oSettings, null, oSettings.jqXHR], true ); 4073 4074 if ( ret.indexOf(true) === -1 ) { 4075 if ( error == "parsererror" ) { 4076 _fnLog( oSettings, 0, 'Invalid JSON response', 1 ); 4077 } 4078 else if ( xhr.readyState === 4 ) { 4079 _fnLog( oSettings, 0, 'Ajax error', 7 ); 4080 } 4081 } 4082 4083 _fnProcessingDisplay( oSettings, false ); 4084 } 4085 }; 4086 4087 // If `ajax` option is an object, extend and override our default base 4088 if ( $.isPlainObject( ajax ) ) { 4089 $.extend( baseAjax, ajax ) 4090 } 4091 4092 // Store the data submitted for the API 4093 oSettings.oAjaxData = data; 4094 4095 // Allow plug-ins and external processes to modify the data 4096 _fnCallbackFire( oSettings, null, 'preXhr', [oSettings, data, baseAjax], true ); 4097 4098 if ( typeof ajax === 'function' ) 4099 { 4100 // Is a function - let the caller define what needs to be done 4101 oSettings.jqXHR = ajax.call( instance, data, callback, oSettings ); 4102 } 4103 else if (ajax.url === '') { 4104 // No url, so don't load any data. Just apply an empty data array 4105 // to the object for the callback. 4106 var empty = {}; 4107 4108 DataTable.util.set(ajax.dataSrc)(empty, []); 4109 callback(empty); 4110 } 4111 else { 4112 // Object to extend the base settings 4113 oSettings.jqXHR = $.ajax( baseAjax ); 4114 4115 // Restore for next time around 4116 if ( ajaxData ) { 4117 ajax.data = ajaxData; 4118 } 4119 } 4120 } 4121 4122 4123 /** 4124 * Update the table using an Ajax call 4125 * @param {object} settings dataTables settings object 4126 * @returns {boolean} Block the table drawing or not 4127 * @memberof DataTable#oApi 4128 */ 4129 function _fnAjaxUpdate( settings ) 4130 { 4131 settings.iDraw++; 4132 _fnProcessingDisplay( settings, true ); 4133 4134 _fnBuildAjax( 4135 settings, 4136 _fnAjaxParameters( settings ), 4137 function(json) { 4138 _fnAjaxUpdateDraw( settings, json ); 4139 } 4140 ); 4141 } 4142 4143 4144 /** 4145 * Build up the parameters in an object needed for a server-side processing 4146 * request. 4147 * @param {object} oSettings dataTables settings object 4148 * @returns {bool} block the table drawing or not 4149 * @memberof DataTable#oApi 4150 */ 4151 function _fnAjaxParameters( settings ) 4152 { 4153 var 4154 columns = settings.aoColumns, 4155 features = settings.oFeatures, 4156 preSearch = settings.oPreviousSearch, 4157 preColSearch = settings.aoPreSearchCols, 4158 colData = function ( idx, prop ) { 4159 return typeof columns[idx][prop] === 'function' ? 4160 'function' : 4161 columns[idx][prop]; 4162 }; 4163 4164 return { 4165 draw: settings.iDraw, 4166 columns: columns.map( function ( column, i ) { 4167 return { 4168 data: colData(i, 'mData'), 4169 name: column.sName, 4170 searchable: column.bSearchable, 4171 orderable: column.bSortable, 4172 search: { 4173 value: preColSearch[i].search, 4174 regex: preColSearch[i].regex, 4175 fixed: Object.keys(column.searchFixed).map( function(name) { 4176 return { 4177 name: name, 4178 term: column.searchFixed[name].toString() 4179 } 4180 }) 4181 } 4182 }; 4183 } ), 4184 order: _fnSortFlatten( settings ).map( function ( val ) { 4185 return { 4186 column: val.col, 4187 dir: val.dir, 4188 name: colData(val.col, 'sName') 4189 }; 4190 } ), 4191 start: settings._iDisplayStart, 4192 length: features.bPaginate ? 4193 settings._iDisplayLength : 4194 -1, 4195 search: { 4196 value: preSearch.search, 4197 regex: preSearch.regex, 4198 fixed: Object.keys(settings.searchFixed).map( function(name) { 4199 return { 4200 name: name, 4201 term: settings.searchFixed[name].toString() 4202 } 4203 }) 4204 } 4205 }; 4206 } 4207 4208 4209 /** 4210 * Data the data from the server (nuking the old) and redraw the table 4211 * @param {object} oSettings dataTables settings object 4212 * @param {object} json json data return from the server. 4213 * @param {string} json.sEcho Tracking flag for DataTables to match requests 4214 * @param {int} json.iTotalRecords Number of records in the data set, not accounting for filtering 4215 * @param {int} json.iTotalDisplayRecords Number of records in the data set, accounting for filtering 4216 * @param {array} json.aaData The data to display on this page 4217 * @param {string} [json.sColumns] Column ordering (sName, comma separated) 4218 * @memberof DataTable#oApi 4219 */ 4220 function _fnAjaxUpdateDraw ( settings, json ) 4221 { 4222 var data = _fnAjaxDataSrc(settings, json); 4223 var draw = _fnAjaxDataSrcParam(settings, 'draw', json); 4224 var recordsTotal = _fnAjaxDataSrcParam(settings, 'recordsTotal', json); 4225 var recordsFiltered = _fnAjaxDataSrcParam(settings, 'recordsFiltered', json); 4226 4227 if ( draw !== undefined ) { 4228 // Protect against out of sequence returns 4229 if ( draw*1 < settings.iDraw ) { 4230 return; 4231 } 4232 settings.iDraw = draw * 1; 4233 } 4234 4235 // No data in returned object, so rather than an array, we show an empty table 4236 if ( ! data ) { 4237 data = []; 4238 } 4239 4240 _fnClearTable( settings ); 4241 settings._iRecordsTotal = parseInt(recordsTotal, 10); 4242 settings._iRecordsDisplay = parseInt(recordsFiltered, 10); 4243 4244 for ( var i=0, ien=data.length ; i<ien ; i++ ) { 4245 _fnAddData( settings, data[i] ); 4246 } 4247 settings.aiDisplay = settings.aiDisplayMaster.slice(); 4248 4249 _fnDraw( settings, true ); 4250 _fnInitComplete( settings ); 4251 _fnProcessingDisplay( settings, false ); 4252 } 4253 4254 4255 /** 4256 * Get the data from the JSON data source to use for drawing a table. Using 4257 * `_fnGetObjectDataFn` allows the data to be sourced from a property of the 4258 * source object, or from a processing function. 4259 * @param {object} settings dataTables settings object 4260 * @param {object} json Data source object / array from the server 4261 * @return {array} Array of data to use 4262 */ 4263 function _fnAjaxDataSrc ( settings, json, write ) 4264 { 4265 var dataProp = 'data'; 4266 4267 if ($.isPlainObject( settings.ajax ) && settings.ajax.dataSrc !== undefined) { 4268 // Could in inside a `dataSrc` object, or not! 4269 var dataSrc = settings.ajax.dataSrc; 4270 4271 // string, function and object are valid types 4272 if (typeof dataSrc === 'string' || typeof dataSrc === 'function') { 4273 dataProp = dataSrc; 4274 } 4275 else if (dataSrc.data !== undefined) { 4276 dataProp = dataSrc.data; 4277 } 4278 } 4279 4280 if ( ! write ) { 4281 if ( dataProp === 'data' ) { 4282 // If the default, then we still want to support the old style, and safely ignore 4283 // it if possible 4284 return json.aaData || json[dataProp]; 4285 } 4286 4287 return dataProp !== "" ? 4288 _fnGetObjectDataFn( dataProp )( json ) : 4289 json; 4290 } 4291 4292 // set 4293 _fnSetObjectDataFn( dataProp )( json, write ); 4294 } 4295 4296 /** 4297 * Very similar to _fnAjaxDataSrc, but for the other SSP properties 4298 * @param {*} settings DataTables settings object 4299 * @param {*} param Target parameter 4300 * @param {*} json JSON data 4301 * @returns Resolved value 4302 */ 4303 function _fnAjaxDataSrcParam (settings, param, json) { 4304 var dataSrc = $.isPlainObject( settings.ajax ) 4305 ? settings.ajax.dataSrc 4306 : null; 4307 4308 if (dataSrc && dataSrc[param]) { 4309 // Get from custom location 4310 return _fnGetObjectDataFn( dataSrc[param] )( json ); 4311 } 4312 4313 // else - Default behaviour 4314 var old = ''; 4315 4316 // Legacy support 4317 if (param === 'draw') { 4318 old = 'sEcho'; 4319 } 4320 else if (param === 'recordsTotal') { 4321 old = 'iTotalRecords'; 4322 } 4323 else if (param === 'recordsFiltered') { 4324 old = 'iTotalDisplayRecords'; 4325 } 4326 4327 return json[old] !== undefined 4328 ? json[old] 4329 : json[param]; 4330 } 4331 4332 4333 /** 4334 * Filter the table using both the global filter and column based filtering 4335 * @param {object} settings dataTables settings object 4336 * @param {object} input search information 4337 * @memberof DataTable#oApi 4338 */ 4339 function _fnFilterComplete ( settings, input ) 4340 { 4341 var columnsSearch = settings.aoPreSearchCols; 4342 4343 // Resolve any column types that are unknown due to addition or invalidation 4344 // @todo As per sort - can this be moved into an event handler? 4345 _fnColumnTypes( settings ); 4346 4347 // In server-side processing all filtering is done by the server, so no point hanging around here 4348 if ( _fnDataSource( settings ) != 'ssp' ) 4349 { 4350 // Check if any of the rows were invalidated 4351 _fnFilterData( settings ); 4352 4353 // Start from the full data set 4354 settings.aiDisplay = settings.aiDisplayMaster.slice(); 4355 4356 // Global filter first 4357 _fnFilter( settings.aiDisplay, settings, input.search, input ); 4358 4359 $.each(settings.searchFixed, function (name, term) { 4360 _fnFilter(settings.aiDisplay, settings, term, {}); 4361 }); 4362 4363 // Then individual column filters 4364 for ( var i=0 ; i<columnsSearch.length ; i++ ) 4365 { 4366 var col = columnsSearch[i]; 4367 4368 _fnFilter( 4369 settings.aiDisplay, 4370 settings, 4371 col.search, 4372 col, 4373 i 4374 ); 4375 4376 $.each(settings.aoColumns[i].searchFixed, function (name, term) { 4377 _fnFilter(settings.aiDisplay, settings, term, {}, i); 4378 }); 4379 } 4380 4381 // And finally global filtering 4382 _fnFilterCustom( settings ); 4383 } 4384 4385 // Tell the draw function we have been filtering 4386 settings.bFiltered = true; 4387 4388 _fnCallbackFire( settings, null, 'search', [settings] ); 4389 } 4390 4391 4392 /** 4393 * Apply custom filtering functions 4394 * 4395 * This is legacy now that we have named functions, but it is widely used 4396 * from 1.x, so it is not yet deprecated. 4397 * @param {object} oSettings dataTables settings object 4398 * @memberof DataTable#oApi 4399 */ 4400 function _fnFilterCustom( settings ) 4401 { 4402 var filters = DataTable.ext.search; 4403 var displayRows = settings.aiDisplay; 4404 var row, rowIdx; 4405 4406 for ( var i=0, ien=filters.length ; i<ien ; i++ ) { 4407 var rows = []; 4408 4409 // Loop over each row and see if it should be included 4410 for ( var j=0, jen=displayRows.length ; j<jen ; j++ ) { 4411 rowIdx = displayRows[ j ]; 4412 row = settings.aoData[ rowIdx ]; 4413 4414 if ( filters[i]( settings, row._aFilterData, rowIdx, row._aData, j ) ) { 4415 rows.push( rowIdx ); 4416 } 4417 } 4418 4419 // So the array reference doesn't break set the results into the 4420 // existing array 4421 displayRows.length = 0; 4422 displayRows.push.apply(displayRows, rows); 4423 } 4424 } 4425 4426 4427 /** 4428 * Filter the data table based on user input and draw the table 4429 */ 4430 function _fnFilter( searchRows, settings, input, options, column ) 4431 { 4432 if ( input === '' ) { 4433 return; 4434 } 4435 4436 var i = 0; 4437 var matched = []; 4438 4439 // Search term can be a function, regex or string - if a string we apply our 4440 // smart filtering regex (assuming the options require that) 4441 var searchFunc = typeof input === 'function' ? input : null; 4442 var rpSearch = input instanceof RegExp 4443 ? input 4444 : searchFunc 4445 ? null 4446 : _fnFilterCreateSearch( input, options ); 4447 4448 // Then for each row, does the test pass. If not, lop the row from the array 4449 for (i=0 ; i<searchRows.length ; i++) { 4450 var row = settings.aoData[ searchRows[i] ]; 4451 var data = column === undefined 4452 ? row._sFilterRow 4453 : row._aFilterData[ column ]; 4454 4455 if ( (searchFunc && searchFunc(data, row._aData, searchRows[i], column)) || (rpSearch && rpSearch.test(data)) ) { 4456 matched.push(searchRows[i]); 4457 } 4458 } 4459 4460 // Mutate the searchRows array 4461 searchRows.length = matched.length; 4462 4463 for (i=0 ; i<matched.length ; i++) { 4464 searchRows[i] = matched[i]; 4465 } 4466 } 4467 4468 4469 /** 4470 * Build a regular expression object suitable for searching a table 4471 * @param {string} sSearch string to search for 4472 * @param {bool} bRegex treat as a regular expression or not 4473 * @param {bool} bSmart perform smart filtering or not 4474 * @param {bool} bCaseInsensitive Do case insensitive matching or not 4475 * @returns {RegExp} constructed object 4476 * @memberof DataTable#oApi 4477 */ 4478 function _fnFilterCreateSearch( search, inOpts ) 4479 { 4480 var not = []; 4481 var options = $.extend({}, { 4482 boundary: false, 4483 caseInsensitive: true, 4484 exact: false, 4485 regex: false, 4486 smart: true 4487 }, inOpts); 4488 4489 if (typeof search !== 'string') { 4490 search = search.toString(); 4491 } 4492 4493 // Remove diacritics if normalize is set up to do so 4494 search = _normalize(search); 4495 4496 if (options.exact) { 4497 return new RegExp( 4498 '^'+_fnEscapeRegex(search)+'$', 4499 options.caseInsensitive ? 'i' : '' 4500 ); 4501 } 4502 4503 search = options.regex ? 4504 search : 4505 _fnEscapeRegex( search ); 4506 4507 if ( options.smart ) { 4508 /* For smart filtering we want to allow the search to work regardless of 4509 * word order. We also want double quoted text to be preserved, so word 4510 * order is important - a la google. And a negative look around for 4511 * finding rows which don't contain a given string. 4512 * 4513 * So this is the sort of thing we want to generate: 4514 * 4515 * ^(?=.*?\bone\b)(?=.*?\btwo three\b)(?=.*?\bfour\b).*$ 4516 */ 4517 var parts = search.match( /!?["\u201C][^"\u201D]+["\u201D]|[^ ]+/g ) || ['']; 4518 var a = parts.map( function ( word ) { 4519 var negative = false; 4520 var m; 4521 4522 // Determine if it is a "does not include" 4523 if ( word.charAt(0) === '!' ) { 4524 negative = true; 4525 word = word.substring(1); 4526 } 4527 4528 // Strip the quotes from around matched phrases 4529 if ( word.charAt(0) === '"' ) { 4530 m = word.match( /^"(.*)"$/ ); 4531 word = m ? m[1] : word; 4532 } 4533 else if ( word.charAt(0) === '\u201C' ) { 4534 // Smart quote match (iPhone users) 4535 m = word.match( /^\u201C(.*)\u201D$/ ); 4536 word = m ? m[1] : word; 4537 } 4538 4539 // For our "not" case, we need to modify the string that is 4540 // allowed to match at the end of the expression. 4541 if (negative) { 4542 if (word.length > 1) { 4543 not.push('(?!'+word+')'); 4544 } 4545 4546 word = ''; 4547 } 4548 4549 return word.replace(/"/g, ''); 4550 } ); 4551 4552 var match = not.length 4553 ? not.join('') 4554 : ''; 4555 4556 var boundary = options.boundary 4557 ? '\\b' 4558 : ''; 4559 4560 search = '^(?=.*?'+boundary+a.join( ')(?=.*?'+boundary )+')('+match+'.)*$'; 4561 } 4562 4563 return new RegExp( search, options.caseInsensitive ? 'i' : '' ); 4564 } 4565 4566 4567 /** 4568 * Escape a string such that it can be used in a regular expression 4569 * @param {string} sVal string to escape 4570 * @returns {string} escaped string 4571 * @memberof DataTable#oApi 4572 */ 4573 var _fnEscapeRegex = DataTable.util.escapeRegex; 4574 4575 var __filter_div = $('<div>')[0]; 4576 var __filter_div_textContent = __filter_div.textContent !== undefined; 4577 4578 // Update the filtering data for each row if needed (by invalidation or first run) 4579 function _fnFilterData ( settings ) 4580 { 4581 var columns = settings.aoColumns; 4582 var data = settings.aoData; 4583 var column; 4584 var j, jen, filterData, cellData, row; 4585 var wasInvalidated = false; 4586 4587 for ( var rowIdx=0 ; rowIdx<data.length ; rowIdx++ ) { 4588 if (! data[rowIdx]) { 4589 continue; 4590 } 4591 4592 row = data[rowIdx]; 4593 4594 if ( ! row._aFilterData ) { 4595 filterData = []; 4596 4597 for ( j=0, jen=columns.length ; j<jen ; j++ ) { 4598 column = columns[j]; 4599 4600 if ( column.bSearchable ) { 4601 cellData = _fnGetCellData( settings, rowIdx, j, 'filter' ); 4602 4603 // Search in DataTables is string based 4604 if ( cellData === null ) { 4605 cellData = ''; 4606 } 4607 4608 if ( typeof cellData !== 'string' && cellData.toString ) { 4609 cellData = cellData.toString(); 4610 } 4611 } 4612 else { 4613 cellData = ''; 4614 } 4615 4616 // If it looks like there is an HTML entity in the string, 4617 // attempt to decode it so sorting works as expected. Note that 4618 // we could use a single line of jQuery to do this, but the DOM 4619 // method used here is much faster https://jsperf.com/html-decode 4620 if ( cellData.indexOf && cellData.indexOf('&') !== -1 ) { 4621 __filter_div.innerHTML = cellData; 4622 cellData = __filter_div_textContent ? 4623 __filter_div.textContent : 4624 __filter_div.innerText; 4625 } 4626 4627 if ( cellData.replace ) { 4628 cellData = cellData.replace(/[\r\n\u2028]/g, ''); 4629 } 4630 4631 filterData.push( cellData ); 4632 } 4633 4634 row._aFilterData = filterData; 4635 row._sFilterRow = filterData.join(' '); 4636 wasInvalidated = true; 4637 } 4638 } 4639 4640 return wasInvalidated; 4641 } 4642 4643 4644 /** 4645 * Draw the table for the first time, adding all required features 4646 * @param {object} settings dataTables settings object 4647 * @memberof DataTable#oApi 4648 */ 4649 function _fnInitialise ( settings ) 4650 { 4651 var i, iAjaxStart=settings.iInitDisplayStart; 4652 4653 /* Ensure that the table data is fully initialised */ 4654 if ( ! settings.bInitialised ) { 4655 setTimeout( function(){ _fnInitialise( settings ); }, 200 ); 4656 return; 4657 } 4658 4659 /* Build and draw the header / footer for the table */ 4660 _fnBuildHead( settings, 'header' ); 4661 _fnBuildHead( settings, 'footer' ); 4662 _fnDrawHead( settings, settings.aoHeader ); 4663 _fnDrawHead( settings, settings.aoFooter ); 4664 4665 // Enable features 4666 _fnAddOptionsHtml( settings ); 4667 _fnSortInit( settings ); 4668 4669 _colGroup( settings ); 4670 4671 /* Okay to show that something is going on now */ 4672 _fnProcessingDisplay( settings, true ); 4673 4674 _fnCallbackFire( settings, null, 'preInit', [settings], true ); 4675 4676 // If there is default sorting required - let's do it. The sort function 4677 // will do the drawing for us. Otherwise we draw the table regardless of the 4678 // Ajax source - this allows the table to look initialised for Ajax sourcing 4679 // data (show 'loading' message possibly) 4680 _fnReDraw( settings ); 4681 4682 var dataSrc = _fnDataSource( settings ); 4683 4684 // Server-side processing init complete is done by _fnAjaxUpdateDraw 4685 if ( dataSrc != 'ssp' ) { 4686 // if there is an ajax source load the data 4687 if ( dataSrc == 'ajax' ) { 4688 _fnBuildAjax( settings, {}, function(json) { 4689 var aData = _fnAjaxDataSrc( settings, json ); 4690 4691 // Got the data - add it to the table 4692 for ( i=0 ; i<aData.length ; i++ ) { 4693 _fnAddData( settings, aData[i] ); 4694 } 4695 4696 // Reset the init display for cookie saving. We've already done 4697 // a filter, and therefore cleared it before. So we need to make 4698 // it appear 'fresh' 4699 settings.iInitDisplayStart = iAjaxStart; 4700 4701 _fnReDraw( settings ); 4702 _fnProcessingDisplay( settings, false ); 4703 _fnInitComplete( settings ); 4704 }, settings ); 4705 } 4706 else { 4707 _fnInitComplete( settings ); 4708 _fnProcessingDisplay( settings, false ); 4709 } 4710 } 4711 } 4712 4713 4714 /** 4715 * Draw the table for the first time, adding all required features 4716 * @param {object} settings dataTables settings object 4717 * @memberof DataTable#oApi 4718 */ 4719 function _fnInitComplete ( settings ) 4720 { 4721 if (settings._bInitComplete) { 4722 return; 4723 } 4724 4725 var args = [settings, settings.json]; 4726 4727 settings._bInitComplete = true; 4728 4729 // Table is fully set up and we have data, so calculate the 4730 // column widths 4731 _fnAdjustColumnSizing( settings ); 4732 4733 _fnCallbackFire( settings, null, 'plugin-init', args, true ); 4734 _fnCallbackFire( settings, 'aoInitComplete', 'init', args, true ); 4735 } 4736 4737 function _fnLengthChange ( settings, val ) 4738 { 4739 var len = parseInt( val, 10 ); 4740 settings._iDisplayLength = len; 4741 4742 _fnLengthOverflow( settings ); 4743 4744 // Fire length change event 4745 _fnCallbackFire( settings, null, 'length', [settings, len] ); 4746 } 4747 4748 /** 4749 * Alter the display settings to change the page 4750 * @param {object} settings DataTables settings object 4751 * @param {string|int} action Paging action to take: "first", "previous", 4752 * "next" or "last" or page number to jump to (integer) 4753 * @param [bool] redraw Automatically draw the update or not 4754 * @returns {bool} true page has changed, false - no change 4755 * @memberof DataTable#oApi 4756 */ 4757 function _fnPageChange ( settings, action, redraw ) 4758 { 4759 var 4760 start = settings._iDisplayStart, 4761 len = settings._iDisplayLength, 4762 records = settings.fnRecordsDisplay(); 4763 4764 if ( records === 0 || len === -1 ) 4765 { 4766 start = 0; 4767 } 4768 else if ( typeof action === "number" ) 4769 { 4770 start = action * len; 4771 4772 if ( start > records ) 4773 { 4774 start = 0; 4775 } 4776 } 4777 else if ( action == "first" ) 4778 { 4779 start = 0; 4780 } 4781 else if ( action == "previous" ) 4782 { 4783 start = len >= 0 ? 4784 start - len : 4785 0; 4786 4787 if ( start < 0 ) 4788 { 4789 start = 0; 4790 } 4791 } 4792 else if ( action == "next" ) 4793 { 4794 if ( start + len < records ) 4795 { 4796 start += len; 4797 } 4798 } 4799 else if ( action == "last" ) 4800 { 4801 start = Math.floor( (records-1) / len) * len; 4802 } 4803 else if ( action === 'ellipsis' ) 4804 { 4805 return; 4806 } 4807 else 4808 { 4809 _fnLog( settings, 0, "Unknown paging action: "+action, 5 ); 4810 } 4811 4812 var changed = settings._iDisplayStart !== start; 4813 settings._iDisplayStart = start; 4814 4815 _fnCallbackFire( settings, null, changed ? 'page' : 'page-nc', [settings] ); 4816 4817 if ( changed && redraw ) { 4818 _fnDraw( settings ); 4819 } 4820 4821 return changed; 4822 } 4823 4824 4825 /** 4826 * Generate the node required for the processing node 4827 * @param {object} settings DataTables settings object 4828 */ 4829 function _processingHtml ( settings ) 4830 { 4831 var table = settings.nTable; 4832 var scrolling = settings.oScroll.sX !== '' || settings.oScroll.sY !== ''; 4833 4834 if ( settings.oFeatures.bProcessing ) { 4835 var n = $('<div/>', { 4836 'id': settings.sTableId + '_processing', 4837 'class': settings.oClasses.processing.container, 4838 'role': 'status' 4839 } ) 4840 .html( settings.oLanguage.sProcessing ) 4841 .append('<div><div></div><div></div><div></div><div></div></div>'); 4842 4843 // Different positioning depending on if scrolling is enabled or not 4844 if (scrolling) { 4845 n.prependTo( $('div.dt-scroll', settings.nTableWrapper) ); 4846 } 4847 else { 4848 n.insertBefore( table ); 4849 } 4850 4851 $(table).on( 'processing.dt.DT', function (e, s, show) { 4852 n.css( 'display', show ? 'block' : 'none' ); 4853 } ); 4854 } 4855 } 4856 4857 4858 /** 4859 * Display or hide the processing indicator 4860 * @param {object} settings DataTables settings object 4861 * @param {bool} show Show the processing indicator (true) or not (false) 4862 */ 4863 function _fnProcessingDisplay ( settings, show ) 4864 { 4865 _fnCallbackFire( settings, null, 'processing', [settings, show] ); 4866 } 4867 /** 4868 * Add any control elements for the table - specifically scrolling 4869 * @param {object} settings dataTables settings object 4870 * @returns {node} Node to add to the DOM 4871 * @memberof DataTable#oApi 4872 */ 4873 function _fnFeatureHtmlTable ( settings ) 4874 { 4875 var table = $(settings.nTable); 4876 4877 // Scrolling from here on in 4878 var scroll = settings.oScroll; 4879 4880 if ( scroll.sX === '' && scroll.sY === '' ) { 4881 return settings.nTable; 4882 } 4883 4884 var scrollX = scroll.sX; 4885 var scrollY = scroll.sY; 4886 var classes = settings.oClasses.scrolling; 4887 var caption = settings.captionNode; 4888 var captionSide = caption ? caption._captionSide : null; 4889 var headerClone = $( table[0].cloneNode(false) ); 4890 var footerClone = $( table[0].cloneNode(false) ); 4891 var footer = table.children('tfoot'); 4892 var _div = '<div/>'; 4893 var size = function ( s ) { 4894 return !s ? null : _fnStringToCss( s ); 4895 }; 4896 4897 if ( ! footer.length ) { 4898 footer = null; 4899 } 4900 4901 /* 4902 * The HTML structure that we want to generate in this function is: 4903 * div - scroller 4904 * div - scroll head 4905 * div - scroll head inner 4906 * table - scroll head table 4907 * thead - thead 4908 * div - scroll body 4909 * table - table (master table) 4910 * thead - thead clone for sizing 4911 * tbody - tbody 4912 * div - scroll foot 4913 * div - scroll foot inner 4914 * table - scroll foot table 4915 * tfoot - tfoot 4916 */ 4917 var scroller = $( _div, { 'class': classes.container } ) 4918 .append( 4919 $(_div, { 'class': classes.header.self } ) 4920 .css( { 4921 overflow: 'hidden', 4922 position: 'relative', 4923 border: 0, 4924 width: scrollX ? size(scrollX) : '100%' 4925 } ) 4926 .append( 4927 $(_div, { 'class': classes.header.inner } ) 4928 .css( { 4929 'box-sizing': 'content-box', 4930 width: scroll.sXInner || '100%' 4931 } ) 4932 .append( 4933 headerClone 4934 .removeAttr('id') 4935 .css( 'margin-left', 0 ) 4936 .append( captionSide === 'top' ? caption : null ) 4937 .append( 4938 table.children('thead') 4939 ) 4940 ) 4941 ) 4942 ) 4943 .append( 4944 $(_div, { 'class': classes.body } ) 4945 .css( { 4946 position: 'relative', 4947 overflow: 'auto', 4948 width: size( scrollX ) 4949 } ) 4950 .append( table ) 4951 ); 4952 4953 if ( footer ) { 4954 scroller.append( 4955 $(_div, { 'class': classes.footer.self } ) 4956 .css( { 4957 overflow: 'hidden', 4958 border: 0, 4959 width: scrollX ? size(scrollX) : '100%' 4960 } ) 4961 .append( 4962 $(_div, { 'class': classes.footer.inner } ) 4963 .append( 4964 footerClone 4965 .removeAttr('id') 4966 .css( 'margin-left', 0 ) 4967 .append( captionSide === 'bottom' ? caption : null ) 4968 .append( 4969 table.children('tfoot') 4970 ) 4971 ) 4972 ) 4973 ); 4974 } 4975 4976 var children = scroller.children(); 4977 var scrollHead = children[0]; 4978 var scrollBody = children[1]; 4979 var scrollFoot = footer ? children[2] : null; 4980 4981 // When the body is scrolled, then we also want to scroll the headers 4982 $(scrollBody).on( 'scroll.DT', function () { 4983 var scrollLeft = this.scrollLeft; 4984 4985 scrollHead.scrollLeft = scrollLeft; 4986 4987 if ( footer ) { 4988 scrollFoot.scrollLeft = scrollLeft; 4989 } 4990 } ); 4991 4992 // When focus is put on the header cells, we might need to scroll the body 4993 $('th, td', scrollHead).on('focus', function () { 4994 var scrollLeft = scrollHead.scrollLeft; 4995 4996 scrollBody.scrollLeft = scrollLeft; 4997 4998 if ( footer ) { 4999 scrollBody.scrollLeft = scrollLeft; 5000 } 5001 }); 5002 5003 $(scrollBody).css('max-height', scrollY); 5004 if (! scroll.bCollapse) { 5005 $(scrollBody).css('height', scrollY); 5006 } 5007 5008 settings.nScrollHead = scrollHead; 5009 settings.nScrollBody = scrollBody; 5010 settings.nScrollFoot = scrollFoot; 5011 5012 // On redraw - align columns 5013 settings.aoDrawCallback.push(_fnScrollDraw); 5014 5015 return scroller[0]; 5016 } 5017 5018 5019 5020 /** 5021 * Update the header, footer and body tables for resizing - i.e. column 5022 * alignment. 5023 * 5024 * Welcome to the most horrible function DataTables. The process that this 5025 * function follows is basically: 5026 * 1. Re-create the table inside the scrolling div 5027 * 2. Correct colgroup > col values if needed 5028 * 3. Copy colgroup > col over to header and footer 5029 * 4. Clean up 5030 * 5031 * @param {object} settings dataTables settings object 5032 * @memberof DataTable#oApi 5033 */ 5034 function _fnScrollDraw ( settings ) 5035 { 5036 // Given that this is such a monster function, a lot of variables are use 5037 // to try and keep the minimised size as small as possible 5038 var 5039 scroll = settings.oScroll, 5040 barWidth = scroll.iBarWidth, 5041 divHeader = $(settings.nScrollHead), 5042 divHeaderInner = divHeader.children('div'), 5043 divHeaderTable = divHeaderInner.children('table'), 5044 divBodyEl = settings.nScrollBody, 5045 divBody = $(divBodyEl), 5046 divFooter = $(settings.nScrollFoot), 5047 divFooterInner = divFooter.children('div'), 5048 divFooterTable = divFooterInner.children('table'), 5049 header = $(settings.nTHead), 5050 table = $(settings.nTable), 5051 footer = settings.nTFoot && $('th, td', settings.nTFoot).length ? $(settings.nTFoot) : null, 5052 browser = settings.oBrowser, 5053 headerCopy, footerCopy; 5054 5055 // If the scrollbar visibility has changed from the last draw, we need to 5056 // adjust the column sizes as the table width will have changed to account 5057 // for the scrollbar 5058 var scrollBarVis = divBodyEl.scrollHeight > divBodyEl.clientHeight; 5059 5060 if ( settings.scrollBarVis !== scrollBarVis && settings.scrollBarVis !== undefined ) { 5061 settings.scrollBarVis = scrollBarVis; 5062 _fnAdjustColumnSizing( settings ); 5063 return; // adjust column sizing will call this function again 5064 } 5065 else { 5066 settings.scrollBarVis = scrollBarVis; 5067 } 5068 5069 // 1. Re-create the table inside the scrolling div 5070 // Remove the old minimised thead and tfoot elements in the inner table 5071 table.children('thead, tfoot').remove(); 5072 5073 // Clone the current header and footer elements and then place it into the inner table 5074 headerCopy = header.clone().prependTo( table ); 5075 headerCopy.find('th, td').removeAttr('tabindex'); 5076 headerCopy.find('[id]').removeAttr('id'); 5077 5078 if ( footer ) { 5079 footerCopy = footer.clone().prependTo( table ); 5080 footerCopy.find('[id]').removeAttr('id'); 5081 } 5082 5083 // 2. Correct colgroup > col values if needed 5084 // It is possible that the cell sizes are smaller than the content, so we need to 5085 // correct colgroup>col for such cases. This can happen if the auto width detection 5086 // uses a cell which has a longer string, but isn't the widest! For example 5087 // "Chief Executive Officer (CEO)" is the longest string in the demo, but 5088 // "Systems Administrator" is actually the widest string since it doesn't collapse. 5089 // Note the use of translating into a column index to get the `col` element. This 5090 // is because of Responsive which might remove `col` elements, knocking the alignment 5091 // of the indexes out. 5092 if (settings.aiDisplay.length) { 5093 // Get the column sizes from the first row in the table 5094 var colSizes = table.children('tbody').eq(0).children('tr').eq(0).children('th, td').map(function (vis) { 5095 return { 5096 idx: _fnVisibleToColumnIndex(settings, vis), 5097 width: $(this).outerWidth() 5098 } 5099 }); 5100 5101 // Check against what the colgroup > col is set to and correct if needed 5102 for (var i=0 ; i<colSizes.length ; i++) { 5103 var colEl = settings.aoColumns[ colSizes[i].idx ].colEl[0]; 5104 var colWidth = colEl.style.width.replace('px', ''); 5105 5106 if (colWidth !== colSizes[i].width) { 5107 colEl.style.width = colSizes[i].width + 'px'; 5108 } 5109 } 5110 } 5111 5112 // 3. Copy the colgroup over to the header and footer 5113 divHeaderTable 5114 .find('colgroup') 5115 .remove(); 5116 5117 divHeaderTable.append(settings.colgroup.clone()); 5118 5119 if ( footer ) { 5120 divFooterTable 5121 .find('colgroup') 5122 .remove(); 5123 5124 divFooterTable.append(settings.colgroup.clone()); 5125 } 5126 5127 // "Hide" the header and footer that we used for the sizing. We need to keep 5128 // the content of the cell so that the width applied to the header and body 5129 // both match, but we want to hide it completely. 5130 $('th, td', headerCopy).each(function () { 5131 $(this.childNodes).wrapAll('<div class="dt-scroll-sizing">'); 5132 }); 5133 5134 if ( footer ) { 5135 $('th, td', footerCopy).each(function () { 5136 $(this.childNodes).wrapAll('<div class="dt-scroll-sizing">'); 5137 }); 5138 } 5139 5140 // 4. Clean up 5141 // Figure out if there are scrollbar present - if so then we need a the header and footer to 5142 // provide a bit more space to allow "overflow" scrolling (i.e. past the scrollbar) 5143 var isScrolling = Math.floor(table.height()) > divBodyEl.clientHeight || divBody.css('overflow-y') == "scroll"; 5144 var paddingSide = 'padding' + (browser.bScrollbarLeft ? 'Left' : 'Right' ); 5145 5146 // Set the width's of the header and footer tables 5147 var outerWidth = table.outerWidth(); 5148 5149 divHeaderTable.css('width', _fnStringToCss( outerWidth )); 5150 divHeaderInner 5151 .css('width', _fnStringToCss( outerWidth )) 5152 .css(paddingSide, isScrolling ? barWidth+"px" : "0px"); 5153 5154 if ( footer ) { 5155 divFooterTable.css('width', _fnStringToCss( outerWidth )); 5156 divFooterInner 5157 .css('width', _fnStringToCss( outerWidth )) 5158 .css(paddingSide, isScrolling ? barWidth+"px" : "0px"); 5159 } 5160 5161 // Correct DOM ordering for colgroup - comes before the thead 5162 table.children('colgroup').prependTo(table); 5163 5164 // Adjust the position of the header in case we loose the y-scrollbar 5165 divBody.trigger('scroll'); 5166 5167 // If sorting or filtering has occurred, jump the scrolling back to the top 5168 // only if we aren't holding the position 5169 if ( (settings.bSorted || settings.bFiltered) && ! settings._drawHold ) { 5170 divBodyEl.scrollTop = 0; 5171 } 5172 } 5173 5174 /** 5175 * Calculate the width of columns for the table 5176 * @param {object} settings dataTables settings object 5177 * @memberof DataTable#oApi 5178 */ 5179 function _fnCalculateColumnWidths ( settings ) 5180 { 5181 // Not interested in doing column width calculation if auto-width is disabled 5182 if (! settings.oFeatures.bAutoWidth) { 5183 return; 5184 } 5185 5186 var 5187 table = settings.nTable, 5188 columns = settings.aoColumns, 5189 scroll = settings.oScroll, 5190 scrollY = scroll.sY, 5191 scrollX = scroll.sX, 5192 scrollXInner = scroll.sXInner, 5193 visibleColumns = _fnGetColumns( settings, 'bVisible' ), 5194 tableWidthAttr = table.getAttribute('width'), // from DOM element 5195 tableContainer = table.parentNode, 5196 i, column, columnIdx; 5197 5198 var styleWidth = table.style.width; 5199 if ( styleWidth && styleWidth.indexOf('%') !== -1 ) { 5200 tableWidthAttr = styleWidth; 5201 } 5202 5203 // Let plug-ins know that we are doing a recalc, in case they have changed any of the 5204 // visible columns their own way (e.g. Responsive uses display:none). 5205 _fnCallbackFire( 5206 settings, 5207 null, 5208 'column-calc', 5209 {visible: visibleColumns}, 5210 false 5211 ); 5212 5213 // Construct a single row, worst case, table with the widest 5214 // node in the data, assign any user defined widths, then insert it into 5215 // the DOM and allow the browser to do all the hard work of calculating 5216 // table widths 5217 var tmpTable = $(table.cloneNode()) 5218 .css( 'visibility', 'hidden' ) 5219 .removeAttr( 'id' ); 5220 5221 // Clean up the table body 5222 tmpTable.append('<tbody>') 5223 var tr = $('<tr/>').appendTo( tmpTable.find('tbody') ); 5224 5225 // Clone the table header and footer - we can't use the header / footer 5226 // from the cloned table, since if scrolling is active, the table's 5227 // real header and footer are contained in different table tags 5228 tmpTable 5229 .append( $(settings.nTHead).clone() ) 5230 .append( $(settings.nTFoot).clone() ); 5231 5232 // Remove any assigned widths from the footer (from scrolling) 5233 tmpTable.find('tfoot th, tfoot td').css('width', ''); 5234 5235 // Apply custom sizing to the cloned header 5236 tmpTable.find('thead th, thead td').each( function () { 5237 // Get the `width` from the header layout 5238 var width = _fnColumnsSumWidth( settings, this, true, false ); 5239 5240 if ( width ) { 5241 this.style.width = width; 5242 5243 // For scrollX we need to force the column width otherwise the 5244 // browser will collapse it. If this width is smaller than the 5245 // width the column requires, then it will have no effect 5246 if ( scrollX ) { 5247 $( this ).append( $('<div/>').css( { 5248 width: width, 5249 margin: 0, 5250 padding: 0, 5251 border: 0, 5252 height: 1 5253 } ) ); 5254 } 5255 } 5256 else { 5257 this.style.width = ''; 5258 } 5259 } ); 5260 5261 // Find the widest piece of data for each column and put it into the table 5262 for ( i=0 ; i<visibleColumns.length ; i++ ) { 5263 columnIdx = visibleColumns[i]; 5264 column = columns[ columnIdx ]; 5265 5266 var longest = _fnGetMaxLenString(settings, columnIdx); 5267 var autoClass = _ext.type.className[column.sType]; 5268 var text = longest + column.sContentPadding; 5269 var insert = longest.indexOf('<') === -1 5270 ? document.createTextNode(text) 5271 : text 5272 5273 $('<td/>') 5274 .addClass(autoClass) 5275 .addClass(column.sClass) 5276 .append(insert) 5277 .appendTo(tr); 5278 } 5279 5280 // Tidy the temporary table - remove name attributes so there aren't 5281 // duplicated in the dom (radio elements for example) 5282 $('[name]', tmpTable).removeAttr('name'); 5283 5284 // Table has been built, attach to the document so we can work with it. 5285 // A holding element is used, positioned at the top of the container 5286 // with minimal height, so it has no effect on if the container scrolls 5287 // or not. Otherwise it might trigger scrolling when it actually isn't 5288 // needed 5289 var holder = $('<div/>').css( scrollX || scrollY ? 5290 { 5291 position: 'absolute', 5292 top: 0, 5293 left: 0, 5294 height: 1, 5295 right: 0, 5296 overflow: 'hidden' 5297 } : 5298 {} 5299 ) 5300 .append( tmpTable ) 5301 .appendTo( tableContainer ); 5302 5303 // When scrolling (X or Y) we want to set the width of the table as 5304 // appropriate. However, when not scrolling leave the table width as it 5305 // is. This results in slightly different, but I think correct behaviour 5306 if ( scrollX && scrollXInner ) { 5307 tmpTable.width( scrollXInner ); 5308 } 5309 else if ( scrollX ) { 5310 tmpTable.css( 'width', 'auto' ); 5311 tmpTable.removeAttr('width'); 5312 5313 // If there is no width attribute or style, then allow the table to 5314 // collapse 5315 if ( tmpTable.width() < tableContainer.clientWidth && tableWidthAttr ) { 5316 tmpTable.width( tableContainer.clientWidth ); 5317 } 5318 } 5319 else if ( scrollY ) { 5320 tmpTable.width( tableContainer.clientWidth ); 5321 } 5322 else if ( tableWidthAttr ) { 5323 tmpTable.width( tableWidthAttr ); 5324 } 5325 5326 // Get the width of each column in the constructed table 5327 var total = 0; 5328 var bodyCells = tmpTable.find('tbody tr').eq(0).children(); 5329 5330 for ( i=0 ; i<visibleColumns.length ; i++ ) { 5331 // Use getBounding for sub-pixel accuracy, which we then want to round up! 5332 var bounding = bodyCells[i].getBoundingClientRect().width; 5333 5334 // Total is tracked to remove any sub-pixel errors as the outerWidth 5335 // of the table might not equal the total given here 5336 total += bounding; 5337 5338 // Width for each column to use 5339 columns[ visibleColumns[i] ].sWidth = _fnStringToCss( bounding ); 5340 } 5341 5342 table.style.width = _fnStringToCss( total ); 5343 5344 // Finished with the table - ditch it 5345 holder.remove(); 5346 5347 // If there is a width attr, we want to attach an event listener which 5348 // allows the table sizing to automatically adjust when the window is 5349 // resized. Use the width attr rather than CSS, since we can't know if the 5350 // CSS is a relative value or absolute - DOM read is always px. 5351 if ( tableWidthAttr ) { 5352 table.style.width = _fnStringToCss( tableWidthAttr ); 5353 } 5354 5355 if ( (tableWidthAttr || scrollX) && ! settings._reszEvt ) { 5356 var bindResize = function () { 5357 $(window).on('resize.DT-'+settings.sInstance, DataTable.util.throttle( function () { 5358 if (! settings.bDestroying) { 5359 _fnAdjustColumnSizing( settings ); 5360 } 5361 } ) ); 5362 }; 5363 5364 bindResize(); 5365 5366 settings._reszEvt = true; 5367 } 5368 } 5369 5370 5371 /** 5372 * Get the maximum strlen for each data column 5373 * @param {object} settings dataTables settings object 5374 * @param {int} colIdx column of interest 5375 * @returns {string} string of the max length 5376 * @memberof DataTable#oApi 5377 */ 5378 function _fnGetMaxLenString( settings, colIdx ) 5379 { 5380 var column = settings.aoColumns[colIdx]; 5381 5382 if (! column.maxLenString) { 5383 var s, max='', maxLen = -1; 5384 5385 for ( var i=0, ien=settings.aiDisplayMaster.length ; i<ien ; i++ ) { 5386 var rowIdx = settings.aiDisplayMaster[i]; 5387 var data = _fnGetRowDisplay(settings, rowIdx)[colIdx]; 5388 5389 var cellString = data && typeof data === 'object' && data.nodeType 5390 ? data.innerHTML 5391 : data+''; 5392 5393 // Remove id / name attributes from elements so they 5394 // don't interfere with existing elements 5395 cellString = cellString 5396 .replace(/id=".*?"/g, '') 5397 .replace(/name=".*?"/g, ''); 5398 5399 s = _stripHtml(cellString) 5400 .replace( / /g, ' ' ); 5401 5402 if ( s.length > maxLen ) { 5403 // We want the HTML in the string, but the length that 5404 // is important is the stripped string 5405 max = cellString; 5406 maxLen = s.length; 5407 } 5408 } 5409 5410 column.maxLenString = max; 5411 } 5412 5413 return column.maxLenString; 5414 } 5415 5416 5417 /** 5418 * Append a CSS unit (only if required) to a string 5419 * @param {string} value to css-ify 5420 * @returns {string} value with css unit 5421 * @memberof DataTable#oApi 5422 */ 5423 function _fnStringToCss( s ) 5424 { 5425 if ( s === null ) { 5426 return '0px'; 5427 } 5428 5429 if ( typeof s == 'number' ) { 5430 return s < 0 ? 5431 '0px' : 5432 s+'px'; 5433 } 5434 5435 // Check it has a unit character already 5436 return s.match(/\d$/) ? 5437 s+'px' : 5438 s; 5439 } 5440 5441 /** 5442 * Re-insert the `col` elements for current visibility 5443 * 5444 * @param {*} settings DT settings 5445 */ 5446 function _colGroup( settings ) { 5447 var cols = settings.aoColumns; 5448 5449 settings.colgroup.empty(); 5450 5451 for (i=0 ; i<cols.length ; i++) { 5452 if (cols[i].bVisible) { 5453 settings.colgroup.append(cols[i].colEl); 5454 } 5455 } 5456 } 5457 5458 5459 function _fnSortInit( settings ) { 5460 var target = settings.nTHead; 5461 var headerRows = target.querySelectorAll('tr'); 5462 var legacyTop = settings.bSortCellsTop; 5463 var notSelector = ':not([data-dt-order="disable"]):not([data-dt-order="icon-only"])'; 5464 5465 // Legacy support for `orderCellsTop` 5466 if (legacyTop === true) { 5467 target = headerRows[0]; 5468 } 5469 else if (legacyTop === false) { 5470 target = headerRows[ headerRows.length - 1 ]; 5471 } 5472 5473 _fnSortAttachListener( 5474 settings, 5475 target, 5476 target === settings.nTHead 5477 ? 'tr'+notSelector+' th'+notSelector+', tr'+notSelector+' td'+notSelector 5478 : 'th'+notSelector+', td'+notSelector 5479 ); 5480 5481 // Need to resolve the user input array into our internal structure 5482 var order = []; 5483 _fnSortResolve( settings, order, settings.aaSorting ); 5484 5485 settings.aaSorting = order; 5486 } 5487 5488 5489 function _fnSortAttachListener(settings, node, selector, column, callback) { 5490 _fnBindAction( node, selector, function (e) { 5491 var run = false; 5492 var columns = column === undefined 5493 ? _fnColumnsFromHeader( e.target ) 5494 : [column]; 5495 5496 if ( columns.length ) { 5497 for ( var i=0, ien=columns.length ; i<ien ; i++ ) { 5498 var ret = _fnSortAdd( settings, columns[i], i, e.shiftKey ); 5499 5500 if (ret !== false) { 5501 run = true; 5502 } 5503 5504 // If the first entry is no sort, then subsequent 5505 // sort columns are ignored 5506 if (settings.aaSorting.length === 1 && settings.aaSorting[0][1] === '') { 5507 break; 5508 } 5509 } 5510 5511 if (run) { 5512 _fnProcessingDisplay( settings, true ); 5513 5514 // Allow the processing display to show 5515 setTimeout( function () { 5516 _fnSort( settings ); 5517 _fnSortDisplay( settings, settings.aiDisplay ); 5518 5519 // Sort processing done - redraw has its own processing display 5520 _fnProcessingDisplay( settings, false ); 5521 5522 _fnReDraw( settings, false, false ); 5523 5524 if (callback) { 5525 callback(); 5526 } 5527 }, 0); 5528 } 5529 } 5530 } ); 5531 } 5532 5533 /** 5534 * Sort the display array to match the master's order 5535 * @param {*} settings 5536 */ 5537 function _fnSortDisplay(settings, display) { 5538 if (display.length < 2) { 5539 return; 5540 } 5541 5542 var master = settings.aiDisplayMaster; 5543 var masterMap = {}; 5544 var map = {}; 5545 var i; 5546 5547 // Rather than needing an `indexOf` on master array, we can create a map 5548 for (i=0 ; i<master.length ; i++) { 5549 masterMap[master[i]] = i; 5550 } 5551 5552 // And then cache what would be the indexOf fom the display 5553 for (i=0 ; i<display.length ; i++) { 5554 map[display[i]] = masterMap[display[i]]; 5555 } 5556 5557 display.sort(function(a, b){ 5558 // Short version of this function is simply `master.indexOf(a) - master.indexOf(b);` 5559 return map[a] - map[b]; 5560 }); 5561 } 5562 5563 5564 function _fnSortResolve (settings, nestedSort, sort) { 5565 var push = function ( a ) { 5566 if ($.isPlainObject(a)) { 5567 if (a.idx !== undefined) { 5568 // Index based ordering 5569 nestedSort.push([a.idx, a.dir]); 5570 } 5571 else if (a.name) { 5572 // Name based ordering 5573 var cols = _pluck( settings.aoColumns, 'sName'); 5574 var idx = cols.indexOf(a.name); 5575 5576 if (idx !== -1) { 5577 nestedSort.push([idx, a.dir]); 5578 } 5579 } 5580 } 5581 else { 5582 // Plain column index and direction pair 5583 nestedSort.push(a); 5584 } 5585 }; 5586 5587 if ( $.isPlainObject(sort) ) { 5588 // Object 5589 push(sort); 5590 } 5591 else if ( sort.length && typeof sort[0] === 'number' ) { 5592 // 1D array 5593 push(sort); 5594 } 5595 else if ( sort.length ) { 5596 // 2D array 5597 for (var z=0; z<sort.length; z++) { 5598 push(sort[z]); // Object or array 5599 } 5600 } 5601 } 5602 5603 5604 function _fnSortFlatten ( settings ) 5605 { 5606 var 5607 i, k, kLen, 5608 aSort = [], 5609 extSort = DataTable.ext.type.order, 5610 aoColumns = settings.aoColumns, 5611 aDataSort, iCol, sType, srcCol, 5612 fixed = settings.aaSortingFixed, 5613 fixedObj = $.isPlainObject( fixed ), 5614 nestedSort = []; 5615 5616 if ( ! settings.oFeatures.bSort ) { 5617 return aSort; 5618 } 5619 5620 // Build the sort array, with pre-fix and post-fix options if they have been 5621 // specified 5622 if ( Array.isArray( fixed ) ) { 5623 _fnSortResolve( settings, nestedSort, fixed ); 5624 } 5625 5626 if ( fixedObj && fixed.pre ) { 5627 _fnSortResolve( settings, nestedSort, fixed.pre ); 5628 } 5629 5630 _fnSortResolve( settings, nestedSort, settings.aaSorting ); 5631 5632 if (fixedObj && fixed.post ) { 5633 _fnSortResolve( settings, nestedSort, fixed.post ); 5634 } 5635 5636 for ( i=0 ; i<nestedSort.length ; i++ ) 5637 { 5638 srcCol = nestedSort[i][0]; 5639 5640 if ( aoColumns[ srcCol ] ) { 5641 aDataSort = aoColumns[ srcCol ].aDataSort; 5642 5643 for ( k=0, kLen=aDataSort.length ; k<kLen ; k++ ) 5644 { 5645 iCol = aDataSort[k]; 5646 sType = aoColumns[ iCol ].sType || 'string'; 5647 5648 if ( nestedSort[i]._idx === undefined ) { 5649 nestedSort[i]._idx = aoColumns[iCol].asSorting.indexOf(nestedSort[i][1]); 5650 } 5651 5652 if ( nestedSort[i][1] ) { 5653 aSort.push( { 5654 src: srcCol, 5655 col: iCol, 5656 dir: nestedSort[i][1], 5657 index: nestedSort[i]._idx, 5658 type: sType, 5659 formatter: extSort[ sType+"-pre" ], 5660 sorter: extSort[ sType+"-"+nestedSort[i][1] ] 5661 } ); 5662 } 5663 } 5664 } 5665 } 5666 5667 return aSort; 5668 } 5669 5670 /** 5671 * Change the order of the table 5672 * @param {object} oSettings dataTables settings object 5673 * @memberof DataTable#oApi 5674 */ 5675 function _fnSort ( oSettings, col, dir ) 5676 { 5677 var 5678 i, ien, iLen, 5679 aiOrig = [], 5680 extSort = DataTable.ext.type.order, 5681 aoData = oSettings.aoData, 5682 sortCol, 5683 displayMaster = oSettings.aiDisplayMaster, 5684 aSort; 5685 5686 // Resolve any column types that are unknown due to addition or invalidation 5687 // @todo Can this be moved into a 'data-ready' handler which is called when 5688 // data is going to be used in the table? 5689 _fnColumnTypes( oSettings ); 5690 5691 // Allow a specific column to be sorted, which will _not_ alter the display 5692 // master 5693 if (col !== undefined) { 5694 var srcCol = oSettings.aoColumns[col]; 5695 aSort = [{ 5696 src: col, 5697 col: col, 5698 dir: dir, 5699 index: 0, 5700 type: srcCol.sType, 5701 formatter: extSort[ srcCol.sType+"-pre" ], 5702 sorter: extSort[ srcCol.sType+"-"+dir ] 5703 }]; 5704 displayMaster = displayMaster.slice(); 5705 } 5706 else { 5707 aSort = _fnSortFlatten( oSettings ); 5708 } 5709 5710 for ( i=0, ien=aSort.length ; i<ien ; i++ ) { 5711 sortCol = aSort[i]; 5712 5713 // Load the data needed for the sort, for each cell 5714 _fnSortData( oSettings, sortCol.col ); 5715 } 5716 5717 /* No sorting required if server-side or no sorting array */ 5718 if ( _fnDataSource( oSettings ) != 'ssp' && aSort.length !== 0 ) 5719 { 5720 // Reset the initial positions on each pass so we get a stable sort 5721 for ( i=0, iLen=displayMaster.length ; i<iLen ; i++ ) { 5722 aiOrig[ i ] = i; 5723 } 5724 5725 // If the first sort is desc, then reverse the array to preserve original 5726 // order, just in reverse 5727 if (aSort.length && aSort[0].dir === 'desc') { 5728 aiOrig.reverse(); 5729 } 5730 5731 /* Do the sort - here we want multi-column sorting based on a given data source (column) 5732 * and sorting function (from oSort) in a certain direction. It's reasonably complex to 5733 * follow on it's own, but this is what we want (example two column sorting): 5734 * fnLocalSorting = function(a,b){ 5735 * var test; 5736 * test = oSort['string-asc']('data11', 'data12'); 5737 * if (test !== 0) 5738 * return test; 5739 * test = oSort['numeric-desc']('data21', 'data22'); 5740 * if (test !== 0) 5741 * return test; 5742 * return oSort['numeric-asc']( aiOrig[a], aiOrig[b] ); 5743 * } 5744 * Basically we have a test for each sorting column, if the data in that column is equal, 5745 * test the next column. If all columns match, then we use a numeric sort on the row 5746 * positions in the original data array to provide a stable sort. 5747 */ 5748 displayMaster.sort( function ( a, b ) { 5749 var 5750 x, y, k, test, sort, 5751 len=aSort.length, 5752 dataA = aoData[a]._aSortData, 5753 dataB = aoData[b]._aSortData; 5754 5755 for ( k=0 ; k<len ; k++ ) { 5756 sort = aSort[k]; 5757 5758 // Data, which may have already been through a `-pre` function 5759 x = dataA[ sort.col ]; 5760 y = dataB[ sort.col ]; 5761 5762 if (sort.sorter) { 5763 // If there is a custom sorter (`-asc` or `-desc`) for this 5764 // data type, use it 5765 test = sort.sorter(x, y); 5766 5767 if ( test !== 0 ) { 5768 return test; 5769 } 5770 } 5771 else { 5772 // Otherwise, use generic sorting 5773 test = x<y ? -1 : x>y ? 1 : 0; 5774 5775 if ( test !== 0 ) { 5776 return sort.dir === 'asc' ? test : -test; 5777 } 5778 } 5779 } 5780 5781 x = aiOrig[a]; 5782 y = aiOrig[b]; 5783 5784 return x<y ? -1 : x>y ? 1 : 0; 5785 } ); 5786 } 5787 else if ( aSort.length === 0 ) { 5788 // Apply index order 5789 displayMaster.sort(function (x, y) { 5790 return x<y ? -1 : x>y ? 1 : 0; 5791 }); 5792 } 5793 5794 if (col === undefined) { 5795 // Tell the draw function that we have sorted the data 5796 oSettings.bSorted = true; 5797 5798 _fnCallbackFire( oSettings, null, 'order', [oSettings, aSort] ); 5799 } 5800 5801 return displayMaster; 5802 } 5803 5804 5805 /** 5806 * Function to run on user sort request 5807 * @param {object} settings dataTables settings object 5808 * @param {node} attachTo node to attach the handler to 5809 * @param {int} colIdx column sorting index 5810 * @param {int} addIndex Counter 5811 * @param {boolean} [shift=false] Shift click add 5812 * @param {function} [callback] callback function 5813 * @memberof DataTable#oApi 5814 */ 5815 function _fnSortAdd ( settings, colIdx, addIndex, shift ) 5816 { 5817 var col = settings.aoColumns[ colIdx ]; 5818 var sorting = settings.aaSorting; 5819 var asSorting = col.asSorting; 5820 var nextSortIdx; 5821 var next = function ( a, overflow ) { 5822 var idx = a._idx; 5823 if ( idx === undefined ) { 5824 idx = asSorting.indexOf(a[1]); 5825 } 5826 5827 return idx+1 < asSorting.length ? 5828 idx+1 : 5829 overflow ? 5830 null : 5831 0; 5832 }; 5833 5834 if ( ! col.bSortable ) { 5835 return false; 5836 } 5837 5838 // Convert to 2D array if needed 5839 if ( typeof sorting[0] === 'number' ) { 5840 sorting = settings.aaSorting = [ sorting ]; 5841 } 5842 5843 // If appending the sort then we are multi-column sorting 5844 if ( (shift || addIndex) && settings.oFeatures.bSortMulti ) { 5845 // Are we already doing some kind of sort on this column? 5846 var sortIdx = _pluck(sorting, '0').indexOf(colIdx); 5847 5848 if ( sortIdx !== -1 ) { 5849 // Yes, modify the sort 5850 nextSortIdx = next( sorting[sortIdx], true ); 5851 5852 if ( nextSortIdx === null && sorting.length === 1 ) { 5853 nextSortIdx = 0; // can't remove sorting completely 5854 } 5855 5856 if ( nextSortIdx === null ) { 5857 sorting.splice( sortIdx, 1 ); 5858 } 5859 else { 5860 sorting[sortIdx][1] = asSorting[ nextSortIdx ]; 5861 sorting[sortIdx]._idx = nextSortIdx; 5862 } 5863 } 5864 else if (shift) { 5865 // No sort on this column yet, being added by shift click 5866 // add it as itself 5867 sorting.push( [ colIdx, asSorting[0], 0 ] ); 5868 sorting[sorting.length-1]._idx = 0; 5869 } 5870 else { 5871 // No sort on this column yet, being added from a colspan 5872 // so add with same direction as first column 5873 sorting.push( [ colIdx, sorting[0][1], 0 ] ); 5874 sorting[sorting.length-1]._idx = 0; 5875 } 5876 } 5877 else if ( sorting.length && sorting[0][0] == colIdx ) { 5878 // Single column - already sorting on this column, modify the sort 5879 nextSortIdx = next( sorting[0] ); 5880 5881 sorting.length = 1; 5882 sorting[0][1] = asSorting[ nextSortIdx ]; 5883 sorting[0]._idx = nextSortIdx; 5884 } 5885 else { 5886 // Single column - sort only on this column 5887 sorting.length = 0; 5888 sorting.push( [ colIdx, asSorting[0] ] ); 5889 sorting[0]._idx = 0; 5890 } 5891 } 5892 5893 5894 /** 5895 * Set the sorting classes on table's body, Note: it is safe to call this function 5896 * when bSort and bSortClasses are false 5897 * @param {object} oSettings dataTables settings object 5898 * @memberof DataTable#oApi 5899 */ 5900 function _fnSortingClasses( settings ) 5901 { 5902 var oldSort = settings.aLastSort; 5903 var sortClass = settings.oClasses.order.position; 5904 var sort = _fnSortFlatten( settings ); 5905 var features = settings.oFeatures; 5906 var i, ien, colIdx; 5907 5908 if ( features.bSort && features.bSortClasses ) { 5909 // Remove old sorting classes 5910 for ( i=0, ien=oldSort.length ; i<ien ; i++ ) { 5911 colIdx = oldSort[i].src; 5912 5913 // Remove column sorting 5914 $( _pluck( settings.aoData, 'anCells', colIdx ) ) 5915 .removeClass( sortClass + (i<2 ? i+1 : 3) ); 5916 } 5917 5918 // Add new column sorting 5919 for ( i=0, ien=sort.length ; i<ien ; i++ ) { 5920 colIdx = sort[i].src; 5921 5922 $( _pluck( settings.aoData, 'anCells', colIdx ) ) 5923 .addClass( sortClass + (i<2 ? i+1 : 3) ); 5924 } 5925 } 5926 5927 settings.aLastSort = sort; 5928 } 5929 5930 5931 // Get the data to sort a column, be it from cache, fresh (populating the 5932 // cache), or from a sort formatter 5933 function _fnSortData( settings, colIdx ) 5934 { 5935 // Custom sorting function - provided by the sort data type 5936 var column = settings.aoColumns[ colIdx ]; 5937 var customSort = DataTable.ext.order[ column.sSortDataType ]; 5938 var customData; 5939 5940 if ( customSort ) { 5941 customData = customSort.call( settings.oInstance, settings, colIdx, 5942 _fnColumnIndexToVisible( settings, colIdx ) 5943 ); 5944 } 5945 5946 // Use / populate cache 5947 var row, cellData; 5948 var formatter = DataTable.ext.type.order[ column.sType+"-pre" ]; 5949 var data = settings.aoData; 5950 5951 for ( var rowIdx=0 ; rowIdx<data.length ; rowIdx++ ) { 5952 // Sparse array 5953 if (! data[rowIdx]) { 5954 continue; 5955 } 5956 5957 row = data[rowIdx]; 5958 5959 if ( ! row._aSortData ) { 5960 row._aSortData = []; 5961 } 5962 5963 if ( ! row._aSortData[colIdx] || customSort ) { 5964 cellData = customSort ? 5965 customData[rowIdx] : // If there was a custom sort function, use data from there 5966 _fnGetCellData( settings, rowIdx, colIdx, 'sort' ); 5967 5968 row._aSortData[ colIdx ] = formatter ? 5969 formatter( cellData, settings ) : 5970 cellData; 5971 } 5972 } 5973 } 5974 5975 5976 /** 5977 * State information for a table 5978 * 5979 * @param {*} settings 5980 * @returns State object 5981 */ 5982 function _fnSaveState ( settings ) 5983 { 5984 if (settings._bLoadingState) { 5985 return; 5986 } 5987 5988 /* Store the interesting variables */ 5989 var state = { 5990 time: +new Date(), 5991 start: settings._iDisplayStart, 5992 length: settings._iDisplayLength, 5993 order: $.extend( true, [], settings.aaSorting ), 5994 search: $.extend({}, settings.oPreviousSearch), 5995 columns: settings.aoColumns.map( function ( col, i ) { 5996 return { 5997 visible: col.bVisible, 5998 search: $.extend({}, settings.aoPreSearchCols[i]) 5999 }; 6000 } ) 6001 }; 6002 6003 settings.oSavedState = state; 6004 _fnCallbackFire( settings, "aoStateSaveParams", 'stateSaveParams', [settings, state] ); 6005 6006 if ( settings.oFeatures.bStateSave && !settings.bDestroying ) 6007 { 6008 settings.fnStateSaveCallback.call( settings.oInstance, settings, state ); 6009 } 6010 } 6011 6012 6013 /** 6014 * Attempt to load a saved table state 6015 * @param {object} oSettings dataTables settings object 6016 * @param {object} oInit DataTables init object so we can override settings 6017 * @param {function} callback Callback to execute when the state has been loaded 6018 * @memberof DataTable#oApi 6019 */ 6020 function _fnLoadState ( settings, init, callback ) 6021 { 6022 if ( ! settings.oFeatures.bStateSave ) { 6023 callback(); 6024 return; 6025 } 6026 6027 var loaded = function(state) { 6028 _fnImplementState(settings, state, callback); 6029 } 6030 6031 var state = settings.fnStateLoadCallback.call( settings.oInstance, settings, loaded ); 6032 6033 if ( state !== undefined ) { 6034 _fnImplementState( settings, state, callback ); 6035 } 6036 // otherwise, wait for the loaded callback to be executed 6037 6038 return true; 6039 } 6040 6041 function _fnImplementState ( settings, s, callback) { 6042 var i, ien; 6043 var columns = settings.aoColumns; 6044 settings._bLoadingState = true; 6045 6046 // When StateRestore was introduced the state could now be implemented at any time 6047 // Not just initialisation. To do this an api instance is required in some places 6048 var api = settings._bInitComplete ? new DataTable.Api(settings) : null; 6049 6050 if ( ! s || ! s.time ) { 6051 settings._bLoadingState = false; 6052 callback(); 6053 return; 6054 } 6055 6056 // Reject old data 6057 var duration = settings.iStateDuration; 6058 if ( duration > 0 && s.time < +new Date() - (duration*1000) ) { 6059 settings._bLoadingState = false; 6060 callback(); 6061 return; 6062 } 6063 6064 // Allow custom and plug-in manipulation functions to alter the saved data set and 6065 // cancelling of loading by returning false 6066 var abStateLoad = _fnCallbackFire( settings, 'aoStateLoadParams', 'stateLoadParams', [settings, s] ); 6067 if ( abStateLoad.indexOf(false) !== -1 ) { 6068 settings._bLoadingState = false; 6069 callback(); 6070 return; 6071 } 6072 6073 // Number of columns have changed - all bets are off, no restore of settings 6074 if ( s.columns && columns.length !== s.columns.length ) { 6075 settings._bLoadingState = false; 6076 callback(); 6077 return; 6078 } 6079 6080 // Store the saved state so it might be accessed at any time 6081 settings.oLoadedState = $.extend( true, {}, s ); 6082 6083 // This is needed for ColReorder, which has to happen first to allow all 6084 // the stored indexes to be usable. It is not publicly documented. 6085 _fnCallbackFire( settings, null, 'stateLoadInit', [settings, s], true ); 6086 6087 // Page Length 6088 if ( s.length !== undefined ) { 6089 // If already initialised just set the value directly so that the select element is also updated 6090 if (api) { 6091 api.page.len(s.length) 6092 } 6093 else { 6094 settings._iDisplayLength = s.length; 6095 } 6096 } 6097 6098 // Restore key features - todo - for 1.11 this needs to be done by 6099 // subscribed events 6100 if ( s.start !== undefined ) { 6101 if(api === null) { 6102 settings._iDisplayStart = s.start; 6103 settings.iInitDisplayStart = s.start; 6104 } 6105 else { 6106 _fnPageChange(settings, s.start/settings._iDisplayLength); 6107 } 6108 } 6109 6110 // Order 6111 if ( s.order !== undefined ) { 6112 settings.aaSorting = []; 6113 $.each( s.order, function ( i, col ) { 6114 settings.aaSorting.push( col[0] >= columns.length ? 6115 [ 0, col[1] ] : 6116 col 6117 ); 6118 } ); 6119 } 6120 6121 // Search 6122 if ( s.search !== undefined ) { 6123 $.extend( settings.oPreviousSearch, s.search ); 6124 } 6125 6126 // Columns 6127 if ( s.columns ) { 6128 for ( i=0, ien=s.columns.length ; i<ien ; i++ ) { 6129 var col = s.columns[i]; 6130 6131 // Visibility 6132 if ( col.visible !== undefined ) { 6133 // If the api is defined, the table has been initialised so we need to use it rather than internal settings 6134 if (api) { 6135 // Don't redraw the columns on every iteration of this loop, we will do this at the end instead 6136 api.column(i).visible(col.visible, false); 6137 } 6138 else { 6139 columns[i].bVisible = col.visible; 6140 } 6141 } 6142 6143 // Search 6144 if ( col.search !== undefined ) { 6145 $.extend( settings.aoPreSearchCols[i], col.search ); 6146 } 6147 } 6148 6149 // If the api is defined then we need to adjust the columns once the visibility has been changed 6150 if (api) { 6151 api.columns.adjust(); 6152 } 6153 } 6154 6155 settings._bLoadingState = false; 6156 _fnCallbackFire( settings, 'aoStateLoaded', 'stateLoaded', [settings, s] ); 6157 callback(); 6158 } 6159 6160 /** 6161 * Log an error message 6162 * @param {object} settings dataTables settings object 6163 * @param {int} level log error messages, or display them to the user 6164 * @param {string} msg error message 6165 * @param {int} tn Technical note id to get more information about the error. 6166 * @memberof DataTable#oApi 6167 */ 6168 function _fnLog( settings, level, msg, tn ) 6169 { 6170 msg = 'DataTables warning: '+ 6171 (settings ? 'table id='+settings.sTableId+' - ' : '')+msg; 6172 6173 if ( tn ) { 6174 msg += '. For more information about this error, please see '+ 6175 'https://datatables.net/tn/'+tn; 6176 } 6177 6178 if ( ! level ) { 6179 // Backwards compatibility pre 1.10 6180 var ext = DataTable.ext; 6181 var type = ext.sErrMode || ext.errMode; 6182 6183 if ( settings ) { 6184 _fnCallbackFire( settings, null, 'dt-error', [ settings, tn, msg ], true ); 6185 } 6186 6187 if ( type == 'alert' ) { 6188 alert( msg ); 6189 } 6190 else if ( type == 'throw' ) { 6191 throw new Error(msg); 6192 } 6193 else if ( typeof type == 'function' ) { 6194 type( settings, tn, msg ); 6195 } 6196 } 6197 else if ( window.console && console.log ) { 6198 console.log( msg ); 6199 } 6200 } 6201 6202 6203 /** 6204 * See if a property is defined on one object, if so assign it to the other object 6205 * @param {object} ret target object 6206 * @param {object} src source object 6207 * @param {string} name property 6208 * @param {string} [mappedName] name to map too - optional, name used if not given 6209 * @memberof DataTable#oApi 6210 */ 6211 function _fnMap( ret, src, name, mappedName ) 6212 { 6213 if ( Array.isArray( name ) ) { 6214 $.each( name, function (i, val) { 6215 if ( Array.isArray( val ) ) { 6216 _fnMap( ret, src, val[0], val[1] ); 6217 } 6218 else { 6219 _fnMap( ret, src, val ); 6220 } 6221 } ); 6222 6223 return; 6224 } 6225 6226 if ( mappedName === undefined ) { 6227 mappedName = name; 6228 } 6229 6230 if ( src[name] !== undefined ) { 6231 ret[mappedName] = src[name]; 6232 } 6233 } 6234 6235 6236 /** 6237 * Extend objects - very similar to jQuery.extend, but deep copy objects, and 6238 * shallow copy arrays. The reason we need to do this, is that we don't want to 6239 * deep copy array init values (such as aaSorting) since the dev wouldn't be 6240 * able to override them, but we do want to deep copy arrays. 6241 * @param {object} out Object to extend 6242 * @param {object} extender Object from which the properties will be applied to 6243 * out 6244 * @param {boolean} breakRefs If true, then arrays will be sliced to take an 6245 * independent copy with the exception of the `data` or `aaData` parameters 6246 * if they are present. This is so you can pass in a collection to 6247 * DataTables and have that used as your data source without breaking the 6248 * references 6249 * @returns {object} out Reference, just for convenience - out === the return. 6250 * @memberof DataTable#oApi 6251 * @todo This doesn't take account of arrays inside the deep copied objects. 6252 */ 6253 function _fnExtend( out, extender, breakRefs ) 6254 { 6255 var val; 6256 6257 for ( var prop in extender ) { 6258 if ( Object.prototype.hasOwnProperty.call(extender, prop) ) { 6259 val = extender[prop]; 6260 6261 if ( $.isPlainObject( val ) ) { 6262 if ( ! $.isPlainObject( out[prop] ) ) { 6263 out[prop] = {}; 6264 } 6265 $.extend( true, out[prop], val ); 6266 } 6267 else if ( breakRefs && prop !== 'data' && prop !== 'aaData' && Array.isArray(val) ) { 6268 out[prop] = val.slice(); 6269 } 6270 else { 6271 out[prop] = val; 6272 } 6273 } 6274 } 6275 6276 return out; 6277 } 6278 6279 6280 /** 6281 * Bind an event handers to allow a click or return key to activate the callback. 6282 * This is good for accessibility since a return on the keyboard will have the 6283 * same effect as a click, if the element has focus. 6284 * @param {element} n Element to bind the action to 6285 * @param {object|string} selector Selector (for delegated events) or data object 6286 * to pass to the triggered function 6287 * @param {function} fn Callback function for when the event is triggered 6288 * @memberof DataTable#oApi 6289 */ 6290 function _fnBindAction( n, selector, fn ) 6291 { 6292 $(n) 6293 .on( 'click.DT', selector, function (e) { 6294 fn(e); 6295 } ) 6296 .on( 'keypress.DT', selector, function (e){ 6297 if ( e.which === 13 ) { 6298 e.preventDefault(); 6299 fn(e); 6300 } 6301 } ) 6302 .on( 'selectstart.DT', selector, function () { 6303 // Don't want a double click resulting in text selection 6304 return false; 6305 } ); 6306 } 6307 6308 6309 /** 6310 * Register a callback function. Easily allows a callback function to be added to 6311 * an array store of callback functions that can then all be called together. 6312 * @param {object} settings dataTables settings object 6313 * @param {string} store Name of the array storage for the callbacks in oSettings 6314 * @param {function} fn Function to be called back 6315 * @memberof DataTable#oApi 6316 */ 6317 function _fnCallbackReg( settings, store, fn ) 6318 { 6319 if ( fn ) { 6320 settings[store].push(fn); 6321 } 6322 } 6323 6324 6325 /** 6326 * Fire callback functions and trigger events. Note that the loop over the 6327 * callback array store is done backwards! Further note that you do not want to 6328 * fire off triggers in time sensitive applications (for example cell creation) 6329 * as its slow. 6330 * @param {object} settings dataTables settings object 6331 * @param {string} callbackArr Name of the array storage for the callbacks in 6332 * oSettings 6333 * @param {string} eventName Name of the jQuery custom event to trigger. If 6334 * null no trigger is fired 6335 * @param {array} args Array of arguments to pass to the callback function / 6336 * trigger 6337 * @param {boolean} [bubbles] True if the event should bubble 6338 * @memberof DataTable#oApi 6339 */ 6340 function _fnCallbackFire( settings, callbackArr, eventName, args, bubbles ) 6341 { 6342 var ret = []; 6343 6344 if ( callbackArr ) { 6345 ret = settings[callbackArr].slice().reverse().map( function (val) { 6346 return val.apply( settings.oInstance, args ); 6347 } ); 6348 } 6349 6350 if ( eventName !== null) { 6351 var e = $.Event( eventName+'.dt' ); 6352 var table = $(settings.nTable); 6353 6354 // Expose the DataTables API on the event object for easy access 6355 e.dt = settings.api; 6356 6357 table[bubbles ? 'trigger' : 'triggerHandler']( e, args ); 6358 6359 // If not yet attached to the document, trigger the event 6360 // on the body directly to sort of simulate the bubble 6361 if (bubbles && table.parents('body').length === 0) { 6362 $('body').trigger( e, args ); 6363 } 6364 6365 ret.push( e.result ); 6366 } 6367 6368 return ret; 6369 } 6370 6371 6372 function _fnLengthOverflow ( settings ) 6373 { 6374 var 6375 start = settings._iDisplayStart, 6376 end = settings.fnDisplayEnd(), 6377 len = settings._iDisplayLength; 6378 6379 /* If we have space to show extra rows (backing up from the end point - then do so */ 6380 if ( start >= end ) 6381 { 6382 start = end - len; 6383 } 6384 6385 // Keep the start record on the current page 6386 start -= (start % len); 6387 6388 if ( len === -1 || start < 0 ) 6389 { 6390 start = 0; 6391 } 6392 6393 settings._iDisplayStart = start; 6394 } 6395 6396 6397 function _fnRenderer( settings, type ) 6398 { 6399 var renderer = settings.renderer; 6400 var host = DataTable.ext.renderer[type]; 6401 6402 if ( $.isPlainObject( renderer ) && renderer[type] ) { 6403 // Specific renderer for this type. If available use it, otherwise use 6404 // the default. 6405 return host[renderer[type]] || host._; 6406 } 6407 else if ( typeof renderer === 'string' ) { 6408 // Common renderer - if there is one available for this type use it, 6409 // otherwise use the default 6410 return host[renderer] || host._; 6411 } 6412 6413 // Use the default 6414 return host._; 6415 } 6416 6417 6418 /** 6419 * Detect the data source being used for the table. Used to simplify the code 6420 * a little (ajax) and to make it compress a little smaller. 6421 * 6422 * @param {object} settings dataTables settings object 6423 * @returns {string} Data source 6424 * @memberof DataTable#oApi 6425 */ 6426 function _fnDataSource ( settings ) 6427 { 6428 if ( settings.oFeatures.bServerSide ) { 6429 return 'ssp'; 6430 } 6431 else if ( settings.ajax ) { 6432 return 'ajax'; 6433 } 6434 return 'dom'; 6435 } 6436 6437 /** 6438 * Common replacement for language strings 6439 * 6440 * @param {*} settings DT settings object 6441 * @param {*} str String with values to replace 6442 * @param {*} entries Plural number for _ENTRIES_ - can be undefined 6443 * @returns String 6444 */ 6445 function _fnMacros ( settings, str, entries ) 6446 { 6447 // When infinite scrolling, we are always starting at 1. _iDisplayStart is 6448 // used only internally 6449 var 6450 formatter = settings.fnFormatNumber, 6451 start = settings._iDisplayStart+1, 6452 len = settings._iDisplayLength, 6453 vis = settings.fnRecordsDisplay(), 6454 max = settings.fnRecordsTotal(), 6455 all = len === -1; 6456 6457 return str. 6458 replace(/_START_/g, formatter.call( settings, start ) ). 6459 replace(/_END_/g, formatter.call( settings, settings.fnDisplayEnd() ) ). 6460 replace(/_MAX_/g, formatter.call( settings, max ) ). 6461 replace(/_TOTAL_/g, formatter.call( settings, vis ) ). 6462 replace(/_PAGE_/g, formatter.call( settings, all ? 1 : Math.ceil( start / len ) ) ). 6463 replace(/_PAGES_/g, formatter.call( settings, all ? 1 : Math.ceil( vis / len ) ) ). 6464 replace(/_ENTRIES_/g, settings.api.i18n('entries', '', entries) ). 6465 replace(/_ENTRIES-MAX_/g, settings.api.i18n('entries', '', max) ). 6466 replace(/_ENTRIES-TOTAL_/g, settings.api.i18n('entries', '', vis) ); 6467 } 6468 6469 6470 6471 /** 6472 * Computed structure of the DataTables API, defined by the options passed to 6473 * `DataTable.Api.register()` when building the API. 6474 * 6475 * The structure is built in order to speed creation and extension of the Api 6476 * objects since the extensions are effectively pre-parsed. 6477 * 6478 * The array is an array of objects with the following structure, where this 6479 * base array represents the Api prototype base: 6480 * 6481 * [ 6482 * { 6483 * name: 'data' -- string - Property name 6484 * val: function () {}, -- function - Api method (or undefined if just an object 6485 * methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result 6486 * propExt: [ ... ] -- array - Array of Api object definitions to extend the property 6487 * }, 6488 * { 6489 * name: 'row' 6490 * val: {}, 6491 * methodExt: [ ... ], 6492 * propExt: [ 6493 * { 6494 * name: 'data' 6495 * val: function () {}, 6496 * methodExt: [ ... ], 6497 * propExt: [ ... ] 6498 * }, 6499 * ... 6500 * ] 6501 * } 6502 * ] 6503 * 6504 * @type {Array} 6505 * @ignore 6506 */ 6507 var __apiStruct = []; 6508 6509 6510 /** 6511 * `Array.prototype` reference. 6512 * 6513 * @type object 6514 * @ignore 6515 */ 6516 var __arrayProto = Array.prototype; 6517 6518 6519 /** 6520 * Abstraction for `context` parameter of the `Api` constructor to allow it to 6521 * take several different forms for ease of use. 6522 * 6523 * Each of the input parameter types will be converted to a DataTables settings 6524 * object where possible. 6525 * 6526 * @param {string|node|jQuery|object} mixed DataTable identifier. Can be one 6527 * of: 6528 * 6529 * * `string` - jQuery selector. Any DataTables' matching the given selector 6530 * with be found and used. 6531 * * `node` - `TABLE` node which has already been formed into a DataTable. 6532 * * `jQuery` - A jQuery object of `TABLE` nodes. 6533 * * `object` - DataTables settings object 6534 * * `DataTables.Api` - API instance 6535 * @return {array|null} Matching DataTables settings objects. `null` or 6536 * `undefined` is returned if no matching DataTable is found. 6537 * @ignore 6538 */ 6539 var _toSettings = function ( mixed ) 6540 { 6541 var idx, jq; 6542 var settings = DataTable.settings; 6543 var tables = _pluck(settings, 'nTable'); 6544 6545 if ( ! mixed ) { 6546 return []; 6547 } 6548 else if ( mixed.nTable && mixed.oFeatures ) { 6549 // DataTables settings object 6550 return [ mixed ]; 6551 } 6552 else if ( mixed.nodeName && mixed.nodeName.toLowerCase() === 'table' ) { 6553 // Table node 6554 idx = tables.indexOf(mixed); 6555 return idx !== -1 ? [ settings[idx] ] : null; 6556 } 6557 else if ( mixed && typeof mixed.settings === 'function' ) { 6558 return mixed.settings().toArray(); 6559 } 6560 else if ( typeof mixed === 'string' ) { 6561 // jQuery selector 6562 jq = $(mixed).get(); 6563 } 6564 else if ( mixed instanceof $ ) { 6565 // jQuery object (also DataTables instance) 6566 jq = mixed.get(); 6567 } 6568 6569 if ( jq ) { 6570 return settings.filter(function (v, idx) { 6571 return jq.includes(tables[idx]); 6572 }); 6573 } 6574 }; 6575 6576 6577 /** 6578 * DataTables API class - used to control and interface with one or more 6579 * DataTables enhanced tables. 6580 * 6581 * The API class is heavily based on jQuery, presenting a chainable interface 6582 * that you can use to interact with tables. Each instance of the API class has 6583 * a "context" - i.e. the tables that it will operate on. This could be a single 6584 * table, all tables on a page or a sub-set thereof. 6585 * 6586 * Additionally the API is designed to allow you to easily work with the data in 6587 * the tables, retrieving and manipulating it as required. This is done by 6588 * presenting the API class as an array like interface. The contents of the 6589 * array depend upon the actions requested by each method (for example 6590 * `rows().nodes()` will return an array of nodes, while `rows().data()` will 6591 * return an array of objects or arrays depending upon your table's 6592 * configuration). The API object has a number of array like methods (`push`, 6593 * `pop`, `reverse` etc) as well as additional helper methods (`each`, `pluck`, 6594 * `unique` etc) to assist your working with the data held in a table. 6595 * 6596 * Most methods (those which return an Api instance) are chainable, which means 6597 * the return from a method call also has all of the methods available that the 6598 * top level object had. For example, these two calls are equivalent: 6599 * 6600 * // Not chained 6601 * api.row.add( {...} ); 6602 * api.draw(); 6603 * 6604 * // Chained 6605 * api.row.add( {...} ).draw(); 6606 * 6607 * @class DataTable.Api 6608 * @param {array|object|string|jQuery} context DataTable identifier. This is 6609 * used to define which DataTables enhanced tables this API will operate on. 6610 * Can be one of: 6611 * 6612 * * `string` - jQuery selector. Any DataTables' matching the given selector 6613 * with be found and used. 6614 * * `node` - `TABLE` node which has already been formed into a DataTable. 6615 * * `jQuery` - A jQuery object of `TABLE` nodes. 6616 * * `object` - DataTables settings object 6617 * @param {array} [data] Data to initialise the Api instance with. 6618 * 6619 * @example 6620 * // Direct initialisation during DataTables construction 6621 * var api = $('#example').DataTable(); 6622 * 6623 * @example 6624 * // Initialisation using a DataTables jQuery object 6625 * var api = $('#example').dataTable().api(); 6626 * 6627 * @example 6628 * // Initialisation as a constructor 6629 * var api = new DataTable.Api( 'table.dataTable' ); 6630 */ 6631 _Api = function ( context, data ) 6632 { 6633 if ( ! (this instanceof _Api) ) { 6634 return new _Api( context, data ); 6635 } 6636 6637 var settings = []; 6638 var ctxSettings = function ( o ) { 6639 var a = _toSettings( o ); 6640 if ( a ) { 6641 settings.push.apply( settings, a ); 6642 } 6643 }; 6644 6645 if ( Array.isArray( context ) ) { 6646 for ( var i=0, ien=context.length ; i<ien ; i++ ) { 6647 ctxSettings( context[i] ); 6648 } 6649 } 6650 else { 6651 ctxSettings( context ); 6652 } 6653 6654 // Remove duplicates 6655 this.context = settings.length > 1 6656 ? _unique( settings ) 6657 : settings; 6658 6659 // Initial data 6660 if ( data ) { 6661 this.push.apply(this, data); 6662 } 6663 6664 // selector 6665 this.selector = { 6666 rows: null, 6667 cols: null, 6668 opts: null 6669 }; 6670 6671 _Api.extend( this, this, __apiStruct ); 6672 }; 6673 6674 DataTable.Api = _Api; 6675 6676 // Don't destroy the existing prototype, just extend it. Required for jQuery 2's 6677 // isPlainObject. 6678 $.extend( _Api.prototype, { 6679 any: function () 6680 { 6681 return this.count() !== 0; 6682 }, 6683 6684 context: [], // array of table settings objects 6685 6686 count: function () 6687 { 6688 return this.flatten().length; 6689 }, 6690 6691 each: function ( fn ) 6692 { 6693 for ( var i=0, ien=this.length ; i<ien; i++ ) { 6694 fn.call( this, this[i], i, this ); 6695 } 6696 6697 return this; 6698 }, 6699 6700 eq: function ( idx ) 6701 { 6702 var ctx = this.context; 6703 6704 return ctx.length > idx ? 6705 new _Api( ctx[idx], this[idx] ) : 6706 null; 6707 }, 6708 6709 filter: function ( fn ) 6710 { 6711 var a = __arrayProto.filter.call( this, fn, this ); 6712 6713 return new _Api( this.context, a ); 6714 }, 6715 6716 flatten: function () 6717 { 6718 var a = []; 6719 6720 return new _Api( this.context, a.concat.apply( a, this.toArray() ) ); 6721 }, 6722 6723 get: function ( idx ) 6724 { 6725 return this[ idx ]; 6726 }, 6727 6728 join: __arrayProto.join, 6729 6730 includes: function ( find ) { 6731 return this.indexOf( find ) === -1 ? false : true; 6732 }, 6733 6734 indexOf: __arrayProto.indexOf, 6735 6736 iterator: function ( flatten, type, fn, alwaysNew ) { 6737 var 6738 a = [], ret, 6739 i, ien, j, jen, 6740 context = this.context, 6741 rows, items, item, 6742 selector = this.selector; 6743 6744 // Argument shifting 6745 if ( typeof flatten === 'string' ) { 6746 alwaysNew = fn; 6747 fn = type; 6748 type = flatten; 6749 flatten = false; 6750 } 6751 6752 for ( i=0, ien=context.length ; i<ien ; i++ ) { 6753 var apiInst = new _Api( context[i] ); 6754 6755 if ( type === 'table' ) { 6756 ret = fn.call( apiInst, context[i], i ); 6757 6758 if ( ret !== undefined ) { 6759 a.push( ret ); 6760 } 6761 } 6762 else if ( type === 'columns' || type === 'rows' ) { 6763 // this has same length as context - one entry for each table 6764 ret = fn.call( apiInst, context[i], this[i], i ); 6765 6766 if ( ret !== undefined ) { 6767 a.push( ret ); 6768 } 6769 } 6770 else if ( type === 'every' || type === 'column' || type === 'column-rows' || type === 'row' || type === 'cell' ) { 6771 // columns and rows share the same structure. 6772 // 'this' is an array of column indexes for each context 6773 items = this[i]; 6774 6775 if ( type === 'column-rows' ) { 6776 rows = _selector_row_indexes( context[i], selector.opts ); 6777 } 6778 6779 for ( j=0, jen=items.length ; j<jen ; j++ ) { 6780 item = items[j]; 6781 6782 if ( type === 'cell' ) { 6783 ret = fn.call( apiInst, context[i], item.row, item.column, i, j ); 6784 } 6785 else { 6786 ret = fn.call( apiInst, context[i], item, i, j, rows ); 6787 } 6788 6789 if ( ret !== undefined ) { 6790 a.push( ret ); 6791 } 6792 } 6793 } 6794 } 6795 6796 if ( a.length || alwaysNew ) { 6797 var api = new _Api( context, flatten ? a.concat.apply( [], a ) : a ); 6798 var apiSelector = api.selector; 6799 apiSelector.rows = selector.rows; 6800 apiSelector.cols = selector.cols; 6801 apiSelector.opts = selector.opts; 6802 return api; 6803 } 6804 return this; 6805 }, 6806 6807 lastIndexOf: __arrayProto.lastIndexOf, 6808 6809 length: 0, 6810 6811 map: function ( fn ) 6812 { 6813 var a = __arrayProto.map.call( this, fn, this ); 6814 6815 return new _Api( this.context, a ); 6816 }, 6817 6818 pluck: function ( prop ) 6819 { 6820 var fn = DataTable.util.get(prop); 6821 6822 return this.map( function ( el ) { 6823 return fn(el); 6824 } ); 6825 }, 6826 6827 pop: __arrayProto.pop, 6828 6829 push: __arrayProto.push, 6830 6831 reduce: __arrayProto.reduce, 6832 6833 reduceRight: __arrayProto.reduceRight, 6834 6835 reverse: __arrayProto.reverse, 6836 6837 // Object with rows, columns and opts 6838 selector: null, 6839 6840 shift: __arrayProto.shift, 6841 6842 slice: function () { 6843 return new _Api( this.context, this ); 6844 }, 6845 6846 sort: __arrayProto.sort, 6847 6848 splice: __arrayProto.splice, 6849 6850 toArray: function () 6851 { 6852 return __arrayProto.slice.call( this ); 6853 }, 6854 6855 to$: function () 6856 { 6857 return $( this ); 6858 }, 6859 6860 toJQuery: function () 6861 { 6862 return $( this ); 6863 }, 6864 6865 unique: function () 6866 { 6867 return new _Api( this.context, _unique(this.toArray()) ); 6868 }, 6869 6870 unshift: __arrayProto.unshift 6871 } ); 6872 6873 6874 function _api_scope( scope, fn, struc ) { 6875 return function () { 6876 var ret = fn.apply( scope || this, arguments ); 6877 6878 // Method extension 6879 _Api.extend( ret, ret, struc.methodExt ); 6880 return ret; 6881 }; 6882 } 6883 6884 function _api_find( src, name ) { 6885 for ( var i=0, ien=src.length ; i<ien ; i++ ) { 6886 if ( src[i].name === name ) { 6887 return src[i]; 6888 } 6889 } 6890 return null; 6891 } 6892 6893 window.__apiStruct = __apiStruct; 6894 6895 _Api.extend = function ( scope, obj, ext ) 6896 { 6897 // Only extend API instances and static properties of the API 6898 if ( ! ext.length || ! obj || ( ! (obj instanceof _Api) && ! obj.__dt_wrapper ) ) { 6899 return; 6900 } 6901 6902 var 6903 i, ien, 6904 struct; 6905 6906 for ( i=0, ien=ext.length ; i<ien ; i++ ) { 6907 struct = ext[i]; 6908 6909 if (struct.name === '__proto__') { 6910 continue; 6911 } 6912 6913 // Value 6914 obj[ struct.name ] = struct.type === 'function' ? 6915 _api_scope( scope, struct.val, struct ) : 6916 struct.type === 'object' ? 6917 {} : 6918 struct.val; 6919 6920 obj[ struct.name ].__dt_wrapper = true; 6921 6922 // Property extension 6923 _Api.extend( scope, obj[ struct.name ], struct.propExt ); 6924 } 6925 }; 6926 6927 // [ 6928 // { 6929 // name: 'data' -- string - Property name 6930 // val: function () {}, -- function - Api method (or undefined if just an object 6931 // methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result 6932 // propExt: [ ... ] -- array - Array of Api object definitions to extend the property 6933 // }, 6934 // { 6935 // name: 'row' 6936 // val: {}, 6937 // methodExt: [ ... ], 6938 // propExt: [ 6939 // { 6940 // name: 'data' 6941 // val: function () {}, 6942 // methodExt: [ ... ], 6943 // propExt: [ ... ] 6944 // }, 6945 // ... 6946 // ] 6947 // } 6948 // ] 6949 6950 6951 _Api.register = _api_register = function ( name, val ) 6952 { 6953 if ( Array.isArray( name ) ) { 6954 for ( var j=0, jen=name.length ; j<jen ; j++ ) { 6955 _Api.register( name[j], val ); 6956 } 6957 return; 6958 } 6959 6960 var 6961 i, ien, 6962 heir = name.split('.'), 6963 struct = __apiStruct, 6964 key, method; 6965 6966 for ( i=0, ien=heir.length ; i<ien ; i++ ) { 6967 method = heir[i].indexOf('()') !== -1; 6968 key = method ? 6969 heir[i].replace('()', '') : 6970 heir[i]; 6971 6972 var src = _api_find( struct, key ); 6973 if ( ! src ) { 6974 src = { 6975 name: key, 6976 val: {}, 6977 methodExt: [], 6978 propExt: [], 6979 type: 'object' 6980 }; 6981 struct.push( src ); 6982 } 6983 6984 if ( i === ien-1 ) { 6985 src.val = val; 6986 src.type = typeof val === 'function' ? 6987 'function' : 6988 $.isPlainObject( val ) ? 6989 'object' : 6990 'other'; 6991 } 6992 else { 6993 struct = method ? 6994 src.methodExt : 6995 src.propExt; 6996 } 6997 } 6998 }; 6999 7000 _Api.registerPlural = _api_registerPlural = function ( pluralName, singularName, val ) { 7001 _Api.register( pluralName, val ); 7002 7003 _Api.register( singularName, function () { 7004 var ret = val.apply( this, arguments ); 7005 7006 if ( ret === this ) { 7007 // Returned item is the API instance that was passed in, return it 7008 return this; 7009 } 7010 else if ( ret instanceof _Api ) { 7011 // New API instance returned, want the value from the first item 7012 // in the returned array for the singular result. 7013 return ret.length ? 7014 Array.isArray( ret[0] ) ? 7015 new _Api( ret.context, ret[0] ) : // Array results are 'enhanced' 7016 ret[0] : 7017 undefined; 7018 } 7019 7020 // Non-API return - just fire it back 7021 return ret; 7022 } ); 7023 }; 7024 7025 7026 /** 7027 * Selector for HTML tables. Apply the given selector to the give array of 7028 * DataTables settings objects. 7029 * 7030 * @param {string|integer} [selector] jQuery selector string or integer 7031 * @param {array} Array of DataTables settings objects to be filtered 7032 * @return {array} 7033 * @ignore 7034 */ 7035 var __table_selector = function ( selector, a ) 7036 { 7037 if ( Array.isArray(selector) ) { 7038 var result = []; 7039 7040 selector.forEach(function (sel) { 7041 var inner = __table_selector(sel, a); 7042 7043 result.push.apply(result, inner); 7044 }); 7045 7046 return result.filter( function (item) { 7047 return item; 7048 }); 7049 } 7050 7051 // Integer is used to pick out a table by index 7052 if ( typeof selector === 'number' ) { 7053 return [ a[ selector ] ]; 7054 } 7055 7056 // Perform a jQuery selector on the table nodes 7057 var nodes = a.map( function (el) { 7058 return el.nTable; 7059 } ); 7060 7061 return $(nodes) 7062 .filter( selector ) 7063 .map( function () { 7064 // Need to translate back from the table node to the settings 7065 var idx = nodes.indexOf(this); 7066 return a[ idx ]; 7067 } ) 7068 .toArray(); 7069 }; 7070 7071 7072 7073 /** 7074 * Context selector for the API's context (i.e. the tables the API instance 7075 * refers to. 7076 * 7077 * @name DataTable.Api#tables 7078 * @param {string|integer} [selector] Selector to pick which tables the iterator 7079 * should operate on. If not given, all tables in the current context are 7080 * used. This can be given as a jQuery selector (for example `':gt(0)'`) to 7081 * select multiple tables or as an integer to select a single table. 7082 * @returns {DataTable.Api} Returns a new API instance if a selector is given. 7083 */ 7084 _api_register( 'tables()', function ( selector ) { 7085 // A new instance is created if there was a selector specified 7086 return selector !== undefined && selector !== null ? 7087 new _Api( __table_selector( selector, this.context ) ) : 7088 this; 7089 } ); 7090 7091 7092 _api_register( 'table()', function ( selector ) { 7093 var tables = this.tables( selector ); 7094 var ctx = tables.context; 7095 7096 // Truncate to the first matched table 7097 return ctx.length ? 7098 new _Api( ctx[0] ) : 7099 tables; 7100 } ); 7101 7102 // Common methods, combined to reduce size 7103 [ 7104 ['nodes', 'node', 'nTable'], 7105 ['body', 'body', 'nTBody'], 7106 ['header', 'header', 'nTHead'], 7107 ['footer', 'footer', 'nTFoot'], 7108 ].forEach(function (item) { 7109 _api_registerPlural( 7110 'tables().' + item[0] + '()', 7111 'table().' + item[1] + '()' , 7112 function () { 7113 return this.iterator( 'table', function ( ctx ) { 7114 return ctx[item[2]]; 7115 }, 1 ); 7116 } 7117 ); 7118 }); 7119 7120 // Structure methods 7121 [ 7122 ['header', 'aoHeader'], 7123 ['footer', 'aoFooter'], 7124 ].forEach(function (item) { 7125 _api_register( 'table().' + item[0] + '.structure()' , function (selector) { 7126 var indexes = this.columns(selector).indexes().flatten(); 7127 var ctx = this.context[0]; 7128 7129 return _fnHeaderLayout(ctx, ctx[item[1]], indexes); 7130 } ); 7131 }) 7132 7133 7134 _api_registerPlural( 'tables().containers()', 'table().container()' , function () { 7135 return this.iterator( 'table', function ( ctx ) { 7136 return ctx.nTableWrapper; 7137 }, 1 ); 7138 } ); 7139 7140 _api_register( 'tables().every()', function ( fn ) { 7141 var that = this; 7142 7143 return this.iterator('table', function (s, i) { 7144 fn.call(that.table(i), i); 7145 }); 7146 }); 7147 7148 _api_register( 'caption()', function ( value, side ) { 7149 var context = this.context; 7150 7151 // Getter - return existing node's content 7152 if ( value === undefined ) { 7153 var caption = context[0].captionNode; 7154 7155 return caption && context.length ? 7156 caption.innerHTML : 7157 null; 7158 } 7159 7160 return this.iterator( 'table', function ( ctx ) { 7161 var table = $(ctx.nTable); 7162 var caption = $(ctx.captionNode); 7163 var container = $(ctx.nTableWrapper); 7164 7165 // Create the node if it doesn't exist yet 7166 if ( ! caption.length ) { 7167 caption = $('<caption/>').html( value ); 7168 ctx.captionNode = caption[0]; 7169 7170 // If side isn't set, we need to insert into the document to let the 7171 // CSS decide so we can read it back, otherwise there is no way to 7172 // know if the CSS would put it top or bottom for scrolling 7173 if (! side) { 7174 table.prepend(caption); 7175 7176 side = caption.css('caption-side'); 7177 } 7178 } 7179 7180 caption.html( value ); 7181 7182 if ( side ) { 7183 caption.css( 'caption-side', side ); 7184 caption[0]._captionSide = side; 7185 } 7186 7187 if (container.find('div.dataTables_scroll').length) { 7188 var selector = (side === 'top' ? 'Head' : 'Foot'); 7189 7190 container.find('div.dataTables_scroll'+ selector +' table').prepend(caption); 7191 } 7192 else { 7193 table.prepend(caption); 7194 } 7195 }, 1 ); 7196 } ); 7197 7198 _api_register( 'caption.node()', function () { 7199 var ctx = this.context; 7200 7201 return ctx.length ? ctx[0].captionNode : null; 7202 } ); 7203 7204 7205 /** 7206 * Redraw the tables in the current context. 7207 */ 7208 _api_register( 'draw()', function ( paging ) { 7209 return this.iterator( 'table', function ( settings ) { 7210 if ( paging === 'page' ) { 7211 _fnDraw( settings ); 7212 } 7213 else { 7214 if ( typeof paging === 'string' ) { 7215 paging = paging === 'full-hold' ? 7216 false : 7217 true; 7218 } 7219 7220 _fnReDraw( settings, paging===false ); 7221 } 7222 } ); 7223 } ); 7224 7225 7226 7227 /** 7228 * Get the current page index. 7229 * 7230 * @return {integer} Current page index (zero based) 7231 *//** 7232 * Set the current page. 7233 * 7234 * Note that if you attempt to show a page which does not exist, DataTables will 7235 * not throw an error, but rather reset the paging. 7236 * 7237 * @param {integer|string} action The paging action to take. This can be one of: 7238 * * `integer` - The page index to jump to 7239 * * `string` - An action to take: 7240 * * `first` - Jump to first page. 7241 * * `next` - Jump to the next page 7242 * * `previous` - Jump to previous page 7243 * * `last` - Jump to the last page. 7244 * @returns {DataTables.Api} this 7245 */ 7246 _api_register( 'page()', function ( action ) { 7247 if ( action === undefined ) { 7248 return this.page.info().page; // not an expensive call 7249 } 7250 7251 // else, have an action to take on all tables 7252 return this.iterator( 'table', function ( settings ) { 7253 _fnPageChange( settings, action ); 7254 } ); 7255 } ); 7256 7257 7258 /** 7259 * Paging information for the first table in the current context. 7260 * 7261 * If you require paging information for another table, use the `table()` method 7262 * with a suitable selector. 7263 * 7264 * @return {object} Object with the following properties set: 7265 * * `page` - Current page index (zero based - i.e. the first page is `0`) 7266 * * `pages` - Total number of pages 7267 * * `start` - Display index for the first record shown on the current page 7268 * * `end` - Display index for the last record shown on the current page 7269 * * `length` - Display length (number of records). Note that generally `start 7270 * + length = end`, but this is not always true, for example if there are 7271 * only 2 records to show on the final page, with a length of 10. 7272 * * `recordsTotal` - Full data set length 7273 * * `recordsDisplay` - Data set length once the current filtering criterion 7274 * are applied. 7275 */ 7276 _api_register( 'page.info()', function () { 7277 if ( this.context.length === 0 ) { 7278 return undefined; 7279 } 7280 7281 var 7282 settings = this.context[0], 7283 start = settings._iDisplayStart, 7284 len = settings.oFeatures.bPaginate ? settings._iDisplayLength : -1, 7285 visRecords = settings.fnRecordsDisplay(), 7286 all = len === -1; 7287 7288 return { 7289 "page": all ? 0 : Math.floor( start / len ), 7290 "pages": all ? 1 : Math.ceil( visRecords / len ), 7291 "start": start, 7292 "end": settings.fnDisplayEnd(), 7293 "length": len, 7294 "recordsTotal": settings.fnRecordsTotal(), 7295 "recordsDisplay": visRecords, 7296 "serverSide": _fnDataSource( settings ) === 'ssp' 7297 }; 7298 } ); 7299 7300 7301 /** 7302 * Get the current page length. 7303 * 7304 * @return {integer} Current page length. Note `-1` indicates that all records 7305 * are to be shown. 7306 *//** 7307 * Set the current page length. 7308 * 7309 * @param {integer} Page length to set. Use `-1` to show all records. 7310 * @returns {DataTables.Api} this 7311 */ 7312 _api_register( 'page.len()', function ( len ) { 7313 // Note that we can't call this function 'length()' because `length` 7314 // is a Javascript property of functions which defines how many arguments 7315 // the function expects. 7316 if ( len === undefined ) { 7317 return this.context.length !== 0 ? 7318 this.context[0]._iDisplayLength : 7319 undefined; 7320 } 7321 7322 // else, set the page length 7323 return this.iterator( 'table', function ( settings ) { 7324 _fnLengthChange( settings, len ); 7325 } ); 7326 } ); 7327 7328 7329 7330 var __reload = function ( settings, holdPosition, callback ) { 7331 // Use the draw event to trigger a callback 7332 if ( callback ) { 7333 var api = new _Api( settings ); 7334 7335 api.one( 'draw', function () { 7336 callback( api.ajax.json() ); 7337 } ); 7338 } 7339 7340 if ( _fnDataSource( settings ) == 'ssp' ) { 7341 _fnReDraw( settings, holdPosition ); 7342 } 7343 else { 7344 _fnProcessingDisplay( settings, true ); 7345 7346 // Cancel an existing request 7347 var xhr = settings.jqXHR; 7348 if ( xhr && xhr.readyState !== 4 ) { 7349 xhr.abort(); 7350 } 7351 7352 // Trigger xhr 7353 _fnBuildAjax( settings, {}, function( json ) { 7354 _fnClearTable( settings ); 7355 7356 var data = _fnAjaxDataSrc( settings, json ); 7357 for ( var i=0, ien=data.length ; i<ien ; i++ ) { 7358 _fnAddData( settings, data[i] ); 7359 } 7360 7361 _fnReDraw( settings, holdPosition ); 7362 _fnInitComplete( settings ); 7363 _fnProcessingDisplay( settings, false ); 7364 } ); 7365 } 7366 }; 7367 7368 7369 /** 7370 * Get the JSON response from the last Ajax request that DataTables made to the 7371 * server. Note that this returns the JSON from the first table in the current 7372 * context. 7373 * 7374 * @return {object} JSON received from the server. 7375 */ 7376 _api_register( 'ajax.json()', function () { 7377 var ctx = this.context; 7378 7379 if ( ctx.length > 0 ) { 7380 return ctx[0].json; 7381 } 7382 7383 // else return undefined; 7384 } ); 7385 7386 7387 /** 7388 * Get the data submitted in the last Ajax request 7389 */ 7390 _api_register( 'ajax.params()', function () { 7391 var ctx = this.context; 7392 7393 if ( ctx.length > 0 ) { 7394 return ctx[0].oAjaxData; 7395 } 7396 7397 // else return undefined; 7398 } ); 7399 7400 7401 /** 7402 * Reload tables from the Ajax data source. Note that this function will 7403 * automatically re-draw the table when the remote data has been loaded. 7404 * 7405 * @param {boolean} [reset=true] Reset (default) or hold the current paging 7406 * position. A full re-sort and re-filter is performed when this method is 7407 * called, which is why the pagination reset is the default action. 7408 * @returns {DataTables.Api} this 7409 */ 7410 _api_register( 'ajax.reload()', function ( callback, resetPaging ) { 7411 return this.iterator( 'table', function (settings) { 7412 __reload( settings, resetPaging===false, callback ); 7413 } ); 7414 } ); 7415 7416 7417 /** 7418 * Get the current Ajax URL. Note that this returns the URL from the first 7419 * table in the current context. 7420 * 7421 * @return {string} Current Ajax source URL 7422 *//** 7423 * Set the Ajax URL. Note that this will set the URL for all tables in the 7424 * current context. 7425 * 7426 * @param {string} url URL to set. 7427 * @returns {DataTables.Api} this 7428 */ 7429 _api_register( 'ajax.url()', function ( url ) { 7430 var ctx = this.context; 7431 7432 if ( url === undefined ) { 7433 // get 7434 if ( ctx.length === 0 ) { 7435 return undefined; 7436 } 7437 ctx = ctx[0]; 7438 7439 return $.isPlainObject( ctx.ajax ) ? 7440 ctx.ajax.url : 7441 ctx.ajax; 7442 } 7443 7444 // set 7445 return this.iterator( 'table', function ( settings ) { 7446 if ( $.isPlainObject( settings.ajax ) ) { 7447 settings.ajax.url = url; 7448 } 7449 else { 7450 settings.ajax = url; 7451 } 7452 } ); 7453 } ); 7454 7455 7456 /** 7457 * Load data from the newly set Ajax URL. Note that this method is only 7458 * available when `ajax.url()` is used to set a URL. Additionally, this method 7459 * has the same effect as calling `ajax.reload()` but is provided for 7460 * convenience when setting a new URL. Like `ajax.reload()` it will 7461 * automatically redraw the table once the remote data has been loaded. 7462 * 7463 * @returns {DataTables.Api} this 7464 */ 7465 _api_register( 'ajax.url().load()', function ( callback, resetPaging ) { 7466 // Same as a reload, but makes sense to present it for easy access after a 7467 // url change 7468 return this.iterator( 'table', function ( ctx ) { 7469 __reload( ctx, resetPaging===false, callback ); 7470 } ); 7471 } ); 7472 7473 7474 7475 7476 var _selector_run = function ( type, selector, selectFn, settings, opts ) 7477 { 7478 var 7479 out = [], res, 7480 a, i, ien, j, jen, 7481 selectorType = typeof selector; 7482 7483 // Can't just check for isArray here, as an API or jQuery instance might be 7484 // given with their array like look 7485 if ( ! selector || selectorType === 'string' || selectorType === 'function' || selector.length === undefined ) { 7486 selector = [ selector ]; 7487 } 7488 7489 for ( i=0, ien=selector.length ; i<ien ; i++ ) { 7490 // Only split on simple strings - complex expressions will be jQuery selectors 7491 a = selector[i] && selector[i].split && ! selector[i].match(/[[(:]/) ? 7492 selector[i].split(',') : 7493 [ selector[i] ]; 7494 7495 for ( j=0, jen=a.length ; j<jen ; j++ ) { 7496 res = selectFn( typeof a[j] === 'string' ? (a[j]).trim() : a[j] ); 7497 7498 // Remove empty items 7499 res = res.filter( function (item) { 7500 return item !== null && item !== undefined; 7501 }); 7502 7503 if ( res && res.length ) { 7504 out = out.concat( res ); 7505 } 7506 } 7507 } 7508 7509 // selector extensions 7510 var ext = _ext.selector[ type ]; 7511 if ( ext.length ) { 7512 for ( i=0, ien=ext.length ; i<ien ; i++ ) { 7513 out = ext[i]( settings, opts, out ); 7514 } 7515 } 7516 7517 return _unique( out ); 7518 }; 7519 7520 7521 var _selector_opts = function ( opts ) 7522 { 7523 if ( ! opts ) { 7524 opts = {}; 7525 } 7526 7527 // Backwards compatibility for 1.9- which used the terminology filter rather 7528 // than search 7529 if ( opts.filter && opts.search === undefined ) { 7530 opts.search = opts.filter; 7531 } 7532 7533 return $.extend( { 7534 search: 'none', 7535 order: 'current', 7536 page: 'all' 7537 }, opts ); 7538 }; 7539 7540 7541 // Reduce the API instance to the first item found 7542 var _selector_first = function ( old ) 7543 { 7544 let inst = new _Api(old.context[0]); 7545 7546 // Use a push rather than passing to the constructor, since it will 7547 // merge arrays down automatically, which isn't what is wanted here 7548 if (old.length) { 7549 inst.push( old[0] ); 7550 } 7551 7552 inst.selector = old.selector; 7553 7554 // Limit to a single row / column / cell 7555 if (inst.length && inst[0].length > 1) { 7556 inst[0].splice(1); 7557 } 7558 7559 return inst; 7560 }; 7561 7562 7563 var _selector_row_indexes = function ( settings, opts ) 7564 { 7565 var 7566 i, ien, tmp, a=[], 7567 displayFiltered = settings.aiDisplay, 7568 displayMaster = settings.aiDisplayMaster; 7569 7570 var 7571 search = opts.search, // none, applied, removed 7572 order = opts.order, // applied, current, index (original - compatibility with 1.9) 7573 page = opts.page; // all, current 7574 7575 if ( _fnDataSource( settings ) == 'ssp' ) { 7576 // In server-side processing mode, most options are irrelevant since 7577 // rows not shown don't exist and the index order is the applied order 7578 // Removed is a special case - for consistency just return an empty 7579 // array 7580 return search === 'removed' ? 7581 [] : 7582 _range( 0, displayMaster.length ); 7583 } 7584 7585 if ( page == 'current' ) { 7586 // Current page implies that order=current and filter=applied, since it is 7587 // fairly senseless otherwise, regardless of what order and search actually 7588 // are 7589 for ( i=settings._iDisplayStart, ien=settings.fnDisplayEnd() ; i<ien ; i++ ) { 7590 a.push( displayFiltered[i] ); 7591 } 7592 } 7593 else if ( order == 'current' || order == 'applied' ) { 7594 if ( search == 'none') { 7595 a = displayMaster.slice(); 7596 } 7597 else if ( search == 'applied' ) { 7598 a = displayFiltered.slice(); 7599 } 7600 else if ( search == 'removed' ) { 7601 // O(n+m) solution by creating a hash map 7602 var displayFilteredMap = {}; 7603 7604 for ( i=0, ien=displayFiltered.length ; i<ien ; i++ ) { 7605 displayFilteredMap[displayFiltered[i]] = null; 7606 } 7607 7608 displayMaster.forEach(function (item) { 7609 if (! Object.prototype.hasOwnProperty.call(displayFilteredMap, item)) { 7610 a.push(item); 7611 } 7612 }); 7613 } 7614 } 7615 else if ( order == 'index' || order == 'original' ) { 7616 for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) { 7617 if (! settings.aoData[i]) { 7618 continue; 7619 } 7620 7621 if ( search == 'none' ) { 7622 a.push( i ); 7623 } 7624 else { // applied | removed 7625 tmp = displayFiltered.indexOf(i); 7626 7627 if ((tmp === -1 && search == 'removed') || 7628 (tmp >= 0 && search == 'applied') ) 7629 { 7630 a.push( i ); 7631 } 7632 } 7633 } 7634 } 7635 else if ( typeof order === 'number' ) { 7636 // Order the rows by the given column 7637 var ordered = _fnSort(settings, order, 'asc'); 7638 7639 if (search === 'none') { 7640 a = ordered; 7641 } 7642 else { // applied | removed 7643 for (i=0; i<ordered.length; i++) { 7644 tmp = displayFiltered.indexOf(ordered[i]); 7645 7646 if ((tmp === -1 && search == 'removed') || 7647 (tmp >= 0 && search == 'applied') ) 7648 { 7649 a.push( ordered[i] ); 7650 } 7651 } 7652 } 7653 } 7654 7655 return a; 7656 }; 7657 7658 7659 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 7660 * Rows 7661 * 7662 * {} - no selector - use all available rows 7663 * {integer} - row aoData index 7664 * {node} - TR node 7665 * {string} - jQuery selector to apply to the TR elements 7666 * {array} - jQuery array of nodes, or simply an array of TR nodes 7667 * 7668 */ 7669 var __row_selector = function ( settings, selector, opts ) 7670 { 7671 var rows; 7672 var run = function ( sel ) { 7673 var selInt = _intVal( sel ); 7674 var aoData = settings.aoData; 7675 7676 // Short cut - selector is a number and no options provided (default is 7677 // all records, so no need to check if the index is in there, since it 7678 // must be - dev error if the index doesn't exist). 7679 if ( selInt !== null && ! opts ) { 7680 return [ selInt ]; 7681 } 7682 7683 if ( ! rows ) { 7684 rows = _selector_row_indexes( settings, opts ); 7685 } 7686 7687 if ( selInt !== null && rows.indexOf(selInt) !== -1 ) { 7688 // Selector - integer 7689 return [ selInt ]; 7690 } 7691 else if ( sel === null || sel === undefined || sel === '' ) { 7692 // Selector - none 7693 return rows; 7694 } 7695 7696 // Selector - function 7697 if ( typeof sel === 'function' ) { 7698 return rows.map( function (idx) { 7699 var row = aoData[ idx ]; 7700 return sel( idx, row._aData, row.nTr ) ? idx : null; 7701 } ); 7702 } 7703 7704 // Selector - node 7705 if ( sel.nodeName ) { 7706 var rowIdx = sel._DT_RowIndex; // Property added by DT for fast lookup 7707 var cellIdx = sel._DT_CellIndex; 7708 7709 if ( rowIdx !== undefined ) { 7710 // Make sure that the row is actually still present in the table 7711 return aoData[ rowIdx ] && aoData[ rowIdx ].nTr === sel ? 7712 [ rowIdx ] : 7713 []; 7714 } 7715 else if ( cellIdx ) { 7716 return aoData[ cellIdx.row ] && aoData[ cellIdx.row ].nTr === sel.parentNode ? 7717 [ cellIdx.row ] : 7718 []; 7719 } 7720 else { 7721 var host = $(sel).closest('*[data-dt-row]'); 7722 return host.length ? 7723 [ host.data('dt-row') ] : 7724 []; 7725 } 7726 } 7727 7728 // ID selector. Want to always be able to select rows by id, regardless 7729 // of if the tr element has been created or not, so can't rely upon 7730 // jQuery here - hence a custom implementation. This does not match 7731 // Sizzle's fast selector or HTML4 - in HTML5 the ID can be anything, 7732 // but to select it using a CSS selector engine (like Sizzle or 7733 // querySelect) it would need to need to be escaped for some characters. 7734 // DataTables simplifies this for row selectors since you can select 7735 // only a row. A # indicates an id any anything that follows is the id - 7736 // unescaped. 7737 if ( typeof sel === 'string' && sel.charAt(0) === '#' ) { 7738 // get row index from id 7739 var rowObj = settings.aIds[ sel.replace( /^#/, '' ) ]; 7740 if ( rowObj !== undefined ) { 7741 return [ rowObj.idx ]; 7742 } 7743 7744 // need to fall through to jQuery in case there is DOM id that 7745 // matches 7746 } 7747 7748 // Get nodes in the order from the `rows` array with null values removed 7749 var nodes = _removeEmpty( 7750 _pluck_order( settings.aoData, rows, 'nTr' ) 7751 ); 7752 7753 // Selector - jQuery selector string, array of nodes or jQuery object/ 7754 // As jQuery's .filter() allows jQuery objects to be passed in filter, 7755 // it also allows arrays, so this will cope with all three options 7756 return $(nodes) 7757 .filter( sel ) 7758 .map( function () { 7759 return this._DT_RowIndex; 7760 } ) 7761 .toArray(); 7762 }; 7763 7764 var matched = _selector_run( 'row', selector, run, settings, opts ); 7765 7766 if (opts.order === 'current' || opts.order === 'applied') { 7767 _fnSortDisplay(settings, matched); 7768 } 7769 7770 return matched; 7771 }; 7772 7773 7774 _api_register( 'rows()', function ( selector, opts ) { 7775 // argument shifting 7776 if ( selector === undefined ) { 7777 selector = ''; 7778 } 7779 else if ( $.isPlainObject( selector ) ) { 7780 opts = selector; 7781 selector = ''; 7782 } 7783 7784 opts = _selector_opts( opts ); 7785 7786 var inst = this.iterator( 'table', function ( settings ) { 7787 return __row_selector( settings, selector, opts ); 7788 }, 1 ); 7789 7790 // Want argument shifting here and in __row_selector? 7791 inst.selector.rows = selector; 7792 inst.selector.opts = opts; 7793 7794 return inst; 7795 } ); 7796 7797 _api_register( 'rows().nodes()', function () { 7798 return this.iterator( 'row', function ( settings, row ) { 7799 return settings.aoData[ row ].nTr || undefined; 7800 }, 1 ); 7801 } ); 7802 7803 _api_register( 'rows().data()', function () { 7804 return this.iterator( true, 'rows', function ( settings, rows ) { 7805 return _pluck_order( settings.aoData, rows, '_aData' ); 7806 }, 1 ); 7807 } ); 7808 7809 _api_registerPlural( 'rows().cache()', 'row().cache()', function ( type ) { 7810 return this.iterator( 'row', function ( settings, row ) { 7811 var r = settings.aoData[ row ]; 7812 return type === 'search' ? r._aFilterData : r._aSortData; 7813 }, 1 ); 7814 } ); 7815 7816 _api_registerPlural( 'rows().invalidate()', 'row().invalidate()', function ( src ) { 7817 return this.iterator( 'row', function ( settings, row ) { 7818 _fnInvalidate( settings, row, src ); 7819 } ); 7820 } ); 7821 7822 _api_registerPlural( 'rows().indexes()', 'row().index()', function () { 7823 return this.iterator( 'row', function ( settings, row ) { 7824 return row; 7825 }, 1 ); 7826 } ); 7827 7828 _api_registerPlural( 'rows().ids()', 'row().id()', function ( hash ) { 7829 var a = []; 7830 var context = this.context; 7831 7832 // `iterator` will drop undefined values, but in this case we want them 7833 for ( var i=0, ien=context.length ; i<ien ; i++ ) { 7834 for ( var j=0, jen=this[i].length ; j<jen ; j++ ) { 7835 var id = context[i].rowIdFn( context[i].aoData[ this[i][j] ]._aData ); 7836 a.push( (hash === true ? '#' : '' )+ id ); 7837 } 7838 } 7839 7840 return new _Api( context, a ); 7841 } ); 7842 7843 _api_registerPlural( 'rows().remove()', 'row().remove()', function () { 7844 this.iterator( 'row', function ( settings, row ) { 7845 var data = settings.aoData; 7846 var rowData = data[ row ]; 7847 7848 // Delete from the display arrays 7849 var idx = settings.aiDisplayMaster.indexOf(row); 7850 if (idx !== -1) { 7851 settings.aiDisplayMaster.splice(idx, 1); 7852 } 7853 7854 // For server-side processing tables - subtract the deleted row from the count 7855 if ( settings._iRecordsDisplay > 0 ) { 7856 settings._iRecordsDisplay--; 7857 } 7858 7859 // Check for an 'overflow' they case for displaying the table 7860 _fnLengthOverflow( settings ); 7861 7862 // Remove the row's ID reference if there is one 7863 var id = settings.rowIdFn( rowData._aData ); 7864 if ( id !== undefined ) { 7865 delete settings.aIds[ id ]; 7866 } 7867 7868 data[row] = null; 7869 } ); 7870 7871 return this; 7872 } ); 7873 7874 7875 _api_register( 'rows.add()', function ( rows ) { 7876 var newRows = this.iterator( 'table', function ( settings ) { 7877 var row, i, ien; 7878 var out = []; 7879 7880 for ( i=0, ien=rows.length ; i<ien ; i++ ) { 7881 row = rows[i]; 7882 7883 if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) { 7884 out.push( _fnAddTr( settings, row )[0] ); 7885 } 7886 else { 7887 out.push( _fnAddData( settings, row ) ); 7888 } 7889 } 7890 7891 return out; 7892 }, 1 ); 7893 7894 // Return an Api.rows() extended instance, so rows().nodes() etc can be used 7895 var modRows = this.rows( -1 ); 7896 modRows.pop(); 7897 modRows.push.apply(modRows, newRows); 7898 7899 return modRows; 7900 } ); 7901 7902 7903 7904 7905 7906 /** 7907 * 7908 */ 7909 _api_register( 'row()', function ( selector, opts ) { 7910 return _selector_first( this.rows( selector, opts ) ); 7911 } ); 7912 7913 7914 _api_register( 'row().data()', function ( data ) { 7915 var ctx = this.context; 7916 7917 if ( data === undefined ) { 7918 // Get 7919 return ctx.length && this.length && this[0].length ? 7920 ctx[0].aoData[ this[0] ]._aData : 7921 undefined; 7922 } 7923 7924 // Set 7925 var row = ctx[0].aoData[ this[0] ]; 7926 row._aData = data; 7927 7928 // If the DOM has an id, and the data source is an array 7929 if ( Array.isArray( data ) && row.nTr && row.nTr.id ) { 7930 _fnSetObjectDataFn( ctx[0].rowId )( data, row.nTr.id ); 7931 } 7932 7933 // Automatically invalidate 7934 _fnInvalidate( ctx[0], this[0], 'data' ); 7935 7936 return this; 7937 } ); 7938 7939 7940 _api_register( 'row().node()', function () { 7941 var ctx = this.context; 7942 7943 if (ctx.length && this.length && this[0].length) { 7944 var row = ctx[0].aoData[ this[0] ]; 7945 7946 if (row && row.nTr) { 7947 return row.nTr; 7948 } 7949 } 7950 7951 return null; 7952 } ); 7953 7954 7955 _api_register( 'row.add()', function ( row ) { 7956 // Allow a jQuery object to be passed in - only a single row is added from 7957 // it though - the first element in the set 7958 if ( row instanceof $ && row.length ) { 7959 row = row[0]; 7960 } 7961 7962 var rows = this.iterator( 'table', function ( settings ) { 7963 if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) { 7964 return _fnAddTr( settings, row )[0]; 7965 } 7966 return _fnAddData( settings, row ); 7967 } ); 7968 7969 // Return an Api.rows() extended instance, with the newly added row selected 7970 return this.row( rows[0] ); 7971 } ); 7972 7973 7974 $(document).on('plugin-init.dt', function (e, context) { 7975 var api = new _Api( context ); 7976 7977 api.on( 'stateSaveParams.DT', function ( e, settings, d ) { 7978 // This could be more compact with the API, but it is a lot faster as a simple 7979 // internal loop 7980 var idFn = settings.rowIdFn; 7981 var rows = settings.aiDisplayMaster; 7982 var ids = []; 7983 7984 for (var i=0 ; i<rows.length ; i++) { 7985 var rowIdx = rows[i]; 7986 var data = settings.aoData[rowIdx]; 7987 7988 if (data._detailsShow) { 7989 ids.push( '#' + idFn(data._aData) ); 7990 } 7991 } 7992 7993 d.childRows = ids; 7994 }); 7995 7996 // For future state loads (e.g. with StateRestore) 7997 api.on( 'stateLoaded.DT', function (e, settings, state) { 7998 __details_state_load( api, state ); 7999 }); 8000 8001 // And the initial load state 8002 __details_state_load( api, api.state.loaded() ); 8003 }); 8004 8005 var __details_state_load = function (api, state) 8006 { 8007 if ( state && state.childRows ) { 8008 api 8009 .rows( state.childRows.map(function (id) { 8010 // Escape any `:` characters from the row id. Accounts for 8011 // already escaped characters. 8012 return id.replace(/([^:\\]*(?:\\.[^:\\]*)*):/g, "$1\\:"); 8013 }) ) 8014 .every( function () { 8015 _fnCallbackFire( api.settings()[0], null, 'requestChild', [ this ] ) 8016 }); 8017 } 8018 } 8019 8020 var __details_add = function ( ctx, row, data, klass ) 8021 { 8022 // Convert to array of TR elements 8023 var rows = []; 8024 var addRow = function ( r, k ) { 8025 // Recursion to allow for arrays of jQuery objects 8026 if ( Array.isArray( r ) || r instanceof $ ) { 8027 for ( var i=0, ien=r.length ; i<ien ; i++ ) { 8028 addRow( r[i], k ); 8029 } 8030 return; 8031 } 8032 8033 // If we get a TR element, then just add it directly - up to the dev 8034 // to add the correct number of columns etc 8035 if ( r.nodeName && r.nodeName.toLowerCase() === 'tr' ) { 8036 r.setAttribute( 'data-dt-row', row.idx ); 8037 rows.push( r ); 8038 } 8039 else { 8040 // Otherwise create a row with a wrapper 8041 var created = $('<tr><td></td></tr>') 8042 .attr( 'data-dt-row', row.idx ) 8043 .addClass( k ); 8044 8045 $('td', created) 8046 .addClass( k ) 8047 .html( r )[0].colSpan = _fnVisbleColumns( ctx ); 8048 8049 rows.push( created[0] ); 8050 } 8051 }; 8052 8053 addRow( data, klass ); 8054 8055 if ( row._details ) { 8056 row._details.detach(); 8057 } 8058 8059 row._details = $(rows); 8060 8061 // If the children were already shown, that state should be retained 8062 if ( row._detailsShow ) { 8063 row._details.insertAfter( row.nTr ); 8064 } 8065 }; 8066 8067 8068 // Make state saving of child row details async to allow them to be batch processed 8069 var __details_state = DataTable.util.throttle( 8070 function (ctx) { 8071 _fnSaveState( ctx[0] ) 8072 }, 8073 500 8074 ); 8075 8076 8077 var __details_remove = function ( api, idx ) 8078 { 8079 var ctx = api.context; 8080 8081 if ( ctx.length ) { 8082 var row = ctx[0].aoData[ idx !== undefined ? idx : api[0] ]; 8083 8084 if ( row && row._details ) { 8085 row._details.remove(); 8086 8087 row._detailsShow = undefined; 8088 row._details = undefined; 8089 $( row.nTr ).removeClass( 'dt-hasChild' ); 8090 __details_state( ctx ); 8091 } 8092 } 8093 }; 8094 8095 8096 var __details_display = function ( api, show ) { 8097 var ctx = api.context; 8098 8099 if ( ctx.length && api.length ) { 8100 var row = ctx[0].aoData[ api[0] ]; 8101 8102 if ( row._details ) { 8103 row._detailsShow = show; 8104 8105 if ( show ) { 8106 row._details.insertAfter( row.nTr ); 8107 $( row.nTr ).addClass( 'dt-hasChild' ); 8108 } 8109 else { 8110 row._details.detach(); 8111 $( row.nTr ).removeClass( 'dt-hasChild' ); 8112 } 8113 8114 _fnCallbackFire( ctx[0], null, 'childRow', [ show, api.row( api[0] ) ] ) 8115 8116 __details_events( ctx[0] ); 8117 __details_state( ctx ); 8118 } 8119 } 8120 }; 8121 8122 8123 var __details_events = function ( settings ) 8124 { 8125 var api = new _Api( settings ); 8126 var namespace = '.dt.DT_details'; 8127 var drawEvent = 'draw'+namespace; 8128 var colvisEvent = 'column-sizing'+namespace; 8129 var destroyEvent = 'destroy'+namespace; 8130 var data = settings.aoData; 8131 8132 api.off( drawEvent +' '+ colvisEvent +' '+ destroyEvent ); 8133 8134 if ( _pluck( data, '_details' ).length > 0 ) { 8135 // On each draw, insert the required elements into the document 8136 api.on( drawEvent, function ( e, ctx ) { 8137 if ( settings !== ctx ) { 8138 return; 8139 } 8140 8141 api.rows( {page:'current'} ).eq(0).each( function (idx) { 8142 // Internal data grab 8143 var row = data[ idx ]; 8144 8145 if ( row._detailsShow ) { 8146 row._details.insertAfter( row.nTr ); 8147 } 8148 } ); 8149 } ); 8150 8151 // Column visibility change - update the colspan 8152 api.on( colvisEvent, function ( e, ctx ) { 8153 if ( settings !== ctx ) { 8154 return; 8155 } 8156 8157 // Update the colspan for the details rows (note, only if it already has 8158 // a colspan) 8159 var row, visible = _fnVisbleColumns( ctx ); 8160 8161 for ( var i=0, ien=data.length ; i<ien ; i++ ) { 8162 row = data[i]; 8163 8164 if ( row && row._details ) { 8165 row._details.each(function () { 8166 var el = $(this).children('td'); 8167 8168 if (el.length == 1) { 8169 el.attr('colspan', visible); 8170 } 8171 }); 8172 } 8173 } 8174 } ); 8175 8176 // Table destroyed - nuke any child rows 8177 api.on( destroyEvent, function ( e, ctx ) { 8178 if ( settings !== ctx ) { 8179 return; 8180 } 8181 8182 for ( var i=0, ien=data.length ; i<ien ; i++ ) { 8183 if ( data[i] && data[i]._details ) { 8184 __details_remove( api, i ); 8185 } 8186 } 8187 } ); 8188 } 8189 }; 8190 8191 // Strings for the method names to help minification 8192 var _emp = ''; 8193 var _child_obj = _emp+'row().child'; 8194 var _child_mth = _child_obj+'()'; 8195 8196 // data can be: 8197 // tr 8198 // string 8199 // jQuery or array of any of the above 8200 _api_register( _child_mth, function ( data, klass ) { 8201 var ctx = this.context; 8202 8203 if ( data === undefined ) { 8204 // get 8205 return ctx.length && this.length && ctx[0].aoData[ this[0] ] 8206 ? ctx[0].aoData[ this[0] ]._details 8207 : undefined; 8208 } 8209 else if ( data === true ) { 8210 // show 8211 this.child.show(); 8212 } 8213 else if ( data === false ) { 8214 // remove 8215 __details_remove( this ); 8216 } 8217 else if ( ctx.length && this.length ) { 8218 // set 8219 __details_add( ctx[0], ctx[0].aoData[ this[0] ], data, klass ); 8220 } 8221 8222 return this; 8223 } ); 8224 8225 8226 _api_register( [ 8227 _child_obj+'.show()', 8228 _child_mth+'.show()' // only when `child()` was called with parameters (without 8229 ], function () { // it returns an object and this method is not executed) 8230 __details_display( this, true ); 8231 return this; 8232 } ); 8233 8234 8235 _api_register( [ 8236 _child_obj+'.hide()', 8237 _child_mth+'.hide()' // only when `child()` was called with parameters (without 8238 ], function () { // it returns an object and this method is not executed) 8239 __details_display( this, false ); 8240 return this; 8241 } ); 8242 8243 8244 _api_register( [ 8245 _child_obj+'.remove()', 8246 _child_mth+'.remove()' // only when `child()` was called with parameters (without 8247 ], function () { // it returns an object and this method is not executed) 8248 __details_remove( this ); 8249 return this; 8250 } ); 8251 8252 8253 _api_register( _child_obj+'.isShown()', function () { 8254 var ctx = this.context; 8255 8256 if ( ctx.length && this.length && ctx[0].aoData[ this[0] ] ) { 8257 // _detailsShown as false or undefined will fall through to return false 8258 return ctx[0].aoData[ this[0] ]._detailsShow || false; 8259 } 8260 return false; 8261 } ); 8262 8263 8264 8265 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 8266 * Columns 8267 * 8268 * {integer} - column index (>=0 count from left, <0 count from right) 8269 * "{integer}:visIdx" - visible column index (i.e. translate to column index) (>=0 count from left, <0 count from right) 8270 * "{integer}:visible" - alias for {integer}:visIdx (>=0 count from left, <0 count from right) 8271 * "{string}:name" - column name 8272 * "{string}" - jQuery selector on column header nodes 8273 * 8274 */ 8275 8276 // can be an array of these items, comma separated list, or an array of comma 8277 // separated lists 8278 8279 var __re_column_selector = /^([^:]+)?:(name|title|visIdx|visible)$/; 8280 8281 8282 // r1 and r2 are redundant - but it means that the parameters match for the 8283 // iterator callback in columns().data() 8284 var __columnData = function ( settings, column, r1, r2, rows, type ) { 8285 var a = []; 8286 for ( var row=0, ien=rows.length ; row<ien ; row++ ) { 8287 a.push( _fnGetCellData( settings, rows[row], column, type ) ); 8288 } 8289 return a; 8290 }; 8291 8292 8293 var __column_header = function ( settings, column, row ) { 8294 var header = settings.aoHeader; 8295 var target = row !== undefined 8296 ? row 8297 : settings.bSortCellsTop // legacy support 8298 ? 0 8299 : header.length - 1; 8300 8301 return header[target][column].cell; 8302 }; 8303 8304 var __column_selector = function ( settings, selector, opts ) 8305 { 8306 var 8307 columns = settings.aoColumns, 8308 names = _pluck( columns, 'sName' ), 8309 titles = _pluck( columns, 'sTitle' ), 8310 cells = DataTable.util.get('[].[].cell')(settings.aoHeader), 8311 nodes = _unique( _flatten([], cells) ); 8312 8313 var run = function ( s ) { 8314 var selInt = _intVal( s ); 8315 8316 // Selector - all 8317 if ( s === '' ) { 8318 return _range( columns.length ); 8319 } 8320 8321 // Selector - index 8322 if ( selInt !== null ) { 8323 return [ selInt >= 0 ? 8324 selInt : // Count from left 8325 columns.length + selInt // Count from right (+ because its a negative value) 8326 ]; 8327 } 8328 8329 // Selector = function 8330 if ( typeof s === 'function' ) { 8331 var rows = _selector_row_indexes( settings, opts ); 8332 8333 return columns.map(function (col, idx) { 8334 return s( 8335 idx, 8336 __columnData( settings, idx, 0, 0, rows ), 8337 __column_header( settings, idx ) 8338 ) ? idx : null; 8339 }); 8340 } 8341 8342 // jQuery or string selector 8343 var match = typeof s === 'string' ? 8344 s.match( __re_column_selector ) : 8345 ''; 8346 8347 if ( match ) { 8348 switch( match[2] ) { 8349 case 'visIdx': 8350 case 'visible': 8351 if (match[1]) { 8352 var idx = parseInt( match[1], 10 ); 8353 // Visible index given, convert to column index 8354 if ( idx < 0 ) { 8355 // Counting from the right 8356 var visColumns = columns.map( function (col,i) { 8357 return col.bVisible ? i : null; 8358 } ); 8359 return [ visColumns[ visColumns.length + idx ] ]; 8360 } 8361 // Counting from the left 8362 return [ _fnVisibleToColumnIndex( settings, idx ) ]; 8363 } 8364 8365 // `:visible` on its own 8366 return columns.map( function (col, i) { 8367 return col.bVisible ? i : null; 8368 } ); 8369 8370 case 'name': 8371 // match by name. `names` is column index complete and in order 8372 return names.map( function (name, i) { 8373 return name === match[1] ? i : null; 8374 } ); 8375 8376 case 'title': 8377 // match by column title 8378 return titles.map( function (title, i) { 8379 return title === match[1] ? i : null; 8380 } ); 8381 8382 default: 8383 return []; 8384 } 8385 } 8386 8387 // Cell in the table body 8388 if ( s.nodeName && s._DT_CellIndex ) { 8389 return [ s._DT_CellIndex.column ]; 8390 } 8391 8392 // jQuery selector on the TH elements for the columns 8393 var jqResult = $( nodes ) 8394 .filter( s ) 8395 .map( function () { 8396 return _fnColumnsFromHeader( this ); // `nodes` is column index complete and in order 8397 } ) 8398 .toArray(); 8399 8400 if ( jqResult.length || ! s.nodeName ) { 8401 return jqResult; 8402 } 8403 8404 // Otherwise a node which might have a `dt-column` data attribute, or be 8405 // a child or such an element 8406 var host = $(s).closest('*[data-dt-column]'); 8407 return host.length ? 8408 [ host.data('dt-column') ] : 8409 []; 8410 }; 8411 8412 return _selector_run( 'column', selector, run, settings, opts ); 8413 }; 8414 8415 8416 var __setColumnVis = function ( settings, column, vis ) { 8417 var 8418 cols = settings.aoColumns, 8419 col = cols[ column ], 8420 data = settings.aoData, 8421 cells, i, ien, tr; 8422 8423 // Get 8424 if ( vis === undefined ) { 8425 return col.bVisible; 8426 } 8427 8428 // Set 8429 // No change 8430 if ( col.bVisible === vis ) { 8431 return false; 8432 } 8433 8434 if ( vis ) { 8435 // Insert column 8436 // Need to decide if we should use appendChild or insertBefore 8437 var insertBefore = _pluck(cols, 'bVisible').indexOf(true, column+1); 8438 8439 for ( i=0, ien=data.length ; i<ien ; i++ ) { 8440 if (data[i]) { 8441 tr = data[i].nTr; 8442 cells = data[i].anCells; 8443 8444 if ( tr ) { 8445 // insertBefore can act like appendChild if 2nd arg is null 8446 tr.insertBefore( cells[ column ], cells[ insertBefore ] || null ); 8447 } 8448 } 8449 } 8450 } 8451 else { 8452 // Remove column 8453 $( _pluck( settings.aoData, 'anCells', column ) ).detach(); 8454 } 8455 8456 // Common actions 8457 col.bVisible = vis; 8458 8459 _colGroup(settings); 8460 8461 return true; 8462 }; 8463 8464 8465 _api_register( 'columns()', function ( selector, opts ) { 8466 // argument shifting 8467 if ( selector === undefined ) { 8468 selector = ''; 8469 } 8470 else if ( $.isPlainObject( selector ) ) { 8471 opts = selector; 8472 selector = ''; 8473 } 8474 8475 opts = _selector_opts( opts ); 8476 8477 var inst = this.iterator( 'table', function ( settings ) { 8478 return __column_selector( settings, selector, opts ); 8479 }, 1 ); 8480 8481 // Want argument shifting here and in _row_selector? 8482 inst.selector.cols = selector; 8483 inst.selector.opts = opts; 8484 8485 return inst; 8486 } ); 8487 8488 _api_registerPlural( 'columns().header()', 'column().header()', function ( row ) { 8489 return this.iterator( 'column', function (settings, column) { 8490 return __column_header(settings, column, row); 8491 }, 1 ); 8492 } ); 8493 8494 _api_registerPlural( 'columns().footer()', 'column().footer()', function ( row ) { 8495 return this.iterator( 'column', function ( settings, column ) { 8496 var footer = settings.aoFooter; 8497 8498 if (! footer.length) { 8499 return null; 8500 } 8501 8502 return settings.aoFooter[row !== undefined ? row : 0][column].cell; 8503 }, 1 ); 8504 } ); 8505 8506 _api_registerPlural( 'columns().data()', 'column().data()', function () { 8507 return this.iterator( 'column-rows', __columnData, 1 ); 8508 } ); 8509 8510 _api_registerPlural( 'columns().render()', 'column().render()', function ( type ) { 8511 return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) { 8512 return __columnData( settings, column, i, j, rows, type ); 8513 }, 1 ); 8514 } ); 8515 8516 _api_registerPlural( 'columns().dataSrc()', 'column().dataSrc()', function () { 8517 return this.iterator( 'column', function ( settings, column ) { 8518 return settings.aoColumns[column].mData; 8519 }, 1 ); 8520 } ); 8521 8522 _api_registerPlural( 'columns().cache()', 'column().cache()', function ( type ) { 8523 return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) { 8524 return _pluck_order( settings.aoData, rows, 8525 type === 'search' ? '_aFilterData' : '_aSortData', column 8526 ); 8527 }, 1 ); 8528 } ); 8529 8530 _api_registerPlural( 'columns().init()', 'column().init()', function () { 8531 return this.iterator( 'column', function ( settings, column ) { 8532 return settings.aoColumns[column]; 8533 }, 1 ); 8534 } ); 8535 8536 _api_registerPlural( 'columns().nodes()', 'column().nodes()', function () { 8537 return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) { 8538 return _pluck_order( settings.aoData, rows, 'anCells', column ) ; 8539 }, 1 ); 8540 } ); 8541 8542 _api_registerPlural( 'columns().titles()', 'column().title()', function (title, row) { 8543 return this.iterator( 'column', function ( settings, column ) { 8544 // Argument shifting 8545 if (typeof title === 'number') { 8546 row = title; 8547 title = undefined; 8548 } 8549 8550 var span = $('span.dt-column-title', this.column(column).header(row)); 8551 8552 if (title !== undefined) { 8553 span.html(title); 8554 return this; 8555 } 8556 8557 return span.html(); 8558 }, 1 ); 8559 } ); 8560 8561 _api_registerPlural( 'columns().types()', 'column().type()', function () { 8562 return this.iterator( 'column', function ( settings, column ) { 8563 var type = settings.aoColumns[column].sType; 8564 8565 // If the type was invalidated, then resolve it. This actually does 8566 // all columns at the moment. Would only happen once if getting all 8567 // column's data types. 8568 if (! type) { 8569 _fnColumnTypes(settings); 8570 } 8571 8572 return type; 8573 }, 1 ); 8574 } ); 8575 8576 _api_registerPlural( 'columns().visible()', 'column().visible()', function ( vis, calc ) { 8577 var that = this; 8578 var changed = []; 8579 var ret = this.iterator( 'column', function ( settings, column ) { 8580 if ( vis === undefined ) { 8581 return settings.aoColumns[ column ].bVisible; 8582 } // else 8583 8584 if (__setColumnVis( settings, column, vis )) { 8585 changed.push(column); 8586 } 8587 } ); 8588 8589 // Group the column visibility changes 8590 if ( vis !== undefined ) { 8591 this.iterator( 'table', function ( settings ) { 8592 // Redraw the header after changes 8593 _fnDrawHead( settings, settings.aoHeader ); 8594 _fnDrawHead( settings, settings.aoFooter ); 8595 8596 // Update colspan for no records display. Child rows and extensions will use their own 8597 // listeners to do this - only need to update the empty table item here 8598 if ( ! settings.aiDisplay.length ) { 8599 $(settings.nTBody).find('td[colspan]').attr('colspan', _fnVisbleColumns(settings)); 8600 } 8601 8602 _fnSaveState( settings ); 8603 8604 // Second loop once the first is done for events 8605 that.iterator( 'column', function ( settings, column ) { 8606 if (changed.includes(column)) { 8607 _fnCallbackFire( settings, null, 'column-visibility', [settings, column, vis, calc] ); 8608 } 8609 } ); 8610 8611 if ( changed.length && (calc === undefined || calc) ) { 8612 that.columns.adjust(); 8613 } 8614 }); 8615 } 8616 8617 return ret; 8618 } ); 8619 8620 _api_registerPlural( 'columns().widths()', 'column().width()', function () { 8621 // Injects a fake row into the table for just a moment so the widths can 8622 // be read, regardless of colspan in the header and rows being present in 8623 // the body 8624 var columns = this.columns(':visible').count(); 8625 var row = $('<tr>').html('<td>' + Array(columns).join('</td><td>') + '</td>'); 8626 8627 $(this.table().body()).append(row); 8628 8629 var widths = row.children().map(function () { 8630 return $(this).outerWidth(); 8631 }); 8632 8633 row.remove(); 8634 8635 return this.iterator( 'column', function ( settings, column ) { 8636 var visIdx = _fnColumnIndexToVisible( settings, column ); 8637 8638 return visIdx !== null ? widths[visIdx] : 0; 8639 }, 1); 8640 } ); 8641 8642 _api_registerPlural( 'columns().indexes()', 'column().index()', function ( type ) { 8643 return this.iterator( 'column', function ( settings, column ) { 8644 return type === 'visible' ? 8645 _fnColumnIndexToVisible( settings, column ) : 8646 column; 8647 }, 1 ); 8648 } ); 8649 8650 _api_register( 'columns.adjust()', function () { 8651 return this.iterator( 'table', function ( settings ) { 8652 _fnAdjustColumnSizing( settings ); 8653 }, 1 ); 8654 } ); 8655 8656 _api_register( 'column.index()', function ( type, idx ) { 8657 if ( this.context.length !== 0 ) { 8658 var ctx = this.context[0]; 8659 8660 if ( type === 'fromVisible' || type === 'toData' ) { 8661 return _fnVisibleToColumnIndex( ctx, idx ); 8662 } 8663 else if ( type === 'fromData' || type === 'toVisible' ) { 8664 return _fnColumnIndexToVisible( ctx, idx ); 8665 } 8666 } 8667 } ); 8668 8669 _api_register( 'column()', function ( selector, opts ) { 8670 return _selector_first( this.columns( selector, opts ) ); 8671 } ); 8672 8673 var __cell_selector = function ( settings, selector, opts ) 8674 { 8675 var data = settings.aoData; 8676 var rows = _selector_row_indexes( settings, opts ); 8677 var cells = _removeEmpty( _pluck_order( data, rows, 'anCells' ) ); 8678 var allCells = $(_flatten( [], cells )); 8679 var row; 8680 var columns = settings.aoColumns.length; 8681 var a, i, ien, j, o, host; 8682 8683 var run = function ( s ) { 8684 var fnSelector = typeof s === 'function'; 8685 8686 if ( s === null || s === undefined || fnSelector ) { 8687 // All cells and function selectors 8688 a = []; 8689 8690 for ( i=0, ien=rows.length ; i<ien ; i++ ) { 8691 row = rows[i]; 8692 8693 for ( j=0 ; j<columns ; j++ ) { 8694 o = { 8695 row: row, 8696 column: j 8697 }; 8698 8699 if ( fnSelector ) { 8700 // Selector - function 8701 host = data[ row ]; 8702 8703 if ( s( o, _fnGetCellData(settings, row, j), host.anCells ? host.anCells[j] : null ) ) { 8704 a.push( o ); 8705 } 8706 } 8707 else { 8708 // Selector - all 8709 a.push( o ); 8710 } 8711 } 8712 } 8713 8714 return a; 8715 } 8716 8717 // Selector - index 8718 if ( $.isPlainObject( s ) ) { 8719 // Valid cell index and its in the array of selectable rows 8720 return s.column !== undefined && s.row !== undefined && rows.indexOf(s.row) !== -1 ? 8721 [s] : 8722 []; 8723 } 8724 8725 // Selector - jQuery filtered cells 8726 var jqResult = allCells 8727 .filter( s ) 8728 .map( function (i, el) { 8729 return { // use a new object, in case someone changes the values 8730 row: el._DT_CellIndex.row, 8731 column: el._DT_CellIndex.column 8732 }; 8733 } ) 8734 .toArray(); 8735 8736 if ( jqResult.length || ! s.nodeName ) { 8737 return jqResult; 8738 } 8739 8740 // Otherwise the selector is a node, and there is one last option - the 8741 // element might be a child of an element which has dt-row and dt-column 8742 // data attributes 8743 host = $(s).closest('*[data-dt-row]'); 8744 return host.length ? 8745 [ { 8746 row: host.data('dt-row'), 8747 column: host.data('dt-column') 8748 } ] : 8749 []; 8750 }; 8751 8752 return _selector_run( 'cell', selector, run, settings, opts ); 8753 }; 8754 8755 8756 8757 8758 _api_register( 'cells()', function ( rowSelector, columnSelector, opts ) { 8759 // Argument shifting 8760 if ( $.isPlainObject( rowSelector ) ) { 8761 // Indexes 8762 if ( rowSelector.row === undefined ) { 8763 // Selector options in first parameter 8764 opts = rowSelector; 8765 rowSelector = null; 8766 } 8767 else { 8768 // Cell index objects in first parameter 8769 opts = columnSelector; 8770 columnSelector = null; 8771 } 8772 } 8773 if ( $.isPlainObject( columnSelector ) ) { 8774 opts = columnSelector; 8775 columnSelector = null; 8776 } 8777 8778 // Cell selector 8779 if ( columnSelector === null || columnSelector === undefined ) { 8780 return this.iterator( 'table', function ( settings ) { 8781 return __cell_selector( settings, rowSelector, _selector_opts( opts ) ); 8782 } ); 8783 } 8784 8785 // The default built in options need to apply to row and columns 8786 var internalOpts = opts ? { 8787 page: opts.page, 8788 order: opts.order, 8789 search: opts.search 8790 } : {}; 8791 8792 // Row + column selector 8793 var columns = this.columns( columnSelector, internalOpts ); 8794 var rows = this.rows( rowSelector, internalOpts ); 8795 var i, ien, j, jen; 8796 8797 var cellsNoOpts = this.iterator( 'table', function ( settings, idx ) { 8798 var a = []; 8799 8800 for ( i=0, ien=rows[idx].length ; i<ien ; i++ ) { 8801 for ( j=0, jen=columns[idx].length ; j<jen ; j++ ) { 8802 a.push( { 8803 row: rows[idx][i], 8804 column: columns[idx][j] 8805 } ); 8806 } 8807 } 8808 8809 return a; 8810 }, 1 ); 8811 8812 // There is currently only one extension which uses a cell selector extension 8813 // It is a _major_ performance drag to run this if it isn't needed, so this is 8814 // an extension specific check at the moment 8815 var cells = opts && opts.selected ? 8816 this.cells( cellsNoOpts, opts ) : 8817 cellsNoOpts; 8818 8819 $.extend( cells.selector, { 8820 cols: columnSelector, 8821 rows: rowSelector, 8822 opts: opts 8823 } ); 8824 8825 return cells; 8826 } ); 8827 8828 8829 _api_registerPlural( 'cells().nodes()', 'cell().node()', function () { 8830 return this.iterator( 'cell', function ( settings, row, column ) { 8831 var data = settings.aoData[ row ]; 8832 8833 return data && data.anCells ? 8834 data.anCells[ column ] : 8835 undefined; 8836 }, 1 ); 8837 } ); 8838 8839 8840 _api_register( 'cells().data()', function () { 8841 return this.iterator( 'cell', function ( settings, row, column ) { 8842 return _fnGetCellData( settings, row, column ); 8843 }, 1 ); 8844 } ); 8845 8846 8847 _api_registerPlural( 'cells().cache()', 'cell().cache()', function ( type ) { 8848 type = type === 'search' ? '_aFilterData' : '_aSortData'; 8849 8850 return this.iterator( 'cell', function ( settings, row, column ) { 8851 return settings.aoData[ row ][ type ][ column ]; 8852 }, 1 ); 8853 } ); 8854 8855 8856 _api_registerPlural( 'cells().render()', 'cell().render()', function ( type ) { 8857 return this.iterator( 'cell', function ( settings, row, column ) { 8858 return _fnGetCellData( settings, row, column, type ); 8859 }, 1 ); 8860 } ); 8861 8862 8863 _api_registerPlural( 'cells().indexes()', 'cell().index()', function () { 8864 return this.iterator( 'cell', function ( settings, row, column ) { 8865 return { 8866 row: row, 8867 column: column, 8868 columnVisible: _fnColumnIndexToVisible( settings, column ) 8869 }; 8870 }, 1 ); 8871 } ); 8872 8873 8874 _api_registerPlural( 'cells().invalidate()', 'cell().invalidate()', function ( src ) { 8875 return this.iterator( 'cell', function ( settings, row, column ) { 8876 _fnInvalidate( settings, row, src, column ); 8877 } ); 8878 } ); 8879 8880 8881 8882 _api_register( 'cell()', function ( rowSelector, columnSelector, opts ) { 8883 return _selector_first( this.cells( rowSelector, columnSelector, opts ) ); 8884 } ); 8885 8886 8887 _api_register( 'cell().data()', function ( data ) { 8888 var ctx = this.context; 8889 var cell = this[0]; 8890 8891 if ( data === undefined ) { 8892 // Get 8893 return ctx.length && cell.length ? 8894 _fnGetCellData( ctx[0], cell[0].row, cell[0].column ) : 8895 undefined; 8896 } 8897 8898 // Set 8899 _fnSetCellData( ctx[0], cell[0].row, cell[0].column, data ); 8900 _fnInvalidate( ctx[0], cell[0].row, 'data', cell[0].column ); 8901 8902 return this; 8903 } ); 8904 8905 8906 8907 /** 8908 * Get current ordering (sorting) that has been applied to the table. 8909 * 8910 * @returns {array} 2D array containing the sorting information for the first 8911 * table in the current context. Each element in the parent array represents 8912 * a column being sorted upon (i.e. multi-sorting with two columns would have 8913 * 2 inner arrays). The inner arrays may have 2 or 3 elements. The first is 8914 * the column index that the sorting condition applies to, the second is the 8915 * direction of the sort (`desc` or `asc`) and, optionally, the third is the 8916 * index of the sorting order from the `column.sorting` initialisation array. 8917 *//** 8918 * Set the ordering for the table. 8919 * 8920 * @param {integer} order Column index to sort upon. 8921 * @param {string} direction Direction of the sort to be applied (`asc` or `desc`) 8922 * @returns {DataTables.Api} this 8923 *//** 8924 * Set the ordering for the table. 8925 * 8926 * @param {array} order 1D array of sorting information to be applied. 8927 * @param {array} [...] Optional additional sorting conditions 8928 * @returns {DataTables.Api} this 8929 *//** 8930 * Set the ordering for the table. 8931 * 8932 * @param {array} order 2D array of sorting information to be applied. 8933 * @returns {DataTables.Api} this 8934 */ 8935 _api_register( 'order()', function ( order, dir ) { 8936 var ctx = this.context; 8937 var args = Array.prototype.slice.call( arguments ); 8938 8939 if ( order === undefined ) { 8940 // get 8941 return ctx.length !== 0 ? 8942 ctx[0].aaSorting : 8943 undefined; 8944 } 8945 8946 // set 8947 if ( typeof order === 'number' ) { 8948 // Simple column / direction passed in 8949 order = [ [ order, dir ] ]; 8950 } 8951 else if ( args.length > 1 ) { 8952 // Arguments passed in (list of 1D arrays) 8953 order = args; 8954 } 8955 // otherwise a 2D array was passed in 8956 8957 return this.iterator( 'table', function ( settings ) { 8958 settings.aaSorting = Array.isArray(order) ? order.slice() : order; 8959 } ); 8960 } ); 8961 8962 8963 /** 8964 * Attach a sort listener to an element for a given column 8965 * 8966 * @param {node|jQuery|string} node Identifier for the element(s) to attach the 8967 * listener to. This can take the form of a single DOM node, a jQuery 8968 * collection of nodes or a jQuery selector which will identify the node(s). 8969 * @param {integer} column the column that a click on this node will sort on 8970 * @param {function} [callback] callback function when sort is run 8971 * @returns {DataTables.Api} this 8972 */ 8973 _api_register( 'order.listener()', function ( node, column, callback ) { 8974 return this.iterator( 'table', function ( settings ) { 8975 _fnSortAttachListener(settings, node, {}, column, callback); 8976 } ); 8977 } ); 8978 8979 8980 _api_register( 'order.fixed()', function ( set ) { 8981 if ( ! set ) { 8982 var ctx = this.context; 8983 var fixed = ctx.length ? 8984 ctx[0].aaSortingFixed : 8985 undefined; 8986 8987 return Array.isArray( fixed ) ? 8988 { pre: fixed } : 8989 fixed; 8990 } 8991 8992 return this.iterator( 'table', function ( settings ) { 8993 settings.aaSortingFixed = $.extend( true, {}, set ); 8994 } ); 8995 } ); 8996 8997 8998 // Order by the selected column(s) 8999 _api_register( [ 9000 'columns().order()', 9001 'column().order()' 9002 ], function ( dir ) { 9003 var that = this; 9004 9005 if ( ! dir ) { 9006 return this.iterator( 'column', function ( settings, idx ) { 9007 var sort = _fnSortFlatten( settings ); 9008 9009 for ( var i=0, ien=sort.length ; i<ien ; i++ ) { 9010 if ( sort[i].col === idx ) { 9011 return sort[i].dir; 9012 } 9013 } 9014 9015 return null; 9016 }, 1 ); 9017 } 9018 else { 9019 return this.iterator( 'table', function ( settings, i ) { 9020 settings.aaSorting = that[i].map( function (col) { 9021 return [ col, dir ]; 9022 } ); 9023 } ); 9024 } 9025 } ); 9026 9027 _api_registerPlural('columns().orderable()', 'column().orderable()', function ( directions ) { 9028 return this.iterator( 'column', function ( settings, idx ) { 9029 var col = settings.aoColumns[idx]; 9030 9031 return directions ? 9032 col.asSorting : 9033 col.bSortable; 9034 }, 1 ); 9035 } ); 9036 9037 9038 _api_register( 'processing()', function ( show ) { 9039 return this.iterator( 'table', function ( ctx ) { 9040 _fnProcessingDisplay( ctx, show ); 9041 } ); 9042 } ); 9043 9044 9045 _api_register( 'search()', function ( input, regex, smart, caseInsen ) { 9046 var ctx = this.context; 9047 9048 if ( input === undefined ) { 9049 // get 9050 return ctx.length !== 0 ? 9051 ctx[0].oPreviousSearch.search : 9052 undefined; 9053 } 9054 9055 // set 9056 return this.iterator( 'table', function ( settings ) { 9057 if ( ! settings.oFeatures.bFilter ) { 9058 return; 9059 } 9060 9061 if (typeof regex === 'object') { 9062 // New style options to pass to the search builder 9063 _fnFilterComplete( settings, $.extend( settings.oPreviousSearch, regex, { 9064 search: input 9065 } ) ); 9066 } 9067 else { 9068 // Compat for the old options 9069 _fnFilterComplete( settings, $.extend( settings.oPreviousSearch, { 9070 search: input, 9071 regex: regex === null ? false : regex, 9072 smart: smart === null ? true : smart, 9073 caseInsensitive: caseInsen === null ? true : caseInsen 9074 } ) ); 9075 } 9076 } ); 9077 } ); 9078 9079 _api_register( 'search.fixed()', function ( name, search ) { 9080 var ret = this.iterator( true, 'table', function ( settings ) { 9081 var fixed = settings.searchFixed; 9082 9083 if (! name) { 9084 return Object.keys(fixed) 9085 } 9086 else if (search === undefined) { 9087 return fixed[name]; 9088 } 9089 else if (search === null) { 9090 delete fixed[name]; 9091 } 9092 else { 9093 fixed[name] = search; 9094 } 9095 9096 return this; 9097 } ); 9098 9099 return name !== undefined && search === undefined 9100 ? ret[0] 9101 : ret; 9102 } ); 9103 9104 _api_registerPlural( 9105 'columns().search()', 9106 'column().search()', 9107 function ( input, regex, smart, caseInsen ) { 9108 return this.iterator( 'column', function ( settings, column ) { 9109 var preSearch = settings.aoPreSearchCols; 9110 9111 if ( input === undefined ) { 9112 // get 9113 return preSearch[ column ].search; 9114 } 9115 9116 // set 9117 if ( ! settings.oFeatures.bFilter ) { 9118 return; 9119 } 9120 9121 if (typeof regex === 'object') { 9122 // New style options to pass to the search builder 9123 $.extend( preSearch[ column ], regex, { 9124 search: input 9125 } ); 9126 } 9127 else { 9128 // Old style (with not all options available) 9129 $.extend( preSearch[ column ], { 9130 search: input, 9131 regex: regex === null ? false : regex, 9132 smart: smart === null ? true : smart, 9133 caseInsensitive: caseInsen === null ? true : caseInsen 9134 } ); 9135 } 9136 9137 _fnFilterComplete( settings, settings.oPreviousSearch ); 9138 } ); 9139 } 9140 ); 9141 9142 _api_register([ 9143 'columns().search.fixed()', 9144 'column().search.fixed()' 9145 ], 9146 function ( name, search ) { 9147 var ret = this.iterator( true, 'column', function ( settings, colIdx ) { 9148 var fixed = settings.aoColumns[colIdx].searchFixed; 9149 9150 if (! name) { 9151 return Object.keys(fixed) 9152 } 9153 else if (search === undefined) { 9154 return fixed[name]; 9155 } 9156 else if (search === null) { 9157 delete fixed[name]; 9158 } 9159 else { 9160 fixed[name] = search; 9161 } 9162 9163 return this; 9164 } ); 9165 9166 return name !== undefined && search === undefined 9167 ? ret[0] 9168 : ret; 9169 } 9170 ); 9171 /* 9172 * State API methods 9173 */ 9174 9175 _api_register( 'state()', function ( set, ignoreTime ) { 9176 // getter 9177 if ( ! set ) { 9178 return this.context.length ? 9179 this.context[0].oSavedState : 9180 null; 9181 } 9182 9183 var setMutate = $.extend( true, {}, set ); 9184 9185 // setter 9186 return this.iterator( 'table', function ( settings ) { 9187 if ( ignoreTime !== false ) { 9188 setMutate.time = +new Date() + 100; 9189 } 9190 9191 _fnImplementState( settings, setMutate, function(){} ); 9192 } ); 9193 } ); 9194 9195 9196 _api_register( 'state.clear()', function () { 9197 return this.iterator( 'table', function ( settings ) { 9198 // Save an empty object 9199 settings.fnStateSaveCallback.call( settings.oInstance, settings, {} ); 9200 } ); 9201 } ); 9202 9203 9204 _api_register( 'state.loaded()', function () { 9205 return this.context.length ? 9206 this.context[0].oLoadedState : 9207 null; 9208 } ); 9209 9210 9211 _api_register( 'state.save()', function () { 9212 return this.iterator( 'table', function ( settings ) { 9213 _fnSaveState( settings ); 9214 } ); 9215 } ); 9216 9217 /** 9218 * Set the jQuery or window object to be used by DataTables 9219 * 9220 * @param {*} module Library / container object 9221 * @param {string} [type] Library or container type `lib`, `win` or `datetime`. 9222 * If not provided, automatic detection is attempted. 9223 */ 9224 DataTable.use = function (module, type) { 9225 if (type === 'lib' || module.fn) { 9226 $ = module; 9227 } 9228 else if (type == 'win' || module.document) { 9229 window = module; 9230 document = module.document; 9231 } 9232 else if (type === 'datetime' || module.type === 'DateTime') { 9233 DataTable.DateTime = module; 9234 } 9235 } 9236 9237 /** 9238 * CommonJS factory function pass through. This will check if the arguments 9239 * given are a window object or a jQuery object. If so they are set 9240 * accordingly. 9241 * @param {*} root Window 9242 * @param {*} jq jQUery 9243 * @returns {boolean} Indicator 9244 */ 9245 DataTable.factory = function (root, jq) { 9246 var is = false; 9247 9248 // Test if the first parameter is a window object 9249 if (root && root.document) { 9250 window = root; 9251 document = root.document; 9252 } 9253 9254 // Test if the second parameter is a jQuery object 9255 if (jq && jq.fn && jq.fn.jquery) { 9256 $ = jq; 9257 is = true; 9258 } 9259 9260 return is; 9261 } 9262 9263 /** 9264 * Provide a common method for plug-ins to check the version of DataTables being 9265 * used, in order to ensure compatibility. 9266 * 9267 * @param {string} version Version string to check for, in the format "X.Y.Z". 9268 * Note that the formats "X" and "X.Y" are also acceptable. 9269 * @param {string} [version2=current DataTables version] As above, but optional. 9270 * If not given the current DataTables version will be used. 9271 * @returns {boolean} true if this version of DataTables is greater or equal to 9272 * the required version, or false if this version of DataTales is not 9273 * suitable 9274 * @static 9275 * @dtopt API-Static 9276 * 9277 * @example 9278 * alert( $.fn.dataTable.versionCheck( '1.9.0' ) ); 9279 */ 9280 DataTable.versionCheck = function( version, version2 ) 9281 { 9282 var aThis = version2 ? 9283 version2.split('.') : 9284 DataTable.version.split('.'); 9285 var aThat = version.split('.'); 9286 var iThis, iThat; 9287 9288 for ( var i=0, iLen=aThat.length ; i<iLen ; i++ ) { 9289 iThis = parseInt( aThis[i], 10 ) || 0; 9290 iThat = parseInt( aThat[i], 10 ) || 0; 9291 9292 // Parts are the same, keep comparing 9293 if (iThis === iThat) { 9294 continue; 9295 } 9296 9297 // Parts are different, return immediately 9298 return iThis > iThat; 9299 } 9300 9301 return true; 9302 }; 9303 9304 9305 /** 9306 * Check if a `<table>` node is a DataTable table already or not. 9307 * 9308 * @param {node|jquery|string} table Table node, jQuery object or jQuery 9309 * selector for the table to test. Note that if more than more than one 9310 * table is passed on, only the first will be checked 9311 * @returns {boolean} true the table given is a DataTable, or false otherwise 9312 * @static 9313 * @dtopt API-Static 9314 * 9315 * @example 9316 * if ( ! $.fn.DataTable.isDataTable( '#example' ) ) { 9317 * $('#example').dataTable(); 9318 * } 9319 */ 9320 DataTable.isDataTable = function ( table ) 9321 { 9322 var t = $(table).get(0); 9323 var is = false; 9324 9325 if ( table instanceof DataTable.Api ) { 9326 return true; 9327 } 9328 9329 $.each( DataTable.settings, function (i, o) { 9330 var head = o.nScrollHead ? $('table', o.nScrollHead)[0] : null; 9331 var foot = o.nScrollFoot ? $('table', o.nScrollFoot)[0] : null; 9332 9333 if ( o.nTable === t || head === t || foot === t ) { 9334 is = true; 9335 } 9336 } ); 9337 9338 return is; 9339 }; 9340 9341 9342 /** 9343 * Get all DataTable tables that have been initialised - optionally you can 9344 * select to get only currently visible tables. 9345 * 9346 * @param {boolean} [visible=false] Flag to indicate if you want all (default) 9347 * or visible tables only. 9348 * @returns {array} Array of `table` nodes (not DataTable instances) which are 9349 * DataTables 9350 * @static 9351 * @dtopt API-Static 9352 * 9353 * @example 9354 * $.each( $.fn.dataTable.tables(true), function () { 9355 * $(table).DataTable().columns.adjust(); 9356 * } ); 9357 */ 9358 DataTable.tables = function ( visible ) 9359 { 9360 var api = false; 9361 9362 if ( $.isPlainObject( visible ) ) { 9363 api = visible.api; 9364 visible = visible.visible; 9365 } 9366 9367 var a = DataTable.settings 9368 .filter( function (o) { 9369 return !visible || (visible && $(o.nTable).is(':visible')) 9370 ? true 9371 : false; 9372 } ) 9373 .map( function (o) { 9374 return o.nTable; 9375 }); 9376 9377 return api ? 9378 new _Api( a ) : 9379 a; 9380 }; 9381 9382 9383 /** 9384 * Convert from camel case parameters to Hungarian notation. This is made public 9385 * for the extensions to provide the same ability as DataTables core to accept 9386 * either the 1.9 style Hungarian notation, or the 1.10+ style camelCase 9387 * parameters. 9388 * 9389 * @param {object} src The model object which holds all parameters that can be 9390 * mapped. 9391 * @param {object} user The object to convert from camel case to Hungarian. 9392 * @param {boolean} force When set to `true`, properties which already have a 9393 * Hungarian value in the `user` object will be overwritten. Otherwise they 9394 * won't be. 9395 */ 9396 DataTable.camelToHungarian = _fnCamelToHungarian; 9397 9398 9399 9400 /** 9401 * 9402 */ 9403 _api_register( '$()', function ( selector, opts ) { 9404 var 9405 rows = this.rows( opts ).nodes(), // Get all rows 9406 jqRows = $(rows); 9407 9408 return $( [].concat( 9409 jqRows.filter( selector ).toArray(), 9410 jqRows.find( selector ).toArray() 9411 ) ); 9412 } ); 9413 9414 9415 // jQuery functions to operate on the tables 9416 $.each( [ 'on', 'one', 'off' ], function (i, key) { 9417 _api_register( key+'()', function ( /* event, handler */ ) { 9418 var args = Array.prototype.slice.call(arguments); 9419 9420 // Add the `dt` namespace automatically if it isn't already present 9421 args[0] = args[0].split( /\s/ ).map( function ( e ) { 9422 return ! e.match(/\.dt\b/) ? 9423 e+'.dt' : 9424 e; 9425 } ).join( ' ' ); 9426 9427 var inst = $( this.tables().nodes() ); 9428 inst[key].apply( inst, args ); 9429 return this; 9430 } ); 9431 } ); 9432 9433 9434 _api_register( 'clear()', function () { 9435 return this.iterator( 'table', function ( settings ) { 9436 _fnClearTable( settings ); 9437 } ); 9438 } ); 9439 9440 9441 _api_register( 'error()', function (msg) { 9442 return this.iterator( 'table', function ( settings ) { 9443 _fnLog( settings, 0, msg ); 9444 } ); 9445 } ); 9446 9447 9448 _api_register( 'settings()', function () { 9449 return new _Api( this.context, this.context ); 9450 } ); 9451 9452 9453 _api_register( 'init()', function () { 9454 var ctx = this.context; 9455 return ctx.length ? ctx[0].oInit : null; 9456 } ); 9457 9458 9459 _api_register( 'data()', function () { 9460 return this.iterator( 'table', function ( settings ) { 9461 return _pluck( settings.aoData, '_aData' ); 9462 } ).flatten(); 9463 } ); 9464 9465 9466 _api_register( 'trigger()', function ( name, args, bubbles ) { 9467 return this.iterator( 'table', function ( settings ) { 9468 return _fnCallbackFire( settings, null, name, args, bubbles ); 9469 } ).flatten(); 9470 } ); 9471 9472 9473 _api_register( 'ready()', function ( fn ) { 9474 var ctx = this.context; 9475 9476 // Get status of first table 9477 if (! fn) { 9478 return ctx.length 9479 ? (ctx[0]._bInitComplete || false) 9480 : null; 9481 } 9482 9483 // Function to run either once the table becomes ready or 9484 // immediately if it is already ready. 9485 return this.tables().every(function () { 9486 if (this.context[0]._bInitComplete) { 9487 fn.call(this); 9488 } 9489 else { 9490 this.on('init', function () { 9491 fn.call(this); 9492 }); 9493 } 9494 } ); 9495 } ); 9496 9497 9498 _api_register( 'destroy()', function ( remove ) { 9499 remove = remove || false; 9500 9501 return this.iterator( 'table', function ( settings ) { 9502 var classes = settings.oClasses; 9503 var table = settings.nTable; 9504 var tbody = settings.nTBody; 9505 var thead = settings.nTHead; 9506 var tfoot = settings.nTFoot; 9507 var jqTable = $(table); 9508 var jqTbody = $(tbody); 9509 var jqWrapper = $(settings.nTableWrapper); 9510 var rows = settings.aoData.map( function (r) { return r ? r.nTr : null; } ); 9511 var orderClasses = classes.order; 9512 9513 // Flag to note that the table is currently being destroyed - no action 9514 // should be taken 9515 settings.bDestroying = true; 9516 9517 // Fire off the destroy callbacks for plug-ins etc 9518 _fnCallbackFire( settings, "aoDestroyCallback", "destroy", [settings], true ); 9519 9520 // If not being removed from the document, make all columns visible 9521 if ( ! remove ) { 9522 new _Api( settings ).columns().visible( true ); 9523 } 9524 9525 // Blitz all `DT` namespaced events (these are internal events, the 9526 // lowercase, `dt` events are user subscribed and they are responsible 9527 // for removing them 9528 jqWrapper.off('.DT').find(':not(tbody *)').off('.DT'); 9529 $(window).off('.DT-'+settings.sInstance); 9530 9531 // When scrolling we had to break the table up - restore it 9532 if ( table != thead.parentNode ) { 9533 jqTable.children('thead').detach(); 9534 jqTable.append( thead ); 9535 } 9536 9537 if ( tfoot && table != tfoot.parentNode ) { 9538 jqTable.children('tfoot').detach(); 9539 jqTable.append( tfoot ); 9540 } 9541 9542 settings.colgroup.remove(); 9543 9544 settings.aaSorting = []; 9545 settings.aaSortingFixed = []; 9546 _fnSortingClasses( settings ); 9547 9548 $('th, td', thead) 9549 .removeClass( 9550 orderClasses.canAsc + ' ' + 9551 orderClasses.canDesc + ' ' + 9552 orderClasses.isAsc + ' ' + 9553 orderClasses.isDesc 9554 ) 9555 .css('width', ''); 9556 9557 // Add the TR elements back into the table in their original order 9558 jqTbody.children().detach(); 9559 jqTbody.append( rows ); 9560 9561 var orig = settings.nTableWrapper.parentNode; 9562 var insertBefore = settings.nTableWrapper.nextSibling; 9563 9564 // Remove the DataTables generated nodes, events and classes 9565 var removedMethod = remove ? 'remove' : 'detach'; 9566 jqTable[ removedMethod ](); 9567 jqWrapper[ removedMethod ](); 9568 9569 // If we need to reattach the table to the document 9570 if ( ! remove && orig ) { 9571 // insertBefore acts like appendChild if !arg[1] 9572 orig.insertBefore( table, insertBefore ); 9573 9574 // Restore the width of the original table - was read from the style property, 9575 // so we can restore directly to that 9576 jqTable 9577 .css( 'width', settings.sDestroyWidth ) 9578 .removeClass( classes.table ); 9579 } 9580 9581 /* Remove the settings object from the settings array */ 9582 var idx = DataTable.settings.indexOf(settings); 9583 if ( idx !== -1 ) { 9584 DataTable.settings.splice( idx, 1 ); 9585 } 9586 } ); 9587 } ); 9588 9589 9590 // Add the `every()` method for rows, columns and cells in a compact form 9591 $.each( [ 'column', 'row', 'cell' ], function ( i, type ) { 9592 _api_register( type+'s().every()', function ( fn ) { 9593 var opts = this.selector.opts; 9594 var api = this; 9595 var inst; 9596 var counter = 0; 9597 9598 return this.iterator( 'every', function ( settings, selectedIdx, tableIdx ) { 9599 inst = api[ type ](selectedIdx, opts); 9600 9601 if (type === 'cell') { 9602 fn.call(inst, inst[0][0].row, inst[0][0].column, tableIdx, counter); 9603 } 9604 else { 9605 fn.call(inst, selectedIdx, tableIdx, counter); 9606 } 9607 9608 counter++; 9609 } ); 9610 } ); 9611 } ); 9612 9613 9614 // i18n method for extensions to be able to use the language object from the 9615 // DataTable 9616 _api_register( 'i18n()', function ( token, def, plural ) { 9617 var ctx = this.context[0]; 9618 var resolved = _fnGetObjectDataFn( token )( ctx.oLanguage ); 9619 9620 if ( resolved === undefined ) { 9621 resolved = def; 9622 } 9623 9624 if ( $.isPlainObject( resolved ) ) { 9625 resolved = plural !== undefined && resolved[ plural ] !== undefined ? 9626 resolved[ plural ] : 9627 resolved._; 9628 } 9629 9630 return typeof resolved === 'string' 9631 ? resolved.replace( '%d', plural ) // nb: plural might be undefined, 9632 : resolved; 9633 } ); 9634 9635 /** 9636 * Version string for plug-ins to check compatibility. Allowed format is 9637 * `a.b.c-d` where: a:int, b:int, c:int, d:string(dev|beta|alpha). `d` is used 9638 * only for non-release builds. See https://semver.org/ for more information. 9639 * @member 9640 * @type string 9641 * @default Version number 9642 */ 9643 DataTable.version = "2.0.8"; 9644 9645 /** 9646 * Private data store, containing all of the settings objects that are 9647 * created for the tables on a given page. 9648 * 9649 * Note that the `DataTable.settings` object is aliased to 9650 * `jQuery.fn.dataTableExt` through which it may be accessed and 9651 * manipulated, or `jQuery.fn.dataTable.settings`. 9652 * @member 9653 * @type array 9654 * @default [] 9655 * @private 9656 */ 9657 DataTable.settings = []; 9658 9659 /** 9660 * Object models container, for the various models that DataTables has 9661 * available to it. These models define the objects that are used to hold 9662 * the active state and configuration of the table. 9663 * @namespace 9664 */ 9665 DataTable.models = {}; 9666 9667 9668 9669 /** 9670 * Template object for the way in which DataTables holds information about 9671 * search information for the global filter and individual column filters. 9672 * @namespace 9673 */ 9674 DataTable.models.oSearch = { 9675 /** 9676 * Flag to indicate if the filtering should be case insensitive or not 9677 */ 9678 "caseInsensitive": true, 9679 9680 /** 9681 * Applied search term 9682 */ 9683 "search": "", 9684 9685 /** 9686 * Flag to indicate if the search term should be interpreted as a 9687 * regular expression (true) or not (false) and therefore and special 9688 * regex characters escaped. 9689 */ 9690 "regex": false, 9691 9692 /** 9693 * Flag to indicate if DataTables is to use its smart filtering or not. 9694 */ 9695 "smart": true, 9696 9697 /** 9698 * Flag to indicate if DataTables should only trigger a search when 9699 * the return key is pressed. 9700 */ 9701 "return": false 9702 }; 9703 9704 9705 9706 9707 /** 9708 * Template object for the way in which DataTables holds information about 9709 * each individual row. This is the object format used for the settings 9710 * aoData array. 9711 * @namespace 9712 */ 9713 DataTable.models.oRow = { 9714 /** 9715 * TR element for the row 9716 */ 9717 "nTr": null, 9718 9719 /** 9720 * Array of TD elements for each row. This is null until the row has been 9721 * created. 9722 */ 9723 "anCells": null, 9724 9725 /** 9726 * Data object from the original data source for the row. This is either 9727 * an array if using the traditional form of DataTables, or an object if 9728 * using mData options. The exact type will depend on the passed in 9729 * data from the data source, or will be an array if using DOM a data 9730 * source. 9731 */ 9732 "_aData": [], 9733 9734 /** 9735 * Sorting data cache - this array is ostensibly the same length as the 9736 * number of columns (although each index is generated only as it is 9737 * needed), and holds the data that is used for sorting each column in the 9738 * row. We do this cache generation at the start of the sort in order that 9739 * the formatting of the sort data need be done only once for each cell 9740 * per sort. This array should not be read from or written to by anything 9741 * other than the master sorting methods. 9742 */ 9743 "_aSortData": null, 9744 9745 /** 9746 * Per cell filtering data cache. As per the sort data cache, used to 9747 * increase the performance of the filtering in DataTables 9748 */ 9749 "_aFilterData": null, 9750 9751 /** 9752 * Filtering data cache. This is the same as the cell filtering cache, but 9753 * in this case a string rather than an array. This is easily computed with 9754 * a join on `_aFilterData`, but is provided as a cache so the join isn't 9755 * needed on every search (memory traded for performance) 9756 */ 9757 "_sFilterRow": null, 9758 9759 /** 9760 * Denote if the original data source was from the DOM, or the data source 9761 * object. This is used for invalidating data, so DataTables can 9762 * automatically read data from the original source, unless uninstructed 9763 * otherwise. 9764 */ 9765 "src": null, 9766 9767 /** 9768 * Index in the aoData array. This saves an indexOf lookup when we have the 9769 * object, but want to know the index 9770 */ 9771 "idx": -1, 9772 9773 /** 9774 * Cached display value 9775 */ 9776 displayData: null 9777 }; 9778 9779 9780 /** 9781 * Template object for the column information object in DataTables. This object 9782 * is held in the settings aoColumns array and contains all the information that 9783 * DataTables needs about each individual column. 9784 * 9785 * Note that this object is related to {@link DataTable.defaults.column} 9786 * but this one is the internal data store for DataTables's cache of columns. 9787 * It should NOT be manipulated outside of DataTables. Any configuration should 9788 * be done through the initialisation options. 9789 * @namespace 9790 */ 9791 DataTable.models.oColumn = { 9792 /** 9793 * Column index. 9794 */ 9795 "idx": null, 9796 9797 /** 9798 * A list of the columns that sorting should occur on when this column 9799 * is sorted. That this property is an array allows multi-column sorting 9800 * to be defined for a column (for example first name / last name columns 9801 * would benefit from this). The values are integers pointing to the 9802 * columns to be sorted on (typically it will be a single integer pointing 9803 * at itself, but that doesn't need to be the case). 9804 */ 9805 "aDataSort": null, 9806 9807 /** 9808 * Define the sorting directions that are applied to the column, in sequence 9809 * as the column is repeatedly sorted upon - i.e. the first value is used 9810 * as the sorting direction when the column if first sorted (clicked on). 9811 * Sort it again (click again) and it will move on to the next index. 9812 * Repeat until loop. 9813 */ 9814 "asSorting": null, 9815 9816 /** 9817 * Flag to indicate if the column is searchable, and thus should be included 9818 * in the filtering or not. 9819 */ 9820 "bSearchable": null, 9821 9822 /** 9823 * Flag to indicate if the column is sortable or not. 9824 */ 9825 "bSortable": null, 9826 9827 /** 9828 * Flag to indicate if the column is currently visible in the table or not 9829 */ 9830 "bVisible": null, 9831 9832 /** 9833 * Store for manual type assignment using the `column.type` option. This 9834 * is held in store so we can manipulate the column's `sType` property. 9835 */ 9836 "_sManualType": null, 9837 9838 /** 9839 * Flag to indicate if HTML5 data attributes should be used as the data 9840 * source for filtering or sorting. True is either are. 9841 */ 9842 "_bAttrSrc": false, 9843 9844 /** 9845 * Developer definable function that is called whenever a cell is created (Ajax source, 9846 * etc) or processed for input (DOM source). This can be used as a compliment to mRender 9847 * allowing you to modify the DOM element (add background colour for example) when the 9848 * element is available. 9849 */ 9850 "fnCreatedCell": null, 9851 9852 /** 9853 * Function to get data from a cell in a column. You should <b>never</b> 9854 * access data directly through _aData internally in DataTables - always use 9855 * the method attached to this property. It allows mData to function as 9856 * required. This function is automatically assigned by the column 9857 * initialisation method 9858 */ 9859 "fnGetData": null, 9860 9861 /** 9862 * Function to set data for a cell in the column. You should <b>never</b> 9863 * set the data directly to _aData internally in DataTables - always use 9864 * this method. It allows mData to function as required. This function 9865 * is automatically assigned by the column initialisation method 9866 */ 9867 "fnSetData": null, 9868 9869 /** 9870 * Property to read the value for the cells in the column from the data 9871 * source array / object. If null, then the default content is used, if a 9872 * function is given then the return from the function is used. 9873 */ 9874 "mData": null, 9875 9876 /** 9877 * Partner property to mData which is used (only when defined) to get 9878 * the data - i.e. it is basically the same as mData, but without the 9879 * 'set' option, and also the data fed to it is the result from mData. 9880 * This is the rendering method to match the data method of mData. 9881 */ 9882 "mRender": null, 9883 9884 /** 9885 * The class to apply to all TD elements in the table's TBODY for the column 9886 */ 9887 "sClass": null, 9888 9889 /** 9890 * When DataTables calculates the column widths to assign to each column, 9891 * it finds the longest string in each column and then constructs a 9892 * temporary table and reads the widths from that. The problem with this 9893 * is that "mmm" is much wider then "iiii", but the latter is a longer 9894 * string - thus the calculation can go wrong (doing it properly and putting 9895 * it into an DOM object and measuring that is horribly(!) slow). Thus as 9896 * a "work around" we provide this option. It will append its value to the 9897 * text that is found to be the longest string for the column - i.e. padding. 9898 */ 9899 "sContentPadding": null, 9900 9901 /** 9902 * Allows a default value to be given for a column's data, and will be used 9903 * whenever a null data source is encountered (this can be because mData 9904 * is set to null, or because the data source itself is null). 9905 */ 9906 "sDefaultContent": null, 9907 9908 /** 9909 * Name for the column, allowing reference to the column by name as well as 9910 * by index (needs a lookup to work by name). 9911 */ 9912 "sName": null, 9913 9914 /** 9915 * Custom sorting data type - defines which of the available plug-ins in 9916 * afnSortData the custom sorting will use - if any is defined. 9917 */ 9918 "sSortDataType": 'std', 9919 9920 /** 9921 * Class to be applied to the header element when sorting on this column 9922 */ 9923 "sSortingClass": null, 9924 9925 /** 9926 * Title of the column - what is seen in the TH element (nTh). 9927 */ 9928 "sTitle": null, 9929 9930 /** 9931 * Column sorting and filtering type 9932 */ 9933 "sType": null, 9934 9935 /** 9936 * Width of the column 9937 */ 9938 "sWidth": null, 9939 9940 /** 9941 * Width of the column when it was first "encountered" 9942 */ 9943 "sWidthOrig": null, 9944 9945 /** Cached string which is the longest in the column */ 9946 maxLenString: null, 9947 9948 /** 9949 * Store for named searches 9950 */ 9951 searchFixed: null 9952 }; 9953 9954 9955 /* 9956 * Developer note: The properties of the object below are given in Hungarian 9957 * notation, that was used as the interface for DataTables prior to v1.10, however 9958 * from v1.10 onwards the primary interface is camel case. In order to avoid 9959 * breaking backwards compatibility utterly with this change, the Hungarian 9960 * version is still, internally the primary interface, but is is not documented 9961 * - hence the @name tags in each doc comment. This allows a Javascript function 9962 * to create a map from Hungarian notation to camel case (going the other direction 9963 * would require each property to be listed, which would add around 3K to the size 9964 * of DataTables, while this method is about a 0.5K hit). 9965 * 9966 * Ultimately this does pave the way for Hungarian notation to be dropped 9967 * completely, but that is a massive amount of work and will break current 9968 * installs (therefore is on-hold until v2). 9969 */ 9970 9971 /** 9972 * Initialisation options that can be given to DataTables at initialisation 9973 * time. 9974 * @namespace 9975 */ 9976 DataTable.defaults = { 9977 /** 9978 * An array of data to use for the table, passed in at initialisation which 9979 * will be used in preference to any data which is already in the DOM. This is 9980 * particularly useful for constructing tables purely in Javascript, for 9981 * example with a custom Ajax call. 9982 */ 9983 "aaData": null, 9984 9985 9986 /** 9987 * If ordering is enabled, then DataTables will perform a first pass sort on 9988 * initialisation. You can define which column(s) the sort is performed 9989 * upon, and the sorting direction, with this variable. The `sorting` array 9990 * should contain an array for each column to be sorted initially containing 9991 * the column's index and a direction string ('asc' or 'desc'). 9992 */ 9993 "aaSorting": [[0,'asc']], 9994 9995 9996 /** 9997 * This parameter is basically identical to the `sorting` parameter, but 9998 * cannot be overridden by user interaction with the table. What this means 9999 * is that you could have a column (visible or hidden) which the sorting 10000 * will always be forced on first - any sorting after that (from the user) 10001 * will then be performed as required. This can be useful for grouping rows 10002 * together. 10003 */ 10004 "aaSortingFixed": [], 10005 10006 10007 /** 10008 * DataTables can be instructed to load data to display in the table from a 10009 * Ajax source. This option defines how that Ajax call is made and where to. 10010 * 10011 * The `ajax` property has three different modes of operation, depending on 10012 * how it is defined. These are: 10013 * 10014 * * `string` - Set the URL from where the data should be loaded from. 10015 * * `object` - Define properties for `jQuery.ajax`. 10016 * * `function` - Custom data get function 10017 * 10018 * `string` 10019 * -------- 10020 * 10021 * As a string, the `ajax` property simply defines the URL from which 10022 * DataTables will load data. 10023 * 10024 * `object` 10025 * -------- 10026 * 10027 * As an object, the parameters in the object are passed to 10028 * [jQuery.ajax](https://api.jquery.com/jQuery.ajax/) allowing fine control 10029 * of the Ajax request. DataTables has a number of default parameters which 10030 * you can override using this option. Please refer to the jQuery 10031 * documentation for a full description of the options available, although 10032 * the following parameters provide additional options in DataTables or 10033 * require special consideration: 10034 * 10035 * * `data` - As with jQuery, `data` can be provided as an object, but it 10036 * can also be used as a function to manipulate the data DataTables sends 10037 * to the server. The function takes a single parameter, an object of 10038 * parameters with the values that DataTables has readied for sending. An 10039 * object may be returned which will be merged into the DataTables 10040 * defaults, or you can add the items to the object that was passed in and 10041 * not return anything from the function. This supersedes `fnServerParams` 10042 * from DataTables 1.9-. 10043 * 10044 * * `dataSrc` - By default DataTables will look for the property `data` (or 10045 * `aaData` for compatibility with DataTables 1.9-) when obtaining data 10046 * from an Ajax source or for server-side processing - this parameter 10047 * allows that property to be changed. You can use Javascript dotted 10048 * object notation to get a data source for multiple levels of nesting, or 10049 * it my be used as a function. As a function it takes a single parameter, 10050 * the JSON returned from the server, which can be manipulated as 10051 * required, with the returned value being that used by DataTables as the 10052 * data source for the table. 10053 * 10054 * * `success` - Should not be overridden it is used internally in 10055 * DataTables. To manipulate / transform the data returned by the server 10056 * use `ajax.dataSrc`, or use `ajax` as a function (see below). 10057 * 10058 * `function` 10059 * ---------- 10060 * 10061 * As a function, making the Ajax call is left up to yourself allowing 10062 * complete control of the Ajax request. Indeed, if desired, a method other 10063 * than Ajax could be used to obtain the required data, such as Web storage 10064 * or an AIR database. 10065 * 10066 * The function is given four parameters and no return is required. The 10067 * parameters are: 10068 * 10069 * 1. _object_ - Data to send to the server 10070 * 2. _function_ - Callback function that must be executed when the required 10071 * data has been obtained. That data should be passed into the callback 10072 * as the only parameter 10073 * 3. _object_ - DataTables settings object for the table 10074 */ 10075 "ajax": null, 10076 10077 10078 /** 10079 * This parameter allows you to readily specify the entries in the length drop 10080 * down menu that DataTables shows when pagination is enabled. It can be 10081 * either a 1D array of options which will be used for both the displayed 10082 * option and the value, or a 2D array which will use the array in the first 10083 * position as the value, and the array in the second position as the 10084 * displayed options (useful for language strings such as 'All'). 10085 * 10086 * Note that the `pageLength` property will be automatically set to the 10087 * first value given in this array, unless `pageLength` is also provided. 10088 */ 10089 "aLengthMenu": [ 10, 25, 50, 100 ], 10090 10091 10092 /** 10093 * The `columns` option in the initialisation parameter allows you to define 10094 * details about the way individual columns behave. For a full list of 10095 * column options that can be set, please see 10096 * {@link DataTable.defaults.column}. Note that if you use `columns` to 10097 * define your columns, you must have an entry in the array for every single 10098 * column that you have in your table (these can be null if you don't which 10099 * to specify any options). 10100 */ 10101 "aoColumns": null, 10102 10103 /** 10104 * Very similar to `columns`, `columnDefs` allows you to target a specific 10105 * column, multiple columns, or all columns, using the `targets` property of 10106 * each object in the array. This allows great flexibility when creating 10107 * tables, as the `columnDefs` arrays can be of any length, targeting the 10108 * columns you specifically want. `columnDefs` may use any of the column 10109 * options available: {@link DataTable.defaults.column}, but it _must_ 10110 * have `targets` defined in each object in the array. Values in the `targets` 10111 * array may be: 10112 * <ul> 10113 * <li>a string - class name will be matched on the TH for the column</li> 10114 * <li>0 or a positive integer - column index counting from the left</li> 10115 * <li>a negative integer - column index counting from the right</li> 10116 * <li>the string "_all" - all columns (i.e. assign a default)</li> 10117 * </ul> 10118 */ 10119 "aoColumnDefs": null, 10120 10121 10122 /** 10123 * Basically the same as `search`, this parameter defines the individual column 10124 * filtering state at initialisation time. The array must be of the same size 10125 * as the number of columns, and each element be an object with the parameters 10126 * `search` and `escapeRegex` (the latter is optional). 'null' is also 10127 * accepted and the default will be used. 10128 */ 10129 "aoSearchCols": [], 10130 10131 10132 /** 10133 * Enable or disable automatic column width calculation. This can be disabled 10134 * as an optimisation (it takes some time to calculate the widths) if the 10135 * tables widths are passed in using `columns`. 10136 */ 10137 "bAutoWidth": true, 10138 10139 10140 /** 10141 * Deferred rendering can provide DataTables with a huge speed boost when you 10142 * are using an Ajax or JS data source for the table. This option, when set to 10143 * true, will cause DataTables to defer the creation of the table elements for 10144 * each row until they are needed for a draw - saving a significant amount of 10145 * time. 10146 */ 10147 "bDeferRender": true, 10148 10149 10150 /** 10151 * Replace a DataTable which matches the given selector and replace it with 10152 * one which has the properties of the new initialisation object passed. If no 10153 * table matches the selector, then the new DataTable will be constructed as 10154 * per normal. 10155 */ 10156 "bDestroy": false, 10157 10158 10159 /** 10160 * Enable or disable filtering of data. Filtering in DataTables is "smart" in 10161 * that it allows the end user to input multiple words (space separated) and 10162 * will match a row containing those words, even if not in the order that was 10163 * specified (this allow matching across multiple columns). Note that if you 10164 * wish to use filtering in DataTables this must remain 'true' - to remove the 10165 * default filtering input box and retain filtering abilities, please use 10166 * {@link DataTable.defaults.dom}. 10167 */ 10168 "bFilter": true, 10169 10170 /** 10171 * Used only for compatiblity with DT1 10172 * @deprecated 10173 */ 10174 "bInfo": true, 10175 10176 /** 10177 * Used only for compatiblity with DT1 10178 * @deprecated 10179 */ 10180 "bLengthChange": true, 10181 10182 /** 10183 * Enable or disable pagination. 10184 */ 10185 "bPaginate": true, 10186 10187 10188 /** 10189 * Enable or disable the display of a 'processing' indicator when the table is 10190 * being processed (e.g. a sort). This is particularly useful for tables with 10191 * large amounts of data where it can take a noticeable amount of time to sort 10192 * the entries. 10193 */ 10194 "bProcessing": false, 10195 10196 10197 /** 10198 * Retrieve the DataTables object for the given selector. Note that if the 10199 * table has already been initialised, this parameter will cause DataTables 10200 * to simply return the object that has already been set up - it will not take 10201 * account of any changes you might have made to the initialisation object 10202 * passed to DataTables (setting this parameter to true is an acknowledgement 10203 * that you understand this). `destroy` can be used to reinitialise a table if 10204 * you need. 10205 */ 10206 "bRetrieve": false, 10207 10208 10209 /** 10210 * When vertical (y) scrolling is enabled, DataTables will force the height of 10211 * the table's viewport to the given height at all times (useful for layout). 10212 * However, this can look odd when filtering data down to a small data set, 10213 * and the footer is left "floating" further down. This parameter (when 10214 * enabled) will cause DataTables to collapse the table's viewport down when 10215 * the result set will fit within the given Y height. 10216 */ 10217 "bScrollCollapse": false, 10218 10219 10220 /** 10221 * Configure DataTables to use server-side processing. Note that the 10222 * `ajax` parameter must also be given in order to give DataTables a 10223 * source to obtain the required data for each draw. 10224 */ 10225 "bServerSide": false, 10226 10227 10228 /** 10229 * Enable or disable sorting of columns. Sorting of individual columns can be 10230 * disabled by the `sortable` option for each column. 10231 */ 10232 "bSort": true, 10233 10234 10235 /** 10236 * Enable or display DataTables' ability to sort multiple columns at the 10237 * same time (activated by shift-click by the user). 10238 */ 10239 "bSortMulti": true, 10240 10241 10242 /** 10243 * Allows control over whether DataTables should use the top (true) unique 10244 * cell that is found for a single column, or the bottom (false - default). 10245 * This is useful when using complex headers. 10246 */ 10247 "bSortCellsTop": null, 10248 10249 10250 /** 10251 * Enable or disable the addition of the classes `sorting\_1`, `sorting\_2` and 10252 * `sorting\_3` to the columns which are currently being sorted on. This is 10253 * presented as a feature switch as it can increase processing time (while 10254 * classes are removed and added) so for large data sets you might want to 10255 * turn this off. 10256 */ 10257 "bSortClasses": true, 10258 10259 10260 /** 10261 * Enable or disable state saving. When enabled HTML5 `localStorage` will be 10262 * used to save table display information such as pagination information, 10263 * display length, filtering and sorting. As such when the end user reloads 10264 * the page the display display will match what thy had previously set up. 10265 */ 10266 "bStateSave": false, 10267 10268 10269 /** 10270 * This function is called when a TR element is created (and all TD child 10271 * elements have been inserted), or registered if using a DOM source, allowing 10272 * manipulation of the TR element (adding classes etc). 10273 */ 10274 "fnCreatedRow": null, 10275 10276 10277 /** 10278 * This function is called on every 'draw' event, and allows you to 10279 * dynamically modify any aspect you want about the created DOM. 10280 */ 10281 "fnDrawCallback": null, 10282 10283 10284 /** 10285 * Identical to fnHeaderCallback() but for the table footer this function 10286 * allows you to modify the table footer on every 'draw' event. 10287 */ 10288 "fnFooterCallback": null, 10289 10290 10291 /** 10292 * When rendering large numbers in the information element for the table 10293 * (i.e. "Showing 1 to 10 of 57 entries") DataTables will render large numbers 10294 * to have a comma separator for the 'thousands' units (e.g. 1 million is 10295 * rendered as "1,000,000") to help readability for the end user. This 10296 * function will override the default method DataTables uses. 10297 */ 10298 "fnFormatNumber": function ( toFormat ) { 10299 return toFormat.toString().replace( 10300 /\B(?=(\d{3})+(?!\d))/g, 10301 this.oLanguage.sThousands 10302 ); 10303 }, 10304 10305 10306 /** 10307 * This function is called on every 'draw' event, and allows you to 10308 * dynamically modify the header row. This can be used to calculate and 10309 * display useful information about the table. 10310 */ 10311 "fnHeaderCallback": null, 10312 10313 10314 /** 10315 * The information element can be used to convey information about the current 10316 * state of the table. Although the internationalisation options presented by 10317 * DataTables are quite capable of dealing with most customisations, there may 10318 * be times where you wish to customise the string further. This callback 10319 * allows you to do exactly that. 10320 */ 10321 "fnInfoCallback": null, 10322 10323 10324 /** 10325 * Called when the table has been initialised. Normally DataTables will 10326 * initialise sequentially and there will be no need for this function, 10327 * however, this does not hold true when using external language information 10328 * since that is obtained using an async XHR call. 10329 */ 10330 "fnInitComplete": null, 10331 10332 10333 /** 10334 * Called at the very start of each table draw and can be used to cancel the 10335 * draw by returning false, any other return (including undefined) results in 10336 * the full draw occurring). 10337 */ 10338 "fnPreDrawCallback": null, 10339 10340 10341 /** 10342 * This function allows you to 'post process' each row after it have been 10343 * generated for each table draw, but before it is rendered on screen. This 10344 * function might be used for setting the row class name etc. 10345 */ 10346 "fnRowCallback": null, 10347 10348 10349 /** 10350 * Load the table state. With this function you can define from where, and how, the 10351 * state of a table is loaded. By default DataTables will load from `localStorage` 10352 * but you might wish to use a server-side database or cookies. 10353 */ 10354 "fnStateLoadCallback": function ( settings ) { 10355 try { 10356 return JSON.parse( 10357 (settings.iStateDuration === -1 ? sessionStorage : localStorage).getItem( 10358 'DataTables_'+settings.sInstance+'_'+location.pathname 10359 ) 10360 ); 10361 } catch (e) { 10362 return {}; 10363 } 10364 }, 10365 10366 10367 /** 10368 * Callback which allows modification of the saved state prior to loading that state. 10369 * This callback is called when the table is loading state from the stored data, but 10370 * prior to the settings object being modified by the saved state. Note that for 10371 * plug-in authors, you should use the `stateLoadParams` event to load parameters for 10372 * a plug-in. 10373 */ 10374 "fnStateLoadParams": null, 10375 10376 10377 /** 10378 * Callback that is called when the state has been loaded from the state saving method 10379 * and the DataTables settings object has been modified as a result of the loaded state. 10380 */ 10381 "fnStateLoaded": null, 10382 10383 10384 /** 10385 * Save the table state. This function allows you to define where and how the state 10386 * information for the table is stored By default DataTables will use `localStorage` 10387 * but you might wish to use a server-side database or cookies. 10388 */ 10389 "fnStateSaveCallback": function ( settings, data ) { 10390 try { 10391 (settings.iStateDuration === -1 ? sessionStorage : localStorage).setItem( 10392 'DataTables_'+settings.sInstance+'_'+location.pathname, 10393 JSON.stringify( data ) 10394 ); 10395 } catch (e) { 10396 // noop 10397 } 10398 }, 10399 10400 10401 /** 10402 * Callback which allows modification of the state to be saved. Called when the table 10403 * has changed state a new state save is required. This method allows modification of 10404 * the state saving object prior to actually doing the save, including addition or 10405 * other state properties or modification. Note that for plug-in authors, you should 10406 * use the `stateSaveParams` event to save parameters for a plug-in. 10407 */ 10408 "fnStateSaveParams": null, 10409 10410 10411 /** 10412 * Duration for which the saved state information is considered valid. After this period 10413 * has elapsed the state will be returned to the default. 10414 * Value is given in seconds. 10415 */ 10416 "iStateDuration": 7200, 10417 10418 10419 /** 10420 * Number of rows to display on a single page when using pagination. If 10421 * feature enabled (`lengthChange`) then the end user will be able to override 10422 * this to a custom setting using a pop-up menu. 10423 */ 10424 "iDisplayLength": 10, 10425 10426 10427 /** 10428 * Define the starting point for data display when using DataTables with 10429 * pagination. Note that this parameter is the number of records, rather than 10430 * the page number, so if you have 10 records per page and want to start on 10431 * the third page, it should be "20". 10432 */ 10433 "iDisplayStart": 0, 10434 10435 10436 /** 10437 * By default DataTables allows keyboard navigation of the table (sorting, paging, 10438 * and filtering) by adding a `tabindex` attribute to the required elements. This 10439 * allows you to tab through the controls and press the enter key to activate them. 10440 * The tabindex is default 0, meaning that the tab follows the flow of the document. 10441 * You can overrule this using this parameter if you wish. Use a value of -1 to 10442 * disable built-in keyboard navigation. 10443 */ 10444 "iTabIndex": 0, 10445 10446 10447 /** 10448 * Classes that DataTables assigns to the various components and features 10449 * that it adds to the HTML table. This allows classes to be configured 10450 * during initialisation in addition to through the static 10451 * {@link DataTable.ext.oStdClasses} object). 10452 */ 10453 "oClasses": {}, 10454 10455 10456 /** 10457 * All strings that DataTables uses in the user interface that it creates 10458 * are defined in this object, allowing you to modified them individually or 10459 * completely replace them all as required. 10460 */ 10461 "oLanguage": { 10462 /** 10463 * Strings that are used for WAI-ARIA labels and controls only (these are not 10464 * actually visible on the page, but will be read by screenreaders, and thus 10465 * must be internationalised as well). 10466 */ 10467 "oAria": { 10468 /** 10469 * ARIA label that is added to the table headers when the column may be sorted 10470 */ 10471 "orderable": ": Activate to sort", 10472 10473 /** 10474 * ARIA label that is added to the table headers when the column is currently being sorted 10475 */ 10476 "orderableReverse": ": Activate to invert sorting", 10477 10478 /** 10479 * ARIA label that is added to the table headers when the column is currently being 10480 * sorted and next step is to remove sorting 10481 */ 10482 "orderableRemove": ": Activate to remove sorting", 10483 10484 paginate: { 10485 first: 'First', 10486 last: 'Last', 10487 next: 'Next', 10488 previous: 'Previous' 10489 } 10490 }, 10491 10492 /** 10493 * Pagination string used by DataTables for the built-in pagination 10494 * control types. 10495 */ 10496 "oPaginate": { 10497 /** 10498 * Label and character for first page button («) 10499 */ 10500 "sFirst": "\u00AB", 10501 10502 /** 10503 * Last page button (») 10504 */ 10505 "sLast": "\u00BB", 10506 10507 /** 10508 * Next page button (›) 10509 */ 10510 "sNext": "\u203A", 10511 10512 /** 10513 * Previous page button (‹) 10514 */ 10515 "sPrevious": "\u2039", 10516 }, 10517 10518 /** 10519 * Plural object for the data type the table is showing 10520 */ 10521 entries: { 10522 _: "entries", 10523 1: "entry" 10524 }, 10525 10526 /** 10527 * This string is shown in preference to `zeroRecords` when the table is 10528 * empty of data (regardless of filtering). Note that this is an optional 10529 * parameter - if it is not given, the value of `zeroRecords` will be used 10530 * instead (either the default or given value). 10531 */ 10532 "sEmptyTable": "No data available in table", 10533 10534 10535 /** 10536 * This string gives information to the end user about the information 10537 * that is current on display on the page. The following tokens can be 10538 * used in the string and will be dynamically replaced as the table 10539 * display updates. This tokens can be placed anywhere in the string, or 10540 * removed as needed by the language requires: 10541 * 10542 * * `\_START\_` - Display index of the first record on the current page 10543 * * `\_END\_` - Display index of the last record on the current page 10544 * * `\_TOTAL\_` - Number of records in the table after filtering 10545 * * `\_MAX\_` - Number of records in the table without filtering 10546 * * `\_PAGE\_` - Current page number 10547 * * `\_PAGES\_` - Total number of pages of data in the table 10548 */ 10549 "sInfo": "Showing _START_ to _END_ of _TOTAL_ _ENTRIES-TOTAL_", 10550 10551 10552 /** 10553 * Display information string for when the table is empty. Typically the 10554 * format of this string should match `info`. 10555 */ 10556 "sInfoEmpty": "Showing 0 to 0 of 0 _ENTRIES-TOTAL_", 10557 10558 10559 /** 10560 * When a user filters the information in a table, this string is appended 10561 * to the information (`info`) to give an idea of how strong the filtering 10562 * is. The variable _MAX_ is dynamically updated. 10563 */ 10564 "sInfoFiltered": "(filtered from _MAX_ total _ENTRIES-MAX_)", 10565 10566 10567 /** 10568 * If can be useful to append extra information to the info string at times, 10569 * and this variable does exactly that. This information will be appended to 10570 * the `info` (`infoEmpty` and `infoFiltered` in whatever combination they are 10571 * being used) at all times. 10572 */ 10573 "sInfoPostFix": "", 10574 10575 10576 /** 10577 * This decimal place operator is a little different from the other 10578 * language options since DataTables doesn't output floating point 10579 * numbers, so it won't ever use this for display of a number. Rather, 10580 * what this parameter does is modify the sort methods of the table so 10581 * that numbers which are in a format which has a character other than 10582 * a period (`.`) as a decimal place will be sorted numerically. 10583 * 10584 * Note that numbers with different decimal places cannot be shown in 10585 * the same table and still be sortable, the table must be consistent. 10586 * However, multiple different tables on the page can use different 10587 * decimal place characters. 10588 */ 10589 "sDecimal": "", 10590 10591 10592 /** 10593 * DataTables has a build in number formatter (`formatNumber`) which is 10594 * used to format large numbers that are used in the table information. 10595 * By default a comma is used, but this can be trivially changed to any 10596 * character you wish with this parameter. 10597 */ 10598 "sThousands": ",", 10599 10600 10601 /** 10602 * Detail the action that will be taken when the drop down menu for the 10603 * pagination length option is changed. The '_MENU_' variable is replaced 10604 * with a default select list of 10, 25, 50 and 100, and can be replaced 10605 * with a custom select box if required. 10606 */ 10607 "sLengthMenu": "_MENU_ _ENTRIES_ per page", 10608 10609 10610 /** 10611 * When using Ajax sourced data and during the first draw when DataTables is 10612 * gathering the data, this message is shown in an empty row in the table to 10613 * indicate to the end user the the data is being loaded. Note that this 10614 * parameter is not used when loading data by server-side processing, just 10615 * Ajax sourced data with client-side processing. 10616 */ 10617 "sLoadingRecords": "Loading...", 10618 10619 10620 /** 10621 * Text which is displayed when the table is processing a user action 10622 * (usually a sort command or similar). 10623 */ 10624 "sProcessing": "", 10625 10626 10627 /** 10628 * Details the actions that will be taken when the user types into the 10629 * filtering input text box. The variable "_INPUT_", if used in the string, 10630 * is replaced with the HTML text box for the filtering input allowing 10631 * control over where it appears in the string. If "_INPUT_" is not given 10632 * then the input box is appended to the string automatically. 10633 */ 10634 "sSearch": "Search:", 10635 10636 10637 /** 10638 * Assign a `placeholder` attribute to the search `input` element 10639 * @type string 10640 * @default 10641 * 10642 * @dtopt Language 10643 * @name DataTable.defaults.language.searchPlaceholder 10644 */ 10645 "sSearchPlaceholder": "", 10646 10647 10648 /** 10649 * All of the language information can be stored in a file on the 10650 * server-side, which DataTables will look up if this parameter is passed. 10651 * It must store the URL of the language file, which is in a JSON format, 10652 * and the object has the same properties as the oLanguage object in the 10653 * initialiser object (i.e. the above parameters). Please refer to one of 10654 * the example language files to see how this works in action. 10655 */ 10656 "sUrl": "", 10657 10658 10659 /** 10660 * Text shown inside the table records when the is no information to be 10661 * displayed after filtering. `emptyTable` is shown when there is simply no 10662 * information in the table at all (regardless of filtering). 10663 */ 10664 "sZeroRecords": "No matching records found" 10665 }, 10666 10667 10668 /** 10669 * This parameter allows you to have define the global filtering state at 10670 * initialisation time. As an object the `search` parameter must be 10671 * defined, but all other parameters are optional. When `regex` is true, 10672 * the search string will be treated as a regular expression, when false 10673 * (default) it will be treated as a straight string. When `smart` 10674 * DataTables will use it's smart filtering methods (to word match at 10675 * any point in the data), when false this will not be done. 10676 */ 10677 "oSearch": $.extend( {}, DataTable.models.oSearch ), 10678 10679 10680 /** 10681 * Table and control layout. This replaces the legacy `dom` option. 10682 */ 10683 layout: { 10684 topStart: 'pageLength', 10685 topEnd: 'search', 10686 bottomStart: 'info', 10687 bottomEnd: 'paging' 10688 }, 10689 10690 10691 /** 10692 * Legacy DOM layout option 10693 */ 10694 "sDom": null, 10695 10696 10697 /** 10698 * Search delay option. This will throttle full table searches that use the 10699 * DataTables provided search input element (it does not effect calls to 10700 * `dt-api search()`, providing a delay before the search is made. 10701 */ 10702 "searchDelay": null, 10703 10704 10705 /** 10706 * DataTables features six different built-in options for the buttons to 10707 * display for pagination control: 10708 * 10709 * * `numbers` - Page number buttons only 10710 * * `simple` - 'Previous' and 'Next' buttons only 10711 * * 'simple_numbers` - 'Previous' and 'Next' buttons, plus page numbers 10712 * * `full` - 'First', 'Previous', 'Next' and 'Last' buttons 10713 * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus page numbers 10714 * * `first_last_numbers` - 'First' and 'Last' buttons, plus page numbers 10715 */ 10716 "sPaginationType": "full_numbers", 10717 10718 10719 /** 10720 * Enable horizontal scrolling. When a table is too wide to fit into a 10721 * certain layout, or you have a large number of columns in the table, you 10722 * can enable x-scrolling to show the table in a viewport, which can be 10723 * scrolled. This property can be `true` which will allow the table to 10724 * scroll horizontally when needed, or any CSS unit, or a number (in which 10725 * case it will be treated as a pixel measurement). Setting as simply `true` 10726 * is recommended. 10727 */ 10728 "sScrollX": "", 10729 10730 10731 /** 10732 * This property can be used to force a DataTable to use more width than it 10733 * might otherwise do when x-scrolling is enabled. For example if you have a 10734 * table which requires to be well spaced, this parameter is useful for 10735 * "over-sizing" the table, and thus forcing scrolling. This property can by 10736 * any CSS unit, or a number (in which case it will be treated as a pixel 10737 * measurement). 10738 */ 10739 "sScrollXInner": "", 10740 10741 10742 /** 10743 * Enable vertical scrolling. Vertical scrolling will constrain the DataTable 10744 * to the given height, and enable scrolling for any data which overflows the 10745 * current viewport. This can be used as an alternative to paging to display 10746 * a lot of data in a small area (although paging and scrolling can both be 10747 * enabled at the same time). This property can be any CSS unit, or a number 10748 * (in which case it will be treated as a pixel measurement). 10749 */ 10750 "sScrollY": "", 10751 10752 10753 /** 10754 * __Deprecated__ The functionality provided by this parameter has now been 10755 * superseded by that provided through `ajax`, which should be used instead. 10756 * 10757 * Set the HTTP method that is used to make the Ajax call for server-side 10758 * processing or Ajax sourced data. 10759 */ 10760 "sServerMethod": "GET", 10761 10762 10763 /** 10764 * DataTables makes use of renderers when displaying HTML elements for 10765 * a table. These renderers can be added or modified by plug-ins to 10766 * generate suitable mark-up for a site. For example the Bootstrap 10767 * integration plug-in for DataTables uses a paging button renderer to 10768 * display pagination buttons in the mark-up required by Bootstrap. 10769 * 10770 * For further information about the renderers available see 10771 * DataTable.ext.renderer 10772 */ 10773 "renderer": null, 10774 10775 10776 /** 10777 * Set the data property name that DataTables should use to get a row's id 10778 * to set as the `id` property in the node. 10779 */ 10780 "rowId": "DT_RowId", 10781 10782 10783 /** 10784 * Caption value 10785 */ 10786 "caption": null 10787 }; 10788 10789 _fnHungarianMap( DataTable.defaults ); 10790 10791 10792 10793 /* 10794 * Developer note - See note in model.defaults.js about the use of Hungarian 10795 * notation and camel case. 10796 */ 10797 10798 /** 10799 * Column options that can be given to DataTables at initialisation time. 10800 * @namespace 10801 */ 10802 DataTable.defaults.column = { 10803 /** 10804 * Define which column(s) an order will occur on for this column. This 10805 * allows a column's ordering to take multiple columns into account when 10806 * doing a sort or use the data from a different column. For example first 10807 * name / last name columns make sense to do a multi-column sort over the 10808 * two columns. 10809 */ 10810 "aDataSort": null, 10811 "iDataSort": -1, 10812 10813 ariaTitle: '', 10814 10815 10816 /** 10817 * You can control the default ordering direction, and even alter the 10818 * behaviour of the sort handler (i.e. only allow ascending ordering etc) 10819 * using this parameter. 10820 */ 10821 "asSorting": [ 'asc', 'desc', '' ], 10822 10823 10824 /** 10825 * Enable or disable filtering on the data in this column. 10826 */ 10827 "bSearchable": true, 10828 10829 10830 /** 10831 * Enable or disable ordering on this column. 10832 */ 10833 "bSortable": true, 10834 10835 10836 /** 10837 * Enable or disable the display of this column. 10838 */ 10839 "bVisible": true, 10840 10841 10842 /** 10843 * Developer definable function that is called whenever a cell is created (Ajax source, 10844 * etc) or processed for input (DOM source). This can be used as a compliment to mRender 10845 * allowing you to modify the DOM element (add background colour for example) when the 10846 * element is available. 10847 */ 10848 "fnCreatedCell": null, 10849 10850 10851 /** 10852 * This property can be used to read data from any data source property, 10853 * including deeply nested objects / properties. `data` can be given in a 10854 * number of different ways which effect its behaviour: 10855 * 10856 * * `integer` - treated as an array index for the data source. This is the 10857 * default that DataTables uses (incrementally increased for each column). 10858 * * `string` - read an object property from the data source. There are 10859 * three 'special' options that can be used in the string to alter how 10860 * DataTables reads the data from the source object: 10861 * * `.` - Dotted Javascript notation. Just as you use a `.` in 10862 * Javascript to read from nested objects, so to can the options 10863 * specified in `data`. For example: `browser.version` or 10864 * `browser.name`. If your object parameter name contains a period, use 10865 * `\\` to escape it - i.e. `first\\.name`. 10866 * * `[]` - Array notation. DataTables can automatically combine data 10867 * from and array source, joining the data with the characters provided 10868 * between the two brackets. For example: `name[, ]` would provide a 10869 * comma-space separated list from the source array. If no characters 10870 * are provided between the brackets, the original array source is 10871 * returned. 10872 * * `()` - Function notation. Adding `()` to the end of a parameter will 10873 * execute a function of the name given. For example: `browser()` for a 10874 * simple function on the data source, `browser.version()` for a 10875 * function in a nested property or even `browser().version` to get an 10876 * object property if the function called returns an object. Note that 10877 * function notation is recommended for use in `render` rather than 10878 * `data` as it is much simpler to use as a renderer. 10879 * * `null` - use the original data source for the row rather than plucking 10880 * data directly from it. This action has effects on two other 10881 * initialisation options: 10882 * * `defaultContent` - When null is given as the `data` option and 10883 * `defaultContent` is specified for the column, the value defined by 10884 * `defaultContent` will be used for the cell. 10885 * * `render` - When null is used for the `data` option and the `render` 10886 * option is specified for the column, the whole data source for the 10887 * row is used for the renderer. 10888 * * `function` - the function given will be executed whenever DataTables 10889 * needs to set or get the data for a cell in the column. The function 10890 * takes three parameters: 10891 * * Parameters: 10892 * * `{array|object}` The data source for the row 10893 * * `{string}` The type call data requested - this will be 'set' when 10894 * setting data or 'filter', 'display', 'type', 'sort' or undefined 10895 * when gathering data. Note that when `undefined` is given for the 10896 * type DataTables expects to get the raw data for the object back< 10897 * * `{*}` Data to set when the second parameter is 'set'. 10898 * * Return: 10899 * * The return value from the function is not required when 'set' is 10900 * the type of call, but otherwise the return is what will be used 10901 * for the data requested. 10902 * 10903 * Note that `data` is a getter and setter option. If you just require 10904 * formatting of data for output, you will likely want to use `render` which 10905 * is simply a getter and thus simpler to use. 10906 * 10907 * Note that prior to DataTables 1.9.2 `data` was called `mDataProp`. The 10908 * name change reflects the flexibility of this property and is consistent 10909 * with the naming of mRender. If 'mDataProp' is given, then it will still 10910 * be used by DataTables, as it automatically maps the old name to the new 10911 * if required. 10912 */ 10913 "mData": null, 10914 10915 10916 /** 10917 * This property is the rendering partner to `data` and it is suggested that 10918 * when you want to manipulate data for display (including filtering, 10919 * sorting etc) without altering the underlying data for the table, use this 10920 * property. `render` can be considered to be the the read only companion to 10921 * `data` which is read / write (then as such more complex). Like `data` 10922 * this option can be given in a number of different ways to effect its 10923 * behaviour: 10924 * 10925 * * `integer` - treated as an array index for the data source. This is the 10926 * default that DataTables uses (incrementally increased for each column). 10927 * * `string` - read an object property from the data source. There are 10928 * three 'special' options that can be used in the string to alter how 10929 * DataTables reads the data from the source object: 10930 * * `.` - Dotted Javascript notation. Just as you use a `.` in 10931 * Javascript to read from nested objects, so to can the options 10932 * specified in `data`. For example: `browser.version` or 10933 * `browser.name`. If your object parameter name contains a period, use 10934 * `\\` to escape it - i.e. `first\\.name`. 10935 * * `[]` - Array notation. DataTables can automatically combine data 10936 * from and array source, joining the data with the characters provided 10937 * between the two brackets. For example: `name[, ]` would provide a 10938 * comma-space separated list from the source array. If no characters 10939 * are provided between the brackets, the original array source is 10940 * returned. 10941 * * `()` - Function notation. Adding `()` to the end of a parameter will 10942 * execute a function of the name given. For example: `browser()` for a 10943 * simple function on the data source, `browser.version()` for a 10944 * function in a nested property or even `browser().version` to get an 10945 * object property if the function called returns an object. 10946 * * `object` - use different data for the different data types requested by 10947 * DataTables ('filter', 'display', 'type' or 'sort'). The property names 10948 * of the object is the data type the property refers to and the value can 10949 * defined using an integer, string or function using the same rules as 10950 * `render` normally does. Note that an `_` option _must_ be specified. 10951 * This is the default value to use if you haven't specified a value for 10952 * the data type requested by DataTables. 10953 * * `function` - the function given will be executed whenever DataTables 10954 * needs to set or get the data for a cell in the column. The function 10955 * takes three parameters: 10956 * * Parameters: 10957 * * {array|object} The data source for the row (based on `data`) 10958 * * {string} The type call data requested - this will be 'filter', 10959 * 'display', 'type' or 'sort'. 10960 * * {array|object} The full data source for the row (not based on 10961 * `data`) 10962 * * Return: 10963 * * The return value from the function is what will be used for the 10964 * data requested. 10965 */ 10966 "mRender": null, 10967 10968 10969 /** 10970 * Change the cell type created for the column - either TD cells or TH cells. This 10971 * can be useful as TH cells have semantic meaning in the table body, allowing them 10972 * to act as a header for a row (you may wish to add scope='row' to the TH elements). 10973 */ 10974 "sCellType": "td", 10975 10976 10977 /** 10978 * Class to give to each cell in this column. 10979 */ 10980 "sClass": "", 10981 10982 /** 10983 * When DataTables calculates the column widths to assign to each column, 10984 * it finds the longest string in each column and then constructs a 10985 * temporary table and reads the widths from that. The problem with this 10986 * is that "mmm" is much wider then "iiii", but the latter is a longer 10987 * string - thus the calculation can go wrong (doing it properly and putting 10988 * it into an DOM object and measuring that is horribly(!) slow). Thus as 10989 * a "work around" we provide this option. It will append its value to the 10990 * text that is found to be the longest string for the column - i.e. padding. 10991 * Generally you shouldn't need this! 10992 */ 10993 "sContentPadding": "", 10994 10995 10996 /** 10997 * Allows a default value to be given for a column's data, and will be used 10998 * whenever a null data source is encountered (this can be because `data` 10999 * is set to null, or because the data source itself is null). 11000 */ 11001 "sDefaultContent": null, 11002 11003 11004 /** 11005 * This parameter is only used in DataTables' server-side processing. It can 11006 * be exceptionally useful to know what columns are being displayed on the 11007 * client side, and to map these to database fields. When defined, the names 11008 * also allow DataTables to reorder information from the server if it comes 11009 * back in an unexpected order (i.e. if you switch your columns around on the 11010 * client-side, your server-side code does not also need updating). 11011 */ 11012 "sName": "", 11013 11014 11015 /** 11016 * Defines a data source type for the ordering which can be used to read 11017 * real-time information from the table (updating the internally cached 11018 * version) prior to ordering. This allows ordering to occur on user 11019 * editable elements such as form inputs. 11020 */ 11021 "sSortDataType": "std", 11022 11023 11024 /** 11025 * The title of this column. 11026 */ 11027 "sTitle": null, 11028 11029 11030 /** 11031 * The type allows you to specify how the data for this column will be 11032 * ordered. Four types (string, numeric, date and html (which will strip 11033 * HTML tags before ordering)) are currently available. Note that only date 11034 * formats understood by Javascript's Date() object will be accepted as type 11035 * date. For example: "Mar 26, 2008 5:03 PM". May take the values: 'string', 11036 * 'numeric', 'date' or 'html' (by default). Further types can be adding 11037 * through plug-ins. 11038 */ 11039 "sType": null, 11040 11041 11042 /** 11043 * Defining the width of the column, this parameter may take any CSS value 11044 * (3em, 20px etc). DataTables applies 'smart' widths to columns which have not 11045 * been given a specific width through this interface ensuring that the table 11046 * remains readable. 11047 */ 11048 "sWidth": null 11049 }; 11050 11051 _fnHungarianMap( DataTable.defaults.column ); 11052 11053 11054 11055 /** 11056 * DataTables settings object - this holds all the information needed for a 11057 * given table, including configuration, data and current application of the 11058 * table options. DataTables does not have a single instance for each DataTable 11059 * with the settings attached to that instance, but rather instances of the 11060 * DataTable "class" are created on-the-fly as needed (typically by a 11061 * $().dataTable() call) and the settings object is then applied to that 11062 * instance. 11063 * 11064 * Note that this object is related to {@link DataTable.defaults} but this 11065 * one is the internal data store for DataTables's cache of columns. It should 11066 * NOT be manipulated outside of DataTables. Any configuration should be done 11067 * through the initialisation options. 11068 */ 11069 DataTable.models.oSettings = { 11070 /** 11071 * Primary features of DataTables and their enablement state. 11072 */ 11073 "oFeatures": { 11074 11075 /** 11076 * Flag to say if DataTables should automatically try to calculate the 11077 * optimum table and columns widths (true) or not (false). 11078 * Note that this parameter will be set by the initialisation routine. To 11079 * set a default use {@link DataTable.defaults}. 11080 */ 11081 "bAutoWidth": null, 11082 11083 /** 11084 * Delay the creation of TR and TD elements until they are actually 11085 * needed by a driven page draw. This can give a significant speed 11086 * increase for Ajax source and Javascript source data, but makes no 11087 * difference at all for DOM and server-side processing tables. 11088 * Note that this parameter will be set by the initialisation routine. To 11089 * set a default use {@link DataTable.defaults}. 11090 */ 11091 "bDeferRender": null, 11092 11093 /** 11094 * Enable filtering on the table or not. Note that if this is disabled 11095 * then there is no filtering at all on the table, including fnFilter. 11096 * To just remove the filtering input use sDom and remove the 'f' option. 11097 * Note that this parameter will be set by the initialisation routine. To 11098 * set a default use {@link DataTable.defaults}. 11099 */ 11100 "bFilter": null, 11101 11102 /** 11103 * Used only for compatiblity with DT1 11104 * @deprecated 11105 */ 11106 "bInfo": true, 11107 11108 /** 11109 * Used only for compatiblity with DT1 11110 * @deprecated 11111 */ 11112 "bLengthChange": true, 11113 11114 /** 11115 * Pagination enabled or not. Note that if this is disabled then length 11116 * changing must also be disabled. 11117 * Note that this parameter will be set by the initialisation routine. To 11118 * set a default use {@link DataTable.defaults}. 11119 */ 11120 "bPaginate": null, 11121 11122 /** 11123 * Processing indicator enable flag whenever DataTables is enacting a 11124 * user request - typically an Ajax request for server-side processing. 11125 * Note that this parameter will be set by the initialisation routine. To 11126 * set a default use {@link DataTable.defaults}. 11127 */ 11128 "bProcessing": null, 11129 11130 /** 11131 * Server-side processing enabled flag - when enabled DataTables will 11132 * get all data from the server for every draw - there is no filtering, 11133 * sorting or paging done on the client-side. 11134 * Note that this parameter will be set by the initialisation routine. To 11135 * set a default use {@link DataTable.defaults}. 11136 */ 11137 "bServerSide": null, 11138 11139 /** 11140 * Sorting enablement flag. 11141 * Note that this parameter will be set by the initialisation routine. To 11142 * set a default use {@link DataTable.defaults}. 11143 */ 11144 "bSort": null, 11145 11146 /** 11147 * Multi-column sorting 11148 * Note that this parameter will be set by the initialisation routine. To 11149 * set a default use {@link DataTable.defaults}. 11150 */ 11151 "bSortMulti": null, 11152 11153 /** 11154 * Apply a class to the columns which are being sorted to provide a 11155 * visual highlight or not. This can slow things down when enabled since 11156 * there is a lot of DOM interaction. 11157 * Note that this parameter will be set by the initialisation routine. To 11158 * set a default use {@link DataTable.defaults}. 11159 */ 11160 "bSortClasses": null, 11161 11162 /** 11163 * State saving enablement flag. 11164 * Note that this parameter will be set by the initialisation routine. To 11165 * set a default use {@link DataTable.defaults}. 11166 */ 11167 "bStateSave": null 11168 }, 11169 11170 11171 /** 11172 * Scrolling settings for a table. 11173 */ 11174 "oScroll": { 11175 /** 11176 * When the table is shorter in height than sScrollY, collapse the 11177 * table container down to the height of the table (when true). 11178 * Note that this parameter will be set by the initialisation routine. To 11179 * set a default use {@link DataTable.defaults}. 11180 */ 11181 "bCollapse": null, 11182 11183 /** 11184 * Width of the scrollbar for the web-browser's platform. Calculated 11185 * during table initialisation. 11186 */ 11187 "iBarWidth": 0, 11188 11189 /** 11190 * Viewport width for horizontal scrolling. Horizontal scrolling is 11191 * disabled if an empty string. 11192 * Note that this parameter will be set by the initialisation routine. To 11193 * set a default use {@link DataTable.defaults}. 11194 */ 11195 "sX": null, 11196 11197 /** 11198 * Width to expand the table to when using x-scrolling. Typically you 11199 * should not need to use this. 11200 * Note that this parameter will be set by the initialisation routine. To 11201 * set a default use {@link DataTable.defaults}. 11202 * @deprecated 11203 */ 11204 "sXInner": null, 11205 11206 /** 11207 * Viewport height for vertical scrolling. Vertical scrolling is disabled 11208 * if an empty string. 11209 * Note that this parameter will be set by the initialisation routine. To 11210 * set a default use {@link DataTable.defaults}. 11211 */ 11212 "sY": null 11213 }, 11214 11215 /** 11216 * Language information for the table. 11217 */ 11218 "oLanguage": { 11219 /** 11220 * Information callback function. See 11221 * {@link DataTable.defaults.fnInfoCallback} 11222 */ 11223 "fnInfoCallback": null 11224 }, 11225 11226 /** 11227 * Browser support parameters 11228 */ 11229 "oBrowser": { 11230 /** 11231 * Determine if the vertical scrollbar is on the right or left of the 11232 * scrolling container - needed for rtl language layout, although not 11233 * all browsers move the scrollbar (Safari). 11234 */ 11235 "bScrollbarLeft": false, 11236 11237 /** 11238 * Browser scrollbar width 11239 */ 11240 "barWidth": 0 11241 }, 11242 11243 11244 "ajax": null, 11245 11246 11247 /** 11248 * Array referencing the nodes which are used for the features. The 11249 * parameters of this object match what is allowed by sDom - i.e. 11250 * <ul> 11251 * <li>'l' - Length changing</li> 11252 * <li>'f' - Filtering input</li> 11253 * <li>'t' - The table!</li> 11254 * <li>'i' - Information</li> 11255 * <li>'p' - Pagination</li> 11256 * <li>'r' - pRocessing</li> 11257 * </ul> 11258 */ 11259 "aanFeatures": [], 11260 11261 /** 11262 * Store data information - see {@link DataTable.models.oRow} for detailed 11263 * information. 11264 */ 11265 "aoData": [], 11266 11267 /** 11268 * Array of indexes which are in the current display (after filtering etc) 11269 */ 11270 "aiDisplay": [], 11271 11272 /** 11273 * Array of indexes for display - no filtering 11274 */ 11275 "aiDisplayMaster": [], 11276 11277 /** 11278 * Map of row ids to data indexes 11279 */ 11280 "aIds": {}, 11281 11282 /** 11283 * Store information about each column that is in use 11284 */ 11285 "aoColumns": [], 11286 11287 /** 11288 * Store information about the table's header 11289 */ 11290 "aoHeader": [], 11291 11292 /** 11293 * Store information about the table's footer 11294 */ 11295 "aoFooter": [], 11296 11297 /** 11298 * Store the applied global search information in case we want to force a 11299 * research or compare the old search to a new one. 11300 * Note that this parameter will be set by the initialisation routine. To 11301 * set a default use {@link DataTable.defaults}. 11302 */ 11303 "oPreviousSearch": {}, 11304 11305 /** 11306 * Store for named searches 11307 */ 11308 searchFixed: {}, 11309 11310 /** 11311 * Store the applied search for each column - see 11312 * {@link DataTable.models.oSearch} for the format that is used for the 11313 * filtering information for each column. 11314 */ 11315 "aoPreSearchCols": [], 11316 11317 /** 11318 * Sorting that is applied to the table. Note that the inner arrays are 11319 * used in the following manner: 11320 * <ul> 11321 * <li>Index 0 - column number</li> 11322 * <li>Index 1 - current sorting direction</li> 11323 * </ul> 11324 * Note that this parameter will be set by the initialisation routine. To 11325 * set a default use {@link DataTable.defaults}. 11326 */ 11327 "aaSorting": null, 11328 11329 /** 11330 * Sorting that is always applied to the table (i.e. prefixed in front of 11331 * aaSorting). 11332 * Note that this parameter will be set by the initialisation routine. To 11333 * set a default use {@link DataTable.defaults}. 11334 */ 11335 "aaSortingFixed": [], 11336 11337 /** 11338 * If restoring a table - we should restore its width 11339 */ 11340 "sDestroyWidth": 0, 11341 11342 /** 11343 * Callback functions array for every time a row is inserted (i.e. on a draw). 11344 */ 11345 "aoRowCallback": [], 11346 11347 /** 11348 * Callback functions for the header on each draw. 11349 */ 11350 "aoHeaderCallback": [], 11351 11352 /** 11353 * Callback function for the footer on each draw. 11354 */ 11355 "aoFooterCallback": [], 11356 11357 /** 11358 * Array of callback functions for draw callback functions 11359 */ 11360 "aoDrawCallback": [], 11361 11362 /** 11363 * Array of callback functions for row created function 11364 */ 11365 "aoRowCreatedCallback": [], 11366 11367 /** 11368 * Callback functions for just before the table is redrawn. A return of 11369 * false will be used to cancel the draw. 11370 */ 11371 "aoPreDrawCallback": [], 11372 11373 /** 11374 * Callback functions for when the table has been initialised. 11375 */ 11376 "aoInitComplete": [], 11377 11378 11379 /** 11380 * Callbacks for modifying the settings to be stored for state saving, prior to 11381 * saving state. 11382 */ 11383 "aoStateSaveParams": [], 11384 11385 /** 11386 * Callbacks for modifying the settings that have been stored for state saving 11387 * prior to using the stored values to restore the state. 11388 */ 11389 "aoStateLoadParams": [], 11390 11391 /** 11392 * Callbacks for operating on the settings object once the saved state has been 11393 * loaded 11394 */ 11395 "aoStateLoaded": [], 11396 11397 /** 11398 * Cache the table ID for quick access 11399 */ 11400 "sTableId": "", 11401 11402 /** 11403 * The TABLE node for the main table 11404 */ 11405 "nTable": null, 11406 11407 /** 11408 * Permanent ref to the thead element 11409 */ 11410 "nTHead": null, 11411 11412 /** 11413 * Permanent ref to the tfoot element - if it exists 11414 */ 11415 "nTFoot": null, 11416 11417 /** 11418 * Permanent ref to the tbody element 11419 */ 11420 "nTBody": null, 11421 11422 /** 11423 * Cache the wrapper node (contains all DataTables controlled elements) 11424 */ 11425 "nTableWrapper": null, 11426 11427 /** 11428 * Indicate if all required information has been read in 11429 */ 11430 "bInitialised": false, 11431 11432 /** 11433 * Information about open rows. Each object in the array has the parameters 11434 * 'nTr' and 'nParent' 11435 */ 11436 "aoOpenRows": [], 11437 11438 /** 11439 * Dictate the positioning of DataTables' control elements - see 11440 * {@link DataTable.model.oInit.sDom}. 11441 * Note that this parameter will be set by the initialisation routine. To 11442 * set a default use {@link DataTable.defaults}. 11443 */ 11444 "sDom": null, 11445 11446 /** 11447 * Search delay (in mS) 11448 */ 11449 "searchDelay": null, 11450 11451 /** 11452 * Which type of pagination should be used. 11453 * Note that this parameter will be set by the initialisation routine. To 11454 * set a default use {@link DataTable.defaults}. 11455 */ 11456 "sPaginationType": "two_button", 11457 11458 /** 11459 * Number of paging controls on the page. Only used for backwards compatibility 11460 */ 11461 pagingControls: 0, 11462 11463 /** 11464 * The state duration (for `stateSave`) in seconds. 11465 * Note that this parameter will be set by the initialisation routine. To 11466 * set a default use {@link DataTable.defaults}. 11467 */ 11468 "iStateDuration": 0, 11469 11470 /** 11471 * Array of callback functions for state saving. Each array element is an 11472 * object with the following parameters: 11473 * <ul> 11474 * <li>function:fn - function to call. Takes two parameters, oSettings 11475 * and the JSON string to save that has been thus far created. Returns 11476 * a JSON string to be inserted into a json object 11477 * (i.e. '"param": [ 0, 1, 2]')</li> 11478 * <li>string:sName - name of callback</li> 11479 * </ul> 11480 */ 11481 "aoStateSave": [], 11482 11483 /** 11484 * Array of callback functions for state loading. Each array element is an 11485 * object with the following parameters: 11486 * <ul> 11487 * <li>function:fn - function to call. Takes two parameters, oSettings 11488 * and the object stored. May return false to cancel state loading</li> 11489 * <li>string:sName - name of callback</li> 11490 * </ul> 11491 */ 11492 "aoStateLoad": [], 11493 11494 /** 11495 * State that was saved. Useful for back reference 11496 */ 11497 "oSavedState": null, 11498 11499 /** 11500 * State that was loaded. Useful for back reference 11501 */ 11502 "oLoadedState": null, 11503 11504 /** 11505 * Note if draw should be blocked while getting data 11506 */ 11507 "bAjaxDataGet": true, 11508 11509 /** 11510 * The last jQuery XHR object that was used for server-side data gathering. 11511 * This can be used for working with the XHR information in one of the 11512 * callbacks 11513 */ 11514 "jqXHR": null, 11515 11516 /** 11517 * JSON returned from the server in the last Ajax request 11518 */ 11519 "json": undefined, 11520 11521 /** 11522 * Data submitted as part of the last Ajax request 11523 */ 11524 "oAjaxData": undefined, 11525 11526 /** 11527 * Send the XHR HTTP method - GET or POST (could be PUT or DELETE if 11528 * required). 11529 * Note that this parameter will be set by the initialisation routine. To 11530 * set a default use {@link DataTable.defaults}. 11531 */ 11532 "sServerMethod": null, 11533 11534 /** 11535 * Format numbers for display. 11536 * Note that this parameter will be set by the initialisation routine. To 11537 * set a default use {@link DataTable.defaults}. 11538 */ 11539 "fnFormatNumber": null, 11540 11541 /** 11542 * List of options that can be used for the user selectable length menu. 11543 * Note that this parameter will be set by the initialisation routine. To 11544 * set a default use {@link DataTable.defaults}. 11545 */ 11546 "aLengthMenu": null, 11547 11548 /** 11549 * Counter for the draws that the table does. Also used as a tracker for 11550 * server-side processing 11551 */ 11552 "iDraw": 0, 11553 11554 /** 11555 * Indicate if a redraw is being done - useful for Ajax 11556 */ 11557 "bDrawing": false, 11558 11559 /** 11560 * Draw index (iDraw) of the last error when parsing the returned data 11561 */ 11562 "iDrawError": -1, 11563 11564 /** 11565 * Paging display length 11566 */ 11567 "_iDisplayLength": 10, 11568 11569 /** 11570 * Paging start point - aiDisplay index 11571 */ 11572 "_iDisplayStart": 0, 11573 11574 /** 11575 * Server-side processing - number of records in the result set 11576 * (i.e. before filtering), Use fnRecordsTotal rather than 11577 * this property to get the value of the number of records, regardless of 11578 * the server-side processing setting. 11579 */ 11580 "_iRecordsTotal": 0, 11581 11582 /** 11583 * Server-side processing - number of records in the current display set 11584 * (i.e. after filtering). Use fnRecordsDisplay rather than 11585 * this property to get the value of the number of records, regardless of 11586 * the server-side processing setting. 11587 */ 11588 "_iRecordsDisplay": 0, 11589 11590 /** 11591 * The classes to use for the table 11592 */ 11593 "oClasses": {}, 11594 11595 /** 11596 * Flag attached to the settings object so you can check in the draw 11597 * callback if filtering has been done in the draw. Deprecated in favour of 11598 * events. 11599 * @deprecated 11600 */ 11601 "bFiltered": false, 11602 11603 /** 11604 * Flag attached to the settings object so you can check in the draw 11605 * callback if sorting has been done in the draw. Deprecated in favour of 11606 * events. 11607 * @deprecated 11608 */ 11609 "bSorted": false, 11610 11611 /** 11612 * Indicate that if multiple rows are in the header and there is more than 11613 * one unique cell per column, if the top one (true) or bottom one (false) 11614 * should be used for sorting / title by DataTables. 11615 * Note that this parameter will be set by the initialisation routine. To 11616 * set a default use {@link DataTable.defaults}. 11617 */ 11618 "bSortCellsTop": null, 11619 11620 /** 11621 * Initialisation object that is used for the table 11622 */ 11623 "oInit": null, 11624 11625 /** 11626 * Destroy callback functions - for plug-ins to attach themselves to the 11627 * destroy so they can clean up markup and events. 11628 */ 11629 "aoDestroyCallback": [], 11630 11631 11632 /** 11633 * Get the number of records in the current record set, before filtering 11634 */ 11635 "fnRecordsTotal": function () 11636 { 11637 return _fnDataSource( this ) == 'ssp' ? 11638 this._iRecordsTotal * 1 : 11639 this.aiDisplayMaster.length; 11640 }, 11641 11642 /** 11643 * Get the number of records in the current record set, after filtering 11644 */ 11645 "fnRecordsDisplay": function () 11646 { 11647 return _fnDataSource( this ) == 'ssp' ? 11648 this._iRecordsDisplay * 1 : 11649 this.aiDisplay.length; 11650 }, 11651 11652 /** 11653 * Get the display end point - aiDisplay index 11654 */ 11655 "fnDisplayEnd": function () 11656 { 11657 var 11658 len = this._iDisplayLength, 11659 start = this._iDisplayStart, 11660 calc = start + len, 11661 records = this.aiDisplay.length, 11662 features = this.oFeatures, 11663 paginate = features.bPaginate; 11664 11665 if ( features.bServerSide ) { 11666 return paginate === false || len === -1 ? 11667 start + records : 11668 Math.min( start+len, this._iRecordsDisplay ); 11669 } 11670 else { 11671 return ! paginate || calc>records || len===-1 ? 11672 records : 11673 calc; 11674 } 11675 }, 11676 11677 /** 11678 * The DataTables object for this table 11679 */ 11680 "oInstance": null, 11681 11682 /** 11683 * Unique identifier for each instance of the DataTables object. If there 11684 * is an ID on the table node, then it takes that value, otherwise an 11685 * incrementing internal counter is used. 11686 */ 11687 "sInstance": null, 11688 11689 /** 11690 * tabindex attribute value that is added to DataTables control elements, allowing 11691 * keyboard navigation of the table and its controls. 11692 */ 11693 "iTabIndex": 0, 11694 11695 /** 11696 * DIV container for the footer scrolling table if scrolling 11697 */ 11698 "nScrollHead": null, 11699 11700 /** 11701 * DIV container for the footer scrolling table if scrolling 11702 */ 11703 "nScrollFoot": null, 11704 11705 /** 11706 * Last applied sort 11707 */ 11708 "aLastSort": [], 11709 11710 /** 11711 * Stored plug-in instances 11712 */ 11713 "oPlugins": {}, 11714 11715 /** 11716 * Function used to get a row's id from the row's data 11717 */ 11718 "rowIdFn": null, 11719 11720 /** 11721 * Data location where to store a row's id 11722 */ 11723 "rowId": null, 11724 11725 caption: '', 11726 11727 captionNode: null, 11728 11729 colgroup: null 11730 }; 11731 11732 /** 11733 * Extension object for DataTables that is used to provide all extension 11734 * options. 11735 * 11736 * Note that the `DataTable.ext` object is available through 11737 * `jQuery.fn.dataTable.ext` where it may be accessed and manipulated. It is 11738 * also aliased to `jQuery.fn.dataTableExt` for historic reasons. 11739 * @namespace 11740 * @extends DataTable.models.ext 11741 */ 11742 11743 11744 var extPagination = DataTable.ext.pager; 11745 11746 // Paging buttons configuration 11747 $.extend( extPagination, { 11748 simple: function () { 11749 return [ 'previous', 'next' ]; 11750 }, 11751 11752 full: function () { 11753 return [ 'first', 'previous', 'next', 'last' ]; 11754 }, 11755 11756 numbers: function () { 11757 return [ 'numbers' ]; 11758 }, 11759 11760 simple_numbers: function () { 11761 return [ 'previous', 'numbers', 'next' ]; 11762 }, 11763 11764 full_numbers: function () { 11765 return [ 'first', 'previous', 'numbers', 'next', 'last' ]; 11766 }, 11767 11768 first_last: function () { 11769 return ['first', 'last']; 11770 }, 11771 11772 first_last_numbers: function () { 11773 return ['first', 'numbers', 'last']; 11774 }, 11775 11776 // For testing and plug-ins to use 11777 _numbers: _pagingNumbers, 11778 11779 // Number of number buttons - legacy, use `numbers` option for paging feature 11780 numbers_length: 7 11781 } ); 11782 11783 11784 $.extend( true, DataTable.ext.renderer, { 11785 pagingButton: { 11786 _: function (settings, buttonType, content, active, disabled) { 11787 var classes = settings.oClasses.paging; 11788 var btnClasses = [classes.button]; 11789 var btn; 11790 11791 if (active) { 11792 btnClasses.push(classes.active); 11793 } 11794 11795 if (disabled) { 11796 btnClasses.push(classes.disabled) 11797 } 11798 11799 if (buttonType === 'ellipsis') { 11800 btn = $('<span class="ellipsis"></span>').html(content)[0]; 11801 } 11802 else { 11803 btn = $('<button>', { 11804 class: btnClasses.join(' '), 11805 role: 'link', 11806 type: 'button' 11807 }).html(content); 11808 } 11809 11810 return { 11811 display: btn, 11812 clicker: btn 11813 } 11814 } 11815 }, 11816 11817 pagingContainer: { 11818 _: function (settings, buttons) { 11819 // No wrapping element - just append directly to the host 11820 return buttons; 11821 } 11822 } 11823 } ); 11824 11825 // Common function to remove new lines, strip HTML and diacritic control 11826 var _filterString = function (stripHtml, normalize) { 11827 return function (str) { 11828 if (_empty(str) || typeof str !== 'string') { 11829 return str; 11830 } 11831 11832 str = str.replace( _re_new_lines, " " ); 11833 11834 if (stripHtml) { 11835 str = _stripHtml(str); 11836 } 11837 11838 if (normalize) { 11839 str = _normalize(str, false); 11840 } 11841 11842 return str; 11843 }; 11844 } 11845 11846 /* 11847 * Public helper functions. These aren't used internally by DataTables, or 11848 * called by any of the options passed into DataTables, but they can be used 11849 * externally by developers working with DataTables. They are helper functions 11850 * to make working with DataTables a little bit easier. 11851 */ 11852 11853 function __mldFnName(name) { 11854 return name.replace(/[\W]/g, '_') 11855 } 11856 11857 // Common logic for moment, luxon or a date action 11858 function __mld( dt, momentFn, luxonFn, dateFn, arg1 ) { 11859 if (window.moment) { 11860 return dt[momentFn]( arg1 ); 11861 } 11862 else if (window.luxon) { 11863 return dt[luxonFn]( arg1 ); 11864 } 11865 11866 return dateFn ? dt[dateFn]( arg1 ) : dt; 11867 } 11868 11869 11870 var __mlWarning = false; 11871 function __mldObj (d, format, locale) { 11872 var dt; 11873 11874 if (window.moment) { 11875 dt = window.moment.utc( d, format, locale, true ); 11876 11877 if (! dt.isValid()) { 11878 return null; 11879 } 11880 } 11881 else if (window.luxon) { 11882 dt = format && typeof d === 'string' 11883 ? window.luxon.DateTime.fromFormat( d, format ) 11884 : window.luxon.DateTime.fromISO( d ); 11885 11886 if (! dt.isValid) { 11887 return null; 11888 } 11889 11890 dt.setLocale(locale); 11891 } 11892 else if (! format) { 11893 // No format given, must be ISO 11894 dt = new Date(d); 11895 } 11896 else { 11897 if (! __mlWarning) { 11898 alert('DataTables warning: Formatted date without Moment.js or Luxon - https://datatables.net/tn/17'); 11899 } 11900 11901 __mlWarning = true; 11902 } 11903 11904 return dt; 11905 } 11906 11907 // Wrapper for date, datetime and time which all operate the same way with the exception of 11908 // the output string for auto locale support 11909 function __mlHelper (localeString) { 11910 return function ( from, to, locale, def ) { 11911 // Luxon and Moment support 11912 // Argument shifting 11913 if ( arguments.length === 0 ) { 11914 locale = 'en'; 11915 to = null; // means toLocaleString 11916 from = null; // means iso8601 11917 } 11918 else if ( arguments.length === 1 ) { 11919 locale = 'en'; 11920 to = from; 11921 from = null; 11922 } 11923 else if ( arguments.length === 2 ) { 11924 locale = to; 11925 to = from; 11926 from = null; 11927 } 11928 11929 var typeName = 'datetime' + (to ? '-' + __mldFnName(to) : ''); 11930 11931 // Add type detection and sorting specific to this date format - we need to be able to identify 11932 // date type columns as such, rather than as numbers in extensions. Hence the need for this. 11933 if (! DataTable.ext.type.order[typeName]) { 11934 DataTable.type(typeName, { 11935 detect: function (d) { 11936 // The renderer will give the value to type detect as the type! 11937 return d === typeName ? typeName : false; 11938 }, 11939 order: { 11940 pre: function (d) { 11941 // The renderer gives us Moment, Luxon or Date obects for the sorting, all of which have a 11942 // `valueOf` which gives milliseconds epoch 11943 return d.valueOf(); 11944 } 11945 }, 11946 className: 'dt-right' 11947 }); 11948 } 11949 11950 return function ( d, type ) { 11951 // Allow for a default value 11952 if (d === null || d === undefined) { 11953 if (def === '--now') { 11954 // We treat everything as UTC further down, so no changes are 11955 // made, as such need to get the local date / time as if it were 11956 // UTC 11957 var local = new Date(); 11958 d = new Date( Date.UTC( 11959 local.getFullYear(), local.getMonth(), local.getDate(), 11960 local.getHours(), local.getMinutes(), local.getSeconds() 11961 ) ); 11962 } 11963 else { 11964 d = ''; 11965 } 11966 } 11967 11968 if (type === 'type') { 11969 // Typing uses the type name for fast matching 11970 return typeName; 11971 } 11972 11973 if (d === '') { 11974 return type !== 'sort' 11975 ? '' 11976 : __mldObj('0000-01-01 00:00:00', null, locale); 11977 } 11978 11979 // Shortcut. If `from` and `to` are the same, we are using the renderer to 11980 // format for ordering, not display - its already in the display format. 11981 if ( to !== null && from === to && type !== 'sort' && type !== 'type' && ! (d instanceof Date) ) { 11982 return d; 11983 } 11984 11985 var dt = __mldObj(d, from, locale); 11986 11987 if (dt === null) { 11988 return d; 11989 } 11990 11991 if (type === 'sort') { 11992 return dt; 11993 } 11994 11995 var formatted = to === null 11996 ? __mld(dt, 'toDate', 'toJSDate', '')[localeString]() 11997 : __mld(dt, 'format', 'toFormat', 'toISOString', to); 11998 11999 // XSS protection 12000 return type === 'display' ? 12001 _escapeHtml( formatted ) : 12002 formatted; 12003 }; 12004 } 12005 } 12006 12007 // Based on locale, determine standard number formatting 12008 // Fallback for legacy browsers is US English 12009 var __thousands = ','; 12010 var __decimal = '.'; 12011 12012 if (window.Intl !== undefined) { 12013 try { 12014 var num = new Intl.NumberFormat().formatToParts(100000.1); 12015 12016 for (var i=0 ; i<num.length ; i++) { 12017 if (num[i].type === 'group') { 12018 __thousands = num[i].value; 12019 } 12020 else if (num[i].type === 'decimal') { 12021 __decimal = num[i].value; 12022 } 12023 } 12024 } 12025 catch (e) { 12026 // noop 12027 } 12028 } 12029 12030 // Formatted date time detection - use by declaring the formats you are going to use 12031 DataTable.datetime = function ( format, locale ) { 12032 var typeName = 'datetime-detect-' + __mldFnName(format); 12033 12034 if (! locale) { 12035 locale = 'en'; 12036 } 12037 12038 if (! DataTable.ext.type.order[typeName]) { 12039 DataTable.type(typeName, { 12040 detect: function (d) { 12041 var dt = __mldObj(d, format, locale); 12042 return d === '' || dt ? typeName : false; 12043 }, 12044 order: { 12045 pre: function (d) { 12046 return __mldObj(d, format, locale) || 0; 12047 } 12048 }, 12049 className: 'dt-right' 12050 }); 12051 } 12052 } 12053 12054 /** 12055 * Helpers for `columns.render`. 12056 * 12057 * The options defined here can be used with the `columns.render` initialisation 12058 * option to provide a display renderer. The following functions are defined: 12059 * 12060 * * `moment` - Uses the MomentJS library to convert from a given format into another. 12061 * This renderer has three overloads: 12062 * * 1 parameter: 12063 * * `string` - Format to convert to (assumes input is ISO8601 and locale is `en`) 12064 * * 2 parameters: 12065 * * `string` - Format to convert from 12066 * * `string` - Format to convert to. Assumes `en` locale 12067 * * 3 parameters: 12068 * * `string` - Format to convert from 12069 * * `string` - Format to convert to 12070 * * `string` - Locale 12071 * * `number` - Will format numeric data (defined by `columns.data`) for 12072 * display, retaining the original unformatted data for sorting and filtering. 12073 * It takes 5 parameters: 12074 * * `string` - Thousands grouping separator 12075 * * `string` - Decimal point indicator 12076 * * `integer` - Number of decimal points to show 12077 * * `string` (optional) - Prefix. 12078 * * `string` (optional) - Postfix (/suffix). 12079 * * `text` - Escape HTML to help prevent XSS attacks. It has no optional 12080 * parameters. 12081 * 12082 * @example 12083 * // Column definition using the number renderer 12084 * { 12085 * data: "salary", 12086 * render: $.fn.dataTable.render.number( '\'', '.', 0, '$' ) 12087 * } 12088 * 12089 * @namespace 12090 */ 12091 DataTable.render = { 12092 date: __mlHelper('toLocaleDateString'), 12093 datetime: __mlHelper('toLocaleString'), 12094 time: __mlHelper('toLocaleTimeString'), 12095 number: function ( thousands, decimal, precision, prefix, postfix ) { 12096 // Auto locale detection 12097 if (thousands === null || thousands === undefined) { 12098 thousands = __thousands; 12099 } 12100 12101 if (decimal === null || decimal === undefined) { 12102 decimal = __decimal; 12103 } 12104 12105 return { 12106 display: function ( d ) { 12107 if ( typeof d !== 'number' && typeof d !== 'string' ) { 12108 return d; 12109 } 12110 12111 if (d === '' || d === null) { 12112 return d; 12113 } 12114 12115 var negative = d < 0 ? '-' : ''; 12116 var flo = parseFloat( d ); 12117 var abs = Math.abs(flo); 12118 12119 // Scientific notation for large and small numbers 12120 if (abs >= 100000000000 || (abs < 0.0001 && abs !== 0) ) { 12121 var exp = flo.toExponential(precision).split(/e\+?/); 12122 return exp[0] + ' x 10<sup>' + exp[1] + '</sup>'; 12123 } 12124 12125 // If NaN then there isn't much formatting that we can do - just 12126 // return immediately, escaping any HTML (this was supposed to 12127 // be a number after all) 12128 if ( isNaN( flo ) ) { 12129 return _escapeHtml( d ); 12130 } 12131 12132 flo = flo.toFixed( precision ); 12133 d = Math.abs( flo ); 12134 12135 var intPart = parseInt( d, 10 ); 12136 var floatPart = precision ? 12137 decimal+(d - intPart).toFixed( precision ).substring( 2 ): 12138 ''; 12139 12140 // If zero, then can't have a negative prefix 12141 if (intPart === 0 && parseFloat(floatPart) === 0) { 12142 negative = ''; 12143 } 12144 12145 return negative + (prefix||'') + 12146 intPart.toString().replace( 12147 /\B(?=(\d{3})+(?!\d))/g, thousands 12148 ) + 12149 floatPart + 12150 (postfix||''); 12151 } 12152 }; 12153 }, 12154 12155 text: function () { 12156 return { 12157 display: _escapeHtml, 12158 filter: _escapeHtml 12159 }; 12160 } 12161 }; 12162 12163 12164 var _extTypes = DataTable.ext.type; 12165 12166 // Get / set type 12167 DataTable.type = function (name, prop, val) { 12168 if (! prop) { 12169 return { 12170 className: _extTypes.className[name], 12171 detect: _extTypes.detect.find(function (fn) { 12172 return fn.name === name; 12173 }), 12174 order: { 12175 pre: _extTypes.order[name + '-pre'], 12176 asc: _extTypes.order[name + '-asc'], 12177 desc: _extTypes.order[name + '-desc'] 12178 }, 12179 render: _extTypes.render[name], 12180 search: _extTypes.search[name] 12181 }; 12182 } 12183 12184 var setProp = function(prop, propVal) { 12185 _extTypes[prop][name] = propVal; 12186 }; 12187 var setDetect = function (fn) { 12188 // Wrap to allow the function to return `true` rather than 12189 // specifying the type name. 12190 var cb = function (d, s) { 12191 var ret = fn(d, s); 12192 12193 return ret === true 12194 ? name 12195 : ret; 12196 }; 12197 Object.defineProperty(cb, "name", {value: name}); 12198 12199 var idx = _extTypes.detect.findIndex(function (fn) { 12200 return fn.name === name; 12201 }); 12202 12203 if (idx === -1) { 12204 _extTypes.detect.unshift(cb); 12205 } 12206 else { 12207 _extTypes.detect.splice(idx, 1, cb); 12208 } 12209 }; 12210 var setOrder = function (obj) { 12211 _extTypes.order[name + '-pre'] = obj.pre; // can be undefined 12212 _extTypes.order[name + '-asc'] = obj.asc; // can be undefined 12213 _extTypes.order[name + '-desc'] = obj.desc; // can be undefined 12214 }; 12215 12216 // prop is optional 12217 if (val === undefined) { 12218 val = prop; 12219 prop = null; 12220 } 12221 12222 if (prop === 'className') { 12223 setProp('className', val); 12224 } 12225 else if (prop === 'detect') { 12226 setDetect(val); 12227 } 12228 else if (prop === 'order') { 12229 setOrder(val); 12230 } 12231 else if (prop === 'render') { 12232 setProp('render', val); 12233 } 12234 else if (prop === 'search') { 12235 setProp('search', val); 12236 } 12237 else if (! prop) { 12238 if (val.className) { 12239 setProp('className', val.className); 12240 } 12241 12242 if (val.detect !== undefined) { 12243 setDetect(val.detect); 12244 } 12245 12246 if (val.order) { 12247 setOrder(val.order); 12248 } 12249 12250 if (val.render !== undefined) { 12251 setProp('render', val.render); 12252 } 12253 12254 if (val.search !== undefined) { 12255 setProp('search', val.search); 12256 } 12257 } 12258 } 12259 12260 // Get a list of types 12261 DataTable.types = function () { 12262 return _extTypes.detect.map(function (fn) { 12263 return fn.name; 12264 }); 12265 }; 12266 12267 // 12268 // Built in data types 12269 // 12270 12271 DataTable.type('string', { 12272 detect: function () { 12273 return 'string'; 12274 }, 12275 order: { 12276 pre: function ( a ) { 12277 // This is a little complex, but faster than always calling toString, 12278 // http://jsperf.com/tostring-v-check 12279 return _empty(a) ? 12280 '' : 12281 typeof a === 'string' ? 12282 a.toLowerCase() : 12283 ! a.toString ? 12284 '' : 12285 a.toString(); 12286 } 12287 }, 12288 search: _filterString(false, true) 12289 }); 12290 12291 12292 DataTable.type('html', { 12293 detect: function ( d ) { 12294 return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1) ? 12295 'html' : null; 12296 }, 12297 order: { 12298 pre: function ( a ) { 12299 return _empty(a) ? 12300 '' : 12301 a.replace ? 12302 _stripHtml(a).trim().toLowerCase() : 12303 a+''; 12304 } 12305 }, 12306 search: _filterString(true, true) 12307 }); 12308 12309 12310 DataTable.type('date', { 12311 className: 'dt-type-date', 12312 detect: function ( d ) 12313 { 12314 // V8 tries _very_ hard to make a string passed into `Date.parse()` 12315 // valid, so we need to use a regex to restrict date formats. Use a 12316 // plug-in for anything other than ISO8601 style strings 12317 if ( d && !(d instanceof Date) && ! _re_date.test(d) ) { 12318 return null; 12319 } 12320 var parsed = Date.parse(d); 12321 return (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null; 12322 }, 12323 order: { 12324 pre: function ( d ) { 12325 var ts = Date.parse( d ); 12326 return isNaN(ts) ? -Infinity : ts; 12327 } 12328 } 12329 }); 12330 12331 12332 DataTable.type('html-num-fmt', { 12333 className: 'dt-type-numeric', 12334 detect: function ( d, settings ) 12335 { 12336 var decimal = settings.oLanguage.sDecimal; 12337 return _htmlNumeric( d, decimal, true ) ? 'html-num-fmt' : null; 12338 }, 12339 order: { 12340 pre: function ( d, s ) { 12341 var dp = s.oLanguage.sDecimal; 12342 return __numericReplace( d, dp, _re_html, _re_formatted_numeric ); 12343 } 12344 }, 12345 search: _filterString(true, true) 12346 }); 12347 12348 12349 DataTable.type('html-num', { 12350 className: 'dt-type-numeric', 12351 detect: function ( d, settings ) 12352 { 12353 var decimal = settings.oLanguage.sDecimal; 12354 return _htmlNumeric( d, decimal ) ? 'html-num' : null; 12355 }, 12356 order: { 12357 pre: function ( d, s ) { 12358 var dp = s.oLanguage.sDecimal; 12359 return __numericReplace( d, dp, _re_html ); 12360 } 12361 }, 12362 search: _filterString(true, true) 12363 }); 12364 12365 12366 DataTable.type('num-fmt', { 12367 className: 'dt-type-numeric', 12368 detect: function ( d, settings ) 12369 { 12370 var decimal = settings.oLanguage.sDecimal; 12371 return _isNumber( d, decimal, true ) ? 'num-fmt' : null; 12372 }, 12373 order: { 12374 pre: function ( d, s ) { 12375 var dp = s.oLanguage.sDecimal; 12376 return __numericReplace( d, dp, _re_formatted_numeric ); 12377 } 12378 } 12379 }); 12380 12381 12382 DataTable.type('num', { 12383 className: 'dt-type-numeric', 12384 detect: function ( d, settings ) 12385 { 12386 var decimal = settings.oLanguage.sDecimal; 12387 return _isNumber( d, decimal ) ? 'num' : null; 12388 }, 12389 order: { 12390 pre: function (d, s) { 12391 var dp = s.oLanguage.sDecimal; 12392 return __numericReplace( d, dp ); 12393 } 12394 } 12395 }); 12396 12397 12398 12399 12400 var __numericReplace = function ( d, decimalPlace, re1, re2 ) { 12401 if ( d !== 0 && (!d || d === '-') ) { 12402 return -Infinity; 12403 } 12404 12405 var type = typeof d; 12406 12407 if (type === 'number' || type === 'bigint') { 12408 return d; 12409 } 12410 12411 // If a decimal place other than `.` is used, it needs to be given to the 12412 // function so we can detect it and replace with a `.` which is the only 12413 // decimal place Javascript recognises - it is not locale aware. 12414 if ( decimalPlace ) { 12415 d = _numToDecimal( d, decimalPlace ); 12416 } 12417 12418 if ( d.replace ) { 12419 if ( re1 ) { 12420 d = d.replace( re1, '' ); 12421 } 12422 12423 if ( re2 ) { 12424 d = d.replace( re2, '' ); 12425 } 12426 } 12427 12428 return d * 1; 12429 }; 12430 12431 12432 $.extend( true, DataTable.ext.renderer, { 12433 footer: { 12434 _: function ( settings, cell, classes ) { 12435 cell.addClass(classes.tfoot.cell); 12436 } 12437 }, 12438 12439 header: { 12440 _: function ( settings, cell, classes ) { 12441 cell.addClass(classes.thead.cell); 12442 12443 if (! settings.oFeatures.bSort) { 12444 cell.addClass(classes.order.none); 12445 } 12446 12447 var legacyTop = settings.bSortCellsTop; 12448 var headerRows = cell.closest('thead').find('tr'); 12449 var rowIdx = cell.parent().index(); 12450 12451 // Conditions to not apply the ordering icons 12452 if ( 12453 // Cells and rows which have the attribute to disable the icons 12454 cell.attr('data-dt-order') === 'disable' || 12455 cell.parent().attr('data-dt-order') === 'disable' || 12456 12457 // Legacy support for `orderCellsTop`. If it is set, then cells 12458 // which are not in the top or bottom row of the header (depending 12459 // on the value) do not get the sorting classes applied to them 12460 (legacyTop === true && rowIdx !== 0) || 12461 (legacyTop === false && rowIdx !== headerRows.length - 1) 12462 ) { 12463 return; 12464 } 12465 12466 // No additional mark-up required 12467 // Attach a sort listener to update on sort - note that using the 12468 // `DT` namespace will allow the event to be removed automatically 12469 // on destroy, while the `dt` namespaced event is the one we are 12470 // listening for 12471 $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting ) { 12472 if ( settings !== ctx ) { // need to check this this is the host 12473 return; // table, not a nested one 12474 } 12475 12476 var orderClasses = classes.order; 12477 var columns = ctx.api.columns( cell ); 12478 var col = settings.aoColumns[columns.flatten()[0]]; 12479 var orderable = columns.orderable().includes(true); 12480 var ariaType = ''; 12481 var indexes = columns.indexes(); 12482 var sortDirs = columns.orderable(true).flatten(); 12483 var orderedColumns = ',' + sorting.map( function (val) { 12484 return val.col; 12485 } ).join(',') + ','; 12486 12487 cell 12488 .removeClass( 12489 orderClasses.isAsc +' '+ 12490 orderClasses.isDesc 12491 ) 12492 .toggleClass( orderClasses.none, ! orderable ) 12493 .toggleClass( orderClasses.canAsc, orderable && sortDirs.includes('asc') ) 12494 .toggleClass( orderClasses.canDesc, orderable && sortDirs.includes('desc') ); 12495 12496 var sortIdx = orderedColumns.indexOf( ',' + indexes.toArray().join(',') + ',' ); 12497 12498 if ( sortIdx !== -1 ) { 12499 // Get the ordering direction for the columns under this cell 12500 // Note that it is possible for a cell to be asc and desc sorting 12501 // (column spanning cells) 12502 var orderDirs = columns.order(); 12503 12504 cell.addClass( 12505 orderDirs.includes('asc') ? orderClasses.isAsc : '' + 12506 orderDirs.includes('desc') ? orderClasses.isDesc : '' 12507 ); 12508 } 12509 12510 // The ARIA spec says that only one column should be marked with aria-sort 12511 if ( sortIdx === 0 ) { 12512 var firstSort = sorting[0]; 12513 var sortOrder = col.asSorting; 12514 12515 cell.attr('aria-sort', firstSort.dir === 'asc' ? 'ascending' : 'descending'); 12516 12517 // Determine if the next click will remove sorting or change the sort 12518 ariaType = ! sortOrder[firstSort.index + 1] ? 'Remove' : 'Reverse'; 12519 } 12520 else { 12521 cell.removeAttr('aria-sort'); 12522 } 12523 12524 cell.attr('aria-label', orderable 12525 ? col.ariaTitle + ctx.api.i18n('oAria.orderable' + ariaType) 12526 : col.ariaTitle 12527 ); 12528 12529 if (orderable) { 12530 cell.find('.dt-column-title').attr('role', 'button'); 12531 cell.attr('tabindex', 0) 12532 } 12533 } ); 12534 } 12535 }, 12536 12537 layout: { 12538 _: function ( settings, container, items ) { 12539 var row = $('<div/>') 12540 .addClass('dt-layout-row') 12541 .appendTo( container ); 12542 12543 $.each( items, function (key, val) { 12544 var klass = ! val.table ? 12545 'dt-'+key+' ' : 12546 ''; 12547 12548 if (val.table) { 12549 row.addClass('dt-layout-table'); 12550 } 12551 12552 $('<div/>') 12553 .attr({ 12554 id: val.id || null, 12555 "class": 'dt-layout-cell '+klass+(val.className || '') 12556 }) 12557 .append( val.contents ) 12558 .appendTo( row ); 12559 } ); 12560 } 12561 } 12562 } ); 12563 12564 12565 DataTable.feature = {}; 12566 12567 // Third parameter is internal only! 12568 DataTable.feature.register = function ( name, cb, legacy ) { 12569 DataTable.ext.features[ name ] = cb; 12570 12571 if (legacy) { 12572 _ext.feature.push({ 12573 cFeature: legacy, 12574 fnInit: cb 12575 }); 12576 } 12577 }; 12578 12579 DataTable.feature.register( 'info', function ( settings, opts ) { 12580 // For compatibility with the legacy `info` top level option 12581 if (! settings.oFeatures.bInfo) { 12582 return null; 12583 } 12584 12585 var 12586 lang = settings.oLanguage, 12587 tid = settings.sTableId, 12588 n = $('<div/>', { 12589 'class': settings.oClasses.info.container, 12590 } ); 12591 12592 opts = $.extend({ 12593 callback: lang.fnInfoCallback, 12594 empty: lang.sInfoEmpty, 12595 postfix: lang.sInfoPostFix, 12596 search: lang.sInfoFiltered, 12597 text: lang.sInfo, 12598 }, opts); 12599 12600 12601 // Update display on each draw 12602 settings.aoDrawCallback.push(function (s) { 12603 _fnUpdateInfo(s, opts, n); 12604 }); 12605 12606 // For the first info display in the table, we add a callback and aria information. 12607 if (! settings._infoEl) { 12608 n.attr({ 12609 'aria-live': 'polite', 12610 id: tid+'_info', 12611 role: 'status' 12612 }); 12613 12614 // Table is described by our info div 12615 $(settings.nTable).attr( 'aria-describedby', tid+'_info' ); 12616 12617 settings._infoEl = n; 12618 } 12619 12620 return n; 12621 }, 'i' ); 12622 12623 /** 12624 * Update the information elements in the display 12625 * @param {object} settings dataTables settings object 12626 * @memberof DataTable#oApi 12627 */ 12628 function _fnUpdateInfo ( settings, opts, node ) 12629 { 12630 var 12631 start = settings._iDisplayStart+1, 12632 end = settings.fnDisplayEnd(), 12633 max = settings.fnRecordsTotal(), 12634 total = settings.fnRecordsDisplay(), 12635 out = total 12636 ? opts.text 12637 : opts.empty; 12638 12639 if ( total !== max ) { 12640 // Record set after filtering 12641 out += ' ' + opts.search; 12642 } 12643 12644 // Convert the macros 12645 out += opts.postfix; 12646 out = _fnMacros( settings, out ); 12647 12648 if ( opts.callback ) { 12649 out = opts.callback.call( settings.oInstance, 12650 settings, start, end, max, total, out 12651 ); 12652 } 12653 12654 node.html( out ); 12655 12656 _fnCallbackFire(settings, null, 'info', [settings, node[0], out]); 12657 } 12658 12659 var __searchCounter = 0; 12660 12661 // opts 12662 // - text 12663 // - placeholder 12664 DataTable.feature.register( 'search', function ( settings, opts ) { 12665 // Don't show the input if filtering isn't available on the table 12666 if (! settings.oFeatures.bFilter) { 12667 return null; 12668 } 12669 12670 var classes = settings.oClasses.search; 12671 var tableId = settings.sTableId; 12672 var language = settings.oLanguage; 12673 var previousSearch = settings.oPreviousSearch; 12674 var input = '<input type="search" class="'+classes.input+'"/>'; 12675 12676 opts = $.extend({ 12677 placeholder: language.sSearchPlaceholder, 12678 text: language.sSearch 12679 }, opts); 12680 12681 // The _INPUT_ is optional - is appended if not present 12682 if (opts.text.indexOf('_INPUT_') === -1) { 12683 opts.text += '_INPUT_'; 12684 } 12685 12686 opts.text = _fnMacros(settings, opts.text); 12687 12688 // We can put the <input> outside of the label if it is at the start or end 12689 // which helps improve accessability (not all screen readers like implicit 12690 // for elements). 12691 var end = opts.text.match(/_INPUT_$/); 12692 var start = opts.text.match(/^_INPUT_/); 12693 var removed = opts.text.replace(/_INPUT_/, ''); 12694 var str = '<label>' + opts.text + '</label>'; 12695 12696 if (start) { 12697 str = '_INPUT_<label>' + removed + '</label>'; 12698 } 12699 else if (end) { 12700 str = '<label>' + removed + '</label>_INPUT_'; 12701 } 12702 12703 var filter = $('<div>') 12704 .addClass(classes.container) 12705 .append(str.replace(/_INPUT_/, input)); 12706 12707 // add for and id to label and input 12708 filter.find('label').attr('for', 'dt-search-' + __searchCounter); 12709 filter.find('input').attr('id', 'dt-search-' + __searchCounter); 12710 __searchCounter++; 12711 12712 var searchFn = function(event) { 12713 var val = this.value; 12714 12715 if(previousSearch.return && event.key !== "Enter") { 12716 return; 12717 } 12718 12719 /* Now do the filter */ 12720 if ( val != previousSearch.search ) { 12721 previousSearch.search = val; 12722 12723 _fnFilterComplete( settings, previousSearch ); 12724 12725 // Need to redraw, without resorting 12726 settings._iDisplayStart = 0; 12727 _fnDraw( settings ); 12728 } 12729 }; 12730 12731 var searchDelay = settings.searchDelay !== null ? 12732 settings.searchDelay : 12733 0; 12734 12735 var jqFilter = $('input', filter) 12736 .val( previousSearch.search ) 12737 .attr( 'placeholder', opts.placeholder ) 12738 .on( 12739 'keyup.DT search.DT input.DT paste.DT cut.DT', 12740 searchDelay ? 12741 DataTable.util.debounce( searchFn, searchDelay ) : 12742 searchFn 12743 ) 12744 .on( 'mouseup.DT', function(e) { 12745 // Edge fix! Edge 17 does not trigger anything other than mouse events when clicking 12746 // on the clear icon (Edge bug 17584515). This is safe in other browsers as `searchFn` 12747 // checks the value to see if it has changed. In other browsers it won't have. 12748 setTimeout( function () { 12749 searchFn.call(jqFilter[0], e); 12750 }, 10); 12751 } ) 12752 .on( 'keypress.DT', function(e) { 12753 /* Prevent form submission */ 12754 if ( e.keyCode == 13 ) { 12755 return false; 12756 } 12757 } ) 12758 .attr('aria-controls', tableId); 12759 12760 // Update the input elements whenever the table is filtered 12761 $(settings.nTable).on( 'search.dt.DT', function ( ev, s ) { 12762 if ( settings === s && jqFilter[0] !== document.activeElement ) { 12763 jqFilter.val( typeof previousSearch.search !== 'function' 12764 ? previousSearch.search 12765 : '' 12766 ); 12767 } 12768 } ); 12769 12770 return filter; 12771 }, 'f' ); 12772 12773 // opts 12774 // - type - button configuration 12775 // - buttons - number of buttons to show - must be odd 12776 DataTable.feature.register( 'paging', function ( settings, opts ) { 12777 // Don't show the paging input if the table doesn't have paging enabled 12778 if (! settings.oFeatures.bPaginate) { 12779 return null; 12780 } 12781 12782 opts = $.extend({ 12783 buttons: DataTable.ext.pager.numbers_length, 12784 type: settings.sPaginationType, 12785 boundaryNumbers: true 12786 }, opts); 12787 12788 // To be removed in 2.1 12789 if (opts.numbers) { 12790 opts.buttons = opts.numbers; 12791 } 12792 12793 var host = $('<div/>').addClass( settings.oClasses.paging.container + ' paging_' + opts.type ); 12794 var draw = function () { 12795 _pagingDraw(settings, host, opts); 12796 }; 12797 12798 settings.aoDrawCallback.push(draw); 12799 12800 // Responsive redraw of paging control 12801 $(settings.nTable).on('column-sizing.dt.DT', draw); 12802 12803 return host; 12804 }, 'p' ); 12805 12806 function _pagingDraw(settings, host, opts) { 12807 if (! settings._bInitComplete) { 12808 return; 12809 } 12810 12811 var 12812 plugin = DataTable.ext.pager[ opts.type ], 12813 aria = settings.oLanguage.oAria.paginate || {}, 12814 start = settings._iDisplayStart, 12815 len = settings._iDisplayLength, 12816 visRecords = settings.fnRecordsDisplay(), 12817 all = len === -1, 12818 page = all ? 0 : Math.ceil( start / len ), 12819 pages = all ? 1 : Math.ceil( visRecords / len ), 12820 buttons = plugin() 12821 .map(function (val) { 12822 return val === 'numbers' 12823 ? _pagingNumbers(page, pages, opts.buttons, opts.boundaryNumbers) 12824 : val; 12825 }) 12826 .flat(); 12827 12828 var buttonEls = []; 12829 12830 for (var i=0 ; i<buttons.length ; i++) { 12831 var button = buttons[i]; 12832 12833 var btnInfo = _pagingButtonInfo(settings, button, page, pages); 12834 var btn = _fnRenderer( settings, 'pagingButton' )( 12835 settings, 12836 button, 12837 btnInfo.display, 12838 btnInfo.active, 12839 btnInfo.disabled 12840 ); 12841 12842 // Common attributes 12843 $(btn.clicker).attr({ 12844 'aria-controls': settings.sTableId, 12845 'aria-disabled': btnInfo.disabled ? 'true' : null, 12846 'aria-current': btnInfo.active ? 'page' : null, 12847 'aria-label': aria[ button ], 12848 'data-dt-idx': button, 12849 'tabIndex': btnInfo.disabled ? -1 : settings.iTabIndex, 12850 }); 12851 12852 if (typeof button !== 'number') { 12853 $(btn.clicker).addClass(button); 12854 } 12855 12856 _fnBindAction( 12857 btn.clicker, {action: button}, function(e) { 12858 e.preventDefault(); 12859 12860 _fnPageChange( settings, e.data.action, true ); 12861 } 12862 ); 12863 12864 buttonEls.push(btn.display); 12865 } 12866 12867 var wrapped = _fnRenderer(settings, 'pagingContainer')( 12868 settings, buttonEls 12869 ); 12870 12871 var activeEl = host.find(document.activeElement).data('dt-idx'); 12872 12873 host.empty().append(wrapped); 12874 12875 if ( activeEl !== undefined ) { 12876 host.find( '[data-dt-idx='+activeEl+']' ).trigger('focus'); 12877 } 12878 12879 // Responsive - check if the buttons are over two lines based on the 12880 // height of the buttons and the container. 12881 if ( 12882 buttonEls.length && // any buttons 12883 opts.numbers > 1 && // prevent infinite 12884 $(host).height() >= ($(buttonEls[0]).outerHeight() * 2) - 10 12885 ) { 12886 _pagingDraw(settings, host, $.extend({}, opts, { numbers: opts.numbers - 2 })); 12887 } 12888 } 12889 12890 /** 12891 * Get properties for a button based on the current paging state of the table 12892 * 12893 * @param {*} settings DT settings object 12894 * @param {*} button The button type in question 12895 * @param {*} page Table's current page 12896 * @param {*} pages Number of pages 12897 * @returns Info object 12898 */ 12899 function _pagingButtonInfo(settings, button, page, pages) { 12900 var lang = settings.oLanguage.oPaginate; 12901 var o = { 12902 display: '', 12903 active: false, 12904 disabled: false 12905 }; 12906 12907 switch ( button ) { 12908 case 'ellipsis': 12909 o.display = '…'; 12910 o.disabled = true; 12911 break; 12912 12913 case 'first': 12914 o.display = lang.sFirst; 12915 12916 if (page === 0) { 12917 o.disabled = true; 12918 } 12919 break; 12920 12921 case 'previous': 12922 o.display = lang.sPrevious; 12923 12924 if ( page === 0 ) { 12925 o.disabled = true; 12926 } 12927 break; 12928 12929 case 'next': 12930 o.display = lang.sNext; 12931 12932 if ( pages === 0 || page === pages-1 ) { 12933 o.disabled = true; 12934 } 12935 break; 12936 12937 case 'last': 12938 o.display = lang.sLast; 12939 12940 if ( pages === 0 || page === pages-1 ) { 12941 o.disabled = true; 12942 } 12943 break; 12944 12945 default: 12946 if ( typeof button === 'number' ) { 12947 o.display = settings.fnFormatNumber( button + 1 ); 12948 12949 if (page === button) { 12950 o.active = true; 12951 } 12952 } 12953 break; 12954 } 12955 12956 return o; 12957 } 12958 12959 /** 12960 * Compute what number buttons to show in the paging control 12961 * 12962 * @param {*} page Current page 12963 * @param {*} pages Total number of pages 12964 * @param {*} buttons Target number of number buttons 12965 * @param {boolean} addFirstLast Indicate if page 1 and end should be included 12966 * @returns Buttons to show 12967 */ 12968 function _pagingNumbers ( page, pages, buttons, addFirstLast ) { 12969 var 12970 numbers = [], 12971 half = Math.floor(buttons / 2), 12972 before = addFirstLast ? 2 : 1, 12973 after = addFirstLast ? 1 : 0; 12974 12975 if ( pages <= buttons ) { 12976 numbers = _range(0, pages); 12977 } 12978 else if (buttons === 1) { 12979 // Single button - current page only 12980 numbers = [page]; 12981 } 12982 else if (buttons === 3) { 12983 // Special logic for just three buttons 12984 if (page <= 1) { 12985 numbers = [0, 1, 'ellipsis']; 12986 } 12987 else if (page >= pages - 2) { 12988 numbers = _range(pages-2, pages); 12989 numbers.unshift('ellipsis'); 12990 } 12991 else { 12992 numbers = ['ellipsis', page, 'ellipsis']; 12993 } 12994 } 12995 else if ( page <= half ) { 12996 numbers = _range(0, buttons-before); 12997 numbers.push('ellipsis'); 12998 12999 if (addFirstLast) { 13000 numbers.push(pages-1); 13001 } 13002 } 13003 else if ( page >= pages - 1 - half ) { 13004 numbers = _range(pages-(buttons-before), pages); 13005 numbers.unshift('ellipsis'); 13006 13007 if (addFirstLast) { 13008 numbers.unshift(0); 13009 } 13010 } 13011 else { 13012 numbers = _range(page-half+before, page+half-after); 13013 numbers.push('ellipsis'); 13014 numbers.unshift('ellipsis'); 13015 13016 if (addFirstLast) { 13017 numbers.push(pages-1); 13018 numbers.unshift(0); 13019 } 13020 } 13021 13022 return numbers; 13023 } 13024 13025 var __lengthCounter = 0; 13026 13027 // opts 13028 // - menu 13029 // - text 13030 DataTable.feature.register( 'pageLength', function ( settings, opts ) { 13031 var features = settings.oFeatures; 13032 13033 // For compatibility with the legacy `pageLength` top level option 13034 if (! features.bPaginate || ! features.bLengthChange) { 13035 return null; 13036 } 13037 13038 opts = $.extend({ 13039 menu: settings.aLengthMenu, 13040 text: settings.oLanguage.sLengthMenu 13041 }, opts); 13042 13043 var 13044 classes = settings.oClasses.length, 13045 tableId = settings.sTableId, 13046 menu = opts.menu, 13047 lengths = [], 13048 language = [], 13049 i; 13050 13051 // Options can be given in a number of ways 13052 if (Array.isArray( menu[0] )) { 13053 // Old 1.x style - 2D array 13054 lengths = menu[0]; 13055 language = menu[1]; 13056 } 13057 else { 13058 for ( i=0 ; i<menu.length ; i++ ) { 13059 // An object with different label and value 13060 if ($.isPlainObject(menu[i])) { 13061 lengths.push(menu[i].value); 13062 language.push(menu[i].label); 13063 } 13064 else { 13065 // Or just a number to display and use 13066 lengths.push(menu[i]); 13067 language.push(menu[i]); 13068 } 13069 } 13070 } 13071 13072 // We can put the <select> outside of the label if it is at the start or 13073 // end which helps improve accessability (not all screen readers like 13074 // implicit for elements). 13075 var end = opts.text.match(/_MENU_$/); 13076 var start = opts.text.match(/^_MENU_/); 13077 var removed = opts.text.replace(/_MENU_/, ''); 13078 var str = '<label>' + opts.text + '</label>'; 13079 13080 if (start) { 13081 str = '_MENU_<label>' + removed + '</label>'; 13082 } 13083 else if (end) { 13084 str = '<label>' + removed + '</label>_MENU_'; 13085 } 13086 13087 // Wrapper element - use a span as a holder for where the select will go 13088 var div = $('<div/>') 13089 .addClass( classes.container ) 13090 .append( 13091 str.replace( '_MENU_', '<span></span>' ) 13092 ); 13093 13094 // Save text node content for macro updating 13095 var textNodes = []; 13096 div.find('label')[0].childNodes.forEach(function (el) { 13097 if (el.nodeType === Node.TEXT_NODE) { 13098 textNodes.push({ 13099 el: el, 13100 text: el.textContent 13101 }); 13102 } 13103 }) 13104 13105 // Update the label text in case it has an entries value 13106 var updateEntries = function (len) { 13107 textNodes.forEach(function (node) { 13108 node.el.textContent = _fnMacros(settings, node.text, len); 13109 }); 13110 } 13111 13112 // Next, the select itself, along with the options 13113 var select = $('<select/>', { 13114 'name': tableId+'_length', 13115 'aria-controls': tableId, 13116 'class': classes.select 13117 } ); 13118 13119 for ( i=0 ; i<lengths.length ; i++ ) { 13120 select[0][ i ] = new Option( 13121 typeof language[i] === 'number' ? 13122 settings.fnFormatNumber( language[i] ) : 13123 language[i], 13124 lengths[i] 13125 ); 13126 } 13127 13128 // add for and id to label and input 13129 div.find('label').attr('for', 'dt-length-' + __lengthCounter); 13130 select.attr('id', 'dt-length-' + __lengthCounter); 13131 __lengthCounter++; 13132 13133 // Swap in the select list 13134 div.find('span').replaceWith(select); 13135 13136 // Can't use `select` variable as user might provide their own and the 13137 // reference is broken by the use of outerHTML 13138 $('select', div) 13139 .val( settings._iDisplayLength ) 13140 .on( 'change.DT', function() { 13141 _fnLengthChange( settings, $(this).val() ); 13142 _fnDraw( settings ); 13143 } ); 13144 13145 // Update node value whenever anything changes the table's length 13146 $(settings.nTable).on( 'length.dt.DT', function (e, s, len) { 13147 if ( settings === s ) { 13148 $('select', div).val( len ); 13149 13150 // Resolve plurals in the text for the new length 13151 updateEntries(len); 13152 } 13153 } ); 13154 13155 updateEntries(settings._iDisplayLength); 13156 13157 return div; 13158 }, 'l' ); 13159 13160 // jQuery access 13161 $.fn.dataTable = DataTable; 13162 13163 // Provide access to the host jQuery object (circular reference) 13164 DataTable.$ = $; 13165 13166 // Legacy aliases 13167 $.fn.dataTableSettings = DataTable.settings; 13168 $.fn.dataTableExt = DataTable.ext; 13169 13170 // With a capital `D` we return a DataTables API instance rather than a 13171 // jQuery object 13172 $.fn.DataTable = function ( opts ) { 13173 return $(this).dataTable( opts ).api(); 13174 }; 13175 13176 // All properties that are available to $.fn.dataTable should also be 13177 // available on $.fn.DataTable 13178 $.each( DataTable, function ( prop, val ) { 13179 $.fn.DataTable[ prop ] = val; 13180 } ); 13181 13182 return DataTable; 13183 })); 13184 13185 13186 /*! DataTables Bootstrap 5 integration 13187 * © SpryMedia Ltd - datatables.net/license 13188 */ 13189 13190 (function( factory ){ 13191 if ( typeof define === 'function' && define.amd ) { 13192 // AMD 13193 define( ['jquery', 'datatables.net'], function ( $ ) { 13194 return factory( $, window, document ); 13195 } ); 13196 } 13197 else if ( typeof exports === 'object' ) { 13198 // CommonJS 13199 var jq = require('jquery'); 13200 var cjsRequires = function (root, $) { 13201 if ( ! $.fn.dataTable ) { 13202 require('datatables.net')(root, $); 13203 } 13204 }; 13205 13206 if (typeof window === 'undefined') { 13207 module.exports = function (root, $) { 13208 if ( ! root ) { 13209 // CommonJS environments without a window global must pass a 13210 // root. This will give an error otherwise 13211 root = window; 13212 } 13213 13214 if ( ! $ ) { 13215 $ = jq( root ); 13216 } 13217 13218 cjsRequires( root, $ ); 13219 return factory( $, root, root.document ); 13220 }; 13221 } 13222 else { 13223 cjsRequires( window, jq ); 13224 module.exports = factory( jq, window, window.document ); 13225 } 13226 } 13227 else { 13228 // Browser 13229 factory( jQuery, window, document ); 13230 } 13231 }(function( $, window, document ) { 13232 'use strict'; 13233 var DataTable = $.fn.dataTable; 13234 13235 13236 13237 /** 13238 * DataTables integration for Bootstrap 5. 13239 * 13240 * This file sets the defaults and adds options to DataTables to style its 13241 * controls using Bootstrap. See https://datatables.net/manual/styling/bootstrap 13242 * for further information. 13243 */ 13244 13245 /* Set the defaults for DataTables initialisation */ 13246 $.extend( true, DataTable.defaults, { 13247 renderer: 'bootstrap' 13248 } ); 13249 13250 13251 /* Default class modification */ 13252 $.extend( true, DataTable.ext.classes, { 13253 container: "dt-container dt-bootstrap5", 13254 search: { 13255 input: "form-control form-control-sm" 13256 }, 13257 length: { 13258 select: "form-select form-select-sm" 13259 }, 13260 processing: { 13261 container: "dt-processing card" 13262 } 13263 } ); 13264 13265 13266 /* Bootstrap paging button renderer */ 13267 DataTable.ext.renderer.pagingButton.bootstrap = function (settings, buttonType, content, active, disabled) { 13268 var btnClasses = ['dt-paging-button', 'page-item']; 13269 13270 if (active) { 13271 btnClasses.push('active'); 13272 } 13273 13274 if (disabled) { 13275 btnClasses.push('disabled') 13276 } 13277 13278 var li = $('<li>').addClass(btnClasses.join(' ')); 13279 var a = $('<a>', { 13280 'href': disabled ? null : '#', 13281 'class': 'page-link' 13282 }) 13283 .html(content) 13284 .appendTo(li); 13285 13286 return { 13287 display: li, 13288 clicker: a 13289 }; 13290 }; 13291 13292 DataTable.ext.renderer.pagingContainer.bootstrap = function (settings, buttonEls) { 13293 return $('<ul/>').addClass('pagination').append(buttonEls); 13294 }; 13295 13296 DataTable.ext.renderer.layout.bootstrap = function ( settings, container, items ) { 13297 var row = $( '<div/>', { 13298 "class": items.full ? 13299 'row mt-2 justify-content-md-center' : 13300 'row mt-2 justify-content-between' 13301 } ) 13302 .appendTo( container ); 13303 13304 $.each( items, function (key, val) { 13305 var klass; 13306 13307 // Apply start / end (left / right when ltr) margins 13308 if (val.table) { 13309 klass = 'col-12'; 13310 } 13311 else if (key === 'start') { 13312 klass = 'col-md-auto me-auto'; 13313 } 13314 else if (key === 'end') { 13315 klass = 'col-md-auto ms-auto'; 13316 } 13317 else { 13318 klass = 'col-md'; 13319 } 13320 13321 $( '<div/>', { 13322 id: val.id || null, 13323 "class": klass + ' ' + (val.className || '') 13324 } ) 13325 .append( val.contents ) 13326 .appendTo( row ); 13327 } ); 13328 }; 13329 13330 13331 return DataTable; 13332 })); 13333 13334