1/*! DataTables 2.3.1 2 * © SpryMedia Ltd - datatables.net/license 3 */ 4 5(function( factory ) { 6 "use strict"; 7 8 if ( typeof define === 'function' && define.amd ) { 9 // AMD 10 define( ['jquery'], function ( $ ) { 11 return factory( $, window, document ); 12 } ); 13 } 14 else if ( typeof exports === 'object' ) { 15 // CommonJS 16 // jQuery's factory checks for a global window - if it isn't present then it 17 // returns a factory function that expects the window object 18 var jq = require('jquery'); 19 20 if (typeof window === 'undefined') { 21 module.exports = function (root, $) { 22 if ( ! root ) { 23 // CommonJS environments without a window global must pass a 24 // root. This will give an error otherwise 25 root = window; 26 } 27 28 if ( ! $ ) { 29 $ = jq( root ); 30 } 31 32 return factory( $, root, root.document ); 33 }; 34 } 35 else { 36 module.exports = factory( jq, window, window.document ); 37 } 38 } 39 else { 40 // Browser 41 window.DataTable = factory( jQuery, window, document ); 42 } 43}(function( $, window, document ) { 44 "use strict"; 45 46 47 var DataTable = function ( selector, options ) 48 { 49 // Check if called with a window or jQuery object for DOM less applications 50 // This is for backwards compatibility 51 if (DataTable.factory(selector, options)) { 52 return DataTable; 53 } 54 55 // When creating with `new`, create a new DataTable, returning the API instance 56 if (this instanceof DataTable) { 57 return $(selector).DataTable(options); 58 } 59 else { 60 // Argument switching 61 options = selector; 62 } 63 64 var _that = this; 65 var emptyInit = options === undefined; 66 var len = this.length; 67 68 if ( emptyInit ) { 69 options = {}; 70 } 71 72 // Method to get DT API instance from jQuery object 73 this.api = function () 74 { 75 return new _Api( this ); 76 }; 77 78 this.each(function() { 79 // For each initialisation we want to give it a clean initialisation 80 // object that can be bashed around 81 var o = {}; 82 var oInit = len > 1 ? // optimisation for single table case 83 _fnExtend( o, options, true ) : 84 options; 85 86 87 var i=0, iLen; 88 var sId = this.getAttribute( 'id' ); 89 var defaults = DataTable.defaults; 90 var $this = $(this); 91 92 // Sanity check 93 if ( this.nodeName.toLowerCase() != 'table' ) 94 { 95 _fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 ); 96 return; 97 } 98 99 // Special case for options 100 if (oInit.on && oInit.on.options) { 101 _fnListener($this, 'options', oInit.on.options); 102 } 103 104 $this.trigger( 'options.dt', oInit ); 105 106 /* Backwards compatibility for the defaults */ 107 _fnCompatOpts( defaults ); 108 _fnCompatCols( defaults.column ); 109 110 /* Convert the camel-case defaults to Hungarian */ 111 _fnCamelToHungarian( defaults, defaults, true ); 112 _fnCamelToHungarian( defaults.column, defaults.column, true ); 113 114 /* Setting up the initialisation object */ 115 _fnCamelToHungarian( defaults, $.extend( oInit, $this.data() ), true ); 116 117 118 119 /* Check to see if we are re-initialising a table */ 120 var allSettings = DataTable.settings; 121 for ( i=0, iLen=allSettings.length ; i<iLen ; i++ ) 122 { 123 var s = allSettings[i]; 124 125 /* Base check on table node */ 126 if ( 127 s.nTable == this || 128 (s.nTHead && s.nTHead.parentNode == this) || 129 (s.nTFoot && s.nTFoot.parentNode == this) 130 ) { 131 var bRetrieve = oInit.bRetrieve !== undefined ? oInit.bRetrieve : defaults.bRetrieve; 132 var bDestroy = oInit.bDestroy !== undefined ? oInit.bDestroy : defaults.bDestroy; 133 134 if ( emptyInit || bRetrieve ) 135 { 136 return s.oInstance; 137 } 138 else if ( bDestroy ) 139 { 140 new DataTable.Api(s).destroy(); 141 break; 142 } 143 else 144 { 145 _fnLog( s, 0, 'Cannot reinitialise DataTable', 3 ); 146 return; 147 } 148 } 149 150 /* If the element we are initialising has the same ID as a table which was previously 151 * initialised, but the table nodes don't match (from before) then we destroy the old 152 * instance by simply deleting it. This is under the assumption that the table has been 153 * destroyed by other methods. Anyone using non-id selectors will need to do this manually 154 */ 155 if ( s.sTableId == this.id ) 156 { 157 allSettings.splice( i, 1 ); 158 break; 159 } 160 } 161 162 /* Ensure the table has an ID - required for accessibility */ 163 if ( sId === null || sId === "" ) 164 { 165 sId = "DataTables_Table_"+(DataTable.ext._unique++); 166 this.id = sId; 167 } 168 169 /* Create the settings object for this table and set some of the default parameters */ 170 var oSettings = $.extend( true, {}, DataTable.models.oSettings, { 171 "sDestroyWidth": $this[0].style.width, 172 "sInstance": sId, 173 "sTableId": sId, 174 colgroup: $('<colgroup>').prependTo(this), 175 fastData: function (row, column, type) {
176 return _fnGetCellData(oSettings, row, column, type); 177 } 178 } ); 179 oSettings.nTable = this; 180 oSettings.oInit = oInit; 181 182 allSettings.push( oSettings ); 183 184 // Make a single API instance available for internal handling 185 oSettings.api = new _Api( oSettings ); 186 187 // Need to add the instance after the instance after the settings object has been added 188 // to the settings array, so we can self reference the table instance if more than one 189 oSettings.oInstance = (_that.length===1) ? _that : $this.dataTable(); 190 191 // Backwards compatibility, before we apply all the defaults 192 _fnCompatOpts( oInit ); 193 194 // If the length menu is given, but the init display length is not, use the length menu 195 if ( oInit.aLengthMenu && ! oInit.iDisplayLength ) 196 { 197 oInit.iDisplayLength = Array.isArray(oInit.aLengthMenu[0]) 198 ? oInit.aLengthMenu[0][0] 199 : $.isPlainObject( oInit.aLengthMenu[0] ) 200 ? oInit.aLengthMenu[0].value 201 : oInit.aLengthMenu[0]; 202 } 203 204 // Apply the defaults and init options to make a single init object will all 205 // options defined from defaults and instance options. 206 oInit = _fnExtend( $.extend( true, {}, defaults ), oInit ); 207 208 209 // Map the initialisation options onto the settings object 210 _fnMap( oSettings.oFeatures, oInit, [ 211 "bPaginate", 212 "bLengthChange", 213 "bFilter", 214 "bSort", 215 "bSortMulti", 216 "bInfo", 217 "bProcessing", 218 "bAutoWidth", 219 "bSortClasses", 220 "bServerSide", 221 "bDeferRender" 222 ] ); 223 _fnMap( oSettings, oInit, [ 224 "ajax", 225 "fnFormatNumber", 226 "sServerMethod", 227 "aaSorting", 228 "aaSortingFixed", 229 "aLengthMenu", 230 "sPaginationType", 231 "iStateDuration", 232 "bSortCellsTop", 233 "iTabIndex", 234 "sDom", 235 "fnStateLoadCallback", 236 "fnStateSaveCallback", 237 "renderer", 238 "searchDelay", 239 "rowId", 240 "caption", 241 "layout", 242 "orderDescReverse", 243 "orderIndicators", 244 "orderHandler", 245 "titleRow", 246 "typeDetect", 247 [ "iCookieDuration", "iStateDuration" ], // backwards compat 248 [ "oSearch", "oPreviousSearch" ], 249 [ "aoSearchCols", "aoPreSearchCols" ], 250 [ "iDisplayLength", "_iDisplayLength" ] 251 ] ); 252 _fnMap( oSettings.oScroll, oInit, [ 253 [ "sScrollX", "sX" ], 254 [ "sScrollXInner", "sXInner" ], 255 [ "sScrollY", "sY" ], 256 [ "bScrollCollapse", "bCollapse" ] 257 ] ); 258 _fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" ); 259 260 /* Callback functions which are array driven */ 261 _fnCallbackReg( oSettings, 'aoDrawCallback', oInit.fnDrawCallback ); 262 _fnCallbackReg( oSettings, 'aoStateSaveParams', oInit.fnStateSaveParams ); 263 _fnCallbackReg( oSettings, 'aoStateLoadParams', oInit.fnStateLoadParams ); 264 _fnCallbackReg( oSettings, 'aoStateLoaded', oInit.fnStateLoaded ); 265 _fnCallbackReg( oSettings, 'aoRowCallback', oInit.fnRowCallback ); 266 _fnCallbackReg( oSettings, 'aoRowCreatedCallback', oInit.fnCreatedRow ); 267 _fnCallbackReg( oSettings, 'aoHeaderCallback', oInit.fnHeaderCallback ); 268 _fnCallbackReg( oSettings, 'aoFooterCallback', oInit.fnFooterCallback ); 269 _fnCallbackReg( oSettings, 'aoInitComplete', oInit.fnInitComplete ); 270 _fnCallbackReg( oSettings, 'aoPreDrawCallback', oInit.fnPreDrawCallback ); 271 272 oSettings.rowIdFn = _fnGetObjectDataFn( oInit.rowId ); 273 274 // Add event listeners 275 if (oInit.on) { 276 Object.keys(oInit.on).forEach(function (key) { 277 _fnListener($this, key, oInit.on[key]); 278 }); 279 } 280 281 /* Browser support detection */ 282 _fnBrowserDetect( oSettings ); 283 284 var oClasses = oSettings.oClasses; 285 286 $.extend( oClasses, DataTable.ext.classes, oInit.oClasses ); 287 $this.addClass( oClasses.table ); 288 289 if (! oSettings.oFeatures.bPaginate) { 290 oInit.iDisplayStart = 0; 291 } 292 293 if ( oSettings.iInitDisplayStart === undefined ) 294 { 295 /* Display start point, taking into account the save saving */ 296 oSettings.iInitDisplayStart = oInit.iDisplayStart; 297 oSettings._iDisplayStart = oInit.iDisplayStart; 298 } 299 300 var defer = oInit.iDeferLoading; 301 if ( defer !== null ) 302 { 303 oSettings.deferLoading = true; 304 305 var tmp = Array.isArray(defer); 306 oSettings._iRecordsDisplay = tmp ? defer[0] : defer; 307 oSettings._iRecordsTotal = tmp ? defer[1] : defer; 308 } 309 310 /* 311 * Columns 312 * See if we should load columns automatically or use defined ones 313 */ 314 var columnsInit = []; 315 var thead = this.getElementsByTagName('thead'); 316 var initHeaderLayout = _fnDetectHeader( oSettings, thead[0] ); 317 318 // If we don't have a columns array, then generate one with nulls 319 if ( oInit.aoColumns ) { 320 columnsInit = oInit.aoColumns; 321 } 322 else if ( initHeaderLayout.length ) { 323 for ( i=0, iLen=initHeaderLayout[0].length ; i<iLen ; i++ ) { 324 columnsInit.push( null ); 325 } 326 } 327 328 // Add the columns 329 for ( i=0, iLen=columnsInit.length ; i<iLen ; i++ ) { 330 _fnAddColumn( oSettings ); 331 } 332 333 // Apply the column definitions 334 _fnApplyColumnDefs( oSettings, oInit.aoColumnDefs, columnsInit, initHeaderLayout, function (iCol, oDef) { 335 _fnColumnOptions( oSettings, iCol, oDef ); 336 } ); 337 338 /* HTML5 attribute detection - build an mData object automatically if the 339 * attributes are found 340 */ 341 var rowOne = $this.children('tbody').find('tr:first-child').eq(0); 342 343 if ( rowOne.length ) { 344 var a = function ( cell, name ) { 345 return cell.getAttribute( 'data-'+name ) !== null ? name : null; 346 }; 347 348 $( rowOne[0] ).children('th, td').each( function (i, cell) { 349 var col = oSettings.aoColumns[i]; 350 351 if (! col) { 352 _fnLog( oSettings, 0, 'Incorrect column count', 18 ); 353 } 354 355 if ( col.mData === i ) { 356 var sort = a( cell, 'sort' ) || a( cell, 'order' ); 357 var filter = a( cell, 'filter' ) || a( cell, 'search' ); 358 359 if ( sort !== null || filter !== null ) { 360 col.mData = { 361 _: i+'.display', 362 sort: sort !== null ? i+'.@data-'+sort : undefined, 363 type: sort !== null ? i+'.@data-'+sort : undefined, 364 filter: filter !== null ? i+'.@data-'+filter : undefined 365 }; 366 col._isArrayHost = true; 367 368 _fnColumnOptions( oSettings, i ); 369 } 370 } 371 } ); 372 } 373 374 // Must be done after everything which can be overridden by the state saving! 375 _fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState ); 376 377 var features = oSettings.oFeatures; 378 if ( oInit.bStateSave ) 379 { 380 features.bStateSave = true; 381 } 382 383 // If aaSorting is not defined, then we use the first indicator in asSorting 384 // in case that has been altered, so the default sort reflects that option 385 if ( oInit.aaSorting === undefined ) { 386 var sorting = oSettings.aaSorting; 387 for ( i=0, iLen=sorting.length ; i<iLen ; i++ ) { 388 sorting[i][1] = oSettings.aoColumns[ i ].asSorting[0]; 389 } 390 } 391 392 // Do a first pass on the sorting classes (allows any size changes to be taken into 393 // account, and also will apply sorting disabled classes if disabled 394 _fnSortingClasses( oSettings ); 395 396 _fnCallbackReg( oSettings, 'aoDrawCallback', function () { 397 if ( oSettings.bSorted || _fnDataSource( oSettings ) === 'ssp' || features.bDeferRender ) { 398 _fnSortingClasses( oSettings ); 399 } 400 } ); 401 402 403 /* 404 * Table HTML init 405 * Cache the header, body and footer as required, creating them if needed 406 */ 407 var caption = $this.children('caption'); 408 409 if ( oSettings.caption ) { 410 if ( caption.length === 0 ) { 411 caption = $('<caption/>').appendTo( $this ); 412 } 413 414 caption.html( oSettings.caption ); 415 } 416 417 // Store the caption side, so we can remove the element from the document 418 // when creating the element 419 if (caption.length) { 420 caption[0]._captionSide = caption.css('caption-side'); 421 oSettings.captionNode = caption[0]; 422 } 423 424 if ( thead.length === 0 ) { 425 thead = $('<thead/>').appendTo($this); 426 } 427 oSettings.nTHead = thead[0]; 428 429 var tbody = $this.children('tbody'); 430 if ( tbody.length === 0 ) { 431 tbody = $('<tbody/>').insertAfter(thead); 432 } 433 oSettings.nTBody = tbody[0]; 434 435 var tfoot = $this.children('tfoot'); 436 if ( tfoot.length === 0 ) { 437 // If we are a scrolling table, and no footer has been given, then we need to create 438 // a tfoot element for the caption element to be appended to 439 tfoot = $('<tfoot/>').appendTo($this); 440 } 441 oSettings.nTFoot = tfoot[0]; 442 443 // Copy the data index array 444 oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); 445 446 // Initialisation complete - table can be drawn
447 oSettings.bInitialised = true; 448 449 // Language definitions 450 var oLanguage = oSettings.oLanguage; 451 $.extend( true, oLanguage, oInit.oLanguage ); 452 453 if ( oLanguage.sUrl ) { 454 // Get the language definitions from a file 455 $.ajax( { 456 dataType: 'json', 457 url: oLanguage.sUrl, 458 success: function ( json ) { 459 _fnCamelToHungarian( defaults.oLanguage, json ); 460 $.extend( true, oLanguage, json, oSettings.oInit.oLanguage ); 461 462 _fnCallbackFire( oSettings, null, 'i18n', [oSettings], true); 463 _fnInitialise( oSettings ); 464 }, 465 error: function () { 466 // Error occurred loading language file 467 _fnLog( oSettings, 0, 'i18n file loading error', 21 ); 468 469 // Continue on as best we can 470 _fnInitialise( oSettings ); 471 } 472 } ); 473 } 474 else { 475 _fnCallbackFire( oSettings, null, 'i18n', [oSettings], true); 476 _fnInitialise( oSettings ); 477 } 478 } ); 479 _that = null; 480 return this; 481 }; 482 483 484 485 /** 486 * DataTables extensions 487 * 488 * This namespace acts as a collection area for plug-ins that can be used to 489 * extend DataTables capabilities. Indeed many of the build in methods 490 * use this method to provide their own capabilities (sorting methods for 491 * example). 492 * 493 * Note that this namespace is aliased to `jQuery.fn.dataTableExt` for legacy 494 * reasons 495 * 496 * @namespace 497 */ 498 DataTable.ext = _ext = { 499 /** 500 * DataTables build type (expanded by the download builder) 501 * 502 * @type string 503 */ 504 builder: "-source-", 505 506 /** 507 * Buttons. For use with the Buttons extension for DataTables. This is 508 * defined here so other extensions can define buttons regardless of load 509 * order. It is _not_ used by DataTables core. 510 * 511 * @type object 512 * @default {} 513 */ 514 buttons: {}, 515 516 517 /** 518 * ColumnControl buttons and content 519 * 520 * @type object 521 */ 522 ccContent: {}, 523 524 525 /** 526 * Element class names 527 * 528 * @type object 529 * @default {} 530 */ 531 classes: {}, 532 533 534 /** 535 * Error reporting. 536 * 537 * How should DataTables report an error. Can take the value 'alert', 538 * 'throw', 'none' or a function. 539 * 540 * @type string|function 541 * @default alert 542 */ 543 errMode: "alert", 544 545 546 /** 547 * Legacy so v1 plug-ins don't throw js errors on load 548 */ 549 feature: [], 550 551 /** 552 * Feature plug-ins. 553 * 554 * This is an object of callbacks which provide the features for DataTables 555 * to be initialised via the `layout` option. 556 */ 557 features: {}, 558 559 560 /** 561 * Row searching. 562 * 563 * This method of searching is complimentary to the default type based 564 * searching, and a lot more comprehensive as it allows you complete control 565 * over the searching logic. Each element in this array is a function 566 * (parameters described below) that is called for every row in the table, 567 * and your logic decides if it should be included in the searching data set 568 * or not. 569 * 570 * Searching functions have the following input parameters: 571 * 572 * 1. `{object}` DataTables settings object: see 573 * {@link DataTable.models.oSettings} 574 * 2. `{array|object}` Data for the row to be processed (same as the 575 * original format that was passed in as the data source, or an array 576 * from a DOM data source 577 * 3. `{int}` Row index ({@link DataTable.models.oSettings.aoData}), which 578 * can be useful to retrieve the `TR` element if you need DOM interaction. 579 * 580 * And the following return is expected: 581 * 582 * * {boolean} Include the row in the searched result set (true) or not 583 * (false) 584 * 585 * Note that as with the main search ability in DataTables, technically this 586 * is "filtering", since it is subtractive. However, for consistency in 587 * naming we call it searching here. 588 * 589 * @type array 590 * @default [] 591 * 592 * @example 593 * // The following example shows custom search being applied to the 594 * // fourth column (i.e. the data[3] index) based on two input values 595 * // from the end-user, matching the data in a certain range. 596 * $.fn.dataTable.ext.search.push( 597 * function( settings, data, dataIndex ) { 598 * var min = document.getElementById('min').value * 1; 599 * var max = document.getElementById('max').value * 1; 600 * var version = data[3] == "-" ? 0 : data[3]*1; 601 * 602 * if ( min == "" && max == "" ) { 603 * return true; 604 * } 605 * else if ( min == "" && version < max ) { 606 * return true; 607 * } 608 * else if ( min < version && "" == max ) { 609 * return true; 610 * } 611 * else if ( min < version && version < max ) { 612 * return true; 613 * } 614 * return false;
615 * } 616 * ); 617 */ 618 search: [], 619 620 621 /** 622 * Selector extensions 623 * 624 * The `selector` option can be used to extend the options available for the 625 * selector modifier options (`selector-modifier` object data type) that 626 * each of the three built in selector types offer (row, column and cell + 627 * their plural counterparts). For example the Select extension uses this 628 * mechanism to provide an option to select only rows, columns and cells 629 * that have been marked as selected by the end user (`{selected: true}`), 630 * which can be used in conjunction with the existing built in selector 631 * options. 632 * 633 * Each property is an array to which functions can be pushed. The functions 634 * take three attributes: 635 * 636 * * Settings object for the host table 637 * * Options object (`selector-modifier` object type) 638 * * Array of selected item indexes 639 * 640 * The return is an array of the resulting item indexes after the custom 641 * selector has been applied. 642 * 643 * @type object 644 */ 645 selector: { 646 cell: [], 647 column: [], 648 row: [] 649 }, 650 651 652 /** 653 * Legacy configuration options. Enable and disable legacy options that 654 * are available in DataTables. 655 * 656 * @type object 657 */ 658 legacy: { 659 /** 660 * Enable / disable DataTables 1.9 compatible server-side processing 661 * requests 662 * 663 * @type boolean 664 * @default null 665 */ 666 ajax: null 667 }, 668 669 670 /** 671 * Pagination plug-in methods. 672 * 673 * Each entry in this object is a function and defines which buttons should 674 * be shown by the pagination rendering method that is used for the table: 675 * {@link DataTable.ext.renderer.pageButton}. The renderer addresses how the 676 * buttons are displayed in the document, while the functions here tell it 677 * what buttons to display. This is done by returning an array of button 678 * descriptions (what each button will do). 679 * 680 * Pagination types (the four built in options and any additional plug-in 681 * options defined here) can be used through the `paginationType` 682 * initialisation parameter. 683 * 684 * The functions defined take two parameters: 685 * 686 * 1. `{int} page` The current page index 687 * 2. `{int} pages` The number of pages in the table 688 * 689 * Each function is expected to return an array where each element of the 690 * array can be one of: 691 * 692 * * `first` - Jump to first page when activated 693 * * `last` - Jump to last page when activated 694 * * `previous` - Show previous page when activated 695 * * `next` - Show next page when activated 696 * * `{int}` - Show page of the index given 697 * * `{array}` - A nested array containing the above elements to add a 698 * containing 'DIV' element (might be useful for styling). 699 * 700 * Note that DataTables v1.9- used this object slightly differently whereby 701 * an object with two functions would be defined for each plug-in. That 702 * ability is still supported by DataTables 1.10+ to provide backwards 703 * compatibility, but this option of use is now decremented and no longer 704 * documented in DataTables 1.10+. 705 * 706 * @type object 707 * @default {} 708 * 709 * @example 710 * // Show previous, next and current page buttons only 711 * $.fn.dataTableExt.oPagination.current = function ( page, pages ) { 712 * return [ 'previous', page, 'next' ]; 713 * }; 714 */ 715 pager: {}, 716 717 718 renderer: { 719 pageButton: {}, 720 header: {} 721 }, 722 723 724 /** 725 * Ordering plug-ins - custom data source 726 * 727 * The extension options for ordering of data available here is complimentary 728 * to the default type based ordering that DataTables typically uses. It 729 * allows much greater control over the the data that is being used to 730 * order a column, but is necessarily therefore more complex. 731 * 732 * This type of ordering is useful if you want to do ordering based on data 733 * live from the DOM (for example the contents of an 'input' element) rather 734 * than just the static string that DataTables knows of. 735 * 736 * The way these plug-ins work is that you create an array of the values you 737 * wish to be ordering for the column in question and then return that 738 * array. The data in the array much be in the index order of the rows in 739 * the table (not the currently ordering order!). Which order data gathering 740 * function is run here depends on the `dt-init columns.orderDataType` 741 * parameter that is used for the column (if any). 742 * 743 * The functions defined take two parameters: 744 * 745 * 1. `{object}` DataTables settings object: see 746 * {@link DataTable.models.oSettings} 747 * 2. `{int}` Target column index 748 * 749 * Each function is expected to return an array: 750 * 751 * * `{array}` Data for the column to be ordering upon 752 * 753 * @type array 754 * 755 * @example 756 * // Ordering using `input` node values 757 * $.fn.dataTable.ext.order['dom-text'] = function ( settings, col ) 758 * { 759 * return this.api().column( col, {order:'index'} ).nodes().map( function ( td, i ) { 760 * return $('input', td).val(); 761 * } ); 762 * } 763 */ 764 order: {}, 765 766 767 /** 768 * Type based plug-ins. 769 * 770 * Each column in DataTables has a type assigned to it, either by automatic 771 * detection or by direct assignment using the `type` option for the column. 772 * The type of a column will effect how it is ordering and search (plug-ins 773 * can also make use of the column type if required). 774 * 775 * @namespace 776 */ 777 type: { 778 /** 779 * Automatic column class assignment 780 */ 781 className: {}, 782 783 /** 784 * Type detection functions. 785 * 786 * The functions defined in this object are used to automatically detect 787 * a column's type, making initialisation of DataTables super easy, even 788 * when complex data is in the table. 789 * 790 * The functions defined take two parameters: 791 * 792 * 1. `{*}` Data from the column cell to be analysed 793 * 2. `{settings}` DataTables settings object. This can be used to 794 * perform context specific type detection - for example detection 795 * based on language settings such as using a comma for a decimal 796 * place. Generally speaking the options from the settings will not 797 * be required 798 *
799 * Each function is expected to return: 800 * 801 * * `{string|null}` Data type detected, or null if unknown (and thus 802 * pass it on to the other type detection functions. 803 * 804 * @type array 805 * 806 * @example 807 * // Currency type detection plug-in: 808 * $.fn.dataTable.ext.type.detect.push( 809 * function ( data, settings ) { 810 * // Check the numeric part 811 * if ( ! data.substring(1).match(/[0-9]/) ) { 812 * return null; 813 * } 814 * 815 * // Check prefixed by currency 816 * if ( data.charAt(0) == '$' || data.charAt(0) == '£' ) { 817 * return 'currency'; 818 * } 819 * return null; 820 * } 821 * ); 822 */ 823 detect: [], 824 825 /** 826 * Automatic renderer assignment 827 */ 828 render: {}, 829 830 831 /** 832 * Type based search formatting. 833 * 834 * The type based searching functions can be used to pre-format the 835 * data to be search on. For example, it can be used to strip HTML 836 * tags or to de-format telephone numbers for numeric only searching. 837 * 838 * Note that is a search is not defined for a column of a given type, 839 * no search formatting will be performed. 840 * 841 * Pre-processing of searching data plug-ins - When you assign the sType 842 * for a column (or have it automatically detected for you by DataTables 843 * or a type detection plug-in), you will typically be using this for 844 * custom sorting, but it can also be used to provide custom searching 845 * by allowing you to pre-processing the data and returning the data in 846 * the format that should be searched upon. This is done by adding 847 * functions this object with a parameter name which matches the sType 848 * for that target column. This is the corollary of <i>afnSortData</i> 849 * for searching data. 850 * 851 * The functions defined take a single parameter: 852 * 853 * 1. `{*}` Data from the column cell to be prepared for searching 854 * 855 * Each function is expected to return: 856 * 857 * * `{string|null}` Formatted string that will be used for the searching. 858 * 859 * @type object 860 * @default {} 861 * 862 * @example 863 * $.fn.dataTable.ext.type.search['title-numeric'] = function ( d ) { 864 * return d.replace(/\n/g," ").replace( /<.*?>/g, "" ); 865 * } 866 */ 867 search: {}, 868 869 870 /** 871 * Type based ordering. 872 * 873 * The column type tells DataTables what ordering to apply to the table 874 * when a column is sorted upon. The order for each type that is defined, 875 * is defined by the functions available in this object. 876 * 877 * Each ordering option can be described by three properties added to 878 * this object: 879 * 880 * * `{type}-pre` - Pre-formatting function 881 * * `{type}-asc` - Ascending order function 882 * * `{type}-desc` - Descending order function 883 * 884 * All three can be used together, only `{type}-pre` or only 885 * `{type}-asc` and `{type}-desc` together. It is generally recommended 886 * that only `{type}-pre` is used, as this provides the optimal 887 * implementation in terms of speed, although the others are provided 888 * for compatibility with existing Javascript sort functions. 889 * 890 * `{type}-pre`: Functions defined take a single parameter: 891 * 892 * 1. `{*}` Data from the column cell to be prepared for ordering 893 * 894 * And return: 895 * 896 * * `{*}` Data to be sorted upon 897 * 898 * `{type}-asc` and `{type}-desc`: Functions are typical Javascript sort 899 * functions, taking two parameters: 900 * 901 * 1. `{*}` Data to compare to the second parameter 902 * 2. `{*}` Data to compare to the first parameter 903 * 904 * And returning: 905 * 906 * * `{*}` Ordering match: <0 if first parameter should be sorted lower 907 * than the second parameter, ===0 if the two parameters are equal and 908 * >0 if the first parameter should be sorted height than the second 909 * parameter. 910 * 911 * @type object 912 * @default {} 913 * 914 * @example 915 * // Numeric ordering of formatted numbers with a pre-formatter 916 * $.extend( $.fn.dataTable.ext.type.order, { 917 * "string-pre": function(x) { 918 * a = (a === "-" || a === "") ? 0 : a.replace( /[^\d\-\.]/g, "" ); 919 * return parseFloat( a ); 920 * } 921 * } ); 922 * 923 * @example 924 * // Case-sensitive string ordering, with no pre-formatting method 925 * $.extend( $.fn.dataTable.ext.order, { 926 * "string-case-asc": function(x,y) { 927 * return ((x < y) ? -1 : ((x > y) ? 1 : 0)); 928 * }, 929 * "string-case-desc": function(x,y) { 930 * return ((x < y) ? 1 : ((x > y) ? -1 : 0)); 931 * } 932 * } ); 933 */ 934 order: {} 935 }, 936 937 /** 938 * Unique DataTables instance counter 939 * 940 * @type int 941 * @private 942 */ 943 _unique: 0, 944 945 946 // 947 // Depreciated 948 // The following properties are retained for backwards compatibility only. 949 // The should not be used in new projects and will be removed in a future 950 // version 951 // 952 953 /** 954 * Version check function. 955 * @type function 956 * @depreciated Since 1.10 957 */ 958 fnVersionCheck: DataTable.fnVersionCheck, 959 960 961 /** 962 * Index for what 'this' index API functions should use 963 * @type int 964 * @deprecated Since v1.10 965 */ 966 iApiIndex: 0, 967 968 969 /** 970 * Software version 971 * @type string 972 * @deprecated Since v1.10 973 */
974 sVersion: DataTable.version 975 }; 976 977 978 // 979 // Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts 980 // 981 $.extend( _ext, { 982 afnFiltering: _ext.search, 983 aTypes: _ext.type.detect, 984 ofnSearch: _ext.type.search, 985 oSort: _ext.type.order, 986 afnSortData: _ext.order, 987 aoFeatures: _ext.feature, 988 oStdClasses: _ext.classes, 989 oPagination: _ext.pager 990 } ); 991 992 993 $.extend( DataTable.ext.classes, { 994 container: 'dt-container', 995 empty: { 996 row: 'dt-empty' 997 }, 998 info: { 999 container: 'dt-info' 1000 }, 1001 layout: { 1002 row: 'dt-layout-row', 1003 cell: 'dt-layout-cell', 1004 tableRow: 'dt-layout-table', 1005 tableCell: '', 1006 start: 'dt-layout-start', 1007 end: 'dt-layout-end', 1008 full: 'dt-layout-full' 1009 }, 1010 length: { 1011 container: 'dt-length', 1012 select: 'dt-input' 1013 }, 1014 order: { 1015 canAsc: 'dt-orderable-asc', 1016 canDesc: 'dt-orderable-desc', 1017 isAsc: 'dt-ordering-asc', 1018 isDesc: 'dt-ordering-desc', 1019 none: 'dt-orderable-none', 1020 position: 'sorting_' 1021 }, 1022 processing: { 1023 container: 'dt-processing' 1024 }, 1025 scrolling: { 1026 body: 'dt-scroll-body', 1027 container: 'dt-scroll', 1028 footer: { 1029 self: 'dt-scroll-foot', 1030 inner: 'dt-scroll-footInner' 1031 }, 1032 header: { 1033 self: 'dt-scroll-head', 1034 inner: 'dt-scroll-headInner' 1035 } 1036 }, 1037 search: { 1038 container: 'dt-search', 1039 input: 'dt-input' 1040 }, 1041 table: 'dataTable', 1042 tbody: { 1043 cell: '', 1044 row: '' 1045 }, 1046 thead: { 1047 cell: '', 1048 row: '' 1049 }, 1050 tfoot: { 1051 cell: '', 1052 row: '' 1053 }, 1054 paging: { 1055 active: 'current', 1056 button: 'dt-paging-button', 1057 container: 'dt-paging', 1058 disabled: 'disabled', 1059 nav: '' 1060 } 1061 } ); 1062 1063 1064 /*
1065 * It is useful to have variables which are scoped locally so only the 1066 * DataTables functions can access them and they don't leak into global space. 1067 * At the same time these functions are often useful over multiple files in the 1068 * core and API, so we list, or at least document, all variables which are used 1069 * by DataTables as private variables here. This also ensures that there is no 1070 * clashing of variable names and that they can easily referenced for reuse. 1071 */ 1072 1073 1074 // Defined else where 1075 // _selector_run 1076 // _selector_opts 1077 // _selector_row_indexes 1078 1079 var _ext; // DataTable.ext 1080 var _Api; // DataTable.Api 1081 var _api_register; // DataTable.Api.register 1082 var _api_registerPlural; // DataTable.Api.registerPlural 1083 1084 var _re_dic = {}; 1085 var _re_new_lines = /[\r\n\u2028]/g; 1086 var _re_html = /<([^>]*>)/g; 1087 var _max_str_len = Math.pow(2, 28); 1088 1089 // This is not strict ISO8601 - Date.parse() is quite lax, although 1090 // implementations differ between browsers. 1091 var _re_date = /^\d{2,4}[./-]\d{1,2}[./-]\d{1,2}([T ]{1}\d{1,2}[:.]\d{2}([.:]\d{2})?)?$/; 1092 1093 // Escape regular expression special characters 1094 var _re_escape_regex = new RegExp( '(\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ].join('|\\') + ')', 'g' ); 1095 1096 // https://en.wikipedia.org/wiki/Foreign_exchange_market 1097 // - \u20BD - Russian ruble. 1098 // - \u20a9 - South Korean Won 1099 // - \u20BA - Turkish Lira 1100 // - \u20B9 - Indian Rupee 1101 // - R - Brazil (R$) and South Africa 1102 // - fr - Swiss Franc 1103 // - kr - Swedish krona, Norwegian krone and Danish krone 1104 // - \u2009 is thin space and \u202F is narrow no-break space, both used in many 1105 // - É - Bitcoin 1106 // - Î - Ethereum 1107 // standards as thousands separators. 1108 var _re_formatted_numeric = /['\u00A0,$£â¬Â¥%\u2009\u202F\u20BD\u20a9\u20BArfkÉÎ]/gi; 1109 1110 1111 var _empty = function ( d ) { 1112 return !d || d === true || d === '-' ? true : false; 1113 }; 1114 1115 1116 var _intVal = function ( s ) { 1117 var integer = parseInt( s, 10 ); 1118 return !isNaN(integer) && isFinite(s) ? integer : null; 1119 }; 1120 1121 // Convert from a formatted number with characters other than `.` as the 1122 // decimal place, to a Javascript number 1123 var _numToDecimal = function ( num, decimalPoint ) { 1124 // Cache created regular expressions for speed as this function is called often 1125 if ( ! _re_dic[ decimalPoint ] ) { 1126 _re_dic[ decimalPoint ] = new RegExp( _fnEscapeRegex( decimalPoint ), 'g' ); 1127 } 1128 return typeof num === 'string' && decimalPoint !== '.' ? 1129 num.replace( /\./g, '' ).replace( _re_dic[ decimalPoint ], '.' ) : 1130 num; 1131 }; 1132 1133 1134 var _isNumber = function ( d, decimalPoint, formatted, allowEmpty ) { 1135 var type = typeof d; 1136 var strType = type === 'string'; 1137 1138 if ( type === 'number' || type === 'bigint') { 1139 return true; 1140 } 1141 1142 // If empty return immediately so there must be a number if it is a 1143 // formatted string (this stops the string "k", or "kr", etc being detected 1144 // as a formatted number for currency 1145 if ( allowEmpty && _empty( d ) ) { 1146 return true; 1147 } 1148 1149 if ( decimalPoint && strType ) { 1150 d = _numToDecimal( d, decimalPoint ); 1151 } 1152 1153 if ( formatted && strType ) { 1154 d = d.replace( _re_formatted_numeric, '' ); 1155 } 1156 1157 return !isNaN( parseFloat(d) ) && isFinite( d ); 1158 }; 1159 1160 1161 // A string without HTML in it can be considered to be HTML still 1162 var _isHtml = function ( d ) { 1163 return _empty( d ) || typeof d === 'string'; 1164 }; 1165 1166 // Is a string a number surrounded by HTML? 1167 var _htmlNumeric = function ( d, decimalPoint, formatted, allowEmpty ) { 1168 if ( allowEmpty && _empty( d ) ) { 1169 return true; 1170 } 1171 1172 // input and select strings mean that this isn't just a number 1173 if (typeof d === 'string' && d.match(/<(input|select)/i)) { 1174 return null; 1175 } 1176 1177 var html = _isHtml( d ); 1178 return ! html ? 1179 null : 1180 _isNumber( _stripHtml( d ), decimalPoint, formatted, allowEmpty ) ? 1181 true : 1182 null; 1183 }; 1184 1185 1186 var _pluck = function ( a, prop, prop2 ) { 1187 var out = []; 1188 var i=0, ien=a.length; 1189 1190 // Could have the test in the loop for slightly smaller code, but speed 1191 // is essential here 1192 if ( prop2 !== undefined ) { 1193 for ( ; i<ien ; i++ ) { 1194 if ( a[i] && a[i][ prop ] ) { 1195 out.push( a[i][ prop ][ prop2 ] ); 1196 } 1197 } 1198 } 1199 else { 1200 for ( ; i<ien ; i++ ) { 1201 if ( a[i] ) { 1202 out.push( a[i][ prop ] ); 1203 } 1204 } 1205 } 1206 1207 return out; 1208 }; 1209 1210 1211 // Basically the same as _pluck, but rather than looping over `a` we use `order` 1212 // as the indexes to pick from `a` 1213 var _pluck_order = function ( a, order, prop, prop2 ) 1214 { 1215 var out = []; 1216 var i=0, ien=order.length; 1217 1218 // Could have the test in the loop for slightly smaller code, but speed 1219 // is essential here 1220 if ( prop2 !== undefined ) { 1221 for ( ; i<ien ; i++ ) { 1222 if ( a[ order[i] ] && a[ order[i] ][ prop ] ) { 1223 out.push( a[ order[i] ][ prop ][ prop2 ] ); 1224 } 1225 } 1226 } 1227 else { 1228 for ( ; i<ien ; i++ ) { 1229 if ( a[ order[i] ] ) { 1230 out.push( a[ order[i] ][ prop ] ); 1231 } 1232 } 1233 } 1234 1235 return out; 1236 }; 1237 1238 1239 var _range = function ( len, start ) 1240 { 1241 var out = []; 1242 var end; 1243 1244 if ( start === undefined ) { 1245 start = 0; 1246 end = len; 1247 } 1248 else { 1249 end = start; 1250 start = len; 1251 } 1252 1253 for ( var i=start ; i<end ; i++ ) { 1254 out.push( i ); 1255 } 1256 1257 return out; 1258 }; 1259 1260 1261 var _removeEmpty = function ( a ) 1262 { 1263 var out = []; 1264 1265 for ( var i=0, ien=a.length ; i<ien ; i++ ) { 1266 if ( a[i] ) { // careful - will remove all falsy values! 1267 out.push( a[i] ); 1268 } 1269 } 1270 1271 return out; 1272 }; 1273 1274 // Replaceable function in api.util 1275 var _stripHtml = function (input) { 1276 if (! input || typeof input !== 'string') { 1277 return input; 1278 } 1279 1280 // Irrelevant check to workaround CodeQL's false positive on the regex 1281 if (input.length > _max_str_len) { 1282 throw new Error('Exceeded max str len'); 1283 } 1284 1285 var previous; 1286 1287 input = input.replace(_re_html, ''); // Complete tags 1288 1289 // Safety for incomplete script tag - use do / while to ensure that 1290 // we get all instances 1291 do { 1292 previous = input; 1293 input = input.replace(/<script/i, ''); 1294 } while (input !== previous); 1295 1296 return previous; 1297 }; 1298 1299 // Replaceable function in api.util 1300 var _escapeHtml = function ( d ) { 1301 if (Array.isArray(d)) { 1302 d = d.join(','); 1303 } 1304 1305 return typeof d === 'string' ? 1306 d 1307 .replace(/&/g, '&') 1308 .replace(/</g, '<') 1309 .replace(/>/g, '>') 1310 .replace(/"/g, '"') : 1311 d; 1312 }; 1313 1314 // Remove diacritics from a string by decomposing it and then removing 1315 // non-ascii characters 1316 var _normalize = function (str, both) { 1317 if (typeof str !== 'string') { 1318 return str; 1319 } 1320 1321 // It is faster to just run `normalize` than it is to check if 1322 // we need to with a regex! (Check as it isn't available in old 1323 // Safari) 1324 var res = str.normalize 1325 ? str.normalize("NFD") 1326 : str; 1327 1328 // Equally, here we check if a regex is needed or not 1329 return res.length !== str.length 1330 ? (both === true ? str + ' ' : '' ) + res.replace(/[\u0300-\u036f]/g, "") 1331 : res; 1332 } 1333 1334 /** 1335 * Determine if all values in the array are unique. This means we can short 1336 * cut the _unique method at the cost of a single loop. A sorted array is used 1337 * to easily check the values. 1338 * 1339 * @param {array} src Source array 1340 * @return {boolean} true if all unique, false otherwise 1341 * @ignore 1342 */ 1343 var _areAllUnique = function ( src ) { 1344 if ( src.length < 2 ) { 1345 return true; 1346 } 1347 1348 var sorted = src.slice().sort(); 1349 var last = sorted[0]; 1350 1351 for ( var i=1, ien=sorted.length ; i<ien ; i++ ) { 1352 if ( sorted[i] === last ) { 1353 return false;
1354 } 1355 1356 last = sorted[i]; 1357 } 1358 1359 return true; 1360 }; 1361 1362 1363 /** 1364 * Find the unique elements in a source array. 1365 * 1366 * @param {array} src Source array 1367 * @return {array} Array of unique items 1368 * @ignore 1369 */ 1370 var _unique = function ( src ) 1371 { 1372 if (Array.from && Set) { 1373 return Array.from(new Set(src)); 1374 } 1375 1376 if ( _areAllUnique( src ) ) { 1377 return src.slice(); 1378 } 1379 1380 // A faster unique method is to use object keys to identify used values, 1381 // but this doesn't work with arrays or objects, which we must also 1382 // consider. See jsperf.app/compare-array-unique-versions/4 for more 1383 // information. 1384 var 1385 out = [], 1386 val, 1387 i, ien=src.length, 1388 j, k=0; 1389 1390 again: for ( i=0 ; i<ien ; i++ ) { 1391 val = src[i]; 1392 1393 for ( j=0 ; j<k ; j++ ) { 1394 if ( out[j] === val ) { 1395 continue again; 1396 } 1397 } 1398 1399 out.push( val ); 1400 k++; 1401 } 1402 1403 return out; 1404 }; 1405 1406 // Surprisingly this is faster than [].concat.apply 1407 // https://jsperf.com/flatten-an-array-loop-vs-reduce/2 1408 var _flatten = function (out, val) { 1409 if (Array.isArray(val)) { 1410 for (var i=0 ; i<val.length ; i++) { 1411 _flatten(out, val[i]); 1412 } 1413 } 1414 else { 1415 out.push(val); 1416 } 1417 1418 return out; 1419 } 1420 1421 // Similar to jQuery's addClass, but use classList.add 1422 function _addClass(el, name) { 1423 if (name) { 1424 name.split(' ').forEach(function (n) { 1425 if (n) { 1426 // `add` does deduplication, so no need to check `contains` 1427 el.classList.add(n); 1428 } 1429 }); 1430 } 1431 } 1432 1433 /** 1434 * DataTables utility methods 1435 * 1436 * This namespace provides helper methods that DataTables uses internally to 1437 * create a DataTable, but which are not exclusively used only for DataTables. 1438 * These methods can be used by extension authors to save the duplication of 1439 * code. 1440 * 1441 * @namespace 1442 */ 1443 DataTable.util = { 1444 /** 1445 * Return a string with diacritic characters decomposed 1446 * @param {*} mixed Function or string to normalize 1447 * @param {*} both Return original string and the normalized string 1448 * @returns String or undefined 1449 */ 1450 diacritics: function (mixed, both) { 1451 var type = typeof mixed; 1452 1453 if (type !== 'function') { 1454 return _normalize(mixed, both); 1455 } 1456 _normalize = mixed; 1457 }, 1458 1459 /** 1460 * Debounce a function 1461 * 1462 * @param {function} fn Function to be called 1463 * @param {integer} freq Call frequency in mS 1464 * @return {function} Wrapped function 1465 */ 1466 debounce: function ( fn, timeout ) { 1467 var timer; 1468 1469 return function () { 1470 var that = this; 1471 var args = arguments; 1472 1473 clearTimeout(timer); 1474 1475 timer = setTimeout( function () { 1476 fn.apply(that, args); 1477 }, timeout || 250 ); 1478 }; 1479 }, 1480 1481 /** 1482 * Throttle the calls to a function. Arguments and context are maintained 1483 * for the throttled function. 1484 * 1485 * @param {function} fn Function to be called 1486 * @param {integer} freq Call frequency in mS 1487 * @return {function} Wrapped function 1488 */ 1489 throttle: function ( fn, freq ) { 1490 var 1491 frequency = freq !== undefined ? freq : 200, 1492 last, 1493 timer; 1494 1495 return function () { 1496 var 1497 that = this, 1498 now = +new Date(), 1499 args = arguments; 1500 1501 if ( last && now < last + frequency ) { 1502 clearTimeout( timer ); 1503 1504 timer = setTimeout( function () { 1505 last = undefined; 1506 fn.apply( that, args ); 1507 }, frequency ); 1508 } 1509 else { 1510 last = now; 1511 fn.apply( that, args ); 1512 } 1513 }; 1514 }, 1515 1516 /** 1517 * Escape a string such that it can be used in a regular expression 1518 * 1519 * @param {string} val string to escape 1520 * @returns {string} escaped string 1521 */ 1522 escapeRegex: function ( val ) { 1523 return val.replace( _re_escape_regex, '\\$1' ); 1524 }, 1525 1526 /** 1527 * Create a function that will write to a nested object or array 1528 * @param {*} source JSON notation string 1529 * @returns Write function 1530 */ 1531 set: function ( source ) { 1532 if ( $.isPlainObject( source ) ) { 1533 /* Unlike get, only the underscore (global) option is used for for 1534 * setting data since we don't know the type here. This is why an object 1535 * option is not documented for `mData` (which is read/write), but it is 1536 * for `mRender` which is read only. 1537 */ 1538 return DataTable.util.set( source._ ); 1539 } 1540 else if ( source === null ) { 1541 // Nothing to do when the data source is null 1542 return function () {}; 1543 } 1544 else if ( typeof source === 'function' ) { 1545 return function (data, val, meta) { 1546 source( data, 'set', val, meta ); 1547 }; 1548 } 1549 else if ( 1550 typeof source === 'string' && (source.indexOf('.') !== -1 || 1551 source.indexOf('[') !== -1 || source.indexOf('(') !== -1) 1552 ) { 1553 // Like the get, we need to get data from a nested object 1554 var setData = function (data, val, src) { 1555 var a = _fnSplitObjNotation( src ), b; 1556 var aLast = a[a.length-1]; 1557 var arrayNotation, funcNotation, o, innerSrc; 1558 1559 for ( var i=0, iLen=a.length-1 ; i<iLen ; i++ ) { 1560 // Protect against prototype pollution 1561 if (a[i] === '__proto__' || a[i] === 'constructor') { 1562 throw new Error('Cannot set prototype values'); 1563 } 1564 1565 // Check if we are dealing with an array notation request 1566 arrayNotation = a[i].match(__reArray); 1567 funcNotation = a[i].match(__reFn); 1568 1569 if ( arrayNotation ) { 1570 a[i] = a[i].replace(__reArray, ''); 1571 data[ a[i] ] = []; 1572 1573 // Get the remainder of the nested object to set so we can recurse 1574 b = a.slice(); 1575 b.splice( 0, i+1 ); 1576 innerSrc = b.join('.'); 1577 1578 // Traverse each entry in the array setting the properties requested 1579 if ( Array.isArray( val ) ) { 1580 for ( var j=0, jLen=val.length ; j<jLen ; j++ ) { 1581 o = {}; 1582 setData( o, val[j], innerSrc ); 1583 data[ a[i] ].push( o ); 1584 } 1585 } 1586 else { 1587 // We've been asked to save data to an array, but it 1588 // isn't array data to be saved. Best that can be done 1589 // is to just save the value. 1590 data[ a[i] ] = val; 1591 } 1592 1593 // The inner call to setData has already traversed through the remainder 1594 // of the source and has set the data, thus we can exit here 1595 return; 1596 } 1597 else if ( funcNotation ) { 1598 // Function call 1599 a[i] = a[i].replace(__reFn, ''); 1600 data = data[ a[i] ]( val ); 1601 } 1602 1603 // If the nested object doesn't currently exist - since we are 1604 // trying to set the value - create it 1605 if ( data[ a[i] ] === null || data[ a[i] ] === undefined ) { 1606 data[ a[i] ] = {}; 1607 } 1608 data = data[ a[i] ]; 1609 } 1610 1611 // Last item in the input - i.e, the actual set 1612 if ( aLast.match(__reFn ) ) { 1613 // Function call 1614 data = data[ aLast.replace(__reFn, '') ]( val ); 1615 } 1616 else { 1617 // If array notation is used, we just want to strip it and use the property name 1618 // and assign the value. If it isn't used, then we get the result we want anyway 1619 data[ aLast.replace(__reArray, '') ] = val; 1620 } 1621 }; 1622 1623 return function (data, val) { // meta is also passed in, but not used 1624 return setData( data, val, source ); 1625 }; 1626 } 1627 else { 1628 // Array or flat object mapping 1629 return function (data, val) { // meta is also passed in, but not used 1630 data[source] = val; 1631 }; 1632 } 1633 }, 1634 1635 /** 1636 * Create a function that will read nested objects from arrays, based on JSON notation 1637 * @param {*} source JSON notation string 1638 * @returns Value read 1639 */ 1640 get: function ( source ) { 1641 if ( $.isPlainObject( source ) ) { 1642 // Build an object of get functions, and wrap them in a single call 1643 var o = {}; 1644 $.each( source, function (key, val) { 1645 if ( val ) { 1646 o[key] = DataTable.util.get( val ); 1647 } 1648 } ); 1649
1650 return function (data, type, row, meta) { 1651 var t = o[type] || o._; 1652 return t !== undefined ? 1653 t(data, type, row, meta) : 1654 data; 1655 }; 1656 } 1657 else if ( source === null ) { 1658 // Give an empty string for rendering / sorting etc 1659 return function (data) { // type, row and meta also passed, but not used 1660 return data; 1661 }; 1662 } 1663 else if ( typeof source === 'function' ) { 1664 return function (data, type, row, meta) { 1665 return source( data, type, row, meta ); 1666 }; 1667 } 1668 else if ( 1669 typeof source === 'string' && (source.indexOf('.') !== -1 || 1670 source.indexOf('[') !== -1 || source.indexOf('(') !== -1) 1671 ) { 1672 /* If there is a . in the source string then the data source is in a 1673 * nested object so we loop over the data for each level to get the next 1674 * level down. On each loop we test for undefined, and if found immediately 1675 * return. This allows entire objects to be missing and sDefaultContent to 1676 * be used if defined, rather than throwing an error 1677 */ 1678 var fetchData = function (data, type, src) { 1679 var arrayNotation, funcNotation, out, innerSrc; 1680 1681 if ( src !== "" ) { 1682 var a = _fnSplitObjNotation( src ); 1683 1684 for ( var i=0, iLen=a.length ; i<iLen ; i++ ) { 1685 // Check if we are dealing with special notation 1686 arrayNotation = a[i].match(__reArray); 1687 funcNotation = a[i].match(__reFn); 1688 1689 if ( arrayNotation ) { 1690 // Array notation 1691 a[i] = a[i].replace(__reArray, ''); 1692 1693 // Condition allows simply [] to be passed in 1694 if ( a[i] !== "" ) { 1695 data = data[ a[i] ]; 1696 } 1697 out = []; 1698 1699 // Get the remainder of the nested object to get 1700 a.splice( 0, i+1 ); 1701 innerSrc = a.join('.'); 1702 1703 // Traverse each entry in the array getting the properties requested 1704 if ( Array.isArray( data ) ) { 1705 for ( var j=0, jLen=data.length ; j<jLen ; j++ ) { 1706 out.push( fetchData( data[j], type, innerSrc ) ); 1707 } 1708 } 1709 1710 // If a string is given in between the array notation indicators, that 1711 // is used to join the strings together, otherwise an array is returned 1712 var join = arrayNotation[0].substring(1, arrayNotation[0].length-1); 1713 data = (join==="") ? out : out.join(join); 1714 1715 // The inner call to fetchData has already traversed through the remainder 1716 // of the source requested, so we exit from the loop 1717 break; 1718 } 1719 else if ( funcNotation ) { 1720 // Function call 1721 a[i] = a[i].replace(__reFn, ''); 1722 data = data[ a[i] ](); 1723 continue; 1724 } 1725 1726 if (data === null || data[ a[i] ] === null) { 1727 return null; 1728 } 1729 else if ( data === undefined || data[ a[i] ] === undefined ) { 1730 return undefined; 1731 } 1732 1733 data = data[ a[i] ]; 1734 } 1735 } 1736 1737 return data; 1738 }; 1739 1740 return function (data, type) { // row and meta also passed, but not used 1741 return fetchData( data, type, source ); 1742 }; 1743 } 1744 else { 1745 // Array or flat object mapping 1746 return function (data) { // row and meta also passed, but not used 1747 return data[source]; 1748 }; 1749 } 1750 }, 1751 1752 stripHtml: function (mixed) { 1753 var type = typeof mixed; 1754 1755 if (type === 'function') { 1756 _stripHtml = mixed; 1757 return; 1758 } 1759 else if (type === 'string') { 1760 return _stripHtml(mixed); 1761 } 1762 return mixed; 1763 }, 1764 1765 escapeHtml: function (mixed) { 1766 var type = typeof mixed; 1767 1768 if (type === 'function') { 1769 _escapeHtml = mixed; 1770 return; 1771 } 1772 else if (type === 'string' || Array.isArray(mixed)) { 1773 return _escapeHtml(mixed); 1774 } 1775 return mixed; 1776 }, 1777 1778 unique: _unique 1779 }; 1780 1781 1782 1783 /** 1784 * Create a mapping object that allows camel case parameters to be looked up 1785 * for their Hungarian counterparts. The mapping is stored in a private 1786 * parameter called `_hungarianMap` which can be accessed on the source object. 1787 * @param {object} o 1788 * @memberof DataTable#oApi 1789 */ 1790 function _fnHungarianMap ( o ) 1791 { 1792 var 1793 hungarian = 'a aa ai ao as b fn i m o s ', 1794 match, 1795 newKey, 1796 map = {}; 1797 1798 $.each( o, function (key) { 1799 match = key.match(/^([^A-Z]+?)([A-Z])/); 1800 1801 if ( match && hungarian.indexOf(match[1]+' ') !== -1 ) 1802 { 1803 newKey = key.replace( match[0], match[2].toLowerCase() ); 1804 map[ newKey ] = key; 1805 1806 if ( match[1] === 'o' ) 1807 { 1808 _fnHungarianMap( o[key] ); 1809 } 1810 } 1811 } ); 1812 1813 o._hungarianMap = map; 1814 } 1815 1816 1817 /** 1818 * Convert from camel case parameters to Hungarian, based on a Hungarian map 1819 * created by _fnHungarianMap. 1820 * @param {object} src The model object which holds all parameters that can be 1821 * mapped. 1822 * @param {object} user The object to convert from camel case to Hungarian. 1823 * @param {boolean} force When set to `true`, properties which already have a 1824 * Hungarian value in the `user` object will be overwritten. Otherwise they 1825 * won't be. 1826 * @memberof DataTable#oApi 1827 */ 1828 function _fnCamelToHungarian ( src, user, force ) 1829 { 1830 if ( ! src._hungarianMap ) { 1831 _fnHungarianMap( src ); 1832 } 1833 1834 var hungarianKey; 1835 1836 $.each( user, function (key) { 1837 hungarianKey = src._hungarianMap[ key ]; 1838 1839 if ( hungarianKey !== undefined && (force || user[hungarianKey] === undefined) ) 1840 { 1841 // For objects, we need to buzz down into the object to copy parameters 1842 if ( hungarianKey.charAt(0) === 'o' ) 1843 { 1844 // Copy the camelCase options over to the hungarian 1845 if ( ! user[ hungarianKey ] ) { 1846 user[ hungarianKey ] = {}; 1847 } 1848 $.extend( true, user[hungarianKey], user[key] ); 1849 1850 _fnCamelToHungarian( src[hungarianKey], user[hungarianKey], force ); 1851 } 1852 else { 1853 user[hungarianKey] = user[ key ]; 1854 } 1855 } 1856 } ); 1857 } 1858 1859 /** 1860 * Map one parameter onto another 1861 * @param {object} o Object to map 1862 * @param {*} knew The new parameter name 1863 * @param {*} old The old parameter name 1864 */ 1865 var _fnCompatMap = function ( o, knew, old ) { 1866 if ( o[ knew ] !== undefined ) { 1867 o[ old ] = o[ knew ]; 1868 } 1869 }; 1870 1871 1872 /** 1873 * Provide backwards compatibility for the main DT options. Note that the new 1874 * options are mapped onto the old parameters, so this is an external interface 1875 * change only. 1876 * @param {object} init Object to map 1877 */ 1878 function _fnCompatOpts ( init ) 1879 { 1880 _fnCompatMap( init, 'ordering', 'bSort' ); 1881 _fnCompatMap( init, 'orderMulti', 'bSortMulti' ); 1882 _fnCompatMap( init, 'orderClasses', 'bSortClasses' ); 1883 _fnCompatMap( init, 'orderCellsTop', 'bSortCellsTop' ); 1884 _fnCompatMap( init, 'order', 'aaSorting' ); 1885 _fnCompatMap( init, 'orderFixed', 'aaSortingFixed' ); 1886 _fnCompatMap( init, 'paging', 'bPaginate' ); 1887 _fnCompatMap( init, 'pagingType', 'sPaginationType' ); 1888 _fnCompatMap( init, 'pageLength', 'iDisplayLength' ); 1889 _fnCompatMap( init, 'searching', 'bFilter' ); 1890 1891 // Boolean initialisation of x-scrolling 1892 if ( typeof init.sScrollX === 'boolean' ) { 1893 init.sScrollX = init.sScrollX ? '100%' : ''; 1894 } 1895 if ( typeof init.scrollX === 'boolean' ) { 1896 init.scrollX = init.scrollX ? '100%' : ''; 1897 } 1898 1899 // Objects for ordering 1900 if ( typeof init.bSort === 'object' ) { 1901 init.orderIndicators = init.bSort.indicators !== undefined ? init.bSort.indicators : true; 1902 init.orderHandler = init.bSort.handler !== undefined ? init.bSort.handler : true; 1903 init.bSort = true; 1904 } 1905 else if (init.bSort === false) { 1906 init.orderIndicators = false;
1907 init.orderHandler = false; 1908 } 1909 else if (init.bSort === true) { 1910 init.orderIndicators = true; 1911 init.orderHandler = true; 1912 } 1913 1914 // Which cells are the title cells? 1915 if (typeof init.bSortCellsTop === 'boolean') { 1916 init.titleRow = init.bSortCellsTop; 1917 } 1918 1919 // Column search objects are in an array, so it needs to be converted 1920 // element by element 1921 var searchCols = init.aoSearchCols; 1922 1923 if ( searchCols ) { 1924 for ( var i=0, ien=searchCols.length ; i<ien ; i++ ) { 1925 if ( searchCols[i] ) { 1926 _fnCamelToHungarian( DataTable.models.oSearch, searchCols[i] ); 1927 } 1928 } 1929 } 1930 1931 // Enable search delay if server-side processing is enabled 1932 if (init.serverSide && ! init.searchDelay) { 1933 init.searchDelay = 400; 1934 } 1935 } 1936 1937 1938 /** 1939 * Provide backwards compatibility for column options. Note that the new options 1940 * are mapped onto the old parameters, so this is an external interface change 1941 * only. 1942 * @param {object} init Object to map 1943 */ 1944 function _fnCompatCols ( init ) 1945 { 1946 _fnCompatMap( init, 'orderable', 'bSortable' ); 1947 _fnCompatMap( init, 'orderData', 'aDataSort' ); 1948 _fnCompatMap( init, 'orderSequence', 'asSorting' ); 1949 _fnCompatMap( init, 'orderDataType', 'sortDataType' ); 1950 1951 // orderData can be given as an integer 1952 var dataSort = init.aDataSort; 1953 if ( typeof dataSort === 'number' && ! Array.isArray( dataSort ) ) { 1954 init.aDataSort = [ dataSort ]; 1955 } 1956 } 1957 1958 1959 /** 1960 * Browser feature detection for capabilities, quirks 1961 * @param {object} settings dataTables settings object 1962 * @memberof DataTable#oApi 1963 */ 1964 function _fnBrowserDetect( settings ) 1965 { 1966 // We don't need to do this every time DataTables is constructed, the values 1967 // calculated are specific to the browser and OS configuration which we 1968 // don't expect to change between initialisations 1969 if ( ! DataTable.__browser ) { 1970 var browser = {}; 1971 DataTable.__browser = browser; 1972 1973 // Scrolling feature / quirks detection 1974 var n = $('<div/>') 1975 .css( { 1976 position: 'fixed', 1977 top: 0, 1978 left: -1 * window.pageXOffset, // allow for scrolling 1979 height: 1, 1980 width: 1, 1981 overflow: 'hidden' 1982 } ) 1983 .append( 1984 $('<div/>') 1985 .css( { 1986 position: 'absolute', 1987 top: 1, 1988 left: 1, 1989 width: 100, 1990 overflow: 'scroll' 1991 } ) 1992 .append( 1993 $('<div/>') 1994 .css( { 1995 width: '100%', 1996 height: 10 1997 } ) 1998 ) 1999 ) 2000 .appendTo( 'body' ); 2001 2002 var outer = n.children(); 2003 var inner = outer.children(); 2004 2005 // Get scrollbar width 2006 browser.barWidth = outer[0].offsetWidth - outer[0].clientWidth; 2007 2008 // In rtl text layout, some browsers (most, but not all) will place the 2009 // scrollbar on the left, rather than the right. 2010 browser.bScrollbarLeft = Math.round( inner.offset().left ) !== 1; 2011 2012 n.remove(); 2013 } 2014 2015 $.extend( settings.oBrowser, DataTable.__browser ); 2016 settings.oScroll.iBarWidth = DataTable.__browser.barWidth; 2017 } 2018 2019 /** 2020 * Add a column to the list used for the table with default values 2021 * @param {object} oSettings dataTables settings object 2022 * @memberof DataTable#oApi 2023 */ 2024 function _fnAddColumn( oSettings ) 2025 { 2026 // Add column to aoColumns array 2027 var oDefaults = DataTable.defaults.column; 2028 var iCol = oSettings.aoColumns.length; 2029 var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, { 2030 "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol], 2031 "mData": oDefaults.mData ? oDefaults.mData : iCol, 2032 idx: iCol, 2033 searchFixed: {}, 2034 colEl: $('<col>').attr('data-dt-column', iCol) 2035 } ); 2036 oSettings.aoColumns.push( oCol ); 2037 2038 // Add search object for column specific search. Note that the `searchCols[ iCol ]` 2039 // passed into extend can be undefined. This allows the user to give a default 2040 // with only some of the parameters defined, and also not give a default 2041 var searchCols = oSettings.aoPreSearchCols; 2042 searchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch, searchCols[ iCol ] ); 2043 } 2044 2045 2046 /** 2047 * Apply options for a column 2048 * @param {object} oSettings dataTables settings object 2049 * @param {int} iCol column index to consider 2050 * @param {object} oOptions object with sType, bVisible and bSearchable etc 2051 * @memberof DataTable#oApi 2052 */ 2053 function _fnColumnOptions( oSettings, iCol, oOptions ) 2054 { 2055 var oCol = oSettings.aoColumns[ iCol ]; 2056 2057 /* User specified column options */ 2058 if ( oOptions !== undefined && oOptions !== null ) 2059 { 2060 // Backwards compatibility 2061 _fnCompatCols( oOptions ); 2062 2063 // Map camel case parameters to their Hungarian counterparts 2064 _fnCamelToHungarian( DataTable.defaults.column, oOptions, true ); 2065 2066 /* Backwards compatibility for mDataProp */ 2067 if ( oOptions.mDataProp !== undefined && !oOptions.mData ) 2068 { 2069 oOptions.mData = oOptions.mDataProp; 2070 } 2071 2072 if ( oOptions.sType ) 2073 { 2074 oCol._sManualType = oOptions.sType; 2075 } 2076 2077 // `class` is a reserved word in Javascript, so we need to provide 2078 // the ability to use a valid name for the camel case input 2079 if ( oOptions.className && ! oOptions.sClass ) 2080 { 2081 oOptions.sClass = oOptions.className; 2082 } 2083 2084 var origClass = oCol.sClass; 2085 2086 $.extend( oCol, oOptions ); 2087 _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" ); 2088 2089 // Merge class from previously defined classes with this one, rather than just 2090 // overwriting it in the extend above 2091 if (origClass !== oCol.sClass) { 2092 oCol.sClass = origClass + ' ' + oCol.sClass; 2093 } 2094 2095 /* iDataSort to be applied (backwards compatibility), but aDataSort will take 2096 * priority if defined 2097 */ 2098 if ( oOptions.iDataSort !== undefined ) 2099 { 2100 oCol.aDataSort = [ oOptions.iDataSort ]; 2101 } 2102 _fnMap( oCol, oOptions, "aDataSort" ); 2103 } 2104 2105 /* Cache the data get and set functions for speed */ 2106 var mDataSrc = oCol.mData; 2107 var mData = _fnGetObjectDataFn( mDataSrc ); 2108 2109 // The `render` option can be given as an array to access the helper rendering methods. 2110 // The first element is the rendering method to use, the rest are the parameters to pass 2111 if ( oCol.mRender && Array.isArray( oCol.mRender ) ) { 2112 var copy = oCol.mRender.slice(); 2113 var name = copy.shift(); 2114 2115 oCol.mRender = DataTable.render[name].apply(window, copy); 2116 } 2117 2118 oCol._render = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null; 2119 2120 var attrTest = function( src ) { 2121 return typeof src === 'string' && src.indexOf('@') !== -1; 2122 }; 2123 oCol._bAttrSrc = $.isPlainObject( mDataSrc ) && ( 2124 attrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter) 2125 ); 2126 oCol._setter = null; 2127 2128 oCol.fnGetData = function (rowData, type, meta) { 2129 var innerData = mData( rowData, type, undefined, meta ); 2130 2131 return oCol._render && type ? 2132 oCol._render( innerData, type, rowData, meta ) : 2133 innerData; 2134 }; 2135 oCol.fnSetData = function ( rowData, val, meta ) { 2136 return _fnSetObjectDataFn( mDataSrc )( rowData, val, meta ); 2137 }; 2138 2139 // Indicate if DataTables should read DOM data as an object or array 2140 // Used in _fnGetRowElements 2141 if ( typeof mDataSrc !== 'number' && ! oCol._isArrayHost ) { 2142 oSettings._rowReadObject = true; 2143 } 2144 2145 /* Feature sorting overrides column specific when off */ 2146 if ( !oSettings.oFeatures.bSort ) 2147 { 2148 oCol.bSortable = false;
2149 } 2150 } 2151 2152 2153 /** 2154 * Adjust the table column widths for new data. Note: you would probably want to 2155 * do a redraw after calling this function! 2156 * @param {object} settings dataTables settings object 2157 * @memberof DataTable#oApi 2158 */ 2159 function _fnAdjustColumnSizing ( settings ) 2160 { 2161 _fnCalculateColumnWidths( settings ); 2162 _fnColumnSizes( settings ); 2163 2164 var scroll = settings.oScroll; 2165 if ( scroll.sY !== '' || scroll.sX !== '') { 2166 _fnScrollDraw( settings ); 2167 } 2168 2169 _fnCallbackFire( settings, null, 'column-sizing', [settings] ); 2170 } 2171 2172 /** 2173 * Apply column sizes 2174 * 2175 * @param {*} settings DataTables settings object 2176 */ 2177 function _fnColumnSizes ( settings ) 2178 { 2179 var cols = settings.aoColumns; 2180 2181 for (var i=0 ; i<cols.length ; i++) { 2182 var width = _fnColumnsSumWidth(settings, [i], false, false); 2183 2184 cols[i].colEl.css('width', width); 2185 2186 if (settings.oScroll.sX) { 2187 cols[i].colEl.css('min-width', width); 2188 } 2189 } 2190 } 2191 2192 2193 /** 2194 * Convert the index of a visible column to the index in the data array (take account 2195 * of hidden columns) 2196 * @param {object} oSettings dataTables settings object 2197 * @param {int} iMatch Visible column index to lookup 2198 * @returns {int} i the data index 2199 * @memberof DataTable#oApi 2200 */ 2201 function _fnVisibleToColumnIndex( oSettings, iMatch ) 2202 { 2203 var aiVis = _fnGetColumns( oSettings, 'bVisible' ); 2204 2205 return typeof aiVis[iMatch] === 'number' ? 2206 aiVis[iMatch] : 2207 null; 2208 } 2209 2210 2211 /** 2212 * Convert the index of an index in the data array and convert it to the visible 2213 * column index (take account of hidden columns) 2214 * @param {int} iMatch Column index to lookup 2215 * @param {object} oSettings dataTables settings object 2216 * @returns {int} i the data index 2217 * @memberof DataTable#oApi 2218 */ 2219 function _fnColumnIndexToVisible( oSettings, iMatch ) 2220 { 2221 var aiVis = _fnGetColumns( oSettings, 'bVisible' ); 2222 var iPos = aiVis.indexOf(iMatch); 2223 2224 return iPos !== -1 ? iPos : null; 2225 } 2226 2227 2228 /** 2229 * Get the number of visible columns 2230 * @param {object} oSettings dataTables settings object 2231 * @returns {int} i the number of visible columns 2232 * @memberof DataTable#oApi 2233 */ 2234 function _fnVisbleColumns( settings ) 2235 { 2236 var layout = settings.aoHeader; 2237 var columns = settings.aoColumns; 2238 var vis = 0; 2239 2240 if ( layout.length ) { 2241 for ( var i=0, ien=layout[0].length ; i<ien ; i++ ) { 2242 if ( columns[i].bVisible && $(layout[0][i].cell).css('display') !== 'none' ) { 2243 vis++; 2244 } 2245 } 2246 } 2247 2248 return vis; 2249 } 2250 2251 2252 /** 2253 * Get an array of column indexes that match a given property 2254 * @param {object} oSettings dataTables settings object 2255 * @param {string} sParam Parameter in aoColumns to look for - typically 2256 * bVisible or bSearchable 2257 * @returns {array} Array of indexes with matched properties 2258 * @memberof DataTable#oApi 2259 */ 2260 function _fnGetColumns( oSettings, sParam ) 2261 { 2262 var a = []; 2263 2264 oSettings.aoColumns.map( function(val, i) { 2265 if ( val[sParam] ) { 2266 a.push( i ); 2267 } 2268 } ); 2269 2270 return a; 2271 } 2272 2273 /** 2274 * Allow the result from a type detection function to be `true` while 2275 * translating that into a string. Old type detection functions will 2276 * return the type name if it passes. An obect store would be better, 2277 * but not backwards compatible. 2278 * 2279 * @param {*} typeDetect Object or function for type detection 2280 * @param {*} res Result from the type detection function 2281 * @returns Type name or false 2282 */ 2283 function _typeResult (typeDetect, res) { 2284 return res === true 2285 ? typeDetect._name 2286 : res; 2287 } 2288 2289 /** 2290 * Calculate the 'type' of a column 2291 * @param {object} settings dataTables settings object 2292 * @memberof DataTable#oApi 2293 */ 2294 function _fnColumnTypes ( settings ) 2295 { 2296 var columns = settings.aoColumns; 2297 var data = settings.aoData; 2298 var types = DataTable.ext.type.detect; 2299 var i, ien, j, jen, k, ken; 2300 var col, detectedType, cache; 2301 2302 // For each column, spin over the data type detection functions, seeing if one matches 2303 for ( i=0, ien=columns.length ; i<ien ; i++ ) { 2304 col = columns[i]; 2305 cache = []; 2306 2307 if ( ! col.sType && col._sManualType ) { 2308 col.sType = col._sManualType; 2309 } 2310 else if ( ! col.sType ) {
2311 // With SSP type detection can be unreliable and error prone, so we provide a way 2312 // to turn it off. 2313 if (! settings.typeDetect) { 2314 return; 2315 } 2316 2317 for ( j=0, jen=types.length ; j<jen ; j++ ) { 2318 var typeDetect = types[j]; 2319 2320 // There can be either one, or three type detection functions 2321 var oneOf = typeDetect.oneOf; 2322 var allOf = typeDetect.allOf || typeDetect; 2323 var init = typeDetect.init; 2324 var one = false; 2325 2326 detectedType = null; 2327 2328 // Fast detect based on column assignment 2329 if (init) { 2330 detectedType = _typeResult(typeDetect, init(settings, col, i)); 2331 2332 if (detectedType) { 2333 col.sType = detectedType; 2334 break; 2335 } 2336 } 2337 2338 for ( k=0, ken=data.length ; k<ken ; k++ ) { 2339 if (! data[k]) { 2340 continue; 2341 } 2342 2343 // Use a cache array so we only need to get the type data 2344 // from the formatter once (when using multiple detectors) 2345 if ( cache[k] === undefined ) { 2346 cache[k] = _fnGetCellData( settings, k, i, 'type' ); 2347 } 2348 2349 // Only one data point in the column needs to match this function 2350 if (oneOf && ! one) { 2351 one = _typeResult(typeDetect, oneOf( cache[k], settings )); 2352 } 2353 2354 // All data points need to match this function 2355 detectedType = _typeResult(typeDetect, allOf( cache[k], settings )); 2356 2357 // If null, then this type can't apply to this column, so 2358 // rather than testing all cells, break out. There is an 2359 // exception for the last type which is `html`. We need to 2360 // scan all rows since it is possible to mix string and HTML 2361 // types 2362 if ( ! detectedType && j !== types.length-3 ) { 2363 break; 2364 } 2365 2366 // Only a single match is needed for html type since it is 2367 // bottom of the pile and very similar to string - but it 2368 // must not be empty 2369 if ( detectedType === 'html' && ! _empty(cache[k]) ) { 2370 break; 2371 } 2372 } 2373 2374 // Type is valid for all data points in the column - use this 2375 // type 2376 if ( (oneOf && one && detectedType) || (!oneOf && detectedType) ) { 2377 col.sType = detectedType; 2378 break; 2379 } 2380 } 2381 2382 // Fall back - if no type was detected, always use string 2383 if ( ! col.sType ) { 2384 col.sType = 'string'; 2385 } 2386 } 2387 2388 // Set class names for header / footer for auto type classes 2389 var autoClass = _ext.type.className[col.sType]; 2390 2391 if (autoClass) { 2392 _columnAutoClass(settings.aoHeader, i, autoClass); 2393 _columnAutoClass(settings.aoFooter, i, autoClass); 2394 } 2395 2396 var renderer = _ext.type.render[col.sType]; 2397 2398 // This can only happen once! There is no way to remove 2399 // a renderer. After the first time the renderer has 2400 // already been set so createTr will run the renderer itself. 2401 if (renderer && ! col._render) { 2402 col._render = DataTable.util.get(renderer); 2403 2404 _columnAutoRender(settings, i); 2405 } 2406 } 2407 } 2408 2409 /** 2410 * Apply an auto detected renderer to data which doesn't yet have 2411 * a renderer 2412 */ 2413 function _columnAutoRender(settings, colIdx) { 2414 var data = settings.aoData; 2415 2416 for (var i=0 ; i<data.length ; i++) { 2417 if (data[i].nTr) { 2418 // We have to update the display here since there is no 2419 // invalidation check for the data 2420 var display = _fnGetCellData( settings, i, colIdx, 'display' ); 2421 2422 data[i].displayData[colIdx] = display; 2423 _fnWriteCell(data[i].anCells[colIdx], display); 2424 2425 // No need to update sort / filter data since it has 2426 // been invalidated and will be re-read with the 2427 // renderer now applied 2428 } 2429 } 2430 } 2431 2432 /** 2433 * Apply a class name to a column's header cells 2434 */ 2435 function _columnAutoClass(container, colIdx, className) { 2436 container.forEach(function (row) { 2437 if (row[colIdx] && row[colIdx].unique) { 2438 _addClass(row[colIdx].cell, className); 2439 } 2440 }); 2441 } 2442 2443 /** 2444 * Take the column definitions and static columns arrays and calculate how 2445 * they relate to column indexes. The callback function will then apply the 2446 * definition found for a column to a suitable configuration object. 2447 * @param {object} oSettings dataTables settings object 2448 * @param {array} aoColDefs The aoColumnDefs array that is to be applied 2449 * @param {array} aoCols The aoColumns array that defines columns individually 2450 * @param {array} headerLayout Layout for header as it was loaded 2451 * @param {function} fn Callback function - takes two parameters, the calculated 2452 * column index and the definition for that column. 2453 * @memberof DataTable#oApi 2454 */ 2455 function _fnApplyColumnDefs( oSettings, aoColDefs, aoCols, headerLayout, fn ) 2456 { 2457 var i, iLen, j, jLen, k, kLen, def; 2458 var columns = oSettings.aoColumns; 2459 2460 if ( aoCols ) { 2461 for ( i=0, iLen=aoCols.length ; i<iLen ; i++ ) { 2462 if (aoCols[i] && aoCols[i].name) { 2463 columns[i].sName = aoCols[i].name; 2464 } 2465 } 2466 } 2467 2468 // Column definitions with aTargets 2469 if ( aoColDefs ) 2470 { 2471 /* Loop over the definitions array - loop in reverse so first instance has priority */ 2472 for ( i=aoColDefs.length-1 ; i>=0 ; i-- ) 2473 { 2474 def = aoColDefs[i]; 2475 2476 /* Each definition can target multiple columns, as it is an array */ 2477 var aTargets = def.target !== undefined 2478 ? def.target 2479 : def.targets !== undefined 2480 ? def.targets 2481 : def.aTargets; 2482 2483 if ( ! Array.isArray( aTargets ) ) 2484 { 2485 aTargets = [ aTargets ]; 2486 } 2487 2488 for ( j=0, jLen=aTargets.length ; j<jLen ; j++ ) 2489 { 2490 var target = aTargets[j]; 2491 2492 if ( typeof target === 'number' && target >= 0 ) 2493 { 2494 /* Add columns that we don't yet know about */ 2495 while( columns.length <= target ) 2496 { 2497 _fnAddColumn( oSettings ); 2498 } 2499
2500 /* Integer, basic index */ 2501 fn( target, def ); 2502 } 2503 else if ( typeof target === 'number' && target < 0 ) 2504 { 2505 /* Negative integer, right to left column counting */ 2506 fn( columns.length+target, def ); 2507 } 2508 else if ( typeof target === 'string' ) 2509 { 2510 for ( k=0, kLen=columns.length ; k<kLen ; k++ ) { 2511 if (target === '_all') { 2512 // Apply to all columns 2513 fn( k, def ); 2514 } 2515 else if (target.indexOf(':name') !== -1) { 2516 // Column selector 2517 if (columns[k].sName === target.replace(':name', '')) { 2518 fn( k, def ); 2519 } 2520 } 2521 else { 2522 // Cell selector 2523 headerLayout.forEach(function (row) { 2524 if (row[k]) { 2525 var cell = $(row[k].cell); 2526 2527 // Legacy support. Note that it means that we don't support 2528 // an element name selector only, since they are treated as 2529 // class names for 1.x compat. 2530 if (target.match(/^[a-z][\w-]*$/i)) { 2531 target = '.' + target; 2532 } 2533 2534 if (cell.is( target )) { 2535 fn( k, def ); 2536 } 2537 } 2538 }); 2539 } 2540 } 2541 } 2542 } 2543 } 2544 } 2545 2546 // Statically defined columns array 2547 if ( aoCols ) { 2548 for ( i=0, iLen=aoCols.length ; i<iLen ; i++ ) { 2549 fn( i, aoCols[i] ); 2550 } 2551 } 2552 } 2553 2554 2555 /** 2556 * Get the width for a given set of columns 2557 * 2558 * @param {*} settings DataTables settings object 2559 * @param {*} targets Columns - comma separated string or array of numbers 2560 * @param {*} original Use the original width (true) or calculated (false) 2561 * @param {*} incVisible Include visible columns (true) or not (false) 2562 * @returns Combined CSS value 2563 */ 2564 function _fnColumnsSumWidth( settings, targets, original, incVisible ) { 2565 if ( ! Array.isArray( targets ) ) { 2566 targets = _fnColumnsFromHeader( targets ); 2567 } 2568 2569 var sum = 0; 2570 var unit; 2571 var columns = settings.aoColumns; 2572 2573 for ( var i=0, ien=targets.length ; i<ien ; i++ ) { 2574 var column = columns[ targets[i] ]; 2575 var definedWidth = original ? 2576 column.sWidthOrig : 2577 column.sWidth; 2578 2579 if ( ! incVisible && column.bVisible === false ) { 2580 continue; 2581 } 2582 2583 if ( definedWidth === null || definedWidth === undefined ) { 2584 return null; // can't determine a defined width - browser defined 2585 } 2586 else if ( typeof definedWidth === 'number' ) { 2587 unit = 'px'; 2588 sum += definedWidth; 2589 } 2590 else { 2591 var matched = definedWidth.match(/([\d\.]+)([^\d]*)/); 2592 2593 if ( matched ) { 2594 sum += matched[1] * 1; 2595 unit = matched.length === 3 ? 2596 matched[2] : 2597 'px'; 2598 } 2599 } 2600 } 2601 2602 return sum + unit; 2603 } 2604 2605 function _fnColumnsFromHeader( cell ) 2606 { 2607 var attr = $(cell).closest('[data-dt-column]').attr('data-dt-column'); 2608 2609 if ( ! attr ) { 2610 return []; 2611 } 2612 2613 return attr.split(',').map( function (val) { 2614 return val * 1; 2615 } ); 2616 } 2617 /** 2618 * Add a data array to the table, creating DOM node etc. This is the parallel to 2619 * _fnGatherData, but for adding rows from a Javascript source, rather than a 2620 * DOM source. 2621 * @param {object} settings dataTables settings object 2622 * @param {array} data data array to be added 2623 * @param {node} [tr] TR element to add to the table - optional. If not given, 2624 * DataTables will create a row automatically 2625 * @param {array} [tds] Array of TD|TH elements for the row - must be given 2626 * if nTr is. 2627 * @returns {int} >=0 if successful (index of new aoData entry), -1 if failed 2628 * @memberof DataTable#oApi 2629 */ 2630 function _fnAddData ( settings, dataIn, tr, tds ) 2631 { 2632 /* Create the object for storing information about this new row */ 2633 var rowIdx = settings.aoData.length; 2634 var rowModel = $.extend( true, {}, DataTable.models.oRow, { 2635 src: tr ? 'dom' : 'data', 2636 idx: rowIdx 2637 } ); 2638 2639 rowModel._aData = dataIn; 2640 settings.aoData.push( rowModel ); 2641 2642 var columns = settings.aoColumns; 2643 2644 for ( var i=0, iLen=columns.length ; i<iLen ; i++ ) 2645 { 2646 // Invalidate the column types as the new data needs to be revalidated 2647 columns[i].sType = null; 2648 } 2649 2650 /* Add to the display array */ 2651 settings.aiDisplayMaster.push( rowIdx ); 2652 2653 var id = settings.rowIdFn( dataIn ); 2654 if ( id !== undefined ) { 2655 settings.aIds[ id ] = rowModel; 2656 } 2657 2658 /* Create the DOM information, or register it if already present */ 2659 if ( tr || ! settings.oFeatures.bDeferRender ) 2660 { 2661 _fnCreateTr( settings, rowIdx, tr, tds ); 2662 } 2663 2664 return rowIdx; 2665 } 2666 2667 2668 /** 2669 * Add one or more TR elements to the table. Generally we'd expect to 2670 * use this for reading data from a DOM sourced table, but it could be 2671 * used for an TR element. Note that if a TR is given, it is used (i.e.
2672 * it is not cloned). 2673 * @param {object} settings dataTables settings object 2674 * @param {array|node|jQuery} trs The TR element(s) to add to the table 2675 * @returns {array} Array of indexes for the added rows 2676 * @memberof DataTable#oApi 2677 */ 2678 function _fnAddTr( settings, trs ) 2679 { 2680 var row; 2681 2682 // Allow an individual node to be passed in 2683 if ( ! (trs instanceof $) ) { 2684 trs = $(trs); 2685 } 2686 2687 return trs.map( function (i, el) { 2688 row = _fnGetRowElements( settings, el ); 2689 return _fnAddData( settings, row.data, el, row.cells ); 2690 } ); 2691 } 2692 2693 2694 /** 2695 * Get the data for a given cell from the internal cache, taking into account data mapping 2696 * @param {object} settings dataTables settings object 2697 * @param {int} rowIdx aoData row id 2698 * @param {int} colIdx Column index 2699 * @param {string} type data get type ('display', 'type' 'filter|search' 'sort|order') 2700 * @returns {*} Cell data 2701 * @memberof DataTable#oApi 2702 */ 2703 function _fnGetCellData( settings, rowIdx, colIdx, type ) 2704 { 2705 if (type === 'search') { 2706 type = 'filter'; 2707 } 2708 else if (type === 'order') { 2709 type = 'sort'; 2710 } 2711 2712 var row = settings.aoData[rowIdx]; 2713 2714 if (! row) { 2715 return undefined; 2716 } 2717 2718 var draw = settings.iDraw; 2719 var col = settings.aoColumns[colIdx]; 2720 var rowData = row._aData; 2721 var defaultContent = col.sDefaultContent; 2722 var cellData = col.fnGetData( rowData, type, { 2723 settings: settings, 2724 row: rowIdx, 2725 col: colIdx 2726 } ); 2727 2728 // Allow for a node being returned for non-display types 2729 if (type !== 'display' && cellData && typeof cellData === 'object' && cellData.nodeName) { 2730 cellData = cellData.innerHTML; 2731 } 2732 2733 if ( cellData === undefined ) { 2734 if ( settings.iDrawError != draw && defaultContent === null ) { 2735 _fnLog( settings, 0, "Requested unknown parameter "+ 2736 (typeof col.mData=='function' ? '{function}' : "'"+col.mData+"'")+ 2737 " for row "+rowIdx+", column "+colIdx, 4 ); 2738 settings.iDrawError = draw; 2739 } 2740 return defaultContent; 2741 } 2742 2743 // When the data source is null and a specific data type is requested (i.e. 2744 // not the original data), we can use default column data 2745 if ( (cellData === rowData || cellData === null) && defaultContent !== null && type !== undefined ) { 2746 cellData = defaultContent; 2747 } 2748 else if ( typeof cellData === 'function' ) { 2749 // If the data source is a function, then we run it and use the return, 2750 // executing in the scope of the data object (for instances) 2751 return cellData.call( rowData ); 2752 } 2753 2754 if ( cellData === null && type === 'display' ) { 2755 return ''; 2756 } 2757 2758 if ( type === 'filter' ) { 2759 var fomatters = DataTable.ext.type.search; 2760 2761 if ( fomatters[ col.sType ] ) { 2762 cellData = fomatters[ col.sType ]( cellData ); 2763 } 2764 } 2765 2766 return cellData; 2767 } 2768 2769 2770 /** 2771 * Set the value for a specific cell, into the internal data cache 2772 * @param {object} settings dataTables settings object 2773 * @param {int} rowIdx aoData row id 2774 * @param {int} colIdx Column index 2775 * @param {*} val Value to set 2776 * @memberof DataTable#oApi 2777 */ 2778 function _fnSetCellData( settings, rowIdx, colIdx, val ) 2779 { 2780 var col = settings.aoColumns[colIdx]; 2781 var rowData = settings.aoData[rowIdx]._aData; 2782 2783 col.fnSetData( rowData, val, { 2784 settings: settings, 2785 row: rowIdx, 2786 col: colIdx 2787 } ); 2788 } 2789 2790 /** 2791 * Write a value to a cell 2792 * @param {*} td Cell 2793 * @param {*} val Value 2794 */ 2795 function _fnWriteCell(td, val) 2796 { 2797 if (val && typeof val === 'object' && val.nodeName) { 2798 $(td) 2799 .empty() 2800 .append(val); 2801 } 2802 else { 2803 td.innerHTML = val; 2804 } 2805 } 2806 2807 2808 // Private variable that is used to match action syntax in the data property object 2809 var __reArray = /\[.*?\]$/; 2810 var __reFn = /\(\)$/; 2811 2812 /** 2813 * Split string on periods, taking into account escaped periods 2814 * @param {string} str String to split 2815 * @return {array} Split string 2816 */ 2817 function _fnSplitObjNotation( str ) 2818 { 2819 var parts = str.match(/(\\.|[^.])+/g) || ['']; 2820 2821 return parts.map( function ( s ) { 2822 return s.replace(/\\\./g, '.'); 2823 } ); 2824 } 2825 2826 2827 /** 2828 * Return a function that can be used to get data from a source object, taking 2829 * into account the ability to use nested objects as a source 2830 * @param {string|int|function} mSource The data source for the object 2831 * @returns {function} Data get function 2832 * @memberof DataTable#oApi 2833 */ 2834 var _fnGetObjectDataFn = DataTable.util.get; 2835 2836 2837 /** 2838 * Return a function that can be used to set data from a source object, taking 2839 * into account the ability to use nested objects as a source 2840 * @param {string|int|function} mSource The data source for the object 2841 * @returns {function} Data set function 2842 * @memberof DataTable#oApi 2843 */ 2844 var _fnSetObjectDataFn = DataTable.util.set; 2845 2846 2847 /** 2848 * Return an array with the full table data 2849 * @param {object} oSettings dataTables settings object 2850 * @returns array {array} aData Master data array 2851 * @memberof DataTable#oApi 2852 */ 2853 function _fnGetDataMaster ( settings ) 2854 { 2855 return _pluck( settings.aoData, '_aData' ); 2856 } 2857 2858 2859 /** 2860 * Nuke the table 2861 * @param {object} oSettings dataTables settings object 2862 * @memberof DataTable#oApi 2863 */ 2864 function _fnClearTable( settings ) 2865 { 2866 settings.aoData.length = 0; 2867 settings.aiDisplayMaster.length = 0; 2868 settings.aiDisplay.length = 0; 2869 settings.aIds = {}; 2870 } 2871 2872 2873 /**
2874 * Mark cached data as invalid such that a re-read of the data will occur when 2875 * the cached data is next requested. Also update from the data source object. 2876 * 2877 * @param {object} settings DataTables settings object 2878 * @param {int} rowIdx Row index to invalidate 2879 * @param {string} [src] Source to invalidate from: undefined, 'auto', 'dom' 2880 * or 'data' 2881 * @param {int} [colIdx] Column index to invalidate. If undefined the whole 2882 * row will be invalidated 2883 * @memberof DataTable#oApi 2884 * 2885 * @todo For the modularisation of v1.11 this will need to become a callback, so 2886 * the sort and filter methods can subscribe to it. That will required 2887 * initialisation options for sorting, which is why it is not already baked in 2888 */ 2889 function _fnInvalidate( settings, rowIdx, src, colIdx ) 2890 { 2891 var row = settings.aoData[ rowIdx ]; 2892 var i, ien; 2893 2894 // Remove the cached data for the row 2895 row._aSortData = null; 2896 row._aFilterData = null; 2897 row.displayData = null; 2898 2899 // Are we reading last data from DOM or the data object? 2900 if ( src === 'dom' || ((! src || src === 'auto') && row.src === 'dom') ) { 2901 // Read the data from the DOM 2902 row._aData = _fnGetRowElements( 2903 settings, row, colIdx, colIdx === undefined ? undefined : row._aData 2904 ) 2905 .data; 2906 } 2907 else { 2908 // Reading from data object, update the DOM 2909 var cells = row.anCells; 2910 var display = _fnGetRowDisplay(settings, rowIdx); 2911 2912 if ( cells ) { 2913 if ( colIdx !== undefined ) { 2914 _fnWriteCell(cells[colIdx], display[colIdx]); 2915 } 2916 else { 2917 for ( i=0, ien=cells.length ; i<ien ; i++ ) { 2918 _fnWriteCell(cells[i], display[i]); 2919 } 2920 } 2921 } 2922 } 2923 2924 // Column specific invalidation 2925 var cols = settings.aoColumns; 2926 if ( colIdx !== undefined ) { 2927 // Type - the data might have changed 2928 cols[ colIdx ].sType = null; 2929 2930 // Max length string. Its a fairly cheep recalculation, so not worth 2931 // something more complicated 2932 cols[ colIdx ].maxLenString = null; 2933 } 2934 else { 2935 for ( i=0, ien=cols.length ; i<ien ; i++ ) { 2936 cols[i].sType = null; 2937 cols[i].maxLenString = null; 2938 } 2939 2940 // Update DataTables special `DT_*` attributes for the row 2941 _fnRowAttributes( settings, row ); 2942 } 2943 } 2944 2945 2946 /** 2947 * Build a data source object from an HTML row, reading the contents of the 2948 * cells that are in the row. 2949 * 2950 * @param {object} settings DataTables settings object 2951 * @param {node|object} TR element from which to read data or existing row 2952 * object from which to re-read the data from the cells 2953 * @param {int} [colIdx] Optional column index 2954 * @param {array|object} [d] Data source object. If `colIdx` is given then this 2955 * parameter should also be given and will be used to write the data into. 2956 * Only the column in question will be written 2957 * @returns {object} Object with two parameters: `data` the data read, in 2958 * document order, and `cells` and array of nodes (they can be useful to the 2959 * caller, so rather than needing a second traversal to get them, just return 2960 * them from here). 2961 * @memberof DataTable#oApi 2962 */ 2963 function _fnGetRowElements( settings, row, colIdx, d ) 2964 { 2965 var 2966 tds = [], 2967 td = row.firstChild, 2968 name, col, i=0, contents, 2969 columns = settings.aoColumns, 2970 objectRead = settings._rowReadObject; 2971 2972 // Allow the data object to be passed in, or construct 2973 d = d !== undefined ? 2974 d : 2975 objectRead ? 2976 {} : 2977 []; 2978 2979 var attr = function ( str, td ) { 2980 if ( typeof str === 'string' ) { 2981 var idx = str.indexOf('@'); 2982 2983 if ( idx !== -1 ) { 2984 var attr = str.substring( idx+1 ); 2985 var setter = _fnSetObjectDataFn( str ); 2986 setter( d, td.getAttribute( attr ) ); 2987 } 2988 } 2989 }; 2990 2991 // Read data from a cell and store into the data object 2992 var cellProcess = function ( cell ) { 2993 if ( colIdx === undefined || colIdx === i ) { 2994 col = columns[i]; 2995 contents = (cell.innerHTML).trim(); 2996 2997 if ( col && col._bAttrSrc ) { 2998 var setter = _fnSetObjectDataFn( col.mData._ ); 2999 setter( d, contents ); 3000 3001 attr( col.mData.sort, cell ); 3002 attr( col.mData.type, cell ); 3003 attr( col.mData.filter, cell ); 3004 } 3005 else { 3006 // Depending on the `data` option for the columns the data can 3007 // be read to either an object or an array.
3008 if ( objectRead ) { 3009 if ( ! col._setter ) { 3010 // Cache the setter function 3011 col._setter = _fnSetObjectDataFn( col.mData ); 3012 } 3013 col._setter( d, contents ); 3014 } 3015 else { 3016 d[i] = contents; 3017 } 3018 } 3019 } 3020 3021 i++; 3022 }; 3023 3024 if ( td ) { 3025 // `tr` element was passed in 3026 while ( td ) { 3027 name = td.nodeName.toUpperCase(); 3028 3029 if ( name == "TD" || name == "TH" ) { 3030 cellProcess( td ); 3031 tds.push( td ); 3032 } 3033 3034 td = td.nextSibling; 3035 } 3036 } 3037 else { 3038 // Existing row object passed in 3039 tds = row.anCells; 3040 3041 for ( var j=0, jen=tds.length ; j<jen ; j++ ) { 3042 cellProcess( tds[j] ); 3043 } 3044 } 3045 3046 // Read the ID from the DOM if present 3047 var rowNode = row.firstChild ? row : row.nTr; 3048 3049 if ( rowNode ) { 3050 var id = rowNode.getAttribute( 'id' ); 3051 3052 if ( id ) { 3053 _fnSetObjectDataFn( settings.rowId )( d, id ); 3054 } 3055 } 3056 3057 return { 3058 data: d, 3059 cells: tds 3060 }; 3061 } 3062 3063 /** 3064 * Render and cache a row's display data for the columns, if required 3065 * @returns 3066 */ 3067 function _fnGetRowDisplay (settings, rowIdx) { 3068 var rowModal = settings.aoData[rowIdx]; 3069 var columns = settings.aoColumns; 3070 3071 if (! rowModal.displayData) { 3072 // Need to render and cache 3073 rowModal.displayData = []; 3074 3075 for ( var colIdx=0, len=columns.length ; colIdx<len ; colIdx++ ) { 3076 rowModal.displayData.push( 3077 _fnGetCellData( settings, rowIdx, colIdx, 'display' ) 3078 ); 3079 } 3080 } 3081 3082 return rowModal.displayData; 3083 } 3084 3085 /** 3086 * Create a new TR element (and it's TD children) for a row 3087 * @param {object} oSettings dataTables settings object 3088 * @param {int} iRow Row to consider 3089 * @param {node} [nTrIn] TR element to add to the table - optional. If not given, 3090 * DataTables will create a row automatically 3091 * @param {array} [anTds] Array of TD|TH elements for the row - must be given 3092 * if nTr is. 3093 * @memberof DataTable#oApi 3094 */ 3095 function _fnCreateTr ( oSettings, iRow, nTrIn, anTds ) 3096 { 3097 var 3098 row = oSettings.aoData[iRow], 3099 rowData = row._aData, 3100 cells = [], 3101 nTr, nTd, oCol, 3102 i, iLen, create, 3103 trClass = oSettings.oClasses.tbody.row; 3104 3105 if ( row.nTr === null ) 3106 { 3107 nTr = nTrIn || document.createElement('tr'); 3108 3109 row.nTr = nTr; 3110 row.anCells = cells; 3111 3112 _addClass(nTr, trClass); 3113 3114 /* Use a private property on the node to allow reserve mapping from the node 3115 * to the aoData array for fast look up 3116 */ 3117 nTr._DT_RowIndex = iRow; 3118 3119 /* Special parameters can be given by the data source to be used on the row */ 3120 _fnRowAttributes( oSettings, row ); 3121 3122 /* Process each column */ 3123 for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ ) 3124 { 3125 oCol = oSettings.aoColumns[i]; 3126 create = nTrIn && anTds[i] ? false : true; 3127 3128 nTd = create ? document.createElement( oCol.sCellType ) : anTds[i]; 3129 3130 if (! nTd) { 3131 _fnLog( oSettings, 0, 'Incorrect column count', 18 ); 3132 } 3133 3134 nTd._DT_CellIndex = { 3135 row: iRow, 3136 column: i 3137 }; 3138 3139 cells.push( nTd ); 3140 3141 var display = _fnGetRowDisplay(oSettings, iRow); 3142 3143 // Need to create the HTML if new, or if a rendering function is defined 3144 if ( 3145 create || 3146 ( 3147 (oCol.mRender || oCol.mData !== i) && 3148 (!$.isPlainObject(oCol.mData) || oCol.mData._ !== i+'.display') 3149 ) 3150 ) { 3151 _fnWriteCell(nTd, display[i]); 3152 } 3153 3154 // column class 3155 _addClass(nTd, oCol.sClass); 3156 3157 // Visibility - add or remove as required 3158 if ( oCol.bVisible && create ) 3159 { 3160 nTr.appendChild( nTd ); 3161 } 3162 else if ( ! oCol.bVisible && ! create ) 3163 { 3164 nTd.parentNode.removeChild( nTd ); 3165 } 3166 3167 if ( oCol.fnCreatedCell ) 3168 { 3169 oCol.fnCreatedCell.call( oSettings.oInstance, 3170 nTd, _fnGetCellData( oSettings, iRow, i ), rowData, iRow, i 3171 ); 3172 } 3173 } 3174 3175 _fnCallbackFire( oSettings, 'aoRowCreatedCallback', 'row-created', [nTr, rowData, iRow, cells] ); 3176 } 3177 else { 3178 _addClass(row.nTr, trClass); 3179 } 3180 } 3181 3182 3183 /** 3184 * Add attributes to a row based on the special `DT_*` parameters in a data 3185 * source object. 3186 * @param {object} settings DataTables settings object 3187 * @param {object} DataTables row object for the row to be modified 3188 * @memberof DataTable#oApi 3189 */ 3190 function _fnRowAttributes( settings, row ) 3191 { 3192 var tr = row.nTr; 3193 var data = row._aData; 3194 3195 if ( tr ) { 3196 var id = settings.rowIdFn( data ); 3197 3198 if ( id ) { 3199 tr.id = id; 3200 } 3201 3202 if ( data.DT_RowClass ) { 3203 // Remove any classes added by DT_RowClass before 3204 var a = data.DT_RowClass.split(' '); 3205 row.__rowc = row.__rowc ? 3206 _unique( row.__rowc.concat( a ) ) : 3207 a; 3208 3209 $(tr) 3210 .removeClass( row.__rowc.join(' ') ) 3211 .addClass( data.DT_RowClass ); 3212 } 3213 3214 if ( data.DT_RowAttr ) { 3215 $(tr).attr( data.DT_RowAttr ); 3216 } 3217 3218 if ( data.DT_RowData ) { 3219 $(tr).data( data.DT_RowData ); 3220 } 3221 } 3222 } 3223 3224 3225 /** 3226 * Create the HTML header for the table 3227 * @param {object} oSettings dataTables settings object 3228 * @memberof DataTable#oApi 3229 */ 3230 function _fnBuildHead( settings, side ) 3231 { 3232 var classes = settings.oClasses; 3233 var columns = settings.aoColumns; 3234 var i, ien, row; 3235 var target = side === 'header' 3236 ? settings.nTHead 3237 : settings.nTFoot; 3238 var titleProp = side === 'header' ? 'sTitle' : side; 3239 3240 // Footer might be defined 3241 if (! target) { 3242 return; 3243 } 3244 3245 // If no cells yet and we have content for them, then create 3246 if (side === 'header' || _pluck(settings.aoColumns, titleProp).join('')) { 3247 row = $('tr', target); 3248 3249 // Add a row if needed 3250 if (! row.length) { 3251 row = $('<tr/>').appendTo(target) 3252 } 3253 3254 // Add the number of cells needed to make up to the number of columns 3255 if (row.length === 1) { 3256 var cellCount = 0; 3257 3258 $('td, th', row).each(function () { 3259 cellCount += this.colSpan; 3260 }); 3261 3262 for ( i=cellCount, ien=columns.length ; i<ien ; i++ ) { 3263 $('<th/>') 3264 .html( columns[i][titleProp] || '' ) 3265 .appendTo( row ); 3266 } 3267 } 3268 } 3269 3270 var detected = _fnDetectHeader( settings, target, true ); 3271 3272 if (side === 'header') { 3273 settings.aoHeader = detected;
3274 $('tr', target).addClass(classes.thead.row); 3275 } 3276 else { 3277 settings.aoFooter = detected; 3278 $('tr', target).addClass(classes.tfoot.row); 3279 } 3280 3281 // Every cell needs to be passed through the renderer 3282 $(target).children('tr').children('th, td') 3283 .each( function () { 3284 _fnRenderer( settings, side )( 3285 settings, $(this), classes 3286 ); 3287 } ); 3288 } 3289 3290 /** 3291 * Build a layout structure for a header or footer 3292 * 3293 * @param {*} settings DataTables settings 3294 * @param {*} source Source layout array 3295 * @param {*} incColumns What columns should be included 3296 * @returns Layout array in column index order 3297 */ 3298 function _fnHeaderLayout( settings, source, incColumns ) 3299 { 3300 var row, column, cell; 3301 var local = []; 3302 var structure = []; 3303 var columns = settings.aoColumns; 3304 var columnCount = columns.length; 3305 var rowspan, colspan; 3306 3307 if ( ! source ) { 3308 return; 3309 } 3310 3311 // Default is to work on only visible columns 3312 if ( ! incColumns ) { 3313 incColumns = _range(columnCount) 3314 .filter(function (idx) { 3315 return columns[idx].bVisible; 3316 }); 3317 } 3318 3319 // Make a copy of the master layout array, but with only the columns we want 3320 for ( row=0 ; row<source.length ; row++ ) { 3321 // Remove any columns we haven't selected 3322 local[row] = source[row].slice().filter(function (cell, i) { 3323 return incColumns.includes(i); 3324 }); 3325 3326 // Prep the structure array - it needs an element for each row 3327 structure.push( [] ); 3328 } 3329 3330 for ( row=0 ; row<local.length ; row++ ) { 3331 for ( column=0 ; column<local[row].length ; column++ ) { 3332 rowspan = 1; 3333 colspan = 1; 3334 3335 // Check to see if there is already a cell (row/colspan) covering our target 3336 // insert point. If there is, then there is nothing to do. 3337 if ( structure[row][column] === undefined ) { 3338 cell = local[row][column].cell; 3339 3340 // Expand for rowspan 3341 while ( 3342 local[row+rowspan] !== undefined && 3343 local[row][column].cell == local[row+rowspan][column].cell 3344 ) { 3345 structure[row+rowspan][column] = null; 3346 rowspan++; 3347 } 3348 3349 // And for colspan 3350 while ( 3351 local[row][column+colspan] !== undefined && 3352 local[row][column].cell == local[row][column+colspan].cell 3353 ) { 3354 // Which also needs to go over rows 3355 for ( var k=0 ; k<rowspan ; k++ ) { 3356 structure[row+k][column+colspan] = null; 3357 } 3358 3359 colspan++; 3360 } 3361 3362 var titleSpan = $('span.dt-column-title', cell); 3363 3364 structure[row][column] = { 3365 cell: cell, 3366 colspan: colspan, 3367 rowspan: rowspan, 3368 title: titleSpan.length 3369 ? titleSpan.html() 3370 : $(cell).html() 3371 }; 3372 } 3373 } 3374 } 3375 3376 return structure; 3377 } 3378 3379 3380 /** 3381 * Draw the header (or footer) element based on the column visibility states. 3382 * 3383 * @param object oSettings dataTables settings object 3384 * @param array aoSource Layout array from _fnDetectHeader 3385 * @memberof DataTable#oApi 3386 */ 3387 function _fnDrawHead( settings, source ) 3388 { 3389 var layout = _fnHeaderLayout(settings, source); 3390 var tr, n; 3391 3392 for ( var row=0 ; row<source.length ; row++ ) { 3393 tr = source[row].row; 3394 3395 // All cells are going to be replaced, so empty out the row 3396 // Can't use $().empty() as that kills event handlers 3397 if (tr) { 3398 while( (n = tr.firstChild) ) { 3399 tr.removeChild( n ); 3400 } 3401 } 3402 3403 for ( var column=0 ; column<layout[row].length ; column++ ) { 3404 var point = layout[row][column]; 3405 3406 if (point) { 3407 $(point.cell) 3408 .appendTo(tr) 3409 .attr('rowspan', point.rowspan) 3410 .attr('colspan', point.colspan); 3411 } 3412 } 3413 } 3414 } 3415 3416 3417 /** 3418 * Insert the required TR nodes into the table for display 3419 * @param {object} oSettings dataTables settings object 3420 * @param ajaxComplete true after ajax call to complete rendering 3421 * @memberof DataTable#oApi 3422 */ 3423 function _fnDraw( oSettings, ajaxComplete ) 3424 { 3425 // Allow for state saving and a custom start position 3426 _fnStart( oSettings ); 3427 3428 /* Provide a pre-callback function which can be used to cancel the draw is false is returned */ 3429 var aPreDraw = _fnCallbackFire( oSettings, 'aoPreDrawCallback', 'preDraw', [oSettings] ); 3430 if ( aPreDraw.indexOf(false) !== -1 ) 3431 { 3432 _fnProcessingDisplay( oSettings, false ); 3433 return; 3434 } 3435 3436 var anRows = []; 3437 var iRowCount = 0; 3438 var bServerSide = _fnDataSource( oSettings ) == 'ssp'; 3439 var aiDisplay = oSettings.aiDisplay; 3440 var iDisplayStart = oSettings._iDisplayStart; 3441 var iDisplayEnd = oSettings.fnDisplayEnd(); 3442 var columns = oSettings.aoColumns; 3443 var body = $(oSettings.nTBody); 3444 3445 oSettings.bDrawing = true; 3446 3447 /* Server-side processing draw intercept */ 3448 if ( oSettings.deferLoading ) 3449 { 3450 oSettings.deferLoading = false;
3451 oSettings.iDraw++; 3452 _fnProcessingDisplay( oSettings, false ); 3453 } 3454 else if ( !bServerSide ) 3455 { 3456 oSettings.iDraw++; 3457 } 3458 else if ( !oSettings.bDestroying && !ajaxComplete) 3459 { 3460 // Show loading message for server-side processing 3461 if (oSettings.iDraw === 0) { 3462 body.empty().append(_emptyRow(oSettings)); 3463 } 3464 3465 _fnAjaxUpdate( oSettings ); 3466 return; 3467 } 3468 3469 if ( aiDisplay.length !== 0 ) 3470 { 3471 var iStart = bServerSide ? 0 : iDisplayStart; 3472 var iEnd = bServerSide ? oSettings.aoData.length : iDisplayEnd; 3473 3474 for ( var j=iStart ; j<iEnd ; j++ ) 3475 { 3476 var iDataIndex = aiDisplay[j]; 3477 var aoData = oSettings.aoData[ iDataIndex ]; 3478 if ( aoData.nTr === null ) 3479 { 3480 _fnCreateTr( oSettings, iDataIndex ); 3481 } 3482 3483 var nRow = aoData.nTr; 3484 3485 // Add various classes as needed 3486 for (var i=0 ; i<columns.length ; i++) { 3487 var col = columns[i]; 3488 var td = aoData.anCells[i]; 3489 3490 _addClass(td, _ext.type.className[col.sType]); // auto class 3491 _addClass(td, oSettings.oClasses.tbody.cell); // all cells 3492 } 3493 3494 // Row callback functions - might want to manipulate the row 3495 // iRowCount and j are not currently documented. Are they at all 3496 // useful? 3497 _fnCallbackFire( oSettings, 'aoRowCallback', null, 3498 [nRow, aoData._aData, iRowCount, j, iDataIndex] ); 3499 3500 anRows.push( nRow ); 3501 iRowCount++; 3502 } 3503 } 3504 else 3505 { 3506 anRows[ 0 ] = _emptyRow(oSettings); 3507 } 3508 3509 /* Header and footer callbacks */ 3510 _fnCallbackFire( oSettings, 'aoHeaderCallback', 'header', [ $(oSettings.nTHead).children('tr')[0], 3511 _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] ); 3512 3513 _fnCallbackFire( oSettings, 'aoFooterCallback', 'footer', [ $(oSettings.nTFoot).children('tr')[0], 3514 _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] ); 3515 3516 // replaceChildren is faster, but only became widespread in 2020, 3517 // so a fall back in jQuery is provided for older browsers. 3518 if (body[0].replaceChildren) { 3519 body[0].replaceChildren.apply(body[0], anRows); 3520 } 3521 else { 3522 body.children().detach(); 3523 body.append( $(anRows) ); 3524 } 3525 3526 // Empty table needs a specific class 3527 $(oSettings.nTableWrapper).toggleClass('dt-empty-footer', $('tr', oSettings.nTFoot).length === 0); 3528 3529 /* Call all required callback functions for the end of a draw */ 3530 _fnCallbackFire( oSettings, 'aoDrawCallback', 'draw', [oSettings], true ); 3531 3532 /* Draw is complete, sorting and filtering must be as well */ 3533 oSettings.bSorted = false; 3534 oSettings.bFiltered = false; 3535 oSettings.bDrawing = false; 3536 } 3537 3538 3539 /** 3540 * Redraw the table - taking account of the various features which are enabled 3541 * @param {object} oSettings dataTables settings object 3542 * @param {boolean} [holdPosition] Keep the current paging position. By default 3543 * the paging is reset to the first page 3544 * @memberof DataTable#oApi 3545 */ 3546 function _fnReDraw( settings, holdPosition, recompute ) 3547 { 3548 var 3549 features = settings.oFeatures, 3550 sort = features.bSort, 3551 filter = features.bFilter; 3552 3553 if (recompute === undefined || recompute === true) { 3554 // Resolve any column types that are unknown due to addition or invalidation 3555 _fnColumnTypes( settings ); 3556 3557 if ( sort ) { 3558 _fnSort( settings ); 3559 } 3560 3561 if ( filter ) { 3562 _fnFilterComplete( settings, settings.oPreviousSearch ); 3563 } 3564 else { 3565 // No filtering, so we want to just use the display master 3566 settings.aiDisplay = settings.aiDisplayMaster.slice(); 3567 } 3568 } 3569 3570 if ( holdPosition !== true ) { 3571 settings._iDisplayStart = 0; 3572 } 3573 3574 // Let any modules know about the draw hold position state (used by 3575 // scrolling internally) 3576 settings._drawHold = holdPosition; 3577 3578 _fnDraw( settings ); 3579 3580 settings.api.one('draw', function () { 3581 settings._drawHold = false; 3582 }); 3583 } 3584 3585 3586 /* 3587 * Table is empty - create a row with an empty message in it 3588 */ 3589 function _emptyRow ( settings ) { 3590 var oLang = settings.oLanguage; 3591 var zero = oLang.sZeroRecords; 3592 var dataSrc = _fnDataSource( settings ); 3593 3594 // Make use of the fact that settings.json is only set once the initial data has 3595 // been loaded. Show loading when that isn't the case 3596 if ((dataSrc === 'ssp' || dataSrc === 'ajax') && ! settings.json) { 3597 zero = oLang.sLoadingRecords; 3598 } 3599 else if ( oLang.sEmptyTable && settings.fnRecordsTotal() === 0 ) 3600 { 3601 zero = oLang.sEmptyTable; 3602 } 3603 3604 return $( '<tr/>' ) 3605 .append( $('<td />', { 3606 'colSpan': _fnVisbleColumns( settings ), 3607 'class': settings.oClasses.empty.row 3608 } ).html( zero ) )[0]; 3609 } 3610 3611 3612 /** 3613 * Expand the layout items into an object for the rendering function 3614 */ 3615 function _layoutItems (row, align, items) { 3616 if ( Array.isArray(items)) { 3617 for (var i=0 ; i<items.length ; i++) { 3618 _layoutItems(row, align, items[i]); 3619 } 3620 3621 return; 3622 } 3623 3624 var rowCell = row[align]; 3625 3626 // If it is an object, then there can be multiple features contained in it 3627 if ( $.isPlainObject( items ) ) { 3628 // A feature plugin cannot be named "features" due to this check 3629 if (items.features) { 3630 if (items.rowId) { 3631 row.id = items.rowId; 3632 } 3633 if (items.rowClass) { 3634 row.className = items.rowClass; 3635 } 3636 3637 rowCell.id = items.id; 3638 rowCell.className = items.className; 3639 3640 _layoutItems(row, align, items.features); 3641 } 3642 else { 3643 Object.keys(items).map(function (key) { 3644 rowCell.contents.push( { 3645 feature: key, 3646 opts: items[key] 3647 }); 3648 }); 3649 } 3650 } 3651 else { 3652 rowCell.contents.push(items); 3653 } 3654 } 3655 3656 /** 3657 * Find, or create a layout row 3658 */ 3659 function _layoutGetRow(rows, rowNum, align) { 3660 var row; 3661 3662 // Find existing rows 3663 for (var i=0; i<rows.length; i++) { 3664 row = rows[i]; 3665 3666 if (row.rowNum === rowNum) { 3667 // full is on its own, but start and end share a row 3668 if ( 3669 (align === 'full' && row.full) || 3670 ((align === 'start' || align === 'end') && (row.start || row.end)) 3671 ) { 3672 if (! row[align]) { 3673 row[align] = { 3674 contents: [] 3675 }; 3676 } 3677 3678 return row; 3679 } 3680 } 3681 } 3682 3683 // If we get this far, then there was no match, create a new row 3684 row = { 3685 rowNum: rowNum 3686 }; 3687 3688 row[align] = { 3689 contents: [] 3690 }; 3691 3692 rows.push(row); 3693 3694 return row; 3695 } 3696 3697 /** 3698 * Convert a `layout` object given by a user to the object structure needed 3699 * for the renderer. This is done twice, once for above and once for below
3700 * the table. Ordering must also be considered. 3701 * 3702 * @param {*} settings DataTables settings object 3703 * @param {*} layout Layout object to convert 3704 * @param {string} side `top` or `bottom` 3705 * @returns Converted array structure - one item for each row. 3706 */ 3707 function _layoutArray ( settings, layout, side ) { 3708 var rows = []; 3709 3710 // Split out into an array 3711 $.each( layout, function ( pos, items ) { 3712 if (items === null) { 3713 return; 3714 } 3715 3716 var parts = pos.match(/^([a-z]+)([0-9]*)([A-Za-z]*)$/); 3717 var rowNum = parts[2] 3718 ? parts[2] * 1 3719 : 0; 3720 var align = parts[3] 3721 ? parts[3].toLowerCase() 3722 : 'full'; 3723 3724 // Filter out the side we aren't interested in 3725 if (parts[1] !== side) { 3726 return; 3727 } 3728 3729 // Get or create the row we should attach to 3730 var row = _layoutGetRow(rows, rowNum, align); 3731 3732 _layoutItems(row, align, items); 3733 }); 3734 3735 // Order by item identifier 3736 rows.sort( function ( a, b ) { 3737 var order1 = a.rowNum; 3738 var order2 = b.rowNum; 3739 3740 // If both in the same row, then the row with `full` comes first 3741 if (order1 === order2) { 3742 var ret = a.full && ! b.full ? -1 : 1; 3743 3744 return side === 'bottom' 3745 ? ret * -1 3746 : ret; 3747 } 3748 3749 return order2 - order1; 3750 } ); 3751 3752 // Invert for below the table 3753 if ( side === 'bottom' ) { 3754 rows.reverse(); 3755 } 3756 3757 for (var row = 0; row<rows.length; row++) { 3758 delete rows[row].rowNum; 3759 3760 _layoutResolve(settings, rows[row]); 3761 } 3762 3763 return rows; 3764 } 3765 3766 3767 /** 3768 * Convert the contents of a row's layout object to nodes that can be inserted 3769 * into the document by a renderer. Execute functions, look up plug-ins, etc. 3770 * 3771 * @param {*} settings DataTables settings object 3772 * @param {*} row Layout object for this row 3773 */ 3774 function _layoutResolve( settings, row ) { 3775 var getFeature = function (feature, opts) { 3776 if ( ! _ext.features[ feature ] ) { 3777 _fnLog( settings, 0, 'Unknown feature: '+ feature ); 3778 } 3779 3780 return _ext.features[ feature ].apply( this, [settings, opts] ); 3781 }; 3782 3783 var resolve = function ( item ) { 3784 if (! row[ item ]) { 3785 return; 3786 } 3787 3788 var line = row[ item ].contents; 3789 3790 for ( var i=0, ien=line.length ; i<ien ; i++ ) { 3791 if ( ! line[i] ) { 3792 continue; 3793 } 3794 else if ( typeof line[i] === 'string' ) { 3795 line[i] = getFeature( line[i], null ); 3796 } 3797 else if ( $.isPlainObject(line[i]) ) { 3798 // If it's an object, it just has feature and opts properties from 3799 // the transform in _layoutArray 3800 line[i] = getFeature(line[i].feature, line[i].opts); 3801 } 3802 else if ( typeof line[i].node === 'function' ) { 3803 line[i] = line[i].node( settings ); 3804 } 3805 else if ( typeof line[i] === 'function' ) { 3806 var inst = line[i]( settings ); 3807 3808 line[i] = typeof inst.node === 'function' ? 3809 inst.node() : 3810 inst; 3811 } 3812 } 3813 }; 3814 3815 resolve('start'); 3816 resolve('end'); 3817 resolve('full'); 3818 } 3819 3820 3821 /** 3822 * Add the options to the page HTML for the table 3823 * @param {object} settings DataTables settings object 3824 * @memberof DataTable#oApi 3825 */ 3826 function _fnAddOptionsHtml ( settings ) 3827 { 3828 var classes = settings.oClasses; 3829 var table = $(settings.nTable); 3830 3831 // Wrapper div around everything DataTables controls 3832 var insert = $('<div/>') 3833 .attr({ 3834 id: settings.sTableId+'_wrapper', 3835 'class': classes.container 3836 }) 3837 .insertBefore(table); 3838 3839 settings.nTableWrapper = insert[0]; 3840 3841 if (settings.sDom) { 3842 // Legacy 3843 _fnLayoutDom(settings, settings.sDom, insert); 3844 } 3845 else { 3846 var top = _layoutArray( settings, settings.layout, 'top' ); 3847 var bottom = _layoutArray( settings, settings.layout, 'bottom' ); 3848 var renderer = _fnRenderer( settings, 'layout' ); 3849 3850 // Everything above - the renderer will actually insert the contents into the document 3851 top.forEach(function (item) { 3852 renderer( settings, insert, item ); 3853 }); 3854 3855 // The table - always the center of attention 3856 renderer( settings, insert, { 3857 full: { 3858 table: true, 3859 contents: [ _fnFeatureHtmlTable(settings) ] 3860 } 3861 } ); 3862 3863 // Everything below 3864 bottom.forEach(function (item) { 3865 renderer( settings, insert, item ); 3866 }); 3867 } 3868 3869 // Processing floats on top, so it isn't an inserted feature 3870 _processingHtml( settings ); 3871 } 3872 3873 /** 3874 * Draw the table with the legacy DOM property 3875 * @param {*} settings DT settings object 3876 * @param {*} dom DOM string 3877 * @param {*} insert Insert point 3878 */ 3879 function _fnLayoutDom( settings, dom, insert ) 3880 { 3881 var parts = dom.match(/(".*?")|('.*?')|./g); 3882 var featureNode, option, newNode, next, attr; 3883 3884 for ( var i=0 ; i<parts.length ; i++ ) { 3885 featureNode = null; 3886 option = parts[i]; 3887 3888 if ( option == '<' ) { 3889 // New container div
3890 newNode = $('<div/>'); 3891 3892 // Check to see if we should append an id and/or a class name to the container 3893 next = parts[i+1]; 3894 3895 if ( next[0] == "'" || next[0] == '"' ) { 3896 attr = next.replace(/['"]/g, ''); 3897 3898 var id = '', className; 3899 3900 /* The attribute can be in the format of "#id.class", "#id" or "class" This logic 3901 * breaks the string into parts and applies them as needed 3902 */ 3903 if ( attr.indexOf('.') != -1 ) { 3904 var split = attr.split('.'); 3905 3906 id = split[0]; 3907 className = split[1]; 3908 } 3909 else if ( attr[0] == "#" ) { 3910 id = attr; 3911 } 3912 else { 3913 className = attr; 3914 } 3915 3916 newNode 3917 .attr('id', id.substring(1)) 3918 .addClass(className); 3919 3920 i++; // Move along the position array 3921 } 3922 3923 insert.append( newNode ); 3924 insert = newNode; 3925 } 3926 else if ( option == '>' ) { 3927 // End container div 3928 insert = insert.parent(); 3929 } 3930 else if ( option == 't' ) { 3931 // Table 3932 featureNode = _fnFeatureHtmlTable( settings ); 3933 } 3934 else 3935 { 3936 DataTable.ext.feature.forEach(function(feature) { 3937 if ( option == feature.cFeature ) { 3938 featureNode = feature.fnInit( settings ); 3939 } 3940 }); 3941 } 3942 3943 // Add to the display 3944 if ( featureNode ) { 3945 insert.append( featureNode ); 3946 } 3947 } 3948 } 3949 3950 3951 /** 3952 * Use the DOM source to create up an array of header cells. The idea here is to 3953 * create a layout grid (array) of rows x columns, which contains a reference 3954 * to the cell that that point in the grid (regardless of col/rowspan), such that 3955 * any column / row could be removed and the new grid constructed 3956 * @param {node} thead The header/footer element for the table 3957 * @returns {array} Calculated layout array 3958 * @memberof DataTable#oApi 3959 */ 3960 function _fnDetectHeader ( settings, thead, write ) 3961 { 3962 var columns = settings.aoColumns; 3963 var rows = $(thead).children('tr'); 3964 var row, cell; 3965 var i, k, l, iLen, shifted, column, colspan, rowspan; 3966 var titleRow = settings.titleRow; 3967 var isHeader = thead && thead.nodeName.toLowerCase() === 'thead'; 3968 var layout = []; 3969 var unique; 3970 var shift = function ( a, i, j ) { 3971 var k = a[i]; 3972 while ( k[j] ) { 3973 j++; 3974 } 3975 return j; 3976 }; 3977 3978 // We know how many rows there are in the layout - so prep it 3979 for ( i=0, iLen=rows.length ; i<iLen ; i++ ) { 3980 layout.push( [] ); 3981 } 3982 3983 for ( i=0, iLen=rows.length ; i<iLen ; i++ ) { 3984 row = rows[i]; 3985 column = 0; 3986 3987 // For every cell in the row.. 3988 cell = row.firstChild; 3989 while ( cell ) { 3990 if ( 3991 cell.nodeName.toUpperCase() == 'TD' || 3992 cell.nodeName.toUpperCase() == 'TH' 3993 ) { 3994 var cols = []; 3995 var jqCell = $(cell); 3996 3997 // Get the col and rowspan attributes from the DOM and sanitise them 3998 colspan = cell.getAttribute('colspan') * 1; 3999 rowspan = cell.getAttribute('rowspan') * 1; 4000 colspan = (!colspan || colspan===0 || colspan===1) ? 1 : colspan; 4001 rowspan = (!rowspan || rowspan===0 || rowspan===1) ? 1 : rowspan; 4002 4003 // There might be colspan cells already in this row, so shift our target 4004 // accordingly 4005 shifted = shift( layout, i, column ); 4006 4007 // Cache calculation for unique columns 4008 unique = colspan === 1 ? 4009 true : 4010 false; 4011 4012 // Perform header setup 4013 if ( write ) { 4014 if (unique) { 4015 // Allow column options to be set from HTML attributes 4016 _fnColumnOptions( settings, shifted, jqCell.data() ); 4017 4018 // Get the width for the column. This can be defined from the 4019 // width attribute, style attribute or `columns.width` option 4020 var columnDef = columns[shifted]; 4021 var width = cell.getAttribute('width') || null; 4022 var t = cell.style.width.match(/width:\s*(\d+[pxem%]+)/); 4023 if ( t ) { 4024 width = t[1]; 4025 } 4026 4027 columnDef.sWidthOrig = columnDef.sWidth || width; 4028 4029 if (isHeader) { 4030 // Column title handling - can be user set, or read from the DOM 4031 // This happens before the render, so the original is still in place 4032 if ( columnDef.sTitle !== null && ! columnDef.autoTitle ) { 4033 if ( 4034 (titleRow === true && i === 0) || // top row 4035 (titleRow === false && i === rows.length -1) || // bottom row 4036 (titleRow === i) || // specific row 4037 (titleRow === null) 4038 ) { 4039 cell.innerHTML = columnDef.sTitle; 4040 } 4041 } 4042 4043 if (! columnDef.sTitle && unique) { 4044 columnDef.sTitle = _stripHtml(cell.innerHTML); 4045 columnDef.autoTitle = true; 4046 } 4047 } 4048 else { 4049 // Footer specific operations 4050 if (columnDef.footer) { 4051 cell.innerHTML = columnDef.footer; 4052 } 4053 } 4054 4055 // Fall back to the aria-label attribute on the table header if no ariaTitle is 4056 // provided. 4057 if (! columnDef.ariaTitle) { 4058 columnDef.ariaTitle = jqCell.attr("aria-label") || columnDef.sTitle; 4059 } 4060 4061 // Column specific class names 4062 if ( columnDef.className ) { 4063 jqCell.addClass( columnDef.className ); 4064 } 4065 } 4066 4067 // Wrap the column title so we can write to it in future 4068 if ( $('span.dt-column-title', cell).length === 0) { 4069 $('<span>') 4070 .addClass('dt-column-title') 4071 .append(cell.childNodes) 4072 .appendTo(cell); 4073 } 4074 4075 if ( 4076 settings.orderIndicators && 4077 isHeader && 4078 jqCell.filter(':not([data-dt-order=disable])').length !== 0 && 4079 jqCell.parent(':not([data-dt-order=disable])').length !== 0 && 4080 $('span.dt-column-order', cell).length === 0 4081 ) { 4082 $('<span>') 4083 .addClass('dt-column-order') 4084 .appendTo(cell); 4085 } 4086 4087 // We need to wrap the elements in the header in another element to use flexbox 4088 // layout for those elements 4089 var headerFooter = isHeader ? 'header' : 'footer'; 4090 4091 if ( $('span.dt-column-' + headerFooter, cell).length === 0) { 4092 $('<div>') 4093 .addClass('dt-column-' + headerFooter) 4094 .append(cell.childNodes) 4095 .appendTo(cell); 4096 } 4097 } 4098 4099 // If there is col / rowspan, copy the information into the layout grid 4100 for ( l=0 ; l<colspan ; l++ ) { 4101 for ( k=0 ; k<rowspan ; k++ ) { 4102 layout[i+k][shifted+l] = { 4103 cell: cell, 4104 unique: unique 4105 }; 4106
4107 layout[i+k].row = row; 4108 } 4109 4110 cols.push( shifted+l ); 4111 } 4112 4113 // Assign an attribute so spanning cells can still be identified 4114 // as belonging to a column 4115 cell.setAttribute('data-dt-column', _unique(cols).join(',')); 4116 } 4117 4118 cell = cell.nextSibling; 4119 } 4120 } 4121 4122 return layout; 4123 } 4124 4125 /** 4126 * Set the start position for draw 4127 * @param {object} oSettings dataTables settings object 4128 */ 4129 function _fnStart( oSettings ) 4130 { 4131 var bServerSide = _fnDataSource( oSettings ) == 'ssp'; 4132 var iInitDisplayStart = oSettings.iInitDisplayStart; 4133 4134 // Check and see if we have an initial draw position from state saving 4135 if ( iInitDisplayStart !== undefined && iInitDisplayStart !== -1 ) 4136 { 4137 oSettings._iDisplayStart = bServerSide ? 4138 iInitDisplayStart : 4139 iInitDisplayStart >= oSettings.fnRecordsDisplay() ? 4140 0 : 4141 iInitDisplayStart; 4142 4143 oSettings.iInitDisplayStart = -1; 4144 } 4145 } 4146 4147 /** 4148 * Create an Ajax call based on the table's settings, taking into account that 4149 * parameters can have multiple forms, and backwards compatibility. 4150 * 4151 * @param {object} oSettings dataTables settings object 4152 * @param {array} data Data to send to the server, required by 4153 * DataTables - may be augmented by developer callbacks 4154 * @param {function} fn Callback function to run when data is obtained 4155 */ 4156 function _fnBuildAjax( oSettings, data, fn ) 4157 { 4158 var ajaxData; 4159 var ajax = oSettings.ajax; 4160 var instance = oSettings.oInstance; 4161 var callback = function ( json ) { 4162 var status = oSettings.jqXHR 4163 ? oSettings.jqXHR.status 4164 : null; 4165 4166 if ( json === null || (typeof status === 'number' && status == 204 ) ) { 4167 json = {}; 4168 _fnAjaxDataSrc( oSettings, json, [] ); 4169 } 4170 4171 var error = json.error || json.sError; 4172 if ( error ) { 4173 _fnLog( oSettings, 0, error ); 4174 } 4175 4176 // Microsoft often wrap JSON as a string in another JSON object 4177 // Let's handle that automatically 4178 if (json.d && typeof json.d === 'string') { 4179 try { 4180 json = JSON.parse(json.d); 4181 } 4182 catch (e) { 4183 // noop 4184 } 4185 } 4186 4187 oSettings.json = json; 4188 4189 _fnCallbackFire( oSettings, null, 'xhr', [oSettings, json, oSettings.jqXHR], true ); 4190 fn( json ); 4191 }; 4192 4193 if ( $.isPlainObject( ajax ) && ajax.data ) 4194 { 4195 ajaxData = ajax.data; 4196 4197 var newData = typeof ajaxData === 'function' ? 4198 ajaxData( data, oSettings ) : // fn can manipulate data or return 4199 ajaxData; // an object object or array to merge 4200 4201 // If the function returned something, use that alone 4202 data = typeof ajaxData === 'function' && newData ? 4203 newData : 4204 $.extend( true, data, newData ); 4205 4206 // Remove the data property as we've resolved it already and don't want 4207 // jQuery to do it again (it is restored at the end of the function) 4208 delete ajax.data; 4209 } 4210 4211 var baseAjax = { 4212 "url": typeof ajax === 'string' ? 4213 ajax : 4214 '', 4215 "data": data, 4216 "success": callback, 4217 "dataType": "json", 4218 "cache": false, 4219 "type": oSettings.sServerMethod, 4220 "error": function (xhr, error) { 4221 var ret = _fnCallbackFire( oSettings, null, 'xhr', [oSettings, null, oSettings.jqXHR], true ); 4222 4223 if ( ret.indexOf(true) === -1 ) { 4224 if ( error == "parsererror" ) { 4225 _fnLog( oSettings, 0, 'Invalid JSON response', 1 ); 4226 } 4227 else if ( xhr.readyState === 4 ) { 4228 _fnLog( oSettings, 0, 'Ajax error', 7 ); 4229 } 4230 } 4231 4232 _fnProcessingDisplay( oSettings, false ); 4233 } 4234 }; 4235 4236 // If `ajax` option is an object, extend and override our default base 4237 if ( $.isPlainObject( ajax ) ) { 4238 $.extend( baseAjax, ajax ) 4239 } 4240 4241 // Store the data submitted for the API 4242 oSettings.oAjaxData = data; 4243 4244 // Allow plug-ins and external processes to modify the data 4245 _fnCallbackFire( oSettings, null, 'preXhr', [oSettings, data, baseAjax], true ); 4246 4247 // Custom Ajax option to submit the parameters as a JSON string 4248 if (baseAjax.submitAs === 'json' && typeof data === 'object') { 4249 baseAjax.data = JSON.stringify(data); 4250 } 4251 4252 if ( typeof ajax === 'function' ) 4253 { 4254 // Is a function - let the caller define what needs to be done 4255 oSettings.jqXHR = ajax.call( instance, data, callback, oSettings ); 4256 } 4257 else if (ajax.url === '') { 4258 // No url, so don't load any data. Just apply an empty data array 4259 // to the object for the callback. 4260 var empty = {}; 4261 4262 DataTable.util.set(ajax.dataSrc)(empty, []); 4263 callback(empty); 4264 } 4265 else { 4266 // Object to extend the base settings 4267 oSettings.jqXHR = $.ajax( baseAjax ); 4268 } 4269 4270 // Restore for next time around 4271 if ( ajaxData ) { 4272 ajax.data = ajaxData; 4273 } 4274 } 4275 4276 4277 /** 4278 * Update the table using an Ajax call 4279 * @param {object} settings dataTables settings object 4280 * @returns {boolean} Block the table drawing or not 4281 * @memberof DataTable#oApi 4282 */ 4283 function _fnAjaxUpdate( settings ) 4284 { 4285 settings.iDraw++; 4286 _fnProcessingDisplay( settings, true ); 4287 4288 _fnBuildAjax( 4289 settings, 4290 _fnAjaxParameters( settings ), 4291 function(json) { 4292 _fnAjaxUpdateDraw( settings, json ); 4293 } 4294 ); 4295 } 4296 4297 4298 /** 4299 * Build up the parameters in an object needed for a server-side processing 4300 * request. 4301 * @param {object} oSettings dataTables settings object 4302 * @returns {bool} block the table drawing or not 4303 * @memberof DataTable#oApi 4304 */ 4305 function _fnAjaxParameters( settings ) 4306 { 4307 var 4308 columns = settings.aoColumns, 4309 features = settings.oFeatures, 4310 preSearch = settings.oPreviousSearch, 4311 preColSearch = settings.aoPreSearchCols, 4312 colData = function ( idx, prop ) { 4313 return typeof columns[idx][prop] === 'function' ? 4314 'function' : 4315 columns[idx][prop]; 4316 }; 4317 4318 return { 4319 draw: settings.iDraw,
4320 columns: columns.map( function ( column, i ) { 4321 return { 4322 data: colData(i, 'mData'), 4323 name: column.sName, 4324 searchable: column.bSearchable, 4325 orderable: column.bSortable, 4326 search: { 4327 value: preColSearch[i].search, 4328 regex: preColSearch[i].regex, 4329 fixed: Object.keys(column.searchFixed).map( function(name) { 4330 return { 4331 name: name, 4332 term: column.searchFixed[name].toString() 4333 } 4334 }) 4335 } 4336 }; 4337 } ), 4338 order: _fnSortFlatten( settings ).map( function ( val ) { 4339 return { 4340 column: val.col, 4341 dir: val.dir, 4342 name: colData(val.col, 'sName') 4343 }; 4344 } ), 4345 start: settings._iDisplayStart, 4346 length: features.bPaginate ? 4347 settings._iDisplayLength : 4348 -1, 4349 search: { 4350 value: preSearch.search, 4351 regex: preSearch.regex, 4352 fixed: Object.keys(settings.searchFixed).map( function(name) { 4353 return { 4354 name: name, 4355 term: settings.searchFixed[name].toString() 4356 } 4357 }) 4358 } 4359 }; 4360 } 4361 4362 4363 /** 4364 * Data the data from the server (nuking the old) and redraw the table 4365 * @param {object} oSettings dataTables settings object 4366 * @param {object} json json data return from the server. 4367 * @param {string} json.sEcho Tracking flag for DataTables to match requests 4368 * @param {int} json.iTotalRecords Number of records in the data set, not accounting for filtering 4369 * @param {int} json.iTotalDisplayRecords Number of records in the data set, accounting for filtering 4370 * @param {array} json.aaData The data to display on this page 4371 * @param {string} [json.sColumns] Column ordering (sName, comma separated) 4372 * @memberof DataTable#oApi 4373 */ 4374 function _fnAjaxUpdateDraw ( settings, json ) 4375 { 4376 var data = _fnAjaxDataSrc(settings, json); 4377 var draw = _fnAjaxDataSrcParam(settings, 'draw', json); 4378 var recordsTotal = _fnAjaxDataSrcParam(settings, 'recordsTotal', json); 4379 var recordsFiltered = _fnAjaxDataSrcParam(settings, 'recordsFiltered', json); 4380 4381 if ( draw !== undefined ) { 4382 // Protect against out of sequence returns 4383 if ( draw*1 < settings.iDraw ) { 4384 return; 4385 } 4386 settings.iDraw = draw * 1; 4387 } 4388 4389 // No data in returned object, so rather than an array, we show an empty table 4390 if ( ! data ) { 4391 data = []; 4392 } 4393 4394 _fnClearTable( settings ); 4395 settings._iRecordsTotal = parseInt(recordsTotal, 10); 4396 settings._iRecordsDisplay = parseInt(recordsFiltered, 10); 4397 4398 for ( var i=0, ien=data.length ; i<ien ; i++ ) { 4399 _fnAddData( settings, data[i] ); 4400 } 4401 settings.aiDisplay = settings.aiDisplayMaster.slice(); 4402 4403 _fnColumnTypes(settings); 4404 _fnDraw( settings, true ); 4405 _fnInitComplete( settings ); 4406 _fnProcessingDisplay( settings, false ); 4407 } 4408 4409 4410 /** 4411 * Get the data from the JSON data source to use for drawing a table. Using 4412 * `_fnGetObjectDataFn` allows the data to be sourced from a property of the 4413 * source object, or from a processing function. 4414 * @param {object} settings dataTables settings object 4415 * @param {object} json Data source object / array from the server 4416 * @return {array} Array of data to use 4417 */ 4418 function _fnAjaxDataSrc ( settings, json, write ) 4419 { 4420 var dataProp = 'data'; 4421 4422 if ($.isPlainObject( settings.ajax ) && settings.ajax.dataSrc !== undefined) { 4423 // Could in inside a `dataSrc` object, or not! 4424 var dataSrc = settings.ajax.dataSrc; 4425 4426 // string, function and object are valid types 4427 if (typeof dataSrc === 'string' || typeof dataSrc === 'function') { 4428 dataProp = dataSrc; 4429 } 4430 else if (dataSrc.data !== undefined) { 4431 dataProp = dataSrc.data; 4432 } 4433 } 4434 4435 if ( ! write ) { 4436 if ( dataProp === 'data' ) { 4437 // If the default, then we still want to support the old style, and safely ignore 4438 // it if possible 4439 return json.aaData || json[dataProp]; 4440 } 4441 4442 return dataProp !== "" ? 4443 _fnGetObjectDataFn( dataProp )( json ) : 4444 json; 4445 } 4446 4447 // set 4448 _fnSetObjectDataFn( dataProp )( json, write ); 4449 } 4450 4451 /** 4452 * Very similar to _fnAjaxDataSrc, but for the other SSP properties 4453 * @param {*} settings DataTables settings object 4454 * @param {*} param Target parameter 4455 * @param {*} json JSON data 4456 * @returns Resolved value 4457 */ 4458 function _fnAjaxDataSrcParam (settings, param, json) { 4459 var dataSrc = $.isPlainObject( settings.ajax ) 4460 ? settings.ajax.dataSrc 4461 : null; 4462 4463 if (dataSrc && dataSrc[param]) { 4464 // Get from custom location 4465 return _fnGetObjectDataFn( dataSrc[param] )( json ); 4466 } 4467 4468 // else - Default behaviour 4469 var old = ''; 4470 4471 // Legacy support 4472 if (param === 'draw') { 4473 old = 'sEcho'; 4474 } 4475 else if (param === 'recordsTotal') { 4476 old = 'iTotalRecords'; 4477 } 4478 else if (param === 'recordsFiltered') { 4479 old = 'iTotalDisplayRecords'; 4480 } 4481 4482 return json[old] !== undefined 4483 ? json[old] 4484 : json[param]; 4485 } 4486 4487 4488 /** 4489 * Filter the table using both the global filter and column based filtering 4490 * @param {object} settings dataTables settings object 4491 * @param {object} input search information 4492 * @memberof DataTable#oApi 4493 */ 4494 function _fnFilterComplete ( settings, input ) 4495 { 4496 var columnsSearch = settings.aoPreSearchCols; 4497 4498 // In server-side processing all filtering is done by the server, so no point hanging around here 4499 if ( _fnDataSource( settings ) != 'ssp' ) 4500 { 4501 // Check if any of the rows were invalidated 4502 _fnFilterData( settings ); 4503 4504 // Start from the full data set 4505 settings.aiDisplay = settings.aiDisplayMaster.slice(); 4506 4507 // Global filter first 4508 _fnFilter( settings.aiDisplay, settings, input.search, input ); 4509 4510 $.each(settings.searchFixed, function (name, term) { 4511 _fnFilter(settings.aiDisplay, settings, term, {}); 4512 }); 4513 4514 // Then individual column filters 4515 for ( var i=0 ; i<columnsSearch.length ; i++ ) 4516 { 4517 var col = columnsSearch[i]; 4518 4519 _fnFilter( 4520 settings.aiDisplay, 4521 settings, 4522 col.search, 4523 col, 4524 i 4525 ); 4526 4527 $.each(settings.aoColumns[i].searchFixed, function (name, term) { 4528 _fnFilter(settings.aiDisplay, settings, term, {}, i); 4529 }); 4530 } 4531 4532 // And finally global filtering 4533 _fnFilterCustom( settings ); 4534 } 4535 4536 // Tell the draw function we have been filtering 4537 settings.bFiltered = true; 4538 4539 _fnCallbackFire( settings, null, 'search', [settings] ); 4540 } 4541 4542 4543 /** 4544 * Apply custom filtering functions 4545 * 4546 * This is legacy now that we have named functions, but it is widely used 4547 * from 1.x, so it is not yet deprecated. 4548 * @param {object} oSettings dataTables settings object 4549 * @memberof DataTable#oApi 4550 */ 4551 function _fnFilterCustom( settings ) 4552 { 4553 var filters = DataTable.ext.search; 4554 var displayRows = settings.aiDisplay; 4555 var row, rowIdx; 4556 4557 for ( var i=0, ien=filters.length ; i<ien ; i++ ) { 4558 var rows = []; 4559 4560 // Loop over each row and see if it should be included 4561 for ( var j=0, jen=displayRows.length ; j<jen ; j++ ) { 4562 rowIdx = displayRows[ j ]; 4563 row = settings.aoData[ rowIdx ]; 4564 4565 if ( filters[i]( settings, row._aFilterData, rowIdx, row._aData, j ) ) { 4566 rows.push( rowIdx ); 4567 } 4568 } 4569 4570 // So the array reference doesn't break set the results into the 4571 // existing array 4572 displayRows.length = 0; 4573 _fnArrayApply(displayRows, rows); 4574 } 4575 } 4576 4577 4578 /** 4579 * Filter the data table based on user input and draw the table 4580 */ 4581 function _fnFilter( searchRows, settings, input, options, column ) 4582 { 4583 if ( input === '' ) { 4584 return; 4585 } 4586 4587 var i = 0; 4588 var matched = []; 4589 4590 // Search term can be a function, regex or string - if a string we apply our 4591 // smart filtering regex (assuming the options require that) 4592 var searchFunc = typeof input === 'function' ? input : null; 4593 var rpSearch = input instanceof RegExp 4594 ? input 4595 : searchFunc 4596 ? null 4597 : _fnFilterCreateSearch( input, options ); 4598 4599 // Then for each row, does the test pass. If not, lop the row from the array 4600 for (i=0 ; i<searchRows.length ; i++) { 4601 var row = settings.aoData[ searchRows[i] ]; 4602 var data = column === undefined 4603 ? row._sFilterRow 4604 : row._aFilterData[ column ]; 4605 4606 if ( (searchFunc && searchFunc(data, row._aData, searchRows[i], column)) || (rpSearch && rpSearch.test(data)) ) { 4607 matched.push(searchRows[i]); 4608 } 4609 } 4610 4611 // Mutate the searchRows array 4612 searchRows.length = matched.length; 4613 4614 for (i=0 ; i<matched.length ; i++) { 4615 searchRows[i] = matched[i]; 4616 } 4617 } 4618 4619 4620 /** 4621 * Build a regular expression object suitable for searching a table 4622 * @param {string} sSearch string to search for 4623 * @param {bool} bRegex treat as a regular expression or not 4624 * @param {bool} bSmart perform smart filtering or not 4625 * @param {bool} bCaseInsensitive Do case insensitive matching or not 4626 * @returns {RegExp} constructed object 4627 * @memberof DataTable#oApi 4628 */ 4629 function _fnFilterCreateSearch( search, inOpts ) 4630 { 4631 var not = []; 4632 var options = $.extend({}, { 4633 boundary: false, 4634 caseInsensitive: true, 4635 exact: false, 4636 regex: false, 4637 smart: true 4638 }, inOpts); 4639 4640 if (typeof search !== 'string') { 4641 search = search.toString(); 4642 } 4643 4644 // Remove diacritics if normalize is set up to do so 4645 search = _normalize(search); 4646 4647 if (options.exact) { 4648 return new RegExp( 4649 '^'+_fnEscapeRegex(search)+'$', 4650 options.caseInsensitive ? 'i' : '' 4651 ); 4652 } 4653 4654 search = options.regex ? 4655 search : 4656 _fnEscapeRegex( search ); 4657 4658 if ( options.smart ) { 4659 /* For smart filtering we want to allow the search to work regardless of 4660 * word order. We also want double quoted text to be preserved, so word 4661 * order is important - a la google. And a negative look around for 4662 * finding rows which don't contain a given string. 4663 * 4664 * So this is the sort of thing we want to generate: 4665 * 4666 * ^(?=.*?\bone\b)(?=.*?\btwo three\b)(?=.*?\bfour\b).*$ 4667 */ 4668 var parts = search.match( /!?["\u201C][^"\u201D]+["\u201D]|[^ ]+/g ) || ['']; 4669 var a = parts.map( function ( word ) { 4670 var negative = false;
4671 var m; 4672 4673 // Determine if it is a "does not include" 4674 if ( word.charAt(0) === '!' ) { 4675 negative = true; 4676 word = word.substring(1); 4677 } 4678 4679 // Strip the quotes from around matched phrases 4680 if ( word.charAt(0) === '"' ) { 4681 m = word.match( /^"(.*)"$/ ); 4682 word = m ? m[1] : word; 4683 } 4684 else if ( word.charAt(0) === '\u201C' ) { 4685 // Smart quote match (iPhone users) 4686 m = word.match( /^\u201C(.*)\u201D$/ ); 4687 word = m ? m[1] : word; 4688 } 4689 4690 // For our "not" case, we need to modify the string that is 4691 // allowed to match at the end of the expression. 4692 if (negative) { 4693 if (word.length > 1) { 4694 not.push('(?!'+word+')'); 4695 } 4696 4697 word = ''; 4698 } 4699 4700 return word.replace(/"/g, ''); 4701 } ); 4702 4703 var match = not.length 4704 ? not.join('') 4705 : ''; 4706 4707 var boundary = options.boundary 4708 ? '\\b' 4709 : ''; 4710 4711 search = '^(?=.*?'+boundary+a.join( ')(?=.*?'+boundary )+')('+match+'.)*$'; 4712 } 4713 4714 return new RegExp( search, options.caseInsensitive ? 'i' : '' ); 4715 } 4716 4717 4718 /** 4719 * Escape a string such that it can be used in a regular expression 4720 * @param {string} sVal string to escape 4721 * @returns {string} escaped string 4722 * @memberof DataTable#oApi 4723 */ 4724 var _fnEscapeRegex = DataTable.util.escapeRegex; 4725 4726 var __filter_div = $('<div>')[0]; 4727 var __filter_div_textContent = __filter_div.textContent !== undefined; 4728 4729 // Update the filtering data for each row if needed (by invalidation or first run) 4730 function _fnFilterData ( settings ) 4731 { 4732 var columns = settings.aoColumns; 4733 var data = settings.aoData; 4734 var column; 4735 var j, jen, filterData, cellData, row; 4736 var wasInvalidated = false; 4737 4738 for ( var rowIdx=0 ; rowIdx<data.length ; rowIdx++ ) { 4739 if (! data[rowIdx]) { 4740 continue; 4741 } 4742 4743 row = data[rowIdx]; 4744 4745 if ( ! row._aFilterData ) { 4746 filterData = []; 4747 4748 for ( j=0, jen=columns.length ; j<jen ; j++ ) { 4749 column = columns[j]; 4750 4751 if ( column.bSearchable ) { 4752 cellData = _fnGetCellData( settings, rowIdx, j, 'filter' ); 4753 4754 // Search in DataTables is string based 4755 if ( cellData === null ) { 4756 cellData = ''; 4757 } 4758 4759 if ( typeof cellData !== 'string' && cellData.toString ) { 4760 cellData = cellData.toString(); 4761 } 4762 } 4763 else { 4764 cellData = ''; 4765 } 4766 4767 // If it looks like there is an HTML entity in the string, 4768 // attempt to decode it so sorting works as expected. Note that 4769 // we could use a single line of jQuery to do this, but the DOM 4770 // method used here is much faster https://jsperf.com/html-decode 4771 if ( cellData.indexOf && cellData.indexOf('&') !== -1 ) { 4772 __filter_div.innerHTML = cellData; 4773 cellData = __filter_div_textContent ? 4774 __filter_div.textContent : 4775 __filter_div.innerText; 4776 } 4777 4778 if ( cellData.replace ) { 4779 cellData = cellData.replace(/[\r\n\u2028]/g, ''); 4780 } 4781 4782 filterData.push( cellData ); 4783 } 4784 4785 row._aFilterData = filterData; 4786 row._sFilterRow = filterData.join(' '); 4787 wasInvalidated = true; 4788 } 4789 } 4790 4791 return wasInvalidated; 4792 } 4793 4794 4795 /** 4796 * Draw the table for the first time, adding all required features 4797 * @param {object} settings dataTables settings object 4798 * @memberof DataTable#oApi 4799 */ 4800 function _fnInitialise ( settings ) 4801 { 4802 var i; 4803 var init = settings.oInit; 4804 var deferLoading = settings.deferLoading; 4805 var dataSrc = _fnDataSource( settings ); 4806 4807 // Ensure that the table data is fully initialised 4808 if ( ! settings.bInitialised ) { 4809 setTimeout( function(){ _fnInitialise( settings ); }, 200 ); 4810 return; 4811 } 4812 4813 // Build the header / footer for the table 4814 _fnBuildHead( settings, 'header' ); 4815 _fnBuildHead( settings, 'footer' ); 4816 4817 // Load the table's state (if needed) and then render around it and draw 4818 _fnLoadState( settings, init, function () { 4819 // Then draw the header / footer 4820 _fnDrawHead( settings, settings.aoHeader ); 4821 _fnDrawHead( settings, settings.aoFooter ); 4822 4823 // Cache the paging start point, as the first redraw will reset it 4824 var iAjaxStart = settings.iInitDisplayStart 4825 4826 // Local data load 4827 // Check if there is data passing into the constructor 4828 if ( init.aaData ) { 4829 for ( i=0 ; i<init.aaData.length ; i++ ) { 4830 _fnAddData( settings, init.aaData[ i ] ); 4831 } 4832 } 4833 else if ( deferLoading || dataSrc == 'dom' ) { 4834 // Grab the data from the page 4835 _fnAddTr( settings, $(settings.nTBody).children('tr') ); 4836 } 4837 4838 // Filter not yet applied - copy the display master 4839 settings.aiDisplay = settings.aiDisplayMaster.slice(); 4840 4841 // Enable features 4842 _fnAddOptionsHtml( settings ); 4843 _fnSortInit( settings ); 4844 4845 _colGroup( settings ); 4846 4847 /* Okay to show that something is going on now */ 4848 _fnProcessingDisplay( settings, true ); 4849 4850 _fnCallbackFire( settings, null, 'preInit', [settings], true ); 4851 4852 // If there is default sorting required - let's do it. The sort function 4853 // will do the drawing for us. Otherwise we draw the table regardless of the 4854 // Ajax source - this allows the table to look initialised for Ajax sourcing 4855 // data (show 'loading' message possibly) 4856 _fnReDraw( settings ); 4857 4858 // Server-side processing init complete is done by _fnAjaxUpdateDraw 4859 if ( dataSrc != 'ssp' || deferLoading ) { 4860 // if there is an ajax source load the data 4861 if ( dataSrc == 'ajax' ) { 4862 _fnBuildAjax( settings, {}, function(json) { 4863 var aData = _fnAjaxDataSrc( settings, json ); 4864 4865 // Got the data - add it to the table 4866 for ( i=0 ; i<aData.length ; i++ ) { 4867 _fnAddData( settings, aData[i] ); 4868 } 4869
4870 // Reset the init display for cookie saving. We've already done 4871 // a filter, and therefore cleared it before. So we need to make 4872 // it appear 'fresh' 4873 settings.iInitDisplayStart = iAjaxStart; 4874 4875 _fnReDraw( settings ); 4876 _fnProcessingDisplay( settings, false ); 4877 _fnInitComplete( settings ); 4878 }, settings ); 4879 } 4880 else { 4881 _fnInitComplete( settings ); 4882 _fnProcessingDisplay( settings, false ); 4883 } 4884 } 4885 } ); 4886 } 4887 4888 4889 /** 4890 * Draw the table for the first time, adding all required features 4891 * @param {object} settings dataTables settings object 4892 * @memberof DataTable#oApi 4893 */ 4894 function _fnInitComplete ( settings ) 4895 { 4896 if (settings._bInitComplete) { 4897 return; 4898 } 4899 4900 var args = [settings, settings.json]; 4901 4902 settings._bInitComplete = true; 4903 4904 // Table is fully set up and we have data, so calculate the 4905 // column widths 4906 _fnAdjustColumnSizing( settings ); 4907 4908 _fnCallbackFire( settings, null, 'plugin-init', args, true ); 4909 _fnCallbackFire( settings, 'aoInitComplete', 'init', args, true ); 4910 } 4911 4912 function _fnLengthChange ( settings, val ) 4913 { 4914 var len = parseInt( val, 10 ); 4915 settings._iDisplayLength = len; 4916 4917 _fnLengthOverflow( settings ); 4918 4919 // Fire length change event 4920 _fnCallbackFire( settings, null, 'length', [settings, len] ); 4921 } 4922 4923 /** 4924 * Alter the display settings to change the page 4925 * @param {object} settings DataTables settings object 4926 * @param {string|int} action Paging action to take: "first", "previous", 4927 * "next" or "last" or page number to jump to (integer) 4928 * @param [bool] redraw Automatically draw the update or not 4929 * @returns {bool} true page has changed, false - no change 4930 * @memberof DataTable#oApi 4931 */ 4932 function _fnPageChange ( settings, action, redraw ) 4933 { 4934 var 4935 start = settings._iDisplayStart, 4936 len = settings._iDisplayLength, 4937 records = settings.fnRecordsDisplay(); 4938 4939 if ( records === 0 || len === -1 ) 4940 { 4941 start = 0; 4942 } 4943 else if ( typeof action === "number" ) 4944 { 4945 start = action * len; 4946 4947 if ( start > records ) 4948 { 4949 start = 0; 4950 } 4951 } 4952 else if ( action == "first" ) 4953 { 4954 start = 0; 4955 } 4956 else if ( action == "previous" ) 4957 { 4958 start = len >= 0 ? 4959 start - len : 4960 0; 4961 4962 if ( start < 0 ) 4963 { 4964 start = 0; 4965 } 4966 } 4967 else if ( action == "next" ) 4968 { 4969 if ( start + len < records ) 4970 { 4971 start += len; 4972 } 4973 } 4974 else if ( action == "last" ) 4975 { 4976 start = Math.floor( (records-1) / len) * len; 4977 } 4978 else if ( action === 'ellipsis' ) 4979 { 4980 return; 4981 } 4982 else 4983 { 4984 _fnLog( settings, 0, "Unknown paging action: "+action, 5 ); 4985 } 4986 4987 var changed = settings._iDisplayStart !== start; 4988 settings._iDisplayStart = start; 4989 4990 _fnCallbackFire( settings, null, changed ? 'page' : 'page-nc', [settings] ); 4991 4992 if ( changed && redraw ) { 4993 _fnDraw( settings ); 4994 } 4995 4996 return changed; 4997 } 4998 4999 5000 /** 5001 * Generate the node required for the processing node 5002 * @param {object} settings DataTables settings object 5003 */ 5004 function _processingHtml ( settings ) 5005 { 5006 var table = settings.nTable; 5007 var scrolling = settings.oScroll.sX !== '' || settings.oScroll.sY !== ''; 5008 5009 if ( settings.oFeatures.bProcessing ) { 5010 var n = $('<div/>', { 5011 'id': settings.sTableId + '_processing', 5012 'class': settings.oClasses.processing.container, 5013 'role': 'status' 5014 } ) 5015 .html( settings.oLanguage.sProcessing ) 5016 .append('<div><div></div><div></div><div></div><div></div></div>'); 5017 5018 // Different positioning depending on if scrolling is enabled or not 5019 if (scrolling) { 5020 n.prependTo( $('div.dt-scroll', settings.nTableWrapper) ); 5021 } 5022 else { 5023 n.insertBefore( table ); 5024 } 5025 5026 $(table).on( 'processing.dt.DT', function (e, s, show) { 5027 n.css( 'display', show ? 'block' : 'none' ); 5028 } ); 5029 } 5030 } 5031 5032 5033 /** 5034 * Display or hide the processing indicator 5035 * @param {object} settings DataTables settings object 5036 * @param {bool} show Show the processing indicator (true) or not (false) 5037 */ 5038 function _fnProcessingDisplay ( settings, show ) 5039 { 5040 // Ignore cases when we are still redrawing 5041 if (settings.bDrawing && show === false) { 5042 return; 5043 } 5044 5045 _fnCallbackFire( settings, null, 'processing', [settings, show] ); 5046 } 5047 5048 /** 5049 * Show the processing element if an action takes longer than a given time 5050 * 5051 * @param {*} settings DataTables settings object 5052 * @param {*} enable Do (true) or not (false) async processing (local feature enablement) 5053 * @param {*} run Function to run 5054 */ 5055 function _fnProcessingRun( settings, enable, run ) { 5056 if (! enable) { 5057 // Immediate execution, synchronous 5058 run(); 5059 } 5060 else { 5061 _fnProcessingDisplay(settings, true); 5062 5063 // Allow the processing display to show if needed 5064 setTimeout(function () { 5065 run(); 5066 5067 _fnProcessingDisplay(settings, false); 5068 }, 0); 5069 } 5070 } 5071 /** 5072 * Add any control elements for the table - specifically scrolling 5073 * @param {object} settings dataTables settings object 5074 * @returns {node} Node to add to the DOM 5075 * @memberof DataTable#oApi 5076 */ 5077 function _fnFeatureHtmlTable ( settings ) 5078 { 5079 var table = $(settings.nTable); 5080 5081 // Scrolling from here on in 5082 var scroll = settings.oScroll; 5083
5084 if ( scroll.sX === '' && scroll.sY === '' ) { 5085 return settings.nTable; 5086 } 5087 5088 var scrollX = scroll.sX; 5089 var scrollY = scroll.sY; 5090 var classes = settings.oClasses.scrolling; 5091 var caption = settings.captionNode; 5092 var captionSide = caption ? caption._captionSide : null; 5093 var headerClone = $( table[0].cloneNode(false) ); 5094 var footerClone = $( table[0].cloneNode(false) ); 5095 var footer = table.children('tfoot'); 5096 var _div = '<div/>'; 5097 var size = function ( s ) { 5098 return !s ? null : _fnStringToCss( s ); 5099 }; 5100 5101 if ( ! footer.length ) { 5102 footer = null; 5103 } 5104 5105 /* 5106 * The HTML structure that we want to generate in this function is: 5107 * div - scroller 5108 * div - scroll head 5109 * div - scroll head inner 5110 * table - scroll head table 5111 * thead - thead 5112 * div - scroll body 5113 * table - table (master table) 5114 * thead - thead clone for sizing 5115 * tbody - tbody 5116 * div - scroll foot 5117 * div - scroll foot inner 5118 * table - scroll foot table 5119 * tfoot - tfoot 5120 */ 5121 var scroller = $( _div, { 'class': classes.container } ) 5122 .append( 5123 $(_div, { 'class': classes.header.self } ) 5124 .css( { 5125 overflow: 'hidden', 5126 position: 'relative', 5127 border: 0, 5128 width: scrollX ? size(scrollX) : '100%' 5129 } ) 5130 .append( 5131 $(_div, { 'class': classes.header.inner } ) 5132 .css( { 5133 'box-sizing': 'content-box', 5134 width: scroll.sXInner || '100%' 5135 } ) 5136 .append( 5137 headerClone 5138 .removeAttr('id') 5139 .css( 'margin-left', 0 ) 5140 .append( captionSide === 'top' ? caption : null ) 5141 .append( 5142 table.children('thead') 5143 ) 5144 ) 5145 ) 5146 ) 5147 .append( 5148 $(_div, { 'class': classes.body } ) 5149 .css( { 5150 position: 'relative', 5151 overflow: 'auto', 5152 width: size( scrollX ) 5153 } ) 5154 .append( table ) 5155 ); 5156 5157 if ( footer ) { 5158 scroller.append( 5159 $(_div, { 'class': classes.footer.self } ) 5160 .css( { 5161 overflow: 'hidden', 5162 border: 0, 5163 width: scrollX ? size(scrollX) : '100%' 5164 } ) 5165 .append( 5166 $(_div, { 'class': classes.footer.inner } ) 5167 .append( 5168 footerClone 5169 .removeAttr('id') 5170 .css( 'margin-left', 0 ) 5171 .append( captionSide === 'bottom' ? caption : null ) 5172 .append( 5173 table.children('tfoot') 5174 ) 5175 ) 5176 ) 5177 ); 5178 } 5179 5180 var children = scroller.children(); 5181 var scrollHead = children[0]; 5182 var scrollBody = children[1]; 5183 var scrollFoot = footer ? children[2] : null; 5184 5185 // When the body is scrolled, then we also want to scroll the headers 5186 $(scrollBody).on( 'scroll.DT', function () { 5187 var scrollLeft = this.scrollLeft; 5188 5189 scrollHead.scrollLeft = scrollLeft; 5190 5191 if ( footer ) { 5192 scrollFoot.scrollLeft = scrollLeft; 5193 } 5194 } ); 5195 5196 // When focus is put on the header cells, we might need to scroll the body 5197 $('th, td', scrollHead).on('focus', function () { 5198 var scrollLeft = scrollHead.scrollLeft; 5199 5200 scrollBody.scrollLeft = scrollLeft; 5201 5202 if ( footer ) { 5203 scrollBody.scrollLeft = scrollLeft; 5204 } 5205 }); 5206 5207 $(scrollBody).css('max-height', scrollY); 5208 if (! scroll.bCollapse) { 5209 $(scrollBody).css('height', scrollY); 5210 } 5211 5212 settings.nScrollHead = scrollHead; 5213 settings.nScrollBody = scrollBody; 5214 settings.nScrollFoot = scrollFoot; 5215 5216 // On redraw - align columns 5217 settings.aoDrawCallback.push(_fnScrollDraw); 5218 5219 return scroller[0]; 5220 } 5221 5222 5223 5224 /** 5225 * Update the header, footer and body tables for resizing - i.e. column 5226 * alignment. 5227 * 5228 * Welcome to the most horrible function DataTables. The process that this 5229 * function follows is basically: 5230 * 1. Re-create the table inside the scrolling div 5231 * 2. Correct colgroup > col values if needed 5232 * 3. Copy colgroup > col over to header and footer 5233 * 4. Clean up 5234 * 5235 * @param {object} settings dataTables settings object 5236 * @memberof DataTable#oApi 5237 */ 5238 function _fnScrollDraw ( settings ) 5239 { 5240 // Given that this is such a monster function, a lot of variables are use 5241 // to try and keep the minimised size as small as possible 5242 var 5243 scroll = settings.oScroll, 5244 barWidth = scroll.iBarWidth, 5245 divHeader = $(settings.nScrollHead), 5246 divHeaderInner = divHeader.children('div'), 5247 divHeaderTable = divHeaderInner.children('table'), 5248 divBodyEl = settings.nScrollBody,
5249 divBody = $(divBodyEl), 5250 divFooter = $(settings.nScrollFoot), 5251 divFooterInner = divFooter.children('div'), 5252 divFooterTable = divFooterInner.children('table'), 5253 header = $(settings.nTHead), 5254 table = $(settings.nTable), 5255 footer = settings.nTFoot && $('th, td', settings.nTFoot).length ? $(settings.nTFoot) : null, 5256 browser = settings.oBrowser, 5257 headerCopy, footerCopy; 5258 5259 // If the scrollbar visibility has changed from the last draw, we need to 5260 // adjust the column sizes as the table width will have changed to account 5261 // for the scrollbar 5262 var scrollBarVis = divBodyEl.scrollHeight > divBodyEl.clientHeight; 5263 5264 if ( settings.scrollBarVis !== scrollBarVis && settings.scrollBarVis !== undefined ) { 5265 settings.scrollBarVis = scrollBarVis; 5266 _fnAdjustColumnSizing( settings ); 5267 return; // adjust column sizing will call this function again 5268 } 5269 else { 5270 settings.scrollBarVis = scrollBarVis; 5271 } 5272 5273 // 1. Re-create the table inside the scrolling div 5274 // Remove the old minimised thead and tfoot elements in the inner table 5275 table.children('thead, tfoot').remove(); 5276 5277 // Clone the current header and footer elements and then place it into the inner table 5278 headerCopy = header.clone().prependTo( table ); 5279 headerCopy.find('th, td').removeAttr('tabindex'); 5280 headerCopy.find('[id]').removeAttr('id'); 5281 5282 if ( footer ) { 5283 footerCopy = footer.clone().prependTo( table ); 5284 footerCopy.find('[id]').removeAttr('id'); 5285 } 5286 5287 // 2. Correct colgroup > col values if needed 5288 // It is possible that the cell sizes are smaller than the content, so we need to 5289 // correct colgroup>col for such cases. This can happen if the auto width detection 5290 // uses a cell which has a longer string, but isn't the widest! For example 5291 // "Chief Executive Officer (CEO)" is the longest string in the demo, but 5292 // "Systems Administrator" is actually the widest string since it doesn't collapse. 5293 // Note the use of translating into a column index to get the `col` element. This 5294 // is because of Responsive which might remove `col` elements, knocking the alignment 5295 // of the indexes out. 5296 if (settings.aiDisplay.length) { 5297 // Get the column sizes from the first row in the table. This should really be a 5298 // [].find, but it wasn't supported in Chrome until Sept 2015, and DT has 10 year 5299 // browser support 5300 var firstTr = null; 5301 var start = _fnDataSource( settings ) !== 'ssp' 5302 ? settings._iDisplayStart 5303 : 0; 5304 5305 for (i=start ; i<start + settings.aiDisplay.length ; i++) { 5306 var idx = settings.aiDisplay[i]; 5307 var tr = settings.aoData[idx].nTr; 5308 5309 if (tr) { 5310 firstTr = tr; 5311 break; 5312 } 5313 } 5314 5315 if (firstTr) { 5316 var colSizes = $(firstTr).children('th, td').map(function (vis) { 5317 return { 5318 idx: _fnVisibleToColumnIndex(settings, vis), 5319 width: $(this).outerWidth() 5320 }; 5321 }); 5322 5323 // Check against what the colgroup > col is set to and correct if needed 5324 for (var i=0 ; i<colSizes.length ; i++) { 5325 var colEl = settings.aoColumns[ colSizes[i].idx ].colEl[0]; 5326 var colWidth = colEl.style.width.replace('px', ''); 5327 5328 if (colWidth !== colSizes[i].width) { 5329 colEl.style.width = colSizes[i].width + 'px'; 5330 5331 if (scroll.sX) { 5332 colEl.style.minWidth = colSizes[i].width + 'px'; 5333 } 5334 } 5335 } 5336 } 5337 } 5338 5339 // 3. Copy the colgroup over to the header and footer 5340 divHeaderTable 5341 .find('colgroup') 5342 .remove(); 5343 5344 divHeaderTable.append(settings.colgroup.clone()); 5345 5346 if ( footer ) { 5347 divFooterTable 5348 .find('colgroup') 5349 .remove(); 5350 5351 divFooterTable.append(settings.colgroup.clone()); 5352 } 5353 5354 // "Hide" the header and footer that we used for the sizing. We need to keep 5355 // the content of the cell so that the width applied to the header and body 5356 // both match, but we want to hide it completely. 5357 $('th, td', headerCopy).each(function () { 5358 $(this.childNodes).wrapAll('<div class="dt-scroll-sizing">'); 5359 }); 5360 5361 if ( footer ) { 5362 $('th, td', footerCopy).each(function () { 5363 $(this.childNodes).wrapAll('<div class="dt-scroll-sizing">'); 5364 }); 5365 } 5366 5367 // 4. Clean up 5368 // Figure out if there are scrollbar present - if so then we need a the header and footer to 5369 // provide a bit more space to allow "overflow" scrolling (i.e. past the scrollbar) 5370 var isScrolling = Math.floor(table.height()) > divBodyEl.clientHeight || divBody.css('overflow-y') == "scroll"; 5371 var paddingSide = 'padding' + (browser.bScrollbarLeft ? 'Left' : 'Right' ); 5372 5373 // Set the width's of the header and footer tables 5374 var outerWidth = table.outerWidth(); 5375 5376 divHeaderTable.css('width', _fnStringToCss( outerWidth )); 5377 divHeaderInner 5378 .css('width', _fnStringToCss( outerWidth )) 5379 .css(paddingSide, isScrolling ? barWidth+"px" : "0px"); 5380 5381 if ( footer ) { 5382 divFooterTable.css('width', _fnStringToCss( outerWidth )); 5383 divFooterInner 5384 .css('width', _fnStringToCss( outerWidth )) 5385 .css(paddingSide, isScrolling ? barWidth+"px" : "0px"); 5386 } 5387 5388 // Correct DOM ordering for colgroup - comes before the thead 5389 table.children('colgroup').prependTo(table); 5390 5391 // Adjust the position of the header in case we loose the y-scrollbar 5392 divBody.trigger('scroll'); 5393 5394 // If sorting or filtering has occurred, jump the scrolling back to the top 5395 // only if we aren't holding the position 5396 if ( (settings.bSorted || settings.bFiltered) && ! settings._drawHold ) { 5397 divBodyEl.scrollTop = 0; 5398 } 5399 } 5400 5401 /** 5402 * Calculate the width of columns for the table 5403 * @param {object} settings dataTables settings object 5404 * @memberof DataTable#oApi 5405 */ 5406 function _fnCalculateColumnWidths ( settings ) 5407 { 5408 // Not interested in doing column width calculation if auto-width is disabled 5409 if (! settings.oFeatures.bAutoWidth) { 5410 return; 5411 } 5412 5413 var 5414 table = settings.nTable, 5415 columns = settings.aoColumns, 5416 scroll = settings.oScroll, 5417 scrollY = scroll.sY, 5418 scrollX = scroll.sX, 5419 scrollXInner = scroll.sXInner, 5420 visibleColumns = _fnGetColumns( settings, 'bVisible' ), 5421 tableWidthAttr = table.getAttribute('width'), // from DOM element 5422 tableContainer = table.parentNode, 5423 i, column, columnIdx; 5424 5425 var styleWidth = table.style.width; 5426 var containerWidth = _fnWrapperWidth(settings); 5427 5428 // Don't re-run for the same width as the last time 5429 if (containerWidth === settings.containerWidth) { 5430 return false;
5431 } 5432 5433 settings.containerWidth = containerWidth; 5434 5435 // If there is no width applied as a CSS style or as an attribute, we assume that 5436 // the width is intended to be 100%, which is usually is in CSS, but it is very 5437 // difficult to correctly parse the rules to get the final result. 5438 if ( ! styleWidth && ! tableWidthAttr) { 5439 table.style.width = '100%'; 5440 styleWidth = '100%'; 5441 } 5442 5443 if ( styleWidth && styleWidth.indexOf('%') !== -1 ) { 5444 tableWidthAttr = styleWidth; 5445 } 5446 5447 // Let plug-ins know that we are doing a recalc, in case they have changed any of the 5448 // visible columns their own way (e.g. Responsive uses display:none). 5449 _fnCallbackFire( 5450 settings, 5451 null, 5452 'column-calc', 5453 {visible: visibleColumns}, 5454 false 5455 ); 5456 5457 // Construct a single row, worst case, table with the widest 5458 // node in the data, assign any user defined widths, then insert it into 5459 // the DOM and allow the browser to do all the hard work of calculating 5460 // table widths 5461 var tmpTable = $(table.cloneNode()) 5462 .css( 'visibility', 'hidden' ) 5463 .removeAttr( 'id' ); 5464 5465 // Clean up the table body 5466 tmpTable.append('<tbody>') 5467 var tr = $('<tr/>').appendTo( tmpTable.find('tbody') ); 5468 5469 // Clone the table header and footer - we can't use the header / footer 5470 // from the cloned table, since if scrolling is active, the table's 5471 // real header and footer are contained in different table tags 5472 tmpTable 5473 .append( $(settings.nTHead).clone() ) 5474 .append( $(settings.nTFoot).clone() ); 5475 5476 // Remove any assigned widths from the footer (from scrolling) 5477 tmpTable.find('tfoot th, tfoot td').css('width', ''); 5478 5479 // Apply custom sizing to the cloned header 5480 tmpTable.find('thead th, thead td').each( function () { 5481 // Get the `width` from the header layout 5482 var width = _fnColumnsSumWidth( settings, this, true, false ); 5483 5484 if ( width ) { 5485 this.style.width = width; 5486 5487 // For scrollX we need to force the column width otherwise the 5488 // browser will collapse it. If this width is smaller than the 5489 // width the column requires, then it will have no effect 5490 if ( scrollX ) { 5491 this.style.minWidth = width; 5492 5493 $( this ).append( $('<div/>').css( { 5494 width: width, 5495 margin: 0, 5496 padding: 0, 5497 border: 0, 5498 height: 1 5499 } ) ); 5500 } 5501 } 5502 else { 5503 this.style.width = ''; 5504 } 5505 } ); 5506 5507 // Find the widest piece of data for each column and put it into the table 5508 for ( i=0 ; i<visibleColumns.length ; i++ ) { 5509 columnIdx = visibleColumns[i]; 5510 column = columns[ columnIdx ]; 5511 5512 var longest = _fnGetMaxLenString(settings, columnIdx); 5513 var autoClass = _ext.type.className[column.sType]; 5514 var text = longest + column.sContentPadding; 5515 var insert = longest.indexOf('<') === -1 5516 ? document.createTextNode(text) 5517 : text 5518 5519 $('<td/>') 5520 .addClass(autoClass) 5521 .addClass(column.sClass) 5522 .append(insert) 5523 .appendTo(tr); 5524 } 5525 5526 // Tidy the temporary table - remove name attributes so there aren't 5527 // duplicated in the dom (radio elements for example) 5528 $('[name]', tmpTable).removeAttr('name'); 5529 5530 // Table has been built, attach to the document so we can work with it. 5531 // A holding element is used, positioned at the top of the container 5532 // with minimal height, so it has no effect on if the container scrolls 5533 // or not. Otherwise it might trigger scrolling when it actually isn't 5534 // needed 5535 var holder = $('<div/>').css( scrollX || scrollY ? 5536 { 5537 position: 'absolute', 5538 top: 0, 5539 left: 0, 5540 height: 1, 5541 right: 0, 5542 overflow: 'hidden' 5543 } : 5544 {} 5545 ) 5546 .append( tmpTable ) 5547 .appendTo( tableContainer ); 5548 5549 // When scrolling (X or Y) we want to set the width of the table as 5550 // appropriate. However, when not scrolling leave the table width as it 5551 // is. This results in slightly different, but I think correct behaviour 5552 if ( scrollX && scrollXInner ) { 5553 tmpTable.width( scrollXInner ); 5554 } 5555 else if ( scrollX ) { 5556 tmpTable.css( 'width', 'auto' ); 5557 tmpTable.removeAttr('width'); 5558 5559 // If there is no width attribute or style, then allow the table to 5560 // collapse 5561 if ( tmpTable.outerWidth() < tableContainer.clientWidth && tableWidthAttr ) { 5562 tmpTable.outerWidth( tableContainer.clientWidth ); 5563 } 5564 } 5565 else if ( scrollY ) { 5566 tmpTable.outerWidth( tableContainer.clientWidth ); 5567 } 5568 else if ( tableWidthAttr ) { 5569 tmpTable.outerWidth( tableWidthAttr ); 5570 } 5571 5572 // Get the width of each column in the constructed table 5573 var total = 0; 5574 var bodyCells = tmpTable.find('tbody tr').eq(0).children(); 5575 5576 for ( i=0 ; i<visibleColumns.length ; i++ ) { 5577 // Use getBounding for sub-pixel accuracy, which we then want to round up! 5578 var bounding = bodyCells[i].getBoundingClientRect().width; 5579 5580 // Total is tracked to remove any sub-pixel errors as the outerWidth 5581 // of the table might not equal the total given here 5582 total += bounding; 5583 5584 // Width for each column to use 5585 columns[ visibleColumns[i] ].sWidth = _fnStringToCss( bounding ); 5586 } 5587 5588 table.style.width = _fnStringToCss( total ); 5589
5590 // Finished with the table - ditch it 5591 holder.remove(); 5592 5593 // If there is a width attr, we want to attach an event listener which 5594 // allows the table sizing to automatically adjust when the window is 5595 // resized. Use the width attr rather than CSS, since we can't know if the 5596 // CSS is a relative value or absolute - DOM read is always px. 5597 if ( tableWidthAttr ) { 5598 table.style.width = _fnStringToCss( tableWidthAttr ); 5599 } 5600 5601 if ( (tableWidthAttr || scrollX) && ! settings._reszEvt ) { 5602 var resize = DataTable.util.throttle( function () { 5603 var newWidth = _fnWrapperWidth(settings); 5604 5605 // Don't do it if destroying or the container width is 0 5606 if (! settings.bDestroying && newWidth !== 0) { 5607 _fnAdjustColumnSizing( settings ); 5608 } 5609 } ); 5610 5611 // For browsers that support it (~2020 onwards for wide support) we can watch for the 5612 // container changing width. 5613 if (window.ResizeObserver) { 5614 // This is a tricky beast - if the element is visible when `.observe()` is called, 5615 // then the callback is immediately run. Which we don't want. If the element isn't 5616 // visible, then it isn't run, but we want it to run when it is then made visible. 5617 // This flag allows the above to be satisfied. 5618 var first = $(settings.nTableWrapper).is(':visible'); 5619 5620 // Use an empty div to attach the observer so it isn't impacted by height changes 5621 var resizer = $('<div>') 5622 .css({ 5623 width: '100%', 5624 height: 0 5625 }) 5626 .addClass('dt-autosize') 5627 .appendTo(settings.nTableWrapper); 5628 5629 settings.resizeObserver = new ResizeObserver(function (e) { 5630 if (first) { 5631 first = false; 5632 } 5633 else { 5634 resize(); 5635 } 5636 }); 5637 5638 settings.resizeObserver.observe(resizer[0]); 5639 } 5640 else { 5641 // For old browsers, the best we can do is listen for a window resize 5642 $(window).on('resize.DT-'+settings.sInstance, resize); 5643 } 5644 5645 settings._reszEvt = true; 5646 } 5647 } 5648 5649 /** 5650 * Get the width of the DataTables wrapper element 5651 * 5652 * @param {*} settings DataTables settings object 5653 * @returns Width 5654 */ 5655 function _fnWrapperWidth(settings) { 5656 return $(settings.nTableWrapper).is(':visible') 5657 ? $(settings.nTableWrapper).width() 5658 : 0; 5659 } 5660 5661 /** 5662 * Get the maximum strlen for each data column 5663 * @param {object} settings dataTables settings object 5664 * @param {int} colIdx column of interest 5665 * @returns {string} string of the max length 5666 * @memberof DataTable#oApi 5667 */ 5668 function _fnGetMaxLenString( settings, colIdx ) 5669 { 5670 var column = settings.aoColumns[colIdx]; 5671 5672 if (! column.maxLenString) { 5673 var s, max='', maxLen = -1; 5674 5675 for ( var i=0, ien=settings.aiDisplayMaster.length ; i<ien ; i++ ) { 5676 var rowIdx = settings.aiDisplayMaster[i]; 5677 var data = _fnGetRowDisplay(settings, rowIdx)[colIdx]; 5678 5679 var cellString = data && typeof data === 'object' && data.nodeType 5680 ? data.innerHTML 5681 : data+''; 5682 5683 // Remove id / name attributes from elements so they 5684 // don't interfere with existing elements 5685 cellString = cellString 5686 .replace(/id=".*?"/g, '') 5687 .replace(/name=".*?"/g, ''); 5688 5689 s = _stripHtml(cellString) 5690 .replace( / /g, ' ' ); 5691 5692 if ( s.length > maxLen ) { 5693 // We want the HTML in the string, but the length that 5694 // is important is the stripped string 5695 max = cellString; 5696 maxLen = s.length; 5697 } 5698 } 5699 5700 column.maxLenString = max; 5701 } 5702 5703 return column.maxLenString; 5704 } 5705 5706 5707 /** 5708 * Append a CSS unit (only if required) to a string 5709 * @param {string} value to css-ify 5710 * @returns {string} value with css unit 5711 * @memberof DataTable#oApi 5712 */ 5713 function _fnStringToCss( s ) 5714 { 5715 if ( s === null ) { 5716 return '0px'; 5717 } 5718 5719 if ( typeof s == 'number' ) { 5720 return s < 0 ? 5721 '0px' : 5722 s+'px'; 5723 } 5724 5725 // Check it has a unit character already 5726 return s.match(/\d$/) ? 5727 s+'px' : 5728 s; 5729 } 5730 5731 /** 5732 * Re-insert the `col` elements for current visibility 5733 * 5734 * @param {*} settings DT settings 5735 */ 5736 function _colGroup( settings ) { 5737 var cols = settings.aoColumns; 5738 5739 settings.colgroup.empty(); 5740 5741 for (i=0 ; i<cols.length ; i++) { 5742 if (cols[i].bVisible) { 5743 settings.colgroup.append(cols[i].colEl); 5744 } 5745 } 5746 } 5747 5748 5749 function _fnSortInit( settings ) { 5750 var target = settings.nTHead; 5751 var headerRows = target.querySelectorAll('tr'); 5752 var titleRow = settings.titleRow; 5753 var notSelector = ':not([data-dt-order="disable"]):not([data-dt-order="icon-only"])'; 5754 5755 // Legacy support for `orderCellsTop` 5756 if (titleRow === true) { 5757 target = headerRows[0]; 5758 } 5759 else if (titleRow === false) { 5760 target = headerRows[ headerRows.length - 1 ]; 5761 } 5762 else if (titleRow !== null) { 5763 target = headerRows[titleRow]; 5764 } 5765 // else - all rows 5766 5767 if (settings.orderHandler) { 5768 _fnSortAttachListener( 5769 settings, 5770 target, 5771 target === settings.nTHead 5772 ? 'tr'+notSelector+' th'+notSelector+', tr'+notSelector+' td'+notSelector 5773 : 'th'+notSelector+', td'+notSelector 5774 ); 5775 } 5776 5777 // Need to resolve the user input array into our internal structure 5778 var order = []; 5779 _fnSortResolve( settings, order, settings.aaSorting ); 5780 5781 settings.aaSorting = order; 5782 } 5783 5784 5785 function _fnSortAttachListener(settings, node, selector, column, callback) { 5786 _fnBindAction( node, selector, function (e) { 5787 var run = false;
5788 var columns = column === undefined 5789 ? _fnColumnsFromHeader( e.target ) 5790 : Array.isArray(column) 5791 ? column 5792 : [column]; 5793 5794 if ( columns.length ) { 5795 for ( var i=0, ien=columns.length ; i<ien ; i++ ) { 5796 var ret = _fnSortAdd( settings, columns[i], i, e.shiftKey ); 5797 5798 if (ret !== false) { 5799 run = true; 5800 } 5801 5802 // If the first entry is no sort, then subsequent 5803 // sort columns are ignored 5804 if (settings.aaSorting.length === 1 && settings.aaSorting[0][1] === '') { 5805 break; 5806 } 5807 } 5808 5809 if (run) { 5810 _fnProcessingRun(settings, true, function () { 5811 _fnSort( settings ); 5812 _fnSortDisplay( settings, settings.aiDisplay ); 5813 5814 _fnReDraw( settings, false, false ); 5815 5816 if (callback) { 5817 callback(); 5818 } 5819 }); 5820 } 5821 } 5822 } ); 5823 } 5824 5825 /** 5826 * Sort the display array to match the master's order 5827 * @param {*} settings 5828 */ 5829 function _fnSortDisplay(settings, display) { 5830 if (display.length < 2) { 5831 return; 5832 } 5833 5834 var master = settings.aiDisplayMaster; 5835 var masterMap = {}; 5836 var map = {}; 5837 var i; 5838 5839 // Rather than needing an `indexOf` on master array, we can create a map 5840 for (i=0 ; i<master.length ; i++) { 5841 masterMap[master[i]] = i; 5842 } 5843 5844 // And then cache what would be the indexOf fom the display 5845 for (i=0 ; i<display.length ; i++) { 5846 map[display[i]] = masterMap[display[i]]; 5847 } 5848 5849 display.sort(function(a, b){ 5850 // Short version of this function is simply `master.indexOf(a) - master.indexOf(b);` 5851 return map[a] - map[b]; 5852 }); 5853 } 5854 5855 5856 function _fnSortResolve (settings, nestedSort, sort) { 5857 var push = function ( a ) { 5858 if ($.isPlainObject(a)) { 5859 if (a.idx !== undefined) { 5860 // Index based ordering 5861 nestedSort.push([a.idx, a.dir]); 5862 } 5863 else if (a.name) { 5864 // Name based ordering 5865 var cols = _pluck( settings.aoColumns, 'sName'); 5866 var idx = cols.indexOf(a.name); 5867 5868 if (idx !== -1) { 5869 nestedSort.push([idx, a.dir]); 5870 } 5871 } 5872 } 5873 else { 5874 // Plain column index and direction pair 5875 nestedSort.push(a); 5876 } 5877 }; 5878 5879 if ( $.isPlainObject(sort) ) { 5880 // Object 5881 push(sort); 5882 } 5883 else if ( sort.length && typeof sort[0] === 'number' ) { 5884 // 1D array 5885 push(sort); 5886 } 5887 else if ( sort.length ) { 5888 // 2D array 5889 for (var z=0; z<sort.length; z++) { 5890 push(sort[z]); // Object or array 5891 } 5892 } 5893 } 5894 5895 5896 function _fnSortFlatten ( settings ) 5897 { 5898 var 5899 i, k, kLen, 5900 aSort = [], 5901 extSort = DataTable.ext.type.order, 5902 aoColumns = settings.aoColumns, 5903 aDataSort, iCol, sType, srcCol, 5904 fixed = settings.aaSortingFixed, 5905 fixedObj = $.isPlainObject( fixed ), 5906 nestedSort = []; 5907 5908 if ( ! settings.oFeatures.bSort ) { 5909 return aSort; 5910 } 5911 5912 // Build the sort array, with pre-fix and post-fix options if they have been 5913 // specified 5914 if ( Array.isArray( fixed ) ) { 5915 _fnSortResolve( settings, nestedSort, fixed ); 5916 } 5917 5918 if ( fixedObj && fixed.pre ) { 5919 _fnSortResolve( settings, nestedSort, fixed.pre ); 5920 } 5921 5922 _fnSortResolve( settings, nestedSort, settings.aaSorting ); 5923 5924 if (fixedObj && fixed.post ) { 5925 _fnSortResolve( settings, nestedSort, fixed.post ); 5926 } 5927 5928 for ( i=0 ; i<nestedSort.length ; i++ ) 5929 { 5930 srcCol = nestedSort[i][0]; 5931 5932 if ( aoColumns[ srcCol ] ) { 5933 aDataSort = aoColumns[ srcCol ].aDataSort; 5934 5935 for ( k=0, kLen=aDataSort.length ; k<kLen ; k++ ) 5936 { 5937 iCol = aDataSort[k]; 5938 sType = aoColumns[ iCol ].sType || 'string'; 5939 5940 if ( nestedSort[i]._idx === undefined ) { 5941 nestedSort[i]._idx = aoColumns[iCol].asSorting.indexOf(nestedSort[i][1]); 5942 } 5943 5944 if ( nestedSort[i][1] ) { 5945 aSort.push( { 5946 src: srcCol, 5947 col: iCol, 5948 dir: nestedSort[i][1], 5949 index: nestedSort[i]._idx, 5950 type: sType, 5951 formatter: extSort[ sType+"-pre" ], 5952 sorter: extSort[ sType+"-"+nestedSort[i][1] ] 5953 } ); 5954 } 5955 } 5956 } 5957 } 5958 5959 return aSort; 5960 } 5961 5962 /** 5963 * Change the order of the table 5964 * @param {object} oSettings dataTables settings object 5965 * @memberof DataTable#oApi 5966 */ 5967 function _fnSort ( oSettings, col, dir ) 5968 { 5969 var 5970 i, ien, iLen, 5971 aiOrig = [], 5972 extSort = DataTable.ext.type.order, 5973 aoData = oSettings.aoData, 5974 sortCol, 5975 displayMaster = oSettings.aiDisplayMaster, 5976 aSort; 5977 5978 // Make sure the columns all have types defined 5979 _fnColumnTypes(oSettings); 5980 5981 // Allow a specific column to be sorted, which will _not_ alter the display 5982 // master 5983 if (col !== undefined) { 5984 var srcCol = oSettings.aoColumns[col]; 5985 5986 aSort = [{ 5987 src: col, 5988 col: col, 5989 dir: dir, 5990 index: 0, 5991 type: srcCol.sType, 5992 formatter: extSort[ srcCol.sType+"-pre" ], 5993 sorter: extSort[ srcCol.sType+"-"+dir ] 5994 }]; 5995 displayMaster = displayMaster.slice(); 5996 } 5997 else { 5998 aSort = _fnSortFlatten( oSettings ); 5999 } 6000 6001 for ( i=0, ien=aSort.length ; i<ien ; i++ ) { 6002 sortCol = aSort[i]; 6003 6004 // Load the data needed for the sort, for each cell 6005 _fnSortData( oSettings, sortCol.col ); 6006 } 6007 6008 /* No sorting required if server-side or no sorting array */ 6009 if ( _fnDataSource( oSettings ) != 'ssp' && aSort.length !== 0 ) 6010 { 6011 // Reset the initial positions on each pass so we get a stable sort 6012 for ( i=0, iLen=displayMaster.length ; i<iLen ; i++ ) { 6013 aiOrig[ i ] = i; 6014 } 6015 6016 // If the first sort is desc, then reverse the array to preserve original 6017 // order, just in reverse 6018 if (aSort.length && aSort[0].dir === 'desc' && oSettings.orderDescReverse) { 6019 aiOrig.reverse(); 6020 } 6021
6022 /* Do the sort - here we want multi-column sorting based on a given data source (column) 6023 * and sorting function (from oSort) in a certain direction. It's reasonably complex to 6024 * follow on it's own, but this is what we want (example two column sorting): 6025 * fnLocalSorting = function(a,b){ 6026 * var test; 6027 * test = oSort['string-asc']('data11', 'data12'); 6028 * if (test !== 0) 6029 * return test; 6030 * test = oSort['numeric-desc']('data21', 'data22'); 6031 * if (test !== 0) 6032 * return test; 6033 * return oSort['numeric-asc']( aiOrig[a], aiOrig[b] ); 6034 * } 6035 * Basically we have a test for each sorting column, if the data in that column is equal, 6036 * test the next column. If all columns match, then we use a numeric sort on the row 6037 * positions in the original data array to provide a stable sort. 6038 */ 6039 displayMaster.sort( function ( a, b ) { 6040 var 6041 x, y, k, test, sort, 6042 len=aSort.length, 6043 dataA = aoData[a]._aSortData, 6044 dataB = aoData[b]._aSortData; 6045 6046 for ( k=0 ; k<len ; k++ ) { 6047 sort = aSort[k]; 6048 6049 // Data, which may have already been through a `-pre` function 6050 x = dataA[ sort.col ]; 6051 y = dataB[ sort.col ]; 6052 6053 if (sort.sorter) { 6054 // If there is a custom sorter (`-asc` or `-desc`) for this 6055 // data type, use it 6056 test = sort.sorter(x, y); 6057 6058 if ( test !== 0 ) { 6059 return test; 6060 } 6061 } 6062 else { 6063 // Otherwise, use generic sorting 6064 test = x<y ? -1 : x>y ? 1 : 0; 6065 6066 if ( test !== 0 ) { 6067 return sort.dir === 'asc' ? test : -test; 6068 } 6069 } 6070 } 6071 6072 x = aiOrig[a]; 6073 y = aiOrig[b]; 6074 6075 return x<y ? -1 : x>y ? 1 : 0; 6076 } ); 6077 } 6078 else if ( aSort.length === 0 ) { 6079 // Apply index order 6080 displayMaster.sort(function (x, y) { 6081 return x<y ? -1 : x>y ? 1 : 0; 6082 }); 6083 } 6084 6085 if (col === undefined) { 6086 // Tell the draw function that we have sorted the data 6087 oSettings.bSorted = true; 6088 oSettings.sortDetails = aSort; 6089 6090 _fnCallbackFire( oSettings, null, 'order', [oSettings, aSort] ); 6091 } 6092
6093 return displayMaster; 6094 } 6095 6096 6097 /** 6098 * Function to run on user sort request 6099 * @param {object} settings dataTables settings object 6100 * @param {node} attachTo node to attach the handler to 6101 * @param {int} colIdx column sorting index 6102 * @param {int} addIndex Counter 6103 * @param {boolean} [shift=false] Shift click add 6104 * @param {function} [callback] callback function 6105 * @memberof DataTable#oApi 6106 */ 6107 function _fnSortAdd ( settings, colIdx, addIndex, shift ) 6108 { 6109 var col = settings.aoColumns[ colIdx ]; 6110 var sorting = settings.aaSorting; 6111 var asSorting = col.asSorting; 6112 var nextSortIdx; 6113 var next = function ( a, overflow ) { 6114 var idx = a._idx; 6115 if ( idx === undefined ) { 6116 idx = asSorting.indexOf(a[1]); 6117 } 6118 6119 return idx+1 < asSorting.length ? 6120 idx+1 : 6121 overflow ? 6122 null : 6123 0; 6124 }; 6125 6126 if ( ! col.bSortable ) { 6127 return false; 6128 } 6129 6130 // Convert to 2D array if needed 6131 if ( typeof sorting[0] === 'number' ) { 6132 sorting = settings.aaSorting = [ sorting ]; 6133 } 6134 6135 // If appending the sort then we are multi-column sorting 6136 if ( (shift || addIndex) && settings.oFeatures.bSortMulti ) { 6137 // Are we already doing some kind of sort on this column? 6138 var sortIdx = _pluck(sorting, '0').indexOf(colIdx); 6139 6140 if ( sortIdx !== -1 ) { 6141 // Yes, modify the sort 6142 nextSortIdx = next( sorting[sortIdx], true ); 6143 6144 if ( nextSortIdx === null && sorting.length === 1 ) { 6145 nextSortIdx = 0; // can't remove sorting completely 6146 } 6147 6148 if ( nextSortIdx === null ) { 6149 sorting.splice( sortIdx, 1 ); 6150 } 6151 else { 6152 sorting[sortIdx][1] = asSorting[ nextSortIdx ]; 6153 sorting[sortIdx]._idx = nextSortIdx; 6154 } 6155 } 6156 else if (shift) { 6157 // No sort on this column yet, being added by shift click 6158 // add it as itself 6159 sorting.push( [ colIdx, asSorting[0], 0 ] ); 6160 sorting[sorting.length-1]._idx = 0; 6161 } 6162 else { 6163 // No sort on this column yet, being added from a colspan 6164 // so add with same direction as first column 6165 sorting.push( [ colIdx, sorting[0][1], 0 ] ); 6166 sorting[sorting.length-1]._idx = 0; 6167 } 6168 } 6169 else if ( sorting.length && sorting[0][0] == colIdx ) { 6170 // Single column - already sorting on this column, modify the sort 6171 nextSortIdx = next( sorting[0] ); 6172 6173 sorting.length = 1; 6174 sorting[0][1] = asSorting[ nextSortIdx ]; 6175 sorting[0]._idx = nextSortIdx; 6176 } 6177 else { 6178 // Single column - sort only on this column 6179 sorting.length = 0; 6180 sorting.push( [ colIdx, asSorting[0] ] ); 6181 sorting[0]._idx = 0; 6182 } 6183 } 6184 6185 6186 /** 6187 * Set the sorting classes on table's body, Note: it is safe to call this function 6188 * when bSort and bSortClasses are false 6189 * @param {object} oSettings dataTables settings object 6190 * @memberof DataTable#oApi 6191 */ 6192 function _fnSortingClasses( settings ) 6193 { 6194 var oldSort = settings.aLastSort; 6195 var sortClass = settings.oClasses.order.position; 6196 var sort = _fnSortFlatten( settings ); 6197 var features = settings.oFeatures; 6198 var i, ien, colIdx; 6199 6200 if ( features.bSort && features.bSortClasses ) { 6201 // Remove old sorting classes 6202 for ( i=0, ien=oldSort.length ; i<ien ; i++ ) { 6203 colIdx = oldSort[i].src; 6204 6205 // Remove column sorting 6206 $( _pluck( settings.aoData, 'anCells', colIdx ) ) 6207 .removeClass( sortClass + (i<2 ? i+1 : 3) ); 6208 } 6209 6210 // Add new column sorting 6211 for ( i=0, ien=sort.length ; i<ien ; i++ ) { 6212 colIdx = sort[i].src; 6213 6214 $( _pluck( settings.aoData, 'anCells', colIdx ) ) 6215 .addClass( sortClass + (i<2 ? i+1 : 3) ); 6216 } 6217 } 6218 6219 settings.aLastSort = sort; 6220 } 6221 6222 6223 // Get the data to sort a column, be it from cache, fresh (populating the 6224 // cache), or from a sort formatter 6225 function _fnSortData( settings, colIdx ) 6226 { 6227 // Custom sorting function - provided by the sort data type 6228 var column = settings.aoColumns[ colIdx ]; 6229 var customSort = DataTable.ext.order[ column.sSortDataType ]; 6230 var customData; 6231 6232 if ( customSort ) { 6233 customData = customSort.call( settings.oInstance, settings, colIdx, 6234 _fnColumnIndexToVisible( settings, colIdx ) 6235 ); 6236 } 6237 6238 // Use / populate cache 6239 var row, cellData; 6240 var formatter = DataTable.ext.type.order[ column.sType+"-pre" ]; 6241 var data = settings.aoData; 6242 6243 for ( var rowIdx=0 ; rowIdx<data.length ; rowIdx++ ) { 6244 // Sparse array 6245 if (! data[rowIdx]) { 6246 continue; 6247 } 6248 6249 row = data[rowIdx]; 6250 6251 if ( ! row._aSortData ) { 6252 row._aSortData = []; 6253 } 6254 6255 if ( ! row._aSortData[colIdx] || customSort ) { 6256 cellData = customSort ? 6257 customData[rowIdx] : // If there was a custom sort function, use data from there 6258 _fnGetCellData( settings, rowIdx, colIdx, 'sort' ); 6259 6260 row._aSortData[ colIdx ] = formatter ? 6261 formatter( cellData, settings ) : 6262 cellData; 6263 } 6264 } 6265 } 6266 6267 6268 /** 6269 * State information for a table 6270 * 6271 * @param {*} settings 6272 * @returns State object 6273 */ 6274 function _fnSaveState ( settings ) 6275 { 6276 if (settings._bLoadingState) { 6277 return; 6278 } 6279 6280 // Sort state saving uses [[idx, order]] structure. 6281 var sorting = []; 6282 _fnSortResolve(settings, sorting, settings.aaSorting ); 6283 6284 /* Store the interesting variables */ 6285 var columns = settings.aoColumns; 6286 var state = { 6287 time: +new Date(), 6288 start: settings._iDisplayStart, 6289 length: settings._iDisplayLength, 6290 order: sorting.map(function (sort) { 6291 // If a column name is available, use it 6292 return columns[sort[0]] && columns[sort[0]].sName 6293 ? [ columns[sort[0]].sName, sort[1] ] 6294 : sort.slice(); 6295 } ), 6296 search: $.extend({}, settings.oPreviousSearch), 6297 columns: settings.aoColumns.map( function ( col, i ) { 6298 return { 6299 name: col.sName, 6300 visible: col.bVisible, 6301 search: $.extend({}, settings.aoPreSearchCols[i]) 6302 }; 6303 } ) 6304 }; 6305 6306 settings.oSavedState = state; 6307 _fnCallbackFire( settings, "aoStateSaveParams", 'stateSaveParams', [settings, state] ); 6308
6309 if ( settings.oFeatures.bStateSave && !settings.bDestroying ) 6310 { 6311 settings.fnStateSaveCallback.call( settings.oInstance, settings, state ); 6312 } 6313 } 6314 6315 6316 /** 6317 * Attempt to load a saved table state 6318 * @param {object} oSettings dataTables settings object 6319 * @param {object} oInit DataTables init object so we can override settings 6320 * @param {function} callback Callback to execute when the state has been loaded 6321 * @memberof DataTable#oApi 6322 */ 6323 function _fnLoadState ( settings, init, callback ) 6324 { 6325 if ( ! settings.oFeatures.bStateSave ) { 6326 callback(); 6327 return; 6328 } 6329 6330 var loaded = function(state) { 6331 _fnImplementState(settings, state, callback); 6332 } 6333 6334 var state = settings.fnStateLoadCallback.call( settings.oInstance, settings, loaded ); 6335 6336 if ( state !== undefined ) { 6337 _fnImplementState( settings, state, callback ); 6338 } 6339 // otherwise, wait for the loaded callback to be executed 6340 6341 return true; 6342 } 6343 6344 function _fnImplementState ( settings, s, callback) { 6345 var i, ien; 6346 var columns = settings.aoColumns; 6347 var currentNames = _pluck(settings.aoColumns, 'sName'); 6348 6349 settings._bLoadingState = true; 6350 6351 // When StateRestore was introduced the state could now be implemented at any time 6352 // Not just initialisation. To do this an api instance is required in some places 6353 var api = settings._bInitComplete ? new DataTable.Api(settings) : null; 6354 6355 if ( ! s || ! s.time ) { 6356 settings._bLoadingState = false; 6357 callback(); 6358 return; 6359 } 6360 6361 // Reject old data 6362 var duration = settings.iStateDuration; 6363 if ( duration > 0 && s.time < +new Date() - (duration*1000) ) { 6364 settings._bLoadingState = false; 6365 callback(); 6366 return; 6367 } 6368 6369 // Allow custom and plug-in manipulation functions to alter the saved data set and 6370 // cancelling of loading by returning false 6371 var abStateLoad = _fnCallbackFire( settings, 'aoStateLoadParams', 'stateLoadParams', [settings, s] ); 6372 if ( abStateLoad.indexOf(false) !== -1 ) { 6373 settings._bLoadingState = false; 6374 callback(); 6375 return; 6376 } 6377 6378 // Store the saved state so it might be accessed at any time 6379 settings.oLoadedState = $.extend( true, {}, s ); 6380 6381 // This is needed for ColReorder, which has to happen first to allow all 6382 // the stored indexes to be usable. It is not publicly documented. 6383 _fnCallbackFire( settings, null, 'stateLoadInit', [settings, s], true ); 6384 6385 // Page Length 6386 if ( s.length !== undefined ) { 6387 // If already initialised just set the value directly so that the select element is also updated 6388 if (api) { 6389 api.page.len(s.length) 6390 } 6391 else { 6392 settings._iDisplayLength = s.length; 6393 } 6394 } 6395 6396 // Restore key features 6397 if ( s.start !== undefined ) { 6398 if(api === null) { 6399 settings._iDisplayStart = s.start; 6400 settings.iInitDisplayStart = s.start; 6401 } 6402 else { 6403 _fnPageChange(settings, s.start/settings._iDisplayLength); 6404 } 6405 } 6406 6407 // Order 6408 if ( s.order !== undefined ) { 6409 settings.aaSorting = []; 6410 $.each( s.order, function ( i, col ) { 6411 var set = [ col[0], col[1] ]; 6412 6413 // A column name was stored and should be used for restore 6414 if (typeof col[0] === 'string') { 6415 // Find the name from the current list of column names 6416 var idx = currentNames.indexOf(col[0]); 6417 6418 if (idx < 0) { 6419 // If the column was not found ignore it and continue 6420 return; 6421 } 6422 6423 set[0] = idx; 6424 } 6425 else if (set[0] >= columns.length) { 6426 // If the column index is out of bounds ignore it and continue 6427 return; 6428 } 6429 6430 settings.aaSorting.push(set); 6431 } ); 6432 } 6433 6434 // Search 6435 if ( s.search !== undefined ) { 6436 $.extend( settings.oPreviousSearch, s.search ); 6437 } 6438 6439 // Columns 6440 if ( s.columns ) { 6441 var set = s.columns; 6442 var incoming = _pluck(s.columns, 'name'); 6443 6444 // Check if it is a 2.2 style state object with a `name` property for the columns, and if 6445 // the name was defined. If so, then create a new array that will map the state object 6446 // given, to the current columns (don't bother if they are already matching tho). 6447 if (incoming.join('').length && incoming.join('') !== currentNames.join('')) { 6448 set = []; 6449 6450 // For each column, try to find the name in the incoming array 6451 for (i=0 ; i<currentNames.length ; i++) { 6452 if (currentNames[i] != '') { 6453 var idx = incoming.indexOf(currentNames[i]); 6454 6455 if (idx >= 0) { 6456 set.push(s.columns[idx]); 6457 } 6458 else { 6459 // No matching column name in the state's columns, so this might be a new 6460 // column and thus can't have a state already. 6461 set.push({}); 6462 } 6463 } 6464 else { 6465 // If no name, but other columns did have a name, then there is no knowing
6466 // where this one came from originally so it can't be restored. 6467 set.push({}); 6468 } 6469 } 6470 } 6471 6472 // If the number of columns to restore is different from current, then all bets are off. 6473 if (set.length === columns.length) { 6474 for ( i=0, ien=set.length ; i<ien ; i++ ) { 6475 var col = set[i]; 6476 6477 // Visibility 6478 if ( col.visible !== undefined ) { 6479 // If the api is defined, the table has been initialised so we need to use it rather than internal settings 6480 if (api) { 6481 // Don't redraw the columns on every iteration of this loop, we will do this at the end instead 6482 api.column(i).visible(col.visible, false); 6483 } 6484 else { 6485 columns[i].bVisible = col.visible; 6486 } 6487 } 6488 6489 // Search 6490 if ( col.search !== undefined ) { 6491 $.extend( settings.aoPreSearchCols[i], col.search ); 6492 } 6493 } 6494 6495 // If the api is defined then we need to adjust the columns once the visibility has been changed 6496 if (api) { 6497 api.columns.adjust(); 6498 } 6499 } 6500 } 6501 6502 settings._bLoadingState = false; 6503 _fnCallbackFire( settings, 'aoStateLoaded', 'stateLoaded', [settings, s] ); 6504 callback(); 6505 } 6506 6507 /** 6508 * Log an error message 6509 * @param {object} settings dataTables settings object 6510 * @param {int} level log error messages, or display them to the user 6511 * @param {string} msg error message 6512 * @param {int} tn Technical note id to get more information about the error. 6513 * @memberof DataTable#oApi 6514 */ 6515 function _fnLog( settings, level, msg, tn ) 6516 { 6517 msg = 'DataTables warning: '+ 6518 (settings ? 'table id='+settings.sTableId+' - ' : '')+msg; 6519 6520 if ( tn ) { 6521 msg += '. For more information about this error, please see '+ 6522 'https://datatables.net/tn/'+tn; 6523 } 6524 6525 if ( ! level ) { 6526 // Backwards compatibility pre 1.10 6527 var ext = DataTable.ext; 6528 var type = ext.sErrMode || ext.errMode; 6529 6530 if ( settings ) { 6531 _fnCallbackFire( settings, null, 'dt-error', [ settings, tn, msg ], true ); 6532 } 6533 6534 if ( type == 'alert' ) { 6535 alert( msg ); 6536 } 6537 else if ( type == 'throw' ) { 6538 throw new Error(msg); 6539 } 6540 else if ( typeof type == 'function' ) { 6541 type( settings, tn, msg ); 6542 } 6543 } 6544 else if ( window.console && console.log ) { 6545 console.log( msg ); 6546 } 6547 } 6548 6549 6550 /** 6551 * See if a property is defined on one object, if so assign it to the other object 6552 * @param {object} ret target object 6553 * @param {object} src source object 6554 * @param {string} name property 6555 * @param {string} [mappedName] name to map too - optional, name used if not given 6556 * @memberof DataTable#oApi 6557 */ 6558 function _fnMap( ret, src, name, mappedName ) 6559 { 6560 if ( Array.isArray( name ) ) { 6561 $.each( name, function (i, val) { 6562 if ( Array.isArray( val ) ) { 6563 _fnMap( ret, src, val[0], val[1] ); 6564 } 6565 else { 6566 _fnMap( ret, src, val ); 6567 } 6568 } ); 6569 6570 return; 6571 } 6572 6573 if ( mappedName === undefined ) { 6574 mappedName = name; 6575 } 6576 6577 if ( src[name] !== undefined ) { 6578 ret[mappedName] = src[name]; 6579 } 6580 } 6581 6582 6583 /** 6584 * Extend objects - very similar to jQuery.extend, but deep copy objects, and 6585 * shallow copy arrays. The reason we need to do this, is that we don't want to 6586 * deep copy array init values (such as aaSorting) since the dev wouldn't be 6587 * able to override them, but we do want to deep copy arrays. 6588 * @param {object} out Object to extend 6589 * @param {object} extender Object from which the properties will be applied to 6590 * out 6591 * @param {boolean} breakRefs If true, then arrays will be sliced to take an 6592 * independent copy with the exception of the `data` or `aaData` parameters 6593 * if they are present. This is so you can pass in a collection to 6594 * DataTables and have that used as your data source without breaking the 6595 * references 6596 * @returns {object} out Reference, just for convenience - out === the return. 6597 * @memberof DataTable#oApi 6598 * @todo This doesn't take account of arrays inside the deep copied objects. 6599 */ 6600 function _fnExtend( out, extender, breakRefs ) 6601 { 6602 var val; 6603 6604 for ( var prop in extender ) { 6605 if ( Object.prototype.hasOwnProperty.call(extender, prop) ) { 6606 val = extender[prop]; 6607 6608 if ( $.isPlainObject( val ) ) { 6609 if ( ! $.isPlainObject( out[prop] ) ) { 6610 out[prop] = {}; 6611 } 6612 $.extend( true, out[prop], val ); 6613 } 6614 else if ( breakRefs && prop !== 'data' && prop !== 'aaData' && Array.isArray(val) ) { 6615 out[prop] = val.slice(); 6616 } 6617 else { 6618 out[prop] = val; 6619 } 6620 } 6621 } 6622 6623 return out; 6624 } 6625 6626 6627 /** 6628 * Bind an event handers to allow a click or return key to activate the callback. 6629 * This is good for accessibility since a return on the keyboard will have the 6630 * same effect as a click, if the element has focus. 6631 * @param {element} n Element to bind the action to 6632 * @param {object|string} selector Selector (for delegated events) or data object 6633 * to pass to the triggered function 6634 * @param {function} fn Callback function for when the event is triggered 6635 * @memberof DataTable#oApi 6636 */ 6637 function _fnBindAction( n, selector, fn ) 6638 { 6639 $(n) 6640 .on( 'click.DT', selector, function (e) { 6641 fn(e); 6642 } ) 6643 .on( 'keypress.DT', selector, function (e){ 6644 if ( e.which === 13 ) { 6645 e.preventDefault(); 6646 fn(e); 6647 } 6648 } ) 6649 .on( 'selectstart.DT', selector, function () { 6650 // Don't want a double click resulting in text selection 6651 return false;
6652 } ); 6653 } 6654 6655 6656 /** 6657 * Register a callback function. Easily allows a callback function to be added to 6658 * an array store of callback functions that can then all be called together. 6659 * @param {object} settings dataTables settings object 6660 * @param {string} store Name of the array storage for the callbacks in oSettings 6661 * @param {function} fn Function to be called back 6662 * @memberof DataTable#oApi 6663 */ 6664 function _fnCallbackReg( settings, store, fn ) 6665 { 6666 if ( fn ) { 6667 settings[store].push(fn); 6668 } 6669 } 6670 6671 6672 /** 6673 * Fire callback functions and trigger events. Note that the loop over the 6674 * callback array store is done backwards! Further note that you do not want to 6675 * fire off triggers in time sensitive applications (for example cell creation) 6676 * as its slow. 6677 * @param {object} settings dataTables settings object 6678 * @param {string} callbackArr Name of the array storage for the callbacks in 6679 * oSettings 6680 * @param {string} eventName Name of the jQuery custom event to trigger. If 6681 * null no trigger is fired 6682 * @param {array} args Array of arguments to pass to the callback function / 6683 * trigger 6684 * @param {boolean} [bubbles] True if the event should bubble 6685 * @memberof DataTable#oApi 6686 */ 6687 function _fnCallbackFire( settings, callbackArr, eventName, args, bubbles ) 6688 { 6689 var ret = []; 6690 6691 if ( callbackArr ) { 6692 ret = settings[callbackArr].slice().reverse().map( function (val) { 6693 return val.apply( settings.oInstance, args ); 6694 } ); 6695 } 6696 6697 if ( eventName !== null) { 6698 var e = $.Event( eventName+'.dt' ); 6699 var table = $(settings.nTable); 6700 6701 // Expose the DataTables API on the event object for easy access 6702 e.dt = settings.api; 6703 6704 table[bubbles ? 'trigger' : 'triggerHandler']( e, args ); 6705 6706 // If not yet attached to the document, trigger the event 6707 // on the body directly to sort of simulate the bubble 6708 if (bubbles && table.parents('body').length === 0) { 6709 $('body').trigger( e, args ); 6710 } 6711 6712 ret.push( e.result ); 6713 } 6714 6715 return ret; 6716 } 6717 6718 6719 function _fnLengthOverflow ( settings ) 6720 { 6721 var 6722 start = settings._iDisplayStart, 6723 end = settings.fnDisplayEnd(), 6724 len = settings._iDisplayLength; 6725 6726 /* If we have space to show extra rows (backing up from the end point - then do so */ 6727 if ( start >= end ) 6728 { 6729 start = end - len; 6730 } 6731 6732 // Keep the start record on the current page 6733 start -= (start % len); 6734 6735 if ( len === -1 || start < 0 ) 6736 { 6737 start = 0; 6738 } 6739 6740 settings._iDisplayStart = start; 6741 } 6742 6743 6744 function _fnRenderer( settings, type ) 6745 { 6746 var renderer = settings.renderer; 6747 var host = DataTable.ext.renderer[type]; 6748 6749 if ( $.isPlainObject( renderer ) && renderer[type] ) { 6750 // Specific renderer for this type. If available use it, otherwise use 6751 // the default. 6752 return host[renderer[type]] || host._; 6753 } 6754 else if ( typeof renderer === 'string' ) { 6755 // Common renderer - if there is one available for this type use it, 6756 // otherwise use the default 6757 return host[renderer] || host._; 6758 } 6759 6760 // Use the default 6761 return host._; 6762 } 6763 6764 6765 /** 6766 * Detect the data source being used for the table. Used to simplify the code 6767 * a little (ajax) and to make it compress a little smaller. 6768 * 6769 * @param {object} settings dataTables settings object 6770 * @returns {string} Data source 6771 * @memberof DataTable#oApi 6772 */ 6773 function _fnDataSource ( settings ) 6774 { 6775 if ( settings.oFeatures.bServerSide ) { 6776 return 'ssp'; 6777 } 6778 else if ( settings.ajax ) { 6779 return 'ajax'; 6780 } 6781 return 'dom'; 6782 } 6783 6784 /** 6785 * Common replacement for language strings 6786 * 6787 * @param {*} settings DT settings object 6788 * @param {*} str String with values to replace 6789 * @param {*} entries Plural number for _ENTRIES_ - can be undefined 6790 * @returns String 6791 */ 6792 function _fnMacros ( settings, str, entries ) 6793 { 6794 // When infinite scrolling, we are always starting at 1. _iDisplayStart is 6795 // used only internally 6796 var 6797 formatter = settings.fnFormatNumber, 6798 start = settings._iDisplayStart+1, 6799 len = settings._iDisplayLength, 6800 vis = settings.fnRecordsDisplay(), 6801 max = settings.fnRecordsTotal(), 6802 all = len === -1; 6803 6804 return str. 6805 replace(/_START_/g, formatter.call( settings, start ) ). 6806 replace(/_END_/g, formatter.call( settings, settings.fnDisplayEnd() ) ). 6807 replace(/_MAX_/g, formatter.call( settings, max ) ). 6808 replace(/_TOTAL_/g, formatter.call( settings, vis ) ). 6809 replace(/_PAGE_/g, formatter.call( settings, all ? 1 : Math.ceil( start / len ) ) ). 6810 replace(/_PAGES_/g, formatter.call( settings, all ? 1 : Math.ceil( vis / len ) ) ). 6811 replace(/_ENTRIES_/g, settings.api.i18n('entries', '', entries) ). 6812 replace(/_ENTRIES-MAX_/g, settings.api.i18n('entries', '', max) ). 6813 replace(/_ENTRIES-TOTAL_/g, settings.api.i18n('entries', '', vis) ); 6814 } 6815 6816 /** 6817 * Add elements to an array as quickly as possible, but stack stafe. 6818 * 6819 * @param {*} arr Array to add the data to 6820 * @param {*} data Data array that is to be added 6821 * @returns 6822 */ 6823 function _fnArrayApply(arr, data) { 6824 if (! data) { 6825 return; 6826 } 6827 6828 // Chrome can throw a max stack error if apply is called with 6829 // too large an array, but apply is faster. 6830 if (data.length < 10000) { 6831 arr.push.apply(arr, data); 6832 } 6833 else { 6834 for (i=0 ; i<data.length ; i++) { 6835 arr.push(data[i]); 6836 } 6837 } 6838 } 6839 6840 /** 6841 * Add one or more listeners to the table 6842 * 6843 * @param {*} that JQ for the table 6844 * @param {*} name Event name 6845 * @param {*} src Listener(s) 6846 */ 6847 function _fnListener(that, name, src) { 6848 if (!Array.isArray(src)) { 6849 src = [src]; 6850 } 6851 6852 for (i=0 ; i<src.length ; i++) { 6853 that.on(name + '.dt', src[i]); 6854 } 6855 } 6856 6857 6858 6859 /** 6860 * Computed structure of the DataTables API, defined by the options passed to 6861 * `DataTable.Api.register()` when building the API. 6862 * 6863 * The structure is built in order to speed creation and extension of the Api 6864 * objects since the extensions are effectively pre-parsed. 6865 * 6866 * The array is an array of objects with the following structure, where this 6867 * base array represents the Api prototype base: 6868 * 6869 * [ 6870 * { 6871 * name: 'data' -- string - Property name 6872 * val: function () {}, -- function - Api method (or undefined if just an object 6873 * methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result 6874 * propExt: [ ... ] -- array - Array of Api object definitions to extend the property 6875 * }, 6876 * { 6877 * name: 'row' 6878 * val: {}, 6879 * methodExt: [ ... ], 6880 * propExt: [ 6881 * { 6882 * name: 'data' 6883 * val: function () {}, 6884 * methodExt: [ ... ], 6885 * propExt: [ ... ] 6886 * }, 6887 * ... 6888 * ] 6889 * } 6890 * ] 6891 * 6892 * @type {Array} 6893 * @ignore 6894 */ 6895 var __apiStruct = []; 6896 6897 6898 /** 6899 * `Array.prototype` reference. 6900 * 6901 * @type object 6902 * @ignore 6903 */ 6904 var __arrayProto = Array.prototype; 6905 6906 6907 /** 6908 * Abstraction for `context` parameter of the `Api` constructor to allow it to 6909 * take several different forms for ease of use. 6910 * 6911 * Each of the input parameter types will be converted to a DataTables settings 6912 * object where possible. 6913 * 6914 * @param {string|node|jQuery|object} mixed DataTable identifier. Can be one 6915 * of: 6916 * 6917 * * `string` - jQuery selector. Any DataTables' matching the given selector 6918 * with be found and used. 6919 * * `node` - `TABLE` node which has already been formed into a DataTable. 6920 * * `jQuery` - A jQuery object of `TABLE` nodes. 6921 * * `object` - DataTables settings object 6922 * * `DataTables.Api` - API instance 6923 * @return {array|null} Matching DataTables settings objects. `null` or 6924 * `undefined` is returned if no matching DataTable is found. 6925 * @ignore 6926 */ 6927 var _toSettings = function ( mixed ) 6928 { 6929 var idx, jq; 6930 var settings = DataTable.settings; 6931 var tables = _pluck(settings, 'nTable'); 6932 6933 if ( ! mixed ) { 6934 return []; 6935 } 6936 else if ( mixed.nTable && mixed.oFeatures ) { 6937 // DataTables settings object 6938 return [ mixed ]; 6939 } 6940 else if ( mixed.nodeName && mixed.nodeName.toLowerCase() === 'table' ) { 6941 // Table node 6942 idx = tables.indexOf(mixed); 6943 return idx !== -1 ? [ settings[idx] ] : null; 6944 } 6945 else if ( mixed && typeof mixed.settings === 'function' ) { 6946 return mixed.settings().toArray(); 6947 } 6948 else if ( typeof mixed === 'string' ) { 6949 // jQuery selector 6950 jq = $(mixed).get(); 6951 } 6952 else if ( mixed instanceof $ ) { 6953 // jQuery object (also DataTables instance) 6954 jq = mixed.get(); 6955 } 6956 6957 if ( jq ) { 6958 return settings.filter(function (v, idx) { 6959 return jq.includes(tables[idx]); 6960 }); 6961 } 6962 }; 6963 6964 6965 /** 6966 * DataTables API class - used to control and interface with one or more 6967 * DataTables enhanced tables. 6968 * 6969 * The API class is heavily based on jQuery, presenting a chainable interface 6970 * that you can use to interact with tables. Each instance of the API class has 6971 * a "context" - i.e. the tables that it will operate on. This could be a single 6972 * table, all tables on a page or a sub-set thereof. 6973 * 6974 * Additionally the API is designed to allow you to easily work with the data in 6975 * the tables, retrieving and manipulating it as required. This is done by 6976 * presenting the API class as an array like interface. The contents of the 6977 * array depend upon the actions requested by each method (for example 6978 * `rows().nodes()` will return an array of nodes, while `rows().data()` will
6979 * return an array of objects or arrays depending upon your table's 6980 * configuration). The API object has a number of array like methods (`push`, 6981 * `pop`, `reverse` etc) as well as additional helper methods (`each`, `pluck`, 6982 * `unique` etc) to assist your working with the data held in a table. 6983 * 6984 * Most methods (those which return an Api instance) are chainable, which means 6985 * the return from a method call also has all of the methods available that the 6986 * top level object had. For example, these two calls are equivalent: 6987 * 6988 * // Not chained 6989 * api.row.add( {...} ); 6990 * api.draw(); 6991 * 6992 * // Chained 6993 * api.row.add( {...} ).draw(); 6994 * 6995 * @class DataTable.Api 6996 * @param {array|object|string|jQuery} context DataTable identifier. This is 6997 * used to define which DataTables enhanced tables this API will operate on. 6998 * Can be one of: 6999 * 7000 * * `string` - jQuery selector. Any DataTables' matching the given selector 7001 * with be found and used. 7002 * * `node` - `TABLE` node which has already been formed into a DataTable. 7003 * * `jQuery` - A jQuery object of `TABLE` nodes. 7004 * * `object` - DataTables settings object 7005 * @param {array} [data] Data to initialise the Api instance with. 7006 * 7007 * @example 7008 * // Direct initialisation during DataTables construction 7009 * var api = $('#example').DataTable(); 7010 * 7011 * @example 7012 * // Initialisation using a DataTables jQuery object 7013 * var api = $('#example').dataTable().api(); 7014 * 7015 * @example 7016 * // Initialisation as a constructor 7017 * var api = new DataTable.Api( 'table.dataTable' ); 7018 */ 7019 _Api = function ( context, data ) 7020 { 7021 if ( ! (this instanceof _Api) ) { 7022 return new _Api( context, data ); 7023 } 7024 7025 var i; 7026 var settings = []; 7027 var ctxSettings = function ( o ) { 7028 var a = _toSettings( o ); 7029 if ( a ) { 7030 settings.push.apply( settings, a ); 7031 } 7032 }; 7033 7034 if ( Array.isArray( context ) ) { 7035 for ( i=0 ; i<context.length ; i++ ) { 7036 ctxSettings( context[i] ); 7037 } 7038 } 7039 else { 7040 ctxSettings( context ); 7041 } 7042 7043 // Remove duplicates 7044 this.context = settings.length > 1 7045 ? _unique( settings ) 7046 : settings; 7047 7048 // Initial data 7049 _fnArrayApply(this, data); 7050 7051 // selector 7052 this.selector = { 7053 rows: null, 7054 cols: null, 7055 opts: null 7056 }; 7057 7058 _Api.extend( this, this, __apiStruct ); 7059 }; 7060 7061 DataTable.Api = _Api; 7062 7063 // Don't destroy the existing prototype, just extend it. Required for jQuery 2's 7064 // isPlainObject. 7065 $.extend( _Api.prototype, { 7066 any: function () 7067 { 7068 return this.count() !== 0; 7069 }, 7070 7071 context: [], // array of table settings objects 7072 7073 count: function () 7074 { 7075 return this.flatten().length; 7076 }, 7077 7078 each: function ( fn ) 7079 { 7080 for ( var i=0, ien=this.length ; i<ien; i++ ) { 7081 fn.call( this, this[i], i, this ); 7082 } 7083 7084 return this; 7085 }, 7086 7087 eq: function ( idx ) 7088 { 7089 var ctx = this.context; 7090 7091 return ctx.length > idx ? 7092 new _Api( ctx[idx], this[idx] ) : 7093 null; 7094 }, 7095 7096 filter: function ( fn ) 7097 { 7098 var a = __arrayProto.filter.call( this, fn, this ); 7099 7100 return new _Api( this.context, a ); 7101 }, 7102 7103 flatten: function () 7104 { 7105 var a = []; 7106 7107 return new _Api( this.context, a.concat.apply( a, this.toArray() ) ); 7108 }, 7109 7110 get: function ( idx ) 7111 { 7112 return this[ idx ]; 7113 }, 7114 7115 join: __arrayProto.join, 7116 7117 includes: function ( find ) { 7118 return this.indexOf( find ) === -1 ? false : true; 7119 }, 7120 7121 indexOf: __arrayProto.indexOf, 7122 7123 iterator: function ( flatten, type, fn, alwaysNew ) { 7124 var 7125 a = [], ret, 7126 i, ien, j, jen, 7127 context = this.context, 7128 rows, items, item, 7129 selector = this.selector; 7130 7131 // Argument shifting 7132 if ( typeof flatten === 'string' ) { 7133 alwaysNew = fn; 7134 fn = type; 7135 type = flatten; 7136 flatten = false; 7137 } 7138 7139 for ( i=0, ien=context.length ; i<ien ; i++ ) { 7140 var apiInst = new _Api( context[i] ); 7141 7142 if ( type === 'table' ) { 7143 ret = fn.call( apiInst, context[i], i ); 7144 7145 if ( ret !== undefined ) { 7146 a.push( ret ); 7147 } 7148 } 7149 else if ( type === 'columns' || type === 'rows' ) { 7150 // this has same length as context - one entry for each table 7151 ret = fn.call( apiInst, context[i], this[i], i ); 7152 7153 if ( ret !== undefined ) { 7154 a.push( ret ); 7155 } 7156 } 7157 else if ( type === 'every' || type === 'column' || type === 'column-rows' || type === 'row' || type === 'cell' ) { 7158 // columns and rows share the same structure. 7159 // 'this' is an array of column indexes for each context 7160 items = this[i]; 7161 7162 if ( type === 'column-rows' ) { 7163 rows = _selector_row_indexes( context[i], selector.opts ); 7164 } 7165 7166 for ( j=0, jen=items.length ; j<jen ; j++ ) { 7167 item = items[j]; 7168 7169 if ( type === 'cell' ) { 7170 ret = fn.call( apiInst, context[i], item.row, item.column, i, j ); 7171 } 7172 else { 7173 ret = fn.call( apiInst, context[i], item, i, j, rows ); 7174 } 7175 7176 if ( ret !== undefined ) { 7177 a.push( ret ); 7178 } 7179 } 7180 } 7181 } 7182 7183 if ( a.length || alwaysNew ) { 7184 var api = new _Api( context, flatten ? a.concat.apply( [], a ) : a ); 7185 var apiSelector = api.selector; 7186 apiSelector.rows = selector.rows; 7187 apiSelector.cols = selector.cols; 7188 apiSelector.opts = selector.opts; 7189 return api; 7190 } 7191 return this; 7192 }, 7193 7194 lastIndexOf: __arrayProto.lastIndexOf, 7195 7196 length: 0, 7197 7198 map: function ( fn ) 7199 { 7200 var a = __arrayProto.map.call( this, fn, this ); 7201 7202 return new _Api( this.context, a ); 7203 }, 7204 7205 pluck: function ( prop ) 7206 { 7207 var fn = DataTable.util.get(prop); 7208 7209 return this.map( function ( el ) { 7210 return fn(el); 7211 } ); 7212 }, 7213 7214 pop: __arrayProto.pop, 7215 7216 push: __arrayProto.push, 7217 7218 reduce: __arrayProto.reduce, 7219 7220 reduceRight: __arrayProto.reduceRight, 7221 7222 reverse: __arrayProto.reverse, 7223 7224 // Object with rows, columns and opts 7225 selector: null, 7226 7227 shift: __arrayProto.shift, 7228 7229 slice: function () { 7230 return new _Api( this.context, this ); 7231 }, 7232 7233 sort: __arrayProto.sort, 7234 7235 splice: __arrayProto.splice, 7236 7237 toArray: function () 7238 { 7239 return __arrayProto.slice.call( this ); 7240 }, 7241 7242 to$: function () 7243 { 7244 return $( this ); 7245 }, 7246 7247 toJQuery: function () 7248 { 7249 return $( this ); 7250 }, 7251 7252 unique: function () 7253 { 7254 return new _Api( this.context, _unique(this.toArray()) ); 7255 }, 7256 7257 unshift: __arrayProto.unshift 7258 } ); 7259 7260 7261 function _api_scope( scope, fn, struc ) { 7262 return function () { 7263 var ret = fn.apply( scope || this, arguments ); 7264 7265 // Method extension 7266 _Api.extend( ret, ret, struc.methodExt ); 7267 return ret; 7268 }; 7269 } 7270 7271 function _api_find( src, name ) { 7272 for ( var i=0, ien=src.length ; i<ien ; i++ ) { 7273 if ( src[i].name === name ) { 7274 return src[i]; 7275 } 7276 } 7277 return null; 7278 } 7279 7280 window.__apiStruct = __apiStruct; 7281 7282 _Api.extend = function ( scope, obj, ext ) 7283 { 7284 // Only extend API instances and static properties of the API 7285 if ( ! ext.length || ! obj || ( ! (obj instanceof _Api) && ! obj.__dt_wrapper ) ) { 7286 return; 7287 } 7288 7289 var 7290 i, ien, 7291 struct; 7292 7293 for ( i=0, ien=ext.length ; i<ien ; i++ ) { 7294 struct = ext[i]; 7295 7296 if (struct.name === '__proto__') { 7297 continue; 7298 } 7299 7300 // Value 7301 obj[ struct.name ] = struct.type === 'function' ? 7302 _api_scope( scope, struct.val, struct ) : 7303 struct.type === 'object' ? 7304 {} : 7305 struct.val; 7306 7307 obj[ struct.name ].__dt_wrapper = true; 7308 7309 // Property extension 7310 _Api.extend( scope, obj[ struct.name ], struct.propExt ); 7311 } 7312 }; 7313 7314 // [ 7315 // { 7316 // name: 'data' -- string - Property name 7317 // val: function () {}, -- function - Api method (or undefined if just an object 7318 // methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result 7319 // propExt: [ ... ] -- array - Array of Api object definitions to extend the property 7320 // }, 7321 // { 7322 // name: 'row' 7323 // val: {}, 7324 // methodExt: [ ... ], 7325 // propExt: [ 7326 // { 7327 // name: 'data' 7328 // val: function () {}, 7329 // methodExt: [ ... ], 7330 // propExt: [ ... ] 7331 // }, 7332 // ... 7333 // ] 7334 // } 7335 // ] 7336 7337 7338 _Api.register = _api_register = function ( name, val ) 7339 { 7340 if ( Array.isArray( name ) ) { 7341 for ( var j=0, jen=name.length ; j<jen ; j++ ) { 7342 _Api.register( name[j], val ); 7343 } 7344 return; 7345 } 7346 7347 var 7348 i, ien, 7349 heir = name.split('.'), 7350 struct = __apiStruct, 7351 key, method; 7352 7353 for ( i=0, ien=heir.length ; i<ien ; i++ ) { 7354 method = heir[i].indexOf('()') !== -1; 7355 key = method ?
7356 heir[i].replace('()', '') : 7357 heir[i]; 7358 7359 var src = _api_find( struct, key ); 7360 if ( ! src ) { 7361 src = { 7362 name: key, 7363 val: {}, 7364 methodExt: [], 7365 propExt: [], 7366 type: 'object' 7367 }; 7368 struct.push( src ); 7369 } 7370 7371 if ( i === ien-1 ) { 7372 src.val = val; 7373 src.type = typeof val === 'function' ? 7374 'function' : 7375 $.isPlainObject( val ) ? 7376 'object' : 7377 'other'; 7378 } 7379 else { 7380 struct = method ? 7381 src.methodExt : 7382 src.propExt; 7383 } 7384 } 7385 }; 7386 7387 _Api.registerPlural = _api_registerPlural = function ( pluralName, singularName, val ) { 7388 _Api.register( pluralName, val ); 7389 7390 _Api.register( singularName, function () { 7391 var ret = val.apply( this, arguments ); 7392 7393 if ( ret === this ) { 7394 // Returned item is the API instance that was passed in, return it 7395 return this; 7396 } 7397 else if ( ret instanceof _Api ) { 7398 // New API instance returned, want the value from the first item 7399 // in the returned array for the singular result. 7400 return ret.length ? 7401 Array.isArray( ret[0] ) ? 7402 new _Api( ret.context, ret[0] ) : // Array results are 'enhanced' 7403 ret[0] : 7404 undefined; 7405 } 7406 7407 // Non-API return - just fire it back 7408 return ret; 7409 } ); 7410 }; 7411 7412 7413 /** 7414 * Selector for HTML tables. Apply the given selector to the give array of 7415 * DataTables settings objects. 7416 * 7417 * @param {string|integer} [selector] jQuery selector string or integer 7418 * @param {array} Array of DataTables settings objects to be filtered 7419 * @return {array} 7420 * @ignore 7421 */ 7422 var __table_selector = function ( selector, a ) 7423 { 7424 if ( Array.isArray(selector) ) { 7425 var result = []; 7426 7427 selector.forEach(function (sel) { 7428 var inner = __table_selector(sel, a); 7429 7430 _fnArrayApply(result, inner); 7431 }); 7432 7433 return result.filter( function (item) { 7434 return item; 7435 }); 7436 } 7437 7438 // Integer is used to pick out a table by index 7439 if ( typeof selector === 'number' ) { 7440 return [ a[ selector ] ]; 7441 } 7442 7443 // Perform a jQuery selector on the table nodes 7444 var nodes = a.map( function (el) { 7445 return el.nTable; 7446 } ); 7447 7448 return $(nodes) 7449 .filter( selector ) 7450 .map( function () { 7451 // Need to translate back from the table node to the settings 7452 var idx = nodes.indexOf(this); 7453 return a[ idx ]; 7454 } ) 7455 .toArray(); 7456 }; 7457 7458 7459 7460 /** 7461 * Context selector for the API's context (i.e. the tables the API instance 7462 * refers to. 7463 * 7464 * @name DataTable.Api#tables 7465 * @param {string|integer} [selector] Selector to pick which tables the iterator 7466 * should operate on. If not given, all tables in the current context are 7467 * used. This can be given as a jQuery selector (for example `':gt(0)'`) to 7468 * select multiple tables or as an integer to select a single table. 7469 * @returns {DataTable.Api} Returns a new API instance if a selector is given. 7470 */ 7471 _api_register( 'tables()', function ( selector ) { 7472 // A new instance is created if there was a selector specified 7473 return selector !== undefined && selector !== null ? 7474 new _Api( __table_selector( selector, this.context ) ) : 7475 this; 7476 } ); 7477 7478 7479 _api_register( 'table()', function ( selector ) { 7480 var tables = this.tables( selector ); 7481 var ctx = tables.context; 7482 7483 // Truncate to the first matched table 7484 return ctx.length ? 7485 new _Api( ctx[0] ) : 7486 tables; 7487 } ); 7488 7489 // Common methods, combined to reduce size 7490 [ 7491 ['nodes', 'node', 'nTable'], 7492 ['body', 'body', 'nTBody'], 7493 ['header', 'header', 'nTHead'], 7494 ['footer', 'footer', 'nTFoot'], 7495 ].forEach(function (item) { 7496 _api_registerPlural( 7497 'tables().' + item[0] + '()', 7498 'table().' + item[1] + '()' , 7499 function () { 7500 return this.iterator( 'table', function ( ctx ) { 7501 return ctx[item[2]]; 7502 }, 1 ); 7503 } 7504 ); 7505 }); 7506 7507 // Structure methods 7508 [ 7509 ['header', 'aoHeader'], 7510 ['footer', 'aoFooter'], 7511 ].forEach(function (item) { 7512 _api_register( 'table().' + item[0] + '.structure()' , function (selector) { 7513 var indexes = this.columns(selector).indexes().flatten().toArray(); 7514 var ctx = this.context[0]; 7515 var structure = _fnHeaderLayout(ctx, ctx[item[1]], indexes); 7516 7517 // The structure is in column index order - but from this method we want the return to be 7518 // in the columns() selector API order. In order to do that we need to map from one form 7519 // to the other 7520 var orderedIndexes = indexes.slice().sort(function (a, b) { 7521 return a - b; 7522 }); 7523 7524 return structure.map(function (row) { 7525 return indexes.map(function (colIdx) { 7526 return row[orderedIndexes.indexOf(colIdx)]; 7527 }); 7528 }); 7529 }); 7530 }); 7531 7532 7533 _api_registerPlural( 'tables().containers()', 'table().container()' , function () { 7534 return this.iterator( 'table', function ( ctx ) { 7535 return ctx.nTableWrapper; 7536 }, 1 ); 7537 } ); 7538 7539 _api_register( 'tables().every()', function ( fn ) { 7540 var that = this; 7541 7542 return this.iterator('table', function (s, i) { 7543 fn.call(that.table(i), i); 7544 }); 7545 }); 7546 7547 _api_register( 'caption()', function ( value, side ) { 7548 var context = this.context; 7549 7550 // Getter - return existing node's content 7551 if ( value === undefined ) { 7552 var caption = context[0].captionNode; 7553 7554 return caption && context.length ? 7555 caption.innerHTML : 7556 null; 7557 } 7558 7559 return this.iterator( 'table', function ( ctx ) { 7560 var table = $(ctx.nTable); 7561 var caption = $(ctx.captionNode); 7562 var container = $(ctx.nTableWrapper); 7563 7564 // Create the node if it doesn't exist yet 7565 if ( ! caption.length ) { 7566 caption = $('<caption/>').html( value ); 7567 ctx.captionNode = caption[0]; 7568 7569 // If side isn't set, we need to insert into the document to let the 7570 // CSS decide so we can read it back, otherwise there is no way to 7571 // know if the CSS would put it top or bottom for scrolling
7572 if (! side) { 7573 table.prepend(caption); 7574 7575 side = caption.css('caption-side'); 7576 } 7577 } 7578 7579 caption.html( value ); 7580 7581 if ( side ) { 7582 caption.css( 'caption-side', side ); 7583 caption[0]._captionSide = side; 7584 } 7585 7586 if (container.find('div.dataTables_scroll').length) { 7587 var selector = (side === 'top' ? 'Head' : 'Foot'); 7588 7589 container.find('div.dataTables_scroll'+ selector +' table').prepend(caption); 7590 } 7591 else { 7592 table.prepend(caption); 7593 } 7594 }, 1 ); 7595 } ); 7596 7597 _api_register( 'caption.node()', function () { 7598 var ctx = this.context; 7599 7600 return ctx.length ? ctx[0].captionNode : null; 7601 } ); 7602 7603 7604 /** 7605 * Redraw the tables in the current context. 7606 */ 7607 _api_register( 'draw()', function ( paging ) { 7608 return this.iterator( 'table', function ( settings ) { 7609 if ( paging === 'page' ) { 7610 _fnDraw( settings ); 7611 } 7612 else { 7613 if ( typeof paging === 'string' ) { 7614 paging = paging === 'full-hold' ? 7615 false : 7616 true; 7617 } 7618 7619 _fnReDraw( settings, paging===false ); 7620 } 7621 } ); 7622 } ); 7623 7624 7625 7626 /** 7627 * Get the current page index. 7628 * 7629 * @return {integer} Current page index (zero based) 7630 *//** 7631 * Set the current page. 7632 * 7633 * Note that if you attempt to show a page which does not exist, DataTables will 7634 * not throw an error, but rather reset the paging. 7635 * 7636 * @param {integer|string} action The paging action to take. This can be one of: 7637 * * `integer` - The page index to jump to 7638 * * `string` - An action to take: 7639 * * `first` - Jump to first page. 7640 * * `next` - Jump to the next page 7641 * * `previous` - Jump to previous page 7642 * * `last` - Jump to the last page. 7643 * @returns {DataTables.Api} this 7644 */ 7645 _api_register( 'page()', function ( action ) { 7646 if ( action === undefined ) { 7647 return this.page.info().page; // not an expensive call 7648 } 7649 7650 // else, have an action to take on all tables 7651 return this.iterator( 'table', function ( settings ) { 7652 _fnPageChange( settings, action ); 7653 } ); 7654 } ); 7655 7656 7657 /** 7658 * Paging information for the first table in the current context. 7659 * 7660 * If you require paging information for another table, use the `table()` method 7661 * with a suitable selector. 7662 * 7663 * @return {object} Object with the following properties set: 7664 * * `page` - Current page index (zero based - i.e. the first page is `0`) 7665 * * `pages` - Total number of pages 7666 * * `start` - Display index for the first record shown on the current page 7667 * * `end` - Display index for the last record shown on the current page 7668 * * `length` - Display length (number of records). Note that generally `start 7669 * + length = end`, but this is not always true, for example if there are 7670 * only 2 records to show on the final page, with a length of 10. 7671 * * `recordsTotal` - Full data set length 7672 * * `recordsDisplay` - Data set length once the current filtering criterion 7673 * are applied. 7674 */ 7675 _api_register( 'page.info()', function () { 7676 if ( this.context.length === 0 ) { 7677 return undefined; 7678 } 7679 7680 var 7681 settings = this.context[0], 7682 start = settings._iDisplayStart, 7683 len = settings.oFeatures.bPaginate ? settings._iDisplayLength : -1, 7684 visRecords = settings.fnRecordsDisplay(), 7685 all = len === -1; 7686 7687 return { 7688 "page": all ? 0 : Math.floor( start / len ), 7689 "pages": all ? 1 : Math.ceil( visRecords / len ), 7690 "start": start, 7691 "end": settings.fnDisplayEnd(), 7692 "length": len, 7693 "recordsTotal": settings.fnRecordsTotal(), 7694 "recordsDisplay": visRecords, 7695 "serverSide": _fnDataSource( settings ) === 'ssp' 7696 }; 7697 } ); 7698 7699 7700 /** 7701 * Get the current page length. 7702 * 7703 * @return {integer} Current page length. Note `-1` indicates that all records 7704 * are to be shown. 7705 *//** 7706 * Set the current page length. 7707 * 7708 * @param {integer} Page length to set. Use `-1` to show all records. 7709 * @returns {DataTables.Api} this 7710 */ 7711 _api_register( 'page.len()', function ( len ) { 7712 // Note that we can't call this function 'length()' because `length` 7713 // is a Javascript property of functions which defines how many arguments 7714 // the function expects. 7715 if ( len === undefined ) { 7716 return this.context.length !== 0 ? 7717 this.context[0]._iDisplayLength : 7718 undefined; 7719 } 7720 7721 // else, set the page length 7722 return this.iterator( 'table', function ( settings ) { 7723 _fnLengthChange( settings, len ); 7724 } ); 7725 } ); 7726 7727 7728 7729 var __reload = function ( settings, holdPosition, callback ) { 7730 // Use the draw event to trigger a callback 7731 if ( callback ) { 7732 var api = new _Api( settings ); 7733 7734 api.one( 'draw', function () { 7735 callback( api.ajax.json() ); 7736 } ); 7737 } 7738 7739 if ( _fnDataSource( settings ) == 'ssp' ) { 7740 _fnReDraw( settings, holdPosition ); 7741 } 7742 else { 7743 _fnProcessingDisplay( settings, true ); 7744 7745 // Cancel an existing request 7746 var xhr = settings.jqXHR; 7747 if ( xhr && xhr.readyState !== 4 ) { 7748 xhr.abort(); 7749 } 7750 7751 // Trigger xhr 7752 _fnBuildAjax( settings, {}, function( json ) { 7753 _fnClearTable( settings ); 7754 7755 var data = _fnAjaxDataSrc( settings, json ); 7756 for ( var i=0, ien=data.length ; i<ien ; i++ ) { 7757 _fnAddData( settings, data[i] ); 7758 } 7759 7760 _fnReDraw( settings, holdPosition ); 7761 _fnInitComplete( settings ); 7762 _fnProcessingDisplay( settings, false ); 7763 } ); 7764 } 7765 }; 7766 7767 7768 /** 7769 * Get the JSON response from the last Ajax request that DataTables made to the 7770 * server. Note that this returns the JSON from the first table in the current 7771 * context. 7772 * 7773 * @return {object} JSON received from the server. 7774 */ 7775 _api_register( 'ajax.json()', function () { 7776 var ctx = this.context; 7777 7778 if ( ctx.length > 0 ) { 7779 return ctx[0].json; 7780 } 7781 7782 // else return undefined; 7783 } ); 7784 7785 7786 /** 7787 * Get the data submitted in the last Ajax request 7788 */ 7789 _api_register( 'ajax.params()', function () { 7790 var ctx = this.context; 7791 7792 if ( ctx.length > 0 ) { 7793 return ctx[0].oAjaxData; 7794 } 7795 7796 // else return undefined; 7797 } ); 7798 7799 7800 /** 7801 * Reload tables from the Ajax data source. Note that this function will 7802 * automatically re-draw the table when the remote data has been loaded. 7803 * 7804 * @param {boolean} [reset=true] Reset (default) or hold the current paging 7805 * position. A full re-sort and re-filter is performed when this method is 7806 * called, which is why the pagination reset is the default action. 7807 * @returns {DataTables.Api} this 7808 */ 7809 _api_register( 'ajax.reload()', function ( callback, resetPaging ) { 7810 return this.iterator( 'table', function (settings) { 7811 __reload( settings, resetPaging===false, callback ); 7812 } ); 7813 } ); 7814 7815 7816 /** 7817 * Get the current Ajax URL. Note that this returns the URL from the first 7818 * table in the current context. 7819 * 7820 * @return {string} Current Ajax source URL 7821 *//** 7822 * Set the Ajax URL. Note that this will set the URL for all tables in the 7823 * current context. 7824 * 7825 * @param {string} url URL to set. 7826 * @returns {DataTables.Api} this 7827 */ 7828 _api_register( 'ajax.url()', function ( url ) { 7829 var ctx = this.context; 7830 7831 if ( url === undefined ) { 7832 // get 7833 if ( ctx.length === 0 ) { 7834 return undefined; 7835 } 7836 ctx = ctx[0]; 7837
7838 return $.isPlainObject( ctx.ajax ) ? 7839 ctx.ajax.url : 7840 ctx.ajax; 7841 } 7842 7843 // set 7844 return this.iterator( 'table', function ( settings ) { 7845 if ( $.isPlainObject( settings.ajax ) ) { 7846 settings.ajax.url = url; 7847 } 7848 else { 7849 settings.ajax = url; 7850 } 7851 } ); 7852 } ); 7853 7854 7855 /** 7856 * Load data from the newly set Ajax URL. Note that this method is only 7857 * available when `ajax.url()` is used to set a URL. Additionally, this method 7858 * has the same effect as calling `ajax.reload()` but is provided for 7859 * convenience when setting a new URL. Like `ajax.reload()` it will 7860 * automatically redraw the table once the remote data has been loaded. 7861 * 7862 * @returns {DataTables.Api} this 7863 */ 7864 _api_register( 'ajax.url().load()', function ( callback, resetPaging ) { 7865 // Same as a reload, but makes sense to present it for easy access after a 7866 // url change 7867 return this.iterator( 'table', function ( ctx ) { 7868 __reload( ctx, resetPaging===false, callback ); 7869 } ); 7870 } ); 7871 7872 7873 7874 7875 var _selector_run = function ( type, selector, selectFn, settings, opts ) 7876 { 7877 var 7878 out = [], res, 7879 i, ien, 7880 selectorType = typeof selector; 7881 7882 // Can't just check for isArray here, as an API or jQuery instance might be 7883 // given with their array like look 7884 if ( ! selector || selectorType === 'string' || selectorType === 'function' || selector.length === undefined ) { 7885 selector = [ selector ]; 7886 } 7887 7888 for ( i=0, ien=selector.length ; i<ien ; i++ ) { 7889 res = selectFn( typeof selector[i] === 'string' ? selector[i].trim() : selector[i] ); 7890 7891 // Remove empty items 7892 res = res.filter( function (item) { 7893 return item !== null && item !== undefined; 7894 }); 7895 7896 if ( res && res.length ) { 7897 out = out.concat( res ); 7898 } 7899 } 7900 7901 // selector extensions 7902 var ext = _ext.selector[ type ]; 7903 if ( ext.length ) { 7904 for ( i=0, ien=ext.length ; i<ien ; i++ ) { 7905 out = ext[i]( settings, opts, out ); 7906 } 7907 } 7908 7909 return _unique( out ); 7910 }; 7911 7912 7913 var _selector_opts = function ( opts ) 7914 { 7915 if ( ! opts ) { 7916 opts = {}; 7917 } 7918 7919 // Backwards compatibility for 1.9- which used the terminology filter rather 7920 // than search 7921 if ( opts.filter && opts.search === undefined ) { 7922 opts.search = opts.filter; 7923 } 7924 7925 return $.extend( { 7926 columnOrder: 'implied', 7927 search: 'none', 7928 order: 'current', 7929 page: 'all' 7930 }, opts ); 7931 }; 7932 7933 7934 // Reduce the API instance to the first item found 7935 var _selector_first = function ( old ) 7936 { 7937 var inst = new _Api(old.context[0]); 7938 7939 // Use a push rather than passing to the constructor, since it will 7940 // merge arrays down automatically, which isn't what is wanted here 7941 if (old.length) { 7942 inst.push( old[0] ); 7943 } 7944 7945 inst.selector = old.selector; 7946 7947 // Limit to a single row / column / cell 7948 if (inst.length && inst[0].length > 1) { 7949 inst[0].splice(1); 7950 } 7951 7952 return inst; 7953 }; 7954 7955 7956 var _selector_row_indexes = function ( settings, opts ) 7957 { 7958 var 7959 i, ien, tmp, a=[], 7960 displayFiltered = settings.aiDisplay, 7961 displayMaster = settings.aiDisplayMaster; 7962 7963 var 7964 search = opts.search, // none, applied, removed 7965 order = opts.order, // applied, current, index (original - compatibility with 1.9) 7966 page = opts.page; // all, current 7967 7968 if ( _fnDataSource( settings ) == 'ssp' ) { 7969 // In server-side processing mode, most options are irrelevant since 7970 // rows not shown don't exist and the index order is the applied order 7971 // Removed is a special case - for consistency just return an empty 7972 // array 7973 return search === 'removed' ? 7974 [] : 7975 _range( 0, displayMaster.length ); 7976 } 7977 7978 if ( page == 'current' ) { 7979 // Current page implies that order=current and filter=applied, since it is 7980 // fairly senseless otherwise, regardless of what order and search actually 7981 // are 7982 for ( i=settings._iDisplayStart, ien=settings.fnDisplayEnd() ; i<ien ; i++ ) { 7983 a.push( displayFiltered[i] ); 7984 } 7985 } 7986 else if ( order == 'current' || order == 'applied' ) { 7987 if ( search == 'none') { 7988 a = displayMaster.slice(); 7989 } 7990 else if ( search == 'applied' ) { 7991 a = displayFiltered.slice(); 7992 } 7993 else if ( search == 'removed' ) { 7994 // O(n+m) solution by creating a hash map 7995 var displayFilteredMap = {}; 7996 7997 for ( i=0, ien=displayFiltered.length ; i<ien ; i++ ) { 7998 displayFilteredMap[displayFiltered[i]] = null; 7999 } 8000 8001 displayMaster.forEach(function (item) { 8002 if (! Object.prototype.hasOwnProperty.call(displayFilteredMap, item)) { 8003 a.push(item); 8004 } 8005 }); 8006 } 8007 } 8008 else if ( order == 'index' || order == 'original' ) { 8009 for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
8010 if (! settings.aoData[i]) { 8011 continue; 8012 } 8013 8014 if ( search == 'none' ) { 8015 a.push( i ); 8016 } 8017 else { // applied | removed 8018 tmp = displayFiltered.indexOf(i); 8019 8020 if ((tmp === -1 && search == 'removed') || 8021 (tmp >= 0 && search == 'applied') ) 8022 { 8023 a.push( i ); 8024 } 8025 } 8026 } 8027 } 8028 else if ( typeof order === 'number' ) { 8029 // Order the rows by the given column 8030 var ordered = _fnSort(settings, order, 'asc'); 8031 8032 if (search === 'none') { 8033 a = ordered; 8034 } 8035 else { // applied | removed 8036 for (i=0; i<ordered.length; i++) { 8037 tmp = displayFiltered.indexOf(ordered[i]); 8038 8039 if ((tmp === -1 && search == 'removed') || 8040 (tmp >= 0 && search == 'applied') ) 8041 { 8042 a.push( ordered[i] ); 8043 } 8044 } 8045 } 8046 } 8047 8048 return a; 8049 }; 8050 8051 8052 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 8053 * Rows 8054 * 8055 * {} - no selector - use all available rows 8056 * {integer} - row aoData index 8057 * {node} - TR node 8058 * {string} - jQuery selector to apply to the TR elements 8059 * {array} - jQuery array of nodes, or simply an array of TR nodes 8060 * 8061 */ 8062 var __row_selector = function ( settings, selector, opts ) 8063 { 8064 var rows; 8065 var run = function ( sel ) { 8066 var selInt = _intVal( sel ); 8067 var aoData = settings.aoData; 8068 8069 // Short cut - selector is a number and no options provided (default is 8070 // all records, so no need to check if the index is in there, since it 8071 // must be - dev error if the index doesn't exist). 8072 if ( selInt !== null && ! opts ) { 8073 return [ selInt ]; 8074 } 8075 8076 if ( ! rows ) { 8077 rows = _selector_row_indexes( settings, opts ); 8078 } 8079 8080 if ( selInt !== null && rows.indexOf(selInt) !== -1 ) { 8081 // Selector - integer 8082 return [ selInt ]; 8083 } 8084 else if ( sel === null || sel === undefined || sel === '' ) { 8085 // Selector - none 8086 return rows; 8087 } 8088 8089 // Selector - function 8090 if ( typeof sel === 'function' ) { 8091 return rows.map( function (idx) { 8092 var row = aoData[ idx ]; 8093 return sel( idx, row._aData, row.nTr ) ? idx : null; 8094 } ); 8095 } 8096 8097 // Selector - node 8098 if ( sel.nodeName ) { 8099 var rowIdx = sel._DT_RowIndex; // Property added by DT for fast lookup 8100 var cellIdx = sel._DT_CellIndex; 8101 8102 if ( rowIdx !== undefined ) { 8103 // Make sure that the row is actually still present in the table 8104 return aoData[ rowIdx ] && aoData[ rowIdx ].nTr === sel ? 8105 [ rowIdx ] : 8106 []; 8107 } 8108 else if ( cellIdx ) { 8109 return aoData[ cellIdx.row ] && aoData[ cellIdx.row ].nTr === sel.parentNode ? 8110 [ cellIdx.row ] : 8111 []; 8112 } 8113 else { 8114 var host = $(sel).closest('*[data-dt-row]'); 8115 return host.length ? 8116 [ host.data('dt-row') ] : 8117 []; 8118 } 8119 } 8120 8121 // ID selector. Want to always be able to select rows by id, regardless 8122 // of if the tr element has been created or not, so can't rely upon 8123 // jQuery here - hence a custom implementation. This does not match 8124 // Sizzle's fast selector or HTML4 - in HTML5 the ID can be anything, 8125 // but to select it using a CSS selector engine (like Sizzle or 8126 // querySelect) it would need to need to be escaped for some characters. 8127 // DataTables simplifies this for row selectors since you can select 8128 // only a row. A # indicates an id any anything that follows is the id - 8129 // unescaped. 8130 if ( typeof sel === 'string' && sel.charAt(0) === '#' ) { 8131 // get row index from id 8132 var rowObj = settings.aIds[ sel.replace( /^#/, '' ) ]; 8133 if ( rowObj !== undefined ) { 8134 return [ rowObj.idx ]; 8135 } 8136 8137 // need to fall through to jQuery in case there is DOM id that 8138 // matches 8139 } 8140 8141 // Get nodes in the order from the `rows` array with null values removed 8142 var nodes = _removeEmpty( 8143 _pluck_order( settings.aoData, rows, 'nTr' ) 8144 ); 8145 8146 // Selector - jQuery selector string, array of nodes or jQuery object/ 8147 // As jQuery's .filter() allows jQuery objects to be passed in filter, 8148 // it also allows arrays, so this will cope with all three options 8149 return $(nodes) 8150 .filter( sel ) 8151 .map( function () { 8152 return this._DT_RowIndex; 8153 } ) 8154 .toArray(); 8155 }; 8156 8157 var matched = _selector_run( 'row', selector, run, settings, opts ); 8158 8159 if (opts.order === 'current' || opts.order === 'applied') { 8160 _fnSortDisplay(settings, matched); 8161 } 8162 8163 return matched; 8164 }; 8165 8166 8167 _api_register( 'rows()', function ( selector, opts ) { 8168 // argument shifting 8169 if ( selector === undefined ) { 8170 selector = ''; 8171 } 8172 else if ( $.isPlainObject( selector ) ) { 8173 opts = selector; 8174 selector = ''; 8175 } 8176 8177 opts = _selector_opts( opts ); 8178 8179 var inst = this.iterator( 'table', function ( settings ) { 8180 return __row_selector( settings, selector, opts ); 8181 }, 1 ); 8182 8183 // Want argument shifting here and in __row_selector? 8184 inst.selector.rows = selector; 8185 inst.selector.opts = opts; 8186 8187 return inst; 8188 } ); 8189 8190 _api_register( 'rows().nodes()', function () { 8191 return this.iterator( 'row', function ( settings, row ) { 8192 return settings.aoData[ row ].nTr || undefined; 8193 }, 1 ); 8194 } ); 8195 8196 _api_register( 'rows().data()', function () { 8197 return this.iterator( true, 'rows', function ( settings, rows ) { 8198 return _pluck_order( settings.aoData, rows, '_aData' ); 8199 }, 1 ); 8200 } ); 8201 8202 _api_registerPlural( 'rows().cache()', 'row().cache()', function ( type ) { 8203 return this.iterator( 'row', function ( settings, row ) { 8204 var r = settings.aoData[ row ]; 8205 return type === 'search' ? r._aFilterData : r._aSortData; 8206 }, 1 ); 8207 } ); 8208 8209 _api_registerPlural( 'rows().invalidate()', 'row().invalidate()', function ( src ) { 8210 return this.iterator( 'row', function ( settings, row ) { 8211 _fnInvalidate( settings, row, src ); 8212 } ); 8213 } ); 8214 8215 _api_registerPlural( 'rows().indexes()', 'row().index()', function () { 8216 return this.iterator( 'row', function ( settings, row ) { 8217 return row; 8218 }, 1 ); 8219 } ); 8220 8221 _api_registerPlural( 'rows().ids()', 'row().id()', function ( hash ) { 8222 var a = []; 8223 var context = this.context; 8224 8225 // `iterator` will drop undefined values, but in this case we want them 8226 for ( var i=0, ien=context.length ; i<ien ; i++ ) { 8227 for ( var j=0, jen=this[i].length ; j<jen ; j++ ) { 8228 var id = context[i].rowIdFn( context[i].aoData[ this[i][j] ]._aData ); 8229 a.push( (hash === true ? '#' : '' )+ id ); 8230 } 8231 } 8232 8233 return new _Api( context, a ); 8234 } ); 8235 8236 _api_registerPlural( 'rows().remove()', 'row().remove()', function () { 8237 this.iterator( 'row', function ( settings, row ) { 8238 var data = settings.aoData; 8239 var rowData = data[ row ]; 8240 8241 // Delete from the display arrays 8242 var idx = settings.aiDisplayMaster.indexOf(row); 8243 if (idx !== -1) { 8244 settings.aiDisplayMaster.splice(idx, 1); 8245 } 8246 8247 // For server-side processing tables - subtract the deleted row from the count 8248 if ( settings._iRecordsDisplay > 0 ) { 8249 settings._iRecordsDisplay--; 8250 } 8251 8252 // Check for an 'overflow' they case for displaying the table 8253 _fnLengthOverflow( settings ); 8254 8255 // Remove the row's ID reference if there is one 8256 var id = settings.rowIdFn( rowData._aData ); 8257 if ( id !== undefined ) { 8258 delete settings.aIds[ id ]; 8259 } 8260 8261 data[row] = null; 8262 } ); 8263 8264 return this; 8265 } ); 8266 8267 8268 _api_register( 'rows.add()', function ( rows ) { 8269 var newRows = this.iterator( 'table', function ( settings ) { 8270 var row, i, ien; 8271 var out = []; 8272 8273 for ( i=0, ien=rows.length ; i<ien ; i++ ) {
8274 row = rows[i]; 8275 8276 if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) { 8277 out.push( _fnAddTr( settings, row )[0] ); 8278 } 8279 else { 8280 out.push( _fnAddData( settings, row ) ); 8281 } 8282 } 8283 8284 return out; 8285 }, 1 ); 8286 8287 // Return an Api.rows() extended instance, so rows().nodes() etc can be used 8288 var modRows = this.rows( -1 ); 8289 modRows.pop(); 8290 _fnArrayApply(modRows, newRows); 8291 8292 return modRows; 8293 } ); 8294 8295 8296 8297 8298 8299 /** 8300 * 8301 */ 8302 _api_register( 'row()', function ( selector, opts ) { 8303 return _selector_first( this.rows( selector, opts ) ); 8304 } ); 8305 8306 8307 _api_register( 'row().data()', function ( data ) { 8308 var ctx = this.context; 8309 8310 if ( data === undefined ) { 8311 // Get 8312 return ctx.length && this.length && this[0].length ? 8313 ctx[0].aoData[ this[0] ]._aData : 8314 undefined; 8315 } 8316 8317 // Set 8318 var row = ctx[0].aoData[ this[0] ]; 8319 row._aData = data; 8320 8321 // If the DOM has an id, and the data source is an array 8322 if ( Array.isArray( data ) && row.nTr && row.nTr.id ) { 8323 _fnSetObjectDataFn( ctx[0].rowId )( data, row.nTr.id ); 8324 } 8325 8326 // Automatically invalidate 8327 _fnInvalidate( ctx[0], this[0], 'data' ); 8328 8329 return this; 8330 } ); 8331 8332 8333 _api_register( 'row().node()', function () { 8334 var ctx = this.context; 8335 8336 if (ctx.length && this.length && this[0].length) { 8337 var row = ctx[0].aoData[ this[0] ]; 8338 8339 if (row && row.nTr) { 8340 return row.nTr; 8341 } 8342 } 8343 8344 return null; 8345 } ); 8346 8347 8348 _api_register( 'row.add()', function ( row ) { 8349 // Allow a jQuery object to be passed in - only a single row is added from 8350 // it though - the first element in the set 8351 if ( row instanceof $ && row.length ) { 8352 row = row[0]; 8353 } 8354 8355 var rows = this.iterator( 'table', function ( settings ) { 8356 if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) { 8357 return _fnAddTr( settings, row )[0]; 8358 } 8359 return _fnAddData( settings, row ); 8360 } ); 8361 8362 // Return an Api.rows() extended instance, with the newly added row selected 8363 return this.row( rows[0] ); 8364 } ); 8365 8366 8367 $(document).on('plugin-init.dt', function (e, context) { 8368 var api = new _Api( context ); 8369 8370 api.on( 'stateSaveParams.DT', function ( e, settings, d ) { 8371 // This could be more compact with the API, but it is a lot faster as a simple 8372 // internal loop 8373 var idFn = settings.rowIdFn; 8374 var rows = settings.aiDisplayMaster; 8375 var ids = []; 8376 8377 for (var i=0 ; i<rows.length ; i++) { 8378 var rowIdx = rows[i]; 8379 var data = settings.aoData[rowIdx]; 8380 8381 if (data._detailsShow) { 8382 ids.push( '#' + idFn(data._aData) ); 8383 } 8384 } 8385 8386 d.childRows = ids; 8387 }); 8388 8389 // For future state loads (e.g. with StateRestore) 8390 api.on( 'stateLoaded.DT', function (e, settings, state) { 8391 __details_state_load( api, state ); 8392 }); 8393 8394 // And the initial load state 8395 __details_state_load( api, api.state.loaded() ); 8396 }); 8397 8398 var __details_state_load = function (api, state) 8399 { 8400 if ( state && state.childRows ) { 8401 api 8402 .rows( state.childRows.map(function (id) { 8403 // Escape any `:` characters from the row id. Accounts for 8404 // already escaped characters. 8405 return id.replace(/([^:\\]*(?:\\.[^:\\]*)*):/g, "$1\\:"); 8406 }) ) 8407 .every( function () { 8408 _fnCallbackFire( api.settings()[0], null, 'requestChild', [ this ] ) 8409 }); 8410 } 8411 } 8412 8413 var __details_add = function ( ctx, row, data, klass ) 8414 { 8415 // Convert to array of TR elements 8416 var rows = []; 8417 var addRow = function ( r, k ) { 8418 // Recursion to allow for arrays of jQuery objects 8419 if ( Array.isArray( r ) || r instanceof $ ) { 8420 for ( var i=0, ien=r.length ; i<ien ; i++ ) { 8421 addRow( r[i], k ); 8422 } 8423 return; 8424 } 8425 8426 // If we get a TR element, then just add it directly - up to the dev 8427 // to add the correct number of columns etc 8428 if ( r.nodeName && r.nodeName.toLowerCase() === 'tr' ) { 8429 r.setAttribute( 'data-dt-row', row.idx ); 8430 rows.push( r ); 8431 } 8432 else { 8433 // Otherwise create a row with a wrapper 8434 var created = $('<tr><td></td></tr>') 8435 .attr( 'data-dt-row', row.idx ) 8436 .addClass( k ); 8437 8438 $('td', created) 8439 .addClass( k ) 8440 .html( r )[0].colSpan = _fnVisbleColumns( ctx ); 8441 8442 rows.push( created[0] ); 8443 } 8444 }; 8445 8446 addRow( data, klass ); 8447 8448 if ( row._details ) { 8449 row._details.detach(); 8450 } 8451 8452 row._details = $(rows); 8453 8454 // If the children were already shown, that state should be retained 8455 if ( row._detailsShow ) { 8456 row._details.insertAfter( row.nTr ); 8457 } 8458 }; 8459 8460 8461 // Make state saving of child row details async to allow them to be batch processed 8462 var __details_state = DataTable.util.throttle( 8463 function (ctx) { 8464 _fnSaveState( ctx[0] ) 8465 }, 8466 500 8467 ); 8468 8469 8470 var __details_remove = function ( api, idx ) 8471 { 8472 var ctx = api.context; 8473 8474 if ( ctx.length ) { 8475 var row = ctx[0].aoData[ idx !== undefined ? idx : api[0] ]; 8476 8477 if ( row && row._details ) { 8478 row._details.remove(); 8479 8480 row._detailsShow = undefined; 8481 row._details = undefined; 8482 $( row.nTr ).removeClass( 'dt-hasChild' ); 8483 __details_state( ctx ); 8484 } 8485 } 8486 }; 8487 8488
8489 var __details_display = function ( api, show ) { 8490 var ctx = api.context; 8491 8492 if ( ctx.length && api.length ) { 8493 var row = ctx[0].aoData[ api[0] ]; 8494 8495 if ( row._details ) { 8496 row._detailsShow = show; 8497 8498 if ( show ) { 8499 row._details.insertAfter( row.nTr ); 8500 $( row.nTr ).addClass( 'dt-hasChild' ); 8501 } 8502 else { 8503 row._details.detach(); 8504 $( row.nTr ).removeClass( 'dt-hasChild' ); 8505 } 8506 8507 _fnCallbackFire( ctx[0], null, 'childRow', [ show, api.row( api[0] ) ] ) 8508 8509 __details_events( ctx[0] ); 8510 __details_state( ctx ); 8511 } 8512 } 8513 }; 8514 8515 8516 var __details_events = function ( settings ) 8517 { 8518 var api = new _Api( settings ); 8519 var namespace = '.dt.DT_details'; 8520 var drawEvent = 'draw'+namespace; 8521 var colvisEvent = 'column-sizing'+namespace; 8522 var destroyEvent = 'destroy'+namespace; 8523 var data = settings.aoData; 8524 8525 api.off( drawEvent +' '+ colvisEvent +' '+ destroyEvent ); 8526 8527 if ( _pluck( data, '_details' ).length > 0 ) { 8528 // On each draw, insert the required elements into the document 8529 api.on( drawEvent, function ( e, ctx ) { 8530 if ( settings !== ctx ) { 8531 return; 8532 } 8533 8534 api.rows( {page:'current'} ).eq(0).each( function (idx) { 8535 // Internal data grab 8536 var row = data[ idx ]; 8537 8538 if ( row._detailsShow ) { 8539 row._details.insertAfter( row.nTr ); 8540 } 8541 } ); 8542 } ); 8543 8544 // Column visibility change - update the colspan 8545 api.on( colvisEvent, function ( e, ctx ) { 8546 if ( settings !== ctx ) { 8547 return; 8548 } 8549 8550 // Update the colspan for the details rows (note, only if it already has 8551 // a colspan) 8552 var row, visible = _fnVisbleColumns( ctx ); 8553 8554 for ( var i=0, ien=data.length ; i<ien ; i++ ) { 8555 row = data[i]; 8556 8557 if ( row && row._details ) { 8558 row._details.each(function () { 8559 var el = $(this).children('td'); 8560 8561 if (el.length == 1) { 8562 el.attr('colspan', visible); 8563 } 8564 }); 8565 } 8566 } 8567 } ); 8568 8569 // Table destroyed - nuke any child rows 8570 api.on( destroyEvent, function ( e, ctx ) { 8571 if ( settings !== ctx ) { 8572 return; 8573 } 8574 8575 for ( var i=0, ien=data.length ; i<ien ; i++ ) { 8576 if ( data[i] && data[i]._details ) { 8577 __details_remove( api, i ); 8578 } 8579 } 8580 } ); 8581 } 8582 }; 8583 8584 // Strings for the method names to help minification 8585 var _emp = ''; 8586 var _child_obj = _emp+'row().child'; 8587 var _child_mth = _child_obj+'()'; 8588 8589 // data can be: 8590 // tr 8591 // string 8592 // jQuery or array of any of the above 8593 _api_register( _child_mth, function ( data, klass ) { 8594 var ctx = this.context; 8595 8596 if ( data === undefined ) { 8597 // get 8598 return ctx.length && this.length && ctx[0].aoData[ this[0] ] 8599 ? ctx[0].aoData[ this[0] ]._details 8600 : undefined; 8601 } 8602 else if ( data === true ) { 8603 // show 8604 this.child.show(); 8605 } 8606 else if ( data === false ) { 8607 // remove 8608 __details_remove( this ); 8609 } 8610 else if ( ctx.length && this.length ) { 8611 // set 8612 __details_add( ctx[0], ctx[0].aoData[ this[0] ], data, klass ); 8613 } 8614 8615 return this; 8616 } ); 8617 8618 8619 _api_register( [ 8620 _child_obj+'.show()', 8621 _child_mth+'.show()' // only when `child()` was called with parameters (without 8622 ], function () { // it returns an object and this method is not executed) 8623 __details_display( this, true ); 8624 return this; 8625 } ); 8626 8627 8628 _api_register( [ 8629 _child_obj+'.hide()', 8630 _child_mth+'.hide()' // only when `child()` was called with parameters (without 8631 ], function () { // it returns an object and this method is not executed) 8632 __details_display( this, false ); 8633 return this; 8634 } ); 8635 8636 8637 _api_register( [ 8638 _child_obj+'.remove()', 8639 _child_mth+'.remove()' // only when `child()` was called with parameters (without 8640 ], function () { // it returns an object and this method is not executed) 8641 __details_remove( this ); 8642 return this; 8643 } ); 8644 8645 8646 _api_register( _child_obj+'.isShown()', function () { 8647 var ctx = this.context; 8648 8649 if ( ctx.length && this.length && ctx[0].aoData[ this[0] ] ) { 8650 // _detailsShown as false or undefined will fall through to return false 8651 return ctx[0].aoData[ this[0] ]._detailsShow || false; 8652 } 8653 return false;
8654 } ); 8655 8656 8657 8658 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 8659 * Columns 8660 * 8661 * {integer} - column index (>=0 count from left, <0 count from right) 8662 * "{integer}:visIdx" - visible column index (i.e. translate to column index) (>=0 count from left, <0 count from right) 8663 * "{integer}:visible" - alias for {integer}:visIdx (>=0 count from left, <0 count from right) 8664 * "{string}:name" - column name 8665 * "{string}" - jQuery selector on column header nodes 8666 * 8667 */ 8668 8669 // can be an array of these items, comma separated list, or an array of comma 8670 // separated lists 8671 8672 var __re_column_selector = /^([^:]+)?:(name|title|visIdx|visible)$/; 8673 8674 8675 // r1 and r2 are redundant - but it means that the parameters match for the 8676 // iterator callback in columns().data() 8677 var __columnData = function ( settings, column, r1, r2, rows, type ) { 8678 var a = []; 8679 for ( var row=0, ien=rows.length ; row<ien ; row++ ) { 8680 a.push( _fnGetCellData( settings, rows[row], column, type ) ); 8681 } 8682 return a; 8683 }; 8684 8685 8686 var __column_header = function ( settings, column, row ) { 8687 var header = settings.aoHeader; 8688 var titleRow = settings.titleRow; 8689 var target = null; 8690 8691 if (row !== undefined) { 8692 target = row; 8693 } 8694 else if (titleRow === true) { // legacy orderCellsTop support 8695 target = 0; 8696 } 8697 else if (titleRow === false) { 8698 target = header.length - 1; 8699 } 8700 else if (titleRow !== null) { 8701 target = titleRow; 8702 } 8703 else { 8704 // Automatic - find the _last_ unique cell from the top that is not empty (last for 8705 // backwards compatibility) 8706 for (var i=0 ; i<header.length ; i++) { 8707 if (header[i][column].unique && $('span.dt-column-title', header[i][column].cell).text()) { 8708 target = i; 8709 } 8710 } 8711 8712 if (target === null) { 8713 target = 0; 8714 } 8715 } 8716 8717 return header[target][column].cell; 8718 }; 8719 8720 var __column_header_cells = function (header) { 8721 var out = []; 8722 8723 for (var i=0 ; i<header.length ; i++) { 8724 for (var j=0 ; j<header[i].length ; j++) { 8725 var cell = header[i][j].cell; 8726 8727 if (!out.includes(cell)) { 8728 out.push(cell); 8729 } 8730 } 8731 } 8732 8733 return out; 8734 } 8735 8736 var __column_selector = function ( settings, selector, opts ) 8737 { 8738 var 8739 columns = settings.aoColumns, 8740 names, titles, 8741 nodes = __column_header_cells(settings.aoHeader); 8742 8743 var run = function ( s ) { 8744 var selInt = _intVal( s ); 8745 8746 // Selector - all 8747 if ( s === '' ) { 8748 return _range( columns.length ); 8749 } 8750 8751 // Selector - index 8752 if ( selInt !== null ) { 8753 return [ selInt >= 0 ? 8754 selInt : // Count from left 8755 columns.length + selInt // Count from right (+ because its a negative value) 8756 ]; 8757 } 8758 8759 // Selector = function 8760 if ( typeof s === 'function' ) { 8761 var rows = _selector_row_indexes( settings, opts ); 8762 8763 return columns.map(function (col, idx) { 8764 return s( 8765 idx, 8766 __columnData( settings, idx, 0, 0, rows ), 8767 __column_header( settings, idx ) 8768 ) ? idx : null; 8769 }); 8770 } 8771 8772 // jQuery or string selector 8773 var match = typeof s === 'string' ? 8774 s.match( __re_column_selector ) : 8775 ''; 8776 8777 if ( match ) { 8778 switch( match[2] ) { 8779 case 'visIdx': 8780 case 'visible': 8781 // Selector is a column index 8782 if (match[1] && match[1].match(/^\d+$/)) { 8783 var idx = parseInt( match[1], 10 ); 8784 8785 // Visible index given, convert to column index 8786 if ( idx < 0 ) { 8787 // Counting from the right 8788 var visColumns = columns.map( function (col,i) { 8789 return col.bVisible ? i : null; 8790 } ); 8791 return [ visColumns[ visColumns.length + idx ] ]; 8792 } 8793 // Counting from the left 8794 return [ _fnVisibleToColumnIndex( settings, idx ) ]; 8795 } 8796 8797 return columns.map( function (col, idx) { 8798 // Not visible, can't match 8799 if (! col.bVisible) { 8800 return null; 8801 } 8802 8803 // Selector 8804 if (match[1]) { 8805 return $(nodes[idx]).filter(match[1]).length > 0 ? idx : null; 8806 } 8807 8808 // `:visible` on its own 8809 return idx; 8810 } ); 8811 8812 case 'name': 8813 // Don't get names, unless needed, and only get once if it is 8814 if (!names) { 8815 names = _pluck( columns, 'sName' ); 8816 } 8817 8818 // match by name. `names` is column index complete and in order 8819 return names.map( function (name, i) { 8820 return name === match[1] ? i : null; 8821 } ); 8822 8823 case 'title': 8824 if (!titles) { 8825 titles = _pluck( columns, 'sTitle' ); 8826 } 8827 8828 // match by column title 8829 return titles.map( function (title, i) { 8830 return title === match[1] ? i : null; 8831 } ); 8832 8833 default: 8834 return []; 8835 } 8836 } 8837 8838 // Cell in the table body 8839 if ( s.nodeName && s._DT_CellIndex ) { 8840 return [ s._DT_CellIndex.column ]; 8841 } 8842 8843 // jQuery selector on the TH elements for the columns 8844 var jqResult = $( nodes ) 8845 .filter( s ) 8846 .map( function () { 8847 return _fnColumnsFromHeader( this ); // `nodes` is column index complete and in order 8848 } ) 8849 .toArray() 8850 .sort(function (a, b) { 8851 return a - b; 8852 }); 8853 8854 if ( jqResult.length || ! s.nodeName ) {
8855 return jqResult; 8856 } 8857 8858 // Otherwise a node which might have a `dt-column` data attribute, or be 8859 // a child or such an element 8860 var host = $(s).closest('*[data-dt-column]'); 8861 return host.length ? 8862 [ host.data('dt-column') ] : 8863 []; 8864 }; 8865 8866 var selected = _selector_run( 'column', selector, run, settings, opts ); 8867 8868 return opts.columnOrder && opts.columnOrder === 'index' 8869 ? selected.sort(function (a, b) { return a - b; }) 8870 : selected; // implied 8871 }; 8872 8873 8874 var __setColumnVis = function ( settings, column, vis ) { 8875 var 8876 cols = settings.aoColumns, 8877 col = cols[ column ], 8878 data = settings.aoData, 8879 cells, i, ien, tr; 8880 8881 // Get 8882 if ( vis === undefined ) { 8883 return col.bVisible; 8884 } 8885 8886 // Set 8887 // No change 8888 if ( col.bVisible === vis ) { 8889 return false; 8890 } 8891 8892 if ( vis ) { 8893 // Insert column 8894 // Need to decide if we should use appendChild or insertBefore 8895 var insertBefore = _pluck(cols, 'bVisible').indexOf(true, column+1); 8896 8897 for ( i=0, ien=data.length ; i<ien ; i++ ) { 8898 if (data[i]) { 8899 tr = data[i].nTr; 8900 cells = data[i].anCells; 8901 8902 if ( tr ) { 8903 // insertBefore can act like appendChild if 2nd arg is null 8904 tr.insertBefore( cells[ column ], cells[ insertBefore ] || null ); 8905 } 8906 } 8907 } 8908 } 8909 else { 8910 // Remove column 8911 $( _pluck( settings.aoData, 'anCells', column ) ).detach(); 8912 } 8913 8914 // Common actions 8915 col.bVisible = vis; 8916 8917 _colGroup(settings); 8918 8919 return true; 8920 }; 8921 8922 8923 _api_register( 'columns()', function ( selector, opts ) { 8924 // argument shifting 8925 if ( selector === undefined ) { 8926 selector = ''; 8927 } 8928 else if ( $.isPlainObject( selector ) ) { 8929 opts = selector; 8930 selector = ''; 8931 } 8932 8933 opts = _selector_opts( opts ); 8934 8935 var inst = this.iterator( 'table', function ( settings ) { 8936 return __column_selector( settings, selector, opts ); 8937 }, 1 ); 8938 8939 // Want argument shifting here and in _row_selector? 8940 inst.selector.cols = selector; 8941 inst.selector.opts = opts; 8942 8943 return inst; 8944 } ); 8945 8946 _api_registerPlural( 'columns().header()', 'column().header()', function ( row ) { 8947 return this.iterator( 'column', function (settings, column) { 8948 return __column_header(settings, column, row); 8949 }, 1 ); 8950 } ); 8951 8952 _api_registerPlural( 'columns().footer()', 'column().footer()', function ( row ) { 8953 return this.iterator( 'column', function ( settings, column ) { 8954 var footer = settings.aoFooter; 8955 8956 if (! footer.length) { 8957 return null; 8958 } 8959 8960 return settings.aoFooter[row !== undefined ? row : 0][column].cell; 8961 }, 1 ); 8962 } ); 8963 8964 _api_registerPlural( 'columns().data()', 'column().data()', function () { 8965 return this.iterator( 'column-rows', __columnData, 1 ); 8966 } ); 8967 8968 _api_registerPlural( 'columns().render()', 'column().render()', function ( type ) { 8969 return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) { 8970 return __columnData( settings, column, i, j, rows, type ); 8971 }, 1 ); 8972 } ); 8973 8974 _api_registerPlural( 'columns().dataSrc()', 'column().dataSrc()', function () { 8975 return this.iterator( 'column', function ( settings, column ) { 8976 return settings.aoColumns[column].mData; 8977 }, 1 ); 8978 } ); 8979 8980 _api_registerPlural( 'columns().cache()', 'column().cache()', function ( type ) { 8981 return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) { 8982 return _pluck_order( settings.aoData, rows, 8983 type === 'search' ? '_aFilterData' : '_aSortData', column 8984 ); 8985 }, 1 ); 8986 } ); 8987 8988 _api_registerPlural( 'columns().init()', 'column().init()', function () { 8989 return this.iterator( 'column', function ( settings, column ) { 8990 return settings.aoColumns[column]; 8991 }, 1 ); 8992 } ); 8993 8994 _api_registerPlural( 'columns().names()', 'column().name()', function () { 8995 return this.iterator( 'column', function ( settings, column ) { 8996 return settings.aoColumns[column].sName; 8997 }, 1 ); 8998 } ); 8999 9000 _api_registerPlural( 'columns().nodes()', 'column().nodes()', function () { 9001 return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) { 9002 return _pluck_order( settings.aoData, rows, 'anCells', column ) ; 9003 }, 1 ); 9004 } ); 9005 9006 _api_registerPlural( 'columns().titles()', 'column().title()', function (title, row) { 9007 return this.iterator( 'column', function ( settings, column ) { 9008 // Argument shifting 9009 if (typeof title === 'number') { 9010 row = title; 9011 title = undefined; 9012 } 9013 9014 var span = $('span.dt-column-title', this.column(column).header(row)); 9015 9016 if (title !== undefined) { 9017 span.html(title); 9018 return this; 9019 } 9020 9021 return span.html(); 9022 }, 1 ); 9023 } ); 9024 9025 _api_registerPlural( 'columns().types()', 'column().type()', function () { 9026 return this.iterator( 'column', function ( settings, column ) { 9027 var type = settings.aoColumns[column].sType; 9028
9029 // If the type was invalidated, then resolve it. This actually does 9030 // all columns at the moment. Would only happen once if getting all 9031 // column's data types. 9032 if (! type) { 9033 _fnColumnTypes(settings); 9034 } 9035 9036 return type; 9037 }, 1 ); 9038 } ); 9039 9040 _api_registerPlural( 'columns().visible()', 'column().visible()', function ( vis, calc ) { 9041 var that = this; 9042 var changed = []; 9043 var ret = this.iterator( 'column', function ( settings, column ) { 9044 if ( vis === undefined ) { 9045 return settings.aoColumns[ column ].bVisible; 9046 } // else 9047 9048 if (__setColumnVis( settings, column, vis )) { 9049 changed.push(column); 9050 } 9051 } ); 9052 9053 // Group the column visibility changes 9054 if ( vis !== undefined ) { 9055 this.iterator( 'table', function ( settings ) { 9056 // Redraw the header after changes 9057 _fnDrawHead( settings, settings.aoHeader ); 9058 _fnDrawHead( settings, settings.aoFooter ); 9059 9060 // Update colspan for no records display. Child rows and extensions will use their own 9061 // listeners to do this - only need to update the empty table item here 9062 if ( ! settings.aiDisplay.length ) { 9063 $(settings.nTBody).find('td[colspan]').attr('colspan', _fnVisbleColumns(settings)); 9064 } 9065 9066 _fnSaveState( settings ); 9067 9068 // Second loop once the first is done for events 9069 that.iterator( 'column', function ( settings, column ) { 9070 if (changed.includes(column)) { 9071 _fnCallbackFire( settings, null, 'column-visibility', [settings, column, vis, calc] ); 9072 } 9073 } ); 9074 9075 if ( changed.length && (calc === undefined || calc) ) { 9076 that.columns.adjust(); 9077 } 9078 }); 9079 } 9080 9081 return ret; 9082 } ); 9083 9084 _api_registerPlural( 'columns().widths()', 'column().width()', function () { 9085 // Injects a fake row into the table for just a moment so the widths can 9086 // be read, regardless of colspan in the header and rows being present in 9087 // the body 9088 var columns = this.columns(':visible').count(); 9089 var row = $('<tr>').html('<td>' + Array(columns).join('</td><td>') + '</td>'); 9090 9091 $(this.table().body()).append(row); 9092 9093 var widths = row.children().map(function () { 9094 return $(this).outerWidth(); 9095 }); 9096 9097 row.remove(); 9098 9099 return this.iterator( 'column', function ( settings, column ) { 9100 var visIdx = _fnColumnIndexToVisible( settings, column ); 9101 9102 return visIdx !== null ? widths[visIdx] : 0; 9103 }, 1); 9104 } ); 9105 9106 _api_registerPlural( 'columns().indexes()', 'column().index()', function ( type ) { 9107 return this.iterator( 'column', function ( settings, column ) { 9108 return type === 'visible' ? 9109 _fnColumnIndexToVisible( settings, column ) : 9110 column; 9111 }, 1 ); 9112 } ); 9113 9114 _api_register( 'columns.adjust()', function () { 9115 return this.iterator( 'table', function ( settings ) { 9116 // Force a column sizing to happen with a manual call - otherwise it can skip 9117 // if the size hasn't changed 9118 settings.containerWidth = -1; 9119 9120 _fnAdjustColumnSizing( settings ); 9121 }, 1 ); 9122 } ); 9123 9124 _api_register( 'column.index()', function ( type, idx ) { 9125 if ( this.context.length !== 0 ) { 9126 var ctx = this.context[0]; 9127 9128 if ( type === 'fromVisible' || type === 'toData' ) { 9129 return _fnVisibleToColumnIndex( ctx, idx ); 9130 } 9131 else if ( type === 'fromData' || type === 'toVisible' ) { 9132 return _fnColumnIndexToVisible( ctx, idx ); 9133 } 9134 } 9135 } ); 9136 9137 _api_register( 'column()', function ( selector, opts ) { 9138 return _selector_first( this.columns( selector, opts ) ); 9139 } ); 9140 9141 var __cell_selector = function ( settings, selector, opts ) 9142 { 9143 var data = settings.aoData; 9144 var rows = _selector_row_indexes( settings, opts ); 9145 var cells = _removeEmpty( _pluck_order( data, rows, 'anCells' ) ); 9146 var allCells = $(_flatten( [], cells )); 9147 var row; 9148 var columns = settings.aoColumns.length; 9149 var a, i, ien, j, o, host; 9150 9151 var run = function ( s ) { 9152 var fnSelector = typeof s === 'function'; 9153 9154 if ( s === null || s === undefined || fnSelector ) { 9155 // All cells and function selectors 9156 a = []; 9157 9158 for ( i=0, ien=rows.length ; i<ien ; i++ ) { 9159 row = rows[i]; 9160 9161 for ( j=0 ; j<columns ; j++ ) { 9162 o = { 9163 row: row, 9164 column: j 9165 }; 9166 9167 if ( fnSelector ) { 9168 // Selector - function 9169 host = data[ row ]; 9170 9171 if ( s( o, _fnGetCellData(settings, row, j), host.anCells ? host.anCells[j] : null ) ) { 9172 a.push( o ); 9173 } 9174 } 9175 else { 9176 // Selector - all 9177 a.push( o ); 9178 } 9179 } 9180 } 9181 9182 return a; 9183 } 9184 9185 // Selector - index 9186 if ( $.isPlainObject( s ) ) { 9187 // Valid cell index and its in the array of selectable rows 9188 return s.column !== undefined && s.row !== undefined && rows.indexOf(s.row) !== -1 ? 9189 [s] : 9190 []; 9191 } 9192 9193 // Selector - jQuery filtered cells 9194 var jqResult = allCells 9195 .filter( s ) 9196 .map( function (i, el) { 9197 return { // use a new object, in case someone changes the values 9198 row: el._DT_CellIndex.row, 9199 column: el._DT_CellIndex.column 9200 }; 9201 } ) 9202 .toArray(); 9203 9204 if ( jqResult.length || ! s.nodeName ) {
9205 return jqResult; 9206 } 9207 9208 // Otherwise the selector is a node, and there is one last option - the 9209 // element might be a child of an element which has dt-row and dt-column 9210 // data attributes 9211 host = $(s).closest('*[data-dt-row]'); 9212 return host.length ? 9213 [ { 9214 row: host.data('dt-row'), 9215 column: host.data('dt-column') 9216 } ] : 9217 []; 9218 }; 9219 9220 return _selector_run( 'cell', selector, run, settings, opts ); 9221 }; 9222 9223 9224 9225 9226 _api_register( 'cells()', function ( rowSelector, columnSelector, opts ) { 9227 // Argument shifting 9228 if ( $.isPlainObject( rowSelector ) ) { 9229 // Indexes 9230 if ( rowSelector.row === undefined ) { 9231 // Selector options in first parameter 9232 opts = rowSelector; 9233 rowSelector = null; 9234 } 9235 else { 9236 // Cell index objects in first parameter 9237 opts = columnSelector; 9238 columnSelector = null; 9239 } 9240 } 9241 if ( $.isPlainObject( columnSelector ) ) { 9242 opts = columnSelector; 9243 columnSelector = null; 9244 } 9245 9246 // Cell selector 9247 if ( columnSelector === null || columnSelector === undefined ) { 9248 return this.iterator( 'table', function ( settings ) { 9249 return __cell_selector( settings, rowSelector, _selector_opts( opts ) ); 9250 } ); 9251 } 9252 9253 // The default built in options need to apply to row and columns 9254 var internalOpts = opts ? { 9255 page: opts.page, 9256 order: opts.order, 9257 search: opts.search 9258 } : {}; 9259 9260 // Row + column selector 9261 var columns = this.columns( columnSelector, internalOpts ); 9262 var rows = this.rows( rowSelector, internalOpts ); 9263 var i, ien, j, jen; 9264 9265 var cellsNoOpts = this.iterator( 'table', function ( settings, idx ) { 9266 var a = []; 9267 9268 for ( i=0, ien=rows[idx].length ; i<ien ; i++ ) { 9269 for ( j=0, jen=columns[idx].length ; j<jen ; j++ ) { 9270 a.push( { 9271 row: rows[idx][i], 9272 column: columns[idx][j] 9273 } ); 9274 } 9275 } 9276 9277 return a; 9278 }, 1 ); 9279 9280 // There is currently only one extension which uses a cell selector extension 9281 // It is a _major_ performance drag to run this if it isn't needed, so this is 9282 // an extension specific check at the moment 9283 var cells = opts && opts.selected ? 9284 this.cells( cellsNoOpts, opts ) : 9285 cellsNoOpts; 9286 9287 $.extend( cells.selector, { 9288 cols: columnSelector, 9289 rows: rowSelector, 9290 opts: opts 9291 } ); 9292 9293 return cells; 9294 } ); 9295 9296 9297 _api_registerPlural( 'cells().nodes()', 'cell().node()', function () { 9298 return this.iterator( 'cell', function ( settings, row, column ) { 9299 var data = settings.aoData[ row ]; 9300 9301 return data && data.anCells ? 9302 data.anCells[ column ] : 9303 undefined; 9304 }, 1 ); 9305 } ); 9306 9307 9308 _api_register( 'cells().data()', function () { 9309 return this.iterator( 'cell', function ( settings, row, column ) { 9310 return _fnGetCellData( settings, row, column ); 9311 }, 1 ); 9312 } ); 9313 9314 9315 _api_registerPlural( 'cells().cache()', 'cell().cache()', function ( type ) { 9316 type = type === 'search' ? '_aFilterData' : '_aSortData'; 9317 9318 return this.iterator( 'cell', function ( settings, row, column ) { 9319 return settings.aoData[ row ][ type ][ column ]; 9320 }, 1 ); 9321 } ); 9322 9323 9324 _api_registerPlural( 'cells().render()', 'cell().render()', function ( type ) { 9325 return this.iterator( 'cell', function ( settings, row, column ) { 9326 return _fnGetCellData( settings, row, column, type ); 9327 }, 1 ); 9328 } ); 9329 9330 9331 _api_registerPlural( 'cells().indexes()', 'cell().index()', function () { 9332 return this.iterator( 'cell', function ( settings, row, column ) { 9333 return { 9334 row: row, 9335 column: column, 9336 columnVisible: _fnColumnIndexToVisible( settings, column ) 9337 }; 9338 }, 1 ); 9339 } ); 9340 9341 9342 _api_registerPlural( 'cells().invalidate()', 'cell().invalidate()', function ( src ) { 9343 return this.iterator( 'cell', function ( settings, row, column ) { 9344 _fnInvalidate( settings, row, src, column ); 9345 } ); 9346 } ); 9347 9348 9349 9350 _api_register( 'cell()', function ( rowSelector, columnSelector, opts ) { 9351 return _selector_first( this.cells( rowSelector, columnSelector, opts ) ); 9352 } ); 9353 9354 9355 _api_register( 'cell().data()', function ( data ) { 9356 var ctx = this.context; 9357 var cell = this[0]; 9358 9359 if ( data === undefined ) { 9360 // Get 9361 return ctx.length && cell.length ? 9362 _fnGetCellData( ctx[0], cell[0].row, cell[0].column ) : 9363 undefined; 9364 } 9365 9366 // Set 9367 _fnSetCellData( ctx[0], cell[0].row, cell[0].column, data ); 9368 _fnInvalidate( ctx[0], cell[0].row, 'data', cell[0].column ); 9369 9370 return this; 9371 } ); 9372 9373 9374 9375 /** 9376 * Get current ordering (sorting) that has been applied to the table. 9377 * 9378 * @returns {array} 2D array containing the sorting information for the first 9379 * table in the current context. Each element in the parent array represents 9380 * a column being sorted upon (i.e. multi-sorting with two columns would have 9381 * 2 inner arrays). The inner arrays may have 2 or 3 elements. The first is 9382 * the column index that the sorting condition applies to, the second is the 9383 * direction of the sort (`desc` or `asc`) and, optionally, the third is the 9384 * index of the sorting order from the `column.sorting` initialisation array.
9385 *//** 9386 * Set the ordering for the table. 9387 * 9388 * @param {integer} order Column index to sort upon. 9389 * @param {string} direction Direction of the sort to be applied (`asc` or `desc`) 9390 * @returns {DataTables.Api} this 9391 *//** 9392 * Set the ordering for the table. 9393 * 9394 * @param {array} order 1D array of sorting information to be applied. 9395 * @param {array} [...] Optional additional sorting conditions 9396 * @returns {DataTables.Api} this 9397 *//** 9398 * Set the ordering for the table. 9399 * 9400 * @param {array} order 2D array of sorting information to be applied. 9401 * @returns {DataTables.Api} this 9402 */ 9403 _api_register( 'order()', function ( order, dir ) { 9404 var ctx = this.context; 9405 var args = Array.prototype.slice.call( arguments ); 9406 9407 if ( order === undefined ) { 9408 // get 9409 return ctx.length !== 0 ? 9410 ctx[0].aaSorting : 9411 undefined; 9412 } 9413 9414 // set 9415 if ( typeof order === 'number' ) { 9416 // Simple column / direction passed in 9417 order = [ [ order, dir ] ]; 9418 } 9419 else if ( args.length > 1 ) { 9420 // Arguments passed in (list of 1D arrays) 9421 order = args; 9422 } 9423 // otherwise a 2D array was passed in 9424 9425 return this.iterator( 'table', function ( settings ) { 9426 var resolved = []; 9427 _fnSortResolve(settings, resolved, order); 9428 9429 settings.aaSorting = resolved; 9430 } ); 9431 } ); 9432 9433 9434 /** 9435 * Attach a sort listener to an element for a given column 9436 * 9437 * @param {node|jQuery|string} node Identifier for the element(s) to attach the 9438 * listener to. This can take the form of a single DOM node, a jQuery 9439 * collection of nodes or a jQuery selector which will identify the node(s). 9440 * @param {integer} column the column that a click on this node will sort on 9441 * @param {function} [callback] callback function when sort is run 9442 * @returns {DataTables.Api} this 9443 */ 9444 _api_register( 'order.listener()', function ( node, column, callback ) { 9445 return this.iterator( 'table', function ( settings ) { 9446 _fnSortAttachListener(settings, node, {}, column, callback); 9447 } ); 9448 } ); 9449 9450 9451 _api_register( 'order.fixed()', function ( set ) { 9452 if ( ! set ) { 9453 var ctx = this.context; 9454 var fixed = ctx.length ? 9455 ctx[0].aaSortingFixed : 9456 undefined; 9457 9458 return Array.isArray( fixed ) ? 9459 { pre: fixed } : 9460 fixed; 9461 } 9462 9463 return this.iterator( 'table', function ( settings ) { 9464 settings.aaSortingFixed = $.extend( true, {}, set ); 9465 } ); 9466 } ); 9467 9468 9469 // Order by the selected column(s) 9470 _api_register( [ 9471 'columns().order()', 9472 'column().order()' 9473 ], function ( dir ) { 9474 var that = this; 9475 9476 if ( ! dir ) { 9477 return this.iterator( 'column', function ( settings, idx ) { 9478 var sort = _fnSortFlatten( settings ); 9479 9480 for ( var i=0, ien=sort.length ; i<ien ; i++ ) { 9481 if ( sort[i].col === idx ) { 9482 return sort[i].dir; 9483 } 9484 } 9485 9486 return null; 9487 }, 1 ); 9488 } 9489 else { 9490 return this.iterator( 'table', function ( settings, i ) { 9491 settings.aaSorting = that[i].map( function (col) { 9492 return [ col, dir ]; 9493 } ); 9494 } ); 9495 } 9496 } ); 9497 9498 _api_registerPlural('columns().orderable()', 'column().orderable()', function ( directions ) { 9499 return this.iterator( 'column', function ( settings, idx ) { 9500 var col = settings.aoColumns[idx]; 9501 9502 return directions ? 9503 col.asSorting : 9504 col.bSortable; 9505 }, 1 ); 9506 } ); 9507 9508 9509 _api_register( 'processing()', function ( show ) { 9510 return this.iterator( 'table', function ( ctx ) { 9511 _fnProcessingDisplay( ctx, show ); 9512 } ); 9513 } ); 9514 9515 9516 _api_register( 'search()', function ( input, regex, smart, caseInsen ) { 9517 var ctx = this.context; 9518 9519 if ( input === undefined ) { 9520 // get 9521 return ctx.length !== 0 ? 9522 ctx[0].oPreviousSearch.search : 9523 undefined; 9524 } 9525 9526 // set 9527 return this.iterator( 'table', function ( settings ) { 9528 if ( ! settings.oFeatures.bFilter ) { 9529 return; 9530 } 9531 9532 if (typeof regex === 'object') { 9533 // New style options to pass to the search builder 9534 _fnFilterComplete( settings, $.extend( settings.oPreviousSearch, regex, { 9535 search: input 9536 } ) ); 9537 } 9538 else { 9539 // Compat for the old options 9540 _fnFilterComplete( settings, $.extend( settings.oPreviousSearch, { 9541 search: input, 9542 regex: regex === null ? false : regex,
9543 smart: smart === null ? true : smart, 9544 caseInsensitive: caseInsen === null ? true : caseInsen 9545 } ) ); 9546 } 9547 } ); 9548 } ); 9549 9550 _api_register( 'search.fixed()', function ( name, search ) { 9551 var ret = this.iterator( true, 'table', function ( settings ) { 9552 var fixed = settings.searchFixed; 9553 9554 if (! name) { 9555 return Object.keys(fixed); 9556 } 9557 else if (search === undefined) { 9558 return fixed[name]; 9559 } 9560 else if (search === null) { 9561 delete fixed[name]; 9562 } 9563 else { 9564 fixed[name] = search; 9565 } 9566 9567 return this; 9568 } ); 9569 9570 return name !== undefined && search === undefined 9571 ? ret[0] 9572 : ret; 9573 } ); 9574 9575 _api_registerPlural( 9576 'columns().search()', 9577 'column().search()', 9578 function ( input, regex, smart, caseInsen ) { 9579 return this.iterator( 'column', function ( settings, column ) { 9580 var preSearch = settings.aoPreSearchCols; 9581 9582 if ( input === undefined ) { 9583 // get 9584 return preSearch[ column ].search; 9585 } 9586 9587 // set 9588 if ( ! settings.oFeatures.bFilter ) { 9589 return; 9590 } 9591 9592 if (typeof regex === 'object') { 9593 // New style options to pass to the search builder 9594 $.extend( preSearch[ column ], regex, { 9595 search: input 9596 } ); 9597 } 9598 else { 9599 // Old style (with not all options available) 9600 $.extend( preSearch[ column ], { 9601 search: input, 9602 regex: regex === null ? false : regex, 9603 smart: smart === null ? true : smart, 9604 caseInsensitive: caseInsen === null ? true : caseInsen 9605 } ); 9606 } 9607 9608 _fnFilterComplete( settings, settings.oPreviousSearch ); 9609 } ); 9610 } 9611 ); 9612 9613 _api_register([ 9614 'columns().search.fixed()', 9615 'column().search.fixed()' 9616 ], 9617 function ( name, search ) { 9618 var ret = this.iterator( true, 'column', function ( settings, colIdx ) { 9619 var fixed = settings.aoColumns[colIdx].searchFixed; 9620 9621 if (! name) { 9622 return Object.keys(fixed); 9623 } 9624 else if (search === undefined) { 9625 return fixed[name] || null; 9626 } 9627 else if (search === null) { 9628 delete fixed[name]; 9629 } 9630 else { 9631 fixed[name] = search; 9632 } 9633 9634 return this; 9635 } ); 9636 9637 return name !== undefined && search === undefined 9638 ? ret[0] 9639 : ret; 9640 } 9641 ); 9642 /* 9643 * State API methods 9644 */ 9645 9646 _api_register( 'state()', function ( set, ignoreTime ) { 9647 // getter 9648 if ( ! set ) { 9649 return this.context.length ? 9650 this.context[0].oSavedState : 9651 null; 9652 } 9653 9654 var setMutate = $.extend( true, {}, set ); 9655 9656 // setter 9657 return this.iterator( 'table', function ( settings ) { 9658 if ( ignoreTime !== false ) { 9659 setMutate.time = +new Date() + 100; 9660 } 9661 9662 _fnImplementState( settings, setMutate, function(){} ); 9663 } ); 9664 } ); 9665 9666 9667 _api_register( 'state.clear()', function () { 9668 return this.iterator( 'table', function ( settings ) { 9669 // Save an empty object 9670 settings.fnStateSaveCallback.call( settings.oInstance, settings, {} ); 9671 } ); 9672 } ); 9673 9674 9675 _api_register( 'state.loaded()', function () { 9676 return this.context.length ? 9677 this.context[0].oLoadedState : 9678 null; 9679 } ); 9680 9681 9682 _api_register( 'state.save()', function () { 9683 return this.iterator( 'table', function ( settings ) { 9684 _fnSaveState( settings ); 9685 } ); 9686 } ); 9687 9688 // Can be assigned in DateTable.use() - note luxon and moment vars are in helpers.js 9689 var __bootstrap; 9690 var __foundation; 9691 9692 /** 9693 * Set the libraries that DataTables uses, or the global objects. 9694 * Note that the arguments can be either way around (legacy support) 9695 * and the second is optional. See docs. 9696 */ 9697 DataTable.use = function (arg1, arg2) { 9698 // Reverse arguments for legacy support 9699 var module = typeof arg1 === 'string' 9700 ? arg2 9701 : arg1; 9702 var type = typeof arg2 === 'string' 9703 ? arg2 9704 : arg1; 9705 9706 // Getter 9707 if (module === undefined && typeof type === 'string') { 9708 switch (type) { 9709 case 'lib': 9710 case 'jq': 9711 return $; 9712 9713 case 'win': 9714 return window; 9715 9716 case 'datetime': 9717 return DataTable.DateTime; 9718 9719 case 'luxon': 9720 return __luxon; 9721 9722 case 'moment': 9723 return __moment; 9724 9725 case 'bootstrap': 9726 // Use local if set, otherwise try window, which could be undefined 9727 return __bootstrap || window.bootstrap; 9728 9729 case 'foundation': 9730 // Ditto 9731 return __foundation || window.Foundation; 9732 9733 default: 9734 return null; 9735 } 9736 } 9737 9738 // Setter 9739 if (type === 'lib' || type === 'jq' || (module && module.fn && module.fn.jquery)) { 9740 $ = module; 9741 } 9742 else if (type === 'win' || (module && module.document)) { 9743 window = module; 9744 document = module.document; 9745 } 9746 else if (type === 'datetime' || (module && module.type === 'DateTime')) { 9747 DataTable.DateTime = module; 9748 } 9749 else if (type === 'luxon' || (module && module.FixedOffsetZone)) { 9750 __luxon = module; 9751 } 9752 else if (type === 'moment' || (module && module.isMoment)) { 9753 __moment = module; 9754 } 9755 else if (type === 'bootstrap' || (module && module.Modal && module.Modal.NAME === 'modal')) 9756 { 9757 // This is currently for BS5 only. BS3/4 attach to jQuery, so no need to use `.use()` 9758 __bootstrap = module; 9759 } 9760 else if (type === 'foundation' || (module && module.Reveal)) { 9761 __foundation = module; 9762 } 9763 } 9764 9765 /** 9766 * CommonJS factory function pass through. This will check if the arguments 9767 * given are a window object or a jQuery object. If so they are set 9768 * accordingly. 9769 * @param {*} root Window 9770 * @param {*} jq jQUery 9771 * @returns {boolean} Indicator 9772 */ 9773 DataTable.factory = function (root, jq) { 9774 var is = false;
9775 9776 // Test if the first parameter is a window object 9777 if (root && root.document) { 9778 window = root; 9779 document = root.document; 9780 } 9781 9782 // Test if the second parameter is a jQuery object 9783 if (jq && jq.fn && jq.fn.jquery) { 9784 $ = jq; 9785 is = true; 9786 } 9787 9788 return is; 9789 } 9790 9791 /** 9792 * Provide a common method for plug-ins to check the version of DataTables being 9793 * used, in order to ensure compatibility. 9794 * 9795 * @param {string} version Version string to check for, in the format "X.Y.Z". 9796 * Note that the formats "X" and "X.Y" are also acceptable. 9797 * @param {string} [version2=current DataTables version] As above, but optional. 9798 * If not given the current DataTables version will be used. 9799 * @returns {boolean} true if this version of DataTables is greater or equal to 9800 * the required version, or false if this version of DataTales is not 9801 * suitable 9802 * @static 9803 * @dtopt API-Static 9804 * 9805 * @example 9806 * alert( $.fn.dataTable.versionCheck( '1.9.0' ) ); 9807 */ 9808 DataTable.versionCheck = function( version, version2 ) 9809 { 9810 var aThis = version2 ? 9811 version2.split('.') : 9812 DataTable.version.split('.'); 9813 var aThat = version.split('.'); 9814 var iThis, iThat; 9815 9816 for ( var i=0, iLen=aThat.length ; i<iLen ; i++ ) { 9817 iThis = parseInt( aThis[i], 10 ) || 0; 9818 iThat = parseInt( aThat[i], 10 ) || 0; 9819 9820 // Parts are the same, keep comparing 9821 if (iThis === iThat) { 9822 continue; 9823 } 9824 9825 // Parts are different, return immediately 9826 return iThis > iThat; 9827 } 9828 9829 return true; 9830 }; 9831 9832 9833 /** 9834 * Check if a `<table>` node is a DataTable table already or not. 9835 * 9836 * @param {node|jquery|string} table Table node, jQuery object or jQuery 9837 * selector for the table to test. Note that if more than more than one 9838 * table is passed on, only the first will be checked 9839 * @returns {boolean} true the table given is a DataTable, or false otherwise 9840 * @static 9841 * @dtopt API-Static 9842 * 9843 * @example 9844 * if ( ! $.fn.DataTable.isDataTable( '#example' ) ) { 9845 * $('#example').dataTable(); 9846 * } 9847 */ 9848 DataTable.isDataTable = function ( table ) 9849 { 9850 var t = $(table).get(0); 9851 var is = false; 9852 9853 if ( table instanceof DataTable.Api ) { 9854 return true; 9855 } 9856 9857 $.each( DataTable.settings, function (i, o) { 9858 var head = o.nScrollHead ? $('table', o.nScrollHead)[0] : null; 9859 var foot = o.nScrollFoot ? $('table', o.nScrollFoot)[0] : null; 9860 9861 if ( o.nTable === t || head === t || foot === t ) { 9862 is = true; 9863 } 9864 } ); 9865 9866 return is; 9867 }; 9868 9869 9870 /** 9871 * Get all DataTable tables that have been initialised - optionally you can 9872 * select to get only currently visible tables. 9873 * 9874 * @param {boolean} [visible=false] Flag to indicate if you want all (default) 9875 * or visible tables only. 9876 * @returns {array} Array of `table` nodes (not DataTable instances) which are 9877 * DataTables 9878 * @static 9879 * @dtopt API-Static 9880 * 9881 * @example 9882 * $.each( $.fn.dataTable.tables(true), function () { 9883 * $(table).DataTable().columns.adjust(); 9884 * } ); 9885 */ 9886 DataTable.tables = function ( visible ) 9887 { 9888 var api = false; 9889 9890 if ( $.isPlainObject( visible ) ) { 9891 api = visible.api; 9892 visible = visible.visible; 9893 } 9894 9895 var a = DataTable.settings 9896 .filter( function (o) { 9897 return !visible || (visible && $(o.nTable).is(':visible')) 9898 ? true 9899 : false; 9900 } ) 9901 .map( function (o) { 9902 return o.nTable; 9903 }); 9904 9905 return api ? 9906 new _Api( a ) : 9907 a; 9908 }; 9909 9910 9911 /** 9912 * Convert from camel case parameters to Hungarian notation. This is made public 9913 * for the extensions to provide the same ability as DataTables core to accept 9914 * either the 1.9 style Hungarian notation, or the 1.10+ style camelCase 9915 * parameters. 9916 * 9917 * @param {object} src The model object which holds all parameters that can be 9918 * mapped. 9919 * @param {object} user The object to convert from camel case to Hungarian. 9920 * @param {boolean} force When set to `true`, properties which already have a 9921 * Hungarian value in the `user` object will be overwritten. Otherwise they 9922 * won't be. 9923 */ 9924 DataTable.camelToHungarian = _fnCamelToHungarian; 9925 9926 9927 9928 /** 9929 * 9930 */ 9931 _api_register( '$()', function ( selector, opts ) { 9932 var 9933 rows = this.rows( opts ).nodes(), // Get all rows 9934 jqRows = $(rows); 9935 9936 return $( [].concat( 9937 jqRows.filter( selector ).toArray(), 9938 jqRows.find( selector ).toArray() 9939 ) ); 9940 } ); 9941 9942 9943 // jQuery functions to operate on the tables 9944 $.each( [ 'on', 'one', 'off' ], function (i, key) { 9945 _api_register( key+'()', function ( /* event, handler */ ) { 9946 var args = Array.prototype.slice.call(arguments); 9947 9948 // Add the `dt` namespace automatically if it isn't already present 9949 args[0] = args[0].split( /\s/ ).map( function ( e ) { 9950 return ! e.match(/\.dt\b/) ? 9951 e+'.dt' : 9952 e; 9953 } ).join( ' ' ); 9954 9955 var inst = $( this.tables().nodes() ); 9956 inst[key].apply( inst, args ); 9957 return this; 9958 } ); 9959 } ); 9960 9961 9962 _api_register( 'clear()', function () { 9963 return this.iterator( 'table', function ( settings ) { 9964 _fnClearTable( settings ); 9965 } ); 9966 } ); 9967 9968 9969 _api_register( 'error()', function (msg) { 9970 return this.iterator( 'table', function ( settings ) { 9971 _fnLog( settings, 0, msg ); 9972 } ); 9973 } ); 9974 9975 9976 _api_register( 'settings()', function () { 9977 return new _Api( this.context, this.context ); 9978 } ); 9979 9980 9981 _api_register( 'init()', function () { 9982 var ctx = this.context; 9983 return ctx.length ? ctx[0].oInit : null; 9984 } ); 9985 9986 9987 _api_register( 'data()', function () { 9988 return this.iterator( 'table', function ( settings ) { 9989 return _pluck( settings.aoData, '_aData' ); 9990 } ).flatten(); 9991 } ); 9992 9993 9994 _api_register( 'trigger()', function ( name, args, bubbles ) { 9995 return this.iterator( 'table', function ( settings ) { 9996 return _fnCallbackFire( settings, null, name, args, bubbles ); 9997 } ).flatten(); 9998 } ); 9999 10000 10001 _api_register( 'ready()', function ( fn ) { 10002 var ctx = this.context; 10003 10004 // Get status of first table 10005 if (! fn) { 10006 return ctx.length 10007 ? (ctx[0]._bInitComplete || false) 10008 : null; 10009 } 10010 10011 // Function to run either once the table becomes ready or 10012 // immediately if it is already ready. 10013 return this.tables().every(function () { 10014 var api = this; 10015 10016 if (this.context[0]._bInitComplete) { 10017 fn.call(api); 10018 } 10019 else { 10020 this.on('init.dt.DT', function () { 10021 fn.call(api); 10022 }); 10023 } 10024 } ); 10025 } ); 10026 10027 10028 _api_register( 'destroy()', function ( remove ) { 10029 remove = remove || false; 10030 10031 return this.iterator( 'table', function ( settings ) { 10032 var classes = settings.oClasses; 10033 var table = settings.nTable; 10034 var tbody = settings.nTBody; 10035 var thead = settings.nTHead; 10036 var tfoot = settings.nTFoot; 10037 var jqTable = $(table); 10038 var jqTbody = $(tbody); 10039 var jqWrapper = $(settings.nTableWrapper); 10040 var rows = settings.aoData.map( function (r) { return r ? r.nTr : null; } ); 10041 var orderClasses = classes.order;
10042 10043 // Flag to note that the table is currently being destroyed - no action 10044 // should be taken 10045 settings.bDestroying = true; 10046 10047 // Fire off the destroy callbacks for plug-ins etc 10048 _fnCallbackFire( settings, "aoDestroyCallback", "destroy", [settings], true ); 10049 10050 // If not being removed from the document, make all columns visible 10051 if ( ! remove ) { 10052 new _Api( settings ).columns().visible( true ); 10053 } 10054 10055 // Container width change listener 10056 if (settings.resizeObserver) { 10057 settings.resizeObserver.disconnect(); 10058 } 10059 10060 // Blitz all `DT` namespaced events (these are internal events, the 10061 // lowercase, `dt` events are user subscribed and they are responsible 10062 // for removing them 10063 jqWrapper.off('.DT').find(':not(tbody *)').off('.DT'); 10064 $(window).off('.DT-'+settings.sInstance); 10065 10066 // When scrolling we had to break the table up - restore it 10067 if ( table != thead.parentNode ) { 10068 jqTable.children('thead').detach(); 10069 jqTable.append( thead ); 10070 } 10071 10072 if ( tfoot && table != tfoot.parentNode ) { 10073 jqTable.children('tfoot').detach(); 10074 jqTable.append( tfoot ); 10075 } 10076 10077 // Clean up the header / footer 10078 cleanHeader(thead, 'header'); 10079 cleanHeader(tfoot, 'footer'); 10080 settings.colgroup.remove(); 10081 10082 settings.aaSorting = []; 10083 settings.aaSortingFixed = []; 10084 _fnSortingClasses( settings ); 10085 10086 $(jqTable).find('th, td').removeClass( 10087 $.map(DataTable.ext.type.className, function (v) { 10088 return v; 10089 }).join(' ') 10090 ); 10091 10092 $('th, td', thead) 10093 .removeClass( 10094 orderClasses.none + ' ' + 10095 orderClasses.canAsc + ' ' + 10096 orderClasses.canDesc + ' ' + 10097 orderClasses.isAsc + ' ' + 10098 orderClasses.isDesc 10099 ) 10100 .css('width', '') 10101 .removeAttr('aria-sort'); 10102 10103 // Add the TR elements back into the table in their original order 10104 jqTbody.children().detach(); 10105 jqTbody.append( rows ); 10106 10107 var orig = settings.nTableWrapper.parentNode; 10108 var insertBefore = settings.nTableWrapper.nextSibling; 10109 10110 // Remove the DataTables generated nodes, events and classes 10111 var removedMethod = remove ? 'remove' : 'detach'; 10112 jqTable[ removedMethod ](); 10113 jqWrapper[ removedMethod ](); 10114 10115 // If we need to reattach the table to the document 10116 if ( ! remove && orig ) { 10117 // insertBefore acts like appendChild if !arg[1] 10118 orig.insertBefore( table, insertBefore ); 10119 10120 // Restore the width of the original table - was read from the style property, 10121 // so we can restore directly to that 10122 jqTable 10123 .css( 'width', settings.sDestroyWidth ) 10124 .removeClass( classes.table ); 10125 } 10126 10127 /* Remove the settings object from the settings array */ 10128 var idx = DataTable.settings.indexOf(settings); 10129 if ( idx !== -1 ) { 10130 DataTable.settings.splice( idx, 1 ); 10131 } 10132 } ); 10133 } ); 10134 10135 10136 // Add the `every()` method for rows, columns and cells in a compact form 10137 $.each( [ 'column', 'row', 'cell' ], function ( i, type ) { 10138 _api_register( type+'s().every()', function ( fn ) { 10139 var opts = this.selector.opts; 10140 var api = this; 10141 var inst; 10142 var counter = 0; 10143 10144 return this.iterator( 'every', function ( settings, selectedIdx, tableIdx ) { 10145 inst = api[ type ](selectedIdx, opts); 10146 10147 if (type === 'cell') { 10148 fn.call(inst, inst[0][0].row, inst[0][0].column, tableIdx, counter); 10149 } 10150 else { 10151 fn.call(inst, selectedIdx, tableIdx, counter); 10152 } 10153 10154 counter++; 10155 } ); 10156 } ); 10157 } ); 10158 10159 10160 // i18n method for extensions to be able to use the language object from the 10161 // DataTable 10162 _api_register( 'i18n()', function ( token, def, plural ) { 10163 var ctx = this.context[0]; 10164 var resolved = _fnGetObjectDataFn( token )( ctx.oLanguage ); 10165 10166 if ( resolved === undefined ) { 10167 resolved = def; 10168 } 10169 10170 if ( $.isPlainObject( resolved ) ) { 10171 resolved = plural !== undefined && resolved[ plural ] !== undefined ? 10172 resolved[ plural ] : 10173 resolved._; 10174 } 10175 10176 return typeof resolved === 'string' 10177 ? resolved.replace( '%d', plural ) // nb: plural might be undefined, 10178 : resolved; 10179 } ); 10180 10181 // Needed for header and footer, so pulled into its own function 10182 function cleanHeader(node, className) { 10183 $(node).find('span.dt-column-order').remove(); 10184 $(node).find('span.dt-column-title').each(function () { 10185 var title = $(this).html(); 10186 $(this).parent().parent().append(title); 10187 $(this).remove(); 10188 }); 10189 $(node).find('div.dt-column-' + className).remove(); 10190 10191 $('th, td', node).removeAttr('data-dt-column'); 10192 } 10193 10194 /**
10195 * Version string for plug-ins to check compatibility. Allowed format is 10196 * `a.b.c-d` where: a:int, b:int, c:int, d:string(dev|beta|alpha). `d` is used 10197 * only for non-release builds. See https://semver.org/ for more information. 10198 * @member 10199 * @type string 10200 * @default Version number 10201 */ 10202 DataTable.version = "2.3.1"; 10203 10204 /** 10205 * Private data store, containing all of the settings objects that are 10206 * created for the tables on a given page. 10207 * 10208 * Note that the `DataTable.settings` object is aliased to 10209 * `jQuery.fn.dataTableExt` through which it may be accessed and 10210 * manipulated, or `jQuery.fn.dataTable.settings`. 10211 * @member 10212 * @type array 10213 * @default [] 10214 * @private 10215 */ 10216 DataTable.settings = []; 10217 10218 /** 10219 * Object models container, for the various models that DataTables has 10220 * available to it. These models define the objects that are used to hold 10221 * the active state and configuration of the table. 10222 * @namespace 10223 */ 10224 DataTable.models = {}; 10225 10226 10227 10228 /** 10229 * Template object for the way in which DataTables holds information about 10230 * search information for the global filter and individual column filters. 10231 * @namespace 10232 */ 10233 DataTable.models.oSearch = { 10234 /** 10235 * Flag to indicate if the filtering should be case insensitive or not 10236 */ 10237 "caseInsensitive": true, 10238 10239 /** 10240 * Applied search term 10241 */ 10242 "search": "", 10243 10244 /** 10245 * Flag to indicate if the search term should be interpreted as a 10246 * regular expression (true) or not (false) and therefore and special 10247 * regex characters escaped. 10248 */ 10249 "regex": false, 10250 10251 /** 10252 * Flag to indicate if DataTables is to use its smart filtering or not. 10253 */ 10254 "smart": true, 10255 10256 /** 10257 * Flag to indicate if DataTables should only trigger a search when 10258 * the return key is pressed. 10259 */ 10260 "return": false 10261 }; 10262 10263 10264 10265 10266 /** 10267 * Template object for the way in which DataTables holds information about 10268 * each individual row. This is the object format used for the settings 10269 * aoData array. 10270 * @namespace 10271 */ 10272 DataTable.models.oRow = { 10273 /** 10274 * TR element for the row 10275 */ 10276 "nTr": null, 10277 10278 /** 10279 * Array of TD elements for each row. This is null until the row has been 10280 * created. 10281 */ 10282 "anCells": null, 10283 10284 /** 10285 * Data object from the original data source for the row. This is either 10286 * an array if using the traditional form of DataTables, or an object if 10287 * using mData options. The exact type will depend on the passed in 10288 * data from the data source, or will be an array if using DOM a data 10289 * source. 10290 */ 10291 "_aData": [], 10292 10293 /** 10294 * Sorting data cache - this array is ostensibly the same length as the 10295 * number of columns (although each index is generated only as it is 10296 * needed), and holds the data that is used for sorting each column in the 10297 * row. We do this cache generation at the start of the sort in order that 10298 * the formatting of the sort data need be done only once for each cell 10299 * per sort. This array should not be read from or written to by anything 10300 * other than the master sorting methods. 10301 */ 10302 "_aSortData": null, 10303 10304 /** 10305 * Per cell filtering data cache. As per the sort data cache, used to 10306 * increase the performance of the filtering in DataTables 10307 */ 10308 "_aFilterData": null, 10309 10310 /** 10311 * Filtering data cache. This is the same as the cell filtering cache, but 10312 * in this case a string rather than an array. This is easily computed with 10313 * a join on `_aFilterData`, but is provided as a cache so the join isn't 10314 * needed on every search (memory traded for performance) 10315 */ 10316 "_sFilterRow": null, 10317 10318 /** 10319 * Denote if the original data source was from the DOM, or the data source 10320 * object. This is used for invalidating data, so DataTables can 10321 * automatically read data from the original source, unless uninstructed 10322 * otherwise. 10323 */ 10324 "src": null, 10325 10326 /** 10327 * Index in the aoData array. This saves an indexOf lookup when we have the 10328 * object, but want to know the index 10329 */ 10330 "idx": -1, 10331 10332 /** 10333 * Cached display value 10334 */ 10335 displayData: null 10336 }; 10337 10338 10339 /** 10340 * Template object for the column information object in DataTables. This object 10341 * is held in the settings aoColumns array and contains all the information that 10342 * DataTables needs about each individual column. 10343 * 10344 * Note that this object is related to {@link DataTable.defaults.column} 10345 * but this one is the internal data store for DataTables's cache of columns. 10346 * It should NOT be manipulated outside of DataTables. Any configuration should 10347 * be done through the initialisation options. 10348 * @namespace 10349 */ 10350 DataTable.models.oColumn = { 10351 /** 10352 * Column index. 10353 */ 10354 "idx": null, 10355 10356 /** 10357 * A list of the columns that sorting should occur on when this column 10358 * is sorted. That this property is an array allows multi-column s
10358orting 10359 * to be defined for a column (for example first name / last name columns 10360 * would benefit from this). The values are integers pointing to the 10361 * columns to be sorted on (typically it will be a single integer pointing 10362 * at itself, but that doesn't need to be the case). 10363 */ 10364 "aDataSort": null, 10365 10366 /** 10367 * Define the sorting directions that are applied to the column, in sequence 10368 * as the column is repeatedly sorted upon - i.e. the first value is used 10369 * as the sorting direction when the column if first sorted (clicked on). 10370 * Sort it again (click again) and it will move on to the next index. 10371 * Repeat until loop. 10372 */ 10373 "asSorting": null, 10374 10375 /** 10376 * Flag to indicate if the column is searchable, and thus should be included 10377 * in the filtering or not. 10378 */ 10379 "bSearchable": null, 10380 10381 /** 10382 * Flag to indicate if the column is sortable or not. 10383 */ 10384 "bSortable": null, 10385 10386 /** 10387 * Flag to indicate if the column is currently visible in the table or not 10388 */ 10389 "bVisible": null, 10390 10391 /** 10392 * Store for manual type assignment using the `column.type` option. This 10393 * is held in store so we can manipulate the column's `sType` property. 10394 */ 10395 "_sManualType": null, 10396 10397 /** 10398 * Flag to indicate if HTML5 data attributes should be used as the data 10399 * source for filtering or sorting. True is either are. 10400 */ 10401 "_bAttrSrc": false, 10402 10403 /** 10404 * Developer definable function that is called whenever a cell is created (Ajax source, 10405 * etc) or processed for input (DOM source). This can be used as a compliment to mRender 10406 * allowing you to modify the DOM element (add background colour for example) when the 10407 * element is available. 10408 */ 10409 "fnCreatedCell": null, 10410 10411 /** 10412 * Function to get data from a cell in a column. You should <b>never</b> 10413 * access data directly through _aData internally in DataTables - always use 10414 * the method attached to this property. It allows mData to function as 10415 * required. This function is automatically assigned by the column 10416 * initialisation method 10417 */ 10418 "fnGetData": null, 10419 10420 /** 10421 * Function to set data for a cell in the column. You should <b>never</b> 10422 * set the data directly to _aData internally in DataTables - always use 10423 * this method. It allows mData to function as required. This function 10424 * is automatically assigned by the column initialisation method 10425 */ 10426 "fnSetData": null, 10427 10428 /** 10429 * Property to read the value for the cells in the column from the data 10430 * source array / object. If null, then the default content is used, if a 10431 * function is given then the return from the function is used. 10432 */ 10433 "mData": null, 10434 10435 /** 10436 * Partner property to mData which is used (only when defined) to get 10437 * the data - i.e. it is basically the same as mData, but without the 10438 * 'set' option, and also the data fed to it is the result from mData. 10439 * This is the rendering method to match the data method of mData. 10440 */ 10441 "mRender": null, 10442 10443 /** 10444 * The class to apply to all TD elements in the table's TBODY for the column 10445 */ 10446 "sClass": null, 10447 10448 /** 10449 * When DataTables calculates the column widths to assign to each column, 10450 * it finds the longest string in each column and then constructs a 10451 * temporary table and reads the widths from that. The problem with this 10452 * is that "mmm" is much wider then "iiii", but the latter is a longer 10453 * string - thus the calculation can go wrong (doing it properly and putting 10454 * it into an DOM object and measuring that is horribly(!) slow). Thus as 10455 * a "work around" we provide this option. It will append its value to the 10456 * text that is found to be the longest string for the column - i.e. padding. 10457 */ 10458 "sContentPadding": null, 10459 10460 /** 10461 * Allows a default value to be given for a column's data, and will be used 10462 * whenever a null data source is encountered (this can be because mData 10463 * is set to null, or because the data source itself is null). 10464 */ 10465 "sDefaultContent": null, 10466 10467 /** 10468 * Name for the column, allowing reference to the column by name as well as 10469 * by index (needs a lookup to work by name). 10470 */ 10471 "sName": null, 10472 10473 /** 10474 * Custom sorting data type - defines which of the available plug-ins in 10475 * afnSortData the custom sorting will use - if any is defined. 10476 */ 10477 "sSortDataType": 'std', 10478 10479 /** 10480 * Class to be applied to the header element when sorting on this column 10481 */ 10482 "sSortingClass": null, 10483 10484 /** 10485 * Title of the column - what is seen in the TH element (nTh). 10486 */ 10487 "sTitle": null, 10488 10489 /** 10490 * Column sorting and filtering type 10491 */ 10492 "sType": null, 10493 10494 /** 10495 * Width of the column 10496 */ 10497 "sWidth": null, 10498 10499 /** 10500 * Width of the column when it was first "encountered" 10501 */ 10502 "sWidthOrig": null, 10503 10504 /** Cached string which is the longest in the column */ 10505 maxLenString: null, 10506 10507 /** 10508 * Store for named searches 10509 */ 10510 searchFixed: null 10511 }; 10512 10513 10514 /* 10515 * Developer note: The properties of the object below are given in Hungarian 10516 * notation, that was used as the interface for DataTables prior to v1.10, however 10517 * from v1.10 onwards the primary interface is camel case. In order to avoid 10518 * breaking backwards compatibility utterly with this change, the Hungarian
10519 * version is still, internally the primary interface, but is is not documented 10520 * - hence the @name tags in each doc comment. This allows a Javascript function 10521 * to create a map from Hungarian notation to camel case (going the other direction 10522 * would require each property to be listed, which would add around 3K to the size 10523 * of DataTables, while this method is about a 0.5K hit). 10524 * 10525 * Ultimately this does pave the way for Hungarian notation to be dropped 10526 * completely, but that is a massive amount of work and will break current 10527 * installs (therefore is on-hold until v2). 10528 */ 10529 10530 /** 10531 * Initialisation options that can be given to DataTables at initialisation 10532 * time. 10533 * @namespace 10534 */ 10535 DataTable.defaults = { 10536 /** 10537 * An array of data to use for the table, passed in at initialisation which 10538 * will be used in preference to any data which is already in the DOM. This is 10539 * particularly useful for constructing tables purely in Javascript, for 10540 * example with a custom Ajax call. 10541 */ 10542 "aaData": null, 10543 10544 10545 /** 10546 * If ordering is enabled, then DataTables will perform a first pass sort on 10547 * initialisation. You can define which column(s) the sort is performed 10548 * upon, and the sorting direction, with this variable. The `sorting` array 10549 * should contain an array for each column to be sorted initially containing 10550 * the column's index and a direction string ('asc' or 'desc'). 10551 */ 10552 "aaSorting": [[0,'asc']], 10553 10554 10555 /** 10556 * This parameter is basically identical to the `sorting` parameter, but 10557 * cannot be overridden by user interaction with the table. What this means 10558 * is that you could have a column (visible or hidden) which the sorting 10559 * will always be forced on first - any sorting after that (from the user) 10560 * will then be performed as required. This can be useful for grouping rows 10561 * together. 10562 */ 10563 "aaSortingFixed": [], 10564 10565 10566 /** 10567 * DataTables can be instructed to load data to display in the table from a 10568 * Ajax source. This option defines how that Ajax call is made and where to. 10569 * 10570 * The `ajax` property has three different modes of operation, depending on 10571 * how it is defined. These are: 10572 * 10573 * * `string` - Set the URL from where the data should be loaded from. 10574 * * `object` - Define properties for `jQuery.ajax`. 10575 * * `function` - Custom data get function 10576 * 10577 * `string` 10578 * -------- 10579 * 10580 * As a string, the `ajax` property simply defines the URL from which 10581 * DataTables will load data. 10582 * 10583 * `object` 10584 * -------- 10585 * 10586 * As an object, the parameters in the object are passed to 10587 * [jQuery.ajax](https://api.jquery.com/jQuery.ajax/) allowing fine control 10588 * of the Ajax request. DataTables has a number of default parameters which 10589 * you can override using this option. Please refer to the jQuery 10590 * documentation for a full description of the options available, although 10591 * the following parameters provide additional options in DataTables or 10592 * require special consideration: 10593 * 10594 * * `data` - As with jQuery, `data` can be provided as an object, but it 10595 * can also be used as a function to manipulate the data DataTables sends 10596 * to the server. The function takes a single parameter, an object of 10597 * parameters with the values that DataTables has readied for sending. An 10598 * object may be returned which will be merged into the DataTables 10599 * defaults, or you can add the items to the object that was passed in and 10600 * not return anything from the function. This supersedes `fnServerParams` 10601 * from DataTables 1.9-. 10602 * 10603 * * `dataSrc` - By default DataTables will look for the property `data` (or 10604 * `aaData` for compatibility with DataTables 1.9-) when obtaining data 10605 * from an Ajax source or for server-side processing - this parameter 10606 * allows that property to be changed. You can use Javascript dotted 10607 * object notation to get a data source for multiple levels of nesting, or 10608 * it my be used as a function. As a function it takes a single parameter, 10609 * the JSON returned from the server, which can be manipulated as 10610 * required, with the returned value being that used by DataTables as the 10611 * data source for the table. 10612 * 10613 * * `success` - Should not be overridden it is used internally in 10614 * DataTables. To manipulate / transform the data returned by the server 10615 * use `ajax.dataSrc`, or use `ajax` as a function (see below). 10616 * 10617 * `function` 10618 * ---------- 10619 * 10620 * As a function, making the Ajax call is left up to yourself allowing 10621 * complete control of the Ajax request. Indeed, if desired, a method other 10622 * than Ajax could be used to obtain the required data, such as Web storage 10623 * or an AIR database. 10624 * 10625 * The function is given four parameters and no return is required. The 10626 * parameters are: 10627 * 10628 * 1. _object_ - Data to send to the server 10629 * 2. _function_ - Callback function that must be executed when the required 10630 * data has been obtained. That data should be passed into the callback 10631 * as the only parameter 10632 * 3. _object_ - DataTables settings object for the table 10633 */ 10634 "ajax": null, 10635 10636 10637 /** 10638 * This parameter allows you to readily specify the entries in the length drop 10639 * down menu that DataTables shows when pagination is enabled. It can be 10640 * either a 1D array of options which will be used for both the displayed 10641 * option and the value, or a 2D array which will use the array in the first 10642 * position as the value, and the array in the second position as the 10643 * displayed options (useful for language strings such as 'All'). 10644 * 10645 * Note that the `pageLength` property will be automatically set to the 10646 * first value given in this array, unless `pageLength` is also provided. 10647 */ 10648 "aLengthMenu": [ 10, 25, 50, 100 ], 10649 10650 10651 /** 10652 * The `columns` option in the initialisation parameter allows you to define 10653 * details about the way individual columns behave. For a full list of 10654 * column options that can be set, please see 10655 * {@link DataTable.defaults.column}. Note that if you use `columns` to 10656 * define your columns, you must have an entry in the array for every single 10657 * column that you have in your table (these can be null if you don't which 10658 * to specify any options). 10659 */ 10660 "aoColumns": null, 10661 10662 /**
10663 * Very similar to `columns`, `columnDefs` allows you to target a specific 10664 * column, multiple columns, or all columns, using the `targets` property of 10665 * each object in the array. This allows great flexibility when creating 10666 * tables, as the `columnDefs` arrays can be of any length, targeting the 10667 * columns you specifically want. `columnDefs` may use any of the column 10668 * options available: {@link DataTable.defaults.column}, but it _must_ 10669 * have `targets` defined in each object in the array. Values in the `targets` 10670 * array may be: 10671 * <ul> 10672 * <li>a string - class name will be matched on the TH for the column</li> 10673 * <li>0 or a positive integer - column index counting from the left</li> 10674 * <li>a negative integer - column index counting from the right</li> 10675 * <li>the string "_all" - all columns (i.e. assign a default)</li> 10676 * </ul> 10677 */ 10678 "aoColumnDefs": null, 10679 10680 10681 /** 10682 * Basically the same as `search`, this parameter defines the individual column 10683 * filtering state at initialisation time. The array must be of the same size 10684 * as the number of columns, and each element be an object with the parameters 10685 * `search` and `escapeRegex` (the latter is optional). 'null' is also 10686 * accepted and the default will be used. 10687 */ 10688 "aoSearchCols": [], 10689 10690 10691 /** 10692 * Enable or disable automatic column width calculation. This can be disabled 10693 * as an optimisation (it takes some time to calculate the widths) if the 10694 * tables widths are passed in using `columns`. 10695 */ 10696 "bAutoWidth": true, 10697 10698 10699 /** 10700 * Deferred rendering can provide DataTables with a huge speed boost when you 10701 * are using an Ajax or JS data source for the table. This option, when set to 10702 * true, will cause DataTables to defer the creation of the table elements for 10703 * each row until they are needed for a draw - saving a significant amount of 10704 * time. 10705 */ 10706 "bDeferRender": true, 10707 10708 10709 /** 10710 * Replace a DataTable which matches the given selector and replace it with 10711 * one which has the properties of the new initialisation object passed. If no 10712 * table matches the selector, then the new DataTable will be constructed as 10713 * per normal. 10714 */ 10715 "bDestroy": false, 10716 10717 10718 /** 10719 * Enable or disable filtering of data. Filtering in DataTables is "smart" in 10720 * that it allows the end user to input multiple words (space separated) and 10721 * will match a row containing those words, even if not in the order that was 10722 * specified (this allow matching across multiple columns). Note that if you 10723 * wish to use filtering in DataTables this must remain 'true' - to remove the 10724 * default filtering input box and retain filtering abilities, please use 10725 * {@link DataTable.defaults.dom}. 10726 */ 10727 "bFilter": true, 10728 10729 /** 10730 * Used only for compatiblity with DT1 10731 * @deprecated 10732 */ 10733 "bInfo": true, 10734 10735 /** 10736 * Used only for compatiblity with DT1 10737 * @deprecated 10738 */ 10739 "bLengthChange": true, 10740 10741 /** 10742 * Enable or disable pagination. 10743 */ 10744 "bPaginate": true, 10745 10746 10747 /** 10748 * Enable or disable the display of a 'processing' indicator when the table is 10749 * being processed (e.g. a sort). This is particularly useful for tables with 10750 * large amounts of data where it can take a noticeable amount of time to sort 10751 * the entries. 10752 */ 10753 "bProcessing": false, 10754 10755 10756 /** 10757 * Retrieve the DataTables object for the given selector. Note that if the 10758 * table has already been initialised, this parameter will cause DataTables 10759 * to simply return the object that has already been set up - it will not take 10760 * account of any changes you might have made to the initialisation object 10761 * passed to DataTables (setting this parameter to true is an acknowledgement 10762 * that you understand this). `destroy` can be used to reinitialise a table if 10763 * you need. 10764 */ 10765 "bRetrieve": false, 10766 10767 10768 /** 10769 * When vertical (y) scrolling is enabled, DataTables will force the height of 10770 * the table's viewport to the given height at all times (useful for layout). 10771 * However, this can look odd when filtering data down to a small data set, 10772 * and the footer is left "floating" further down. This parameter (when 10773 * enabled) will cause DataTables to collapse the table's viewport down when 10774 * the result set will fit within the given Y height. 10775 */
10776 "bScrollCollapse": false, 10777 10778 10779 /** 10780 * Configure DataTables to use server-side processing. Note that the 10781 * `ajax` parameter must also be given in order to give DataTables a 10782 * source to obtain the required data for each draw. 10783 */ 10784 "bServerSide": false, 10785 10786 10787 /** 10788 * Enable or disable sorting of columns. Sorting of individual columns can be 10789 * disabled by the `sortable` option for each column. 10790 */ 10791 "bSort": true, 10792 10793 10794 /** 10795 * Enable or display DataTables' ability to sort multiple columns at the 10796 * same time (activated by shift-click by the user). 10797 */ 10798 "bSortMulti": true, 10799 10800 10801 /** 10802 * Allows control over whether DataTables should use the top (true) unique 10803 * cell that is found for a single column, or the bottom (false - default). 10804 * This is useful when using complex headers. 10805 */ 10806 "bSortCellsTop": null, 10807 10808 10809 /** Specify which row is the title row in the header. Replacement for bSortCellsTop */ 10810 titleRow: null, 10811 10812 10813 /** 10814 * Enable or disable the addition of the classes `sorting\_1`, `sorting\_2` and 10815 * `sorting\_3` to the columns which are currently being sorted on. This is 10816 * presented as a feature switch as it can increase processing time (while 10817 * classes are removed and added) so for large data sets you might want to 10818 * turn this off. 10819 */ 10820 "bSortClasses": true, 10821 10822 10823 /** 10824 * Enable or disable state saving. When enabled HTML5 `localStorage` will be 10825 * used to save table display information such as pagination information, 10826 * display length, filtering and sorting. As such when the end user reloads 10827 * the page the display display will match what thy had previously set up. 10828 */ 10829 "bStateSave": false, 10830 10831 10832 /** 10833 * This function is called when a TR element is created (and all TD child 10834 * elements have been inserted), or registered if using a DOM source, allowing 10835 * manipulation of the TR element (adding classes etc). 10836 */ 10837 "fnCreatedRow": null, 10838 10839 10840 /** 10841 * This function is called on every 'draw' event, and allows you to 10842 * dynamically modify any aspect you want about the created DOM. 10843 */ 10844 "fnDrawCallback": null, 10845 10846 10847 /** 10848 * Identical to fnHeaderCallback() but for the table footer this function 10849 * allows you to modify the table footer on every 'draw' event. 10850 */ 10851 "fnFooterCallback": null, 10852 10853 10854 /** 10855 * When rendering large numbers in the information element for the table 10856 * (i.e. "Showing 1 to 10 of 57 entries") DataTables will render large numbers 10857 * to have a comma separator for the 'thousands' units (e.g. 1 million is 10858 * rendered as "1,000,000") to help readability for the end user. This 10859 * function will override the default method DataTables uses. 10860 */ 10861 "fnFormatNumber": function ( toFormat ) { 10862 return toFormat.toString().replace( 10863 /\B(?=(\d{3})+(?!\d))/g, 10864 this.oLanguage.sThousands 10865 ); 10866 }, 10867 10868 10869 /** 10870 * This function is called on every 'draw' event, and allows you to 10871 * dynamically modify the header row. This can be used to calculate and 10872 * display useful information about the table. 10873 */ 10874 "fnHeaderCallback": null, 10875 10876 10877 /** 10878 * The information element can be used to convey information about the current 10879 * state of the table. Although the internationalisation options presented by 10880 * DataTables are quite capable of dealing with most customisations, there may 10881 * be times where you wish to customise the string further. This callback 10882 * allows you to do exactly that. 10883 */ 10884 "fnInfoCallback": null, 10885 10886 10887 /** 10888 * Called when the table has been initialised. Normally DataTables will 10889 * initialise sequentially and there will be no need for this function, 10890 * however, this does not hold true when using external language information 10891 * since that is obtained using an async XHR call. 10892 */ 10893 "fnInitComplete": null, 10894 10895 10896 /** 10897 * Called at the very start of each table draw and can be used to cancel the 10898 * draw by returning false, any other return (including undefined) results in 10899 * the full draw occurring). 10900 */ 10901 "fnPreDrawCallback": null, 10902 10903 10904 /** 10905 * This function allows you to 'post process' each row after it have been 10906 * generated for each table draw, but before it is rendered on screen. This 10907 * function might be used for setting the row class name etc. 10908 */ 10909 "fnRowCallback": null, 10910 10911 10912 /** 10913 * Load the table state. With this function you can define from where, and how, the 10914 * state of a table is loaded. By default DataTables will load from `localStorage` 10915 * but you might wish to use a server-side database or cookies. 10916 */ 10917 "fnStateLoadCallback": function ( settings ) { 10918 try { 10919 return JSON.parse(
10920 (settings.iStateDuration === -1 ? sessionStorage : localStorage).getItem( 10921 'DataTables_'+settings.sInstance+'_'+location.pathname 10922 ) 10923 ); 10924 } catch (e) { 10925 return {}; 10926 } 10927 }, 10928 10929 10930 /** 10931 * Callback which allows modification of the saved state prior to loading that state. 10932 * This callback is called when the table is loading state from the stored data, but 10933 * prior to the settings object being modified by the saved state. Note that for 10934 * plug-in authors, you should use the `stateLoadParams` event to load parameters for 10935 * a plug-in. 10936 */ 10937 "fnStateLoadParams": null, 10938 10939 10940 /** 10941 * Callback that is called when the state has been loaded from the state saving method 10942 * and the DataTables settings object has been modified as a result of the loaded state. 10943 */ 10944 "fnStateLoaded": null, 10945 10946 10947 /** 10948 * Save the table state. This function allows you to define where and how the state 10949 * information for the table is stored By default DataTables will use `localStorage` 10950 * but you might wish to use a server-side database or cookies. 10951 */ 10952 "fnStateSaveCallback": function ( settings, data ) { 10953 try { 10954 (settings.iStateDuration === -1 ? sessionStorage : localStorage).setItem( 10955 'DataTables_'+settings.sInstance+'_'+location.pathname, 10956 JSON.stringify( data ) 10957 ); 10958 } catch (e) { 10959 // noop 10960 } 10961 }, 10962 10963 10964 /** 10965 * Callback which allows modification of the state to be saved. Called when the table 10966 * has changed state a new state save is required. This method allows modification of 10967 * the state saving object prior to actually doing the save, including addition or 10968 * other state properties or modification. Note that for plug-in authors, you should 10969 * use the `stateSaveParams` event to save parameters for a plug-in. 10970 */ 10971 "fnStateSaveParams": null, 10972 10973 10974 /** 10975 * Duration for which the saved state information is considered valid. After this period 10976 * has elapsed the state will be returned to the default. 10977 * Value is given in seconds. 10978 */ 10979 "iStateDuration": 7200, 10980 10981 10982 /** 10983 * Number of rows to display on a single page when using pagination. If 10984 * feature enabled (`lengthChange`) then the end user will be able to override 10985 * this to a custom setting using a pop-up menu. 10986 */ 10987 "iDisplayLength": 10, 10988 10989 10990 /** 10991 * Define the starting point for data display when using DataTables with 10992 * pagination. Note that this parameter is the number of records, rather than 10993 * the page number, so if you have 10 records per page and want to start on 10994 * the third page, it should be "20". 10995 */ 10996 "iDisplayStart": 0, 10997 10998 10999 /** 11000 * By default DataTables allows keyboard navigation of the table (sorting, paging, 11001 * and filtering) by adding a `tabindex` attribute to the required elements. This 11002 * allows you to tab through the controls and press the enter key to activate them. 11003 * The tabindex is default 0, meaning that the tab follows the flow of the document. 11004 * You can overrule this using this parameter if you wish. Use a value of -1 to 11005 * disable built-in keyboard navigation. 11006 */ 11007 "iTabIndex": 0, 11008 11009 11010 /** 11011 * Classes that DataTables assigns to the various components and features 11012 * that it adds to the HTML table. This allows classes to be configured 11013 * during initialisation in addition to through the static 11014 * {@link DataTable.ext.oStdClasses} object). 11015 */ 11016 "oClasses": {}, 11017 11018 11019 /** 11020 * All strings that DataTables uses in the user interface that it creates 11021 * are defined in this object, allowing you to modified them individually or 11022 * completely replace them all as required. 11023 */ 11024 "oLanguage": { 11025 /** 11026 * Strings that are used for WAI-ARIA labels and controls only (these are not 11027 * actually visible on the page, but will be read by screenreaders, and thus 11028 * must be internationalised as well). 11029 */ 11030 "oAria": { 11031 /** 11032 * ARIA label that is added to the table headers when the column may be sorted 11033 */ 11034 "orderable": ": Activate to sort", 11035 11036 /** 11037 * ARIA label that is added to the table headers when the column is currently being sorted 11038 */ 11039 "orderableReverse": ": Activate to invert sorting", 11040 11041 /** 11042 * ARIA label that is added to the table headers when the column is currently being 11043 * sorted and next step is to remove sorting 11044 */ 11045 "orderableRemove": ": Activate to remove sorting", 11046 11047 paginate: { 11048 first: 'First', 11049 last: 'Last', 11050 next: 'Next', 11051 previous: 'Previous', 11052 number: '' 11053 } 11054 }, 11055 11056 /** 11057 * Pagination string used by DataTables for the built-in pagination 11058 * control types. 11059 */ 11060 "oPaginate": { 11061 /** 11062 * Label and character for first page button («) 11063 */ 11064 "sFirst": "\u00AB", 11065 11066 /** 11067 * Last page button (») 11068 */ 11069 "sLast": "\u00BB", 11070 11071 /** 11072 * Next page button (âº) 11073 */ 11074 "sNext": "\u203A", 11075 11076 /** 11077 * Previous page button (â¹) 11078 */ 11079 "sPrevious": "\u2039", 11080 }, 11081 11082 /** 11083 * Plural object for the data type the table is showing 11084 */ 11085 entries: { 11086 _: "entries", 11087 1: "entry" 11088 }, 11089 11090 /** 11091 * Page length options 11092 */ 11093 lengthLabels: { 11094 '-1': 'All' 11095 }, 11096 11097 /** 11098 * This string is shown in preference to `zeroRecords` when the table is 11099 * empty of data (regardless of filtering). Note that this is an optional 11100 * parameter - if it is not given, the value of `zeroRecords` will be used 11101 * instead (either the default or given value). 11102 */ 11103 "sEmptyTable": "No data available in table", 11104 11105 11106 /** 11107 * This string gives information to the end user about the information 11108 * that is current on display on the page. The following tokens can be 11109 * used in the string and will be dynamically replaced as the table 11110 * display updates. This tokens can be placed anywhere in the string, or 11111 * removed as needed by the language requires: 11112 * 11113 * * `\_START\_` - Display index of the first record on the current page 11114 * * `\_END\_` - Display index of the last record on the current page 11115 * * `\_TOTAL\_` - Number of records in the table after filtering 11116 * * `\_MAX\_` - Number of records in the table without filtering 11117 * * `\_PAGE\_` - Current page number 11118 * * `\_PAGES\_` - Total number of pages of data in the table 11119 */ 11120 "sInfo": "Showing _START_ to _END_ of _TOTAL_ _ENTRIES-TOTAL_", 11121 11122 11123 /**
11124 * Display information string for when the table is empty. Typically the 11125 * format of this string should match `info`. 11126 */ 11127 "sInfoEmpty": "Showing 0 to 0 of 0 _ENTRIES-TOTAL_", 11128 11129 11130 /** 11131 * When a user filters the information in a table, this string is appended 11132 * to the information (`info`) to give an idea of how strong the filtering 11133 * is. The variable _MAX_ is dynamically updated. 11134 */ 11135 "sInfoFiltered": "(filtered from _MAX_ total _ENTRIES-MAX_)", 11136 11137 11138 /** 11139 * If can be useful to append extra information to the info string at times, 11140 * and this variable does exactly that. This information will be appended to 11141 * the `info` (`infoEmpty` and `infoFiltered` in whatever combination they are 11142 * being used) at all times. 11143 */ 11144 "sInfoPostFix": "", 11145 11146 11147 /** 11148 * This decimal place operator is a little different from the other 11149 * language options since DataTables doesn't output floating point 11150 * numbers, so it won't ever use this for display of a number. Rather, 11151 * what this parameter does is modify the sort methods of the table so 11152 * that numbers which are in a format which has a character other than 11153 * a period (`.`) as a decimal place will be sorted numerically. 11154 * 11155 * Note that numbers with different decimal places cannot be shown in 11156 * the same table and still be sortable, the table must be consistent. 11157 * However, multiple different tables on the page can use different 11158 * decimal place characters. 11159 */ 11160 "sDecimal": "", 11161 11162 11163 /** 11164 * DataTables has a build in number formatter (`formatNumber`) which is 11165 * used to format large numbers that are used in the table information. 11166 * By default a comma is used, but this can be trivially changed to any 11167 * character you wish with this parameter. 11168 */ 11169 "sThousands": ",", 11170 11171 11172 /** 11173 * Detail the action that will be taken when the drop down menu for the 11174 * pagination length option is changed. The '_MENU_' variable is replaced 11175 * with a default select list of 10, 25, 50 and 100, and can be replaced 11176 * with a custom select box if required. 11177 */ 11178 "sLengthMenu": "_MENU_ _ENTRIES_ per page", 11179 11180 11181 /** 11182 * When using Ajax sourced data and during the first draw when DataTables is 11183 * gathering the data, this message is shown in an empty row in the table to 11184 * indicate to the end user the the data is being loaded. Note that this 11185 * parameter is not used when loading data by server-side processing, just 11186 * Ajax sourced data with client-side processing. 11187 */ 11188 "sLoadingRecords": "Loading...", 11189 11190 11191 /** 11192 * Text which is displayed when the table is processing a user action 11193 * (usually a sort command or similar). 11194 */ 11195 "sProcessing": "", 11196 11197 11198 /** 11199 * Details the actions that will be taken when the user types into the 11200 * filtering input text box. The variable "_INPUT_", if used in the string, 11201 * is replaced with the HTML text box for the filtering input allowing 11202 * control over where it appears in the string. If "_INPUT_" is not given 11203 * then the input box is appended to the string automatically. 11204 */ 11205 "sSearch": "Search:", 11206 11207 11208 /** 11209 * Assign a `placeholder` attribute to the search `input` element 11210 * @type string 11211 * @default 11212 * 11213 * @dtopt Language 11214 * @name DataTable.defaults.language.searchPlaceholder 11215 */ 11216 "sSearchPlaceholder": "", 11217 11218 11219 /** 11220 * All of the language information can be stored in a file on the 11221 * server-side, which DataTables will look up if this parameter is passed. 11222 * It must store the URL of the language file, which is in a JSON format, 11223 * and the object has the same properties as the oLanguage object in the 11224 * initialiser object (i.e. the above parameters). Please refer to one of 11225 * the example language files to see how this works in action. 11226 */ 11227 "sUrl": "", 11228 11229 11230 /** 11231 * Text shown inside the table records when the is no information to be 11232 * displayed after filtering. `emptyTable` is shown when there is simply no 11233 * information in the table at all (regardless of filtering). 11234 */
11235 "sZeroRecords": "No matching records found" 11236 }, 11237 11238 11239 /** The initial data order is reversed when `desc` ordering */ 11240 orderDescReverse: true, 11241 11242 11243 /** 11244 * This parameter allows you to have define the global filtering state at 11245 * initialisation time. As an object the `search` parameter must be 11246 * defined, but all other parameters are optional. When `regex` is true, 11247 * the search string will be treated as a regular expression, when false 11248 * (default) it will be treated as a straight string. When `smart` 11249 * DataTables will use it's smart filtering methods (to word match at 11250 * any point in the data), when false this will not be done. 11251 */ 11252 "oSearch": $.extend( {}, DataTable.models.oSearch ), 11253 11254 11255 /** 11256 * Table and control layout. This replaces the legacy `dom` option. 11257 */ 11258 layout: { 11259 topStart: 'pageLength', 11260 topEnd: 'search', 11261 bottomStart: 'info', 11262 bottomEnd: 'paging' 11263 }, 11264 11265 11266 /** 11267 * Legacy DOM layout option 11268 */ 11269 "sDom": null, 11270 11271 11272 /** 11273 * Search delay option. This will throttle full table searches that use the 11274 * DataTables provided search input element (it does not effect calls to 11275 * `dt-api search()`, providing a delay before the search is made. 11276 */ 11277 "searchDelay": null, 11278 11279 11280 /** 11281 * DataTables features six different built-in options for the buttons to 11282 * display for pagination control: 11283 * 11284 * * `numbers` - Page number buttons only 11285 * * `simple` - 'Previous' and 'Next' buttons only 11286 * * 'simple_numbers` - 'Previous' and 'Next' buttons, plus page numbers 11287 * * `full` - 'First', 'Previous', 'Next' and 'Last' buttons 11288 * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus page numbers 11289 * * `first_last_numbers` - 'First' and 'Last' buttons, plus page numbers 11290 */ 11291 "sPaginationType": "", 11292 11293 11294 /** 11295 * Enable horizontal scrolling. When a table is too wide to fit into a 11296 * certain layout, or you have a large number of columns in the table, you 11297 * can enable x-scrolling to show the table in a viewport, which can be 11298 * scrolled. This property can be `true` which will allow the table to 11299 * scroll horizontally when needed, or any CSS unit, or a number (in which 11300 * case it will be treated as a pixel measurement). Setting as simply `true` 11301 * is recommended. 11302 */ 11303 "sScrollX": "", 11304 11305 11306 /** 11307 * This property can be used to force a DataTable to use more width than it 11308 * might otherwise do when x-scrolling is enabled. For example if you have a 11309 * table which requires to be well spaced, this parameter is useful for 11310 * "over-sizing" the table, and thus forcing scrolling. This property can by 11311 * any CSS unit, or a number (in which case it will be treated as a pixel 11312 * measurement). 11313 */ 11314 "sScrollXInner": "", 11315 11316 11317 /** 11318 * Enable vertical scrolling. Vertical scrolling will constrain the DataTable 11319 * to the given height, and enable scrolling for any data which overflows the 11320 * current viewport. This can be used as an alternative to paging to display 11321 * a lot of data in a small area (although paging and scrolling can both be 11322 * enabled at the same time). This property can be any CSS unit, or a number 11323 * (in which case it will be treated as a pixel measurement). 11324 */ 11325 "sScrollY": "", 11326 11327 11328 /** 11329 * __Deprecated__ The functionality provided by this parameter has now been 11330 * superseded by that provided through `ajax`, which should be used instead. 11331 * 11332 * Set the HTTP method that is used to make the Ajax call for server-side 11333 * processing or Ajax sourced data. 11334 */ 11335 "sServerMethod": "GET", 11336 11337 11338 /** 11339 * DataTables makes use of renderers when displaying HTML elements for 11340 * a table. These renderers can be added or modified by plug-ins to 11341 * generate suitable mark-up for a site. For example the Bootstrap 11342 * integration plug-in for DataTables uses a paging button renderer to 11343 * display pagination buttons in the mark-up required by Bootstrap. 11344 * 11345 * For further information about the renderers available see 11346 * DataTable.ext.renderer 11347 */ 11348 "renderer": null, 11349 11350 11351 /** 11352 * Set the data property name that DataTables should use to get a row's id 11353 * to set as the `id` property in the node. 11354 */ 11355 "rowId": "DT_RowId", 11356 11357 11358 /** 11359 * Caption value 11360 */ 11361 "caption": null, 11362 11363 11364 /** 11365 * For server-side processing - use the data from the DOM for the first draw 11366 */ 11367 iDeferLoading: null, 11368 11369 /** Event listeners */ 11370 on: null 11371 }; 11372 11373 _fnHungarianMap( DataTable.defaults ); 11374 11375 11376 11377 /* 11378 * Developer note - See note in model.defaults.js about the use of Hungarian 11379 * notation and camel case. 11380 */ 11381 11382 /** 11383 * Column options that can be given to DataTables at initialisation time. 11384 * @namespace 11385 */ 11386 DataTable.defaults.column = { 11387 /** 11388 * Define which column(s) an order will occur on for this column. This 11389 * allows a column's ordering to take multiple columns into account when
11390 * doing a sort or use the data from a different column. For example first 11391 * name / last name columns make sense to do a multi-column sort over the 11392 * two columns. 11393 */ 11394 "aDataSort": null, 11395 "iDataSort": -1, 11396 11397 ariaTitle: '', 11398 11399 11400 /** 11401 * You can control the default ordering direction, and even alter the 11402 * behaviour of the sort handler (i.e. only allow ascending ordering etc) 11403 * using this parameter. 11404 */ 11405 "asSorting": [ 'asc', 'desc', '' ], 11406 11407 11408 /** 11409 * Enable or disable filtering on the data in this column. 11410 */ 11411 "bSearchable": true, 11412 11413 11414 /** 11415 * Enable or disable ordering on this column. 11416 */ 11417 "bSortable": true, 11418 11419 11420 /** 11421 * Enable or disable the display of this column. 11422 */ 11423 "bVisible": true, 11424 11425 11426 /** 11427 * Developer definable function that is called whenever a cell is created (Ajax source, 11428 * etc) or processed for input (DOM source). This can be used as a compliment to mRender 11429 * allowing you to modify the DOM element (add background colour for example) when the 11430 * element is available. 11431 */ 11432 "fnCreatedCell": null, 11433 11434 11435 /** 11436 * This property can be used to read data from any data source property, 11437 * including deeply nested objects / properties. `data` can be given in a 11438 * number of different ways which effect its behaviour: 11439 * 11440 * * `integer` - treated as an array index for the data source. This is the 11441 * default that DataTables uses (incrementally increased for each column). 11442 * * `string` - read an object property from the data source. There are 11443 * three 'special' options that can be used in the string to alter how 11444 * DataTables reads the data from the source object: 11445 * * `.` - Dotted Javascript notation. Just as you use a `.` in 11446 * Javascript to read from nested objects, so to can the options 11447 * specified in `data`. For example: `browser.version` or 11448 * `browser.name`. If your object parameter name contains a period, use 11449 * `\\` to escape it - i.e. `first\\.name`. 11450 * * `[]` - Array notation. DataTables can automatically combine data 11451 * from and array source, joining the data with the characters provided 11452 * between the two brackets. For example: `name[, ]` would provide a 11453 * comma-space separated list from the source array. If no characters 11454 * are provided between the brackets, the original array source is 11455 * returned. 11456 * * `()` - Function notation. Adding `()` to the end of a parameter will 11457 * execute a function of the name given. For example: `browser()` for a 11458 * simple function on the data source, `browser.version()` for a 11459 * function in a nested property or even `browser().version` to get an 11460 * object property if the function called returns an object. Note that 11461 * function notation is recommended for use in `render` rather than 11462 * `data` as it is much simpler to use as a renderer. 11463 * * `null` - use the original data source for the row rather than plucking 11464 * data directly from it. This action has effects on two other 11465 * initialisation options: 11466 * * `defaultContent` - When null is given as the `data` option and 11467 * `defaultContent` is specified for the column, the value defined by 11468 * `defaultContent` will be used for the cell. 11469 * * `render` - When null is used for the `data` option and the `render` 11470 * option is specified for the column, the whole data source for the 11471 * row is used for the renderer. 11472 * * `function` - the function given will be executed whenever DataTables 11473 * needs to set or get the data for a cell in the column. The function 11474 * takes three parameters: 11475 * * Parameters: 11476 * * `{array|object}` The data source for the row 11477 * * `{string}` The type call data requested - this will be 'set' when 11478 * setting data or 'filter', 'display', 'type', 'sort' or undefined 11479 * when gathering data. Note that when `undefined` is given for the 11480 * type DataTables expects to get the raw data for the object back< 11481 * * `{*}` Data to set when the second parameter is 'set'. 11482 * * Return: 11483 * * The return value from the function is not required when '
11483set' is 11484 * the type of call, but otherwise the return is what will be used 11485 * for the data requested. 11486 * 11487 * Note that `data` is a getter and setter option. If you just require 11488 * formatting of data for output, you will likely want to use `render` which 11489 * is simply a getter and thus simpler to use. 11490 * 11491 * Note that prior to DataTables 1.9.2 `data` was called `mDataProp`. The 11492 * name change reflects the flexibility of this property and is consistent 11493 * with the naming of mRender. If 'mDataProp' is given, then it will still 11494 * be used by DataTables, as it automatically maps the old name to the new 11495 * if required. 11496 */ 11497 "mData": null, 11498 11499 11500 /** 11501 * This property is the rendering partner to `data` and it is suggested that 11502 * when you want to manipulate data for display (including filtering, 11503 * sorting etc) without altering the underlying data for the table, use this 11504 * property. `render` can be considered to be the the read only companion to 11505 * `data` which is read / write (then as such more complex). Like `data` 11506 * this option can be given in a number of different ways to effect its 11507 * behaviour: 11508 * 11509 * * `integer` - treated as an array index for the data source. This is the 11510 * default that DataTables uses (incrementally increased for each column). 11511 * * `string` - read an object property from the data source. There are 11512 * three 'special' options that can be used in the string to alter how 11513 * DataTables reads the data from the source object: 11514 * * `.` - Dotted Javascript notation. Just as you use a `.` in 11515 * Javascript to read from nested objects, so to can the options 11516 * specified in `data`. For example: `browser.version` or 11517 * `browser.name`. If your object parameter name contains a period, use 11518 * `\\` to escape it - i.e. `first\\.name`. 11519 * * `[]` - Array notation. DataTables can automatically combine data 11520 * from and array source, joining the data with the characters provided 11521 * between the two brackets. For example: `name[, ]` would provide a 11522 * comma-space separated list from the source array. If no characters 11523 * are provided between the brackets, the original array source is 11524 * returned. 11525 * * `()` - Function notation. Adding `()` to the end of a parameter will 11526 * execute a function of the name given. For example: `browser()` for a 11527 * simple function on the data source, `browser.version()` for a 11528 * function in a nested property or even `browser().version` to get an 11529 * object property if the function called returns an object. 11530 * * `object` - use different data for the different data types requested by 11531 * DataTables ('filter', 'display', 'type' or 'sort'). The property names 11532 * of the object is the data type the property refers to and the value can 11533 * defined using an integer, string or function using the same rules as 11534 * `render` normally does. Note that an `_` option _must_ be specified. 11535 * This is the default value to use if you haven't specified a value for 11536 * the data type requested by DataTables. 11537 * * `function` - the function given will be executed whenever DataTables 11538 * needs to set or get the data for a cell in the column. The function 11539 * takes three parameters: 11540 * * Parameters: 11541 * * {array|object} The data source for the row (based on `data`) 11542 * * {string} The type call data requested - this will be 'filter', 11543 * 'display', 'type' or 'sort'. 11544 * * {array|object} The full data source for the row (not based on 11545 * `data`) 11546 * * Return: 11547 * * The return value from the function is what will be used for the 11548 * data requested. 11549 */ 11550 "mRender": null, 11551 11552 11553 /** 11554 * Change the cell type created for the column - either TD cells or TH cells. This 11555 * can be useful as TH cells have semantic meaning in the table body, allowing them 11556 * to act as a header for a row (you may wish to add scope='row' to the TH elements). 11557 */ 11558 "sCellType": "td", 11559 11560 11561 /** 11562 * Class to give to each cell in this column. 11563 */ 11564 "sClass": "", 11565 11566 /** 11567 * When DataTables calculates the column widths to assign to each column, 11568 * it finds the longest string in each column and then constructs a 11569 * temporary table and reads the widths from that. The problem with this 11570 * is that "mmm" is much wider then "iiii", but the latter is a longer 11571 * string - thus the calculation can go wrong (doing it properly and putting 11572 * it into an DOM object and measuring that is horribly(!) slow). Thus as 11573 * a "work around" we provide this option. It will append its value to the 11574 * text that is found to be the longest string for the column - i.e. padding. 11575 * Generally you shouldn't need this! 11576 */ 11577 "sContentPadding": "", 11578 11579 11580 /** 11581 * Allows a default value to be given for a column's data, and will be used 11582 * whenever a null data source is encountered (this can be because `data` 11583 * is set to null, or because the data source itself is null). 11584 */ 11585 "sDefaultContent": null, 11586 11587 11588 /** 11589 * This parameter is only used in DataTables' server-side processing. It can 11590 * be exceptionally useful to know what columns are being displayed on the 11591 * client side, and to map these to database fields. When defined, the names 11592 * also allow DataTables to reorder information from the server if it comes 11593 * back in an unexpected order (i.e. if you switch your columns around on the 11594 * client-side, your server-side code does not also need updating). 11595 */ 11596 "sName": "", 11597 11598 11599 /** 11600 * Defines a data source type for the ordering which can be used to read 11601 * real-time information from the table (updating the internally cached 11602 * version) prior to ordering. This allows ordering to occur on user 11603 * editable elements such as form inputs. 11604 */ 11605 "sSortDataType": "std", 11606 11607 11608 /** 11609 * The title of this column. 11610 */ 11611 "sTitle": null, 11612 11613 11614 /** 11615 * The type allows you to specify how the data for this column will be
11616 * ordered. Four types (string, numeric, date and html (which will strip 11617 * HTML tags before ordering)) are currently available. Note that only date 11618 * formats understood by Javascript's Date() object will be accepted as type 11619 * date. For example: "Mar 26, 2008 5:03 PM". May take the values: 'string', 11620 * 'numeric', 'date' or 'html' (by default). Further types can be adding 11621 * through plug-ins. 11622 */ 11623 "sType": null, 11624 11625 11626 /** 11627 * Defining the width of the column, this parameter may take any CSS value 11628 * (3em, 20px etc). DataTables applies 'smart' widths to columns which have not 11629 * been given a specific width through this interface ensuring that the table 11630 * remains readable. 11631 */ 11632 "sWidth": null 11633 }; 11634 11635 _fnHungarianMap( DataTable.defaults.column ); 11636 11637 11638 11639 /** 11640 * DataTables settings object - this holds all the information needed for a 11641 * given table, including configuration, data and current application of the 11642 * table options. DataTables does not have a single instance for each DataTable 11643 * with the settings attached to that instance, but rather instances of the 11644 * DataTable "class" are created on-the-fly as needed (typically by a 11645 * $().dataTable() call) and the settings object is then applied to that 11646 * instance. 11647 * 11648 * Note that this object is related to {@link DataTable.defaults} but this 11649 * one is the internal data store for DataTables's cache of columns. It should 11650 * NOT be manipulated outside of DataTables. Any configuration should be done 11651 * through the initialisation options. 11652 */ 11653 DataTable.models.oSettings = { 11654 /** 11655 * Primary features of DataTables and their enablement state. 11656 */ 11657 "oFeatures": { 11658 11659 /** 11660 * Flag to say if DataTables should automatically try to calculate the 11661 * optimum table and columns widths (true) or not (false). 11662 * Note that this parameter will be set by the initialisation routine. To 11663 * set a default use {@link DataTable.defaults}. 11664 */ 11665 "bAutoWidth": null, 11666 11667 /** 11668 * Delay the creation of TR and TD elements until they are actually 11669 * needed by a driven page draw. This can give a significant speed 11670 * increase for Ajax source and Javascript source data, but makes no 11671 * difference at all for DOM and server-side processing tables. 11672 * Note that this parameter will be set by the initialisation routine. To 11673 * set a default use {@link DataTable.defaults}. 11674 */ 11675 "bDeferRender": null, 11676 11677 /** 11678 * Enable filtering on the table or not. Note that if this is disabled 11679 * then there is no filtering at all on the table, including fnFilter. 11680 * To just remove the filtering input use sDom and remove the 'f' option. 11681 * Note that this parameter will be set by the initialisation routine. To 11682 * set a default use {@link DataTable.defaults}. 11683 */ 11684 "bFilter": null, 11685 11686 /** 11687 * Used only for compatiblity with DT1 11688 * @deprecated 11689 */ 11690 "bInfo": true, 11691 11692 /** 11693 * Used only for compatiblity with DT1 11694 * @deprecated 11695 */ 11696 "bLengthChange": true, 11697 11698 /** 11699 * Pagination enabled or not. Note that if this is disabled then length 11700 * changing must also be disabled. 11701 * Note that this parameter will be set by the initialisation routine. To 11702 * set a default use {@link DataTable.defaults}. 11703 */ 11704 "bPaginate": null, 11705 11706 /** 11707 * Processing indicator enable flag whenever DataTables is enacting a 11708 * user request - typically an Ajax request for server-side processing. 11709 * Note that this parameter will be set by the initialisation routine. To 11710 * set a default use {@link DataTable.defaults}. 11711 */ 11712 "bProcessing": null, 11713 11714 /** 11715 * Server-side processing enabled flag - when enabled DataTables will 11716 * get all data from the server for every draw - there is no filtering, 11717 * sorting or paging done on the client-side. 11718 * Note that this parameter will be set by the initialisation routine. To 11719 * set a default use {@link DataTable.defaults}. 11720 */ 11721 "bServerSide": null, 11722 11723 /** 11724 * Sorting enablement flag. 11725 * Note that this parameter will be set by the initialisation routine. To 11726 * set a default use {@link DataTable.defaults}. 11727 */ 11728 "bSort": null, 11729 11730 /**
11731 * Multi-column sorting 11732 * Note that this parameter will be set by the initialisation routine. To 11733 * set a default use {@link DataTable.defaults}. 11734 */ 11735 "bSortMulti": null, 11736 11737 /** 11738 * Apply a class to the columns which are being sorted to provide a 11739 * visual highlight or not. This can slow things down when enabled since 11740 * there is a lot of DOM interaction. 11741 * Note that this parameter will be set by the initialisation routine. To 11742 * set a default use {@link DataTable.defaults}. 11743 */ 11744 "bSortClasses": null, 11745 11746 /** 11747 * State saving enablement flag. 11748 * Note that this parameter will be set by the initialisation routine. To 11749 * set a default use {@link DataTable.defaults}. 11750 */ 11751 "bStateSave": null 11752 }, 11753 11754 11755 /** 11756 * Scrolling settings for a table. 11757 */ 11758 "oScroll": { 11759 /** 11760 * When the table is shorter in height than sScrollY, collapse the 11761 * table container down to the height of the table (when true). 11762 * Note that this parameter will be set by the initialisation routine. To 11763 * set a default use {@link DataTable.defaults}. 11764 */ 11765 "bCollapse": null, 11766 11767 /** 11768 * Width of the scrollbar for the web-browser's platform. Calculated 11769 * during table initialisation. 11770 */ 11771 "iBarWidth": 0, 11772 11773 /** 11774 * Viewport width for horizontal scrolling. Horizontal scrolling is 11775 * disabled if an empty string. 11776 * Note that this parameter will be set by the initialisation routine. To 11777 * set a default use {@link DataTable.defaults}. 11778 */ 11779 "sX": null, 11780 11781 /** 11782 * Width to expand the table to when using x-scrolling. Typically you 11783 * should not need to use this. 11784 * Note that this parameter will be set by the initialisation routine. To 11785 * set a default use {@link DataTable.defaults}. 11786 * @deprecated 11787 */ 11788 "sXInner": null, 11789 11790 /** 11791 * Viewport height for vertical scrolling. Vertical scrolling is disabled 11792 * if an empty string. 11793 * Note that this parameter will be set by the initialisation routine. To 11794 * set a default use {@link DataTable.defaults}. 11795 */ 11796 "sY": null 11797 }, 11798 11799 /** 11800 * Language information for the table. 11801 */ 11802 "oLanguage": { 11803 /** 11804 * Information callback function. See 11805 * {@link DataTable.defaults.fnInfoCallback} 11806 */ 11807 "fnInfoCallback": null 11808 }, 11809 11810 /** 11811 * Browser support parameters 11812 */ 11813 "oBrowser": { 11814 /** 11815 * Determine if the vertical scrollbar is on the right or left of the 11816 * scrolling container - needed for rtl language layout, although not 11817 * all browsers move the scrollbar (Safari). 11818 */ 11819 "bScrollbarLeft": false, 11820 11821 /** 11822 * Browser scrollbar width 11823 */ 11824 "barWidth": 0 11825 }, 11826 11827 11828 "ajax": null, 11829 11830 11831 /** 11832 * Array referencing the nodes which are used for the features. The 11833 * parameters of this object match what is allowed by sDom - i.e. 11834 * <ul> 11835 * <li>'l' - Length changing</li> 11836 * <li>'f' - Filtering input</li> 11837 * <li>'t' - The table!</li> 11838 * <li>'i' - Information</li> 11839 * <li>'p' - Pagination</li> 11840 * <li>'r' - pRocessing</li> 11841 * </ul> 11842 */ 11843 "aanFeatures": [], 11844 11845 /** 11846 * Store data information - see {@link DataTable.models.oRow} for detailed 11847 * information. 11848 */ 11849 "aoData": [], 11850 11851 /** 11852 * Array of indexes which are in the current display (after filtering etc) 11853 */ 11854 "aiDisplay": [], 11855 11856 /** 11857 * Array of indexes for display - no filtering 11858 */ 11859 "aiDisplayMaster": [], 11860 11861 /** 11862 * Map of row ids to data indexes 11863 */ 11864 "aIds": {}, 11865 11866 /** 11867 * Store information about each column that is in use 11868 */ 11869 "aoColumns": [], 11870 11871 /** 11872 * Store information about the table's header 11873 */ 11874 "aoHeader": [], 11875 11876 /** 11877 * Store information about the table's footer 11878 */ 11879 "aoFooter": [], 11880 11881 /** 11882 * Store the applied global search information in case we want to force a 11883 * research or compare the old search to a new one. 11884 * Note that this parameter will be set by the initialisation routine. To 11885 * set a default use {@link DataTable.defaults}. 11886 */ 11887 "oPreviousSearch": {}, 11888 11889 /** 11890 * Store for named searches 11891 */ 11892 searchFixed: {}, 11893 11894 /** 11895 * Store the applied search for each column - see 11896 * {@link DataTable.models.oSearch} for the format that is used for the 11897 * filtering information for each column. 11898 */ 11899 "aoPreSearchCols": [], 11900 11901 /** 11902 * Sorting that is applied to the table. Note that the inner arrays are 11903 * used in the following manner: 11904 * <ul> 11905 * <li>Index 0 - column number</li> 11906 * <li>Index 1 - current sorting direction</li> 11907 * </ul> 11908 * Note that this parameter will be set by the initialisation routine. To 11909 * set a default use {@link DataTable.defaults}. 11910 */ 11911 "aaSorting": null, 11912 11913 /** 11914 * Sorting that is always applied to the table (i.e. prefixed in front of 11915 * aaSorting). 11916 * Note that this parameter will be set by the initialisation routine. To 11917 * set a default use {@link DataTable.defaults}. 11918 */ 11919 "aaSortingFixed": [], 11920 11921 /** 11922 * If restoring a table - we should restore its width 11923 */ 11924 "sDestroyWidth": 0, 11925 11926 /** 11927 * Callback functions array for every time a row is inserted (i.e. on a draw). 11928 */ 11929 "aoRowCallback": [], 11930 11931 /** 11932 * Callback functions for the header on each draw. 11933 */ 11934 "aoHeaderCallback": [], 11935 11936 /** 11937 * Callback function for the footer on each draw. 11938 */ 11939 "aoFooterCallback": [], 11940 11941 /** 11942 * Array of callback functions for draw callback functions 11943 */ 11944 "aoDrawCallback": [], 11945 11946 /** 11947 * Array of callback functions for row created function 11948 */ 11949 "aoRowCreatedCallback": [], 11950 11951 /** 11952 * Callback functions for just before the table is redrawn. A return of 11953 * false will be used to cancel the draw. 11954 */ 11955 "aoPreDrawCallback": [], 11956 11957 /** 11958 * Callback functions for when the table has been initialised. 11959 */ 11960 "aoInitComplete": [], 11961 11962 11963 /** 11964 * Callbacks for modifying the settings to be stored for state saving, prior to 11965 * saving state. 11966 */ 11967 "aoStateSaveParams": [], 11968 11969 /** 11970 * Callbacks for modifying the settings that have been stored for state saving 11971 * prior to using the stored values to restore the state. 11972 */ 11973 "aoStateLoadParams": [], 11974 11975 /** 11976 * Callbacks for operating on the settings object once the saved state has been 11977 * loaded 11978 */ 11979 "aoStateLoaded": [], 11980 11981 /** 11982 * Cache the table ID for quick access 11983 */ 11984 "sTableId": "", 11985 11986 /** 11987 * The TABLE node for the main table 11988 */ 11989 "nTable": null, 11990 11991 /** 11992 * Permanent ref to the thead element 11993 */ 11994 "nTHead": null, 11995 11996 /** 11997 * Permanent ref to the tfoot element - if it exists 11998 */ 11999 "nTFoot": null, 12000 12001 /** 12002 * Permanent ref to the tbody element 12003 */ 12004 "nTBody": null, 12005 12006 /** 12007 * Cache the wrapper node (contains all DataTables controlled elements) 12008 */ 12009 "nTableWrapper": null, 12010 12011 /** 12012 * Indicate if all required information has been read in 12013 */ 12014 "bInitialised": false, 12015 12016 /** 12017 * Information about open rows. Each object in the array has the parameters 12018 * 'nTr' and 'nParent' 12019 */ 12020 "aoOpenRows": [], 12021 12022 /**
12023 * Dictate the positioning of DataTables' control elements - see 12024 * {@link DataTable.model.oInit.sDom}. 12025 * Note that this parameter will be set by the initialisation routine. To 12026 * set a default use {@link DataTable.defaults}. 12027 */ 12028 "sDom": null, 12029 12030 /** 12031 * Search delay (in mS) 12032 */ 12033 "searchDelay": null, 12034 12035 /** 12036 * Which type of pagination should be used. 12037 * Note that this parameter will be set by the initialisation routine. To 12038 * set a default use {@link DataTable.defaults}. 12039 */ 12040 "sPaginationType": "two_button", 12041 12042 /** 12043 * Number of paging controls on the page. Only used for backwards compatibility 12044 */ 12045 pagingControls: 0, 12046 12047 /** 12048 * The state duration (for `stateSave`) in seconds. 12049 * Note that this parameter will be set by the initialisation routine. To 12050 * set a default use {@link DataTable.defaults}. 12051 */ 12052 "iStateDuration": 0, 12053 12054 /** 12055 * Array of callback functions for state saving. Each array element is an 12056 * object with the following parameters: 12057 * <ul> 12058 * <li>function:fn - function to call. Takes two parameters, oSettings 12059 * and the JSON string to save that has been thus far created. Returns 12060 * a JSON string to be inserted into a json object 12061 * (i.e. '"param": [ 0, 1, 2]')</li> 12062 * <li>string:sName - name of callback</li> 12063 * </ul> 12064 */ 12065 "aoStateSave": [], 12066 12067 /** 12068 * Array of callback functions for state loading. Each array element is an 12069 * object with the following parameters: 12070 * <ul> 12071 * <li>function:fn - function to call. Takes two parameters, oSettings 12072 * and the object stored. May return false to cancel state loading</li> 12073 * <li>string:sName - name of callback</li> 12074 * </ul> 12075 */ 12076 "aoStateLoad": [], 12077 12078 /** 12079 * State that was saved. Useful for back reference 12080 */ 12081 "oSavedState": null, 12082 12083 /** 12084 * State that was loaded. Useful for back reference 12085 */ 12086 "oLoadedState": null, 12087 12088 /** 12089 * Note if draw should be blocked while getting data 12090 */ 12091 "bAjaxDataGet": true, 12092 12093 /** 12094 * The last jQuery XHR object that was used for server-side data gathering. 12095 * This can be used for working with the XHR information in one of the 12096 * callbacks 12097 */ 12098 "jqXHR": null, 12099 12100 /** 12101 * JSON returned from the server in the last Ajax request 12102 */ 12103 "json": undefined, 12104 12105 /** 12106 * Data submitted as part of the last Ajax request 12107 */ 12108 "oAjaxData": undefined, 12109 12110 /** 12111 * Send the XHR HTTP method - GET or POST (could be PUT or DELETE if 12112 * required). 12113 * Note that this parameter will be set by the initialisation routine. To 12114 * set a default use {@link DataTable.defaults}. 12115 */ 12116 "sServerMethod": null, 12117 12118 /** 12119 * Format numbers for display. 12120 * Note that this parameter will be set by the initialisation routine. To 12121 * set a default use {@link DataTable.defaults}. 12122 */ 12123 "fnFormatNumber": null, 12124 12125 /** 12126 * List of options that can be used for the user selectable length menu. 12127 * Note that this parameter will be set by the initialisation routine. To 12128 * set a default use {@link DataTable.defaults}. 12129 */ 12130 "aLengthMenu": null, 12131 12132 /** 12133 * Counter for the draws that the table does. Also used as a tracker for 12134 * server-side processing 12135 */ 12136 "iDraw": 0, 12137 12138 /** 12139 * Indicate if a redraw is being done - useful for Ajax 12140 */ 12141 "bDrawing": false, 12142 12143 /** 12144 * Draw index (iDraw) of the last error when parsing the returned data 12145 */ 12146 "iDrawError": -1, 12147 12148 /** 12149 * Paging display length 12150 */ 12151 "_iDisplayLength": 10, 12152 12153 /** 12154 * Paging start point - aiDisplay index 12155 */ 12156 "_iDisplayStart": 0, 12157 12158 /** 12159 * Server-side processing - number of records in the result set 12160 * (i.e. before filtering), Use fnRecordsTotal rather than 12161 * this property to get the value of the number of records, regardless of 12162 * the server-side processing setting. 12163 */ 12164 "_iRecordsTotal": 0, 12165 12166 /** 12167 * Server-side processing - number of records in the current display set 12168 * (i.e. after filtering). Use fnRecordsDisplay rather than 12169 * this property to get the value of the number of records, regardless of 12170 * the server-side processing setting. 12171 */ 12172 "_iRecordsDisplay": 0, 12173 12174 /** 12175 * The classes to use for the table 12176 */
12177 "oClasses": {}, 12178 12179 /** 12180 * Flag attached to the settings object so you can check in the draw 12181 * callback if filtering has been done in the draw. Deprecated in favour of 12182 * events. 12183 * @deprecated 12184 */ 12185 "bFiltered": false, 12186 12187 /** 12188 * Flag attached to the settings object so you can check in the draw 12189 * callback if sorting has been done in the draw. Deprecated in favour of 12190 * events. 12191 * @deprecated 12192 */ 12193 "bSorted": false, 12194 12195 /** 12196 * Indicate that if multiple rows are in the header and there is more than 12197 * one unique cell per column. Replaced by titleRow 12198 */ 12199 "bSortCellsTop": null, 12200 12201 /** 12202 * Initialisation object that is used for the table 12203 */ 12204 "oInit": null, 12205 12206 /** 12207 * Destroy callback functions - for plug-ins to attach themselves to the 12208 * destroy so they can clean up markup and events. 12209 */ 12210 "aoDestroyCallback": [], 12211 12212 12213 /** 12214 * Get the number of records in the current record set, before filtering 12215 */ 12216 "fnRecordsTotal": function () 12217 { 12218 return _fnDataSource( this ) == 'ssp' ? 12219 this._iRecordsTotal * 1 : 12220 this.aiDisplayMaster.length; 12221 }, 12222 12223 /** 12224 * Get the number of records in the current record set, after filtering 12225 */ 12226 "fnRecordsDisplay": function () 12227 { 12228 return _fnDataSource( this ) == 'ssp' ? 12229 this._iRecordsDisplay * 1 : 12230 this.aiDisplay.length; 12231 }, 12232 12233 /** 12234 * Get the display end point - aiDisplay index 12235 */ 12236 "fnDisplayEnd": function () 12237 { 12238 var 12239 len = this._iDisplayLength, 12240 start = this._iDisplayStart, 12241 calc = start + len, 12242 records = this.aiDisplay.length, 12243 features = this.oFeatures, 12244 paginate = features.bPaginate; 12245 12246 if ( features.bServerSide ) { 12247 return paginate === false || len === -1 ? 12248 start + records : 12249 Math.min( start+len, this._iRecordsDisplay ); 12250 } 12251 else { 12252 return ! paginate || calc>records || len===-1 ? 12253 records : 12254 calc; 12255 } 12256 }, 12257 12258 /** 12259 * The DataTables object for this table 12260 */ 12261 "oInstance": null, 12262 12263 /** 12264 * Unique identifier for each instance of the DataTables object. If there 12265 * is an ID on the table node, then it takes that value, otherwise an 12266 * incrementing internal counter is used. 12267 */ 12268 "sInstance": null, 12269 12270 /** 12271 * tabindex attribute value that is added to DataTables control elements, allowing 12272 * keyboard navigation of the table and its controls. 12273 */ 12274 "iTabIndex": 0, 12275 12276 /** 12277 * DIV container for the footer scrolling table if scrolling 12278 */ 12279 "nScrollHead": null, 12280 12281 /** 12282 * DIV container for the footer scrolling table if scrolling 12283 */ 12284 "nScrollFoot": null, 12285 12286 /** 12287 * Last applied sort 12288 */ 12289 "aLastSort": [], 12290 12291 /** 12292 * Stored plug-in instances 12293 */ 12294 "oPlugins": {}, 12295 12296 /** 12297 * Function used to get a row's id from the row's data 12298 */ 12299 "rowIdFn": null, 12300 12301 /** 12302 * Data location where to store a row's id 12303 */ 12304 "rowId": null, 12305 12306 caption: '', 12307 12308 captionNode: null, 12309 12310 colgroup: null, 12311 12312 /** Delay loading of data */ 12313 deferLoading: null, 12314 12315 /** Allow auto type detection */ 12316 typeDetect: true, 12317 12318 /** ResizeObserver for the container div */ 12319 resizeObserver: null, 12320 12321 /** Keep a record of the last size of the container, so we can skip duplicates */ 12322 containerWidth: -1, 12323 12324 /** Reverse the initial order of the data set on desc ordering */ 12325 orderDescReverse: null, 12326 12327 /** Show / hide ordering indicators in headers */ 12328 orderIndicators: true, 12329 12330 /** Default ordering listener */ 12331 orderHandler: true, 12332 12333 /** Title row indicator */ 12334 titleRow: null 12335 }; 12336 12337 /** 12338 * Extension object for DataTables that is used to provide all extension 12339 * options. 12340 * 12341 * Note that the `DataTable.ext` object is available through 12342 * `jQuery.fn.dataTable.ext` where it may be accessed and manipulated. It is 12343 * also aliased to `jQuery.fn.dataTableExt` for historic reasons. 12344 * @namespace 12345 * @extends DataTable.models.ext 12346 */ 12347 12348 12349 var extPagination = DataTable.ext.pager; 12350 12351 // Paging buttons configuration 12352 $.extend( extPagination, { 12353 simple: function () { 12354 return [ 'previous', 'next' ]; 12355 }, 12356 12357 full: function () { 12358 return [ 'first', 'previous', 'next', 'last' ]; 12359 }, 12360 12361 numbers: function () { 12362 return [ 'numbers' ]; 12363 }, 12364 12365 simple_numbers: function () { 12366 return [ 'previous', 'numbers', 'next' ]; 12367 }, 12368 12369 full_numbers: function () { 12370 return [ 'first', 'previous', 'numbers', 'next', 'last' ]; 12371 }, 12372 12373 first_last: function () { 12374 return ['first', 'last']; 12375 }, 12376 12377 first_last_numbers: function () { 12378 return ['first', 'numbers', 'last']; 12379 }, 12380 12381 // For testing and plug-ins to use 12382 _numbers: _pagingNumbers, 12383 12384 // Number of number buttons - legacy, use `numbers` option for paging feature 12385 numbers_length: 7 12386 } ); 12387 12388 12389 $.extend( true, DataTable.ext.renderer, { 12390 pagingButton: { 12391 _: function (settings, buttonType, content, active, disabled) { 12392 var classes = settings.oClasses.paging; 12393 var btnClasses = [classes.button]; 12394 var btn; 12395 12396 if (active) { 12397 btnClasses.push(classes.active); 12398 } 12399 12400 if (disabled) { 12401 btnClasses.push(classes.disabled) 12402 } 12403 12404 if (buttonType === 'ellipsis') { 12405 btn = $('<span class="ellipsis"></span>').html(content)[0]; 12406 } 12407 else { 12408 btn = $('<button>', { 12409 class: btnClasses.join(' '), 12410 role: 'link', 12411 type: 'button' 12412 }).html(content); 12413 } 12414 12415 return { 12416 display: btn, 12417 clicker: btn 12418 } 12419 } 12420 }, 12421 12422 pagingContainer: { 12423 _: function (settings, buttons) { 12424 // No wrapping element - just append directly to the host 12425 return buttons; 12426 } 12427 } 12428 } ); 12429 12430 // Common function to remove new lines, strip HTML and diacritic control 12431 var _filterString = function (stripHtml, normalize) { 12432 return function (str) { 12433 if (_empty(str) || typeof str !== 'string') { 12434 return str; 12435 } 12436 12437 str = str.replace( _re_new_lines, " " ); 12438 12439 if (stripHtml) { 12440 str = _stripHtml(str); 12441 } 12442 12443 if (normalize) { 12444 str = _normalize(str, false); 12445 } 12446 12447 return str; 12448 }; 12449 } 12450 12451 /* 12452 * Public helper functions. These aren't used internally by DataTables, or 12453 * called by any of the options passed into DataTables, but they can be used 12454 * externally by developers working with DataTables. They are helper functions 12455 * to make working with DataTables a little bit easier. 12456 */ 12457 12458 /** 12459 * Common logic for moment, luxon or a date action. 12460 * 12461 * Happens after __mldObj, so don't need to call `resolveWindowsLibs` again 12462 */ 12463 function __mld( dtLib, momentFn, luxonFn, dateFn, arg1 ) { 12464 if (__moment) { 12465 return dtLib[momentFn]( arg1 ); 12466 } 12467 else if (__luxon) { 12468 return dtLib[luxonFn]( arg1 ); 12469 } 12470 12471 return dateFn ? dtLib[dateFn]( arg1 ) : dtLib; 12472 } 12473 12474 12475 var __mlWarning = false;
12476 var __luxon; // Can be assigned in DateTable.use() 12477 var __moment; // Can be assigned in DateTable.use() 12478 12479 /** 12480 * 12481 */ 12482 function resolveWindowLibs() { 12483 if (window.luxon && ! __luxon) { 12484 __luxon = window.luxon; 12485 } 12486 12487 if (window.moment && ! __moment) { 12488 __moment = window.moment; 12489 } 12490 } 12491 12492 function __mldObj (d, format, locale) { 12493 var dt; 12494 12495 resolveWindowLibs(); 12496 12497 if (__moment) { 12498 dt = __moment.utc( d, format, locale, true ); 12499 12500 if (! dt.isValid()) { 12501 return null; 12502 } 12503 } 12504 else if (__luxon) { 12505 dt = format && typeof d === 'string' 12506 ? __luxon.DateTime.fromFormat( d, format ) 12507 : __luxon.DateTime.fromISO( d ); 12508 12509 if (! dt.isValid) { 12510 return null; 12511 } 12512 12513 dt = dt.setLocale(locale); 12514 } 12515 else if (! format) { 12516 // No format given, must be ISO 12517 dt = new Date(d); 12518 } 12519 else { 12520 if (! __mlWarning) { 12521 alert('DataTables warning: Formatted date without Moment.js or Luxon - https://datatables.net/tn/17'); 12522 } 12523 12524 __mlWarning = true; 12525 } 12526 12527 return dt; 12528 } 12529 12530 // Wrapper for date, datetime and time which all operate the same way with the exception of 12531 // the output string for auto locale support 12532 function __mlHelper (localeString) { 12533 return function ( from, to, locale, def ) { 12534 // Luxon and Moment support 12535 // Argument shifting 12536 if ( arguments.length === 0 ) { 12537 locale = 'en'; 12538 to = null; // means toLocaleString 12539 from = null; // means iso8601 12540 } 12541 else if ( arguments.length === 1 ) { 12542 locale = 'en'; 12543 to = from; 12544 from = null; 12545 } 12546 else if ( arguments.length === 2 ) { 12547 locale = to; 12548 to = from; 12549 from = null; 12550 } 12551 12552 var typeName = 'datetime' + (to ? '-' + to : ''); 12553 12554 // Add type detection and sorting specific to this date format - we need to be able to identify 12555 // date type columns as such, rather than as numbers in extensions. Hence the need for this. 12556 if (! DataTable.ext.type.order[typeName + '-pre']) { 12557 DataTable.type(typeName, { 12558 detect: function (d) { 12559 // The renderer will give the value to type detect as the type! 12560 return d === typeName ? typeName : false; 12561 }, 12562 order: { 12563 pre: function (d) { 12564 // The renderer gives us Moment, Luxon or Date obects for the sorting, all of which have a 12565 // `valueOf` which gives milliseconds epoch 12566 return d.valueOf(); 12567 } 12568 }, 12569 className: 'dt-right' 12570 }); 12571 } 12572 12573 return function ( d, type ) { 12574 // Allow for a default value 12575 if (d === null || d === undefined) { 12576 if (def === '--now') { 12577 // We treat everything as UTC further down, so no changes are 12578 // made, as such need to get the local date / time as if it were 12579 // UTC 12580 var local = new Date(); 12581 d = new Date( Date.UTC( 12582 local.getFullYear(), local.getMonth(), local.getDate(), 12583 local.getHours(), local.getMinutes(), local.getSeconds() 12584 ) ); 12585 } 12586 else { 12587 d = ''; 12588 } 12589 } 12590 12591 if (type === 'type') { 12592 // Typing uses the type name for fast matching 12593 return typeName; 12594 } 12595 12596 if (d === '') { 12597 return type !== 'sort' 12598 ? '' 12599 : __mldObj('0000-01-01 00:00:00', null, locale); 12600 } 12601 12602 // Shortcut. If `from` and `to` are the same, we are using the renderer to 12603 // format for ordering, not display - its already in the display format. 12604 if ( to !== null && from === to && type !== 'sort' && type !== 'type' && ! (d instanceof Date) ) { 12605 return d; 12606 } 12607 12608 var dt = __mldObj(d, from, locale); 12609 12610 if (dt === null) { 12611 return d; 12612 } 12613 12614 if (type === 'sort') { 12615 return dt; 12616 } 12617 12618 var formatted = to === null 12619 ? __mld(dt, 'toDate', 'toJSDate', '')[localeString]() 12620 : __mld(dt, 'format', 'toFormat', 'toISOString', to); 12621 12622 // XSS protection 12623 return type === 'display' ? 12624 _escapeHtml( formatted ) : 12625 formatted; 12626 }; 12627 } 12628 } 12629 12630 // Based on locale, determine standard number formatting 12631 // Fallback for legacy browsers is US English 12632 var __thousands = ','; 12633 var __decimal = '.'; 12634 12635 if (window.Intl !== undefined) { 12636 try { 12637 var num = new Intl.NumberFormat().formatToParts(100000.1); 12638 12639 for (var i=0 ; i<num.length ; i++) { 12640 if (num[i].type === 'group') { 12641 __thousands = num[i].value; 12642 } 12643 else if (num[i].type === 'decimal') { 12644 __decimal = num[i].value; 12645 } 12646 } 12647 } 12648 catch (e) { 12649 // noop 12650 } 12651 } 12652 12653 // Formatted date time detection - use by declaring the formats you are going to use 12654 DataTable.datetime = function ( format, locale ) { 12655 var typeName = 'datetime-' + format; 12656 12657 if (! locale) { 12658 locale = 'en'; 12659 } 12660 12661 if (! DataTable.ext.type.order[typeName]) { 12662 DataTable.type(typeName, { 12663 detect: function (d) { 12664 var dt = __mldObj(d, format, locale); 12665 return d === '' || dt ? typeName : false;
12666 }, 12667 order: { 12668 pre: function (d) { 12669 return __mldObj(d, format, locale) || 0; 12670 } 12671 }, 12672 className: 'dt-right' 12673 }); 12674 } 12675 } 12676 12677 /** 12678 * Helpers for `columns.render`. 12679 * 12680 * The options defined here can be used with the `columns.render` initialisation 12681 * option to provide a display renderer. The following functions are defined: 12682 * 12683 * * `moment` - Uses the MomentJS library to convert from a given format into another. 12684 * This renderer has three overloads: 12685 * * 1 parameter: 12686 * * `string` - Format to convert to (assumes input is ISO8601 and locale is `en`) 12687 * * 2 parameters: 12688 * * `string` - Format to convert from 12689 * * `string` - Format to convert to. Assumes `en` locale 12690 * * 3 parameters: 12691 * * `string` - Format to convert from 12692 * * `string` - Format to convert to 12693 * * `string` - Locale 12694 * * `number` - Will format numeric data (defined by `columns.data`) for 12695 * display, retaining the original unformatted data for sorting and filtering. 12696 * It takes 5 parameters: 12697 * * `string` - Thousands grouping separator 12698 * * `string` - Decimal point indicator 12699 * * `integer` - Number of decimal points to show 12700 * * `string` (optional) - Prefix. 12701 * * `string` (optional) - Postfix (/suffix). 12702 * * `text` - Escape HTML to help prevent XSS attacks. It has no optional 12703 * parameters. 12704 * 12705 * @example 12706 * // Column definition using the number renderer 12707 * { 12708 * data: "salary", 12709 * render: $.fn.dataTable.render.number( '\'', '.', 0, '$' ) 12710 * } 12711 * 12712 * @namespace 12713 */ 12714 DataTable.render = { 12715 date: __mlHelper('toLocaleDateString'), 12716 datetime: __mlHelper('toLocaleString'), 12717 time: __mlHelper('toLocaleTimeString'), 12718 number: function ( thousands, decimal, precision, prefix, postfix ) { 12719 // Auto locale detection 12720 if (thousands === null || thousands === undefined) { 12721 thousands = __thousands; 12722 } 12723 12724 if (decimal === null || decimal === undefined) { 12725 decimal = __decimal; 12726 } 12727 12728 return { 12729 display: function ( d ) { 12730 if ( typeof d !== 'number' && typeof d !== 'string' ) { 12731 return d; 12732 } 12733 12734 if (d === '' || d === null) { 12735 return d; 12736 } 12737 12738 var negative = d < 0 ? '-' : ''; 12739 var flo = parseFloat( d ); 12740 var abs = Math.abs(flo); 12741 12742 // Scientific notation for large and small numbers 12743 if (abs >= 100000000000 || (abs < 0.0001 && abs !== 0) ) { 12744 var exp = flo.toExponential(precision).split(/e\+?/); 12745 return exp[0] + ' x 10<sup>' + exp[1] + '</sup>'; 12746 } 12747 12748 // If NaN then there isn't much formatting that we can do - just 12749 // return immediately, escaping any HTML (this was supposed to 12750 // be a number after all) 12751 if ( isNaN( flo ) ) { 12752 return _escapeHtml( d ); 12753 } 12754 12755 flo = flo.toFixed( precision ); 12756 d = Math.abs( flo ); 12757 12758 var intPart = parseInt( d, 10 ); 12759 var floatPart = precision ? 12760 decimal+(d - intPart).toFixed( precision ).substring( 2 ): 12761 ''; 12762 12763 // If zero, then can't have a negative prefix 12764 if (intPart === 0 && parseFloat(floatPart) === 0) { 12765 negative = ''; 12766 } 12767 12768 return negative + (prefix||'') + 12769 intPart.toString().replace( 12770 /\B(?=(\d{3})+(?!\d))/g, thousands 12771 ) + 12772 floatPart + 12773 (postfix||''); 12774 } 12775 }; 12776 }, 12777 12778 text: function () { 12779 return { 12780 display: _escapeHtml, 12781 filter: _escapeHtml 12782 }; 12783 } 12784 }; 12785 12786 12787 var _extTypes = DataTable.ext.type; 12788 12789 // Get / set type 12790 DataTable.type = function (name, prop, val) { 12791 if (! prop) { 12792 return { 12793 className: _extTypes.className[name], 12794 detect: _extTypes.detect.find(function (fn) { 12795 return fn._name === name; 12796 }), 12797 order: { 12798 pre: _extTypes.order[name + '-pre'], 12799 asc: _extTypes.order[name + '-asc'], 12800 desc: _extTypes.order[name + '-desc'] 12801 }, 12802 render: _extTypes.render[name], 12803 search: _extTypes.search[name] 12804 }; 12805 } 12806 12807 var setProp = function(prop, propVal) { 12808 _extTypes[prop][name] = propVal; 12809 }; 12810 var setDetect = function (detect) { 12811 // `detect` can be a function or an object - we set a name 12812 // property for either - that is used for the detection 12813 Object.defineProperty(detect, "_name", {value: name}); 12814 12815 var idx = _extTypes.detect.findIndex(function (item) { 12816 return item._name === name; 12817 }); 12818 12819 if (idx === -1) { 12820 _extTypes.detect.unshift(detect); 12821 } 12822 else { 12823 _extTypes.detect.splice(idx, 1, detect); 12824 } 12825 }; 12826 var setOrder = function (obj) { 12827 _extTypes.order[name + '-pre'] = obj.pre; // can be undefined 12828 _extTypes.order[name + '-asc'] = obj.asc; // can be undefined 12829 _extTypes.order[name + '-desc'] = obj.desc; // can be undefined 12830 }; 12831 12832 // prop is optional 12833 if (val === undefined) { 12834 val = prop; 12835 prop = null; 12836 } 12837 12838 if (prop === 'className') { 12839 setProp('className', val); 12840 } 12841 else if (prop === 'detect') { 12842 setDetect(val); 12843 } 12844 else if (prop === 'order') { 12845 setOrder(val); 12846 } 12847 else if (prop === 'render') { 12848 setProp('render', val); 12849 } 12850 else if (prop === 'search') { 12851 setProp('search', val); 12852 } 12853 else if (! prop) { 12854 if (val.className) { 12855 setProp('className', val.className); 12856 } 12857 12858 if (val.detect !== undefined) { 12859 setDetect(val.detect); 12860 } 12861 12862 if (val.order) { 12863 setOrder(val.order); 12864 } 12865 12866 if (val.render !== undefined) { 12867 setProp('render', val.render); 12868 } 12869 12870 if (val.search !== undefined) { 12871 setProp('search', val.search); 12872 } 12873 } 12874 } 12875 12876 // Get a list of types 12877 DataTable.types = function () { 12878 return _extTypes.detect.map(function (fn) { 12879 return fn._name; 12880 }); 12881 }; 12882 12883 var __diacriticSort = function (a, b) { 12884 a = a !== null && a !== undefined ? a.toString().toLowerCase() : ''; 12885 b = b !== null && b !== undefined ? b.toString().toLowerCase() : ''; 12886 12887 // Checked for `navigator.languages` support in `oneOf` so this code can't execute in old 12888 // Safari and thus can disable this check 12889 // eslint-disable-next-line compat/compat 12890 return a.localeCompare(b, navigator.languages[0] || navigator.language, { 12891 numeric: true, 12892 ignorePunctuation: true, 12893 }); 12894 } 12895 12896 var __diacriticHtmlSort = function (a, b) { 12897 a = _stripHtml(a); 12898 b = _stripHtml(b); 12899 12900 return __diacriticSort(a, b); 12901 } 12902 12903 // 12904 // Built in data types 12905 // 12906 12907 DataTable.type('string', { 12908 detect: function () { 12909 return 'string'; 12910 }, 12911 order: { 12912 pre: function ( a ) { 12913 // This is a little complex, but faster than always calling toString, 12914 // http://jsperf.com/tostring-v-check 12915 return _empty(a) && typeof a !== 'boolean' ? 12916 '' : 12917 typeof a === 'string' ? 12918 a.toLowerCase() : 12919 ! a.toString ? 12920 '' : 12921 a.toString(); 12922 } 12923 }, 12924 search: _filterString(false, true) 12925 }); 12926 12927 DataTable.type('string-utf8', { 12928 detect: { 12929 allOf: function ( d ) { 12930 return true; 12931 }, 12932 oneOf: function ( d ) { 12933 // At least one data point must contain a non-ASCII character 12934 // This line will also check if navigator.languages is supported or not. If not (Safari 10.0-) 12935 // this data type won't be supported. 12936 // eslint-disable-next-line compat/compat 12937 return ! _empty( d ) && navigator.languages && typeof d === 'string' && d.match(/[^\x00-\x7F]/); 12938 } 12939 }, 12940 order: { 12941 asc: __diacriticSort, 12942 desc: function (a, b) { 12943 return __diacriticSort(a, b) * -1; 12944 } 12945 }, 12946 search: _filterString(false, true) 12947 }); 12948 12949 12950 DataTable.type('html', { 12951 detect: { 12952 allOf: function ( d ) { 12953 return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1); 12954 }, 12955 oneOf: function ( d ) { 12956 // At least one data point must contain a `<` 12957 return ! _empty( d ) && typeof d === 'string' && d.indexOf('<') !== -1; 12958 } 12959 }, 12960 order: { 12961 pre: function ( a ) { 12962 return _empty(a) ? 12963 '' : 12964 a.replace ?
12965 _stripHtml(a).trim().toLowerCase() : 12966 a+''; 12967 } 12968 }, 12969 search: _filterString(true, true) 12970 }); 12971 12972 12973 DataTable.type('html-utf8', { 12974 detect: { 12975 allOf: function ( d ) { 12976 return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1); 12977 }, 12978 oneOf: function ( d ) { 12979 // At least one data point must contain a `<` and a non-ASCII character 12980 // eslint-disable-next-line compat/compat 12981 return navigator.languages && 12982 ! _empty( d ) && 12983 typeof d === 'string' && 12984 d.indexOf('<') !== -1 && 12985 typeof d === 'string' && d.match(/[^\x00-\x7F]/); 12986 } 12987 }, 12988 order: { 12989 asc: __diacriticHtmlSort, 12990 desc: function (a, b) { 12991 return __diacriticHtmlSort(a, b) * -1; 12992 } 12993 }, 12994 search: _filterString(true, true) 12995 }); 12996 12997 12998 DataTable.type('date', { 12999 className: 'dt-type-date', 13000 detect: { 13001 allOf: function ( d ) { 13002 // V8 tries _very_ hard to make a string passed into `Date.parse()` 13003 // valid, so we need to use a regex to restrict date formats. Use a 13004 // plug-in for anything other than ISO8601 style strings 13005 if ( d && !(d instanceof Date) && ! _re_date.test(d) ) { 13006 return null; 13007 } 13008 var parsed = Date.parse(d); 13009 return (parsed !== null && !isNaN(parsed)) || _empty(d); 13010 }, 13011 oneOf: function ( d ) { 13012 // At least one entry must be a date or a string with a date 13013 return (d instanceof Date) || (typeof d === 'string' && _re_date.test(d)); 13014 } 13015 }, 13016 order: { 13017 pre: function ( d ) { 13018 var ts = Date.parse( d ); 13019 return isNaN(ts) ? -Infinity : ts; 13020 } 13021 } 13022 }); 13023 13024 13025 DataTable.type('html-num-fmt', { 13026 className: 'dt-type-numeric', 13027 detect: { 13028 allOf: function ( d, settings ) { 13029 var decimal = settings.oLanguage.sDecimal; 13030 return _htmlNumeric( d, decimal, true, false ); 13031 }, 13032 oneOf: function (d, settings) { 13033 // At least one data point must contain a numeric value 13034 var decimal = settings.oLanguage.sDecimal; 13035 return _htmlNumeric( d, decimal, true, false ); 13036 } 13037 }, 13038 order: { 13039 pre: function ( d, s ) { 13040 var dp = s.oLanguage.sDecimal; 13041 return __numericReplace( d, dp, _re_html, _re_formatted_numeric ); 13042 } 13043 }, 13044 search: _filterString(true, true) 13045 }); 13046 13047 13048 DataTable.type('html-num', { 13049 className: 'dt-type-numeric', 13050 detect: { 13051 allOf: function ( d, settings ) { 13052 var decimal = settings.oLanguage.sDecimal; 13053 return _htmlNumeric( d, decimal, false, true ); 13054 }, 13055 oneOf: function (d, settings) { 13056 // At least one data point must contain a numeric value 13057 var decimal = settings.oLanguage.sDecimal; 13058 return _htmlNumeric( d, decimal, false, false ); 13059 } 13060 }, 13061 order: { 13062 pre: function ( d, s ) { 13063 var dp = s.oLanguage.sDecimal; 13064 return __numericReplace( d, dp, _re_html ); 13065 } 13066 }, 13067 search: _filterString(true, true) 13068 }); 13069 13070 13071 DataTable.type('num-fmt', { 13072 className: 'dt-type-numeric', 13073 detect: { 13074 allOf: function ( d, settings ) { 13075 var decimal = settings.oLanguage.sDecimal; 13076 return _isNumber( d, decimal, true, true ); 13077 }, 13078 oneOf: function (d, settings) { 13079 // At least one data point must contain a numeric value 13080 var decimal = settings.oLanguage.sDecimal; 13081 return _isNumber( d, decimal, true, false ); 13082 } 13083 }, 13084 order: { 13085 pre: function ( d, s ) { 13086 var dp = s.oLanguage.sDecimal; 13087 return __numericReplace( d, dp, _re_formatted_numeric ); 13088 } 13089 } 13090 }); 13091 13092 13093 DataTable.type('num', { 13094 className: 'dt-type-numeric', 13095 detect: { 13096 allOf: function ( d, settings ) { 13097 var decimal = settings.oLanguage.sDecimal; 13098 return _isNumber( d, decimal, false, true ); 13099 }, 13100 oneOf: function (d, settings) { 13101 // At least one data point must contain a numeric value 13102 var decimal = settings.oLanguage.sDecimal; 13103 return _isNumber( d, decimal, false, false ); 13104 } 13105 }, 13106 order: { 13107 pre: function (d, s) { 13108 var dp = s.oLanguage.sDecimal; 13109 return __numericReplace( d, dp ); 13110 } 13111 } 13112 }); 13113 13114 13115 13116 13117 var __numericReplace = function ( d, decimalPlace, re1, re2 ) { 13118 if ( d !== 0 && (!d || d === '-') ) { 13119 return -Infinity; 13120 } 13121 13122 var type = typeof d; 13123 13124 if (type === 'number' || type === 'bigint') { 13125 return d; 13126 } 13127 13128 // If a decimal place other than `.` is used, it needs to be given to the 13129 // function so we can detect it and replace with a `.` which is the only 13130 // decimal place Javascript recognises - it is not locale aware. 13131 if ( decimalPlace ) { 13132 d = _numToDecimal( d, decimalPlace ); 13133 } 13134 13135 if ( d.replace ) { 13136 if ( re1 ) { 13137 d = d.replace( re1, '' ); 13138 } 13139 13140 if ( re2 ) { 13141 d = d.replace( re2, '' ); 13142 } 13143 } 13144 13145 return d * 1; 13146 }; 13147 13148 13149 $.extend( true, DataTable.ext.renderer, { 13150 footer: { 13151 _: function ( settings, cell, classes ) {
13152 cell.addClass(classes.tfoot.cell); 13153 } 13154 }, 13155 13156 header: { 13157 _: function ( settings, cell, classes ) { 13158 cell.addClass(classes.thead.cell); 13159 13160 if (! settings.oFeatures.bSort) { 13161 cell.addClass(classes.order.none); 13162 } 13163 13164 var titleRow = settings.titleRow; 13165 var headerRows = cell.closest('thead').find('tr'); 13166 var rowIdx = cell.parent().index(); 13167 13168 // Conditions to not apply the ordering icons 13169 if ( 13170 // Cells and rows which have the attribute to disable the icons 13171 cell.attr('data-dt-order') === 'disable' || 13172 cell.parent().attr('data-dt-order') === 'disable' || 13173 13174 // titleRow support, for defining a specific row in the header 13175 (titleRow === true && rowIdx !== 0) || 13176 (titleRow === false && rowIdx !== headerRows.length - 1) || 13177 (typeof titleRow === 'number' && rowIdx !== titleRow) 13178 ) { 13179 return; 13180 } 13181 13182 // No additional mark-up required 13183 // Attach a sort listener to update on sort - note that using the 13184 // `DT` namespace will allow the event to be removed automatically 13185 // on destroy, while the `dt` namespaced event is the one we are 13186 // listening for 13187 $(settings.nTable).on( 'order.dt.DT column-visibility.dt.DT', function ( e, ctx, column ) { 13188 if ( settings !== ctx ) { // need to check this this is the host 13189 return; // table, not a nested one 13190 } 13191 13192 var sorting = ctx.sortDetails; 13193
13194 if (! sorting) { 13195 return; 13196 } 13197 13198 var orderedColumns = _pluck(sorting, 'col'); 13199 13200 // This handler is only needed on column visibility if the column is part of the 13201 // ordering. If it isn't, then we can bail out to save performance. It could be a 13202 // separate event handler, but this is a balance between code reuse / size and performance 13203 // console.log(e, e.name, column, orderedColumns, orderedColumns.includes(column)) 13204 if (e.type === 'column-visibility' && ! orderedColumns.includes(column)) { 13205 return; 13206 } 13207 13208 var i; 13209 var orderClasses = classes.order; 13210 var columns = ctx.api.columns( cell ); 13211 var col = settings.aoColumns[columns.flatten()[0]]; 13212 var orderable = columns.orderable().includes(true); 13213 var ariaType = ''; 13214 var indexes = columns.indexes(); 13215 var sortDirs = columns.orderable(true).flatten(); 13216 var tabIndex = settings.iTabIndex; 13217 var canOrder = ctx.orderHandler && orderable; 13218 13219 cell 13220 .removeClass( 13221 orderClasses.isAsc +' '+ 13222 orderClasses.isDesc 13223 ) 13224 .toggleClass( orderClasses.none, ! orderable ) 13225 .toggleClass( orderClasses.canAsc, canOrder && sortDirs.includes('asc') ) 13226 .toggleClass( orderClasses.canDesc, canOrder && sortDirs.includes('desc') ); 13227 13228 // Determine if all of the columns that this cell covers are included in the 13229 // current ordering 13230 var isOrdering = true; 13231 13232 for (i=0; i<indexes.length; i++) { 13233 if (! orderedColumns.includes(indexes[i])) { 13234 isOrdering = false; 13235 } 13236 } 13237 13238 if ( isOrdering ) { 13239 // Get the ordering direction for the columns under this cell 13240 // Note that it is possible for a cell to be asc and desc sorting 13241 // (column spanning cells) 13242 var orderDirs = columns.order(); 13243 13244 cell.addClass( 13245 orderDirs.includes('asc') ? orderClasses.isAsc : '' + 13246 orderDirs.includes('desc') ? orderClasses.isDesc : '' 13247 ); 13248 } 13249 13250 // Find the first visible column that has ordering applied to it - it get's 13251 // the aria information, as the ARIA spec says that only one column should 13252 // be marked with aria-sort 13253 var firstVis = -1; // column index 13254 13255 for (i=0; i<orderedColumns.length; i++) { 13256 if (settings.aoColumns[orderedColumns[i]].bVisible) { 13257 firstVis = orderedColumns[i]; 13258 break; 13259 } 13260 } 13261 13262 if (indexes[0] == firstVis) { 13263 var firstSort = sorting[0]; 13264 var sortOrder = col.asSorting; 13265 13266 cell.attr('aria-sort', firstSort.dir === 'asc' ? 'ascending' : 'descending'); 13267 13268 // Determine if the next click will remove sorting or change the sort 13269 ariaType = ! sortOrder[firstSort.index + 1] ? 'Remove' : 'Reverse'; 13270 } 13271 else { 13272 cell.removeAttr('aria-sort'); 13273 } 13274 13275 // Make the headers tab-able for keyboard navigation 13276 if (orderable) { 13277 var orderSpan = cell.find('.dt-column-order'); 13278 13279 orderSpan 13280 .attr('role', 'button') 13281 .attr('aria-label', orderable 13282 ? col.ariaTitle + ctx.api.i18n('oAria.orderable' + ariaType) 13283 : col.ariaTitle 13284 ); 13285 13286 if (tabIndex !== -1) { 13287 orderSpan.attr('tabindex', tabIndex); 13288 } 13289 } 13290 } ); 13291 } 13292 }, 13293 13294 layout: { 13295 _: function ( settings, container, items ) { 13296 var classes = settings.oClasses.layout; 13297 var row = $('<div/>') 13298 .attr('id', items.id || null) 13299 .addClass(items.className || classes.row) 13300 .appendTo( container ); 13301 13302 DataTable.ext.renderer.layout._forLayoutRow(items, function (key, val) { 13303 if (key === 'id' || key === 'className') { 13304 return; 13305 } 13306 13307 var klass = ''; 13308 13309 if (val.table) {
13310 row.addClass(classes.tableRow); 13311 klass += classes.tableCell + ' '; 13312 } 13313 13314 if (key === 'start') { 13315 klass += classes.start; 13316 } 13317 else if (key === 'end') { 13318 klass += classes.end; 13319 } 13320 else { 13321 klass += classes.full; 13322 } 13323 13324 $('<div/>') 13325 .attr({ 13326 id: val.id || null, 13327 "class": val.className 13328 ? val.className 13329 : classes.cell + ' ' + klass 13330 }) 13331 .append( val.contents ) 13332 .appendTo( row ); 13333 }); 13334 }, 13335 13336 // Shared for use by the styling frameworks 13337 _forLayoutRow: function (items, fn) { 13338 // As we are inserting dom elements, we need start / end in a 13339 // specific order, this function is used for sorting the layout 13340 // keys. 13341 var layoutEnum = function (x) { 13342 switch (x) { 13343 case '': return 0; 13344 case 'start': return 1; 13345 case 'end': return 2; 13346 default: return 3; 13347 } 13348 }; 13349 13350 Object 13351 .keys(items) 13352 .sort(function (a, b) { 13353 return layoutEnum(a) - layoutEnum(b); 13354 }) 13355 .forEach(function (key) { 13356 fn(key, items[key]); 13357 }); 13358 } 13359 } 13360 } ); 13361 13362 13363 DataTable.feature = {}; 13364 13365 // Third parameter is internal only! 13366 DataTable.feature.register = function ( name, cb, legacy ) { 13367 DataTable.ext.features[ name ] = cb; 13368 13369 if (legacy) { 13370 _ext.feature.push({ 13371 cFeature: legacy, 13372 fnInit: cb 13373 }); 13374 } 13375 }; 13376 13377 function _divProp(el, prop, val) { 13378 if (val) { 13379 el[prop] = val; 13380 } 13381 } 13382 13383 DataTable.feature.register( 'div', function ( settings, opts ) { 13384 var n = $('<div>')[0]; 13385 13386 if (opts) { 13387 _divProp(n, 'className', opts.className); 13388 _divProp(n, 'id', opts.id); 13389 _divProp(n, 'innerHTML', opts.html); 13390 _divProp(n, 'textContent', opts.text); 13391 } 13392 13393 return n; 13394 } ); 13395 13396 DataTable.feature.register( 'info', function ( settings, opts ) { 13397 // For compatibility with the legacy `info` top level option 13398 if (! settings.oFeatures.bInfo) { 13399 return null; 13400 } 13401 13402 var 13403 lang = settings.oLanguage, 13404 tid = settings.sTableId, 13405 n = $('<div/>', { 13406 'class': settings.oClasses.info.container, 13407 } ); 13408 13409 opts = $.extend({ 13410 callback: lang.fnInfoCallback, 13411 empty: lang.sInfoEmpty, 13412 postfix: lang.sInfoPostFix, 13413 search: lang.sInfoFiltered, 13414 text: lang.sInfo, 13415 }, opts); 13416 13417 13418 // Update display on each draw 13419 settings.aoDrawCallback.push(function (s) { 13420 _fnUpdateInfo(s, opts, n); 13421 }); 13422 13423 // For the first info display in the table, we add a callback and aria information. 13424 if (! settings._infoEl) { 13425 n.attr({ 13426 'aria-live': 'polite', 13427 id: tid+'_info', 13428 role: 'status' 13429 }); 13430 13431 // Table is described by our info div 13432 $(settings.nTable).attr( 'aria-describedby', tid+'_info' ); 13433 13434 settings._infoEl = n; 13435 } 13436 13437 return n; 13438 }, 'i' ); 13439 13440 /** 13441 * Update the information elements in the display 13442 * @param {object} settings dataTables settings object 13443 * @memberof DataTable#oApi 13444 */ 13445 function _fnUpdateInfo ( settings, opts, node ) 13446 { 13447 var 13448 start = settings._iDisplayStart+1, 13449 end = settings.fnDisplayEnd(), 13450 max = settings.fnRecordsTotal(), 13451 total = settings.fnRecordsDisplay(), 13452 out = total 13453 ? opts.text 13454 : opts.empty; 13455 13456 if ( total !== max ) { 13457 // Record set after filtering 13458 out += ' ' + opts.search; 13459 } 13460 13461 // Convert the macros 13462 out += opts.postfix; 13463 out = _fnMacros( settings, out ); 13464 13465 if ( opts.callback ) { 13466 out = opts.callback.call( settings.oInstance, 13467 settings, start, end, max, total, out 13468 ); 13469 } 13470 13471 node.html( out ); 13472 13473 _fnCallbackFire(settings, null, 'info', [settings, node[0], out]); 13474 } 13475 13476 var __searchCounter = 0; 13477 13478 // opts 13479 // - text 13480 // - placeholder 13481 DataTable.feature.register( 'search', function ( settings, opts ) { 13482 // Don't show the input if filtering isn't available on the table 13483 if (! settings.oFeatures.bFilter) { 13484 return null; 13485 } 13486 13487 var classes = settings.oClasses.search; 13488 var tableId = settings.sTableId; 13489 var language = settings.oLanguage; 13490 var previousSearch = settings.oPreviousSearch; 13491 var input = '<input type="search" class="'+classes.input+'"/>'; 13492 13493 opts = $.extend({ 13494 placeholder: language.sSearchPlaceholder, 13495 processing: false, 13496 text: language.sSearch 13497 }, opts); 13498
13499 // The _INPUT_ is optional - is appended if not present 13500 if (opts.text.indexOf('_INPUT_') === -1) { 13501 opts.text += '_INPUT_'; 13502 } 13503 13504 opts.text = _fnMacros(settings, opts.text); 13505 13506 // We can put the <input> outside of the label if it is at the start or end 13507 // which helps improve accessability (not all screen readers like implicit 13508 // for elements). 13509 var end = opts.text.match(/_INPUT_$/); 13510 var start = opts.text.match(/^_INPUT_/); 13511 var removed = opts.text.replace(/_INPUT_/, ''); 13512 var str = '<label>' + opts.text + '</label>'; 13513 13514 if (start) { 13515 str = '_INPUT_<label>' + removed + '</label>'; 13516 } 13517 else if (end) { 13518 str = '<label>' + removed + '</label>_INPUT_'; 13519 } 13520 13521 var filter = $('<div>') 13522 .addClass(classes.container) 13523 .append(str.replace(/_INPUT_/, input)); 13524 13525 // add for and id to label and input 13526 filter.find('label').attr('for', 'dt-search-' + __searchCounter); 13527 filter.find('input').attr('id', 'dt-search-' + __searchCounter); 13528 __searchCounter++; 13529 13530 var searchFn = function(event) { 13531 var val = this.value; 13532 13533 if(previousSearch.return && event.key !== "Enter") { 13534 return; 13535 } 13536 13537 /* Now do the filter */ 13538 if ( val != previousSearch.search ) { 13539 _fnProcessingRun(settings, opts.processing, function () { 13540 previousSearch.search = val; 13541 13542 _fnFilterComplete( settings, previousSearch ); 13543 13544 // Need to redraw, without resorting 13545 settings._iDisplayStart = 0; 13546 _fnDraw( settings ); 13547 }); 13548 } 13549 }; 13550 13551 var searchDelay = settings.searchDelay !== null ? 13552 settings.searchDelay : 13553 0; 13554 13555 var jqFilter = $('input', filter) 13556 .val( previousSearch.search ) 13557 .attr( 'placeholder', opts.placeholder ) 13558 .on( 13559 'keyup.DT search.DT input.DT paste.DT cut.DT', 13560 searchDelay ? 13561 DataTable.util.debounce( searchFn, searchDelay ) : 13562 searchFn 13563 ) 13564 .on( 'mouseup.DT', function(e) { 13565 // Edge fix! Edge 17 does not trigger anything other than mouse events when clicking 13566 // on the clear icon (Edge bug 17584515). This is safe in other browsers as `searchFn` 13567 // checks the value to see if it has changed. In other browsers it won't have. 13568 setTimeout( function () { 13569 searchFn.call(jqFilter[0], e); 13570 }, 10); 13571 } ) 13572 .on( 'keypress.DT', function(e) { 13573 /* Prevent form submission */ 13574 if ( e.keyCode == 13 ) { 13575 return false; 13576 } 13577 } ) 13578 .attr('aria-controls', tableId); 13579 13580 // Update the input elements whenever the table is filtered 13581 $(settings.nTable).on( 'search.dt.DT', function ( ev, s ) { 13582 if ( settings === s && jqFilter[0] !== document.activeElement ) { 13583 jqFilter.val( typeof previousSearch.search !== 'function' 13584 ? previousSearch.search 13585 : '' 13586 ); 13587 } 13588 } ); 13589 13590 return filter; 13591 }, 'f' ); 13592 13593 // opts 13594 // - type - button configuration 13595 // - buttons - number of buttons to show - must be odd 13596 DataTable.feature.register( 'paging', function ( settings, opts ) { 13597 // Don't show the paging input if the table doesn't have paging enabled 13598 if (! settings.oFeatures.bPaginate) { 13599 return null; 13600 } 13601 13602 opts = $.extend({ 13603 buttons: DataTable.ext.pager.numbers_length, 13604 type: settings.sPaginationType, 13605 boundaryNumbers: true, 13606 firstLast: true, 13607 previousNext: true, 13608 numbers: true 13609 }, opts); 13610 13611 var host = $('<div/>') 13612 .addClass(settings.oClasses.paging.container + (opts.type ? ' paging_' + opts.type : '')) 13613 .append( 13614 $('<nav>') 13615 .attr('aria-label', 'pagination') 13616 .addClass(settings.oClasses.paging.nav) 13617 ); 13618 var draw = function () { 13619 _pagingDraw(settings, host.children(), opts); 13620 }; 13621 13622 settings.aoDrawCallback.push(draw); 13623 13624 // Responsive redraw of paging control 13625 $(settings.nTable).on('column-sizing.dt.DT', draw); 13626 13627 return host; 13628 }, 'p' ); 13629 13630 /** 13631 * Dynamically create the button type array based on the configuration options. 13632 * This will only happen if the paging type is not defined. 13633 */ 13634 function _pagingDynamic(opts) { 13635 var out = []; 13636 13637 if (opts.numbers) { 13638 out.push('numbers'); 13639 } 13640 13641 if (opts.previousNext) { 13642 out.unshift('previous'); 13643 out.push('next'); 13644 } 13645 13646 if (opts.firstLast) { 13647 out.unshift('first'); 13648 out.push('last'); 13649 } 13650 13651 return out; 13652 } 13653 13654 function _pagingDraw(settings, host, opts) { 13655 if (! settings._bInitComplete) { 13656 return; 13657 } 13658 13659 var 13660 plugin = opts.type 13661 ? DataTable.ext.pager[ opts.type ] 13662 : _pagingDynamic, 13663 aria = settings.oLanguage.oAria.paginate || {}, 13664 start = settings._iDisplayStart, 13665 len = settings._iDisplayLength, 13666 visRecords = settings.fnRecordsDisplay(), 13667 all = len === -1, 13668 page = all ? 0 : Math.ceil( start / len ), 13669 pages = all ? 1 : Math.ceil( visRecords / len ), 13670 buttons = [], 13671 buttonEls = [], 13672 buttonsNested = plugin(opts) 13673 .map(function (val) { 13674 return val === 'numbers' 13675 ? _pagingNumbers(page, pages, opts.buttons, opts.boundaryNumbers) 13676 : val; 13677 }); 13678 13679 // .flat() would be better, but not supported in old Safari 13680 buttons = buttons.concat.apply(buttons, buttonsNested); 13681 13682 for (var i=0 ; i<buttons.length ; i++) { 13683 var button = buttons[i]; 13684 13685 var btnInfo = _pagingButtonInfo(settings, button, page, pages); 13686 var btn = _fnRenderer( settings, 'pagingButton' )( 13687 settings, 13688 button, 13689 btnInfo.display, 13690 btnInfo.active, 13691 btnInfo.disabled 13692 ); 13693 13694 var ariaLabel = typeof button === 'string' 13695 ? aria[ button ] 13696 : aria.number 13697 ? aria.number + (button+1) 13698 : null; 13699 13700 // Common attributes 13701 $(btn.clicker).attr({ 13702 'aria-controls': settings.sTableId, 13703 'aria-disabled': btnInfo.disabled ? 'true' : null, 13704 'aria-current': btnInfo.active ? 'page' : null, 13705 'aria-label': ariaLabel, 13706 'data-dt-idx': button, 13707 'tabIndex': btnInfo.disabled 13708 ? -1 13709 : settings.iTabIndex && btn.clicker[0].nodeName.toLowerCase() !== 'span' 13710 ? settings.iTabIndex 13711 : null, // `0` doesn't need a tabIndex since it is the default 13712 }); 13713 13714 if (typeof button !== 'number') { 13715 $(btn.clicker).addClass(button); 13716 } 13717 13718 _fnBindAction( 13719 btn.clicker, {action: button}, function(e) { 13720 e.preventDefault(); 13721 13722 _fnPageChange( settings, e.data.action, true ); 13723 } 13724 ); 13725 13726 buttonEls.push(btn.display); 13727 } 13728 13729 var wrapped = _fnRenderer(settings, 'pagingContainer')( 13730 settings, buttonEls 13731 ); 13732 13733 var activeEl = host.find(document.activeElement).data('dt-idx'); 13734 13735 host.empty().append(wrapped); 13736 13737 if ( activeEl !== undefined ) { 13738 host.find( '[data-dt-idx='+activeEl+']' ).trigger('focus'); 13739 } 13740 13741 // Responsive - check if the buttons are over two lines based on the 13742 // height of the buttons and the container. 13743 if (buttonEls.length) { 13744 var outerHeight = $(buttonEls[0]).outerHeight(); 13745 13746 if ( 13747 opts.buttons > 1 && // prevent infinite 13748 outerHeight > 0 && // will be 0 if hidden 13749 $(host).height() >= (outerHeight * 2) - 10 13750 ) { 13751 _pagingDraw(settings, host, $.extend({}, opts, { buttons: opts.buttons - 2 })); 13752 } 13753 } 13754 } 13755 13756 /** 13757 * Get properties for a button based on the current paging state of the table 13758 * 13759 * @param {*} settings DT settings object 13760 * @param {*} button The button type in question 13761 * @param {*} page Table's current page 13762 * @param {*} pages Number of pages 13763 * @returns Info object 13764 */ 13765 function _pagingButtonInfo(settings, button, page, pages) { 13766 var lang = settings.oLanguage.oPaginate; 13767 var o = { 13768 display: '', 13769 active: false, 13770 disabled: false 13771 }; 13772 13773 switch ( button ) { 13774 case 'ellipsis': 13775 o.display = '…'; 13776 break; 13777 13778 case 'first': 13779 o.display = lang.sFirst; 13780 13781 if (page === 0) { 13782 o.disabled = true; 13783 } 13784 break; 13785 13786 case 'previous': 13787 o.display = lang.sPrevious; 13788 13789 if ( page === 0 ) { 13790 o.disabled = true; 13791 } 13792 break; 13793 13794 case 'next': 13795 o.display = lang.sNext; 13796 13797 if ( pages === 0 || page === pages-1 ) { 13798 o.disabled = true; 13799 } 13800 break; 13801 13802 case 'last': 13803 o.display = lang.sLast; 13804 13805 if ( pages === 0 || page === pages-1 ) { 13806 o.disabled = true; 13807 } 13808 break; 13809 13810 default: 13811 if ( typeof button === 'number' ) { 13812 o.display = settings.fnFormatNumber( button + 1 ); 13813 13814 if (page === button) { 13815 o.active = true; 13816 } 13817 } 13818 break; 13819 } 13820 13821 return o; 13822 } 13823 13824 /** 13825 * Compute what number buttons to show in the paging control 13826 * 13827 * @param {*} page Current page 13828 * @param {*} pages Total number of pages 13829 * @param {*} buttons Target number of number buttons 13830 * @param {boolean} addFirstLast Indicate if page 1 and end should be included 13831 * @returns Buttons to show 13832 */ 13833 function _pagingNumbers ( page, pages, buttons, addFirstLast ) { 13834 var 13835 numbers = [], 13836 half = Math.floor(buttons / 2), 13837 before = addFirstLast ? 2 : 1, 13838 after = addFirstLast ? 1 : 0; 13839 13840 if ( pages <= buttons ) { 13841 numbers = _range(0, pages); 13842 } 13843 else if (buttons === 1) { 13844 // Single button - current page only 13845 numbers = [page]; 13846 } 13847 else if (buttons === 3) { 13848 // Special logic for just three buttons 13849 if (page <= 1) { 13850 numbers = [0, 1, 'ellipsis']; 13851 } 13852 else if (page >= pages - 2) { 13853 numbers = _range(pages-2, pages); 13854 numbers.unshift('ellipsis'); 13855 } 13856 else { 13857 numbers = ['ellipsis', page, 'ellipsis']; 13858 } 13859 } 13860 else if ( page <= half ) { 13861 numbers = _range(0, buttons-before); 13862 numbers.push('ellipsis'); 13863 13864 if (addFirstLast) { 13865 numbers.push(pages-1); 13866 } 13867 } 13868 else if ( page >= pages - 1 - half ) { 13869 numbers = _range(pages-(buttons-before), pages); 13870 numbers.unshift('ellipsis'); 13871 13872 if (addFirstLast) { 13873 numbers.unshift(0); 13874 } 13875 } 13876 else { 13877 numbers = _range(page-half+before, page+half-after); 13878 numbers.push('ellipsis'); 13879 numbers.unshift('ellipsis'); 13880 13881 if (addFirstLast) { 13882 numbers.push(pages-1); 13883 numbers.unshift(0); 13884 } 13885 } 13886 13887 return numbers; 13888 } 13889 13890 var __lengthCounter = 0; 13891 13892 // opts 13893 // - menu 13894 // - text 13895 DataTable.feature.register( 'pageLength', function ( settings, opts ) { 13896 var features = settings.oFeatures; 13897 13898 // For compatibility with the legacy `pageLength` top level option 13899 if (! features.bPaginate || ! features.bLengthChange) { 13900 return null; 13901 } 13902 13903 opts = $.extend({ 13904 menu: settings.aLengthMenu, 13905 text: settings.oLanguage.sLengthMenu 13906 }, opts); 13907 13908 var 13909 classes = settings.oClasses.length, 13910 tableId = settings.sTableId, 13911 menu = opts.menu, 13912 lengths = [], 13913 language = [], 13914 i; 13915 13916 // Options can be given in a number of ways 13917 if (Array.isArray( menu[0] )) { 13918 // Old 1.x style - 2D array 13919 lengths = menu[0]; 13920 language = menu[1]; 13921 } 13922 else { 13923 for ( i=0 ; i<menu.length ; i++ ) { 13924 // An object with different label and value 13925 if ($.isPlainObject(menu[i])) { 13926 lengths.push(menu[i].value); 13927 language.push(menu[i].label); 13928 } 13929 else { 13930 // Or just a number to display and use 13931 lengths.push(menu[i]); 13932 language.push(menu[i]); 13933 } 13934 } 13935 } 13936 13937 // We can put the <select> outside of the label if it is at the start or 13938 // end which helps improve accessability (not all screen readers like 13939 // implicit for elements). 13940 var end = opts.text.match(/_MENU_$/); 13941 var start = opts.text.match(/^_MENU_/); 13942 var removed = opts.text.replace(/_MENU_/, ''); 13943 var str = '<label>' + opts.text + '</label>'; 13944 13945 if (start) { 13946 str = '_MENU_<label>' + removed + '</label>'; 13947 } 13948 else if (end) { 13949 str = '<label>' + removed + '</label>_MENU_'; 13950 } 13951 13952 // Wrapper element - use a span as a holder for where the select will go 13953 var tmpId = 'tmp-' + (+new Date())
13954 var div = $('<div/>') 13955 .addClass( classes.container ) 13956 .append( 13957 str.replace( '_MENU_', '<span id="'+tmpId+'"></span>' ) 13958 ); 13959 13960 // Save text node content for macro updating 13961 var textNodes = []; 13962 Array.prototype.slice.call(div.find('label')[0].childNodes).forEach(function (el) { 13963 if (el.nodeType === Node.TEXT_NODE) { 13964 textNodes.push({ 13965 el: el, 13966 text: el.textContent 13967 }); 13968 } 13969 }); 13970 13971 // Update the label text in case it has an entries value 13972 var updateEntries = function (len) { 13973 textNodes.forEach(function (node) { 13974 node.el.textContent = _fnMacros(settings, node.text, len); 13975 }); 13976 } 13977 13978 // Next, the select itself, along with the options 13979 var select = $('<select/>', { 13980 'aria-controls': tableId, 13981 'class': classes.select 13982 } ); 13983 13984 for ( i=0 ; i<lengths.length ; i++ ) { 13985 // Attempt to look up the length from the i18n options 13986 var label = settings.api.i18n('lengthLabels.' + lengths[i], null); 13987 13988 if (label === null) { 13989 // If not present, fallback to old style 13990 label = typeof language[i] === 'number' ? 13991 settings.fnFormatNumber( language[i] ) : 13992 language[i]; 13993 } 13994 13995 select[0][ i ] = new Option(label, lengths[i]); 13996 } 13997 13998 // add for and id to label and input 13999 div.find('label').attr('for', 'dt-length-' + __lengthCounter); 14000 select.attr('id', 'dt-length-' + __lengthCounter); 14001 __lengthCounter++; 14002 14003 // Swap in the select list 14004 div.find('#' + tmpId).replaceWith(select); 14005 14006 // Can't use `select` variable as user might provide their own and the 14007 // reference is broken by the use of outerHTML 14008 $('select', div) 14009 .val( settings._iDisplayLength ) 14010 .on( 'change.DT', function() { 14011 _fnLengthChange( settings, $(this).val() ); 14012 _fnDraw( settings ); 14013 } ); 14014 14015 // Update node value whenever anything changes the table's length 14016 $(settings.nTable).on( 'length.dt.DT', function (e, s, len) { 14017 if ( settings === s ) { 14018 $('select', div).val( len ); 14019 14020 // Resolve plurals in the text for the new length 14021 updateEntries(len); 14022 } 14023 } ); 14024 14025 updateEntries(settings._iDisplayLength); 14026 14027 return div; 14028 }, 'l' ); 14029 14030 // jQuery access 14031 $.fn.dataTable = DataTable; 14032 14033 // Provide access to the host jQuery object (circular reference) 14034 DataTable.$ = $; 14035 14036 // Legacy aliases 14037 $.fn.dataTableSettings = DataTable.settings; 14038 $.fn.dataTableExt = DataTable.ext; 14039 14040 // With a capital `D` we return a DataTables API instance rather than a 14041 // jQuery object 14042 $.fn.DataTable = function ( opts ) { 14043 return $(this).dataTable( opts ).api(); 14044 }; 14045 14046 // All properties that are available to $.fn.dataTable should also be 14047 // available on $.fn.DataTable 14048 $.each( DataTable, function ( prop, val ) { 14049 $.fn.DataTable[ prop ] = val; 14050 } ); 14051 14052 return DataTable; 14053}));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.