vendor: 5,308 bytes, lines 1-190
1/*! DataTables 2.0.7 2 * © SpryMedia Ltd - datatables.net/license 3 */ 4 5/** 6 * @summary DataTables 7 * @description Paginate, search and order HTML tables 8 * @version 2.0.7 9 * @author SpryMedia Ltd 10 * @contact www.datatables.net 11 * @copyright SpryMedia Ltd. 12 * 13 * This source file is free software, available under the following license: 14 * MIT license - https://datatables.net/license 15 * 16 * This source file is distributed in the hope that it will be useful, but 17 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 18 * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details. 19 * 20 * For details please refer to: https://www.datatables.net 21 */ 22 23(function( factory ) { 24 "use strict"; 25 26 if ( typeof define === 'function' && define.amd ) { 27 // AMD 28 define( ['jquery'], function ( $ ) { 29 return factory( $, window, document ); 30 } ); 31 } 32 else if ( typeof exports === 'object' ) { 33 // CommonJS 34 // jQuery's factory checks for a global window - if it isn't present then it 35 // returns a factory function that expects the window object 36 var jq = require('jquery'); 37 38 if (typeof window === 'undefined') { 39 module.exports = function (root, $) { 40 if ( ! root ) { 41 // CommonJS environments without a window global must pass a 42 // root. This will give an error otherwise 43 root = window; 44 } 45 46 if ( ! $ ) { 47 $ = jq( root ); 48 } 49 50 return factory( $, root, root.document ); 51 }; 52 } 53 else { 54 module.exports = factory( jq, window, window.document ); 55 } 56 } 57 else { 58 // Browser 59 window.DataTable = factory( jQuery, window, document ); 60 } 61}(function( $, window, document ) { 62 "use strict"; 63 64 65 var DataTable = function ( selector, options ) 66 { 67 // Check if called with a window or jQuery object for DOM less applications 68 // This is for backwards compatibility 69 if (DataTable.factory(selector, options)) { 70 return DataTable; 71 } 72 73 // When creating with `new`, create a new DataTable, returning the API instance 74 if (this instanceof DataTable) { 75 return $(selector).DataTable(options); 76 } 77 else { 78 // Argument switching 79 options = selector; 80 } 81 82 var _that = this; 83 var emptyInit = options === undefined; 84 var len = this.length; 85 86 if ( emptyInit ) { 87 options = {}; 88 } 89 90 // Method to get DT API instance from jQuery object 91 this.api = function () 92 { 93 return new _Api( this ); 94 }; 95 96 this.each(function() { 97 // For each initialisation we want to give it a clean initialisation 98 // object that can be bashed around 99 var o = {}; 100 var oInit = len > 1 ? // optimisation for single table case 101 _fnExtend( o, options, true ) : 102 options; 103 104 105 var i=0, iLen; 106 var sId = this.getAttribute( 'id' ); 107 var bInitHandedOff = false; 108 var defaults = DataTable.defaults; 109 var $this = $(this); 110 111 112 /* Sanity check */ 113 if ( this.nodeName.toLowerCase() != 'table' ) 114 { 115 _fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 ); 116 return; 117 } 118 119 $(this).trigger( 'options.dt', oInit ); 120 121 /* Backwards compatibility for the defaults */ 122 _fnCompatOpts( defaults ); 123 _fnCompatCols( defaults.column ); 124 125 /* Convert the camel-case defaults to Hungarian */ 126 _fnCamelToHungarian( defaults, defaults, true ); 127 _fnCamelToHungarian( defaults.column, defaults.column, true ); 128 129 /* Setting up the initialisation object */ 130 _fnCamelToHungarian( defaults, $.extend( oInit, $this.data() ), true ); 131 132 133 134 /* Check to see if we are re-initialising a table */ 135 var allSettings = DataTable.settings; 136 for ( i=0, iLen=allSettings.length ; i<iLen ; i++ ) 137 { 138 var s = allSettings[i]; 139 140 /* Base check on table node */ 141 if ( 142 s.nTable == this || 143 (s.nTHead && s.nTHead.parentNode == this) || 144 (s.nTFoot && s.nTFoot.parentNode == this) 145 ) { 146 var bRetrieve = oInit.bRetrieve !== undefined ? oInit.bRetrieve : defaults.bRetrieve; 147 var bDestroy = oInit.bDestroy !== undefined ? oInit.bDestroy : defaults.bDestroy; 148 149 if ( emptyInit || bRetrieve ) 150 { 151 return s.oInstance; 152 } 153 else if ( bDestroy ) 154 { 155 new DataTable.Api(s).destroy(); 156 break; 157 } 158 else 159 { 160 _fnLog( s, 0, 'Cannot reinitialise DataTable', 3 ); 161 return; 162 } 163 } 164 165 /* If the element we are initialising has the same ID as a table which was previously 166 * initialised, but the table nodes don't match (from before) then we destroy the old 167 * instance by simply deleting it. This is under the assumption that the table has been 168 * destroyed by other methods. Anyone using non-id selectors will need to do this manually 169 */ 170 if ( s.sTableId == this.id ) 171 { 172 allSettings.splice( i, 1 ); 173 break; 174 } 175 } 176 177 /* Ensure the table has an ID - required for accessibility */ 178 if ( sId === null || sId === "" ) 179 { 180 sId = "DataTables_Table_"+(DataTable.ext._unique++); 181 this.id = sId; 182 } 183 184 /* Create the settings object for this table and set some of the default parameters */ 185 var oSettings = $.extend( true, {}, DataTable.models.oSettings, { 186 "sDestroyWidth": $this[0].style.width, 187 "sInstance": sId, 188 "sTableId": sId, 189 colgroup: $('<colgroup>').prependTo(this), 190 fastData: function (row, column, type) {
191 return _fnGetCellData(oSettings, row, column, type); 192 } 193 } ); 194 oSettings.nTable = this; 195 oSettings.oInit = oInit; 196 197 allSettings.push( oSettings ); 198 199 // Make a single API instance available for internal handling 200 oSettings.api = new _Api( oSettings ); 201 202 // Need to add the instance after the instance after the settings object has been added 203 // to the settings array, so we can self reference the table instance if more than one 204 oSettings.oInstance = (_that.length===1) ? _that : $this.dataTable(); 205 206 // Backwards compatibility, before we apply all the defaults 207 _fnCompatOpts( oInit ); 208 209 // If the length menu is given, but the init display length is not, use the length menu 210 if ( oInit.aLengthMenu && ! oInit.iDisplayLength ) 211 { 212 oInit.iDisplayLength = Array.isArray(oInit.aLengthMenu[0]) 213 ? oInit.aLengthMenu[0][0] 214 : $.isPlainObject( oInit.aLengthMenu[0] ) 215 ? oInit.aLengthMenu[0].value 216 : oInit.aLengthMenu[0]; 217 } 218 219 // Apply the defaults and init options to make a single init object will all 220 // options defined from defaults and instance options. 221 oInit = _fnExtend( $.extend( true, {}, defaults ), oInit ); 222 223 224 // Map the initialisation options onto the settings object 225 _fnMap( oSettings.oFeatures, oInit, [ 226 "bPaginate", 227 "bLengthChange", 228 "bFilter", 229 "bSort", 230 "bSortMulti", 231 "bInfo", 232 "bProcessing", 233 "bAutoWidth", 234 "bSortClasses", 235 "bServerSide", 236 "bDeferRender" 237 ] ); 238 _fnMap( oSettings, oInit, [ 239 "ajax", 240 "fnFormatNumber", 241 "sServerMethod", 242 "aaSorting", 243 "aaSortingFixed", 244 "aLengthMenu", 245 "sPaginationType", 246 "iStateDuration", 247 "bSortCellsTop", 248 "iTabIndex", 249 "sDom", 250 "fnStateLoadCallback", 251 "fnStateSaveCallback", 252 "renderer", 253 "searchDelay", 254 "rowId", 255 "caption", 256 "layout", 257 [ "iCookieDuration", "iStateDuration" ], // backwards compat 258 [ "oSearch", "oPreviousSearch" ], 259 [ "aoSearchCols", "aoPreSearchCols" ], 260 [ "iDisplayLength", "_iDisplayLength" ] 261 ] ); 262 _fnMap( oSettings.oScroll, oInit, [ 263 [ "sScrollX", "sX" ], 264 [ "sScrollXInner", "sXInner" ], 265 [ "sScrollY", "sY" ], 266 [ "bScrollCollapse", "bCollapse" ] 267 ] ); 268 _fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" ); 269 270 /* Callback functions which are array driven */ 271 _fnCallbackReg( oSettings, 'aoDrawCallback', oInit.fnDrawCallback ); 272 _fnCallbackReg( oSettings, 'aoStateSaveParams', oInit.fnStateSaveParams ); 273 _fnCallbackReg( oSettings, 'aoStateLoadParams', oInit.fnStateLoadParams ); 274 _fnCallbackReg( oSettings, 'aoStateLoaded', oInit.fnStateLoaded ); 275 _fnCallbackReg( oSettings, 'aoRowCallback', oInit.fnRowCallback ); 276 _fnCallbackReg( oSettings, 'aoRowCreatedCallback', oInit.fnCreatedRow ); 277 _fnCallbackReg( oSettings, 'aoHeaderCallback', oInit.fnHeaderCallback ); 278 _fnCallbackReg( oSettings, 'aoFooterCallback', oInit.fnFooterCallback ); 279 _fnCallbackReg( oSettings, 'aoInitComplete', oInit.fnInitComplete ); 280 _fnCallbackReg( oSettings, 'aoPreDrawCallback', oInit.fnPreDrawCallback ); 281 282 oSettings.rowIdFn = _fnGetObjectDataFn( oInit.rowId ); 283 284 /* Browser support detection */ 285 _fnBrowserDetect( oSettings ); 286 287 var oClasses = oSettings.oClasses; 288 289 $.extend( oClasses, DataTable.ext.classes, oInit.oClasses ); 290 $this.addClass( oClasses.table ); 291 292 if (! oSettings.oFeatures.bPaginate) { 293 oInit.iDisplayStart = 0; 294 } 295 296 if ( oSettings.iInitDisplayStart === undefined ) 297 { 298 /* Display start point, taking into account the save saving */ 299 oSettings.iInitDisplayStart = oInit.iDisplayStart; 300 oSettings._iDisplayStart = oInit.iDisplayStart; 301 } 302 303 /* Language definitions */ 304 var oLanguage = oSettings.oLanguage; 305 $.extend( true, oLanguage, oInit.oLanguage ); 306 307 if ( oLanguage.sUrl ) 308 { 309 /* Get the language definitions from a file - because this Ajax call makes the language 310 * get async to the remainder of this function we use bInitHandedOff to indicate that 311 * _fnInitialise will be fired by the returned Ajax handler, rather than the constructor 312 */ 313 $.ajax( { 314 dataType: 'json', 315 url: oLanguage.sUrl, 316 success: function ( json ) { 317 _fnCamelToHungarian( defaults.oLanguage, json ); 318 $.extend( true, oLanguage, json, oSettings.oInit.oLanguage ); 319 320 _fnCallbackFire( oSettings, null, 'i18n', [oSettings], true); 321 _fnInitialise( oSettings ); 322 }, 323 error: function () { 324 // Error occurred loading language file 325 _fnLog( oSettings, 0, 'i18n file loading error', 21 ); 326 327 // continue on as best we can 328 _fnInitialise( oSettings ); 329 } 330 } ); 331 bInitHandedOff = true; 332 } 333 else { 334 _fnCallbackFire( oSettings, null, 'i18n', [oSettings]); 335 } 336 337 /* 338 * Columns 339 * See if we should load columns automatically or use defined ones 340 */ 341 var columnsInit = []; 342 var thead = this.getElementsByTagName('thead'); 343 var initHeaderLayout = _fnDetectHeader( oSettings, thead[0] ); 344 345 // If we don't have a columns array, then generate one with nulls 346 if ( oInit.aoColumns ) { 347 columnsInit = oInit.aoColumns; 348 } 349 else if ( initHeaderLayout.length ) { 350 for ( i=0, iLen=initHeaderLayout[0].length ; i<iLen ; i++ ) { 351 columnsInit.push( null ); 352 } 353 } 354 355 // Add the columns 356 for ( i=0, iLen=columnsInit.length ; i<iLen ; i++ ) { 357 _fnAddColumn( oSettings ); 358 } 359 360 // Apply the column definitions 361 _fnApplyColumnDefs( oSettings, oInit.aoColumnDefs, columnsInit, initHeaderLayout, function (iCol, oDef) { 362 _fnColumnOptions( oSettings, iCol, oDef ); 363 } ); 364 365 /* HTML5 attribute detection - build an mData object automatically if the 366 * attributes are found 367 */ 368 var rowOne = $this.children('tbody').find('tr').eq(0); 369 370 if ( rowOne.length ) { 371 var a = function ( cell, name ) { 372 return cell.getAttribute( 'data-'+name ) !== null ? name : null; 373 }; 374 375 $( rowOne[0] ).children('th, td').each( function (i, cell) { 376 var col = oSettings.aoColumns[i]; 377 378 if (! col) { 379 _fnLog( oSettings, 0, 'Incorrect column count', 18 ); 380 } 381 382 if ( col.mData === i ) { 383 var sort = a( cell, 'sort' ) || a( cell, 'order' ); 384 var filter = a( cell, 'filter' ) || a( cell, 'search' ); 385 386 if ( sort !== null || filter !== null ) { 387 col.mData = { 388 _: i+'.display', 389 sort: sort !== null ? i+'.@data-'+sort : undefined, 390 type: sort !== null ? i+'.@data-'+sort : undefined, 391 filter: filter !== null ? i+'.@data-'+filter : undefined 392 }; 393 col._isArrayHost = true; 394 395 _fnColumnOptions( oSettings, i ); 396 } 397 } 398 } ); 399 } 400 401 var features = oSettings.oFeatures; 402 var loadedInit = function () { 403 /* 404 * Sorting 405 * @todo For modularisation (1.11) this needs to do into a sort start up handler 406 */ 407 408 // If aaSorting is not defined, then we use the first indicator in asSorting 409 // in case that has been altered, so the default sort reflects that option 410 if ( oInit.aaSorting === undefined ) { 411 var sorting = oSettings.aaSorting; 412 for ( i=0, iLen=sorting.length ; i<iLen ; i++ ) { 413 sorting[i][1] = oSettings.aoColumns[ i ].asSorting[0]; 414 } 415 } 416 417 /* Do a first pass on the sorting classes (allows any size changes to be taken into 418 * account, and also will apply sorting disabled classes if disabled 419 */ 420 _fnSortingClasses( oSettings ); 421 422 _fnCallbackReg( oSettings, 'aoDrawCallback', function () { 423 if ( oSettings.bSorted || _fnDataSource( oSettings ) === 'ssp' || features.bDeferRender ) { 424 _fnSortingClasses( oSettings ); 425 } 426 } ); 427 428 429 /* 430 * Final init 431 * Cache the header, body and footer as required, creating them if needed 432 */ 433 var caption = $this.children('caption'); 434 435 if ( oSettings.caption ) { 436 if ( caption.length === 0 ) { 437 caption = $('<caption/>').appendTo( $this ); 438 } 439 440 caption.html( oSettings.caption ); 441 } 442 443 // Store the caption side, so we can remove the element from the document 444 // when creating the element 445 if (caption.length) { 446 caption[0]._captionSide = caption.css('caption-side'); 447 oSettings.captionNode = caption[0]; 448 } 449 450 if ( thead.length === 0 ) { 451 thead = $('<thead/>').appendTo($this); 452 } 453 oSettings.nTHead = thead[0]; 454 $('tr', thead).addClass(oClasses.thead.row); 455 456 var tbody = $this.children('tbody'); 457 if ( tbody.length === 0 ) { 458 tbody = $('<tbody/>').insertAfter(thead); 459 } 460 oSettings.nTBody = tbody[0]; 461 462 var tfoot = $this.children('tfoot'); 463 if ( tfoot.length === 0 ) { 464 // If we are a scrolling table, and no footer has been given, then we need to create 465 // a tfoot element for the caption element to be appended to 466 tfoot = $('<tfoot/>').appendTo($this); 467 } 468 oSettings.nTFoot = tfoot[0]; 469 $('tr', tfoot).addClass(oClasses.tfoot.row); 470 471 // Check if there is data passing into the constructor 472 if ( oInit.aaData ) { 473 for ( i=0 ; i<oInit.aaData.length ; i++ ) { 474 _fnAddData( oSettings, oInit.aaData[ i ] ); 475 } 476 } 477 else if ( _fnDataSource( oSettings ) == 'dom' ) { 478 // Grab the data from the page 479 _fnAddTr( oSettings, $(oSettings.nTBody).children('tr') ); 480 } 481 482 /* Copy the data index array */ 483 oSettings.aiDisplay = oSettings.aiDisplayMaster.slice(); 484 485 /* Initialisation complete - table can be drawn */
486 oSettings.bInitialised = true; 487 488 /* Check if we need to initialise the table (it might not have been handed off to the 489 * language processor) 490 */ 491 if ( bInitHandedOff === false ) { 492 _fnInitialise( oSettings ); 493 } 494 }; 495 496 /* Must be done after everything which can be overridden by the state saving! */ 497 _fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState ); 498 499 if ( oInit.bStateSave ) 500 { 501 features.bStateSave = true; 502 _fnLoadState( oSettings, oInit, loadedInit ); 503 } 504 else { 505 loadedInit(); 506 } 507 508 } ); 509 _that = null; 510 return this; 511 }; 512 513 514 515 /** 516 * DataTables extensions 517 * 518 * This namespace acts as a collection area for plug-ins that can be used to 519 * extend DataTables capabilities. Indeed many of the build in methods 520 * use this method to provide their own capabilities (sorting methods for 521 * example). 522 * 523 * Note that this namespace is aliased to `jQuery.fn.dataTableExt` for legacy 524 * reasons 525 * 526 * @namespace 527 */ 528 DataTable.ext = _ext = { 529 /** 530 * Buttons. For use with the Buttons extension for DataTables. This is 531 * defined here so other extensions can define buttons regardless of load 532 * order. It is _not_ used by DataTables core. 533 * 534 * @type object 535 * @default {} 536 */ 537 buttons: {}, 538 539 540 /** 541 * Element class names 542 * 543 * @type object 544 * @default {} 545 */ 546 classes: {}, 547 548 549 /** 550 * DataTables build type (expanded by the download builder) 551 * 552 * @type string 553 */ 554 builder: "-source-", 555 556 557 /** 558 * Error reporting. 559 * 560 * How should DataTables report an error. Can take the value 'alert', 561 * 'throw', 'none' or a function. 562 * 563 * @type string|function 564 * @default alert 565 */ 566 errMode: "alert", 567 568 569 /** 570 * Legacy so v1 plug-ins don't throw js errors on load 571 */ 572 feature: [], 573 574 /** 575 * Feature plug-ins. 576 * 577 * This is an object of callbacks which provide the features for DataTables 578 * to be initialised via the `layout` option. 579 */ 580 features: {}, 581 582 583 /** 584 * Row searching. 585 * 586 * This method of searching is complimentary to the default type based 587 * searching, and a lot more comprehensive as it allows you complete control 588 * over the searching logic. Each element in this array is a function 589 * (parameters described below) that is called for every row in the table, 590 * and your logic decides if it should be included in the searching data set 591 * or not. 592 * 593 * Searching functions have the following input parameters: 594 * 595 * 1. `{object}` DataTables settings object: see 596 * {@link DataTable.models.oSettings} 597 * 2. `{array|object}` Data for the row to be processed (same as the 598 * original format that was passed in as the data source, or an array 599 * from a DOM data source 600 * 3. `{int}` Row index ({@link DataTable.models.oSettings.aoData}), which 601 * can be useful to retrieve the `TR` element if you need DOM interaction. 602 * 603 * And the following return is expected: 604 * 605 * * {boolean} Include the row in the searched result set (true) or not 606 * (false) 607 * 608 * Note that as with the main search ability in DataTables, technically this 609 * is "filtering", since it is subtractive. However, for consistency in 610 * naming we call it searching here. 611 * 612 * @type array 613 * @default [] 614 * 615 * @example 616 * // The following example shows custom search being applied to the 617 * // fourth column (i.e. the data[3] index) based on two input values 618 * // from the end-user, matching the data in a certain range. 619 * $.fn.dataTable.ext.search.push( 620 * function( settings, data, dataIndex ) { 621 * var min = document.getElementById('min').value * 1; 622 * var max = document.getElementById('max').value * 1; 623 * var version = data[3] == "-" ? 0 : data[3]*1; 624 * 625 * if ( min == "" && max == "" ) { 626 * return true; 627 * } 628 * else if ( min == "" && version < max ) { 629 * return true; 630 * } 631 * else if ( min < version && "" == max ) { 632 * return true; 633 * } 634 * else if ( min < version && version < max ) { 635 * return true; 636 * } 637 * return false;
638 * } 639 * ); 640 */ 641 search: [], 642 643 644 /** 645 * Selector extensions 646 * 647 * The `selector` option can be used to extend the options available for the 648 * selector modifier options (`selector-modifier` object data type) that 649 * each of the three built in selector types offer (row, column and cell + 650 * their plural counterparts). For example the Select extension uses this 651 * mechanism to provide an option to select only rows, columns and cells 652 * that have been marked as selected by the end user (`{selected: true}`), 653 * which can be used in conjunction with the existing built in selector 654 * options. 655 * 656 * Each property is an array to which functions can be pushed. The functions 657 * take three attributes: 658 * 659 * * Settings object for the host table 660 * * Options object (`selector-modifier` object type) 661 * * Array of selected item indexes 662 * 663 * The return is an array of the resulting item indexes after the custom 664 * selector has been applied. 665 * 666 * @type object 667 */ 668 selector: { 669 cell: [], 670 column: [], 671 row: [] 672 }, 673 674 675 /** 676 * Legacy configuration options. Enable and disable legacy options that 677 * are available in DataTables. 678 * 679 * @type object 680 */ 681 legacy: { 682 /** 683 * Enable / disable DataTables 1.9 compatible server-side processing 684 * requests 685 * 686 * @type boolean 687 * @default null 688 */ 689 ajax: null 690 }, 691 692 693 /** 694 * Pagination plug-in methods. 695 * 696 * Each entry in this object is a function and defines which buttons should 697 * be shown by the pagination rendering method that is used for the table: 698 * {@link DataTable.ext.renderer.pageButton}. The renderer addresses how the 699 * buttons are displayed in the document, while the functions here tell it 700 * what buttons to display. This is done by returning an array of button 701 * descriptions (what each button will do). 702 * 703 * Pagination types (the four built in options and any additional plug-in 704 * options defined here) can be used through the `paginationType` 705 * initialisation parameter. 706 * 707 * The functions defined take two parameters: 708 * 709 * 1. `{int} page` The current page index 710 * 2. `{int} pages` The number of pages in the table 711 * 712 * Each function is expected to return an array where each element of the 713 * array can be one of: 714 * 715 * * `first` - Jump to first page when activated 716 * * `last` - Jump to last page when activated 717 * * `previous` - Show previous page when activated 718 * * `next` - Show next page when activated 719 * * `{int}` - Show page of the index given 720 * * `{array}` - A nested array containing the above elements to add a 721 * containing 'DIV' element (might be useful for styling). 722 * 723 * Note that DataTables v1.9- used this object slightly differently whereby 724 * an object with two functions would be defined for each plug-in. That 725 * ability is still supported by DataTables 1.10+ to provide backwards 726 * compatibility, but this option of use is now decremented and no longer 727 * documented in DataTables 1.10+. 728 * 729 * @type object 730 * @default {} 731 * 732 * @example 733 * // Show previous, next and current page buttons only 734 * $.fn.dataTableExt.oPagination.current = function ( page, pages ) { 735 * return [ 'previous', page, 'next' ]; 736 * }; 737 */ 738 pager: {}, 739 740 741 renderer: { 742 pageButton: {}, 743 header: {} 744 }, 745 746 747 /** 748 * Ordering plug-ins - custom data source 749 * 750 * The extension options for ordering of data available here is complimentary 751 * to the default type based ordering that DataTables typically uses. It 752 * allows much greater control over the the data that is being used to 753 * order a column, but is necessarily therefore more complex. 754 * 755 * This type of ordering is useful if you want to do ordering based on data 756 * live from the DOM (for example the contents of an 'input' element) rather 757 * than just the static string that DataTables knows of. 758 * 759 * The way these plug-ins work is that you create an array of the values you 760 * wish to be ordering for the column in question and then return that 761 * array. The data in the array much be in the index order of the rows in 762 * the table (not the currently ordering order!). Which order data gathering 763 * function is run here depends on the `dt-init columns.orderDataType` 764 * parameter that is used for the column (if any). 765 * 766 * The functions defined take two parameters: 767 * 768 * 1. `{object}` DataTables settings object: see 769 * {@link DataTable.models.oSettings} 770 * 2. `{int}` Target column index 771 * 772 * Each function is expected to return an array: 773 * 774 * * `{array}` Data for the column to be ordering upon 775 * 776 * @type array 777 * 778 * @example 779 * // Ordering using `input` node values 780 * $.fn.dataTable.ext.order['dom-text'] = function ( settings, col ) 781 * { 782 * return this.api().column( col, {order:'index'} ).nodes().map( function ( td, i ) { 783 * return $('input', td).val(); 784 * } ); 785 * } 786 */ 787 order: {}, 788 789 790 /** 791 * Type based plug-ins. 792 * 793 * Each column in DataTables has a type assigned to it, either by automatic 794 * detection or by direct assignment using the `type` option for the column. 795 * The type of a column will effect how it is ordering and search (plug-ins 796 * can also make use of the column type if required). 797 * 798 * @namespace 799 */ 800 type: { 801 /** 802 * Automatic column class assignment 803 */ 804 className: {}, 805 806 /** 807 * Type detection functions. 808 * 809 * The functions defined in this object are used to automatically detect 810 * a column's type, making initialisation of DataTables super easy, even 811 * when complex data is in the table. 812 * 813 * The functions defined take two parameters: 814 * 815 * 1. `{*}` Data from the column cell to be analysed 816 * 2. `{settings}` DataTables settings object. This can be used to 817 * perform context specific type detection - for example detection 818 * based on language settings such as using a comma for a decimal 819 * place. Generally speaking the options from the settings will not 820 * be required 821 *
822 * Each function is expected to return: 823 * 824 * * `{string|null}` Data type detected, or null if unknown (and thus 825 * pass it on to the other type detection functions. 826 * 827 * @type array 828 * 829 * @example 830 * // Currency type detection plug-in: 831 * $.fn.dataTable.ext.type.detect.push( 832 * function ( data, settings ) { 833 * // Check the numeric part 834 * if ( ! data.substring(1).match(/[0-9]/) ) { 835 * return null; 836 * } 837 * 838 * // Check prefixed by currency 839 * if ( data.charAt(0) == '$' || data.charAt(0) == '£' ) { 840 * return 'currency'; 841 * } 842 * return null; 843 * } 844 * ); 845 */ 846 detect: [], 847 848 /** 849 * Automatic renderer assignment 850 */ 851 render: {}, 852 853 854 /** 855 * Type based search formatting. 856 * 857 * The type based searching functions can be used to pre-format the 858 * data to be search on. For example, it can be used to strip HTML 859 * tags or to de-format telephone numbers for numeric only searching. 860 * 861 * Note that is a search is not defined for a column of a given type, 862 * no search formatting will be performed. 863 * 864 * Pre-processing of searching data plug-ins - When you assign the sType 865 * for a column (or have it automatically detected for you by DataTables 866 * or a type detection plug-in), you will typically be using this for 867 * custom sorting, but it can also be used to provide custom searching 868 * by allowing you to pre-processing the data and returning the data in 869 * the format that should be searched upon. This is done by adding 870 * functions this object with a parameter name which matches the sType 871 * for that target column. This is the corollary of <i>afnSortData</i> 872 * for searching data. 873 * 874 * The functions defined take a single parameter: 875 * 876 * 1. `{*}` Data from the column cell to be prepared for searching 877 * 878 * Each function is expected to return: 879 * 880 * * `{string|null}` Formatted string that will be used for the searching. 881 * 882 * @type object 883 * @default {} 884 * 885 * @example 886 * $.fn.dataTable.ext.type.search['title-numeric'] = function ( d ) { 887 * return d.replace(/\n/g," ").replace( /<.*?>/g, "" ); 888 * } 889 */ 890 search: {}, 891 892 893 /** 894 * Type based ordering. 895 * 896 * The column type tells DataTables what ordering to apply to the table 897 * when a column is sorted upon. The order for each type that is defined, 898 * is defined by the functions available in this object. 899 * 900 * Each ordering option can be described by three properties added to 901 * this object: 902 * 903 * * `{type}-pre` - Pre-formatting function 904 * * `{type}-asc` - Ascending order function 905 * * `{type}-desc` - Descending order function 906 * 907 * All three can be used together, only `{type}-pre` or only 908 * `{type}-asc` and `{type}-desc` together. It is generally recommended 909 * that only `{type}-pre` is used, as this provides the optimal 910 * implementation in terms of speed, although the others are provided 911 * for compatibility with existing Javascript sort functions. 912 * 913 * `{type}-pre`: Functions defined take a single parameter: 914 * 915 * 1. `{*}` Data from the column cell to be prepared for ordering 916 * 917 * And return: 918 * 919 * * `{*}` Data to be sorted upon 920 * 921 * `{type}-asc` and `{type}-desc`: Functions are typical Javascript sort 922 * functions, taking two parameters: 923 * 924 * 1. `{*}` Data to compare to the second parameter 925 * 2. `{*}` Data to compare to the first parameter 926 * 927 * And returning: 928 * 929 * * `{*}` Ordering match: <0 if first parameter should be sorted lower 930 * than the second parameter, ===0 if the two parameters are equal and 931 * >0 if the first parameter should be sorted height than the second 932 * parameter. 933 * 934 * @type object 935 * @default {} 936 * 937 * @example 938 * // Numeric ordering of formatted numbers with a pre-formatter 939 * $.extend( $.fn.dataTable.ext.type.order, { 940 * "string-pre": function(x) { 941 * a = (a === "-" || a === "") ? 0 : a.replace( /[^\d\-\.]/g, "" ); 942 * return parseFloat( a ); 943 * } 944 * } ); 945 * 946 * @example 947 * // Case-sensitive string ordering, with no pre-formatting method 948 * $.extend( $.fn.dataTable.ext.order, { 949 * "string-case-asc": function(x,y) { 950 * return ((x < y) ? -1 : ((x > y) ? 1 : 0)); 951 * }, 952 * "string-case-desc": function(x,y) { 953 * return ((x < y) ? 1 : ((x > y) ? -1 : 0)); 954 * } 955 * } ); 956 */ 957 order: {} 958 }, 959 960 /** 961 * Unique DataTables instance counter 962 * 963 * @type int 964 * @private 965 */ 966 _unique: 0, 967 968 969 // 970 // Depreciated 971 // The following properties are retained for backwards compatibility only. 972 // The should not be used in new projects and will be removed in a future 973 // version 974 // 975 976 /** 977 * Version check function. 978 * @type function 979 * @depreciated Since 1.10 980 */ 981 fnVersionCheck: DataTable.fnVersionCheck, 982 983 984 /** 985 * Index for what 'this' index API functions should use 986 * @type int 987 * @deprecated Since v1.10 988 */ 989 iApiIndex: 0, 990 991 992 /** 993 * Software version 994 * @type string 995 * @deprecated Since v1.10 996 */
997 sVersion: DataTable.version 998 }; 999 1000 1001 // 1002 // Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts 1003 // 1004 $.extend( _ext, { 1005 afnFiltering: _ext.search, 1006 aTypes: _ext.type.detect, 1007 ofnSearch: _ext.type.search, 1008 oSort: _ext.type.order, 1009 afnSortData: _ext.order, 1010 aoFeatures: _ext.feature, 1011 oStdClasses: _ext.classes, 1012 oPagination: _ext.pager 1013 } ); 1014 1015 1016 $.extend( DataTable.ext.classes, { 1017 container: 'dt-container', 1018 empty: { 1019 row: 'dt-empty' 1020 }, 1021 info: { 1022 container: 'dt-info' 1023 }, 1024 length: { 1025 container: 'dt-length', 1026 select: 'dt-input' 1027 }, 1028 order: { 1029 canAsc: 'dt-orderable-asc', 1030 canDesc: 'dt-orderable-desc', 1031 isAsc: 'dt-ordering-asc', 1032 isDesc: 'dt-ordering-desc', 1033 none: 'dt-orderable-none', 1034 position: 'sorting_' 1035 }, 1036 processing: { 1037 container: 'dt-processing' 1038 }, 1039 scrolling: { 1040 body: 'dt-scroll-body', 1041 container: 'dt-scroll', 1042 footer: { 1043 self: 'dt-scroll-foot', 1044 inner: 'dt-scroll-footInner' 1045 }, 1046 header: { 1047 self: 'dt-scroll-head', 1048 inner: 'dt-scroll-headInner' 1049 } 1050 }, 1051 search: { 1052 container: 'dt-search', 1053 input: 'dt-input' 1054 }, 1055 table: 'dataTable', 1056 tbody: { 1057 cell: '', 1058 row: '' 1059 }, 1060 thead: { 1061 cell: '', 1062 row: '' 1063 }, 1064 tfoot: { 1065 cell: '', 1066 row: '' 1067 }, 1068 paging: { 1069 active: 'current', 1070 button: 'dt-paging-button', 1071 container: 'dt-paging', 1072 disabled: 'disabled' 1073 } 1074 } ); 1075 1076 1077 /*
1078 * It is useful to have variables which are scoped locally so only the 1079 * DataTables functions can access them and they don't leak into global space. 1080 * At the same time these functions are often useful over multiple files in the 1081 * core and API, so we list, or at least document, all variables which are used 1082 * by DataTables as private variables here. This also ensures that there is no 1083 * clashing of variable names and that they can easily referenced for reuse. 1084 */ 1085 1086 1087 // Defined else where 1088 // _selector_run 1089 // _selector_opts 1090 // _selector_row_indexes 1091 1092 var _ext; // DataTable.ext 1093 var _Api; // DataTable.Api 1094 var _api_register; // DataTable.Api.register 1095 var _api_registerPlural; // DataTable.Api.registerPlural 1096 1097 var _re_dic = {}; 1098 var _re_new_lines = /[\r\n\u2028]/g; 1099 var _re_html = /<([^>]*>)/g; 1100 var _max_str_len = Math.pow(2, 28); 1101 1102 // This is not strict ISO8601 - Date.parse() is quite lax, although 1103 // implementations differ between browsers. 1104 var _re_date = /^\d{2,4}[./-]\d{1,2}[./-]\d{1,2}([T ]{1}\d{1,2}[:.]\d{2}([.:]\d{2})?)?$/; 1105 1106 // Escape regular expression special characters 1107 var _re_escape_regex = new RegExp( '(\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ].join('|\\') + ')', 'g' ); 1108 1109 // https://en.wikipedia.org/wiki/Foreign_exchange_market 1110 // - \u20BD - Russian ruble. 1111 // - \u20a9 - South Korean Won 1112 // - \u20BA - Turkish Lira 1113 // - \u20B9 - Indian Rupee 1114 // - R - Brazil (R$) and South Africa 1115 // - fr - Swiss Franc 1116 // - kr - Swedish krona, Norwegian krone and Danish krone 1117 // - \u2009 is thin space and \u202F is narrow no-break space, both used in many 1118 // - É - Bitcoin 1119 // - Î - Ethereum 1120 // standards as thousands separators. 1121 var _re_formatted_numeric = /['\u00A0,$£â¬Â¥%\u2009\u202F\u20BD\u20a9\u20BArfkÉÎ]/gi; 1122 1123 1124 var _empty = function ( d ) { 1125 return !d || d === true || d === '-' ? true : false; 1126 }; 1127 1128 1129 var _intVal = function ( s ) { 1130 var integer = parseInt( s, 10 ); 1131 return !isNaN(integer) && isFinite(s) ? integer : null; 1132 }; 1133 1134 // Convert from a formatted number with characters other than `.` as the 1135 // decimal place, to a Javascript number 1136 var _numToDecimal = function ( num, decimalPoint ) { 1137 // Cache created regular expressions for speed as this function is called often 1138 if ( ! _re_dic[ decimalPoint ] ) { 1139 _re_dic[ decimalPoint ] = new RegExp( _fnEscapeRegex( decimalPoint ), 'g' ); 1140 } 1141 return typeof num === 'string' && decimalPoint !== '.' ? 1142 num.replace( /\./g, '' ).replace( _re_dic[ decimalPoint ], '.' ) : 1143 num; 1144 }; 1145 1146 1147 var _isNumber = function ( d, decimalPoint, formatted ) { 1148 var type = typeof d; 1149 var strType = type === 'string'; 1150 1151 if ( type === 'number' || type === 'bigint') { 1152 return true; 1153 } 1154 1155 // If empty return immediately so there must be a number if it is a 1156 // formatted string (this stops the string "k", or "kr", etc being detected 1157 // as a formatted number for currency 1158 if ( _empty( d ) ) { 1159 return true; 1160 } 1161 1162 if ( decimalPoint && strType ) { 1163 d = _numToDecimal( d, decimalPoint ); 1164 } 1165 1166 if ( formatted && strType ) { 1167 d = d.replace( _re_formatted_numeric, '' ); 1168 } 1169 1170 return !isNaN( parseFloat(d) ) && isFinite( d ); 1171 }; 1172 1173 1174 // A string without HTML in it can be considered to be HTML still 1175 var _isHtml = function ( d ) { 1176 return _empty( d ) || typeof d === 'string'; 1177 }; 1178 1179 // Is a string a number surrounded by HTML? 1180 var _htmlNumeric = function ( d, decimalPoint, formatted ) { 1181 if ( _empty( d ) ) { 1182 return true; 1183 } 1184 1185 // input and select strings mean that this isn't just a number 1186 if (typeof d === 'string' && d.match(/<(input|select)/i)) { 1187 return null; 1188 } 1189 1190 var html = _isHtml( d ); 1191 return ! html ? 1192 null : 1193 _isNumber( _stripHtml( d ), decimalPoint, formatted ) ? 1194 true : 1195 null; 1196 }; 1197 1198 1199 var _pluck = function ( a, prop, prop2 ) { 1200 var out = []; 1201 var i=0, ien=a.length; 1202 1203 // Could have the test in the loop for slightly smaller code, but speed 1204 // is essential here 1205 if ( prop2 !== undefined ) { 1206 for ( ; i<ien ; i++ ) { 1207 if ( a[i] && a[i][ prop ] ) { 1208 out.push( a[i][ prop ][ prop2 ] ); 1209 } 1210 } 1211 } 1212 else { 1213 for ( ; i<ien ; i++ ) { 1214 if ( a[i] ) { 1215 out.push( a[i][ prop ] ); 1216 } 1217 } 1218 } 1219 1220 return out; 1221 }; 1222 1223 1224 // Basically the same as _pluck, but rather than looping over `a` we use `order` 1225 // as the indexes to pick from `a` 1226 var _pluck_order = function ( a, order, prop, prop2 ) 1227 { 1228 var out = []; 1229 var i=0, ien=order.length; 1230 1231 // Could have the test in the loop for slightly smaller code, but speed 1232 // is essential here 1233 if ( prop2 !== undefined ) { 1234 for ( ; i<ien ; i++ ) { 1235 if ( a[ order[i] ][ prop ] ) { 1236 out.push( a[ order[i] ][ prop ][ prop2 ] ); 1237 } 1238 } 1239 } 1240 else { 1241 for ( ; i<ien ; i++ ) { 1242 if ( a[ order[i] ] ) { 1243 out.push( a[ order[i] ][ prop ] ); 1244 } 1245 } 1246 } 1247 1248 return out; 1249 }; 1250 1251 1252 var _range = function ( len, start ) 1253 { 1254 var out = []; 1255 var end; 1256 1257 if ( start === undefined ) { 1258 start = 0; 1259 end = len; 1260 } 1261 else { 1262 end = start; 1263 start = len; 1264 } 1265 1266 for ( var i=start ; i<end ; i++ ) { 1267 out.push( i ); 1268 } 1269 1270 return out; 1271 }; 1272 1273 1274 var _removeEmpty = function ( a ) 1275 { 1276 var out = []; 1277 1278 for ( var i=0, ien=a.length ; i<ien ; i++ ) { 1279 if ( a[i] ) { // careful - will remove all falsy values! 1280 out.push( a[i] ); 1281 } 1282 } 1283 1284 return out; 1285 }; 1286 1287 // Replaceable function in api.util 1288 var _stripHtml = function (input) { 1289 // Irrelevant check to workaround CodeQL's false positive on the regex 1290 if (input.length > _max_str_len) { 1291 throw new Error('Exceeded max str len'); 1292 } 1293 1294 var previous; 1295 1296 input = input.replace(_re_html, ''); // Complete tags 1297 1298 // Safety for incomplete script tag - use do / while to ensure that 1299 // we get all instances 1300 do { 1301 previous = input; 1302 input = input.replace(/<script/i, ''); 1303 } while (input !== previous); 1304 1305 return previous; 1306 }; 1307 1308 // Replaceable function in api.util 1309 var _escapeHtml = function ( d ) { 1310 if (Array.isArray(d)) { 1311 d = d.join(','); 1312 } 1313 1314 return typeof d === 'string' ? 1315 d 1316 .replace(/&/g, '&') 1317 .replace(/</g, '<') 1318 .replace(/>/g, '>') 1319 .replace(/"/g, '"') : 1320 d; 1321 }; 1322 1323 // Remove diacritics from a string by decomposing it and then removing 1324 // non-ascii characters 1325 var _normalize = function (str, both) { 1326 if (typeof str !== 'string') { 1327 return str; 1328 } 1329 1330 // It is faster to just run `normalize` than it is to check if 1331 // we need to with a regex! 1332 var res = str.normalize("NFD"); 1333 1334 // Equally, here we check if a regex is needed or not 1335 return res.length !== str.length 1336 ? (both === true ? str + ' ' : '' ) + res.replace(/[\u0300-\u036f]/g, "") 1337 : res; 1338 } 1339 1340 /** 1341 * Determine if all values in the array are unique. This means we can short 1342 * cut the _unique method at the cost of a single loop. A sorted array is used 1343 * to easily check the values. 1344 * 1345 * @param {array} src Source array 1346 * @return {boolean} true if all unique, false otherwise 1347 * @ignore 1348 */ 1349 var _areAllUnique = function ( src ) { 1350 if ( src.length < 2 ) { 1351 return true; 1352 } 1353 1354 var sorted = src.slice().sort(); 1355 var last = sorted[0]; 1356 1357 for ( var i=1, ien=sorted.length ; i<ien ; i++ ) { 1358 if ( sorted[i] === last ) { 1359 return false;
vendor: 17,993 bytes, lines 1360-2019
1360 } 1361 1362 last = sorted[i]; 1363 } 1364 1365 return true; 1366 }; 1367 1368 1369 /** 1370 * Find the unique elements in a source array. 1371 * 1372 * @param {array} src Source array 1373 * @return {array} Array of unique items 1374 * @ignore 1375 */ 1376 var _unique = function ( src ) 1377 { 1378 if (Array.from && Set) { 1379 return Array.from(new Set(src)); 1380 } 1381 1382 if ( _areAllUnique( src ) ) { 1383 return src.slice(); 1384 } 1385 1386 // A faster unique method is to use object keys to identify used values, 1387 // but this doesn't work with arrays or objects, which we must also 1388 // consider. See jsperf.app/compare-array-unique-versions/4 for more 1389 // information. 1390 var 1391 out = [], 1392 val, 1393 i, ien=src.length, 1394 j, k=0; 1395 1396 again: for ( i=0 ; i<ien ; i++ ) { 1397 val = src[i]; 1398 1399 for ( j=0 ; j<k ; j++ ) { 1400 if ( out[j] === val ) { 1401 continue again; 1402 } 1403 } 1404 1405 out.push( val ); 1406 k++; 1407 } 1408 1409 return out; 1410 }; 1411 1412 // Surprisingly this is faster than [].concat.apply 1413 // https://jsperf.com/flatten-an-array-loop-vs-reduce/2 1414 var _flatten = function (out, val) { 1415 if (Array.isArray(val)) { 1416 for (var i=0 ; i<val.length ; i++) { 1417 _flatten(out, val[i]); 1418 } 1419 } 1420 else { 1421 out.push(val); 1422 } 1423 1424 return out; 1425 } 1426 1427 // Similar to jQuery's addClass, but use classList.add 1428 function _addClass(el, name) { 1429 if (name) { 1430 name.split(' ').forEach(function (n) { 1431 if (n) { 1432 // `add` does deduplication, so no need to check `contains` 1433 el.classList.add(n); 1434 } 1435 }); 1436 } 1437 } 1438 1439 /** 1440 * DataTables utility methods 1441 * 1442 * This namespace provides helper methods that DataTables uses internally to 1443 * create a DataTable, but which are not exclusively used only for DataTables. 1444 * These methods can be used by extension authors to save the duplication of 1445 * code. 1446 * 1447 * @namespace 1448 */ 1449 DataTable.util = { 1450 /** 1451 * Return a string with diacritic characters decomposed 1452 * @param {*} mixed Function or string to normalize 1453 * @param {*} both Return original string and the normalized string 1454 * @returns String or undefined 1455 */ 1456 diacritics: function (mixed, both) { 1457 var type = typeof mixed; 1458 1459 if (type !== 'function') { 1460 return _normalize(mixed, both); 1461 } 1462 _normalize = mixed; 1463 }, 1464 1465 /** 1466 * Debounce a function 1467 * 1468 * @param {function} fn Function to be called 1469 * @param {integer} freq Call frequency in mS 1470 * @return {function} Wrapped function 1471 */ 1472 debounce: function ( fn, timeout ) { 1473 var timer; 1474 1475 return function () { 1476 var that = this; 1477 var args = arguments; 1478 1479 clearTimeout(timer); 1480 1481 timer = setTimeout( function () { 1482 fn.apply(that, args); 1483 }, timeout || 250 ); 1484 }; 1485 }, 1486 1487 /** 1488 * Throttle the calls to a function. Arguments and context are maintained 1489 * for the throttled function. 1490 * 1491 * @param {function} fn Function to be called 1492 * @param {integer} freq Call frequency in mS 1493 * @return {function} Wrapped function 1494 */ 1495 throttle: function ( fn, freq ) { 1496 var 1497 frequency = freq !== undefined ? freq : 200, 1498 last, 1499 timer; 1500 1501 return function () { 1502 var 1503 that = this, 1504 now = +new Date(), 1505 args = arguments; 1506 1507 if ( last && now < last + frequency ) { 1508 clearTimeout( timer ); 1509 1510 timer = setTimeout( function () { 1511 last = undefined; 1512 fn.apply( that, args ); 1513 }, frequency ); 1514 } 1515 else { 1516 last = now; 1517 fn.apply( that, args ); 1518 } 1519 }; 1520 }, 1521 1522 /** 1523 * Escape a string such that it can be used in a regular expression 1524 * 1525 * @param {string} val string to escape 1526 * @returns {string} escaped string 1527 */ 1528 escapeRegex: function ( val ) { 1529 return val.replace( _re_escape_regex, '\\$1' ); 1530 }, 1531 1532 /** 1533 * Create a function that will write to a nested object or array 1534 * @param {*} source JSON notation string 1535 * @returns Write function 1536 */ 1537 set: function ( source ) { 1538 if ( $.isPlainObject( source ) ) { 1539 /* Unlike get, only the underscore (global) option is used for for 1540 * setting data since we don't know the type here. This is why an object 1541 * option is not documented for `mData` (which is read/write), but it is 1542 * for `mRender` which is read only. 1543 */ 1544 return DataTable.util.set( source._ ); 1545 } 1546 else if ( source === null ) { 1547 // Nothing to do when the data source is null 1548 return function () {}; 1549 } 1550 else if ( typeof source === 'function' ) { 1551 return function (data, val, meta) { 1552 source( data, 'set', val, meta ); 1553 }; 1554 } 1555 else if ( 1556 typeof source === 'string' && (source.indexOf('.') !== -1 || 1557 source.indexOf('[') !== -1 || source.indexOf('(') !== -1) 1558 ) { 1559 // Like the get, we need to get data from a nested object 1560 var setData = function (data, val, src) { 1561 var a = _fnSplitObjNotation( src ), b; 1562 var aLast = a[a.length-1]; 1563 var arrayNotation, funcNotation, o, innerSrc; 1564 1565 for ( var i=0, iLen=a.length-1 ; i<iLen ; i++ ) { 1566 // Protect against prototype pollution 1567 if (a[i] === '__proto__' || a[i] === 'constructor') { 1568 throw new Error('Cannot set prototype values'); 1569 } 1570 1571 // Check if we are dealing with an array notation request 1572 arrayNotation = a[i].match(__reArray); 1573 funcNotation = a[i].match(__reFn); 1574 1575 if ( arrayNotation ) { 1576 a[i] = a[i].replace(__reArray, ''); 1577 data[ a[i] ] = []; 1578 1579 // Get the remainder of the nested object to set so we can recurse 1580 b = a.slice(); 1581 b.splice( 0, i+1 ); 1582 innerSrc = b.join('.'); 1583 1584 // Traverse each entry in the array setting the properties requested 1585 if ( Array.isArray( val ) ) { 1586 for ( var j=0, jLen=val.length ; j<jLen ; j++ ) { 1587 o = {}; 1588 setData( o, val[j], innerSrc ); 1589 data[ a[i] ].push( o ); 1590 } 1591 } 1592 else { 1593 // We've been asked to save data to an array, but it 1594 // isn't array data to be saved. Best that can be done 1595 // is to just save the value. 1596 data[ a[i] ] = val; 1597 } 1598 1599 // The inner call to setData has already traversed through the remainder 1600 // of the source and has set the data, thus we can exit here 1601 return; 1602 } 1603 else if ( funcNotation ) { 1604 // Function call 1605 a[i] = a[i].replace(__reFn, ''); 1606 data = data[ a[i] ]( val ); 1607 } 1608 1609 // If the nested object doesn't currently exist - since we are 1610 // trying to set the value - create it 1611 if ( data[ a[i] ] === null || data[ a[i] ] === undefined ) { 1612 data[ a[i] ] = {}; 1613 } 1614 data = data[ a[i] ]; 1615 } 1616 1617 // Last item in the input - i.e, the actual set 1618 if ( aLast.match(__reFn ) ) { 1619 // Function call 1620 data = data[ aLast.replace(__reFn, '') ]( val ); 1621 } 1622 else { 1623 // If array notation is used, we just want to strip it and use the property name 1624 // and assign the value. If it isn't used, then we get the result we want anyway 1625 data[ aLast.replace(__reArray, '') ] = val; 1626 } 1627 }; 1628 1629 return function (data, val) { // meta is also passed in, but not used 1630 return setData( data, val, source ); 1631 }; 1632 } 1633 else { 1634 // Array or flat object mapping 1635 return function (data, val) { // meta is also passed in, but not used 1636 data[source] = val; 1637 }; 1638 } 1639 }, 1640 1641 /** 1642 * Create a function that will read nested objects from arrays, based on JSON notation 1643 * @param {*} source JSON notation string 1644 * @returns Value read 1645 */ 1646 get: function ( source ) { 1647 if ( $.isPlainObject( source ) ) { 1648 // Build an object of get functions, and wrap them in a single call 1649 var o = {}; 1650 $.each( source, function (key, val) { 1651 if ( val ) { 1652 o[key] = DataTable.util.get( val ); 1653 } 1654 } ); 1655 1656 return function (data, type, row, meta) { 1657 var t = o[type] || o._; 1658 return t !== undefined ? 1659 t(data, type, row, meta) : 1660 data; 1661 }; 1662 } 1663 else if ( source === null ) { 1664 // Give an empty string for rendering / sorting etc 1665 return function (data) { // type, row and meta also passed, but not used 1666 return data; 1667 }; 1668 } 1669 else if ( typeof source === 'function' ) { 1670 return function (data, type, row, meta) { 1671 return source( data, type, row, meta ); 1672 }; 1673 } 1674 else if ( 1675 typeof source === 'string' && (source.indexOf('.') !== -1 || 1676 source.indexOf('[') !== -1 || source.indexOf('(') !== -1) 1677 ) { 1678 /* If there is a . in the source string then the data source is in a 1679 * nested object so we loop over the data for each level to get the next 1680 * level down. On each loop we test for undefined, and if found immediately 1681 * return. This allows entire objects to be missing and sDefaultContent to 1682 * be used if defined, rather than throwing an error 1683 */ 1684 var fetchData = function (data, type, src) { 1685 var arrayNotation, funcNotation, out, innerSrc; 1686 1687 if ( src !== "" ) { 1688 var a = _fnSplitObjNotation( src ); 1689 1690 for ( var i=0, iLen=a.length ; i<iLen ; i++ ) { 1691 // Check if we are dealing with special notation 1692 arrayNotation = a[i].match(__reArray); 1693 funcNotation = a[i].match(__reFn); 1694 1695 if ( arrayNotation ) { 1696 // Array notation 1697 a[i] = a[i].replace(__reArray, ''); 1698 1699 // Condition allows simply [] to be passed in 1700 if ( a[i] !== "" ) { 1701 data = data[ a[i] ]; 1702 } 1703 out = []; 1704 1705 // Get the remainder of the nested object to get 1706 a.splice( 0, i+1 ); 1707 innerSrc = a.join('.'); 1708 1709 // Traverse each entry in the array getting the properties requested 1710 if ( Array.isArray( data ) ) { 1711 for ( var j=0, jLen=data.length ; j<jLen ; j++ ) { 1712 out.push( fetchData( data[j], type, innerSrc ) ); 1713 } 1714 } 1715 1716 // If a string is given in between the array notation indicators, that 1717 // is used to join the strings together, otherwise an array is returned 1718 var join = arrayNotation[0].substring(1, arrayNotation[0].length-1); 1719 data = (join==="") ? out : out.join(join); 1720 1721 // The inner call to fetchData has already traversed through the remainder 1722 // of the source requested, so we exit from the loop 1723 break; 1724 } 1725 else if ( funcNotation ) { 1726 // Function call 1727 a[i] = a[i].replace(__reFn, ''); 1728 data = data[ a[i] ](); 1729 continue; 1730 } 1731 1732 if (data === null || data[ a[i] ] === null) { 1733 return null; 1734 } 1735 else if ( data === undefined || data[ a[i] ] === undefined ) { 1736 return undefined; 1737 } 1738 1739 data = data[ a[i] ]; 1740 } 1741 } 1742 1743 return data; 1744 }; 1745 1746 return function (data, type) { // row and meta also passed, but not used 1747 return fetchData( data, type, source ); 1748 }; 1749 } 1750 else { 1751 // Array or flat object mapping 1752 return function (data) { // row and meta also passed, but not used 1753 return data[source]; 1754 }; 1755 } 1756 }, 1757 1758 stripHtml: function (mixed) { 1759 var type = typeof mixed; 1760 1761 if (type === 'function') { 1762 _stripHtml = mixed; 1763 return; 1764 } 1765 else if (type === 'string') { 1766 return _stripHtml(mixed); 1767 } 1768 return mixed; 1769 }, 1770 1771 escapeHtml: function (mixed) { 1772 var type = typeof mixed; 1773 1774 if (type === 'function') { 1775 _escapeHtml = mixed; 1776 return; 1777 } 1778 else if (type === 'string' || Array.isArray(mixed)) { 1779 return _escapeHtml(mixed); 1780 } 1781 return mixed; 1782 }, 1783 1784 unique: _unique 1785 }; 1786 1787 1788 1789 /** 1790 * Create a mapping object that allows camel case parameters to be looked up 1791 * for their Hungarian counterparts. The mapping is stored in a private 1792 * parameter called `_hungarianMap` which can be accessed on the source object. 1793 * @param {object} o 1794 * @memberof DataTable#oApi 1795 */ 1796 function _fnHungarianMap ( o ) 1797 { 1798 var 1799 hungarian = 'a aa ai ao as b fn i m o s ', 1800 match, 1801 newKey, 1802 map = {}; 1803 1804 $.each( o, function (key) { 1805 match = key.match(/^([^A-Z]+?)([A-Z])/); 1806 1807 if ( match && hungarian.indexOf(match[1]+' ') !== -1 ) 1808 { 1809 newKey = key.replace( match[0], match[2].toLowerCase() ); 1810 map[ newKey ] = key; 1811 1812 if ( match[1] === 'o' ) 1813 { 1814 _fnHungarianMap( o[key] ); 1815 } 1816 } 1817 } ); 1818 1819 o._hungarianMap = map; 1820 } 1821 1822 1823 /** 1824 * Convert from camel case parameters to Hungarian, based on a Hungarian map 1825 * created by _fnHungarianMap. 1826 * @param {object} src The model object which holds all parameters that can be 1827 * mapped. 1828 * @param {object} user The object to convert from camel case to Hungarian. 1829 * @param {boolean} force When set to `true`, properties which already have a 1830 * Hungarian value in the `user` object will be overwritten. Otherwise they 1831 * won't be. 1832 * @memberof DataTable#oApi 1833 */ 1834 function _fnCamelToHungarian ( src, user, force ) 1835 { 1836 if ( ! src._hungarianMap ) { 1837 _fnHungarianMap( src ); 1838 } 1839 1840 var hungarianKey; 1841 1842 $.each( user, function (key) { 1843 hungarianKey = src._hungarianMap[ key ]; 1844 1845 if ( hungarianKey !== undefined && (force || user[hungarianKey] === undefined) ) 1846 { 1847 // For objects, we need to buzz down into the object to copy parameters 1848 if ( hungarianKey.charAt(0) === 'o' ) 1849 { 1850 // Copy the camelCase options over to the hungarian 1851 if ( ! user[ hungarianKey ] ) { 1852 user[ hungarianKey ] = {}; 1853 } 1854 $.extend( true, user[hungarianKey], user[key] ); 1855 1856 _fnCamelToHungarian( src[hungarianKey], user[hungarianKey], force ); 1857 } 1858 else { 1859 user[hungarianKey] = user[ key ]; 1860 } 1861 } 1862 } ); 1863 } 1864 1865 /** 1866 * Map one parameter onto another 1867 * @param {object} o Object to map 1868 * @param {*} knew The new parameter name 1869 * @param {*} old The old parameter name 1870 */ 1871 var _fnCompatMap = function ( o, knew, old ) { 1872 if ( o[ knew ] !== undefined ) { 1873 o[ old ] = o[ knew ]; 1874 } 1875 }; 1876 1877 1878 /** 1879 * Provide backwards compatibility for the main DT options. Note that the new 1880 * options are mapped onto the old parameters, so this is an external interface 1881 * change only. 1882 * @param {object} init Object to map 1883 */ 1884 function _fnCompatOpts ( init ) 1885 { 1886 _fnCompatMap( init, 'ordering', 'bSort' ); 1887 _fnCompatMap( init, 'orderMulti', 'bSortMulti' ); 1888 _fnCompatMap( init, 'orderClasses', 'bSortClasses' ); 1889 _fnCompatMap( init, 'orderCellsTop', 'bSortCellsTop' ); 1890 _fnCompatMap( init, 'order', 'aaSorting' ); 1891 _fnCompatMap( init, 'orderFixed', 'aaSortingFixed' ); 1892 _fnCompatMap( init, 'paging', 'bPaginate' ); 1893 _fnCompatMap( init, 'pagingType', 'sPaginationType' ); 1894 _fnCompatMap( init, 'pageLength', 'iDisplayLength' ); 1895 _fnCompatMap( init, 'searching', 'bFilter' ); 1896 1897 // Boolean initialisation of x-scrolling 1898 if ( typeof init.sScrollX === 'boolean' ) { 1899 init.sScrollX = init.sScrollX ? '100%' : ''; 1900 } 1901 if ( typeof init.scrollX === 'boolean' ) { 1902 init.scrollX = init.scrollX ? '100%' : ''; 1903 } 1904 1905 // Column search objects are in an array, so it needs to be converted 1906 // element by element 1907 var searchCols = init.aoSearchCols; 1908 1909 if ( searchCols ) { 1910 for ( var i=0, ien=searchCols.length ; i<ien ; i++ ) { 1911 if ( searchCols[i] ) { 1912 _fnCamelToHungarian( DataTable.models.oSearch, searchCols[i] ); 1913 } 1914 } 1915 } 1916 1917 // Enable search delay if server-side processing is enabled 1918 if (init.serverSide && ! init.searchDelay) { 1919 init.searchDelay = 400; 1920 } 1921 } 1922 1923 1924 /** 1925 * Provide backwards compatibility for column options. Note that the new options 1926 * are mapped onto the old parameters, so this is an external interface change 1927 * only. 1928 * @param {object} init Object to map 1929 */ 1930 function _fnCompatCols ( init ) 1931 { 1932 _fnCompatMap( init, 'orderable', 'bSortable' ); 1933 _fnCompatMap( init, 'orderData', 'aDataSort' ); 1934 _fnCompatMap( init, 'orderSequence', 'asSorting' ); 1935 _fnCompatMap( init, 'orderDataType', 'sortDataType' ); 1936 1937 // orderData can be given as an integer 1938 var dataSort = init.aDataSort; 1939 if ( typeof dataSort === 'number' && ! Array.isArray( dataSort ) ) { 1940 init.aDataSort = [ dataSort ]; 1941 } 1942 } 1943 1944 1945 /** 1946 * Browser feature detection for capabilities, quirks 1947 * @param {object} settings dataTables settings object 1948 * @memberof DataTable#oApi 1949 */ 1950 function _fnBrowserDetect( settings ) 1951 { 1952 // We don't need to do this every time DataTables is constructed, the values 1953 // calculated are specific to the browser and OS configuration which we 1954 // don't expect to change between initialisations 1955 if ( ! DataTable.__browser ) { 1956 var browser = {}; 1957 DataTable.__browser = browser; 1958 1959 // Scrolling feature / quirks detection 1960 var n = $('<div/>') 1961 .css( { 1962 position: 'fixed', 1963 top: 0, 1964 left: -1 * window.pageXOffset, // allow for scrolling 1965 height: 1, 1966 width: 1, 1967 overflow: 'hidden' 1968 } ) 1969 .append( 1970 $('<div/>') 1971 .css( { 1972 position: 'absolute', 1973 top: 1, 1974 left: 1, 1975 width: 100, 1976 overflow: 'scroll' 1977 } ) 1978 .append( 1979 $('<div/>') 1980 .css( { 1981 width: '100%', 1982 height: 10 1983 } ) 1984 ) 1985 ) 1986 .appendTo( 'body' ); 1987 1988 var outer = n.children(); 1989 var inner = outer.children(); 1990 1991 // Get scrollbar width 1992 browser.barWidth = outer[0].offsetWidth - outer[0].clientWidth; 1993 1994 // In rtl text layout, some browsers (most, but not all) will place the 1995 // scrollbar on the left, rather than the right. 1996 browser.bScrollbarLeft = Math.round( inner.offset().left ) !== 1; 1997 1998 n.remove(); 1999 } 2000 2001 $.extend( settings.oBrowser, DataTable.__browser ); 2002 settings.oScroll.iBarWidth = DataTable.__browser.barWidth; 2003 } 2004 2005 /** 2006 * Add a column to the list used for the table with default values 2007 * @param {object} oSettings dataTables settings object 2008 * @memberof DataTable#oApi 2009 */ 2010 function _fnAddColumn( oSettings ) 2011 { 2012 // Add column to aoColumns array 2013 var oDefaults = DataTable.defaults.column; 2014 var iCol = oSettings.aoColumns.length; 2015 var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, { 2016 "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol], 2017 "mData": oDefaults.mData ? oDefaults.mData : iCol, 2018 idx: iCol, 2019 searchFixed: {},
2020 colEl: $('<col>').attr('data-dt-column', iCol) 2021 } ); 2022 oSettings.aoColumns.push( oCol ); 2023 2024 // Add search object for column specific search. Note that the `searchCols[ iCol ]` 2025 // passed into extend can be undefined. This allows the user to give a default 2026 // with only some of the parameters defined, and also not give a default 2027 var searchCols = oSettings.aoPreSearchCols; 2028 searchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch, searchCols[ iCol ] ); 2029 } 2030 2031 2032 /** 2033 * Apply options for a column 2034 * @param {object} oSettings dataTables settings object 2035 * @param {int} iCol column index to consider 2036 * @param {object} oOptions object with sType, bVisible and bSearchable etc 2037 * @memberof DataTable#oApi 2038 */ 2039 function _fnColumnOptions( oSettings, iCol, oOptions ) 2040 { 2041 var oCol = oSettings.aoColumns[ iCol ]; 2042 2043 /* User specified column options */ 2044 if ( oOptions !== undefined && oOptions !== null ) 2045 { 2046 // Backwards compatibility 2047 _fnCompatCols( oOptions ); 2048 2049 // Map camel case parameters to their Hungarian counterparts 2050 _fnCamelToHungarian( DataTable.defaults.column, oOptions, true ); 2051 2052 /* Backwards compatibility for mDataProp */ 2053 if ( oOptions.mDataProp !== undefined && !oOptions.mData ) 2054 { 2055 oOptions.mData = oOptions.mDataProp; 2056 } 2057 2058 if ( oOptions.sType ) 2059 { 2060 oCol._sManualType = oOptions.sType; 2061 } 2062 2063 // `class` is a reserved word in Javascript, so we need to provide 2064 // the ability to use a valid name for the camel case input 2065 if ( oOptions.className && ! oOptions.sClass ) 2066 { 2067 oOptions.sClass = oOptions.className; 2068 } 2069 2070 var origClass = oCol.sClass; 2071 2072 $.extend( oCol, oOptions ); 2073 _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" ); 2074 2075 // Merge class from previously defined classes with this one, rather than just 2076 // overwriting it in the extend above 2077 if (origClass !== oCol.sClass) { 2078 oCol.sClass = origClass + ' ' + oCol.sClass; 2079 } 2080 2081 /* iDataSort to be applied (backwards compatibility), but aDataSort will take 2082 * priority if defined 2083 */ 2084 if ( oOptions.iDataSort !== undefined ) 2085 { 2086 oCol.aDataSort = [ oOptions.iDataSort ]; 2087 } 2088 _fnMap( oCol, oOptions, "aDataSort" ); 2089 } 2090 2091 /* Cache the data get and set functions for speed */ 2092 var mDataSrc = oCol.mData; 2093 var mData = _fnGetObjectDataFn( mDataSrc ); 2094 2095 // The `render` option can be given as an array to access the helper rendering methods. 2096 // The first element is the rendering method to use, the rest are the parameters to pass 2097 if ( oCol.mRender && Array.isArray( oCol.mRender ) ) { 2098 var copy = oCol.mRender.slice(); 2099 var name = copy.shift(); 2100 2101 oCol.mRender = DataTable.render[name].apply(window, copy); 2102 } 2103 2104 oCol._render = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null; 2105 2106 var attrTest = function( src ) { 2107 return typeof src === 'string' && src.indexOf('@') !== -1; 2108 }; 2109 oCol._bAttrSrc = $.isPlainObject( mDataSrc ) && ( 2110 attrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter) 2111 ); 2112 oCol._setter = null; 2113 2114 oCol.fnGetData = function (rowData, type, meta) { 2115 var innerData = mData( rowData, type, undefined, meta ); 2116 2117 return oCol._render && type ? 2118 oCol._render( innerData, type, rowData, meta ) : 2119 innerData; 2120 }; 2121 oCol.fnSetData = function ( rowData, val, meta ) { 2122 return _fnSetObjectDataFn( mDataSrc )( rowData, val, meta ); 2123 }; 2124 2125 // Indicate if DataTables should read DOM data as an object or array 2126 // Used in _fnGetRowElements 2127 if ( typeof mDataSrc !== 'number' && ! oCol._isArrayHost ) { 2128 oSettings._rowReadObject = true; 2129 } 2130 2131 /* Feature sorting overrides column specific when off */ 2132 if ( !oSettings.oFeatures.bSort ) 2133 { 2134 oCol.bSortable = false; 2135 } 2136 } 2137 2138 2139 /** 2140 * Adjust the table column widths for new data. Note: you would probably want to 2141 * do a redraw after calling this function! 2142 * @param {object} settings dataTables settings object 2143 * @memberof DataTable#oApi 2144 */ 2145 function _fnAdjustColumnSizing ( settings ) 2146 { 2147 _fnCalculateColumnWidths( settings ); 2148 _fnColumnSizes( settings ); 2149 2150 var scroll = settings.oScroll;
2151 if ( scroll.sY !== '' || scroll.sX !== '') { 2152 _fnScrollDraw( settings ); 2153 } 2154 2155 _fnCallbackFire( settings, null, 'column-sizing', [settings] ); 2156 } 2157 2158 /** 2159 * Apply column sizes 2160 * 2161 * @param {*} settings DataTables settings object 2162 */ 2163 function _fnColumnSizes ( settings ) 2164 { 2165 var cols = settings.aoColumns; 2166 2167 for (var i=0 ; i<cols.length ; i++) { 2168 var width = _fnColumnsSumWidth(settings, [i], false, false); 2169 2170 cols[i].colEl.css('width', width); 2171 } 2172 } 2173 2174 2175 /** 2176 * Convert the index of a visible column to the index in the data array (take account 2177 * of hidden columns) 2178 * @param {object} oSettings dataTables settings object 2179 * @param {int} iMatch Visible column index to lookup 2180 * @returns {int} i the data index 2181 * @memberof DataTable#oApi 2182 */ 2183 function _fnVisibleToColumnIndex( oSettings, iMatch ) 2184 { 2185 var aiVis = _fnGetColumns( oSettings, 'bVisible' ); 2186 2187 return typeof aiVis[iMatch] === 'number' ? 2188 aiVis[iMatch] : 2189 null; 2190 } 2191 2192 2193 /** 2194 * Convert the index of an index in the data array and convert it to the visible 2195 * column index (take account of hidden columns) 2196 * @param {int} iMatch Column index to lookup 2197 * @param {object} oSettings dataTables settings object 2198 * @returns {int} i the data index 2199 * @memberof DataTable#oApi 2200 */ 2201 function _fnColumnIndexToVisible( oSettings, iMatch ) 2202 { 2203 var aiVis = _fnGetColumns( oSettings, 'bVisible' ); 2204 var iPos = aiVis.indexOf(iMatch); 2205 2206 return iPos !== -1 ? iPos : null; 2207 } 2208 2209 2210 /** 2211 * Get the number of visible columns 2212 * @param {object} oSettings dataTables settings object 2213 * @returns {int} i the number of visible columns 2214 * @memberof DataTable#oApi 2215 */ 2216 function _fnVisbleColumns( settings ) 2217 { 2218 var layout = settings.aoHeader; 2219 var columns = settings.aoColumns; 2220 var vis = 0; 2221 2222 if ( layout.length ) { 2223 for ( var i=0, ien=layout[0].length ; i<ien ; i++ ) { 2224 if ( columns[i].bVisible && $(layout[0][i].cell).css('display') !== 'none' ) { 2225 vis++; 2226 } 2227 } 2228 } 2229 2230 return vis; 2231 } 2232 2233 2234 /** 2235 * Get an array of column indexes that match a given property 2236 * @param {object} oSettings dataTables settings object 2237 * @param {string} sParam Parameter in aoColumns to look for - typically 2238 * bVisible or bSearchable 2239 * @returns {array} Array of indexes with matched properties 2240 * @memberof DataTable#oApi 2241 */ 2242 function _fnGetColumns( oSettings, sParam ) 2243 { 2244 var a = []; 2245 2246 oSettings.aoColumns.map( function(val, i) { 2247 if ( val[sParam] ) { 2248 a.push( i ); 2249 } 2250 } ); 2251 2252 return a; 2253 } 2254 2255 2256 /** 2257 * Calculate the 'type' of a column 2258 * @param {object} settings dataTables settings object 2259 * @memberof DataTable#oApi 2260 */ 2261 function _fnColumnTypes ( settings ) 2262 { 2263 var columns = settings.aoColumns; 2264 var data = settings.aoData; 2265 var types = DataTable.ext.type.detect; 2266 var i, ien, j, jen, k, ken; 2267 var col, detectedType, cache; 2268 2269 // For each column, spin over the 2270 for ( i=0, ien=columns.length ; i<ien ; i++ ) { 2271 col = columns[i]; 2272 cache = []; 2273 2274 if ( ! col.sType && col._sManualType ) { 2275 col.sType = col._sManualType; 2276 } 2277 else if ( ! col.sType ) { 2278 for ( j=0, jen=types.length ; j<jen ; j++ ) { 2279 for ( k=0, ken=data.length ; k<ken ; k++ ) { 2280 2281 if (! data[k]) { 2282 continue; 2283 } 2284 2285 // Use a cache array so we only need to get the type data 2286 // from the formatter once (when using multiple detectors) 2287 if ( cache[k] === undefined ) { 2288 cache[k] = _fnGetCellData( settings, k, i, 'type' ); 2289 } 2290 2291 detectedType = types[j]( cache[k], settings ); 2292 2293 // If null, then this type can't apply to this column, so 2294 // rather than testing all cells, break out. There is an 2295 // exception for the last type which is `html`. We need to 2296 // scan all rows since it is possible to mix string and HTML 2297 // types 2298 if ( ! detectedType && j !== types.length-2 ) { 2299 break; 2300 } 2301 2302 // Only a single match is needed for html type since it is 2303 // bottom of the pile and very similar to string - but it 2304 // must not be empty 2305 if ( detectedType === 'html' && ! _empty(cache[k]) ) { 2306 break; 2307 } 2308 } 2309 2310 // Type is valid for all data points in the column - use this 2311 // type 2312 if ( detectedType ) {
2313 col.sType = detectedType; 2314 break; 2315 } 2316 } 2317 2318 // Fall back - if no type was detected, always use string 2319 if ( ! col.sType ) { 2320 col.sType = 'string'; 2321 } 2322 } 2323 2324 // Set class names for header / footer for auto type classes 2325 var autoClass = _ext.type.className[col.sType]; 2326 2327 if (autoClass) { 2328 _columnAutoClass(settings.aoHeader, i, autoClass); 2329 _columnAutoClass(settings.aoFooter, i, autoClass); 2330 } 2331 2332 var renderer = _ext.type.render[col.sType]; 2333 2334 // This can only happen once! There is no way to remover 2335 // a renderer. After the first time the renderer has 2336 // already been set so createTr will run the renderer itself. 2337 if (renderer && ! col._render) { 2338 col._render = DataTable.util.get(renderer); 2339 2340 _columnAutoRender(settings, i); 2341 } 2342 } 2343 } 2344 2345 /** 2346 * Apply an auto detected renderer to data which doesn't yet have 2347 * a renderer 2348 */ 2349 function _columnAutoRender(settings, colIdx) { 2350 var data = settings.aoData; 2351 2352 for (var i=0 ; i<data.length ; i++) { 2353 if (data[i].nTr) { 2354 // We have to update the display here since there is no 2355 // invalidation check for the data 2356 var display = _fnGetCellData( settings, i, colIdx, 'display' ); 2357 2358 data[i].displayData[colIdx] = display; 2359 _fnWriteCell(data[i].anCells[colIdx], display); 2360 2361 // No need to update sort / filter data since it has 2362 // been invalidated and will be re-read with the 2363 // renderer now applied 2364 } 2365 } 2366 } 2367 2368 /** 2369 * Apply a class name to a column's header cells 2370 */ 2371 function _columnAutoClass(container, colIdx, className) { 2372 container.forEach(function (row) { 2373 if (row[colIdx] && row[colIdx].unique) { 2374 _addClass(row[colIdx].cell, className); 2375 } 2376 }); 2377 } 2378 2379 /** 2380 * Take the column definitions and static columns arrays and calculate how 2381 * they relate to column indexes. The callback function will then apply the 2382 * definition found for a column to a suitable configuration object. 2383 * @param {object} oSettings dataTables settings object 2384 * @param {array} aoColDefs The aoColumnDefs array that is to be applied 2385 * @param {array} aoCols The aoColumns array that defines columns individually 2386 * @param {array} headerLayout Layout for header as it was loaded 2387 * @param {function} fn Callback function - takes two parameters, the calculated 2388 * column index and the definition for that column. 2389 * @memberof DataTable#oApi 2390 */ 2391 function _fnApplyColumnDefs( oSettings, aoColDefs, aoCols, headerLayout, fn ) 2392 { 2393 var i, iLen, j, jLen, k, kLen, def; 2394 var columns = oSettings.aoColumns; 2395 2396 if ( aoCols ) { 2397 for ( i=0, iLen=aoCols.length ; i<iLen ; i++ ) { 2398 if (aoCols[i] && aoCols[i].name) { 2399 columns[i].sName = aoCols[i].name; 2400 } 2401 } 2402 } 2403 2404 // Column definitions with aTargets 2405 if ( aoColDefs ) 2406 { 2407 /* Loop over the definitions array - loop in reverse so first instance has priority */ 2408 for ( i=aoColDefs.length-1 ; i>=0 ; i-- ) 2409 { 2410 def = aoColDefs[i]; 2411 2412 /* Each definition can target multiple columns, as it is an array */ 2413 var aTargets = def.target !== undefined 2414 ? def.target 2415 : def.targets !== undefined 2416 ? def.targets 2417 : def.aTargets; 2418 2419 if ( ! Array.isArray( aTargets ) ) 2420 { 2421 aTargets = [ aTargets ]; 2422 } 2423 2424 for ( j=0, jLen=aTargets.length ; j<jLen ; j++ ) 2425 { 2426 var target = aTargets[j]; 2427 2428 if ( typeof target === 'number' && target >= 0 ) 2429 { 2430 /* Add columns that we don't yet know about */ 2431 while( columns.length <= target ) 2432 { 2433 _fnAddColumn( oSettings ); 2434 } 2435 2436 /* Integer, basic index */ 2437 fn( target, def ); 2438 } 2439 else if ( typeof target === 'number' && target < 0 ) 2440 { 2441 /* Negative integer, right to left column counting */ 2442 fn( columns.length+target, def ); 2443 } 2444 else if ( typeof target === 'string' ) 2445 { 2446 for ( k=0, kLen=columns.length ; k<kLen ; k++ ) { 2447 if (target === '_all') { 2448 // Apply to all columns 2449 fn( k, def ); 2450 } 2451 else if (target.indexOf(':name') !== -1) { 2452 // Column selector 2453 if (columns[k].sName === target.replace(':name', '')) { 2454 fn( k, def ); 2455 } 2456 } 2457 else { 2458 // Cell selector 2459 headerLayout.forEach(function (row) { 2460 if (row[k]) { 2461 var cell = $(row[k].cell); 2462 2463 // Legacy support. Note that it means that we don't support 2464 // an element name selector only, since they are treated as
2465 // class names for 1.x compat. 2466 if (target.match(/^[a-z][\w-]*$/i)) { 2467 target = '.' + target; 2468 } 2469 2470 if (cell.is( target )) { 2471 fn( k, def ); 2472 } 2473 } 2474 }); 2475 } 2476 } 2477 } 2478 } 2479 } 2480 } 2481 2482 // Statically defined columns array 2483 if ( aoCols ) { 2484 for ( i=0, iLen=aoCols.length ; i<iLen ; i++ ) { 2485 fn( i, aoCols[i] ); 2486 } 2487 } 2488 } 2489 2490 2491 /** 2492 * Get the width for a given set of columns 2493 * 2494 * @param {*} settings DataTables settings object 2495 * @param {*} targets Columns - comma separated string or array of numbers 2496 * @param {*} original Use the original width (true) or calculated (false) 2497 * @param {*} incVisible Include visible columns (true) or not (false) 2498 * @returns Combined CSS value 2499 */ 2500 function _fnColumnsSumWidth( settings, targets, original, incVisible ) { 2501 if ( ! Array.isArray( targets ) ) { 2502 targets = _fnColumnsFromHeader( targets ); 2503 } 2504 2505 var sum = 0; 2506 var unit; 2507 var columns = settings.aoColumns; 2508 2509 for ( var i=0, ien=targets.length ; i<ien ; i++ ) { 2510 var column = columns[ targets[i] ]; 2511 var definedWidth = original ? 2512 column.sWidthOrig : 2513 column.sWidth; 2514 2515 if ( ! incVisible && column.bVisible === false ) { 2516 continue; 2517 } 2518 2519 if ( definedWidth === null || definedWidth === undefined ) { 2520 return null; // can't determine a defined width - browser defined 2521 } 2522 else if ( typeof definedWidth === 'number' ) { 2523 unit = 'px'; 2524 sum += definedWidth; 2525 } 2526 else { 2527 var matched = definedWidth.match(/([\d\.]+)([^\d]*)/); 2528 2529 if ( matched ) { 2530 sum += matched[1] * 1; 2531 unit = matched.length === 3 ? 2532 matched[2] : 2533 'px'; 2534 } 2535 } 2536 } 2537 2538 return sum + unit; 2539 } 2540 2541 function _fnColumnsFromHeader( cell ) 2542 { 2543 var attr = $(cell).closest('[data-dt-column]').attr('data-dt-column'); 2544 2545 if ( ! attr ) { 2546 return []; 2547 } 2548 2549 return attr.split(',').map( function (val) { 2550 return val * 1; 2551 } ); 2552 } 2553 /** 2554 * Add a data array to the table, creating DOM node etc. This is the parallel to 2555 * _fnGatherData, but for adding rows from a Javascript source, rather than a 2556 * DOM source. 2557 * @param {object} settings dataTables settings object 2558 * @param {array} data data array to be added 2559 * @param {node} [tr] TR element to add to the table - optional. If not given, 2560 * DataTables will create a row automatically 2561 * @param {array} [tds] Array of TD|TH elements for the row - must be given 2562 * if nTr is. 2563 * @returns {int} >=0 if successful (index of new aoData entry), -1 if failed 2564 * @memberof DataTable#oApi 2565 */ 2566 function _fnAddData ( settings, dataIn, tr, tds ) 2567 { 2568 /* Create the object for storing information about this new row */ 2569 var rowIdx = settings.aoData.length; 2570 var rowModel = $.extend( true, {}, DataTable.models.oRow, { 2571 src: tr ? 'dom' : 'data', 2572 idx: rowIdx 2573 } ); 2574 2575 rowModel._aData = dataIn; 2576 settings.aoData.push( rowModel ); 2577 2578 var columns = settings.aoColumns; 2579 2580 for ( var i=0, iLen=columns.length ; i<iLen ; i++ ) 2581 { 2582 // Invalidate the column types as the new data needs to be revalidated 2583 columns[i].sType = null; 2584 } 2585 2586 /* Add to the display array */ 2587 settings.aiDisplayMaster.push( rowIdx ); 2588 2589 var id = settings.rowIdFn( dataIn ); 2590 if ( id !== undefined ) { 2591 settings.aIds[ id ] = rowModel; 2592 } 2593 2594 /* Create the DOM information, or register it if already present */ 2595 if ( tr || ! settings.oFeatures.bDeferRender ) 2596 { 2597 _fnCreateTr( settings, rowIdx, tr, tds ); 2598 } 2599 2600 return rowIdx; 2601 } 2602 2603 2604 /** 2605 * Add one or more TR elements to the table. Generally we'd expect to 2606 * use this for reading data from a DOM sourced table, but it could be 2607 * used for an TR element. Note that if a TR is given, it is used (i.e. 2608 * it is not cloned). 2609 * @param {object} settings dataTables settings object 2610 * @param {array|node|jQuery} trs The TR element(s) to add to the table 2611 * @returns {array} Array of indexes for the added rows 2612 * @memberof DataTable#oApi 2613 */ 2614 function _fnAddTr( settings, trs ) 2615 { 2616 var row; 2617 2618 // Allow an individual node to be passed in 2619 if ( ! (trs instanceof $) ) { 2620 trs = $(trs); 2621 } 2622 2623 return trs.map( function (i, el) { 2624 row = _fnGetRowElements( settings, el ); 2625 return _fnAddData( settings, row.data, el, row.cells ); 2626 } ); 2627 } 2628 2629 2630 /**
2631 * Get the data for a given cell from the internal cache, taking into account data mapping 2632 * @param {object} settings dataTables settings object 2633 * @param {int} rowIdx aoData row id 2634 * @param {int} colIdx Column index 2635 * @param {string} type data get type ('display', 'type' 'filter|search' 'sort|order') 2636 * @returns {*} Cell data 2637 * @memberof DataTable#oApi 2638 */ 2639 function _fnGetCellData( settings, rowIdx, colIdx, type ) 2640 { 2641 if (type === 'search') { 2642 type = 'filter'; 2643 } 2644 else if (type === 'order') { 2645 type = 'sort'; 2646 } 2647 2648 var row = settings.aoData[rowIdx]; 2649 2650 if (! row) { 2651 return undefined; 2652 } 2653 2654 var draw = settings.iDraw; 2655 var col = settings.aoColumns[colIdx]; 2656 var rowData = row._aData; 2657 var defaultContent = col.sDefaultContent; 2658 var cellData = col.fnGetData( rowData, type, { 2659 settings: settings, 2660 row: rowIdx, 2661 col: colIdx 2662 } ); 2663 2664 // Allow for a node being returned for non-display types 2665 if (type !== 'display' && cellData && typeof cellData === 'object' && cellData.nodeName) { 2666 cellData = cellData.innerHTML; 2667 } 2668 2669 if ( cellData === undefined ) { 2670 if ( settings.iDrawError != draw && defaultContent === null ) { 2671 _fnLog( settings, 0, "Requested unknown parameter "+ 2672 (typeof col.mData=='function' ? '{function}' : "'"+col.mData+"'")+ 2673 " for row "+rowIdx+", column "+colIdx, 4 ); 2674 settings.iDrawError = draw; 2675 } 2676 return defaultContent; 2677 } 2678 2679 // When the data source is null and a specific data type is requested (i.e. 2680 // not the original data), we can use default column data 2681 if ( (cellData === rowData || cellData === null) && defaultContent !== null && type !== undefined ) { 2682 cellData = defaultContent; 2683 } 2684 else if ( typeof cellData === 'function' ) { 2685 // If the data source is a function, then we run it and use the return, 2686 // executing in the scope of the data object (for instances) 2687 return cellData.call( rowData ); 2688 } 2689 2690 if ( cellData === null && type === 'display' ) { 2691 return ''; 2692 } 2693 2694 if ( type === 'filter' ) { 2695 var fomatters = DataTable.ext.type.search; 2696 2697 if ( fomatters[ col.sType ] ) { 2698 cellData = fomatters[ col.sType ]( cellData ); 2699 } 2700 } 2701 2702 return cellData; 2703 } 2704 2705 2706 /** 2707 * Set the value for a specific cell, into the internal data cache 2708 * @param {object} settings dataTables settings object 2709 * @param {int} rowIdx aoData row id 2710 * @param {int} colIdx Column index 2711 * @param {*} val Value to set 2712 * @memberof DataTable#oApi 2713 */ 2714 function _fnSetCellData( settings, rowIdx, colIdx, val ) 2715 { 2716 var col = settings.aoColumns[colIdx]; 2717 var rowData = settings.aoData[rowIdx]._aData; 2718 2719 col.fnSetData( rowData, val, { 2720 settings: settings, 2721 row: rowIdx, 2722 col: colIdx 2723 } ); 2724 } 2725 2726 /** 2727 * Write a value to a cell 2728 * @param {*} td Cell 2729 * @param {*} val Value 2730 */ 2731 function _fnWriteCell(td, val) 2732 { 2733 if (val && typeof val === 'object' && val.nodeName) { 2734 $(td) 2735 .empty() 2736 .append(val); 2737 } 2738 else { 2739 td.innerHTML = val; 2740 } 2741 } 2742 2743 2744 // Private variable that is used to match action syntax in the data property object 2745 var __reArray = /\[.*?\]$/; 2746 var __reFn = /\(\)$/; 2747 2748 /** 2749 * Split string on periods, taking into account escaped periods 2750 * @param {string} str String to split 2751 * @return {array} Split string 2752 */ 2753 function _fnSplitObjNotation( str ) 2754 { 2755 var parts = str.match(/(\\.|[^.])+/g) || ['']; 2756 2757 return parts.map( function ( s ) { 2758 return s.replace(/\\\./g, '.'); 2759 } ); 2760 } 2761 2762 2763 /** 2764 * Return a function that can be used to get data from a source object, taking 2765 * into account the ability to use nested objects as a source 2766 * @param {string|int|function} mSource The data source for the object 2767 * @returns {function} Data get function 2768 * @memberof DataTable#oApi 2769 */ 2770 var _fnGetObjectDataFn = DataTable.util.get; 2771 2772 2773 /** 2774 * Return a function that can be used to set data from a source object, taking 2775 * into account the ability to use nested objects as a source 2776 * @param {string|int|function} mSource The data source for the object 2777 * @returns {function} Data set function 2778 * @memberof DataTable#oApi 2779 */ 2780 var _fnSetObjectDataFn = DataTable.util.set; 2781 2782 2783 /** 2784 * Return an array with the full table data 2785 * @param {object} oSettings dataTables settings object 2786 * @returns array {array} aData Master data array 2787 * @memberof DataTable#oApi 2788 */ 2789 function _fnGetDataMaster ( settings ) 2790 { 2791 return _pluck( settings.aoData, '_aData' ); 2792 } 2793 2794 2795 /** 2796 * Nuke the table 2797 * @param {object} oSettings dataTables settings object 2798 * @memberof DataTable#oApi 2799 */ 2800 function _fnClearTable( settings ) 2801 { 2802 settings.aoData.length = 0; 2803 settings.aiDisplayMaster.length = 0; 2804 settings.aiDisplay.length = 0; 2805 settings.aIds = {}; 2806 } 2807 2808 2809 /**
2810 * Mark cached data as invalid such that a re-read of the data will occur when 2811 * the cached data is next requested. Also update from the data source object. 2812 * 2813 * @param {object} settings DataTables settings object 2814 * @param {int} rowIdx Row index to invalidate 2815 * @param {string} [src] Source to invalidate from: undefined, 'auto', 'dom' 2816 * or 'data' 2817 * @param {int} [colIdx] Column index to invalidate. If undefined the whole 2818 * row will be invalidated 2819 * @memberof DataTable#oApi 2820 * 2821 * @todo For the modularisation of v1.11 this will need to become a callback, so 2822 * the sort and filter methods can subscribe to it. That will required 2823 * initialisation options for sorting, which is why it is not already baked in 2824 */ 2825 function _fnInvalidate( settings, rowIdx, src, colIdx ) 2826 { 2827 var row = settings.aoData[ rowIdx ]; 2828 var i, ien; 2829 2830 // Remove the cached data for the row 2831 row._aSortData = null; 2832 row._aFilterData = null; 2833 row.displayData = null; 2834 2835 // Are we reading last data from DOM or the data object? 2836 if ( src === 'dom' || ((! src || src === 'auto') && row.src === 'dom') ) { 2837 // Read the data from the DOM 2838 row._aData = _fnGetRowElements( 2839 settings, row, colIdx, colIdx === undefined ? undefined : row._aData 2840 ) 2841 .data; 2842 } 2843 else { 2844 // Reading from data object, update the DOM 2845 var cells = row.anCells; 2846 var display = _fnGetRowDisplay(settings, rowIdx); 2847 2848 if ( cells ) { 2849 if ( colIdx !== undefined ) { 2850 _fnWriteCell(cells[colIdx], display[colIdx]); 2851 } 2852 else { 2853 for ( i=0, ien=cells.length ; i<ien ; i++ ) { 2854 _fnWriteCell(cells[i], display[i]); 2855 } 2856 } 2857 } 2858 } 2859 2860 // Column specific invalidation 2861 var cols = settings.aoColumns; 2862 if ( colIdx !== undefined ) { 2863 // Type - the data might have changed 2864 cols[ colIdx ].sType = null; 2865 2866 // Max length string. Its a fairly cheep recalculation, so not worth 2867 // something more complicated 2868 cols[ colIdx ].maxLenString = null; 2869 } 2870 else { 2871 for ( i=0, ien=cols.length ; i<ien ; i++ ) { 2872 cols[i].sType = null; 2873 cols[i].maxLenString = null; 2874 } 2875 2876 // Update DataTables special `DT_*` attributes for the row 2877 _fnRowAttributes( settings, row ); 2878 } 2879 } 2880 2881 2882 /** 2883 * Build a data source object from an HTML row, reading the contents of the 2884 * cells that are in the row. 2885 * 2886 * @param {object} settings DataTables settings object 2887 * @param {node|object} TR element from which to read data or existing row 2888 * object from which to re-read the data from the cells 2889 * @param {int} [colIdx] Optional column index 2890 * @param {array|object} [d] Data source object. If `colIdx` is given then this 2891 * parameter should also be given and will be used to write the data into. 2892 * Only the column in question will be written 2893 * @returns {object} Object with two parameters: `data` the data read, in 2894 * document order, and `cells` and array of nodes (they can be useful to the 2895 * caller, so rather than needing a second traversal to get them, just return 2896 * them from here). 2897 * @memberof DataTable#oApi 2898 */ 2899 function _fnGetRowElements( settings, row, colIdx, d ) 2900 { 2901 var 2902 tds = [], 2903 td = row.firstChild, 2904 name, col, i=0, contents, 2905 columns = settings.aoColumns, 2906 objectRead = settings._rowReadObject; 2907 2908 // Allow the data object to be passed in, or construct 2909 d = d !== undefined ? 2910 d : 2911 objectRead ? 2912 {} : 2913 []; 2914 2915 var attr = function ( str, td ) { 2916 if ( typeof str === 'string' ) { 2917 var idx = str.indexOf('@'); 2918 2919 if ( idx !== -1 ) { 2920 var attr = str.substring( idx+1 ); 2921 var setter = _fnSetObjectDataFn( str ); 2922 setter( d, td.getAttribute( attr ) ); 2923 } 2924 } 2925 }; 2926 2927 // Read data from a cell and store into the data object 2928 var cellProcess = function ( cell ) { 2929 if ( colIdx === undefined || colIdx === i ) { 2930 col = columns[i]; 2931 contents = (cell.innerHTML).trim(); 2932 2933 if ( col && col._bAttrSrc ) { 2934 var setter = _fnSetObjectDataFn( col.mData._ ); 2935 setter( d, contents ); 2936 2937 attr( col.mData.sort, cell ); 2938 attr( col.mData.type, cell ); 2939 attr( col.mData.filter, cell ); 2940 } 2941 else { 2942 // Depending on the `data` option for the columns the data can 2943 // be read to either an object or an array.
2944 if ( objectRead ) { 2945 if ( ! col._setter ) { 2946 // Cache the setter function 2947 col._setter = _fnSetObjectDataFn( col.mData ); 2948 } 2949 col._setter( d, contents ); 2950 } 2951 else { 2952 d[i] = contents; 2953 } 2954 } 2955 } 2956 2957 i++; 2958 }; 2959 2960 if ( td ) { 2961 // `tr` element was passed in 2962 while ( td ) { 2963 name = td.nodeName.toUpperCase(); 2964 2965 if ( name == "TD" || name == "TH" ) { 2966 cellProcess( td ); 2967 tds.push( td ); 2968 } 2969 2970 td = td.nextSibling; 2971 } 2972 } 2973 else { 2974 // Existing row object passed in 2975 tds = row.anCells; 2976 2977 for ( var j=0, jen=tds.length ; j<jen ; j++ ) { 2978 cellProcess( tds[j] ); 2979 } 2980 } 2981 2982 // Read the ID from the DOM if present 2983 var rowNode = row.firstChild ? row : row.nTr; 2984 2985 if ( rowNode ) { 2986 var id = rowNode.getAttribute( 'id' ); 2987 2988 if ( id ) { 2989 _fnSetObjectDataFn( settings.rowId )( d, id ); 2990 } 2991 } 2992 2993 return { 2994 data: d, 2995 cells: tds 2996 }; 2997 } 2998 2999 /** 3000 * Render and cache a row's display data for the columns, if required 3001 * @returns 3002 */ 3003 function _fnGetRowDisplay (settings, rowIdx) { 3004 let rowModal = settings.aoData[rowIdx]; 3005 let columns = settings.aoColumns; 3006 3007 if (! rowModal.displayData) { 3008 // Need to render and cache 3009 rowModal.displayData = []; 3010 3011 for ( var colIdx=0, len=columns.length ; colIdx<len ; colIdx++ ) { 3012 rowModal.displayData.push( 3013 _fnGetCellData( settings, rowIdx, colIdx, 'display' ) 3014 ); 3015 } 3016 } 3017 3018 return rowModal.displayData; 3019 } 3020 3021 /** 3022 * Create a new TR element (and it's TD children) for a row 3023 * @param {object} oSettings dataTables settings object 3024 * @param {int} iRow Row to consider 3025 * @param {node} [nTrIn] TR element to add to the table - optional. If not given, 3026 * DataTables will create a row automatically 3027 * @param {array} [anTds] Array of TD|TH elements for the row - must be given 3028 * if nTr is. 3029 * @memberof DataTable#oApi 3030 */ 3031 function _fnCreateTr ( oSettings, iRow, nTrIn, anTds ) 3032 { 3033 var 3034 row = oSettings.aoData[iRow], 3035 rowData = row._aData, 3036 cells = [], 3037 nTr, nTd, oCol, 3038 i, iLen, create, 3039 trClass = oSettings.oClasses.tbody.row; 3040 3041 if ( row.nTr === null ) 3042 { 3043 nTr = nTrIn || document.createElement('tr'); 3044 3045 row.nTr = nTr; 3046 row.anCells = cells; 3047 3048 _addClass(nTr, trClass); 3049 3050 /* Use a private property on the node to allow reserve mapping from the node 3051 * to the aoData array for fast look up 3052 */ 3053 nTr._DT_RowIndex = iRow; 3054 3055 /* Special parameters can be given by the data source to be used on the row */ 3056 _fnRowAttributes( oSettings, row ); 3057 3058 /* Process each column */ 3059 for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ ) 3060 { 3061 oCol = oSettings.aoColumns[i]; 3062 create = nTrIn && anTds[i] ? false : true; 3063 3064 nTd = create ? document.createElement( oCol.sCellType ) : anTds[i]; 3065 3066 if (! nTd) { 3067 _fnLog( oSettings, 0, 'Incorrect column count', 18 ); 3068 } 3069 3070 nTd._DT_CellIndex = { 3071 row: iRow, 3072 column: i 3073 }; 3074 3075 cells.push( nTd ); 3076 3077 var display = _fnGetRowDisplay(oSettings, iRow); 3078 3079 // Need to create the HTML if new, or if a rendering function is defined 3080 if ( 3081 create || 3082 ( 3083 (oCol.mRender || oCol.mData !== i) && 3084 (!$.isPlainObject(oCol.mData) || oCol.mData._ !== i+'.display') 3085 ) 3086 ) { 3087 _fnWriteCell(nTd, display[i]); 3088 } 3089 3090 // Visibility - add or remove as required 3091 if ( oCol.bVisible && create ) 3092 { 3093 nTr.appendChild( nTd ); 3094 } 3095 else if ( ! oCol.bVisible && ! create ) 3096 { 3097 nTd.parentNode.removeChild( nTd ); 3098 } 3099 3100 if ( oCol.fnCreatedCell ) 3101 { 3102 oCol.fnCreatedCell.call( oSettings.oInstance, 3103 nTd, _fnGetCellData( oSettings, iRow, i ), rowData, iRow, i 3104 ); 3105 } 3106 } 3107 3108 _fnCallbackFire( oSettings, 'aoRowCreatedCallback', 'row-created', [nTr, rowData, iRow, cells] ); 3109 } 3110 else { 3111 _addClass(row.nTr, trClass); 3112 } 3113 } 3114 3115 3116 /** 3117 * Add attributes to a row based on the special `DT_*` parameters in a data 3118 * source object. 3119 * @param {object} settings DataTables settings object 3120 * @param {object} DataTables row object for the row to be modified 3121 * @memberof DataTable#oApi 3122 */ 3123 function _fnRowAttributes( settings, row ) 3124 { 3125 var tr = row.nTr; 3126 var data = row._aData; 3127 3128 if ( tr ) { 3129 var id = settings.rowIdFn( data ); 3130 3131 if ( id ) { 3132 tr.id = id; 3133 } 3134 3135 if ( data.DT_RowClass ) { 3136 // Remove any classes added by DT_RowClass before 3137 var a = data.DT_RowClass.split(' '); 3138 row.__rowc = row.__rowc ? 3139 _unique( row.__rowc.concat( a ) ) : 3140 a; 3141 3142 $(tr) 3143 .removeClass( row.__rowc.join(' ') ) 3144 .addClass( data.DT_RowClass ); 3145 } 3146 3147 if ( data.DT_RowAttr ) { 3148 $(tr).attr( data.DT_RowAttr ); 3149 } 3150 3151 if ( data.DT_RowData ) { 3152 $(tr).data( data.DT_RowData ); 3153 } 3154 } 3155 } 3156 3157 3158 /** 3159 * Create the HTML header for the table 3160 * @param {object} oSettings dataTables settings object 3161 * @memberof DataTable#oApi 3162 */ 3163 function _fnBuildHead( settings, side ) 3164 { 3165 var classes = settings.oClasses; 3166 var columns = settings.aoColumns; 3167 var i, ien, row; 3168 var target = side === 'header' 3169 ? settings.nTHead 3170 : settings.nTFoot; 3171 var titleProp = side === 'header' ? 'sTitle' : side; 3172 3173 // Footer might be defined 3174 if (! target) { 3175 return; 3176 } 3177 3178 // If no cells yet and we have content for them, then create 3179 if (side === 'header' || _pluck(settings.aoColumns, titleProp).join('')) { 3180 row = $('tr', target); 3181 3182 // Add a row if needed 3183 if (! row.length) { 3184 row = $('<tr/>').appendTo(target) 3185 } 3186 3187 // Add the number of cells needed to make up to the number of columns 3188 if (row.length === 1) { 3189 var cells = $('td, th', row); 3190 3191 for ( i=cells.length, ien=columns.length ; i<ien ; i++ ) { 3192 $('<th/>') 3193 .html( columns[i][titleProp] || '' ) 3194 .appendTo( row ); 3195 } 3196 } 3197 } 3198 3199 var detected = _fnDetectHeader( settings, target, true ); 3200 3201 if (side === 'header') { 3202 settings.aoHeader = detected; 3203 } 3204 else { 3205 settings.aoFooter = detected; 3206 } 3207 3208 // ARIA role for the rows 3209 $(target).children('tr').attr('role', 'row'); 3210 3211 // Every cell needs to be passed through the renderer 3212 $(target).children('tr').children('th, td') 3213 .each( function () { 3214 _fnRenderer( settings, side )( 3215 settings, $(this), classes 3216 ); 3217 } ); 3218 } 3219 3220 /** 3221 * Build a layout structure for a header or footer 3222 * 3223 * @param {*} settings DataTables settings 3224 * @param {*} source Source layout array 3225 * @param {*} incColumns What columns should be included 3226 * @returns Layout array 3227 */ 3228 function _fnHeaderLayout( settings, source, incColumns ) 3229 {
3230 var row, column, cell; 3231 var local = []; 3232 var structure = []; 3233 var columns = settings.aoColumns; 3234 var columnCount = columns.length; 3235 var rowspan, colspan; 3236 3237 if ( ! source ) { 3238 return; 3239 } 3240 3241 // Default is to work on only visible columns 3242 if ( ! incColumns ) { 3243 incColumns = _range(columnCount) 3244 .filter(function (idx) { 3245 return columns[idx].bVisible; 3246 }); 3247 } 3248 3249 // Make a copy of the master layout array, but with only the columns we want 3250 for ( row=0 ; row<source.length ; row++ ) { 3251 // Remove any columns we haven't selected 3252 local[row] = source[row].slice().filter(function (cell, i) { 3253 return incColumns.includes(i); 3254 }); 3255 3256 // Prep the structure array - it needs an element for each row 3257 structure.push( [] ); 3258 } 3259 3260 for ( row=0 ; row<local.length ; row++ ) { 3261 for ( column=0 ; column<local[row].length ; column++ ) { 3262 rowspan = 1; 3263 colspan = 1; 3264 3265 // Check to see if there is already a cell (row/colspan) covering our target 3266 // insert point. If there is, then there is nothing to do. 3267 if ( structure[row][column] === undefined ) { 3268 cell = local[row][column].cell; 3269 3270 // Expand for rowspan 3271 while ( 3272 local[row+rowspan] !== undefined && 3273 local[row][column].cell == local[row+rowspan][column].cell 3274 ) { 3275 structure[row+rowspan][column] = null; 3276 rowspan++; 3277 } 3278 3279 // And for colspan 3280 while ( 3281 local[row][column+colspan] !== undefined && 3282 local[row][column].cell == local[row][column+colspan].cell 3283 ) { 3284 // Which also needs to go over rows 3285 for ( var k=0 ; k<rowspan ; k++ ) { 3286 structure[row+k][column+colspan] = null; 3287 } 3288 3289 colspan++; 3290 } 3291 3292 var titleSpan = $('span.dt-column-title', cell); 3293 3294 structure[row][column] = { 3295 cell: cell, 3296 colspan: colspan, 3297 rowspan: rowspan, 3298 title: titleSpan.length 3299 ? titleSpan.html() 3300 : $(cell).html() 3301 }; 3302 } 3303 } 3304 } 3305 3306 return structure; 3307 } 3308 3309 3310 /** 3311 * Draw the header (or footer) element based on the column visibility states. 3312 * 3313 * @param object oSettings dataTables settings object 3314 * @param array aoSource Layout array from _fnDetectHeader 3315 * @memberof DataTable#oApi 3316 */ 3317 function _fnDrawHead( settings, source ) 3318 { 3319 var layout = _fnHeaderLayout(settings, source); 3320 var tr, n; 3321 3322 for ( var row=0 ; row<source.length ; row++ ) { 3323 tr = source[row].row; 3324 3325 // All cells are going to be replaced, so empty out the row 3326 // Can't use $().empty() as that kills event handlers 3327 if (tr) { 3328 while( (n = tr.firstChild) ) { 3329 tr.removeChild( n ); 3330 } 3331 } 3332 3333 for ( var column=0 ; column<layout[row].length ; column++ ) { 3334 var point = layout[row][column]; 3335 3336 if (point) { 3337 $(point.cell) 3338 .appendTo(tr) 3339 .attr('rowspan', point.rowspan) 3340 .attr('colspan', point.colspan); 3341 } 3342 } 3343 } 3344 } 3345 3346 3347 /** 3348 * Insert the required TR nodes into the table for display 3349 * @param {object} oSettings dataTables settings object 3350 * @param ajaxComplete true after ajax call to complete rendering 3351 * @memberof DataTable#oApi 3352 */ 3353 function _fnDraw( oSettings, ajaxComplete ) 3354 { 3355 // Allow for state saving and a custom start position 3356 _fnStart( oSettings ); 3357 3358 /* Provide a pre-callback function which can be used to cancel the draw is false is returned */ 3359 var aPreDraw = _fnCallbackFire( oSettings, 'aoPreDrawCallback', 'preDraw', [oSettings] ); 3360 if ( aPreDraw.indexOf(false) !== -1 ) 3361 { 3362 _fnProcessingDisplay( oSettings, false ); 3363 return; 3364 } 3365 3366 var anRows = []; 3367 var iRowCount = 0; 3368 var bServerSide = _fnDataSource( oSettings ) == 'ssp'; 3369 var aiDisplay = oSettings.aiDisplay; 3370 var iDisplayStart = oSettings._iDisplayStart; 3371 var iDisplayEnd = oSettings.fnDisplayEnd(); 3372 var columns = oSettings.aoColumns; 3373 var body = $(oSettings.nTBody); 3374 3375 oSettings.bDrawing = true; 3376 3377 /* Server-side processing draw intercept */ 3378 if ( !bServerSide ) 3379 { 3380 oSettings.iDraw++; 3381 } 3382 else if ( !oSettings.bDestroying && !ajaxComplete) 3383 { 3384 // Show loading message for server-side processing 3385 if (oSettings.iDraw === 0) {
3386 body.empty().append(_emptyRow(oSettings)); 3387 } 3388 3389 _fnAjaxUpdate( oSettings ); 3390 return; 3391 } 3392 3393 if ( aiDisplay.length !== 0 ) 3394 { 3395 var iStart = bServerSide ? 0 : iDisplayStart; 3396 var iEnd = bServerSide ? oSettings.aoData.length : iDisplayEnd; 3397 3398 for ( var j=iStart ; j<iEnd ; j++ ) 3399 { 3400 var iDataIndex = aiDisplay[j]; 3401 var aoData = oSettings.aoData[ iDataIndex ]; 3402 if ( aoData.nTr === null ) 3403 { 3404 _fnCreateTr( oSettings, iDataIndex ); 3405 } 3406 3407 var nRow = aoData.nTr; 3408 3409 // Add various classes as needed 3410 for (var i=0 ; i<columns.length ; i++) { 3411 var col = columns[i]; 3412 var td = aoData.anCells[i]; 3413 3414 _addClass(td, _ext.type.className[col.sType]); // auto class 3415 _addClass(td, col.sClass); // column class 3416 _addClass(td, oSettings.oClasses.tbody.cell); // all cells 3417 } 3418 3419 // Row callback functions - might want to manipulate the row 3420 // iRowCount and j are not currently documented. Are they at all 3421 // useful? 3422 _fnCallbackFire( oSettings, 'aoRowCallback', null, 3423 [nRow, aoData._aData, iRowCount, j, iDataIndex] ); 3424 3425 anRows.push( nRow ); 3426 iRowCount++; 3427 } 3428 } 3429 else 3430 { 3431 anRows[ 0 ] = _emptyRow(oSettings); 3432 } 3433 3434 /* Header and footer callbacks */ 3435 _fnCallbackFire( oSettings, 'aoHeaderCallback', 'header', [ $(oSettings.nTHead).children('tr')[0], 3436 _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] ); 3437 3438 _fnCallbackFire( oSettings, 'aoFooterCallback', 'footer', [ $(oSettings.nTFoot).children('tr')[0], 3439 _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] ); 3440 3441 // replaceChildren is faster, but only became widespread in 2020, 3442 // so a fall back in jQuery is provided for older browsers. 3443 if (body[0].replaceChildren) { 3444 body[0].replaceChildren.apply(body[0], anRows); 3445 } 3446 else { 3447 body.children().detach(); 3448 body.append( $(anRows) ); 3449 } 3450 3451 // Empty table needs a specific class 3452 $(oSettings.nTableWrapper).toggleClass('dt-empty-footer', $('tr', oSettings.nTFoot).length === 0); 3453 3454 /* Call all required callback functions for the end of a draw */ 3455 _fnCallbackFire( oSettings, 'aoDrawCallback', 'draw', [oSettings], true ); 3456 3457 /* Draw is complete, sorting and filtering must be as well */ 3458 oSettings.bSorted = false; 3459 oSettings.bFiltered = false; 3460 oSettings.bDrawing = false; 3461 } 3462 3463 3464 /** 3465 * Redraw the table - taking account of the various features which are enabled 3466 * @param {object} oSettings dataTables settings object 3467 * @param {boolean} [holdPosition] Keep the current paging position. By default 3468 * the paging is reset to the first page 3469 * @memberof DataTable#oApi 3470 */ 3471 function _fnReDraw( settings, holdPosition, recompute ) 3472 { 3473 var 3474 features = settings.oFeatures, 3475 sort = features.bSort, 3476 filter = features.bFilter; 3477 3478 if (recompute === undefined || recompute === true) { 3479 if ( sort ) { 3480 _fnSort( settings ); 3481 } 3482 3483 if ( filter ) { 3484 _fnFilterComplete( settings, settings.oPreviousSearch ); 3485 } 3486 else { 3487 // No filtering, so we want to just use the display master 3488 settings.aiDisplay = settings.aiDisplayMaster.slice(); 3489 } 3490 } 3491 3492 if ( holdPosition !== true ) { 3493 settings._iDisplayStart = 0; 3494 } 3495 3496 // Let any modules know about the draw hold position state (used by 3497 // scrolling internally) 3498 settings._drawHold = holdPosition; 3499 3500 _fnDraw( settings ); 3501 3502 settings._drawHold = false; 3503 } 3504 3505 3506 /* 3507 * Table is empty - create a row with an empty message in it 3508 */ 3509 function _emptyRow ( settings ) { 3510 var oLang = settings.oLanguage; 3511 var zero = oLang.sZeroRecords; 3512 var dataSrc = _fnDataSource( settings ); 3513 3514 if ( 3515 (settings.iDraw < 1 && dataSrc === 'ssp') || 3516 (settings.iDraw <= 1 && dataSrc === 'ajax') 3517 ) { 3518 zero = oLang.sLoadingRecords; 3519 } 3520 else if ( oLang.sEmptyTable && settings.fnRecordsTotal() === 0 ) 3521 { 3522 zero = oLang.sEmptyTable; 3523 } 3524 3525 return $( '<tr/>' ) 3526 .append( $('<td />', { 3527 'colSpan': _fnVisbleColumns( settings ), 3528 'class': settings.oClasses.empty.row 3529 } ).html( zero ) )[0]; 3530 } 3531 3532 3533 /** 3534 * Convert a `layout` object given by a user to the object structure needed 3535 * for the renderer. This is done twice, once for above and once for below
3536 * the table. Ordering must also be considered. 3537 * 3538 * @param {*} settings DataTables settings object 3539 * @param {*} layout Layout object to convert 3540 * @param {string} side `top` or `bottom` 3541 * @returns Converted array structure - one item for each row. 3542 */ 3543 function _layoutArray ( settings, layout, side ) 3544 { 3545 var groups = {}; 3546 3547 // Combine into like groups (e.g. `top`, `top2`, etc) 3548 $.each( layout, function ( pos, val ) { 3549 if (val === null) { 3550 return; 3551 } 3552 3553 var splitPos = pos.replace(/([A-Z])/g, ' $1').split(' '); 3554 3555 if ( ! groups[ splitPos[0] ] ) { 3556 groups[ splitPos[0] ] = {}; 3557 } 3558 3559 var align = splitPos.length === 1 ? 3560 'full' : 3561 splitPos[1].toLowerCase(); 3562 var group = groups[ splitPos[0] ]; 3563 var groupRun = function (contents, innerVal) { 3564 // If it is an object, then there can be multiple features contained in it 3565 if ( $.isPlainObject( innerVal ) ) { 3566 Object.keys(innerVal).map(function (key) { 3567 contents.push( { 3568 feature: key, 3569 opts: innerVal[key] 3570 }); 3571 }); 3572 } 3573 else { 3574 contents.push(innerVal); 3575 } 3576 } 3577 3578 // Transform to an object with a contents property 3579 if (! group[align] || ! group[align].contents) { 3580 group[align] = { contents: [] }; 3581 } 3582 3583 // Allow for an array or just a single object 3584 if ( Array.isArray(val)) { 3585 for (var i=0 ; i<val.length ; i++) { 3586 groupRun(group[align].contents, val[i]); 3587 } 3588 } 3589 else { 3590 groupRun(group[ align ].contents, val); 3591 } 3592 3593 // And make contents an array 3594 if ( ! Array.isArray( group[ align ].contents ) ) { 3595 group[ align ].contents = [ group[ align ].contents ]; 3596 } 3597 } ); 3598 3599 var filtered = Object.keys(groups) 3600 .map( function ( pos ) { 3601 // Filter to only the side we need 3602 if ( pos.indexOf(side) !== 0 ) { 3603 return null; 3604 } 3605 3606 return { 3607 name: pos, 3608 val: groups[pos] 3609 }; 3610 } ) 3611 .filter( function (item) { 3612 return item !== null; 3613 }); 3614 3615 // Order by item identifier 3616 filtered.sort( function ( a, b ) { 3617 var order1 = a.name.replace(/[^0-9]/g, '') * 1; 3618 var order2 = b.name.replace(/[^0-9]/g, '') * 1; 3619 3620 return order2 - order1; 3621 } ); 3622 3623 if ( side === 'bottom' ) { 3624 filtered.reverse(); 3625 } 3626 3627 // Split into rows 3628 var rows = []; 3629 for ( var i=0, ien=filtered.length ; i<ien ; i++ ) { 3630 if ( filtered[i].val.full ) { 3631 rows.push( { full: filtered[i].val.full } ); 3632 _layoutResolve( settings, rows[ rows.length - 1 ] ); 3633 3634 delete filtered[i].val.full; 3635 } 3636 3637 if ( Object.keys(filtered[i].val).length ) { 3638 rows.push( filtered[i].val ); 3639 _layoutResolve( settings, rows[ rows.length - 1 ] ); 3640 } 3641 } 3642 3643 return rows; 3644 } 3645 3646 3647 /** 3648 * Convert the contents of a row's layout object to nodes that can be inserted 3649 * into the document by a renderer. Execute functions, look up plug-ins, etc. 3650 * 3651 * @param {*} settings DataTables settings object 3652 * @param {*} row Layout object for this row 3653 */ 3654 function _layoutResolve( settings, row ) { 3655 var getFeature = function (feature, opts) { 3656 if ( ! _ext.features[ feature ] ) { 3657 _fnLog( settings, 0, 'Unknown feature: '+ feature ); 3658 } 3659 3660 return _ext.features[ feature ].apply( this, [settings, opts] ); 3661 }; 3662 3663 var resolve = function ( item ) { 3664 var line = row[ item ].contents; 3665 3666 for ( var i=0, ien=line.length ; i<ien ; i++ ) { 3667 if ( ! line[i] ) { 3668 continue; 3669 } 3670 else if ( typeof line[i] === 'string' ) { 3671 line[i] = getFeature( line[i], null ); 3672 } 3673 else if ( $.isPlainObject(line[i]) ) { 3674 // If it's an object, it just has feature and opts properties from 3675 // the transform in _layoutArray 3676 line[i] = getFeature(line[i].feature, line[i].opts); 3677 } 3678 else if ( typeof line[i].node === 'function' ) { 3679 line[i] = line[i].node( settings ); 3680 } 3681 else if ( typeof line[i] === 'function' ) { 3682 var inst = line[i]( settings ); 3683 3684 line[i] = typeof inst.node === 'function' ? 3685 inst.node() : 3686 inst; 3687 } 3688 } 3689 }; 3690 3691 $.each( row, function ( key ) { 3692 resolve( key ); 3693 } ); 3694 } 3695 3696 3697 /** 3698 * Add the options to the page HTML for the table 3699 * @param {object} settings DataTables settings object 3700 * @memberof DataTable#oApi 3701 */ 3702 function _fnAddOptionsHtml ( settings ) 3703 { 3704 var classes = settings.oClasses; 3705 var table = $(settings.nTable); 3706 3707 // Wrapper div around everything DataTables controls
3708 var insert = $('<div/>') 3709 .attr({ 3710 id: settings.sTableId+'_wrapper', 3711 'class': classes.container 3712 }) 3713 .insertBefore(table); 3714 3715 settings.nTableWrapper = insert[0]; 3716 3717 if (settings.sDom) { 3718 // Legacy 3719 _fnLayoutDom(settings, settings.sDom, insert); 3720 } 3721 else { 3722 var top = _layoutArray( settings, settings.layout, 'top' ); 3723 var bottom = _layoutArray( settings, settings.layout, 'bottom' ); 3724 var renderer = _fnRenderer( settings, 'layout' ); 3725 3726 // Everything above - the renderer will actually insert the contents into the document 3727 top.forEach(function (item) { 3728 renderer( settings, insert, item ); 3729 }); 3730 3731 // The table - always the center of attention 3732 renderer( settings, insert, { 3733 full: { 3734 table: true, 3735 contents: [ _fnFeatureHtmlTable(settings) ] 3736 } 3737 } ); 3738 3739 // Everything below 3740 bottom.forEach(function (item) { 3741 renderer( settings, insert, item ); 3742 }); 3743 } 3744 3745 // Processing floats on top, so it isn't an inserted feature 3746 _processingHtml( settings ); 3747 } 3748 3749 /** 3750 * Draw the table with the legacy DOM property 3751 * @param {*} settings DT settings object 3752 * @param {*} dom DOM string 3753 * @param {*} insert Insert point 3754 */ 3755 function _fnLayoutDom( settings, dom, insert ) 3756 { 3757 var parts = dom.match(/(".*?")|('.*?')|./g); 3758 var featureNode, option, newNode, next, attr; 3759 3760 for ( var i=0 ; i<parts.length ; i++ ) { 3761 featureNode = null; 3762 option = parts[i]; 3763 3764 if ( option == '<' ) { 3765 // New container div 3766 newNode = $('<div/>'); 3767 3768 // Check to see if we should append an id and/or a class name to the container 3769 next = parts[i+1]; 3770 3771 if ( next[0] == "'" || next[0] == '"' ) { 3772 attr = next.replace(/['"]/g, ''); 3773 3774 var id = '', className; 3775 3776 /* The attribute can be in the format of "#id.class", "#id" or "class" This logic 3777 * breaks the string into parts and applies them as needed 3778 */ 3779 if ( attr.indexOf('.') != -1 ) { 3780 var split = attr.split('.'); 3781 3782 id = split[0]; 3783 className = split[1]; 3784 } 3785 else if ( attr[0] == "#" ) { 3786 id = attr; 3787 } 3788 else { 3789 className = attr; 3790 } 3791 3792 newNode 3793 .attr('id', id.substring(1)) 3794 .addClass(className); 3795 3796 i++; // Move along the position array 3797 } 3798 3799 insert.append( newNode ); 3800 insert = newNode; 3801 } 3802 else if ( option == '>' ) { 3803 // End container div 3804 insert = insert.parent(); 3805 } 3806 else if ( option == 't' ) { 3807 // Table 3808 featureNode = _fnFeatureHtmlTable( settings ); 3809 } 3810 else 3811 { 3812 DataTable.ext.feature.forEach(function(feature) { 3813 if ( option == feature.cFeature ) { 3814 featureNode = feature.fnInit( settings ); 3815 } 3816 }); 3817 } 3818 3819 // Add to the display 3820 if ( featureNode ) { 3821 insert.append( featureNode ); 3822 } 3823 } 3824 } 3825 3826 3827 /** 3828 * Use the DOM source to create up an array of header cells. The idea here is to 3829 * create a layout grid (array) of rows x columns, which contains a reference 3830 * to the cell that that point in the grid (regardless of col/rowspan), such that 3831 * any column / row could be removed and the new grid constructed 3832 * @param {node} thead The header/footer element for the table 3833 * @returns {array} Calculated layout array 3834 * @memberof DataTable#oApi 3835 */ 3836 function _fnDetectHeader ( settings, thead, write ) 3837 { 3838 var columns = settings.aoColumns; 3839 var rows = $(thead).children('tr'); 3840 var row, cell; 3841 var i, k, l, iLen, shifted, column, colspan, rowspan; 3842 var isHeader = thead && thead.nodeName.toLowerCase() === 'thead'; 3843 var layout = []; 3844 var unique; 3845 var shift = function ( a, i, j ) { 3846 var k = a[i]; 3847 while ( k[j] ) { 3848 j++; 3849 } 3850 return j; 3851 }; 3852 3853 // We know how many rows there are in the layout - so prep it 3854 for ( i=0, iLen=rows.length ; i<iLen ; i++ ) { 3855 layout.push( [] ); 3856 } 3857 3858 for ( i=0, iLen=rows.length ; i<iLen ; i++ ) { 3859 row = rows[i]; 3860 column = 0; 3861 3862 // For every cell in the row.. 3863 cell = row.firstChild; 3864 while ( cell ) { 3865 if ( 3866 cell.nodeName.toUpperCase() == 'TD' || 3867 cell.nodeName.toUpperCase() == 'TH' 3868 ) { 3869 var cols = []; 3870 3871 // Get the col and rowspan attributes from the DOM and sanitise them 3872 colspan = cell.getAttribute('colspan') * 1; 3873 rowspan = cell.getAttribute('rowspan') * 1; 3874 colspan = (!colspan || colspan===0 || colspan===1) ? 1 : colspan; 3875 rowspan = (!rowspan || rowspan===0 || rowspan===1) ? 1 : rows
3875pan; 3876 3877 // There might be colspan cells already in this row, so shift our target 3878 // accordingly 3879 shifted = shift( layout, i, column ); 3880 3881 // Cache calculation for unique columns 3882 unique = colspan === 1 ? 3883 true : 3884 false; 3885 3886 // Perform header setup 3887 if ( write ) { 3888 if (unique) { 3889 // Allow column options to be set from HTML attributes 3890 _fnColumnOptions( settings, shifted, $(cell).data() ); 3891 3892 // Get the width for the column. This can be defined from the 3893 // width attribute, style attribute or `columns.width` option 3894 var columnDef = columns[shifted]; 3895 var width = cell.getAttribute('width') || null; 3896 var t = cell.style.width.match(/width:\s*(\d+[pxem%]+)/); 3897 if ( t ) { 3898 width = t[1]; 3899 } 3900 3901 columnDef.sWidthOrig = columnDef.sWidth || width; 3902 3903 if (isHeader) { 3904 // Column title handling - can be user set, or read from the DOM 3905 // This happens before the render, so the original is still in place 3906 if ( columnDef.sTitle !== null && ! columnDef.autoTitle ) { 3907 cell.innerHTML = columnDef.sTitle; 3908 } 3909 3910 if (! columnDef.sTitle && unique) { 3911 columnDef.sTitle = _stripHtml(cell.innerHTML); 3912 columnDef.autoTitle = true; 3913 } 3914 } 3915 else { 3916 // Footer specific operations 3917 if (columnDef.footer) { 3918 cell.innerHTML = columnDef.footer; 3919 } 3920 } 3921 3922 // Fall back to the aria-label attribute on the table header if no ariaTitle is 3923 // provided. 3924 if (! columnDef.ariaTitle) { 3925 columnDef.ariaTitle = $(cell).attr("aria-label") || columnDef.sTitle; 3926 } 3927 3928 // Column specific class names 3929 if ( columnDef.className ) { 3930 $(cell).addClass( columnDef.className ); 3931 } 3932 } 3933 3934 // Wrap the column title so we can write to it in future 3935 if ( $('span.dt-column-title', cell).length === 0) { 3936 $('<span>') 3937 .addClass('dt-column-title') 3938 .append(cell.childNodes) 3939 .appendTo(cell); 3940 } 3941 3942 if ( isHeader && $('span.dt-column-order', cell).length === 0) { 3943 $('<span>') 3944 .addClass('dt-column-order') 3945 .appendTo(cell); 3946 } 3947 } 3948 3949 // If there is col / rowspan, copy the information into the layout grid 3950 for ( l=0 ; l<colspan ; l++ ) { 3951 for ( k=0 ; k<rowspan ; k++ ) { 3952 layout[i+k][shifted+l] = { 3953 cell: cell, 3954 unique: unique 3955 }; 3956 3957 layout[i+k].row = row; 3958 } 3959 3960 cols.push( shifted+l ); 3961 } 3962 3963 // Assign an attribute so spanning cells can still be identified 3964 // as belonging to a column 3965 cell.setAttribute('data-dt-column', _unique(cols).join(',')); 3966 } 3967 3968 cell = cell.nextSibling; 3969 } 3970 } 3971 3972 return layout; 3973 } 3974 3975 /** 3976 * Set the start position for draw 3977 * @param {object} oSettings dataTables settings object 3978 */ 3979 function _fnStart( oSettings ) 3980 { 3981 var bServerSide = _fnDataSource( oSettings ) == 'ssp'; 3982 var iInitDisplayStart = oSettings.iInitDisplayStart; 3983 3984 // Check and see if we have an initial draw position from state saving 3985 if ( iInitDisplayStart !== undefined && iInitDisplayStart !== -1 ) 3986 { 3987 oSettings._iDisplayStart = bServerSide ? 3988 iInitDisplayStart : 3989 iInitDisplayStart >= oSettings.fnRecordsDisplay() ? 3990 0 : 3991 iInitDisplayStart; 3992 3993 oSettings.iInitDisplayStart = -1; 3994 } 3995 } 3996 3997 /** 3998 * Create an Ajax call based on the table's settings, taking into account that 3999 * parameters can have multiple forms, and backwards compatibility. 4000 * 4001 * @param {object} oSettings dataTables settings object 4002 * @param {array} data Data to send to the server, required by 4003 * DataTables - may be augmented by developer callbacks 4004 * @param {function} fn Callback function to run when data is obtained 4005 */ 4006 function _fnBuildAjax( oSettings, data, fn ) 4007 { 4008 var ajaxData; 4009 var ajax = oSettings.ajax; 4010 var instance = oSettings.oInstance; 4011 var callback = function ( json ) { 4012 var status = oSettings.jqXHR 4013 ? oSettings.jqXHR.status 4014 : null; 4015 4016 if ( json === null || (typeof status === 'number' && status == 204 ) ) { 4017 json = {}; 4018 _fnAjaxDataSrc( oSettings, json, [] ); 4019 } 4020 4021 var error = json.error || json.sError; 4022 if ( error ) { 4023 _fnLog( oSettings, 0, error ); 4024 } 4025 4026 oSettings.json = json; 4027 4028 _fnCallbackFire( oSettings, null, 'xhr', [oSettings, json, oSettings.jqXHR], true ); 4029 fn( json ); 4030 }; 4031 4032 if ( $.isPlainObject( ajax ) && ajax.data ) 4033 { 4034 ajaxData = ajax.data; 4035 4036 var newData = typeof ajaxData === 'function' ? 4037 ajaxData( data, oSettings ) : // fn can manipulate data or return 4038 ajaxData; // an object object or array to merge 4039 4040 // If the function returned something, use that alone 4041 data = typeof ajaxData === 'function' && newData ? 4042 newData : 4043 $.extend( true, data, newData ); 4044
4045 // Remove the data property as we've resolved it already and don't want 4046 // jQuery to do it again (it is restored at the end of the function) 4047 delete ajax.data; 4048 } 4049 4050 var baseAjax = { 4051 "url": typeof ajax === 'string' ? 4052 ajax : 4053 '', 4054 "data": data, 4055 "success": callback, 4056 "dataType": "json", 4057 "cache": false, 4058 "type": oSettings.sServerMethod, 4059 "error": function (xhr, error) { 4060 var ret = _fnCallbackFire( oSettings, null, 'xhr', [oSettings, null, oSettings.jqXHR], true ); 4061 4062 if ( ret.indexOf(true) === -1 ) { 4063 if ( error == "parsererror" ) { 4064 _fnLog( oSettings, 0, 'Invalid JSON response', 1 ); 4065 } 4066 else if ( xhr.readyState === 4 ) { 4067 _fnLog( oSettings, 0, 'Ajax error', 7 ); 4068 } 4069 } 4070 4071 _fnProcessingDisplay( oSettings, false ); 4072 } 4073 }; 4074 4075 // If `ajax` option is an object, extend and override our default base 4076 if ( $.isPlainObject( ajax ) ) { 4077 $.extend( baseAjax, ajax ) 4078 } 4079 4080 // Store the data submitted for the API 4081 oSettings.oAjaxData = data; 4082 4083 // Allow plug-ins and external processes to modify the data 4084 _fnCallbackFire( oSettings, null, 'preXhr', [oSettings, data, baseAjax], true ); 4085 4086 if ( typeof ajax === 'function' ) 4087 { 4088 // Is a function - let the caller define what needs to be done 4089 oSettings.jqXHR = ajax.call( instance, data, callback, oSettings ); 4090 } 4091 else if (ajax.url === '') { 4092 // No url, so don't load any data. Just apply an empty data array 4093 // to the object for the callback. 4094 var empty = {}; 4095 4096 DataTable.util.set(ajax.dataSrc)(empty, []); 4097 callback(empty); 4098 } 4099 else { 4100 // Object to extend the base settings 4101 oSettings.jqXHR = $.ajax( baseAjax ); 4102 4103 // Restore for next time around 4104 if ( ajaxData ) { 4105 ajax.data = ajaxData; 4106 } 4107 } 4108 } 4109 4110 4111 /** 4112 * Update the table using an Ajax call 4113 * @param {object} settings dataTables settings object 4114 * @returns {boolean} Block the table drawing or not 4115 * @memberof DataTable#oApi 4116 */ 4117 function _fnAjaxUpdate( settings ) 4118 { 4119 settings.iDraw++; 4120 _fnProcessingDisplay( settings, true ); 4121 4122 _fnBuildAjax( 4123 settings, 4124 _fnAjaxParameters( settings ), 4125 function(json) { 4126 _fnAjaxUpdateDraw( settings, json ); 4127 } 4128 ); 4129 } 4130 4131 4132 /** 4133 * Build up the parameters in an object needed for a server-side processing 4134 * request. 4135 * @param {object} oSettings dataTables settings object 4136 * @returns {bool} block the table drawing or not 4137 * @memberof DataTable#oApi 4138 */ 4139 function _fnAjaxParameters( settings ) 4140 { 4141 var 4142 columns = settings.aoColumns, 4143 features = settings.oFeatures, 4144 preSearch = settings.oPreviousSearch, 4145 preColSearch = settings.aoPreSearchCols, 4146 colData = function ( idx, prop ) { 4147 return typeof columns[idx][prop] === 'function' ? 4148 'function' : 4149 columns[idx][prop]; 4150 }; 4151 4152 return { 4153 draw: settings.iDraw, 4154 columns: columns.map( function ( column, i ) { 4155 return { 4156 data: colData(i, 'mData'), 4157 name: column.sName, 4158 searchable: column.bSearchable, 4159 orderable: column.bSortable, 4160 search: { 4161 value: preColSearch[i].search, 4162 regex: preColSearch[i].regex, 4163 fixed: Object.keys(column.searchFixed).map( function(name) { 4164 return { 4165 name: name, 4166 term: column.searchFixed[name].toString() 4167 } 4168 }) 4169 } 4170 }; 4171 } ), 4172 order: _fnSortFlatten( settings ).map( function ( val ) { 4173 return { 4174 column: val.col, 4175 dir: val.dir, 4176 name: colData(val.col, 'sName') 4177 }; 4178 } ), 4179 start: settings._iDisplayStart, 4180 length: features.bPaginate ? 4181 settings._iDisplayLength : 4182 -1, 4183 search: { 4184 value: preSearch.search, 4185 regex: preSearch.regex, 4186 fixed: Object.keys(settings.searchFixed).map( function(name) { 4187 return { 4188 name: name, 4189 term: settings.searchFixed[name].toString() 4190 } 4191 }) 4192 } 4193 }; 4194 } 4195 4196 4197 /** 4198 * Data the data from the server (nuking the old) and redraw the table 4199 * @param {object} oSettings dataTables settings object 4200 * @param {object} json json data return from the server. 4201 * @param {string} json.sEcho Tracking flag for DataTables to match requests 4202 * @param {int} json.iTotalRecords Number of records in the data set, not accounting for filtering 4203 * @param {int} json.iTotalDisplayRecords Number of records in the data set, accounting for filtering 4204 * @param {array} json.aaData The data to display on this page 4205 * @param {string} [json.sColumns] Column ordering (sName, comma separated) 4206 * @memberof DataTable#oApi 4207 */ 4208 function _fnAjaxUpdateDraw ( settings, json ) 4209 { 4210 var data = _fnAjaxDataSrc(settings, json); 4211 var draw = _fnAjaxDataSrcParam(settings, 'draw', json); 4212 var recordsTotal = _fnAjaxDataSrcParam(settings, 'recordsTotal', json); 4213 var recordsFiltered = _fnAjaxDataSrcParam(settings, 'recordsFiltered', json); 4214 4215 if ( draw !== undefined ) { 4216 // Protect against out of sequence returns 4217 if ( draw*1 < settings.iDraw ) { 4218 return; 4219 } 4220 settings.iDraw = draw * 1; 4221 } 4222 4223 // No data in returned object, so rather than an array, we show an empty table 4224 if ( ! data ) { 4225 data = []; 4226 } 4227 4228 _fnClearTable( settings ); 4229 settings._iRecordsTotal = parseInt(recordsTotal, 10); 4230 settings._iRecordsDisplay = parseInt(recordsFiltered, 10); 4231 4232 for ( var i=0, ien=data.length ; i<ien ; i++ ) { 4233 _fnAddData( settings, data[i] ); 4234 } 4235 settings.aiDisplay = settings.aiDisplayMaster.slice(); 4236 4237 _fnDraw( settings, true ); 4238 _fnInitComplete( settings ); 4239 _fnProcessingDisplay( settings, false ); 4240 } 4241 4242 4243 /** 4244 * Get the data from the JSON data source to use for drawing a table. Using 4245 * `_fnGetObjectDataFn` allows the data to be sourced from a property of the 4246 * source object, or from a processing function. 4247 * @param {object} settings dataTables settings object 4248 * @param {object} json Data source object / array from the server 4249 * @return {array} Array of data to use 4250 */ 4251 function _fnAjaxDataSrc ( settings, json, write ) 4252 { 4253 var dataProp = 'data'; 4254 4255 if ($.isPlainObject( settings.ajax ) && settings.ajax.dataSrc !== undefined) { 4256 // Could in inside a `dataSrc` object, or not! 4257 var dataSrc = settings.ajax.dataSrc; 4258 4259 // string, function and object are valid types 4260 if (typeof dataSrc === 'string' || typeof dataSrc === 'function') { 4261 dataProp = dataSrc; 4262 } 4263 else if (dataSrc.data !== undefined) { 4264 dataProp = dataSrc.data; 4265 } 4266 } 4267 4268 if ( ! write ) { 4269 if ( dataProp === 'data' ) {
4270 // If the default, then we still want to support the old style, and safely ignore 4271 // it if possible 4272 return json.aaData || json[dataProp]; 4273 } 4274 4275 return dataProp !== "" ? 4276 _fnGetObjectDataFn( dataProp )( json ) : 4277 json; 4278 } 4279 4280 // set 4281 _fnSetObjectDataFn( dataProp )( json, write ); 4282 } 4283 4284 /** 4285 * Very similar to _fnAjaxDataSrc, but for the other SSP properties 4286 * @param {*} settings DataTables settings object 4287 * @param {*} param Target parameter 4288 * @param {*} json JSON data 4289 * @returns Resolved value 4290 */ 4291 function _fnAjaxDataSrcParam (settings, param, json) { 4292 var dataSrc = $.isPlainObject( settings.ajax ) 4293 ? settings.ajax.dataSrc 4294 : null; 4295 4296 if (dataSrc && dataSrc[param]) { 4297 // Get from custom location 4298 return _fnGetObjectDataFn( dataSrc[param] )( json ); 4299 } 4300 4301 // else - Default behaviour 4302 var old = ''; 4303 4304 // Legacy support 4305 if (param === 'draw') { 4306 old = 'sEcho'; 4307 } 4308 else if (param === 'recordsTotal') { 4309 old = 'iTotalRecords'; 4310 } 4311 else if (param === 'recordsFiltered') { 4312 old = 'iTotalDisplayRecords'; 4313 } 4314 4315 return json[old] !== undefined 4316 ? json[old] 4317 : json[param]; 4318 } 4319 4320 4321 /** 4322 * Filter the table using both the global filter and column based filtering 4323 * @param {object} settings dataTables settings object 4324 * @param {object} input search information 4325 * @memberof DataTable#oApi 4326 */ 4327 function _fnFilterComplete ( settings, input ) 4328 { 4329 var columnsSearch = settings.aoPreSearchCols; 4330 4331 // Resolve any column types that are unknown due to addition or invalidation 4332 // @todo As per sort - can this be moved into an event handler? 4333 _fnColumnTypes( settings ); 4334 4335 // In server-side processing all filtering is done by the server, so no point hanging around here 4336 if ( _fnDataSource( settings ) != 'ssp' ) 4337 { 4338 // Check if any of the rows were invalidated 4339 _fnFilterData( settings ); 4340 4341 // Start from the full data set 4342 settings.aiDisplay = settings.aiDisplayMaster.slice(); 4343 4344 // Global filter first 4345 _fnFilter( settings.aiDisplay, settings, input.search, input ); 4346 4347 $.each(settings.searchFixed, function (name, term) { 4348 _fnFilter(settings.aiDisplay, settings, term, {}); 4349 }); 4350 4351 // Then individual column filters 4352 for ( var i=0 ; i<columnsSearch.length ; i++ ) 4353 { 4354 var col = columnsSearch[i]; 4355 4356 _fnFilter( 4357 settings.aiDisplay, 4358 settings, 4359 col.search, 4360 col, 4361 i 4362 ); 4363 4364 $.each(settings.aoColumns[i].searchFixed, function (name, term) { 4365 _fnFilter(settings.aiDisplay, settings, term, {}, i); 4366 }); 4367 } 4368 4369 // And finally global filtering 4370 _fnFilterCustom( settings ); 4371 } 4372 4373 // Tell the draw function we have been filtering 4374 settings.bFiltered = true; 4375 4376 _fnCallbackFire( settings, null, 'search', [settings] ); 4377 } 4378 4379 4380 /** 4381 * Apply custom filtering functions 4382 * 4383 * This is legacy now that we have named functions, but it is widely used 4384 * from 1.x, so it is not yet deprecated. 4385 * @param {object} oSettings dataTables settings object 4386 * @memberof DataTable#oApi 4387 */ 4388 function _fnFilterCustom( settings ) 4389 { 4390 var filters = DataTable.ext.search; 4391 var displayRows = settings.aiDisplay; 4392 var row, rowIdx; 4393 4394 for ( var i=0, ien=filters.length ; i<ien ; i++ ) { 4395 var rows = []; 4396 4397 // Loop over each row and see if it should be included 4398 for ( var j=0, jen=displayRows.length ; j<jen ; j++ ) { 4399 rowIdx = displayRows[ j ]; 4400 row = settings.aoData[ rowIdx ]; 4401 4402 if ( filters[i]( settings, row._aFilterData, rowIdx, row._aData, j ) ) { 4403 rows.push( rowIdx ); 4404 } 4405 } 4406 4407 // So the array reference doesn't break set the results into the 4408 // existing array 4409 displayRows.length = 0; 4410 displayRows.push.apply(displayRows, rows); 4411 } 4412 } 4413 4414 4415 /** 4416 * Filter the data table based on user input and draw the table 4417 */ 4418 function _fnFilter( searchRows, settings, input, options, column ) 4419 { 4420 if ( input === '' ) { 4421 return; 4422 } 4423 4424 var i = 0; 4425 var matched = []; 4426 4427 // Search term can be a function, regex or string - if a string we apply our 4428 // smart filtering regex (assuming the options require that) 4429 var searchFunc = typeof input === 'function' ? input : null; 4430 var rpSearch = input instanceof RegExp 4431 ? input 4432 : searchFunc 4433 ? null 4434 : _fnFilterCreateSearch( input, options ); 4435 4436 // Then for each row, does the test pass. If not, lop the row from the array 4437 for (i=0 ; i<searchRows.length ; i++) { 4438 var row = settings.aoData[ searchRows[i] ]; 4439 var data = column === undefined 4440 ? row._sFilterRow 4441 : row._aFilterData[ column ]; 4442 4443 if ( (searchFunc && searchFunc(data, row._aData, searchRows[i], column)) || (rpSearch && rpSearch.test(data)) ) { 4444 matched.push(searchRows[i]); 4445 } 4446 } 4447 4448 // Mutate the searchRows array 4449 searchRows.length = matched.length; 4450 4451 for (i=0 ; i<matched.length ; i++) { 4452 searchRows[i] = matched[i]; 4453 } 4454 } 4455 4456 4457 /** 4458 * Build a regular expression object suitable for searching a table 4459 * @param {string} sSearch string to search for 4460 * @param {bool} bRegex treat as a regular expression or not 4461 * @param {bool} bSmart perform smart filtering or not 4462 * @param {bool} bCaseInsensitive Do case insensitive matching or not 4463 * @returns {RegExp} constructed object 4464 * @memberof DataTable#oApi 4465 */ 4466 function _fnFilterCreateSearch( search, inOpts ) 4467 { 4468 var not = []; 4469 var options = $.extend({}, { 4470 boundary: false, 4471 caseInsensitive: true, 4472 exact: false, 4473 regex: false, 4474 smart: true 4475 }, inOpts); 4476 4477 if (typeof search !== 'string') { 4478 search = search.toString(); 4479 } 4480 4481 // Remove diacritics if normalize is set up to do so 4482 search = _normalize(search); 4483 4484 if (options.exact) { 4485 return new RegExp( 4486 '^'+_fnEscapeRegex(search)+'$', 4487 options.caseInsensitive ? 'i' : '' 4488 ); 4489 } 4490 4491 search = options.regex ? 4492 search : 4493 _fnEscapeRegex( search ); 4494 4495 if ( options.smart ) { 4496 /* For smart filtering we want to allow the search to work regardless of 4497 * word order. We also want double quoted text to be preserved, so word 4498 * order is important - a la google. And a negative look around for 4499 * finding rows which don't contain a given string. 4500 * 4501 * So this is the sort of thing we want to generate: 4502 * 4503 * ^(?=.*?\bone\b)(?=.*?\btwo three\b)(?=.*?\bfour\b).*$ 4504 */ 4505 var parts = search.match( /!?["\u201C][^"\u201D]+["\u201D]|[^ ]+/g ) || ['']; 4506 var a = parts.map( function ( word ) { 4507 var negative = false;
4508 var m; 4509 4510 // Determine if it is a "does not include" 4511 if ( word.charAt(0) === '!' ) { 4512 negative = true; 4513 word = word.substring(1); 4514 } 4515 4516 // Strip the quotes from around matched phrases 4517 if ( word.charAt(0) === '"' ) { 4518 m = word.match( /^"(.*)"$/ ); 4519 word = m ? m[1] : word; 4520 } 4521 else if ( word.charAt(0) === '\u201C' ) { 4522 // Smart quote match (iPhone users) 4523 m = word.match( /^\u201C(.*)\u201D$/ ); 4524 word = m ? m[1] : word; 4525 } 4526 4527 // For our "not" case, we need to modify the string that is 4528 // allowed to match at the end of the expression. 4529 if (negative) { 4530 if (word.length > 1) { 4531 not.push('(?!'+word+')'); 4532 } 4533 4534 word = ''; 4535 } 4536 4537 return word.replace(/"/g, ''); 4538 } ); 4539 4540 var match = not.length 4541 ? not.join('') 4542 : ''; 4543 4544 var boundary = options.boundary 4545 ? '\\b' 4546 : ''; 4547 4548 search = '^(?=.*?'+boundary+a.join( ')(?=.*?'+boundary )+')('+match+'.)*$'; 4549 } 4550 4551 return new RegExp( search, options.caseInsensitive ? 'i' : '' ); 4552 } 4553 4554 4555 /** 4556 * Escape a string such that it can be used in a regular expression 4557 * @param {string} sVal string to escape 4558 * @returns {string} escaped string 4559 * @memberof DataTable#oApi 4560 */ 4561 var _fnEscapeRegex = DataTable.util.escapeRegex; 4562 4563 var __filter_div = $('<div>')[0]; 4564 var __filter_div_textContent = __filter_div.textContent !== undefined; 4565 4566 // Update the filtering data for each row if needed (by invalidation or first run) 4567 function _fnFilterData ( settings ) 4568 { 4569 var columns = settings.aoColumns; 4570 var data = settings.aoData; 4571 var column; 4572 var j, jen, filterData, cellData, row; 4573 var wasInvalidated = false; 4574 4575 for ( var rowIdx=0 ; rowIdx<data.length ; rowIdx++ ) { 4576 if (! data[rowIdx]) { 4577 continue; 4578 } 4579 4580 row = data[rowIdx]; 4581 4582 if ( ! row._aFilterData ) { 4583 filterData = []; 4584 4585 for ( j=0, jen=columns.length ; j<jen ; j++ ) { 4586 column = columns[j]; 4587 4588 if ( column.bSearchable ) { 4589 cellData = _fnGetCellData( settings, rowIdx, j, 'filter' ); 4590 4591 // Search in DataTables is string based 4592 if ( cellData === null ) { 4593 cellData = ''; 4594 } 4595 4596 if ( typeof cellData !== 'string' && cellData.toString ) { 4597 cellData = cellData.toString(); 4598 } 4599 } 4600 else { 4601 cellData = ''; 4602 } 4603 4604 // If it looks like there is an HTML entity in the string, 4605 // attempt to decode it so sorting works as expected. Note that 4606 // we could use a single line of jQuery to do this, but the DOM 4607 // method used here is much faster https://jsperf.com/html-decode 4608 if ( cellData.indexOf && cellData.indexOf('&') !== -1 ) { 4609 __filter_div.innerHTML = cellData; 4610 cellData = __filter_div_textContent ? 4611 __filter_div.textContent : 4612 __filter_div.innerText; 4613 } 4614 4615 if ( cellData.replace ) { 4616 cellData = cellData.replace(/[\r\n\u2028]/g, ''); 4617 } 4618 4619 filterData.push( cellData ); 4620 } 4621 4622 row._aFilterData = filterData; 4623 row._sFilterRow = filterData.join(' '); 4624 wasInvalidated = true; 4625 } 4626 } 4627 4628 return wasInvalidated; 4629 } 4630 4631 4632 /** 4633 * Draw the table for the first time, adding all required features 4634 * @param {object} settings dataTables settings object 4635 * @memberof DataTable#oApi 4636 */ 4637 function _fnInitialise ( settings ) 4638 { 4639 var i, iAjaxStart=settings.iInitDisplayStart; 4640 4641 /* Ensure that the table data is fully initialised */ 4642 if ( ! settings.bInitialised ) { 4643 setTimeout( function(){ _fnInitialise( settings ); }, 200 ); 4644 return; 4645 } 4646 4647 /* Build and draw the header / footer for the table */ 4648 _fnBuildHead( settings, 'header' ); 4649 _fnBuildHead( settings, 'footer' ); 4650 _fnDrawHead( settings, settings.aoHeader ); 4651 _fnDrawHead( settings, settings.aoFooter ); 4652 4653 // Enable features 4654 _fnAddOptionsHtml( settings ); 4655 _fnSortInit( settings ); 4656 4657 _colGroup( settings ); 4658 4659 /* Okay to show that something is going on now */ 4660 _fnProcessingDisplay( settings, true ); 4661 4662 _fnCallbackFire( settings, null, 'preInit', [settings], true ); 4663 4664 // If there is default sorting required - let's do it. The sort function 4665 // will do the drawing for us. Otherwise we draw the table regardless of the 4666 // Ajax source - this allows the table to look initialised for Ajax sourcing 4667 // data (show 'loading' message possibly) 4668 _fnReDraw( settings ); 4669 4670 var dataSrc = _fnDataSource( settings ); 4671 4672 // Server-side processing init complete is done by _fnAjaxUpdateDraw 4673 if ( dataSrc != 'ssp' ) { 4674 // if there is an ajax source load the data 4675 if ( dataSrc == 'ajax' ) { 4676 _fnBuildAjax( settings, {}, function(json) { 4677 var aData = _fnAjaxDataSrc( settings, json ); 4678 4679 // Got the data - add it to the table 4680 for ( i=0 ; i<aData.length ; i++ ) { 4681 _fnAddData( settings, aData[i] ); 4682 } 4683
4684 // Reset the init display for cookie saving. We've already done 4685 // a filter, and therefore cleared it before. So we need to make 4686 // it appear 'fresh' 4687 settings.iInitDisplayStart = iAjaxStart; 4688 4689 _fnReDraw( settings ); 4690 _fnProcessingDisplay( settings, false ); 4691 _fnInitComplete( settings ); 4692 }, settings ); 4693 } 4694 else { 4695 _fnInitComplete( settings ); 4696 _fnProcessingDisplay( settings, false ); 4697 } 4698 } 4699 } 4700 4701 4702 /** 4703 * Draw the table for the first time, adding all required features 4704 * @param {object} settings dataTables settings object 4705 * @memberof DataTable#oApi 4706 */ 4707 function _fnInitComplete ( settings ) 4708 { 4709 if (settings._bInitComplete) { 4710 return; 4711 } 4712 4713 var args = [settings, settings.json]; 4714 4715 settings._bInitComplete = true; 4716 4717 // Table is fully set up and we have data, so calculate the 4718 // column widths 4719 _fnAdjustColumnSizing( settings ); 4720 4721 _fnCallbackFire( settings, null, 'plugin-init', args, true ); 4722 _fnCallbackFire( settings, 'aoInitComplete', 'init', args, true ); 4723 } 4724 4725 function _fnLengthChange ( settings, val ) 4726 { 4727 var len = parseInt( val, 10 ); 4728 settings._iDisplayLength = len; 4729 4730 _fnLengthOverflow( settings ); 4731 4732 // Fire length change event 4733 _fnCallbackFire( settings, null, 'length', [settings, len] ); 4734 } 4735 4736 /** 4737 * Alter the display settings to change the page 4738 * @param {object} settings DataTables settings object 4739 * @param {string|int} action Paging action to take: "first", "previous", 4740 * "next" or "last" or page number to jump to (integer) 4741 * @param [bool] redraw Automatically draw the update or not 4742 * @returns {bool} true page has changed, false - no change 4743 * @memberof DataTable#oApi 4744 */ 4745 function _fnPageChange ( settings, action, redraw ) 4746 { 4747 var 4748 start = settings._iDisplayStart, 4749 len = settings._iDisplayLength, 4750 records = settings.fnRecordsDisplay(); 4751 4752 if ( records === 0 || len === -1 ) 4753 { 4754 start = 0; 4755 } 4756 else if ( typeof action === "number" ) 4757 { 4758 start = action * len; 4759 4760 if ( start > records ) 4761 { 4762 start = 0; 4763 } 4764 } 4765 else if ( action == "first" ) 4766 { 4767 start = 0; 4768 } 4769 else if ( action == "previous" ) 4770 { 4771 start = len >= 0 ? 4772 start - len : 4773 0; 4774 4775 if ( start < 0 ) 4776 { 4777 start = 0; 4778 } 4779 } 4780 else if ( action == "next" ) 4781 { 4782 if ( start + len < records ) 4783 { 4784 start += len; 4785 } 4786 } 4787 else if ( action == "last" ) 4788 { 4789 start = Math.floor( (records-1) / len) * len; 4790 } 4791 else if ( action === 'ellipsis' ) 4792 { 4793 return; 4794 } 4795 else 4796 { 4797 _fnLog( settings, 0, "Unknown paging action: "+action, 5 ); 4798 } 4799 4800 var changed = settings._iDisplayStart !== start; 4801 settings._iDisplayStart = start; 4802 4803 _fnCallbackFire( settings, null, changed ? 'page' : 'page-nc', [settings] ); 4804 4805 if ( changed && redraw ) { 4806 _fnDraw( settings ); 4807 } 4808 4809 return changed; 4810 } 4811 4812 4813 /** 4814 * Generate the node required for the processing node 4815 * @param {object} settings DataTables settings object 4816 */ 4817 function _processingHtml ( settings ) 4818 { 4819 var table = settings.nTable; 4820 var scrolling = settings.oScroll.sX !== '' || settings.oScroll.sY !== ''; 4821 4822 if ( settings.oFeatures.bProcessing ) { 4823 var n = $('<div/>', { 4824 'id': settings.sTableId + '_processing', 4825 'class': settings.oClasses.processing.container, 4826 'role': 'status' 4827 } ) 4828 .html( settings.oLanguage.sProcessing ) 4829 .append('<div><div></div><div></div><div></div><div></div></div>'); 4830 4831 // Different positioning depending on if scrolling is enabled or not 4832 if (scrolling) { 4833 n.prependTo( $('div.dt-scroll', settings.nTableWrapper) ); 4834 } 4835 else { 4836 n.insertBefore( table ); 4837 } 4838 4839 $(table).on( 'processing.dt.DT', function (e, s, show) { 4840 n.css( 'display', show ? 'block' : 'none' ); 4841 } ); 4842 } 4843 } 4844 4845 4846 /** 4847 * Display or hide the processing indicator 4848 * @param {object} settings DataTables settings object 4849 * @param {bool} show Show the processing indicator (true) or not (false) 4850 */ 4851 function _fnProcessingDisplay ( settings, show ) 4852 { 4853 _fnCallbackFire( settings, null, 'processing', [settings, show] ); 4854 } 4855 /** 4856 * Add any control elements for the table - specifically scrolling 4857 * @param {object} settings dataTables settings object 4858 * @returns {node} Node to add to the DOM 4859 * @memberof DataTable#oApi 4860 */ 4861 function _fnFeatureHtmlTable ( settings ) 4862 { 4863 var table = $(settings.nTable); 4864 4865 // Scrolling from here on in 4866 var scroll = settings.oScroll; 4867
vendor: 4,403 bytes, lines 4868-5032
4868 if ( scroll.sX === '' && scroll.sY === '' ) { 4869 return settings.nTable; 4870 } 4871 4872 var scrollX = scroll.sX; 4873 var scrollY = scroll.sY; 4874 var classes = settings.oClasses.scrolling; 4875 var caption = settings.captionNode; 4876 var captionSide = caption ? caption._captionSide : null; 4877 var headerClone = $( table[0].cloneNode(false) ); 4878 var footerClone = $( table[0].cloneNode(false) ); 4879 var footer = table.children('tfoot'); 4880 var _div = '<div/>'; 4881 var size = function ( s ) { 4882 return !s ? null : _fnStringToCss( s ); 4883 }; 4884 4885 if ( ! footer.length ) { 4886 footer = null; 4887 } 4888 4889 /* 4890 * The HTML structure that we want to generate in this function is: 4891 * div - scroller 4892 * div - scroll head 4893 * div - scroll head inner 4894 * table - scroll head table 4895 * thead - thead 4896 * div - scroll body 4897 * table - table (master table) 4898 * thead - thead clone for sizing 4899 * tbody - tbody 4900 * div - scroll foot 4901 * div - scroll foot inner 4902 * table - scroll foot table 4903 * tfoot - tfoot 4904 */ 4905 var scroller = $( _div, { 'class': classes.container } ) 4906 .append( 4907 $(_div, { 'class': classes.header.self } ) 4908 .css( { 4909 overflow: 'hidden', 4910 position: 'relative', 4911 border: 0, 4912 width: scrollX ? size(scrollX) : '100%' 4913 } ) 4914 .append( 4915 $(_div, { 'class': classes.header.inner } ) 4916 .css( { 4917 'box-sizing': 'content-box', 4918 width: scroll.sXInner || '100%' 4919 } ) 4920 .append( 4921 headerClone 4922 .removeAttr('id') 4923 .css( 'margin-left', 0 ) 4924 .append( captionSide === 'top' ? caption : null ) 4925 .append( 4926 table.children('thead') 4927 ) 4928 ) 4929 ) 4930 ) 4931 .append( 4932 $(_div, { 'class': classes.body } ) 4933 .css( { 4934 position: 'relative', 4935 overflow: 'auto', 4936 width: size( scrollX ) 4937 } ) 4938 .append( table ) 4939 ); 4940 4941 if ( footer ) { 4942 scroller.append( 4943 $(_div, { 'class': classes.footer.self } ) 4944 .css( { 4945 overflow: 'hidden', 4946 border: 0, 4947 width: scrollX ? size(scrollX) : '100%' 4948 } ) 4949 .append( 4950 $(_div, { 'class': classes.footer.inner } ) 4951 .append( 4952 footerClone 4953 .removeAttr('id') 4954 .css( 'margin-left', 0 ) 4955 .append( captionSide === 'bottom' ? caption : null ) 4956 .append( 4957 table.children('tfoot') 4958 ) 4959 ) 4960 ) 4961 ); 4962 } 4963 4964 var children = scroller.children(); 4965 var scrollHead = children[0]; 4966 var scrollBody = children[1]; 4967 var scrollFoot = footer ? children[2] : null; 4968 4969 // When the body is scrolled, then we also want to scroll the headers 4970 $(scrollBody).on( 'scroll.DT', function () { 4971 var scrollLeft = this.scrollLeft; 4972 4973 scrollHead.scrollLeft = scrollLeft; 4974 4975 if ( footer ) { 4976 scrollFoot.scrollLeft = scrollLeft; 4977 } 4978 } ); 4979 4980 // When focus is put on the header cells, we might need to scroll the body 4981 $('th, td', scrollHead).on('focus', function () { 4982 var scrollLeft = scrollHead.scrollLeft; 4983 4984 scrollBody.scrollLeft = scrollLeft; 4985 4986 if ( footer ) { 4987 scrollBody.scrollLeft = scrollLeft; 4988 } 4989 }); 4990 4991 $(scrollBody).css('max-height', scrollY); 4992 if (! scroll.bCollapse) { 4993 $(scrollBody).css('height', scrollY); 4994 } 4995 4996 settings.nScrollHead = scrollHead; 4997 settings.nScrollBody = scrollBody; 4998 settings.nScrollFoot = scrollFoot; 4999 5000 // On redraw - align columns 5001 settings.aoDrawCallback.push(_fnScrollDraw); 5002 5003 return scroller[0]; 5004 } 5005 5006 5007 5008 /** 5009 * Update the header, footer and body tables for resizing - i.e. column 5010 * alignment. 5011 * 5012 * Welcome to the most horrible function DataTables. The process that this 5013 * function follows is basically: 5014 * 1. Re-create the table inside the scrolling div 5015 * 2. Correct colgroup > col values if needed 5016 * 3. Copy colgroup > col over to header and footer 5017 * 4. Clean up 5018 * 5019 * @param {object} settings dataTables settings object 5020 * @memberof DataTable#oApi 5021 */ 5022 function _fnScrollDraw ( settings ) 5023 { 5024 // Given that this is such a monster function, a lot of variables are use 5025 // to try and keep the minimised size as small as possible 5026 var 5027 scroll = settings.oScroll, 5028 barWidth = scroll.iBarWidth, 5029 divHeader = $(settings.nScrollHead), 5030 divHeaderInner = divHeader.children('div'), 5031 divHeaderTable = divHeaderInner.children('table'), 5032 divBodyEl = settings.nScrollBody,
5033 divBody = $(divBodyEl), 5034 divFooter = $(settings.nScrollFoot), 5035 divFooterInner = divFooter.children('div'), 5036 divFooterTable = divFooterInner.children('table'), 5037 header = $(settings.nTHead), 5038 table = $(settings.nTable), 5039 footer = settings.nTFoot && $('th, td', settings.nTFoot).length ? $(settings.nTFoot) : null, 5040 browser = settings.oBrowser, 5041 headerCopy, footerCopy; 5042 5043 // If the scrollbar visibility has changed from the last draw, we need to 5044 // adjust the column sizes as the table width will have changed to account 5045 // for the scrollbar 5046 var scrollBarVis = divBodyEl.scrollHeight > divBodyEl.clientHeight; 5047 5048 if ( settings.scrollBarVis !== scrollBarVis && settings.scrollBarVis !== undefined ) { 5049 settings.scrollBarVis = scrollBarVis; 5050 _fnAdjustColumnSizing( settings ); 5051 return; // adjust column sizing will call this function again 5052 } 5053 else { 5054 settings.scrollBarVis = scrollBarVis; 5055 } 5056 5057 // 1. Re-create the table inside the scrolling div 5058 // Remove the old minimised thead and tfoot elements in the inner table 5059 table.children('thead, tfoot').remove(); 5060 5061 // Clone the current header and footer elements and then place it into the inner table 5062 headerCopy = header.clone().prependTo( table ); 5063 headerCopy.find('th, td').removeAttr('tabindex'); 5064 headerCopy.find('[id]').removeAttr('id'); 5065 5066 if ( footer ) { 5067 footerCopy = footer.clone().prependTo( table ); 5068 footerCopy.find('[id]').removeAttr('id'); 5069 } 5070 5071 // 2. Correct colgroup > col values if needed 5072 // It is possible that the cell sizes are smaller than the content, so we need to 5073 // correct colgroup>col for such cases. This can happen if the auto width detection 5074 // uses a cell which has a longer string, but isn't the widest! For example 5075 // "Chief Executive Officer (CEO)" is the longest string in the demo, but 5076 // "Systems Administrator" is actually the widest string since it doesn't collapse. 5077 // Note the use of translating into a column index to get the `col` element. This 5078 // is because of Responsive which might remove `col` elements, knocking the alignment 5079 // of the indexes out. 5080 if (settings.aiDisplay.length) { 5081 // Get the column sizes from the first row in the table 5082 var colSizes = table.children('tbody').eq(0).children('tr').eq(0).children('th, td').map(function (vis) { 5083 return { 5084 idx: _fnVisibleToColumnIndex(settings, vis), 5085 width: $(this).outerWidth() 5086 } 5087 }); 5088 5089 // Check against what the colgroup > col is set to and correct if needed 5090 for (var i=0 ; i<colSizes.length ; i++) { 5091 var colEl = settings.aoColumns[ colSizes[i].idx ].colEl[0]; 5092 var colWidth = colEl.style.width.replace('px', ''); 5093 5094 if (colWidth !== colSizes[i].width) { 5095 colEl.style.width = colSizes[i].width + 'px'; 5096 } 5097 } 5098 } 5099 5100 // 3. Copy the colgroup over to the header and footer 5101 divHeaderTable 5102 .find('colgroup') 5103 .remove(); 5104 5105 divHeaderTable.append(settings.colgroup.clone()); 5106 5107 if ( footer ) { 5108 divFooterTable 5109 .find('colgroup') 5110 .remove(); 5111 5112 divFooterTable.append(settings.colgroup.clone()); 5113 } 5114 5115 // "Hide" the header and footer that we used for the sizing. We need to keep 5116 // the content of the cell so that the width applied to the header and body 5117 // both match, but we want to hide it completely. 5118 $('th, td', headerCopy).each(function () { 5119 $(this.childNodes).wrapAll('<div class="dt-scroll-sizing">'); 5120 }); 5121 5122 if ( footer ) { 5123 $('th, td', footerCopy).each(function () { 5124 $(this.childNodes).wrapAll('<div class="dt-scroll-sizing">'); 5125 }); 5126 } 5127 5128 // 4. Clean up 5129 // Figure out if there are scrollbar present - if so then we need a the header and footer to 5130 // provide a bit more space to allow "overflow" scrolling (i.e. past the scrollbar) 5131 var isScrolling = Math.floor(table.height()) > divBodyEl.clientHeight || divBody.css('overflow-y') == "scroll"; 5132 var paddingSide = 'padding' + (browser.bScrollbarLeft ? 'Left' : 'Right' ); 5133 5134 // Set the width's of the header and footer tables 5135 var outerWidth = table.outerWidth(); 5136 5137 divHeaderTable.css('width', _fnStringToCss( outerWidth )); 5138 divHeaderInner 5139 .css('width', _fnStringToCss( outerWidth )) 5140 .css(paddingSide, isScrolling ? barWidth+"px" : "0px"); 5141 5142 if ( footer ) { 5143 divFooterTable.css('width', _fnStringToCss( outerWidth )); 5144 divFooterInner 5145 .css('width', _fnStringToCss( outerWidth )) 5146 .css(paddingSide, isScrolling ? barWidth+"px" : "0px"); 5147 } 5148 5149 // Correct DOM ordering for colgroup - comes before the thead 5150 table.children('colgroup').prependTo(table); 5151 5152 // Adjust the position of the header in case we loose the y-scrollbar 5153 divBody.trigger('scroll'); 5154 5155 // If sorting or filtering has occurred, jump the scrolling back to the top 5156 // only if we aren't holding the position 5157 if ( (settings.bSorted || settings.bFiltered) && ! settings._drawHold ) { 5158 divBodyEl.scrollTop = 0; 5159 } 5160 } 5161 5162 /** 5163 * Calculate the width of columns for the table 5164 * @param {object} settings dataTables settings object 5165 * @memberof DataTable#oApi 5166 */ 5167 function _fnCalculateColumnWidths ( settings ) 5168 { 5169 // Not interested in doing column width calculation if auto-width is disabled 5170 if (! settings.oFeatures.bAutoWidth) { 5171 return; 5172 } 5173 5174 var 5175 table = settings.nTable, 5176 columns = settings.aoColumns, 5177 scroll = settings.oScroll, 5178 scrollY = scroll.sY, 5179 scrollX = scroll.sX, 5180 scrollXInner = scroll.sXInner, 5181 visibleColumns = _fnGetColumns( settings, 'bVisible' ), 5182 tableWidthAttr = table.getAttribute('width'), // from DOM element 5183 tableContainer = table.parentNode, 5184 i, column, columnIdx; 5185 5186 var styleWidth = table.style.width; 5187 if ( styleWidth && styleWidth.indexOf('%') !== -1 ) { 5188 tableWidthAttr = styleWidth; 5189 } 5190 5191 // Let plug-ins know that we are doing a recalc, in case they have changed any of the 5192 // visible columns their own way (e.g. Responsive uses display:none). 5193 _fnCallbackFire( 5194 settings, 5195 null, 5196 'column-calc', 5197 {visible: visibleColumns}, 5198 false 5199 ); 5200 5201 // Construct a single row, worst case, table with the widest 5202 // node in the data, assign any user defined widths, then insert it into
5203 // the DOM and allow the browser to do all the hard work of calculating 5204 // table widths 5205 var tmpTable = $(table.cloneNode()) 5206 .css( 'visibility', 'hidden' ) 5207 .removeAttr( 'id' ); 5208 5209 // Clean up the table body 5210 tmpTable.append('<tbody>') 5211 var tr = $('<tr/>').appendTo( tmpTable.find('tbody') ); 5212 5213 // Clone the table header and footer - we can't use the header / footer 5214 // from the cloned table, since if scrolling is active, the table's 5215 // real header and footer are contained in different table tags 5216 tmpTable 5217 .append( $(settings.nTHead).clone() ) 5218 .append( $(settings.nTFoot).clone() ); 5219 5220 // Remove any assigned widths from the footer (from scrolling) 5221 tmpTable.find('tfoot th, tfoot td').css('width', ''); 5222 5223 // Apply custom sizing to the cloned header 5224 tmpTable.find('thead th, thead td').each( function () { 5225 // Get the `width` from the header layout 5226 var width = _fnColumnsSumWidth( settings, this, true, false ); 5227 5228 if ( width ) { 5229 this.style.width = width; 5230 5231 // For scrollX we need to force the column width otherwise the 5232 // browser will collapse it. If this width is smaller than the 5233 // width the column requires, then it will have no effect 5234 if ( scrollX ) { 5235 $( this ).append( $('<div/>').css( { 5236 width: width, 5237 margin: 0, 5238 padding: 0, 5239 border: 0, 5240 height: 1 5241 } ) ); 5242 } 5243 } 5244 else { 5245 this.style.width = ''; 5246 } 5247 } ); 5248 5249 // Find the widest piece of data for each column and put it into the table 5250 for ( i=0 ; i<visibleColumns.length ; i++ ) { 5251 columnIdx = visibleColumns[i]; 5252 column = columns[ columnIdx ]; 5253 5254 var longest = _fnGetMaxLenString(settings, columnIdx); 5255 var autoClass = _ext.type.className[column.sType]; 5256 var text = longest + column.sContentPadding; 5257 var insert = longest.indexOf('<') === -1 5258 ? document.createTextNode(text) 5259 : text 5260 5261 $('<td/>') 5262 .addClass(autoClass) 5263 .addClass(column.sClass) 5264 .append(insert) 5265 .appendTo(tr); 5266 } 5267 5268 // Tidy the temporary table - remove name attributes so there aren't 5269 // duplicated in the dom (radio elements for example) 5270 $('[name]', tmpTable).removeAttr('name'); 5271 5272 // Table has been built, attach to the document so we can work with it. 5273 // A holding element is used, positioned at the top of the container 5274 // with minimal height, so it has no effect on if the container scrolls 5275 // or not. Otherwise it might trigger scrolling when it actually isn't 5276 // needed 5277 var holder = $('<div/>').css( scrollX || scrollY ? 5278 { 5279 position: 'absolute', 5280 top: 0, 5281 left: 0, 5282 height: 1, 5283 right: 0, 5284 overflow: 'hidden' 5285 } : 5286 {} 5287 ) 5288 .append( tmpTable ) 5289 .appendTo( tableContainer ); 5290 5291 // When scrolling (X or Y) we want to set the width of the table as 5292 // appropriate. However, when not scrolling leave the table width as it 5293 // is. This results in slightly different, but I think correct behaviour 5294 if ( scrollX && scrollXInner ) { 5295 tmpTable.width( scrollXInner ); 5296 } 5297 else if ( scrollX ) { 5298 tmpTable.css( 'width', 'auto' ); 5299 tmpTable.removeAttr('width'); 5300 5301 // If there is no width attribute or style, then allow the table to 5302 // collapse 5303 if ( tmpTable.width() < tableContainer.clientWidth && tableWidthAttr ) { 5304 tmpTable.width( tableContainer.clientWidth ); 5305 } 5306 } 5307 else if ( scrollY ) { 5308 tmpTable.width( tableContainer.clientWidth ); 5309 } 5310 else if ( tableWidthAttr ) { 5311 tmpTable.width( tableWidthAttr ); 5312 } 5313 5314 // Get the width of each column in the constructed table 5315 var total = 0; 5316 var bodyCells = tmpTable.find('tbody tr').eq(0).children(); 5317 5318 for ( i=0 ; i<visibleColumns.length ; i++ ) { 5319 // Use getBounding for sub-pixel accuracy, which we then want to round up! 5320 var bounding = bodyCells[i].getBoundingClientRect().width; 5321 5322 // Total is tracked to remove any sub-pixel errors as the outerWidth 5323 // of the table might not equal the total given here 5324 total += bounding; 5325 5326 // Width for each column to use 5327 columns[ visibleColumns[i] ].sWidth = _fnStringToCss( bounding ); 5328 } 5329 5330 table.style.width = _fnStringToCss( total ); 5331
5332 // Finished with the table - ditch it 5333 holder.remove(); 5334 5335 // If there is a width attr, we want to attach an event listener which 5336 // allows the table sizing to automatically adjust when the window is 5337 // resized. Use the width attr rather than CSS, since we can't know if the 5338 // CSS is a relative value or absolute - DOM read is always px. 5339 if ( tableWidthAttr ) { 5340 table.style.width = _fnStringToCss( tableWidthAttr ); 5341 } 5342 5343 if ( (tableWidthAttr || scrollX) && ! settings._reszEvt ) { 5344 var bindResize = function () { 5345 $(window).on('resize.DT-'+settings.sInstance, DataTable.util.throttle( function () { 5346 if (! settings.bDestroying) { 5347 _fnAdjustColumnSizing( settings ); 5348 } 5349 } ) ); 5350 }; 5351 5352 bindResize(); 5353 5354 settings._reszEvt = true; 5355 } 5356 } 5357 5358 5359 /** 5360 * Get the maximum strlen for each data column 5361 * @param {object} settings dataTables settings object 5362 * @param {int} colIdx column of interest 5363 * @returns {string} string of the max length 5364 * @memberof DataTable#oApi 5365 */ 5366 function _fnGetMaxLenString( settings, colIdx ) 5367 { 5368 var column = settings.aoColumns[colIdx]; 5369 5370 if (! column.maxLenString) { 5371 var s, max='', maxLen = -1; 5372 5373 for ( var i=0, ien=settings.aiDisplayMaster.length ; i<ien ; i++ ) { 5374 var rowIdx = settings.aiDisplayMaster[i]; 5375 var data = _fnGetRowDisplay(settings, rowIdx)[colIdx]; 5376 5377 var cellString = data && typeof data === 'object' && data.nodeType 5378 ? data.innerHTML 5379 : data+''; 5380 5381 // Remove id / name attributes from elements so they 5382 // don't interfere with existing elements 5383 cellString = cellString 5384 .replace(/id=".*?"/g, '') 5385 .replace(/name=".*?"/g, ''); 5386 5387 s = _stripHtml(cellString) 5388 .replace( / /g, ' ' ); 5389 5390 if ( s.length > maxLen ) { 5391 // We want the HTML in the string, but the length that 5392 // is important is the stripped string 5393 max = cellString; 5394 maxLen = s.length; 5395 } 5396 } 5397 5398 column.maxLenString = max; 5399 } 5400 5401 return column.maxLenString; 5402 } 5403 5404 5405 /** 5406 * Append a CSS unit (only if required) to a string 5407 * @param {string} value to css-ify 5408 * @returns {string} value with css unit 5409 * @memberof DataTable#oApi 5410 */ 5411 function _fnStringToCss( s ) 5412 { 5413 if ( s === null ) { 5414 return '0px'; 5415 } 5416 5417 if ( typeof s == 'number' ) { 5418 return s < 0 ? 5419 '0px' : 5420 s+'px'; 5421 } 5422 5423 // Check it has a unit character already 5424 return s.match(/\d$/) ? 5425 s+'px' : 5426 s; 5427 } 5428 5429 /** 5430 * Re-insert the `col` elements for current visibility 5431 * 5432 * @param {*} settings DT settings 5433 */ 5434 function _colGroup( settings ) { 5435 var cols = settings.aoColumns; 5436 5437 settings.colgroup.empty(); 5438 5439 for (i=0 ; i<cols.length ; i++) { 5440 if (cols[i].bVisible) { 5441 settings.colgroup.append(cols[i].colEl); 5442 } 5443 } 5444 } 5445 5446 5447 function _fnSortInit( settings ) { 5448 var target = settings.nTHead; 5449 var headerRows = target.querySelectorAll('tr'); 5450 var legacyTop = settings.bSortCellsTop; 5451 var notSelector = ':not([data-dt-order="disable"]):not([data-dt-order="icon-only"])'; 5452 5453 // Legacy support for `orderCellsTop` 5454 if (legacyTop === true) { 5455 target = headerRows[0]; 5456 } 5457 else if (legacyTop === false) { 5458 target = headerRows[ headerRows.length - 1 ]; 5459 } 5460 5461 _fnSortAttachListener( 5462 settings, 5463 target, 5464 target === settings.nTHead 5465 ? 'tr'+notSelector+' th'+notSelector+', tr'+notSelector+' td'+notSelector 5466 : 'th'+notSelector+', td'+notSelector 5467 ); 5468 5469 // Need to resolve the user input array into our internal structure 5470 var order = []; 5471 _fnSortResolve( settings, order, settings.aaSorting ); 5472 5473 settings.aaSorting = order; 5474 } 5475 5476 5477 function _fnSortAttachListener(settings, node, selector, column, callback) { 5478 _fnBindAction( node, selector, function (e) { 5479 var run = false; 5480 var columns = column === undefined 5481 ? _fnColumnsFromHeader( e.target ) 5482 : [column]; 5483 5484 if ( columns.length ) { 5485 for ( var i=0, ien=columns.length ; i<ien ; i++ ) { 5486 var ret = _fnSortAdd( settings, columns[i], i, e.shiftKey ); 5487 5488 if (ret !== false) { 5489 run = true; 5490 } 5491 5492 // If the first entry is no sort, then subsequent 5493 // sort columns are ignored 5494 if (settings.aaSorting.length === 1 && settings.aaSorting[0][1] === '') { 5495 break; 5496 } 5497 } 5498 5499 if (run) { 5500 _fnProcessingDisplay( settings, true ); 5501 5502 // Allow the processing display to show 5503 setTimeout( function () { 5504 _fnSort( settings ); 5505 _fnSortDisplay( settings, settings.aiDisplay ); 5506 5507 // Sort processing done - redraw has its own processing display 5508 _fnProcessingDisplay( settings, false ); 5509 5510 _fnReDraw( settings, false, false ); 5511 5512 if (callback) { 5513 callback(); 5514 } 5515 }, 0); 5516 } 5517 } 5518 } ); 5519 } 5520 5521 /** 5522 * Sort the display array to match the master's order 5523 * @param {*} settings 5524 */ 5525 function _fnSortDisplay(settings, display) { 5526 if (display.length < 2) { 5527 return; 5528 } 5529 5530 var master = settings.aiDisplayMaster; 5531 var masterMap = {}; 5532 var map = {}; 5533 var i; 5534 5535 // Rather than needing an `indexOf` on master array, we can create a map 5536 for (i=0 ; i<master.length ; i++) { 5537 masterMap[master[i]] = i; 5538 } 5539 5540 // And then cache what would be the indexOf fom the display 5541 for (i=0 ; i<display.length ; i++) { 5542 map[display[i]] = masterMap[display[i]]; 5543 } 5544 5545 display.sort(function(a, b){ 5546 // Short version of this function is simply `master.indexOf(a) - master.indexOf(b);` 5547 return map[a] - map[b]; 5548 }); 5549 } 5550 5551
5552 function _fnSortResolve (settings, nestedSort, sort) { 5553 var push = function ( a ) { 5554 if ($.isPlainObject(a)) { 5555 if (a.idx !== undefined) { 5556 // Index based ordering 5557 nestedSort.push([a.idx, a.dir]); 5558 } 5559 else if (a.name) { 5560 // Name based ordering 5561 var cols = _pluck( settings.aoColumns, 'sName'); 5562 var idx = cols.indexOf(a.name); 5563 5564 if (idx !== -1) { 5565 nestedSort.push([idx, a.dir]); 5566 } 5567 } 5568 } 5569 else { 5570 // Plain column index and direction pair 5571 nestedSort.push(a); 5572 } 5573 }; 5574 5575 if ( $.isPlainObject(sort) ) { 5576 // Object 5577 push(sort); 5578 } 5579 else if ( sort.length && typeof sort[0] === 'number' ) { 5580 // 1D array 5581 push(sort); 5582 } 5583 else if ( sort.length ) { 5584 // 2D array 5585 for (var z=0; z<sort.length; z++) { 5586 push(sort[z]); // Object or array 5587 } 5588 } 5589 } 5590 5591 5592 function _fnSortFlatten ( settings ) 5593 { 5594 var 5595 i, k, kLen, 5596 aSort = [], 5597 extSort = DataTable.ext.type.order, 5598 aoColumns = settings.aoColumns, 5599 aDataSort, iCol, sType, srcCol, 5600 fixed = settings.aaSortingFixed, 5601 fixedObj = $.isPlainObject( fixed ), 5602 nestedSort = []; 5603 5604 if ( ! settings.oFeatures.bSort ) { 5605 return aSort; 5606 } 5607 5608 // Build the sort array, with pre-fix and post-fix options if they have been 5609 // specified 5610 if ( Array.isArray( fixed ) ) { 5611 _fnSortResolve( settings, nestedSort, fixed ); 5612 } 5613 5614 if ( fixedObj && fixed.pre ) { 5615 _fnSortResolve( settings, nestedSort, fixed.pre ); 5616 } 5617 5618 _fnSortResolve( settings, nestedSort, settings.aaSorting ); 5619 5620 if (fixedObj && fixed.post ) { 5621 _fnSortResolve( settings, nestedSort, fixed.post ); 5622 } 5623 5624 for ( i=0 ; i<nestedSort.length ; i++ ) 5625 { 5626 srcCol = nestedSort[i][0]; 5627 5628 if ( aoColumns[ srcCol ] ) { 5629 aDataSort = aoColumns[ srcCol ].aDataSort; 5630 5631 for ( k=0, kLen=aDataSort.length ; k<kLen ; k++ ) 5632 { 5633 iCol = aDataSort[k]; 5634 sType = aoColumns[ iCol ].sType || 'string'; 5635 5636 if ( nestedSort[i]._idx === undefined ) { 5637 nestedSort[i]._idx = aoColumns[iCol].asSorting.indexOf(nestedSort[i][1]); 5638 } 5639 5640 if ( nestedSort[i][1] ) { 5641 aSort.push( { 5642 src: srcCol, 5643 col: iCol, 5644 dir: nestedSort[i][1], 5645 index: nestedSort[i]._idx, 5646 type: sType, 5647 formatter: extSort[ sType+"-pre" ], 5648 sorter: extSort[ sType+"-"+nestedSort[i][1] ] 5649 } ); 5650 } 5651 } 5652 } 5653 } 5654 5655 return aSort; 5656 } 5657 5658 /** 5659 * Change the order of the table 5660 * @param {object} oSettings dataTables settings object 5661 * @memberof DataTable#oApi 5662 */ 5663 function _fnSort ( oSettings, col, dir ) 5664 { 5665 var 5666 i, ien, iLen, 5667 aiOrig = [], 5668 extSort = DataTable.ext.type.order, 5669 aoData = oSettings.aoData, 5670 sortCol, 5671 displayMaster = oSettings.aiDisplayMaster, 5672 aSort; 5673 5674 // Resolve any column types that are unknown due to addition or invalidation 5675 // @todo Can this be moved into a 'data-ready' handler which is called when 5676 // data is going to be used in the table? 5677 _fnColumnTypes( oSettings ); 5678 5679 // Allow a specific column to be sorted, which will _not_ alter the display 5680 // master 5681 if (col !== undefined) { 5682 var srcCol = oSettings.aoColumns[col]; 5683 aSort = [{ 5684 src: col, 5685 col: col, 5686 dir: dir, 5687 index: 0, 5688 type: srcCol.sType, 5689 formatter: extSort[ srcCol.sType+"-pre" ], 5690 sorter: extSort[ srcCol.sType+"-"+dir ] 5691 }]; 5692 displayMaster = displayMaster.slice(); 5693 } 5694 else { 5695 aSort = _fnSortFlatten( oSettings ); 5696 } 5697 5698 for ( i=0, ien=aSort.length ; i<ien ; i++ ) { 5699 sortCol = aSort[i]; 5700 5701 // Load the data needed for the sort, for each cell 5702 _fnSortData( oSettings, sortCol.col ); 5703 } 5704 5705 /* No sorting required if server-side or no sorting array */ 5706 if ( _fnDataSource( oSettings ) != 'ssp' && aSort.length !== 0 ) 5707 { 5708 // Reset the initial positions on each pass so we get a stable sort 5709 for ( i=0, iLen=displayMaster.length ; i<iLen ; i++ ) { 5710 aiOrig[ i ] = i; 5711 } 5712 5713 // If the first sort is desc, then reverse the array to preserve original 5714 // order, just in reverse 5715 if (aSort.length && aSort[0].dir === 'desc') { 5716 aiOrig.reverse(); 5717 } 5718
5719 /* Do the sort - here we want multi-column sorting based on a given data source (column) 5720 * and sorting function (from oSort) in a certain direction. It's reasonably complex to 5721 * follow on it's own, but this is what we want (example two column sorting): 5722 * fnLocalSorting = function(a,b){ 5723 * var test; 5724 * test = oSort['string-asc']('data11', 'data12'); 5725 * if (test !== 0) 5726 * return test; 5727 * test = oSort['numeric-desc']('data21', 'data22'); 5728 * if (test !== 0) 5729 * return test; 5730 * return oSort['numeric-asc']( aiOrig[a], aiOrig[b] ); 5731 * } 5732 * Basically we have a test for each sorting column, if the data in that column is equal, 5733 * test the next column. If all columns match, then we use a numeric sort on the row 5734 * positions in the original data array to provide a stable sort. 5735 */ 5736 displayMaster.sort( function ( a, b ) { 5737 var 5738 x, y, k, test, sort, 5739 len=aSort.length, 5740 dataA = aoData[a]._aSortData, 5741 dataB = aoData[b]._aSortData; 5742 5743 for ( k=0 ; k<len ; k++ ) { 5744 sort = aSort[k]; 5745 5746 // Data, which may have already been through a `-pre` function 5747 x = dataA[ sort.col ]; 5748 y = dataB[ sort.col ]; 5749 5750 if (sort.sorter) { 5751 // If there is a custom sorter (`-asc` or `-desc`) for this 5752 // data type, use it 5753 test = sort.sorter(x, y); 5754 5755 if ( test !== 0 ) { 5756 return test; 5757 } 5758 } 5759 else { 5760 // Otherwise, use generic sorting 5761 test = x<y ? -1 : x>y ? 1 : 0; 5762 5763 if ( test !== 0 ) { 5764 return sort.dir === 'asc' ? test : -test; 5765 } 5766 } 5767 } 5768 5769 x = aiOrig[a]; 5770 y = aiOrig[b]; 5771 5772 return x<y ? -1 : x>y ? 1 : 0; 5773 } ); 5774 } 5775 else if ( aSort.length === 0 ) { 5776 // Apply index order 5777 displayMaster.sort(function (x, y) { 5778 return x<y ? -1 : x>y ? 1 : 0; 5779 }); 5780 } 5781 5782 if (col === undefined) { 5783 // Tell the draw function that we have sorted the data 5784 oSettings.bSorted = true; 5785 5786 _fnCallbackFire( oSettings, null, 'order', [oSettings, aSort] ); 5787 } 5788
5789 return displayMaster; 5790 } 5791 5792 5793 /** 5794 * Function to run on user sort request 5795 * @param {object} settings dataTables settings object 5796 * @param {node} attachTo node to attach the handler to 5797 * @param {int} colIdx column sorting index 5798 * @param {int} addIndex Counter 5799 * @param {boolean} [shift=false] Shift click add 5800 * @param {function} [callback] callback function 5801 * @memberof DataTable#oApi 5802 */ 5803 function _fnSortAdd ( settings, colIdx, addIndex, shift ) 5804 { 5805 var col = settings.aoColumns[ colIdx ]; 5806 var sorting = settings.aaSorting; 5807 var asSorting = col.asSorting; 5808 var nextSortIdx; 5809 var next = function ( a, overflow ) { 5810 var idx = a._idx; 5811 if ( idx === undefined ) { 5812 idx = asSorting.indexOf(a[1]); 5813 } 5814 5815 return idx+1 < asSorting.length ? 5816 idx+1 : 5817 overflow ? 5818 null : 5819 0; 5820 }; 5821 5822 if ( ! col.bSortable ) { 5823 return false; 5824 } 5825 5826 // Convert to 2D array if needed 5827 if ( typeof sorting[0] === 'number' ) { 5828 sorting = settings.aaSorting = [ sorting ]; 5829 } 5830 5831 // If appending the sort then we are multi-column sorting 5832 if ( (shift || addIndex) && settings.oFeatures.bSortMulti ) { 5833 // Are we already doing some kind of sort on this column? 5834 var sortIdx = _pluck(sorting, '0').indexOf(colIdx); 5835 5836 if ( sortIdx !== -1 ) { 5837 // Yes, modify the sort 5838 nextSortIdx = next( sorting[sortIdx], true ); 5839 5840 if ( nextSortIdx === null && sorting.length === 1 ) { 5841 nextSortIdx = 0; // can't remove sorting completely 5842 } 5843 5844 if ( nextSortIdx === null ) { 5845 sorting.splice( sortIdx, 1 ); 5846 } 5847 else { 5848 sorting[sortIdx][1] = asSorting[ nextSortIdx ]; 5849 sorting[sortIdx]._idx = nextSortIdx; 5850 } 5851 } 5852 else if (shift) { 5853 // No sort on this column yet, being added by shift click 5854 // add it as itself 5855 sorting.push( [ colIdx, asSorting[0], 0 ] ); 5856 sorting[sorting.length-1]._idx = 0; 5857 } 5858 else { 5859 // No sort on this column yet, being added from a colspan 5860 // so add with same direction as first column 5861 sorting.push( [ colIdx, sorting[0][1], 0 ] ); 5862 sorting[sorting.length-1]._idx = 0; 5863 } 5864 } 5865 else if ( sorting.length && sorting[0][0] == colIdx ) { 5866 // Single column - already sorting on this column, modify the sort 5867 nextSortIdx = next( sorting[0] ); 5868 5869 sorting.length = 1; 5870 sorting[0][1] = asSorting[ nextSortIdx ]; 5871 sorting[0]._idx = nextSortIdx; 5872 } 5873 else { 5874 // Single column - sort only on this column 5875 sorting.length = 0; 5876 sorting.push( [ colIdx, asSorting[0] ] ); 5877 sorting[0]._idx = 0; 5878 } 5879 } 5880 5881 5882 /** 5883 * Set the sorting classes on table's body, Note: it is safe to call this function 5884 * when bSort and bSortClasses are false 5885 * @param {object} oSettings dataTables settings object 5886 * @memberof DataTable#oApi 5887 */ 5888 function _fnSortingClasses( settings ) 5889 { 5890 var oldSort = settings.aLastSort; 5891 var sortClass = settings.oClasses.order.position; 5892 var sort = _fnSortFlatten( settings ); 5893 var features = settings.oFeatures; 5894 var i, ien, colIdx; 5895 5896 if ( features.bSort && features.bSortClasses ) { 5897 // Remove old sorting classes 5898 for ( i=0, ien=oldSort.length ; i<ien ; i++ ) { 5899 colIdx = oldSort[i].src; 5900 5901 // Remove column sorting 5902 $( _pluck( settings.aoData, 'anCells', colIdx ) ) 5903 .removeClass( sortClass + (i<2 ? i+1 : 3) ); 5904 } 5905 5906 // Add new column sorting 5907 for ( i=0, ien=sort.length ; i<ien ; i++ ) { 5908 colIdx = sort[i].src; 5909 5910 $( _pluck( settings.aoData, 'anCells', colIdx ) ) 5911 .addClass( sortClass + (i<2 ? i+1 : 3) ); 5912 } 5913 } 5914 5915 settings.aLastSort = sort; 5916 } 5917 5918 5919 // Get the data to sort a column, be it from cache, fresh (populating the 5920 // cache), or from a sort formatter 5921 function _fnSortData( settings, colIdx ) 5922 { 5923 // Custom sorting function - provided by the sort data type 5924 var column = settings.aoColumns[ colIdx ]; 5925 var customSort = DataTable.ext.order[ column.sSortDataType ]; 5926 var customData; 5927 5928 if ( customSort ) { 5929 customData = customSort.call( settings.oInstance, settings, colIdx, 5930 _fnColumnIndexToVisible( settings, colIdx ) 5931 ); 5932 } 5933 5934 // Use / populate cache 5935 var row, cellData; 5936 var formatter = DataTable.ext.type.order[ column.sType+"-pre" ]; 5937 var data = settings.aoData; 5938 5939 for ( var rowIdx=0 ; rowIdx<data.length ; rowIdx++ ) { 5940 // Sparse array 5941 if (! data[rowIdx]) { 5942 continue; 5943 } 5944 5945 row = data[rowIdx]; 5946 5947 if ( ! row._aSortData ) { 5948 row._aSortData = []; 5949 } 5950 5951 if ( ! row._aSortData[colIdx] || customSort ) { 5952 cellData = customSort ? 5953 customData[rowIdx] : // If there was a custom sort function, use data from there 5954 _fnGetCellData( settings, rowIdx, colIdx, 'sort' ); 5955 5956 row._aSortData[ colIdx ] = formatter ? 5957 formatter( cellData, settings ) : 5958 cellData; 5959 } 5960 } 5961 } 5962 5963 5964 /** 5965 * State information for a table 5966 * 5967 * @param {*} settings 5968 * @returns State object 5969 */ 5970 function _fnSaveState ( settings ) 5971 { 5972 if (settings._bLoadingState) { 5973 return; 5974 } 5975 5976 /* Store the interesting variables */ 5977 var state = { 5978 time: +new Date(), 5979 start: settings._iDisplayStart, 5980 length: settings._iDisplayLength, 5981 order: $.extend( true, [], settings.aaSorting ), 5982 search: $.extend({}, settings.oPreviousSearch), 5983 columns: settings.aoColumns.map( function ( col, i ) { 5984 return { 5985 visible: col.bVisible, 5986 search: $.extend({}, settings.aoPreSearchCols[i]) 5987 }; 5988 } ) 5989 }; 5990 5991 settings.oSavedState = state; 5992 _fnCallbackFire( settings, "aoStateSaveParams", 'stateSaveParams', [settings, state] ); 5993
vendor: 17,306 bytes, lines 5994-6578
5994 if ( settings.oFeatures.bStateSave && !settings.bDestroying ) 5995 { 5996 settings.fnStateSaveCallback.call( settings.oInstance, settings, state ); 5997 } 5998 } 5999 6000 6001 /** 6002 * Attempt to load a saved table state 6003 * @param {object} oSettings dataTables settings object 6004 * @param {object} oInit DataTables init object so we can override settings 6005 * @param {function} callback Callback to execute when the state has been loaded 6006 * @memberof DataTable#oApi 6007 */ 6008 function _fnLoadState ( settings, init, callback ) 6009 { 6010 if ( ! settings.oFeatures.bStateSave ) { 6011 callback(); 6012 return; 6013 } 6014 6015 var loaded = function(state) { 6016 _fnImplementState(settings, state, callback); 6017 } 6018 6019 var state = settings.fnStateLoadCallback.call( settings.oInstance, settings, loaded ); 6020 6021 if ( state !== undefined ) { 6022 _fnImplementState( settings, state, callback ); 6023 } 6024 // otherwise, wait for the loaded callback to be executed 6025 6026 return true; 6027 } 6028 6029 function _fnImplementState ( settings, s, callback) { 6030 var i, ien; 6031 var columns = settings.aoColumns; 6032 settings._bLoadingState = true; 6033 6034 // When StateRestore was introduced the state could now be implemented at any time 6035 // Not just initialisation. To do this an api instance is required in some places 6036 var api = settings._bInitComplete ? new DataTable.Api(settings) : null; 6037 6038 if ( ! s || ! s.time ) { 6039 settings._bLoadingState = false; 6040 callback(); 6041 return; 6042 } 6043 6044 // Reject old data 6045 var duration = settings.iStateDuration; 6046 if ( duration > 0 && s.time < +new Date() - (duration*1000) ) { 6047 settings._bLoadingState = false; 6048 callback(); 6049 return; 6050 } 6051 6052 // Allow custom and plug-in manipulation functions to alter the saved data set and 6053 // cancelling of loading by returning false 6054 var abStateLoad = _fnCallbackFire( settings, 'aoStateLoadParams', 'stateLoadParams', [settings, s] ); 6055 if ( abStateLoad.indexOf(false) !== -1 ) { 6056 settings._bLoadingState = false; 6057 callback(); 6058 return; 6059 } 6060 6061 // Number of columns have changed - all bets are off, no restore of settings 6062 if ( s.columns && columns.length !== s.columns.length ) { 6063 settings._bLoadingState = false; 6064 callback(); 6065 return; 6066 } 6067 6068 // Store the saved state so it might be accessed at any time 6069 settings.oLoadedState = $.extend( true, {}, s ); 6070 6071 // This is needed for ColReorder, which has to happen first to allow all 6072 // the stored indexes to be usable. It is not publicly documented. 6073 _fnCallbackFire( settings, null, 'stateLoadInit', [settings, s], true ); 6074 6075 // Page Length 6076 if ( s.length !== undefined ) { 6077 // If already initialised just set the value directly so that the select element is also updated 6078 if (api) { 6079 api.page.len(s.length) 6080 } 6081 else { 6082 settings._iDisplayLength = s.length; 6083 } 6084 } 6085 6086 // Restore key features - todo - for 1.11 this needs to be done by 6087 // subscribed events 6088 if ( s.start !== undefined ) { 6089 if(api === null) { 6090 settings._iDisplayStart = s.start; 6091 settings.iInitDisplayStart = s.start; 6092 } 6093 else { 6094 _fnPageChange(settings, s.start/settings._iDisplayLength); 6095 } 6096 } 6097 6098 // Order 6099 if ( s.order !== undefined ) { 6100 settings.aaSorting = []; 6101 $.each( s.order, function ( i, col ) { 6102 settings.aaSorting.push( col[0] >= columns.length ? 6103 [ 0, col[1] ] : 6104 col 6105 ); 6106 } ); 6107 } 6108 6109 // Search 6110 if ( s.search !== undefined ) { 6111 $.extend( settings.oPreviousSearch, s.search ); 6112 } 6113 6114 // Columns 6115 if ( s.columns ) { 6116 for ( i=0, ien=s.columns.length ; i<ien ; i++ ) { 6117 var col = s.columns[i]; 6118 6119 // Visibility 6120 if ( col.visible !== undefined ) { 6121 // If the api is defined, the table has been initialised so we need to use it rather than internal settings 6122 if (api) { 6123 // Don't redraw the columns on every iteration of this loop, we will do this at the end instead 6124 api.column(i).visible(col.visible, false); 6125 } 6126 else { 6127 columns[i].bVisible = col.visible; 6128 } 6129 } 6130 6131 // Search 6132 if ( col.search !== undefined ) { 6133 $.extend( settings.aoPreSearchCols[i], col.search ); 6134 } 6135 } 6136 6137 // If the api is defined then we need to adjust the columns once the visibility has been changed 6138 if (api) { 6139 api.columns.adjust(); 6140 } 6141 } 6142 6143 settings._bLoadingState = false; 6144 _fnCallbackFire( settings, 'aoStateLoaded', 'stateLoaded', [settings, s] ); 6145 callback(); 6146 } 6147 6148 /** 6149 * Log an error message 6150 * @param {object} settings dataTables settings object 6151 * @param {int} level log error messages, or display them to the user 6152 * @param {string} msg error message 6153 * @param {int} tn Technical note id to get more information about the error. 6154 * @memberof DataTable#oApi 6155 */ 6156 function _fnLog( settings, level, msg, tn ) 6157 { 6158 msg = 'DataTables warning: '+ 6159 (settings ? 'table id='+settings.sTableId+' - ' : '')+msg; 6160 6161 if ( tn ) { 6162 msg += '. For more information about this error, please see '+ 6163 'https://datatables.net/tn/'+tn; 6164 } 6165 6166 if ( ! level ) { 6167 // Backwards compatibility pre 1.10 6168 var ext = DataTable.ext; 6169 var type = ext.sErrMode || ext.errMode; 6170 6171 if ( settings ) { 6172 _fnCallbackFire( settings, null, 'dt-error', [ settings, tn, msg ], true ); 6173 } 6174 6175 if ( type == 'alert' ) { 6176 alert( msg ); 6177 } 6178 else if ( type == 'throw' ) { 6179 throw new Error(msg); 6180 } 6181 else if ( typeof type == 'function' ) { 6182 type( settings, tn, msg ); 6183 } 6184 } 6185 else if ( window.console && console.log ) { 6186 console.log( msg ); 6187 } 6188 } 6189 6190 6191 /** 6192 * See if a property is defined on one object, if so assign it to the other object 6193 * @param {object} ret target object 6194 * @param {object} src source object 6195 * @param {string} name property 6196 * @param {string} [mappedName] name to map too - optional, name used if not given 6197 * @memberof DataTable#oApi 6198 */ 6199 function _fnMap( ret, src, name, mappedName ) 6200 { 6201 if ( Array.isArray( name ) ) { 6202 $.each( name, function (i, val) { 6203 if ( Array.isArray( val ) ) { 6204 _fnMap( ret, src, val[0], val[1] ); 6205 } 6206 else { 6207 _fnMap( ret, src, val ); 6208 } 6209 } ); 6210 6211 return; 6212 } 6213 6214 if ( mappedName === undefined ) { 6215 mappedName = name; 6216 } 6217 6218 if ( src[name] !== undefined ) { 6219 ret[mappedName] = src[name]; 6220 } 6221 } 6222 6223 6224 /** 6225 * Extend objects - very similar to jQuery.extend, but deep copy objects, and 6226 * shallow copy arrays. The reason we need to do this, is that we don't want to 6227 * deep copy array init values (such as aaSorting) since the dev wouldn't be 6228 * able to override them, but we do want to deep copy arrays. 6229 * @param {object} out Object to extend 6230 * @param {object} extender Object from which the properties will be applied to 6231 * out 6232 * @param {boolean} breakRefs If true, then arrays will be sliced to take an 6233 * independent copy with the exception of the `data` or `aaData` parameters 6234 * if they are present. This is so you can pass in a collection to 6235 * DataTables and have that used as your data source without breaking the 6236 * references 6237 * @returns {object} out Reference, just for convenience - out === the return. 6238 * @memberof DataTable#oApi 6239 * @todo This doesn't take account of arrays inside the deep copied objects. 6240 */ 6241 function _fnExtend( out, extender, breakRefs ) 6242 { 6243 var val; 6244 6245 for ( var prop in extender ) { 6246 if ( Object.prototype.hasOwnProperty.call(extender, prop) ) { 6247 val = extender[prop]; 6248 6249 if ( $.isPlainObject( val ) ) { 6250 if ( ! $.isPlainObject( out[prop] ) ) { 6251 out[prop] = {}; 6252 } 6253 $.extend( true, out[prop], val ); 6254 } 6255 else if ( breakRefs && prop !== 'data' && prop !== 'aaData' && Array.isArray(val) ) { 6256 out[prop] = val.slice(); 6257 } 6258 else { 6259 out[prop] = val; 6260 } 6261 } 6262 } 6263 6264 return out; 6265 } 6266 6267 6268 /** 6269 * Bind an event handers to allow a click or return key to activate the callback. 6270 * This is good for accessibility since a return on the keyboard will have the 6271 * same effect as a click, if the element has focus. 6272 * @param {element} n Element to bind the action to 6273 * @param {object|string} selector Selector (for delegated events) or data object 6274 * to pass to the triggered function 6275 * @param {function} fn Callback function for when the event is triggered 6276 * @memberof DataTable#oApi 6277 */ 6278 function _fnBindAction( n, selector, fn ) 6279 { 6280 $(n) 6281 .on( 'click.DT', selector, function (e) { 6282 fn(e); 6283 } ) 6284 .on( 'keypress.DT', selector, function (e){ 6285 if ( e.which === 13 ) { 6286 e.preventDefault(); 6287 fn(e); 6288 } 6289 } ) 6290 .on( 'selectstart.DT', selector, function () { 6291 // Don't want a double click resulting in text selection 6292 return false; 6293 } ); 6294 } 6295 6296 6297 /** 6298 * Register a callback function. Easily allows a callback function to be added to 6299 * an array store of callback functions that can then all be called together. 6300 * @param {object} settings dataTables settings object 6301 * @param {string} store Name of the array storage for the callbacks in oSettings 6302 * @param {function} fn Function to be called back 6303 * @memberof DataTable#oApi 6304 */ 6305 function _fnCallbackReg( settings, store, fn ) 6306 { 6307 if ( fn ) { 6308 settings[store].push(fn); 6309 } 6310 } 6311 6312 6313 /** 6314 * Fire callback functions and trigger events. Note that the loop over the 6315 * callback array store is done backwards! Further note that you do not want to 6316 * fire off triggers in time sensitive applications (for example cell creation) 6317 * as its slow. 6318 * @param {object} settings dataTables settings object 6319 * @param {string} callbackArr Name of the array storage for the callbacks in 6320 * oSettings 6321 * @param {string} eventName Name of the jQuery custom event to trigger. If 6322 * null no trigger is fired 6323 * @param {array} args Array of arguments to pass to the callback function / 6324 * trigger 6325 * @param {boolean} [bubbles] True if the event should bubble 6326 * @memberof DataTable#oApi 6327 */ 6328 function _fnCallbackFire( settings, callbackArr, eventName, args, bubbles ) 6329 { 6330 var ret = []; 6331 6332 if ( callbackArr ) { 6333 ret = settings[callbackArr].slice().reverse().map( function (val) { 6334 return val.apply( settings.oInstance, args ); 6335 } ); 6336 } 6337 6338 if ( eventName !== null) { 6339 var e = $.Event( eventName+'.dt' ); 6340 var table = $(settings.nTable); 6341 6342 // Expose the DataTables API on the event object for easy access 6343 e.dt = settings.api; 6344 6345 table[bubbles ? 'trigger' : 'triggerHandler']( e, args ); 6346 6347 // If not yet attached to the document, trigger the event 6348 // on the body directly to sort of simulate the bubble 6349 if (bubbles && table.parents('body').length === 0) { 6350 $('body').trigger( e, args ); 6351 } 6352 6353 ret.push( e.result ); 6354 } 6355 6356 return ret; 6357 } 6358 6359 6360 function _fnLengthOverflow ( settings ) 6361 { 6362 var 6363 start = settings._iDisplayStart, 6364 end = settings.fnDisplayEnd(), 6365 len = settings._iDisplayLength; 6366 6367 /* If we have space to show extra rows (backing up from the end point - then do so */ 6368 if ( start >= end ) 6369 { 6370 start = end - len; 6371 } 6372 6373 // Keep the start record on the current page 6374 start -= (start % len); 6375 6376 if ( len === -1 || start < 0 ) 6377 { 6378 start = 0; 6379 } 6380 6381 settings._iDisplayStart = start; 6382 } 6383 6384 6385 function _fnRenderer( settings, type ) 6386 { 6387 var renderer = settings.renderer; 6388 var host = DataTable.ext.renderer[type]; 6389 6390 if ( $.isPlainObject( renderer ) && renderer[type] ) { 6391 // Specific renderer for this type. If available use it, otherwise use 6392 // the default. 6393 return host[renderer[type]] || host._; 6394 } 6395 else if ( typeof renderer === 'string' ) { 6396 // Common renderer - if there is one available for this type use it, 6397 // otherwise use the default 6398 return host[renderer] || host._; 6399 } 6400 6401 // Use the default 6402 return host._; 6403 } 6404 6405 6406 /** 6407 * Detect the data source being used for the table. Used to simplify the code 6408 * a little (ajax) and to make it compress a little smaller. 6409 * 6410 * @param {object} settings dataTables settings object 6411 * @returns {string} Data source 6412 * @memberof DataTable#oApi 6413 */ 6414 function _fnDataSource ( settings ) 6415 { 6416 if ( settings.oFeatures.bServerSide ) { 6417 return 'ssp'; 6418 } 6419 else if ( settings.ajax ) { 6420 return 'ajax'; 6421 } 6422 return 'dom'; 6423 } 6424 6425 /** 6426 * Common replacement for language strings 6427 * 6428 * @param {*} settings DT settings object 6429 * @param {*} str String with values to replace 6430 * @param {*} entries Plural number for _ENTRIES_ - can be undefined 6431 * @returns String 6432 */ 6433 function _fnMacros ( settings, str, entries ) 6434 { 6435 // When infinite scrolling, we are always starting at 1. _iDisplayStart is 6436 // used only internally 6437 var 6438 formatter = settings.fnFormatNumber, 6439 start = settings._iDisplayStart+1, 6440 len = settings._iDisplayLength, 6441 vis = settings.fnRecordsDisplay(), 6442 max = settings.fnRecordsTotal(), 6443 all = len === -1; 6444 6445 return str. 6446 replace(/_START_/g, formatter.call( settings, start ) ). 6447 replace(/_END_/g, formatter.call( settings, settings.fnDisplayEnd() ) ). 6448 replace(/_MAX_/g, formatter.call( settings, max ) ). 6449 replace(/_TOTAL_/g, formatter.call( settings, vis ) ). 6450 replace(/_PAGE_/g, formatter.call( settings, all ? 1 : Math.ceil( start / len ) ) ). 6451 replace(/_PAGES_/g, formatter.call( settings, all ? 1 : Math.ceil( vis / len ) ) ). 6452 replace(/_ENTRIES_/g, settings.api.i18n('entries', '', entries) ). 6453 replace(/_ENTRIES-MAX_/g, settings.api.i18n('entries', '', max) ). 6454 replace(/_ENTRIES-TOTAL_/g, settings.api.i18n('entries', '', vis) ); 6455 } 6456 6457 6458 6459 /** 6460 * Computed structure of the DataTables API, defined by the options passed to 6461 * `DataTable.Api.register()` when building the API. 6462 * 6463 * The structure is built in order to speed creation and extension of the Api 6464 * objects since the extensions are effectively pre-parsed. 6465 * 6466 * The array is an array of objects with the following structure, where this 6467 * base array represents the Api prototype base: 6468 * 6469 * [ 6470 * { 6471 * name: 'data' -- string - Property name 6472 * val: function () {}, -- function - Api method (or undefined if just an object 6473 * methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result 6474 * propExt: [ ... ] -- array - Array of Api object definitions to extend the property 6475 * }, 6476 * { 6477 * name: 'row' 6478 * val: {}, 6479 * methodExt: [ ... ], 6480 * propExt: [ 6481 * { 6482 * name: 'data' 6483 * val: function () {}, 6484 * methodExt: [ ... ], 6485 * propExt: [ ... ] 6486 * }, 6487 * ... 6488 * ] 6489 * } 6490 * ] 6491 * 6492 * @type {Array} 6493 * @ignore 6494 */ 6495 var __apiStruct = []; 6496 6497 6498 /** 6499 * `Array.prototype` reference. 6500 * 6501 * @type object 6502 * @ignore 6503 */ 6504 var __arrayProto = Array.prototype; 6505 6506 6507 /** 6508 * Abstraction for `context` parameter of the `Api` constructor to allow it to 6509 * take several different forms for ease of use. 6510 * 6511 * Each of the input parameter types will be converted to a DataTables settings 6512 * object where possible. 6513 * 6514 * @param {string|node|jQuery|object} mixed DataTable identifier. Can be one 6515 * of: 6516 * 6517 * * `string` - jQuery selector. Any DataTables' matching the given selector 6518 * with be found and used. 6519 * * `node` - `TABLE` node which has already been formed into a DataTable. 6520 * * `jQuery` - A jQuery object of `TABLE` nodes. 6521 * * `object` - DataTables settings object 6522 * * `DataTables.Api` - API instance 6523 * @return {array|null} Matching DataTables settings objects. `null` or 6524 * `undefined` is returned if no matching DataTable is found. 6525 * @ignore 6526 */ 6527 var _toSettings = function ( mixed ) 6528 { 6529 var idx, jq; 6530 var settings = DataTable.settings; 6531 var tables = _pluck(settings, 'nTable'); 6532 6533 if ( ! mixed ) { 6534 return []; 6535 } 6536 else if ( mixed.nTable && mixed.oFeatures ) { 6537 // DataTables settings object 6538 return [ mixed ]; 6539 } 6540 else if ( mixed.nodeName && mixed.nodeName.toLowerCase() === 'table' ) { 6541 // Table node 6542 idx = tables.indexOf(mixed); 6543 return idx !== -1 ? [ settings[idx] ] : null; 6544 } 6545 else if ( mixed && typeof mixed.settings === 'function' ) { 6546 return mixed.settings().toArray(); 6547 } 6548 else if ( typeof mixed === 'string' ) { 6549 // jQuery selector 6550 jq = $(mixed).get(); 6551 } 6552 else if ( mixed instanceof $ ) { 6553 // jQuery object (also DataTables instance) 6554 jq = mixed.get(); 6555 } 6556 6557 if ( jq ) { 6558 return settings.filter(function (v, idx) { 6559 return jq.includes(tables[idx]); 6560 }); 6561 } 6562 }; 6563 6564 6565 /** 6566 * DataTables API class - used to control and interface with one or more 6567 * DataTables enhanced tables. 6568 * 6569 * The API class is heavily based on jQuery, presenting a chainable interface 6570 * that you can use to interact with tables. Each instance of the API class has 6571 * a "context" - i.e. the tables that it will operate on. This could be a single 6572 * table, all tables on a page or a sub-set thereof. 6573 * 6574 * Additionally the API is designed to allow you to easily work with the data in 6575 * the tables, retrieving and manipulating it as required. This is done by 6576 * presenting the API class as an array like interface. The contents of the 6577 * array depend upon the actions requested by each method (for example 6578 * `rows().nodes()` will return an array of nodes, while `rows().data()` will
6579 * return an array of objects or arrays depending upon your table's 6580 * configuration). The API object has a number of array like methods (`push`, 6581 * `pop`, `reverse` etc) as well as additional helper methods (`each`, `pluck`, 6582 * `unique` etc) to assist your working with the data held in a table. 6583 * 6584 * Most methods (those which return an Api instance) are chainable, which means 6585 * the return from a method call also has all of the methods available that the 6586 * top level object had. For example, these two calls are equivalent: 6587 * 6588 * // Not chained 6589 * api.row.add( {...} ); 6590 * api.draw(); 6591 * 6592 * // Chained 6593 * api.row.add( {...} ).draw(); 6594 * 6595 * @class DataTable.Api 6596 * @param {array|object|string|jQuery} context DataTable identifier. This is 6597 * used to define which DataTables enhanced tables this API will operate on. 6598 * Can be one of: 6599 * 6600 * * `string` - jQuery selector. Any DataTables' matching the given selector 6601 * with be found and used. 6602 * * `node` - `TABLE` node which has already been formed into a DataTable. 6603 * * `jQuery` - A jQuery object of `TABLE` nodes. 6604 * * `object` - DataTables settings object 6605 * @param {array} [data] Data to initialise the Api instance with. 6606 * 6607 * @example 6608 * // Direct initialisation during DataTables construction 6609 * var api = $('#example').DataTable(); 6610 * 6611 * @example 6612 * // Initialisation using a DataTables jQuery object 6613 * var api = $('#example').dataTable().api(); 6614 * 6615 * @example 6616 * // Initialisation as a constructor 6617 * var api = new DataTable.Api( 'table.dataTable' ); 6618 */ 6619 _Api = function ( context, data ) 6620 { 6621 if ( ! (this instanceof _Api) ) { 6622 return new _Api( context, data ); 6623 } 6624 6625 var settings = []; 6626 var ctxSettings = function ( o ) { 6627 var a = _toSettings( o ); 6628 if ( a ) { 6629 settings.push.apply( settings, a ); 6630 } 6631 }; 6632 6633 if ( Array.isArray( context ) ) { 6634 for ( var i=0, ien=context.length ; i<ien ; i++ ) { 6635 ctxSettings( context[i] ); 6636 } 6637 } 6638 else { 6639 ctxSettings( context ); 6640 } 6641 6642 // Remove duplicates 6643 this.context = settings.length > 1 6644 ? _unique( settings ) 6645 : settings; 6646 6647 // Initial data 6648 if ( data ) { 6649 this.push.apply(this, data); 6650 } 6651 6652 // selector 6653 this.selector = { 6654 rows: null, 6655 cols: null, 6656 opts: null 6657 }; 6658 6659 _Api.extend( this, this, __apiStruct ); 6660 }; 6661 6662 DataTable.Api = _Api; 6663 6664 // Don't destroy the existing prototype, just extend it. Required for jQuery 2's 6665 // isPlainObject. 6666 $.extend( _Api.prototype, { 6667 any: function () 6668 { 6669 return this.count() !== 0; 6670 }, 6671 6672 context: [], // array of table settings objects 6673 6674 count: function () 6675 { 6676 return this.flatten().length; 6677 }, 6678 6679 each: function ( fn ) 6680 { 6681 for ( var i=0, ien=this.length ; i<ien; i++ ) { 6682 fn.call( this, this[i], i, this ); 6683 } 6684 6685 return this; 6686 }, 6687 6688 eq: function ( idx ) 6689 { 6690 var ctx = this.context; 6691 6692 return ctx.length > idx ? 6693 new _Api( ctx[idx], this[idx] ) : 6694 null; 6695 }, 6696 6697 filter: function ( fn ) 6698 { 6699 var a = __arrayProto.filter.call( this, fn, this ); 6700 6701 return new _Api( this.context, a ); 6702 }, 6703 6704 flatten: function () 6705 { 6706 var a = []; 6707 6708 return new _Api( this.context, a.concat.apply( a, this.toArray() ) ); 6709 }, 6710 6711 get: function ( idx ) 6712 { 6713 return this[ idx ]; 6714 }, 6715 6716 join: __arrayProto.join, 6717 6718 includes: function ( find ) { 6719 return this.indexOf( find ) === -1 ? false : true; 6720 }, 6721 6722 indexOf: __arrayProto.indexOf, 6723 6724 iterator: function ( flatten, type, fn, alwaysNew ) { 6725 var 6726 a = [], ret, 6727 i, ien, j, jen, 6728 context = this.context, 6729 rows, items, item, 6730 selector = this.selector; 6731 6732 // Argument shifting 6733 if ( typeof flatten === 'string' ) { 6734 alwaysNew = fn; 6735 fn = type; 6736 type = flatten; 6737 flatten = false; 6738 } 6739 6740 for ( i=0, ien=context.length ; i<ien ; i++ ) { 6741 var apiInst = new _Api( context[i] ); 6742 6743 if ( type === 'table' ) { 6744 ret = fn.call( apiInst, context[i], i ); 6745 6746 if ( ret !== undefined ) { 6747 a.push( ret ); 6748 } 6749 } 6750 else if ( type === 'columns' || type === 'rows' ) { 6751 // this has same length as context - one entry for each table 6752 ret = fn.call( apiInst, context[i], this[i], i ); 6753 6754 if ( ret !== undefined ) { 6755 a.push( ret ); 6756 } 6757 } 6758 else if ( type === 'every' || type === 'column' || type === 'column-rows' || type === 'row' || type === 'cell' ) { 6759 // columns and rows share the same structure. 6760 // 'this' is an array of column indexes for each context 6761 items = this[i]; 6762 6763 if ( type === 'column-rows' ) { 6764 rows = _selector_row_indexes( context[i], selector.opts ); 6765 } 6766 6767 for ( j=0, jen=items.length ; j<jen ; j++ ) { 6768 item = items[j]; 6769 6770 if ( type === 'cell' ) { 6771 ret = fn.call( apiInst, context[i], item.row, item.column, i, j ); 6772 } 6773 else { 6774 ret = fn.call( apiInst, context[i], item, i, j, rows ); 6775 } 6776 6777 if ( ret !== undefined ) { 6778 a.push( ret ); 6779 } 6780 } 6781 } 6782 } 6783 6784 if ( a.length || alwaysNew ) { 6785 var api = new _Api( context, flatten ? a.concat.apply( [], a ) : a ); 6786 var apiSelector = api.selector; 6787 apiSelector.rows = selector.rows; 6788 apiSelector.cols = selector.cols; 6789 apiSelector.opts = selector.opts; 6790 return api; 6791 } 6792 return this; 6793 }, 6794 6795 lastIndexOf: __arrayProto.lastIndexOf, 6796 6797 length: 0, 6798 6799 map: function ( fn ) 6800 { 6801 var a = __arrayProto.map.call( this, fn, this ); 6802 6803 return new _Api( this.context, a ); 6804 }, 6805 6806 pluck: function ( prop ) 6807 { 6808 var fn = DataTable.util.get(prop); 6809 6810 return this.map( function ( el ) { 6811 return fn(el); 6812 } ); 6813 }, 6814 6815 pop: __arrayProto.pop, 6816 6817 push: __arrayProto.push, 6818 6819 reduce: __arrayProto.reduce, 6820 6821 reduceRight: __arrayProto.reduceRight, 6822 6823 reverse: __arrayProto.reverse, 6824 6825 // Object with rows, columns and opts 6826 selector: null, 6827 6828 shift: __arrayProto.shift, 6829 6830 slice: function () { 6831 return new _Api( this.context, this ); 6832 }, 6833 6834 sort: __arrayProto.sort, 6835 6836 splice: __arrayProto.splice, 6837 6838 toArray: function () 6839 { 6840 return __arrayProto.slice.call( this ); 6841 }, 6842 6843 to$: function () 6844 { 6845 return $( this ); 6846 }, 6847 6848 toJQuery: function () 6849 { 6850 return $( this ); 6851 }, 6852 6853 unique: function () 6854 { 6855 return new _Api( this.context, _unique(this.toArray()) ); 6856 }, 6857 6858 unshift: __arrayProto.unshift 6859 } ); 6860 6861 6862 function _api_scope( scope, fn, struc ) { 6863 return function () { 6864 var ret = fn.apply( scope || this, arguments ); 6865 6866 // Method extension 6867 _Api.extend( ret, ret, struc.methodExt ); 6868 return ret; 6869 }; 6870 } 6871 6872 function _api_find( src, name ) { 6873 for ( var i=0, ien=src.length ; i<ien ; i++ ) { 6874 if ( src[i].name === name ) { 6875 return src[i]; 6876 } 6877 } 6878 return null; 6879 } 6880 6881 window.__apiStruct = __apiStruct; 6882 6883 _Api.extend = function ( scope, obj, ext ) 6884 { 6885 // Only extend API instances and static properties of the API 6886 if ( ! ext.length || ! obj || ( ! (obj instanceof _Api) && ! obj.__dt_wrapper ) ) { 6887 return; 6888 } 6889 6890 var 6891 i, ien, 6892 struct; 6893 6894 for ( i=0, ien=ext.length ; i<ien ; i++ ) { 6895 struct = ext[i]; 6896 6897 if (struct.name === '__proto__') { 6898 continue; 6899 } 6900 6901 // Value 6902 obj[ struct.name ] = struct.type === 'function' ? 6903 _api_scope( scope, struct.val, struct ) : 6904 struct.type === 'object' ? 6905 {} : 6906 struct.val; 6907 6908 obj[ struct.name ].__dt_wrapper = true; 6909 6910 // Property extension 6911 _Api.extend( scope, obj[ struct.name ], struct.propExt ); 6912 } 6913 }; 6914 6915 // [ 6916 // { 6917 // name: 'data' -- string - Property name 6918 // val: function () {}, -- function - Api method (or undefined if just an object 6919 // methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result 6920 // propExt: [ ... ] -- array - Array of Api object definitions to extend the property 6921 // }, 6922 // { 6923 // name: 'row' 6924 // val: {}, 6925 // methodExt: [ ... ], 6926 // propExt: [ 6927 // { 6928 // name: 'data' 6929 // val: function () {}, 6930 // methodExt: [ ... ], 6931 // propExt: [ ... ] 6932 // }, 6933 // ... 6934 // ] 6935 // } 6936 // ] 6937 6938 6939 _Api.register = _api_register = function ( name, val ) 6940 { 6941 if ( Array.isArray( name ) ) { 6942 for ( var j=0, jen=name.length ; j<jen ; j++ ) { 6943 _Api.register( name[j], val ); 6944 } 6945 return; 6946 } 6947 6948 var 6949 i, ien, 6950 heir = name.split('.'), 6951 struct = __apiStruct, 6952 key, method; 6953 6954 for ( i=0, ien=heir.length ; i<ien ; i++ ) { 6955 method = heir[i].indexOf('()') !== -1; 6956 key = method ?
6957 heir[i].replace('()', '') : 6958 heir[i]; 6959 6960 var src = _api_find( struct, key ); 6961 if ( ! src ) { 6962 src = { 6963 name: key, 6964 val: {}, 6965 methodExt: [], 6966 propExt: [], 6967 type: 'object' 6968 }; 6969 struct.push( src ); 6970 } 6971 6972 if ( i === ien-1 ) { 6973 src.val = val; 6974 src.type = typeof val === 'function' ? 6975 'function' : 6976 $.isPlainObject( val ) ? 6977 'object' : 6978 'other'; 6979 } 6980 else { 6981 struct = method ? 6982 src.methodExt : 6983 src.propExt; 6984 } 6985 } 6986 }; 6987 6988 _Api.registerPlural = _api_registerPlural = function ( pluralName, singularName, val ) { 6989 _Api.register( pluralName, val ); 6990 6991 _Api.register( singularName, function () { 6992 var ret = val.apply( this, arguments ); 6993 6994 if ( ret === this ) { 6995 // Returned item is the API instance that was passed in, return it 6996 return this; 6997 } 6998 else if ( ret instanceof _Api ) { 6999 // New API instance returned, want the value from the first item 7000 // in the returned array for the singular result. 7001 return ret.length ? 7002 Array.isArray( ret[0] ) ? 7003 new _Api( ret.context, ret[0] ) : // Array results are 'enhanced' 7004 ret[0] : 7005 undefined; 7006 } 7007 7008 // Non-API return - just fire it back 7009 return ret; 7010 } ); 7011 }; 7012 7013 7014 /** 7015 * Selector for HTML tables. Apply the given selector to the give array of 7016 * DataTables settings objects. 7017 * 7018 * @param {string|integer} [selector] jQuery selector string or integer 7019 * @param {array} Array of DataTables settings objects to be filtered 7020 * @return {array} 7021 * @ignore 7022 */ 7023 var __table_selector = function ( selector, a ) 7024 { 7025 if ( Array.isArray(selector) ) { 7026 var result = []; 7027 7028 selector.forEach(function (sel) { 7029 var inner = __table_selector(sel, a); 7030 7031 result.push.apply(result, inner); 7032 }); 7033 7034 return result.filter( function (item) { 7035 return item; 7036 }); 7037 } 7038 7039 // Integer is used to pick out a table by index 7040 if ( typeof selector === 'number' ) { 7041 return [ a[ selector ] ]; 7042 } 7043 7044 // Perform a jQuery selector on the table nodes 7045 var nodes = a.map( function (el) { 7046 return el.nTable; 7047 } ); 7048 7049 return $(nodes) 7050 .filter( selector ) 7051 .map( function () { 7052 // Need to translate back from the table node to the settings 7053 var idx = nodes.indexOf(this); 7054 return a[ idx ]; 7055 } ) 7056 .toArray(); 7057 }; 7058 7059 7060 7061 /** 7062 * Context selector for the API's context (i.e. the tables the API instance 7063 * refers to. 7064 * 7065 * @name DataTable.Api#tables 7066 * @param {string|integer} [selector] Selector to pick which tables the iterator 7067 * should operate on. If not given, all tables in the current context are 7068 * used. This can be given as a jQuery selector (for example `':gt(0)'`) to 7069 * select multiple tables or as an integer to select a single table. 7070 * @returns {DataTable.Api} Returns a new API instance if a selector is given. 7071 */ 7072 _api_register( 'tables()', function ( selector ) { 7073 // A new instance is created if there was a selector specified 7074 return selector !== undefined && selector !== null ? 7075 new _Api( __table_selector( selector, this.context ) ) : 7076 this; 7077 } ); 7078 7079 7080 _api_register( 'table()', function ( selector ) { 7081 var tables = this.tables( selector ); 7082 var ctx = tables.context; 7083 7084 // Truncate to the first matched table 7085 return ctx.length ? 7086 new _Api( ctx[0] ) : 7087 tables; 7088 } ); 7089 7090 // Common methods, combined to reduce size 7091 [ 7092 ['nodes', 'node', 'nTable'], 7093 ['body', 'body', 'nTBody'], 7094 ['header', 'header', 'nTHead'], 7095 ['footer', 'footer', 'nTFoot'], 7096 ].forEach(function (item) { 7097 _api_registerPlural( 7098 'tables().' + item[0] + '()', 7099 'table().' + item[1] + '()' , 7100 function () { 7101 return this.iterator( 'table', function ( ctx ) { 7102 return ctx[item[2]]; 7103 }, 1 ); 7104 } 7105 ); 7106 }); 7107 7108 // Structure methods 7109 [ 7110 ['header', 'aoHeader'], 7111 ['footer', 'aoFooter'], 7112 ].forEach(function (item) { 7113 _api_register( 'table().' + item[0] + '.structure()' , function (selector) { 7114 var indexes = this.columns(selector).indexes().flatten(); 7115 var ctx = this.context[0]; 7116 7117 return _fnHeaderLayout(ctx, ctx[item[1]], indexes); 7118 } ); 7119 }) 7120 7121 7122 _api_registerPlural( 'tables().containers()', 'table().container()' , function () { 7123 return this.iterator( 'table', function ( ctx ) { 7124 return ctx.nTableWrapper; 7125 }, 1 ); 7126 } ); 7127 7128 _api_register( 'tables().every()', function ( fn ) { 7129 var that = this; 7130 7131 return this.iterator('table', function (s, i) { 7132 fn.call(that.table(i), i); 7133 }); 7134 }); 7135 7136 _api_register( 'caption()', function ( value, side ) { 7137 var context = this.context; 7138 7139 // Getter - return existing node's content 7140 if ( value === undefined ) { 7141 var caption = context[0].captionNode; 7142 7143 return caption && context.length ? 7144 caption.innerHTML : 7145 null; 7146 } 7147 7148 return this.iterator( 'table', function ( ctx ) { 7149 var table = $(ctx.nTable); 7150 var caption = $(ctx.captionNode); 7151 var container = $(ctx.nTableWrapper); 7152 7153 // Create the node if it doesn't exist yet 7154 if ( ! caption.length ) { 7155 caption = $('<caption/>').html( value ); 7156 ctx.captionNode = caption[0]; 7157 7158 // If side isn't set, we need to insert into the document to let the 7159 // CSS decide so we can read it back, otherwise there is no way to 7160 // know if the CSS would put it top or bottom for scrolling
vendor: 7,114 bytes, lines 7161-7426
7161 if (! side) { 7162 table.prepend(caption); 7163 7164 side = caption.css('caption-side'); 7165 } 7166 } 7167 7168 caption.html( value ); 7169 7170 if ( side ) { 7171 caption.css( 'caption-side', side ); 7172 caption[0]._captionSide = side; 7173 } 7174 7175 if (container.find('div.dataTables_scroll').length) { 7176 var selector = (side === 'top' ? 'Head' : 'Foot'); 7177 7178 container.find('div.dataTables_scroll'+ selector +' table').prepend(caption); 7179 } 7180 else { 7181 table.prepend(caption); 7182 } 7183 }, 1 ); 7184 } ); 7185 7186 _api_register( 'caption.node()', function () { 7187 var ctx = this.context; 7188 7189 return ctx.length ? ctx[0].captionNode : null; 7190 } ); 7191 7192 7193 /** 7194 * Redraw the tables in the current context. 7195 */ 7196 _api_register( 'draw()', function ( paging ) { 7197 return this.iterator( 'table', function ( settings ) { 7198 if ( paging === 'page' ) { 7199 _fnDraw( settings ); 7200 } 7201 else { 7202 if ( typeof paging === 'string' ) { 7203 paging = paging === 'full-hold' ? 7204 false : 7205 true; 7206 } 7207 7208 _fnReDraw( settings, paging===false ); 7209 } 7210 } ); 7211 } ); 7212 7213 7214 7215 /** 7216 * Get the current page index. 7217 * 7218 * @return {integer} Current page index (zero based) 7219 *//** 7220 * Set the current page. 7221 * 7222 * Note that if you attempt to show a page which does not exist, DataTables will 7223 * not throw an error, but rather reset the paging. 7224 * 7225 * @param {integer|string} action The paging action to take. This can be one of: 7226 * * `integer` - The page index to jump to 7227 * * `string` - An action to take: 7228 * * `first` - Jump to first page. 7229 * * `next` - Jump to the next page 7230 * * `previous` - Jump to previous page 7231 * * `last` - Jump to the last page. 7232 * @returns {DataTables.Api} this 7233 */ 7234 _api_register( 'page()', function ( action ) { 7235 if ( action === undefined ) { 7236 return this.page.info().page; // not an expensive call 7237 } 7238 7239 // else, have an action to take on all tables 7240 return this.iterator( 'table', function ( settings ) { 7241 _fnPageChange( settings, action ); 7242 } ); 7243 } ); 7244 7245 7246 /** 7247 * Paging information for the first table in the current context. 7248 * 7249 * If you require paging information for another table, use the `table()` method 7250 * with a suitable selector. 7251 * 7252 * @return {object} Object with the following properties set: 7253 * * `page` - Current page index (zero based - i.e. the first page is `0`) 7254 * * `pages` - Total number of pages 7255 * * `start` - Display index for the first record shown on the current page 7256 * * `end` - Display index for the last record shown on the current page 7257 * * `length` - Display length (number of records). Note that generally `start 7258 * + length = end`, but this is not always true, for example if there are 7259 * only 2 records to show on the final page, with a length of 10. 7260 * * `recordsTotal` - Full data set length 7261 * * `recordsDisplay` - Data set length once the current filtering criterion 7262 * are applied. 7263 */ 7264 _api_register( 'page.info()', function () { 7265 if ( this.context.length === 0 ) { 7266 return undefined; 7267 } 7268 7269 var 7270 settings = this.context[0], 7271 start = settings._iDisplayStart, 7272 len = settings.oFeatures.bPaginate ? settings._iDisplayLength : -1, 7273 visRecords = settings.fnRecordsDisplay(), 7274 all = len === -1; 7275 7276 return { 7277 "page": all ? 0 : Math.floor( start / len ), 7278 "pages": all ? 1 : Math.ceil( visRecords / len ), 7279 "start": start, 7280 "end": settings.fnDisplayEnd(), 7281 "length": len, 7282 "recordsTotal": settings.fnRecordsTotal(), 7283 "recordsDisplay": visRecords, 7284 "serverSide": _fnDataSource( settings ) === 'ssp' 7285 }; 7286 } ); 7287 7288 7289 /** 7290 * Get the current page length. 7291 * 7292 * @return {integer} Current page length. Note `-1` indicates that all records 7293 * are to be shown. 7294 *//** 7295 * Set the current page length. 7296 * 7297 * @param {integer} Page length to set. Use `-1` to show all records. 7298 * @returns {DataTables.Api} this 7299 */ 7300 _api_register( 'page.len()', function ( len ) { 7301 // Note that we can't call this function 'length()' because `length` 7302 // is a Javascript property of functions which defines how many arguments 7303 // the function expects. 7304 if ( len === undefined ) { 7305 return this.context.length !== 0 ? 7306 this.context[0]._iDisplayLength : 7307 undefined; 7308 } 7309 7310 // else, set the page length 7311 return this.iterator( 'table', function ( settings ) { 7312 _fnLengthChange( settings, len ); 7313 } ); 7314 } ); 7315 7316 7317 7318 var __reload = function ( settings, holdPosition, callback ) { 7319 // Use the draw event to trigger a callback 7320 if ( callback ) { 7321 var api = new _Api( settings ); 7322 7323 api.one( 'draw', function () { 7324 callback( api.ajax.json() ); 7325 } ); 7326 } 7327 7328 if ( _fnDataSource( settings ) == 'ssp' ) { 7329 _fnReDraw( settings, holdPosition ); 7330 } 7331 else { 7332 _fnProcessingDisplay( settings, true ); 7333 7334 // Cancel an existing request 7335 var xhr = settings.jqXHR; 7336 if ( xhr && xhr.readyState !== 4 ) { 7337 xhr.abort(); 7338 } 7339 7340 // Trigger xhr 7341 _fnBuildAjax( settings, {}, function( json ) { 7342 _fnClearTable( settings ); 7343 7344 var data = _fnAjaxDataSrc( settings, json ); 7345 for ( var i=0, ien=data.length ; i<ien ; i++ ) { 7346 _fnAddData( settings, data[i] ); 7347 } 7348 7349 _fnReDraw( settings, holdPosition ); 7350 _fnInitComplete( settings ); 7351 _fnProcessingDisplay( settings, false ); 7352 } ); 7353 } 7354 }; 7355 7356 7357 /** 7358 * Get the JSON response from the last Ajax request that DataTables made to the 7359 * server. Note that this returns the JSON from the first table in the current 7360 * context. 7361 * 7362 * @return {object} JSON received from the server. 7363 */ 7364 _api_register( 'ajax.json()', function () { 7365 var ctx = this.context; 7366 7367 if ( ctx.length > 0 ) { 7368 return ctx[0].json; 7369 } 7370 7371 // else return undefined; 7372 } ); 7373 7374 7375 /** 7376 * Get the data submitted in the last Ajax request 7377 */ 7378 _api_register( 'ajax.params()', function () { 7379 var ctx = this.context; 7380 7381 if ( ctx.length > 0 ) { 7382 return ctx[0].oAjaxData; 7383 } 7384 7385 // else return undefined; 7386 } ); 7387 7388 7389 /** 7390 * Reload tables from the Ajax data source. Note that this function will 7391 * automatically re-draw the table when the remote data has been loaded. 7392 * 7393 * @param {boolean} [reset=true] Reset (default) or hold the current paging 7394 * position. A full re-sort and re-filter is performed when this method is 7395 * called, which is why the pagination reset is the default action. 7396 * @returns {DataTables.Api} this 7397 */ 7398 _api_register( 'ajax.reload()', function ( callback, resetPaging ) { 7399 return this.iterator( 'table', function (settings) { 7400 __reload( settings, resetPaging===false, callback ); 7401 } ); 7402 } ); 7403 7404 7405 /** 7406 * Get the current Ajax URL. Note that this returns the URL from the first 7407 * table in the current context. 7408 * 7409 * @return {string} Current Ajax source URL 7410 *//** 7411 * Set the Ajax URL. Note that this will set the URL for all tables in the 7412 * current context. 7413 * 7414 * @param {string} url URL to set. 7415 * @returns {DataTables.Api} this 7416 */ 7417 _api_register( 'ajax.url()', function ( url ) { 7418 var ctx = this.context; 7419 7420 if ( url === undefined ) { 7421 // get 7422 if ( ctx.length === 0 ) { 7423 return undefined; 7424 } 7425 ctx = ctx[0]; 7426
7427 return $.isPlainObject( ctx.ajax ) ? 7428 ctx.ajax.url : 7429 ctx.ajax; 7430 } 7431 7432 // set 7433 return this.iterator( 'table', function ( settings ) { 7434 if ( $.isPlainObject( settings.ajax ) ) { 7435 settings.ajax.url = url; 7436 } 7437 else { 7438 settings.ajax = url; 7439 } 7440 } ); 7441 } ); 7442 7443 7444 /** 7445 * Load data from the newly set Ajax URL. Note that this method is only 7446 * available when `ajax.url()` is used to set a URL. Additionally, this method 7447 * has the same effect as calling `ajax.reload()` but is provided for 7448 * convenience when setting a new URL. Like `ajax.reload()` it will 7449 * automatically redraw the table once the remote data has been loaded. 7450 * 7451 * @returns {DataTables.Api} this 7452 */ 7453 _api_register( 'ajax.url().load()', function ( callback, resetPaging ) { 7454 // Same as a reload, but makes sense to present it for easy access after a 7455 // url change 7456 return this.iterator( 'table', function ( ctx ) { 7457 __reload( ctx, resetPaging===false, callback ); 7458 } ); 7459 } ); 7460 7461 7462 7463 7464 var _selector_run = function ( type, selector, selectFn, settings, opts ) 7465 { 7466 var 7467 out = [], res, 7468 a, i, ien, j, jen, 7469 selectorType = typeof selector; 7470 7471 // Can't just check for isArray here, as an API or jQuery instance might be 7472 // given with their array like look 7473 if ( ! selector || selectorType === 'string' || selectorType === 'function' || selector.length === undefined ) { 7474 selector = [ selector ]; 7475 } 7476 7477 for ( i=0, ien=selector.length ; i<ien ; i++ ) { 7478 // Only split on simple strings - complex expressions will be jQuery selectors 7479 a = selector[i] && selector[i].split && ! selector[i].match(/[[(:]/) ? 7480 selector[i].split(',') : 7481 [ selector[i] ]; 7482 7483 for ( j=0, jen=a.length ; j<jen ; j++ ) { 7484 res = selectFn( typeof a[j] === 'string' ? (a[j]).trim() : a[j] ); 7485 7486 // Remove empty items 7487 res = res.filter( function (item) { 7488 return item !== null && item !== undefined; 7489 }); 7490 7491 if ( res && res.length ) { 7492 out = out.concat( res ); 7493 } 7494 } 7495 } 7496 7497 // selector extensions 7498 var ext = _ext.selector[ type ]; 7499 if ( ext.length ) { 7500 for ( i=0, ien=ext.length ; i<ien ; i++ ) { 7501 out = ext[i]( settings, opts, out ); 7502 } 7503 } 7504 7505 return _unique( out ); 7506 }; 7507 7508 7509 var _selector_opts = function ( opts ) 7510 { 7511 if ( ! opts ) { 7512 opts = {}; 7513 } 7514 7515 // Backwards compatibility for 1.9- which used the terminology filter rather 7516 // than search 7517 if ( opts.filter && opts.search === undefined ) { 7518 opts.search = opts.filter; 7519 } 7520 7521 return $.extend( { 7522 search: 'none', 7523 order: 'current', 7524 page: 'all' 7525 }, opts ); 7526 }; 7527 7528 7529 // Reduce the API instance to the first item found 7530 var _selector_first = function ( old ) 7531 { 7532 let inst = new _Api(old.context[0]); 7533 7534 // Use a push rather than passing to the constructor, since it will 7535 // merge arrays down automatically, which isn't what is wanted here 7536 if (old.length) { 7537 inst.push( old[0] ); 7538 } 7539 7540 inst.selector = old.selector; 7541 7542 // Limit to a single row / column / cell 7543 if (inst.length && inst[0].length > 1) { 7544 inst[0].splice(1); 7545 } 7546 7547 return inst; 7548 }; 7549 7550 7551 var _selector_row_indexes = function ( settings, opts ) 7552 { 7553 var 7554 i, ien, tmp, a=[], 7555 displayFiltered = settings.aiDisplay, 7556 displayMaster = settings.aiDisplayMaster; 7557 7558 var 7559 search = opts.search, // none, applied, removed 7560 order = opts.order, // applied, current, index (original - compatibility with 1.9) 7561 page = opts.page; // all, current 7562 7563 if ( page == 'current' ) { 7564 // Current page implies that order=current and filter=applied, since it is 7565 // fairly senseless otherwise, regardless of what order and search actually 7566 // are 7567 for ( i=settings._iDisplayStart, ien=settings.fnDisplayEnd() ; i<ien ; i++ ) { 7568 a.push( displayFiltered[i] ); 7569 } 7570 } 7571 else if ( order == 'current' || order == 'applied' ) { 7572 if ( search == 'none') { 7573 a = displayMaster.slice(); 7574 } 7575 else if ( search == 'applied' ) { 7576 a = displayFiltered.slice(); 7577 } 7578 else if ( search == 'removed' ) { 7579 // O(n+m) solution by creating a hash map 7580 var displayFilteredMap = {}; 7581 7582 for ( i=0, ien=displayFiltered.length ; i<ien ; i++ ) { 7583 displayFilteredMap[displayFiltered[i]] = null; 7584 } 7585 7586 displayMaster.forEach(function (item) { 7587 if (! Object.prototype.hasOwnProperty.call(displayFilteredMap, item)) { 7588 a.push(item); 7589 } 7590 }); 7591 } 7592 } 7593 else if ( order == 'index' || order == 'original' ) { 7594 for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
vendor: 20,168 bytes, lines 7595-8371
7595 if (! settings.aoData[i]) { 7596 continue; 7597 } 7598 7599 if ( search == 'none' ) { 7600 a.push( i ); 7601 } 7602 else { // applied | removed 7603 tmp = displayFiltered.indexOf(i); 7604 7605 if ((tmp === -1 && search == 'removed') || 7606 (tmp >= 0 && search == 'applied') ) 7607 { 7608 a.push( i ); 7609 } 7610 } 7611 } 7612 } 7613 else if ( typeof order === 'number' ) { 7614 // Order the rows by the given column 7615 var ordered = _fnSort(settings, order, 'asc'); 7616 7617 if (search === 'none') { 7618 a = ordered; 7619 } 7620 else { // applied | removed 7621 for (i=0; i<ordered.length; i++) { 7622 tmp = displayFiltered.indexOf(ordered[i]); 7623 7624 if ((tmp === -1 && search == 'removed') || 7625 (tmp >= 0 && search == 'applied') ) 7626 { 7627 a.push( ordered[i] ); 7628 } 7629 } 7630 } 7631 } 7632 7633 return a; 7634 }; 7635 7636 7637 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 7638 * Rows 7639 * 7640 * {} - no selector - use all available rows 7641 * {integer} - row aoData index 7642 * {node} - TR node 7643 * {string} - jQuery selector to apply to the TR elements 7644 * {array} - jQuery array of nodes, or simply an array of TR nodes 7645 * 7646 */ 7647 var __row_selector = function ( settings, selector, opts ) 7648 { 7649 var rows; 7650 var run = function ( sel ) { 7651 var selInt = _intVal( sel ); 7652 var aoData = settings.aoData; 7653 7654 // Short cut - selector is a number and no options provided (default is 7655 // all records, so no need to check if the index is in there, since it 7656 // must be - dev error if the index doesn't exist). 7657 if ( selInt !== null && ! opts ) { 7658 return [ selInt ]; 7659 } 7660 7661 if ( ! rows ) { 7662 rows = _selector_row_indexes( settings, opts ); 7663 } 7664 7665 if ( selInt !== null && rows.indexOf(selInt) !== -1 ) { 7666 // Selector - integer 7667 return [ selInt ]; 7668 } 7669 else if ( sel === null || sel === undefined || sel === '' ) { 7670 // Selector - none 7671 return rows; 7672 } 7673 7674 // Selector - function 7675 if ( typeof sel === 'function' ) { 7676 return rows.map( function (idx) { 7677 var row = aoData[ idx ]; 7678 return sel( idx, row._aData, row.nTr ) ? idx : null; 7679 } ); 7680 } 7681 7682 // Selector - node 7683 if ( sel.nodeName ) { 7684 var rowIdx = sel._DT_RowIndex; // Property added by DT for fast lookup 7685 var cellIdx = sel._DT_CellIndex; 7686 7687 if ( rowIdx !== undefined ) { 7688 // Make sure that the row is actually still present in the table 7689 return aoData[ rowIdx ] && aoData[ rowIdx ].nTr === sel ? 7690 [ rowIdx ] : 7691 []; 7692 } 7693 else if ( cellIdx ) { 7694 return aoData[ cellIdx.row ] && aoData[ cellIdx.row ].nTr === sel.parentNode ? 7695 [ cellIdx.row ] : 7696 []; 7697 } 7698 else { 7699 var host = $(sel).closest('*[data-dt-row]'); 7700 return host.length ? 7701 [ host.data('dt-row') ] : 7702 []; 7703 } 7704 } 7705 7706 // ID selector. Want to always be able to select rows by id, regardless 7707 // of if the tr element has been created or not, so can't rely upon 7708 // jQuery here - hence a custom implementation. This does not match 7709 // Sizzle's fast selector or HTML4 - in HTML5 the ID can be anything, 7710 // but to select it using a CSS selector engine (like Sizzle or 7711 // querySelect) it would need to need to be escaped for some characters. 7712 // DataTables simplifies this for row selectors since you can select 7713 // only a row. A # indicates an id any anything that follows is the id - 7714 // unescaped. 7715 if ( typeof sel === 'string' && sel.charAt(0) === '#' ) { 7716 // get row index from id 7717 var rowObj = settings.aIds[ sel.replace( /^#/, '' ) ]; 7718 if ( rowObj !== undefined ) { 7719 return [ rowObj.idx ]; 7720 } 7721 7722 // need to fall through to jQuery in case there is DOM id that 7723 // matches 7724 } 7725 7726 // Get nodes in the order from the `rows` array with null values removed 7727 var nodes = _removeEmpty( 7728 _pluck_order( settings.aoData, rows, 'nTr' ) 7729 ); 7730 7731 // Selector - jQuery selector string, array of nodes or jQuery object/ 7732 // As jQuery's .filter() allows jQuery objects to be passed in filter, 7733 // it also allows arrays, so this will cope with all three options 7734 return $(nodes) 7735 .filter( sel ) 7736 .map( function () { 7737 return this._DT_RowIndex; 7738 } ) 7739 .toArray(); 7740 }; 7741 7742 var matched = _selector_run( 'row', selector, run, settings, opts ); 7743 7744 if (opts.order === 'current' || opts.order === 'applied') { 7745 _fnSortDisplay(settings, matched); 7746 } 7747 7748 return matched; 7749 }; 7750 7751 7752 _api_register( 'rows()', function ( selector, opts ) { 7753 // argument shifting 7754 if ( selector === undefined ) { 7755 selector = ''; 7756 } 7757 else if ( $.isPlainObject( selector ) ) { 7758 opts = selector; 7759 selector = ''; 7760 } 7761 7762 opts = _selector_opts( opts ); 7763 7764 var inst = this.iterator( 'table', function ( settings ) { 7765 return __row_selector( settings, selector, opts ); 7766 }, 1 ); 7767 7768 // Want argument shifting here and in __row_selector? 7769 inst.selector.rows = selector; 7770 inst.selector.opts = opts; 7771 7772 return inst; 7773 } ); 7774 7775 _api_register( 'rows().nodes()', function () { 7776 return this.iterator( 'row', function ( settings, row ) { 7777 return settings.aoData[ row ].nTr || undefined; 7778 }, 1 ); 7779 } ); 7780 7781 _api_register( 'rows().data()', function () { 7782 return this.iterator( true, 'rows', function ( settings, rows ) { 7783 return _pluck_order( settings.aoData, rows, '_aData' ); 7784 }, 1 ); 7785 } ); 7786 7787 _api_registerPlural( 'rows().cache()', 'row().cache()', function ( type ) { 7788 return this.iterator( 'row', function ( settings, row ) { 7789 var r = settings.aoData[ row ]; 7790 return type === 'search' ? r._aFilterData : r._aSortData; 7791 }, 1 ); 7792 } ); 7793 7794 _api_registerPlural( 'rows().invalidate()', 'row().invalidate()', function ( src ) { 7795 return this.iterator( 'row', function ( settings, row ) { 7796 _fnInvalidate( settings, row, src ); 7797 } ); 7798 } ); 7799 7800 _api_registerPlural( 'rows().indexes()', 'row().index()', function () { 7801 return this.iterator( 'row', function ( settings, row ) { 7802 return row; 7803 }, 1 ); 7804 } ); 7805 7806 _api_registerPlural( 'rows().ids()', 'row().id()', function ( hash ) { 7807 var a = []; 7808 var context = this.context; 7809 7810 // `iterator` will drop undefined values, but in this case we want them 7811 for ( var i=0, ien=context.length ; i<ien ; i++ ) { 7812 for ( var j=0, jen=this[i].length ; j<jen ; j++ ) { 7813 var id = context[i].rowIdFn( context[i].aoData[ this[i][j] ]._aData ); 7814 a.push( (hash === true ? '#' : '' )+ id ); 7815 } 7816 } 7817 7818 return new _Api( context, a ); 7819 } ); 7820 7821 _api_registerPlural( 'rows().remove()', 'row().remove()', function () { 7822 this.iterator( 'row', function ( settings, row ) { 7823 var data = settings.aoData; 7824 var rowData = data[ row ]; 7825 7826 // Delete from the display arrays 7827 var idx = settings.aiDisplayMaster.indexOf(row); 7828 if (idx !== -1) { 7829 settings.aiDisplayMaster.splice(idx, 1); 7830 } 7831 7832 // For server-side processing tables - subtract the deleted row from the count 7833 if ( settings._iRecordsDisplay > 0 ) { 7834 settings._iRecordsDisplay--; 7835 } 7836 7837 // Check for an 'overflow' they case for displaying the table 7838 _fnLengthOverflow( settings ); 7839 7840 // Remove the row's ID reference if there is one 7841 var id = settings.rowIdFn( rowData._aData ); 7842 if ( id !== undefined ) { 7843 delete settings.aIds[ id ]; 7844 } 7845 7846 data[row] = null; 7847 } ); 7848 7849 return this; 7850 } ); 7851 7852 7853 _api_register( 'rows.add()', function ( rows ) { 7854 var newRows = this.iterator( 'table', function ( settings ) { 7855 var row, i, ien; 7856 var out = []; 7857 7858 for ( i=0, ien=rows.length ; i<ien ; i++ ) { 7859 row = rows[i]; 7860 7861 if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) { 7862 out.push( _fnAddTr( settings, row )[0] ); 7863 } 7864 else { 7865 out.push( _fnAddData( settings, row ) ); 7866 } 7867 } 7868 7869 return out; 7870 }, 1 ); 7871 7872 // Return an Api.rows() extended instance, so rows().nodes() etc can be used 7873 var modRows = this.rows( -1 ); 7874 modRows.pop(); 7875 modRows.push.apply(modRows, newRows); 7876 7877 return modRows; 7878 } ); 7879 7880 7881 7882 7883 7884 /** 7885 * 7886 */ 7887 _api_register( 'row()', function ( selector, opts ) { 7888 return _selector_first( this.rows( selector, opts ) ); 7889 } ); 7890 7891 7892 _api_register( 'row().data()', function ( data ) { 7893 var ctx = this.context; 7894 7895 if ( data === undefined ) { 7896 // Get 7897 return ctx.length && this.length && this[0].length ? 7898 ctx[0].aoData[ this[0] ]._aData : 7899 undefined; 7900 } 7901 7902 // Set 7903 var row = ctx[0].aoData[ this[0] ]; 7904 row._aData = data; 7905 7906 // If the DOM has an id, and the data source is an array 7907 if ( Array.isArray( data ) && row.nTr && row.nTr.id ) { 7908 _fnSetObjectDataFn( ctx[0].rowId )( data, row.nTr.id ); 7909 } 7910 7911 // Automatically invalidate 7912 _fnInvalidate( ctx[0], this[0], 'data' ); 7913 7914 return this; 7915 } ); 7916 7917 7918 _api_register( 'row().node()', function () { 7919 var ctx = this.context; 7920 7921 if (ctx.length && this.length && this[0].length) { 7922 var row = ctx[0].aoData[ this[0] ]; 7923 7924 if (row && row.nTr) { 7925 return row.nTr; 7926 } 7927 } 7928 7929 return null; 7930 } ); 7931 7932 7933 _api_register( 'row.add()', function ( row ) { 7934 // Allow a jQuery object to be passed in - only a single row is added from 7935 // it though - the first element in the set 7936 if ( row instanceof $ && row.length ) { 7937 row = row[0]; 7938 } 7939 7940 var rows = this.iterator( 'table', function ( settings ) { 7941 if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) { 7942 return _fnAddTr( settings, row )[0]; 7943 } 7944 return _fnAddData( settings, row ); 7945 } ); 7946 7947 // Return an Api.rows() extended instance, with the newly added row selected 7948 return this.row( rows[0] ); 7949 } ); 7950 7951 7952 $(document).on('plugin-init.dt', function (e, context) { 7953 var api = new _Api( context ); 7954 7955 api.on( 'stateSaveParams.DT', function ( e, settings, d ) { 7956 // This could be more compact with the API, but it is a lot faster as a simple 7957 // internal loop 7958 var idFn = settings.rowIdFn; 7959 var rows = settings.aiDisplayMaster; 7960 var ids = []; 7961 7962 for (var i=0 ; i<rows.length ; i++) { 7963 var rowIdx = rows[i]; 7964 var data = settings.aoData[rowIdx]; 7965 7966 if (data._detailsShow) { 7967 ids.push( '#' + idFn(data._aData) ); 7968 } 7969 } 7970 7971 d.childRows = ids; 7972 }); 7973 7974 // For future state loads (e.g. with StateRestore) 7975 api.on( 'stateLoaded.DT', function (e, settings, state) { 7976 __details_state_load( api, state ); 7977 }); 7978 7979 // And the initial load state 7980 __details_state_load( api, api.state.loaded() ); 7981 }); 7982 7983 var __details_state_load = function (api, state) 7984 { 7985 if ( state && state.childRows ) { 7986 api 7987 .rows( state.childRows.map(function (id) { 7988 // Escape any `:` characters from the row id. Accounts for 7989 // already escaped characters. 7990 return id.replace(/([^:\\]*(?:\\.[^:\\]*)*):/g, "$1\\:"); 7991 }) ) 7992 .every( function () { 7993 _fnCallbackFire( api.settings()[0], null, 'requestChild', [ this ] ) 7994 }); 7995 } 7996 } 7997 7998 var __details_add = function ( ctx, row, data, klass ) 7999 { 8000 // Convert to array of TR elements 8001 var rows = []; 8002 var addRow = function ( r, k ) { 8003 // Recursion to allow for arrays of jQuery objects 8004 if ( Array.isArray( r ) || r instanceof $ ) { 8005 for ( var i=0, ien=r.length ; i<ien ; i++ ) { 8006 addRow( r[i], k ); 8007 } 8008 return; 8009 } 8010 8011 // If we get a TR element, then just add it directly - up to the dev 8012 // to add the correct number of columns etc 8013 if ( r.nodeName && r.nodeName.toLowerCase() === 'tr' ) { 8014 r.setAttribute( 'data-dt-row', row.idx ); 8015 rows.push( r ); 8016 } 8017 else { 8018 // Otherwise create a row with a wrapper 8019 var created = $('<tr><td></td></tr>') 8020 .attr( 'data-dt-row', row.idx ) 8021 .addClass( k ); 8022 8023 $('td', created) 8024 .addClass( k ) 8025 .html( r )[0].colSpan = _fnVisbleColumns( ctx ); 8026 8027 rows.push( created[0] ); 8028 } 8029 }; 8030 8031 addRow( data, klass ); 8032 8033 if ( row._details ) { 8034 row._details.detach(); 8035 } 8036 8037 row._details = $(rows); 8038 8039 // If the children were already shown, that state should be retained 8040 if ( row._detailsShow ) { 8041 row._details.insertAfter( row.nTr ); 8042 } 8043 }; 8044 8045 8046 // Make state saving of child row details async to allow them to be batch processed 8047 var __details_state = DataTable.util.throttle( 8048 function (ctx) { 8049 _fnSaveState( ctx[0] ) 8050 }, 8051 500 8052 ); 8053 8054 8055 var __details_remove = function ( api, idx ) 8056 { 8057 var ctx = api.context; 8058 8059 if ( ctx.length ) { 8060 var row = ctx[0].aoData[ idx !== undefined ? idx : api[0] ]; 8061 8062 if ( row && row._details ) { 8063 row._details.remove(); 8064 8065 row._detailsShow = undefined; 8066 row._details = undefined; 8067 $( row.nTr ).removeClass( 'dt-hasChild' ); 8068 __details_state( ctx ); 8069 } 8070 } 8071 }; 8072 8073 8074 var __details_display = function ( api, show ) { 8075 var ctx = api.context; 8076 8077 if ( ctx.length && api.length ) { 8078 var row = ctx[0].aoData[ api[0] ]; 8079 8080 if ( row._details ) { 8081 row._detailsShow = show; 8082 8083 if ( show ) { 8084 row._details.insertAfter( row.nTr ); 8085 $( row.nTr ).addClass( 'dt-hasChild' ); 8086 } 8087 else { 8088 row._details.detach(); 8089 $( row.nTr ).removeClass( 'dt-hasChild' ); 8090 } 8091 8092 _fnCallbackFire( ctx[0], null, 'childRow', [ show, api.row( api[0] ) ] ) 8093 8094 __details_events( ctx[0] ); 8095 __details_state( ctx ); 8096 } 8097 } 8098 }; 8099 8100 8101 var __details_events = function ( settings ) 8102 { 8103 var api = new _Api( settings ); 8104 var namespace = '.dt.DT_details'; 8105 var drawEvent = 'draw'+namespace; 8106 var colvisEvent = 'column-sizing'+namespace; 8107 var destroyEvent = 'destroy'+namespace; 8108 var data = settings.aoData; 8109 8110 api.off( drawEvent +' '+ colvisEvent +' '+ destroyEvent ); 8111 8112 if ( _pluck( data, '_details' ).length > 0 ) { 8113 // On each draw, insert the required elements into the document 8114 api.on( drawEvent, function ( e, ctx ) { 8115 if ( settings !== ctx ) { 8116 return; 8117 } 8118 8119 api.rows( {page:'current'} ).eq(0).each( function (idx) { 8120 // Internal data grab 8121 var row = data[ idx ]; 8122 8123 if ( row._detailsShow ) { 8124 row._details.insertAfter( row.nTr ); 8125 } 8126 } ); 8127 } ); 8128 8129 // Column visibility change - update the colspan 8130 api.on( colvisEvent, function ( e, ctx ) { 8131 if ( settings !== ctx ) { 8132 return; 8133 } 8134 8135 // Update the colspan for the details rows (note, only if it already has 8136 // a colspan) 8137 var row, visible = _fnVisbleColumns( ctx ); 8138 8139 for ( var i=0, ien=data.length ; i<ien ; i++ ) { 8140 row = data[i]; 8141 8142 if ( row && row._details ) { 8143 row._details.each(function () { 8144 var el = $(this).children('td'); 8145 8146 if (el.length == 1) { 8147 el.attr('colspan', visible); 8148 } 8149 }); 8150 } 8151 } 8152 } ); 8153 8154 // Table destroyed - nuke any child rows 8155 api.on( destroyEvent, function ( e, ctx ) { 8156 if ( settings !== ctx ) { 8157 return; 8158 } 8159 8160 for ( var i=0, ien=data.length ; i<ien ; i++ ) { 8161 if ( data[i] && data[i]._details ) { 8162 __details_remove( api, i ); 8163 } 8164 } 8165 } ); 8166 } 8167 }; 8168 8169 // Strings for the method names to help minification 8170 var _emp = ''; 8171 var _child_obj = _emp+'row().child'; 8172 var _child_mth = _child_obj+'()'; 8173 8174 // data can be: 8175 // tr 8176 // string 8177 // jQuery or array of any of the above 8178 _api_register( _child_mth, function ( data, klass ) { 8179 var ctx = this.context; 8180 8181 if ( data === undefined ) { 8182 // get 8183 return ctx.length && this.length && ctx[0].aoData[ this[0] ] 8184 ? ctx[0].aoData[ this[0] ]._details 8185 : undefined; 8186 } 8187 else if ( data === true ) { 8188 // show 8189 this.child.show(); 8190 } 8191 else if ( data === false ) { 8192 // remove 8193 __details_remove( this ); 8194 } 8195 else if ( ctx.length && this.length ) { 8196 // set 8197 __details_add( ctx[0], ctx[0].aoData[ this[0] ], data, klass ); 8198 } 8199 8200 return this; 8201 } ); 8202 8203 8204 _api_register( [ 8205 _child_obj+'.show()', 8206 _child_mth+'.show()' // only when `child()` was called with parameters (without 8207 ], function () { // it returns an object and this method is not executed) 8208 __details_display( this, true ); 8209 return this; 8210 } ); 8211 8212 8213 _api_register( [ 8214 _child_obj+'.hide()', 8215 _child_mth+'.hide()' // only when `child()` was called with parameters (without 8216 ], function () { // it returns an object and this method is not executed) 8217 __details_display( this, false ); 8218 return this; 8219 } ); 8220 8221 8222 _api_register( [ 8223 _child_obj+'.remove()', 8224 _child_mth+'.remove()' // only when `child()` was called with parameters (without 8225 ], function () { // it returns an object and this method is not executed) 8226 __details_remove( this ); 8227 return this; 8228 } ); 8229 8230 8231 _api_register( _child_obj+'.isShown()', function () { 8232 var ctx = this.context; 8233 8234 if ( ctx.length && this.length ) { 8235 // _detailsShown as false or undefined will fall through to return false 8236 return ctx[0].aoData[ this[0] ]._detailsShow || false; 8237 } 8238 return false; 8239 } ); 8240 8241 8242 8243 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 8244 * Columns 8245 * 8246 * {integer} - column index (>=0 count from left, <0 count from right) 8247 * "{integer}:visIdx" - visible column index (i.e. translate to column index) (>=0 count from left, <0 count from right) 8248 * "{integer}:visible" - alias for {integer}:visIdx (>=0 count from left, <0 count from right) 8249 * "{string}:name" - column name 8250 * "{string}" - jQuery selector on column header nodes 8251 * 8252 */ 8253 8254 // can be an array of these items, comma separated list, or an array of comma 8255 // separated lists 8256 8257 var __re_column_selector = /^([^:]+):(name|title|visIdx|visible)$/; 8258 8259 8260 // r1 and r2 are redundant - but it means that the parameters match for the 8261 // iterator callback in columns().data() 8262 var __columnData = function ( settings, column, r1, r2, rows, type ) { 8263 var a = []; 8264 for ( var row=0, ien=rows.length ; row<ien ; row++ ) { 8265 a.push( _fnGetCellData( settings, rows[row], column, type ) ); 8266 } 8267 return a; 8268 }; 8269 8270 8271 var __column_header = function ( settings, column, row ) { 8272 var header = settings.aoHeader; 8273 var target = row !== undefined 8274 ? row 8275 : settings.bSortCellsTop // legacy support 8276 ? 0 8277 : header.length - 1; 8278 8279 return header[target][column].cell; 8280 }; 8281 8282 var __column_selector = function ( settings, selector, opts ) 8283 { 8284 var 8285 columns = settings.aoColumns, 8286 names = _pluck( columns, 'sName' ), 8287 titles = _pluck( columns, 'sTitle' ), 8288 cells = DataTable.util.get('[].[].cell')(settings.aoHeader), 8289 nodes = _unique( _flatten([], cells) ); 8290 8291 var run = function ( s ) { 8292 var selInt = _intVal( s ); 8293 8294 // Selector - all 8295 if ( s === '' ) { 8296 return _range( columns.length ); 8297 } 8298 8299 // Selector - index 8300 if ( selInt !== null ) { 8301 return [ selInt >= 0 ? 8302 selInt : // Count from left 8303 columns.length + selInt // Count from right (+ because its a negative value) 8304 ]; 8305 } 8306 8307 // Selector = function 8308 if ( typeof s === 'function' ) { 8309 var rows = _selector_row_indexes( settings, opts ); 8310 8311 return columns.map(function (col, idx) { 8312 return s( 8313 idx, 8314 __columnData( settings, idx, 0, 0, rows ), 8315 __column_header( settings, idx ) 8316 ) ? idx : null; 8317 }); 8318 } 8319 8320 // jQuery or string selector 8321 var match = typeof s === 'string' ? 8322 s.match( __re_column_selector ) : 8323 ''; 8324 8325 if ( match ) { 8326 switch( match[2] ) { 8327 case 'visIdx': 8328 case 'visible': 8329 var idx = parseInt( match[1], 10 ); 8330 // Visible index given, convert to column index 8331 if ( idx < 0 ) { 8332 // Counting from the right 8333 var visColumns = columns.map( function (col,i) { 8334 return col.bVisible ? i : null; 8335 } ); 8336 return [ visColumns[ visColumns.length + idx ] ]; 8337 } 8338 // Counting from the left 8339 return [ _fnVisibleToColumnIndex( settings, idx ) ]; 8340 8341 case 'name': 8342 // match by name. `names` is column index complete and in order 8343 return names.map( function (name, i) { 8344 return name === match[1] ? i : null; 8345 } ); 8346 8347 case 'title': 8348 // match by column title 8349 return titles.map( function (title, i) { 8350 return title === match[1] ? i : null; 8351 } ); 8352 8353 default: 8354 return []; 8355 } 8356 } 8357 8358 // Cell in the table body 8359 if ( s.nodeName && s._DT_CellIndex ) { 8360 return [ s._DT_CellIndex.column ]; 8361 } 8362 8363 // jQuery selector on the TH elements for the columns 8364 var jqResult = $( nodes ) 8365 .filter( s ) 8366 .map( function () { 8367 return _fnColumnsFromHeader( this ); // `nodes` is column index complete and in order 8368 } ) 8369 .toArray(); 8370 8371 if ( jqResult.length || ! s.nodeName ) {
8372 return jqResult; 8373 } 8374 8375 // Otherwise a node which might have a `dt-column` data attribute, or be 8376 // a child or such an element 8377 var host = $(s).closest('*[data-dt-column]'); 8378 return host.length ? 8379 [ host.data('dt-column') ] : 8380 []; 8381 }; 8382 8383 return _selector_run( 'column', selector, run, settings, opts ); 8384 }; 8385 8386 8387 var __setColumnVis = function ( settings, column, vis ) { 8388 var 8389 cols = settings.aoColumns, 8390 col = cols[ column ], 8391 data = settings.aoData, 8392 cells, i, ien, tr; 8393 8394 // Get 8395 if ( vis === undefined ) { 8396 return col.bVisible; 8397 } 8398 8399 // Set 8400 // No change 8401 if ( col.bVisible === vis ) { 8402 return false; 8403 } 8404 8405 if ( vis ) { 8406 // Insert column 8407 // Need to decide if we should use appendChild or insertBefore 8408 var insertBefore = _pluck(cols, 'bVisible').indexOf(true, column+1); 8409 8410 for ( i=0, ien=data.length ; i<ien ; i++ ) { 8411 if (data[i]) { 8412 tr = data[i].nTr; 8413 cells = data[i].anCells; 8414 8415 if ( tr ) { 8416 // insertBefore can act like appendChild if 2nd arg is null 8417 tr.insertBefore( cells[ column ], cells[ insertBefore ] || null ); 8418 } 8419 } 8420 } 8421 } 8422 else { 8423 // Remove column 8424 $( _pluck( settings.aoData, 'anCells', column ) ).detach(); 8425 } 8426 8427 // Common actions 8428 col.bVisible = vis; 8429 8430 _colGroup(settings); 8431 8432 return true; 8433 }; 8434 8435 8436 _api_register( 'columns()', function ( selector, opts ) { 8437 // argument shifting 8438 if ( selector === undefined ) { 8439 selector = ''; 8440 } 8441 else if ( $.isPlainObject( selector ) ) { 8442 opts = selector; 8443 selector = ''; 8444 } 8445 8446 opts = _selector_opts( opts ); 8447 8448 var inst = this.iterator( 'table', function ( settings ) { 8449 return __column_selector( settings, selector, opts ); 8450 }, 1 ); 8451 8452 // Want argument shifting here and in _row_selector? 8453 inst.selector.cols = selector; 8454 inst.selector.opts = opts; 8455 8456 return inst; 8457 } ); 8458 8459 _api_registerPlural( 'columns().header()', 'column().header()', function ( row ) { 8460 return this.iterator( 'column', function (settings, column) { 8461 return __column_header(settings, column, row); 8462 }, 1 ); 8463 } ); 8464 8465 _api_registerPlural( 'columns().footer()', 'column().footer()', function ( row ) { 8466 return this.iterator( 'column', function ( settings, column ) { 8467 var footer = settings.aoFooter; 8468 8469 if (! footer.length) { 8470 return null; 8471 } 8472 8473 return settings.aoFooter[row !== undefined ? row : 0][column].cell; 8474 }, 1 ); 8475 } ); 8476 8477 _api_registerPlural( 'columns().data()', 'column().data()', function () { 8478 return this.iterator( 'column-rows', __columnData, 1 ); 8479 } ); 8480 8481 _api_registerPlural( 'columns().render()', 'column().render()', function ( type ) { 8482 return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) { 8483 return __columnData( settings, column, i, j, rows, type ); 8484 }, 1 ); 8485 } ); 8486 8487 _api_registerPlural( 'columns().dataSrc()', 'column().dataSrc()', function () { 8488 return this.iterator( 'column', function ( settings, column ) { 8489 return settings.aoColumns[column].mData; 8490 }, 1 ); 8491 } ); 8492 8493 _api_registerPlural( 'columns().cache()', 'column().cache()', function ( type ) { 8494 return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) { 8495 return _pluck_order( settings.aoData, rows, 8496 type === 'search' ? '_aFilterData' : '_aSortData', column 8497 ); 8498 }, 1 ); 8499 } ); 8500 8501 _api_registerPlural( 'columns().init()', 'column().init()', function () { 8502 return this.iterator( 'column', function ( settings, column ) { 8503 return settings.aoColumns[column]; 8504 }, 1 ); 8505 } ); 8506 8507 _api_registerPlural( 'columns().nodes()', 'column().nodes()', function () { 8508 return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) { 8509 return _pluck_order( settings.aoData, rows, 'anCells', column ) ; 8510 }, 1 ); 8511 } ); 8512 8513 _api_registerPlural( 'columns().titles()', 'column().title()', function (title, row) { 8514 return this.iterator( 'column', function ( settings, column ) { 8515 // Argument shifting 8516 if (typeof title === 'number') { 8517 row = title; 8518 title = undefined; 8519 } 8520 8521 var span = $('span.dt-column-title', this.column(column).header(row)); 8522 8523 if (title !== undefined) { 8524 span.html(title); 8525 return this; 8526 } 8527 8528 return span.html(); 8529 }, 1 ); 8530 } ); 8531 8532 _api_registerPlural( 'columns().types()', 'column().type()', function () { 8533 return this.iterator( 'column', function ( settings, column ) { 8534 var type = settings.aoColumns[column].sType; 8535
8536 // If the type was invalidated, then resolve it. This actually does 8537 // all columns at the moment. Would only happen once if getting all 8538 // column's data types. 8539 if (! type) { 8540 _fnColumnTypes(settings); 8541 } 8542 8543 return type; 8544 }, 1 ); 8545 } ); 8546 8547 _api_registerPlural( 'columns().visible()', 'column().visible()', function ( vis, calc ) { 8548 var that = this; 8549 var changed = []; 8550 var ret = this.iterator( 'column', function ( settings, column ) { 8551 if ( vis === undefined ) { 8552 return settings.aoColumns[ column ].bVisible; 8553 } // else 8554 8555 if (__setColumnVis( settings, column, vis )) { 8556 changed.push(column); 8557 } 8558 } ); 8559 8560 // Group the column visibility changes 8561 if ( vis !== undefined ) { 8562 this.iterator( 'table', function ( settings ) { 8563 // Redraw the header after changes 8564 _fnDrawHead( settings, settings.aoHeader ); 8565 _fnDrawHead( settings, settings.aoFooter ); 8566 8567 // Update colspan for no records display. Child rows and extensions will use their own 8568 // listeners to do this - only need to update the empty table item here 8569 if ( ! settings.aiDisplay.length ) { 8570 $(settings.nTBody).find('td[colspan]').attr('colspan', _fnVisbleColumns(settings)); 8571 } 8572 8573 _fnSaveState( settings ); 8574 8575 // Second loop once the first is done for events 8576 that.iterator( 'column', function ( settings, column ) { 8577 if (changed.includes(column)) { 8578 _fnCallbackFire( settings, null, 'column-visibility', [settings, column, vis, calc] ); 8579 } 8580 } ); 8581 8582 if ( changed.length && (calc === undefined || calc) ) { 8583 that.columns.adjust(); 8584 } 8585 }); 8586 } 8587 8588 return ret; 8589 } ); 8590 8591 _api_registerPlural( 'columns().widths()', 'column().width()', function () { 8592 // Injects a fake row into the table for just a moment so the widths can 8593 // be read, regardless of colspan in the header and rows being present in 8594 // the body 8595 var columns = this.columns(':visible').count(); 8596 var row = $('<tr>').html('<td>' + Array(columns).join('</td><td>') + '</td>'); 8597 8598 $(this.table().body()).append(row); 8599 8600 var widths = row.children().map(function () { 8601 return $(this).outerWidth(); 8602 }); 8603 8604 row.remove(); 8605 8606 return this.iterator( 'column', function ( settings, column ) { 8607 var visIdx = _fnColumnIndexToVisible( settings, column ); 8608 8609 return visIdx !== null ? widths[visIdx] : 0; 8610 }, 1); 8611 } ); 8612 8613 _api_registerPlural( 'columns().indexes()', 'column().index()', function ( type ) { 8614 return this.iterator( 'column', function ( settings, column ) { 8615 return type === 'visible' ? 8616 _fnColumnIndexToVisible( settings, column ) : 8617 column; 8618 }, 1 ); 8619 } ); 8620 8621 _api_register( 'columns.adjust()', function () { 8622 return this.iterator( 'table', function ( settings ) { 8623 _fnAdjustColumnSizing( settings ); 8624 }, 1 ); 8625 } ); 8626 8627 _api_register( 'column.index()', function ( type, idx ) { 8628 if ( this.context.length !== 0 ) { 8629 var ctx = this.context[0]; 8630 8631 if ( type === 'fromVisible' || type === 'toData' ) { 8632 return _fnVisibleToColumnIndex( ctx, idx ); 8633 } 8634 else if ( type === 'fromData' || type === 'toVisible' ) { 8635 return _fnColumnIndexToVisible( ctx, idx ); 8636 } 8637 } 8638 } ); 8639 8640 _api_register( 'column()', function ( selector, opts ) { 8641 return _selector_first( this.columns( selector, opts ) ); 8642 } ); 8643 8644 var __cell_selector = function ( settings, selector, opts ) 8645 { 8646 var data = settings.aoData; 8647 var rows = _selector_row_indexes( settings, opts ); 8648 var cells = _removeEmpty( _pluck_order( data, rows, 'anCells' ) ); 8649 var allCells = $(_flatten( [], cells )); 8650 var row; 8651 var columns = settings.aoColumns.length; 8652 var a, i, ien, j, o, host; 8653 8654 var run = function ( s ) { 8655 var fnSelector = typeof s === 'function'; 8656 8657 if ( s === null || s === undefined || fnSelector ) { 8658 // All cells and function selectors 8659 a = []; 8660 8661 for ( i=0, ien=rows.length ; i<ien ; i++ ) { 8662 row = rows[i]; 8663 8664 for ( j=0 ; j<columns ; j++ ) { 8665 o = { 8666 row: row, 8667 column: j 8668 }; 8669 8670 if ( fnSelector ) { 8671 // Selector - function 8672 host = data[ row ]; 8673 8674 if ( s( o, _fnGetCellData(settings, row, j), host.anCells ? host.anCells[j] : null ) ) { 8675 a.push( o ); 8676 } 8677 } 8678 else { 8679 // Selector - all 8680 a.push( o ); 8681 } 8682 } 8683 } 8684 8685 return a; 8686 } 8687 8688 // Selector - index 8689 if ( $.isPlainObject( s ) ) { 8690 // Valid cell index and its in the array of selectable rows 8691 return s.column !== undefined && s.row !== undefined && rows.indexOf(s.row) !== -1 ? 8692 [s] : 8693 []; 8694 } 8695 8696 // Selector - jQuery filtered cells 8697 var jqResult = allCells 8698 .filter( s ) 8699 .map( function (i, el) { 8700 return { // use a new object, in case someone changes the values 8701 row: el._DT_CellIndex.row, 8702 column: el._DT_CellIndex.column 8703 }; 8704 } ) 8705 .toArray(); 8706 8707 if ( jqResult.length || ! s.nodeName ) {
vendor: 4,988 bytes, lines 8708-8887
8708 return jqResult; 8709 } 8710 8711 // Otherwise the selector is a node, and there is one last option - the 8712 // element might be a child of an element which has dt-row and dt-column 8713 // data attributes 8714 host = $(s).closest('*[data-dt-row]'); 8715 return host.length ? 8716 [ { 8717 row: host.data('dt-row'), 8718 column: host.data('dt-column') 8719 } ] : 8720 []; 8721 }; 8722 8723 return _selector_run( 'cell', selector, run, settings, opts ); 8724 }; 8725 8726 8727 8728 8729 _api_register( 'cells()', function ( rowSelector, columnSelector, opts ) { 8730 // Argument shifting 8731 if ( $.isPlainObject( rowSelector ) ) { 8732 // Indexes 8733 if ( rowSelector.row === undefined ) { 8734 // Selector options in first parameter 8735 opts = rowSelector; 8736 rowSelector = null; 8737 } 8738 else { 8739 // Cell index objects in first parameter 8740 opts = columnSelector; 8741 columnSelector = null; 8742 } 8743 } 8744 if ( $.isPlainObject( columnSelector ) ) { 8745 opts = columnSelector; 8746 columnSelector = null; 8747 } 8748 8749 // Cell selector 8750 if ( columnSelector === null || columnSelector === undefined ) { 8751 return this.iterator( 'table', function ( settings ) { 8752 return __cell_selector( settings, rowSelector, _selector_opts( opts ) ); 8753 } ); 8754 } 8755 8756 // The default built in options need to apply to row and columns 8757 var internalOpts = opts ? { 8758 page: opts.page, 8759 order: opts.order, 8760 search: opts.search 8761 } : {}; 8762 8763 // Row + column selector 8764 var columns = this.columns( columnSelector, internalOpts ); 8765 var rows = this.rows( rowSelector, internalOpts ); 8766 var i, ien, j, jen; 8767 8768 var cellsNoOpts = this.iterator( 'table', function ( settings, idx ) { 8769 var a = []; 8770 8771 for ( i=0, ien=rows[idx].length ; i<ien ; i++ ) { 8772 for ( j=0, jen=columns[idx].length ; j<jen ; j++ ) { 8773 a.push( { 8774 row: rows[idx][i], 8775 column: columns[idx][j] 8776 } ); 8777 } 8778 } 8779 8780 return a; 8781 }, 1 ); 8782 8783 // There is currently only one extension which uses a cell selector extension 8784 // It is a _major_ performance drag to run this if it isn't needed, so this is 8785 // an extension specific check at the moment 8786 var cells = opts && opts.selected ? 8787 this.cells( cellsNoOpts, opts ) : 8788 cellsNoOpts; 8789 8790 $.extend( cells.selector, { 8791 cols: columnSelector, 8792 rows: rowSelector, 8793 opts: opts 8794 } ); 8795 8796 return cells; 8797 } ); 8798 8799 8800 _api_registerPlural( 'cells().nodes()', 'cell().node()', function () { 8801 return this.iterator( 'cell', function ( settings, row, column ) { 8802 var data = settings.aoData[ row ]; 8803 8804 return data && data.anCells ? 8805 data.anCells[ column ] : 8806 undefined; 8807 }, 1 ); 8808 } ); 8809 8810 8811 _api_register( 'cells().data()', function () { 8812 return this.iterator( 'cell', function ( settings, row, column ) { 8813 return _fnGetCellData( settings, row, column ); 8814 }, 1 ); 8815 } ); 8816 8817 8818 _api_registerPlural( 'cells().cache()', 'cell().cache()', function ( type ) { 8819 type = type === 'search' ? '_aFilterData' : '_aSortData'; 8820 8821 return this.iterator( 'cell', function ( settings, row, column ) { 8822 return settings.aoData[ row ][ type ][ column ]; 8823 }, 1 ); 8824 } ); 8825 8826 8827 _api_registerPlural( 'cells().render()', 'cell().render()', function ( type ) { 8828 return this.iterator( 'cell', function ( settings, row, column ) { 8829 return _fnGetCellData( settings, row, column, type ); 8830 }, 1 ); 8831 } ); 8832 8833 8834 _api_registerPlural( 'cells().indexes()', 'cell().index()', function () { 8835 return this.iterator( 'cell', function ( settings, row, column ) { 8836 return { 8837 row: row, 8838 column: column, 8839 columnVisible: _fnColumnIndexToVisible( settings, column ) 8840 }; 8841 }, 1 ); 8842 } ); 8843 8844 8845 _api_registerPlural( 'cells().invalidate()', 'cell().invalidate()', function ( src ) { 8846 return this.iterator( 'cell', function ( settings, row, column ) { 8847 _fnInvalidate( settings, row, src, column ); 8848 } ); 8849 } ); 8850 8851 8852 8853 _api_register( 'cell()', function ( rowSelector, columnSelector, opts ) { 8854 return _selector_first( this.cells( rowSelector, columnSelector, opts ) ); 8855 } ); 8856 8857 8858 _api_register( 'cell().data()', function ( data ) { 8859 var ctx = this.context; 8860 var cell = this[0]; 8861 8862 if ( data === undefined ) { 8863 // Get 8864 return ctx.length && cell.length ? 8865 _fnGetCellData( ctx[0], cell[0].row, cell[0].column ) : 8866 undefined; 8867 } 8868 8869 // Set 8870 _fnSetCellData( ctx[0], cell[0].row, cell[0].column, data ); 8871 _fnInvalidate( ctx[0], cell[0].row, 'data', cell[0].column ); 8872 8873 return this; 8874 } ); 8875 8876 8877 8878 /** 8879 * Get current ordering (sorting) that has been applied to the table. 8880 * 8881 * @returns {array} 2D array containing the sorting information for the first 8882 * table in the current context. Each element in the parent array represents 8883 * a column being sorted upon (i.e. multi-sorting with two columns would have 8884 * 2 inner arrays). The inner arrays may have 2 or 3 elements. The first is 8885 * the column index that the sorting condition applies to, the second is the 8886 * direction of the sort (`desc` or `asc`) and, optionally, the third is the 8887 * index of the sorting order from the `column.sorting` initialisation array.
8888 *//** 8889 * Set the ordering for the table. 8890 * 8891 * @param {integer} order Column index to sort upon. 8892 * @param {string} direction Direction of the sort to be applied (`asc` or `desc`) 8893 * @returns {DataTables.Api} this 8894 *//** 8895 * Set the ordering for the table. 8896 * 8897 * @param {array} order 1D array of sorting information to be applied. 8898 * @param {array} [...] Optional additional sorting conditions 8899 * @returns {DataTables.Api} this 8900 *//** 8901 * Set the ordering for the table. 8902 * 8903 * @param {array} order 2D array of sorting information to be applied. 8904 * @returns {DataTables.Api} this 8905 */ 8906 _api_register( 'order()', function ( order, dir ) { 8907 var ctx = this.context; 8908 var args = Array.prototype.slice.call( arguments ); 8909 8910 if ( order === undefined ) { 8911 // get 8912 return ctx.length !== 0 ? 8913 ctx[0].aaSorting : 8914 undefined; 8915 } 8916 8917 // set 8918 if ( typeof order === 'number' ) { 8919 // Simple column / direction passed in 8920 order = [ [ order, dir ] ]; 8921 } 8922 else if ( args.length > 1 ) { 8923 // Arguments passed in (list of 1D arrays) 8924 order = args; 8925 } 8926 // otherwise a 2D array was passed in 8927 8928 return this.iterator( 'table', function ( settings ) { 8929 settings.aaSorting = Array.isArray(order) ? order.slice() : order; 8930 } ); 8931 } ); 8932 8933 8934 /** 8935 * Attach a sort listener to an element for a given column 8936 * 8937 * @param {node|jQuery|string} node Identifier for the element(s) to attach the 8938 * listener to. This can take the form of a single DOM node, a jQuery 8939 * collection of nodes or a jQuery selector which will identify the node(s). 8940 * @param {integer} column the column that a click on this node will sort on 8941 * @param {function} [callback] callback function when sort is run 8942 * @returns {DataTables.Api} this 8943 */ 8944 _api_register( 'order.listener()', function ( node, column, callback ) { 8945 return this.iterator( 'table', function ( settings ) { 8946 _fnSortAttachListener(settings, node, {}, column, callback); 8947 } ); 8948 } ); 8949 8950 8951 _api_register( 'order.fixed()', function ( set ) { 8952 if ( ! set ) { 8953 var ctx = this.context; 8954 var fixed = ctx.length ? 8955 ctx[0].aaSortingFixed : 8956 undefined; 8957 8958 return Array.isArray( fixed ) ? 8959 { pre: fixed } : 8960 fixed; 8961 } 8962 8963 return this.iterator( 'table', function ( settings ) { 8964 settings.aaSortingFixed = $.extend( true, {}, set ); 8965 } ); 8966 } ); 8967 8968 8969 // Order by the selected column(s) 8970 _api_register( [ 8971 'columns().order()', 8972 'column().order()' 8973 ], function ( dir ) { 8974 var that = this; 8975 8976 if ( ! dir ) { 8977 return this.iterator( 'column', function ( settings, idx ) { 8978 var sort = _fnSortFlatten( settings ); 8979 8980 for ( var i=0, ien=sort.length ; i<ien ; i++ ) { 8981 if ( sort[i].col === idx ) { 8982 return sort[i].dir; 8983 } 8984 } 8985 8986 return null; 8987 }, 1 ); 8988 } 8989 else { 8990 return this.iterator( 'table', function ( settings, i ) { 8991 settings.aaSorting = that[i].map( function (col) { 8992 return [ col, dir ]; 8993 } ); 8994 } ); 8995 } 8996 } ); 8997 8998 _api_registerPlural('columns().orderable()', 'column().orderable()', function ( directions ) { 8999 return this.iterator( 'column', function ( settings, idx ) { 9000 var col = settings.aoColumns[idx]; 9001 9002 return directions ? 9003 col.asSorting : 9004 col.bSortable; 9005 }, 1 ); 9006 } ); 9007 9008 9009 _api_register( 'processing()', function ( show ) { 9010 return this.iterator( 'table', function ( ctx ) { 9011 _fnProcessingDisplay( ctx, show ); 9012 } ); 9013 } ); 9014 9015 9016 _api_register( 'search()', function ( input, regex, smart, caseInsen ) { 9017 var ctx = this.context; 9018 9019 if ( input === undefined ) { 9020 // get 9021 return ctx.length !== 0 ? 9022 ctx[0].oPreviousSearch.search : 9023 undefined; 9024 } 9025 9026 // set 9027 return this.iterator( 'table', function ( settings ) { 9028 if ( ! settings.oFeatures.bFilter ) { 9029 return; 9030 } 9031 9032 if (typeof regex === 'object') { 9033 // New style options to pass to the search builder 9034 _fnFilterComplete( settings, $.extend( settings.oPreviousSearch, regex, { 9035 search: input 9036 } ) ); 9037 } 9038 else { 9039 // Compat for the old options 9040 _fnFilterComplete( settings, $.extend( settings.oPreviousSearch, { 9041 search: input, 9042 regex: regex === null ? false : regex,
9043 smart: smart === null ? true : smart, 9044 caseInsensitive: caseInsen === null ? true : caseInsen 9045 } ) ); 9046 } 9047 } ); 9048 } ); 9049 9050 _api_register( 'search.fixed()', function ( name, search ) { 9051 var ret = this.iterator( true, 'table', function ( settings ) { 9052 var fixed = settings.searchFixed; 9053 9054 if (! name) { 9055 return Object.keys(fixed) 9056 } 9057 else if (search === undefined) { 9058 return fixed[name]; 9059 } 9060 else if (search === null) { 9061 delete fixed[name]; 9062 } 9063 else { 9064 fixed[name] = search; 9065 } 9066 9067 return this; 9068 } ); 9069 9070 return name !== undefined && search === undefined 9071 ? ret[0] 9072 : ret; 9073 } ); 9074 9075 _api_registerPlural( 9076 'columns().search()', 9077 'column().search()', 9078 function ( input, regex, smart, caseInsen ) { 9079 return this.iterator( 'column', function ( settings, column ) { 9080 var preSearch = settings.aoPreSearchCols; 9081 9082 if ( input === undefined ) { 9083 // get 9084 return preSearch[ column ].search; 9085 } 9086 9087 // set 9088 if ( ! settings.oFeatures.bFilter ) { 9089 return; 9090 } 9091 9092 if (typeof regex === 'object') { 9093 // New style options to pass to the search builder 9094 $.extend( preSearch[ column ], regex, { 9095 search: input 9096 } ); 9097 } 9098 else { 9099 // Old style (with not all options available) 9100 $.extend( preSearch[ column ], { 9101 search: input, 9102 regex: regex === null ? false : regex, 9103 smart: smart === null ? true : smart, 9104 caseInsensitive: caseInsen === null ? true : caseInsen 9105 } ); 9106 } 9107 9108 _fnFilterComplete( settings, settings.oPreviousSearch ); 9109 } ); 9110 } 9111 ); 9112 9113 _api_register([ 9114 'columns().search.fixed()', 9115 'column().search.fixed()' 9116 ], 9117 function ( name, search ) { 9118 var ret = this.iterator( true, 'column', function ( settings, colIdx ) { 9119 var fixed = settings.aoColumns[colIdx].searchFixed; 9120 9121 if (! name) { 9122 return Object.keys(fixed) 9123 } 9124 else if (search === undefined) { 9125 return fixed[name]; 9126 } 9127 else if (search === null) { 9128 delete fixed[name]; 9129 } 9130 else { 9131 fixed[name] = search; 9132 } 9133 9134 return this; 9135 } ); 9136 9137 return name !== undefined && search === undefined 9138 ? ret[0] 9139 : ret; 9140 } 9141 ); 9142 /* 9143 * State API methods 9144 */ 9145 9146 _api_register( 'state()', function ( set, ignoreTime ) { 9147 // getter 9148 if ( ! set ) { 9149 return this.context.length ? 9150 this.context[0].oSavedState : 9151 null; 9152 } 9153 9154 var setMutate = $.extend( true, {}, set ); 9155 9156 // setter 9157 return this.iterator( 'table', function ( settings ) { 9158 if ( ignoreTime !== false ) { 9159 setMutate.time = +new Date() + 100; 9160 } 9161 9162 _fnImplementState( settings, setMutate, function(){} ); 9163 } ); 9164 } ); 9165 9166 9167 _api_register( 'state.clear()', function () { 9168 return this.iterator( 'table', function ( settings ) { 9169 // Save an empty object 9170 settings.fnStateSaveCallback.call( settings.oInstance, settings, {} ); 9171 } ); 9172 } ); 9173 9174 9175 _api_register( 'state.loaded()', function () { 9176 return this.context.length ? 9177 this.context[0].oLoadedState : 9178 null; 9179 } ); 9180 9181 9182 _api_register( 'state.save()', function () { 9183 return this.iterator( 'table', function ( settings ) { 9184 _fnSaveState( settings ); 9185 } ); 9186 } ); 9187 9188 /** 9189 * Set the jQuery or window object to be used by DataTables 9190 * 9191 * @param {*} module Library / container object 9192 * @param {string} [type] Library or container type `lib`, `win` or `datetime`. 9193 * If not provided, automatic detection is attempted. 9194 */ 9195 DataTable.use = function (module, type) { 9196 if (type === 'lib' || module.fn) { 9197 $ = module; 9198 } 9199 else if (type == 'win' || module.document) { 9200 window = module; 9201 document = module.document; 9202 } 9203 else if (type === 'datetime' || module.type === 'DateTime') { 9204 DataTable.DateTime = module; 9205 } 9206 } 9207 9208 /** 9209 * CommonJS factory function pass through. This will check if the arguments 9210 * given are a window object or a jQuery object. If so they are set 9211 * accordingly. 9212 * @param {*} root Window 9213 * @param {*} jq jQUery 9214 * @returns {boolean} Indicator 9215 */ 9216 DataTable.factory = function (root, jq) { 9217 var is = false; 9218 9219 // Test if the first parameter is a window object 9220 if (root && root.document) { 9221 window = root; 9222 document = root.document; 9223 } 9224 9225 // Test if the second parameter is a jQuery object 9226 if (jq && jq.fn && jq.fn.jquery) { 9227 $ = jq; 9228 is = true; 9229 } 9230 9231 return is; 9232 } 9233 9234 /**
9235 * Provide a common method for plug-ins to check the version of DataTables being 9236 * used, in order to ensure compatibility. 9237 * 9238 * @param {string} version Version string to check for, in the format "X.Y.Z". 9239 * Note that the formats "X" and "X.Y" are also acceptable. 9240 * @param {string} [version2=current DataTables version] As above, but optional. 9241 * If not given the current DataTables version will be used. 9242 * @returns {boolean} true if this version of DataTables is greater or equal to 9243 * the required version, or false if this version of DataTales is not 9244 * suitable 9245 * @static 9246 * @dtopt API-Static 9247 * 9248 * @example 9249 * alert( $.fn.dataTable.versionCheck( '1.9.0' ) ); 9250 */ 9251 DataTable.versionCheck = function( version, version2 ) 9252 { 9253 var aThis = version2 ? 9254 version2.split('.') : 9255 DataTable.version.split('.'); 9256 var aThat = version.split('.'); 9257 var iThis, iThat; 9258 9259 for ( var i=0, iLen=aThat.length ; i<iLen ; i++ ) { 9260 iThis = parseInt( aThis[i], 10 ) || 0; 9261 iThat = parseInt( aThat[i], 10 ) || 0; 9262 9263 // Parts are the same, keep comparing 9264 if (iThis === iThat) { 9265 continue; 9266 } 9267 9268 // Parts are different, return immediately 9269 return iThis > iThat; 9270 } 9271 9272 return true; 9273 }; 9274 9275 9276 /** 9277 * Check if a `<table>` node is a DataTable table already or not. 9278 * 9279 * @param {node|jquery|string} table Table node, jQuery object or jQuery 9280 * selector for the table to test. Note that if more than more than one 9281 * table is passed on, only the first will be checked 9282 * @returns {boolean} true the table given is a DataTable, or false otherwise 9283 * @static 9284 * @dtopt API-Static 9285 * 9286 * @example 9287 * if ( ! $.fn.DataTable.isDataTable( '#example' ) ) { 9288 * $('#example').dataTable(); 9289 * } 9290 */ 9291 DataTable.isDataTable = function ( table ) 9292 { 9293 var t = $(table).get(0); 9294 var is = false; 9295 9296 if ( table instanceof DataTable.Api ) { 9297 return true; 9298 } 9299 9300 $.each( DataTable.settings, function (i, o) { 9301 var head = o.nScrollHead ? $('table', o.nScrollHead)[0] : null; 9302 var foot = o.nScrollFoot ? $('table', o.nScrollFoot)[0] : null; 9303 9304 if ( o.nTable === t || head === t || foot === t ) { 9305 is = true; 9306 } 9307 } ); 9308 9309 return is; 9310 }; 9311 9312 9313 /** 9314 * Get all DataTable tables that have been initialised - optionally you can 9315 * select to get only currently visible tables. 9316 * 9317 * @param {boolean} [visible=false] Flag to indicate if you want all (default) 9318 * or visible tables only. 9319 * @returns {array} Array of `table` nodes (not DataTable instances) which are 9320 * DataTables 9321 * @static 9322 * @dtopt API-Static 9323 * 9324 * @example 9325 * $.each( $.fn.dataTable.tables(true), function () { 9326 * $(table).DataTable().columns.adjust(); 9327 * } ); 9328 */ 9329 DataTable.tables = function ( visible ) 9330 { 9331 var api = false; 9332 9333 if ( $.isPlainObject( visible ) ) { 9334 api = visible.api; 9335 visible = visible.visible; 9336 } 9337 9338 var a = DataTable.settings 9339 .filter( function (o) { 9340 return !visible || (visible && $(o.nTable).is(':visible')) 9341 ? true 9342 : false; 9343 } ) 9344 .map( function (o) { 9345 return o.nTable; 9346 }); 9347 9348 return api ? 9349 new _Api( a ) : 9350 a; 9351 }; 9352 9353 9354 /** 9355 * Convert from camel case parameters to Hungarian notation. This is made public 9356 * for the extensions to provide the same ability as DataTables core to accept 9357 * either the 1.9 style Hungarian notation, or the 1.10+ style camelCase 9358 * parameters. 9359 * 9360 * @param {object} src The model object which holds all parameters that can be 9361 * mapped. 9362 * @param {object} user The object to convert from camel case to Hungarian. 9363 * @param {boolean} force When set to `true`, properties which already have a 9364 * Hungarian value in the `user` object will be overwritten. Otherwise they 9365 * won't be. 9366 */ 9367 DataTable.camelToHungarian = _fnCamelToHungarian; 9368 9369 9370 9371 /** 9372 * 9373 */ 9374 _api_register( '$()', function ( selector, opts ) { 9375 var 9376 rows = this.rows( opts ).nodes(), // Get all rows 9377 jqRows = $(rows); 9378 9379 return $( [].concat( 9380 jqRows.filter( selector ).toArray(), 9381 jqRows.find( selector ).toArray() 9382 ) ); 9383 } ); 9384 9385 9386 // jQuery functions to operate on the tables 9387 $.each( [ 'on', 'one', 'off' ], function (i, key) { 9388 _api_register( key+'()', function ( /* event, handler */ ) { 9389 var args = Array.prototype.slice.call(arguments); 9390 9391 // Add the `dt` namespace automatically if it isn't already present 9392 args[0] = args[0].split( /\s/ ).map( function ( e ) { 9393 return ! e.match(/\.dt\b/) ? 9394 e+'.dt' : 9395 e; 9396 } ).join( ' ' ); 9397 9398 var inst = $( this.tables().nodes() ); 9399 inst[key].apply( inst, args ); 9400 return this; 9401 } ); 9402 } ); 9403 9404 9405 _api_register( 'clear()', function () { 9406 return this.iterator( 'table', function ( settings ) { 9407 _fnClearTable( settings ); 9408 } ); 9409 } ); 9410 9411 9412 _api_register( 'error()', function (msg) { 9413 return this.iterator( 'table', function ( settings ) { 9414 _fnLog( settings, 0, msg ); 9415 } ); 9416 } ); 9417 9418 9419 _api_register( 'settings()', function () { 9420 return new _Api( this.context, this.context ); 9421 } ); 9422 9423 9424 _api_register( 'init()', function () { 9425 var ctx = this.context; 9426 return ctx.length ? ctx[0].oInit : null; 9427 } ); 9428 9429 9430 _api_register( 'data()', function () { 9431 return this.iterator( 'table', function ( settings ) { 9432 return _pluck( settings.aoData, '_aData' ); 9433 } ).flatten(); 9434 } ); 9435 9436 9437 _api_register( 'trigger()', function ( name, args, bubbles ) { 9438 return this.iterator( 'table', function ( settings ) { 9439 return _fnCallbackFire( settings, null, name, args, bubbles ); 9440 } ).flatten(); 9441 } ); 9442 9443 9444 _api_register( 'ready()', function ( fn ) { 9445 var ctx = this.context; 9446 9447 // Get status of first table 9448 if (! fn) { 9449 return ctx.length 9450 ? (ctx[0]._bInitComplete || false) 9451 : null; 9452 } 9453 9454 // Function to run either once the table becomes ready or 9455 // immediately if it is already ready. 9456 return this.tables().every(function () { 9457 if (this.context[0]._bInitComplete) { 9458 fn.call(this); 9459 } 9460 else { 9461 this.on('init', function () { 9462 fn.call(this); 9463 }); 9464 } 9465 } ); 9466 } ); 9467 9468 9469 _api_register( 'destroy()', function ( remove ) { 9470 remove = remove || false; 9471 9472 return this.iterator( 'table', function ( settings ) { 9473 var classes = settings.oClasses; 9474 var table = settings.nTable; 9475 var tbody = settings.nTBody; 9476 var thead = settings.nTHead; 9477 var tfoot = settings.nTFoot; 9478 var jqTable = $(table); 9479 var jqTbody = $(tbody); 9480 var jqWrapper = $(settings.nTableWrapper); 9481 var rows = settings.aoData.map( function (r) { return r ? r.nTr : null; } ); 9482 var orderClasses = classes.order;
9483 9484 // Flag to note that the table is currently being destroyed - no action 9485 // should be taken 9486 settings.bDestroying = true; 9487 9488 // Fire off the destroy callbacks for plug-ins etc 9489 _fnCallbackFire( settings, "aoDestroyCallback", "destroy", [settings], true ); 9490 9491 // If not being removed from the document, make all columns visible 9492 if ( ! remove ) { 9493 new _Api( settings ).columns().visible( true ); 9494 } 9495 9496 // Blitz all `DT` namespaced events (these are internal events, the 9497 // lowercase, `dt` events are user subscribed and they are responsible 9498 // for removing them 9499 jqWrapper.off('.DT').find(':not(tbody *)').off('.DT'); 9500 $(window).off('.DT-'+settings.sInstance); 9501 9502 // When scrolling we had to break the table up - restore it 9503 if ( table != thead.parentNode ) { 9504 jqTable.children('thead').detach(); 9505 jqTable.append( thead ); 9506 } 9507 9508 if ( tfoot && table != tfoot.parentNode ) { 9509 jqTable.children('tfoot').detach(); 9510 jqTable.append( tfoot ); 9511 } 9512 9513 settings.colgroup.remove(); 9514 9515 settings.aaSorting = []; 9516 settings.aaSortingFixed = []; 9517 _fnSortingClasses( settings ); 9518 9519 $('th, td', thead) 9520 .removeClass( 9521 orderClasses.canAsc + ' ' + 9522 orderClasses.canDesc + ' ' + 9523 orderClasses.isAsc + ' ' + 9524 orderClasses.isDesc 9525 ) 9526 .css('width', ''); 9527 9528 // Add the TR elements back into the table in their original order 9529 jqTbody.children().detach(); 9530 jqTbody.append( rows ); 9531 9532 var orig = settings.nTableWrapper.parentNode; 9533 var insertBefore = settings.nTableWrapper.nextSibling; 9534 9535 // Remove the DataTables generated nodes, events and classes 9536 var removedMethod = remove ? 'remove' : 'detach'; 9537 jqTable[ removedMethod ](); 9538 jqWrapper[ removedMethod ](); 9539 9540 // If we need to reattach the table to the document 9541 if ( ! remove && orig ) { 9542 // insertBefore acts like appendChild if !arg[1] 9543 orig.insertBefore( table, insertBefore ); 9544 9545 // Restore the width of the original table - was read from the style property, 9546 // so we can restore directly to that 9547 jqTable 9548 .css( 'width', settings.sDestroyWidth ) 9549 .removeClass( classes.table ); 9550 } 9551 9552 /* Remove the settings object from the settings array */ 9553 var idx = DataTable.settings.indexOf(settings); 9554 if ( idx !== -1 ) { 9555 DataTable.settings.splice( idx, 1 ); 9556 } 9557 } ); 9558 } ); 9559 9560 9561 // Add the `every()` method for rows, columns and cells in a compact form 9562 $.each( [ 'column', 'row', 'cell' ], function ( i, type ) { 9563 _api_register( type+'s().every()', function ( fn ) { 9564 var opts = this.selector.opts; 9565 var api = this; 9566 var inst; 9567 var counter = 0; 9568 9569 return this.iterator( 'every', function ( settings, selectedIdx, tableIdx ) { 9570 inst = api[ type ](selectedIdx, opts); 9571 9572 if (type === 'cell') { 9573 fn.call(inst, inst[0][0].row, inst[0][0].column, tableIdx, counter); 9574 } 9575 else { 9576 fn.call(inst, selectedIdx, tableIdx, counter); 9577 } 9578 9579 counter++; 9580 } ); 9581 } ); 9582 } ); 9583 9584 9585 // i18n method for extensions to be able to use the language object from the 9586 // DataTable 9587 _api_register( 'i18n()', function ( token, def, plural ) { 9588 var ctx = this.context[0]; 9589 var resolved = _fnGetObjectDataFn( token )( ctx.oLanguage ); 9590 9591 if ( resolved === undefined ) { 9592 resolved = def; 9593 } 9594 9595 if ( $.isPlainObject( resolved ) ) { 9596 resolved = plural !== undefined && resolved[ plural ] !== undefined ? 9597 resolved[ plural ] : 9598 resolved._; 9599 } 9600 9601 return typeof resolved === 'string' 9602 ? resolved.replace( '%d', plural ) // nb: plural might be undefined, 9603 : resolved; 9604 } ); 9605 9606 /** 9607 * Version string for plug-ins to check compatibility. Allowed format is 9608 * `a.b.c-d` where: a:int, b:int, c:int, d:string(dev|beta|alpha). `d` is used 9609 * only for non-release builds. See https://semver.org/ for more information. 9610 * @member 9611 * @type string 9612 * @default Version number 9613 */ 9614 DataTable.version = "2.0.7"; 9615 9616 /** 9617 * Private data store, containing all of the settings objects that are 9618 * created for the tables on a given page. 9619 * 9620 * Note that the `DataTable.settings` object is aliased to 9621 * `jQuery.fn.dataTableExt` through which it may be accessed and 9622 * manipulated, or `jQuery.fn.dataTable.settings`. 9623 * @member 9624 * @type array 9625 * @default [] 9626 * @private 9627 */ 9628 DataTable.settings = []; 9629 9630 /** 9631 * Object models container, for the various models that DataTables has 9632 * available to it. These models define the objects that are used to hold 9633 * the active state and configuration of the table. 9634 * @namespace 9635 */ 9636 DataTable.models = {}; 9637 9638 9639 9640 /** 9641 * Template object for the way in which DataTables holds information about 9642 * search information for the global filter and individual column filters. 9643 * @namespace 9644 */ 9645 DataTable.models.oSearch = { 9646 /** 9647 * Flag to indicate if the filtering should be case insensitive or not 9648 */ 9649 "caseInsensitive": true, 9650 9651 /** 9652 * Applied search term 9653 */ 9654 "search": "", 9655 9656 /** 9657 * Flag to indicate if the search term should be interpreted as a 9658 * regular expression (true) or not (false) and therefore and special 9659 * regex characters escaped. 9660 */ 9661 "regex": false, 9662 9663 /** 9664 * Flag to indicate if DataTables is to use its smart filtering or not. 9665 */ 9666 "smart": true, 9667 9668 /** 9669 * Flag to indicate if DataTables should only trigger a search when 9670 * the return key is pressed. 9671 */ 9672 "return": false 9673 }; 9674 9675 9676 9677 9678 /** 9679 * Template object for the way in which DataTables holds information about 9680 * each individual row. This is the object format used for the settings 9681 * aoData array.
9682 * @namespace 9683 */ 9684 DataTable.models.oRow = { 9685 /** 9686 * TR element for the row 9687 */ 9688 "nTr": null, 9689 9690 /** 9691 * Array of TD elements for each row. This is null until the row has been 9692 * created. 9693 */ 9694 "anCells": null, 9695 9696 /** 9697 * Data object from the original data source for the row. This is either 9698 * an array if using the traditional form of DataTables, or an object if 9699 * using mData options. The exact type will depend on the passed in 9700 * data from the data source, or will be an array if using DOM a data 9701 * source. 9702 */ 9703 "_aData": [], 9704 9705 /** 9706 * Sorting data cache - this array is ostensibly the same length as the 9707 * number of columns (although each index is generated only as it is 9708 * needed), and holds the data that is used for sorting each column in the 9709 * row. We do this cache generation at the start of the sort in order that 9710 * the formatting of the sort data need be done only once for each cell 9711 * per sort. This array should not be read from or written to by anything 9712 * other than the master sorting methods. 9713 */ 9714 "_aSortData": null, 9715 9716 /** 9717 * Per cell filtering data cache. As per the sort data cache, used to 9718 * increase the performance of the filtering in DataTables 9719 */ 9720 "_aFilterData": null, 9721 9722 /** 9723 * Filtering data cache. This is the same as the cell filtering cache, but 9724 * in this case a string rather than an array. This is easily computed with 9725 * a join on `_aFilterData`, but is provided as a cache so the join isn't 9726 * needed on every search (memory traded for performance) 9727 */ 9728 "_sFilterRow": null, 9729 9730 /** 9731 * Denote if the original data source was from the DOM, or the data source 9732 * object. This is used for invalidating data, so DataTables can 9733 * automatically read data from the original source, unless uninstructed 9734 * otherwise. 9735 */ 9736 "src": null, 9737 9738 /** 9739 * Index in the aoData array. This saves an indexOf lookup when we have the 9740 * object, but want to know the index 9741 */ 9742 "idx": -1, 9743 9744 /** 9745 * Cached display value 9746 */ 9747 displayData: null 9748 }; 9749 9750 9751 /** 9752 * Template object for the column information object in DataTables. This object 9753 * is held in the settings aoColumns array and contains all the information that 9754 * DataTables needs about each individual column. 9755 * 9756 * Note that this object is related to {@link DataTable.defaults.column} 9757 * but this one is the internal data store for DataTables's cache of columns. 9758 * It should NOT be manipulated outside of DataTables. Any configuration should 9759 * be done through the initialisation options. 9760 * @namespace 9761 */ 9762 DataTable.models.oColumn = { 9763 /** 9764 * Column index. 9765 */ 9766 "idx": null, 9767 9768 /** 9769 * A list of the columns that sorting should occur on when this column 9770 * is sorted. That this property is an array allows multi-column sorting 9771 * to be defined for a column (for example first name / last name columns 9772 * would benefit from this). The values are integers pointing to the 9773 * columns to be sorted on (typically it will be a single integer pointing 9774 * at itself, but that doesn't need to be the case). 9775 */ 9776 "aDataSort": null, 9777 9778 /** 9779 * Define the sorting directions that are applied to the column, in sequence 9780 * as the column is repeatedly sorted upon - i.e. the first value is used 9781 * as the sorting direction when the column if first sorted (clicked on). 9782 * Sort it again (click again) and it will move on to the next index. 9783 * Repeat until loop. 9784 */ 9785 "asSorting": null, 9786 9787 /** 9788 * Flag to indicate if the column is searchable, and thus should be included 9789 * in the filtering or not. 9790 */ 9791 "bSearchable": null, 9792 9793 /** 9794 * Flag to indicate if the column is sortable or not. 9795 */ 9796 "bSortable": null, 9797 9798 /** 9799 * Flag to indicate if the column is currently visible in the table or not 9800 */ 9801 "bVisible": null, 9802 9803 /** 9804 * Store for manual type assignment using the `column.type` option. This 9805 * is held in store so we can manipulate the column's `sType` property. 9806 */ 9807 "_sManualType": null, 9808 9809 /** 9810 * Flag to indicate if HTML5 data attributes should be used as the data 9811 * source for filtering or sorting. True is either are. 9812 */ 9813 "_bAttrSrc": false, 9814 9815 /** 9816 * Developer definable function that is called whenever a cell is created (Ajax source, 9817 * etc) or processed for input (DOM source). This can be used as a compliment to mRender 9818 * allowing you to modify the DOM element (add background colour for example) when the 9819 * element is available. 9820 */ 9821 "fnCreatedCell": null, 9822 9823 /** 9824 * Function to get data from a cell in a column. You should <b>never</b> 9825 * access data directly through _aData internally in DataTables - always use 9826 * the method attached to this property. It allows mData to function as 9827 * required. This function is automatically assigned by the column 9828 * initialisation method 9829 */ 9830 "fnGetData": null, 9831 9832 /** 9833 * Function to set data for a cell in the column. You should <b>never</b> 9834 * set the data directly to _aData internally in DataTables - always use 9835 * this method. It allows mData to function as required. This function 9836 * is automatically assigned by the column initialisation method 9837 */ 9838 "fnSetData": null, 9839 9840 /** 9841 * Property to read the value for the cells in the column from the data 9842 * source array / object. If null, then the default content is used, if a 9843 * function is given then the return from the function is used. 9844 */ 9845 "mData": null, 9846 9847 /** 9848 * Partner property to mData which is used (only when defined) to get 9849 * the data - i.e. it is basically the same as mData, but without the 9850 * 'set' option, and also the data fed to it is the result from mData. 9851 * This is the rendering method to match the data method of mData. 9852 */
9853 "mRender": null, 9854 9855 /** 9856 * The class to apply to all TD elements in the table's TBODY for the column 9857 */ 9858 "sClass": null, 9859 9860 /** 9861 * When DataTables calculates the column widths to assign to each column, 9862 * it finds the longest string in each column and then constructs a 9863 * temporary table and reads the widths from that. The problem with this 9864 * is that "mmm" is much wider then "iiii", but the latter is a longer 9865 * string - thus the calculation can go wrong (doing it properly and putting 9866 * it into an DOM object and measuring that is horribly(!) slow). Thus as 9867 * a "work around" we provide this option. It will append its value to the 9868 * text that is found to be the longest string for the column - i.e. padding. 9869 */ 9870 "sContentPadding": null, 9871 9872 /** 9873 * Allows a default value to be given for a column's data, and will be used 9874 * whenever a null data source is encountered (this can be because mData 9875 * is set to null, or because the data source itself is null). 9876 */ 9877 "sDefaultContent": null, 9878 9879 /** 9880 * Name for the column, allowing reference to the column by name as well as 9881 * by index (needs a lookup to work by name). 9882 */ 9883 "sName": null, 9884 9885 /** 9886 * Custom sorting data type - defines which of the available plug-ins in 9887 * afnSortData the custom sorting will use - if any is defined. 9888 */ 9889 "sSortDataType": 'std', 9890 9891 /** 9892 * Class to be applied to the header element when sorting on this column 9893 */ 9894 "sSortingClass": null, 9895 9896 /** 9897 * Title of the column - what is seen in the TH element (nTh). 9898 */ 9899 "sTitle": null, 9900 9901 /** 9902 * Column sorting and filtering type 9903 */ 9904 "sType": null, 9905 9906 /** 9907 * Width of the column 9908 */ 9909 "sWidth": null, 9910 9911 /** 9912 * Width of the column when it was first "encountered" 9913 */ 9914 "sWidthOrig": null, 9915 9916 /** Cached string which is the longest in the column */ 9917 maxLenString: null, 9918 9919 /** 9920 * Store for named searches 9921 */ 9922 searchFixed: null 9923 }; 9924 9925 9926 /* 9927 * Developer note: The properties of the object below are given in Hungarian 9928 * notation, that was used as the interface for DataTables prior to v1.10, however 9929 * from v1.10 onwards the primary interface is camel case. In order to avoid 9930 * breaking backwards compatibility utterly with this change, the Hungarian 9931 * version is still, internally the primary interface, but is is not documented 9932 * - hence the @name tags in each doc comment. This allows a Javascript function 9933 * to create a map from Hungarian notation to camel case (going the other direction 9934 * would require each property to be listed, which would add around 3K to the size 9935 * of DataTables, while this method is about a 0.5K hit). 9936 * 9937 * Ultimately this does pave the way for Hungarian notation to be dropped 9938 * completely, but that is a massive amount of work and will break current 9939 * installs (therefore is on-hold until v2). 9940 */ 9941 9942 /** 9943 * Initialisation options that can be given to DataTables at initialisation 9944 * time. 9945 * @namespace 9946 */ 9947 DataTable.defaults = { 9948 /** 9949 * An array of data to use for the table, passed in at initialisation which 9950 * will be used in preference to any data which is already in the DOM. This is 9951 * particularly useful for constructing tables purely in Javascript, for 9952 * example with a custom Ajax call. 9953 */ 9954 "aaData": null, 9955 9956 9957 /** 9958 * If ordering is enabled, then DataTables will perform a first pass sort on 9959 * initialisation. You can define which column(s) the sort is performed 9960 * upon, and the sorting direction, with this variable. The `sorting` array 9961 * should contain an array for each column to be sorted initially containing 9962 * the column's index and a direction string ('asc' or 'desc'). 9963 */ 9964 "aaSorting": [[0,'asc']], 9965 9966 9967 /** 9968 * This parameter is basically identical to the `sorting` parameter, but 9969 * cannot be overridden by user interaction with the table. What this means 9970 * is that you could have a column (visible or hidden) which the sorting 9971 * will always be forced on first - any sorting after that (from the user) 9972 * will then be performed as required. This can be useful for grouping rows 9973 * together. 9974 */ 9975 "aaSortingFixed": [], 9976 9977 9978 /** 9979 * DataTables can be instructed to load data to display in the table from a 9980 * Ajax source. This option defines how that Ajax call is made and where to. 9981 * 9982 * The `ajax` property has three different modes of operation, depending on 9983 * how it is defined. These are: 9984 * 9985 * * `string` - Set the URL from where the data should be loaded from. 9986 * * `object` - Define properties for `jQuery.ajax`. 9987 * * `function` - Custom data get function 9988 * 9989 * `string` 9990 * -------- 9991 * 9992 * As a string, the `ajax` property simply defines the URL from which 9993 * DataTables will load data. 9994 * 9995 * `object` 9996 * -------- 9997 * 9998 * As an object, the parameters in the object are passed to 9999 * [jQuery.ajax](https://api.jquery.com/jQuery.ajax/) allowing fine control
10000 * of the Ajax request. DataTables has a number of default parameters which 10001 * you can override using this option. Please refer to the jQuery 10002 * documentation for a full description of the options available, although 10003 * the following parameters provide additional options in DataTables or 10004 * require special consideration: 10005 * 10006 * * `data` - As with jQuery, `data` can be provided as an object, but it 10007 * can also be used as a function to manipulate the data DataTables sends 10008 * to the server. The function takes a single parameter, an object of 10009 * parameters with the values that DataTables has readied for sending. An 10010 * object may be returned which will be merged into the DataTables 10011 * defaults, or you can add the items to the object that was passed in and 10012 * not return anything from the function. This supersedes `fnServerParams` 10013 * from DataTables 1.9-. 10014 * 10015 * * `dataSrc` - By default DataTables will look for the property `data` (or 10016 * `aaData` for compatibility with DataTables 1.9-) when obtaining data 10017 * from an Ajax source or for server-side processing - this parameter 10018 * allows that property to be changed. You can use Javascript dotted 10019 * object notation to get a data source for multiple levels of nesting, or 10020 * it my be used as a function. As a function it takes a single parameter, 10021 * the JSON returned from the server, which can be manipulated as 10022 * required, with the returned value being that used by DataTables as the 10023 * data source for the table. 10024 * 10025 * * `success` - Should not be overridden it is used internally in 10026 * DataTables. To manipulate / transform the data returned by the server 10027 * use `ajax.dataSrc`, or use `ajax` as a function (see below). 10028 * 10029 * `function` 10030 * ---------- 10031 * 10032 * As a function, making the Ajax call is left up to yourself allowing 10033 * complete control of the Ajax request. Indeed, if desired, a method other 10034 * than Ajax could be used to obtain the required data, such as Web storage 10035 * or an AIR database. 10036 * 10037 * The function is given four parameters and no return is required. The 10038 * parameters are: 10039 * 10040 * 1. _object_ - Data to send to the server 10041 * 2. _function_ - Callback function that must be executed when the required 10042 * data has been obtained. That data should be passed into the callback 10043 * as the only parameter 10044 * 3. _object_ - DataTables settings object for the table 10045 */ 10046 "ajax": null, 10047 10048 10049 /** 10050 * This parameter allows you to readily specify the entries in the length drop 10051 * down menu that DataTables shows when pagination is enabled. It can be 10052 * either a 1D array of options which will be used for both the displayed 10053 * option and the value, or a 2D array which will use the array in the first 10054 * position as the value, and the array in the second position as the 10055 * displayed options (useful for language strings such as 'All'). 10056 * 10057 * Note that the `pageLength` property will be automatically set to the 10058 * first value given in this array, unless `pageLength` is also provided. 10059 */ 10060 "aLengthMenu": [ 10, 25, 50, 100 ], 10061 10062 10063 /** 10064 * The `columns` option in the initialisation parameter allows you to define 10065 * details about the way individual columns behave. For a full list of 10066 * column options that can be set, please see 10067 * {@link DataTable.defaults.column}. Note that if you use `columns` to 10068 * define your columns, you must have an entry in the array for every single 10069 * column that you have in your table (these can be null if you don't which 10070 * to specify any options). 10071 */ 10072 "aoColumns": null, 10073 10074 /** 10075 * Very similar to `columns`, `columnDefs` allows you to target a specific 10076 * column, multiple columns, or all columns, using the `targets` property of 10077 * each object in the array. This allows great flexibility when creating 10078 * tables, as the `columnDefs` arrays can be of any length, targeting the 10079 * columns you specifically want. `columnDefs` may use any of the column 10080 * options available: {@link DataTable.defaults.column}, but it _must_ 10081 * have `targets` defined in each object in the array. Values in the `targets` 10082 * array may be: 10083 * <ul> 10084 * <li>
10084a string - class name will be matched on the TH for the column</li> 10085 * <li>0 or a positive integer - column index counting from the left</li> 10086 * <li>a negative integer - column index counting from the right</li> 10087 * <li>the string "_all" - all columns (i.e. assign a default)</li> 10088 * </ul> 10089 */ 10090 "aoColumnDefs": null, 10091 10092 10093 /** 10094 * Basically the same as `search`, this parameter defines the individual column 10095 * filtering state at initialisation time. The array must be of the same size 10096 * as the number of columns, and each element be an object with the parameters 10097 * `search` and `escapeRegex` (the latter is optional). 'null' is also 10098 * accepted and the default will be used. 10099 */ 10100 "aoSearchCols": [], 10101 10102 10103 /** 10104 * Enable or disable automatic column width calculation. This can be disabled 10105 * as an optimisation (it takes some time to calculate the widths) if the 10106 * tables widths are passed in using `columns`. 10107 */ 10108 "bAutoWidth": true, 10109 10110 10111 /** 10112 * Deferred rendering can provide DataTables with a huge speed boost when you 10113 * are using an Ajax or JS data source for the table. This option, when set to 10114 * true, will cause DataTables to defer the creation of the table elements for 10115 * each row until they are needed for a draw - saving a significant amount of 10116 * time. 10117 */ 10118 "bDeferRender": true, 10119 10120 10121 /** 10122 * Replace a DataTable which matches the given selector and replace it with 10123 * one which has the properties of the new initialisation object passed. If no 10124 * table matches the selector, then the new DataTable will be constructed as 10125 * per normal. 10126 */ 10127 "bDestroy": false, 10128 10129 10130 /** 10131 * Enable or disable filtering of data. Filtering in DataTables is "smart" in 10132 * that it allows the end user to input multiple words (space separated) and 10133 * will match a row containing those words, even if not in the order that was 10134 * specified (this allow matching across multiple columns). Note that if you 10135 * wish to use filtering in DataTables this must remain 'true' - to remove the 10136 * default filtering input box and retain filtering abilities, please use 10137 * {@link DataTable.defaults.dom}. 10138 */ 10139 "bFilter": true, 10140 10141 /** 10142 * Used only for compatiblity with DT1 10143 * @deprecated 10144 */ 10145 "bInfo": true, 10146 10147 /** 10148 * Used only for compatiblity with DT1 10149 * @deprecated 10150 */ 10151 "bLengthChange": true, 10152 10153 /** 10154 * Enable or disable pagination. 10155 */ 10156 "bPaginate": true, 10157 10158 10159 /** 10160 * Enable or disable the display of a 'processing' indicator when the table is 10161 * being processed (e.g. a sort). This is particularly useful for tables with 10162 * large amounts of data where it can take a noticeable amount of time to sort 10163 * the entries. 10164 */ 10165 "bProcessing": false, 10166 10167 10168 /** 10169 * Retrieve the DataTables object for the given selector. Note that if the 10170 * table has already been initialised, this parameter will cause DataTables 10171 * to simply return the object that has already been set up - it will not take 10172 * account of any changes you might have made to the initialisation object 10173 * passed to DataTables (setting this parameter to true is an acknowledgement 10174 * that you understand this). `destroy` can be used to reinitialise a table if 10175 * you need. 10176 */ 10177 "bRetrieve": false, 10178 10179 10180 /** 10181 * When vertical (y) scrolling is enabled, DataTables will force the height of 10182 * the table's viewport to the given height at all times (useful for layout). 10183 * However, this can look odd when filtering data down to a small data set, 10184 * and the footer is left "floating" further down. This parameter (when 10185 * enabled) will cause DataTables to collapse the table's viewport down when 10186 * the result set will fit within the given Y height. 10187 */ 10188 "bScrollCollapse": false, 10189 10190 10191 /** 10192 * Configure DataTables to use server-side processing. Note that the 10193 * `ajax` parameter must also be given in order to give DataTables a 10194 * source to obtain the required data for each draw. 10195 */ 10196 "bServerSide": false, 10197 10198 10199 /** 10200 * Enable or disable sorting of columns. Sorting of individual columns can be 10201 * disabled by the `sortable` option for each column. 10202 */ 10203 "bSort": true, 10204 10205 10206 /** 10207 * Enable or display DataTables' ability to sort multiple columns at the 10208 * same time (activated by shift-click by the user). 10209 */ 10210 "bSortMulti": true, 10211 10212 10213 /** 10214 * Allows control over whether DataTables should use the top (true) unique 10215 * cell that is found for a single column, or the bottom (false - default). 10216 * This is useful when using complex headers. 10217 */ 10218 "bSortCellsTop": null, 10219 10220 10221 /** 10222 * Enable or disable the addition of the classes `sorting\_1`, `sorting\_2` and 10223 * `sorting\_3` to the columns which are currently being sorted on. This is 10224 * presented as a feature switch as it can increase processing time (while 10225 * classes are removed and added) so for large data sets you might want to 10226 * turn this off. 10227 */ 10228 "bSortClasses": true, 10229 10230 10231 /** 10232 * Enable or disable state saving. When enabled HTML5 `localStorage` will be 10233 * used to save table display information such as pagination information, 10234 * display length, filtering and sorting. As such when the end user reloads 10235 * the page the display display will match what thy had previously set up. 10236 */ 10237 "bStateSave": false, 10238 10239 10240 /** 10241 * This function is called when a TR element is created (and all TD child 10242 * elements have been inserted), or registered if using a DOM source, allowing 10243 * manipulation of the TR element (adding classes etc). 10244 */ 10245 "fnCreatedRow": null, 10246 10247 10248 /** 10249 * This function is called on every 'draw' event, and allows you to 10250 * dynamically modify any aspect you want about the created DOM. 10251 */ 10252 "fnDrawCallback": null, 10253 10254 10255 /** 10256 * Identical to fnHeaderCallback() but for the table footer this function 10257 * allows you to modify the table footer on every 'draw' event. 10258 */ 10259 "fnFooterCallback": null, 10260 10261 10262 /** 10263 * When rendering large numbers in the information element for the table 10264 * (i.e. "Showing 1 to 10 of 57 entries") DataTables will render large numbers 10265 * to have a comma separator for the 'thousands' units (e.g. 1 million is 10266 * rendered as "1,000,000") to help readability for the end user. This 10267 * function will override the default method DataTables uses. 10268 */ 10269 "fnFormatNumber": function ( toFormat ) { 10270 return toFormat.toString().replace( 10271 /\B(?=(\d{3})+(?!\d))/g, 10272 this.oLanguage.sThousands 10273 ); 10274 }, 10275 10276 10277 /** 10278 * This function is called on every 'draw' event, and allows you to 10279 * dynamically modify the header row. This can be used to calculate and 10280 * display useful information about the table. 10281 */ 10282 "fnHeaderCallback": null, 10283 10284 10285 /** 10286 * The information element can be used to convey information about the current 10287 * state of the table. Although the internationalisation options presented by 10288 * DataTables are quite capable of dealing with most customisations, there may 10289 * be times where you wish to customise the string further. This callback 10290 * allows you to do exactly that. 10291 */ 10292 "fnInfoCallback": null, 10293 10294 10295 /** 10296 * Called when the table has been initialised. Normally DataTables will 10297 * initialise sequentially and there will be no need for this function, 10298 * however, this does not hold true when using external language information 10299 * since that is obtained using an async XHR call. 10300 */ 10301 "fnInitComplete": null, 10302 10303 10304 /** 10305 * Called at the very start of each table draw and can be used to cancel the 10306 * draw by returning false, any other return (including undefined) results in 10307 * the full draw occurring). 10308 */ 10309 "fnPreDrawCallback": null, 10310 10311 10312 /** 10313 * This function allows you to 'post process' each row after it have been 10314 * generated for each table draw, but before it is rendered on screen. This 10315 * function might be used for setting the row class name etc. 10316 */ 10317 "fnRowCallback": null, 10318 10319 10320 /** 10321 * Load the table state. With this function you can define from where, and how, the 10322 * state of a table is loaded. By default DataTables will load from `localStorage` 10323 * but you might wish to use a server-side database or cookies. 10324 */ 10325 "fnStateLoadCallback": function ( settings ) { 10326 try { 10327 return JSON.parse(
vendor: 6,254 bytes, lines 10328-10523
10328 (settings.iStateDuration === -1 ? sessionStorage : localStorage).getItem( 10329 'DataTables_'+settings.sInstance+'_'+location.pathname 10330 ) 10331 ); 10332 } catch (e) { 10333 return {}; 10334 } 10335 }, 10336 10337 10338 /** 10339 * Callback which allows modification of the saved state prior to loading that state. 10340 * This callback is called when the table is loading state from the stored data, but 10341 * prior to the settings object being modified by the saved state. Note that for 10342 * plug-in authors, you should use the `stateLoadParams` event to load parameters for 10343 * a plug-in. 10344 */ 10345 "fnStateLoadParams": null, 10346 10347 10348 /** 10349 * Callback that is called when the state has been loaded from the state saving method 10350 * and the DataTables settings object has been modified as a result of the loaded state. 10351 */ 10352 "fnStateLoaded": null, 10353 10354 10355 /** 10356 * Save the table state. This function allows you to define where and how the state 10357 * information for the table is stored By default DataTables will use `localStorage` 10358 * but you might wish to use a server-side database or cookies. 10359 */ 10360 "fnStateSaveCallback": function ( settings, data ) { 10361 try { 10362 (settings.iStateDuration === -1 ? sessionStorage : localStorage).setItem( 10363 'DataTables_'+settings.sInstance+'_'+location.pathname, 10364 JSON.stringify( data ) 10365 ); 10366 } catch (e) { 10367 // noop 10368 } 10369 }, 10370 10371 10372 /** 10373 * Callback which allows modification of the state to be saved. Called when the table 10374 * has changed state a new state save is required. This method allows modification of 10375 * the state saving object prior to actually doing the save, including addition or 10376 * other state properties or modification. Note that for plug-in authors, you should 10377 * use the `stateSaveParams` event to save parameters for a plug-in. 10378 */ 10379 "fnStateSaveParams": null, 10380 10381 10382 /** 10383 * Duration for which the saved state information is considered valid. After this period 10384 * has elapsed the state will be returned to the default. 10385 * Value is given in seconds. 10386 */ 10387 "iStateDuration": 7200, 10388 10389 10390 /** 10391 * Number of rows to display on a single page when using pagination. If 10392 * feature enabled (`lengthChange`) then the end user will be able to override 10393 * this to a custom setting using a pop-up menu. 10394 */ 10395 "iDisplayLength": 10, 10396 10397 10398 /** 10399 * Define the starting point for data display when using DataTables with 10400 * pagination. Note that this parameter is the number of records, rather than 10401 * the page number, so if you have 10 records per page and want to start on 10402 * the third page, it should be "20". 10403 */ 10404 "iDisplayStart": 0, 10405 10406 10407 /** 10408 * By default DataTables allows keyboard navigation of the table (sorting, paging, 10409 * and filtering) by adding a `tabindex` attribute to the required elements. This 10410 * allows you to tab through the controls and press the enter key to activate them. 10411 * The tabindex is default 0, meaning that the tab follows the flow of the document. 10412 * You can overrule this using this parameter if you wish. Use a value of -1 to 10413 * disable built-in keyboard navigation. 10414 */ 10415 "iTabIndex": 0, 10416 10417 10418 /** 10419 * Classes that DataTables assigns to the various components and features 10420 * that it adds to the HTML table. This allows classes to be configured 10421 * during initialisation in addition to through the static 10422 * {@link DataTable.ext.oStdClasses} object). 10423 */ 10424 "oClasses": {}, 10425 10426 10427 /** 10428 * All strings that DataTables uses in the user interface that it creates 10429 * are defined in this object, allowing you to modified them individually or 10430 * completely replace them all as required. 10431 */ 10432 "oLanguage": { 10433 /** 10434 * Strings that are used for WAI-ARIA labels and controls only (these are not 10435 * actually visible on the page, but will be read by screenreaders, and thus 10436 * must be internationalised as well). 10437 */ 10438 "oAria": { 10439 /** 10440 * ARIA label that is added to the table headers when the column may be sorted 10441 */ 10442 "orderable": ": Activate to sort", 10443 10444 /** 10445 * ARIA label that is added to the table headers when the column is currently being sorted 10446 */ 10447 "orderableReverse": ": Activate to invert sorting", 10448 10449 /** 10450 * ARIA label that is added to the table headers when the column is currently being 10451 * sorted and next step is to remove sorting 10452 */ 10453 "orderableRemove": ": Activate to remove sorting", 10454 10455 paginate: { 10456 first: 'First', 10457 last: 'Last', 10458 next: 'Next', 10459 previous: 'Previous' 10460 } 10461 }, 10462 10463 /** 10464 * Pagination string used by DataTables for the built-in pagination 10465 * control types. 10466 */ 10467 "oPaginate": { 10468 /** 10469 * Label and character for first page button («) 10470 */ 10471 "sFirst": "\u00AB", 10472 10473 /** 10474 * Last page button (») 10475 */ 10476 "sLast": "\u00BB", 10477 10478 /** 10479 * Next page button (âº) 10480 */ 10481 "sNext": "\u203A", 10482 10483 /** 10484 * Previous page button (â¹) 10485 */ 10486 "sPrevious": "\u2039", 10487 }, 10488 10489 /** 10490 * Plural object for the data type the table is showing 10491 */ 10492 entries: { 10493 _: "entries", 10494 1: "entry" 10495 }, 10496 10497 /** 10498 * This string is shown in preference to `zeroRecords` when the table is 10499 * empty of data (regardless of filtering). Note that this is an optional 10500 * parameter - if it is not given, the value of `zeroRecords` will be used 10501 * instead (either the default or given value). 10502 */ 10503 "sEmptyTable": "No data available in table", 10504 10505 10506 /** 10507 * This string gives information to the end user about the information 10508 * that is current on display on the page. The following tokens can be 10509 * used in the string and will be dynamically replaced as the table 10510 * display updates. This tokens can be placed anywhere in the string, or 10511 * removed as needed by the language requires: 10512 * 10513 * * `\_START\_` - Display index of the first record on the current page 10514 * * `\_END\_` - Display index of the last record on the current page 10515 * * `\_TOTAL\_` - Number of records in the table after filtering 10516 * * `\_MAX\_` - Number of records in the table without filtering 10517 * * `\_PAGE\_` - Current page number 10518 * * `\_PAGES\_` - Total number of pages of data in the table 10519 */ 10520 "sInfo": "Showing _START_ to _END_ of _TOTAL_ _ENTRIES-TOTAL_", 10521 10522 10523 /**
10524 * Display information string for when the table is empty. Typically the 10525 * format of this string should match `info`. 10526 */ 10527 "sInfoEmpty": "Showing 0 to 0 of 0 _ENTRIES-TOTAL_", 10528 10529 10530 /** 10531 * When a user filters the information in a table, this string is appended 10532 * to the information (`info`) to give an idea of how strong the filtering 10533 * is. The variable _MAX_ is dynamically updated. 10534 */ 10535 "sInfoFiltered": "(filtered from _MAX_ total _ENTRIES-MAX_)", 10536 10537 10538 /** 10539 * If can be useful to append extra information to the info string at times, 10540 * and this variable does exactly that. This information will be appended to 10541 * the `info` (`infoEmpty` and `infoFiltered` in whatever combination they are 10542 * being used) at all times. 10543 */ 10544 "sInfoPostFix": "", 10545 10546 10547 /** 10548 * This decimal place operator is a little different from the other 10549 * language options since DataTables doesn't output floating point 10550 * numbers, so it won't ever use this for display of a number. Rather, 10551 * what this parameter does is modify the sort methods of the table so 10552 * that numbers which are in a format which has a character other than 10553 * a period (`.`) as a decimal place will be sorted numerically. 10554 * 10555 * Note that numbers with different decimal places cannot be shown in 10556 * the same table and still be sortable, the table must be consistent. 10557 * However, multiple different tables on the page can use different 10558 * decimal place characters. 10559 */ 10560 "sDecimal": "", 10561 10562 10563 /** 10564 * DataTables has a build in number formatter (`formatNumber`) which is 10565 * used to format large numbers that are used in the table information. 10566 * By default a comma is used, but this can be trivially changed to any 10567 * character you wish with this parameter. 10568 */ 10569 "sThousands": ",", 10570 10571 10572 /** 10573 * Detail the action that will be taken when the drop down menu for the 10574 * pagination length option is changed. The '_MENU_' variable is replaced 10575 * with a default select list of 10, 25, 50 and 100, and can be replaced 10576 * with a custom select box if required. 10577 */ 10578 "sLengthMenu": "_MENU_ _ENTRIES_ per page", 10579 10580 10581 /** 10582 * When using Ajax sourced data and during the first draw when DataTables is 10583 * gathering the data, this message is shown in an empty row in the table to 10584 * indicate to the end user the the data is being loaded. Note that this 10585 * parameter is not used when loading data by server-side processing, just 10586 * Ajax sourced data with client-side processing. 10587 */ 10588 "sLoadingRecords": "Loading...", 10589 10590 10591 /** 10592 * Text which is displayed when the table is processing a user action 10593 * (usually a sort command or similar). 10594 */ 10595 "sProcessing": "", 10596 10597 10598 /** 10599 * Details the actions that will be taken when the user types into the 10600 * filtering input text box. The variable "_INPUT_", if used in the string, 10601 * is replaced with the HTML text box for the filtering input allowing 10602 * control over where it appears in the string. If "_INPUT_" is not given 10603 * then the input box is appended to the string automatically. 10604 */ 10605 "sSearch": "Search:", 10606 10607 10608 /** 10609 * Assign a `placeholder` attribute to the search `input` element 10610 * @type string 10611 * @default 10612 * 10613 * @dtopt Language 10614 * @name DataTable.defaults.language.searchPlaceholder 10615 */ 10616 "sSearchPlaceholder": "", 10617 10618 10619 /** 10620 * All of the language information can be stored in a file on the 10621 * server-side, which DataTables will look up if this parameter is passed. 10622 * It must store the URL of the language file, which is in a JSON format, 10623 * and the object has the same properties as the oLanguage object in the 10624 * initialiser object (i.e. the above parameters). Please refer to one of 10625 * the example language files to see how this works in action. 10626 */ 10627 "sUrl": "", 10628 10629 10630 /** 10631 * Text shown inside the table records when the is no information to be 10632 * displayed after filtering. `emptyTable` is shown when there is simply no 10633 * information in the table at all (regardless of filtering). 10634 */
10635 "sZeroRecords": "No matching records found" 10636 }, 10637 10638 10639 /** 10640 * This parameter allows you to have define the global filtering state at 10641 * initialisation time. As an object the `search` parameter must be 10642 * defined, but all other parameters are optional. When `regex` is true, 10643 * the search string will be treated as a regular expression, when false 10644 * (default) it will be treated as a straight string. When `smart` 10645 * DataTables will use it's smart filtering methods (to word match at 10646 * any point in the data), when false this will not be done. 10647 */ 10648 "oSearch": $.extend( {}, DataTable.models.oSearch ), 10649 10650 10651 /** 10652 * Table and control layout. This replaces the legacy `dom` option. 10653 */ 10654 layout: { 10655 topStart: 'pageLength', 10656 topEnd: 'search', 10657 bottomStart: 'info', 10658 bottomEnd: 'paging' 10659 }, 10660 10661 10662 /** 10663 * Legacy DOM layout option 10664 */ 10665 "sDom": null, 10666 10667 10668 /** 10669 * Search delay option. This will throttle full table searches that use the 10670 * DataTables provided search input element (it does not effect calls to 10671 * `dt-api search()`, providing a delay before the search is made. 10672 */ 10673 "searchDelay": null, 10674 10675 10676 /** 10677 * DataTables features six different built-in options for the buttons to 10678 * display for pagination control: 10679 * 10680 * * `numbers` - Page number buttons only 10681 * * `simple` - 'Previous' and 'Next' buttons only 10682 * * 'simple_numbers` - 'Previous' and 'Next' buttons, plus page numbers 10683 * * `full` - 'First', 'Previous', 'Next' and 'Last' buttons 10684 * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus page numbers 10685 * * `first_last_numbers` - 'First' and 'Last' buttons, plus page numbers 10686 */ 10687 "sPaginationType": "full_numbers", 10688 10689 10690 /** 10691 * Enable horizontal scrolling. When a table is too wide to fit into a 10692 * certain layout, or you have a large number of columns in the table, you 10693 * can enable x-scrolling to show the table in a viewport, which can be 10694 * scrolled. This property can be `true` which will allow the table to 10695 * scroll horizontally when needed, or any CSS unit, or a number (in which 10696 * case it will be treated as a pixel measurement). Setting as simply `true` 10697 * is recommended. 10698 */ 10699 "sScrollX": "", 10700 10701 10702 /** 10703 * This property can be used to force a DataTable to use more width than it 10704 * might otherwise do when x-scrolling is enabled. For example if you have a 10705 * table which requires to be well spaced, this parameter is useful for 10706 * "over-sizing" the table, and thus forcing scrolling. This property can by 10707 * any CSS unit, or a number (in which case it will be treated as a pixel 10708 * measurement). 10709 */ 10710 "sScrollXInner": "", 10711 10712 10713 /** 10714 * Enable vertical scrolling. Vertical scrolling will constrain the DataTable 10715 * to the given height, and enable scrolling for any data which overflows the 10716 * current viewport. This can be used as an alternative to paging to display 10717 * a lot of data in a small area (although paging and scrolling can both be 10718 * enabled at the same time). This property can be any CSS unit, or a number 10719 * (in which case it will be treated as a pixel measurement). 10720 */ 10721 "sScrollY": "", 10722 10723 10724 /** 10725 * __Deprecated__ The functionality provided by this parameter has now been 10726 * superseded by that provided through `ajax`, which should be used instead. 10727 * 10728 * Set the HTTP method that is used to make the Ajax call for server-side 10729 * processing or Ajax sourced data. 10730 */ 10731 "sServerMethod": "GET", 10732 10733 10734 /** 10735 * DataTables makes use of renderers when displaying HTML elements for 10736 * a table. These renderers can be added or modified by plug-ins to 10737 * generate suitable mark-up for a site. For example the Bootstrap 10738 * integration plug-in for DataTables uses a paging button renderer to 10739 * display pagination buttons in the mark-up required by Bootstrap. 10740 * 10741 * For further information about the renderers available see 10742 * DataTable.ext.renderer 10743 */ 10744 "renderer": null, 10745 10746 10747 /** 10748 * Set the data property name that DataTables should use to get a row's id 10749 * to set as the `id` property in the node. 10750 */ 10751 "rowId": "DT_RowId", 10752 10753 10754 /** 10755 * Caption value 10756 */ 10757 "caption": null 10758 }; 10759 10760 _fnHungarianMap( DataTable.defaults ); 10761 10762 10763 10764 /* 10765 * Developer note - See note in model.defaults.js about the use of Hungarian 10766 * notation and camel case. 10767 */ 10768 10769 /** 10770 * Column options that can be given to DataTables at initialisation time. 10771 * @namespace 10772 */ 10773 DataTable.defaults.column = { 10774 /** 10775 * Define which column(s) an order will occur on for this column. This 10776 * allows a column's ordering to take multiple columns into account when
10777 * doing a sort or use the data from a different column. For example first 10778 * name / last name columns make sense to do a multi-column sort over the 10779 * two columns. 10780 */ 10781 "aDataSort": null, 10782 "iDataSort": -1, 10783 10784 ariaTitle: '', 10785 10786 10787 /** 10788 * You can control the default ordering direction, and even alter the 10789 * behaviour of the sort handler (i.e. only allow ascending ordering etc) 10790 * using this parameter. 10791 */ 10792 "asSorting": [ 'asc', 'desc', '' ], 10793 10794 10795 /** 10796 * Enable or disable filtering on the data in this column. 10797 */ 10798 "bSearchable": true, 10799 10800 10801 /** 10802 * Enable or disable ordering on this column. 10803 */ 10804 "bSortable": true, 10805 10806 10807 /** 10808 * Enable or disable the display of this column. 10809 */ 10810 "bVisible": true, 10811 10812 10813 /** 10814 * Developer definable function that is called whenever a cell is created (Ajax source, 10815 * etc) or processed for input (DOM source). This can be used as a compliment to mRender 10816 * allowing you to modify the DOM element (add background colour for example) when the 10817 * element is available. 10818 */ 10819 "fnCreatedCell": null, 10820 10821 10822 /** 10823 * This property can be used to read data from any data source property, 10824 * including deeply nested objects / properties. `data` can be given in a 10825 * number of different ways which effect its behaviour: 10826 * 10827 * * `integer` - treated as an array index for the data source. This is the 10828 * default that DataTables uses (incrementally increased for each column). 10829 * * `string` - read an object property from the data source. There are 10830 * three 'special' options that can be used in the string to alter how 10831 * DataTables reads the data from the source object: 10832 * * `.` - Dotted Javascript notation. Just as you use a `.` in 10833 * Javascript to read from nested objects, so to can the options 10834 * specified in `data`. For example: `browser.version` or 10835 * `browser.name`. If your object parameter name contains a period, use 10836 * `\\` to escape it - i.e. `first\\.name`. 10837 * * `[]` - Array notation. DataTables can automatically combine data 10838 * from and array source, joining the data with the characters provided 10839 * between the two brackets. For example: `name[, ]` would provide a 10840 * comma-space separated list from the source array. If no characters 10841 * are provided between the brackets, the original array source is 10842 * returned. 10843 * * `()` - Function notation. Adding `()` to the end of a parameter will 10844 * execute a function of the name given. For example: `browser()` for a 10845 * simple function on the data source, `browser.version()` for a 10846 * function in a nested property or even `browser().version` to get an 10847 * object property if the function called returns an object. Note that 10848 * function notation is recommended for use in `render` rather than 10849 * `data` as it is much simpler to use as a renderer. 10850 * * `null` - use the original data source for the row rather than plucking 10851 * data directly from it. This action has effects on two other 10852 * initialisation options: 10853 * * `defaultContent` - When null is given as the `data` option and 10854 * `defaultContent` is specified for the column, the value defined by 10855 * `defaultContent` will be used for the cell. 10856 * * `render` - When null is used for the `data` option and the `render` 10857 * option is specified for the column, the whole data source for the 10858 * row is used for the renderer. 10859 * * `function` - the function given will be executed whenever DataTables 10860 * needs to set or get the data for a cell in the column. The function 10861 * takes three parameters: 10862 * * Parameters: 10863 * * `{array|object}` The data source for the row 10864 * * `{string}` The type call data requested - this will be 'set' when 10865 * setting data or 'filter', 'display', 'type', 'sort' or undefined 10866 * when gathering data. Note that when `undefined` is given for the 10867 * type DataTables expects to get the raw data for the object back< 10868 * * `{*}` Data to set when the second parameter is 'set'. 10869 * * Return: 10870 * * The return value from the function is not required when '
10870set' is 10871 * the type of call, but otherwise the return is what will be used 10872 * for the data requested. 10873 * 10874 * Note that `data` is a getter and setter option. If you just require 10875 * formatting of data for output, you will likely want to use `render` which 10876 * is simply a getter and thus simpler to use. 10877 * 10878 * Note that prior to DataTables 1.9.2 `data` was called `mDataProp`. The 10879 * name change reflects the flexibility of this property and is consistent 10880 * with the naming of mRender. If 'mDataProp' is given, then it will still 10881 * be used by DataTables, as it automatically maps the old name to the new 10882 * if required. 10883 */ 10884 "mData": null, 10885 10886 10887 /** 10888 * This property is the rendering partner to `data` and it is suggested that 10889 * when you want to manipulate data for display (including filtering, 10890 * sorting etc) without altering the underlying data for the table, use this 10891 * property. `render` can be considered to be the the read only companion to 10892 * `data` which is read / write (then as such more complex). Like `data` 10893 * this option can be given in a number of different ways to effect its 10894 * behaviour: 10895 * 10896 * * `integer` - treated as an array index for the data source. This is the 10897 * default that DataTables uses (incrementally increased for each column). 10898 * * `string` - read an object property from the data source. There are 10899 * three 'special' options that can be used in the string to alter how 10900 * DataTables reads the data from the source object: 10901 * * `.` - Dotted Javascript notation. Just as you use a `.` in 10902 * Javascript to read from nested objects, so to can the options 10903 * specified in `data`. For example: `browser.version` or 10904 * `browser.name`. If your object parameter name contains a period, use 10905 * `\\` to escape it - i.e. `first\\.name`. 10906 * * `[]` - Array notation. DataTables can automatically combine data 10907 * from and array source, joining the data with the characters provided 10908 * between the two brackets. For example: `name[, ]` would provide a 10909 * comma-space separated list from the source array. If no characters 10910 * are provided between the brackets, the original array source is 10911 * returned. 10912 * * `()` - Function notation. Adding `()` to the end of a parameter will 10913 * execute a function of the name given. For example: `browser()` for a 10914 * simple function on the data source, `browser.version()` for a 10915 * function in a nested property or even `browser().version` to get an 10916 * object property if the function called returns an object. 10917 * * `object` - use different data for the different data types requested by 10918 * DataTables ('filter', 'display', 'type' or 'sort'). The property names 10919 * of the object is the data type the property refers to and the value can 10920 * defined using an integer, string or function using the same rules as 10921 * `render` normally does. Note that an `_` option _must_ be specified. 10922 * This is the default value to use if you haven't specified a value for 10923 * the data type requested by DataTables. 10924 * * `function` - the function given will be executed whenever DataTables 10925 * needs to set or get the data for a cell in the column. The function 10926 * takes three parameters: 10927 * * Parameters: 10928 * * {array|object} The data source for the row (based on `data`) 10929 * * {string} The type call data requested - this will be 'filter', 10930 * 'display', 'type' or 'sort'. 10931 * * {array|object} The full data source for the row (not based on 10932 * `data`) 10933 * * Return: 10934 * * The return value from the function is what will be used for the 10935 * data requested. 10936 */ 10937 "mRender": null, 10938 10939 10940 /** 10941 * Change the cell type created for the column - either TD cells or TH cells. This 10942 * can be useful as TH cells have semantic meaning in the table body, allowing them 10943 * to act as a header for a row (you may wish to add scope='row' to the TH elements). 10944 */ 10945 "sCellType": "td", 10946 10947 10948 /** 10949 * Class to give to each cell in this column. 10950 */ 10951 "sClass": "", 10952 10953 /** 10954 * When DataTables calculates the column widths to assign to each column, 10955 * it finds the longest string in each column and then constructs a 10956 * temporary table and reads the widths from that. The problem with this 10957 * is that "mmm" is much wider then "iiii", but the latter is a longer 10958 * string - thus the calculation can go wrong (doing it properly and putting 10959 * it into an DOM object and measuring that is horribly(!) slow). Thus as 10960 * a "work around" we provide this option. It will append its value to the 10961 * text that is found to be the longest string for the column - i.e. padding. 10962 * Generally you shouldn't need this! 10963 */ 10964 "sContentPadding": "", 10965 10966 10967 /** 10968 * Allows a default value to be given for a column's data, and will be used 10969 * whenever a null data source is encountered (this can be because `data` 10970 * is set to null, or because the data source itself is null). 10971 */ 10972 "sDefaultContent": null, 10973 10974 10975 /** 10976 * This parameter is only used in DataTables' server-side processing. It can 10977 * be exceptionally useful to know what columns are being displayed on the 10978 * client side, and to map these to database fields. When defined, the names 10979 * also allow DataTables to reorder information from the server if it comes 10980 * back in an unexpected order (i.e. if you switch your columns around on the 10981 * client-side, your server-side code does not also need updating). 10982 */ 10983 "sName": "", 10984 10985 10986 /** 10987 * Defines a data source type for the ordering which can be used to read 10988 * real-time information from the table (updating the internally cached 10989 * version) prior to ordering. This allows ordering to occur on user 10990 * editable elements such as form inputs. 10991 */ 10992 "sSortDataType": "std", 10993 10994 10995 /** 10996 * The title of this column. 10997 */ 10998 "sTitle": null, 10999 11000 11001 /** 11002 * The type allows you to specify how the data for this column will be
11003 * ordered. Four types (string, numeric, date and html (which will strip 11004 * HTML tags before ordering)) are currently available. Note that only date 11005 * formats understood by Javascript's Date() object will be accepted as type 11006 * date. For example: "Mar 26, 2008 5:03 PM". May take the values: 'string', 11007 * 'numeric', 'date' or 'html' (by default). Further types can be adding 11008 * through plug-ins. 11009 */ 11010 "sType": null, 11011 11012 11013 /** 11014 * Defining the width of the column, this parameter may take any CSS value 11015 * (3em, 20px etc). DataTables applies 'smart' widths to columns which have not 11016 * been given a specific width through this interface ensuring that the table 11017 * remains readable. 11018 */ 11019 "sWidth": null 11020 }; 11021 11022 _fnHungarianMap( DataTable.defaults.column ); 11023 11024 11025 11026 /** 11027 * DataTables settings object - this holds all the information needed for a 11028 * given table, including configuration, data and current application of the 11029 * table options. DataTables does not have a single instance for each DataTable 11030 * with the settings attached to that instance, but rather instances of the 11031 * DataTable "class" are created on-the-fly as needed (typically by a 11032 * $().dataTable() call) and the settings object is then applied to that 11033 * instance. 11034 * 11035 * Note that this object is related to {@link DataTable.defaults} but this 11036 * one is the internal data store for DataTables's cache of columns. It should 11037 * NOT be manipulated outside of DataTables. Any configuration should be done 11038 * through the initialisation options. 11039 */ 11040 DataTable.models.oSettings = { 11041 /** 11042 * Primary features of DataTables and their enablement state. 11043 */ 11044 "oFeatures": { 11045 11046 /** 11047 * Flag to say if DataTables should automatically try to calculate the 11048 * optimum table and columns widths (true) or not (false). 11049 * Note that this parameter will be set by the initialisation routine. To 11050 * set a default use {@link DataTable.defaults}. 11051 */ 11052 "bAutoWidth": null, 11053 11054 /** 11055 * Delay the creation of TR and TD elements until they are actually 11056 * needed by a driven page draw. This can give a significant speed 11057 * increase for Ajax source and Javascript source data, but makes no 11058 * difference at all for DOM and server-side processing tables. 11059 * Note that this parameter will be set by the initialisation routine. To 11060 * set a default use {@link DataTable.defaults}. 11061 */ 11062 "bDeferRender": null, 11063 11064 /** 11065 * Enable filtering on the table or not. Note that if this is disabled 11066 * then there is no filtering at all on the table, including fnFilter. 11067 * To just remove the filtering input use sDom and remove the 'f' option. 11068 * Note that this parameter will be set by the initialisation routine. To 11069 * set a default use {@link DataTable.defaults}. 11070 */ 11071 "bFilter": null, 11072 11073 /** 11074 * Used only for compatiblity with DT1 11075 * @deprecated 11076 */ 11077 "bInfo": true, 11078 11079 /** 11080 * Used only for compatiblity with DT1 11081 * @deprecated 11082 */ 11083 "bLengthChange": true, 11084 11085 /** 11086 * Pagination enabled or not. Note that if this is disabled then length 11087 * changing must also be disabled. 11088 * Note that this parameter will be set by the initialisation routine. To 11089 * set a default use {@link DataTable.defaults}. 11090 */ 11091 "bPaginate": null, 11092 11093 /** 11094 * Processing indicator enable flag whenever DataTables is enacting a 11095 * user request - typically an Ajax request for server-side processing. 11096 * Note that this parameter will be set by the initialisation routine. To 11097 * set a default use {@link DataTable.defaults}. 11098 */ 11099 "bProcessing": null, 11100 11101 /** 11102 * Server-side processing enabled flag - when enabled DataTables will 11103 * get all data from the server for every draw - there is no filtering, 11104 * sorting or paging done on the client-side. 11105 * Note that this parameter will be set by the initialisation routine. To 11106 * set a default use {@link DataTable.defaults}. 11107 */ 11108 "bServerSide": null, 11109 11110 /** 11111 * Sorting enablement flag. 11112 * Note that this parameter will be set by the initialisation routine. To 11113 * set a default use {@link DataTable.defaults}. 11114 */ 11115 "bSort": null, 11116 11117 /**
11118 * Multi-column sorting 11119 * Note that this parameter will be set by the initialisation routine. To 11120 * set a default use {@link DataTable.defaults}. 11121 */ 11122 "bSortMulti": null, 11123 11124 /** 11125 * Apply a class to the columns which are being sorted to provide a 11126 * visual highlight or not. This can slow things down when enabled since 11127 * there is a lot of DOM interaction. 11128 * Note that this parameter will be set by the initialisation routine. To 11129 * set a default use {@link DataTable.defaults}. 11130 */ 11131 "bSortClasses": null, 11132 11133 /** 11134 * State saving enablement flag. 11135 * Note that this parameter will be set by the initialisation routine. To 11136 * set a default use {@link DataTable.defaults}. 11137 */ 11138 "bStateSave": null 11139 }, 11140 11141 11142 /** 11143 * Scrolling settings for a table. 11144 */ 11145 "oScroll": { 11146 /** 11147 * When the table is shorter in height than sScrollY, collapse the 11148 * table container down to the height of the table (when true). 11149 * Note that this parameter will be set by the initialisation routine. To 11150 * set a default use {@link DataTable.defaults}. 11151 */ 11152 "bCollapse": null, 11153 11154 /** 11155 * Width of the scrollbar for the web-browser's platform. Calculated 11156 * during table initialisation. 11157 */ 11158 "iBarWidth": 0, 11159 11160 /** 11161 * Viewport width for horizontal scrolling. Horizontal scrolling is 11162 * disabled if an empty string. 11163 * Note that this parameter will be set by the initialisation routine. To 11164 * set a default use {@link DataTable.defaults}. 11165 */ 11166 "sX": null, 11167 11168 /** 11169 * Width to expand the table to when using x-scrolling. Typically you 11170 * should not need to use this. 11171 * Note that this parameter will be set by the initialisation routine. To 11172 * set a default use {@link DataTable.defaults}. 11173 * @deprecated 11174 */ 11175 "sXInner": null, 11176 11177 /** 11178 * Viewport height for vertical scrolling. Vertical scrolling is disabled 11179 * if an empty string. 11180 * Note that this parameter will be set by the initialisation routine. To 11181 * set a default use {@link DataTable.defaults}. 11182 */ 11183 "sY": null 11184 }, 11185 11186 /** 11187 * Language information for the table. 11188 */ 11189 "oLanguage": { 11190 /** 11191 * Information callback function. See 11192 * {@link DataTable.defaults.fnInfoCallback} 11193 */ 11194 "fnInfoCallback": null 11195 }, 11196 11197 /** 11198 * Browser support parameters 11199 */ 11200 "oBrowser": { 11201 /** 11202 * Determine if the vertical scrollbar is on the right or left of the 11203 * scrolling container - needed for rtl language layout, although not 11204 * all browsers move the scrollbar (Safari). 11205 */ 11206 "bScrollbarLeft": false, 11207 11208 /** 11209 * Browser scrollbar width 11210 */ 11211 "barWidth": 0 11212 }, 11213 11214 11215 "ajax": null, 11216 11217 11218 /** 11219 * Array referencing the nodes which are used for the features. The 11220 * parameters of this object match what is allowed by sDom - i.e. 11221 * <ul> 11222 * <li>'l' - Length changing</li> 11223 * <li>'f' - Filtering input</li> 11224 * <li>'t' - The table!</li> 11225 * <li>'i' - Information</li> 11226 * <li>'p' - Pagination</li> 11227 * <li>'r' - pRocessing</li> 11228 * </ul> 11229 */ 11230 "aanFeatures": [], 11231 11232 /** 11233 * Store data information - see {@link DataTable.models.oRow} for detailed 11234 * information. 11235 */ 11236 "aoData": [], 11237 11238 /** 11239 * Array of indexes which are in the current display (after filtering etc) 11240 */ 11241 "aiDisplay": [], 11242 11243 /** 11244 * Array of indexes for display - no filtering 11245 */ 11246 "aiDisplayMaster": [], 11247 11248 /** 11249 * Map of row ids to data indexes 11250 */ 11251 "aIds": {}, 11252 11253 /** 11254 * Store information about each column that is in use 11255 */ 11256 "aoColumns": [], 11257 11258 /** 11259 * Store information about the table's header 11260 */ 11261 "aoHeader": [], 11262 11263 /** 11264 * Store information about the table's footer 11265 */ 11266 "aoFooter": [], 11267 11268 /** 11269 * Store the applied global search information in case we want to force a 11270 * research or compare the old search to a new one. 11271 * Note that this parameter will be set by the initialisation routine. To 11272 * set a default use {@link DataTable.defaults}. 11273 */ 11274 "oPreviousSearch": {}, 11275 11276 /** 11277 * Store for named searches 11278 */ 11279 searchFixed: {}, 11280 11281 /** 11282 * Store the applied search for each column - see 11283 * {@link DataTable.models.oSearch} for the format that is used for the 11284 * filtering information for each column. 11285 */ 11286 "aoPreSearchCols": [], 11287 11288 /** 11289 * Sorting that is applied to the table. Note that the inner arrays are 11290 * used in the following manner: 11291 * <ul> 11292 * <li>Index 0 - column number</li> 11293 * <li>Index 1 - current sorting direction</li> 11294 * </ul> 11295 * Note that this parameter will be set by the initialisation routine. To 11296 * set a default use {@link DataTable.defaults}. 11297 */ 11298 "aaSorting": null, 11299 11300 /** 11301 * Sorting that is always applied to the table (i.e. prefixed in front of 11302 * aaSorting). 11303 * Note that this parameter will be set by the initialisation routine. To 11304 * set a default use {@link DataTable.defaults}. 11305 */ 11306 "aaSortingFixed": [], 11307 11308 /** 11309 * If restoring a table - we should restore its width 11310 */ 11311 "sDestroyWidth": 0, 11312 11313 /** 11314 * Callback functions array for every time a row is inserted (i.e. on a draw). 11315 */ 11316 "aoRowCallback": [], 11317 11318 /** 11319 * Callback functions for the header on each draw. 11320 */ 11321 "aoHeaderCallback": [], 11322 11323 /** 11324 * Callback function for the footer on each draw. 11325 */ 11326 "aoFooterCallback": [], 11327 11328 /** 11329 * Array of callback functions for draw callback functions 11330 */ 11331 "aoDrawCallback": [], 11332 11333 /** 11334 * Array of callback functions for row created function 11335 */ 11336 "aoRowCreatedCallback": [], 11337 11338 /** 11339 * Callback functions for just before the table is redrawn. A return of 11340 * false will be used to cancel the draw. 11341 */ 11342 "aoPreDrawCallback": [], 11343 11344 /** 11345 * Callback functions for when the table has been initialised. 11346 */ 11347 "aoInitComplete": [], 11348 11349 11350 /** 11351 * Callbacks for modifying the settings to be stored for state saving, prior to 11352 * saving state. 11353 */ 11354 "aoStateSaveParams": [], 11355 11356 /** 11357 * Callbacks for modifying the settings that have been stored for state saving 11358 * prior to using the stored values to restore the state. 11359 */ 11360 "aoStateLoadParams": [], 11361 11362 /** 11363 * Callbacks for operating on the settings object once the saved state has been 11364 * loaded 11365 */ 11366 "aoStateLoaded": [], 11367 11368 /** 11369 * Cache the table ID for quick access 11370 */ 11371 "sTableId": "", 11372 11373 /** 11374 * The TABLE node for the main table 11375 */ 11376 "nTable": null, 11377 11378 /** 11379 * Permanent ref to the thead element 11380 */ 11381 "nTHead": null, 11382 11383 /** 11384 * Permanent ref to the tfoot element - if it exists 11385 */ 11386 "nTFoot": null, 11387 11388 /** 11389 * Permanent ref to the tbody element 11390 */ 11391 "nTBody": null, 11392 11393 /** 11394 * Cache the wrapper node (contains all DataTables controlled elements) 11395 */ 11396 "nTableWrapper": null, 11397 11398 /** 11399 * Indicate if all required information has been read in 11400 */ 11401 "bInitialised": false, 11402 11403 /** 11404 * Information about open rows. Each object in the array has the parameters 11405 * 'nTr' and 'nParent' 11406 */ 11407 "aoOpenRows": [], 11408 11409 /**
11410 * Dictate the positioning of DataTables' control elements - see 11411 * {@link DataTable.model.oInit.sDom}. 11412 * Note that this parameter will be set by the initialisation routine. To 11413 * set a default use {@link DataTable.defaults}. 11414 */ 11415 "sDom": null, 11416 11417 /** 11418 * Search delay (in mS) 11419 */ 11420 "searchDelay": null, 11421 11422 /** 11423 * Which type of pagination should be used. 11424 * Note that this parameter will be set by the initialisation routine. To 11425 * set a default use {@link DataTable.defaults}. 11426 */ 11427 "sPaginationType": "two_button", 11428 11429 /** 11430 * Number of paging controls on the page. Only used for backwards compatibility 11431 */ 11432 pagingControls: 0, 11433 11434 /** 11435 * The state duration (for `stateSave`) in seconds. 11436 * Note that this parameter will be set by the initialisation routine. To 11437 * set a default use {@link DataTable.defaults}. 11438 */ 11439 "iStateDuration": 0, 11440 11441 /** 11442 * Array of callback functions for state saving. Each array element is an 11443 * object with the following parameters: 11444 * <ul> 11445 * <li>function:fn - function to call. Takes two parameters, oSettings 11446 * and the JSON string to save that has been thus far created. Returns 11447 * a JSON string to be inserted into a json object 11448 * (i.e. '"param": [ 0, 1, 2]')</li> 11449 * <li>string:sName - name of callback</li> 11450 * </ul> 11451 */ 11452 "aoStateSave": [], 11453 11454 /** 11455 * Array of callback functions for state loading. Each array element is an 11456 * object with the following parameters: 11457 * <ul> 11458 * <li>function:fn - function to call. Takes two parameters, oSettings 11459 * and the object stored. May return false to cancel state loading</li> 11460 * <li>string:sName - name of callback</li> 11461 * </ul> 11462 */ 11463 "aoStateLoad": [], 11464 11465 /** 11466 * State that was saved. Useful for back reference 11467 */ 11468 "oSavedState": null, 11469 11470 /** 11471 * State that was loaded. Useful for back reference 11472 */ 11473 "oLoadedState": null, 11474 11475 /** 11476 * Note if draw should be blocked while getting data 11477 */ 11478 "bAjaxDataGet": true, 11479 11480 /** 11481 * The last jQuery XHR object that was used for server-side data gathering. 11482 * This can be used for working with the XHR information in one of the 11483 * callbacks 11484 */ 11485 "jqXHR": null, 11486 11487 /** 11488 * JSON returned from the server in the last Ajax request 11489 */ 11490 "json": undefined, 11491 11492 /** 11493 * Data submitted as part of the last Ajax request 11494 */ 11495 "oAjaxData": undefined, 11496 11497 /** 11498 * Send the XHR HTTP method - GET or POST (could be PUT or DELETE if 11499 * required). 11500 * Note that this parameter will be set by the initialisation routine. To 11501 * set a default use {@link DataTable.defaults}. 11502 */ 11503 "sServerMethod": null, 11504 11505 /** 11506 * Format numbers for display. 11507 * Note that this parameter will be set by the initialisation routine. To 11508 * set a default use {@link DataTable.defaults}. 11509 */ 11510 "fnFormatNumber": null, 11511 11512 /** 11513 * List of options that can be used for the user selectable length menu. 11514 * Note that this parameter will be set by the initialisation routine. To 11515 * set a default use {@link DataTable.defaults}. 11516 */ 11517 "aLengthMenu": null, 11518 11519 /** 11520 * Counter for the draws that the table does. Also used as a tracker for 11521 * server-side processing 11522 */ 11523 "iDraw": 0, 11524 11525 /** 11526 * Indicate if a redraw is being done - useful for Ajax 11527 */ 11528 "bDrawing": false, 11529 11530 /** 11531 * Draw index (iDraw) of the last error when parsing the returned data 11532 */ 11533 "iDrawError": -1, 11534 11535 /** 11536 * Paging display length 11537 */ 11538 "_iDisplayLength": 10, 11539 11540 /** 11541 * Paging start point - aiDisplay index 11542 */ 11543 "_iDisplayStart": 0, 11544 11545 /** 11546 * Server-side processing - number of records in the result set 11547 * (i.e. before filtering), Use fnRecordsTotal rather than 11548 * this property to get the value of the number of records, regardless of 11549 * the server-side processing setting. 11550 */ 11551 "_iRecordsTotal": 0, 11552 11553 /** 11554 * Server-side processing - number of records in the current display set 11555 * (i.e. after filtering). Use fnRecordsDisplay rather than 11556 * this property to get the value of the number of records, regardless of 11557 * the server-side processing setting. 11558 */ 11559 "_iRecordsDisplay": 0, 11560 11561 /** 11562 * The classes to use for the table 11563 */
11564 "oClasses": {}, 11565 11566 /** 11567 * Flag attached to the settings object so you can check in the draw 11568 * callback if filtering has been done in the draw. Deprecated in favour of 11569 * events. 11570 * @deprecated 11571 */ 11572 "bFiltered": false, 11573 11574 /** 11575 * Flag attached to the settings object so you can check in the draw 11576 * callback if sorting has been done in the draw. Deprecated in favour of 11577 * events. 11578 * @deprecated 11579 */ 11580 "bSorted": false, 11581 11582 /** 11583 * Indicate that if multiple rows are in the header and there is more than 11584 * one unique cell per column, if the top one (true) or bottom one (false) 11585 * should be used for sorting / title by DataTables. 11586 * Note that this parameter will be set by the initialisation routine. To 11587 * set a default use {@link DataTable.defaults}. 11588 */ 11589 "bSortCellsTop": null, 11590 11591 /** 11592 * Initialisation object that is used for the table 11593 */ 11594 "oInit": null, 11595 11596 /** 11597 * Destroy callback functions - for plug-ins to attach themselves to the 11598 * destroy so they can clean up markup and events. 11599 */ 11600 "aoDestroyCallback": [], 11601 11602 11603 /** 11604 * Get the number of records in the current record set, before filtering 11605 */ 11606 "fnRecordsTotal": function () 11607 { 11608 return _fnDataSource( this ) == 'ssp' ? 11609 this._iRecordsTotal * 1 : 11610 this.aiDisplayMaster.length; 11611 }, 11612 11613 /** 11614 * Get the number of records in the current record set, after filtering 11615 */ 11616 "fnRecordsDisplay": function () 11617 { 11618 return _fnDataSource( this ) == 'ssp' ? 11619 this._iRecordsDisplay * 1 : 11620 this.aiDisplay.length; 11621 }, 11622 11623 /** 11624 * Get the display end point - aiDisplay index 11625 */ 11626 "fnDisplayEnd": function () 11627 { 11628 var 11629 len = this._iDisplayLength, 11630 start = this._iDisplayStart, 11631 calc = start + len, 11632 records = this.aiDisplay.length, 11633 features = this.oFeatures, 11634 paginate = features.bPaginate; 11635 11636 if ( features.bServerSide ) { 11637 return paginate === false || len === -1 ? 11638 start + records : 11639 Math.min( start+len, this._iRecordsDisplay ); 11640 } 11641 else { 11642 return ! paginate || calc>records || len===-1 ? 11643 records : 11644 calc; 11645 } 11646 }, 11647 11648 /** 11649 * The DataTables object for this table 11650 */ 11651 "oInstance": null, 11652 11653 /** 11654 * Unique identifier for each instance of the DataTables object. If there 11655 * is an ID on the table node, then it takes that value, otherwise an 11656 * incrementing internal counter is used. 11657 */ 11658 "sInstance": null, 11659 11660 /** 11661 * tabindex attribute value that is added to DataTables control elements, allowing 11662 * keyboard navigation of the table and its controls. 11663 */ 11664 "iTabIndex": 0, 11665 11666 /** 11667 * DIV container for the footer scrolling table if scrolling 11668 */ 11669 "nScrollHead": null, 11670 11671 /** 11672 * DIV container for the footer scrolling table if scrolling 11673 */ 11674 "nScrollFoot": null, 11675 11676 /** 11677 * Last applied sort 11678 */ 11679 "aLastSort": [], 11680 11681 /** 11682 * Stored plug-in instances 11683 */ 11684 "oPlugins": {}, 11685 11686 /** 11687 * Function used to get a row's id from the row's data 11688 */ 11689 "rowIdFn": null, 11690 11691 /** 11692 * Data location where to store a row's id 11693 */ 11694 "rowId": null, 11695 11696 caption: '', 11697 11698 captionNode: null, 11699 11700 colgroup: null 11701 }; 11702 11703 /** 11704 * Extension object for DataTables that is used to provide all extension 11705 * options. 11706 * 11707 * Note that the `DataTable.ext` object is available through 11708 * `jQuery.fn.dataTable.ext` where it may be accessed and manipulated. It is 11709 * also aliased to `jQuery.fn.dataTableExt` for historic reasons. 11710 * @namespace 11711 * @extends DataTable.models.ext 11712 */ 11713 11714 11715 var extPagination = DataTable.ext.pager; 11716 11717 // Paging buttons configuration 11718 $.extend( extPagination, { 11719 simple: function () { 11720 return [ 'previous', 'next' ]; 11721 }, 11722 11723 full: function () { 11724 return [ 'first', 'previous', 'next', 'last' ]; 11725 }, 11726 11727 numbers: function () { 11728 return [ 'numbers' ]; 11729 }, 11730 11731 simple_numbers: function () { 11732 return [ 'previous', 'numbers', 'next' ]; 11733 }, 11734 11735 full_numbers: function () { 11736 return [ 'first', 'previous', 'numbers', 'next', 'last' ]; 11737 }, 11738 11739 first_last: function () { 11740 return ['first', 'last']; 11741 }, 11742 11743 first_last_numbers: function () { 11744 return ['first', 'numbers', 'last']; 11745 }, 11746 11747 // For testing and plug-ins to use 11748 _numbers: _pagingNumbers, 11749 11750 // Number of number buttons - legacy, use `numbers` option for paging feature 11751 numbers_length: 7 11752 } ); 11753 11754 11755 $.extend( true, DataTable.ext.renderer, { 11756 pagingButton: { 11757 _: function (settings, buttonType, content, active, disabled) { 11758 var classes = settings.oClasses.paging; 11759 var btnClasses = [classes.button]; 11760 var btn; 11761 11762 if (active) { 11763 btnClasses.push(classes.active); 11764 } 11765 11766 if (disabled) { 11767 btnClasses.push(classes.disabled) 11768 } 11769 11770 if (buttonType === 'ellipsis') { 11771 btn = $('<span class="ellipsis"></span>').html(content)[0]; 11772 } 11773 else { 11774 btn = $('<button>', { 11775 class: btnClasses.join(' '), 11776 role: 'link', 11777 type: 'button' 11778 }).html(content); 11779 } 11780 11781 return { 11782 display: btn, 11783 clicker: btn 11784 } 11785 } 11786 }, 11787 11788 pagingContainer: { 11789 _: function (settings, buttons) { 11790 // No wrapping element - just append directly to the host 11791 return buttons; 11792 } 11793 } 11794 } ); 11795 11796 // Common function to remove new lines, strip HTML and diacritic control 11797 var _filterString = function (stripHtml, normalize) { 11798 return function (str) { 11799 if (_empty(str) || typeof str !== 'string') { 11800 return str; 11801 } 11802 11803 str = str.replace( _re_new_lines, " " ); 11804 11805 if (stripHtml) { 11806 str = _stripHtml(str); 11807 } 11808 11809 if (normalize) { 11810 str = _normalize(str, false); 11811 } 11812 11813 return str; 11814 }; 11815 } 11816 11817 /* 11818 * Public helper functions. These aren't used internally by DataTables, or 11819 * called by any of the options passed into DataTables, but they can be used 11820 * externally by developers working with DataTables. They are helper functions 11821 * to make working with DataTables a little bit easier. 11822 */ 11823 11824 function __mldFnName(name) { 11825 return name.replace(/[\W]/g, '_') 11826 } 11827 11828 // Common logic for moment, luxon or a date action 11829 function __mld( dt, momentFn, luxonFn, dateFn, arg1 ) { 11830 if (window.moment) { 11831 return dt[momentFn]( arg1 ); 11832 } 11833 else if (window.luxon) { 11834 return dt[luxonFn]( arg1 ); 11835 } 11836 11837 return dateFn ? dt[dateFn]( arg1 ) : dt; 11838 } 11839 11840 11841 var __mlWarning = false;
vendor: 4,458 bytes, lines 11842-12013
11842 function __mldObj (d, format, locale) { 11843 var dt; 11844 11845 if (window.moment) { 11846 dt = window.moment.utc( d, format, locale, true ); 11847 11848 if (! dt.isValid()) { 11849 return null; 11850 } 11851 } 11852 else if (window.luxon) { 11853 dt = format && typeof d === 'string' 11854 ? window.luxon.DateTime.fromFormat( d, format ) 11855 : window.luxon.DateTime.fromISO( d ); 11856 11857 if (! dt.isValid) { 11858 return null; 11859 } 11860 11861 dt.setLocale(locale); 11862 } 11863 else if (! format) { 11864 // No format given, must be ISO 11865 dt = new Date(d); 11866 } 11867 else { 11868 if (! __mlWarning) { 11869 alert('DataTables warning: Formatted date without Moment.js or Luxon - https://datatables.net/tn/17'); 11870 } 11871 11872 __mlWarning = true; 11873 } 11874 11875 return dt; 11876 } 11877 11878 // Wrapper for date, datetime and time which all operate the same way with the exception of 11879 // the output string for auto locale support 11880 function __mlHelper (localeString) { 11881 return function ( from, to, locale, def ) { 11882 // Luxon and Moment support 11883 // Argument shifting 11884 if ( arguments.length === 0 ) { 11885 locale = 'en'; 11886 to = null; // means toLocaleString 11887 from = null; // means iso8601 11888 } 11889 else if ( arguments.length === 1 ) { 11890 locale = 'en'; 11891 to = from; 11892 from = null; 11893 } 11894 else if ( arguments.length === 2 ) { 11895 locale = to; 11896 to = from; 11897 from = null; 11898 } 11899 11900 var typeName = 'datetime' + (to ? '-' + __mldFnName(to) : ''); 11901 11902 // Add type detection and sorting specific to this date format - we need to be able to identify 11903 // date type columns as such, rather than as numbers in extensions. Hence the need for this. 11904 if (! DataTable.ext.type.order[typeName]) { 11905 DataTable.type(typeName, { 11906 detect: function (d) { 11907 // The renderer will give the value to type detect as the type! 11908 return d === typeName ? typeName : false; 11909 }, 11910 order: { 11911 pre: function (d) { 11912 // The renderer gives us Moment, Luxon or Date obects for the sorting, all of which have a 11913 // `valueOf` which gives milliseconds epoch 11914 return d.valueOf(); 11915 } 11916 }, 11917 className: 'dt-right' 11918 }); 11919 } 11920 11921 return function ( d, type ) { 11922 // Allow for a default value 11923 if (d === null || d === undefined) { 11924 if (def === '--now') { 11925 // We treat everything as UTC further down, so no changes are 11926 // made, as such need to get the local date / time as if it were 11927 // UTC 11928 var local = new Date(); 11929 d = new Date( Date.UTC( 11930 local.getFullYear(), local.getMonth(), local.getDate(), 11931 local.getHours(), local.getMinutes(), local.getSeconds() 11932 ) ); 11933 } 11934 else { 11935 d = ''; 11936 } 11937 } 11938 11939 if (type === 'type') { 11940 // Typing uses the type name for fast matching 11941 return typeName; 11942 } 11943 11944 if (d === '') { 11945 return type !== 'sort' 11946 ? '' 11947 : __mldObj('0000-01-01 00:00:00', null, locale); 11948 } 11949 11950 // Shortcut. If `from` and `to` are the same, we are using the renderer to 11951 // format for ordering, not display - its already in the display format. 11952 if ( to !== null && from === to && type !== 'sort' && type !== 'type' && ! (d instanceof Date) ) { 11953 return d; 11954 } 11955 11956 var dt = __mldObj(d, from, locale); 11957 11958 if (dt === null) { 11959 return d; 11960 } 11961 11962 if (type === 'sort') { 11963 return dt; 11964 } 11965 11966 var formatted = to === null 11967 ? __mld(dt, 'toDate', 'toJSDate', '')[localeString]() 11968 : __mld(dt, 'format', 'toFormat', 'toISOString', to); 11969 11970 // XSS protection 11971 return type === 'display' ? 11972 _escapeHtml( formatted ) : 11973 formatted; 11974 }; 11975 } 11976 } 11977 11978 // Based on locale, determine standard number formatting 11979 // Fallback for legacy browsers is US English 11980 var __thousands = ','; 11981 var __decimal = '.'; 11982 11983 if (window.Intl !== undefined) { 11984 try { 11985 var num = new Intl.NumberFormat().formatToParts(100000.1); 11986 11987 for (var i=0 ; i<num.length ; i++) { 11988 if (num[i].type === 'group') { 11989 __thousands = num[i].value; 11990 } 11991 else if (num[i].type === 'decimal') { 11992 __decimal = num[i].value; 11993 } 11994 } 11995 } 11996 catch (e) { 11997 // noop 11998 } 11999 } 12000 12001 // Formatted date time detection - use by declaring the formats you are going to use 12002 DataTable.datetime = function ( format, locale ) { 12003 var typeName = 'datetime-detect-' + __mldFnName(format); 12004 12005 if (! locale) { 12006 locale = 'en'; 12007 } 12008 12009 if (! DataTable.ext.type.order[typeName]) { 12010 DataTable.type(typeName, { 12011 detect: function (d) { 12012 var dt = __mldObj(d, format, locale); 12013 return d === '' || dt ? typeName : false;
12014 }, 12015 order: { 12016 pre: function (d) { 12017 return __mldObj(d, format, locale) || 0; 12018 } 12019 }, 12020 className: 'dt-right' 12021 }); 12022 } 12023 } 12024 12025 /** 12026 * Helpers for `columns.render`. 12027 * 12028 * The options defined here can be used with the `columns.render` initialisation 12029 * option to provide a display renderer. The following functions are defined: 12030 * 12031 * * `moment` - Uses the MomentJS library to convert from a given format into another. 12032 * This renderer has three overloads: 12033 * * 1 parameter: 12034 * * `string` - Format to convert to (assumes input is ISO8601 and locale is `en`) 12035 * * 2 parameters: 12036 * * `string` - Format to convert from 12037 * * `string` - Format to convert to. Assumes `en` locale 12038 * * 3 parameters: 12039 * * `string` - Format to convert from 12040 * * `string` - Format to convert to 12041 * * `string` - Locale 12042 * * `number` - Will format numeric data (defined by `columns.data`) for 12043 * display, retaining the original unformatted data for sorting and filtering. 12044 * It takes 5 parameters: 12045 * * `string` - Thousands grouping separator 12046 * * `string` - Decimal point indicator 12047 * * `integer` - Number of decimal points to show 12048 * * `string` (optional) - Prefix. 12049 * * `string` (optional) - Postfix (/suffix). 12050 * * `text` - Escape HTML to help prevent XSS attacks. It has no optional 12051 * parameters. 12052 * 12053 * @example 12054 * // Column definition using the number renderer 12055 * { 12056 * data: "salary", 12057 * render: $.fn.dataTable.render.number( '\'', '.', 0, '$' ) 12058 * } 12059 * 12060 * @namespace 12061 */ 12062 DataTable.render = { 12063 date: __mlHelper('toLocaleDateString'), 12064 datetime: __mlHelper('toLocaleString'), 12065 time: __mlHelper('toLocaleTimeString'), 12066 number: function ( thousands, decimal, precision, prefix, postfix ) { 12067 // Auto locale detection 12068 if (thousands === null || thousands === undefined) { 12069 thousands = __thousands; 12070 } 12071 12072 if (decimal === null || decimal === undefined) { 12073 decimal = __decimal; 12074 } 12075 12076 return { 12077 display: function ( d ) { 12078 if ( typeof d !== 'number' && typeof d !== 'string' ) { 12079 return d; 12080 } 12081 12082 if (d === '' || d === null) { 12083 return d; 12084 } 12085 12086 var negative = d < 0 ? '-' : ''; 12087 var flo = parseFloat( d ); 12088 var abs = Math.abs(flo); 12089 12090 // Scientific notation for large and small numbers 12091 if (abs >= 100000000000 || (abs < 0.0001 && abs !== 0) ) { 12092 var exp = flo.toExponential(precision).split(/e\+?/); 12093 return exp[0] + ' x 10<sup>' + exp[1] + '</sup>'; 12094 } 12095 12096 // If NaN then there isn't much formatting that we can do - just 12097 // return immediately, escaping any HTML (this was supposed to 12098 // be a number after all) 12099 if ( isNaN( flo ) ) { 12100 return _escapeHtml( d ); 12101 } 12102 12103 flo = flo.toFixed( precision ); 12104 d = Math.abs( flo ); 12105 12106 var intPart = parseInt( d, 10 ); 12107 var floatPart = precision ? 12108 decimal+(d - intPart).toFixed( precision ).substring( 2 ): 12109 ''; 12110 12111 // If zero, then can't have a negative prefix 12112 if (intPart === 0 && parseFloat(floatPart) === 0) { 12113 negative = ''; 12114 } 12115 12116 return negative + (prefix||'') + 12117 intPart.toString().replace( 12118 /\B(?=(\d{3})+(?!\d))/g, thousands 12119 ) + 12120 floatPart + 12121 (postfix||''); 12122 } 12123 }; 12124 }, 12125 12126 text: function () { 12127 return { 12128 display: _escapeHtml, 12129 filter: _escapeHtml 12130 }; 12131 } 12132 }; 12133 12134 12135 var _extTypes = DataTable.ext.type; 12136 12137 // Get / set type 12138 DataTable.type = function (name, prop, val) { 12139 if (! prop) { 12140 return { 12141 className: _extTypes.className[name], 12142 detect: _extTypes.detect.find(function (fn) { 12143 return fn.name === name; 12144 }), 12145 order: { 12146 pre: _extTypes.order[name + '-pre'], 12147 asc: _extTypes.order[name + '-asc'], 12148 desc: _extTypes.order[name + '-desc'] 12149 }, 12150 render: _extTypes.render[name], 12151 search: _extTypes.search[name] 12152 }; 12153 } 12154 12155 var setProp = function(prop, propVal) { 12156 _extTypes[prop][name] = propVal; 12157 }; 12158 var setDetect = function (fn) { 12159 // Wrap to allow the function to return `true` rather than 12160 // specifying the type name. 12161 var cb = function (d, s) { 12162 var ret = fn(d, s); 12163 12164 return ret === true 12165 ? name 12166 : ret; 12167 }; 12168 Object.defineProperty(cb, "name", {value: name}); 12169 12170 var idx = _extTypes.detect.findIndex(function (fn) { 12171 return fn.name === name; 12172 }); 12173 12174 if (idx === -1) { 12175 _extTypes.detect.unshift(cb); 12176 } 12177 else { 12178 _extTypes.detect.splice(idx, 1, cb); 12179 } 12180 }; 12181 var setOrder = function (obj) { 12182 _extTypes.order[name + '-pre'] = obj.pre; // can be undefined 12183 _extTypes.order[name + '-asc'] = obj.asc; // can be undefined 12184 _extTypes.order[name + '-desc'] = obj.desc; // can be undefined 12185 }; 12186 12187 // prop is optional 12188 if (val === undefined) { 12189 val = prop; 12190 prop = null; 12191 } 12192 12193 if (prop === 'className') { 12194 setProp('className', val); 12195 } 12196 else if (prop === 'detect') { 12197 setDetect(val); 12198 } 12199 else if (prop === 'order') { 12200 setOrder(val); 12201 } 12202 else if (prop === 'render') { 12203 setProp('render', val); 12204 } 12205 else if (prop === 'search') { 12206 setProp('search', val); 12207 } 12208 else if (! prop) { 12209 if (val.className) { 12210 setProp('className', val.className); 12211 } 12212 12213 if (val.detect !== undefined) { 12214 setDetect(val.detect); 12215 } 12216 12217 if (val.order) { 12218 setOrder(val.order); 12219 } 12220 12221 if (val.render !== undefined) { 12222 setProp('render', val.render); 12223 } 12224 12225 if (val.search !== undefined) { 12226 setProp('search', val.search); 12227 } 12228 } 12229 } 12230 12231 // Get a list of types 12232 DataTable.types = function () { 12233 return _extTypes.detect.map(function (fn) { 12234 return fn.name; 12235 }); 12236 }; 12237 12238 // 12239 // Built in data types 12240 // 12241 12242 DataTable.type('string', { 12243 detect: function () { 12244 return 'string'; 12245 }, 12246 order: { 12247 pre: function ( a ) { 12248 // This is a little complex, but faster than always calling toString, 12249 // http://jsperf.com/tostring-v-check 12250 return _empty(a) ? 12251 '' : 12252 typeof a === 'string' ? 12253 a.toLowerCase() : 12254 ! a.toString ? 12255 '' : 12256 a.toString(); 12257 } 12258 }, 12259 search: _filterString(false, true) 12260 }); 12261 12262 12263 DataTable.type('html', { 12264 detect: function ( d ) { 12265 return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1) ? 12266 'html' : null; 12267 }, 12268 order: { 12269 pre: function ( a ) { 12270 return _empty(a) ? 12271 '' : 12272 a.replace ?
12273 _stripHtml(a).trim().toLowerCase() : 12274 a+''; 12275 } 12276 }, 12277 search: _filterString(true, true) 12278 }); 12279 12280 12281 DataTable.type('date', { 12282 className: 'dt-type-date', 12283 detect: function ( d ) 12284 { 12285 // V8 tries _very_ hard to make a string passed into `Date.parse()` 12286 // valid, so we need to use a regex to restrict date formats. Use a 12287 // plug-in for anything other than ISO8601 style strings 12288 if ( d && !(d instanceof Date) && ! _re_date.test(d) ) { 12289 return null; 12290 } 12291 var parsed = Date.parse(d); 12292 return (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null; 12293 }, 12294 order: { 12295 pre: function ( d ) { 12296 var ts = Date.parse( d ); 12297 return isNaN(ts) ? -Infinity : ts; 12298 } 12299 } 12300 }); 12301 12302 12303 DataTable.type('html-num-fmt', { 12304 className: 'dt-type-numeric', 12305 detect: function ( d, settings ) 12306 { 12307 var decimal = settings.oLanguage.sDecimal; 12308 return _htmlNumeric( d, decimal, true ) ? 'html-num-fmt' : null; 12309 }, 12310 order: { 12311 pre: function ( d, s ) { 12312 var dp = s.oLanguage.sDecimal; 12313 return __numericReplace( d, dp, _re_html, _re_formatted_numeric ); 12314 } 12315 }, 12316 search: _filterString(true, true) 12317 }); 12318 12319 12320 DataTable.type('html-num', { 12321 className: 'dt-type-numeric', 12322 detect: function ( d, settings ) 12323 { 12324 var decimal = settings.oLanguage.sDecimal; 12325 return _htmlNumeric( d, decimal ) ? 'html-num' : null; 12326 }, 12327 order: { 12328 pre: function ( d, s ) { 12329 var dp = s.oLanguage.sDecimal; 12330 return __numericReplace( d, dp, _re_html ); 12331 } 12332 }, 12333 search: _filterString(true, true) 12334 }); 12335 12336 12337 DataTable.type('num-fmt', { 12338 className: 'dt-type-numeric', 12339 detect: function ( d, settings ) 12340 { 12341 var decimal = settings.oLanguage.sDecimal; 12342 return _isNumber( d, decimal, true ) ? 'num-fmt' : null; 12343 }, 12344 order: { 12345 pre: function ( d, s ) { 12346 var dp = s.oLanguage.sDecimal; 12347 return __numericReplace( d, dp, _re_formatted_numeric ); 12348 } 12349 } 12350 }); 12351 12352 12353 DataTable.type('num', { 12354 className: 'dt-type-numeric', 12355 detect: function ( d, settings ) 12356 { 12357 var decimal = settings.oLanguage.sDecimal; 12358 return _isNumber( d, decimal ) ? 'num' : null; 12359 }, 12360 order: { 12361 pre: function (d, s) { 12362 var dp = s.oLanguage.sDecimal; 12363 return __numericReplace( d, dp ); 12364 } 12365 } 12366 }); 12367 12368 12369 12370 12371 var __numericReplace = function ( d, decimalPlace, re1, re2 ) { 12372 if ( d !== 0 && (!d || d === '-') ) { 12373 return -Infinity; 12374 } 12375 12376 var type = typeof d; 12377 12378 if (type === 'number' || type === 'bigint') { 12379 return d; 12380 } 12381 12382 // If a decimal place other than `.` is used, it needs to be given to the 12383 // function so we can detect it and replace with a `.` which is the only 12384 // decimal place Javascript recognises - it is not locale aware. 12385 if ( decimalPlace ) { 12386 d = _numToDecimal( d, decimalPlace ); 12387 } 12388 12389 if ( d.replace ) { 12390 if ( re1 ) { 12391 d = d.replace( re1, '' ); 12392 } 12393 12394 if ( re2 ) { 12395 d = d.replace( re2, '' ); 12396 } 12397 } 12398 12399 return d * 1; 12400 }; 12401 12402 12403 $.extend( true, DataTable.ext.renderer, { 12404 footer: { 12405 _: function ( settings, cell, classes ) {
12406 cell.addClass(classes.tfoot.cell); 12407 } 12408 }, 12409 12410 header: { 12411 _: function ( settings, cell, classes ) { 12412 cell.addClass(classes.thead.cell); 12413 12414 if (! settings.oFeatures.bSort) { 12415 cell.addClass(classes.order.none); 12416 } 12417 12418 var legacyTop = settings.bSortCellsTop; 12419 var headerRows = cell.closest('thead').find('tr'); 12420 var rowIdx = cell.parent().index(); 12421 12422 // Conditions to not apply the ordering icons 12423 if ( 12424 // Cells and rows which have the attribute to disable the icons 12425 cell.attr('data-dt-order') === 'disable' || 12426 cell.parent().attr('data-dt-order') === 'disable' || 12427 12428 // Legacy support for `orderCellsTop`. If it is set, then cells 12429 // which are not in the top or bottom row of the header (depending 12430 // on the value) do not get the sorting classes applied to them 12431 (legacyTop === true && rowIdx !== 0) || 12432 (legacyTop === false && rowIdx !== headerRows.length - 1) 12433 ) { 12434 return; 12435 } 12436 12437 // No additional mark-up required 12438 // Attach a sort listener to update on sort - note that using the 12439 // `DT` namespace will allow the event to be removed automatically 12440 // on destroy, while the `dt` namespaced event is the one we are 12441 // listening for 12442 $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting ) { 12443 if ( settings !== ctx ) { // need to check this this is the host 12444 return; // table, not a nested one 12445 } 12446 12447 var orderClasses = classes.order; 12448 var columns = ctx.api.columns( cell ); 12449 var col = settings.aoColumns[columns.flatten()[0]]; 12450 var orderable = columns.orderable().includes(true); 12451 var ariaType = ''; 12452 var indexes = columns.indexes(); 12453 var sortDirs = columns.orderable(true).flatten(); 12454 var orderedColumns = ',' + sorting.map( function (val) { 12455 return val.col; 12456 } ).join(',') + ','; 12457 12458 cell 12459 .removeClass( 12460 orderClasses.isAsc +' '+ 12461 orderClasses.isDesc 12462 ) 12463 .toggleClass( orderClasses.none, ! orderable ) 12464 .toggleClass( orderClasses.canAsc, orderable && sortDirs.includes('asc') ) 12465 .toggleClass( orderClasses.canDesc, orderable && sortDirs.includes('desc') ); 12466 12467 var sortIdx = orderedColumns.indexOf( ',' + indexes.toArray().join(',') + ',' ); 12468 12469 if ( sortIdx !== -1 ) { 12470 // Get the ordering direction for the columns under this cell 12471 // Note that it is possible for a cell to be asc and desc sorting 12472 // (column spanning cells) 12473 var orderDirs = columns.order(); 12474 12475 cell.addClass( 12476 orderDirs.includes('asc') ? orderClasses.isAsc : '' + 12477 orderDirs.includes('desc') ? orderClasses.isDesc : '' 12478 ); 12479 } 12480 12481 // The ARIA spec says that only one column should be marked with aria-sort 12482 if ( sortIdx === 0 ) { 12483 var firstSort = sorting[0]; 12484 var sortOrder = col.asSorting; 12485 12486 cell.attr('aria-sort', firstSort.dir === 'asc' ? 'ascending' : 'descending'); 12487 12488 // Determine if the next click will remove sorting or change the sort 12489 ariaType = ! sortOrder[firstSort.index + 1] ? 'Remove' : 'Reverse'; 12490 } 12491 else { 12492 cell.removeAttr('aria-sort'); 12493 } 12494 12495 cell.attr('aria-label', orderable 12496 ? col.ariaTitle + ctx.api.i18n('oAria.orderable' + ariaType) 12497 : col.ariaTitle 12498 ); 12499 12500 if (orderable) { 12501 cell.find('.dt-column-title').attr('role', 'button'); 12502 cell.attr('tabindex', 0) 12503 } 12504 } ); 12505 } 12506 }, 12507 12508 layout: { 12509 _: function ( settings, container, items ) { 12510 var row = $('<div/>') 12511 .addClass('dt-layout-row') 12512 .appendTo( container ); 12513 12514 $.each( items, function (key, val) { 12515 var klass = ! val.table ? 12516 'dt-'+key+' ' : 12517 ''; 12518 12519 if (val.table) { 12520 row.addClass('dt-layout-table'); 12521 } 12522 12523 $('<div/>') 12524 .attr({ 12525 id: val.id || null, 12526 "class": 'dt-layout-cell '+klass+(val.className || '') 12527 }) 12528 .append( val.contents ) 12529 .appendTo( row ); 12530 } ); 12531 } 12532 } 12533 } ); 12534 12535 12536 DataTable.feature = {}; 12537 12538 // Third parameter is internal only! 12539 DataTable.feature.register = function ( name, cb, legacy ) { 12540 DataTable.ext.features[ name ] = cb; 12541 12542 if (legacy) { 12543 _ext.feature.push({ 12544 cFeature: legacy, 12545 fnInit: cb 12546 }); 12547 } 12548 }; 12549 12550 DataTable.feature.register( 'info', function ( settings, opts ) { 12551 // For compatibility with the legacy `info` top level option 12552 if (! settings.oFeatures.bInfo) { 12553 return null; 12554 } 12555 12556 var 12557 lang = settings.oLanguage, 12558 tid = settings.sTableId, 12559 n = $('<div/>', { 12560 'class': settings.oClasses.info.container, 12561 } ); 12562 12563 opts = $.extend({ 12564 callback: lang.fnInfoCallback, 12565 empty: lang.sInfoEmpty, 12566 postfix: lang.sInfoPostFix, 12567 search: lang.sInfoFiltered, 12568 text: lang.sInfo, 12569 }, opts); 12570 12571 12572 // Update display on each draw 12573 settings.aoDrawCallback.push(function (s) { 12574 _fnUpdateInfo(s, opts, n); 12575 }); 12576 12577 // For the first info display in the table, we add a callback and aria information. 12578 if (! settings._infoEl) { 12579 n.attr({ 12580 'aria-live': 'polite', 12581 id: tid+'_info', 12582 role: 'status' 12583 }); 12584 12585 // Table is described by our info div 12586 $(settings.nTable).attr( 'aria-describedby', tid+'_info' ); 12587 12588 settings._infoEl = n; 12589 } 12590 12591 return n; 12592 }, 'i' ); 12593 12594 /** 12595 * Update the information elements in the display 12596 * @param {object} settings dataTables settings object 12597 * @memberof DataTable#oApi 12598 */ 12599 function _fnUpdateInfo ( settings, opts, node ) 12600 { 12601 var 12602 start = settings._iDisplayStart+1, 12603 end = settings.fnDisplayEnd(), 12604 max = settings.fnRecordsTotal(), 12605 total = settings.fnRecordsDisplay(), 12606 out = total 12607 ? opts.text 12608 : opts.empty; 12609 12610 if ( total !== max ) { 12611 // Record set after filtering 12612 out += ' ' + opts.search; 12613 } 12614 12615 // Convert the macros 12616 out += opts.postfix; 12617 out = _fnMacros( settings, out ); 12618 12619 if ( opts.callback ) { 12620 out = opts.callback.call( settings.oInstance, 12621 settings, start, end, max, total, out 12622 ); 12623 } 12624 12625 node.html( out ); 12626 12627 _fnCallbackFire(settings, null, 'info', [settings, node[0], out]); 12628 } 12629 12630 var __searchCounter = 0; 12631 12632 // opts 12633 // - text 12634 // - placeholder 12635 DataTable.feature.register( 'search', function ( settings, opts ) { 12636 // Don't show the input if filtering isn't available on the table 12637 if (! settings.oFeatures.bFilter) { 12638 return null; 12639 } 12640 12641 var classes = settings.oClasses.search; 12642 var tableId = settings.sTableId; 12643 var language = settings.oLanguage; 12644 var previousSearch = settings.oPreviousSearch; 12645 var input = '<input type="search" class="'+classes.input+'"/>'; 12646 12647 opts = $.extend({ 12648 placeholder: language.sSearchPlaceholder, 12649 text: language.sSearch 12650 }, opts); 12651
12652 // The _INPUT_ is optional - is appended if not present 12653 if (opts.text.indexOf('_INPUT_') === -1) { 12654 opts.text += '_INPUT_'; 12655 } 12656 12657 opts.text = _fnMacros(settings, opts.text); 12658 12659 // We can put the <input> outside of the label if it is at the start or end 12660 // which helps improve accessability (not all screen readers like implicit 12661 // for elements). 12662 var end = opts.text.match(/_INPUT_$/); 12663 var start = opts.text.match(/^_INPUT_/); 12664 var removed = opts.text.replace(/_INPUT_/, ''); 12665 var str = '<label>' + opts.text + '</label>'; 12666 12667 if (start) { 12668 str = '_INPUT_<label>' + removed + '</label>'; 12669 } 12670 else if (end) { 12671 str = '<label>' + removed + '</label>_INPUT_'; 12672 } 12673 12674 var filter = $('<div>') 12675 .addClass(classes.container) 12676 .append(str.replace(/_INPUT_/, input)); 12677 12678 // add for and id to label and input 12679 filter.find('label').attr('for', 'dt-search-' + __searchCounter); 12680 filter.find('input').attr('id', 'dt-search-' + __searchCounter); 12681 __searchCounter++; 12682 12683 var searchFn = function(event) { 12684 var val = this.value; 12685 12686 if(previousSearch.return && event.key !== "Enter") { 12687 return; 12688 } 12689 12690 /* Now do the filter */ 12691 if ( val != previousSearch.search ) { 12692 previousSearch.search = val; 12693 12694 _fnFilterComplete( settings, previousSearch ); 12695 12696 // Need to redraw, without resorting 12697 settings._iDisplayStart = 0; 12698 _fnDraw( settings ); 12699 } 12700 }; 12701 12702 var searchDelay = settings.searchDelay !== null ? 12703 settings.searchDelay : 12704 0; 12705 12706 var jqFilter = $('input', filter) 12707 .val( previousSearch.search ) 12708 .attr( 'placeholder', opts.placeholder ) 12709 .on( 12710 'keyup.DT search.DT input.DT paste.DT cut.DT', 12711 searchDelay ? 12712 DataTable.util.debounce( searchFn, searchDelay ) : 12713 searchFn 12714 ) 12715 .on( 'mouseup.DT', function(e) { 12716 // Edge fix! Edge 17 does not trigger anything other than mouse events when clicking 12717 // on the clear icon (Edge bug 17584515). This is safe in other browsers as `searchFn` 12718 // checks the value to see if it has changed. In other browsers it won't have. 12719 setTimeout( function () { 12720 searchFn.call(jqFilter[0], e); 12721 }, 10); 12722 } ) 12723 .on( 'keypress.DT', function(e) { 12724 /* Prevent form submission */ 12725 if ( e.keyCode == 13 ) { 12726 return false; 12727 } 12728 } ) 12729 .attr('aria-controls', tableId); 12730 12731 // Update the input elements whenever the table is filtered 12732 $(settings.nTable).on( 'search.dt.DT', function ( ev, s ) { 12733 if ( settings === s && jqFilter[0] !== document.activeElement ) { 12734 jqFilter.val( typeof previousSearch.search !== 'function' 12735 ? previousSearch.search 12736 : '' 12737 ); 12738 } 12739 } ); 12740 12741 return filter; 12742 }, 'f' ); 12743 12744 // opts 12745 // - type - button configuration 12746 // - buttons - number of buttons to show - must be odd 12747 DataTable.feature.register( 'paging', function ( settings, opts ) { 12748 // Don't show the paging input if the table doesn't have paging enabled 12749 if (! settings.oFeatures.bPaginate) { 12750 return null; 12751 } 12752 12753 opts = $.extend({ 12754 buttons: DataTable.ext.pager.numbers_length, 12755 type: settings.sPaginationType, 12756 boundaryNumbers: true 12757 }, opts); 12758 12759 // To be removed in 2.1 12760 if (opts.numbers) { 12761 opts.buttons = opts.numbers; 12762 } 12763 12764 var host = $('<div/>').addClass( settings.oClasses.paging.container + ' paging_' + opts.type ); 12765 var draw = function () { 12766 _pagingDraw(settings, host, opts); 12767 }; 12768 12769 settings.aoDrawCallback.push(draw); 12770 12771 // Responsive redraw of paging control 12772 $(settings.nTable).on('column-sizing.dt.DT', draw); 12773 12774 return host; 12775 }, 'p' ); 12776 12777 function _pagingDraw(settings, host, opts) { 12778 if (! settings._bInitComplete) { 12779 return; 12780 } 12781 12782 var 12783 plugin = DataTable.ext.pager[ opts.type ], 12784 aria = settings.oLanguage.oAria.paginate || {}, 12785 start = settings._iDisplayStart, 12786 len = settings._iDisplayLength, 12787 visRecords = settings.fnRecordsDisplay(), 12788 all = len === -1, 12789 page = all ? 0 : Math.ceil( start / len ), 12790 pages = all ? 1 : Math.ceil( visRecords / len ), 12791 buttons = plugin() 12792 .map(function (val) { 12793 return val === 'numbers' 12794 ? _pagingNumbers(page, pages, opts.buttons, opts.boundaryNumbers) 12795 : val; 12796 }) 12797 .flat(); 12798 12799 var buttonEls = []; 12800 12801 for (var i=0 ; i<buttons.length ; i++) { 12802 var button = buttons[i]; 12803 12804 var btnInfo = _pagingButtonInfo(settings, button, page, pages); 12805 var btn = _fnRenderer( settings, 'pagingButton' )( 12806 settings, 12807 button, 12808 btnInfo.display, 12809 btnInfo.active, 12810 btnInfo.disabled 12811 ); 12812 12813 // Common attributes 12814 $(btn.clicker).attr({ 12815 'aria-controls': settings.sTableId, 12816 'aria-disabled': btnInfo.disabled ? 'true' : null, 12817 'aria-current': btnInfo.active ? 'page' : null, 12818 'aria-label': aria[ button ], 12819 'data-dt-idx': button, 12820 'tabIndex': btnInfo.disabled ? -1 : settings.iTabIndex, 12821 }); 12822 12823 if (typeof button !== 'number') { 12824 $(btn.clicker).addClass(button); 12825 } 12826 12827 _fnBindAction( 12828 btn.clicker, {action: button}, function(e) { 12829 e.preventDefault(); 12830 12831 _fnPageChange( settings, e.data.action, true ); 12832 } 12833 ); 12834 12835 buttonEls.push(btn.display); 12836 } 12837 12838 var wrapped = _fnRenderer(settings, 'pagingContainer')( 12839 settings, buttonEls 12840 ); 12841 12842 var activeEl = host.find(document.activeElement).data('dt-idx'); 12843 12844 host.empty().append(wrapped); 12845 12846 if ( activeEl !== undefined ) { 12847 host.find( '[data-dt-idx='+activeEl+']' ).trigger('focus'); 12848 } 12849 12850 // Responsive - check if the buttons are over two lines based on the 12851 // height of the buttons and the container. 12852 if ( 12853 buttonEls.length && // any buttons 12854 opts.numbers > 1 && // prevent infinite 12855 $(host).height() >= ($(buttonEls[0]).outerHeight() * 2) - 10 12856 ) { 12857 _pagingDraw(settings, host, $.extend({}, opts, { numbers: opts.numbers - 2 })); 12858 } 12859 } 12860 12861 /** 12862 * Get properties for a button based on the current paging state of the table 12863 * 12864 * @param {*} settings DT settings object 12865 * @param {*} button The button type in question 12866 * @param {*} page Table's current page 12867 * @param {*} pages Number of pages 12868 * @returns Info object 12869 */ 12870 function _pagingButtonInfo(settings, button, page, pages) { 12871 var lang = settings.oLanguage.oPaginate; 12872 var o = { 12873 display: '', 12874 active: false, 12875 disabled: false 12876 }; 12877 12878 switch ( button ) { 12879 case 'ellipsis': 12880 o.display = '…'; 12881 o.disabled = true; 12882 break; 12883 12884 case 'first': 12885 o.display = lang.sFirst; 12886 12887 if (page === 0) { 12888 o.disabled = true; 12889 } 12890 break; 12891 12892 case 'previous': 12893 o.display = lang.sPrevious; 12894 12895 if ( page === 0 ) { 12896 o.disabled = true; 12897 } 12898 break; 12899 12900 case 'next': 12901 o.display = lang.sNext; 12902 12903 if ( pages === 0 || page === pages-1 ) { 12904 o.disabled = true; 12905 } 12906 break; 12907 12908 case 'last': 12909 o.display = lang.sLast; 12910 12911 if ( pages === 0 || page === pages-1 ) { 12912 o.disabled = true; 12913 } 12914 break; 12915 12916 default: 12917 if ( typeof button === 'number' ) { 12918 o.display = settings.fnFormatNumber( button + 1 ); 12919 12920 if (page === button) { 12921 o.active = true; 12922 } 12923 } 12924 break; 12925 } 12926 12927 return o; 12928 } 12929 12930 /** 12931 * Compute what number buttons to show in the paging control 12932 * 12933 * @param {*} page Current page 12934 * @param {*} pages Total number of pages 12935 * @param {*} buttons Target number of number buttons 12936 * @param {boolean} addFirstLast Indicate if page 1 and end should be included 12937 * @returns Buttons to show 12938 */ 12939 function _pagingNumbers ( page, pages, buttons, addFirstLast ) { 12940 var 12941 numbers = [], 12942 half = Math.floor(buttons / 2), 12943 before = addFirstLast ? 2 : 1, 12944 after = addFirstLast ? 1 : 0; 12945 12946 if ( pages <= buttons ) { 12947 numbers = _range(0, pages); 12948 } 12949 else if (buttons === 1) { 12950 // Single button - current page only 12951 numbers = [page]; 12952 } 12953 else if (buttons === 3) { 12954 // Special logic for just three buttons 12955 if (page <= 1) { 12956 numbers = [0, 1, 'ellipsis']; 12957 } 12958 else if (page >= pages - 2) { 12959 numbers = _range(pages-2, pages); 12960 numbers.unshift('ellipsis'); 12961 } 12962 else { 12963 numbers = ['ellipsis', page, 'ellipsis']; 12964 } 12965 } 12966 else if ( page <= half ) { 12967 numbers = _range(0, buttons-before); 12968 numbers.push('ellipsis'); 12969 12970 if (addFirstLast) { 12971 numbers.push(pages-1); 12972 } 12973 } 12974 else if ( page >= pages - 1 - half ) { 12975 numbers = _range(pages-(buttons-before), pages); 12976 numbers.unshift('ellipsis'); 12977 12978 if (addFirstLast) { 12979 numbers.unshift(0); 12980 } 12981 } 12982 else { 12983 numbers = _range(page-half+before, page+half-after); 12984 numbers.push('ellipsis'); 12985 numbers.unshift('ellipsis'); 12986 12987 if (addFirstLast) { 12988 numbers.push(pages-1); 12989 numbers.unshift(0); 12990 } 12991 } 12992 12993 return numbers; 12994 } 12995 12996 var __lengthCounter = 0; 12997 12998 // opts 12999 // - menu 13000 // - text 13001 DataTable.feature.register( 'pageLength', function ( settings, opts ) { 13002 var features = settings.oFeatures; 13003 13004 // For compatibility with the legacy `pageLength` top level option 13005 if (! features.bPaginate || ! features.bLengthChange) { 13006 return null; 13007 } 13008 13009 opts = $.extend({ 13010 menu: settings.aLengthMenu, 13011 text: settings.oLanguage.sLengthMenu 13012 }, opts); 13013 13014 var 13015 classes = settings.oClasses.length, 13016 tableId = settings.sTableId, 13017 menu = opts.menu, 13018 lengths = [], 13019 language = [], 13020 i; 13021 13022 // Options can be given in a number of ways 13023 if (Array.isArray( menu[0] )) { 13024 // Old 1.x style - 2D array 13025 lengths = menu[0]; 13026 language = menu[1]; 13027 } 13028 else { 13029 for ( i=0 ; i<menu.length ; i++ ) { 13030 // An object with different label and value 13031 if ($.isPlainObject(menu[i])) { 13032 lengths.push(menu[i].value); 13033 language.push(menu[i].label); 13034 } 13035 else { 13036 // Or just a number to display and use 13037 lengths.push(menu[i]); 13038 language.push(menu[i]); 13039 } 13040 } 13041 } 13042 13043 // We can put the <select> outside of the label if it is at the start or 13044 // end which helps improve accessability (not all screen readers like 13045 // implicit for elements). 13046 var end = opts.text.match(/_MENU_$/); 13047 var start = opts.text.match(/^_MENU_/); 13048 var removed = opts.text.replace(/_MENU_/, ''); 13049 var str = '<label>' + opts.text + '</label>'; 13050 13051 if (start) { 13052 str = '_MENU_<label>' + removed + '</label>'; 13053 } 13054 else if (end) { 13055 str = '<label>' + removed + '</label>_MENU_'; 13056 } 13057 13058 // Wrapper element - use a span as a holder for where the select will go
13059 var div = $('<div/>') 13060 .addClass( classes.container ) 13061 .append( 13062 str.replace( '_MENU_', '<span></span>' ) 13063 ); 13064 13065 // Save text node content for macro updating 13066 var textNodes = []; 13067 div.find('label')[0].childNodes.forEach(function (el) { 13068 if (el.nodeType === Node.TEXT_NODE) { 13069 textNodes.push({ 13070 el: el, 13071 text: el.textContent 13072 }); 13073 } 13074 }) 13075 13076 // Update the label text in case it has an entries value 13077 var updateEntries = function (len) { 13078 textNodes.forEach(function (node) { 13079 node.el.textContent = _fnMacros(settings, node.text, len); 13080 }); 13081 } 13082 13083 // Next, the select itself, along with the options 13084 var select = $('<select/>', { 13085 'name': tableId+'_length', 13086 'aria-controls': tableId, 13087 'class': classes.select 13088 } ); 13089 13090 for ( i=0 ; i<lengths.length ; i++ ) { 13091 select[0][ i ] = new Option( 13092 typeof language[i] === 'number' ? 13093 settings.fnFormatNumber( language[i] ) : 13094 language[i], 13095 lengths[i] 13096 ); 13097 } 13098 13099 // add for and id to label and input 13100 div.find('label').attr('for', 'dt-length-' + __lengthCounter); 13101 select.attr('id', 'dt-length-' + __lengthCounter); 13102 __lengthCounter++; 13103 13104 // Swap in the select list 13105 div.find('span').replaceWith(select); 13106 13107 // Can't use `select` variable as user might provide their own and the 13108 // reference is broken by the use of outerHTML 13109 $('select', div) 13110 .val( settings._iDisplayLength ) 13111 .on( 'change.DT', function() { 13112 _fnLengthChange( settings, $(this).val() ); 13113 _fnDraw( settings ); 13114 } ); 13115 13116 // Update node value whenever anything changes the table's length 13117 $(settings.nTable).on( 'length.dt.DT', function (e, s, len) { 13118 if ( settings === s ) { 13119 $('select', div).val( len ); 13120 13121 // Resolve plurals in the text for the new length 13122 updateEntries(len); 13123 } 13124 } ); 13125 13126 updateEntries(settings._iDisplayLength); 13127 13128 return div; 13129 }, 'l' ); 13130 13131 // jQuery access 13132 $.fn.dataTable = DataTable; 13133 13134 // Provide access to the host jQuery object (circular reference) 13135 DataTable.$ = $; 13136 13137 // Legacy aliases 13138 $.fn.dataTableSettings = DataTable.settings; 13139 $.fn.dataTableExt = DataTable.ext; 13140 13141 // With a capital `D` we return a DataTables API instance rather than a 13142 // jQuery object 13143 $.fn.DataTable = function ( opts ) { 13144 return $(this).dataTable( opts ).api(); 13145 }; 13146 13147 // All properties that are available to $.fn.dataTable should also be 13148 // available on $.fn.DataTable 13149 $.each( DataTable, function ( prop, val ) { 13150 $.fn.DataTable[ prop ] = val; 13151 } ); 13152 13153 return DataTable; 13154}));
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.